Merge "/bin/wifi:  Save options per-interface rather than per-band."
diff --git a/Makefile b/Makefile
index 82a7d8b..146023b 100644
--- a/Makefile
+++ b/Makefile
@@ -90,6 +90,10 @@
 DIRS+=diags
 endif
 
+ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gflt400)
+DIRS+=diags
+endif
+
 ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gfrg240)
 DIRS+=diags
 endif
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index 326b946..c74b960 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -511,7 +511,7 @@
       if ((not self.acs() or provisioning_failed) and
           not getattr(wifi, 'last_successful_bss_info', None) and
           _gettime() > wifi.last_wifi_scan_time + self._wifi_scan_period_s):
-        logger.debug('Performing scan on %s.', wifi.name)
+        logger.info('Performing scan on %s.', wifi.name)
         self._wifi_scan(wifi)
 
       # Periodically retry rejoining the WLAN.  If the WLAN configuration is
@@ -866,7 +866,7 @@
     band = wlan_configuration.band
     current = self._wlan_configuration.get(band, None)
     if current is None or wlan_configuration.command != current.command:
-      logger.debug('Received new WLAN configuration for band %s', band)
+      logger.info('Received new WLAN configuration for band %s', band)
       if current is not None:
         wlan_configuration.access_point = current.access_point
       else:
diff --git a/diags/Makefile b/diags/Makefile
index 1a7386b..3f9a77c 100644
--- a/diags/Makefile
+++ b/diags/Makefile
@@ -10,6 +10,9 @@
 ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gflt110)
  DIRS += chameleon
 endif
+ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gflt400)
+ DIRS += chameleon
+endif
 ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gfrg240)
  DIRS += prowl
 endif
diff --git a/gpio-mailbox/brcm-direct.c b/gpio-mailbox/brcm-direct.c
index 8abacbb..9667db1 100644
--- a/gpio-mailbox/brcm-direct.c
+++ b/gpio-mailbox/brcm-direct.c
@@ -102,7 +102,7 @@
       .offset_data = 0x6580,            // PWM_CTRL ...
       .channel = 0,
       .open_drain = 1,
