summaryrefslogtreecommitdiffstats
path: root/data/lineageos_wiki/find_lineageos_devices.py
diff options
context:
space:
mode:
Diffstat (limited to 'data/lineageos_wiki/find_lineageos_devices.py')
-rwxr-xr-xdata/lineageos_wiki/find_lineageos_devices.py297
1 files changed, 297 insertions, 0 deletions
diff --git a/data/lineageos_wiki/find_lineageos_devices.py b/data/lineageos_wiki/find_lineageos_devices.py
new file mode 100755
index 0000000..c75da3b
--- /dev/null
+++ b/data/lineageos_wiki/find_lineageos_devices.py
@@ -0,0 +1,297 @@
+#!/bin/env python
+# Program to find devices to support in LineageOS
+# Copyright (C) 2019 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+import os
+import re
+import sys
+import yaml
+
+basedir = '_data' + os.sep + 'devices'
+results = {}
+last_lineageos_version = 16.0
+
+def still_supported(document):
+ if not last_lineageos_version in document['versions']:
+ return False
+ if 'discontinued' in document['channels']:
+ return False
+
+ return True
+
+# In some cases it might be interesting to look if a given device has a modem in
+# the case where the SOC is known not to have any modem, so we can automatically
+# know that the is no shared memory between the modem and the device if there is
+# no modem.
+# Another case would be for SOCs that have optional modems where the device has
+# no modem feature and that we are sure that the modem CPU is not running any code.
+def device_has_modem(vendor, product):
+ if vendor == 'Google' and product == 'Pixel C':
+ return False
+
+ # unknown
+ return None
+
+def device_has_shared_memory_between_modem_and_soc(vendor, product):
+ # In the "Samsung Mobile Modem Driver (SVNET2) V1 for Memory-type Interface"
+ # section in lineageos_s5neolte_defconfig in the lineage-17.0 branch of
+ # https://github.com/LineageOS/android_kernel_samsung_universal7580 there
+ # is the following configuration:
+ # CONFIG_LINK_DEVICE_SHMEM=y
+ # CONFIG_LINK_DEVICE_HSIC is not set
+ if vendor == 'Samsung' and product == 'Galaxy S5 Neo':
+ return True
+
+ # unknown
+ return None
+
+def soc_has_modem(vendor, product):
+ if vendor == 'HiSilicon':
+ pass
+ elif vendor == 'Intel':
+ pass
+ elif vendor in ['Nvidia', 'NVIDIA'] and re.search("^Tegra 4", product):
+ # The modem could be implemented in software
+ pass
+ elif vendor in ['Nvidia', 'NVIDIA'] and re.search("^Tegra K1", product):
+ pass
+ elif vendor in ['Nvidia', 'NVIDIA'] and re.search("^Tegra X1", product):
+ pass
+ elif vendor == 'Qualcomm' and re.search("^MSM", product):
+ return True
+ elif vendor == 'Qualcomm' and re.search("^APQ", product) or re.search("^Snapdragon 600", product):
+ return False
+ elif vendor == 'Qualcomm' and product == "Qualcomm SDM845 Snapdragon 845":
+ return True
+ elif vendor == 'Qualcomm' and re.search("^SDM", product):
+ pass
+ elif vendor == 'Qualcomm' and (re.search("^SM", product) or re.search("^sm", product)):
+ pass
+ elif vendor == 'Qualcomm' and re.search("^Snapdragon", product):
+ pass
+ elif vendor == 'Samsung' and re.search("^Exynos", product):
+ # Some exynos modems do exists but they are either standalone
+ # modems or are SOCs where the AP is a Cortex M7 which are very
+ # unlikely to be able to run Android for smartphones or tablets
+ return False
+ elif vendor == 'TI' and product in ['OMAP4430', 'OMAP4460']:
+ # The modem could be implemented in software in the DSP but I never saw
+ # it on any device. It's used like that on the SysmoBTS which are BTS,
+ # not phones or a tablets.
+ return False
+ else:
+ print("<{}|{}>".format(vendor, product))
+ sys.exit(1)
+ # Unknown
+ return None
+
+def parse_soc(soc):
+ soc_vendors = [
+ "HiSilicon",
+ "Intel",
+ "NVIDIA",
+ "Nvidia",
+ "Qualcomm",
+ "Samsung",
+ "TI"]
+
+ soc_vendors_quirks = {
+ # ^Match : Vendor
+ 'Exynos' : 'Samsung',
+ }
+
+ results = {}
+ found = False
+ for vendor in soc_vendors:
+ match = re.search("^{0} (.*)".format(vendor), soc)
+ if match:
+ found = True
+ if vendor == 'NVIDIA':
+ vendor = 'Nvidia'
+ results['vendor'] = vendor
+ results['product'] = match.group(1)
+ results['has_modem'] = soc_has_modem(results['vendor'], results['product'])
+
+ if not found:
+ for match_data, vendor in soc_vendors_quirks.items():
+ match = re.search("^{0} (.*)".format(match_data), soc)
+ if match:
+ found = True
+ results['vendor'] = vendor
+ results['product'] = soc
+ results['has_modem'] = soc_has_modem(results['vendor'], results['product'])
+
+ if not 'vendor' in results:
+ print("TODO: Handle vendor in \"{}\"".format(soc))
+ sys.exit(1)
+
+ return results
+
+def battery_is_removable(battery):
+ # Example: Set top box
+ if battery == "None" or battery == None:
+ return None
+
+ if 'removable' not in battery:
+ print("TODO: Handle batteries where removable is absent: {}".format(battery))
+ sys.exit(1)
+
+ removable_battery = battery.get('removable')
+ if removable_battery == True:
+ return True
+ elif removable_battery == False:
+ return False
+ else:
+ print("TODO: Handle batteries where removable is \"{}\": {}".format(removable, battery))
+ sys.exit(1)
+
+def has_removable_battery(document):
+ removable_battery = None
+ if 'battery' not in document:
+ # We don't know the policies reguarding incomplete data so the data
+ # may either be incomplete or the device may not have a battery.
+ print("TODO: Add support for devices lacking a battery")
+ sys.exit(1)
+ else:
+ battery = document.get('battery')
+ if type(battery) is not list:
+ return battery_is_removable(battery)
+ else:
+ for battery_version in battery:
+ if battery_version == None:
+ continue
+
+ if len(battery_version.keys()) != 1:
+ print("TODO: Add support for battery versions with multiple keys: {}".format(battery_version))
+ sys.exit(1)
+
+ nr_removable = 0
+ nr_not_removable = 0
+
+ (battery_name, battery_data), = battery_version.items()
+
+ removable = battery_is_removable(battery_data)
+ if removable == True:
+ nr_removable += 1
+ elif removable == False:
+ nr_not_removable += 1
+
+ if nr_removable == 0 and nr_not_removable > 0:
+ return False
+ elif nr_not_removable == 0 and nr_removable > 0:
+ return True
+ else:
+ print("TODO: The data has removable and non-removable batteries"
+ "versions for the same device: {}".format(battery))
+ print(" Add support for it in the parsing code")
+ sys.exit(1)
+
+def interesting_for_replicant(document):
+ # For smartphones or tablets with a modem, since the modem is in
+ # the same SOC, we would need to do some extensive review of the SOC
+ # architecture to understand if the modem can be isolated with an IOMMU
+ # We would also need to make sure that the IOMMU is correctly setup (hard)
+ # and that the setup happens before the modem core are booted.
+ # For devices without a modem, we would still need to make sure that the
+ # sound card and the microphone is completely under the control of free
+ # software and that there aren't too much proprietary libraries to replace
+ soc = parse_soc(document['soc'])
+ if soc_has_modem(soc['vendor'], soc['product']) == True:
+ return False
+
+ if device_has_shared_memory_between_modem_and_soc(document['vendor'],
+ document['name']):
+ return False
+
+ # Non replaceable batteries causes too much issues for both users
+ # and developers as once you buy a device second hand, the battery
+ # doesn't last as much as when the device is new. On some devices,
+ # replicing non-removable batteries can be very difficult and damage
+ # the device along the way
+ #
+ # There are some devices where the battery is not removable but
+ # since the device can be easily opened, and that the battery has
+ # a connector, it might be possible for an experienced user or
+ # developer, or a repair shop, or a repair café to open the device
+ # and replace the battery.
+ #
+ # However it would take more research to understand if compatible
+ # batteries replacement can easily be found. The LineageOS wiki also
+ # doesn't have a way to distinguish between devices that can be easily
+ # opened and the ones that aren't.
+ #
+ # So for now we aproximate non-removable batteries to non-replaceable
+ # batteries until we find a way to deal with it.
+ if document['type'] != 'Set top box' and not has_removable_battery(document):
+ return False
+
+ return True
+
+def store_infos(results, document):
+ fields = ['vendor', 'name', 'type']
+
+ device_dict = {}
+ for field in fields:
+ device_dict[field] = document[field]
+
+ device_dict['removable_battery'] = has_removable_battery(document)
+
+ soc = document['soc']
+ soc_data = parse_soc(soc)
+ soc_vendor = None
+ soc_product = None
+
+ if soc_data['vendor'] not in results:
+ results[soc_data['vendor']] = {}
+ soc_vendor = results[soc_data['vendor']]
+
+ if soc_data['product'] not in soc_vendor:
+ soc_vendor[soc_data['product']] = []
+ soc_product = soc_vendor[soc_data['product']]
+
+ soc_product.append(device_dict)
+
+def print_results(results):
+ soc_vendors = list(results.keys())
+ soc_vendors.sort()
+ for soc_vendor in soc_vendors:
+ print ("{0}:".format(soc_vendor))
+ for soc_product in results[soc_vendor]:
+ print ("- {0}:".format(soc_product))
+ for device in results[soc_vendor][soc_product]:
+ print(" * {0}: {1} ({2})".format(
+ device['vendor'], device['name'], device['type']))
+
+def find_devices(path):
+ for filename in os.listdir(path + os.sep + basedir):
+ filepath = path + os.sep + basedir + os.sep + filename
+ if re.search("\.yml$", filepath):
+ yaml_file = open(filepath, 'r')
+ document = yaml.load(yaml_file)
+ if still_supported(document) and interesting_for_replicant(document):
+ store_infos(results, document)
+ print_results(results)
+
+def usage(argv0):
+ progname = os.path.basename(argv0)
+ print("{} [path/to/lineage_wiki]".format(progname))
+ sys.exit(1)
+
+if len(sys.argv) != 2:
+ usage(sys.argv[0])
+
+find_devices(sys.argv[1])
+