1
1
mirror of https://github.com/NixOS/mobile-nixos.git synced 2024-12-15 19:23:01 +03:00

initrd-usb: Implement gadgetfs usb gadget

This has only been tested with google-walleye, with gsi.rndis.
This commit is contained in:
Samuel Dionne-Riel 2020-01-12 15:49:02 -05:00
parent b47ffc14e4
commit 50a04bd651
2 changed files with 169 additions and 0 deletions

View File

@ -3,6 +3,7 @@
with lib;
let
device_info = config.mobile.device.info;
cfg = config.mobile.boot.stage-1;
device_name = device_config.name;
device_config = config.mobile.device;
@ -27,6 +28,20 @@ in
'';
};
};
options.mobile.usb = {
idVendor = mkOption {
type = types.str;
description = ''
USB vendor ID for the USB gadget.
'';
};
idProduct = mkOption {
type = types.str;
description = ''
USB product ID for the USB gadget.
'';
};
};
config = lib.mkIf cfg.usb.enable {
boot.specialFileSystems = {
@ -48,9 +63,16 @@ in
++ optional cfg.networking.enable "rndis"
;
tasks = [
./stage-1/tasks/usb-gadget-task.rb
];
bootConfig = {
boot.usb.features = cfg.usb.features;
boot.usb.functions = builtins.listToAttrs (
builtins.map (feature: { name = feature; value = device_info.gadgetfs.functions."${feature}"; }) cfg.usb.features
);
usb = {
inherit (config.mobile.usb) idVendor idProduct;
};
};
};
};

View File

@ -0,0 +1,147 @@
module System::ConfigFSUSB
CONFIGFS = "/sys/kernel/config"
CONFIGFS_USB = File.join(CONFIGFS, "usb_gadget")
module Quirks
def self.gsi_rndis()
# Activate the IPA stuff... ugh.
File.write("/dev/ipa", 1)
end
end
# This is a bit underdocumented in the configfs and gadgetfs docs, but this
# would be used to translate strings in multiple languages.
# In practice, it never happens and is hardcoded to en-US.
# https://docs.microsoft.com/en-us/windows/win32/intl/language-identifier-constants-and-strings
# https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables
# Yes, the language identifiers are the Microsoft ones. I haven't found a
# detailed explanation for this, but it's part of the USB spec.
STRINGS_SUFFIX = "strings/0x409" # en-US ¯\_(ツ)_/¯
class Gadget
attr_reader :name
attr_accessor :features
# Initializes a USB gadget
# The name is actually arbitrary, but it is customary to use `gn` where n
# is an incrementing number.
def initialize(name)
@name = name
FileUtils.mkdir_p(File.join(path_prefix, STRINGS_SUFFIX))
end
def path_prefix()
File.join(CONFIGFS_USB, name)
end
def id_vendor=(value)
set_id("idVendor", value)
end
def id_product=(value)
set_id("idProduct", value)
end
def manufacturer=(value)
set_string("manufacturer", value)
end
def product=(value)
set_string("product", value)
end
def serial_number=(value)
set_string("serialnumber", value)
end
def set_id(kind, value)
value = ["0", value.sub(/^0x/, "")].join("x")
File.write(File.join(path_prefix, kind), value)
end
def set_string(name, value)
File.write(File.join(path_prefix, STRINGS_SUFFIX, name), value)
end
def activate!()
# TODO: explore more than one "config" in a gadget. (c.1)
# First, "document" features.
config_dir = File.join(path_prefix, "configs/c.1")
FileUtils.mkdir_p(File.join(config_dir, STRINGS_SUFFIX))
File.write(File.join(config_dir, STRINGS_SUFFIX, "configuration"), features.join(","))
# Then activate features.
features.each do |feature|
# We're using a "feature name -> function name" mapping to make a better
# end-user UX. They don't care *how* rndis is enabled, only that rndis
# has to be enabled.
# Though here we need to know *how* to enable e.g. rndis.
# This is why `function_name` exists, it's the gadgetfs function name
# for the required "logical" feature.
function_name = Configuration["boot"]["usb"]["functions"][feature]
function_dir = File.join(path_prefix, "functions", function_name)
feature_dir = File.join(config_dir, feature)
FileUtils.mkdir_p(function_dir)
File.symlink(function_dir, feature_dir)
quirk_name = function_name.gsub(/\./, "_").to_sym
Quirks.send(quirk_name) if Quirks.respond_to?(quirk_name)
end
# Then, attach to the USB driver.
if features.length > 0 then
# Equivalent to:
# (cd /sys/class/udc; echo *) > g1/UDC
# The idea is to give a controller name taken from the filesystem.
File.write(
File.join(path_prefix, "UDC"),
Dir.children("/sys/class/udc").first
)
end
end
end
end
# This task detects which gadget mode to use, and sets it up.
class Tasks::SetupGadgetMode < SingletonTask
ANDROID_USB = "/sys/class/android_usb"
def initialize()
add_dependency(:Mount, "/sys")
add_dependency(:Mount, System::ConfigFSUSB::CONFIGFS)
# If there's a `/vendor` mount point, it's likely that it's highly possible
# that it's going to be required for firmwares.
if Configuration["nixos"]["boot"]["specialFileSystems"]["/vendor"]
add_dependency(:Mount, "/vendor")
end
Targets[:SwitchRoot].add_dependency(:Task, self)
end
def run()
if File.exist?(System::ConfigFSUSB::CONFIGFS_USB)
run_configfs_usb
elsif File.exist?(ANDROID_USB)
run_androidusb
else
log("No way to configure USB Gadget found.")
end
end
def run_configfs_usb()
log("Configuring CONFIGFS USB Gadget.")
gadget = System::ConfigFSUSB::Gadget.new("g1")
gadget.id_vendor = Configuration["usb"]["idVendor"]
gadget.id_product = Configuration["usb"]["idProduct"]
gadget.product = Configuration["device"]["name"]
# FIXME : could this cause issues?
gadget.manufacturer = "Mobile NixOS"
gadget.serial_number = "0123456789"
gadget.features = Configuration["boot"]["usb"]["features"]
gadget.activate!
end
def run_android_usb()
log("Configuring ANDROID_USB Gadget.")
end
end