-      .period = 0x63,
+      .period = 0xf0,
     },
     .temp_monitor = {
       .is_present = 1,                  // 7425 AVS_RO_REGISTERS_0
@@ -261,12 +261,12 @@
         .is_present = 1,                  // AON_GPIO_05
         .pinmux_offset = 0x10700,         // PIN_MUX_CTRL_0
         .pinmux_mask =  0x00f00000,
-        .pinmux_value = 0x00200000,       // LED_LD_13
-        .offset_data = 0x1701c,           // LDK_DIGIT1
-        .mask = 1<<13,                    // 1<<13
-        .shift = 13,
-        .off_value =1,
-        .on_value = 0,
+        .pinmux_value = 0x00000000,       // AON_GPIO_05
+        .offset_data = 0x17404,           // AON_DATA
+        .mask = 1<<5,
+        .shift = 5,
+        .off_value =0,
+        .on_value = 1,
         .old_val = -1,
       },
       .led_blue = {
@@ -276,12 +276,12 @@
         .is_present = 1,                  // AON_GPIO_04
         .pinmux_offset = 0x10700,         // PIN_MUX_CTRL_0
         .pinmux_mask = 0x000f0000,
-        .pinmux_value = 0x00020000,       // LED_LD_12
-        .offset_data = 0x1701c,           // LDK_DIGIT1
-        .mask = 1<<12,                    // 1<<12
-        .shift = 12,
-        .off_value = 1,
-        .on_value = 0,
+        .pinmux_value = 0x00000000,       // AON_GPIO_04
+        .offset_data = 0x17404,           // LDK_DIGIT1
+        .mask = 1<<4,                    // 1<<12
+        .shift = 4,
+        .off_value = 0,
+        .on_value = 1,
         .old_val = -1,
       },
       .led_standby = {
@@ -293,7 +293,7 @@
         .offset_data = 0x9000,          // PWM_2
         .channel = 0,
         .old_percent = -1,
-        .period = 0x63,
+        .period = 0x65,
       },
     },
     .reset_button = {
@@ -374,51 +374,8 @@
 /* set LED/Keypad timings to control LED brightness */
 static void init_gfhd254(struct platform_info* p) {
   volatile uint32_t* reg;
-
-  // The following comment explains how the LED controller works on <= EVT3.
-  //  For EVT4+, the LED controller was changed to control via PWM. We currently
-  //  configure both. The EVT3 specific code can be removed at a later date.
-  //
-  // The led display controller works like this:
-  //  - there are 16 gpios (we connect our leds to 2 of these)
-  //  - the controller steps through digit1-4 and then status
-  //  - bit0 in a register maps to a particular gpio
-  //     when digit1 is being displayed the controller uses digit1_bit[15:0] to
-  //     drive the gpios.  When digit 2 is displayed digit2[15:0] and so forth.
-  //  - duty_on controls how many clocks a digit is displayed
-  //  - duty_off controls number of clocks of all off time when switching
-  //    between digits
-  //
-  //  To get 100% brightness you set all of digit1-4 and status to 1 for the led
-  //  you are drivng, and set duty_off to 0.
-  //
-  //  Here we also invert the values, so a 1 means off, and 0 means on, this is
-  //  done because for unknown reasons the time between status and digit1 is on,
-  //  so we can't get the brightness to 0 unless we invert.
-  //
-  //  For simplicity we enable only one of the digits because the leds are
-  //  already insanely bright, and then to disable an led we simply toggle the
-  //  bit in that one digit register.
-  //
-  //  The red led is attached to bit 13 and blue led is attached to bit 12.
-  reg = mmap_addr + 0x17034;     // LDK_CONTROL
-  *reg = 0x01;                   // reset
-  *reg = 0x18;                   // ver=1
-
-  reg = mmap_addr + 0x17018;
-  reg[0] = 0xffff;               // LDK_DIGIT2
-  reg[1] = 0xcfff;               // LDK_DIGIT1
-  reg[2] = 0xffff;               // LDK_DIGIT4
-  reg[3] = 0xffff;               // LDK_DIGIT3
-  reg[5] = 0xffff;               // LDK_STATUS
-
-  reg = mmap_addr + 0x17008;     // LDK_PRESCHI, LO (clock divisor)
-  reg[0] = 0x00;
-  reg[1] = 0x10;                 // tick = clock / 0x0010, not sure what clock is
-
-  reg = mmap_addr + 0x17010;     // LDK_DUTYOFF, ON
-  reg[0] = 0x40;
-  reg[1] = 0xc0;                 // 0x40 off ticks then 0xc0 on ticks to dim a bit more.
+  reg = mmap_addr + 0x17408;         // AON_IODIR
+  reg[0] |= reg[0] & ~(1<<4 | 1<<5); // set gpios to be output
 
   // The fan is connected to PWM3, the register PWM3_CWORD_LSB is set to 1,
   // this is the frequency of the PWM, the other pwm register control
@@ -429,8 +386,9 @@
 
   // LEDs are connected to PWM2. Setting CWORD_LSB to 0xf to control
   // the output freq of the var rate clock.
-  reg = mmap_addr + 0x900c;
-  reg[0] = 0xf;
+  reg = mmap_addr + 0x9008;
+  reg[0] = 0x00;
+  reg[1] = 0x57;
 
   // Default the LED brightness to 50.
   set_pwm(&p->leds.led_brightness, 50);
diff --git a/sysmgr/peripheral/fancontrol.cc b/sysmgr/peripheral/fancontrol.cc
index 175589e..335b8e4 100644
--- a/sysmgr/peripheral/fancontrol.cc
+++ b/sysmgr/peripheral/fancontrol.cc
@@ -236,6 +236,16 @@
                           temp_overheat : 97,
                         };
 
+const FanControlParams FanControl::kGFLT400FanCtrlSocDefaults = {
+                          temp_setpt    : 0,  /* No fan */
+                          temp_max      : 0,
+                          temp_step     : 0,
+                          duty_cycle_min: 0,
+                          duty_cycle_max: 0,
+                          pwm_step      : 0,
+                          temp_overheat : 97,
+                        };
+
 const FanControlParams FanControl::kGFCH100FanCtrlSocDefaults = {
                           temp_setpt    : 0,  /* No fan */
                           temp_max      : 0,
@@ -333,6 +343,9 @@
     case BRUNO_GFLT300:
       pfan_ctrl_params_[BRUNO_SOC] = kGFLT300FanCtrlSocDefaults;
       break;
+    case BRUNO_GFLT400:
+      pfan_ctrl_params_[BRUNO_SOC] = kGFLT400FanCtrlSocDefaults;
+      break;
     case BRUNO_GFCH100:
       pfan_ctrl_params_[BRUNO_SOC] = kGFCH100FanCtrlSocDefaults;
       break;
diff --git a/sysmgr/peripheral/fancontrol.h b/sysmgr/peripheral/fancontrol.h
index a648cef..e10ae57 100644
--- a/sysmgr/peripheral/fancontrol.h
+++ b/sysmgr/peripheral/fancontrol.h
@@ -82,6 +82,7 @@
 
   static const FanControlParams kGFLT110FanCtrlSocDefaults;
   static const FanControlParams kGFLT300FanCtrlSocDefaults;
+  static const FanControlParams kGFLT400FanCtrlSocDefaults;
 
   static const FanControlParams kGFCH100FanCtrlSocDefaults;
 
diff --git a/sysmgr/peripheral/platform.cc b/sysmgr/peripheral/platform.cc
index d120dff..731de95 100644
--- a/sysmgr/peripheral/platform.cc
+++ b/sysmgr/peripheral/platform.cc
@@ -21,6 +21,7 @@
   Platform("GFLT120", BRUNO_GFLT110, false, false, false),
   Platform("GFHD254", BRUNO_GFHD254, false, true, true),
   Platform("GFLT300", BRUNO_GFLT300, false, false, false),
+  Platform("GFLT400", BRUNO_GFLT400, false, false, false),
   Platform("GFCH100", BRUNO_GFCH100, false, false, false),
   Platform("UNKNOWN PLATFORM", BRUNO_UNKNOWN, false, false,  false),
 };
diff --git a/sysmgr/peripheral/platform.h b/sysmgr/peripheral/platform.h
index 01ea3e8..2e743ed 100644
--- a/sysmgr/peripheral/platform.h
+++ b/sysmgr/peripheral/platform.h
@@ -27,6 +27,7 @@
   BRUNO_GFLT110,          /* Fiber Jack */
   BRUNO_GFHD254,          /* Lockdown */
   BRUNO_GFLT300,          /* Go-Long FiberJack */
+  BRUNO_GFLT400,          /* Co-ax Jack */
   BRUNO_GFCH100,          /* Chimera mm-wave */
   BRUNO_UNKNOWN
 };
