Merge "bouncer: a bouncer for commmon room WiFi"
diff --git a/Makefile b/Makefile
index 5281c30..cdc600d 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@
BUILD_SSDP?=y
BUILD_DNSSD?=y
BUILD_LOGUPLOAD?= # default off: needs libgtest
-BUILD_IBEACON?= # default off: needs bluetooth.h
+BUILD_BLUETOOTH?= # default off: needs bluetooth.h
BUILD_WAVEGUIDE?=y
BUILD_DVBUTILS?=y
BUILD_SYSMGR?=y
@@ -18,7 +18,7 @@
BUILD_BOUNCER?= # default off: costly
BUILD_PRESTERASTATS?=n
export BUILD_HNVRAM BUILD_SSDP BUILD_DNSSD BUILD_LOGUPLOAD \
- BUILD_IBEACON BUILD_WAVEGUIDE BUILD_DVBUTILS BUILD_SYSMGR \
+ BUILD_BLUETOOTH BUILD_WAVEGUIDE BUILD_DVBUTILS BUILD_SYSMGR \
BUILD_STATUTILS BUILD_CRYPTDEV BUILD_SIGNING BUILD_JSONPOLL \
BUILD_PRESTERASTATS BUILD_CACHE_WARMING BUILD_BOUNCER
@@ -102,6 +102,10 @@
DIRS+=ledpattern
endif
+ifeq ($(BUILD_BLUETOOTH),y)
+DIRS+=rcu_audio
+endif
+
PREFIX=/usr
BINDIR=$(DESTDIR)$(PREFIX)/bin
LIBDIR=$(DESTDIR)$(PREFIX)/lib
@@ -123,7 +127,7 @@
$(MAKE) install-commonpy
mkdir -p $(BINDIR)
rm -fv $(BINDIR)/hnvram
-ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME), gfmn110)
+ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME), gfmn100)
ln -s /usr/bin/hnvram_wrapper $(BINDIR)/hnvram
else ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME), gflt110)
ln -s /usr/bin/hnvram_wrapper $(BINDIR)/hnvram
diff --git a/cmds/Makefile b/cmds/Makefile
index 24bc8a1..b058618 100644
--- a/cmds/Makefile
+++ b/cmds/Makefile
@@ -76,7 +76,7 @@
SCRIPT_TARGETS += castcheck
endif
-ifeq ($(BUILD_IBEACON),y)
+ifeq ($(BUILD_BLUETOOTH),y)
ARCH_TARGETS += ibeacon eddystone
endif
@@ -97,6 +97,10 @@
-g -O -std=c99 -D_GNU_SOURCE $(EXTRACFLAGS)
CXXFLAGS += -Wall -Wextra -Wswitch-enum -Werror -Wno-unused-parameter \
-g -O -std=gnu++0x -D_GNU_SOURCE $(EXTRACXXFLAGS)
+ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gfrg240)
+CFLAGS += -Wno-error=format
+CXXFLAGS += -Wno-error=format
+endif
LDFLAGS += $(EXTRALDFLAGS)
HOST_INCS=-I$(HOSTDIR)/usr/include
HOST_LIBS=-L$(HOSTDIR)/usr/lib -Wl,-rpath=$(HOSTDIR)/usr/lib
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index 374d495..8307efd 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -23,11 +23,13 @@
import experiment
import interface
import iw
+import ratchet
import status
HOSTNAME = socket.gethostname()
TMP_HOSTS = '/tmp/hosts'
+CWMP_PATH = '/tmp/cwmp'
experiment.register('WifiNo2GClient')
@@ -63,7 +65,7 @@
WIFI_SETCLIENT = ['wifi', 'setclient', '--persist']
WIFI_STOPCLIENT = ['wifi', 'stopclient', '--persist']
- def __init__(self, band, wifi, command_lines, _status, wpa_control_interface):
+ def __init__(self, band, wifi, command_lines, wpa_control_interface):
self.band = band
self.wifi = wifi
self.command = command_lines.splitlines()
@@ -72,7 +74,6 @@
self.passphrase = None
self.interface_suffix = None
self.access_point = None
- self._status = _status
self._wpa_control_interface = wpa_control_interface
binwifi_option_attrs = {
@@ -161,22 +162,24 @@
Returns:
Whether the command succeeded.
"""
+ self.wifi.set_gateway_ip(None)
+ self.wifi.set_subnet(None)
command = self.WIFI_SETCLIENT + ['--ssid', self.ssid, '--band', self.band]
env = dict(os.environ)
if self.passphrase:
env['WIFI_CLIENT_PSK'] = self.passphrase
try:
- self._status.trying_wlan = True
+ self.wifi.status.trying_wlan = True
subprocess.check_output(command, stderr=subprocess.STDOUT, env=env)
except subprocess.CalledProcessError as e:
logging.error('Failed to start wifi client: %s', e.output)
- self._status.wlan_failed = True
+ self.wifi.status.wlan_failed = True
return False
return True
def _post_start_client(self):
- self._status.connected_to_wlan = True
+ self.wifi.status.connected_to_wlan = True
logging.info('Started wifi client on %s GHz', self.band)
self.wifi.attach_wpa_control(self._wpa_control_interface)
@@ -191,7 +194,7 @@
subprocess.check_output(self.WIFI_STOPCLIENT + ['-b', self.band],
stderr=subprocess.STDOUT)
# TODO(rofrankel): Make this work for dual-radio devices.
- self._status.connected_to_wlan = False
+ self.wifi.status.connected_to_wlan = False
logging.info('Stopped wifi client on %s GHz', self.band)
except subprocess.CalledProcessError as e:
logging.error('Failed to stop wifi client: %s', e.output)
@@ -221,6 +224,7 @@
IFPLUGD_ACTION = ['/etc/ifplugd/ifplugd.action']
BINWIFI = ['wifi']
UPLOAD_LOGS_AND_WAIT = ['timeout', '60', 'upload-logs-and-wait']
+ CWMP_WAKEUP = ['cwmp', 'wakeup']
def __init__(self,
bridge_interface='br0',
@@ -229,8 +233,9 @@
moca_tmp_dir='/tmp/cwmp/monitoring/moca2',
wpa_control_interface='/var/run/wpa_supplicant',
run_duration_s=1, interface_update_period=5,
- wifi_scan_period_s=120, wlan_retry_s=15, acs_update_wait_s=10,
- dhcp_wait_s=10, bssid_cycle_length_s=30):
+ wifi_scan_period_s=120, wlan_retry_s=15, associate_wait_s=15,
+ dhcp_wait_s=10, acs_start_wait_s=20, acs_finish_wait_s=120,
+ bssid_cycle_length_s=30):
self._tmp_dir = tmp_dir
self._config_dir = config_dir
@@ -242,8 +247,10 @@
self._interface_update_period = interface_update_period
self._wifi_scan_period_s = wifi_scan_period_s
self._wlan_retry_s = wlan_retry_s
- self._acs_update_wait_s = acs_update_wait_s
+ self._associate_wait_s = associate_wait_s
self._dhcp_wait_s = dhcp_wait_s
+ self._acs_start_wait_s = acs_start_wait_s
+ self._acs_finish_wait_s = acs_finish_wait_s
self._bssid_cycle_length_s = bssid_cycle_length_s
self._wlan_configuration = {}
self._try_to_upload_logs = False
@@ -263,7 +270,13 @@
self.create_wifi_interfaces()
- self._status = status.Status(self._status_dir)
+ for ifc in self.interfaces():
+ status_dir = os.path.join(self._status_dir, ifc.name)
+ if not os.path.exists(status_dir):
+ os.makedirs(status_dir)
+ ifc.status = status.Status(status_dir)
+ self._status = status.CompositeStatus(self._status_dir,
+ [i.status for i in self.interfaces()])
wm = pyinotify.WatchManager()
wm.add_watch(self._config_dir,
@@ -293,7 +306,7 @@
wifi_up = self.is_interface_up(wifi.name)
self.ifplugd_action(wifi.name, wifi_up)
if wifi_up:
- self._status.attached_to_wpa_supplicant = wifi.attach_wpa_control(
+ wifi.status.attached_to_wpa_supplicant = wifi.attach_wpa_control(
self._wpa_control_interface)
for path, prefix in ((self._tmp_dir, self.GATEWAY_FILE_PREFIX),
@@ -324,13 +337,32 @@
# Now that we've read any existing state, it's okay to let interfaces touch
# the routing table.
- for ifc in [self.bridge] + self.wifi:
+ for ifc in self.interfaces():
ifc.initialize()
logging.info('%s initialized', ifc.name)
self._interface_update_counter = 0
self._try_wlan_after = {'5': 0, '2.4': 0}
+ for wifi in self.wifi:
+ ratchet_name = '%s provisioning' % wifi.name
+ wifi.provisioning_ratchet = ratchet.Ratchet(ratchet_name, [
+ ratchet.Condition('trying_open', wifi.connected_to_open,
+ self._associate_wait_s,
+ callback=wifi.expire_connection_status_cache),
+ ratchet.Condition('waiting_for_dhcp', wifi.gateway, self._dhcp_wait_s,
+ callback=self.cwmp_wakeup),
+ ratchet.FileTouchedCondition('waiting_for_cwmp_wakeup',
+ os.path.join(CWMP_PATH, 'acscontact'),
+ self._acs_start_wait_s),
+ ratchet.FileTouchedCondition('waiting_for_acs_session',
+ os.path.join(CWMP_PATH, 'acsconnected'),
+ self._acs_finish_wait_s),
+ ], wifi.status)
+
+ def interfaces(self):
+ return [self.bridge] + self.wifi
+
def create_wifi_interfaces(self):
"""Create Wifi interfaces."""
@@ -444,7 +476,7 @@
if not wifi.attached():
logging.debug('Attempting to attach to wpa control interface for %s',
wifi.name)
- self._status.attached_to_wpa_supplicant = wifi.attach_wpa_control(
+ wifi.status.attached_to_wpa_supplicant = wifi.attach_wpa_control(
self._wpa_control_interface)
wifi.handle_wpa_events()
@@ -455,13 +487,13 @@
# If this interface is connected to the user's WLAN, there is nothing else
# to do.
if self._connected_to_wlan(wifi):
- self._status.connected_to_wlan = True
+ wifi.status.connected_to_wlan = True
logging.debug('Connected to WLAN on %s, nothing else to do.', wifi.name)
break
# This interface is not connected to the WLAN, so scan for potential
# routes to the ACS for provisioning.
- if (not self.acs() and
+ if ((not self.acs() or self.provisioning_failed(wifi)) and
not getattr(wifi, 'last_successful_bss_info', None) and
time.time() > wifi.last_wifi_scan_time + self._wifi_scan_period_s):
logging.debug('Performing scan on %s.', wifi.name)
@@ -479,12 +511,12 @@
wlan_configuration.start_client()
if self._connected_to_wlan(wifi):
logging.info('Joined WLAN on %s.', wifi.name)
- self._status.connected_to_wlan = True
+ wifi.status.connected_to_wlan = True
self._try_wlan_after[band] = 0
break
else:
logging.error('Failed to connect to WLAN on %s.', wifi.name)
- self._status.connected_to_wlan = False
+ wifi.status.connected_to_wlan = False
self._try_wlan_after[band] = time.time() + self._wlan_retry_s
else:
# If we are aren't on the WLAN, can ping the ACS, and haven't gotten a
@@ -494,7 +526,8 @@
# 2) cwmpd isn't writing a configuration, possibly because the device
# isn't registered to any accounts.
logging.debug('Unable to join WLAN on %s', wifi.name)
- self._status.connected_to_wlan = False
+ wifi.status.connected_to_wlan = False
+ provisioning_failed = self.provisioning_failed(wifi)
if self.acs():
logging.debug('Connected to ACS')
if self._try_to_upload_logs:
@@ -505,54 +538,52 @@
wifi.last_successful_bss_info = getattr(wifi,
'last_attempted_bss_info',
None)
+ if provisioning_failed:
+ wifi.last_successful_bss_info = None
+
now = time.time()
- if (self._wlan_configuration and
- hasattr(wifi, 'waiting_for_acs_since')):
- if now - wifi.waiting_for_acs_since > self._acs_update_wait_s:
- logging.info('ACS has not updated WLAN configuration; will retry '
- ' with old config.')
- for w in self.wifi:
- for b in w.bands:
- self._try_wlan_after[b] = now
- continue
+ if self._wlan_configuration:
+ logging.info('ACS has not updated WLAN configuration; will retry '
+ ' with old config.')
+ for w in self.wifi:
+ for b in w.bands:
+ self._try_wlan_after[b] = now
+ continue
# We don't want to want to log this spammily, so do exponential
# backoff.
elif (hasattr(wifi, 'complain_about_acs_at')
and now >= wifi.complain_about_acs_at):
- wait = wifi.complain_about_acs_at - wifi.waiting_for_acs_since
+ wait = wifi.complain_about_acs_at - self.provisioning_since(wifi)
logging.info('Can ping ACS, but no WLAN configuration for %ds.',
wait)
wifi.complain_about_acs_at += wait
- # If we didn't manage to join the WLAN and we don't have an ACS
- # connection, we should try to establish one.
- else:
- # If we are associated but waiting for a DHCP lease, try again later.
+ # If we didn't manage to join the WLAN, and we don't have an ACS
+ # connection or the ACS session failed, we should try another open AP.
+ if not self.acs() or provisioning_failed:
now = time.time()
- connected_to_open = (
- wifi.wpa_status().get('wpa_state', None) == 'COMPLETED' and
- wifi.wpa_status().get('key_mgmt', None) == 'NONE')
- wait_for_dhcp = (
- not wifi.gateway() and
- hasattr(wifi, 'waiting_for_dhcp_since') and
- now - wifi.waiting_for_dhcp_since < self._dhcp_wait_s)
- if connected_to_open and wait_for_dhcp:
- logging.debug('Waiting for DHCP lease after %ds.',
- now - wifi.waiting_for_acs_since)
+ if self._connected_to_open(wifi) and not provisioning_failed:
+ logging.debug('Waiting for provisioning for %ds.',
+ now - self.provisioning_since(wifi))
else:
- logging.debug('Not connected to ACS')
+ logging.debug('Not connected to ACS or provisioning failed')
self._try_next_bssid(wifi)
time.sleep(max(0, self._run_duration_s - (time.time() - start_time)))
def acs(self):
- result = self.bridge.acs() or any(wifi.acs() for wifi in self.wifi)
- self._status.can_reach_acs = result
+ result = False
+ for ifc in self.interfaces():
+ acs = ifc.acs()
+ ifc.status.can_reach_acs = acs
+ result |= acs
return result
def internet(self):
- result = self.bridge.internet() or any(wifi.internet()
- for wifi in self.wifi)
- self._status.can_reach_internet = result
+ result = False
+ for ifc in self.interfaces():
+ internet = ifc.internet()
+ ifc.status.can_reach_internet = internet
+ result |= internet
return result
def _update_interfaces_and_routes(self):
@@ -585,7 +616,7 @@
def _update_tmp_hosts(self):
"""Update the contents of /tmp/hosts."""
lowest_metric_interface = None
- for ifc in [self.bridge] + self.wifi:
+ for ifc in self.interfaces():
route = ifc.current_routes().get('default', None)
if route:
metric = route.get('metric', 0)
@@ -640,7 +671,7 @@
config.stop_access_point()
del self._wlan_configuration[band]
if not self._wlan_configuration:
- self._status.have_config = False
+ self.wifi_for_band(band).status.have_config = False
def _process_file(self, path, filename):
"""Process or ignore an updated file in a watched directory."""
@@ -674,7 +705,7 @@
wifi = self.wifi_for_band(band)
if wifi:
self._update_wlan_configuration(
- self.WLANConfiguration(band, wifi, contents, self._status,
+ self.WLANConfiguration(band, wifi, contents,
self._wpa_control_interface))
elif filename.startswith(self.ACCESS_POINT_FILE_PREFIX):
match = re.match(self.ACCESS_POINT_FILE_REGEXP, filename)
@@ -688,6 +719,10 @@
elif path == self._tmp_dir:
if filename.startswith(self.GATEWAY_FILE_PREFIX):
interface_name = filename.split(self.GATEWAY_FILE_PREFIX)[-1]
+ # If we get a new gateway file, we probably have a new subnet file, and
+ # we need the subnet in order to add the gateway route. So try to
+ # process the subnet file before doing anything further with this file.
+ self._process_file(path, self.SUBNET_FILE_PREFIX + interface_name)
ifc = self.interface_by_name(interface_name)
if ifc:
ifc.set_gateway_ip(contents)
@@ -721,7 +756,7 @@
self.ifplugd_action('moca0', has_moca)
def interface_by_name(self, interface_name):
- for ifc in [self.bridge] + self.wifi:
+ for ifc in self.interfaces():
if ifc.name == interface_name:
return ifc
@@ -776,20 +811,19 @@
if bss_info is not None:
logging.info('Attempting to connect to SSID %s (%s) for provisioning',
bss_info.ssid, bss_info.bssid)
- self._status.trying_open = True
- wifi.set_gateway_ip(None)
+ self.start_provisioning(wifi)
connected = self._try_bssid(wifi, bss_info)
if connected:
- self._status.connected_to_open = True
+ wifi.status.connected_to_open = True
now = time.time()
- wifi.waiting_for_acs_since = now
- wifi.waiting_for_dhcp_since = now
wifi.complain_about_acs_at = now + 5
logging.info('Attempting to provision via SSID %s', bss_info.ssid)
self._try_to_upload_logs = True
# If we can no longer connect to this, it's no longer successful.
- elif bss_info == last_successful_bss_info:
- wifi.last_successful_bss_info = None
+ else:
+ wifi.status.connected_to_open = False
+ if bss_info == last_successful_bss_info:
+ wifi.last_successful_bss_info = None
return connected
else:
# TODO(rofrankel): There are probably more cases in which this should be
@@ -797,7 +831,8 @@
# Relatedly, once we find ACS access on an open network we may want to
# save that SSID/BSSID and that first in future. If we do that then we
# can declare that provisioning has failed much more aggressively.
- self._status.provisioning_failed = True
+ logging.info('Ran out of BSSIDs to try on %s', wifi.name)
+ wifi.status.provisioning_failed = True
return False
@@ -814,6 +849,11 @@
in self._wlan_configuration.iteritems()
if band in wifi.bands))
+ def _connected_to_open(self, wifi):
+ result = wifi.connected_to_open()
+ wifi.status.connected_to_open = result
+ return result
+
def _update_wlan_configuration(self, wlan_configuration):
band = wlan_configuration.band
current = self._wlan_configuration.get(band, None)
@@ -827,7 +867,7 @@
if wlan_configuration.interface_suffix else '') + band)
wlan_configuration.access_point = os.path.exists(ap_file)
self._wlan_configuration[band] = wlan_configuration
- self._status.have_config = True
+ self.wifi_for_band(band).status.have_config = True
logging.info('Updated WLAN configuration for %s GHz', band)
self._update_access_point(wlan_configuration)
@@ -887,6 +927,31 @@
if subprocess.call(self.UPLOAD_LOGS_AND_WAIT) != 0:
logging.error('Failed to upload logs')
+ def cwmp_wakeup(self):
+ if subprocess.call(self.CWMP_WAKEUP) != 0:
+ logging.error('cwmp wakeup failed')
+
+ def start_provisioning(self, wifi):
+ wifi.set_gateway_ip(None)
+ wifi.set_subnet(None)
+ wifi.provisioning_ratchet.reset()
+
+ def provisioning_failed(self, wifi):
+ try:
+ wifi.provisioning_ratchet.check()
+ if wifi.provisioning_ratchet.done_after:
+ wifi.status.provisioning_completed = True
+ logging.info('%s successfully provisioned', wifi.name)
+ return False
+ except ratchet.TimeoutException:
+ wifi.status.provisioning_failed = True
+ logging.info('%s failed to provision: %s', wifi.name,
+ wifi.provisioning_ratchet.current_step().name)
+ return True
+
+ def provisioning_since(self, wifi):
+ return wifi.provisioning_ratchet.t0
+
def _wifi_show():
try:
diff --git a/conman/connection_manager_test.py b/conman/connection_manager_test.py
index 7cf1785..a64ab79 100755
--- a/conman/connection_manager_test.py
+++ b/conman/connection_manager_test.py
@@ -255,8 +255,7 @@
'WIFI_PSK=abcdWIFI_PSK=qwer', 'wifi', 'set', '-P', '-b', '5',
'--bridge=br0', '-s', 'my ssid=1', '--interface-suffix', '_suffix',
])
- config = WLANConfiguration('5', interface_test.Wifi('wcli0', 20), cmd, None,
- None)
+ config = WLANConfiguration('5', interface_test.Wifi('wcli0', 20), cmd, None)
wvtest.WVPASSEQ('my ssid=1', config.ssid)
wvtest.WVPASSEQ('abcdWIFI_PSK=qwer', config.passphrase)
@@ -291,6 +290,7 @@
IFPLUGD_ACTION = ['echo', 'ifplugd.action']
BINWIFI = ['echo', 'wifi']
UPLOAD_LOGS_AND_WAIT = ['echo', 'upload-logs-and-wait']
+ CWMP_WAKEUP = ['echo', 'cwmp', 'wakeup']
def __init__(self, *args, **kwargs):
self._binwifi_commands = []
@@ -326,6 +326,7 @@
# Will s3 fail to acquire a DHCP lease?
self.dhcp_failure_on_s3 = False
self.log_upload_count = 0
+ self.acs_session_fails = False
def create_wifi_interfaces(self):
super(ConnectionManager, self).create_wifi_interfaces()
@@ -461,6 +462,7 @@
gateway_file = os.path.join(self._tmp_dir,
self.GATEWAY_FILE_PREFIX + interface_name)
with open(gateway_file, 'w') as f:
+ logging.debug('Writing gateway file %s', gateway_file)
# This value doesn't matter to conman, so it's fine to hard code it here.
f.write('192.168.1.1')
@@ -468,6 +470,7 @@
subnet_file = os.path.join(self._tmp_dir,
self.SUBNET_FILE_PREFIX + interface_name)
with open(subnet_file, 'w') as f:
+ logging.debug('Writing subnet file %s', subnet_file)
# This value doesn't matter to conman, so it's fine to hard code it here.
f.write('192.168.1.0/24')
@@ -509,6 +512,19 @@
def has_status_files(self, files):
return not set(files) - set(os.listdir(self._status_dir))
+ def cwmp_wakeup(self):
+ super(ConnectionManager, self).cwmp_wakeup()
+ self.write_acscontact()
+ if self.acs():
+ self.write_acsconnected()
+
+ def write_acscontact(self):
+ open(os.path.join(connection_manager.CWMP_PATH, 'acscontact'), 'w')
+
+ def write_acsconnected(self):
+ if not self.acs_session_fails:
+ open(os.path.join(connection_manager.CWMP_PATH, 'acsconnected'), 'w')
+
def wlan_config_filename(path, band):
return os.path.join(path, 'command.%s' % band)
@@ -560,7 +576,10 @@
interface_update_period = 5
wifi_scan_period = 15
wifi_scan_period_s = run_duration_s * wifi_scan_period
+ associate_wait_s = 0
dhcp_wait_s = .5
+ acs_start_wait_s = 0
+ acs_finish_wait_s = 0
# pylint: disable=protected-access
old_wifi_show = connection_manager._wifi_show
@@ -579,6 +598,7 @@
moca_tmp_dir = tempfile.mkdtemp()
wpa_control_interface = tempfile.mkdtemp()
FrenzyWifi.WPACtrl.WIFIINFO_PATH = tempfile.mkdtemp()
+ connection_manager.CWMP_PATH = tempfile.mkdtemp()
for band, access_point in wlan_configs.iteritems():
write_wlan_config(config_dir, band, 'initial ssid', 'initial psk')
@@ -595,14 +615,13 @@
run_duration_s=run_duration_s,
interface_update_period=interface_update_period,
wifi_scan_period_s=wifi_scan_period_s,
+ associate_wait_s=associate_wait_s,
dhcp_wait_s=dhcp_wait_s,
+ acs_start_wait_s=acs_start_wait_s,
+ acs_finish_wait_s=acs_finish_wait_s,
bssid_cycle_length_s=1,
**cm_kwargs)
- c.test_interface_update_period = interface_update_period
- c.test_wifi_scan_period = wifi_scan_period
- c.test_dhcp_wait_s = dhcp_wait_s
-
f(c)
finally:
if os.path.exists(connection_manager.TMP_HOSTS):
@@ -612,6 +631,7 @@
shutil.rmtree(moca_tmp_dir)
shutil.rmtree(wpa_control_interface)
shutil.rmtree(FrenzyWifi.WPACtrl.WIFIINFO_PATH)
+ shutil.rmtree(connection_manager.CWMP_PATH)
# pylint: disable=protected-access
connection_manager._wifi_show = old_wifi_show
connection_manager._get_quantenna_interfaces = old_gqi
@@ -889,7 +909,7 @@
c.can_connect_to_s2 = True
# Give it time to try all BSSIDs. This means sleeping long enough that
# everything in the cycler is active, then doing n+1 loops (the n+1st loop is
- # when we decided that the SSID in the nth loop was successful).
+ # when we decide that the SSID in the nth loop was successful).
time.sleep(c._bssid_cycle_length_s)
for _ in range(len(c.wifi_for_band(band).cycler) + 1):
c.run_once()
@@ -931,13 +951,68 @@
last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
wvtest.WVPASSEQ(last_bss_info.ssid, 's3')
wvtest.WVPASSEQ(last_bss_info.bssid, 'ff:ee:dd:cc:bb:aa')
+ wvtest.WVPASS(c.has_status_files([status.P.WAITING_FOR_DHCP,
+ status.P.WAITING_FOR_PROVISIONING]))
# Third iteration: sleep for dhcp_wait_s and check that we try another AP.
- time.sleep(c.test_dhcp_wait_s)
+ time.sleep(c._dhcp_wait_s)
c.run_once()
last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
wvtest.WVPASSNE(last_bss_info.ssid, 's3')
wvtest.WVPASSNE(last_bss_info.bssid, 'ff:ee:dd:cc:bb:aa')
+ # Now repeat the above, but for an ACS session that takes a while. We don't
+ # necessarily want to leave if it fails (so we don't want the third check),
+ # but we do want to make sure we don't leave while we're still waiting for it.
+ #
+ # Unlike DHCP, which we can always simulate working immediately above, it is
+ # wrong to simulate ACS sessions working for connections without ACS access.
+ # This means we can either always wait for the ACS session timeout in every
+ # test above, making the tests much slower, or we can set that timeout to 0
+ # and then be a little gross here and change it. The latter is unfortunately
+ # the lesser evil, because slow tests are bad.
+ del c.wifi_for_band(band).cycler
+ c.dhcp_failure_on_s3 = False
+ c.acs_session_fails = True
+ # First iteration: check that we try s3.
+ c.run_until_scan(band)
+ last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
+ wvtest.WVPASSEQ(last_bss_info.ssid, 's3')
+ wvtest.WVPASSEQ(last_bss_info.bssid, 'ff:ee:dd:cc:bb:aa')
+
+ # This is the gross part.
+ # pylint: disable=protected-access
+ c.wifi_for_band(band).provisioning_ratchet.steps[3].timeout = 0.5
+
+ # Second iteration: check that we don't leave while waiting.
+ c.run_once()
+ last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
+ wvtest.WVPASSEQ(last_bss_info.ssid, 's3')
+ wvtest.WVPASSEQ(last_bss_info.bssid, 'ff:ee:dd:cc:bb:aa')
+ wvtest.WVPASS(c.has_status_files([status.P.WAITING_FOR_ACS_SESSION,
+ status.P.WAITING_FOR_PROVISIONING]))
+ time.sleep(0.5)
+ c.run_once()
+ wvtest.WVPASS(c.has_status_files([status.P.PROVISIONING_FAILED]))
+ c.wifi_for_band(band).provisioning_ratchet.steps[3].timeout = 0
+
+ # Finally, test successful provisioning.
+ del c.wifi_for_band(band).cycler
+ c.dhcp_failure_on_s3 = False
+ c.acs_session_fails = False
+ # First iteration: check that we try s3.
+ c.run_until_scan(band)
+ last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
+ wvtest.WVPASSEQ(last_bss_info.ssid, 's3')
+ wvtest.WVPASSEQ(last_bss_info.bssid, 'ff:ee:dd:cc:bb:aa')
+
+ c.run_once()
+ last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
+ wvtest.WVPASSEQ(last_bss_info.ssid, 's3')
+ wvtest.WVPASSEQ(last_bss_info.bssid, 'ff:ee:dd:cc:bb:aa')
+ wvtest.WVFAIL(c.has_status_files([status.P.WAITING_FOR_ACS_SESSION,
+ status.P.WAITING_FOR_PROVISIONING]))
+ wvtest.WVPASS(c.has_status_files([status.P.PROVISIONING_COMPLETED]))
+
@wvtest.wvtest
@connection_manager_test(WIFI_SHOW_OUTPUT_MARVELL8897)
diff --git a/conman/interface.py b/conman/interface.py
index 2d0fbbe..cdeaf67 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -83,6 +83,8 @@
return False
self.add_routes()
+ if 'default' not in self.current_routes():
+ return False
cmd = [self.CONNECTION_CHECK, '-I', self.name]
if check_acs:
@@ -547,6 +549,10 @@
self.initial_ssid = None
super(Wifi, self).initialize()
+ def connected_to_open(self):
+ return (self.wpa_status().get('wpa_state', None) == 'COMPLETED' and
+ self.wpa_status().get('key_mgmt', None) == 'NONE')
+
class FrenzyWPACtrl(object):
"""A WPACtrl for Frenzy devices.
diff --git a/conman/interface_test.py b/conman/interface_test.py
index 79c58ae..dd5e037 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -248,8 +248,10 @@
super(Wifi, self).detach_wpa_control()
def start_wpa_supplicant_testonly(self, path):
- logging.debug('Starting fake wpa_supplicant for %s', self.name)
- open(os.path.join(path, self.name), 'w')
+ wpa_socket = os.path.join(path, self.name)
+ logging.debug('Starting fake wpa_supplicant for %s: %s',
+ self.name, wpa_socket)
+ open(wpa_socket, 'w')
def kill_wpa_supplicant_testonly(self, path):
logging.debug('Killing fake wpa_supplicant for %s', self.name)
diff --git a/conman/ratchet.py b/conman/ratchet.py
new file mode 100644
index 0000000..7873c39
--- /dev/null
+++ b/conman/ratchet.py
@@ -0,0 +1,181 @@
+#!/usr/bin/python -S
+
+"""Utility for ensuring a series of events occur or time out."""
+
+import logging
+import os
+import time
+
+# This has to be called before another module calls it with a higher log level.
+# pylint: disable=g-import-not-at-top
+logging.basicConfig(level=logging.DEBUG)
+
+
+class TimeoutException(Exception):
+ pass
+
+
+class Condition(object):
+ """Wrapper for a function that may time out."""
+
+ def __init__(self, name, evaluate, timeout, logger=None, callback=None):
+ self.name = name
+ if evaluate:
+ self.evaluate = evaluate
+ self.timeout = timeout
+ self.logger = logger or logging
+ self.callback = callback
+ self.reset()
+
+ def reset(self, t0=None, start_at=None):
+ """Reset the Condition to an initial state.
+
+ Takes two different timestamp values to account for uncertainty in when a
+ previous condition may have been met.
+
+ Args:
+ t0: The timestamp after which to evaluate the condition.
+ start_at: The timestamp from which to compute the timeout.
+ """
+ self.t0 = t0 or time.time()
+ self.start_at = start_at or time.time()
+ self.done_after = None
+ self.done_by = None
+ self.timed_out = False
+ self.not_done_before = self.t0
+
+ def check(self):
+ """Check whether the condition has completed or timed out."""
+ if self.timed_out:
+ raise TimeoutException()
+
+ if self.done_after:
+ return True
+
+ if self.evaluate():
+ self.mark_done()
+ return True
+
+ now = time.time()
+ if now > self.start_at + self.timeout:
+ self.timed_out = True
+ self.logger.info('%s timed out after %.2f seconds',
+ self.name, now - self.t0)
+ raise TimeoutException()
+
+ self.not_done_before = time.time()
+ return False
+
+ def mark_done(self):
+ # In general, we don't know when a condition finished, but we know it was
+ # *after* whenever it was most recently not done.
+ self.done_after = self.not_done_before
+ self.done_by = time.time()
+ self.logger.info('%s completed after %.2f seconds',
+ self.name, self.done_by - self.t0)
+
+ if self.callback:
+ self.callback()
+
+
+class FileExistsCondition(Condition):
+ """A condition that checks for the existence of a file."""
+
+ def __init__(self, name, filename, timeout):
+ self._filename = filename
+ super(FileExistsCondition, self).__init__(name, None, timeout)
+
+ def evaluate(self):
+ return os.path.exists(self._filename)
+
+ def mtime(self):
+ if os.path.exists(self._filename):
+ return os.stat(self._filename).st_mtime
+
+ return None
+
+ def mark_done(self):
+ super(FileExistsCondition, self).mark_done()
+ # We have to check this because the file could have been deleted while this
+ # was being called. But this condition should almost always be true.
+ mtime = self.mtime()
+ if mtime:
+ self.done_after = self.done_by = mtime
+
+
+class FileTouchedCondition(FileExistsCondition):
+ """A condition that checks that a file was touched after a certain time."""
+
+ def reset(self, t0=None, start_at=None):
+ mtime = self.mtime
+ if t0 and mtime and mtime < t0:
+ self.initial_mtime = self.mtime()
+ else:
+ self.initial_mtime = None
+ super(FileTouchedCondition, self).reset(t0, start_at)
+
+ def evaluate(self):
+ if not super(FileTouchedCondition, self).evaluate():
+ return False
+
+ if self.initial_mtime:
+ return self.mtime() > self.initial_mtime
+
+ return self.mtime() >= self.t0
+
+
+class Ratchet(Condition):
+ """A condition that comprises a series of subconditions."""
+
+ def __init__(self, name, steps, status):
+ self.name = name
+ self.steps = steps
+ for step in self.steps:
+ step.logger = logging.getLogger(self.name)
+ self._status = status
+ super(Ratchet, self).__init__(name, None, 0)
+
+ def reset(self):
+ self._current_step = 0
+ for step in self.steps:
+ step.reset()
+ self._set_step_status(step, False)
+ self._set_current_step_status(True)
+ super(Ratchet, self).reset()
+
+ # Override check rather than evaluate because we don't want the Ratchet to
+ # time out unless one of its steps does.
+ def check(self):
+ if not self.done_after:
+ while self.current_step().check():
+ if not self.advance():
+ self.mark_done()
+ break
+
+ return self.done_after
+
+ def current_step(self):
+ return self.steps[self._current_step]
+
+ def on_final_step(self):
+ return self._current_step == len(self.steps) - 1
+
+ def advance(self):
+ if self.on_final_step():
+ return False
+ else:
+ prev_step = self.current_step()
+ self._current_step += 1
+ self.current_step().start_at = prev_step.done_by
+ self._set_current_step_status(True)
+ return True
+
+ def mark_done(self):
+ super(Ratchet, self).mark_done()
+ self.done_after = self.steps[-1].done_after
+
+ def _set_step_status(self, step, value):
+ setattr(self._status, step.name, value)
+
+ def _set_current_step_status(self, value):
+ self._set_step_status(self.current_step(), value)
diff --git a/conman/ratchet_test.py b/conman/ratchet_test.py
new file mode 100755
index 0000000..10c10ab
--- /dev/null
+++ b/conman/ratchet_test.py
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+
+"""Tests for ratchet.py."""
+
+import os
+import shutil
+import tempfile
+import time
+
+import ratchet
+import status
+from wvtest import wvtest
+
+
+@wvtest.wvtest
+def condition_test():
+ """Test basic Condition functionality."""
+ x = y = 0
+ callback_sink = []
+ cx = ratchet.Condition('cx', lambda: x != 0, 0)
+ cy = ratchet.Condition('cx', lambda: y != 0, 0.1,
+ callback=lambda: callback_sink.append([0]))
+ wvtest.WVEXCEPT(ratchet.TimeoutException, cx.check)
+ wvtest.WVFAIL(cy.check())
+ time.sleep(0.1)
+ wvtest.WVEXCEPT(ratchet.TimeoutException, cy.check)
+
+ x = 1
+ wvtest.WVEXCEPT(ratchet.TimeoutException, cx.check)
+ cx.reset()
+ wvtest.WVPASS(cx.check())
+
+ y = 1
+ cy.reset()
+ wvtest.WVPASS(cy.check())
+ wvtest.WVPASSEQ(len(callback_sink), 1)
+ # Callback shouldn't fire again.
+ wvtest.WVPASS(cy.check())
+ wvtest.WVPASSEQ(len(callback_sink), 1)
+ cy.reset()
+ wvtest.WVPASS(cy.check())
+ wvtest.WVPASSEQ(len(callback_sink), 2)
+
+
+@wvtest.wvtest
+def file_condition_test():
+ """Test File*Condition functionality."""
+ try:
+ _, filename = tempfile.mkstemp()
+ c_exists = ratchet.FileExistsCondition('c exists', filename, 0.1)
+ c_mtime = ratchet.FileTouchedCondition('c mtime', filename, 0.1)
+ wvtest.WVPASS(c_exists.check())
+ wvtest.WVFAIL(c_mtime.check())
+ # mtime precision is too low to notice that we're touching the file *after*
+ # capturing its initial mtime rather than at the same time, so take a short
+ # nap before touching it.
+ time.sleep(0.01)
+ open(filename, 'w')
+ wvtest.WVPASS(c_mtime.check())
+
+ # Test that old mtimes don't count.
+ time.sleep(0.01)
+ c_mtime.reset()
+ wvtest.WVFAIL(c_mtime.check())
+ time.sleep(0.1)
+ wvtest.WVEXCEPT(ratchet.TimeoutException, c_mtime.check)
+
+ # Test t0 and start_at.
+ os.unlink(filename)
+ now = time.time()
+ c_mtime.reset(t0=now, start_at=now + 0.2)
+ wvtest.WVFAIL(c_mtime.check())
+ time.sleep(0.15)
+ wvtest.WVFAIL(c_mtime.check())
+ open(filename, 'w')
+ wvtest.WVPASS(c_mtime.check())
+
+ finally:
+ os.unlink(filename)
+
+
+@wvtest.wvtest
+def ratchet_test():
+ """Test Ratchet functionality."""
+
+ class P(object):
+ X = 'X'
+ Y = 'Y'
+ Z = 'Z'
+ status.P = P
+ status.IMPLICATIONS = {}
+
+ status_export_path = tempfile.mkdtemp()
+ try:
+ x = y = z = 0
+ r = ratchet.Ratchet('test ratchet', [
+ ratchet.Condition('x', lambda: x, 0.1),
+ ratchet.Condition('y', lambda: y, 0.1),
+ ratchet.Condition('z', lambda: z, 0.1),
+ ], status.Status(status_export_path))
+ x = y = 1
+
+ # Test that timeouts are not just summed, but start whenever the previous
+ # step completed.
+ wvtest.WVPASSEQ(r._current_step, 0) # pylint: disable=protected-access
+ wvtest.WVPASS(os.path.isfile(os.path.join(status_export_path, 'X')))
+ wvtest.WVFAIL(os.path.isfile(os.path.join(status_export_path, 'Y')))
+ wvtest.WVFAIL(os.path.isfile(os.path.join(status_export_path, 'Z')))
+ time.sleep(0.05)
+ wvtest.WVFAIL(r.check())
+ wvtest.WVPASSEQ(r._current_step, 2) # pylint: disable=protected-access
+ wvtest.WVPASS(os.path.isfile(os.path.join(status_export_path, 'X')))
+ wvtest.WVPASS(os.path.isfile(os.path.join(status_export_path, 'Y')))
+ wvtest.WVPASS(os.path.isfile(os.path.join(status_export_path, 'Z')))
+ wvtest.WVFAIL(r.check())
+ wvtest.WVPASSEQ(r._current_step, 2) # pylint: disable=protected-access
+ time.sleep(0.1)
+ wvtest.WVEXCEPT(ratchet.TimeoutException, r.check)
+
+ x = y = z = 1
+ r.reset()
+ wvtest.WVPASS(r.check())
+ finally:
+ shutil.rmtree(status_export_path)
+
+
+if __name__ == '__main__':
+ wvtest.wvtest_main()
diff --git a/conman/status.py b/conman/status.py
index 7f75682..8f8d3a1 100644
--- a/conman/status.py
+++ b/conman/status.py
@@ -32,17 +32,25 @@
PROVISIONING_FAILED = 'PROVISIONING_FAILED'
ATTACHED_TO_WPA_SUPPLICANT = 'ATTACHED_TO_WPA_SUPPLICANT'
+ WAITING_FOR_PROVISIONING = 'WAITING_FOR_PROVISIONING'
+ WAITING_FOR_DHCP = 'WAITING_FOR_DHCP'
+ WAITING_FOR_CWMP_WAKEUP = 'WAITING_FOR_CWMP_WAKEUP'
+ WAITING_FOR_ACS_SESSION = 'WAITING_FOR_ACS_SESSION'
+ PROVISIONING_COMPLETED = 'PROVISIONING_COMPLETED'
+
# Format: { proposition: (implications, counter-implications), ... }
# If you want to add a new proposition to the Status class, just edit this dict.
IMPLICATIONS = {
P.TRYING_OPEN: (
(),
- (P.CONNECTED_TO_OPEN, P.TRYING_WLAN, P.CONNECTED_TO_WLAN)
+ (P.CONNECTED_TO_OPEN, P.TRYING_WLAN, P.CONNECTED_TO_WLAN,
+ P.WAITING_FOR_PROVISIONING)
),
P.TRYING_WLAN: (
(),
- (P.TRYING_OPEN, P.CONNECTED_TO_OPEN, P.CONNECTED_TO_WLAN)
+ (P.TRYING_OPEN, P.CONNECTED_TO_OPEN, P.CONNECTED_TO_WLAN,
+ P.WAITING_FOR_PROVISIONING)
),
P.WLAN_FAILED: (
(),
@@ -54,7 +62,8 @@
),
P.CONNECTED_TO_WLAN: (
(P.HAVE_WORKING_CONFIG,),
- (P.CONNECTED_TO_OPEN, P.TRYING_OPEN, P.TRYING_WLAN)
+ (P.CONNECTED_TO_OPEN, P.TRYING_OPEN, P.TRYING_WLAN,
+ P.WAITING_FOR_PROVISIONING)
),
P.CAN_REACH_ACS: (
(P.COULD_REACH_ACS,),
@@ -62,11 +71,11 @@
),
P.COULD_REACH_ACS: (
(),
- (P.PROVISIONING_FAILED,),
+ (),
),
P.PROVISIONING_FAILED: (
(),
- (P.COULD_REACH_ACS,),
+ (P.WAITING_FOR_PROVISIONING, P.PROVISIONING_COMPLETED,),
),
P.HAVE_WORKING_CONFIG: (
(P.HAVE_CONFIG,),
@@ -75,24 +84,60 @@
P.ATTACHED_TO_WPA_SUPPLICANT: (
(),
(),
- )
+ ),
+ P.WAITING_FOR_PROVISIONING: (
+ (P.CONNECTED_TO_OPEN,),
+ (),
+ ),
+ P.WAITING_FOR_DHCP: (
+ (P.WAITING_FOR_PROVISIONING,),
+ (P.WAITING_FOR_CWMP_WAKEUP, P.WAITING_FOR_ACS_SESSION),
+ ),
+ P.WAITING_FOR_CWMP_WAKEUP: (
+ (P.WAITING_FOR_PROVISIONING,),
+ (P.WAITING_FOR_DHCP, P.WAITING_FOR_ACS_SESSION),
+ ),
+ P.WAITING_FOR_ACS_SESSION: (
+ (P.WAITING_FOR_PROVISIONING,),
+ (P.WAITING_FOR_DHCP, P.WAITING_FOR_CWMP_WAKEUP),
+ ),
+ P.PROVISIONING_COMPLETED: (
+ (),
+ (P.WAITING_FOR_PROVISIONING, P.PROVISIONING_FAILED,),
+ ),
}
-class Proposition(object):
+class ExportedValue(object):
+
+ def __init__(self, name, export_path):
+ self._name = name
+ self._export_path = export_path
+
+ def export(self):
+ filepath = os.path.join(self._export_path, self._name)
+ if self.value():
+ if not os.path.exists(filepath):
+ open(filepath, 'w')
+ else:
+ if os.path.exists(filepath):
+ os.unlink(filepath)
+
+
+class Proposition(ExportedValue):
"""Represents a proposition.
May imply truth or falsity of other propositions.
"""
- def __init__(self, name, export_path):
- self._name = name
- self._export_path = export_path
+ def __init__(self, *args, **kwargs):
+ super(Proposition, self).__init__(*args, **kwargs)
self._value = None
self._implications = set()
self._counter_implications = set()
self._impliers = set()
self._counter_impliers = set()
+ self.parents = set()
def implies(self, implication):
self._counter_implications.discard(implication)
@@ -115,11 +160,20 @@
self._counter_impliers.add(counter_implier)
def set(self, value):
+ """Set this Proposition's value.
+
+ If the value changed, update any dependent values/files.
+
+ Args:
+ value: The new value.
+ """
if value == self._value:
return
self._value = value
self.export()
+ for parent in self.parents:
+ parent.export()
logging.debug('%s is now %s', self._name, self._value)
if value:
@@ -135,14 +189,20 @@
for implier in self._impliers:
implier.set(False)
- def export(self):
- filepath = os.path.join(self._export_path, self._name)
- if self._value:
- if not os.path.exists(filepath):
- open(filepath, 'w')
- else:
- if os.path.exists(filepath):
- os.unlink(filepath)
+ def value(self):
+ return self._value
+
+
+class Disjunction(ExportedValue):
+
+ def __init__(self, name, export_path, disjuncts):
+ super(Disjunction, self).__init__(name, export_path)
+ self.disjuncts = disjuncts
+ for disjunct in self.disjuncts:
+ disjunct.parents.add(self)
+
+ def value(self):
+ return any(d.value() for d in self.disjuncts)
class Status(object):
@@ -154,6 +214,9 @@
self._export_path = export_path
+ self._set_up_propositions()
+
+ def _set_up_propositions(self):
self._propositions = {
p: Proposition(p, self._export_path)
for p in dict(inspect.getmembers(P)) if not p.startswith('_')
@@ -166,7 +229,7 @@
self._propositions[p].implies_not(
self._propositions[counter_implication])
- def _proposition(self, p):
+ def proposition(self, p):
return self._propositions[p]
def __setattr__(self, attr, value):
@@ -187,3 +250,17 @@
return
super(Status, self).__setattr__(attr, value)
+
+
+class CompositeStatus(Status):
+
+ def __init__(self, export_path, children):
+ self._children = children
+ super(CompositeStatus, self).__init__(export_path)
+
+ def _set_up_propositions(self):
+ self._propositions = {
+ p: Disjunction(p, self._export_path,
+ [c.proposition(p) for c in self._children])
+ for p in dict(inspect.getmembers(P)) if not p.startswith('_')
+ }
diff --git a/conman/status_test.py b/conman/status_test.py
index 03223f8..befebbf 100755
--- a/conman/status_test.py
+++ b/conman/status_test.py
@@ -17,6 +17,10 @@
return os.path.exists(os.path.join(path, filename))
+def has_file(s, filename):
+ return file_in(s._export_path, filename)
+
+
@wvtest.wvtest
def test_proposition():
export_path = tempfile.mkdtemp()
@@ -71,51 +75,85 @@
@wvtest.wvtest
def test_status():
- export_path = tempfile.mkdtemp()
+ export_path_s = tempfile.mkdtemp()
+ export_path_t = tempfile.mkdtemp()
+ export_path_st = tempfile.mkdtemp()
try:
- s = status.Status(export_path)
+ s = status.Status(export_path_s)
+ t = status.Status(export_path_t)
+ st = status.CompositeStatus(export_path_st, [s, t])
# Sanity check that there are no contradictions.
for p, (want_true, want_false) in status.IMPLICATIONS.iteritems():
setattr(s, p.lower(), True)
- wvtest.WVPASS(file_in(export_path, p))
+ wvtest.WVPASS(has_file(s, p))
for wt in want_true:
- wvtest.WVPASS(file_in(export_path, wt))
+ wvtest.WVPASS(has_file(s, wt))
for wf in want_false:
- wvtest.WVFAIL(file_in(export_path, wf))
+ wvtest.WVFAIL(has_file(s, wf))
+
+ def check_exported(check_s, check_t, filename):
+ wvtest.WVPASSEQ(check_s, has_file(s, filename))
+ wvtest.WVPASSEQ(check_t, has_file(t, filename))
+ wvtest.WVPASSEQ(check_s or check_t, has_file(st, filename))
s.trying_wlan = True
- wvtest.WVPASS(file_in(export_path, status.P.TRYING_WLAN))
- wvtest.WVFAIL(file_in(export_path, status.P.CONNECTED_TO_WLAN))
+ t.trying_wlan = False
+ check_exported(True, False, status.P.TRYING_WLAN)
+ check_exported(False, False, status.P.CONNECTED_TO_WLAN)
s.connected_to_open = True
- wvtest.WVPASS(file_in(export_path, status.P.CONNECTED_TO_OPEN))
- wvtest.WVFAIL(file_in(export_path, status.P.CONNECTED_TO_WLAN))
+ check_exported(True, False, status.P.CONNECTED_TO_OPEN)
+ check_exported(False, False, status.P.CONNECTED_TO_WLAN)
s.connected_to_wlan = True
- wvtest.WVPASS(file_in(export_path, status.P.CONNECTED_TO_WLAN))
- wvtest.WVPASS(file_in(export_path, status.P.HAVE_WORKING_CONFIG))
- wvtest.WVFAIL(file_in(export_path, status.P.CONNECTED_TO_OPEN))
- wvtest.WVFAIL(file_in(export_path, status.P.TRYING_WLAN))
- wvtest.WVFAIL(file_in(export_path, status.P.TRYING_OPEN))
+ t.trying_wlan = True
+ check_exported(True, False, status.P.CONNECTED_TO_WLAN)
+ check_exported(True, False, status.P.HAVE_WORKING_CONFIG)
+ check_exported(False, False, status.P.CONNECTED_TO_OPEN)
+ check_exported(False, True, status.P.TRYING_WLAN)
+ check_exported(False, False, status.P.TRYING_OPEN)
s.can_reach_acs = True
s.can_reach_internet = True
- wvtest.WVPASS(file_in(export_path, status.P.CAN_REACH_ACS))
- wvtest.WVPASS(file_in(export_path, status.P.COULD_REACH_ACS))
- wvtest.WVPASS(file_in(export_path, status.P.CAN_REACH_INTERNET))
- wvtest.WVFAIL(file_in(export_path, status.P.PROVISIONING_FAILED))
+ check_exported(True, False, status.P.CAN_REACH_ACS)
+ check_exported(True, False, status.P.COULD_REACH_ACS)
+ check_exported(True, False, status.P.CAN_REACH_INTERNET)
+ check_exported(False, False, status.P.PROVISIONING_FAILED)
# These should not have changed
- wvtest.WVPASS(file_in(export_path, status.P.CONNECTED_TO_WLAN))
- wvtest.WVPASS(file_in(export_path, status.P.HAVE_WORKING_CONFIG))
- wvtest.WVFAIL(file_in(export_path, status.P.CONNECTED_TO_OPEN))
- wvtest.WVFAIL(file_in(export_path, status.P.TRYING_WLAN))
- wvtest.WVFAIL(file_in(export_path, status.P.TRYING_OPEN))
+ check_exported(True, False, status.P.CONNECTED_TO_WLAN)
+ check_exported(True, False, status.P.HAVE_WORKING_CONFIG)
+ check_exported(False, False, status.P.CONNECTED_TO_OPEN)
+ check_exported(False, True, status.P.TRYING_WLAN)
+ check_exported(False, False, status.P.TRYING_OPEN)
+
+ # Test provisioning statuses.
+ s.waiting_for_dhcp = True
+ check_exported(False, True, status.P.TRYING_WLAN)
+ check_exported(False, False, status.P.TRYING_OPEN)
+ check_exported(False, False, status.P.CONNECTED_TO_WLAN)
+ check_exported(True, False, status.P.CONNECTED_TO_OPEN)
+ check_exported(True, False, status.P.WAITING_FOR_PROVISIONING)
+ check_exported(True, False, status.P.WAITING_FOR_DHCP)
+ s.waiting_for_cwmp_wakeup = True
+ check_exported(False, False, status.P.WAITING_FOR_DHCP)
+ check_exported(True, False, status.P.WAITING_FOR_CWMP_WAKEUP)
+ s.waiting_for_acs_session = True
+ check_exported(False, False, status.P.WAITING_FOR_DHCP)
+ check_exported(False, False, status.P.WAITING_FOR_CWMP_WAKEUP)
+ check_exported(True, False, status.P.WAITING_FOR_ACS_SESSION)
+ s.provisioning_completed = True
+ check_exported(False, False, status.P.WAITING_FOR_PROVISIONING)
+ check_exported(False, False, status.P.WAITING_FOR_DHCP)
+ check_exported(False, False, status.P.WAITING_FOR_CWMP_WAKEUP)
+ check_exported(False, False, status.P.WAITING_FOR_CWMP_WAKEUP)
finally:
- shutil.rmtree(export_path)
+ shutil.rmtree(export_path_s)
+ shutil.rmtree(export_path_t)
+ shutil.rmtree(export_path_st)
if __name__ == '__main__':
diff --git a/devcert/gfiber-device-cert b/devcert/gfiber-device-cert
index 10e259a..bc9ca07 100755
--- a/devcert/gfiber-device-cert
+++ b/devcert/gfiber-device-cert
@@ -13,7 +13,8 @@
host=$(
hostname -f |
sed -e 's/\([^.]*\.[^.]*\).*/\1/' \
- -e 's/\./_/g'
+ -e 's/\./_/g' \
+ -e 's/-//g'
)
full_cn="f88fca-Linux-$host"
diff --git a/gpio-mailbox/Makefile b/gpio-mailbox/Makefile
index eaaee20..b73d99f 100644
--- a/gpio-mailbox/Makefile
+++ b/gpio-mailbox/Makefile
@@ -33,6 +33,10 @@
CFLAGS += -DWINDCHARGER
else ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gfch100)
CFLAGS += -DGFCH100
+else ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gfrg240)
+ # stub out gpio mailbox on gfrg240
+ CFLAGS += -DSTUB
+ LDFLAGS += -lm
else ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),kvm)
CFLAGS += -DSTUB
LDFLAGS += -lm
diff --git a/gpio-mailbox/brcm-direct.c b/gpio-mailbox/brcm-direct.c
new file mode 100644
index 0000000..8abacbb
--- /dev/null
+++ b/gpio-mailbox/brcm-direct.c
@@ -0,0 +1,653 @@
+#ifdef BROADCOM
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "brcm-platform.h"
+
+#define UNUSED __attribute__((unused))
+#define DEVMEM "/dev/mem"
+
+static volatile void* mmap_addr = MAP_FAILED;
+static size_t mmap_size = 0;
+static int mmap_fd = -1;
+
+static void init_gfhd200(UNUSED struct platform_info* p);
+static void init_gfhd254(UNUSED struct platform_info* p);
+static double get_avs_voltage_7252(struct Voltage* v);
+static double get_avs_voltage_74xx(struct Voltage* v);
+static double get_avs_temperature_7252(struct Temp* t);
+static double get_avs_temperature_74xx(struct Temp* t);
+
+struct platform_info platforms[] = {
+ {
+ .name = "GFHD100",
+ .mmap_base = 0x10400000, // base of many brcm registers
+ .mmap_size = 0x40000,
+ .leds = {
+ .led_red = {
+ .is_present = 1, // GPIO 17
+ .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
+ .offset_data = 0x94c4, // GIO_AON_DATA_LO
+ .mask = 0x00020000, // 1<<17
+ .shift = 17,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 0,
+ .old_val = -1,
+ },
+ .led_blue = {
+ .is_present = 1, // GPIO 12
+ .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
+ .offset_data = 0x94c4, // GIO_AON_DATA_LO
+ .mask = 0x00001000, // 1<<12
+ .shift = 12,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 0,
+ .old_val = -1,
+ },
+ .led_activity = {
+ .is_present = 1, // GPIO 13
+ .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
+ .offset_data = 0x94c4, // GIO_AON_DATA_LO
+ .mask = 0x00002000, // 1<<13
+ .shift = 13,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 0,
+ .old_val = -1,
+ },
+ .led_standby = {
+ .is_present = 1, // GPIO 10
+ .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
+ .offset_data = 0x94c4, // GIO_AON_DATA_LO
+ .mask = 0x00000400, // 1<<10
+ .shift = 10,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 0,
+ .old_val = -1,
+ },
+ },
+ .reset_button = {
+ .is_present = 1, // GPIO 4
+ .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
+ .offset_data = 0x94c4, // GIO_AON_DATA_LO
+ .mask = 0x00000010, // 1<<4
+ .shift = 4,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 1,
+ .old_val = -1,
+ },
+ .fan_tick = {
+ .is_present = 1, // GPIO 98
+ .offset_direction = 0x6768, // GIO_IODIR_EXT_HI
+ .offset_data = 0x6764, // GIO_DATA_EXT_HI
+ .mask = 0x00000100, // 1<<8
+ .shift = 8,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 1,
+ .old_val = -1,
+ },
+ .fan_control = {
+ .is_present = 1, // PWM 1
+ .offset_data = 0x6580, // PWM_CTRL ...
+ .channel = 0,
+ .open_drain = 1,
+ .period = 0x63,
+ },
+ .temp_monitor = {
+ .is_present = 1, // 7425 AVS_RO_REGISTERS_0
+ .offset_data = 0x32b00, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
+ .get_temp = get_avs_temperature_74xx,
+ },
+ .voltage_monitor = {
+ .is_present = 1, // 7425 AVS_RO_REGISTERS_0
+ .offset_data = 0x32b0c, // BCHP_AVS_RO_REGISTERS_0_PVT_1P10V_0_MNTR_STATUS
+ .get_voltage = get_avs_voltage_74xx,
+ },
+ },
+ {
+ .name = "GFMS100",
+ .mmap_base = 0x10400000, // base of many brcm registers
+ .mmap_size = 0x40000,
+ .leds = {
+ .led_red = {
+ .is_present = 1, // GPIO 17
+ .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
+ .offset_data = 0x94c4, // GIO_AON_DATA_LO 0..17
+ .mask = 0x00020000, // 1<<17
+ .shift = 17,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 0,
+ .old_val = -1,
+ },
+ .led_blue = {
+ .is_present = 0,
+ },
+ .led_activity = {
+ .is_present = 1, // GPIO 13
+ .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
+ .offset_data = 0x94c4, // GIO_AON_DATA_LO
+ .mask = 0x00002000, // 1<<13
+ .shift = 13,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 0,
+ .old_val = -1,
+ },
+ .led_standby = {
+ .is_present = 0,
+ },
+ },
+ .reset_button = {
+ .is_present = 1, // GPIO 4
+ .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
+ .offset_data = 0x94c4, // GIO_AON_DATA_LO
+ .mask = 0x00000010, // 1<<4
+ .shift = 4,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 1,
+ .old_val = -1,
+ },
+ .fan_tick = {
+ .is_present = 1, // GPIO 98
+ .offset_direction = 0x6768, // GIO_IODIR_EXT_HI
+ .offset_data = 0x6764, // GIO_DATA_EXT_HI
+ .mask = 0x00000100, // 1<<8
+ .shift = 8,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 1,
+ .old_val = -1,
+ },
+ .fan_control = {
+ .is_present = 1, // PWM 1
+ .offset_data = 0x6580, // PWM_CTRL ...
+ .channel = 0,
+ .open_drain = 1,
+ .period = 0x63,
+ },
+ .temp_monitor = {
+ .is_present = 1, // 7425 AVS_RO_REGISTERS_0
+ .offset_data = 0x32b00, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
+ .get_temp = get_avs_temperature_74xx,
+ },
+ .voltage_monitor = {
+ .is_present = 1, // 7425 AVS_RO_REGISTERS_0
+ .offset_data = 0x32b0c, // BCHP_AVS_RO_REGISTERS_0_PVT_1P10V_0_MNTR_STATUS
+ .get_voltage = get_avs_voltage_74xx,
+ },
+ },
+ {
+ .name = "GFHD200",
+ .init = init_gfhd200,
+ .mmap_base = 0x10400000, // AON_PIN_CTRL ...
+ .mmap_size = 0x30000,
+ .leds = {
+ .led_red = {
+ .is_present = 1, // GPIO 5
+ .pinmux_offset = 0x8500, // PIN_MUX_CTRL_0
+ .pinmux_mask = 0xf0000000,
+ .pinmux_value = 0x10000000, // LED_LD1 (segment 1 on led digit1)
+ .offset_data = 0x9018, // GIO_AON_DATA_LO
+ .mask = 0x00000002, // 1<<1
+ .shift = 1,
+ .off_value =1,
+ .on_value = 0,
+ .old_val = -1,
+ },
+ .led_blue = {
+ .is_present = 0,
+ },
+ .led_activity = {
+ .is_present = 1, // GPIO 4
+ .pinmux_offset = 0x8500, // PIN_MUX_CTRL_0
+ .pinmux_mask = 0x0f000000,
+ .pinmux_value = 0x01000000, // LED_LD0 (segment 0 on led digit1)
+ .offset_data = 0x9018, // GIO_AON_DATA_LO
+ .mask = 0x00000001, // 1<<0
+ .shift = 0,
+ .off_value = 1,
+ .on_value = 0,
+ .old_val = -1,
+ },
+ .led_standby = {
+ .is_present = 0,
+ },
+ },
+ .reset_button = {
+ .is_present = 1, // GPIO 3
+ .offset_direction = 0x9808, // GIO_AON_IODIR_LO
+ .offset_data = 0x9804, // GIO_AON_DATA_LO
+ .mask = 0x00000008, // 1<<3
+ .shift = 3,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 1,
+ .old_val = -1,
+ },
+ .fan_control = {
+ .is_present = 0,
+ },
+ .temp_monitor = {
+ .is_present = 1, // 7429 AVS_RO_REGISTERS_0
+ .offset_data = 0x23300, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
+ .get_temp = get_avs_temperature_74xx,
+ },
+ .voltage_monitor = {
+ .is_present = 1, // 7429 AVS_RO_REGISTERS_0
+ .offset_data = 0x2330c, // BCHP_AVS_RO_REGISTERS_0_PVT_1P10V_0_MNTR_STATUS
+ .get_voltage = get_avs_voltage_74xx,
+ },
+ },
+ {
+ .name = "GFHD254",
+ .init = init_gfhd254,
+ .mmap_base = 0xf0400000, // AON_PIN_CTRL ...
+ .mmap_size = 0xe0000,
+ .leds = {
+ .led_red = {
+ .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,
+ .old_val = -1,
+ },
+ .led_blue = {
+ .is_present = 0,
+ },
+ .led_activity = {
+ .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,
+ .old_val = -1,
+ },
+ .led_standby = {
+ .is_present = 0,
+ },
+ .led_brightness = {
+ .is_present = 1, // GPIO_098
+ .open_drain = 0,
+ .offset_data = 0x9000, // PWM_2
+ .channel = 0,
+ .old_percent = -1,
+ .period = 0x63,
+ },
+ },
+ .reset_button = {
+ .is_present = 1, // GPIO_009
+ .pinmux_offset = 0x4120, // SUN_TOP_CTRL_PIN_MUX_CTRL_8
+ .pinmux_mask = 0xf0000000,
+ .pinmux_value = 0x00000000, // GPIO_009
+ .offset_direction = 0xa608, // GIO_IODIR_LO
+ .offset_data = 0xa604, // GIO_DATA_LO
+ .mask = 0x00000200, // 1<<9
+ .shift = 9,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 1,
+ .old_val = -1,
+ },
+ .fan_tick = {
+ .is_present = 1, // GPIO 78
+ .offset_direction = 0xa648, // GIO_IODIR_EXT_HI
+ .offset_data = 0xa644, // GIO_DATA_EXT_HI
+ .mask = 1<<14,
+ .shift = 14,
+ .off_value = 0,
+ .on_value = 1,
+ .direction_value = 1,
+ .old_val = -1,
+ },
+ .fan_control = {
+ .is_present = 1, // PWM 3
+ .offset_data = 0x9000, // PWM_CTRL ...
+ .channel = 1,
+ .open_drain = 0,
+ .period = 0x91,
+ },
+ .temp_monitor = {
+ .is_present = 1, // 7252 AVS_RO_REGISTERS_0
+ .offset_data = 0xd2200, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
+ .get_temp = get_avs_temperature_7252,
+ },
+ .voltage_monitor = {
+ .is_present = 1, // 7252 AVS_RO_REGISTERS_0
+ .offset_data = 0xd220c, // BCHP_AVS_RO_REGISTERS_0_PVT_1V_0_MNTR_STATUS
+ .get_voltage = get_avs_voltage_7252,
+ },
+ }
+};
+
+
+struct platform_info *get_platform_info(const char *platform_name) {
+ int lim = sizeof(platforms) / sizeof(platforms[0]);
+ for (int i = 0; i < lim; ++i) {
+ struct platform_info *p = &platforms[i];
+ if (0 == strncmp(platform_name, p->name, strlen(p->name))) {
+ return p;
+ }
+ }
+ fprintf(stderr, "No support for platform %s", platform_name);
+ return NULL;
+}
+
+/* set LED/Keypad timings to control LED brightness */
+static void init_gfhd200(UNUSED struct platform_info* p) {
+ volatile uint32_t* reg;
+
+ reg = mmap_addr + 0x9034; // LDK_CONTROL
+ *reg = 0x01; // reset
+ *reg = 0x18; // ver=1 inv_led=1
+
+ reg = mmap_addr + 0x9008; // LDK_PRESCHI, LO (clock divisor)
+ reg[0] = 0x00;
+ reg[1] = 0x10; // tick = clock / 0x0010, not sure what clock is
+
+ reg = mmap_addr + 0x9010; // LDK_DUTYOFF, ON
+ reg[0] = 0x40;
+ reg[1] = 0xc0; // 0x40 off ticks then 0xc0 on ticks == 75% brightness
+}
+
+/* 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.
+
+ // 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
+ // the duty cycle.
+ reg = mmap_addr + 0x9010; // PWM3_CWORD_MSB
+ reg[0] = 0x20;
+ reg[1] = 0x0; // PWM3_CWORD_LSB
+
+ // 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;
+
+ // Default the LED brightness to 50.
+ set_pwm(&p->leds.led_brightness, 50);
+}
+
+static double get_avs_voltage_7252(struct Voltage* v) {
+ volatile uint32_t* reg;
+ uint32_t value, valid, raw_data;
+
+ reg = mmap_addr + v->offset_data;
+ value = *reg;
+ valid = (value & 0x00000400) >> 10;
+ raw_data = value & 0x000003ff;
+ if (!valid) return -1.0;
+ return ((880.0/1024.0)/(0.7)*raw_data) / 1000;
+}
+
+static double get_avs_voltage_74xx(struct Voltage* v) {
+ volatile uint32_t* reg;
+ uint32_t value, valid, raw_data;
+
+ reg = mmap_addr + v->offset_data;
+ value = *reg;
+ // see 7425-PR500-RDS.pdf
+ valid = (value & 0x00000400) >> 10;
+ raw_data = value & 0x000003ff;
+ if (!valid) return -1.0;
+ return ((990 * raw_data * 8) / (7*1024)) / 1000.0;
+}
+
+static double get_avs_temperature_74xx(struct Temp* t) {
+ volatile uint32_t* reg;
+ uint32_t value, valid, raw_data;
+
+ reg = mmap_addr + t->offset_data;
+ value = *reg;
+ // see 7425-PR500-RDS.pdf
+ valid = (value & 0x00000400) >> 10;
+ raw_data = value & 0x000003ff;
+ if (!valid) return -1.0;
+ return (418000 - (556 * raw_data)) / 1000.0;
+}
+
+static double get_avs_temperature_7252(struct Temp* t) {
+ volatile uint32_t* reg;
+ uint32_t value, valid, raw_data;
+
+ reg = mmap_addr + t->offset_data;
+ value = *reg;
+ valid = (value & 0x00000400) >> 10;
+ raw_data = value & 0x000003ff;
+ if (!valid) return -1.0;
+ return 410.04 - (0.48705 * raw_data);
+}
+
+/*
+ platform_init has three steps:
+ 1) Generic platform init. This sets up the mmap.
+ 2) Platform-specific init. Calls the gf* function
+ corresponding to the passed in platform.
+ 3) GPIO init. Sets up the gpios properly.
+*/
+int platform_init(struct platform_info* p) {
+ platform_cleanup();
+
+ mmap_fd = open(DEVMEM, O_RDWR);
+ if (mmap_fd < 0) {
+ perror(DEVMEM);
+ return -1;
+ }
+ mmap_size = p->mmap_size;
+ mmap_addr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, mmap_fd, p->mmap_base);
+ if (mmap_addr == MAP_FAILED) {
+ perror("mmap");
+ platform_cleanup();
+ return -1;
+ }
+
+ if (p->init) {
+ (*p->init)(p);
+ }
+
+ set_pinmux(&p->leds.led_red);
+ set_pinmux(&p->leds.led_blue);
+ set_pinmux(&p->leds.led_activity);
+ set_pinmux(&p->leds.led_standby);
+
+ set_direction(&p->leds.led_red);
+ set_direction(&p->leds.led_blue);
+ set_direction(&p->leds.led_activity);
+ set_direction(&p->leds.led_standby);
+ set_direction(&p->reset_button);
+ set_direction(&p->fan_tick);
+
+ return 0;
+}
+
+void platform_cleanup() {
+ if (mmap_addr != MAP_FAILED) {
+ if (munmap((void*) mmap_addr, mmap_size) < 0) {
+ perror("munmap");
+ }
+ mmap_addr = MAP_FAILED;
+ mmap_size = 0;
+ }
+ if (mmap_fd >= 0) {
+ close(mmap_fd);
+ mmap_fd = -1;
+ }
+
+}
+
+void set_gpio(struct Gpio *g, int level) {
+ volatile uint32_t* reg;
+ uint32_t value;
+
+ if (g->old_val == level) {
+ // If this is the same value as last time, don't do anything, for two
+ // reasons:
+ // 1) If you set the gpio too often, it seems to stay low (the led
+ // stays off).
+ // 2) If some process other than us is twiddling a led, this way we
+ // won't interfere with it.
+ return;
+ }
+ g->old_val = level;
+
+ reg = mmap_addr + g->offset_data;
+ value = *reg;
+ value &= ~g->mask;
+ value |= (level ? g->on_value : g->off_value) << g->shift;
+ *reg = value;
+}
+
+int get_gpio(struct Gpio *g) {
+ volatile uint32_t* reg;
+ uint32_t value;
+
+ reg = mmap_addr + g->offset_data;
+ value = (*reg & g->mask) >> g->shift;
+ return (value == g->on_value);
+}
+
+/*
+ Set the PWM duty duty cycle. Percent bounded [0, 100].
+
+ The output period of the constant-freq PWM is calculated
+ by (period_programmed + 1) / Fv, where Fv is the output
+ of the variable-frequency PWM (in mhz).
+
+ Fv is calculated by the following formula:
+
+ Fv = (cword) * 2^-16 * 27MHz
+
+ cword is the programmed frequency control word.
+
+ The fan on lockdown must stay at a constant 23KHz
+*/
+void set_pwm(struct PwmControl *f, int percent) {
+ volatile uint32_t* reg;
+ uint32_t mask0, val0, mask1, val1, on;
+
+ if (percent < 0) percent = 0;
+ if (percent > 100) percent = 100;
+ if (f->old_percent == percent) return;
+ f->old_percent = percent;
+
+ reg = mmap_addr + f->offset_data;
+ if (f->channel == 0) {
+ mask0 = 0xf0; // preserve other channel
+ val0 = 0x01; // open-drain|start
+ if (f->open_drain)
+ val0 |= 0x08;
+ mask1 = 0x10; // preserve
+ val1 = 0x01; // constant-freq
+ on = 6;
+ } else {
+ mask0 = 0x0f; // see above
+ val0 = 0x10;
+ if (f->open_drain)
+ val0 |= 0x80;
+ mask1 = 0x01;
+ val1 = 0x10;
+ on = 8;
+ }
+ reg[0] = (reg[0] & mask0) | val0;
+ reg[1] = (reg[1] & mask1) | val1;
+ reg[on] = (f->period * percent)/100;
+ reg[on+1] = f->period;
+}
+
+void set_direction(struct Gpio *g) {
+ volatile uint32_t* reg;
+ uint32_t value;
+
+ if (!g->is_present || g->offset_direction == 0)
+ return;
+
+ reg = mmap_addr + g->offset_direction;
+ value = *reg;
+ value &= ~g->mask;
+ value |= g->direction_value << g->shift;
+ *reg = value;
+}
+
+void set_pinmux(struct Gpio *g) {
+ volatile uint32_t* reg;
+ uint32_t value;
+
+ if (!g->is_present || g->pinmux_offset == 0)
+ return;
+
+ reg = mmap_addr + g->pinmux_offset;
+ value = *reg;
+ value &= ~g->pinmux_mask;
+ value |= g->pinmux_value;
+ *reg = value;
+}
+
+#endif // BROADCOM
diff --git a/gpio-mailbox/brcm-nexus.c b/gpio-mailbox/brcm-nexus.c
new file mode 100644
index 0000000..f559c1b
--- /dev/null
+++ b/gpio-mailbox/brcm-nexus.c
@@ -0,0 +1,329 @@
+#ifdef ANDROID /* Should work fine on non Android platforms, but is only necessary on Android */
+#ifdef BROADCOM
+
+/* Broadcom platform implementation using the NEXUS API.
+ Only the GFHD254 is supported via this API as of now. */
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "brcm-platform.h"
+
+#include "nexus_avs.h"
+#include "nexus_pwm.h"
+#include "nexus_gpio.h"
+#include "nxclient.h"
+
+#define UNUSED __attribute__((unused))
+#define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0]))
+
+static void init_gfhd254(struct platform_info* p);
+static double get_avs_voltage_7252(struct Voltage* v);
+static double get_avs_temperature_7252(struct Temp* t);
+
+/* This is an array. Of structs! It contains structs of
+ the type platform_info. The platform_info struct provides
+ much useful information for use in all sorts of fun
+ operations. Structs are considered "aggregate types",
+ which you can of course read all about by pointing your
+ browser at Google and searching for your favorite version
+ of our beloved C standard, such as ISO/IEC 9899:TC3! */
+struct platform_info platforms[] = {
+ {
+ .name = "GFHD254",
+ .init = init_gfhd254,
+ .leds = {
+ .led_red = {
+ .is_present = 1, // AON_GPIO_05
+ .type = AON,
+ .pin = 5,
+ .old_val = -1,
+ },
+ .led_blue = {
+ .is_present = 0,
+ },
+ .led_activity = {
+ .is_present = 1, // AON_GPIO_04
+ .pin = 4,
+ .type = AON,
+ .old_val = -1,
+ },
+ .led_standby = {
+ .is_present = 0,
+ },
+ .led_brightness = {
+ .is_present = 1, // GPIO_098
+ .open_drain = 0,
+ .pwm_index = 2,
+ .old_percent = -1,
+ },
+ },
+ .reset_button = {
+ .is_present = 1, // GPIO_009
+ .pin = 9,
+ .type = STANDARD,
+ .old_val = -1,
+ },
+ .fan_tick = {
+ .is_present = 1, // GPIO 78
+ .pin = 78,
+ .type = STANDARD,
+ .old_val = -1,
+ },
+ .fan_control = {
+ .is_present = 1, // GPIO_098
+ .open_drain = 0,
+ .pwm_index = 3,
+ .old_percent = -1,
+ },
+ .temp_monitor = {
+ .is_present = 1, // 7252 AVS_RO_REGISTERS_0
+ .get_temp = get_avs_temperature_7252,
+ },
+ .voltage_monitor = {
+ .is_present = 1,
+ .get_voltage = get_avs_voltage_7252,
+ },
+ }
+};
+
+struct platform_info *get_platform_info(const char *platform_name) {
+ for (unsigned int i = 0; i < ARRAYSIZE(platforms); ++i) {
+ struct platform_info *p = &platforms[i];
+ if (0 == strncmp(platform_name, p->name, strlen(p->name))) {
+ return p;
+ }
+ }
+ fprintf(stderr, "No support for platform %s", platform_name);
+ return NULL;
+}
+
+static NEXUS_GpioType get_nexus_type(enum GpioType type) {
+ switch (type) {
+ case STANDARD:
+ return NEXUS_GpioType_eStandard;
+ case AON:
+ return NEXUS_GpioType_eAonStandard;
+ }
+
+ /* If we added a GpioType and are using it and never updated this, we
+ are going to have a bad time somewhere. Return an invalid val and let
+ nxclient handle it, while logging the problem. */
+ fprintf(stderr, "No matching NEXUS type for GPIO: %d\n", type);
+ return -1;
+}
+
+// Don't need to set direction for GFHD254
+static void initialize_gpio(struct Gpio* gpio) {
+ if (!gpio || !gpio->is_present)
+ return;
+
+ /* TODO(doughorn): Cannot set pinmux from NEXUS? Can retrieve
+ pinmux information but can't set it because it is 'dangerous'.
+ So we can use the raw Read/WriteRegister functions. These, however,
+ are also dangerous and their "indiscriminate use will result in
+ system failure." Seems to work properly without us setting... */
+ NEXUS_GpioSettings gpioSettings;
+ NEXUS_GpioHandle handle;
+ NEXUS_GpioType type = get_nexus_type(gpio->type);
+
+ NEXUS_Gpio_GetDefaultSettings(type, &gpioSettings);
+ gpioSettings.mode = NEXUS_GpioMode_eOutputPushPull;
+ gpioSettings.interruptMode = NEXUS_GpioInterrupt_eDisabled;
+
+ handle = NEXUS_Gpio_Open(type, gpio->pin, &gpioSettings);
+ if (!handle) {
+ fprintf(stderr, "Failed opening GPIO pin %d. gpio-mailbox cannot continue.\n", gpio->pin);
+ platform_cleanup();
+ exit(EXIT_FAILURE);
+ }
+
+ NEXUS_Gpio_Close(handle);
+}
+
+/* TODO(doughorn): We can probably avoid calling GetAvsStatus twice for
+ the voltage and temp individually, but the poll rate is low enough
+ that it most likely doesn't matter...*/
+static double get_avs_voltage_7252(UNUSED struct Voltage* v) {
+ NEXUS_AvsStatus status;
+ if (NEXUS_GetAvsStatus(&status)) {
+ fprintf(stderr, "Could not get AVS status. Aborting...\n");
+ platform_cleanup();
+ exit(EXIT_FAILURE);
+ }
+
+ /* NEXUS_AvsStatus.voltage measured in millivolts */
+ return (double)status.voltage / 1000;
+}
+
+static double get_avs_temperature_7252(UNUSED struct Temp* t) {
+ NEXUS_AvsStatus status;
+
+ if (NEXUS_GetAvsStatus(&status)) {
+ fprintf(stderr, "Could not get AVS status. Aborting...\n");
+ platform_cleanup();
+ exit(EXIT_FAILURE);
+ }
+
+ /* Temp is in thousands of a degree. */
+ return (double)status.temperature / 1000;
+}
+
+static void init_gfhd254(struct platform_info* p) {
+ NEXUS_PwmChannelSettings pwmSettings;
+ NEXUS_PwmChannelHandle pwm;
+ NEXUS_PwmFreqModeType frequencyMode;
+
+ /* Set the control word for the led brightness PWM to 0xf.
+ This is used to control the output frequency from the
+ variable rate clock. */
+ NEXUS_Pwm_GetDefaultChannelSettings(&pwmSettings);
+ pwm = NEXUS_Pwm_OpenChannel(2, &pwmSettings);
+ if (NEXUS_Pwm_SetControlWord(pwm, 0xf)) {
+ fprintf(stderr, "Failed setting control word for PWM.\n");
+ platform_cleanup();
+ exit(EXIT_FAILURE);
+ }
+
+ NEXUS_Pwm_CloseChannel(pwm);
+
+ /* Set the control word for the fan to 0x2000. */
+ NEXUS_Pwm_GetDefaultChannelSettings(&pwmSettings);
+ pwm = NEXUS_Pwm_OpenChannel(3, &pwmSettings);
+ if (NEXUS_Pwm_SetControlWord(pwm, 0x2000)) {
+ fprintf(stderr, "Failed setting control word for PWM.\n");
+ platform_cleanup();
+ exit(EXIT_FAILURE);
+ }
+
+ NEXUS_Pwm_CloseChannel(pwm);
+
+ // Default the LED brightness to 50.
+ set_pwm(&p->leds.led_brightness, 50);
+}
+
+void platform_cleanup() {
+ NxClient_Uninit();
+}
+
+int platform_init(struct platform_info* p) {
+ if (NxClient_Join(NULL)) {
+ fprintf(stderr, "gpio-mailbox failed to connect to nxserver. Aborting...\n");
+ return -1;
+ }
+
+ if (p->init) {
+ (*p->init)(p);
+ }
+
+ initialize_gpio(&p->leds.led_red);
+ initialize_gpio(&p->leds.led_blue);
+ initialize_gpio(&p->leds.led_activity);
+ initialize_gpio(&p->leds.led_standby);
+ return 0;
+}
+
+void set_gpio(struct Gpio *gpio, int level) {
+ if (!gpio || !gpio->is_present || gpio->old_val == level) {
+ // If this is the same value as last time, don't do anything, for two
+ // reasons:
+ // 1) If you set the gpio too often, it seems to stay low (the led
+ // stays off).
+ // 2) If some process other than us is twiddling a led, this way we
+ // won't interfere with it.
+ return;
+ }
+
+ gpio->old_val = level;
+
+ NEXUS_GpioSettings gpioSettings;
+ NEXUS_GpioHandle handle;
+ uint32_t error;
+ NEXUS_GpioType type = get_nexus_type(gpio->type);
+
+ handle = NEXUS_Gpio_Open(type, gpio->pin, NULL);
+ if (!handle) {
+ fprintf(stderr, "Failed opening GPIO pin %d. Cannot continue.\n", gpio->pin);
+ platform_cleanup();
+ exit(EXIT_FAILURE);
+ }
+
+ NEXUS_Gpio_GetSettings(handle, &gpioSettings);
+ gpioSettings.value = level ? NEXUS_GpioValue_eHigh : NEXUS_GpioValue_eLow;
+ if ((error = NEXUS_Gpio_SetSettings(handle, &gpioSettings))) {
+ fprintf(stderr, "Failed setting GPIO pin %d. Cannot continue.\n", gpio->pin);
+ platform_cleanup();
+ exit(EXIT_FAILURE);
+ }
+
+ NEXUS_Gpio_Close(handle);
+}
+
+int get_gpio(struct Gpio *gpio) {
+ if (!gpio || !gpio->is_present)
+ return 0;
+
+ NEXUS_GpioStatus status;
+ NEXUS_GpioHandle handle;
+ uint32_t error;
+ NEXUS_GpioType type = get_nexus_type(gpio->type);
+
+ handle = NEXUS_Gpio_Open(type, gpio->pin, NULL);
+
+ if (!handle) {
+ fprintf(stderr, "Failed opening GPIO pin %d. Cannot continue.\n", gpio->pin);
+ platform_cleanup();
+ exit(EXIT_FAILURE);
+ }
+
+ if ((error = NEXUS_Gpio_GetStatus(handle, &status))) {
+ fprintf(stderr, "Failed getting status of GPIO pin %d. Cannot continue.\n", gpio->pin);
+ platform_cleanup();
+ exit(EXIT_FAILURE);
+ }
+
+ NEXUS_Gpio_Close(handle);
+
+ return status.value != NEXUS_GpioValue_eLow;
+}
+
+/*
+ Set the pwm. See set_pwm in brcm-direct.c
+ for details.
+*/
+void set_pwm(struct PwmControl *f, int percent) {
+ if (percent < 0) percent = 0;
+ if (percent > 100) percent = 100;
+ if (percent == f->old_percent) return;
+ f->old_percent = percent;
+ uint32_t period = f->pwm_index % 2 ? 0x91 : 0x63;
+
+ NEXUS_PwmChannelSettings pwmSettings;
+ NEXUS_PwmChannelHandle pwm;
+ uint16_t onInterval = (period * percent)/100;
+
+ NEXUS_Pwm_GetDefaultChannelSettings(&pwmSettings);
+ pwmSettings.openDrain = f->open_drain;
+ pwmSettings.eFreqMode = NEXUS_PwmFreqModeType_eConstant;
+ pwm = NEXUS_Pwm_OpenChannel(f->pwm_index, &pwmSettings);
+
+ if (NEXUS_Pwm_SetOnAndPeriodInterval(pwm, onInterval, period)) {
+ fprintf(stderr, "Could not set ON and PERIOD for PWM. Aborting...\n");
+ platform_cleanup();
+ exit(EXIT_FAILURE);
+ }
+
+ if (NEXUS_Pwm_Start(pwm)) {
+ fprintf(stderr, "Could not start PWM %d!\n", f->pwm_index);
+ platform_cleanup();
+ exit(EXIT_FAILURE);
+ }
+
+ NEXUS_Pwm_CloseChannel(pwm);
+}
+
+#endif // BROADCOM
+#endif // ANDROID
diff --git a/gpio-mailbox/brcm-platform.h b/gpio-mailbox/brcm-platform.h
new file mode 100644
index 0000000..4b53be9
--- /dev/null
+++ b/gpio-mailbox/brcm-platform.h
@@ -0,0 +1,111 @@
+/*
+ * Hardware abstraction layer for GPIO's and PWMs.
+ */
+
+#ifndef BRCM_PLATFORM_
+#define BRCM_PLATFORM_
+
+/*
+ * Defines the category that a given GPIO falls under.
+ */
+enum GpioType {
+ STANDARD,
+ AON,
+};
+
+struct Gpio {
+ int is_present;
+
+ unsigned int pinmux_offset;
+ unsigned int pinmux_mask;
+ unsigned int pinmux_value;
+
+ unsigned int offset_direction;
+ unsigned int offset_data;
+
+ /* for offset_direction and offset_data */
+ unsigned int mask; // eg, (*reg & mask) >> shift == on_value
+ unsigned int shift;
+ unsigned int off_value;
+ unsigned int on_value;
+ unsigned int direction_value; // 0 is output
+ unsigned int pin; // gpio #
+ enum GpioType type; // 'type' of gpio (aon/standard)
+ int old_val;
+};
+
+struct PwmControl {
+ int is_present;
+ int open_drain;
+ unsigned int offset_data;
+ unsigned int pwm_index; // index of this pwm.
+ unsigned int channel;
+ int old_percent;
+ int period;
+};
+
+struct Temp {
+ int is_present;
+ unsigned int offset_data;
+ double (*get_temp)(struct Temp* t);
+};
+
+struct Voltage {
+ int is_present;
+ unsigned int offset_data;
+ double (*get_voltage)(struct Voltage* v);
+};
+
+struct Leds {
+ struct Gpio led_red;
+ struct Gpio led_blue;
+ struct Gpio led_activity;
+ struct Gpio led_standby;
+ struct PwmControl led_brightness;
+};
+
+struct platform_info {
+ const char *name;
+ off_t mmap_base;
+ size_t mmap_size;
+ void (*init)(struct platform_info* p);
+ struct Leds leds;
+ struct Gpio reset_button;
+ struct Gpio fan_tick;
+ struct PwmControl fan_control;
+ struct Temp temp_monitor;
+ struct Voltage voltage_monitor;
+};
+
+/* This value, from old code, controls the pwm period. The duty cycle
+ is defined as on/(period + 1) and on is defined as (on/Fv). Fv is
+ the frequency of the variable rate PWM.*/
+extern const int PWM_CYCLE_PERIOD;
+
+/* Return the master platform_info struct for the provided platforn_name.
+ If no platform matches, returns NULL */
+extern struct platform_info *get_platform_info(const char *);
+
+/* Initialize the platform! */
+extern int platform_init(struct platform_info *);
+
+/* Cleanup the platform! */
+extern void platform_cleanup();
+
+/* Set the gpio represented by to the provided level.
+ Level is restricted to [0, 1] */
+extern void set_gpio(struct Gpio *, int);
+
+/* Get the value of the gpio provided. */
+extern int get_gpio(struct Gpio *);
+
+/* Set the provided PWM to the given duty cycle percent */
+extern void set_pwm(struct PwmControl *, int);
+
+/* Init GPIO to input or output. */
+extern void set_direction(struct Gpio *);
+
+/* Set the pinmux (init pin to LED, GPIO, etc) */
+extern void set_pinmux(struct Gpio *);
+
+#endif
diff --git a/gpio-mailbox/broadcom.c b/gpio-mailbox/broadcom.c
index a53e51e..7540571 100644
--- a/gpio-mailbox/broadcom.c
+++ b/gpio-mailbox/broadcom.c
@@ -2,35 +2,19 @@
#define _POSIX_C_SOURCE 199309L /* for clock_gettime */
#define _BSD_SOURCE /* for usleep */
-#include <features.h>
-#include <assert.h>
#include <fcntl.h>
-#include <signal.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/select.h>
-#include <sys/time.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-#include <sys/mman.h>
+#include <stacktrace.h>
+#include <sys/types.h>
#include <time.h>
#include <unistd.h>
-#include <stacktrace.h>
+#include "brcm-platform.h"
#include "pin.h"
-#define UNUSED __attribute__((unused))
-
-#define DEVMEM "/dev/mem"
-
-/* This value, from old code, controls the pwm period. The duty cycle
- is defined as on/(period + 1) and on is defined as (on/Fv). Fv is
- the frequency of the variable rate PWM.*/
-static const int PWM_CYCLE_PERIOD = 0x63;
-
struct PinHandle_s {
int unused;
};
@@ -43,556 +27,8 @@
} \
} while (0)
-static volatile void* mmap_addr = MAP_FAILED;
-static size_t mmap_size = 0;
-static int mmap_fd = -1;
-
-struct Gpio {
- int is_present;
-
- unsigned int pinmux_offset;
- unsigned int pinmux_mask;
- unsigned int pinmux_value;
-
- unsigned int offset_direction;
- unsigned int offset_data;
-
- /* for offset_direction and offset_data */
- unsigned int mask; // eg, (*reg & mask) >> shift == on_value
- unsigned int shift;
- unsigned int off_value;
- unsigned int on_value;
- unsigned int direction_value; // 0 is output
- int old_val;
-};
-
-struct PwmControl {
- int is_present;
- int open_drain;
- unsigned int offset_data;
- unsigned int channel;
- int old_percent;
-};
-
-struct Temp {
- int is_present;
- unsigned int offset_data;
- double (*get_temp)(struct Temp* t);
-};
-
-struct Voltage {
- int is_present;
- unsigned int offset_data;
- double (*get_voltage)(struct Voltage* v);
-};
-
-struct Leds {
- struct Gpio led_red;
- struct Gpio led_blue;
- struct Gpio led_activity;
- struct Gpio led_standby;
- struct PwmControl led_brightness;
-};
-
-struct platform_info {
- const char *name;
- off_t mmap_base;
- size_t mmap_size;
- void (*init)(struct platform_info* p);
- struct Leds leds;
- struct Gpio reset_button;
- struct Gpio fan_tick;
- struct PwmControl fan_control;
- struct Temp temp_monitor;
- struct Voltage voltage_monitor;
-};
-
-static void init_gfhd200(struct platform_info* p);
-static void init_gfhd254(struct platform_info* p);
-static double get_avs_temperature_74xx(struct Temp* t);
-static double get_avs_temperature_7252(struct Temp* t);
-static double get_avs_voltage_74xx(struct Voltage* v);
-static double get_avs_voltage_7252(struct Voltage* v);
-
-struct platform_info platforms[] = {
- {
- .name = "GFHD100",
- .mmap_base = 0x10400000, // base of many brcm registers
- .mmap_size = 0x40000,
- .leds = {
- .led_red = {
- .is_present = 1, // GPIO 17
- .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
- .offset_data = 0x94c4, // GIO_AON_DATA_LO
- .mask = 0x00020000, // 1<<17
- .shift = 17,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 0,
- .old_val = -1,
- },
- .led_blue = {
- .is_present = 1, // GPIO 12
- .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
- .offset_data = 0x94c4, // GIO_AON_DATA_LO
- .mask = 0x00001000, // 1<<12
- .shift = 12,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 0,
- .old_val = -1,
- },
- .led_activity = {
- .is_present = 1, // GPIO 13
- .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
- .offset_data = 0x94c4, // GIO_AON_DATA_LO
- .mask = 0x00002000, // 1<<13
- .shift = 13,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 0,
- .old_val = -1,
- },
- .led_standby = {
- .is_present = 1, // GPIO 10
- .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
- .offset_data = 0x94c4, // GIO_AON_DATA_LO
- .mask = 0x00000400, // 1<<10
- .shift = 10,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 0,
- .old_val = -1,
- },
- },
- .reset_button = {
- .is_present = 1, // GPIO 4
- .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
- .offset_data = 0x94c4, // GIO_AON_DATA_LO
- .mask = 0x00000010, // 1<<4
- .shift = 4,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 1,
- .old_val = -1,
- },
- .fan_tick = {
- .is_present = 1, // GPIO 98
- .offset_direction = 0x6768, // GIO_IODIR_EXT_HI
- .offset_data = 0x6764, // GIO_DATA_EXT_HI
- .mask = 0x00000100, // 1<<8
- .shift = 8,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 1,
- .old_val = -1,
- },
- .fan_control = {
- .is_present = 1, // PWM 1
- .offset_data = 0x6580, // PWM_CTRL ...
- .channel = 0,
- .open_drain = 1,
- },
- .temp_monitor = {
- .is_present = 1, // 7425 AVS_RO_REGISTERS_0
- .offset_data = 0x32b00, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
- .get_temp = get_avs_temperature_74xx,
- },
- .voltage_monitor = {
- .is_present = 1, // 7425 AVS_RO_REGISTERS_0
- .offset_data = 0x32b0c, // BCHP_AVS_RO_REGISTERS_0_PVT_1P10V_0_MNTR_STATUS
- .get_voltage = get_avs_voltage_74xx,
- },
- },
- {
- .name = "GFMS100",
- .mmap_base = 0x10400000, // base of many brcm registers
- .mmap_size = 0x40000,
- .leds = {
- .led_red = {
- .is_present = 1, // GPIO 17
- .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
- .offset_data = 0x94c4, // GIO_AON_DATA_LO 0..17
- .mask = 0x00020000, // 1<<17
- .shift = 17,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 0,
- .old_val = -1,
- },
- .led_blue = {
- .is_present = 0,
- },
- .led_activity = {
- .is_present = 1, // GPIO 13
- .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
- .offset_data = 0x94c4, // GIO_AON_DATA_LO
- .mask = 0x00002000, // 1<<13
- .shift = 13,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 0,
- .old_val = -1,
- },
- .led_standby = {
- .is_present = 0,
- },
- },
- .reset_button = {
- .is_present = 1, // GPIO 4
- .offset_direction = 0x94c8, // GIO_AON_IODIR_LO
- .offset_data = 0x94c4, // GIO_AON_DATA_LO
- .mask = 0x00000010, // 1<<4
- .shift = 4,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 1,
- .old_val = -1,
- },
- .fan_tick = {
- .is_present = 1, // GPIO 98
- .offset_direction = 0x6768, // GIO_IODIR_EXT_HI
- .offset_data = 0x6764, // GIO_DATA_EXT_HI
- .mask = 0x00000100, // 1<<8
- .shift = 8,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 1,
- .old_val = -1,
- },
- .fan_control = {
- .is_present = 1, // PWM 1
- .offset_data = 0x6580, // PWM_CTRL ...
- .channel = 0,
- .open_drain = 1,
- },
- .temp_monitor = {
- .is_present = 1, // 7425 AVS_RO_REGISTERS_0
- .offset_data = 0x32b00, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
- .get_temp = get_avs_temperature_74xx,
- },
- .voltage_monitor = {
- .is_present = 1, // 7425 AVS_RO_REGISTERS_0
- .offset_data = 0x32b0c, // BCHP_AVS_RO_REGISTERS_0_PVT_1P10V_0_MNTR_STATUS
- .get_voltage = get_avs_voltage_74xx,
- },
- },
- {
- .name = "GFHD200",
- .init = init_gfhd200,
- .mmap_base = 0x10400000, // AON_PIN_CTRL ...
- .mmap_size = 0x30000,
- .leds = {
- .led_red = {
- .is_present = 1, // GPIO 5
- .pinmux_offset = 0x8500, // PIN_MUX_CTRL_0
- .pinmux_mask = 0xf0000000,
- .pinmux_value = 0x10000000, // LED_LD1 (segment 1 on led digit1)
- .offset_data = 0x9018, // GIO_AON_DATA_LO
- .mask = 0x00000002, // 1<<1
- .shift = 1,
- .off_value =1,
- .on_value = 0,
- .old_val = -1,
- },
- .led_blue = {
- .is_present = 0,
- },
- .led_activity = {
- .is_present = 1, // GPIO 4
- .pinmux_offset = 0x8500, // PIN_MUX_CTRL_0
- .pinmux_mask = 0x0f000000,
- .pinmux_value = 0x01000000, // LED_LD0 (segment 0 on led digit1)
- .offset_data = 0x9018, // GIO_AON_DATA_LO
- .mask = 0x00000001, // 1<<0
- .shift = 0,
- .off_value = 1,
- .on_value = 0,
- .old_val = -1,
- },
- .led_standby = {
- .is_present = 0,
- },
- },
- .reset_button = {
- .is_present = 1, // GPIO 3
- .offset_direction = 0x9808, // GIO_AON_IODIR_LO
- .offset_data = 0x9804, // GIO_AON_DATA_LO
- .mask = 0x00000008, // 1<<3
- .shift = 3,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 1,
- .old_val = -1,
- },
- .fan_control = {
- .is_present = 0,
- },
- .temp_monitor = {
- .is_present = 1, // 7429 AVS_RO_REGISTERS_0
- .offset_data = 0x23300, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
- .get_temp = get_avs_temperature_74xx,
- },
- .voltage_monitor = {
- .is_present = 1, // 7429 AVS_RO_REGISTERS_0
- .offset_data = 0x2330c, // BCHP_AVS_RO_REGISTERS_0_PVT_1P10V_0_MNTR_STATUS
- .get_voltage = get_avs_voltage_74xx,
- },
- },
- {
- .name = "GFHD254",
- .init = init_gfhd254,
- .mmap_base = 0xf0400000, // AON_PIN_CTRL ...
- .mmap_size = 0xe0000,
- .leds = {
- .led_red = {
- .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,
- .old_val = -1,
- },
- .led_blue = {
- .is_present = 0,
- },
- .led_activity = {
- .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,
- .old_val = -1,
- },
- .led_standby = {
- .is_present = 0,
- },
- .led_brightness = {
- .is_present = 1, // GPIO_098
- .open_drain = 0,
- .offset_data = 0x9000, // PWM_2
- .channel = 0,
- .old_percent = -1,
- },
- },
- .reset_button = {
- .is_present = 1, // GPIO_009
- .pinmux_offset = 0x4120, // SUN_TOP_CTRL_PIN_MUX_CTRL_8
- .pinmux_mask = 0xf0000000,
- .pinmux_value = 0x00000000, // GPIO_009
- .offset_direction = 0xa608, // GIO_IODIR_LO
- .offset_data = 0xa604, // GIO_DATA_LO
- .mask = 0x00000200, // 1<<9
- .shift = 9,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 1,
- .old_val = -1,
- },
- .fan_tick = {
- .is_present = 1, // GPIO 78
- .offset_direction = 0xa648, // GIO_IODIR_EXT_HI
- .offset_data = 0xa644, // GIO_DATA_EXT_HI
- .mask = 1<<14,
- .shift = 14,
- .off_value = 0,
- .on_value = 1,
- .direction_value = 1,
- .old_val = -1,
- },
- .fan_control = {
- .is_present = 1, // PWM 3
- .offset_data = 0x9000, // PWM_CTRL ...
- .channel = 1,
- .open_drain = 0,
- },
- .temp_monitor = {
- .is_present = 1, // 7252 AVS_RO_REGISTERS_0
- .offset_data = 0xd2200, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
- .get_temp = get_avs_temperature_7252,
- },
- .voltage_monitor = {
- .is_present = 1, // 7252 AVS_RO_REGISTERS_0
- .offset_data = 0xd220c, // BCHP_AVS_RO_REGISTERS_0_PVT_1V_0_MNTR_STATUS
- .get_voltage = get_avs_voltage_7252,
- },
- }
-};
-
struct platform_info *platform = NULL;
-/* PWM operates on either channel 0 or 1. We want to get the duty cycle value
- by calculating it from the "ON" register, located offset 6 for channel 0
- and 8 for channel 1.
-
- Duty cycle is calculated by ON / Period.
-*/
-static UNUSED int get_pwm(struct PwmControl *f) {
- volatile uint32_t* reg = mmap_addr + f->offset_data;
- uint8_t offset = f->channel ? 8 : 6;
- uint32_t val = reg[offset];
- return ((uint64_t)val * 100) / PWM_CYCLE_PERIOD;
-}
-
-// Set the given PWM (pulse width modulator) to the given percent duty cycle.
-static void set_pwm(struct PwmControl *f, int percent) {
- volatile uint32_t* reg;
- uint32_t mask0, val0, mask1, val1, on;
-
- if (percent < 0) percent = 0;
- if (percent > 100) percent = 100;
- if (f->old_percent == percent) return;
- f->old_percent = percent;
-
- reg = mmap_addr + f->offset_data;
- if (f->channel == 0) {
- mask0 = 0xf0; // preserve other channel
- val0 = 0x01; // open-drain|start
- if (f->open_drain)
- val0 |= 0x08;
- mask1 = 0x10; // preserve
- val1 = 0x01; // constant-freq
- on = 6;
- } else {
- mask0 = 0x0f; // see above
- val0 = 0x10;
- if (f->open_drain)
- val0 |= 0x80;
- mask1 = 0x01;
- val1 = 0x10;
- on = 8;
- }
- reg[0] = (reg[0] & mask0) | val0;
- reg[1] = (reg[1] & mask1) | val1;
- reg[on] = (PWM_CYCLE_PERIOD * percent)/100; // 0x63 is what old code used
- reg[on+1] = PWM_CYCLE_PERIOD;
-}
-
-// Get the CPU temperature. I think it's in Celsius.
-static double get_avs_temperature_74xx(struct Temp* t) {
- volatile uint32_t* reg;
- uint32_t value, valid, raw_data;
-
- reg = mmap_addr + t->offset_data;
- value = *reg;
- // see 7425-PR500-RDS.pdf
- valid = (value & 0x00000400) >> 10;
- raw_data = value & 0x000003ff;
- if (!valid) return -1.0;
- return (418000 - (556 * raw_data)) / 1000.0;
-}
-
-static double get_avs_temperature_7252(struct Temp* t) {
- volatile uint32_t* reg;
- uint32_t value, valid, raw_data;
-
- reg = mmap_addr + t->offset_data;
- value = *reg;
- valid = (value & 0x00000400) >> 10;
- raw_data = value & 0x000003ff;
- if (!valid) return -1.0;
- return 410.04 - (0.48705 * raw_data);
-}
-
-static double get_avs_voltage_74xx(struct Voltage* v) {
- volatile uint32_t* reg;
- uint32_t value, valid, raw_data;
-
- reg = mmap_addr + v->offset_data;
- value = *reg;
- // see 7425-PR500-RDS.pdf
- valid = (value & 0x00000400) >> 10;
- raw_data = value & 0x000003ff;
- if (!valid) return -1.0;
- return ((990 * raw_data * 8) / (7*1024)) / 1000.0;
-}
-
-static double get_avs_voltage_7252(struct Voltage* v) {
- volatile uint32_t* reg;
- uint32_t value, valid, raw_data;
-
- reg = mmap_addr + v->offset_data;
- value = *reg;
- valid = (value & 0x00000400) >> 10;
- raw_data = value & 0x000003ff;
- if (!valid) return -1.0;
- return ((880.0/1024.0)/(0.7)*raw_data) / 1000;
-}
-
-// Write the given GPIO pin.
-static void set_gpio(struct Gpio *g, int level) {
- volatile uint32_t* reg;
- uint32_t value;
-
- if (g->old_val == level) {
- // If this is the same value as last time, don't do anything, for two
- // reasons:
- // 1) If you set the gpio too often, it seems to stay low (the led
- // stays off).
- // 2) If some process other than us is twiddling a led, this way we
- // won't interfere with it.
- return;
- }
- g->old_val = level;
-
- reg = mmap_addr + g->offset_data;
- value = *reg;
- value &= ~g->mask;
- value |= (level ? g->on_value : g->off_value) << g->shift;
- *reg = value;
-}
-
-
-// Read the given GPIO pin
-static int get_gpio(struct Gpio *g) {
- volatile uint32_t* reg;
- uint32_t value;
-
- reg = mmap_addr + g->offset_data;
- value = (*reg & g->mask) >> g->shift;
- return (value == g->on_value);
-}
-
-
-// initialize GPIO to input or output
-static void set_direction(struct Gpio *g)
-{
- volatile uint32_t* reg;
- uint32_t value;
-
- if (!g->is_present || g->offset_direction == 0)
- return;
-
- reg = mmap_addr + g->offset_direction;
- value = *reg;
- value &= ~g->mask;
- value |= g->direction_value << g->shift;
- *reg = value;
-}
-
-// initialize pin to LED or GPIO etc
-static void set_pinmux(struct Gpio *g) {
- volatile uint32_t* reg;
- uint32_t value;
-
- if (!g->is_present || g->pinmux_offset == 0)
- return;
-
- reg = mmap_addr + g->pinmux_offset;
- value = *reg;
- value &= ~g->pinmux_mask;
- value |= g->pinmux_value;
- *reg = value;
-}
-
// Same as time(), but in monotonic clock milliseconds instead.
static long long msec_now(void) {
struct timespec ts;
@@ -600,61 +36,6 @@
return ((long long)ts.tv_sec) * 1000 + (ts.tv_nsec / 1000000);
}
-static void platform_cleanup(void) {
- if (mmap_addr != MAP_FAILED) {
- if (munmap((void*) mmap_addr, mmap_size) < 0) {
- perror("munmap");
- }
- mmap_addr = MAP_FAILED;
- mmap_size = 0;
- }
- if (mmap_fd >= 0) {
- close(mmap_fd);
- mmap_fd = -1;
- }
-}
-
-static void *mmap_(void* addr, size_t size, int prot, int flags, int fd,
- off_t offset) {
-#ifdef __ANDROID__
- return mmap64(addr, size, prot, flags, fd, (off64_t)(uint64_t)(uint32_t)offset);
-#else
- return mmap(addr, size, prot, flags, fd, offset);
-#endif
-}
-
-static int platform_init(struct platform_info* p) {
- platform_cleanup();
-
- mmap_fd = open(DEVMEM, O_RDWR);
- if (mmap_fd < 0) {
- perror(DEVMEM);
- return -1;
- }
- mmap_size = p->mmap_size;
- mmap_addr = mmap_(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
- mmap_fd, p->mmap_base);
- if (mmap_addr == MAP_FAILED) {
- perror("mmap");
- platform_cleanup();
- return -1;
- }
- return 0;
-}
-
-static struct platform_info *get_platform_info(const char *platform_name) {
- int lim = sizeof(platforms) / sizeof(platforms[0]);
- for (int i = 0; i < lim; ++i) {
- struct platform_info *p = &platforms[i];
- if (0 == strncmp(platform_name, p->name, strlen(p->name))) {
- return p;
- }
- }
- fprintf(stderr, "No support for platform %s", platform_name);
- exit(1);
-}
-
-
// read a file containing a single short string.
// Returns a static buffer. Be careful!
static char *read_file(const char *filename) {
@@ -677,7 +58,6 @@
return (platform->fan_control.is_present);
}
-
/*
* We're polling at a very high frequency, which is a pain. This would be
* slightly less gross inside the kernel (for less context switching and
@@ -796,109 +176,6 @@
set_pwm(&platform->leds.led_brightness, level);
}
-/* set LED/Keypad timings to control LED brightness */
-static void init_gfhd200(UNUSED struct platform_info* p) {
- volatile uint32_t* reg;
-
- reg = mmap_addr + 0x9034; // LDK_CONTROL
- *reg = 0x01; // reset
- *reg = 0x18; // ver=1 inv_led=1
-
- reg = mmap_addr + 0x9008; // LDK_PRESCHI, LO (clock divisor)
- reg[0] = 0x00;
- reg[1] = 0x10; // tick = clock / 0x0010, not sure what clock is
-
- reg = mmap_addr + 0x9010; // LDK_DUTYOFF, ON
- reg[0] = 0x40;
- reg[1] = 0xc0; // 0x40 off ticks then 0xc0 on ticks == 75% brightness
-}
-
-/* set LED/Keypad timings to control LED brightness */
-static void init_gfhd254(UNUSED 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.
-
- // 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
- // the duty cycle.
- reg = mmap_addr + 0x9014; // PWM3_CWORD_LSB
- reg[0] = 1;
-
- // 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;
-
- // Default the LED brightness to 50.
- set_led_brightness(50);
-}
-
-static void init_platform(struct platform_info* p) {
- if (p->init) {
- (*p->init)(p);
- }
-}
-
-static void initialize_gpios(void) {
- init_platform(platform);
-
- set_pinmux(&platform->leds.led_red);
- set_pinmux(&platform->leds.led_blue);
- set_pinmux(&platform->leds.led_activity);
- set_pinmux(&platform->leds.led_standby);
-
- set_direction(&platform->leds.led_red);
- set_direction(&platform->leds.led_blue);
- set_direction(&platform->leds.led_activity);
- set_direction(&platform->leds.led_standby);
- set_direction(&platform->reset_button);
- set_direction(&platform->fan_tick);
-}
-
/* standard API follows */
PinHandle PinCreate(void) {
@@ -907,13 +184,14 @@
perror("calloc(PinHandle)");
return NULL;
}
+
platform = get_platform_info(read_file("/etc/platform"));
if (platform_init(platform) < 0) {
fprintf(stderr, "platform_init failed\n");
PinDestroy(handle);
return NULL;
}
- initialize_gpios();
+
return handle;
}
diff --git a/rcu_audio/.gitignore b/rcu_audio/.gitignore
new file mode 100644
index 0000000..e08ad34
--- /dev/null
+++ b/rcu_audio/.gitignore
@@ -0,0 +1,4 @@
+ti-rcu-audio
+gfrm100-rcu-audio
+gfrm-voice-demo
+*.o
diff --git a/rcu_audio/Makefile b/rcu_audio/Makefile
new file mode 100644
index 0000000..aa98aba
--- /dev/null
+++ b/rcu_audio/Makefile
@@ -0,0 +1,37 @@
+CC:=$(CROSS_COMPILE)gcc
+CPP:=$(CROSS_COMPILE)g++
+HOST_PROTOC ?= $(HOSTDIR)/usr/bin/protoc
+PREFIX=/usr
+BINDIR=$(DESTDIR)$(PREFIX)/bin
+CFLAGS += $(EXTRACFLAGS)
+LDFLAGS += $(EXTRALDFLAGS)
+
+BINARIES = ti-rcu-audio gfrm100-rcu-audio gfrm-voice-demo
+CHECKING = -Wall -Werror
+all: $(BINARIES)
+
+%.pb.cc: %.proto
+ echo "Building .pb.cc"
+ $(HOST_PROTOC) --cpp_out=. $<
+
+ti-rcu-audio: ti-rcu-audio.cc RAS_lib.c RAS_lib.h rcu-audio.h remote_control_audio.pb.cc rcu-utils.cc
+ $(CPP) -I. $(CHECKING) $(CFLAGS) ti-rcu-audio.cc RAS_lib.c remote_control_audio.pb.cc rcu-utils.cc -o $@ $(LDFLAGS) -lprotobuf-lite
+
+gfrm100-rcu-audio: gfrm100-rcu-audio.cc rcu-audio.h remote_control_audio.pb.cc rcu-utils.cc
+ $(CPP) -I. $(CHECKING) $(CFLAGS) gfrm100-rcu-audio.cc remote_control_audio.pb.cc rcu-utils.cc -o $@ $(LDFLAGS) -lprotobuf-lite
+
+gfrm-voice-demo: gfrm-voice-demo.cc rcu-audio.h remote_control_audio.pb.cc rcu-utils.cc
+ $(CPP) -I. $(CHECKING) $(CFLAGS) gfrm-voice-demo.cc remote_control_audio.pb.cc rcu-utils.cc -o $@ $(LDFLAGS) -lprotobuf-lite
+
+install:
+ mkdir -p $(BINDIR)
+ cp $(BINARIES) $(BINDIR)
+
+install-libs:
+ @echo "No libs to install."
+
+clean:
+ rm -f $(BINARIES) *.o
+
+test:
+ true
diff --git a/rcu_audio/RAS_lib.c b/rcu_audio/RAS_lib.c
new file mode 100755
index 0000000..cfb33df
--- /dev/null
+++ b/rcu_audio/RAS_lib.c
@@ -0,0 +1,332 @@
+/*
+* Copyright (c) [2015] Texas Instruments Incorporated
+*
+* All rights reserved not granted herein.
+* Limited License.
+*
+* Texas Instruments Incorporated grants a world-wide, royalty-free,
+* non-exclusive license under copyrights and patents it now or hereafter
+* owns or controls to make, have made, use, import, offer to sell and sell ("Utilize")
+* this software subject to the terms herein. With respect to the foregoing patent
+*license, such license is granted solely to the extent that any such patent is necessary
+* to Utilize the software alone. The patent license shall not apply to any combinations which
+* include this software, other than combinations with devices manufactured by or for TI ("TI Devices").
+* No hardware patent is licensed hereunder.
+*
+* Redistributions must preserve existing copyright notices and reproduce this license (including the
+* above copyright notice and the disclaimer and (if applicable) source code license limitations below)
+* in the documentation and/or other materials provided with the distribution
+*
+* Redistribution and use in binary form, without modification, are permitted provided that the
+* following conditions are met:
+*
+* * No reverse engineering, decompilation, or disassembly of this software is permitted
+* with respect to any software provided in binary form.
+* * any redistribution and use are licensed by TI for use only with TI Devices.
+* * Nothing shall obligate TI to provide you with source code for the software licensed
+* and provided to you in object code.
+*
+* If software source code is provided to you, modification and redistribution of the source code are
+* permitted provided that the following conditions are met:
+*
+* * any redistribution and use of the source code, including any resulting derivative works, are
+* licensed by TI for use only with TI Devices.
+* * any redistribution and use of any object code compiled from the source code and any resulting
+* derivative works, are licensed by TI for use only with TI Devices.
+*
+* Neither the name of Texas Instruments Incorporated nor the names of its suppliers may be used to
+* endorse or promote products derived from this software without specific prior written permission.
+*
+* DISCLAIMER.
+*
+* THIS SOFTWARE IS PROVIDED BY TI AND TI'S LICENSORS "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 TI AND TI'S LICENSORS 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "RAS_lib.h"
+
+static int16 PV_Dec;
+static int8 SI_Dec;
+
+
+#define NULL 0
+#define PRED_CYPHER_CONST 0x3292
+#define STEP_CYPHER_CONST 0x5438
+
+#define HDR_NOT_SCRAMBLED 1
+
+
+static uint8 ras_pec_mode;
+static int16 per_buff[MAX_INPUT_BUF_SIZE*4];
+
+const uint16 codec_stepsize_Lut[89] =
+{
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 16, 17, 19, 21, 23, 25, 28, 31,
+ 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143,
+ 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
+ 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
+ 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,10442,11487,12635,13899,
+ 15289,16818,18500,20350,22385,24623,27086,29794, 32767
+};
+
+const int8 codec_IndexLut[16] =
+{
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+};
+
+
+/**************************************************************************************************
+*
+* @fn codec_DecodeSingle
+*
+* @brief This routine decode a 4bits ADPCM sample to a uin16 PCM audio sample.
+*
+* @param uint8 a 4 bits ADPCM sample
+*
+*
+* @return the 16 bits PCM samples.
+*/
+static int16 codec_DecodeSingle(uint8 codec_4bits)
+{
+ int16 step = codec_stepsize_Lut[SI_Dec];
+ int16 cum_diff = step>>3;
+
+ // DBG("step %d cum_diff %d\n", step, cum_diff);
+
+ SI_Dec += codec_IndexLut[codec_4bits];
+ if(SI_Dec<0) SI_Dec = 0; else if(SI_Dec>88) SI_Dec = 88;
+
+ if(codec_4bits&4)
+ cum_diff += step;
+ if(codec_4bits&2)
+ cum_diff += step>>1;
+ if(codec_4bits&1)
+ cum_diff += step>>2;
+
+ if(codec_4bits&8)
+ {
+ if (PV_Dec < (-32767+cum_diff))
+ (PV_Dec) = -32767;
+ else
+ PV_Dec -= cum_diff;
+ }
+ else
+ {
+ if (PV_Dec > (0x7fff-cum_diff))
+ (PV_Dec) = 0x7fff;
+ else
+ PV_Dec += cum_diff;
+ }
+ return PV_Dec;
+}
+
+/**************************************************************************************************
+ *
+ * @fn codec_DecodeBuff
+ *
+ * @brief This routine encode a buffer with ADPCM IMA.
+ *
+ * @param int16* dst pointer to buffer where decoding result will be copy
+ * uint8* src input buffer, size must be a multiple of 4 bytes
+ * srcSize Number of byte that will be generated by the encoder (4* (src buffer size in byte))
+ *
+ *
+ * @return none
+ */
+static void codec_DecodeBuff(int16* dst, uint8* src, unsigned int srcSize, int8 *si, int16 *pv)
+{
+
+ // calculate pointers to iterate output buffer
+ int16* out = dst;
+ int16* end = out+(srcSize>>1);
+ int16 temp;
+
+ PV_Dec = *pv;
+ SI_Dec = *si;
+
+ while(out<end)
+ {
+ // get byte from src
+ uint8 codec = *src;
+ // DBG("codec %04x\n", codec);
+ // *out++ = codec_DecodeSingle((codec&0xF)); // decode value and store it
+ temp = codec_DecodeSingle((codec&0xF)); // decode value and store it
+ // DBG("from low %04x\n", temp);
+ *out++ = temp;
+ codec >>= 4; // use high nibble of byte
+ codec &= 0xF; // use high nibble of byte
+ // *out++ = codec_DecodeSingle(codec); // decode value and store it
+ temp = codec_DecodeSingle((codec)); // decode value and store it
+ // DBG("from high %04x\n", temp);
+ *out++ = temp;
+ ++src; // move on a byte for next sample
+ }
+
+ *pv = PV_Dec;
+ *si = SI_Dec;
+}
+
+
+/**************************************************************************************************
+ *
+ * @fn RAS_Init
+ *
+ * @brief RemoTI Audio Subsystem, initialization function
+ *
+ * input parameters
+ *
+ * @param pec_mode: Packet Error concealment algorithm to apply:
+ * RAS_NO_PEC(0): None (default)
+ * RAS_PEC_MODE1(1): Replace lost packets by last valid.
+ *
+ * output parameters
+ *codec_ima_DecodeBuff
+ * None.
+ *
+ * @return .
+ * status. 0 SUCCESS
+ * -1 ERROR: INVALID PARAMETER
+ */
+uint8 RAS_Init( uint8 pec_mode )
+{
+ uint16 i;
+ if (pec_mode>RAS_PEC_MODE1) return -1;
+ ras_pec_mode = pec_mode;
+
+ for (i=0; i<(MAX_INPUT_BUF_SIZE*4);i++)
+ per_buff[i]=0;
+
+ return 0;
+}
+
+/**************************************************************************************************
+ *
+ * @fn RAS_GetVersion
+ *
+ * @brief RemoTI Audio Subsystem, retrieve software version
+ *
+ * input parameters
+ *
+ * none
+ *
+ * output parameters
+ *
+ * None.
+ *
+ * @return .
+ * Software Version. MSB Major revision number
+ * LSB: Minor revision number
+ */
+uint16 RAS_GetVersion( void )
+{
+ return RAS_SOFTWARE_VERSION;
+}
+/**************************************************************************************************
+ *
+ * @fn RAS_Decode
+ *
+ * @brief RemoTI Audio Subsystem, decoding function. decode encoded audioframe to PCM samples.
+ *
+ * input parameters
+ *
+ * @param option: decoding option. can be pure decoding, or packet lot concealment algorithm:
+ * RAS_PACKET_LOST(0)
+ * RAS_DECODE(1)
+ * @param input: address of the buffer to decode, this buffer must include the 3 bytes header..
+ *
+ * @param inputLength: length of the buffer to decode, excluding the 3 bytes header.
+ * cannot be greater than 128 (MAX_INPUT_BUF_SIZE);
+ *
+ * output parameters
+ *
+ * @param output: buffer where the decoded PCM will be written. This buffer must be allocated by the caller.
+ * it must have a length of 4 times the inputLength variable
+ *
+ * @param outputLenght: length of the decoded buffer.
+ * max possible value 512 (4*MAX_INPUT_BUF_SIZE);
+ *
+ *
+ * @return .
+ * status. 0 SUCCESS
+ * -1 ERROR: INVALID PARAMETER
+ *
+ */
+uint8 RAS_Decode( uint8 option, uint8* input, uint16 inputLength, int16* output,uint16 *outputLenght )
+{
+ int8 step_index;
+ int16 predicted_value;
+ uint16 i;
+ static uint8 *rf_DataFrame;
+ *outputLenght = 0;
+
+ // DBG("RAS_Decode option %d input %04x inputLength %d output %04x outputLength %d\n", option, input, inputLength, output, outputLenght);
+
+ if ((output == NULL) || (inputLength > MAX_INPUT_BUF_SIZE)) return -1;
+
+#ifdef HDR_NOT_SCRAMBLED
+ predicted_value = (input [0] + ((input[1])<<8));
+ step_index = input [2] & 0xFF;
+#else
+ predicted_value = (int16)(((int16)((input[0])<<8))+((int16)(input [2] ))) ^ PRED_CYPHER_CONST;
+ step_index = (input [1] & 0xFF) ^STEP_CYPHER_CONST;
+#endif
+
+ //extract Predicted value and step index from the header.
+ inputLength-=3; //Remove Header Size
+ // check Option
+ switch(option)
+ {
+ case RAS_PACKET_LOST:
+ {
+ switch (ras_pec_mode)
+ {
+ case RAS_PEC_MODE1:
+ for (i=0; i<(inputLength*4);i++)
+ output[i] = per_buff[i];
+ break;
+ default:
+ break;
+ }
+
+ }
+ break;
+
+ case RAS_DECODE_TI_TYPE1:
+ if (input == NULL) return -1;
+ rf_DataFrame = input+3;
+ codec_DecodeBuff(output, rf_DataFrame, inputLength*4, &step_index, &predicted_value);
+
+ //Save Frame for packet error concealment
+ switch (ras_pec_mode)
+ {
+ case RAS_PEC_MODE1:
+ for (i=0; i<(inputLength*4);i++)
+ per_buff[i] = output[i];
+ break;
+ default:
+ break;
+ }
+
+ break;
+
+ default:
+ break;
+
+
+ }
+ *outputLenght = inputLength*4;
+ return 0;
+};
+
+
diff --git a/rcu_audio/RAS_lib.h b/rcu_audio/RAS_lib.h
new file mode 100755
index 0000000..e009902
--- /dev/null
+++ b/rcu_audio/RAS_lib.h
@@ -0,0 +1,195 @@
+/*
+* Copyright (c) [2015] Texas Instruments Incorporated
+*
+* All rights reserved not granted herein.
+* Limited License.
+*
+* Texas Instruments Incorporated grants a world-wide, royalty-free,
+* non-exclusive license under copyrights and patents it now or hereafter
+* owns or controls to make, have made, use, import, offer to sell and sell ("Utilize")
+* this software subject to the terms herein. With respect to the foregoing patent
+*license, such license is granted solely to the extent that any such patent is necessary
+* to Utilize the software alone. The patent license shall not apply to any combinations which
+* include this software, other than combinations with devices manufactured by or for TI ("TI Devices").
+* No hardware patent is licensed hereunder.
+*
+* Redistributions must preserve existing copyright notices and reproduce this license (including the
+* above copyright notice and the disclaimer and (if applicable) source code license limitations below)
+* in the documentation and/or other materials provided with the distribution
+*
+* Redistribution and use in binary form, without modification, are permitted provided that the
+* following conditions are met:
+*
+* * No reverse engineering, decompilation, or disassembly of this software is permitted
+* with respect to any software provided in binary form.
+* * any redistribution and use are licensed by TI for use only with TI Devices.
+* * Nothing shall obligate TI to provide you with source code for the software licensed
+* and provided to you in object code.
+*
+* If software source code is provided to you, modification and redistribution of the source code are
+* permitted provided that the following conditions are met:
+*
+* * any redistribution and use of the source code, including any resulting derivative works, are
+* licensed by TI for use only with TI Devices.
+* * any redistribution and use of any object code compiled from the source code and any resulting
+* derivative works, are licensed by TI for use only with TI Devices.
+*
+* Neither the name of Texas Instruments Incorporated nor the names of its suppliers may be used to
+* endorse or promote products derived from this software without specific prior written permission.
+*
+* DISCLAIMER.
+*
+* THIS SOFTWARE IS PROVIDED BY TI AND TI'S LICENSORS "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 TI AND TI'S LICENSORS BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef RSA_LIB_H
+#define RSA_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#if !defined PACK_1
+#define PACK_1
+#endif
+
+
+#if defined(_MSC_VER) || defined(unix) || (defined(__ICC430__) && (__ICC430__==1))
+#pragma pack(1)
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Defines
+
+#define MAX_INPUT_BUF_SIZE 128
+
+#define RAS_PACKET_LOST 0
+#define RAS_DECODE_TI_TYPE1 1
+
+#define RAS_NO_PEC 0
+#define RAS_PEC_MODE1 1
+
+//RAS Software Version: v1.3
+#define RAS_SOFTWARE_VERSION 0x0103
+/////////////////////////////////////////////////////////////////////////////
+// Typedefs
+#ifndef int8
+typedef signed char int8;
+#endif
+
+#ifndef uint8
+typedef unsigned char uint8;
+#endif
+
+#ifndef int16
+typedef signed short int16;
+#endif
+
+#ifndef uint16
+typedef unsigned short uint16;
+#endif
+
+#ifndef int32
+typedef signed int int32;
+#endif
+
+#ifndef uint32
+typedef unsigned int uint32;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Global variable
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Function declarations
+/**************************************************************************************************
+ *
+ * @fn RAS_GetVersion
+ *
+ * @brief RemoTI Audio Subsystem, retrieve software version
+ *
+ * input parameters
+ *
+ * none
+ *
+ * output parameters
+ *
+ * None.
+ *
+ * @return .
+ * Software Version. MSB Major revision number
+ * LSB: Minor revision number
+ */
+uint16 RAS_GetVersion( void );
+
+/**************************************************************************************************
+ *
+ * @fn RAS_Init
+ *
+ * @brief RemoTI Audio Subsystem, initialization function
+ *
+ * input parameters
+ *
+ * @param pec_mode: Packet Error concealment algorithm to apply:
+ * RAS_NO_PEC(0): None (default)
+ * RAS_PEC_MODE1(1): Replace lost packets by last valid.
+ *
+ * output parameters
+ *
+ * None.
+ *
+ * @return .
+ * status. 0 SUCCESS
+ * -1 ERROR: INVALID PARAMETER
+ */
+uint8 RAS_Init( uint8 pec_mode );
+
+
+/**************************************************************************************************
+ *
+ * @fn RAS_Decode
+ *
+ * @brief RemoTI Audio Subsystem, decoding function. decode encoded audioframe to PCM samples.
+ *
+ * input parameters
+ *
+ * @param option: decoding option. can be pure decoding, or packet lot concealment algorithm:
+ * RAS_PACKET_LOST(0)
+ * RAS_DECODE(1)
+ * @param input: address of the buffer to decode, this buffer must include the 3 bytes header..
+ *
+ * @param inputLenght: length of the buffer to decode, excluding the 3 bytes header.
+ * cannot be greater than 128 (MAX_INPUT_BUF_SIZE);
+ *
+ * output parameters
+ *
+ * @param output: buffer where the decoded PCM will be written. This buffer must be allocated by the caller.
+ * it must have a length of 4 times the inputLength variable
+ *
+ * @param outputLenght: length of the decoded buffer.
+ * max possible value 512 (4*MAX_INPUT_BUF_SIZE);
+ *
+ *
+ * @return .
+ * status. 0 SUCCESS
+ * -1 ERROR: INVALID PARAMETER
+ *
+ */
+uint8 RAS_Decode( uint8 option, uint8* input, uint16 inputLenght, int16* output,uint16 *outputLenght );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RSA_LIB_H
diff --git a/rcu_audio/gfrm-voice-demo.cc b/rcu_audio/gfrm-voice-demo.cc
new file mode 100644
index 0000000..c097252
--- /dev/null
+++ b/rcu_audio/gfrm-voice-demo.cc
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _BSD_SOURCE
+#include <endian.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "rcu-audio.h"
+#include "remote_control_audio.pb.h"
+
+
+typedef struct WAV_hdr
+{
+ uint32_t chunk_id;
+ uint32_t chunk_size;
+ uint32_t format;
+
+ uint32_t subchunk1_id;
+ uint32_t subchunk1_size;
+ uint16_t audio_format;
+ uint16_t num_channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate;
+ uint16_t block_align;
+ uint16_t bits_per_sample;
+
+ uint32_t subchunk2_id;
+ uint32_t subchunk2_size;
+} WAV_hdr_t;
+
+
+static int usage(const char *progname)
+{
+ fprintf(stderr, "usage: %s [-f outfile]\n, where:", progname);
+ fprintf(stderr, "\t-f outfile: file to write audio to in WAV format.\n");
+ exit(1);
+}
+
+
+int main(int argc, char **argv)
+{
+ mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
+ int fd;
+ struct sockaddr_un sun;
+ const char *outfile = "/tmp/audio.wav";
+ int outfd;
+ uint8_t buf[8192];
+ WAV_hdr_t hdr;
+ ssize_t len, totlen=0;
+ int c;
+ struct timeval tv;
+ const char *model = "UNKNOWN";
+
+ memset(buf, 0, sizeof(buf));
+ memset(&hdr, 0, sizeof(hdr));
+
+ while ((c = getopt(argc, argv, "f:")) != -1) {
+ switch (c) {
+ case 'f':
+ outfile = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
+ perror("socket(AF_UNIX) RCU_AUDIO_PATH");
+ exit(1);
+ }
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strncpy(&sun.sun_path[1], RCU_AUDIO_PATH, sizeof(sun.sun_path) - 2);
+ if (bind(fd, (const struct sockaddr *) &sun, sizeof(sun)) < 0) {
+ perror("bind(AF_UNIX) RCU_AUDIO_PATH");
+ exit(1);
+ }
+
+ if ((outfd = open(outfile, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) {
+ fprintf(stderr, "Unable to open %s for writing.\n", outfile);
+ exit(1);
+ }
+
+ if ((len = write(outfd, &hdr, sizeof(WAV_hdr_t))) != sizeof(WAV_hdr_t)) {
+ fprintf(stderr, "Incorrect size for WAV header: %zd != %zd\n",
+ len, sizeof(WAV_hdr_t));
+ exit(1);
+ }
+
+ tv.tv_sec = 0x7fffffff;
+ tv.tv_usec = 0;
+
+ while (1) {
+ fd_set rfds;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ if (select(fd + 1, &rfds, NULL, NULL, &tv) <= 0) {
+ /* No more data, close the output and exit. */
+ break;
+ }
+
+ len = read(fd, buf, sizeof(buf));
+ if (len > 0) {
+ rcaudio::AudioSamples samples;
+ const char *data;
+ ssize_t data_len;
+
+ if (!samples.ParseFromArray(buf, len)) {
+ if (pacing()) {
+ printf("failed to parse rcaudio::AudioSamples.\n");
+ }
+ continue;
+ }
+
+ if (samples.audio_format() != rcaudio::AudioSamples::PCM_16BIT_16KHZ) {
+ /* if we ever build a remote with a different format, we'll need
+ * to keep track of it here and adjust the WAV header to match. */
+ if (pacing()) {
+ fprintf(stderr, "unknown audio format %d\n", samples.audio_format());
+ }
+ continue;
+ }
+
+ switch (samples.remote_type()) {
+ case rcaudio::AudioSamples::GFRM210: model = "GFRM210"; break;
+ case rcaudio::AudioSamples::GFRM100: model = "GFRM100"; break;
+
+ case rcaudio::AudioSamples::UNDEFINED_REMOTE_TYPE:
+ default:
+ model = "UNKNOWN";
+ break;
+ }
+
+ data = samples.audio_samples().c_str();
+ data_len = samples.audio_samples().size();
+ totlen += data_len;
+ if (write(outfd, data, data_len) != data_len) {
+ fprintf(stderr, "short write!\n");
+ exit(1);
+ }
+ } else if (len == 0) {
+ break;
+ } else if (len < 0) {
+ perror("read");
+ exit(1);
+ }
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ }
+
+ /* print the remote control type to stdout, demo script uses it. */
+ puts(model);
+
+ lseek(outfd, 0, SEEK_SET);
+
+ #define BITS_PER_SAMPLE 16
+ #define SAMPLES_PER_SECOND 16000
+ /* http://soundfile.sapp.org/doc/WaveFormat/ */
+ hdr.chunk_id = htole32(0x46464952); // "RIFF"
+ hdr.chunk_size = htole32(36 + totlen);
+ hdr.format = htole32(0x45564157); // "WAVE"
+
+ hdr.subchunk1_id = htole32(0x20746d66); // "fmt "
+ hdr.subchunk1_size = htole32(16);
+ hdr.audio_format = htole16(1);
+ hdr.num_channels = htole16(1);
+ hdr.sample_rate = htole32(SAMPLES_PER_SECOND);
+ hdr.byte_rate = htole32(SAMPLES_PER_SECOND * 1 * BITS_PER_SAMPLE/8);
+ hdr.block_align = htole16(1 * BITS_PER_SAMPLE/8);
+ hdr.bits_per_sample = htole16(BITS_PER_SAMPLE);
+
+ hdr.subchunk2_id = htole32(0x61746164); // "data"
+ hdr.subchunk2_size = htole32(totlen);
+ if ((len = write(outfd, &hdr, sizeof(WAV_hdr_t))) != sizeof(WAV_hdr_t)) {
+ fprintf(stderr, "Incorrect size for WAV header: %zd != %zd\n",
+ len, sizeof(WAV_hdr_t));
+ exit(1);
+ }
+
+ exit(0);
+}
diff --git a/rcu_audio/gfrm100-rcu-audio.cc b/rcu_audio/gfrm100-rcu-audio.cc
new file mode 100644
index 0000000..6e55e93
--- /dev/null
+++ b/rcu_audio/gfrm100-rcu-audio.cc
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Derived from hid-example.c, license:
+ *
+ * Hidraw Userspace Example
+ *
+ * Copyright (c) 2010 Alan Ott <alan@signal11.us>
+ * Copyright (c) 2010 Signal 11 Software
+ *
+ * The code may be used by anyone for any purpose,
+ * and can serve as a starting point for developing
+ * applications using hidraw.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/hidraw.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "rcu-audio.h"
+#include "remote_control_audio.pb.h"
+
+
+int main(int argc, char **argv)
+{
+ int in = -1, out = -1, connected = 0;
+ const char *device;
+ struct sockaddr_un sun;
+ char name[16];
+ char address[64];
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s /dev/hidraw#\n", argv[0]);
+ exit(1);
+ }
+ device = argv[1];
+
+ if ((in = open(device, O_RDWR)) < 0) {
+ perror("open /dev/hidraw");
+ exit(1);
+ }
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strncpy(&sun.sun_path[1], RCU_AUDIO_PATH, sizeof(sun.sun_path) - 2);
+
+ if (ioctl(in, HIDIOCGRAWNAME(sizeof(name)), name) < 0) {
+ perror("HIDIOCGRAWNAME");
+ exit(1);
+ }
+ if (strcmp(name, "GFRM100") != 0) {
+ fprintf(stderr, "%s is not a GFRM100. Exiting.\n", device);
+ exit(0);
+ }
+
+ if (ioctl(in, HIDIOCGRAWPHYS(sizeof(address)), address) < 0) {
+ perror("HIDIOCGRAWPHYS");
+ exit(1);
+ }
+
+ /* this process will be started out of the hotplug script when a new
+ * remote appears. We either exit if not a GFRM100, or daemonize to
+ * let the hotplug script continue. */
+ if (daemon(0, 1)) {
+ perror("daemon()");
+ exit(1);
+ }
+
+ while (1) {
+ uint8_t data[2048];
+ size_t len = read(in, data, sizeof(data));
+
+ if (len < 0) {
+ fprintf(stderr, "GFRM100 has disconnected. Exiting.\n");
+ exit(0);
+ }
+
+ if (data[0] != 0xf7) {
+ /* Not an audio packet */
+ continue;
+ }
+
+ if (data[1] == 0x01 && len > 4) {
+ rcaudio::AudioSamples samples;
+ std::vector<uint8_t> pkt;
+
+ samples.set_rc_address(address);
+ samples.set_audio_format(rcaudio::AudioSamples::PCM_16BIT_16KHZ);
+ samples.set_remote_type(rcaudio::AudioSamples::GFRM100);
+
+ /*
+ * data[0] == 0xf7
+ * data[1] == 0x01
+ * data[2] and data[3] are a count of the number of samples.
+ * data[4] == first byte of audio data.
+ */
+ samples.set_audio_samples(data + 4, len - 4);
+
+ pkt.resize(samples.ByteSize());
+ samples.SerializeToArray(&pkt[0], samples.ByteSize());
+
+ if (out < 0) {
+ out = get_socket_or_die();
+ }
+
+ if (!connected) {
+ if (connect(out, (const struct sockaddr *) &sun, sizeof(sun)) == 0) {
+ connected = 1;
+ } else {
+ sleep(2); /* rate limit how often we retry. */
+ }
+ }
+
+ if (connected) {
+ if (send(out, &pkt[0], pkt.size(), 0) != (ssize_t)pkt.size()) {
+ fprintf(stderr, "Audio send failed, will reconnect.\n");
+ close(out);
+ out = -1;
+ connected = 0;
+ }
+ }
+ }
+ }
+ return 0;
+}
diff --git a/rcu_audio/rcu-audio.h b/rcu_audio/rcu-audio.h
new file mode 100644
index 0000000..725ca10
--- /dev/null
+++ b/rcu_audio/rcu-audio.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RCU_AUDIO_H
+#define RCU_AUDIO_H
+
+#define RCU_AUDIO_PATH "rcu_audio"
+
+/* Return 1 if at least one second has passed since the
+ * last successful call to pacing(). */
+extern int pacing();
+
+/* Return an AF_UNIX socket, or die trying. */
+extern int get_socket_or_die();
+
+#endif /* RCU_AUDIO_H */
diff --git a/rcu_audio/rcu-utils.cc b/rcu_audio/rcu-utils.cc
new file mode 100644
index 0000000..2bd1e41
--- /dev/null
+++ b/rcu_audio/rcu-utils.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <unistd.h>
+
+static uint64_t monotime(void) {
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
+ perror("clock_gettime(CLOCK_MONOTONIC)");
+ exit(1);
+ } else {
+ return (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
+ }
+}
+
+
+/* Return 1 if at least one second has passed since the
+ * last successful call to pacing(). */
+int pacing() {
+ static uint64_t last = 0;
+ uint64_t now = monotime();
+ int rc = 0;
+
+ if ((now - last) > 1000000) {
+ last = now;
+ rc = 1;
+ }
+
+ return rc;
+}
+
+
+int get_socket_or_die()
+{
+ int s;
+
+ if ((s = socket(AF_UNIX, SOCK_NONBLOCK | SOCK_DGRAM, 0)) < 0) {
+ perror("socket(AF_UNIX)");
+ exit(1);
+ }
+
+ return s;
+}
diff --git a/rcu_audio/remote_control_audio.proto b/rcu_audio/remote_control_audio.proto
new file mode 100644
index 0000000..3f6b767
--- /dev/null
+++ b/rcu_audio/remote_control_audio.proto
@@ -0,0 +1,25 @@
+syntax = "proto2";
+package rcaudio;
+option optimize_for = LITE_RUNTIME;
+
+message AudioSamples {
+ // A unique identifier for the remote control.
+ // For Bluetooth this will be the BDADDR like "00:11:22:33:44:55"
+ optional string rc_address = 1;
+
+ enum AudioFormat {
+ UNDEFINED_AUDIO_FORMAT = 0;
+ PCM_16BIT_16KHZ = 1;
+ }
+ optional AudioFormat audio_format = 2;
+
+ enum RemoteType {
+ UNDEFINED_REMOTE_TYPE = 0;
+ GFRM210 = 1;
+ GFRM100 = 2;
+ }
+ optional RemoteType remote_type = 3;
+
+ optional bytes audio_samples = 4;
+};
+
diff --git a/rcu_audio/ti-rcu-audio.cc b/rcu_audio/ti-rcu-audio.cc
new file mode 100644
index 0000000..76f1126
--- /dev/null
+++ b/rcu_audio/ti-rcu-audio.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* Note: though this specific file is licensed under the Apache license,
+ * it exists in order to interface with the RAS_LIB.c implementation
+ * provided by TI which is not licensed under Apache. */
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "rcu-audio.h"
+#include "remote_control_audio.pb.h"
+#include "RAS_lib.h"
+
+#define TI_AUDIO_PATH "\0rc_audio_ti"
+
+int main(int argc, char **argv)
+{
+ int is = -1, os = -1, connected = 0;
+ struct sockaddr_un sun;
+ uint8 prev = 0;
+ int msgs = 0, missed = 0, errors = 0;
+
+ is = get_socket_or_die();
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", TI_AUDIO_PATH);
+ if (bind(is, (const struct sockaddr *) &sun, sizeof(sun)) < 0) {
+ perror("bind(AF_UNIX)");
+ exit(1);
+ }
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strncpy(&sun.sun_path[1], RCU_AUDIO_PATH, sizeof(sun.sun_path) - 2);
+
+ while (1) {
+ uint8 ibuf[MAX_INPUT_BUF_SIZE + 6 + 1 + 4];
+ size_t ilen;
+
+ ilen = recv(is, ibuf, sizeof(ibuf), 0);
+ if (ilen < 23) {
+ /* end of an audio file, prepare for the next one */
+ printf("Finished audio stream; msgs = %d, missed = %d, errors = %d\n",
+ msgs, missed, errors);
+ msgs = missed = errors = 0;
+ RAS_Init(RAS_NO_PEC);
+ } else {
+ uint8_t remote_type = ibuf[6];
+ uint8 expected = (prev + 1) & 0x1f;
+ uint8 seqnum = (ibuf[7] >> 3) & 0x1f;
+ uint8 *data;
+ uint16 data_len;
+ int16 obuf[4 * MAX_INPUT_BUF_SIZE];
+ uint16 olen;
+
+ if (seqnum != expected) {
+ missed++;
+ }
+ prev = seqnum;
+ msgs++;
+
+ /*
+ * We skip over:
+ * the first 6 bytes, which is the BDADDR of the remote.
+ * the remote type byte (0 == GFRM210)
+ * the sequence number byte
+ *
+ * The first three bytes of payload are some kind of RAS header.
+ * RAS_Decode() says the data pointer must include the three bytes
+ * of header, but the length must not include those 3 bytes.
+ * So the length is decremented by 6+1+1+3 for a total of 11.
+ */
+ data = &ibuf[6 + 1 + 1];
+ data_len = ilen - (6 + 1 + 1 + 3);
+ if (RAS_Decode(RAS_DECODE_TI_TYPE1, data, data_len, obuf, &olen) == 0) {
+ rcaudio::AudioSamples samples;
+ char bdaddr[18];
+ std::vector<uint8_t> pkt;
+
+ snprintf(bdaddr, sizeof(bdaddr),
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ ibuf[0], ibuf[1], ibuf[2], ibuf[3], ibuf[4], ibuf[5]);
+
+ samples.set_rc_address(bdaddr);
+ if (remote_type == 0) {
+ samples.set_audio_format(rcaudio::AudioSamples::PCM_16BIT_16KHZ);
+ samples.set_remote_type(rcaudio::AudioSamples::GFRM210);
+ } else {
+ samples.set_audio_format(rcaudio::AudioSamples::UNDEFINED_AUDIO_FORMAT);
+ samples.set_remote_type(rcaudio::AudioSamples::UNDEFINED_REMOTE_TYPE);
+ }
+ samples.set_audio_samples(obuf, olen);
+
+ pkt.resize(samples.ByteSize());
+ samples.SerializeToArray(&pkt[0], samples.ByteSize());
+
+ if (os < 0) {
+ os = get_socket_or_die();
+ }
+
+ if (!connected) {
+ if (connect(os, (const struct sockaddr *) &sun, sizeof(sun)) == 0) {
+ connected = 1;
+ } else {
+ sleep(2); /* rate limit how often we try */
+ }
+ }
+
+ if (connected) {
+ if (send(os, &pkt[0], pkt.size(), 0) != (ssize_t)pkt.size()) {
+ fprintf(stderr, "Audio send failed, will reconnect.\n");
+ connected = 0;
+ close(os);
+ os = -1;
+ }
+ }
+ } else {
+ if (pacing()) {
+ printf("RAS_Decode(RAS_DECODE_TI_TYPE1) failed\n");
+ }
+ errors++;
+ }
+ }
+ }
+}
diff --git a/spectralanalyzer/Makefile b/spectralanalyzer/Makefile
index 047dcb3..21531d6 100644
--- a/spectralanalyzer/Makefile
+++ b/spectralanalyzer/Makefile
@@ -7,6 +7,9 @@
BINDIR=$(DESTDIR)/bin
CFLAGS += -Wall -Werror $(EXTRACFLAGS)
+ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gfrg240)
+CFLAGS += -Wno-error=format
+endif
LDFLAGS += -lm -lrt $(EXTRALDFLAGS)
all: spectral