-
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support modem discovery via automatic modem implementation
- Loading branch information
1 parent
c4856bc
commit f0bae8c
Showing
4 changed files
with
222 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
defmodule VintageNetMobile.Modem.Automatic do | ||
@moduledoc """ | ||
Modem for automatic detection and configuration of USB-based modems | ||
This is useful for when you need to support different types of modems and you | ||
cannot provide a static configuration for one particular modem. | ||
""" | ||
@behaviour VintageNetMobile.Modem | ||
|
||
alias VintageNet.Interface.RawConfig | ||
alias VintageNetMobile.Modem.Automatic.Discovery | ||
|
||
@impl VintageNetMobile.Modem | ||
def normalize(config), do: config | ||
|
||
@impl VintageNetMobile.Modem | ||
def add_raw_config(raw_config, %{vintage_net_mobile: _mobile}, _opts) do | ||
child_specs = [ | ||
{Discovery, [raw_config: raw_config]} | ||
] | ||
|
||
%RawConfig{ | ||
raw_config | ||
| child_specs: child_specs | ||
} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
defmodule VintageNetMobile.Modem.Automatic.Discovery do | ||
@moduledoc false | ||
|
||
# A process for trying to discover a the type of modem and some basic | ||
# information to allow dynamic configuration items | ||
|
||
use GenServer | ||
|
||
alias Circuits.UART | ||
|
||
# the poll time for trying to enumerate the UARTS | ||
@enumeration_poll 3_000 | ||
|
||
# To discover a which modem to use this process looks at the vendor ids and | ||
# manufacturer id to map to a `VintageNetMobile.Modem` implementation. | ||
|
||
# A source to find vendor and manufacturer ids is | ||
# https://elixir.bootlin.com/linux/latest/source/drivers/usb/serial/option.c#L245 | ||
|
||
@quectel_vender_id 0x2C7C | ||
@telit_vendor_id 0x1BC7 | ||
|
||
@modems %{ | ||
{@telit_vendor_id, 0x1201} => VintageNetMobile.Modem.TelitLE910, | ||
{@quectel_vender_id, 0x0125} => VintageNetMobile.Modem.QuectelEC25, | ||
# map the EC21 to the EC25 as they work the same | ||
{@quectel_vender_id, 0x0121} => VintageNetMobile.Modem.QuectelEC25, | ||
{@quectel_vender_id, 0x0296} => VintageNetMobile.Modem.QuectelBG96 | ||
} | ||
|
||
@typedoc """ | ||
Init args | ||
- `:raw_config` - The RawConfig that contains configuration information to | ||
pass the discovered modem | ||
""" | ||
@type init_arg() :: {:raw_config, VintageNet.Interface.RawConfig.t()} | ||
|
||
@doc """ | ||
Start the discovery server | ||
""" | ||
@spec start_link([init_arg()]) :: GenServer.on_start() | ||
def start_link(args) do | ||
GenServer.start_link(__MODULE__, args, name: __MODULE__) | ||
end | ||
|
||
@impl GenServer | ||
def init(args) do | ||
{:ok, %{modem: nil, raw_config: args[:raw_config]}, {:continue, :detect_modem}} | ||
end | ||
|
||
@impl GenServer | ||
def handle_continue(:detect_modem, state) do | ||
{:noreply, do_detect_modem(state)} | ||
end | ||
|
||
@impl GenServer | ||
def handle_info(:detect_modem, state) do | ||
{:noreply, do_detect_modem(state)} | ||
end | ||
|
||
defp do_detect_modem(state) do | ||
case detect_modem() do | ||
nil -> | ||
Process.send_after(self(), :detect_modem, @enumeration_poll) | ||
state | ||
|
||
modem -> | ||
state = %{state | modem: modem} | ||
:ok = configure_vintage_net(state) | ||
|
||
state | ||
end | ||
end | ||
|
||
defp configure_vintage_net(state) do | ||
config = state.raw_config.source_config.vintage_net_mobile |> Map.put(:modem, state.modem) | ||
|
||
VintageNet.configure( | ||
state.raw_config.ifname, | ||
%{type: VintageNetMobile, vintage_net_mobile: config}, | ||
persist: false | ||
) | ||
end | ||
|
||
defp detect_modem() do | ||
UART.enumerate() | ||
|> Enum.find_value(&known_modem/1) | ||
end | ||
|
||
defp known_modem({_tty, %{vendor_id: vid, product_id: pid}}) do | ||
Map.get(@modems, {vid, pid}) | ||
end | ||
|
||
defp known_modem(_), do: nil | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
defmodule VintageNetMobile.Modem.AutomaticTest do | ||
use ExUnit.Case, async: true | ||
|
||
alias VintageNetMobile.Modem.Automatic | ||
alias VintageNetMobile.Modem.Automatic.Discovery | ||
alias VintageNet.Interface.RawConfig | ||
|
||
test "create LTE configuration" do | ||
input = %{ | ||
type: VintageNetMobile, | ||
vintage_net_mobile: %{ | ||
modem: Automatic, | ||
service_providers: [%{apn: "choosethislteitissafe"}, %{apn: "wireless.twilio.com"}] | ||
} | ||
} | ||
|
||
# RawConfig that will be passed to the discovered modem | ||
base_raw_config = %RawConfig{ | ||
ifname: "ppp0", | ||
type: VintageNetMobile, | ||
source_config: input, | ||
required_ifnames: ["wwan0"], | ||
up_cmds: [], | ||
down_cmds: [], | ||
files: [], | ||
child_specs: [] | ||
} | ||
|
||
output = %RawConfig{ | ||
ifname: "ppp0", | ||
type: VintageNetMobile, | ||
source_config: input, | ||
required_ifnames: ["wwan0"], | ||
up_cmds: [ | ||
{:run_ignore_errors, "mknod", ["/dev/ppp", "c", "108", "0"]} | ||
], | ||
down_cmds: [ | ||
{:fun, VintageNet.PropertyTable, :clear_prefix, | ||
[VintageNet, ["interface", "ppp0", "mobile"]]} | ||
], | ||
files: [], | ||
child_specs: [ | ||
{Discovery, [raw_config: base_raw_config]} | ||
] | ||
} | ||
|
||
assert output == VintageNetMobile.to_raw_config("ppp0", input, Utils.default_opts()) | ||
end | ||
end |