diff --git a/wifi/configs.py b/wifi/configs.py
index 572b1c9..240dd63 100644
--- a/wifi/configs.py
+++ b/wifi/configs.py
@@ -370,20 +370,22 @@
   else:
     network_block_lines = wpa_network_lines(ssid, passphrase)
 
-  freq_list = ' '.join(autochannel.get_all_frequencies(opt.band))
+  freq_list = 'freq_list=' + ' '.join(autochannel.get_all_frequencies(opt.band))
+  use_freq_list = not opt.no_band_restriction
 
   network_block_lines.append('\tscan_ssid=1')
   if opt.bssid:
     network_block_lines.append('\tbssid=%s' %
                                utils.validate_and_sanitize_bssid(opt.bssid))
-  network_block_lines.append('\tfreq_list=' + freq_list)
+  if use_freq_list:
+    network_block_lines.append('\t' + freq_list)
   network_block = make_network_block(network_block_lines)
 
   lines = [
       'ctrl_interface=/var/run/wpa_supplicant',
       'ap_scan=1',
       'autoscan=exponential:1:30',
-      'freq_list=' + freq_list,
+      freq_list if use_freq_list else '',
       network_block
   ]
   return '\n'.join(lines)
diff --git a/wifi/configs_test.py b/wifi/configs_test.py
index bd28626..68e7b2a 100755
--- a/wifi/configs_test.py
+++ b/wifi/configs_test.py
@@ -59,6 +59,18 @@
 }}
 """
 
+_WPA_SUPPLICANT_CONFIG_NO_FREQ_LIST = """ctrl_interface=/var/run/wpa_supplicant
+ap_scan=1
+autoscan=exponential:1:30
+
+network={
+\tssid="some ssid"
+\t#psk="some passphrase"
+\tpsk=41821f7ca3ea5d85beea7644ed7e0fefebd654177fa06c26fbdfdc3c599a317f
+\tscan_ssid=1
+}
+"""
+
 
 @wvtest.wvtest
 def generate_wpa_supplicant_config_test():
@@ -92,6 +104,13 @@
         freq_list=_FREQ_LIST[band])
     wvtest.WVPASSEQ(want, got)
 
+    opt.no_band_restriction = True
+    opt.bssid = None
+    got = configs.generate_wpa_supplicant_config(
+        'some ssid', 'some passphrase', opt)
+    want = _WPA_SUPPLICANT_CONFIG_NO_FREQ_LIST
+    wvtest.WVPASSEQ(want, got)
+
 
 _PHY_INFO = """Wiphy phy0
   max # scan SSIDs: 4
