am 992b10f4: (-s ours) Reconcile with jb-dev - do not merge

* commit '992b10f49def736e424b9d6e01130de28b09cd1f':
diff --git a/COPYING b/COPYING
index 14f5453..8a98582 100644
--- a/COPYING
+++ b/COPYING
@@ -1,340 +1,22 @@
-		    GNU GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
+wpa_supplicant and hostapd
+--------------------------
 
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-		    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-			    NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-	    How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) 19yy  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
+All Rights Reserved.
 
 
-Also add information on how to contact you by electronic and paper mail.
+See the README file for the current license terms.
 
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
+This software was previously distributed under BSD/GPL v2 dual license
+terms that allowed either of those license alternatives to be
+selected. As of February 11, 2012, the project has chosen to use only
+the BSD license option for future distribution. As such, the GPL v2
+license option is no longer used. It should be noted that the BSD
+license option (the one with advertisement clause removed) is compatible
+with GPL and as such, does not prevent use of this software in projects
+that use GPL.
 
-    Gnomovision version 69, Copyright (C) 19yy name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
-Public License instead of this License.
+Some of the files may still include pointers to GPL version 2 license
+terms. However, such copyright and license notifications are maintained
+only for attribution purposes and any distribution of this software
+after February 11, 2012 is no longer under the GPL v2 option.
diff --git a/README b/README
index 94ef1a7..805c6cf 100644
--- a/README
+++ b/README
@@ -1,12 +1,11 @@
 wpa_supplicant and hostapd
 --------------------------
 
-Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
-These programs are dual-licensed under both the GPL version 2 and BSD
-license (the one with advertisement clause removed). Either license
-may be used at your option.
+These programs are licensed under the BSD license (the one with
+advertisement clause removed).
 
 
 This package may include either wpa_supplicant, hostapd, or both. See
@@ -23,26 +22,8 @@
 License
 -------
 
-GPL v2:
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License version 2 as
-published by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-
-(this copy of the license is in COPYING file)
-
-
-Alternatively, this software may be distributed, used, and modified
-under the terms of BSD license:
+This software may be distributed, used, and modified under the terms of
+BSD license:
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index c1e5d8b..ee153d0 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -28,6 +28,15 @@
 L_CFLAGS += -DANDROID_P2P
 endif
 
+ifeq ($(BOARD_WLAN_DEVICE), qcwcn)
+L_CFLAGS += -DANDROID_QCOM_WCN
+L_CFLAGS += -DANDROID_P2P
+endif
+
+# Use Android specific directory for control interface sockets
+L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
+L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/hostapd\"
+
 # To force sizeof(enum) = 4
 ifeq ($(TARGET_ARCH),arm)
 L_CFLAGS += -mabi=aapcs-linux
@@ -95,6 +104,7 @@
 NEED_AES=y
 NEED_MD5=y
 NEED_SHA1=y
+NEED_SHA256=y
 
 OBJS += src/drivers/drivers.c
 L_CFLAGS += -DHOSTAPD
@@ -140,6 +150,7 @@
 else
 OBJS += src/radius/radius.c
 OBJS += src/radius/radius_client.c
+OBJS += src/radius/radius_das.c
 endif
 
 ifdef CONFIG_NO_ACCOUNTING
@@ -152,6 +163,12 @@
 L_CFLAGS += -DCONFIG_NO_VLAN
 else
 OBJS += src/ap/vlan_init.c
+ifdef CONFIG_VLAN_NETLINK
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+OBJS += src/ap/vlan_util.c
+endif
+L_CFLAGS += -DCONFIG_VLAN_NETLINK
+endif
 endif
 
 ifdef CONFIG_NO_CTRL_IFACE
@@ -194,10 +211,23 @@
 NEED_AES_UNWRAP=y
 endif
 
+ifdef CONFIG_IEEE80211V
+L_CFLAGS += -DCONFIG_IEEE80211V
+OBJS += src/ap/wnm_ap.c
+endif
+
 ifdef CONFIG_IEEE80211N
 L_CFLAGS += -DCONFIG_IEEE80211N
 endif
 
+ifdef CONFIG_WNM
+L_CFLAGS += -DCONFIG_WNM
+endif
+
+ifdef CONFIG_IEEE80211AC
+L_CFLAGS += -DCONFIG_IEEE80211AC
+endif
+
 include $(LOCAL_PATH)/src/drivers/drivers.mk
 
 OBJS += $(DRV_AP_OBJS)
@@ -234,6 +264,14 @@
 TLS_FUNCS=y
 endif
 
+ifdef CONFIG_EAP_UNAUTH_TLS
+L_CFLAGS += -DEAP_SERVER_UNAUTH_TLS
+ifndef CONFIG_EAP_TLS
+OBJS += src/eap_server/eap_server_tls.c
+TLS_FUNCS=y
+endif
+endif
+
 ifdef CONFIG_EAP_PEAP
 L_CFLAGS += -DEAP_SERVER_PEAP
 OBJS += src/eap_server/eap_server_peap.c
@@ -467,6 +505,15 @@
 CONFIG_TLS=openssl
 endif
 
+ifdef CONFIG_TLSV11
+L_CFLAGS += -DCONFIG_TLSV11
+endif
+
+ifdef CONFIG_TLSV12
+L_CFLAGS += -DCONFIG_TLSV12
+NEED_SHA256=y
+endif
+
 ifeq ($(CONFIG_TLS), openssl)
 ifdef TLS_FUNCS
 OBJS += src/crypto/tls_openssl.c
@@ -550,6 +597,9 @@
 NEED_SHA256=y
 NEED_BASE64=y
 NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
 NEED_MODEXP=y
 NEED_CIPHER=y
 L_CFLAGS += -DCONFIG_TLS_INTERNAL
@@ -664,14 +714,19 @@
 
 SHA1OBJS =
 ifdef NEED_SHA1
+ifneq ($(CONFIG_TLS), openssl)
 SHA1OBJS += src/crypto/sha1.c
+endif
+SHA1OBJS += src/crypto/sha1-prf.c
 ifdef CONFIG_INTERNAL_SHA1
 SHA1OBJS += src/crypto/sha1-internal.c
 ifdef NEED_FIPS186_2_PRF
 SHA1OBJS += src/crypto/fips_prf_internal.c
 endif
 endif
+ifneq ($(CONFIG_TLS), openssl)
 SHA1OBJS += src/crypto/sha1-pbkdf2.c
+endif
 ifdef NEED_T_PRF
 SHA1OBJS += src/crypto/sha1-tprf.c
 endif
@@ -710,10 +765,17 @@
 endif
 
 ifdef NEED_SHA256
+L_CFLAGS += -DCONFIG_SHA256
+ifneq ($(CONFIG_TLS), openssl)
 OBJS += src/crypto/sha256.c
+endif
+OBJS += src/crypto/sha256-prf.c
 ifdef CONFIG_INTERNAL_SHA256
 OBJS += src/crypto/sha256-internal.c
 endif
+ifdef NEED_TLS_PRF_SHA256
+OBJS += src/crypto/sha256-tlsprf.c
+endif
 endif
 
 ifdef NEED_DH_GROUPS
@@ -733,6 +795,7 @@
 else
 OBJS += src/crypto/random.c
 HOBJS += src/crypto/random.c
+HOBJS += src/utils/eloop.c
 HOBJS += $(SHA1OBJS)
 HOBJS += src/crypto/md5.c
 endif
@@ -771,11 +834,27 @@
 OBJS += src/ap/ieee802_11_ht.c
 endif
 
+ifdef CONFIG_IEEE80211AC
+OBJS += src/ap/ieee802_11_vht.c
+endif
+
 ifdef CONFIG_P2P_MANAGER
 L_CFLAGS += -DCONFIG_P2P_MANAGER
 OBJS += src/ap/p2p_hostapd.c
 endif
 
+ifdef CONFIG_HS20
+L_CFLAGS += -DCONFIG_HS20
+OBJS += src/ap/hs20.c
+CONFIG_INTERWORKING=y
+endif
+
+ifdef CONFIG_INTERWORKING
+L_CFLAGS += -DCONFIG_INTERWORKING
+OBJS += src/common/gas.c
+OBJS += src/ap/gas_serv.c
+endif
+
 OBJS += src/drivers/driver_common.c
 
 ifdef CONFIG_NO_STDOUT_DEBUG
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index 47f2423..9fc05f7 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,5 +1,112 @@
 ChangeLog for hostapd
 
+2012-05-10 - v1.0
+	* Add channel selection support in hostapd. See hostapd.conf.
+	* Add support for IEEE 802.11v Time Advertisement mechanism with UTC
+	  TSF offset. See hostapd.conf for config info.
+	* Delay STA entry removal until Deauth/Disassoc TX status in AP mode.
+	  This allows the driver to use PS buffering of Deauthentication and
+	  Disassociation frames when the STA is in power save sleep. Only
+	  available with drivers that provide TX status events for Deauth/
+	  Disassoc frames (nl80211).
+	* Allow PMKSA caching to be disabled on the Authenticator. See
+	  hostap.conf config parameter disable_pmksa_caching.
+	* atheros: Add support for IEEE 802.11w configuration.
+	* bsd: Add support for setting HT values in IFM_MMASK.
+	* Allow client isolation to be configured with ap_isolate. Client
+	  isolation can be used to prevent low-level bridging of frames
+	  between associated stations in the BSS. By default, this bridging
+	  is allowed.
+	* Allow coexistance of HT BSSes with WEP/TKIP BSSes.
+	* Add require_ht config parameter, which can be used to configure
+	  hostapd to reject association with any station that does not support
+	  HT PHY.
+	* Add support for writing debug log to a file using "-f" option. Also
+	  add relog CLI command to re-open the log file.
+	* Add bridge handling for WDS STA interfaces. By default they are
+	  added to the configured bridge of the AP interface (if present),
+	  but the user can also specify a separate bridge using cli command
+	  wds_bridge.
+	* hostapd_cli:
+	  - Add wds_bridge command for specifying bridge for WDS STA
+	    interfaces.
+	  - Add relog command for reopening log file.
+	  - Send AP-STA-DISCONNECTED event when an AP disconnects a station
+	    due to inactivity.
+	  - Add wps_config ctrl_interface command for configuring AP. This
+	    command can be used to configure the AP using the internal WPS
+	    registrar. It works in the same way as new AP settings received
+	    from an ER.
+	  - Many WPS/WPS ER commands - see WPS/WPS ER sections for details.
+	  - Add command get version, that returns hostapd version string.
+	* WNM: Add BSS Transition Management Request for ESS Disassoc Imminent.
+	  Use hostapd_cli ess_disassoc (STA addr) (URL) to send the
+	  notification to the STA.
+	* Allow AP mode to disconnect STAs based on low ACK condition (when
+	  the data connection is not working properly, e.g., due to the STA
+	  going outside the range of the AP). Disabled by default, enable by
+	  config option disassoc_low_ack.
+	* Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad
+	  config file.
+	* WPS:
+	  - Send AP Settings as a wrapped Credential attribute to ctrl_iface
+	    in WPS-NEW-AP-SETTINGS.
+	  - Dispatch more WPS events through hostapd ctrl_iface.
+	  - Add mechanism for indicating non-standard WPS errors.
+	  - Change concurrent radio AP to use only one WPS UPnP instance.
+	  - Add wps_check_pin command for processing PIN from user input.
+	    UIs can use this command to process a PIN entered by a user and to
+	    validate the checksum digit (if present).
+	  - Add hostap_cli get_config command to display current AP config.
+	  - Add new hostapd_cli command, wps_ap_pin, to manage AP PIN at
+	    runtime and support dynamic AP PIN management.
+	  - Disable AP PIN after 10 consecutive failures. Slow down attacks
+	    on failures up to 10.
+	  - Allow AP to start in Enrollee mode without AP PIN for probing,
+	    to be compatible with Windows 7.
+	  - Add Config Error into WPS-FAIL events to provide more info
+	    to the user on how to resolve the issue.
+	  - When controlling multiple interfaces:
+	     - apply WPS commands to all interfaces configured to use WPS
+	     - apply WPS config changes to all interfaces that use WPS
+	     - when an attack is detected on any interface, disable AP PIN on
+	       all interfaces
+	* WPS ER:
+	  - Show SetSelectedRegistrar events as ctrl_iface events.
+	  - Add special AP Setup Locked mode to allow read only ER.
+	    ap_setup_locked=2 can now be used to enable a special mode where
+	    WPS ER can learn the current AP settings, but cannot change them.
+	* WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2)
+	  - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool
+	    for testing protocol extensibility.
+	  - Add build option CONFIG_WPS_STRICT to allow disabling of WPS
+	    workarounds.
+	  - Add support for AuthorizedMACs attribute.
+	* TDLS:
+	  - Allow TDLS use or TDLS channel switching in the BSS to be
+	    prohibited in the BSS, using config params tdls_prohibit and
+	    tdls_prohibit_chan_switch.
+	* EAP server: Add support for configuring fragment size (see
+	  fragment_size in hostapd.conf).
+	* wlantest: Add a tool wlantest for IEEE802.11 protocol testing.
+	  wlantest can be used to capture frames from a monitor interface
+	  for realtime capturing or from pcap files for offline analysis.
+	* Interworking: Support added for 802.11u. Enable in .config with
+	  CONFIG_INTERWORKING. See hostapd.conf for config parameters for
+	  interworking.
+	* Android: Add build and runtime support for Android hostapd.
+	* Add a new debug message level for excessive information. Use
+	  -ddd to enable.
+	* TLS: Add support for tls_disable_time_checks=1 in client mode.
+	* Internal TLS:
+	  - Add support for TLS v1.1 (RFC 4346). Enable with build parameter
+	    CONFIG_TLSV11.
+	  - Add domainComponent parser for X.509 names
+	* Reorder some IEs to get closer to IEEE 802.11 standard. Move
+	  WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames.
+	  Move HT IEs to be later in (Re)Assoc Resp.
+	* Many bugfixes.
+
 2010-04-18 - v0.7.2
 	* fix WPS internal Registrar use when an external Registrar is also
 	  active
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 22c09c1..f5dfce0 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -110,6 +110,7 @@
 else
 OBJS += ../src/radius/radius.o
 OBJS += ../src/radius/radius_client.o
+OBJS += ../src/radius/radius_das.o
 endif
 
 ifdef CONFIG_NO_ACCOUNTING
@@ -122,6 +123,12 @@
 CFLAGS += -DCONFIG_NO_VLAN
 else
 OBJS += ../src/ap/vlan_init.o
+ifdef CONFIG_VLAN_NETLINK
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+OBJS += ../src/ap/vlan_util.o
+endif
+CFLAGS += -DCONFIG_VLAN_NETLINK
+endif
 endif
 
 ifdef CONFIG_NO_CTRL_IFACE
@@ -164,10 +171,23 @@
 NEED_AES_UNWRAP=y
 endif
 
+ifdef CONFIG_IEEE80211V
+CFLAGS += -DCONFIG_IEEE80211V
+OBJS += ../src/ap/wnm_ap.o
+endif
+
 ifdef CONFIG_IEEE80211N
 CFLAGS += -DCONFIG_IEEE80211N
 endif
 
+ifdef CONFIG_WNM
+CFLAGS += -DCONFIG_WNM
+endif
+
+ifdef CONFIG_IEEE80211AC
+CFLAGS += -DCONFIG_IEEE80211AC
+endif
+
 include ../src/drivers/drivers.mak
 OBJS += $(DRV_AP_OBJS)
 CFLAGS += $(DRV_AP_CFLAGS)
@@ -203,6 +223,14 @@
 TLS_FUNCS=y
 endif
 
+ifdef CONFIG_EAP_UNAUTH_TLS
+CFLAGS += -DEAP_SERVER_UNAUTH_TLS
+ifndef CONFIG_EAP_TLS
+OBJS += ../src/eap_server/eap_server_tls.o
+TLS_FUNCS=y
+endif
+endif
+
 ifdef CONFIG_EAP_PEAP
 CFLAGS += -DEAP_SERVER_PEAP
 OBJS += ../src/eap_server/eap_server_peap.o
@@ -640,14 +668,19 @@
 endif
 
 ifdef NEED_SHA1
+ifneq ($(CONFIG_TLS), openssl)
 SHA1OBJS += ../src/crypto/sha1.o
+endif
+SHA1OBJS += ../src/crypto/sha1-prf.o
 ifdef CONFIG_INTERNAL_SHA1
 SHA1OBJS += ../src/crypto/sha1-internal.o
 ifdef NEED_FIPS186_2_PRF
 SHA1OBJS += ../src/crypto/fips_prf_internal.o
 endif
 endif
+ifneq ($(CONFIG_TLS), openssl)
 SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+endif
 ifdef NEED_T_PRF
 SHA1OBJS += ../src/crypto/sha1-tprf.o
 endif
@@ -687,7 +720,10 @@
 
 ifdef NEED_SHA256
 CFLAGS += -DCONFIG_SHA256
+ifneq ($(CONFIG_TLS), openssl)
 OBJS += ../src/crypto/sha256.o
+endif
+OBJS += ../src/crypto/sha256-prf.o
 ifdef CONFIG_INTERNAL_SHA256
 OBJS += ../src/crypto/sha256-internal.o
 endif
@@ -752,13 +788,25 @@
 OBJS += ../src/ap/ieee802_11_ht.o
 endif
 
+ifdef CONFIG_IEEE80211AC
+OBJS += ../src/ap/ieee802_11_vht.o
+endif
+
 ifdef CONFIG_P2P_MANAGER
 CFLAGS += -DCONFIG_P2P_MANAGER
 OBJS += ../src/ap/p2p_hostapd.o
 endif
 
+ifdef CONFIG_HS20
+CFLAGS += -DCONFIG_HS20
+OBJS += ../src/ap/hs20.o
+CONFIG_INTERWORKING=y
+endif
+
 ifdef CONFIG_INTERWORKING
 CFLAGS += -DCONFIG_INTERWORKING
+OBJS += ../src/common/gas.o
+OBJS += ../src/ap/gas_serv.o
 endif
 
 OBJS += ../src/drivers/driver_common.o
@@ -777,6 +825,12 @@
 CFLAGS += -DCONFIG_DEBUG_FILE
 endif
 
+ifdef CONFIG_SQLITE
+CFLAGS += -DCONFIG_SQLITE
+LIBS += -lsqlite3
+LIBS_h += -lsqlite3
+endif
+
 ALL=hostapd hostapd_cli
 
 all: verify_config $(ALL)
diff --git a/hostapd/README-WPS b/hostapd/README-WPS
index 17988d4..87a6f91 100644
--- a/hostapd/README-WPS
+++ b/hostapd/README-WPS
@@ -66,6 +66,10 @@
 CONFIG_WPS2=y
 CONFIG_WPS_UPNP=y
 
+Following parameter can be used to enable support for NFC config method:
+
+CONFIG_WPS_NFC=y
+
 
 Following section shows an example runtime configuration
 (hostapd.conf) that enables WPS:
@@ -289,3 +293,48 @@
 
 This can be used to update the externally stored AP configuration and
 then update hostapd configuration (followed by restarting of hostapd).
+
+
+WPS with NFC
+------------
+
+WPS can be used with NFC-based configuration method. An NFC tag
+containing a password token from the Enrollee can be used to
+authenticate the connection instead of the PIN. In addition, an NFC tag
+with a configuration token can be used to transfer AP settings without
+going through the WPS protocol.
+
+When the AP acts as an Enrollee, a local NFC tag with a password token
+can be used by touching the NFC interface of an external Registrar. The
+wps_nfc_token command is used to manage use of the NFC password token
+from the AP. "wps_nfc_token enable" enables the use of the AP's NFC
+password token (in place of AP PIN) and "wps_nfc_token disable" disables
+the NFC password token.
+
+The NFC password token that is either pre-configured in the
+configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey,
+wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with
+"wps_nfc_token <WPS|NDEF>" command. The nfc_pw_token tool from
+wpa_supplicant can be used to generate NFC password tokens during
+manufacturing (each AP needs to have its own random keys).
+
+The "wps_nfc_config_token <WPS/NDEF>" command can be used to build an
+NFC configuration token. The output value from this command is a hexdump
+of the current AP configuration (WPS parameter requests this to include
+only the WPS attributes; NDEF parameter requests additional NDEF
+encapsulation to be included). This data needs to be written to an NFC
+tag with an external program. Once written, the NFC configuration token
+can be used to touch an NFC interface on a station to provision the
+credentials needed to access the network.
+
+When the NFC device on the AP reads an NFC tag with a MIME media type
+"application/vnd.wfa.wsc", the NDEF message payload (with or without
+NDEF encapsulation) can be delivered to hostapd using the
+following hostapd_cli command:
+
+wps_nfc_tag_read <hexdump of payload>
+
+If the NFC tag contains a password token, the token is added to the
+internal Registrar. This allows station Enrollee from which the password
+token was received to run through WPS protocol to provision the
+credential.
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 78f1e3d..0a24ec3 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration file parser
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -167,7 +167,7 @@
 		if (*pos != '\0')
 			vlan_id = atoi(pos);
 
-		newacl = os_realloc(*acl, (*num + 1) * sizeof(**acl));
+		newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
 		if (newacl == NULL) {
 			wpa_printf(MSG_ERROR, "MAC list reallocation failed");
 			fclose(f);
@@ -475,7 +475,7 @@
 	int ret;
 	static int server_index = 1;
 
-	nserv = os_realloc(*server, (*num_server + 1) * sizeof(*nserv));
+	nserv = os_realloc_array(*server, *num_server + 1, sizeof(*nserv));
 	if (nserv == NULL)
 		return -1;
 
@@ -491,6 +491,100 @@
 
 	return ret;
 }
+
+
+static struct hostapd_radius_attr *
+hostapd_parse_radius_attr(const char *value)
+{
+	const char *pos;
+	char syntax;
+	struct hostapd_radius_attr *attr;
+	size_t len;
+
+	attr = os_zalloc(sizeof(*attr));
+	if (attr == NULL)
+		return NULL;
+
+	attr->type = atoi(value);
+
+	pos = os_strchr(value, ':');
+	if (pos == NULL) {
+		attr->val = wpabuf_alloc(1);
+		if (attr->val == NULL) {
+			os_free(attr);
+			return NULL;
+		}
+		wpabuf_put_u8(attr->val, 0);
+		return attr;
+	}
+
+	pos++;
+	if (pos[0] == '\0' || pos[1] != ':') {
+		os_free(attr);
+		return NULL;
+	}
+	syntax = *pos++;
+	pos++;
+
+	switch (syntax) {
+	case 's':
+		attr->val = wpabuf_alloc_copy(pos, os_strlen(pos));
+		break;
+	case 'x':
+		len = os_strlen(pos);
+		if (len & 1)
+			break;
+		len /= 2;
+		attr->val = wpabuf_alloc(len);
+		if (attr->val == NULL)
+			break;
+		if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) {
+			wpabuf_free(attr->val);
+			os_free(attr);
+			return NULL;
+		}
+		break;
+	case 'd':
+		attr->val = wpabuf_alloc(4);
+		if (attr->val)
+			wpabuf_put_be32(attr->val, atoi(pos));
+		break;
+	default:
+		os_free(attr);
+		return NULL;
+	}
+
+	if (attr->val == NULL) {
+		os_free(attr);
+		return NULL;
+	}
+
+	return attr;
+}
+
+
+static int hostapd_parse_das_client(struct hostapd_bss_config *bss,
+				    const char *val)
+{
+	char *secret;
+
+	secret = os_strchr(val, ' ');
+	if (secret == NULL)
+		return -1;
+
+	secret++;
+
+	if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr))
+		return -1;
+
+	os_free(bss->radius_das_shared_secret);
+	bss->radius_das_shared_secret = (u8 *) os_strdup(secret);
+	if (bss->radius_das_shared_secret == NULL)
+		return -1;
+	bss->radius_das_shared_secret_len = os_strlen(secret);
+
+	return 0;
+}
 #endif /* CONFIG_NO_RADIUS */
 
 
@@ -575,6 +669,8 @@
 		*end = '\0';
 		if (os_strcmp(start, "CCMP") == 0)
 			val |= WPA_CIPHER_CCMP;
+		else if (os_strcmp(start, "GCMP") == 0)
+			val |= WPA_CIPHER_GCMP;
 		else if (os_strcmp(start, "TKIP") == 0)
 			val |= WPA_CIPHER_TKIP;
 		else if (os_strcmp(start, "WEP104") == 0)
@@ -686,8 +782,8 @@
 	if (*ifname == '\0')
 		return -1;
 
-	bss = os_realloc(conf->bss, (conf->num_bss + 1) *
-			 sizeof(struct hostapd_bss_config));
+	bss = os_realloc_array(conf->bss, conf->num_bss + 1,
+			       sizeof(struct hostapd_bss_config));
 	if (bss == NULL) {
 		wpa_printf(MSG_ERROR, "Failed to allocate memory for "
 			   "multi-BSS entry");
@@ -811,78 +907,6 @@
 }
 
 
-static int hostapd_config_wmm_ac(struct hostapd_config *conf, char *name,
-				 char *val)
-{
-	int num, v;
-	char *pos;
-	struct hostapd_wmm_ac_params *ac;
-
-	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
-	pos = name + 7;
-	if (os_strncmp(pos, "be_", 3) == 0) {
-		num = 0;
-		pos += 3;
-	} else if (os_strncmp(pos, "bk_", 3) == 0) {
-		num = 1;
-		pos += 3;
-	} else if (os_strncmp(pos, "vi_", 3) == 0) {
-		num = 2;
-		pos += 3;
-	} else if (os_strncmp(pos, "vo_", 3) == 0) {
-		num = 3;
-		pos += 3;
-	} else {
-		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
-		return -1;
-	}
-
-	ac = &conf->wmm_ac_params[num];
-
-	if (os_strcmp(pos, "aifs") == 0) {
-		v = atoi(val);
-		if (v < 1 || v > 255) {
-			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
-			return -1;
-		}
-		ac->aifs = v;
-	} else if (os_strcmp(pos, "cwmin") == 0) {
-		v = atoi(val);
-		if (v < 0 || v > 12) {
-			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
-			return -1;
-		}
-		ac->cwmin = v;
-	} else if (os_strcmp(pos, "cwmax") == 0) {
-		v = atoi(val);
-		if (v < 0 || v > 12) {
-			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
-			return -1;
-		}
-		ac->cwmax = v;
-	} else if (os_strcmp(pos, "txop_limit") == 0) {
-		v = atoi(val);
-		if (v < 0 || v > 0xffff) {
-			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
-			return -1;
-		}
-		ac->txop_limit = v;
-	} else if (os_strcmp(pos, "acm") == 0) {
-		v = atoi(val);
-		if (v < 0 || v > 1) {
-			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
-			return -1;
-		}
-		ac->admission_control_mandatory = v;
-	} else {
-		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
-		return -1;
-	}
-
-	return 0;
-}
-
-
 #ifdef CONFIG_IEEE80211R
 static int add_r0kh(struct hostapd_bss_config *bss, char *value)
 {
@@ -1034,6 +1058,71 @@
 #endif /* CONFIG_IEEE80211N */
 
 
+#ifdef CONFIG_IEEE80211AC
+static int hostapd_config_vht_capab(struct hostapd_config *conf,
+				    const char *capab)
+{
+	if (os_strstr(capab, "[MAX-MPDU-7991]"))
+		conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_7991;
+	if (os_strstr(capab, "[MAX-MPDU-11454]"))
+		conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_11454;
+	if (os_strstr(capab, "[VHT160]"))
+		conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+	if (os_strstr(capab, "[VHT160-80PLUS80]"))
+		conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+	if (os_strstr(capab, "[VHT160-80PLUS80]"))
+		conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+	if (os_strstr(capab, "[RXLDPC]"))
+		conf->vht_capab |= VHT_CAP_RXLDPC;
+	if (os_strstr(capab, "[SHORT-GI-80]"))
+		conf->vht_capab |= VHT_CAP_SHORT_GI_80;
+	if (os_strstr(capab, "[SHORT-GI-160]"))
+		conf->vht_capab |= VHT_CAP_SHORT_GI_160;
+	if (os_strstr(capab, "[TX-STBC-2BY1]"))
+		conf->vht_capab |= VHT_CAP_TXSTBC;
+	if (os_strstr(capab, "[RX-STBC-1]"))
+		conf->vht_capab |= VHT_CAP_RXSTBC_1;
+	if (os_strstr(capab, "[RX-STBC-12]"))
+		conf->vht_capab |= VHT_CAP_RXSTBC_2;
+	if (os_strstr(capab, "[RX-STBC-123]"))
+		conf->vht_capab |= VHT_CAP_RXSTBC_3;
+	if (os_strstr(capab, "[RX-STBC-1234]"))
+		conf->vht_capab |= VHT_CAP_RXSTBC_4;
+	if (os_strstr(capab, "[SU-BEAMFORMER]"))
+		conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
+	if (os_strstr(capab, "[SU-BEAMFORMEE]"))
+		conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+	if (os_strstr(capab, "[BF-ANTENNA-2]") &&
+	    (conf->vht_capab & VHT_CAP_MU_BEAMFORMER_CAPABLE))
+		conf->vht_capab |= VHT_CAP_BEAMFORMER_ANTENNAS_MAX;
+	if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
+	    (conf->vht_capab & VHT_CAP_MU_BEAMFORMER_CAPABLE))
+		conf->vht_capab |= VHT_CAP_SOUNDING_DIMENTION_MAX;
+	if (os_strstr(capab, "[MU-BEAMFORMER]"))
+		conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
+	if (os_strstr(capab, "[MU-BEAMFORMEE]"))
+		conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+	if (os_strstr(capab, "[VHT-TXOP-PS]"))
+		conf->vht_capab |= VHT_CAP_VHT_TXOP_PS;
+	if (os_strstr(capab, "[HTC-VHT]"))
+		conf->vht_capab |= VHT_CAP_HTC_VHT;
+	if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP0]"))
+		conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT;
+	if (os_strstr(capab, "[VHT-LINK-ADAPT2]") &&
+	    (conf->vht_capab & VHT_CAP_HTC_VHT))
+		conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB;
+	if (os_strstr(capab, "[VHT-LINK-ADAPT3]") &&
+	    (conf->vht_capab & VHT_CAP_HTC_VHT))
+		conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
+	if (os_strstr(capab, "[RX-ANTENNA-PATTERN]"))
+		conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
+	if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
+		conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
+	return 0;
+}
+#endif /* CONFIG_IEEE80211AC */
+
+
 static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
 				    struct hostapd_config *conf)
 {
@@ -1090,6 +1179,12 @@
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_IEEE80211N
+	if (conf->ieee80211n && conf->hw_mode == HOSTAPD_MODE_IEEE80211B) {
+		bss->disable_11n = 1;
+		wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not "
+			   "allowed, disabling HT capabilites");
+	}
+
 	if (conf->ieee80211n &&
 	    bss->ssid.security_policy == SECURITY_STATIC_WEP) {
 		bss->disable_11n = 1;
@@ -1099,10 +1194,10 @@
 
 	if (conf->ieee80211n && bss->wpa &&
 	    !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
-	    !(bss->rsn_pairwise & WPA_CIPHER_CCMP)) {
+	    !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))) {
 		bss->disable_11n = 1;
 		wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 "
-			   "requires CCMP to be enabled, disabling HT "
+			   "requires CCMP/GCMP to be enabled, disabling HT "
 			   "capabilities");
 	}
 #endif /* CONFIG_IEEE80211N */
@@ -1119,8 +1214,27 @@
 			   "disabled");
 		bss->wps_state = 0;
 	}
+
+	if (bss->wps_state && bss->wpa &&
+	    (!(bss->wpa & 2) ||
+	     !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) {
+		wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
+			   "WPA2/CCMP forced WPS to be disabled");
+		bss->wps_state = 0;
+	}
 #endif /* CONFIG_WPS2 */
 
+#ifdef CONFIG_HS20
+	if (bss->hs20 &&
+	    (!(bss->wpa & 2) ||
+	     !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) {
+		wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP "
+			   "configuration is required for Hotspot 2.0 "
+			   "functionality");
+		return -1;
+	}
+#endif /* CONFIG_HS20 */
+
 	return 0;
 }
 
@@ -1161,9 +1275,9 @@
 	}
 	len /= 2;
 
-	rc = os_realloc(bss->roaming_consortium,
-			sizeof(struct hostapd_roaming_consortium) *
-			(bss->roaming_consortium_count + 1));
+	rc = os_realloc_array(bss->roaming_consortium,
+			      bss->roaming_consortium_count + 1,
+			      sizeof(struct hostapd_roaming_consortium));
 	if (rc == NULL)
 		return -1;
 
@@ -1175,76 +1289,421 @@
 
 	return 0;
 }
+
+
+static int parse_lang_string(struct hostapd_lang_string **array,
+			     unsigned int *count, char *pos)
+{
+	char *sep;
+	size_t clen, nlen;
+	struct hostapd_lang_string *ls;
+
+	sep = os_strchr(pos, ':');
+	if (sep == NULL)
+		return -1;
+	*sep++ = '\0';
+
+	clen = os_strlen(pos);
+	if (clen < 2)
+		return -1;
+	nlen = os_strlen(sep);
+	if (nlen > 252)
+		return -1;
+
+	ls = os_realloc_array(*array, *count + 1,
+			      sizeof(struct hostapd_lang_string));
+	if (ls == NULL)
+		return -1;
+
+	*array = ls;
+	ls = &(*array)[*count];
+	(*count)++;
+
+	os_memset(ls->lang, 0, sizeof(ls->lang));
+	os_memcpy(ls->lang, pos, clen);
+	ls->name_len = nlen;
+	os_memcpy(ls->name, sep, nlen);
+
+	return 0;
+}
+
+
+static int parse_venue_name(struct hostapd_bss_config *bss, char *pos,
+			    int line)
+{
+	if (parse_lang_string(&bss->venue_name, &bss->venue_name_count, pos)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid venue_name '%s'",
+			   line, pos);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf,
+			       int line)
+{
+	size_t count;
+	char *pos;
+	u8 *info = NULL, *ipos;
+
+	/* format: <MCC1,MNC1>[;<MCC2,MNC2>][;...] */
+
+	count = 1;
+	for (pos = buf; *pos; pos++) {
+		if ((*pos < '0' && *pos > '9') && *pos != ';' && *pos != ',')
+			goto fail;
+		if (*pos == ';')
+			count++;
+	}
+	if (1 + count * 3 > 0x7f)
+		goto fail;
+
+	info = os_zalloc(2 + 3 + count * 3);
+	if (info == NULL)
+		return -1;
+
+	ipos = info;
+	*ipos++ = 0; /* GUD - Version 1 */
+	*ipos++ = 3 + count * 3; /* User Data Header Length (UDHL) */
+	*ipos++ = 0; /* PLMN List IEI */
+	/* ext(b8) | Length of PLMN List value contents(b7..1) */
+	*ipos++ = 1 + count * 3;
+	*ipos++ = count; /* Number of PLMNs */
+
+	pos = buf;
+	while (pos && *pos) {
+		char *mcc, *mnc;
+		size_t mnc_len;
+
+		mcc = pos;
+		mnc = os_strchr(pos, ',');
+		if (mnc == NULL)
+			goto fail;
+		*mnc++ = '\0';
+		pos = os_strchr(mnc, ';');
+		if (pos)
+			*pos++ = '\0';
+
+		mnc_len = os_strlen(mnc);
+		if (os_strlen(mcc) != 3 || (mnc_len != 2 && mnc_len != 3))
+			goto fail;
+
+		/* BC coded MCC,MNC */
+		/* MCC digit 2 | MCC digit 1 */
+		*ipos++ = ((mcc[1] - '0') << 4) | (mcc[0] - '0');
+		/* MNC digit 3 | MCC digit 3 */
+		*ipos++ = (((mnc_len == 2) ? 0xf0 : ((mnc[2] - '0') << 4))) |
+			(mcc[2] - '0');
+		/* MNC digit 2 | MNC digit 1 */
+		*ipos++ = ((mnc[1] - '0') << 4) | (mnc[0] - '0');
+	}
+
+	os_free(bss->anqp_3gpp_cell_net);
+	bss->anqp_3gpp_cell_net = info;
+	bss->anqp_3gpp_cell_net_len = 2 + 3 + 3 * count;
+	wpa_hexdump(MSG_MSGDUMP, "3GPP Cellular Network information",
+		    bss->anqp_3gpp_cell_net, bss->anqp_3gpp_cell_net_len);
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_ERROR, "Line %d: Invalid anqp_3gpp_cell_net: %s",
+		   line, buf);
+	os_free(info);
+	return -1;
+}
+
+
+static int parse_nai_realm(struct hostapd_bss_config *bss, char *buf, int line)
+{
+	struct hostapd_nai_realm_data *realm;
+	size_t i, j, len;
+	int *offsets;
+	char *pos, *end, *rpos;
+
+	offsets = os_calloc(bss->nai_realm_count * MAX_NAI_REALMS,
+			    sizeof(int));
+	if (offsets == NULL)
+		return -1;
+
+	for (i = 0; i < bss->nai_realm_count; i++) {
+		realm = &bss->nai_realm_data[i];
+		for (j = 0; j < MAX_NAI_REALMS; j++) {
+			offsets[i * MAX_NAI_REALMS + j] =
+				realm->realm[j] ?
+				realm->realm[j] - realm->realm_buf : -1;
+		}
+	}
+
+	realm = os_realloc_array(bss->nai_realm_data, bss->nai_realm_count + 1,
+				 sizeof(struct hostapd_nai_realm_data));
+	if (realm == NULL) {
+		os_free(offsets);
+		return -1;
+	}
+	bss->nai_realm_data = realm;
+
+	/* patch the pointers after realloc */
+	for (i = 0; i < bss->nai_realm_count; i++) {
+		realm = &bss->nai_realm_data[i];
+		for (j = 0; j < MAX_NAI_REALMS; j++) {
+			int offs = offsets[i * MAX_NAI_REALMS + j];
+			if (offs >= 0)
+				realm->realm[j] = realm->realm_buf + offs;
+			else
+				realm->realm[j] = NULL;
+		}
+	}
+	os_free(offsets);
+
+	realm = &bss->nai_realm_data[bss->nai_realm_count];
+	os_memset(realm, 0, sizeof(*realm));
+
+	pos = buf;
+	realm->encoding = atoi(pos);
+	pos = os_strchr(pos, ',');
+	if (pos == NULL)
+		goto fail;
+	pos++;
+
+	end = os_strchr(pos, ',');
+	if (end) {
+		len = end - pos;
+		*end = '\0';
+	} else {
+		len = os_strlen(pos);
+	}
+
+	if (len > MAX_NAI_REALMLEN) {
+		wpa_printf(MSG_ERROR, "Too long a realm string (%d > max %d "
+			   "characters)", (int) len, MAX_NAI_REALMLEN);
+		goto fail;
+	}
+	os_memcpy(realm->realm_buf, pos, len);
+
+	if (end)
+		pos = end + 1;
+	else
+		pos = NULL;
+
+	while (pos && *pos) {
+		struct hostapd_nai_realm_eap *eap;
+
+		if (realm->eap_method_count >= MAX_NAI_EAP_METHODS) {
+			wpa_printf(MSG_ERROR, "Too many EAP methods");
+			goto fail;
+		}
+
+		eap = &realm->eap_method[realm->eap_method_count];
+		realm->eap_method_count++;
+
+		end = os_strchr(pos, ',');
+		if (end == NULL)
+			end = pos + os_strlen(pos);
+
+		eap->eap_method = atoi(pos);
+		for (;;) {
+			pos = os_strchr(pos, '[');
+			if (pos == NULL || pos > end)
+				break;
+			pos++;
+			if (eap->num_auths >= MAX_NAI_AUTH_TYPES) {
+				wpa_printf(MSG_ERROR, "Too many auth params");
+				goto fail;
+			}
+			eap->auth_id[eap->num_auths] = atoi(pos);
+			pos = os_strchr(pos, ':');
+			if (pos == NULL || pos > end)
+				goto fail;
+			pos++;
+			eap->auth_val[eap->num_auths] = atoi(pos);
+			pos = os_strchr(pos, ']');
+			if (pos == NULL || pos > end)
+				goto fail;
+			pos++;
+			eap->num_auths++;
+		}
+
+		if (*end != ',')
+			break;
+
+		pos = end + 1;
+	}
+
+	/* Split realm list into null terminated realms */
+	rpos = realm->realm_buf;
+	i = 0;
+	while (*rpos) {
+		if (i >= MAX_NAI_REALMS) {
+			wpa_printf(MSG_ERROR, "Too many realms");
+			goto fail;
+		}
+		realm->realm[i++] = rpos;
+		rpos = os_strchr(rpos, ';');
+		if (rpos == NULL)
+			break;
+		*rpos++ = '\0';
+	}
+
+	bss->nai_realm_count++;
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_ERROR, "Line %d: invalid nai_realm '%s'", line, buf);
+	return -1;
+}
+
 #endif /* CONFIG_INTERWORKING */
 
 
-/**
- * hostapd_config_read - Read and parse a configuration file
- * @fname: Configuration file name (including path, if needed)
- * Returns: Allocated configuration data structure
- */
-struct hostapd_config * hostapd_config_read(const char *fname)
+#ifdef CONFIG_HS20
+static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
+				 int line)
 {
-	struct hostapd_config *conf;
-	struct hostapd_bss_config *bss;
-	FILE *f;
-	char buf[256], *pos;
-	int line = 0;
+	u8 *conn_cap;
+	char *pos;
+
+	if (bss->hs20_connection_capability_len >= 0xfff0)
+		return -1;
+
+	conn_cap = os_realloc(bss->hs20_connection_capability,
+			      bss->hs20_connection_capability_len + 4);
+	if (conn_cap == NULL)
+		return -1;
+
+	bss->hs20_connection_capability = conn_cap;
+	conn_cap += bss->hs20_connection_capability_len;
+	pos = buf;
+	conn_cap[0] = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	WPA_PUT_LE16(conn_cap + 1, atoi(pos));
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	conn_cap[3] = atoi(pos);
+	bss->hs20_connection_capability_len += 4;
+
+	return 0;
+}
+
+
+static int hs20_parse_wan_metrics(struct hostapd_bss_config *bss, char *buf,
+				  int line)
+{
+	u8 *wan_metrics;
+	char *pos;
+
+	/* <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD> */
+
+	wan_metrics = os_zalloc(13);
+	if (wan_metrics == NULL)
+		return -1;
+
+	pos = buf;
+	/* WAN Info */
+	if (hexstr2bin(pos, wan_metrics, 1) < 0)
+		goto fail;
+	pos += 2;
+	if (*pos != ':')
+		goto fail;
+	pos++;
+
+	/* Downlink Speed */
+	WPA_PUT_LE32(wan_metrics + 1, atoi(pos));
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		goto fail;
+	pos++;
+
+	/* Uplink Speed */
+	WPA_PUT_LE32(wan_metrics + 5, atoi(pos));
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		goto fail;
+	pos++;
+
+	/* Downlink Load */
+	wan_metrics[9] = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		goto fail;
+	pos++;
+
+	/* Uplink Load */
+	wan_metrics[10] = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		goto fail;
+	pos++;
+
+	/* LMD */
+	WPA_PUT_LE16(wan_metrics + 11, atoi(pos));
+
+	os_free(bss->hs20_wan_metrics);
+	bss->hs20_wan_metrics = wan_metrics;
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'",
+		   line, pos);
+	os_free(wan_metrics);
+	return -1;
+}
+
+
+static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss,
+					 char *pos, int line)
+{
+	if (parse_lang_string(&bss->hs20_oper_friendly_name,
+			      &bss->hs20_oper_friendly_name_count, pos)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid "
+			   "hs20_oper_friendly_name '%s'", line, pos);
+		return -1;
+	}
+	return 0;
+}
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_WPS_NFC
+static struct wpabuf * hostapd_parse_bin(const char *buf)
+{
+	size_t len;
+	struct wpabuf *ret;
+
+	len = os_strlen(buf);
+	if (len & 0x01)
+		return NULL;
+	len /= 2;
+
+	ret = wpabuf_alloc(len);
+	if (ret == NULL)
+		return NULL;
+
+	if (hexstr2bin(buf, wpabuf_put(ret, len), len)) {
+		wpabuf_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+#endif /* CONFIG_WPS_NFC */
+
+
+static int hostapd_config_fill(struct hostapd_config *conf,
+			       struct hostapd_bss_config *bss,
+			       char *buf, char *pos, int line)
+{
 	int errors = 0;
-	int pairwise;
-	size_t i;
 
-	f = fopen(fname, "r");
-	if (f == NULL) {
-		wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
-			   "for reading.", fname);
-		return NULL;
-	}
-
-	conf = hostapd_config_defaults();
-	if (conf == NULL) {
-		fclose(f);
-		return NULL;
-	}
-
-	/* set default driver based on configuration */
-	conf->driver = wpa_drivers[0];
-	if (conf->driver == NULL) {
-		wpa_printf(MSG_ERROR, "No driver wrappers registered!");
-		hostapd_config_free(conf);
-		fclose(f);
-		return NULL;
-	}
-
-	bss = conf->last_bss = conf->bss;
-
-	while (fgets(buf, sizeof(buf), f)) {
-		bss = conf->last_bss;
-		line++;
-
-		if (buf[0] == '#')
-			continue;
-		pos = buf;
-		while (*pos != '\0') {
-			if (*pos == '\n') {
-				*pos = '\0';
-				break;
-			}
-			pos++;
-		}
-		if (buf[0] == '\0')
-			continue;
-
-		pos = os_strchr(buf, '=');
-		if (pos == NULL) {
-			wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'",
-				   line, buf);
-			errors++;
-			continue;
-		}
-		*pos = '\0';
-		pos++;
-
+	{
 		if (os_strcmp(buf, "interface") == 0) {
 			os_strlcpy(conf->bss[0].iface, pos,
 				   sizeof(conf->bss[0].iface));
@@ -1293,9 +1752,22 @@
 			} else {
 				os_memcpy(bss->ssid.ssid, pos,
 					  bss->ssid.ssid_len);
-				bss->ssid.ssid[bss->ssid.ssid_len] = '\0';
 				bss->ssid.ssid_set = 1;
 			}
+		} else if (os_strcmp(buf, "ssid2") == 0) {
+			size_t slen;
+			char *str = wpa_config_parse_string(pos, &slen);
+			if (str == NULL || slen < 1 ||
+				   slen > HOSTAPD_MAX_SSID_LEN) {
+				wpa_printf(MSG_ERROR, "Line %d: invalid SSID "
+					   "'%s'", line, pos);
+				errors++;
+			} else {
+				os_memcpy(bss->ssid.ssid, str, slen);
+				bss->ssid.ssid_len = slen;
+				bss->ssid.ssid_set = 1;
+			}
+			os_free(str);
 		} else if (os_strcmp(buf, "macaddr_acl") == 0) {
 			bss->macaddr_acl = atoi(pos);
 			if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
@@ -1446,7 +1918,7 @@
 					   "allocate memory for "
 					   "eap_req_id_text", line);
 				errors++;
-				continue;
+				return errors;
 			}
 			bss->eap_req_id_text_len =
 				os_strlen(bss->eap_req_id_text);
@@ -1566,6 +2038,51 @@
 		} else if (os_strcmp(buf, "radius_acct_interim_interval") == 0)
 		{
 			bss->acct_interim_interval = atoi(pos);
+		} else if (os_strcmp(buf, "radius_request_cui") == 0) {
+			bss->radius_request_cui = atoi(pos);
+		} else if (os_strcmp(buf, "radius_auth_req_attr") == 0) {
+			struct hostapd_radius_attr *attr, *a;
+			attr = hostapd_parse_radius_attr(pos);
+			if (attr == NULL) {
+				wpa_printf(MSG_ERROR, "Line %d: invalid "
+					   "radius_auth_req_attr", line);
+				errors++;
+			} else if (bss->radius_auth_req_attr == NULL) {
+				bss->radius_auth_req_attr = attr;
+			} else {
+				a = bss->radius_auth_req_attr;
+				while (a->next)
+					a = a->next;
+				a->next = attr;
+			}
+		} else if (os_strcmp(buf, "radius_acct_req_attr") == 0) {
+			struct hostapd_radius_attr *attr, *a;
+			attr = hostapd_parse_radius_attr(pos);
+			if (attr == NULL) {
+				wpa_printf(MSG_ERROR, "Line %d: invalid "
+					   "radius_acct_req_attr", line);
+				errors++;
+			} else if (bss->radius_acct_req_attr == NULL) {
+				bss->radius_acct_req_attr = attr;
+			} else {
+				a = bss->radius_acct_req_attr;
+				while (a->next)
+					a = a->next;
+				a->next = attr;
+			}
+		} else if (os_strcmp(buf, "radius_das_port") == 0) {
+			bss->radius_das_port = atoi(pos);
+		} else if (os_strcmp(buf, "radius_das_client") == 0) {
+			if (hostapd_parse_das_client(bss, pos) < 0) {
+				wpa_printf(MSG_ERROR, "Line %d: invalid "
+					   "DAS client", line);
+				errors++;
+			}
+		} else if (os_strcmp(buf, "radius_das_time_window") == 0) {
+			bss->radius_das_time_window = atoi(pos);
+		} else if (os_strcmp(buf, "radius_das_require_event_timestamp")
+			   == 0) {
+			bss->radius_das_require_event_timestamp = atoi(pos);
 #endif /* CONFIG_NO_RADIUS */
 		} else if (os_strcmp(buf, "auth_algs") == 0) {
 			bss->auth_algs = atoi(pos);
@@ -1605,6 +2122,8 @@
 			} else {
 				os_free(bss->ssid.wpa_passphrase);
 				bss->ssid.wpa_passphrase = os_strdup(pos);
+				os_free(bss->ssid.wpa_psk);
+				bss->ssid.wpa_psk = NULL;
 			}
 		} else if (os_strcmp(buf, "wpa_psk") == 0) {
 			os_free(bss->ssid.wpa_psk);
@@ -1620,6 +2139,8 @@
 				errors++;
 			} else {
 				bss->ssid.wpa_psk->group = 1;
+				os_free(bss->ssid.wpa_passphrase);
+				bss->ssid.wpa_passphrase = NULL;
 			}
 		} else if (os_strcmp(buf, "wpa_psk_file") == 0) {
 			os_free(bss->ssid.wpa_psk_file);
@@ -1690,7 +2211,7 @@
 				wpa_printf(MSG_DEBUG, "Line %d: Invalid "
 					   "mobility_domain '%s'", line, pos);
 				errors++;
-				continue;
+				return errors;
 			}
 		} else if (os_strcmp(buf, "r1_key_holder") == 0) {
 			if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN ||
@@ -1699,7 +2220,7 @@
 				wpa_printf(MSG_DEBUG, "Line %d: Invalid "
 					   "r1_key_holder '%s'", line, pos);
 				errors++;
-				continue;
+				return errors;
 			}
 		} else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
 			bss->r0_key_lifetime = atoi(pos);
@@ -1710,14 +2231,14 @@
 				wpa_printf(MSG_DEBUG, "Line %d: Invalid "
 					   "r0kh '%s'", line, pos);
 				errors++;
-				continue;
+				return errors;
 			}
 		} else if (os_strcmp(buf, "r1kh") == 0) {
 			if (add_r1kh(bss, pos) < 0) {
 				wpa_printf(MSG_DEBUG, "Line %d: Invalid "
 					   "r1kh '%s'", line, pos);
 				errors++;
-				continue;
+				return errors;
 			}
 		} else if (os_strcmp(buf, "pmk_r1_push") == 0) {
 			bss->pmk_r1_push = atoi(pos);
@@ -1741,7 +2262,7 @@
 				wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
 					   " (from group name '%s')",
 					   bss->ctrl_interface_gid, group);
-				continue;
+				return errors;
 			}
 
 			/* Group name not found - try to parse this as gid */
@@ -1750,7 +2271,7 @@
 				wpa_printf(MSG_DEBUG, "Line %d: Invalid group "
 					   "'%s'", line, group);
 				errors++;
-				continue;
+				return errors;
 			}
 			bss->ctrl_interface_gid_set = 1;
 			wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
@@ -1894,6 +2415,15 @@
 					   "read VLAN file '%s'", line, pos);
 				errors++;
 			}
+		} else if (os_strcmp(buf, "vlan_naming") == 0) {
+			bss->ssid.vlan_naming = atoi(pos);
+			if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END ||
+			    bss->ssid.vlan_naming < 0) {
+				wpa_printf(MSG_ERROR, "Line %d: invalid "
+					   "naming scheme %d", line,
+                                           bss->ssid.vlan_naming);
+				errors++;
+                        }
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
 		} else if (os_strcmp(buf, "vlan_tagged_interface") == 0) {
 			bss->ssid.vlan_tagged_interface = os_strdup(pos);
@@ -1916,7 +2446,8 @@
 			bss->wmm_uapsd = atoi(pos);
 		} else if (os_strncmp(buf, "wme_ac_", 7) == 0 ||
 			   os_strncmp(buf, "wmm_ac_", 7) == 0) {
-			if (hostapd_config_wmm_ac(conf, buf, pos)) {
+			if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf,
+						  pos)) {
 				wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
 					   "ac item", line);
 				errors++;
@@ -1965,6 +2496,23 @@
 		} else if (os_strcmp(buf, "require_ht") == 0) {
 			conf->require_ht = atoi(pos);
 #endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+		} else if (os_strcmp(buf, "ieee80211ac") == 0) {
+			conf->ieee80211ac = atoi(pos);
+		} else if (os_strcmp(buf, "vht_capab") == 0) {
+			if (hostapd_config_vht_capab(conf, pos) < 0) {
+				wpa_printf(MSG_ERROR, "Line %d: invalid "
+					   "vht_capab", line);
+				errors++;
+			}
+		} else if (os_strcmp(buf, "require_vht") == 0) {
+			conf->require_vht = atoi(pos);
+		} else if (os_strcmp(buf, "vht_oper_chwidth") == 0) {
+			conf->vht_oper_chwidth = atoi(pos);
+		} else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0)
+		{
+			conf->vht_oper_centr_freq_seg0_idx = atoi(pos);
+#endif /* CONFIG_IEEE80211AC */
 		} else if (os_strcmp(buf, "max_listen_interval") == 0) {
 			bss->max_listen_interval = atoi(pos);
 		} else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
@@ -2088,6 +2636,25 @@
 			bss->upc = os_strdup(pos);
 		} else if (os_strcmp(buf, "pbc_in_m1") == 0) {
 			bss->pbc_in_m1 = atoi(pos);
+#ifdef CONFIG_WPS_NFC
+		} else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) {
+			bss->wps_nfc_dev_pw_id = atoi(pos);
+			if (bss->wps_nfc_dev_pw_id < 0x10 ||
+			    bss->wps_nfc_dev_pw_id > 0xffff) {
+				wpa_printf(MSG_ERROR, "Line %d: Invalid "
+					   "wps_nfc_dev_pw_id value", line);
+				errors++;
+			}
+		} else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
+			wpabuf_free(bss->wps_nfc_dh_pubkey);
+			bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos);
+		} else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
+			wpabuf_free(bss->wps_nfc_dh_privkey);
+			bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos);
+		} else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
+			wpabuf_free(bss->wps_nfc_dev_pw);
+			bss->wps_nfc_dev_pw = hostapd_parse_bin(pos);
+#endif /* CONFIG_WPS_NFC */
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P_MANAGER
 		} else if (os_strcmp(buf, "manage_p2p") == 0) {
@@ -2129,7 +2696,7 @@
 				wpa_printf(MSG_DEBUG, "Line %d: invalid "
 					   "time_zone", line);
 				errors++;
-				continue;
+				return errors;
 			}
 			os_free(bss->time_zone);
 			bss->time_zone = os_strdup(pos);
@@ -2169,12 +2736,180 @@
 		} else if (os_strcmp(buf, "roaming_consortium") == 0) {
 			if (parse_roaming_consortium(bss, pos, line) < 0)
 				errors++;
+		} else if (os_strcmp(buf, "venue_name") == 0) {
+			if (parse_venue_name(bss, pos, line) < 0)
+				errors++;
+		} else if (os_strcmp(buf, "network_auth_type") == 0) {
+			u8 auth_type;
+			u16 redirect_url_len;
+			if (hexstr2bin(pos, &auth_type, 1)) {
+				wpa_printf(MSG_ERROR, "Line %d: Invalid "
+					   "network_auth_type '%s'",
+					   line, pos);
+				errors++;
+				return errors;
+			}
+			if (auth_type == 0 || auth_type == 2)
+				redirect_url_len = os_strlen(pos + 2);
+			else
+				redirect_url_len = 0;
+			os_free(bss->network_auth_type);
+			bss->network_auth_type =
+				os_malloc(redirect_url_len + 3 + 1);
+			if (bss->network_auth_type == NULL) {
+				errors++;
+				return errors;
+			}
+			*bss->network_auth_type = auth_type;
+			WPA_PUT_LE16(bss->network_auth_type + 1,
+				     redirect_url_len);
+			if (redirect_url_len)
+				os_memcpy(bss->network_auth_type + 3,
+					  pos + 2, redirect_url_len);
+			bss->network_auth_type_len = 3 + redirect_url_len;
+		} else if (os_strcmp(buf, "ipaddr_type_availability") == 0) {
+			if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1))
+			{
+				wpa_printf(MSG_ERROR, "Line %d: Invalid "
+					   "ipaddr_type_availability '%s'",
+					   line, pos);
+				bss->ipaddr_type_configured = 0;
+				errors++;
+				return errors;
+			}
+			bss->ipaddr_type_configured = 1;
+		} else if (os_strcmp(buf, "domain_name") == 0) {
+			int j, num_domains, domain_len, domain_list_len = 0;
+			char *tok_start, *tok_prev;
+			u8 *domain_list, *domain_ptr;
+
+			domain_list_len = os_strlen(pos) + 1;
+			domain_list = os_malloc(domain_list_len);
+			if (domain_list == NULL) {
+				errors++;
+				return errors;
+			}
+
+			domain_ptr = domain_list;
+			tok_prev = pos;
+			num_domains = 1;
+			while ((tok_prev = os_strchr(tok_prev, ','))) {
+				num_domains++;
+				tok_prev++;
+			}
+			tok_prev = pos;
+			for (j = 0; j < num_domains; j++) {
+				tok_start = os_strchr(tok_prev, ',');
+				if (tok_start) {
+					domain_len = tok_start - tok_prev;
+					*domain_ptr = domain_len;
+					os_memcpy(domain_ptr + 1, tok_prev,
+						  domain_len);
+					domain_ptr += domain_len + 1;
+					tok_prev = ++tok_start;
+				} else {
+					domain_len = os_strlen(tok_prev);
+					*domain_ptr = domain_len;
+					os_memcpy(domain_ptr + 1, tok_prev,
+						  domain_len);
+					domain_ptr += domain_len + 1;
+				}
+			}
+
+			os_free(bss->domain_name);
+			bss->domain_name = domain_list;
+			bss->domain_name_len = domain_list_len;
+		} else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) {
+			if (parse_3gpp_cell_net(bss, pos, line) < 0)
+				errors++;
+		} else if (os_strcmp(buf, "nai_realm") == 0) {
+			if (parse_nai_realm(bss, pos, line) < 0)
+				errors++;
+		} else if (os_strcmp(buf, "gas_frag_limit") == 0) {
+			bss->gas_frag_limit = atoi(pos);
+		} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
+			bss->gas_comeback_delay = atoi(pos);
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_RADIUS_TEST
 		} else if (os_strcmp(buf, "dump_msk_file") == 0) {
 			os_free(bss->dump_msk_file);
 			bss->dump_msk_file = os_strdup(pos);
 #endif /* CONFIG_RADIUS_TEST */
+#ifdef CONFIG_HS20
+		} else if (os_strcmp(buf, "hs20") == 0) {
+			bss->hs20 = atoi(pos);
+		} else if (os_strcmp(buf, "disable_dgaf") == 0) {
+			bss->disable_dgaf = atoi(pos);
+		} else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) {
+			if (hs20_parse_oper_friendly_name(bss, pos, line) < 0)
+				errors++;
+		} else if (os_strcmp(buf, "hs20_wan_metrics") == 0) {
+			if (hs20_parse_wan_metrics(bss, pos, line) < 0) {
+				errors++;
+				return errors;
+			}
+		} else if (os_strcmp(buf, "hs20_conn_capab") == 0) {
+			if (hs20_parse_conn_capab(bss, pos, line) < 0) {
+				errors++;
+				return errors;
+			}
+		} else if (os_strcmp(buf, "hs20_operating_class") == 0) {
+			u8 *oper_class;
+			size_t oper_class_len;
+			oper_class_len = os_strlen(pos);
+			if (oper_class_len < 2 || (oper_class_len & 0x01)) {
+				wpa_printf(MSG_ERROR, "Line %d: Invalid "
+					   "hs20_operating_class '%s'",
+					   line, pos);
+				errors++;
+				return errors;
+			}
+			oper_class_len /= 2;
+			oper_class = os_malloc(oper_class_len);
+			if (oper_class == NULL) {
+				errors++;
+				return errors;
+			}
+			if (hexstr2bin(pos, oper_class, oper_class_len)) {
+				wpa_printf(MSG_ERROR, "Line %d: Invalid "
+					   "hs20_operating_class '%s'",
+					   line, pos);
+				os_free(oper_class);
+				errors++;
+				return errors;
+			}
+			os_free(bss->hs20_operating_class);
+			bss->hs20_operating_class = oper_class;
+			bss->hs20_operating_class_len = oper_class_len;
+#endif /* CONFIG_HS20 */
+		} else if (os_strcmp(buf, "vendor_elements") == 0) {
+			struct wpabuf *elems;
+			size_t len = os_strlen(pos);
+			if (len & 0x01) {
+				wpa_printf(MSG_ERROR, "Line %d: Invalid "
+					   "vendor_elements '%s'", line, pos);
+				return 1;
+			}
+			len /= 2;
+			if (len == 0) {
+				wpabuf_free(bss->vendor_elements);
+				bss->vendor_elements = NULL;
+				return 0;
+			}
+
+			elems = wpabuf_alloc(len);
+			if (elems == NULL)
+				return 1;
+
+			if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
+				wpabuf_free(elems);
+				wpa_printf(MSG_ERROR, "Line %d: Invalid "
+					   "vendor_elements '%s'", line, pos);
+				return 1;
+			}
+
+			wpabuf_free(bss->vendor_elements);
+			bss->vendor_elements = elems;
 		} else {
 			wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
 				   "item '%s'", line, buf);
@@ -2182,64 +2917,144 @@
 		}
 	}
 
+	return errors;
+}
+
+
+static void hostapd_set_security_params(struct hostapd_bss_config *bss)
+{
+	int pairwise;
+
+	if (bss->individual_wep_key_len == 0) {
+		/* individual keys are not use; can use key idx0 for
+		 * broadcast keys */
+		bss->broadcast_key_idx_min = 0;
+	}
+
+	/* Select group cipher based on the enabled pairwise cipher
+	 * suites */
+	pairwise = 0;
+	if (bss->wpa & 1)
+		pairwise |= bss->wpa_pairwise;
+	if (bss->wpa & 2) {
+		if (bss->rsn_pairwise == 0)
+			bss->rsn_pairwise = bss->wpa_pairwise;
+		pairwise |= bss->rsn_pairwise;
+	}
+	if (pairwise & WPA_CIPHER_TKIP)
+		bss->wpa_group = WPA_CIPHER_TKIP;
+	else if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ==
+		 WPA_CIPHER_GCMP)
+		bss->wpa_group = WPA_CIPHER_GCMP;
+	else
+		bss->wpa_group = WPA_CIPHER_CCMP;
+
+	bss->radius->auth_server = bss->radius->auth_servers;
+	bss->radius->acct_server = bss->radius->acct_servers;
+
+	if (bss->wpa && bss->ieee802_1x) {
+		bss->ssid.security_policy = SECURITY_WPA;
+	} else if (bss->wpa) {
+		bss->ssid.security_policy = SECURITY_WPA_PSK;
+	} else if (bss->ieee802_1x) {
+		int cipher = WPA_CIPHER_NONE;
+		bss->ssid.security_policy = SECURITY_IEEE_802_1X;
+		bss->ssid.wep.default_len = bss->default_wep_key_len;
+		if (bss->default_wep_key_len)
+			cipher = bss->default_wep_key_len >= 13 ?
+				WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
+		bss->wpa_group = cipher;
+		bss->wpa_pairwise = cipher;
+		bss->rsn_pairwise = cipher;
+	} else if (bss->ssid.wep.keys_set) {
+		int cipher = WPA_CIPHER_WEP40;
+		if (bss->ssid.wep.len[0] >= 13)
+			cipher = WPA_CIPHER_WEP104;
+		bss->ssid.security_policy = SECURITY_STATIC_WEP;
+		bss->wpa_group = cipher;
+		bss->wpa_pairwise = cipher;
+		bss->rsn_pairwise = cipher;
+	} else {
+		bss->ssid.security_policy = SECURITY_PLAINTEXT;
+		bss->wpa_group = WPA_CIPHER_NONE;
+		bss->wpa_pairwise = WPA_CIPHER_NONE;
+		bss->rsn_pairwise = WPA_CIPHER_NONE;
+	}
+}
+
+
+/**
+ * hostapd_config_read - Read and parse a configuration file
+ * @fname: Configuration file name (including path, if needed)
+ * Returns: Allocated configuration data structure
+ */
+struct hostapd_config * hostapd_config_read(const char *fname)
+{
+	struct hostapd_config *conf;
+	struct hostapd_bss_config *bss;
+	FILE *f;
+	char buf[512], *pos;
+	int line = 0;
+	int errors = 0;
+	size_t i;
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
+			   "for reading.", fname);
+		return NULL;
+	}
+
+	conf = hostapd_config_defaults();
+	if (conf == NULL) {
+		fclose(f);
+		return NULL;
+	}
+
+	/* set default driver based on configuration */
+	conf->driver = wpa_drivers[0];
+	if (conf->driver == NULL) {
+		wpa_printf(MSG_ERROR, "No driver wrappers registered!");
+		hostapd_config_free(conf);
+		fclose(f);
+		return NULL;
+	}
+
+	bss = conf->last_bss = conf->bss;
+
+	while (fgets(buf, sizeof(buf), f)) {
+		bss = conf->last_bss;
+		line++;
+
+		if (buf[0] == '#')
+			continue;
+		pos = buf;
+		while (*pos != '\0') {
+			if (*pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+			pos++;
+		}
+		if (buf[0] == '\0')
+			continue;
+
+		pos = os_strchr(buf, '=');
+		if (pos == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'",
+				   line, buf);
+			errors++;
+			continue;
+		}
+		*pos = '\0';
+		pos++;
+		errors += hostapd_config_fill(conf, bss, buf, pos, line);
+	}
+
 	fclose(f);
 
-	for (i = 0; i < conf->num_bss; i++) {
-		bss = &conf->bss[i];
-
-		if (bss->individual_wep_key_len == 0) {
-			/* individual keys are not use; can use key idx0 for
-			 * broadcast keys */
-			bss->broadcast_key_idx_min = 0;
-		}
-
-		/* Select group cipher based on the enabled pairwise cipher
-		 * suites */
-		pairwise = 0;
-		if (bss->wpa & 1)
-			pairwise |= bss->wpa_pairwise;
-		if (bss->wpa & 2) {
-			if (bss->rsn_pairwise == 0)
-				bss->rsn_pairwise = bss->wpa_pairwise;
-			pairwise |= bss->rsn_pairwise;
-		}
-		if (pairwise & WPA_CIPHER_TKIP)
-			bss->wpa_group = WPA_CIPHER_TKIP;
-		else
-			bss->wpa_group = WPA_CIPHER_CCMP;
-
-		bss->radius->auth_server = bss->radius->auth_servers;
-		bss->radius->acct_server = bss->radius->acct_servers;
-
-		if (bss->wpa && bss->ieee802_1x) {
-			bss->ssid.security_policy = SECURITY_WPA;
-		} else if (bss->wpa) {
-			bss->ssid.security_policy = SECURITY_WPA_PSK;
-		} else if (bss->ieee802_1x) {
-			int cipher = WPA_CIPHER_NONE;
-			bss->ssid.security_policy = SECURITY_IEEE_802_1X;
-			bss->ssid.wep.default_len = bss->default_wep_key_len;
-			if (bss->default_wep_key_len)
-				cipher = bss->default_wep_key_len >= 13 ?
-					WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
-			bss->wpa_group = cipher;
-			bss->wpa_pairwise = cipher;
-			bss->rsn_pairwise = cipher;
-		} else if (bss->ssid.wep.keys_set) {
-			int cipher = WPA_CIPHER_WEP40;
-			if (bss->ssid.wep.len[0] >= 13)
-				cipher = WPA_CIPHER_WEP104;
-			bss->ssid.security_policy = SECURITY_STATIC_WEP;
-			bss->wpa_group = cipher;
-			bss->wpa_pairwise = cipher;
-			bss->rsn_pairwise = cipher;
-		} else {
-			bss->ssid.security_policy = SECURITY_PLAINTEXT;
-			bss->wpa_group = WPA_CIPHER_NONE;
-			bss->wpa_pairwise = WPA_CIPHER_NONE;
-			bss->rsn_pairwise = WPA_CIPHER_NONE;
-		}
-	}
+	for (i = 0; i < conf->num_bss; i++)
+		hostapd_set_security_params(&conf->bss[i]);
 
 	if (hostapd_config_check(conf))
 		errors++;
@@ -2255,3 +3070,28 @@
 
 	return conf;
 }
+
+
+int hostapd_set_iface(struct hostapd_config *conf,
+		      struct hostapd_bss_config *bss, char *field, char *value)
+{
+	int errors;
+	size_t i;
+
+	errors = hostapd_config_fill(conf, bss, field, value, 0);
+	if (errors) {
+		wpa_printf(MSG_INFO, "Failed to set configuration field '%s' "
+			   "to value '%s'", field, value);
+		return -1;
+	}
+
+	for (i = 0; i < conf->num_bss; i++)
+		hostapd_set_security_params(&conf->bss[i]);
+
+	if (hostapd_config_check(conf)) {
+		wpa_printf(MSG_ERROR, "Configuration check failed");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/hostapd/config_file.h b/hostapd/config_file.h
index 80d182e..fba57b8 100644
--- a/hostapd/config_file.h
+++ b/hostapd/config_file.h
@@ -10,5 +10,8 @@
 #define CONFIG_FILE_H
 
 struct hostapd_config * hostapd_config_read(const char *fname);
+int hostapd_set_iface(struct hostapd_config *conf,
+		      struct hostapd_bss_config *bss, char *field,
+		      char *value);
 
 #endif /* CONFIG_FILE_H */
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 9d5a67e..1b8bede 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / UNIX domain socket -based control interface
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -31,6 +31,7 @@
 #include "ap/ap_drv_ops.h"
 #include "wps/wps_defs.h"
 #include "wps/wps.h"
+#include "config_file.h"
 #include "ctrl_iface.h"
 
 
@@ -152,173 +153,6 @@
 }
 
 
-#ifdef CONFIG_P2P_MANAGER
-static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
-				  u8 minor_reason_code, const u8 *addr)
-{
-	struct ieee80211_mgmt *mgmt;
-	int ret;
-	u8 *pos;
-
-	if (hapd->driver->send_frame == NULL)
-		return -1;
-
-	mgmt = os_zalloc(sizeof(*mgmt) + 100);
-	if (mgmt == NULL)
-		return -1;
-
-	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
-		" with minor reason code %u (stype=%u)",
-		MAC2STR(addr), minor_reason_code, stype);
-
-	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
-	os_memcpy(mgmt->da, addr, ETH_ALEN);
-	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
-	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
-	if (stype == WLAN_FC_STYPE_DEAUTH) {
-		mgmt->u.deauth.reason_code =
-			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
-		pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
-	} else {
-		mgmt->u.disassoc.reason_code =
-			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
-		pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
-	}
-
-	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-	*pos++ = 4 + 3 + 1;
-	WPA_PUT_BE24(pos, OUI_WFA);
-	pos += 3;
-	*pos++ = P2P_OUI_TYPE;
-
-	*pos++ = P2P_ATTR_MINOR_REASON_CODE;
-	WPA_PUT_LE16(pos, 1);
-	pos += 2;
-	*pos++ = minor_reason_code;
-
-	ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt,
-				       pos - (u8 *) mgmt, 1);
-	os_free(mgmt);
-
-	return ret < 0 ? -1 : 0;
-}
-#endif /* CONFIG_P2P_MANAGER */
-
-
-static int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
-					     const char *txtaddr)
-{
-	u8 addr[ETH_ALEN];
-	struct sta_info *sta;
-	const char *pos;
-
-	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
-		txtaddr);
-
-	if (hwaddr_aton(txtaddr, addr))
-		return -1;
-
-	pos = os_strstr(txtaddr, " test=");
-	if (pos) {
-		struct ieee80211_mgmt mgmt;
-		int encrypt;
-		if (hapd->driver->send_frame == NULL)
-			return -1;
-		pos += 6;
-		encrypt = atoi(pos);
-		os_memset(&mgmt, 0, sizeof(mgmt));
-		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-						  WLAN_FC_STYPE_DEAUTH);
-		os_memcpy(mgmt.da, addr, ETH_ALEN);
-		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
-		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
-		mgmt.u.deauth.reason_code =
-			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
-		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
-					     IEEE80211_HDRLEN +
-					     sizeof(mgmt.u.deauth),
-					     encrypt) < 0)
-			return -1;
-		return 0;
-	}
-
-#ifdef CONFIG_P2P_MANAGER
-	pos = os_strstr(txtaddr, " p2p=");
-	if (pos) {
-		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
-					      atoi(pos + 5), addr);
-	}
-#endif /* CONFIG_P2P_MANAGER */
-
-	hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
-	sta = ap_get_sta(hapd, addr);
-	if (sta)
-		ap_sta_deauthenticate(hapd, sta,
-				      WLAN_REASON_PREV_AUTH_NOT_VALID);
-	else if (addr[0] == 0xff)
-		hostapd_free_stas(hapd);
-
-	return 0;
-}
-
-
-static int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
-					   const char *txtaddr)
-{
-	u8 addr[ETH_ALEN];
-	struct sta_info *sta;
-	const char *pos;
-
-	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
-		txtaddr);
-
-	if (hwaddr_aton(txtaddr, addr))
-		return -1;
-
-	pos = os_strstr(txtaddr, " test=");
-	if (pos) {
-		struct ieee80211_mgmt mgmt;
-		int encrypt;
-		if (hapd->driver->send_frame == NULL)
-			return -1;
-		pos += 6;
-		encrypt = atoi(pos);
-		os_memset(&mgmt, 0, sizeof(mgmt));
-		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-						  WLAN_FC_STYPE_DISASSOC);
-		os_memcpy(mgmt.da, addr, ETH_ALEN);
-		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
-		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
-		mgmt.u.disassoc.reason_code =
-			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
-		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
-					     IEEE80211_HDRLEN +
-					     sizeof(mgmt.u.deauth),
-					     encrypt) < 0)
-			return -1;
-		return 0;
-	}
-
-#ifdef CONFIG_P2P_MANAGER
-	pos = os_strstr(txtaddr, " p2p=");
-	if (pos) {
-		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
-					      atoi(pos + 5), addr);
-	}
-#endif /* CONFIG_P2P_MANAGER */
-
-	hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
-	sta = ap_get_sta(hapd, addr);
-	if (sta)
-		ap_sta_disassociate(hapd, sta,
-				    WLAN_REASON_PREV_AUTH_NOT_VALID);
-	else if (addr[0] == 0xff)
-		hostapd_free_stas(hapd);
-
-	return 0;
-}
-
-
 #ifdef CONFIG_IEEE80211W
 #ifdef NEED_AP_MLME
 static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
@@ -440,6 +274,111 @@
 #endif /* CONFIG_WPS_OOB */
 
 
+#ifdef CONFIG_WPS_NFC
+static int hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data *hapd,
+					       char *pos)
+{
+	size_t len;
+	struct wpabuf *buf;
+	int ret;
+
+	len = os_strlen(pos);
+	if (len & 0x01)
+		return -1;
+	len /= 2;
+
+	buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return -1;
+	if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
+		wpabuf_free(buf);
+		return -1;
+	}
+
+	ret = hostapd_wps_nfc_tag_read(hapd, buf);
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+
+static int hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data *hapd,
+						   char *cmd, char *reply,
+						   size_t max_len)
+{
+	int ndef;
+	struct wpabuf *buf;
+	int res;
+
+	if (os_strcmp(cmd, "WPS") == 0)
+		ndef = 0;
+	else if (os_strcmp(cmd, "NDEF") == 0)
+		ndef = 1;
+	else
+		return -1;
+
+	buf = hostapd_wps_nfc_config_token(hapd, ndef);
+	if (buf == NULL)
+		return -1;
+
+	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+					 wpabuf_len(buf));
+	reply[res++] = '\n';
+	reply[res] = '\0';
+
+	wpabuf_free(buf);
+
+	return res;
+}
+
+
+static int hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data *hapd,
+						char *reply, size_t max_len,
+						int ndef)
+{
+	struct wpabuf *buf;
+	int res;
+
+	buf = hostapd_wps_nfc_token_gen(hapd, ndef);
+	if (buf == NULL)
+		return -1;
+
+	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+					 wpabuf_len(buf));
+	reply[res++] = '\n';
+	reply[res] = '\0';
+
+	wpabuf_free(buf);
+
+	return res;
+}
+
+
+static int hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data *hapd,
+					    char *cmd, char *reply,
+					    size_t max_len)
+{
+	if (os_strcmp(cmd, "WPS") == 0)
+		return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
+							    max_len, 0);
+
+	if (os_strcmp(cmd, "NDEF") == 0)
+		return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
+							    max_len, 1);
+
+	if (os_strcmp(cmd, "enable") == 0)
+		return hostapd_wps_nfc_token_enable(hapd);
+
+	if (os_strcmp(cmd, "disable") == 0) {
+		hostapd_wps_nfc_token_disable(hapd);
+		return 0;
+	}
+
+	return -1;
+}
+#endif /* CONFIG_WPS_NFC */
+
+
 static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt,
 					 char *buf, size_t buflen)
 {
@@ -584,7 +523,8 @@
 	ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n"
 			  "ssid=%s\n",
 			  MAC2STR(hapd->own_addr),
-			  hapd->conf->ssid.ssid);
+			  wpa_ssid_txt(hapd->conf->ssid.ssid,
+				       hapd->conf->ssid.ssid_len));
 	if (ret < 0 || ret >= end - pos)
 		return pos - buf;
 	pos += ret;
@@ -679,6 +619,12 @@
 			return pos - buf;
 		pos += ret;
 	} else if (hapd->conf->wpa &&
+		   hapd->conf->wpa_group == WPA_CIPHER_GCMP) {
+		ret = os_snprintf(pos, end - pos, "group_cipher=GCMP\n");
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+	} else if (hapd->conf->wpa &&
 		   hapd->conf->wpa_group == WPA_CIPHER_TKIP) {
 		ret = os_snprintf(pos, end - pos, "group_cipher=TKIP\n");
 		if (ret < 0 || ret >= end - pos)
@@ -698,6 +644,12 @@
 				return pos - buf;
 			pos += ret;
 		}
+		if (hapd->conf->rsn_pairwise & WPA_CIPHER_GCMP) {
+			ret = os_snprintf(pos, end - pos, "GCMP ");
+			if (ret < 0 || ret >= end - pos)
+				return pos - buf;
+			pos += ret;
+		}
 		if (hapd->conf->rsn_pairwise & WPA_CIPHER_TKIP) {
 			ret = os_snprintf(pos, end - pos, "TKIP ");
 			if (ret < 0 || ret >= end - pos)
@@ -723,6 +675,12 @@
 				return pos - buf;
 			pos += ret;
 		}
+		if (hapd->conf->wpa_pairwise & WPA_CIPHER_GCMP) {
+			ret = os_snprintf(pos, end - pos, "GCMP ");
+			if (ret < 0 || ret >= end - pos)
+				return pos - buf;
+			pos += ret;
+		}
 		if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) {
 			ret = os_snprintf(pos, end - pos, "TKIP ");
 			if (ret < 0 || ret >= end - pos)
@@ -773,8 +731,16 @@
 		wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
 			   wps_testing_dummy_cred);
 #endif /* CONFIG_WPS_TESTING */
+#ifdef CONFIG_INTERWORKING
+	} else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) {
+		int val = atoi(value);
+		if (val <= 0)
+			ret = -1;
+		else
+			hapd->gas_frag_limit = val;
+#endif /* CONFIG_INTERWORKING */
 	} else {
-		ret = -1;
+		ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
 	}
 
 	return ret;
@@ -799,6 +765,36 @@
 }
 
 
+static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface)
+{
+	if (hostapd_enable_iface(iface) < 0) {
+		wpa_printf(MSG_ERROR, "Enabling of interface failed");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface)
+{
+	if (hostapd_reload_iface(iface) < 0) {
+		wpa_printf(MSG_ERROR, "Reloading of interface failed");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
+{
+	if (hostapd_disable_iface(iface) < 0) {
+		wpa_printf(MSG_ERROR, "Disabling of interface failed");
+		return -1;
+	}
+	return 0;
+}
+
+
 static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
 				       void *sock_ctx)
 {
@@ -913,6 +909,9 @@
 	} else if (os_strcmp(buf, "WPS_PBC") == 0) {
 		if (hostapd_wps_button_pushed(hapd, NULL))
 			reply_len = -1;
+	} else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
+		if (hostapd_wps_cancel(hapd))
+			reply_len = -1;
 #ifdef CONFIG_WPS_OOB
 	} else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
 		if (hostapd_ctrl_iface_wps_oob(hapd, buf + 8))
@@ -924,6 +923,17 @@
 	} else if (os_strncmp(buf, "WPS_CONFIG ", 11) == 0) {
 		if (hostapd_ctrl_iface_wps_config(hapd, buf + 11) < 0)
 			reply_len = -1;
+#ifdef CONFIG_WPS_NFC
+	} else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
+		if (hostapd_ctrl_iface_wps_nfc_tag_read(hapd, buf + 17))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
+		reply_len = hostapd_ctrl_iface_wps_nfc_config_token(
+			hapd, buf + 21, reply, reply_size);
+	} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
+		reply_len = hostapd_ctrl_iface_wps_nfc_token(
+			hapd, buf + 14, reply, reply_size);
+#endif /* CONFIG_WPS_NFC */
 #endif /* CONFIG_WPS */
 	} else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
 		if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13))
@@ -937,6 +947,15 @@
 	} else if (os_strncmp(buf, "GET ", 4) == 0) {
 		reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply,
 						   reply_size);
+	} else if (os_strncmp(buf, "ENABLE", 6) == 0) {
+		if (hostapd_ctrl_iface_enable(hapd->iface))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "RELOAD", 6) == 0) {
+		if (hostapd_ctrl_iface_reload(hapd->iface))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DISABLE", 7) == 0) {
+		if (hostapd_ctrl_iface_disable(hapd->iface))
+			reply_len = -1;
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -988,7 +1007,10 @@
 	int s = -1;
 	char *fname = NULL;
 
-	hapd->ctrl_sock = -1;
+	if (hapd->ctrl_sock > -1) {
+		wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
+		return 0;
+	}
 
 	if (hapd->conf->ctrl_interface == NULL)
 		return 0;
@@ -1045,7 +1067,7 @@
 			}
 			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
 			    0) {
-				perror("bind(PF_UNIX)");
+				perror("hostapd-ctrl-iface: bind(PF_UNIX)");
 				goto fail;
 			}
 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -1127,6 +1149,220 @@
 }
 
 
+static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces,
+				  char *buf)
+{
+	if (hostapd_add_iface(interfaces, buf) < 0) {
+		wpa_printf(MSG_ERROR, "Adding interface %s failed", buf);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces,
+				     char *buf)
+{
+	if (hostapd_remove_iface(interfaces, buf) < 0) {
+		wpa_printf(MSG_ERROR, "Removing interface %s failed", buf);
+		return -1;
+	}
+	return 0;
+}
+
+
+static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
+					      void *sock_ctx)
+{
+	void *interfaces = eloop_ctx;
+	char buf[256];
+	int res;
+	struct sockaddr_un from;
+	socklen_t fromlen = sizeof(from);
+	char reply[24];
+	int reply_len;
+
+	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (res < 0) {
+		perror("recvfrom(ctrl_iface)");
+		return;
+	}
+	buf[res] = '\0';
+
+	os_memcpy(reply, "OK\n", 3);
+	reply_len = 3;
+
+	if (os_strcmp(buf, "PING") == 0) {
+		os_memcpy(reply, "PONG\n", 5);
+		reply_len = 5;
+	} else if (os_strncmp(buf, "ADD ", 4) == 0) {
+		if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "REMOVE ", 7) == 0) {
+		if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0)
+			reply_len = -1;
+	} else {
+		wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
+			   "ignored");
+		reply_len = -1;
+	}
+
+	if (reply_len < 0) {
+		os_memcpy(reply, "FAIL\n", 5);
+		reply_len = 5;
+	}
+
+	sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
+}
+
+
+static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface)
+{
+	char *buf;
+	size_t len;
+
+	if (interface->global_iface_path == NULL)
+		return NULL;
+
+	len = os_strlen(interface->global_iface_path) +
+		os_strlen(interface->global_iface_name) + 2;
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return NULL;
+
+	os_snprintf(buf, len, "%s/%s", interface->global_iface_path,
+		    interface->global_iface_name);
+	buf[len - 1] = '\0';
+	return buf;
+}
+
+
+int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
+{
+	struct sockaddr_un addr;
+	int s = -1;
+	char *fname = NULL;
+
+	if (interface->global_iface_path == NULL) {
+		wpa_printf(MSG_DEBUG, "ctrl_iface not configured!");
+		return 0;
+	}
+
+	if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) {
+		if (errno == EEXIST) {
+			wpa_printf(MSG_DEBUG, "Using existing control "
+				   "interface directory.");
+		} else {
+			perror("mkdir[ctrl_interface]");
+			goto fail;
+		}
+	}
+
+	if (os_strlen(interface->global_iface_path) + 1 +
+	    os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path))
+		goto fail;
+
+	s = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (s < 0) {
+		perror("socket(PF_UNIX)");
+		goto fail;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+#ifdef __FreeBSD__
+	addr.sun_len = sizeof(addr);
+#endif /* __FreeBSD__ */
+	addr.sun_family = AF_UNIX;
+	fname = hostapd_global_ctrl_iface_path(interface);
+	if (fname == NULL)
+		goto fail;
+	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
+	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
+			   strerror(errno));
+		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
+				   " allow connections - assuming it was left"
+				   "over from forced program termination");
+			if (unlink(fname) < 0) {
+				perror("unlink[ctrl_iface]");
+				wpa_printf(MSG_ERROR, "Could not unlink "
+					   "existing ctrl_iface socket '%s'",
+					   fname);
+				goto fail;
+			}
+			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
+			    0) {
+				perror("bind(PF_UNIX)");
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+				   "ctrl_iface socket '%s'", fname);
+		} else {
+			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
+				   "be in use - cannot override it");
+			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+				   "not used anymore", fname);
+			os_free(fname);
+			fname = NULL;
+			goto fail;
+		}
+	}
+
+	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
+		perror("chmod[ctrl_interface/ifname]");
+		goto fail;
+	}
+	os_free(fname);
+
+	interface->global_ctrl_sock = s;
+	eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
+				 interface, NULL);
+
+	return 0;
+
+fail:
+	if (s >= 0)
+		close(s);
+	if (fname) {
+		unlink(fname);
+		os_free(fname);
+	}
+	return -1;
+}
+
+
+void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
+{
+	char *fname = NULL;
+
+	if (interfaces->global_ctrl_sock > -1) {
+		eloop_unregister_read_sock(interfaces->global_ctrl_sock);
+		close(interfaces->global_ctrl_sock);
+		interfaces->global_ctrl_sock = -1;
+		fname = hostapd_global_ctrl_iface_path(interfaces);
+		if (fname) {
+			unlink(fname);
+			os_free(fname);
+		}
+
+		if (interfaces->global_iface_path &&
+		    rmdir(interfaces->global_iface_path) < 0) {
+			if (errno == ENOTEMPTY) {
+				wpa_printf(MSG_DEBUG, "Control interface "
+					   "directory not empty - leaving it "
+					   "behind");
+			} else {
+				perror("rmdir[ctrl_interface]");
+			}
+		}
+		os_free(interfaces->global_iface_path);
+		interfaces->global_iface_path = NULL;
+	}
+}
+
+
 static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
 				    const char *buf, size_t len)
 {
diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h
index 64a5080..3341a66 100644
--- a/hostapd/ctrl_iface.h
+++ b/hostapd/ctrl_iface.h
@@ -12,6 +12,8 @@
 #ifndef CONFIG_NO_CTRL_IFACE
 int hostapd_ctrl_iface_init(struct hostapd_data *hapd);
 void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd);
+int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface);
+void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface);
 #else /* CONFIG_NO_CTRL_IFACE */
 static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
 {
@@ -21,6 +23,17 @@
 static inline void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
 {
 }
+
+static inline int
+hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
+{
+	return 0;
+}
+
+static inline void
+hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface)
+{
+}
 #endif /* CONFIG_NO_CTRL_IFACE */
 
 #endif /* CTRL_IFACE_H */
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 3cf0d13..204aa76 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -108,6 +108,8 @@
 #CONFIG_WPS2=y
 # Enable UPnP support for external WPS Registrars
 #CONFIG_WPS_UPNP=y
+# Enable WPS support with NFC config method
+#CONFIG_WPS_NFC=y
 
 # EAP-IKEv2
 #CONFIG_EAP_IKEV2=y
@@ -136,6 +138,13 @@
 # IEEE 802.11n (High Throughput) support
 #CONFIG_IEEE80211N=y
 
+# Wireless Network Management (IEEE Std 802.11v-2011)
+# Note: This is experimental and not complete implementation.
+#CONFIG_WNM=y
+
+# IEEE 802.11ac (Very High Throughput) support
+#CONFIG_IEEE80211AC=y
+
 # Remove debugging code that is printing out debug messages to stdout.
 # This can be used to reduce the size of the hostapd considerably if debugging
 # code is not needed.
@@ -158,6 +167,10 @@
 # automatically create bridge and VLAN interfaces if necessary.
 #CONFIG_FULL_DYNAMIC_VLAN=y
 
+# Use netlink-based kernel API for VLAN operations instead of ioctl()
+# Note: This requires libnl 3.1 or newer.
+#CONFIG_VLAN_NETLINK=y
+
 # Remove support for dumping state into a file on SIGUSR1 signal
 # This can be used to reduce binary size at the cost of disabling a debugging
 # option.
@@ -248,3 +261,9 @@
 # This can be used to enable functionality to improve interworking with
 # external networks.
 #CONFIG_INTERWORKING=y
+
+# Hotspot 2.0
+#CONFIG_HS20=y
+
+# Enable SQLite database support in hlr_auc_gw
+#CONFIG_SQLITE=y
diff --git a/hostapd/eap_register.c b/hostapd/eap_register.c
index 089c7b2..0a7ff91 100644
--- a/hostapd/eap_register.c
+++ b/hostapd/eap_register.c
@@ -39,6 +39,11 @@
 		ret = eap_server_tls_register();
 #endif /* EAP_SERVER_TLS */
 
+#ifdef EAP_SERVER_UNAUTH_TLS
+	if (ret == 0)
+		ret = eap_server_unauth_tls_register();
+#endif /* EAP_SERVER_TLS */
+
 #ifdef EAP_SERVER_MSCHAPV2
 	if (ret == 0)
 		ret = eap_server_mschapv2_register();
diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c
index b59ea1b..e04e2e9 100644
--- a/hostapd/hlr_auc_gw.c
+++ b/hostapd/hlr_auc_gw.c
@@ -1,6 +1,6 @@
 /*
  * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
- * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2007, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -34,11 +34,18 @@
  * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex
  * strings. This is used to simulate an HLR/AuC. As such, it is not very useful
  * for real life authentication, but it is useful both as an example
- * implementation and for EAP-SIM testing.
+ * implementation and for EAP-SIM/AKA/AKA' testing.
+ *
+ * SQN generation follows the not time-based Profile 2 described in
+ * 3GPP TS 33.102 Annex C.3.2. The length of IND is 5 bits by default, but this
+ * can be changed with a command line options if needed.
  */
 
 #include "includes.h"
 #include <sys/un.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
 
 #include "common.h"
 #include "crypto/milenage.h"
@@ -47,6 +54,10 @@
 static const char *default_socket_path = "/tmp/hlr_auc_gw.sock";
 static const char *socket_path;
 static int serv_sock = -1;
+static char *milenage_file = NULL;
+static int update_milenage = 0;
+static int sqn_changes = 0;
+static int ind_len = 5;
 
 /* GSM triplets */
 struct gsm_triplet {
@@ -67,6 +78,7 @@
 	u8 opc[16];
 	u8 amf[2];
 	u8 sqn[6];
+	int set;
 };
 
 static struct milenage_parameters *milenage_db = NULL;
@@ -81,6 +93,144 @@
 #define EAP_AKA_CK_LEN 16
 
 
+#ifdef CONFIG_SQLITE
+
+static sqlite3 *sqlite_db = NULL;
+static struct milenage_parameters db_tmp_milenage;
+
+
+static int db_table_exists(sqlite3 *db, const char *name)
+{
+	char cmd[128];
+	os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
+	return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
+}
+
+
+static int db_table_create_milenage(sqlite3 *db)
+{
+	char *err = NULL;
+	const char *sql =
+		"CREATE TABLE milenage("
+		"  imsi INTEGER PRIMARY KEY NOT NULL,"
+		"  ki CHAR(32) NOT NULL,"
+		"  opc CHAR(32) NOT NULL,"
+		"  amf CHAR(4) NOT NULL,"
+		"  sqn CHAR(12) NOT NULL"
+		");";
+
+	printf("Adding database table for milenage information\n");
+	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+		printf("SQLite error: %s\n", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static sqlite3 * db_open(const char *db_file)
+{
+	sqlite3 *db;
+
+	if (sqlite3_open(db_file, &db)) {
+		printf("Failed to open database %s: %s\n",
+		       db_file, sqlite3_errmsg(db));
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	if (!db_table_exists(db, "milenage") &&
+	    db_table_create_milenage(db) < 0) {
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	return db;
+}
+
+
+static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct milenage_parameters *m = ctx;
+	int i;
+
+	m->set = 1;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "ki") == 0 && argv[i] &&
+		    hexstr2bin(argv[i], m->ki, sizeof(m->ki))) {
+			printf("Invalid ki value in database\n");
+			return -1;
+		}
+
+		if (os_strcmp(col[i], "opc") == 0 && argv[i] &&
+		    hexstr2bin(argv[i], m->opc, sizeof(m->opc))) {
+			printf("Invalid opcvalue in database\n");
+			return -1;
+		}
+
+		if (os_strcmp(col[i], "amf") == 0 && argv[i] &&
+		    hexstr2bin(argv[i], m->amf, sizeof(m->amf))) {
+			printf("Invalid amf value in database\n");
+			return -1;
+		}
+
+		if (os_strcmp(col[i], "sqn") == 0 && argv[i] &&
+		    hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) {
+			printf("Invalid sqn value in database\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static struct milenage_parameters * db_get_milenage(const char *imsi_txt)
+{
+	char cmd[128];
+	unsigned long long imsi;
+
+	os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage));
+	imsi = atoll(imsi_txt);
+	os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi),
+		    "%llu", imsi);
+	os_snprintf(cmd, sizeof(cmd),
+		    "SELECT ki,opc,amf,sqn FROM milenage WHERE imsi=%llu;",
+		    imsi);
+	if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage,
+			 NULL) != SQLITE_OK)
+		return NULL;
+
+	if (!db_tmp_milenage.set)
+		return NULL;
+	return &db_tmp_milenage;
+}
+
+
+static int db_update_milenage_sqn(struct milenage_parameters *m)
+{
+	char cmd[128], val[13], *pos;
+
+	pos = val;
+	pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6);
+	*pos = '\0';
+	os_snprintf(cmd, sizeof(cmd),
+		    "UPDATE milenage SET sqn='%s' WHERE imsi=%s;",
+		    val, m->imsi);
+	if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+		printf("Failed to update SQN in database for IMSI %s\n",
+		       m->imsi);
+		return -1;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_SQLITE */
+
+
 static int open_socket(const char *path)
 {
 	struct sockaddr_un addr;
@@ -96,7 +246,7 @@
 	addr.sun_family = AF_UNIX;
 	os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("bind(PF_UNIX)");
+		perror("hlr-auc-gw: bind(PF_UNIX)");
 		close(s);
 		return -1;
 	}
@@ -210,7 +360,7 @@
 		gsm_db = g;
 		g = NULL;
 	}
-	free(g);
+	os_free(g);
 
 	fclose(f);
 
@@ -360,7 +510,7 @@
 		milenage_db = m;
 		m = NULL;
 	}
-	free(m);
+	os_free(m);
 
 	fclose(f);
 
@@ -368,6 +518,80 @@
 }
 
 
+static void update_milenage_file(const char *fname)
+{
+	FILE *f, *f2;
+	char buf[500], *pos;
+	char *end = buf + sizeof(buf);
+	struct milenage_parameters *m;
+	size_t imsi_len;
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		printf("Could not open Milenage data file '%s'\n", fname);
+		return;
+	}
+
+	snprintf(buf, sizeof(buf), "%s.new", fname);
+	f2 = fopen(buf, "w");
+	if (f2 == NULL) {
+		printf("Could not write Milenage data file '%s'\n", buf);
+		fclose(f);
+		return;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		/* IMSI Ki OPc AMF SQN */
+		buf[sizeof(buf) - 1] = '\0';
+
+		pos = strchr(buf, ' ');
+		if (buf[0] == '#' || pos == NULL || pos - buf >= 20)
+			goto no_update;
+
+		imsi_len = pos - buf;
+
+		for (m = milenage_db; m; m = m->next) {
+			if (strncmp(buf, m->imsi, imsi_len) == 0 &&
+			    m->imsi[imsi_len] == '\0')
+				break;
+		}
+
+		if (!m)
+			goto no_update;
+
+		pos = buf;
+		pos += snprintf(pos, end - pos, "%s ", m->imsi);
+		pos += wpa_snprintf_hex(pos, end - pos, m->ki, 16);
+		*pos++ = ' ';
+		pos += wpa_snprintf_hex(pos, end - pos, m->opc, 16);
+		*pos++ = ' ';
+		pos += wpa_snprintf_hex(pos, end - pos, m->amf, 2);
+		*pos++ = ' ';
+		pos += wpa_snprintf_hex(pos, end - pos, m->sqn, 6);
+		*pos++ = '\n';
+
+	no_update:
+		fprintf(f2, "%s", buf);
+	}
+
+	fclose(f2);
+	fclose(f);
+
+	snprintf(buf, sizeof(buf), "%s.bak", fname);
+	if (rename(fname, buf) < 0) {
+		perror("rename");
+		return;
+	}
+
+	snprintf(buf, sizeof(buf), "%s.new", fname);
+	if (rename(buf, fname) < 0) {
+		perror("rename");
+		return;
+	}
+
+}
+
+
 static struct milenage_parameters * get_milenage(const char *imsi)
 {
 	struct milenage_parameters *m = milenage_db;
@@ -378,6 +602,11 @@
 		m = m->next;
 	}
 
+#ifdef CONFIG_SQLITE
+	if (!m)
+		m = db_get_milenage(imsi);
+#endif /* CONFIG_SQLITE */
+
 	return m;
 }
 
@@ -460,6 +689,28 @@
 }
 
 
+static void inc_sqn(u8 *sqn)
+{
+	u64 val, seq, ind;
+
+	/*
+	 * SQN = SEQ | IND = SEQ1 | SEQ2 | IND
+	 *
+	 * The mechanism used here is not time-based, so SEQ2 is void and
+	 * SQN = SEQ1 | IND. The length of IND is ind_len bits and the length
+	 * of SEQ1 is 48 - ind_len bits.
+	 */
+
+	/* Increment both SEQ and IND by one */
+	val = ((u64) WPA_GET_BE32(sqn) << 16) | ((u64) WPA_GET_BE16(sqn + 4));
+	seq = (val >> ind_len) + 1;
+	ind = (val + 1) & ((1 << ind_len) - 1);
+	val = (seq << ind_len) | ind;
+	WPA_PUT_BE32(sqn, val >> 16);
+	WPA_PUT_BE16(sqn + 4, val & 0xffff);
+}
+
+
 static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
 			 char *imsi)
 {
@@ -473,13 +724,18 @@
 	size_t res_len;
 	int ret;
 	struct milenage_parameters *m;
+	int failed = 0;
 
 	m = get_milenage(imsi);
 	if (m) {
 		if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0)
 			return;
 		res_len = EAP_AKA_RES_MAX_LEN;
-		inc_byte_array(m->sqn, 6);
+		inc_sqn(m->sqn);
+#ifdef CONFIG_SQLITE
+		db_update_milenage_sqn(m);
+#endif /* CONFIG_SQLITE */
+		sqn_changes = 1;
 		printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
 		       m->sqn[0], m->sqn[1], m->sqn[2],
 		       m->sqn[3], m->sqn[4], m->sqn[5]);
@@ -496,7 +752,7 @@
 		memset(res, '2', EAP_AKA_RES_MAX_LEN);
 		res_len = EAP_AKA_RES_MAX_LEN;
 #else /* AKA_USE_FIXED_TEST_VALUES */
-		return;
+		failed = 1;
 #endif /* AKA_USE_FIXED_TEST_VALUES */
 	}
 
@@ -506,6 +762,13 @@
 	if (ret < 0 || ret >= end - pos)
 		return;
 	pos += ret;
+	if (failed) {
+		ret = snprintf(pos, end - pos, "FAILURE");
+		if (ret < 0 || ret >= end - pos)
+			return;
+		pos += ret;
+		goto done;
+	}
 	pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
 	*pos++ = ' ';
 	pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN);
@@ -516,6 +779,7 @@
 	*pos++ = ' ';
 	pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
 
+done:
 	printf("Send: %s\n", reply);
 
 	if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from,
@@ -563,6 +827,10 @@
 		printf("AKA-AUTS: Re-synchronized: "
 		       "SQN=%02x%02x%02x%02x%02x%02x\n",
 		       sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
+#ifdef CONFIG_SQLITE
+		db_update_milenage_sqn(m);
+#endif /* CONFIG_SQLITE */
+		sqn_changes = 1;
 	}
 }
 
@@ -609,22 +877,32 @@
 	struct gsm_triplet *g, *gprev;
 	struct milenage_parameters *m, *prev;
 
+	if (update_milenage && milenage_file && sqn_changes)
+		update_milenage_file(milenage_file);
+
 	g = gsm_db;
 	while (g) {
 		gprev = g;
 		g = g->next;
-		free(gprev);
+		os_free(gprev);
 	}
 
 	m = milenage_db;
 	while (m) {
 		prev = m;
 		m = m->next;
-		free(prev);
+		os_free(prev);
 	}
 
 	close(serv_sock);
 	unlink(socket_path);
+
+#ifdef CONFIG_SQLITE
+	if (sqlite_db) {
+		sqlite3_close(sqlite_db);
+		sqlite_db = NULL;
+	}
+#endif /* CONFIG_SQLITE */
 }
 
 
@@ -639,18 +917,22 @@
 {
 	printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
 	       "database/authenticator\n"
-	       "Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>\n"
+	       "Copyright (c) 2005-2007, 2012, Jouni Malinen <j@w1.fi>\n"
 	       "\n"
 	       "usage:\n"
-	       "hlr_auc_gw [-h] [-s<socket path>] [-g<triplet file>] "
-	       "[-m<milenage file>]\n"
+	       "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
+	       "[-m<milenage file>] \\\n"
+	       "        [-D<DB file>] [-i<IND len in bits>]\n"
 	       "\n"
 	       "options:\n"
 	       "  -h = show this usage help\n"
+	       "  -u = update SQN in Milenage file on exit\n"
 	       "  -s<socket path> = path for UNIX domain socket\n"
 	       "                    (default: %s)\n"
 	       "  -g<triplet file> = path for GSM authentication triplets\n"
-	       "  -m<milenage file> = path for Milenage keys\n",
+	       "  -m<milenage file> = path for Milenage keys\n"
+	       "  -D<DB file> = path to SQLite database\n"
+	       "  -i<IND len in bits> = IND length for SQN (default: 5)\n",
 	       default_socket_path);
 }
 
@@ -658,34 +940,65 @@
 int main(int argc, char *argv[])
 {
 	int c;
-	char *milenage_file = NULL;
 	char *gsm_triplet_file = NULL;
+	char *sqlite_db_file = NULL;
+
+	if (os_program_init())
+		return -1;
 
 	socket_path = default_socket_path;
 
 	for (;;) {
-		c = getopt(argc, argv, "g:hm:s:");
+		c = getopt(argc, argv, "D:g:hi:m:s:u");
 		if (c < 0)
 			break;
 		switch (c) {
+		case 'D':
+#ifdef CONFIG_SQLITE
+			sqlite_db_file = optarg;
+			break;
+#else /* CONFIG_SQLITE */
+			printf("No SQLite support included in the build\n");
+			return -1;
+#endif /* CONFIG_SQLITE */
 		case 'g':
 			gsm_triplet_file = optarg;
 			break;
 		case 'h':
 			usage();
 			return 0;
+		case 'i':
+			ind_len = atoi(optarg);
+			if (ind_len < 0 || ind_len > 32) {
+				printf("Invalid IND length\n");
+				return -1;
+			}
+			break;
 		case 'm':
 			milenage_file = optarg;
 			break;
 		case 's':
 			socket_path = optarg;
 			break;
+		case 'u':
+			update_milenage = 1;
+			break;
 		default:
 			usage();
 			return -1;
 		}
 	}
 
+	if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) {
+		usage();
+		return -1;
+	}
+
+#ifdef CONFIG_SQLITE
+	if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL)
+		return -1;
+#endif /* CONFIG_SQLITE */
+
 	if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0)
 		return -1;
 
@@ -705,5 +1018,14 @@
 	for (;;)
 		process(serv_sock);
 
+#ifdef CONFIG_SQLITE
+	if (sqlite_db) {
+		sqlite3_close(sqlite_db);
+		sqlite_db = NULL;
+	}
+#endif /* CONFIG_SQLITE */
+
+	os_program_deinit();
+
 	return 0;
 }
diff --git a/hostapd/hlr_auc_gw.txt b/hostapd/hlr_auc_gw.txt
new file mode 100644
index 0000000..097bbce
--- /dev/null
+++ b/hostapd/hlr_auc_gw.txt
@@ -0,0 +1,104 @@
+HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
+
+hlr_auc_gw is an example implementation of the EAP-SIM/AKA/AKA'
+database/authentication gateway interface to HLR/AuC. It could be
+replaced with an implementation of SS7 gateway to GSM/UMTS
+authentication center (HLR/AuC). hostapd will send SIM/AKA
+authentication queries over a UNIX domain socket to and external
+program, e.g., hlr_auc_gw.
+
+hlr_auc_gw can be configured with GSM and UMTS authentication data with
+text files: GSM triplet file (see hostapd.sim_db) and Milenage file (see
+hlr_auc_gw.milenage_db). Milenage parameters can be used to generate
+dynamic authentication data for EAP-SIM, EAP-AKA, and EAP-AKA' while the
+GSM triplet data is used for a more static configuration (e.g., triplets
+extracted from a SIM card).
+
+Alternatively, hlr_auc_gw can be built with support for an SQLite
+database for more dynamic operations. This is enabled by adding
+"CONFIG_SQLITE=y" into hostapd/.config before building hlr_auc_gw ("make
+clean; make hlr_auc_gw" in this directory).
+
+hostapd is configured to use hlr_auc_gw with the eap_sim_db parameter in
+hostapd.conf (e.g., "eap_sim_db=unix:/tmp/hlr_auc_gw.sock"). hlr_auc_gw
+is configured with command line parameters:
+
+hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] [-m<milenage file>] \
+        [-D<DB file>] [-i<IND len in bits>]
+
+options:
+  -h = show this usage help
+  -u = update SQN in Milenage file on exit
+  -s<socket path> = path for UNIX domain socket
+                    (default: /tmp/hlr_auc_gw.sock)
+  -g<triplet file> = path for GSM authentication triplets
+  -m<milenage file> = path for Milenage keys
+  -D<DB file> = path to SQLite database
+  -i<IND len in bits> = IND length for SQN (default: 5)
+
+
+The SQLite database can be initialized with sqlite, e.g., by running
+following commands in "sqlite3 /path/to/hlr_auc_gw.db":
+
+CREATE TABLE milenage(
+	imsi INTEGER PRIMARY KEY NOT NULL,
+	ki CHAR(32) NOT NULL,
+	opc CHAR(32) NOT NULL,
+	amf CHAR(4) NOT NULL,
+	sqn CHAR(12) NOT NULL
+);
+INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
+	232010000000000,
+	'90dca4eda45b53cf0f12d7c9c3bc6a89',
+	'cb9cccc4b9258e6dca4760379fb82581',
+	'61df',
+	'000000000000'
+);
+INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
+	555444333222111,
+	'5122250214c33e723a5dd523fc145fc0',
+	'981d464c7c52eb6e5036234984ad0bcf',
+	'c3ab',
+	'16f3b3f70fc1'
+);
+
+
+hostapd (EAP server) can also be configured to store the EAP-SIM/AKA
+pseudonyms and reauth information into a SQLite database. This is
+configured with the db parameter within the eap_sim_db configuration
+option.
+
+
+"hlr_auc_gw -D /path/to/hlr_auc_gw.db" can then be used to fetch
+Milenage parameters based on IMSI from the database. The database can be
+updated dynamically while hlr_auc_gw is running to add/remove/modify
+entries.
+
+
+Example configuration files for hostapd to operate as a RADIUS
+authentication server for EAP-SIM/AKA/AKA':
+
+hostapd.conf:
+
+driver=none
+radius_server_clients=hostapd.radius_clients
+eap_server=1
+eap_user_file=hostapd.eap_user
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/eap_sim.db
+eap_sim_aka_result_ind=1
+
+hostapd.radius_clients:
+
+0.0.0.0/0	radius
+
+hostapd.eap_user:
+
+"0"*	AKA
+"1"*	SIM
+"2"*	AKA
+"3"*	SIM
+"4"*	AKA
+"5"*	SIM
+"6"*	AKA'
+"7"*	AKA'
+"8"*	AKA'
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 4e6202b..edbd772 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -84,6 +84,11 @@
 
 # SSID to be used in IEEE 802.11 management frames
 ssid=test
+# Alternative formats for configuring SSID
+# (double quoted string, hexdump, printf-escaped string)
+#ssid2="test"
+#ssid2=74657374
+#ssid2=P"hello\nthere"
 
 # Country code (ISO/IEC 3166-1). Used to set regulatory domain.
 # Set as needed to indicate country in which device is operating.
@@ -196,6 +201,13 @@
 #     requests for broadcast SSID
 ignore_broadcast_ssid=0
 
+# Additional vendor specfic elements for Beacon and Probe Response frames
+# This parameter can be used to add additional vendor specific element(s) into
+# the end of the Beacon and Probe Response frames. The format for these
+# element(s) is a hexdump of the raw information elements (id+len+payload for
+# one or more elements)
+#vendor_elements=dd0411223301
+
 # TX queue parameters (EDCF / bursting)
 # tx_queue_<queue name>_<param>
 # queues: data0, data1, data2, data3, after_beacon, beacon
@@ -416,6 +428,151 @@
 # Require stations to support HT PHY (reject association if they do not)
 #require_ht=1
 
+##### IEEE 802.11ac related configuration #####################################
+
+# ieee80211ac: Whether IEEE 802.11ac (VHT) is enabled
+# 0 = disabled (default)
+# 1 = enabled
+# Note: You will also need to enable WMM for full VHT functionality.
+#ieee80211ac=1
+
+# vht_capab: VHT capabilities (list of flags)
+#
+# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454]
+# Indicates maximum MPDU length
+# 0 = 3895 octets (default)
+# 1 = 7991 octets
+# 2 = 11454 octets
+# 3 = reserved
+#
+# supported_chan_width: [VHT160] [VHT160-80PLUS80]
+# Indicates supported Channel widths
+# 0 = 160 MHz & 80+80 channel widths are not supported (default)
+# 1 = 160 MHz channel width is supported
+# 2 = 160 MHz & 80+80 channel widths are supported
+# 3 = reserved
+#
+# Rx LDPC coding capability: [RXLDPC]
+# Indicates support for receiving LDPC coded pkts
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 80 MHz: [SHORT-GI-80]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 80Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 160 MHz: [SHORT-GI-160]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 160Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Tx STBC: [TX-STBC-2BY1]
+# Indicates support for the transmission of at least 2x1 STBC
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Rx STBC: [RX-STBC-1] [RX-STBC-12] [RX-STBC-123] [RX-STBC-1234]
+# Indicates support for the reception of PPDUs using STBC
+# 0 = Not supported (default)
+# 1 = support of one spatial stream
+# 2 = support of one and two spatial streams
+# 3 = support of one, two and three spatial streams
+# 4 = support of one, two, three and four spatial streams
+# 5,6,7 = reserved
+#
+# SU Beamformer Capable: [SU-BEAMFORMER]
+# Indicates support for operation as a single user beamformer
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# SU Beamformee Capable: [SU-BEAMFORMEE]
+# Indicates support for operation as a single user beamformee
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Compressed Steering Number of Beamformer Antennas Supported: [BF-ANTENNA-2]
+#   Beamformee's capability indicating the maximum number of beamformer
+#   antennas the beamformee can support when sending compressed beamforming
+#   feedback
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# Number of Sounding Dimensions: [SOUNDING-DIMENSION-2]
+# Beamformer's capability indicating the maximum value of the NUM_STS parameter
+# in the TXVECTOR of a VHT NDP
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# MU Beamformer Capable: [MU-BEAMFORMER]
+# Indicates support for operation as an MU beamformer
+# 0 = Not supported or sent by Non-AP STA (default)
+# 1 = Supported
+#
+# MU Beamformee Capable: [MU-BEAMFORMEE]
+# Indicates support for operation as an MU beamformee
+# 0 = Not supported or sent by AP (default)
+# 1 = Supported
+#
+# VHT TXOP PS: [VHT-TXOP-PS]
+# Indicates whether or not the AP supports VHT TXOP Power Save Mode
+#  or whether or not the STA is in VHT TXOP Power Save mode
+# 0 = VHT AP doesnt support VHT TXOP PS mode (OR) VHT Sta not in VHT TXOP PS
+#  mode
+# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT Sta is in VHT TXOP power save
+#  mode
+#
+# +HTC-VHT Capable: [HTC-VHT]
+# Indicates whether or not the STA supports receiving a VHT variant HT Control
+# field.
+# 0 = Not supported (default)
+# 1 = supported
+#
+# Maximum A-MPDU Length Exponent: [MAX-A-MPDU-LEN-EXP0]..[MAX-A-MPDU-LEN-EXP7]
+# Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv
+# This field is an integer in the range of 0 to 7.
+# The length defined by this field is equal to
+# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets
+#
+# VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3]
+# Indicates whether or not the STA supports link adaptation using VHT variant
+# HT Control field
+# If +HTC-VHTcapable is 1
+#  0 = (no feedback) if the STA does not provide VHT MFB (default)
+#  1 = reserved
+#  2 = (Unsolicited) if the STA provides only unsolicited VHT MFB
+#  3 = (Both) if the STA can provide VHT MFB in response to VHT MRQ and if the
+#      STA provides unsolicited VHT MFB
+# Reserved if +HTC-VHTcapable is 0
+#
+# Rx Antenna Pattern Consistency: [RX-ANTENNA-PATTERN]
+# Indicates the possibility of Rx antenna pattern change
+# 0 = Rx antenna pattern might change during the lifetime of an association
+# 1 = Rx antenna pattern does not change during the lifetime of an association
+#
+# Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN]
+# Indicates the possibility of Tx antenna pattern change
+# 0 = Tx antenna pattern might change during the lifetime of an association
+# 1 = Tx antenna pattern does not change during the lifetime of an association
+#vht_capab=[SHORT-GI-80][HTC-VHT]
+#
+# Require stations to support VHT PHY (reject association if they do not)
+#require_vht=1
+
+# 0 = 20 or 40 MHz operating Channel width
+# 1 = 80 MHz channel width
+# 2 = 160 MHz channel width
+# 3 = 80+80 MHz channel width
+#vht_oper_chwidth=1
+#
+# center freq = 5 GHz + (5 * index)
+# So index 42 gives center freq 5.210 GHz
+# which is channel 42 in 5G band
+#
+#vht_oper_centr_freq_seg0_idx=42
+
 ##### IEEE 802.1X-2004 related configuration ##################################
 
 # Require IEEE 802.1X authorization
@@ -524,8 +681,10 @@
 # This is a text string in implementation specific format. The example
 # implementation in eap_sim_db.c uses this as the UNIX domain socket name for
 # the HLR/AuC gateway (e.g., hlr_auc_gw). In this case, the path uses "unix:"
-# prefix.
+# prefix. If hostapd is built with SQLite support (CONFIG_SQLITE=y in .config),
+# database file can be described with an optional db=<path> parameter.
 #eap_sim_db=unix:/tmp/hlr_auc_gw.sock
+#eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db
 
 # Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret,
 # random value. It is configured as a 16-octet value in hex format. It can be
@@ -632,6 +791,12 @@
 # 60 (1 minute).
 #radius_acct_interim_interval=600
 
+# Request Chargeable-User-Identity (RFC 4372)
+# This parameter can be used to configure hostapd to request CUI from the
+# RADIUS server by including Chargeable-User-Identity attribute into
+# Access-Request packets.
+#radius_request_cui=1
+
 # Dynamic VLAN mode; allow RADIUS authentication server to decide which VLAN
 # is used for the stations. This information is parsed from following RADIUS
 # attributes based on RFC 3580 and RFC 2868: Tunnel-Type (value 13 = VLAN),
@@ -659,6 +824,61 @@
 # to the bridge.
 #vlan_tagged_interface=eth0
 
+# When hostapd creates a VLAN interface on vlan_tagged_interfaces, it needs
+# to know how to name it.
+# 0 = vlan<XXX>, e.g., vlan1
+# 1 = <vlan_tagged_interface>.<XXX>, e.g. eth0.1
+#vlan_naming=0
+
+# Arbitrary RADIUS attributes can be added into Access-Request and
+# Accounting-Request packets by specifying the contents of the attributes with
+# the following configuration parameters. There can be multiple of these to
+# add multiple attributes. These parameters can also be used to override some
+# of the attributes added automatically by hostapd.
+# Format: <attr_id>[:<syntax:value>]
+# attr_id: RADIUS attribute type (e.g., 26 = Vendor-Specific)
+# syntax: s = string (UTF-8), d = integer, x = octet string
+# value: attribute value in format indicated by the syntax
+# If syntax and value parts are omitted, a null value (single 0x00 octet) is
+# used.
+#
+# Additional Access-Request attributes
+# radius_auth_req_attr=<attr_id>[:<syntax:value>]
+# Examples:
+# Operator-Name = "Operator"
+#radius_auth_req_attr=126:s:Operator
+# Service-Type = Framed (2)
+#radius_auth_req_attr=6:d:2
+# Connect-Info = "testing" (this overrides the automatically generated value)
+#radius_auth_req_attr=77:s:testing
+# Same Connect-Info value set as a hexdump
+#radius_auth_req_attr=77:x:74657374696e67
+
+#
+# Additional Accounting-Request attributes
+# radius_acct_req_attr=<attr_id>[:<syntax:value>]
+# Examples:
+# Operator-Name = "Operator"
+#radius_acct_req_attr=126:s:Operator
+
+# Dynamic Authorization Extensions (RFC 5176)
+# This mechanism can be used to allow dynamic changes to user session based on
+# commands from a RADIUS server (or some other disconnect client that has the
+# needed session information). For example, Disconnect message can be used to
+# request an associated station to be disconnected.
+#
+# This is disabled by default. Set radius_das_port to non-zero UDP port
+# number to enable.
+#radius_das_port=3799
+#
+# DAS client (the host that can send Disconnect/CoA requests) and shared secret
+#radius_das_client=192.168.1.123 shared secret here
+#
+# DAS Event-Timestamp time window in seconds
+#radius_das_time_window=300
+#
+# DAS require Event-Timestamp
+#radius_das_require_event_timestamp=1
 
 ##### RADIUS authentication server configuration ##############################
 
@@ -1033,6 +1253,18 @@
 # set to ag to allow both RF bands to be advertized.
 #wps_rf_bands=ag
 
+# NFC password token for WPS
+# These parameters can be used to configure a fixed NFC password token for the
+# AP. This can be generated, e.g., with nfc_pw_token from wpa_supplicant. When
+# these parameters are used, the AP is assumed to be deployed with a NFC tag
+# that includes the matching NFC password token (e.g., written based on the
+# NDEF record from nfc_pw_token).
+#
+#wps_nfc_dev_pw_id: Device Password ID (16..65535)
+#wps_nfc_dh_pubkey: Hexdump of DH Public Key
+#wps_nfc_dh_privkey: Hexdump of DH Private Key
+#wps_nfc_dev_pw: Hexdump of Device Password
+
 ##### Wi-Fi Direct (P2P) ######################################################
 
 # Enable P2P Device management
@@ -1113,11 +1345,138 @@
 # Arbitrary number of Roaming Consortium OIs can be configured with each line
 # adding a new OI to the list. The first three entries are available through
 # Beacon and Probe Response frames. Any additional entry will be available only
-# through ANQP queries. Each OI is between 3 and 15 octets and is configured a
+# through ANQP queries. Each OI is between 3 and 15 octets and is configured as
 # a hexstring.
 #roaming_consortium=021122
 #roaming_consortium=2233445566
 
+# Venue Name information
+# This parameter can be used to configure one or more Venue Name Duples for
+# Venue Name ANQP information. Each entry has a two or three character language
+# code (ISO-639) separated by colon from the venue name string.
+# Note that venue_group and venue_type have to be set for Venue Name
+# information to be complete.
+#venue_name=eng:Example venue
+#venue_name=fin:Esimerkkipaikka
+
+# Network Authentication Type
+# This parameter indicates what type of network authentication is used in the
+# network.
+# format: <network auth type indicator (1-octet hex str)> [redirect URL]
+# Network Authentication Type Indicator values:
+# 00 = Acceptance of terms and conditions
+# 01 = On-line enrollment supported
+# 02 = http/https redirection
+# 03 = DNS redirection
+#network_auth_type=00
+#network_auth_type=02http://www.example.com/redirect/me/here/
+
+# IP Address Type Availability
+# format: <1-octet encoded value as hex str>
+# (ipv4_type & 0x3f) << 2 | (ipv6_type & 0x3)
+# ipv4_type:
+# 0 = Address type not available
+# 1 = Public IPv4 address available
+# 2 = Port-restricted IPv4 address available
+# 3 = Single NATed private IPv4 address available
+# 4 = Double NATed private IPv4 address available
+# 5 = Port-restricted IPv4 address and single NATed IPv4 address available
+# 6 = Port-restricted IPv4 address and double NATed IPv4 address available
+# 7 = Availability of the address type is not known
+# ipv6_type:
+# 0 = Address type not available
+# 1 = Address type available
+# 2 = Availability of the address type not known
+#ipaddr_type_availability=14
+
+# Domain Name
+# format: <variable-octet str>[,<variable-octet str>]
+#domain_name=example.com,another.example.com,yet-another.example.com
+
+# 3GPP Cellular Network information
+# format: <MCC1,MNC1>[;<MCC2,MNC2>][;...]
+#anqp_3gpp_cell_net=244,91;310,026;234,56
+
+# NAI Realm information
+# One or more realm can be advertised. Each nai_realm line adds a new realm to
+# the set. These parameters provide information for stations using Interworking
+# network selection to allow automatic connection to a network based on
+# credentials.
+# format: <encoding>,<NAI Realm(s)>[,<EAP Method 1>][,<EAP Method 2>][,...]
+# encoding:
+#	0 = Realm formatted in accordance with IETF RFC 4282
+#	1 = UTF-8 formatted character string that is not formatted in
+#	    accordance with IETF RFC 4282
+# NAI Realm(s): Semi-colon delimited NAI Realm(s)
+# EAP Method: <EAP Method>[:<[AuthParam1:Val1]>][<[AuthParam2:Val2]>][...]
+# AuthParam (Table 8-188 in IEEE Std 802.11-2012):
+# ID 2 = Non-EAP Inner Authentication Type
+#	1 = PAP, 2 = CHAP, 3 = MSCHAP, 4 = MSCHAPV2
+# ID 3 = Inner authentication EAP Method Type
+# ID 5 = Credential Type
+#	1 = SIM, 2 = USIM, 3 = NFC Secure Element, 4 = Hardware Token,
+#	5 = Softoken, 6 = Certificate, 7 = username/password, 9 = Anonymous,
+#	10 = Vendor Specific
+#nai_realm=0,example.com;example.net
+# EAP methods EAP-TLS with certificate and EAP-TTLS/MSCHAPv2 with
+# username/password
+#nai_realm=0,example.org,13[5:6],21[2:4][5:7]
+
+##### Hotspot 2.0 #############################################################
+
+# Enable Hotspot 2.0 support
+#hs20=1
+
+# Disable Downstream Group-Addressed Forwarding (DGAF)
+# This can be used to configure a network where no group-addressed frames are
+# allowed. The AP will not forward any group-address frames to the stations and
+# random GTKs are issued for each station to prevent associated stations from
+# forging such frames to other stations in the BSS.
+#disable_dgaf=1
+
+# Operator Friendly Name
+# This parameter can be used to configure one or more Operator Friendly Name
+# Duples. Each entry has a two or three character language code (ISO-639)
+# separated by colon from the operator friendly name string.
+#hs20_oper_friendly_name=eng:Example operator
+#hs20_oper_friendly_name=fin:Esimerkkioperaattori
+
+# Connection Capability
+# This can be used to advertise what type of IP traffic can be sent through the
+# hotspot (e.g., due to firewall allowing/blocking protocols/ports).
+# format: <IP Protocol>:<Port Number>:<Status>
+# IP Protocol: 1 = ICMP, 6 = TCP, 17 = UDP
+# Port Number: 0..65535
+# Status: 0 = Closed, 1 = Open, 2 = Unknown
+# Each hs20_conn_capab line is added to the list of advertised tuples.
+#hs20_conn_capab=1:0:2
+#hs20_conn_capab=6:22:1
+#hs20_conn_capab=17:5060:0
+
+# WAN Metrics
+# format: <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD>
+# WAN Info: B0-B1: Link Status, B2: Symmetric Link, B3: At Capabity
+#    (encoded as two hex digits)
+#    Link Status: 1 = Link up, 2 = Link down, 3 = Link in test state
+# Downlink Speed: Estimate of WAN backhaul link current downlink speed in kbps;
+#	1..4294967295; 0 = unknown
+# Uplink Speed: Estimate of WAN backhaul link current uplink speed in kbps
+#	1..4294967295; 0 = unknown
+# Downlink Load: Current load of downlink WAN connection (scaled to 255 = 100%)
+# Uplink Load: Current load of uplink WAN connection (scaled to 255 = 100%)
+# Load Measurement Duration: Duration for measuring downlink/uplink load in
+# tenths of a second (1..65535); 0 if load cannot be determined
+#hs20_wan_metrics=01:8000:1000:80:240:3000
+
+# Operating Class Indication
+# List of operating classes the BSSes in this ESS use. The Global operating
+# classes in Table E-4 of IEEE Std 802.11-2012 Annex E define the values that
+# can be used in this.
+# format: hexdump of operating class octets
+# for example, operating classes 81 (2.4 GHz channels 1-13) and 115 (5 GHz
+# channels 36-48):
+#hs20_operating_class=5173
+
 ##### Multiple BSSID support ##################################################
 #
 # Above configuration is using the default interface (wlan#, or multi-SSID VLAN
diff --git a/hostapd/hostapd.eap_user b/hostapd/hostapd.eap_user
index ac9a5d8..12a2c61 100644
--- a/hostapd/hostapd.eap_user
+++ b/hostapd/hostapd.eap_user
@@ -69,6 +69,9 @@
 "3"*		SIM,TTLS,TLS,PEAP,AKA
 "4"*		AKA,TTLS,TLS,PEAP,SIM
 "5"*		SIM,TTLS,TLS,PEAP,AKA
+"6"*		AKA'
+"7"*		AKA'
+"8"*		AKA'
 
 # Wildcard for all other identities
 *		PEAP,TTLS,TLS,SIM,AKA
@@ -89,3 +92,6 @@
 "3"*		SIM	[2]
 "4"*		AKA	[2]
 "5"*		SIM	[2]
+"6"*		AKA'	[2]
+"7"*		AKA'	[2]
+"8"*		AKA'	[2]
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 89125fd..27bea2a 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - command line interface for hostapd daemon
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -71,9 +71,15 @@
 "   wps_pin <uuid> <pin> [timeout] [addr]  add WPS Enrollee PIN\n"
 "   wps_check_pin <PIN>  verify PIN checksum\n"
 "   wps_pbc              indicate button pushed to initiate PBC\n"
+"   wps_cancel           cancel the pending WPS operation\n"
 #ifdef CONFIG_WPS_OOB
 "   wps_oob <type> <path> <method>  use WPS with out-of-band (UFD)\n"
 #endif /* CONFIG_WPS_OOB */
+#ifdef CONFIG_WPS_NFC
+"   wps_nfc_tag_read <hexdump>  report read NFC tag with WPS data\n"
+"   wps_nfc_config_token <WPS/NDEF>  build NFC configuration token\n"
+"   wps_nfc_token <WPS/NDEF/enable/disable>  manager NFC password token\n"
+#endif /* CONFIG_WPS_NFC */
 "   wps_ap_pin <cmd> [params..]  enable/disable AP PIN\n"
 "   wps_config <SSID> <auth> <encr> <key>  configure AP\n"
 #endif /* CONFIG_WPS */
@@ -87,7 +93,12 @@
 static struct wpa_ctrl *ctrl_conn;
 static int hostapd_cli_quit = 0;
 static int hostapd_cli_attached = 0;
-static const char *ctrl_iface_dir = "/var/run/hostapd";
+
+#ifndef CONFIG_CTRL_IFACE_DIR
+#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
+#endif /* CONFIG_CTRL_IFACE_DIR */
+static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
+
 static char *ctrl_ifname = NULL;
 static const char *pid_file = NULL;
 static const char *action_file = NULL;
@@ -392,6 +403,13 @@
 }
 
 
+static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "WPS_CANCEL");
+}
+
+
 #ifdef CONFIG_WPS_OOB
 static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
@@ -426,6 +444,77 @@
 #endif /* CONFIG_WPS_OOB */
 
 
+#ifdef CONFIG_WPS_NFC
+static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	int ret;
+	char *buf;
+	size_t buflen;
+
+	if (argc != 1) {
+		printf("Invalid 'wps_nfc_tag_read' command - one argument "
+		       "is required.\n");
+		return -1;
+	}
+
+	buflen = 18 + os_strlen(argv[0]);
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return -1;
+	os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
+
+	ret = wpa_ctrl_command(ctrl, buf);
+	os_free(buf);
+
+	return ret;
+}
+
+
+static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
+						int argc, char *argv[])
+{
+	char cmd[64];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid 'wps_nfc_config_token' command - one argument "
+		       "is required.\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
+			  argv[0]);
+	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+		printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
+					 int argc, char *argv[])
+{
+	char cmd[64];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid 'wps_nfc_token' command - one argument is "
+		       "required.\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
+	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+		printf("Too long WPS_NFC_TOKEN command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+#endif /* CONFIG_WPS_NFC */
+
+
 static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
@@ -719,9 +808,15 @@
 	{ "wps_pin", hostapd_cli_cmd_wps_pin },
 	{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
 	{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
+	{ "wps_cancel", hostapd_cli_cmd_wps_cancel },
 #ifdef CONFIG_WPS_OOB
 	{ "wps_oob", hostapd_cli_cmd_wps_oob },
 #endif /* CONFIG_WPS_OOB */
+#ifdef CONFIG_WPS_NFC
+	{ "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
+	{ "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
+	{ "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
+#endif /* CONFIG_WPS_NFC */
 	{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
 	{ "wps_config", hostapd_cli_cmd_wps_config },
 #endif /* CONFIG_WPS */
@@ -888,7 +983,7 @@
 
 	eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
 	edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
-		  NULL, NULL, NULL);
+		  NULL, NULL, NULL, NULL);
 	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
 
 	eloop_run();
diff --git a/hostapd/main.c b/hostapd/main.c
index 3918737..56f0002 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -21,6 +21,7 @@
 #include "eap_server/tncs.h"
 #include "ap/hostapd.h"
 #include "ap/ap_config.h"
+#include "ap/ap_drv_ops.h"
 #include "config_file.h"
 #include "eap_register.h"
 #include "dump_state.h"
@@ -42,29 +43,6 @@
 static struct hapd_global global;
 
 
-struct hapd_interfaces {
-	size_t count;
-	struct hostapd_iface **iface;
-};
-
-
-static int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
-				      int (*cb)(struct hostapd_iface *iface,
-						void *ctx), void *ctx)
-{
-	size_t i;
-	int ret;
-
-	for (i = 0; i < interfaces->count; i++) {
-		ret = cb(interfaces->iface[i], ctx);
-		if (ret)
-			return ret;
-	}
-
-	return 0;
-}
-
-
 #ifndef CONFIG_NO_HOSTAPD_LOGGER
 static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
 			      int level, const char *txt, size_t len)
@@ -188,14 +166,9 @@
 	if (hapd_iface == NULL)
 		goto fail;
 
-	hapd_iface->reload_config = hostapd_reload_config;
-	hapd_iface->config_read_cb = hostapd_config_read;
 	hapd_iface->config_fname = os_strdup(config_file);
 	if (hapd_iface->config_fname == NULL)
 		goto fail;
-	hapd_iface->ctrl_iface_init = hostapd_ctrl_iface_init;
-	hapd_iface->ctrl_iface_deinit = hostapd_ctrl_iface_deinit;
-	hapd_iface->for_each_interface = hostapd_for_each_interface;
 
 	conf = hostapd_config_read(hapd_iface->config_fname);
 	if (conf == NULL)
@@ -203,7 +176,7 @@
 	hapd_iface->conf = conf;
 
 	hapd_iface->num_bss = conf->num_bss;
-	hapd_iface->bss = os_zalloc(conf->num_bss *
+	hapd_iface->bss = os_calloc(conf->num_bss,
 				    sizeof(struct hostapd_data *));
 	if (hapd_iface->bss == NULL)
 		goto fail;
@@ -270,13 +243,13 @@
 	}
 	params.bssid = b;
 	params.ifname = hapd->conf->iface;
-	params.ssid = (const u8 *) hapd->conf->ssid.ssid;
+	params.ssid = hapd->conf->ssid.ssid;
 	params.ssid_len = hapd->conf->ssid.ssid_len;
 	params.test_socket = hapd->conf->test_socket;
 	params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
 
 	params.num_bridge = hapd->iface->num_bss;
-	params.bridge = os_zalloc(hapd->iface->num_bss * sizeof(char *));
+	params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *));
 	if (params.bridge == NULL)
 		return -1;
 	for (i = 0; i < hapd->iface->num_bss; i++) {
@@ -306,21 +279,6 @@
 }
 
 
-static void hostapd_interface_deinit_free(struct hostapd_iface *iface)
-{
-	const struct wpa_driver_ops *driver;
-	void *drv_priv;
-	if (iface == NULL)
-		return;
-	driver = iface->bss[0]->driver;
-	drv_priv = iface->bss[0]->drv_priv;
-	hostapd_interface_deinit(iface);
-	if (driver && driver->hapd_deinit)
-		driver->hapd_deinit(drv_priv);
-	hostapd_interface_free(iface);
-}
-
-
 static struct hostapd_iface *
 hostapd_interface_init(struct hapd_interfaces *interfaces,
 		       const char *config_fname, int debug)
@@ -339,10 +297,13 @@
 			iface->bss[0]->conf->logger_stdout_level--;
 	}
 
-	if (hostapd_driver_init(iface) ||
-	    hostapd_setup_interface(iface)) {
-		hostapd_interface_deinit_free(iface);
-		return NULL;
+	if (iface->conf->bss[0].iface[0] != 0 ||
+	    hostapd_drv_none(iface->bss[0])) {
+		if (hostapd_driver_init(iface) ||
+			hostapd_setup_interface(iface)) {
+			hostapd_interface_deinit_free(iface);
+			return NULL;
+		}
 	}
 
 	return iface;
@@ -430,7 +391,7 @@
 		wpa_printf(MSG_ERROR, "No drivers enabled");
 		return -1;
 	}
-	global.drv_priv = os_zalloc(global.drv_count * sizeof(void *));
+	global.drv_priv = os_calloc(global.drv_count, sizeof(void *));
 	if (global.drv_priv == NULL)
 		return -1;
 
@@ -518,13 +479,15 @@
 	fprintf(stderr,
 		"\n"
 		"usage: hostapd [-hdBKtv] [-P <PID file>] [-e <entropy file>] "
-		"<configuration file(s)>\n"
+		"\\\n"
+		"         [-g <global ctrl_iface>] <configuration file(s)>\n"
 		"\n"
 		"options:\n"
 		"   -h   show this usage\n"
 		"   -d   show more debug messages (-dd for even more)\n"
 		"   -B   run daemon in the background\n"
 		"   -e   entropy file\n"
+		"   -g   global control interface path\n"
 		"   -P   PID file\n"
 		"   -K   include key data in debug messages\n"
 #ifdef CONFIG_DEBUG_FILE
@@ -546,6 +509,28 @@
 }
 
 
+static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces,
+					 const char *path)
+{
+	char *pos;
+	os_free(interfaces->global_iface_path);
+	interfaces->global_iface_path = os_strdup(path);
+	if (interfaces->global_iface_path == NULL)
+		return -1;
+	pos = os_strrchr(interfaces->global_iface_path, '/');
+	if (pos == NULL) {
+		os_free(interfaces->global_iface_path);
+		interfaces->global_iface_path = NULL;
+		return -1;
+	}
+
+	*pos = '\0';
+	interfaces->global_iface_name = pos + 1;
+
+	return 0;
+}
+
+
 int main(int argc, char *argv[])
 {
 	struct hapd_interfaces interfaces;
@@ -559,8 +544,19 @@
 	if (os_program_init())
 		return -1;
 
+	os_memset(&interfaces, 0, sizeof(interfaces));
+	interfaces.reload_config = hostapd_reload_config;
+	interfaces.config_read_cb = hostapd_config_read;
+	interfaces.for_each_interface = hostapd_for_each_interface;
+	interfaces.ctrl_iface_init = hostapd_ctrl_iface_init;
+	interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit;
+	interfaces.driver_init = hostapd_driver_init;
+	interfaces.global_iface_path = NULL;
+	interfaces.global_iface_name = NULL;
+	interfaces.global_ctrl_sock = -1;
+
 	for (;;) {
-		c = getopt(argc, argv, "Bde:f:hKP:tv");
+		c = getopt(argc, argv, "Bde:f:hKP:tvg:");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -595,6 +591,9 @@
 			show_version();
 			exit(1);
 			break;
+		case 'g':
+			hostapd_get_global_ctrl_iface(&interfaces, optarg);
+			break;
 
 		default:
 			usage();
@@ -602,7 +601,7 @@
 		}
 	}
 
-	if (optind == argc)
+	if (optind == argc && interfaces.global_iface_path == NULL)
 		usage();
 
 	wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);
@@ -611,11 +610,13 @@
 		wpa_debug_open_file(log_file);
 
 	interfaces.count = argc - optind;
-	interfaces.iface = os_zalloc(interfaces.count *
-				     sizeof(struct hostapd_iface *));
-	if (interfaces.iface == NULL) {
-		wpa_printf(MSG_ERROR, "malloc failed");
-		return -1;
+	if (interfaces.count) {
+		interfaces.iface = os_calloc(interfaces.count,
+					     sizeof(struct hostapd_iface *));
+		if (interfaces.iface == NULL) {
+			wpa_printf(MSG_ERROR, "malloc failed");
+			return -1;
+		}
 	}
 
 	if (hostapd_global_init(&interfaces, entropy_file))
@@ -630,12 +631,15 @@
 			goto out;
 	}
 
+	hostapd_global_ctrl_iface_init(&interfaces);
+
 	if (hostapd_global_run(&interfaces, daemonize, pid_file))
 		goto out;
 
 	ret = 0;
 
  out:
+	hostapd_global_ctrl_iface_deinit(&interfaces);
 	/* Deinitialize all interfaces */
 	for (i = 0; i < interfaces.count; i++)
 		hostapd_interface_deinit_free(interfaces.iface[i]);
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index 2a04560..7563b52 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / RADIUS Accounting
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -28,6 +28,8 @@
 
 static void accounting_sta_get_id(struct hostapd_data *hapd,
 				  struct sta_info *sta);
+static void accounting_sta_interim(struct hostapd_data *hapd,
+				   struct sta_info *sta);
 
 
 static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
@@ -39,6 +41,7 @@
 	u8 *val;
 	size_t len;
 	int i;
+	struct wpabuf *b;
 
 	msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
 			     radius_client_get_id(hapd->radius));
@@ -67,7 +70,9 @@
 		goto fail;
 	}
 
-	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
+					    RADIUS_ATTR_ACCT_AUTHENTIC) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
 				       hapd->conf->ieee802_1x ?
 				       RADIUS_ACCT_AUTHENTIC_RADIUS :
 				       RADIUS_ACCT_AUTHENTIC_LOCAL)) {
@@ -76,7 +81,17 @@
 	}
 
 	if (sta) {
+		/* Use 802.1X identity if available */
 		val = ieee802_1x_get_identity(sta->eapol_sm, &len);
+
+		/* Use RADIUS ACL identity if 802.1X provides no identity */
+		if (!val && sta->identity) {
+			val = (u8 *) sta->identity;
+			len = os_strlen(sta->identity);
+		}
+
+		/* Use STA MAC if neither 802.1X nor RADIUS ACL provided
+		 * identity */
 		if (!val) {
 			os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
 				    MAC2STR(sta->addr));
@@ -91,70 +106,11 @@
 		}
 	}
 
-	if (hapd->conf->own_ip_addr.af == AF_INET &&
-	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
-				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
-		printf("Could not add NAS-IP-Address\n");
+	if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
+				   msg) < 0)
 		goto fail;
-	}
-
-#ifdef CONFIG_IPV6
-	if (hapd->conf->own_ip_addr.af == AF_INET6 &&
-	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
-				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
-		printf("Could not add NAS-IPv6-Address\n");
-		goto fail;
-	}
-#endif /* CONFIG_IPV6 */
-
-	if (hapd->conf->nas_identifier &&
-	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
-				 (u8 *) hapd->conf->nas_identifier,
-				 os_strlen(hapd->conf->nas_identifier))) {
-		printf("Could not add NAS-Identifier\n");
-		goto fail;
-	}
-
-	if (sta &&
-	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
-		printf("Could not add NAS-Port\n");
-		goto fail;
-	}
-
-	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
-		    MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
-	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
-				 (u8 *) buf, os_strlen(buf))) {
-		printf("Could not add Called-Station-Id\n");
-		goto fail;
-	}
 
 	if (sta) {
-		os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
-			    MAC2STR(sta->addr));
-		if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
-					 (u8 *) buf, os_strlen(buf))) {
-			printf("Could not add Calling-Station-Id\n");
-			goto fail;
-		}
-
-		if (!radius_msg_add_attr_int32(
-			    msg, RADIUS_ATTR_NAS_PORT_TYPE,
-			    RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
-			printf("Could not add NAS-Port-Type\n");
-			goto fail;
-		}
-
-		os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
-			    radius_sta_rate(hapd, sta) / 2,
-			    (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
-			    radius_mode_txt(hapd));
-		if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
-					 (u8 *) buf, os_strlen(buf))) {
-			printf("Could not add Connect-Info\n");
-			goto fail;
-		}
-
 		for (i = 0; ; i++) {
 			val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
 							  i);
@@ -167,6 +123,24 @@
 				goto fail;
 			}
 		}
+
+		b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+		if (b &&
+		    !radius_msg_add_attr(msg,
+					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+					 wpabuf_head(b), wpabuf_len(b))) {
+			wpa_printf(MSG_ERROR, "Could not add CUI");
+			goto fail;
+		}
+
+		if (!b && sta->radius_cui &&
+		    !radius_msg_add_attr(msg,
+					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+					 (u8 *) sta->radius_cui,
+					 os_strlen(sta->radius_cui))) {
+			wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
+			goto fail;
+		}
 	}
 
 	return msg;
@@ -259,8 +233,9 @@
 			       hapd, sta);
 
 	msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
-	if (msg)
-		radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
+	if (msg &&
+	    radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
+		radius_msg_free(msg);
 
 	sta->acct_session_started = 1;
 }
@@ -358,9 +333,10 @@
 		goto fail;
 	}
 
-	radius_client_send(hapd->radius, msg,
-			   stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
-			   sta->addr);
+	if (radius_client_send(hapd->radius, msg,
+			       stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
+			       sta->addr) < 0)
+		goto fail;
 	return;
 
  fail:
@@ -373,7 +349,8 @@
  * @hapd: hostapd BSS data
  * @sta: The station
  */
-void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta)
+static void accounting_sta_interim(struct hostapd_data *hapd,
+				   struct sta_info *sta)
 {
 	if (sta->acct_session_started)
 		accounting_sta_report(hapd, sta, 0);
@@ -463,7 +440,8 @@
 		return;
 	}
 
-	radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
+	if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
+		radius_msg_free(msg);
 }
 
 
diff --git a/src/ap/accounting.h b/src/ap/accounting.h
index 797e24d..9d13d01 100644
--- a/src/ap/accounting.h
+++ b/src/ap/accounting.h
@@ -9,7 +9,6 @@
 #ifndef ACCOUNTING_H
 #define ACCOUNTING_H
 
-void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta);
 #ifdef CONFIG_NO_ACCOUNTING
 static inline void accounting_sta_start(struct hostapd_data *hapd,
 					struct sta_info *sta)
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index b9f5994..31e1c19 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration helper functions
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -87,6 +87,8 @@
 #ifdef CONFIG_IEEE80211R
 	bss->ft_over_ds = 1;
 #endif /* CONFIG_IEEE80211R */
+
+	bss->radius_das_time_window = 300;
 }
 
 
@@ -102,9 +104,9 @@
 	const struct hostapd_wmm_ac_params ac_be =
 		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
 	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
-		{ aCWmin - 1, aCWmin, 2, 3000 / 32, 1 };
+		{ aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
 	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
-		{ aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 };
+		{ aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
 	const struct hostapd_tx_queue_params txq_bk =
 		{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
 	const struct hostapd_tx_queue_params txq_be =
@@ -336,6 +338,30 @@
 }
 
 
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type)
+{
+	for (; attr; attr = attr->next) {
+		if (attr->type == type)
+			return attr;
+	}
+	return NULL;
+}
+
+
+static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
+{
+	struct hostapd_radius_attr *prev;
+
+	while (attr) {
+		prev = attr;
+		attr = attr->next;
+		wpabuf_free(prev->val);
+		os_free(prev);
+	}
+}
+
+
 static void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
 {
 	os_free(user->identity);
@@ -392,6 +418,8 @@
 				   conf->radius->num_auth_servers);
 	hostapd_config_free_radius(conf->radius->acct_servers,
 				   conf->radius->num_acct_servers);
+	hostapd_config_free_radius_attr(conf->radius_auth_req_attr);
+	hostapd_config_free_radius_attr(conf->radius_acct_req_attr);
 	os_free(conf->rsn_preauth_interfaces);
 	os_free(conf->ctrl_interface);
 	os_free(conf->ca_cert);
@@ -406,6 +434,7 @@
 	os_free(conf->radius_server_clients);
 	os_free(conf->test_socket);
 	os_free(conf->radius);
+	os_free(conf->radius_das_shared_secret);
 	hostapd_config_free_vlan(conf);
 	if (conf->ssid.dyn_vlan_keys) {
 		struct hostapd_ssid *ssid = &conf->ssid;
@@ -445,9 +474,6 @@
 	}
 #endif /* CONFIG_IEEE80211R */
 
-#ifdef ANDROID_P2P
-	os_free(conf->prioritize);
-#endif
 #ifdef CONFIG_WPS
 	os_free(conf->wps_pin_requests);
 	os_free(conf->device_name);
@@ -465,13 +491,30 @@
 	os_free(conf->model_description);
 	os_free(conf->model_url);
 	os_free(conf->upc);
+	wpabuf_free(conf->wps_nfc_dh_pubkey);
+	wpabuf_free(conf->wps_nfc_dh_privkey);
+	wpabuf_free(conf->wps_nfc_dev_pw);
 #endif /* CONFIG_WPS */
 
 	os_free(conf->roaming_consortium);
+	os_free(conf->venue_name);
+	os_free(conf->nai_realm_data);
+	os_free(conf->network_auth_type);
+	os_free(conf->anqp_3gpp_cell_net);
+	os_free(conf->domain_name);
 
 #ifdef CONFIG_RADIUS_TEST
 	os_free(conf->dump_msk_file);
 #endif /* CONFIG_RADIUS_TEST */
+
+#ifdef CONFIG_HS20
+	os_free(conf->hs20_oper_friendly_name);
+	os_free(conf->hs20_wan_metrics);
+	os_free(conf->hs20_connection_capability);
+	os_free(conf->hs20_operating_class);
+#endif /* CONFIG_HS20 */
+
+	wpabuf_free(conf->vendor_elements);
 }
 
 
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 2ec25ad..f5e4a6a 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration definitions and helpers functions
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,6 +12,7 @@
 #include "common/defs.h"
 #include "ip_addr.h"
 #include "common/wpa_common.h"
+#include "common/ieee802_11_common.h"
 #include "wps/wps.h"
 
 #define MAX_STA_COUNT 2007
@@ -48,7 +49,7 @@
 } secpolicy;
 
 struct hostapd_ssid {
-	char ssid[HOSTAPD_MAX_SSID_LEN + 1];
+	u8 ssid[HOSTAPD_MAX_SSID_LEN];
 	size_t ssid_len;
 	int ssid_set;
 
@@ -65,6 +66,10 @@
 #define DYNAMIC_VLAN_OPTIONAL 1
 #define DYNAMIC_VLAN_REQUIRED 2
 	int dynamic_vlan;
+#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
+#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
+#define DYNAMIC_VLAN_NAMING_END 2
+	int vlan_naming;
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
 	char *vlan_tagged_interface;
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
@@ -116,6 +121,12 @@
 	int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
 };
 
+struct hostapd_radius_attr {
+	u8 type;
+	struct wpabuf *val;
+	struct hostapd_radius_attr *next;
+};
+
 
 #define NUM_TX_QUEUES 4
 
@@ -126,14 +137,6 @@
 	int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
 };
 
-struct hostapd_wmm_ac_params {
-	int cwmin;
-	int cwmax;
-	int aifs;
-	int txop_limit; /* in units of 32us */
-	int admission_control_mandatory;
-};
-
 
 #define MAX_ROAMING_CONSORTIUM_LEN 15
 
@@ -142,6 +145,29 @@
 	u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
 };
 
+struct hostapd_lang_string {
+	u8 lang[3];
+	u8 name_len;
+	u8 name[252];
+};
+
+#define MAX_NAI_REALMS 10
+#define MAX_NAI_REALMLEN 255
+#define MAX_NAI_EAP_METHODS 5
+#define MAX_NAI_AUTH_TYPES 4
+struct hostapd_nai_realm_data {
+	u8 encoding;
+	char realm_buf[MAX_NAI_REALMLEN + 1];
+	char *realm[MAX_NAI_REALMS];
+	u8 eap_method_count;
+	struct hostapd_nai_realm_eap {
+		u8 eap_method;
+		u8 num_auths;
+		u8 auth_id[MAX_NAI_AUTH_TYPES];
+		u8 auth_val[MAX_NAI_AUTH_TYPES];
+	} eap_method[MAX_NAI_EAP_METHODS];
+};
+
 /**
  * struct hostapd_bss_config - Per-BSS configuration
  */
@@ -171,6 +197,15 @@
 	char *nas_identifier;
 	struct hostapd_radius_servers *radius;
 	int acct_interim_interval;
+	int radius_request_cui;
+	struct hostapd_radius_attr *radius_auth_req_attr;
+	struct hostapd_radius_attr *radius_acct_req_attr;
+	int radius_das_port;
+	unsigned int radius_das_time_window;
+	int radius_das_require_event_timestamp;
+	struct hostapd_ip_addr radius_das_client_addr;
+	u8 *radius_das_shared_secret;
+	size_t radius_das_shared_secret_len;
 
 	struct hostapd_ssid ssid;
 
@@ -302,9 +337,6 @@
 	u8 uuid[16];
 	char *wps_pin_requests;
 	char *device_name;
-#ifdef ANDROID_P2P
-	char *prioritize;
-#endif
 	char *manufacturer;
 	char *model_name;
 	char *model_number;
@@ -326,6 +358,10 @@
 	char *model_url;
 	char *upc;
 	struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+	int wps_nfc_dev_pw_id;
+	struct wpabuf *wps_nfc_dh_pubkey;
+	struct wpabuf *wps_nfc_dh_privkey;
+	struct wpabuf *wps_nfc_dev_pw;
 #endif /* CONFIG_WPS */
 	int pbc_in_m1;
 
@@ -343,6 +379,7 @@
 #define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
 	int tdls;
 	int disable_11n;
+	int disable_11ac;
 
 	/* IEEE 802.11v */
 	int time_advertisement;
@@ -364,11 +401,51 @@
 	unsigned int roaming_consortium_count;
 	struct hostapd_roaming_consortium *roaming_consortium;
 
+	/* IEEE 802.11u - Venue Name duples */
+	unsigned int venue_name_count;
+	struct hostapd_lang_string *venue_name;
+
+	/* IEEE 802.11u - Network Authentication Type */
+	u8 *network_auth_type;
+	size_t network_auth_type_len;
+
+	/* IEEE 802.11u - IP Address Type Availability */
+	u8 ipaddr_type_availability;
+	u8 ipaddr_type_configured;
+
+	/* IEEE 802.11u - 3GPP Cellular Network */
+	u8 *anqp_3gpp_cell_net;
+	size_t anqp_3gpp_cell_net_len;
+
+	/* IEEE 802.11u - Domain Name */
+	u8 *domain_name;
+	size_t domain_name_len;
+
+	unsigned int nai_realm_count;
+	struct hostapd_nai_realm_data *nai_realm_data;
+
+	u16 gas_comeback_delay;
+	int gas_frag_limit;
+
+#ifdef CONFIG_HS20
+	int hs20;
+	int disable_dgaf;
+	unsigned int hs20_oper_friendly_name_count;
+	struct hostapd_lang_string *hs20_oper_friendly_name;
+	u8 *hs20_wan_metrics;
+	u8 *hs20_connection_capability;
+	size_t hs20_connection_capability_len;
+	u8 *hs20_operating_class;
+	u8 hs20_operating_class_len;
+#endif /* CONFIG_HS20 */
+
 	u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
 
 #ifdef CONFIG_RADIUS_TEST
 	char *dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
+
+	struct wpabuf *vendor_elements;
 };
 
 
@@ -423,6 +500,11 @@
 	int ieee80211n;
 	int secondary_channel;
 	int require_ht;
+	u32 vht_capab;
+	int ieee80211ac;
+	int require_vht;
+	u8 vht_oper_chwidth;
+	u8 vht_oper_centr_freq_seg0_idx;
 };
 
 
@@ -444,5 +526,7 @@
 const struct hostapd_eap_user *
 hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
 		     size_t identity_len, int phase2);
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
 
 #endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index d07cc6b..02da25b 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -12,11 +12,13 @@
 #include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
 #include "wps/wps.h"
+#include "p2p/p2p.h"
 #include "hostapd.h"
 #include "ieee802_11.h"
 #include "sta_info.h"
 #include "ap_config.h"
 #include "p2p_hostapd.h"
+#include "hs20.h"
 #include "ap_drv_ops.h"
 
 
@@ -147,6 +149,30 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (hapd->p2p_group) {
+		struct wpabuf *a;
+		a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
+		if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
+			wpabuf_put_buf(assocresp, a);
+		wpabuf_free(a);
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+#ifdef CONFIG_HS20
+	pos = buf;
+	pos = hostapd_eid_hs20_indication(hapd, pos);
+	if (pos != buf) {
+		if (wpabuf_resize(&beacon, pos - buf) != 0)
+			goto fail;
+		wpabuf_put_data(beacon, buf, pos - buf);
+
+		if (wpabuf_resize(&proberesp, pos - buf) != 0)
+			goto fail;
+		wpabuf_put_data(proberesp, buf, pos - buf);
+	}
+#endif /* CONFIG_HS20 */
+
 	*beacon_ret = beacon;
 	*proberesp_ret = proberesp;
 	*assocresp_ret = assocresp;
@@ -584,3 +610,25 @@
 	return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
 					  reason);
 }
+
+
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
+			 const u8 *peer, u8 *buf, u16 *buf_len)
+{
+	if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
+		return 0;
+	return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
+				      buf_len);
+}
+
+
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+			    unsigned int wait, const u8 *dst, const u8 *data,
+			    size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->send_action == NULL)
+		return 0;
+	return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+					 hapd->own_addr, hapd->own_addr, data,
+					 len, 0);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 8038fa2..9c53b99 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -86,6 +86,9 @@
 			   const u8 *addr, int reason);
 int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
 			     const u8 *addr, int reason);
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+			    unsigned int wait, const u8 *dst, const u8 *data,
+			    size_t len);
 int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
 			 u16 auth_alg);
 int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
@@ -98,6 +101,10 @@
 
 #include "drivers/driver.h"
 
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+			 enum wnm_oper oper, const u8 *peer,
+			 u8 *buf, u16 *buf_len);
+
 static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
 						  int enabled)
 {
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index b4252cf..18090ca 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -251,23 +251,9 @@
 		ap->ssid_len = len;
 	}
 
-	os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX);
-	len = 0;
-	if (elems->supp_rates) {
-		len = elems->supp_rates_len;
-		if (len > WLAN_SUPP_RATES_MAX)
-			len = WLAN_SUPP_RATES_MAX;
-		os_memcpy(ap->supported_rates, elems->supp_rates, len);
-	}
-	if (elems->ext_supp_rates) {
-		int len2;
-		if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX)
-			len2 = WLAN_SUPP_RATES_MAX - len;
-		else
-			len2 = elems->ext_supp_rates_len;
-		os_memcpy(ap->supported_rates + len, elems->ext_supp_rates,
-			  len2);
-	}
+	merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX,
+			  elems->supp_rates, elems->supp_rates_len,
+			  elems->ext_supp_rates, elems->ext_supp_rates_len);
 
 	ap->wpa = elems->wpa_ie != NULL;
 
@@ -289,10 +275,8 @@
 	ap->num_beacons++;
 	os_get_time(&now);
 	ap->last_beacon = now.sec;
-	if (fi) {
-		ap->ssi_signal = fi->ssi_signal;
+	if (fi)
 		ap->datarate = fi->datarate;
-	}
 
 	if (!new_ap && ap != iface->ap_list) {
 		/* move AP entry into the beginning of the list so that the
@@ -320,7 +304,7 @@
 #endif /* CONFIG_IEEE80211N */
 
 	if (set_beacon)
-		ieee802_11_set_beacons(iface);
+		ieee802_11_update_beacons(iface);
 }
 
 
@@ -375,7 +359,7 @@
 	}
 
 	if (set_beacon)
-		ieee802_11_set_beacons(iface);
+		ieee802_11_update_beacons(iface);
 }
 
 
diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h
index 201f6ec..f0b4125 100644
--- a/src/ap/ap_list.h
+++ b/src/ap/ap_list.h
@@ -34,7 +34,6 @@
 
 	int channel;
 	int datarate; /* in 100 kbps */
-	int ssi_signal;
 
 	int ht_support;
 
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 4ea8684..f761bf5 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -33,6 +33,7 @@
 #include "p2p_hostapd.h"
 #include "ap_drv_ops.h"
 #include "beacon.h"
+#include "hs20.h"
 
 
 #ifdef NEED_AP_MLME
@@ -205,6 +206,8 @@
 	if (hapd->p2p_probe_resp_ie)
 		buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
 #endif /* CONFIG_P2P */
+	if (hapd->conf->vendor_elements)
+		buflen += wpabuf_len(hapd->conf->vendor_elements);
 	resp = os_zalloc(buflen);
 	if (resp == NULL)
 		return NULL;
@@ -262,6 +265,11 @@
 	pos = hostapd_eid_adv_proto(hapd, pos);
 	pos = hostapd_eid_roaming_consortium(hapd, pos);
 
+#ifdef CONFIG_IEEE80211AC
+	pos = hostapd_eid_vht_capabilities(hapd, pos);
+	pos = hostapd_eid_vht_operation(hapd, pos);
+#endif /* CONFIG_IEEE80211AC */
+
 	/* Wi-Fi Alliance WMM */
 	pos = hostapd_eid_wmm(hapd, pos);
 
@@ -287,13 +295,24 @@
 		pos = hostapd_eid_p2p_manage(hapd, pos);
 #endif /* CONFIG_P2P_MANAGER */
 
+#ifdef CONFIG_HS20
+	pos = hostapd_eid_hs20_indication(hapd, pos);
+#endif /* CONFIG_HS20 */
+
+	if (hapd->conf->vendor_elements) {
+		os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
+			  wpabuf_len(hapd->conf->vendor_elements));
+		pos += wpabuf_len(hapd->conf->vendor_elements);
+	}
+
 	*resp_len = pos - (u8 *) resp;
 	return (u8 *) resp;
 }
 
 
 void handle_probe_req(struct hostapd_data *hapd,
-		      const struct ieee80211_mgmt *mgmt, size_t len)
+		      const struct ieee80211_mgmt *mgmt, size_t len,
+		      int ssi_signal)
 {
 	u8 *resp;
 	struct ieee802_11_elems elems;
@@ -311,7 +330,7 @@
 	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
 		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
 					    mgmt->sa, mgmt->da, mgmt->bssid,
-					    ie, ie_len) > 0)
+					    ie, ie_len, ssi_signal) > 0)
 			return;
 
 	if (!hapd->iconf->send_probe_response)
@@ -517,6 +536,8 @@
 	if (hapd->p2p_beacon_ie)
 		tail_len += wpabuf_len(hapd->p2p_beacon_ie);
 #endif /* CONFIG_P2P */
+	if (hapd->conf->vendor_elements)
+		tail_len += wpabuf_len(hapd->conf->vendor_elements);
 	tailpos = tail = os_malloc(tail_len);
 	if (head == NULL || tail == NULL) {
 		wpa_printf(MSG_ERROR, "Failed to set beacon data");
@@ -594,6 +615,11 @@
 	tailpos = hostapd_eid_adv_proto(hapd, tailpos);
 	tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
 
+#ifdef CONFIG_IEEE80211AC
+	tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
+	tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+#endif /* CONFIG_IEEE80211AC */
+
 	/* Wi-Fi Alliance WMM */
 	tailpos = hostapd_eid_wmm(hapd, tailpos);
 
@@ -618,6 +644,16 @@
 		tailpos = hostapd_eid_p2p_manage(hapd, tailpos);
 #endif /* CONFIG_P2P_MANAGER */
 
+#ifdef CONFIG_HS20
+	tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
+#endif /* CONFIG_HS20 */
+
+	if (hapd->conf->vendor_elements) {
+		os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
+			  wpabuf_len(hapd->conf->vendor_elements));
+		tailpos += wpabuf_len(hapd->conf->vendor_elements);
+	}
+
 	tail_len = tailpos > tail ? tailpos - tail : 0;
 
 	resp = hostapd_probe_resp_offloads(hapd, &resp_len);
@@ -632,8 +668,8 @@
 	params.proberesp_len = resp_len;
 	params.dtim_period = hapd->conf->dtim_period;
 	params.beacon_int = hapd->iconf->beacon_int;
-	params.basic_rates = hapd->iconf->basic_rates;
-	params.ssid = (u8 *) hapd->conf->ssid.ssid;
+	params.basic_rates = hapd->iface->basic_rates;
+	params.ssid = hapd->conf->ssid.ssid;
 	params.ssid_len = hapd->conf->ssid.ssid_len;
 	params.pairwise_ciphers = hapd->conf->rsn_pairwise ?
 		hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise;
@@ -682,6 +718,10 @@
 	    !is_zero_ether_addr(hapd->conf->hessid))
 		params.hessid = hapd->conf->hessid;
 	params.access_network_type = hapd->conf->access_network_type;
+	params.ap_max_inactivity = hapd->conf->ap_max_inactivity;
+#ifdef CONFIG_HS20
+	params.disable_dgaf = hapd->conf->disable_dgaf;
+#endif /* CONFIG_HS20 */
 	if (hostapd_drv_set_ap(hapd, &params))
 		wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
 	hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
@@ -699,4 +739,14 @@
 		ieee802_11_set_beacon(iface->bss[i]);
 }
 
+
+/* only update beacons if started */
+void ieee802_11_update_beacons(struct hostapd_iface *iface)
+{
+	size_t i;
+	for (i = 0; i < iface->num_bss; i++)
+		if (iface->bss[i]->beacon_set_done)
+			ieee802_11_set_beacon(iface->bss[i]);
+}
+
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index a944f5f..37f10d2 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -19,8 +19,10 @@
 struct ieee80211_mgmt;
 
 void handle_probe_req(struct hostapd_data *hapd,
-		      const struct ieee80211_mgmt *mgmt, size_t len);
+		      const struct ieee80211_mgmt *mgmt, size_t len,
+		      int ssi_signal);
 void ieee802_11_set_beacon(struct hostapd_data *hapd);
 void ieee802_11_set_beacons(struct hostapd_iface *iface);
+void ieee802_11_update_beacons(struct hostapd_iface *iface);
 
 #endif /* BEACON_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index d76b381..ab9c83e 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "common/ieee802_11_defs.h"
 #include "hostapd.h"
 #include "ieee802_1x.h"
 #include "wpa_auth.h"
@@ -17,6 +18,7 @@
 #include "wps_hostapd.h"
 #include "p2p_hostapd.h"
 #include "ctrl_iface_ap.h"
+#include "ap_drv_ops.h"
 
 
 static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
@@ -100,3 +102,170 @@
 	}		
 	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
 }
+
+
+#ifdef CONFIG_P2P_MANAGER
+static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
+				  u8 minor_reason_code, const u8 *addr)
+{
+	struct ieee80211_mgmt *mgmt;
+	int ret;
+	u8 *pos;
+
+	if (hapd->driver->send_frame == NULL)
+		return -1;
+
+	mgmt = os_zalloc(sizeof(*mgmt) + 100);
+	if (mgmt == NULL)
+		return -1;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
+		" with minor reason code %u (stype=%u)",
+		MAC2STR(addr), minor_reason_code, stype);
+
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
+	os_memcpy(mgmt->da, addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	if (stype == WLAN_FC_STYPE_DEAUTH) {
+		mgmt->u.deauth.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
+	} else {
+		mgmt->u.disassoc.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
+	}
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = 4 + 3 + 1;
+	WPA_PUT_BE24(pos, OUI_WFA);
+	pos += 3;
+	*pos++ = P2P_OUI_TYPE;
+
+	*pos++ = P2P_ATTR_MINOR_REASON_CODE;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	*pos++ = minor_reason_code;
+
+	ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt,
+				       pos - (u8 *) mgmt, 1);
+	os_free(mgmt);
+
+	return ret < 0 ? -1 : 0;
+}
+#endif /* CONFIG_P2P_MANAGER */
+
+
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+				      const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	const char *pos;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
+		txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	pos = os_strstr(txtaddr, " test=");
+	if (pos) {
+		struct ieee80211_mgmt mgmt;
+		int encrypt;
+		if (hapd->driver->send_frame == NULL)
+			return -1;
+		pos += 6;
+		encrypt = atoi(pos);
+		os_memset(&mgmt, 0, sizeof(mgmt));
+		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+						  WLAN_FC_STYPE_DEAUTH);
+		os_memcpy(mgmt.da, addr, ETH_ALEN);
+		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+		mgmt.u.deauth.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
+					     IEEE80211_HDRLEN +
+					     sizeof(mgmt.u.deauth),
+					     encrypt) < 0)
+			return -1;
+		return 0;
+	}
+
+#ifdef CONFIG_P2P_MANAGER
+	pos = os_strstr(txtaddr, " p2p=");
+	if (pos) {
+		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
+					      atoi(pos + 5), addr);
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+	hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		ap_sta_deauthenticate(hapd, sta,
+				      WLAN_REASON_PREV_AUTH_NOT_VALID);
+	else if (addr[0] == 0xff)
+		hostapd_free_stas(hapd);
+
+	return 0;
+}
+
+
+int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+				    const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	const char *pos;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
+		txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	pos = os_strstr(txtaddr, " test=");
+	if (pos) {
+		struct ieee80211_mgmt mgmt;
+		int encrypt;
+		if (hapd->driver->send_frame == NULL)
+			return -1;
+		pos += 6;
+		encrypt = atoi(pos);
+		os_memset(&mgmt, 0, sizeof(mgmt));
+		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+						  WLAN_FC_STYPE_DISASSOC);
+		os_memcpy(mgmt.da, addr, ETH_ALEN);
+		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+		mgmt.u.disassoc.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
+					     IEEE80211_HDRLEN +
+					     sizeof(mgmt.u.deauth),
+					     encrypt) < 0)
+			return -1;
+		return 0;
+	}
+
+#ifdef CONFIG_P2P_MANAGER
+	pos = os_strstr(txtaddr, " p2p=");
+	if (pos) {
+		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
+					      atoi(pos + 5), addr);
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+	hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		ap_sta_disassociate(hapd, sta,
+				    WLAN_REASON_PREV_AUTH_NOT_VALID);
+	else if (addr[0] == 0xff)
+		hostapd_free_stas(hapd);
+
+	return 0;
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index 0e4286d..e83f894 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -15,5 +15,9 @@
 			   char *buf, size_t buflen);
 int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
 				char *buf, size_t buflen);
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+				      const char *txtaddr);
+int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+				    const char *txtaddr);
 
 #endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 820a903..23fa241 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -16,6 +16,7 @@
 #include "crypto/random.h"
 #include "p2p/p2p.h"
 #include "wps/wps.h"
+#include "wnm_ap.h"
 #include "hostapd.h"
 #include "ieee802_11.h"
 #include "sta_info.h"
@@ -26,6 +27,7 @@
 #include "wps_hostapd.h"
 #include "ap_drv_ops.h"
 #include "ap_config.h"
+#include "hw_features.h"
 
 
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -36,7 +38,12 @@
 	struct ieee802_11_elems elems;
 	const u8 *ie;
 	size_t ielen;
+#ifdef CONFIG_IEEE80211R
+	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+	u8 *p = buf;
+#endif /* CONFIG_IEEE80211R */
 	u16 reason = WLAN_REASON_UNSPECIFIED;
+	u16 status = WLAN_STATUS_SUCCESS;
 
 	if (addr == NULL) {
 		/*
@@ -78,10 +85,19 @@
 	sta = ap_get_sta(hapd, addr);
 	if (sta) {
 		accounting_sta_stop(hapd, sta);
+
+		/*
+		 * Make sure that the previously registered inactivity timer
+		 * will not remove the STA immediately.
+		 */
+		sta->timeout_next = STA_NULLFUNC;
 	} else {
 		sta = ap_sta_add(hapd, addr);
-		if (sta == NULL)
+		if (sta == NULL) {
+			hostapd_drv_sta_disassoc(hapd, addr,
+						 WLAN_REASON_DISASSOC_AP_BUSY);
 			return -1;
+		}
 	}
 	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
 
@@ -136,27 +152,85 @@
 			return -1;
 		}
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
-					  ie, ielen, NULL, 0);
+					  ie, ielen,
+					  elems.mdie, elems.mdie_len);
 		if (res != WPA_IE_OK) {
 			wpa_printf(MSG_DEBUG, "WPA/RSN information element "
 				   "rejected? (res %u)", res);
 			wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
-			if (res == WPA_INVALID_GROUP)
+			if (res == WPA_INVALID_GROUP) {
 				reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
-			else if (res == WPA_INVALID_PAIRWISE)
+				status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+			} else if (res == WPA_INVALID_PAIRWISE) {
 				reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
-			else if (res == WPA_INVALID_AKMP)
+				status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+			} else if (res == WPA_INVALID_AKMP) {
 				reason = WLAN_REASON_AKMP_NOT_VALID;
+				status = WLAN_STATUS_AKMP_NOT_VALID;
+			}
 #ifdef CONFIG_IEEE80211W
-			else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+			else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) {
 				reason = WLAN_REASON_INVALID_IE;
-			else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+				status = WLAN_STATUS_INVALID_IE;
+			} else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) {
 				reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+				status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+			}
 #endif /* CONFIG_IEEE80211W */
-			else
+			else {
 				reason = WLAN_REASON_INVALID_IE;
+				status = WLAN_STATUS_INVALID_IE;
+			}
 			goto fail;
 		}
+#ifdef CONFIG_IEEE80211W
+		if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+		    sta->sa_query_count > 0)
+			ap_check_sa_query_timeout(hapd, sta);
+		if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+		    (sta->auth_alg != WLAN_AUTH_FT)) {
+			/*
+			 * STA has already been associated with MFP and SA
+			 * Query timeout has not been reached. Reject the
+			 * association attempt temporarily and start SA Query,
+			 * if one is not pending.
+			 */
+
+			if (sta->sa_query_count == 0)
+				ap_sta_start_sa_query(hapd, sta);
+
+#ifdef CONFIG_IEEE80211R
+			status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+			p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+
+			hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
+					  p - buf);
+#endif /* CONFIG_IEEE80211R */
+			return 0;
+		}
+
+		if (wpa_auth_uses_mfp(sta->wpa_sm))
+			sta->flags |= WLAN_STA_MFP;
+		else
+			sta->flags &= ~WLAN_STA_MFP;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R
+		if (sta->auth_alg == WLAN_AUTH_FT) {
+			status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
+							 req_ies_len);
+			if (status != WLAN_STATUS_SUCCESS) {
+				if (status == WLAN_STATUS_INVALID_PMKID)
+					reason = WLAN_REASON_INVALID_IE;
+				if (status == WLAN_STATUS_INVALID_MDIE)
+					reason = WLAN_REASON_INVALID_IE;
+				if (status == WLAN_STATUS_INVALID_FTIE)
+					reason = WLAN_REASON_INVALID_IE;
+				goto fail;
+			}
+		}
+#endif /* CONFIG_IEEE80211R */
 	} else if (hapd->conf->wps_state) {
 #ifdef CONFIG_WPS
 		struct wpabuf *wps;
@@ -168,6 +242,7 @@
 #ifdef CONFIG_WPS_STRICT
 		if (wps && wps_validate_assoc_req(wps) < 0) {
 			reason = WLAN_REASON_INVALID_IE;
+			status = WLAN_STATUS_INVALID_IE;
 			wpabuf_free(wps);
 			goto fail;
 		}
@@ -188,9 +263,24 @@
 skip_wpa_check:
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_IEEE80211R
+	p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
+					sta->auth_alg, req_ies, req_ies_len);
+
+	hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+#else /* CONFIG_IEEE80211R */
+	/* Keep compiler silent about unused variables */
+	if (status) {
+	}
+#endif /* CONFIG_IEEE80211R */
+
 	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
 	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
-	wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+
+	if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+	else
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
 
 	hostapd_new_assoc_sta(hapd, sta, !new_assoc);
 
@@ -206,6 +296,9 @@
 	return 0;
 
 fail:
+#ifdef CONFIG_IEEE80211R
+	hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+#endif /* CONFIG_IEEE80211R */
 	hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
 	ap_free_sta(hapd, sta);
 	return -1;
@@ -264,8 +357,36 @@
 }
 
 
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+			     int offset)
+{
+#ifdef NEED_AP_MLME
+	int channel;
+
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "driver had channel switch: "
+		       "freq=%d, ht=%d, offset=%d", freq, ht, offset);
+
+	hapd->iface->freq = freq;
+
+	channel = hostapd_hw_get_channel(hapd, freq);
+	if (!channel) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING, "driver switched to "
+			       "bad channel!");
+		return;
+	}
+
+	hapd->iconf->channel = channel;
+	hapd->iconf->ieee80211n = ht;
+	hapd->iconf->secondary_channel = offset;
+#endif /* NEED_AP_MLME */
+}
+
+
 int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
-			 const u8 *bssid, const u8 *ie, size_t ie_len)
+			 const u8 *bssid, const u8 *ie, size_t ie_len,
+			 int ssi_signal)
 {
 	size_t i;
 	int ret = 0;
@@ -276,7 +397,8 @@
 	random_add_randomness(sa, ETH_ALEN);
 	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
 		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
-					    sa, da, bssid, ie, ie_len) > 0) {
+					    sa, da, bssid, ie, ie_len,
+					    ssi_signal) > 0) {
 			ret = 1;
 			break;
 		}
@@ -287,6 +409,110 @@
 
 #ifdef HOSTAPD
 
+#ifdef CONFIG_IEEE80211R
+static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
+					  const u8 *bssid,
+					  u16 auth_transaction, u16 status,
+					  const u8 *ies, size_t ies_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, dst);
+	if (sta == NULL)
+		return;
+
+	hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+	sta->flags |= WLAN_STA_AUTH;
+
+	hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+static void hostapd_notif_auth(struct hostapd_data *hapd,
+			       struct auth_info *rx_auth)
+{
+	struct sta_info *sta;
+	u16 status = WLAN_STATUS_SUCCESS;
+	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+	size_t resp_ies_len = 0;
+
+	sta = ap_get_sta(hapd, rx_auth->peer);
+	if (!sta) {
+		sta = ap_sta_add(hapd, rx_auth->peer);
+		if (sta == NULL) {
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+	}
+	sta->flags &= ~WLAN_STA_PREAUTH;
+	ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+#ifdef CONFIG_IEEE80211R
+	if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
+		sta->auth_alg = WLAN_AUTH_FT;
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
+				   "state machine");
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid,
+				    rx_auth->auth_transaction, rx_auth->ies,
+				    rx_auth->ies_len,
+				    hostapd_notify_auth_ft_finish, hapd);
+		return;
+	}
+#endif /* CONFIG_IEEE80211R */
+fail:
+	hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
+			 status, resp_ies, resp_ies_len);
+}
+
+
+static void hostapd_action_rx(struct hostapd_data *hapd,
+			      struct rx_action *action)
+{
+	struct sta_info *sta;
+
+        wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
+		   action->category, (int) action->len);
+
+	sta = ap_get_sta(hapd, action->sa);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+		return;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (action->category == WLAN_ACTION_FT) {
+		wpa_printf(MSG_DEBUG, "%s: FT_ACTION length %d",
+			   __func__, (int) action->len);
+		wpa_ft_action_rx(sta->wpa_sm, action->data, action->len);
+	}
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	if (action->category == WLAN_ACTION_SA_QUERY && action->len >= 4) {
+		wpa_printf(MSG_DEBUG, "%s: SA_QUERY_ACTION length %d",
+			   __func__, (int) action->len);
+		ieee802_11_sa_query_action(hapd, action->sa,
+					   *(action->data + 1),
+					   action->data + 2);
+	}
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211V
+	if (action->category == WLAN_ACTION_WNM) {
+		wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d",
+			   __func__, (int) action->len);
+		ieee802_11_rx_wnm_action_ap(hapd, action);
+	}
+#endif /* CONFIG_IEEE80211V */
+}
+
+
 #ifdef NEED_AP_MLME
 
 #define HAPD_BROADCAST ((struct hostapd_data *) -1)
@@ -466,7 +692,7 @@
 #ifndef CONFIG_NO_STDOUT_DEBUG
 	int level = MSG_DEBUG;
 
-	if (event == EVENT_RX_MGMT && data && data->rx_mgmt.frame &&
+	if (event == EVENT_RX_MGMT && data->rx_mgmt.frame &&
 	    data->rx_mgmt.frame_len >= 24) {
 		const struct ieee80211_hdr *hdr;
 		u16 fc;
@@ -541,7 +767,8 @@
 				     data->rx_probe_req.da,
 				     data->rx_probe_req.bssid,
 				     data->rx_probe_req.ie,
-				     data->rx_probe_req.ie_len);
+				     data->rx_probe_req.ie_len,
+				     data->rx_probe_req.ssi_signal);
 		break;
 	case EVENT_NEW_STA:
 		hostapd_event_new_sta(hapd, data->new_sta.addr);
@@ -570,14 +797,25 @@
 			break;
 		hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
 		break;
-#ifdef NEED_AP_MLME
 	case EVENT_RX_ACTION:
 		if (data->rx_action.da == NULL || data->rx_action.sa == NULL ||
 		    data->rx_action.bssid == NULL)
 			break;
+#ifdef NEED_AP_MLME
 		hostapd_rx_action(hapd, &data->rx_action);
-		break;
 #endif /* NEED_AP_MLME */
+		hostapd_action_rx(hapd, &data->rx_action);
+		break;
+	case EVENT_AUTH:
+		hostapd_notif_auth(hapd, &data->auth);
+		break;
+	case EVENT_CH_SWITCH:
+		if (!data)
+			break;
+		hostapd_event_ch_switch(hapd, data->ch_switch.freq,
+					data->ch_switch.ht_enabled,
+					data->ch_switch.ch_offset);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
 		break;
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
new file mode 100644
index 0000000..851c183
--- /dev/null
+++ b/src/ap/gas_serv.c
@@ -0,0 +1,1172 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "gas_serv.h"
+
+
+static struct gas_dialog_info *
+gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
+{
+	struct sta_info *sta;
+	struct gas_dialog_info *dia = NULL;
+	int i, j;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		/*
+		 * We need a STA entry to be able to maintain state for
+		 * the GAS query.
+		 */
+		wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
+			   "GAS query");
+		sta = ap_sta_add(hapd, addr);
+		if (!sta) {
+			wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
+				   " for GAS query", MAC2STR(addr));
+			return NULL;
+		}
+		sta->flags |= WLAN_STA_GAS;
+		/*
+		 * The default inactivity is 300 seconds. We don't need
+		 * it to be that long.
+		 */
+		ap_sta_session_timeout(hapd, sta, 5);
+	}
+
+	if (sta->gas_dialog == NULL) {
+		sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
+					    sizeof(struct gas_dialog_info));
+		if (sta->gas_dialog == NULL)
+			return NULL;
+	}
+
+	for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
+		if (i == GAS_DIALOG_MAX)
+			i = 0;
+		if (sta->gas_dialog[i].valid)
+			continue;
+		dia = &sta->gas_dialog[i];
+		dia->valid = 1;
+		dia->index = i;
+		dia->dialog_token = dialog_token;
+		sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
+		return dia;
+	}
+
+	wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
+		MACSTR " dialog_token %u. Consider increasing "
+		"GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
+
+	return NULL;
+}
+
+
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+		     u8 dialog_token)
+{
+	struct sta_info *sta;
+	int i;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
+			   MAC2STR(addr));
+		return NULL;
+	}
+	for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
+		if (sta->gas_dialog[i].dialog_token != dialog_token ||
+		    !sta->gas_dialog[i].valid)
+			continue;
+		return &sta->gas_dialog[i];
+	}
+	wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
+		   MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
+	return NULL;
+}
+
+
+void gas_serv_dialog_clear(struct gas_dialog_info *dia)
+{
+	wpabuf_free(dia->sd_resp);
+	os_memset(dia, 0, sizeof(*dia));
+}
+
+
+static void gas_serv_free_dialogs(struct hostapd_data *hapd,
+				  const u8 *sta_addr)
+{
+	struct sta_info *sta;
+	int i;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (sta == NULL || sta->gas_dialog == NULL)
+		return;
+
+	for (i = 0; i < GAS_DIALOG_MAX; i++) {
+		if (sta->gas_dialog[i].valid)
+			return;
+	}
+
+	os_free(sta->gas_dialog);
+	sta->gas_dialog = NULL;
+}
+
+
+#ifdef CONFIG_HS20
+static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
+				   struct wpabuf *buf)
+{
+	u8 *len;
+
+	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+	wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
+	wpabuf_put_u8(buf, 0); /* Reserved */
+	wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
+	if (hapd->conf->hs20_oper_friendly_name)
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+	if (hapd->conf->hs20_wan_metrics)
+		wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
+	if (hapd->conf->hs20_connection_capability)
+		wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
+	if (hapd->conf->nai_realm_data)
+		wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
+	if (hapd->conf->hs20_operating_class)
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+	gas_anqp_set_element_len(buf, len);
+}
+#endif /* CONFIG_HS20 */
+
+
+static void anqp_add_capab_list(struct hostapd_data *hapd,
+				struct wpabuf *buf)
+{
+	u8 *len;
+
+	len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
+	wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
+	if (hapd->conf->venue_name)
+		wpabuf_put_le16(buf, ANQP_VENUE_NAME);
+	if (hapd->conf->network_auth_type)
+		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+	if (hapd->conf->roaming_consortium)
+		wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
+	if (hapd->conf->ipaddr_type_configured)
+		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+	if (hapd->conf->nai_realm_data)
+		wpabuf_put_le16(buf, ANQP_NAI_REALM);
+	if (hapd->conf->anqp_3gpp_cell_net)
+		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+	if (hapd->conf->domain_name)
+		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+#ifdef CONFIG_HS20
+	anqp_add_hs_capab_list(hapd, buf);
+#endif /* CONFIG_HS20 */
+	gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+	if (hapd->conf->venue_name) {
+		u8 *len;
+		unsigned int i;
+		len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
+		wpabuf_put_u8(buf, hapd->conf->venue_group);
+		wpabuf_put_u8(buf, hapd->conf->venue_type);
+		for (i = 0; i < hapd->conf->venue_name_count; i++) {
+			struct hostapd_lang_string *vn;
+			vn = &hapd->conf->venue_name[i];
+			wpabuf_put_u8(buf, 3 + vn->name_len);
+			wpabuf_put_data(buf, vn->lang, 3);
+			wpabuf_put_data(buf, vn->name, vn->name_len);
+		}
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_network_auth_type(struct hostapd_data *hapd,
+				       struct wpabuf *buf)
+{
+	if (hapd->conf->network_auth_type) {
+		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+		wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
+		wpabuf_put_data(buf, hapd->conf->network_auth_type,
+				hapd->conf->network_auth_type_len);
+	}
+}
+
+
+static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
+					struct wpabuf *buf)
+{
+	unsigned int i;
+	u8 *len;
+
+	len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
+	for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
+		struct hostapd_roaming_consortium *rc;
+		rc = &hapd->conf->roaming_consortium[i];
+		wpabuf_put_u8(buf, rc->len);
+		wpabuf_put_data(buf, rc->oi, rc->len);
+	}
+	gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
+					       struct wpabuf *buf)
+{
+	if (hapd->conf->ipaddr_type_configured) {
+		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+		wpabuf_put_le16(buf, 1);
+		wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
+	}
+}
+
+
+static void anqp_add_nai_realm_eap(struct wpabuf *buf,
+				   struct hostapd_nai_realm_data *realm)
+{
+	unsigned int i, j;
+
+	wpabuf_put_u8(buf, realm->eap_method_count);
+
+	for (i = 0; i < realm->eap_method_count; i++) {
+		struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
+		wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
+		wpabuf_put_u8(buf, eap->eap_method);
+		wpabuf_put_u8(buf, eap->num_auths);
+		for (j = 0; j < eap->num_auths; j++) {
+			wpabuf_put_u8(buf, eap->auth_id[j]);
+			wpabuf_put_u8(buf, 1);
+			wpabuf_put_u8(buf, eap->auth_val[j]);
+		}
+	}
+}
+
+
+static void anqp_add_nai_realm_data(struct wpabuf *buf,
+				    struct hostapd_nai_realm_data *realm,
+				    unsigned int realm_idx)
+{
+	u8 *realm_data_len;
+
+	wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
+		   (int) os_strlen(realm->realm[realm_idx]));
+	realm_data_len = wpabuf_put(buf, 2);
+	wpabuf_put_u8(buf, realm->encoding);
+	wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
+	wpabuf_put_str(buf, realm->realm[realm_idx]);
+	anqp_add_nai_realm_eap(buf, realm);
+	gas_anqp_set_element_len(buf, realm_data_len);
+}
+
+
+static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
+					   struct wpabuf *buf,
+					   const u8 *home_realm,
+					   size_t home_realm_len)
+{
+	unsigned int i, j, k;
+	u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
+	struct hostapd_nai_realm_data *realm;
+	const u8 *pos, *realm_name, *end;
+	struct {
+		unsigned int realm_data_idx;
+		unsigned int realm_idx;
+	} matches[10];
+
+	pos = home_realm;
+	end = pos + home_realm_len;
+	if (pos + 1 > end) {
+		wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
+			    home_realm, home_realm_len);
+		return -1;
+	}
+	num_realms = *pos++;
+
+	for (i = 0; i < num_realms && num_matching < 10; i++) {
+		if (pos + 2 > end) {
+			wpa_hexdump(MSG_DEBUG,
+				    "Truncated NAI Home Realm Query",
+				    home_realm, home_realm_len);
+			return -1;
+		}
+		encoding = *pos++;
+		realm_len = *pos++;
+		if (pos + realm_len > end) {
+			wpa_hexdump(MSG_DEBUG,
+				    "Truncated NAI Home Realm Query",
+				    home_realm, home_realm_len);
+			return -1;
+		}
+		realm_name = pos;
+		for (j = 0; j < hapd->conf->nai_realm_count &&
+			     num_matching < 10; j++) {
+			const u8 *rpos, *rend;
+			realm = &hapd->conf->nai_realm_data[j];
+			if (encoding != realm->encoding)
+				continue;
+
+			rpos = realm_name;
+			while (rpos < realm_name + realm_len &&
+			       num_matching < 10) {
+				for (rend = rpos;
+				     rend < realm_name + realm_len; rend++) {
+					if (*rend == ';')
+						break;
+				}
+				for (k = 0; k < MAX_NAI_REALMS &&
+					     realm->realm[k] &&
+					     num_matching < 10; k++) {
+					if ((int) os_strlen(realm->realm[k]) !=
+					    rend - rpos ||
+					    os_strncmp((char *) rpos,
+						       realm->realm[k],
+						       rend - rpos) != 0)
+						continue;
+					matches[num_matching].realm_data_idx =
+						j;
+					matches[num_matching].realm_idx = k;
+					num_matching++;
+				}
+				rpos = rend + 1;
+			}
+		}
+		pos += realm_len;
+	}
+
+	realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
+	wpabuf_put_le16(buf, num_matching);
+
+	/*
+	 * There are two ways to format. 1. each realm in a NAI Realm Data unit
+	 * 2. all realms that share the same EAP methods in a NAI Realm Data
+	 * unit. The first format is likely to be bigger in size than the
+	 * second, but may be easier to parse and process by the receiver.
+	 */
+	for (i = 0; i < num_matching; i++) {
+		wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
+			   matches[i].realm_data_idx, matches[i].realm_idx);
+		realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
+		anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
+	}
+	gas_anqp_set_element_len(buf, realm_list_len);
+	return 0;
+}
+
+
+static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
+			       const u8 *home_realm, size_t home_realm_len,
+			       int nai_realm, int nai_home_realm)
+{
+	if (nai_realm && hapd->conf->nai_realm_data) {
+		u8 *len;
+		unsigned int i, j;
+		len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
+		wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
+		for (i = 0; i < hapd->conf->nai_realm_count; i++) {
+			u8 *realm_data_len, *realm_len;
+			struct hostapd_nai_realm_data *realm;
+
+			realm = &hapd->conf->nai_realm_data[i];
+			realm_data_len = wpabuf_put(buf, 2);
+			wpabuf_put_u8(buf, realm->encoding);
+			realm_len = wpabuf_put(buf, 1);
+			for (j = 0; realm->realm[j]; j++) {
+				if (j > 0)
+					wpabuf_put_u8(buf, ';');
+				wpabuf_put_str(buf, realm->realm[j]);
+			}
+			*realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
+			anqp_add_nai_realm_eap(buf, realm);
+			gas_anqp_set_element_len(buf, realm_data_len);
+		}
+		gas_anqp_set_element_len(buf, len);
+	} else if (nai_home_realm && hapd->conf->nai_realm_data) {
+		hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
+						home_realm_len);
+	}
+}
+
+
+static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
+					   struct wpabuf *buf)
+{
+	if (hapd->conf->anqp_3gpp_cell_net) {
+		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+		wpabuf_put_le16(buf,
+				hapd->conf->anqp_3gpp_cell_net_len);
+		wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
+				hapd->conf->anqp_3gpp_cell_net_len);
+	}
+}
+
+
+static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+	if (hapd->conf->domain_name) {
+		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+		wpabuf_put_le16(buf, hapd->conf->domain_name_len);
+		wpabuf_put_data(buf, hapd->conf->domain_name,
+				hapd->conf->domain_name_len);
+	}
+}
+
+
+#ifdef CONFIG_HS20
+
+static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
+					    struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_oper_friendly_name) {
+		u8 *len;
+		unsigned int i;
+		len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
+		{
+			struct hostapd_lang_string *vn;
+			vn = &hapd->conf->hs20_oper_friendly_name[i];
+			wpabuf_put_u8(buf, 3 + vn->name_len);
+			wpabuf_put_data(buf, vn->lang, 3);
+			wpabuf_put_data(buf, vn->name, vn->name_len);
+		}
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_wan_metrics(struct hostapd_data *hapd,
+				 struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_wan_metrics) {
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_connection_capability(struct hostapd_data *hapd,
+					   struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_connection_capability) {
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
+				hapd->conf->hs20_connection_capability_len);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_operating_class(struct hostapd_data *hapd,
+				     struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_operating_class) {
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
+				hapd->conf->hs20_operating_class_len);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static struct wpabuf *
+gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
+				unsigned int request,
+				struct gas_dialog_info *di,
+				const u8 *home_realm, size_t home_realm_len)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(1400);
+	if (buf == NULL)
+		return NULL;
+
+	if (request & ANQP_REQ_CAPABILITY_LIST)
+		anqp_add_capab_list(hapd, buf);
+	if (request & ANQP_REQ_VENUE_NAME)
+		anqp_add_venue_name(hapd, buf);
+	if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
+		anqp_add_network_auth_type(hapd, buf);
+	if (request & ANQP_REQ_ROAMING_CONSORTIUM)
+		anqp_add_roaming_consortium(hapd, buf);
+	if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
+		anqp_add_ip_addr_type_availability(hapd, buf);
+	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+		anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
+				   request & ANQP_REQ_NAI_REALM,
+				   request & ANQP_REQ_NAI_HOME_REALM);
+	if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
+		anqp_add_3gpp_cellular_network(hapd, buf);
+	if (request & ANQP_REQ_DOMAIN_NAME)
+		anqp_add_domain_name(hapd, buf);
+
+#ifdef CONFIG_HS20
+	if (request & ANQP_REQ_HS_CAPABILITY_LIST)
+		anqp_add_hs_capab_list(hapd, buf);
+	if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
+		anqp_add_operator_friendly_name(hapd, buf);
+	if (request & ANQP_REQ_WAN_METRICS)
+		anqp_add_wan_metrics(hapd, buf);
+	if (request & ANQP_REQ_CONNECTION_CAPABILITY)
+		anqp_add_connection_capability(hapd, buf);
+	if (request & ANQP_REQ_OPERATING_CLASS)
+		anqp_add_operating_class(hapd, buf);
+#endif /* CONFIG_HS20 */
+
+	return buf;
+}
+
+
+static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
+{
+	struct gas_dialog_info *dia = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
+		   "dialog token %d", dia->dialog_token);
+
+	gas_serv_dialog_clear(dia);
+}
+
+
+struct anqp_query_info {
+	unsigned int request;
+	unsigned int remote_request;
+	const u8 *home_realm_query;
+	size_t home_realm_query_len;
+	u16 remote_delay;
+};
+
+
+static void set_anqp_req(unsigned int bit, const char *name, int local,
+			 unsigned int remote, u16 remote_delay,
+			 struct anqp_query_info *qi)
+{
+	qi->request |= bit;
+	if (local) {
+		wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
+	} else if (bit & remote) {
+		wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
+		qi->remote_request |= bit;
+		if (remote_delay > qi->remote_delay)
+			qi->remote_delay = remote_delay;
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
+	}
+}
+
+
+static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
+				  struct anqp_query_info *qi)
+{
+	switch (info_id) {
+	case ANQP_CAPABILITY_LIST:
+		set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
+			     0, qi);
+		break;
+	case ANQP_VENUE_NAME:
+		set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
+			     hapd->conf->venue_name != NULL, 0, 0, qi);
+		break;
+	case ANQP_NETWORK_AUTH_TYPE:
+		set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
+			     hapd->conf->network_auth_type != NULL,
+			     0, 0, qi);
+		break;
+	case ANQP_ROAMING_CONSORTIUM:
+		set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
+			     hapd->conf->roaming_consortium != NULL, 0, 0, qi);
+		break;
+	case ANQP_IP_ADDR_TYPE_AVAILABILITY:
+		set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
+			     "IP Addr Type Availability",
+			     hapd->conf->ipaddr_type_configured,
+			     0, 0, qi);
+		break;
+	case ANQP_NAI_REALM:
+		set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
+			     hapd->conf->nai_realm_data != NULL,
+			     0, 0, qi);
+		break;
+	case ANQP_3GPP_CELLULAR_NETWORK:
+		set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
+			     "3GPP Cellular Network",
+			     hapd->conf->anqp_3gpp_cell_net != NULL,
+			     0, 0, qi);
+		break;
+	case ANQP_DOMAIN_NAME:
+		set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
+			     hapd->conf->domain_name != NULL,
+			     0, 0, qi);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
+			   info_id);
+		break;
+	}
+}
+
+
+static void rx_anqp_query_list(struct hostapd_data *hapd,
+			       const u8 *pos, const u8 *end,
+			       struct anqp_query_info *qi)
+{
+	wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
+		   (unsigned int) (end - pos) / 2);
+
+	while (pos + 2 <= end) {
+		rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
+		pos += 2;
+	}
+}
+
+
+#ifdef CONFIG_HS20
+
+static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
+				  struct anqp_query_info *qi)
+{
+	switch (subtype) {
+	case HS20_STYPE_CAPABILITY_LIST:
+		set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
+			     1, 0, 0, qi);
+		break;
+	case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
+		set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
+			     "Operator Friendly Name",
+			     hapd->conf->hs20_oper_friendly_name != NULL,
+			     0, 0, qi);
+		break;
+	case HS20_STYPE_WAN_METRICS:
+		set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
+			     hapd->conf->hs20_wan_metrics != NULL,
+			     0, 0, qi);
+		break;
+	case HS20_STYPE_CONNECTION_CAPABILITY:
+		set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
+			     "Connection Capability",
+			     hapd->conf->hs20_connection_capability != NULL,
+			     0, 0, qi);
+		break;
+	case HS20_STYPE_OPERATING_CLASS:
+		set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
+			     hapd->conf->hs20_operating_class != NULL,
+			     0, 0, qi);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
+			   subtype);
+		break;
+	}
+}
+
+
+static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
+				      const u8 *pos, const u8 *end,
+				      struct anqp_query_info *qi)
+{
+	qi->request |= ANQP_REQ_NAI_HOME_REALM;
+	qi->home_realm_query = pos;
+	qi->home_realm_query_len = end - pos;
+	if (hapd->conf->nai_realm_data != NULL) {
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
+			   "(local)");
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
+			   "available");
+	}
+}
+
+
+static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
+				    const u8 *pos, const u8 *end,
+				    struct anqp_query_info *qi)
+{
+	u32 oui;
+	u8 subtype;
+
+	if (pos + 4 > end) {
+		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
+			   "Query element");
+		return;
+	}
+
+	oui = WPA_GET_BE24(pos);
+	pos += 3;
+	if (oui != OUI_WFA) {
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
+			   oui);
+		return;
+	}
+
+	if (*pos != HS20_ANQP_OUI_TYPE) {
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
+			   *pos);
+		return;
+	}
+	pos++;
+
+	if (pos + 1 >= end)
+		return;
+
+	subtype = *pos++;
+	pos++; /* Reserved */
+	switch (subtype) {
+	case HS20_STYPE_QUERY_LIST:
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
+		while (pos < end) {
+			rx_anqp_hs_query_list(hapd, *pos, qi);
+			pos++;
+		}
+		break;
+	case HS20_STYPE_NAI_HOME_REALM_QUERY:
+		rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
+			   "%u", subtype);
+		break;
+	}
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static void gas_serv_req_local_processing(struct hostapd_data *hapd,
+					  const u8 *sa, u8 dialog_token,
+					  struct anqp_query_info *qi)
+{
+	struct wpabuf *buf, *tx_buf;
+
+	buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
+					      qi->home_realm_query,
+					      qi->home_realm_query_len);
+	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
+			buf);
+	if (!buf)
+		return;
+
+	if (wpabuf_len(buf) > hapd->gas_frag_limit ||
+	    hapd->conf->gas_comeback_delay) {
+		struct gas_dialog_info *di;
+		u16 comeback_delay = 1;
+
+		if (hapd->conf->gas_comeback_delay) {
+			/* Testing - allow overriding of the delay value */
+			comeback_delay = hapd->conf->gas_comeback_delay;
+		}
+
+		wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
+			   "initial response - use GAS comeback");
+		di = gas_dialog_create(hapd, sa, dialog_token);
+		if (!di) {
+			wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
+				   "for " MACSTR " (dialog token %u)",
+				   MAC2STR(sa), dialog_token);
+			wpabuf_free(buf);
+			return;
+		}
+		di->sd_resp = buf;
+		di->sd_resp_pos = 0;
+		tx_buf = gas_anqp_build_initial_resp_buf(
+			dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
+			NULL);
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
+		tx_buf = gas_anqp_build_initial_resp_buf(
+			dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
+		wpabuf_free(buf);
+	}
+	if (!tx_buf)
+		return;
+
+	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+	wpabuf_free(tx_buf);
+}
+
+
+static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
+					const u8 *sa,
+					const u8 *data, size_t len)
+{
+	const u8 *pos = data;
+	const u8 *end = data + len;
+	const u8 *next;
+	u8 dialog_token;
+	u16 slen;
+	struct anqp_query_info qi;
+	const u8 *adv_proto;
+
+	if (len < 1 + 2)
+		return;
+
+	os_memset(&qi, 0, sizeof(qi));
+
+	dialog_token = *pos++;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+		"GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
+		MAC2STR(sa), dialog_token);
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Unexpected IE in GAS Initial Request: %u", *pos);
+		return;
+	}
+	adv_proto = pos++;
+
+	slen = *pos++;
+	next = pos + slen;
+	if (next > end || slen < 2) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Invalid IE in GAS Initial Request");
+		return;
+	}
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+		struct wpabuf *buf;
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Unsupported GAS advertisement protocol id %u",
+			*pos);
+		if (sa[0] & 0x01)
+			return; /* Invalid source address - drop silently */
+		buf = gas_build_initial_resp(
+			dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
+			0, 2 + slen + 2);
+		if (buf == NULL)
+			return;
+		wpabuf_put_data(buf, adv_proto, 2 + slen);
+		wpabuf_put_le16(buf, 0); /* Query Response Length */
+		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+					wpabuf_head(buf), wpabuf_len(buf));
+		wpabuf_free(buf);
+		return;
+	}
+
+	pos = next;
+	/* Query Request */
+	if (pos + 2 > end)
+		return;
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + slen > end)
+		return;
+	end = pos + slen;
+
+	/* ANQP Query Request */
+	while (pos < end) {
+		u16 info_id, elen;
+
+		if (pos + 4 > end)
+			return;
+
+		info_id = WPA_GET_LE16(pos);
+		pos += 2;
+		elen = WPA_GET_LE16(pos);
+		pos += 2;
+
+		if (pos + elen > end) {
+			wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
+			return;
+		}
+
+		switch (info_id) {
+		case ANQP_QUERY_LIST:
+			rx_anqp_query_list(hapd, pos, pos + elen, &qi);
+			break;
+#ifdef CONFIG_HS20
+		case ANQP_VENDOR_SPECIFIC:
+			rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
+			break;
+#endif /* CONFIG_HS20 */
+		default:
+			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
+				   "Request element %u", info_id);
+			break;
+		}
+
+		pos += elen;
+	}
+
+	gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
+}
+
+
+void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
+			      struct gas_dialog_info *dialog)
+{
+	struct wpabuf *buf, *tx_buf;
+	u8 dialog_token = dialog->dialog_token;
+	size_t frag_len;
+
+	if (dialog->sd_resp == NULL) {
+		buf = gas_serv_build_gas_resp_payload(hapd,
+						      dialog->all_requested,
+						      dialog, NULL, 0);
+		wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
+			buf);
+		if (!buf)
+			goto tx_gas_response_done;
+		dialog->sd_resp = buf;
+		dialog->sd_resp_pos = 0;
+	}
+	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
+	if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
+	    hapd->conf->gas_comeback_delay) {
+		u16 comeback_delay_tus = dialog->comeback_delay +
+			GAS_SERV_COMEBACK_DELAY_FUDGE;
+		u32 comeback_delay_secs, comeback_delay_usecs;
+
+		if (hapd->conf->gas_comeback_delay) {
+			/* Testing - allow overriding of the delay value */
+			comeback_delay_tus = hapd->conf->gas_comeback_delay;
+		}
+
+		wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
+			   "%u) and comeback delay %u, "
+			   "requesting comebacks", (unsigned int) frag_len,
+			   (unsigned int) hapd->gas_frag_limit,
+			   dialog->comeback_delay);
+		tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
+							 WLAN_STATUS_SUCCESS,
+							 comeback_delay_tus,
+							 NULL);
+		if (tx_buf) {
+			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+				"GAS: Tx GAS Initial Resp (comeback = 10TU)");
+			hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+						dst,
+						wpabuf_head(tx_buf),
+						wpabuf_len(tx_buf));
+		}
+		wpabuf_free(tx_buf);
+
+		/* start a timer of 1.5 * comeback-delay */
+		comeback_delay_tus = comeback_delay_tus +
+			(comeback_delay_tus / 2);
+		comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
+		comeback_delay_usecs = (comeback_delay_tus * 1024) -
+			(comeback_delay_secs * 1000000);
+		eloop_register_timeout(comeback_delay_secs,
+				       comeback_delay_usecs,
+				       gas_serv_clear_cached_ies, dialog,
+				       NULL);
+		goto tx_gas_response_done;
+	}
+
+	buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
+				dialog->sd_resp_pos, frag_len);
+	if (buf == NULL) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
+			"failed");
+		goto tx_gas_response_done;
+	}
+	tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
+						 WLAN_STATUS_SUCCESS, 0, buf);
+	wpabuf_free(buf);
+	if (tx_buf == NULL)
+		goto tx_gas_response_done;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
+		"Response (frag_id %d frag_len %d)",
+		dialog->sd_frag_id, (int) frag_len);
+	dialog->sd_frag_id++;
+
+	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
+				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+	wpabuf_free(tx_buf);
+tx_gas_response_done:
+	gas_serv_clear_cached_ies(dialog, NULL);
+}
+
+
+static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
+					 const u8 *sa,
+					 const u8 *data, size_t len)
+{
+	struct gas_dialog_info *dialog;
+	struct wpabuf *buf, *tx_buf;
+	u8 dialog_token;
+	size_t frag_len;
+	int more = 0;
+
+	wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
+	if (len < 1)
+		return;
+	dialog_token = *data;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
+		dialog_token);
+
+	dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
+	if (!dialog) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
+			"response fragment for " MACSTR " dialog token %u",
+			MAC2STR(sa), dialog_token);
+
+		if (sa[0] & 0x01)
+			return; /* Invalid source address - drop silently */
+		tx_buf = gas_anqp_build_comeback_resp_buf(
+			dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
+			0, NULL);
+		if (tx_buf == NULL)
+			return;
+		goto send_resp;
+	}
+
+	if (dialog->sd_resp == NULL) {
+		wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
+			   dialog->requested, dialog->received);
+		if ((dialog->requested & dialog->received) !=
+		    dialog->requested) {
+			wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
+				   "from remote processing");
+			gas_serv_dialog_clear(dialog);
+			tx_buf = gas_anqp_build_comeback_resp_buf(
+				dialog_token,
+				WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
+				NULL);
+			if (tx_buf == NULL)
+				return;
+			goto send_resp;
+		}
+
+		buf = gas_serv_build_gas_resp_payload(hapd,
+						      dialog->all_requested,
+						      dialog, NULL, 0);
+		wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
+			buf);
+		if (!buf)
+			goto rx_gas_comeback_req_done;
+		dialog->sd_resp = buf;
+		dialog->sd_resp_pos = 0;
+	}
+	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
+	if (frag_len > hapd->gas_frag_limit) {
+		frag_len = hapd->gas_frag_limit;
+		more = 1;
+	}
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
+		(unsigned int) frag_len);
+	buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
+				dialog->sd_resp_pos, frag_len);
+	if (buf == NULL) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
+			"buffer");
+		goto rx_gas_comeback_req_done;
+	}
+	tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
+						  WLAN_STATUS_SUCCESS,
+						  dialog->sd_frag_id,
+						  more, 0, buf);
+	wpabuf_free(buf);
+	if (tx_buf == NULL)
+		goto rx_gas_comeback_req_done;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
+		"(frag_id %d more=%d frag_len=%d)",
+		dialog->sd_frag_id, more, (int) frag_len);
+	dialog->sd_frag_id++;
+	dialog->sd_resp_pos += frag_len;
+
+	if (more) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
+			"to be sent",
+			(int) (wpabuf_len(dialog->sd_resp) -
+			       dialog->sd_resp_pos));
+	} else {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
+			"SD response sent");
+		gas_serv_dialog_clear(dialog);
+		gas_serv_free_dialogs(hapd, sa);
+	}
+
+send_resp:
+	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+	wpabuf_free(tx_buf);
+	return;
+
+rx_gas_comeback_req_done:
+	gas_serv_clear_cached_ies(dialog, NULL);
+}
+
+
+static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
+				      int freq)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct ieee80211_mgmt *mgmt;
+	size_t hdr_len;
+	const u8 *sa, *data;
+
+	mgmt = (const struct ieee80211_mgmt *) buf;
+	hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
+	if (hdr_len > len)
+		return;
+	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+		return;
+	sa = mgmt->sa;
+	len -= hdr_len;
+	data = &mgmt->u.action.u.public_action.action;
+	switch (data[0]) {
+	case WLAN_PA_GAS_INITIAL_REQ:
+		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
+		break;
+	case WLAN_PA_GAS_COMEBACK_REQ:
+		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1);
+		break;
+	}
+}
+
+
+int gas_serv_init(struct hostapd_data *hapd)
+{
+	hapd->public_action_cb = gas_serv_rx_public_action;
+	hapd->public_action_cb_ctx = hapd;
+	hapd->gas_frag_limit = 1400;
+	if (hapd->conf->gas_frag_limit > 0)
+		hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
+	return 0;
+}
+
+
+void gas_serv_deinit(struct hostapd_data *hapd)
+{
+}
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
new file mode 100644
index 0000000..4213cf6
--- /dev/null
+++ b/src/ap/gas_serv.h
@@ -0,0 +1,71 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_SERV_H
+#define GAS_SERV_H
+
+#define ANQP_REQ_CAPABILITY_LIST \
+	(1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
+#define ANQP_REQ_VENUE_NAME \
+	(1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_NETWORK_AUTH_TYPE \
+	(1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
+#define ANQP_REQ_ROAMING_CONSORTIUM \
+	(1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST))
+#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \
+	(1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST))
+#define ANQP_REQ_NAI_REALM \
+	(1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
+#define ANQP_REQ_3GPP_CELLULAR_NETWORK \
+	(1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
+#define ANQP_REQ_DOMAIN_NAME \
+	(1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_HS_CAPABILITY_LIST \
+	(0x10000 << HS20_STYPE_CAPABILITY_LIST)
+#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
+	(0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME)
+#define ANQP_REQ_WAN_METRICS \
+	(0x10000 << HS20_STYPE_WAN_METRICS)
+#define ANQP_REQ_CONNECTION_CAPABILITY \
+	(0x10000 << HS20_STYPE_CONNECTION_CAPABILITY)
+#define ANQP_REQ_NAI_HOME_REALM \
+	(0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
+#define ANQP_REQ_OPERATING_CLASS \
+	(0x10000 << HS20_STYPE_OPERATING_CLASS)
+
+/* To account for latencies between hostapd and external ANQP processor */
+#define GAS_SERV_COMEBACK_DELAY_FUDGE 10
+#define GAS_SERV_MIN_COMEBACK_DELAY 100 /* in TU */
+
+struct gas_dialog_info {
+	u8 valid;
+	u8 index;
+	struct wpabuf *sd_resp; /* Fragmented response */
+	u8 dialog_token;
+	size_t sd_resp_pos; /* Offset in sd_resp */
+	u8 sd_frag_id;
+	u16 comeback_delay;
+
+	unsigned int requested;
+	unsigned int received;
+	unsigned int all_requested;
+};
+
+struct hostapd_data;
+
+void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
+			      struct gas_dialog_info *dialog);
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+		     u8 dialog_token);
+void gas_serv_dialog_clear(struct gas_dialog_info *dialog);
+
+int gas_serv_init(struct hostapd_data *hapd);
+void gas_serv_deinit(struct hostapd_data *hapd);
+
+#endif /* GAS_SERV_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index c5cbdf7..3429258 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Initialization and configuration
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,6 +12,7 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "radius/radius_client.h"
+#include "radius/radius_das.h"
 #include "drivers/driver.h"
 #include "hostapd.h"
 #include "authsrv.h"
@@ -30,13 +31,32 @@
 #include "ap_drv_ops.h"
 #include "ap_config.h"
 #include "p2p_hostapd.h"
+#include "gas_serv.h"
 
 
-static int hostapd_flush_old_stations(struct hostapd_data *hapd);
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
 static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
 static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
 
 extern int wpa_debug_level;
+extern struct wpa_driver_ops *wpa_drivers[];
+
+
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+			       int (*cb)(struct hostapd_iface *iface,
+					 void *ctx), void *ctx)
+{
+	size_t i;
+	int ret;
+
+	for (i = 0; i < interfaces->count; i++) {
+		ret = cb(interfaces->iface[i], ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
 
 
 static void hostapd_reload_bss(struct hostapd_data *hapd)
@@ -79,7 +99,7 @@
 	hostapd_update_wps(hapd);
 
 	if (hapd->conf->ssid.ssid_set &&
-	    hostapd_set_ssid(hapd, (u8 *) hapd->conf->ssid.ssid,
+	    hostapd_set_ssid(hapd, hapd->conf->ssid.ssid,
 			     hapd->conf->ssid.ssid_len)) {
 		wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
 		/* try to continue */
@@ -94,9 +114,10 @@
 	struct hostapd_config *newconf, *oldconf;
 	size_t j;
 
-	if (iface->config_read_cb == NULL)
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->config_read_cb == NULL)
 		return -1;
-	newconf = iface->config_read_cb(iface->config_fname);
+	newconf = iface->interfaces->config_read_cb(iface->config_fname);
 	if (newconf == NULL)
 		return -1;
 
@@ -105,7 +126,8 @@
 	 * allow them to use the BSS anymore.
 	 */
 	for (j = 0; j < iface->num_bss; j++) {
-		hostapd_flush_old_stations(iface->bss[j]);
+		hostapd_flush_old_stations(iface->bss[j],
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 		hostapd_broadcast_wep_clear(iface->bss[j]);
 
 #ifndef CONFIG_NO_RADIUS
@@ -210,21 +232,9 @@
 	return errors;
 }
 
-/**
- * hostapd_cleanup - Per-BSS cleanup (deinitialization)
- * @hapd: Pointer to BSS data
- *
- * This function is used to free all per-BSS data structures and resources.
- * This gets called in a loop for each BSS between calls to
- * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
- * is deinitialized. Most of the modules that are initialized in
- * hostapd_setup_bss() are deinitialized here.
- */
-static void hostapd_cleanup(struct hostapd_data *hapd)
-{
-	if (hapd->iface->ctrl_iface_deinit)
-		hapd->iface->ctrl_iface_deinit(hapd);
 
+static void hostapd_free_hapd_data(struct hostapd_data *hapd)
+{
 	iapp_deinit(hapd->iapp);
 	hapd->iapp = NULL;
 	accounting_deinit(hapd);
@@ -234,6 +244,8 @@
 #ifndef CONFIG_NO_RADIUS
 	radius_client_deinit(hapd->radius);
 	hapd->radius = NULL;
+	radius_das_deinit(hapd->radius_das);
+	hapd->radius_das = NULL;
 #endif /* CONFIG_NO_RADIUS */
 
 	hostapd_deinit_wps(hapd);
@@ -257,6 +269,29 @@
 #endif /* CONFIG_P2P */
 
 	wpabuf_free(hapd->time_adv);
+
+#ifdef CONFIG_INTERWORKING
+	gas_serv_deinit(hapd);
+#endif /* CONFIG_INTERWORKING */
+}
+
+
+/**
+ * hostapd_cleanup - Per-BSS cleanup (deinitialization)
+ * @hapd: Pointer to BSS data
+ *
+ * This function is used to free all per-BSS data structures and resources.
+ * This gets called in a loop for each BSS between calls to
+ * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
+ * is deinitialized. Most of the modules that are initialized in
+ * hostapd_setup_bss() are deinitialized here.
+ */
+static void hostapd_cleanup(struct hostapd_data *hapd)
+{
+	if (hapd->iface->interfaces &&
+	    hapd->iface->interfaces->ctrl_iface_deinit)
+		hapd->iface->interfaces->ctrl_iface_deinit(hapd);
+	hostapd_free_hapd_data(hapd);
 }
 
 
@@ -272,6 +307,18 @@
 }
 
 
+static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+{
+	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+	iface->hw_features = NULL;
+	os_free(iface->current_rates);
+	iface->current_rates = NULL;
+	os_free(iface->basic_rates);
+	iface->basic_rates = NULL;
+	ap_list_deinit(iface);
+}
+
+
 /**
  * hostapd_cleanup_iface - Complete per-interface cleanup
  * @iface: Pointer to interface data
@@ -281,13 +328,7 @@
  */
 static void hostapd_cleanup_iface(struct hostapd_iface *iface)
 {
-	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
-	iface->hw_features = NULL;
-	os_free(iface->current_rates);
-	iface->current_rates = NULL;
-	os_free(iface->basic_rates);
-	iface->basic_rates = NULL;
-	ap_list_deinit(iface);
+	hostapd_cleanup_iface_partial(iface);
 	hostapd_config_free(iface->conf);
 	iface->conf = NULL;
 
@@ -297,6 +338,15 @@
 }
 
 
+static void hostapd_clear_wep(struct hostapd_data *hapd)
+{
+	if (hapd->drv_priv) {
+		hostapd_set_privacy(hapd, 0);
+		hostapd_broadcast_wep_clear(hapd);
+	}
+}
+
+
 static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
 {
 	int i;
@@ -333,7 +383,7 @@
 }
 
 
-static int hostapd_flush_old_stations(struct hostapd_data *hapd)
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
 {
 	int ret = 0;
 	u8 addr[ETH_ALEN];
@@ -349,7 +399,7 @@
 	}
 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations");
 	os_memset(addr, 0xff, ETH_ALEN);
-	hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	hostapd_drv_sta_deauth(hapd, addr, reason);
 	hostapd_free_stas(hapd);
 
 	return ret;
@@ -464,6 +514,86 @@
 }
 
 
+#ifndef CONFIG_NO_RADIUS
+
+static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
+				    struct radius_das_attrs *attr)
+{
+	/* TODO */
+	return 0;
+}
+
+
+static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
+					      struct radius_das_attrs *attr)
+{
+	struct sta_info *sta = NULL;
+	char buf[128];
+
+	if (attr->sta_addr)
+		sta = ap_get_sta(hapd, attr->sta_addr);
+
+	if (sta == NULL && attr->acct_session_id &&
+	    attr->acct_session_id_len == 17) {
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			os_snprintf(buf, sizeof(buf), "%08X-%08X",
+				    sta->acct_session_id_hi,
+				    sta->acct_session_id_lo);
+			if (os_memcmp(attr->acct_session_id, buf, 17) == 0)
+				break;
+		}
+	}
+
+	if (sta == NULL && attr->cui) {
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			struct wpabuf *cui;
+			cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
+			if (cui && wpabuf_len(cui) == attr->cui_len &&
+			    os_memcmp(wpabuf_head(cui), attr->cui,
+				      attr->cui_len) == 0)
+				break;
+		}
+	}
+
+	if (sta == NULL && attr->user_name) {
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			u8 *identity;
+			size_t identity_len;
+			identity = ieee802_1x_get_identity(sta->eapol_sm,
+							   &identity_len);
+			if (identity &&
+			    identity_len == attr->user_name_len &&
+			    os_memcmp(identity, attr->user_name, identity_len)
+			    == 0)
+				break;
+		}
+	}
+
+	return sta;
+}
+
+
+static enum radius_das_res
+hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	if (hostapd_das_nas_mismatch(hapd, attr))
+		return RADIUS_DAS_NAS_MISMATCH;
+
+	sta = hostapd_das_find_sta(hapd, attr);
+	if (sta == NULL)
+		return RADIUS_DAS_SESSION_NOT_FOUND;
+
+	hostapd_drv_sta_deauth(hapd, sta->addr,
+			       WLAN_REASON_PREV_AUTH_NOT_VALID);
+	ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+	return RADIUS_DAS_SUCCESS;
+}
+
+#endif /* CONFIG_NO_RADIUS */
 
 
 /**
@@ -519,7 +649,7 @@
 	if (conf->wmm_enabled < 0)
 		conf->wmm_enabled = hapd->iconf->ieee80211n;
 
-	hostapd_flush_old_stations(hapd);
+	hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID);
 	hostapd_set_privacy(hapd, 0);
 
 	hostapd_broadcast_wep_clear(hapd);
@@ -552,14 +682,14 @@
 		set_ssid = 0;
 		conf->ssid.ssid_len = ssid_len;
 		os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len);
-		conf->ssid.ssid[conf->ssid.ssid_len] = '\0';
 	}
 
 	if (!hostapd_drv_none(hapd)) {
 		wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR
-			   " and ssid '%s'",
+			   " and ssid \"%s\"",
 			   hapd->conf->iface, MAC2STR(hapd->own_addr),
-			   hapd->conf->ssid.ssid);
+			   wpa_ssid_txt(hapd->conf->ssid.ssid,
+					hapd->conf->ssid.ssid_len));
 	}
 
 	if (hostapd_setup_wpa_psk(conf)) {
@@ -569,7 +699,7 @@
 
 	/* Set SSID for the kernel driver (to be used in beacon and probe
 	 * response frames) */
-	if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid.ssid,
+	if (set_ssid && hostapd_set_ssid(hapd, conf->ssid.ssid,
 					 conf->ssid.ssid_len)) {
 		wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
 		return -1;
@@ -583,6 +713,27 @@
 		wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
 		return -1;
 	}
+
+	if (hapd->conf->radius_das_port) {
+		struct radius_das_conf das_conf;
+		os_memset(&das_conf, 0, sizeof(das_conf));
+		das_conf.port = hapd->conf->radius_das_port;
+		das_conf.shared_secret = hapd->conf->radius_das_shared_secret;
+		das_conf.shared_secret_len =
+			hapd->conf->radius_das_shared_secret_len;
+		das_conf.client_addr = &hapd->conf->radius_das_client_addr;
+		das_conf.time_window = hapd->conf->radius_das_time_window;
+		das_conf.require_event_timestamp =
+			hapd->conf->radius_das_require_event_timestamp;
+		das_conf.ctx = hapd;
+		das_conf.disconnect = hostapd_das_disconnect;
+		hapd->radius_das = radius_das_init(&das_conf);
+		if (hapd->radius_das == NULL) {
+			wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
+				   "failed.");
+			return -1;
+		}
+	}
 #endif /* CONFIG_NO_RADIUS */
 
 	if (hostapd_acl_init(hapd)) {
@@ -615,8 +766,16 @@
 		return -1;
 	}
 
-	if (hapd->iface->ctrl_iface_init &&
-	    hapd->iface->ctrl_iface_init(hapd)) {
+#ifdef CONFIG_INTERWORKING
+	if (gas_serv_init(hapd)) {
+		wpa_printf(MSG_ERROR, "GAS server initialization failed");
+		return -1;
+	}
+#endif /* CONFIG_INTERWORKING */
+
+	if (hapd->iface->interfaces &&
+	    hapd->iface->interfaces->ctrl_iface_init &&
+	    hapd->iface->interfaces->ctrl_iface_init(hapd)) {
 		wpa_printf(MSG_ERROR, "Failed to setup control interface");
 		return -1;
 	}
@@ -857,6 +1016,7 @@
 	hapd->conf = bss;
 	hapd->iface = hapd_iface;
 	hapd->driver = hapd->iconf->driver;
+	hapd->ctrl_sock = -1;
 
 	return hapd;
 }
@@ -873,7 +1033,8 @@
 	for (j = 0; j < iface->num_bss; j++) {
 		struct hostapd_data *hapd = iface->bss[j];
 		hostapd_free_stas(hapd);
-		hostapd_flush_old_stations(hapd);
+		hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+		hostapd_clear_wep(hapd);
 		hostapd_cleanup(hapd);
 	}
 }
@@ -888,6 +1049,292 @@
 }
 
 
+#ifdef HOSTAPD
+
+void hostapd_interface_deinit_free(struct hostapd_iface *iface)
+{
+	const struct wpa_driver_ops *driver;
+	void *drv_priv;
+	if (iface == NULL)
+		return;
+	driver = iface->bss[0]->driver;
+	drv_priv = iface->bss[0]->drv_priv;
+	hostapd_interface_deinit(iface);
+	if (driver && driver->hapd_deinit && drv_priv)
+		driver->hapd_deinit(drv_priv);
+	hostapd_interface_free(iface);
+}
+
+
+int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
+{
+	if (hapd_iface->bss[0]->drv_priv != NULL) {
+		wpa_printf(MSG_ERROR, "Interface %s already enabled",
+			   hapd_iface->conf->bss[0].iface);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "Enable interface %s",
+		   hapd_iface->conf->bss[0].iface);
+
+	if (hapd_iface->interfaces == NULL ||
+	    hapd_iface->interfaces->driver_init == NULL ||
+	    hapd_iface->interfaces->driver_init(hapd_iface) ||
+	    hostapd_setup_interface(hapd_iface)) {
+		hostapd_interface_deinit_free(hapd_iface);
+		return -1;
+	}
+	return 0;
+}
+
+
+int hostapd_reload_iface(struct hostapd_iface *hapd_iface)
+{
+	size_t j;
+
+	wpa_printf(MSG_DEBUG, "Reload interface %s",
+		   hapd_iface->conf->bss[0].iface);
+	for (j = 0; j < hapd_iface->num_bss; j++) {
+		hostapd_flush_old_stations(hapd_iface->bss[j],
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+#ifndef CONFIG_NO_RADIUS
+		/* TODO: update dynamic data based on changed configuration
+		 * items (e.g., open/close sockets, etc.) */
+		radius_client_flush(hapd_iface->bss[j]->radius, 0);
+#endif  /* CONFIG_NO_RADIUS */
+
+		hostapd_reload_bss(hapd_iface->bss[j]);
+	}
+	return 0;
+}
+
+
+int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
+{
+	size_t j;
+	struct hostapd_bss_config *bss = hapd_iface->bss[0]->conf;
+	const struct wpa_driver_ops *driver;
+	void *drv_priv;
+
+	if (hapd_iface == NULL)
+		return -1;
+	driver = hapd_iface->bss[0]->driver;
+	drv_priv = hapd_iface->bss[0]->drv_priv;
+
+	/* whatever hostapd_interface_deinit does */
+	for (j = 0; j < hapd_iface->num_bss; j++) {
+		struct hostapd_data *hapd = hapd_iface->bss[j];
+		hostapd_free_stas(hapd);
+		hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+		hostapd_clear_wep(hapd);
+		hostapd_free_hapd_data(hapd);
+	}
+
+	if (driver && driver->hapd_deinit && drv_priv) {
+		driver->hapd_deinit(drv_priv);
+		hapd_iface->bss[0]->drv_priv = NULL;
+	}
+
+	/* From hostapd_cleanup_iface: These were initialized in
+	 * hostapd_setup_interface and hostapd_setup_interface_complete
+	 */
+	hostapd_cleanup_iface_partial(hapd_iface);
+	bss->wpa = 0;
+	bss->wpa_key_mgmt = -1;
+	bss->wpa_pairwise = -1;
+
+	wpa_printf(MSG_DEBUG, "Interface %s disabled", bss->iface);
+	return 0;
+}
+
+
+static struct hostapd_iface *
+hostapd_iface_alloc(struct hapd_interfaces *interfaces)
+{
+	struct hostapd_iface **iface, *hapd_iface;
+
+	iface = os_realloc_array(interfaces->iface, interfaces->count + 1,
+				 sizeof(struct hostapd_iface *));
+	if (iface == NULL)
+		return NULL;
+	interfaces->iface = iface;
+	hapd_iface = interfaces->iface[interfaces->count] =
+		os_zalloc(sizeof(*hapd_iface));
+	if (hapd_iface == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+			   "the interface", __func__);
+		return NULL;
+	}
+	interfaces->count++;
+	hapd_iface->interfaces = interfaces;
+
+	return hapd_iface;
+}
+
+
+static struct hostapd_config *
+hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
+		     const char *ctrl_iface)
+{
+	struct hostapd_bss_config *bss;
+	struct hostapd_config *conf;
+
+	/* Allocates memory for bss and conf */
+	conf = hostapd_config_defaults();
+	if (conf == NULL) {
+		 wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+				"configuration", __func__);
+		return NULL;
+	}
+
+	conf->driver = wpa_drivers[0];
+	if (conf->driver == NULL) {
+		wpa_printf(MSG_ERROR, "No driver wrappers registered!");
+		hostapd_config_free(conf);
+		return NULL;
+	}
+
+	bss = conf->last_bss = conf->bss;
+
+	os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
+	bss->ctrl_interface = os_strdup(ctrl_iface);
+	if (bss->ctrl_interface == NULL) {
+		hostapd_config_free(conf);
+		return NULL;
+	}
+
+	/* Reading configuration file skipped, will be done in SET!
+	 * From reading the configuration till the end has to be done in
+	 * SET
+	 */
+	return conf;
+}
+
+
+static struct hostapd_iface * hostapd_data_alloc(
+	struct hapd_interfaces *interfaces, struct hostapd_config *conf)
+{
+	size_t i;
+	struct hostapd_iface *hapd_iface =
+		interfaces->iface[interfaces->count - 1];
+	struct hostapd_data *hapd;
+
+	hapd_iface->conf = conf;
+	hapd_iface->num_bss = conf->num_bss;
+
+	hapd_iface->bss = os_zalloc(conf->num_bss *
+				    sizeof(struct hostapd_data *));
+	if (hapd_iface->bss == NULL)
+		return NULL;
+
+	for (i = 0; i < conf->num_bss; i++) {
+		hapd = hapd_iface->bss[i] =
+			hostapd_alloc_bss_data(hapd_iface, conf,
+					       &conf->bss[i]);
+		if (hapd == NULL)
+			return NULL;
+		hapd->msg_ctx = hapd;
+	}
+
+	hapd_iface->interfaces = interfaces;
+
+	return hapd_iface;
+}
+
+
+int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
+{
+	struct hostapd_config *conf = NULL;
+	struct hostapd_iface *hapd_iface = NULL;
+	char *ptr;
+	size_t i;
+
+	ptr = os_strchr(buf, ' ');
+	if (ptr == NULL)
+		return -1;
+	*ptr++ = '\0';
+
+	for (i = 0; i < interfaces->count; i++) {
+		if (!os_strcmp(interfaces->iface[i]->conf->bss[0].iface,
+			       buf)) {
+			wpa_printf(MSG_INFO, "Cannot add interface - it "
+				   "already exists");
+			return -1;
+		}
+	}
+
+	hapd_iface = hostapd_iface_alloc(interfaces);
+	if (hapd_iface == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+			   "for interface", __func__);
+		goto fail;
+	}
+
+	conf = hostapd_config_alloc(interfaces, buf, ptr);
+	if (conf == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+			   "for configuration", __func__);
+		goto fail;
+	}
+
+	hapd_iface = hostapd_data_alloc(interfaces, conf);
+	if (hapd_iface == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+			   "for hostapd", __func__);
+		goto fail;
+	}
+
+	if (hapd_iface->interfaces &&
+	    hapd_iface->interfaces->ctrl_iface_init &&
+	    hapd_iface->interfaces->ctrl_iface_init(hapd_iface->bss[0])) {
+		wpa_printf(MSG_ERROR, "%s: Failed to setup control "
+			   "interface", __func__);
+		goto fail;
+	}
+	wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0].iface);
+
+	return 0;
+
+fail:
+	if (conf)
+		hostapd_config_free(conf);
+	if (hapd_iface) {
+		os_free(hapd_iface->bss[interfaces->count]);
+		os_free(hapd_iface);
+	}
+	return -1;
+}
+
+
+int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+{
+	struct hostapd_iface *hapd_iface;
+	size_t i, k = 0;
+
+	for (i = 0; i < interfaces->count; i++) {
+		hapd_iface = interfaces->iface[i];
+		if (hapd_iface == NULL)
+			return -1;
+		if (!os_strcmp(hapd_iface->conf->bss[0].iface, buf)) {
+			wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+			hostapd_interface_deinit_free(hapd_iface);
+			k = i;
+			while (k < (interfaces->count - 1)) {
+				interfaces->iface[k] =
+					interfaces->iface[k + 1];
+				k++;
+			}
+			interfaces->count--;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+#endif /* HOSTAPD */
+
+
 /**
  * hostapd_new_assoc_sta - Notify that a new station associated with the AP
  * @hapd: Pointer to BSS data
@@ -937,4 +1384,12 @@
 			wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
 	} else
 		wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - ap_max_inactivity)",
+		   __func__, MAC2STR(sta->addr),
+		   hapd->conf->ap_max_inactivity);
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+			       ap_handle_timer, hapd, sta);
 }
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index bc28805..71f476c 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -15,7 +15,6 @@
 struct wpa_ctrl_dst;
 struct radius_server_data;
 struct upnp_wps_device_sm;
-struct hapd_interfaces;
 struct hostapd_data;
 struct sta_info;
 struct hostap_sta_driver_data;
@@ -24,9 +23,29 @@
 enum wps_event;
 union wps_event_data;
 
+struct hostapd_iface;
+
+struct hapd_interfaces {
+	int (*reload_config)(struct hostapd_iface *iface);
+	struct hostapd_config * (*config_read_cb)(const char *config_fname);
+	int (*ctrl_iface_init)(struct hostapd_data *hapd);
+	void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
+	int (*for_each_interface)(struct hapd_interfaces *interfaces,
+				  int (*cb)(struct hostapd_iface *iface,
+					    void *ctx), void *ctx);
+	int (*driver_init)(struct hostapd_iface *iface);
+
+	size_t count;
+	int global_ctrl_sock;
+	char *global_iface_path;
+	char *global_iface_name;
+	struct hostapd_iface **iface;
+};
+
+
 struct hostapd_probereq_cb {
 	int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
-		  const u8 *ie, size_t ie_len);
+		  const u8 *ie, size_t ie_len, int ssi_signal);
 	void *ctx;
 };
 
@@ -40,7 +59,7 @@
 struct hostapd_frame_info {
 	u32 channel;
 	u32 datarate;
-	u32 ssi_signal;
+	int ssi_signal; /* dBm */
 };
 
 
@@ -80,6 +99,7 @@
 
 	struct radius_client_data *radius;
 	u32 acct_session_id_hi, acct_session_id_lo;
+	struct radius_das_data *radius_das;
 
 	struct iapp_data *iapp;
 
@@ -164,6 +184,9 @@
 	int noa_start;
 	int noa_duration;
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+	size_t gas_frag_limit;
+#endif /* CONFIG_INTERWORKING */
 };
 
 
@@ -173,8 +196,6 @@
 struct hostapd_iface {
 	struct hapd_interfaces *interfaces;
 	void *owner;
-	int (*reload_config)(struct hostapd_iface *iface);
-	struct hostapd_config * (*config_read_cb)(const char *config_fname);
 	char *config_fname;
 	struct hostapd_config *conf;
 
@@ -232,16 +253,12 @@
 
 	u16 ht_op_mode;
 	void (*scan_cb)(struct hostapd_iface *iface);
-
-	int (*ctrl_iface_init)(struct hostapd_data *hapd);
-	void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
-
-	int (*for_each_interface)(struct hapd_interfaces *interfaces,
-				  int (*cb)(struct hostapd_iface *iface,
-					    void *ctx), void *ctx);
 };
 
 /* hostapd.c */
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+			       int (*cb)(struct hostapd_iface *iface,
+					 void *ctx), void *ctx);
 int hostapd_reload_config(struct hostapd_iface *iface);
 struct hostapd_data *
 hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
@@ -253,12 +270,19 @@
 void hostapd_interface_free(struct hostapd_iface *iface);
 void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
 			   int reassoc);
+void hostapd_interface_deinit_free(struct hostapd_iface *iface);
+int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
+int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
+int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
+int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
+int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
 
 /* utils.c */
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
 				 int (*cb)(void *ctx, const u8 *sa,
 					   const u8 *da, const u8 *bssid,
-					   const u8 *ie, size_t ie_len),
+					   const u8 *ie, size_t ie_len,
+					   int ssi_signal),
 				 void *ctx);
 void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
 
@@ -268,6 +292,9 @@
 void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
 void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
 int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
-			 const u8 *bssid, const u8 *ie, size_t ie_len);
+			 const u8 *bssid, const u8 *ie, size_t ie_len,
+			 int ssi_signal);
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+			     int offset);
 
 #endif /* HOSTAPD_H */
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
new file mode 100644
index 0000000..45d518b
--- /dev/null
+++ b/src/ap/hs20.c
@@ -0,0 +1,31 @@
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2009, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "hs20.h"
+
+
+u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
+{
+	if (!hapd->conf->hs20)
+		return eid;
+	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
+	*eid++ = 5;
+	WPA_PUT_BE24(eid, OUI_WFA);
+	eid += 3;
+	*eid++ = HS20_INDICATION_OUI_TYPE;
+	/* Hotspot Configuration: DGAF Enabled */
+	*eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00;
+	return eid;
+}
diff --git a/src/ap/hs20.h b/src/ap/hs20.h
new file mode 100644
index 0000000..98698ce
--- /dev/null
+++ b/src/ap/hs20.h
@@ -0,0 +1,16 @@
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HS20_H
+#define HS20_H
+
+struct hostapd_data;
+
+u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
+
+#endif /* HS20_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 8c6fef2..97e1238 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -2,7 +2,7 @@
  * hostapd / Hardware feature query and different modes
  * Copyright 2002-2003, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
- * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -129,6 +129,8 @@
 	i = 0;
 	while (basic_rates[i] >= 0)
 		i++;
+	if (i)
+		i++; /* -1 termination */
 	os_free(iface->basic_rates);
 	iface->basic_rates = os_malloc(i * sizeof(int));
 	if (iface->basic_rates)
@@ -138,7 +140,7 @@
 	iface->num_rates = 0;
 
 	iface->current_rates =
-		os_zalloc(mode->num_rates * sizeof(struct hostapd_rate_data));
+		os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
 	if (!iface->current_rates) {
 		wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
 			   "table.");
@@ -416,7 +418,7 @@
 	int res;
 
 	/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
-	 * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */
+	 * allowed per IEEE Std 802.11-2012, 10.15.3.2 */
 
 	iface->scan_cb = NULL;
 
@@ -447,6 +449,46 @@
 }
 
 
+static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
+					 struct wpa_driver_scan_params *params)
+{
+	/* Scan only the affected frequency range */
+	int pri_freq, sec_freq;
+	int affected_start, affected_end;
+	int i, pos;
+	struct hostapd_hw_modes *mode;
+
+	if (iface->current_mode == NULL)
+		return;
+
+	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+	if (iface->conf->secondary_channel > 0)
+		sec_freq = pri_freq + 20;
+	else
+		sec_freq = pri_freq - 20;
+	affected_start = (pri_freq + sec_freq) / 2 - 25;
+	affected_end = (pri_freq + sec_freq) / 2 + 25;
+	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+
+	mode = iface->current_mode;
+	params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+	if (params->freqs == NULL)
+		return;
+	pos = 0;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		if (chan->freq < affected_start ||
+		    chan->freq > affected_end)
+			continue;
+		params->freqs[pos++] = chan->freq;
+	}
+}
+
+
 static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
 {
 	struct wpa_driver_scan_params params;
@@ -457,12 +499,15 @@
 	wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
 		   "40 MHz channel");
 	os_memset(&params, 0, sizeof(params));
-	/* TODO: scan only the needed frequency */
+	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+		ieee80211n_scan_channels_2g4(iface, &params);
 	if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
 		wpa_printf(MSG_ERROR, "Failed to request a scan of "
 			   "neighboring BSSes");
+		os_free(params.freqs);
 		return -1;
 	}
+	os_free(params.freqs);
 
 	iface->scan_cb = ieee80211n_check_scan;
 	return 1;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 9c931ca..ce20e5f 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -311,6 +311,8 @@
 	int has_psk = 0;
 	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
 	size_t resp_ies_len = 0;
+	char *identity = NULL;
+	char *radius_cui = NULL;
 
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
 		printf("handle_auth - too short payload (len=%lu)\n",
@@ -372,7 +374,7 @@
 	res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
 				      &session_timeout,
 				      &acct_interim_interval, &vlan_id,
-				      psk, &has_psk);
+				      psk, &has_psk, &identity, &radius_cui);
 
 	if (res == HOSTAPD_ACL_REJECT) {
 		printf("Station " MACSTR " not allowed to authenticate.\n",
@@ -421,6 +423,11 @@
 		sta->psk = NULL;
 	}
 
+	sta->identity = identity;
+	identity = NULL;
+	sta->radius_cui = radius_cui;
+	radius_cui = NULL;
+
 	sta->flags &= ~WLAN_STA_PREAUTH;
 	ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
 
@@ -482,6 +489,9 @@
 	}
 
  fail:
+	os_free(identity);
+	os_free(radius_cui);
+
 	send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
 			auth_transaction + 1, resp, resp_ies, resp_ies_len);
 }
@@ -576,35 +586,20 @@
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
-	if (elems->supp_rates_len > sizeof(sta->supported_rates)) {
+	if (elems->supp_rates_len + elems->ext_supp_rates_len >
+	    sizeof(sta->supported_rates)) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
-			       "Invalid supported rates element length %d",
-			       elems->supp_rates_len);
+			       "Invalid supported rates element length %d+%d",
+			       elems->supp_rates_len,
+			       elems->ext_supp_rates_len);
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
-	os_memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
-	os_memcpy(sta->supported_rates, elems->supp_rates,
-		  elems->supp_rates_len);
-	sta->supported_rates_len = elems->supp_rates_len;
-
-	if (elems->ext_supp_rates) {
-		if (elems->supp_rates_len + elems->ext_supp_rates_len >
-		    sizeof(sta->supported_rates)) {
-			hostapd_logger(hapd, sta->addr,
-				       HOSTAPD_MODULE_IEEE80211,
-				       HOSTAPD_LEVEL_DEBUG,
-				       "Invalid supported rates element length"
-				       " %d+%d", elems->supp_rates_len,
-				       elems->ext_supp_rates_len);
-			return WLAN_STATUS_UNSPECIFIED_FAILURE;
-		}
-
-		os_memcpy(sta->supported_rates + elems->supp_rates_len,
-			  elems->ext_supp_rates, elems->ext_supp_rates_len);
-		sta->supported_rates_len += elems->ext_supp_rates_len;
-	}
+	sta->supported_rates_len = merge_byte_arrays(
+		sta->supported_rates, sizeof(sta->supported_rates),
+		elems->supp_rates, elems->supp_rates_len,
+		elems->ext_supp_rates, elems->ext_supp_rates_len);
 
 	return WLAN_STATUS_SUCCESS;
 }
@@ -648,6 +643,20 @@
 	}
 #endif /* CONFIG_IEEE80211N */
 
+#ifdef CONFIG_IEEE80211AC
+	resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities,
+				  elems.vht_capabilities_len);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
+	    !(sta->flags & WLAN_STA_VHT)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "Station does not support "
+			       "mandatory VHT PHY - reject association");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+#endif /* CONFIG_IEEE80211AC */
+
 	if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
 		wpa_ie = elems.rsn_ie;
 		wpa_ie_len = elems.rsn_ie_len;
@@ -771,7 +780,7 @@
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_IEEE80211N
-		if ((sta->flags & WLAN_STA_HT) &&
+		if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
 		    wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
@@ -875,7 +884,13 @@
 	p = hostapd_eid_ht_operation(hapd, p);
 #endif /* CONFIG_IEEE80211N */
 
+#ifdef CONFIG_IEEE80211AC
+	p = hostapd_eid_vht_capabilities(hapd, p);
+	p = hostapd_eid_vht_operation(hapd, p);
+#endif /* CONFIG_IEEE80211AC */
+
 	p = hostapd_eid_ext_capab(hapd, p);
+	p = hostapd_eid_bss_max_idle_period(hapd, p);
 
 	if (sta->flags & WLAN_STA_WMM)
 		p = hostapd_eid_wmm(hapd, p);
@@ -1336,7 +1351,10 @@
 		os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
 		resp->u.action.category |= 0x80;
 
-		hostapd_drv_send_mlme(hapd, resp, len, 0);
+		if (hostapd_drv_send_mlme(hapd, resp, len, 0) < 0) {
+			wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send "
+				   "Action frame");
+		}
 		os_free(resp);
 	}
 }
@@ -1392,7 +1410,7 @@
 
 
 	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
-		handle_probe_req(hapd, mgmt, len);
+		handle_probe_req(hapd, mgmt, len, fi->ssi_signal);
 		return;
 	}
 
@@ -1488,13 +1506,6 @@
 	int new_assoc = 1;
 	struct ieee80211_ht_capabilities ht_cap;
 
-	if (!ok) {
-		hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
-			       HOSTAPD_LEVEL_DEBUG,
-			       "did not acknowledge association response");
-		return;
-	}
-
 	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
 				      sizeof(mgmt->u.assoc_resp))) {
 		printf("handle_assoc_cb(reassoc=%d) - too short payload "
@@ -1502,11 +1513,6 @@
 		return;
 	}
 
-	if (reassoc)
-		status = le_to_host16(mgmt->u.reassoc_resp.status_code);
-	else
-		status = le_to_host16(mgmt->u.assoc_resp.status_code);
-
 	sta = ap_get_sta(hapd, mgmt->da);
 	if (!sta) {
 		printf("handle_assoc_cb: STA " MACSTR " not found\n",
@@ -1514,6 +1520,19 @@
 		return;
 	}
 
+	if (!ok) {
+		hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "did not acknowledge association response");
+		sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
+		return;
+	}
+
+	if (reassoc)
+		status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+	else
+		status = le_to_host16(mgmt->u.assoc_resp.status_code);
+
 	if (status != WLAN_STATUS_SUCCESS)
 		goto fail;
 
@@ -1812,6 +1831,9 @@
 
 	sta = ap_get_sta(hapd, src);
 	if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+		if (!hapd->conf->wds_sta)
+			return;
+
 		if (wds && !(sta->flags & WLAN_STA_WDS)) {
 			wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
 				   "STA " MACSTR " (aid %u)",
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index d30e90f..1e5800d 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -45,6 +45,8 @@
 u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
 				  const u8 *addr, const u8 *trans_id);
@@ -54,6 +56,8 @@
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      const u8 *ht_capab, size_t ht_capab_len);
 void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
+u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *vht_capab, size_t vht_capab_len);
 void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
 		       const u8 *buf, size_t len, int ack);
 void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
@@ -72,5 +76,6 @@
 u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
 int hostapd_update_time_adv(struct hostapd_data *hapd);
 void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
 
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 109c4bc..63ae345 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.11 authentication (ACL)
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,6 +22,7 @@
 #include "ap_config.h"
 #include "ap_drv_ops.h"
 #include "ieee802_11.h"
+#include "ieee802_1x.h"
 #include "ieee802_11_auth.h"
 
 #define RADIUS_ACL_TIMEOUT 30
@@ -37,6 +38,8 @@
 	int vlan_id;
 	int has_psk;
 	u8 psk[PMK_LEN];
+	char *identity;
+	char *radius_cui;
 };
 
 
@@ -51,6 +54,14 @@
 
 
 #ifndef CONFIG_NO_RADIUS
+static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
+{
+	os_free(e->identity);
+	os_free(e->radius_cui);
+	os_free(e);
+}
+
+
 static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
 {
 	struct hostapd_cached_radius_acl *prev;
@@ -58,7 +69,7 @@
 	while (acl_cache) {
 		prev = acl_cache;
 		acl_cache = acl_cache->next;
-		os_free(prev);
+		hostapd_acl_cache_free_entry(prev);
 	}
 }
 
@@ -66,35 +77,45 @@
 static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
 				 u32 *session_timeout,
 				 u32 *acct_interim_interval, int *vlan_id,
-				 u8 *psk, int *has_psk)
+				 u8 *psk, int *has_psk, char **identity,
+				 char **radius_cui)
 {
 	struct hostapd_cached_radius_acl *entry;
 	struct os_time now;
 
 	os_get_time(&now);
-	entry = hapd->acl_cache;
 
-	while (entry) {
-		if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
-			if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
-				return -1; /* entry has expired */
-			if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
-				if (session_timeout)
-					*session_timeout =
-						entry->session_timeout;
-			if (acct_interim_interval)
-				*acct_interim_interval =
-					entry->acct_interim_interval;
-			if (vlan_id)
-				*vlan_id = entry->vlan_id;
-			if (psk)
-				os_memcpy(psk, entry->psk, PMK_LEN);
-			if (has_psk)
-				*has_psk = entry->has_psk;
-			return entry->accepted;
+	for (entry = hapd->acl_cache; entry; entry = entry->next) {
+		if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
+			continue;
+
+		if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
+			return -1; /* entry has expired */
+		if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+			if (session_timeout)
+				*session_timeout = entry->session_timeout;
+		if (acct_interim_interval)
+			*acct_interim_interval =
+				entry->acct_interim_interval;
+		if (vlan_id)
+			*vlan_id = entry->vlan_id;
+		if (psk)
+			os_memcpy(psk, entry->psk, PMK_LEN);
+		if (has_psk)
+			*has_psk = entry->has_psk;
+		if (identity) {
+			if (entry->identity)
+				*identity = os_strdup(entry->identity);
+			else
+				*identity = NULL;
 		}
-
-		entry = entry->next;
+		if (radius_cui) {
+			if (entry->radius_cui)
+				*radius_cui = os_strdup(entry->radius_cui);
+			else
+				*radius_cui = NULL;
+		}
+		return entry->accepted;
 	}
 
 	return -1;
@@ -140,37 +161,9 @@
 		goto fail;
 	}
 
-	if (hapd->conf->own_ip_addr.af == AF_INET &&
-	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
-				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
-		wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address");
+	if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr,
+				   NULL, msg) < 0)
 		goto fail;
-	}
-
-#ifdef CONFIG_IPV6
-	if (hapd->conf->own_ip_addr.af == AF_INET6 &&
-	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
-				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
-		wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address");
-		goto fail;
-	}
-#endif /* CONFIG_IPV6 */
-
-	if (hapd->conf->nas_identifier &&
-	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
-				 (u8 *) hapd->conf->nas_identifier,
-				 os_strlen(hapd->conf->nas_identifier))) {
-		wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier");
-		goto fail;
-	}
-
-	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
-		    MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
-	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
-				 (u8 *) buf, os_strlen(buf))) {
-		wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id");
-		goto fail;
-	}
 
 	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
 		    MAC2STR(addr));
@@ -180,12 +173,6 @@
 		goto fail;
 	}
 
-	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
-				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
-		wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type");
-		goto fail;
-	}
-
 	os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
 				 (u8 *) buf, os_strlen(buf))) {
@@ -193,7 +180,8 @@
 		goto fail;
 	}
 
-	radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr);
+	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
+		goto fail;
 	return 0;
 
  fail:
@@ -214,12 +202,18 @@
  * @vlan_id: Buffer for returning VLAN ID
  * @psk: Buffer for returning WPA PSK
  * @has_psk: Buffer for indicating whether psk was filled
+ * @identity: Buffer for returning identity (from RADIUS)
+ * @radius_cui: Buffer for returning CUI (from RADIUS)
  * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ *
+ * The caller is responsible for freeing the returned *identity and *radius_cui
+ * values with os_free().
  */
 int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 			    const u8 *msg, size_t len, u32 *session_timeout,
 			    u32 *acct_interim_interval, int *vlan_id,
-			    u8 *psk, int *has_psk)
+			    u8 *psk, int *has_psk, char **identity,
+			    char **radius_cui)
 {
 	if (session_timeout)
 		*session_timeout = 0;
@@ -231,6 +225,10 @@
 		*has_psk = 0;
 	if (psk)
 		os_memset(psk, 0, PMK_LEN);
+	if (identity)
+		*identity = NULL;
+	if (radius_cui)
+		*radius_cui = NULL;
 
 	if (hostapd_maclist_found(hapd->conf->accept_mac,
 				  hapd->conf->num_accept_mac, addr, vlan_id))
@@ -255,7 +253,8 @@
 		/* Check whether ACL cache has an entry for this station */
 		int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
 						acct_interim_interval,
-						vlan_id, psk, has_psk);
+						vlan_id, psk, has_psk,
+						identity, radius_cui);
 		if (res == HOSTAPD_ACL_ACCEPT ||
 		    res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
 			return res;
@@ -267,6 +266,14 @@
 			if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
 				/* pending query in RADIUS retransmit queue;
 				 * do not generate a new one */
+				if (identity) {
+					os_free(*identity);
+					*identity = NULL;
+				}
+				if (radius_cui) {
+					os_free(*radius_cui);
+					*radius_cui = NULL;
+				}
 				return HOSTAPD_ACL_PENDING;
 			}
 			query = query->next;
@@ -332,7 +339,7 @@
 			hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
 			tmp = entry;
 			entry = entry->next;
-			os_free(tmp);
+			hostapd_acl_cache_free_entry(tmp);
 			continue;
 		}
 
@@ -449,6 +456,8 @@
 	if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
 		int passphraselen;
 		char *passphrase;
+		u8 *buf;
+		size_t len;
 
 		if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
 					      &cache->session_timeout) == 0)
@@ -490,9 +499,22 @@
 			}
 			os_free(passphrase);
 		}
+		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+					    &buf, &len, NULL) == 0) {
+			cache->identity = os_zalloc(len + 1);
+			if (cache->identity)
+				os_memcpy(cache->identity, buf, len);
+		}
+		if (radius_msg_get_attr_ptr(
+			    msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+			    &buf, &len, NULL) == 0) {
+			cache->radius_cui = os_zalloc(len + 1);
+			if (cache->radius_cui)
+				os_memcpy(cache->radius_cui, buf, len);
+		}
 
 		if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
-		    cache->psk == NULL)
+		    !cache->has_psk)
 			cache->accepted = HOSTAPD_ACL_REJECT;
 	} else
 		cache->accepted = HOSTAPD_ACL_REJECT;
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index b8a4c13..0e8d1cb 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -19,7 +19,8 @@
 int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 			    const u8 *msg, size_t len, u32 *session_timeout,
 			    u32 *acct_interim_interval, int *vlan_id,
-			    u8 *psk, int *has_psk);
+			    u8 *psk, int *has_psk, char **identity,
+			    char **radius_cui);
 int hostapd_acl_init(struct hostapd_data *hapd);
 void hostapd_acl_deinit(struct hostapd_data *hapd);
 
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 0935cd5..b3fdf3d 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -397,3 +397,31 @@
 
 	return 0;
 }
+
+
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+
+#ifdef CONFIG_WNM
+	if (hapd->conf->ap_max_inactivity > 0) {
+		unsigned int val;
+		*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
+		*pos++ = 3;
+		val = hapd->conf->ap_max_inactivity;
+		if (val > 68000)
+			val = 68000;
+		val *= 1000;
+		val /= 1024;
+		if (val == 0)
+			val = 1;
+		if (val > 65535)
+			val = 65535;
+		WPA_PUT_LE16(pos, val);
+		pos += 2;
+		*pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
+	}
+#endif /* CONFIG_WNM */
+
+	return pos;
+}
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
new file mode 100644
index 0000000..7599ef8
--- /dev/null
+++ b/src/ap/ieee802_11_vht.c
@@ -0,0 +1,108 @@
+/*
+ * hostapd / IEEE 802.11ac VHT
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of BSD license
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+
+
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_vht_capabilities *cap;
+	u8 *pos = eid;
+
+	if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode ||
+	    hapd->conf->disable_11ac)
+		return eid;
+
+	*pos++ = WLAN_EID_VHT_CAP;
+	*pos++ = sizeof(*cap);
+
+	cap = (struct ieee80211_vht_capabilities *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+	cap->vht_capabilities_info = host_to_le32(
+		hapd->iface->current_mode->vht_capab);
+
+	/* Supported MCS set comes from hw */
+	os_memcpy(cap->vht_supported_mcs_set,
+	          hapd->iface->current_mode->vht_mcs_set, 8);
+
+	pos += sizeof(*cap);
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_vht_operation *oper;
+	u8 *pos = eid;
+
+	if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac)
+		return eid;
+
+	*pos++ = WLAN_EID_VHT_OPERATION;
+	*pos++ = sizeof(*oper);
+
+	oper = (struct ieee80211_vht_operation *) pos;
+	os_memset(oper, 0, sizeof(*oper));
+
+	/*
+	 * center freq = 5 GHz + (5 * index)
+	 * So index 42 gives center freq 5.210 GHz
+	 * which is channel 42 in 5G band
+	 */
+	oper->vht_op_info_chan_center_freq_seg0_idx =
+		hapd->iconf->vht_oper_centr_freq_seg0_idx;
+
+	oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
+
+	/* VHT Basic MCS set comes from hw */
+	/* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
+	oper->vht_basic_mcs_set = host_to_le16(0xfffc);
+	pos += sizeof(*oper);
+
+	return pos;
+}
+
+
+u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *vht_capab, size_t vht_capab_len)
+{
+	/* Disable VHT caps for STAs associated to no-VHT BSSes. */
+	if (!vht_capab ||
+	    vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
+	    hapd->conf->disable_11ac) {
+		sta->flags &= ~WLAN_STA_VHT;
+		os_free(sta->vht_capabilities);
+		sta->vht_capabilities = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (sta->vht_capabilities == NULL) {
+		sta->vht_capabilities =
+			os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+		if (sta->vht_capabilities == NULL)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_VHT;
+	os_memcpy(sta->vht_capabilities, vht_capab,
+		  sizeof(struct ieee80211_vht_capabilities));
+
+	return WLAN_STATUS_SUCCESS;
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index a329777..c4d3da8 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -126,7 +126,7 @@
 	hdr = (struct ieee802_1x_hdr *) buf;
 	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
 	key->type = EAPOL_KEY_TYPE_RC4;
-	key->key_length = htons(key_len);
+	WPA_PUT_BE16(key->key_length, key_len);
 	wpa_get_ntp_timestamp(key->replay_counter);
 
 	if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
@@ -409,12 +409,132 @@
 }
 
 
+static int add_common_radius_sta_attr(struct hostapd_data *hapd,
+				      struct hostapd_radius_attr *req_attr,
+				      struct sta_info *sta,
+				      struct radius_msg *msg)
+{
+	char buf[128];
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_PORT) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-Port");
+		return -1;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+		    MAC2STR(sta->addr));
+	buf[sizeof(buf) - 1] = '\0';
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id");
+		return -1;
+	}
+
+	if (sta->flags & WLAN_STA_PREAUTH) {
+		os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
+			   sizeof(buf));
+	} else {
+		os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
+			    radius_sta_rate(hapd, sta) / 2,
+			    (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
+			    radius_mode_txt(hapd));
+		buf[sizeof(buf) - 1] = '\0';
+	}
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_CONNECT_INFO) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_ERROR, "Could not add Connect-Info");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int add_common_radius_attr(struct hostapd_data *hapd,
+			   struct hostapd_radius_attr *req_attr,
+			   struct sta_info *sta,
+			   struct radius_msg *msg)
+{
+	char buf[128];
+	struct hostapd_radius_attr *attr;
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
+	    hapd->conf->own_ip_addr.af == AF_INET &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address");
+		return -1;
+	}
+
+#ifdef CONFIG_IPV6
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_IPV6_ADDRESS) &&
+	    hapd->conf->own_ip_addr.af == AF_INET6 &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address");
+		return -1;
+	}
+#endif /* CONFIG_IPV6 */
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_IDENTIFIER) &&
+	    hapd->conf->nas_identifier &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+				 (u8 *) hapd->conf->nas_identifier,
+				 os_strlen(hapd->conf->nas_identifier))) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-Identifier");
+		return -1;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+		    MAC2STR(hapd->own_addr),
+		    wpa_ssid_txt(hapd->conf->ssid.ssid,
+				 hapd->conf->ssid.ssid_len));
+	buf[sizeof(buf) - 1] = '\0';
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_CALLED_STATION_ID) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_ERROR, "Could not add Called-Station-Id");
+		return -1;
+	}
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_PORT_TYPE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type");
+		return -1;
+	}
+
+	if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0)
+		return -1;
+
+	for (attr = req_attr; attr; attr = attr->next) {
+		if (!radius_msg_add_attr(msg, attr->type,
+					 wpabuf_head(attr->val),
+					 wpabuf_len(attr->val))) {
+			wpa_printf(MSG_ERROR, "Could not add RADIUS "
+				   "attribute");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
 static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
 					  struct sta_info *sta,
 					  const u8 *eap, size_t len)
 {
 	struct radius_msg *msg;
-	char buf[128];
 	struct eapol_state_machine *sm = sta->eapol_sm;
 
 	if (sm == NULL)
@@ -442,83 +562,20 @@
 		goto fail;
 	}
 
-	if (hapd->conf->own_ip_addr.af == AF_INET &&
-	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
-				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
-		printf("Could not add NAS-IP-Address\n");
+	if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta,
+				   msg) < 0)
 		goto fail;
-	}
-
-#ifdef CONFIG_IPV6
-	if (hapd->conf->own_ip_addr.af == AF_INET6 &&
-	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
-				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
-		printf("Could not add NAS-IPv6-Address\n");
-		goto fail;
-	}
-#endif /* CONFIG_IPV6 */
-
-	if (hapd->conf->nas_identifier &&
-	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
-				 (u8 *) hapd->conf->nas_identifier,
-				 os_strlen(hapd->conf->nas_identifier))) {
-		printf("Could not add NAS-Identifier\n");
-		goto fail;
-	}
-
-	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
-		printf("Could not add NAS-Port\n");
-		goto fail;
-	}
-
-	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
-		    MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
-	buf[sizeof(buf) - 1] = '\0';
-	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
-				 (u8 *) buf, os_strlen(buf))) {
-		printf("Could not add Called-Station-Id\n");
-		goto fail;
-	}
-
-	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
-		    MAC2STR(sta->addr));
-	buf[sizeof(buf) - 1] = '\0';
-	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
-				 (u8 *) buf, os_strlen(buf))) {
-		printf("Could not add Calling-Station-Id\n");
-		goto fail;
-	}
 
 	/* TODO: should probably check MTU from driver config; 2304 is max for
 	 * IEEE 802.11, but use 1400 to avoid problems with too large packets
 	 */
-	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+					    RADIUS_ATTR_FRAMED_MTU) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
 		printf("Could not add Framed-MTU\n");
 		goto fail;
 	}
 
-	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
-				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
-		printf("Could not add NAS-Port-Type\n");
-		goto fail;
-	}
-
-	if (sta->flags & WLAN_STA_PREAUTH) {
-		os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
-			   sizeof(buf));
-	} else {
-		os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
-			    radius_sta_rate(hapd, sta) / 2,
-			    (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
-			    radius_mode_txt(hapd));
-		buf[sizeof(buf) - 1] = '\0';
-	}
-	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
-				 (u8 *) buf, os_strlen(buf))) {
-		printf("Could not add Connect-Info\n");
-		goto fail;
-	}
-
 	if (eap && !radius_msg_add_eap(msg, eap, len)) {
 		printf("Could not add EAP-Message\n");
 		goto fail;
@@ -541,6 +598,25 @@
 		}
 	}
 
+	if (hapd->conf->radius_request_cui) {
+		const u8 *cui;
+		size_t cui_len;
+		/* Add previously learned CUI or nul CUI to request CUI */
+		if (sm->radius_cui) {
+			cui = wpabuf_head(sm->radius_cui);
+			cui_len = wpabuf_len(sm->radius_cui);
+		} else {
+			cui = (const u8 *) "\0";
+			cui_len = 1;
+		}
+		if (!radius_msg_add_attr(msg,
+					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+					 cui, cui_len)) {
+			wpa_printf(MSG_ERROR, "Could not add CUI");
+			goto fail;
+		}
+	}
+
 	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
 		goto fail;
 
@@ -646,7 +722,8 @@
 			flags |= EAPOL_SM_FROM_PMKSA_CACHE;
 	}
 	return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
-				sta->wps_ie, sta->p2p_ie, sta);
+				sta->wps_ie, sta->p2p_ie, sta,
+				sta->identity, sta->radius_cui);
 }
 
 
@@ -861,12 +938,22 @@
 	if (!force_1x && !hapd->conf->ieee802_1x) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - "
 			   "802.1X not enabled or forced for WPS");
+		/*
+		 * Clear any possible EAPOL authenticator state to support
+		 * reassociation change from WPS to PSK.
+		 */
+		ieee802_1x_free_station(sta);
 		return;
 	}
 
 	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
 	if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
+		/*
+		 * Clear any possible EAPOL authenticator state to support
+		 * reassociation change from WPA-EAP to PSK.
+		 */
+		ieee802_1x_free_station(sta);
 		return;
 	}
 
@@ -968,6 +1055,7 @@
 #ifndef CONFIG_NO_RADIUS
 	radius_msg_free(sm->last_recv_radius);
 	radius_free_class(&sm->radius_class);
+	wpabuf_free(sm->radius_cui);
 #endif /* CONFIG_NO_RADIUS */
 
 	os_free(sm->identity);
@@ -979,9 +1067,8 @@
 static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
 					  struct sta_info *sta)
 {
-	u8 *eap;
-	size_t len;
-	struct eap_hdr *hdr;
+	struct wpabuf *eap;
+	const struct eap_hdr *hdr;
 	int eap_type = -1;
 	char buf[64];
 	struct radius_msg *msg;
@@ -995,7 +1082,7 @@
 
 	msg = sm->last_recv_radius;
 
-	eap = radius_msg_get_eap(msg, &len);
+	eap = radius_msg_get_eap(msg);
 	if (eap == NULL) {
 		/* RFC 3579, Chap. 2.6.3:
 		 * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
@@ -1007,19 +1094,19 @@
 		return;
 	}
 
-	if (len < sizeof(*hdr)) {
+	if (wpabuf_len(eap) < sizeof(*hdr)) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 			       HOSTAPD_LEVEL_WARNING, "too short EAP packet "
 			       "received from authentication server");
-		os_free(eap);
+		wpabuf_free(eap);
 		sm->eap_if->aaaEapNoReq = TRUE;
 		return;
 	}
 
-	if (len > sizeof(*hdr))
-		eap_type = eap[sizeof(*hdr)];
+	if (wpabuf_len(eap) > sizeof(*hdr))
+		eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
 
-	hdr = (struct eap_hdr *) eap;
+	hdr = wpabuf_head(eap);
 	switch (hdr->code) {
 	case EAP_CODE_REQUEST:
 		if (eap_type >= 0)
@@ -1054,7 +1141,7 @@
 	sm->eap_if->aaaEapReq = TRUE;
 
 	wpabuf_free(sm->eap_if->aaaEapReqData);
-	sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len);
+	sm->eap_if->aaaEapReqData = eap;
 }
 
 
@@ -1119,7 +1206,7 @@
 	if (count <= 0)
 		return;
 
-	nclass = os_zalloc(count * sizeof(struct radius_attr_data));
+	nclass = os_calloc(count, sizeof(struct radius_attr_data));
 	if (nclass == NULL)
 		return;
 
@@ -1189,6 +1276,32 @@
 }
 
 
+/* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */
+static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd,
+				      struct sta_info *sta,
+				      struct radius_msg *msg)
+{
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	struct wpabuf *cui;
+	u8 *buf;
+	size_t len;
+
+	if (sm == NULL)
+		return;
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+				    &buf, &len, NULL) < 0)
+		return;
+
+	cui = wpabuf_alloc_copy(buf, len);
+	if (cui == NULL)
+		return;
+
+	wpabuf_free(sm->radius_cui);
+	sm->radius_cui = cui;
+}
+
+
 struct sta_id_search {
 	u8 identifier;
 	struct eapol_state_machine *sm;
@@ -1348,6 +1461,7 @@
 				    shared_secret_len);
 		ieee802_1x_store_radius_class(hapd, sta, msg);
 		ieee802_1x_update_sta_identity(hapd, sta, msg);
+		ieee802_1x_update_sta_cui(hapd, sta, msg);
 		if (sm->eap_if->eapKeyAvailable &&
 		    wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
 				       session_timeout_set ?
@@ -1865,6 +1979,14 @@
 }
 
 
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm)
+{
+	if (sm == NULL)
+		return NULL;
+	return sm->radius_cui;
+}
+
+
 const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
 {
 	*len = 0;
@@ -2100,7 +2222,12 @@
 		 * driver reorder operations.
 		 */
 		os_sleep(0, 10000);
+#ifndef ANDROID_P2P
+		/* We need not do this for driver. For AP-SME flags if we send this disassoc,
+		  * the p2p_client is gettig disassoc after it has completed the assoc
+		  */
 		ap_sta_disconnect(hapd, sta, sta->addr,
 				  WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
+#endif
 	}
 }
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index 58f6084..e1df940 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.1X-2004 Authenticator
- * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,38 +14,8 @@
 struct eapol_state_machine;
 struct hostapd_config;
 struct hostapd_bss_config;
-
-#ifdef _MSC_VER
-#pragma pack(push, 1)
-#endif /* _MSC_VER */
-
-/* RFC 3580, 4. RC4 EAPOL-Key Frame */
-
-struct ieee802_1x_eapol_key {
-	u8 type;
-	u16 key_length;
-	u8 replay_counter[8]; /* does not repeat within the life of the keying
-			       * material used to encrypt the Key field;
-			       * 64-bit NTP timestamp MAY be used here */
-	u8 key_iv[16]; /* cryptographically random number */
-	u8 key_index; /* key flag in the most significant bit:
-		       * 0 = broadcast (default key),
-		       * 1 = unicast (key mapping key); key index is in the
-		       * 7 least significant bits */
-	u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with
-			       * MS-MPPE-Send-Key as the key */
-
-	/* followed by key: if packet body length = 44 + key length, then the
-	 * key field (of key_length bytes) contains the key in encrypted form;
-	 * if packet body length = 44, key field is absent and key_length
-	 * represents the number of least significant octets from
-	 * MS-MPPE-Send-Key attribute to be used as the keying material;
-	 * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
-} STRUCT_PACKED;
-
-#ifdef _MSC_VER
-#pragma pack(pop)
-#endif /* _MSC_VER */
+struct hostapd_radius_attr;
+struct radius_msg;
 
 
 void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
@@ -67,6 +37,7 @@
 u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
 u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
 				 int idx);
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm);
 const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
 void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
 				    int enabled);
@@ -82,4 +53,9 @@
 const char *radius_mode_txt(struct hostapd_data *hapd);
 int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta);
 
+int add_common_radius_attr(struct hostapd_data *hapd,
+			   struct hostapd_radius_attr *req_attr,
+			   struct sta_info *sta,
+			   struct radius_msg *msg);
+
 #endif /* IEEE802_1X_H */
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 9eb4840..ba2c033 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - PMKSA cache for IEEE 802.11i RSN
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -40,6 +40,7 @@
 	if (entry == NULL)
 		return;
 	os_free(entry->identity);
+	wpabuf_free(entry->cui);
 #ifndef CONFIG_NO_RADIUS
 	radius_free_class(&entry->radius_class);
 #endif /* CONFIG_NO_RADIUS */
@@ -136,6 +137,9 @@
 		}
 	}
 
+	if (eapol->radius_cui)
+		entry->cui = wpabuf_dup(eapol->radius_cui);
+
 #ifndef CONFIG_NO_RADIUS
 	radius_copy_class(&entry->radius_class, &eapol->radius_class);
 #endif /* CONFIG_NO_RADIUS */
@@ -163,6 +167,11 @@
 				  eapol->identity, eapol->identity_len);
 	}
 
+	if (entry->cui) {
+		wpabuf_free(eapol->radius_cui);
+		eapol->radius_cui = wpabuf_dup(entry->cui);
+	}
+
 #ifndef CONFIG_NO_RADIUS
 	radius_free_class(&eapol->radius_class);
 	radius_copy_class(&eapol->radius_class, &entry->radius_class);
@@ -299,6 +308,8 @@
 				  old_entry->identity_len);
 		}
 	}
+	if (old_entry->cui)
+		entry->cui = wpabuf_dup(old_entry->cui);
 #ifndef CONFIG_NO_RADIUS
 	radius_copy_class(&entry->radius_class, &old_entry->radius_class);
 #endif /* CONFIG_NO_RADIUS */
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 74b73c4..d473f3f 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -1,6 +1,6 @@
 /*
  * hostapd - PMKSA cache for IEEE 802.11i RSN
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -25,6 +25,7 @@
 
 	u8 *identity;
 	size_t identity_len;
+	struct wpabuf *cui;
 	struct radius_class_data radius_class;
 	u8 eap_type_authsrv;
 	int vlan_id;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index afabdaa..d61177f 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -28,6 +28,7 @@
 #include "vlan_init.h"
 #include "p2p_hostapd.h"
 #include "ap_drv_ops.h"
+#include "gas_serv.h"
 #include "sta_info.h"
 
 static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
@@ -194,6 +195,8 @@
 	if (set_beacon)
 		ieee802_11_set_beacons(hapd->iface);
 
+	wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR,
+		   __func__, MAC2STR(sta->addr));
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
 	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
@@ -218,11 +221,22 @@
 	p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_INTERWORKING
+	if (sta->gas_dialog) {
+		int i;
+		for (i = 0; i < GAS_DIALOG_MAX; i++)
+			gas_serv_dialog_clear(&sta->gas_dialog[i]);
+		os_free(sta->gas_dialog);
+	}
+#endif /* CONFIG_INTERWORKING */
+
 	wpabuf_free(sta->wps_ie);
 	wpabuf_free(sta->p2p_ie);
 
 	os_free(sta->ht_capabilities);
 	os_free(sta->psk);
+	os_free(sta->identity);
+	os_free(sta->radius_cui);
 
 	os_free(sta);
 }
@@ -262,6 +276,9 @@
 	struct sta_info *sta = timeout_ctx;
 	unsigned long next_time = 0;
 
+	wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d",
+		   __func__, MAC2STR(sta->addr), sta->flags,
+		   sta->timeout_next);
 	if (sta->timeout_next == STA_REMOVE) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
@@ -274,6 +291,12 @@
 	    (sta->timeout_next == STA_NULLFUNC ||
 	     sta->timeout_next == STA_DISASSOC)) {
 		int inactive_sec;
+		/*
+		 * Add random value to timeout so that we don't end up bouncing
+		 * all stations at the same time if we have lots of associated
+		 * stations that are idle (but keep re-associating).
+		 */
+		int fuzz = os_random() % 20;
 		inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr);
 		if (inactive_sec == -1) {
 			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
@@ -285,7 +308,7 @@
 			 * Anyway, try again after the next inactivity timeout,
 			 * but do not disconnect the station now.
 			 */
-			next_time = hapd->conf->ap_max_inactivity;
+			next_time = hapd->conf->ap_max_inactivity + fuzz;
 		} else if (inactive_sec < hapd->conf->ap_max_inactivity &&
 			   sta->flags & WLAN_STA_ASSOC) {
 			/* station activity detected; reset timeout state */
@@ -293,7 +316,7 @@
 				"Station " MACSTR " has been active %is ago",
 				MAC2STR(sta->addr), inactive_sec);
 			sta->timeout_next = STA_NULLFUNC;
-			next_time = hapd->conf->ap_max_inactivity -
+			next_time = hapd->conf->ap_max_inactivity + fuzz -
 				inactive_sec;
 		} else {
 			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
@@ -320,6 +343,9 @@
 	}
 
 	if (next_time) {
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%lu seconds)",
+			   __func__, MAC2STR(sta->addr), next_time);
 		eloop_register_timeout(next_time, 0, ap_handle_timer, hapd,
 				       sta);
 		return;
@@ -353,6 +379,9 @@
 	switch (sta->timeout_next) {
 	case STA_NULLFUNC:
 		sta->timeout_next = STA_DISASSOC;
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%d seconds - AP_DISASSOC_DELAY)",
+			   __func__, MAC2STR(sta->addr), AP_DISASSOC_DELAY);
 		eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer,
 				       hapd, sta);
 		break;
@@ -369,6 +398,9 @@
 			       HOSTAPD_LEVEL_INFO, "disassociated due to "
 			       "inactivity");
 		sta->timeout_next = STA_DEAUTH;
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)",
+			   __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY);
 		eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
 				       hapd, sta);
 		mlme_disassociate_indication(
@@ -397,8 +429,14 @@
 	struct sta_info *sta = timeout_ctx;
 	u8 addr[ETH_ALEN];
 
-	if (!(sta->flags & WLAN_STA_AUTH))
+	if (!(sta->flags & WLAN_STA_AUTH)) {
+		if (sta->flags & WLAN_STA_GAS) {
+			wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA "
+				   "entry " MACSTR, MAC2STR(sta->addr));
+			ap_free_sta(hapd, sta);
+		}
 		return;
+	}
 
 	mlme_deauthenticate_indication(hapd, sta,
 				       WLAN_REASON_PREV_AUTH_NOT_VALID);
@@ -455,6 +493,10 @@
 	sta->acct_interim_interval = hapd->conf->acct_interim_interval;
 
 	/* initialize STA info data */
+	wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - ap_max_inactivity)",
+		   __func__, MAC2STR(addr),
+		   hapd->conf->ap_max_inactivity);
 	eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
 			       ap_handle_timer, hapd, sta);
 	os_memcpy(sta->addr, addr, ETH_ALEN);
@@ -528,6 +570,11 @@
 	sta->flags &= ~WLAN_STA_ASSOC;
 	ap_sta_set_authorized(hapd, sta, 0);
 	sta->timeout_next = STA_DEAUTH;
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
+		   __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DISASSOC);
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
 			       ap_handle_timer, hapd, sta);
@@ -561,6 +608,11 @@
 	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
 	ap_sta_set_authorized(hapd, sta, 0);
 	sta->timeout_next = STA_REMOVE;
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+		   __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DEAUTH);
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
 			       ap_handle_timer, hapd, sta);
@@ -576,6 +628,23 @@
 }
 
 
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+		      struct sta_info *sta, void *ctx)
+{
+	if (sta && (sta->flags & WLAN_STA_WPS)) {
+		ap_sta_deauthenticate(hapd, sta,
+				      WLAN_REASON_PREV_AUTH_NOT_VALID);
+		wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR,
+			   __func__, MAC2STR(sta->addr));
+		return 1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_WPS */
+
+
 int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
 		     int old_vlanid)
 {
@@ -728,8 +797,9 @@
 	    ap_check_sa_query_timeout(hapd, sta))
 		return;
 
-	nbuf = os_realloc(sta->sa_query_trans_id,
-			  (sta->sa_query_count + 1) * WLAN_SA_QUERY_TR_ID_LEN);
+	nbuf = os_realloc_array(sta->sa_query_trans_id,
+				sta->sa_query_count + 1,
+				WLAN_SA_QUERY_TR_ID_LEN);
 	if (nbuf == NULL)
 		return;
 	if (sta->sa_query_count == 0) {
@@ -751,9 +821,7 @@
 		       HOSTAPD_LEVEL_DEBUG,
 		       "association SA Query attempt %d", sta->sa_query_count);
 
-#ifdef NEED_AP_MLME
 	ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id);
-#endif /* NEED_AP_MLME */
 }
 
 
@@ -778,11 +846,20 @@
 			   int authorized)
 {
 	const u8 *dev_addr = NULL;
+#ifdef CONFIG_P2P
+	u8 addr[ETH_ALEN];
+#endif /* CONFIG_P2P */
+
 	if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
 		return;
 
 #ifdef CONFIG_P2P
-	dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
+	if (hapd->p2p_group == NULL) {
+		if (sta->p2p_ie != NULL &&
+		    p2p_parse_dev_addr_in_p2p_ie(sta->p2p_ie, addr) == 0)
+			dev_addr = addr;
+	} else
+		dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
 #endif /* CONFIG_P2P */
 
 	if (authorized) {
@@ -848,6 +925,11 @@
 	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
 	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
 	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+		   __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DEAUTH);
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
 			       ap_handle_timer, hapd, sta);
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 60b3a7b..b3c57b4 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -27,6 +27,8 @@
 #define WLAN_STA_WDS BIT(14)
 #define WLAN_STA_ASSOC_REQ_OK BIT(15)
 #define WLAN_STA_WPS2 BIT(16)
+#define WLAN_STA_GAS BIT(17)
+#define WLAN_STA_VHT BIT(18)
 #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
 #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
 #define WLAN_STA_NONERP BIT(31)
@@ -95,7 +97,11 @@
 	int vlan_id;
 	u8 *psk; /* PSK from RADIUS authentication server */
 
+	char *identity; /* User-Name from RADIUS */
+	char *radius_cui; /* Chargeable-User-Identity from RADIUS */
+
 	struct ieee80211_ht_capabilities *ht_capabilities;
+	struct ieee80211_vht_capabilities *vht_capabilities;
 
 #ifdef CONFIG_IEEE80211W
 	int sa_query_count; /* number of pending SA Query requests;
@@ -107,6 +113,12 @@
 	struct os_time sa_query_start;
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_INTERWORKING
+#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
+	struct gas_dialog_info *gas_dialog;
+	u8 gas_dialog_next;
+#endif /* CONFIG_INTERWORKING */
+
 	struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
 	struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
 };
@@ -137,7 +149,6 @@
 struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
 void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
-void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
 void hostapd_free_stas(struct hostapd_data *hapd);
 void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
 void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
@@ -149,6 +160,10 @@
 			 u16 reason);
 void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
 			   u16 reason);
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+		      struct sta_info *sta, void *ctx);
+#endif /* CONFIG_WPS */
 int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
 		     int old_vlanid);
 void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
index 60088ee..dd5aa68 100644
--- a/src/ap/tkip_countermeasures.c
+++ b/src/ap/tkip_countermeasures.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / TKIP countermeasures
- * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
 #include "hostapd.h"
 #include "sta_info.h"
 #include "ap_mlme.h"
@@ -44,12 +45,17 @@
 	eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
 	eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
 			       hapd, NULL);
-	for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
+	while ((sta = hapd->sta_list)) {
+		sta->acct_terminate_cause =
+			RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET;
+		if (sta->flags & WLAN_STA_AUTH) {
+			mlme_deauthenticate_indication(
+				hapd, sta,
+				WLAN_REASON_MICHAEL_MIC_FAILURE);
+		}
 		hostapd_drv_sta_deauth(hapd, sta->addr,
 				       WLAN_REASON_MICHAEL_MIC_FAILURE);
-		ap_sta_set_authorized(hapd, sta, 0);
-		sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
-		hostapd_drv_sta_remove(hapd, sta->addr);
+		ap_free_sta(hapd, sta);
 	}
 }
 
diff --git a/src/ap/utils.c b/src/ap/utils.c
index 36c1182..931968c 100644
--- a/src/ap/utils.c
+++ b/src/ap/utils.c
@@ -17,13 +17,14 @@
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
 				 int (*cb)(void *ctx, const u8 *sa,
 					   const u8 *da, const u8 *bssid,
-					   const u8 *ie, size_t ie_len),
+					   const u8 *ie, size_t ie_len,
+					   int ssi_signal),
 				 void *ctx)
 {
 	struct hostapd_probereq_cb *n;
 
-	n = os_realloc(hapd->probereq_cb, (hapd->num_probereq_cb + 1) *
-		       sizeof(struct hostapd_probereq_cb));
+	n = os_realloc_array(hapd->probereq_cb, hapd->num_probereq_cb + 1,
+			     sizeof(struct hostapd_probereq_cb));
 	if (n == NULL)
 		return -1;
 
@@ -77,7 +78,8 @@
 	struct prune_data data;
 	data.hapd = hapd;
 	data.addr = addr;
-	if (hapd->iface->for_each_interface)
-		hapd->iface->for_each_interface(hapd->iface->interfaces,
-						prune_associations, &data);
+	if (hapd->iface->interfaces &&
+	    hapd->iface->interfaces->for_each_interface)
+		hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, prune_associations, &data);
 }
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index f2f766f..7b1a9e6 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -21,6 +21,7 @@
 #include "ap_config.h"
 #include "ap_drv_ops.h"
 #include "vlan_init.h"
+#include "vlan_util.h"
 
 
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
@@ -335,7 +336,9 @@
 }
 
 
-static int vlan_rem(const char *if_name)
+#ifndef CONFIG_VLAN_NETLINK
+
+int vlan_rem(const char *if_name)
 {
 	int fd;
 	struct vlan_ioctl_args if_request;
@@ -378,7 +381,7 @@
 	returns 1 if the interface already exists
 	returns 0 otherwise
 */
-static int vlan_add(const char *if_name, int vid)
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
 {
 	int fd;
 	struct vlan_ioctl_args if_request;
@@ -474,6 +477,8 @@
 	return 0;
 }
 
+#endif /* CONFIG_VLAN_NETLINK */
+
 
 static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
 {
@@ -481,6 +486,7 @@
 	char br_name[IFNAMSIZ];
 	struct hostapd_vlan *vlan = hapd->conf->vlan;
 	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+	int vlan_naming = hapd->conf->ssid.vlan_naming;
 
 	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
 
@@ -496,13 +502,22 @@
 			ifconfig_up(br_name);
 
 			if (tagged_interface) {
+				if (vlan_naming ==
+				    DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+					os_snprintf(vlan_ifname,
+						    sizeof(vlan_ifname),
+						    "%s.%d", tagged_interface,
+						    vlan->vlan_id);
+				else
+					os_snprintf(vlan_ifname,
+						    sizeof(vlan_ifname),
+						    "vlan%d", vlan->vlan_id);
 
-				if (!vlan_add(tagged_interface, vlan->vlan_id))
+				ifconfig_up(tagged_interface);
+				if (!vlan_add(tagged_interface, vlan->vlan_id,
+					      vlan_ifname))
 					vlan->clean |= DVLAN_CLEAN_VLAN;
 
-				os_snprintf(vlan_ifname, sizeof(vlan_ifname),
-					    "vlan%d", vlan->vlan_id);
-
 				if (!br_addif(br_name, vlan_ifname))
 					vlan->clean |= DVLAN_CLEAN_VLAN_PORT;
 
@@ -527,6 +542,7 @@
 	char br_name[IFNAMSIZ];
 	struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
 	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+	int vlan_naming = hapd->conf->ssid.vlan_naming;
 
 	wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
 
@@ -541,8 +557,16 @@
 				br_delif(br_name, vlan->ifname);
 
 			if (tagged_interface) {
-				os_snprintf(vlan_ifname, sizeof(vlan_ifname),
-					    "vlan%d", vlan->vlan_id);
+				if (vlan_naming ==
+				    DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+					os_snprintf(vlan_ifname,
+						    sizeof(vlan_ifname),
+						    "%s.%d", tagged_interface,
+						    vlan->vlan_id);
+				else
+					os_snprintf(vlan_ifname,
+						    sizeof(vlan_ifname),
+						    "vlan%d", vlan->vlan_id);
 				if (vlan->clean & DVLAN_CLEAN_VLAN_PORT)
 					br_delif(br_name, vlan_ifname);
 				ifconfig_down(vlan_ifname);
@@ -682,7 +706,12 @@
 	if (priv == NULL)
 		return NULL;
 
-	vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+#ifndef CONFIG_VLAN_NETLINK
+	vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
+			   DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
+			   VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
+			   VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+#endif /* CONFIG_VLAN_NETLINK */
 
 	priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
 	if (priv->s < 0) {
diff --git a/src/ap/vlan_util.c b/src/ap/vlan_util.c
new file mode 100644
index 0000000..cc54051
--- /dev/null
+++ b/src/ap/vlan_util.c
@@ -0,0 +1,177 @@
+/*
+ * hostapd / VLAN netlink api
+ * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/if_vlan.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/vlan.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "vlan_util.h"
+
+/*
+ * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and
+ * tagged interface 'if_name'.
+ *
+ * returns -1 on error
+ * returns 1 if the interface already exists
+ * returns 0 otherwise
+*/
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
+{
+	int ret = -1;
+	struct nl_sock *handle = NULL;
+	struct nl_cache *cache = NULL;
+	struct rtnl_link *rlink = NULL;
+	int if_idx = 0;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, "
+		   "vlan_if_name=%s)", if_name, vid, vlan_if_name);
+
+	if ((os_strlen(if_name) + 1) > IFNAMSIZ) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   if_name);
+		return -1;
+	}
+
+	if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   vlan_if_name);
+		return -1;
+	}
+
+	handle = nl_socket_alloc();
+	if (!handle) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
+		goto vlan_add_error;
+	}
+
+	if (nl_connect(handle, NETLINK_ROUTE) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
+		goto vlan_add_error;
+	}
+
+	if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
+		cache = NULL;
+		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
+		goto vlan_add_error;
+	}
+
+	if (!(if_idx = rtnl_link_name2i(cache, if_name))) {
+		/* link does not exist */
+		wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist",
+			   if_name);
+		goto vlan_add_error;
+	}
+
+	if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) {
+		/* link does exist */
+		rtnl_link_put(rlink);
+		rlink = NULL;
+		wpa_printf(MSG_ERROR, "VLAN: interface %s already exists",
+			   vlan_if_name);
+		ret = 1;
+		goto vlan_add_error;
+	}
+
+	rlink = rtnl_link_alloc();
+	if (!rlink) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link");
+		goto vlan_add_error;
+	}
+
+	if (rtnl_link_set_type(rlink, "vlan") < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to set link type");
+		goto vlan_add_error;
+	}
+
+	rtnl_link_set_link(rlink, if_idx);
+	rtnl_link_set_name(rlink, vlan_if_name);
+
+	if (rtnl_link_vlan_set_id(rlink, vid) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id");
+		goto vlan_add_error;
+	}
+
+	if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for "
+			   "vlan %d on %s (%d)",
+			   vlan_if_name, vid, if_name, if_idx);
+		goto vlan_add_error;
+	}
+
+	ret = 0;
+
+vlan_add_error:
+	if (rlink)
+		rtnl_link_put(rlink);
+	if (cache)
+		nl_cache_free(cache);
+	if (handle)
+		nl_socket_free(handle);
+	return ret;
+}
+
+
+int vlan_rem(const char *if_name)
+{
+	int ret = -1;
+	struct nl_sock *handle = NULL;
+	struct nl_cache *cache = NULL;
+	struct rtnl_link *rlink = NULL;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name);
+
+	handle = nl_socket_alloc();
+	if (!handle) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
+		goto vlan_rem_error;
+	}
+
+	if (nl_connect(handle, NETLINK_ROUTE) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
+		goto vlan_rem_error;
+	}
+
+	if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
+		cache = NULL;
+		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
+		goto vlan_rem_error;
+	}
+
+	if (!(rlink = rtnl_link_get_by_name(cache, if_name))) {
+		/* link does not exist */
+		wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists",
+			   if_name);
+		goto vlan_rem_error;
+	}
+
+	if (rtnl_link_delete(handle, rlink) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s",
+			   if_name);
+		goto vlan_rem_error;
+	}
+
+	ret = 0;
+
+vlan_rem_error:
+	if (rlink)
+		rtnl_link_put(rlink);
+	if (cache)
+		nl_cache_free(cache);
+	if (handle)
+		nl_socket_free(handle);
+	return ret;
+}
diff --git a/src/ap/vlan_util.h b/src/ap/vlan_util.h
new file mode 100644
index 0000000..bef5a16
--- /dev/null
+++ b/src/ap/vlan_util.h
@@ -0,0 +1,15 @@
+/*
+ * hostapd / VLAN netlink api
+ * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef VLAN_UTIL_H
+#define VLAN_UTIL_H
+
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name);
+int vlan_rem(const char *if_name);
+
+#endif /* VLAN_UTIL_H */
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
new file mode 100644
index 0000000..2594404
--- /dev/null
+++ b/src/ap/wnm_ap.c
@@ -0,0 +1,258 @@
+/*
+ * hostapd - WNM
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#include "ap/ap_config.h"
+#include "ap/ap_drv_ops.h"
+#include "ap/wpa_auth.h"
+#include "wnm_ap.h"
+
+#define MAX_TFS_IE_LEN  1024
+
+#ifdef CONFIG_IEEE80211V
+
+/* get the TFS IE from driver */
+static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
+				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
+
+	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
+}
+
+
+/* set the TFS IE to driver */
+static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
+				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
+
+	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
+}
+
+
+/* MLME-SLEEPMODE.response */
+static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
+					 const u8 *addr, u8 dialog_token,
+					 u8 action_type, u16 intval)
+{
+	struct ieee80211_mgmt *mgmt;
+	int res;
+	size_t len;
+	size_t gtk_elem_len = 0;
+	size_t igtk_elem_len = 0;
+	struct wnm_sleep_element wnmsleep_ie;
+	u8 *wnmtfs_ie;
+	u8 wnmsleep_ie_len;
+	u16 wnmtfs_ie_len;
+	u8 *pos;
+	struct sta_info *sta;
+	enum wnm_oper tfs_oper = action_type == 0 ? WNM_SLEEP_TFS_RESP_IE_ADD :
+		WNM_SLEEP_TFS_RESP_IE_NONE;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+		return -EINVAL;
+	}
+
+	/* WNM-Sleep Mode IE */
+	os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element));
+	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
+	wnmsleep_ie.eid = WLAN_EID_WNMSLEEP;
+	wnmsleep_ie.len = wnmsleep_ie_len - 2;
+	wnmsleep_ie.action_type = action_type;
+	wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
+	wnmsleep_ie.intval = intval;
+
+	/* TFS IE(s) */
+	wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
+	if (wnmtfs_ie == NULL)
+		return -1;
+	if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len,
+				    tfs_oper)) {
+		wnmtfs_ie_len = 0;
+		os_free(wnmtfs_ie);
+		wnmtfs_ie = NULL;
+	}
+
+#define MAX_GTK_SUBELEM_LEN 45
+#define MAX_IGTK_SUBELEM_LEN 26
+	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
+			 MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN);
+	if (mgmt == NULL) {
+		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+			   "WNM-Sleep Response action frame");
+		return -1;
+	}
+	os_memcpy(mgmt->da, addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP;
+	mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
+	pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
+	/* add key data if MFP is enabled */
+	if (wpa_auth_uses_mfp(sta->wpa_sm) || action_type != 1){
+		mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
+	} else {
+		gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
+		pos += gtk_elem_len;
+		wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
+			   (int) gtk_elem_len);
+#ifdef CONFIG_IEEE80211W
+		res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
+		if (res < 0) {
+			os_free(wnmtfs_ie);
+			os_free(mgmt);
+			return -1;
+		}
+		igtk_elem_len = res;
+		pos += igtk_elem_len;
+		wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
+			   (int) igtk_elem_len);
+#endif /* CONFIG_IEEE80211W */
+
+		WPA_PUT_LE16((u8 *)
+			     &mgmt->u.action.u.wnm_sleep_resp.keydata_len,
+			     gtk_elem_len + igtk_elem_len);
+	}
+	os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
+	/* copy TFS IE here */
+	pos += wnmsleep_ie_len;
+	os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
+
+	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
+		igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len;
+
+	/* In driver, response frame should be forced to sent when STA is in
+	 * PS mode */
+	res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+				      mgmt->da, &mgmt->u.action.category, len);
+
+	if (!res) {
+		wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response "
+			   "frame");
+
+		/* when entering wnmsleep
+		 * 1. pause the node in driver
+		 * 2. mark the node so that AP won't update GTK/IGTK during
+		 * WNM Sleep
+		 */
+		if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
+		    wnmsleep_ie.action_type == 0) {
+			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
+					     addr, NULL, NULL);
+			wpa_set_wnmsleep(sta->wpa_sm, 1);
+		}
+		/* when exiting wnmsleep
+		 * 1. unmark the node
+		 * 2. start GTK/IGTK update if MFP is not used
+		 * 3. unpause the node in driver
+		 */
+		if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
+		    wnmsleep_ie.action_type == 1) {
+			wpa_set_wnmsleep(sta->wpa_sm, 0);
+			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
+					     addr, NULL, NULL);
+			if (wpa_auth_uses_mfp(sta->wpa_sm) && action_type == 1)
+				wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
+		}
+	} else
+		wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame");
+
+#undef MAX_GTK_SUBELEM_LEN
+#undef MAX_IGTK_SUBELEM_LEN
+	os_free(wnmtfs_ie);
+	os_free(mgmt);
+	return res;
+}
+
+
+static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
+				       const u8 *addr, const u8 *frm, int len)
+{
+	/*
+	 * Action [1] | Dialog Token [1] | WNM-Sleep Mode IE |
+	 * TFS Response IE
+	 */
+	u8 *pos = (u8 *) frm; /* point to action field */
+	u8 dialog_token = pos[1];
+	struct wnm_sleep_element *wnmsleep_ie = NULL;
+	/* multiple TFS Req IE (assuming consecutive) */
+	u8 *tfsreq_ie_start = NULL;
+	u8 *tfsreq_ie_end = NULL;
+	u16 tfsreq_ie_len = 0;
+
+	pos += 1 + 1;
+	while (pos - frm < len - 1) {
+		u8 ie_len = *(pos+1);
+		if (*pos == WLAN_EID_WNMSLEEP)
+			wnmsleep_ie = (struct wnm_sleep_element *)pos;
+		else if (*pos == WLAN_EID_TFS_REQ) {
+			if (!tfsreq_ie_start)
+				tfsreq_ie_start = pos;
+			tfsreq_ie_end = pos;
+		} else
+			wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
+		pos += ie_len + 2;
+	}
+
+	if (!wnmsleep_ie) {
+		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
+		return;
+	}
+
+	if (wnmsleep_ie->action_type == 0 && tfsreq_ie_start &&
+	    tfsreq_ie_end && tfsreq_ie_end - tfsreq_ie_start >= 0) {
+		tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
+			tfsreq_ie_start;
+		wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
+		/* pass the TFS Req IE(s) to driver for processing */
+		if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
+					    &tfsreq_ie_len,
+					    WNM_SLEEP_TFS_REQ_IE_SET))
+			wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE");
+	}
+
+	ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
+				      wnmsleep_ie->action_type,
+				      wnmsleep_ie->intval);
+
+	if (wnmsleep_ie->action_type == 1) {
+		/* clear the tfs after sending the resp frame */
+		ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
+					&tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
+	}
+}
+
+
+void ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+				 struct rx_action *action)
+{
+	u8 *pos = (u8 *) action->data + 1; /* point to the action field */
+	u8 act = *pos;
+
+	switch (act) {
+	case WNM_SLEEP_MODE_REQ:
+		ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1,
+					   action->len);
+		break;
+	default:
+		break;
+	}
+}
+
+#endif /* CONFIG_IEEE80211V */
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
new file mode 100644
index 0000000..ab7c4f1
--- /dev/null
+++ b/src/ap/wnm_ap.h
@@ -0,0 +1,17 @@
+/*
+ * IEEE 802.11v WNM related functions and structures
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WNM_AP_H
+#define WNM_AP_H
+
+struct rx_action;
+
+void ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+				 struct rx_action *action);
+
+#endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index d1b9b4d..49d8175 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -278,25 +278,6 @@
 }
 
 
-static void wpa_group_set_key_len(struct wpa_group *group, int cipher)
-{
-	switch (cipher) {
-	case WPA_CIPHER_CCMP:
-		group->GTK_len = 16;
-		break;
-	case WPA_CIPHER_TKIP:
-		group->GTK_len = 32;
-		break;
-	case WPA_CIPHER_WEP104:
-		group->GTK_len = 13;
-		break;
-	case WPA_CIPHER_WEP40:
-		group->GTK_len = 5;
-		break;
-	}
-}
-
-
 static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
 					  struct wpa_group *group)
 {
@@ -338,8 +319,7 @@
 
 	group->GTKAuthenticator = TRUE;
 	group->vlan_id = vlan_id;
-
-	wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+	group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
 
 	if (random_pool_ready() != 1) {
 		wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
@@ -514,7 +494,7 @@
 	 * configuration.
 	 */
 	group = wpa_auth->group;
-	wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+	group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
 	group->GInit = TRUE;
 	wpa_group_sm_step(wpa_auth, group);
 	group->GInit = FALSE;
@@ -849,7 +829,8 @@
 	if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
 	    msg == GROUP_2) {
 		u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
-		if (sm->pairwise == WPA_CIPHER_CCMP) {
+		if (sm->pairwise == WPA_CIPHER_CCMP ||
+		    sm->pairwise == WPA_CIPHER_GCMP) {
 			if (wpa_use_aes_cmac(sm) &&
 			    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
 				wpa_auth_logger(wpa_auth, sm->addr,
@@ -865,7 +846,7 @@
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
 						"did not use HMAC-SHA1-AES "
-						"with CCMP");
+						"with CCMP/GCMP");
 				return;
 			}
 		}
@@ -1240,7 +1221,7 @@
 		version = force_version;
 	else if (wpa_use_aes_cmac(sm))
 		version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
-	else if (sm->pairwise == WPA_CIPHER_CCMP)
+	else if (sm->pairwise != WPA_CIPHER_TKIP)
 		version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 	else
 		version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -1287,20 +1268,7 @@
 	WPA_PUT_BE16(key->key_info, key_info);
 
 	alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
-	switch (alg) {
-	case WPA_CIPHER_CCMP:
-		WPA_PUT_BE16(key->key_length, 16);
-		break;
-	case WPA_CIPHER_TKIP:
-		WPA_PUT_BE16(key->key_length, 32);
-		break;
-	case WPA_CIPHER_WEP40:
-		WPA_PUT_BE16(key->key_length, 5);
-		break;
-	case WPA_CIPHER_WEP104:
-		WPA_PUT_BE16(key->key_length, 13);
-		break;
-	}
+	WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
 	if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
 		WPA_PUT_BE16(key->key_length, 0);
 
@@ -1533,22 +1501,6 @@
 }
 
 
-static enum wpa_alg wpa_alg_enum(int alg)
-{
-	switch (alg) {
-	case WPA_CIPHER_CCMP:
-		return WPA_ALG_CCMP;
-	case WPA_CIPHER_TKIP:
-		return WPA_ALG_TKIP;
-	case WPA_CIPHER_WEP104:
-	case WPA_CIPHER_WEP40:
-		return WPA_ALG_WEP;
-	default:
-		return WPA_ALG_NONE;
-	}
-}
-
-
 SM_STATE(WPA_PTK, INITIALIZE)
 {
 	SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk);
@@ -1641,10 +1593,23 @@
 
 	wpa_group_ensure_init(sm->wpa_auth, sm->group);
 
-	os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN);
+	/*
+	 * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat
+	 * ambiguous. The Authenticator state machine uses a counter that is
+	 * incremented by one for each 4-way handshake. However, the security
+	 * analysis of 4-way handshake points out that unpredictable nonces
+	 * help in preventing precomputation attacks. Instead of the state
+	 * machine definition, use an unpredictable nonce value here to provide
+	 * stronger protection against potential precomputation attacks.
+	 */
+	if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_ERROR, "WPA: Failed to get random data for "
+			   "ANonce.");
+		wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+		return;
+	}
 	wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
 		    WPA_NONCE_LEN);
-	inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
 	sm->ReAuthenticationRequest = FALSE;
 	/* IEEE 802.11i does not clear TimeoutCtr here, but this is more
 	 * logical place than INITIALIZE since AUTHENTICATION2 can be
@@ -1760,7 +1725,7 @@
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk,
 			  struct wpa_ptk *ptk)
 {
-	size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64;
+	size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64;
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 		return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len);
@@ -1885,6 +1850,14 @@
 	    wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0)
 		os_memset(igtk.pn, 0, sizeof(igtk.pn));
 	os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+	if (sm->wpa_auth->conf.disable_gtk) {
+		/*
+		 * Provide unique random IGTK to each STA to prevent use of
+		 * IGTK in the BSS.
+		 */
+		if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0)
+			return pos;
+	}
 	pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
 			  (const u8 *) &igtk, sizeof(igtk), NULL, 0);
 
@@ -1909,7 +1882,7 @@
 
 SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 {
-	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32];
 	size_t gtk_len, kde_len;
 	struct wpa_group *gsm = sm->group;
 	u8 *wpa_ie;
@@ -1947,6 +1920,15 @@
 		secure = 1;
 		gtk = gsm->GTK[gsm->GN - 1];
 		gtk_len = gsm->GTK_len;
+		if (sm->wpa_auth->conf.disable_gtk) {
+			/*
+			 * Provide unique random GTK to each STA to prevent use
+			 * of GTK in the BSS.
+			 */
+			if (random_get_bytes(dummy_gtk, gtk_len) < 0)
+				return;
+			gtk = dummy_gtk;
+		}
 		keyidx = gsm->GN;
 		_rsc = rsc;
 		encr = 1;
@@ -2058,15 +2040,8 @@
 	SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk);
 	sm->EAPOLKeyReceived = FALSE;
 	if (sm->Pair) {
-		enum wpa_alg alg;
-		int klen;
-		if (sm->pairwise == WPA_CIPHER_TKIP) {
-			alg = WPA_ALG_TKIP;
-			klen = 32;
-		} else {
-			alg = WPA_ALG_CCMP;
-			klen = 16;
-		}
+		enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
+		int klen = wpa_cipher_key_len(sm->pairwise);
 		if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
 				     sm->PTK.tk1, klen)) {
 			wpa_sta_disconnect(sm->wpa_auth, sm->addr);
@@ -2243,6 +2218,7 @@
 	struct wpa_group *gsm = sm->group;
 	u8 *kde, *pos, hdr[2];
 	size_t kde_len;
+	u8 *gtk, dummy_gtk[32];
 
 	SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
 
@@ -2263,6 +2239,16 @@
 	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 			"sending 1/2 msg of Group Key Handshake");
 
+	gtk = gsm->GTK[gsm->GN - 1];
+	if (sm->wpa_auth->conf.disable_gtk) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use
+		 * of GTK in the BSS.
+		 */
+		if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0)
+			return;
+		gtk = dummy_gtk;
+	}
 	if (sm->wpa == WPA_VERSION_WPA2) {
 		kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
 			ieee80211w_kde_len(sm);
@@ -2274,10 +2260,10 @@
 		hdr[0] = gsm->GN & 0x03;
 		hdr[1] = 0;
 		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
-				  gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+				  gtk, gsm->GTK_len);
 		pos = ieee80211w_kde_add(sm, pos);
 	} else {
-		kde = gsm->GTK[gsm->GN - 1];
+		kde = gtk;
 		pos = kde + gsm->GTK_len;
 	}
 
@@ -2403,6 +2389,9 @@
 
 static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
 {
+	if (ctx != NULL && ctx != sm->group)
+		return 0;
+
 	if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
 		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 				"Not in PTKINITDONE; skip Group Key update");
@@ -2420,6 +2409,12 @@
 				"marking station for GTK rekeying");
 	}
 
+#ifdef CONFIG_IEEE80211V
+	/* Do not rekey GTK/IGTK when STA is in wnmsleep */
+	if (sm->is_wnmsleep)
+		return 0;
+#endif /* CONFIG_IEEE80211V */
+
 	sm->group->GKeyDoneStations++;
 	sm->GUpdateStationKeys = TRUE;
 
@@ -2428,6 +2423,132 @@
 }
 
 
+#ifdef CONFIG_IEEE80211V
+/* update GTK when exiting wnmsleep mode */
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
+{
+	if (sm->is_wnmsleep)
+		return;
+
+	wpa_group_update_sta(sm, NULL);
+}
+
+
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
+{
+	sm->is_wnmsleep = !!flag;
+}
+
+
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+	u8 *subelem;
+	struct wpa_group *gsm = sm->group;
+	size_t subelem_len, pad_len;
+	const u8 *key;
+	size_t key_len;
+	u8 keybuf[32];
+
+	/* GTK subslement */
+	key_len = gsm->GTK_len;
+	if (key_len > sizeof(keybuf))
+		return 0;
+
+	/*
+	 * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
+	 * than 16 bytes.
+	 */
+	pad_len = key_len % 8;
+	if (pad_len)
+		pad_len = 8 - pad_len;
+	if (key_len + pad_len < 16)
+		pad_len += 8;
+	if (pad_len) {
+		os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
+		os_memset(keybuf + key_len, 0, pad_len);
+		keybuf[key_len] = 0xdd;
+		key_len += pad_len;
+		key = keybuf;
+	} else
+		key = gsm->GTK[gsm->GN - 1];
+
+	/*
+	 * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
+	 * Key[5..32] | 8 padding.
+	 */
+	subelem_len = 13 + key_len + 8;
+	subelem = os_zalloc(subelem_len);
+	if (subelem == NULL)
+		return 0;
+
+	subelem[0] = WNM_SLEEP_SUBELEM_GTK;
+	subelem[1] = 11 + key_len + 8;
+	/* Key ID in B0-B1 of Key Info */
+	WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
+	subelem[4] = gsm->GTK_len;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5) != 0)
+	{
+		os_free(subelem);
+		return 0;
+	}
+	if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) {
+		os_free(subelem);
+		return 0;
+	}
+
+	os_memcpy(pos, subelem, subelem_len);
+
+	wpa_hexdump_key(MSG_DEBUG, "Plaintext GTK",
+			gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+	os_free(subelem);
+
+	return subelem_len;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+	u8 *subelem, *ptr;
+	struct wpa_group *gsm = sm->group;
+	size_t subelem_len;
+
+	/* IGTK subelement
+	 * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] |
+	 * Key[16] | 8 padding */
+	subelem_len = 1 + 1 + 2 + 6 + WPA_IGTK_LEN + 8;
+	subelem = os_zalloc(subelem_len);
+	if (subelem == NULL)
+		return 0;
+
+	ptr = subelem;
+	*ptr++ = WNM_SLEEP_SUBELEM_IGTK;
+	*ptr++ = subelem_len - 2;
+	WPA_PUT_LE16(ptr, gsm->GN_igtk);
+	ptr += 2;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, ptr) != 0) {
+		os_free(subelem);
+		return 0;
+	}
+	ptr += 6;
+	if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8,
+		     gsm->IGTK[gsm->GN_igtk - 4], ptr)) {
+		os_free(subelem);
+		return -1;
+	}
+
+	os_memcpy(pos, subelem, subelem_len);
+
+	wpa_hexdump_key(MSG_DEBUG, "Plaintext IGTK",
+			gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+	os_free(subelem);
+
+	return subelem_len;
+}
+#endif /* CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211V */
+
+
 static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
 			      struct wpa_group *group)
 {
@@ -2457,7 +2578,7 @@
 			   group->GKeyDoneStations);
 		group->GKeyDoneStations = 0;
 	}
-	wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL);
+	wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
 	wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
 		   group->GKeyDoneStations);
 }
@@ -2469,7 +2590,7 @@
 	int ret = 0;
 
 	if (wpa_auth_set_key(wpa_auth, group->vlan_id,
-			     wpa_alg_enum(wpa_auth->conf.wpa_group),
+			     wpa_cipher_to_alg(wpa_auth->conf.wpa_group),
 			     broadcast_ether_addr, group->GN,
 			     group->GTK[group->GN - 1], group->GTK_len) < 0)
 		ret = -1;
@@ -2609,23 +2730,6 @@
 }
 
 
-static int wpa_cipher_bits(int cipher)
-{
-	switch (cipher) {
-	case WPA_CIPHER_CCMP:
-		return 128;
-	case WPA_CIPHER_TKIP:
-		return 256;
-	case WPA_CIPHER_WEP104:
-		return 104;
-	case WPA_CIPHER_WEP40:
-		return 40;
-	default:
-		return 0;
-	}
-}
-
-
 #define RSN_SUITE "%02x-%02x-%02x-%d"
 #define RSN_SUITE_ARG(s) \
 ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
@@ -2688,7 +2792,7 @@
 		!!wpa_auth->conf.wpa_strict_rekey,
 		dot11RSNAConfigGroupUpdateCount,
 		dot11RSNAConfigPairwiseUpdateCount,
-		wpa_cipher_bits(wpa_auth->conf.wpa_group),
+		wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8,
 		dot11RSNAConfigPMKLifetime,
 		dot11RSNAConfigPMKReauthThreshold,
 		dot11RSNAConfigSATimeout,
@@ -2731,29 +2835,10 @@
 
 	/* dot11RSNAStatsEntry */
 
-	if (sm->wpa == WPA_VERSION_WPA) {
-		if (sm->pairwise == WPA_CIPHER_CCMP)
-			pairwise = WPA_CIPHER_SUITE_CCMP;
-		else if (sm->pairwise == WPA_CIPHER_TKIP)
-			pairwise = WPA_CIPHER_SUITE_TKIP;
-		else if (sm->pairwise == WPA_CIPHER_WEP104)
-			pairwise = WPA_CIPHER_SUITE_WEP104;
-		else if (sm->pairwise == WPA_CIPHER_WEP40)
-			pairwise = WPA_CIPHER_SUITE_WEP40;
-		else if (sm->pairwise == WPA_CIPHER_NONE)
-			pairwise = WPA_CIPHER_SUITE_NONE;
-	} else if (sm->wpa == WPA_VERSION_WPA2) {
-		if (sm->pairwise == WPA_CIPHER_CCMP)
-			pairwise = RSN_CIPHER_SUITE_CCMP;
-		else if (sm->pairwise == WPA_CIPHER_TKIP)
-			pairwise = RSN_CIPHER_SUITE_TKIP;
-		else if (sm->pairwise == WPA_CIPHER_WEP104)
-			pairwise = RSN_CIPHER_SUITE_WEP104;
-		else if (sm->pairwise == WPA_CIPHER_WEP40)
-			pairwise = RSN_CIPHER_SUITE_WEP40;
-		else if (sm->pairwise == WPA_CIPHER_NONE)
-			pairwise = RSN_CIPHER_SUITE_NONE;
-	} else
+	pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ?
+				       WPA_PROTO_RSN : WPA_PROTO_WPA,
+				       sm->pairwise);
+	if (pairwise == 0)
 		return 0;
 
 	ret = os_snprintf(
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index a07779f..91ba499 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -158,6 +158,8 @@
 	int pmk_r1_push;
 	int ft_over_ds;
 #endif /* CONFIG_IEEE80211R */
+	int disable_gtk;
+	int ap_mlme;
 };
 
 typedef enum {
@@ -196,6 +198,8 @@
 	struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
 	int (*send_ft_action)(void *ctx, const u8 *dst,
 			      const u8 *data, size_t data_len);
+	int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
+                         size_t tspec_ielen);
 #endif /* CONFIG_IEEE80211R */
 };
 
@@ -278,4 +282,13 @@
 void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_IEEE80211V
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+#ifdef CONFIG_IEEE80211W
+int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+#endif /* CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211V */
+
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index d2ec088..48bf79b 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -52,6 +52,19 @@
 }
 
 
+static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
+			    const u8 *sta_addr,
+			    u8 *tspec_ie, size_t tspec_ielen)
+{
+	if (wpa_auth->cb.add_tspec == NULL) {
+	        wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
+		return -1;
+	}
+	return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
+				      tspec_ielen);
+}
+
+
 int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
 {
 	u8 *pos = buf;
@@ -471,7 +484,8 @@
 #endif /* CONFIG_IEEE80211W */
 
 
-static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count,
+static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
+				u8 *pos, u8 *end, u8 id, u8 descr_count,
 				const u8 *ies, size_t ies_len)
 {
 	struct ieee802_11_elems parse;
@@ -504,7 +518,7 @@
 	}
 
 #ifdef NEED_AP_MLME
-	if (parse.wmm_tspec) {
+	if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
 		struct wmm_tspec_element *tspec;
 		int res;
 
@@ -541,13 +555,35 @@
 	}
 #endif /* NEED_AP_MLME */
 
+	if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
+		struct wmm_tspec_element *tspec;
+		int res;
+
+		tspec = (struct wmm_tspec_element *) pos;
+		os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
+		res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
+				       sizeof(*tspec));
+		if (res >= 0) {
+			if (res)
+				rdie->status_code = host_to_le16(res);
+			else {
+				/* TSPEC accepted; include updated TSPEC in
+				 * response */
+		                rdie->descr_count = 1;
+	                        pos += sizeof(*tspec);
+			}
+			return pos;
+		}
+	}
+
 	wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
 	rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
 	return pos;
 }
 
 
-static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len)
+static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end,
+			       const u8 *ric, size_t ric_len)
 {
 	const u8 *rpos, *start;
 	const struct rsn_rdie *rdie;
@@ -569,7 +605,7 @@
 				break;
 			rpos += 2 + rpos[1];
 		}
-		pos = wpa_ft_process_rdie(pos, end, rdie->id,
+		pos = wpa_ft_process_rdie(sm, pos, end, rdie->id,
 					  rdie->descr_count,
 					  start, rpos - start);
 	}
@@ -678,7 +714,8 @@
 
 	ric_start = pos;
 	if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
-		pos = wpa_ft_process_ric(pos, end, parse.ric, parse.ric_len);
+		pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
+					 parse.ric_len);
 		if (auth_alg == WLAN_AUTH_FT)
 			_ftie->mic_control[1] +=
 				ieee802_11_ie_count(ric_start,
@@ -717,13 +754,9 @@
 	int klen;
 
 	/* MLME-SETKEYS.request(PTK) */
-	if (sm->pairwise == WPA_CIPHER_TKIP) {
-		alg = WPA_ALG_TKIP;
-		klen = 32;
-	} else if (sm->pairwise == WPA_CIPHER_CCMP) {
-		alg = WPA_ALG_CCMP;
-		klen = 16;
-	} else {
+	alg = wpa_cipher_to_alg(sm->pairwise);
+	klen = wpa_cipher_key_len(sm->pairwise);
+	if (!wpa_cipher_valid_pairwise(sm->pairwise)) {
 		wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
 			   "PTK configuration", sm->pairwise);
 		return;
@@ -845,7 +878,7 @@
 	wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
 		    sm->ANonce, WPA_NONCE_LEN);
 
-	ptk_len = pairwise != WPA_CIPHER_CCMP ? 64 : 48;
+	ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48;
 	wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
 			  sm->wpa_auth->addr, pmk_r1_name,
 			  (u8 *) &sm->PTK, ptk_len, ptk_name);
@@ -1061,8 +1094,16 @@
 
 	if (os_memcmp(mic, ftie->mic, 16) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+		wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
+			   MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
 		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
 		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+		wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
+			    parse.mdie - 2, parse.mdie_len + 2);
+		wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
+			    parse.ftie - 2, parse.ftie_len + 2);
+		wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
+			    parse.rsn - 2, parse.rsn_len + 2);
 		return WLAN_STATUS_INVALID_FTIE;
 	}
 
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 3e5ac1d..bdc89e4 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -70,6 +70,9 @@
 	wconf->pmk_r1_push = conf->pmk_r1_push;
 	wconf->ft_over_ds = conf->ft_over_ds;
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_HS20
+	wconf->disable_gtk = conf->disable_dgaf;
+#endif /* CONFIG_HS20 */
 }
 
 
@@ -181,9 +184,15 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = ap_get_sta(hapd, addr);
-	if (sta && sta->psk)
-		return sta->psk;
-	return hostapd_get_psk(hapd->conf, addr, prev_psk);
+	const u8 *psk = hostapd_get_psk(hapd->conf, addr, prev_psk);
+	/*
+	 * This is about to iterate over all psks, prev_psk gives the last
+	 * returned psk which should not be returned again.
+	 * logic list (all hostapd_get_psk; sta->psk)
+	 */
+	if (sta && sta->psk && !psk && sta->psk != prev_psk)
+		psk = sta->psk;
+	return psk;
 }
 
 
@@ -294,12 +303,13 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct wpa_auth_iface_iter_data data;
-	if (hapd->iface->for_each_interface == NULL)
+	if (hapd->iface->interfaces == NULL ||
+	    hapd->iface->interfaces->for_each_interface == NULL)
 		return -1;
 	data.cb = cb;
 	data.cb_ctx = cb_ctx;
-	return hapd->iface->for_each_interface(hapd->iface->interfaces,
-					       wpa_auth_iface_iter, &data);
+	return hapd->iface->interfaces->for_each_interface(
+		hapd->iface->interfaces, wpa_auth_iface_iter, &data);
 }
 
 
@@ -351,16 +361,17 @@
 	int ret;
 
 #ifdef CONFIG_IEEE80211R
-	if (proto == ETH_P_RRB && hapd->iface->for_each_interface) {
+	if (proto == ETH_P_RRB && hapd->iface->interfaces &&
+	    hapd->iface->interfaces->for_each_interface) {
 		int res;
 		struct wpa_auth_ft_iface_iter_data idata;
 		idata.src_hapd = hapd;
 		idata.dst = dst;
 		idata.data = data;
 		idata.data_len = data_len;
-		res = hapd->iface->for_each_interface(hapd->iface->interfaces,
-						      hostapd_wpa_auth_ft_iter,
-						      &idata);
+		res = hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, hostapd_wpa_auth_ft_iter,
+			&idata);
 		if (res == 1)
 			return data_len;
 	}
@@ -425,6 +436,9 @@
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta;
 
+	if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0)
+		return NULL;
+
 	sta = ap_sta_add(hapd, sta_addr);
 	if (sta == NULL)
 		return NULL;
@@ -458,6 +472,14 @@
 		      len - sizeof(*ethhdr));
 }
 
+
+static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
+				      u8 *tspec_ie, size_t tspec_ielen)
+{
+	struct hostapd_data *hapd = ctx;
+	return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
+}
+
 #endif /* CONFIG_IEEE80211R */
 
 
@@ -471,6 +493,8 @@
 	hostapd_wpa_auth_conf(hapd->conf, &_conf);
 	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
 		_conf.tx_status = 1;
+	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
+		_conf.ap_mlme = 1;
 	os_memset(&cb, 0, sizeof(cb));
 	cb.ctx = hapd;
 	cb.logger = hostapd_wpa_auth_logger;
@@ -489,6 +513,7 @@
 #ifdef CONFIG_IEEE80211R
 	cb.send_ft_action = hostapd_wpa_auth_send_ft_action;
 	cb.add_sta = hostapd_wpa_auth_add_sta;
+	cb.add_tspec = hostapd_wpa_auth_add_tspec;
 #endif /* CONFIG_IEEE80211R */
 	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
 	if (hapd->wpa_auth == NULL) {
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index b223576..d5cf2c5 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -87,6 +87,9 @@
 	unsigned int ft_completed:1;
 	unsigned int pmk_r1_name_valid:1;
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211V
+	unsigned int is_wnmsleep:1;
+#endif /* CONFIG_IEEE80211V */
 
 	u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
 	int req_replay_counter_used;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index f687182..1786230 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -29,6 +29,7 @@
 	struct wpa_ie_hdr *hdr;
 	int num_suites;
 	u8 *pos, *count;
+	u32 suite;
 
 	hdr = (struct wpa_ie_hdr *) buf;
 	hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
@@ -36,46 +37,25 @@
 	WPA_PUT_LE16(hdr->version, WPA_VERSION);
 	pos = (u8 *) (hdr + 1);
 
-	if (conf->wpa_group == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
-	} else if (conf->wpa_group == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
-	} else if (conf->wpa_group == WPA_CIPHER_WEP104) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
-	} else if (conf->wpa_group == WPA_CIPHER_WEP40) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group);
+	if (suite == 0) {
 		wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
 			   conf->wpa_group);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += WPA_SELECTOR_LEN;
 
-	num_suites = 0;
 	count = pos;
 	pos += 2;
 
-	if (conf->wpa_pairwise & WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
-		pos += WPA_SELECTOR_LEN;
-		num_suites++;
-	}
-	if (conf->wpa_pairwise & WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
-		pos += WPA_SELECTOR_LEN;
-		num_suites++;
-	}
-	if (conf->wpa_pairwise & WPA_CIPHER_NONE) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
-		pos += WPA_SELECTOR_LEN;
-		num_suites++;
-	}
-
+	num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise);
 	if (num_suites == 0) {
 		wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
 			   conf->wpa_pairwise);
 		return -1;
 	}
+	pos += num_suites * WPA_SELECTOR_LEN;
 	WPA_PUT_LE16(count, num_suites);
 
 	num_suites = 0;
@@ -112,28 +92,23 @@
 		     const u8 *pmkid)
 {
 	struct rsn_ie_hdr *hdr;
-	int num_suites;
+	int num_suites, res;
 	u8 *pos, *count;
 	u16 capab;
+	u32 suite;
 
 	hdr = (struct rsn_ie_hdr *) buf;
 	hdr->elem_id = WLAN_EID_RSN;
 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
 	pos = (u8 *) (hdr + 1);
 
-	if (conf->wpa_group == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	} else if (conf->wpa_group == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-	} else if (conf->wpa_group == WPA_CIPHER_WEP104) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
-	} else if (conf->wpa_group == WPA_CIPHER_WEP40) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+	if (suite == 0) {
 		wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
 			   conf->wpa_group);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += RSN_SELECTOR_LEN;
 
 	num_suites = 0;
@@ -148,21 +123,9 @@
 	}
 #endif /* CONFIG_RSN_TESTING */
 
-	if (conf->rsn_pairwise & WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-		pos += RSN_SELECTOR_LEN;
-		num_suites++;
-	}
-	if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-		pos += RSN_SELECTOR_LEN;
-		num_suites++;
-	}
-	if (conf->rsn_pairwise & WPA_CIPHER_NONE) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
-		pos += RSN_SELECTOR_LEN;
-		num_suites++;
-	}
+	res = rsn_cipher_put_suites(pos, conf->rsn_pairwise);
+	num_suites += res;
+	pos += res * RSN_SELECTOR_LEN;
 
 #ifdef CONFIG_RSN_TESTING
 	if (rsn_testing) {
@@ -450,30 +413,16 @@
 			selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
 
-		selector = RSN_CIPHER_SUITE_CCMP;
-		if (data.pairwise_cipher & WPA_CIPHER_CCMP)
+		selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+					       data.pairwise_cipher);
+		if (!selector)
 			selector = RSN_CIPHER_SUITE_CCMP;
-		else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
-			selector = RSN_CIPHER_SUITE_TKIP;
-		else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
-			selector = RSN_CIPHER_SUITE_WEP104;
-		else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
-			selector = RSN_CIPHER_SUITE_WEP40;
-		else if (data.pairwise_cipher & WPA_CIPHER_NONE)
-			selector = RSN_CIPHER_SUITE_NONE;
 		wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
 
-		selector = RSN_CIPHER_SUITE_CCMP;
-		if (data.group_cipher & WPA_CIPHER_CCMP)
+		selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+					       data.group_cipher);
+		if (!selector)
 			selector = RSN_CIPHER_SUITE_CCMP;
-		else if (data.group_cipher & WPA_CIPHER_TKIP)
-			selector = RSN_CIPHER_SUITE_TKIP;
-		else if (data.group_cipher & WPA_CIPHER_WEP104)
-			selector = RSN_CIPHER_SUITE_WEP104;
-		else if (data.group_cipher & WPA_CIPHER_WEP40)
-			selector = RSN_CIPHER_SUITE_WEP40;
-		else if (data.group_cipher & WPA_CIPHER_NONE)
-			selector = RSN_CIPHER_SUITE_NONE;
 		wpa_auth->dot11RSNAGroupCipherSelected = selector;
 	} else {
 		res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data);
@@ -485,30 +434,16 @@
 			selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
 
-		selector = WPA_CIPHER_SUITE_TKIP;
-		if (data.pairwise_cipher & WPA_CIPHER_CCMP)
-			selector = WPA_CIPHER_SUITE_CCMP;
-		else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
-			selector = WPA_CIPHER_SUITE_TKIP;
-		else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
-			selector = WPA_CIPHER_SUITE_WEP104;
-		else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
-			selector = WPA_CIPHER_SUITE_WEP40;
-		else if (data.pairwise_cipher & WPA_CIPHER_NONE)
-			selector = WPA_CIPHER_SUITE_NONE;
+		selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+					       data.pairwise_cipher);
+		if (!selector)
+			selector = RSN_CIPHER_SUITE_TKIP;
 		wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
 
-		selector = WPA_CIPHER_SUITE_TKIP;
-		if (data.group_cipher & WPA_CIPHER_CCMP)
-			selector = WPA_CIPHER_SUITE_CCMP;
-		else if (data.group_cipher & WPA_CIPHER_TKIP)
+		selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+					       data.group_cipher);
+		if (!selector)
 			selector = WPA_CIPHER_SUITE_TKIP;
-		else if (data.group_cipher & WPA_CIPHER_WEP104)
-			selector = WPA_CIPHER_SUITE_WEP104;
-		else if (data.group_cipher & WPA_CIPHER_WEP40)
-			selector = WPA_CIPHER_SUITE_WEP40;
-		else if (data.group_cipher & WPA_CIPHER_NONE)
-			selector = WPA_CIPHER_SUITE_NONE;
 		wpa_auth->dot11RSNAGroupCipherSelected = selector;
 	}
 	if (res) {
@@ -607,6 +542,8 @@
 
 	if (ciphers & WPA_CIPHER_CCMP)
 		sm->pairwise = WPA_CIPHER_CCMP;
+	else if (ciphers & WPA_CIPHER_GCMP)
+		sm->pairwise = WPA_CIPHER_GCMP;
 	else
 		sm->pairwise = WPA_CIPHER_TKIP;
 
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 8999217..5e44c72 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPS integration
- * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,6 +12,7 @@
 #include "utils/eloop.h"
 #include "utils/uuid.h"
 #include "crypto/dh_groups.h"
+#include "crypto/dh_group5.h"
 #include "common/wpa_ctrl.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -20,6 +21,7 @@
 #include "wps/wps.h"
 #include "wps/wps_defs.h"
 #include "wps/wps_dev_attr.h"
+#include "wps/wps_attr_parse.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
@@ -37,7 +39,8 @@
 
 static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
 				    const u8 *bssid,
-				    const u8 *ie, size_t ie_len);
+				    const u8 *ie, size_t ie_len,
+				    int ssi_signal);
 static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
 
 
@@ -73,10 +76,11 @@
 	struct wps_for_each_data data;
 	data.func = func;
 	data.ctx = ctx;
-	if (iface->for_each_interface == NULL)
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->for_each_interface == NULL)
 		return wps_for_each(iface, &data);
-	return iface->for_each_interface(iface->interfaces, wps_for_each,
-					 &data);
+	return iface->interfaces->for_each_interface(iface->interfaces,
+						     wps_for_each, &data);
 }
 
 
@@ -183,19 +187,23 @@
 struct wps_stop_reg_data {
 	struct hostapd_data *current_hapd;
 	const u8 *uuid_e;
+	const u8 *dev_pw;
+	size_t dev_pw_len;
 };
 
 static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
 {
 	struct wps_stop_reg_data *data = ctx;
 	if (hapd != data->current_hapd && hapd->wps != NULL)
-		wps_registrar_complete(hapd->wps->registrar, data->uuid_e);
+		wps_registrar_complete(hapd->wps->registrar, data->uuid_e,
+				       data->dev_pw, data->dev_pw_len);
 	return 0;
 }
 
 
 static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
-				       const u8 *uuid_e)
+				       const u8 *uuid_e, const u8 *dev_pw,
+				       size_t dev_pw_len)
 {
 	struct hostapd_data *hapd = ctx;
 	char uuid[40];
@@ -209,6 +217,8 @@
 					 mac_addr, uuid_e);
 	data.current_hapd = hapd;
 	data.uuid_e = uuid_e;
+	data.dev_pw = dev_pw;
+	data.dev_pw_len = dev_pw_len;
 	hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
 }
 
@@ -247,7 +257,8 @@
 	struct hostapd_iface *iface = eloop_data;
 
 	wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
-	if (iface->reload_config(iface) < 0) {
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->reload_config(iface) < 0) {
 		wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
 			   "configuration");
 	}
@@ -334,6 +345,8 @@
 	}
 	hapd->wps->wps_state = WPS_STATE_CONFIGURED;
 
+	if (hapd->iface->config_fname == NULL)
+		return 0;
 	len = os_strlen(hapd->iface->config_fname) + 5;
 	tmp_fname = os_malloc(len);
 	if (tmp_fname == NULL)
@@ -361,10 +374,17 @@
 
 	fprintf(nconf, "wps_state=2\n");
 
-	fprintf(nconf, "ssid=");
-	for (i = 0; i < cred->ssid_len; i++)
-		fputc(cred->ssid[i], nconf);
-	fprintf(nconf, "\n");
+	if (is_hex(cred->ssid, cred->ssid_len)) {
+		fprintf(nconf, "ssid2=");
+		for (i = 0; i < cred->ssid_len; i++)
+			fprintf(nconf, "%02x", cred->ssid[i]);
+		fprintf(nconf, "\n");
+	} else {
+		fprintf(nconf, "ssid=");
+		for (i = 0; i < cred->ssid_len; i++)
+			fputc(cred->ssid[i], nconf);
+		fprintf(nconf, "\n");
+	}
 
 	if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
 	    (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
@@ -454,6 +474,7 @@
 			multi_bss = 1;
 		if (!multi_bss &&
 		    (str_starts(buf, "ssid=") ||
+		     str_starts(buf, "ssid2=") ||
 		     str_starts(buf, "auth_algs=") ||
 		     str_starts(buf, "wep_default_key=") ||
 		     str_starts(buf, "wep_key") ||
@@ -700,10 +721,12 @@
 static const u8 * get_own_uuid(struct hostapd_iface *iface)
 {
 	const u8 *uuid;
-	if (iface->for_each_interface == NULL)
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->for_each_interface == NULL)
 		return NULL;
 	uuid = NULL;
-	iface->for_each_interface(iface->interfaces, get_uuid_cb, &uuid);
+	iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb,
+					      &uuid);
 	return uuid;
 }
 
@@ -719,10 +742,11 @@
 static int interface_count(struct hostapd_iface *iface)
 {
 	int count = 0;
-	if (iface->for_each_interface == NULL)
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->for_each_interface == NULL)
 		return 0;
-	iface->for_each_interface(iface->interfaces, count_interface_cb,
-				  &count);
+	iface->interfaces->for_each_interface(iface->interfaces,
+					      count_interface_cb, &count);
 	return count;
 }
 
@@ -984,6 +1008,20 @@
 }
 
 
+static void hostapd_wps_nfc_clear(struct wps_context *wps)
+{
+#ifdef CONFIG_WPS_NFC
+	wps->ap_nfc_dev_pw_id = 0;
+	wpabuf_free(wps->ap_nfc_dh_pubkey);
+	wps->ap_nfc_dh_pubkey = NULL;
+	wpabuf_free(wps->ap_nfc_dh_privkey);
+	wps->ap_nfc_dh_privkey = NULL;
+	wpabuf_free(wps->ap_nfc_dev_pw);
+	wps->ap_nfc_dev_pw = NULL;
+#endif /* CONFIG_WPS_NFC */
+}
+
+
 void hostapd_deinit_wps(struct hostapd_data *hapd)
 {
 	eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
@@ -1001,6 +1039,7 @@
 	wpabuf_free(hapd->wps->oob_conf.pubkey_hash);
 	wpabuf_free(hapd->wps->oob_conf.dev_password);
 	wps_free_pending_msgs(hapd->wps->upnp_msgs);
+	hostapd_wps_nfc_clear(hapd->wps);
 	os_free(hapd->wps);
 	hapd->wps = NULL;
 	hostapd_wps_clear_ies(hapd);
@@ -1098,6 +1137,24 @@
 }
 
 
+static int wps_cancel(struct hostapd_data *hapd, void *ctx)
+{
+	if (hapd->wps == NULL)
+		return 0;
+
+	wps_registrar_wps_cancel(hapd->wps->registrar);
+	ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+
+	return 0;
+}
+
+
+int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+	return hostapd_wps_for_each(hapd, wps_cancel, NULL);
+}
+
+
 #ifdef CONFIG_WPS_OOB
 int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
 			  char *path, char *method, char *name)
@@ -1154,7 +1211,8 @@
 
 static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
 				    const u8 *bssid,
-				    const u8 *ie, size_t ie_len)
+				    const u8 *ie, size_t ie_len,
+				    int ssi_signal)
 {
 	struct hostapd_data *hapd = ctx;
 	struct wpabuf *wps_ie;
@@ -1471,3 +1529,156 @@
 
 	return wps_registrar_config_ap(hapd->wps->registrar, &cred);
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_password_token_data {
+	const u8 *oob_dev_pw;
+	size_t oob_dev_pw_len;
+	int added;
+};
+
+
+static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_nfc_password_token_data *data = ctx;
+	int ret;
+
+	if (hapd->wps == NULL)
+		return 0;
+	ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar,
+						   data->oob_dev_pw,
+						   data->oob_dev_pw_len);
+	if (ret == 0)
+		data->added++;
+	return ret;
+}
+
+
+static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd,
+					      struct wps_parse_attr *attr)
+{
+	struct wps_nfc_password_token_data data;
+
+	data.oob_dev_pw = attr->oob_dev_password;
+	data.oob_dev_pw_len = attr->oob_dev_password_len;
+	data.added = 0;
+	if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0)
+		return -1;
+	return data.added ? 0 : -1;
+}
+
+
+static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd,
+				       const struct wpabuf *wps)
+{
+	struct wps_parse_attr attr;
+
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+	if (wps_parse_msg(wps, &attr)) {
+		wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+		return -1;
+	}
+
+	if (attr.oob_dev_password)
+		return hostapd_wps_add_nfc_password_token(hapd, &attr);
+
+	wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+	return -1;
+}
+
+
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+			     const struct wpabuf *data)
+{
+	const struct wpabuf *wps = data;
+	struct wpabuf *tmp = NULL;
+	int ret;
+
+	if (wpabuf_len(data) < 4)
+		return -1;
+
+	if (*wpabuf_head_u8(data) != 0x10) {
+		/* Assume this contains full NDEF record */
+		tmp = ndef_parse_wifi(data);
+		if (tmp == NULL) {
+			wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+			return -1;
+		}
+		wps = tmp;
+	}
+
+	ret = hostapd_wps_nfc_tag_process(hapd, wps);
+	wpabuf_free(tmp);
+	return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+					     int ndef)
+{
+	struct wpabuf *ret;
+
+	if (hapd->wps == NULL)
+		return NULL;
+
+	ret = wps_get_oob_cred(hapd->wps);
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
+{
+	return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
+				 &hapd->conf->wps_nfc_dh_pubkey,
+				 &hapd->conf->wps_nfc_dh_privkey,
+				 &hapd->conf->wps_nfc_dev_pw);
+}
+
+
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
+{
+	struct wps_context *wps = hapd->wps;
+
+	if (wps == NULL)
+		return -1;
+
+	if (!hapd->conf->wps_nfc_dh_pubkey ||
+	    !hapd->conf->wps_nfc_dh_privkey ||
+	    !hapd->conf->wps_nfc_dev_pw ||
+	    !hapd->conf->wps_nfc_dev_pw_id)
+		return -1;
+
+	hostapd_wps_nfc_clear(wps);
+	wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
+	wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+	wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+	wps->ap_nfc_dev_pw = wpabuf_dup(hapd->conf->wps_nfc_dev_pw);
+
+	if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
+	    !wps->ap_nfc_dev_pw) {
+		hostapd_wps_nfc_clear(wps);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
+{
+	hostapd_wps_nfc_clear(hapd->wps);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
index 9194225..f968e15 100644
--- a/src/ap/wps_hostapd.h
+++ b/src/ap/wps_hostapd.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPS integration
- * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -20,6 +20,7 @@
 			const char *uuid, const char *pin, int timeout);
 int hostapd_wps_button_pushed(struct hostapd_data *hapd,
 			      const u8 *p2p_dev_addr);
+int hostapd_wps_cancel(struct hostapd_data *hapd);
 int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
 			  char *path, char *method, char *name);
 int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
@@ -32,6 +33,13 @@
 void hostapd_wps_update_ie(struct hostapd_data *hapd);
 int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
 			  const char *auth, const char *encr, const char *key);
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+			     const struct wpabuf *data);
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+					     int ndef);
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
 
 #else /* CONFIG_WPS */
 
@@ -67,6 +75,11 @@
 	return 0;
 }
 
+static inline int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
 #endif /* CONFIG_WPS */
 
 #endif /* WPS_HOSTAPD_H */
diff --git a/src/common/defs.h b/src/common/defs.h
index f057ffe..db29b5d 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -26,6 +26,7 @@
 #ifdef CONFIG_IEEE80211W
 #define WPA_CIPHER_AES_128_CMAC BIT(5)
 #endif /* CONFIG_IEEE80211W */
+#define WPA_CIPHER_GCMP BIT(6)
 
 #define WPA_KEY_MGMT_IEEE8021X BIT(0)
 #define WPA_KEY_MGMT_PSK BIT(1)
@@ -91,7 +92,8 @@
 	WPA_ALG_TKIP,
 	WPA_ALG_CCMP,
 	WPA_ALG_IGTK,
-	WPA_ALG_PMK
+	WPA_ALG_PMK,
+	WPA_ALG_GCMP
 };
 
 /**
@@ -102,7 +104,8 @@
 	CIPHER_WEP40,
 	CIPHER_TKIP,
 	CIPHER_CCMP,
-	CIPHER_WEP104
+	CIPHER_WEP104,
+	CIPHER_GCMP
 };
 
 /**
diff --git a/src/common/eapol_common.h b/src/common/eapol_common.h
index 4e392e7..4811f38 100644
--- a/src/common/eapol_common.h
+++ b/src/common/eapol_common.h
@@ -38,4 +38,44 @@
 enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2,
        EAPOL_KEY_TYPE_WPA = 254 };
 
+
+#define IEEE8021X_REPLAY_COUNTER_LEN 8
+#define IEEE8021X_KEY_SIGN_LEN 16
+#define IEEE8021X_KEY_IV_LEN 16
+
+#define IEEE8021X_KEY_INDEX_FLAG 0x80
+#define IEEE8021X_KEY_INDEX_MASK 0x03
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee802_1x_eapol_key {
+	u8 type;
+	/* Note: key_length is unaligned */
+	u8 key_length[2];
+	/* does not repeat within the life of the keying material used to
+	 * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
+	u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
+	u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
+	u8 key_index; /* key flag in the most significant bit:
+		       * 0 = broadcast (default key),
+		       * 1 = unicast (key mapping key); key index is in the
+		       * 7 least significant bits */
+	/* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
+	 * the key */
+	u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
+
+	/* followed by key: if packet body length = 44 + key length, then the
+	 * key field (of key_length bytes) contains the key in encrypted form;
+	 * if packet body length = 44, key field is absent and key_length
+	 * represents the number of least significant octets from
+	 * MS-MPPE-Send-Key attribute to be used as the keying material;
+	 * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
 #endif /* EAPOL_COMMON_H */
diff --git a/src/common/gas.c b/src/common/gas.c
index a67325c..cff9254 100644
--- a/src/common/gas.c
+++ b/src/common/gas.c
@@ -1,7 +1,7 @@
 /*
  * Generic advertisement service (GAS) (IEEE 802.11u)
  * Copyright (c) 2009, Atheros Communications
- * Copyright (c) 2011, Qualcomm Atheros
+ * Copyright (c) 2011-2012, Qualcomm Atheros
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -31,7 +31,7 @@
 }
 
 
-static struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
+struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
 {
 	return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token,
 			     size);
diff --git a/src/common/gas.h b/src/common/gas.h
index 8664a79..306adc5 100644
--- a/src/common/gas.h
+++ b/src/common/gas.h
@@ -1,7 +1,7 @@
 /*
  * Generic advertisement service (GAS) (IEEE 802.11u)
  * Copyright (c) 2009, Atheros Communications
- * Copyright (c) 2011, Qualcomm Atheros
+ * Copyright (c) 2011-2012, Qualcomm Atheros
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 #ifndef GAS_H
 #define GAS_H
 
+struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size);
 struct wpabuf * gas_build_comeback_req(u8 dialog_token);
 struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
 				       u16 comeback_delay, size_t size);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index b0d310d..d9d3cd0 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 Common routines
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -97,6 +97,16 @@
 			elems->p2p = pos;
 			elems->p2p_len = elen;
 			break;
+		case WFD_OUI_TYPE:
+			/* Wi-Fi Alliance - WFD IE */
+			elems->wfd = pos;
+			elems->wfd_len = elen;
+			break;
+		case HS20_INDICATION_OUI_TYPE:
+			/* Hotspot 2.0 */
+			elems->hs20 = pos;
+			elems->hs20_len = elen;
+			break;
 		default:
 			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
 				   "information element ignored "
@@ -248,6 +258,14 @@
 			elems->ht_operation = pos;
 			elems->ht_operation_len = elen;
 			break;
+		case WLAN_EID_VHT_CAP:
+			elems->vht_capabilities = pos;
+			elems->vht_capabilities_len = elen;
+			break;
+		case WLAN_EID_VHT_OPERATION:
+			elems->vht_operation = pos;
+			elems->vht_operation_len = elen;
+			break;
 		case WLAN_EID_LINK_ID:
 			if (elen < 18)
 				break;
@@ -257,6 +275,15 @@
 			elems->interworking = pos;
 			elems->interworking_len = elen;
 			break;
+		case WLAN_EID_EXT_CAPAB:
+			elems->ext_capab = pos;
+			elems->ext_capab_len = elen;
+			break;
+		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
+			if (elen < 3)
+				break;
+			elems->bss_max_idle_period = pos;
+			break;
 		default:
 			unknown++;
 			if (!show_errors)
@@ -383,3 +410,75 @@
 		return NULL;
 	}
 }
+
+
+int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
+			  const char *name, const char *val)
+{
+	int num, v;
+	const char *pos;
+	struct hostapd_wmm_ac_params *ac;
+
+	/* skip 'wme_ac_' or 'wmm_ac_' prefix */
+	pos = name + 7;
+	if (os_strncmp(pos, "be_", 3) == 0) {
+		num = 0;
+		pos += 3;
+	} else if (os_strncmp(pos, "bk_", 3) == 0) {
+		num = 1;
+		pos += 3;
+	} else if (os_strncmp(pos, "vi_", 3) == 0) {
+		num = 2;
+		pos += 3;
+	} else if (os_strncmp(pos, "vo_", 3) == 0) {
+		num = 3;
+		pos += 3;
+	} else {
+		wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
+		return -1;
+	}
+
+	ac = &wmm_ac_params[num];
+
+	if (os_strcmp(pos, "aifs") == 0) {
+		v = atoi(val);
+		if (v < 1 || v > 255) {
+			wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
+			return -1;
+		}
+		ac->aifs = v;
+	} else if (os_strcmp(pos, "cwmin") == 0) {
+		v = atoi(val);
+		if (v < 0 || v > 12) {
+			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
+			return -1;
+		}
+		ac->cwmin = v;
+	} else if (os_strcmp(pos, "cwmax") == 0) {
+		v = atoi(val);
+		if (v < 0 || v > 12) {
+			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
+			return -1;
+		}
+		ac->cwmax = v;
+	} else if (os_strcmp(pos, "txop_limit") == 0) {
+		v = atoi(val);
+		if (v < 0 || v > 0xffff) {
+			wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
+			return -1;
+		}
+		ac->txop_limit = v;
+	} else if (os_strcmp(pos, "acm") == 0) {
+		v = atoi(val);
+		if (v < 0 || v > 1) {
+			wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
+			return -1;
+		}
+		ac->admission_control_mandatory = v;
+	} else {
+		wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index bfac88c..bfc3eb2 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -33,10 +33,16 @@
 	const u8 *timeout_int;
 	const u8 *ht_capabilities;
 	const u8 *ht_operation;
+	const u8 *vht_capabilities;
+	const u8 *vht_operation;
 	const u8 *vendor_ht_cap;
 	const u8 *p2p;
+	const u8 *wfd;
 	const u8 *link_id;
 	const u8 *interworking;
+	const u8 *hs20;
+	const u8 *ext_capab;
+	const u8 *bss_max_idle_period;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -60,9 +66,14 @@
 	u8 timeout_int_len;
 	u8 ht_capabilities_len;
 	u8 ht_operation_len;
+	u8 vht_capabilities_len;
+	u8 vht_operation_len;
 	u8 vendor_ht_cap_len;
 	u8 p2p_len;
+	u8 wfd_len;
 	u8 interworking_len;
+	u8 hs20_len;
+	u8 ext_capab_len;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -76,4 +87,15 @@
 struct ieee80211_hdr;
 const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len);
 
+struct hostapd_wmm_ac_params {
+	int cwmin;
+	int cwmax;
+	int aifs;
+	int txop_limit; /* in units of 32us */
+	int admission_control_mandatory;
+};
+
+int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
+			  const char *name, const char *val);
+
 #endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 3bef006..2ab7fbf 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -228,12 +228,25 @@
 #define WLAN_EID_20_40_BSS_INTOLERANT 73
 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
 #define WLAN_EID_MMIE 76
+#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
+#define WLAN_EID_TFS_REQ 91
+#define WLAN_EID_TFS_RESP 92
+#define WLAN_EID_WNMSLEEP 93
 #define WLAN_EID_TIME_ZONE 98
 #define WLAN_EID_LINK_ID 101
 #define WLAN_EID_INTERWORKING 107
 #define WLAN_EID_ADV_PROTO 108
 #define WLAN_EID_ROAMING_CONSORTIUM 111
 #define WLAN_EID_EXT_CAPAB 127
+#define WLAN_EID_VHT_CAP 191
+#define WLAN_EID_VHT_OPERATION 192
+#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
+#define WLAN_EID_VHT_WIDE_BW_CHSWITCH  194
+#define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195
+#define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196
+#define WLAN_EID_VHT_AID 197
+#define WLAN_EID_VHT_QUIET_CHANNEL 198
+#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199
 #define WLAN_EID_VENDOR_SPECIFIC 221
 
 
@@ -254,6 +267,7 @@
 #define WLAN_ACTION_VENDOR_SPECIFIC 127
 
 /* Public action codes */
+#define WLAN_PA_20_40_BSS_COEX 0
 #define WLAN_PA_VENDOR_SPECIFIC 9
 #define WLAN_PA_GAS_INITIAL_REQ 10
 #define WLAN_PA_GAS_INITIAL_RESP 11
@@ -487,6 +501,17 @@
 				} STRUCT_PACKED sa_query_resp;
 				struct {
 					u8 action;
+					u8 dialogtoken;
+					u8 variable[0];
+				} STRUCT_PACKED wnm_sleep_req;
+				struct {
+					u8 action;
+					u8 dialogtoken;
+					le16 keydata_len;
+					u8 variable[0];
+				} STRUCT_PACKED wnm_sleep_resp;
+				struct {
+					u8 action;
 					u8 variable[0];
 				} STRUCT_PACKED public_action;
 				struct {
@@ -534,6 +559,19 @@
 	u8 basic_set[16];
 } STRUCT_PACKED;
 
+
+struct ieee80211_vht_capabilities {
+	le32 vht_capabilities_info;
+	u8 vht_supported_mcs_set[8];
+} STRUCT_PACKED;
+
+struct ieee80211_vht_operation {
+	u8 vht_op_info_chwidth;
+	u8 vht_op_info_chan_center_freq_seg0_idx;
+	u8 vht_op_info_chan_center_freq_seg1_idx;
+	le16 vht_basic_mcs_set;
+} STRUCT_PACKED;
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
@@ -630,12 +668,42 @@
 
 #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
 
+/* VHT Defines */
+#define VHT_CAP_MAX_MPDU_LENGTH_7991                ((u32) BIT(0))
+#define VHT_CAP_MAX_MPDU_LENGTH_11454               ((u32) BIT(1))
+#define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ              ((u32) BIT(2))
+#define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ     ((u32) BIT(3))
+#define VHT_CAP_RXLDPC                              ((u32) BIT(4))
+#define VHT_CAP_SHORT_GI_80                         ((u32) BIT(5))
+#define VHT_CAP_SHORT_GI_160                        ((u32) BIT(6))
+#define VHT_CAP_TXSTBC                              ((u32) BIT(7))
+#define VHT_CAP_RXSTBC_1                            ((u32) BIT(8))
+#define VHT_CAP_RXSTBC_2                            ((u32) BIT(9))
+#define VHT_CAP_RXSTBC_3                            ((u32) BIT(8) | BIT(9))
+#define VHT_CAP_RXSTBC_4                            ((u32) BIT(10))
+#define VHT_CAP_SU_BEAMFORMER_CAPABLE               ((u32) BIT(11))
+#define VHT_CAP_SU_BEAMFORMEE_CAPABLE               ((u32) BIT(12))
+#define VHT_CAP_BEAMFORMER_ANTENNAS_MAX             ((u32) BIT(13) | BIT(14))
+#define VHT_CAP_SOUNDING_DIMENTION_MAX              ((u32) BIT(16) | BIT(17))
+#define VHT_CAP_MU_BEAMFORMER_CAPABLE               ((u32) BIT(19))
+#define VHT_CAP_MU_BEAMFORMEE_CAPABLE               ((u32) BIT(20))
+#define VHT_CAP_VHT_TXOP_PS                         ((u32) BIT(21))
+#define VHT_CAP_HTC_VHT                             ((u32) BIT(22))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT          ((u32) BIT(23))
+#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB   ((u32) BIT(27))
+#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB     ((u32) BIT(26) | BIT(27))
+#define VHT_CAP_RX_ANTENNA_PATTERN                  ((u32) BIT(28))
+#define VHT_CAP_TX_ANTENNA_PATTERN                  ((u32) BIT(29))
+
 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
 				* 00:50:F2 */
 #define WPA_IE_VENDOR_TYPE 0x0050f201
 #define WPS_IE_VENDOR_TYPE 0x0050f204
 #define OUI_WFA 0x506f9a
 #define P2P_IE_VENDOR_TYPE 0x506f9a09
+#define WFD_IE_VENDOR_TYPE 0x506f9a0a
+#define WFD_OUI_TYPE 10
+#define HS20_IE_VENDOR_TYPE 0x506f9a10
 
 #define WMM_OUI_TYPE 2
 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -749,6 +817,16 @@
 };
 
 
+#define HS20_INDICATION_OUI_TYPE 16
+#define HS20_ANQP_OUI_TYPE 17
+#define HS20_STYPE_QUERY_LIST 1
+#define HS20_STYPE_CAPABILITY_LIST 2
+#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3
+#define HS20_STYPE_WAN_METRICS 4
+#define HS20_STYPE_CONNECTION_CAPABILITY 5
+#define HS20_STYPE_NAI_HOME_REALM_QUERY 6
+#define HS20_STYPE_OPERATING_CLASS 7
+
 /* Wi-Fi Direct (P2P) */
 
 #define P2P_OUI_TYPE 9
@@ -847,6 +925,7 @@
 	P2P_SERV_BONJOUR = 1,
 	P2P_SERV_UPNP = 2,
 	P2P_SERV_WS_DISCOVERY = 3,
+	P2P_SERV_WIFI_DISPLAY = 4,
 	P2P_SERV_VENDOR_SPECIFIC = 255
 };
 
@@ -858,6 +937,20 @@
 };
 
 
+enum wifi_display_subelem {
+	WFD_SUBELEM_DEVICE_INFO = 0,
+	WFD_SUBELEM_ASSOCIATED_BSSID = 1,
+	WFD_SUBELEM_AUDIO_FORMATS = 2,
+	WFD_SUBELEM_VIDEO_FORMATS = 3,
+	WFD_SUBELEM_3D_VIDEO_FORMATS = 4,
+	WFD_SUBELEM_CONTENT_PROTECTION = 5,
+	WFD_SUBELEM_COUPLED_SINK = 6,
+	WFD_SUBELEM_EXT_CAPAB = 7,
+	WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
+	WFD_SUBELEM_SESSION_INFO = 9
+};
+
+
 #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
 
 #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
@@ -870,6 +963,8 @@
 #define WLAN_CIPHER_SUITE_CCMP		0x000FAC04
 #define WLAN_CIPHER_SUITE_WEP104	0x000FAC05
 #define WLAN_CIPHER_SUITE_AES_CMAC	0x000FAC06
+#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR	0x000FAC07
+#define WLAN_CIPHER_SUITE_GCMP		0x000FAC08
 
 /* AKM suite selectors */
 #define WLAN_AKM_SUITE_8021X		0x000FAC01
@@ -915,4 +1010,48 @@
 #define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
 #define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
 
+/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
+#define WLAN_20_40_BSS_COEX_INFO_REQ            BIT(0)
+#define WLAN_20_40_BSS_COEX_40MHZ_INTOL         BIT(1)
+#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ     BIT(2)
+#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ     BIT(3)
+#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT    BIT(4)
+
+struct ieee80211_2040_bss_coex_ie {
+	u8 element_id;
+	u8 length;
+	u8 coex_param;
+} STRUCT_PACKED;
+
+struct ieee80211_2040_intol_chan_report {
+	u8 element_id;
+	u8 length;
+	u8 op_class;
+	u8 variable[0];	/* Channel List */
+} STRUCT_PACKED;
+
+/* IEEE 802.11v - WNM-Sleep Mode element */
+struct wnm_sleep_element {
+	u8 eid;     /* WLAN_EID_WNMSLEEP */
+	u8 len;
+	u8 action_type; /* WLAN_WNM_SLEEP_ENTER/EXIT */
+	u8 status;
+	le16 intval;
+} STRUCT_PACKED;
+
+enum wnm_sleep_mode_response_status {
+	WNM_STATUS_SLEEP_ACCEPT = 0,
+	WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1,
+	WNM_STATUS_DENIED_ACTION = 2,
+	WNM_STATUS_DENIED_TMP = 3,
+	WNM_STATUS_DENIED_KEY = 4,
+	WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5
+};
+
+/* WNM-Sleep Mode subelement IDs */
+enum wnm_sleep_mode_subelement_id {
+	WNM_SLEEP_SUBELEM_GTK = 0,
+	WNM_SLEEP_SUBELEM_IGTK = 1
+};
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 7a013a8..36c308a 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -43,8 +43,10 @@
 	u8 hash[SHA1_MAC_LEN];
 
 	switch (ver) {
+#ifndef CONFIG_FIPS
 	case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
 		return hmac_md5(key, 16, buf, len, mic);
+#endif /* CONFIG_FIPS */
 	case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
 		if (hmac_sha1(key, 16, buf, len, hash))
 			return -1;
@@ -350,6 +352,8 @@
 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
 		return WPA_CIPHER_AES_128_CMAC;
 #endif /* CONFIG_IEEE80211W */
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP)
+		return WPA_CIPHER_GCMP;
 	return 0;
 }
 
@@ -906,6 +910,8 @@
 		return "CCMP";
 	case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP:
 		return "CCMP+TKIP";
+	case WPA_CIPHER_GCMP:
+		return "GCMP";
 	default:
 		return "UNKNOWN";
 	}
@@ -1067,3 +1073,138 @@
 	return added;
 }
 #endif /* CONFIG_IEEE80211R */
+
+
+int wpa_cipher_key_len(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_CCMP:
+	case WPA_CIPHER_GCMP:
+		return 16;
+	case WPA_CIPHER_TKIP:
+		return 32;
+	case WPA_CIPHER_WEP104:
+		return 13;
+	case WPA_CIPHER_WEP40:
+		return 5;
+	}
+
+	return 0;
+}
+
+
+int wpa_cipher_rsc_len(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_CCMP:
+	case WPA_CIPHER_GCMP:
+	case WPA_CIPHER_TKIP:
+		return 6;
+	case WPA_CIPHER_WEP104:
+	case WPA_CIPHER_WEP40:
+		return 0;
+	}
+
+	return 0;
+}
+
+
+int wpa_cipher_to_alg(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_CCMP:
+		return WPA_ALG_CCMP;
+	case WPA_CIPHER_GCMP:
+		return WPA_ALG_GCMP;
+	case WPA_CIPHER_TKIP:
+		return WPA_ALG_TKIP;
+	case WPA_CIPHER_WEP104:
+	case WPA_CIPHER_WEP40:
+		return WPA_ALG_WEP;
+	}
+	return WPA_ALG_NONE;
+}
+
+
+int wpa_cipher_valid_pairwise(int cipher)
+{
+	return cipher == WPA_CIPHER_CCMP ||
+		cipher == WPA_CIPHER_GCMP ||
+		cipher == WPA_CIPHER_TKIP;
+}
+
+
+u32 wpa_cipher_to_suite(int proto, int cipher)
+{
+	if (cipher & WPA_CIPHER_CCMP)
+		return (proto == WPA_PROTO_RSN ?
+			RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
+	if (cipher & WPA_CIPHER_GCMP)
+		return RSN_CIPHER_SUITE_GCMP;
+	if (cipher & WPA_CIPHER_TKIP)
+		return (proto == WPA_PROTO_RSN ?
+			RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
+	if (cipher & WPA_CIPHER_WEP104)
+		return (proto == WPA_PROTO_RSN ?
+			RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
+	if (cipher & WPA_CIPHER_WEP40)
+		return (proto == WPA_PROTO_RSN ?
+			RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
+	if (cipher & WPA_CIPHER_NONE)
+		return (proto == WPA_PROTO_RSN ?
+			RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
+	return 0;
+}
+
+
+int rsn_cipher_put_suites(u8 *pos, int ciphers)
+{
+	int num_suites = 0;
+
+	if (ciphers & WPA_CIPHER_CCMP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (ciphers & WPA_CIPHER_GCMP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (ciphers & WPA_CIPHER_TKIP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (ciphers & WPA_CIPHER_NONE) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+
+	return num_suites;
+}
+
+
+int wpa_cipher_put_suites(u8 *pos, int ciphers)
+{
+	int num_suites = 0;
+
+	if (ciphers & WPA_CIPHER_CCMP) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (ciphers & WPA_CIPHER_TKIP) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (ciphers & WPA_CIPHER_NONE) {
+		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+
+	return num_suites;
+}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 6b50997..603166b 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -64,6 +64,7 @@
 #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
 #endif /* CONFIG_IEEE80211W */
 #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
 
 /* EAPOL-Key Key Data Encapsulation
  * GroupKey and PeerKey require encryption, otherwise, encryption is optional.
@@ -83,6 +84,9 @@
 #ifdef CONFIG_IEEE80211W
 #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
 #endif /* CONFIG_IEEE80211W */
+#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
+#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
 
 #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
 
@@ -375,4 +379,12 @@
 
 int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
 
+int wpa_cipher_key_len(int cipher);
+int wpa_cipher_rsc_len(int cipher);
+int wpa_cipher_to_alg(int cipher);
+int wpa_cipher_valid_pairwise(int cipher);
+u32 wpa_cipher_to_suite(int proto, int cipher);
+int rsn_cipher_put_suites(u8 *pos, int ciphers);
+int wpa_cipher_put_suites(u8 *pos, int ciphers);
+
 #endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
index b2b0683..58cbe6a 100644
--- a/src/common/wpa_ctrl.c
+++ b/src/common/wpa_ctrl.c
@@ -12,7 +12,12 @@
 
 #ifdef CONFIG_CTRL_IFACE_UNIX
 #include <sys/un.h>
+#include <unistd.h>
+#include <fcntl.h>
 #endif /* CONFIG_CTRL_IFACE_UNIX */
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+#include <netdb.h>
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
 #ifdef ANDROID
 #include <dirent.h>
@@ -44,6 +49,8 @@
 	struct sockaddr_in local;
 	struct sockaddr_in dest;
 	char *cookie;
+	char *remote_ifname;
+	char *remote_ip;
 #endif /* CONFIG_CTRL_IFACE_UDP */
 #ifdef CONFIG_CTRL_IFACE_UNIX
 	int s;
@@ -73,6 +80,7 @@
 	int ret;
 	size_t res;
 	int tries = 0;
+	int flags;
 
 	ctrl = os_malloc(sizeof(*ctrl));
 	if (ctrl == NULL)
@@ -156,6 +164,19 @@
 		return NULL;
 	}
 
+	/*
+	 * Make socket non-blocking so that we don't hang forever if
+	 * target dies unexpectedly.
+	 */
+	flags = fcntl(ctrl->s, F_GETFL);
+	if (flags >= 0) {
+		flags |= O_NONBLOCK;
+		if (fcntl(ctrl->s, F_SETFL, flags) < 0) {
+			perror("fcntl(ctrl->s, O_NONBLOCK)");
+			/* Not fatal, continue on.*/
+		}
+	}
+
 	return ctrl;
 }
 
@@ -230,6 +251,9 @@
 	struct wpa_ctrl *ctrl;
 	char buf[128];
 	size_t len;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	struct hostent *h;
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
 	ctrl = os_malloc(sizeof(*ctrl));
 	if (ctrl == NULL)
@@ -244,7 +268,11 @@
 	}
 
 	ctrl->local.sin_family = AF_INET;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	ctrl->local.sin_addr.s_addr = INADDR_ANY;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 	ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 	if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
 		 sizeof(ctrl->local)) < 0) {
 		close(ctrl->s);
@@ -255,10 +283,48 @@
 	ctrl->dest.sin_family = AF_INET;
 	ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1);
 	ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT);
+
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	if (ctrl_path) {
+		char *port, *name;
+		int port_id;
+
+		name = os_strdup(ctrl_path);
+		if (name == NULL) {
+			close(ctrl->s);
+			os_free(ctrl);
+			return NULL;
+		}
+		port = os_strchr(name, ':');
+
+		if (port) {
+			port_id = atoi(&port[1]);
+			port[0] = '\0';
+		} else
+			port_id = WPA_CTRL_IFACE_PORT;
+
+		h = gethostbyname(name);
+		ctrl->remote_ip = os_strdup(name);
+		os_free(name);
+		if (h == NULL) {
+			perror("gethostbyname");
+			close(ctrl->s);
+			os_free(ctrl->remote_ip);
+			os_free(ctrl);
+			return NULL;
+		}
+		ctrl->dest.sin_port = htons(port_id);
+		os_memcpy(h->h_addr, (char *) &ctrl->dest.sin_addr.s_addr,
+			  h->h_length);
+	} else
+		ctrl->remote_ip = os_strdup("localhost");
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
 	if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
 		    sizeof(ctrl->dest)) < 0) {
 		perror("connect");
 		close(ctrl->s);
+		os_free(ctrl->remote_ip);
 		os_free(ctrl);
 		return NULL;
 	}
@@ -269,14 +335,31 @@
 		ctrl->cookie = os_strdup(buf);
 	}
 
+	if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) {
+		buf[len] = '\0';
+		ctrl->remote_ifname = os_strdup(buf);
+	}
+
 	return ctrl;
 }
 
 
+char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl)
+{
+#define WPA_CTRL_MAX_PS_NAME 100
+	static char ps[WPA_CTRL_MAX_PS_NAME] = {};
+	os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s",
+		    ctrl->remote_ip, ctrl->remote_ifname);
+	return ps;
+}
+
+
 void wpa_ctrl_close(struct wpa_ctrl *ctrl)
 {
 	close(ctrl->s);
 	os_free(ctrl->cookie);
+	os_free(ctrl->remote_ifname);
+	os_free(ctrl->remote_ip);
 	os_free(ctrl);
 }
 
@@ -289,6 +372,7 @@
 		     void (*msg_cb)(char *msg, size_t len))
 {
 	struct timeval tv;
+	struct os_time started_at;
 	int res;
 	fd_set rfds;
 	const char *_cmd;
@@ -315,7 +399,30 @@
 		_cmd_len = cmd_len;
 	}
 
+	errno = 0;
+	started_at.sec = 0;
+	started_at.usec = 0;
+retry_send:
 	if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
+		if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK)
+		{
+			/*
+			 * Must be a non-blocking socket... Try for a bit
+			 * longer before giving up.
+			 */
+			if (started_at.sec == 0)
+				os_get_time(&started_at);
+			else {
+				struct os_time n;
+				os_get_time(&n);
+				/* Try for a few seconds. */
+				if (n.sec > started_at.sec + 5)
+					goto send_err;
+			}
+			os_sleep(1, 0);
+			goto retry_send;
+		}
+	send_err:
 		os_free(cmd_buf);
 		return -1;
 	}
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 5768fdd..84f1195 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -48,6 +48,10 @@
 #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
 /** EAP authentication failed (EAP-Failure received) */
 #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
+/** Network block temporarily disabled (e.g., due to authentication failure) */
+#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED "
+/** Temporarily disabled network block re-enabled */
+#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED "
 /** New scan results available */
 #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
 /** wpa_supplicant state change */
@@ -120,6 +124,8 @@
 #define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
 /* parameters: <peer address> */
 #define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
+/* parameters: <peer address> <status> */
+#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE"
 /* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
 #define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
 /* parameters: <src addr> <update indicator> <TLVs> */
@@ -131,6 +137,8 @@
 #define INTERWORKING_AP "INTERWORKING-AP "
 #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
 
+#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO "
+
 /* hostapd control interface - fixed message prefixes */
 #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
 #define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
@@ -143,6 +151,28 @@
 #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
 
 
+/* BSS command information masks */
+
+#define WPA_BSS_MASK_ALL		0xFFFFFFFF
+#define WPA_BSS_MASK_ID			BIT(0)
+#define WPA_BSS_MASK_BSSID		BIT(1)
+#define WPA_BSS_MASK_FREQ		BIT(2)
+#define WPA_BSS_MASK_BEACON_INT		BIT(3)
+#define WPA_BSS_MASK_CAPABILITIES	BIT(4)
+#define WPA_BSS_MASK_QUAL		BIT(5)
+#define WPA_BSS_MASK_NOISE		BIT(6)
+#define WPA_BSS_MASK_LEVEL		BIT(7)
+#define WPA_BSS_MASK_TSF		BIT(8)
+#define WPA_BSS_MASK_AGE		BIT(9)
+#define WPA_BSS_MASK_IE			BIT(10)
+#define WPA_BSS_MASK_FLAGS		BIT(11)
+#define WPA_BSS_MASK_SSID		BIT(12)
+#define WPA_BSS_MASK_WPS_SCAN		BIT(13)
+#define WPA_BSS_MASK_P2P_SCAN		BIT(14)
+#define WPA_BSS_MASK_INTERNETW		BIT(15)
+#define WPA_BSS_MASK_WIFI_DISPLAY	BIT(16)
+
+
 /* wpa_supplicant/hostapd control interface access */
 
 /**
@@ -267,6 +297,8 @@
  */
 int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
 
+char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
+
 #ifdef ANDROID
 /**
  * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
@@ -279,8 +311,11 @@
 #endif /* ANDROID */
 
 #ifdef CONFIG_CTRL_IFACE_UDP
+/* Port range for multiple wpa_supplicant instances and multiple VIFs */
 #define WPA_CTRL_IFACE_PORT 9877
+#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */
 #define WPA_GLOBAL_CTRL_IFACE_PORT 9878
+#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */
 #endif /* CONFIG_CTRL_IFACE_UDP */
 
 
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index b221dd4..a605a65 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -16,9 +16,11 @@
 
 LIB_OBJS= \
 	aes-cbc.o \
+	aes-ccm.o \
 	aes-ctr.o \
 	aes-eax.o \
 	aes-encblock.o \
+	aes-gcm.o \
 	aes-internal.o \
 	aes-internal-dec.o \
 	aes-internal-enc.o \
@@ -31,16 +33,17 @@
 	md4-internal.o \
 	md5.o \
 	md5-internal.o \
-	md5-non-fips.o \
 	milenage.o \
 	ms_funcs.o \
 	rc4.o \
 	sha1.o \
 	sha1-internal.o \
 	sha1-pbkdf2.o \
+	sha1-prf.o \
 	sha1-tlsprf.o \
 	sha1-tprf.o \
 	sha256.o \
+	sha256-prf.o \
 	sha256-tlsprf.o \
 	sha256-internal.o
 
diff --git a/src/crypto/aes-ccm.c b/src/crypto/aes-ccm.c
new file mode 100644
index 0000000..d14670d
--- /dev/null
+++ b/src/crypto/aes-ccm.c
@@ -0,0 +1,212 @@
+/*
+ * Counter with CBC-MAC (CCM) with AES
+ *
+ * Copyright (c) 2010-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+
+static void xor_aes_block(u8 *dst, const u8 *src)
+{
+	u32 *d = (u32 *) dst;
+	u32 *s = (u32 *) src;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+}
+
+
+static void aes_ccm_auth_start(void *aes, size_t M, size_t L, const u8 *nonce,
+			       const u8 *aad, size_t aad_len, size_t plain_len,
+			       u8 *x)
+{
+	u8 aad_buf[2 * AES_BLOCK_SIZE];
+	u8 b[AES_BLOCK_SIZE];
+
+	/* Authentication */
+	/* B_0: Flags | Nonce N | l(m) */
+	b[0] = aad_len ? 0x40 : 0 /* Adata */;
+	b[0] |= (((M - 2) / 2) /* M' */ << 3);
+	b[0] |= (L - 1) /* L' */;
+	os_memcpy(&b[1], nonce, 15 - L);
+	WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len);
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM B_0", b, AES_BLOCK_SIZE);
+	aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */
+
+	if (!aad_len)
+		return;
+
+	WPA_PUT_BE16(aad_buf, aad_len);
+	os_memcpy(aad_buf + 2, aad, aad_len);
+	os_memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len);
+
+	xor_aes_block(aad_buf, x);
+	aes_encrypt(aes, aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */
+
+	if (aad_len > AES_BLOCK_SIZE - 2) {
+		xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x);
+		/* X_3 = E(K, X_2 XOR B_2) */
+		aes_encrypt(aes, &aad_buf[AES_BLOCK_SIZE], x);
+	}
+}
+
+
+static void aes_ccm_auth(void *aes, const u8 *data, size_t len, u8 *x)
+{
+	size_t last = len % AES_BLOCK_SIZE;
+	size_t i;
+
+	for (i = 0; i < len / AES_BLOCK_SIZE; i++) {
+		/* X_i+1 = E(K, X_i XOR B_i) */
+		xor_aes_block(x, data);
+		data += AES_BLOCK_SIZE;
+		aes_encrypt(aes, x, x);
+	}
+	if (last) {
+		/* XOR zero-padded last block */
+		for (i = 0; i < last; i++)
+			x[i] ^= *data++;
+		aes_encrypt(aes, x, x);
+	}
+}
+
+
+static void aes_ccm_encr_start(size_t L, const u8 *nonce, u8 *a)
+{
+	/* A_i = Flags | Nonce N | Counter i */
+	a[0] = L - 1; /* Flags = L' */
+	os_memcpy(&a[1], nonce, 15 - L);
+}
+
+
+static void aes_ccm_encr(void *aes, size_t L, const u8 *in, size_t len, u8 *out,
+			 u8 *a)
+{
+	size_t last = len % AES_BLOCK_SIZE;
+	size_t i;
+
+	/* crypt = msg XOR (S_1 | S_2 | ... | S_n) */
+	for (i = 1; i <= len / AES_BLOCK_SIZE; i++) {
+		WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i);
+		/* S_i = E(K, A_i) */
+		aes_encrypt(aes, a, out);
+		xor_aes_block(out, in);
+		out += AES_BLOCK_SIZE;
+		in += AES_BLOCK_SIZE;
+	}
+	if (last) {
+		WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i);
+		aes_encrypt(aes, a, out);
+		/* XOR zero-padded last block */
+		for (i = 0; i < last; i++)
+			*out++ ^= *in++;
+	}
+}
+
+
+static void aes_ccm_encr_auth(void *aes, size_t M, u8 *x, u8 *a, u8 *auth)
+{
+	size_t i;
+	u8 tmp[AES_BLOCK_SIZE];
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", x, M);
+	/* U = T XOR S_0; S_0 = E(K, A_0) */
+	WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0);
+	aes_encrypt(aes, a, tmp);
+	for (i = 0; i < M; i++)
+		auth[i] = x[i] ^ tmp[i];
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M);
+}
+
+
+static void aes_ccm_decr_auth(void *aes, size_t M, u8 *a, const u8 *auth, u8 *t)
+{
+	size_t i;
+	u8 tmp[AES_BLOCK_SIZE];
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M);
+	/* U = T XOR S_0; S_0 = E(K, A_0) */
+	WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0);
+	aes_encrypt(aes, a, tmp);
+	for (i = 0; i < M; i++)
+		t[i] = auth[i] ^ tmp[i];
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", t, M);
+}
+
+
+/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */
+int aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce,
+	       size_t M, const u8 *plain, size_t plain_len,
+	       const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth)
+{
+	const size_t L = 2;
+	void *aes;
+	u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE];
+
+	if (aad_len > 30 || M > AES_BLOCK_SIZE)
+		return -1;
+
+	aes = aes_encrypt_init(key, key_len);
+	if (aes == NULL)
+		return -1;
+
+	aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, plain_len, x);
+	aes_ccm_auth(aes, plain, plain_len, x);
+
+	/* Encryption */
+	aes_ccm_encr_start(L, nonce, a);
+	aes_ccm_encr(aes, L, plain, plain_len, crypt, a);
+	aes_ccm_encr_auth(aes, M, x, a, auth);
+
+	aes_encrypt_deinit(aes);
+
+	return 0;
+}
+
+
+/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */
+int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce,
+	       size_t M, const u8 *crypt, size_t crypt_len,
+	       const u8 *aad, size_t aad_len, const u8 *auth, u8 *plain)
+{
+	const size_t L = 2;
+	void *aes;
+	u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE];
+	u8 t[AES_BLOCK_SIZE];
+
+	if (aad_len > 30 || M > AES_BLOCK_SIZE)
+		return -1;
+
+	aes = aes_encrypt_init(key, key_len);
+	if (aes == NULL)
+		return -1;
+
+	/* Decryption */
+	aes_ccm_encr_start(L, nonce, a);
+	aes_ccm_decr_auth(aes, M, a, auth, t);
+
+	/* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */
+	aes_ccm_encr(aes, L, crypt, crypt_len, plain, a);
+
+	aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, crypt_len, x);
+	aes_ccm_auth(aes, plain, crypt_len, x);
+
+	aes_encrypt_deinit(aes);
+
+	if (os_memcmp(x, t, M) != 0) {
+		wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/src/crypto/aes-gcm.c b/src/crypto/aes-gcm.c
new file mode 100644
index 0000000..3d91c71
--- /dev/null
+++ b/src/crypto/aes-gcm.c
@@ -0,0 +1,327 @@
+/*
+ * Galois/Counter Mode (GCM) and GMAC with AES
+ *
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+static void inc32(u8 *block)
+{
+	u32 val;
+	val = WPA_GET_BE32(block + AES_BLOCK_SIZE - 4);
+	val++;
+	WPA_PUT_BE32(block + AES_BLOCK_SIZE - 4, val);
+}
+
+
+static void xor_block(u8 *dst, const u8 *src)
+{
+	u32 *d = (u32 *) dst;
+	u32 *s = (u32 *) src;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+}
+
+
+static void shift_right_block(u8 *v)
+{
+	u32 val;
+
+	val = WPA_GET_BE32(v + 12);
+	val >>= 1;
+	if (v[11] & 0x01)
+		val |= 0x80000000;
+	WPA_PUT_BE32(v + 12, val);
+
+	val = WPA_GET_BE32(v + 8);
+	val >>= 1;
+	if (v[7] & 0x01)
+		val |= 0x80000000;
+	WPA_PUT_BE32(v + 8, val);
+
+	val = WPA_GET_BE32(v + 4);
+	val >>= 1;
+	if (v[3] & 0x01)
+		val |= 0x80000000;
+	WPA_PUT_BE32(v + 4, val);
+
+	val = WPA_GET_BE32(v);
+	val >>= 1;
+	WPA_PUT_BE32(v, val);
+}
+
+
+/* Multiplication in GF(2^128) */
+static void gf_mult(const u8 *x, const u8 *y, u8 *z)
+{
+	u8 v[16];
+	int i, j;
+
+	os_memset(z, 0, 16); /* Z_0 = 0^128 */
+	os_memcpy(v, y, 16); /* V_0 = Y */
+
+	for (i = 0; i < 16; i++) {
+		for (j = 0; j < 8; j++) {
+			if (x[i] & BIT(7 - j)) {
+				/* Z_(i + 1) = Z_i XOR V_i */
+				xor_block(z, v);
+			} else {
+				/* Z_(i + 1) = Z_i */
+			}
+
+			if (v[15] & 0x01) {
+				/* V_(i + 1) = (V_i >> 1) XOR R */
+				shift_right_block(v);
+				/* R = 11100001 || 0^120 */
+				v[0] ^= 0xe1;
+			} else {
+				/* V_(i + 1) = V_i >> 1 */
+				shift_right_block(v);
+			}
+		}
+	}
+}
+
+
+static void ghash_start(u8 *y)
+{
+	/* Y_0 = 0^128 */
+	os_memset(y, 0, 16);
+}
+
+
+static void ghash(const u8 *h, const u8 *x, size_t xlen, u8 *y)
+{
+	size_t m, i;
+	const u8 *xpos = x;
+	u8 tmp[16];
+
+	m = xlen / 16;
+
+	for (i = 0; i < m; i++) {
+		/* Y_i = (Y^(i-1) XOR X_i) dot H */
+		xor_block(y, xpos);
+		xpos += 16;
+
+		/* dot operation:
+		 * multiplication operation for binary Galois (finite) field of
+		 * 2^128 elements */
+		gf_mult(y, h, tmp);
+		os_memcpy(y, tmp, 16);
+	}
+
+	if (x + xlen > xpos) {
+		/* Add zero padded last block */
+		size_t last = x + xlen - xpos;
+		os_memcpy(tmp, xpos, last);
+		os_memset(tmp + last, 0, sizeof(tmp) - last);
+
+		/* Y_i = (Y^(i-1) XOR X_i) dot H */
+		xor_block(y, tmp);
+
+		/* dot operation:
+		 * multiplication operation for binary Galois (finite) field of
+		 * 2^128 elements */
+		gf_mult(y, h, tmp);
+		os_memcpy(y, tmp, 16);
+	}
+
+	/* Return Y_m */
+}
+
+
+static void aes_gctr(void *aes, const u8 *icb, const u8 *x, size_t xlen, u8 *y)
+{
+	size_t i, n, last;
+	u8 cb[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
+	const u8 *xpos = x;
+	u8 *ypos = y;
+
+	if (xlen == 0)
+		return;
+
+	n = xlen / 16;
+
+	os_memcpy(cb, icb, AES_BLOCK_SIZE);
+	/* Full blocks */
+	for (i = 0; i < n; i++) {
+		aes_encrypt(aes, cb, ypos);
+		xor_block(ypos, xpos);
+		xpos += AES_BLOCK_SIZE;
+		ypos += AES_BLOCK_SIZE;
+		inc32(cb);
+	}
+
+	last = x + xlen - xpos;
+	if (last) {
+		/* Last, partial block */
+		aes_encrypt(aes, cb, tmp);
+		for (i = 0; i < last; i++)
+			*ypos++ = *xpos++ ^ tmp[i];
+	}
+}
+
+
+static void * aes_gcm_init_hash_subkey(const u8 *key, size_t key_len, u8 *H)
+{
+	void *aes;
+
+	aes = aes_encrypt_init(key, key_len);
+	if (aes == NULL)
+		return NULL;
+
+	/* Generate hash subkey H = AES_K(0^128) */
+	os_memset(H, 0, AES_BLOCK_SIZE);
+	aes_encrypt(aes, H, H);
+	wpa_hexdump_key(MSG_EXCESSIVE, "Hash subkey H for GHASH",
+			H, AES_BLOCK_SIZE);
+	return aes;
+}
+
+
+static void aes_gcm_prepare_j0(const u8 *iv, size_t iv_len, const u8 *H, u8 *J0)
+{
+	u8 len_buf[16];
+
+	if (iv_len == 12) {
+		/* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */
+		os_memcpy(J0, iv, iv_len);
+		os_memset(J0 + iv_len, 0, AES_BLOCK_SIZE - iv_len);
+		J0[AES_BLOCK_SIZE - 1] = 0x01;
+	} else {
+		/*
+		 * s = 128 * ceil(len(IV)/128) - len(IV)
+		 * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64)
+		 */
+		ghash_start(J0);
+		ghash(H, iv, iv_len, J0);
+		WPA_PUT_BE64(len_buf, 0);
+		WPA_PUT_BE64(len_buf + 8, iv_len * 8);
+		ghash(H, len_buf, sizeof(len_buf), J0);
+	}
+}
+
+
+static void aes_gcm_gctr(void *aes, const u8 *J0, const u8 *in, size_t len,
+			 u8 *out)
+{
+	u8 J0inc[AES_BLOCK_SIZE];
+
+	if (len == 0)
+		return;
+
+	os_memcpy(J0inc, J0, AES_BLOCK_SIZE);
+	inc32(J0inc);
+	aes_gctr(aes, J0inc, in, len, out);
+}
+
+
+static void aes_gcm_ghash(const u8 *H, const u8 *aad, size_t aad_len,
+			  const u8 *crypt, size_t crypt_len, u8 *S)
+{
+	u8 len_buf[16];
+
+	/*
+	 * u = 128 * ceil[len(C)/128] - len(C)
+	 * v = 128 * ceil[len(A)/128] - len(A)
+	 * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64)
+	 * (i.e., zero padded to block size A || C and lengths of each in bits)
+	 */
+	ghash_start(S);
+	ghash(H, aad, aad_len, S);
+	ghash(H, crypt, crypt_len, S);
+	WPA_PUT_BE64(len_buf, aad_len * 8);
+	WPA_PUT_BE64(len_buf + 8, crypt_len * 8);
+	ghash(H, len_buf, sizeof(len_buf), S);
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "S = GHASH_H(...)", S, 16);
+}
+
+
+/**
+ * aes_gcm_ae - GCM-AE_K(IV, P, A)
+ */
+int aes_gcm_ae(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+	       const u8 *plain, size_t plain_len,
+	       const u8 *aad, size_t aad_len, u8 *crypt, u8 *tag)
+{
+	u8 H[AES_BLOCK_SIZE];
+	u8 J0[AES_BLOCK_SIZE];
+	u8 S[16];
+	void *aes;
+
+	aes = aes_gcm_init_hash_subkey(key, key_len, H);
+	if (aes == NULL)
+		return -1;
+
+	aes_gcm_prepare_j0(iv, iv_len, H, J0);
+
+	/* C = GCTR_K(inc_32(J_0), P) */
+	aes_gcm_gctr(aes, J0, plain, plain_len, crypt);
+
+	aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S);
+
+	/* T = MSB_t(GCTR_K(J_0, S)) */
+	aes_gctr(aes, J0, S, sizeof(S), tag);
+
+	/* Return (C, T) */
+
+	aes_encrypt_deinit(aes);
+
+	return 0;
+}
+
+
+/**
+ * aes_gcm_ad - GCM-AD_K(IV, C, A, T)
+ */
+int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+	       const u8 *crypt, size_t crypt_len,
+	       const u8 *aad, size_t aad_len, const u8 *tag, u8 *plain)
+{
+	u8 H[AES_BLOCK_SIZE];
+	u8 J0[AES_BLOCK_SIZE];
+	u8 S[16], T[16];
+	void *aes;
+
+	aes = aes_gcm_init_hash_subkey(key, key_len, H);
+	if (aes == NULL)
+		return -1;
+
+	aes_gcm_prepare_j0(iv, iv_len, H, J0);
+
+	/* P = GCTR_K(inc_32(J_0), C) */
+	aes_gcm_gctr(aes, J0, crypt, crypt_len, plain);
+
+	aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S);
+
+	/* T' = MSB_t(GCTR_K(J_0, S)) */
+	aes_gctr(aes, J0, S, sizeof(S), T);
+
+	aes_encrypt_deinit(aes);
+
+	if (os_memcmp(tag, T, 16) != 0) {
+		wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+	     const u8 *aad, size_t aad_len, u8 *tag)
+{
+	return aes_gcm_ae(key, key_len, iv, iv_len, NULL, 0, aad, aad_len, NULL,
+			  tag);
+}
diff --git a/src/crypto/aes-internal-dec.c b/src/crypto/aes-internal-dec.c
index 70ce310..720c703 100644
--- a/src/crypto/aes-internal-dec.c
+++ b/src/crypto/aes-internal-dec.c
@@ -2,14 +2,13 @@
  * AES (Rijndael) cipher - decrypt
  *
  * Modifications to public domain implementation:
- * - support only 128-bit keys
  * - cleanup
  * - use C pre-processor to make it easier to change S table access
  * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
  *   cost of reduced throughput (quite small difference on Pentium 4,
  *   10-25% when using -O1 or -O2 optimization)
  *
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -26,13 +25,15 @@
  *
  * @return	the number of rounds for the given cipher key size.
  */
-static void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[])
+static int rijndaelKeySetupDec(u32 rk[], const u8 cipherKey[], int keyBits)
 {
-	int Nr = 10, i, j;
+	int Nr, i, j;
 	u32 temp;
 
 	/* expand the cipher key: */
-	rijndaelKeySetupEnc(rk, cipherKey);
+	Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits);
+	if (Nr < 0)
+		return Nr;
 	/* invert the order of the round keys: */
 	for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) {
 		temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
@@ -51,24 +52,30 @@
 				TD3_(TE4((rk[j]      ) & 0xff));
 		}
 	}
+
+	return Nr;
 }
 
 void * aes_decrypt_init(const u8 *key, size_t len)
 {
 	u32 *rk;
-	if (len != 16)
-		return NULL;
+	int res;
 	rk = os_malloc(AES_PRIV_SIZE);
 	if (rk == NULL)
 		return NULL;
-	rijndaelKeySetupDec(rk, key);
+	res = rijndaelKeySetupDec(rk, key, len * 8);
+	if (res < 0) {
+		os_free(rk);
+		return NULL;
+	}
+	rk[AES_PRIV_NR_POS] = res;
 	return rk;
 }
 
-static void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16])
+static void rijndaelDecrypt(const u32 rk[/*44*/], int Nr, const u8 ct[16],
+			    u8 pt[16])
 {
 	u32 s0, s1, s2, s3, t0, t1, t2, t3;
-	const int Nr = 10;
 #ifndef FULL_UNROLL
 	int r;
 #endif /* ?FULL_UNROLL */
@@ -99,6 +106,14 @@
 	ROUND(7,t,s);
 	ROUND(8,s,t);
 	ROUND(9,t,s);
+	if (Nr > 10) {
+		ROUND(10,s,t);
+		ROUND(11,t,s);
+		if (Nr > 12) {
+			ROUND(12,s,t);
+			ROUND(13,t,s);
+		}
+	}
 
 	rk += Nr << 2;
 
@@ -134,7 +149,8 @@
 
 void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
 {
-	rijndaelDecrypt(ctx, crypt, plain);
+	u32 *rk = ctx;
+	rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain);
 }
 
 
diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c
index 645bc92..f3c61b8 100644
--- a/src/crypto/aes-internal-enc.c
+++ b/src/crypto/aes-internal-enc.c
@@ -2,14 +2,13 @@
  * AES (Rijndael) cipher - encrypt
  *
  * Modifications to public domain implementation:
- * - support only 128-bit keys
  * - cleanup
  * - use C pre-processor to make it easier to change S table access
  * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
  *   cost of reduced throughput (quite small difference on Pentium 4,
  *   10-25% when using -O1 or -O2 optimization)
  *
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -21,10 +20,9 @@
 #include "crypto.h"
 #include "aes_i.h"
 
-static void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16])
+static void rijndaelEncrypt(const u32 rk[], int Nr, const u8 pt[16], u8 ct[16])
 {
 	u32 s0, s1, s2, s3, t0, t1, t2, t3;
-	const int Nr = 10;
 #ifndef FULL_UNROLL
 	int r;
 #endif /* ?FULL_UNROLL */
@@ -55,6 +53,14 @@
 	ROUND(7,t,s);
 	ROUND(8,s,t);
 	ROUND(9,t,s);
+	if (Nr > 10) {
+		ROUND(10,s,t);
+		ROUND(11,t,s);
+		if (Nr > 12) {
+			ROUND(12,s,t);
+			ROUND(13,t,s);
+		}
+	}
 
 	rk += Nr << 2;
 
@@ -92,19 +98,24 @@
 void * aes_encrypt_init(const u8 *key, size_t len)
 {
 	u32 *rk;
-	if (len != 16)
-		return NULL;
+	int res;
 	rk = os_malloc(AES_PRIV_SIZE);
 	if (rk == NULL)
 		return NULL;
-	rijndaelKeySetupEnc(rk, key);
+	res = rijndaelKeySetupEnc(rk, key, len * 8);
+	if (res < 0) {
+		os_free(rk);
+		return NULL;
+	}
+	rk[AES_PRIV_NR_POS] = res;
 	return rk;
 }
 
 
 void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
 {
-	rijndaelEncrypt(ctx, plain, crypt);
+	u32 *rk = ctx;
+	rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt);
 }
 
 
diff --git a/src/crypto/aes-internal.c b/src/crypto/aes-internal.c
index 698606f..bd4535d 100644
--- a/src/crypto/aes-internal.c
+++ b/src/crypto/aes-internal.c
@@ -2,14 +2,13 @@
  * AES (Rijndael) cipher
  *
  * Modifications to public domain implementation:
- * - support only 128-bit keys
  * - cleanup
  * - use C pre-processor to make it easier to change S table access
  * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
  *   cost of reduced throughput (quite small difference on Pentium 4,
  *   10-25% when using -O1 or -O2 optimization)
  *
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -777,7 +776,7 @@
  *
  * @return	the number of rounds for the given cipher key size.
  */
-void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[])
+int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits)
 {
 	int i;
 	u32 temp;
@@ -786,14 +785,61 @@
 	rk[1] = GETU32(cipherKey +  4);
 	rk[2] = GETU32(cipherKey +  8);
 	rk[3] = GETU32(cipherKey + 12);
-	for (i = 0; i < 10; i++) {
-		temp  = rk[3];
-		rk[4] = rk[0] ^
-			TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^
-			RCON(i);
-		rk[5] = rk[1] ^ rk[4];
-		rk[6] = rk[2] ^ rk[5];
-		rk[7] = rk[3] ^ rk[6];
-		rk += 4;
+
+	if (keyBits == 128) {
+		for (i = 0; i < 10; i++) {
+			temp  = rk[3];
+			rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+				TE443(temp) ^ TE414(temp) ^ RCON(i);
+			rk[5] = rk[1] ^ rk[4];
+			rk[6] = rk[2] ^ rk[5];
+			rk[7] = rk[3] ^ rk[6];
+			rk += 4;
+		}
+		return 10;
 	}
+
+	rk[4] = GETU32(cipherKey + 16);
+	rk[5] = GETU32(cipherKey + 20);
+
+	if (keyBits == 192) {
+		for (i = 0; i < 8; i++) {
+			temp  = rk[5];
+			rk[6] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+				TE443(temp) ^ TE414(temp) ^ RCON(i);
+			rk[7] = rk[1] ^ rk[6];
+			rk[8] = rk[2] ^ rk[7];
+			rk[9] = rk[3] ^ rk[8];
+			if (i == 7)
+				return 12;
+			rk[10] = rk[4] ^ rk[9];
+			rk[11] = rk[5] ^ rk[10];
+			rk += 6;
+		}
+	}
+
+	rk[6] = GETU32(cipherKey + 24);
+	rk[7] = GETU32(cipherKey + 28);
+
+	if (keyBits == 256) {
+		for (i = 0; i < 7; i++) {
+			temp  = rk[7];
+			rk[8] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+				TE443(temp) ^ TE414(temp) ^ RCON(i);
+			rk[9] = rk[1] ^ rk[8];
+			rk[10] = rk[2] ^ rk[9];
+			rk[11] = rk[3] ^ rk[10];
+			if (i == 6)
+				return 14;
+			temp  = rk[11];
+			rk[12] = rk[4] ^ TE411(temp) ^ TE422(temp) ^
+				TE433(temp) ^ TE444(temp);
+			rk[13] = rk[5] ^ rk[12];
+			rk[14] = rk[6] ^ rk[13];
+			rk[15] = rk[7] ^ rk[14];
+			rk += 8;
+		}
+	}
+
+	return -1;
 }
diff --git a/src/crypto/aes_i.h b/src/crypto/aes_i.h
index 163d4f6..54375cf 100644
--- a/src/crypto/aes_i.h
+++ b/src/crypto/aes_i.h
@@ -1,6 +1,6 @@
 /*
  * AES (Rijndael) cipher
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -44,6 +44,10 @@
 #define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000)
 #define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00)
 #define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff)
+#define TE411(i) (Te4[((i) >> 24) & 0xff] & 0xff000000)
+#define TE422(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE433(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE444(i) (Te4[(i) & 0xff] & 0x000000ff)
 #define TE4(i) (Te4[(i)] & 0x000000ff)
 
 #define TD0(i) Td0[((i) >> 24) & 0xff]
@@ -80,6 +84,10 @@
 #define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000)
 #define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00)
 #define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff)
+#define TE411(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000)
+#define TE422(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE433(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE444(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff)
 #define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff)
 
 #define TD0(i) Td0[((i) >> 24) & 0xff]
@@ -109,8 +117,9 @@
 (ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }
 #endif
 
-#define AES_PRIV_SIZE (4 * 44)
+#define AES_PRIV_SIZE (4 * 4 * 15 + 4)
+#define AES_PRIV_NR_POS (4 * 15)
 
-void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]);
+int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits);
 
 #endif /* AES_I_H */
diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h
index 4791792..0433c04 100644
--- a/src/crypto/aes_wrap.h
+++ b/src/crypto/aes_wrap.h
@@ -6,8 +6,10 @@
  * - AES-128 CTR mode encryption
  * - AES-128 EAX mode encryption/decryption
  * - AES-128 CBC
+ * - AES-GCM
+ * - AES-CCM
  *
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -38,5 +40,25 @@
 				     size_t data_len);
 int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data,
 				     size_t data_len);
+int __must_check aes_gcm_ae(const u8 *key, size_t key_len,
+			    const u8 *iv, size_t iv_len,
+			    const u8 *plain, size_t plain_len,
+			    const u8 *aad, size_t aad_len,
+			    u8 *crypt, u8 *tag);
+int __must_check aes_gcm_ad(const u8 *key, size_t key_len,
+			    const u8 *iv, size_t iv_len,
+			    const u8 *crypt, size_t crypt_len,
+			    const u8 *aad, size_t aad_len, const u8 *tag,
+			    u8 *plain);
+int __must_check aes_gmac(const u8 *key, size_t key_len,
+			  const u8 *iv, size_t iv_len,
+			  const u8 *aad, size_t aad_len, u8 *tag);
+int __must_check aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce,
+			    size_t M, const u8 *plain, size_t plain_len,
+			    const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth);
+int __must_check aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce,
+			    size_t M, const u8 *crypt, size_t crypt_len,
+			    const u8 *aad, size_t aad_len, const u8 *auth,
+			    u8 *plain);
 
 #endif /* AES_WRAP_H */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 7e61cb9..26b9acf 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -41,21 +41,6 @@
  */
 int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
 
-#ifdef CONFIG_FIPS
-/**
- * md5_vector_non_fips_allow - MD5 hash for data vector (non-FIPS use allowed)
- * @num_elem: Number of elements in the data vector
- * @addr: Pointers to the data areas
- * @len: Lengths of the data blocks
- * @mac: Buffer for the hash
- * Returns: 0 on success, -1 on failure
- */
-int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[],
-			      const size_t *len, u8 *mac);
-#else /* CONFIG_FIPS */
-#define md5_vector_non_fips_allow md5_vector
-#endif /* CONFIG_FIPS */
-
 
 /**
  * sha1_vector - SHA-1 hash for data vector
@@ -461,4 +446,15 @@
 int rc4_skip(const u8 *key, size_t keylen, size_t skip,
 	     u8 *data, size_t data_len);
 
+/**
+ * crypto_get_random - Generate cryptographically strong pseudy-random bytes
+ * @buf: Buffer for data
+ * @len: Number of bytes to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * If the PRNG does not have enough entropy to ensure unpredictable byte
+ * sequence, this functions must return -1.
+ */
+int crypto_get_random(void *buf, size_t len);
+
 #endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_internal-cipher.c b/src/crypto/crypto_internal-cipher.c
index 4bd711d..ad0930a 100644
--- a/src/crypto/crypto_internal-cipher.c
+++ b/src/crypto/crypto_internal-cipher.c
@@ -24,7 +24,6 @@
 		} rc4;
 		struct {
 			u8 cbc[32];
-			size_t block_size;
 			void *ctx_enc;
 			void *ctx_dec;
 		} aes;
@@ -63,10 +62,6 @@
 		os_memcpy(ctx->u.rc4.key, key, key_len);
 		break;
 	case CRYPTO_CIPHER_ALG_AES:
-		if (key_len > sizeof(ctx->u.aes.cbc)) {
-			os_free(ctx);
-			return NULL;
-		}
 		ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len);
 		if (ctx->u.aes.ctx_enc == NULL) {
 			os_free(ctx);
@@ -78,8 +73,7 @@
 			os_free(ctx);
 			return NULL;
 		}
-		ctx->u.aes.block_size = key_len;
-		os_memcpy(ctx->u.aes.cbc, iv, ctx->u.aes.block_size);
+		os_memcpy(ctx->u.aes.cbc, iv, AES_BLOCK_SIZE);
 		break;
 	case CRYPTO_CIPHER_ALG_3DES:
 		if (key_len != 24) {
@@ -120,18 +114,17 @@
 		ctx->u.rc4.used_bytes += len;
 		break;
 	case CRYPTO_CIPHER_ALG_AES:
-		if (len % ctx->u.aes.block_size)
+		if (len % AES_BLOCK_SIZE)
 			return -1;
-		blocks = len / ctx->u.aes.block_size;
+		blocks = len / AES_BLOCK_SIZE;
 		for (i = 0; i < blocks; i++) {
-			for (j = 0; j < ctx->u.aes.block_size; j++)
+			for (j = 0; j < AES_BLOCK_SIZE; j++)
 				ctx->u.aes.cbc[j] ^= plain[j];
 			aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc,
 				    ctx->u.aes.cbc);
-			os_memcpy(crypt, ctx->u.aes.cbc,
-				  ctx->u.aes.block_size);
-			plain += ctx->u.aes.block_size;
-			crypt += ctx->u.aes.block_size;
+			os_memcpy(crypt, ctx->u.aes.cbc, AES_BLOCK_SIZE);
+			plain += AES_BLOCK_SIZE;
+			crypt += AES_BLOCK_SIZE;
 		}
 		break;
 	case CRYPTO_CIPHER_ALG_3DES:
@@ -185,17 +178,17 @@
 		ctx->u.rc4.used_bytes += len;
 		break;
 	case CRYPTO_CIPHER_ALG_AES:
-		if (len % ctx->u.aes.block_size)
+		if (len % AES_BLOCK_SIZE)
 			return -1;
-		blocks = len / ctx->u.aes.block_size;
+		blocks = len / AES_BLOCK_SIZE;
 		for (i = 0; i < blocks; i++) {
-			os_memcpy(tmp, crypt, ctx->u.aes.block_size);
+			os_memcpy(tmp, crypt, AES_BLOCK_SIZE);
 			aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain);
-			for (j = 0; j < ctx->u.aes.block_size; j++)
+			for (j = 0; j < AES_BLOCK_SIZE; j++)
 				plain[j] ^= ctx->u.aes.cbc[j];
-			os_memcpy(ctx->u.aes.cbc, tmp, ctx->u.aes.block_size);
-			plain += ctx->u.aes.block_size;
-			crypt += ctx->u.aes.block_size;
+			os_memcpy(ctx->u.aes.cbc, tmp, AES_BLOCK_SIZE);
+			plain += AES_BLOCK_SIZE;
+			crypt += AES_BLOCK_SIZE;
 		}
 		break;
 	case CRYPTO_CIPHER_ALG_3DES:
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 1171f29..711e312 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / wrapper functions for libcrypto
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,6 +14,11 @@
 #include <openssl/bn.h>
 #include <openssl/evp.h>
 #include <openssl/dh.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#ifdef CONFIG_OPENSSL_CMAC
+#include <openssl/cmac.h>
+#endif /* CONFIG_OPENSSL_CMAC */
 
 #include "common.h"
 #include "wpabuf.h"
@@ -68,21 +73,14 @@
 #define NO_SHA256_WRAPPER
 #endif
 
-static int openssl_digest_vector(const EVP_MD *type, int non_fips,
-				 size_t num_elem, const u8 *addr[],
-				 const size_t *len, u8 *mac)
+static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
+				 const u8 *addr[], const size_t *len, u8 *mac)
 {
 	EVP_MD_CTX ctx;
 	size_t i;
 	unsigned int mac_len;
 
 	EVP_MD_CTX_init(&ctx);
-#ifdef CONFIG_FIPS
-#ifdef OPENSSL_FIPS
-	if (non_fips)
-		EVP_MD_CTX_set_flags(&ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
-#endif /* OPENSSL_FIPS */
-#endif /* CONFIG_FIPS */
 	if (!EVP_DigestInit_ex(&ctx, type, NULL)) {
 		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
@@ -108,7 +106,7 @@
 
 int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
-	return openssl_digest_vector(EVP_md4(), 0, num_elem, addr, len, mac);
+	return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
 }
 
 
@@ -172,22 +170,13 @@
 
 int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
-	return openssl_digest_vector(EVP_md5(), 0, num_elem, addr, len, mac);
+	return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
 }
 
 
-#ifdef CONFIG_FIPS
-int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[],
-			      const size_t *len, u8 *mac)
-{
-	return openssl_digest_vector(EVP_md5(), 1, num_elem, addr, len, mac);
-}
-#endif /* CONFIG_FIPS */
-
-
 int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
-	return openssl_digest_vector(EVP_sha1(), 0, num_elem, addr, len, mac);
+	return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac);
 }
 
 
@@ -195,60 +184,124 @@
 int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
 		  u8 *mac)
 {
-	return openssl_digest_vector(EVP_sha256(), 0, num_elem, addr, len,
-				     mac);
+	return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac);
 }
 #endif /* NO_SHA256_WRAPPER */
 
 
+static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
+{
+	switch (keylen) {
+	case 16:
+		return EVP_aes_128_ecb();
+	case 24:
+		return EVP_aes_192_ecb();
+	case 32:
+		return EVP_aes_256_ecb();
+	}
+
+	return NULL;
+}
+
+
 void * aes_encrypt_init(const u8 *key, size_t len)
 {
-	AES_KEY *ak;
-	ak = os_malloc(sizeof(*ak));
-	if (ak == NULL)
+	EVP_CIPHER_CTX *ctx;
+	const EVP_CIPHER *type;
+
+	type = aes_get_evp_cipher(len);
+	if (type == NULL)
 		return NULL;
-	if (AES_set_encrypt_key(key, 8 * len, ak) < 0) {
-		os_free(ak);
+
+	ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	EVP_CIPHER_CTX_init(ctx);
+	if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+		os_free(ctx);
 		return NULL;
 	}
-	return ak;
+	EVP_CIPHER_CTX_set_padding(ctx, 0);
+	return ctx;
 }
 
 
 void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
 {
-	AES_encrypt(plain, crypt, ctx);
+	EVP_CIPHER_CTX *c = ctx;
+	int clen = 16;
+	if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+	}
 }
 
 
 void aes_encrypt_deinit(void *ctx)
 {
-	os_free(ctx);
+	EVP_CIPHER_CTX *c = ctx;
+	u8 buf[16];
+	int len = sizeof(buf);
+	if (EVP_EncryptFinal_ex(c, buf, &len) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: "
+			   "%s", ERR_error_string(ERR_get_error(), NULL));
+	}
+	if (len != 0) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+			   "in AES encrypt", len);
+	}
+	EVP_CIPHER_CTX_cleanup(c);
+	os_free(c);
 }
 
 
 void * aes_decrypt_init(const u8 *key, size_t len)
 {
-	AES_KEY *ak;
-	ak = os_malloc(sizeof(*ak));
-	if (ak == NULL)
+	EVP_CIPHER_CTX *ctx;
+	const EVP_CIPHER *type;
+
+	type = aes_get_evp_cipher(len);
+	if (type == NULL)
 		return NULL;
-	if (AES_set_decrypt_key(key, 8 * len, ak) < 0) {
-		os_free(ak);
+
+	ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	EVP_CIPHER_CTX_init(ctx);
+	if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+		os_free(ctx);
 		return NULL;
 	}
-	return ak;
+	EVP_CIPHER_CTX_set_padding(ctx, 0);
+	return ctx;
 }
 
 
 void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
 {
-	AES_decrypt(crypt, plain, ctx);
+	EVP_CIPHER_CTX *c = ctx;
+	int plen = 16;
+	if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+	}
 }
 
 
 void aes_decrypt_deinit(void *ctx)
 {
+	EVP_CIPHER_CTX *c = ctx;
+	u8 buf[16];
+	int len = sizeof(buf);
+	if (EVP_DecryptFinal_ex(c, buf, &len) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: "
+			   "%s", ERR_error_string(ERR_get_error(), NULL));
+	}
+	if (len != 0) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+			   "in AES decrypt", len);
+	}
+	EVP_CIPHER_CTX_cleanup(c);
 	os_free(ctx);
 }
 
@@ -452,6 +505,41 @@
 }
 
 
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+	DH *dh;
+
+	dh = DH_new();
+	if (dh == NULL)
+		return NULL;
+
+	dh->g = BN_new();
+	if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
+		goto err;
+
+	dh->p = get_group5_prime();
+	if (dh->p == NULL)
+		goto err;
+
+	dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
+	if (dh->priv_key == NULL)
+		goto err;
+
+	dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
+	if (dh->pub_key == NULL)
+		goto err;
+
+	if (DH_generate_key(dh) != 1)
+		goto err;
+
+	return dh;
+
+err:
+	DH_free(dh);
+	return NULL;
+}
+
+
 struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
 				  const struct wpabuf *own_private)
 {
@@ -497,3 +585,236 @@
 	dh = ctx;
 	DH_free(dh);
 }
+
+
+struct crypto_hash {
+	HMAC_CTX ctx;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ctx;
+	const EVP_MD *md;
+
+	switch (alg) {
+#ifndef OPENSSL_NO_MD5
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		md = EVP_md5();
+		break;
+#endif /* OPENSSL_NO_MD5 */
+#ifndef OPENSSL_NO_SHA
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		md = EVP_sha1();
+		break;
+#endif /* OPENSSL_NO_SHA */
+#ifndef OPENSSL_NO_SHA256
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		md = EVP_sha256();
+		break;
+#endif /* CONFIG_SHA256 */
+#endif /* OPENSSL_NO_SHA256 */
+	default:
+		return NULL;
+	}
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	HMAC_CTX_init(&ctx->ctx);
+
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL);
+#else /* openssl < 0.9.9 */
+	if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) {
+		os_free(ctx);
+		return NULL;
+	}
+#endif /* openssl < 0.9.9 */
+
+	return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	if (ctx == NULL)
+		return;
+	HMAC_Update(&ctx->ctx, data, len);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	unsigned int mdlen;
+	int res;
+
+	if (ctx == NULL)
+		return -2;
+
+	if (mac == NULL || len == NULL) {
+		os_free(ctx);
+		return 0;
+	}
+
+	mdlen = *len;
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Final(&ctx->ctx, mac, &mdlen);
+	res = 1;
+#else /* openssl < 0.9.9 */
+	res = HMAC_Final(&ctx->ctx, mac, &mdlen);
+#endif /* openssl < 0.9.9 */
+	HMAC_CTX_cleanup(&ctx->ctx);
+	os_free(ctx);
+
+	if (res == 1) {
+		*len = mdlen;
+		return 0;
+	}
+
+	return -1;
+}
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen)
+{
+#if OPENSSL_VERSION_NUMBER < 0x00908000
+	if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase),
+				   (unsigned char *) ssid,
+				   ssid_len, 4096, buflen, buf) != 1)
+		return -1;
+#else /* openssl < 0.9.8 */
+	if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
+				   ssid_len, 4096, buflen, buf) != 1)
+		return -1;
+#endif /* openssl < 0.9.8 */
+	return 0;
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	HMAC_CTX ctx;
+	size_t i;
+	unsigned int mdlen;
+	int res;
+
+	HMAC_CTX_init(&ctx);
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL);
+#else /* openssl < 0.9.9 */
+	if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1)
+		return -1;
+#endif /* openssl < 0.9.9 */
+
+	for (i = 0; i < num_elem; i++)
+		HMAC_Update(&ctx, addr[i], len[i]);
+
+	mdlen = 20;
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Final(&ctx, mac, &mdlen);
+	res = 1;
+#else /* openssl < 0.9.9 */
+	res = HMAC_Final(&ctx, mac, &mdlen);
+#endif /* openssl < 0.9.9 */
+	HMAC_CTX_cleanup(&ctx);
+
+	return res == 1 ? 0 : -1;
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	HMAC_CTX ctx;
+	size_t i;
+	unsigned int mdlen;
+	int res;
+
+	HMAC_CTX_init(&ctx);
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL);
+#else /* openssl < 0.9.9 */
+	if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL) != 1)
+		return -1;
+#endif /* openssl < 0.9.9 */
+
+	for (i = 0; i < num_elem; i++)
+		HMAC_Update(&ctx, addr[i], len[i]);
+
+	mdlen = 32;
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Final(&ctx, mac, &mdlen);
+	res = 1;
+#else /* openssl < 0.9.9 */
+	res = HMAC_Final(&ctx, mac, &mdlen);
+#endif /* openssl < 0.9.9 */
+	HMAC_CTX_cleanup(&ctx);
+
+	return res == 1 ? 0 : -1;
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+int crypto_get_random(void *buf, size_t len)
+{
+	if (RAND_bytes(buf, len) != 1)
+		return -1;
+	return 0;
+}
+
+
+#ifdef CONFIG_OPENSSL_CMAC
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	CMAC_CTX *ctx;
+	int ret = -1;
+	size_t outlen, i;
+
+	ctx = CMAC_CTX_new();
+	if (ctx == NULL)
+		return -1;
+
+	if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL))
+		goto fail;
+	for (i = 0; i < num_elem; i++) {
+		if (!CMAC_Update(ctx, addr[i], len[i]))
+			goto fail;
+	}
+	if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16)
+		goto fail;
+
+	ret = 0;
+fail:
+	CMAC_CTX_free(ctx);
+	return ret;
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+#endif /* CONFIG_OPENSSL_CMAC */
diff --git a/src/crypto/dh_group5.c b/src/crypto/dh_group5.c
index 9a94ca5..ccdbfc8 100644
--- a/src/crypto/dh_group5.c
+++ b/src/crypto/dh_group5.c
@@ -1,6 +1,6 @@
 /*
  * Diffie-Hellman group 5 operations
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,6 +22,12 @@
 }
 
 
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+	return (void *) 1;
+}
+
+
 struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
 				  const struct wpabuf *own_private)
 {
diff --git a/src/crypto/dh_group5.h b/src/crypto/dh_group5.h
index 8813427..abee8ea 100644
--- a/src/crypto/dh_group5.h
+++ b/src/crypto/dh_group5.h
@@ -1,6 +1,6 @@
 /*
  * Diffie-Hellman group 5 operations
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 #define DH_GROUP5_H
 
 void * dh5_init(struct wpabuf **priv, struct wpabuf **publ);
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ);
 struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
 				  const struct wpabuf *own_private);
 void dh5_free(void *ctx);
diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c
index 92f0b57..d69ecea 100644
--- a/src/crypto/fips_prf_openssl.c
+++ b/src/crypto/fips_prf_openssl.c
@@ -31,13 +31,14 @@
 	u8 *xpos = x;
 	u32 carry;
 
-	if (seed_len > sizeof(xkey))
+	if (seed_len < sizeof(xkey))
+		os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+	else
 		seed_len = sizeof(xkey);
 
 	/* FIPS 186-2 + change notice 1 */
 
 	os_memcpy(xkey, seed, seed_len);
-	os_memset(xkey + seed_len, 0, 64 - seed_len);
 	t[0] = 0x67452301;
 	t[1] = 0xEFCDAB89;
 	t[2] = 0x98BADCFE;
diff --git a/src/crypto/md5-non-fips.c b/src/crypto/md5-non-fips.c
deleted file mode 100644
index 418f782..0000000
--- a/src/crypto/md5-non-fips.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * MD5 hash implementation and interface functions (non-FIPS allowed cases)
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "md5.h"
-#include "crypto.h"
-
-
-/**
- * hmac_md5_vector_non_fips_allow - HMAC-MD5 over data vector (RFC 2104)
- * @key: Key for HMAC operations
- * @key_len: Length of the key in bytes
- * @num_elem: Number of elements in the data vector
- * @addr: Pointers to the data areas
- * @len: Lengths of the data blocks
- * @mac: Buffer for the hash (16 bytes)
- * Returns: 0 on success, -1 on failure
- */
-int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len,
-				   size_t num_elem, const u8 *addr[],
-				   const size_t *len, u8 *mac)
-{
-	u8 k_pad[64]; /* padding - key XORd with ipad/opad */
-	u8 tk[16];
-	const u8 *_addr[6];
-	size_t i, _len[6];
-
-	if (num_elem > 5) {
-		/*
-		 * Fixed limit on the number of fragments to avoid having to
-		 * allocate memory (which could fail).
-		 */
-		return -1;
-	}
-
-        /* if key is longer than 64 bytes reset it to key = MD5(key) */
-        if (key_len > 64) {
-		if (md5_vector_non_fips_allow(1, &key, &key_len, tk))
-			return -1;
-		key = tk;
-		key_len = 16;
-        }
-
-	/* the HMAC_MD5 transform looks like:
-	 *
-	 * MD5(K XOR opad, MD5(K XOR ipad, text))
-	 *
-	 * where K is an n byte key
-	 * ipad is the byte 0x36 repeated 64 times
-	 * opad is the byte 0x5c repeated 64 times
-	 * and text is the data being protected */
-
-	/* start out by storing key in ipad */
-	os_memset(k_pad, 0, sizeof(k_pad));
-	os_memcpy(k_pad, key, key_len);
-
-	/* XOR key with ipad values */
-	for (i = 0; i < 64; i++)
-		k_pad[i] ^= 0x36;
-
-	/* perform inner MD5 */
-	_addr[0] = k_pad;
-	_len[0] = 64;
-	for (i = 0; i < num_elem; i++) {
-		_addr[i + 1] = addr[i];
-		_len[i + 1] = len[i];
-	}
-	if (md5_vector_non_fips_allow(1 + num_elem, _addr, _len, mac))
-		return -1;
-
-	os_memset(k_pad, 0, sizeof(k_pad));
-	os_memcpy(k_pad, key, key_len);
-	/* XOR key with opad values */
-	for (i = 0; i < 64; i++)
-		k_pad[i] ^= 0x5c;
-
-	/* perform outer MD5 */
-	_addr[0] = k_pad;
-	_len[0] = 64;
-	_addr[1] = mac;
-	_len[1] = MD5_MAC_LEN;
-	return md5_vector_non_fips_allow(2, _addr, _len, mac);
-}
-
-
-/**
- * hmac_md5_non_fips_allow - HMAC-MD5 over data buffer (RFC 2104)
- * @key: Key for HMAC operations
- * @key_len: Length of the key in bytes
- * @data: Pointers to the data area
- * @data_len: Length of the data area
- * @mac: Buffer for the hash (16 bytes)
- * Returns: 0 on success, -1 on failure
- */
-int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data,
-			    size_t data_len, u8 *mac)
-{
-	return hmac_md5_vector_non_fips_allow(key, key_len, 1, &data,
-					      &data_len, mac);
-}
diff --git a/src/crypto/md5.h b/src/crypto/md5.h
index 4d6e904..33f8426 100644
--- a/src/crypto/md5.h
+++ b/src/crypto/md5.h
@@ -15,15 +15,5 @@
 		    const u8 *addr[], const size_t *len, u8 *mac);
 int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
 	     u8 *mac);
-#ifdef CONFIG_FIPS
-int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len,
-				   size_t num_elem, const u8 *addr[],
-				   const size_t *len, u8 *mac);
-int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data,
-			    size_t data_len, u8 *mac);
-#else /* CONFIG_FIPS */
-#define hmac_md5_vector_non_fips_allow hmac_md5_vector
-#define hmac_md5_non_fips_allow hmac_md5
-#endif /* CONFIG_FIPS */
 
 #endif /* MD5_H */
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index 3594d84..b2bbab2 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -173,8 +173,9 @@
 	u8 challenge[8];
 	u8 password_hash[16];
 
-	challenge_hash(peer_challenge, auth_challenge, username, username_len,
-		       challenge);
+	if (challenge_hash(peer_challenge, auth_challenge, username,
+			   username_len, challenge))
+		return -1;
 	if (nt_password_hash(password, password_len, password_hash))
 		return -1;
 	challenge_response(challenge, password_hash, response);
@@ -260,8 +261,9 @@
 	if (sha1_vector(3, addr1, len1, response))
 		return -1;
 
-	challenge_hash(peer_challenge, auth_challenge, username, username_len,
-		       challenge);
+	if (challenge_hash(peer_challenge, auth_challenge, username,
+			   username_len, challenge))
+		return -1;
 	return sha1_vector(3, addr2, len2, response);
 }
 
diff --git a/src/crypto/random.c b/src/crypto/random.c
index 55e9391..053740e 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -29,6 +29,7 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "crypto/crypto.h"
 #include "sha1.h"
 #include "random.h"
 
@@ -128,8 +129,6 @@
 	static unsigned int count = 0;
 
 	count++;
-	wpa_printf(MSG_MSGDUMP, "Add randomness: count=%u entropy=%u",
-		   count, entropy);
 	if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) {
 		/*
 		 * No need to add more entropy at this point, so save CPU and
@@ -137,6 +136,8 @@
 		 */
 		return;
 	}
+	wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u",
+		   count, entropy);
 
 	os_get_time(&t);
 	wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
@@ -177,6 +178,27 @@
 			*bytes++ ^= tmp[i];
 		left -= siz;
 	}
+
+#ifdef CONFIG_FIPS
+	/* Mix in additional entropy from the crypto module */
+	left = len;
+	while (left) {
+		size_t siz, i;
+		u8 tmp[EXTRACT_LEN];
+		if (crypto_get_random(tmp, sizeof(tmp)) < 0) {
+			wpa_printf(MSG_ERROR, "random: No entropy available "
+				   "for generating strong random bytes");
+			return -1;
+		}
+		wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module",
+				tmp, sizeof(tmp));
+		siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
+		for (i = 0; i < siz; i++)
+			*bytes++ ^= tmp[i];
+		left -= siz;
+	}
+#endif /* CONFIG_FIPS */
+
 	wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len);
 
 	if (entropy < len)
diff --git a/src/crypto/sha1-pbkdf2.c b/src/crypto/sha1-pbkdf2.c
index 450c2a0..8effe2f 100644
--- a/src/crypto/sha1-pbkdf2.c
+++ b/src/crypto/sha1-pbkdf2.c
@@ -11,7 +11,7 @@
 #include "common.h"
 #include "sha1.h"
 
-static int pbkdf2_sha1_f(const char *passphrase, const char *ssid,
+static int pbkdf2_sha1_f(const char *passphrase, const u8 *ssid,
 			 size_t ssid_len, int iterations, unsigned int count,
 			 u8 *digest)
 {
@@ -22,7 +22,7 @@
 	size_t len[2];
 	size_t passphrase_len = os_strlen(passphrase);
 
-	addr[0] = (u8 *) ssid;
+	addr[0] = ssid;
 	len[0] = ssid_len;
 	addr[1] = count_buf;
 	len[1] = 4;
@@ -69,7 +69,7 @@
  * iterations is set to 4096 and buflen to 32. This function is described in
  * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0.
  */
-int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
 		int iterations, u8 *buf, size_t buflen)
 {
 	unsigned int count = 0;
diff --git a/src/crypto/sha1-prf.c b/src/crypto/sha1-prf.c
new file mode 100644
index 0000000..90b9e74
--- /dev/null
+++ b/src/crypto/sha1-prf.c
@@ -0,0 +1,66 @@
+/*
+ * SHA1-based PRF
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., PMK in IEEE 802.11i).
+ */
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	u8 counter = 0;
+	size_t pos, plen;
+	u8 hash[SHA1_MAC_LEN];
+	size_t label_len = os_strlen(label) + 1;
+	const unsigned char *addr[3];
+	size_t len[3];
+
+	addr[0] = (u8 *) label;
+	len[0] = label_len;
+	addr[1] = data;
+	len[1] = data_len;
+	addr[2] = &counter;
+	len[2] = 1;
+
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		if (plen >= SHA1_MAC_LEN) {
+			if (hmac_sha1_vector(key, key_len, 3, addr, len,
+					     &buf[pos]))
+				return -1;
+			pos += SHA1_MAC_LEN;
+		} else {
+			if (hmac_sha1_vector(key, key_len, 3, addr, len,
+					     hash))
+				return -1;
+			os_memcpy(&buf[pos], hash, plen);
+			break;
+		}
+		counter++;
+	}
+
+	return 0;
+}
diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c
index e79265b..0effd9b 100644
--- a/src/crypto/sha1-tlsprf.c
+++ b/src/crypto/sha1-tlsprf.c
@@ -71,19 +71,16 @@
 		S2--;
 	}
 
-	hmac_md5_vector_non_fips_allow(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1],
-				       A_MD5);
+	hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5);
 	hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1);
 
 	MD5_pos = MD5_MAC_LEN;
 	SHA1_pos = SHA1_MAC_LEN;
 	for (i = 0; i < outlen; i++) {
 		if (MD5_pos == MD5_MAC_LEN) {
-			hmac_md5_vector_non_fips_allow(S1, L_S1, 3, MD5_addr,
-						       MD5_len, P_MD5);
+			hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5);
 			MD5_pos = 0;
-			hmac_md5_non_fips_allow(S1, L_S1, A_MD5, MD5_MAC_LEN,
-						A_MD5);
+			hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5);
 		}
 		if (SHA1_pos == SHA1_MAC_LEN) {
 			hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len,
diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c
index 274d81f..d48c77d 100644
--- a/src/crypto/sha1.c
+++ b/src/crypto/sha1.c
@@ -102,56 +102,3 @@
 {
 	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
 }
-
-
-/**
- * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
- * @key: Key for PRF
- * @key_len: Length of the key in bytes
- * @label: A unique label for each purpose of the PRF
- * @data: Extra data to bind into the key
- * @data_len: Length of the data
- * @buf: Buffer for the generated pseudo-random key
- * @buf_len: Number of bytes of key to generate
- * Returns: 0 on success, -1 of failure
- *
- * This function is used to derive new, cryptographically separate keys from a
- * given key (e.g., PMK in IEEE 802.11i).
- */
-int sha1_prf(const u8 *key, size_t key_len, const char *label,
-	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-{
-	u8 counter = 0;
-	size_t pos, plen;
-	u8 hash[SHA1_MAC_LEN];
-	size_t label_len = os_strlen(label) + 1;
-	const unsigned char *addr[3];
-	size_t len[3];
-
-	addr[0] = (u8 *) label;
-	len[0] = label_len;
-	addr[1] = data;
-	len[1] = data_len;
-	addr[2] = &counter;
-	len[2] = 1;
-
-	pos = 0;
-	while (pos < buf_len) {
-		plen = buf_len - pos;
-		if (plen >= SHA1_MAC_LEN) {
-			if (hmac_sha1_vector(key, key_len, 3, addr, len,
-					     &buf[pos]))
-				return -1;
-			pos += SHA1_MAC_LEN;
-		} else {
-			if (hmac_sha1_vector(key, key_len, 3, addr, len,
-					     hash))
-				return -1;
-			os_memcpy(&buf[pos], hash, plen);
-			break;
-		}
-		counter++;
-	}
-
-	return 0;
-}
diff --git a/src/crypto/sha1.h b/src/crypto/sha1.h
index 08e5a59..933cd81 100644
--- a/src/crypto/sha1.h
+++ b/src/crypto/sha1.h
@@ -22,6 +22,6 @@
 int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len,
 				  const char *label, const u8 *seed,
 				  size_t seed_len, u8 *out, size_t outlen);
-int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
 		int iterations, u8 *buf, size_t buflen);
 #endif /* SHA1_H */
diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c
new file mode 100644
index 0000000..0da6d13
--- /dev/null
+++ b/src/crypto/sha256-prf.c
@@ -0,0 +1,64 @@
+/*
+ * SHA256-based PRF (IEEE 802.11r)
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+
+/**
+ * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	u16 counter = 1;
+	size_t pos, plen;
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 counter_le[2], length_le[2];
+
+	addr[0] = counter_le;
+	len[0] = 2;
+	addr[1] = (u8 *) label;
+	len[1] = os_strlen(label);
+	addr[2] = data;
+	len[2] = data_len;
+	addr[3] = length_le;
+	len[3] = sizeof(length_le);
+
+	WPA_PUT_LE16(length_le, buf_len * 8);
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		WPA_PUT_LE16(counter_le, counter);
+		if (plen >= SHA256_MAC_LEN) {
+			hmac_sha256_vector(key, key_len, 4, addr, len,
+					   &buf[pos]);
+			pos += SHA256_MAC_LEN;
+		} else {
+			hmac_sha256_vector(key, key_len, 4, addr, len, hash);
+			os_memcpy(&buf[pos], hash, plen);
+			break;
+		}
+		counter++;
+	}
+}
diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c
index 5c09ed1..b55e976 100644
--- a/src/crypto/sha256.c
+++ b/src/crypto/sha256.c
@@ -1,6 +1,6 @@
 /*
  * SHA-256 hash implementation and interface functions
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -21,9 +21,10 @@
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
  * @mac: Buffer for the hash (32 bytes)
+ * Returns: 0 on success, -1 on failure
  */
-void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
-			const u8 *addr[], const size_t *len, u8 *mac)
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
 {
 	unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
 	unsigned char tk[32];
@@ -35,12 +36,13 @@
 		 * Fixed limit on the number of fragments to avoid having to
 		 * allocate memory (which could fail).
 		 */
-		return;
+		return -1;
 	}
 
         /* if key is longer than 64 bytes reset it to key = SHA256(key) */
         if (key_len > 64) {
-		sha256_vector(1, &key, &key_len, tk);
+		if (sha256_vector(1, &key, &key_len, tk) < 0)
+			return -1;
 		key = tk;
 		key_len = 32;
         }
@@ -68,7 +70,8 @@
 		_addr[i + 1] = addr[i];
 		_len[i + 1] = len[i];
 	}
-	sha256_vector(1 + num_elem, _addr, _len, mac);
+	if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0)
+		return -1;
 
 	os_memset(k_pad, 0, sizeof(k_pad));
 	os_memcpy(k_pad, key, key_len);
@@ -81,7 +84,7 @@
 	_len[0] = 64;
 	_addr[1] = mac;
 	_len[1] = SHA256_MAC_LEN;
-	sha256_vector(2, _addr, _len, mac);
+	return sha256_vector(2, _addr, _len, mac);
 }
 
 
@@ -91,61 +94,11 @@
  * @key_len: Length of the key in bytes
  * @data: Pointers to the data area
  * @data_len: Length of the data area
- * @mac: Buffer for the hash (20 bytes)
+ * @mac: Buffer for the hash (32 bytes)
+ * Returns: 0 on success, -1 on failure
  */
-void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
-		 size_t data_len, u8 *mac)
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
 {
-	hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
-}
-
-
-/**
- * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
- * @key: Key for PRF
- * @key_len: Length of the key in bytes
- * @label: A unique label for each purpose of the PRF
- * @data: Extra data to bind into the key
- * @data_len: Length of the data
- * @buf: Buffer for the generated pseudo-random key
- * @buf_len: Number of bytes of key to generate
- *
- * This function is used to derive new, cryptographically separate keys from a
- * given key.
- */
-void sha256_prf(const u8 *key, size_t key_len, const char *label,
-		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-{
-	u16 counter = 1;
-	size_t pos, plen;
-	u8 hash[SHA256_MAC_LEN];
-	const u8 *addr[4];
-	size_t len[4];
-	u8 counter_le[2], length_le[2];
-
-	addr[0] = counter_le;
-	len[0] = 2;
-	addr[1] = (u8 *) label;
-	len[1] = os_strlen(label);
-	addr[2] = data;
-	len[2] = data_len;
-	addr[3] = length_le;
-	len[3] = sizeof(length_le);
-
-	WPA_PUT_LE16(length_le, buf_len * 8);
-	pos = 0;
-	while (pos < buf_len) {
-		plen = buf_len - pos;
-		WPA_PUT_LE16(counter_le, counter);
-		if (plen >= SHA256_MAC_LEN) {
-			hmac_sha256_vector(key, key_len, 4, addr, len,
-					   &buf[pos]);
-			pos += SHA256_MAC_LEN;
-		} else {
-			hmac_sha256_vector(key, key_len, 4, addr, len, hash);
-			os_memcpy(&buf[pos], hash, plen);
-			break;
-		}
-		counter++;
-	}
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
 }
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
index 412fb60..fcac800 100644
--- a/src/crypto/sha256.h
+++ b/src/crypto/sha256.h
@@ -11,10 +11,10 @@
 
 #define SHA256_MAC_LEN 32
 
-void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
-		      const u8 *addr[], const size_t *len, u8 *mac);
-void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
-		 size_t data_len, u8 *mac);
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac);
 void sha256_prf(const u8 *key, size_t key_len, const char *label,
 	      const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
 void tls_prf_sha256(const u8 *secret, size_t secret_len,
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 2bd3bbb..b61e439 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -21,8 +21,10 @@
 };
 
 enum tls_event {
+	TLS_CERT_CHAIN_SUCCESS,
 	TLS_CERT_CHAIN_FAILURE,
-	TLS_PEER_CERTIFICATE
+	TLS_PEER_CERTIFICATE,
+	TLS_ALERT
 };
 
 /*
@@ -57,6 +59,12 @@
 		const u8 *hash;
 		size_t hash_len;
 	} peer_cert;
+
+	struct {
+		int is_local;
+		const char *type;
+		const char *description;
+	} alert;
 };
 
 struct tls_config {
@@ -73,6 +81,7 @@
 
 #define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0)
 #define TLS_CONN_DISABLE_TIME_CHECKS BIT(1)
+#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index be94e8a..dfb63ad 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -527,6 +527,15 @@
 			else
 				conn->write_alerts++;
 		}
+		if (tls_global->event_cb != NULL) {
+			union tls_event_data ev;
+			os_memset(&ev, 0, sizeof(ev));
+			ev.alert.is_local = !(where & SSL_CB_READ);
+			ev.alert.type = SSL_alert_type_string_long(ret);
+			ev.alert.description = SSL_alert_desc_string_long(ret);
+			tls_global->event_cb(tls_global->cb_ctx, TLS_ALERT,
+					     &ev);
+		}
 	} else if (where & SSL_CB_EXIT && ret <= 0) {
 		wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
 			   str, ret == 0 ? "failed" : "error",
@@ -702,6 +711,8 @@
 					   "mode");
 				ERR_load_crypto_strings();
 				ERR_print_errors_fp(stderr);
+				os_free(tls_global);
+				tls_global = NULL;
 				return NULL;
 			} else
 				wpa_printf(MSG_INFO, "Running in FIPS mode");
@@ -710,6 +721,8 @@
 		if (conf && conf->fips_mode) {
 			wpa_printf(MSG_ERROR, "FIPS mode requested, but not "
 				   "supported");
+			os_free(tls_global);
+			tls_global = NULL;
 			return NULL;
 		}
 #endif /* OPENSSL_FIPS */
@@ -1279,6 +1292,10 @@
 				       TLS_FAIL_SERVER_CHAIN_PROBE);
 	}
 
+	if (preverify_ok && tls_global->event_cb != NULL)
+		tls_global->event_cb(tls_global->cb_ctx,
+				     TLS_CERT_CHAIN_SUCCESS, NULL);
+
 	return preverify_ok;
 }
 
@@ -1923,6 +1940,8 @@
 	wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine "
 		   "to certificate store", __func__);
 	SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+	conn->ca_cert_verify = 1;
+
 	return 0;
 
 #else /* OPENSSL_NO_ENGINE */
@@ -2086,7 +2105,7 @@
 	ERR_clear_error();
 	SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
 	os_free(passwd);
-	
+
 	if (!SSL_check_private_key(conn->ssl)) {
 		tls_show_errors(MSG_INFO, __func__, "Private key failed "
 				"verification");
@@ -2132,7 +2151,7 @@
 	os_free(passwd);
 	ERR_clear_error();
 	SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
-	
+
 	if (!SSL_CTX_check_private_key(ssl_ctx)) {
 		tls_show_errors(MSG_INFO, __func__,
 				"Private key failed verification");
@@ -2294,6 +2313,11 @@
 int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
 			    struct tls_keys *keys)
 {
+#ifdef CONFIG_FIPS
+	wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
+		   "mode");
+	return -1;
+#else /* CONFIG_FIPS */
 	SSL *ssl;
 
 	if (conn == NULL || keys == NULL)
@@ -2311,6 +2335,7 @@
 	keys->server_random_len = SSL3_RANDOM_SIZE;
 
 	return 0;
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -2318,6 +2343,19 @@
 		       const char *label, int server_random_first,
 		       u8 *out, size_t out_len)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+	SSL *ssl;
+	if (conn == NULL)
+		return -1;
+	if (server_random_first)
+		return -1;
+	ssl = conn->ssl;
+	if (SSL_export_keying_material(ssl, out, out_len, label,
+				       os_strlen(label), NULL, 0, 0) == 1) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF");
+		return 0;
+	}
+#endif
 	return -1;
 }
 
@@ -2750,6 +2788,13 @@
 		return -1;
 	}
 
+#ifdef SSL_OP_NO_TICKET
+	if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
+		SSL_set_options(conn->ssl, SSL_OP_NO_TICKET);
+	else
+		SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET);
+#endif /*  SSL_OP_NO_TICKET */
+
 	conn->flags = params->flags;
 
 	tls_get_errors(tls_ctx);
@@ -2785,6 +2830,13 @@
 		return -1;
 	}
 
+#ifdef SSL_OP_NO_TICKET
+	if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
+		SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
+	else
+		SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
+#endif /*  SSL_OP_NO_TICKET */
+
 	return 0;
 }
 
@@ -2794,6 +2846,7 @@
 {
 	const EVP_CIPHER *c;
 	const EVP_MD *h;
+	int md_size;
 
 	if (conn == NULL || conn->ssl == NULL ||
 	    conn->ssl->enc_read_ctx == NULL ||
@@ -2807,9 +2860,20 @@
 #else
 	h = conn->ssl->read_hash;
 #endif
+	if (h)
+		md_size = EVP_MD_size(h);
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+	else if (conn->ssl->s3)
+		md_size = conn->ssl->s3->tmp.new_mac_secret_size;
+#endif
+	else
+		return -1;
 
+	wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d "
+		   "IV_len=%d", EVP_CIPHER_key_length(c), md_size,
+		   EVP_CIPHER_iv_length(c));
 	return 2 * (EVP_CIPHER_key_length(c) +
-		    EVP_MD_size(h) +
+		    md_size +
 		    EVP_CIPHER_iv_length(c));
 }
 
diff --git a/src/drivers/android_drv.h b/src/drivers/android_drv.h
index 6df7160..5906527 100644
--- a/src/drivers/android_drv.h
+++ b/src/drivers/android_drv.h
@@ -14,8 +14,6 @@
 
 #define WPA_EVENT_DRIVER_STATE "CTRL-EVENT-DRIVER-STATE "
 
-#define WEXT_CSCAN_AMOUNT		9
-
 #define MAX_SSID_LEN 32
 
 #define MAX_DRV_CMD_SIZE		248
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index f1d4a14..e9f926f 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -100,6 +100,16 @@
 	 */
 	u8 a_mpdu_params;
 
+	/**
+	 * vht_capab - VHT (IEEE 802.11ac) capabilities
+	 */
+	u32 vht_capab;
+
+	/**
+	 * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters
+	 */
+	u8 vht_mcs_set[8];
+
 	unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
 };
 
@@ -264,6 +274,15 @@
 	size_t num_filter_ssids;
 
 	/**
+	 * filter_rssi - Filter by RSSI
+	 *
+	 * The driver may filter scan results in firmware to reduce host
+	 * wakeups and thereby save power. Specify the RSSI threshold in s32
+	 * dBm.
+	 */
+	s32 filter_rssi;
+
+	/**
 	 * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes
 	 *
 	 * When set, the driver is expected to remove rates 1, 2, 5.5, and 11
@@ -333,6 +352,13 @@
 	int freq;
 
 	/**
+	 * bg_scan_period - Background scan period in seconds, 0 to disable
+	 * background scan, or -1 to indicate no change to default driver
+	 * configuration
+	 */
+	int bg_scan_period;
+
+	/**
 	 * wpa_ie - WPA information element for (Re)Association Request
 	 * WPA information element to be included in (Re)Association
 	 * Request (including information element id and length). Use
@@ -707,6 +733,18 @@
 	 * enabled.
 	 */
 	u8 access_network_type;
+
+	/**
+	 * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
+	 *
+	 * This is used by driver which advertises this capability.
+	 */
+	int ap_max_inactivity;
+
+	/**
+	 * disable_dgaf - Whether group-addressed frames are disabled
+	 */
+	int disable_dgaf;
 };
 
 /**
@@ -726,6 +764,8 @@
 #define WPA_DRIVER_CAPA_ENC_WEP104	0x00000002
 #define WPA_DRIVER_CAPA_ENC_TKIP	0x00000004
 #define WPA_DRIVER_CAPA_ENC_CCMP	0x00000008
+#define WPA_DRIVER_CAPA_ENC_WEP128	0x00000010
+#define WPA_DRIVER_CAPA_ENC_GCMP	0x00000020
 	unsigned int enc;
 
 #define WPA_DRIVER_AUTH_OPEN		0x00000001
@@ -790,6 +830,10 @@
 #define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD		0x00200000
 /* Driver supports U-APSD in AP mode */
 #define WPA_DRIVER_FLAGS_AP_UAPSD			0x00400000
+/* Driver supports inactivity timer in AP mode */
+#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER		0x00800000
+/* Driver expects user space implementation of MLME in AP mode */
+#define WPA_DRIVER_FLAGS_AP_MLME			0x01000000
 	unsigned int flags;
 
 	int max_scan_ssids;
@@ -956,6 +1000,23 @@
 	TDLS_DISABLE
 };
 
+enum wnm_oper {
+	WNM_SLEEP_ENTER_CONFIRM,
+	WNM_SLEEP_ENTER_FAIL,
+	WNM_SLEEP_EXIT_CONFIRM,
+	WNM_SLEEP_EXIT_FAIL,
+	WNM_SLEEP_TFS_REQ_IE_ADD,   /* STA requests driver to add TFS req IE */
+	WNM_SLEEP_TFS_REQ_IE_NONE,  /* STA requests empty TFS req IE */
+	WNM_SLEEP_TFS_REQ_IE_SET,   /* AP requests driver to set TFS req IE for
+				     * a STA */
+	WNM_SLEEP_TFS_RESP_IE_ADD,  /* AP requests driver to add TFS resp IE
+				     * for a STA */
+	WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */
+	WNM_SLEEP_TFS_RESP_IE_SET,  /* AP requests driver to set TFS resp IE
+				     * for a STA */
+	WNM_SLEEP_TFS_IE_DEL        /* AP delete the TFS IE */
+};
+
 /**
  * struct wpa_signal_info - Information about channel signal quality
  */
@@ -1015,7 +1076,8 @@
 	 * @ifname: Interface name (for multi-SSID/VLAN support)
 	 * @priv: private driver interface data
 	 * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
-	 *	%WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK);
+	 *	%WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK,
+	 *	%WPA_ALG_GCMP);
 	 *	%WPA_ALG_NONE clears the key.
 	 * @addr: Address of the peer STA (BSSID of the current AP when setting
 	 *	pairwise key in station mode), ff:ff:ff:ff:ff:ff for
@@ -1032,11 +1094,11 @@
 	 *	for Rx keys (in most cases, this is only used with broadcast
 	 *	keys and set to zero for unicast keys); %NULL if not set
 	 * @seq_len: length of the seq, depends on the algorithm:
-	 *	TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets
+	 *	TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets
 	 * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
 	 *	8-byte Rx Mic Key
 	 * @key_len: length of the key buffer in octets (WEP: 5 or 13,
-	 *	TKIP: 32, CCMP: 16, IGTK: 16)
+	 *	TKIP: 32, CCMP/GCMP: 16, IGTK: 16)
 	 *
 	 * Returns: 0 on success, -1 on failure
 	 *
@@ -1541,9 +1603,9 @@
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This function is used to fetch the last used TSC/packet number for
-	 * a TKIP, CCMP, or BIP/IGTK key. It is mainly used with group keys, so
-	 * there is no strict requirement on implementing support for unicast
-	 * keys (i.e., addr != %NULL).
+	 * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group
+	 * keys, so there is no strict requirement on implementing support for
+	 * unicast keys (i.e., addr != %NULL).
 	 */
 	int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr,
 			  int idx, u8 *seq);
@@ -1576,7 +1638,7 @@
 	int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
 
 	/**
-	 * read_sta_data - Fetch station data (AP only)
+	 * read_sta_data - Fetch station data
 	 * @priv: Private driver interface data
 	 * @data: Buffer for returning station information
 	 * @addr: MAC address of the station
@@ -2003,6 +2065,16 @@
 	int (*deinit_ap)(void *priv);
 
 	/**
+	 * deinit_p2p_cli - Deinitialize P2P client mode
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This optional function can be used to disable P2P client mode. It
+	 * can be used to change the interface type back to station mode.
+	 */
+	int (*deinit_p2p_cli)(void *priv);
+
+	/**
 	 * suspend - Notification on system suspend/hibernate event
 	 * @priv: Private driver interface data
 	 */
@@ -2362,6 +2434,18 @@
 	int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer);
 
 	/**
+	 * wnm_oper - Notify driver of the WNM frame reception
+	 * @priv: Private driver interface data
+	 * @oper: WNM operation. See %enum wnm_oper
+	 * @peer: Destination (peer) MAC address
+	 * @buf: Buffer for the driver to fill in (for getting IE)
+	 * @buf_len: Return the len of buf
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer,
+			u8 *buf, u16 *buf_len);
+
+	/**
 	 * signal_poll - Get current connection information
 	 * @priv: Private driver interface data
 	 * @signal_info: Connection info structure
@@ -2508,19 +2592,30 @@
 	 */
 	void (*poll_client)(void *priv, const u8 *own_addr,
 			    const u8 *addr, int qos);
-#ifdef ANDROID_P2P
+
 	/**
-	 * switch_channel - Announce channel switch and migrate the GO to a
-	 * given frequency.
+	 * radio_disable - Disable/enable radio
 	 * @priv: Private driver interface data
-	 * @freq: frequency in MHz
+	 * @disabled: 1=disable 0=enable radio
 	 * Returns: 0 on success, -1 on failure
 	 *
-	 * This function is used to move the GO to the legacy STA channel to avoid
-	 * frequency conflict in single channel concurrency.
+	 * This optional command is for testing purposes. It can be used to
+	 * disable the radio on a testbed device to simulate out-of-radio-range
+	 * conditions.
+	 */
+	int (*radio_disable)(void *priv, int disabled);
+
+	/**
+	 * switch_channel - Announce channel switch and migrate the GO to the
+	 * given frequency
+	 * @priv: Private driver interface data
+	 * @freq: Frequency in MHz
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to move the GO to the legacy STA channel to
+	 * avoid frequency conflict in single channel concurrency.
 	 */
 	int (*switch_channel)(void *priv, unsigned int freq);
-#endif
 };
 
 
@@ -2951,7 +3046,21 @@
 	/**
 	 * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status
 	 */
-	EVENT_EAPOL_TX_STATUS
+	EVENT_EAPOL_TX_STATUS,
+
+	/**
+	 * EVENT_CH_SWITCH - AP or GO decided to switch channels
+	 *
+	 * Described in wpa_event_data.ch_switch
+	 * */
+	EVENT_CH_SWITCH,
+
+	/**
+	 * EVENT_WNM - Request WNM operation
+	 *
+	 * This event can be used to request a WNM operation to be performed.
+	 */
+	EVENT_WNM
 };
 
 
@@ -3154,6 +3263,24 @@
 	} tdls;
 
 	/**
+	 * struct wnm - Data for EVENT_WNM
+	 */
+	struct wnm {
+		u8 addr[ETH_ALEN];
+		enum {
+			WNM_OPER_SLEEP,
+		} oper;
+		enum {
+			WNM_SLEEP_ENTER,
+			WNM_SLEEP_EXIT
+		} sleep_action;
+		int sleep_intval;
+		u16 reason_code;
+		u8 *buf;
+		u16 buf_len;
+	} wnm;
+
+	/**
 	 * struct ft_ies - FT information elements (EVENT_FT_RESPONSE)
 	 *
 	 * During FT (IEEE 802.11r) authentication sequence, the driver is
@@ -3267,7 +3394,7 @@
 		const u8 *frame;
 		size_t frame_len;
 		u32 datarate;
-		u32 ssi_signal;
+		int ssi_signal; /* dBm */
 	} rx_mgmt;
 
 	/**
@@ -3385,6 +3512,11 @@
 		 * ie_len - Length of ie buffer in octets
 		 */
 		size_t ie_len;
+
+		/**
+		 * signal - signal strength in dBm (or 0 if not available)
+		 */
+		int ssi_signal;
 	} rx_probe_req;
 
 	/**
@@ -3541,6 +3673,18 @@
 		int data_len;
 		int ack;
 	} eapol_tx_status;
+
+	/**
+	 * struct ch_switch
+	 * @freq: Frequency of new channel in MHz
+	 * @ht_enabled: Whether this is an HT channel
+	 * @ch_offset: Secondary channel offset
+	 */
+	struct ch_switch {
+		int freq;
+		int ht_enabled;
+		int ch_offset;
+	} ch_switch;
 };
 
 /**
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index b17d1a6..5f2e675 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -5,14 +5,8 @@
  * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2009, Atheros Communications
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -20,6 +14,12 @@
 #include <sys/ioctl.h>
 
 #include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "l2_packet/l2_packet.h"
+#include "p2p/p2p.h"
+
+#include "common.h"
 #ifndef _BYTE_ORDER
 #ifdef WORDS_BIGENDIAN
 #define _BYTE_ORDER _BIG_ENDIAN
@@ -39,11 +39,11 @@
 
 #ifdef CONFIG_WPS
 #include <netpacket/packet.h>
+#endif /* CONFIG_WPS */
 
 #ifndef ETH_P_80211_RAW
 #define ETH_P_80211_RAW 0x0019
 #endif
-#endif /* CONFIG_WPS */
 
 #include "linux_wext.h"
 
@@ -73,6 +73,7 @@
 	struct wpabuf *wpa_ie;
 	struct wpabuf *wps_beacon_ie;
 	struct wpabuf *wps_probe_resp_ie;
+	u8	own_addr[ETH_ALEN];
 };
 
 static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
@@ -323,8 +324,7 @@
 	}
 #endif /* CONFIG_IEEE80211W */
 
-	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
-		   __func__, params->rsn_preauth);
+	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v);
 	if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
 		printf("Unable to set RSN capabilities to 0x%x\n", v);
 		return -1;
@@ -732,8 +732,8 @@
 }
 
 #ifdef CONFIG_WPS
-static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
-				size_t len)
+static void atheros_raw_recv_wps(void *ctx, const u8 *src_addr, const u8 *buf,
+				 size_t len)
 {
 	struct atheros_driver_data *drv = ctx;
 	const struct ieee80211_mgmt *mgmt;
@@ -762,28 +762,233 @@
 }
 #endif /* CONFIG_WPS */
 
-static int atheros_receive_probe_req(struct atheros_driver_data *drv)
+#ifdef CONFIG_IEEE80211R
+static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf,
+				 size_t len)
+{
+	struct atheros_driver_data *drv = ctx;
+	union wpa_event_data event;
+	const struct ieee80211_mgmt *mgmt;
+	u16 fc;
+	u16 stype;
+	int ielen;
+	const u8 *iebuf;
+
+	/* Do 11R processing for ASSOC/AUTH/FT ACTION frames */
+	if (len < IEEE80211_HDRLEN)
+		return;
+	mgmt = (const struct ieee80211_mgmt *) buf;
+
+	fc = le_to_host16(mgmt->frame_control);
+
+	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+		return;
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
+		   (int) len);
+
+	if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
+			   __func__);
+		return;
+	}
+	switch (stype) {
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.assoc_req))
+			break;
+		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+		iebuf = mgmt->u.assoc_req.variable;
+		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0);
+		break;
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.reassoc_req))
+			break;
+		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+		iebuf = mgmt->u.reassoc_req.variable;
+		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
+		break;
+	case WLAN_FC_STYPE_ACTION:
+		if (&mgmt->u.action.category > buf + len)
+			break;
+		os_memset(&event, 0, sizeof(event));
+		event.rx_action.da = mgmt->da;
+		event.rx_action.sa = mgmt->sa;
+		event.rx_action.bssid = mgmt->bssid;
+		event.rx_action.category = mgmt->u.action.category;
+		event.rx_action.data = &mgmt->u.action.category;
+		event.rx_action.len = buf + len - event.rx_action.data;
+		wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event);
+		break;
+	case WLAN_FC_STYPE_AUTH:
+		if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.auth))
+			break;
+		os_memset(&event, 0, sizeof(event));
+		os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+		os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN);
+		event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+		event.auth.status_code =
+			le_to_host16(mgmt->u.auth.status_code);
+		event.auth.auth_transaction =
+			le_to_host16(mgmt->u.auth.auth_transaction);
+		event.auth.ies = mgmt->u.auth.variable;
+		event.auth.ies_len = len - IEEE80211_HDRLEN -
+			sizeof(mgmt->u.auth);
+		wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event);
+		break;
+	default:
+		break;
+	}
+}
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_HS20
+static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf,
+				 size_t len)
+{
+	struct atheros_driver_data *drv = ctx;
+	const struct ieee80211_mgmt *mgmt;
+	u16 fc;
+	union wpa_event_data event;
+
+	/* Send the Action frame for HS20 processing */
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.action.category) +
+	    sizeof(mgmt->u.action.u.public_action))
+		return;
+
+	mgmt = (const struct ieee80211_mgmt *) buf;
+
+	fc = le_to_host16(mgmt->frame_control);
+	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
+	    WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION ||
+	    mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+		return;
+
+	wpa_printf(MSG_DEBUG, "%s:Received Public Action frame", __func__);
+
+	os_memset(&event, 0, sizeof(event));
+	event.rx_mgmt.frame = (const u8 *) mgmt;
+	event.rx_mgmt.frame_len = len;
+	wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+}
+#endif /* CONFIG_HS20 */
+
+#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R)
+static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf,
+				 size_t len)
+{
+	struct atheros_driver_data *drv = ctx;
+	union wpa_event_data event;
+	const struct ieee80211_mgmt *mgmt;
+	u16 fc;
+	u16 stype;
+
+	/* Do 11R processing for WNM ACTION frames */
+	if (len < IEEE80211_HDRLEN)
+		return;
+	mgmt = (const struct ieee80211_mgmt *) buf;
+
+	fc = le_to_host16(mgmt->frame_control);
+
+	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+		return;
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
+		   (int) len);
+
+	if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
+			   __func__);
+		return;
+	}
+
+	switch (stype) {
+	case WLAN_FC_STYPE_ACTION:
+		if (&mgmt->u.action.category > buf + len)
+			break;
+		os_memset(&event, 0, sizeof(event));
+		event.rx_action.da = mgmt->da;
+		event.rx_action.sa = mgmt->sa;
+		event.rx_action.bssid = mgmt->bssid;
+		event.rx_action.category = mgmt->u.action.category;
+		event.rx_action.data = &mgmt->u.action.category;
+		event.rx_action.len = buf + len - event.rx_action.data;
+		wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event);
+		break;
+	default:
+		break;
+	}
+}
+#endif /* CONFIG_IEEE80211V */
+
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211V)
+static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+				size_t len)
+{
+#ifdef CONFIG_WPS
+	atheros_raw_recv_wps(ctx, src_addr, buf, len);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_IEEE80211R
+	atheros_raw_recv_11r(ctx, src_addr, buf, len);
+#endif /* CONFIG_IEEE80211R */
+#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R)
+	atheros_raw_recv_11v(ctx, src_addr, buf, len);
+#endif /* CONFIG_IEEE80211V */
+#ifdef CONFIG_HS20
+	atheros_raw_recv_hs20(ctx, src_addr, buf, len);
+#endif /* CONFIG_HS20 */
+}
+#endif /* CONFIG_WPS || CONFIG_IEEE80211R */
+
+static int atheros_receive_pkt(struct atheros_driver_data *drv)
 {
 	int ret = 0;
-#ifdef CONFIG_WPS
 	struct ieee80211req_set_filter filt;
 
 	wpa_printf(MSG_DEBUG, "%s Enter", __func__);
-	filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ;
+	filt.app_filterype = 0;
+#ifdef CONFIG_WPS
+	filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_IEEE80211R
+	filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
+			       IEEE80211_FILTER_TYPE_AUTH |
+			       IEEE80211_FILTER_TYPE_ACTION);
+#endif
+#ifdef CONFIG_IEEE80211V
+	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
+#endif /* CONFIG_IEEE80211V */
+#ifdef CONFIG_HS20
+	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
+#endif /* CONFIG_HS20 */
+	if (filt.app_filterype) {
+		ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+				   sizeof(struct ieee80211req_set_filter));
+		if (ret)
+			return ret;
+	}
 
-	ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
-			   sizeof(struct ieee80211req_set_filter));
-	if (ret)
-		return ret;
-
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R)
 	drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
 				       atheros_raw_receive, drv, 1);
 	if (drv->sock_raw == NULL)
 		return -1;
-#endif /* CONFIG_WPS */
+#endif /* CONFIG_WPS || CONFIG_IEEE80211R */
+	if (l2_packet_get_own_addr(drv->sock_xmit, drv->own_addr))
+		return -1;
 	return ret;
 }
 
+static int atheros_reset_appfilter(struct atheros_driver_data *drv)
+{
+	struct ieee80211req_set_filter filt;
+	filt.app_filterype = 0;
+	return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+			    sizeof(struct ieee80211req_set_filter));
+}
+
 #ifdef CONFIG_WPS
 static int
 atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
@@ -852,6 +1057,84 @@
 #define atheros_set_ap_wps_ie NULL
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_IEEE80211R
+static int
+atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq,
+		 u16 status_code, const u8 *ie, size_t len)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d",
+		   __func__, ether_sprintf(addr), status_code);
+
+	mlme.im_op = IEEE80211_MLME_AUTH;
+	mlme.im_reason = status_code;
+	mlme.im_seq = seq;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	mlme.im_optie_len = len;
+	if (len) {
+		if (len < IEEE80211_MAX_OPT_IE) {
+			os_memcpy(mlme.im_optie, ie, len);
+		} else {
+			wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
+				   "opt_ie STA (addr " MACSTR " reason %d, "
+				   "ie_len %d)",
+				   __func__, MAC2STR(addr), status_code,
+				   (int) len);
+			return -1;
+		}
+	}
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR
+			   " reason %d)",
+			   __func__, MAC2STR(addr), status_code);
+	}
+	return ret;
+}
+
+static int
+atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr,
+		  int reassoc, u16 status_code, const u8 *ie, size_t len)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d",
+		   __func__, ether_sprintf(addr), status_code, reassoc);
+
+	if (reassoc)
+		mlme.im_op = IEEE80211_MLME_REASSOC;
+	else
+		mlme.im_op = IEEE80211_MLME_ASSOC;
+	mlme.im_reason = status_code;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	mlme.im_optie_len = len;
+	if (len) {
+		if (len < IEEE80211_MAX_OPT_IE) {
+			os_memcpy(mlme.im_optie, ie, len);
+		} else {
+			wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
+				   "opt_ie STA (addr " MACSTR " reason %d, "
+				   "ie_len %d)",
+				   __func__, MAC2STR(addr), status_code,
+				   (int) len);
+			return -1;
+		}
+	}
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR
+			   " reason %d)",
+			   __func__, MAC2STR(addr), status_code);
+	}
+	return ret;
+}
+#endif /* CONFIG_IEEE80211R */
+
 static void
 atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
 {
@@ -980,6 +1263,9 @@
 		 * so all are enabled for WPS... ugh.
 		 */
 		wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL);
+#endif /* CONFIG_WPS */
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20)
+#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */
 	} else if (strncmp(custom, "Manage.prob_req ", 16) == 0) {
 		/*
 		 * Atheros driver uses a hack to pass Probe Request frames as a
@@ -987,16 +1273,134 @@
 		 * packet sniffing) didn't work when bridging.
 		 * Format: "Manage.prob_req <frame len>" | zero padding | frame
 		 */
-#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */
 		int len = atoi(custom + 16);
-		if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) {
+		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
 			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event "
 				   "length %d", len);
 			return;
 		}
 		atheros_raw_receive(drv, NULL,
-				    (u8 *) custom + WPS_FRAM_TAG_SIZE, len);
-#endif /* CONFIG_WPS */
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+	} else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) {
+		/* Format: "Manage.assoc_req <frame len>" | zero padding |
+		 * frame */
+		int len = atoi(custom + 17);
+		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
+			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
+				   "assoc_req/auth event length %d", len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+	} else if (strncmp(custom, "Manage.action ", 14) == 0) {
+		/* Format: "Manage.assoc_req <frame len>" | zero padding |
+		 * frame */
+		int len = atoi(custom + 14);
+		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
+			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
+				   "assoc_req/auth event length %d", len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+	} else if (strncmp(custom, "Manage.auth ", 12) == 0) {
+		/* Format: "Manage.auth <frame len>" | zero padding | frame
+		 */
+		int len = atoi(custom + 12);
+		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
+			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
+				   "assoc_req/auth event length %d", len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+#endif /* CONFIG_WPS or CONFIG_IEEE80211R */
+	}
+}
+
+/*
+* Handle size of data problem. WEXT only allows data of 256 bytes for custom
+* events, and p2p data can be much bigger. So the athr driver sends a small
+* event telling me to collect the big data with an ioctl.
+* On the first event, send all pending events to supplicant.
+*/
+static void fetch_pending_big_events(struct atheros_driver_data *drv)
+{
+	union wpa_event_data event;
+	const struct ieee80211_mgmt *mgmt;
+	u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */
+	u16 fc, stype;
+	struct iwreq iwr;
+	size_t data_len;
+	u32 freq, frame_type;
+
+	while (1) {
+		os_memset(&iwr, 0, sizeof(iwr));
+		os_strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+		iwr.u.data.pointer = (void *) tbuf;
+		iwr.u.data.length = sizeof(tbuf);
+		iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME;
+
+		if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr)
+		    < 0) {
+			if (errno == ENOSPC) {
+				wpa_printf(MSG_DEBUG, "%s:%d exit",
+					   __func__, __LINE__);
+				return;
+			}
+			wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM["
+				   "P2P_FETCH_FRAME] failed: %s",
+				   __func__, strerror(errno));
+			return;
+		}
+		data_len = iwr.u.data.length;
+		wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data",
+			    (u8 *) tbuf, data_len);
+		if (data_len < sizeof(freq) + sizeof(frame_type) + 24) {
+			wpa_printf(MSG_DEBUG, "athr: frame too short");
+			continue;
+		}
+		os_memcpy(&freq, tbuf, sizeof(freq));
+		os_memcpy(&frame_type, &tbuf[sizeof(freq)],
+			  sizeof(frame_type));
+		mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)];
+		data_len -= sizeof(freq) + sizeof(frame_type);
+
+		if (frame_type == IEEE80211_EV_RX_MGMT) {
+			fc = le_to_host16(mgmt->frame_control);
+			stype = WLAN_FC_GET_STYPE(fc);
+
+			wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u "
+				"freq=%u len=%u", stype, freq, (int) data_len);
+
+			if (stype == WLAN_FC_STYPE_ACTION) {
+				os_memset(&event, 0, sizeof(event));
+				event.rx_mgmt.frame = (const u8 *) mgmt;
+				event.rx_mgmt.frame_len = data_len;
+				wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT,
+						     &event);
+				continue;
+			}
+		} else {
+			wpa_printf(MSG_DEBUG, "athr: %s unknown type %d",
+				   __func__, frame_type);
+			continue;
+		}
+	}
+}
+
+static void
+atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv,
+				      int opcode, char *buf, int len)
+{
+	switch (opcode) {
+	case IEEE80211_EV_RX_MGMT:
+		wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT");
+		fetch_pending_big_events(drv);
+		break;
+	default:
+		break;
 	}
 }
 
@@ -1055,8 +1459,15 @@
 				return;		/* XXX */
 			memcpy(buf, custom, iwe->u.data.length);
 			buf[iwe->u.data.length] = '\0';
-			atheros_wireless_event_wireless_custom(
-				drv, buf, buf + iwe->u.data.length);
+
+			if (iwe->u.data.flags != 0) {
+				atheros_wireless_event_atheros_custom(
+					drv, (int) iwe->u.data.flags,
+					buf, len);
+			} else {
+				atheros_wireless_event_wireless_custom(
+					drv, buf, buf + iwe->u.data.length);
+			}
 			free(buf);
 			break;
 		}
@@ -1278,7 +1689,7 @@
 	linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
 	atheros_set_privacy(drv, 0); /* default to no privacy */
 
-	atheros_receive_probe_req(drv);
+	atheros_receive_pkt(drv);
 
 	if (atheros_wireless_event_init(drv))
 		goto bad;
@@ -1302,6 +1713,7 @@
 {
 	struct atheros_driver_data *drv = priv;
 
+	atheros_reset_appfilter(drv);
 	netlink_deinit(drv->netlink);
 	(void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
 	if (drv->ioctl_sock >= 0)
@@ -1348,7 +1760,6 @@
 	memset(&iwr, 0, sizeof(iwr));
 	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
 	iwr.u.essid.pointer = (caddr_t) buf;
-	iwr.u.essid.length = len;
 	iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ?
 		IW_ESSID_MAX_SIZE : len;
 
@@ -1421,6 +1832,290 @@
 	return 0;
 }
 
+
+#ifdef CONFIG_IEEE80211R
+
+static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
+			     int noack)
+{
+	struct atheros_driver_data *drv = priv;
+	u8 buf[1510];
+	const struct ieee80211_mgmt *mgmt;
+	struct ieee80211req_mgmtbuf *mgmt_frm;
+
+	mgmt = (const struct ieee80211_mgmt *) frm;
+	wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__,
+		   (unsigned long) data_len, MAC2STR(mgmt->da));
+	mgmt_frm = (struct ieee80211req_mgmtbuf *) buf;
+	memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN);
+	mgmt_frm->buflen = data_len;
+	if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) {
+		wpa_printf(MSG_INFO, "atheros: Too long frame for "
+			   "atheros_send_mgmt (%u)", (unsigned int) data_len);
+		return -1;
+	}
+	os_memcpy(&mgmt_frm->buf[0], frm, data_len);
+	return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm,
+			    sizeof(struct ieee80211req_mgmtbuf) + data_len);
+}
+
+
+static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie,
+			     size_t tspec_ielen)
+{
+	struct atheros_driver_data *drv = priv;
+	int retv;
+	struct ieee80211req_res req;
+	struct ieee80211req_res_addts *addts = &req.u.addts;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+	req.type = IEEE80211_RESREQ_ADDTS;
+	os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
+	os_memcpy(addts->tspecie, tspec_ie, tspec_ielen);
+	retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
+			    sizeof(struct ieee80211req_res));
+	if (retv < 0) {
+		wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED "
+			   "retv = %d", __func__, retv);
+		return -1;
+	}
+	os_memcpy(tspec_ie, addts->tspecie, tspec_ielen);
+	return addts->status;
+}
+
+
+static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_res req;
+	struct ieee80211req_res_addnode *addnode = &req.u.addnode;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+	req.type = IEEE80211_RESREQ_ADDNODE;
+	os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
+	addnode->auth_alg = auth_alg;
+	return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
+			    sizeof(struct ieee80211req_res));
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+/* Use only to set a big param, get will not work. */
+static int
+set80211big(struct atheros_driver_data *drv, int op, const void *data, int len)
+{
+	struct iwreq iwr;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+	iwr.u.data.pointer = (void *) data;
+	iwr.u.data.length = len;
+	iwr.u.data.flags = op;
+	wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x",
+		   __func__, op, op, athr_get_param_name(op), len);
+
+	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) {
+		wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d "
+			   "value=0x%x,0x%x failed: %d (%s)",
+			   __func__, op, athr_get_ioctl_name(op), iwr.u.mode,
+			   iwr.u.mode, iwr.u.data.length,
+			   iwr.u.data.flags, errno, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+
+static int atheros_send_action(void *priv, unsigned int freq,
+			       unsigned int wait,
+			       const u8 *dst, const u8 *src,
+			       const u8 *bssid,
+			       const u8 *data, size_t data_len, int no_cck)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211_p2p_send_action *act;
+	int res;
+
+	act = os_zalloc(sizeof(*act) + data_len);
+	if (act == NULL)
+		return -1;
+	act->freq = freq;
+	os_memcpy(act->dst_addr, dst, ETH_ALEN);
+	os_memcpy(act->src_addr, src, ETH_ALEN);
+	os_memcpy(act->bssid, bssid, ETH_ALEN);
+	os_memcpy(act + 1, data, data_len);
+	wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src="
+		   MACSTR ", bssid=" MACSTR,
+		   __func__, act->freq, wait, MAC2STR(act->dst_addr),
+		   MAC2STR(act->src_addr), MAC2STR(act->bssid));
+	wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act));
+	wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len);
+
+	res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION,
+			  act, sizeof(*act) + data_len);
+	os_free(act);
+	return res;
+}
+
+
+#ifdef CONFIG_IEEE80211V
+static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer,
+			u8 *ie, u16 *len, enum wnm_oper oper)
+{
+#define IEEE80211_APPIE_MAX    1024 /* max appie buffer size */
+	u8 buf[IEEE80211_APPIE_MAX];
+	struct ieee80211req_getset_appiebuf *tfs_ie;
+	u16 val;
+
+	wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR,
+		   drv->iface, oper, MAC2STR(peer));
+
+	switch (oper) {
+	case WNM_SLEEP_TFS_REQ_IE_SET:
+		if (*len > IEEE80211_APPIE_MAX -
+		    sizeof(struct ieee80211req_getset_appiebuf)) {
+			wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large");
+			return -1;
+		}
+		tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+		tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+		tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len;
+
+		/* Command header for driver */
+		os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+		val = oper;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+		val = *len;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+		/* copy the ie */
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len);
+
+		if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
+				 IEEE80211_APPIE_MAX)) {
+			wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
+				   "%s", __func__, strerror(errno));
+			return -1;
+		}
+		break;
+	case WNM_SLEEP_TFS_RESP_IE_ADD:
+		tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+		tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+		tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
+			sizeof(struct ieee80211req_getset_appiebuf);
+		/* Command header for driver */
+		os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+		val = oper;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+		val = 0;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+		if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie,
+				 IEEE80211_APPIE_MAX)) {
+			wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: "
+				   "%s", __func__, strerror(errno));
+			return -1;
+		}
+
+		*len = tfs_ie->app_buflen;
+		os_memcpy(ie, &(tfs_ie->app_buf[0]), *len);
+		wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0],
+			   *len);
+		break;
+	case WNM_SLEEP_TFS_RESP_IE_NONE:
+		*len = 0;
+		break;
+	case WNM_SLEEP_TFS_IE_DEL:
+		tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+		tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+		tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
+			sizeof(struct ieee80211req_getset_appiebuf);
+		/* Command header for driver */
+		os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+		val = oper;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+		val = 0;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+		if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
+				 IEEE80211_APPIE_MAX)) {
+			wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
+				   "%s", __func__, strerror(errno));
+			return -1;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper);
+		break;
+	}
+
+	return 0;
+}
+
+
+static int atheros_wnm_sleep(struct atheros_driver_data *drv,
+			     const u8 *peer, enum wnm_oper oper)
+{
+	u8 *data, *pos;
+	size_t dlen;
+	int ret;
+	u16 val;
+
+	wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR,
+		   oper, MAC2STR(peer));
+
+	dlen = ETH_ALEN + 2 + 2;
+	data = os_malloc(dlen);
+	if (data == NULL)
+		return -1;
+
+	/* Command header for driver */
+	pos = data;
+	os_memcpy(pos, peer, ETH_ALEN);
+	pos += ETH_ALEN;
+
+	val = oper;
+	os_memcpy(pos, &val, 2);
+	pos += 2;
+
+	val = 0;
+	os_memcpy(pos, &val, 2);
+
+	ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM);
+
+	os_free(data);
+
+	return ret;
+}
+
+
+static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer,
+			    u8 *buf, u16 *buf_len)
+{
+	struct atheros_driver_data *drv = priv;
+
+	switch (oper) {
+	case WNM_SLEEP_ENTER_CONFIRM:
+	case WNM_SLEEP_ENTER_FAIL:
+	case WNM_SLEEP_EXIT_CONFIRM:
+	case WNM_SLEEP_EXIT_FAIL:
+		return atheros_wnm_sleep(drv, peer, oper);
+	case WNM_SLEEP_TFS_REQ_IE_SET:
+	case WNM_SLEEP_TFS_RESP_IE_ADD:
+	case WNM_SLEEP_TFS_RESP_IE_NONE:
+	case WNM_SLEEP_TFS_IE_DEL:
+		return athr_wnm_tfs(drv, peer, buf, buf_len, oper);
+	default:
+		wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d",
+			   oper);
+		return -1;
+	}
+}
+#endif /* CONFIG_IEEE80211V */
+
+
 const struct wpa_driver_ops wpa_driver_atheros_ops = {
 	.name			= "atheros",
 	.hapd_init		= atheros_init,
@@ -1444,4 +2139,15 @@
 	.set_ap_wps_ie		= atheros_set_ap_wps_ie,
 	.set_authmode		= atheros_set_authmode,
 	.set_ap			= atheros_set_ap,
+#ifdef CONFIG_IEEE80211R
+	.sta_assoc              = atheros_sta_assoc,
+	.sta_auth               = atheros_sta_auth,
+	.send_mlme       	= atheros_send_mgmt,
+	.add_tspec      	= atheros_add_tspec,
+	.add_sta_node    	= atheros_add_sta_node,
+#endif /* CONFIG_IEEE80211R */
+	.send_action		= atheros_send_action,
+#ifdef CONFIG_IEEE80211V
+	.wnm_oper		= atheros_wnm_oper,
+#endif /* CONFIG_IEEE80211V */
 };
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 4596a51..a2b34c1 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -3,14 +3,8 @@
  * Copyright (c) 2004, Sam Leffler <sam@errno.com>
  * Copyright (c) 2004, 2Wire, Inc
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -1334,8 +1328,8 @@
 
 	result->ie_len = pos - (u8 *)(result + 1);
 
-	tmp = os_realloc(res->res,
-			 (res->num + 1) * sizeof(struct wpa_scan_res *));
+	tmp = os_realloc_array(res->res, res->num + 1,
+			       sizeof(struct wpa_scan_res *));
 	if (tmp == NULL) {
 		os_free(result);
 		return;
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 345e851..418cf1a 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -77,6 +77,8 @@
 	E2S(SCHED_SCAN_STOPPED);
 	E2S(DRIVER_CLIENT_POLL_OK);
 	E2S(EAPOL_TX_STATUS);
+	E2S(CH_SWITCH);
+	E2S(WNM);
 	}
 
 	return "UNKNOWN";
diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c
index edb086f..bb48011 100644
--- a/src/drivers/driver_madwifi.c
+++ b/src/drivers/driver_madwifi.c
@@ -4,14 +4,8 @@
  * Copyright (c) 2004, Video54 Technologies
  * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  *
  * While this driver wrapper supports both AP (hostapd) and station
  * (wpa_supplicant) operations, the station side is deprecated and
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 185ed4f..b9e096c 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -858,7 +858,7 @@
 		os_free(b);
 		return NULL;
 	}
-	results->res = os_zalloc(count * sizeof(struct wpa_scan_res *));
+	results->res = os_calloc(count, sizeof(struct wpa_scan_res *));
 	if (results->res == NULL) {
 		os_free(results);
 		os_free(b);
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 8795104..4574938 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -189,6 +189,7 @@
 	unsigned int beacon_set:1;
 	unsigned int added_if_into_bridge:1;
 	unsigned int added_bridge:1;
+	unsigned int in_deinit:1;
 
 	u8 addr[ETH_ALEN];
 
@@ -305,9 +306,6 @@
 static int android_pno_stop(struct i802_bss *bss);
 #endif /* ANDROID */
 #ifdef ANDROID_P2P
-static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
-				  enum wpa_event_type type,
-				  const u8 *frame, size_t len);
 int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration);
 int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len);
 int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow);
@@ -456,6 +454,10 @@
 			     valid_data);
 }
 
+
+#ifndef ANDROID
+static
+#endif
 int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
 			      struct nl_msg *msg,
 			      int (*valid_handler)(struct nl_msg *, void *),
@@ -797,10 +799,28 @@
 		   del ? "removed" : "added");
 
 	if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) {
-		if (del)
+		if (del) {
+			if (drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+					   "already set - ignore event");
+				return;
+			}
 			drv->if_removed = 1;
-		else
+		} else {
+			if (if_nametoindex(drv->first_bss.ifname) == 0) {
+				wpa_printf(MSG_DEBUG, "nl80211: Interface %s "
+					   "does not exist - ignore "
+					   "RTM_NEWLINK",
+					   drv->first_bss.ifname);
+				return;
+			}
+			if (!drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+					   "already cleared - ignore event");
+				return;
+			}
 			drv->if_removed = 0;
+		}
 	}
 
 	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
@@ -917,6 +937,14 @@
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
 				   "event since interface %s is down",
 				   namebuf);
+		} else if (if_nametoindex(drv->first_bss.ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+				   "event since interface %s does not exist",
+				   drv->first_bss.ifname);
+		} else if (drv->if_removed) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+				   "event since interface %s is marked "
+				   "removed", drv->first_bss.ifname);
 		} else {
 			wpa_printf(MSG_DEBUG, "nl80211: Interface up");
 			drv->if_disabled = 0;
@@ -1103,6 +1131,7 @@
 	}
 
 	event.assoc_info.freq = drv->assoc_freq;
+
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -1176,9 +1205,43 @@
 	drv->associated = 0;
 	os_memset(&data, 0, sizeof(data));
 	if (reason)
-		data.disassoc_info.reason_code = nla_get_u16(reason);
-	data.disassoc_info.locally_generated = by_ap == NULL;
-	wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
+		data.deauth_info.reason_code = nla_get_u16(reason);
+	data.deauth_info.locally_generated = by_ap == NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data);
+}
+
+
+static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+				 struct nlattr *freq, struct nlattr *type)
+{
+	union wpa_event_data data;
+	int ht_enabled = 1;
+	int chan_offset = 0;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
+
+	if (!freq || !type)
+		return;
+
+	switch (nla_get_u32(type)) {
+	case NL80211_CHAN_NO_HT:
+		ht_enabled = 0;
+		break;
+	case NL80211_CHAN_HT20:
+		break;
+	case NL80211_CHAN_HT40PLUS:
+		chan_offset = 1;
+		break;
+	case NL80211_CHAN_HT40MINUS:
+		chan_offset = -1;
+		break;
+	}
+
+	data.ch_switch.freq = nla_get_u32(freq);
+	data.ch_switch.ht_enabled = ht_enabled;
+	data.ch_switch.ch_offset = chan_offset;
+
+	wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data);
 }
 
 
@@ -1208,11 +1271,13 @@
 
 
 static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv,
-			    struct nlattr *freq, const u8 *frame, size_t len)
+			    struct nlattr *freq, struct nlattr *sig,
+			    const u8 *frame, size_t len)
 {
 	const struct ieee80211_mgmt *mgmt;
 	union wpa_event_data event;
 	u16 fc, stype;
+	int ssi_signal = 0;
 
 	mgmt = (const struct ieee80211_mgmt *) frame;
 	if (len < 24) {
@@ -1223,6 +1288,9 @@
 	fc = le_to_host16(mgmt->frame_control);
 	stype = WLAN_FC_GET_STYPE(fc);
 
+	if (sig)
+		ssi_signal = (s32) nla_get_u32(sig);
+
 	os_memset(&event, 0, sizeof(event));
 	if (freq) {
 		event.rx_action.freq = nla_get_u32(freq);
@@ -1239,6 +1307,7 @@
 	} else {
 		event.rx_mgmt.frame = frame;
 		event.rx_mgmt.frame_len = len;
+		event.rx_mgmt.ssi_signal = ssi_signal;
 		wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
 	}
 }
@@ -1379,7 +1448,7 @@
 		       enum nl80211_commands cmd, struct nlattr *frame,
 		       struct nlattr *addr, struct nlattr *timed_out,
 		       struct nlattr *freq, struct nlattr *ack,
-		       struct nlattr *cookie)
+		       struct nlattr *cookie, struct nlattr *sig)
 {
 	if (timed_out && addr) {
 		mlme_timeout_event(drv, cmd, addr);
@@ -1412,7 +1481,8 @@
 					   nla_data(frame), nla_len(frame));
 		break;
 	case NL80211_CMD_FRAME:
-		mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame));
+		mlme_event_mgmt(drv, freq, sig, nla_data(frame),
+				nla_len(frame));
 		break;
 	case NL80211_CMD_FRAME_TX_STATUS:
 		mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
@@ -2084,7 +2154,8 @@
 		mlme_event(drv, cmd, tb[NL80211_ATTR_FRAME],
 			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
 			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
-			   tb[NL80211_ATTR_COOKIE]);
+			   tb[NL80211_ATTR_COOKIE],
+			   tb[NL80211_ATTR_RX_SIGNAL_DBM]);
 		break;
 	case NL80211_CMD_CONNECT:
 	case NL80211_CMD_ROAM:
@@ -2094,6 +2165,10 @@
 				   tb[NL80211_ATTR_REQ_IE],
 				   tb[NL80211_ATTR_RESP_IE]);
 		break;
+	case NL80211_CMD_CH_SWITCH_NOTIFY:
+		mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ],
+				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+		break;
 	case NL80211_CMD_DISCONNECT:
 		mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
 				      tb[NL80211_ATTR_MAC],
@@ -2176,7 +2251,7 @@
 	struct nl80211_global *global = arg;
 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct wpa_driver_nl80211_data *drv;
+	struct wpa_driver_nl80211_data *drv, *tmp;
 	int ifidx = -1;
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -2185,15 +2260,11 @@
 	if (tb[NL80211_ATTR_IFINDEX])
 		ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
 
-	dl_list_for_each(drv, &global->interfaces,
-			 struct wpa_driver_nl80211_data, list) {
+	dl_list_for_each_safe(drv, tmp, &global->interfaces,
+			      struct wpa_driver_nl80211_data, list) {
 		if (ifidx == -1 || ifidx == drv->ifindex ||
-		    have_ifidx(drv, ifidx)) {
+		    have_ifidx(drv, ifidx))
 			do_process_drv_event(drv, gnlh->cmd, tb);
-#ifdef ANDROID_P2P
-			break;
-#endif
-		}
 	}
 
 	return NL_SKIP;
@@ -2215,7 +2286,8 @@
 		mlme_event(bss->drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
 			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
 			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
-			   tb[NL80211_ATTR_COOKIE]);
+			   tb[NL80211_ATTR_COOKIE],
+			   tb[NL80211_ATTR_RX_SIGNAL_DBM]);
 		break;
 	case NL80211_CMD_UNEXPECTED_FRAME:
 		nl80211_spurious_frame(bss, tb, 0);
@@ -2314,7 +2386,7 @@
 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
 	struct wiphy_info_data *info = arg;
 	int p2p_go_supported = 0, p2p_client_supported = 0;
-	int p2p_concurrent = 0;
+	int p2p_concurrent = 0, p2p_multichan_concurrent = 0;
 	int auth_supported = 0, connect_supported = 0;
 	struct wpa_driver_capa *capa = info->capa;
 	static struct nla_policy
@@ -2415,6 +2487,8 @@
 
 			if (combination_has_p2p && combination_has_mgd) {
 				p2p_concurrent = 1;
+				if (nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) > 1)
+					p2p_multichan_concurrent = 1;
 				break;
 			}
 
@@ -2482,6 +2556,13 @@
 			   "interface (driver advertised support)");
 		capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
 		capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+
+		if (p2p_multichan_concurrent) {
+			wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
+				   "concurrent (driver advertised support)");
+			capa->flags |=
+				WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT;
+		}
 	}
 
 	if (tb[NL80211_ATTR_TDLS_SUPPORT]) {
@@ -2494,9 +2575,8 @@
 				WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
 		}
 	}
-#ifndef ANDROID_P2P
+
 	if (tb[NL80211_ATTR_DEVICE_AP_SME])
-#endif
 		info->device_ap_sme = 1;
 
 	if (tb[NL80211_ATTR_FEATURE_FLAGS]) {
@@ -2504,6 +2584,9 @@
 
 		if (flags & NL80211_FEATURE_SK_TX_STATUS)
 			info->data_tx_status = 1;
+
+		if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
+			capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
 	}
 
 	if (tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]) {
@@ -2571,17 +2654,35 @@
 	drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
 	drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
 	drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
-	drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
+
+	if (!info.device_ap_sme) {
+		drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
+
+		/*
+		 * No AP SME is currently assumed to also indicate no AP MLME
+		 * in the driver/firmware.
+		 */
+		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME;
+	}
 
 	drv->device_ap_sme = info.device_ap_sme;
 	drv->poll_command_supported = info.poll_command_supported;
 	drv->data_tx_status = info.data_tx_status;
 
+#ifdef ANDROID_P2P
+	if(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
+		/* Driver is new enough to support monitorless mode*/
+		wpa_printf(MSG_DEBUG, "nl80211: Driver is new "
+			  "enough to support monitor-less mode");
+		drv->use_monitor = 0;
+	}
+#else
 	/*
-	 * If poll command is supported mac80211 is new enough to
-	 * have everything we need to not need monitor interfaces.
+	 * If poll command and tx status are supported, mac80211 is new enough
+	 * to have everything we need to not need monitor interfaces.
 	 */
-	drv->use_monitor = !info.poll_command_supported;
+	drv->use_monitor = !info.poll_command_supported || !info.data_tx_status;
+#endif
 
 	if (drv->device_ap_sme && drv->use_monitor) {
 		/*
@@ -2803,10 +2904,7 @@
 	u8 data[2048];
 	struct msghdr msg;
 	struct iovec entry;
-	struct {
-		struct cmsghdr cm;
-		char control[512];
-	} control;
+	u8 control[512];
 	struct cmsghdr *cmsg;
 	int res, found_ee = 0, found_wifi = 0, acked = 0;
 	union wpa_event_data event;
@@ -3309,6 +3407,7 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 
+	bss->in_deinit = 1;
 	if (drv->data_tx_status)
 		eloop_unregister_read_sock(drv->eapol_tx_sock);
 	if (drv->eapol_tx_sock >= 0)
@@ -3402,6 +3501,83 @@
 }
 
 
+static struct nl_msg *
+nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd,
+		    struct wpa_driver_scan_params *params)
+{
+	struct nl_msg *msg;
+	int err;
+	size_t i;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return NULL;
+
+	nl80211_cmd(drv, msg, 0, cmd);
+
+	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) < 0)
+		goto fail;
+
+	if (params->num_ssids) {
+		struct nl_msg *ssids = nlmsg_alloc();
+		if (ssids == NULL)
+			goto fail;
+		for (i = 0; i < params->num_ssids; i++) {
+			wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
+					  params->ssids[i].ssid,
+					  params->ssids[i].ssid_len);
+			if (nla_put(ssids, i + 1, params->ssids[i].ssid_len,
+				    params->ssids[i].ssid) < 0) {
+				nlmsg_free(ssids);
+				goto fail;
+			}
+		}
+		err = nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
+		nlmsg_free(ssids);
+		if (err < 0)
+			goto fail;
+	}
+
+	if (params->extra_ies) {
+		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+			    params->extra_ies, params->extra_ies_len);
+		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
+			    params->extra_ies) < 0)
+			goto fail;
+	}
+
+	if (params->freqs) {
+		struct nl_msg *freqs = nlmsg_alloc();
+		if (freqs == NULL)
+			goto fail;
+		for (i = 0; params->freqs[i]; i++) {
+			wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+				   "MHz", params->freqs[i]);
+			if (nla_put_u32(freqs, i + 1, params->freqs[i]) < 0) {
+				nlmsg_free(freqs);
+				goto fail;
+			}
+		}
+		err = nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES,
+				     freqs);
+		nlmsg_free(freqs);
+		if (err < 0)
+			goto fail;
+	}
+
+	os_free(drv->filter_ssids);
+	drv->filter_ssids = params->filter_ssids;
+	params->filter_ssids = NULL;
+	drv->num_filter_ssids = params->num_filter_ssids;
+
+	return msg;
+
+fail:
+	nlmsg_free(msg);
+	return NULL;
+}
+
+
 /**
  * wpa_driver_nl80211_scan - Request the driver to initiate scan
  * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
@@ -3413,60 +3589,22 @@
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-	int ret = 0, timeout;
-	struct nl_msg *msg, *ssids, *freqs, *rates;
-	size_t i;
+	int ret = -1, timeout;
+	struct nl_msg *msg, *rates = NULL;
 
 	drv->scan_for_auth = 0;
 
-	msg = nlmsg_alloc();
-	ssids = nlmsg_alloc();
-	freqs = nlmsg_alloc();
-	rates = nlmsg_alloc();
-	if (!msg || !ssids || !freqs || !rates) {
-		nlmsg_free(msg);
-		nlmsg_free(ssids);
-		nlmsg_free(freqs);
-		nlmsg_free(rates);
+	msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params);
+	if (!msg)
 		return -1;
-	}
-
-	os_free(drv->filter_ssids);
-	drv->filter_ssids = params->filter_ssids;
-	params->filter_ssids = NULL;
-	drv->num_filter_ssids = params->num_filter_ssids;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_TRIGGER_SCAN);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
-	for (i = 0; i < params->num_ssids; i++) {
-		wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
-				  params->ssids[i].ssid,
-				  params->ssids[i].ssid_len);
-		NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len,
-			params->ssids[i].ssid);
-	}
-	if (params->num_ssids)
-		nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
-
-	if (params->extra_ies) {
-		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
-			    params->extra_ies, params->extra_ies_len);
-		NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len,
-			params->extra_ies);
-	}
-
-	if (params->freqs) {
-		for (i = 0; params->freqs[i]; i++) {
-			wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
-				   "MHz", params->freqs[i]);
-			NLA_PUT_U32(freqs, i + 1, params->freqs[i]);
-		}
-		nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
-	}
 
 	if (params->p2p_probe) {
+		wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+		rates = nlmsg_alloc();
+		if (rates == NULL)
+			goto nla_put_failure;
+
 		/*
 		 * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
 		 * by masking out everything else apart from the OFDM rates 6,
@@ -3475,7 +3613,9 @@
 		 */
 		NLA_PUT(rates, NL80211_BAND_2GHZ, 8,
 			"\x0c\x12\x18\x24\x30\x48\x60\x6c");
-		nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates);
+		if (nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates) <
+		    0)
+			goto nla_put_failure;
 
 		NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
 	}
@@ -3528,9 +3668,7 @@
 			       drv, drv->ctx);
 
 nla_put_failure:
-	nlmsg_free(ssids);
 	nlmsg_free(msg);
-	nlmsg_free(freqs);
 	nlmsg_free(rates);
 	return ret;
 }
@@ -3549,8 +3687,10 @@
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-	int ret = 0;
-	struct nl_msg *msg, *ssids, *freqs, *match_set_ssid, *match_sets;
+	int ret = -1;
+	struct nl_msg *msg;
+	struct nl_msg *match_set_ssid = NULL, *match_sets = NULL;
+	struct nl_msg *match_set_rssi = NULL;
 	size_t i;
 
 #ifdef ANDROID
@@ -3558,30 +3698,18 @@
 		return android_pno_start(bss, params);
 #endif /* ANDROID */
 
-	msg = nlmsg_alloc();
-	ssids = nlmsg_alloc();
-	freqs = nlmsg_alloc();
-	if (!msg || !ssids || !freqs) {
-		nlmsg_free(msg);
-		nlmsg_free(ssids);
-		nlmsg_free(freqs);
-		return -1;
-	}
-
-	os_free(drv->filter_ssids);
-	drv->filter_ssids = params->filter_ssids;
-	params->filter_ssids = NULL;
-	drv->num_filter_ssids = params->num_filter_ssids;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_START_SCHED_SCAN);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+	msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params);
+	if (!msg)
+		goto nla_put_failure;
 
 	NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval);
 
-	if (drv->num_filter_ssids &&
-	    (int) drv->num_filter_ssids <= drv->capa.max_match_sets) {
+	if ((drv->num_filter_ssids &&
+	    (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
+	    params->filter_rssi) {
 		match_sets = nlmsg_alloc();
+		if (match_sets == NULL)
+			goto nla_put_failure;
 
 		for (i = 0; i < drv->num_filter_ssids; i++) {
 			wpa_hexdump_ascii(MSG_MSGDUMP,
@@ -3590,45 +3718,35 @@
 					  drv->filter_ssids[i].ssid_len);
 
 			match_set_ssid = nlmsg_alloc();
-			nla_put(match_set_ssid,
+			if (match_set_ssid == NULL)
+				goto nla_put_failure;
+			NLA_PUT(match_set_ssid,
 				NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
 				drv->filter_ssids[i].ssid_len,
 				drv->filter_ssids[i].ssid);
 
-			nla_put_nested(match_sets, i + 1, match_set_ssid);
-
-			nlmsg_free(match_set_ssid);
+			if (nla_put_nested(match_sets, i + 1, match_set_ssid) <
+			    0)
+				goto nla_put_failure;
 		}
 
-		nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH,
-			       match_sets);
-		nlmsg_free(match_sets);
-	}
-
-	for (i = 0; i < params->num_ssids; i++) {
-		wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan SSID",
-				  params->ssids[i].ssid,
-				  params->ssids[i].ssid_len);
-		NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len,
-			params->ssids[i].ssid);
-	}
-	if (params->num_ssids)
-		nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
-
-	if (params->extra_ies) {
-		wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan extra IEs",
-				  params->extra_ies, params->extra_ies_len);
-		NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len,
-			params->extra_ies);
-	}
-
-	if (params->freqs) {
-		for (i = 0; params->freqs[i]; i++) {
-			wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
-				   "MHz", params->freqs[i]);
-			NLA_PUT_U32(freqs, i + 1, params->freqs[i]);
+		if (params->filter_rssi) {
+			match_set_rssi = nlmsg_alloc();
+			if (match_set_rssi == NULL)
+				goto nla_put_failure;
+			NLA_PUT_U32(match_set_rssi,
+				    NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+				    params->filter_rssi);
+			wpa_printf(MSG_MSGDUMP,
+				   "nl80211: Sched scan RSSI filter %d dBm",
+				   params->filter_rssi);
+			if (nla_put_nested(match_sets, 0, match_set_rssi) < 0)
+				goto nla_put_failure;
 		}
-		nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
+
+		if (nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH,
+				   match_sets) < 0)
+			goto nla_put_failure;
 	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -3646,9 +3764,10 @@
 		   "scan interval %d msec", ret, interval);
 
 nla_put_failure:
-	nlmsg_free(ssids);
+	nlmsg_free(match_set_ssid);
+	nlmsg_free(match_sets);
+	nlmsg_free(match_set_rssi);
 	nlmsg_free(msg);
-	nlmsg_free(freqs);
 	return ret;
 }
 
@@ -3896,8 +4015,8 @@
 		return NL_SKIP;
 	}
 
-	tmp = os_realloc(res->res,
-			 (res->num + 1) * sizeof(struct wpa_scan_res *));
+	tmp = os_realloc_array(res->res, res->num + 1,
+			       sizeof(struct wpa_scan_res *));
 	if (tmp == NULL) {
 		os_free(r);
 		return NL_SKIP;
@@ -4100,6 +4219,10 @@
 			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
 				    WLAN_CIPHER_SUITE_CCMP);
 			break;
+		case WPA_ALG_GCMP:
+			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+				    WLAN_CIPHER_SUITE_GCMP);
+			break;
 		case WPA_ALG_IGTK:
 			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
 				    WLAN_CIPHER_SUITE_AES_CMAC);
@@ -4240,6 +4363,9 @@
 	case WPA_ALG_CCMP:
 		NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP);
 		break;
+	case WPA_ALG_GCMP:
+		NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_GCMP);
+		break;
 	case WPA_ALG_IGTK:
 		NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
 			    WLAN_CIPHER_SUITE_AES_CMAC);
@@ -4692,7 +4818,9 @@
 		return NL_SKIP;
 
 	nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
-		mode = os_realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode));
+		mode = os_realloc_array(phy_info->modes,
+					*phy_info->num_modes + 1,
+					sizeof(*mode));
 		if (!mode)
 			return NL_SKIP;
 		phy_info->modes = mode;
@@ -4731,6 +4859,18 @@
 			os_memcpy(mode->mcs_set, mcs, 16);
 		}
 
+		if (tb_band[NL80211_BAND_ATTR_VHT_CAPA]) {
+			mode->vht_capab = nla_get_u32(
+				tb_band[NL80211_BAND_ATTR_VHT_CAPA]);
+		}
+
+		if (tb_band[NL80211_BAND_ATTR_VHT_MCS_SET] &&
+		    nla_len(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET])) {
+			u8 *mcs;
+			mcs = nla_data(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
+			os_memcpy(mode->vht_mcs_set, mcs, 8);
+		}
+
 		nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
 			nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
 				  nla_len(nl_freq), freq_policy);
@@ -4739,7 +4879,8 @@
 			mode->num_channels++;
 		}
 
-		mode->channels = os_zalloc(mode->num_channels * sizeof(struct hostapd_channel_data));
+		mode->channels = os_calloc(mode->num_channels,
+					   sizeof(struct hostapd_channel_data));
 		if (!mode->channels)
 			return NL_SKIP;
 
@@ -4801,7 +4942,7 @@
 			mode->num_rates++;
 		}
 
-		mode->rates = os_zalloc(mode->num_rates * sizeof(int));
+		mode->rates = os_calloc(mode->num_rates, sizeof(int));
 		if (!mode->rates)
 			return NL_SKIP;
 
@@ -4846,7 +4987,7 @@
 	if (mode11g_idx < 0)
 		return modes; /* 2.4 GHz band not supported at all */
 
-	nmodes = os_realloc(modes, (*num_modes + 1) * sizeof(*nmodes));
+	nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes));
 	if (nmodes == NULL)
 		return modes; /* Could not add 802.11b mode */
 
@@ -5191,7 +5332,9 @@
 	if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
 		if (freq == 0)
 			freq = bss->freq;
-		return nl80211_send_frame_cmd(bss, freq, 0,
+		return nl80211_send_frame_cmd(bss, freq,
+					      (int) freq == bss->freq ? 0 :
+					      wait_time,
 					      data, data_len,
 					      &drv->send_action_cookie,
 					      no_cck, noack, offchanok);
@@ -5358,6 +5501,8 @@
 	num_suites = 0;
 	if (params->pairwise_ciphers & WPA_CIPHER_CCMP)
 		suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
+	if (params->pairwise_ciphers & WPA_CIPHER_GCMP)
+		suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP;
 	if (params->pairwise_ciphers & WPA_CIPHER_TKIP)
 		suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP;
 	if (params->pairwise_ciphers & WPA_CIPHER_WEP104)
@@ -5374,6 +5519,10 @@
 		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
 			    WLAN_CIPHER_SUITE_CCMP);
 		break;
+	case WPA_CIPHER_GCMP:
+		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
+			    WLAN_CIPHER_SUITE_GCMP);
+		break;
 	case WPA_CIPHER_TKIP:
 		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
 			    WLAN_CIPHER_SUITE_TKIP);
@@ -5403,6 +5552,11 @@
 			wpabuf_head(params->assocresp_ies));
 	}
 
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)  {
+		NLA_PUT_U16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT,
+			    params->ap_max_inactivity);
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
@@ -5413,7 +5567,6 @@
 				params->short_slot_time, params->ht_opmode,
 				params->isolate, params->basic_rates);
 	}
-
 	return ret;
  nla_put_failure:
 	nlmsg_free(msg);
@@ -5540,7 +5693,8 @@
 		NLA_PUT_U8(wme, NL80211_STA_WME_MAX_SP,
 				(params->qosinfo > WMM_QOSINFO_STA_SP_SHIFT) &
 				WMM_QOSINFO_STA_SP_MASK);
-		nla_put_nested(msg, NL80211_ATTR_STA_WME, wme);
+		if (nla_put_nested(msg, NL80211_ATTR_STA_WME, wme) < 0)
+			goto nla_put_failure;
 	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -5851,8 +6005,8 @@
 		case IEEE80211_RADIOTAP_RATE:
 			datarate = *iter.this_arg * 5;
 			break;
-		case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
-			ssi_signal = *iter.this_arg;
+		case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
+			ssi_signal = (s8) *iter.this_arg;
 			break;
 		}
 	}
@@ -6149,7 +6303,6 @@
 		if (nl80211_mgmt_subscribe_ap(bss))
 			return -1;
 
-#ifndef ANDROID_P2P
 	if (drv->device_ap_sme && !drv->use_monitor)
 		if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
 			return -1;
@@ -6157,15 +6310,17 @@
 	if (!drv->device_ap_sme && drv->use_monitor &&
 	    nl80211_create_monitor_interface(drv) &&
 	    !drv->device_ap_sme)
-#else
-	if (drv->device_ap_sme)
+		return -1;
+
+#ifdef ANDROID_P2P
+	if (drv->device_ap_sme && drv->use_monitor)
 		if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
 			return -1;
 
 	if (drv->use_monitor &&
 	    nl80211_create_monitor_interface(drv))
-#endif
 		return -1;
+#endif
 
 	if (drv->device_ap_sme &&
 	    wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
@@ -6268,8 +6423,8 @@
 	pos = (u8 *) (hdr + 1);
 
 	if (qos) {
-		/* add an empty QoS header if needed */
-		pos[0] = 0;
+		/* Set highest priority in QoS header */
+		pos[0] = 7;
 		pos[1] = 0;
 		pos += 2;
 	}
@@ -6568,6 +6723,12 @@
 		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
 		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
 	}
+	if (params->bg_scan_period >= 0) {
+		wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
+			   params->bg_scan_period);
+		NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
+			    params->bg_scan_period);
+	}
 	if (params->ssid) {
 		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
 				  params->ssid, params->ssid_len);
@@ -6636,6 +6797,9 @@
 		case CIPHER_CCMP:
 			cipher = WLAN_CIPHER_SUITE_CCMP;
 			break;
+		case CIPHER_GCMP:
+			cipher = WLAN_CIPHER_SUITE_GCMP;
+			break;
 		case CIPHER_TKIP:
 		default:
 			cipher = WLAN_CIPHER_SUITE_TKIP;
@@ -6657,6 +6821,9 @@
 		case CIPHER_CCMP:
 			cipher = WLAN_CIPHER_SUITE_CCMP;
 			break;
+		case CIPHER_GCMP:
+			cipher = WLAN_CIPHER_SUITE_GCMP;
+			break;
 		case CIPHER_TKIP:
 		default:
 			cipher = WLAN_CIPHER_SUITE_TKIP;
@@ -6765,6 +6932,12 @@
 		drv->assoc_freq = params->freq;
 	} else
 		drv->assoc_freq = 0;
+	if (params->bg_scan_period >= 0) {
+		wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
+			   params->bg_scan_period);
+		NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
+			    params->bg_scan_period);
+	}
 	if (params->ssid) {
 		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
 				  params->ssid, params->ssid_len);
@@ -6793,6 +6966,9 @@
 		case CIPHER_CCMP:
 			cipher = WLAN_CIPHER_SUITE_CCMP;
 			break;
+		case CIPHER_GCMP:
+			cipher = WLAN_CIPHER_SUITE_GCMP;
+			break;
 		case CIPHER_TKIP:
 		default:
 			cipher = WLAN_CIPHER_SUITE_TKIP;
@@ -6815,6 +6991,9 @@
 		case CIPHER_CCMP:
 			cipher = WLAN_CIPHER_SUITE_CCMP;
 			break;
+		case CIPHER_GCMP:
+			cipher = WLAN_CIPHER_SUITE_GCMP;
+			break;
 		case CIPHER_TKIP:
 		default:
 			cipher = WLAN_CIPHER_SUITE_TKIP;
@@ -6968,6 +7147,11 @@
 		return ret;
 	}
 
+	if (is_p2p_interface(nlmode))
+		nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+	else if (drv->disabled_11b_rates)
+		nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+
 	if (is_ap_interface(nlmode)) {
 		nl80211_mgmt_unsubscribe(bss, "start AP");
 		/* Setup additional AP mode functionality if needed */
@@ -6980,7 +7164,7 @@
 		nl80211_mgmt_unsubscribe(bss, "mode change");
 	}
 
-	if (!is_ap_interface(nlmode) &&
+	if (!bss->in_deinit && !is_ap_interface(nlmode) &&
 	    nl80211_mgmt_subscribe_non_ap(bss) < 0)
 		wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
 			   "frame processing - ignore for now");
@@ -7207,6 +7391,8 @@
 	return -ENOBUFS;
 }
 
+#endif /* HOSTAPD || CONFIG_AP */
+
 
 static int get_sta_handler(struct nl_msg *msg, void *arg)
 {
@@ -7220,6 +7406,7 @@
 		[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
 	};
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -7255,6 +7442,9 @@
 	if (stats[NL80211_STA_INFO_TX_PACKETS])
 		data->tx_packets =
 			nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
+	if (stats[NL80211_STA_INFO_TX_FAILED])
+		data->tx_retry_failed =
+			nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
 
 	return NL_SKIP;
 }
@@ -7283,6 +7473,8 @@
 }
 
 
+#if defined(HOSTAPD) || defined(CONFIG_AP)
+
 static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
 				    int cw_min, int cw_max, int burst_time)
 {
@@ -7402,8 +7594,12 @@
 			   int reason)
 {
 	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt mgmt;
 
+	if (drv->device_ap_sme)
+		return wpa_driver_nl80211_sta_remove(bss, addr);
+
 	memset(&mgmt, 0, sizeof(mgmt));
 	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 					  WLAN_FC_STYPE_DEAUTH);
@@ -7421,8 +7617,12 @@
 			     int reason)
 {
 	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt mgmt;
 
+	if (drv->device_ap_sme)
+		return wpa_driver_nl80211_sta_remove(bss, addr);
+
 	memset(&mgmt, 0, sizeof(mgmt));
 	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 					  WLAN_FC_STYPE_DISASSOC);
@@ -7458,8 +7658,8 @@
 	else
 		old = NULL;
 
-	drv->if_indices = os_realloc(old,
-				     sizeof(int) * (drv->num_if_indices + 1));
+	drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1,
+					   sizeof(int));
 	if (!drv->if_indices) {
 		if (!old)
 			drv->if_indices = drv->default_if_indices;
@@ -7523,9 +7723,16 @@
 					    bridge_ifname, name) < 0)
 				return -1;
 		}
-		linux_set_iface_flags(drv->global->ioctl_sock, name, 1);
+		if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
+				   "interface %s up", name);
+		}
 		return i802_set_sta_vlan(priv, addr, name, 0);
 	} else {
+		if (bridge_ifname)
+			linux_br_del_if(drv->global->ioctl_sock, bridge_ifname,
+					name);
+
 		i802_set_sta_vlan(priv, addr, bss->ifname, 0);
 		return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
 						    name);
@@ -7893,7 +8100,12 @@
 	if (ifindex <= 0)
 		return -1;
 
+	nl80211_remove_iface(drv, ifindex);
+
 #ifdef HOSTAPD
+	if (type != WPA_IF_AP_BSS)
+		return 0;
+
 	if (bss->added_if_into_bridge) {
 		if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
 				    bss->ifname) < 0)
@@ -7907,13 +8119,6 @@
 				   "bridge %s: %s",
 				   bss->brname, strerror(errno));
 	}
-#endif /* HOSTAPD */
-
-	nl80211_remove_iface(drv, ifindex);
-
-#ifdef HOSTAPD
-	if (type != WPA_IF_AP_BSS)
-		return 0;
 
 	if (bss != &drv->first_bss) {
 		struct i802_bss *tbss;
@@ -7967,14 +8172,15 @@
 	if (!msg)
 		return -1;
 
+	wpa_printf(MSG_DEBUG, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d "
+		   "no_ack=%d offchanok=%d",
+		   freq, wait, no_cck, no_ack, offchanok);
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME);
 
 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
-#ifndef ANDROID_P2P
 	if (wait)
 		NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
-#endif
 	if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
 		NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
 	if (no_cck)
@@ -8248,7 +8454,8 @@
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
 			   "(%s)", ret, strerror(-ret));
-	}
+	} else
+		drv->disabled_11b_rates = disabled;
 
 	return ret;
 
@@ -8269,6 +8476,16 @@
 }
 
 
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
+		return -1;
+	return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
+
+
 static void wpa_driver_nl80211_resume(void *priv)
 {
 	struct i802_bss *bss = priv;
@@ -8332,6 +8549,7 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg, *cqm = NULL;
+	int ret = -1;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
 		   "hysteresis=%d", threshold, hysteresis);
@@ -8346,20 +8564,20 @@
 
 	cqm = nlmsg_alloc();
 	if (cqm == NULL)
-		return -1;
+		goto nla_put_failure;
 
 	NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold);
 	NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis);
-	nla_put_nested(msg, NL80211_ATTR_CQM, cqm);
+	if (nla_put_nested(msg, NL80211_ATTR_CQM, cqm) < 0)
+		goto nla_put_failure;
 
-	if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
-		return 0;
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 
 nla_put_failure:
 	nlmsg_free(cqm);
 	nlmsg_free(msg);
-	return -1;
+	return ret;
 }
 
 
@@ -8407,11 +8625,9 @@
 			   MACSTR,
 			   driver->phyname, driver->first_bss.ifname,
 			   MAC2STR(driver->first_bss.addr));
-#ifdef ANDROID_P2P
-		if(is_ap_interface(driver->nlmode))
+		if (is_ap_interface(driver->nlmode))
 			freq = driver->first_bss.freq;
 		else
-#endif
 			freq = nl80211_get_assoc_freq(driver);
 		wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d",
 			   drv->phyname, freq);
@@ -8998,7 +9214,6 @@
 #if defined(HOSTAPD) || defined(CONFIG_AP)
 	.get_seqnum = i802_get_seqnum,
 	.flush = i802_flush,
-	.read_sta_data = i802_read_sta_data,
 	.get_inact_sec = i802_get_inact_sec,
 	.sta_clear_stats = i802_sta_clear_stats,
 	.set_rts = i802_set_rts,
@@ -9008,6 +9223,7 @@
 	.sta_deauth = i802_sta_deauth,
 	.sta_disassoc = i802_sta_disassoc,
 #endif /* HOSTAPD || CONFIG_AP */
+	.read_sta_data = i802_read_sta_data,
 	.set_freq = i802_set_freq,
 	.send_action = wpa_driver_nl80211_send_action,
 	.send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,
@@ -9016,6 +9232,7 @@
 	wpa_driver_nl80211_cancel_remain_on_channel,
 	.probe_req_report = wpa_driver_nl80211_probe_req_report,
 	.deinit_ap = wpa_driver_nl80211_deinit_ap,
+	.deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
 	.resume = wpa_driver_nl80211_resume,
 	.send_ft_action = nl80211_send_ft_action,
 	.signal_monitor = nl80211_signal_monitor,
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 81a328a..9481cbf 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -152,7 +152,7 @@
 		return NULL;
 	}
 
-	results->res = os_zalloc(num * sizeof(struct wpa_scan_res *));
+	results->res = os_calloc(num, sizeof(struct wpa_scan_res *));
 	if (results->res == NULL) {
 		os_free(results);
 		os_free(buf);
@@ -651,7 +651,7 @@
 	os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
 	if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) <
 	    0) {
-		perror("bind(PF_UNIX)");
+		perror("privsep-set-params priv-sock: bind(PF_UNIX)");
 		close(drv->priv_socket);
 		drv->priv_socket = -1;
 		unlink(drv->own_socket_path);
@@ -676,7 +676,7 @@
 	os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path));
 	if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
 	{
-		perror("bind(PF_UNIX)");
+		perror("privsep-set-params cmd-sock: bind(PF_UNIX)");
 		close(drv->cmd_socket);
 		drv->cmd_socket = -1;
 		unlink(drv->own_cmd_path);
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
index 61b75b1..0a9078a 100644
--- a/src/drivers/driver_roboswitch.c
+++ b/src/drivers/driver_roboswitch.c
@@ -2,14 +2,8 @@
  * WPA Supplicant - roboswitch driver interface
  * Copyright (c) 2008-2009 Jouke Witteveen
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c
index 43b30e9..e7bf195 100644
--- a/src/drivers/driver_test.c
+++ b/src/drivers/driver_test.c
@@ -1275,7 +1275,7 @@
 			alen = sizeof(addr_un);
 		}
 		if (bind(drv->test_socket, addr, alen) < 0) {
-			perror("bind(PF_UNIX)");
+			perror("test-driver-init: bind(PF_UNIX)");
 			close(drv->test_socket);
 			if (drv->own_socket_path)
 				unlink(drv->own_socket_path);
@@ -1478,7 +1478,7 @@
 	if (res == NULL)
 		return NULL;
 
-	res->res = os_zalloc(drv->num_scanres * sizeof(struct wpa_scan_res *));
+	res->res = os_calloc(drv->num_scanres, sizeof(struct wpa_scan_res *));
 	if (res->res == NULL) {
 		os_free(res);
 		return NULL;
@@ -2252,7 +2252,7 @@
 	os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
 	if (bind(drv->test_socket, (struct sockaddr *) &addr,
 		 sizeof(addr)) < 0) {
-		perror("bind(PF_UNIX)");
+		perror("test-driver-attach: bind(PF_UNIX)");
 		close(drv->test_socket);
 		unlink(drv->own_socket_path);
 		os_free(drv->own_socket_path);
@@ -2570,15 +2570,14 @@
 
 	*num_modes = 3;
 	*flags = 0;
-	modes = os_zalloc(*num_modes * sizeof(struct hostapd_hw_modes));
+	modes = os_calloc(*num_modes, sizeof(struct hostapd_hw_modes));
 	if (modes == NULL)
 		return NULL;
 	modes[0].mode = HOSTAPD_MODE_IEEE80211G;
 	modes[0].num_channels = 11;
 	modes[0].num_rates = 12;
-	modes[0].channels =
-		os_zalloc(11 * sizeof(struct hostapd_channel_data));
-	modes[0].rates = os_zalloc(modes[0].num_rates * sizeof(int));
+	modes[0].channels = os_calloc(11, sizeof(struct hostapd_channel_data));
+	modes[0].rates = os_calloc(modes[0].num_rates, sizeof(int));
 	if (modes[0].channels == NULL || modes[0].rates == NULL)
 		goto fail;
 	for (i = 0; i < 11; i++) {
@@ -2602,9 +2601,8 @@
 	modes[1].mode = HOSTAPD_MODE_IEEE80211B;
 	modes[1].num_channels = 11;
 	modes[1].num_rates = 4;
-	modes[1].channels =
-		os_zalloc(11 * sizeof(struct hostapd_channel_data));
-	modes[1].rates = os_zalloc(modes[1].num_rates * sizeof(int));
+	modes[1].channels = os_calloc(11, sizeof(struct hostapd_channel_data));
+	modes[1].rates = os_calloc(modes[1].num_rates, sizeof(int));
 	if (modes[1].channels == NULL || modes[1].rates == NULL)
 		goto fail;
 	for (i = 0; i < 11; i++) {
@@ -2620,8 +2618,8 @@
 	modes[2].mode = HOSTAPD_MODE_IEEE80211A;
 	modes[2].num_channels = 1;
 	modes[2].num_rates = 8;
-	modes[2].channels = os_zalloc(sizeof(struct hostapd_channel_data));
-	modes[2].rates = os_zalloc(modes[2].num_rates * sizeof(int));
+	modes[2].channels = os_calloc(1, sizeof(struct hostapd_channel_data));
+	modes[2].rates = os_calloc(modes[2].num_rates, sizeof(int));
 	if (modes[2].channels == NULL || modes[2].rates == NULL)
 		goto fail;
 	modes[2].channels[0].chan = 60;
@@ -2822,17 +2820,19 @@
 
 static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type)
 {
-	struct wpa_driver_test_data *drv = priv;
+	struct test_driver_bss *dbss = priv;
+	struct wpa_driver_test_data *drv = dbss->drv;
 	wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
 	if (!drv->p2p)
 		return -1;
-	return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL);
+	return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL, 0);
 }
 
 
 static int wpa_driver_test_p2p_stop_find(void *priv)
 {
-	struct wpa_driver_test_data *drv = priv;
+	struct test_driver_bss *dbss = priv;
+	struct wpa_driver_test_data *drv = dbss->drv;
 	wpa_printf(MSG_DEBUG, "%s", __func__);
 	if (!drv->p2p)
 		return -1;
@@ -2843,7 +2843,8 @@
 
 static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout)
 {
-	struct wpa_driver_test_data *drv = priv;
+	struct test_driver_bss *dbss = priv;
+	struct wpa_driver_test_data *drv = dbss->drv;
 	wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
 	if (!drv->p2p)
 		return -1;
@@ -2857,7 +2858,8 @@
 				       unsigned int force_freq,
 				       int persistent_group)
 {
-	struct wpa_driver_test_data *drv = priv;
+	struct test_driver_bss *dbss = priv;
+	struct wpa_driver_test_data *drv = dbss->drv;
 	wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d "
 		   "go_intent=%d "
 		   "own_interface_addr=" MACSTR " force_freq=%u "
@@ -2867,13 +2869,15 @@
 	if (!drv->p2p)
 		return -1;
 	return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent,
-			   own_interface_addr, force_freq, persistent_group);
+			   own_interface_addr, force_freq, persistent_group,
+			   NULL, 0, 0);
 }
 
 
 static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr)
 {
-	struct wpa_driver_test_data *drv = priv;
+	struct test_driver_bss *dbss = priv;
+	struct wpa_driver_test_data *drv = dbss->drv;
 	wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")",
 		   __func__, MAC2STR(peer_addr));
 	if (!drv->p2p)
@@ -2885,7 +2889,8 @@
 
 static int wpa_driver_test_p2p_group_formation_failed(void *priv)
 {
-	struct wpa_driver_test_data *drv = priv;
+	struct test_driver_bss *dbss = priv;
+	struct wpa_driver_test_data *drv = dbss->drv;
 	wpa_printf(MSG_DEBUG, "%s", __func__);
 	if (!drv->p2p)
 		return -1;
@@ -2897,7 +2902,8 @@
 static int wpa_driver_test_p2p_set_params(void *priv,
 					  const struct p2p_params *params)
 {
-	struct wpa_driver_test_data *drv = priv;
+	struct test_driver_bss *dbss = priv;
+	struct wpa_driver_test_data *drv = dbss->drv;
 	wpa_printf(MSG_DEBUG, "%s", __func__);
 	if (!drv->p2p)
 		return -1;
@@ -2912,7 +2918,7 @@
 
 static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
 			 unsigned int num_req_dev_types,
-			 const u8 *req_dev_types, const u8 *dev_id)
+			 const u8 *req_dev_types, const u8 *dev_id, u16 pw_id)
 {
 	struct wpa_driver_test_data *drv = ctx;
 	struct wpa_driver_scan_params params;
@@ -2933,8 +2939,8 @@
 
 #if 0 /* TODO: WPS IE */
 	wpa_s->wps->dev.p2p = 1;
-	wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
-					WPS_REQ_ENROLLEE);
+	wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
+					wpa_s->wps->uuid, WPS_REQ_ENROLLEE);
 #else
 	wps_ie = wpabuf_alloc(1);
 #endif
@@ -2961,11 +2967,6 @@
 		break;
 	case P2P_SCAN_FULL:
 		break;
-	case P2P_SCAN_SPECIFIC:
-		social_channels[0] = freq;
-		social_channels[1] = 0;
-		params.freqs = social_channels;
-		break;
 	case P2P_SCAN_SOCIAL_PLUS_ONE:
 		social_channels[3] = freq;
 		params.freqs = social_channels;
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 204de34..bd37ca1 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -40,13 +40,7 @@
 static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv);
 static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv);
 static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg);
-#ifdef ANDROID
-extern int wpa_driver_wext_driver_cmd(void *priv, char *cmd, char *buf,
-					size_t buf_len);
-extern int wpa_driver_wext_combo_scan(void *priv,
-					struct wpa_driver_scan_params *params);
-extern int wpa_driver_signal_poll(void *priv, struct wpa_signal_info *si);
-#endif
+
 
 int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
 				   int idx, u32 value)
@@ -484,19 +478,10 @@
 				drv->assoc_req_ies = NULL;
 				os_free(drv->assoc_resp_ies);
 				drv->assoc_resp_ies = NULL;
-#ifdef ANDROID
-				if (!drv->skip_disconnect) {
-					drv->skip_disconnect = 1;
-#endif
 				wpa_supplicant_event(drv->ctx, EVENT_DISASSOC,
 						     NULL);
-#ifdef ANDROID
-				}
-#endif
+			
 			} else {
-#ifdef ANDROID
-				drv->skip_disconnect = 0;
-#endif
 				wpa_driver_wext_event_assoc_ies(drv);
 				wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
 						     NULL);
@@ -584,10 +569,28 @@
 		   del ? "removed" : "added");
 
 	if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) {
-		if (del)
+		if (del) {
+			if (drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+					   "already set - ignore event");
+				return;
+			}
 			drv->if_removed = 1;
-		else
+		} else {
+			if (if_nametoindex(drv->ifname) == 0) {
+				wpa_printf(MSG_DEBUG, "WEXT: Interface %s "
+					   "does not exist - ignore "
+					   "RTM_NEWLINK",
+					   drv->ifname);
+				return;
+			}
+			if (!drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+					   "already cleared - ignore event");
+				return;
+			}
 			drv->if_removed = 0;
+		}
 	}
 
 	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
@@ -643,6 +646,7 @@
 	struct wpa_driver_wext_data *drv = ctx;
 	int attrlen, rta_len;
 	struct rtattr *attr;
+	char namebuf[IFNAMSIZ];
 
 	if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) {
 		wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
@@ -665,9 +669,25 @@
 	}
 
 	if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
-		wpa_printf(MSG_DEBUG, "WEXT: Interface up");
-		drv->if_disabled = 0;
-		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
+		if (if_indextoname(ifi->ifi_index, namebuf) &&
+		    linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s is down",
+				   namebuf);
+		} else if (if_nametoindex(drv->ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s does not exist",
+				   drv->ifname);
+		} else if (drv->if_removed) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s is marked "
+				   "removed", drv->ifname);
+		} else {
+			wpa_printf(MSG_DEBUG, "WEXT: Interface up");
+			drv->if_disabled = 0;
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+					     NULL);
+		}
 	}
 
 	/*
@@ -843,7 +863,6 @@
 #ifdef ANDROID
 	drv->errors = 0;
 	drv->driver_is_started = TRUE;
-	drv->skip_disconnect = 0;
 	drv->bgscan_enabled = 0;
 #endif /* ANDROID */
 
@@ -1007,13 +1026,6 @@
 	const u8 *ssid = params->ssids[0].ssid;
 	size_t ssid_len = params->ssids[0].ssid_len;
 
-#ifdef ANDROID
-	if (drv->capa.max_scan_ssids > 1) {
-		ret = wpa_driver_wext_combo_scan(priv, params);
-		goto scan_out;
-	}
-#endif
-
 	if (ssid_len > IW_ESSID_MAX_SIZE) {
 		wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
 			   __FUNCTION__, (unsigned long) ssid_len);
@@ -1039,9 +1051,6 @@
 		ret = -1;
 	}
 
-#ifdef ANDROID
-scan_out:
-#endif
 	/* Not all drivers generate "scan completed" wireless event, so try to
 	 * read results after a timeout. */
 	timeout = 10;
@@ -1405,8 +1414,8 @@
 	if (data->ie)
 		os_memcpy(pos, data->ie, data->ie_len);
 
-	tmp = os_realloc(res->res,
-			 (res->num + 1) * sizeof(struct wpa_scan_res *));
+	tmp = os_realloc_array(res->res, res->num + 1,
+			       sizeof(struct wpa_scan_res *));
 	if (tmp == NULL) {
 		os_free(r);
 		return;
@@ -1566,6 +1575,7 @@
 		}
 		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
 			WPA_DRIVER_CAPA_ENC_WEP104;
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP128;
 		if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
 			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
 		if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
@@ -1575,11 +1585,7 @@
 		drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
 			WPA_DRIVER_AUTH_SHARED |
 			WPA_DRIVER_AUTH_LEAP;
-#ifdef ANDROID
-		drv->capa.max_scan_ssids = WEXT_CSCAN_AMOUNT;
-#else
 		drv->capa.max_scan_ssids = 1;
-#endif
 
 		wpa_printf(MSG_DEBUG, "  capabilities: key_mgmt 0x%x enc 0x%x "
 			   "flags 0x%x",
@@ -2051,9 +2057,7 @@
 	int value;
 
 	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
-#ifdef ANDROID
-	drv->skip_disconnect = 0;
-#endif
+
 	if (drv->cfg80211) {
 		/*
 		 * Stop cfg80211 from trying to associate before we are done
diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h
index 7e0b28b..c4a5bc9 100644
--- a/src/drivers/driver_wext.h
+++ b/src/drivers/driver_wext.h
@@ -48,7 +48,6 @@
 #ifdef ANDROID
 	int errors;
 	int driver_is_started;
-	int skip_disconnect;
 	int bgscan_enabled;
 #endif /* ANDROID */
 };
diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c
index 618db26..e0f0f22 100644
--- a/src/drivers/driver_wired.c
+++ b/src/drivers/driver_wired.c
@@ -3,14 +3,8 @@
  * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 0cc81f9..c7a98d3 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -30,7 +30,7 @@
 ifdef CONFIG_LIBNL32
   DRV_LIBS += -lnl-3
   DRV_LIBS += -lnl-genl-3
-  DRV_CFLAGS += -DCONFIG_LIBNL20
+  DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3
 else
   ifdef CONFIG_LIBNL_TINY
     DRV_LIBS += -lnl-tiny
@@ -142,6 +142,28 @@
 DRV_OBJS += ../src/drivers/rfkill.o
 endif
 
+ifdef CONFIG_VLAN_NETLINK
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+ifdef CONFIG_LIBNL32
+  DRV_LIBS += -lnl-3
+  DRV_LIBS += -lnl-genl-3
+  DRV_LIBS += -lnl-route-3
+  DRV_CFLAGS += -DCONFIG_LIBNL20
+else
+  ifdef CONFIG_LIBNL_TINY
+    DRV_LIBS += -lnl-tiny
+  else
+    DRV_LIBS += -lnl
+  endif
+
+  ifdef CONFIG_LIBNL20
+    DRV_LIBS += -lnl-genl
+    DRV_LIBS += -lnl-route
+    DRV_CFLAGS += -DCONFIG_LIBNL20
+  endif
+endif
+endif
+endif
 
 ##### COMMON VARS
 DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 1d7129c..23fcbb7 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -30,7 +30,7 @@
 ifdef CONFIG_LIBNL32
   DRV_LIBS += -lnl-3
   DRV_LIBS += -lnl-genl-3
-  DRV_CFLAGS += -DCONFIG_LIBNL20
+  DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3
 else
   ifdef CONFIG_LIBNL_TINY
     DRV_LIBS += -lnl-tiny
@@ -146,6 +146,29 @@
 DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM
 endif
 
+ifdef CONFIG_VLAN_NETLINK
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+ifdef CONFIG_LIBNL32
+  DRV_LIBS += -lnl-3
+  DRV_LIBS += -lnl-genl-3
+  DRV_LIBS += -lnl-route-3
+  DRV_CFLAGS += -DCONFIG_LIBNL20
+else
+  ifdef CONFIG_LIBNL_TINY
+    DRV_LIBS += -lnl-tiny
+  else
+    DRV_LIBS += -lnl
+  endif
+
+  ifdef CONFIG_LIBNL20
+    DRV_LIBS += -lnl-genl
+    DRV_LIBS += -lnl-route
+    DRV_CFLAGS += -DCONFIG_LIBNL20
+  endif
+endif
+endif
+endif
+
 ##### COMMON VARS
 DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
 DRV_WPA_CFLAGS += $(DRV_CFLAGS)
diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
index 940b69a..4380428 100644
--- a/src/drivers/linux_ioctl.c
+++ b/src/drivers/linux_ioctl.c
@@ -45,8 +45,9 @@
 
 	if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
 		ret = errno ? -errno : -999;
-		wpa_printf(MSG_ERROR, "Could not set interface %s flags: %s",
-			   ifname, strerror(errno));
+		wpa_printf(MSG_ERROR, "Could not set interface %s flags (%s): "
+			   "%s",
+			   ifname, dev_up ? "UP" : "DOWN", strerror(errno));
 		return ret;
 	}
 
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 4f98fae..2f38788 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -156,21 +156,25 @@
  * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
  *	or %NL80211_ATTR_MAC.
  *
- * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a
- *	%NL80222_CMD_NEW_BEACON message)
- * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface
- *	using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD,
- *	%NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes.
- *	Following attributes are provided for drivers that generate full Beacon
- *	and Probe Response frames internally: %NL80211_ATTR_SSID,
+ * @NL80211_CMD_GET_BEACON: (not used)
+ * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
+ *	using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL
+ *	attributes. For drivers that generate the beacon and probe responses
+ *	internally, the following attributes must be provided: %NL80211_ATTR_IE,
+ *	%NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP.
+ * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters
+ *	are like for %NL80211_CMD_SET_BEACON, and additionally parameters that
+ *	do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL,
+ *	%NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID,
  *	%NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
  *	%NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
  *	%NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
- *	%NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_IE, %NL80211_ATTR_IE_PROBE_RESP,
- *	%NL80211_ATTR_IE_ASSOC_RESP.
- * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface,
- *	parameters are like for %NL80211_CMD_SET_BEACON.
- * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it
+ *	%NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT.
+ *	The channel to use can be set on the interface or be given using the
+ *	%NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_CHANNEL_TYPE attrs.
+ * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
+ * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface
+ * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP
  *
  * @NL80211_CMD_GET_STATION: Get station attributes for station identified by
  *	%NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
@@ -273,6 +277,12 @@
  * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
  *	NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
  *
+ * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC
+ *	(for the BSSID) and %NL80211_ATTR_PMKID.
+ * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
+ *	(for the BSSID) and %NL80211_ATTR_PMKID.
+ * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
+ *
  * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
  * 	has been changed and provides details of the request information
  * 	that caused the change such as who initiated the regulatory request
@@ -367,6 +377,11 @@
  *	%NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
  *	%NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
  *	%NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ *	Background scan period can optionally be
+ *	specified in %NL80211_ATTR_BG_SCAN_PERIOD,
+ *	if not specified default background scan configuration
+ *	in driver is used and if period value is 0, bg scan will be disabled.
+ *	This attribute is ignored if driver does not support roam scan.
  *	It is also sent as an event, with the BSSID and response IEs when the
  *	connection is established or failed to be established. This can be
  *	determined by the STATUS_CODE attribute.
@@ -447,6 +462,10 @@
  *	the frame.
  * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
  *	backward compatibility.
+ *
+ * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE
+ * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE
+ *
  * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
  *	is used to configure connection quality monitoring notification trigger
  *	levels.
@@ -541,6 +560,11 @@
  * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether
  *      No Acknowledgement Policy should be applied.
  *
+ * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
+ *	independently of the userspace SME, send this event indicating
+ *	%NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with
+ *	%NL80211_ATTR_WIPHY_CHANNEL_TYPE.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -565,8 +589,10 @@
 
 	NL80211_CMD_GET_BEACON,
 	NL80211_CMD_SET_BEACON,
-	NL80211_CMD_NEW_BEACON,
-	NL80211_CMD_DEL_BEACON,
+	NL80211_CMD_START_AP,
+	NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP,
+	NL80211_CMD_STOP_AP,
+	NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP,
 
 	NL80211_CMD_GET_STATION,
 	NL80211_CMD_SET_STATION,
@@ -680,6 +706,8 @@
 
 	NL80211_CMD_SET_NOACK_MAP,
 
+	NL80211_CMD_CH_SWITCH_NOTIFY,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -743,6 +771,9 @@
  * @NL80211_ATTR_IFNAME: network interface name
  * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype
  *
+ * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices
+ *	that don't have a netdev (u64)
+ *
  * @NL80211_ATTR_MAC: MAC address (various uses)
  *
  * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of
@@ -753,6 +784,13 @@
  *	section 7.3.2.25.1, e.g. 0x000FAC04)
  * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
  *	CCMP keys, each six bytes in little endian
+ * @NL80211_ATTR_KEY_DEFAULT: Flag attribute indicating the key is default key
+ * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the
+ *	default management key
+ * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or
+ *	other commands, indicates which pairwise cipher suites are used
+ * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or
+ *	other commands, indicates which group cipher suite is used
  *
  * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU
  * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing
@@ -988,6 +1026,8 @@
  * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
  *	acknowledged by the recipient.
  *
+ * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values.
+ *
  * @NL80211_ATTR_CQM: connection quality monitor configuration in a
  *	nested attribute with %NL80211_ATTR_CQM_* sub-attributes.
  *
@@ -1045,7 +1085,7 @@
  *	flag isn't set, the frame will be rejected. This is also used as an
  *	nl80211 capability flag.
  *
- * @NL80211_ATTR_BSS_HTOPMODE: HT operation mode (u16)
+ * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16)
  *
  * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags
  *	attributes, specifying what a key should be set as default as.
@@ -1069,10 +1109,10 @@
  *	indicate which WoW triggers should be enabled. This is also
  *	used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN
  *	triggers.
-
+ *
  * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan
  *	cycles, in msecs.
-
+ *
  * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more
  *	sets of attributes to match during scheduled scans.  Only BSSs
  *	that match any of the sets will be reported.  These are
@@ -1099,7 +1139,7 @@
  *	are managed in software: interfaces of these types aren't subject to
  *	any restrictions in their number or combinations.
  *
- * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information
+ * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information
  *	necessary for GTK rekeying in the device, see &enum nl80211_rekey_data.
  *
  * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan,
@@ -1166,7 +1206,6 @@
  * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from
  *	&enum nl80211_feature_flags and is advertised in wiphy information.
  * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe
- *
  *	requests while operating in AP-mode.
  *	This attribute holds a bitmap of the supported protocols for
  *	offloading (see &enum nl80211_probe_resp_offload_support_attr).
@@ -1193,6 +1232,25 @@
  * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of
  *      up to 16 TIDs.
  *
+ * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be
+ *	used by the drivers which has MLME in firmware and does not have support
+ *	to report per station tx/rx activity to free up the staion entry from
+ *	the list. This needs to be used when the driver advertises the
+ *	capability to timeout the stations.
+ *
+ * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int);
+ *	this attribute is (depending on the driver capabilities) added to
+ *	received frames indicated with %NL80211_CMD_FRAME.
+ *
+ * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
+ *      or 0 to disable background scan.
+ *
+ * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from
+ *	userspace. If unset it is assumed the hint comes directly from
+ *	a user. If set code could specify exactly what type of source
+ *	was used to provide the hint. For the different types of
+ *	allowed user regulatory hints see nl80211_user_reg_hint_type.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1438,6 +1496,16 @@
 
 	NL80211_ATTR_NOACK_MAP,
 
+	NL80211_ATTR_INACTIVITY_TIMEOUT,
+
+	NL80211_ATTR_RX_SIGNAL_DBM,
+
+	NL80211_ATTR_BG_SCAN_PERIOD,
+
+	NL80211_ATTR_WDEV,
+
+	NL80211_ATTR_USER_REG_HINT_TYPE,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -1475,6 +1543,7 @@
 #define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
 
 #define NL80211_MAX_SUPP_RATES			32
+#define NL80211_MAX_SUPP_HT_RATES		77
 #define NL80211_MAX_SUPP_REG_RULES		32
 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY	0
 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY	16
@@ -1484,6 +1553,13 @@
 #define NL80211_MAX_NR_CIPHER_SUITES		5
 #define NL80211_MAX_NR_AKM_SUITES		2
 
+#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME	10
+
+/* default RSSI threshold for scan results if none specified. */
+#define NL80211_SCAN_RSSI_THOLD_OFF		-300
+
+#define NL80211_CQM_TXE_MAX_INTVL		1800
+
 /**
  * enum nl80211_iftype - (virtual) interface types
  *
@@ -1558,6 +1634,8 @@
 	NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
 };
 
+#define NL80211_STA_FLAG_MAX_OLD_API	NL80211_STA_FLAG_TDLS_PEER
+
 /**
  * struct nl80211_sta_flag_update - station flags mask/set
  * @mask: mask of station flags to set
@@ -1575,12 +1653,20 @@
  *
  * These attribute types are used with %NL80211_STA_INFO_TXRATE
  * when getting information about the bitrate of a station.
+ * There are 2 attributes for bitrate, a legacy one that represents
+ * a 16-bit value, and new one that represents a 32-bit value.
+ * If the rate value fits into 16 bit, both attributes are reported
+ * with the same value. If the rate is too high to fit into 16 bits
+ * (>6.5535Gbps) only 32-bit attribute is included.
+ * User space tools encouraged to use the 32-bit attribute and fall
+ * back to the 16-bit one for compatibility with older kernels.
  *
  * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
  * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
  * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
  * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate
  * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
+ * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s)
  * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
  * @__NL80211_RATE_INFO_AFTER_LAST: internal use
  */
@@ -1590,6 +1676,7 @@
 	NL80211_RATE_INFO_MCS,
 	NL80211_RATE_INFO_40_MHZ_WIDTH,
 	NL80211_RATE_INFO_SHORT_GI,
+	NL80211_RATE_INFO_BITRATE32,
 
 	/* keep last */
 	__NL80211_RATE_INFO_AFTER_LAST,
@@ -1656,6 +1743,7 @@
  * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected
  * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update.
  * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32)
+ * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64)
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -1679,6 +1767,7 @@
 	NL80211_STA_INFO_CONNECTED_TIME,
 	NL80211_STA_INFO_STA_FLAGS,
 	NL80211_STA_INFO_BEACON_LOSS,
+	NL80211_STA_INFO_T_OFFSET,
 
 	/* keep last */
 	__NL80211_STA_INFO_AFTER_LAST,
@@ -1748,6 +1837,9 @@
  * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE
  * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n
  * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n
+ * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as
+ *	defined in 802.11ac
+ * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE
  * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
  * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
  */
@@ -1761,6 +1853,9 @@
 	NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
 	NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
 
+	NL80211_BAND_ATTR_VHT_MCS_SET,
+	NL80211_BAND_ATTR_VHT_CAPA,
+
 	/* keep last */
 	__NL80211_BAND_ATTR_AFTER_LAST,
 	NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
@@ -1912,6 +2007,8 @@
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
  * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
  * only report BSS with matching SSID.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
+ *	BSS in scan results. Filtering is turned off if not specified.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
  *	attribute number currently defined
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -1919,7 +2016,8 @@
 enum nl80211_sched_scan_match_attr {
 	__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID,
 
-	NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+	NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+	NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
 
 	/* keep last */
 	__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
@@ -1927,6 +2025,9 @@
 		__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1
 };
 
+/* only for backward compatibility */
+#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID
+
 /**
  * enum nl80211_reg_rule_flags - regulatory rule flags
  *
@@ -1956,9 +2057,9 @@
  * enum nl80211_dfs_regions - regulatory DFS regions
  *
  * @NL80211_DFS_UNSET: Country has no DFS master region specified
- * @NL80211_DFS_FCC_: Country follows DFS master rules from FCC
- * @NL80211_DFS_FCC_: Country follows DFS master rules from ETSI
- * @NL80211_DFS_JP_: Country follows DFS master rules from JP/MKK/Telec
+ * @NL80211_DFS_FCC: Country follows DFS master rules from FCC
+ * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI
+ * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec
  */
 enum nl80211_dfs_regions {
 	NL80211_DFS_UNSET	= 0,
@@ -1968,6 +2069,26 @@
 };
 
 /**
+ * enum nl80211_user_reg_hint_type - type of user regulatory hint
+ *
+ * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always
+ *	assumed if the attribute is not set.
+ * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular
+ *	base station. Device drivers that have been tested to work
+ *	properly to support this type of hint can enable these hints
+ *	by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature
+ *	capability on the struct wiphy. The wireless core will
+ *	ignore all cell base station hints until at least one device
+ *	present has been registered with the wireless core that
+ *	has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
+ *	supported feature.
+ */
+enum nl80211_user_reg_hint_type {
+	NL80211_USER_REG_HINT_USER	= 0,
+	NL80211_USER_REG_HINT_CELL_BASE = 1,
+};
+
+/**
  * enum nl80211_survey_info - survey information
  *
  * These attribute types are used with %NL80211_ATTR_SURVEY_INFO
@@ -2046,69 +2167,91 @@
  * @__NL80211_MESHCONF_INVALID: internal use
  *
  * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in
- * millisecond units, used by the Peer Link Open message
+ *	millisecond units, used by the Peer Link Open message
  *
  * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in
- * millisecond units, used by the peer link management to close a peer link
+ *	millisecond units, used by the peer link management to close a peer link
  *
  * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
- * millisecond units
+ *	millisecond units
  *
  * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed
- * on this mesh interface
+ *	on this mesh interface
  *
  * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link
- * open retries that can be sent to establish a new peer link instance in a
- * mesh
+ *	open retries that can be sent to establish a new peer link instance in a
+ *	mesh
  *
  * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
- * point.
+ *	point.
  *
  * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically
- * open peer links when we detect compatible mesh peers.
+ *	open peer links when we detect compatible mesh peers.
  *
  * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
- * containing a PREQ that an MP can send to a particular destination (path
- * target)
+ *	containing a PREQ that an MP can send to a particular destination (path
+ *	target)
  *
  * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths
- * (in milliseconds)
+ *	(in milliseconds)
  *
  * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait
- * until giving up on a path discovery (in milliseconds)
+ *	until giving up on a path discovery (in milliseconds)
  *
  * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh
- * points receiving a PREQ shall consider the forwarding information from the
- * root to be valid. (TU = time unit)
+ *	points receiving a PREQ shall consider the forwarding information from
+ *	the root to be valid. (TU = time unit)
  *
  * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in
- * TUs) during which an MP can send only one action frame containing a PREQ
- * reference element
+ *	TUs) during which an MP can send only one action frame containing a PREQ
+ *	reference element
  *
  * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
- * that it takes for an HWMP information element to propagate across the mesh
+ *	that it takes for an HWMP information element to propagate across the
+ *	mesh
  *
  * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not
  *
  * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
- * source mesh point for path selection elements.
+ *	source mesh point for path selection elements.
  *
  * @NL80211_MESHCONF_HWMP_RANN_INTERVAL:  The interval of time (in TUs) between
- * root announcements are transmitted.
+ *	root announcements are transmitted.
  *
  * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has
- * access to a broader network beyond the MBSS.  This is done via Root
- * Announcement frames.
+ *	access to a broader network beyond the MBSS.  This is done via Root
+ *	Announcement frames.
  *
  * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in
- * TUs) during which a mesh STA can send only one Action frame containing a
- * PERR element.
+ *	TUs) during which a mesh STA can send only one Action frame containing a
+ *	PERR element.
  *
  * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding
- * or forwarding entity (default is TRUE - forwarding entity)
+ *	or forwarding entity (default is TRUE - forwarding entity)
+ *
+ * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the
+ *	threshold for average signal strength of candidate station to establish
+ *	a peer link.
+ *
+ * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors
+ *	to synchronize to for 11s default synchronization method
+ *	(see 11C.12.2.2)
+ *
+ * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode.
  *
  * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
  *
+ * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for
+ *	which mesh STAs receiving a proactive PREQ shall consider the forwarding
+ *	information to the root mesh STA to be valid.
+ *
+ * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between
+ *	proactive PREQs are transmitted.
+ *
+ * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time
+ *	(in TUs) during which a mesh STA can send only one Action frame
+ *	containing a PREQ element for root path confirmation.
+ *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_meshconf_params {
@@ -2132,6 +2275,12 @@
 	NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
 	NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
 	NL80211_MESHCONF_FORWARDING,
+	NL80211_MESHCONF_RSSI_THRESHOLD,
+	NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+	NL80211_MESHCONF_HT_OPMODE,
+	NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
+	NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
+	NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
 
 	/* keep last */
 	__NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -2147,30 +2296,37 @@
  * @__NL80211_MESH_SETUP_INVALID: Internal use
  *
  * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a
- * vendor specific path selection algorithm or disable it to use the default
- * HWMP.
+ *	vendor specific path selection algorithm or disable it to use the
+ *	default HWMP.
  *
  * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a
- * vendor specific path metric or disable it to use the default Airtime
- * metric.
+ *	vendor specific path metric or disable it to use the default Airtime
+ *	metric.
  *
  * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a
- * robust security network ie, or a vendor specific information element that
- * vendors will use to identify the path selection methods and metrics in use.
+ *	robust security network ie, or a vendor specific information element
+ *	that vendors will use to identify the path selection methods and
+ *	metrics in use.
  *
  * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication
- * daemon will be authenticating mesh candidates.
+ *	daemon will be authenticating mesh candidates.
  *
  * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication
- * daemon will be securing peer link frames.  AMPE is a secured version of Mesh
- * Peering Management (MPM) and is implemented with the assistance of a
- * userspace daemon.  When this flag is set, the kernel will send peer
- * management frames to a userspace daemon that will implement AMPE
- * functionality (security capabilities selection, key confirmation, and key
- * management).  When the flag is unset (default), the kernel can autonomously
- * complete (unsecured) mesh peering without the need of a userspace daemon.
+ *	daemon will be securing peer link frames.  AMPE is a secured version of
+ *	Mesh Peering Management (MPM) and is implemented with the assistance of
+ *	a userspace daemon.  When this flag is set, the kernel will send peer
+ *	management frames to a userspace daemon that will implement AMPE
+ *	functionality (security capabilities selection, key confirmation, and
+ *	key management).  When the flag is unset (default), the kernel can
+ *	autonomously complete (unsecured) mesh peering without the need of a
+ *	userspace daemon.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a
+ *	vendor specific synchronization method or disable it to use the default
+ *	neighbor offset synchronization
  *
  * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
+ *
  * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
  */
 enum nl80211_mesh_setup_params {
@@ -2180,6 +2336,7 @@
 	NL80211_MESH_SETUP_IE,
 	NL80211_MESH_SETUP_USERSPACE_AUTH,
 	NL80211_MESH_SETUP_USERSPACE_AMPE,
+	NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
 
 	/* keep last */
 	__NL80211_MESH_SETUP_ATTR_AFTER_LAST,
@@ -2189,7 +2346,7 @@
 /**
  * enum nl80211_txq_attr - TX queue parameter attributes
  * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
- * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*)
+ * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*)
  * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning
  *	disabled
  * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form
@@ -2202,7 +2359,7 @@
  */
 enum nl80211_txq_attr {
 	__NL80211_TXQ_ATTR_INVALID,
-	NL80211_TXQ_ATTR_QUEUE,
+	NL80211_TXQ_ATTR_AC,
 	NL80211_TXQ_ATTR_TXOP,
 	NL80211_TXQ_ATTR_CWMIN,
 	NL80211_TXQ_ATTR_CWMAX,
@@ -2213,13 +2370,21 @@
 	NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1
 };
 
-enum nl80211_txq_q {
-	NL80211_TXQ_Q_VO,
-	NL80211_TXQ_Q_VI,
-	NL80211_TXQ_Q_BE,
-	NL80211_TXQ_Q_BK
+enum nl80211_ac {
+	NL80211_AC_VO,
+	NL80211_AC_VI,
+	NL80211_AC_BE,
+	NL80211_AC_BK,
+	NL80211_NUM_ACS
 };
 
+/* backward compat */
+#define NL80211_TXQ_ATTR_QUEUE	NL80211_TXQ_ATTR_AC
+#define NL80211_TXQ_Q_VO	NL80211_AC_VO
+#define NL80211_TXQ_Q_VI	NL80211_AC_VI
+#define NL80211_TXQ_Q_BE	NL80211_AC_BE
+#define NL80211_TXQ_Q_BK	NL80211_AC_BK
+
 enum nl80211_channel_type {
 	NL80211_CHAN_NO_HT,
 	NL80211_CHAN_HT20,
@@ -2405,12 +2570,15 @@
  *	in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with
  *	1 = 500 kbps) but without the IE length restriction (at most
  *	%NL80211_MAX_SUPP_RATES in a single array).
+ * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection
+ *	in an array of MCS numbers.
  * @__NL80211_TXRATE_AFTER_LAST: internal
  * @NL80211_TXRATE_MAX: highest TX rate attribute
  */
 enum nl80211_tx_rate_attributes {
 	__NL80211_TXRATE_INVALID,
 	NL80211_TXRATE_LEGACY,
+	NL80211_TXRATE_MCS,
 
 	/* keep last */
 	__NL80211_TXRATE_AFTER_LAST,
@@ -2421,12 +2589,19 @@
  * enum nl80211_band - Frequency band
  * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
  * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
+ * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz)
  */
 enum nl80211_band {
 	NL80211_BAND_2GHZ,
 	NL80211_BAND_5GHZ,
+	NL80211_BAND_60GHZ,
 };
 
+/**
+ * enum nl80211_ps_state - powersave state
+ * @NL80211_PS_DISABLED: powersave is disabled
+ * @NL80211_PS_ENABLED: powersave is enabled
+ */
 enum nl80211_ps_state {
 	NL80211_PS_DISABLED,
 	NL80211_PS_ENABLED,
@@ -2444,6 +2619,17 @@
  * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
  * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many
  *	consecutive packets were not acknowledged by the peer
+ * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures
+ *	during the given %NL80211_ATTR_CQM_TXE_INTVL before an
+ *	%NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and
+ *	%NL80211_ATTR_CQM_TXE_PKTS is generated.
+ * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given
+ *	%NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is
+ *	checked.
+ * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic
+ *	interval in which %NL80211_ATTR_CQM_TXE_PKTS and
+ *	%NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an
+ *	%NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
  * @__NL80211_ATTR_CQM_AFTER_LAST: internal
  * @NL80211_ATTR_CQM_MAX: highest key attribute
  */
@@ -2453,6 +2639,9 @@
 	NL80211_ATTR_CQM_RSSI_HYST,
 	NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
 	NL80211_ATTR_CQM_PKT_LOSS_EVENT,
+	NL80211_ATTR_CQM_TXE_RATE,
+	NL80211_ATTR_CQM_TXE_PKTS,
+	NL80211_ATTR_CQM_TXE_INTVL,
 
 	/* keep last */
 	__NL80211_ATTR_CQM_AFTER_LAST,
@@ -2465,10 +2654,14 @@
  *      configured threshold
  * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
  *      configured threshold
+ * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss.
+ *	(Note that deauth/disassoc will still follow if the AP is not
+ *	available. This event might get used as roaming event, etc.)
  */
 enum nl80211_cqm_rssi_threshold_event {
 	NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
 	NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+	NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
 };
 
 
@@ -2796,10 +2989,17 @@
  *	TX status to the socket error queue when requested with the
  *	socket option.
  * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates.
+ * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
+ *	the connected inactive stations in AP mode.
+ * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
+ *	to work properly to suppport receiving regulatory hints from
+ *	cellular base stations.
  */
 enum nl80211_feature_flags {
 	NL80211_FEATURE_SK_TX_STATUS	= 1 << 0,
 	NL80211_FEATURE_HT_IBSS		= 1 << 1,
+	NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2,
+	NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3,
 };
 
 /**
diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c
index 0d6ef93..7b077cb 100644
--- a/src/eap_common/eap_common.c
+++ b/src/eap_common/eap_common.c
@@ -1,6 +1,6 @@
 /*
  * EAP common peer/server definitions
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -13,6 +13,41 @@
 #include "eap_common.h"
 
 /**
+ * eap_hdr_len_valid - Validate EAP header length field
+ * @msg: EAP frame (starting with EAP header)
+ * @min_payload: Minimum payload length needed
+ * Returns: 1 for valid header, 0 for invalid
+ *
+ * This is a helper function that does minimal validation of EAP messages. The
+ * length field is verified to be large enough to include the header and not
+ * too large to go beyond the end of the buffer.
+ */
+int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
+{
+	const struct eap_hdr *hdr;
+	size_t len;
+
+	if (msg == NULL)
+		return 0;
+
+	hdr = wpabuf_head(msg);
+
+	if (wpabuf_len(msg) < sizeof(*hdr)) {
+		wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
+		return 0;
+	}
+
+	len = be_to_host16(hdr->length);
+	if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
+		wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
+		return 0;
+	}
+
+	return 1;
+}
+
+
+/**
  * eap_hdr_validate - Validate EAP header
  * @vendor: Expected EAP Vendor-Id (0 = IETF)
  * @eap_type: Expected EAP type number
@@ -35,19 +70,11 @@
 	const u8 *pos;
 	size_t len;
 
+	if (!eap_hdr_len_valid(msg, 1))
+		return NULL;
+
 	hdr = wpabuf_head(msg);
-
-	if (wpabuf_len(msg) < sizeof(*hdr)) {
-		wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
-		return NULL;
-	}
-
 	len = be_to_host16(hdr->length);
-	if (len < sizeof(*hdr) + 1 || len > wpabuf_len(msg)) {
-		wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
-		return NULL;
-	}
-
 	pos = (const u8 *) (hdr + 1);
 
 	if (*pos == EAP_TYPE_EXPANDED) {
diff --git a/src/eap_common/eap_common.h b/src/eap_common/eap_common.h
index 73f2797..8850c1f 100644
--- a/src/eap_common/eap_common.h
+++ b/src/eap_common/eap_common.h
@@ -1,6 +1,6 @@
 /*
  * EAP common peer/server definitions
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,7 @@
 
 #include "wpabuf.h"
 
+int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload);
 const u8 * eap_hdr_validate(int vendor, EapType eap_type,
 			    const struct wpabuf *msg, size_t *plen);
 struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h
index af0a29a..0d247c4 100644
--- a/src/eap_common/eap_defs.h
+++ b/src/eap_common/eap_defs.h
@@ -60,7 +60,7 @@
 	EAP_TYPE_PSK = 47 /* RFC 4764 */,
 	EAP_TYPE_SAKE = 48 /* RFC 4763 */,
 	EAP_TYPE_IKEV2 = 49 /* RFC 5106 */,
-	EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */,
+	EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */,
 	EAP_TYPE_GPSK = 51 /* RFC 5433 */,
 	EAP_TYPE_PWD = 52 /* RFC 5931 */,
 	EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
@@ -71,9 +71,13 @@
 enum {
 	EAP_VENDOR_IETF = 0,
 	EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */,
-	EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */
+	EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */,
+	EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */
 };
 
+#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP
+#define EAP_VENDOR_TYPE_UNAUTH_TLS 1
+
 #define EAP_MSK_LEN 64
 #define EAP_EMSK_LEN 64
 
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 5b6bf3c..7d6e6b8 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -8,70 +8,76 @@
 
 #include "includes.h"
 #include "common.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
 #include "eap_defs.h"
 #include "eap_pwd_common.h"
 
 /* The random function H(x) = HMAC-SHA256(0^32, x) */
-void H_Init(HMAC_CTX *ctx)
+struct crypto_hash * eap_pwd_h_init(void)
 {
-	u8 allzero[SHA256_DIGEST_LENGTH];
-
-	os_memset(allzero, 0, SHA256_DIGEST_LENGTH);
-	HMAC_Init(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256());
+	u8 allzero[SHA256_MAC_LEN];
+	os_memset(allzero, 0, SHA256_MAC_LEN);
+	return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero,
+				SHA256_MAC_LEN);
 }
 
 
-void H_Update(HMAC_CTX *ctx, const u8 *data, int len)
+void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len)
 {
-	HMAC_Update(ctx, data, len);
+	crypto_hash_update(hash, data, len);
 }
 
 
-void H_Final(HMAC_CTX *ctx, u8 *digest)
+void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest)
 {
-	unsigned int mdlen = SHA256_DIGEST_LENGTH;
-
-	HMAC_Final(ctx, digest, &mdlen);
-	HMAC_CTX_cleanup(ctx);
+	size_t len = SHA256_MAC_LEN;
+	crypto_hash_finish(hash, digest, &len);
 }
 
 
 /* a counter-based KDF based on NIST SP800-108 */
-void eap_pwd_kdf(u8 *key, int keylen, u8 *label, int labellen,
-		 u8 *result, int resultbitlen)
+static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label,
+		       size_t labellen, u8 *result, size_t resultbitlen)
 {
-	HMAC_CTX hctx;
-	unsigned char digest[SHA256_DIGEST_LENGTH];
+	struct crypto_hash *hash;
+	u8 digest[SHA256_MAC_LEN];
 	u16 i, ctr, L;
-	int resultbytelen, len = 0;
-	unsigned int mdlen = SHA256_DIGEST_LENGTH;
-	unsigned char mask = 0xff;
+	size_t resultbytelen, len = 0, mdlen;
 
-	resultbytelen = (resultbitlen + 7)/8;
+	resultbytelen = (resultbitlen + 7) / 8;
 	ctr = 0;
 	L = htons(resultbitlen);
 	while (len < resultbytelen) {
-		ctr++; i = htons(ctr);
-		HMAC_Init(&hctx, key, keylen, EVP_sha256());
+		ctr++;
+		i = htons(ctr);
+		hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256,
+					key, keylen);
+		if (hash == NULL)
+			return -1;
 		if (ctr > 1)
-			HMAC_Update(&hctx, digest, mdlen);
-		HMAC_Update(&hctx, (u8 *) &i, sizeof(u16));
-		HMAC_Update(&hctx, label, labellen);
-		HMAC_Update(&hctx, (u8 *) &L, sizeof(u16));
-		HMAC_Final(&hctx, digest, &mdlen);
-		if ((len + (int) mdlen) > resultbytelen)
+			crypto_hash_update(hash, digest, SHA256_MAC_LEN);
+		crypto_hash_update(hash, (u8 *) &i, sizeof(u16));
+		crypto_hash_update(hash, label, labellen);
+		crypto_hash_update(hash, (u8 *) &L, sizeof(u16));
+		mdlen = SHA256_MAC_LEN;
+		if (crypto_hash_finish(hash, digest, &mdlen) < 0)
+			return -1;
+		if ((len + mdlen) > resultbytelen)
 			os_memcpy(result + len, digest, resultbytelen - len);
 		else
 			os_memcpy(result + len, digest, mdlen);
 		len += mdlen;
-		HMAC_CTX_cleanup(&hctx);
 	}
 
 	/* since we're expanding to a bit length, mask off the excess */
 	if (resultbitlen % 8) {
+		u8 mask = 0xff;
 		mask <<= (8 - (resultbitlen % 8));
 		result[resultbytelen - 1] &= mask;
 	}
+
+	return 0;
 }
 
 
@@ -85,9 +91,10 @@
 			     u8 *id_peer, int id_peer_len, u8 *token)
 {
 	BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
-	HMAC_CTX ctx;
-	unsigned char pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr;
-	int nid, is_odd, primebitlen, primebytelen, ret = 0;
+	struct crypto_hash *hash;
+	unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
+	int nid, is_odd, ret = 0;
+	size_t primebytelen, primebitlen;
 
 	switch (num) { /* from IANA registry for IKE D-H groups */
         case 19:
@@ -154,7 +161,7 @@
 	os_memset(prfbuf, 0, primebytelen);
 	ctr = 0;
 	while (1) {
-		if (ctr > 10) {
+		if (ctr > 30) {
 			wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
 				   "point on curve for group %d, something's "
 				   "fishy", num);
@@ -167,20 +174,23 @@
 		 *    pwd-seed = H(token | peer-id | server-id | password |
 		 *		   counter)
 		 */
-		H_Init(&ctx);
-		H_Update(&ctx, token, sizeof(u32));
-		H_Update(&ctx, id_peer, id_peer_len);
-		H_Update(&ctx, id_server, id_server_len);
-		H_Update(&ctx, password, password_len);
-		H_Update(&ctx, &ctr, sizeof(ctr));
-		H_Final(&ctx, pwe_digest);
+		hash = eap_pwd_h_init();
+		if (hash == NULL)
+			goto fail;
+		eap_pwd_h_update(hash, token, sizeof(u32));
+		eap_pwd_h_update(hash, id_peer, id_peer_len);
+		eap_pwd_h_update(hash, id_server, id_server_len);
+		eap_pwd_h_update(hash, password, password_len);
+		eap_pwd_h_update(hash, &ctr, sizeof(ctr));
+		eap_pwd_h_final(hash, pwe_digest);
 
-		BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd);
+		BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd);
 
-		eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH,
-			    (unsigned char *) "EAP-pwd Hunting And Pecking",
-			    os_strlen("EAP-pwd Hunting And Pecking"),
-			    prfbuf, primebitlen);
+		if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
+				(u8 *) "EAP-pwd Hunting And Pecking",
+				os_strlen("EAP-pwd Hunting And Pecking"),
+				prfbuf, primebitlen) < 0)
+			goto fail;
 
 		BN_bin2bn(prfbuf, primebytelen, x_candidate);
 
@@ -252,11 +262,13 @@
 	if (0) {
  fail:
 		EC_GROUP_free(grp->group);
+		grp->group = NULL;
 		EC_POINT_free(grp->pwe);
+		grp->pwe = NULL;
 		BN_free(grp->order);
+		grp->order = NULL;
 		BN_free(grp->prime);
-		os_free(grp);
-		grp = NULL;
+		grp->prime = NULL;
 		ret = 1;
 	}
 	/* cleanliness and order.... */
@@ -274,9 +286,9 @@
 		 u8 *confirm_peer, u8 *confirm_server,
 		 u32 *ciphersuite, u8 *msk, u8 *emsk)
 {
-	HMAC_CTX ctx;
-	u8 mk[SHA256_DIGEST_LENGTH], *cruft;
-	u8 session_id[SHA256_DIGEST_LENGTH + 1];
+	struct crypto_hash *hash;
+	u8 mk[SHA256_MAC_LEN], *cruft;
+	u8 session_id[SHA256_MAC_LEN + 1];
 	u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
 	int offset;
 
@@ -288,37 +300,46 @@
 	 *	scal_s)
 	 */
 	session_id[0] = EAP_TYPE_PWD;
-	H_Init(&ctx);
-	H_Update(&ctx, (u8 *)ciphersuite, sizeof(u32));
+	hash = eap_pwd_h_init();
+	if (hash == NULL) {
+		os_free(cruft);
+		return -1;
+	}
+	eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32));
 	offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar);
 	os_memset(cruft, 0, BN_num_bytes(grp->prime));
 	BN_bn2bin(peer_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
 	offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar);
 	os_memset(cruft, 0, BN_num_bytes(grp->prime));
 	BN_bn2bin(server_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(grp->order));
-	H_Final(&ctx, &session_id[1]);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
+	eap_pwd_h_final(hash, &session_id[1]);
 
 	/* then compute MK = H(k | confirm-peer | confirm-server) */
-	H_Init(&ctx);
+	hash = eap_pwd_h_init();
+	if (hash == NULL) {
+		os_free(cruft);
+		return -1;
+	}
 	offset = BN_num_bytes(grp->prime) - BN_num_bytes(k);
 	os_memset(cruft, 0, BN_num_bytes(grp->prime));
 	BN_bn2bin(k, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(grp->prime));
-	H_Update(&ctx, confirm_peer, SHA256_DIGEST_LENGTH);
-	H_Update(&ctx, confirm_server, SHA256_DIGEST_LENGTH);
-	H_Final(&ctx, mk);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime));
+	os_free(cruft);
+	eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
+	eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN);
+	eap_pwd_h_final(hash, mk);
 
 	/* stretch the mk with the session-id to get MSK | EMSK */
-	eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH,
-		    session_id, SHA256_DIGEST_LENGTH+1,
-		    msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8);
+	if (eap_pwd_kdf(mk, SHA256_MAC_LEN,
+			session_id, SHA256_MAC_LEN + 1,
+			msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) {
+		return -1;
+	}
 
 	os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
 	os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
 
-	os_free(cruft);
-
 	return 1;
 }
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
index 358d008..816e58c 100644
--- a/src/eap_common/eap_pwd_common.h
+++ b/src/eap_common/eap_pwd_common.h
@@ -10,10 +10,8 @@
 #define EAP_PWD_COMMON_H
 
 #include <openssl/bn.h>
-#include <openssl/sha.h>
 #include <openssl/ec.h>
 #include <openssl/evp.h>
-#include <openssl/hmac.h>
 
 /*
  * definition of a finite cyclic group
@@ -62,8 +60,8 @@
 			     int, u8 *);
 int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *,
 		 u8 *, u8 *, u32 *, u8 *, u8 *);
-void H_Init(HMAC_CTX *);
-void H_Update(HMAC_CTX *, const u8 *, int);
-void H_Final(HMAC_CTX *, u8 *);
+struct crypto_hash * eap_pwd_h_init(void);
+void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
+void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
 
 #endif  /* EAP_PWD_COMMON_H */
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 6a88589..2ed74b8 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer state machines (RFC 4137)
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -20,6 +20,7 @@
 #include "common.h"
 #include "pcsc_funcs.h"
 #include "state_machine.h"
+#include "ext_password.h"
 #include "crypto/crypto.h"
 #include "crypto/tls.h"
 #include "common/wpa_ctrl.h"
@@ -81,8 +82,21 @@
 }
 
 
+static void eap_notify_status(struct eap_sm *sm, const char *status,
+				      const char *parameter)
+{
+	wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)",
+		   status, parameter);
+	if (sm->eapol_cb->notify_status)
+		sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter);
+}
+
+
 static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
 {
+	ext_password_free(sm->ext_pw_buf);
+	sm->ext_pw_buf = NULL;
+
 	if (sm->m == NULL || sm->eap_method_priv == NULL)
 		return;
 
@@ -175,6 +189,12 @@
 {
 	SM_ENTRY(EAP, DISABLED);
 	sm->num_rounds = 0;
+	/*
+	 * RFC 4137 does not describe clearing of idleWhile here, but doing so
+	 * allows the timer tick to be stopped more quickly when EAP is not in
+	 * use.
+	 */
+	eapol_set_int(sm, EAPOL_idleWhile, 0);
 }
 
 
@@ -213,6 +233,7 @@
 {
 	int reinit;
 	EapType method;
+	const struct eap_method *eap_method;
 
 	SM_ENTRY(EAP, GET_METHOD);
 
@@ -221,18 +242,24 @@
 	else
 		method = sm->reqMethod;
 
+	eap_method = eap_peer_get_eap_method(sm->reqVendor, method);
+
 	if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) {
 		wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed",
 			   sm->reqVendor, method);
 		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
 			"vendor=%u method=%u -> NAK",
 			sm->reqVendor, method);
+		eap_notify_status(sm, "refuse proposed method",
+				  eap_method ?  eap_method->name : "unknown");
 		goto nak;
 	}
 
 	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
 		"vendor=%u method=%u", sm->reqVendor, method);
 
+	eap_notify_status(sm, "accept proposed method",
+			  eap_method ?  eap_method->name : "unknown");
 	/*
 	 * RFC 4137 does not define specific operation for fast
 	 * re-authentication (session resumption). The design here is to allow
@@ -256,7 +283,7 @@
 
 	sm->selectedMethod = sm->reqMethod;
 	if (sm->m == NULL)
-		sm->m = eap_peer_get_eap_method(sm->reqVendor, method);
+		sm->m = eap_method;
 	if (!sm->m) {
 		wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: "
 			   "vendor %d method %d",
@@ -329,6 +356,8 @@
 	}
 
 	eapReqData = eapol_get_eapReqData(sm);
+	if (!eap_hdr_len_valid(eapReqData, 1))
+		return;
 
 	/*
 	 * Get ignore, methodState, decision, allowNotifications, and
@@ -417,6 +446,8 @@
 
 	SM_ENTRY(EAP, IDENTITY);
 	eapReqData = eapol_get_eapReqData(sm);
+	if (!eap_hdr_len_valid(eapReqData, 1))
+		return;
 	eap_sm_processIdentity(sm, eapReqData);
 	wpabuf_free(sm->eapRespData);
 	sm->eapRespData = NULL;
@@ -433,6 +464,8 @@
 
 	SM_ENTRY(EAP, NOTIFICATION);
 	eapReqData = eapol_get_eapReqData(sm);
+	if (!eap_hdr_len_valid(eapReqData, 1))
+		return;
 	eap_sm_processNotify(sm, eapReqData);
 	wpabuf_free(sm->eapRespData);
 	sm->eapRespData = NULL;
@@ -850,13 +883,17 @@
 
 static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req)
 {
-	const struct eap_hdr *hdr = wpabuf_head(req);
-	const u8 *pos = (const u8 *) (hdr + 1);
-	pos++;
+	const u8 *pos;
+	size_t msg_len;
 
 	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
 		"EAP authentication started");
 
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req,
+			       &msg_len);
+	if (pos == NULL)
+		return;
+
 	/*
 	 * RFC 3748 - 5.1: Identity
 	 * Data field may contain a displayable message in UTF-8. If this
@@ -867,7 +904,7 @@
 	/* TODO: could save displayable message so that it can be shown to the
 	 * user in case of interaction is required */
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data",
-			  pos, be_to_host16(hdr->length) - 5);
+			  pos, msg_len);
 }
 
 
@@ -938,7 +975,7 @@
 static int eap_sm_imsi_identity(struct eap_sm *sm,
 				struct eap_peer_config *conf)
 {
-	int aka = 0;
+	enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM;
 	char imsi[100];
 	size_t imsi_len;
 	struct eap_method_type *m = conf->eap_methods;
@@ -966,8 +1003,14 @@
 	for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF ||
 			  m[i].method != EAP_TYPE_NONE); i++) {
 		if (m[i].vendor == EAP_VENDOR_IETF &&
+		    m[i].method == EAP_TYPE_AKA_PRIME) {
+			method = EAP_SM_AKA_PRIME;
+			break;
+		}
+
+		if (m[i].vendor == EAP_VENDOR_IETF &&
 		    m[i].method == EAP_TYPE_AKA) {
-			aka = 1;
+			method = EAP_SM_AKA;
 			break;
 		}
 	}
@@ -980,7 +1023,17 @@
 		return -1;
 	}
 
-	conf->identity[0] = aka ? '0' : '1';
+	switch (method) {
+	case EAP_SM_SIM:
+		conf->identity[0] = '1';
+		break;
+	case EAP_SM_AKA:
+		conf->identity[0] = '0';
+		break;
+	case EAP_SM_AKA_PRIME:
+		conf->identity[0] = '6';
+		break;
+	}
 	os_memcpy(conf->identity + 1, imsi, imsi_len);
 	conf->identity_len = 1 + imsi_len;
 
@@ -1219,10 +1272,12 @@
 		break;
 	case EAP_CODE_SUCCESS:
 		wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success");
+		eap_notify_status(sm, "completion", "success");
 		sm->rxSuccess = TRUE;
 		break;
 	case EAP_CODE_FAILURE:
 		wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
+		eap_notify_status(sm, "completion", "failure");
 		sm->rxFailure = TRUE;
 		break;
 	default:
@@ -1240,6 +1295,10 @@
 	char *hash_hex = NULL;
 
 	switch (ev) {
+	case TLS_CERT_CHAIN_SUCCESS:
+		eap_notify_status(sm, "remote certificate verification",
+				  "success");
+		break;
 	case TLS_CERT_CHAIN_FAILURE:
 		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR
 			"reason=%d depth=%d subject='%s' err='%s'",
@@ -1247,6 +1306,8 @@
 			data->cert_fail.depth,
 			data->cert_fail.subject,
 			data->cert_fail.reason_txt);
+		eap_notify_status(sm, "remote certificate verification",
+				  data->cert_fail.reason_txt);
 		break;
 	case TLS_PEER_CERTIFICATE:
 		if (!sm->eapol_cb->notify_cert)
@@ -1267,6 +1328,14 @@
 					  data->peer_cert.subject,
 					  hash_hex, data->peer_cert.cert);
 		break;
+	case TLS_ALERT:
+		if (data->alert.is_local)
+			eap_notify_status(sm, "local TLS alert",
+					  data->alert.description);
+		else
+			eap_notify_status(sm, "remote TLS alert",
+					  data->alert.description);
+		break;
 	}
 
 	os_free(hash_hex);
@@ -1321,6 +1390,13 @@
 		return NULL;
 	}
 
+	sm->ssl_ctx2 = tls_init(&tlsconf);
+	if (sm->ssl_ctx2 == NULL) {
+		wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS "
+			   "context (2).");
+		/* Run without separate TLS context within TLS tunnel */
+	}
+
 	return sm;
 }
 
@@ -1338,6 +1414,8 @@
 		return;
 	eap_deinit_prev_method(sm, "EAP deinit");
 	eap_sm_abort(sm);
+	if (sm->ssl_ctx2)
+		tls_deinit(sm->ssl_ctx2);
 	tls_deinit(sm->ssl_ctx);
 	os_free(sm);
 }
@@ -1857,6 +1935,27 @@
 }
 
 
+static int eap_get_ext_password(struct eap_sm *sm,
+				struct eap_peer_config *config)
+{
+	char *name;
+
+	if (config->password == NULL)
+		return -1;
+
+	name = os_zalloc(config->password_len + 1);
+	if (name == NULL)
+		return -1;
+	os_memcpy(name, config->password, config->password_len);
+
+	ext_password_free(sm->ext_pw_buf);
+	sm->ext_pw_buf = ext_password_get(sm->ext_pw, name);
+	os_free(name);
+
+	return sm->ext_pw_buf == NULL ? -1 : 0;
+}
+
+
 /**
  * eap_get_config_password - Get password from the network configuration
  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
@@ -1868,6 +1967,14 @@
 	struct eap_peer_config *config = eap_get_config(sm);
 	if (config == NULL)
 		return NULL;
+
+	if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+		if (eap_get_ext_password(sm, config) < 0)
+			return NULL;
+		*len = wpabuf_len(sm->ext_pw_buf);
+		return wpabuf_head(sm->ext_pw_buf);
+	}
+
 	*len = config->password_len;
 	return config->password;
 }
@@ -1887,6 +1994,14 @@
 	struct eap_peer_config *config = eap_get_config(sm);
 	if (config == NULL)
 		return NULL;
+
+	if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+		if (eap_get_ext_password(sm, config) < 0)
+			return NULL;
+		*len = wpabuf_len(sm->ext_pw_buf);
+		return wpabuf_head(sm->ext_pw_buf);
+	}
+
 	*len = config->password_len;
 	if (hash)
 		*hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
@@ -2198,3 +2313,24 @@
 
 	return 1;
 }
+
+
+void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext)
+{
+	ext_password_free(sm->ext_pw_buf);
+	sm->ext_pw_buf = NULL;
+	sm->ext_pw = ext;
+}
+
+
+/**
+ * eap_set_anon_id - Set or add anonymous identity
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
+ * @len: Length of anonymous identity in octets
+ */
+void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len)
+{
+	if (sm->eapol_cb->set_anon_id)
+		sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len);
+}
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index b95a285..8bccef1 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -1,6 +1,6 @@
 /*
  * EAP peer state machine functions (RFC 4137)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -226,6 +226,23 @@
 	 */
 	void (*notify_cert)(void *ctx, int depth, const char *subject,
 			    const char *cert_hash, const struct wpabuf *cert);
+
+	/**
+	 * notify_status - Notification of the current EAP state
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @status: Step in the process of EAP authentication
+	 * @parameter: Step-specific parameter, e.g., EAP method name
+	 */
+	void (*notify_status)(void *ctx, const char *status,
+			      const char *parameter);
+
+	/**
+	 * set_anon_id - Set or add anonymous identity
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
+	 * @len: Length of anonymous identity in octets
+	 */
+	void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
 };
 
 /**
@@ -297,6 +314,10 @@
 int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf);
 int eap_is_wps_pin_enrollee(struct eap_peer_config *conf);
 
+struct ext_password_data;
+void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext);
+void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len);
+
 #endif /* IEEE8021X_EAPOL */
 
 #endif /* EAP_H */
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 7e37e44..59861cb 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -1,6 +1,6 @@
 /*
- * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -90,6 +90,7 @@
 {
 	struct eap_aka_data *data;
 	const char *phase1 = eap_get_config_phase1(sm);
+	struct eap_peer_config *config = eap_get_config(sm);
 
 	data = os_zalloc(sizeof(*data));
 	if (data == NULL)
@@ -102,6 +103,15 @@
 
 	data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL;
 
+	if (config && config->anonymous_identity) {
+		data->pseudonym = os_malloc(config->anonymous_identity_len);
+		if (data->pseudonym) {
+			os_memcpy(data->pseudonym, config->anonymous_identity,
+				  config->anonymous_identity_len);
+			data->pseudonym_len = config->anonymous_identity_len;
+		}
+	}
+
 	return data;
 }
 
@@ -227,21 +237,23 @@
 #define CLEAR_REAUTH_ID	0x02
 #define CLEAR_EAP_ID	0x04
 
-static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
+static void eap_aka_clear_identities(struct eap_sm *sm,
+				     struct eap_aka_data *data, int id)
 {
-	if (id & CLEAR_PSEUDONYM) {
+	if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym");
 		os_free(data->pseudonym);
 		data->pseudonym = NULL;
 		data->pseudonym_len = 0;
+		eap_set_anon_id(sm, NULL, 0);
 	}
-	if (id & CLEAR_REAUTH_ID) {
+	if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id");
 		os_free(data->reauth_id);
 		data->reauth_id = NULL;
 		data->reauth_id_len = 0;
 	}
-	if (id & CLEAR_EAP_ID) {
+	if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id");
 		os_free(data->last_eap_identity);
 		data->last_eap_identity = NULL;
@@ -288,6 +300,7 @@
 				  realm, realm_len);
 		}
 		data->pseudonym_len = attr->next_pseudonym_len + realm_len;
+		eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
 	}
 
 	if (attr->next_reauth_id) {
@@ -425,6 +438,8 @@
 	data->num_id_req = 0;
 	data->num_notification = 0;
 
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)",
+		   err);
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
 			       EAP_AKA_SUBTYPE_CLIENT_ERROR);
 	eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
@@ -486,16 +501,16 @@
 		   data->pseudonym) {
 		identity = data->pseudonym;
 		identity_len = data->pseudonym_len;
-		eap_aka_clear_identities(data, CLEAR_REAUTH_ID);
+		eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
 	} else if (id_req != NO_ID_REQ) {
 		identity = eap_get_config_identity(sm, &identity_len);
 		if (identity) {
-			eap_aka_clear_identities(data, CLEAR_PSEUDONYM |
+			eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM |
 						 CLEAR_REAUTH_ID);
 		}
 	}
 	if (id_req != NO_ID_REQ)
-		eap_aka_clear_identities(data, CLEAR_EAP_ID);
+		eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
 
 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id);
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
@@ -898,7 +913,7 @@
 	 * other words, if no new identities are received, full
 	 * authentication will be used on next reauthentication (using
 	 * pseudonym identity or permanent identity). */
-	eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 
 	if (attr->encr_data) {
 		u8 *decrypted;
@@ -1126,7 +1141,7 @@
 					   data->nonce_s, data->mk,
 					   data->msk, data->emsk);
 	}
-	eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 	eap_aka_learn_ids(sm, data, &eattr);
 
 	if (data->result_ind && attr->result_ind)
@@ -1142,7 +1157,8 @@
 	if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of "
 			   "fast reauths performed - force fullauth");
-		eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+		eap_aka_clear_identities(sm, data,
+					 CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 	}
 	os_free(decrypted);
 	return eap_aka_response_reauth(data, id, 0, data->nonce_s);
@@ -1260,7 +1276,7 @@
 static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
 {
 	struct eap_aka_data *data = priv;
-	eap_aka_clear_identities(data, CLEAR_EAP_ID);
+	eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
 	data->prev_id = -1;
 	wpabuf_free(data->id_msgs);
 	data->id_msgs = NULL;
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index a6f6f4c..ed90919 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -35,6 +35,9 @@
 	 *
 	 * If not set, the identity field will be used for both unencrypted and
 	 * protected fields.
+	 *
+	 * This field can also be used with EAP-SIM/AKA/AKA' to store the
+	 * pseudonym identity.
 	 */
 	u8 *anonymous_identity;
 
@@ -619,6 +622,7 @@
 	int fragment_size;
 
 #define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
+#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1)
 	/**
 	 * flags - Network configuration flags (bitfield)
 	 *
@@ -626,6 +630,8 @@
 	 * for the network parameters.
 	 * bit 0 = password is represented as a 16-byte NtPasswordHash value
 	 *         instead of plaintext password
+	 * bit 1 = password is stored in external storage; the value in the
+	 *         password field is the name of that external entry
 	 */
 	u32 flags;
 };
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index 31d9f7c..7ca5288 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -169,7 +169,7 @@
 	data->phase2_type.vendor = EAP_VENDOR_IETF;
 	data->phase2_type.method = EAP_TYPE_NONE;
 
-	if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_FAST)) {
 		wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
 		eap_fast_deinit(sm, data);
 		return NULL;
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 06d6db6..dd94317 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -317,6 +317,7 @@
 	void *msg_ctx;
 	void *scard_ctx;
 	void *ssl_ctx;
+	void *ssl_ctx2;
 
 	unsigned int workaround;
 
@@ -329,6 +330,9 @@
 	struct wps_context *wps;
 
 	int prev_failure;
+
+	struct ext_password_data *ext_pw;
+	struct wpabuf *ext_pw_buf;
 };
 
 const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
diff --git a/src/eap_peer/eap_md5.c b/src/eap_peer/eap_md5.c
index e348415..d06befa 100644
--- a/src/eap_peer/eap_md5.c
+++ b/src/eap_peer/eap_md5.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994)
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -86,7 +86,13 @@
 
 	id = eap_get_id(resp);
 	rpos = wpabuf_put(resp, CHAP_MD5_LEN);
-	chap_md5(id, password, password_len, challenge, challenge_len, rpos);
+	if (chap_md5(id, password, password_len, challenge, challenge_len,
+		     rpos)) {
+		wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed");
+		ret->ignore = TRUE;
+		wpabuf_free(resp);
+		return NULL;
+	}
 	wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN);
 
 	return resp;
diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h
index ff9f50d..4994ff1 100644
--- a/src/eap_peer/eap_methods.h
+++ b/src/eap_peer/eap_methods.h
@@ -85,6 +85,7 @@
 /* EAP peer method registration calls for statically linked in methods */
 int eap_peer_md5_register(void);
 int eap_peer_tls_register(void);
+int eap_peer_unauth_tls_register(void);
 int eap_peer_mschapv2_register(void);
 int eap_peer_peap_register(void);
 int eap_peer_ttls_register(void);
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index 3b0a116..fb6c282 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -304,7 +304,9 @@
 			"EAP-MSCHAPV2: Password changed successfully");
 		data->prev_error = 0;
 		os_free(config->password);
-		if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
+		if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+			/* TODO: update external storage */
+		} else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
 			config->password = os_malloc(16);
 			config->password_len = 16;
 			if (config->password) {
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 0caa77e..7fff145 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -159,7 +159,7 @@
 	data->phase2_type.vendor = EAP_VENDOR_IETF;
 	data->phase2_type.method = EAP_TYPE_NONE;
 
-	if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) {
 		wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
 		eap_peap_deinit(sm, data);
 		return NULL;
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 37e9234..267d0a5 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/sha256.h"
 #include "eap_peer/eap_i.h"
 #include "eap_common/eap_pwd_common.h"
 
@@ -459,10 +460,10 @@
 				 const u8 *payload, size_t payload_len)
 {
 	BIGNUM *x = NULL, *y = NULL;
-	HMAC_CTX ctx;
+	struct crypto_hash *hash;
 	u32 cs;
 	u16 grp;
-	u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
 	int offset;
 
 	/*
@@ -489,7 +490,9 @@
 	 * server's commit is H(k | server_element | server_scalar |
 	 *			peer_element | peer_scalar | ciphersuite)
 	 */
-	H_Init(&ctx);
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
 
 	/*
 	 * zero the memory each time because this is mod prime math and some
@@ -498,7 +501,7 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 	BN_bn2bin(data->k, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -511,18 +514,18 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->server_scalar);
 	BN_bn2bin(data->server_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* my element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -536,27 +539,27 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* my scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->my_scalar);
 	BN_bn2bin(data->my_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* the ciphersuite */
-	H_Update(&ctx, (u8 *) &cs, sizeof(u32));
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
 
 	/* random function fin */
-	H_Final(&ctx, conf);
+	eap_pwd_h_final(hash, conf);
 
 	ptr = (u8 *) payload;
-	if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
+	if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
 		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
 		goto fin;
 	}
@@ -568,13 +571,15 @@
 	 *  H(k | peer_element | peer_scalar | server_element | server_scalar |
 	 *    ciphersuite)
 	 */
-	H_Init(&ctx);
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
 
 	/* k */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 	BN_bn2bin(data->k, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* my element */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -587,18 +592,18 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* my scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->my_scalar);
 	BN_bn2bin(data->my_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* server element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -611,24 +616,24 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->server_scalar);
 	BN_bn2bin(data->server_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* the ciphersuite */
-	H_Update(&ctx, (u8 *) &cs, sizeof(u32));
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
 
 	/* all done */
-	H_Final(&ctx, conf);
+	eap_pwd_h_final(hash, conf);
 
 	if (compute_keys(data->grp, data->bnctx, data->k,
 			 data->my_scalar, data->server_scalar, conf, ptr,
@@ -638,11 +643,11 @@
 		goto fin;
 	}
 
-	data->outbuf = wpabuf_alloc(SHA256_DIGEST_LENGTH);
+	data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
 	if (data->outbuf == NULL)
 		goto fin;
 
-	wpabuf_put_data(data->outbuf, conf, SHA256_DIGEST_LENGTH);
+	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 
 fin:
 	os_free(cruft);
@@ -725,6 +730,7 @@
 		 */
 		if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
 			wpabuf_free(data->outbuf);
+			data->outbuf = NULL;
 			data->out_frag_pos = 0;
 		}
 		wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes",
@@ -856,8 +862,11 @@
 	/*
 	 * if we're not fragmenting then there's no need to carry this around
 	 */
-	if (data->out_frag_pos == 0)
+	if (data->out_frag_pos == 0) {
 		wpabuf_free(data->outbuf);
+		data->outbuf = NULL;
+		data->out_frag_pos = 0;
+	}
 
 	return resp;
 }
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index fb4ae82..c936a44 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-SIM (RFC 4186)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -117,6 +117,15 @@
 			NULL;
 	}
 
+	if (config && config->anonymous_identity) {
+		data->pseudonym = os_malloc(config->anonymous_identity_len);
+		if (data->pseudonym) {
+			os_memcpy(data->pseudonym, config->anonymous_identity,
+				  config->anonymous_identity_len);
+			data->pseudonym_len = config->anonymous_identity_len;
+		}
+	}
+
 	eap_sim_state(data, CONTINUE);
 
 	return data;
@@ -258,13 +267,15 @@
 #define CLEAR_REAUTH_ID	0x02
 #define CLEAR_EAP_ID	0x04
 
-static void eap_sim_clear_identities(struct eap_sim_data *data, int id)
+static void eap_sim_clear_identities(struct eap_sm *sm,
+				     struct eap_sim_data *data, int id)
 {
 	if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym");
 		os_free(data->pseudonym);
 		data->pseudonym = NULL;
 		data->pseudonym_len = 0;
+		eap_set_anon_id(sm, NULL, 0);
 	}
 	if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id");
@@ -319,6 +330,7 @@
 				  realm, realm_len);
 		}
 		data->pseudonym_len = attr->next_pseudonym_len + realm_len;
+		eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
 	}
 
 	if (attr->next_reauth_id) {
@@ -352,6 +364,8 @@
 	data->num_id_req = 0;
 	data->num_notification = 0;
 
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Send Client-Error (error code %d)",
+		   err);
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
 			       EAP_SIM_SUBTYPE_CLIENT_ERROR);
 	eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
@@ -376,16 +390,16 @@
 		   data->pseudonym) {
 		identity = data->pseudonym;
 		identity_len = data->pseudonym_len;
-		eap_sim_clear_identities(data, CLEAR_REAUTH_ID);
+		eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID);
 	} else if (id_req != NO_ID_REQ) {
 		identity = eap_get_config_identity(sm, &identity_len);
 		if (identity) {
-			eap_sim_clear_identities(data, CLEAR_PSEUDONYM |
+			eap_sim_clear_identities(sm, data, CLEAR_PSEUDONYM |
 						 CLEAR_REAUTH_ID);
 		}
 	}
 	if (id_req != NO_ID_REQ)
-		eap_sim_clear_identities(data, CLEAR_EAP_ID);
+		eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
 
 	wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
@@ -432,7 +446,8 @@
 
 
 static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data,
-					       u8 id, int counter_too_small)
+					       u8 id, int counter_too_small,
+					       const u8 *nonce_s)
 {
 	struct eap_sim_msg *msg;
 	unsigned int counter;
@@ -467,7 +482,7 @@
 	}
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->k_aut, data->nonce_s,
+	return eap_sim_msg_finish(msg, data->k_aut, nonce_s,
 				  EAP_SIM_NONCE_S_LEN);
 }
 
@@ -667,7 +682,7 @@
 	 * other words, if no new reauth identity is received, full
 	 * authentication will be used on next reauthentication (using
 	 * pseudonym identity or permanent identity). */
-	eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 
 	if (attr->encr_data) {
 		u8 *decrypted;
@@ -863,7 +878,7 @@
 		data->reauth_id = NULL;
 		data->reauth_id_len = 0;
 		os_free(decrypted);
-		return eap_sim_response_reauth(data, id, 1);
+		return eap_sim_response_reauth(data, id, 1, eattr.nonce_s);
 	}
 	data->counter = eattr.counter;
 
@@ -875,7 +890,7 @@
 				   data->reauth_id, data->reauth_id_len,
 				   data->nonce_s, data->mk, data->msk,
 				   data->emsk);
-	eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+	eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 	eap_sim_learn_ids(sm, data, &eattr);
 
 	if (data->result_ind && attr->result_ind)
@@ -891,10 +906,11 @@
 	if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of "
 			   "fast reauths performed - force fullauth");
-		eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+		eap_sim_clear_identities(sm, data,
+					 CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 	}
 	os_free(decrypted);
-	return eap_sim_response_reauth(data, id, 0);
+	return eap_sim_response_reauth(data, id, 0, data->nonce_s);
 }
 
 
@@ -1002,7 +1018,7 @@
 static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv)
 {
 	struct eap_sim_data *data = priv;
-	eap_sim_clear_identities(data, CLEAR_EAP_ID);
+	eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
 	data->use_result_ind = 0;
 }
 
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index a3067fa..061a72b 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-TLS (RFC 2716)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -21,6 +21,8 @@
 struct eap_tls_data {
 	struct eap_ssl_data ssl;
 	u8 *key_data;
+	void *ssl_ctx;
+	u8 eap_type;
 };
 
 
@@ -40,7 +42,10 @@
 	if (data == NULL)
 		return NULL;
 
-	if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+	data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+		sm->ssl_ctx;
+
+	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) {
 		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
 		eap_tls_deinit(sm, data);
 		if (config->engine) {
@@ -58,10 +63,39 @@
 		return NULL;
 	}
 
+	data->eap_type = EAP_TYPE_TLS;
+
 	return data;
 }
 
 
+#ifdef EAP_UNAUTH_TLS
+static void * eap_unauth_tls_init(struct eap_sm *sm)
+{
+	struct eap_tls_data *data;
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+		sm->ssl_ctx;
+
+	if (eap_peer_tls_ssl_init(sm, &data->ssl, config,
+				  EAP_UNAUTH_TLS_TYPE)) {
+		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+		eap_tls_deinit(sm, data);
+		return NULL;
+	}
+
+	data->eap_type = EAP_UNAUTH_TLS_TYPE;
+
+	return data;
+}
+#endif /* EAP_UNAUTH_TLS */
+
+
 static void eap_tls_deinit(struct eap_sm *sm, void *priv)
 {
 	struct eap_tls_data *data = priv;
@@ -105,7 +139,7 @@
 		return resp;
 	}
 
-	return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
+	return eap_peer_tls_build_ack(id, data->eap_type, 0);
 }
 
 
@@ -145,7 +179,7 @@
 	const u8 *pos;
 	struct eap_tls_data *data = priv;
 
-	pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret,
+	pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret,
 					reqData, &left, &flags);
 	if (pos == NULL)
 		return NULL;
@@ -158,19 +192,19 @@
 	}
 
 	resp = NULL;
-	res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id,
-					  pos, left, &resp);
+	res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0,
+					  id, pos, left, &resp);
 
 	if (res < 0) {
 		return eap_tls_failure(sm, data, ret, res, resp, id);
 	}
 
-	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
+	if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
 		eap_tls_success(sm, data, ret);
 
 	if (res == 1) {
 		wpabuf_free(resp);
-		return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
+		return eap_peer_tls_build_ack(id, data->eap_type, 0);
 	}
 
 	return resp;
@@ -180,7 +214,7 @@
 static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv)
 {
 	struct eap_tls_data *data = priv;
-	return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+	return tls_connection_established(data->ssl_ctx, data->ssl.conn);
 }
 
 
@@ -281,3 +315,34 @@
 		eap_peer_method_free(eap);
 	return ret;
 }
+
+
+#ifdef EAP_UNAUTH_TLS
+int eap_peer_unauth_tls_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_UNAUTH_TLS,
+				    EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_unauth_tls_init;
+	eap->deinit = eap_tls_deinit;
+	eap->process = eap_tls_process;
+	eap->isKeyAvailable = eap_tls_isKeyAvailable;
+	eap->getKey = eap_tls_getKey;
+	eap->get_status = eap_tls_get_status;
+	eap->has_reauth_data = eap_tls_has_reauth_data;
+	eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
+	eap->init_for_reauth = eap_tls_init_for_reauth;
+	eap->get_emsk = eap_tls_get_emsk;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
+#endif /* EAP_UNAUTH_TLS */
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 52549f4..7eefe8c 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -16,6 +16,18 @@
 #include "eap_config.h"
 
 
+static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+					 u8 code, u8 identifier)
+{
+	if (type == EAP_UNAUTH_TLS_TYPE)
+		return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
+				     EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
+				     code, identifier);
+	return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
+			     identifier);
+}
+
+
 static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
 			      const u8 **data, size_t *data_len)
 {
@@ -48,6 +60,10 @@
 		params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
 	if (os_strstr(txt, "tls_disable_time_checks=1"))
 		params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+	if (os_strstr(txt, "tls_disable_session_ticket=1"))
+		params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+	if (os_strstr(txt, "tls_disable_session_ticket=0"))
+		params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
 }
 
 
@@ -99,6 +115,18 @@
 				    struct eap_peer_config *config, int phase2)
 {
 	os_memset(params, 0, sizeof(*params));
+	if (sm->workaround && data->eap_type != EAP_TYPE_FAST) {
+		/*
+		 * Some deployed authentication servers seem to be unable to
+		 * handle the TLS Session Ticket extension (they are supposed
+		 * to ignore unrecognized TLS extensions, but end up rejecting
+		 * the ClientHello instead). As a workaround, disable use of
+		 * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and
+		 * EAP-TTLS (EAP-FAST uses session ticket, so any server that
+		 * supports EAP-FAST does not need this workaround).
+		 */
+		params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+	}
 	if (phase2) {
 		wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
 		eap_tls_params_from_conf2(params, config);
@@ -136,14 +164,14 @@
 {
 	int res;
 
-	data->conn = tls_connection_init(sm->ssl_ctx);
+	data->conn = tls_connection_init(data->ssl_ctx);
 	if (data->conn == NULL) {
 		wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
 			   "connection");
 		return -1;
 	}
 
-	res = tls_connection_set_params(sm->ssl_ctx, data->conn, params);
+	res = tls_connection_set_params(data->ssl_ctx, data->conn, params);
 	if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
 		/*
 		 * At this point with the pkcs11 engine the PIN might be wrong.
@@ -162,13 +190,13 @@
 		config->pin = NULL;
 		eap_sm_request_pin(sm);
 		sm->ignore = TRUE;
-		tls_connection_deinit(sm->ssl_ctx, data->conn);
+		tls_connection_deinit(data->ssl_ctx, data->conn);
 		data->conn = NULL;
 		return -1;
 	} else if (res) {
 		wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
 			   "parameters");
-		tls_connection_deinit(sm->ssl_ctx, data->conn);
+		tls_connection_deinit(data->ssl_ctx, data->conn);
 		data->conn = NULL;
 		return -1;
 	}
@@ -182,13 +210,14 @@
  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
  * @data: Data for TLS processing
  * @config: Pointer to the network configuration
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
  * Returns: 0 on success, -1 on failure
  *
  * This function is used to initialize shared TLS functionality for EAP-TLS,
  * EAP-PEAP, EAP-TTLS, and EAP-FAST.
  */
 int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
-			  struct eap_peer_config *config)
+			  struct eap_peer_config *config, u8 eap_type)
 {
 	struct tls_connection_params params;
 
@@ -196,7 +225,10 @@
 		return -1;
 
 	data->eap = sm;
+	data->eap_type = eap_type;
 	data->phase2 = sm->init_phase2;
+	data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+		sm->ssl_ctx;
 	if (eap_tls_params_from_conf(sm, data, &params, config, data->phase2) <
 	    0)
 		return -1;
@@ -234,7 +266,7 @@
  */
 void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
 {
-	tls_connection_deinit(sm->ssl_ctx, data->conn);
+	tls_connection_deinit(data->ssl_ctx, data->conn);
 	eap_peer_tls_reset_input(data);
 	eap_peer_tls_reset_output(data);
 }
@@ -257,7 +289,9 @@
 u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
 			     const char *label, size_t len)
 {
+#ifndef CONFIG_FIPS
 	struct tls_keys keys;
+#endif /* CONFIG_FIPS */
 	u8 *rnd = NULL, *out;
 
 	out = os_malloc(len);
@@ -265,16 +299,17 @@
 		return NULL;
 
 	/* First, try to use TLS library function for PRF, if available. */
-	if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
-	    0)
+	if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len)
+	    == 0)
 		return out;
 
+#ifndef CONFIG_FIPS
 	/*
 	 * TLS library did not support key generation, so get the needed TLS
 	 * session parameters and use an internal implementation of TLS PRF to
 	 * derive the key.
 	 */
-	if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+	if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys))
 		goto fail;
 
 	if (keys.client_random == NULL || keys.server_random == NULL ||
@@ -297,6 +332,7 @@
 	return out;
 
 fail:
+#endif /* CONFIG_FIPS */
 	os_free(out);
 	os_free(rnd);
 	return NULL;
@@ -441,14 +477,14 @@
 		WPA_ASSERT(data->tls_out == NULL);
 	}
 	appl_data = NULL;
-	data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn,
+	data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn,
 						 msg, &appl_data);
 
 	eap_peer_tls_reset_input(data);
 
 	if (appl_data &&
-	    tls_connection_established(sm->ssl_ctx, data->conn) &&
-	    !tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+	    tls_connection_established(data->ssl_ctx, data->conn) &&
+	    !tls_connection_get_failed(data->ssl_ctx, data->conn)) {
 		wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
 				    appl_data);
 		*out_data = appl_data;
@@ -514,9 +550,8 @@
 		length_included = 1;
 	}
 
-	*out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type,
-				  1 + length_included * 4 + len,
-				  EAP_CODE_RESPONSE, id);
+	*out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len,
+				      EAP_CODE_RESPONSE, id);
 	if (*out_data == NULL)
 		return -1;
 
@@ -616,7 +651,7 @@
 		return -1;
 	}
 
-	if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+	if (tls_connection_get_failed(data->ssl_ctx, data->conn)) {
 		/* TLS processing has failed - return error */
 		wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
 			   "report error");
@@ -654,8 +689,7 @@
 {
 	struct wpabuf *resp;
 
-	resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE,
-			     id);
+	resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id);
 	if (resp == NULL)
 		return NULL;
 	wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)",
@@ -675,7 +709,7 @@
 {
 	eap_peer_tls_reset_input(data);
 	eap_peer_tls_reset_output(data);
-	return tls_connection_shutdown(sm->ssl_ctx, data->conn);
+	return tls_connection_shutdown(data->ssl_ctx, data->conn);
 }
 
 
@@ -694,7 +728,8 @@
 	char name[128];
 	int len = 0, ret;
 
-	if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) {
+	if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0)
+	{
 		ret = os_snprintf(buf + len, buflen - len,
 				  "EAP TLS cipher=%s\n", name);
 		if (ret < 0 || (size_t) ret >= buflen - len)
@@ -741,13 +776,19 @@
 	size_t left;
 	unsigned int tls_msg_len;
 
-	if (tls_get_errors(sm->ssl_ctx)) {
+	if (tls_get_errors(data->ssl_ctx)) {
 		wpa_printf(MSG_INFO, "SSL: TLS errors detected");
 		ret->ignore = TRUE;
 		return NULL;
 	}
 
-	pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left);
+	if (eap_type == EAP_UNAUTH_TLS_TYPE)
+		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
+				       EAP_VENDOR_TYPE_UNAUTH_TLS, reqData,
+				       &left);
+	else
+		pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData,
+				       &left);
 	if (pos == NULL) {
 		ret->ignore = TRUE;
 		return NULL;
@@ -849,7 +890,7 @@
 	if (msg == NULL)
 		return need_more_input ? 1 : -1;
 
-	*in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg);
+	*in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg);
 	eap_peer_tls_reset_input(data);
 	if (*in_decrypted == NULL) {
 		wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
@@ -877,8 +918,8 @@
 {
 	if (in_data) {
 		eap_peer_tls_reset_output(data);
-		data->tls_out = tls_connection_encrypt(sm->ssl_ctx, data->conn,
-						       in_data);
+		data->tls_out = tls_connection_encrypt(data->ssl_ctx,
+						       data->conn, in_data);
 		if (data->tls_out == NULL) {
 			wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
 				   "data (in_len=%lu)",
@@ -943,8 +984,8 @@
 				   "method '%s'", start);
 		} else {
 			num_methods++;
-			_methods = os_realloc(methods,
-					      num_methods * sizeof(*methods));
+			_methods = os_realloc_array(methods, num_methods,
+						    sizeof(*methods));
 			if (_methods == NULL) {
 				os_free(methods);
 				os_free(buf);
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
index 7426467..91d3a25 100644
--- a/src/eap_peer/eap_tls_common.h
+++ b/src/eap_peer/eap_tls_common.h
@@ -1,6 +1,6 @@
 /*
  * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -63,6 +63,16 @@
 	 * eap - EAP state machine allocated with eap_peer_sm_init()
 	 */
 	struct eap_sm *eap;
+
+	/**
+	 * ssl_ctx - TLS library context to use for the connection
+	 */
+	void *ssl_ctx;
+
+	/**
+	 * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+	 */
+	u8 eap_type;
 };
 
 
@@ -75,9 +85,12 @@
  /* could be up to 128 bytes, but only the first 64 bytes are used */
 #define EAP_TLS_KEY_LEN 64
 
+/* dummy type used as a flag for UNAUTH-TLS */
+#define EAP_UNAUTH_TLS_TYPE 255
+
 
 int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
-			  struct eap_peer_config *config);
+			  struct eap_peer_config *config, u8 eap_type);
 void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
 u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
 			     const char *label, size_t len);
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 0204ba2..9360a42 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -110,7 +110,7 @@
 		data->phase2_eap_type.method = EAP_TYPE_NONE;
 	}
 
-	if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) {
 		wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
 		eap_ttls_deinit(sm, data);
 		return NULL;
@@ -401,6 +401,7 @@
 					    struct eap_method_ret *ret,
 					    struct wpabuf **resp)
 {
+#ifdef EAP_MSCHAPv2
 	struct wpabuf *msg;
 	u8 *buf, *pos, *challenge, *peer_challenge;
 	const u8 *identity, *password;
@@ -435,7 +436,6 @@
 			   "implicit challenge");
 		return -1;
 	}
-	peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
 
 	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
 			       RADIUS_VENDOR_ID_MICROSOFT, 1,
@@ -448,7 +448,14 @@
 	data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
 	*pos++ = data->ident;
 	*pos++ = 0; /* Flags */
-	os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+	if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {
+		os_free(challenge);
+		wpabuf_free(msg);
+		wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "
+			   "random data for peer challenge");
+		return -1;
+	}
+	peer_challenge = pos;
 	pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
 	os_memset(pos, 0, 8); /* Reserved, must be zero */
 	pos += 8;
@@ -456,6 +463,7 @@
 				     password_len, pwhash, challenge,
 				     peer_challenge, pos, data->auth_response,
 				     data->master_key)) {
+		os_free(challenge);
 		wpabuf_free(msg);
 		wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
 			   "response");
@@ -481,6 +489,10 @@
 	}
 
 	return 0;
+#else /* EAP_MSCHAPv2 */
+	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
+	return -1;
+#endif /* EAP_MSCHAPv2 */
 }
 
 
@@ -1035,6 +1047,7 @@
 					    struct eap_method_ret *ret,
 					    struct ttls_parse_avp *parse)
 {
+#ifdef EAP_MSCHAPv2
 	if (parse->mschapv2_error) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received "
 			   "MS-CHAP-Error - failed");
@@ -1083,6 +1096,10 @@
 	 * with EAP-Success after this.
 	 */
 	return 1;
+#else /* EAP_MSCHAPv2 */
+	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
+	return -1;
+#endif /* EAP_MSCHAPv2 */
 }
 
 
diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c
index f23b9d2..040d1e7 100644
--- a/src/eap_peer/eap_vendor_test.c
+++ b/src/eap_peer/eap_vendor_test.c
@@ -19,7 +19,7 @@
 #endif /* TEST_PENDING_REQUEST */
 
 
-#define EAP_VENDOR_ID 0xfffefd
+#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP
 #define EAP_VENDOR_TYPE 0xfcfbfaf9
 
 
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index 4473b90..d007a57 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -1,6 +1,6 @@
 /*
  * EAP-WSC peer for Wi-Fi Protected Setup
- * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -137,6 +137,8 @@
 	struct wps_context *wps;
 	struct wps_credential new_ap_settings;
 	int res;
+	u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN];
+	int nfc = 0;
 
 	wps = sm->wps;
 	if (wps == NULL) {
@@ -184,13 +186,26 @@
 		while (*pos != '\0' && *pos != ' ')
 			pos++;
 		cfg.pin_len = pos - (const char *) cfg.pin;
+		if (cfg.pin_len >= WPS_OOB_DEVICE_PASSWORD_MIN_LEN * 2 &&
+		    cfg.pin_len <= WPS_OOB_DEVICE_PASSWORD_LEN * 2 &&
+		    hexstr2bin((const char *) cfg.pin, dev_pw,
+			       cfg.pin_len / 2) == 0) {
+			/* Convert OOB Device Password to binary */
+			cfg.pin = dev_pw;
+			cfg.pin_len /= 2;
+		}
+		if (cfg.pin_len == 6 && os_strncmp(pos, "nfc-pw", 6) == 0) {
+			cfg.pin = NULL;
+			cfg.pin_len = 0;
+			nfc = 1;
+		}
 	} else {
 		pos = os_strstr(phase1, "pbc=1");
 		if (pos)
 			cfg.pbc = 1;
 	}
 
-	if (cfg.pin == NULL && !cfg.pbc) {
+	if (cfg.pin == NULL && !cfg.pbc && !nfc) {
 		wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
 			   "configuration data");
 		os_free(data);
diff --git a/src/eap_peer/mschapv2.c b/src/eap_peer/mschapv2.c
index 67dbadb..37e6735 100644
--- a/src/eap_peer/mschapv2.c
+++ b/src/eap_peer/mschapv2.c
@@ -63,22 +63,28 @@
 	if (pwhash) {
 		wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash",
 				password, password_len);
-		generate_nt_response_pwhash(auth_challenge, peer_challenge,
-					    username, username_len,
-					    password, nt_response);
-		generate_authenticator_response_pwhash(
-			password, peer_challenge, auth_challenge,
-			username, username_len, nt_response, auth_response);
+		if (generate_nt_response_pwhash(auth_challenge, peer_challenge,
+						username, username_len,
+						password, nt_response) ||
+		    generate_authenticator_response_pwhash(
+			    password, peer_challenge, auth_challenge,
+			    username, username_len, nt_response,
+			    auth_response))
+			return -1;
 	} else {
 		wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password",
 				      password, password_len);
-		generate_nt_response(auth_challenge, peer_challenge,
-				     username, username_len,
-				     password, password_len, nt_response);
-		generate_authenticator_response(password, password_len,
-						peer_challenge, auth_challenge,
-						username, username_len,
-						nt_response, auth_response);
+		if (generate_nt_response(auth_challenge, peer_challenge,
+					 username, username_len,
+					 password, password_len,
+					 nt_response) ||
+		    generate_authenticator_response(password, password_len,
+						    peer_challenge,
+						    auth_challenge,
+						    username, username_len,
+						    nt_response,
+						    auth_response))
+			return -1;
 	}
 	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response",
 		    nt_response, MSCHAPV2_NT_RESPONSE_LEN);
@@ -94,7 +100,8 @@
 		    hash_nt_password_hash(password_hash, password_hash_hash))
 			return -1;
 	}
-	get_master_key(password_hash_hash, nt_response, master_key);
+	if (get_master_key(password_hash_hash, nt_response, master_key))
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key",
 			master_key, MSCHAPV2_MASTER_KEY_LEN);
 
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index dfb0ff5..f92704a 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -151,7 +151,7 @@
 	int user_eap_method_index;
 	int init_phase2;
 	void *ssl_ctx;
-	void *eap_sim_db_priv;
+	struct eap_sim_db_data *eap_sim_db_priv;
 	Boolean backend_auth;
 	Boolean update_user;
 	int eap_server;
diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h
index 9667cf4..bc810a9 100644
--- a/src/eap_server/eap_methods.h
+++ b/src/eap_server/eap_methods.h
@@ -26,6 +26,7 @@
 int eap_server_identity_register(void);
 int eap_server_md5_register(void);
 int eap_server_tls_register(void);
+int eap_server_unauth_tls_register(void);
 int eap_server_mschapv2_register(void);
 int eap_server_peap_register(void);
 int eap_server_tlv_register(void);
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 44c089f..15f7e22 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -275,6 +275,11 @@
 {
 	SM_ENTRY(EAP, INTEGRITY_CHECK);
 
+	if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) {
+		sm->ignore = TRUE;
+		return;
+	}
+
 	if (sm->m->check) {
 		sm->ignore = sm->m->check(sm, sm->eap_method_priv,
 					  sm->eap_if.eapRespData);
@@ -309,6 +314,9 @@
 {
 	SM_ENTRY(EAP, METHOD_RESPONSE);
 
+	if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
+		return;
+
 	sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData);
 	if (sm->m->isDone(sm, sm->eap_method_priv)) {
 		eap_sm_Policy_update(sm, NULL, 0);
@@ -380,6 +388,9 @@
 	}
 	sm->m = NULL;
 
+	if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
+		return;
+
 	nak = wpabuf_head(sm->eap_if.eapRespData);
 	if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) {
 		len = be_to_host16(nak->length);
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 135c02b..a965cac 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -1,6 +1,6 @@
 /*
- * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf)
- * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
+ * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
+ * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -49,12 +49,12 @@
 	u8 *network_name;
 	size_t network_name_len;
 	u16 kdf;
+	int identity_round;
+	char permanent[20]; /* Permanent username */
 };
 
 
-static void eap_aka_determine_identity(struct eap_sm *sm,
-				       struct eap_aka_data *data,
-				       int before_identity, int after_reauth);
+static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data);
 
 
 static const char * eap_aka_state_txt(int state)
@@ -87,6 +87,96 @@
 }
 
 
+static int eap_aka_check_identity_reauth(struct eap_sm *sm,
+					 struct eap_aka_data *data,
+					 const char *username)
+{
+	if (data->eap_method == EAP_TYPE_AKA_PRIME &&
+	    username[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX)
+		return 0;
+	if (data->eap_method == EAP_TYPE_AKA &&
+	    username[0] != EAP_AKA_REAUTH_ID_PREFIX)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username);
+	data->reauth = eap_sim_db_get_reauth_entry(sm->eap_sim_db_priv,
+						   username);
+	if (data->reauth == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - "
+			   "request full auth identity");
+		/* Remain in IDENTITY state for another round */
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast re-authentication");
+	os_strlcpy(data->permanent, data->reauth->permanent,
+		   sizeof(data->permanent));
+	data->counter = data->reauth->counter;
+	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+		os_memcpy(data->k_encr, data->reauth->k_encr,
+			  EAP_SIM_K_ENCR_LEN);
+		os_memcpy(data->k_aut, data->reauth->k_aut,
+			  EAP_AKA_PRIME_K_AUT_LEN);
+		os_memcpy(data->k_re, data->reauth->k_re,
+			  EAP_AKA_PRIME_K_RE_LEN);
+	} else {
+		os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
+	}
+
+	eap_aka_state(data, REAUTH);
+	return 1;
+}
+
+
+static void eap_aka_check_identity(struct eap_sm *sm,
+				   struct eap_aka_data *data)
+{
+	char *username;
+
+	/* Check if we already know the identity from EAP-Response/Identity */
+
+	username = sim_get_username(sm->identity, sm->identity_len);
+	if (username == NULL)
+		return;
+
+	if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
+		os_free(username);
+		/*
+		 * Since re-auth username was recognized, skip AKA/Identity
+		 * exchange.
+		 */
+		return;
+	}
+
+	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
+	    (data->eap_method == EAP_TYPE_AKA &&
+	     username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
+		const char *permanent;
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
+			   username);
+		permanent = eap_sim_db_get_permanent(
+			sm->eap_sim_db_priv, username);
+		if (permanent == NULL) {
+			os_free(username);
+			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
+				   "identity - request permanent identity");
+			/* Remain in IDENTITY state for another round */
+			return;
+		}
+		os_strlcpy(data->permanent, permanent,
+			   sizeof(data->permanent));
+		/*
+		 * Since pseudonym username was recognized, skip AKA/Identity
+		 * exchange.
+		 */
+		eap_aka_fullauth(sm, data);
+	}
+
+	os_free(username);
+}
+
+
 static void * eap_aka_init(struct eap_sm *sm)
 {
 	struct eap_aka_data *data;
@@ -103,8 +193,8 @@
 	data->eap_method = EAP_TYPE_AKA;
 
 	data->state = IDENTITY;
-	eap_aka_determine_identity(sm, data, 1, 0);
 	data->pending_id = -1;
+	eap_aka_check_identity(sm, data);
 
 	return data;
 }
@@ -136,8 +226,8 @@
 	data->network_name_len = os_strlen(network_name);
 
 	data->state = IDENTITY;
-	eap_aka_determine_identity(sm, data, 1, 0);
 	data->pending_id = -1;
+	eap_aka_check_identity(sm, data);
 
 	return data;
 }
@@ -264,11 +354,8 @@
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity");
 	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
 			       EAP_AKA_SUBTYPE_IDENTITY);
-	if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
-				      sm->identity_len)) {
-		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
-		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
-	} else {
+	data->identity_round++;
+	if (data->identity_round == 1) {
 		/*
 		 * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is
 		 * ignored and the AKA/Identity is used to request the
@@ -276,6 +363,18 @@
 		 */
 		wpa_printf(MSG_DEBUG, "   AT_ANY_ID_REQ");
 		eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+	} else if (data->identity_round > 3) {
+		/* Cannot use more than three rounds of Identity messages */
+		return NULL;
+	} else if (sm->identity && sm->identity_len > 0 &&
+		   (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
+		    sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) {
+		/* Reauth id may have expired - try fullauth */
+		wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
+	} else {
+		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
 	}
 	buf = eap_sim_msg_finish(msg, NULL, NULL, 0);
 	if (eap_aka_add_id_msg(data, buf) < 0) {
@@ -292,12 +391,23 @@
 			      const u8 *nonce_s)
 {
 	os_free(data->next_pseudonym);
-	data->next_pseudonym =
-		eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1);
+	if (nonce_s == NULL) {
+		data->next_pseudonym =
+			eap_sim_db_get_next_pseudonym(
+				sm->eap_sim_db_priv,
+				data->eap_method == EAP_TYPE_AKA_PRIME ?
+				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
+	} else {
+		/* Do not update pseudonym during re-authentication */
+		data->next_pseudonym = NULL;
+	}
 	os_free(data->next_reauth_id);
 	if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
 		data->next_reauth_id =
-			eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 1);
+			eap_sim_db_get_next_reauth_id(
+				sm->eap_sim_db_priv,
+				data->eap_method == EAP_TYPE_AKA_PRIME ?
+				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
 	} else {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication "
 			   "count exceeded - force full authentication");
@@ -601,92 +711,72 @@
 
 
 static void eap_aka_determine_identity(struct eap_sm *sm,
-				       struct eap_aka_data *data,
-				       int before_identity, int after_reauth)
+				       struct eap_aka_data *data)
 {
-	const u8 *identity;
-	size_t identity_len;
-	int res;
-
-	identity = NULL;
-	identity_len = 0;
-
-	if (after_reauth && data->reauth) {
-		identity = data->reauth->identity;
-		identity_len = data->reauth->identity_len;
-	} else if (sm->identity && sm->identity_len > 0 &&
-		   sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	} else {
-		identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
-						    sm->identity,
-						    sm->identity_len,
-						    &identity_len);
-		if (identity == NULL) {
-			data->reauth = eap_sim_db_get_reauth_entry(
-				sm->eap_sim_db_priv, sm->identity,
-				sm->identity_len);
-			if (data->reauth &&
-			    data->reauth->aka_prime !=
-			    (data->eap_method == EAP_TYPE_AKA_PRIME)) {
-				wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data "
-					   "was for different AKA version");
-				data->reauth = NULL;
-			}
-			if (data->reauth) {
-				wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast "
-					   "re-authentication");
-				identity = data->reauth->identity;
-				identity_len = data->reauth->identity_len;
-				data->counter = data->reauth->counter;
-				if (data->eap_method == EAP_TYPE_AKA_PRIME) {
-					os_memcpy(data->k_encr,
-						  data->reauth->k_encr,
-						  EAP_SIM_K_ENCR_LEN);
-					os_memcpy(data->k_aut,
-						  data->reauth->k_aut,
-						  EAP_AKA_PRIME_K_AUT_LEN);
-					os_memcpy(data->k_re,
-						  data->reauth->k_re,
-						  EAP_AKA_PRIME_K_RE_LEN);
-				} else {
-					os_memcpy(data->mk, data->reauth->mk,
-						  EAP_SIM_MK_LEN);
-				}
-			}
-		}
-	}
-
-	if (identity == NULL ||
-	    eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
-				      sm->identity_len) < 0) {
-		if (before_identity) {
-			wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent user name "
-				   "not known - send AKA-Identity request");
-			eap_aka_state(data, IDENTITY);
-			return;
-		} else {
-			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown whether the "
-				   "permanent user name is known; try to use "
-				   "it");
-			/* eap_sim_db_get_aka_auth() will report failure, if
-			 * this identity is not known. */
-		}
-	}
+	char *username;
 
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity",
-			  identity, identity_len);
+			  sm->identity, sm->identity_len);
 
-	if (!after_reauth && data->reauth) {
-		eap_aka_state(data, REAUTH);
+	username = sim_get_username(sm->identity, sm->identity_len);
+	if (username == NULL) {
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
 		return;
 	}
 
-	res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, identity,
-				      identity_len, data->rand, data->autn,
-				      data->ik, data->ck, data->res,
-				      &data->res_len, sm);
+	if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
+		os_free(username);
+		return;
+	}
+
+	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
+	    (data->eap_method == EAP_TYPE_AKA &&
+	     username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
+		const char *permanent;
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
+			   username);
+		permanent = eap_sim_db_get_permanent(
+			sm->eap_sim_db_priv, username);
+		os_free(username);
+		if (permanent == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
+				   "identity - request permanent identity");
+			/* Remain in IDENTITY state for another round */
+			return;
+		}
+		os_strlcpy(data->permanent, permanent,
+			   sizeof(data->permanent));
+	} else if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+		    username[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+		   (data->eap_method == EAP_TYPE_AKA &&
+		    username[0] == EAP_AKA_PERMANENT_PREFIX)) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent username '%s'",
+			   username);
+		os_strlcpy(data->permanent, username, sizeof(data->permanent));
+		os_free(username);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'",
+			   username);
+		os_free(username);
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+
+	eap_aka_fullauth(sm, data);
+}
+
+
+static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data)
+{
+	size_t identity_len;
+	int res;
+
+	res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, data->permanent,
+				      data->rand, data->autn, data->ik,
+				      data->ck, data->res, &data->res_len, sm);
 	if (res == EAP_SIM_DB_PENDING) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
 			   "not yet available - pending request");
@@ -731,7 +821,7 @@
 			  sm->identity, identity_len);
 
 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
-		eap_aka_prime_derive_keys(identity, identity_len, data->ik,
+		eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik,
 					  data->ck, data->k_encr, data->k_aut,
 					  data->k_re, data->msk, data->emsk);
 	} else {
@@ -750,6 +840,8 @@
 				     struct wpabuf *respData,
 				     struct eap_sim_attrs *attr)
 {
+	u8 *new_identity;
+
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity");
 
 	if (attr->mac || attr->iv || attr->encr_data) {
@@ -760,17 +852,30 @@
 		return;
 	}
 
-	if (attr->identity) {
-		os_free(sm->identity);
-		sm->identity = os_malloc(attr->identity_len);
-		if (sm->identity) {
-			os_memcpy(sm->identity, attr->identity,
-				  attr->identity_len);
-			sm->identity_len = attr->identity_len;
-		}
+	/*
+	 * We always request identity with AKA/Identity, so the peer is
+	 * required to have replied with one.
+	 */
+	if (!attr->identity || attr->identity_len == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Peer did not provide any "
+			   "identity");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
 	}
 
-	eap_aka_determine_identity(sm, data, 0, 0);
+	new_identity = os_malloc(attr->identity_len);
+	if (new_identity == NULL) {
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_aka_state(data, NOTIFICATION);
+		return;
+	}
+	os_free(sm->identity);
+	sm->identity = new_identity;
+	os_memcpy(sm->identity, attr->identity, attr->identity_len);
+	sm->identity_len = attr->identity_len;
+
+	eap_aka_determine_identity(sm, data);
 	if (eap_get_id(respData) == data->pending_id) {
 		data->pending_id = -1;
 		eap_aka_add_id_msg(data, respData);
@@ -795,9 +900,6 @@
 				      struct wpabuf *respData,
 				      struct eap_sim_attrs *attr)
 {
-	const u8 *identity;
-	size_t identity_len;
-
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge");
 
 #ifdef EAP_SERVER_AKA_PRIME
@@ -870,16 +972,8 @@
 	} else
 		eap_aka_state(data, SUCCESS);
 
-	identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
-					    sm->identity_len, &identity_len);
-	if (identity == NULL) {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	}
-
 	if (data->next_pseudonym) {
-		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
-					 identity_len,
+		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
 					 data->next_pseudonym);
 		data->next_pseudonym = NULL;
 	}
@@ -887,16 +981,15 @@
 		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 #ifdef EAP_SERVER_AKA_PRIME
 			eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
-						    identity,
-						    identity_len,
+						    data->permanent,
 						    data->next_reauth_id,
 						    data->counter + 1,
 						    data->k_encr, data->k_aut,
 						    data->k_re);
 #endif /* EAP_SERVER_AKA_PRIME */
 		} else {
-			eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
-					      identity_len,
+			eap_sim_db_add_reauth(sm->eap_sim_db_priv,
+					      data->permanent,
 					      data->next_reauth_id,
 					      data->counter + 1,
 					      data->mk);
@@ -925,9 +1018,8 @@
 	 * maintaining a local flag stating whether this AUTS has already been
 	 * reported. */
 	if (!data->auts_reported &&
-	    eap_sim_db_resynchronize(sm->eap_sim_db_priv, sm->identity,
-				     sm->identity_len, attr->auts,
-				     data->rand)) {
+	    eap_sim_db_resynchronize(sm->eap_sim_db_priv, data->permanent,
+				     attr->auts, data->rand)) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed");
 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
 		eap_aka_state(data, NOTIFICATION);
@@ -935,8 +1027,7 @@
 	}
 	data->auts_reported = 1;
 
-	/* Try again after resynchronization */
-	eap_aka_determine_identity(sm, data, 0, 0);
+	/* Remain in CHALLENGE state to re-try after resynchronization */
 }
 
 
@@ -947,8 +1038,6 @@
 {
 	struct eap_sim_attrs eattr;
 	u8 *decrypted = NULL;
-	const u8 *identity, *id2;
-	size_t identity_len, id2_len;
 
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication");
 
@@ -991,7 +1080,7 @@
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
 			   "included AT_COUNTER_TOO_SMALL - starting full "
 			   "authentication");
-		eap_aka_determine_identity(sm, data, 0, 1);
+		eap_aka_fullauth(sm, data);
 		return;
 	}
 
@@ -1002,40 +1091,19 @@
 	} else
 		eap_aka_state(data, SUCCESS);
 
-	if (data->reauth) {
-		identity = data->reauth->identity;
-		identity_len = data->reauth->identity_len;
-	} else {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	}
-
-	id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
-				       identity_len, &id2_len);
-	if (id2) {
-		identity = id2;
-		identity_len = id2_len;
-	}
-
-	if (data->next_pseudonym) {
-		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
-					 identity_len, data->next_pseudonym);
-		data->next_pseudonym = NULL;
-	}
 	if (data->next_reauth_id) {
 		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 #ifdef EAP_SERVER_AKA_PRIME
 			eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
-						    identity,
-						    identity_len,
+						    data->permanent,
 						    data->next_reauth_id,
 						    data->counter + 1,
 						    data->k_encr, data->k_aut,
 						    data->k_re);
 #endif /* EAP_SERVER_AKA_PRIME */
 		} else {
-			eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
-					      identity_len,
+			eap_sim_db_add_reauth(sm->eap_sim_db_priv,
+					      data->permanent,
 					      data->next_reauth_id,
 					      data->counter + 1,
 					      data->mk);
diff --git a/src/eap_server/eap_server_md5.c b/src/eap_server/eap_server_md5.c
index f4bb261..5a5e290 100644
--- a/src/eap_server/eap_server_md5.c
+++ b/src/eap_server/eap_server_md5.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP-MD5 server
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -119,8 +119,12 @@
 	wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, CHAP_MD5_LEN);
 
 	id = eap_get_id(respData);
-	chap_md5(id, sm->user->password, sm->user->password_len,
-		 data->challenge, CHALLENGE_LEN, hash);
+	if (chap_md5(id, sm->user->password, sm->user->password_len,
+		     data->challenge, CHALLENGE_LEN, hash)) {
+		wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed");
+		data->state = FAILURE;
+		return;
+	}
 
 	if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) {
 		wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success");
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 6c47dee..b61061b 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/sha256.h"
 #include "eap_server/eap_i.h"
 #include "eap_common/eap_pwd_common.h"
 
@@ -40,7 +41,7 @@
 	EC_POINT *my_element;
 	EC_POINT *peer_element;
 
-	u8 my_confirm[SHA256_DIGEST_LENGTH];
+	u8 my_confirm[SHA256_MAC_LEN];
 
 	u8 msk[EAP_MSK_LEN];
 	u8 emsk[EAP_EMSK_LEN];
@@ -288,8 +289,8 @@
 				      struct eap_pwd_data *data, u8 id)
 {
 	BIGNUM *x = NULL, *y = NULL;
-	HMAC_CTX ctx;
-	u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+	struct crypto_hash *hash;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
 	u16 grp;
 	int offset;
 
@@ -313,7 +314,9 @@
 	 * commit is H(k | server_element | server_scalar | peer_element |
 	 *	       peer_scalar | ciphersuite)
 	 */
-	H_Init(&ctx);
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
 
 	/*
 	 * Zero the memory each time because this is mod prime math and some
@@ -324,7 +327,7 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 	BN_bn2bin(data->k, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -338,18 +341,18 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->my_scalar);
 	BN_bn2bin(data->my_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* peer element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -363,18 +366,18 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* peer scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->peer_scalar);
 	BN_bn2bin(data->peer_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* ciphersuite */
 	grp = htons(data->group_num);
@@ -386,17 +389,17 @@
 	ptr += sizeof(u8);
 	*ptr = EAP_PWD_DEFAULT_PRF;
 	ptr += sizeof(u8);
-	H_Update(&ctx, cruft, ptr-cruft);
+	eap_pwd_h_update(hash, cruft, ptr - cruft);
 
 	/* all done with the random function */
-	H_Final(&ctx, conf);
-	os_memcpy(data->my_confirm, conf, SHA256_DIGEST_LENGTH);
+	eap_pwd_h_final(hash, conf);
+	os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN);
 
-	data->outbuf = wpabuf_alloc(SHA256_DIGEST_LENGTH);
+	data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
 	if (data->outbuf == NULL)
 		goto fin;
 
-	wpabuf_put_data(data->outbuf, conf, SHA256_DIGEST_LENGTH);
+	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 
 fin:
 	os_free(cruft);
@@ -404,8 +407,6 @@
 	BN_free(y);
 	if (data->outbuf == NULL)
 		eap_pwd_state(data, FAILURE);
-
-	return;
 }
 
 
@@ -735,10 +736,10 @@
 			     const u8 *payload, size_t payload_len)
 {
 	BIGNUM *x = NULL, *y = NULL;
-	HMAC_CTX ctx;
+	struct crypto_hash *hash;
 	u32 cs;
 	u16 grp;
-	u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
 	int offset;
 
 	/* build up the ciphersuite: group | random_function | prf */
@@ -761,13 +762,15 @@
 	 * commit is H(k | peer_element | peer_scalar | server_element |
 	 *	       server_scalar | ciphersuite)
 	 */
-	H_Init(&ctx);
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
 
 	/* k */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 	BN_bn2bin(data->k, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* peer element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -780,18 +783,18 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* peer scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->peer_scalar);
 	BN_bn2bin(data->peer_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* server element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -805,28 +808,28 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->my_scalar);
 	BN_bn2bin(data->my_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* ciphersuite */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
-	H_Update(&ctx, (u8 *)&cs, sizeof(u32));
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
 
 	/* all done */
-	H_Final(&ctx, conf);
+	eap_pwd_h_final(hash, conf);
 
 	ptr = (u8 *) payload;
-	if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
+	if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
 		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
 			   "verify");
 		goto fin;
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index f49f70e..f83c3cb 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP-SIM (RFC 4186)
- * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -36,6 +36,8 @@
 	struct eap_sim_reauth *reauth;
 	u16 notification;
 	int use_result_ind;
+	int start_round;
+	char permanent[20]; /* Permanent username */
 };
 
 
@@ -105,17 +107,32 @@
 	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
 	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
 			       EAP_SIM_SUBTYPE_START);
-	if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
-				      sm->identity_len)) {
-		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
-		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
-	} else {
+	data->start_round++;
+	if (data->start_round == 1) {
 		/*
 		 * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
 		 * ignored and the SIM/Start is used to request the identity.
 		 */
 		wpa_printf(MSG_DEBUG, "   AT_ANY_ID_REQ");
 		eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+	} else if (data->start_round > 3) {
+		/* Cannot use more than three rounds of Start messages */
+		return NULL;
+	} else if (data->start_round == 0) {
+		/*
+		 * This is a special case that is used to recover from
+		 * AT_COUNTER_TOO_SMALL during re-authentication. Since we
+		 * already know the identity of the peer, there is no need to
+		 * request any identity in this case.
+		 */
+	} else if (sm->identity && sm->identity_len > 0 &&
+		   sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) {
+		/* Reauth id may have expired - try fullauth */
+		wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
+	} else {
+		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
+		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
 	}
 	wpa_printf(MSG_DEBUG, "   AT_VERSION_LIST");
 	ver[0] = 0;
@@ -131,12 +148,19 @@
 			      const u8 *nonce_s)
 {
 	os_free(data->next_pseudonym);
-	data->next_pseudonym =
-		eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
+	if (nonce_s == NULL) {
+		data->next_pseudonym =
+			eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv,
+						      EAP_SIM_DB_SIM);
+	} else {
+		/* Do not update pseudonym during re-authentication */
+		data->next_pseudonym = NULL;
+	}
 	os_free(data->next_reauth_id);
 	if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
 		data->next_reauth_id =
-			eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 0);
+			eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv,
+						      EAP_SIM_DB_SIM);
 	} else {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
 			   "count exceeded - force full authentication");
@@ -321,18 +345,22 @@
 static Boolean eap_sim_check(struct eap_sm *sm, void *priv,
 			     struct wpabuf *respData)
 {
-	struct eap_sim_data *data = priv;
 	const u8 *pos;
 	size_t len;
-	u8 subtype;
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
 	if (pos == NULL || len < 3) {
 		wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
 		return TRUE;
 	}
-	subtype = *pos;
 
+	return FALSE;
+}
+
+
+static Boolean eap_sim_unexpected_subtype(struct eap_sim_data *data,
+					  u8 subtype)
+{
 	if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
 		return FALSE;
 
@@ -386,85 +414,113 @@
 				  struct wpabuf *respData,
 				  struct eap_sim_attrs *attr)
 {
-	const u8 *identity;
 	size_t identity_len;
 	u8 ver_list[2];
+	u8 *new_identity;
+	char *username;
 
 	wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
 
-	if (attr->identity) {
-		os_free(sm->identity);
-		sm->identity = os_malloc(attr->identity_len);
-		if (sm->identity) {
-			os_memcpy(sm->identity, attr->identity,
-				  attr->identity_len);
-			sm->identity_len = attr->identity_len;
-		}
+	if (data->start_round == 0) {
+		/*
+		 * Special case for AT_COUNTER_TOO_SMALL recovery - no identity
+		 * was requested since we already know it.
+		 */
+		goto skip_id_update;
 	}
 
-	identity = NULL;
-	identity_len = 0;
-
-	if (sm->identity && sm->identity_len > 0 &&
-	    sm->identity[0] == EAP_SIM_PERMANENT_PREFIX) {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	} else {
-		identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
-						    sm->identity,
-						    sm->identity_len,
-						    &identity_len);
-		if (identity == NULL) {
-			data->reauth = eap_sim_db_get_reauth_entry(
-				sm->eap_sim_db_priv, sm->identity,
-				sm->identity_len);
-			if (data->reauth) {
-				wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast "
-					   "re-authentication");
-				identity = data->reauth->identity;
-				identity_len = data->reauth->identity_len;
-				data->counter = data->reauth->counter;
-				os_memcpy(data->mk, data->reauth->mk,
-					  EAP_SIM_MK_LEN);
-			}
-		}
+	/*
+	 * We always request identity in SIM/Start, so the peer is required to
+	 * have replied with one.
+	 */
+	if (!attr->identity || attr->identity_len == 0) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any "
+			   "identity");
+		goto failed;
 	}
 
-	if (identity == NULL) {
-		wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent"
-			   " user name");
-		eap_sim_state(data, FAILURE);
-		return;
-	}
+	new_identity = os_malloc(attr->identity_len);
+	if (new_identity == NULL)
+		goto failed;
+	os_free(sm->identity);
+	sm->identity = new_identity;
+	os_memcpy(sm->identity, attr->identity, attr->identity_len);
+	sm->identity_len = attr->identity_len;
 
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
-			  identity, identity_len);
+			  sm->identity, sm->identity_len);
+	username = sim_get_username(sm->identity, sm->identity_len);
+	if (username == NULL)
+		goto failed;
 
-	if (data->reauth) {
+	if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'",
+			   username);
+		data->reauth = eap_sim_db_get_reauth_entry(
+			sm->eap_sim_db_priv, username);
+		os_free(username);
+		if (data->reauth == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth "
+				   "identity - request full auth identity");
+			/* Remain in START state for another round */
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast re-authentication");
+		os_strlcpy(data->permanent, data->reauth->permanent,
+			   sizeof(data->permanent));
+		data->counter = data->reauth->counter;
+		os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
 		eap_sim_state(data, REAUTH);
 		return;
 	}
 
+	if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) {
+		const char *permanent;
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'",
+			   username);
+		permanent = eap_sim_db_get_permanent(
+			sm->eap_sim_db_priv, username);
+		os_free(username);
+		if (permanent == NULL) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym "
+				   "identity - request permanent identity");
+			/* Remain in START state for another round */
+			return;
+		}
+		os_strlcpy(data->permanent, permanent,
+			   sizeof(data->permanent));
+	} else if (username[0] == EAP_SIM_PERMANENT_PREFIX) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'",
+			   username);
+		os_strlcpy(data->permanent, username, sizeof(data->permanent));
+		os_free(username);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'",
+			   username);
+		os_free(username);
+		goto failed;
+	}
+
+skip_id_update:
+	/* Full authentication */
+
 	if (attr->nonce_mt == NULL || attr->selected_version < 0) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
 			   "required attributes");
-		eap_sim_state(data, FAILURE);
-		return;
+		goto failed;
 	}
 
 	if (!eap_sim_supported_ver(data, attr->selected_version)) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
 			   "version %d", attr->selected_version);
-		eap_sim_state(data, FAILURE);
-		return;
+		goto failed;
 	}
 
 	data->counter = 0; /* reset re-auth counter since this is full auth */
 	data->reauth = NULL;
 
 	data->num_chal = eap_sim_db_get_gsm_triplets(
-		sm->eap_sim_db_priv, identity, identity_len,
-		EAP_SIM_MAX_CHAL,
+		sm->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL,
 		(u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
 	if (data->num_chal == EAP_SIM_DB_PENDING) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
@@ -475,8 +531,7 @@
 	if (data->num_chal < 2) {
 		wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
 			   "authentication triplets for the peer");
-		eap_sim_state(data, FAILURE);
-		return;
+		goto failed;
 	}
 
 	identity_len = sm->identity_len;
@@ -497,6 +552,11 @@
 			    data->emsk);
 
 	eap_sim_state(data, CHALLENGE);
+	return;
+
+failed:
+	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+	eap_sim_state(data, NOTIFICATION);
 }
 
 
@@ -505,16 +565,14 @@
 				      struct wpabuf *respData,
 				      struct eap_sim_attrs *attr)
 {
-	const u8 *identity;
-	size_t identity_len;
-
 	if (attr->mac == NULL ||
 	    eap_sim_verify_mac(data->k_aut, respData, attr->mac,
 			       (u8 *) data->sres,
 			       data->num_chal * EAP_SIM_SRES_LEN)) {
 		wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
 			   "did not include valid AT_MAC");
-		eap_sim_state(data, FAILURE);
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_sim_state(data, NOTIFICATION);
 		return;
 	}
 
@@ -527,22 +585,13 @@
 	} else
 		eap_sim_state(data, SUCCESS);
 
-	identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
-					    sm->identity_len, &identity_len);
-	if (identity == NULL) {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	}
-
 	if (data->next_pseudonym) {
-		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
-					 identity_len,
+		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
 					 data->next_pseudonym);
 		data->next_pseudonym = NULL;
 	}
 	if (data->next_reauth_id) {
-		eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
-				      identity_len,
+		eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
 				      data->next_reauth_id, data->counter + 1,
 				      data->mk);
 		data->next_reauth_id = NULL;
@@ -557,8 +606,6 @@
 {
 	struct eap_sim_attrs eattr;
 	u8 *decrypted = NULL;
-	const u8 *identity, *id2;
-	size_t identity_len, id2_len;
 
 	if (attr->mac == NULL ||
 	    eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s,
@@ -594,6 +641,16 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes "
 		   "the correct AT_MAC");
+
+	if (eattr.counter_too_small) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
+			   "included AT_COUNTER_TOO_SMALL - starting full "
+			   "authentication");
+		data->start_round = -1;
+		eap_sim_state(data, START);
+		return;
+	}
+
 	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
 		data->use_result_ind = 1;
 		data->notification = EAP_SIM_SUCCESS;
@@ -601,29 +658,9 @@
 	} else
 		eap_sim_state(data, SUCCESS);
 
-	if (data->reauth) {
-		identity = data->reauth->identity;
-		identity_len = data->reauth->identity_len;
-	} else {
-		identity = sm->identity;
-		identity_len = sm->identity_len;
-	}
-
-	id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
-				       identity_len, &id2_len);
-	if (id2) {
-		identity = id2;
-		identity_len = id2_len;
-	}
-
-	if (data->next_pseudonym) {
-		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
-					 identity_len, data->next_pseudonym);
-		data->next_pseudonym = NULL;
-	}
 	if (data->next_reauth_id) {
-		eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
-				      identity_len, data->next_reauth_id,
+		eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
+				      data->next_reauth_id,
 				      data->counter + 1, data->mk);
 		data->next_reauth_id = NULL;
 	} else {
@@ -634,7 +671,8 @@
 	return;
 
 fail:
-	eap_sim_state(data, FAILURE);
+	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+	eap_sim_state(data, NOTIFICATION);
 	eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
 	data->reauth = NULL;
 	os_free(decrypted);
@@ -685,8 +723,24 @@
 	subtype = *pos;
 	pos += 3;
 
+	if (eap_sim_unexpected_subtype(data, subtype)) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected "
+			   "EAP-SIM Subtype in EAP Response");
+		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+		eap_sim_state(data, NOTIFICATION);
+		return;
+	}
+
 	if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
+		if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR &&
+		    (data->state == START || data->state == CHALLENGE ||
+		     data->state == REAUTH)) {
+			data->notification =
+				EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+			eap_sim_state(data, NOTIFICATION);
+			return;
+		}
 		eap_sim_state(data, FAILURE);
 		return;
 	}
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index d34ef41..447f47c 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -21,6 +21,7 @@
 	struct eap_ssl_data ssl;
 	enum { START, CONTINUE, SUCCESS, FAILURE } state;
 	int established;
+	u8 eap_type;
 };
 
 
@@ -65,10 +66,34 @@
 		return NULL;
 	}
 
+	data->eap_type = EAP_TYPE_TLS;
+
 	return data;
 }
 
 
+#ifdef EAP_SERVER_UNAUTH_TLS
+static void * eap_unauth_tls_init(struct eap_sm *sm)
+{
+	struct eap_tls_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = START;
+
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+		eap_tls_reset(sm, data);
+		return NULL;
+	}
+
+	data->eap_type = EAP_UNAUTH_TLS_TYPE;
+	return data;
+}
+#endif /* EAP_SERVER_UNAUTH_TLS */
+
+
 static void eap_tls_reset(struct eap_sm *sm, void *priv)
 {
 	struct eap_tls_data *data = priv;
@@ -84,8 +109,7 @@
 {
 	struct wpabuf *req;
 
-	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST,
-			    id);
+	req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id);
 	if (req == NULL) {
 		wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
 			   "request");
@@ -107,11 +131,11 @@
 	struct wpabuf *res;
 
 	if (data->ssl.state == FRAG_ACK) {
-		return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0);
+		return eap_server_tls_build_ack(id, data->eap_type, 0);
 	}
 
 	if (data->ssl.state == WAIT_FRAG_ACK) {
-		res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0,
+		res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0,
 					       id);
 		goto check_established;
 	}
@@ -129,7 +153,7 @@
 		return NULL;
 	}
 
-	res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id);
+	res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id);
 
 check_established:
 	if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
@@ -146,10 +170,17 @@
 static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
 			     struct wpabuf *respData)
 {
+	struct eap_tls_data *data = priv;
 	const u8 *pos;
 	size_t len;
 
-	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len);
+	if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
+		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
+				       EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
+				       &len);
+	else
+		pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
+				       respData, &len);
 	if (pos == NULL || len < 1) {
 		wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
 		return TRUE;
@@ -178,7 +209,7 @@
 {
 	struct eap_tls_data *data = priv;
 	if (eap_server_tls_process(sm, &data->ssl, respData, data,
-				   EAP_TYPE_TLS, NULL, eap_tls_process_msg) <
+				   data->eap_type, NULL, eap_tls_process_msg) <
 	    0)
 		eap_tls_state(data, FAILURE);
 }
@@ -278,3 +309,34 @@
 		eap_server_method_free(eap);
 	return ret;
 }
+
+
+#ifdef EAP_SERVER_UNAUTH_TLS
+int eap_server_unauth_tls_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_UNAUTH_TLS,
+				      EAP_VENDOR_TYPE_UNAUTH_TLS,
+				      "UNAUTH-TLS");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_unauth_tls_init;
+	eap->reset = eap_tls_reset;
+	eap->buildReq = eap_tls_buildReq;
+	eap->check = eap_tls_check;
+	eap->process = eap_tls_process;
+	eap->isDone = eap_tls_isDone;
+	eap->getKey = eap_tls_getKey;
+	eap->isSuccess = eap_tls_isSuccess;
+	eap->get_emsk = eap_tls_get_emsk;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
+#endif /* EAP_SERVER_UNAUTH_TLS */
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 88a410d..31be2ec 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -18,6 +18,18 @@
 static void eap_server_tls_free_in_buf(struct eap_ssl_data *data);
 
 
+struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+				  u8 code, u8 identifier)
+{
+	if (type == EAP_UNAUTH_TLS_TYPE)
+		return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
+				     EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
+				     code, identifier);
+	return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
+			     identifier);
+}
+
+
 int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
 			    int verify_peer)
 {
@@ -131,8 +143,7 @@
 	if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
 		plen += 4;
 
-	req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen,
-			    EAP_CODE_REQUEST, id);
+	req = eap_tls_msg_alloc(eap_type, plen, EAP_CODE_REQUEST, id);
 	if (req == NULL)
 		return NULL;
 
@@ -168,8 +179,7 @@
 {
 	struct wpabuf *req;
 
-	req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST,
-			    id);
+	req = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_REQUEST, id);
 	if (req == NULL)
 		return NULL;
 	wpa_printf(MSG_DEBUG, "SSL: Building ACK");
@@ -359,7 +369,13 @@
 	size_t left;
 	int ret, res = 0;
 
-	pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left);
+	if (eap_type == EAP_UNAUTH_TLS_TYPE)
+		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
+				       EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
+				       &left);
+	else
+		pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData,
+				       &left);
 	if (pos == NULL || left < 1)
 		return 0; /* Should not happen - frame already validated */
 	flags = *pos++;
diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c
index 3d2403e..67a3dfa 100644
--- a/src/eap_server/eap_server_tnc.c
+++ b/src/eap_server/eap_server_tnc.c
@@ -9,7 +9,6 @@
 #include "includes.h"
 
 #include "common.h"
-#include "base64.h"
 #include "eap_i.h"
 #include "tncs.h"
 
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index 45fbf52..647bd2f 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -674,6 +674,13 @@
 		return;
 	}
 
+	if (sm->identity == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user identity "
+			   "known");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
 	/* MSCHAPv2 does not include optional domain name in the
 	 * challenge-response calculation, so remove domain prefix
 	 * (if present). */
@@ -979,11 +986,12 @@
 	if (parse.user_name) {
 		os_free(sm->identity);
 		sm->identity = os_malloc(parse.user_name_len);
-		if (sm->identity) {
-			os_memcpy(sm->identity, parse.user_name,
-				  parse.user_name_len);
-			sm->identity_len = parse.user_name_len;
+		if (sm->identity == NULL) {
+			eap_ttls_state(data, FAILURE);
+			goto done;
 		}
+		os_memcpy(sm->identity, parse.user_name, parse.user_name_len);
+		sm->identity_len = parse.user_name_len;
 		if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1)
 		    != 0) {
 			wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not "
diff --git a/src/eap_server/eap_server_vendor_test.c b/src/eap_server/eap_server_vendor_test.c
index e4c8bc3..30f600d 100644
--- a/src/eap_server/eap_server_vendor_test.c
+++ b/src/eap_server/eap_server_vendor_test.c
@@ -12,7 +12,7 @@
 #include "eap_i.h"
 
 
-#define EAP_VENDOR_ID 0xfffefd
+#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP
 #define EAP_VENDOR_TYPE 0xfcfbfaf9
 
 
diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c
index 9db26dc..257013e 100644
--- a/src/eap_server/eap_sim_db.c
+++ b/src/eap_server/eap_sim_db.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP-SIM database/authenticator gateway
- * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2010, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -17,6 +17,9 @@
 
 #include "includes.h"
 #include <sys/un.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
 
 #include "common.h"
 #include "crypto/random.h"
@@ -26,15 +29,13 @@
 
 struct eap_sim_pseudonym {
 	struct eap_sim_pseudonym *next;
-	u8 *identity;
-	size_t identity_len;
-	char *pseudonym;
+	char *permanent; /* permanent username */
+	char *pseudonym; /* pseudonym username */
 };
 
 struct eap_sim_db_pending {
 	struct eap_sim_db_pending *next;
-	u8 imsi[20];
-	size_t imsi_len;
+	char imsi[20];
 	enum { PENDING, SUCCESS, FAILURE } state;
 	void *cb_session_ctx;
 	struct os_time timestamp;
@@ -66,19 +67,316 @@
 	struct eap_sim_pseudonym *pseudonyms;
 	struct eap_sim_reauth *reauths;
 	struct eap_sim_db_pending *pending;
+#ifdef CONFIG_SQLITE
+	sqlite3 *sqlite_db;
+	char db_tmp_identity[100];
+	char db_tmp_pseudonym_str[100];
+	struct eap_sim_pseudonym db_tmp_pseudonym;
+	struct eap_sim_reauth db_tmp_reauth;
+#endif /* CONFIG_SQLITE */
 };
 
 
+#ifdef CONFIG_SQLITE
+
+static int db_table_exists(sqlite3 *db, const char *name)
+{
+	char cmd[128];
+	os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
+	return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
+}
+
+
+static int db_table_create_pseudonym(sqlite3 *db)
+{
+	char *err = NULL;
+	const char *sql =
+		"CREATE TABLE pseudonyms("
+		"  permanent CHAR(21) PRIMARY KEY,"
+		"  pseudonym CHAR(21) NOT NULL"
+		");";
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
+		   "pseudonym information");
+	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int db_table_create_reauth(sqlite3 *db)
+{
+	char *err = NULL;
+	const char *sql =
+		"CREATE TABLE reauth("
+		"  permanent CHAR(21) PRIMARY KEY,"
+		"  reauth_id CHAR(21) NOT NULL,"
+		"  counter INTEGER,"
+		"  mk CHAR(40),"
+		"  k_encr CHAR(32),"
+		"  k_aut CHAR(64),"
+		"  k_re CHAR(64)"
+		");";
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
+		   "reauth information");
+	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static sqlite3 * db_open(const char *db_file)
+{
+	sqlite3 *db;
+
+	if (sqlite3_open(db_file, &db)) {
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database "
+			   "%s: %s", db_file, sqlite3_errmsg(db));
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	if (!db_table_exists(db, "pseudonyms") &&
+	    db_table_create_pseudonym(db) < 0) {
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	if (!db_table_exists(db, "reauth") &&
+	    db_table_create_reauth(db) < 0) {
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	return db;
+}
+
+
+static int valid_db_string(const char *str)
+{
+	const char *pos = str;
+	while (*pos) {
+		if ((*pos < '0' || *pos > '9') &&
+		    (*pos < 'a' || *pos > 'f'))
+			return 0;
+		pos++;
+	}
+	return 1;
+}
+
+
+static int db_add_pseudonym(struct eap_sim_db_data *data,
+			    const char *permanent, char *pseudonym)
+{
+	char cmd[128];
+	char *err = NULL;
+
+	if (!valid_db_string(permanent) || !valid_db_string(pseudonym)) {
+		os_free(pseudonym);
+		return -1;
+	}
+
+	os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms "
+		    "(permanent, pseudonym) VALUES ('%s', '%s');",
+		    permanent, pseudonym);
+	os_free(pseudonym);
+	if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
+	{
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct eap_sim_db_data *data = ctx;
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "permanent") == 0 && argv[i]) {
+			os_strlcpy(data->db_tmp_identity, argv[i],
+				   sizeof(data->db_tmp_identity));
+		}
+	}
+
+	return 0;
+}
+
+
+static char *
+db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym)
+{
+	char cmd[128];
+
+	if (!valid_db_string(pseudonym))
+		return NULL;
+	os_memset(&data->db_tmp_identity, 0, sizeof(data->db_tmp_identity));
+	os_snprintf(cmd, sizeof(cmd),
+		    "SELECT permanent FROM pseudonyms WHERE pseudonym='%s';",
+		    pseudonym);
+	if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) !=
+	    SQLITE_OK)
+		return NULL;
+	if (data->db_tmp_identity[0] == '\0')
+		return NULL;
+	return data->db_tmp_identity;
+}
+
+
+static int db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+			 char *reauth_id, u16 counter, const u8 *mk,
+			 const u8 *k_encr, const u8 *k_aut, const u8 *k_re)
+{
+	char cmd[2000], *pos, *end;
+	char *err = NULL;
+
+	if (!valid_db_string(permanent) || !valid_db_string(reauth_id)) {
+		os_free(reauth_id);
+		return -1;
+	}
+
+	pos = cmd;
+	end = pos + sizeof(cmd);
+	pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth "
+			   "(permanent, reauth_id, counter%s%s%s%s) "
+			   "VALUES ('%s', '%s', %u",
+			   mk ? ", mk" : "",
+			   k_encr ? ", k_encr" : "",
+			   k_aut ? ", k_aut" : "",
+			   k_re ? ", k_re" : "",
+			   permanent, reauth_id, counter);
+	os_free(reauth_id);
+
+	if (mk) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	if (k_encr) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, k_encr,
+					EAP_SIM_K_ENCR_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	if (k_aut) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, k_aut,
+					EAP_AKA_PRIME_K_AUT_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	if (k_re) {
+		pos += os_snprintf(pos, end - pos, ", '");
+		pos += wpa_snprintf_hex(pos, end - pos, k_re,
+					EAP_AKA_PRIME_K_RE_LEN);
+		pos += os_snprintf(pos, end - pos, "'");
+	}
+
+	os_snprintf(pos, end - pos, ");");
+
+	if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
+	{
+		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct eap_sim_db_data *data = ctx;
+	int i;
+	struct eap_sim_reauth *reauth = &data->db_tmp_reauth;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "permanent") == 0 && argv[i]) {
+			os_strlcpy(data->db_tmp_identity, argv[i],
+				   sizeof(data->db_tmp_identity));
+			reauth->permanent = data->db_tmp_identity;
+		} else if (os_strcmp(col[i], "counter") == 0 && argv[i]) {
+			reauth->counter = atoi(argv[i]);
+		} else if (os_strcmp(col[i], "mk") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk));
+		} else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->k_encr,
+				   sizeof(reauth->k_encr));
+		} else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->k_aut,
+				   sizeof(reauth->k_aut));
+		} else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) {
+			hexstr2bin(argv[i], reauth->k_re,
+				   sizeof(reauth->k_re));
+		}
+	}
+
+	return 0;
+}
+
+
+static struct eap_sim_reauth *
+db_get_reauth(struct eap_sim_db_data *data, const char *reauth_id)
+{
+	char cmd[256];
+
+	if (!valid_db_string(reauth_id))
+		return NULL;
+	os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth));
+	os_strlcpy(data->db_tmp_pseudonym_str, reauth_id,
+		   sizeof(data->db_tmp_pseudonym_str));
+	data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str;
+	os_snprintf(cmd, sizeof(cmd),
+		    "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id);
+	if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) !=
+	    SQLITE_OK)
+		return NULL;
+	if (data->db_tmp_reauth.permanent == NULL)
+		return NULL;
+	return &data->db_tmp_reauth;
+}
+
+
+static void db_remove_reauth(struct eap_sim_db_data *data,
+			     struct eap_sim_reauth *reauth)
+{
+	char cmd[256];
+
+	if (!valid_db_string(reauth->permanent))
+		return;
+	os_snprintf(cmd, sizeof(cmd),
+		    "DELETE FROM reauth WHERE permanent='%s';",
+		    reauth->permanent);
+	sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL);
+}
+
+#endif /* CONFIG_SQLITE */
+
+
 static struct eap_sim_db_pending *
-eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi,
-		       size_t imsi_len, int aka)
+eap_sim_db_get_pending(struct eap_sim_db_data *data, const char *imsi, int aka)
 {
 	struct eap_sim_db_pending *entry, *prev = NULL;
 
 	entry = data->pending;
 	while (entry) {
-		if (entry->aka == aka && entry->imsi_len == imsi_len &&
-		    os_memcmp(entry->imsi, imsi, imsi_len) == 0) {
+		if (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) {
 			if (prev)
 				prev->next = entry->next;
 			else
@@ -113,7 +411,7 @@
 	 * (IMSI = ASCII string, Kc/SRES/RAND = hex string)
 	 */
 
-	entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0);
+	entry = eap_sim_db_get_pending(data, imsi, 0);
 	if (entry == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
 			   "received message found");
@@ -191,7 +489,7 @@
 	 * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string)
 	 */
 
-	entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1);
+	entry = eap_sim_db_get_pending(data, imsi, 1);
 	if (entry == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
 			   "received message found");
@@ -340,6 +638,7 @@
 	addr.sun_family = AF_UNIX;
 	os_snprintf(addr.sun_path, sizeof(addr.sun_path),
 		    "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
+	os_free(data->local_sock);
 	data->local_sock = os_strdup(addr.sun_path);
 	if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 		perror("bind(eap_sim_db)");
@@ -389,11 +688,13 @@
  * @ctx: Context pointer for get_complete_cb
  * Returns: Pointer to a private data structure or %NULL on failure
  */
-void * eap_sim_db_init(const char *config,
-		       void (*get_complete_cb)(void *ctx, void *session_ctx),
-		       void *ctx)
+struct eap_sim_db_data *
+eap_sim_db_init(const char *config,
+		void (*get_complete_cb)(void *ctx, void *session_ctx),
+		void *ctx)
 {
 	struct eap_sim_db_data *data;
+	char *pos;
 
 	data = os_zalloc(sizeof(*data));
 	if (data == NULL)
@@ -405,10 +706,23 @@
 	data->fname = os_strdup(config);
 	if (data->fname == NULL)
 		goto fail;
+	pos = os_strstr(data->fname, " db=");
+	if (pos) {
+		*pos = '\0';
+#ifdef CONFIG_SQLITE
+		pos += 4;
+		data->sqlite_db = db_open(pos);
+		if (data->sqlite_db == NULL)
+			goto fail;
+#endif /* CONFIG_SQLITE */
+	}
 
 	if (os_strncmp(data->fname, "unix:", 5) == 0) {
-		if (eap_sim_db_open_socket(data))
-			goto fail;
+		if (eap_sim_db_open_socket(data)) {
+			wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database "
+				   "connection not available - will retry "
+				   "later");
+		}
 	}
 
 	return data;
@@ -423,7 +737,7 @@
 
 static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
 {
-	os_free(p->identity);
+	os_free(p->permanent);
 	os_free(p->pseudonym);
 	os_free(p);
 }
@@ -431,7 +745,7 @@
 
 static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
 {
-	os_free(r->identity);
+	os_free(r->permanent);
 	os_free(r->reauth_id);
 	os_free(r);
 }
@@ -448,6 +762,13 @@
 	struct eap_sim_reauth *r, *prevr;
 	struct eap_sim_db_pending *pending, *prev_pending;
 
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db) {
+		sqlite3_close(data->sqlite_db);
+		data->sqlite_db = NULL;
+	}
+#endif /* CONFIG_SQLITE */
+
 	eap_sim_db_close_socket(data);
 	os_free(data->fname);
 
@@ -514,9 +835,8 @@
 
 /**
  * eap_sim_db_get_gsm_triplets - Get GSM triplets
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username (prefix | IMSI)
  * @max_chal: Maximum number of triplets
  * @_rand: Buffer for RAND values
  * @kc: Buffer for Kc values
@@ -528,9 +848,6 @@
  * callback function registered with eap_sim_db_init() will be called once the
  * results become available.
  *
- * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
- * ASCII format.
- *
  * When using an external server for GSM triplets, this function can always
  * start a request and return EAP_SIM_DB_PENDING immediately if authentication
  * triplets are not available. Once the triplets are received, callback
@@ -539,39 +856,28 @@
  * function will then be called again and the newly received triplets will then
  * be given to the caller.
  */
-int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
-				size_t identity_len, int max_chal,
+int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data,
+				const char *username, int max_chal,
 				u8 *_rand, u8 *kc, u8 *sres,
 				void *cb_session_ctx)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_db_pending *entry;
 	int len, ret;
-	size_t i;
 	char msg[40];
+	const char *imsi;
+	size_t imsi_len;
 
-	if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
+	if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX ||
+	    username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+			   username);
 		return EAP_SIM_DB_FAILURE;
 	}
-	identity++;
-	identity_len--;
-	for (i = 0; i < identity_len; i++) {
-		if (identity[i] == '@') {
-			identity_len = i;
-			break;
-		}
-	}
-	if (identity_len + 1 > sizeof(entry->imsi)) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
-		return EAP_SIM_DB_FAILURE;
-	}
-	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
-			  identity, identity_len);
+	imsi = username + 1;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'",
+		   imsi);
 
-	entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
+	entry = eap_sim_db_get_pending(data, imsi, 0);
 	if (entry) {
 		int num_chal;
 		if (entry->state == FAILURE) {
@@ -606,18 +912,19 @@
 			return EAP_SIM_DB_FAILURE;
 	}
 
+	imsi_len = os_strlen(imsi);
 	len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
-	if (len < 0 || len + identity_len >= sizeof(msg))
+	if (len < 0 || len + imsi_len >= sizeof(msg))
 		return EAP_SIM_DB_FAILURE;
-	os_memcpy(msg + len, identity, identity_len);
-	len += identity_len;
+	os_memcpy(msg + len, imsi, imsi_len);
+	len += imsi_len;
 	ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
 	if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
 		return EAP_SIM_DB_FAILURE;
 	len += ret;
 
-	wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
-		    "data for IMSI", identity, identity_len);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
+		   "data for IMSI '%s'", imsi);
 	if (eap_sim_db_send(data, msg, len) < 0)
 		return EAP_SIM_DB_FAILURE;
 
@@ -626,8 +933,7 @@
 		return EAP_SIM_DB_FAILURE;
 
 	os_get_time(&entry->timestamp);
-	os_memcpy(entry->imsi, identity, identity_len);
-	entry->imsi_len = identity_len;
+	os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
 	entry->cb_session_ctx = cb_session_ctx;
 	entry->state = PENDING;
 	eap_sim_db_add_pending(data, entry);
@@ -637,189 +943,6 @@
 }
 
 
-static struct eap_sim_pseudonym *
-eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
-			 size_t identity_len)
-{
-	char *pseudonym;
-	size_t len;
-	struct eap_sim_pseudonym *p;
-
-	if (identity_len == 0 ||
-	    (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
-	     identity[0] != EAP_AKA_PSEUDONYM_PREFIX))
-		return NULL;
-
-	/* Remove possible realm from identity */
-	len = 0;
-	while (len < identity_len) {
-		if (identity[len] == '@')
-			break;
-		len++;
-	}
-
-	pseudonym = os_malloc(len + 1);
-	if (pseudonym == NULL)
-		return NULL;
-	os_memcpy(pseudonym, identity, len);
-	pseudonym[len] = '\0';
-
-	p = data->pseudonyms;
-	while (p) {
-		if (os_strcmp(p->pseudonym, pseudonym) == 0)
-			break;
-		p = p->next;
-	}
-
-	os_free(pseudonym);
-
-	return p;
-}
-
-
-static struct eap_sim_pseudonym *
-eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
-			    size_t identity_len)
-{
-	struct eap_sim_pseudonym *p;
-
-	if (identity_len == 0 ||
-	    (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
-	     identity[0] != EAP_AKA_PERMANENT_PREFIX))
-		return NULL;
-
-	p = data->pseudonyms;
-	while (p) {
-		if (identity_len == p->identity_len &&
-		    os_memcmp(p->identity, identity, identity_len) == 0)
-			break;
-		p = p->next;
-	}
-
-	return p;
-}
-
-
-static struct eap_sim_reauth *
-eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
-		      size_t identity_len)
-{
-	char *reauth_id;
-	size_t len;
-	struct eap_sim_reauth *r;
-
-	if (identity_len == 0 ||
-	    (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
-	     identity[0] != EAP_AKA_REAUTH_ID_PREFIX))
-		return NULL;
-
-	/* Remove possible realm from identity */
-	len = 0;
-	while (len < identity_len) {
-		if (identity[len] == '@')
-			break;
-		len++;
-	}
-
-	reauth_id = os_malloc(len + 1);
-	if (reauth_id == NULL)
-		return NULL;
-	os_memcpy(reauth_id, identity, len);
-	reauth_id[len] = '\0';
-
-	r = data->reauths;
-	while (r) {
-		if (os_strcmp(r->reauth_id, reauth_id) == 0)
-			break;
-		r = r->next;
-	}
-
-	os_free(reauth_id);
-
-	return r;
-}
-
-
-static struct eap_sim_reauth *
-eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
-			 size_t identity_len)
-{
-	struct eap_sim_pseudonym *p;
-	struct eap_sim_reauth *r;
-
-	if (identity_len == 0)
-		return NULL;
-
-	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
-	if (p == NULL)
-		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
-	if (p) {
-		identity = p->identity;
-		identity_len = p->identity_len;
-	}
-
-	r = data->reauths;
-	while (r) {
-		if (identity_len == r->identity_len &&
-		    os_memcmp(r->identity, identity, identity_len) == 0)
-			break;
-		r = r->next;
-	}
-
-	return r;
-}
-
-
-/**
- * eap_sim_db_identity_known - Verify whether the given identity is known
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes 
- * Returns: 0 if the user is found or -1 on failure
- *
- * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the
- * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
- */
-int eap_sim_db_identity_known(void *priv, const u8 *identity,
-			      size_t identity_len)
-{
-	struct eap_sim_db_data *data = priv;
-
-	if (identity == NULL || identity_len < 2)
-		return -1;
-
-	if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
-	    identity[0] == EAP_AKA_PSEUDONYM_PREFIX) {
-		struct eap_sim_pseudonym *p =
-			eap_sim_db_get_pseudonym(data, identity, identity_len);
-		return p ? 0 : -1;
-	}
-
-	if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
-	    identity[0] == EAP_AKA_REAUTH_ID_PREFIX) {
-		struct eap_sim_reauth *r =
-			eap_sim_db_get_reauth(data, identity, identity_len);
-		return r ? 0 : -1;
-	}
-
-	if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
-	    identity[0] != EAP_AKA_PERMANENT_PREFIX) {
-		/* Unknown identity prefix */
-		return -1;
-	}
-
-	/* TODO: Should consider asking HLR/AuC gateway whether this permanent
-	 * identity is known. If it is, EAP-SIM/AKA can skip identity request.
-	 * In case of EAP-AKA, this would reduce number of needed round-trips.
-	 * Ideally, this would be done with one wait, i.e., just request
-	 * authentication data and store it for the next use. This would then
-	 * need to use similar pending-request functionality as the normal
-	 * request for authentication data at later phase.
-	 */
-	return -1;
-}
-
-
 static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
 {
 	char *id, *pos, *end;
@@ -842,8 +965,8 @@
 
 /**
  * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
- * @priv: Private data pointer from eap_sim_db_init()
- * @aka: Using EAP-AKA instead of EAP-SIM
+ * @data: Private data pointer from eap_sim_db_init()
+ * @method: EAP method (SIM/AKA/AKA')
  * Returns: Next pseudonym (allocated string) or %NULL on failure
  *
  * This function is used to generate a pseudonym for EAP-SIM. The returned
@@ -851,18 +974,31 @@
  * with eap_sim_db_add_pseudonym() once the authentication has been completed
  * successfully. Caller is responsible for freeing the returned buffer.
  */
-char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
+char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data,
+				     enum eap_sim_db_method method)
 {
-	struct eap_sim_db_data *data = priv;
-	return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX :
-				   EAP_SIM_PSEUDONYM_PREFIX);
+	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
+
+	switch (method) {
+	case EAP_SIM_DB_SIM:
+		prefix = EAP_SIM_PSEUDONYM_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA:
+		prefix = EAP_AKA_PSEUDONYM_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA_PRIME:
+		prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX;
+		break;
+	}
+
+	return eap_sim_db_get_next(data, prefix);
 }
 
 
 /**
  * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
- * @priv: Private data pointer from eap_sim_db_init()
- * @aka: Using EAP-AKA instead of EAP-SIM
+ * @data: Private data pointer from eap_sim_db_init()
+ * @method: EAP method (SIM/AKA/AKA')
  * Returns: Next reauth_id (allocated string) or %NULL on failure
  *
  * This function is used to generate a fast re-authentication identity for
@@ -871,19 +1007,31 @@
  * has been completed successfully. Caller is responsible for freeing the
  * returned buffer.
  */
-char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
+char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data,
+				     enum eap_sim_db_method method)
 {
-	struct eap_sim_db_data *data = priv;
-	return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX :
-				   EAP_SIM_REAUTH_ID_PREFIX);
+	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
+
+	switch (method) {
+	case EAP_SIM_DB_SIM:
+		prefix = EAP_SIM_REAUTH_ID_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA:
+		prefix = EAP_AKA_REAUTH_ID_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA_PRIME:
+		prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX;
+		break;
+	}
+
+	return eap_sim_db_get_next(data, prefix);
 }
 
 
 /**
  * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
- * @identity_len: Length of identity
+ * @data: Private data pointer from eap_sim_db_init()
+ * @permanent: Permanent username
  * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
  * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
  * free it.
@@ -892,20 +1040,22 @@
  * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
  * responsible of freeing pseudonym buffer once it is not needed anymore.
  */
-int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
-			     size_t identity_len, char *pseudonym)
+int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data,
+			     const char *permanent, char *pseudonym)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_pseudonym *p;
-	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
-			  identity, identity_len);
-	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent "
+		   "username '%s'", pseudonym, permanent);
 
 	/* TODO: could store last two pseudonyms */
-	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
-	if (p == NULL)
-		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
-
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_add_pseudonym(data, permanent, pseudonym);
+#endif /* CONFIG_SQLITE */
+	for (p = data->pseudonyms; p; p = p->next) {
+		if (os_strcmp(permanent, p->permanent) == 0)
+			break;
+	}
 	if (p) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
 			   "pseudonym: %s", p->pseudonym);
@@ -921,14 +1071,12 @@
 	}
 
 	p->next = data->pseudonyms;
-	p->identity = os_malloc(identity_len);
-	if (p->identity == NULL) {
+	p->permanent = os_strdup(permanent);
+	if (p->permanent == NULL) {
 		os_free(p);
 		os_free(pseudonym);
 		return -1;
 	}
-	os_memcpy(p->identity, identity, identity_len);
-	p->identity_len = identity_len;
 	p->pseudonym = pseudonym;
 	data->pseudonyms = p;
 
@@ -938,18 +1086,16 @@
 
 
 static struct eap_sim_reauth *
-eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
-			   size_t identity_len, char *reauth_id, u16 counter)
+eap_sim_db_add_reauth_data(struct eap_sim_db_data *data,
+			   const char *permanent,
+			   char *reauth_id, u16 counter)
 {
 	struct eap_sim_reauth *r;
 
-	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
-			  identity, identity_len);
-	wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
-
-	r = eap_sim_db_get_reauth(data, identity, identity_len);
-	if (r == NULL)
-		r = eap_sim_db_get_reauth_id(data, identity, identity_len);
+	for (r = data->reauths; r; r = r->next) {
+		if (os_strcmp(r->permanent, permanent) == 0)
+			break;
+	}
 
 	if (r) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
@@ -964,14 +1110,12 @@
 		}
 
 		r->next = data->reauths;
-		r->identity = os_malloc(identity_len);
-		if (r->identity == NULL) {
+		r->permanent = os_strdup(permanent);
+		if (r->permanent == NULL) {
 			os_free(r);
 			os_free(reauth_id);
 			return NULL;
 		}
-		os_memcpy(r->identity, identity, identity_len);
-		r->identity_len = identity_len;
 		r->reauth_id = reauth_id;
 		data->reauths = r;
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
@@ -986,7 +1130,7 @@
 /**
  * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
  * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
+ * @permanent: Permanent username
  * @identity_len: Length of identity
  * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
  * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
@@ -999,20 +1143,24 @@
  * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
  * anymore.
  */
-int eap_sim_db_add_reauth(void *priv, const u8 *identity,
-			  size_t identity_len, char *reauth_id, u16 counter,
-			  const u8 *mk)
+int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+			  char *reauth_id, u16 counter, const u8 *mk)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_reauth *r;
 
-	r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
-				       counter);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent "
+		   "identity '%s'", reauth_id, permanent);
+
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_add_reauth(data, permanent, reauth_id, counter, mk,
+				     NULL, NULL, NULL);
+#endif /* CONFIG_SQLITE */
+	r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter);
 	if (r == NULL)
 		return -1;
 
 	os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
-	r->aka_prime = 0;
 
 	return 0;
 }
@@ -1021,9 +1169,8 @@
 #ifdef EAP_SERVER_AKA_PRIME
 /**
  * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
- * @identity_len: Length of identity
+ * @data: Private data pointer from eap_sim_db_init()
+ * @permanent: Permanent username
  * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
  * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
  * free it.
@@ -1037,20 +1184,25 @@
  * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
  * anymore.
  */
-int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
-				size_t identity_len, char *reauth_id,
-				u16 counter, const u8 *k_encr, const u8 *k_aut,
-				const u8 *k_re)
+int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data,
+				const char *permanent, char *reauth_id,
+				u16 counter, const u8 *k_encr,
+				const u8 *k_aut, const u8 *k_re)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_reauth *r;
 
-	r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
-				       counter);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent "
+		   "identity '%s'", reauth_id, permanent);
+
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_add_reauth(data, permanent, reauth_id, counter, NULL,
+				     k_encr, k_aut, k_re);
+#endif /* CONFIG_SQLITE */
+	r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter);
 	if (r == NULL)
 		return -1;
 
-	r->aka_prime = 1;
 	os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
 	os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
 	os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
@@ -1062,66 +1214,75 @@
 
 /**
  * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
- * @identity_len: Length of identity
- * @len: Buffer for length of the returned permanent identity
- * Returns: Pointer to the permanent identity, or %NULL if not found
+ * @data: Private data pointer from eap_sim_db_init()
+ * @pseudonym: Pseudonym username
+ * Returns: Pointer to permanent username or %NULL if not found
  */
-const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
-				    size_t identity_len, size_t *len)
+const char *
+eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_pseudonym *p;
 
-	if (identity == NULL)
-		return NULL;
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_get_pseudonym(data, pseudonym);
+#endif /* CONFIG_SQLITE */
 
-	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
-	if (p == NULL)
-		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
-	if (p == NULL)
-		return NULL;
+	p = data->pseudonyms;
+	while (p) {
+		if (os_strcmp(p->pseudonym, pseudonym) == 0)
+			return p->permanent;
+		p = p->next;
+	}
 
-	*len = p->identity_len;
-	return p->identity;
+	return NULL;
 }
 
 
 /**
  * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity, pseudonym, or
- * reauth_id)
- * @identity_len: Length of identity
+ * @data: Private data pointer from eap_sim_db_init()
+ * @reauth_id: Fast re-authentication username
  * Returns: Pointer to the re-auth entry, or %NULL if not found
  */
 struct eap_sim_reauth *
-eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
-			    size_t identity_len)
+eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data,
+			    const char *reauth_id)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_reauth *r;
 
-	if (identity == NULL)
-		return NULL;
-	r = eap_sim_db_get_reauth(data, identity, identity_len);
-	if (r == NULL)
-		r = eap_sim_db_get_reauth_id(data, identity, identity_len);
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db)
+		return db_get_reauth(data, reauth_id);
+#endif /* CONFIG_SQLITE */
+
+	r = data->reauths;
+	while (r) {
+		if (os_strcmp(r->reauth_id, reauth_id) == 0)
+			break;
+		r = r->next;
+	}
+
 	return r;
 }
 
 
 /**
  * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
- * @priv: Private data pointer from eap_sim_db_init()
+ * @data: Private data pointer from eap_sim_db_init()
  * @reauth: Pointer to re-authentication entry from
  * eap_sim_db_get_reauth_entry()
  */
-void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
+void eap_sim_db_remove_reauth(struct eap_sim_db_data *data,
+			      struct eap_sim_reauth *reauth)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_reauth *r, *prev = NULL;
+#ifdef CONFIG_SQLITE
+	if (data->sqlite_db) {
+		db_remove_reauth(data, reauth);
+		return;
+	}
+#endif /* CONFIG_SQLITE */
 	r = data->reauths;
 	while (r) {
 		if (r == reauth) {
@@ -1140,9 +1301,8 @@
 
 /**
  * eap_sim_db_get_aka_auth - Get AKA authentication values
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username (prefix | IMSI)
  * @_rand: Buffer for RAND value
  * @autn: Buffer for AUTN value
  * @ik: Buffer for IK value
@@ -1155,9 +1315,6 @@
  * case, the callback function registered with eap_sim_db_init() will be
  * called once the results become available.
  *
- * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
- * ASCII format.
- *
  * When using an external server for AKA authentication, this function can
  * always start a request and return EAP_SIM_DB_PENDING immediately if
  * authentication triplets are not available. Once the authentication data are
@@ -1166,40 +1323,29 @@
  * eap_sim_db_get_aka_auth() function will then be called again and the newly
  * received triplets will then be given to the caller.
  */
-int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
-			    size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
-			    u8 *ck, u8 *res, size_t *res_len,
-			    void *cb_session_ctx)
+int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
+			    u8 *_rand, u8 *autn, u8 *ik, u8 *ck,
+			    u8 *res, size_t *res_len, void *cb_session_ctx)
 {
-	struct eap_sim_db_data *data = priv;
 	struct eap_sim_db_pending *entry;
 	int len;
-	size_t i;
 	char msg[40];
+	const char *imsi;
+	size_t imsi_len;
 
-	if (identity_len < 2 || identity == NULL ||
-	    identity[0] != EAP_AKA_PERMANENT_PREFIX) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
+	if (username == NULL ||
+	    (username[0] != EAP_AKA_PERMANENT_PREFIX &&
+	     username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+	    username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+			   username);
 		return EAP_SIM_DB_FAILURE;
 	}
-	identity++;
-	identity_len--;
-	for (i = 0; i < identity_len; i++) {
-		if (identity[i] == '@') {
-			identity_len = i;
-			break;
-		}
-	}
-	if (identity_len + 1 > sizeof(entry->imsi)) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
-		return EAP_SIM_DB_FAILURE;
-	}
-	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
-			  identity, identity_len);
+	imsi = username + 1;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'",
+		   imsi);
 
-	entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
+	entry = eap_sim_db_get_pending(data, imsi, 1);
 	if (entry) {
 		if (entry->state == FAILURE) {
 			os_free(entry);
@@ -1230,14 +1376,15 @@
 			return EAP_SIM_DB_FAILURE;
 	}
 
+	imsi_len = os_strlen(imsi);
 	len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
-	if (len < 0 || len + identity_len >= sizeof(msg))
+	if (len < 0 || len + imsi_len >= sizeof(msg))
 		return EAP_SIM_DB_FAILURE;
-	os_memcpy(msg + len, identity, identity_len);
-	len += identity_len;
+	os_memcpy(msg + len, imsi, imsi_len);
+	len += imsi_len;
 
-	wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
-		    "data for IMSI", identity, identity_len);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
+		    "data for IMSI '%s'", imsi);
 	if (eap_sim_db_send(data, msg, len) < 0)
 		return EAP_SIM_DB_FAILURE;
 
@@ -1247,8 +1394,7 @@
 
 	os_get_time(&entry->timestamp);
 	entry->aka = 1;
-	os_memcpy(entry->imsi, identity, identity_len);
-	entry->imsi_len = identity_len;
+	os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
 	entry->cb_session_ctx = cb_session_ctx;
 	entry->state = PENDING;
 	eap_sim_db_add_pending(data, entry);
@@ -1260,9 +1406,8 @@
 
 /**
  * eap_sim_db_resynchronize - Resynchronize AKA AUTN
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username
  * @auts: AUTS value from the peer
  * @_rand: RAND value used in the rejected message
  * Returns: 0 on success, -1 on failure
@@ -1273,42 +1418,35 @@
  * eap_sim_db_get_aka_auth() will be called again to to fetch updated
  * RAND/AUTN values for the next challenge.
  */
-int eap_sim_db_resynchronize(void *priv, const u8 *identity,
-			     size_t identity_len, const u8 *auts,
-			     const u8 *_rand)
+int eap_sim_db_resynchronize(struct eap_sim_db_data *data,
+			     const char *username,
+			     const u8 *auts, const u8 *_rand)
 {
-	struct eap_sim_db_data *data = priv;
-	size_t i;
+	const char *imsi;
+	size_t imsi_len;
 
-	if (identity_len < 2 || identity == NULL ||
-	    identity[0] != EAP_AKA_PERMANENT_PREFIX) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
+	if (username == NULL ||
+	    (username[0] != EAP_AKA_PERMANENT_PREFIX &&
+	     username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+	    username[1] == '\0' || os_strlen(username) > 20) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+			   username);
 		return -1;
 	}
-	identity++;
-	identity_len--;
-	for (i = 0; i < identity_len; i++) {
-		if (identity[i] == '@') {
-			identity_len = i;
-			break;
-		}
-	}
-	if (identity_len > 20) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
-				  identity, identity_len);
-		return -1;
-	}
+	imsi = username + 1;
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'",
+		   imsi);
 
 	if (data->sock >= 0) {
 		char msg[100];
 		int len, ret;
 
+		imsi_len = os_strlen(imsi);
 		len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
-		if (len < 0 || len + identity_len >= sizeof(msg))
+		if (len < 0 || len + imsi_len >= sizeof(msg))
 			return -1;
-		os_memcpy(msg + len, identity, identity_len);
-		len += identity_len;
+		os_memcpy(msg + len, imsi, imsi_len);
+		len += imsi_len;
 
 		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
 		if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
@@ -1322,11 +1460,42 @@
 		len += ret;
 		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
 					_rand, EAP_AKA_RAND_LEN);
-		wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
-			    "IMSI", identity, identity_len);
+		wpa_printf(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
+			   "IMSI '%s'", imsi);
 		if (eap_sim_db_send(data, msg, len) < 0)
 			return -1;
 	}
 
 	return 0;
 }
+
+
+/**
+ * sim_get_username - Extract username from SIM identity
+ * @identity: Identity
+ * @identity_len: Identity length
+ * Returns: Allocated buffer with the username part of the identity
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+char * sim_get_username(const u8 *identity, size_t identity_len)
+{
+	char *username;
+	size_t pos;
+
+	if (identity == NULL)
+		return NULL;
+
+	for (pos = 0; pos < identity_len; pos++) {
+		if (identity[pos] == '@' || identity[pos] == '\0')
+			break;
+	}
+
+	username = os_malloc(pos + 1);
+	if (username == NULL)
+		return NULL;
+	os_memcpy(username, identity, pos);
+	username[pos] = '\0';
+
+	return username;
+}
diff --git a/src/eap_server/eap_sim_db.h b/src/eap_server/eap_sim_db.h
index abe185e..53a1a7c 100644
--- a/src/eap_server/eap_sim_db.h
+++ b/src/eap_server/eap_sim_db.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP-SIM database/authenticator gateway
- * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2008, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,49 +18,57 @@
 #define EAP_AKA_PERMANENT_PREFIX '0'
 #define EAP_AKA_PSEUDONYM_PREFIX '2'
 #define EAP_AKA_REAUTH_ID_PREFIX '4'
+#define EAP_AKA_PRIME_PERMANENT_PREFIX '6'
+#define EAP_AKA_PRIME_PSEUDONYM_PREFIX '7'
+#define EAP_AKA_PRIME_REAUTH_ID_PREFIX '8'
 
-void * eap_sim_db_init(const char *config,
-		       void (*get_complete_cb)(void *ctx, void *session_ctx),
-		       void *ctx);
+enum eap_sim_db_method {
+	EAP_SIM_DB_SIM,
+	EAP_SIM_DB_AKA,
+	EAP_SIM_DB_AKA_PRIME
+};
+
+struct eap_sim_db_data;
+
+struct eap_sim_db_data *
+eap_sim_db_init(const char *config,
+		void (*get_complete_cb)(void *ctx, void *session_ctx),
+		void *ctx);
 
 void eap_sim_db_deinit(void *priv);
 
-int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
-				size_t identity_len, int max_chal,
+int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data,
+				const char *username, int max_chal,
 				u8 *_rand, u8 *kc, u8 *sres,
 				void *cb_session_ctx);
 
 #define EAP_SIM_DB_FAILURE -1
 #define EAP_SIM_DB_PENDING -2
 
-int eap_sim_db_identity_known(void *priv, const u8 *identity,
-			      size_t identity_len);
+char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data,
+				     enum eap_sim_db_method method);
 
-char * eap_sim_db_get_next_pseudonym(void *priv, int aka);
+char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data,
+				     enum eap_sim_db_method method);
 
-char * eap_sim_db_get_next_reauth_id(void *priv, int aka);
+int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data,
+			     const char *permanent, char *pseudonym);
 
-int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
-			     size_t identity_len, char *pseudonym);
+int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+			  char *reauth_id, u16 counter, const u8 *mk);
+int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data,
+				const char *permanent,
+				char *reauth_id, u16 counter, const u8 *k_encr,
+				const u8 *k_aut, const u8 *k_re);
 
-int eap_sim_db_add_reauth(void *priv, const u8 *identity,
-			  size_t identity_len, char *reauth_id, u16 counter,
-			  const u8 *mk);
-int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
-				size_t identity_len, char *reauth_id,
-				u16 counter, const u8 *k_encr, const u8 *k_aut,
-				const u8 *k_re);
-
-const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
-				    size_t identity_len, size_t *len);
+const char * eap_sim_db_get_permanent(struct eap_sim_db_data *data,
+				      const char *pseudonym);
 
 struct eap_sim_reauth {
 	struct eap_sim_reauth *next;
-	u8 *identity;
-	size_t identity_len;
-	char *reauth_id;
+	char *permanent; /* Permanent username */
+	char *reauth_id; /* Fast re-authentication username */
 	u16 counter;
-	int aka_prime;
 	u8 mk[EAP_SIM_MK_LEN];
 	u8 k_encr[EAP_SIM_K_ENCR_LEN];
 	u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
@@ -68,18 +76,20 @@
 };
 
 struct eap_sim_reauth *
-eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
-			    size_t identity_len);
+eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data,
+			    const char *reauth_id);
 
-void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth);
+void eap_sim_db_remove_reauth(struct eap_sim_db_data *data,
+			      struct eap_sim_reauth *reauth);
 
-int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
-			    size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
-			    u8 *ck, u8 *res, size_t *res_len,
-			    void *cb_session_ctx);
+int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
+			    u8 *_rand, u8 *autn, u8 *ik, u8 *ck,
+			    u8 *res, size_t *res_len, void *cb_session_ctx);
 
-int eap_sim_db_resynchronize(void *priv, const u8 *identity,
-			     size_t identity_len, const u8 *auts,
+int eap_sim_db_resynchronize(struct eap_sim_db_data *data,
+			     const char *username, const u8 *auts,
 			     const u8 *_rand);
 
+char * sim_get_username(const u8 *identity, size_t identity_len);
+
 #endif /* EAP_SIM_DB_H */
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index 31fe503..11f5827 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -62,7 +62,12 @@
  /* could be up to 128 bytes, but only the first 64 bytes are used */
 #define EAP_TLS_KEY_LEN 64
 
+/* dummy type used as a flag for UNAUTH-TLS */
+#define EAP_UNAUTH_TLS_TYPE 255
 
+
+struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+				  u8 code, u8 identifier);
 int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
 			    int verify_peer);
 void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index 8d6d18f..c3ccb46 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -757,7 +757,8 @@
 struct eapol_state_machine *
 eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
 		 int flags, const struct wpabuf *assoc_wps_ie,
-		 const struct wpabuf *assoc_p2p_ie, void *sta_ctx)
+		 const struct wpabuf *assoc_p2p_ie, void *sta_ctx,
+		 const char *identity, const char *radius_cui)
 {
 	struct eapol_state_machine *sm;
 	struct eap_config eap_conf;
@@ -838,6 +839,15 @@
 
 	eapol_auth_initialize(sm);
 
+	if (identity) {
+		sm->identity = (u8 *) os_strdup(identity);
+		if (sm->identity)
+			sm->identity_len = os_strlen(identity);
+	}
+	if (radius_cui)
+		sm->radius_cui = wpabuf_alloc_copy(radius_cui,
+						   os_strlen(radius_cui));
+
 	return sm;
 }
 
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index 5e8ff43..b50bbdd 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -77,7 +77,8 @@
 struct eapol_state_machine *
 eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
 		 int flags, const struct wpabuf *assoc_wps_ie,
-		 const struct wpabuf *assoc_p2p_ie, void *sta_ctx);
+		 const struct wpabuf *assoc_p2p_ie, void *sta_ctx,
+		 const char *identity, const char *radius_cui);
 void eapol_auth_free(struct eapol_state_machine *sm);
 void eapol_auth_step(struct eapol_state_machine *sm);
 void eapol_auth_dump_state(FILE *f, const char *prefix,
diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h
index d0b7641..d7f893a 100644
--- a/src/eapol_auth/eapol_auth_sm_i.h
+++ b/src/eapol_auth/eapol_auth_sm_i.h
@@ -157,6 +157,7 @@
 			      * Authentication server */
 	u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */
 	struct radius_class_data radius_class;
+	struct wpabuf *radius_cui; /* Chargeable-User-Identity */
 
 	/* Keys for encrypting and signing EAPOL-Key frames */
 	u8 *eapol_key_sign;
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index c83709f..851cf49 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -1,6 +1,6 @@
 /*
  * EAPOL supplicant state machines
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -139,46 +139,6 @@
 };
 
 
-#define IEEE8021X_REPLAY_COUNTER_LEN 8
-#define IEEE8021X_KEY_SIGN_LEN 16
-#define IEEE8021X_KEY_IV_LEN 16
-
-#define IEEE8021X_KEY_INDEX_FLAG 0x80
-#define IEEE8021X_KEY_INDEX_MASK 0x03
-
-#ifdef _MSC_VER
-#pragma pack(push, 1)
-#endif /* _MSC_VER */
-
-struct ieee802_1x_eapol_key {
-	u8 type;
-	/* Note: key_length is unaligned */
-	u8 key_length[2];
-	/* does not repeat within the life of the keying material used to
-	 * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
-	u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
-	u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
-	u8 key_index; /* key flag in the most significant bit:
-		       * 0 = broadcast (default key),
-		       * 1 = unicast (key mapping key); key index is in the
-		       * 7 least significant bits */
-	/* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
-	 * the key */
-	u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
-
-	/* followed by key: if packet body length = 44 + key length, then the
-	 * key field (of key_length bytes) contains the key in encrypted form;
-	 * if packet body length = 44, key field is absent and key_length
-	 * represents the number of least significant octets from
-	 * MS-MPPE-Send-Key attribute to be used as the keying material;
-	 * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
-} STRUCT_PACKED;
-
-#ifdef _MSC_VER
-#pragma pack(pop)
-#endif /* _MSC_VER */
-
-
 static void eapol_sm_txLogoff(struct eapol_sm *sm);
 static void eapol_sm_txStart(struct eapol_sm *sm);
 static void eapol_sm_processKey(struct eapol_sm *sm);
@@ -664,6 +624,7 @@
 
 static void eapol_sm_processKey(struct eapol_sm *sm)
 {
+#ifndef CONFIG_FIPS
 	struct ieee802_1x_hdr *hdr;
 	struct ieee802_1x_eapol_key *key;
 	struct eap_key_data keydata;
@@ -671,6 +632,7 @@
 	u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
 	int key_len, res, sign_key_len, encr_key_len;
 	u16 rx_key_length;
+	size_t plen;
 
 	wpa_printf(MSG_DEBUG, "EAPOL: processKey");
 	if (sm->last_rx_key == NULL)
@@ -683,9 +645,12 @@
 		return;
 	}
 
+	if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key))
+		return;
 	hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
 	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
-	if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) {
+	plen = be_to_host16(hdr->length);
+	if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) {
 		wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
 		return;
 	}
@@ -751,7 +716,7 @@
 	}
 	wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
 
-	key_len = be_to_host16(hdr->length) - sizeof(*key);
+	key_len = plen - sizeof(*key);
 	if (key_len > 32 || rx_key_length > 32) {
 		wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
 			   key_len ? key_len : rx_key_length);
@@ -822,6 +787,7 @@
 				sm->ctx->eapol_done_cb(sm->ctx->ctx);
 		}
 	}
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -1778,7 +1744,8 @@
 	switch (variable) {
 	case EAPOL_idleWhile:
 		sm->idleWhile = value;
-		eapol_enable_timer_tick(sm);
+		if (sm->idleWhile > 0)
+			eapol_enable_timer_tick(sm);
 		break;
 	}
 }
@@ -1847,6 +1814,26 @@
 				 cert_hash, cert);
 }
 
+
+static void eapol_sm_notify_status(void *ctx, const char *status,
+				   const char *parameter)
+{
+	struct eapol_sm *sm = ctx;
+
+	if (sm->ctx->status_cb)
+		sm->ctx->status_cb(sm->ctx->ctx, status, parameter);
+}
+
+
+static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+	struct eapol_sm *sm = ctx;
+
+	if (sm->ctx->set_anon_id)
+		sm->ctx->set_anon_id(sm->ctx->ctx, id, len);
+}
+
+
 static struct eapol_callbacks eapol_cb =
 {
 	eapol_sm_get_config,
@@ -1859,7 +1846,9 @@
 	eapol_sm_get_config_blob,
 	eapol_sm_notify_pending,
 	eapol_sm_eap_param_needed,
-	eapol_sm_notify_cert
+	eapol_sm_notify_cert,
+	eapol_sm_notify_status,
+	eapol_sm_set_anon_id
 };
 
 
@@ -1934,3 +1923,19 @@
 	os_free(sm->ctx);
 	os_free(sm);
 }
+
+
+void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+			     struct ext_password_data *ext)
+{
+	if (sm && sm->eap)
+		eap_sm_set_ext_pw_ctx(sm->eap, ext);
+}
+
+
+int eapol_sm_failed(struct eapol_sm *sm)
+{
+	if (sm == NULL)
+		return 0;
+	return !sm->eapSuccess && sm->eapFail;
+}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index 3260c61..c4b87da 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -1,6 +1,6 @@
 /*
  * EAPOL supplicant state machines
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -230,10 +230,28 @@
 	 * cert_in_cb - Include server certificates in callback
 	 */
 	int cert_in_cb;
+
+	/**
+	 * status_cb - Notification of a change in EAP status
+	 * @ctx: Callback context (ctx)
+	 * @status: Step in the process of EAP authentication
+	 * @parameter: Step-specific parameter, e.g., EAP method name
+	 */
+	void (*status_cb)(void *ctx, const char *status,
+			  const char *parameter);
+
+	/**
+	 * set_anon_id - Set or add anonymous identity
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @id: Anonymous identity (e.g., EAP-SIM pseudonym)
+	 * @len: Length of anonymous identity in octets
+	 */
+	void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
 };
 
 
 struct eap_peer_config;
+struct ext_password_data;
 
 #ifdef IEEE8021X_EAPOL
 struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx);
@@ -266,6 +284,9 @@
 void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm);
 void eapol_sm_invalidate_cached_session(struct eapol_sm *sm);
 const char * eapol_sm_get_method_name(struct eapol_sm *sm);
+void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+			     struct ext_password_data *ext);
+int eapol_sm_failed(struct eapol_sm *sm);
 #else /* IEEE8021X_EAPOL */
 static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
 {
@@ -357,6 +378,14 @@
 {
 	return NULL;
 }
+static inline void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+					   struct ext_password_data *ext)
+{
+}
+static inline int eapol_sm_failed(struct eapol_sm *sm)
+{
+	return 0;
+}
 #endif /* IEEE8021X_EAPOL */
 
 #endif /* EAPOL_SUPP_SM_H */
diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c
index e24277c..2e9a04c 100644
--- a/src/l2_packet/l2_packet_freebsd.c
+++ b/src/l2_packet/l2_packet_freebsd.c
@@ -3,14 +3,8 @@
  * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2005, Sam Leffler <sam@errno.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c
index aacd76b..6b117ca 100644
--- a/src/l2_packet/l2_packet_privsep.c
+++ b/src/l2_packet/l2_packet_privsep.c
@@ -173,7 +173,7 @@
 	addr.sun_family = AF_UNIX;
 	os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path));
 	if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("bind(PF_UNIX)");
+		perror("l2-pkt-privsep: bind(PF_UNIX)");
 		goto fail;
 	}
 
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index aeeaccb..f038812 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -119,7 +119,7 @@
 			continue;
 #endif
 
-		wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR, "P2P: Expiring old peer "
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer "
 			"entry " MACSTR, MAC2STR(dev->info.p2p_device_addr));
 #ifdef ANDROID_P2P
 		/* SD_FAIR_POLICY: Update the current sd_dev_list pointer to next device */
@@ -172,6 +172,8 @@
 		return "INVITE_LISTEN";
 	case P2P_SEARCH_WHEN_READY:
 		return "SEARCH_WHEN_READY";
+	case P2P_CONTINUE_SEARCH_WHEN_READY:
+		return "CONTINUE_SEARCH_WHEN_READY";
 	default:
 		return "?";
 	}
@@ -193,14 +195,14 @@
 }
 
 
-void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *iface_addr)
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr)
 {
 	struct p2p_device *dev = NULL;
 
-	if (!iface_addr || !p2p)
+	if (!addr || !p2p)
 		return;
 
-	dev = p2p_get_device_interface(p2p, iface_addr);
+	dev = p2p_get_device(p2p, addr);
 	if (dev)
 		dev->wps_prov_info = 0;
 }
@@ -315,7 +317,7 @@
 	p2p->pending_listen_usec = (timeout % 1000) * 1000;
 
 	if (p2p->p2p_scan_running) {
-		if (p2p->start_after_scan == P2P_AFTER_SCAN_NOTHING) {
+		if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) {
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 				"P2P: p2p_scan running - connect is already "
 				"pending - skip listen");
@@ -484,13 +486,25 @@
 			continue; /* ignore our own entry */
 		dev = p2p_get_device(p2p, cli->p2p_device_addr);
 		if (dev) {
-			/*
-			 * Update information only if we have not received this
-			 * directly from the client.
-			 */
 			if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY |
-					  P2P_DEV_PROBE_REQ_ONLY))
+					  P2P_DEV_PROBE_REQ_ONLY)) {
+				/*
+				 * Update information since we have not
+				 * received this directly from the client.
+				 */
 				p2p_copy_client_info(dev, cli);
+			} else {
+				/*
+				 * Need to update P2P Client Discoverability
+				 * flag since it is valid only in P2P Group
+				 * Info attribute.
+				 */
+				dev->info.dev_capab &=
+					~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+				dev->info.dev_capab |=
+					cli->dev_capab &
+					P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+			}
 			if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
 				dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
 			}
@@ -573,7 +587,13 @@
 	}
 
 	if (msg->capability) {
-		dev->info.dev_capab = msg->capability[0];
+		/*
+		 * P2P Client Discoverability bit is reserved in all frames
+		 * that use this function, so do not change its value here.
+		 */
+		dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+		dev->info.dev_capab |= msg->capability[0] &
+			~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 		dev->info.group_capab = msg->capability[1];
 	}
 
@@ -582,6 +602,7 @@
 		dev->ext_listen_interval =
 			WPA_GET_LE16(msg->ext_listen_timing + 2);
 	}
+
 	if (!probe_req) {
 		dev->info.config_methods = msg->config_methods ?
 			msg->config_methods : msg->wps_config_methods;
@@ -590,7 +611,7 @@
 
 
 /**
- * p2p_add_device - Add peer entries based on scan results
+ * p2p_add_device - Add peer entries based on scan results or P2P frames
  * @p2p: P2P module context from p2p_init()
  * @addr: Source address of Beacon or Probe Response frame (may be either
  *	P2P Device Address or P2P Interface Address)
@@ -598,6 +619,7 @@
  * @freq: Frequency on which the Beacon or Probe Response frame was received
  * @ies: IEs from the Beacon or Probe Response frame
  * @ies_len: Length of ies buffer in octets
+ * @scan_res: Whether this was based on scan results
  * Returns: 0 on success, -1 on failure
  *
  * If the scan result is for a GO, the clients in the group will also be added
@@ -606,7 +628,7 @@
  * Info attributes.
  */
 int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
-		   const u8 *ies, size_t ies_len)
+		   const u8 *ies, size_t ies_len, int scan_res)
 {
 	struct p2p_device *dev;
 	struct p2p_message msg;
@@ -675,16 +697,18 @@
 		}
 	}
 
-	if (dev->listen_freq && dev->listen_freq != freq) {
+	if (dev->listen_freq && dev->listen_freq != freq && scan_res) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Update Listen frequency based on scan "
 			"results (" MACSTR " %d -> %d MHz (DS param %d)",
 			MAC2STR(dev->info.p2p_device_addr), dev->listen_freq,
 			freq, msg.ds_params ? *msg.ds_params : -1);
 	}
-	dev->listen_freq = freq;
-	if (msg.group_info)
-		dev->oper_freq = freq;
+	if (scan_res) {
+		dev->listen_freq = freq;
+		if (msg.group_info)
+			dev->oper_freq = freq;
+	}
 	dev->info.level = level;
 
 	p2p_copy_wps_info(dev, 0, &msg);
@@ -703,8 +727,15 @@
 			break;
 	}
 
-	p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info,
-			      msg.group_info_len);
+	if (msg.wfd_subelems) {
+		wpabuf_free(dev->info.wfd_subelems);
+		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+	}
+
+	if (scan_res) {
+		p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq,
+				      msg.group_info, msg.group_info_len);
+	}
 
 	p2p_parse_free(&msg);
 
@@ -758,6 +789,8 @@
 		dev->info.wps_vendor_ext[i] = NULL;
 	}
 
+	wpabuf_free(dev->info.wfd_subelems);
+
 	os_free(dev);
 }
 
@@ -821,6 +854,8 @@
 {
 	int freq = 0;
 	enum p2p_scan_type type;
+	u16 pw_id = DEV_PW_DEFAULT;
+	int res;
 
 	if (p2p->drv_in_listen) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still "
@@ -830,30 +865,8 @@
 	}
 	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
 
-	if (p2p->go_neg_peer) {
-		/*
-		 * Only scan the known listen frequency of the peer
-		 * during GO Negotiation start.
-		 */
-		freq = p2p->go_neg_peer->listen_freq;
-		if (freq <= 0)
-			freq = p2p->go_neg_peer->oper_freq;
-		type = P2P_SCAN_SPECIFIC;
-		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
-			"for freq %u (GO Neg)", freq);
-	} else if (p2p->invite_peer) {
-		/*
-		 * Only scan the known listen frequency of the peer
-		 * during Invite start.
-		 */
-		freq = p2p->invite_peer->listen_freq;
-		if (freq <= 0)
-			freq = p2p->invite_peer->oper_freq;
-		type = P2P_SCAN_SPECIFIC;
-		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
-			"for freq %u (Invite)", freq);
-	} else if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
-		   (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+	if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
+	    (freq = p2p_get_next_prog_freq(p2p)) > 0) {
 		type = P2P_SCAN_SOCIAL_PLUS_ONE;
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
 			"(+ freq %u)", freq);
@@ -862,12 +875,18 @@
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search");
 	}
 
-	if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
-			       p2p->num_req_dev_types, p2p->req_dev_types,
-			       p2p->find_dev_id)) {
+	res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
+				 p2p->num_req_dev_types, p2p->req_dev_types,
+				 p2p->find_dev_id, pw_id);
+	if (res < 0) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Scan request failed");
 		p2p_continue_find(p2p);
+	} else if (res == 1) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start "
+			"p2p_scan at this point - will try again after "
+			"previous scan completes");
+		p2p_set_state(p2p, P2P_CONTINUE_SEARCH_WHEN_READY);
 	} else {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan");
 		p2p->p2p_scan_running = 1;
@@ -972,7 +991,7 @@
 int p2p_find(struct p2p_data *p2p, unsigned int timeout,
 	     enum p2p_discovery_type type,
 	     unsigned int num_req_dev_types, const u8 *req_dev_types,
-	     const u8 *dev_id)
+	     const u8 *dev_id, unsigned int search_delay)
 {
 	int res;
 
@@ -1006,6 +1025,8 @@
 	p2p->find_type = type;
 	p2p_device_clear_reported(p2p);
 	p2p_set_state(p2p, P2P_SEARCH);
+	p2p->search_delay = search_delay;
+	p2p->in_search_delay = 0;
 	eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
 	p2p->last_p2p_find_timeout = timeout;
 	if (timeout)
@@ -1016,12 +1037,14 @@
 	case P2P_FIND_PROGRESSIVE:
 		res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
 					 p2p->num_req_dev_types,
-					 p2p->req_dev_types, dev_id);
+					 p2p->req_dev_types, dev_id,
+					 DEV_PW_DEFAULT);
 		break;
 	case P2P_FIND_ONLY_SOCIAL:
 		res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0,
 					 p2p->num_req_dev_types,
-					 p2p->req_dev_types, dev_id);
+					 p2p->req_dev_types, dev_id,
+					 DEV_PW_DEFAULT);
 		break;
 	default:
 		return -1;
@@ -1065,13 +1088,18 @@
 
 int p2p_other_scan_completed(struct p2p_data *p2p)
 {
+	if (p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) {
+		p2p_set_state(p2p, P2P_SEARCH);
+		p2p_search(p2p);
+		return 1;
+	}
 	if (p2p->state != P2P_SEARCH_WHEN_READY)
 		return 0;
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find "
 		"now that previous scan was completed");
 	if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type,
 		     p2p->num_req_dev_types, p2p->req_dev_types,
-		     p2p->find_dev_id) < 0)
+		     p2p->find_dev_id, p2p->search_delay) < 0)
 		return 0;
 	return 1;
 }
@@ -1090,11 +1118,21 @@
 	p2p->go_neg_peer = NULL;
 	p2p->sd_peer = NULL;
 	p2p->invite_peer = NULL;
+	p2p_stop_listen_for_freq(p2p, freq);
+}
+
+
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
+{
 	if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen "
 			"since we are on correct channel for response");
 		return;
 	}
+	if (p2p->in_listen) {
+		p2p->in_listen = 0;
+		p2p_clear_timeout(p2p);
+	}
 	if (p2p->drv_in_listen) {
 		/*
 		 * The driver may not deliver callback to p2p_listen_end()
@@ -1136,10 +1174,32 @@
 		}
 		p2p->op_reg_class = op_reg_class;
 		p2p->op_channel = op_channel;
+#ifndef ANDROID_P2P
 		p2p->channels.reg_classes = 1;
 		p2p->channels.reg_class[0].channels = 1;
 		p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
 		p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
+#else
+		if(p2p->cfg->p2p_concurrency == P2P_MULTI_CHANNEL_CONCURRENT) {
+			/* We we are requesting for a preferred channel. But since
+			 * are multichannel concurrent, we have to poplulate the
+			 * p2p_channels with list of channels that we support.
+			 */
+#ifdef ANDROID_P2P
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "Full channel list");
+#endif
+			os_memcpy(&p2p->channels, &p2p->cfg->channels,
+				sizeof(struct p2p_channels));
+		} else {
+#ifdef ANDROID_P2P
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "Single channel list %d", p2p->op_channel);
+#endif
+			p2p->channels.reg_classes = 1;
+			p2p->channels.reg_class[0].channels = 1;
+			p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
+			p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
+		}
+#endif
 	} else {
 		u8 op_reg_class, op_channel;
 
@@ -1217,21 +1277,22 @@
 int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
 		enum p2p_wps_method wps_method,
 		int go_intent, const u8 *own_interface_addr,
-		unsigned int force_freq, int persistent_group)
+		unsigned int force_freq, int persistent_group,
+		const u8 *force_ssid, size_t force_ssid_len,
+		int pd_before_go_neg)
 {
 	struct p2p_device *dev;
 
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 		"P2P: Request to start group negotiation - peer=" MACSTR
 		"  GO Intent=%d  Intended Interface Address=" MACSTR
-		" wps_method=%d persistent_group=%d",
+		" wps_method=%d persistent_group=%d pd_before_go_neg=%d force_freq %d",
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-		wps_method, persistent_group);
+		wps_method, persistent_group, pd_before_go_neg, force_freq);
 
 	if (p2p_prepare_channel(p2p, force_freq) < 0)
 		return -1;
 
-	p2p->ssid_set = 0;
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -1264,10 +1325,23 @@
 		 */
 	}
 
+	p2p->ssid_set = 0;
+	if (force_ssid) {
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+				  force_ssid, force_ssid_len);
+		os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+		p2p->ssid_len = force_ssid_len;
+		p2p->ssid_set = 1;
+	}
+
 	dev->flags &= ~P2P_DEV_NOT_YET_READY;
 	dev->flags &= ~P2P_DEV_USER_REJECTED;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+	if (pd_before_go_neg)
+		dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG;
+	else
+		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
 	dev->connect_reqs = 0;
 	dev->go_neg_req_sent = 0;
 	dev->go_state = UNKNOWN_GO;
@@ -1315,7 +1389,8 @@
 int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
 		  enum p2p_wps_method wps_method,
 		  int go_intent, const u8 *own_interface_addr,
-		  unsigned int force_freq, int persistent_group)
+		  unsigned int force_freq, int persistent_group,
+		  const u8 *force_ssid, size_t force_ssid_len)
 {
 	struct p2p_device *dev;
 
@@ -1337,6 +1412,15 @@
 		return -1;
 	}
 
+	p2p->ssid_set = 0;
+	if (force_ssid) {
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+				  force_ssid, force_ssid_len);
+		os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+		p2p->ssid_len = force_ssid_len;
+		p2p->ssid_set = 1;
+	}
+
 	dev->flags &= ~P2P_DEV_NOT_YET_READY;
 	dev->flags &= ~P2P_DEV_USER_REJECTED;
 	dev->go_neg_req_sent = 0;
@@ -1387,6 +1471,11 @@
 		}
 	}
 
+	if (msg->wfd_subelems) {
+		wpabuf_free(dev->info.wfd_subelems);
+		dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems);
+	}
+
 	if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
 		dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -1724,6 +1813,11 @@
 
 	p2p_copy_wps_info(dev, 1, &msg);
 
+	if (msg.wfd_subelems) {
+		wpabuf_free(dev->info.wfd_subelems);
+		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+	}
+
 	p2p_parse_free(&msg);
 
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -1821,16 +1915,34 @@
 {
 	struct wpabuf *buf;
 	u8 *len;
+	int pw_id = -1;
+	size_t extra = 0;
 
-	buf = wpabuf_alloc(1000);
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_probe_resp)
+		extra = wpabuf_len(p2p->wfd_ie_probe_resp);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
 
-	p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1);
+	if (p2p->go_neg_peer) {
+		/* Advertise immediate availability of WPS credential */
+		pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
+	}
+
+	p2p_build_wps_ie(p2p, buf, pw_id, 1);
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_probe_resp)
+		wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp);
+#endif /* CONFIG_WIFI_DISPLAY */
 
 	/* P2P IE */
 	len = p2p_buf_add_ie_hdr(buf);
-	p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
 	if (p2p->ext_listen_interval)
 		p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
 					      p2p->ext_listen_interval);
@@ -1874,9 +1986,9 @@
 }
 
 
-static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr,
-			    const u8 *dst, const u8 *bssid, const u8 *ie,
-			    size_t ie_len)
+static enum p2p_probe_req_status
+p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+		const u8 *bssid, const u8 *ie, size_t ie_len)
 {
 	struct ieee802_11_elems elems;
 	struct wpabuf *buf;
@@ -1886,55 +1998,55 @@
 
 	if (!p2p->in_listen || !p2p->drv_in_listen) {
 		/* not in Listen state - ignore Probe Request */
-		return;
+		return P2P_PREQ_NOT_LISTEN;
 	}
 
 	if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
 	    ParseFailed) {
 		/* Ignore invalid Probe Request frames */
-		return;
+		return P2P_PREQ_MALFORMED;
 	}
 
 	if (elems.p2p == NULL) {
 		/* not a P2P probe - ignore it */
-		return;
+		return P2P_PREQ_NOT_P2P;
 	}
 
 	if (dst && !is_broadcast_ether_addr(dst) &&
 	    os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
 		/* Not sent to the broadcast address or our P2P Device Address
 		 */
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	if (bssid && !is_broadcast_ether_addr(bssid)) {
 		/* Not sent to the Wildcard BSSID */
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN ||
 	    os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) !=
 	    0) {
 		/* not using P2P Wildcard SSID - ignore */
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	if (supp_rates_11b_only(&elems)) {
 		/* Indicates support for 11b rates only */
-		return;
+		return P2P_PREQ_NOT_P2P;
 	}
 
 	os_memset(&msg, 0, sizeof(msg));
 	if (p2p_parse_ies(ie, ie_len, &msg) < 0) {
 		/* Could not parse P2P attributes */
-		return;
+		return P2P_PREQ_NOT_P2P;
 	}
 
 	if (msg.device_id &&
-	    os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN != 0)) {
+	    os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
 		/* Device ID did not match */
 		p2p_parse_free(&msg);
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	/* Check Requested Device Type match */
@@ -1942,12 +2054,14 @@
 	    !p2p_match_dev_type(p2p, msg.wps_attributes)) {
 		/* No match with Requested Device Type */
 		p2p_parse_free(&msg);
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 	p2p_parse_free(&msg);
 
-	if (!p2p->cfg->send_probe_resp)
-		return; /* Response generated elsewhere */
+	if (!p2p->cfg->send_probe_resp) {
+		/* Response generated elsewhere */
+		return P2P_PREQ_NOT_PROCESSED;
+	}
 
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 		"P2P: Reply to P2P Probe Request in Listen state");
@@ -1960,12 +2074,12 @@
 	 */
 	ies = p2p_build_probe_resp_ies(p2p);
 	if (ies == NULL)
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 
 	buf = wpabuf_alloc(200 + wpabuf_len(ies));
 	if (buf == NULL) {
 		wpabuf_free(ies);
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	resp = NULL;
@@ -2008,15 +2122,20 @@
 	p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf);
 
 	wpabuf_free(buf);
+
+	return P2P_PREQ_NOT_PROCESSED;
 }
 
 
-int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
-		     const u8 *bssid, const u8 *ie, size_t ie_len)
+enum p2p_probe_req_status
+p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+		 const u8 *bssid, const u8 *ie, size_t ie_len)
 {
+	enum p2p_probe_req_status res;
+
 	p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
 
-	p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
+	res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
 
 	if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
 	    p2p->go_neg_peer &&
@@ -2027,7 +2146,7 @@
 			"P2P: Found GO Negotiation peer - try to start GO "
 			"negotiation from timeout");
 		eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
-		return 1;
+		return P2P_PREQ_PROCESSED;
 	}
 
 	if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
@@ -2039,10 +2158,10 @@
 			"P2P: Found Invite peer - try to start Invite from "
 			"timeout");
 		eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
-		return 1;
+		return P2P_PREQ_PROCESSED;
 	}
 
-	return 0;
+	return res;
 }
 
 
@@ -2104,20 +2223,31 @@
 	struct p2p_device *peer;
 	size_t tmplen;
 	int res;
+	size_t extra = 0;
 
 	if (!p2p_group)
 		return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie);
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_assoc_req)
+		extra = wpabuf_len(p2p->wfd_ie_assoc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	/*
 	 * (Re)Association Request - P2P IE
 	 * P2P Capability attribute (shall be present)
 	 * Extended Listen Timing (may be present)
 	 * P2P Device Info attribute (shall be present)
 	 */
-	tmp = wpabuf_alloc(200);
+	tmp = wpabuf_alloc(200 + extra);
 	if (tmp == NULL)
 		return -1;
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_assoc_req)
+		wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	peer = bssid ? p2p_get_device(p2p, bssid) : NULL;
 
 	lpos = p2p_buf_add_ie_hdr(tmp);
@@ -2156,29 +2286,37 @@
 }
 
 
+int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr)
+{
+	struct p2p_message msg;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg))
+		return -1;
+
+	if (msg.p2p_device_addr) {
+		os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+		return 0;
+	} else if (msg.device_id) {
+		os_memcpy(dev_addr, msg.device_id, ETH_ALEN);
+		return 0;
+	}
+	return -1;
+}
+
+
 int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr)
 {
 	struct wpabuf *p2p_ie;
-	struct p2p_message msg;
+	int ret;
 
 	p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
 					     P2P_IE_VENDOR_TYPE);
 	if (p2p_ie == NULL)
 		return -1;
-	os_memset(&msg, 0, sizeof(msg));
-	if (p2p_parse_p2p_ie(p2p_ie, &msg)) {
-		wpabuf_free(p2p_ie);
-		return -1;
-	}
-
-	if (msg.p2p_device_addr == NULL) {
-		wpabuf_free(p2p_ie);
-		return -1;
-	}
-
-	os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+	ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr);
 	wpabuf_free(p2p_ie);
-	return 0;
+	return ret;
 }
 
 
@@ -2256,6 +2394,17 @@
 		p2p->cfg->model_number = os_strdup(cfg->model_number);
 	if (cfg->serial_number)
 		p2p->cfg->serial_number = os_strdup(cfg->serial_number);
+	if (cfg->pref_chan) {
+		p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan *
+						sizeof(struct p2p_channel));
+		if (p2p->cfg->pref_chan) {
+			os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan,
+				  cfg->num_pref_chan *
+				  sizeof(struct p2p_channel));
+		} else
+			p2p->cfg->num_pref_chan = 0;
+	}
+
 #ifdef ANDROID_P2P
 	/* 100ms listen time is too less to receive the response frames in some scenarios
 	 * increasing min listen time to 200ms.
@@ -2282,12 +2431,29 @@
 	eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
 			       p2p_expiration_timeout, p2p, NULL);
 
+	p2p->go_timeout = 100;
+	p2p->client_timeout = 20;
+
 	return p2p;
 }
 
 
 void p2p_deinit(struct p2p_data *p2p)
 {
+#ifdef CONFIG_WIFI_DISPLAY
+	wpabuf_free(p2p->wfd_ie_beacon);
+	wpabuf_free(p2p->wfd_ie_probe_req);
+	wpabuf_free(p2p->wfd_ie_probe_resp);
+	wpabuf_free(p2p->wfd_ie_assoc_req);
+	wpabuf_free(p2p->wfd_ie_invitation);
+	wpabuf_free(p2p->wfd_ie_prov_disc_req);
+	wpabuf_free(p2p->wfd_ie_prov_disc_resp);
+	wpabuf_free(p2p->wfd_ie_go_neg);
+	wpabuf_free(p2p->wfd_dev_info);
+	wpabuf_free(p2p->wfd_assoc_bssid);
+	wpabuf_free(p2p->wfd_coupled_sink_info);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL);
 	eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
 	eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
@@ -2298,6 +2464,7 @@
 	os_free(p2p->cfg->model_name);
 	os_free(p2p->cfg->model_number);
 	os_free(p2p->cfg->serial_number);
+	os_free(p2p->cfg->pref_chan);
 	os_free(p2p->groups);
 	wpabuf_free(p2p->sd_resp);
 	os_free(p2p->after_scan_tx);
@@ -2309,16 +2476,7 @@
 void p2p_flush(struct p2p_data *p2p)
 {
 	struct p2p_device *dev, *prev;
-	p2p_clear_timeout(p2p);
-#ifdef ANDROID_P2P
-	if (p2p->state == P2P_SEARCH)
-		wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
-						P2P_EVENT_FIND_STOPPED);
-#endif
-	p2p_set_state(p2p, P2P_IDLE);
-	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
-	p2p->go_neg_peer = NULL;
-	eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+	p2p_stop_find(p2p);
 	dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
 			      list) {
 		dl_list_del(&dev->list);
@@ -2643,7 +2801,13 @@
 	if (!success) {
 		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 
-		if (p2p->state != P2P_IDLE)
+		if (p2p->user_initiated_pd &&
+		    (p2p->state == P2P_SEARCH || p2p->state == P2P_LISTEN_ONLY))
+		{
+			/* Retry request from timeout to avoid busy loops */
+			p2p->pending_action_state = P2P_PENDING_PD;
+			p2p_set_timeout(p2p, 0, 50000);
+		} else if (p2p->state != P2P_IDLE)
 			p2p_continue_find(p2p);
 		else if (p2p->user_initiated_pd) {
 			p2p->pending_action_state = P2P_PENDING_PD;
@@ -2671,17 +2835,7 @@
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
 			 int level, const u8 *ies, size_t ies_len)
 {
-	p2p_add_device(p2p, bssid, freq, level, ies, ies_len);
-
-	if (p2p->go_neg_peer && p2p->state == P2P_SEARCH &&
-	    os_memcmp(p2p->go_neg_peer->info.p2p_device_addr, bssid, ETH_ALEN)
-	    == 0) {
-		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-			"P2P: Found GO Negotiation peer - try to start GO "
-			"negotiation");
-		p2p_connect_send(p2p, p2p->go_neg_peer);
-		return 1;
-	}
+	p2p_add_device(p2p, bssid, freq, level, ies, ies_len, 1);
 
 	return 0;
 }
@@ -2705,8 +2859,16 @@
 
 void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
 {
-	u8 *len = p2p_buf_add_ie_hdr(ies);
-	p2p_buf_add_capability(ies, p2p->dev_capab, 0);
+	u8 *len;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_probe_req)
+		wpabuf_put_buf(ies, p2p->wfd_ie_probe_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	len = p2p_buf_add_ie_hdr(ies);
+	p2p_buf_add_capability(ies, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
 	if (dev_id)
 		p2p_buf_add_device_id(ies, dev_id);
 	if (p2p->cfg->reg_class && p2p->cfg->channel)
@@ -2723,7 +2885,14 @@
 
 size_t p2p_scan_ie_buf_len(struct p2p_data *p2p)
 {
-	return 100;
+	size_t len = 100;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p && p2p->wfd_ie_probe_req)
+		len += wpabuf_len(p2p->wfd_ie_probe_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	return len;
 }
 
 
@@ -2748,19 +2917,14 @@
 	}
 
 	if (success) {
-#ifndef ANDROID_P2P
-		dev->go_neg_req_sent++;
-#endif
 		if (dev->flags & P2P_DEV_USER_REJECTED) {
 			p2p_set_state(p2p, P2P_IDLE);
 			return;
 		}
-	}
-#ifdef ANDROID_P2P
-	else {
+	} else if (dev->go_neg_req_sent) {
+		/* Cancel the increment from p2p_connect_send() on failure */
 		dev->go_neg_req_sent--;
 	}
-#endif
 
 	if (!success &&
 	    (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) &&
@@ -2779,7 +2943,7 @@
 	 * channel.
 	 */
 	p2p_set_state(p2p, P2P_CONNECT);
-	p2p_set_timeout(p2p, 0, 100000);
+	p2p_set_timeout(p2p, 0, success ? 200000 : 100000);
 }
 
 
@@ -2795,7 +2959,7 @@
 		return;
 	}
 	p2p_set_state(p2p, P2P_CONNECT);
-	p2p_set_timeout(p2p, 0, 100000);
+	p2p_set_timeout(p2p, 0, 250000);
 }
 
 
@@ -2978,6 +3142,26 @@
 				"new one");
 			return 1;
 		}
+		if (p2p->pending_listen_freq) {
+			/*
+			 * Better wait a bit if the driver is unable to start
+			 * offchannel operation for some reason. p2p_search()
+			 * will be started from internal timeout.
+			 */
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Listen "
+				"operation did not seem to start - delay "
+				"search phase to avoid busy loop");
+			p2p_set_timeout(p2p, 0, 100000);
+			return 1;
+		}
+		if (p2p->search_delay) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay "
+				"search operation by %u ms",
+				p2p->search_delay);
+			p2p_set_timeout(p2p, p2p->search_delay / 1000,
+					(p2p->search_delay % 1000) * 1000);
+			return 1;
+		}
 		p2p_search(p2p);
 		return 1;
 	}
@@ -2989,6 +3173,14 @@
 static void p2p_timeout_connect(struct p2p_data *p2p)
 {
 	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	if (p2p->go_neg_peer &&
+	    (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Wait for GO "
+			"Negotiation Confirm timed out - assume GO "
+			"Negotiation failed");
+		p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+		return;
+	}
 	p2p_set_state(p2p, P2P_CONNECT_LISTEN);
 	p2p_listen_in_find(p2p);
 }
@@ -3164,6 +3356,16 @@
 		/* Check if we timed out waiting for PD req */
 		if (p2p->pending_action_state == P2P_PENDING_PD)
 			p2p_timeout_prov_disc_req(p2p);
+		if (p2p->search_delay && !p2p->in_search_delay) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay "
+				"search operation by %u ms",
+				p2p->search_delay);
+			p2p->in_search_delay = 1;
+			p2p_set_timeout(p2p, p2p->search_delay / 1000,
+					(p2p->search_delay % 1000) * 1000);
+			break;
+		}
+		p2p->in_search_delay = 0;
 		p2p_search(p2p);
 		break;
 	case P2P_CONNECT:
@@ -3209,6 +3411,8 @@
 		break;
 	case P2P_SEARCH_WHEN_READY:
 		break;
+	case P2P_CONTINUE_SEARCH_WHEN_READY:
+		break;
 	}
 }
 
@@ -3392,6 +3596,24 @@
 		pos += res;
 	}
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (dev->info.wfd_subelems) {
+		res = os_snprintf(pos, end - pos, "wfd_subelems=");
+		if (res < 0 || res >= end - pos)
+			return pos - buf;
+		pos += res;
+
+		pos += wpa_snprintf_hex(pos, end - pos,
+					wpabuf_head(dev->info.wfd_subelems),
+					wpabuf_len(dev->info.wfd_subelems));
+
+		res = os_snprintf(pos, end - pos, "\n");
+		if (res < 0 || res >= end - pos)
+			return pos - buf;
+		pos += res;
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	return pos - buf;
 }
 
@@ -3802,6 +4024,28 @@
 }
 
 
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+		      const struct p2p_channel *pref_chan)
+{
+	struct p2p_channel *n;
+
+	if (pref_chan) {
+		n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
+		if (n == NULL)
+			return -1;
+		os_memcpy(n, pref_chan,
+			  num_pref_chan * sizeof(struct p2p_channel));
+	} else
+		n = NULL;
+
+	os_free(p2p->cfg->pref_chan);
+	p2p->cfg->pref_chan = n;
+	p2p->cfg->num_pref_chan = num_pref_chan;
+
+	return 0;
+}
+
+
 int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
 			   u8 *iface_addr)
 {
@@ -3977,5 +4221,150 @@
 {
 	if (p2p == NULL)
 		return 0;
+	if (p2p->state == P2P_SEARCH || p2p->state == P2P_SEARCH_WHEN_READY ||
+	    p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY)
+		return 2;
 	return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
 }
+
+
+void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
+			    u8 client_timeout)
+{
+	if (p2p) {
+		p2p->go_timeout = go_timeout;
+		p2p->client_timeout = client_timeout;
+	}
+}
+
+
+void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay)
+{
+	if (p2p && p2p->search_delay < delay)
+		p2p->search_delay = delay;
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
+{
+	size_t g;
+	struct p2p_group *group;
+
+	for (g = 0; g < p2p->num_groups; g++) {
+		group = p2p->groups[g];
+		p2p_group_update_ies(group);
+	}
+}
+
+
+int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_beacon);
+	p2p->wfd_ie_beacon = ie;
+	p2p_update_wfd_ie_groups(p2p);
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_probe_req);
+	p2p->wfd_ie_probe_req = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_probe_resp);
+	p2p->wfd_ie_probe_resp = ie;
+	p2p_update_wfd_ie_groups(p2p);
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_assoc_req);
+	p2p->wfd_ie_assoc_req = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_invitation);
+	p2p->wfd_ie_invitation = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_prov_disc_req);
+	p2p->wfd_ie_prov_disc_req = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_prov_disc_resp);
+	p2p->wfd_ie_prov_disc_resp = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie)
+{
+	wpabuf_free(p2p->wfd_ie_go_neg);
+	p2p->wfd_ie_go_neg = ie;
+	return 0;
+}
+
+
+int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem)
+{
+	wpabuf_free(p2p->wfd_dev_info);
+	if (elem) {
+		p2p->wfd_dev_info = wpabuf_dup(elem);
+		if (p2p->wfd_dev_info == NULL)
+			return -1;
+	} else
+		p2p->wfd_dev_info = NULL;
+
+	return 0;
+}
+
+
+int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem)
+{
+	wpabuf_free(p2p->wfd_assoc_bssid);
+	if (elem) {
+		p2p->wfd_assoc_bssid = wpabuf_dup(elem);
+		if (p2p->wfd_assoc_bssid == NULL)
+			return -1;
+	} else
+		p2p->wfd_assoc_bssid = NULL;
+
+	return 0;
+}
+
+
+int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
+				  const struct wpabuf *elem)
+{
+	wpabuf_free(p2p->wfd_coupled_sink_info);
+	if (elem) {
+		p2p->wfd_coupled_sink_info = wpabuf_dup(elem);
+		if (p2p->wfd_coupled_sink_info == NULL)
+			return -1;
+	} else
+		p2p->wfd_coupled_sink_info = NULL;
+
+	return 0;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index fe98f5e..b80f898 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -53,6 +53,10 @@
 	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
 };
 
+enum p2p_sd_action {
+	SRV_UPDATE, SRV_ADD, SRV_DEL, SRV_FLUSH
+};
+
 /**
  * struct p2p_go_neg_results - P2P Group Owner Negotiation results
  */
@@ -75,6 +79,8 @@
 	 */
 	int freq;
 
+	int ht40;
+
 	/**
 	 * ssid - SSID of the group
 	 */
@@ -210,6 +216,11 @@
 	size_t wps_sec_dev_type_list_len;
 
 	struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+
+	/**
+	 * wfd_subelems - Wi-Fi Display subelements from WFD IE(s)
+	 */
+	struct wpabuf *wfd_subelems;
 };
 
 enum p2p_prov_disc_status {
@@ -218,6 +229,11 @@
 	P2P_PROV_DISC_REJECTED,
 };
 
+struct p2p_channel {
+	u8 op_class;
+	u8 chan;
+};
+
 /**
  * struct p2p_config - P2P configuration
  *
@@ -265,6 +281,16 @@
 	struct p2p_channels channels;
 
 	/**
+	 * num_pref_chan - Number of pref_chan entries
+	 */
+	unsigned int num_pref_chan;
+
+	/**
+	 * pref_chan - Preferred channels for GO Negotiation
+	 */
+	struct p2p_channel *pref_chan;
+
+	/**
 	 * pri_dev_type - Primary Device Type (see WPS)
 	 */
 	u8 pri_dev_type[8];
@@ -333,6 +359,14 @@
 	 */
 	size_t ssid_postfix_len;
 
+#ifdef ANDROID_P2P
+	enum p2p_concurrency_type {
+		P2P_NON_CONCURRENT,
+		P2P_SINGLE_CHANNEL_CONCURRENT,
+		P2P_MULTI_CHANNEL_CONCURRENT,
+	} p2p_concurrency;
+#endif
+
 	/**
 	 * msg_ctx - Context to use with wpa_msg() calls
 	 */
@@ -354,14 +388,14 @@
 	 * @num_req_dev_types: Number of requested device types
 	 * @req_dev_types: Array containing requested device types
 	 * @dev_id: Device ID to search for or %NULL to find all devices
+	 * @pw_id: Device Password ID
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This callback function is used to request a P2P scan or search
 	 * operation to be completed. Type type argument specifies which type
 	 * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the
 	 * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL
-	 * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC
-	 * request a scan of a single channel specified by freq.
+	 * indicates that all channels are to be scanned.
 	 * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels
 	 * plus one extra channel specified by freq.
 	 *
@@ -377,7 +411,7 @@
 	 */
 	int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq,
 			unsigned int num_req_dev_types,
-			const u8 *req_dev_types, const u8 *dev_id);
+			const u8 *req_dev_types, const u8 *dev_id, u16 pw_id);
 
 	/**
 	 * send_probe_resp - Transmit a Probe Response frame
@@ -814,12 +848,13 @@
  *	containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no
  *	requested device types.
  * @dev_id: Device ID to search for or %NULL to find all devices
+ * @search_delay: Extra delay in milliseconds between search iterations
  * Returns: 0 on success, -1 on failure
  */
 int p2p_find(struct p2p_data *p2p, unsigned int timeout,
 	     enum p2p_discovery_type type,
 	     unsigned int num_req_dev_types, const u8 *req_dev_types,
-	     const u8 *dev_id);
+	     const u8 *dev_id, unsigned int search_delay);
 
 /**
  * p2p_stop_find - Stop P2P Find (Device Discovery)
@@ -861,12 +896,20 @@
  * @persistent_group: Whether to create a persistent group (0 = no, 1 =
  * persistent group without persistent reconnect, 2 = persistent group with
  * persistent reconnect)
+ * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
+ *	a new SSID
+ * @force_ssid_len: Length of $force_ssid buffer
+ * @pd_before_go_neg: Whether to send Provision Discovery prior to GO
+ *	Negotiation as an interoperability workaround when initiating group
+ *	formation
  * Returns: 0 on success, -1 on failure
  */
 int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
 		enum p2p_wps_method wps_method,
 		int go_intent, const u8 *own_interface_addr,
-		unsigned int force_freq, int persistent_group);
+		unsigned int force_freq, int persistent_group,
+		const u8 *force_ssid, size_t force_ssid_len,
+		int pd_before_go_neg);
 
 /**
  * p2p_authorize - Authorize P2P group formation (GO negotiation)
@@ -879,6 +922,9 @@
  * @persistent_group: Whether to create a persistent group (0 = no, 1 =
  * persistent group without persistent reconnect, 2 = persistent group with
  * persistent reconnect)
+ * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
+ *	a new SSID
+ * @force_ssid_len: Length of $force_ssid buffer
  * Returns: 0 on success, -1 on failure
  *
  * This is like p2p_connect(), but the actual group negotiation is not
@@ -887,7 +933,8 @@
 int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
 		  enum p2p_wps_method wps_method,
 		  int go_intent, const u8 *own_interface_addr,
-		  unsigned int force_freq, int persistent_group);
+		  unsigned int force_freq, int persistent_group,
+		  const u8 *force_ssid, size_t force_ssid_len);
 
 /**
  * p2p_reject - Reject peer device (explicitly block connection attempts)
@@ -930,6 +977,11 @@
 void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
 		      const struct wpabuf *tlvs);
 
+#ifdef CONFIG_WIFI_DISPLAY
+void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst,
+			  const struct wpabuf *tlvs);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 /**
  * p2p_sd_cancel_request - Cancel a pending service discovery query
  * @p2p: P2P module context from p2p_init()
@@ -960,7 +1012,11 @@
  * of the local services. This will increment the Service Update Indicator
  * value which will be used in SD Request and Response frames.
  */
+#ifdef ANDROID_P2P
+void p2p_sd_service_update(struct p2p_data *p2p, int action);
+#else
 void p2p_sd_service_update(struct p2p_data *p2p);
+#endif
 
 
 enum p2p_invite_role {
@@ -1060,17 +1116,34 @@
 /**
  * p2p_clear_provisioning_info - Clear any stored provisioning info
  * @p2p: P2P module context from p2p_init()
- * @iface_addr: Peer P2P Interface Address
+ * @iface_addr: Peer P2P Device Address
  *
  * This function is used to clear stored WPS provisioning info for the given
  * peer.
  */
-void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *iface_addr);
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr);
 
 
 /* Event notifications from lower layer driver operations */
 
 /**
+ * enum p2p_probe_req_status
+ *
+ * @P2P_PREQ_MALFORMED: frame was not well-formed
+ * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored
+ * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request
+ * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed
+ * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P
+ */
+enum p2p_probe_req_status {
+	P2P_PREQ_MALFORMED,
+	P2P_PREQ_NOT_LISTEN,
+	P2P_PREQ_NOT_P2P,
+	P2P_PREQ_NOT_PROCESSED,
+	P2P_PREQ_PROCESSED
+};
+
+/**
  * p2p_probe_req_rx - Report reception of a Probe Request frame
  * @p2p: P2P module context from p2p_init()
  * @addr: Source MAC address
@@ -1078,10 +1151,11 @@
  * @bssid: BSSID if available or %NULL
  * @ie: Information elements from the Probe Request frame body
  * @ie_len: Length of ie buffer in octets
- * Returns: 0 to indicate the frame was not processed or 1 if it was
+ * Returns: value indicating the type and status of the probe request
  */
-int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
-		     const u8 *bssid, const u8 *ie, size_t ie_len);
+enum p2p_probe_req_status
+p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+		 const u8 *bssid, const u8 *ie, size_t ie_len);
 
 /**
  * p2p_rx_action - Report received Action frame
@@ -1220,6 +1294,16 @@
 	unsigned int max_clients;
 
 	/**
+	 * ssid - Group SSID
+	 */
+	u8 ssid[32];
+
+	/**
+	 * ssid_len - Length of SSID
+	 */
+	size_t ssid_len;
+
+	/**
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
@@ -1371,6 +1455,15 @@
 int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end);
 
 /**
+ * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated
+ * P2P IE
+ * @p2p_ie: P2P IE
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: 0 on success or -1 if P2P Device Address could not be parsed
+ */
+int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr);
+
+/**
  * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s)
  * @ies: Information elements from scan results
  * @ies_len: ies buffer length in octets
@@ -1608,6 +1701,16 @@
 			 int cfg_op_channel);
 
 /**
+ * p2p_set_pref_chan - Set P2P preferred channel list
+ * @p2p: P2P module context from p2p_init()
+ * @num_pref_chan: Number of entries in pref_chan list
+ * @pref_chan: Preferred channels or %NULL to remove preferences
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+		      const struct p2p_channel *pref_chan);
+
+/**
  * p2p_in_progress - Check whether a P2P operation is progress
  * @p2p: P2P module context from p2p_init()
  * Returns: 0 if P2P module is idle or 1 if an operation is in progress
@@ -1639,4 +1742,29 @@
 
 const char * p2p_wps_method_text(enum p2p_wps_method method);
 
+/**
+ * p2p_set_config_timeout - Set local config timeouts
+ * @p2p: P2P module context from p2p_init()
+ * @go_timeout: Time in 10 ms units it takes to start the GO mode
+ * @client_timeout: Time in 10 ms units it takes to start the client mode
+ */
+void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
+			    u8 client_timeout);
+
+void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay);
+
+int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
+				  const struct wpabuf *elem);
+struct wpabuf * wifi_display_encaps(struct wpabuf *subelems);
+
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 2106964..be069a5 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -132,10 +132,10 @@
 
 	/* Update attribute length */
 	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
-	wpa_printf(MSG_DEBUG, "P2P: * Channel List");
+	wpa_hexdump(MSG_DEBUG, "P2P: * Channel List",
+		    len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2);
 }
 
-
 void p2p_buf_add_status(struct wpabuf *buf, u8 status)
 {
 	/* Status */
@@ -351,7 +351,7 @@
 }
 
 
-void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 		      int all_attr)
 {
 	u8 *len;
@@ -369,11 +369,14 @@
 		wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
 	}
 
-	/* Device Password ID */
-	wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
-	wpabuf_put_be16(buf, 2);
-	wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d", pw_id);
-	wpabuf_put_be16(buf, pw_id);
+	if (pw_id >= 0) {
+		/* Device Password ID */
+		wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
+		wpabuf_put_be16(buf, 2);
+		wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d",
+			   pw_id);
+		wpabuf_put_be16(buf, pw_id);
+	}
 
 	if (all_attr) {
 		wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 6e818c2..3835590 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -98,7 +98,7 @@
 }
 
 
-static u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
+u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
 {
 	switch (wps_method) {
 	case WPS_PIN_DISPLAY:
@@ -134,8 +134,14 @@
 	struct wpabuf *buf;
 	u8 *len;
 	u8 group_capab;
+	size_t extra = 0;
 
-	buf = wpabuf_alloc(1000);
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_go_neg)
+		extra = wpabuf_len(p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
 
@@ -155,11 +161,13 @@
 		group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
 	if (p2p->cfg->p2p_intra_bss)
 		group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
-	p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+			       group_capab);
 	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) |
 			      p2p->next_tie_breaker);
 	p2p->next_tie_breaker = !p2p->next_tie_breaker;
-	p2p_buf_add_config_timeout(buf, 100, 20);
+	p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
 	p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
 				   p2p->cfg->channel);
 	if (p2p->ext_listen_interval)
@@ -175,6 +183,11 @@
 	/* WPS IE with Device Password ID attribute */
 	p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0);
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_go_neg)
+		wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	return buf;
 }
 
@@ -184,6 +197,23 @@
 	struct wpabuf *req;
 	int freq;
 
+	if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
+		u16 config_method;
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Use PD-before-GO-Neg workaround for " MACSTR,
+			MAC2STR(dev->info.p2p_device_addr));
+		if (dev->wps_method == WPS_PIN_DISPLAY)
+			config_method = WPS_CONFIG_KEYPAD;
+		else if (dev->wps_method == WPS_PIN_KEYPAD)
+			config_method = WPS_CONFIG_DISPLAY;
+		else if (dev->wps_method == WPS_PBC)
+			config_method = WPS_CONFIG_PUSHBUTTON;
+		else
+			return -1;
+		return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr,
+					 config_method, 0, 0);
+	}
+
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
 	if (freq <= 0) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -203,9 +233,6 @@
 	p2p->go_neg_peer = dev;
 	dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
 	dev->connect_reqs++;
-#ifdef ANDROID_P2P
-	dev->go_neg_req_sent++;
-#endif
 	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
 			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
 			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
@@ -213,7 +240,8 @@
 			"P2P: Failed to send Action frame");
 		/* Use P2P find to recover and retry */
 		p2p_set_timeout(p2p, 0, 0);
-	}
+	} else
+		dev->go_neg_req_sent++;
 
 	wpabuf_free(req);
 
@@ -229,10 +257,17 @@
 	struct wpabuf *buf;
 	u8 *len;
 	u8 group_capab;
+	size_t extra = 0;
 
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 		"P2P: Building GO Negotiation Response");
-	buf = wpabuf_alloc(1000);
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_go_neg)
+		extra = wpabuf_len(p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
 
@@ -253,9 +288,11 @@
 		if (p2p->cfg->p2p_intra_bss)
 			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
 	}
-	p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+			       group_capab);
 	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
-	p2p_buf_add_config_timeout(buf, 100, 20);
+	p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
 	if (peer && peer->go_state == REMOTE_GO) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Omit Operating "
 			"Channel attribute");
@@ -265,6 +302,7 @@
 					      p2p->op_channel);
 	}
 	p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+
 	if (status || peer == NULL) {
 		p2p_buf_add_channel_list(buf, p2p->cfg->country,
 					 &p2p->channels);
@@ -277,6 +315,7 @@
 				       &res);
 		p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
 	}
+
 	p2p_buf_add_device_info(buf, p2p, peer);
 	if (peer && peer->go_state == LOCAL_GO) {
 		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
@@ -289,16 +328,23 @@
 			 p2p_wps_method_pw_id(peer ? peer->wps_method :
 					      WPS_NOT_READY), 0);
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_go_neg)
+		wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
 	return buf;
 }
 
 
-static void p2p_reselect_channel(struct p2p_data *p2p,
-				 struct p2p_channels *intersection)
+void p2p_reselect_channel(struct p2p_data *p2p,
+			  struct p2p_channels *intersection)
 {
 	struct p2p_reg_class *cl;
 	int freq;
 	u8 op_reg_class, op_channel;
+	unsigned int i;
 
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
 		"channel (reg_class %u channel %u) not acceptable to the "
@@ -331,6 +377,21 @@
 		return;
 	}
 
+	/* Select channel with highest preference if the peer supports it */
+	for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
+		if (p2p_channels_includes(intersection,
+					  p2p->cfg->pref_chan[i].op_class,
+					  p2p->cfg->pref_chan[i].chan)) {
+			p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class;
+			p2p->op_channel = p2p->cfg->pref_chan[i].chan;
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick "
+				"highest preferred chnnel (op_class %u "
+				"channel %u) from intersection",
+				p2p->op_reg_class, p2p->op_channel);
+			return;
+		}
+	}
+
 	/*
 	 * Fall back to whatever is included in the channel intersection since
 	 * no better options seems to be available.
@@ -641,12 +702,23 @@
 	if (status == P2P_SC_SUCCESS) {
 		p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE;
 		dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM;
+		if (os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) < 0) {
+			/*
+			 * Peer has smaller address, so the GO Negotiation
+			 * Response from us is expected to complete
+			 * negotiation. Ignore a GO Negotiation Response from
+			 * the peer if it happens to be received after this
+			 * point due to a race condition in GO Negotiation
+			 * Request transmission and processing.
+			 */
+			dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+		}
 	} else
 		p2p->pending_action_state =
 			P2P_PENDING_GO_NEG_RESPONSE_FAILURE;
 	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
 			    p2p->cfg->dev_addr,
-			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+			    wpabuf_head(resp), wpabuf_len(resp), 250) < 0) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Failed to send Action frame");
 	}
@@ -664,10 +736,17 @@
 	u8 *len;
 	struct p2p_channels res;
 	u8 group_capab;
+	size_t extra = 0;
 
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 		"P2P: Building GO Negotiation Confirm");
-	buf = wpabuf_alloc(1000);
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_go_neg)
+		extra = wpabuf_len(p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
 
@@ -688,7 +767,9 @@
 		if (p2p->cfg->p2p_intra_bss)
 			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
 	}
-	p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+			       group_capab);
 	if (go || resp_chan == NULL)
 		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
 					      p2p->op_reg_class,
@@ -704,6 +785,11 @@
 	}
 	p2p_buf_update_ie_hdr(buf, len);
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_go_neg)
+		wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	return buf;
 }
 
@@ -992,7 +1078,7 @@
 	else
 		freq = dev->listen_freq;
 	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
-			    wpabuf_head(conf), wpabuf_len(conf), 200) < 0) {
+			    wpabuf_head(conf), wpabuf_len(conf), 0) < 0) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Failed to send Action frame");
 		p2p_go_neg_failed(p2p, dev, -1);
@@ -1084,6 +1170,18 @@
 #endif /* CONFIG_P2P_STRICT */
 	}
 
+#ifdef ANDROID_P2P
+	if (msg.operating_channel) {
+		dev->oper_freq = p2p_channel_to_freq((const char *)
+						     msg.operating_channel,
+						     msg.operating_channel[3],
+						     msg.operating_channel[4]);
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating "
+			"channel preference: %d MHz", dev->oper_freq);
+	} else
+		dev->oper_freq = 0;
+#endif
+
 	if (!msg.channel_list) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Mandatory Operating Channel attribute missing "
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index fafd135..8687320 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -22,6 +22,7 @@
 	u8 addr[ETH_ALEN]; /* P2P Interface Address */
 	u8 dev_addr[ETH_ALEN]; /* P2P Device Address */
 	struct wpabuf *p2p_ie;
+	struct wpabuf *wfd_ie;
 	struct wpabuf *client_info;
 	u8 dev_capab;
 };
@@ -37,12 +38,10 @@
 	int group_formation;
 	int beacon_update;
 	struct wpabuf *noa;
+	struct wpabuf *wfd_ie;
 };
 
 
-static void p2p_group_update_ies(struct p2p_group *group);
-
-
 struct p2p_group * p2p_group_init(struct p2p_data *p2p,
 				  struct p2p_group_config *config)
 {
@@ -52,8 +51,8 @@
 	if (group == NULL)
 		return NULL;
 
-	groups = os_realloc(p2p->groups, (p2p->num_groups + 1) *
-			    sizeof(struct p2p_group *));
+	groups = os_realloc_array(p2p->groups, p2p->num_groups + 1,
+				  sizeof(struct p2p_group *));
 	if (groups == NULL) {
 		os_free(group);
 		return NULL;
@@ -74,6 +73,7 @@
 
 static void p2p_group_free_member(struct p2p_group_member *m)
 {
+	wpabuf_free(m->wfd_ie);
 	wpabuf_free(m->p2p_ie);
 	wpabuf_free(m->client_info);
 	os_free(m);
@@ -118,6 +118,7 @@
 	p2p_group_free_members(group);
 	os_free(group->cfg);
 	wpabuf_free(group->noa);
+	wpabuf_free(group->wfd_ie);
 	os_free(group);
 }
 
@@ -135,11 +136,10 @@
 static void p2p_group_add_common_ies(struct p2p_group *group,
 				     struct wpabuf *ie)
 {
-	u8 dev_capab = 0, group_capab = 0;
+	u8 dev_capab = group->p2p->dev_capab, group_capab = 0;
 
 	/* P2P Capability */
-	dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
-	dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+	dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 	group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
 	if (group->cfg->persistent_group) {
 		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
@@ -173,11 +173,22 @@
 {
 	struct wpabuf *ie;
 	u8 *len;
+	size_t extra = 0;
 
-	ie = wpabuf_alloc(257);
+#ifdef CONFIG_WIFI_DISPLAY
+	if (group->p2p->wfd_ie_beacon)
+		extra = wpabuf_len(group->p2p->wfd_ie_beacon);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	ie = wpabuf_alloc(257 + extra);
 	if (ie == NULL)
 		return NULL;
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (group->p2p->wfd_ie_beacon)
+		wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	len = p2p_buf_add_ie_hdr(ie);
 	p2p_group_add_common_ies(group, ie);
 	p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr);
@@ -188,17 +199,193 @@
 }
 
 
+#ifdef CONFIG_WIFI_DISPLAY
+
+struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g)
+{
+	return g->wfd_ie;
+}
+
+
+struct wpabuf * wifi_display_encaps(struct wpabuf *subelems)
+{
+	struct wpabuf *ie;
+	const u8 *pos, *end;
+
+	if (subelems == NULL)
+		return NULL;
+
+	ie = wpabuf_alloc(wpabuf_len(subelems) + 100);
+	if (ie == NULL)
+		return NULL;
+
+	pos = wpabuf_head(subelems);
+	end = pos + wpabuf_len(subelems);
+
+	while (end > pos) {
+		size_t frag_len = end - pos;
+		if (frag_len > 251)
+			frag_len = 251;
+		wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+		wpabuf_put_u8(ie, 4 + frag_len);
+		wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE);
+		wpabuf_put_data(ie, pos, frag_len);
+		pos += frag_len;
+	}
+
+	return ie;
+}
+
+
+static int wifi_display_add_dev_info_descr(struct wpabuf *buf,
+					   struct p2p_group_member *m)
+{
+	const u8 *pos, *end;
+	const u8 *dev_info = NULL;
+	const u8 *assoc_bssid = NULL;
+	const u8 *coupled_sink = NULL;
+	u8 zero_addr[ETH_ALEN];
+
+	if (m->wfd_ie == NULL)
+		return 0;
+
+	os_memset(zero_addr, 0, ETH_ALEN);
+	pos = wpabuf_head_u8(m->wfd_ie);
+	end = pos + wpabuf_len(m->wfd_ie);
+	while (pos + 1 < end) {
+		u8 id;
+		u16 len;
+
+		id = *pos++;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (pos + len > end)
+			break;
+
+		switch (id) {
+		case WFD_SUBELEM_DEVICE_INFO:
+			if (len < 6)
+				break;
+			dev_info = pos;
+			break;
+		case WFD_SUBELEM_ASSOCIATED_BSSID:
+			if (len < ETH_ALEN)
+				break;
+			assoc_bssid = pos;
+			break;
+		case WFD_SUBELEM_COUPLED_SINK:
+			if (len < 1 + ETH_ALEN)
+				break;
+			coupled_sink = pos;
+			break;
+		}
+
+		pos += len;
+	}
+
+	if (dev_info == NULL)
+		return 0;
+
+	wpabuf_put_u8(buf, 23);
+	wpabuf_put_data(buf, m->dev_addr, ETH_ALEN);
+	if (assoc_bssid)
+		wpabuf_put_data(buf, assoc_bssid, ETH_ALEN);
+	else
+		wpabuf_put_data(buf, zero_addr, ETH_ALEN);
+	wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */
+	wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */
+	if (coupled_sink) {
+		wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN);
+	} else {
+		wpabuf_put_u8(buf, 0);
+		wpabuf_put_data(buf, zero_addr, ETH_ALEN);
+	}
+
+	return 1;
+}
+
+
+static struct wpabuf *
+wifi_display_build_go_ie(struct p2p_group *group)
+{
+	struct wpabuf *wfd_subelems, *wfd_ie;
+	struct p2p_group_member *m;
+	u8 *len;
+	unsigned int count = 0;
+
+	if (!group->p2p->wfd_ie_probe_resp)
+		return NULL;
+
+	wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) +
+				    group->num_members * 24 + 100);
+	if (wfd_subelems == NULL)
+		return NULL;
+	if (group->p2p->wfd_dev_info)
+		wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info);
+	if (group->p2p->wfd_assoc_bssid)
+		wpabuf_put_buf(wfd_subelems,
+			       group->p2p->wfd_assoc_bssid);
+	if (group->p2p->wfd_coupled_sink_info)
+		wpabuf_put_buf(wfd_subelems,
+			       group->p2p->wfd_coupled_sink_info);
+
+	/* Build WFD Session Info */
+	wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO);
+	len = wpabuf_put(wfd_subelems, 2);
+	m = group->members;
+	while (m) {
+		if (wifi_display_add_dev_info_descr(wfd_subelems, m))
+			count++;
+		m = m->next;
+	}
+
+	if (count == 0) {
+		/* No Wi-Fi Display clients - do not include subelement */
+		wfd_subelems->used -= 3;
+	} else {
+		WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len -
+			     2);
+		wpa_printf(MSG_DEBUG, "WFD: WFD Session Info: %u descriptors",
+			   count);
+	}
+
+	wfd_ie = wifi_display_encaps(wfd_subelems);
+	wpabuf_free(wfd_subelems);
+
+	return wfd_ie;
+}
+
+static void wifi_display_group_update(struct p2p_group *group)
+{
+	wpabuf_free(group->wfd_ie);
+	group->wfd_ie = wifi_display_build_go_ie(group);
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
 static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
 {
 	u8 *group_info;
 	struct wpabuf *ie;
 	struct p2p_group_member *m;
 	u8 *len;
+	size_t extra = 0;
 
-	ie = wpabuf_alloc(257);
+#ifdef CONFIG_WIFI_DISPLAY
+	if (group->wfd_ie)
+		extra += wpabuf_len(group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	ie = wpabuf_alloc(257 + extra);
 	if (ie == NULL)
 		return NULL;
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (group->wfd_ie)
+		wpabuf_put_buf(ie, group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	len = p2p_buf_add_ie_hdr(ie);
 
 	p2p_group_add_common_ies(group, ie);
@@ -217,15 +404,20 @@
 		     (u8 *) wpabuf_put(ie, 0) - group_info - 3);
 
 	p2p_buf_update_ie_hdr(ie, len);
+
 	return ie;
 }
 
 
-static void p2p_group_update_ies(struct p2p_group *group)
+void p2p_group_update_ies(struct p2p_group *group)
 {
 	struct wpabuf *beacon_ie;
 	struct wpabuf *probe_resp_ie;
 
+#ifdef CONFIG_WIFI_DISPLAY
+	wifi_display_group_update(group);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	probe_resp_ie = p2p_group_build_probe_resp_ie(group);
 	if (probe_resp_ie == NULL)
 		return;
@@ -355,6 +547,9 @@
 						       &m->dev_capab,
 						       m->dev_addr);
 	}
+#ifdef CONFIG_WIFI_DISPLAY
+	m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE);
+#endif /* CONFIG_WIFI_DISPLAY */
 
 	p2p_group_remove_member(group, addr);
 
@@ -362,8 +557,9 @@
 	group->members = m;
 	group->num_members++;
 	wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR
-		" to group (p2p=%d client_info=%d); num_members=%u/%u",
-		MAC2STR(addr), m->p2p_ie ? 1 : 0, m->client_info ? 1 : 0,
+		" to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u",
+		MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0,
+		m->client_info ? 1 : 0,
 		group->num_members, group->cfg->max_clients);
 	if (group->num_members == group->cfg->max_clients)
 		group->beacon_update = 1;
@@ -379,6 +575,12 @@
 {
 	struct wpabuf *resp;
 	u8 *rlen;
+	size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (group->wfd_ie)
+		extra = wpabuf_len(group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
 
 	/*
 	 * (Re)Association Response - P2P IE
@@ -386,9 +588,15 @@
 	 *	denied)
 	 * Extended Listen Timing (may be present)
 	 */
-	resp = wpabuf_alloc(20);
+	resp = wpabuf_alloc(20 + extra);
 	if (resp == NULL)
 		return NULL;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (group->wfd_ie)
+		wpabuf_put_buf(resp, group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	rlen = p2p_buf_add_ie_hdr(resp);
 	if (status != P2P_SC_SUCCESS)
 		p2p_buf_add_status(resp, status);
@@ -731,3 +939,15 @@
 
 	return 0;
 }
+
+
+int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
+				size_t group_id_len)
+{
+	if (group_id_len != ETH_ALEN + group->cfg->ssid_len)
+		return 0;
+	if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0)
+		return 0;
+	return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid,
+			 group->cfg->ssid_len) == 0;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 4ccd38d..673c4c1 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -90,6 +90,7 @@
 #define P2P_DEV_PD_FOR_JOIN BIT(14)
 #define P2P_DEV_REPORTED_ONCE BIT(15)
 #define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16)
+#define P2P_DEV_PD_BEFORE_GO_NEG BIT(17)
 	unsigned int flags;
 
 	int status; /* enum p2p_status_code */
@@ -108,6 +109,7 @@
 	struct p2p_sd_query *next;
 	u8 peer[ETH_ALEN];
 	int for_all_peers;
+	int wsd; /* Wi-Fi Display Service Discovery Request */
 	struct wpabuf *tlvs;
 };
 
@@ -206,6 +208,11 @@
 		 * P2P_SEARCH_WHEN_READY - Waiting to start Search
 		 */
 		P2P_SEARCH_WHEN_READY,
+
+		/**
+		 * P2P_CONTINUE_SEARCH_WHEN_READY - Waiting to continue Search
+		 */
+		P2P_CONTINUE_SEARCH_WHEN_READY,
 	} state;
 
 	/**
@@ -343,7 +350,12 @@
 	 * srv_update_indic - Service Update Indicator for local services
 	 */
 	u16 srv_update_indic;
-
+#ifdef ANDROID_P2P
+	/**
+	 * srv_count - Registered services count
+	 */
+	u16 srv_count;
+#endif
 	struct wpabuf *sd_resp; /* Fragmented SD response */
 	u8 sd_resp_addr[ETH_ALEN];
 	u8 sd_resp_dialog_token;
@@ -431,6 +443,27 @@
 	 * in IDLE state.
 	 */
 	int pd_retries;
+
+	u8 go_timeout;
+	u8 client_timeout;
+
+	/* Extra delay in milliseconds between search iterations */
+	unsigned int search_delay;
+	int in_search_delay;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	struct wpabuf *wfd_ie_beacon;
+	struct wpabuf *wfd_ie_probe_req;
+	struct wpabuf *wfd_ie_probe_resp;
+	struct wpabuf *wfd_ie_assoc_req;
+	struct wpabuf *wfd_ie_invitation;
+	struct wpabuf *wfd_ie_prov_disc_req;
+	struct wpabuf *wfd_ie_prov_disc_resp;
+	struct wpabuf *wfd_ie_go_neg;
+	struct wpabuf *wfd_dev_info;
+	struct wpabuf *wfd_assoc_bssid;
+	struct wpabuf *wfd_coupled_sink_info;
+#endif /* CONFIG_WIFI_DISPLAY */
 };
 
 /**
@@ -439,6 +472,7 @@
 struct p2p_message {
 	struct wpabuf *p2p_attributes;
 	struct wpabuf *wps_attributes;
+	struct wpabuf *wfd_subelems;
 
 	u8 dialog_token;
 
@@ -557,6 +591,10 @@
 u8 p2p_group_presence_req(struct p2p_group *group,
 			  const u8 *client_interface_addr,
 			  const u8 *noa, size_t noa_len);
+int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
+				size_t group_id_len);
+void p2p_group_update_ies(struct p2p_group *group);
+struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g);
 
 
 void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
@@ -588,7 +626,7 @@
 void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
 				   u16 interval);
 void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
-void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 		      int all_attr);
 
 /* p2p_sd.c */
@@ -616,6 +654,9 @@
 void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
 			     const u8 *data, size_t len);
 int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev);
+u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method);
+void p2p_reselect_channel(struct p2p_data *p2p,
+			  struct p2p_channels *intersection);
 
 /* p2p_pd.c */
 void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
@@ -660,7 +701,7 @@
 void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
 		      struct p2p_device *dev, struct p2p_message *msg);
 int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
-		   const u8 *ies, size_t ies_len);
+		   const u8 *ies, size_t ies_len, int scan_res);
 struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
 struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
 					     const u8 *addr);
@@ -675,5 +716,6 @@
 int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
 		    const u8 *src, const u8 *bssid, const u8 *buf,
 		    size_t len, unsigned int wait_time);
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq);
 
 #endif /* P2P_I_H */
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 417f1e7..769e57b 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -21,8 +21,30 @@
 	struct wpabuf *buf;
 	u8 *len;
 	const u8 *dev_addr;
+	size_t extra = 0;
 
-	buf = wpabuf_alloc(1000);
+#ifdef CONFIG_WIFI_DISPLAY
+	struct wpabuf *wfd_ie = p2p->wfd_ie_invitation;
+	if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) {
+		size_t i;
+		for (i = 0; i < p2p->num_groups; i++) {
+			struct p2p_group *g = p2p->groups[i];
+			struct wpabuf *ie;
+			if (os_memcmp(p2p_group_get_interface_addr(g),
+				      p2p->inv_bssid, ETH_ALEN) != 0)
+				continue;
+			ie = p2p_group_get_wfd_ie(g);
+			if (ie) {
+				wfd_ie = ie;
+				break;
+			}
+		}
+	}
+	if (wfd_ie)
+		extra = wpabuf_len(wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
 
@@ -36,7 +58,8 @@
 	if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent)
 		p2p_buf_add_config_timeout(buf, 0, 0);
 	else
-		p2p_buf_add_config_timeout(buf, 100, 20);
+		p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+					   p2p->client_timeout);
 	p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ?
 				     P2P_INVITATION_FLAGS_TYPE : 0);
 	p2p_buf_add_operating_channel(buf, p2p->cfg->country,
@@ -54,6 +77,11 @@
 	p2p_buf_add_device_info(buf, p2p, peer);
 	p2p_buf_update_ie_hdr(buf, len);
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (wfd_ie)
+		wpabuf_put_buf(buf, wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	return buf;
 }
 
@@ -67,8 +95,30 @@
 {
 	struct wpabuf *buf;
 	u8 *len;
+	size_t extra = 0;
 
-	buf = wpabuf_alloc(1000);
+#ifdef CONFIG_WIFI_DISPLAY
+	struct wpabuf *wfd_ie = p2p->wfd_ie_invitation;
+	if (wfd_ie && group_bssid) {
+		size_t i;
+		for (i = 0; i < p2p->num_groups; i++) {
+			struct p2p_group *g = p2p->groups[i];
+			struct wpabuf *ie;
+			if (os_memcmp(p2p_group_get_interface_addr(g),
+				      group_bssid, ETH_ALEN) != 0)
+				continue;
+			ie = p2p_group_get_wfd_ie(g);
+			if (ie) {
+				wfd_ie = ie;
+				break;
+			}
+		}
+	}
+	if (wfd_ie)
+		extra = wpabuf_len(wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
 
@@ -87,6 +137,11 @@
 		p2p_buf_add_channel_list(buf, p2p->cfg->country, channels);
 	p2p_buf_update_ie_hdr(buf, len);
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (wfd_ie)
+		wpabuf_put_buf(buf, wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	return buf;
 }
 
@@ -121,7 +176,8 @@
 			"P2P: Invitation Request from unknown peer "
 			MACSTR, MAC2STR(sa));
 
-		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1, 0))
+		{
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 				"P2P: Invitation Request add device failed "
 				MACSTR, MAC2STR(sa));
@@ -177,6 +233,8 @@
 	}
 
 	if (op_freq) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Invitation "
+			"processing forced frequency %d MHz", op_freq);
 		if (p2p_freq_to_channel(p2p->cfg->country, op_freq,
 					&reg_class, &channel) < 0) {
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -200,24 +258,89 @@
 		if (status == P2P_SC_SUCCESS)
 			channels = &intersection;
 	} else {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No forced channel from invitation processing - "
+			"figure out best one to use");
+
+		p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+				       &intersection);
+		/* Default to own configuration as a starting point */
+		p2p->op_reg_class = p2p->cfg->op_reg_class;
+		p2p->op_channel = p2p->cfg->op_channel;
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own default "
+			"op_class %d channel %d",
+			p2p->op_reg_class, p2p->op_channel);
+
+		/* Use peer preference if specified and compatible */
+		if (msg.operating_channel) {
+			int req_freq;
+			req_freq = p2p_channel_to_freq(
+				(const char *) msg.operating_channel,
+				msg.operating_channel[3],
+				msg.operating_channel[4]);
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer "
+				"operating channel preference: %d MHz",
+				req_freq);
+			if (req_freq > 0 &&
+			    p2p_channels_includes(&intersection,
+						  msg.operating_channel[3],
+						  msg.operating_channel[4])) {
+				p2p->op_reg_class = msg.operating_channel[3];
+				p2p->op_channel = msg.operating_channel[4];
+				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+					"P2P: Use peer preference op_class %d "
+					"channel %d",
+					p2p->op_reg_class, p2p->op_channel);
+			} else {
+				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+					"P2P: Cannot use peer channel "
+					"preference");
+			}
+		}
+
+		if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+					   p2p->op_channel)) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Initially selected channel (op_class %d "
+				"channel %d) not in channel intersection - try "
+				"to reselect",
+				p2p->op_reg_class, p2p->op_channel);
+			p2p_reselect_channel(p2p, &intersection);
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Re-selection result: op_class %d "
+				"channel %d",
+				p2p->op_reg_class, p2p->op_channel);
+			if (!p2p_channels_includes(&intersection,
+						   p2p->op_reg_class,
+						   p2p->op_channel)) {
+				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+					"P2P: Peer does not support selected "
+					"operating channel (reg_class=%u "
+					"channel=%u)",
+					p2p->op_reg_class, p2p->op_channel);
+				status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+				goto fail;
+			}
+		}
+
 		op_freq = p2p_channel_to_freq(p2p->cfg->country,
-					      p2p->cfg->op_reg_class,
-					      p2p->cfg->op_channel);
+					      p2p->op_reg_class,
+					      p2p->op_channel);
 		if (op_freq < 0) {
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 				"P2P: Unknown operational channel "
 				"(country=%c%c reg_class=%u channel=%u)",
 				p2p->cfg->country[0], p2p->cfg->country[1],
-				p2p->cfg->op_reg_class, p2p->cfg->op_channel);
+				p2p->op_reg_class, p2p->op_channel);
 			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 			goto fail;
 		}
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
+			"channel - %d MHz", op_freq);
 
-		p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
-				       &intersection);
 		if (status == P2P_SC_SUCCESS) {
-			reg_class = p2p->cfg->op_reg_class;
-			channel = p2p->cfg->op_channel;
+			reg_class = p2p->op_reg_class;
+			channel = p2p->op_channel;
 			channels = &intersection;
 		}
 	}
@@ -344,6 +467,8 @@
 	req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
 	if (req == NULL)
 		return -1;
+	if (p2p->state != P2P_IDLE)
+		p2p_stop_listen_for_freq(p2p, freq);
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 		"P2P: Sending Invitation Request");
 	p2p_set_state(p2p, P2P_INVITE);
@@ -458,11 +583,17 @@
 				force_freq);
 			return -1;
 		}
+#ifdef ANDROID_P2P
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "Single channel list %d", p2p->op_channel);
+#endif
 		p2p->channels.reg_classes = 1;
 		p2p->channels.reg_class[0].channels = 1;
 		p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
 		p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
 	} else {
+#ifdef ANDROID_P2P
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "Full channel list");
+#endif
 		p2p->op_reg_class = p2p->cfg->op_reg_class;
 		p2p->op_channel = p2p->cfg->op_channel;
 		os_memcpy(&p2p->channels, &p2p->cfg->channels,
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index a3ec57d..097a31d 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -414,6 +414,13 @@
 		return -1;
 	}
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (elems.wfd) {
+		msg->wfd_subelems = ieee802_11_vendor_ie_concat(
+			data, len, WFD_IE_VENDOR_TYPE);
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	return 0;
 }
 
@@ -453,6 +460,10 @@
 	msg->p2p_attributes = NULL;
 	wpabuf_free(msg->wps_attributes);
 	msg->wps_attributes = NULL;
+#ifdef CONFIG_WIFI_DISPLAY
+	wpabuf_free(msg->wfd_subelems);
+	msg->wfd_subelems = NULL;
+#endif /* CONFIG_WIFI_DISPLAY */
 }
 
 
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 55a3b90..42447e5 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -16,8 +16,8 @@
 
 
 /*
- * Number of retries to attempt for provision discovery requests during IDLE
- * state in case the peer is not listening.
+ * Number of retries to attempt for provision discovery requests
+ * in case the peer is not listening.
  */
 #define MAX_PROV_DISC_REQ_RETRIES 10
 
@@ -46,15 +46,22 @@
 {
 	struct wpabuf *buf;
 	u8 *len;
+	size_t extra = 0;
 
-	buf = wpabuf_alloc(1000);
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_prov_disc_req)
+		extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
 
 	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
 
 	len = p2p_buf_add_ie_hdr(buf);
-	p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
 	p2p_buf_add_device_info(buf, p2p, NULL);
 	if (go) {
 		p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
@@ -65,17 +72,46 @@
 	/* WPS IE with Config Methods attribute */
 	p2p_build_wps_ie_config_methods(buf, config_methods);
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (p2p->wfd_ie_prov_disc_req)
+		wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	return buf;
 }
 
 
 static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
 						u8 dialog_token,
-						u16 config_methods)
+						u16 config_methods,
+						const u8 *group_id,
+						size_t group_id_len)
 {
 	struct wpabuf *buf;
+	size_t extra = 0;
 
-	buf = wpabuf_alloc(100);
+#ifdef CONFIG_WIFI_DISPLAY
+	struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
+	if (wfd_ie && group_id) {
+		size_t i;
+		for (i = 0; i < p2p->num_groups; i++) {
+			struct p2p_group *g = p2p->groups[i];
+			struct wpabuf *ie;
+			if (!p2p_group_is_group_id_match(g, group_id,
+							 group_id_len))
+				continue;
+			ie = p2p_group_get_wfd_ie(g);
+			if (ie) {
+				wfd_ie = ie;
+				break;
+			}
+		}
+	}
+	if (wfd_ie)
+		extra = wpabuf_len(wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+	buf = wpabuf_alloc(100 + extra);
 	if (buf == NULL)
 		return NULL;
 
@@ -84,6 +120,11 @@
 	/* WPS IE with Config Methods attribute */
 	p2p_build_wps_ie_config_methods(buf, config_methods);
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (wfd_ie)
+		wpabuf_put_buf(buf, wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	return buf;
 }
 
@@ -110,11 +151,15 @@
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Provision Discovery Request from "
 			"unknown peer " MACSTR, MAC2STR(sa));
-		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1, 0))
+		{
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			        "P2P: Provision Discovery Request add device "
 				"failed " MACSTR, MAC2STR(sa));
 		}
+	} else if (msg.wfd_subelems) {
+		wpabuf_free(dev->info.wfd_subelems);
+		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
 	}
 
 	if (!(msg.wps_config_methods &
@@ -125,6 +170,21 @@
 		goto out;
 	}
 
+	if (msg.group_id) {
+		size_t i;
+		for (i = 0; i < p2p->num_groups; i++) {
+			if (p2p_group_is_group_id_match(p2p->groups[i],
+							msg.group_id,
+							msg.group_id_len))
+				break;
+		}
+		if (i == p2p->num_groups) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: PD "
+				"request for unknown P2P Group ID - reject");
+			goto out;
+		}
+	}
+
 	if (dev)
 		dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
 				P2P_DEV_PD_PEER_KEYPAD);
@@ -145,7 +205,8 @@
 
 out:
 	resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
-					reject ? 0 : msg.wps_config_methods);
+					reject ? 0 : msg.wps_config_methods,
+					msg.group_id, msg.group_id_len);
 	if (resp == NULL) {
 		p2p_parse_free(&msg);
 		return;
@@ -198,6 +259,7 @@
 	struct p2p_message msg;
 	struct p2p_device *dev;
 	u16 report_config_methods = 0;
+	int success = 0;
 
 	if (p2p_parse(data, len, &msg))
 		return;
@@ -216,11 +278,6 @@
 		return;
 	}
 
-	if (p2p->pending_action_state == P2P_PENDING_PD) {
-		os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
-		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
-	}
-
 	if (dev->dialog_token != msg.dialog_token) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Ignore Provision Discovery Response with "
@@ -230,6 +287,11 @@
 		return;
 	}
 
+	if (p2p->pending_action_state == P2P_PENDING_PD) {
+		os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
+		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	}
+
 	/*
 	 * If the response is from the peer to whom a user initiated request
 	 * was sent earlier, we reset that state info here.
@@ -266,11 +328,21 @@
 	dev->wps_prov_info = msg.wps_config_methods;
 
 	p2p_parse_free(&msg);
+	success = 1;
 
 out:
 	dev->req_config_methods = 0;
 	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
-	if (p2p->cfg->prov_disc_resp)
+	if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Start GO Neg after the PD-before-GO-Neg "
+			"workaround with " MACSTR,
+			MAC2STR(dev->info.p2p_device_addr));
+		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
+		p2p_connect_send(p2p, dev);
+		return;
+	}
+	if (success && p2p->cfg->prov_disc_resp)
 		p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
 					 report_config_methods);
 }
@@ -316,6 +388,8 @@
 	if (req == NULL)
 		return -1;
 
+	if (p2p->state != P2P_IDLE)
+		p2p_stop_listen_for_freq(p2p, freq);
 	p2p->pending_action_state = P2P_PENDING_PD;
 	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
 			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
@@ -378,8 +452,7 @@
 	 */
 	p2p->user_initiated_pd = !join;
 
-	/* Also set some retries to attempt in case of IDLE state */
-	if (p2p->user_initiated_pd && p2p->state == P2P_IDLE)
+	if (p2p->user_initiated_pd)
 		p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
 
 	return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index 37b3f7b..bf75605 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -15,15 +15,55 @@
 #include "p2p.h"
 
 
+#ifdef CONFIG_WIFI_DISPLAY
+static int wfd_wsd_supported(struct wpabuf *wfd)
+{
+	const u8 *pos, *end;
+	u8 subelem;
+	u16 len;
+
+	if (wfd == NULL)
+		return 0;
+
+	pos = wpabuf_head(wfd);
+	end = pos + wpabuf_len(wfd);
+
+	while (pos + 3 <= end) {
+		subelem = *pos++;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (pos + len > end)
+			break;
+
+		if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) {
+			u16 info = WPA_GET_BE16(pos);
+			return !!(info & 0x0040);
+		}
+
+		pos += len;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
 struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
 					 struct p2p_device *dev)
 {
 	struct p2p_sd_query *q;
+	int wsd = 0;
 
 	if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY))
 		return NULL; /* peer does not support SD */
+#ifdef CONFIG_WIFI_DISPLAY
+	if (wfd_wsd_supported(dev->info.wfd_subelems))
+		wsd = 1;
+#endif /* CONFIG_WIFI_DISPLAY */
 
 	for (q = p2p->sd_queries; q; q = q->next) {
+		/* Use WSD only if the peer indicates support or it */
+		if (q->wsd && !wsd)
+			continue;
 		if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO))
 			return q;
 		if (!q->for_all_peers &&
@@ -364,9 +404,14 @@
 				"previous SD response");
 			wpabuf_free(p2p->sd_resp);
 		}
+		p2p->sd_resp = wpabuf_dup(resp_tlvs);
+		if (p2p->sd_resp == NULL) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR, "P2P: Failed to "
+				"allocate SD response fragmentation area");
+			return;
+		}
 		os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN);
 		p2p->sd_resp_dialog_token = dialog_token;
-		p2p->sd_resp = wpabuf_dup(resp_tlvs);
 		p2p->sd_resp_pos = 0;
 		p2p->sd_frag_id = 0;
 		resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS,
@@ -404,9 +449,18 @@
 	u16 slen;
 	u16 update_indic;
 
+#ifdef ANDROID_P2P
+	if (p2p->state != P2P_SD_DURING_FIND) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: #### Not ignoring unexpected GAS Initial Response from "
+			MACSTR " state %d", MAC2STR(sa), p2p->state);
+	}
+	if (p2p->sd_peer == NULL ||
+#else
 	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+#endif
 	    os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
-		wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR,
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Ignore unexpected GAS Initial Response from "
 			MACSTR, MAC2STR(sa));
 		return;
@@ -645,7 +699,16 @@
 
 	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len);
 
+#ifdef ANDROID_P2P
+	if (p2p->state != P2P_SD_DURING_FIND) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: #### Not ignoring unexpected GAS Comeback Response from "
+			MACSTR " state %d", MAC2STR(sa), p2p->state);
+	}
+	if (p2p->sd_peer == NULL ||
+#else
 	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+#endif
 	    os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Ignore unexpected GAS Comeback Response from "
@@ -842,7 +905,7 @@
 {
 	struct p2p_sd_query *q;
 #ifdef ANDROID_P2P
-	/* Currently, supplicant doesn't support more than one pending broadcast SD request. 
+	/* Currently, supplicant doesn't support more than one pending broadcast SD request.
 	 * So reject if application is registering another one before cancelling the existing one.
 	 */
 	for (q = p2p->sd_queries; q; q = q->next) {
@@ -872,7 +935,7 @@
 
 	q->next = p2p->sd_queries;
 	p2p->sd_queries = q;
-	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p for_all_peers %d", q, q->for_all_peers);
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q);
 
 	if (dst == NULL) {
 		struct p2p_device *dev;
@@ -884,33 +947,56 @@
 }
 
 
+#ifdef CONFIG_WIFI_DISPLAY
+void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst,
+			  const struct wpabuf *tlvs)
+{
+	struct p2p_sd_query *q;
+	q = p2p_sd_request(p2p, dst, tlvs);
+	if (q)
+		q->wsd = 1;
+	return q;
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+#ifdef ANDROID_P2P
+void p2p_sd_service_update(struct p2p_data *p2p, int action)
+#else
 void p2p_sd_service_update(struct p2p_data *p2p)
+#endif
 {
 	p2p->srv_update_indic++;
+#ifdef ANDROID_P2P
+	if(action == SRV_FLUSH)
+		p2p->srv_count = 0;
+	else if (action == SRV_DEL)
+		p2p->srv_count--;
+	else if (action == SRV_ADD)
+		p2p->srv_count++;
+
+	if(p2p->cfg->sd_request) {
+		if (p2p->srv_count == 1) {
+			/* First Service Registered. Enable SD capability */
+			p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+		} else if (p2p->srv_count == 0 && !p2p->sd_queries) {
+			/* No services remaining + No queries registered .
+			 * Remove the SD Capability 
+			 */
+			p2p->dev_capab &= ~P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+		}
+	}
+#endif
 }
 
 
 int p2p_sd_cancel_request(struct p2p_data *p2p, void *req)
 {
 	if (p2p_unlink_sd_query(p2p, req)) {
-#ifdef ANDROID_P2P
-	struct p2p_device *dev;
-	struct p2p_sd_query *q = (struct p2p_sd_query *)req;
-#endif
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Cancel pending SD query %p", req);
 #ifdef ANDROID_P2P
-		/* If the request is a bcast query, then clear the
-		 * P2P_DEV_SD_INFO flag so that when new sd query is registered,
-		 * we will send the SD request frames to peer devices.
-		 */
-		if(q->for_all_peers) {
-			p2p->sd_dev_list = NULL;
-			dl_list_for_each(dev, &p2p->devices,
-							struct p2p_device, list) {
-				dev->flags &= ~P2P_DEV_SD_INFO;
-			}
-		}
+		p2p->sd_dev_list = NULL;
 #endif
 		p2p_free_sd_query(req);
 		return 0;
diff --git a/src/radius/radius.c b/src/radius/radius.c
index 0dd6b12..d5edfd8 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2011-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -78,8 +78,8 @@
 
 static int radius_msg_initialize(struct radius_msg *msg)
 {
-	msg->attr_pos =
-		os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos));
+	msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT,
+				  sizeof(*msg->attr_pos));
 	if (msg->attr_pos == NULL)
 		return -1;
 
@@ -147,6 +147,12 @@
 	case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
 	case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
 	case RADIUS_CODE_RESERVED: return "Reserved";
+	case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request";
+	case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK";
+	case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK";
+	case RADIUS_CODE_COA_REQUEST: return "CoA-Request";
+	case RADIUS_CODE_COA_ACK: return "CoA-ACK";
+	case RADIUS_CODE_COA_NAK: return "CoA-NAK";
 	default: return "?Unknown?";
 	}
 }
@@ -222,9 +228,10 @@
 	  RADIUS_ATTR_HEXDUMP },
 	{ RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
 	  RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
+	{ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity",
 	  RADIUS_ATTR_TEXT },
 	{ RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
+	{ RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }
 };
 #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
 
@@ -262,7 +269,7 @@
 	printf("   Attribute %d (%s) length=%d\n",
 	       hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
 
-	if (attr == NULL)
+	if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
 		return;
 
 	len = hdr->length - sizeof(struct radius_attr_hdr);
@@ -325,7 +332,7 @@
 
 	printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
 	       msg->hdr->code, radius_code_string(msg->hdr->code),
-	       msg->hdr->identifier, ntohs(msg->hdr->length));
+	       msg->hdr->identifier, be_to_host16(msg->hdr->length));
 
 	for (i = 0; i < msg->attr_used; i++) {
 		struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
@@ -350,11 +357,11 @@
 				   "Message-Authenticator");
 			return -1;
 		}
-		msg->hdr->length = htons(wpabuf_len(msg->buf));
+		msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 		hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
 			 wpabuf_len(msg->buf), (u8 *) (attr + 1));
 	} else
-		msg->hdr->length = htons(wpabuf_len(msg->buf));
+		msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 
 	if (wpabuf_len(msg->buf) > 0xffff) {
 		wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
@@ -380,7 +387,7 @@
 		printf("WARNING: Could not add Message-Authenticator\n");
 		return -1;
 	}
-	msg->hdr->length = htons(wpabuf_len(msg->buf));
+	msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 	os_memcpy(msg->hdr->authenticator, req_authenticator,
 		  sizeof(msg->hdr->authenticator));
 	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
@@ -406,13 +413,52 @@
 }
 
 
+int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len,
+			       const struct radius_hdr *req_hdr)
+{
+	const u8 *addr[2];
+	size_t len[2];
+	u8 auth[MD5_MAC_LEN];
+	struct radius_attr_hdr *attr;
+
+	os_memset(auth, 0, MD5_MAC_LEN);
+	attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+				   auth, MD5_MAC_LEN);
+	if (attr == NULL) {
+		wpa_printf(MSG_WARNING, "Could not add Message-Authenticator");
+		return -1;
+	}
+
+	msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
+	os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
+	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+		 wpabuf_len(msg->buf), (u8 *) (attr + 1));
+
+	/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+	addr[0] = wpabuf_head_u8(msg->buf);
+	len[0] = wpabuf_len(msg->buf);
+	addr[1] = secret;
+	len[1] = secret_len;
+	if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0)
+		return -1;
+
+	if (wpabuf_len(msg->buf) > 0xffff) {
+		wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
+			   (unsigned long) wpabuf_len(msg->buf));
+		return -1;
+	}
+	return 0;
+}
+
+
 void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
 			    size_t secret_len)
 {
 	const u8 *addr[2];
 	size_t len[2];
 
-	msg->hdr->length = htons(wpabuf_len(msg->buf));
+	msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 	os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
 	addr[0] = wpabuf_head(msg->buf);
 	len[0] = wpabuf_len(msg->buf);
@@ -427,6 +473,88 @@
 }
 
 
+int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len)
+{
+	const u8 *addr[4];
+	size_t len[4];
+	u8 zero[MD5_MAC_LEN];
+	u8 hash[MD5_MAC_LEN];
+
+	os_memset(zero, 0, sizeof(zero));
+	addr[0] = (u8 *) msg->hdr;
+	len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
+	addr[1] = zero;
+	len[1] = MD5_MAC_LEN;
+	addr[2] = (u8 *) (msg->hdr + 1);
+	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+	addr[3] = secret;
+	len[3] = secret_len;
+	md5_vector(4, addr, len, hash);
+	return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
+}
+
+
+int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
+			      size_t secret_len)
+{
+	const u8 *addr[4];
+	size_t len[4];
+	u8 zero[MD5_MAC_LEN];
+	u8 hash[MD5_MAC_LEN];
+	u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
+	u8 orig_authenticator[16];
+
+	struct radius_attr_hdr *attr = NULL, *tmp;
+	size_t i;
+
+	os_memset(zero, 0, sizeof(zero));
+	addr[0] = (u8 *) msg->hdr;
+	len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
+	addr[1] = zero;
+	len[1] = MD5_MAC_LEN;
+	addr[2] = (u8 *) (msg->hdr + 1);
+	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+	addr[3] = secret;
+	len[3] = secret_len;
+	md5_vector(4, addr, len, hash);
+	if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
+		return 1;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		tmp = radius_get_attr_hdr(msg, i);
+		if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
+			if (attr != NULL) {
+				wpa_printf(MSG_WARNING, "Multiple "
+					   "Message-Authenticator attributes "
+					   "in RADIUS message");
+				return 1;
+			}
+			attr = tmp;
+		}
+	}
+
+	if (attr == NULL) {
+		/* Message-Authenticator is MAY; not required */
+		return 0;
+	}
+
+	os_memcpy(orig, attr + 1, MD5_MAC_LEN);
+	os_memset(attr + 1, 0, MD5_MAC_LEN);
+	os_memcpy(orig_authenticator, msg->hdr->authenticator,
+		  sizeof(orig_authenticator));
+	os_memset(msg->hdr->authenticator, 0,
+		  sizeof(msg->hdr->authenticator));
+	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+		 wpabuf_len(msg->buf), auth);
+	os_memcpy(attr + 1, orig, MD5_MAC_LEN);
+	os_memcpy(msg->hdr->authenticator, orig_authenticator,
+		  sizeof(orig_authenticator));
+
+	return os_memcmp(orig, auth, MD5_MAC_LEN) != 0;
+}
+
+
 static int radius_msg_add_attr_to_array(struct radius_msg *msg,
 					struct radius_attr_hdr *attr)
 {
@@ -434,8 +562,8 @@
 		size_t *nattr_pos;
 		int nlen = msg->attr_size * 2;
 
-		nattr_pos = os_realloc(msg->attr_pos,
-				       nlen * sizeof(*msg->attr_pos));
+		nattr_pos = os_realloc_array(msg->attr_pos, nlen,
+					     sizeof(*msg->attr_pos));
 		if (nattr_pos == NULL)
 			return -1;
 
@@ -505,7 +633,7 @@
 
 	hdr = (struct radius_hdr *) data;
 
-	msg_len = ntohs(hdr->length);
+	msg_len = be_to_host16(hdr->length);
 	if (msg_len < sizeof(*hdr) || msg_len > len) {
 		wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
 		return NULL;
@@ -579,9 +707,9 @@
 }
 
 
-u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
+struct wpabuf * radius_msg_get_eap(struct radius_msg *msg)
 {
-	u8 *eap, *pos;
+	struct wpabuf *eap;
 	size_t len, i;
 	struct radius_attr_hdr *attr;
 
@@ -591,30 +719,27 @@
 	len = 0;
 	for (i = 0; i < msg->attr_used; i++) {
 		attr = radius_get_attr_hdr(msg, i);
-		if (attr->type == RADIUS_ATTR_EAP_MESSAGE)
+		if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
+		    attr->length > sizeof(struct radius_attr_hdr))
 			len += attr->length - sizeof(struct radius_attr_hdr);
 	}
 
 	if (len == 0)
 		return NULL;
 
-	eap = os_malloc(len);
+	eap = wpabuf_alloc(len);
 	if (eap == NULL)
 		return NULL;
 
-	pos = eap;
 	for (i = 0; i < msg->attr_used; i++) {
 		attr = radius_get_attr_hdr(msg, i);
-		if (attr->type == RADIUS_ATTR_EAP_MESSAGE) {
+		if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
+		    attr->length > sizeof(struct radius_attr_hdr)) {
 			int flen = attr->length - sizeof(*attr);
-			os_memcpy(pos, attr + 1, flen);
-			pos += flen;
+			wpabuf_put_data(eap, attr + 1, flen);
 		}
 	}
 
-	if (eap_len)
-		*eap_len = len;
-
 	return eap;
 }
 
@@ -715,7 +840,7 @@
 
 	for (i = 0; i < src->attr_used; i++) {
 		attr = radius_get_attr_hdr(src, i);
-		if (attr->type == type) {
+		if (attr->type == type && attr->length >= sizeof(*attr)) {
 			if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
 						 attr->length - sizeof(*attr)))
 				return -1;
@@ -772,7 +897,8 @@
 		u32 vendor_id;
 		struct radius_attr_vendor *vhdr;
 
-		if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
+		if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC ||
+		    attr->length < sizeof(*attr))
 			continue;
 
 		left = attr->length - sizeof(*attr);
@@ -1145,7 +1271,7 @@
 		}
 	}
 
-	if (!attr)
+	if (!attr || attr->length < sizeof(*attr))
 		return -1;
 
 	dlen = attr->length - sizeof(*attr);
@@ -1170,7 +1296,7 @@
 		}
 	}
 
-	if (!attr)
+	if (!attr || attr->length < sizeof(*attr))
 		return -1;
 
 	*buf = (u8 *) (attr + 1);
@@ -1221,6 +1347,8 @@
 
 	for (i = 0; i < msg->attr_used; i++) {
 		attr = radius_get_attr_hdr(msg, i);
+		if (attr->length < sizeof(*attr))
+			return -1;
 		data = (const u8 *) (attr + 1);
 		dlen = attr->length - sizeof(*attr);
 		if (attr->length < 3)
@@ -1406,7 +1534,7 @@
 	if (src->attr == NULL)
 		return 0;
 
-	dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data));
+	dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data));
 	if (dst->attr == NULL)
 		return -1;
 
@@ -1424,3 +1552,24 @@
 
 	return 0;
 }
+
+
+u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs)
+{
+	size_t i, j;
+	struct radius_attr_hdr *attr;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		attr = radius_get_attr_hdr(msg, i);
+
+		for (j = 0; attrs[j]; j++) {
+			if (attr->type == attrs[j])
+				break;
+		}
+
+		if (attrs[j] == 0)
+			return attr->type; /* unlisted attr */
+	}
+
+	return 0;
+}
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 44123bd..727640b 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,7 +18,7 @@
 struct radius_hdr {
 	u8 code;
 	u8 identifier;
-	u16 length; /* including this header */
+	be16 length; /* including this header */
 	u8 authenticator[16];
 	/* followed by length-20 octets of attributes */
 } STRUCT_PACKED;
@@ -31,6 +31,12 @@
        RADIUS_CODE_ACCESS_CHALLENGE = 11,
        RADIUS_CODE_STATUS_SERVER = 12,
        RADIUS_CODE_STATUS_CLIENT = 13,
+       RADIUS_CODE_DISCONNECT_REQUEST = 40,
+       RADIUS_CODE_DISCONNECT_ACK = 41,
+       RADIUS_CODE_DISCONNECT_NAK = 42,
+       RADIUS_CODE_COA_REQUEST = 43,
+       RADIUS_CODE_COA_ACK = 44,
+       RADIUS_CODE_COA_NAK = 45,
        RADIUS_CODE_RESERVED = 255
 };
 
@@ -83,7 +89,8 @@
        RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
        RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
        RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
-       RADIUS_ATTR_NAS_IPV6_ADDRESS = 95
+       RADIUS_ATTR_NAS_IPV6_ADDRESS = 95,
+       RADIUS_ATTR_ERROR_CAUSE = 101
 };
 
 
@@ -192,14 +199,21 @@
 		      size_t secret_len);
 int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
 			  size_t secret_len, const u8 *req_authenticator);
+int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len,
+			       const struct radius_hdr *req_hdr);
 void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
 			    size_t secret_len);
+int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len);
+int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len);
 struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
 					     const u8 *data, size_t data_len);
 struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
 int radius_msg_add_eap(struct radius_msg *msg, const u8 *data,
 		       size_t data_len);
-u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len);
+struct wpabuf * radius_msg_get_eap(struct radius_msg *msg);
 int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
 		      size_t secret_len, struct radius_msg *sent_msg,
 		      int auth);
@@ -268,4 +282,6 @@
 int radius_copy_class(struct radius_class_data *dst,
 		      const struct radius_class_data *src);
 
+u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs);
+
 #endif /* RADIUS_H */
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index bcd471b..425ad93 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -281,8 +281,8 @@
 		num = &radius->num_auth_handlers;
 	}
 
-	newh = os_realloc(*handlers,
-			  (*num + 1) * sizeof(struct radius_rx_handler));
+	newh = os_realloc_array(*handlers, *num + 1,
+				sizeof(struct radius_rx_handler));
 	if (newh == NULL)
 		return -1;
 
@@ -505,7 +505,7 @@
 			       NULL);
 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
 		       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
-		       " %ld seconds\n", (long int) (first - now.sec));
+		       " %ld seconds", (long int) (first - now.sec));
 }
 
 
@@ -678,7 +678,7 @@
 	radius_client_list_add(radius, msg, msg_type, shared_secret,
 			       shared_secret_len, addr);
 
-	return res;
+	return 0;
 }
 
 
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
new file mode 100644
index 0000000..bded965
--- /dev/null
+++ b/src/radius/radius_das.c
@@ -0,0 +1,364 @@
+/*
+ * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <net/if.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/ip_addr.h"
+#include "radius.h"
+#include "radius_das.h"
+
+
+extern int wpa_debug_level;
+
+
+struct radius_das_data {
+	int sock;
+	u8 *shared_secret;
+	size_t shared_secret_len;
+	struct hostapd_ip_addr client_addr;
+	unsigned int time_window;
+	int require_event_timestamp;
+	void *ctx;
+	enum radius_das_res (*disconnect)(void *ctx,
+					  struct radius_das_attrs *attr);
+};
+
+
+static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
+						 struct radius_msg *msg,
+						 const char *abuf,
+						 int from_port)
+{
+	struct radius_hdr *hdr;
+	struct radius_msg *reply;
+	u8 allowed[] = {
+		RADIUS_ATTR_USER_NAME,
+		RADIUS_ATTR_CALLING_STATION_ID,
+		RADIUS_ATTR_ACCT_SESSION_ID,
+		RADIUS_ATTR_EVENT_TIMESTAMP,
+		RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+		RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+		0
+	};
+	int error = 405;
+	u8 attr;
+	enum radius_das_res res;
+	struct radius_das_attrs attrs;
+	u8 *buf;
+	size_t len;
+	char tmp[100];
+	u8 sta_addr[ETH_ALEN];
+
+	hdr = radius_msg_get_hdr(msg);
+
+	attr = radius_msg_find_unlisted_attr(msg, allowed);
+	if (attr) {
+		wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
+			   "Disconnect-Request from %s:%d", attr,
+			   abuf, from_port);
+		error = 401;
+		goto fail;
+	}
+
+	os_memset(&attrs, 0, sizeof(attrs));
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+				    &buf, &len, NULL) == 0) {
+		if (len >= sizeof(tmp))
+			len = sizeof(tmp) - 1;
+		os_memcpy(tmp, buf, len);
+		tmp[len] = '\0';
+		if (hwaddr_aton2(tmp, sta_addr) < 0) {
+			wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
+				   "'%s' from %s:%d", tmp, abuf, from_port);
+			error = 407;
+			goto fail;
+		}
+		attrs.sta_addr = sta_addr;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+				    &buf, &len, NULL) == 0) {
+		attrs.user_name = buf;
+		attrs.user_name_len = len;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+				    &buf, &len, NULL) == 0) {
+		attrs.acct_session_id = buf;
+		attrs.acct_session_id_len = len;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+				    &buf, &len, NULL) == 0) {
+		attrs.cui = buf;
+		attrs.cui_len = len;
+	}
+
+	res = das->disconnect(das->ctx, &attrs);
+	switch (res) {
+	case RADIUS_DAS_NAS_MISMATCH:
+		wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
+			   abuf, from_port);
+		error = 403;
+		break;
+	case RADIUS_DAS_SESSION_NOT_FOUND:
+		wpa_printf(MSG_INFO, "DAS: Session not found for request from "
+			   "%s:%d", abuf, from_port);
+		error = 503;
+		break;
+	case RADIUS_DAS_SUCCESS:
+		error = 0;
+		break;
+	}
+
+fail:
+	reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
+			       RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
+	if (reply == NULL)
+		return NULL;
+
+	if (error) {
+		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
+					       error)) {
+			radius_msg_free(reply);
+			return NULL;
+		}
+	}
+
+	return reply;
+}
+
+
+static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct radius_das_data *das = eloop_ctx;
+	u8 buf[1500];
+	union {
+		struct sockaddr_storage ss;
+		struct sockaddr_in sin;
+#ifdef CONFIG_IPV6
+		struct sockaddr_in6 sin6;
+#endif /* CONFIG_IPV6 */
+	} from;
+	char abuf[50];
+	int from_port = 0;
+	socklen_t fromlen;
+	int len;
+	struct radius_msg *msg, *reply = NULL;
+	struct radius_hdr *hdr;
+	struct wpabuf *rbuf;
+	u32 val;
+	int res;
+	struct os_time now;
+
+	fromlen = sizeof(from);
+	len = recvfrom(sock, buf, sizeof(buf), 0,
+		       (struct sockaddr *) &from.ss, &fromlen);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
+		return;
+	}
+
+	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+	from_port = ntohs(from.sin.sin_port);
+
+	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
+		   len, abuf, from_port);
+	if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
+		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
+		return;
+	}
+
+	msg = radius_msg_parse(buf, len);
+	if (msg == NULL) {
+		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
+			   "from %s:%d failed", abuf, from_port);
+		return;
+	}
+
+	if (wpa_debug_level <= MSG_MSGDUMP)
+		radius_msg_dump(msg);
+
+	if (radius_msg_verify_das_req(msg, das->shared_secret,
+				       das->shared_secret_len)) {
+		wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
+			   "from %s:%d - drop", abuf, from_port);
+		goto fail;
+	}
+
+	os_get_time(&now);
+	res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+				  (u8 *) &val, 4);
+	if (res == 4) {
+		u32 timestamp = ntohl(val);
+		if (abs(now.sec - timestamp) > das->time_window) {
+			wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
+				   "Event-Timestamp (%u; local time %u) in "
+				   "packet from %s:%d - drop",
+				   timestamp, (unsigned int) now.sec,
+				   abuf, from_port);
+			goto fail;
+		}
+	} else if (das->require_event_timestamp) {
+		wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
+			   "from %s:%d - drop", abuf, from_port);
+		goto fail;
+	}
+
+	hdr = radius_msg_get_hdr(msg);
+
+	switch (hdr->code) {
+	case RADIUS_CODE_DISCONNECT_REQUEST:
+		reply = radius_das_disconnect(das, msg, abuf, from_port);
+		break;
+	case RADIUS_CODE_COA_REQUEST:
+		/* TODO */
+		reply = radius_msg_new(RADIUS_CODE_COA_NAK,
+				       hdr->identifier);
+		if (reply == NULL)
+			break;
+
+		/* Unsupported Service */
+		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
+					       405)) {
+			radius_msg_free(reply);
+			reply = NULL;
+			break;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
+			   "packet from %s:%d",
+			   hdr->code, abuf, from_port);
+	}
+
+	if (reply) {
+		wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
+
+		if (!radius_msg_add_attr_int32(reply,
+					       RADIUS_ATTR_EVENT_TIMESTAMP,
+					       now.sec)) {
+			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
+				   "Event-Timestamp attribute");
+		}
+
+		if (radius_msg_finish_das_resp(reply, das->shared_secret,
+					       das->shared_secret_len, hdr) <
+		    0) {
+			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
+				   "Message-Authenticator attribute");
+		}
+
+		if (wpa_debug_level <= MSG_MSGDUMP)
+			radius_msg_dump(reply);
+
+		rbuf = radius_msg_get_buf(reply);
+		res = sendto(das->sock, wpabuf_head(rbuf),
+			     wpabuf_len(rbuf), 0,
+			     (struct sockaddr *) &from.ss, fromlen);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
+				   abuf, from_port, strerror(errno));
+		}
+	}
+
+fail:
+	radius_msg_free(msg);
+	radius_msg_free(reply);
+}
+
+
+static int radius_das_open_socket(int port)
+{
+	int s;
+	struct sockaddr_in addr;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		perror("socket");
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("bind");
+		close(s);
+		return -1;
+	}
+
+	return s;
+}
+
+
+struct radius_das_data *
+radius_das_init(struct radius_das_conf *conf)
+{
+	struct radius_das_data *das;
+
+	if (conf->port == 0 || conf->shared_secret == NULL ||
+	    conf->client_addr == NULL)
+		return NULL;
+
+	das = os_zalloc(sizeof(*das));
+	if (das == NULL)
+		return NULL;
+
+	das->time_window = conf->time_window;
+	das->require_event_timestamp = conf->require_event_timestamp;
+	das->ctx = conf->ctx;
+	das->disconnect = conf->disconnect;
+
+	os_memcpy(&das->client_addr, conf->client_addr,
+		  sizeof(das->client_addr));
+
+	das->shared_secret = os_malloc(conf->shared_secret_len);
+	if (das->shared_secret == NULL) {
+		radius_das_deinit(das);
+		return NULL;
+	}
+	os_memcpy(das->shared_secret, conf->shared_secret,
+		  conf->shared_secret_len);
+	das->shared_secret_len = conf->shared_secret_len;
+
+	das->sock = radius_das_open_socket(conf->port);
+	if (das->sock < 0) {
+		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
+			   "DAS");
+		radius_das_deinit(das);
+		return NULL;
+	}
+
+	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
+	{
+		radius_das_deinit(das);
+		return NULL;
+	}
+
+	return das;
+}
+
+
+void radius_das_deinit(struct radius_das_data *das)
+{
+	if (das == NULL)
+		return;
+
+	if (das->sock >= 0) {
+		eloop_unregister_read_sock(das->sock);
+		close(das->sock);
+	}
+
+	os_free(das->shared_secret);
+	os_free(das);
+}
diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
new file mode 100644
index 0000000..738b18b
--- /dev/null
+++ b/src/radius/radius_das.h
@@ -0,0 +1,47 @@
+/*
+ * RADIUS Dynamic Authorization Server (DAS)
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RADIUS_DAS_H
+#define RADIUS_DAS_H
+
+struct radius_das_data;
+
+enum radius_das_res {
+	RADIUS_DAS_SUCCESS,
+	RADIUS_DAS_NAS_MISMATCH,
+	RADIUS_DAS_SESSION_NOT_FOUND
+};
+
+struct radius_das_attrs {
+	const u8 *sta_addr;
+	const u8 *user_name;
+	size_t user_name_len;
+	const u8 *acct_session_id;
+	size_t acct_session_id_len;
+	const u8 *cui;
+	size_t cui_len;
+};
+
+struct radius_das_conf {
+	int port;
+	const u8 *shared_secret;
+	size_t shared_secret_len;
+	const struct hostapd_ip_addr *client_addr;
+	unsigned int time_window;
+	int require_event_timestamp;
+	void *ctx;
+	enum radius_das_res (*disconnect)(void *ctx,
+					  struct radius_das_attrs *attr);
+};
+
+struct radius_das_data *
+radius_das_init(struct radius_das_conf *conf);
+
+void radius_das_deinit(struct radius_das_data *data);
+
+#endif /* RADIUS_DAS_H */
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index 123c59b..5b2d711 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -689,8 +689,7 @@
 				 const char *from_addr, int from_port,
 				 struct radius_session *force_sess)
 {
-	u8 *eap = NULL;
-	size_t eap_len;
+	struct wpabuf *eap = NULL;
 	int res, state_included = 0;
 	u8 statebuf[4];
 	unsigned int state;
@@ -754,7 +753,7 @@
 		return -1;
 	}
 		      
-	eap = radius_msg_get_eap(msg, &eap_len);
+	eap = radius_msg_get_eap(msg);
 	if (eap == NULL) {
 		RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
 			     from_addr);
@@ -763,7 +762,7 @@
 		return -1;
 	}
 
-	RADIUS_DUMP("Received EAP data", eap, eap_len);
+	RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap));
 
 	/* FIX: if Code is Request, Success, or Failure, send Access-Reject;
 	 * RFC3579 Sect. 2.6.2.
@@ -773,10 +772,7 @@
 	 * Or is this already done by the EAP state machine? */
 
 	wpabuf_free(sess->eap_if->eapRespData);
-	sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len);
-	if (sess->eap_if->eapRespData == NULL)
-		os_free(eap);
-	eap = NULL;
+	sess->eap_if->eapRespData = eap;
 	sess->eap_if->eapResp = TRUE;
 	eap_server_sm_step(sess->eap);
 
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
index 3d5aac6..f2bac34 100644
--- a/src/rsn_supp/peerkey.c
+++ b/src/rsn_supp/peerkey.c
@@ -221,6 +221,9 @@
 	if (cipher & WPA_CIPHER_CCMP) {
 		wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
 		cipher = WPA_CIPHER_CCMP;
+	} else if (cipher & WPA_CIPHER_GCMP) {
+		wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey");
+		cipher = WPA_CIPHER_GCMP;
 	} else if (cipher & WPA_CIPHER_TKIP) {
 		wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
 		cipher = WPA_CIPHER_TKIP;
@@ -267,10 +270,7 @@
 	/* Include only the selected cipher in pairwise cipher suite */
 	WPA_PUT_LE16(pos, 1);
 	pos += 2;
-	if (cipher == WPA_CIPHER_CCMP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	else if (cipher == WPA_CIPHER_TKIP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher));
 	pos += RSN_SELECTOR_LEN;
 
 	hdr->len = (pos - peerkey->rsnie_p) - 2;
@@ -344,7 +344,7 @@
 
 	msg->type = EAPOL_KEY_TYPE_RSN;
 
-	if (peerkey->cipher == WPA_CIPHER_CCMP)
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
 		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 	else
 		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -352,7 +352,7 @@
 	key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK;
 	WPA_PUT_BE16(msg->key_info, key_info);
 
-	if (peerkey->cipher == WPA_CIPHER_CCMP)
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
 		WPA_PUT_BE16(msg->key_length, 16);
 	else
 		WPA_PUT_BE16(msg->key_length, 32);
@@ -403,7 +403,7 @@
 
 	msg->type = EAPOL_KEY_TYPE_RSN;
 
-	if (peerkey->cipher == WPA_CIPHER_CCMP)
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
 		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 	else
 		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -412,7 +412,7 @@
 		WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
 	WPA_PUT_BE16(msg->key_info, key_info);
 
-	if (peerkey->cipher == WPA_CIPHER_CCMP)
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
 		WPA_PUT_BE16(msg->key_length, 16);
 	else
 		WPA_PUT_BE16(msg->key_length, 32);
@@ -500,6 +500,9 @@
 	if (cipher & WPA_CIPHER_CCMP) {
 		wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
 		peerkey->cipher = WPA_CIPHER_CCMP;
+	} else if (cipher & WPA_CIPHER_GCMP) {
+		wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey");
+		peerkey->cipher = WPA_CIPHER_GCMP;
 	} else if (cipher & WPA_CIPHER_TKIP) {
 		wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
 		peerkey->cipher = WPA_CIPHER_TKIP;
@@ -1016,7 +1019,7 @@
 		return -1;
 	}
 
-	if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
+	if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
 		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 	else
 		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -1055,17 +1058,8 @@
 	count_pos = pos;
 	pos += 2;
 
-	count = 0;
-	if (sm->allowed_pairwise_cipher & WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-		pos += RSN_SELECTOR_LEN;
-		count++;
-	}
-	if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-		pos += RSN_SELECTOR_LEN;
-		count++;
-	}
+	count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher);
+	pos += count * RSN_SELECTOR_LEN;
 	WPA_PUT_LE16(count_pos, count);
 
 	hdr->len = (pos - peerkey->rsnie_i) - 2;
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index 2d265d0..9783e7c 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -197,11 +197,25 @@
 	if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
 		/* Remove the oldest entry to make room for the new entry */
 		pos = pmksa->pmksa;
-		pmksa->pmksa = pos->next;
-		wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
-			   "entry (for " MACSTR ") to make room for new one",
-			   MAC2STR(pos->aa));
-		pmksa_cache_free_entry(pmksa, pos, 0);
+
+		if (pos == pmksa->sm->cur_pmksa) {
+			/*
+			 * Never remove the current PMKSA cache entry, since
+			 * it's in use, and removing it triggers a needless
+			 * deauthentication.
+			 */
+			pos = pos->next;
+			pmksa->pmksa->next = pos ? pos->next : NULL;
+		} else
+			pmksa->pmksa = pos->next;
+
+		if (pos) {
+			wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle "
+				   "PMKSA cache entry (for " MACSTR ") to "
+				   "make room for new one",
+				   MAC2STR(pos->aa));
+			pmksa_cache_free_entry(pmksa, pos, 0);
+		}
 	}
 
 	/* Add the new entry; order by expiration time */
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 6ebc37a..7646ca8 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -2202,7 +2202,9 @@
 	if (sm == NULL)
 		return -1;
 
-	sm->l2_tdls = l2_packet_init(sm->ifname, sm->own_addr,
+	sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
+				     sm->ifname,
+				     sm->own_addr,
 				     ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
 				     sm, 0);
 	if (sm->l2_tdls == NULL) {
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 7159c3a..5cf32df 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -91,7 +91,7 @@
 
 	if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt))
 		ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
-	else if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
+	else if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
 		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 	else
 		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -190,14 +190,17 @@
 #endif /* CONFIG_IEEE80211R */
 		}
 		if (res == 0) {
+			struct rsn_pmksa_cache_entry *sa = NULL;
 			wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
 					"machines", sm->pmk, pmk_len);
 			sm->pmk_len = pmk_len;
 			if (sm->proto == WPA_PROTO_RSN &&
 			    !wpa_key_mgmt_ft(sm->key_mgmt)) {
-				pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len,
-						src_addr, sm->own_addr,
-						sm->network_ctx, sm->key_mgmt);
+				sa = pmksa_cache_add(sm->pmksa,
+						     sm->pmk, pmk_len,
+						     src_addr, sm->own_addr,
+						     sm->network_ctx,
+						     sm->key_mgmt);
 			}
 			if (!sm->cur_pmksa && pmkid &&
 			    pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL))
@@ -207,6 +210,9 @@
 					"PMKID");
 				abort_cached = 0;
 			}
+
+			if (!sm->cur_pmksa)
+				sm->cur_pmksa = sa;
 		} else {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Failed to get master session key from "
@@ -350,7 +356,7 @@
 			  const struct wpa_eapol_key *key,
 			  struct wpa_ptk *ptk)
 {
-	size_t ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64;
+	size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->key_mgmt))
 		return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len);
@@ -514,28 +520,23 @@
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 		"WPA: Installing PTK to the driver");
 
-	switch (sm->pairwise_cipher) {
-	case WPA_CIPHER_CCMP:
-		alg = WPA_ALG_CCMP;
-		keylen = 16;
-		rsclen = 6;
-		break;
-	case WPA_CIPHER_TKIP:
-		alg = WPA_ALG_TKIP;
-		keylen = 32;
-		rsclen = 6;
-		break;
-	case WPA_CIPHER_NONE:
+	if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher "
 			"Suite: NONE - do not use pairwise keys");
 		return 0;
-	default:
+	}
+
+	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Unsupported pairwise cipher %d",
 			sm->pairwise_cipher);
 		return -1;
 	}
 
+	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
+
 	if (sm->proto == WPA_PROTO_RSN) {
 		key_rsc = null_rsc;
 	} else {
@@ -568,55 +569,25 @@
 					     int *key_rsc_len,
 					     enum wpa_alg *alg)
 {
-	int ret = 0;
+	int klen;
 
-	switch (group_cipher) {
-	case WPA_CIPHER_CCMP:
-		if (keylen != 16 || maxkeylen < 16) {
-			ret = -1;
-			break;
-		}
-		*key_rsc_len = 6;
-		*alg = WPA_ALG_CCMP;
-		break;
-	case WPA_CIPHER_TKIP:
-		if (keylen != 32 || maxkeylen < 32) {
-			ret = -1;
-			break;
-		}
-		*key_rsc_len = 6;
-		*alg = WPA_ALG_TKIP;
-		break;
-	case WPA_CIPHER_WEP104:
-		if (keylen != 13 || maxkeylen < 13) {
-			ret = -1;
-			break;
-		}
-		*key_rsc_len = 0;
-		*alg = WPA_ALG_WEP;
-		break;
-	case WPA_CIPHER_WEP40:
-		if (keylen != 5 || maxkeylen < 5) {
-			ret = -1;
-			break;
-		}
-		*key_rsc_len = 0;
-		*alg = WPA_ALG_WEP;
-		break;
-	default:
+	*alg = wpa_cipher_to_alg(group_cipher);
+	if (*alg == WPA_ALG_NONE) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Unsupported Group Cipher %d",
 			group_cipher);
 		return -1;
 	}
+	*key_rsc_len = wpa_cipher_rsc_len(group_cipher);
 
-	if (ret < 0 ) {
+	klen = wpa_cipher_key_len(group_cipher);
+	if (keylen != klen || maxkeylen < klen) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Unsupported %s Group Cipher key length %d (%d)",
 			wpa_cipher_txt(group_cipher), keylen, maxkeylen);
+		return -1;
 	}
-
-	return ret;
+	return 0;
 }
 
 
@@ -1116,23 +1087,12 @@
 	}
 
 	keylen = WPA_GET_BE16(key->key_length);
-	switch (sm->pairwise_cipher) {
-	case WPA_CIPHER_CCMP:
-		if (keylen != 16) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: Invalid CCMP key length %d (src=" MACSTR
-				")", keylen, MAC2STR(sm->bssid));
-			goto failed;
-		}
-		break;
-	case WPA_CIPHER_TKIP:
-		if (keylen != 32) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: Invalid TKIP key length %d (src=" MACSTR
-				")", keylen, MAC2STR(sm->bssid));
-			goto failed;
-		}
-		break;
+	if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Invalid %s key length %d (src=" MACSTR
+			")", wpa_cipher_txt(sm->pairwise_cipher), keylen,
+			MAC2STR(sm->bssid));
+		goto failed;
 	}
 
 	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
@@ -1712,6 +1672,13 @@
 		} else
 			goto out;
 	}
+	if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
+	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: GCMP is used, but EAPOL-Key "
+			"descriptor version (%d) is not 2", ver);
+		goto out;
+	}
 
 #ifdef CONFIG_PEERKEY
 	for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
@@ -1846,23 +1813,6 @@
 
 
 #ifdef CONFIG_CTRL_IFACE
-static int wpa_cipher_bits(int cipher)
-{
-	switch (cipher) {
-	case WPA_CIPHER_CCMP:
-		return 128;
-	case WPA_CIPHER_TKIP:
-		return 256;
-	case WPA_CIPHER_WEP104:
-		return 104;
-	case WPA_CIPHER_WEP40:
-		return 40;
-	default:
-		return 0;
-	}
-}
-
-
 static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
 {
 	switch (sm->key_mgmt) {
@@ -1894,30 +1844,6 @@
 }
 
 
-static u32 wpa_cipher_suite(struct wpa_sm *sm, int cipher)
-{
-	switch (cipher) {
-	case WPA_CIPHER_CCMP:
-		return (sm->proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
-	case WPA_CIPHER_TKIP:
-		return (sm->proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
-	case WPA_CIPHER_WEP104:
-		return (sm->proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
-	case WPA_CIPHER_WEP40:
-		return (sm->proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
-	case WPA_CIPHER_NONE:
-		return (sm->proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
-	default:
-		return 0;
-	}
-}
-
-
 #define RSN_SUITE "%02x-%02x-%02x-%d"
 #define RSN_SUITE_ARG(s) \
 ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
@@ -1965,7 +1891,7 @@
 			  rsna ? "TRUE" : "FALSE",
 			  rsna ? "TRUE" : "FALSE",
 			  RSN_VERSION,
-			  wpa_cipher_bits(sm->group_cipher),
+			  wpa_cipher_key_len(sm->group_cipher) * 8,
 			  sm->dot11RSNAConfigPMKLifetime,
 			  sm->dot11RSNAConfigPMKReauthThreshold,
 			  sm->dot11RSNAConfigSATimeout);
@@ -1985,12 +1911,16 @@
 		"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n"
 		"dot11RSNA4WayHandshakeFailures=%u\n",
 		RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
-		RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)),
-		RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->pairwise_cipher)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->group_cipher)),
 		pmkid_txt,
 		RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
-		RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)),
-		RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->pairwise_cipher)),
+		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+						  sm->group_cipher)),
 		sm->dot11RSNA4WayHandshakeFailures);
 	if (ret >= 0 && (size_t) ret < buflen)
 		len += ret;
@@ -2659,3 +2589,102 @@
 	pmksa_cache_flush(sm->pmksa, network_ctx);
 #endif /* CONFIG_NO_WPA2 */
 }
+
+
+#ifdef CONFIG_IEEE80211V
+int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
+{
+	struct wpa_gtk_data gd;
+#ifdef CONFIG_IEEE80211W
+	struct wpa_igtk_kde igd;
+	u16 keyidx;
+#endif /* CONFIG_IEEE80211W */
+	u16 keyinfo;
+	u8 keylen;  /* plaintext key len */
+	u8 keydatalen;
+	u8 *key_rsc;
+
+	os_memset(&gd, 0, sizeof(gd));
+#ifdef CONFIG_IEEE80211W
+	os_memset(&igd, 0, sizeof(igd));
+#endif /* CONFIG_IEEE80211W */
+
+	keylen = wpa_cipher_key_len(sm->group_cipher);
+	gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
+	gd.alg = wpa_cipher_to_alg(sm->group_cipher);
+	if (gd.alg == WPA_ALG_NONE) {
+		wpa_printf(MSG_DEBUG, "Unsupported group cipher suite");
+		return -1;
+	}
+
+	if (subelem_id == WNM_SLEEP_SUBELEM_GTK) {
+		key_rsc = buf + 5;
+		keyinfo = WPA_GET_LE16(buf+2);
+		keydatalen = buf[1] - 11 - 8;
+		gd.gtk_len = keylen;
+		if (gd.gtk_len != buf[4]) {
+			wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d",
+				   gd.gtk_len, buf[4]);
+			return -1;
+		}
+		gd.keyidx = keyinfo & 0x03; /* B0 - B1 */
+		gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
+		         sm, !!(keyinfo & WPA_KEY_INFO_TXRX));
+
+		if (keydatalen % 8) {
+			wpa_printf(MSG_DEBUG, "WPA: Unsupported AES-WRAP len "
+				   "%d", keydatalen);
+			return -1;
+		}
+
+		if (aes_unwrap(sm->ptk.kek, keydatalen / 8, buf + 13, gd.gtk))
+		{
+			wpa_printf(MSG_WARNING, "WNM: AES unwrap failed - "
+				   "could not decrypt GTK");
+			return -1;
+		}
+
+		wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
+				gd.gtk, gd.gtk_len);
+		if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) {
+			wpa_printf(MSG_DEBUG, "Failed to install the GTK in "
+				   "WNM mode");
+			return -1;
+		}
+#ifdef CONFIG_IEEE80211W
+	} else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
+		if (buf[1] != 2 + 6 + WPA_IGTK_LEN + 8) {
+			wpa_printf(MSG_DEBUG, "WPA: Unsupported AES-WRAP len "
+				   "%d", buf[1] - 2 - 6 - 8);
+			return -1;
+		}
+		os_memcpy(igd.keyid, buf + 2, 2);
+		os_memcpy(igd.pn, buf + 4, 6);
+
+		keyidx = WPA_GET_LE16(igd.keyid);
+
+		if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, buf + 10,
+			       igd.igtk)) {
+			wpa_printf(MSG_WARNING, "WNM: AES unwrap failed - "
+				   "could not decrypr IGTK");
+			return -1;
+		}
+
+		wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
+				igd.igtk, WPA_IGTK_LEN);
+		if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr,
+				   keyidx, 0, igd.pn, sizeof(igd.pn),
+				   igd.igtk, WPA_IGTK_LEN) < 0) {
+			wpa_printf(MSG_DEBUG, "Failed to install the IGTK in "
+				   "WNM mode");
+			return -1;
+		}
+#endif /* CONFIG_IEEE80211W */
+	} else {
+		wpa_printf(MSG_DEBUG, "Unknown element id");
+		return -1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211V */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index a70b57b..1077b5a 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -366,4 +366,8 @@
 void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr);
 int wpa_tdls_is_external_setup(struct wpa_sm *sm);
 
+#ifdef CONFIG_IEEE80211V
+int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
+#endif /* CONFIG_IEEE80211V */
+
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index fec9544..2df060c 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -171,16 +171,16 @@
 	pos = (u8 *) (rsnie + 1);
 
 	/* Group Suite Selector */
-	if (sm->group_cipher == WPA_CIPHER_CCMP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	else if (sm->group_cipher == WPA_CIPHER_TKIP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-	else {
+	if (sm->group_cipher != WPA_CIPHER_CCMP &&
+	    sm->group_cipher != WPA_CIPHER_GCMP &&
+	    sm->group_cipher != WPA_CIPHER_TKIP) {
 		wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
 			   sm->group_cipher);
 		os_free(buf);
 		return NULL;
 	}
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+						  sm->group_cipher));
 	pos += RSN_SELECTOR_LEN;
 
 	/* Pairwise Suite Count */
@@ -188,16 +188,14 @@
 	pos += 2;
 
 	/* Pairwise Suite List */
-	if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	else if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-	else {
+	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
 		wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
 			   sm->pairwise_cipher);
 		os_free(buf);
 		return NULL;
 	}
+	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+						  sm->pairwise_cipher));
 	pos += RSN_SELECTOR_LEN;
 
 	/* Authenticated Key Management Suite Count */
@@ -323,21 +321,15 @@
 
 	wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver.");
 
-	switch (sm->pairwise_cipher) {
-	case WPA_CIPHER_CCMP:
-		alg = WPA_ALG_CCMP;
-		keylen = 16;
-		break;
-	case WPA_CIPHER_TKIP:
-		alg = WPA_ALG_TKIP;
-		keylen = 32;
-		break;
-	default:
+	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
 		wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d",
 			   sm->pairwise_cipher);
 		return -1;
 	}
 
+	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+
 	if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc,
 			   sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) {
 		wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
@@ -483,7 +475,7 @@
 		    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 
 	bssid = target_ap;
-	ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64;
+	ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
 	wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr,
 			  bssid, sm->pmk_r1_name,
 			  (u8 *) &sm->ptk, ptk_len, ptk_name);
@@ -571,28 +563,10 @@
 		return -1;
 	}
 
-	switch (sm->group_cipher) {
-	case WPA_CIPHER_CCMP:
-		keylen = 16;
-		rsc_len = 6;
-		alg = WPA_ALG_CCMP;
-		break;
-	case WPA_CIPHER_TKIP:
-		keylen = 32;
-		rsc_len = 6;
-		alg = WPA_ALG_TKIP;
-		break;
-	case WPA_CIPHER_WEP104:
-		keylen = 13;
-		rsc_len = 0;
-		alg = WPA_ALG_WEP;
-		break;
-	case WPA_CIPHER_WEP40:
-		keylen = 5;
-		rsc_len = 0;
-		alg = WPA_ALG_WEP;
-		break;
-	default:
+	keylen = wpa_cipher_key_len(sm->group_cipher);
+	rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
+	alg = wpa_cipher_to_alg(sm->group_cipher);
+	if (alg == WPA_ALG_NONE) {
 		wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
 			   sm->group_cipher);
 		return -1;
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 56d564c..6a8f9f1 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -41,6 +41,7 @@
 {
 	u8 *pos;
 	struct wpa_ie_hdr *hdr;
+	u32 suite;
 
 	if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
 	    2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
@@ -52,34 +53,26 @@
 	WPA_PUT_LE16(hdr->version, WPA_VERSION);
 	pos = (u8 *) (hdr + 1);
 
-	if (group_cipher == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
-	} else if (group_cipher == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
-	} else if (group_cipher == WPA_CIPHER_WEP104) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
-	} else if (group_cipher == WPA_CIPHER_WEP40) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher);
+	if (suite == 0) {
 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
 			   group_cipher);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += WPA_SELECTOR_LEN;
 
 	*pos++ = 1;
 	*pos++ = 0;
-	if (pairwise_cipher == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
-	} else if (pairwise_cipher == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
-	} else if (pairwise_cipher == WPA_CIPHER_NONE) {
-		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher);
+	if (suite == 0 ||
+	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
+	     pairwise_cipher != WPA_CIPHER_NONE)) {
 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
 			   pairwise_cipher);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += WPA_SELECTOR_LEN;
 
 	*pos++ = 1;
@@ -116,6 +109,7 @@
 	u8 *pos;
 	struct rsn_ie_hdr *hdr;
 	u16 capab;
+	u32 suite;
 
 	if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
 	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
@@ -130,34 +124,26 @@
 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
 	pos = (u8 *) (hdr + 1);
 
-	if (group_cipher == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	} else if (group_cipher == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-	} else if (group_cipher == WPA_CIPHER_WEP104) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
-	} else if (group_cipher == WPA_CIPHER_WEP40) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
+	if (suite == 0) {
 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
 			   group_cipher);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += RSN_SELECTOR_LEN;
 
 	*pos++ = 1;
 	*pos++ = 0;
-	if (pairwise_cipher == WPA_CIPHER_CCMP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
-	} else if (pairwise_cipher == WPA_CIPHER_TKIP) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
-	} else if (pairwise_cipher == WPA_CIPHER_NONE) {
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
-	} else {
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
+	if (suite == 0 ||
+	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
+	     pairwise_cipher != WPA_CIPHER_NONE)) {
 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
 			   pairwise_cipher);
 		return -1;
 	}
+	RSN_SELECTOR_PUT(pos, suite);
 	pos += RSN_SELECTOR_LEN;
 
 	*pos++ = 1;
diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c
index 7c9857f..741b442 100644
--- a/src/tls/libtommath.c
+++ b/src/tls/libtommath.c
@@ -66,11 +66,19 @@
 
 #define  OPT_CAST(x)
 
+#ifdef __x86_64__
+typedef unsigned long mp_digit;
+typedef unsigned long mp_word __attribute__((mode(TI)));
+
+#define DIGIT_BIT 60
+#define MP_64BIT
+#else
 typedef unsigned long mp_digit;
 typedef u64 mp_word;
 
 #define DIGIT_BIT          28
 #define MP_28BIT
+#endif
 
 
 #define XMALLOC  os_malloc
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index c5cd362..12148b6 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -459,10 +459,8 @@
 
 	count = 0;
 	suites = conn->cipher_suites;
-#ifndef CONFIG_CRYPTO_INTERNAL
 	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
 	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
-#endif /* CONFIG_CRYPTO_INTERNAL */
 	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
 	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
@@ -735,10 +733,8 @@
 	if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
 		count = 0;
 		suites = conn->cipher_suites;
-#ifndef CONFIG_CRYPTO_INTERNAL
 		suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256;
 		suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
-#endif /* CONFIG_CRYPTO_INTERNAL */
 		suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256;
 		suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
 		suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index 72e2716..2880309 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -361,9 +361,7 @@
 
 	count = 0;
 	suites = conn->cipher_suites;
-#ifndef CONFIG_CRYPTO_INTERNAL
 	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
-#endif /* CONFIG_CRYPTO_INTERNAL */
 	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
@@ -587,16 +585,12 @@
 	if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
 		count = 0;
 		suites = conn->cipher_suites;
-#ifndef CONFIG_CRYPTO_INTERNAL
 		suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
-#endif /* CONFIG_CRYPTO_INTERNAL */
 		suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
 		suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
 		suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
 		suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
-#ifndef CONFIG_CRYPTO_INTERNAL
 		suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
-#endif /* CONFIG_CRYPTO_INTERNAL */
 		suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
 		suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
 		suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
diff --git a/src/utils/build_config.h b/src/utils/build_config.h
index 04b8d98..f947388 100644
--- a/src/utils/build_config.h
+++ b/src/utils/build_config.h
@@ -47,28 +47,6 @@
 #endif /* USE_INTERNAL_CRYPTO */
 #endif /* CONFIG_WIN32_DEFAULTS */
 
-#ifdef __SYMBIAN32__
-#define OS_NO_C_LIB_DEFINES
-#define CONFIG_ANSI_C_EXTRA
-#define CONFIG_NO_WPA_MSG
-#define CONFIG_NO_HOSTAPD_LOGGER
-#define CONFIG_NO_STDOUT_DEBUG
-#define CONFIG_BACKEND_FILE
-#define CONFIG_INTERNAL_LIBTOMMATH
-#define CONFIG_CRYPTO_INTERNAL
-#define IEEE8021X_EAPOL
-#define PKCS12_FUNCS
-#define EAP_MD5
-#define EAP_TLS
-#define EAP_MSCHAPv2
-#define EAP_PEAP
-#define EAP_TTLS
-#define EAP_GTC
-#define EAP_OTP
-#define EAP_LEAP
-#define EAP_FAST
-#endif /* __SYMBIAN32__ */
-
 #ifdef CONFIG_XCODE_DEFAULTS
 #define CONFIG_DRIVER_OSX
 #define CONFIG_BACKEND_FILE
diff --git a/src/utils/common.c b/src/utils/common.c
index 26fea1b..e636984 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -344,6 +344,135 @@
 #endif /* CONFIG_NATIVE_WINDOWS */
 
 
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len)
+{
+	char *end = txt + maxlen;
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (txt + 4 > end)
+			break;
+
+		switch (data[i]) {
+		case '\"':
+			*txt++ = '\\';
+			*txt++ = '\"';
+			break;
+		case '\\':
+			*txt++ = '\\';
+			*txt++ = '\\';
+			break;
+		case '\e':
+			*txt++ = '\\';
+			*txt++ = 'e';
+			break;
+		case '\n':
+			*txt++ = '\\';
+			*txt++ = 'n';
+			break;
+		case '\r':
+			*txt++ = '\\';
+			*txt++ = 'r';
+			break;
+		case '\t':
+			*txt++ = '\\';
+			*txt++ = 't';
+			break;
+		default:
+			if (data[i] >= 32 && data[i] <= 127) {
+				*txt++ = data[i];
+			} else {
+				txt += os_snprintf(txt, end - txt, "\\x%02x",
+						   data[i]);
+			}
+			break;
+		}
+	}
+
+	*txt = '\0';
+}
+
+
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
+{
+	const char *pos = str;
+	size_t len = 0;
+	int val;
+
+	while (*pos) {
+		if (len == maxlen)
+			break;
+		switch (*pos) {
+		case '\\':
+			pos++;
+			switch (*pos) {
+			case '\\':
+				buf[len++] = '\\';
+				pos++;
+				break;
+			case '"':
+				buf[len++] = '"';
+				pos++;
+				break;
+			case 'n':
+				buf[len++] = '\n';
+				pos++;
+				break;
+			case 'r':
+				buf[len++] = '\r';
+				pos++;
+				break;
+			case 't':
+				buf[len++] = '\t';
+				pos++;
+				break;
+			case 'e':
+				buf[len++] = '\e';
+				pos++;
+				break;
+			case 'x':
+				pos++;
+				val = hex2byte(pos);
+				if (val < 0) {
+					val = hex2num(*pos);
+					if (val < 0)
+						break;
+					buf[len++] = val;
+					pos++;
+				} else {
+					buf[len++] = val;
+					pos += 2;
+				}
+				break;
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+				val = *pos++ - '0';
+				if (*pos >= '0' && *pos <= '7')
+					val = val * 8 + (*pos++ - '0');
+				if (*pos >= '0' && *pos <= '7')
+					val = val * 8 + (*pos++ - '0');
+				buf[len++] = val;
+				break;
+			default:
+				break;
+			}
+			break;
+		default:
+			buf[len++] = *pos++;
+			break;
+		}
+	}
+
+	return len;
+}
+
+
 /**
  * wpa_ssid_txt - Convert SSID to a printable string
  * @ssid: SSID (32-octet string)
@@ -360,19 +489,14 @@
  */
 const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
 {
-	static char ssid_txt[33];
-	char *pos;
+	static char ssid_txt[32 * 4 + 1];
 
-	if (ssid_len > 32)
-		ssid_len = 32;
-	os_memcpy(ssid_txt, ssid, ssid_len);
-	ssid_txt[ssid_len] = '\0';
-	for (pos = ssid_txt; *pos != '\0'; pos++) {
-#ifndef WPA_UNICODE_SSID
-		if ((u8) *pos < 32 || (u8) *pos >= 127)
-			*pos = '_';
-#endif
+	if (ssid == NULL) {
+		ssid_txt[0] = '\0';
+		return ssid_txt;
 	}
+
+	printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len);
 	return ssid_txt;
 }
 
@@ -381,3 +505,108 @@
 {
 	return foo;
 }
+
+
+char * wpa_config_parse_string(const char *value, size_t *len)
+{
+	if (*value == '"') {
+		const char *pos;
+		char *str;
+		value++;
+		pos = os_strrchr(value, '"');
+		if (pos == NULL || pos[1] != '\0')
+			return NULL;
+		*len = pos - value;
+		str = os_malloc(*len + 1);
+		if (str == NULL)
+			return NULL;
+		os_memcpy(str, value, *len);
+		str[*len] = '\0';
+		return str;
+	} else if (*value == 'P' && value[1] == '"') {
+		const char *pos;
+		char *tstr, *str;
+		size_t tlen;
+		value += 2;
+		pos = os_strrchr(value, '"');
+		if (pos == NULL || pos[1] != '\0')
+			return NULL;
+		tlen = pos - value;
+		tstr = os_malloc(tlen + 1);
+		if (tstr == NULL)
+			return NULL;
+		os_memcpy(tstr, value, tlen);
+		tstr[tlen] = '\0';
+
+		str = os_malloc(tlen + 1);
+		if (str == NULL) {
+			os_free(tstr);
+			return NULL;
+		}
+
+		*len = printf_decode((u8 *) str, tlen + 1, tstr);
+		os_free(tstr);
+
+		return str;
+	} else {
+		u8 *str;
+		size_t tlen, hlen = os_strlen(value);
+		if (hlen & 1)
+			return NULL;
+		tlen = hlen / 2;
+		str = os_malloc(tlen + 1);
+		if (str == NULL)
+			return NULL;
+		if (hexstr2bin(value, str, tlen)) {
+			os_free(str);
+			return NULL;
+		}
+		str[tlen] = '\0';
+		*len = tlen;
+		return (char *) str;
+	}
+}
+
+
+int is_hex(const u8 *data, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (data[i] < 32 || data[i] >= 127)
+			return 1;
+	}
+	return 0;
+}
+
+
+size_t merge_byte_arrays(u8 *res, size_t res_len,
+			 const u8 *src1, size_t src1_len,
+			 const u8 *src2, size_t src2_len)
+{
+	size_t len = 0;
+
+	os_memset(res, 0, res_len);
+
+	if (src1) {
+		if (src1_len >= res_len) {
+			os_memcpy(res, src1, res_len);
+			return res_len;
+		}
+
+		os_memcpy(res, src1, src1_len);
+		len += src1_len;
+	}
+
+	if (src2) {
+		if (len + src2_len >= res_len) {
+			os_memcpy(res + len, src2, res_len - len);
+			return res_len;
+		}
+
+		os_memcpy(res + len, src2, src2_len);
+		len += src2_len;
+	}
+
+	return len;
+}
diff --git a/src/utils/common.h b/src/utils/common.h
index 43ceb54..5fc916c 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -63,12 +63,6 @@
 #endif
 #endif /* CONFIG_TI_COMPILER */
 
-#ifdef __SYMBIAN32__
-#define __BIG_ENDIAN 4321
-#define __LITTLE_ENDIAN 1234
-#define __BYTE_ORDER __LITTLE_ENDIAN
-#endif /* __SYMBIAN32__ */
-
 #ifdef CONFIG_NATIVE_WINDOWS
 #include <winsock.h>
 
@@ -132,16 +126,6 @@
 #define WPA_TYPES_DEFINED
 #endif /* CONFIG_TI_COMPILER */
 
-#ifdef __SYMBIAN32__
-#define __REMOVE_PLATSEC_DIAGNOSTICS__
-#include <e32def.h>
-typedef TUint64 u64;
-typedef TUint32 u32;
-typedef TUint16 u16;
-typedef TUint8 u8;
-#define WPA_TYPES_DEFINED
-#endif /* __SYMBIAN32__ */
-
 #ifndef WPA_TYPES_DEFINED
 #ifdef CONFIG_USE_INTTYPES_H
 #include <inttypes.h>
@@ -457,8 +441,17 @@
 #define wpa_strdup_tchar(s) strdup((s))
 #endif /* CONFIG_NATIVE_WINDOWS */
 
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len);
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str);
+
 const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len);
 
+char * wpa_config_parse_string(const char *value, size_t *len);
+int is_hex(const u8 *data, size_t len);
+size_t merge_byte_arrays(u8 *res, size_t res_len,
+			 const u8 *src1, size_t src1_len,
+			 const u8 *src2, size_t src2_len);
+
 static inline int is_zero_ether_addr(const u8 *a)
 {
 	return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
diff --git a/src/utils/edit.c b/src/utils/edit.c
index 280d244..b01e08d 100644
--- a/src/utils/edit.c
+++ b/src/utils/edit.c
@@ -20,6 +20,7 @@
 static int cmdbuf_len = 0;
 static char currbuf[CMD_BUF_LEN];
 static int currbuf_valid = 0;
+static const char *ps2 = NULL;
 
 #define HISTORY_MAX 100
 
@@ -47,7 +48,7 @@
 {
 	int i;
 	putchar('\r');
-	for (i = 0; i < cmdbuf_len + 2; i++)
+	for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++)
 		putchar(' ');
 }
 
@@ -346,7 +347,7 @@
 {
 
 	if (cmdbuf_len == 0) {
-		printf("\n> ");
+		printf("\n%s> ", ps2 ? ps2 : "");
 		fflush(stdout);
 		return;
 	}
@@ -356,7 +357,7 @@
 	cmdbuf_pos = 0;
 	cmdbuf_len = 0;
 	edit_cmd_cb(edit_cb_ctx, cmdbuf);
-	printf("> ");
+	printf("%s> ", ps2 ? ps2 : "");
 	fflush(stdout);
 }
 
@@ -1112,7 +1113,7 @@
 int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
 	      void (*eof_cb)(void *ctx),
 	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
-	      void *ctx, const char *history_file)
+	      void *ctx, const char *history_file, const char *ps)
 {
 	currbuf[0] = '\0';
 	dl_list_init(&history_list);
@@ -1132,7 +1133,8 @@
 
 	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
 
-	printf("> ");
+	ps2 = ps;
+	printf("%s> ", ps2 ? ps2 : "");
 	fflush(stdout);
 
 	return 0;
@@ -1161,11 +1163,11 @@
 {
 	char tmp;
 	cmdbuf[cmdbuf_len] = '\0';
-	printf("\r> %s", cmdbuf);
+	printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
 	if (cmdbuf_pos != cmdbuf_len) {
 		tmp = cmdbuf[cmdbuf_pos];
 		cmdbuf[cmdbuf_pos] = '\0';
-		printf("\r> %s", cmdbuf);
+		printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
 		cmdbuf[cmdbuf_pos] = tmp;
 	}
 	fflush(stdout);
diff --git a/src/utils/edit.h b/src/utils/edit.h
index e8d04e7..ad27f1c 100644
--- a/src/utils/edit.h
+++ b/src/utils/edit.h
@@ -12,7 +12,7 @@
 int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
 	      void (*eof_cb)(void *ctx),
 	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
-	      void *ctx, const char *history_file);
+	      void *ctx, const char *history_file, const char *ps);
 void edit_deinit(const char *history_file,
 		 int (*filter_cb)(void *ctx, const char *cmd));
 void edit_clear_line(void);
diff --git a/src/utils/edit_readline.c b/src/utils/edit_readline.c
index c9ff0e6..add26fa 100644
--- a/src/utils/edit_readline.c
+++ b/src/utils/edit_readline.c
@@ -112,7 +112,7 @@
 int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
 	      void (*eof_cb)(void *ctx),
 	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
-	      void *ctx, const char *history_file)
+	      void *ctx, const char *history_file, const char *ps)
 {
 	edit_cb_ctx = ctx;
 	edit_cmd_cb = cmd_cb;
@@ -127,6 +127,17 @@
 
 	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
 
+	if (ps) {
+		size_t blen = os_strlen(ps) + 3;
+		char *ps2 = os_malloc(blen);
+		if (ps2) {
+			os_snprintf(ps2, blen, "%s> ", ps);
+			rl_callback_handler_install(ps2, readline_cmd_handler);
+			os_free(ps2);
+			return 0;
+		}
+	}
+
 	rl_callback_handler_install("> ", readline_cmd_handler);
 
 	return 0;
@@ -136,6 +147,9 @@
 void edit_deinit(const char *history_file,
 		 int (*filter_cb)(void *ctx, const char *cmd))
 {
+	rl_set_prompt("");
+	rl_replace_line("", 0);
+	rl_redisplay();
 	rl_callback_handler_remove();
 	readline_free_completions();
 
diff --git a/src/utils/edit_simple.c b/src/utils/edit_simple.c
index 0e3637c..a095ea6 100644
--- a/src/utils/edit_simple.c
+++ b/src/utils/edit_simple.c
@@ -16,6 +16,7 @@
 #define CMD_BUF_LEN 256
 static char cmdbuf[CMD_BUF_LEN];
 static int cmdbuf_pos = 0;
+static const char *ps2 = NULL;
 
 static void *edit_cb_ctx;
 static void (*edit_cmd_cb)(void *ctx, char *cmd);
@@ -41,7 +42,7 @@
 		cmdbuf[cmdbuf_pos] = '\0';
 		cmdbuf_pos = 0;
 		edit_cmd_cb(edit_cb_ctx, cmdbuf);
-		printf("> ");
+		printf("%s> ", ps2 ? ps2 : "");
 		fflush(stdout);
 		return;
 	}
@@ -57,14 +58,15 @@
 int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
 	      void (*eof_cb)(void *ctx),
 	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
-	      void *ctx, const char *history_file)
+	      void *ctx, const char *history_file, const char *ps)
 {
 	edit_cb_ctx = ctx;
 	edit_cmd_cb = cmd_cb;
 	edit_eof_cb = eof_cb;
 	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+	ps2 = ps;
 
-	printf("> ");
+	printf("%s> ", ps2 ? ps2 : "");
 	fflush(stdout);
 
 	return 0;
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index 5691f15..d01ae64 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -153,8 +153,8 @@
 #ifdef CONFIG_ELOOP_POLL
 	if (new_max_sock >= eloop.max_pollfd_map) {
 		struct pollfd **nmap;
-		nmap = os_realloc(eloop.pollfds_map, sizeof(struct pollfd *) *
-				  (new_max_sock + 50));
+		nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50,
+					sizeof(struct pollfd *));
 		if (nmap == NULL)
 			return -1;
 
@@ -165,7 +165,8 @@
 	if (eloop.count + 1 > eloop.max_poll_fds) {
 		struct pollfd *n;
 		int nmax = eloop.count + 1 + 50;
-		n = os_realloc(eloop.pollfds, sizeof(struct pollfd) * nmax);
+		n = os_realloc_array(eloop.pollfds, nmax,
+				     sizeof(struct pollfd));
 		if (n == NULL)
 			return -1;
 
@@ -175,9 +176,8 @@
 #endif /* CONFIG_ELOOP_POLL */
 
 	eloop_trace_sock_remove_ref(table);
-	tmp = (struct eloop_sock *)
-		os_realloc(table->table,
-			   (table->count + 1) * sizeof(struct eloop_sock));
+	tmp = os_realloc_array(table->table, table->count + 1,
+			       sizeof(struct eloop_sock));
 	if (tmp == NULL)
 		return -1;
 
@@ -278,7 +278,7 @@
 				pollfds_map[fd] = pfd;
 				nxt++;
 			}
-			pfd->events |= POLLIN;
+			pfd->events |= POLLOUT;
 		}
 	}
 
@@ -350,7 +350,8 @@
 				      int max_pollfd_map)
 {
 	if (eloop_sock_table_dispatch_table(readers, pollfds_map,
-					    max_pollfd_map, POLLIN))
+					    max_pollfd_map, POLLIN | POLLERR |
+					    POLLHUP))
 		return; /* pollfds may be invalid at this point */
 
 	if (eloop_sock_table_dispatch_table(writers, pollfds_map,
@@ -638,10 +639,8 @@
 {
 	struct eloop_signal *tmp;
 
-	tmp = (struct eloop_signal *)
-		os_realloc(eloop.signals,
-			   (eloop.signal_count + 1) *
-			   sizeof(struct eloop_signal));
+	tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
+			       sizeof(struct eloop_signal));
 	if (tmp == NULL)
 		return -1;
 
diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c
index 1cf5579..1fafeb2 100644
--- a/src/utils/eloop_win.c
+++ b/src/utils/eloop_win.c
@@ -98,8 +98,8 @@
 
 	if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8)
 		return 0;
-	n = os_realloc(eloop.handles,
-		       eloop.num_handles * 2 * sizeof(eloop.handles[0]));
+	n = os_realloc_array(eloop.handles, eloop.num_handles * 2,
+			     sizeof(eloop.handles[0]));
 	if (n == NULL)
 		return -1;
 	eloop.handles = n;
@@ -128,8 +128,8 @@
 		WSACloseEvent(event);
 		return -1;
 	}
-	tmp = os_realloc(eloop.readers,
-			 (eloop.reader_count + 1) * sizeof(struct eloop_sock));
+	tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1,
+			       sizeof(struct eloop_sock));
 	if (tmp == NULL) {
 		WSAEventSelect(sock, event, 0);
 		WSACloseEvent(event);
@@ -191,8 +191,8 @@
 	if (eloop_prepare_handles())
 		return -1;
 
-	tmp = os_realloc(eloop.events,
-			 (eloop.event_count + 1) * sizeof(struct eloop_event));
+	tmp = os_realloc_array(eloop.events, eloop.event_count + 1,
+			       sizeof(struct eloop_event));
 	if (tmp == NULL)
 		return -1;
 
@@ -392,9 +392,8 @@
 {
 	struct eloop_signal *tmp;
 
-	tmp = os_realloc(eloop.signals,
-			 (eloop.signal_count + 1) *
-			 sizeof(struct eloop_signal));
+	tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
+			       sizeof(struct eloop_signal));
 	if (tmp == NULL)
 		return -1;
 
diff --git a/src/utils/ext_password.c b/src/utils/ext_password.c
new file mode 100644
index 0000000..0613119
--- /dev/null
+++ b/src/utils/ext_password.c
@@ -0,0 +1,116 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#ifdef __linux__
+#include <sys/mman.h>
+#endif /* __linux__ */
+
+#include "common.h"
+#include "ext_password_i.h"
+
+
+#ifdef CONFIG_EXT_PASSWORD_TEST
+extern struct ext_password_backend ext_password_test;
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+
+static const struct ext_password_backend *backends[] = {
+#ifdef CONFIG_EXT_PASSWORD_TEST
+	&ext_password_test,
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+	NULL
+};
+
+struct ext_password_data {
+	const struct ext_password_backend *backend;
+	void *priv;
+};
+
+
+struct ext_password_data * ext_password_init(const char *backend,
+					     const char *params)
+{
+	struct ext_password_data *data;
+	int i;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	for (i = 0; backends[i]; i++) {
+		if (os_strcmp(backends[i]->name, backend) == 0) {
+			data->backend = backends[i];
+			break;
+		}
+	}
+
+	if (!data->backend) {
+		os_free(data);
+		return NULL;
+	}
+
+	data->priv = data->backend->init(params);
+	if (data->priv == NULL) {
+		os_free(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+void ext_password_deinit(struct ext_password_data *data)
+{
+	if (data && data->backend && data->priv)
+		data->backend->deinit(data->priv);
+	os_free(data);
+}
+
+
+struct wpabuf * ext_password_get(struct ext_password_data *data,
+				 const char *name)
+{
+	if (data == NULL)
+		return NULL;
+	return data->backend->get(data->priv, name);
+}
+
+
+struct wpabuf * ext_password_alloc(size_t len)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return NULL;
+
+#ifdef __linux__
+	if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) {
+		wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s",
+			   strerror(errno));
+	}
+#endif /* __linux__ */
+
+	return buf;
+}
+
+
+void ext_password_free(struct wpabuf *pw)
+{
+	if (pw == NULL)
+		return;
+	os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw));
+#ifdef __linux__
+	if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) {
+		wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s",
+			   strerror(errno));
+	}
+#endif /* __linux__ */
+	wpabuf_free(pw);
+}
diff --git a/src/utils/ext_password.h b/src/utils/ext_password.h
new file mode 100644
index 0000000..e3e46ea
--- /dev/null
+++ b/src/utils/ext_password.h
@@ -0,0 +1,33 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EXT_PASSWORD_H
+#define EXT_PASSWORD_H
+
+struct ext_password_data;
+
+#ifdef CONFIG_EXT_PASSWORD
+
+struct ext_password_data * ext_password_init(const char *backend,
+					     const char *params);
+void ext_password_deinit(struct ext_password_data *data);
+
+struct wpabuf * ext_password_get(struct ext_password_data *data,
+				 const char *name);
+void ext_password_free(struct wpabuf *pw);
+
+#else /* CONFIG_EXT_PASSWORD */
+
+#define ext_password_init(b, p) ((void *) 1)
+#define ext_password_deinit(d) do { } while (0)
+#define ext_password_get(d, n) (NULL)
+#define ext_password_free(p) do { } while (0)
+
+#endif /* CONFIG_EXT_PASSWORD */
+
+#endif /* EXT_PASSWORD_H */
diff --git a/src/utils/ext_password_i.h b/src/utils/ext_password_i.h
new file mode 100644
index 0000000..043e731
--- /dev/null
+++ b/src/utils/ext_password_i.h
@@ -0,0 +1,23 @@
+/*
+ * External password backend - internal definitions
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EXT_PASSWORD_I_H
+#define EXT_PASSWORD_I_H
+
+#include "ext_password.h"
+
+struct ext_password_backend {
+	const char *name;
+	void * (*init)(const char *params);
+	void (*deinit)(void *ctx);
+	struct wpabuf * (*get)(void *ctx, const char *name);
+};
+
+struct wpabuf * ext_password_alloc(size_t len);
+
+#endif /* EXT_PASSWORD_I_H */
diff --git a/src/utils/ext_password_test.c b/src/utils/ext_password_test.c
new file mode 100644
index 0000000..3801bb8
--- /dev/null
+++ b/src/utils/ext_password_test.c
@@ -0,0 +1,90 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ext_password_i.h"
+
+
+struct ext_password_test_data {
+	char *params;
+};
+
+
+static void * ext_password_test_init(const char *params)
+{
+	struct ext_password_test_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	if (params)
+		data->params = os_strdup(params);
+
+	return data;
+}
+
+
+static void ext_password_test_deinit(void *ctx)
+{
+	struct ext_password_test_data *data = ctx;
+
+	os_free(data->params);
+	os_free(data);
+}
+
+
+static struct wpabuf * ext_password_test_get(void *ctx, const char *name)
+{
+	struct ext_password_test_data *data = ctx;
+	char *pos, *pos2;
+	size_t nlen;
+
+	wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s)", name);
+
+	pos = data->params;
+	if (pos == NULL)
+		return NULL;
+	nlen = os_strlen(name);
+
+	while (pos && *pos) {
+		if (os_strncmp(pos, name, nlen) == 0 && pos[nlen] == '=') {
+			struct wpabuf *buf;
+			pos += nlen + 1;
+			pos2 = pos;
+			while (*pos2 != '|' && *pos2 != '\0')
+				pos2++;
+			buf = ext_password_alloc(pos2 - pos);
+			if (buf == NULL)
+				return NULL;
+			wpabuf_put_data(buf, pos, pos2 - pos);
+			wpa_hexdump_ascii_key(MSG_DEBUG, "EXT PW TEST: value",
+					      wpabuf_head(buf),
+					      wpabuf_len(buf));
+			return buf;
+		}
+
+		pos = os_strchr(pos + 1, '|');
+		if (pos)
+			pos++;
+	}
+
+	wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s) - not found", name);
+
+	return NULL;
+}
+
+
+const struct ext_password_backend ext_password_test = {
+	.name = "test",
+	.init = ext_password_test_init,
+	.deinit = ext_password_test_deinit,
+	.get = ext_password_test_get,
+};
diff --git a/src/utils/includes.h b/src/utils/includes.h
index 787be18..6c6ec87 100644
--- a/src/utils/includes.h
+++ b/src/utils/includes.h
@@ -41,9 +41,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #ifndef __vxworks
-#ifndef __SYMBIAN32__
 #include <sys/uio.h>
-#endif /* __SYMBIAN32__ */
 #include <sys/time.h>
 #endif /* __vxworks */
 #endif /* CONFIG_TI_COMPILER */
diff --git a/src/utils/os.h b/src/utils/os.h
index 3058b88..ad20834 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -180,6 +180,25 @@
  */
 void * os_zalloc(size_t size);
 
+/**
+ * os_calloc - Allocate and zero memory for an array
+ * @nmemb: Number of members in the array
+ * @size: Number of bytes in each member
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * This function can be used as a wrapper for os_zalloc(nmemb * size) when an
+ * allocation is used for an array. The main benefit over os_zalloc() is in
+ * having an extra check to catch integer overflows in multiplication.
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+static inline void * os_calloc(size_t nmemb, size_t size)
+{
+	if (size && nmemb > (~(size_t) 0) / size)
+		return NULL;
+	return os_zalloc(nmemb * size);
+}
+
 
 /*
  * The following functions are wrapper for standard ANSI C or POSIX functions.
@@ -467,6 +486,14 @@
 #endif /* OS_NO_C_LIB_DEFINES */
 
 
+static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
+{
+	if (size && nmemb > (~(size_t) 0) / size)
+		return NULL;
+	return os_realloc(ptr, nmemb * size);
+}
+
+
 /**
  * os_strlcpy - Copy a string with size bound and NUL-termination
  * @dest: Destination
diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c
index a97f9fa..08510d0 100644
--- a/src/utils/pcsc_funcs.c
+++ b/src/utils/pcsc_funcs.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2007, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -70,6 +70,9 @@
 #define USIM_TLV_TOTAL_FILE_SIZE	0x81
 #define USIM_TLV_PIN_STATUS_TEMPLATE	0xC6
 #define USIM_TLV_SHORT_FILE_ID		0x88
+#define USIM_TLV_SECURITY_ATTR_8B	0x8B
+#define USIM_TLV_SECURITY_ATTR_8C	0x8C
+#define USIM_TLV_SECURITY_ATTR_AB	0xAB
 
 #define USIM_PS_DO_TAG			0x90
 
@@ -81,6 +84,27 @@
 #define CK_LEN 16
 
 
+/* GSM files
+ * File type in first octet:
+ * 3F = Master File
+ * 7F = Dedicated File
+ * 2F = Elementary File under the Master File
+ * 6F = Elementary File under a Dedicated File
+ */
+#define SCARD_FILE_MF		0x3F00
+#define SCARD_FILE_GSM_DF	0x7F20
+#define SCARD_FILE_UMTS_DF	0x7F50
+#define SCARD_FILE_GSM_EF_IMSI	0x6F07
+#define SCARD_FILE_GSM_EF_AD	0x6FAD
+#define SCARD_FILE_EF_DIR	0x2F00
+#define SCARD_FILE_EF_ICCID	0x2FE2
+#define SCARD_FILE_EF_CK	0x6FE1
+#define SCARD_FILE_EF_IK	0x6FE2
+
+#define SCARD_CHV1_OFFSET	13
+#define SCARD_CHV1_FLAG		0x80
+
+
 typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types;
 
 struct scard_data {
@@ -234,37 +258,60 @@
 static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
 				 int *ps_do, int *file_len)
 {
-		unsigned char *pos, *end;
+	unsigned char *pos, *end;
 
-		if (ps_do)
-			*ps_do = -1;
-		if (file_len)
-			*file_len = -1;
+	if (ps_do)
+		*ps_do = -1;
+	if (file_len)
+		*file_len = -1;
 
-		pos = buf;
-		end = pos + buf_len;
-		if (*pos != USIM_FSP_TEMPL_TAG) {
-			wpa_printf(MSG_DEBUG, "SCARD: file header did not "
-				   "start with FSP template tag");
-			return -1;
-		}
-		pos++;
-		if (pos >= end)
-			return -1;
-		if ((pos + pos[0]) < end)
-			end = pos + 1 + pos[0];
-		pos++;
-		wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
-			    pos, end - pos);
+	pos = buf;
+	end = pos + buf_len;
+	if (*pos != USIM_FSP_TEMPL_TAG) {
+		wpa_printf(MSG_DEBUG, "SCARD: file header did not "
+			   "start with FSP template tag");
+		return -1;
+	}
+	pos++;
+	if (pos >= end)
+		return -1;
+	if ((pos + pos[0]) < end)
+		end = pos + 1 + pos[0];
+	pos++;
+	wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
+		    pos, end - pos);
 
-		while (pos + 1 < end) {
-			wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV "
-				   "0x%02x len=%d", pos[0], pos[1]);
-			if (pos + 2 + pos[1] > end)
-				break;
+	while (pos + 1 < end) {
+		wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d",
+			   pos[0], pos[1]);
+		if (pos + 2 + pos[1] > end)
+			break;
 
-			if (pos[0] == USIM_TLV_FILE_SIZE &&
-			    (pos[1] == 1 || pos[1] == 2) && file_len) {
+		switch (pos[0]) {
+		case USIM_TLV_FILE_DESC:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV",
+				    pos + 2, pos[1]);
+			break;
+		case USIM_TLV_FILE_ID:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV",
+				    pos + 2, pos[1]);
+			break;
+		case USIM_TLV_DF_NAME:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV",
+				    pos + 2, pos[1]);
+			break;
+		case USIM_TLV_PROPR_INFO:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary "
+				    "information TLV", pos + 2, pos[1]);
+			break;
+		case USIM_TLV_LIFE_CYCLE_STATUS:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status "
+				    "Integer TLV", pos + 2, pos[1]);
+			break;
+		case USIM_TLV_FILE_SIZE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV",
+				    pos + 2, pos[1]);
+			if ((pos[1] == 1 || pos[1] == 2) && file_len) {
 				if (pos[1] == 1)
 					*file_len = (int) pos[2];
 				else
@@ -273,21 +320,43 @@
 				wpa_printf(MSG_DEBUG, "SCARD: file_size=%d",
 					   *file_len);
 			}
-
-			if (pos[0] == USIM_TLV_PIN_STATUS_TEMPLATE &&
-			    pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG &&
+			break;
+		case USIM_TLV_TOTAL_FILE_SIZE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV",
+				    pos + 2, pos[1]);
+			break;
+		case USIM_TLV_PIN_STATUS_TEMPLATE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template "
+				    "DO TLV", pos + 2, pos[1]);
+			if (pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG &&
 			    pos[3] >= 1 && ps_do) {
 				wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
 					   pos[4]);
 				*ps_do = (int) pos[4];
 			}
-
-			pos += 2 + pos[1];
-
-			if (pos == end)
-				return 0;
+			break;
+		case USIM_TLV_SHORT_FILE_ID:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File "
+				    "Identifier (SFI) TLV", pos + 2, pos[1]);
+			break;
+		case USIM_TLV_SECURITY_ATTR_8B:
+		case USIM_TLV_SECURITY_ATTR_8C:
+		case USIM_TLV_SECURITY_ATTR_AB:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute "
+				    "TLV", pos + 2, pos[1]);
+			break;
+		default:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV",
+				    pos, 2 + pos[1]);
+			break;
 		}
-		return -1;
+
+		pos += 2 + pos[1];
+
+		if (pos == end)
+			return 0;
+	}
+	return -1;
 }
 
 
@@ -328,7 +397,7 @@
 		unsigned char rid[5];
 		unsigned char appl_code[2]; /* 0x1002 for 3G USIM */
 	} *efdir;
-	unsigned char buf[100];
+	unsigned char buf[127];
 	size_t blen;
 
 	efdir = (struct efdir *) buf;
@@ -417,6 +486,7 @@
 /**
  * scard_init - Initialize SIM/USIM connection using PC/SC
  * @sim_type: Allowed SIM types (SIM, USIM, or both)
+ * @reader: Reader name prefix to search for
  * Returns: Pointer to private data structure, or %NULL on failure
  *
  * This function is used to initialize SIM/USIM connection. PC/SC is used to
@@ -425,10 +495,10 @@
  * access some of the card functions. Once the connection is not needed
  * anymore, scard_deinit() can be used to close it.
  */
-struct scard_data * scard_init(scard_sim_type sim_type)
+struct scard_data * scard_init(scard_sim_type sim_type, const char *reader)
 {
 	long ret;
-	unsigned long len;
+	unsigned long len, pos;
 	struct scard_data *scard;
 #ifdef CONFIG_NATIVE_WINDOWS
 	TCHAR *readers = NULL;
@@ -482,18 +552,41 @@
 			   "available.");
 		goto failed;
 	}
-	/* readers is a list of available reader. Last entry is terminated with
-	 * double NUL.
-	 * TODO: add support for selecting the reader; now just use the first
-	 * one.. */
+	wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len);
+	/*
+	 * readers is a list of available readers. The last entry is terminated
+	 * with double null.
+	 */
+	pos = 0;
 #ifdef UNICODE
-	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers);
+	/* TODO */
 #else /* UNICODE */
-	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers);
+	while (pos < len) {
+		if (reader == NULL ||
+		    os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0)
+			break;
+		while (pos < len && readers[pos])
+			pos++;
+		pos++; /* skip separating null */
+		if (pos < len && readers[pos] == '\0')
+			pos = len; /* double null terminates list */
+	}
+#endif /* UNICODE */
+	if (pos >= len) {
+		wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' "
+			   "found", reader);
+		goto failed;
+	}
+
+#ifdef UNICODE
+	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]);
+#else /* UNICODE */
+	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]);
 #endif /* UNICODE */
 
-	ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED,
-			   SCARD_PROTOCOL_T0, &scard->card, &scard->protocol);
+	ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED,
+			   SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
+			   &scard->card, &scard->protocol);
 	if (ret != SCARD_S_SUCCESS) {
 		if (ret == (long) SCARD_E_NO_SMARTCARD)
 			wpa_printf(MSG_INFO, "No smart card inserted.");
@@ -582,7 +675,8 @@
 	}
 	if (pin_needed) {
 		scard->pin1_required = 1;
-		wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access");
+		wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry "
+			   "counter=%d)", scard_get_pin_retry_counter(scard));
 	}
 
 	ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
@@ -939,6 +1033,46 @@
 }
 
 
+int scard_get_pin_retry_counter(struct scard_data *scard)
+{
+	long ret;
+	unsigned char resp[3];
+	unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 };
+	size_t len;
+	u16 val;
+
+	wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter");
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	cmd[4] = 0; /* Empty data */
+
+	len = sizeof(resp);
+	ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+	if (ret != SCARD_S_SUCCESS)
+		return -2;
+
+	if (len != 2) {
+		wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry "
+			   "counter");
+		return -1;
+	}
+
+	val = WPA_GET_BE16(resp);
+	if (val == 0x63c0 || val == 0x6983) {
+		wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked");
+		return 0;
+	}
+
+	if (val >= 0x63c0 && val <= 0x63cf)
+		return val & 0x000f;
+
+	wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response "
+		   "value 0x%x", val);
+	return 0;
+}
+
+
 /**
  * scard_get_imsi - Read IMSI from SIM/USIM card
  * @scard: Pointer to private data from scard_init()
@@ -1285,3 +1419,9 @@
 	wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response");
 	return -1;
 }
+
+
+int scard_supports_umts(struct scard_data *scard)
+{
+	return scard->sim_type == SCARD_USIM;
+}
diff --git a/src/utils/pcsc_funcs.h b/src/utils/pcsc_funcs.h
index 2fd3610..b4ebc99 100644
--- a/src/utils/pcsc_funcs.h
+++ b/src/utils/pcsc_funcs.h
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2006, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,26 +9,6 @@
 #ifndef PCSC_FUNCS_H
 #define PCSC_FUNCS_H
 
-/* GSM files
- * File type in first octet:
- * 3F = Master File
- * 7F = Dedicated File
- * 2F = Elementary File under the Master File
- * 6F = Elementary File under a Dedicated File
- */
-#define SCARD_FILE_MF		0x3F00
-#define SCARD_FILE_GSM_DF	0x7F20
-#define SCARD_FILE_UMTS_DF	0x7F50
-#define SCARD_FILE_GSM_EF_IMSI	0x6F07
-#define SCARD_FILE_GSM_EF_AD	0x6FAD
-#define SCARD_FILE_EF_DIR	0x2F00
-#define SCARD_FILE_EF_ICCID	0x2FE2
-#define SCARD_FILE_EF_CK	0x6FE1
-#define SCARD_FILE_EF_IK	0x6FE2
-
-#define SCARD_CHV1_OFFSET	13
-#define SCARD_CHV1_FLAG		0x80
-
 typedef enum {
 	SCARD_GSM_SIM_ONLY,
 	SCARD_USIM_ONLY,
@@ -37,7 +17,7 @@
 
 
 #ifdef PCSC_FUNCS
-struct scard_data * scard_init(scard_sim_type sim_type);
+struct scard_data * scard_init(scard_sim_type sim_type, const char *reader);
 void scard_deinit(struct scard_data *scard);
 
 int scard_set_pin(struct scard_data *scard, const char *pin);
@@ -49,15 +29,20 @@
 		    const unsigned char *autn,
 		    unsigned char *res, size_t *res_len,
 		    unsigned char *ik, unsigned char *ck, unsigned char *auts);
+int scard_get_pin_retry_counter(struct scard_data *scard);
+int scard_supports_umts(struct scard_data *scard);
 
 #else /* PCSC_FUNCS */
 
-#define scard_init(s) NULL
+#define scard_init(s, r) NULL
 #define scard_deinit(s) do { } while (0)
 #define scard_set_pin(s, p) -1
 #define scard_get_imsi(s, i, l) -1
+#define scard_get_mnc_len(s) -1
 #define scard_gsm_auth(s, r, s2, k) -1
 #define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1
+#define scard_get_pin_retry_counter(s) -1
+#define scard_supports_umts(s) 0
 
 #endif /* PCSC_FUNCS */
 
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index a8b9106..5511ef1 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -16,6 +16,18 @@
 static int wpa_debug_syslog = 0;
 #endif /* CONFIG_DEBUG_SYSLOG */
 
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+
+static FILE *wpa_debug_tracing_file = NULL;
+
+#define WPAS_TRACE_PFX "wpas <%d>: "
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
 
 int wpa_debug_level = MSG_INFO;
 int wpa_debug_show_keys = 0;
@@ -30,25 +42,18 @@
 #define ANDROID_LOG_NAME	"wpa_supplicant"
 #endif /* ANDROID_LOG_NAME */
 
-void android_printf(int level, char *format, ...)
+static int wpa_to_android_level(int level)
 {
-	if (level >= wpa_debug_level) {
-		va_list ap;
-		if (level == MSG_ERROR)
-			level = ANDROID_LOG_ERROR;
-		else if (level == MSG_WARNING)
-			level = ANDROID_LOG_WARN;
-		else if (level == MSG_INFO)
-			level = ANDROID_LOG_INFO;
-		else
-			level = ANDROID_LOG_DEBUG;
-		va_start(ap, format);
-		__android_log_vprint(level, ANDROID_LOG_NAME, format, ap);
-		va_end(ap);
-	}
+	if (level == MSG_ERROR)
+		return ANDROID_LOG_ERROR;
+	if (level == MSG_WARNING)
+		return ANDROID_LOG_WARN;
+	if (level == MSG_INFO)
+		return ANDROID_LOG_INFO;
+	return ANDROID_LOG_DEBUG;
 }
 
-#else /* CONFIG_ANDROID_LOG */
+#endif /* CONFIG_ANDROID_LOG */
 
 #ifndef CONFIG_NO_STDOUT_DEBUG
 
@@ -59,6 +64,7 @@
 
 void wpa_debug_print_timestamp(void)
 {
+#ifndef CONFIG_ANDROID_LOG
 	struct os_time tv;
 
 	if (!wpa_debug_timestamp)
@@ -72,6 +78,7 @@
 	} else
 #endif /* CONFIG_DEBUG_FILE */
 	printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec);
+#endif /* CONFIG_ANDROID_LOG */
 }
 
 
@@ -112,6 +119,77 @@
 #endif /* CONFIG_DEBUG_SYSLOG */
 
 
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void)
+{
+	int mounts, trace_fd;
+	char buf[4096] = {};
+	ssize_t buflen;
+	char *line, *tmp1, *path = NULL;
+
+	mounts = open("/proc/mounts", O_RDONLY);
+	if (mounts < 0) {
+		printf("no /proc/mounts\n");
+		return -1;
+	}
+
+	buflen = read(mounts, buf, sizeof(buf) - 1);
+	close(mounts);
+	if (buflen < 0) {
+		printf("failed to read /proc/mounts\n");
+		return -1;
+	}
+
+	line = strtok_r(buf, "\n", &tmp1);
+	while (line) {
+		char *tmp2, *tmp_path, *fstype;
+		/* "<dev> <mountpoint> <fs type> ..." */
+		strtok_r(line, " ", &tmp2);
+		tmp_path = strtok_r(NULL, " ", &tmp2);
+		fstype = strtok_r(NULL, " ", &tmp2);
+		if (strcmp(fstype, "debugfs") == 0) {
+			path = tmp_path;
+			break;
+		}
+
+		line = strtok_r(NULL, "\n", &tmp1);
+	}
+
+	if (path == NULL) {
+		printf("debugfs mountpoint not found\n");
+		return -1;
+	}
+
+	snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path);
+
+	trace_fd = open(buf, O_WRONLY);
+	if (trace_fd < 0) {
+		printf("failed to open trace_marker file\n");
+		return -1;
+	}
+	wpa_debug_tracing_file = fdopen(trace_fd, "w");
+	if (wpa_debug_tracing_file == NULL) {
+		close(trace_fd);
+		printf("failed to fdopen()\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void wpa_debug_close_linux_tracing(void)
+{
+	if (wpa_debug_tracing_file == NULL)
+		return;
+	fclose(wpa_debug_tracing_file);
+	wpa_debug_tracing_file = NULL;
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
 /**
  * wpa_printf - conditional printf
  * @level: priority level (MSG_*) of the message
@@ -129,6 +207,10 @@
 
 	va_start(ap, fmt);
 	if (level >= wpa_debug_level) {
+#ifdef CONFIG_ANDROID_LOG
+		__android_log_vprint(wpa_to_android_level(level),
+				     ANDROID_LOG_NAME, fmt, ap);
+#else /* CONFIG_ANDROID_LOG */
 #ifdef CONFIG_DEBUG_SYSLOG
 		if (wpa_debug_syslog) {
 			vsyslog(syslog_priority(level), fmt, ap);
@@ -149,8 +231,20 @@
 #ifdef CONFIG_DEBUG_SYSLOG
 		}
 #endif /* CONFIG_DEBUG_SYSLOG */
+#endif /* CONFIG_ANDROID_LOG */
 	}
 	va_end(ap);
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (wpa_debug_tracing_file != NULL) {
+		va_start(ap, fmt);
+		fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level);
+		vfprintf(wpa_debug_tracing_file, fmt, ap);
+		fprintf(wpa_debug_tracing_file, "\n");
+		fflush(wpa_debug_tracing_file);
+		va_end(ap);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
 }
 
 
@@ -158,8 +252,65 @@
 			 size_t len, int show)
 {
 	size_t i;
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (wpa_debug_tracing_file != NULL) {
+		fprintf(wpa_debug_tracing_file,
+			WPAS_TRACE_PFX "%s - hexdump(len=%lu):",
+			level, title, (unsigned long) len);
+		if (buf == NULL) {
+			fprintf(wpa_debug_tracing_file, " [NULL]\n");
+		} else if (!show) {
+			fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+		} else {
+			for (i = 0; i < len; i++)
+				fprintf(wpa_debug_tracing_file,
+					" %02x", buf[i]);
+		}
+		fflush(wpa_debug_tracing_file);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
 	if (level < wpa_debug_level)
 		return;
+#ifdef CONFIG_ANDROID_LOG
+	{
+		const char *display;
+		char *strbuf = NULL;
+		size_t slen = len;
+		if (buf == NULL) {
+			display = " [NULL]";
+		} else if (len == 0) {
+			display = "";
+		} else if (show && len) {
+			/* Limit debug message length for Android log */
+			if (slen > 32)
+				slen = 32;
+			strbuf = os_malloc(1 + 3 * slen);
+			if (strbuf == NULL) {
+				wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+					   "allocate message buffer");
+				return;
+			}
+
+			for (i = 0; i < slen; i++)
+				os_snprintf(&strbuf[i * 3], 4, " %02x",
+					    buf[i]);
+
+			display = strbuf;
+		} else {
+			display = " [REMOVED]";
+		}
+
+		__android_log_print(wpa_to_android_level(level),
+				    ANDROID_LOG_NAME,
+				    "%s - hexdump(len=%lu):%s%s",
+				    title, (long unsigned int) len, display,
+				    len > slen ? " ..." : "");
+		os_free(strbuf);
+		return;
+	}
+#else /* CONFIG_ANDROID_LOG */
 #ifdef CONFIG_DEBUG_SYSLOG
 	if (wpa_debug_syslog) {
 		const char *display;
@@ -187,7 +338,7 @@
 		}
 
 		syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s",
-		       title, len, display);
+		       title, (unsigned long) len, display);
 		os_free(strbuf);
 		return;
 	}
@@ -221,6 +372,7 @@
 #ifdef CONFIG_DEBUG_FILE
 	}
 #endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
 }
 
 void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len)
@@ -242,8 +394,30 @@
 	const u8 *pos = buf;
 	const size_t line_len = 16;
 
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (wpa_debug_tracing_file != NULL) {
+		fprintf(wpa_debug_tracing_file,
+			WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):",
+			level, title, (unsigned long) len);
+		if (buf == NULL) {
+			fprintf(wpa_debug_tracing_file, " [NULL]\n");
+		} else if (!show) {
+			fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+		} else {
+			/* can do ascii processing in userspace */
+			for (i = 0; i < len; i++)
+				fprintf(wpa_debug_tracing_file,
+					" %02x", buf[i]);
+		}
+		fflush(wpa_debug_tracing_file);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
 	if (level < wpa_debug_level)
 		return;
+#ifdef CONFIG_ANDROID_LOG
+	_wpa_hexdump(level, title, buf, len, show);
+#else /* CONFIG_ANDROID_LOG */
 	wpa_debug_print_timestamp();
 #ifdef CONFIG_DEBUG_FILE
 	if (out_file) {
@@ -317,6 +491,7 @@
 #ifdef CONFIG_DEBUG_FILE
 	}
 #endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
 }
 
 
@@ -398,7 +573,6 @@
 
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
-#endif /* CONFIG_ANDROID_LOG */
 
 #ifndef CONFIG_NO_WPA_MSG
 static wpa_msg_cb_func wpa_msg_cb = NULL;
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index bbef49b..339c749 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -18,32 +18,6 @@
 	MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
 };
 
-#ifdef CONFIG_ANDROID_LOG
-
-#define wpa_debug_print_timestamp() do {} while (0)
-#define wpa_hexdump(...)            do {} while (0)
-#define wpa_hexdump_key(...)        do {} while (0)
-#define wpa_hexdump_buf(l,t,b)      do {} while (0)
-#define wpa_hexdump_buf_key(l,t,b)  do {} while (0)
-#define wpa_hexdump_ascii(...)      do {} while (0)
-#define wpa_hexdump_ascii_key(...)  do {} while (0)
-#define wpa_debug_open_file(...)    do {} while (0)
-#define wpa_debug_close_file()      do {} while (0)
-#define wpa_dbg(...)                do {} while (0)
-
-static inline int wpa_debug_reopen_file(void)
-{
-	return 0;
-}
-
-
-void android_printf(int level, char *format, ...)
-PRINTF_FORMAT(2, 3);
-
-#define wpa_printf android_printf
-
-#else /* CONFIG_ANDROID_LOG */
-
 #ifdef CONFIG_NO_STDOUT_DEBUG
 
 #define wpa_debug_print_timestamp() do { } while (0)
@@ -177,8 +151,6 @@
 
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
-#endif /* CONFIG_ANDROID_LOG */
-
 
 #ifdef CONFIG_NO_WPA_MSG
 #define wpa_msg(args...) do { } while (0)
@@ -283,6 +255,24 @@
 
 #endif /* CONFIG_DEBUG_SYSLOG */
 
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void);
+void wpa_debug_close_linux_tracing(void);
+
+#else /* CONFIG_DEBUG_LINUX_TRACING */
+
+static inline int wpa_debug_open_linux_tracing(void)
+{
+	return 0;
+}
+
+static inline void wpa_debug_close_linux_tracing(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
 
 #ifdef EAPOL_TEST
 #define WPA_ASSERT(a)						       \
diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c
index b007e66..b257b36 100644
--- a/src/utils/wpabuf.c
+++ b/src/utils/wpabuf.c
@@ -1,6 +1,6 @@
 /*
  * Dynamic data buffer
- * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -68,12 +68,12 @@
 
 	if (buf->used + add_len > buf->size) {
 		unsigned char *nbuf;
-		if (buf->ext_data) {
-			nbuf = os_realloc(buf->ext_data, buf->used + add_len);
+		if (buf->flags & WPABUF_FLAG_EXT_DATA) {
+			nbuf = os_realloc(buf->buf, buf->used + add_len);
 			if (nbuf == NULL)
 				return -1;
 			os_memset(nbuf + buf->used, 0, add_len);
-			buf->ext_data = nbuf;
+			buf->buf = nbuf;
 		} else {
 #ifdef WPA_TRACE
 			nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) +
@@ -95,6 +95,7 @@
 			os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0,
 				  add_len);
 #endif /* WPA_TRACE */
+			buf->buf = (u8 *) (buf + 1);
 			*_buf = buf;
 		}
 		buf->size = buf->used + add_len;
@@ -126,6 +127,7 @@
 #endif /* WPA_TRACE */
 
 	buf->size = len;
+	buf->buf = (u8 *) (buf + 1);
 	return buf;
 }
 
@@ -148,7 +150,8 @@
 
 	buf->size = len;
 	buf->used = len;
-	buf->ext_data = data;
+	buf->buf = data;
+	buf->flags |= WPABUF_FLAG_EXT_DATA;
 
 	return buf;
 }
@@ -189,12 +192,14 @@
 		wpa_trace_show("wpabuf_free magic mismatch");
 		abort();
 	}
-	os_free(buf->ext_data);
+	if (buf->flags & WPABUF_FLAG_EXT_DATA)
+		os_free(buf->buf);
 	os_free(trace);
 #else /* WPA_TRACE */
 	if (buf == NULL)
 		return;
-	os_free(buf->ext_data);
+	if (buf->flags & WPABUF_FLAG_EXT_DATA)
+		os_free(buf->buf);
 	os_free(buf);
 #endif /* WPA_TRACE */
 }
diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h
index 8c36b40..dbce925 100644
--- a/src/utils/wpabuf.h
+++ b/src/utils/wpabuf.h
@@ -1,6 +1,6 @@
 /*
  * Dynamic data buffer
- * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,6 +9,9 @@
 #ifndef WPABUF_H
 #define WPABUF_H
 
+/* wpabuf::buf is a pointer to external data */
+#define WPABUF_FLAG_EXT_DATA BIT(0)
+
 /*
  * Internal data structure for wpabuf. Please do not touch this directly from
  * elsewhere. This is only defined in header file to allow inline functions
@@ -17,8 +20,8 @@
 struct wpabuf {
 	size_t size; /* total size of the allocated buffer */
 	size_t used; /* length of data in the buffer */
-	u8 *ext_data; /* pointer to external data; NULL if data follows
-		       * struct wpabuf */
+	u8 *buf; /* pointer to the head of the buffer */
+	unsigned int flags;
 	/* optionally followed by the allocated buffer */
 };
 
@@ -72,9 +75,7 @@
  */
 static inline const void * wpabuf_head(const struct wpabuf *buf)
 {
-	if (buf->ext_data)
-		return buf->ext_data;
-	return buf + 1;
+	return buf->buf;
 }
 
 static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
@@ -89,9 +90,7 @@
  */
 static inline void * wpabuf_mhead(struct wpabuf *buf)
 {
-	if (buf->ext_data)
-		return buf->ext_data;
-	return buf + 1;
+	return buf->buf;
 }
 
 static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf)
@@ -150,7 +149,8 @@
 
 static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len)
 {
-	buf->ext_data = (u8 *) data;
+	buf->buf = (u8 *) data;
+	buf->flags = WPABUF_FLAG_EXT_DATA;
 	buf->size = buf->used = len;
 }
 
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index 9baec7f..7630ecb 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -1,16 +1,10 @@
 /*
  * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup
  *   Reference is "NFCForum-TS-NDEF_1.0 2006-07-24".
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -26,9 +20,9 @@
 #define FLAG_TNF_RFC2046 (0x02)
 
 struct ndef_record {
-	u8 *type;
-	u8 *id;
-	u8 *payload;
+	const u8 *type;
+	const u8 *id;
+	const u8 *payload;
 	u8 type_length;
 	u8 id_length;
 	u32 payload_length;
@@ -37,9 +31,10 @@
 
 static char wifi_handover_type[] = "application/vnd.wfa.wsc";
 
-static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record)
+static int ndef_parse_record(const u8 *data, u32 size,
+			     struct ndef_record *record)
 {
-	u8 *pos = data + 1;
+	const u8 *pos = data + 1;
 
 	if (size < 2)
 		return -1;
@@ -78,12 +73,12 @@
 }
 
 
-static struct wpabuf * ndef_parse_records(struct wpabuf *buf,
+static struct wpabuf * ndef_parse_records(const struct wpabuf *buf,
 					  int (*filter)(struct ndef_record *))
 {
 	struct ndef_record record;
 	int len = wpabuf_len(buf);
-	u8 *data = wpabuf_mhead(buf);
+	const u8 *data = wpabuf_head(buf);
 
 	while (len > 0) {
 		if (ndef_parse_record(data, len, &record) < 0) {
@@ -103,13 +98,14 @@
 
 static struct wpabuf * ndef_build_record(u8 flags, void *type,
 					 u8 type_length, void *id,
-					 u8 id_length, void *payload,
-					 u32 payload_length)
+					 u8 id_length,
+					 const struct wpabuf *payload)
 {
 	struct wpabuf *record;
 	size_t total_len;
 	int short_record;
 	u8 local_flag;
+	size_t payload_length = wpabuf_len(payload);
 
 	short_record = payload_length < 256 ? 1 : 0;
 
@@ -144,7 +140,7 @@
 		wpabuf_put_u8(record, id_length);
 	wpabuf_put_data(record, type, type_length);
 	wpabuf_put_data(record, id, id_length);
-	wpabuf_put_data(record, payload, payload_length);
+	wpabuf_put_buf(record, payload);
 	return record;
 }
 
@@ -160,16 +156,15 @@
 }
 
 
-struct wpabuf * ndef_parse_wifi(struct wpabuf *buf)
+struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf)
 {
 	return ndef_parse_records(buf, wifi_filter);
 }
 
 
-struct wpabuf * ndef_build_wifi(struct wpabuf *buf)
+struct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
 {
 	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
 				 FLAG_TNF_RFC2046, wifi_handover_type,
-				 os_strlen(wifi_handover_type), NULL, 0,
-				 wpabuf_mhead(buf), wpabuf_len(buf));
+				 os_strlen(wifi_handover_type), NULL, 0, buf);
 }
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 9422c71..4c2322d 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -56,6 +56,23 @@
 		data->dev_password_len = cfg->pin_len;
 	}
 
+#ifdef CONFIG_WPS_NFC
+	if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
+		data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id;
+		os_free(data->dev_password);
+		data->dev_password =
+			os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+		if (data->dev_password == NULL) {
+			os_free(data);
+			return NULL;
+		}
+		os_memcpy(data->dev_password,
+			  wpabuf_head(cfg->wps->ap_nfc_dev_pw),
+			  wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+		data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw);
+	}
+#endif /* CONFIG_WPS_NFC */
+
 	data->pbc = cfg->pbc;
 	if (cfg->pbc) {
 		/* Use special PIN '00000000' for PBC */
@@ -118,6 +135,12 @@
  */
 void wps_deinit(struct wps_data *data)
 {
+#ifdef CONFIG_WPS_NFC
+	if (data->registrar && data->nfc_pw_token)
+		wps_registrar_remove_nfc_pw_token(data->wps->registrar,
+						  data->nfc_pw_token);
+#endif /* CONFIG_WPS_NFC */
+
 	if (data->wps_pin_revealed) {
 		wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
 			   "negotiation failed");
@@ -136,6 +159,7 @@
 	wps_device_data_free(&data->peer_dev);
 	os_free(data->new_ap_settings);
 	dh5_free(data->dh_ctx);
+	os_free(data->nfc_pw_token);
 	os_free(data);
 }
 
@@ -263,7 +287,8 @@
  * @msg: WPS IE contents from Beacon or Probe Response frame
  * @addr: MAC address to search for
  * @ver1_compat: Whether to use version 1 compatibility mode
- * Returns: 1 if address is authorized, 0 if not
+ * Returns: 2 if the specified address is explicit authorized, 1 if address is
+ * authorized (broadcast), 0 if not
  */
 int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
 			   int ver1_compat)
@@ -289,8 +314,9 @@
 
 	pos = attr.authorized_macs;
 	for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) {
-		if (os_memcmp(pos, addr, ETH_ALEN) == 0 ||
-		    os_memcmp(pos, bcast, ETH_ALEN) == 0)
+		if (os_memcmp(pos, addr, ETH_ALEN) == 0)
+			return 2;
+		if (os_memcmp(pos, bcast, ETH_ALEN) == 0)
 			return 1;
 		pos += ETH_ALEN;
 	}
@@ -431,7 +457,8 @@
 
 /**
  * wps_build_probe_req_ie - Build WPS IE for Probe Request
- * @pbc: Whether searching for PBC mode APs
+ * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for
+ * most other use cases)
  * @dev: Device attributes
  * @uuid: Own UUID
  * @req_type: Value for Request Type attribute
@@ -442,7 +469,7 @@
  *
  * The caller is responsible for freeing the buffer.
  */
-struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
 				       const u8 *uuid,
 				       enum wps_request_type req_type,
 				       unsigned int num_req_dev_types,
@@ -464,8 +491,7 @@
 	    wps_build_rf_bands(dev, ie) ||
 	    wps_build_assoc_state(NULL, ie) ||
 	    wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
-	    wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON :
-				      DEV_PW_DEFAULT) ||
+	    wps_build_dev_password_id(ie, pw_id) ||
 #ifdef CONFIG_WPS2
 	    wps_build_manufacturer(dev, ie) ||
 	    wps_build_model_name(dev, ie) ||
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 22e029f..c45b68c 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup
- * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -27,6 +27,7 @@
 struct wps_registrar;
 struct upnp_wps_device_sm;
 struct wps_er;
+struct wps_parse_attr;
 
 /**
  * struct wps_credential - WPS Credential
@@ -94,6 +95,7 @@
 	u32 os_version;
 	u8 rf_bands;
 	u16 config_methods;
+	struct wpabuf *vendor_ext_m1;
 	struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
 
 	int p2p;
@@ -238,7 +240,7 @@
 
 struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type);
 struct wpabuf * wps_build_assoc_resp_ie(void);
-struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
 				       const u8 *uuid,
 				       enum wps_request_type req_type,
 				       unsigned int num_req_dev_types,
@@ -294,12 +296,15 @@
 	 * @ctx: Higher layer context data (cb_ctx)
 	 * @mac_addr: MAC address of the Enrollee
 	 * @uuid_e: UUID-E of the Enrollee
+	 * @dev_pw: Device Password (PIN) used during registration
+	 * @dev_pw_len: Length of dev_pw in octets
 	 *
 	 * This callback is called whenever an Enrollee completes registration
 	 * successfully.
 	 */
 	void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
-			       const u8 *uuid_e);
+			       const u8 *uuid_e, const u8 *dev_pw,
+			       size_t dev_pw_len);
 
 	/**
 	 * set_sel_reg_cb - Callback for reporting selected registrar changes
@@ -752,6 +757,11 @@
 
 	/* Pending messages from UPnP PutWLANResponse */
 	struct upnp_pending_message *upnp_msgs;
+
+	u16 ap_nfc_dev_pw_id;
+	struct wpabuf *ap_nfc_dh_pubkey;
+	struct wpabuf *ap_nfc_dh_privkey;
+	struct wpabuf *ap_nfc_dev_pw;
 };
 
 struct oob_device_data {
@@ -783,7 +793,8 @@
 int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
 int wps_registrar_button_pushed(struct wps_registrar *reg,
 				const u8 *p2p_dev_addr);
-void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e);
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+			    const u8 *dev_pw, size_t dev_pw_len);
 void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
 				const struct wpabuf *wps_data,
 				int p2p_wildcard);
@@ -792,6 +803,12 @@
 			   char *buf, size_t buflen);
 int wps_registrar_config_ap(struct wps_registrar *reg,
 			    struct wps_credential *cred);
+int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
+				   const u8 *pubkey_hash, u16 pw_id,
+				   const u8 *dev_pw, size_t dev_pw_len);
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+					 const u8 *oob_dev_pw,
+					 size_t oob_dev_pw_len);
 
 int wps_build_credential_wrap(struct wpabuf *msg,
 			      const struct wps_credential *cred);
@@ -799,6 +816,7 @@
 unsigned int wps_pin_checksum(unsigned int pin);
 unsigned int wps_pin_valid(unsigned int pin);
 unsigned int wps_generate_pin(void);
+int wps_pin_str_valid(const char *pin);
 void wps_free_pending_msgs(struct upnp_pending_message *msgs);
 
 struct oob_device_data * wps_get_oob_device(char *device_type);
@@ -806,6 +824,8 @@
 int wps_get_oob_method(char *method);
 int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
 		    int registrar);
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps);
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
 int wps_attr_text(struct wpabuf *data, char *buf, char *end);
 
 struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname,
@@ -821,12 +841,23 @@
 		      const struct wps_credential *cred);
 int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
 		  size_t pin_len, const struct wps_credential *cred);
+struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid);
 
 int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
 char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
 			    size_t buf_len);
 void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
 u16 wps_config_methods_str2bin(const char *str);
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+				       const struct wpabuf *pubkey,
+				       const struct wpabuf *dev_pw);
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+				  struct wpabuf **privkey,
+				  struct wpabuf **dev_pw);
+
+/* ndef.c */
+struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
 
 #ifdef CONFIG_WPS_STRICT
 int wps_validate_beacon(const struct wpabuf *wps_ie);
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index 753083d..9be30b9 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -30,6 +30,14 @@
 		wps->dh_ctx = wps->wps->dh_ctx;
 		wps->wps->dh_ctx = NULL;
 		pubkey = wpabuf_dup(wps->wps->dh_pubkey);
+#ifdef CONFIG_WPS_NFC
+	} else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
+		   wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
+		wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
+		wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey);
+		pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey);
+		wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey);
+#endif /* CONFIG_WPS_NFC */
 	} else {
 		wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys");
 		wps->dh_privkey = NULL;
@@ -340,19 +348,34 @@
 
 
 #ifdef CONFIG_WPS_OOB
-int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+			 const struct wpabuf *pubkey, const u8 *dev_pw,
+			 size_t dev_pw_len)
 {
 	size_t hash_len;
 	const u8 *addr[1];
 	u8 pubkey_hash[WPS_HASH_LEN];
+
+	addr[0] = wpabuf_head(pubkey);
+	hash_len = wpabuf_len(pubkey);
+	sha256_vector(1, addr, &hash_len, pubkey_hash);
+
+	wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
+	wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
+	wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+	wpabuf_put_be16(msg, dev_pw_id);
+	wpabuf_put_data(msg, dev_pw, dev_pw_len);
+
+	return 0;
+}
+
+
+int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
+{
 	u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN];
 
 	wpa_printf(MSG_DEBUG, "WPS:  * OOB Device Password");
 
-	addr[0] = wpabuf_head(wps->dh_pubkey);
-	hash_len = wpabuf_len(wps->dh_pubkey);
-	sha256_vector(1, addr, &hash_len, pubkey_hash);
-
 	if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) {
 		wpa_printf(MSG_ERROR, "WPS: device password id "
 			   "generation error");
@@ -367,19 +390,15 @@
 		return -1;
 	}
 
-	wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
-	wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
-	wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
-	wpabuf_put_be16(msg, wps->oob_dev_pw_id);
-	wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
-
 	wpa_snprintf_hex_uppercase(
 		wpabuf_put(wps->oob_conf.dev_password,
 			   wpabuf_size(wps->oob_conf.dev_password)),
 		wpabuf_size(wps->oob_conf.dev_password),
 		dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
 
-	return 0;
+	return wps_build_oob_dev_pw(msg, wps->oob_dev_pw_id, wps->dh_pubkey,
+				    dev_password_bin,
+				    WPS_OOB_DEVICE_PASSWORD_LEN);
 }
 #endif /* CONFIG_WPS_OOB */
 
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index f5ad403..5aa9b00 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -9,7 +9,8 @@
 #include "includes.h"
 
 #include "common.h"
-#include "wps_i.h"
+#include "wps_defs.h"
+#include "wps_attr_parse.h"
 
 #ifndef CONFIG_WPS_STRICT
 #define WPS_WORKAROUNDS
@@ -262,12 +263,16 @@
 		attr->dev_password_id = pos;
 		break;
 	case ATTR_OOB_DEVICE_PASSWORD:
-		if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) {
+		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		    WPS_OOB_DEVICE_PASSWORD_LEN) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
 				   "Password length %u", len);
 			return -1;
 		}
 		attr->oob_dev_password = pos;
+		attr->oob_dev_password_len = len;
 		break;
 	case ATTR_OS_VERSION:
 		if (len != 4) {
diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h
new file mode 100644
index 0000000..332e966
--- /dev/null
+++ b/src/wps/wps_attr_parse.h
@@ -0,0 +1,107 @@
+/*
+ * Wi-Fi Protected Setup - attribute parsing
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_ATTR_PARSE_H
+#define WPS_ATTR_PARSE_H
+
+#include "wps.h"
+
+struct wps_parse_attr {
+	/* fixed length fields */
+	const u8 *version; /* 1 octet */
+	const u8 *version2; /* 1 octet */
+	const u8 *msg_type; /* 1 octet */
+	const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
+	const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
+	const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
+	const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
+	const u8 *auth_type_flags; /* 2 octets */
+	const u8 *encr_type_flags; /* 2 octets */
+	const u8 *conn_type_flags; /* 1 octet */
+	const u8 *config_methods; /* 2 octets */
+	const u8 *sel_reg_config_methods; /* 2 octets */
+	const u8 *primary_dev_type; /* 8 octets */
+	const u8 *rf_bands; /* 1 octet */
+	const u8 *assoc_state; /* 2 octets */
+	const u8 *config_error; /* 2 octets */
+	const u8 *dev_password_id; /* 2 octets */
+	const u8 *os_version; /* 4 octets */
+	const u8 *wps_state; /* 1 octet */
+	const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
+	const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
+	const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
+	const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
+	const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
+	const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
+	const u8 *auth_type; /* 2 octets */
+	const u8 *encr_type; /* 2 octets */
+	const u8 *network_idx; /* 1 octet */
+	const u8 *network_key_idx; /* 1 octet */
+	const u8 *mac_addr; /* ETH_ALEN (6) octets */
+	const u8 *key_prov_auto; /* 1 octet (Bool) */
+	const u8 *dot1x_enabled; /* 1 octet (Bool) */
+	const u8 *selected_registrar; /* 1 octet (Bool) */
+	const u8 *request_type; /* 1 octet */
+	const u8 *response_type; /* 1 octet */
+	const u8 *ap_setup_locked; /* 1 octet */
+	const u8 *settings_delay_time; /* 1 octet */
+	const u8 *network_key_shareable; /* 1 octet (Bool) */
+	const u8 *request_to_enroll; /* 1 octet (Bool) */
+
+	/* variable length fields */
+	const u8 *manufacturer;
+	size_t manufacturer_len;
+	const u8 *model_name;
+	size_t model_name_len;
+	const u8 *model_number;
+	size_t model_number_len;
+	const u8 *serial_number;
+	size_t serial_number_len;
+	const u8 *dev_name;
+	size_t dev_name_len;
+	const u8 *public_key;
+	size_t public_key_len;
+	const u8 *encr_settings;
+	size_t encr_settings_len;
+	const u8 *ssid; /* <= 32 octets */
+	size_t ssid_len;
+	const u8 *network_key; /* <= 64 octets */
+	size_t network_key_len;
+	const u8 *eap_type; /* <= 8 octets */
+	size_t eap_type_len;
+	const u8 *eap_identity; /* <= 64 octets */
+	size_t eap_identity_len;
+	const u8 *authorized_macs; /* <= 30 octets */
+	size_t authorized_macs_len;
+	const u8 *sec_dev_type_list; /* <= 128 octets */
+	size_t sec_dev_type_list_len;
+	const u8 *oob_dev_password; /* 38..54 octets */
+	size_t oob_dev_password_len;
+
+	/* attributes that can occur multiple times */
+#define MAX_CRED_COUNT 10
+	const u8 *cred[MAX_CRED_COUNT];
+	size_t cred_len[MAX_CRED_COUNT];
+	size_t num_cred;
+
+#define MAX_REQ_DEV_TYPE_COUNT 10
+	const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
+	size_t num_req_dev_type;
+
+	const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+	size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+	size_t num_vendor_ext;
+};
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+
+#endif /* WPS_ATTR_PARSE_H */
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 2dff4b2..5a8817f 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - common functionality
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -249,6 +249,22 @@
 }
 
 
+int wps_pin_str_valid(const char *pin)
+{
+	const char *p;
+	size_t len;
+
+	p = pin;
+	while (*p >= '0' && *p <= '9')
+		p++;
+	if (*p != '\0')
+		return 0;
+
+	len = p - pin;
+	return len == 4 || len == 8;
+}
+
+
 void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
 		    u16 config_error, u16 error_indication)
 {
@@ -308,7 +324,7 @@
 
 #ifdef CONFIG_WPS_OOB
 
-static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
 {
 	struct wps_data data;
 	struct wpabuf *plain;
@@ -335,11 +351,35 @@
 }
 
 
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+				       const struct wpabuf *pubkey,
+				       const struct wpabuf *dev_pw)
+{
+	struct wpabuf *data;
+
+	data = wpabuf_alloc(200);
+	if (data == NULL)
+		return NULL;
+
+	if (wps_build_version(data) ||
+	    wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
+				 wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
+	    wps_build_wfa_ext(data, 0, NULL, 0)) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
+			   "token");
+		wpabuf_free(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
 static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
 {
 	struct wpabuf *data;
 
-	data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+	data = wpabuf_alloc(200);
 	if (data == NULL) {
 		wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
 			   "device password attribute");
@@ -375,6 +415,7 @@
 	struct oob_conf_data *oob_conf = &wps->oob_conf;
 	struct wps_parse_attr attr;
 	const u8 *pos;
+	size_t pw_len;
 
 	if (wps_parse_msg(data, &attr) < 0 ||
 	    attr.oob_dev_password == NULL) {
@@ -384,6 +425,7 @@
 
 	pos = attr.oob_dev_password;
 
+	wpabuf_free(oob_conf->pubkey_hash);
 	oob_conf->pubkey_hash =
 		wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN);
 	if (oob_conf->pubkey_hash == NULL) {
@@ -396,39 +438,32 @@
 	wps->oob_dev_pw_id = WPA_GET_BE16(pos);
 	pos += sizeof(wps->oob_dev_pw_id);
 
-	oob_conf->dev_password =
-		wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+	pw_len = attr.oob_dev_password_len - WPS_OOB_PUBKEY_HASH_LEN - 2;
+	oob_conf->dev_password = wpabuf_alloc(pw_len * 2 + 1);
 	if (oob_conf->dev_password == NULL) {
 		wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
 			   "device password");
 		return -1;
 	}
 	wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password,
-				   wpabuf_size(oob_conf->dev_password)),
-				   wpabuf_size(oob_conf->dev_password), pos,
-				   WPS_OOB_DEVICE_PASSWORD_LEN);
+					      pw_len * 2 + 1),
+				   pw_len * 2 + 1, pos, pw_len);
 
 	return 0;
 }
 
 
-static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr)
 {
 	struct wpabuf msg;
-	struct wps_parse_attr attr;
 	size_t i;
 
-	if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) {
-		wpa_printf(MSG_ERROR, "WPS: OOB credential not found");
-		return -1;
-	}
-
-	for (i = 0; i < attr.num_cred; i++) {
+	for (i = 0; i < attr->num_cred; i++) {
 		struct wps_credential local_cred;
 		struct wps_parse_attr cattr;
 
 		os_memset(&local_cred, 0, sizeof(local_cred));
-		wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]);
+		wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]);
 		if (wps_parse_msg(&msg, &cattr) < 0 ||
 		    wps_process_cred(&cattr, &local_cred)) {
 			wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
@@ -442,6 +477,19 @@
 }
 
 
+static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
+{
+	struct wps_parse_attr attr;
+
+	if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) {
+		wpa_printf(MSG_ERROR, "WPS: OOB credential not found");
+		return -1;
+	}
+
+	return wps_oob_use_cred(wps, &attr);
+}
+
+
 int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
 		    int registrar)
 {
@@ -695,3 +743,53 @@
 
 	return msg;
 }
+
+
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+				  struct wpabuf **privkey,
+				  struct wpabuf **dev_pw)
+{
+	struct wpabuf *priv = NULL, *pub = NULL, *pw, *ret;
+	void *dh_ctx;
+	u16 val;
+
+	pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
+	if (pw == NULL)
+		return NULL;
+
+	if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
+			     WPS_OOB_DEVICE_PASSWORD_LEN) ||
+	    random_get_bytes((u8 *) &val, sizeof(val))) {
+		wpabuf_free(pw);
+		return NULL;
+	}
+
+	dh_ctx = dh5_init(&priv, &pub);
+	if (dh_ctx == NULL) {
+		wpabuf_free(pw);
+		return NULL;
+	}
+	dh5_free(dh_ctx);
+
+	*id = 0x10 + val % 0xfff0;
+	wpabuf_free(*pubkey);
+	*pubkey = pub;
+	wpabuf_free(*privkey);
+	*privkey = priv;
+	wpabuf_free(*dev_pw);
+	*dev_pw = pw;
+
+	ret = wps_build_nfc_pw_token(*id, *pubkey, *dev_pw);
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index e128a19..2f42603 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -41,7 +41,7 @@
 #define WPS_MGMTAUTHKEY_LEN 32
 #define WPS_MGMTENCKEY_LEN 16
 #define WPS_MGMT_KEY_ID_LEN 16
-#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54
+#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16
 #define WPS_OOB_DEVICE_PASSWORD_LEN 32
 #define WPS_OOB_PUBKEY_HASH_LEN 20
 
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
index 559582d..3c94a43 100644
--- a/src/wps/wps_dev_attr.c
+++ b/src/wps/wps_dev_attr.c
@@ -203,6 +203,20 @@
 }
 
 
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	if (dev->vendor_ext_m1 != NULL) {
+		wpa_hexdump(MSG_DEBUG, "WPS:  * Vendor Extension M1",
+			    wpabuf_head_u8(dev->vendor_ext_m1),
+			    wpabuf_len(dev->vendor_ext_m1));
+		wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+		wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1));
+		wpabuf_put_buf(msg, dev->vendor_ext_m1);
+	}
+	return 0;
+}
+
+
 int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
 {
 	wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", dev->rf_bands);
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
index 7ca81ad..200c9c4 100644
--- a/src/wps/wps_dev_attr.h
+++ b/src/wps/wps_dev_attr.h
@@ -17,6 +17,7 @@
 int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_primary_dev_type(struct wps_device_data *dev,
 			       struct wpabuf *msg);
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 85d2e95..389aa84 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -163,7 +163,8 @@
 	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
 	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
 	    wps_build_os_version(&wps->wps->dev, msg) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
@@ -1149,7 +1150,7 @@
 		return WPS_FAILURE;
 
 	if (attr.enrollee_nonce == NULL ||
-	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 		return WPS_FAILURE;
 	}
@@ -1241,14 +1242,14 @@
 	}
 
 	if (attr.registrar_nonce == NULL ||
-	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
 	{
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 		return WPS_FAILURE;
 	}
 
 	if (attr.enrollee_nonce == NULL ||
-	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 		return WPS_FAILURE;
 	}
@@ -1288,7 +1289,7 @@
 	}
 
 	if (attr.registrar_nonce == NULL ||
-	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
 	{
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 		wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
@@ -1299,7 +1300,7 @@
 	}
 
 	if (attr.enrollee_nonce == NULL ||
-	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 		wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
 			    attr.enrollee_nonce, WPS_NONCE_LEN);
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 0655a3a..95a0dec 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - External Registrar
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -1996,3 +1996,41 @@
 
 	return 0;
 }
+
+
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid)
+{
+	struct wps_er_ap *ap;
+	struct wpabuf *ret;
+	struct wps_data data;
+
+	if (er == NULL)
+		return NULL;
+
+	ap = wps_er_ap_get(er, NULL, uuid);
+	if (ap == NULL)
+		return NULL;
+	if (ap->ap_settings == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the "
+			   "selected AP");
+		return NULL;
+	}
+
+	ret = wpabuf_alloc(500);
+	if (ret == NULL)
+		return NULL;
+
+	os_memset(&data, 0, sizeof(data));
+	data.wps = er->wps;
+	data.use_cred = ap->ap_settings;
+	if (wps_build_version(ret) ||
+	    wps_build_cred(&data, ret) ||
+	    wps_build_wfa_ext(ret, 0, NULL, 0)) {
+		wpabuf_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index 1297f65..86ad248 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - internal definitions
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,9 @@
 #define WPS_I_H
 
 #include "wps.h"
+#include "wps_attr_parse.h"
+
+struct wps_nfc_pw_token;
 
 /**
  * struct wps_data - WPS registration protocol data
@@ -114,100 +117,11 @@
 	u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or
 				    * 00:00:00:00:00:00 if not a P2p client */
 	int pbc_in_m1;
+
+	struct wps_nfc_pw_token *nfc_pw_token;
 };
 
 
-struct wps_parse_attr {
-	/* fixed length fields */
-	const u8 *version; /* 1 octet */
-	const u8 *version2; /* 1 octet */
-	const u8 *msg_type; /* 1 octet */
-	const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
-	const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
-	const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
-	const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
-	const u8 *auth_type_flags; /* 2 octets */
-	const u8 *encr_type_flags; /* 2 octets */
-	const u8 *conn_type_flags; /* 1 octet */
-	const u8 *config_methods; /* 2 octets */
-	const u8 *sel_reg_config_methods; /* 2 octets */
-	const u8 *primary_dev_type; /* 8 octets */
-	const u8 *rf_bands; /* 1 octet */
-	const u8 *assoc_state; /* 2 octets */
-	const u8 *config_error; /* 2 octets */
-	const u8 *dev_password_id; /* 2 octets */
-	const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54)
-				     * octets */
-	const u8 *os_version; /* 4 octets */
-	const u8 *wps_state; /* 1 octet */
-	const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
-	const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
-	const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
-	const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
-	const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
-	const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
-	const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
-	const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
-	const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
-	const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
-	const u8 *auth_type; /* 2 octets */
-	const u8 *encr_type; /* 2 octets */
-	const u8 *network_idx; /* 1 octet */
-	const u8 *network_key_idx; /* 1 octet */
-	const u8 *mac_addr; /* ETH_ALEN (6) octets */
-	const u8 *key_prov_auto; /* 1 octet (Bool) */
-	const u8 *dot1x_enabled; /* 1 octet (Bool) */
-	const u8 *selected_registrar; /* 1 octet (Bool) */
-	const u8 *request_type; /* 1 octet */
-	const u8 *response_type; /* 1 octet */
-	const u8 *ap_setup_locked; /* 1 octet */
-	const u8 *settings_delay_time; /* 1 octet */
-	const u8 *network_key_shareable; /* 1 octet (Bool) */
-	const u8 *request_to_enroll; /* 1 octet (Bool) */
-
-	/* variable length fields */
-	const u8 *manufacturer;
-	size_t manufacturer_len;
-	const u8 *model_name;
-	size_t model_name_len;
-	const u8 *model_number;
-	size_t model_number_len;
-	const u8 *serial_number;
-	size_t serial_number_len;
-	const u8 *dev_name;
-	size_t dev_name_len;
-	const u8 *public_key;
-	size_t public_key_len;
-	const u8 *encr_settings;
-	size_t encr_settings_len;
-	const u8 *ssid; /* <= 32 octets */
-	size_t ssid_len;
-	const u8 *network_key; /* <= 64 octets */
-	size_t network_key_len;
-	const u8 *eap_type; /* <= 8 octets */
-	size_t eap_type_len;
-	const u8 *eap_identity; /* <= 64 octets */
-	size_t eap_identity_len;
-	const u8 *authorized_macs; /* <= 30 octets */
-	size_t authorized_macs_len;
-	const u8 *sec_dev_type_list; /* <= 128 octets */
-	size_t sec_dev_type_list_len;
-
-	/* attributes that can occur multiple times */
-#define MAX_CRED_COUNT 10
-	const u8 *cred[MAX_CRED_COUNT];
-	size_t cred_len[MAX_CRED_COUNT];
-	size_t num_cred;
-
-#define MAX_REQ_DEV_TYPE_COUNT 10
-	const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
-	size_t num_req_dev_type;
-
-	const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
-	size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
-	size_t num_vendor_ext;
-};
-
 /* wps_common.c */
 void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
 	     const char *label, u8 *res, size_t res_len);
@@ -230,9 +144,6 @@
 struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
 struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
 
-/* wps_attr_parse.c */
-int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
-
 /* wps_attr_build.c */
 int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type);
@@ -255,6 +166,9 @@
 int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+			 const struct wpabuf *pubkey, const u8 *dev_pw,
+			 size_t dev_pw_len);
 int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps);
 struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
 
@@ -288,9 +202,7 @@
 const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count);
 int wps_registrar_pbc_overlap(struct wps_registrar *reg,
 			      const u8 *addr, const u8 *uuid_e);
-
-/* ndef.c */
-struct wpabuf * ndef_parse_wifi(struct wpabuf *buf);
-struct wpabuf * ndef_build_wifi(struct wpabuf *buf);
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+				       struct wps_nfc_pw_token *token);
 
 #endif /* WPS_I_H */
diff --git a/src/wps/wps_nfc.c b/src/wps/wps_nfc.c
index ff12000..6804350 100644
--- a/src/wps/wps_nfc.c
+++ b/src/wps/wps_nfc.c
@@ -1,15 +1,9 @@
 /*
  * NFC routines for Wi-Fi Protected Setup
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/src/wps/wps_nfc_pn531.c b/src/wps/wps_nfc_pn531.c
index 7e05e4d..c2bf457 100644
--- a/src/wps/wps_nfc_pn531.c
+++ b/src/wps/wps_nfc_pn531.c
@@ -1,15 +1,9 @@
 /*
  * NFC PN531 routines for Wi-Fi Protected Setup
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 5ed7ea4..53684d6 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - Registrar
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -26,6 +26,53 @@
 #define WPS_WORKAROUNDS
 #endif /* CONFIG_WPS_STRICT */
 
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_pw_token {
+	struct dl_list list;
+	u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+	u16 pw_id;
+	u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN];
+	size_t dev_pw_len;
+};
+
+
+static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token)
+{
+	dl_list_del(&token->list);
+	os_free(token);
+}
+
+
+static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id)
+{
+	struct wps_nfc_pw_token *token, *prev;
+	dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token,
+			      list) {
+		if (pw_id == 0 || pw_id == token->pw_id)
+			wps_remove_nfc_pw_token(token);
+	}
+}
+
+
+static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens,
+						      u16 pw_id)
+{
+	struct wps_nfc_pw_token *token;
+	dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) {
+		if (pw_id == token->pw_id)
+			return token;
+	}
+	return NULL;
+}
+
+#else /* CONFIG_WPS_NFC */
+
+#define wps_free_nfc_pw_tokens(t, p) do { } while (0)
+
+#endif /* CONFIG_WPS_NFC */
+
+
 struct wps_uuid_pin {
 	struct dl_list list;
 	u8 uuid[WPS_UUID_LEN];
@@ -102,7 +149,8 @@
 	void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
 			      const struct wps_device_data *dev);
 	void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
-			       const u8 *uuid_e);
+			       const u8 *uuid_e, const u8 *dev_pw,
+			       size_t dev_pw_len);
 	void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
 			       u16 sel_reg_config_methods);
 	void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
@@ -112,6 +160,7 @@
 	void *cb_ctx;
 
 	struct dl_list pins;
+	struct dl_list nfc_pw_tokens;
 	struct wps_pbc_session *pbc_sessions;
 
 	int skip_cred_build;
@@ -484,12 +533,16 @@
 {
 	*methods |= WPS_CONFIG_PUSHBUTTON;
 #ifdef CONFIG_WPS2
-	if (conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON)
+	if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) ==
+	    WPS_CONFIG_VIRT_PUSHBUTTON)
 		*methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
-	if (conf_methods & WPS_CONFIG_PHY_PUSHBUTTON)
+	if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) ==
+	    WPS_CONFIG_PHY_PUSHBUTTON)
 		*methods |= WPS_CONFIG_PHY_PUSHBUTTON;
-	if (!(*methods & (WPS_CONFIG_VIRT_PUSHBUTTON |
-			  WPS_CONFIG_PHY_PUSHBUTTON))) {
+	if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) !=
+	    WPS_CONFIG_VIRT_PUSHBUTTON &&
+	    (*methods & WPS_CONFIG_PHY_PUSHBUTTON) !=
+	    WPS_CONFIG_PHY_PUSHBUTTON) {
 		/*
 		 * Required to include virtual/physical flag, but we were not
 		 * configured with push button type, so have to default to one
@@ -591,6 +644,7 @@
 		return NULL;
 
 	dl_list_init(&reg->pins);
+	dl_list_init(&reg->nfc_pw_tokens);
 	reg->wps = wps;
 	reg->new_psk_cb = cfg->new_psk_cb;
 	reg->set_ie_cb = cfg->set_ie_cb;
@@ -634,6 +688,7 @@
 	eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
 	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
 	wps_free_pins(&reg->pins);
+	wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, 0);
 	wps_free_pbc_sessions(reg->pbc_sessions);
 	wpabuf_free(reg->extra_cred);
 	wps_free_devices(reg->devices);
@@ -740,14 +795,22 @@
 /**
  * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN
  * @reg: Registrar data from wps_registrar_init()
+ * @dev_pw: PIN to search for or %NULL to match any
+ * @dev_pw_len: Length of dev_pw in octets
  * Returns: 0 on success, -1 if not wildcard PIN is enabled
  */
-static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg)
+static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg,
+						 const u8 *dev_pw,
+						 size_t dev_pw_len)
 {
 	struct wps_uuid_pin *pin, *prev;
 
 	dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
 	{
+		if (dev_pw && pin->pin &&
+		    (dev_pw_len != pin->pin_len ||
+		     os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0))
+			continue; /* different PIN */
 		if (pin->wildcard_uuid) {
 			wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
 				    pin->uuid, WPS_UUID_LEN);
@@ -945,7 +1008,8 @@
 }
 
 
-void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e)
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+			    const u8 *dev_pw, size_t dev_pw_len)
 {
 	if (registrar->pbc) {
 		wps_registrar_remove_pbc_session(registrar,
@@ -954,6 +1018,13 @@
 	} else {
 		wps_registrar_pin_completed(registrar);
 	}
+
+	if (dev_pw &&
+	    wps_registrar_invalidate_wildcard_pin(registrar, dev_pw,
+						  dev_pw_len) == 0) {
+		wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN",
+				dev_pw, dev_pw_len);
+	}
 }
 
 
@@ -968,7 +1039,7 @@
 		/* PIN Method */
 		wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it");
 		wps_registrar_pin_completed(reg);
-		wps_registrar_invalidate_wildcard_pin(reg);
+		wps_registrar_invalidate_wildcard_pin(reg, NULL, 0);
 		return 1;
 	}
 	return 0;
@@ -1071,12 +1142,13 @@
 
 
 static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr,
-			       const u8 *uuid_e)
+			       const u8 *uuid_e, const u8 *dev_pw,
+			       size_t dev_pw_len)
 {
 	if (reg->reg_success_cb == NULL)
 		return;
 
-	reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e);
+	reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len);
 }
 
 
@@ -1238,6 +1310,13 @@
 		wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC");
 		pin = (const u8 *) "00000000";
 		pin_len = 8;
+#ifdef CONFIG_WPS_NFC
+	} else if (wps->nfc_pw_token) {
+		wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC "
+			   "Password Token");
+		pin = wps->nfc_pw_token->dev_pw;
+		pin_len = wps->nfc_pw_token->dev_pw_len;
+#endif /* CONFIG_WPS_NFC */
 	} else {
 		pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
 					    &pin_len);
@@ -2424,8 +2503,34 @@
 		return WPS_CONTINUE;
 	}
 
+#ifdef CONFIG_WPS_NFC
+	if (wps->dev_pw_id >= 0x10) {
+		struct wps_nfc_pw_token *token;
+		const u8 *addr[1];
+		u8 hash[WPS_HASH_LEN];
+
+		token = wps_get_nfc_pw_token(
+			&wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
+		if (token) {
+			wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+				   "Password Token");
+			dl_list_del(&token->list);
+			wps->nfc_pw_token = token;
+
+			addr[0] = attr->public_key;
+			sha256_vector(1, addr, &attr->public_key_len, hash);
+			if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash,
+				      WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+				wpa_printf(MSG_ERROR, "WPS: Public Key hash "
+					   "mismatch");
+				return WPS_FAILURE;
+			}
+		}
+	}
+#endif /* CONFIG_WPS_NFC */
+
 #ifdef CONFIG_WPS_OOB
-	if (wps->dev_pw_id >= 0x10 &&
+	if (wps->dev_pw_id >= 0x10 && wps->nfc_pw_token == NULL &&
 	    wps->dev_pw_id != wps->wps->oob_dev_pw_id) {
 		wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID "
 			   "%d mismatch", wps->dev_pw_id);
@@ -2749,7 +2854,7 @@
 	if (*attr.msg_type != WPS_M1 &&
 	    (attr.registrar_nonce == NULL ||
 	     os_memcmp(wps->nonce_r, attr.registrar_nonce,
-		       WPS_NONCE_LEN != 0))) {
+		       WPS_NONCE_LEN) != 0)) {
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 		return WPS_FAILURE;
 	}
@@ -2845,14 +2950,14 @@
 #endif /* CONFIG_WPS_UPNP */
 
 	if (attr.registrar_nonce == NULL ||
-	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
 	{
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 		return WPS_FAILURE;
 	}
 
 	if (attr.enrollee_nonce == NULL ||
-	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 		return WPS_FAILURE;
 	}
@@ -2914,14 +3019,14 @@
 #endif /* CONFIG_WPS_UPNP */
 
 	if (attr.registrar_nonce == NULL ||
-	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
 	{
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 		return WPS_FAILURE;
 	}
 
 	if (attr.enrollee_nonce == NULL ||
-	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 		return WPS_FAILURE;
 	}
@@ -3000,14 +3105,14 @@
 #endif /* CONFIG_WPS_UPNP */
 
 	if (attr.registrar_nonce == NULL ||
-	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
 	{
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 		return WPS_FAILURE;
 	}
 
 	if (attr.enrollee_nonce == NULL ||
-	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 		return WPS_FAILURE;
 	}
@@ -3055,7 +3160,8 @@
 		wps->new_psk = NULL;
 	}
 
-	wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e);
+	wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e,
+			   wps->dev_password, wps->dev_password_len);
 
 	if (wps->pbc) {
 		wps_registrar_remove_pbc_session(wps->wps->registrar,
@@ -3348,3 +3454,84 @@
 
 	return -1;
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
+				   const u8 *pubkey_hash, u16 pw_id,
+				   const u8 *dev_pw, size_t dev_pw_len)
+{
+	struct wps_nfc_pw_token *token;
+
+	if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
+		return -1;
+
+	wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, pw_id);
+
+	token = os_zalloc(sizeof(*token));
+	if (token == NULL)
+		return -1;
+
+	os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+	token->pw_id = pw_id;
+	os_memcpy(token->dev_pw, dev_pw, dev_pw_len);
+	token->dev_pw_len = dev_pw_len;
+
+	dl_list_add(&reg->nfc_pw_tokens, &token->list);
+
+	reg->selected_registrar = 1;
+	reg->pbc = 0;
+	wps_registrar_add_authorized_mac(reg,
+					 (u8 *) "\xff\xff\xff\xff\xff\xff");
+	wps_registrar_selected_registrar_changed(reg);
+	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+	eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+			       wps_registrar_set_selected_timeout,
+			       reg, NULL);
+
+	return 0;
+}
+
+
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+					 const u8 *oob_dev_pw,
+					 size_t oob_dev_pw_len)
+{
+	const u8 *pos, *hash, *dev_pw;
+	u16 id;
+	size_t dev_pw_len;
+
+	if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+	    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+	    oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+	    WPS_OOB_DEVICE_PASSWORD_LEN)
+		return -1;
+
+	hash = oob_dev_pw;
+	pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN;
+	id = WPA_GET_BE16(pos);
+	dev_pw = pos + 2;
+	dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw;
+
+	wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u",
+		   id);
+
+	wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
+		    hash, WPS_OOB_PUBKEY_HASH_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
+
+	return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
+					      dev_pw_len);
+}
+
+
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+				       struct wps_nfc_pw_token *token)
+{
+	wps_registrar_remove_authorized_mac(reg,
+					    (u8 *) "\xff\xff\xff\xff\xff\xff");
+	wps_registrar_selected_registrar_changed(reg);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_ufd.c b/src/wps/wps_ufd.c
index 61f6553..f83bdf4 100644
--- a/src/wps/wps_ufd.c
+++ b/src/wps/wps_ufd.c
@@ -1,15 +1,9 @@
 /*
  * UFD routines for Wi-Fi Protected Setup
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -165,8 +159,10 @@
 	}
 
 	data = os_zalloc(sizeof(*data));
-	if (data == NULL)
+	if (data == NULL) {
+		close(ufd_fd);
 		return NULL;
+	}
 	data->ufd_fd = ufd_fd;
 	return data;
 }
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index 766cac4..09a46a2 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -305,15 +305,15 @@
 	int alloc_len;
 	char *scratch_mem = NULL;
 	char *mem;
-	char *domain_and_port;
+	char *host;
 	char *delim;
 	char *path;
-	char *domain;
 	int port = 80;  /* port to send to (default is port 80) */
 	struct addrinfo hints;
 	struct addrinfo *result = NULL;
 	struct addrinfo *rp;
 	int rerr;
+	size_t host_len, path_len;
 
 	/* url MUST begin with http: */
 	if (url_len < 7 || os_strncasecmp(url, "http://", 7))
@@ -321,30 +321,24 @@
 	url += 7;
 	url_len -= 7;
 
-	/* allocate memory for the extra stuff we need */
-	alloc_len = 2 * (url_len + 1);
-	scratch_mem = os_zalloc(alloc_len);
+	/* Make a copy of the string to allow modification during parsing */
+	scratch_mem = os_malloc(url_len + 1);
 	if (scratch_mem == NULL)
 		goto fail;
-	mem = scratch_mem;
-	os_strncpy(mem, url, url_len);
-	wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", mem);
-	domain_and_port = mem;
-	mem += 1 + os_strlen(mem);
-	delim = os_strchr(domain_and_port, '/');
+	os_memcpy(scratch_mem, url, url_len);
+	scratch_mem[url_len] = '\0';
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem);
+	host = scratch_mem;
+	path = os_strchr(host, '/');
+	if (path)
+		*path++ = '\0'; /* null terminate host */
+
+	/* Process and remove optional port component */
+	delim = os_strchr(host, ':');
 	if (delim) {
-		*delim++ = 0;   /* null terminate domain and port */
-		path = delim;
-	} else {
-		path = domain_and_port + os_strlen(domain_and_port);
-	}
-	domain = mem;
-	strcpy(domain, domain_and_port);
-	delim = os_strchr(domain, ':');
-	if (delim) {
-		*delim++ = 0;   /* null terminate domain */
-		if (isdigit(*delim))
-			port = atol(delim);
+		*delim = '\0'; /* null terminate host name for now */
+		if (isdigit(delim[1]))
+			port = atol(delim + 1);
 	}
 
 	/*
@@ -367,13 +361,21 @@
 	hints.ai_flags = 0;
 #endif
 	hints.ai_protocol = 0;          /* Any protocol? */
-	rerr = getaddrinfo(domain, NULL /* fill in port ourselves */,
+	rerr = getaddrinfo(host, NULL /* fill in port ourselves */,
 			   &hints, &result);
 	if (rerr) {
 		wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
-			   rerr, gai_strerror(rerr), domain);
+			   rerr, gai_strerror(rerr), host);
 		goto fail;
 	}
+
+	if (delim)
+		*delim = ':'; /* Restore port */
+
+	host_len = os_strlen(host);
+	path_len = path ? os_strlen(path) : 0;
+	alloc_len = host_len + 1 + 1 + path_len + 1;
+
 	for (rp = result; rp; rp = rp->ai_next) {
 		struct subscr_addr *a;
 
@@ -386,16 +388,16 @@
 
 		a = os_zalloc(sizeof(*a) + alloc_len);
 		if (a == NULL)
-			continue;
-		mem = (void *) (a + 1);
+			break;
+		mem = (char *) (a + 1);
 		a->domain_and_port = mem;
-		strcpy(mem, domain_and_port);
-		mem += 1 + strlen(mem);
+		os_memcpy(mem, host, host_len);
+		mem += host_len + 1;
 		a->path = mem;
-		if (path[0] != '/')
+		if (path == NULL || path[0] != '/')
 			*mem++ = '/';
-		strcpy(mem, path);
-		mem += 1 + os_strlen(mem);
+		if (path)
+			os_memcpy(mem, path, path_len);
 		os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
 		a->saddr.sin_port = htons(port);
 
diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c
index 4c4aebf..17a8207 100644
--- a/src/wps/wps_upnp_ssdp.c
+++ b/src/wps/wps_upnp_ssdp.c
@@ -866,20 +866,24 @@
 		return -1;
 
 #if 0   /* maybe ok if we sometimes block on writes */
-	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) {
+		close(sd);
 		return -1;
+	}
 #endif
 
 	if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
 		       &ip_addr, sizeof(ip_addr))) {
 		wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: "
 			   "%d (%s)", ip_addr, errno, strerror(errno));
+		close(sd);
 		return -1;
 	}
 	if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
 		       &ttl, sizeof(ttl))) {
 		wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): "
 			   "%d (%s)", errno, strerror(errno));
+		close(sd);
 		return -1;
 	}
 
@@ -898,6 +902,7 @@
 				   "WPS UPnP: setsockopt "
 				   "IP_ADD_MEMBERSHIP errno %d (%s)",
 				   errno, strerror(errno));
+			close(sd);
 			return -1;
 		}
 	}
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 065295a..4f58a92 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -33,6 +33,11 @@
 L_CFLAGS += -DANDROID_P2P
 endif
 
+ifeq ($(BOARD_WLAN_DEVICE), qcwcn)
+L_CFLAGS += -DANDROID_QCOM_WCN
+L_CFLAGS += -DANDROID_P2P
+endif
+
 # Use Android specific directory for control interface sockets
 L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
 L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/wpa_supplicant\"
@@ -67,6 +72,11 @@
 INCLUDES += external/libnl-headers
 endif
 
+ifdef CONFIG_FIPS
+CONFIG_NO_RANDOM_POOL=
+CONFIG_OPENSSL_CMAC=y
+endif
+
 OBJS = config.c
 OBJS += notify.c
 OBJS += bss.c
@@ -179,6 +189,11 @@
 NEED_AES_OMAC1=y
 endif
 
+ifdef CONFIG_IEEE80211V
+L_CFLAGS += -DCONFIG_IEEE80211V
+OBJS += wnm_sta.c
+endif
+
 ifdef CONFIG_TDLS
 L_CFLAGS += -DCONFIG_TDLS
 OBJS += src/rsn_supp/tdls.c
@@ -239,6 +254,17 @@
 endif
 endif
 
+ifdef CONFIG_WIFI_DISPLAY
+L_CFLAGS += -DCONFIG_WIFI_DISPLAY
+OBJS += wifi_display.c
+endif
+
+ifdef CONFIG_HS20
+OBJS += hs20_supplicant.c
+L_CFLAGS += -DCONFIG_HS20
+CONFIG_INTERWORKING=y
+endif
+
 ifdef CONFIG_INTERWORKING
 OBJS += interworking.c
 L_CFLAGS += -DCONFIG_INTERWORKING
@@ -729,6 +755,10 @@
 L_CFLAGS += -DCONFIG_IEEE80211N
 endif
 
+ifdef CONFIG_WNM
+L_CFLAGS += -DCONFIG_WNM
+endif
+
 ifdef NEED_AP_MLME
 OBJS += src/ap/wmm.c
 OBJS += src/ap/ap_list.c
@@ -741,6 +771,12 @@
 OBJS += src/ap/wps_hostapd.c
 OBJS += src/eap_server/eap_server_wsc.c
 endif
+ifdef CONFIG_INTERWORKING
+OBJS += src/ap/gas_serv.c
+endif
+ifdef CONFIG_HS20
+OBJS += src/ap/hs20.c
+endif
 endif
 
 ifdef NEED_RSN_AUTHENTICATOR
@@ -839,8 +875,10 @@
 # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
 OBJS += src/eap_peer/eap_tls_common.c
 OBJS_h += src/eap_server/eap_server_tls_common.c
+ifndef CONFIG_FIPS
 NEED_TLS_PRF=y
 endif
+endif
 
 ifndef CONFIG_TLS
 CONFIG_TLS=openssl
@@ -850,6 +888,11 @@
 L_CFLAGS += -DCONFIG_TLSV11
 endif
 
+ifdef CONFIG_TLSV12
+L_CFLAGS += -DCONFIG_TLSV12
+NEED_SHA256=y
+endif
+
 ifeq ($(CONFIG_TLS), openssl)
 ifdef TLS_FUNCS
 L_CFLAGS += -DEAP_TLS_OPENSSL
@@ -934,6 +977,9 @@
 NEED_SHA256=y
 NEED_BASE64=y
 NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
 NEED_MODEXP=y
 NEED_CIPHER=y
 L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
@@ -1039,8 +1085,12 @@
 endif
 ifdef NEED_AES_OMAC1
 NEED_AES_ENC=y
+ifdef CONFIG_OPENSSL_CMAC
+CFLAGS += -DCONFIG_OPENSSL_CMAC
+else
 AESOBJS += src/crypto/aes-omac1.c
 endif
+endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
 AESOBJS += src/crypto/aes-wrap.c
@@ -1060,7 +1110,10 @@
 
 SHA1OBJS =
 ifdef NEED_SHA1
+ifneq ($(CONFIG_TLS), openssl)
 SHA1OBJS += src/crypto/sha1.c
+endif
+SHA1OBJS += src/crypto/sha1-prf.c
 ifdef CONFIG_INTERNAL_SHA1
 SHA1OBJS += src/crypto/sha1-internal.c
 ifdef NEED_FIPS186_2_PRF
@@ -1070,8 +1123,10 @@
 ifdef CONFIG_NO_WPA_PASSPHRASE
 L_CFLAGS += -DCONFIG_NO_PBKDF2
 else
+ifneq ($(CONFIG_TLS), openssl)
 SHA1OBJS += src/crypto/sha1-pbkdf2.c
 endif
+endif
 ifdef NEED_T_PRF
 SHA1OBJS += src/crypto/sha1-tprf.c
 endif
@@ -1080,14 +1135,14 @@
 endif
 endif
 
-MD5OBJS = src/crypto/md5.c
+MD5OBJS =
+ifndef CONFIG_FIPS
+MD5OBJS += src/crypto/md5.c
+endif
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 MD5OBJS += src/crypto/md5-internal.c
 endif
-ifdef CONFIG_FIPS
-MD5OBJS += src/crypto/md5-non-fips.c
-endif
 OBJS += $(MD5OBJS)
 OBJS_p += $(MD5OBJS)
 endif
@@ -1114,10 +1169,16 @@
 SHA256OBJS = # none by default
 ifdef NEED_SHA256
 L_CFLAGS += -DCONFIG_SHA256
+ifneq ($(CONFIG_TLS), openssl)
 SHA256OBJS += src/crypto/sha256.c
+endif
+SHA256OBJS += src/crypto/sha256-prf.c
 ifdef CONFIG_INTERNAL_SHA256
 SHA256OBJS += src/crypto/sha256-internal.c
 endif
+ifdef NEED_TLS_PRF_SHA256
+SHA256OBJS += src/crypto/sha256-tlsprf.c
+endif
 OBJS += $(SHA256OBJS)
 endif
 
@@ -1157,6 +1218,11 @@
 ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
 L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
 endif
+ifeq ($(CONFIG_CTRL_IFACE), udp-remote)
+CONFIG_CTRL_IFACE=udp
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
+endif
 OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c
 endif
 
@@ -1277,6 +1343,10 @@
 endif
 endif
 
+ifdef CONFIG_DEBUG_LINUX_TRACING
+L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
+endif
+
 ifdef CONFIG_DEBUG_FILE
 L_CFLAGS += -DCONFIG_DEBUG_FILE
 endif
@@ -1292,6 +1362,7 @@
 OBJS += $(SHA1OBJS) $(DESOBJS)
 
 OBJS_p += $(SHA1OBJS)
+OBJS_p += $(SHA256OBJS)
 
 ifdef CONFIG_BGSCAN_SIMPLE
 L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE
@@ -1310,8 +1381,36 @@
 OBJS += bgscan.c
 endif
 
+ifdef CONFIG_AUTOSCAN_EXPONENTIAL
+L_CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL
+OBJS += autoscan_exponential.c
+NEED_AUTOSCAN=y
+endif
+
+ifdef CONFIG_AUTOSCAN_PERIODIC
+CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
+OBJS += autoscan_periodic.c
+NEED_AUTOSCAN=y
+endif
+
+ifdef NEED_AUTOSCAN
+L_CFLAGS += -DCONFIG_AUTOSCAN
+OBJS += autoscan.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_TEST
+OBJS += src/utils/ext_password_test.c
+L_CFLAGS += -DCONFIG_EXT_PASSWORD_TEST
+NEED_EXT_PASSWORD=y
+endif
+
+ifdef NEED_EXT_PASSWORD
+OBJS += src/utils/ext_password.c
+L_CFLAGS += -DCONFIG_EXT_PASSWORD
+endif
+
 ifdef NEED_GAS
-OBJS += ../src/common/gas.c
+OBJS += src/common/gas.c
 OBJS += gas_query.c
 L_CFLAGS += -DCONFIG_GAS
 NEED_OFFCHANNEL=y
@@ -1349,6 +1448,9 @@
 OBJS_priv += src/utils/wpa_debug.c
 OBJS_priv += src/utils/wpabuf.c
 OBJS_priv += wpa_priv.c
+ifdef CONFIG_DRIVER_NL80211
+OBJS_priv += src/common/ieee802_11_common.c
+endif
 ifdef CONFIG_DRIVER_TEST
 OBJS_priv += $(SHA1OBJS)
 OBJS_priv += $(MD5OBJS)
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index 06119c6..650b8a0 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,5 +1,179 @@
 ChangeLog for wpa_supplicant
 
+2012-05-10 - v1.0
+	* bsd: Add support for setting HT values in IFM_MMASK.
+	* Delay STA entry removal until Deauth/Disassoc TX status in AP mode.
+	  This allows the driver to use PS buffering of Deauthentication and
+	  Disassociation frames when the STA is in power save sleep. Only
+	  available with drivers that provide TX status events for Deauth/
+	  Disassoc frames (nl80211).
+	* Drop oldest unknown BSS table entries first. This makes it less
+	  likely to hit connection issues in environments with huge number
+	  of visible APs.
+	* Add systemd support.
+	* Add support for setting the syslog facility from the config file
+	  at build time.
+	* atheros: Add support for IEEE 802.11w configuration.
+	* AP mode: Allow enable HT20 if driver supports it, by setting the
+	  config parameter ieee80211n.
+	* Allow AP mode to disconnect STAs based on low ACK condition (when
+	  the data connection is not working properly, e.g., due to the STA
+	  going outside the range of the AP). Disabled by default, enable by
+	  config option disassoc_low_ack.
+	* nl80211:
+	  - Support GTK rekey offload.
+	  - Support PMKSA candidate events. This adds support for RSN
+	    pre-authentication with nl80211 interface and drivers that handle
+	    roaming internally.
+	* dbus:
+	  - Add a DBus signal for EAP SM requests, emitted on the Interface
+	    object.
+	  - Export max scan ssids supported by the driver as MaxScanSSID.
+	  - Add signal Certification for information about server certification.
+	  - Add BSSExpireAge and BSSExpireCount interface properties and
+	    support set/get, which allows for setting BSS cache expiration age
+	    and expiration scan count.
+	  - Add ConfigFile to AddInterface properties.
+	  - Add Interface.Country property and support to get/set the value.
+	  - Add DBus property CurrentAuthMode.
+	  - P2P DBus API added.
+	  - Emit property changed events (for property BSSs) when adding/
+	    removing BSSs.
+	  - Treat '' in SSIDs of Interface.Scan as a request for broadcast
+	    scan, instead of ignoring it.
+	  - Add DBus getter/setter for FastReauth.
+	  - Raise PropertiesChanged on org.freedesktop.DBus.Properties.
+	* wpa_cli:
+	  - Send AP-STA-DISCONNECTED event when an AP disconnects a station
+	    due to inactivity.
+	  - Make second argument to set command optional. This can be used to
+	    indicate a zero length value.
+	  - Add signal_poll command.
+	  - Add bss_expire_age and bss_expire_count commands to set/get BSS
+	    cache expiration age and expiration scan count.
+	  - Add ability to set scan interval (the time in seconds wpa_s waits
+	    before requesting a new scan after failing to find a suitable
+	    network in scan results) using scan_interval command.
+	  - Add event CTRL-EVENT-ASSOC-REJECT for association rejected.
+	  - Add command get version, that returns wpa_supplicant version string.
+	  - Add command sta_autoconnect for disabling automatic reconnection
+	    on receiving disconnection event.
+	  - Setting bssid parameter to an empty string "" or any can now be
+	    used to clear the bssid_set flag in a network block, i.e., to remove
+	    bssid filtering.
+	  - Add tdls_testing command to add a special testing feature for
+	    changing TDLS behavior. Build param CONFIG_TDLS_TESTING must be
+	    enabled as well.
+	  - For interworking, add wpa_cli commands interworking_select,
+	    interworking_connect, anqp_get, fetch_anqp, and stop_fetch_anqp.
+	  - Many P2P commands were added. See README-P2P.
+	  - Many WPS/WPS ER commands - see WPS/WPS ER sections for details.
+	  - Allow set command to change global config parameters.
+	  - Add log_level command, which can be used to display the current
+	    debugging level and to change the log level during run time.
+	  - Add note command, which can be used to insert notes to the debug
+	    log.
+	  - Add internal line edit implementation. CONFIG_WPA_CLI_EDIT=y
+	    can now be used to build wpa_cli with internal implementation of
+	    line editing and history support. This can be used as a replacement
+	    for CONFIG_READLINE=y.
+	* AP mode: Add max_num_sta config option, which can be used to limit
+	  the number of stations allowed to connect to the AP.
+	* Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad
+	  config file.
+	* wext: Increase scan timeout from 5 to 10 seconds.
+	* Add blacklist command, allowing an external program to
+	  manage the BSS blacklist and display its current contents.
+	* WPS:
+	  - Add wpa_cli wps_pin get command for generating random PINs. This can
+	    be used in a UI to generate a PIN without starting WPS (or P2P)
+	    operation.
+	  - Set RF bands based on driver capabilities, instead of hardcoding
+	    them.
+	  - Add mechanism for indicating non-standard WPS errors.
+	  - Add CONFIG_WPS_REG_DISABLE_OPEN=y option to disable open networks
+	    by default.
+	  - Add wps_ap_pin cli command for wpa_supplicant AP mode.
+	  - Add wps_check_pin cli command for processing PIN from user input.
+	    UIs can use this command to process a PIN entered by a user and to
+	    validate the checksum digit (if present).
+	  - Cancel WPS operation on PBC session overlap detection.
+	  - New wps_cancel command in wpa_cli will cancel a pending WPS
+	    operation.
+	  - wpa_cli action: Add WPS_EVENT_SUCCESS and WPS_EVENT_FAIL handlers.
+	  - Trigger WPS config update on Manufacturer, Model Name, Model
+	    Number, and Serial Number changes.
+	  - Fragment size is now configurable for EAP-WSC peer. Use
+	    wpa_cli set wps_fragment_size <val>.
+	  - Disable AP PIN after 10 consecutive failures. Slow down attacks on
+	    failures up to 10.
+	  - Allow AP to start in Enrollee mode without AP PIN for probing, to
+	    be compatible with Windows 7.
+	  - Add Config Error into WPS-FAIL events to provide more info to the
+	    user on how to resolve the issue.
+	  - Label and Display config methods are not allowed to be enabled
+	    at the same time, since it is unclear which PIN to use if both
+	    methods are advertised.
+	  - When controlling multiple interfaces:
+	     - apply WPS commands to all interfaces configured to use WPS
+	     - apply WPS config changes to all interfaces that use WPS
+	     - when an attack is detected on any interface, disable AP PIN on
+	       all interfaces
+	* WPS ER:
+	  - Add special AP Setup Locked mode to allow read only ER.
+	    ap_setup_locked=2 can now be used to enable a special mode where
+	    WPS ER can learn the current AP settings, but cannot change them.
+	  - Show SetSelectedRegistrar events as ctrl_iface events
+	  - Add wps_er_set_config to enroll a network based on a local
+	    network configuration block instead of having to (re-)learn the
+	    current AP settings with wps_er_learn.
+	  - Allow AP filtering based on IP address, add ctrl_iface event for
+	    learned AP settings, add wps_er_config command to configure an AP.
+	* WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2)
+	  - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool
+	    for testing protocol extensibility.
+	  - Add build option CONFIG_WPS_STRICT to allow disabling of WPS
+	    workarounds.
+	  - Add support for AuthorizedMACs attribute.
+	* TDLS:
+	  - Propogate TDLS related nl80211 capability flags from kernel and
+	    add them as driver capability flags. If the driver doesn't support
+	    capabilities, assume TDLS is supported internally. When TDLS is
+	    explicitly not supported, disable all user facing TDLS operations.
+	  - Allow TDLS to be disabled at runtime (mostly for testing).
+	    Use set tdls_disabled.
+	  - Honor AP TDLS settings that prohibit/allow TDLS.
+	  - Add a special testing feature for changing TDLS behavior. Use
+	    CONFIG_TDLS_TESTING build param to enable. Configure at runtime
+	    with tdls_testing cli command.
+	  - Add support for TDLS 802.11z.
+	* wlantest: Add a tool wlantest for IEEE802.11 protocol testing.
+	  wlantest can be used to capture frames from a monitor interface
+	  for realtime capturing or from pcap files for offline analysis.
+	* Interworking: Support added for 802.11u. Enable in .config with
+	  CONFIG_INTERWORKING. See wpa_supplicant.conf for config parameters
+	  for interworking. wpa_cli commands added to support this are
+	  interworking_select, interworking_connect, anqp_get, fetch_anqp,
+	  and stop_fetch_anqp.
+	* Android: Add build and runtime support for Android wpa_supplicant.
+	* bgscan learn: Add new bgscan that learns BSS information based on
+	  previous scans, and uses that information to dynamically generate
+	  the list of channels for background scans.
+	* Add a new debug message level for excessive information. Use
+	  -ddd to enable.
+	* TLS: Add support for tls_disable_time_checks=1 in client mode.
+	* Internal TLS:
+	  - Add support for TLS v1.1 (RFC 4346). Enable with build parameter
+	    CONFIG_TLSV11.
+	  - Add domainComponent parser for X.509 names.
+	* Linux: Add RFKill support by adding an interface state "disabled".
+	* Reorder some IEs to get closer to IEEE 802.11 standard. Move
+	  WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames.
+	  Move HT IEs to be later in (Re)Assoc Resp.
+	* Solaris: Add support for wired 802.1X client.
+	* Wi-Fi Direct support. See README-P2P for more information.
+	* Many bugfixes.
+
 2010-04-18 - v0.7.2
 	* nl80211: fixed number of issues with roaming
 	* avoid unnecessary roaming if multiple APs with similar signal
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 03241c5..227fb4f 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -55,6 +55,11 @@
 install: $(addprefix $(DESTDIR)$(BINDIR)/,$(BINALL))
 	$(MAKE) -C ../src install
 
+ifdef CONFIG_FIPS
+CONFIG_NO_RANDOM_POOL=
+CONFIG_OPENSSL_CMAC=y
+endif
+
 OBJS = config.o
 OBJS += notify.o
 OBJS += bss.o
@@ -167,6 +172,11 @@
 NEED_AES_OMAC1=y
 endif
 
+ifdef CONFIG_IEEE80211V
+CFLAGS += -DCONFIG_IEEE80211V
+OBJS += wnm_sta.o
+endif
+
 ifdef CONFIG_TDLS
 CFLAGS += -DCONFIG_TDLS
 OBJS += ../src/rsn_supp/tdls.o
@@ -227,6 +237,17 @@
 endif
 endif
 
+ifdef CONFIG_WIFI_DISPLAY
+CFLAGS += -DCONFIG_WIFI_DISPLAY
+OBJS += wifi_display.o
+endif
+
+ifdef CONFIG_HS20
+OBJS += hs20_supplicant.o
+CFLAGS += -DCONFIG_HS20
+CONFIG_INTERWORKING=y
+endif
+
 ifdef CONFIG_INTERWORKING
 OBJS += interworking.o
 CFLAGS += -DCONFIG_INTERWORKING
@@ -290,6 +311,17 @@
 CONFIG_IEEE8021X_EAPOL=y
 endif
 
+ifdef CONFIG_EAP_UNAUTH_TLS
+# EAP-UNAUTH-TLS
+CFLAGS += -DEAP_UNAUTH_TLS
+ifndef CONFIG_EAP_UNAUTH_TLS
+OBJS += ../src/eap_peer/eap_tls.o
+OBJS_h += ../src/eap_server/eap_server_tls.o
+TLS_FUNCS=y
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
 ifdef CONFIG_EAP_PEAP
 # EAP-PEAP
 ifeq ($(CONFIG_EAP_PEAP), dyn)
@@ -716,6 +748,10 @@
 CFLAGS += -DCONFIG_IEEE80211N
 endif
 
+ifdef CONFIG_WNM
+CFLAGS += -DCONFIG_WNM
+endif
+
 ifdef NEED_AP_MLME
 OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
@@ -728,6 +764,12 @@
 OBJS += ../src/ap/wps_hostapd.o
 OBJS += ../src/eap_server/eap_server_wsc.o
 endif
+ifdef CONFIG_INTERWORKING
+OBJS += ../src/ap/gas_serv.o
+endif
+ifdef CONFIG_HS20
+OBJS += ../src/ap/hs20.o
+endif
 endif
 
 ifdef NEED_RSN_AUTHENTICATOR
@@ -826,8 +868,10 @@
 # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
 OBJS += ../src/eap_peer/eap_tls_common.o
 OBJS_h += ../src/eap_server/eap_server_tls_common.o
+ifndef CONFIG_FIPS
 NEED_TLS_PRF=y
 endif
+endif
 
 ifndef CONFIG_TLS
 CONFIG_TLS=openssl
@@ -855,6 +899,10 @@
 endif
 LIBS += -lcrypto
 LIBS_p += -lcrypto
+ifdef CONFIG_TLS_ADD_DL
+LIBS += -ldl
+LIBS_p += -ldl
+endif
 endif
 
 ifeq ($(CONFIG_TLS), gnutls)
@@ -1034,8 +1082,12 @@
 endif
 ifdef NEED_AES_OMAC1
 NEED_AES_ENC=y
+ifdef CONFIG_OPENSSL_CMAC
+CFLAGS += -DCONFIG_OPENSSL_CMAC
+else
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
+endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
 AESOBJS += ../src/crypto/aes-wrap.o
@@ -1054,7 +1106,10 @@
 endif
 
 ifdef NEED_SHA1
+ifneq ($(CONFIG_TLS), openssl)
 SHA1OBJS += ../src/crypto/sha1.o
+endif
+SHA1OBJS += ../src/crypto/sha1-prf.o
 ifdef CONFIG_INTERNAL_SHA1
 SHA1OBJS += ../src/crypto/sha1-internal.o
 ifdef NEED_FIPS186_2_PRF
@@ -1064,8 +1119,10 @@
 ifdef CONFIG_NO_WPA_PASSPHRASE
 CFLAGS += -DCONFIG_NO_PBKDF2
 else
+ifneq ($(CONFIG_TLS), openssl)
 SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
 endif
+endif
 ifdef NEED_T_PRF
 SHA1OBJS += ../src/crypto/sha1-tprf.o
 endif
@@ -1074,14 +1131,13 @@
 endif
 endif
 
-MD5OBJS = ../src/crypto/md5.o
+ifndef CONFIG_FIPS
+MD5OBJS += ../src/crypto/md5.o
+endif
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 MD5OBJS += ../src/crypto/md5-internal.o
 endif
-ifdef CONFIG_FIPS
-MD5OBJS += ../src/crypto/md5-non-fips.o
-endif
 OBJS += $(MD5OBJS)
 OBJS_p += $(MD5OBJS)
 endif
@@ -1108,7 +1164,10 @@
 SHA256OBJS = # none by default
 ifdef NEED_SHA256
 CFLAGS += -DCONFIG_SHA256
+ifneq ($(CONFIG_TLS), openssl)
 SHA256OBJS += ../src/crypto/sha256.o
+endif
+SHA256OBJS += ../src/crypto/sha256-prf.o
 ifdef CONFIG_INTERNAL_SHA256
 SHA256OBJS += ../src/crypto/sha256-internal.o
 endif
@@ -1154,6 +1213,11 @@
 ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
 CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
 endif
+ifeq ($(CONFIG_CTRL_IFACE), udp-remote)
+CONFIG_CTRL_IFACE=udp
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
+endif
 OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o
 endif
 
@@ -1270,6 +1334,10 @@
 endif
 endif
 
+ifdef CONFIG_DEBUG_LINUX_TRACING
+CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
+endif
+
 ifdef CONFIG_DEBUG_FILE
 CFLAGS += -DCONFIG_DEBUG_FILE
 endif
@@ -1280,6 +1348,9 @@
 
 ifdef CONFIG_FIPS
 CFLAGS += -DCONFIG_FIPS
+ifneq ($(CONFIG_TLS), openssl)
+$(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
+endif
 endif
 
 OBJS += $(SHA1OBJS) $(DESOBJS)
@@ -1304,6 +1375,34 @@
 OBJS += bgscan.o
 endif
 
+ifdef CONFIG_AUTOSCAN_EXPONENTIAL
+CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL
+OBJS += autoscan_exponential.o
+NEED_AUTOSCAN=y
+endif
+
+ifdef CONFIG_AUTOSCAN_PERIODIC
+CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
+OBJS += autoscan_periodic.o
+NEED_AUTOSCAN=y
+endif
+
+ifdef NEED_AUTOSCAN
+CFLAGS += -DCONFIG_AUTOSCAN
+OBJS += autoscan.o
+endif
+
+ifdef CONFIG_EXT_PASSWORD_TEST
+OBJS += ../src/utils/ext_password_test.o
+CFLAGS += -DCONFIG_EXT_PASSWORD_TEST
+NEED_EXT_PASSWORD=y
+endif
+
+ifdef NEED_EXT_PASSWORD
+OBJS += ../src/utils/ext_password.o
+CFLAGS += -DCONFIG_EXT_PASSWORD
+endif
+
 ifdef NEED_GAS
 OBJS += ../src/common/gas.o
 OBJS += gas_query.o
@@ -1332,6 +1431,10 @@
 OBJS_t += ../src/utils/ip_addr.o
 endif
 OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o
+
+OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o
+OBJS_nfc += $(OBJS_d) ../src/drivers/drivers.o
+
 OBJS += $(CONFIG_MAIN).o
 
 ifdef CONFIG_PRIVSEP
@@ -1440,6 +1543,10 @@
 	$(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS)
 	@$(E) "  LD " $@
 
+nfc_pw_token: $(OBJS_nfc)
+	$(Q)$(LDO) $(LDFLAGS) -o nfc_pw_token $(OBJS_nfc) $(LIBS)
+	@$(E) "  LD " $@
+
 win_if_list: win_if_list.c
 	$(Q)$(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w)
 	@$(E) "  LD " $@
@@ -1517,10 +1624,16 @@
 
 tests: test-eap_sim_common
 
+FIPSDIR=/usr/local/ssl/fips-2.0
+FIPSLD=$(FIPSDIR)/bin/fipsld
+fips:
+	$(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)"
+
 clean:
 	$(MAKE) -C ../src clean
 	$(MAKE) -C dbus clean
 	rm -f core *~ *.o *.d eap_*.so $(ALL) $(WINALL) eapol_test preauth_test
 	rm -f wpa_priv
+	rm -f nfc_pw_token
 
 -include $(OBJS:%.o=%.d)
diff --git a/wpa_supplicant/README b/wpa_supplicant/README
index 9ee82a2..a06e5c1 100644
--- a/wpa_supplicant/README
+++ b/wpa_supplicant/README
@@ -425,7 +425,7 @@
   -K = include keys (passwords, etc.) in debug output
   -t = include timestamp in debug messages
   -h = show this help text
-  -L = show license (GPL and BSD)
+  -L = show license (BSD)
   -p = driver parameters
   -P = PID file
   -q = decrease debugging verbosity (-qq even less)
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
new file mode 100644
index 0000000..feb9049
--- /dev/null
+++ b/wpa_supplicant/README-HS20
@@ -0,0 +1,470 @@
+wpa_supplicant and Hotspot 2.0
+==============================
+
+This document describe how the IEEE 802.11u Interworking and Wi-Fi
+Hotspot 2.0 (Release 1) implementation in wpa_supplicant can be
+configured and how an external component on the client e.g., management
+GUI or Wi-Fi framework) is used to manage this functionality.
+
+
+Introduction to Wi-Fi Hotspot 2.0
+---------------------------------
+
+Hotspot 2.0 is the name of the Wi-Fi Alliance specification that is used
+in the Wi-Fi CERTIFIED Passpoint<TM> program. More information about
+this is available in this white paper:
+
+http://www.wi-fi.org/knowledge-center/white-papers/wi-fi-certified-passpoint%E2%84%A2-new-program-wi-fi-alliance%C2%AE-enable-seamless
+
+The Hotspot 2.0 specification is also available from WFA:
+https://www.wi-fi.org/knowledge-center/published-specifications
+
+The core Interworking functionality (network selection, GAS/ANQP) were
+standardized in IEEE Std 802.11u-2011 which is now part of the IEEE Std
+802.11-2012.
+
+
+wpa_supplicant network selection
+--------------------------------
+
+Interworking support added option for configuring credentials that can
+work with multiple networks as an alternative to configuration of
+network blocks (e.g., per-SSID parameters). When requested to perform
+network selection, wpa_supplicant picks the highest priority enabled
+network block or credential. If a credential is picked (based on ANQP
+information from APs), a temporary network block is created
+automatically for the matching network. This temporary network block is
+used similarly to the network blocks that can be configured by the user,
+but it is not stored into the configuration file and is meant to be used
+only for temporary period of time since a new one can be created
+whenever needed based on ANQP information and the credential.
+
+By default, wpa_supplicant is not using automatic network selection
+unless requested explicitly with the interworking_select command. This
+can be changed with the auto_interworking=1 parameter to perform network
+selection automatically whenever trying to find a network for connection
+and none of the enabled network blocks match with the scan results. This
+case works similarly to "interworking_select auto", i.e., wpa_supplicant
+will internally determine which network or credential is going to be
+used based on configured priorities, scan results, and ANQP information.
+
+
+wpa_supplicant configuration
+----------------------------
+
+Interworking and Hotspot 2.0 functionality are optional components that
+need to be enabled in the wpa_supplicant build configuration
+(.config). This is done by adding following parameters into that file:
+
+CONFIG_INTERWORKING=y
+CONFIG_HS20=y
+
+It should be noted that this functionality requires a driver that
+supports GAS/ANQP operations. This uses the same design as P2P, i.e.,
+Action frame processing and building in user space within
+wpa_supplicant. The Linux nl80211 driver interface provides the needed
+functionality for this.
+
+
+There are number of run-time configuration parameters (e.g., in
+wpa_supplicant.conf when using the configuration file) that can be used
+to control Hotspot 2.0 operations.
+
+# Enable Interworking
+interworking=1
+
+# Enable Hotspot 2.0
+hs20=1
+
+# Parameters for controlling scanning
+
+# Homogenous ESS identifier
+# If this is set, scans will be used to request response only from BSSes
+# belonging to the specified Homogeneous ESS. This is used only if interworking
+# is enabled.
+#hessid=00:11:22:33:44:55
+
+# Access Network Type
+# When Interworking is enabled, scans can be limited to APs that advertise the
+# specified Access Network Type (0..15; with 15 indicating wildcard match).
+# This value controls the Access Network Type value in Probe Request frames.
+#access_network_type=15
+
+# Automatic network selection behavior
+# 0 = do not automatically go through Interworking network selection
+#     (i.e., require explicit interworking_select command for this; default)
+# 1 = perform Interworking network selection if one or more
+#     credentials have been configured and scan did not find a
+#     matching network block
+#auto_interworking=0
+
+
+Credentials can be pre-configured for automatic network selection:
+
+# credential block
+#
+# Each credential used for automatic network selection is configured as a set
+# of parameters that are compared to the information advertised by the APs when
+# interworking_select and interworking_connect commands are used.
+#
+# credential fields:
+#
+# priority: Priority group
+#	By default, all networks and credentials get the same priority group
+#	(0). This field can be used to give higher priority for credentials
+#	(and similarly in struct wpa_ssid for network blocks) to change the
+#	Interworking automatic networking selection behavior. The matching
+#	network (based on either an enabled network block or a credential)
+#	with the highest priority value will be selected.
+#
+# pcsc: Use PC/SC and SIM/USIM card
+#
+# realm: Home Realm for Interworking
+#
+# username: Username for Interworking network selection
+#
+# password: Password for Interworking network selection
+#
+# ca_cert: CA certificate for Interworking network selection
+#
+# client_cert: File path to client certificate file (PEM/DER)
+#	This field is used with Interworking networking selection for a case
+#	where client certificate/private key is used for authentication
+#	(EAP-TLS). Full path to the file should be used since working
+#	directory may change when wpa_supplicant is run in the background.
+#
+#	Alternatively, a named configuration blob can be used by setting
+#	this to blob://blob_name.
+#
+# private_key: File path to client private key file (PEM/DER/PFX)
+#	When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+#	commented out. Both the private key and certificate will be read
+#	from the PKCS#12 file in this case. Full path to the file should be
+#	used since working directory may change when wpa_supplicant is run
+#	in the background.
+#
+#	Windows certificate store can be used by leaving client_cert out and
+#	configuring private_key in one of the following formats:
+#
+#	cert://substring_to_match
+#
+#	hash://certificate_thumbprint_in_hex
+#
+#	For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+#
+#	Note that when running wpa_supplicant as an application, the user
+#	certificate store (My user account) is used, whereas computer store
+#	(Computer account) is used when running wpasvc as a service.
+#
+#	Alternatively, a named configuration blob can be used by setting
+#	this to blob://blob_name.
+#
+# private_key_passwd: Password for private key file
+#
+# imsi: IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+#
+# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
+#	format
+#
+# domain: Home service provider FQDN
+#	This is used to compare against the Domain Name List to figure out
+#	whether the AP is operated by the Home SP.
+#
+# roaming_consortium: Roaming Consortium OI
+#	If roaming_consortium_len is non-zero, this field contains the
+#	Roaming Consortium OI that can be used to determine which access
+#	points support authentication with this credential. This is an
+#	alternative to the use of the realm parameter. When using Roaming
+#	Consortium to match the network, the EAP parameters need to be
+#	pre-configured with the credential since the NAI Realm information
+#	may not be available or fetched.
+#
+# eap: Pre-configured EAP method
+#	This optional field can be used to specify which EAP method will be
+#	used with this credential. If not set, the EAP method is selected
+#	automatically based on ANQP information (e.g., NAI Realm).
+#
+# phase1: Pre-configure Phase 1 (outer authentication) parameters
+#	This optional field is used with like the 'eap' parameter.
+#
+# phase2: Pre-configure Phase 2 (inner authentication) parameters
+#	This optional field is used with like the 'eap' parameter.
+#
+# for example:
+#
+#cred={
+#	realm="example.com"
+#	username="user@example.com"
+#	password="password"
+#	ca_cert="/etc/wpa_supplicant/ca.pem"
+#	domain="example.com"
+#}
+#
+#cred={
+#	imsi="310026-000000000"
+#	milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82"
+#}
+#
+#cred={
+#	realm="example.com"
+#	username="user"
+#	password="password"
+#	ca_cert="/etc/wpa_supplicant/ca.pem"
+#	domain="example.com"
+#	roaming_consortium=223344
+#	eap=TTLS
+#	phase2="auth=MSCHAPV2"
+#}
+
+
+Control interface
+-----------------
+
+wpa_supplicant provides a control interface that can be used from
+external programs to manage various operations. The included command
+line tool, wpa_cli, can be used for manual testing with this interface.
+
+Following wpa_cli interactive mode commands show some examples of manual
+operations related to Hotspot 2.0:
+
+Remove configured networks and credentials:
+
+> remove_network all
+OK
+> remove_cred all
+OK
+
+
+Add a username/password credential:
+
+> add_cred
+0
+> set_cred 0 realm "mail.example.com"
+OK
+> set_cred 0 username "username"
+OK
+> set_cred 0 password "password"
+OK
+> set_cred 0 priority 1
+OK
+
+Add a SIM credential using a simulated SIM/USIM card for testing:
+
+> add_cred
+1
+> set_cred 1 imsi "23456-0000000000"
+OK
+> set_cred 1 milenage "90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123"
+OK
+> set_cred 1 priority 1
+OK
+
+Note: the return value of add_cred is used as the first argument to
+the following set_cred commands.
+
+
+Add a WPA2-Enterprise network:
+
+> add_network
+0
+> set_network 0 key_mgmt WPA-EAP
+OK
+> set_network 0 ssid "enterprise"
+OK
+> set_network 0 eap TTLS
+OK
+> set_network 0 anonymous_identity "anonymous"
+OK
+> set_network 0 identity "user"
+OK
+> set_network 0 password "password"
+OK
+> set_network 0 priority 0
+OK
+> enable_network 0 no-connect
+OK
+
+
+Add an open network:
+
+> add_network
+3
+> set_network 3 key_mgmt NONE
+OK
+> set_network 3 ssid "coffee-shop"
+OK
+> select_network 3
+OK
+
+Note: the return value of add_network is used as the first argument to
+the following set_network commands.
+
+The preferred credentials/networks can be indicated with the priority
+parameter (1 is higher priority than 0).
+
+
+Interworking network selection can be started with interworking_select
+command. This instructs wpa_supplicant to run a network scan and iterate
+through the discovered APs to request ANQP information from the APs that
+advertise support for Interworking/Hotspot 2.0:
+
+> interworking_select
+OK
+<3>Starting ANQP fetch for 02:00:00:00:01:00
+<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+<3>ANQP fetch completed
+<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown
+
+
+INTERWORKING-AP event messages indicate the APs that support network
+selection and for which there is a matching
+credential. interworking_connect command can be used to select a network
+to connect with:
+
+
+> interworking_connect 02:00:00:00:01:00
+OK
+<3>CTRL-EVENT-SCAN-RESULTS
+<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Associated with 02:00:00:00:01:00
+<3>CTRL-EVENT-EAP-STARTED EAP authentication started
+<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21
+<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected
+<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully
+<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP]
+<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (auth) [id=0 id_str=]
+
+
+wpa_supplicant creates a temporary network block for the selected
+network based on the configured credential and ANQP information from the
+AP:
+
+> list_networks
+network id / ssid / bssid / flags
+0	Example Network	any	[CURRENT]
+> get_network 0 key_mgmt
+WPA-EAP
+> get_network 0 eap
+TTLS
+
+
+Alternatively to using an external program to select the network,
+"interworking_select auto" command can be used to request wpa_supplicant
+to select which network to use based on configured priorities:
+
+
+> remove_network all
+OK
+<3>CTRL-EVENT-DISCONNECTED bssid=02:00:00:00:01:00 reason=1 locally_generated=1
+> interworking_select auto
+OK
+<3>Starting ANQP fetch for 02:00:00:00:01:00
+<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+<3>ANQP fetch completed
+<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown
+<3>CTRL-EVENT-SCAN-RESULTS
+<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Associated with 02:00:00:00:01:00
+<3>CTRL-EVENT-EAP-STARTED EAP authentication started
+<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21
+<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected
+<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully
+<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP]
+<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (reauth) [id=0 id_str=]
+
+
+The connection status can be shown with the status command:
+
+> status
+bssid=02:00:00:00:01:00
+ssid=Example Network
+id=0
+mode=station
+pairwise_cipher=CCMP       <--- link layer security indication
+group_cipher=CCMP
+key_mgmt=WPA2/IEEE 802.1X/EAP
+wpa_state=COMPLETED
+p2p_device_address=02:00:00:00:00:00
+address=02:00:00:00:00:00
+hs20=1      <--- HS 2.0 indication
+Supplicant PAE state=AUTHENTICATED
+suppPortStatus=Authorized
+EAP state=SUCCESS
+selectedMethod=21 (EAP-TTLS)
+EAP TLS cipher=AES-128-SHA
+EAP-TTLSv0 Phase2 method=PAP
+
+
+> status
+bssid=02:00:00:00:02:00
+ssid=coffee-shop
+id=3
+mode=station
+pairwise_cipher=NONE
+group_cipher=NONE
+key_mgmt=NONE
+wpa_state=COMPLETED
+p2p_device_address=02:00:00:00:00:00
+address=02:00:00:00:00:00
+
+
+Note: The Hotspot 2.0 indication is shown as "hs20=1" in the status
+command output. Link layer security is indicated with the
+pairwise_cipher (CCMP = secure, NONE = no encryption used).
+
+
+Also the scan results include the Hotspot 2.0 indication:
+
+> scan_results
+bssid / frequency / signal level / flags / ssid
+02:00:00:00:01:00	2412	-30	[WPA2-EAP-CCMP][ESS][HS20]	Example Network
+
+
+ANQP information for the BSS can be fetched using the BSS command:
+
+> bss 02:00:00:00:01:00
+id=1
+bssid=02:00:00:00:01:00
+freq=2412
+beacon_int=100
+capabilities=0x0411
+qual=0
+noise=-92
+level=-30
+tsf=1345573286517276
+age=105
+ie=000f4578616d706c65204e6574776f726b010882848b960c1218240301012a010432043048606c30140100000fac040100000fac040100000fac0100007f04000000806b091e07010203040506076c027f006f1001531122331020304050010203040506dd05506f9a1000
+flags=[WPA2-EAP-CCMP][ESS][HS20]
+ssid=Example Network
+anqp_roaming_consortium=031122330510203040500601020304050603fedcba
+
+
+ANQP queries can also be requested with the anqp_get and hs20_anqp_get
+commands:
+
+> anqp_get 02:00:00:00:01:00 261
+OK
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+> hs20_anqp_get 02:00:00:00:01:00 2
+OK
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+
+In addition, fetch_anqp command can be used to request similar set of
+ANQP queries to be done as is run as part of interworking_select:
+
+> scan
+OK
+<3>CTRL-EVENT-SCAN-RESULTS
+> fetch_anqp
+OK
+<3>Starting ANQP fetch for 02:00:00:00:01:00
+<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+<3>ANQP fetch completed
diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P
index db6e4ae..4abc2f2 100644
--- a/wpa_supplicant/README-P2P
+++ b/wpa_supplicant/README-P2P
@@ -71,7 +71,8 @@
 
 Device Discovery
 
-p2p_find [timeout in seconds] [type=<social|progressive>]
+p2p_find [timeout in seconds] [type=<social|progressive>] \
+	[dev_id=<addr>] [delay=<search delay in ms>]
 
 The default behavior is to run a single full scan in the beginning and
 then scan only social channels. type=social will scan only social
@@ -81,6 +82,11 @@
 will help in finding new groups or groups missed during the initial
 full scan.
 
+The optional dev_id option can be used to specify a single P2P peer to
+search for. The optional delay parameter can be used to request an extra
+delay to be used between search iterations (e.g., to free up radio
+resources for concurrent operations).
+
 p2p_listen [timeout in seconds]
 
 Start Listen-only state (become discoverable without searching for
@@ -101,7 +107,7 @@
 
 Group Formation
 
-p2p_prov_disc <peer device address> <display|keypad|pbc> [join]
+p2p_prov_disc <peer device address> <display|keypad|pbc> [join|auto]
 
 Send P2P provision discovery request to the specified peer. The
 parameters for this command are the P2P device address of the peer and
@@ -112,10 +118,14 @@
 
 The optional "join" parameter can be used to indicate that this command
 is requesting an already running GO to prepare for a new client. This is
-mainly used with "display" to request it to display a PIN.
+mainly used with "display" to request it to display a PIN. The "auto"
+parameter can be used to request wpa_supplicant to automatically figure
+out whether the peer device is operating as a GO and if so, use
+join-a-group style PD instead of GO Negotiation style PD.
 
 p2p_connect <peer device address> <pbc|pin|PIN#> [display|keypad]
-	[persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>]
+	[persistent|persistent=<network id>] [join|auth]
+	[go_intent=<0..15>] [freq=<in MHz>] [ht40] [provdisc]
 
 Start P2P group formation with a discovered P2P peer. This includes
 optional group owner negotiation, group interface setup, provisioning,
@@ -128,7 +138,12 @@
 used (e.g., 12345670). [display|keypad] is used with PIN method
 to specify which PIN is used (display=dynamically generated random PIN
 from local display, keypad=PIN entered from peer display). "persistent"
-parameter can be used to request a persistent group to be formed.
+parameter can be used to request a persistent group to be formed. The
+"persistent=<network id>" alternative can be used to pre-populate
+SSID/passphrase configuration based on a previously used persistent
+group where this device was the GO. The previously used parameters will
+then be used if the local end becomes the GO in GO Negotiation (which
+can be forced with go_intent=15).
 
 "join" indicates that this is a command to join an existing group as a
 client. It skips the GO Negotiation part. This will send a Provision
@@ -146,7 +161,12 @@
 "freq" can be used to set a forced operating channel (e.g., freq=2412
 to select 2.4 GHz channel 1).
 
-p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>]
+"provdisc" can be used to request a Provision Discovery exchange to be
+used prior to starting GO Negotiation as a workaround with some deployed
+P2P implementations that require this to allow the user to accept the
+connection.
+
+p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>] [ht40]
 
 Set up a P2P group owner manually (i.e., without group owner
 negotiation with a specific peer). This is also known as autonomous
@@ -236,6 +256,14 @@
 p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
 p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1
 
+# Wi-Fi Display examples
+# format: wifi-display <list of roles> <list of subelements>
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source] 2,3,4,5
+p2p_serv_disc_req 02:01:02:03:04:05 wifi-display [pri-sink] 3
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [sec-source] 2
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source+sink] 2,3,4,5
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source][pri-sink] 2,3,4,5
+
 p2p_serv_disc_cancel_req <query identifier>
 
 Cancel a pending P2P service discovery request. This command takes a
@@ -318,15 +346,16 @@
 Invitation
 
 p2p_invite [persistent=<network id>|group=<group ifname>] [peer=address]
-	[go_dev_addr=address]
+	[go_dev_addr=address] [freq=<freq in MHz>] [ht40]
 
 Invite a peer to join a group (e.g., group=wlan1) or to reinvoke a
 persistent group (e.g., persistent=4). If the peer device is the GO of
-the persisten group, the peer parameter is not needed. Otherwise it is
+the persistent group, the peer parameter is not needed. Otherwise it is
 used to specify which device to invite. go_dev_addr parameter can be
 used to override the GO device address for Invitation Request should
 it be not known for some reason (this should not be needed in most
-cases).
+cases). When reinvoking a persistent group, the GO device can specify
+the frequency for the group with the freq parameter.
 
 Group Operations
 
diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS
index bf75cb4..692d5f5 100644
--- a/wpa_supplicant/README-WPS
+++ b/wpa_supplicant/README-WPS
@@ -67,6 +67,10 @@
 
 CONFIG_WPS_ER=y
 
+Following parameter can be used to enable support for NFC config method:
+
+CONFIG_WPS_NFC=y
+
 
 WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for
 the device. This is configured in the runtime configuration for
@@ -303,3 +307,41 @@
 - WPS ER learned AP settings
 
 WPS-ER-AP-SETTINGS uuid=fd91b4ec-e3fa-5891-a57d-8c59efeed1d2 ssid=test-wps auth_type=0x0020 encr_type=0x0008 key=12345678
+
+
+WPS with NFC
+------------
+
+WPS can be used with NFC-based configuration method. An NFC tag
+containing a password token from the Enrollee can be used to
+authenticate the connection instead of the PIN. In addition, an NFC tag
+with a configuration token can be used to transfer AP settings without
+going through the WPS protocol.
+
+When the station acts as an Enrollee, a local NFC tag with a password
+token can be used by touching the NFC interface of a Registrar.
+
+"wps_nfc [BSSID]" command starts WPS protocol run with the local end as
+the Enrollee using the NFC password token that is either pre-configured
+in the configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey,
+wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with
+"wps_nfc_token <WPS|NDEF>" command. The included nfc_pw_token tool
+(build with "make nfc_pw_token") can be used to generate NFC password
+tokens during manufacturing (each station needs to have its own random
+keys).
+
+If the station includes NFC interface and reads an NFC tag with a MIME
+media type "application/vnd.wfa.wsc", the NDEF message payload (with or
+without NDEF encapsulation) can be delivered to wpa_supplicant using the
+following wpa_cli command:
+
+wps_nfc_tag_read <hexdump of payload>
+
+If the NFC tag contains a configuration token, the network is added to
+wpa_supplicant configuration. If the NFC tag contains a password token,
+the token is added to the WPS Registrar component. This information can
+then be used with wps_reg command (when the NFC password token was from
+an AP) using a special value "nfc-pw" in place of the PIN parameter. If
+the ER functionality has been started (wps_er_start), the NFC password
+token is used to enable enrollment of a new station (that was the source
+of the NFC password token).
diff --git a/wpa_supplicant/README-Windows.txt b/wpa_supplicant/README-Windows.txt
index 292223d..7288abd 100644
--- a/wpa_supplicant/README-Windows.txt
+++ b/wpa_supplicant/README-Windows.txt
@@ -4,13 +4,8 @@
 Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
-This program is dual-licensed under both the GPL version 2 and BSD
-license. Either license may be used at your option.
-
-This product includes software developed by the OpenSSL Project
-for use in the OpenSSL Toolkit (http://www.openssl.org/). This
-product includes cryptographic software written by Eric Young
-(eay@cryptsoft.com).
+This program is licensed under the BSD license (the one with
+advertisement clause removed).
 
 
 wpa_supplicant has support for being used as a WPA/WPA2/IEEE 802.1X
@@ -35,20 +30,6 @@
 - WPA2-EAP, TKIP, CCMP, TKIP+CCMP
 
 
-Binary version
---------------
-
-Compiled binary version of the wpa_supplicant and additional tools is
-available from http://w1.fi/wpa_supplicant/. These binaries can be
-used after installing WinPcap.
-
-wpa_gui uses Qt 4 framework and may need additional dynamic libraries
-(DLLs). These libraries are available from
-http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip
-You can copy the DLL files from this ZIP package into the same directory
-with wpa_gui.exe to allow wpa_gui to be started.
-
-
 Building wpa_supplicant with mingw
 ----------------------------------
 
@@ -316,135 +297,3 @@
 See win_example.reg for an example on how to setup wpasvc.exe
 parameters in registry. It can also be imported to registry as a
 starting point for the configuration.
-
-
-
-License information for third party software used in this product:
-
-  OpenSSL License
-  ---------------
-
-/* ====================================================================
- * Copyright (c) 1998-2004 The OpenSSL Project.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer. 
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- *    software must display the following acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- *    endorse or promote products derived from this software without
- *    prior written permission. For written permission, please contact
- *    openssl-core@openssl.org.
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- *    nor may "OpenSSL" appear in their names without prior written
- *    permission of the OpenSSL Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- *    acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
- *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED 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 OpenSSL PROJECT OR
- * ITS 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.
- * ====================================================================
- *
- * This product includes cryptographic software written by Eric Young
- * (eay@cryptsoft.com).  This product includes software written by Tim
- * Hudson (tjh@cryptsoft.com).
- *
- */
-
- Original SSLeay License
- -----------------------
-
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- * 
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.  The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- * 
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- * 
- * 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 copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *    "This product includes cryptographic software written by
- *     Eric Young (eay@cryptsoft.com)"
- *    The word 'cryptographic' can be left out if the rouines from the library
- *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from 
- *    the apps directory (application code) you must include an acknowledgement:
- *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- * 
- * THIS SOFTWARE IS PROVIDED BY ERIC 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 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.
- * 
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed.  i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.]
- */
-
-
-
-   Qt Open Source Edition
-   ----------------------
-
-The Qt GUI Toolkit is Copyright (C) 1994-2007 Trolltech ASA.
-Qt Open Source Edition is licensed under GPL version 2.
-
-Source code for the library is available at
-http://w1.fi/wpa_supplicant/qt4/qt-win-opensource-src-4.3.3.zip
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 5aa4fcc..58d0c43 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -55,11 +55,6 @@
 # Set include directory to the madwifi source tree
 #CFLAGS += -I../../madwifi
 
-# Driver interface for Prism54 driver
-# (Note: Prism54 is not yet supported, i.e., this will not work as-is and is
-# for developers only)
-#CONFIG_DRIVER_PRISM54=y
-
 # Driver interface for ndiswrapper
 # Deprecated; use CONFIG_DRIVER_WEXT=y instead.
 #CONFIG_DRIVER_NDISWRAPPER=y
@@ -83,6 +78,11 @@
 #CONFIG_DRIVER_RALINK=y
 
 # Driver interface for generic Linux wireless extensions
+# Note: WEXT is deprecated in the current Linux kernel version and no new
+# functionality is added to it. nl80211-based interface is the new
+# replacement for WEXT and its use allows wpa_supplicant to properly control
+# the driver to improve existing functionality like roaming and to support new
+# functionality.
 #CONFIG_DRIVER_WEXT=y
 
 # Driver interface for Linux drivers using the nl80211 kernel interface
@@ -93,6 +93,8 @@
 #CONFIG_DRIVER_BSD=y
 #CFLAGS += -I/usr/local/include
 #LIBS += -L/usr/local/lib
+#LIBS_p += -L/usr/local/lib
+#LIBS_c += -L/usr/local/lib
 
 # Driver interface for Windows NDIS
 #CONFIG_DRIVER_NDIS=y
@@ -113,11 +115,6 @@
 # Driver interface for development testing
 #CONFIG_DRIVER_TEST=y
 
-# Include client MLME (management frame processing) for test driver
-# This can be used to test MLME operations in hostapd with the test interface.
-# space.
-#CONFIG_CLIENT_MLME=y
-
 # Driver interface for wired Ethernet drivers
 #CONFIG_DRIVER_WIRED=y
 
@@ -127,6 +124,10 @@
 # Driver interface for no driver (e.g., WPS ER only)
 #CONFIG_DRIVER_NONE=y
 
+# Solaris libraries
+#LIBS += -lsocket -ldlpi -lnsl
+#LIBS_c += -lsocket
+
 # Enable IEEE 802.1X Supplicant (automatically included if any EAP method is
 # included)
 CONFIG_IEEE8021X_EAPOL=y
@@ -165,6 +166,9 @@
 # EAP-PSK (experimental; this is _not_ needed for WPA-PSK)
 #CONFIG_EAP_PSK=y
 
+# EAP-pwd (secure authentication using only a password)
+CONFIG_EAP_PWD=y
+
 # EAP-PAX
 #CONFIG_EAP_PAX=y
 
@@ -196,13 +200,15 @@
 CONFIG_WPS=y
 # Enable WSC 2.0 support
 CONFIG_WPS2=y
+# Enable WPS external registrar functionality
+#CONFIG_WPS_ER=y
+# Disable credentials for an open network by default when acting as a WPS
+# registrar.
+#CONFIG_WPS_REG_DISABLE_OPEN=y
 
 # EAP-IKEv2
 #CONFIG_EAP_IKEV2=y
 
-# EAP-PWD
-CONFIG_EAP_PWD=y
-
 # PKCS#12 (PFX) support (used to read private key and certificate file from
 # a file that usually has extension .p12 or .pfx)
 CONFIG_PKCS12=y
@@ -215,6 +221,9 @@
 # Enable this if EAP-SIM or EAP-AKA is included
 #CONFIG_PCSC=y
 
+# Support HT overrides (disable HT/HT40, mask MCS rates, etc.)
+#CONFIG_HT_OVERRIDES=y
+
 # Development testing
 #CONFIG_EAPOL_TEST=y
 
@@ -233,6 +242,10 @@
 # the resulting binary.
 #CONFIG_READLINE=y
 
+# Include internal line edit mode in wpa_cli. This can be used as a replacement
+# for GNU Readline to provide limited command line editing and history support.
+CONFIG_WPA_CLI_EDIT=y
+
 # Remove debugging code that is printing out debug message to stdout.
 # This can be used to reduce the size of the wpa_supplicant considerably
 # if debugging code is not needed. The size reduction can be around 35%
@@ -294,6 +307,9 @@
 # eloop_none = Empty template
 CONFIG_ELOOP=eloop
 
+# Should we use poll instead of select? Select is used by default.
+#CONFIG_ELOOP_POLL=y
+
 # Select layer 2 packet implementation
 # linux = Linux packet socket (default)
 # pcap = libpcap/libdnet/WinPcap
@@ -314,18 +330,24 @@
 
 # Select TLS implementation
 # openssl = OpenSSL (default)
-# gnutls = GnuTLS (needed for TLS/IA, see also CONFIG_GNUTLS_EXTRA)
+# gnutls = GnuTLS
 # internal = Internal TLSv1 implementation (experimental)
 # none = Empty template
 #CONFIG_TLS=openssl
 
-# Whether to enable TLS/IA support, which is required for EAP-TTLSv1.
-# You need CONFIG_TLS=gnutls for this to have any effect. Please note that
-# even though the core GnuTLS library is released under LGPL, this extra
-# library uses GPL and as such, the terms of GPL apply to the combination
-# of wpa_supplicant and GnuTLS if this option is enabled. BSD license may not
-# apply for distribution of the resulting binary.
-#CONFIG_GNUTLS_EXTRA=y
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
+# can be enabled to get a stronger construction of messages when block ciphers
+# are used. It should be noted that some existing TLS v1.0 -based
+# implementation may not be compatible with TLS v1.1 message (ClientHello is
+# sent prior to negotiating which version will be used)
+#CONFIG_TLSV11=y
+
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2)
+# can be enabled to enable use of stronger crypto algorithms. It should be
+# noted that some existing TLS v1.0 -based implementation may not be compatible
+# with TLS v1.2 message (ClientHello is sent prior to negotiating which version
+# will be used)
+#CONFIG_TLSV12=y
 
 # If CONFIG_TLS=internal is used, additional library and include paths are
 # needed for LibTomMath. Alternatively, an integrated, minimal version of
@@ -357,7 +379,7 @@
 # (fi.w1.hostap.wpa_supplicant1)
 #CONFIG_CTRL_IFACE_DBUS_NEW=y
 
-# Add introspection support for new DBus control interface (requires libxml2)
+# Add introspection support for new DBus control interface
 #CONFIG_CTRL_IFACE_DBUS_INTRO=y
 
 # Add support for loading EAP methods dynamically as shared libraries.
@@ -387,7 +409,19 @@
 # Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
 #CONFIG_DEBUG_FILE=y
 
-# Add support for writing debug log to Android logcat instead of standard output
+# Send debug messages to syslog instead of stdout
+#CONFIG_DEBUG_SYSLOG=y
+# Set syslog facility for debug messages
+#CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON
+
+# Add support for sending all debug messages (regardless of debug verbosity)
+# to the Linux kernel tracing facility. This helps debug the entire stack by
+# making it easy to record everything happening from the driver up into the
+# same file, e.g., using trace-cmd.
+#CONFIG_DEBUG_LINUX_TRACING=y
+
+# Add support for writing debug log to Android logcat instead of standard
+# output
 CONFIG_ANDROID_LOG=y
 
 # Enable privilege separation (see README 'Privilege separation' for details)
@@ -397,11 +431,73 @@
 # MIC error reports by a random amount of time between 0 and 60 seconds
 #CONFIG_DELAYED_MIC_ERROR_REPORT=y
 
+# Enable tracing code for developer debugging
+# This tracks use of memory allocations and other registrations and reports
+# incorrect use with a backtrace of call (or allocation) location.
+#CONFIG_WPA_TRACE=y
+# For BSD, uncomment these.
+#LIBS += -lexecinfo
+#LIBS_p += -lexecinfo
+#LIBS_c += -lexecinfo
+
+# Use libbfd to get more details for developer debugging
+# This enables use of libbfd to get more detailed symbols for the backtraces
+# generated by CONFIG_WPA_TRACE=y.
+#CONFIG_WPA_TRACE_BFD=y
+# For BSD, uncomment these.
+#LIBS += -lbfd -liberty -lz
+#LIBS_p += -lbfd -liberty -lz
+#LIBS_c += -lbfd -liberty -lz
+
+# wpa_supplicant depends on strong random number generation being available
+# from the operating system. os_get_random() function is used to fetch random
+# data when needed, e.g., for key generation. On Linux and BSD systems, this
+# works by reading /dev/urandom. It should be noted that the OS entropy pool
+# needs to be properly initialized before wpa_supplicant is started. This is
+# important especially on embedded devices that do not have a hardware random
+# number generator and may by default start up with minimal entropy available
+# for random number generation.
+#
+# As a safety net, wpa_supplicant is by default trying to internally collect
+# additional entropy for generating random data to mix in with the data fetched
+# from the OS. This by itself is not considered to be very strong, but it may
+# help in cases where the system pool is not initialized properly. However, it
+# is very strongly recommended that the system pool is initialized with enough
+# entropy either by using hardware assisted random number generator or by
+# storing state over device reboots.
+#
+# wpa_supplicant can be configured to maintain its own entropy store over
+# restarts to enhance random number generation. This is not perfect, but it is
+# much more secure than using the same sequence of random numbers after every
+# reboot. This can be enabled with -e<entropy file> command line option. The
+# specified file needs to be readable and writable by wpa_supplicant.
+#
+# If the os_get_random() is known to provide strong random data (e.g., on
+# Linux/BSD, the board in question is known to have reliable source of random
+# data from /dev/urandom), the internal wpa_supplicant random pool can be
+# disabled. This will save some in binary size and CPU use. However, this
+# should only be considered for builds that are known to be used on devices
+# that meet the requirements described above.
+#CONFIG_NO_RANDOM_POOL=y
+
+# IEEE 802.11n (High Throughput) support (mainly for AP mode)
+CONFIG_IEEE80211N=y
+
+# Interworking (IEEE 802.11u)
+# This can be used to enable functionality to improve interworking with
+# external networks (GAS/ANQP to learn more about the networks and network
+# selection based on available credentials).
+#CONFIG_INTERWORKING=y
+
+# Hotspot 2.0
+#CONFIG_HS20=y
+
 # Disable roaming in wpa_supplicant
 CONFIG_NO_ROAMING=y
 
 # Enable P2P
-# IEEE 802.11n (High Throughput) support (mainly for AP mode)
-CONFIG_IEEE80211N=y
 CONFIG_P2P=y
 CONFIG_AP=y
+
+#Enable Wifi Display
+CONFIG_WIFI_DISPLAY=y
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 6046249..3798f5a 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -81,15 +81,36 @@
 	 */
 	if (wpa_s->hw.modes) {
 		struct hostapd_hw_modes *mode = NULL;
-		int i;
+		int i, no_ht = 0;
 		for (i = 0; i < wpa_s->hw.num_modes; i++) {
 			if (wpa_s->hw.modes[i].mode == conf->hw_mode) {
 				mode = &wpa_s->hw.modes[i];
 				break;
 			}
 		}
-		if (mode && mode->ht_capab) {
+
+#ifdef CONFIG_HT_OVERRIDES
+		if (ssid->disable_ht) {
+			conf->ieee80211n = 0;
+			conf->ht_capab = 0;
+			no_ht = 1;
+		}
+#endif /* CONFIG_HT_OVERRIDES */
+
+		if (!no_ht && mode && mode->ht_capab) {
 			conf->ieee80211n = 1;
+#ifdef CONFIG_P2P
+			if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
+			    (mode->ht_capab &
+			     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+			    ssid->ht40)
+				conf->secondary_channel =
+					wpas_p2p_get_ht40_mode(wpa_s, mode,
+							       conf->channel);
+			if (conf->secondary_channel)
+				conf->ht_capab |=
+					HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+#endif /* CONFIG_P2P */
 
 			/*
 			 * white-list capabilities that won't cause issues
@@ -141,10 +162,11 @@
 		return -1;
 	}
 	os_memcpy(bss->ssid.ssid, ssid->ssid, ssid->ssid_len);
-	bss->ssid.ssid[ssid->ssid_len] = '\0';
 	bss->ssid.ssid_len = ssid->ssid_len;
 	bss->ssid.ssid_set = 1;
 
+	bss->ignore_broadcast_ssid = ssid->ignore_broadcast_ssid;
+
 	if (ssid->auth_alg)
 		bss->auth_algs = ssid->auth_alg;
 
@@ -179,6 +201,12 @@
 		wep->keys_set = 1;
 	}
 
+	if (ssid->ap_max_inactivity)
+		bss->ap_max_inactivity = ssid->ap_max_inactivity;
+
+	if (ssid->dtim_period)
+		bss->dtim_period = ssid->dtim_period;
+
 	/* Select group cipher based on the enabled pairwise cipher suites */
 	pairwise = 0;
 	if (bss->wpa & 1)
@@ -190,6 +218,9 @@
 	}
 	if (pairwise & WPA_CIPHER_TKIP)
 		bss->wpa_group = WPA_CIPHER_TKIP;
+	else if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ==
+		 WPA_CIPHER_GCMP)
+		bss->wpa_group = WPA_CIPHER_GCMP;
 	else
 		bss->wpa_group = WPA_CIPHER_CCMP;
 
@@ -238,7 +269,10 @@
 			      * configuration */
 #endif /* CONFIG_WPS2 */
 	bss->eap_server = 1;
-	bss->wps_state = 2;
+
+	if (!ssid->ignore_broadcast_ssid)
+		bss->wps_state = 2;
+
 	bss->ap_setup_locked = 2;
 	if (wpa_s->conf->config_methods)
 		bss->config_methods = os_strdup(wpa_s->conf->config_methods);
@@ -261,6 +295,7 @@
 	else
 		os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
 	os_memcpy(bss->os_version, wpa_s->conf->os_version, 4);
+	bss->pbc_in_m1 = wpa_s->conf->pbc_in_m1;
 no_wps:
 #endif /* CONFIG_WPS */
 
@@ -349,11 +384,13 @@
 
 
 static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da,
-			   const u8 *bssid, const u8 *ie, size_t ie_len)
+			   const u8 *bssid, const u8 *ie, size_t ie_len,
+			   int ssi_signal)
 {
 #ifdef CONFIG_P2P
 	struct wpa_supplicant *wpa_s = ctx;
-	return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len);
+	return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len,
+				     ssi_signal);
 #else /* CONFIG_P2P */
 	return 0;
 #endif /* CONFIG_P2P */
@@ -427,6 +464,8 @@
 
 	if (ssid->pairwise_cipher & WPA_CIPHER_CCMP)
 		wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
+	else if (ssid->pairwise_cipher & WPA_CIPHER_GCMP)
+		wpa_s->pairwise_cipher = WPA_CIPHER_GCMP;
 	else if (ssid->pairwise_cipher & WPA_CIPHER_TKIP)
 		wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
 	else if (ssid->pairwise_cipher & WPA_CIPHER_NONE)
@@ -468,6 +507,10 @@
 		return -1;
 	}
 
+	os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
+		  wpa_s->conf->wmm_ac_params,
+		  sizeof(wpa_s->conf->wmm_ac_params));
+
 	if (params.uapsd > 0) {
 		conf->bss->wmm_enabled = 1;
 		conf->bss->wmm_uapsd = 1;
@@ -488,7 +531,7 @@
 #endif /* CONFIG_P2P */
 
 	hapd_iface->num_bss = conf->num_bss;
-	hapd_iface->bss = os_zalloc(conf->num_bss *
+	hapd_iface->bss = os_calloc(conf->num_bss,
 				    sizeof(struct hostapd_data *));
 	if (hapd_iface->bss == NULL) {
 		wpa_supplicant_ap_deinit(wpa_s);
@@ -520,9 +563,8 @@
 		hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s;
 #ifdef CONFIG_P2P
 		hapd_iface->bss[i]->p2p = wpa_s->global->p2p;
-		hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(
-			wpa_s, ssid->p2p_persistent_group,
-			ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION);
+		hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(wpa_s,
+								    ssid);
 #endif /* CONFIG_P2P */
 		hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb;
 		hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s;
@@ -651,21 +693,6 @@
 }
 
 
-static int wpa_supplicant_ap_wps_sta_cancel(struct hostapd_data *hapd,
-					    struct sta_info *sta, void *ctx)
-{
-	if (sta && (sta->flags & WLAN_STA_WPS)) {
-		ap_sta_deauthenticate(hapd, sta,
-				      WLAN_REASON_PREV_AUTH_NOT_VALID);
-		wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR,
-			   __func__, MAC2STR(sta->addr));
-		return 1;
-	}
-
-	return 0;
-}
-
-
 int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s)
 {
 	struct wps_registrar *reg;
@@ -677,7 +704,7 @@
 	reg = wpa_s->ap_iface->bss[0]->wps->registrar;
 	reg_sel = wps_registrar_wps_cancel(reg);
 	wps_sta = ap_for_each_sta(wpa_s->ap_iface->bss[0],
-				  wpa_supplicant_ap_wps_sta_cancel, NULL);
+				  ap_sta_wps_cancel, NULL);
 
 	if (!reg_sel && !wps_sta) {
 		wpa_printf(MSG_DEBUG, "No WPS operation in progress at this "
@@ -870,6 +897,26 @@
 }
 
 
+int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s,
+				   const char *txtaddr)
+{
+	if (wpa_s->ap_iface == NULL)
+		return -1;
+	return hostapd_ctrl_iface_disassociate(wpa_s->ap_iface->bss[0],
+					       txtaddr);
+}
+
+
+int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s,
+				     const char *txtaddr)
+{
+	if (wpa_s->ap_iface == NULL)
+		return -1;
+	return hostapd_ctrl_iface_deauthenticate(wpa_s->ap_iface->bss[0],
+						 txtaddr);
+}
+
+
 int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
 				 size_t buflen, int verbose)
 {
@@ -930,6 +977,17 @@
 }
 
 
+void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
+		       int offset)
+{
+	if (!wpa_s->ap_iface)
+		return;
+
+	wpa_s->assoc_freq = freq;
+	hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset);
+}
+
+
 int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
 				      const u8 *addr)
 {
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index f0eb67d..bc953d9 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -31,6 +31,10 @@
 		      char *buf, size_t buflen);
 int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
 			   char *buf, size_t buflen);
+int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s,
+				     const char *txtaddr);
+int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s,
+				   const char *txtaddr);
 int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
 				 size_t buflen, int verbose);
 void ap_tx_status(void *ctx, const u8 *addr,
@@ -45,5 +49,7 @@
 int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
 				      const u8 *addr);
 void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s);
+void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
+		       int offset);
 
 #endif /* AP_H */
diff --git a/wpa_supplicant/autoscan.c b/wpa_supplicant/autoscan.c
new file mode 100644
index 0000000..d0c040a
--- /dev/null
+++ b/wpa_supplicant/autoscan.c
@@ -0,0 +1,143 @@
+/*
+ * WPA Supplicant - auto scan
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "bss.h"
+#include "scan.h"
+#include "autoscan.h"
+
+#ifdef CONFIG_AUTOSCAN_EXPONENTIAL
+extern const struct autoscan_ops autoscan_exponential_ops;
+#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */
+
+#ifdef CONFIG_AUTOSCAN_PERIODIC
+extern const struct autoscan_ops autoscan_periodic_ops;
+#endif /* CONFIG_AUTOSCAN_PERIODIC */
+
+static const struct autoscan_ops * autoscan_modules[] = {
+#ifdef CONFIG_AUTOSCAN_EXPONENTIAL
+	&autoscan_exponential_ops,
+#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */
+#ifdef CONFIG_AUTOSCAN_PERIODIC
+	&autoscan_periodic_ops,
+#endif /* CONFIG_AUTOSCAN_PERIODIC */
+	NULL
+};
+
+
+static void request_scan(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->scan_req = 2;
+
+	if (wpa_supplicant_req_sched_scan(wpa_s))
+		wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval, 0);
+}
+
+
+int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
+{
+	const char *name = wpa_s->conf->autoscan;
+	const char *params;
+	size_t nlen;
+	int i;
+	const struct autoscan_ops *ops = NULL;
+
+	if (wpa_s->autoscan && wpa_s->autoscan_priv)
+		return 0;
+
+	if (name == NULL)
+		return 0;
+
+	params = os_strchr(name, ':');
+	if (params == NULL) {
+		params = "";
+		nlen = os_strlen(name);
+	} else {
+		nlen = params - name;
+		params++;
+	}
+
+	for (i = 0; autoscan_modules[i]; i++) {
+		if (os_strncmp(name, autoscan_modules[i]->name, nlen) == 0) {
+			ops = autoscan_modules[i];
+			break;
+		}
+	}
+
+	if (ops == NULL) {
+		wpa_printf(MSG_ERROR, "autoscan: Could not find module "
+			   "matching the parameter '%s'", name);
+		return -1;
+	}
+
+	wpa_s->autoscan_params = NULL;
+
+	wpa_s->autoscan_priv = ops->init(wpa_s, params);
+	if (wpa_s->autoscan_priv == NULL)
+		return -1;
+	wpa_s->autoscan = ops;
+
+	wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with "
+		   "parameters '%s'", ops->name, params);
+	if (!req_scan)
+		return 0;
+
+	/*
+	 * Cancelling existing scan requests, if any.
+	 */
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+	wpa_supplicant_cancel_scan(wpa_s);
+
+	/*
+	 * Firing first scan, which will lead to call autoscan_notify_scan.
+	 */
+	request_scan(wpa_s);
+
+	return 0;
+}
+
+
+void autoscan_deinit(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->autoscan && wpa_s->autoscan_priv) {
+		wpa_printf(MSG_DEBUG, "autoscan: Deinitializing module '%s'",
+			   wpa_s->autoscan->name);
+		wpa_s->autoscan->deinit(wpa_s->autoscan_priv);
+		wpa_s->autoscan = NULL;
+		wpa_s->autoscan_priv = NULL;
+
+		wpa_s->scan_interval = 5;
+		wpa_s->sched_scan_interval = 0;
+	}
+}
+
+
+int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
+			 struct wpa_scan_results *scan_res)
+{
+	int interval;
+
+	if (wpa_s->autoscan && wpa_s->autoscan_priv) {
+		interval = wpa_s->autoscan->notify_scan(wpa_s->autoscan_priv,
+							scan_res);
+
+		if (interval <= 0)
+			return -1;
+
+		wpa_s->scan_interval = interval;
+		wpa_s->sched_scan_interval = interval;
+
+		request_scan(wpa_s);
+	}
+
+	return 0;
+}
diff --git a/wpa_supplicant/autoscan.h b/wpa_supplicant/autoscan.h
new file mode 100644
index 0000000..e2a7652
--- /dev/null
+++ b/wpa_supplicant/autoscan.h
@@ -0,0 +1,49 @@
+/*
+ * WPA Supplicant - auto scan
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AUTOSCAN_H
+#define AUTOSCAN_H
+
+struct wpa_supplicant;
+
+struct autoscan_ops {
+	const char *name;
+
+	void * (*init)(struct wpa_supplicant *wpa_s, const char *params);
+	void (*deinit)(void *priv);
+
+	int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res);
+};
+
+#ifdef CONFIG_AUTOSCAN
+
+int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan);
+void autoscan_deinit(struct wpa_supplicant *wpa_s);
+int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
+			 struct wpa_scan_results *scan_res);
+
+#else /* CONFIG_AUTOSCAN */
+
+static inline int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
+{
+	return 0;
+}
+
+static inline void autoscan_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
+				       struct wpa_scan_results *scan_res)
+{
+	return 0;
+}
+
+#endif /* CONFIG_AUTOSCAN */
+
+#endif /* AUTOSCAN_H */
diff --git a/wpa_supplicant/autoscan_exponential.c b/wpa_supplicant/autoscan_exponential.c
new file mode 100644
index 0000000..424477b
--- /dev/null
+++ b/wpa_supplicant/autoscan_exponential.c
@@ -0,0 +1,104 @@
+/*
+ * WPA Supplicant - auto scan exponential module
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+#include "autoscan.h"
+
+struct autoscan_exponential_data {
+	struct wpa_supplicant *wpa_s;
+	int base;
+	int limit;
+	int interval;
+};
+
+
+static int
+autoscan_exponential_get_params(struct autoscan_exponential_data *data,
+				const char *params)
+{
+	const char *pos;
+
+	if (params == NULL)
+		return -1;
+
+	data->base = atoi(params);
+
+	pos = os_strchr(params, ':');
+	if (pos == NULL)
+		return -1;
+
+	pos++;
+	data->limit = atoi(pos);
+
+	return 0;
+}
+
+
+static void * autoscan_exponential_init(struct wpa_supplicant *wpa_s,
+					const char *params)
+{
+	struct autoscan_exponential_data *data;
+
+	data = os_zalloc(sizeof(struct autoscan_exponential_data));
+	if (data == NULL)
+		return NULL;
+
+	if (autoscan_exponential_get_params(data, params) < 0) {
+		os_free(data);
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "autoscan exponential: base exponential is %d "
+		   "and limit is %d", data->base, data->limit);
+
+	data->wpa_s = wpa_s;
+
+	return data;
+}
+
+
+static void autoscan_exponential_deinit(void *priv)
+{
+	struct autoscan_exponential_data *data = priv;
+
+	os_free(data);
+}
+
+
+static int autoscan_exponential_notify_scan(void *priv,
+					    struct wpa_scan_results *scan_res)
+{
+	struct autoscan_exponential_data *data = priv;
+
+	wpa_printf(MSG_DEBUG, "autoscan exponential: scan result "
+		   "notification");
+
+	if (data->interval >= data->limit)
+		return data->limit;
+
+	if (data->interval <= 0)
+		data->interval = data->base;
+	else {
+		data->interval = data->interval * data->base;
+		if (data->interval > data->limit)
+			return data->limit;
+	}
+
+	return data->interval;
+}
+
+
+const struct autoscan_ops autoscan_exponential_ops = {
+	.name = "exponential",
+	.init = autoscan_exponential_init,
+	.deinit = autoscan_exponential_deinit,
+	.notify_scan = autoscan_exponential_notify_scan,
+};
diff --git a/wpa_supplicant/autoscan_periodic.c b/wpa_supplicant/autoscan_periodic.c
new file mode 100644
index 0000000..102d723
--- /dev/null
+++ b/wpa_supplicant/autoscan_periodic.c
@@ -0,0 +1,85 @@
+/*
+ * WPA Supplicant - auto scan periodic module
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+#include "autoscan.h"
+
+
+struct autoscan_periodic_data {
+	int periodic_interval;
+};
+
+
+static int autoscan_periodic_get_params(struct autoscan_periodic_data *data,
+					const char *params)
+{
+	int interval;
+
+	if (params == NULL)
+		return -1;
+
+	interval = atoi(params);
+
+	if (interval < 0)
+		return -1;
+
+	data->periodic_interval = interval;
+
+	return 0;
+}
+
+
+static void * autoscan_periodic_init(struct wpa_supplicant *wpa_s,
+				     const char *params)
+{
+	struct autoscan_periodic_data *data;
+
+	data = os_zalloc(sizeof(struct autoscan_periodic_data));
+	if (data == NULL)
+		return NULL;
+
+	if (autoscan_periodic_get_params(data, params) < 0) {
+		os_free(data);
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "autoscan periodic: interval is %d",
+		   data->periodic_interval);
+
+	return data;
+}
+
+
+static void autoscan_periodic_deinit(void *priv)
+{
+	struct autoscan_periodic_data *data = priv;
+
+	os_free(data);
+}
+
+
+static int autoscan_periodic_notify_scan(void *priv,
+					 struct wpa_scan_results *scan_res)
+{
+	struct autoscan_periodic_data *data = priv;
+
+	wpa_printf(MSG_DEBUG, "autoscan periodic: scan result notification");
+
+	return data->periodic_interval;
+}
+
+
+const struct autoscan_ops autoscan_periodic_ops = {
+	.name = "periodic",
+	.init = autoscan_periodic_init,
+	.deinit = autoscan_periodic_deinit,
+	.notify_scan = autoscan_periodic_notify_scan,
+};
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
index ed4d879..adf82d8 100644
--- a/wpa_supplicant/bgscan_learn.c
+++ b/wpa_supplicant/bgscan_learn.c
@@ -75,7 +75,7 @@
 	if (bssid_in_array(bss->neigh, bss->num_neigh, bssid))
 		return;
 
-	n = os_realloc(bss->neigh, (bss->num_neigh + 1) * ETH_ALEN);
+	n = os_realloc_array(bss->neigh, bss->num_neigh + 1, ETH_ALEN);
 	if (n == NULL)
 		return;
 
@@ -219,7 +219,7 @@
 	dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
 		if (in_array(freqs, bss->freq))
 			continue;
-		n = os_realloc(freqs, (*count + 2) * sizeof(int));
+		n = os_realloc_array(freqs, *count + 2, sizeof(int));
 		if (n == NULL)
 			return freqs;
 		freqs = n;
@@ -248,7 +248,7 @@
 			wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq "
 				   "%u", data->supp_freqs[idx]);
 			data->probe_idx = idx;
-			n = os_realloc(freqs, (count + 2) * sizeof(int));
+			n = os_realloc_array(freqs, count + 2, sizeof(int));
 			if (n == NULL)
 				return freqs;
 			freqs = n;
@@ -360,7 +360,7 @@
 		for (j = 0; j < modes[i].num_channels; j++) {
 			if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED)
 				continue;
-			n = os_realloc(freqs, (count + 2) * sizeof(int));
+			n = os_realloc_array(freqs, count + 2, sizeof(int));
 			if (n == NULL)
 				continue;
 
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index cbed2e0..6223beb 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -35,24 +35,88 @@
 #define WPA_BSS_IES_CHANGED_FLAG	BIT(8)
 
 
-static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static void wpa_bss_set_hessid(struct wpa_bss *bss)
 {
+#ifdef CONFIG_INTERWORKING
+	const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING);
+	if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) {
+		os_memset(bss->hessid, 0, ETH_ALEN);
+		return;
+	}
+	if (ie[1] == 7)
+		os_memcpy(bss->hessid, ie + 3, ETH_ALEN);
+	else
+		os_memcpy(bss->hessid, ie + 5, ETH_ALEN);
+#endif /* CONFIG_INTERWORKING */
+}
+
+
+struct wpa_bss_anqp * wpa_bss_anqp_alloc(void)
+{
+	struct wpa_bss_anqp *anqp;
+	anqp = os_zalloc(sizeof(*anqp));
+	if (anqp == NULL)
+		return NULL;
+	anqp->users = 1;
+	return anqp;
+}
+
+
+static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
+{
+	if (anqp == NULL)
+		return;
+
+	anqp->users--;
+	if (anqp->users > 0) {
+		/* Another BSS entry holds a pointer to this ANQP info */
+		return;
+	}
+
+#ifdef CONFIG_INTERWORKING
+	wpabuf_free(anqp->venue_name);
+	wpabuf_free(anqp->network_auth_type);
+	wpabuf_free(anqp->roaming_consortium);
+	wpabuf_free(anqp->ip_addr_type_availability);
+	wpabuf_free(anqp->nai_realm);
+	wpabuf_free(anqp->anqp_3gpp);
+	wpabuf_free(anqp->domain_name);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+	wpabuf_free(anqp->hs20_operator_friendly_name);
+	wpabuf_free(anqp->hs20_wan_metrics);
+	wpabuf_free(anqp->hs20_connection_capability);
+	wpabuf_free(anqp->hs20_operating_class);
+#endif /* CONFIG_HS20 */
+
+	os_free(anqp);
+}
+
+
+static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			   const char *reason)
+{
+	if (wpa_s->last_scan_res) {
+		unsigned int i;
+		for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+			if (wpa_s->last_scan_res[i] == bss) {
+				os_memmove(&wpa_s->last_scan_res[i],
+					   &wpa_s->last_scan_res[i + 1],
+					   (wpa_s->last_scan_res_used - i - 1)
+					   * sizeof(struct wpa_bss *));
+				wpa_s->last_scan_res_used--;
+				break;
+			}
+		}
+	}
 	dl_list_del(&bss->list);
 	dl_list_del(&bss->list_id);
 	wpa_s->num_bss--;
 	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
-		" SSID '%s'", bss->id, MAC2STR(bss->bssid),
-		wpa_ssid_txt(bss->ssid, bss->ssid_len));
+		" SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
+		wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
 	wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
-#ifdef CONFIG_INTERWORKING
-	wpabuf_free(bss->anqp_venue_name);
-	wpabuf_free(bss->anqp_network_auth_type);
-	wpabuf_free(bss->anqp_roaming_consortium);
-	wpabuf_free(bss->anqp_ip_addr_type_availability);
-	wpabuf_free(bss->anqp_nai_realm);
-	wpabuf_free(bss->anqp_3gpp);
-	wpabuf_free(bss->anqp_domain_name);
-#endif /* CONFIG_INTERWORKING */
+	wpa_bss_anqp_free(bss->anqp);
 	os_free(bss);
 }
 
@@ -61,6 +125,8 @@
 			     const u8 *ssid, size_t ssid_len)
 {
 	struct wpa_bss *bss;
+	if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+		return NULL;
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
 		    bss->ssid_len == ssid_len &&
@@ -112,13 +178,21 @@
 }
 
 
+static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	return bss == wpa_s->current_bss ||
+		os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
+		os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0;
+}
+
+
 static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_bss *bss;
 
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 		if (!wpa_bss_known(wpa_s, bss)) {
-			wpa_bss_remove(wpa_s, bss);
+			wpa_bss_remove(wpa_s, bss, __func__);
 			return 0;
 		}
 	}
@@ -127,33 +201,40 @@
 }
 
 
-static void wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
+static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
 {
+	struct wpa_bss *bss;
+
 	/*
 	 * Remove the oldest entry that does not match with any configured
 	 * network.
 	 */
 	if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
-		return;
+		return 0;
 
 	/*
-	 * Remove the oldest entry since no better candidate for removal was
-	 * found.
+	 * Remove the oldest entry that isn't currently in use.
 	 */
-	wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss,
-					    struct wpa_bss, list));
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (!wpa_bss_in_use(wpa_s, bss)) {
+			wpa_bss_remove(wpa_s, bss, __func__);
+			return 0;
+		}
+	}
+
+	return -1;
 }
 
 
-static void wpa_bss_add(struct wpa_supplicant *wpa_s,
-			const u8 *ssid, size_t ssid_len,
-			struct wpa_scan_res *res)
+static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
+				    const u8 *ssid, size_t ssid_len,
+				    struct wpa_scan_res *res)
 {
 	struct wpa_bss *bss;
 
 	bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
 	if (bss == NULL)
-		return;
+		return NULL;
 	bss->id = wpa_s->bss_next_id++;
 	bss->last_update_idx = wpa_s->bss_update_idx;
 	wpa_bss_copy_res(bss, res);
@@ -162,6 +243,7 @@
 	bss->ie_len = res->ie_len;
 	bss->beacon_ie_len = res->beacon_ie_len;
 	os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
+	wpa_bss_set_hessid(bss);
 
 	dl_list_add_tail(&wpa_s->bss, &bss->list);
 	dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
@@ -170,8 +252,14 @@
 		" SSID '%s'",
 		bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
 	wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
-	if (wpa_s->num_bss > wpa_s->conf->bss_max_count)
-		wpa_bss_remove_oldest(wpa_s);
+	if (wpa_s->num_bss > wpa_s->conf->bss_max_count &&
+	    wpa_bss_remove_oldest(wpa_s) != 0) {
+		wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
+			   "because all BSSes are in use. We should normally "
+			   "not get here!", (int) wpa_s->num_bss);
+		wpa_s->conf->bss_max_count = wpa_s->num_bss;
+	}
+	return bss;
 }
 
 
@@ -303,8 +391,9 @@
 }
 
 
-static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
-			   struct wpa_scan_res *res)
+static struct wpa_bss *
+wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+	       struct wpa_scan_res *res)
 {
 	u32 changes;
 
@@ -326,6 +415,15 @@
 		nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
 				  res->beacon_ie_len);
 		if (nbss) {
+			unsigned int i;
+			for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+				if (wpa_s->last_scan_res[i] == bss) {
+					wpa_s->last_scan_res[i] = nbss;
+					break;
+				}
+			}
+			if (wpa_s->current_bss == bss)
+				wpa_s->current_bss = nbss;
 			bss = nbss;
 			os_memcpy(bss + 1, res + 1,
 				  res->ie_len + res->beacon_ie_len);
@@ -334,17 +432,13 @@
 		}
 		dl_list_add(prev, &bss->list_id);
 	}
+	if (changes & WPA_BSS_IES_CHANGED_FLAG)
+		wpa_bss_set_hessid(bss);
 	dl_list_add_tail(&wpa_s->bss, &bss->list);
 
 	notify_bss_changes(wpa_s, changes, bss);
-}
 
-
-static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
-{
-	return bss == wpa_s->current_bss ||
-		os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
-		os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0;
+	return bss;
 }
 
 
@@ -353,6 +447,7 @@
 	wpa_s->bss_update_idx++;
 	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
 		wpa_s->bss_update_idx);
+	wpa_s->last_scan_res_used = 0;
 }
 
 
@@ -375,6 +470,18 @@
 	}
 
 	p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);
+#ifdef CONFIG_P2P
+	if (p2p == NULL &&
+	    wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+		/*
+		 * If it's a P2P specific interface, then don't update
+		 * the scan result without a P2P IE.
+		 */
+		wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR
+			   " update for P2P interface", MAC2STR(res->bssid));
+		return;
+	}
+#endif /* CONFIG_P2P */
 	if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN &&
 	    os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0)
 		return; /* Skip P2P listen discovery results here */
@@ -383,9 +490,28 @@
 	 * (to save memory) */
 	bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
 	if (bss == NULL)
-		wpa_bss_add(wpa_s, ssid + 2, ssid[1], res);
+		bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res);
 	else
-		wpa_bss_update(wpa_s, bss, res);
+		bss = wpa_bss_update(wpa_s, bss, res);
+
+	if (bss == NULL)
+		return;
+	if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
+		struct wpa_bss **n;
+		unsigned int siz;
+		if (wpa_s->last_scan_res_size == 0)
+			siz = 32;
+		else
+			siz = wpa_s->last_scan_res_size * 2;
+		n = os_realloc_array(wpa_s->last_scan_res, siz,
+				     sizeof(struct wpa_bss *));
+		if (n == NULL)
+			return;
+		wpa_s->last_scan_res = n;
+		wpa_s->last_scan_res_size = siz;
+	}
+
+	wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
 }
 
 
@@ -435,9 +561,26 @@
 {
 	struct wpa_bss *bss, *n;
 
+	wpa_s->last_scan_full = 0;
+	os_get_time(&wpa_s->last_scan);
 	if (!new_scan)
 		return; /* do not expire entries without new scan */
 
+	if (info && !info->aborted && !info->freqs) {
+		size_t i;
+		if (info->num_ssids == 0) {
+			wpa_s->last_scan_full = 1;
+		} else {
+			for (i = 0; i < info->num_ssids; i++) {
+				if (info->ssids[i].ssid == NULL ||
+				    info->ssids[i].ssid_len == 0) {
+					wpa_s->last_scan_full = 1;
+					break;
+				}
+			}
+		}
+	}
+
 	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
 		if (wpa_bss_in_use(wpa_s, bss))
 			continue;
@@ -447,11 +590,14 @@
 			bss->scan_miss_count++;
 		if (bss->scan_miss_count >=
 		    wpa_s->conf->bss_expiration_scan_count) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to "
-				"no match in scan", bss->id);
-			wpa_bss_remove(wpa_s, bss);
+			wpa_bss_remove(wpa_s, bss, "no match in scan");
 		}
 	}
+
+	wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u "
+		   "last_scan_full=%d",
+		   wpa_s->last_scan_res_used, wpa_s->last_scan_res_size,
+		   wpa_s->last_scan_full);
 }
 
 
@@ -471,9 +617,7 @@
 			continue;
 
 		if (os_time_before(&bss->last_update, &t)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to "
-				"age", bss->id);
-			wpa_bss_remove(wpa_s, bss);
+			wpa_bss_remove(wpa_s, bss, __func__);
 		} else
 			break;
 	}
@@ -510,7 +654,7 @@
 	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
 		if (wpa_bss_in_use(wpa_s, bss))
 			continue;
-		wpa_bss_remove(wpa_s, bss);
+		wpa_bss_remove(wpa_s, bss, __func__);
 	}
 }
 
@@ -526,6 +670,8 @@
 				   const u8 *bssid)
 {
 	struct wpa_bss *bss;
+	if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+		return NULL;
 	dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
 		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
 			return bss;
@@ -632,6 +778,38 @@
 }
 
 
+struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
+						   u32 vendor_type)
+{
+	struct wpabuf *buf;
+	const u8 *end, *pos;
+
+	buf = wpabuf_alloc(bss->beacon_ie_len);
+	if (buf == NULL)
+		return NULL;
+
+	pos = (const u8 *) (bss + 1);
+	pos += bss->ie_len;
+	end = pos + bss->beacon_ie_len;
+
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+		    vendor_type == WPA_GET_BE32(&pos[2]))
+			wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
+		pos += 2 + pos[1];
+	}
+
+	if (wpabuf_len(buf) == 0) {
+		wpabuf_free(buf);
+		buf = NULL;
+	}
+
+	return buf;
+}
+
+
 int wpa_bss_get_max_rate(const struct wpa_bss *bss)
 {
 	int rate = 0;
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 2bc9f82..4a386b6 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -19,23 +19,24 @@
 #define WPA_BSS_ASSOCIATED		BIT(5)
 #define WPA_BSS_ANQP_FETCH_TRIED	BIT(6)
 
-#define WPA_BSS_MASK_ALL		0xFFFFFFFF
-#define WPA_BSS_MASK_ID			BIT(0)
-#define WPA_BSS_MASK_BSSID		BIT(1)
-#define WPA_BSS_MASK_FREQ		BIT(2)
-#define WPA_BSS_MASK_BEACON_INT		BIT(3)
-#define WPA_BSS_MASK_CAPABILITIES	BIT(4)
-#define WPA_BSS_MASK_QUAL		BIT(5)
-#define WPA_BSS_MASK_NOISE		BIT(6)
-#define WPA_BSS_MASK_LEVEL		BIT(7)
-#define WPA_BSS_MASK_TSF		BIT(8)
-#define WPA_BSS_MASK_AGE		BIT(9)
-#define WPA_BSS_MASK_IE			BIT(10)
-#define WPA_BSS_MASK_FLAGS		BIT(11)
-#define WPA_BSS_MASK_SSID		BIT(12)
-#define WPA_BSS_MASK_WPS_SCAN		BIT(13)
-#define WPA_BSS_MASK_P2P_SCAN		BIT(14)
-#define WPA_BSS_MASK_INTERNETW		BIT(15)
+struct wpa_bss_anqp {
+	unsigned int users;
+#ifdef CONFIG_INTERWORKING
+	struct wpabuf *venue_name;
+	struct wpabuf *network_auth_type;
+	struct wpabuf *roaming_consortium;
+	struct wpabuf *ip_addr_type_availability;
+	struct wpabuf *nai_realm;
+	struct wpabuf *anqp_3gpp;
+	struct wpabuf *domain_name;
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+	struct wpabuf *hs20_operator_friendly_name;
+	struct wpabuf *hs20_wan_metrics;
+	struct wpabuf *hs20_connection_capability;
+	struct wpabuf *hs20_operating_class;
+#endif /* CONFIG_HS20 */
+};
 
 /**
  * struct wpa_bss - BSS table
@@ -46,6 +47,7 @@
  * @flags: information flags about the BSS/IBSS (WPA_BSS_*)
  * @last_update_idx: Index of the last scan update
  * @bssid: BSSID
+ * @hessid: HESSID
  * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
  * @beacon_int: beacon interval in TUs (host byte order)
  * @caps: capability information field in host byte order
@@ -68,6 +70,7 @@
 	unsigned int last_update_idx;
 	unsigned int flags;
 	u8 bssid[ETH_ALEN];
+	u8 hessid[ETH_ALEN];
 	u8 ssid[32];
 	size_t ssid_len;
 	int freq;
@@ -78,15 +81,7 @@
 	int level;
 	u64 tsf;
 	struct os_time last_update;
-#ifdef CONFIG_INTERWORKING
-	struct wpabuf *anqp_venue_name;
-	struct wpabuf *anqp_network_auth_type;
-	struct wpabuf *anqp_roaming_consortium;
-	struct wpabuf *anqp_ip_addr_type_availability;
-	struct wpabuf *anqp_nai_realm;
-	struct wpabuf *anqp_3gpp;
-	struct wpabuf *anqp_domain_name;
-#endif /* CONFIG_INTERWORKING */
+	struct wpa_bss_anqp *anqp;
 	size_t ie_len;
 	size_t beacon_ie_len;
 	/* followed by ie_len octets of IEs */
@@ -113,7 +108,10 @@
 const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
 struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
 					    u32 vendor_type);
+struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
+						   u32 vendor_type);
 int wpa_bss_get_max_rate(const struct wpa_bss *bss);
 int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates);
+struct wpa_bss_anqp * wpa_bss_anqp_alloc(void);
 
 #endif /* BSS_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index d36d027..be21029 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Configuration parser and common functions
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -13,6 +13,7 @@
 #include "crypto/sha1.h"
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap.h"
+#include "p2p/p2p.h"
 #include "config.h"
 
 
@@ -52,42 +53,6 @@
 };
 
 
-static char * wpa_config_parse_string(const char *value, size_t *len)
-{
-	if (*value == '"') {
-		const char *pos;
-		char *str;
-		value++;
-		pos = os_strrchr(value, '"');
-		if (pos == NULL || pos[1] != '\0')
-			return NULL;
-		*len = pos - value;
-		str = os_malloc(*len + 1);
-		if (str == NULL)
-			return NULL;
-		os_memcpy(str, value, *len);
-		str[*len] = '\0';
-		return str;
-	} else {
-		u8 *str;
-		size_t tlen, hlen = os_strlen(value);
-		if (hlen & 1)
-			return NULL;
-		tlen = hlen / 2;
-		str = os_malloc(tlen + 1);
-		if (str == NULL)
-			return NULL;
-		if (hexstr2bin(value, str, tlen)) {
-			os_free(str);
-			return NULL;
-		}
-		str[tlen] = '\0';
-		*len = tlen;
-		return (char *) str;
-	}
-}
-
-
 static int wpa_config_parse_str(const struct parse_data *data,
 				struct wpa_ssid *ssid,
 				int line, const char *value)
@@ -148,18 +113,6 @@
 
 
 #ifndef NO_CONFIG_WRITE
-static int is_hex(const u8 *data, size_t len)
-{
-	size_t i;
-
-	for (i = 0; i < len; i++) {
-		if (data[i] < 32 || data[i] >= 127)
-			return 1;
-	}
-	return 0;
-}
-
-
 static char * wpa_config_write_string_ascii(const u8 *value, size_t len)
 {
 	char *buf;
@@ -218,26 +171,6 @@
 
 	return wpa_config_write_string((const u8 *) *src, len);
 }
-
-#ifdef WPA_UNICODE_SSID
-static char * wpa_config_write_str_unicode(const struct parse_data *data,
-						struct wpa_ssid *ssid)
-{
-	size_t len;
-	char **src;
-
-	src = (char **) (((u8 *) ssid) + (long) data->param1);
-	if (*src == NULL)
-		return NULL;
-
-	if (data->param2)
-		len = *((size_t *) (((u8 *) ssid) + (long) data->param2));
-	else
-		len = os_strlen(*src);
-
-	return wpa_config_write_string_ascii((const u8 *) *src, len);
-}
-#endif
 #endif /* NO_CONFIG_WRITE */
 
 
@@ -343,6 +276,21 @@
 				struct wpa_ssid *ssid, int line,
 				const char *value)
 {
+#ifdef CONFIG_EXT_PASSWORD
+	if (os_strncmp(value, "ext:", 4) == 0) {
+		os_free(ssid->passphrase);
+		ssid->passphrase = NULL;
+		ssid->psk_set = 0;
+		os_free(ssid->ext_psk);
+		ssid->ext_psk = os_strdup(value + 4);
+		if (ssid->ext_psk == NULL)
+			return -1;
+		wpa_printf(MSG_DEBUG, "PSK: External password '%s'",
+			   ssid->ext_psk);
+		return 0;
+	}
+#endif /* CONFIG_EXT_PASSWORD */
+
 	if (*value == '"') {
 #ifndef CONFIG_NO_PBKDF2
 		const char *pos;
@@ -400,6 +348,17 @@
 static char * wpa_config_write_psk(const struct parse_data *data,
 				   struct wpa_ssid *ssid)
 {
+#ifdef CONFIG_EXT_PASSWORD
+	if (ssid->ext_psk) {
+		size_t len = 4 + os_strlen(ssid->ext_psk) + 1;
+		char *buf = os_malloc(len);
+		if (buf == NULL)
+			return NULL;
+		os_snprintf(buf, len, "ext:%s", ssid->ext_psk);
+		return buf;
+	}
+#endif /* CONFIG_EXT_PASSWORD */
+
 	if (ssid->passphrase)
 		return wpa_config_write_string_ascii(
 			(const u8 *) ssid->passphrase,
@@ -684,6 +643,8 @@
 		*end = '\0';
 		if (os_strcmp(start, "CCMP") == 0)
 			val |= WPA_CIPHER_CCMP;
+		else if (os_strcmp(start, "GCMP") == 0)
+			val |= WPA_CIPHER_GCMP;
 		else if (os_strcmp(start, "TKIP") == 0)
 			val |= WPA_CIPHER_TKIP;
 		else if (os_strcmp(start, "WEP104") == 0)
@@ -735,6 +696,16 @@
 		pos += ret;
 	}
 
+	if (cipher & WPA_CIPHER_GCMP) {
+		ret = os_snprintf(pos, end - pos, "%sGCMP",
+				  pos == buf ? "" : " ");
+		if (ret < 0 || ret >= end - pos) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
 	if (cipher & WPA_CIPHER_TKIP) {
 		ret = os_snprintf(pos, end - pos, "%sTKIP",
 				  pos == buf ? "" : " ");
@@ -788,7 +759,8 @@
 	val = wpa_config_parse_cipher(line, value);
 	if (val == -1)
 		return -1;
-	if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)) {
+	if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP |
+		    WPA_CIPHER_NONE)) {
 		wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher "
 			   "(0x%x).", line, val);
 		return -1;
@@ -817,8 +789,8 @@
 	val = wpa_config_parse_cipher(line, value);
 	if (val == -1)
 		return -1;
-	if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 |
-		    WPA_CIPHER_WEP40)) {
+	if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP |
+		    WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) {
 		wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
 			   "(0x%x).", line, val);
 		return -1;
@@ -948,7 +920,7 @@
 
 	used = 0;
 	len = 10;
-	freqs = os_zalloc((len + 1) * sizeof(int));
+	freqs = os_calloc(len + 1, sizeof(int));
 	if (freqs == NULL)
 		return NULL;
 
@@ -959,7 +931,7 @@
 		if (used == len) {
 			int *n;
 			size_t i;
-			n = os_realloc(freqs, (len * 2 + 1) * sizeof(int));
+			n = os_realloc_array(freqs, len * 2 + 1, sizeof(int));
 			if (n == NULL) {
 				os_free(freqs);
 				return NULL;
@@ -1088,8 +1060,8 @@
 		last = *end == '\0';
 		*end = '\0';
 		tmp = methods;
-		methods = os_realloc(methods,
-				     (num_methods + 1) * sizeof(*methods));
+		methods = os_realloc_array(methods, num_methods + 1,
+					   sizeof(*methods));
 		if (methods == NULL) {
 			os_free(tmp);
 			os_free(buf);
@@ -1119,7 +1091,7 @@
 	os_free(buf);
 
 	tmp = methods;
-	methods = os_realloc(methods, (num_methods + 1) * sizeof(*methods));
+	methods = os_realloc_array(methods, num_methods + 1, sizeof(*methods));
 	if (methods == NULL) {
 		os_free(tmp);
 		return -1;
@@ -1130,6 +1102,7 @@
 
 	wpa_hexdump(MSG_MSGDUMP, "eap methods",
 		    (u8 *) methods, num_methods * sizeof(*methods));
+	os_free(ssid->eap.eap_methods);
 	ssid->eap.eap_methods = methods;
 	return errors ? -1 : 0;
 }
@@ -1184,6 +1157,20 @@
 		return 0;
 	}
 
+#ifdef CONFIG_EXT_PASSWORD
+	if (os_strncmp(value, "ext:", 4) == 0) {
+		char *name = os_strdup(value + 4);
+		if (name == NULL)
+			return -1;
+		os_free(ssid->eap.password);
+		ssid->eap.password = (u8 *) name;
+		ssid->eap.password_len = os_strlen(name);
+		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+		ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_PASSWORD;
+		return 0;
+	}
+#endif /* CONFIG_EXT_PASSWORD */
+
 	if (os_strncmp(value, "hash:", 5) != 0) {
 		char *tmp;
 		size_t res_len;
@@ -1201,6 +1188,7 @@
 		ssid->eap.password = (u8 *) tmp;
 		ssid->eap.password_len = res_len;
 		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
 
 		return 0;
 	}
@@ -1229,6 +1217,7 @@
 	ssid->eap.password = hash;
 	ssid->eap.password_len = 16;
 	ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+	ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
 
 	return 0;
 }
@@ -1242,6 +1231,17 @@
 	if (ssid->eap.password == NULL)
 		return NULL;
 
+#ifdef CONFIG_EXT_PASSWORD
+	if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+		buf = os_zalloc(4 + ssid->eap.password_len + 1);
+		if (buf == NULL)
+			return NULL;
+		os_memcpy(buf, "ext:", 4);
+		os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len);
+		return buf;
+	}
+#endif /* CONFIG_EXT_PASSWORD */
+
 	if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
 		return wpa_config_write_string(
 			ssid->eap.password, ssid->eap.password_len);
@@ -1277,6 +1277,11 @@
 		os_free(buf);
 		return -1;
 	}
+	if (*len && *len != 5 && *len != 13 && *len != 16) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key length %u - "
+			   "this network block will be ignored",
+			   line, (unsigned int) *len);
+	}
 	os_memcpy(key, buf, *len);
 	os_free(buf);
 	res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
@@ -1384,18 +1389,27 @@
 			pos++;
 
 		if (hwaddr_aton(pos, addr)) {
-			wpa_printf(MSG_ERROR, "Line %d: Invalid "
-				   "p2p_client_list address '%s'.",
-				   line, value);
-			/* continue anyway */
+			if (count == 0) {
+				wpa_printf(MSG_ERROR, "Line %d: Invalid "
+					   "p2p_client_list address '%s'.",
+					   line, value);
+				os_free(buf);
+				return -1;
+			}
+			/* continue anyway since this could have been from a
+			 * truncated configuration file line */
+			wpa_printf(MSG_INFO, "Line %d: Ignore likely "
+				   "truncated p2p_client_list address '%s'",
+				   line, pos);
 		} else {
-			n = os_realloc(buf, (count + 1) * ETH_ALEN);
+			n = os_realloc_array(buf, count + 1, ETH_ALEN);
 			if (n == NULL) {
 				os_free(buf);
 				return -1;
 			}
 			buf = n;
-			os_memcpy(buf + count * ETH_ALEN, addr, ETH_ALEN);
+			os_memmove(buf + ETH_ALEN, buf, count * ETH_ALEN);
+			os_memcpy(buf, addr, ETH_ALEN);
 			count++;
 			wpa_hexdump(MSG_MSGDUMP, "p2p_client_list",
 				    addr, ETH_ALEN);
@@ -1429,10 +1443,10 @@
 	pos = value;
 	end = value + 20 * ssid->num_p2p_clients;
 
-	for (i = 0; i < ssid->num_p2p_clients; i++) {
+	for (i = ssid->num_p2p_clients; i > 0; i--) {
 		res = os_snprintf(pos, end - pos, MACSTR " ",
 				  MAC2STR(ssid->p2p_client_list +
-					  i * ETH_ALEN));
+					  (i - 1) * ETH_ALEN));
 		if (res < 0 || res >= end - pos) {
 			os_free(value);
 			return NULL;
@@ -1493,15 +1507,6 @@
 	OFFSET(f), (void *) 0
 #define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \
 	OFFSET(eap.f), (void *) 0
-#ifdef WPA_UNICODE_SSID
-/* STR_* variants that do not force conversion to ASCII */
-#define _STR_UNICODE(f) #f, wpa_config_parse_str, wpa_config_write_str_unicode, OFFSET(f)
-#define STR_UNICODE(f) _STR_UNICODE(f), NULL, NULL, NULL, 0
-#define _STR_LEN_UNICODE(f) _STR_UNICODE(f), OFFSET(f ## _len)
-#define STR_LEN_UNICODE(f) _STR_LEN_UNICODE(f), NULL, NULL, 0
-#define _STR_RANGE_UNICODE(f, min, max) _STR_LEN_UNICODE(f), (void *) (min), (void *) (max)
-#define STR_RANGE_UNICODE(f, min, max) _STR_RANGE_UNICODE(f, min, max), 0
-#endif
 #endif /* NO_CONFIG_WRITE */
 
 /* INT: Define an integer variable */
@@ -1546,16 +1551,13 @@
  * functions.
  */
 static const struct parse_data ssid_fields[] = {
-#ifdef WPA_UNICODE_SSID
-	{ STR_RANGE_UNICODE(ssid, 0, MAX_SSID_LEN) },
-#else
 	{ STR_RANGE(ssid, 0, MAX_SSID_LEN) },
-#endif
 	{ INT_RANGE(scan_ssid, 0, 1) },
 	{ FUNC(bssid) },
 	{ FUNC_KEY(psk) },
 	{ FUNC(proto) },
 	{ FUNC(key_mgmt) },
+	{ INT(bg_scan_period) },
 	{ FUNC(pairwise) },
 	{ FUNC(group) },
 	{ FUNC(auth_alg) },
@@ -1622,6 +1624,7 @@
 	{ INT_RANGE(frequency, 0, 10000) },
 	{ INT(wpa_ptk_rekey) },
 	{ STR(bgscan) },
+	{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
 #ifdef CONFIG_P2P
 	{ FUNC(p2p_client_list) },
 #endif /* CONFIG_P2P */
@@ -1633,17 +1636,10 @@
 	{ INT_RANGE(ampdu_density, -1, 7) },
 	{ STR(ht_mcs) },
 #endif /* CONFIG_HT_OVERRIDES */
+	{ INT(ap_max_inactivity) },
+	{ INT(dtim_period) },
 };
 
-#ifdef WPA_UNICODE_SSID
-#undef _STR_UNICODE
-#undef STR_UNICODE
-#undef _STR_LEN_UNICODE
-#undef STR_LEN_UNICODE
-#undef _STR_RANGE_UNICODE
-#undef STR_RANGE_UNICODE
-#endif
-
 #undef OFFSET
 #undef _STR
 #undef STR
@@ -1695,19 +1691,20 @@
 	}
 
 	/* First network for this priority - add a new priority list */
-	nlist = os_realloc(config->pssid,
-			   (config->num_prio + 1) * sizeof(struct wpa_ssid *));
+	nlist = os_realloc_array(config->pssid, config->num_prio + 1,
+				 sizeof(struct wpa_ssid *));
 	if (nlist == NULL)
 		return -1;
 
 	for (prio = 0; prio < config->num_prio; prio++) {
-		if (nlist[prio]->priority < ssid->priority)
+		if (nlist[prio]->priority < ssid->priority) {
+			os_memmove(&nlist[prio + 1], &nlist[prio],
+				   (config->num_prio - prio) *
+				   sizeof(struct wpa_ssid *));
 			break;
+		}
 	}
 
-	os_memmove(&nlist[prio + 1], &nlist[prio],
-		   (config->num_prio - prio) * sizeof(struct wpa_ssid *));
-
 	nlist[prio] = ssid;
 	config->num_prio++;
 	config->pssid = nlist;
@@ -1801,6 +1798,7 @@
 {
 	os_free(ssid->ssid);
 	os_free(ssid->passphrase);
+	os_free(ssid->ext_psk);
 #ifdef IEEE8021X_EAPOL
 	eap_peer_config_free(&ssid->eap);
 #endif /* IEEE8021X_EAPOL */
@@ -1816,6 +1814,25 @@
 }
 
 
+void wpa_config_free_cred(struct wpa_cred *cred)
+{
+	os_free(cred->realm);
+	os_free(cred->username);
+	os_free(cred->password);
+	os_free(cred->ca_cert);
+	os_free(cred->client_cert);
+	os_free(cred->private_key);
+	os_free(cred->private_key_passwd);
+	os_free(cred->imsi);
+	os_free(cred->milenage);
+	os_free(cred->domain);
+	os_free(cred->eap_method);
+	os_free(cred->phase1);
+	os_free(cred->phase2);
+	os_free(cred);
+}
+
+
 /**
  * wpa_config_free - Free configuration data
  * @config: Configuration data from wpa_config_read()
@@ -1829,6 +1846,7 @@
 	struct wpa_config_blob *blob, *prevblob;
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 	struct wpa_ssid *ssid, *prev = NULL;
+	struct wpa_cred *cred, *cprev;
 
 	ssid = config->ssid;
 	while (ssid) {
@@ -1837,6 +1855,13 @@
 		wpa_config_free_ssid(prev);
 	}
 
+	cred = config->cred;
+	while (cred) {
+		cprev = cred;
+		cred = cred->next;
+		wpa_config_free_cred(cprev);
+	}
+
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	blob = config->blobs;
 	prevblob = NULL;
@@ -1847,11 +1872,14 @@
 	}
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 
+	wpabuf_free(config->wps_vendor_ext_m1);
 	os_free(config->ctrl_interface);
 	os_free(config->ctrl_interface_group);
 	os_free(config->opensc_engine_path);
 	os_free(config->pkcs11_engine_path);
 	os_free(config->pkcs11_module_path);
+	os_free(config->pcsc_reader);
+	os_free(config->pcsc_pin);
 	os_free(config->driver_param);
 	os_free(config->device_name);
 	os_free(config->manufacturer);
@@ -1861,15 +1889,12 @@
 	os_free(config->config_methods);
 	os_free(config->p2p_ssid_postfix);
 	os_free(config->pssid);
-	os_free(config->home_realm);
-	os_free(config->home_username);
-	os_free(config->home_password);
-	os_free(config->home_ca_cert);
-	os_free(config->home_imsi);
-	os_free(config->home_milenage);
-#ifdef ANDROID_P2P
-	os_free(config->prioritize);
-#endif
+	os_free(config->p2p_pref_chan);
+	os_free(config->autoscan);
+	wpabuf_free(config->wps_nfc_dh_pubkey);
+	wpabuf_free(config->wps_nfc_dh_privkey);
+	wpabuf_free(config->wps_nfc_dev_pw);
+	os_free(config->ext_password_backend);
 	os_free(config);
 }
 
@@ -1997,6 +2022,7 @@
 	ssid->pairwise_cipher = DEFAULT_PAIRWISE;
 	ssid->group_cipher = DEFAULT_GROUP;
 	ssid->key_mgmt = DEFAULT_KEY_MGMT;
+	ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
 #ifdef IEEE8021X_EAPOL
 	ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
 	ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
@@ -2103,7 +2129,7 @@
 
 	get_keys = get_keys && ssid->export_keys;
 
-	props = os_zalloc(sizeof(char *) * ((2 * NUM_SSID_FIELDS) + 1));
+	props = os_calloc(2 * NUM_SSID_FIELDS + 1, sizeof(char *));
 	if (!props)
 		return NULL;
 
@@ -2231,8 +2257,7 @@
 void wpa_config_update_psk(struct wpa_ssid *ssid)
 {
 #ifndef CONFIG_NO_PBKDF2
-	pbkdf2_sha1(ssid->passphrase,
-		    (char *) ssid->ssid, ssid->ssid_len, 4096,
+	pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096,
 		    ssid->psk, PMK_LEN);
 	wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
 			ssid->psk, PMK_LEN);
@@ -2241,6 +2266,220 @@
 }
 
 
+int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
+			const char *value, int line)
+{
+	char *val;
+	size_t len;
+
+	if (os_strcmp(var, "priority") == 0) {
+		cred->priority = atoi(value);
+		return 0;
+	}
+
+	if (os_strcmp(var, "pcsc") == 0) {
+		cred->pcsc = atoi(value);
+		return 0;
+	}
+
+	if (os_strcmp(var, "eap") == 0) {
+		struct eap_method_type method;
+		method.method = eap_peer_get_type(value, &method.vendor);
+		if (method.vendor == EAP_VENDOR_IETF &&
+		    method.method == EAP_TYPE_NONE) {
+			wpa_printf(MSG_ERROR, "Line %d: unknown EAP type '%s' "
+				   "for a credential", line, value);
+			return -1;
+		}
+		os_free(cred->eap_method);
+		cred->eap_method = os_malloc(sizeof(*cred->eap_method));
+		if (cred->eap_method == NULL)
+			return -1;
+		os_memcpy(cred->eap_method, &method, sizeof(method));
+		return 0;
+	}
+
+	if (os_strcmp(var, "password") == 0 &&
+	    os_strncmp(value, "ext:", 4) == 0) {
+		os_free(cred->password);
+		cred->password = os_strdup(value);
+		cred->ext_password = 1;
+		return 0;
+	}
+
+	val = wpa_config_parse_string(value, &len);
+	if (val == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
+			   "value '%s'.", line, var, value);
+		return -1;
+	}
+
+	if (os_strcmp(var, "realm") == 0) {
+		os_free(cred->realm);
+		cred->realm = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "username") == 0) {
+		os_free(cred->username);
+		cred->username = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "password") == 0) {
+		os_free(cred->password);
+		cred->password = val;
+		cred->ext_password = 0;
+		return 0;
+	}
+
+	if (os_strcmp(var, "ca_cert") == 0) {
+		os_free(cred->ca_cert);
+		cred->ca_cert = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "client_cert") == 0) {
+		os_free(cred->client_cert);
+		cred->client_cert = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "private_key") == 0) {
+		os_free(cred->private_key);
+		cred->private_key = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "private_key_passwd") == 0) {
+		os_free(cred->private_key_passwd);
+		cred->private_key_passwd = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "imsi") == 0) {
+		os_free(cred->imsi);
+		cred->imsi = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "milenage") == 0) {
+		os_free(cred->milenage);
+		cred->milenage = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "domain") == 0) {
+		os_free(cred->domain);
+		cred->domain = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "phase1") == 0) {
+		os_free(cred->phase1);
+		cred->phase1 = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "phase2") == 0) {
+		os_free(cred->phase2);
+		cred->phase2 = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "roaming_consortium") == 0) {
+		if (len < 3 || len > sizeof(cred->roaming_consortium)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid "
+				   "roaming_consortium length %d (3..15 "
+				   "expected)", line, (int) len);
+			os_free(val);
+			return -1;
+		}
+		os_memcpy(cred->roaming_consortium, val, len);
+		cred->roaming_consortium_len = len;
+		os_free(val);
+		return 0;
+	}
+
+	if (line) {
+		wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
+			   line, var);
+	}
+
+	os_free(val);
+
+	return -1;
+}
+
+
+struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id)
+{
+	struct wpa_cred *cred;
+
+	cred = config->cred;
+	while (cred) {
+		if (id == cred->id)
+			break;
+		cred = cred->next;
+	}
+
+	return cred;
+}
+
+
+struct wpa_cred * wpa_config_add_cred(struct wpa_config *config)
+{
+	int id;
+	struct wpa_cred *cred, *last = NULL;
+
+	id = -1;
+	cred = config->cred;
+	while (cred) {
+		if (cred->id > id)
+			id = cred->id;
+		last = cred;
+		cred = cred->next;
+	}
+	id++;
+
+	cred = os_zalloc(sizeof(*cred));
+	if (cred == NULL)
+		return NULL;
+	cred->id = id;
+	if (last)
+		last->next = cred;
+	else
+		config->cred = cred;
+
+	return cred;
+}
+
+
+int wpa_config_remove_cred(struct wpa_config *config, int id)
+{
+	struct wpa_cred *cred, *prev = NULL;
+
+	cred = config->cred;
+	while (cred) {
+		if (id == cred->id)
+			break;
+		prev = cred;
+		cred = cred->next;
+	}
+
+	if (cred == NULL)
+		return -1;
+
+	if (prev)
+		prev->next = cred->next;
+	else
+		config->cred = cred->next;
+
+	wpa_config_free_cred(cred);
+	return 0;
+}
+
+
 #ifndef CONFIG_NO_CONFIG_BLOBS
 /**
  * wpa_config_get_blob - Get a named configuration blob
@@ -2332,6 +2571,15 @@
 					   const char *driver_param)
 {
 	struct wpa_config *config;
+	const int aCWmin = 4, aCWmax = 10;
+	const struct hostapd_wmm_ac_params ac_bk =
+		{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+	const struct hostapd_wmm_ac_params ac_be =
+		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
+	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
+		{ aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
+	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+		{ aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
 
 	config = os_zalloc(sizeof(*config));
 	if (config == NULL)
@@ -2341,11 +2589,16 @@
 	config->fast_reauth = DEFAULT_FAST_REAUTH;
 	config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
 	config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
+	config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
 	config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
 	config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
 	config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
 	config->max_num_sta = DEFAULT_MAX_NUM_STA;
 	config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
+	config->wmm_ac_params[0] = ac_be;
+	config->wmm_ac_params[1] = ac_bk;
+	config->wmm_ac_params[2] = ac_vi;
+	config->wmm_ac_params[3] = ac_vo;
 
 	if (ctrl_interface)
 		config->ctrl_interface = os_strdup(ctrl_interface);
@@ -2454,6 +2707,35 @@
 }
 
 
+static int wpa_global_config_parse_bin(const struct global_parse_data *data,
+				       struct wpa_config *config, int line,
+				       const char *pos)
+{
+	size_t len;
+	struct wpabuf **dst, *tmp;
+
+	len = os_strlen(pos);
+	if (len & 0x01)
+		return -1;
+
+	tmp = wpabuf_alloc(len / 2);
+	if (tmp == NULL)
+		return -1;
+
+	if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) {
+		wpabuf_free(tmp);
+		return -1;
+	}
+
+	dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
+	wpabuf_free(*dst);
+	*dst = tmp;
+	wpa_printf(MSG_DEBUG, "%s", data->name);
+
+	return 0;
+}
+
+
 static int wpa_config_process_country(const struct global_parse_data *data,
 				      struct wpa_config *config, int line,
 				      const char *pos)
@@ -2528,6 +2810,43 @@
 	return 0;
 }
 
+
+static int wpa_config_process_wps_vendor_ext_m1(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	struct wpabuf *tmp;
+	int len = os_strlen(pos) / 2;
+	u8 *p;
+
+	if (!len) {
+		wpa_printf(MSG_ERROR, "Line %d: "
+			   "invalid wps_vendor_ext_m1", line);
+		return -1;
+	}
+
+	tmp = wpabuf_alloc(len);
+	if (tmp) {
+		p = wpabuf_put(tmp, len);
+
+		if (hexstr2bin(pos, p, len)) {
+			wpa_printf(MSG_ERROR, "Line %d: "
+				   "invalid wps_vendor_ext_m1", line);
+			wpabuf_free(tmp);
+			return -1;
+		}
+
+		wpabuf_free(config->wps_vendor_ext_m1);
+		config->wps_vendor_ext_m1 = tmp;
+	} else {
+		wpa_printf(MSG_ERROR, "Can not allocate "
+			   "memory for wps_vendor_ext_m1");
+		return -1;
+	}
+
+	return 0;
+}
+
 #endif /* CONFIG_WPS */
 
 #ifdef CONFIG_P2P
@@ -2551,6 +2870,56 @@
 	config->num_sec_device_types++;
 	return 0;
 }
+
+
+static int wpa_config_process_p2p_pref_chan(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	struct p2p_channel *pref = NULL, *n;
+	unsigned int num = 0;
+	const char *pos2;
+	u8 op_class, chan;
+
+	/* format: class:chan,class:chan,... */
+
+	while (*pos) {
+		op_class = atoi(pos);
+		pos2 = os_strchr(pos, ':');
+		if (pos2 == NULL)
+			goto fail;
+		pos2++;
+		chan = atoi(pos2);
+
+		n = os_realloc_array(pref, num + 1,
+				     sizeof(struct p2p_channel));
+		if (n == NULL)
+			goto fail;
+		pref = n;
+		pref[num].op_class = op_class;
+		pref[num].chan = chan;
+		num++;
+
+		pos = os_strchr(pos2, ',');
+		if (pos == NULL)
+			break;
+		pos++;
+	}
+
+	os_free(config->p2p_pref_chan);
+	config->p2p_pref_chan = pref;
+	config->num_p2p_pref_chan = num;
+	wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs",
+		    (u8 *) config->p2p_pref_chan,
+		    config->num_p2p_pref_chan * sizeof(struct p2p_channel));
+
+	return 0;
+
+fail:
+	os_free(pref);
+	wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
+	return -1;
+}
 #endif /* CONFIG_P2P */
 
 
@@ -2582,6 +2951,7 @@
 #define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f)
 #define STR(f) _STR(f), NULL, NULL
 #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
+#define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL
 
 static const struct global_parse_data global_fields[] = {
 #ifdef CONFIG_CTRL_IFACE
@@ -2590,10 +2960,13 @@
 #endif /* CONFIG_CTRL_IFACE */
 	{ INT_RANGE(eapol_version, 1, 2), 0 },
 	{ INT(ap_scan), 0 },
+	{ INT(disable_scan_offload), 0 },
 	{ INT(fast_reauth), 0 },
 	{ STR(opensc_engine_path), 0 },
 	{ STR(pkcs11_engine_path), 0 },
 	{ STR(pkcs11_module_path), 0 },
+	{ STR(pcsc_reader), 0 },
+	{ STR(pcsc_pin), 0 },
 	{ STR(driver_param), 0 },
 	{ INT(dot11RSNAConfigPMKLifetime), 0 },
 	{ INT(dot11RSNAConfigPMKReauthThreshold), 0 },
@@ -2613,6 +2986,7 @@
 	{ FUNC(os_version), CFG_CHANGED_OS_VERSION },
 	{ STR(config_methods), CFG_CHANGED_CONFIG_METHODS },
 	{ INT_RANGE(wps_cred_processing, 0, 2), 0 },
+	{ FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION },
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
 	{ FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
@@ -2625,26 +2999,31 @@
 	{ INT_RANGE(persistent_reconnect, 0, 1), 0 },
 	{ INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
 	{ INT(p2p_group_idle), 0 },
+	{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
 #endif /* CONFIG_P2P */
-#ifdef ANDROID_P2P
-	{ STR_RANGE(prioritize, 0, 32), CFG_CHANGED_IFACE_PRIORITY },
-#endif
 	{ FUNC(country), CFG_CHANGED_COUNTRY },
 	{ INT(bss_max_count), 0 },
 	{ INT(bss_expiration_age), 0 },
 	{ INT(bss_expiration_scan_count), 0 },
 	{ INT_RANGE(filter_ssids, 0, 1), 0 },
+	{ INT_RANGE(filter_rssi, -100, 0), 0 },
 	{ INT(max_num_sta), 0 },
 	{ INT_RANGE(disassoc_low_ack, 0, 1), 0 },
-	{ STR(home_realm), 0 },
-	{ STR(home_username), 0 },
-	{ STR(home_password), 0 },
-	{ STR(home_ca_cert), 0 },
-	{ STR(home_imsi), 0 },
-	{ STR(home_milenage), 0 },
+#ifdef CONFIG_HS20
+	{ INT_RANGE(hs20, 0, 1), 0 },
+#endif /* CONFIG_HS20 */
 	{ INT_RANGE(interworking, 0, 1), 0 },
 	{ FUNC(hessid), 0 },
-	{ INT_RANGE(access_network_type, 0, 15), 0 }
+	{ INT_RANGE(access_network_type, 0, 15), 0 },
+	{ INT_RANGE(pbc_in_m1, 0, 1), 0 },
+	{ STR(autoscan), 0 },
+	{ INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff), 0 },
+	{ BIN(wps_nfc_dh_pubkey), 0 },
+	{ BIN(wps_nfc_dh_privkey), 0 },
+	{ BIN(wps_nfc_dev_pw), 0 },
+	{ STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND },
+	{ INT(p2p_go_max_inactivity), 0 },
+	{ INT_RANGE(auto_interworking, 0, 1), 0 },
 };
 
 #undef FUNC
@@ -2654,6 +3033,7 @@
 #undef _STR
 #undef STR
 #undef STR_RANGE
+#undef BIN
 #define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
 
 
@@ -2678,6 +3058,25 @@
 		break;
 	}
 	if (i == NUM_GLOBAL_FIELDS) {
+#ifdef CONFIG_AP
+		if (os_strncmp(pos, "wmm_ac_", 7) == 0) {
+			char *tmp = os_strchr(pos, '=');
+			if (tmp == NULL) {
+				if (line < 0)
+					return -1;
+				wpa_printf(MSG_ERROR, "Line %d: invalid line "
+					   "'%s'", line, pos);
+				return -1;
+			}
+			*tmp++ = '\0';
+			if (hostapd_config_wmm_ac(config->wmm_ac_params, pos,
+						  tmp)) {
+				wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
+					   "AC item", line);
+				return -1;
+			}
+		}
+#endif /* CONFIG_AP */
 		if (line < 0)
 			return -1;
 		wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 432a5d4..b889ab8 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Configuration file structures
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,6 +18,7 @@
 #define DEFAULT_FAST_REAUTH 1
 #define DEFAULT_P2P_GO_INTENT 7
 #define DEFAULT_P2P_INTRA_BSS 1
+#define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60)
 #define DEFAULT_BSS_MAX_COUNT 200
 #define DEFAULT_BSS_EXPIRATION_AGE 180
 #define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2
@@ -26,6 +27,176 @@
 
 #include "config_ssid.h"
 #include "wps/wps.h"
+#include "common/ieee802_11_common.h"
+
+
+struct wpa_cred {
+	/**
+	 * next - Next credential in the list
+	 *
+	 * This pointer can be used to iterate over all credentials. The head
+	 * of this list is stored in the cred field of struct wpa_config.
+	 */
+	struct wpa_cred *next;
+
+	/**
+	 * id - Unique id for the credential
+	 *
+	 * This identifier is used as a unique identifier for each credential
+	 * block when using the control interface. Each credential is allocated
+	 * an id when it is being created, either when reading the
+	 * configuration file or when a new credential is added through the
+	 * control interface.
+	 */
+	int id;
+
+	/**
+	 * priority - Priority group
+	 *
+	 * By default, all networks and credentials get the same priority group
+	 * (0). This field can be used to give higher priority for credentials
+	 * (and similarly in struct wpa_ssid for network blocks) to change the
+	 * Interworking automatic networking selection behavior. The matching
+	 * network (based on either an enabled network block or a credential)
+	 * with the highest priority value will be selected.
+	 */
+	int priority;
+
+	/**
+	 * pcsc - Use PC/SC and SIM/USIM card
+	 */
+	int pcsc;
+
+	/**
+	 * realm - Home Realm for Interworking
+	 */
+	char *realm;
+
+	/**
+	 * username - Username for Interworking network selection
+	 */
+	char *username;
+
+	/**
+	 * password - Password for Interworking network selection
+	 */
+	char *password;
+
+	/**
+	 * ext_password - Whether password is a name for external storage
+	 */
+	int ext_password;
+
+	/**
+	 * ca_cert - CA certificate for Interworking network selection
+	 */
+	char *ca_cert;
+
+	/**
+	 * client_cert - File path to client certificate file (PEM/DER)
+	 *
+	 * This field is used with Interworking networking selection for a case
+	 * where client certificate/private key is used for authentication
+	 * (EAP-TLS). Full path to the file should be used since working
+	 * directory may change when wpa_supplicant is run in the background.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	char *client_cert;
+
+	/**
+	 * private_key - File path to client private key file (PEM/DER/PFX)
+	 *
+	 * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+	 * commented out. Both the private key and certificate will be read
+	 * from the PKCS#12 file in this case. Full path to the file should be
+	 * used since working directory may change when wpa_supplicant is run
+	 * in the background.
+	 *
+	 * Windows certificate store can be used by leaving client_cert out and
+	 * configuring private_key in one of the following formats:
+	 *
+	 * cert://substring_to_match
+	 *
+	 * hash://certificate_thumbprint_in_hex
+	 *
+	 * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+	 *
+	 * Note that when running wpa_supplicant as an application, the user
+	 * certificate store (My user account) is used, whereas computer store
+	 * (Computer account) is used when running wpasvc as a service.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	char *private_key;
+
+	/**
+	 * private_key_passwd - Password for private key file
+	 */
+	char *private_key_passwd;
+
+	/**
+	 * imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+	 */
+	char *imsi;
+
+	/**
+	 * milenage - Milenage parameters for SIM/USIM simulator in
+	 *	<Ki>:<OPc>:<SQN> format
+	 */
+	char *milenage;
+
+	/**
+	 * domain - Home service provider FQDN
+	 *
+	 * This is used to compare against the Domain Name List to figure out
+	 * whether the AP is operated by the Home SP.
+	 */
+	char *domain;
+
+	/**
+	 * roaming_consortium - Roaming Consortium OI
+	 *
+	 * If roaming_consortium_len is non-zero, this field contains the
+	 * Roaming Consortium OI that can be used to determine which access
+	 * points support authentication with this credential. This is an
+	 * alternative to the use of the realm parameter. When using Roaming
+	 * Consortium to match the network, the EAP parameters need to be
+	 * pre-configured with the credential since the NAI Realm information
+	 * may not be available or fetched.
+	 */
+	u8 roaming_consortium[15];
+
+	/**
+	 * roaming_consortium_len - Length of roaming_consortium
+	 */
+	size_t roaming_consortium_len;
+
+	/**
+	 * eap_method - EAP method to use
+	 *
+	 * Pre-configured EAP method to use with this credential or %NULL to
+	 * indicate no EAP method is selected, i.e., the method will be
+	 * selected automatically based on ANQP information.
+	 */
+	struct eap_method_type *eap_method;
+
+	/**
+	 * phase1 - Phase 1 (outer authentication) parameters
+	 *
+	 * Pre-configured EAP parameters or %NULL.
+	 */
+	char *phase1;
+
+	/**
+	 * phase2 - Phase 2 (inner authentication) parameters
+	 *
+	 * Pre-configured EAP parameters or %NULL.
+	 */
+	char *phase2;
+};
 
 
 #define CFG_CHANGED_DEVICE_NAME BIT(0)
@@ -41,9 +212,8 @@
 #define CFG_CHANGED_VENDOR_EXTENSION BIT(10)
 #define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11)
 #define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12)
-#ifdef ANDROID_P2P
-#define CFG_CHANGED_IFACE_PRIORITY BIT(13)
-#endif
+#define CFG_CHANGED_P2P_PREF_CHAN BIT(13)
+#define CFG_CHANGED_EXT_PW_BACKEND BIT(14)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -75,6 +245,13 @@
 	int num_prio;
 
 	/**
+	 * cred - Head of the credential list
+	 *
+	 * This is the head for the list of all the configured credentials.
+	 */
+	struct wpa_cred *cred;
+
+	/**
 	 * eapol_version - IEEE 802.1X/EAPOL version number
 	 *
 	 * wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which
@@ -116,6 +293,15 @@
 	int ap_scan;
 
 	/**
+	 * disable_scan_offload - Disable automatic offloading of scan requests
+	 *
+	 * By default, %wpa_supplicant tries to offload scanning if the driver
+	 * indicates support for this (sched_scan). This configuration
+	 * parameter can be used to disable this offloading mechanism.
+	 */
+	int disable_scan_offload;
+
+	/**
 	 * ctrl_interface - Parameters for the control interface
 	 *
 	 * If this is specified, %wpa_supplicant will open a control interface
@@ -214,6 +400,23 @@
 	char *pkcs11_module_path;
 
 	/**
+	 * pcsc_reader - PC/SC reader name prefix
+	 *
+	 * If not %NULL, PC/SC reader with a name that matches this prefix is
+	 * initialized for SIM/USIM access. Empty string can be used to match
+	 * the first available reader.
+	 */
+	char *pcsc_reader;
+
+	/**
+	 * pcsc_pin - PIN for USIM, GSM SIM, and smartcards
+	 *
+	 * This field is used to configure PIN for SIM/USIM for EAP-SIM and
+	 * EAP-AKA. If left out, this will be asked through control interface.
+	 */
+	char *pcsc_pin;
+
+	/**
 	 * driver_param - Driver interface parameters
 	 *
 	 * This text string is passed to the selected driver interface with the
@@ -359,6 +562,10 @@
 	char *p2p_ssid_postfix;
 	int persistent_reconnect;
 	int p2p_intra_bss;
+	unsigned int num_p2p_pref_chan;
+	struct p2p_channel *p2p_pref_chan;
+
+	struct wpabuf *wps_vendor_ext_m1;
 
 #define MAX_WPS_VENDOR_EXT 10
 	/**
@@ -377,9 +584,12 @@
 	 * state indefinitely until explicitly removed. As a P2P client, the
 	 * maximum idle time of P2P_MAX_CLIENT_IDLE seconds is enforced, i.e.,
 	 * this parameter is mainly meant for GO use and for P2P client, it can
-	 * only be used to reduce the default timeout to smaller value.
+	 * only be used to reduce the default timeout to smaller value. A
+	 * special value -1 can be used to configure immediate removal of the
+	 * group for P2P client role on any disconnection after the data
+	 * connection has been established.
 	 */
-	unsigned int p2p_group_idle;
+	int p2p_group_idle;
 
 	/**
 	 * bss_max_count - Maximum number of BSS entries to keep in memory
@@ -414,6 +624,14 @@
 	int filter_ssids;
 
 	/**
+	 * filter_rssi - RSSI-based scan result filtering
+	 *
+	 * 0 = do not filter scan results
+	 * -n = filter scan results below -n dBm
+	 */
+	int filter_rssi;
+
+	/**
 	 * max_num_sta - Maximum number of STAs in an AP/P2P GO
 	 */
 	unsigned int max_num_sta;
@@ -452,44 +670,83 @@
 	u8 hessid[ETH_ALEN];
 
 	/**
-	 * home_realm - Home Realm for Interworking
+	 * hs20 - Hotspot 2.0
 	 */
-	char *home_realm;
+	int hs20;
 
 	/**
-	 * home_username - Username for Interworking network selection
+	 * pbc_in_m1 - AP mode WPS probing workaround for PBC with Windows 7
+	 *
+	 * Windows 7 uses incorrect way of figuring out AP's WPS capabilities
+	 * by acting as a Registrar and using M1 from the AP. The config
+	 * methods attribute in that message is supposed to indicate only the
+	 * configuration method supported by the AP in Enrollee role, i.e., to
+	 * add an external Registrar. For that case, PBC shall not be used and
+	 * as such, the PushButton config method is removed from M1 by default.
+	 * If pbc_in_m1=1 is included in the configuration file, the PushButton
+	 * config method is left in M1 (if included in config_methods
+	 * parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from
+	 * a label in the AP).
 	 */
-	char *home_username;
+	int pbc_in_m1;
 
 	/**
-	 * home_password - Password for Interworking network selection
+	 * autoscan - Automatic scan parameters or %NULL if none
+	 *
+	 * This is an optional set of parameters for automatic scanning
+	 * within an interface in following format:
+	 * <autoscan module name>:<module parameters>
 	 */
-	char *home_password;
+	char *autoscan;
 
 	/**
-	 * home_ca_cert - CA certificate for Interworking network selection
+	 * wps_nfc_dev_pw_id - NFC Device Password ID for password token
 	 */
-	char *home_ca_cert;
+	int wps_nfc_dev_pw_id;
 
 	/**
-	 * home_imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+	 * wps_nfc_dh_pubkey - NFC DH Public Key for password token
 	 */
-	char *home_imsi;
+	struct wpabuf *wps_nfc_dh_pubkey;
 
 	/**
-	 * home_milenage - Milenage parameters for SIM/USIM simulator in
-	 *	<Ki>:<OPc>:<SQN> format
+	 * wps_nfc_dh_pubkey - NFC DH Private Key for password token
 	 */
-	char *home_milenage;
-#ifdef ANDROID_P2P
+	struct wpabuf *wps_nfc_dh_privkey;
+
 	/**
-	 * prioritize - Prioritize an Interface
-	 * Interface name of the interface that needs to be proritized; Useful
-	 * for resolving conflicts in connection. up to 16 octets encoded in
-	 * UTF-8
+	 * wps_nfc_dh_pubkey - NFC Device Password for password token
 	 */
-	char *prioritize;
-#endif
+	struct wpabuf *wps_nfc_dev_pw;
+
+	/**
+	 * ext_password_backend - External password backend or %NULL if none
+	 *
+	 * format: <backend name>[:<optional backend parameters>]
+	 */
+	char *ext_password_backend;
+
+	/*
+	 * p2p_go_max_inactivity - Timeout in seconds to detect STA inactivity
+	 *
+	 * This timeout value is used in P2P GO mode to clean up
+	 * inactive stations.
+	 * By default: 300 seconds.
+	 */
+	int p2p_go_max_inactivity;
+
+	struct hostapd_wmm_ac_params wmm_ac_params[4];
+
+	/**
+	 * auto_interworking - Whether to use network selection automatically
+	 *
+	 * 0 = do not automatically go through Interworking network selection
+	 *     (i.e., require explicit interworking_select command for this)
+	 * 1 = perform Interworking network selection if one or more
+	 *     credentials have been configured and scan did not find a
+	 *     matching network block
+	 */
+	int auto_interworking;
 };
 
 
@@ -522,6 +779,13 @@
 void wpa_config_free_blob(struct wpa_config_blob *blob);
 int wpa_config_remove_blob(struct wpa_config *config, const char *name);
 
+struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id);
+struct wpa_cred * wpa_config_add_cred(struct wpa_config *config);
+int wpa_config_remove_cred(struct wpa_config *config, int id);
+void wpa_config_free_cred(struct wpa_cred *cred);
+int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
+			const char *value, int line);
+
 struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
 					   const char *driver_param);
 #ifndef CONFIG_NO_STDOUT_DEBUG
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index a1955d4..531957a 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Configuration backend: text file
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -16,6 +16,30 @@
 #include "config.h"
 #include "base64.h"
 #include "uuid.h"
+#include "p2p/p2p.h"
+
+
+static int newline_terminated(const char *buf, size_t buflen)
+{
+	size_t len = os_strlen(buf);
+	if (len == 0)
+		return 0;
+	if (len == buflen - 1 && buf[buflen - 1] != '\r' &&
+	    buf[len - 1] != '\n')
+		return 0;
+	return 1;
+}
+
+
+static void skip_line_end(FILE *stream)
+{
+	char buf[100];
+	while (fgets(buf, sizeof(buf), stream)) {
+		buf[sizeof(buf) - 1] = '\0';
+		if (newline_terminated(buf, sizeof(buf)))
+			return;
+	}
+}
 
 
 /**
@@ -40,6 +64,15 @@
 	while (fgets(s, size, stream)) {
 		(*line)++;
 		s[size - 1] = '\0';
+		if (!newline_terminated(s, size)) {
+			/*
+			 * The line was truncated - skip rest of it to avoid
+			 * confusing error messages.
+			 */
+			wpa_printf(MSG_INFO, "Long line in configuration file "
+				   "truncated");
+			skip_line_end(stream);
+		}
 		pos = s;
 
 		/* Skip white space from the beginning of line. */
@@ -98,12 +131,6 @@
 		wpa_config_update_psk(ssid);
 	}
 
-	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set) {
-		wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key "
-			   "management, but no PSK configured.", line);
-		errors++;
-	}
-
 	if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
 	    !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
 	    !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
@@ -122,7 +149,7 @@
 {
 	struct wpa_ssid *ssid;
 	int errors = 0, end = 0;
-	char buf[256], *pos, *pos2;
+	char buf[2000], *pos, *pos2;
 
 	wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block",
 		   *line);
@@ -178,6 +205,61 @@
 }
 
 
+static struct wpa_cred * wpa_config_read_cred(FILE *f, int *line, int id)
+{
+	struct wpa_cred *cred;
+	int errors = 0, end = 0;
+	char buf[256], *pos, *pos2;
+
+	wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new cred block", *line);
+	cred = os_zalloc(sizeof(*cred));
+	if (cred == NULL)
+		return NULL;
+	cred->id = id;
+
+	while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
+		if (os_strcmp(pos, "}") == 0) {
+			end = 1;
+			break;
+		}
+
+		pos2 = os_strchr(pos, '=');
+		if (pos2 == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid cred line "
+				   "'%s'.", *line, pos);
+			errors++;
+			continue;
+		}
+
+		*pos2++ = '\0';
+		if (*pos2 == '"') {
+			if (os_strchr(pos2 + 1, '"') == NULL) {
+				wpa_printf(MSG_ERROR, "Line %d: invalid "
+					   "quotation '%s'.", *line, pos2);
+				errors++;
+				continue;
+			}
+		}
+
+		if (wpa_config_set_cred(cred, pos, pos2, *line) < 0)
+			errors++;
+	}
+
+	if (!end) {
+		wpa_printf(MSG_ERROR, "Line %d: cred block was not "
+			   "terminated properly.", *line);
+		errors++;
+	}
+
+	if (errors) {
+		wpa_config_free_cred(cred);
+		cred = NULL;
+	}
+
+	return cred;
+}
+
+
 #ifndef CONFIG_NO_CONFIG_BLOBS
 static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line,
 						     const char *name)
@@ -264,18 +346,26 @@
 struct wpa_config * wpa_config_read(const char *name)
 {
 	FILE *f;
-	char buf[256], *pos;
+	char buf[512], *pos;
 	int errors = 0, line = 0;
 	struct wpa_ssid *ssid, *tail = NULL, *head = NULL;
+	struct wpa_cred *cred, *cred_tail = NULL, *cred_head = NULL;
 	struct wpa_config *config;
 	int id = 0;
+	int cred_id = 0;
 
 	config = wpa_config_alloc_empty(NULL, NULL);
-	if (config == NULL)
+	if (config == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to allocate config file "
+			   "structure");
 		return NULL;
+	}
+
 	wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
 	f = fopen(name, "r");
 	if (f == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
+			   "error: %s", name, strerror(errno));
 		os_free(config);
 		return NULL;
 	}
@@ -302,10 +392,26 @@
 				errors++;
 				continue;
 			}
+		} else if (os_strcmp(pos, "cred={") == 0) {
+			cred = wpa_config_read_cred(f, &line, cred_id++);
+			if (cred == NULL) {
+				wpa_printf(MSG_ERROR, "Line %d: failed to "
+					   "parse cred block.", line);
+				errors++;
+				continue;
+			}
+			if (cred_head == NULL) {
+				cred_head = cred_tail = cred;
+			} else {
+				cred_tail->next = cred;
+				cred_tail = cred;
+			}
 #ifndef CONFIG_NO_CONFIG_BLOBS
 		} else if (os_strncmp(pos, "blob-base64-", 12) == 0) {
 			if (wpa_config_process_blob(config, f, &line, pos + 12)
 			    < 0) {
+				wpa_printf(MSG_ERROR, "Line %d: failed to "
+					   "process blob.", line);
 				errors++;
 				continue;
 			}
@@ -322,6 +428,7 @@
 
 	config->ssid = head;
 	wpa_config_debug_dump_networks(config);
+	config->cred = cred_head;
 
 #ifndef WPA_IGNORE_CONFIG_ERRORS
 	if (errors) {
@@ -515,9 +622,12 @@
 	write_psk(f, ssid);
 	write_proto(f, ssid);
 	write_key_mgmt(f, ssid);
+	INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
 	write_pairwise(f, ssid);
 	write_group(f, ssid);
 	write_auth_alg(f, ssid);
+	STR(bgscan);
+	STR(autoscan);
 #ifdef IEEE8021X_EAPOL
 	write_eap(f, ssid);
 	STR(identity);
@@ -583,6 +693,29 @@
 }
 
 
+static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
+{
+	if (cred->priority)
+		fprintf(f, "\tpriority=%d\n", cred->priority);
+	if (cred->pcsc)
+		fprintf(f, "\tpcsc=%d\n", cred->pcsc);
+	if (cred->realm)
+		fprintf(f, "\trealm=\"%s\"\n", cred->realm);
+	if (cred->username)
+		fprintf(f, "\tusername=\"%s\"\n", cred->username);
+	if (cred->password)
+		fprintf(f, "\tpassword=\"%s\"\n", cred->password);
+	if (cred->ca_cert)
+		fprintf(f, "\tca_cert=\"%s\"\n", cred->ca_cert);
+	if (cred->imsi)
+		fprintf(f, "\timsi=\"%s\"\n", cred->imsi);
+	if (cred->milenage)
+		fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage);
+	if (cred->domain)
+		fprintf(f, "\tdomain=\"%s\"\n", cred->domain);
+}
+
+
 #ifndef CONFIG_NO_CONFIG_BLOBS
 static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob)
 {
@@ -599,6 +732,23 @@
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 
 
+static void write_global_bin(FILE *f, const char *field,
+			     const struct wpabuf *val)
+{
+	size_t i;
+	const u8 *pos;
+
+	if (val == NULL)
+		return;
+
+	fprintf(f, "%s=", field);
+	pos = wpabuf_head(val);
+	for (i = 0; i < wpabuf_len(val); i++)
+		fprintf(f, "%02X", *pos++);
+	fprintf(f, "\n");
+}
+
+
 static void wpa_config_write_global(FILE *f, struct wpa_config *config)
 {
 #ifdef CONFIG_CTRL_IFACE
@@ -612,6 +762,9 @@
 		fprintf(f, "eapol_version=%d\n", config->eapol_version);
 	if (config->ap_scan != DEFAULT_AP_SCAN)
 		fprintf(f, "ap_scan=%d\n", config->ap_scan);
+	if (config->disable_scan_offload)
+		fprintf(f, "disable_scan_offload=%d\n",
+			config->disable_scan_offload);
 	if (config->fast_reauth != DEFAULT_FAST_REAUTH)
 		fprintf(f, "fast_reauth=%d\n", config->fast_reauth);
 	if (config->opensc_engine_path)
@@ -623,6 +776,10 @@
 	if (config->pkcs11_module_path)
 		fprintf(f, "pkcs11_module_path=%s\n",
 			config->pkcs11_module_path);
+	if (config->pcsc_reader)
+		fprintf(f, "pcsc_reader=%s\n", config->pcsc_reader);
+	if (config->pcsc_pin)
+		fprintf(f, "pcsc_pin=%s\n", config->pcsc_pin);
 	if (config->driver_param)
 		fprintf(f, "driver_param=%s\n", config->driver_param);
 	if (config->dot11RSNAConfigPMKLifetime)
@@ -667,6 +824,16 @@
 	if (config->wps_cred_processing)
 		fprintf(f, "wps_cred_processing=%d\n",
 			config->wps_cred_processing);
+	if (config->wps_vendor_ext_m1) {
+		int i, len = wpabuf_len(config->wps_vendor_ext_m1);
+		const u8 *p = wpabuf_head_u8(config->wps_vendor_ext_m1);
+		if (len > 0) {
+			fprintf(f, "wps_vendor_ext_m1=");
+			for (i = 0; i < len; i++)
+				fprintf(f, "%02x", *p++);
+			fprintf(f, "\n");
+		}
+	}
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
 	if (config->p2p_listen_reg_class)
@@ -691,6 +858,16 @@
 		fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
 	if (config->p2p_group_idle)
 		fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
+	if (config->p2p_pref_chan) {
+		unsigned int i;
+		fprintf(f, "p2p_pref_chan=");
+		for (i = 0; i < config->num_p2p_pref_chan; i++) {
+			fprintf(f, "%s%u:%u", i > 0 ? "," : "",
+				config->p2p_pref_chan[i].op_class,
+				config->p2p_pref_chan[i].chan);
+		}
+		fprintf(f, "\n");
+	}
 #endif /* CONFIG_P2P */
 	if (config->country[0] && config->country[1]) {
 		fprintf(f, "country=%c%c\n",
@@ -711,19 +888,11 @@
 		fprintf(f, "max_num_sta=%u\n", config->max_num_sta);
 	if (config->disassoc_low_ack)
 		fprintf(f, "disassoc_low_ack=%u\n", config->disassoc_low_ack);
+#ifdef CONFIG_HS20
+	if (config->hs20)
+		fprintf(f, "hs20=1\n");
+#endif /* CONFIG_HS20 */
 #ifdef CONFIG_INTERWORKING
-	if (config->home_realm)
-		fprintf(f, "home_realm=%s\n", config->home_realm);
-	if (config->home_username)
-		fprintf(f, "home_username=%s\n", config->home_username);
-	if (config->home_password)
-		fprintf(f, "home_password=%s\n", config->home_password);
-	if (config->home_ca_cert)
-		fprintf(f, "home_ca_cert=%s\n", config->home_ca_cert);
-	if (config->home_imsi)
-		fprintf(f, "home_imsi=%s\n", config->home_imsi);
-	if (config->home_milenage)
-		fprintf(f, "home_milenage=%s\n", config->home_milenage);
 	if (config->interworking)
 		fprintf(f, "interworking=%u\n", config->interworking);
 	if (!is_zero_ether_addr(config->hessid))
@@ -732,6 +901,24 @@
 		fprintf(f, "access_network_type=%d\n",
 			config->access_network_type);
 #endif /* CONFIG_INTERWORKING */
+	if (config->pbc_in_m1)
+		fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1);
+	if (config->wps_nfc_dev_pw_id)
+		fprintf(f, "wps_nfc_dev_pw_id=%d\n",
+			config->wps_nfc_dev_pw_id);
+	write_global_bin(f, "wps_nfc_dh_pubkey", config->wps_nfc_dh_pubkey);
+	write_global_bin(f, "wps_nfc_dh_privkey", config->wps_nfc_dh_privkey);
+	write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw);
+
+	if (config->ext_password_backend)
+		fprintf(f, "ext_password_backend=%s\n",
+			config->ext_password_backend);
+	if (config->p2p_go_max_inactivity != DEFAULT_P2P_GO_MAX_INACTIVITY)
+		fprintf(f, "p2p_go_max_inactivity=%d\n",
+			config->p2p_go_max_inactivity);
+	if (config->auto_interworking)
+		fprintf(f, "auto_interworking=%d\n",
+			config->auto_interworking);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
@@ -742,6 +929,7 @@
 #ifndef CONFIG_NO_CONFIG_WRITE
 	FILE *f;
 	struct wpa_ssid *ssid;
+	struct wpa_cred *cred;
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	struct wpa_config_blob *blob;
 #endif /* CONFIG_NO_CONFIG_BLOBS */
@@ -757,6 +945,12 @@
 
 	wpa_config_write_global(f, config);
 
+	for (cred = config->cred; cred; cred = cred->next) {
+		fprintf(f, "\ncred={\n");
+		wpa_config_write_cred(f, cred);
+		fprintf(f, "}\n");
+	}
+
 	for (ssid = config->ssid; ssid; ssid = ssid->next) {
 		if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary)
 			continue; /* do not save temporary networks */
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 45a423c..ff97379 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -25,6 +25,7 @@
 		       WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)
 #define DEFAULT_FRAGMENT_SIZE 1398
 
+#define DEFAULT_BG_SCAN_PERIOD -1
 #define DEFAULT_DISABLE_HT 0
 #define DEFAULT_DISABLE_HT40 0
 #define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */
@@ -140,6 +141,14 @@
 	char *passphrase;
 
 	/**
+	 * ext_psk - PSK/passphrase name in external storage
+	 *
+	 * If this is set, PSK/passphrase will be fetched from external storage
+	 * when requesting association with the network.
+	 */
+	char *ext_psk;
+
+	/**
 	 * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_*
 	 */
 	int pairwise_cipher;
@@ -157,6 +166,12 @@
 	int key_mgmt;
 
 	/**
+	 * bg_scan_period - Background scan period in seconds, 0 to disable, or
+	 * -1 to indicate no change to default driver configuration
+	 */
+	int bg_scan_period;
+
+	/**
 	 * proto - Bitfield of allowed protocols, WPA_PROTO_*
 	 */
 	int proto;
@@ -349,6 +364,8 @@
 	 */
 	int frequency;
 
+	int ht40;
+
 	/**
 	 * wpa_ptk_rekey - Maximum lifetime for PTK in seconds
 	 *
@@ -377,6 +394,20 @@
 	char *bgscan;
 
 	/**
+	 * ignore_broadcast_ssid - Hide SSID in AP mode
+	 *
+	 * Send empty SSID in beacons and ignore probe request frames that do
+	 * not specify full SSID, i.e., require stations to know SSID.
+	 * default: disabled (0)
+	 * 1 = send empty (length=0) SSID in beacon and ignore probe request
+	 * for broadcast SSID
+	 * 2 = clear SSID (ASCII 0), but keep the original length (this may be
+	 * required with some clients that do not support empty SSID) and
+	 * ignore probe requests for broadcast SSID
+	 */
+	int ignore_broadcast_ssid;
+
+	/**
 	 * freq_list - Array of allowed frequencies or %NULL for all
 	 *
 	 * This is an optional zero-terminated array of frequencies in
@@ -400,6 +431,10 @@
 	 */
 	size_t num_p2p_clients;
 
+#ifndef P2P_MAX_STORED_CLIENTS
+#define P2P_MAX_STORED_CLIENTS 100
+#endif /* P2P_MAX_STORED_CLIENTS */
+
 	/**
 	 * p2p_group - Network generated as a P2P group (used internally)
 	 */
@@ -477,6 +512,30 @@
 	 */
 	char *ht_mcs;
 #endif /* CONFIG_HT_OVERRIDES */
+
+	/**
+	 * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
+	 *
+	 * This timeout value is used in AP mode to clean up inactive stations.
+	 * By default: 300 seconds.
+	 */
+	int ap_max_inactivity;
+
+	/**
+	 * dtim_period - DTIM period in Beacon intervals
+	 * By default: 2
+	 */
+	int dtim_period;
+
+	/**
+	 * auth_failures - Number of consecutive authentication failures
+	 */
+	unsigned int auth_failures;
+
+	/**
+	 * disabled_until - Network block disabled until this time if non-zero
+	 */
+	struct os_time disabled_until;
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index de2ec58..6d9876c 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -344,13 +344,6 @@
 		wpa_config_update_psk(ssid);
 	}
 
-	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set) {
-		wpa_printf(MSG_ERROR, "WPA-PSK accepted for key management, "
-			   "but no PSK configured for network '" TSTR "'.",
-			   netw);
-		errors++;
-	}
-
 	if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
 	    !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
 	    !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 5d8409c..bcf27be 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Control interface (shared code for all backends)
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,6 +12,7 @@
 #include "utils/eloop.h"
 #include "common/version.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "eap_peer/eap.h"
 #include "eapol_supp/eapol_supp_sm.h"
@@ -28,6 +29,8 @@
 #include "ap.h"
 #include "p2p_supplicant.h"
 #include "p2p/p2p.h"
+#include "hs20_supplicant.h"
+#include "wifi_display.h"
 #include "notify.h"
 #include "bss.h"
 #include "scan.h"
@@ -35,6 +38,7 @@
 #include "interworking.h"
 #include "blacklist.h"
 #include "wpas_glue.h"
+#include "autoscan.h"
 
 extern struct wpa_driver_ops *wpa_drivers[];
 
@@ -59,7 +63,7 @@
 	num_ssid = 0;
 	ssid = wpa_s->conf->ssid;
 	while (ssid) {
-		if (!ssid->disabled)
+		if (!wpas_network_disabled(wpa_s, ssid))
 			num_ssid++;
 		ssid = ssid->next;
 	}
@@ -81,7 +85,7 @@
 	i = 0;
 	ssid = wpa_s->conf->ssid;
 	while (ssid) {
-		if (!ssid->disabled) {
+		if (!wpas_network_disabled(wpa_s, ssid)) {
 			params.ssids[i].ssid = ssid->ssid;
 			params.ssids[i].ssid_len = ssid->ssid_len;
 			params.num_ssids++;
@@ -96,6 +100,9 @@
 		ssid = ssid->next;
 	}
 
+	if (wpa_s->conf->filter_rssi)
+		params.filter_rssi = wpa_s->conf->filter_rssi;
+
 	ret = wpa_drv_sched_scan(wpa_s, &params, 10 * 1000);
 	os_free(params.filter_ssids);
 	if (ret == 0)
@@ -114,6 +121,43 @@
 }
 
 
+static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
+{
+	char *pos;
+	u8 addr[ETH_ALEN], *filter = NULL, *n;
+	size_t count = 0;
+
+	pos = val;
+	while (pos) {
+		if (*pos == '\0')
+			break;
+		if (hwaddr_aton(pos, addr)) {
+			os_free(filter);
+			return -1;
+		}
+		n = os_realloc_array(filter, count + 1, ETH_ALEN);
+		if (n == NULL) {
+			os_free(filter);
+			return -1;
+		}
+		filter = n;
+		os_memcpy(filter + count * ETH_ALEN, addr, ETH_ALEN);
+		count++;
+
+		pos = os_strchr(pos, ' ');
+		if (pos)
+			pos++;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "bssid_filter", filter, count * ETH_ALEN);
+	os_free(wpa_s->bssid_filter);
+	wpa_s->bssid_filter = filter;
+	wpa_s->bssid_filter_count = count;
+
+	return 0;
+}
+
+
 static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
 					 char *cmd)
 {
@@ -197,8 +241,56 @@
 			ret = pno_start(wpa_s);
 		else
 			ret = pno_stop(wpa_s);
+	} else if (os_strcasecmp(cmd, "radio_disabled") == 0) {
+		int disabled = atoi(value);
+		if (wpa_drv_radio_disable(wpa_s, disabled) < 0)
+			ret = -1;
+		else if (disabled)
+			wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+	} else if (os_strcasecmp(cmd, "uapsd") == 0) {
+		if (os_strcmp(value, "disable") == 0)
+			wpa_s->set_sta_uapsd = 0;
+		else {
+			int be, bk, vi, vo;
+			char *pos;
+			/* format: BE,BK,VI,VO;max SP Length */
+			be = atoi(value);
+			pos = os_strchr(value, ',');
+			if (pos == NULL)
+				return -1;
+			pos++;
+			bk = atoi(pos);
+			pos = os_strchr(pos, ',');
+			if (pos == NULL)
+				return -1;
+			pos++;
+			vi = atoi(pos);
+			pos = os_strchr(pos, ',');
+			if (pos == NULL)
+				return -1;
+			pos++;
+			vo = atoi(pos);
+			/* ignore max SP Length for now */
+
+			wpa_s->set_sta_uapsd = 1;
+			wpa_s->sta_uapsd = 0;
+			if (be)
+				wpa_s->sta_uapsd |= BIT(0);
+			if (bk)
+				wpa_s->sta_uapsd |= BIT(1);
+			if (vi)
+				wpa_s->sta_uapsd |= BIT(2);
+			if (vo)
+				wpa_s->sta_uapsd |= BIT(3);
+		}
 	} else if (os_strcasecmp(cmd, "ps") == 0) {
 		ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1);
+#ifdef CONFIG_WIFI_DISPLAY
+	} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
+		wifi_display_enable(wpa_s->global, !!atoi(value));
+#endif /* CONFIG_WIFI_DISPLAY */
+	} else if (os_strcasecmp(cmd, "bssid_filter") == 0) {
+		ret = set_bssid_filter(wpa_s, value);
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -224,6 +316,14 @@
 			res = os_snprintf(buf, buflen, "%c%c",
 					  wpa_s->conf->country[0],
 					  wpa_s->conf->country[1]);
+#ifdef CONFIG_WIFI_DISPLAY
+	} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
+		res = os_snprintf(buf, buflen, "%d",
+				  wpa_s->global->wifi_display);
+		if (res < 0 || (unsigned int) res >= buflen)
+			return -1;
+		return res;
+#endif /* CONFIG_WIFI_DISPLAY */
 	}
 
 	if (res < 0 || (unsigned int) res >= buflen)
@@ -389,7 +489,7 @@
 	u8 *_p2p_dev_addr = NULL;
 #endif /* CONFIG_AP */
 
-	if (cmd == NULL || os_strcmp(cmd, "any") == 0) {
+	if (cmd == NULL || os_strcmp(cmd, "any") == 0 || cmd[0] == '\0') {
 		_bssid = NULL;
 #ifdef CONFIG_P2P
 	} else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
@@ -539,6 +639,80 @@
 #endif /* CONFIG_WPS_OOB */
 
 
+#ifdef CONFIG_WPS_NFC
+
+static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s,
+					     char *cmd)
+{
+	u8 bssid[ETH_ALEN], *_bssid = bssid;
+
+	if (cmd == NULL || cmd[0] == '\0')
+		_bssid = NULL;
+	else if (hwaddr_aton(cmd, bssid))
+		return -1;
+
+	return wpas_wps_start_nfc(wpa_s, _bssid);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_nfc_token(
+	struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+	int ndef;
+	struct wpabuf *buf;
+	int res;
+
+	if (os_strcmp(cmd, "WPS") == 0)
+		ndef = 0;
+	else if (os_strcmp(cmd, "NDEF") == 0)
+		ndef = 1;
+	else
+		return -1;
+
+	buf = wpas_wps_nfc_token(wpa_s, ndef);
+	if (buf == NULL)
+		return -1;
+
+	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+					 wpabuf_len(buf));
+	reply[res++] = '\n';
+	reply[res] = '\0';
+
+	wpabuf_free(buf);
+
+	return res;
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
+	struct wpa_supplicant *wpa_s, char *pos)
+{
+	size_t len;
+	struct wpabuf *buf;
+	int ret;
+
+	len = os_strlen(pos);
+	if (len & 0x01)
+		return -1;
+	len /= 2;
+
+	buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return -1;
+	if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
+		wpabuf_free(buf);
+		return -1;
+	}
+
+	ret = wpas_wps_nfc_tag_read(wpa_s, buf);
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
 static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
 					     char *cmd)
 {
@@ -735,6 +909,43 @@
 	ap.key_hex = new_key;
 	return wpas_wps_er_config(wpa_s, cmd, pin, &ap);
 }
+
+
+#ifdef CONFIG_WPS_NFC
+static int wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
+	struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+	int ndef;
+	struct wpabuf *buf;
+	int res;
+	char *uuid;
+
+	uuid = os_strchr(cmd, ' ');
+	if (uuid == NULL)
+		return -1;
+	*uuid++ = '\0';
+
+	if (os_strcmp(cmd, "WPS") == 0)
+		ndef = 0;
+	else if (os_strcmp(cmd, "NDEF") == 0)
+		ndef = 1;
+	else
+		return -1;
+
+	buf = wpas_wps_er_nfc_config_token(wpa_s, ndef, uuid);
+	if (buf == NULL)
+		return -1;
+
+	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+					 wpabuf_len(buf));
+	reply[res++] = '\n';
+	reply[res] = '\0';
+
+	wpabuf_free(buf);
+
+	return res;
+}
+#endif /* CONFIG_WPS_NFC */
 #endif /* CONFIG_WPS_ER */
 
 #endif /* CONFIG_WPS */
@@ -760,78 +971,6 @@
 #endif /* CONFIG_IBSS_RSN */
 
 
-int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
-					      struct wpa_ssid *ssid,
-					      const char *field,
-					      const char *value)
-{
-#ifdef IEEE8021X_EAPOL
-	struct eap_peer_config *eap = &ssid->eap;
-
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field);
-	wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value",
-			      (const u8 *) value, os_strlen(value));
-
-	switch (wpa_supplicant_ctrl_req_from_string(field)) {
-	case WPA_CTRL_REQ_EAP_IDENTITY:
-		os_free(eap->identity);
-		eap->identity = (u8 *) os_strdup(value);
-		eap->identity_len = os_strlen(value);
-		eap->pending_req_identity = 0;
-		if (ssid == wpa_s->current_ssid)
-			wpa_s->reassociate = 1;
-		break;
-	case WPA_CTRL_REQ_EAP_PASSWORD:
-		os_free(eap->password);
-		eap->password = (u8 *) os_strdup(value);
-		eap->password_len = os_strlen(value);
-		eap->pending_req_password = 0;
-		if (ssid == wpa_s->current_ssid)
-			wpa_s->reassociate = 1;
-		break;
-	case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
-		os_free(eap->new_password);
-		eap->new_password = (u8 *) os_strdup(value);
-		eap->new_password_len = os_strlen(value);
-		eap->pending_req_new_password = 0;
-		if (ssid == wpa_s->current_ssid)
-			wpa_s->reassociate = 1;
-		break;
-	case WPA_CTRL_REQ_EAP_PIN:
-		os_free(eap->pin);
-		eap->pin = os_strdup(value);
-		eap->pending_req_pin = 0;
-		if (ssid == wpa_s->current_ssid)
-			wpa_s->reassociate = 1;
-		break;
-	case WPA_CTRL_REQ_EAP_OTP:
-		os_free(eap->otp);
-		eap->otp = (u8 *) os_strdup(value);
-		eap->otp_len = os_strlen(value);
-		os_free(eap->pending_req_otp);
-		eap->pending_req_otp = NULL;
-		eap->pending_req_otp_len = 0;
-		break;
-	case WPA_CTRL_REQ_EAP_PASSPHRASE:
-		os_free(eap->private_key_passwd);
-		eap->private_key_passwd = (u8 *) os_strdup(value);
-		eap->pending_req_passphrase = 0;
-		if (ssid == wpa_s->current_ssid)
-			wpa_s->reassociate = 1;
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
-		return -1;
-	}
-
-	return 0;
-#else /* IEEE8021X_EAPOL */
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included");
-	return -1;
-#endif /* IEEE8021X_EAPOL */
-}
-
-
 static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
 					      char *rsp)
 {
@@ -997,6 +1136,18 @@
 		return pos - buf;
 	pos += ret;
 
+#ifdef CONFIG_HS20
+	if (wpa_s->current_bss &&
+	    wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE) &&
+	    wpa_s->wpa_proto == WPA_PROTO_RSN &&
+	    wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+		ret = os_snprintf(pos, end - pos, "hs20=1\n");
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_HS20 */
+
 	if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 		res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos,
@@ -1014,7 +1165,7 @@
 		     "id=%d state=%d BSSID=" MACSTR " SSID=%s",
 		     wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
 		     wpa_s->wpa_state,
-		     MAC2STR(wpa_s->pending_bssid),
+		     MAC2STR(wpa_s->bssid),
 		     wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
 		     wpa_ssid_txt(wpa_s->current_ssid->ssid,
 		     wpa_s->current_ssid->ssid_len) : "");
@@ -1245,10 +1396,12 @@
 		if (ret < 0 || ret >= end - pos)
 			return pos - buf;
 		pos += ret;
-		ret = os_snprintf(pos, end - pos, "\t%s%s%s",
+		ret = os_snprintf(pos, end - pos, "\t%s%s%s%s",
 				  ssid == wpa_s->current_ssid ?
 				  "[CURRENT]" : "",
 				  ssid->disabled ? "[DISABLED]" : "",
+				  ssid->disabled_until.sec ?
+				  "[TEMP-DISABLED]" : "",
 				  ssid->disabled == 2 ? "[P2P-PERSISTENT]" :
 				  "");
 		if (ret < 0 || ret >= end - pos)
@@ -1309,6 +1462,13 @@
 		pos += ret;
 		first = 0;
 	}
+	if (cipher & WPA_CIPHER_GCMP) {
+		ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : "+");
+		if (ret < 0 || ret >= end - pos)
+			return pos;
+		pos += ret;
+		first = 0;
+	}
 	return pos;
 }
 
@@ -1507,6 +1667,14 @@
 			return -1;
 		pos += ret;
 	}
+#ifdef CONFIG_HS20
+	if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) {
+		ret = os_snprintf(pos, end - pos, "[HS20]");
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+#endif /* CONFIG_HS20 */
 
 	ret = os_snprintf(pos, end - pos, "\t%s",
 			  wpa_ssid_txt(bss->ssid, bss->ssid_len));
@@ -1608,6 +1776,11 @@
 				   "ENABLE_NETWORK with persistent P2P group");
 			return -1;
 		}
+
+		if (os_strstr(cmd, " no-connect")) {
+			ssid->disabled = 0;
+			return 0;
+		}
 	}
 	wpa_supplicant_enable_network(wpa_s, ssid);
 
@@ -1691,6 +1864,9 @@
 		}
 		eapol_sm_invalidate_cached_session(wpa_s->eapol);
 		if (wpa_s->current_ssid) {
+#ifdef CONFIG_SME
+			wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
 			wpa_sm_set_config(wpa_s->wpa, NULL);
 			eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 			wpa_supplicant_disassociate(wpa_s,
@@ -1705,14 +1881,16 @@
 	ssid = wpa_config_get_network(wpa_s->conf, id);
 	if (ssid)
 		wpas_notify_network_removed(wpa_s, ssid);
-	if (ssid == NULL ||
-	    wpa_config_remove_network(wpa_s->conf, id) < 0) {
+	if (ssid == NULL) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
 			   "id=%d", id);
 		return -1;
 	}
 
 	if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) {
+#ifdef CONFIG_SME
+		wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
 		/*
 		 * Invalidate the EAP session cache if the current or
 		 * previously used network is removed.
@@ -1727,6 +1905,12 @@
 		wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 	}
 
+	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the "
+			   "network id=%d", id);
+		return -1;
+	}
+
 	return 0;
 }
 
@@ -1833,6 +2017,132 @@
 }
 
 
+static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s,
+						char *buf, size_t buflen)
+{
+	char *pos, *end;
+	struct wpa_cred *cred;
+	int ret;
+
+	pos = buf;
+	end = buf + buflen;
+	ret = os_snprintf(pos, end - pos,
+			  "cred id / realm / username / domain / imsi\n");
+	if (ret < 0 || ret >= end - pos)
+		return pos - buf;
+	pos += ret;
+
+	cred = wpa_s->conf->cred;
+	while (cred) {
+		ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n",
+				  cred->id, cred->realm ? cred->realm : "",
+				  cred->username ? cred->username : "",
+				  cred->domain ? cred->domain : "",
+				  cred->imsi ? cred->imsi : "");
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+
+		cred = cred->next;
+	}
+
+	return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s,
+					      char *buf, size_t buflen)
+{
+	struct wpa_cred *cred;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_CRED");
+
+	cred = wpa_config_add_cred(wpa_s->conf);
+	if (cred == NULL)
+		return -1;
+
+	ret = os_snprintf(buf, buflen, "%d\n", cred->id);
+	if (ret < 0 || (size_t) ret >= buflen)
+		return -1;
+	return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s,
+						 char *cmd)
+{
+	int id;
+	struct wpa_cred *cred;
+
+	/* cmd: "<cred id>" or "all" */
+	if (os_strcmp(cmd, "all") == 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all");
+		cred = wpa_s->conf->cred;
+		while (cred) {
+			id = cred->id;
+			cred = cred->next;
+			wpa_config_remove_cred(wpa_s->conf, id);
+		}
+		return 0;
+	}
+
+	id = atoi(cmd);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id);
+
+	cred = wpa_config_get_cred(wpa_s->conf, id);
+	if (cred == NULL ||
+	    wpa_config_remove_cred(wpa_s->conf, id) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
+			   id);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s,
+					      char *cmd)
+{
+	int id;
+	struct wpa_cred *cred;
+	char *name, *value;
+
+	/* cmd: "<cred id> <variable name> <value>" */
+	name = os_strchr(cmd, ' ');
+	if (name == NULL)
+		return -1;
+	*name++ = '\0';
+
+	value = os_strchr(name, ' ');
+	if (value == NULL)
+		return -1;
+	*value++ = '\0';
+
+	id = atoi(cmd);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_CRED id=%d name='%s'",
+		   id, name);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
+			      (u8 *) value, os_strlen(value));
+
+	cred = wpa_config_get_cred(wpa_s->conf, id);
+	if (cred == NULL) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
+			   id);
+		return -1;
+	}
+
+	if (wpa_config_set_cred(cred, name, value, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set cred "
+			   "variable '%s'", name);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 #ifndef CONFIG_NO_CONFIG_WRITE
 static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s)
 {
@@ -1886,6 +2196,14 @@
 		first = 0;
 	}
 
+	if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) {
+		ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " ");
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+		first = 0;
+	}
+
 	if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
 		ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
 		if (ret < 0 || ret >= end - pos)
@@ -1934,6 +2252,14 @@
 		first = 0;
 	}
 
+	if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) {
+		ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " ");
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+		first = 0;
+	}
+
 	if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
 		ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
 		if (ret < 0 || ret >= end - pos)
@@ -2107,6 +2433,53 @@
 }
 
 
+static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s,
+					      char *buf, size_t buflen)
+{
+	struct hostapd_channel_data *chnl;
+	int ret, i, j;
+	char *pos, *end, *hmode;
+
+	pos = buf;
+	end = pos + buflen;
+
+	for (j = 0; j < wpa_s->hw.num_modes; j++) {
+		switch (wpa_s->hw.modes[j].mode) {
+		case HOSTAPD_MODE_IEEE80211B:
+			hmode = "B";
+			break;
+		case HOSTAPD_MODE_IEEE80211G:
+			hmode = "G";
+			break;
+		case HOSTAPD_MODE_IEEE80211A:
+			hmode = "A";
+			break;
+		default:
+			continue;
+		}
+		ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode);
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+		chnl = wpa_s->hw.modes[j].channels;
+		for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) {
+			if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
+				continue;
+			ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan);
+			if (ret < 0 || ret >= end - pos)
+				return pos - buf;
+			pos += ret;
+		}
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
 static int wpa_supplicant_ctrl_iface_get_capability(
 	struct wpa_supplicant *wpa_s, const char *_field, char *buf,
 	size_t buflen)
@@ -2157,6 +2530,9 @@
 		return ctrl_iface_get_capability_auth_alg(res, strict, &capa,
 							  buf, buflen);
 
+	if (os_strcmp(field, "channels") == 0)
+		return ctrl_iface_get_capability_channels(wpa_s, buf, buflen);
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
 		   field);
 
@@ -2206,9 +2582,7 @@
 	int ret;
 	char *pos, *end;
 	const u8 *ie, *ie2;
-	struct os_time now;
 
-	os_get_time(&now);
 	pos = buf;
 	end = buf + buflen;
 
@@ -2280,6 +2654,9 @@
 	}
 
 	if (mask & WPA_BSS_MASK_AGE) {
+		struct os_time now;
+
+		os_get_time(&now);
 		ret = os_snprintf(pos, end - pos, "age=%d\n",
 				  (int) (now.sec - bss->last_update.sec));
 		if (ret < 0 || ret >= end - pos)
@@ -2346,6 +2723,14 @@
 				return 0;
 			pos += ret;
 		}
+#ifdef CONFIG_HS20
+		if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
+			ret = os_snprintf(pos, end - pos, "[HS20]");
+			if (ret < 0 || ret >= end - pos)
+				return -1;
+			pos += ret;
+		}
+#endif /* CONFIG_HS20 */
 
 		ret = os_snprintf(pos, end - pos, "\n");
 		if (ret < 0 || ret >= end - pos)
@@ -2381,34 +2766,76 @@
 	}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (mask & WPA_BSS_MASK_WIFI_DISPLAY) {
+		struct wpabuf *wfd;
+		ie = (const u8 *) (bss + 1);
+		wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len,
+						  WFD_IE_VENDOR_TYPE);
+		if (wfd) {
+			ret = os_snprintf(pos, end - pos, "wfd_subelems=");
+			if (ret < 0 || ret >= end - pos)
+				return pos - buf;
+			pos += ret;
+
+			pos += wpa_snprintf_hex(pos, end - pos,
+						wpabuf_head(wfd),
+						wpabuf_len(wfd));
+			wpabuf_free(wfd);
+
+			ret = os_snprintf(pos, end - pos, "\n");
+			if (ret < 0 || ret >= end - pos)
+				return pos - buf;
+			pos += ret;
+		}
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
 #ifdef CONFIG_INTERWORKING
-	if (mask & WPA_BSS_MASK_INTERNETW) {
+	if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
+		struct wpa_bss_anqp *anqp = bss->anqp;
 		pos = anqp_add_hex(pos, end, "anqp_venue_name",
-				   bss->anqp_venue_name);
+				   anqp->venue_name);
 		pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
-				   bss->anqp_network_auth_type);
+				   anqp->network_auth_type);
 		pos = anqp_add_hex(pos, end, "anqp_roaming_consortium",
-				   bss->anqp_roaming_consortium);
+				   anqp->roaming_consortium);
 		pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability",
-				   bss->anqp_ip_addr_type_availability);
+				   anqp->ip_addr_type_availability);
 		pos = anqp_add_hex(pos, end, "anqp_nai_realm",
-				   bss->anqp_nai_realm);
-		pos = anqp_add_hex(pos, end, "anqp_3gpp", bss->anqp_3gpp);
+				   anqp->nai_realm);
+		pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp);
 		pos = anqp_add_hex(pos, end, "anqp_domain_name",
-				   bss->anqp_domain_name);
+				   anqp->domain_name);
+#ifdef CONFIG_HS20
+		pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name",
+				   anqp->hs20_operator_friendly_name);
+		pos = anqp_add_hex(pos, end, "hs20_wan_metrics",
+				   anqp->hs20_wan_metrics);
+		pos = anqp_add_hex(pos, end, "hs20_connection_capability",
+				   anqp->hs20_connection_capability);
+#endif /* CONFIG_HS20 */
 	}
 #endif /* CONFIG_INTERWORKING */
 
+#ifdef ANDROID
+	ret = os_snprintf(pos, end - pos, "====\n");
+	if (ret < 0 || ret >= end - pos)
+		return 0;
+	pos += ret;
+#endif
+
 	return pos - buf;
 }
 
+
 static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
 					 const char *cmd, char *buf,
 					 size_t buflen)
 {
 	u8 bssid[ETH_ALEN];
 	size_t i;
-	struct wpa_bss *bss = NULL;
+	struct wpa_bss *bss;
 	struct wpa_bss *bsslast = NULL;
 	struct dl_list *next;
 	int ret = 0;
@@ -2419,33 +2846,45 @@
 	if (os_strncmp(cmd, "RANGE=", 6) == 0) {
 		if (os_strncmp(cmd + 6, "ALL", 3) == 0) {
 			bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss,
-			                    list_id);
+					    list_id);
 			bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss,
 					       list_id);
 		} else { /* N1-N2 */
-			if ((ctmp = os_strchr(cmd + 6, '-')) != NULL) {
-				int id1, id2;
-				id1 = atoi(cmd + 6);
-				bss = wpa_bss_get_id(wpa_s, id1);
-				id2 = atoi(ctmp + 1);
-				if (id2 == 0)
-					bsslast = dl_list_last(&wpa_s->bss_id,
-							       struct wpa_bss,
-							       list_id);
-				else
-					bsslast = wpa_bss_get_id(wpa_s, id2);
-			} else {
-				wpa_printf(MSG_ERROR, "Wrong range format");
+			unsigned int id1, id2;
+
+			if ((ctmp = os_strchr(cmd + 6, '-')) == NULL) {
+				wpa_printf(MSG_INFO, "Wrong BSS range "
+					   "format");
 				return 0;
 			}
-		}
-		if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) {
-			mask = strtoul(ctmp + 5, NULL, 0x10);
-			if (mask == 0)
-				mask = WPA_BSS_MASK_ALL;
+
+			id1 = atoi(cmd + 6);
+			bss = wpa_bss_get_id(wpa_s, id1);
+			id2 = atoi(ctmp + 1);
+			if (id2 == 0)
+				bsslast = dl_list_last(&wpa_s->bss_id,
+						       struct wpa_bss,
+						       list_id);
+			else {
+				bsslast = wpa_bss_get_id(wpa_s, id2);
+				if (bsslast == NULL && bss && id2 > id1) {
+					struct wpa_bss *tmp = bss;
+					for (;;) {
+						next = tmp->list_id.next;
+						if (next == &wpa_s->bss_id)
+							break;
+						tmp = dl_list_entry(
+							next, struct wpa_bss,
+							list_id);
+						if (tmp->id > id2)
+							break;
+						bsslast = tmp;
+					}
+				}
+			}
 		}
 	} else if (os_strcmp(cmd, "FIRST") == 0)
-		bss = dl_list_first(&wpa_s->bss, struct wpa_bss, list);
+		bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id);
 	else if (os_strncmp(cmd, "ID-", 3) == 0) {
 		i = atoi(cmd + 3);
 		bss = wpa_bss_get_id(wpa_s, i);
@@ -2482,6 +2921,12 @@
 		}
 	}
 
+	if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) {
+		mask = strtoul(ctmp + 5, NULL, 0x10);
+		if (mask == 0)
+			mask = WPA_BSS_MASK_ALL;
+	}
+
 	if (bss == NULL)
 		return 0;
 
@@ -2516,10 +2961,7 @@
 	struct wpa_supplicant *wpa_s, char *cmd)
 {
 	int scan_int = atoi(cmd);
-	if (scan_int < 0)
-		return -1;
-	wpa_s->scan_interval = scan_int;
-	return 0;
+	return wpa_supplicant_set_scan_interval(wpa_s, scan_int);
 }
 
 
@@ -2539,6 +2981,19 @@
 }
 
 
+static int wpa_supplicant_ctrl_iface_bss_flush(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int flush_age = atoi(cmd);
+
+	if (flush_age == 0)
+		wpa_bss_flush(wpa_s);
+	else
+		wpa_bss_flush_by_age(wpa_s, flush_age);
+	return 0;
+}
+
+
 static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
 {
 	wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
@@ -2613,6 +3068,7 @@
 	enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
 	u8 dev_id[ETH_ALEN], *_dev_id = NULL;
 	char *pos;
+	unsigned int search_delay;
 
 	if (os_strstr(cmd, "type=social"))
 		type = P2P_FIND_ONLY_SOCIAL;
@@ -2627,7 +3083,15 @@
 		_dev_id = dev_id;
 	}
 
-	return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id);
+	pos = os_strstr(cmd, "delay=");
+	if (pos) {
+		pos += 6;
+		search_delay = atoi(pos);
+	} else
+		search_delay = wpas_p2p_search_delay(wpa_s);
+
+	return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id,
+			     search_delay);
 }
 
 
@@ -2640,14 +3104,19 @@
 	enum p2p_wps_method wps_method;
 	int new_pin;
 	int ret;
-	int persistent_group;
+	int persistent_group, persistent_id = -1;
 	int join;
 	int auth;
+	int automatic;
 	int go_intent = -1;
 	int freq = 0;
+	int pd;
+	int ht40;
 
-	/* <addr> <"pbc" | "pin" | PIN> [label|display|keypad] [persistent]
-	 * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] */
+	/* <addr> <"pbc" | "pin" | PIN> [label|display|keypad]
+	 * [persistent|persistent=<network id>]
+	 * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
+	 * [ht40] */
 
 	if (hwaddr_aton(cmd, addr))
 		return -1;
@@ -2658,8 +3127,24 @@
 	pos++;
 
 	persistent_group = os_strstr(pos, " persistent") != NULL;
+	pos2 = os_strstr(pos, " persistent=");
+	if (pos2) {
+		struct wpa_ssid *ssid;
+		persistent_id = atoi(pos2 + 12);
+		ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
+		if (ssid == NULL || ssid->disabled != 2 ||
+		    ssid->mode != WPAS_MODE_P2P_GO) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+				   "SSID id=%d for persistent P2P group (GO)",
+				   persistent_id);
+			return -1;
+		}
+	}
 	join = os_strstr(pos, " join") != NULL;
 	auth = os_strstr(pos, " auth") != NULL;
+	automatic = os_strstr(pos, " auto") != NULL;
+	pd = os_strstr(pos, " provdisc") != NULL;
+	ht40 = os_strstr(pos, " ht40") != NULL;
 
 	pos2 = os_strstr(pos, " go_intent=");
 	if (pos2) {
@@ -2691,11 +3176,16 @@
 			if (os_strncmp(pos, "display", 7) == 0)
 				wps_method = WPS_PIN_DISPLAY;
 		}
+		if (!wps_pin_str_valid(pin)) {
+			os_memcpy(buf, "FAIL-INVALID-PIN\n", 17);
+			return 17;
+		}
 	}
 
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
-				   persistent_group, join, auth, go_intent,
-				   freq);
+				   persistent_group, automatic, join,
+				   auth, go_intent, freq, persistent_id, pd,
+				   ht40);
 	if (new_pin == -2) {
 		os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
 		return 25;
@@ -2729,8 +3219,9 @@
 {
 	u8 addr[ETH_ALEN];
 	char *pos;
+	enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG;
 
-	/* <addr> <config method> [join] */
+	/* <addr> <config method> [join|auto] */
 
 	if (hwaddr_aton(cmd, addr))
 		return -1;
@@ -2740,8 +3231,12 @@
 		return -1;
 	pos++;
 
-	return wpas_p2p_prov_disc(wpa_s, addr, pos,
-				  os_strstr(pos, "join") != NULL);
+	if (os_strstr(pos, " join") != NULL)
+		use = WPAS_P2P_PD_FOR_JOIN;
+	else if (os_strstr(pos, " auto") != NULL)
+		use = WPAS_P2P_PD_AUTO;
+
+	return wpas_p2p_prov_disc(wpa_s, addr, pos, use);
 }
 
 
@@ -2790,6 +3285,10 @@
 			return -1;
 		pos++;
 		ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos);
+#ifdef CONFIG_WIFI_DISPLAY
+	} else if (os_strncmp(pos, "wifi-display ", 13) == 0) {
+		ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13);
+#endif /* CONFIG_WIFI_DISPLAY */
 	} else {
 		len = os_strlen(pos);
 		if (len & 1)
@@ -2878,6 +3377,8 @@
 static int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s,
 				       char *cmd)
 {
+	if (os_strcmp(cmd, "0") && os_strcmp(cmd, "1"))
+		return -1;
 	wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd);
 	return 0;
 }
@@ -3046,7 +3547,9 @@
 	char *pos;
 	int id;
 	struct wpa_ssid *ssid;
-	u8 peer[ETH_ALEN];
+	u8 *_peer = NULL, peer[ETH_ALEN];
+	int freq = 0;
+	int ht40;
 
 	id = atoi(cmd);
 	pos = os_strstr(cmd, " peer=");
@@ -3054,6 +3557,7 @@
 		pos += 6;
 		if (hwaddr_aton(pos, peer))
 			return -1;
+		_peer = peer;
 	}
 	ssid = wpa_config_get_network(wpa_s->conf, id);
 	if (ssid == NULL || ssid->disabled != 2) {
@@ -3063,7 +3567,17 @@
 		return -1;
 	}
 
-	return wpas_p2p_invite(wpa_s, pos ? peer : NULL, ssid, NULL);
+	pos = os_strstr(cmd, " freq=");
+	if (pos) {
+		pos += 6;
+		freq = atoi(pos);
+		if (freq <= 0)
+			return -1;
+	}
+
+	ht40 = os_strstr(cmd, " ht40") != NULL;
+
+	return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40);
 }
 
 
@@ -3110,7 +3624,7 @@
 
 
 static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
-					 char *cmd, int freq)
+					 char *cmd, int freq, int ht40)
 {
 	int id;
 	struct wpa_ssid *ssid;
@@ -3124,26 +3638,31 @@
 		return -1;
 	}
 
-	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq);
+	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, ht40);
 }
 
 
 static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
 {
-	int freq = 0;
+	int freq = 0, ht40;
 	char *pos;
 
 	pos = os_strstr(cmd, "freq=");
 	if (pos)
 		freq = atoi(pos + 5);
 
+	ht40 = os_strstr(cmd, "ht40") != NULL;
+
 	if (os_strncmp(cmd, "persistent=", 11) == 0)
-		return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq);
+		return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq,
+						     ht40);
 	if (os_strcmp(cmd, "persistent") == 0 ||
 	    os_strncmp(cmd, "persistent ", 11) == 0)
-		return wpas_p2p_group_add(wpa_s, 1, freq);
+		return wpas_p2p_group_add(wpa_s, 1, freq, ht40);
 	if (os_strncmp(cmd, "freq=", 5) == 0)
-		return wpas_p2p_group_add(wpa_s, 0, freq);
+		return wpas_p2p_group_add(wpa_s, 0, freq, ht40);
+	if (ht40)
+		return wpas_p2p_group_add(wpa_s, 0, freq, ht40);
 
 	wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'",
 		   cmd);
@@ -3230,6 +3749,56 @@
 }
 
 
+static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s,
+				  const char *param)
+{
+	struct wpa_freq_range *freq = NULL, *n;
+	unsigned int count = 0, i;
+	const char *pos, *pos2, *pos3;
+
+	if (wpa_s->global->p2p == NULL)
+		return -1;
+
+	/*
+	 * param includes comma separated frequency range.
+	 * For example: 2412-2432,2462,5000-6000
+	 */
+	pos = param;
+	while (pos && pos[0]) {
+		n = os_realloc_array(freq, count + 1,
+				     sizeof(struct wpa_freq_range));
+		if (n == NULL) {
+			os_free(freq);
+			return -1;
+		}
+		freq = n;
+		freq[count].min = atoi(pos);
+		pos2 = os_strchr(pos, '-');
+		pos3 = os_strchr(pos, ',');
+		if (pos2 && (!pos3 || pos2 < pos3)) {
+			pos2++;
+			freq[count].max = atoi(pos2);
+		} else
+			freq[count].max = freq[count].min;
+		pos = pos3;
+		if (pos)
+			pos++;
+		count++;
+	}
+
+	for (i = 0; i < count; i++) {
+		wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u",
+			   freq[i].min, freq[i].max);
+	}
+
+	os_free(wpa_s->global->p2p_disallow_freq);
+	wpa_s->global->p2p_disallow_freq = freq;
+	wpa_s->global->num_p2p_disallow_freq = count;
+	wpas_p2p_update_channel_list(wpa_s);
+	return 0;
+}
+
+
 static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	char *param;
@@ -3308,7 +3877,7 @@
 		}
 		return 0;
 	}
-#ifdef ANDROID_P2P
+
 	if (os_strcmp(cmd, "conc_pref") == 0) {
 		if (os_strcmp(param, "sta") == 0)
 			wpa_s->global->conc_pref = WPA_CONC_PREF_STA;
@@ -3319,10 +3888,10 @@
 			return -1;
 		}
 		wpa_printf(MSG_DEBUG, "Single channel concurrency preference: "
-			"%s", param);
+			   "%s", param);
 		return 0;
 	}
-#endif
+
 	if (os_strcmp(cmd, "force_long_sd") == 0) {
 		wpa_s->force_long_sd = atoi(param);
 		return 0;
@@ -3388,6 +3957,9 @@
 		return 0;
 	}
 
+	if (os_strcmp(cmd, "disallow_freq") == 0)
+		return p2p_ctrl_disallow_freq(wpa_s, param);
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
 		   cmd);
 
@@ -3497,9 +4069,230 @@
 
 	return anqp_send_req(wpa_s, dst_addr, id, num_id);
 }
+
+
+static int gas_request(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 dst_addr[ETH_ALEN];
+	struct wpabuf *advproto, *query = NULL;
+	int used, ret = -1;
+	char *pos, *end;
+	size_t len;
+
+	used = hwaddr_aton2(cmd, dst_addr);
+	if (used < 0)
+		return -1;
+
+	pos = cmd + used;
+	while (*pos == ' ')
+		pos++;
+
+	/* Advertisement Protocol ID */
+	end = os_strchr(pos, ' ');
+	if (end)
+		len = end - pos;
+	else
+		len = os_strlen(pos);
+	if (len & 0x01)
+		return -1;
+	len /= 2;
+	if (len == 0)
+		return -1;
+	advproto = wpabuf_alloc(len);
+	if (advproto == NULL)
+		return -1;
+	if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0)
+		goto fail;
+
+	if (end) {
+		/* Optional Query Request */
+		pos = end + 1;
+		while (*pos == ' ')
+			pos++;
+
+		len = os_strlen(pos);
+		if (len) {
+			if (len & 0x01)
+				goto fail;
+			len /= 2;
+			if (len == 0)
+				goto fail;
+			query = wpabuf_alloc(len);
+			if (query == NULL)
+				goto fail;
+			if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0)
+				goto fail;
+		}
+	}
+
+	ret = gas_send_request(wpa_s, dst_addr, advproto, query);
+
+fail:
+	wpabuf_free(advproto);
+	wpabuf_free(query);
+
+	return ret;
+}
+
+
+static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
+			    size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	int dialog_token;
+	int used;
+	char *pos;
+	size_t resp_len, start, requested_len;
+
+	if (!wpa_s->last_gas_resp)
+		return -1;
+
+	used = hwaddr_aton2(cmd, addr);
+	if (used < 0)
+		return -1;
+
+	pos = cmd + used;
+	while (*pos == ' ')
+		pos++;
+	dialog_token = atoi(pos);
+
+	if (os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) != 0 ||
+	    dialog_token != wpa_s->last_gas_dialog_token)
+		return -1;
+
+	resp_len = wpabuf_len(wpa_s->last_gas_resp);
+	start = 0;
+	requested_len = resp_len;
+
+	pos = os_strchr(pos, ' ');
+	if (pos) {
+		start = atoi(pos);
+		if (start > resp_len)
+			return os_snprintf(buf, buflen, "FAIL-Invalid range");
+		pos = os_strchr(pos, ',');
+		if (pos == NULL)
+			return -1;
+		pos++;
+		requested_len = atoi(pos);
+		if (start + requested_len > resp_len)
+			return os_snprintf(buf, buflen, "FAIL-Invalid range");
+	}
+
+	if (requested_len * 2 + 1 > buflen)
+		return os_snprintf(buf, buflen, "FAIL-Too long response");
+
+	return wpa_snprintf_hex(buf, buflen,
+				wpabuf_head_u8(wpa_s->last_gas_resp) + start,
+				requested_len);
+}
 #endif /* CONFIG_INTERWORKING */
 
 
+#ifdef CONFIG_HS20
+
+static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst)
+{
+	u8 dst_addr[ETH_ALEN];
+	int used;
+	char *pos;
+	u32 subtypes = 0;
+
+	used = hwaddr_aton2(dst, dst_addr);
+	if (used < 0)
+		return -1;
+	pos = dst + used;
+	for (;;) {
+		int num = atoi(pos);
+		if (num <= 0 || num > 31)
+			return -1;
+		subtypes |= BIT(num);
+		pos = os_strchr(pos + 1, ',');
+		if (pos == NULL)
+			break;
+		pos++;
+	}
+
+	if (subtypes == 0)
+		return -1;
+
+	return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0);
+}
+
+
+static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s,
+				    const u8 *addr, const char *realm)
+{
+	u8 *buf;
+	size_t rlen, len;
+	int ret;
+
+	rlen = os_strlen(realm);
+	len = 3 + rlen;
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+	buf[0] = 1; /* NAI Home Realm Count */
+	buf[1] = 0; /* Formatted in accordance with RFC 4282 */
+	buf[2] = rlen;
+	os_memcpy(buf + 3, realm, rlen);
+
+	ret = hs20_anqp_send_req(wpa_s, addr,
+				 BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
+				 buf, len);
+
+	os_free(buf);
+
+	return ret;
+}
+
+
+static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s,
+					char *dst)
+{
+	struct wpa_cred *cred = wpa_s->conf->cred;
+	u8 dst_addr[ETH_ALEN];
+	int used;
+	u8 *buf;
+	size_t len;
+	int ret;
+
+	used = hwaddr_aton2(dst, dst_addr);
+	if (used < 0)
+		return -1;
+
+	while (dst[used] == ' ')
+		used++;
+	if (os_strncmp(dst + used, "realm=", 6) == 0)
+		return hs20_nai_home_realm_list(wpa_s, dst_addr,
+						dst + used + 6);
+
+	len = os_strlen(dst + used);
+
+	if (len == 0 && cred && cred->realm)
+		return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm);
+
+	if (len % 1)
+		return -1;
+	len /= 2;
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+	if (hexstr2bin(dst + used, buf, len) < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	ret = hs20_anqp_send_req(wpa_s, dst_addr,
+				 BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
+				 buf, len);
+	os_free(buf);
+
+	return ret;
+}
+
+#endif /* CONFIG_HS20 */
+
+
 static int wpa_supplicant_ctrl_iface_sta_autoconnect(
 	struct wpa_supplicant *wpa_s, char *cmd)
 {
@@ -3508,6 +4301,36 @@
 }
 
 
+#ifdef CONFIG_AUTOSCAN
+
+static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s,
+					      char *cmd)
+{
+	enum wpa_states state = wpa_s->wpa_state;
+	char *new_params = NULL;
+
+	if (os_strlen(cmd) > 0) {
+		new_params = os_strdup(cmd);
+		if (new_params == NULL)
+			return -1;
+	}
+
+	os_free(wpa_s->conf->autoscan);
+	wpa_s->conf->autoscan = new_params;
+
+	if (wpa_s->conf->autoscan == NULL)
+		autoscan_deinit(wpa_s);
+	else if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
+		autoscan_init(wpa_s, 1);
+	else if (state == WPA_SCANNING)
+		wpa_supplicant_reinit_autoscan(wpa_s);
+
+	return 0;
+}
+
+#endif /* CONFIG_AUTOSCAN */
+
+
 static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
 				      size_t buflen)
 {
@@ -3527,6 +4350,25 @@
 	return ret;
 }
 
+
+static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
+				      size_t buflen)
+{
+	struct hostap_sta_driver_data sta;
+	int ret;
+
+	ret = wpa_drv_pktcnt_poll(wpa_s, &sta);
+	if (ret)
+		return -1;
+
+	ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n",
+			  sta.tx_packets, sta.tx_retry_failed, sta.rx_packets);
+	if (ret < 0 || (size_t) ret > buflen)
+		return -1;
+	return ret;
+}
+
+
 #ifdef ANDROID
 static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd,
 				     char *buf, size_t buflen)
@@ -3540,6 +4382,7 @@
 }
 #endif
 
+
 char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 					 char *buf, size_t *resp_len)
 {
@@ -3558,6 +4401,7 @@
 			level = MSG_EXCESSIVE;
 		wpa_hexdump_ascii(level, "RX ctrl_iface",
 				  (const u8 *) buf, os_strlen(buf));
+		wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
 	}
 
 	reply = os_malloc(reply_size);
@@ -3572,6 +4416,9 @@
 	if (os_strcmp(buf, "PING") == 0) {
 		os_memcpy(reply, "PONG\n", 5);
 		reply_len = 5;
+	} else if (os_strcmp(buf, "IFNAME") == 0) {
+		reply_len = os_strlen(wpa_s->ifname);
+		os_memcpy(reply, wpa_s->ifname, reply_len);
 	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
 		if (wpa_debug_reopen_file() < 0)
 			reply_len = -1;
@@ -3606,6 +4453,7 @@
 		eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
 	} else if (os_strcmp(buf, "REASSOCIATE") == 0) {
 		wpa_s->normal_scans = 0;
+		wpa_supplicant_reinit_autoscan(wpa_s);
 		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 			reply_len = -1;
 		else {
@@ -3615,6 +4463,7 @@
 		}
 	} else if (os_strcmp(buf, "RECONNECT") == 0) {
 		wpa_s->normal_scans = 0;
+		wpa_supplicant_reinit_autoscan(wpa_s);
 		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 			reply_len = -1;
 		else if (wpa_s->disconnected) {
@@ -3667,6 +4516,21 @@
 		if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8))
 			reply_len = -1;
 #endif /* CONFIG_WPS_OOB */
+#ifdef CONFIG_WPS_NFC
+	} else if (os_strcmp(buf, "WPS_NFC") == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token(
+			wpa_s, buf + 14, reply, reply_size);
+	} else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s,
+							       buf + 17))
+			reply_len = -1;
+#endif /* CONFIG_WPS_NFC */
 	} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
 		if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
 			reply_len = -1;
@@ -3711,6 +4575,11 @@
 	} else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) {
 		if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14))
 			reply_len = -1;
+#ifdef CONFIG_WPS_NFC
+	} else if (os_strncmp(buf, "WPS_ER_NFC_CONFIG_TOKEN ", 24) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
+			wpa_s, buf + 24, reply, reply_size);
+#endif /* CONFIG_WPS_NFC */
 #endif /* CONFIG_WPS_ER */
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_IBSS_RSN
@@ -3740,7 +4609,7 @@
 		if (wpas_p2p_group_remove(wpa_s, buf + 17))
 			reply_len = -1;
 	} else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
-		if (wpas_p2p_group_add(wpa_s, 0, 0))
+		if (wpas_p2p_group_add(wpa_s, 0, 0, 0))
 			reply_len = -1;
 	} else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
 		if (p2p_ctrl_group_add(wpa_s, buf + 14))
@@ -3760,7 +4629,11 @@
 		if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0)
 			reply_len = -1;
 	} else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) {
+#ifdef ANDROID_P2P
+		wpas_p2p_sd_service_update(wpa_s, SRV_UPDATE);
+#else
 		wpas_p2p_sd_service_update(wpa_s);
+#endif
 	} else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) {
 		if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0)
 			reply_len = -1;
@@ -3808,6 +4681,14 @@
 		if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
 			reply_len = -1;
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_WIFI_DISPLAY
+	} else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
+		if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) {
+		reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16,
+						     reply, reply_size);
+#endif /* CONFIG_WIFI_DISPLAY */
 #ifdef CONFIG_INTERWORKING
 	} else if (os_strcmp(buf, "FETCH_ANQP") == 0) {
 		if (interworking_fetch_anqp(wpa_s) < 0)
@@ -3824,7 +4705,21 @@
 	} else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
 		if (get_anqp(wpa_s, buf + 9) < 0)
 			reply_len = -1;
+	} else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) {
+		if (gas_request(wpa_s, buf + 12) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) {
+		reply_len = gas_response_get(wpa_s, buf + 17, reply,
+					     reply_size);
 #endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+	} else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) {
+		if (get_hs20_anqp(wpa_s, buf + 14) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) {
+		if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0)
+			reply_len = -1;
+#endif /* CONFIG_HS20 */
 	} else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
 	{
 		if (wpa_supplicant_ctrl_iface_ctrl_rsp(
@@ -3850,19 +4745,30 @@
 		reply_len = wpa_supplicant_ctrl_iface_list_networks(
 			wpa_s, reply, reply_size);
 	} else if (os_strcmp(buf, "DISCONNECT") == 0) {
+#ifdef CONFIG_SME
+		wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
 		wpa_s->reassociate = 0;
 		wpa_s->disconnected = 1;
 		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_cancel_scan(wpa_s);
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
 	} else if (os_strcmp(buf, "SCAN") == 0) {
-		wpa_s->normal_scans = 0;
 		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 			reply_len = -1;
 		else {
 			if (!wpa_s->scanning &&
 			    ((wpa_s->wpa_state <= WPA_SCANNING) ||
 			     (wpa_s->wpa_state == WPA_COMPLETED))) {
+				wpa_s->normal_scans = 0;
+				wpa_s->scan_req = 2;
+				wpa_supplicant_req_scan(wpa_s, 0, 0);
+			} else if (wpa_s->sched_scanning) {
+				wpa_printf(MSG_DEBUG, "Stop ongoing "
+					   "sched_scan to allow requested "
+					   "full scan to proceed");
+				wpa_supplicant_cancel_sched_scan(wpa_s);
 				wpa_s->scan_req = 2;
 				wpa_supplicant_req_scan(wpa_s, 0, 0);
 			} else {
@@ -3896,6 +4802,18 @@
 	} else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) {
 		reply_len = wpa_supplicant_ctrl_iface_get_network(
 			wpa_s, buf + 12, reply, reply_size);
+	} else if (os_strcmp(buf, "LIST_CREDS") == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_list_creds(
+			wpa_s, reply, reply_size);
+	} else if (os_strcmp(buf, "ADD_CRED") == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_add_cred(
+			wpa_s, reply, reply_size);
+	} else if (os_strncmp(buf, "REMOVE_CRED ", 12) == 0) {
+		if (wpa_supplicant_ctrl_iface_remove_cred(wpa_s, buf + 12))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "SET_CRED ", 9) == 0) {
+		if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9))
+			reply_len = -1;
 #ifndef CONFIG_NO_CONFIG_WRITE
 	} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
 		if (wpa_supplicant_ctrl_iface_save_config(wpa_s))
@@ -3928,6 +4846,12 @@
 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
 		reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
 						   reply_size);
+	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
+		if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
+		if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
+			reply_len = -1;
 #endif /* CONFIG_AP */
 	} else if (os_strcmp(buf, "SUSPEND") == 0) {
 		wpas_notify_suspend(wpa_s->global);
@@ -3948,6 +4872,9 @@
 		if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s,
 							       buf + 17))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) {
+		if (wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10))
+			reply_len = -1;
 #ifdef CONFIG_TDLS
 	} else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) {
 		if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14))
@@ -3962,6 +4889,14 @@
 	} else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
 		reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
 						       reply_size);
+	} else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) {
+		reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply,
+						       reply_size);
+#ifdef CONFIG_AUTOSCAN
+	} else if (os_strncmp(buf, "AUTOSCAN ", 9) == 0) {
+		if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9))
+			reply_len = -1;
+#endif /* CONFIG_AUTOSCAN */
 #ifdef ANDROID
 	} else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
 		reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply,
diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h
index 3f1c6a0..a329ef3 100644
--- a/wpa_supplicant/ctrl_iface.h
+++ b/wpa_supplicant/ctrl_iface.h
@@ -89,21 +89,6 @@
 void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv);
 
 /**
- * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
- * @wpa_s: Pointer to wpa_supplicant data
- * @ssid: Pointer to the network block the reply is for
- * @field: field the response is a reply for
- * @value: value (ie, password, etc) for @field
- * Returns: 0 on success, non-zero on error
- *
- * Helper function to handle replies to control interface requests.
- */
-int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
-					      struct wpa_ssid *ssid,
-					      const char *field,
-					      const char *value);
-
-/**
  * wpa_supplicant_global_ctrl_iface_init - Initialize global control interface
  * @global: Pointer to global data from wpa_supplicant_init()
  * Returns: Pointer to private data on success, %NULL on failure
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index c831e6c..994f9b1 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -163,6 +163,8 @@
 		perror("recvfrom(ctrl_iface)");
 		return;
 	}
+
+#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
 	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
 		/*
 		 * The OS networking stack is expected to drop this kind of
@@ -174,6 +176,8 @@
 			   "source %s", inet_ntoa(from.sin_addr));
 		return;
 	}
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
 	buf[res] = '\0';
 
 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
@@ -266,6 +270,7 @@
 {
 	struct ctrl_iface_priv *priv;
 	struct sockaddr_in addr;
+	int port = WPA_CTRL_IFACE_PORT;
 
 	priv = os_zalloc(sizeof(*priv));
 	if (priv == NULL)
@@ -285,13 +290,25 @@
 
 	os_memset(&addr, 0, sizeof(addr));
 	addr.sin_family = AF_INET;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	addr.sin_addr.s_addr = INADDR_ANY;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 	addr.sin_addr.s_addr = htonl((127 << 24) | 1);
-	addr.sin_port = htons(WPA_CTRL_IFACE_PORT);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+try_again:
+	addr.sin_port = htons(port);
 	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		port--;
+		if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT)
+			goto try_again;
 		perror("bind(AF_INET)");
 		goto fail;
 	}
 
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
 	eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
 				 wpa_s, priv);
 	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
@@ -442,6 +459,8 @@
 		perror("recvfrom(ctrl_iface)");
 		return;
 	}
+
+#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
 	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
 		/*
 		 * The OS networking stack is expected to drop this kind of
@@ -453,6 +472,8 @@
 			   "source %s", inet_ntoa(from.sin_addr));
 		return;
 	}
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
 	buf[res] = '\0';
 
 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
@@ -502,6 +523,7 @@
 {
 	struct ctrl_iface_global_priv *priv;
 	struct sockaddr_in addr;
+	int port = WPA_GLOBAL_CTRL_IFACE_PORT;
 
 	priv = os_zalloc(sizeof(*priv));
 	if (priv == NULL)
@@ -523,13 +545,26 @@
 
 	os_memset(&addr, 0, sizeof(addr));
 	addr.sin_family = AF_INET;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	addr.sin_addr.s_addr = INADDR_ANY;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 	addr.sin_addr.s_addr = htonl((127 << 24) | 1);
-	addr.sin_port = htons(WPA_GLOBAL_CTRL_IFACE_PORT);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+try_again:
+	addr.sin_port = htons(port);
 	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		port++;
+		if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) <
+		    WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT)
+			goto try_again;
 		perror("bind(AF_INET)");
 		goto fail;
 	}
 
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
 	eloop_register_read_sock(priv->sock,
 				 wpa_supplicant_global_ctrl_iface_receive,
 				 global, priv);
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index b4af793..80db27e 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -11,6 +11,8 @@
 #include <sys/stat.h>
 #include <grp.h>
 #include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
 #ifdef ANDROID
 #include <cutils/sockets.h>
 #endif /* ANDROID */
@@ -165,33 +167,28 @@
 			reply_len = 2;
 	} else {
 #if defined(CONFIG_P2P) && defined(ANDROID_P2P)
-		char *ifname = NULL, *arg;
-		char cmd[256];
-		/* Skip the command name */
-		arg = os_strchr(buf, ' ');
-		if (arg) {
-			*arg++ = '\0';
-			os_strncpy(cmd, buf, sizeof(cmd));
-			/* Now search for interface= */
-			if (os_strncmp(arg, "interface=", 10) == 0) {
-				ifname = arg + 10;
-				arg = os_strchr(ifname, ' ');
-				if (arg)
-					*arg++ = '\0';
-				wpa_printf(MSG_DEBUG, "Found interface= in the arg %s ifname %s", arg, ifname);
-				for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
-					if (os_strcmp(wpa_s->ifname, ifname) == 0)
-						break;
-				}
-				if (wpa_s == NULL) {
-					wpa_printf(MSG_ERROR, "P2P: interface=%s does not exist", ifname);
-					wpa_s = eloop_ctx;
-				}
+		char *ifname, *ifend;
+
+		ifname = os_strstr(buf, "interface=");
+		if (ifname != NULL) {
+			ifend = os_strchr(ifname + 10, ' ');
+			if (ifend != NULL)
+				*ifend++ = '\0';
+			else
+				*(ifname - 1) = '\0';
+			wpa_printf(MSG_DEBUG, "Found %s", ifname);
+			for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+				if (os_strcmp(wpa_s->ifname, ifname + 10) == 0)
+					break;
 			}
-			if (arg)
-				os_snprintf(buf, sizeof(buf), "%s %s", cmd, arg);
+			if (wpa_s == NULL) {
+				wpa_printf(MSG_ERROR, "P2P: %s does not exist", ifname);
+				wpa_s = eloop_ctx;
+			}
+			if (ifend != NULL)
+				os_memmove(ifname, ifend, strlen(ifend) + 1);
+			wpa_printf(MSG_INFO, "wpa_s->ifname %s cmd %s", wpa_s ? wpa_s->ifname : "NULL", buf);
 		}
-		wpa_printf(MSG_DEBUG, "wpa_s %p cmd %s", wpa_s, buf);
 #endif /* defined CONFIG_P2P && defined ANDROID_P2P */
 		reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
 							  &reply_len);
@@ -288,6 +285,7 @@
 	char *buf, *dir = NULL, *gid_str = NULL;
 	struct group *grp;
 	char *endp;
+	int flags;
 
 	priv = os_zalloc(sizeof(*priv));
 	if (priv == NULL)
@@ -404,7 +402,7 @@
 			}
 			if (bind(priv->sock, (struct sockaddr *) &addr,
 				 sizeof(addr)) < 0) {
-				perror("bind(PF_UNIX)");
+				perror("supp-ctrl-iface-init: bind(PF_UNIX)");
 				goto fail;
 			}
 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -434,6 +432,20 @@
 #ifdef ANDROID
 havesock:
 #endif /* ANDROID */
+
+	/*
+	 * Make socket non-blocking so that we don't hang forever if
+	 * target dies unexpectedly.
+	 */
+	flags = fcntl(priv->sock, F_GETFL);
+	if (flags >= 0) {
+		flags |= O_NONBLOCK;
+		if (fcntl(priv->sock, F_SETFL, flags) < 0) {
+			perror("fcntl(ctrl, O_NONBLOCK)");
+			/* Not fatal, continue on.*/
+		}
+	}
+
 	eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
 				 wpa_s, priv);
 	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
@@ -696,7 +708,8 @@
 	os_strlcpy(addr.sun_path, global->params.ctrl_interface,
 		   sizeof(addr.sun_path));
 	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("bind(PF_UNIX)");
+		perror("supp-global-ctrl-iface-init (will try fixup): "
+		       "bind(PF_UNIX)");
 		if (connect(priv->sock, (struct sockaddr *) &addr,
 			    sizeof(addr)) < 0) {
 			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
@@ -711,7 +724,7 @@
 			}
 			if (bind(priv->sock, (struct sockaddr *) &addr,
 				 sizeof(addr)) < 0) {
-				perror("bind(PF_UNIX)");
+				perror("supp-glb-iface-init: bind(PF_UNIX)");
 				goto fail;
 			}
 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c
index 5850636..5d0e31e 100644
--- a/wpa_supplicant/dbus/dbus_common.c
+++ b/wpa_supplicant/dbus/dbus_common.c
@@ -4,14 +4,8 @@
  * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
diff --git a/wpa_supplicant/dbus/dbus_common.h b/wpa_supplicant/dbus/dbus_common.h
index 50da09b..aea7db7 100644
--- a/wpa_supplicant/dbus/dbus_common.h
+++ b/wpa_supplicant/dbus/dbus_common.h
@@ -4,14 +4,8 @@
  * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef DBUS_COMMON_H
diff --git a/wpa_supplicant/dbus/dbus_common_i.h b/wpa_supplicant/dbus/dbus_common_i.h
index 9dab1ee..a551ccd 100644
--- a/wpa_supplicant/dbus/dbus_common_i.h
+++ b/wpa_supplicant/dbus/dbus_common_i.h
@@ -4,14 +4,8 @@
  * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef DBUS_COMMON_I_H
@@ -25,6 +19,10 @@
 	struct wpa_global *global;
 	u32 next_objid;
 	int dbus_new_initialized;
+
+#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) && defined(CONFIG_AP)
+	int dbus_noc_refcnt;
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW && CONFIG_AP */
 };
 
 #endif /* DBUS_COMMON_I_H */
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index 68a9c28..61a9430 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -2,14 +2,8 @@
  * WPA Supplicant / dbus-based control interface
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -744,7 +738,7 @@
 	entry->bytearray_value = NULL;
 	entry->array_type = DBUS_TYPE_BYTE;
 
-	buffer = os_zalloc(BYTE_ARRAY_ITEM_SIZE * BYTE_ARRAY_CHUNK_SIZE);
+	buffer = os_calloc(BYTE_ARRAY_CHUNK_SIZE, BYTE_ARRAY_ITEM_SIZE);
 	if (!buffer)
 		return FALSE;
 
@@ -754,8 +748,9 @@
 		char byte;
 
 		if ((count % BYTE_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
-			nbuffer = os_realloc(buffer, BYTE_ARRAY_ITEM_SIZE *
-					     (count + BYTE_ARRAY_CHUNK_SIZE));
+			nbuffer = os_realloc_array(
+				buffer, count + BYTE_ARRAY_CHUNK_SIZE,
+				BYTE_ARRAY_ITEM_SIZE);
 			if (nbuffer == NULL) {
 				os_free(buffer);
 				wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_"
@@ -801,7 +796,7 @@
 	entry->strarray_value = NULL;
 	entry->array_type = DBUS_TYPE_STRING;
 
-	buffer = os_zalloc(STR_ARRAY_ITEM_SIZE * STR_ARRAY_CHUNK_SIZE);
+	buffer = os_calloc(STR_ARRAY_CHUNK_SIZE, STR_ARRAY_ITEM_SIZE);
 	if (buffer == NULL)
 		return FALSE;
 
@@ -812,8 +807,9 @@
 		char *str;
 
 		if ((count % STR_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
-			nbuffer = os_realloc(buffer, STR_ARRAY_ITEM_SIZE *
-					     (count + STR_ARRAY_CHUNK_SIZE));
+			nbuffer = os_realloc_array(
+				buffer, count + STR_ARRAY_CHUNK_SIZE,
+				STR_ARRAY_ITEM_SIZE);
 			if (nbuffer == NULL) {
 				os_free(buffer);
 				wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_"
@@ -877,8 +873,8 @@
 
 			buflen += BIN_ARRAY_CHUNK_SIZE;
 
-			newbuf = os_realloc(entry->binarray_value,
-					    buflen * BIN_ARRAY_ITEM_SIZE);
+			newbuf = os_realloc_array(entry->binarray_value,
+						  buflen, BIN_ARRAY_ITEM_SIZE);
 			if (!newbuf)
 				goto cleanup;
 			entry->binarray_value = newbuf;
@@ -1104,5 +1100,5 @@
 		break;
 	}
 
-	memset(entry, 0, sizeof(struct wpa_dbus_dict_entry));
+	os_memset(entry, 0, sizeof(struct wpa_dbus_dict_entry));
 }
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index 2f6eb45..9666349 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -2,14 +2,8 @@
  * WPA Supplicant / dbus-based control interface
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef DBUS_DICT_HELPERS_H
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index ce7cffb..4eeb93a 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -4,14 +4,8 @@
  * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -31,6 +25,99 @@
 #include "dbus_new_handlers_p2p.h"
 #include "p2p/p2p.h"
 
+#ifdef CONFIG_AP /* until needed by something else */
+
+/*
+ * NameOwnerChanged handling
+ *
+ * Some services we provide allow an application to register for
+ * a signal that it needs. While it can also unregister, we must
+ * be prepared for the case where the application simply crashes
+ * and thus doesn't clean up properly. The way to handle this in
+ * DBus is to register for the NameOwnerChanged signal which will
+ * signal an owner change to NULL if the peer closes the socket
+ * for whatever reason.
+ *
+ * Handle this signal via a filter function whenever necessary.
+ * The code below also handles refcounting in case in the future
+ * there will be multiple instances of this subscription scheme.
+ */
+static const char wpas_dbus_noc_filter_str[] =
+	"interface=org.freedesktop.DBus,member=NameOwnerChanged";
+
+
+static DBusHandlerResult noc_filter(DBusConnection *conn,
+				    DBusMessage *message, void *data)
+{
+	struct wpas_dbus_priv *priv = data;
+
+	if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
+				   "NameOwnerChanged")) {
+		const char *name;
+		const char *prev_owner;
+		const char *new_owner;
+		DBusError derr;
+		struct wpa_supplicant *wpa_s;
+
+		dbus_error_init(&derr);
+
+		if (!dbus_message_get_args(message, &derr,
+					   DBUS_TYPE_STRING, &name,
+					   DBUS_TYPE_STRING, &prev_owner,
+					   DBUS_TYPE_STRING, &new_owner,
+					   DBUS_TYPE_INVALID)) {
+			/* Ignore this error */
+			dbus_error_free(&derr);
+			return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+		}
+
+		for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next)
+		{
+			if (wpa_s->preq_notify_peer != NULL &&
+			    os_strcmp(name, wpa_s->preq_notify_peer) == 0 &&
+			    (new_owner == NULL || os_strlen(new_owner) == 0)) {
+				/* probe request owner disconnected */
+				os_free(wpa_s->preq_notify_peer);
+				wpa_s->preq_notify_peer = NULL;
+				wpas_dbus_unsubscribe_noc(priv);
+			}
+		}
+	}
+
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv)
+{
+	priv->dbus_noc_refcnt++;
+	if (priv->dbus_noc_refcnt > 1)
+		return;
+
+	if (!dbus_connection_add_filter(priv->con, noc_filter, priv, NULL)) {
+		wpa_printf(MSG_ERROR, "dbus: failed to add filter");
+		return;
+	}
+
+	dbus_bus_add_match(priv->con, wpas_dbus_noc_filter_str, NULL);
+}
+
+
+void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv)
+{
+	priv->dbus_noc_refcnt--;
+	if (priv->dbus_noc_refcnt > 0)
+		return;
+
+	dbus_bus_remove_match(priv->con, wpas_dbus_noc_filter_str, NULL);
+	dbus_connection_remove_filter(priv->con, noc_filter, priv);
+}
+
+#endif /* CONFIG_AP */
+
 
 /**
  * wpas_dbus_signal_interface - Send a interface related event signal
@@ -747,6 +834,41 @@
 	dbus_message_unref(msg);
 }
 
+
+void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
+				 const char *status, const char *parameter)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "EAP");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status)
+	    ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+					    &parameter))
+		goto nomem;
+
+	dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+	dbus_message_unref(msg);
+}
+
+
 #ifdef CONFIG_P2P
 
 /**
@@ -953,7 +1075,7 @@
 	if (os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN))
 		return -1;
 
-	memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2);
+	os_memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2);
 	group_name[2] = '\0';
 
 	os_snprintf(group_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
@@ -982,7 +1104,6 @@
 	DBusMessage *msg;
 	DBusMessageIter iter, dict_iter;
 	struct wpas_dbus_priv *iface;
-	char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 	char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 
 	iface = wpa_s->parent->global->dbus;
@@ -1020,14 +1141,8 @@
 					 client ? "client" : "GO"))
 		goto nomem;
 
-	os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-		    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
-		    wpa_s->parent->dbus_new_path, network_id);
-
 	if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
 					     group_obj_path) ||
-	   !wpa_dbus_dict_append_object_path(&dict_iter, "network_object",
-					     net_obj_path) ||
 	   !wpa_dbus_dict_close_write(&iter, &dict_iter))
 		goto nomem;
 
@@ -1607,10 +1722,12 @@
 				   enum wpas_dbus_prop property)
 {
 	char *prop;
+	dbus_bool_t flush;
 
 	if (wpa_s->dbus_new_path == NULL)
 		return; /* Skip signal since D-Bus setup is not yet ready */
 
+	flush = FALSE;
 	switch (property) {
 	case WPAS_DBUS_PROP_AP_SCAN:
 		prop = "ApScan";
@@ -1633,6 +1750,10 @@
 	case WPAS_DBUS_PROP_CURRENT_AUTH_MODE:
 		prop = "CurrentAuthMode";
 		break;
+	case WPAS_DBUS_PROP_DISCONNECT_REASON:
+		prop = "DisconnectReason";
+		flush = TRUE;
+		break;
 	default:
 		wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
 			   __func__, property);
@@ -1642,6 +1763,10 @@
 	wpa_dbus_mark_property_changed(wpa_s->global->dbus,
 				       wpa_s->dbus_new_path,
 				       WPAS_DBUS_NEW_IFACE_INTERFACE, prop);
+	if (flush) {
+		wpa_dbus_flush_object_changed_properties(
+			wpa_s->global->dbus->con, wpa_s->dbus_new_path);
+	}
 }
 
 
@@ -1792,6 +1917,15 @@
 		  END_ARGS
 	  }
 	},
+#ifdef CONFIG_AUTOSCAN
+	{ "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) &wpas_dbus_handler_autoscan,
+	  {
+		  { "arg", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_AUTOSCAN */
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
 
@@ -2029,11 +2163,11 @@
 	struct wpas_dbus_priv *ctrl_iface;
 	char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 	int ret;
+#ifdef CONFIG_P2P
 	struct wpa_ssid *ssid;
 
 	ssid = wpa_config_get_network(wpa_s->conf, nid);
 
-#ifdef CONFIG_P2P
 	/* If it is a persistent group unregister it as such */
 	if (ssid && network_is_persistent_group(ssid))
 		return wpas_dbus_unregister_persistent_group(wpa_s, nid);
@@ -2250,6 +2384,12 @@
 		  END_ARGS
 	  }
 	},
+	{ "Reassociate", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) &wpas_dbus_handler_reassociate,
+	  {
+		  END_ARGS
+	  }
+	},
 	{ "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
 	  (WPADBusMethodHandler) &wpas_dbus_handler_remove_network,
 	  {
@@ -2487,6 +2627,20 @@
 		  END_ARGS
 	  }
 	},
+#ifdef CONFIG_AP
+	{ "SubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_subscribe_preq,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "UnsubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_unsubscribe_preq,
+	  {
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_AP */
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
 
@@ -2559,6 +2713,10 @@
 	  wpas_dbus_getter_fast_reauth,
 	  wpas_dbus_setter_fast_reauth
 	},
+	{ "ScanInterval", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
+	  wpas_dbus_getter_scan_interval,
+	  wpas_dbus_setter_scan_interval
+	},
 #ifdef CONFIG_WPS
 	{ "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
 	  wpas_dbus_getter_process_credentials,
@@ -2566,9 +2724,9 @@
 	},
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
-	{ "P2PDeviceProperties", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}",
-	  wpas_dbus_getter_p2p_device_properties,
-	  wpas_dbus_setter_p2p_device_properties
+	{ "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}",
+	  wpas_dbus_getter_p2p_device_config,
+	  wpas_dbus_setter_p2p_device_config
 	},
 	{ "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
 	  wpas_dbus_getter_p2p_peers,
@@ -2591,6 +2749,10 @@
 	  NULL
 	},
 #endif /* CONFIG_P2P */
+	{ "DisconnectReason", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
+	  wpas_dbus_getter_disconnect_reason,
+	  NULL
+	},
 	{ NULL, NULL, NULL, NULL, NULL }
 };
 
@@ -2809,12 +2971,27 @@
 	  }
 	},
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_AP
+	{ "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_AP */
 	{ "Certification", WPAS_DBUS_NEW_IFACE_INTERFACE,
 	  {
 		  { "certification", "a{sv}", ARG_OUT },
 		  END_ARGS
 	  }
 	},
+	{ "EAP", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "status", "s", ARG_OUT },
+		  { "parameter", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
 	{ NULL, NULL, { END_ARGS } }
 };
 
@@ -2882,6 +3059,15 @@
 
 	wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'",
 		   wpa_s->dbus_new_path);
+
+#ifdef CONFIG_AP
+	if (wpa_s->preq_notify_peer) {
+		wpas_dbus_unsubscribe_noc(ctrl_iface);
+		os_free(wpa_s->preq_notify_peer);
+		wpa_s->preq_notify_peer = NULL;
+	}
+#endif /* CONFIG_AP */
+
 	if (wpa_dbus_unregister_object_per_iface(ctrl_iface,
 						 wpa_s->dbus_new_path))
 		return -1;
@@ -2921,11 +3107,11 @@
 	  wpas_dbus_getter_p2p_peer_group_capability,
 	  NULL
 	},
-	{ "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+	{ "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
 	  wpas_dbus_getter_p2p_peer_secondary_device_types,
 	  NULL
 	},
-	{ "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "as",
+	{ "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
 	  wpas_dbus_getter_p2p_peer_vendor_extension,
 	  NULL
 	},
@@ -3126,10 +3312,37 @@
 	  wpas_dbus_getter_p2p_group_members,
 	  NULL
 	},
-	{ "Properties",
-	  WPAS_DBUS_NEW_IFACE_P2P_GROUP, "a{sv}",
-	  wpas_dbus_getter_p2p_group_properties,
-	  wpas_dbus_setter_p2p_group_properties
+	{ "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o",
+	  wpas_dbus_getter_p2p_group,
+	  NULL
+	},
+	{ "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
+	  wpas_dbus_getter_p2p_role,
+	  NULL
+	},
+	{ "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+	  wpas_dbus_getter_p2p_group_ssid,
+	  NULL
+	},
+	{ "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+	  wpas_dbus_getter_p2p_group_bssid,
+	  NULL
+	},
+	{ "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q",
+	  wpas_dbus_getter_p2p_group_frequency,
+	  NULL
+	},
+	{ "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
+	  wpas_dbus_getter_p2p_group_passphrase,
+	  NULL
+	},
+	{ "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+	  wpas_dbus_getter_p2p_group_psk,
+	  NULL
+	},
+	{ "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay",
+	  wpas_dbus_getter_p2p_group_vendor_ext,
+	  wpas_dbus_setter_p2p_group_vendor_ext
 	},
 	{ NULL, NULL, NULL, NULL, NULL }
 };
@@ -3251,10 +3464,6 @@
 
 static const struct wpa_dbus_property_desc
 wpas_dbus_p2p_groupmember_properties[] = {
-	{ "Properties", WPAS_DBUS_NEW_IFACE_P2P_GROUPMEMBER, "a{sv}",
-	  wpas_dbus_getter_p2p_group_properties,
-	  NULL
-	},
 	{ NULL, NULL, NULL, NULL, NULL }
 };
 
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 93ce722..44cde42 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -3,14 +3,8 @@
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef CTRL_IFACE_DBUS_NEW_H
@@ -34,6 +28,7 @@
 	WPAS_DBUS_PROP_CURRENT_NETWORK,
 	WPAS_DBUS_PROP_CURRENT_AUTH_MODE,
 	WPAS_DBUS_PROP_BSSS,
+	WPAS_DBUS_PROP_DISCONNECT_REASON,
 };
 
 enum wpas_dbus_bss_prop {
@@ -115,6 +110,17 @@
 #define WPAS_DBUS_ERROR_BLOB_UNKNOWN \
 	WPAS_DBUS_NEW_INTERFACE ".BlobUnknown"
 
+#define WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE \
+	WPAS_DBUS_NEW_INTERFACE ".SubscriptionInUse"
+#define WPAS_DBUS_ERROR_NO_SUBSCRIPTION \
+	WPAS_DBUS_NEW_INTERFACE ".NoSubscription"
+#define WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM \
+	WPAS_DBUS_NEW_INTERFACE ".SubscriptionNotYou"
+
+
+void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv);
+void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv);
+
 
 #ifdef CONFIG_CTRL_IFACE_DBUS_NEW
 
@@ -210,6 +216,11 @@
 				    int depth, const char *subject,
 				    const char *cert_hash,
 				    const struct wpabuf *cert);
+void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
+			   const u8 *addr, const u8 *dst, const u8 *bssid,
+			   const u8 *ie, size_t ie_len, u32 ssi_signal);
+void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
+				 const char *status, const char *parameter);
 
 #else /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
@@ -467,6 +478,20 @@
 {
 }
 
+static inline void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
+					 const u8 *addr, const u8 *dst,
+					 const u8 *bssid,
+					 const u8 *ie, size_t ie_len,
+					 u32 ssi_signal)
+{
+}
+
+static inline void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
+					       const char *status,
+					       const char *parameter)
+{
+}
+
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
 #endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index da67bea..5668e1a 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -4,14 +4,8 @@
  * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -28,10 +22,12 @@
 #include "../bss.h"
 #include "../scan.h"
 #include "../ctrl_iface.h"
+#include "../autoscan.h"
 #include "dbus_new_helpers.h"
 #include "dbus_new.h"
 #include "dbus_new_handlers.h"
 #include "dbus_dict_helpers.h"
+#include "dbus_common_i.h"
 
 extern int wpa_debug_level;
 extern int wpa_debug_show_keys;
@@ -253,7 +249,7 @@
 
 		if ((os_strcmp(entry.key, "psk") == 0 &&
 		     value[0] == '"' && ssid->ssid_len) ||
-		    (strcmp(entry.key, "ssid") == 0 && ssid->passphrase))
+		    (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase))
 			wpa_config_update_psk(ssid);
 		else if (os_strcmp(entry.key, "priority") == 0)
 			wpa_config_update_prio_list(wpa_s->conf);
@@ -448,6 +444,76 @@
 
 
 /**
+ * wpas_dbus_simple_array_array_property_getter - Get array array type property
+ * @iter: Pointer to incoming dbus message iterator
+ * @type: DBus type of property array elements (must be basic type)
+ * @array: pointer to array of elements to put into response message
+ * @array_len: length of above array
+ * @error: a pointer to an error to fill on failure
+ * Returns: TRUE if the request succeeded, FALSE if it failed
+ *
+ * Generic getter for array type properties. Array elements type is
+ * required to be basic.
+ */
+dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
+							 const int type,
+							 struct wpabuf **array,
+							 size_t array_len,
+							 DBusError *error)
+{
+	DBusMessageIter variant_iter, array_iter;
+	char type_str[] = "aa?";
+	char inner_type_str[] = "a?";
+	const char *sub_type_str;
+	size_t i;
+
+	if (!dbus_type_is_basic(type)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: given type is not basic", __func__);
+		return FALSE;
+	}
+
+	sub_type_str = wpa_dbus_type_as_string(type);
+	type_str[2] = sub_type_str[0];
+	inner_type_str[1] = sub_type_str[0];
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      type_str, &variant_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: failed to construct message 1", __func__);
+		return FALSE;
+	}
+	if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      inner_type_str, &array_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: failed to construct message 2", __func__);
+		return FALSE;
+	}
+
+	for (i = 0; i < array_len; i++) {
+		wpa_dbus_dict_bin_array_add_element(&array_iter,
+						    wpabuf_head(array[i]),
+						    wpabuf_len(array[i]));
+
+	}
+
+	if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: failed to close message 2", __func__);
+		return FALSE;
+	}
+
+	if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: failed to close message 1", __func__);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/**
  * wpas_dbus_handler_create_interface - Request registration of a network iface
  * @message: Pointer to incoming dbus message
  * @global: %wpa_supplicant global data structure
@@ -477,25 +543,25 @@
 	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto error;
-		if (!strcmp(entry.key, "Driver") &&
+		if (!os_strcmp(entry.key, "Driver") &&
 		    (entry.type == DBUS_TYPE_STRING)) {
 			driver = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
 			if (driver == NULL)
 				goto error;
-		} else if (!strcmp(entry.key, "Ifname") &&
+		} else if (!os_strcmp(entry.key, "Ifname") &&
 			   (entry.type == DBUS_TYPE_STRING)) {
 			ifname = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
 			if (ifname == NULL)
 				goto error;
-		} else if (!strcmp(entry.key, "ConfigFile") &&
+		} else if (!os_strcmp(entry.key, "ConfigFile") &&
 			   (entry.type == DBUS_TYPE_STRING)) {
 			confname = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
 			if (confname == NULL)
 				goto error;
-		} else if (!strcmp(entry.key, "BridgeIfname") &&
+		} else if (!os_strcmp(entry.key, "BridgeIfname") &&
 			   (entry.type == DBUS_TYPE_STRING)) {
 			bridge_ifname = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
@@ -543,6 +609,7 @@
 out:
 	os_free(driver);
 	os_free(ifname);
+	os_free(confname);
 	os_free(bridge_ifname);
 	return reply;
 
@@ -804,7 +871,7 @@
 	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
 		num++;
 
-	paths = os_zalloc(num * sizeof(char*));
+	paths = os_calloc(num, sizeof(char *));
 	if (!paths) {
 		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 		return FALSE;
@@ -1092,8 +1159,9 @@
 
 #define FREQS_ALLOC_CHUNK 32
 		if (freqs_num % FREQS_ALLOC_CHUNK == 0) {
-			nfreqs = os_realloc(freqs, sizeof(int) *
-					    (freqs_num + FREQS_ALLOC_CHUNK));
+			nfreqs = os_realloc_array(
+				freqs, freqs_num + FREQS_ALLOC_CHUNK,
+				sizeof(int));
 			if (nfreqs == NULL)
 				os_free(freqs);
 			freqs = nfreqs;
@@ -1113,8 +1181,7 @@
 		dbus_message_iter_next(&array_iter);
 	}
 
-	nfreqs = os_realloc(freqs,
-			    sizeof(int) * (freqs_num + 1));
+	nfreqs = os_realloc_array(freqs, freqs_num + 1, sizeof(int));
 	if (nfreqs == NULL)
 		os_free(freqs);
 	freqs = nfreqs;
@@ -1216,6 +1283,9 @@
 			/* Add wildcard ssid */
 			params.num_ssids++;
 		}
+#ifdef CONFIG_AUTOSCAN
+		autoscan_deinit(wpa_s);
+#endif /* CONFIG_AUTOSCAN */
 		wpa_supplicant_trigger_scan(wpa_s, &params);
 	} else {
 		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
@@ -1335,6 +1405,33 @@
 
 
 /**
+ * wpas_dbus_handler_reassociate - Reassociate to current AP
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NotConnected DBus error message if not connected
+ * or NULL otherwise.
+ *
+ * Handler function for "Reassociate" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->current_ssid != NULL) {
+		wpa_s->normal_scans = 0;
+		wpa_supplicant_reinit_autoscan(wpa_s);
+		wpa_s->disconnected = 0;
+		wpa_s->reassociate = 1;
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+		return NULL;
+	}
+
+	return dbus_message_new_error(message, WPAS_DBUS_ERROR_NOT_CONNECTED,
+				      "This interface is not connected");
+}
+
+
+/**
  * wpas_dbus_handler_remove_network - Remove a configured network
  * @message: Pointer to incoming dbus message
  * @wpa_s: wpa_supplicant structure for a network interface
@@ -1736,6 +1833,54 @@
 }
 
 
+#ifdef CONFIG_AUTOSCAN
+/**
+ * wpas_dbus_handler_autoscan - Set autoscan parameters for the interface
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL
+ *
+ * Handler function for "AutoScan" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	enum wpa_states state = wpa_s->wpa_state;
+	char *arg;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg,
+			      DBUS_TYPE_INVALID);
+
+	if (arg != NULL && os_strlen(arg) > 0) {
+		char *tmp;
+		tmp = os_strdup(arg);
+		if (tmp == NULL) {
+			reply = dbus_message_new_error(message,
+						       DBUS_ERROR_NO_MEMORY,
+						       NULL);
+		} else {
+			os_free(wpa_s->conf->autoscan);
+			wpa_s->conf->autoscan = tmp;
+			if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
+				autoscan_init(wpa_s, 1);
+			else if (state == WPA_SCANNING)
+				wpa_supplicant_reinit_autoscan(wpa_s);
+		}
+	} else if (arg != NULL && os_strlen(arg) == 0) {
+		os_free(wpa_s->conf->autoscan);
+		wpa_s->conf->autoscan = NULL;
+		autoscan_deinit(wpa_s);
+	} else
+		reply = dbus_message_new_error(message,
+					       DBUS_ERROR_INVALID_ARGS,
+					       NULL);
+
+	return reply;
+}
+#endif /* CONFIG_AUTOSCAN */
+
+
 /**
  * wpas_dbus_getter_capabilities - Return interface capabilities
  * @iter: Pointer to incoming dbus message iter
@@ -1784,6 +1929,12 @@
 				goto nomem;
 		}
 
+		if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) {
+			if (!wpa_dbus_dict_string_array_add_element(
+				    &iter_array, "gcmp"))
+				goto nomem;
+		}
+
 		if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
 			if (!wpa_dbus_dict_string_array_add_element(
 				    &iter_array, "tkip"))
@@ -1825,6 +1976,12 @@
 				goto nomem;
 		}
 
+		if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) {
+			if (!wpa_dbus_dict_string_array_add_element(
+				    &iter_array, "gcmp"))
+				goto nomem;
+		}
+
 		if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
 			if (!wpa_dbus_dict_string_array_add_element(
 				    &iter_array, "tkip"))
@@ -2229,6 +2386,27 @@
 
 
 /**
+ * wpas_dbus_getter_disconnect_reason - Get most recent reason for disconnect
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "DisconnectReason" property.  The reason is negative if it is
+ * locally generated.
+ */
+dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_int32_t reason = wpa_s->disconnect_reason;
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
+						&reason, error);
+}
+
+
+/**
  * wpas_dbus_getter_bss_expire_age - Get BSS entry expiration age
  * @iter: Pointer to incoming dbus message iter
  * @error: Location to store error on failure
@@ -2292,7 +2470,7 @@
 					      void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_age;
+	dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_scan_count;
 
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
 						&expire_count, error);
@@ -2392,6 +2570,56 @@
 
 
 /**
+ * wpas_dbus_getter_scan_interval - Get scan interval
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "ScanInterval" property.
+ */
+dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_int32_t scan_interval = wpa_s->scan_interval;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
+						&scan_interval, error);
+}
+
+
+/**
+ * wpas_dbus_setter_scan_interval - Control scan interval
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "ScanInterval" property.
+ */
+dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_int32_t scan_interval;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_INT32,
+					      &scan_interval))
+		return FALSE;
+
+	if (wpa_supplicant_set_scan_interval(wpa_s, scan_interval)) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "scan_interval must be >= 0");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+
+/**
  * wpas_dbus_getter_ifname - Get interface name
  * @iter: Pointer to incoming dbus message iter
  * @error: Location to store error on failure
@@ -2571,7 +2799,7 @@
 	unsigned int i = 0;
 	dbus_bool_t success = FALSE;
 
-	paths = os_zalloc(wpa_s->num_bss * sizeof(char *));
+	paths = os_calloc(wpa_s->num_bss, sizeof(char *));
 	if (!paths) {
 		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 		return FALSE;
@@ -2634,7 +2862,7 @@
 		if (!network_is_persistent_group(ssid))
 			num++;
 
-	paths = os_zalloc(num * sizeof(char *));
+	paths = os_calloc(num, sizeof(char *));
 	if (!paths) {
 		dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
 		return FALSE;
@@ -2966,7 +3194,7 @@
 {
 	DBusMessageIter iter_dict, variant_iter;
 	const char *group;
-	const char *pairwise[2]; /* max 2 pairwise ciphers is supported */
+	const char *pairwise[3]; /* max 3 pairwise ciphers is supported */
 	const char *key_mgmt[7]; /* max 7 key managements may be supported */
 	int n;
 
@@ -3009,6 +3237,9 @@
 	case WPA_CIPHER_CCMP:
 		group = "ccmp";
 		break;
+	case WPA_CIPHER_GCMP:
+		group = "gcmp";
+		break;
 	case WPA_CIPHER_WEP104:
 		group = "wep104";
 		break;
@@ -3026,6 +3257,8 @@
 		pairwise[n++] = "tkip";
 	if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP)
 		pairwise[n++] = "ccmp";
+	if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP)
+		pairwise[n++] = "gcmp";
 
 	if (!wpa_dbus_dict_append_string_array(&iter_dict, "Pairwise",
 					       pairwise, n))
@@ -3292,3 +3525,139 @@
 	dbus_message_iter_recurse(iter, &variant_iter);
 	return set_network_properties(net->wpa_s, ssid, &variant_iter, error);
 }
+
+
+#ifdef CONFIG_AP
+
+DBusMessage * wpas_dbus_handler_subscribe_preq(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *priv = wpa_s->global->dbus;
+	char *name;
+
+	if (wpa_s->preq_notify_peer != NULL) {
+		if (os_strcmp(dbus_message_get_sender(message),
+			      wpa_s->preq_notify_peer) == 0)
+			return NULL;
+
+		return dbus_message_new_error(message,
+			WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE,
+			"Another application is already subscribed");
+	}
+
+	name = os_strdup(dbus_message_get_sender(message));
+	if (!name)
+		return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+					      "out of memory");
+
+	wpa_s->preq_notify_peer = name;
+
+	/* Subscribe to clean up if application closes socket */
+	wpas_dbus_subscribe_noc(priv);
+
+	/*
+	 * Double-check it's still alive to make sure that we didn't
+	 * miss the NameOwnerChanged signal, e.g. while strdup'ing.
+	 */
+	if (!dbus_bus_name_has_owner(priv->con, name, NULL)) {
+		/*
+		 * Application no longer exists, clean up.
+		 * The return value is irrelevant now.
+		 *
+		 * Need to check if the NameOwnerChanged handling
+		 * already cleaned up because we have processed
+		 * DBus messages while checking if the name still
+		 * has an owner.
+		 */
+		if (!wpa_s->preq_notify_peer)
+			return NULL;
+		os_free(wpa_s->preq_notify_peer);
+		wpa_s->preq_notify_peer = NULL;
+		wpas_dbus_unsubscribe_noc(priv);
+	}
+
+	return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_unsubscribe_preq(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *priv = wpa_s->global->dbus;
+
+	if (!wpa_s->preq_notify_peer)
+		return dbus_message_new_error(message,
+			WPAS_DBUS_ERROR_NO_SUBSCRIPTION,
+			"Not subscribed");
+
+	if (os_strcmp(wpa_s->preq_notify_peer,
+		      dbus_message_get_sender(message)))
+		return dbus_message_new_error(message,
+			WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM,
+			"Can't unsubscribe others");
+
+	os_free(wpa_s->preq_notify_peer);
+	wpa_s->preq_notify_peer = NULL;
+	wpas_dbus_unsubscribe_noc(priv);
+	return NULL;
+}
+
+
+void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
+			   const u8 *addr, const u8 *dst, const u8 *bssid,
+			   const u8 *ie, size_t ie_len, u32 ssi_signal)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *priv = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (priv == NULL)
+		return;
+
+	if (wpa_s->preq_notify_peer == NULL)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "ProbeRequest");
+	if (msg == NULL)
+		return;
+
+	dbus_message_set_destination(msg, wpa_s->preq_notify_peer);
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+		goto fail;
+	if (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr",
+						     (const char *) addr,
+						     ETH_ALEN))
+		goto fail;
+	if (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst",
+						    (const char *) dst,
+						    ETH_ALEN))
+		goto fail;
+	if (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
+						      (const char *) bssid,
+						      ETH_ALEN))
+		goto fail;
+	if (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies",
+							     (const char *) ie,
+							     ie_len))
+		goto fail;
+	if (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal",
+						      ssi_signal))
+		goto fail;
+	if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+		goto fail;
+
+	dbus_connection_send(priv->con, msg, NULL);
+	goto out;
+fail:
+	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+out:
+	dbus_message_unref(msg);
+}
+
+#endif /* CONFIG_AP */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index c0272d5..178a76b 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -3,14 +3,8 @@
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef CTRL_IFACE_DBUS_NEW_HANDLERS_H
@@ -41,6 +35,12 @@
 						   size_t array_len,
 						   DBusError *error);
 
+dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
+							 const int type,
+							 struct wpabuf **array,
+							 size_t array_len,
+							 DBusError *error);
+
 DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
 						 struct wpa_global *global);
 
@@ -94,6 +94,9 @@
 DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
 					    struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
 					       struct wpa_supplicant *wpa_s);
 
@@ -118,6 +121,9 @@
 DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
 					  struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s);
+
 dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
 					  DBusError *error, void *user_data);
 
@@ -141,6 +147,10 @@
 					 DBusError *error,
 					 void *user_data);
 
+dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data);
+
 dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
 					    DBusError *error, void *user_data);
 
@@ -162,6 +172,14 @@
 dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
 				     void *user_data);
 
+dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data);
+
+dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data);
+
 dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
 				    void *user_data);
 
@@ -252,4 +270,9 @@
 DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
 					    const char *arg);
 
+DBusMessage * wpas_dbus_handler_subscribe_preq(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_unsubscribe_preq(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
 #endif /* CTRL_IFACE_DBUS_HANDLERS_NEW_H */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index 79373b4..30e0eb3 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -1,14 +1,9 @@
 /*
  * WPA Supplicant / dbus-based control interface (P2P)
+ * Copyright (c) 2011-2012, Intel Corporation
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -45,7 +40,7 @@
 
 	if (!peer_path)
 		return -1;
-	p = strrchr(peer_path, '/');
+	p = os_strrchr(peer_path, '/');
 	if (!p)
 		return -1;
 	p++;
@@ -132,7 +127,7 @@
 	}
 
 	wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types,
-		      NULL);
+		      NULL, 0);
 	os_free(req_dev_types);
 	return reply;
 
@@ -351,13 +346,13 @@
 		if (ssid == NULL || ssid->disabled != 2)
 			goto inv_args;
 
-		if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq)) {
+		if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0)) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
 			goto out;
 		}
-	} else if (wpas_p2p_group_add(wpa_s, persistent_group, freq))
+	} else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0))
 		goto inv_args;
 
 out:
@@ -508,8 +503,8 @@
 		goto inv_args;
 
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
-				   persistent_group, join, authorize_only,
-				   go_intent, freq);
+				   persistent_group, 0, join, authorize_only,
+				   go_intent, freq, -1, 0, 0);
 
 	if (new_pin >= 0) {
 		char npin[9];
@@ -635,7 +630,7 @@
 		if (ssid == NULL || ssid->disabled != 2)
 			goto err;
 
-		if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL) < 0) {
+		if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0) < 0) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
@@ -692,7 +687,8 @@
 	    os_strcmp(config_method, "pushbutton"))
 		return wpas_dbus_error_invalid_args(message, NULL);
 
-	if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, 0) < 0)
+	if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method,
+			       WPAS_P2P_PD_FOR_GO_NEG) < 0)
 		return wpas_dbus_error_unknown_error(message,
 				"Failed to send provision discovery request");
 
@@ -704,9 +700,9 @@
  * P2P Device property accessor methods.
  */
 
-dbus_bool_t wpas_dbus_getter_p2p_device_properties(DBusMessageIter *iter,
-						   DBusError *error,
-						   void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	DBusMessageIter variant_iter, dict_iter;
@@ -782,7 +778,7 @@
 		goto err_no_mem;
 
 	/* Persistent Reconnect */
-	if (!wpa_dbus_dict_append_bool(&dict_iter, "PersistantReconnect",
+	if (!wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect",
 				       wpa_s->conf->persistent_reconnect))
 		goto err_no_mem;
 
@@ -839,9 +835,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_setter_p2p_device_properties(DBusMessageIter *iter,
-						   DBusError *error,
-						   void *user_data)
+dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	DBusMessageIter variant_iter, iter_dict;
@@ -927,7 +923,7 @@
 			   (entry.type == DBUS_TYPE_UINT32) &&
 			   (entry.uint32_value <= 15))
 			wpa_s->conf->p2p_go_intent = entry.uint32_value;
-		else if ((os_strcmp(entry.key, "PersistantReconnect") == 0) &&
+		else if ((os_strcmp(entry.key, "PersistentReconnect") == 0) &&
 			 (entry.type == DBUS_TYPE_BOOLEAN))
 			wpa_s->conf->persistent_reconnect = entry.bool_value;
 		else if ((os_strcmp(entry.key, "ListenRegClass") == 0) &&
@@ -1053,7 +1049,7 @@
 	 * Now construct the peer object paths in a form suitable for
 	 * array_property_getter helper below.
 	 */
-	peer_obj_paths = os_zalloc(num * sizeof(char *));
+	peer_obj_paths = os_calloc(num, sizeof(char *));
 
 	if (!peer_obj_paths) {
 		out_of_mem = 1;
@@ -1140,13 +1136,18 @@
 				       void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
+	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX];
+	char *dbus_groupobj_path = path_buf;
 
 	if (wpa_s->dbus_groupobj_path == NULL)
-		return FALSE;
+		os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			    "/");
+	else
+		os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s", wpa_s->dbus_groupobj_path);
 
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
-						&wpa_s->dbus_groupobj_path,
-						error);
+						&dbus_groupobj_path, error);
 }
 
 
@@ -1157,11 +1158,13 @@
 	char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 
 	if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_CLIENT)
-		return FALSE;
+		os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
+	else
+		os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+			    COMPACT_MACSTR,
+			    wpa_s->dbus_new_path, MAC2STR(wpa_s->go_dev_addr));
 
-	os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
-		    wpa_s->dbus_new_path, MAC2STR(wpa_s->go_dev_addr));
 	path = go_peer_obj_path;
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
 						&path, error);
@@ -1341,6 +1344,7 @@
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
+	DBusMessageIter variant_iter, array_iter;
 
 	info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
 				  peer_args->p2p_device_addr, 0);
@@ -1350,29 +1354,80 @@
 		return FALSE;
 	}
 
-	if (info->wps_sec_dev_type_list_len) {
-		const u8 *sec_dev_type_list = info->wps_sec_dev_type_list;
-		int num_sec_dev_types = info->wps_sec_dev_type_list_len;
-
-		if (!wpas_dbus_simple_array_property_getter(iter,
-							    DBUS_TYPE_BYTE,
-							    sec_dev_type_list,
-							    num_sec_dev_types,
-							    error))
-			goto err_no_mem;
-		else
-			return TRUE;
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &variant_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+		               "%s: failed to construct message 1", __func__);
+		return FALSE;
 	}
 
-	if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, NULL,
-						    0, error))
-		goto err_no_mem;
+	if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      DBUS_TYPE_ARRAY_AS_STRING
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &array_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+		               "%s: failed to construct message 2", __func__);
+		return FALSE;
+	}
+
+	if (info->wps_sec_dev_type_list_len) {
+		const u8 *sec_dev_type_list = info->wps_sec_dev_type_list;
+		int num_sec_device_types =
+			info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN;
+		int i;
+		DBusMessageIter inner_array_iter;
+
+		for (i = 0; i < num_sec_device_types; i++) {
+			if (!dbus_message_iter_open_container(
+				    &array_iter, DBUS_TYPE_ARRAY,
+				    DBUS_TYPE_BYTE_AS_STRING,
+				    &inner_array_iter)) {
+				dbus_set_error(error, DBUS_ERROR_FAILED,
+					       "%s: failed to construct "
+					       "message 3 (%d)",
+					       __func__, i);
+				return FALSE;
+			}
+
+			if (!dbus_message_iter_append_fixed_array(
+				    &inner_array_iter, DBUS_TYPE_BYTE,
+				    &sec_dev_type_list, WPS_DEV_TYPE_LEN)) {
+				dbus_set_error(error, DBUS_ERROR_FAILED,
+					       "%s: failed to construct "
+					       "message 4 (%d)",
+					       __func__, i);
+				return FALSE;
+			}
+
+			if (!dbus_message_iter_close_container(
+				    &array_iter, &inner_array_iter)) {
+				dbus_set_error(error, DBUS_ERROR_FAILED,
+					       "%s: failed to construct "
+					       "message 5 (%d)",
+					       __func__, i);
+				return FALSE;
+			}
+
+			sec_dev_type_list += WPS_DEV_TYPE_LEN;
+		}
+	}
+
+	if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+		               "%s: failed to construct message 6", __func__);
+		return FALSE;
+	}
+
+	if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+		               "%s: failed to construct message 7", __func__);
+		return FALSE;
+	}
 
 	return TRUE;
-
-err_no_mem:
-	dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
-	return FALSE;
 }
 
 
@@ -1380,7 +1435,7 @@
 						       DBusError *error,
 						       void *user_data)
 {
-	const struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
+	struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
 	int i, num;
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1401,12 +1456,10 @@
 		num++;
 	}
 
-	if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_STRING,
-						    vendor_extension, num,
-						    error)) {
-		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+	if (!wpas_dbus_simple_array_array_property_getter(iter, DBUS_TYPE_BYTE,
+							  vendor_extension,
+							  num, error))
 		return FALSE;
-	}
 
 	return TRUE;
 }
@@ -1415,10 +1468,12 @@
 dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
 					  DBusError *error, void *user_data)
 {
+	dbus_bool_t success;
 	/* struct peer_handler_args *peer_args = user_data; */
 
-	dbus_set_error_const(error, DBUS_ERROR_FAILED, "not implemented");
-	return FALSE;
+	success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+							 NULL, 0, error);
+	return success;
 }
 
 
@@ -1454,7 +1509,7 @@
 		if (network_is_persistent_group(ssid))
 			num++;
 
-	paths = os_zalloc(num * sizeof(char *));
+	paths = os_calloc(num, sizeof(char *));
 	if (!paths) {
 		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 		return FALSE;
@@ -1746,9 +1801,11 @@
 	const u8 *addr;
 	dbus_bool_t success = FALSE;
 
-	/* Ensure we are a GO */
-	if (wpa_s->wpa_state != WPA_COMPLETED)
-		return FALSE;
+	/* Verify correct role for this property */
+	if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_GO) {
+		return wpas_dbus_simple_array_property_getter(
+			iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error);
+	}
 
 	ssid = wpa_s->conf->ssid;
 	/* At present WPAS P2P_GO mode only applicable for p2p_go */
@@ -1759,7 +1816,7 @@
 
 	num_members = p2p_get_group_num_members(wpa_s->p2p_group);
 
-	paths = os_zalloc(num_members * sizeof(char *));
+	paths = os_calloc(num_members, sizeof(char *));
 	if (!paths)
 		goto out_of_memory;
 
@@ -1796,111 +1853,145 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_properties(DBusMessageIter *iter,
+dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
+					    DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	if (wpa_s->current_ssid == NULL)
+		return FALSE;
+	return wpas_dbus_simple_array_property_getter(
+		iter, DBUS_TYPE_BYTE, wpa_s->current_ssid->ssid,
+		wpa_s->current_ssid->ssid_len, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	u8 role = wpas_get_p2p_role(wpa_s);
+	u8 *p_bssid;
+
+	if (role == WPAS_P2P_ROLE_CLIENT) {
+		if (wpa_s->current_ssid == NULL)
+			return FALSE;
+		p_bssid = wpa_s->current_ssid->bssid;
+	} else {
+		if (wpa_s->ap_iface == NULL)
+			return FALSE;
+		p_bssid = wpa_s->ap_iface->bss[0]->own_addr;
+	}
+
+	return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						      p_bssid, ETH_ALEN,
+						      error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
+						 DBusError *error,
+						 void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	u16 op_freq;
+	u8 role = wpas_get_p2p_role(wpa_s);
+
+	if (role == WPAS_P2P_ROLE_CLIENT) {
+		if (wpa_s->go_params == NULL)
+			return FALSE;
+		op_freq = wpa_s->go_params->freq;
+	} else {
+		if (wpa_s->ap_iface == NULL)
+			return FALSE;
+		op_freq = wpa_s->ap_iface->freq;
+	}
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+						&op_freq, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
 						  DBusError *error,
 						  void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	DBusMessageIter variant_iter, dict_iter;
-	struct hostapd_data *hapd = NULL;
-	const struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
-	int num_vendor_ext = 0;
-	int i;
 	u8 role = wpas_get_p2p_role(wpa_s);
-	u16 op_freq = 0;
-	u8 *p_bssid = NULL;
-	char *role_name = NULL;
+	char *p_pass = NULL;
 
-	if (!wpa_s->current_ssid)
-		return FALSE;
-
-	/* Check current role and adjust information accordingly */
-	switch (role) {
-	case WPAS_P2P_ROLE_CLIENT:
-		/* go_params is only valid for a client */
-		if (wpa_s->go_params) {
-			op_freq = wpa_s->go_params->freq;
-			p_bssid = wpa_s->current_ssid->bssid;
-			role_name = "client";
-		} else
-			return FALSE;
-		break;
-	case WPAS_P2P_ROLE_GO:
-		/* ap_iface is only valid for a GO */
-		if (wpa_s->ap_iface) {
-			hapd = wpa_s->ap_iface->bss[0];
-			p_bssid = hapd->own_addr;
-			op_freq = wpa_s->ap_iface->freq;
-			role_name = "GO";
-		} else
-			return FALSE;
-		break;
-	default:
-		/* Error condition; this should NEVER occur */
-		return FALSE;
-	}
-
-	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
-					      "a{sv}", &variant_iter) ||
-	    !wpa_dbus_dict_open_write(&variant_iter, &dict_iter))
-		goto err_no_mem;
-	/* Provide the SSID */
-	if (!wpa_dbus_dict_append_byte_array(
-		    &dict_iter, "SSID",
-		    (const char *) wpa_s->current_ssid->ssid,
-		    wpa_s->current_ssid->ssid_len))
-		goto err_no_mem;
-	/* Provide the BSSID */
-	if (p_bssid &&
-	    !wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID",
-					     (const char *) p_bssid, ETH_ALEN))
-		goto err_no_mem;
-	/* Provide the role within the group */
-	if (role_name &&
-	    !wpa_dbus_dict_append_string(&dict_iter, "Role", role_name))
-		goto err_no_mem;
-	/* Provide the operational frequency */
-	if (!wpa_dbus_dict_append_uint16(&dict_iter, "Frequency", op_freq))
-		goto err_no_mem;
-
-	/* Additional information for group owners */
+	/* Verify correct role for this property */
 	if (role == WPAS_P2P_ROLE_GO) {
-		/* Provide the passphrase */
-		if (!wpa_dbus_dict_append_string(&dict_iter, "Passphrase",
-					wpa_s->current_ssid->passphrase))
-			goto err_no_mem;
-		/* Parse WPS Vendor Extensions sent in Beacon/Probe Response */
-		for (i = 0; hapd && i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
-			if (hapd->conf->wps_vendor_ext[i] == NULL)
-				continue;
-			vendor_ext[num_vendor_ext++] =
-				hapd->conf->wps_vendor_ext[i];
-		}
-		if (!wpa_dbus_dict_append_wpabuf_array(&dict_iter,
-					"WPSVendorExtensions",
-					vendor_ext, num_vendor_ext))
-			goto err_no_mem;
-	} else {
-		/* If not a GO, provide the PSK */
-		if (!wpa_dbus_dict_append_byte_array(
-			    &dict_iter, "PSK",
-			    (const char *) wpa_s->current_ssid->psk, 32))
-			goto err_no_mem;
-	}
+		if (wpa_s->current_ssid == NULL)
+			return FALSE;
+		p_pass = wpa_s->current_ssid->passphrase;
+	} else
+		p_pass = "";
 
-	if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
-	    !dbus_message_iter_close_container(iter, &variant_iter))
-		goto err_no_mem;
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&p_pass, error);
 
-	return TRUE;
-
-err_no_mem:
-	dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
-	return FALSE;
 }
 
 
-dbus_bool_t wpas_dbus_setter_p2p_group_properties(DBusMessageIter *iter,
+dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
+					   DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	u8 role = wpas_get_p2p_role(wpa_s);
+	u8 *p_psk = NULL;
+	u8 psk_len = 0;
+
+	/* Verify correct role for this property */
+	if (role == WPAS_P2P_ROLE_CLIENT) {
+		if (wpa_s->current_ssid == NULL)
+			return FALSE;
+		p_psk = wpa_s->current_ssid->psk;
+		psk_len = 32;
+	}
+
+	return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						      &p_psk, psk_len, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct hostapd_data *hapd;
+	struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+	int num_vendor_ext = 0;
+	int i;
+
+	/* Verify correct role for this property */
+	if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO) {
+		if (wpa_s->ap_iface == NULL)
+			return FALSE;
+		hapd = wpa_s->ap_iface->bss[0];
+
+		/* Parse WPS Vendor Extensions sent in Beacon/Probe Response */
+		for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+			if (hapd->conf->wps_vendor_ext[i] == NULL)
+				vendor_ext[i] = NULL;
+			else {
+				vendor_ext[num_vendor_ext++] =
+					hapd->conf->wps_vendor_ext[i];
+			}
+		}
+	}
+
+	/* Return vendor extensions or no data */
+	return wpas_dbus_simple_array_array_property_getter(iter,
+							    DBUS_TYPE_BYTE,
+							    vendor_ext,
+							    num_vendor_ext,
+						 error);
+}
+
+
+dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
 						  DBusError *error,
 						  void *user_data)
 {
@@ -1978,7 +2069,7 @@
 	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 		goto error;
 
-	if (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto error;
 
@@ -1990,23 +2081,30 @@
 				bonjour = 1;
 			else
 				goto error_clear;
-			wpa_dbus_dict_entry_clear(&entry);
+		} else if (!os_strcmp(entry.key, "version") &&
+		           entry.type == DBUS_TYPE_INT32) {
+			version = entry.uint32_value;
+		} else if (!os_strcmp(entry.key, "service") &&
+			     (entry.type == DBUS_TYPE_STRING)) {
+			service = os_strdup(entry.str_value);
+		} else if (!os_strcmp(entry.key, "query")) {
+			if ((entry.type != DBUS_TYPE_ARRAY) ||
+			    (entry.array_type != DBUS_TYPE_BYTE))
+				goto error_clear;
+			query = wpabuf_alloc_copy(
+				entry.bytearray_value,
+				entry.array_len);
+		} else if (!os_strcmp(entry.key, "response")) {
+			if ((entry.type != DBUS_TYPE_ARRAY) ||
+			    (entry.array_type != DBUS_TYPE_BYTE))
+				goto error_clear;
+			resp = wpabuf_alloc_copy(entry.bytearray_value,
+						 entry.array_len);
 		}
+		wpa_dbus_dict_entry_clear(&entry);
 	}
 
 	if (upnp == 1) {
-		while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
-			if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
-				goto error;
-
-			if (!os_strcmp(entry.key, "version") &&
-			    entry.type == DBUS_TYPE_INT32)
-				version = entry.uint32_value;
-			else if (!os_strcmp(entry.key, "service") &&
-				 entry.type == DBUS_TYPE_STRING)
-				service = os_strdup(entry.str_value);
-			wpa_dbus_dict_entry_clear(&entry);
-		}
 		if (version <= 0 || service == NULL)
 			goto error;
 
@@ -2014,37 +2112,15 @@
 			goto error;
 
 		os_free(service);
+		service = NULL;
 	} else if (bonjour == 1) {
-		while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
-			if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
-				goto error;
-
-			if (!os_strcmp(entry.key, "query")) {
-				if ((entry.type != DBUS_TYPE_ARRAY) ||
-				    (entry.array_type != DBUS_TYPE_BYTE))
-					goto error_clear;
-				query = wpabuf_alloc_copy(
-					entry.bytearray_value,
-					entry.array_len);
-			} else if (!os_strcmp(entry.key, "response")) {
-				if ((entry.type != DBUS_TYPE_ARRAY) ||
-				    (entry.array_type != DBUS_TYPE_BYTE))
-					goto error_clear;
-				resp = wpabuf_alloc_copy(entry.bytearray_value,
-							 entry.array_len);
-			}
-
-			wpa_dbus_dict_entry_clear(&entry);
-		}
-
 		if (query == NULL || resp == NULL)
 			goto error;
 
-		if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) {
-			wpabuf_free(query);
-			wpabuf_free(resp);
+		if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0)
 			goto error;
-		}
+		query = NULL;
+		resp = NULL;
 	} else
 		goto error;
 
@@ -2052,6 +2128,9 @@
 error_clear:
 	wpa_dbus_dict_entry_clear(&entry);
 error:
+	os_free(service);
+	wpabuf_free(query);
+	wpabuf_free(resp);
 	return wpas_dbus_error_invalid_args(message, NULL);
 }
 
@@ -2170,7 +2249,7 @@
 	struct wpabuf *tlv = NULL;
 	u8 version = 0;
 	u64 ref = 0;
-	u8 addr[ETH_ALEN];
+	u8 addr_buf[ETH_ALEN], *addr;
 
 	dbus_message_iter_init(message, &iter);
 
@@ -2207,10 +2286,15 @@
 		wpa_dbus_dict_entry_clear(&entry);
 	}
 
-	if (!peer_object_path ||
-	    (parse_peer_object_path(peer_object_path, addr) < 0) ||
-	    !p2p_peer_known(wpa_s->global->p2p, addr))
-		goto error;
+	if (!peer_object_path) {
+		addr = NULL;
+	} else {
+		if (parse_peer_object_path(peer_object_path, addr_buf) < 0 ||
+		    !p2p_peer_known(wpa_s->global->p2p, addr_buf))
+			goto error;
+
+		addr = addr_buf;
+	}
 
 	if (upnp == 1) {
 		if (version <= 0 || service == NULL)
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
index 293eb6b..a11b3c8 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
@@ -1,15 +1,9 @@
-
 /*
  * WPA Supplicant / dbus-based control interface for p2p
+ * Copyright (c) 2011-2012, Intel Corporation
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef DBUS_NEW_HANDLERS_P2P_H
@@ -94,13 +88,13 @@
 /*
  * P2P Device property accessor methods.
  */
-dbus_bool_t wpas_dbus_setter_p2p_device_properties(DBusMessageIter *iter,
-						   DBusError *error,
-						   void *user_data);
+dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data);
 
-dbus_bool_t wpas_dbus_getter_p2p_device_properties(DBusMessageIter *iter,
-						   DBusError *error,
-						   void *user_data);
+dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
 				       void *user_data);
@@ -161,11 +155,31 @@
 					       DBusError *error,
 					       void *user_data);
 
-dbus_bool_t wpas_dbus_getter_p2p_group_properties(DBusMessageIter *iter,
+dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
+					    DBusError *error,
+					    void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
+						 DBusError *error,
+						 void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
 						  DBusError *error,
 						  void *user_data);
 
-dbus_bool_t wpas_dbus_setter_p2p_group_properties(DBusMessageIter *iter,
+dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
+					   DBusError *error,
+					   void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data);
+
+dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
 						  DBusError *error,
 						  void *user_data);
 
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
index a72cfb3..8489ce7 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
@@ -3,14 +3,8 @@
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index e254365..cfa6a15 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -3,14 +3,8 @@
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h
index d6e7b48..6d31ad5 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.h
+++ b/wpa_supplicant/dbus/dbus_new_helpers.h
@@ -3,14 +3,8 @@
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef WPA_DBUS_CTRL_H
diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c
index d443269..3b090c0 100644
--- a/wpa_supplicant/dbus/dbus_new_introspect.c
+++ b/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -4,14 +4,8 @@
  * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
  * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
diff --git a/wpa_supplicant/dbus/dbus_old.c b/wpa_supplicant/dbus/dbus_old.c
index 71ab61e..5f298e7 100644
--- a/wpa_supplicant/dbus/dbus_old.c
+++ b/wpa_supplicant/dbus/dbus_old.c
@@ -2,14 +2,8 @@
  * WPA Supplicant / dbus-based control interface
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/wpa_supplicant/dbus/dbus_old.h b/wpa_supplicant/dbus/dbus_old.h
index 9523867..e668231 100644
--- a/wpa_supplicant/dbus/dbus_old.h
+++ b/wpa_supplicant/dbus/dbus_old.h
@@ -2,14 +2,8 @@
  * WPA Supplicant / dbus-based control interface
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef CTRL_IFACE_DBUS_H
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c
index 8370a95..e217a72 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers.c
+++ b/wpa_supplicant/dbus/dbus_old_handlers.c
@@ -2,14 +2,8 @@
  * WPA Supplicant / dbus-based control interface
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.h b/wpa_supplicant/dbus/dbus_old_handlers.h
index 009e807..825bc6d 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers.h
+++ b/wpa_supplicant/dbus/dbus_old_handlers.h
@@ -2,14 +2,8 @@
  * WPA Supplicant / dbus-based control interface
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef CTRL_IFACE_DBUS_HANDLERS_H
diff --git a/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/wpa_supplicant/dbus/dbus_old_handlers_wps.c
index c04b844..bb79382 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_old_handlers_wps.c
@@ -2,14 +2,8 @@
  * WPA Supplicant / dbus-based control interface (WPS)
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in
new file mode 100644
index 0000000..a75918f
--- /dev/null
+++ b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=fi.epitest.hostap.WPASupplicant
+Exec=@BINDIR@/wpa_supplicant -u
+User=root
+SystemdService=wpa_supplicant.service
diff --git a/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in
new file mode 100644
index 0000000..d97ff39
--- /dev/null
+++ b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=fi.w1.wpa_supplicant1
+Exec=@BINDIR@/wpa_supplicant -u
+User=root
+SystemdService=wpa_supplicant.service
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 480bc64..711b407 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -204,6 +204,8 @@
 # Disable credentials for an open network by default when acting as a WPS
 # registrar.
 #CONFIG_WPS_REG_DISABLE_OPEN=y
+# Enable WPS support with NFC config method
+#CONFIG_WPS_NFC=y
 
 # EAP-IKEv2
 #CONFIG_EAP_IKEV2=y
@@ -230,6 +232,7 @@
 # unix = UNIX domain sockets (default for Linux/*BSD)
 # udp = UDP sockets using localhost (127.0.0.1)
 # named_pipe = Windows Named Pipe (default for Windows)
+# udp-remote = UDP sockets with remote access (only for tests systems/purpose)
 # y = use default (backwards compatibility)
 # If this option is commented out, control interface is not included in the
 # build.
@@ -321,9 +324,7 @@
 # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
 CONFIG_PEERKEY=y
 
-# IEEE 802.11w (management frame protection)
-# This version is an experimental implementation based on IEEE 802.11w/D1.0
-# draft and is subject to change since the standard has not yet been finalized.
+# IEEE 802.11w (management frame protection), also known as PMF
 # Driver support is also needed for IEEE 802.11w.
 #CONFIG_IEEE80211W=y
 
@@ -413,6 +414,12 @@
 # Set syslog facility for debug messages
 #CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON
 
+# Add support for sending all debug messages (regardless of debug verbosity)
+# to the Linux kernel tracing facility. This helps debug the entire stack by
+# making it easy to record everything happening from the driver up into the
+# same file, e.g., using trace-cmd.
+#CONFIG_DEBUG_LINUX_TRACING=y
+
 # Enable privilege separation (see README 'Privilege separation' for details)
 #CONFIG_PRIVSEP=y
 
@@ -424,7 +431,7 @@
 # This tracks use of memory allocations and other registrations and reports
 # incorrect use with a backtrace of call (or allocation) location.
 #CONFIG_WPA_TRACE=y
-# For BSD, comment out these.
+# For BSD, uncomment these.
 #LIBS += -lexecinfo
 #LIBS_p += -lexecinfo
 #LIBS_c += -lexecinfo
@@ -433,7 +440,7 @@
 # This enables use of libbfd to get more detailed symbols for the backtraces
 # generated by CONFIG_WPA_TRACE=y.
 #CONFIG_WPA_TRACE_BFD=y
-# For BSD, comment out these.
+# For BSD, uncomment these.
 #LIBS += -lbfd -liberty -lz
 #LIBS_p += -lbfd -liberty -lz
 #LIBS_c += -lbfd -liberty -lz
@@ -472,8 +479,45 @@
 # IEEE 802.11n (High Throughput) support (mainly for AP mode)
 #CONFIG_IEEE80211N=y
 
+# Wireless Network Management (IEEE Std 802.11v-2011)
+# Note: This is experimental and not complete implementation.
+#CONFIG_WNM=y
+
 # Interworking (IEEE 802.11u)
 # This can be used to enable functionality to improve interworking with
 # external networks (GAS/ANQP to learn more about the networks and network
 # selection based on available credentials).
 #CONFIG_INTERWORKING=y
+
+# Hotspot 2.0
+#CONFIG_HS20=y
+
+# AP mode operations with wpa_supplicant
+# This can be used for controlling AP mode operations with wpa_supplicant. It
+# should be noted that this is mainly aimed at simple cases like
+# WPA2-Personal while more complex configurations like WPA2-Enterprise with an
+# external RADIUS server can be supported with hostapd.
+#CONFIG_AP=y
+
+# P2P (Wi-Fi Direct)
+# This can be used to enable P2P support in wpa_supplicant. See README-P2P for
+# more information on P2P operations.
+#CONFIG_P2P=y
+
+# Autoscan
+# This can be used to enable automatic scan support in wpa_supplicant.
+# See wpa_supplicant.conf for more information on autoscan usage.
+#
+# Enabling directly a module will enable autoscan support.
+# For exponential module:
+#CONFIG_AUTOSCAN_EXPONENTIAL=y
+# For periodic module:
+#CONFIG_AUTOSCAN_PERIODIC=y
+
+# Password (and passphrase, etc.) backend for external storage
+# These optional mechanisms can be used to add support for storing passwords
+# and other secrets in external (to wpa_supplicant) location. This allows, for
+# example, operating system specific key storage to be used
+#
+# External password backend for testing purposes (developer use)
+#CONFIG_EXT_PASSWORD_TEST=y
diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml
index f47235b..eb3a089 100644
--- a/wpa_supplicant/doc/docbook/wpa_background.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_background.sgml
@@ -90,12 +90,12 @@
 
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2007,
+    <para>wpa_supplicant is copyright (c) 2003-2012,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
 
-    <para>This program is dual-licensed under both the GPL version 2
-    and BSD license. Either license may be used at your option.</para>
+    <para>This program is licensed under the BSD license (the one with
+    advertisement clause removed).</para>
   </refsect1>
 </refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml
index 1fe98f4..c080c07 100644
--- a/wpa_supplicant/doc/docbook/wpa_cli.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml
@@ -328,12 +328,12 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2007,
+    <para>wpa_supplicant is copyright (c) 2003-2012,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
 
-    <para>This program is dual-licensed under both the GPL version 2
-    and BSD license. Either license may be used at your option.</para>
+    <para>This program is licensed under the BSD license (the one with
+    advertisement clause removed).</para>
   </refsect1>
 </refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml
index 41b5849..0ab6419 100644
--- a/wpa_supplicant/doc/docbook/wpa_gui.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml
@@ -74,12 +74,12 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2007,
+    <para>wpa_supplicant is copyright (c) 2003-2012,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
 
-    <para>This program is dual-licensed under both the GPL version 2
-    and BSD license. Either license may be used at your option.</para>
+    <para>This program is licensed under the BSD license (the one with
+    advertisement clause removed).</para>
   </refsect1>
 </refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
index 402ea09..336c03b 100644
--- a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
@@ -62,12 +62,12 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2007,
+    <para>wpa_supplicant is copyright (c) 2003-2012,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
 
-    <para>This program is dual-licensed under both the GPL version 2
-    and BSD license. Either license may be used at your option.</para>
+    <para>This program is licensed under the BSD license (the one with
+    advertisement clause removed).</para>
   </refsect1>
 </refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml
index 89b8a92..eb907a8 100644
--- a/wpa_supplicant/doc/docbook/wpa_priv.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml
@@ -137,12 +137,12 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2007,
+    <para>wpa_supplicant is copyright (c) 2003-2012,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
 
-    <para>This program is dual-licensed under both the GPL version 2
-    and BSD license. Either license may be used at your option.</para>
+    <para>This program is licensed under the BSD license (the one with
+    advertisement clause removed).</para>
   </refsect1>
 </refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
index 0ab4e15..aa20e57 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -246,28 +246,6 @@
 
     <variablelist>
       <varlistentry>
-	<term>hostap</term>
-	<listitem>
-	  <para>(default) Host AP driver (Intersil Prism2/2.5/3).
-  	  (this can also be used with Linuxant DriverLoader).</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
-	<term>hermes</term>
-	<listitem>
-	  <para>Agere Systems Inc. driver (Hermes-I/Hermes-II).</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
-	<term>madwifi</term>
-	<listitem>
-	  <para>MADWIFI 802.11 support (Atheros, etc.).</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
 	<term>wext</term>
 	<listitem>
 	  <para>Linux wireless extensions (generic).</para>
@@ -275,13 +253,6 @@
       </varlistentry>
 
       <varlistentry>
-	<term>broadcom</term>
-	<listitem>
-	  <para>Broadcom wl.o driver.</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
 	<term>wired</term>
 	<listitem>
 	  <para>wpa_supplicant wired Ethernet driver</para>
@@ -411,7 +382,7 @@
       <varlistentry>
 	<term>-L</term>
 	<listitem>
-	  <para>Show license (GPL and BSD).</para>
+	  <para>Show license (BSD).</para>
 	</listitem>
       </varlistentry>
 
@@ -506,8 +477,8 @@
 
 <blockquote><programlisting>
 wpa_supplicant \
-	-c wpa1.conf -i wlan0 -D hostap -N \
-	-c wpa2.conf -i ath0 -D madwifi
+	-c wpa1.conf -i wlan0 -D nl80211 -N \
+	-c wpa2.conf -i ath0 -D wext
 </programlisting></blockquote>
   </refsect1>
 
@@ -537,86 +508,6 @@
     <title>Supported Drivers</title>
     <variablelist>
       <varlistentry>
-	<term>Host AP driver for Prism2/2.5/3 (development
-	snapshot/v0.2.x)</term>
-	<listitem>
-	  <para> (http://hostap.epitest.fi/) Driver needs to be set in
-	  Managed mode (<emphasis>iwconfig wlan0 mode managed</emphasis>).
-	  Please note that station firmware version needs to be 1.7.0 or
-	  newer to work in WPA mode.</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
-	<term>Linuxant DriverLoader</term>
-	<listitem>
-	  <para>(http://www.linuxant.com/driverloader/)
-	with Windows NDIS driver for your wlan card supporting WPA.</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
-	<term>Agere Systems Inc. Linux Driver</term>
-	<listitem>
-	  <para> (http://www.agere.com/support/drivers/) Please note
-	that the driver interface file (driver_hermes.c) and hardware
-	specific include files are not included in the wpa_supplicant
-	distribution. You will need to copy these from the source
-	package of the Agere driver.</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
-	<term>madwifi driver for cards based on Atheros chip set (ar521x)</term>
-	<listitem>
-	  <para> (http://sourceforge.net/projects/madwifi/) Please
-	note that you will need to modify the wpa_supplicant .config
-	file to use the correct path for the madwifi driver root
-	directory (CFLAGS += -I../madwifi/wpa line in example
-	defconfig).</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
-	<term>Linux ndiswrapper</term>
-	<listitem>
-	  <para> (http://ndiswrapper.sourceforge.net/) with Windows
-	NDIS driver.</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
-	<term>Broadcom wl.o driver</term>
-	<listitem>
-	  <para> This is a generic Linux driver for Broadcom IEEE
-	802.11a/g cards.  However, it is proprietary driver that is
-	not publicly available except for couple of exceptions, mainly
-	Broadcom-based APs/wireless routers that use Linux. The driver
-	binary can be downloaded, e.g., from Linksys support site
-	(http://www.linksys.com/support/gpl.asp) for Linksys
-	WRT54G. The GPL tarball includes cross-compiler and the needed
-	header file, wlioctl.h, for compiling wpa_supplicant.  This
-	driver support in wpa_supplicant is expected to work also with
-	other devices based on Broadcom driver (assuming the driver
-	includes client mode support).</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
-	<term> Intel ipw2100 driver</term>
-	<listitem>
-	  <para> (http://sourceforge.net/projects/ipw2100/)</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
-	<term>Intel ipw2200 driver</term>
-	<listitem>
-	  <para> (http://sourceforge.net/projects/ipw2200/)</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
 	<term>Linux wireless extensions</term>
 	<listitem>
 	  <para>In theory, any driver that supports Linux wireless
@@ -788,12 +679,12 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2007,
+    <para>wpa_supplicant is copyright (c) 2003-2012,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
 
-    <para>This program is dual-licensed under both the GPL version 2
-    and BSD license. Either license may be used at your option.</para>
+    <para>This program is licensed under the BSD license (the one with
+    advertisement clause removed).</para>
   </refsect1>
 </refentry>
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index b9ea291..bc148ca 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -427,6 +427,13 @@
 	return 0;
 }
 
+static inline int wpa_drv_deinit_p2p_cli(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->deinit_p2p_cli)
+		return wpa_s->driver->deinit_p2p_cli(wpa_s->drv_priv);
+	return 0;
+}
+
 static inline void wpa_drv_suspend(struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s->driver->suspend)
@@ -456,6 +463,15 @@
 	return -1;
 }
 
+static inline int wpa_drv_pktcnt_poll(struct wpa_supplicant *wpa_s,
+				      struct hostap_sta_driver_data *sta)
+{
+	if (wpa_s->driver->read_sta_data)
+		return wpa_s->driver->read_sta_data(wpa_s->drv_priv, sta,
+						    wpa_s->bssid);
+	return -1;
+}
+
 static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s,
 					const struct wpabuf *beacon,
 					const struct wpabuf *proberesp,
@@ -667,13 +683,30 @@
 	wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr);
 }
 
-#ifdef ANDROID_P2P
+static inline int wpa_drv_radio_disable(struct wpa_supplicant *wpa_s,
+					int disabled)
+{
+	if (!wpa_s->driver->radio_disable)
+		return -1;
+	return wpa_s->driver->radio_disable(wpa_s->drv_priv, disabled);
+}
+
 static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s,
-					  int freq)
+					 unsigned int freq)
 {
 	if (!wpa_s->driver->switch_channel)
 		return -1;
 	return wpa_s->driver->switch_channel(wpa_s->drv_priv, freq);
 }
-#endif
+
+static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s,
+				   enum wnm_oper oper, const u8 *peer,
+				   u8 *buf, u16 *buf_len)
+{
+	if (!wpa_s->driver->wnm_oper)
+		return -1;
+	return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf,
+				       buf_len);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c
index c220aec..d1eb4ff 100644
--- a/wpa_supplicant/eap_register.c
+++ b/wpa_supplicant/eap_register.c
@@ -35,6 +35,11 @@
 		ret = eap_peer_tls_register();
 #endif /* EAP_TLS */
 
+#ifdef EAP_UNAUTH_TLS
+	if (ret == 0)
+		ret = eap_peer_unauth_tls_register();
+#endif /* EAP_UNAUTH_TLS */
+
 #ifdef EAP_MSCHAPv2
 	if (ret == 0)
 		ret = eap_peer_mschapv2_register();
@@ -145,6 +150,11 @@
 		ret = eap_server_tls_register();
 #endif /* EAP_SERVER_TLS */
 
+#ifdef EAP_SERVER_UNAUTH_TLS
+	if (ret == 0)
+		ret = eap_server_unauth_tls_register();
+#endif /* EAP_SERVER_UNAUTH_TLS */
+
 #ifdef EAP_SERVER_MSCHAPV2
 	if (ret == 0)
 		ret = eap_server_mschapv2_register();
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 65e6742..03b8c7e 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - test code
- * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -13,6 +13,7 @@
 #include <assert.h>
 
 #include "common.h"
+#include "utils/ext_password.h"
 #include "config.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "eap_peer/eap.h"
@@ -55,9 +56,8 @@
 	struct radius_client_data *radius;
 	struct hostapd_radius_servers *radius_conf;
 
-	u8 *last_eap_radius; /* last received EAP Response from Authentication
-			      * Server */
-	size_t last_eap_radius_len;
+	 /* last received EAP Response from Authentication Server */
+	struct wpabuf *last_eap_radius;
 
 	u8 authenticator_pmk[PMK_LEN];
 	size_t authenticator_pmk_len;
@@ -278,7 +278,9 @@
 		}
 	}
 
-	radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr);
+	if (radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr)
+	    < 0)
+		goto fail;
 	return;
 
  fail:
@@ -427,6 +429,37 @@
 }
 
 
+static void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+	struct eapol_test_data *e = ctx;
+	struct wpa_supplicant *wpa_s = e->wpa_s;
+	char *str;
+	int res;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
+			  id, len);
+
+	if (wpa_s->current_ssid == NULL)
+		return;
+
+	if (id == NULL) {
+		if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				   "NULL", 0) < 0)
+			return;
+	} else {
+		str = os_malloc(len * 2 + 1);
+		if (str == NULL)
+			return;
+		wpa_snprintf_hex(str, len * 2 + 1, id, len);
+		res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				     str, 0);
+		os_free(str);
+		if (res < 0)
+			return;
+	}
+}
+
+
 static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
 		      struct wpa_ssid *ssid)
 {
@@ -454,6 +487,7 @@
 	ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
 	ctx->cert_cb = eapol_test_cert_cb;
 	ctx->cert_in_cb = 1;
+	ctx->set_anon_id = eapol_test_set_anon_id;
 
 	wpa_s->eapol = eapol_sm_init(ctx);
 	if (wpa_s->eapol == NULL) {
@@ -486,7 +520,7 @@
 	struct extra_radius_attr *p, *prev;
 
 	radius_client_deinit(e->radius);
-	os_free(e->last_eap_radius);
+	wpabuf_free(e->last_eap_radius);
 	radius_msg_free(e->last_recv_radius);
 	e->last_recv_radius = NULL;
 	os_free(e->eap_identity);
@@ -504,6 +538,10 @@
 		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
 		wpa_s->ctrl_iface = NULL;
 	}
+
+	ext_password_deinit(wpa_s->ext_pw);
+	wpa_s->ext_pw = NULL;
+
 	wpa_config_free(wpa_s->conf);
 
 	p = e->extra_attrs;
@@ -572,9 +610,8 @@
 
 static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e)
 {
-	u8 *eap;
-	size_t len;
-	struct eap_hdr *hdr;
+	struct wpabuf *eap;
+	const struct eap_hdr *hdr;
 	int eap_type = -1;
 	char buf[64];
 	struct radius_msg *msg;
@@ -584,30 +621,29 @@
 
 	msg = e->last_recv_radius;
 
-	eap = radius_msg_get_eap(msg, &len);
+	eap = radius_msg_get_eap(msg);
 	if (eap == NULL) {
 		/* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3:
 		 * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
 		 * attribute */
 		wpa_printf(MSG_DEBUG, "could not extract "
 			       "EAP-Message from RADIUS message");
-		os_free(e->last_eap_radius);
+		wpabuf_free(e->last_eap_radius);
 		e->last_eap_radius = NULL;
-		e->last_eap_radius_len = 0;
 		return;
 	}
 
-	if (len < sizeof(*hdr)) {
+	if (wpabuf_len(eap) < sizeof(*hdr)) {
 		wpa_printf(MSG_DEBUG, "too short EAP packet "
 			       "received from authentication server");
-		os_free(eap);
+		wpabuf_free(eap);
 		return;
 	}
 
-	if (len > sizeof(*hdr))
-		eap_type = eap[sizeof(*hdr)];
+	if (wpabuf_len(eap) > sizeof(*hdr))
+		eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
 
-	hdr = (struct eap_hdr *) eap;
+	hdr = wpabuf_head(eap);
 	switch (hdr->code) {
 	case EAP_CODE_REQUEST:
 		os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
@@ -630,7 +666,7 @@
 		break;
 	default:
 		os_strlcpy(buf, "unknown EAP code", sizeof(buf));
-		wpa_hexdump(MSG_DEBUG, "Decapsulated EAP packet", eap, len);
+		wpa_hexdump_buf(MSG_DEBUG, "Decapsulated EAP packet", eap);
 		break;
 	}
 	wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d "
@@ -639,20 +675,21 @@
 
 	/* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */
 
-	os_free(e->last_eap_radius);
+	wpabuf_free(e->last_eap_radius);
 	e->last_eap_radius = eap;
-	e->last_eap_radius_len = len;
 
 	{
 		struct ieee802_1x_hdr *dot1x;
-		dot1x = os_malloc(sizeof(*dot1x) + len);
+		dot1x = os_malloc(sizeof(*dot1x) + wpabuf_len(eap));
 		assert(dot1x != NULL);
 		dot1x->version = EAPOL_VERSION;
 		dot1x->type = IEEE802_1X_TYPE_EAP_PACKET;
-		dot1x->length = htons(len);
-		os_memcpy((u8 *) (dot1x + 1), eap, len);
+		dot1x->length = htons(wpabuf_len(eap));
+		os_memcpy((u8 *) (dot1x + 1), wpabuf_head(eap),
+			  wpabuf_len(eap));
 		eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid,
-				  (u8 *) dot1x, sizeof(*dot1x) + len);
+				  (u8 *) dot1x,
+				  sizeof(*dot1x) + wpabuf_len(eap));
 		os_free(dot1x);
 	}
 }
@@ -856,7 +893,7 @@
 	unsigned char aka_ik[IK_LEN];
 	unsigned char aka_ck[CK_LEN];
 
-	scard = scard_init(SCARD_TRY_BOTH);
+	scard = scard_init(SCARD_TRY_BOTH, NULL);
 	if (scard == NULL)
 		return -1;
 	if (scard_set_pin(scard, "1234")) {
@@ -956,7 +993,7 @@
 		wpa_debug_level = 99;
 	}
 
-	scard = scard_init(SCARD_GSM_SIM_ONLY);
+	scard = scard_init(SCARD_GSM_SIM_ONLY, NULL);
 	if (scard == NULL) {
 		printf("Failed to open smartcard connection\n");
 		return -1;
@@ -1226,6 +1263,9 @@
 	if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid))
 		return -1;
 
+	if (wpas_init_ext_pw(&wpa_s) < 0)
+		return -1;
+
 	if (wait_for_monitor)
 		wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface);
 
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index ee0af50..6e9d7ba 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -23,6 +23,7 @@
 #include "eap_peer/eap.h"
 #include "ap/hostapd.h"
 #include "p2p/p2p.h"
+#include "wnm_sta.h"
 #include "notify.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -35,15 +36,36 @@
 #include "gas_query.h"
 #include "p2p_supplicant.h"
 #include "bgscan.h"
+#include "autoscan.h"
 #include "ap.h"
 #include "bss.h"
 #include "scan.h"
 #include "offchannel.h"
+#include "interworking.h"
+
+
+static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid)
+{
+	struct os_time now;
+
+	if (ssid == NULL || ssid->disabled_until.sec == 0)
+		return 0;
+
+	os_get_time(&now);
+	if (ssid->disabled_until.sec > now.sec)
+		return ssid->disabled_until.sec - now.sec;
+
+	wpas_clear_temp_disabled(wpa_s, ssid, 0);
+
+	return 0;
+}
 
 
 static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid, *old_ssid;
+	int res;
 
 	if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid)
 		return 0;
@@ -57,11 +79,18 @@
 		return -1;
 	}
 
-	if (ssid->disabled) {
+	if (wpas_network_disabled(wpa_s, ssid)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled");
 		return -1;
 	}
 
+	res = wpas_temp_disabled(wpa_s, ssid);
+	if (res > 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily "
+			"disabled for %d second(s)", res);
+		return -1;
+	}
+
 	wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the "
 		"current AP");
 	if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
@@ -103,6 +132,8 @@
 {
 	int bssid_changed;
 
+	wnm_bss_keep_alive_deinit(wpa_s);
+
 #ifdef CONFIG_IBSS_RSN
 	ibss_rsn_deinit(wpa_s->ibss_rsn);
 	wpa_s->ibss_rsn = NULL;
@@ -122,6 +153,9 @@
 	bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
 	os_memset(wpa_s->bssid, 0, ETH_ALEN);
 	os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+#ifdef CONFIG_SME
+	wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
 #ifdef CONFIG_P2P
 	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
 #endif /* CONFIG_P2P */
@@ -142,6 +176,8 @@
 	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
 		eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
 	wpa_s->ap_ies_from_associnfo = 0;
+	wpa_s->current_ssid = NULL;
+	wpa_s->key_mgmt = 0;
 }
 
 
@@ -241,7 +277,8 @@
 			if (eap->vendor == EAP_VENDOR_IETF) {
 				if (eap->method == EAP_TYPE_SIM)
 					sim = 1;
-				else if (eap->method == EAP_TYPE_AKA)
+				else if (eap->method == EAP_TYPE_AKA ||
+					 eap->method == EAP_TYPE_AKA_PRIME)
 					aka = 1;
 			}
 			eap++;
@@ -250,7 +287,9 @@
 
 	if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL)
 		sim = 0;
-	if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL)
+	if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL &&
+	    eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME) ==
+	    NULL)
 		aka = 0;
 
 	if (!sim && !aka) {
@@ -269,7 +308,7 @@
 	else
 		type = SCARD_GSM_SIM_ONLY;
 
-	wpa_s->scard = scard_init(type);
+	wpa_s->scard = scard_init(type, NULL);
 	if (wpa_s->scard == NULL) {
 		wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM "
 			"(pcsc-lite)");
@@ -285,7 +324,7 @@
 
 
 #ifndef CONFIG_NO_SCAN_PROCESSING
-static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss,
+static int wpa_supplicant_match_privacy(struct wpa_bss *bss,
 					struct wpa_ssid *ssid)
 {
 	int i, privacy = 0;
@@ -322,7 +361,7 @@
 
 static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
 					 struct wpa_ssid *ssid,
-					 struct wpa_scan_res *bss)
+					 struct wpa_bss *bss)
 {
 	struct wpa_ie_data ie;
 	int proto_match = 0;
@@ -340,7 +379,7 @@
 		  ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) ||
 		 (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA));
 
-	rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
+	rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
 	while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
 		proto_match++;
 
@@ -395,7 +434,7 @@
 		return 1;
 	}
 
-	wpa_ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+	wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 	while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
 		proto_match++;
 
@@ -441,6 +480,12 @@
 		return 1;
 	}
 
+	if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie &&
+	    !rsn_ie) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "   allow for non-WPA IEEE 802.1X");
+		return 1;
+	}
+
 	if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) &&
 	    wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no WPA/RSN proto match");
@@ -491,7 +536,7 @@
 }
 
 
-static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_scan_res *bss)
+static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
 	const struct hostapd_hw_modes *mode = NULL, *modes;
 	const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES };
@@ -529,7 +574,7 @@
 		return 0;
 
 	for (i = 0; i < (int) sizeof(scan_ie); i++) {
-		rate_ie = wpa_scan_get_ie(bss, scan_ie[i]);
+		rate_ie = wpa_bss_get_ie(bss, scan_ie[i]);
 		if (rate_ie == NULL)
 			continue;
 
@@ -583,36 +628,31 @@
 
 
 static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
-					    int i, struct wpa_scan_res *bss,
+					    int i, struct wpa_bss *bss,
 					    struct wpa_ssid *group)
 {
-	const u8 *ssid_;
-	u8 wpa_ie_len, rsn_ie_len, ssid_len;
+	u8 wpa_ie_len, rsn_ie_len;
 	int wpa;
 	struct wpa_blacklist *e;
 	const u8 *ie;
 	struct wpa_ssid *ssid;
 
-	ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
-	ssid_ = ie ? ie + 2 : (u8 *) "";
-	ssid_len = ie ? ie[1] : 0;
-
-	ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 	wpa_ie_len = ie ? ie[1] : 0;
 
-	ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
+	ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
 	rsn_ie_len = ie ? ie[1] : 0;
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
 		"wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s",
-		i, MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len),
+		i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
 		wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
-		wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "");
+		wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "");
 
 	e = wpa_blacklist_get(wpa_s, bss->bssid);
 	if (e) {
 		int limit = 1;
-		if (wpa_supplicant_enabled_networks(wpa_s->conf) == 1) {
+		if (wpa_supplicant_enabled_networks(wpa_s) == 1) {
 			/*
 			 * When only a single network is enabled, we can
 			 * trigger blacklisting on the first failure. This
@@ -630,7 +670,7 @@
 		}
 	}
 
-	if (ssid_len == 0) {
+	if (bss->ssid_len == 0) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID not known");
 		return NULL;
 	}
@@ -639,12 +679,20 @@
 
 	for (ssid = group; ssid; ssid = ssid->pnext) {
 		int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
+		int res;
 
-		if (ssid->disabled) {
+		if (wpas_network_disabled(wpa_s, ssid)) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled");
 			continue;
 		}
 
+		res = wpas_temp_disabled(wpa_s, ssid);
+		if (res > 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled "
+				"temporarily for %d second(s)", res);
+			continue;
+		}
+
 #ifdef CONFIG_WPS
 		if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - blacklisted "
@@ -672,8 +720,8 @@
 			check_ssid = 0;
 
 		if (check_ssid &&
-		    (ssid_len != ssid->ssid_len ||
-		     os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
+		    (bss->ssid_len != ssid->ssid_len ||
+		     os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID mismatch");
 			continue;
 		}
@@ -739,32 +787,24 @@
 
 static struct wpa_bss *
 wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
-			  struct wpa_scan_results *scan_res,
 			  struct wpa_ssid *group,
 			  struct wpa_ssid **selected_ssid)
 {
-	size_t i;
+	unsigned int i;
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
 		group->priority);
 
-	for (i = 0; i < scan_res->num; i++) {
-		struct wpa_scan_res *bss = scan_res->res[i];
-		const u8 *ie, *ssid;
-		u8 ssid_len;
-
+	for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+		struct wpa_bss *bss = wpa_s->last_scan_res[i];
 		*selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group);
 		if (!*selected_ssid)
 			continue;
-
-		ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
-		ssid = ie ? ie + 2 : (u8 *) "";
-		ssid_len = ie ? ie[1] : 0;
-
 		wpa_dbg(wpa_s, MSG_DEBUG, "   selected BSS " MACSTR
 			" ssid='%s'",
-			MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
-		return wpa_bss_get(wpa_s, bss->bssid, ssid, ssid_len);
+			MAC2STR(bss->bssid),
+			wpa_ssid_txt(bss->ssid, bss->ssid_len));
+		return bss;
 	}
 
 	return NULL;
@@ -773,16 +813,19 @@
 
 static struct wpa_bss *
 wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
-			    struct wpa_scan_results *scan_res,
 			    struct wpa_ssid **selected_ssid)
 {
 	struct wpa_bss *selected = NULL;
 	int prio;
 
+	if (wpa_s->last_scan_res == NULL ||
+	    wpa_s->last_scan_res_used == 0)
+		return NULL; /* no scan results from last update */
+
 	while (selected == NULL) {
 		for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
 			selected = wpa_supplicant_select_bss(
-				wpa_s, scan_res, wpa_s->conf->pssid[prio],
+				wpa_s, wpa_s->conf->pssid[prio],
 				selected_ssid);
 			if (selected)
 				break;
@@ -805,13 +848,18 @@
 static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s,
 					int timeout_sec, int timeout_usec)
 {
-	if (!wpa_supplicant_enabled_networks(wpa_s->conf)) {
+	if (!wpa_supplicant_enabled_networks(wpa_s)) {
 		/*
 		 * No networks are enabled; short-circuit request so
 		 * we don't wait timeout seconds before transitioning
 		 * to INACTIVE state.
 		 */
+		wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request "
+			"since there are no enabled networks");
 		wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+#ifdef CONFIG_P2P
+		wpa_s->sta_scan_pending = 0;
+#endif /* CONFIG_P2P */
 		return;
 	}
 	wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec);
@@ -876,7 +924,7 @@
 	for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
 		for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext)
 		{
-			if (ssid->disabled)
+			if (wpas_network_disabled(wpa_s, ssid))
 				continue;
 			if (ssid->mode == IEEE80211_MODE_IBSS ||
 			    ssid->mode == IEEE80211_MODE_AP)
@@ -916,11 +964,9 @@
 
 static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
 				       struct wpa_bss *selected,
-				       struct wpa_ssid *ssid,
-				       struct wpa_scan_results *scan_res)
+				       struct wpa_ssid *ssid)
 {
-	size_t i;
-	struct wpa_scan_res *current_bss = NULL;
+	struct wpa_bss *current_bss = NULL;
 	int min_diff;
 
 	if (wpa_s->reassociate)
@@ -935,25 +981,22 @@
 	if (wpas_driver_bss_selection(wpa_s))
 		return 0; /* Driver-based roaming */
 
-	for (i = 0; i < scan_res->num; i++) {
-		struct wpa_scan_res *res = scan_res->res[i];
-		const u8 *ie;
-		if (os_memcmp(res->bssid, wpa_s->bssid, ETH_ALEN) != 0)
-			continue;
-
-		ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
-		if (ie == NULL)
-			continue;
-		if (ie[1] != wpa_s->current_ssid->ssid_len ||
-		    os_memcmp(ie + 2, wpa_s->current_ssid->ssid, ie[1]) != 0)
-			continue;
-		current_bss = res;
-		break;
-	}
+	if (wpa_s->current_ssid->ssid)
+		current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
+					  wpa_s->current_ssid->ssid,
+					  wpa_s->current_ssid->ssid_len);
+	if (!current_bss)
+		current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
 
 	if (!current_bss)
 		return 1; /* current BSS not seen in scan results */
 
+	if (current_bss == selected)
+		return 0;
+
+	if (selected->last_update_idx > current_bss->last_update_idx)
+		return 1; /* current BSS not seen in the last scan */
+
 #ifndef CONFIG_NO_ROAMING
 	wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
 	wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
@@ -989,13 +1032,14 @@
 	}
 
 	return 1;
-#else
+#else /* CONFIG_NO_ROAMING */
 	return 0;
-#endif
+#endif /* CONFIG_NO_ROAMING */
 }
 
 
-/* Return < 0 if no scan results could be fetched. */
+/* Return != 0 if no scan results could be fetched or if scan results should not
+ * be shared with other virtual interfaces. */
 #ifdef ANDROID_P2P
 static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 					      union wpa_event_data *data, int suppress_event)
@@ -1004,10 +1048,11 @@
 					      union wpa_event_data *data)
 #endif
 {
-	struct wpa_bss *selected;
-	struct wpa_ssid *ssid = NULL;
 	struct wpa_scan_results *scan_res;
 	int ap = 0;
+#ifndef CONFIG_NO_RANDOM_POOL
+	size_t i, num;
+#endif /* CONFIG_NO_RANDOM_POOL */
 
 #ifdef CONFIG_AP
 	if (wpa_s->ap_iface)
@@ -1017,19 +1062,20 @@
 	wpa_supplicant_notify_scanning(wpa_s, 0);
 
 #ifdef CONFIG_P2P
-#ifdef ANDROID_P2P
-	if (p2p_search_pending(wpa_s->global->p2p) && !wpa_s->global->p2p_disabled &&
-#else
-	if (wpa_s->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
-#endif
-	    wpa_s->global->p2p != NULL) {
-		wpa_s->p2p_cb_on_scan_complete = 0;
+	if (wpa_s->global->p2p_cb_on_scan_complete &&
+	    !wpa_s->global->p2p_disabled &&
+	    wpa_s->global->p2p != NULL && !wpa_s->sta_scan_pending &&
+	    !wpa_s->scan_res_handler) {
+		wpa_s->global->p2p_cb_on_scan_complete = 0;
 		if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
 				"stopped scan processing");
+			wpa_s->sta_scan_pending = 1;
+			wpa_supplicant_req_scan(wpa_s, 5, 0);
 			return -1;
 		}
 	}
+	wpa_s->sta_scan_pending = 0;
 #endif /* CONFIG_P2P */
 
 	scan_res = wpa_supplicant_get_scan_results(wpa_s,
@@ -1045,7 +1091,6 @@
 	}
 
 #ifndef CONFIG_NO_RANDOM_POOL
-	size_t i, num;
 	num = scan_res->num;
 	if (num > 10)
 		num = 10;
@@ -1070,7 +1115,7 @@
 		scan_res_handler(wpa_s, scan_res);
 
 		wpa_scan_results_free(scan_res);
-		return 0;
+		return -2;
 	}
 
 	if (ap) {
@@ -1084,20 +1129,34 @@
 	}
 #ifdef ANDROID_P2P
 	if(!suppress_event)
-#endif
 	{
 		wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available");
 		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
 		wpas_notify_scan_results(wpa_s);
 	}
+#else
+	wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available");
+	wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+	wpas_notify_scan_results(wpa_s);
+#endif
 
 	wpas_notify_scan_done(wpa_s, 1);
 
+	if (sme_proc_obss_scan(wpa_s) > 0) {
+		wpa_scan_results_free(scan_res);
+		return 0;
+	}
+
 	if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) {
 		wpa_scan_results_free(scan_res);
 		return 0;
 	}
 
+	if (autoscan_notify_scan(wpa_s, scan_res)) {
+		wpa_scan_results_free(scan_res);
+		return 0;
+	}
+
 	if (wpa_s->disconnected) {
 		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 		wpa_scan_results_free(scan_res);
@@ -1110,13 +1169,24 @@
 		return 0;
 	}
 
-	selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid);
+	wpas_wps_update_ap_info(wpa_s, scan_res);
+
+	wpa_scan_results_free(scan_res);
+
+	return wpas_select_network_from_last_scan(wpa_s);
+}
+
+
+int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *selected;
+	struct wpa_ssid *ssid = NULL;
+
+	selected = wpa_supplicant_pick_network(wpa_s, &ssid);
 
 	if (selected) {
 		int skip;
-		skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid,
-						    scan_res);
-		wpa_scan_results_free(scan_res);
+		skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
 		if (skip) {
 			wpa_supplicant_rsn_preauth_scan_results(wpa_s);
 			return 0;
@@ -1127,8 +1197,12 @@
 			return -1;
 		}
 		wpa_supplicant_rsn_preauth_scan_results(wpa_s);
+		/*
+		 * Do not notify other virtual radios of scan results since we do not
+		 * want them to start other associations at the same time.
+		 */
+		return 1;
 	} else {
-		wpa_scan_results_free(scan_res);
 		wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
 		ssid = wpa_supplicant_pick_new_network(wpa_s);
 		if (ssid) {
@@ -1139,6 +1213,9 @@
 			int timeout_sec = wpa_s->scan_interval;
 			int timeout_usec = 0;
 #ifdef CONFIG_P2P
+			if (wpas_p2p_scan_no_go_seen(wpa_s) == 1)
+				return 0;
+
 			if (wpa_s->p2p_in_provisioning) {
 				/*
 				 * Use shorter wait during P2P Provisioning
@@ -1151,6 +1228,19 @@
 				return 0;
 			}
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+			if (wpa_s->conf->auto_interworking &&
+			    wpa_s->conf->interworking &&
+			    wpa_s->conf->cred) {
+				wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: "
+					"start ANQP fetch since no matching "
+					"networks found");
+				wpa_s->network_select = 1;
+				wpa_s->auto_network_select = 1;
+				interworking_start_fetch_anqp(wpa_s);
+				return 1;
+			}
+#endif /* CONFIG_INTERWORKING */
 			if (wpa_supplicant_req_sched_scan(wpa_s))
 				wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
 							    timeout_usec);
@@ -1166,14 +1256,16 @@
 	const char *rn, *rn2;
 	struct wpa_supplicant *ifs;
 #ifdef ANDROID_P2P
-	if (_wpa_supplicant_event_scan_results(wpa_s, data, 0) < 0) {
+	if (_wpa_supplicant_event_scan_results(wpa_s, data, 0) != 0) {
 #else
-	if (_wpa_supplicant_event_scan_results(wpa_s, data) < 0) {
+	if (_wpa_supplicant_event_scan_results(wpa_s, data) != 0) {
 #endif
 		/*
 		 * If no scan results could be fetched, then no need to
 		 * notify those interfaces that did not actually request
-		 * this scan.
+		 * this scan. Similarly, if scan results started a new operation on this
+		 * interface, do not notify other interfaces to avoid concurrent
+		 * operations during a connection attempt.
 		 */
 		return;
 	}
@@ -1228,6 +1320,82 @@
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 
 
+#ifdef CONFIG_WNM
+
+static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (wpa_s->wpa_state < WPA_ASSOCIATED)
+		return;
+
+	wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR,
+		   MAC2STR(wpa_s->bssid));
+	/* TODO: could skip this if normal data traffic has been sent */
+	/* TODO: Consider using some more appropriate data frame for this */
+	if (wpa_s->l2)
+		l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800, (u8 *) "", 0);
+
+#ifdef CONFIG_SME
+	if (wpa_s->sme.bss_max_idle_period) {
+		unsigned int msec;
+		msec = wpa_s->sme.bss_max_idle_period * 1024; /* times 1000 */
+		if (msec > 100)
+			msec -= 100;
+		eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
+				       wnm_bss_keep_alive, wpa_s, NULL);
+	}
+#endif /* CONFIG_SME */
+}
+
+
+static void wnm_process_assoc_resp(struct wpa_supplicant *wpa_s,
+				   const u8 *ies, size_t ies_len)
+{
+	struct ieee802_11_elems elems;
+
+	if (ies == NULL)
+		return;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed)
+		return;
+
+#ifdef CONFIG_SME
+	if (elems.bss_max_idle_period) {
+		unsigned int msec;
+		wpa_s->sme.bss_max_idle_period =
+			WPA_GET_LE16(elems.bss_max_idle_period);
+		wpa_printf(MSG_DEBUG, "WNM: BSS Max Idle Period: %u (* 1000 "
+			   "TU)%s", wpa_s->sme.bss_max_idle_period,
+			   (elems.bss_max_idle_period[2] & 0x01) ?
+			   " (protected keep-live required)" : "");
+		if (wpa_s->sme.bss_max_idle_period == 0)
+			wpa_s->sme.bss_max_idle_period = 1;
+		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) {
+			eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
+			 /* msec times 1000 */
+			msec = wpa_s->sme.bss_max_idle_period * 1024;
+			if (msec > 100)
+				msec -= 100;
+			eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
+					       wnm_bss_keep_alive, wpa_s,
+					       NULL);
+		}
+	}
+#endif /* CONFIG_SME */
+}
+
+#endif /* CONFIG_WNM */
+
+
+void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WNM
+	eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
+#endif /* CONFIG_WNM */
+}
+
+
 static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
 					  union wpa_event_data *data)
 {
@@ -1245,6 +1413,10 @@
 		wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies,
 					data->assoc_info.resp_ies_len);
 #endif /* CONFIG_TDLS */
+#ifdef CONFIG_WNM
+		wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+				       data->assoc_info.resp_ies_len);
+#endif /* CONFIG_WNM */
 	}
 	if (data->assoc_info.beacon_ies)
 		wpa_hexdump(MSG_DEBUG, "beacon_ies",
@@ -1441,7 +1613,6 @@
 {
 	u8 bssid[ETH_ALEN];
 	int ft_completed;
-	int bssid_changed;
 	struct wpa_driver_capa capa;
 
 #ifdef CONFIG_AP
@@ -1459,17 +1630,21 @@
 	if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
 		return;
 
+	if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+		wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
+		wpa_supplicant_disassociate(
+			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		return;
+	}
+
 	wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
-	if (wpa_drv_get_bssid(wpa_s, bssid) >= 0 &&
-	    os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
+	if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
 			MACSTR, MAC2STR(bssid));
 		random_add_randomness(bssid, ETH_ALEN);
-		bssid_changed = os_memcmp(wpa_s->bssid, bssid, ETH_ALEN);
 		os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
 		os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
-		if (bssid_changed)
-			wpas_notify_bssid_changed(wpa_s);
+		wpas_notify_bssid_changed(wpa_s);
 
 		if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) {
 			wpa_clear_keys(wpa_s, bssid);
@@ -1621,6 +1796,8 @@
 		ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk);
 	}
 #endif /* CONFIG_IBSS_RSN */
+
+	wpas_wps_notify_assoc(wpa_s, bssid);
 }
 
 
@@ -1637,10 +1814,40 @@
 					  int locally_generated)
 {
 	const u8 *bssid;
+
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+		/*
+		 * At least Host AP driver and a Prism3 card seemed to be
+		 * generating streams of disconnected events when configuring
+		 * IBSS for WPA-None. Ignore them for now.
+		 */
+		return;
+	}
+
+	bssid = wpa_s->bssid;
+	if (is_zero_ether_addr(bssid))
+		bssid = wpa_s->pending_bssid;
+
+	if (!is_zero_ether_addr(bssid) ||
+	    wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+			" reason=%d%s",
+			MAC2STR(bssid), reason_code,
+			locally_generated ? " locally_generated=1" : "");
+	}
+}
+
+
+static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
+						 u16 reason_code,
+						 int locally_generated)
+{
+	const u8 *bssid;
 	int authenticating;
 	u8 prev_pending_bssid[ETH_ALEN];
 	struct wpa_bss *fast_reconnect = NULL;
 	struct wpa_ssid *fast_reconnect_ssid = NULL;
+	struct wpa_ssid *last_ssid;
 
 	authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
 	os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
@@ -1660,6 +1867,7 @@
 	    wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
 		wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
 			"pre-shared key may be incorrect");
+		wpas_auth_failed(wpa_s);
 	}
 	if (!wpa_s->auto_reconnect_disabled ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
@@ -1702,18 +1910,23 @@
 	if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
 		wpas_connection_failed(wpa_s, bssid);
 	wpa_sm_notify_disassoc(wpa_s->wpa);
-	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
-		" reason=%d",
-		MAC2STR(bssid), reason_code);
+	if (locally_generated)
+		wpa_s->disconnect_reason = -reason_code;
+	else
+		wpa_s->disconnect_reason = reason_code;
+	wpas_notify_disconnect_reason(wpa_s);
 	if (wpa_supplicant_dynamic_keys(wpa_s)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
 		wpa_s->keys_cleared = 0;
 		wpa_clear_keys(wpa_s, wpa_s->bssid);
 	}
+	last_ssid = wpa_s->current_ssid;
 	wpa_supplicant_mark_disassoc(wpa_s);
 
-	if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+	if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
 		sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
+		wpa_s->current_ssid = last_ssid;
+	}
 
 	if (fast_reconnect) {
 #ifndef CONFIG_NO_SCAN_PROCESSING
@@ -1867,11 +2080,13 @@
 			wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
 				"driver after interface was added");
 		}
+		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 		break;
 	case EVENT_INTERFACE_REMOVED:
 		wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
 		wpa_s->interface_removed = 1;
 		wpa_supplicant_mark_disassoc(wpa_s);
+		wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
 		l2_packet_deinit(wpa_s->l2);
 		wpa_s->l2 = NULL;
 #ifdef CONFIG_IBSS_RSN
@@ -1919,6 +2134,25 @@
 #endif /* CONFIG_TDLS */
 
 
+#ifdef CONFIG_IEEE80211V
+static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s,
+				     union wpa_event_data *data)
+{
+	if (data == NULL)
+		return;
+	switch (data->wnm.oper) {
+	case WNM_OPER_SLEEP:
+		wpa_printf(MSG_DEBUG, "Start sending WNM-Sleep Request "
+			   "(action=%d, intval=%d)",
+			   data->wnm.sleep_action, data->wnm.sleep_intval);
+		ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action,
+					     data->wnm.sleep_intval);
+		break;
+	}
+}
+#endif /* CONFIG_IEEE80211V */
+
+
 #ifdef CONFIG_IEEE80211R
 static void
 wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s,
@@ -2104,8 +2338,7 @@
 {
 	int level = MSG_DEBUG;
 
-	if (event == EVENT_RX_MGMT && data && data->rx_mgmt.frame &&
-	    data->rx_mgmt.frame_len >= 24) {
+	if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) {
 		const struct ieee80211_hdr *hdr;
 		u16 fc;
 		hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
@@ -2161,7 +2394,8 @@
 			wpas_p2p_disassoc_notif(
 				wpa_s, data->disassoc_info.addr, reason_code,
 				data->disassoc_info.ie,
-				data->disassoc_info.ie_len);
+				data->disassoc_info.ie_len,
+				locally_generated);
 #endif /* CONFIG_P2P */
 		}
 		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
@@ -2189,13 +2423,6 @@
 					    "Deauthentication frame IE(s)",
 					    data->deauth_info.ie,
 					    data->deauth_info.ie_len);
-#ifdef CONFIG_P2P
-				wpas_p2p_deauth_notif(
-					wpa_s, data->deauth_info.addr,
-					reason_code,
-					data->deauth_info.ie,
-					data->deauth_info.ie_len);
-#endif /* CONFIG_P2P */
 			}
 		}
 #ifdef CONFIG_AP
@@ -2212,6 +2439,30 @@
 #endif /* CONFIG_AP */
 		wpa_supplicant_event_disassoc(wpa_s, reason_code,
 					      locally_generated);
+		if (reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED ||
+		    ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+		      (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
+		     eapol_sm_failed(wpa_s->eapol)))
+			wpas_auth_failed(wpa_s);
+#ifdef CONFIG_P2P
+		if (event == EVENT_DEAUTH && data) {
+			if (wpas_p2p_deauth_notif(wpa_s,
+						  data->deauth_info.addr,
+						  reason_code,
+						  data->deauth_info.ie,
+						  data->deauth_info.ie_len,
+						  locally_generated) > 0) {
+				/*
+				 * The interface was removed, so cannot
+				 * continue processing any additional
+				 * operations after this.
+				 */
+				break;
+			}
+		}
+#endif /* CONFIG_P2P */
+		wpa_supplicant_event_disassoc_finish(wpa_s, reason_code,
+						     locally_generated);
 		break;
 	case EVENT_MICHAEL_MIC_FAILURE:
 		wpa_supplicant_event_michael_mic_failure(wpa_s, data);
@@ -2219,6 +2470,18 @@
 #ifndef CONFIG_NO_SCAN_PROCESSING
 	case EVENT_SCAN_RESULTS:
 		wpa_supplicant_event_scan_results(wpa_s, data);
+#ifdef CONFIG_P2P
+	if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
+	    wpa_s->global->p2p != NULL &&
+	    wpa_s->wpa_state != WPA_AUTHENTICATING &&
+	    wpa_s->wpa_state != WPA_ASSOCIATING) {
+		wpa_s->global->p2p_cb_on_scan_complete = 0;
+		if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
+				"continued after scan result processing");
+		}
+	}
+#endif /* CONFIG_P2P */
 		break;
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 	case EVENT_ASSOCINFO:
@@ -2240,6 +2503,11 @@
 		wpa_supplicant_event_tdls(wpa_s, data);
 		break;
 #endif /* CONFIG_TDLS */
+#ifdef CONFIG_IEEE80211V
+	case EVENT_WNM:
+		wpa_supplicant_event_wnm(wpa_s, data);
+		break;
+#endif /* CONFIG_IEEE80211V */
 #ifdef CONFIG_IEEE80211R
 	case EVENT_FT_RESPONSE:
 		wpa_supplicant_event_ft_response(wpa_s, data);
@@ -2265,7 +2533,7 @@
 #ifdef ANDROID_P2P
 #ifdef CONFIG_P2P
 		else if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
-			if (!wpa_s->current_ssid) {
+			if(!wpa_s->current_ssid) {
 				wpa_printf(MSG_ERROR, "current_ssid == NULL");
 				break;
 			}
@@ -2273,7 +2541,7 @@
 			 * waiting for  the authentication timeout. Cancel the
 			 * authentication timeout and retry the assoc.
 			 */
-			if (wpa_s->current_ssid->assoc_retry++ < 5) {
+			if(wpa_s->current_ssid->assoc_retry++ < 5) {
 				wpa_printf(MSG_ERROR, "Retrying assoc: %d ",
 								wpa_s->current_ssid->assoc_retry);
 				wpa_supplicant_cancel_auth_timeout(wpa_s);
@@ -2286,8 +2554,8 @@
 				wpa_supplicant_req_scan(wpa_s, 1, 0);
 			} else {
 				/* If we ASSOC_REJECT's hits threshold, disable the 
-				 * network
-				 */
+			 	 * network
+			 	 */
 				wpa_printf(MSG_ERROR, "Assoc retry threshold reached. "
 				"Disabling the network");
 				wpa_s->current_ssid->assoc_retry = 0;
@@ -2381,15 +2649,32 @@
 		ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr,
 				       data->rx_from_unknown.wds);
 		break;
-	case EVENT_RX_MGMT:
+	case EVENT_CH_SWITCH:
+		if (!data)
+			break;
+		if (!wpa_s->ap_iface) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch "
+				"event in non-AP mode");
+			break;
+		}
+
+#ifdef CONFIG_AP
+		wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
+				  data->ch_switch.ht_enabled,
+				  data->ch_switch.ch_offset);
+#endif /* CONFIG_AP */
+		break;
+	case EVENT_RX_MGMT: {
+		u16 fc, stype;
+		const struct ieee80211_mgmt *mgmt;
+
+		mgmt = (const struct ieee80211_mgmt *)
+			data->rx_mgmt.frame;
+		fc = le_to_host16(mgmt->frame_control);
+		stype = WLAN_FC_GET_STYPE(fc);
+
 		if (wpa_s->ap_iface == NULL) {
 #ifdef CONFIG_P2P
-			u16 fc, stype;
-			const struct ieee80211_mgmt *mgmt;
-			mgmt = (const struct ieee80211_mgmt *)
-				data->rx_mgmt.frame;
-			fc = le_to_host16(mgmt->frame_control);
-			stype = WLAN_FC_GET_STYPE(fc);
 			if (stype == WLAN_FC_STYPE_PROBE_REQ &&
 			    data->rx_mgmt.frame_len > 24) {
 				const u8 *src = mgmt->sa;
@@ -2397,8 +2682,10 @@
 				size_t ie_len = data->rx_mgmt.frame_len -
 					(mgmt->u.probe_req.variable -
 					 data->rx_mgmt.frame);
-				wpas_p2p_probe_req_rx(wpa_s, src, mgmt->da,
-						      mgmt->bssid, ie, ie_len);
+				wpas_p2p_probe_req_rx(
+					wpa_s, src, mgmt->da,
+					mgmt->bssid, ie, ie_len,
+					data->rx_mgmt.ssi_signal);
 				break;
 			}
 #endif /* CONFIG_P2P */
@@ -2406,8 +2693,22 @@
 				"management frame in non-AP mode");
 			break;
 		}
+
+		if (stype == WLAN_FC_STYPE_PROBE_REQ &&
+		    data->rx_mgmt.frame_len > 24) {
+			const u8 *ie = mgmt->u.probe_req.variable;
+			size_t ie_len = data->rx_mgmt.frame_len -
+				(mgmt->u.probe_req.variable -
+				 data->rx_mgmt.frame);
+
+			wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da,
+					 mgmt->bssid, ie, ie_len,
+					 data->rx_mgmt.ssi_signal);
+		}
+
 		ap_mgmt_rx(wpa_s, &data->rx_mgmt);
 		break;
+		}
 #endif /* CONFIG_AP */
 	case EVENT_RX_ACTION:
 		wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
@@ -2432,6 +2733,12 @@
 		}
 #endif /* CONFIG_SME */
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211V
+		if (data->rx_action.category == WLAN_ACTION_WNM) {
+			ieee802_11_rx_wnm_action(wpa_s, &data->rx_action);
+			break;
+		}
+#endif /* CONFIG_IEEE80211V */
 #ifdef CONFIG_GAS
 		if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
 		    gas_query_rx(wpa_s->gas, data->rx_action.da,
@@ -2474,7 +2781,8 @@
 					     data->rx_probe_req.da,
 					     data->rx_probe_req.bssid,
 					     data->rx_probe_req.ie,
-					     data->rx_probe_req.ie_len);
+					     data->rx_probe_req.ie_len,
+					     data->rx_probe_req.ssi_signal);
 			break;
 		}
 #endif /* CONFIG_AP */
@@ -2483,7 +2791,8 @@
 				      data->rx_probe_req.da,
 				      data->rx_probe_req.bssid,
 				      data->rx_probe_req.ie,
-				      data->rx_probe_req.ie_len);
+				      data->rx_probe_req.ie_len,
+				      data->rx_probe_req.ssi_signal);
 #endif /* CONFIG_P2P */
 		break;
 	case EVENT_REMAIN_ON_CHANNEL:
@@ -2611,6 +2920,11 @@
 	case EVENT_CHANNEL_LIST_CHANGED:
 		if (wpa_s->drv_priv == NULL)
 			break; /* Ignore event during drv initialization */
+
+		free_hw_features(wpa_s);
+		wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
+			wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
+
 #ifdef CONFIG_P2P
 		wpas_p2p_update_channel_list(wpa_s);
 #endif /* CONFIG_P2P */
diff --git a/wpa_supplicant/examples/dbus-listen-preq.py b/wpa_supplicant/examples/dbus-listen-preq.py
new file mode 100755
index 0000000..5ac9859
--- /dev/null
+++ b/wpa_supplicant/examples/dbus-listen-preq.py
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+import time
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+
+WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1"
+WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface"
+
+def usage():
+	print "Usage: %s <ifname>" % sys.argv[0]
+	print "Press Ctrl-C to stop"
+
+def ProbeRequest(args):
+	if 'addr' in args:
+		print '%.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['addr']),
+	if 'dst' in args:
+		print '-> %.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['dst']),
+	if 'bssid' in args:
+		print '(bssid %.2x:%.2x:%.2x:%.2x:%.2x:%.2x)' % tuple(args['dst']),
+	if 'signal' in args:
+		print 'signal:%d' % args['signal'],
+	if 'ies' in args:
+		print 'have IEs (%d bytes)' % len(args['ies']),
+        print ''
+
+if __name__ == "__main__":
+	global bus
+	global wpas_obj
+	global if_obj
+	global p2p_iface
+
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+	wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH)
+
+	# Print list of i/f if no one is specified
+	if (len(sys.argv) < 2)  :
+		usage()
+		sys.exit(0)
+
+	wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE)
+
+	ifname = sys.argv[1]
+
+	path = wpas.GetInterface(ifname)
+
+	if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+	iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE)
+
+	bus.add_signal_receiver(ProbeRequest,
+				dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+				signal_name="ProbeRequest")
+
+	iface.SubscribeProbeReq()
+
+	gobject.MainLoop().run()
diff --git a/wpa_supplicant/examples/p2p/p2p_connect.py b/wpa_supplicant/examples/p2p/p2p_connect.py
new file mode 100644
index 0000000..59b0a9d
--- /dev/null
+++ b/wpa_supplicant/examples/p2p/p2p_connect.py
@@ -0,0 +1,299 @@
+#!/usr/bin/python
+# Tests p2p_connect
+# Will try to connect to another peer
+# and form a group
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> -m <wps_method> \ " \
+		% sys.argv[0]
+	print "		-a <addr> [-p <pin>] [-g <go_intent>] \ "
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -m = wps method"
+	print "  -a = peer address"
+	print "  -p = pin number (8 digits)"
+	print "  -g = group owner intent"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0 -a 0015008352c0 -m display -p 12345670" % sys.argv[0]
+
+
+# Required Signals
+def GONegotiationSuccess(status):
+	print "Go Negotiation Success"
+
+def GONegotiationFailure(status):
+	print 'Go Negotiation Failed. Status:'
+	print format(status)
+	os._exit(0)
+
+def GroupStarted(properties):
+	if properties.has_key("group_object"):
+		print 'Group Formation Complete %s' \
+			% properties["group_object"]
+	os._exit(0)
+
+def WpsFailure(status, etc):
+	print "WPS Authentication Failure".format(status)
+	print etc
+	os._exit(0)
+
+class P2P_Connect():
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global ifname
+	global wpas
+	global wpas_dbus_interface
+	global timeout
+	global path
+	global wps_method
+	global go_intent
+	global addr
+	global pin
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Dictionary of Arguements
+	global p2p_connect_arguements
+
+	# Constructor
+	def __init__(self,ifname,wpas_dbus_interface,addr,
+					pin,wps_method,go_intent):
+		# Initializes variables and threads
+		self.ifname = ifname
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.wps_method = wps_method
+		self.go_intent = go_intent
+		self.addr = addr
+		self.pin = pin
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = \
+			"/" + self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = \
+			self.wpas_dbus_opath + "/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+			self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+			self.wpas_dbus_interfaces_interface + ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(
+				self.wpas_object, self.wpas_dbus_interface)
+
+		# See if wpa_supplicant already knows about this interface
+		self.path = None
+		try:
+			self.path = self.wpas.GetInterface(ifname)
+		except:
+			if not str(exc).startswith(
+				self.wpas_dbus_interface + \
+				".InterfaceUnknown:"):
+				raise exc
+			try:
+				path = self.wpas.CreateInterface(
+					{'Ifname': ifname, 'Driver': 'test'})
+				time.sleep(1)
+
+			except dbus.DBusException, exc:
+				if not str(exc).startswith(
+					self.wpas_dbus_interface + \
+					".InterfaceExists:"):
+					raise exc
+
+		# Get Interface and objects
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface,self.path)
+		self.p2p_interface = dbus.Interface(
+				self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		# Add signals
+		self.bus.add_signal_receiver(GONegotiationSuccess,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="GONegotiationSuccess")
+		self.bus.add_signal_receiver(GONegotiationFailure,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="GONegotiationFailure")
+		self.bus.add_signal_receiver(GroupStarted,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="GroupStarted")
+		self.bus.add_signal_receiver(WpsFailure,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="WpsFailed")
+
+
+	#Constructing all the arguements needed to connect
+	def constructArguements(self):
+		# Adding required arguements
+		self.p2p_connect_arguements = {'wps_method':self.wps_method,
+			'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)}
+
+		# Display requires a pin, and a go intent of 15
+		if (self.wps_method == 'display'):
+			if (self.pin != None):
+				self.p2p_connect_arguements.update({'pin':self.pin})
+			else:
+				print "Error:\n  Pin required for wps_method=display"
+				usage()
+				quit()
+
+			if (self.go_intent != None and int(self.go_intent) != 15):
+				print "go_intent overwritten to 15"
+
+			self.go_intent = '15'
+
+		# Keypad requires a pin, and a go intent of less than 15
+		elif (self.wps_method == 'keypad'):
+			if (self.pin != None):
+				self.p2p_connect_arguements.update({'pin':self.pin})
+			else:
+				print "Error:\n  Pin required for wps_method=keypad"
+				usage()
+				quit()
+
+			if (self.go_intent != None and int(self.go_intent) == 15):
+				error = "Error :\n Group Owner intent cannot be" + \
+					" 15 for wps_method=keypad"
+				print error
+				usage()
+				quit()
+
+		# Doesn't require pin
+		# for ./wpa_cli, p2p_connect [mac] [pin#], wps_method=keypad
+		elif (self.wps_method == 'pin'):
+			if (self.pin != None):
+				print "pin ignored"
+
+		# No pin is required for pbc so it is ignored
+		elif (self.wps_method == 'pbc'):
+			if (self.pin != None):
+				print "pin ignored"
+
+		else:
+			print "Error:\n  wps_method not supported or does not exist"
+			usage()
+			quit()
+
+		# Go_intent is optional for all arguements
+		if (self.go_intent != None):
+			self.p2p_connect_arguements.update(
+				{'go_intent':dbus.Int32(self.go_intent)})
+
+	# Running p2p_connect
+	def run(self):
+		try:
+			result_pin = self.p2p_interface.Connect(
+				self.p2p_connect_arguements)
+
+		except dbus.DBusException, exc:
+				raise exc
+
+		if (self.wps_method == 'pin' and \
+		not self.p2p_connect_arguements.has_key('pin') ):
+			print "Connect return with pin value of %d " % int(result_pin)
+		gobject.MainLoop().run()
+
+if __name__ == "__main__":
+
+	# Required
+	interface_name = None
+	wps_method = None
+	addr = None
+
+	# Conditionally optional
+	pin = None
+
+	# Optional
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+	go_intent = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:m:a:p:g:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# WPS Method
+		elif (key == "-m"):
+			wps_method = value
+		# Address
+		elif (key == "-a"):
+			addr = value
+		# Pin
+		elif (key == "-p"):
+			pin = value
+		# Group Owner Intent
+		elif (key == "-g"):
+			go_intent = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Required Arguements check
+	if (interface_name == None or wps_method == None or addr == None):
+		print "Error:\n  Required arguements not specified"
+		usage()
+		quit()
+
+	# Group Owner Intent Check
+	if (go_intent != None and (int(go_intent) > 15 or int(go_intent) < 0) ):
+		print "Error:\n  Group Owner Intent must be between 0 and 15 inclusive"
+		usage()
+		quit()
+
+	# Pin Check
+	if (pin != None and len(pin) != 8):
+		print "Error:\n  Pin is not 8 digits"
+		usage()
+		quit()
+
+	try:
+		p2p_connect_test = P2P_Connect(interface_name,wpas_dbus_interface,
+			addr,pin,wps_method,go_intent)
+
+	except:
+		print "Error:\n  Invalid Arguements"
+		usage()
+		quit()
+
+	p2p_connect_test.constructArguements()
+	p2p_connect_test.run()
+
+	os._exit(0)
diff --git a/wpa_supplicant/examples/p2p/p2p_disconnect.py b/wpa_supplicant/examples/p2p/p2p_disconnect.py
new file mode 100644
index 0000000..c3e39b3
--- /dev/null
+++ b/wpa_supplicant/examples/p2p/p2p_disconnect.py
@@ -0,0 +1,169 @@
+#!/usr/bin/python
+# Tests P2P_Disconnect
+# Will perform disconnect on interface_name
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> \ " \
+		% sys.argv[0]
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i p2p-wlan0-0" % sys.argv[0]
+
+# Required Signals
+def GroupFinished(status, etc):
+	print "Disconnected"	
+	os._exit(0)
+
+class P2P_Disconnect (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global timeout
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,timeout):
+		# Initializes variables and threads
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.timeout = timeout
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		# Signals
+		self.bus.add_signal_receiver(GroupFinished,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="GroupFinished")
+
+	# Runs p2p_disconnect
+	def run(self):
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		self.p2p_interface.Disconnect()
+		gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+
+	timeout = 5
+	# Defaults for optional inputs
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	# Constructor
+	try:
+		p2p_disconnect_test = P2P_Disconnect(interface_name,
+						wpas_dbus_interface,timeout)
+
+	except:
+		print "Error:\n  Invalid wpas_dbus_interface"
+		usage()
+		quit()
+
+	# Start P2P_Disconnect
+	p2p_disconnect_test.start()
+
+	try:
+		time.sleep(int(p2p_disconnect_test.timeout))
+
+	except:
+		pass
+
+	print "Disconnect timed out"
+	quit()
diff --git a/wpa_supplicant/examples/p2p/p2p_find.py b/wpa_supplicant/examples/p2p/p2p_find.py
new file mode 100644
index 0000000..973d46a
--- /dev/null
+++ b/wpa_supplicant/examples/p2p/p2p_find.py
@@ -0,0 +1,192 @@
+#!/usr/bin/python
+# Tests p2p_find
+# Will list all devices found/lost within a time frame (timeout)
+# Then Program will exit
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> [-t <timeout>] \ " \
+		% sys.argv[0]
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -t = timeout = 0s (infinite)"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0 -t 10" % sys.argv[0]
+
+# Required Signals
+def deviceFound(devicepath):
+	print "Device found: %s" % (devicepath)
+
+def deviceLost(devicepath):
+	print "Device lost: %s" % (devicepath)
+
+class P2P_Find (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global timeout
+	global path
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,timeout):
+		# Initializes variables and threads
+		self.timeout = int(timeout)
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		#Adds listeners for find and lost
+		self.bus.add_signal_receiver(deviceFound,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="DeviceFound")
+		self.bus.add_signal_receiver(deviceLost,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="DeviceLost")
+
+
+		# Sets up p2p_find
+		P2PFindDict = dbus.Dictionary(
+				{'Timeout':int(self.timeout)})
+		self.p2p_interface.Find(P2PFindDict)
+
+	# Run p2p_find
+	def run(self):
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		gobject.MainLoop().run()
+
+if __name__ == "__main__":
+
+	# Defaults for optional inputs
+	timeout = 0
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:t:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Timeout
+		elif (key == "-t"):
+			if ( int(value) >= 0):
+				timeout = value
+			else:
+				print "Error:\n  Timeout cannot be negative"
+				usage()
+				quit()
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	# Constructor
+	try:
+		p2p_find_test = P2P_Find(interface_name, wpas_dbus_interface, timeout)
+
+	except:
+		print "Error:\n  Invalid wpas_dbus_interface"
+		usage()
+		quit()
+
+	# Start P2P_Find
+	p2p_find_test.start()
+
+	try:
+		# If timeout is 0, then run forever
+		if (timeout == 0):
+			while(True):
+				pass
+		# Else sleep for (timeout)
+		else:
+			time.sleep(p2p_find_test.timeout)
+
+	except:
+		pass
+
+	quit()
diff --git a/wpa_supplicant/examples/p2p/p2p_flush.py b/wpa_supplicant/examples/p2p/p2p_flush.py
new file mode 100644
index 0000000..ff8509d
--- /dev/null
+++ b/wpa_supplicant/examples/p2p/p2p_flush.py
@@ -0,0 +1,168 @@
+#!/usr/bin/python
+# Tests P2P_Flush
+# Will flush the p2p interface
+# Then Program will exit
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> \ " \
+		% sys.argv[0]
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0" % sys.argv[0]
+
+# Required Signals\
+def deviceLost(devicepath):
+	print "Device lost: %s" % (devicepath)
+
+class P2P_Flush (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global timeout
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,timeout):
+		# Initializes variables and threads
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.timeout = timeout
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		# Signals
+		self.bus.add_signal_receiver(deviceLost,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="DeviceLost")
+
+	# Runs p2p_flush
+	def run(self):
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		self.p2p_interface.Flush()
+		gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+	# Needed to show which devices were lost
+	timeout = 5
+	# Defaults for optional inputs
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	# Constructor
+	try:
+		p2p_flush_test = P2P_Flush(interface_name, wpas_dbus_interface,timeout)
+
+	except:
+		print "Error:\n  Invalid wpas_dbus_interface"
+		usage()
+		quit()
+
+	# Start P2P_Find
+	p2p_flush_test.start()
+
+	try:
+		time.sleep(int(p2p_flush_test.timeout))
+
+	except:
+		pass
+
+	print "p2p_flush complete"
+	quit()
diff --git a/wpa_supplicant/examples/p2p/p2p_group_add.py b/wpa_supplicant/examples/p2p/p2p_group_add.py
new file mode 100644
index 0000000..5c8fdaf
--- /dev/null
+++ b/wpa_supplicant/examples/p2p/p2p_group_add.py
@@ -0,0 +1,222 @@
+#!/usr/bin/python
+# Tests p2p_group_add
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import getopt
+import threading
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> [-p <persistent>] \ " \
+		% sys.argv[0]
+	print "		[-f <frequency>] [-o <group_object_path>] \ "
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -p = persistant group = 0 (0=false, 1=true)"
+	print "  -f = frequency"
+	print "  -o = persistent group object path"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0" % sys.argv[0]
+
+# Required Signals
+def GroupStarted(properties):
+	if properties.has_key("group_object"):
+		print 'Group Formation Complete %s' \
+			% properties["group_object"]
+	os._exit(0)
+
+def WpsFailure(status, etc):
+	print "WPS Authentication Failure".format(status)
+	print etc
+	os._exit(0)
+
+class P2P_Group_Add (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global persistent
+	global frequency
+	global persistent_group_object
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Arguements
+	global P2PDictionary
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,persistent,frequency,
+						persistent_group_object):
+		# Initializes variables and threads
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.persistent = persistent
+		self.frequency = frequency
+		self.persistent_group_object = persistent_group_object
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		#Adds listeners
+		self.bus.add_signal_receiver(GroupStarted,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="GroupStarted")
+		self.bus.add_signal_receiver(WpsFailure,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="WpsFailed")
+
+		# Sets up p2p_group_add dictionary
+	def constructArguements(self):
+		self.P2PDictionary = {'persistent':self.persistent}
+
+		if (self.frequency != None):
+			if (int(self.frequency) > 0):
+				self.P2PDictionary.update({'frequency':int(self.frequency)})
+			else:
+				print "Error:\n  Frequency must be greater than 0"
+				usage()
+				os._exit(0)
+
+		if (self.persistent_group_object != None):
+			self.P2PDictionary.update({'persistent_group_object':
+						self.persistent_group_object})
+
+	# Run p2p_group_remove
+	def run(self):
+		try:
+			self.p2p_interface.GroupAdd(self.P2PDictionary)
+
+		except:
+			print "Error:\n  Could not preform group add"
+			usage()
+			os._exit(0)
+
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+
+	# Defaults for optional inputs
+	# 0 = false, 1 = true
+	persistent = False
+	frequency = None
+	persistent_group_object = None
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:p:f:o:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Timeout
+		elif (key == "-p"):
+			if (value == '0'):
+				persistent = False
+			elif (value == '1'):
+				persistent = True
+			else:
+				print "Error:\n  Persistent can only be 1 or 0"
+				usage()
+				os._exit(0)
+		# Frequency
+		elif (key == "-f"):
+			frequency = value
+		# Persistent group object path
+		elif (key == "-o"):
+			persistent_group_object = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	try:
+		p2p_group_add_test = P2P_Group_Add(interface_name,wpas_dbus_interface,
+					persistent,frequency,persistent_group_object)
+	except:
+		print "Error:\n  Invalid Arguements"
+
+	p2p_group_add_test.constructArguements()
+	p2p_group_add_test.start()
+	time.sleep(5)
+	print "Error:\n  Group formation timed out"
+	os._exit(0)
diff --git a/wpa_supplicant/examples/p2p/p2p_invite.py b/wpa_supplicant/examples/p2p/p2p_invite.py
new file mode 100644
index 0000000..6deb397
--- /dev/null
+++ b/wpa_supplicant/examples/p2p/p2p_invite.py
@@ -0,0 +1,201 @@
+#!/usr/bin/python
+# Tests p2p_invite
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import getopt
+import threading
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> -a <addr> \ " \
+		% sys.argv[0]
+	print "		[-o <persistent_group_object>] [-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -a = address of peer"
+	print "  -o = persistent group object path"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i p2p-wlan0-0 -a 00150083523c" % sys.argv[0]
+
+# Required Signals
+def InvitationResult(invite_result):
+	print "Inviation Result signal :"
+	status = invite_result['status']
+	print "status = ", status
+	if invite_result.has_key('BSSID'):
+		bssid = invite_result['BSSID']
+		print "BSSID = ", hex(bssid[0]) , ":" , \
+		 hex(bssid[1]) , ":" , hex(bssid[2]) , ":", \
+		 hex(bssid[3]) , ":" , hex(bssid[4]) , ":" , \
+		hex(bssid[5])
+	os._exit(0)
+
+class P2P_Invite (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global addr
+	global persistent_group_object
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Arguements
+	global P2PDictionary
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,addr,
+						persistent_group_object):
+		# Initializes variables and threads
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.addr = addr
+		self.persistent_group_object = persistent_group_object
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		#Adds listeners
+		self.bus.add_signal_receiver(InvitationResult,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="InvitationResult")
+
+	# Sets up p2p_invite dictionary
+	def constructArguements(self):
+		self.P2PDictionary = \
+			{'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)}
+		if (self.persistent_group_object != None):
+			self.P2PDictionary.update({"persistent_group_object":
+					self.persistent_group_object})
+
+	# Run p2p_invite
+	def run(self):
+		try:
+			self.p2p_interface.Invite(self.P2PDictionary)
+
+		except:
+			print "Error:\n  Invalid Arguements"
+			usage()
+			os._exit(0)
+
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		gobject.MainLoop().run()
+
+if __name__ == "__main__":
+	# Defaults for optional inputs
+	addr = None
+	persistent_group_object = None
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:o:w:a:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		elif (key == "-a"):
+			addr = value
+		# Persistent group object path
+		elif (key == "-o"):
+			persistent_group_object = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	if (addr == None):
+		print "Error:\n  peer address is required"
+		usage()
+		quit()
+
+	try:
+		p2p_invite_test = \
+			P2P_Invite(interface_name,wpas_dbus_interface,
+					addr,persistent_group_object)
+	except:
+		print "Error:\n  Invalid Arguements"
+		usage()
+		os._exit(1)
+
+	p2p_invite_test.constructArguements()
+	p2p_invite_test.start()
+	time.sleep(10)
+	print "Error:\n  p2p_invite timed out"
+	os._exit(0)
diff --git a/wpa_supplicant/examples/p2p/p2p_listen.py b/wpa_supplicant/examples/p2p/p2p_listen.py
new file mode 100644
index 0000000..bb3c1e4
--- /dev/null
+++ b/wpa_supplicant/examples/p2p/p2p_listen.py
@@ -0,0 +1,182 @@
+#!/usr/bin/python
+# Tests P2P_Find
+# Will listen
+# Then Program will exit
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> [-t <timeout>] \ " \
+		% sys.argv[0]
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -t = timeout = 0s (infinite)"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0 -t 5" % sys.argv[0]
+
+# Required Signals
+def p2pStateChange(status):
+	print status
+
+class P2P_Listen(threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global timeout
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,timeout):
+		# Initializes variables and threads
+		self.timeout = int(timeout)
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		self.bus.add_signal_receiver(p2pStateChange,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="P2PStateChanged")
+
+	# Run p2p_find
+	def run(self):
+		# Sets up p2p_listen
+		self.p2p_interface.Listen(int(self.timeout))
+
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		gobject.MainLoop().run()
+
+if __name__ == "__main__":
+
+	# Defaults for optional inputs
+	timeout = 0
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"hi:t:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Timeout
+		elif (key == "-t"):
+			if ( int(value) >= 0):
+				timeout = value
+			else:
+				print "Error:\n  Timeout cannot be negative"
+				usage()
+				quit()
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	# Constructor
+	try:
+		p2p_listen_test = P2P_Listen(interface_name, wpas_dbus_interface, timeout)
+
+	except:
+		print "Error:\n  Invalid wpas_dbus_interface"
+		usage()
+		quit()
+
+	# Start P2P_Find
+	p2p_listen_test.start()
+
+	try:
+		# If timeout is 0, then run forever
+		if (int(p2p_listen_test.timeout) == 0):
+			while(True):
+				pass
+		# Else sleep for (timeout)
+		else:
+			time.sleep(int(p2p_listen_test.timeout))
+
+	except:
+		pass
+
+	quit()
diff --git a/wpa_supplicant/examples/p2p/p2p_stop_find.py b/wpa_supplicant/examples/p2p/p2p_stop_find.py
new file mode 100644
index 0000000..f6c03b0
--- /dev/null
+++ b/wpa_supplicant/examples/p2p/p2p_stop_find.py
@@ -0,0 +1,174 @@
+#!/usr/bin/python
+# Tests p2p_stop_find
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+	print "Usage:"
+	print "  %s -i <interface_name> \ " \
+		% sys.argv[0]
+	print "  		[-w <wpas_dbus_interface>]"
+	print "Options:"
+	print "  -i = interface name"
+	print "  -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+	print "Example:"
+	print "  %s -i wlan0" % sys.argv[0]
+
+# Required Signals
+def deviceLost(devicepath):
+	print "Device lost: %s" % (devicepath)
+
+def p2pStateChange(status):
+	print status
+	os._exit(0)
+
+class P2P_Stop_Find (threading.Thread):
+	# Needed Variables
+	global bus
+	global wpas_object
+	global interface_object
+	global p2p_interface
+	global interface_name
+	global wpas
+	global wpas_dbus_interface
+	global path
+	global timeout
+
+	# Dbus Paths
+	global wpas_dbus_opath
+	global wpas_dbus_interfaces_opath
+	global wpas_dbus_interfaces_interface
+	global wpas_dbus_interfaces_p2pdevice
+
+	# Constructor
+	def __init__(self,interface_name,wpas_dbus_interface,timeout):
+		# Initializes variables and threads
+		self.interface_name = interface_name
+		self.wpas_dbus_interface = wpas_dbus_interface
+		self.timeout = timeout
+
+		# Initializes thread and daemon allows for ctrl-c kill
+		threading.Thread.__init__(self)
+		self.daemon = True
+
+		# Generating interface/object paths
+		self.wpas_dbus_opath = "/" + \
+				self.wpas_dbus_interface.replace(".","/")
+		self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+				"/Interfaces"
+		self.wpas_dbus_interfaces_interface = \
+				self.wpas_dbus_interface + ".Interface"
+		self.wpas_dbus_interfaces_p2pdevice = \
+				self.wpas_dbus_interfaces_interface \
+				+ ".P2PDevice"
+
+		# Getting interfaces and objects
+		DBusGMainLoop(set_as_default=True)
+		self.bus = dbus.SystemBus()
+		self.wpas_object = self.bus.get_object(
+				self.wpas_dbus_interface,
+				self.wpas_dbus_opath)
+		self.wpas = dbus.Interface(self.wpas_object,
+				self.wpas_dbus_interface)
+
+		# Try to see if supplicant knows about interface
+		# If not, throw an exception
+		try:
+			self.path = self.wpas.GetInterface(
+					self.interface_name)
+		except dbus.DBusException, exc:
+			error = 'Error:\n  Interface ' + self.interface_name \
+				+ ' was not found'
+			print error
+			usage()
+			os._exit(0)
+
+		self.interface_object = self.bus.get_object(
+				self.wpas_dbus_interface, self.path)
+		self.p2p_interface = dbus.Interface(self.interface_object,
+				self.wpas_dbus_interfaces_p2pdevice)
+
+		# Signals
+		self.bus.add_signal_receiver(deviceLost,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="DeviceLost")
+		self.bus.add_signal_receiver(p2pStateChange,
+			dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+			signal_name="P2PStateChanged")
+
+	# Runs p2p_stop_find
+	def run(self):
+		# Allows other threads to keep working while MainLoop runs
+		# Required for timeout implementation
+		gobject.MainLoop().get_context().iteration(True)
+		gobject.threads_init()
+		self.p2p_interface.StopFind()
+		gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+	# Needed because P2PStateChanged signal is not caught
+	timeout = 5
+	# Defaults for optional inputs
+	wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+	# interface_name is required
+	interface_name = None
+
+	# Using getopts to handle options
+	try:
+		options, args = getopt.getopt(sys.argv[1:],"ht:i:w:")
+
+	except getopt.GetoptError:
+		usage()
+		quit()
+
+	# If theres a switch, override default option
+	for key, value in options:
+		# Help
+		if (key == "-h"):
+			usage()
+			quit()
+		# Interface Name
+		elif (key == "-i"):
+			interface_name = value
+		# Dbus interface
+		elif (key == "-w"):
+			wpas_dbus_interface = value
+		else:
+			assert False, "unhandled option"
+
+	# Interface name is required and was not given
+	if (interface_name == None):
+		print "Error:\n  interface_name is required"
+		usage()
+		quit()
+
+	# Constructor
+	try:
+		p2p_stop_find_test = P2P_Stop_Find(interface_name,
+						wpas_dbus_interface,timeout)
+
+	except:
+		print "Error:\n  Invalid wpas_dbus_interface"
+		usage()
+		quit()
+
+	# Start P2P_Find
+	p2p_stop_find_test.start()
+
+	try:
+		time.sleep(int(p2p_stop_find_test.timeout))
+
+	except:
+		pass
+
+	print "p2p find stopped"
+	quit()
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 1fd6e00..efa9be8 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -19,7 +19,7 @@
 #include "gas_query.h"
 
 
-#define GAS_QUERY_TIMEOUT 5
+#define GAS_QUERY_TIMEOUT_PERIOD 5
 
 
 struct gas_query_pending {
@@ -457,7 +457,7 @@
 		return -1;
 	}
 
-	eloop_register_timeout(GAS_QUERY_TIMEOUT, 0, gas_query_timeout,
+	eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout,
 			       gas, query);
 
 	return dialog_token;
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
new file mode 100644
index 0000000..0eb6119
--- /dev/null
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2009, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "config.h"
+#include "bss.h"
+#include "gas_query.h"
+#include "interworking.h"
+#include "hs20_supplicant.h"
+
+
+void wpas_hs20_add_indication(struct wpabuf *buf)
+{
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 5);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE);
+	wpabuf_put_u8(buf, 0x00); /* Hotspot Configuration */
+}
+
+
+struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
+				    size_t payload_len)
+{
+	struct wpabuf *buf;
+	u8 *len_pos;
+
+	buf = gas_anqp_build_initial_req(0, 100 + payload_len);
+	if (buf == NULL)
+		return NULL;
+
+	len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+	if (stypes == BIT(HS20_STYPE_NAI_HOME_REALM_QUERY)) {
+		wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		if (payload)
+			wpabuf_put_data(buf, payload, payload_len);
+	} else {
+		u8 i;
+		wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		for (i = 0; i < 32; i++) {
+			if (stypes & BIT(i))
+				wpabuf_put_u8(buf, i);
+		}
+	}
+	gas_anqp_set_element_len(buf, len_pos);
+
+	gas_anqp_set_len(buf);
+
+	return buf;
+}
+
+
+int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
+		       const u8 *payload, size_t payload_len)
+{
+	struct wpabuf *buf;
+	int ret = 0;
+	int freq;
+	struct wpa_bss *bss;
+	int res;
+
+	freq = wpa_s->assoc_freq;
+	bss = wpa_bss_get_bssid(wpa_s, dst);
+	if (bss)
+		freq = bss->freq;
+	if (freq <= 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for "
+		   "subtypes 0x%x", MAC2STR(dst), stypes);
+
+	buf = hs20_build_anqp_req(stypes, payload, payload_len);
+	if (buf == NULL)
+		return -1;
+
+	res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+		ret = -1;
+	} else
+		wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
+			   "%u", res);
+
+	wpabuf_free(buf);
+	return ret;
+}
+
+
+void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
+				  const u8 *sa, const u8 *data, size_t slen)
+{
+	const u8 *pos = data;
+	u8 subtype;
+	struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
+	struct wpa_bss_anqp *anqp = NULL;
+
+	if (slen < 2)
+		return;
+
+	if (bss)
+		anqp = bss->anqp;
+
+	subtype = *pos++;
+	slen--;
+
+	pos++; /* Reserved */
+	slen--;
+
+	switch (subtype) {
+	case HS20_STYPE_CAPABILITY_LIST:
+		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+			" HS Capability List", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen);
+		break;
+	case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
+		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+			" Operator Friendly Name", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->hs20_operator_friendly_name);
+			anqp->hs20_operator_friendly_name =
+				wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case HS20_STYPE_WAN_METRICS:
+		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+			" WAN Metrics", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "WAN Metrics", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->hs20_wan_metrics);
+			anqp->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case HS20_STYPE_CONNECTION_CAPABILITY:
+		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+			" Connection Capability", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->hs20_connection_capability);
+			anqp->hs20_connection_capability =
+				wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case HS20_STYPE_OPERATING_CLASS:
+		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+			" Operating Class", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->hs20_operating_class);
+			anqp->hs20_operating_class =
+				wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
+		break;
+	}
+}
diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h
new file mode 100644
index 0000000..6eb3926
--- /dev/null
+++ b/wpa_supplicant/hs20_supplicant.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HS20_SUPPLICANT_H
+#define HS20_SUPPLICANT_H
+
+void wpas_hs20_add_indication(struct wpabuf *buf);
+
+int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
+		       const u8 *payload, size_t payload_len);
+struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
+				    size_t payload_len);
+void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
+				  const u8 *sa, const u8 *data, size_t slen);
+
+#endif /* HS20_SUPPLICANT_H */
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index ac89ff8..22f709f 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -1,6 +1,6 @@
 /*
  * Interworking (IEEE 802.11u)
- * Copyright (c) 2011, Qualcomm Atheros
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,15 +12,19 @@
 #include "common/ieee802_11_defs.h"
 #include "common/gas.h"
 #include "common/wpa_ctrl.h"
+#include "utils/pcsc_funcs.h"
 #include "drivers/driver.h"
 #include "eap_common/eap_defs.h"
+#include "eap_peer/eap.h"
 #include "eap_peer/eap_methods.h"
 #include "wpa_supplicant_i.h"
 #include "config.h"
+#include "config_ssid.h"
 #include "bss.h"
 #include "scan.h"
 #include "notify.h"
 #include "gas_query.h"
+#include "hs20_supplicant.h"
 #include "interworking.h"
 
 
@@ -39,6 +43,31 @@
 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
 
 
+static void interworking_reconnect(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+	}
+	wpa_s->disconnected = 0;
+	wpa_s->reassociate = 1;
+
+	if (wpa_s->last_scan_res_used > 0) {
+		struct os_time now;
+		os_get_time(&now);
+		if (now.sec - wpa_s->last_scan.sec <= 5) {
+			wpa_printf(MSG_DEBUG, "Interworking: Old scan results "
+				   "are fresh - connect without new scan");
+			if (wpas_select_network_from_last_scan(wpa_s) >= 0)
+				return;
+		}
+	}
+
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
 static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
 				      struct wpabuf *extra)
 {
@@ -79,29 +108,127 @@
 }
 
 
+static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->roaming_consortium_len)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int cred_with_3gpp(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->pcsc || cred->imsi)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int cred_with_nai_realm(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->pcsc || cred->imsi)
+			continue;
+		if (!cred->eap_method)
+			return 1;
+		if (cred->realm && cred->roaming_consortium_len == 0)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int cred_with_domain(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->domain || cred->pcsc || cred->imsi)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int additional_roaming_consortiums(struct wpa_bss *bss)
+{
+	const u8 *ie;
+	ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+	if (ie == NULL || ie[1] == 0)
+		return 0;
+	return ie[2]; /* Number of ANQP OIs */
+}
+
+
 static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
 				      struct wpa_bss *bss)
 {
 	struct wpabuf *buf;
 	int ret = 0;
 	int res;
-	u16 info_ids[] = {
-		ANQP_CAPABILITY_LIST,
-		ANQP_VENUE_NAME,
-		ANQP_NETWORK_AUTH_TYPE,
-		ANQP_ROAMING_CONSORTIUM,
-		ANQP_IP_ADDR_TYPE_AVAILABILITY,
-		ANQP_NAI_REALM,
-		ANQP_3GPP_CELLULAR_NETWORK,
-		ANQP_DOMAIN_NAME
-	};
+	u16 info_ids[8];
+	size_t num_info_ids = 0;
 	struct wpabuf *extra = NULL;
+	int all = wpa_s->fetch_all_anqp;
 
 	wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
 		   MAC2STR(bss->bssid));
 
-	buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]),
-			     extra);
+	info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
+	if (all) {
+		info_ids[num_info_ids++] = ANQP_VENUE_NAME;
+		info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE;
+	}
+	if (all || (cred_with_roaming_consortium(wpa_s) &&
+		    additional_roaming_consortiums(bss)))
+		info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM;
+	if (all)
+		info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY;
+	if (all || cred_with_nai_realm(wpa_s))
+		info_ids[num_info_ids++] = ANQP_NAI_REALM;
+	if (all || cred_with_3gpp(wpa_s))
+		info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK;
+	if (all || cred_with_domain(wpa_s))
+		info_ids[num_info_ids++] = ANQP_DOMAIN_NAME;
+	wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info",
+		    (u8 *) info_ids, num_info_ids * 2);
+
+#ifdef CONFIG_HS20
+	if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
+		u8 *len_pos;
+
+		extra = wpabuf_alloc(100);
+		if (!extra)
+			return -1;
+
+		len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(extra, OUI_WFA);
+		wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
+		wpabuf_put_u8(extra, 0); /* Reserved */
+		wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
+		if (all) {
+			wpabuf_put_u8(extra,
+				      HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+			wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
+			wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
+			wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
+		}
+		gas_anqp_set_element_len(extra, len_pos);
+	}
+#endif /* CONFIG_HS20 */
+
+	buf = anqp_build_req(info_ids, num_info_ids, extra);
 	wpabuf_free(extra);
 	if (buf == NULL)
 		return -1;
@@ -291,7 +418,7 @@
 		wpa_printf(MSG_DEBUG, "No room for EAP Methods");
 		return NULL;
 	}
-	r->eap = os_zalloc(r->eap_count * sizeof(struct nai_realm_eap));
+	r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap));
 	if (r->eap == NULL)
 		return NULL;
 
@@ -327,7 +454,7 @@
 		return NULL;
 	}
 
-	realm = os_zalloc(num * sizeof(struct nai_realm));
+	realm = os_calloc(num, sizeof(struct nai_realm));
 	if (realm == NULL)
 		return NULL;
 
@@ -416,20 +543,41 @@
 }
 
 
-static struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
+static int nai_realm_cred_cert(struct nai_realm_eap *eap)
+{
+	if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
+		return 0; /* method not supported */
+
+	if (eap->method != EAP_TYPE_TLS) {
+		/* Only EAP-TLS supported for credential authentication */
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
 						 struct nai_realm *realm)
 {
 	u8 e;
 
-	if (wpa_s->conf->home_username == NULL ||
-	    wpa_s->conf->home_username[0] == '\0' ||
-	    wpa_s->conf->home_password == NULL ||
-	    wpa_s->conf->home_password[0] == '\0')
+	if (cred == NULL ||
+	    cred->username == NULL ||
+	    cred->username[0] == '\0' ||
+	    ((cred->password == NULL ||
+	      cred->password[0] == '\0') &&
+	     (cred->private_key == NULL ||
+	      cred->private_key[0] == '\0')))
 		return NULL;
 
 	for (e = 0; e < realm->eap_count; e++) {
 		struct nai_realm_eap *eap = &realm->eap[e];
-		if (nai_realm_cred_username(eap))
+		if (cred->password && cred->password[0] &&
+		    nai_realm_cred_username(eap))
+			return eap;
+		if (cred->private_key && cred->private_key[0] &&
+		    nai_realm_cred_cert(eap))
 			return eap;
 	}
 
@@ -439,21 +587,17 @@
 
 #ifdef INTERWORKING_3GPP
 
-static int plmn_id_match(struct wpabuf *anqp, const char *imsi)
+static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
 {
-	const char *sep;
 	u8 plmn[3];
 	const u8 *pos, *end;
 	u8 udhl;
 
-	sep = os_strchr(imsi, '-');
-	if (sep == NULL || (sep - imsi != 5 && sep - imsi != 6))
-		return 0;
-
 	/* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
 	plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
 	plmn[1] = imsi[2] - '0';
-	if (sep - imsi == 6)
+	/* default to MNC length 3 if unknown */
+	if (mnc_len != 2)
 		plmn[1] |= (imsi[5] - '0') << 4;
 	else
 		plmn[1] |= 0xf0;
@@ -495,6 +639,7 @@
 					break;
 				if (os_memcmp(pos, plmn, 3) == 0)
 					return 1; /* Found matching PLMN */
+				pos += 3;
 			}
 		}
 
@@ -505,10 +650,11 @@
 }
 
 
-static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
+static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
+			  size_t mnc_len, char prefix)
 {
 	const char *sep, *msin;
-	char nai[100], *end, *pos;
+	char *end, *pos;
 	size_t msin_len, plmn_len;
 
 	/*
@@ -523,17 +669,22 @@
 		return -1;
 	}
 	sep = os_strchr(imsi, '-');
-	if (sep == NULL)
+	if (sep) {
+		plmn_len = sep - imsi;
+		msin = sep + 1;
+	} else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) {
+		plmn_len = 3 + mnc_len;
+		msin = imsi + plmn_len;
+	} else
 		return -1;
-	plmn_len = sep - imsi;
 	if (plmn_len != 5 && plmn_len != 6)
 		return -1;
-	msin = sep + 1;
 	msin_len = os_strlen(msin);
 
 	pos = nai;
-	end = pos + sizeof(nai);
-	*pos++ = prefix;
+	end = nai + nai_len;
+	if (prefix)
+		*pos++ = prefix;
 	os_memcpy(pos, imsi, plmn_len);
 	pos += plmn_len;
 	os_memcpy(pos, msin, msin_len);
@@ -551,18 +702,80 @@
 	pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
 			   imsi[0], imsi[1], imsi[2]);
 
+	return 0;
+}
+
+
+static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
+{
+	char nai[100];
+	if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0)
+		return -1;
 	return wpa_config_set_quoted(ssid, "identity", nai);
 }
 
 #endif /* INTERWORKING_3GPP */
 
 
+static int interworking_set_hs20_params(struct wpa_ssid *ssid)
+{
+	if (wpa_config_set(ssid, "key_mgmt", "WPA-EAP", 0) < 0)
+		return -1;
+	if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
+		return -1;
+	if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
+		return -1;
+	return 0;
+}
+
+
 static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
 				     struct wpa_bss *bss)
 {
 #ifdef INTERWORKING_3GPP
+	struct wpa_cred *cred;
 	struct wpa_ssid *ssid;
 	const u8 *ie;
+	int eap_type;
+	int res;
+	char prefix;
+
+	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
+		return -1;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		char *sep;
+		const char *imsi;
+		int mnc_len;
+
+#ifdef PCSC_FUNCS
+		if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
+		    wpa_s->imsi[0]) {
+			imsi = wpa_s->imsi;
+			mnc_len = wpa_s->mnc_len;
+			goto compare;
+		}
+#endif /* PCSC_FUNCS */
+
+		if (cred->imsi == NULL || !cred->imsi[0] ||
+		    cred->milenage == NULL || !cred->milenage[0])
+			continue;
+
+		sep = os_strchr(cred->imsi, '-');
+		if (sep == NULL ||
+		    (sep - cred->imsi != 5 && sep - cred->imsi != 6))
+			continue;
+		mnc_len = sep - cred->imsi - 3;
+		imsi = cred->imsi;
+
+#ifdef PCSC_FUNCS
+	compare:
+#endif /* PCSC_FUNCS */
+		if (plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len))
+			break;
+	}
+	if (cred == NULL)
+		return -1;
 
 	ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
 	if (ie == NULL)
@@ -576,6 +789,7 @@
 
 	wpas_notify_network_added(wpa_s, ssid);
 	wpa_config_set_network_defaults(ssid);
+	ssid->priority = cred->priority;
 	ssid->temporary = 1;
 	ssid->ssid = os_zalloc(ie[1] + 1);
 	if (ssid->ssid == NULL)
@@ -583,32 +797,66 @@
 	os_memcpy(ssid->ssid, ie + 2, ie[1]);
 	ssid->ssid_len = ie[1];
 
-	/* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */
-	if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) {
-		wpa_printf(MSG_DEBUG, "EAP-SIM not supported");
+	if (interworking_set_hs20_params(ssid) < 0)
+		goto fail;
+
+	eap_type = EAP_TYPE_SIM;
+	if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard))
+		eap_type = EAP_TYPE_AKA;
+	if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) {
+		if (cred->eap_method[0].method == EAP_TYPE_SIM ||
+		    cred->eap_method[0].method == EAP_TYPE_AKA ||
+		    cred->eap_method[0].method == EAP_TYPE_AKA_PRIME)
+			eap_type = cred->eap_method[0].method;
+	}
+
+	switch (eap_type) {
+	case EAP_TYPE_SIM:
+		prefix = '1';
+		res = wpa_config_set(ssid, "eap", "SIM", 0);
+		break;
+	case EAP_TYPE_AKA:
+		prefix = '0';
+		res = wpa_config_set(ssid, "eap", "AKA", 0);
+		break;
+	case EAP_TYPE_AKA_PRIME:
+		prefix = '6';
+		res = wpa_config_set(ssid, "eap", "AKA'", 0);
+		break;
+	default:
+		res = -1;
+		break;
+	}
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported",
+			   eap_type);
 		goto fail;
 	}
-	if (set_root_nai(ssid, wpa_s->conf->home_imsi, '1') < 0) {
+
+	if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) {
 		wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
 		goto fail;
 	}
 
-	if (wpa_s->conf->home_milenage && wpa_s->conf->home_milenage[0]) {
+	if (cred->milenage && cred->milenage[0]) {
 		if (wpa_config_set_quoted(ssid, "password",
-					  wpa_s->conf->home_milenage) < 0)
+					  cred->milenage) < 0)
 			goto fail;
-	} else {
-		/* TODO: PIN */
+	} else if (cred->pcsc) {
 		if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
 			goto fail;
+		if (wpa_s->conf->pcsc_pin &&
+		    wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin)
+		    < 0)
+			goto fail;
 	}
 
-	if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] &&
-	    wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password)
-	    < 0)
+	if (cred->password && cred->password[0] &&
+	    wpa_config_set_quoted(ssid, "password", cred->password) < 0)
 		goto fail;
 
-	wpa_supplicant_select_network(wpa_s, ssid);
+	wpa_config_update_prio_list(wpa_s->conf);
+	interworking_reconnect(wpa_s);
 
 	return 0;
 
@@ -620,8 +868,262 @@
 }
 
 
+static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id,
+					    size_t rc_len)
+{
+	const u8 *pos, *end;
+	u8 lens;
+
+	if (ie == NULL)
+		return 0;
+
+	pos = ie + 2;
+	end = ie + 2 + ie[1];
+
+	/* Roaming Consortium element:
+	 * Number of ANQP OIs
+	 * OI #1 and #2 lengths
+	 * OI #1, [OI #2], [OI #3]
+	 */
+
+	if (pos + 2 > end)
+		return 0;
+
+	pos++; /* skip Number of ANQP OIs */
+	lens = *pos++;
+	if (pos + (lens & 0x0f) + (lens >> 4) > end)
+		return 0;
+
+	if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+		return 1;
+	pos += lens & 0x0f;
+
+	if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+		return 1;
+	pos += lens >> 4;
+
+	if (pos < end && (size_t) (end - pos) == rc_len &&
+	    os_memcmp(pos, rc_id, rc_len) == 0)
+		return 1;
+
+	return 0;
+}
+
+
+static int roaming_consortium_anqp_match(const struct wpabuf *anqp,
+					 const u8 *rc_id, size_t rc_len)
+{
+	const u8 *pos, *end;
+	u8 len;
+
+	if (anqp == NULL)
+		return 0;
+
+	pos = wpabuf_head(anqp);
+	end = pos + wpabuf_len(anqp);
+
+	/* Set of <OI Length, OI> duples */
+	while (pos < end) {
+		len = *pos++;
+		if (pos + len > end)
+			break;
+		if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+			return 1;
+		pos += len;
+	}
+
+	return 0;
+}
+
+
+static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
+				    const u8 *rc_id, size_t rc_len)
+{
+	return roaming_consortium_element_match(ie, rc_id, rc_len) ||
+		roaming_consortium_anqp_match(anqp, rc_id, rc_len);
+}
+
+
+static struct wpa_cred * interworking_credentials_available_roaming_consortium(
+	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	struct wpa_cred *cred, *selected = NULL;
+	const u8 *ie;
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+
+	if (ie == NULL &&
+	    (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
+		return NULL;
+
+	if (wpa_s->conf->cred == NULL)
+		return NULL;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->roaming_consortium_len == 0)
+			continue;
+
+		if (!roaming_consortium_match(ie,
+					      bss->anqp ?
+					      bss->anqp->roaming_consortium :
+					      NULL,
+					      cred->roaming_consortium,
+					      cred->roaming_consortium_len))
+			continue;
+
+		if (selected == NULL ||
+		    selected->priority < cred->priority)
+			selected = cred;
+	}
+
+	return selected;
+}
+
+
+static int interworking_set_eap_params(struct wpa_ssid *ssid,
+				       struct wpa_cred *cred, int ttls)
+{
+	if (cred->eap_method) {
+		ttls = cred->eap_method->vendor == EAP_VENDOR_IETF &&
+			cred->eap_method->method == EAP_TYPE_TTLS;
+
+		os_free(ssid->eap.eap_methods);
+		ssid->eap.eap_methods =
+			os_malloc(sizeof(struct eap_method_type) * 2);
+		if (ssid->eap.eap_methods == NULL)
+			return -1;
+		os_memcpy(ssid->eap.eap_methods, cred->eap_method,
+			  sizeof(*cred->eap_method));
+		ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF;
+		ssid->eap.eap_methods[1].method = EAP_TYPE_NONE;
+	}
+
+	if (ttls && cred->username && cred->username[0]) {
+		const char *pos;
+		char *anon;
+		/* Use anonymous NAI in Phase 1 */
+		pos = os_strchr(cred->username, '@');
+		if (pos) {
+			size_t buflen = 9 + os_strlen(pos) + 1;
+			anon = os_malloc(buflen);
+			if (anon == NULL)
+				return -1;
+			os_snprintf(anon, buflen, "anonymous%s", pos);
+		} else if (cred->realm) {
+			size_t buflen = 10 + os_strlen(cred->realm) + 1;
+			anon = os_malloc(buflen);
+			if (anon == NULL)
+				return -1;
+			os_snprintf(anon, buflen, "anonymous@%s", cred->realm);
+		} else {
+			anon = os_strdup("anonymous");
+			if (anon == NULL)
+				return -1;
+		}
+		if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
+		    0) {
+			os_free(anon);
+			return -1;
+		}
+		os_free(anon);
+	}
+
+	if (cred->username && cred->username[0] &&
+	    wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
+		return -1;
+
+	if (cred->password && cred->password[0]) {
+		if (cred->ext_password &&
+		    wpa_config_set(ssid, "password", cred->password, 0) < 0)
+			return -1;
+		if (!cred->ext_password &&
+		    wpa_config_set_quoted(ssid, "password", cred->password) <
+		    0)
+			return -1;
+	}
+
+	if (cred->client_cert && cred->client_cert[0] &&
+	    wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0)
+		return -1;
+
+	if (cred->private_key && cred->private_key[0] &&
+	    wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0)
+		return -1;
+
+	if (cred->private_key_passwd && cred->private_key_passwd[0] &&
+	    wpa_config_set_quoted(ssid, "private_key_passwd",
+				  cred->private_key_passwd) < 0)
+		return -1;
+
+	if (cred->phase1) {
+		os_free(ssid->eap.phase1);
+		ssid->eap.phase1 = os_strdup(cred->phase1);
+	}
+	if (cred->phase2) {
+		os_free(ssid->eap.phase2);
+		ssid->eap.phase2 = os_strdup(cred->phase2);
+	}
+
+	if (cred->ca_cert && cred->ca_cert[0] &&
+	    wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int interworking_connect_roaming_consortium(
+	struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
+	struct wpa_bss *bss, const u8 *ssid_ie)
+{
+	struct wpa_ssid *ssid;
+
+	wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on "
+		   "roaming consortium match", MAC2STR(bss->bssid));
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (ssid == NULL)
+		return -1;
+	wpas_notify_network_added(wpa_s, ssid);
+	wpa_config_set_network_defaults(ssid);
+	ssid->priority = cred->priority;
+	ssid->temporary = 1;
+	ssid->ssid = os_zalloc(ssid_ie[1] + 1);
+	if (ssid->ssid == NULL)
+		goto fail;
+	os_memcpy(ssid->ssid, ssid_ie + 2, ssid_ie[1]);
+	ssid->ssid_len = ssid_ie[1];
+
+	if (interworking_set_hs20_params(ssid) < 0)
+		goto fail;
+
+	if (cred->eap_method == NULL) {
+		wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for "
+			   "credential using roaming consortium");
+		goto fail;
+	}
+
+	if (interworking_set_eap_params(
+		    ssid, cred,
+		    cred->eap_method->vendor == EAP_VENDOR_IETF &&
+		    cred->eap_method->method == EAP_TYPE_TTLS) < 0)
+		goto fail;
+
+	wpa_config_update_prio_list(wpa_s->conf);
+	interworking_reconnect(wpa_s);
+
+	return 0;
+
+fail:
+	wpas_notify_network_removed(wpa_s, ssid);
+	wpa_config_remove_network(wpa_s->conf, ssid->id);
+	return -1;
+}
+
+
 int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
+	struct wpa_cred *cred;
 	struct wpa_ssid *ssid;
 	struct nai_realm *realm;
 	struct nai_realm_eap *eap = NULL;
@@ -629,7 +1131,7 @@
 	char buf[100];
 	const u8 *ie;
 
-	if (bss == NULL)
+	if (wpa_s->conf->cred == NULL || bss == NULL)
 		return -1;
 	ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
 	if (ie == NULL || ie[1] == 0) {
@@ -638,17 +1140,38 @@
 		return -1;
 	}
 
-	realm = nai_realm_parse(bss->anqp_nai_realm, &count);
+	if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
+		/*
+		 * We currently support only HS 2.0 networks and those are
+		 * required to use WPA2-Enterprise.
+		 */
+		wpa_printf(MSG_DEBUG, "Interworking: Network does not use "
+			   "RSN");
+		return -1;
+	}
+
+	cred = interworking_credentials_available_roaming_consortium(wpa_s,
+								     bss);
+	if (cred)
+		return interworking_connect_roaming_consortium(wpa_s, cred,
+							       bss, ie);
+
+	realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
+				&count);
 	if (realm == NULL) {
 		wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
 			   "Realm list from " MACSTR, MAC2STR(bss->bssid));
 		count = 0;
 	}
 
-	for (i = 0; i < count; i++) {
-		if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm))
-			continue;
-		eap = nai_realm_find_eap(wpa_s, &realm[i]);
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		for (i = 0; i < count; i++) {
+			if (!nai_realm_match(&realm[i], cred->realm))
+				continue;
+			eap = nai_realm_find_eap(cred, &realm[i]);
+			if (eap)
+				break;
+		}
 		if (eap)
 			break;
 	}
@@ -677,6 +1200,7 @@
 	}
 	wpas_notify_network_added(wpa_s, ssid);
 	wpa_config_set_network_defaults(ssid);
+	ssid->priority = cred->priority;
 	ssid->temporary = 1;
 	ssid->ssid = os_zalloc(ie[1] + 1);
 	if (ssid->ssid == NULL)
@@ -684,20 +1208,13 @@
 	os_memcpy(ssid->ssid, ie + 2, ie[1]);
 	ssid->ssid_len = ie[1];
 
+	if (interworking_set_hs20_params(ssid) < 0)
+		goto fail;
+
 	if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
 						     eap->method), 0) < 0)
 		goto fail;
 
-	if (wpa_s->conf->home_username && wpa_s->conf->home_username[0] &&
-	    wpa_config_set_quoted(ssid, "identity",
-				  wpa_s->conf->home_username) < 0)
-		goto fail;
-
-	if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] &&
-	    wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password)
-	    < 0)
-		goto fail;
-
 	switch (eap->method) {
 	case EAP_TYPE_TTLS:
 		if (eap->inner_method) {
@@ -737,16 +1254,18 @@
 		if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
 			goto fail;
 		break;
+	case EAP_TYPE_TLS:
+		break;
 	}
 
-	if (wpa_s->conf->home_ca_cert && wpa_s->conf->home_ca_cert[0] &&
-	    wpa_config_set_quoted(ssid, "ca_cert", wpa_s->conf->home_ca_cert) <
-	    0)
+	if (interworking_set_eap_params(ssid, cred,
+					eap->method == EAP_TYPE_TTLS) < 0)
 		goto fail;
 
 	nai_realm_free(realm, count);
 
-	wpa_supplicant_select_network(wpa_s, ssid);
+	wpa_config_update_prio_list(wpa_s->conf);
+	interworking_reconnect(wpa_s);
 
 	return 0;
 
@@ -758,92 +1277,310 @@
 }
 
 
-static int interworking_credentials_available_3gpp(
+static struct wpa_cred * interworking_credentials_available_3gpp(
 	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
-	int ret = 0;
+	struct wpa_cred *cred, *selected = NULL;
+	int ret;
 
 #ifdef INTERWORKING_3GPP
-	if (bss->anqp_3gpp == NULL)
-		return ret;
+	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
+		return NULL;
 
-	if (wpa_s->conf->home_imsi == NULL || !wpa_s->conf->home_imsi[0] ||
-	    wpa_s->conf->home_milenage == NULL ||
-	    !wpa_s->conf->home_milenage[0])
-		return ret;
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		char *sep;
+		const char *imsi;
+		int mnc_len;
 
-	wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " MACSTR,
-		   MAC2STR(bss->bssid));
-	ret = plmn_id_match(bss->anqp_3gpp, wpa_s->conf->home_imsi);
-	wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
+#ifdef PCSC_FUNCS
+		if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
+		    wpa_s->imsi[0]) {
+			imsi = wpa_s->imsi;
+			mnc_len = wpa_s->mnc_len;
+			goto compare;
+		}
+#endif /* PCSC_FUNCS */
+
+		if (cred->imsi == NULL || !cred->imsi[0] ||
+		    cred->milenage == NULL || !cred->milenage[0])
+			continue;
+
+		sep = os_strchr(cred->imsi, '-');
+		if (sep == NULL ||
+		    (sep - cred->imsi != 5 && sep - cred->imsi != 6))
+			continue;
+		mnc_len = sep - cred->imsi - 3;
+		imsi = cred->imsi;
+
+#ifdef PCSC_FUNCS
+	compare:
+#endif /* PCSC_FUNCS */
+		wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from "
+			   MACSTR, MAC2STR(bss->bssid));
+		ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
+		wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
+		if (ret) {
+			if (selected == NULL ||
+			    selected->priority < cred->priority)
+				selected = cred;
+		}
+	}
 #endif /* INTERWORKING_3GPP */
-	return ret;
+	return selected;
 }
 
 
-static int interworking_credentials_available_realm(
+static struct wpa_cred * interworking_credentials_available_realm(
 	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
+	struct wpa_cred *cred, *selected = NULL;
 	struct nai_realm *realm;
 	u16 count, i;
-	int found = 0;
 
-	if (bss->anqp_nai_realm == NULL)
-		return 0;
+	if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
+		return NULL;
 
-	if (wpa_s->conf->home_realm == NULL)
-		return 0;
+	if (wpa_s->conf->cred == NULL)
+		return NULL;
 
 	wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
 		   MACSTR, MAC2STR(bss->bssid));
-	realm = nai_realm_parse(bss->anqp_nai_realm, &count);
+	realm = nai_realm_parse(bss->anqp->nai_realm, &count);
 	if (realm == NULL) {
 		wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
 			   "Realm list from " MACSTR, MAC2STR(bss->bssid));
-		return 0;
+		return NULL;
 	}
 
-	for (i = 0; i < count; i++) {
-		if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm))
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->realm == NULL)
 			continue;
-		if (nai_realm_find_eap(wpa_s, &realm[i])) {
-			found++;
-			break;
+
+		for (i = 0; i < count; i++) {
+			if (!nai_realm_match(&realm[i], cred->realm))
+				continue;
+			if (nai_realm_find_eap(cred, &realm[i])) {
+				if (selected == NULL ||
+				    selected->priority < cred->priority)
+					selected = cred;
+				break;
+			}
 		}
 	}
 
 	nai_realm_free(realm, count);
 
-	return found;
+	return selected;
 }
 
 
-static int interworking_credentials_available(struct wpa_supplicant *wpa_s,
-					      struct wpa_bss *bss)
+static struct wpa_cred * interworking_credentials_available(
+	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
-	return interworking_credentials_available_realm(wpa_s, bss) ||
-		interworking_credentials_available_3gpp(wpa_s, bss);
+	struct wpa_cred *cred, *cred2;
+
+	cred = interworking_credentials_available_realm(wpa_s, bss);
+	cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
+	if (cred && cred2 && cred2->priority >= cred->priority)
+		cred = cred2;
+	if (!cred)
+		cred = cred2;
+
+	cred2 = interworking_credentials_available_roaming_consortium(wpa_s,
+								      bss);
+	if (cred && cred2 && cred2->priority >= cred->priority)
+		cred = cred2;
+	if (!cred)
+		cred = cred2;
+
+	return cred;
+}
+
+
+static int domain_name_list_contains(struct wpabuf *domain_names,
+				     const char *domain)
+{
+	const u8 *pos, *end;
+	size_t len;
+
+	len = os_strlen(domain);
+	pos = wpabuf_head(domain_names);
+	end = pos + wpabuf_len(domain_names);
+
+	while (pos + 1 < end) {
+		if (pos + 1 + pos[0] > end)
+			break;
+
+		wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
+				  pos + 1, pos[0]);
+		if (pos[0] == len &&
+		    os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
+			return 1;
+
+		pos += 1 + pos[0];
+	}
+
+	return 0;
+}
+
+
+static int interworking_home_sp(struct wpa_supplicant *wpa_s,
+				struct wpabuf *domain_names)
+{
+	struct wpa_cred *cred;
+#ifdef INTERWORKING_3GPP
+	char nai[100], *realm;
+#endif /* INTERWORKING_3GPP */
+
+	if (domain_names == NULL || wpa_s->conf->cred == NULL)
+		return -1;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+#ifdef INTERWORKING_3GPP
+		char *imsi = NULL;
+		int mnc_len = 0;
+		if (cred->imsi)
+			imsi = cred->imsi;
+#ifdef CONFIG_PCSC
+		else if (cred->pcsc && wpa_s->conf->pcsc_reader &&
+			 wpa_s->scard && wpa_s->imsi[0]) {
+			imsi = wpa_s->imsi;
+			mnc_len = wpa_s->mnc_len;
+		}
+#endif /* CONFIG_PCSC */
+		if (imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0)
+		    == 0) {
+			realm = os_strchr(nai, '@');
+			if (realm)
+				realm++;
+			wpa_printf(MSG_DEBUG, "Interworking: Search for match "
+				   "with SIM/USIM domain %s", realm);
+			if (realm &&
+			    domain_name_list_contains(domain_names, realm))
+				return 1;
+		}
+#endif /* INTERWORKING_3GPP */
+
+		if (cred->domain == NULL)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
+			   "home SP FQDN %s", cred->domain);
+		if (domain_name_list_contains(domain_names, cred->domain))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+	struct wpa_ssid *ssid;
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+			if (wpas_network_disabled(wpa_s, ssid) ||
+			    ssid->mode != WPAS_MODE_INFRA)
+				continue;
+			if (ssid->ssid_len != bss->ssid_len ||
+			    os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
+			    0)
+				continue;
+			/*
+			 * TODO: Consider more accurate matching of security
+			 * configuration similarly to what is done in events.c
+			 */
+			return 1;
+		}
+	}
+
+	return 0;
 }
 
 
 static void interworking_select_network(struct wpa_supplicant *wpa_s)
 {
-	struct wpa_bss *bss, *selected = NULL;
+	struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
+	int selected_prio = -999999, selected_home_prio = -999999;
 	unsigned int count = 0;
+	const char *type;
+	int res;
+	struct wpa_cred *cred;
 
 	wpa_s->network_select = 0;
 
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-		if (!interworking_credentials_available(wpa_s, bss))
+		cred = interworking_credentials_available(wpa_s, bss);
+		if (!cred)
 			continue;
+		if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
+			/*
+			 * We currently support only HS 2.0 networks and those
+			 * are required to use WPA2-Enterprise.
+			 */
+			wpa_printf(MSG_DEBUG, "Interworking: Credential match "
+				   "with " MACSTR " but network does not use "
+				   "RSN", MAC2STR(bss->bssid));
+			continue;
+		}
 		count++;
-		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR,
-			MAC2STR(bss->bssid));
-		if (selected == NULL && wpa_s->auto_select)
-			selected = bss;
+		res = interworking_home_sp(wpa_s, bss->anqp ?
+					   bss->anqp->domain_name : NULL);
+		if (res > 0)
+			type = "home";
+		else if (res == 0)
+			type = "roaming";
+		else
+			type = "unknown";
+		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
+			MAC2STR(bss->bssid), type);
+		if (wpa_s->auto_select ||
+		    (wpa_s->conf->auto_interworking &&
+		     wpa_s->auto_network_select)) {
+			if (selected == NULL ||
+			    cred->priority > selected_prio) {
+				selected = bss;
+				selected_prio = cred->priority;
+			}
+			if (res > 0 &&
+			    (selected_home == NULL ||
+			     cred->priority > selected_home_prio)) {
+				selected_home = bss;
+				selected_home_prio = cred->priority;
+			}
+		}
+	}
+
+	if (selected_home && selected_home != selected &&
+	    selected_home_prio >= selected_prio) {
+		/* Prefer network operated by the Home SP */
+		selected = selected_home;
 	}
 
 	if (count == 0) {
+		/*
+		 * No matching network was found based on configured
+		 * credentials. Check whether any of the enabled network blocks
+		 * have matching APs.
+		 */
+		if (interworking_find_network_match(wpa_s)) {
+			wpa_printf(MSG_DEBUG, "Interworking: Possible BSS "
+				   "match for enabled network configurations");
+			if (wpa_s->auto_select)
+				interworking_reconnect(wpa_s);
+			return;
+		}
+
+		if (wpa_s->auto_network_select) {
+			wpa_printf(MSG_DEBUG, "Interworking: Continue "
+				   "scanning after ANQP fetch");
+			wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval,
+						0);
+			return;
+		}
+
 		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
 			"with matching credentials found");
 	}
@@ -853,6 +1590,38 @@
 }
 
 
+static struct wpa_bss_anqp *
+interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	struct wpa_bss *other;
+
+	if (is_zero_ether_addr(bss->hessid))
+		return NULL; /* Cannot be in the same homegenous ESS */
+
+	dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) {
+		if (other == bss)
+			continue;
+		if (other->anqp == NULL)
+			continue;
+		if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
+			continue;
+		if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
+			continue;
+		if (bss->ssid_len != other->ssid_len ||
+		    os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with "
+			   "already fetched BSSID " MACSTR " and " MACSTR,
+			   MAC2STR(other->bssid), MAC2STR(bss->bssid));
+		other->anqp->users++;
+		return other->anqp;
+	}
+
+	return NULL;
+}
+
+
 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_bss *bss;
@@ -870,6 +1639,17 @@
 			continue; /* AP does not support Interworking */
 
 		if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
+			if (bss->anqp == NULL) {
+				bss->anqp = interworking_match_anqp_info(wpa_s,
+									 bss);
+				if (bss->anqp) {
+					/* Shared data already fetched */
+					continue;
+				}
+				bss->anqp = wpa_bss_anqp_alloc();
+				if (bss->anqp == NULL)
+					break;
+			}
 			found++;
 			bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
 			wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
@@ -888,7 +1668,7 @@
 }
 
 
-static void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
+void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_bss *bss;
 
@@ -906,6 +1686,7 @@
 		return 0;
 
 	wpa_s->network_select = 0;
+	wpa_s->fetch_all_anqp = 1;
 
 	interworking_start_fetch_anqp(wpa_s);
 
@@ -964,6 +1745,13 @@
 {
 	const u8 *pos = data;
 	struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
+	struct wpa_bss_anqp *anqp = NULL;
+#ifdef CONFIG_HS20
+	u8 type;
+#endif /* CONFIG_HS20 */
+
+	if (bss)
+		anqp = bss->anqp;
 
 	switch (info_id) {
 	case ANQP_CAPABILITY_LIST:
@@ -974,9 +1762,9 @@
 		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 			" Venue Name", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_venue_name);
-			bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->venue_name);
+			anqp->venue_name = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_NETWORK_AUTH_TYPE:
@@ -985,10 +1773,9 @@
 			MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
 				  "Type", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_network_auth_type);
-			bss->anqp_network_auth_type =
-				wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->network_auth_type);
+			anqp->network_auth_type = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_ROAMING_CONSORTIUM:
@@ -996,10 +1783,9 @@
 			" Roaming Consortium list", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
 				  pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_roaming_consortium);
-			bss->anqp_roaming_consortium =
-				wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->roaming_consortium);
+			anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_IP_ADDR_TYPE_AVAILABILITY:
@@ -1008,9 +1794,9 @@
 			MAC2STR(sa));
 		wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
 			    pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_ip_addr_type_availability);
-			bss->anqp_ip_addr_type_availability =
+		if (anqp) {
+			wpabuf_free(anqp->ip_addr_type_availability);
+			anqp->ip_addr_type_availability =
 				wpabuf_alloc_copy(pos, slen);
 		}
 		break;
@@ -1018,9 +1804,9 @@
 		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 			" NAI Realm list", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_nai_realm);
-			bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->nai_realm);
+			anqp->nai_realm = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_3GPP_CELLULAR_NETWORK:
@@ -1028,18 +1814,18 @@
 			" 3GPP Cellular Network information", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
 				  pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_3gpp);
-			bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->anqp_3gpp);
+			anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_DOMAIN_NAME:
 		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 			" Domain Name list", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_domain_name);
-			bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->domain_name);
+			anqp->domain_name = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_VENDOR_SPECIFIC:
@@ -1047,6 +1833,28 @@
 			return;
 
 		switch (WPA_GET_BE24(pos)) {
+#ifdef CONFIG_HS20
+		case OUI_WFA:
+			pos += 3;
+			slen -= 3;
+
+			if (slen < 1)
+				return;
+			type = *pos++;
+			slen--;
+
+			switch (type) {
+			case HS20_ANQP_OUI_TYPE:
+				hs20_parse_rx_hs20_anqp_resp(wpa_s, sa, pos,
+							     slen);
+				break;
+			default:
+				wpa_printf(MSG_DEBUG, "HS20: Unsupported ANQP "
+					   "vendor type %u", type);
+				break;
+			}
+			break;
+#endif /* CONFIG_HS20 */
 		default:
 			wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
 				   "vendor-specific ANQP OUI %06x",
@@ -1121,7 +1929,9 @@
 {
 	interworking_stop_fetch_anqp(wpa_s);
 	wpa_s->network_select = 1;
+	wpa_s->auto_network_select = 0;
 	wpa_s->auto_select = !!auto_select;
+	wpa_s->fetch_all_anqp = 0;
 	wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
 		   "selection");
 	wpa_s->scan_res_handler = interworking_scan_res_handler;
@@ -1130,3 +1940,84 @@
 
 	return 0;
 }
+
+
+static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+			enum gas_query_result result,
+			const struct wpabuf *adv_proto,
+			const struct wpabuf *resp, u16 status_code)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR
+		" dialog_token=%d status_code=%d resp_len=%d",
+		MAC2STR(addr), dialog_token, status_code,
+		resp ? (int) wpabuf_len(resp) : -1);
+	if (!resp)
+		return;
+
+	wpabuf_free(wpa_s->last_gas_resp);
+	wpa_s->last_gas_resp = wpabuf_dup(resp);
+	if (wpa_s->last_gas_resp == NULL)
+		return;
+	os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN);
+	wpa_s->last_gas_dialog_token = dialog_token;
+}
+
+
+int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+		     const struct wpabuf *adv_proto,
+		     const struct wpabuf *query)
+{
+	struct wpabuf *buf;
+	int ret = 0;
+	int freq;
+	struct wpa_bss *bss;
+	int res;
+	size_t len;
+	u8 query_resp_len_limit = 0, pame_bi = 0;
+
+	freq = wpa_s->assoc_freq;
+	bss = wpa_bss_get_bssid(wpa_s, dst);
+	if (bss)
+		freq = bss->freq;
+	if (freq <= 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
+		   MAC2STR(dst), freq);
+	wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
+	wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
+
+	len = 3 + wpabuf_len(adv_proto) + 2;
+	if (query)
+		len += wpabuf_len(query);
+	buf = gas_build_initial_req(0, len);
+	if (buf == NULL)
+		return -1;
+
+	/* Advertisement Protocol IE */
+	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */
+	wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
+		      (pame_bi ? 0x80 : 0));
+	wpabuf_put_buf(buf, adv_proto);
+
+	/* GAS Query */
+	if (query) {
+		wpabuf_put_le16(buf, wpabuf_len(query));
+		wpabuf_put_buf(buf, query);
+	} else
+		wpabuf_put_le16(buf, 0);
+
+	res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request");
+		ret = -1;
+	} else
+		wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token "
+			   "%u", res);
+
+	wpabuf_free(buf);
+	return ret;
+}
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index 7dda8d8..cb8438e 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -1,6 +1,6 @@
 /*
  * Interworking (IEEE 802.11u)
- * Copyright (c) 2011, Qualcomm Atheros
+ * Copyright (c) 2011-2012, Qualcomm Atheros
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -17,9 +17,13 @@
 		  enum gas_query_result result,
 		  const struct wpabuf *adv_proto,
 		  const struct wpabuf *resp, u16 status_code);
+int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+		     const struct wpabuf *adv_proto,
+		     const struct wpabuf *query);
 int interworking_fetch_anqp(struct wpa_supplicant *wpa_s);
 void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s);
 int interworking_select(struct wpa_supplicant *wpa_s, int auto_select);
 int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s);
 
 #endif /* INTERWORKING_H */
diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
index c124ca2..19f7ce6 100644
--- a/wpa_supplicant/main.c
+++ b/wpa_supplicant/main.c
@@ -61,6 +61,10 @@
 #ifdef CONFIG_DEBUG_SYSLOG
 	printf("  -s = log output to syslog instead of stdout\n");
 #endif /* CONFIG_DEBUG_SYSLOG */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	printf("  -T = record to Linux tracing in addition to logging\n");
+	printf("       (records all messages regardless of debug verbosity)\n");
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
 	printf("  -t = include timestamp in debug messages\n"
 	       "  -h = show this help text\n"
 	       "  -L = show license (BSD)\n"
@@ -97,20 +101,31 @@
 }
 
 
-static void wpa_supplicant_fd_workaround(void)
+static void wpa_supplicant_fd_workaround(int start)
 {
 #ifdef __linux__
-	int s, i;
+	static int fd[3] = { -1, -1, -1 };
+	int i;
 	/* When started from pcmcia-cs scripts, wpa_supplicant might start with
 	 * fd 0, 1, and 2 closed. This will cause some issues because many
 	 * places in wpa_supplicant are still printing out to stdout. As a
 	 * workaround, make sure that fd's 0, 1, and 2 are not used for other
 	 * sockets. */
-	for (i = 0; i < 3; i++) {
-		s = open("/dev/null", O_RDWR);
-		if (s > 2) {
-			close(s);
-			break;
+	if (start) {
+		for (i = 0; i < 3; i++) {
+			fd[i] = open("/dev/null", O_RDWR);
+			if (fd[i] > 2) {
+				close(fd[i]);
+				fd[i] = -1;
+				break;
+			}
+		}
+	} else {
+		for (i = 0; i < 3; i++) {
+			if (fd[i] >= 0) {
+				close(fd[i]);
+				fd[i] = -1;
+			}
 		}
 	}
 #endif /* __linux__ */
@@ -136,10 +151,11 @@
 		return -1;
 	iface_count = 1;
 
-	wpa_supplicant_fd_workaround();
+	wpa_supplicant_fd_workaround(1);
 
 	for (;;) {
-		c = getopt(argc, argv, "b:Bc:C:D:de:f:g:hi:KLNo:O:p:P:qstuvW");
+		c = getopt(argc, argv,
+			   "b:Bc:C:D:de:f:g:hi:KLNo:O:p:P:qsTtuvW");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -214,6 +230,11 @@
 			params.wpa_debug_syslog++;
 			break;
 #endif /* CONFIG_DEBUG_SYSLOG */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+		case 'T':
+			params.wpa_debug_tracing++;
+			break;
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
 		case 't':
 			params.wpa_debug_timestamp++;
 			break;
@@ -231,8 +252,8 @@
 			break;
 		case 'N':
 			iface_count++;
-			iface = os_realloc(ifaces, iface_count *
-					   sizeof(struct wpa_interface));
+			iface = os_realloc_array(ifaces, iface_count,
+						 sizeof(struct wpa_interface));
 			if (iface == NULL)
 				goto out;
 			ifaces = iface;
@@ -252,6 +273,9 @@
 		wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant");
 		exitcode = -1;
 		goto out;
+	} else {
+		wpa_printf(MSG_INFO, "Successfully initialized "
+			   "wpa_supplicant");
 	}
 
 	for (i = 0; exitcode == 0 && i < iface_count; i++) {
@@ -275,6 +299,7 @@
 	wpa_supplicant_deinit(global);
 
 out:
+	wpa_supplicant_fd_workaround(0);
 	os_free(ifaces);
 	os_free(params.pid_file);
 
diff --git a/wpa_supplicant/main_symbian.cpp b/wpa_supplicant/main_symbian.cpp
deleted file mode 100644
index 254fead..0000000
--- a/wpa_supplicant/main_symbian.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * WPA Supplicant / Program entrypoint for Symbian
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-extern "C" {
-#include "common.h"
-#include "wpa_supplicant_i.h"
-}
-
-GLDEF_C TInt E32Main(void)
-{
-	struct wpa_interface iface;
-	int exitcode = 0;
-	struct wpa_params params;
-	struct wpa_global *global;
-
-	memset(&params, 0, sizeof(params));
-	params.wpa_debug_level = MSG_INFO;
-
-	global = wpa_supplicant_init(&params);
-	if (global == NULL)
-		return -1;
-
-	memset(&iface, 0, sizeof(iface));
-	/* TODO: set interface parameters */
-
-	if (wpa_supplicant_add_iface(global, &iface) == NULL)
-		exitcode = -1;
-
-	if (exitcode == 0)
-		exitcode = wpa_supplicant_run(global);
-
-	wpa_supplicant_deinit(global);
-
-	return exitcode;
-}
diff --git a/wpa_supplicant/nfc_pw_token.c b/wpa_supplicant/nfc_pw_token.c
new file mode 100644
index 0000000..11afb5b
--- /dev/null
+++ b/wpa_supplicant/nfc_pw_token.c
@@ -0,0 +1,83 @@
+/*
+ * nfc_pw_token - Tool for building NFC password tokens for WPS
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "crypto/random.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "wps_supplicant.h"
+
+
+static void print_bin(const char *title, const struct wpabuf *buf)
+{
+	size_t i, len;
+	const u8 *pos;
+
+	if (buf == NULL)
+		return;
+
+	printf("%s=", title);
+
+	pos = wpabuf_head(buf);
+	len = wpabuf_len(buf);
+	for (i = 0; i < len; i++)
+		printf("%02X", *pos++);
+
+	printf("\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct wpa_supplicant wpa_s;
+	int ret = -1;
+	struct wpabuf *buf = NULL, *ndef = NULL;
+	char txt[1000];
+
+	if (os_program_init())
+		return -1;
+	random_init(NULL);
+
+	os_memset(&wpa_s, 0, sizeof(wpa_s));
+	wpa_s.conf = os_zalloc(sizeof(*wpa_s.conf));
+	if (wpa_s.conf == NULL)
+		goto fail;
+
+	buf = wpas_wps_nfc_token(&wpa_s, 0);
+	if (buf == NULL)
+		goto fail;
+
+	ndef = ndef_build_wifi(buf);
+	if (ndef == NULL)
+		goto fail;
+
+	wpa_snprintf_hex_uppercase(txt, sizeof(txt), wpabuf_head(buf),
+				   wpabuf_len(buf));
+	printf("#WPS=%s\n", txt);
+
+	wpa_snprintf_hex_uppercase(txt, sizeof(txt), wpabuf_head(ndef),
+				   wpabuf_len(ndef));
+	printf("#NDEF=%s\n", txt);
+
+	printf("wps_nfc_dev_pw_id=%d\n", wpa_s.conf->wps_nfc_dev_pw_id);
+	print_bin("wps_nfc_dh_pubkey", wpa_s.conf->wps_nfc_dh_pubkey);
+	print_bin("wps_nfc_dh_privkey", wpa_s.conf->wps_nfc_dh_privkey);
+	print_bin("wps_nfc_dev_pw", wpa_s.conf->wps_nfc_dev_pw);
+
+	ret = 0;
+fail:
+	wpabuf_free(ndef);
+	wpabuf_free(buf);
+	wpa_config_free(wpa_s.conf);
+	random_deinit();
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index f613897..770907c 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -82,7 +82,7 @@
 #ifdef CONFIG_P2P
 	if (new_state == WPA_COMPLETED)
 		wpas_p2p_notif_connected(wpa_s);
-	else if (new_state < WPA_ASSOCIATED)
+	else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
 		wpas_p2p_notif_disconnected(wpa_s);
 #endif /* CONFIG_P2P */
 
@@ -93,7 +93,7 @@
 		     "id=%d state=%d BSSID=" MACSTR " SSID=%s",
 		     wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
 		     new_state,
-		     MAC2STR(wpa_s->pending_bssid),
+		     MAC2STR(wpa_s->bssid),
 		     wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
 		     wpa_ssid_txt(wpa_s->current_ssid->ssid,
 		     wpa_s->current_ssid->ssid_len): "");
@@ -101,6 +101,12 @@
 }
 
 
+void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s)
+{
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON);
+}
+
+
 void wpas_notify_network_changed(struct wpa_supplicant *wpa_s)
 {
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_NETWORK);
@@ -606,3 +612,20 @@
 	/* notify the new DBus API */
 	wpas_dbus_signal_certification(wpa_s, depth, subject, cert_hash, cert);
 }
+
+
+void wpas_notify_preq(struct wpa_supplicant *wpa_s,
+		      const u8 *addr, const u8 *dst, const u8 *bssid,
+		      const u8 *ie, size_t ie_len, u32 ssi_signal)
+{
+#ifdef CONFIG_AP
+	wpas_dbus_signal_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal);
+#endif /* CONFIG_AP */
+}
+
+
+void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
+			    const char *parameter)
+{
+	wpas_dbus_signal_eap_status(wpa_s, status, parameter);
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index bd99a76..58675ac 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -22,6 +22,7 @@
 void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
 			       enum wpa_states new_state,
 			       enum wpa_states old_state);
+void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
 void wpas_notify_network_changed(struct wpa_supplicant *wpa_s);
 void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s);
 void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s);
@@ -121,5 +122,10 @@
 void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
 			       const char *subject, const char *cert_hash,
 			       const struct wpabuf *cert);
+void wpas_notify_preq(struct wpa_supplicant *wpa_s,
+		      const u8 *addr, const u8 *dst, const u8 *bssid,
+		      const u8 *ie, size_t ie_len, u32 ssi_signal);
+void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
+			    const char *parameter);
 
 #endif /* NOTIFY_H */
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 5f1329e..c721a29 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -39,6 +39,8 @@
  */
 #define P2P_MAX_JOIN_SCAN_ATTEMPTS 10
 
+#define P2P_AUTO_PD_SCAN_ATTEMPTS 5
+
 #ifndef P2P_MAX_CLIENT_IDLE
 /*
  * How many seconds to try to reconnect to the GO when connection in P2P client
@@ -51,40 +53,51 @@
 #endif /* ANDROID_P2P */
 #endif /* P2P_MAX_CLIENT_IDLE */
 
+#ifndef P2P_MAX_INITIAL_CONN_WAIT
+/*
+ * How many seconds to wait for initial 4-way handshake to get completed after
+ * WPS provisioning step.
+ */
+#define P2P_MAX_INITIAL_CONN_WAIT 10
+#endif /* P2P_MAX_INITIAL_CONN_WAIT */
+
+#ifndef P2P_CONCURRENT_SEARCH_DELAY
+#define P2P_CONCURRENT_SEARCH_DELAY 500
+#endif /* P2P_CONCURRENT_SEARCH_DELAY */
+
+enum p2p_group_removal_reason {
+	P2P_GROUP_REMOVAL_UNKNOWN,
+	P2P_GROUP_REMOVAL_SILENT,
+	P2P_GROUP_REMOVAL_FORMATION_FAILED,
+	P2P_GROUP_REMOVAL_REQUESTED,
+	P2P_GROUP_REMOVAL_IDLE_TIMEOUT,
+	P2P_GROUP_REMOVAL_UNAVAILABLE,
+	P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
 #ifdef ANDROID_P2P
-static int wpas_global_scan_in_progress(struct wpa_supplicant *wpa_s);
+	P2P_GROUP_REMOVAL_FREQ_CONFLICT
 #endif
+};
+
+
 static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
 static struct wpa_supplicant *
 wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
 			 int go);
 static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq);
 static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
-			 const u8 *dev_addr, enum p2p_wps_method wps_method);
+			 const u8 *dev_addr, enum p2p_wps_method wps_method,
+			 int auto_join);
 static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx,
 					    void *timeout_ctx);
 static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
 static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+					int group_added);
 
-#ifdef ANDROID_P2P
-static int wpas_global_scan_in_progress(struct wpa_supplicant *wpa_s)
-{
-	struct wpa_supplicant *iface = NULL;
-
-	for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
-		if(iface->scanning  || iface->wpa_state == WPA_SCANNING) {
-			wpa_printf(MSG_DEBUG, "P2P: Scan in progress on %s,"
-			"defer P2P SEARCH", iface->ifname);
-			return 1;
-		}
-	}
-
-	return 0;
-}
-#endif
 
 static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
 				      struct wpa_scan_results *scan_res)
@@ -112,19 +125,31 @@
 
 static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
 			 unsigned int num_req_dev_types,
-			 const u8 *req_dev_types, const u8 *dev_id)
+			 const u8 *req_dev_types, const u8 *dev_id, u16 pw_id)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_supplicant *ifs;
 	struct wpa_driver_scan_params params;
 	int ret;
 	struct wpabuf *wps_ie, *ies;
 	int social_channels[] = { 2412, 2437, 2462, 0, 0 };
 	size_t ielen;
-	int was_in_p2p_scan;
 
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
+	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+		if (ifs->sta_scan_pending &&
+		    wpas_p2p_in_progress(wpa_s) == 2) {
+			wpa_printf(MSG_DEBUG, "Delaying P2P scan to allow "
+				   "pending station mode scan to be "
+				   "completed on interface %s", ifs->ifname);
+			wpa_s->global->p2p_cb_on_scan_complete = 1;
+			wpa_supplicant_req_scan(ifs, 0, 0);
+			return 1;
+		}
+	}
+
 	os_memset(&params, 0, sizeof(params));
 
 	/* P2P Wildcard SSID */
@@ -133,8 +158,8 @@
 	params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
 
 	wpa_s->wps->dev.p2p = 1;
-	wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
-					WPS_REQ_ENROLLEE,
+	wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
+					wpa_s->wps->uuid, WPS_REQ_ENROLLEE,
 					num_req_dev_types, req_dev_types);
 	if (wps_ie == NULL)
 		return -1;
@@ -171,23 +196,21 @@
 		break;
 	}
 
-	was_in_p2p_scan = wpa_s->scan_res_handler == wpas_p2p_scan_res_handler;
-	wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
 	ret = wpa_drv_scan(wpa_s, &params);
 
 	wpabuf_free(ies);
 
 	if (ret) {
-		wpa_s->scan_res_handler = NULL;
-#ifdef ANDROID_P2P
-		if (wpa_s->scanning || was_in_p2p_scan || wpas_global_scan_in_progress(wpa_s)) {
-#else
-		if (wpa_s->scanning || was_in_p2p_scan) {
-#endif
-			wpa_s->p2p_cb_on_scan_complete = 1;
-			ret = 1;
+		for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+			if (ifs->scanning ||
+			    ifs->scan_res_handler == wpas_p2p_scan_res_handler) {
+				wpa_s->global->p2p_cb_on_scan_complete = 1;
+				ret = 1;
+				break;
+			}
 		}
-	}
+	} else
+		wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
 
 	return ret;
 }
@@ -233,28 +256,33 @@
 }
 
 
-static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
+				 enum p2p_group_removal_reason removal_reason)
 {
 	struct wpa_ssid *ssid;
 	char *gtype;
 	const char *reason;
 
-	eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
-
 	ssid = wpa_s->current_ssid;
 	if (ssid == NULL) {
 		/*
 		 * The current SSID was not known, but there may still be a
-		 * pending P2P group interface waiting for provisioning.
+		 * pending P2P group interface waiting for provisioning or a
+		 * P2P group that is trying to reconnect.
 		 */
 		ssid = wpa_s->conf->ssid;
 		while (ssid) {
-			if (ssid->p2p_group &&
-			    (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
-			     (ssid->key_mgmt & WPA_KEY_MGMT_WPS)))
+			if (ssid->p2p_group)
 				break;
 			ssid = ssid->next;
 		}
+		if (ssid == NULL &&
+			wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)
+		{
+			wpa_printf(MSG_ERROR, "P2P: P2P group interface "
+				   "not found");
+			return -1;
+		}
 	}
 	if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO)
 		gtype = "GO";
@@ -273,16 +301,22 @@
 			P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
 			wpa_s->ifname, wpa_s->cross_connect_uplink);
 	}
-	switch (wpa_s->removal_reason) {
+	switch (removal_reason) {
 	case P2P_GROUP_REMOVAL_REQUESTED:
 		reason = " reason=REQUESTED";
 		break;
+	case P2P_GROUP_REMOVAL_FORMATION_FAILED:
+		reason = " reason=FORMATION_FAILED";
+		break;
 	case P2P_GROUP_REMOVAL_IDLE_TIMEOUT:
 		reason = " reason=IDLE";
 		break;
 	case P2P_GROUP_REMOVAL_UNAVAILABLE:
 		reason = " reason=UNAVAILABLE";
 		break;
+	case P2P_GROUP_REMOVAL_GO_ENDING_SESSION:
+		reason = " reason=GO_ENDING_SESSION";
+		break;
 #ifdef ANDROID_P2P
 	case P2P_GROUP_REMOVAL_FREQ_CONFLICT:
 		reason = " reason=FREQ_CONFLICT";
@@ -292,10 +326,16 @@
 		reason = "";
 		break;
 	}
-	wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s",
-		wpa_s->ifname, gtype, reason);
+	if (removal_reason != P2P_GROUP_REMOVAL_SILENT) {
+		wpa_msg(wpa_s->parent, MSG_INFO,
+			P2P_EVENT_GROUP_REMOVED "%s %s%s",
+			wpa_s->ifname, gtype, reason);
+	}
 
-	if (ssid)
+	if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
+		wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
+
+	if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
 		wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
 
 	if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
@@ -312,7 +352,7 @@
 		if (wpa_s && ifname)
 			wpa_drv_if_remove(wpa_s, type, ifname);
 		os_free(ifname);
-		return;
+		return 1;
 	}
 
 	wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
@@ -337,11 +377,17 @@
 		wpa_config_remove_network(wpa_s->conf, id);
 		wpa_supplicant_clear_status(wpa_s);
 		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_s->sta_scan_pending = 0;
 	} else {
 		wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
 			   "found");
 	}
-	wpa_supplicant_ap_deinit(wpa_s);
+	if (wpa_s->ap_iface)
+		wpa_supplicant_ap_deinit(wpa_s);
+	else
+		wpa_drv_deinit_p2p_cli(wpa_s);
+
+	return 0;
 }
 
 
@@ -499,6 +545,7 @@
 	struct wpa_ssid *ssid, *s;
 	u8 *n;
 	size_t i;
+	int found = 0;
 
 	ssid = wpa_s->current_ssid;
 	if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
@@ -519,17 +566,40 @@
 
 	for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
 		if (os_memcmp(s->p2p_client_list + i * ETH_ALEN, addr,
-			      ETH_ALEN) == 0)
-			return; /* already in list */
+			      ETH_ALEN) != 0)
+			continue;
+
+		if (i == s->num_p2p_clients - 1)
+			return; /* already the most recent entry */
+
+		/* move the entry to mark it most recent */
+		os_memmove(s->p2p_client_list + i * ETH_ALEN,
+			   s->p2p_client_list + (i + 1) * ETH_ALEN,
+			   (s->num_p2p_clients - i - 1) * ETH_ALEN);
+		os_memcpy(s->p2p_client_list +
+			  (s->num_p2p_clients - 1) * ETH_ALEN, addr, ETH_ALEN);
+		found = 1;
+		break;
 	}
 
-	n = os_realloc(s->p2p_client_list,
-		       (s->num_p2p_clients + 1) * ETH_ALEN);
-	if (n == NULL)
-		return;
-	os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN);
-	s->p2p_client_list = n;
-	s->num_p2p_clients++;
+	if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) {
+		n = os_realloc_array(s->p2p_client_list,
+				     s->num_p2p_clients + 1, ETH_ALEN);
+		if (n == NULL)
+			return;
+		os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN);
+		s->p2p_client_list = n;
+		s->num_p2p_clients++;
+	} else if (!found) {
+		/* Not enough room for an additional entry - drop the oldest
+		 * entry */
+		os_memmove(s->p2p_client_list,
+			   s->p2p_client_list + ETH_ALEN,
+			   (s->num_p2p_clients - 1) * ETH_ALEN);
+		os_memcpy(s->p2p_client_list +
+			  (s->num_p2p_clients - 1) * ETH_ALEN,
+			  addr, ETH_ALEN);
+	}
 
 #ifndef CONFIG_NO_CONFIG_WRITE
 	if (wpa_s->parent->conf->update_config &&
@@ -562,7 +632,8 @@
 	if (!success) {
 		wpa_msg(wpa_s->parent, MSG_INFO,
 			P2P_EVENT_GROUP_FORMATION_FAILURE);
-		wpas_p2p_group_delete(wpa_s);
+		wpas_p2p_group_delete(wpa_s,
+				      P2P_GROUP_REMOVAL_FORMATION_FAILED);
 		return;
 	}
 
@@ -603,6 +674,13 @@
 		 * packets.
 		 */
 		wpa_s->show_group_started = 1;
+#ifdef ANDROID_P2P
+		/* For client Second phase of Group formation (4-way handshake) can be still pending
+		 * So we need to restore wpa_s->global->p2p_group_formation */
+		wpa_printf(MSG_INFO, "Restoring back wpa_s->global->p2p_group_formation to wpa_s %p\n", wpa_s);
+		wpa_s->global->p2p_group_formation = wpa_s;
+#endif
+
 	} else if (ssid && ssid->passphrase == NULL && ssid->psk_set) {
 		char psk[65];
 		wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
@@ -670,6 +748,13 @@
 	    (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
 	     os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
 		wpa_s->pending_pd_before_join = 0;
+		if (wpa_s->p2p_fallback_to_go_neg) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req "
+				"during p2p_connect-auto");
+			wpas_p2p_fallback_to_go_neg(wpa_s, 0);
+			return;
+		}
+
 		wpa_printf(MSG_DEBUG, "P2P: Starting pending "
 			   "join-existing-group operation (no ACK for PD "
 			   "Req)");
@@ -788,12 +873,18 @@
 {
 	struct wpa_ssid *ssid;
 
-	if (wpas_copy_go_neg_results(wpa_s, params) < 0)
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Starting GO");
+	if (wpas_copy_go_neg_results(wpa_s, params) < 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not copy GO Negotiation "
+			"results");
 		return;
+	}
 
 	ssid = wpa_config_add_network(wpa_s->conf);
-	if (ssid == NULL)
+	if (ssid == NULL) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not add network for GO");
 		return;
+	}
 
 	wpa_s->show_group_started = 0;
 
@@ -804,6 +895,7 @@
 	ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION :
 		WPAS_MODE_P2P_GO;
 	ssid->frequency = params->freq;
+	ssid->ht40 = params->ht40;
 	ssid->ssid = os_zalloc(params->ssid_len + 1);
 	if (ssid->ssid) {
 		os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
@@ -814,6 +906,14 @@
 	ssid->proto = WPA_PROTO_RSN;
 	ssid->pairwise_cipher = WPA_CIPHER_CCMP;
 	ssid->passphrase = os_strdup(params->passphrase);
+	if (ssid->passphrase == NULL) {
+		wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to copy passphrase for "
+			"GO");
+		wpa_config_remove_network(wpa_s->conf, ssid->id);
+		return;
+	}
+	wpa_config_update_psk(ssid);
+	ssid->ap_max_inactivity = wpa_s->parent->conf->p2p_go_max_inactivity;
 
 	wpa_s->ap_configured_cb = p2p_go_configured;
 	wpa_s->ap_configured_cb_ctx = wpa_s;
@@ -821,6 +921,8 @@
 	wpa_s->connect_without_scan = ssid;
 	wpa_s->reassociate = 1;
 	wpa_s->disconnected = 0;
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Request scan (that will be skipped) to "
+		"start GO)");
 	wpa_supplicant_req_scan(wpa_s, 0, 0);
 }
 
@@ -851,6 +953,8 @@
 	d->p2p_group_idle = s->p2p_group_idle;
 	d->p2p_intra_bss = s->p2p_intra_bss;
 	d->persistent_reconnect = s->persistent_reconnect;
+	d->max_num_sta = s->max_num_sta;
+	d->pbc_in_m1 = s->pbc_in_m1;
 }
 
 
@@ -999,9 +1103,26 @@
 		return;
 	}
 
+	if (wpa_s->p2p_go_ht40)
+		res->ht40 = 1;
+
 	wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS);
 	wpas_notify_p2p_go_neg_completed(wpa_s, res);
 
+	if (res->role_go && wpa_s->p2p_persistent_id >= 0) {
+		struct wpa_ssid *ssid;
+		ssid = wpa_config_get_network(wpa_s->conf,
+					      wpa_s->p2p_persistent_id);
+		if (ssid && ssid->disabled == 2 &&
+		    ssid->mode == WPAS_MODE_P2P_GO && ssid->passphrase) {
+			size_t len = os_strlen(ssid->passphrase);
+			wpa_printf(MSG_DEBUG, "P2P: Override passphrase based "
+				   "on requested persistent group");
+			os_memcpy(res->passphrase, ssid->passphrase, len);
+			res->passphrase[len] = '\0';
+		}
+	}
+
 	if (wpa_s->create_p2p_iface) {
 		struct wpa_supplicant *group_wpa_s =
 			wpas_p2p_init_group_interface(wpa_s, res->role_go);
@@ -1059,16 +1180,26 @@
 #ifndef CONFIG_NO_STDOUT_DEBUG
 	struct wpa_supplicant *wpa_s = ctx;
 	char devtype[WPS_DEV_TYPE_BUFSIZE];
-
+#define WFD_DEV_INFO_SIZE 9
+	char wfd_dev_info_hex[2 * WFD_DEV_INFO_SIZE + 1];
+	os_memset(wfd_dev_info_hex, 0, sizeof(wfd_dev_info_hex));
+#ifdef CONFIG_WIFI_DISPLAY
+	if (info->wfd_subelems) {
+		wpa_snprintf_hex(wfd_dev_info_hex, sizeof(wfd_dev_info_hex),
+					wpabuf_head(info->wfd_subelems),
+					WFD_DEV_INFO_SIZE);
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
 	wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
 		" p2p_dev_addr=" MACSTR
 		" pri_dev_type=%s name='%s' config_methods=0x%x "
-		"dev_capab=0x%x group_capab=0x%x",
+		"dev_capab=0x%x group_capab=0x%x%s%s",
 		MAC2STR(addr), MAC2STR(info->p2p_device_addr),
 		wps_dev_type_bin2str(info->pri_dev_type, devtype,
 				     sizeof(devtype)),
 		info->device_name, info->config_methods,
-		info->dev_capab, info->group_capab);
+		info->dev_capab, info->group_capab,
+		wfd_dev_info_hex[0] ? " wfd_dev_info=0x" : "", wfd_dev_info_hex);
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
 	wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
@@ -1402,6 +1533,62 @@
 }
 
 
+#ifdef CONFIG_WIFI_DISPLAY
+static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
+			    struct wpabuf *resp, u8 srv_trans_id,
+			    const u8 *query, size_t query_len)
+{
+	const u8 *pos;
+	u8 role;
+	u8 *len_pos;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
+
+	if (!wpa_s->global->wifi_display) {
+		wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
+					    srv_trans_id);
+		return;
+	}
+
+	if (query_len < 1) {
+		wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
+			   "Role");
+		return;
+	}
+
+	if (wpabuf_tailroom(resp) < 5)
+		return;
+
+	pos = query;
+	role = *pos++;
+	wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
+
+	/* TODO: role specific handling */
+
+	/* Length (to be filled) */
+	len_pos = wpabuf_put(resp, 2);
+	wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
+	wpabuf_put_u8(resp, srv_trans_id);
+	wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
+
+	while (pos < query + query_len) {
+		if (*pos < MAX_WFD_SUBELEMS &&
+		    wpa_s->global->wfd_subelem[*pos] &&
+		    wpabuf_tailroom(resp) >=
+		    wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
+			wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
+				   "subelement %u", *pos);
+			wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
+		}
+		pos++;
+	}
+
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
 void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
 		     u16 update_indic, const u8 *tlvs, size_t tlvs_len)
 {
@@ -1493,6 +1680,12 @@
 			wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
 					 pos, tlv_end - pos);
 			break;
+#ifdef CONFIG_WIFI_DISPLAY
+		case P2P_SERV_WIFI_DISPLAY:
+			wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
+					pos, tlv_end - pos);
+			break;
+#endif /* CONFIG_WIFI_DISPLAY */
 		default:
 			wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
 				   "protocol %u", srv_proto);
@@ -1610,6 +1803,88 @@
 }
 
 
+#ifdef CONFIG_WIFI_DISPLAY
+
+static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
+				   const struct wpabuf *tlvs)
+{
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+		return 0;
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return 0;
+	return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+#define MAX_WFD_SD_SUBELEMS 20
+
+static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
+				const char *subelems)
+{
+	u8 *len;
+	const char *pos;
+	int val;
+	int count = 0;
+
+	len = wpabuf_put(tlvs, 2);
+	wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
+	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
+
+	wpabuf_put_u8(tlvs, role);
+
+	pos = subelems;
+	while (*pos) {
+		val = atoi(pos);
+		if (val >= 0 && val < 256) {
+			wpabuf_put_u8(tlvs, val);
+			count++;
+			if (count == MAX_WFD_SD_SUBELEMS)
+				break;
+		}
+		pos = os_strchr(pos + 1, ',');
+		if (pos == NULL)
+			break;
+		pos++;
+	}
+
+	WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
+}
+
+
+u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
+				     const u8 *dst, const char *role)
+{
+	struct wpabuf *tlvs;
+	u64 ret;
+	const char *subelems;
+	u8 id = 1;
+
+	subelems = os_strchr(role, ' ');
+	if (subelems == NULL)
+		return 0;
+	subelems++;
+
+	tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
+	if (tlvs == NULL)
+		return 0;
+
+	if (os_strstr(role, "[source]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
+	if (os_strstr(role, "[pri-sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
+	if (os_strstr(role, "[sec-sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
+	if (os_strstr(role, "[source+sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
+
+	ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
+	wpabuf_free(tlvs);
+	return ret;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
 int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
 {
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
@@ -1636,15 +1911,22 @@
 			resp_tlvs);
 }
 
-
+#ifdef ANDROID_P2P
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s, int action)
+#else
 void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
+#endif
 {
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
 		wpa_drv_p2p_service_update(wpa_s);
 		return;
 	}
 	if (wpa_s->global->p2p)
+#ifdef ANDROID_P2P
+		p2p_sd_service_update(wpa_s->global->p2p, action);
+#else
 		p2p_sd_service_update(wpa_s->global->p2p);
+#endif
 }
 
 
@@ -1678,7 +1960,11 @@
 			      struct p2p_srv_upnp, list)
 		wpas_p2p_srv_upnp_free(usrv);
 
+#ifdef ANDROID_P2P
+	wpas_p2p_sd_service_update(wpa_s, SRV_FLUSH);
+#else
 	wpas_p2p_sd_service_update(wpa_s);
+#endif
 }
 
 
@@ -1702,7 +1988,11 @@
 	bsrv->resp = resp;
 	dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
 
+#ifdef ANDROID_P2P
+	wpas_p2p_sd_service_update(wpa_s, SRV_ADD);
+#else
 	wpas_p2p_sd_service_update(wpa_s);
+#endif
 	return 0;
 }
 
@@ -1716,7 +2006,11 @@
 	if (bsrv == NULL)
 		return -1;
 	wpas_p2p_srv_bonjour_free(bsrv);
+#ifdef ANDROID_P2P
+	wpas_p2p_sd_service_update(wpa_s, SRV_DEL);
+#else
 	wpas_p2p_sd_service_update(wpa_s);
+#endif
 	return 0;
 }
 
@@ -1739,7 +2033,11 @@
 	}
 	dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
 
+#ifdef ANDROID_P2P
+	wpas_p2p_sd_service_update(wpa_s, SRV_ADD);
+#else
 	wpas_p2p_sd_service_update(wpa_s);
+#endif
 	return 0;
 }
 
@@ -1753,7 +2051,11 @@
 	if (usrv == NULL)
 		return -1;
 	wpas_p2p_srv_upnp_free(usrv);
+#ifdef ANDROID_P2P
+	wpas_p2p_sd_service_update(wpa_s, SRV_DEL);
+#else
 	wpas_p2p_sd_service_update(wpa_s);
+#endif
 	return 0;
 }
 
@@ -1836,6 +2138,7 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	unsigned int generated_pin = 0;
+	char params[20];
 
 	if (wpa_s->pending_pd_before_join &&
 	    (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
@@ -1847,14 +2150,22 @@
 		return;
 	}
 
+	if (wpa_s->pending_pd_use == AUTO_PD_JOIN ||
+	    wpa_s->pending_pd_use == AUTO_PD_GO_NEG)
+		os_snprintf(params, sizeof(params), " peer_go=%d",
+			    wpa_s->pending_pd_use == AUTO_PD_JOIN);
+	else
+		params[0] = '\0';
+
 	if (config_methods & WPS_CONFIG_DISPLAY)
-		wpas_prov_disc_local_keypad(wpa_s, peer, "");
+		wpas_prov_disc_local_keypad(wpa_s, peer, params);
 	else if (config_methods & WPS_CONFIG_KEYPAD) {
 		generated_pin = wps_generate_pin();
-		wpas_prov_disc_local_display(wpa_s, peer, "", generated_pin);
+		wpas_prov_disc_local_display(wpa_s, peer, params,
+					     generated_pin);
 	} else if (config_methods & WPS_CONFIG_PUSHBUTTON)
-		wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR,
-			MAC2STR(peer));
+		wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR
+			"%s", MAC2STR(peer), params);
 
 	wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
 					    P2P_PROV_DISC_SUCCESS,
@@ -1867,6 +2178,29 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
+	if (wpa_s->p2p_fallback_to_go_neg) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto "
+			"failed - fall back to GO Negotiation");
+		wpas_p2p_fallback_to_go_neg(wpa_s, 0);
+		return;
+	}
+
+#ifdef ANDROID_P2P
+	/* If provision discovery failed it is safe to cancel the timer here and
+	 * also do not start the join */
+	if (wpa_s->pending_pd_before_join &&
+	    (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
+	     os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
+		wpa_s->pending_pd_before_join = 0;
+		wpa_printf(MSG_DEBUG, "P2P: Do not Start pending "
+			   "join-existing-group operation");
+		eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL);
+	}
+#endif /* ANDROID_P2P */
+	wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+		" p2p_dev_addr=" MACSTR " status=%d",
+		MAC2STR(peer), status);
+
 	wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
 					    status, 0, 0);
 }
@@ -1989,11 +2323,12 @@
 			   " was accepted; op_freq=%d MHz",
 			   MAC2STR(sa), op_freq);
 		if (s) {
+			int go = s->mode == WPAS_MODE_P2P_GO;
 			wpas_p2p_group_add_persistent(
-				wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0);
+				wpa_s, s, go, go ? op_freq : 0, 0);
 		} else if (bssid) {
 			wpas_p2p_join(wpa_s, bssid, go_dev_addr,
-				      wpa_s->p2p_wps_method);
+				      wpa_s->p2p_wps_method, 0);
 		}
 		return;
 	}
@@ -2057,7 +2392,34 @@
 	}
 
 	wpas_p2p_group_add_persistent(wpa_s, ssid,
-				      ssid->mode == WPAS_MODE_P2P_GO, 0);
+				      ssid->mode == WPAS_MODE_P2P_GO,
+				      wpa_s->p2p_persistent_go_freq,
+				      wpa_s->p2p_go_ht40);
+}
+
+
+static int wpas_p2p_disallowed_freq(struct wpa_global *global,
+				    unsigned int freq)
+{
+	unsigned int i;
+
+	if (global->p2p_disallow_freq == NULL)
+		return 0;
+
+	for (i = 0; i < global->num_p2p_disallow_freq; i++) {
+		if (freq >= global->p2p_disallow_freq[i].min &&
+		    freq <= global->p2p_disallow_freq[i].max)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan)
+{
+	reg->channel[reg->channels] = chan;
+	reg->channels++;
 }
 
 
@@ -2071,34 +2433,47 @@
 
 	/* Operating class 81 - 2.4 GHz band channels 1..13 */
 	chan->reg_class[cla].reg_class = 81;
-	chan->reg_class[cla].channels = 11;
-	for (i = 0; i < 11; i++)
-		chan->reg_class[cla].channel[i] = i + 1;
-	cla++;
+	chan->reg_class[cla].channels = 0;
+	for (i = 0; i < 11; i++) {
+		if (!wpas_p2p_disallowed_freq(wpa_s->global, 2412 + i * 5))
+			wpas_p2p_add_chan(&chan->reg_class[cla], i + 1);
+	}
+	if (chan->reg_class[cla].channels)
+		cla++;
 
 	wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz "
 		   "band");
 
 	/* Operating class 115 - 5 GHz, channels 36-48 */
 	chan->reg_class[cla].reg_class = 115;
-	chan->reg_class[cla].channels = 4;
-	chan->reg_class[cla].channel[0] = 36;
-	chan->reg_class[cla].channel[1] = 40;
-	chan->reg_class[cla].channel[2] = 44;
-	chan->reg_class[cla].channel[3] = 48;
-	cla++;
+	chan->reg_class[cla].channels = 0;
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 36 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 36);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 40 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 40);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 44 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 44);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 48 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 48);
+	if (chan->reg_class[cla].channels)
+		cla++;
 
 	wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz "
 		   "band");
 
 	/* Operating class 124 - 5 GHz, channels 149,153,157,161 */
 	chan->reg_class[cla].reg_class = 124;
-	chan->reg_class[cla].channels = 4;
-	chan->reg_class[cla].channel[0] = 149;
-	chan->reg_class[cla].channel[1] = 153;
-	chan->reg_class[cla].channel[2] = 157;
-	chan->reg_class[cla].channel[3] = 161;
-	cla++;
+	chan->reg_class[cla].channels = 0;
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 149 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 149);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 153 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 153);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 156 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 157);
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 161 * 5))
+		wpas_p2p_add_chan(&chan->reg_class[cla], 161);
+	if (chan->reg_class[cla].channels)
+		cla++;
 
 	chan->reg_classes = cla;
 	return 0;
@@ -2120,9 +2495,16 @@
 }
 
 
-static int has_channel(struct hostapd_hw_modes *mode, u8 chan, int *flags)
+static int has_channel(struct wpa_global *global,
+		       struct hostapd_hw_modes *mode, u8 chan, int *flags)
 {
 	int i;
+	unsigned int freq;
+
+	freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) +
+		chan * 5;
+	if (wpas_p2p_disallowed_freq(global, freq))
+		return 0;
 
 	for (i = 0; i < mode->num_channels; i++) {
 		if (mode->channels[i].chan == chan) {
@@ -2149,26 +2531,47 @@
 	enum { BW20, BW40PLUS, BW40MINUS } bw;
 };
 
+static struct p2p_oper_class_map op_class[] = {
+	{ HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 },
+#if 0 /* Do not enable HT40 on 2 GHz for now */
+	{ HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS },
+	{ HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS },
+#endif
+	{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 },
+	{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 },
+	{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS },
+	{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS },
+	{ HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS },
+	{ HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS },
+	{ -1, 0, 0, 0, 0, BW20 }
+};
+
+
+static int wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
+				   struct hostapd_hw_modes *mode,
+				   u8 channel, u8 bw)
+{
+	int flag;
+
+	if (!has_channel(wpa_s->global, mode, channel, &flag))
+		return -1;
+	if (bw == BW40MINUS &&
+	    (!(flag & HOSTAPD_CHAN_HT40MINUS) ||
+	     !has_channel(wpa_s->global, mode, channel - 4, NULL)))
+		return 0;
+	if (bw == BW40PLUS &&
+	    (!(flag & HOSTAPD_CHAN_HT40PLUS) ||
+	     !has_channel(wpa_s->global, mode, channel + 4, NULL)))
+		return 0;
+	return 1;
+}
+
+
 static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
 				   struct p2p_channels *chan)
 {
 	struct hostapd_hw_modes *mode;
 	int cla, op;
-	struct p2p_oper_class_map op_class[] = {
-		{ HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 },
-		{ HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20 },
-#if 0 /* Do not enable HT40 on 2 GHz for now */
-		{ HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS },
-		{ HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS },
-#endif
-		{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 },
-		{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 },
-		{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS },
-		{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS },
-		{ HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS },
-		{ HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS },
-		{ -1, 0, 0, 0, 0, BW20 }
-	};
 
 	if (wpa_s->hw.modes == NULL) {
 		wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching "
@@ -2188,16 +2591,7 @@
 		if (mode == NULL)
 			continue;
 		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
-			int flag;
-			if (!has_channel(mode, ch, &flag))
-				continue;
-			if (o->bw == BW40MINUS &&
-			    (!(flag & HOSTAPD_CHAN_HT40MINUS) ||
-			     !has_channel(mode, ch - 4, NULL)))
-				continue;
-			if (o->bw == BW40PLUS &&
-			    (!(flag & HOSTAPD_CHAN_HT40PLUS) ||
-			     !has_channel(mode, ch + 4, NULL)))
+			if (wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw) < 1)
 				continue;
 			if (reg == NULL) {
 				wpa_printf(MSG_DEBUG, "P2P: Add operating "
@@ -2221,6 +2615,32 @@
 }
 
 
+int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
+			   struct hostapd_hw_modes *mode, u8 channel)
+{
+	int op, ret;
+
+	for (op = 0; op_class[op].op_class; op++) {
+		struct p2p_oper_class_map *o = &op_class[op];
+		u8 ch;
+
+		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+			if (o->mode != HOSTAPD_MODE_IEEE80211A ||
+			    o->bw == BW20 || ch != channel)
+				continue;
+			ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
+			if (ret < 0)
+				continue;
+			else if (ret > 0)
+				return (o->bw == BW40MINUS) ? -1 : 1;
+			else
+				return 0;
+		}
+	}
+	return 0;
+}
+
+
 static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
 			size_t buf_len)
 {
@@ -2401,9 +2821,21 @@
 
 	p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss;
 
+#ifdef ANDROID_P2P
+	if(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) {
+		p2p.p2p_concurrency = P2P_MULTI_CHANNEL_CONCURRENT;
+		wpa_printf(MSG_DEBUG, "P2P: Multi channel concurrency support");
+	} else {
+	// Add support for WPA_DRIVER_FLAGS_P2P_CONCURRENT
+		p2p.p2p_concurrency = P2P_SINGLE_CHANNEL_CONCURRENT;
+		wpa_printf(MSG_DEBUG, "P2P: Single channel concurrency support");
+	}
+#endif
+
 	global->p2p = p2p_init(&p2p);
 	if (global->p2p == NULL)
 		return -1;
+	global->p2p_init_wpa_s = wpa_s;
 
 	for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
 		if (wpa_s->conf->wps_vendor_ext[i] == NULL)
@@ -2431,7 +2863,7 @@
 		/* Clear any stored provisioning info */
 		p2p_clear_provisioning_info(
 			wpa_s->global->p2p,
-			wpa_s->go_params->peer_interface_addr);
+			wpa_s->go_params->peer_device_addr);
 	}
 
 	os_free(wpa_s->go_params);
@@ -2458,19 +2890,18 @@
 void wpas_p2p_deinit_global(struct wpa_global *global)
 {
 	struct wpa_supplicant *wpa_s, *tmp;
-	char *ifname;
+
+	wpa_s = global->ifaces;
+	if (wpa_s)
+		wpas_p2p_service_flush(wpa_s);
 
 	if (global->p2p == NULL)
 		return;
 
 	/* Remove remaining P2P group interfaces */
-	wpa_s = global->ifaces;
-	if (wpa_s)
-		wpas_p2p_service_flush(wpa_s);
 	while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
 		wpa_s = wpa_s->next;
 	while (wpa_s) {
-		enum wpa_driver_if_type type;
 		tmp = global->ifaces;
 		while (tmp &&
 		       (tmp == wpa_s ||
@@ -2479,12 +2910,8 @@
 		}
 		if (tmp == NULL)
 			break;
-		ifname = os_strdup(tmp->ifname);
-		type = wpas_p2p_if_type(tmp->p2p_group_interface);
-		wpa_supplicant_remove_iface(global, tmp, 0);
-		if (ifname)
-			wpa_drv_if_remove(wpa_s, type, ifname);
-		os_free(ifname);
+		/* Disconnect from the P2P group and deinit the interface */
+		wpas_p2p_disconnect(tmp);
 	}
 
 	/*
@@ -2498,6 +2925,7 @@
 
 	p2p_deinit(global->p2p);
 	global->p2p = NULL;
+	global->p2p_init_wpa_s = NULL;
 }
 
 
@@ -2522,7 +2950,8 @@
 				 const u8 *peer_addr,
 				 enum p2p_wps_method wps_method,
 				 int go_intent, const u8 *own_interface_addr,
-				 unsigned int force_freq, int persistent_group)
+				 unsigned int force_freq, int persistent_group,
+				 struct wpa_ssid *ssid)
 {
 	if (persistent_group && wpa_s->conf->persistent_reconnect)
 		persistent_group = 2;
@@ -2533,9 +2962,18 @@
 					   force_freq, persistent_group);
 	}
 
+	/*
+	 * Increase GO config timeout if HT40 is used since it takes some time
+	 * to scan channels for coex purposes before the BSS can be started.
+	 */
+	p2p_set_config_timeout(wpa_s->global->p2p,
+			       wpa_s->p2p_go_ht40 ? 255 : 100, 20);
+
 	return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method,
 			   go_intent, own_interface_addr, force_freq,
-			   persistent_group);
+			   persistent_group, ssid ? ssid->ssid : NULL,
+			   ssid ? ssid->ssid_len : 0,
+			   wpa_s->p2p_pd_before_go_neg);
 }
 
 
@@ -2543,7 +2981,8 @@
 				const u8 *peer_addr,
 				enum p2p_wps_method wps_method,
 				int go_intent, const u8 *own_interface_addr,
-				unsigned int force_freq, int persistent_group)
+				unsigned int force_freq, int persistent_group,
+				struct wpa_ssid *ssid)
 {
 	if (persistent_group && wpa_s->conf->persistent_reconnect)
 		persistent_group = 2;
@@ -2553,7 +2992,8 @@
 
 	return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
 			     go_intent, own_interface_addr, force_freq,
-			     persistent_group);
+			     persistent_group, ssid ? ssid->ssid : NULL,
+			     ssid ? ssid->ssid_len : 0);
 }
 
 
@@ -2567,6 +3007,13 @@
 			   " for join operationg - stop join attempt",
 			   MAC2STR(wpa_s->pending_join_iface_addr));
 		eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+		if (wpa_s->p2p_auto_pd) {
+			wpa_s->p2p_auto_pd = 0;
+			wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+				" p2p_dev_addr=" MACSTR " status=N/A",
+				MAC2STR(wpa_s->pending_join_dev_addr));
+			return;
+		}
 		wpa_msg(wpa_s->parent, MSG_INFO,
 			P2P_EVENT_GROUP_FORMATION_FAILURE);
 	}
@@ -2588,26 +3035,156 @@
 }
 
 
+static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq)
+{
+	struct wpa_supplicant *iface;
+	int shared_freq;
+	u8 bssid[ETH_ALEN];
+
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)
+		return 0;
+
+	for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+		if (!wpas_p2p_create_iface(wpa_s) && iface == wpa_s)
+			continue;
+		if (iface->current_ssid == NULL || iface->assoc_freq == 0)
+			continue;
+		if (iface->current_ssid->mode == WPAS_MODE_AP ||
+		    iface->current_ssid->mode == WPAS_MODE_P2P_GO)
+			shared_freq = iface->current_ssid->frequency;
+		else if (wpa_drv_get_bssid(iface, bssid) == 0)
+			shared_freq = iface->assoc_freq;
+		else
+			shared_freq = 0;
+
+		if (shared_freq && freq != shared_freq) {
+			wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - %s "
+				   "connected on %d MHz - new connection on "
+				   "%d MHz", iface->ifname, shared_freq, freq);
+			return 1;
+		}
+	}
+
+	shared_freq = wpa_drv_shared_freq(wpa_s);
+	if (shared_freq > 0 && shared_freq != freq) {
+		wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - shared "
+			   "virtual interface connected on %d MHz - new "
+			   "connection on %d MHz", shared_freq, freq);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s,
+			    const u8 *peer_dev_addr)
+{
+	struct wpa_bss *bss;
+	int updated;
+
+	bss = wpa_bss_get_p2p_dev_addr(wpa_s, peer_dev_addr);
+	if (bss == NULL)
+		return -1;
+	if (bss->last_update_idx < wpa_s->bss_update_idx) {
+		wpa_printf(MSG_DEBUG, "P2P: Peer BSS entry not updated in the "
+			   "last scan");
+		return 0;
+	}
+
+	updated = os_time_before(&wpa_s->p2p_auto_started, &bss->last_update);
+	wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at "
+		   "%ld.%06ld (%supdated in last scan)",
+		   bss->last_update.sec, bss->last_update.usec,
+		   updated ? "": "not ");
+
+	return updated;
+}
+
+
 static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
 				   struct wpa_scan_results *scan_res)
 {
 	struct wpa_bss *bss;
 	int freq;
 	u8 iface_addr[ETH_ALEN];
-#ifdef ANDROID_P2P	
-	int shared_freq = 0;
-#endif
+
 	eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
 
 	if (wpa_s->global->p2p_disabled)
 		return;
 
-	wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for join",
-		   scan_res ? (int) scan_res->num : -1);
+	wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for %sjoin",
+		   scan_res ? (int) scan_res->num : -1,
+		   wpa_s->p2p_auto_join ? "auto_" : "");
 
 	if (scan_res)
 		wpas_p2p_scan_res_handler(wpa_s, scan_res);
 
+	if (wpa_s->p2p_auto_pd) {
+		int join = wpas_p2p_peer_go(wpa_s,
+					    wpa_s->pending_join_dev_addr);
+		if (join == 0 &&
+		    wpa_s->auto_pd_scan_retry < P2P_AUTO_PD_SCAN_ATTEMPTS) {
+			wpa_s->auto_pd_scan_retry++;
+			bss = wpa_bss_get_bssid(wpa_s,
+						wpa_s->pending_join_dev_addr);
+			if (bss) {
+				freq = bss->freq;
+				wpa_printf(MSG_DEBUG, "P2P: Scan retry %d for "
+					   "the peer " MACSTR " at %d MHz",
+					   wpa_s->auto_pd_scan_retry,
+					   MAC2STR(wpa_s->
+						   pending_join_dev_addr),
+					   freq);
+				wpas_p2p_join_scan_req(wpa_s, freq);
+				return;
+			}
+		}
+
+		if (join < 0)
+			join = 0;
+
+		wpa_s->p2p_auto_pd = 0;
+		wpa_s->pending_pd_use = join ? AUTO_PD_JOIN : AUTO_PD_GO_NEG;
+		wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d",
+			   MAC2STR(wpa_s->pending_join_dev_addr), join);
+		if (p2p_prov_disc_req(wpa_s->global->p2p,
+				      wpa_s->pending_join_dev_addr,
+				      wpa_s->pending_pd_config_methods, join,
+				      0) < 0) {
+			wpa_s->p2p_auto_pd = 0;
+			wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+				" p2p_dev_addr=" MACSTR " status=N/A",
+				MAC2STR(wpa_s->pending_join_dev_addr));
+		}
+		return;
+	}
+
+	if (wpa_s->p2p_auto_join) {
+		int join = wpas_p2p_peer_go(wpa_s,
+					    wpa_s->pending_join_dev_addr);
+		if (join < 0) {
+			wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be "
+				   "running a GO -> use GO Negotiation");
+			wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr,
+					 wpa_s->p2p_pin, wpa_s->p2p_wps_method,
+					 wpa_s->p2p_persistent_group, 0, 0, 0,
+					 wpa_s->p2p_go_intent,
+					 wpa_s->p2p_connect_freq,
+					 wpa_s->p2p_persistent_id,
+					 wpa_s->p2p_pd_before_go_neg,
+					 wpa_s->p2p_go_ht40);
+			return;
+		}
+
+		wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> "
+			   "try to join the group", join ? "" :
+			   " in older scan");
+		if (!join)
+			wpa_s->p2p_fallback_to_go_neg = 1;
+	}
+
 	freq = p2p_get_oper_freq(wpa_s->global->p2p,
 				 wpa_s->pending_join_iface_addr);
 	if (freq < 0 &&
@@ -2631,16 +3208,6 @@
 		wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
 			   "from P2P peer table: %d MHz", freq);
 	}
-
-#ifdef ANDROID_P2P
-	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) && 
-		((shared_freq = wpa_drv_shared_freq(wpa_s)) > 0) && (shared_freq != freq)) {
-		wpa_msg(wpa_s->parent, MSG_INFO,
-					P2P_EVENT_GROUP_FORMATION_FAILURE "reason=FREQ_CONFLICT");
-		return;
-	}
-#endif
-
 	bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr);
 	if (bss) {
 		freq = bss->freq;
@@ -2650,6 +3217,13 @@
 	if (freq > 0) {
 		u16 method;
 
+		if (wpas_check_freq_conflict(wpa_s, freq) > 0) {
+			wpa_msg(wpa_s->parent, MSG_INFO,
+				P2P_EVENT_GROUP_FORMATION_FAILURE
+				"reason=FREQ_CONFLICT");
+			return;
+		}
+
 		wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
 			   "prior to joining an existing group (GO " MACSTR
 			   " freq=%u MHz)",
@@ -2722,14 +3296,24 @@
 }
 
 
-static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
+static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq)
 {
-	struct wpa_supplicant *wpa_s = eloop_ctx;
 	int ret;
 	struct wpa_driver_scan_params params;
 	struct wpabuf *wps_ie, *ies;
 	size_t ielen;
+	int freqs[2] = { 0, 0 };
+#ifdef ANDROID_P2P
+	int oper_freq;
 
+	/* If freq is not provided, check the operating freq of the GO and do a
+	 * a directed scan to save time
+	 */
+	if(!freq) {
+		freq = (oper_freq = p2p_get_oper_freq(wpa_s->global->p2p,
+			 wpa_s->pending_join_iface_addr) == -1) ? 0 : oper_freq; 
+	}
+#endif
 	os_memset(&params, 0, sizeof(params));
 
 	/* P2P Wildcard SSID */
@@ -2738,8 +3322,9 @@
 	params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
 
 	wpa_s->wps->dev.p2p = 1;
-	wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
-					WPS_REQ_ENROLLEE, 0, NULL);
+	wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev,
+					wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0,
+					NULL);
 	if (wps_ie == NULL) {
 		wpas_p2p_scan_res_join(wpa_s, NULL);
 		return;
@@ -2760,13 +3345,18 @@
 	params.p2p_probe = 1;
 	params.extra_ies = wpabuf_head(ies);
 	params.extra_ies_len = wpabuf_len(ies);
+	if (freq > 0) {
+		freqs[0] = freq;
+		params.freqs = freqs;
+	}
 
 	/*
 	 * Run a scan to update BSS table and start Provision Discovery once
 	 * the new scan results become available.
 	 */
-	wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
 	ret = wpa_drv_scan(wpa_s, &params);
+	if (!ret)
+		wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
 
 	wpabuf_free(ies);
 
@@ -2780,13 +3370,24 @@
 }
 
 
+static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	wpas_p2p_join_scan_req(wpa_s, 0);
+}
+
+
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
-			 const u8 *dev_addr, enum p2p_wps_method wps_method)
+			 const u8 *dev_addr, enum p2p_wps_method wps_method,
+			 int auto_join)
 {
 	wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
-		   MACSTR " dev " MACSTR ")",
-		   MAC2STR(iface_addr), MAC2STR(dev_addr));
+		   MACSTR " dev " MACSTR ")%s",
+		   MAC2STR(iface_addr), MAC2STR(dev_addr),
+		   auto_join ? " (auto_join)" : "");
 
+	wpa_s->p2p_auto_pd = 0;
+	wpa_s->p2p_auto_join = !!auto_join;
 	os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
 	os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
 	wpa_s->pending_join_wps_method = wps_method;
@@ -2804,6 +3405,7 @@
 {
 	struct wpa_supplicant *group;
 	struct p2p_go_neg_results res;
+	struct wpa_bss *bss;
 
 	eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL);
 	group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
@@ -2816,11 +3418,19 @@
 	}
 
 	group->p2p_in_provisioning = 1;
+	group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
 
 	os_memset(&res, 0, sizeof(res));
 	os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
 		  ETH_ALEN);
 	res.wps_method = wpa_s->pending_join_wps_method;
+	bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr);
+	if (bss) {
+		res.freq = bss->freq;
+		res.ssid_len = bss->ssid_len;
+		os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+	}
+
 	if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
 		wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel prior to "
 			   "starting client");
@@ -2849,30 +3459,45 @@
  * @peer_addr: Address of the peer P2P Device
  * @pin: PIN to use during provisioning or %NULL to indicate PBC mode
  * @persistent_group: Whether to create a persistent group
+ * @auto_join: Whether to select join vs. GO Negotiation automatically
  * @join: Whether to join an existing group (as a client) instead of starting
  *	Group Owner negotiation; @peer_addr is BSSID in that case
  * @auth: Whether to only authorize the connection instead of doing that and
  *	initiating Group Owner negotiation
  * @go_intent: GO Intent or -1 to use default
  * @freq: Frequency for the group or 0 for auto-selection
+ * @persistent_id: Persistent group credentials to use for forcing GO
+ *	parameters or -1 to generate new values (SSID/passphrase)
+ * @pd: Whether to send Provision Discovery prior to GO Negotiation as an
+ *	interoperability workaround when initiating group formation
+ * @ht40: Start GO with 40 MHz channel width
  * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
  *	failure, -2 on failure due to channel not currently available,
  *	-3 if forced channel is not supported
  */
 int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		     const char *pin, enum p2p_wps_method wps_method,
-		     int persistent_group, int join, int auth, int go_intent,
-		     int freq)
+		     int persistent_group, int auto_join, int join, int auth,
+		     int go_intent, int freq, int persistent_id, int pd,
+		     int ht40)
 {
 	int force_freq = 0, oper_freq = 0;
 	u8 bssid[ETH_ALEN];
 	int ret = 0;
 	enum wpa_driver_if_type iftype;
 	const u8 *if_addr;
+	struct wpa_ssid *ssid = NULL;
 
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
+	if (persistent_id >= 0) {
+		ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
+		if (ssid == NULL || ssid->disabled != 2 ||
+		    ssid->mode != WPAS_MODE_P2P_GO)
+			return -1;
+	}
+
 	if (go_intent < 0)
 		go_intent = wpa_s->conf->p2p_go_intent;
 
@@ -2880,6 +3505,13 @@
 		wpa_s->p2p_long_listen = 0;
 
 	wpa_s->p2p_wps_method = wps_method;
+	wpa_s->p2p_persistent_group = !!persistent_group;
+	wpa_s->p2p_persistent_id = persistent_id;
+	wpa_s->p2p_go_intent = go_intent;
+	wpa_s->p2p_connect_freq = freq;
+	wpa_s->p2p_fallback_to_go_neg = 0;
+	wpa_s->p2p_pd_before_go_neg = !!pd;
+	wpa_s->p2p_go_ht40 = !!ht40;
 
 	if (pin)
 		os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
@@ -2892,7 +3524,7 @@
 	} else
 		wpa_s->p2p_pin[0] = '\0';
 
-	if (join) {
+	if (join || auto_join) {
 		u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
 		if (auth) {
 			wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
@@ -2908,8 +3540,15 @@
 			p2p_get_dev_addr(wpa_s->global->p2p, peer_addr,
 					 dev_addr);
 		}
-		if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method) <
-		    0)
+		if (auto_join) {
+			os_get_time(&wpa_s->p2p_auto_started);
+			wpa_printf(MSG_DEBUG, "P2P: Auto join started at "
+				   "%ld.%06ld",
+				   wpa_s->p2p_auto_started.sec,
+				   wpa_s->p2p_auto_started.usec);
+		}
+		if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
+				  auto_join) < 0)
 			return -1;
 		return ret;
 	}
@@ -2982,14 +3621,15 @@
 	if (auth) {
 		if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
 					 go_intent, if_addr,
-					 force_freq, persistent_group) < 0)
+					 force_freq, persistent_group, ssid) <
+		    0)
 			return -1;
 		return ret;
 	}
 
 	if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
 				  go_intent, if_addr, force_freq,
-				  persistent_group) < 0) {
+				  persistent_group, ssid) < 0) {
 		if (wpa_s->create_p2p_iface)
 			wpas_p2p_remove_pending_group_interface(wpa_s);
 		return -1;
@@ -3103,13 +3743,14 @@
 
 static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
 				   struct p2p_go_neg_results *params,
-				   int freq)
+				   int freq, int ht40)
 {
 	u8 bssid[ETH_ALEN];
 	int res;
 
 	os_memset(params, 0, sizeof(*params));
 	params->role_go = 1;
+	params->ht40 = ht40;
 	if (freq) {
 		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced "
 			   "frequency %d MHz", freq);
@@ -3147,7 +3788,18 @@
 		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
 			   "channel %d MHz", params->freq);
 	} else {
-		params->freq = 2412;
+		int chan;
+		for (chan = 0; chan < 11; chan++) {
+			params->freq = 2412 + chan * 5;
+			if (!wpas_p2p_disallowed_freq(wpa_s->global,
+						      params->freq))
+				break;
+		}
+		if (chan == 11) {
+			wpa_printf(MSG_DEBUG, "P2P: No 2.4 GHz channel "
+				   "allowed");
+			return -1;
+		}
 		wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference "
 			   "known)", params->freq);
 	}
@@ -3183,18 +3835,27 @@
 {
 	struct wpa_supplicant *group_wpa_s;
 
-	if (!wpas_p2p_create_iface(wpa_s))
+	if (!wpas_p2p_create_iface(wpa_s)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use same interface for group "
+			"operations");
 		return wpa_s;
+	}
 
 	if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
-					 WPA_IF_P2P_CLIENT) < 0)
+					 WPA_IF_P2P_CLIENT) < 0) {
+		wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to add group interface");
 		return NULL;
+	}
 	group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go);
 	if (group_wpa_s == NULL) {
+		wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to initialize group "
+			"interface");
 		wpas_p2p_remove_pending_group_interface(wpa_s);
 		return NULL;
 	}
 
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s",
+		group_wpa_s->ifname);
 	return group_wpa_s;
 }
 
@@ -3210,7 +3871,7 @@
  * i.e., without using Group Owner Negotiation.
  */
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
-		       int freq)
+		       int freq, int ht40)
 {
 	struct p2p_go_neg_results params;
 	unsigned int r;
@@ -3268,8 +3929,15 @@
 		return -1;
 	}
 
-	if (wpas_p2p_init_go_params(wpa_s, &params, freq))
+	if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40))
 		return -1;
+	if (params.freq &&
+	    !p2p_supported_freq(wpa_s->global->p2p, params.freq)) {
+		wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO "
+			   "(%u MHz) is not supported for P2P uses",
+			   params.freq);
+		return -1;
+	}
 	p2p_go_params(wpa_s->global->p2p, &params);
 	params.persistent_group = persistent_group;
 
@@ -3328,7 +3996,7 @@
 
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid, int addr_allocated,
-				  int freq)
+				  int freq, int ht40)
 {
 	struct p2p_go_neg_results params;
 	int go = 0;
@@ -3346,13 +4014,15 @@
 	/* Make sure we are not running find during connection establishment */
 	wpas_p2p_stop_find(wpa_s);
 
+	wpa_s->p2p_fallback_to_go_neg = 0;
+
 	if (ssid->mode == WPAS_MODE_INFRA)
 		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated);
 
 	if (ssid->mode != WPAS_MODE_P2P_GO)
 		return -1;
 
-	if (wpas_p2p_init_go_params(wpa_s, &params, freq))
+	if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40))
 		return -1;
 
 	params.role_go = 1;
@@ -3409,18 +4079,15 @@
 	if (!wpa_s->ap_iface)
 		return;
 	wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
-	if (idle) {
-		wpa_printf(MSG_DEBUG,"Calling set group idle time out from idle_update");
+	if (idle)
 		wpas_p2p_set_group_idle_timeout(wpa_s);
-	}
 	else
 		eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 }
 
 
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
-				       int persistent_group,
-				       int group_formation)
+				       struct wpa_ssid *ssid)
 {
 	struct p2p_group *group;
 	struct p2p_group_config *cfg;
@@ -3434,9 +4101,9 @@
 	if (cfg == NULL)
 		return NULL;
 
-	if (persistent_group && wpa_s->conf->persistent_reconnect)
+	if (ssid->p2p_persistent_group && wpa_s->conf->persistent_reconnect)
 		cfg->persistent_group = 2;
-	else if (persistent_group)
+	else if (ssid->p2p_persistent_group)
 		cfg->persistent_group = 1;
 	os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN);
 	if (wpa_s->max_stations &&
@@ -3444,6 +4111,8 @@
 		cfg->max_clients = wpa_s->max_stations;
 	else
 		cfg->max_clients = wpa_s->conf->max_num_sta;
+	os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len);
+	cfg->ssid_len = ssid->ssid_len;
 	cfg->cb_ctx = wpa_s;
 	cfg->ie_update = wpas_p2p_ie_update;
 	cfg->idle_update = wpas_p2p_idle_update;
@@ -3451,7 +4120,7 @@
 	group = p2p_group_init(wpa_s->global->p2p, cfg);
 	if (group == NULL)
 		os_free(cfg);
-	if (!group_formation)
+	if (ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)
 		p2p_group_notif_formation_done(group);
 	wpa_s->p2p_group = group;
 	return group;
@@ -3461,17 +4130,36 @@
 void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 			  int registrar)
 {
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
 	if (!wpa_s->p2p_in_provisioning) {
 		wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P "
 			   "provisioning not in progress");
 		return;
 	}
 
-	/* Clear any stored provisioning info */
-	p2p_clear_provisioning_info(wpa_s->global->p2p, peer_addr);
+	if (ssid && ssid->mode == WPAS_MODE_INFRA) {
+		u8 go_dev_addr[ETH_ALEN];
+		os_memcpy(go_dev_addr, wpa_s->bssid, ETH_ALEN);
+		wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
+					  ssid->ssid_len);
+		/* Clear any stored provisioning info */
+		p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr);
+	}
 
 	eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
 			     NULL);
+	if (ssid && ssid->mode == WPAS_MODE_INFRA) {
+		/*
+		 * Use a separate timeout for initial data connection to
+		 * complete to allow the group to be removed automatically if
+		 * something goes wrong in this step before the P2P group idle
+		 * timeout mechanism is taken into use.
+		 */
+		eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+				       wpas_p2p_group_formation_timeout,
+				       wpa_s->parent, NULL);
+	}
 	if (wpa_s->global->p2p)
 		p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
 	else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
@@ -3492,7 +4180,7 @@
 	if (wpa_s->go_params) {
 		p2p_clear_provisioning_info(
 			wpa_s->global->p2p,
-			wpa_s->go_params->peer_interface_addr);
+			wpa_s->go_params->peer_device_addr);
 	}
 
 	wpas_notify_p2p_wps_failed(wpa_s, fail);
@@ -3500,10 +4188,13 @@
 
 
 int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
-		       const char *config_method, int join)
+		       const char *config_method,
+		       enum wpas_p2p_prov_disc_use use)
 {
 	u16 config_methods;
 
+	wpa_s->p2p_fallback_to_go_neg = 0;
+	wpa_s->pending_pd_use = NORMAL_PD;
 	if (os_strncmp(config_method, "display", 7) == 0)
 		config_methods = WPS_CONFIG_DISPLAY;
 	else if (os_strncmp(config_method, "keypad", 6) == 0)
@@ -3516,16 +4207,35 @@
 		return -1;
 	}
 
+	if (use == WPAS_P2P_PD_AUTO) {
+		os_memcpy(wpa_s->pending_join_dev_addr, peer_addr, ETH_ALEN);
+		wpa_s->pending_pd_config_methods = config_methods;
+		wpa_s->p2p_auto_pd = 1;
+		wpa_s->p2p_auto_join = 0;
+		wpa_s->pending_pd_before_join = 0;
+		wpa_s->auto_pd_scan_retry = 0;
+		wpas_p2p_stop_find(wpa_s);
+		wpa_s->p2p_join_scan_count = 0;
+		os_get_time(&wpa_s->p2p_auto_started);
+		wpa_printf(MSG_DEBUG, "P2P: Auto PD started at %ld.%06ld",
+			   wpa_s->p2p_auto_started.sec,
+			   wpa_s->p2p_auto_started.usec);
+		wpas_p2p_join_scan(wpa_s, NULL);
+		return 0;
+	}
+
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
 		return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr,
-						 config_methods, join);
+						 config_methods,
+						 use == WPAS_P2P_PD_FOR_JOIN);
 	}
 
 	if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
 		return -1;
 
 	return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr,
-				 config_methods, join, 0);
+				 config_methods, use == WPAS_P2P_PD_FOR_JOIN,
+				 0);
 }
 
 
@@ -3551,7 +4261,7 @@
 int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
 		  enum p2p_discovery_type type,
 		  unsigned int num_req_dev_types, const u8 *req_dev_types,
-		  const u8 *dev_id)
+		  const u8 *dev_id, unsigned int search_delay)
 {
 	wpas_p2p_clear_pending_action_tx(wpa_s);
 	wpa_s->p2p_long_listen = 0;
@@ -3559,13 +4269,15 @@
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
 		return wpa_drv_p2p_find(wpa_s, timeout, type);
 
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
+	    wpa_s->p2p_in_provisioning)
 		return -1;
 
 	wpa_supplicant_cancel_sched_scan(wpa_s);
 
 	return p2p_find(wpa_s->global->p2p, timeout, type,
-			num_req_dev_types, req_dev_types, dev_id);
+			num_req_dev_types, req_dev_types, dev_id,
+			search_delay);
 }
 
 
@@ -3575,7 +4287,7 @@
 	wpa_s->p2p_long_listen = 0;
 	eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
-	wpa_s->p2p_cb_on_scan_complete = 0;
+	wpa_s->global->p2p_cb_on_scan_complete = 0;
 
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
 		wpa_drv_p2p_stop_find(wpa_s);
@@ -3661,15 +4373,27 @@
 
 int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
 			  const u8 *dst, const u8 *bssid,
-			  const u8 *ie, size_t ie_len)
+			  const u8 *ie, size_t ie_len, int ssi_signal)
 {
 	if (wpa_s->global->p2p_disabled)
 		return 0;
 	if (wpa_s->global->p2p == NULL)
 		return 0;
 
-	return p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
-				ie, ie_len);
+	switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
+				 ie, ie_len)) {
+	case P2P_PREQ_NOT_P2P:
+		wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
+				 ssi_signal);
+		/* fall through */
+	case P2P_PREQ_MALFORMED:
+	case P2P_PREQ_NOT_LISTEN:
+	case P2P_PREQ_NOT_PROCESSED:
+	default: /* make gcc happy */
+		return 0;
+	case P2P_PREQ_PROCESSED:
+		return 1;
+	}
 }
 
 
@@ -3726,11 +4450,17 @@
 
 /* Invite to reinvoke a persistent group */
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
-		    struct wpa_ssid *ssid, const u8 *go_dev_addr)
+		    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
+		    int ht40)
 {
 	enum p2p_invite_role role;
 	u8 *bssid = NULL;
+#ifdef ANDROID_P2P
+	int force_freq = 0, oper_freq = 0;
+#endif
 
+	wpa_s->p2p_persistent_go_freq = freq;
+	wpa_s->p2p_go_ht40 = !!ht40;
 	if (ssid->mode == WPAS_MODE_P2P_GO) {
 		role = P2P_INVITE_ROLE_GO;
 		if (peer_addr == NULL) {
@@ -3755,6 +4485,55 @@
 	}
 	wpa_s->pending_invite_ssid_id = ssid->id;
 
+#ifdef ANDROID_P2P
+	if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+	    wpa_s->assoc_freq)
+		oper_freq = wpa_s->assoc_freq;
+	else {
+		oper_freq = wpa_drv_shared_freq(wpa_s);
+		if (oper_freq < 0)
+			oper_freq = 0;
+	}
+
+	if (freq > 0) {
+		if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+			wpa_printf(MSG_DEBUG, "P2P: The forced channel "
+				   "(%u MHz) is not supported for P2P uses",
+				   freq);
+			return -3;
+		}
+
+		if (oper_freq > 0 && freq != oper_freq &&
+		    !(wpa_s->drv_flags &
+		      WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+			wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
+				   "on %u MHz while connected on another "
+				   "channel (%u MHz)", freq, oper_freq);
+			return -2;
+		}
+		wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+			   "requested channel (%u MHz)", freq);
+		force_freq = freq;
+	} else if (oper_freq > 0 &&
+		   !p2p_supported_freq(wpa_s->global->p2p, oper_freq)) {
+		if (!(wpa_s->drv_flags &
+		      WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+			wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
+				   "while connected on non-P2P supported "
+				   "channel (%u MHz)", oper_freq);
+			return -2;
+		}
+		wpa_printf(MSG_DEBUG, "P2P: Current operating channel "
+			   "(%u MHz) not available for P2P - try to use "
+			   "another channel", oper_freq);
+		force_freq = 0;
+	} else if (oper_freq > 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+			   "channel we are already using (%u MHz) on another "
+			   "interface", oper_freq);
+		force_freq = oper_freq;
+	}
+#endif
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
 		return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
 					  ssid->ssid, ssid->ssid_len,
@@ -3763,8 +4542,13 @@
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
+#ifdef ANDROID_P2P
 	return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
-			  ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 1);
+			  ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr, 1);
+#else
+	return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
+			  ssid->ssid, ssid->ssid_len, freq, go_dev_addr, 1);
+#endif
 }
 
 
@@ -3778,6 +4562,9 @@
 	struct wpa_ssid *ssid;
 	int persistent;
 
+	wpa_s->p2p_persistent_go_freq = 0;
+	wpa_s->p2p_go_ht40 = 0;
+
 	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 		if (os_strcmp(wpa_s->ifname, ifname) == 0)
 			break;
@@ -3840,8 +4627,13 @@
 	int persistent;
 	int freq;
 
+	if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) {
+		eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+				     wpa_s->parent, NULL);
+	}
+
 	if (!wpa_s->show_group_started || !ssid)
-		return;
+		goto done;
 
 	wpa_s->show_group_started = 0;
 
@@ -3883,6 +4675,19 @@
 	if (network_id < 0)
 		network_id = ssid->id;
 	wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1);
+
+done:
+	if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
+	    wpa_s->global->p2p != NULL) {
+		wpa_s->global->p2p_cb_on_scan_complete = 0;
+		if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
+				"continued after successful connection");
+			p2p_increase_search_delay(
+				wpa_s->global->p2p,
+				wpas_p2p_search_delay(wpa_s));
+		}
+	}
 }
 
 
@@ -3936,18 +4741,19 @@
 		return;
 	}
 
-	wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate %d"
-		   "group",wpa_s->conf->p2p_group_idle);
-	wpa_s->removal_reason = P2P_GROUP_REMOVAL_IDLE_TIMEOUT;
-	wpas_p2p_group_delete(wpa_s);
+	wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate "
+		   "group");
+	wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_IDLE_TIMEOUT);
 }
 
 
 static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
 {
-	unsigned int timeout;
+	int timeout;
 
-	eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+	if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
+		wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
+
 	if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
 		return;
 
@@ -3959,6 +4765,38 @@
 	if (timeout == 0)
 		return;
 
+	if (timeout < 0) {
+		if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA)
+			timeout = 0; /* special client mode no-timeout */
+		else
+			return;
+	}
+
+	if (wpa_s->p2p_in_provisioning) {
+		/*
+		 * Use the normal group formation timeout during the
+		 * provisioning phase to avoid terminating this process too
+		 * early due to group idle timeout.
+		 */
+		wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
+			   "during provisioning");
+		return;
+	}
+#ifndef ANDROID_P2P
+	if (wpa_s->show_group_started) {
+		/*
+		 * Use the normal group formation timeout between the end of
+		 * the provisioning phase and completion of 4-way handshake to
+		 * avoid terminating this process too early due to group idle
+		 * timeout.
+		 */
+		wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
+			   "while waiting for initial 4-way handshake to "
+			   "complete");
+		return;
+	}
+#endif
+
 	wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds",
 		   timeout);
 	eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout,
@@ -3966,27 +4804,48 @@
 }
 
 
-void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
-			   u16 reason_code, const u8 *ie, size_t ie_len)
+/* Returns 1 if the interface was removed */
+int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			  u16 reason_code, const u8 *ie, size_t ie_len,
+			  int locally_generated)
 {
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return;
+		return 0;
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return;
+		return 0;
 
-	p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len);
+	if (!locally_generated)
+		p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie,
+				 ie_len);
+
+	if (reason_code == WLAN_REASON_DEAUTH_LEAVING && !locally_generated &&
+	    wpa_s->current_ssid &&
+	    wpa_s->current_ssid->p2p_group &&
+	    wpa_s->current_ssid->mode == WPAS_MODE_INFRA) {
+		wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group "
+			   "session is ending");
+		if (wpas_p2p_group_delete(wpa_s,
+					  P2P_GROUP_REMOVAL_GO_ENDING_SESSION)
+		    > 0)
+			return 1;
+	}
+
+	return 0;
 }
 
 
 void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
-			     u16 reason_code, const u8 *ie, size_t ie_len)
+			     u16 reason_code, const u8 *ie, size_t ie_len,
+			     int locally_generated)
 {
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return;
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
 		return;
 
-	p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len);
+	if (!locally_generated)
+		p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie,
+				   ie_len);
 }
 
 
@@ -4102,6 +4961,14 @@
 			wpa_printf(MSG_ERROR, "P2P: Own oper channel update "
 				   "failed: %d", ret);
 	}
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PREF_CHAN) {
+		if (p2p_set_pref_chan(p2p, wpa_s->conf->num_p2p_pref_chan,
+				      wpa_s->conf->p2p_pref_chan) < 0) {
+			wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
+				   "update failed");
+		}
+	}
 }
 
 
@@ -4200,8 +5067,9 @@
 		wpas_p2p_disable_cross_connect(wpa_s);
 	else
 		wpas_p2p_enable_cross_connect(wpa_s);
-	if (!wpa_s->ap_iface)
-		eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+	if (!wpa_s->ap_iface &&
+	    eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
+		wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
 }
 
 
@@ -4211,10 +5079,7 @@
 	if (!wpa_s->ap_iface &&
 	    !eloop_is_timeout_registered(wpas_p2p_group_idle_timeout,
 					 wpa_s, NULL))
-	{
-		wpa_printf(MSG_DEBUG,"Calling set grouple idle_timeout from notif_disconnected");
 		wpas_p2p_set_group_idle_timeout(wpa_s);
-	}
 }
 
 
@@ -4316,6 +5181,7 @@
 		wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer "
 			   MACSTR, MAC2STR(peer));
 		p2p_unauthorize(global->p2p, peer);
+		found = 1;
 	}
 
 	wpas_p2p_stop_find(wpa_s);
@@ -4331,7 +5197,8 @@
 			found = 1;
 			eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
 					     wpa_s->parent, NULL);
-			wpas_p2p_group_delete(wpa_s);
+			wpas_p2p_group_delete(wpa_s,
+					      P2P_GROUP_REMOVAL_REQUESTED);
 			break;
 		}
 	}
@@ -4352,8 +5219,7 @@
 
 	wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not "
 		   "being available anymore");
-	wpa_s->removal_reason = P2P_GROUP_REMOVAL_UNAVAILABLE;
-	wpas_p2p_group_delete(wpa_s);
+	wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_UNAVAILABLE);
 }
 
 
@@ -4399,10 +5265,8 @@
 	if (wpa_s == NULL)
 		return -1;
 
-	wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
-	wpas_p2p_group_delete(wpa_s);
-
-	return 0;
+	return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED) < 0 ?
+		-1 : 0;
 }
 
 
@@ -4417,20 +5281,22 @@
 
 void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
 			      struct wpa_ssid *ssid)
-
 {
 	if (wpa_s->p2p_in_provisioning && ssid->p2p_group &&
 	    eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
 				 wpa_s->parent, NULL) > 0) {
+		/**
+		 * Remove the network by scheduling the group formation
+		 * timeout to happen immediately. The teardown code
+		 * needs to be scheduled to run asynch later so that we
+		 * don't delete data from under ourselves unexpectedly.
+		 * Calling wpas_p2p_group_formation_timeout directly
+		 * causes a series of crashes in WPS failure scenarios.
+		 */
 		wpa_printf(MSG_DEBUG, "P2P: Canceled group formation due to "
 			   "P2P group network getting removed");
-#ifdef ANDROID_P2P
-		/* Give time for any Pending WPS Frame exchange */
-		eloop_register_timeout(5, 0, wpas_p2p_group_formation_timeout,
-			wpa_s->parent, NULL);
-#else
-		wpas_p2p_group_formation_timeout(wpa_s->parent, NULL);
-#endif
+		eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
+				       wpa_s->parent, NULL);
 	}
 }
 
@@ -4473,6 +5339,82 @@
 	wpas_p2p_add_persistent_group_client(wpa_s, addr);
 }
 
+
+static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+					int group_added)
+{
+	struct wpa_supplicant *group = wpa_s;
+	eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL);
+	if (wpa_s->global->p2p_group_formation)
+		group = wpa_s->global->p2p_group_formation;
+	wpa_s = wpa_s->parent;
+	offchannel_send_action_done(wpa_s);
+	if (group_added)
+		wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT);
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation");
+	wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
+			 wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
+			 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
+			 wpa_s->p2p_persistent_id,
+			 wpa_s->p2p_pd_before_go_neg,
+			 wpa_s->p2p_go_ht40);
+}
+
+
+int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->p2p_fallback_to_go_neg ||
+	    wpa_s->p2p_in_provisioning <= 5)
+		return 0;
+
+	if (wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr) > 0)
+		return 0; /* peer operating as a GO */
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - "
+		"fallback to GO Negotiation");
+	wpas_p2p_fallback_to_go_neg(wpa_s, 1);
+
+	return 1;
+}
+
+
+unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s)
+{
+	const char *rn, *rn2;
+	struct wpa_supplicant *ifs;
+
+	if (wpa_s->wpa_state > WPA_SCANNING) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to "
+			"concurrent operation",
+			P2P_CONCURRENT_SEARCH_DELAY);
+		return P2P_CONCURRENT_SEARCH_DELAY;
+	}
+
+	if (!wpa_s->driver->get_radio_name)
+		return 0;
+	rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
+	if (rn == NULL || rn[0] == '\0')
+		return 0;
+
+	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+		if (ifs == wpa_s || !ifs->driver->get_radio_name)
+			continue;
+
+		rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
+		if (!rn2 || os_strcmp(rn, rn2) != 0)
+			continue;
+		if (ifs->wpa_state > WPA_SCANNING) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search "
+				"delay due to concurrent operation on "
+				"interface %s",
+				P2P_CONCURRENT_SEARCH_DELAY, ifs->ifname);
+			return P2P_CONCURRENT_SEARCH_DELAY;
+		}
+	}
+
+	return 0;
+}
+
 #ifdef ANDROID_P2P
 int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq)
 {
@@ -4498,12 +5440,11 @@
 			 * P2P Client, remove the interface depending up on the connection
 			 * priority */
 			if(!wpas_is_p2p_prioritized(wpa_s)) {
-				/* STA connection has priority over existing 
+				/* STA connection has priority over existing
 				 * P2P connection. So remove the interface */
 				wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to Single channel"
 						"concurrent mode frequency conflict");
-				iface->removal_reason = P2P_GROUP_REMOVAL_FREQ_CONFLICT;
-				wpas_p2p_group_delete(iface);
+				wpas_p2p_group_delete(iface, P2P_GROUP_REMOVAL_FREQ_CONFLICT);
 			} else {
 				/* Existing connection has the priority. Disable the newly
                  * selected network and let the application know about it.
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 05c648a..6e4cc1d 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -19,8 +19,9 @@
 void wpas_p2p_deinit_global(struct wpa_global *global);
 int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		     const char *pin, enum p2p_wps_method wps_method,
-		     int persistent_group, int join, int auth, int go_intent,
-		     int freq);
+		     int persistent_group, int auto_join, int join,
+		     int auth, int go_intent, int freq, int persistent_id,
+		     int pd, int ht40);
 void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 				   unsigned int freq, unsigned int duration);
 void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
@@ -31,17 +32,22 @@
 #endif
 int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
-		       int freq);
+		       int freq, int ht40);
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid, int addr_allocated,
-				  int freq);
+				  int freq, int ht40);
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
-				       int persistent_group,
-				       int group_formation);
+				       struct wpa_ssid *ssid);
 void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 			  int registrar);
+enum wpas_p2p_prov_disc_use {
+	WPAS_P2P_PD_FOR_GO_NEG,
+	WPAS_P2P_PD_FOR_JOIN,
+	WPAS_P2P_PD_AUTO
+};
 int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
-		       const char *config_method, int join);
+		       const char *config_method,
+		       enum wpas_p2p_prov_disc_use use);
 void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
 				const u8 *data, size_t data_len,
 				enum p2p_send_action_result result);
@@ -51,14 +57,15 @@
 int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
 		  enum p2p_discovery_type type,
 		  unsigned int num_req_dev_types, const u8 *req_dev_types,
-		  const u8 *dev_id);
+		  const u8 *dev_id, unsigned int search_delay);
 void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
 int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
 int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			  u8 *buf, size_t len, int p2p_group);
 int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
 			  const u8 *dst, const u8 *bssid,
-			  const u8 *ie, size_t ie_len);
+			  const u8 *ie, size_t ie_len,
+			  int ssi_signal);
 void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
 			const u8 *sa, const u8 *bssid,
 			u8 category, const u8 *data, size_t len, int freq);
@@ -83,11 +90,17 @@
 			const struct wpabuf *tlvs);
 u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
 			     u8 version, const char *query);
+u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
+				     const u8 *dst, const char *role);
 int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req);
 void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
 			  const u8 *dst, u8 dialog_token,
 			  const struct wpabuf *resp_tlvs);
+#ifdef ANDROID_P2P
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s, int action);
+#else
 void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s);
+#endif
 void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s);
 int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
 				 struct wpabuf *query, struct wpabuf *resp);
@@ -99,7 +112,8 @@
 			      const char *service);
 int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
-		    struct wpa_ssid *ssid, const u8 *go_dev_addr);
+		    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
+		    int ht40);
 int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
 			  const u8 *peer_addr, const u8 *go_dev_addr);
 void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
@@ -107,10 +121,12 @@
 			  u32 interval1, u32 duration2, u32 interval2);
 int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
 			unsigned int interval);
-void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
-			   u16 reason_code, const u8 *ie, size_t ie_len);
+int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			  u16 reason_code, const u8 *ie, size_t ie_len,
+			  int locally_generated);
 void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
-			     u16 reason_code, const u8 *ie, size_t ie_len);
+			     u16 reason_code, const u8 *ie, size_t ie_len,
+			     int locally_generated);
 void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
 int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
 		     int duration);
@@ -135,5 +151,9 @@
 					  size_t ssid_len);
 void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
 				       const u8 *addr);
+int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s);
+int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
+			   struct hostapd_hw_modes *mode, u8 channel);
+unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s);
 
 #endif /* P2P_SUPPLICANT_H */
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 5aee7ff..0f6433b 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -17,6 +17,7 @@
 #include "wps_supplicant.h"
 #include "p2p_supplicant.h"
 #include "p2p/p2p.h"
+#include "hs20_supplicant.h"
 #include "notify.h"
 #include "bss.h"
 #include "scan.h"
@@ -79,15 +80,24 @@
 #endif /* CONFIG_WPS */
 
 
-int wpa_supplicant_enabled_networks(struct wpa_config *conf)
+int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s)
 {
-	struct wpa_ssid *ssid = conf->ssid;
-	int count = 0;
+	struct wpa_ssid *ssid = wpa_s->conf->ssid;
+	int count = 0, disabled = 0;
 	while (ssid) {
-		if (!ssid->disabled)
+		if (!wpas_network_disabled(wpa_s, ssid))
 			count++;
+		else
+			disabled++;
 		ssid = ssid->next;
 	}
+	if (wpa_s->conf->cred && wpa_s->conf->interworking &&
+	    wpa_s->conf->auto_interworking)
+		count++;
+	if (count == 0 && disabled > 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled "
+			"networks)", disabled);
+	}
 	return count;
 }
 
@@ -96,7 +106,7 @@
 				     struct wpa_ssid *ssid)
 {
 	while (ssid) {
-		if (!ssid->disabled)
+		if (!wpas_network_disabled(wpa_s, ssid))
 			break;
 		ssid = ssid->next;
 	}
@@ -137,7 +147,7 @@
 	reslen = int_array_len(*res);
 	alen = int_array_len(a);
 
-	n = os_realloc(*res, (reslen + alen + 1) * sizeof(int));
+	n = os_realloc_array(*res, reslen + alen + 1, sizeof(int));
 	if (n == NULL) {
 		os_free(*res);
 		*res = NULL;
@@ -384,9 +394,7 @@
 #endif /* CONFIG_INTERWORKING */
 
 
-static struct wpabuf *
-wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s,
-			 struct wpa_driver_scan_params *params)
+static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
 {
 	struct wpabuf *extra_ie = NULL;
 #ifdef CONFIG_WPS
@@ -405,7 +413,9 @@
 
 	if (wps) {
 		struct wpabuf *wps_ie;
-		wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev,
+		wps_ie = wps_build_probe_req_ie(wps == 2 ? DEV_PW_PUSHBUTTON :
+						DEV_PW_DEFAULT,
+						&wpa_s->wps->dev,
 						wpa_s->wps->uuid, req_type,
 						0, NULL);
 		if (wps_ie) {
@@ -434,8 +444,9 @@
 	struct wpa_supplicant *wpa_s = eloop_ctx;
 	struct wpa_ssid *ssid;
 	int scan_req = 0, ret;
-	struct wpabuf *extra_ie;
+	struct wpabuf *extra_ie = NULL;
 	struct wpa_driver_scan_params params;
+	struct wpa_driver_scan_params *scan_params;
 	size_t max_ssids;
 	enum wpa_states prev_state;
 
@@ -445,14 +456,24 @@
 	}
 
 	if (wpa_s->disconnected && !wpa_s->scan_req) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan");
 		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 		return;
 	}
-
-	if (!wpa_supplicant_enabled_networks(wpa_s->conf) &&
+#ifdef ANDROID
+	if (wpa_s->scanning) {
+		/* If we are already in scanning state, we shall ignore this new scan request*/
+		wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - already scanning");
+		return;
+	}
+#endif
+	if (!wpa_supplicant_enabled_networks(wpa_s) &&
 	    !wpa_s->scan_req) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
 		wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+#ifdef CONFIG_P2P
+		wpa_s->sta_scan_pending = 0;
+#endif /* CONFIG_P2P */
 		return;
 	}
 
@@ -471,15 +492,18 @@
 
 #ifdef CONFIG_P2P
 	if (wpas_p2p_in_progress(wpa_s)) {
-		if (wpa_s->wpa_state == WPA_SCANNING) {
+		if (wpa_s->sta_scan_pending &&
+		    wpas_p2p_in_progress(wpa_s) == 2 &&
+		    wpa_s->global->p2p_cb_on_scan_complete) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "Process pending station "
+				"mode scan during P2P search");
+		} else {
 			wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan "
 				"while P2P operation is in progress");
+			wpa_s->sta_scan_pending = 1;
 			wpa_supplicant_req_scan(wpa_s, 5, 0);
-		} else {
-			wpa_dbg(wpa_s, MSG_DEBUG, "Do not request scan while "
-				"P2P operation is in progress");
+			return;
 		}
-		return;
 	}
 #endif /* CONFIG_P2P */
 
@@ -501,6 +525,14 @@
 	    wpa_s->wpa_state == WPA_INACTIVE)
 		wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
 
+	/*
+	 * If autoscan has set its own scanning parameters
+	 */
+	if (wpa_s->autoscan_params != NULL) {
+		scan_params = wpa_s->autoscan_params;
+		goto scan;
+	}
+
 	if (scan_req != 2 && wpa_s->connect_without_scan) {
 		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 			if (ssid == wpa_s->connect_without_scan)
@@ -515,6 +547,18 @@
 		}
 	}
 
+#ifdef CONFIG_P2P
+	if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
+	    wpa_s->go_params) {
+		wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during "
+			   "P2P group formation");
+		params.ssids[0].ssid = wpa_s->go_params->ssid;
+		params.ssids[0].ssid_len = wpa_s->go_params->ssid_len;
+		params.num_ssids = 1;
+		goto ssid_list_set;
+	}
+#endif /* CONFIG_P2P */
+
 	/* Find the starting point from which to continue scanning */
 	ssid = wpa_s->conf->ssid;
 	if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
@@ -546,7 +590,8 @@
 		if (ssid == NULL && max_ssids > 1)
 			ssid = wpa_s->conf->ssid;
 		while (ssid) {
-			if (!ssid->disabled && ssid->scan_ssid) {
+			if (!wpas_network_disabled(wpa_s, ssid) &&
+			    ssid->scan_ssid) {
 				wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
 						  ssid->ssid, ssid->ssid_len);
 				params.ssids[params.num_ssids].ssid =
@@ -566,7 +611,7 @@
 		}
 
 		for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) {
-			if (tssid->disabled)
+			if (wpas_network_disabled(wpa_s, tssid))
 				continue;
 			if ((params.freqs || !freqs_set) && tssid->scan_freq) {
 				int_array_concat(&params.freqs,
@@ -612,9 +657,17 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
 			"SSID");
 	}
+#ifdef CONFIG_P2P
+ssid_list_set:
+#endif /* CONFIG_P2P */
 
 	wpa_supplicant_optimize_freqs(wpa_s, &params);
-	extra_ie = wpa_supplicant_extra_ies(wpa_s, &params);
+	extra_ie = wpa_supplicant_extra_ies(wpa_s);
+
+#ifdef CONFIG_HS20
+	if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 6) == 0)
+		wpas_hs20_add_indication(extra_ie);
+#endif /* CONFIG_HS20 */
 
 	if (params.freqs == NULL && wpa_s->next_scan_freqs) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
@@ -632,7 +685,8 @@
 	}
 
 #ifdef CONFIG_P2P
-	if (wpa_s->p2p_in_provisioning) {
+	if (wpa_s->p2p_in_provisioning ||
+	    (wpa_s->show_group_started && wpa_s->go_params)) {
 		/*
 		 * The interface may not yet be in P2P mode, so we have to
 		 * explicitly request P2P probe to disable CCK rates.
@@ -641,7 +695,10 @@
 	}
 #endif /* CONFIG_P2P */
 
-	ret = wpa_supplicant_trigger_scan(wpa_s, &params);
+	scan_params = &params;
+
+scan:
+	ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
 
 	wpabuf_free(extra_ie);
 	os_free(params.freqs);
@@ -649,6 +706,12 @@
 
 	if (ret) {
 		wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
+#ifdef ANDROID_P2P
+		/* Restore back the wpa_s->scan_req if we failed the scan because of any reason */
+		wpa_msg(wpa_s, MSG_DEBUG, "Restoring back the wpa_s->scan_req "
+			"to the original value %d", scan_req);
+		wpa_s->scan_req = scan_req;
+#endif
 		if (prev_state != wpa_s->wpa_state)
 			wpa_supplicant_set_state(wpa_s, prev_state);
 		wpa_supplicant_req_scan(wpa_s, 1, 0);
@@ -679,7 +742,8 @@
 		struct wpa_ssid *ssid = wpa_s->conf->ssid;
 
 		while (ssid) {
-			if (!ssid->disabled && ssid->scan_ssid)
+			if (!wpas_network_disabled(wpa_s, ssid) &&
+			    ssid->scan_ssid)
 				break;
 			ssid = ssid->next;
 		}
@@ -730,9 +794,10 @@
 int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_driver_scan_params params;
+	struct wpa_driver_scan_params *scan_params;
 	enum wpa_states prev_state;
-	struct wpa_ssid *ssid;
-	struct wpabuf *wps_ie = NULL;
+	struct wpa_ssid *ssid = NULL;
+	struct wpabuf *extra_ie = NULL;
 	int ret;
 	unsigned int max_sched_scan_ssids;
 	int wildcard = 0;
@@ -745,7 +810,7 @@
 		max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
 	else
 		max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
-	if (max_sched_scan_ssids < 1)
+	if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload)
 		return -1;
 
 	if (wpa_s->sched_scanning) {
@@ -755,11 +820,27 @@
 
 	need_ssids = 0;
 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
-		if (!ssid->disabled && !ssid->scan_ssid) {
+		if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) {
 			/* Use wildcard SSID to find this network */
 			wildcard = 1;
-		} else if (!ssid->disabled && ssid->ssid_len)
+		} else if (!wpas_network_disabled(wpa_s, ssid) &&
+			   ssid->ssid_len)
 			need_ssids++;
+
+#ifdef CONFIG_WPS
+		if (!wpas_network_disabled(wpa_s, ssid) &&
+		    ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
+			/*
+			 * Normal scan is more reliable and faster for WPS
+			 * operations and since these are for short periods of
+			 * time, the benefit of trying to use sched_scan would
+			 * be limited.
+			 */
+			wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
+				"sched_scan for WPS");
+			return -1;
+		}
+#endif /* CONFIG_WPS */
 	}
 	if (wildcard)
 		need_ssids++;
@@ -793,6 +874,11 @@
 		wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
 #endif
 
+	if (wpa_s->autoscan_params != NULL) {
+		scan_params = wpa_s->autoscan_params;
+		goto scan;
+	}
+
 	/* Find the starting point from which to continue scanning */
 	ssid = wpa_s->conf->ssid;
 	if (wpa_s->prev_sched_ssid) {
@@ -808,7 +894,8 @@
 	if (!ssid || !wpa_s->prev_sched_ssid) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
 
-		wpa_s->sched_scan_interval = 10;
+		if (wpa_s->sched_scan_interval == 0)
+			wpa_s->sched_scan_interval = 10;
 		wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
 		wpa_s->first_sched_scan = 1;
 		ssid = wpa_s->conf->ssid;
@@ -821,7 +908,7 @@
 	}
 
 	while (ssid) {
-		if (ssid->disabled)
+		if (wpas_network_disabled(wpa_s, ssid))
 			goto next;
 
 		if (params.num_filter_ssids < wpa_s->max_match_sets &&
@@ -855,6 +942,11 @@
 			params.num_ssids++;
 			if (params.num_ssids >= max_sched_scan_ssids) {
 				wpa_s->prev_sched_ssid = ssid;
+				do {
+					ssid = ssid->next;
+				} while (ssid &&
+					 (wpas_network_disabled(wpa_s, ssid) ||
+					  !ssid->scan_ssid));
 				break;
 			}
 		}
@@ -869,22 +961,28 @@
 		params.filter_ssids = NULL;
 	}
 
-	if (wpa_s->wps)
-		wps_ie = wpa_supplicant_extra_ies(wpa_s, &params);
+	extra_ie = wpa_supplicant_extra_ies(wpa_s);
+	if (extra_ie) {
+		params.extra_ies = wpabuf_head(extra_ie);
+		params.extra_ies_len = wpabuf_len(extra_ie);
+	}
 
+	scan_params = &params;
+
+scan:
 	if (ssid || !wpa_s->first_sched_scan) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
-			"Starting sched scan: interval %d (no timeout)",
-			wpa_s->sched_scan_interval);
-	} else {
-		wpa_dbg(wpa_s, MSG_DEBUG,
 			"Starting sched scan: interval %d timeout %d",
 			wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Starting sched scan: interval %d (no timeout)",
+			wpa_s->sched_scan_interval);
 	}
 
-	ret = wpa_supplicant_start_sched_scan(wpa_s, &params,
+	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
 					      wpa_s->sched_scan_interval);
-	wpabuf_free(wps_ie);
+	wpabuf_free(extra_ie);
 	os_free(params.filter_ssids);
 	if (ret) {
 		wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
@@ -1220,6 +1318,7 @@
 
 	for (i = 0; i < scan_res->num; i++) {
 		struct wpa_scan_res *r = scan_res->res[i];
+		u8 *pos;
 		if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID))
 		    == WPA_SCAN_LEVEL_DBM) {
 			int snr = r->level - r->noise;
@@ -1234,11 +1333,62 @@
 				   MAC2STR(r->bssid), r->freq, r->qual,
 				   r->noise, r->level, r->flags);
 		}
+		pos = (u8 *) (r + 1);
+		if (r->ie_len)
+			wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len);
+		pos += r->ie_len;
+		if (r->beacon_ie_len)
+			wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs",
+				    pos, r->beacon_ie_len);
 	}
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 }
 
 
+int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
+				      const u8 *bssid)
+{
+	size_t i;
+
+	if (wpa_s->bssid_filter == NULL)
+		return 1;
+
+	for (i = 0; i < wpa_s->bssid_filter_count; i++) {
+		if (os_memcmp(wpa_s->bssid_filter + i * ETH_ALEN, bssid,
+			      ETH_ALEN) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void filter_scan_res(struct wpa_supplicant *wpa_s,
+			    struct wpa_scan_results *res)
+{
+	size_t i, j;
+
+	if (wpa_s->bssid_filter == NULL)
+		return;
+
+	for (i = 0, j = 0; i < res->num; i++) {
+		if (wpa_supplicant_filter_bssid_match(wpa_s,
+						      res->res[i]->bssid)) {
+			res->res[j++] = res->res[i];
+		} else {
+			os_free(res->res[i]);
+			res->res[i] = NULL;
+		}
+	}
+
+	if (res->num != j) {
+		wpa_printf(MSG_DEBUG, "Filtered out %d scan results",
+			   (int) (res->num - j));
+		res->num = j;
+	}
+}
+
+
 /**
  * wpa_supplicant_get_scan_results - Get scan results
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1263,6 +1413,7 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
 		return NULL;
 	}
+	filter_scan_res(wpa_s, scan_res);
 
 #ifdef CONFIG_WPS
 	if (wpas_wps_in_progress(wpa_s)) {
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index b794105..b0ddf97 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -9,7 +9,7 @@
 #ifndef SCAN_H
 #define SCAN_H
 
-int wpa_supplicant_enabled_networks(struct wpa_config *conf);
+int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
 int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
 				      int sec, int usec);
@@ -32,5 +32,7 @@
 					     u32 vendor_type);
 struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon(
 	const struct wpa_scan_res *res, u32 vendor_type);
+int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
+				      const u8 *bssid);
 
 #endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 690f395..b09e5f1 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -26,12 +26,14 @@
 #include "bss.h"
 #include "scan.h"
 #include "sme.h"
+#include "hs20_supplicant.h"
 
 #define SME_AUTH_TIMEOUT 5
 #define SME_ASSOC_TIMEOUT 5
 
 static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx);
 static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx);
+static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx);
 #ifdef CONFIG_IEEE80211W
 static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
 #endif /* CONFIG_IEEE80211W */
@@ -125,6 +127,15 @@
 				"key management and encryption suites");
 			return;
 		}
+	} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
+		   wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
+		/*
+		 * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
+		 * use non-WPA since the scan results did not indicate that the
+		 * AP is using WPA or WPA2.
+		 */
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		wpa_s->sme.assoc_req_ie_len = 0;
 	} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
 		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
 		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
@@ -221,6 +232,21 @@
 	}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_HS20
+	if (wpa_s->conf->hs20) {
+		struct wpabuf *hs20;
+		hs20 = wpabuf_alloc(20);
+		if (hs20) {
+			wpas_hs20_add_indication(hs20);
+			os_memcpy(wpa_s->sme.assoc_req_ie +
+				  wpa_s->sme.assoc_req_ie_len,
+				  wpabuf_head(hs20), wpabuf_len(hs20));
+			wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
+			wpabuf_free(hs20);
+		}
+	}
+#endif /* CONFIG_HS20 */
+
 #ifdef CONFIG_INTERWORKING
 	if (wpa_s->conf->interworking) {
 		u8 *pos = wpa_s->sme.assoc_req_ie;
@@ -315,6 +341,7 @@
 		    wpa_s->sme.auth_alg == data->auth.auth_type ||
 		    wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) {
 			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 			return;
 		}
 
@@ -371,6 +398,8 @@
 	params.ssid = wpa_s->sme.ssid;
 	params.ssid_len = wpa_s->sme.ssid_len;
 	params.freq = wpa_s->sme.freq;
+	params.bg_scan_period = wpa_s->current_ssid ?
+		wpa_s->current_ssid->bg_scan_period : -1;
 	params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
 		wpa_s->sme.assoc_req_ie : NULL;
 	params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
@@ -429,6 +458,7 @@
 		wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the "
 			"driver failed");
 		wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 		os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 		return;
 	}
@@ -604,6 +634,276 @@
 
 	eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
 	eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+	eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
+}
+
+
+static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s,
+				   const u8 *chan_list, u8 num_channels,
+				   u8 num_intol)
+{
+	struct ieee80211_2040_bss_coex_ie *bc_ie;
+	struct ieee80211_2040_intol_chan_report *ic_report;
+	struct wpabuf *buf;
+
+	wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR,
+		   MAC2STR(wpa_s->bssid));
+
+	buf = wpabuf_alloc(2 + /* action.category + action_code */
+			   sizeof(struct ieee80211_2040_bss_coex_ie) +
+			   sizeof(struct ieee80211_2040_intol_chan_report) +
+			   num_channels);
+	if (buf == NULL)
+		return;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, WLAN_PA_20_40_BSS_COEX);
+
+	bc_ie = wpabuf_put(buf, sizeof(*bc_ie));
+	bc_ie->element_id = WLAN_EID_20_40_BSS_COEXISTENCE;
+	bc_ie->length = 1;
+	if (num_intol)
+		bc_ie->coex_param |= WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ;
+
+	if (num_channels > 0) {
+		ic_report = wpabuf_put(buf, sizeof(*ic_report));
+		ic_report->element_id = WLAN_EID_20_40_BSS_INTOLERANT;
+		ic_report->length = num_channels + 1;
+		ic_report->op_class = 0;
+		os_memcpy(wpabuf_put(buf, num_channels), chan_list,
+			  num_channels);
+	}
+
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"SME: Failed to send 20/40 BSS Coexistence frame");
+	}
+
+	wpabuf_free(buf);
+}
+
+
+/**
+ * enum wpas_band - Frequency band
+ * @WPAS_BAND_2GHZ: 2.4 GHz ISM band
+ * @WPAS_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
+ */
+enum wpas_band {
+	WPAS_BAND_2GHZ,
+	WPAS_BAND_5GHZ,
+	WPAS_BAND_INVALID
+};
+
+/**
+ * freq_to_channel - Convert frequency into channel info
+ * @channel: Buffer for returning channel number
+ * Returns: Band (2 or 5 GHz)
+ */
+static enum wpas_band freq_to_channel(int freq, u8 *channel)
+{
+	enum wpas_band band = (freq <= 2484) ? WPAS_BAND_2GHZ : WPAS_BAND_5GHZ;
+	u8 chan = 0;
+
+	if (freq >= 2412 && freq <= 2472)
+		chan = (freq - 2407) / 5;
+	else if (freq == 2484)
+		chan = 14;
+	else if (freq >= 5180 && freq <= 5805)
+		chan = (freq - 5000) / 5;
+
+	*channel = chan;
+	return band;
+}
+
+
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+	const u8 *ie;
+	u16 ht_cap;
+	u8 chan_list[P2P_MAX_CHANNELS], channel;
+	u8 num_channels = 0, num_intol = 0, i;
+
+	if (!wpa_s->sme.sched_obss_scan)
+		return 0;
+
+	wpa_s->sme.sched_obss_scan = 0;
+	if (!wpa_s->current_bss || wpa_s->wpa_state != WPA_COMPLETED)
+		return 1;
+
+	/*
+	 * Check whether AP uses regulatory triplet or channel triplet in
+	 * country info. Right now the operating class of the BSS channel
+	 * width trigger event is "unknown" (IEEE Std 802.11-2012 10.15.12),
+	 * based on the assumption that operating class triplet is not used in
+	 * beacon frame. If the First Channel Number/Operating Extension
+	 * Identifier octet has a positive integer value of 201 or greater,
+	 * then its operating class triplet.
+	 *
+	 * TODO: If Supported Operating Classes element is present in beacon
+	 * frame, have to lookup operating class in Annex E and fill them in
+	 * 2040 coex frame.
+	 */
+	ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY);
+	if (ie && (ie[1] >= 6) && (ie[5] >= 201))
+		return 1;
+
+	os_memset(chan_list, 0, sizeof(chan_list));
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		/* Skip other band bss */
+		if (freq_to_channel(bss->freq, &channel) != WPAS_BAND_2GHZ)
+			continue;
+
+		ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP);
+		ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0;
+
+		if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) {
+			/* Check whether the channel is already considered */
+			for (i = 0; i < num_channels; i++) {
+				if (channel == chan_list[i])
+					break;
+			}
+			if (i != num_channels)
+				continue;
+
+			if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
+				num_intol++;
+
+			chan_list[num_channels++] = channel;
+		}
+	}
+
+	sme_send_2040_bss_coex(wpa_s, chan_list, num_channels, num_intol);
+	return 1;
+}
+
+
+static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+					  u16 num_modes,
+					  enum hostapd_hw_mode mode)
+{
+	u16 i;
+
+	for (i = 0; i < num_modes; i++) {
+		if (modes[i].mode == mode)
+			return &modes[i];
+	}
+
+	return NULL;
+}
+
+
+static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s,
+					enum hostapd_hw_mode band,
+					struct wpa_driver_scan_params *params)
+{
+	/* Include only supported channels for the specified band */
+	struct hostapd_hw_modes *mode;
+	int count, i;
+
+	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band);
+	if (mode == NULL) {
+		/* No channels supported in this band - use empty list */
+		params->freqs = os_zalloc(sizeof(int));
+		return;
+	}
+
+	params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+	if (params->freqs == NULL)
+		return;
+	for (count = 0, i = 0; i < mode->num_channels; i++) {
+		if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		params->freqs[count++] = mode->channels[i].freq;
+	}
+}
+
+
+static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpa_driver_scan_params params;
+
+	if (!wpa_s->current_bss) {
+		wpa_printf(MSG_DEBUG, "SME OBSS: Ignore scan request");
+		return;
+	}
+
+	os_memset(&params, 0, sizeof(params));
+	wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, &params);
+	wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan");
+
+	if (wpa_supplicant_trigger_scan(wpa_s, &params))
+		wpa_printf(MSG_DEBUG, "SME OBSS: Failed to trigger scan");
+	else
+		wpa_s->sme.sched_obss_scan = 1;
+	os_free(params.freqs);
+
+	eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
+			       sme_obss_scan_timeout, wpa_s, NULL);
+}
+
+
+void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable)
+{
+	const u8 *ie;
+	struct wpa_bss *bss = wpa_s->current_bss;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	struct hostapd_hw_modes *hw_mode = NULL;
+	int i;
+
+	eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
+	wpa_s->sme.sched_obss_scan = 0;
+	if (!enable)
+		return;
+
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || ssid == NULL ||
+	    ssid->mode != IEEE80211_MODE_INFRA)
+		return; /* Not using station SME in wpa_supplicant */
+
+	if (!wpa_s->hw.modes)
+		return;
+
+	/* only HT caps in 11g mode are relevant */
+	for (i = 0; i < wpa_s->hw.num_modes; i++) {
+		hw_mode = &wpa_s->hw.modes[i];
+		if (hw_mode->mode == HOSTAPD_MODE_IEEE80211G)
+			break;
+	}
+
+	/* Driver does not support HT40 for 11g or doesn't have 11g. */
+	if (i == wpa_s->hw.num_modes || !hw_mode ||
+	    !(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+		return;
+
+	if (bss == NULL || bss->freq < 2400 || bss->freq > 2500)
+		return; /* Not associated on 2.4 GHz band */
+
+	/* Check whether AP supports HT40 */
+	ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_CAP);
+	if (!ie || ie[1] < 2 ||
+	    !(WPA_GET_LE16(ie + 2) & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+		return; /* AP does not support HT40 */
+
+	ie = wpa_bss_get_ie(wpa_s->current_bss,
+			    WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS);
+	if (!ie || ie[1] < 14)
+		return; /* AP does not request OBSS scans */
+
+	wpa_s->sme.obss_scan_int = WPA_GET_LE16(ie + 6);
+	if (wpa_s->sme.obss_scan_int < 10) {
+		wpa_printf(MSG_DEBUG, "SME: Invalid OBSS Scan Interval %u "
+			   "replaced with the minimum 10 sec",
+			   wpa_s->sme.obss_scan_int);
+		wpa_s->sme.obss_scan_int = 10;
+	}
+	wpa_printf(MSG_DEBUG, "SME: OBSS Scan Interval %u sec",
+		   wpa_s->sme.obss_scan_int);
+	eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
+			       sme_obss_scan_timeout, wpa_s, NULL);
 }
 
 
@@ -660,9 +960,9 @@
 	    sme_check_sa_query_timeout(wpa_s))
 		return;
 
-	nbuf = os_realloc(wpa_s->sme.sa_query_trans_id,
-			  (wpa_s->sme.sa_query_count + 1) *
-			  WLAN_SA_QUERY_TR_ID_LEN);
+	nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id,
+				wpa_s->sme.sa_query_count + 1,
+				WLAN_SA_QUERY_TR_ID_LEN);
 	if (nbuf == NULL)
 		return;
 	if (wpa_s->sme.sa_query_count == 0) {
diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h
index 33530bb..a7cc507 100644
--- a/wpa_supplicant/sme.h
+++ b/wpa_supplicant/sme.h
@@ -35,6 +35,9 @@
 				       const u8 *prev_pending_bssid);
 void sme_deinit(struct wpa_supplicant *wpa_s);
 
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s);
+void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable);
+
 #else /* CONFIG_SME */
 
 static inline void sme_authenticate(struct wpa_supplicant *wpa_s,
@@ -95,6 +98,16 @@
 {
 }
 
+static inline int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
+static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s,
+				       int enable)
+{
+}
+
 #endif /* CONFIG_SME */
 
 #endif /* SME_H */
diff --git a/wpa_supplicant/tests/test_eap_sim_common.c b/wpa_supplicant/tests/test_eap_sim_common.c
index 4448858..f60b182 100644
--- a/wpa_supplicant/tests/test_eap_sim_common.c
+++ b/wpa_supplicant/tests/test_eap_sim_common.c
@@ -28,7 +28,7 @@
 
 	printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n");
 	eap_sim_prf(xkey, buf, sizeof(buf));
-	if (memcmp(w, buf, sizeof(w) != 0)) {
+	if (memcmp(w, buf, sizeof(w)) != 0) {
 		printf("eap_sim_prf failed\n");
 		return 1;
 	}
diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c
new file mode 100644
index 0000000..92ca536
--- /dev/null
+++ b/wpa_supplicant/wifi_display.c
@@ -0,0 +1,251 @@
+/*
+ * wpa_supplicant - Wi-Fi Display
+ * Copyright (c) 2011, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "p2p/p2p.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa_supplicant_i.h"
+#include "wifi_display.h"
+
+
+int wifi_display_init(struct wpa_global *global)
+{
+	global->wifi_display = 1;
+	return 0;
+}
+
+
+void wifi_display_deinit(struct wpa_global *global)
+{
+	int i;
+	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
+		wpabuf_free(global->wfd_subelem[i]);
+		global->wfd_subelem[i] = NULL;
+	}
+}
+
+
+static int wifi_display_update_wfd_ie(struct wpa_global *global)
+{
+	struct wpabuf *ie, *buf;
+	size_t len, plen;
+
+	wpa_printf(MSG_DEBUG, "WFD: Update WFD IE");
+
+	if (!global->wifi_display) {
+		wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not "
+			   "include WFD IE");
+		p2p_set_wfd_ie_beacon(global->p2p, NULL);
+		p2p_set_wfd_ie_probe_req(global->p2p, NULL);
+		p2p_set_wfd_ie_probe_resp(global->p2p, NULL);
+		p2p_set_wfd_ie_assoc_req(global->p2p, NULL);
+		p2p_set_wfd_ie_invitation(global->p2p, NULL);
+		p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL);
+		p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
+		p2p_set_wfd_ie_go_neg(global->p2p, NULL);
+		p2p_set_wfd_dev_info(global->p2p, NULL);
+		p2p_set_wfd_assoc_bssid(global->p2p, NULL);
+		p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
+		return 0;
+	}
+
+	p2p_set_wfd_dev_info(global->p2p,
+			     global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+	p2p_set_wfd_assoc_bssid(
+		global->p2p,
+		global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
+	p2p_set_wfd_coupled_sink_info(
+		global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
+
+	/*
+	 * WFD IE is included in number of management frames. Two different
+	 * sets of subelements are included depending on the frame:
+	 *
+	 * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf,
+	 * Provision Discovery Req:
+	 * WFD Device Info
+	 * [Associated BSSID]
+	 * [Coupled Sink Info]
+	 *
+	 * Probe Request:
+	 * WFD Device Info
+	 * [Associated BSSID]
+	 * [Coupled Sink Info]
+	 * [WFD Extended Capability]
+	 *
+	 * Probe Response:
+	 * WFD Device Info
+	 * [Associated BSSID]
+	 * [Coupled Sink Info]
+	 * [WFD Extended Capability]
+	 * [WFD Session Info]
+	 *
+	 * (Re)Association Response, P2P Invitation Req/Resp,
+	 * Provision Discovery Resp:
+	 * WFD Device Info
+	 * [Associated BSSID]
+	 * [Coupled Sink Info]
+	 * [WFD Session Info]
+	 */
+	len = 0;
+	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
+		len += wpabuf_len(global->wfd_subelem[
+					  WFD_SUBELEM_DEVICE_INFO]);
+	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
+		len += wpabuf_len(global->wfd_subelem[
+					  WFD_SUBELEM_ASSOCIATED_BSSID]);
+	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
+		len += wpabuf_len(global->wfd_subelem[
+					  WFD_SUBELEM_COUPLED_SINK]);
+	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
+		len += wpabuf_len(global->wfd_subelem[
+					  WFD_SUBELEM_SESSION_INFO]);
+	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
+		len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
+	buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
+		wpabuf_put_buf(buf, global->wfd_subelem[
+				       WFD_SUBELEM_ASSOCIATED_BSSID]);
+	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie);
+	p2p_set_wfd_ie_beacon(global->p2p, ie);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request",
+			ie);
+	p2p_set_wfd_ie_assoc_req(global->p2p, ie);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie);
+	p2p_set_wfd_ie_go_neg(global->p2p, ie);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
+			"Request", ie);
+	p2p_set_wfd_ie_prov_disc_req(global->p2p, ie);
+
+	plen = buf->used;
+	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie);
+	p2p_set_wfd_ie_probe_req(global->p2p, ie);
+
+	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie);
+	p2p_set_wfd_ie_probe_resp(global->p2p, ie);
+
+	/* Remove WFD Extended Capability from buffer */
+	buf->used = plen;
+	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
+		wpabuf_put_buf(buf,
+			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie);
+	p2p_set_wfd_ie_invitation(global->p2p, ie);
+
+	ie = wifi_display_encaps(buf);
+	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
+			"Response", ie);
+	p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie);
+
+	wpabuf_free(buf);
+
+	return 0;
+}
+
+
+void wifi_display_enable(struct wpa_global *global, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s",
+		   enabled ? "enabled" : "disabled");
+	global->wifi_display = enabled;
+	wifi_display_update_wfd_ie(global);
+}
+
+
+int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
+{
+	char *pos;
+	int subelem;
+	size_t len;
+	struct wpabuf *e;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+	subelem = atoi(cmd);
+	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
+		return -1;
+
+	len = os_strlen(pos);
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	if (len == 0) {
+		/* Clear subelement */
+		e = NULL;
+		wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem);
+	} else {
+		e = wpabuf_alloc(1 + len);
+		if (e == NULL)
+			return -1;
+		wpabuf_put_u8(e, subelem);
+		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
+			wpabuf_free(e);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem);
+	}
+
+	wpabuf_free(global->wfd_subelem[subelem]);
+	global->wfd_subelem[subelem] = e;
+	wifi_display_update_wfd_ie(global);
+
+	return 0;
+}
+
+
+int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
+			     char *buf, size_t buflen)
+{
+	int subelem;
+
+	subelem = atoi(cmd);
+	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
+		return -1;
+
+	if (global->wfd_subelem[subelem] == NULL)
+		return 0;
+
+	return wpa_snprintf_hex(buf, buflen,
+				wpabuf_head_u8(global->wfd_subelem[subelem]) +
+				1,
+				wpabuf_len(global->wfd_subelem[subelem]) - 1);
+}
diff --git a/wpa_supplicant/wifi_display.h b/wpa_supplicant/wifi_display.h
new file mode 100644
index 0000000..b75d4f2
--- /dev/null
+++ b/wpa_supplicant/wifi_display.h
@@ -0,0 +1,20 @@
+/*
+ * wpa_supplicant - Wi-Fi Display
+ * Copyright (c) 2011, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WIFI_DISPLAY_H
+#define WIFI_DISPLAY_H
+
+int wifi_display_init(struct wpa_global *global);
+void wifi_display_deinit(struct wpa_global *global);
+void wifi_display_enable(struct wpa_global *global, int enabled);
+int wifi_display_subelem_set(struct wpa_global *global, char *cmd);
+int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
+			     char *buf, size_t buflen);
+
+#endif /* WIFI_DISPLAY_H */
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
new file mode 100644
index 0000000..98ce966
--- /dev/null
+++ b/wpa_supplicant/wnm_sta.c
@@ -0,0 +1,248 @@
+/*
+ * wpa_supplicant - WNM
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "rsn_supp/wpa.h"
+#include "../wpa_supplicant/wpa_supplicant_i.h"
+#include "../wpa_supplicant/driver_i.h"
+
+#define MAX_TFS_IE_LEN  1024
+
+#ifdef CONFIG_IEEE80211V
+
+/* get the TFS IE from driver */
+static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
+				   u16 *buf_len, enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
+
+	return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
+}
+
+
+/* set the TFS IE to driver */
+static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
+				   const u8 *addr, u8 *buf, u16 *buf_len,
+				   enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
+
+	return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len);
+}
+
+
+/* MLME-SLEEPMODE.request */
+int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
+				 u8 action, u8 intval)
+{
+	struct ieee80211_mgmt *mgmt;
+	int res;
+	size_t len;
+	struct wnm_sleep_element *wnmsleep_ie;
+	u8 *wnmtfs_ie;
+	u8 wnmsleep_ie_len;
+	u16 wnmtfs_ie_len;  /* possibly multiple IE(s) */
+	enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
+		WNM_SLEEP_TFS_REQ_IE_NONE;
+
+	/* WNM-Sleep Mode IE */
+	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
+	wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
+	if (wnmsleep_ie == NULL)
+		return -1;
+	wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
+	wnmsleep_ie->len = wnmsleep_ie_len - 2;
+	wnmsleep_ie->action_type = action;
+	wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
+	wnmsleep_ie->intval = intval;
+
+	/* TFS IE(s) */
+	wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
+	if (wnmtfs_ie == NULL) {
+		os_free(wnmsleep_ie);
+		return -1;
+	}
+	if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
+				    tfs_oper)) {
+		wnmtfs_ie_len = 0;
+		os_free(wnmtfs_ie);
+		wnmtfs_ie = NULL;
+	}
+
+	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
+	if (mgmt == NULL) {
+		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+			   "WNM-Sleep Request action frame");
+		return -1;
+	}
+
+	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
+	os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
+		  wnmsleep_ie_len);
+	/* copy TFS IE here */
+	if (wnmtfs_ie_len > 0) {
+		os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
+			  wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
+	}
+
+	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
+		wnmtfs_ie_len;
+
+	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  &mgmt->u.action.category, len, 0);
+	if (res < 0)
+		wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
+			   "(action=%d, intval=%d)", action, intval);
+
+	os_free(wnmsleep_ie);
+	os_free(wnmtfs_ie);
+	os_free(mgmt);
+
+	return res;
+}
+
+
+static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
+					const u8 *frm, int len)
+{
+	/*
+	 * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data |
+	 * WNM-Sleep Mode IE | TFS Response IE
+	 */
+	u8 *pos = (u8 *) frm; /* point to action field */
+	u16 key_len_total = le_to_host16(*((u16 *)(frm+2)));
+	u8 gtk_len;
+#ifdef CONFIG_IEEE80211W
+	u8 igtk_len;
+#endif /* CONFIG_IEEE80211W */
+	struct wnm_sleep_element *wnmsleep_ie = NULL;
+	/* multiple TFS Resp IE (assuming consecutive) */
+	u8 *tfsresp_ie_start = NULL;
+	u8 *tfsresp_ie_end = NULL;
+	u16 tfsresp_ie_len = 0;
+
+	wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d",
+		   frm[0], frm[1], key_len_total);
+	pos += 4 + key_len_total;
+	while (pos - frm < len) {
+		u8 ie_len = *(pos + 1);
+		if (*pos == WLAN_EID_WNMSLEEP)
+			wnmsleep_ie = (struct wnm_sleep_element *) pos;
+		else if (*pos == WLAN_EID_TFS_RESP) {
+			if (!tfsresp_ie_start)
+				tfsresp_ie_start = pos;
+			tfsresp_ie_end = pos;
+		} else
+			wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
+		pos += ie_len + 2;
+	}
+
+	if (!wnmsleep_ie) {
+		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
+		return;
+	}
+
+	if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT) {
+		wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
+			   "frame (action=%d, intval=%d)",
+			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
+		if (wnmsleep_ie->action_type == 0) {
+			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
+					 wpa_s->bssid, NULL, NULL);
+			/* remove GTK/IGTK ?? */
+
+			/* set the TFS Resp IE(s) */
+			if (tfsresp_ie_start && tfsresp_ie_end &&
+			    tfsresp_ie_end - tfsresp_ie_start >= 0) {
+				tfsresp_ie_len = (tfsresp_ie_end +
+						  tfsresp_ie_end[1] + 2) -
+					tfsresp_ie_start;
+				wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
+				/*
+				 * pass the TFS Resp IE(s) to driver for
+				 * processing
+				 */
+				if (ieee80211_11_set_tfs_ie(
+					    wpa_s, wpa_s->bssid,
+					    tfsresp_ie_start,
+					    &tfsresp_ie_len,
+					    WNM_SLEEP_TFS_RESP_IE_SET))
+					wpa_printf(MSG_DEBUG, "Fail to set "
+						   "TFS Resp IE");
+			}
+		} else if (wnmsleep_ie->action_type == 1) {
+			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM,
+					 wpa_s->bssid, NULL, NULL);
+			/* Install GTK/IGTK */
+			do {
+				/* point to key data field */
+				u8 *ptr = (u8 *) frm + 1 + 1 + 2;
+				while (ptr < (u8 *) frm + 4 + key_len_total) {
+					if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
+						gtk_len = *(ptr + 4);
+						wpa_wnmsleep_install_key(
+							wpa_s->wpa,
+							WNM_SLEEP_SUBELEM_GTK,
+							ptr);
+						ptr += 13 + gtk_len;
+#ifdef CONFIG_IEEE80211W
+					} else if (*ptr ==
+						   WNM_SLEEP_SUBELEM_IGTK) {
+						igtk_len = WPA_IGTK_LEN;
+						wpa_wnmsleep_install_key(
+							wpa_s->wpa,
+							WNM_SLEEP_SUBELEM_IGTK,
+							ptr);
+						ptr += 10 + WPA_IGTK_LEN;
+#endif /* CONFIG_IEEE80211W */
+					} else
+						break; /* skip the loop */
+				}
+			} while(0);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
+			   "(action=%d, intval=%d)",
+			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
+		if (wnmsleep_ie->action_type == 0)
+			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
+					 wpa_s->bssid, NULL, NULL);
+		else if (wnmsleep_ie->action_type == 1)
+			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
+					 wpa_s->bssid, NULL, NULL);
+	}
+}
+
+
+void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
+			      struct rx_action *action)
+{
+	u8 *pos = (u8 *) action->data; /* point to action field */
+	u8 act = *pos++;
+	/* u8 dialog_token = *pos++; */
+
+	switch (act) {
+	case WNM_SLEEP_MODE_RESP:
+		ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len);
+		break;
+	default:
+		break;
+	}
+}
+
+#endif /* CONFIG_IEEE80211V */
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
new file mode 100644
index 0000000..ba2535b
--- /dev/null
+++ b/wpa_supplicant/wnm_sta.h
@@ -0,0 +1,21 @@
+/*
+ * IEEE 802.11v WNM related functions and structures
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WNM_STA_H
+#define WNM_STA_H
+
+struct rx_action;
+struct wpa_supplicant;
+
+int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
+				 u8 action, u8 intval);
+
+void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
+			      struct rx_action *action);
+
+#endif /* WNM_STA_H */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 4685970..3986b9b 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - command line interface for wpa_supplicant daemon
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -20,6 +20,7 @@
 #include "utils/edit.h"
 #include "utils/list.h"
 #include "common/version.h"
+#include "common/ieee802_11_defs.h"
 #ifdef ANDROID
 #include <cutils/properties.h>
 #endif /* ANDROID */
@@ -94,8 +95,11 @@
 static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */
 
 
-static void print_help(void);
+static void print_help(const char *cmd);
 static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx);
+static void wpa_cli_close_connection(void);
+static char * wpa_cli_get_default_ifname(void);
+static char ** wpa_list_cmd_list(void);
 
 
 static void usage(void)
@@ -112,7 +116,7 @@
 	       "  -B = run a daemon in the background\n"
 	       "  default path: " CONFIG_CTRL_IFACE_DIR "\n"
 	       "  default interface: first interface found in socket path\n");
-	print_help();
+	print_help(NULL);
 }
 
 
@@ -240,7 +244,7 @@
 	char **res;
 	struct cli_txt_entry *e;
 
-	res = os_zalloc((count + 1) * sizeof(char *));
+	res = os_calloc(count + 1, sizeof(char *));
 	if (res == NULL)
 		return NULL;
 
@@ -363,6 +367,7 @@
 		} else {
 			printf("Warning: Failed to attach to "
 			       "wpa_supplicant.\n");
+			wpa_cli_close_connection();
 			return -1;
 		}
 	}
@@ -455,6 +460,58 @@
 }
 
 
+static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc,
+		     char *argv[])
+{
+	int i, res;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	res = os_snprintf(pos, end - pos, "%s", cmd);
+	if (res < 0 || res >= end - pos)
+		goto fail;
+	pos += res;
+
+	for (i = 0; i < argc; i++) {
+		res = os_snprintf(pos, end - pos, " %s", argv[i]);
+		if (res < 0 || res >= end - pos)
+			goto fail;
+		pos += res;
+	}
+
+	buf[buflen - 1] = '\0';
+	return 0;
+
+fail:
+	printf("Too long command\n");
+	return -1;
+}
+
+
+static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args,
+		       int argc, char *argv[])
+{
+	char buf[256];
+	if (argc < min_args) {
+		printf("Invalid %s command - at least %d argument%s "
+		       "required.\n", cmd, min_args,
+		       min_args > 1 ? "s are" : " is");
+		return -1;
+	}
+	if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0)
+		return -1;
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int wpa_cli_cmd_ifname(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "IFNAME");
+}
+
+
 static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	if (argc > 0 && os_strcmp(argv[0], "verbose") == 0)
@@ -479,14 +536,7 @@
 
 static int wpa_cli_cmd_note(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
-	int ret;
-	if (argc == 0)
-		return -1;
-	ret = os_snprintf(cmd, sizeof(cmd), "NOTE %s", argv[0]);
-	if (ret < 0 || (size_t) ret >= sizeof(cmd))
-		return -1;
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "NOTE", 1, argc, argv);
 }
 
 
@@ -504,11 +554,26 @@
 
 static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	print_help();
+	print_help(argc > 0 ? argv[0] : NULL);
 	return 0;
 }
 
 
+static char ** wpa_cli_complete_help(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = wpa_list_cmd_list();
+		break;
+	}
+
+	return res;
+}
+
+
 static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license);
@@ -576,21 +641,7 @@
 
 static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid GET command: need one argument (variable "
-		       "name)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long GET command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "GET", 1, argc, argv);
 }
 
 
@@ -616,97 +667,48 @@
 static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc,
 				       char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid PREAUTH command: needs one argument "
-		       "(BSSID)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "PREAUTH %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long PREAUTH command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "PREAUTH", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid AP_SCAN command: needs one argument (ap_scan "
-		       "value)\n");
-		return -1;
-	}
-	res = os_snprintf(cmd, sizeof(cmd), "AP_SCAN %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long AP_SCAN command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "AP_SCAN", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_scan_interval(struct wpa_ctrl *ctrl, int argc,
 				     char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid SCAN_INTERVAL command: needs one argument "
-		       "scan_interval value)\n");
-		return -1;
-	}
-	res = os_snprintf(cmd, sizeof(cmd), "SCAN_INTERVAL %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long SCAN_INTERVAL command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "SCAN_INTERVAL", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid BSS_EXPIRE_AGE command: needs one argument "
-		       "(bss_expire_age value)\n");
-		return -1;
-	}
-	res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_AGE %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long BSS_EXPIRE_AGE command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "BSS_EXPIRE_AGE", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc,
 				        char *argv[])
 {
+	return wpa_cli_cmd(ctrl, "BSS_EXPIRE_COUNT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
 	char cmd[256];
 	int res;
 
-	if (argc != 1) {
-		printf("Invalid BSS_EXPIRE_COUNT command: needs one argument "
-		       "(bss_expire_count value)\n");
-		return -1;
-	}
-	res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_COUNT %s", argv[0]);
+	if (argc < 1)
+		res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH 0");
+	else
+		res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH %s", argv[0]);
 	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long BSS_EXPIRE_COUNT command.\n");
+		printf("Too long BSS_FLUSH command.\n");
 		return -1;
 	}
 	return wpa_ctrl_command(ctrl, cmd);
@@ -716,69 +718,24 @@
 static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc,
 				char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid STKSTART command: needs one argument "
-		       "(Peer STA MAC address)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "STKSTART %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long STKSTART command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "STKSTART", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid FT_DS command: needs one argument "
-		       "(Target AP MAC address)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "FT_DS %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long FT_DS command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc == 0) {
-		/* Any BSSID */
-		return wpa_ctrl_command(ctrl, "WPS_PBC");
-	}
-
-	/* Specific BSSID */
-	res = os_snprintf(cmd, sizeof(cmd), "WPS_PBC %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long WPS_PBC command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "WPS_PBC", 0, argc, argv);
 }
 
 
 static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
-	int res;
-
 	if (argc == 0) {
 		printf("Invalid WPS_PIN command: need one or two arguments:\n"
 		       "- BSSID: use 'any' to select any\n"
@@ -787,49 +744,14 @@
 		return -1;
 	}
 
-	if (argc == 1) {
-		/* Use dynamically generated PIN (returned as reply) */
-		res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s", argv[0]);
-		if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-			printf("Too long WPS_PIN command.\n");
-			return -1;
-		}
-		return wpa_ctrl_command(ctrl, cmd);
-	}
-
-	/* Use hardcoded PIN from a label */
-	res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long WPS_PIN command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "WPS_PIN", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
 				     char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1 && argc != 2) {
-		printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
-		       "- PIN to be verified\n");
-		return -1;
-	}
-
-	if (argc == 2)
-		res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
-				  argv[0], argv[1]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
-				  argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long WPS_CHECK_PIN command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "WPS_CHECK_PIN", 1, argc, argv);
 }
 
 
@@ -843,9 +765,6 @@
 #ifdef CONFIG_WPS_OOB
 static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
-	int res;
-
 	if (argc != 3 && argc != 4) {
 		printf("Invalid WPS_OOB command: need three or four "
 		       "arguments:\n"
@@ -858,21 +777,54 @@
 		return -1;
 	}
 
-	if (argc == 3)
-		res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
-				  argv[0], argv[1], argv[2]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s",
-				  argv[0], argv[1], argv[2], argv[3]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long WPS_OOB command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "WPS_OOB", 3, argc, argv);
 }
 #endif /* CONFIG_WPS_OOB */
 
 
+#ifdef CONFIG_WPS_NFC
+
+static int wpa_cli_cmd_wps_nfc(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WPS_NFC", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WPS_NFC_TOKEN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	int ret;
+	char *buf;
+	size_t buflen;
+
+	if (argc != 1) {
+		printf("Invalid 'wps_nfc_tag_read' command - one argument "
+		       "is required.\n");
+		return -1;
+	}
+
+	buflen = 18 + os_strlen(argv[0]);
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return -1;
+	os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
+
+	ret = wpa_ctrl_command(ctrl, buf);
+	os_free(buf);
+
+	return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
 static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	char cmd[256];
@@ -933,41 +885,14 @@
 static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
 				  char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc < 1) {
-		printf("Invalid WPS_AP_PIN command: needs at least one "
-		       "argument\n");
-		return -1;
-	}
-
-	if (argc > 2)
-		res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s %s",
-				  argv[0], argv[1], argv[2]);
-	else if (argc > 1)
-		res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s",
-				  argv[0], argv[1]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s",
-				  argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long WPS_AP_PIN command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "WPS_AP_PIN", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc,
 				    char *argv[])
 {
-	char cmd[100];
-	if (argc > 0) {
-		os_snprintf(cmd, sizeof(cmd), "WPS_ER_START %s", argv[0]);
-		return wpa_ctrl_command(ctrl, cmd);
-	}
-	return wpa_ctrl_command(ctrl, "WPS_ER_START");
+	return wpa_cli_cmd(ctrl, "WPS_ER_START", 0, argc, argv);
 }
 
 
@@ -982,9 +907,6 @@
 static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc,
 				  char *argv[])
 {
-	char cmd[256];
-	int res;
-
 	if (argc < 2) {
 		printf("Invalid WPS_ER_PIN command: need at least two "
 		       "arguments:\n"
@@ -994,48 +916,20 @@
 		return -1;
 	}
 
-	if (argc > 2)
-		res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s",
-				  argv[0], argv[1], argv[2]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
-				  argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long WPS_ER_PIN command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "WPS_ER_PIN", 2, argc, argv);
 }
 
 
 static int wpa_cli_cmd_wps_er_pbc(struct wpa_ctrl *ctrl, int argc,
 				  char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid WPS_ER_PBC command: need one argument:\n"
-		       "- UUID: Specify the Enrollee\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
-			  argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long WPS_ER_PBC command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "WPS_ER_PBC", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc,
 				    char *argv[])
 {
-	char cmd[256];
-	int res;
-
 	if (argc != 2) {
 		printf("Invalid WPS_ER_LEARN command: need two arguments:\n"
 		       "- UUID: specify which AP to use\n"
@@ -1043,22 +937,13 @@
 		return -1;
 	}
 
-	res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
-			  argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long WPS_ER_LEARN command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "WPS_ER_LEARN", 2, argc, argv);
 }
 
 
 static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc,
 					 char *argv[])
 {
-	char cmd[256];
-	int res;
-
 	if (argc != 2) {
 		printf("Invalid WPS_ER_SET_CONFIG command: need two "
 		       "arguments:\n"
@@ -1067,13 +952,7 @@
 		return -1;
 	}
 
-	res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_SET_CONFIG %s %s",
-			  argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long WPS_ER_SET_CONFIG command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "WPS_ER_SET_CONFIG", 2, argc, argv);
 }
 
 
@@ -1128,42 +1007,32 @@
 }
 
 
+#ifdef CONFIG_WPS_NFC
+static int wpa_cli_cmd_wps_er_nfc_config_token(struct wpa_ctrl *ctrl, int argc,
+					       char *argv[])
+{
+	if (argc != 2) {
+		printf("Invalid WPS_ER_NFC_CONFIG_TOKEN command: need two "
+		       "arguments:\n"
+		       "- WPS/NDEF: token format\n"
+		       "- UUID: specify which AP to use\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "WPS_ER_NFC_CONFIG_TOKEN", 2, argc, argv);
+}
+#endif /* CONFIG_WPS_NFC */
+
+
 static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid IBSS_RSN command: needs one argument "
-		       "(Peer STA MAC address)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long IBSS_RSN command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "IBSS_RSN", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid LEVEL command: needs one argument (debug "
-		       "level)\n");
-		return -1;
-	}
-	res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long LEVEL command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "LEVEL", 1, argc, argv);
 }
 
 
@@ -1368,85 +1237,25 @@
 
 static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256], *pos, *end;
-	int i, ret;
-
 	if (argc < 2) {
 		printf("Invalid BSSID command: needs two arguments (network "
 		       "id and BSSID)\n");
 		return -1;
 	}
 
-	end = cmd + sizeof(cmd);
-	pos = cmd;
-	ret = os_snprintf(pos, end - pos, "BSSID");
-	if (ret < 0 || ret >= end - pos) {
-		printf("Too long BSSID command.\n");
-		return -1;
-	}
-	pos += ret;
-	for (i = 0; i < argc; i++) {
-		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (ret < 0 || ret >= end - pos) {
-			printf("Too long BSSID command.\n");
-			return -1;
-		}
-		pos += ret;
-	}
-
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "BSSID", 2, argc, argv);
 }
 
 
 static int wpa_cli_cmd_blacklist(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256], *pos, *end;
-	int i, ret;
-
-	end = cmd + sizeof(cmd);
-	pos = cmd;
-	ret = os_snprintf(pos, end - pos, "BLACKLIST");
-	if (ret < 0 || ret >= end - pos) {
-		printf("Too long BLACKLIST command.\n");
-		return -1;
-	}
-	pos += ret;
-	for (i = 0; i < argc; i++) {
-		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (ret < 0 || ret >= end - pos) {
-			printf("Too long BLACKLIST command.\n");
-			return -1;
-		}
-		pos += ret;
-	}
-
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "BLACKLIST", 0, argc, argv);
 }
 
 
 static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256], *pos, *end;
-	int i, ret;
-
-	end = cmd + sizeof(cmd);
-	pos = cmd;
-	ret = os_snprintf(pos, end - pos, "LOG_LEVEL");
-	if (ret < 0 || ret >= end - pos) {
-		printf("Too long LOG_LEVEL command.\n");
-		return -1;
-	}
-	pos += ret;
-	for (i = 0; i < argc; i++) {
-		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (ret < 0 || ret >= end - pos) {
-			printf("Too long LOG_LEVEL command.\n");
-			return -1;
-		}
-		pos += ret;
-	}
-
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "LOG_LEVEL", 0, argc, argv);
 }
 
 
@@ -1460,63 +1269,21 @@
 static int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
-	char cmd[32];
-	int res;
-
-	if (argc < 1) {
-		printf("Invalid SELECT_NETWORK command: needs one argument "
-		       "(network id)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "SELECT_NETWORK", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
-	char cmd[32];
-	int res;
-
-	if (argc < 1) {
-		printf("Invalid ENABLE_NETWORK command: needs one argument "
-		       "(network id)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "ENABLE_NETWORK", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc,
 				       char *argv[])
 {
-	char cmd[32];
-	int res;
-
-	if (argc < 1) {
-		printf("Invalid DISABLE_NETWORK command: needs one argument "
-		       "(network id)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "DISABLE_NETWORK %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "DISABLE_NETWORK", 1, argc, argv);
 }
 
 
@@ -1530,21 +1297,7 @@
 static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
-	char cmd[32];
-	int res;
-
-	if (argc < 1) {
-		printf("Invalid REMOVE_NETWORK command: needs one argument "
-		       "(network id)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv);
 }
 
 
@@ -1572,9 +1325,6 @@
 static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
-	char cmd[256];
-	int res;
-
 	if (argc == 0) {
 		wpa_cli_show_network_variables();
 		return 0;
@@ -1586,22 +1336,13 @@
 		return -1;
 	}
 
-	res = os_snprintf(cmd, sizeof(cmd), "SET_NETWORK %s %s %s",
-			  argv[0], argv[1], argv[2]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long SET_NETWORK command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "SET_NETWORK", 3, argc, argv);
 }
 
 
 static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
-	char cmd[256];
-	int res;
-
 	if (argc == 0) {
 		wpa_cli_show_network_variables();
 		return 0;
@@ -1613,13 +1354,39 @@
 		return -1;
 	}
 
-	res = os_snprintf(cmd, sizeof(cmd), "GET_NETWORK %s %s",
-			  argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long GET_NETWORK command.\n");
+	return wpa_cli_cmd(ctrl, "GET_NETWORK", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "LIST_CREDS");
+}
+
+
+static int wpa_cli_cmd_add_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "ADD_CRED");
+}
+
+
+static int wpa_cli_cmd_remove_cred(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_set_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc != 3) {
+		printf("Invalid SET_CRED command: needs three arguments\n"
+		       "(cred id, variable name, and value)\n");
 		return -1;
 	}
-	return wpa_ctrl_command(ctrl, cmd);
+
+	return wpa_cli_cmd(ctrl, "SET_CRED", 3, argc, argv);
 }
 
 
@@ -1659,23 +1426,7 @@
 
 static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[64];
-	int res;
-
-	if (argc < 1) {
-		printf("Invalid BSS command: need at least one argument"
-		       "(index or BSSID)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "BSS %s\t%s\t%s", argv[0],
-			  argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "");
-
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv);
 }
 
 
@@ -1697,9 +1448,6 @@
 static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
-	char cmd[64];
-	int res;
-
 	if (argc < 1 || argc > 2) {
 		printf("Invalid GET_CAPABILITY command: need either one or "
 		       "two arguments\n");
@@ -1712,13 +1460,7 @@
 		return -1;
 	}
 
-	res = os_snprintf(cmd, sizeof(cmd), "GET_CAPABILITY %s%s", argv[0],
-			  (argc == 2) ? " strict" : "");
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "GET_CAPABILITY", 1, argc, argv);
 }
 
 
@@ -1798,20 +1540,7 @@
 static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc,
 					char *argv[])
 {
-	char cmd[128];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid INTERFACE_REMOVE command: needs one argument "
-		       "(interface name)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "INTERFACE_REMOVE %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "INTERFACE_REMOVE", 1, argc, argv);
 }
 
 
@@ -1825,14 +1554,7 @@
 #ifdef CONFIG_AP
 static int wpa_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char buf[64];
-	if (argc != 1) {
-		printf("Invalid 'sta' command - exactly one argument, STA "
-		       "address, is required.\n");
-		return -1;
-	}
-	os_snprintf(buf, sizeof(buf), "STA %s", argv[0]);
-	return wpa_ctrl_command(ctrl, buf);
+	return wpa_cli_cmd(ctrl, "STA", 1, argc, argv);
 }
 
 
@@ -1848,7 +1570,7 @@
 		return -1;
 	}
 	len = sizeof(buf) - 1;
-	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
+	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
 			       wpa_cli_msg_cb);
 	if (ret == -2) {
 		printf("'%s' command timed out.\n", cmd);
@@ -1859,7 +1581,7 @@
 	}
 
 	buf[len] = '\0';
-	if (memcmp(buf, "FAIL", 4) == 0)
+	if (os_memcmp(buf, "FAIL", 4) == 0)
 		return -1;
 	printf("%s", buf);
 
@@ -1884,6 +1606,20 @@
 
 	return -1;
 }
+
+
+static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DEAUTHENTICATE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv);
+}
 #endif /* CONFIG_AP */
 
 
@@ -1907,21 +1643,7 @@
 
 static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[128];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid ROAM command: needs one argument "
-		       "(target AP's BSSID)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long ROAM command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "ROAM", 1, argc, argv);
 }
 
 
@@ -1929,24 +1651,36 @@
 
 static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[128];
-	int res;
+	return wpa_cli_cmd(ctrl, "P2P_FIND", 0, argc, argv);
+}
 
-	if (argc == 0)
-		return wpa_ctrl_command(ctrl, "P2P_FIND");
 
-	if (argc > 2)
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s %s",
-				  argv[0], argv[1], argv[2]);
-	else if (argc > 1)
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s",
-				  argv[0], argv[1]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+static char ** wpa_cli_complete_p2p_find(const char *str, int pos)
+{
+	char **res = NULL;
+	int arg = get_cmd_arg_num(str, pos);
+
+	res = os_calloc(6, sizeof(char *));
+	if (res == NULL)
+		return NULL;
+	res[0] = os_strdup("type=social");
+	if (res[0] == NULL) {
+		os_free(res);
+		return NULL;
+	}
+	res[1] = os_strdup("type=progressive");
+	if (res[1] == NULL)
+		return res;
+	res[2] = os_strdup("delay=");
+	if (res[2] == NULL)
+		return res;
+	res[3] = os_strdup("dev_id=");
+	if (res[3] == NULL)
+		return res;
+	if (arg == 1)
+		res[4] = os_strdup("[timeout]");
+
+	return res;
 }
 
 
@@ -1960,33 +1694,7 @@
 static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
-	char cmd[128];
-	int res;
-
-	if (argc < 2) {
-		printf("Invalid P2P_CONNECT command: needs at least two "
-		       "arguments (address and pbc/PIN)\n");
-		return -1;
-	}
-
-	if (argc > 4)
-		res = os_snprintf(cmd, sizeof(cmd),
-				  "P2P_CONNECT %s %s %s %s %s",
-				  argv[0], argv[1], argv[2], argv[3],
-				  argv[4]);
-	else if (argc > 3)
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s %s",
-				  argv[0], argv[1], argv[2], argv[3]);
-	else if (argc > 2)
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s",
-				  argv[0], argv[1], argv[2]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s",
-				  argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_CONNECT", 2, argc, argv);
 }
 
 
@@ -2008,37 +1716,14 @@
 static int wpa_cli_cmd_p2p_listen(struct wpa_ctrl *ctrl, int argc,
 				  char *argv[])
 {
-	char cmd[128];
-	int res;
-
-	if (argc == 0)
-		return wpa_ctrl_command(ctrl, "P2P_LISTEN");
-
-	res = os_snprintf(cmd, sizeof(cmd), "P2P_LISTEN %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_LISTEN", 0, argc, argv);
 }
 
 
 static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc,
 					char *argv[])
 {
-	char cmd[128];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid P2P_GROUP_REMOVE command: needs one argument "
-		       "(interface name)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_GROUP_REMOVE", 1, argc, argv);
 }
 
 
@@ -2060,31 +1745,13 @@
 static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc,
 					char *argv[])
 {
-	char cmd[128];
-	int res;
-
-	if (argc == 0)
-		return wpa_ctrl_command(ctrl, "P2P_GROUP_ADD");
-
-	if (argc > 1)
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s %s",
-				  argv[0], argv[1]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s",
-				  argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_GROUP_ADD", 0, argc, argv);
 }
 
 
 static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc,
 				     char *argv[])
 {
-	char cmd[128];
-	int res;
-
 	if (argc != 2 && argc != 3) {
 		printf("Invalid P2P_PROV_DISC command: needs at least "
 		       "two arguments, address and config method\n"
@@ -2092,16 +1759,7 @@
 		return -1;
 	}
 
-	if (argc == 3)
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s %s",
-				  argv[0], argv[1], argv[2]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s",
-				  argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_PROV_DISC", 2, argc, argv);
 }
 
 
@@ -2116,7 +1774,6 @@
 					 char *argv[])
 {
 	char cmd[4096];
-	int res;
 
 	if (argc != 2 && argc != 4) {
 		printf("Invalid P2P_SERV_DISC_REQ command: needs two "
@@ -2126,16 +1783,8 @@
 		return -1;
 	}
 
-	if (argc == 4)
-		res = os_snprintf(cmd, sizeof(cmd),
-				  "P2P_SERV_DISC_REQ %s %s %s %s",
-				  argv[0], argv[1], argv[2], argv[3]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ %s %s",
-				  argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
+	if (write_cmd(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ", argc, argv) < 0)
 		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
 	return wpa_ctrl_command(ctrl, cmd);
 }
 
@@ -2143,21 +1792,7 @@
 static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl,
 						int argc, char *argv[])
 {
-	char cmd[128];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid P2P_SERV_DISC_CANCEL_REQ command: needs one "
-		       "argument (pending request identifier)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_CANCEL_REQ %s",
-			  argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_CANCEL_REQ", 1, argc, argv);
 }
 
 
@@ -2192,21 +1827,7 @@
 static int wpa_cli_cmd_p2p_serv_disc_external(struct wpa_ctrl *ctrl,
 					      int argc, char *argv[])
 {
-	char cmd[128];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid P2P_SERV_DISC_EXTERNAL command: needs one "
-		       "argument (external processing: 0/1)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_EXTERNAL %s",
-			  argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_EXTERNAL", 1, argc, argv);
 }
 
 
@@ -2274,60 +1895,20 @@
 static int wpa_cli_cmd_p2p_reject(struct wpa_ctrl *ctrl,
 				  int argc, char *argv[])
 {
-	char cmd[128];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid P2P_REJECT command: needs one argument "
-		       "(peer address)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "P2P_REJECT %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_REJECT", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl,
 				  int argc, char *argv[])
 {
-	char cmd[128];
-	int res;
-
-	if (argc < 1) {
-		printf("Invalid P2P_INVITE command: needs at least one "
-		       "argument\n");
-		return -1;
-	}
-
-	if (argc > 2)
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s %s",
-				  argv[0], argv[1], argv[2]);
-	else if (argc > 1)
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s",
-				  argv[0], argv[1]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_INVITE", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char buf[64];
-	if (argc != 1) {
-		printf("Invalid 'p2p_peer' command - exactly one argument, "
-		       "P2P peer device address, is required.\n");
-		return -1;
-	}
-	os_snprintf(buf, sizeof(buf), "P2P_PEER %s", argv[0]);
-	return wpa_ctrl_command(ctrl, buf);
+	return wpa_cli_cmd(ctrl, "P2P_PEER", 1, argc, argv);
 }
 
 
@@ -2357,7 +1938,7 @@
 	if (ctrl_conn == NULL)
 		return -1;
 	len = sizeof(buf) - 1;
-	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
+	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
 			       wpa_cli_msg_cb);
 	if (ret == -2) {
 		printf("'%s' command timed out.\n", cmd);
@@ -2368,7 +1949,7 @@
 	}
 
 	buf[len] = '\0';
-	if (memcmp(buf, "FAIL", 4) == 0)
+	if (os_memcmp(buf, "FAIL", 4) == 0)
 		return -1;
 
 	pos = buf;
@@ -2403,20 +1984,7 @@
 
 static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[100];
-	int res;
-
-	if (argc != 2) {
-		printf("Invalid P2P_SET command: needs two arguments (field, "
-		       "value)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "P2P_SET %s %s", argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_SET", 2, argc, argv);
 }
 
 
@@ -2436,31 +2004,13 @@
 static int wpa_cli_cmd_p2p_unauthorize(struct wpa_ctrl *ctrl, int argc,
 				       char *argv[])
 {
-	char cmd[100];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid P2P_UNAUTHORIZE command: needs one argument "
-		       "(peer address)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "P2P_UNAUTHORIZE %s", argv[0]);
-
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_UNAUTHORIZE", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc,
 					char *argv[])
 {
-	char cmd[100];
-	int res;
-
 	if (argc != 0 && argc != 2 && argc != 4) {
 		printf("Invalid P2P_PRESENCE_REQ command: needs two arguments "
 		       "(preferred duration, interval; in microsecods).\n"
@@ -2469,28 +2019,13 @@
 		return -1;
 	}
 
-	if (argc == 4)
-		res = os_snprintf(cmd, sizeof(cmd),
-				  "P2P_PRESENCE_REQ %s %s %s %s",
-				  argv[0], argv[1], argv[2], argv[3]);
-	else if (argc == 2)
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ %s %s",
-				  argv[0], argv[1]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ");
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_PRESENCE_REQ", 0, argc, argv);
 }
 
 
 static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
-	char cmd[100];
-	int res;
-
 	if (argc != 0 && argc != 2) {
 		printf("Invalid P2P_EXT_LISTEN command: needs two arguments "
 		       "(availability period, availability interval; in "
@@ -2500,18 +2035,54 @@
 		return -1;
 	}
 
-	if (argc == 2)
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN %s %s",
-				  argv[0], argv[1]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN");
+	return wpa_cli_cmd(ctrl, "P2P_EXT_LISTEN", 0, argc, argv);
+}
+
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	char cmd[100];
+	int res;
+
+	if (argc != 1 && argc != 2) {
+		printf("Invalid WFD_SUBELEM_SET command: needs one or two "
+		       "arguments (subelem, hexdump)\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s",
+			  argv[0], argc > 1 ? argv[1] : "");
 	if (res < 0 || (size_t) res >= sizeof(cmd))
 		return -1;
 	cmd[sizeof(cmd) - 1] = '\0';
 	return wpa_ctrl_command(ctrl, cmd);
 }
 
-#endif /* CONFIG_P2P */
+
+static int wpa_cli_cmd_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	char cmd[100];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid WFD_SUBELEM_GET command: needs one "
+		       "argument (subelem)\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s",
+			  argv[0]);
+	if (res < 0 || (size_t) res >= sizeof(cmd))
+		return -1;
+	cmd[sizeof(cmd) - 1] = '\0';
+	return wpa_ctrl_command(ctrl, cmd);
+}
+#endif /* CONFIG_WIFI_DISPLAY */
 
 
 #ifdef CONFIG_INTERWORKING
@@ -2532,142 +2103,93 @@
 static int wpa_cli_cmd_interworking_select(struct wpa_ctrl *ctrl, int argc,
 					   char *argv[])
 {
-	char cmd[100];
-	int res;
-
-	if (argc == 0)
-		return wpa_ctrl_command(ctrl, "INTERWORKING_SELECT");
-
-	res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_SELECT %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "INTERWORKING_SELECT", 0, argc, argv);
 }
 
 
 static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc,
 					    char *argv[])
 {
-	char cmd[100];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid INTERWORKING_CONNECT commands: needs one "
-		       "argument (BSSID)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_CONNECT %s",
-			  argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "INTERWORKING_CONNECT", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[100];
-	int res;
+	return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv);
+}
 
-	if (argc != 2) {
-		printf("Invalid ANQP_GET command: needs two arguments "
-		       "(addr and info id list)\n");
+
+static int wpa_cli_cmd_gas_request(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "GAS_REQUEST", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_gas_response_get(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "GAS_RESPONSE_GET", 2, argc, argv);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+#ifdef CONFIG_HS20
+
+static int wpa_cli_cmd_hs20_anqp_get(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "HS20_ANQP_GET", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc,
+					       char *argv[])
+{
+	char cmd[512];
+
+	if (argc == 0) {
+		printf("Command needs one or two arguments (dst mac addr and "
+		       "optional home realm)\n");
 		return -1;
 	}
 
-	res = os_snprintf(cmd, sizeof(cmd), "ANQP_GET %s %s",
-			  argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
+	if (write_cmd(cmd, sizeof(cmd), "HS20_GET_NAI_HOME_REALM_LIST",
+		      argc, argv) < 0)
 		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
+
 	return wpa_ctrl_command(ctrl, cmd);
 }
-#endif /* CONFIG_INTERWORKING */
+
+#endif /* CONFIG_HS20 */
 
 
 static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc,
 				       char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid STA_AUTOCONNECT command: needs one argument "
-		       "(0/1 = disable/enable automatic reconnection)\n");
-		return -1;
-	}
-	res = os_snprintf(cmd, sizeof(cmd), "STA_AUTOCONNECT %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long STA_AUTOCONNECT command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "STA_AUTOCONNECT", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc,
 				     char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid TDLS_DISCOVER command: needs one argument "
-		       "(Peer STA MAC address)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "TDLS_DISCOVER %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long TDLS_DISCOVER command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "TDLS_DISCOVER", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc,
 				  char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid TDLS_SETUP command: needs one argument "
-		       "(Peer STA MAC address)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "TDLS_SETUP %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long TDLS_SETUP command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "TDLS_SETUP", 1, argc, argv);
 }
 
 
 static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc,
 				     char *argv[])
 {
-	char cmd[256];
-	int res;
-
-	if (argc != 1) {
-		printf("Invalid TDLS_TEARDOWN command: needs one argument "
-		       "(Peer STA MAC address)\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "TDLS_TEARDOWN %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-		printf("Too long TDLS_TEARDOWN command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "TDLS_TEARDOWN", 1, argc, argv);
 }
 
 
@@ -2678,12 +2200,41 @@
 }
 
 
+static int wpa_cli_cmd_pktcnt_poll(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PKTCNT_POLL");
+}
+
+
 static int wpa_cli_cmd_reauthenticate(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
 	return wpa_ctrl_command(ctrl, "REAUTHENTICATE");
 }
 
+
+#ifdef CONFIG_AUTOSCAN
+
+static int wpa_cli_cmd_autoscan(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc == 0)
+		return wpa_ctrl_command(ctrl, "AUTOSCAN ");
+
+	return wpa_cli_cmd(ctrl, "AUTOSCAN", 0, argc, argv);
+}
+
+#endif /* CONFIG_AUTOSCAN */
+
+
+static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc == 0)
+		return -1;
+	return wpa_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]);
+}
+
+
 #ifdef ANDROID
 static int wpa_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
@@ -2705,6 +2256,7 @@
 }
 #endif
 
+
 enum wpa_cli_cmd_flags {
 	cli_cmd_flag_none		= 0x00,
 	cli_cmd_flag_sensitive		= 0x01
@@ -2713,348 +2265,433 @@
 struct wpa_cli_cmd {
 	const char *cmd;
 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+	char ** (*completion)(const char *str, int pos);
 	enum wpa_cli_cmd_flags flags;
 	const char *usage;
 };
 
 static struct wpa_cli_cmd wpa_cli_commands[] = {
-	{ "status", wpa_cli_cmd_status,
+	{ "status", wpa_cli_cmd_status, NULL,
 	  cli_cmd_flag_none,
 	  "[verbose] = get current WPA/EAPOL/EAP status" },
-	{ "ping", wpa_cli_cmd_ping,
+	{ "ifname", wpa_cli_cmd_ifname, NULL,
+	  cli_cmd_flag_none,
+	  "= get current interface name" },
+	{ "ping", wpa_cli_cmd_ping, NULL,
 	  cli_cmd_flag_none,
 	  "= pings wpa_supplicant" },
-	{ "relog", wpa_cli_cmd_relog,
+	{ "relog", wpa_cli_cmd_relog, NULL,
 	  cli_cmd_flag_none,
 	  "= re-open log-file (allow rolling logs)" },
-	{ "note", wpa_cli_cmd_note,
+	{ "note", wpa_cli_cmd_note, NULL,
 	  cli_cmd_flag_none,
 	  "<text> = add a note to wpa_supplicant debug log" },
-	{ "mib", wpa_cli_cmd_mib,
+	{ "mib", wpa_cli_cmd_mib, NULL,
 	  cli_cmd_flag_none,
 	  "= get MIB variables (dot1x, dot11)" },
-	{ "help", wpa_cli_cmd_help,
+	{ "help", wpa_cli_cmd_help, wpa_cli_complete_help,
 	  cli_cmd_flag_none,
-	  "= show this usage help" },
-	{ "interface", wpa_cli_cmd_interface,
+	  "[command] = show usage help" },
+	{ "interface", wpa_cli_cmd_interface, NULL,
 	  cli_cmd_flag_none,
 	  "[ifname] = show interfaces/select interface" },
-	{ "level", wpa_cli_cmd_level,
+	{ "level", wpa_cli_cmd_level, NULL,
 	  cli_cmd_flag_none,
 	  "<debug level> = change debug level" },
-	{ "license", wpa_cli_cmd_license,
+	{ "license", wpa_cli_cmd_license, NULL,
 	  cli_cmd_flag_none,
 	  "= show full wpa_cli license" },
-	{ "quit", wpa_cli_cmd_quit,
+	{ "quit", wpa_cli_cmd_quit, NULL,
 	  cli_cmd_flag_none,
 	  "= exit wpa_cli" },
-	{ "set", wpa_cli_cmd_set,
+	{ "set", wpa_cli_cmd_set, NULL,
 	  cli_cmd_flag_none,
 	  "= set variables (shows list of variables when run without "
 	  "arguments)" },
-	{ "get", wpa_cli_cmd_get,
+	{ "get", wpa_cli_cmd_get, NULL,
 	  cli_cmd_flag_none,
 	  "<name> = get information" },
-	{ "logon", wpa_cli_cmd_logon,
+	{ "logon", wpa_cli_cmd_logon, NULL,
 	  cli_cmd_flag_none,
 	  "= IEEE 802.1X EAPOL state machine logon" },
-	{ "logoff", wpa_cli_cmd_logoff,
+	{ "logoff", wpa_cli_cmd_logoff, NULL,
 	  cli_cmd_flag_none,
 	  "= IEEE 802.1X EAPOL state machine logoff" },
-	{ "pmksa", wpa_cli_cmd_pmksa,
+	{ "pmksa", wpa_cli_cmd_pmksa, NULL,
 	  cli_cmd_flag_none,
 	  "= show PMKSA cache" },
-	{ "reassociate", wpa_cli_cmd_reassociate,
+	{ "reassociate", wpa_cli_cmd_reassociate, NULL,
 	  cli_cmd_flag_none,
 	  "= force reassociation" },
-	{ "preauthenticate", wpa_cli_cmd_preauthenticate,
+	{ "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<BSSID> = force preauthentication" },
-	{ "identity", wpa_cli_cmd_identity,
+	{ "identity", wpa_cli_cmd_identity, NULL,
 	  cli_cmd_flag_none,
 	  "<network id> <identity> = configure identity for an SSID" },
-	{ "password", wpa_cli_cmd_password,
+	{ "password", wpa_cli_cmd_password, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <password> = configure password for an SSID" },
-	{ "new_password", wpa_cli_cmd_new_password,
+	{ "new_password", wpa_cli_cmd_new_password, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <password> = change password for an SSID" },
-	{ "pin", wpa_cli_cmd_pin,
+	{ "pin", wpa_cli_cmd_pin, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <pin> = configure pin for an SSID" },
-	{ "otp", wpa_cli_cmd_otp,
+	{ "otp", wpa_cli_cmd_otp, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <password> = configure one-time-password for an SSID"
 	},
-	{ "passphrase", wpa_cli_cmd_passphrase,
+	{ "passphrase", wpa_cli_cmd_passphrase, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <passphrase> = configure private key passphrase\n"
 	  "  for an SSID" },
-	{ "bssid", wpa_cli_cmd_bssid,
+	{ "bssid", wpa_cli_cmd_bssid, NULL,
 	  cli_cmd_flag_none,
 	  "<network id> <BSSID> = set preferred BSSID for an SSID" },
-	{ "blacklist", wpa_cli_cmd_blacklist,
+	{ "blacklist", wpa_cli_cmd_blacklist, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<BSSID> = add a BSSID to the blacklist\n"
 	  "blacklist clear = clear the blacklist\n"
 	  "blacklist = display the blacklist" },
-	{ "log_level", wpa_cli_cmd_log_level,
+	{ "log_level", wpa_cli_cmd_log_level, NULL,
 	  cli_cmd_flag_none,
 	  "<level> [<timestamp>] = update the log level/timestamp\n"
 	  "log_level = display the current log level and log options" },
-	{ "list_networks", wpa_cli_cmd_list_networks,
+	{ "list_networks", wpa_cli_cmd_list_networks, NULL,
 	  cli_cmd_flag_none,
 	  "= list configured networks" },
-	{ "select_network", wpa_cli_cmd_select_network,
+	{ "select_network", wpa_cli_cmd_select_network, NULL,
 	  cli_cmd_flag_none,
 	  "<network id> = select a network (disable others)" },
-	{ "enable_network", wpa_cli_cmd_enable_network,
+	{ "enable_network", wpa_cli_cmd_enable_network, NULL,
 	  cli_cmd_flag_none,
 	  "<network id> = enable a network" },
-	{ "disable_network", wpa_cli_cmd_disable_network,
+	{ "disable_network", wpa_cli_cmd_disable_network, NULL,
 	  cli_cmd_flag_none,
 	  "<network id> = disable a network" },
-	{ "add_network", wpa_cli_cmd_add_network,
+	{ "add_network", wpa_cli_cmd_add_network, NULL,
 	  cli_cmd_flag_none,
 	  "= add a network" },
-	{ "remove_network", wpa_cli_cmd_remove_network,
+	{ "remove_network", wpa_cli_cmd_remove_network, NULL,
 	  cli_cmd_flag_none,
 	  "<network id> = remove a network" },
-	{ "set_network", wpa_cli_cmd_set_network,
+	{ "set_network", wpa_cli_cmd_set_network, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <variable> <value> = set network variables (shows\n"
 	  "  list of variables when run without arguments)" },
-	{ "get_network", wpa_cli_cmd_get_network,
+	{ "get_network", wpa_cli_cmd_get_network, NULL,
 	  cli_cmd_flag_none,
 	  "<network id> <variable> = get network variables" },
-	{ "save_config", wpa_cli_cmd_save_config,
+	{ "list_creds", wpa_cli_cmd_list_creds, NULL,
+	  cli_cmd_flag_none,
+	  "= list configured credentials" },
+	{ "add_cred", wpa_cli_cmd_add_cred, NULL,
+	  cli_cmd_flag_none,
+	  "= add a credential" },
+	{ "remove_cred", wpa_cli_cmd_remove_cred, NULL,
+	  cli_cmd_flag_none,
+	  "<cred id> = remove a credential" },
+	{ "set_cred", wpa_cli_cmd_set_cred, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<cred id> <variable> <value> = set credential variables" },
+	{ "save_config", wpa_cli_cmd_save_config, NULL,
 	  cli_cmd_flag_none,
 	  "= save the current configuration" },
-	{ "disconnect", wpa_cli_cmd_disconnect,
+	{ "disconnect", wpa_cli_cmd_disconnect, NULL,
 	  cli_cmd_flag_none,
 	  "= disconnect and wait for reassociate/reconnect command before\n"
 	  "  connecting" },
-	{ "reconnect", wpa_cli_cmd_reconnect,
+	{ "reconnect", wpa_cli_cmd_reconnect, NULL,
 	  cli_cmd_flag_none,
 	  "= like reassociate, but only takes effect if already disconnected"
 	},
-	{ "scan", wpa_cli_cmd_scan,
+	{ "scan", wpa_cli_cmd_scan, NULL,
 	  cli_cmd_flag_none,
 	  "= request new BSS scan" },
-	{ "scan_results", wpa_cli_cmd_scan_results,
+	{ "scan_results", wpa_cli_cmd_scan_results, NULL,
 	  cli_cmd_flag_none,
 	  "= get latest scan results" },
-	{ "bss", wpa_cli_cmd_bss,
+	{ "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<<idx> | <bssid>> = get detailed scan result info" },
-	{ "get_capability", wpa_cli_cmd_get_capability,
+	{ "get_capability", wpa_cli_cmd_get_capability, NULL,
 	  cli_cmd_flag_none,
-	  "<eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilies" },
-	{ "reconfigure", wpa_cli_cmd_reconfigure,
+	  "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels> "
+	  "= get capabilies" },
+	{ "reconfigure", wpa_cli_cmd_reconfigure, NULL,
 	  cli_cmd_flag_none,
 	  "= force wpa_supplicant to re-read its configuration file" },
-	{ "terminate", wpa_cli_cmd_terminate,
+	{ "terminate", wpa_cli_cmd_terminate, NULL,
 	  cli_cmd_flag_none,
 	  "= terminate wpa_supplicant" },
-	{ "interface_add", wpa_cli_cmd_interface_add,
+	{ "interface_add", wpa_cli_cmd_interface_add, NULL,
 	  cli_cmd_flag_none,
 	  "<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n"
 	  "  <bridge_name> = adds new interface, all parameters but <ifname>\n"
 	  "  are optional" },
-	{ "interface_remove", wpa_cli_cmd_interface_remove,
+	{ "interface_remove", wpa_cli_cmd_interface_remove, NULL,
 	  cli_cmd_flag_none,
 	  "<ifname> = removes the interface" },
-	{ "interface_list", wpa_cli_cmd_interface_list,
+	{ "interface_list", wpa_cli_cmd_interface_list, NULL,
 	  cli_cmd_flag_none,
 	  "= list available interfaces" },
-	{ "ap_scan", wpa_cli_cmd_ap_scan,
+	{ "ap_scan", wpa_cli_cmd_ap_scan, NULL,
 	  cli_cmd_flag_none,
 	  "<value> = set ap_scan parameter" },
-	{ "scan_interval", wpa_cli_cmd_scan_interval,
+	{ "scan_interval", wpa_cli_cmd_scan_interval, NULL,
 	  cli_cmd_flag_none,
 	  "<value> = set scan_interval parameter (in seconds)" },
-	{ "bss_expire_age", wpa_cli_cmd_bss_expire_age,
+	{ "bss_expire_age", wpa_cli_cmd_bss_expire_age, NULL,
 	  cli_cmd_flag_none,
 	  "<value> = set BSS expiration age parameter" },
-	{ "bss_expire_count", wpa_cli_cmd_bss_expire_count,
+	{ "bss_expire_count", wpa_cli_cmd_bss_expire_count, NULL,
 	  cli_cmd_flag_none,
 	  "<value> = set BSS expiration scan count parameter" },
-	{ "stkstart", wpa_cli_cmd_stkstart,
+	{ "bss_flush", wpa_cli_cmd_bss_flush, NULL,
+	  cli_cmd_flag_none,
+	  "<value> = set BSS flush age (0 by default)" },
+	{ "stkstart", wpa_cli_cmd_stkstart, NULL,
 	  cli_cmd_flag_none,
 	  "<addr> = request STK negotiation with <addr>" },
-	{ "ft_ds", wpa_cli_cmd_ft_ds,
+	{ "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<addr> = request over-the-DS FT with <addr>" },
-	{ "wps_pbc", wpa_cli_cmd_wps_pbc,
+	{ "wps_pbc", wpa_cli_cmd_wps_pbc, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" },
-	{ "wps_pin", wpa_cli_cmd_wps_pin,
+	{ "wps_pin", wpa_cli_cmd_wps_pin, wpa_cli_complete_bss,
 	  cli_cmd_flag_sensitive,
 	  "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
 	  "hardcoded)" },
-	{ "wps_check_pin", wpa_cli_cmd_wps_check_pin,
+	{ "wps_check_pin", wpa_cli_cmd_wps_check_pin, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<PIN> = verify PIN checksum" },
-	{ "wps_cancel", wpa_cli_cmd_wps_cancel, cli_cmd_flag_none,
+	{ "wps_cancel", wpa_cli_cmd_wps_cancel, NULL, cli_cmd_flag_none,
 	  "Cancels the pending WPS operation" },
 #ifdef CONFIG_WPS_OOB
-	{ "wps_oob", wpa_cli_cmd_wps_oob,
+	{ "wps_oob", wpa_cli_cmd_wps_oob, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<DEV_TYPE> <PATH> <METHOD> [DEV_NAME] = start WPS OOB" },
 #endif /* CONFIG_WPS_OOB */
-	{ "wps_reg", wpa_cli_cmd_wps_reg,
+#ifdef CONFIG_WPS_NFC
+	{ "wps_nfc", wpa_cli_cmd_wps_nfc, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
+	  "[BSSID] = start Wi-Fi Protected Setup: NFC" },
+	{ "wps_nfc_token", wpa_cli_cmd_wps_nfc_token, NULL,
+	  cli_cmd_flag_none,
+	  "<WPS|NDEF> = create password token" },
+	{ "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<hexdump of payload> = report read NFC tag with WPS data" },
+#endif /* CONFIG_WPS_NFC */
+	{ "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss,
 	  cli_cmd_flag_sensitive,
 	  "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },
-	{ "wps_ap_pin", wpa_cli_cmd_wps_ap_pin,
+	{ "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, NULL,
 	  cli_cmd_flag_sensitive,
 	  "[params..] = enable/disable AP PIN" },
-	{ "wps_er_start", wpa_cli_cmd_wps_er_start,
+	{ "wps_er_start", wpa_cli_cmd_wps_er_start, NULL,
 	  cli_cmd_flag_none,
 	  "[IP address] = start Wi-Fi Protected Setup External Registrar" },
-	{ "wps_er_stop", wpa_cli_cmd_wps_er_stop,
+	{ "wps_er_stop", wpa_cli_cmd_wps_er_stop, NULL,
 	  cli_cmd_flag_none,
 	  "= stop Wi-Fi Protected Setup External Registrar" },
-	{ "wps_er_pin", wpa_cli_cmd_wps_er_pin,
+	{ "wps_er_pin", wpa_cli_cmd_wps_er_pin, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<UUID> <PIN> = add an Enrollee PIN to External Registrar" },
-	{ "wps_er_pbc", wpa_cli_cmd_wps_er_pbc,
+	{ "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, NULL,
 	  cli_cmd_flag_none,
 	  "<UUID> = accept an Enrollee PBC using External Registrar" },
-	{ "wps_er_learn", wpa_cli_cmd_wps_er_learn,
+	{ "wps_er_learn", wpa_cli_cmd_wps_er_learn, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<UUID> <PIN> = learn AP configuration" },
-	{ "wps_er_set_config", wpa_cli_cmd_wps_er_set_config,
+	{ "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, NULL,
 	  cli_cmd_flag_none,
 	  "<UUID> <network id> = set AP configuration for enrolling" },
-	{ "wps_er_config", wpa_cli_cmd_wps_er_config,
+	{ "wps_er_config", wpa_cli_cmd_wps_er_config, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<UUID> <PIN> <SSID> <auth> <encr> <key> = configure AP" },
-	{ "ibss_rsn", wpa_cli_cmd_ibss_rsn,
+#ifdef CONFIG_WPS_NFC
+	{ "wps_er_nfc_config_token", wpa_cli_cmd_wps_er_nfc_config_token, NULL,
+	  cli_cmd_flag_none,
+	  "<WPS/NDEF> <UUID> = build NFC configuration token" },
+#endif /* CONFIG_WPS_NFC */
+	{ "ibss_rsn", wpa_cli_cmd_ibss_rsn, NULL,
 	  cli_cmd_flag_none,
 	  "<addr> = request RSN authentication with <addr> in IBSS" },
 #ifdef CONFIG_AP
-	{ "sta", wpa_cli_cmd_sta,
+	{ "sta", wpa_cli_cmd_sta, NULL,
 	  cli_cmd_flag_none,
 	  "<addr> = get information about an associated station (AP)" },
-	{ "all_sta", wpa_cli_cmd_all_sta,
+	{ "all_sta", wpa_cli_cmd_all_sta, NULL,
 	  cli_cmd_flag_none,
 	  "= get information about all associated stations (AP)" },
+	{ "deauthenticate", wpa_cli_cmd_deauthenticate, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = deauthenticate a station" },
+	{ "disassociate", wpa_cli_cmd_disassociate, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = disassociate a station" },
 #endif /* CONFIG_AP */
-	{ "suspend", wpa_cli_cmd_suspend, cli_cmd_flag_none,
+	{ "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none,
 	  "= notification of suspend/hibernate" },
-	{ "resume", wpa_cli_cmd_resume, cli_cmd_flag_none,
+	{ "resume", wpa_cli_cmd_resume, NULL, cli_cmd_flag_none,
 	  "= notification of resume/thaw" },
-	{ "drop_sa", wpa_cli_cmd_drop_sa, cli_cmd_flag_none,
+	{ "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none,
 	  "= drop SA without deauth/disassoc (test command)" },
-	{ "roam", wpa_cli_cmd_roam,
+	{ "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<addr> = roam to the specified BSS" },
 #ifdef CONFIG_P2P
-	{ "p2p_find", wpa_cli_cmd_p2p_find, cli_cmd_flag_none,
+	{ "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,
+	  cli_cmd_flag_none,
 	  "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" },
-	{ "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, cli_cmd_flag_none,
+	{ "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, NULL, cli_cmd_flag_none,
 	  "= stop P2P Devices search" },
-	{ "p2p_connect", wpa_cli_cmd_p2p_connect, cli_cmd_flag_none,
-	  "<addr> <\"pbc\"|PIN> = connect to a P2P Devices" },
-	{ "p2p_listen", wpa_cli_cmd_p2p_listen, cli_cmd_flag_none,
+	{ "p2p_connect", wpa_cli_cmd_p2p_connect, wpa_cli_complete_p2p_connect,
+	  cli_cmd_flag_none,
+	  "<addr> <\"pbc\"|PIN> [ht40] = connect to a P2P Device" },
+	{ "p2p_listen", wpa_cli_cmd_p2p_listen, NULL, cli_cmd_flag_none,
 	  "[timeout] = listen for P2P Devices for up-to timeout seconds" },
-	{ "p2p_group_remove", wpa_cli_cmd_p2p_group_remove, cli_cmd_flag_none,
+	{ "p2p_group_remove", wpa_cli_cmd_p2p_group_remove,
+	  wpa_cli_complete_p2p_group_remove, cli_cmd_flag_none,
 	  "<ifname> = remove P2P group interface (terminate group if GO)" },
-	{ "p2p_group_add", wpa_cli_cmd_p2p_group_add, cli_cmd_flag_none,
-	  "= add a new P2P group (local end as GO)" },
-	{ "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, cli_cmd_flag_none,
+	{ "p2p_group_add", wpa_cli_cmd_p2p_group_add, NULL, cli_cmd_flag_none,
+	  "[ht40] = add a new P2P group (local end as GO)" },
+	{ "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc,
+	  wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
 	  "<addr> <method> = request provisioning discovery" },
-	{ "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase,
+	{ "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, NULL,
 	  cli_cmd_flag_none,
 	  "= get the passphrase for a group (GO only)" },
 	{ "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req,
-	  cli_cmd_flag_none,
+	  wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
 	  "<addr> <TLVs> = schedule service discovery request" },
 	{ "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req,
-	  cli_cmd_flag_none,
+	  NULL, cli_cmd_flag_none,
 	  "<id> = cancel pending service discovery request" },
-	{ "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp,
+	{ "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, NULL,
 	  cli_cmd_flag_none,
 	  "<freq> <addr> <dialog token> <TLVs> = service discovery response" },
-	{ "p2p_service_update", wpa_cli_cmd_p2p_service_update,
+	{ "p2p_service_update", wpa_cli_cmd_p2p_service_update, NULL,
 	  cli_cmd_flag_none,
 	  "= indicate change in local services" },
-	{ "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external,
+	{ "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, NULL,
 	  cli_cmd_flag_none,
 	  "<external> = set external processing of service discovery" },
-	{ "p2p_service_flush", wpa_cli_cmd_p2p_service_flush,
+	{ "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, NULL,
 	  cli_cmd_flag_none,
 	  "= remove all stored service entries" },
-	{ "p2p_service_add", wpa_cli_cmd_p2p_service_add,
+	{ "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL,
 	  cli_cmd_flag_none,
 	  "<bonjour|upnp> <query|version> <response|service> = add a local "
 	  "service" },
-	{ "p2p_service_del", wpa_cli_cmd_p2p_service_del,
+	{ "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL,
 	  cli_cmd_flag_none,
 	  "<bonjour|upnp> <query|version> [|service] = remove a local "
 	  "service" },
-	{ "p2p_reject", wpa_cli_cmd_p2p_reject,
+	{ "p2p_reject", wpa_cli_cmd_p2p_reject, wpa_cli_complete_p2p_peer,
 	  cli_cmd_flag_none,
 	  "<addr> = reject connection attempts from a specific peer" },
-	{ "p2p_invite", wpa_cli_cmd_p2p_invite,
+	{ "p2p_invite", wpa_cli_cmd_p2p_invite, NULL,
 	  cli_cmd_flag_none,
 	  "<cmd> [peer=addr] = invite peer" },
-	{ "p2p_peers", wpa_cli_cmd_p2p_peers, cli_cmd_flag_none,
+	{ "p2p_peers", wpa_cli_cmd_p2p_peers, NULL, cli_cmd_flag_none,
 	  "[discovered] = list known (optionally, only fully discovered) P2P "
 	  "peers" },
-	{ "p2p_peer", wpa_cli_cmd_p2p_peer, cli_cmd_flag_none,
+	{ "p2p_peer", wpa_cli_cmd_p2p_peer, wpa_cli_complete_p2p_peer,
+	  cli_cmd_flag_none,
 	  "<address> = show information about known P2P peer" },
-	{ "p2p_set", wpa_cli_cmd_p2p_set, cli_cmd_flag_none,
+	{ "p2p_set", wpa_cli_cmd_p2p_set, NULL, cli_cmd_flag_none,
 	  "<field> <value> = set a P2P parameter" },
-	{ "p2p_flush", wpa_cli_cmd_p2p_flush, cli_cmd_flag_none,
+	{ "p2p_flush", wpa_cli_cmd_p2p_flush, NULL, cli_cmd_flag_none,
 	  "= flush P2P state" },
-	{ "p2p_cancel", wpa_cli_cmd_p2p_cancel, cli_cmd_flag_none,
+	{ "p2p_cancel", wpa_cli_cmd_p2p_cancel, NULL, cli_cmd_flag_none,
 	  "= cancel P2P group formation" },
-	{ "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize, cli_cmd_flag_none,
+	{ "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize,
+	  wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
 	  "<address> = unauthorize a peer" },
-	{ "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, cli_cmd_flag_none,
+	{ "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, NULL,
+	  cli_cmd_flag_none,
 	  "[<duration> <interval>] [<duration> <interval>] = request GO "
 	  "presence" },
-	{ "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, cli_cmd_flag_none,
+	{ "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, NULL,
+	  cli_cmd_flag_none,
 	  "[<period> <interval>] = set extended listen timing" },
 #endif /* CONFIG_P2P */
-
+#ifdef CONFIG_WIFI_DISPLAY
+	{ "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,
+	  cli_cmd_flag_none,
+	  "<subelem> [contents] = set Wi-Fi Display subelement" },
+	{ "wfd_subelem_get", wpa_cli_cmd_wfd_subelem_get, NULL,
+	  cli_cmd_flag_none,
+	  "<subelem> = get Wi-Fi Display subelement" },
+#endif /* CONFIG_WIFI_DISPLAY */
 #ifdef CONFIG_INTERWORKING
-	{ "fetch_anqp", wpa_cli_cmd_fetch_anqp, cli_cmd_flag_none,
+	{ "fetch_anqp", wpa_cli_cmd_fetch_anqp, NULL, cli_cmd_flag_none,
 	  "= fetch ANQP information for all APs" },
-	{ "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, cli_cmd_flag_none,
+	{ "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, NULL,
+	  cli_cmd_flag_none,
 	  "= stop fetch_anqp operation" },
-	{ "interworking_select", wpa_cli_cmd_interworking_select,
+	{ "interworking_select", wpa_cli_cmd_interworking_select, NULL,
 	  cli_cmd_flag_none,
 	  "[auto] = perform Interworking network selection" },
 	{ "interworking_connect", wpa_cli_cmd_interworking_connect,
-	  cli_cmd_flag_none,
+	  wpa_cli_complete_bss, cli_cmd_flag_none,
 	  "<BSSID> = connect using Interworking credentials" },
-	{ "anqp_get", wpa_cli_cmd_anqp_get, cli_cmd_flag_none,
+	{ "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
 	  "<addr> <info id>[,<info id>]... = request ANQP information" },
+	{ "gas_request", wpa_cli_cmd_gas_request, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
+	  "<addr> <AdvProtoID> [QueryReq] = GAS request" },
+	{ "gas_response_get", wpa_cli_cmd_gas_response_get,
+	  wpa_cli_complete_bss, cli_cmd_flag_none,
+	  "<addr> <dialog token> [start,len] = Fetch last GAS response" },
 #endif /* CONFIG_INTERWORKING */
-	{ "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, cli_cmd_flag_none,
+#ifdef CONFIG_HS20
+	{ "hs20_anqp_get", wpa_cli_cmd_hs20_anqp_get, wpa_cli_complete_bss,
+	  cli_cmd_flag_none,
+	  "<addr> <subtype>[,<subtype>]... = request HS 2.0 ANQP information"
+	},
+	{ "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list,
+	  wpa_cli_complete_bss, cli_cmd_flag_none,
+	  "<addr> <home realm> = get HS20 nai home realm list" },
+#endif /* CONFIG_HS20 */
+	{ "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL,
+	  cli_cmd_flag_none,
 	  "<0/1> = disable/enable automatic reconnection" },
-	{ "tdls_discover", wpa_cli_cmd_tdls_discover,
+	{ "tdls_discover", wpa_cli_cmd_tdls_discover, NULL,
 	  cli_cmd_flag_none,
 	  "<addr> = request TDLS discovery with <addr>" },
-	{ "tdls_setup", wpa_cli_cmd_tdls_setup,
+	{ "tdls_setup", wpa_cli_cmd_tdls_setup, NULL,
 	  cli_cmd_flag_none,
 	  "<addr> = request TDLS setup with <addr>" },
-	{ "tdls_teardown", wpa_cli_cmd_tdls_teardown,
+	{ "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL,
 	  cli_cmd_flag_none,
 	  "<addr> = tear down TDLS with <addr>" },
-	{ "signal_poll", wpa_cli_cmd_signal_poll,
+	{ "signal_poll", wpa_cli_cmd_signal_poll, NULL,
 	  cli_cmd_flag_none,
 	  "= get signal parameters" },
-	{ "reauthenticate", wpa_cli_cmd_reauthenticate, cli_cmd_flag_none,
+	{ "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, NULL,
+	  cli_cmd_flag_none,
+	  "= get TX/RX packet counters" },
+	{ "reauthenticate", wpa_cli_cmd_reauthenticate, NULL,
+	  cli_cmd_flag_none,
 	  "= trigger IEEE 802.1X/EAPOL reauthentication" },
+#ifdef CONFIG_AUTOSCAN
+	{ "autoscan", wpa_cli_cmd_autoscan, NULL, cli_cmd_flag_none,
+	  "[params] = Set or unset (if none) autoscan parameters" },
+#endif /* CONFIG_AUTOSCAN */
+	{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
+	  "<params..> = Sent unprocessed command" },
 #ifdef ANDROID
-	{ "driver", wpa_cli_cmd_driver,
+	{ "driver", wpa_cli_cmd_driver, NULL,
 	  cli_cmd_flag_none,
 	  "<command> = driver private commands" },
 #endif
-	{ NULL, NULL, cli_cmd_flag_none, NULL }
+	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
 
@@ -3076,12 +2713,14 @@
 }
 
 
-static void print_help(void)
+static void print_help(const char *cmd)
 {
 	int n;
 	printf("commands:\n");
-	for (n = 0; wpa_cli_commands[n].cmd; n++)
-		print_cmd_help(&wpa_cli_commands[n], "  ");
+	for (n = 0; wpa_cli_commands[n].cmd; n++) {
+		if (cmd == NULL || str_starts(wpa_cli_commands[n].cmd, cmd))
+			print_cmd_help(&wpa_cli_commands[n], "  ");
+	}
 }
 
 
@@ -3112,7 +2751,7 @@
 	int i, count;
 
 	count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]);
-	res = os_zalloc(count * sizeof(char *));
+	res = os_calloc(count, sizeof(char *));
 	if (res == NULL)
 		return NULL;
 
@@ -3131,19 +2770,11 @@
 {
 	int i;
 
-	if (os_strcasecmp(cmd, "bss") == 0)
-		return wpa_cli_complete_bss(str, pos);
-#ifdef CONFIG_P2P
-	if (os_strcasecmp(cmd, "p2p_connect") == 0)
-		return wpa_cli_complete_p2p_connect(str, pos);
-	if (os_strcasecmp(cmd, "p2p_peer") == 0)
-		return wpa_cli_complete_p2p_peer(str, pos);
-	if (os_strcasecmp(cmd, "p2p_group_remove") == 0)
-		return wpa_cli_complete_p2p_group_remove(str, pos);
-#endif /* CONFIG_P2P */
-
 	for (i = 0; wpa_cli_commands[i].cmd; i++) {
 		if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) {
+			if (wpa_cli_commands[i].completion)
+				return wpa_cli_commands[i].completion(str,
+								      pos);
 			edit_clear_line();
 			printf("\r%s\n", wpa_cli_commands[i].usage);
 			edit_redraw();
@@ -3354,7 +2985,14 @@
 static void wpa_cli_reconnect(void)
 {
 	wpa_cli_close_connection();
-	wpa_cli_open_connection(ctrl_ifname, 1);
+	if (wpa_cli_open_connection(ctrl_ifname, 1) < 0)
+		return;
+
+	if (interactive) {
+		edit_clear_line();
+		printf("\rConnection to wpa_supplicant re-established\n");
+		edit_redraw();
+	}
 }
 
 
@@ -3426,6 +3064,33 @@
 }
 
 
+static int check_terminating(const char *msg)
+{
+	const char *pos = msg;
+
+	if (*pos == '<') {
+		/* skip priority */
+		pos = os_strchr(pos, '>');
+		if (pos)
+			pos++;
+		else
+			pos = msg;
+	}
+
+	if (str_match(pos, WPA_EVENT_TERMINATING) && ctrl_conn) {
+		edit_clear_line();
+		printf("\rConnection to wpa_supplicant lost - trying to "
+		       "reconnect\n");
+		edit_redraw();
+		wpa_cli_attached = 0;
+		wpa_cli_close_connection();
+		return 1;
+	}
+
+	return 0;
+}
+
+
 static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor)
 {
 	if (ctrl_conn == NULL) {
@@ -3446,6 +3111,9 @@
 					printf("\r%s\n", buf);
 					edit_redraw();
 				}
+
+				if (interactive && check_terminating(buf) > 0)
+					return;
 			}
 		} else {
 			printf("Could not read pending message.\n");
@@ -3505,12 +3173,6 @@
 }
 
 
-static void wpa_cli_eloop_terminate(int sig, void *signal_ctx)
-{
-	eloop_terminate();
-}
-
-
 static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx)
 {
 	wpa_cli_recv_pending(mon_conn, 0);
@@ -3533,11 +3195,18 @@
 }
 
 
-static void wpa_cli_interactive(void)
-{
-	char *home, *hfile = NULL;
+static int warning_displayed = 0;
+static char *hfile = NULL;
+static int edit_started = 0;
 
-	printf("\nInteractive mode\n\n");
+static void start_edit(void)
+{
+	char *home;
+	char *ps = NULL;
+
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	ps = wpa_ctrl_get_remote_ifname(ctrl_conn);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
 	home = getenv("HOME");
 	if (home) {
@@ -3548,17 +3217,52 @@
 			os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
 	}
 
-	eloop_register_signal_terminate(wpa_cli_eloop_terminate, NULL);
-	edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb,
-		  wpa_cli_edit_completion_cb, NULL, hfile);
-	eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
+	if (edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb,
+		      wpa_cli_edit_completion_cb, NULL, hfile, ps) < 0) {
+		eloop_terminate();
+		return;
+	}
 
+	edit_started = 1;
+	eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
+}
+
+
+static void try_connection(void *eloop_ctx, void *timeout_ctx)
+{
+	if (ctrl_ifname == NULL)
+		ctrl_ifname = wpa_cli_get_default_ifname();
+
+	if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
+		if (!warning_displayed) {
+			printf("Could not connect to wpa_supplicant: "
+			       "%s - re-trying\n", ctrl_ifname);
+			warning_displayed = 1;
+		}
+		eloop_register_timeout(1, 0, try_connection, NULL, NULL);
+		return;
+	}
+
+	if (warning_displayed)
+		printf("Connection established.\n");
+
+	start_edit();
+}
+
+
+static void wpa_cli_interactive(void)
+{
+	printf("\nInteractive mode\n\n");
+
+	eloop_register_timeout(0, 0, try_connection, NULL, NULL);
 	eloop_run();
+	eloop_cancel_timeout(try_connection, NULL, NULL);
 
 	cli_txt_list_flush(&p2p_peers);
 	cli_txt_list_flush(&p2p_groups);
 	cli_txt_list_flush(&bsses);
-	edit_deinit(hfile, wpa_cli_edit_filter_history_cb);
+	if (edit_started)
+		edit_deinit(hfile, wpa_cli_edit_filter_history_cb);
 	os_free(hfile);
 	eloop_cancel_timeout(wpa_cli_ping, NULL, NULL);
 	wpa_cli_close_connection();
@@ -3617,10 +3321,10 @@
 	os_program_deinit();
 }
 
-static void wpa_cli_terminate(int sig)
+
+static void wpa_cli_terminate(int sig, void *ctx)
 {
-	wpa_cli_cleanup();
-	exit(0);
+	eloop_terminate();
 }
 
 
@@ -3694,7 +3398,6 @@
 
 int main(int argc, char *argv[])
 {
-	int warning_displayed = 0;
 	int c;
 	int daemonize = 0;
 	int ret = 0;
@@ -3757,41 +3460,26 @@
 		ctrl_conn = wpa_ctrl_open(global);
 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
 		if (ctrl_conn == NULL) {
-			perror("Failed to connect to wpa_supplicant - "
-			       "wpa_ctrl_open");
+			fprintf(stderr, "Failed to connect to wpa_supplicant "
+				"global interface: %s  error: %s\n",
+				global, strerror(errno));
 			return -1;
 		}
 	}
 
-#ifndef _WIN32_WCE
-	signal(SIGINT, wpa_cli_terminate);
-	signal(SIGTERM, wpa_cli_terminate);
-#endif /* _WIN32_WCE */
+	eloop_register_signal_terminate(wpa_cli_terminate, NULL);
 
 	if (ctrl_ifname == NULL)
 		ctrl_ifname = wpa_cli_get_default_ifname();
 
 	if (interactive) {
-		for (; !global;) {
-			if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
-				if (warning_displayed)
-					printf("Connection established.\n");
-				break;
-			}
-
-			if (!warning_displayed) {
-				printf("Could not connect to wpa_supplicant - "
-				       "re-trying\n");
-				warning_displayed = 1;
-			}
-			os_sleep(1, 0);
-			continue;
-		}
+		wpa_cli_interactive();
 	} else {
 		if (!global &&
 		    wpa_cli_open_connection(ctrl_ifname, 0) < 0) {
-			perror("Failed to connect to wpa_supplicant - "
-			       "wpa_ctrl_open");
+			fprintf(stderr, "Failed to connect to non-global "
+				"ctrl_ifname: %s  error: %s\n",
+				ctrl_ifname, strerror(errno));
 			return -1;
 		}
 
@@ -3804,18 +3492,17 @@
 				return -1;
 			}
 		}
+
+		if (daemonize && os_daemonize(pid_file))
+			return -1;
+
+		if (action_file)
+			wpa_cli_action(ctrl_conn);
+		else
+			ret = wpa_request(ctrl_conn, argc - optind,
+					  &argv[optind]);
 	}
 
-	if (daemonize && os_daemonize(pid_file))
-		return -1;
-
-	if (interactive)
-		wpa_cli_interactive();
-	else if (action_file)
-		wpa_cli_action(ctrl_conn);
-	else
-		ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]);
-
 	os_free(ctrl_ifname);
 	eloop_destroy();
 	wpa_cli_cleanup();
diff --git a/wpa_supplicant/wpa_gui-qt4/signalbar.cpp b/wpa_supplicant/wpa_gui-qt4/signalbar.cpp
index f2688d5..2bba582 100644
--- a/wpa_supplicant/wpa_gui-qt4/signalbar.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/signalbar.cpp
@@ -2,14 +2,8 @@
  * wpa_gui - SignalBar class
  * Copyright (c) 2011, Kel Modderman <kel@otaku42.de>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include <cstdio>
diff --git a/wpa_supplicant/wpa_gui-qt4/signalbar.h b/wpa_supplicant/wpa_gui-qt4/signalbar.h
index 3d5dec1..37da5dd 100644
--- a/wpa_supplicant/wpa_gui-qt4/signalbar.h
+++ b/wpa_supplicant/wpa_gui-qt4/signalbar.h
@@ -2,14 +2,8 @@
  * wpa_gui - SignalBar class
  * Copyright (c) 2011, Kel Modderman <kel@otaku42.de>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef SIGNALBAR_H
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
index 97211e4..42e14f0 100644
--- a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
@@ -6,16 +6,12 @@
  * See README for more details.
  */
 
-#ifdef __MINGW32__
-/* Need to get getopt() */
-#include <unistd.h>
-#endif
-
 #ifdef CONFIG_NATIVE_WINDOWS
 #include <windows.h>
 #endif /* CONFIG_NATIVE_WINDOWS */
 
 #include <cstdio>
+#include <unistd.h>
 #include <QMessageBox>
 #include <QCloseEvent>
 #include <QImageReader>
@@ -713,17 +709,13 @@
 void WpaGui::helpAbout()
 {
 	QMessageBox::about(this, "wpa_gui for wpa_supplicant",
-			   "Copyright (c) 2003-2011,\n"
+			   "Copyright (c) 2003-2012,\n"
 			   "Jouni Malinen <j@w1.fi>\n"
 			   "and contributors.\n"
 			   "\n"
-			   "This program is free software. You can\n"
-			   "distribute it and/or modify it under the terms "
-			   "of\n"
-			   "the GNU General Public License version 2.\n"
-			   "\n"
-			   "Alternatively, this software may be distributed\n"
-			   "under the terms of the BSD license.\n"
+			   "This software may be distributed under\n"
+			   "the terms of the BSD license.\n"
+			   "See README for more details.\n"
 			   "\n"
 			   "This product includes software developed\n"
 			   "by the OpenSSL Project for use in the\n"
diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c
index a9ec236..9b568f0 100644
--- a/wpa_supplicant/wpa_passphrase.c
+++ b/wpa_supplicant/wpa_passphrase.c
@@ -52,7 +52,7 @@
 		return 1;
 	}
 
-	pbkdf2_sha1(passphrase, ssid, os_strlen(ssid), 4096, psk, 32);
+	pbkdf2_sha1(passphrase, (u8 *) ssid, os_strlen(ssid), 4096, psk, 32);
 
 	printf("network={\n");
 	printf("\tssid=\"%s\"\n", ssid);
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 74476e2..ad6a080 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -643,7 +643,7 @@
 			}
 			if (bind(iface->fd, (struct sockaddr *) &addr,
 				 sizeof(addr)) < 0) {
-				perror("bind(PF_UNIX)");
+				perror("wpa-priv-iface-init: bind(PF_UNIX)");
 				goto fail;
 			}
 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index e1ad4d9..d8f3c44 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -21,6 +21,7 @@
 #include "rsn_supp/wpa.h"
 #include "eloop.h"
 #include "config.h"
+#include "utils/ext_password.h"
 #include "l2_packet/l2_packet.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
@@ -40,11 +41,14 @@
 #include "gas_query.h"
 #include "ap.h"
 #include "p2p_supplicant.h"
+#include "wifi_display.h"
 #include "notify.h"
 #include "bgscan.h"
+#include "autoscan.h"
 #include "bss.h"
 #include "scan.h"
 #include "offchannel.h"
+#include "hs20_supplicant.h"
 
 const char *wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
@@ -151,6 +155,11 @@
 		keylen = 16;
 		alg = WPA_ALG_CCMP;
 		break;
+	case WPA_CIPHER_GCMP:
+		os_memcpy(key, ssid->psk, 16);
+		keylen = 16;
+		alg = WPA_ALG_GCMP;
+		break;
 	case WPA_CIPHER_TKIP:
 		/* WPA-None uses the same Michael MIC key for both TX and RX */
 		os_memcpy(key, ssid->psk, 16 + 8);
@@ -189,6 +198,17 @@
 	 * So, wait a second until scanning again.
 	 */
 	wpa_supplicant_req_scan(wpa_s, 1, 0);
+
+#ifdef CONFIG_P2P
+	if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
+	    wpa_s->global->p2p != NULL) {
+		wpa_s->global->p2p_cb_on_scan_complete = 0;
+		if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
+				"continued after timed out authentication");
+		}
+	}
+#endif /* CONFIG_P2P */
 }
 
 
@@ -348,7 +368,7 @@
 }
 
 
-static void free_hw_features(struct wpa_supplicant *wpa_s)
+void free_hw_features(struct wpa_supplicant *wpa_s)
 {
 	int i;
 	if (wpa_s->hw.modes == NULL)
@@ -367,6 +387,7 @@
 static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 {
 	bgscan_deinit(wpa_s);
+	autoscan_deinit(wpa_s);
 	scard_deinit(wpa_s->scard);
 	wpa_s->scard = NULL;
 	wpa_sm_set_scard_ctx(wpa_s->wpa, NULL);
@@ -445,6 +466,19 @@
 	wpa_s->gas = NULL;
 
 	free_hw_features(wpa_s);
+
+	os_free(wpa_s->bssid_filter);
+	wpa_s->bssid_filter = NULL;
+
+	wnm_bss_keep_alive_deinit(wpa_s);
+
+	ext_password_deinit(wpa_s->ext_pw);
+	wpa_s->ext_pw = NULL;
+
+	wpabuf_free(wpa_s->last_gas_resp);
+
+	os_free(wpa_s->last_scan_res);
+	wpa_s->last_scan_res = NULL;
 }
 
 
@@ -546,8 +580,16 @@
 			 * optimization, so the initial connection is not
 			 * affected.
 			 */
-		} else
+		} else {
+			struct wpa_scan_results *scan_res;
 			wpa_s->bgscan_ssid = wpa_s->current_ssid;
+			scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL,
+								   0);
+			if (scan_res) {
+				bgscan_notify_scan(wpa_s, scan_res);
+				wpa_scan_results_free(scan_res);
+			}
+		}
 	} else
 		wpa_s->bgscan_ssid = NULL;
 }
@@ -564,6 +606,29 @@
 #endif /* CONFIG_BGSCAN */
 
 
+static void wpa_supplicant_start_autoscan(struct wpa_supplicant *wpa_s)
+{
+	if (autoscan_init(wpa_s, 0))
+		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize autoscan");
+}
+
+
+static void wpa_supplicant_stop_autoscan(struct wpa_supplicant *wpa_s)
+{
+	autoscan_deinit(wpa_s);
+}
+
+
+void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->wpa_state == WPA_DISCONNECTED ||
+	    wpa_s->wpa_state == WPA_SCANNING) {
+		autoscan_deinit(wpa_s);
+		wpa_supplicant_start_autoscan(wpa_s);
+	}
+}
+
+
 /**
  * wpa_supplicant_set_state - Set current connection state
  * @wpa_s: Pointer to wpa_supplicant data
@@ -600,6 +665,7 @@
 			ssid ? ssid->id : -1,
 			ssid && ssid->id_str ? ssid->id_str : "");
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+		wpas_clear_temp_disabled(wpa_s, ssid, 1);
 		wpa_s->new_connection = 0;
 		wpa_s->reassociated_connection = 1;
 		wpa_drv_set_operstate(wpa_s, 1);
@@ -610,6 +676,8 @@
 #ifdef CONFIG_P2P
 		wpas_p2p_completed(wpa_s);
 #endif /* CONFIG_P2P */
+
+		sme_sched_obss_scan(wpa_s, 1);
 	} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
 		   state == WPA_ASSOCIATED) {
 		wpa_s->new_connection = 1;
@@ -617,6 +685,7 @@
 #ifndef IEEE8021X_EAPOL
 		wpa_drv_set_supp_port(wpa_s, 0);
 #endif /* IEEE8021X_EAPOL */
+		sme_sched_obss_scan(wpa_s, 0);
 	}
 	wpa_s->wpa_state = state;
 
@@ -627,6 +696,12 @@
 		wpa_supplicant_stop_bgscan(wpa_s);
 #endif /* CONFIG_BGSCAN */
 
+	if (state == WPA_AUTHENTICATING)
+		wpa_supplicant_stop_autoscan(wpa_s);
+
+	if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
+		wpa_supplicant_start_autoscan(wpa_s);
+
 	if (wpa_s->wpa_state != old_state) {
 		wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
 
@@ -670,7 +745,7 @@
 	wpa_s->mgmt_group_cipher = 0;
 	wpa_s->key_mgmt = 0;
 	if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
-		wpa_s->wpa_state = WPA_DISCONNECTED;
+		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 
 	if (wpa_s->wpa_state != old_state)
 		wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
@@ -749,7 +824,7 @@
 	wpa_supplicant_update_config(wpa_s);
 
 	wpa_supplicant_clear_status(wpa_s);
-	if (wpa_supplicant_enabled_networks(wpa_s->conf)) {
+	if (wpa_supplicant_enabled_networks(wpa_s)) {
 		wpa_s->reassociate = 1;
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
 	}
@@ -783,6 +858,8 @@
 		return CIPHER_WEP104;
 	case WPA_CIPHER_CCMP:
 		return CIPHER_CCMP;
+	case WPA_CIPHER_GCMP:
+		return CIPHER_GCMP;
 	case WPA_CIPHER_TKIP:
 	default:
 		return CIPHER_TKIP;
@@ -959,6 +1036,9 @@
 	if (sel & WPA_CIPHER_CCMP) {
 		wpa_s->group_cipher = WPA_CIPHER_CCMP;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP");
+	} else if (sel & WPA_CIPHER_GCMP) {
+		wpa_s->group_cipher = WPA_CIPHER_GCMP;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK GCMP");
 	} else if (sel & WPA_CIPHER_TKIP) {
 		wpa_s->group_cipher = WPA_CIPHER_TKIP;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP");
@@ -978,6 +1058,9 @@
 	if (sel & WPA_CIPHER_CCMP) {
 		wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP");
+	} else if (sel & WPA_CIPHER_GCMP) {
+		wpa_s->pairwise_cipher = WPA_CIPHER_GCMP;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK GCMP");
 	} else if (sel & WPA_CIPHER_TKIP) {
 		wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP");
@@ -1059,13 +1142,70 @@
 		if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
 		    ssid->passphrase) {
 			u8 psk[PMK_LEN];
-		        pbkdf2_sha1(ssid->passphrase, (char *) bss->ssid,
-				    bss->ssid_len, 4096, psk, PMK_LEN);
+		        pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len,
+				    4096, psk, PMK_LEN);
 		        wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
 					psk, PMK_LEN);
 			wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
 		}
 #endif /* CONFIG_NO_PBKDF2 */
+#ifdef CONFIG_EXT_PASSWORD
+		if (ssid->ext_psk) {
+			struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
+							     ssid->ext_psk);
+			char pw_str[64 + 1];
+			u8 psk[PMK_LEN];
+
+			if (pw == NULL) {
+				wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK "
+					"found from external storage");
+				return -1;
+			}
+
+			if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
+				wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected "
+					"PSK length %d in external storage",
+					(int) wpabuf_len(pw));
+				ext_password_free(pw);
+				return -1;
+			}
+
+			os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
+			pw_str[wpabuf_len(pw)] = '\0';
+
+#ifndef CONFIG_NO_PBKDF2
+			if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
+			{
+				pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len,
+					    4096, psk, PMK_LEN);
+				os_memset(pw_str, 0, sizeof(pw_str));
+				wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
+						"external passphrase)",
+						psk, PMK_LEN);
+				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+			} else
+#endif /* CONFIG_NO_PBKDF2 */
+			if (wpabuf_len(pw) == 2 * PMK_LEN) {
+				if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
+					wpa_msg(wpa_s, MSG_INFO, "EXT PW: "
+						"Invalid PSK hex string");
+					os_memset(pw_str, 0, sizeof(pw_str));
+					ext_password_free(pw);
+					return -1;
+				}
+				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+			} else {
+				wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
+					"PSK available");
+				os_memset(pw_str, 0, sizeof(pw_str));
+				ext_password_free(pw);
+				return -1;
+			}
+
+			os_memset(pw_str, 0, sizeof(pw_str));
+			ext_password_free(pw);
+		}
+#endif /* CONFIG_EXT_PASSWORD */
 	} else
 		wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
 
@@ -1115,7 +1255,10 @@
 				"mode");
 			return;
 		}
-		wpa_supplicant_create_ap(wpa_s, ssid);
+		if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) {
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+			return;
+		}
 		wpa_s->current_bss = bss;
 #else /* CONFIG_AP */
 		wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in "
@@ -1217,6 +1360,16 @@
 				"key management and encryption suites");
 			return;
 		}
+	} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
+		   wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
+		/*
+		 * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
+		 * use non-WPA since the scan results did not indicate that the
+		 * AP is using WPA or WPA2.
+		 */
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		wpa_ie_len = 0;
+		wpa_s->wpa_proto = 0;
 	} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
 		wpa_ie_len = sizeof(wpa_ie);
 		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
@@ -1254,11 +1407,10 @@
 		u8 *pos;
 		size_t len;
 		int res;
-		int p2p_group;
-		p2p_group = wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE;
 		pos = wpa_ie + wpa_ie_len;
 		len = sizeof(wpa_ie) - wpa_ie_len;
-		res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len, p2p_group);
+		res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
+					    ssid->p2p_group);
 		if (res >= 0)
 			wpa_ie_len += res;
 	}
@@ -1279,6 +1431,20 @@
 	}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_HS20
+	if (wpa_s->conf->hs20) {
+		struct wpabuf *hs20;
+		hs20 = wpabuf_alloc(20);
+		if (hs20) {
+			wpas_hs20_add_indication(hs20);
+			os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20),
+				  wpabuf_len(hs20));
+			wpa_ie_len += wpabuf_len(hs20);
+			wpabuf_free(hs20);
+		}
+	}
+#endif /* CONFIG_HS20 */
+
 #ifdef CONFIG_INTERWORKING
 	if (wpa_s->conf->interworking) {
 		u8 *pos = wpa_ie;
@@ -1336,7 +1502,12 @@
 	if (bss) {
 		params.ssid = bss->ssid;
 		params.ssid_len = bss->ssid_len;
-		if (!wpas_driver_bss_selection(wpa_s)) {
+		if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
+			wpa_printf(MSG_DEBUG, "Limit connection to BSSID "
+				   MACSTR " freq=%u MHz based on scan results "
+				   "(bssid_set=%d)",
+				   MAC2STR(bss->bssid), bss->freq,
+				   ssid->bssid_set);
 			params.bssid = bss->bssid;
 			params.freq = bss->freq;
 		}
@@ -1362,6 +1533,7 @@
 	params.wpa_proto = wpa_s->wpa_proto;
 	params.auth_alg = algs;
 	params.mode = ssid->mode;
+	params.bg_scan_period = ssid->bg_scan_period;
 	for (i = 0; i < NUM_WEP_KEYS; i++) {
 		if (ssid->wep_key_len[i])
 			params.wep_key[i] = ssid->wep_key[i];
@@ -1441,6 +1613,7 @@
 			 * succeed.
 			 */
 			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 			os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 			return;
 		}
@@ -1510,10 +1683,8 @@
 	struct wpa_ssid *old_ssid;
 
 	wpa_clear_keys(wpa_s, addr);
-	wpa_supplicant_mark_disassoc(wpa_s);
 	old_ssid = wpa_s->current_ssid;
-	wpa_s->current_ssid = NULL;
-	wpa_s->current_bss = NULL;
+	wpa_supplicant_mark_disassoc(wpa_s);
 	wpa_sm_set_config(wpa_s->wpa, NULL);
 	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 	if (old_ssid != wpa_s->current_ssid)
@@ -1534,10 +1705,15 @@
 				 int reason_code)
 {
 	u8 *addr = NULL;
+	union wpa_event_data event;
 
 	if (!is_zero_ether_addr(wpa_s->bssid)) {
 		wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code);
 		addr = wpa_s->bssid;
+		os_memset(&event, 0, sizeof(event));
+		event.disassoc_info.reason_code = (u16) reason_code;
+		event.disassoc_info.locally_generated = 1;
+		wpa_supplicant_event(wpa_s, EVENT_DISASSOC, &event);
 	}
 
 	wpa_supplicant_clear_connection(wpa_s, addr);
@@ -1556,10 +1732,15 @@
 				   int reason_code)
 {
 	u8 *addr = NULL;
+	union wpa_event_data event;
 
 	if (!is_zero_ether_addr(wpa_s->bssid)) {
 		wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, reason_code);
 		addr = wpa_s->bssid;
+		os_memset(&event, 0, sizeof(event));
+		event.deauth_info.reason_code = (u16) reason_code;
+		event.deauth_info.locally_generated = 1;
+		wpa_supplicant_event(wpa_s, EVENT_DEAUTH, &event);
 	}
 
 	wpa_supplicant_clear_connection(wpa_s, addr);
@@ -1592,6 +1773,8 @@
 			was_disabled = other_ssid->disabled;
 
 			other_ssid->disabled = 0;
+			if (was_disabled)
+				wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
 
 			if (was_disabled != other_ssid->disabled)
 				wpas_notify_network_enabled_changed(
@@ -1612,6 +1795,7 @@
 		was_disabled = ssid->disabled;
 
 		ssid->disabled = 0;
+		wpas_clear_temp_disabled(wpa_s, ssid, 1);
 
 		if (was_disabled != ssid->disabled)
 			wpas_notify_network_enabled_changed(wpa_s, ssid);
@@ -1682,6 +1866,9 @@
 		disconnected = 1;
 	}
 
+	if (ssid)
+		wpas_clear_temp_disabled(wpa_s, ssid, 1);
+
 	/*
 	 * Mark all other networks disabled or mark all networks enabled if no
 	 * network specified.
@@ -1693,6 +1880,8 @@
 			continue; /* do not change persistent P2P group data */
 
 		other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0;
+		if (was_disabled && !other_ssid->disabled)
+			wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
 
 		if (was_disabled != other_ssid->disabled)
 			wpas_notify_network_enabled_changed(wpa_s, other_ssid);
@@ -1799,6 +1988,29 @@
 
 
 /**
+ * wpa_supplicant_set_scan_interval - Set scan interval
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @scan_interval: scan interval in seconds
+ * Returns: 0 if succeed or -1 if scan_interval has an invalid value
+ *
+ */
+int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
+				     int scan_interval)
+{
+	if (scan_interval < 0) {
+		wpa_msg(wpa_s, MSG_ERROR, "Invalid scan interval %d",
+			scan_interval);
+		return -1;
+	}
+	wpa_msg(wpa_s, MSG_DEBUG, "Setting scan interval: %d sec",
+		scan_interval);
+	wpa_s->scan_interval = scan_interval;
+
+	return 0;
+}
+
+
+/**
  * wpa_supplicant_set_debug_params - Set global debug params
  * @global: wpa_global structure
  * @debug_level: debug level
@@ -1873,14 +2085,14 @@
 
 	entry = wpa_s->conf->ssid;
 	while (entry) {
-		if (!entry->disabled &&
+		if (!wpas_network_disabled(wpa_s, entry) &&
 		    ((ssid_len == entry->ssid_len &&
 		      os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) &&
 		    (!entry->bssid_set ||
 		     os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
 			return entry;
 #ifdef CONFIG_WPS
-		if (!entry->disabled &&
+		if (!wpas_network_disabled(wpa_s, entry) &&
 		    (entry->key_mgmt & WPA_KEY_MGMT_WPS) &&
 		    (entry->ssid == NULL || entry->ssid_len == 0) &&
 		    (!entry->bssid_set ||
@@ -1888,7 +2100,7 @@
 			return entry;
 #endif /* CONFIG_WPS */
 
-		if (!entry->disabled && entry->bssid_set &&
+		if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set &&
 		    entry->ssid_len == 0 &&
 		    os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
 			return entry;
@@ -2111,6 +2323,31 @@
 }
 
 
+static void wpa_supplicant_rx_eapol_bridge(void *ctx, const u8 *src_addr,
+					   const u8 *buf, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const struct l2_ethhdr *eth;
+
+	if (len < sizeof(*eth))
+		return;
+	eth = (const struct l2_ethhdr *) buf;
+
+	if (os_memcmp(eth->h_dest, wpa_s->own_addr, ETH_ALEN) != 0 &&
+	    !(eth->h_dest[0] & 0x01)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
+			" (bridge - not for this interface - ignore)",
+			MAC2STR(src_addr), MAC2STR(eth->h_dest));
+		return;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
+		" (bridge)", MAC2STR(src_addr), MAC2STR(eth->h_dest));
+	wpa_supplicant_rx_eapol(wpa_s, src_addr, buf + sizeof(*eth),
+				len - sizeof(*eth));
+}
+
+
 /**
  * wpa_supplicant_driver_init - Initialize driver interface parameters
  * @wpa_s: Pointer to wpa_supplicant data
@@ -2133,8 +2370,8 @@
 		wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname,
 					      wpa_s->own_addr,
 					      ETH_P_EAPOL,
-					      wpa_supplicant_rx_eapol, wpa_s,
-					      0);
+					      wpa_supplicant_rx_eapol_bridge,
+					      wpa_s, 1);
 		if (wpa_s->l2_br == NULL) {
 			wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet "
 				"connection for the bridge interface '%s'",
@@ -2155,7 +2392,7 @@
 	wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
 	wpa_s->prev_scan_wildcard = 0;
 
-	if (wpa_supplicant_enabled_networks(wpa_s->conf)) {
+	if (wpa_supplicant_enabled_networks(wpa_s)) {
 		if (wpa_supplicant_delayed_sched_scan(wpa_s, interface_count,
 						      100000))
 			wpa_supplicant_req_scan(wpa_s, interface_count,
@@ -2369,6 +2606,80 @@
 #endif /* CONFIG_HT_OVERRIDES */
 
 
+static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
+{
+#ifdef PCSC_FUNCS
+	size_t len;
+
+	if (!wpa_s->conf->pcsc_reader)
+		return 0;
+
+	wpa_s->scard = scard_init(SCARD_TRY_BOTH, wpa_s->conf->pcsc_reader);
+	if (!wpa_s->scard)
+		return 1;
+
+	if (wpa_s->conf->pcsc_pin &&
+	    scard_set_pin(wpa_s->scard, wpa_s->conf->pcsc_pin) < 0) {
+		scard_deinit(wpa_s->scard);
+		wpa_s->scard = NULL;
+		wpa_msg(wpa_s, MSG_ERROR, "PC/SC PIN validation failed");
+		return -1;
+	}
+
+	len = sizeof(wpa_s->imsi) - 1;
+	if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) {
+		scard_deinit(wpa_s->scard);
+		wpa_s->scard = NULL;
+		wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI");
+		return -1;
+	}
+	wpa_s->imsi[len] = '\0';
+
+	wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard);
+
+	wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)",
+		   wpa_s->imsi, wpa_s->mnc_len);
+
+	wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
+	eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+#endif /* PCSC_FUNCS */
+
+	return 0;
+}
+
+
+int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
+{
+	char *val, *pos;
+
+	ext_password_deinit(wpa_s->ext_pw);
+	wpa_s->ext_pw = NULL;
+	eapol_sm_set_ext_pw_ctx(wpa_s->eapol, NULL);
+
+	if (!wpa_s->conf->ext_password_backend)
+		return 0;
+
+	val = os_strdup(wpa_s->conf->ext_password_backend);
+	if (val == NULL)
+		return -1;
+	pos = os_strchr(val, ':');
+	if (pos)
+		*pos++ = '\0';
+
+	wpa_printf(MSG_DEBUG, "EXT PW: Initialize backend '%s'", val);
+
+	wpa_s->ext_pw = ext_password_init(val, pos);
+	os_free(val);
+	if (wpa_s->ext_pw == NULL) {
+		wpa_printf(MSG_DEBUG, "EXT PW: Failed to initialize backend");
+		return -1;
+	}
+	eapol_sm_set_ext_pw_ctx(wpa_s->eapol, wpa_s->ext_pw);
+
+	return 0;
+}
+
+
 static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 				     struct wpa_interface *iface)
 {
@@ -2528,6 +2839,7 @@
 	if (wpa_drv_get_capa(wpa_s, &capa) == 0) {
 		wpa_s->drv_capa_known = 1;
 		wpa_s->drv_flags = capa.flags;
+		wpa_s->drv_enc = capa.enc;
 		wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
 		wpa_s->max_scan_ssids = capa.max_scan_ssids;
 		wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
@@ -2590,6 +2902,12 @@
 	if (wpa_bss_init(wpa_s) < 0)
 		return -1;
 
+	if (pcsc_reader_init(wpa_s) < 0)
+		return -1;
+
+	if (wpas_init_ext_pw(wpa_s) < 0)
+		return -1;
+
 	return 0;
 }
 
@@ -2607,6 +2925,14 @@
 
 	wpa_supplicant_cleanup(wpa_s);
 
+#ifdef CONFIG_P2P
+	if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
+			"the management interface is being removed");
+		wpas_p2p_deinit_global(wpa_s->global);
+	}
+#endif /* CONFIG_P2P */
+
 	if (wpa_s->drv_priv)
 		wpa_drv_deinit(wpa_s);
 
@@ -2693,6 +3019,7 @@
 	global->ifaces = wpa_s;
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname);
+	wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 
 	return wpa_s;
 }
@@ -2821,6 +3148,14 @@
 	wpa_debug_open_file(params->wpa_debug_file_path);
 	if (params->wpa_debug_syslog)
 		wpa_debug_open_syslog();
+	if (params->wpa_debug_tracing) {
+		ret = wpa_debug_open_linux_tracing();
+		if (ret) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to enable trace logging");
+			return NULL;
+		}
+	}
 
 	ret = eap_register_methods();
 	if (ret) {
@@ -2891,6 +3226,14 @@
 		return NULL;
 	}
 
+#ifdef CONFIG_WIFI_DISPLAY
+	if (wifi_display_init(global) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display");
+		wpa_supplicant_deinit(global);
+		return NULL;
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
 	return global;
 }
 
@@ -2942,6 +3285,9 @@
 	if (global == NULL)
 		return;
 
+#ifdef CONFIG_WIFI_DISPLAY
+	wifi_display_deinit(global);
+#endif /* CONFIG_WIFI_DISPLAY */
 #ifdef CONFIG_P2P
 	wpas_p2p_deinit_global(global);
 #endif /* CONFIG_P2P */
@@ -2978,9 +3324,12 @@
 	os_free(global->params.override_driver);
 	os_free(global->params.override_ctrl_interface);
 
+	os_free(global->p2p_disallow_freq);
+
 	os_free(global);
 	wpa_debug_close_syslog();
 	wpa_debug_close_file();
+	wpa_debug_close_linux_tracing();
 }
 
 
@@ -2998,6 +3347,9 @@
 		}
 	}
 
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
+		wpas_init_ext_pw(wpa_s);
+
 #ifdef CONFIG_WPS
 	wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
@@ -3120,6 +3472,17 @@
 	 */
 	wpa_supplicant_req_scan(wpa_s, timeout / 1000,
 				1000 * (timeout % 1000));
+
+#ifdef CONFIG_P2P
+	if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
+	    wpa_s->global->p2p != NULL) {
+		wpa_s->global->p2p_cb_on_scan_complete = 0;
+		if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
+				"continued after failed association");
+		}
+	}
+#endif /* CONFIG_P2P */
 }
 
 
@@ -3129,15 +3492,183 @@
 		(wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION);
 }
 
-#ifdef ANDROID_P2P
+
+#if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW)
+int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+					      struct wpa_ssid *ssid,
+					      const char *field,
+					      const char *value)
+{
+#ifdef IEEE8021X_EAPOL
+	struct eap_peer_config *eap = &ssid->eap;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value",
+			      (const u8 *) value, os_strlen(value));
+
+	switch (wpa_supplicant_ctrl_req_from_string(field)) {
+	case WPA_CTRL_REQ_EAP_IDENTITY:
+		os_free(eap->identity);
+		eap->identity = (u8 *) os_strdup(value);
+		eap->identity_len = os_strlen(value);
+		eap->pending_req_identity = 0;
+		if (ssid == wpa_s->current_ssid)
+			wpa_s->reassociate = 1;
+		break;
+	case WPA_CTRL_REQ_EAP_PASSWORD:
+		os_free(eap->password);
+		eap->password = (u8 *) os_strdup(value);
+		eap->password_len = os_strlen(value);
+		eap->pending_req_password = 0;
+		if (ssid == wpa_s->current_ssid)
+			wpa_s->reassociate = 1;
+		break;
+	case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
+		os_free(eap->new_password);
+		eap->new_password = (u8 *) os_strdup(value);
+		eap->new_password_len = os_strlen(value);
+		eap->pending_req_new_password = 0;
+		if (ssid == wpa_s->current_ssid)
+			wpa_s->reassociate = 1;
+		break;
+	case WPA_CTRL_REQ_EAP_PIN:
+		os_free(eap->pin);
+		eap->pin = os_strdup(value);
+		eap->pending_req_pin = 0;
+		if (ssid == wpa_s->current_ssid)
+			wpa_s->reassociate = 1;
+		break;
+	case WPA_CTRL_REQ_EAP_OTP:
+		os_free(eap->otp);
+		eap->otp = (u8 *) os_strdup(value);
+		eap->otp_len = os_strlen(value);
+		os_free(eap->pending_req_otp);
+		eap->pending_req_otp = NULL;
+		eap->pending_req_otp_len = 0;
+		break;
+	case WPA_CTRL_REQ_EAP_PASSPHRASE:
+		os_free(eap->private_key_passwd);
+		eap->private_key_passwd = (u8 *) os_strdup(value);
+		eap->pending_req_passphrase = 0;
+		if (ssid == wpa_s->current_ssid)
+			wpa_s->reassociate = 1;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
+		return -1;
+	}
+
+	return 0;
+#else /* IEEE8021X_EAPOL */
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included");
+	return -1;
+#endif /* IEEE8021X_EAPOL */
+}
+#endif /* CONFIG_CTRL_IFACE || CONFIG_CTRL_IFACE_DBUS_NEW */
+
+
+int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	int i;
+	unsigned int drv_enc;
+
+	if (ssid == NULL)
+		return 1;
+
+	if (ssid->disabled)
+		return 1;
+
+	if (wpa_s && wpa_s->drv_capa_known)
+		drv_enc = wpa_s->drv_enc;
+	else
+		drv_enc = (unsigned int) -1;
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		size_t len = ssid->wep_key_len[i];
+		if (len == 0)
+			continue;
+		if (len == 5 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP40))
+			continue;
+		if (len == 13 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP104))
+			continue;
+		if (len == 16 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP128))
+			continue;
+		return 1; /* invalid WEP key */
+	}
+
+	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
+	    !ssid->ext_psk)
+		return 1;
+
+	return 0;
+}
+
+
 int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P)
 		return 1;
 	if (wpa_s->global->conc_pref == WPA_CONC_PREF_STA)
 		return 0;
-
-	/* IF conc_priority is not set, return -1 */
 	return -1;
 }
-#endif
+
+
+void wpas_auth_failed(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	int dur;
+	struct os_time now;
+
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "Authentication failure but no known "
+			   "SSID block");
+		return;
+	}
+
+	if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+		return;
+
+	ssid->auth_failures++;
+	if (ssid->auth_failures > 50)
+		dur = 300;
+	else if (ssid->auth_failures > 20)
+		dur = 120;
+	else if (ssid->auth_failures > 10)
+		dur = 60;
+	else if (ssid->auth_failures > 5)
+		dur = 30;
+	else if (ssid->auth_failures > 1)
+		dur = 20;
+	else
+		dur = 10;
+
+	os_get_time(&now);
+	if (now.sec + dur <= ssid->disabled_until.sec)
+		return;
+
+	ssid->disabled_until.sec = now.sec + dur;
+
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED
+		"id=%d ssid=\"%s\" auth_failures=%u duration=%d",
+		ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+		ssid->auth_failures, dur);
+}
+
+
+void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid, int clear_failures)
+{
+	if (ssid == NULL)
+		return;
+
+	if (ssid->disabled_until.sec) {
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REENABLED
+			"id=%d ssid=\"%s\"",
+			ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+	}
+	ssid->disabled_until.sec = 0;
+	ssid->disabled_until.usec = 0;
+	if (clear_failures)
+		ssid->auth_failures = 0;
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index d393015..0b0ea88 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -214,6 +214,22 @@
 #	to external program(s)
 #wps_cred_processing=0
 
+# Vendor attribute in WPS M1, e.g., Windows 7 Vertical Pairing
+# The vendor attribute contents to be added in M1 (hex string)
+#wps_vendor_ext_m1=000137100100020001
+
+# NFC password token for WPS
+# These parameters can be used to configure a fixed NFC password token for the
+# station. This can be generated, e.g., with nfc_pw_token. When these
+# parameters are used, the station is assumed to be deployed with a NFC tag
+# that includes the matching NFC password token (e.g., written based on the
+# NDEF record from nfc_pw_token).
+#
+#wps_nfc_dev_pw_id: Device Password ID (16..65535)
+#wps_nfc_dh_pubkey: Hexdump of DH Public Key
+#wps_nfc_dh_privkey: Hexdump of DH Private Key
+#wps_nfc_dev_pw: Hexdump of Device Password
+
 # Maximum number of BSS entries to keep in memory
 # Default: 200
 # This can be used to limit memory use on the BSS entries (cached scan
@@ -221,12 +237,34 @@
 # of APs when using ap_scan=1 mode.
 #bss_max_count=200
 
+# Automatic scan
+# This is an optional set of parameters for automatic scanning
+# within an interface in following format:
+#autoscan=<autoscan module name>:<module parameters>
+# autoscan is like bgscan but on disconnected or inactive state.
+# For instance, on exponential module parameters would be <base>:<limit>
+#autoscan=exponential:3:300
+# Which means a delay between scans on a base exponential of 3,
+# up to the limit of 300 seconds (3, 9, 27 ... 300)
+# For periodic module, parameters would be <fixed interval>
+#autoscan=periodic:30
+# So a delay of 30 seconds will be applied between each scan
 
 # filter_ssids - SSID-based scan result filtering
 # 0 = do not filter scan results (default)
 # 1 = only include configured SSIDs in scan results/BSS table
 #filter_ssids=0
 
+# Password (and passphrase, etc.) backend for external storage
+# format: <backend name>[:<optional backend parameters>]
+#ext_password_backend=test:pw1=password|pw2=testing
+
+# Timeout in seconds to detect STA inactivity (default: 300 seconds)
+#
+# This timeout value is used in P2P GO mode to clean up
+# inactive stations.
+#p2p_go_max_inactivity=300
+
 
 # Interworking (IEEE 802.11u)
 
@@ -239,23 +277,131 @@
 # is enabled.
 # hessid=00:11:22:33:44:55
 
-# Home Realm for Interworking
-#home_realm=example.com
+# Automatic network selection behavior
+# 0 = do not automatically go through Interworking network selection
+#     (i.e., require explicit interworking_select command for this; default)
+# 1 = perform Interworking network selection if one or more
+#     credentials have been configured and scan did not find a
+#     matching network block
+#auto_interworking=0
 
-# Username for Interworking network selection
-#home_username=user
+# credential block
+#
+# Each credential used for automatic network selection is configured as a set
+# of parameters that are compared to the information advertised by the APs when
+# interworking_select and interworking_connect commands are used.
+#
+# credential fields:
+#
+# priority: Priority group
+#	By default, all networks and credentials get the same priority group
+#	(0). This field can be used to give higher priority for credentials
+#	(and similarly in struct wpa_ssid for network blocks) to change the
+#	Interworking automatic networking selection behavior. The matching
+#	network (based on either an enabled network block or a credential)
+#	with the highest priority value will be selected.
+#
+# pcsc: Use PC/SC and SIM/USIM card
+#
+# realm: Home Realm for Interworking
+#
+# username: Username for Interworking network selection
+#
+# password: Password for Interworking network selection
+#
+# ca_cert: CA certificate for Interworking network selection
+#
+# client_cert: File path to client certificate file (PEM/DER)
+#	This field is used with Interworking networking selection for a case
+#	where client certificate/private key is used for authentication
+#	(EAP-TLS). Full path to the file should be used since working
+#	directory may change when wpa_supplicant is run in the background.
+#
+#	Alternatively, a named configuration blob can be used by setting
+#	this to blob://blob_name.
+#
+# private_key: File path to client private key file (PEM/DER/PFX)
+#	When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+#	commented out. Both the private key and certificate will be read
+#	from the PKCS#12 file in this case. Full path to the file should be
+#	used since working directory may change when wpa_supplicant is run
+#	in the background.
+#
+#	Windows certificate store can be used by leaving client_cert out and
+#	configuring private_key in one of the following formats:
+#
+#	cert://substring_to_match
+#
+#	hash://certificate_thumbprint_in_hex
+#
+#	For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+#
+#	Note that when running wpa_supplicant as an application, the user
+#	certificate store (My user account) is used, whereas computer store
+#	(Computer account) is used when running wpasvc as a service.
+#
+#	Alternatively, a named configuration blob can be used by setting
+#	this to blob://blob_name.
+#
+# private_key_passwd: Password for private key file
+#
+# imsi: IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+#
+# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
+#	format
+#
+# domain: Home service provider FQDN
+#	This is used to compare against the Domain Name List to figure out
+#	whether the AP is operated by the Home SP.
+#
+# roaming_consortium: Roaming Consortium OI
+#	If roaming_consortium_len is non-zero, this field contains the
+#	Roaming Consortium OI that can be used to determine which access
+#	points support authentication with this credential. This is an
+#	alternative to the use of the realm parameter. When using Roaming
+#	Consortium to match the network, the EAP parameters need to be
+#	pre-configured with the credential since the NAI Realm information
+#	may not be available or fetched.
+#
+# eap: Pre-configured EAP method
+#	This optional field can be used to specify which EAP method will be
+#	used with this credential. If not set, the EAP method is selected
+#	automatically based on ANQP information (e.g., NAI Realm).
+#
+# phase1: Pre-configure Phase 1 (outer authentication) parameters
+#	This optional field is used with like the 'eap' parameter.
+#
+# phase2: Pre-configure Phase 2 (inner authentication) parameters
+#	This optional field is used with like the 'eap' parameter.
+#
+# for example:
+#
+#cred={
+#	realm="example.com"
+#	username="user@example.com"
+#	password="password"
+#	ca_cert="/etc/wpa_supplicant/ca.pem"
+#	domain="example.com"
+#}
+#
+#cred={
+#	imsi="310026-000000000"
+#	milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82"
+#}
+#
+#cred={
+#	realm="example.com"
+#	username="user"
+#	password="password"
+#	ca_cert="/etc/wpa_supplicant/ca.pem"
+#	domain="example.com"
+#	roaming_consortium=223344
+#	eap=TTLS
+#	phase2="auth=MSCHAPV2"
+#}
 
-# Password for Interworking network selection
-#home_password=secret
-
-# CA certificate for Interworking network selection
-#home_ca_cert=/etc/cert/ca.pem
-
-# IMSI in <MCC> | <MNC> | '-' | <MSIN> format
-#home_imsi=232010000000000
-
-# Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN> format
-#home_milenage=90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123
+# Hotspot 2.0
+# hs20=1
 
 # network block
 #
@@ -274,8 +420,10 @@
 #	to external action script through wpa_cli as WPA_ID_STR environment
 #	variable to make it easier to do network specific configuration.
 #
-# ssid: SSID (mandatory); either as an ASCII string with double quotation or
-#	as hex string; network name
+# ssid: SSID (mandatory); network name in one of the optional formats:
+#	- an ASCII string with double quotation
+#	- a hex string (two characters per octet of SSID)
+#	- a printf-escaped ASCII string P"<escaped string>"
 #
 # scan_ssid:
 #	0 = do not scan this SSID with specific Probe Request frames (default)
@@ -341,6 +489,16 @@
 # WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms
 # If not set, this defaults to: WPA-PSK WPA-EAP
 #
+# ieee80211w: whether management frame protection is enabled
+# 0 = disabled (default)
+# 1 = optional
+# 2 = required
+# The most common configuration options for this based on the PMF (protected
+# management frames) certification program are:
+# PMF enabled: ieee80211w=1 and key_mgmt=WPA-EAP WPA-EAP-SHA256
+# PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256
+# (and similarly for WPA-PSK and WPA-WPSK-SHA256 if WPA2-Personal is used)
+#
 # auth_alg: list of allowed IEEE 802.11 authentication algorithms
 # OPEN = Open System authentication (required for WPA/WPA2)
 # SHARED = Shared Key authentication (requires static WEP keys)
@@ -366,7 +524,8 @@
 # The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e.,
 # 32 bytes or as an ASCII passphrase (in which case, the real PSK will be
 # generated using the passphrase and SSID). ASCII passphrase must be between
-# 8 and 63 characters (inclusive).
+# 8 and 63 characters (inclusive). ext:<name of external PSK field> format can
+# be used to indicate that the PSK/passphrase is stored in external storage.
 # This field is not needed, if WPA-EAP is used.
 # Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys
 # from ASCII passphrase. This process uses lot of CPU and wpa_supplicant
@@ -427,7 +586,8 @@
 #	EAP-PSK/PAX/SAKE/GPSK.
 # anonymous_identity: Anonymous identity string for EAP (to be used as the
 #	unencrypted identity with EAP types that support different tunnelled
-#	identity, e.g., EAP-TTLS)
+#	identity, e.g., EAP-TTLS). This field can also be used with
+#	EAP-SIM/AKA/AKA' to store the pseudonym identity.
 # password: Password string for EAP. This field can include either the
 #	plaintext password (using ASCII or hex string) or a NtPasswordHash
 #	(16-byte MD4 hash of password) in hash:<32 hex digits> format.
@@ -435,7 +595,8 @@
 #	MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
 #	EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit
 #	PSK) is also configured using this field. For EAP-GPSK, this is a
-#	variable length PSK.
+#	variable length PSK. ext:<name of external password field> format can
+#	be used to indicate that the password is stored in external storage.
 # ca_cert: File path to CA certificate file (PEM/DER). This file can have one
 #	or more trusted CA certificates. If ca_cert and ca_path are not
 #	included, server certificate will not be verified. This is insecure and
@@ -538,6 +699,25 @@
 # phase2: Phase2 (inner authentication with TLS tunnel) parameters
 #	(string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
 #	"autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS)
+#
+# TLS-based methods can use the following parameters to control TLS behavior
+# (these are normally in the phase1 parameter, but can be used also in the
+# phase2 parameter when EAP-TLS is used within the inner tunnel):
+# tls_allow_md5=1 - allow MD5-based certificate signatures (depending on the
+#	TLS library, these may be disabled by default to enforce stronger
+#	security)
+# tls_disable_time_checks=1 - ignore certificate validity time (this requests
+#	the TLS library to accept certificates even if they are not currently
+#	valid, i.e., have expired or have not yet become valid; this should be
+#	used only for testing purposes)
+# tls_disable_session_ticket=1 - disable TLS Session Ticket extension
+# tls_disable_session_ticket=0 - allow TLS Session Ticket extension to be used
+#	Note: If not set, this is automatically set to 1 for EAP-TLS/PEAP/TTLS
+#	as a workaround for broken authentication server implementations unless
+#	EAP workarounds are disabled with eap_workarounds=0.
+#	For EAP-FAST, this must be set to 0 (or left unconfigured for the
+#	default value to be used automatically).
+#
 # Following certificate/private key fields are used in inner Phase2
 # authentication when using EAP-TTLS or EAP-PEAP.
 # ca_cert2: File path to CA certificate file. This file can have one or more
@@ -587,6 +767,26 @@
 # number of authentication servers. Strict EAP conformance mode can be
 # configured by disabling workarounds with eap_workaround=0.
 
+# Station inactivity limit
+#
+# If a station does not send anything in ap_max_inactivity seconds, an
+# empty data frame is sent to it in order to verify whether it is
+# still in range. If this frame is not ACKed, the station will be
+# disassociated and then deauthenticated. This feature is used to
+# clear station table of old entries when the STAs move out of the
+# range.
+#
+# The station can associate again with the AP if it is still in range;
+# this inactivity poll is just used as a nicer way of verifying
+# inactivity; i.e., client will not report broken connection because
+# disassociation frame is not sent immediately without first polling
+# the STA with a data frame.
+# default: 300 (i.e., 5 minutes)
+#ap_max_inactivity=300
+
+# DTIM period in Beacon intervals for AP mode (default: 2)
+#dtim_period=2
+
 # Example blocks:
 
 # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 21fe5cc..55f3d88 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - Internal definitions
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -161,6 +161,11 @@
 	int wpa_debug_syslog;
 
 	/**
+	 * wpa_debug_tracing - Enable log output through Linux tracing
+	 */
+	int wpa_debug_tracing;
+
+	/**
 	 * override_driver - Optional driver parameter override
 	 *
 	 * This parameter can be used to override the driver parameter in
@@ -199,6 +204,12 @@
 	char *service;
 };
 
+struct wpa_freq_range {
+	unsigned int min;
+	unsigned int max;
+};
+
+
 /**
  * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
  *
@@ -214,19 +225,27 @@
 	size_t drv_count;
 	struct os_time suspend_time;
 	struct p2p_data *p2p;
+	struct wpa_supplicant *p2p_init_wpa_s;
 	struct wpa_supplicant *p2p_group_formation;
 	u8 p2p_dev_addr[ETH_ALEN];
 	struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */
 	struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */
 	int p2p_disabled;
-#ifdef ANDROID_P2P
+	int cross_connection;
+	struct wpa_freq_range *p2p_disallow_freq;
+	unsigned int num_p2p_disallow_freq;
 	enum wpa_conc_pref {
 		WPA_CONC_PREF_NOT_SET,
 		WPA_CONC_PREF_STA,
 		WPA_CONC_PREF_P2P
 	} conc_pref;
-#endif
-	int cross_connection;
+	unsigned int p2p_cb_on_scan_complete:1;
+
+#ifdef CONFIG_WIFI_DISPLAY
+	int wifi_display;
+#define MAX_WFD_SUBELEMS 10
+	struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS];
+#endif /* CONFIG_WIFI_DISPLAY */
 };
 
 
@@ -237,6 +256,17 @@
 	OFFCHANNEL_SEND_ACTION_FAILED /* Frame was not sent due to a failure */
 };
 
+struct wps_ap_info {
+	u8 bssid[ETH_ALEN];
+	enum wps_ap_info_type {
+		WPS_AP_NOT_SEL_REG,
+		WPS_AP_SEL_REG,
+		WPS_AP_SEL_REG_OUR
+	} type;
+	unsigned int tries;
+	struct os_time last_attempt;
+};
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -259,6 +289,9 @@
 #ifdef CONFIG_CTRL_IFACE_DBUS_NEW
 	char *dbus_new_path;
 	char *dbus_groupobj_path;
+#ifdef CONFIG_AP
+	char *preq_notify_peer;
+#endif /* CONFIG_AP */
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 	char bridge_ifname[16];
 
@@ -287,6 +320,9 @@
 	void *drv_priv; /* private data used by driver_ops */
 	void *global_drv_priv;
 
+	u8 *bssid_filter;
+	size_t bssid_filter_count;
+
 	/* previous scan was wildcard when interleaving between
 	 * wildcard scans and specific SSID scan when max_ssids=1 */
 	int prev_scan_wildcard;
@@ -312,6 +348,16 @@
 	unsigned int bss_update_idx;
 	unsigned int bss_next_id;
 
+	 /*
+	  * Pointers to BSS entries in the order they were in the last scan
+	  * results.
+	  */
+	struct wpa_bss **last_scan_res;
+	unsigned int last_scan_res_used;
+	unsigned int last_scan_res_size;
+	int last_scan_full;
+	struct os_time last_scan;
+
 	struct wpa_driver_ops *driver;
 	int interface_removed; /* whether the network interface has been
 				* removed */
@@ -330,6 +376,10 @@
 			     * previous association event */
 
 	struct scard_data *scard;
+#ifdef PCSC_FUNCS
+	char imsi[20];
+	int mnc_len;
+#endif /* PCSC_FUNCS */
 
 	unsigned char last_eapol_src[ETH_ALEN];
 
@@ -345,6 +395,7 @@
 	int normal_scans; /* normal scans run before sched_scan */
 
 	unsigned int drv_flags;
+	unsigned int drv_enc;
 
 	/*
 	 * A bitmap of supported protocols for probe response offload. See
@@ -403,6 +454,9 @@
 					* sa_query_count octets of pending
 					* SA Query transaction identifiers */
 		struct os_time sa_query_start;
+		u8 sched_obss_scan;
+		u16 obss_scan_int;
+		u16 bss_max_idle_period;
 	} sme;
 #endif /* CONFIG_SME */
 
@@ -460,7 +514,12 @@
 	u8 pending_join_dev_addr[ETH_ALEN];
 	int pending_join_wps_method;
 	int p2p_join_scan_count;
+	int auto_pd_scan_retry;
 	int force_long_sd;
+	u16 pending_pd_config_methods;
+	enum {
+		NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN
+	} pending_pd_use;
 
 	/*
 	 * Whether cross connection is disallowed by the AP to which this
@@ -483,25 +542,34 @@
 	 */
 	char cross_connect_uplink[100];
 
-	enum {
-		P2P_GROUP_REMOVAL_UNKNOWN,
-		P2P_GROUP_REMOVAL_REQUESTED,
-		P2P_GROUP_REMOVAL_IDLE_TIMEOUT,
-		P2P_GROUP_REMOVAL_UNAVAILABLE,
-#ifdef ANDROID_P2P
-		P2P_GROUP_REMOVAL_FREQ_CONFLICT
-#endif
-	} removal_reason;
-
-	unsigned int p2p_cb_on_scan_complete:1;
+	unsigned int sta_scan_pending:1;
+	unsigned int p2p_auto_join:1;
+	unsigned int p2p_auto_pd:1;
+	unsigned int p2p_persistent_group:1;
+	unsigned int p2p_fallback_to_go_neg:1;
+	unsigned int p2p_pd_before_go_neg:1;
+	unsigned int p2p_go_ht40:1;
+	int p2p_persistent_go_freq;
+	int p2p_persistent_id;
+	int p2p_go_intent;
+	int p2p_connect_freq;
+	struct os_time p2p_auto_started;
 #endif /* CONFIG_P2P */
 
 	struct wpa_ssid *bgscan_ssid;
 	const struct bgscan_ops *bgscan;
 	void *bgscan_priv;
 
+	const struct autoscan_ops *autoscan;
+	struct wpa_driver_scan_params *autoscan_params;
+	void *autoscan_priv;
+
 	struct wpa_ssid *connect_without_scan;
 
+	struct wps_ap_info *wps_ap;
+	size_t num_wps_ap;
+	int wps_ap_iter;
+
 	int after_wps;
 	int known_wps_freq;
 	unsigned int wps_freq;
@@ -519,6 +587,8 @@
 	unsigned int fetch_anqp_in_progress:1;
 	unsigned int network_select:1;
 	unsigned int auto_select:1;
+	unsigned int auto_network_select:1;
+	unsigned int fetch_all_anqp:1;
 #endif /* CONFIG_INTERWORKING */
 	unsigned int drv_capa_known;
 
@@ -529,6 +599,15 @@
 	} hw;
 
 	int pno;
+
+	/* WLAN_REASON_* reason codes. Negative if locally generated. */
+	int disconnect_reason;
+
+	struct ext_password_data *ext_pw;
+
+	struct wpabuf *last_gas_resp;
+	u8 last_gas_addr[ETH_ALEN];
+	u8 last_gas_dialog_token;
 };
 
 
@@ -556,6 +635,7 @@
 void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr);
 void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
 				     int sec, int usec);
+void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
 			      enum wpa_states state);
 struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s);
@@ -578,9 +658,12 @@
 					  unsigned int expire_age);
 int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
 					    unsigned int expire_count);
+int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
+				     int scan_interval);
 int wpa_supplicant_set_debug_params(struct wpa_global *global,
 				    int debug_level, int debug_timestamp,
 				    int debug_show_keys);
+void free_hw_features(struct wpa_supplicant *wpa_s);
 
 void wpa_show_license(void);
 
@@ -606,9 +689,26 @@
 void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
 void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
 int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
-#ifdef ANDROID_P2P
 int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
-#endif
+void wpas_auth_failed(struct wpa_supplicant *wpa_s);
+void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid, int clear_failures);
+void wpa_supplicant_proc_40mhz_intolerant(struct wpa_supplicant *wpa_s);
+
+/**
+ * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @ssid: Pointer to the network block the reply is for
+ * @field: field the response is a reply for
+ * @value: value (ie, password, etc) for @field
+ * Returns: 0 on success, non-zero on error
+ *
+ * Helper function to handle replies to control interface requests.
+ */
+int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+					      struct wpa_ssid *ssid,
+					      const char *field,
+					      const char *value);
 
 /* events.c */
 void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);
@@ -617,6 +717,8 @@
 			   struct wpa_ssid *ssid);
 void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx);
 void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx);
+void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s);
+int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s);
 
 /* eap_register.c */
 int eap_register_methods(void);
@@ -631,4 +733,8 @@
 	return ((ssid->disabled == 2) || ssid->p2p_persistent_group);
 }
 
+int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+
+int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
+
 #endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 5b9dc9e..6aa5205 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Glue code to setup EAPOL and RSN modules
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -720,6 +720,53 @@
 
 	wpas_notify_certification(wpa_s, depth, subject, cert_hash, cert);
 }
+
+
+static void wpa_supplicant_status_cb(void *ctx, const char *status,
+				     const char *parameter)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpas_notify_eap_status(wpa_s, status, parameter);
+}
+
+
+static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	char *str;
+	int res;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
+			  id, len);
+
+	if (wpa_s->current_ssid == NULL)
+		return;
+
+	if (id == NULL) {
+		if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				   "NULL", 0) < 0)
+			return;
+	} else {
+		str = os_malloc(len * 2 + 1);
+		if (str == NULL)
+			return;
+		wpa_snprintf_hex(str, len * 2 + 1, id, len);
+		res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				     str, 0);
+		os_free(str);
+		if (res < 0)
+			return;
+	}
+
+	if (wpa_s->conf->update_config) {
+		res = wpa_config_write(wpa_s->confname, wpa_s->conf);
+		if (res) {
+			wpa_printf(MSG_DEBUG, "Failed to update config after "
+				   "anonymous_id update");
+		}
+	}
+}
 #endif /* IEEE8021X_EAPOL */
 
 
@@ -751,6 +798,8 @@
 	ctx->port_cb = wpa_supplicant_port_cb;
 	ctx->cb = wpa_supplicant_eapol_cb;
 	ctx->cert_cb = wpa_supplicant_cert_cb;
+	ctx->status_cb = wpa_supplicant_status_cb;
+	ctx->set_anon_id = wpa_supplicant_set_anon_id;
 	ctx->cb_ctx = wpa_s;
 	wpa_s->eapol = eapol_sm_init(ctx);
 	if (wpa_s->eapol == NULL) {
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 4965439..915ca57 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant / WPS integration
- * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "eloop.h"
 #include "uuid.h"
+#include "crypto/random.h"
 #include "crypto/dh_group5.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -20,6 +21,7 @@
 #include "eap_peer/eap.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
+#include "wps/wps_attr_parse.h"
 #include "config.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
@@ -41,6 +43,15 @@
 static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
 
 
+static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s)
+{
+	os_free(wpa_s->wps_ap);
+	wpa_s->wps_ap = NULL;
+	wpa_s->num_wps_ap = 0;
+	wpa_s->wps_ap_iter = 0;
+}
+
+
 int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
 {
 	if (!wpa_s->wps_success &&
@@ -64,6 +75,7 @@
 		return 1;
 	}
 
+	wpas_wps_clear_ap_info(wpa_s);
 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success)
 		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL);
@@ -261,8 +273,13 @@
 		ssid->eap.phase1 = NULL;
 		os_free(ssid->eap.eap_methods);
 		ssid->eap.eap_methods = NULL;
-		if (!ssid->p2p_group)
+		if (!ssid->p2p_group) {
 			ssid->temporary = 0;
+			ssid->bssid_set = 0;
+		}
+		ssid->disabled_until.sec = 0;
+		ssid->disabled_until.usec = 0;
+		ssid->auth_failures = 0;
 	} else {
 		wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
 			   "received credential");
@@ -350,16 +367,6 @@
 		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
 		ssid->proto = WPA_PROTO_WPA;
 		break;
-	case WPS_AUTH_WPA:
-		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
-		ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
-		ssid->proto = WPA_PROTO_WPA;
-		break;
-	case WPS_AUTH_WPA2:
-		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
-		ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
-		ssid->proto = WPA_PROTO_RSN;
-		break;
 	case WPS_AUTH_WPA2PSK:
 		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
 		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
@@ -709,6 +716,8 @@
 			wpa_config_remove_network(wpa_s->conf, id);
 		}
 	}
+
+	wpas_wps_clear_ap_info(wpa_s);
 }
 
 
@@ -904,6 +913,7 @@
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
 			       wpa_s, NULL);
+	wpa_s->wps_ap_iter = 1;
 	wpas_wps_reassoc(wpa_s, ssid, bssid);
 	return rpin;
 }
@@ -919,7 +929,8 @@
 	}
 #endif /* CONFIG_AP */
 
-	if (wpa_s->wpa_state == WPA_SCANNING) {
+	if (wpa_s->wpa_state == WPA_SCANNING ||
+	    wpa_s->wpa_state == WPA_DISCONNECTED) {
 		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
 		wpa_supplicant_cancel_scan(wpa_s);
 		wpas_clear_wps(wpa_s);
@@ -929,7 +940,8 @@
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
 		wpas_clear_wps(wpa_s);
-	}
+	} else
+		wpas_wps_clear_ap_info(wpa_s);
 
 	return 0;
 }
@@ -1113,8 +1125,10 @@
 		while (first && first->next)
 			first = first->next;
 		if (first && first != wpa_s) {
-			os_memcpy(wps->uuid, wpa_s->global->ifaces->wps->uuid,
-				  WPS_UUID_LEN);
+			if (wps != wpa_s->global->ifaces->wps)
+				os_memcpy(wps->uuid,
+					  wpa_s->global->ifaces->wps->uuid,
+					  WPS_UUID_LEN);
 			wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first "
 				    "interface", wps->uuid, WPS_UUID_LEN);
 		} else {
@@ -1130,6 +1144,23 @@
 }
 
 
+static void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s,
+				       struct wps_context *wps)
+{
+	wpabuf_free(wps->dev.vendor_ext_m1);
+	wps->dev.vendor_ext_m1 = NULL;
+
+	if (wpa_s->conf->wps_vendor_ext_m1) {
+		wps->dev.vendor_ext_m1 =
+			wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1);
+		if (!wps->dev.vendor_ext_m1) {
+			wpa_printf(MSG_ERROR, "WPS: Cannot "
+				   "allocate memory for vendor_ext_m1");
+		}
+	}
+}
+
+
 int wpas_wps_init(struct wpa_supplicant *wpa_s)
 {
 	struct wps_context *wps;
@@ -1168,6 +1199,8 @@
 	os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
 		  WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types);
 
+	wpas_wps_set_vendor_ext_m1(wpa_s, wps);
+
 	wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
 	modes = wpa_s->hw.modes;
 	if (modes) {
@@ -1214,6 +1247,7 @@
 void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
 {
 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+	wpas_wps_clear_ap_info(wpa_s);
 
 	if (wpa_s->wps == NULL)
 		return;
@@ -1228,6 +1262,7 @@
 	wpabuf_free(wpa_s->wps->dh_privkey);
 	wpabuf_free(wpa_s->wps->oob_conf.pubkey_hash);
 	wpabuf_free(wpa_s->wps->oob_conf.dev_password);
+	wpabuf_free(wpa_s->wps->dev.vendor_ext_m1);
 	os_free(wpa_s->wps->network_key);
 	os_free(wpa_s->wps);
 	wpa_s->wps = NULL;
@@ -1235,14 +1270,14 @@
 
 
 int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
-			    struct wpa_ssid *ssid, struct wpa_scan_res *bss)
+			    struct wpa_ssid *ssid, struct wpa_bss *bss)
 {
 	struct wpabuf *wps_ie;
 
 	if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
 		return -1;
 
-	wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+	wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
 	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
 		if (!wps_ie) {
 			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
@@ -1304,19 +1339,19 @@
 
 int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
 			      struct wpa_ssid *ssid,
-			      struct wpa_scan_res *bss)
+			      struct wpa_bss *bss)
 {
 	struct wpabuf *wps_ie = NULL;
 	int ret = 0;
 
 	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
-		wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+		wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
 		if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
 			/* allow wildcard SSID for WPS PBC */
 			ret = 1;
 		}
 	} else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
-		wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+		wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
 		if (wps_ie &&
 		    (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) ||
 		     wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
@@ -1338,7 +1373,7 @@
 			ret = 0;
 		if (bss->beacon_ie_len) {
 			struct wpabuf *bcn_wps;
-			bcn_wps = wpa_scan_get_vendor_ie_multi_beacon(
+			bcn_wps = wpa_bss_get_vendor_ie_multi_beacon(
 				bss, WPS_IE_VENDOR_TYPE);
 			if (bcn_wps == NULL) {
 				wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE "
@@ -1648,6 +1683,34 @@
 }
 
 
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
+					     int ndef, const char *uuid)
+{
+	struct wpabuf *ret;
+	u8 u[UUID_LEN];
+
+	if (!wpa_s->wps_er)
+		return NULL;
+
+	if (uuid_str2bin(uuid, u))
+		return NULL;
+
+	ret = wps_er_nfc_config_token(wpa_s->wps_er, u);
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+#endif /* CONFIG_WPS_NFC */
+
+
 static int callbacks_pending = 0;
 
 static void wpas_wps_terminate_cb(void *ctx)
@@ -1706,6 +1769,7 @@
 		}
 	}
 	wps->config_methods = wps_fix_config_methods(wps->config_methods);
+	wps->dev.config_methods = wps->config_methods;
 
 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
 		os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
@@ -1717,6 +1781,9 @@
 			  wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN);
 	}
 
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION)
+		wpas_wps_set_vendor_ext_m1(wpa_s, wps);
+
 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION)
 		wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
 
@@ -1733,3 +1800,261 @@
 		wps->dev.serial_number = wpa_s->conf->serial_number;
 	}
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
+{
+	return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id,
+				 &wpa_s->conf->wps_nfc_dh_pubkey,
+				 &wpa_s->conf->wps_nfc_dh_privkey,
+				 &wpa_s->conf->wps_nfc_dev_pw);
+}
+
+
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	struct wps_context *wps = wpa_s->wps;
+	char pw[32 * 2 + 1];
+
+	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
+	    wpa_s->conf->wps_nfc_dh_privkey == NULL ||
+	    wpa_s->conf->wps_nfc_dev_pw == NULL)
+		return -1;
+
+	dh5_free(wps->dh_ctx);
+	wpabuf_free(wps->dh_pubkey);
+	wpabuf_free(wps->dh_privkey);
+	wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+	wps->dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+	if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
+		wps->dh_ctx = NULL;
+		wpabuf_free(wps->dh_pubkey);
+		wps->dh_pubkey = NULL;
+		wpabuf_free(wps->dh_privkey);
+		wps->dh_privkey = NULL;
+		return -1;
+	}
+	wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
+	if (wps->dh_ctx == NULL)
+		return -1;
+
+	wpa_snprintf_hex_uppercase(pw, sizeof(pw),
+				   wpabuf_head(wpa_s->conf->wps_nfc_dev_pw),
+				   wpabuf_len(wpa_s->conf->wps_nfc_dev_pw));
+	return wpas_wps_start_pin(wpa_s, bssid, pw, 0,
+				  wpa_s->conf->wps_nfc_dev_pw_id);
+}
+
+
+static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
+			     struct wps_parse_attr *attr)
+{
+	if (wps_oob_use_cred(wpa_s->wps, attr) < 0)
+		return -1;
+
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
+		   "based on the received credential added");
+	wpa_s->normal_scans = 0;
+	wpa_supplicant_reinit_autoscan(wpa_s);
+	wpa_s->disconnected = 0;
+	wpa_s->reassociate = 1;
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+	return 0;
+}
+
+
+#ifdef CONFIG_WPS_ER
+static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s,
+					   struct wps_parse_attr *attr)
+{
+	return wps_registrar_add_nfc_password_token(
+		wpa_s->wps->registrar, attr->oob_dev_password,
+		attr->oob_dev_password_len);
+}
+#endif /* CONFIG_WPS_ER */
+
+
+static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s,
+				    const struct wpabuf *wps)
+{
+	struct wps_parse_attr attr;
+
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+	if (wps_parse_msg(wps, &attr)) {
+		wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+		return -1;
+	}
+
+	if (attr.num_cred)
+		return wpas_wps_use_cred(wpa_s, &attr);
+
+#ifdef CONFIG_WPS_ER
+	if (attr.oob_dev_password)
+		return wpas_wps_add_nfc_password_token(wpa_s, &attr);
+#endif /* CONFIG_WPS_ER */
+
+	wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+	return -1;
+}
+
+
+int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
+			  const struct wpabuf *data)
+{
+	const struct wpabuf *wps = data;
+	struct wpabuf *tmp = NULL;
+	int ret;
+
+	if (wpabuf_len(data) < 4)
+		return -1;
+
+	if (*wpabuf_head_u8(data) != 0x10) {
+		/* Assume this contains full NDEF record */
+		tmp = ndef_parse_wifi(data);
+		if (tmp == NULL) {
+			wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+			return -1;
+		}
+		wps = tmp;
+	}
+
+	ret = wpas_wps_nfc_tag_process(wpa_s, wps);
+	wpabuf_free(tmp);
+	return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+extern int wpa_debug_level;
+
+static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
+{
+	size_t i;
+	struct os_time now;
+
+	if (wpa_debug_level > MSG_DEBUG)
+		return;
+
+	if (wpa_s->wps_ap == NULL)
+		return;
+
+	os_get_time(&now);
+
+	for (i = 0; i < wpa_s->num_wps_ap; i++) {
+		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+		struct wpa_blacklist *e = wpa_blacklist_get(wpa_s, ap->bssid);
+
+		wpa_printf(MSG_DEBUG, "WPS: AP[%d] " MACSTR " type=%d "
+			   "tries=%d last_attempt=%d sec ago blacklist=%d",
+			   (int) i, MAC2STR(ap->bssid), ap->type, ap->tries,
+			   ap->last_attempt.sec > 0 ?
+			   (int) now.sec - (int) ap->last_attempt.sec : -1,
+			   e ? e->count : 0);
+	}
+}
+
+
+static struct wps_ap_info * wpas_wps_get_ap_info(struct wpa_supplicant *wpa_s,
+						 const u8 *bssid)
+{
+	size_t i;
+
+	if (wpa_s->wps_ap == NULL)
+		return NULL;
+
+	for (i = 0; i < wpa_s->num_wps_ap; i++) {
+		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+		if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0)
+			return ap;
+	}
+
+	return NULL;
+}
+
+
+static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s,
+					struct wpa_scan_res *res)
+{
+	struct wpabuf *wps;
+	enum wps_ap_info_type type;
+	struct wps_ap_info *ap;
+	int r;
+
+	if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL)
+		return;
+
+	wps = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE);
+	if (wps == NULL)
+		return;
+
+	r = wps_is_addr_authorized(wps, wpa_s->own_addr, 1);
+	if (r == 2)
+		type = WPS_AP_SEL_REG_OUR;
+	else if (r == 1)
+		type = WPS_AP_SEL_REG;
+	else
+		type = WPS_AP_NOT_SEL_REG;
+
+	wpabuf_free(wps);
+
+	ap = wpas_wps_get_ap_info(wpa_s, res->bssid);
+	if (ap) {
+		if (ap->type != type) {
+			wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR
+				   " changed type %d -> %d",
+				   MAC2STR(res->bssid), ap->type, type);
+			ap->type = type;
+			if (type != WPS_AP_NOT_SEL_REG)
+				wpa_blacklist_del(wpa_s, ap->bssid);
+		}
+		return;
+	}
+
+	ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1,
+			      sizeof(struct wps_ap_info));
+	if (ap == NULL)
+		return;
+
+	wpa_s->wps_ap = ap;
+	ap = &wpa_s->wps_ap[wpa_s->num_wps_ap];
+	wpa_s->num_wps_ap++;
+
+	os_memset(ap, 0, sizeof(*ap));
+	os_memcpy(ap->bssid, res->bssid, ETH_ALEN);
+	ap->type = type;
+	wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added",
+		   MAC2STR(ap->bssid), ap->type);
+}
+
+
+void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
+			     struct wpa_scan_results *scan_res)
+{
+	size_t i;
+
+	for (i = 0; i < scan_res->num; i++)
+		wpas_wps_update_ap_info_bss(wpa_s, scan_res->res[i]);
+
+	wpas_wps_dump_ap_info(wpa_s);
+}
+
+
+void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	struct wps_ap_info *ap;
+	if (!wpa_s->wps_ap_iter)
+		return;
+	ap = wpas_wps_get_ap_info(wpa_s, bssid);
+	if (ap == NULL)
+		return;
+	ap->tries++;
+	os_get_time(&ap->last_attempt);
+}
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index a5472a0..d5eb3b6 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant / WPS integration
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 #define WPS_SUPPLICANT_H
 
 struct wpa_scan_res;
+struct wpa_scan_results;
 
 #ifdef CONFIG_WPS
 
@@ -39,9 +40,9 @@
 int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
 		       const char *pin, struct wps_new_ap_settings *settings);
 int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
-			    struct wpa_ssid *ssid, struct wpa_scan_res *bss);
+			    struct wpa_ssid *ssid, struct wpa_bss *bss);
 int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
-			      struct wpa_ssid *ssid, struct wpa_scan_res *bss);
+			      struct wpa_ssid *ssid, struct wpa_bss *bss);
 int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
 			      struct wpa_bss *selected, struct wpa_ssid *ssid);
 void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s);
@@ -59,9 +60,18 @@
 			   int id);
 int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
 		       const char *pin, struct wps_new_ap_settings *settings);
+struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
+					     int ndef, const char *uuid);
 int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
 int wpas_wps_in_progress(struct wpa_supplicant *wpa_s);
 void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
+struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef);
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
+			  const struct wpabuf *data);
+void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
+			     struct wpa_scan_results *scan_res);
+void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);
 
 #else /* CONFIG_WPS */
 
@@ -114,6 +124,16 @@
 	return 0;
 }
 
+static inline void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
+					   struct wpa_scan_results *scan_res)
+{
+}
+
+static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s,
+					 const u8 *bssid)
+{
+}
+
 #endif /* CONFIG_WPS */
 
 #endif /* WPS_SUPPLICANT_H */