From 6a03a688ee7b6ce68d295230ddfeb5356eca007a Mon Sep 17 00:00:00 2001 From: Denis 'GNUtoo' Carikli Date: Wed, 18 Mar 2020 04:31:58 +0100 Subject: research: move in data/lineageos_wiki Signed-off-by: Denis 'GNUtoo' Carikli --- data/lineageos_wiki/README | 3 + data/lineageos_wiki/find_lineageos_devices.py | 297 ++++++++++++++++++++++++++ research/README | 3 - research/find_lineageos_devices.py | 297 -------------------------- 4 files changed, 300 insertions(+), 300 deletions(-) create mode 100644 data/lineageos_wiki/README create mode 100755 data/lineageos_wiki/find_lineageos_devices.py delete mode 100644 research/README delete mode 100755 research/find_lineageos_devices.py diff --git a/data/lineageos_wiki/README b/data/lineageos_wiki/README new file mode 100644 index 0000000..cb18d94 --- /dev/null +++ b/data/lineageos_wiki/README @@ -0,0 +1,3 @@ +== Usage == +$ git clone git://github.com/LineageOS/lineage_wiki.git +$ ./find_lineageos_devices.py lineage_wiki 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 +# +# 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 . + +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]) + diff --git a/research/README b/research/README deleted file mode 100644 index cb18d94..0000000 --- a/research/README +++ /dev/null @@ -1,3 +0,0 @@ -== Usage == -$ git clone git://github.com/LineageOS/lineage_wiki.git -$ ./find_lineageos_devices.py lineage_wiki diff --git a/research/find_lineageos_devices.py b/research/find_lineageos_devices.py deleted file mode 100755 index c75da3b..0000000 --- a/research/find_lineageos_devices.py +++ /dev/null @@ -1,297 +0,0 @@ -#!/bin/env python -# Program to find devices to support in LineageOS -# Copyright (C) 2019 Denis 'GNUtoo' Carikli -# -# 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 . - -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]) - -- cgit v1.2.3