@@ -415,6 +434,7 @@
     self.interface_suffix = ''
     self.client_isolation = False
     self.supports_provisioning = False
+    self.no_band_restriction = False
 
 
 def wpa_passphrase(ssid, passphrase):
diff --git a/wifi/qca9880_cal.py b/wifi/qca9880_cal.py
index 7c18454..2cf394c 100755
--- a/wifi/qca9880_cal.py
+++ b/wifi/qca9880_cal.py
@@ -134,45 +134,45 @@
     return None
 
   if oui not in (bytearray(s) for s in SUSPECT_OUIS):
-    _log('OUI {} is properly calibrated.'.format(_oui_string(oui)))
-    # Create an empty directory so this script short-circuits if run again.
-    _create_calibration_dir()
+    if not _cal_dir_exists():
+      _log('OUI {} is properly calibrated.'.format(_oui_string(oui)))
     return None
 
   # V01 is retroactively represented not by a string, but by 3 0 value bytes.
   if version == '\x00\x00\x00':
-    _log('version field is V01. CAL + FCC calibration required.')
+    if not _cal_dir_exists():
+      _log('version field is V01. CAL + FCC calibration required.')
     return ('fcc', 'cal')
 
   if version == 'V02':
-    _log('version field is V02. Only FCC calibration required.')
+    if not _cal_dir_exists():
+      _log('version field is V02. Only FCC calibration required.')
     return ('fcc',)
 
   if version == 'V03':
-    _log('version field is V03. No patching required.')
-    # Create an empty directory so this script short-circuits if run again.
-    _create_calibration_dir()
+    if not _cal_dir_exists():
+      _log('version field is V03. No patching required.')
     return None
 
-  _log('version field unknown: {}'.format(version))
+  if not _cal_dir_exists():
+    _log('version field unknown: {}'.format(version))
   return None
 
 
-def _is_previously_calibrated():
-  """Check if this calibration script already ran since the last boot.
-
-  Returns:
-    True if calibration checks already ran, False otherwise.
-  """
+def _cal_dir_exists():
   return os.path.exists(CALIBRATION_DIR)
 
 
