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:
parent
b47ffc14e4
commit
50a04bd651
@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
147
modules/stage-1/tasks/usb-gadget-task.rb
Normal file
147
modules/stage-1/tasks/usb-gadget-task.rb
Normal 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
|
Loading…
Reference in New Issue
Block a user