-def _create_calibration_dir():
+def _patch_exists():
+  return os.path.exists(os.path.join(CALIBRATION_DIR, CAL_PATCH_FILE))
+
+
+def _create_cal_dir():
   """Create calibration directory.
 
   Calibration directory contains the calibration patch file.
   If the directory is empty it signals that calibration checks have already
-  run.
+  completed.
 
   Returns:
     True if directory exists or is created, false if any error.
@@ -233,9 +233,6 @@
   if 'fcc' in calibration_state:
     _apply_patch('Applying FCC patch...', cal_data, FCC_PATCH)
 
-  if not _create_calibration_dir():
-    return False
-
   try:
     patched_file = os.path.join(CALIBRATION_DIR, CAL_PATCH_FILE)
     open(patched_file, 'wb').write(cal_data)
@@ -264,24 +261,40 @@
 def qca8990_calibration():
   """Main QCA8990 calibration check."""
 
-  if not experiment.enabled(CAL_EXPERIMENT):
-    _log('experiment {} not specified. Skipping calibration check.'.
-         format(CAL_EXPERIMENT))
-    return
-
-  if _is_previously_calibrated():
-    _log('calibration check completed earlier.')
-    return
-
   if not _is_ath10k('wlan1'):
-    _log('this platform does not use ath10k.')
+    if not _cal_dir_exists():
+      _log('This system does not use ath10k')
+    _create_cal_dir()
     return
 
+  if not experiment.enabled(CAL_EXPERIMENT):
+    if  _patch_exists():
+      os.remove(os.path.join(CALIBRATION_DIR, CAL_PATCH_FILE))
+      _reload_driver()
+      _log('experiment {} removed. Removed patch and reloaded driver.'.
+           format(CAL_EXPERIMENT))
+      return
+    if not _cal_dir_exists():
+      _log('experiment {} not active. Skipping calibration check.'
+           .format(CAL_EXPERIMENT))
+      _create_cal_dir()
+    return
+
+  # Experiment is enabled.
+
   calibration_state = _is_module_miscalibrated()
-  if calibration_state is not None:
+  if calibration_state is not None and not _patch_exists():
+    _create_cal_dir()
     if _generate_calibration_patch(calibration_state):
       _log('generated new patch.')
       _reload_driver()
+    return
+
+  if calibration_state is None:
+    if not _cal_dir_exists():
+      _log('This system does not need calibration.')
+    _create_cal_dir()
+    return
 
 
 if __name__ == '__main__':
diff --git a/wifi/wifi.py b/wifi/wifi.py
index 8c34774..56d742d 100755
--- a/wifi/wifi.py
+++ b/wifi/wifi.py
@@ -57,6 +57,7 @@
 scan-passive                      (Scan only) do not probe, scan passively
 scan-freq=                        (Scan only) limit scan to specific frequencies.
 supports-provisioning             Indicate via vendor IE that this AP supports provisioning.  Corresponds to feature ID 01 of OUI f4f5e8 at go/alphabet-ie-registry.
+no-band-restriction               For setclient only.  If set, let --band select the wifi radio but do not actually enforce it for multi-band radios.
 """
 
 _FINGERPRINTS_DIRECTORY = '/tmp/wifi/fingerprints'
@@ -243,6 +244,9 @@
   if not iw.RUNNABLE_IW():
     raise utils.BinWifiException("Can't proceed without iw")
 
+  # Check for calibration errors on ath10k.
+  qca9880_cal.qca8990_calibration()
+
   # If this phy is running client mode, we need to use its width/channel.
   phy = iw.find_phy(band, channel)
   if phy is None:
@@ -256,9 +260,6 @@
         'no wifi interface found for band=%s channel=%s suffix=%s',
         band, channel, opt.interface_suffix)
 
-  # Check for calibration errors on ath10k.
-  qca9880_cal.qca8990_calibration()
-
   found_active_config = False
   for other_interface in (set(iw.find_all_interfaces_from_phy(phy)) -
                           set([interface])):