nerves-networking / vintage_net_mobile

Mobile networking for VintageNet
Apache License 2.0
27 stars 11 forks source link

Add a modem lookup table #19

Closed mattludwigs closed 4 years ago

mattludwigs commented 4 years ago

This allows more flexibility in terms of modem specifications paired with the provider. Also, allows for users to add modem definitions to the lookup table for modems that are supported in VintageNetLTE.

fhunleth commented 4 years ago

@mobileoverlord and any other people lurking around here - could we get your modem configurations (model and service provider) and make sure that they fit in this abstraction? Or if you can't share, could you let us know if you think they'll fit?

LostKobrakai commented 4 years ago

The following is mine. Given my naivete I'm wondering though how related modems and providers actually are. Like if I switch from my huawei to e.g. the quictel one would I actually need to supply different values to be able to connect to my provider?

defmodule RentoryHub.Hardware.HuaweiE3372 do
  use GenServer
  @behaviour VintageNetLTE.Modem

  @hilink_id "12d1:1f01"
  @modem_id "12d1:155e"

  def start_link(init_arg) do
    GenServer.start_link(__MODULE__, init_arg)
  end

  @impl GenServer
  def init(_) do
    RentoryHub.PubSub.subscribe("usb/#{@hilink_id}")
    RentoryHub.PubSub.subscribe("usb/#{@modem_id}")
    {:ok, nil}
  end

  @impl GenServer
  def handle_info({RentoryHub.PubSub, "usb/12d1:1f01", %{state: availability}}, state) do
    case availability do
      :available -> Toolshed.cmd("usb_modeswitch -v 12d1 -p 1f01 -X")
      _ -> nil
    end

    {:noreply, state}
  end

  def handle_info({RentoryHub.PubSub, "usb/12d1:155e", %{state: availability}}, state) do
    case availability do
      :available ->
        ifname = "ppp0"

        config = %{
          type: VintageNetLTE,
          modem: RentoryHub.Hardware.HuaweiE3372,
          provider: %{apn: "web.vodafone.de", dial_tone: "*99#"}
        }

        VintageNet.configure(ifname, config, persist: false)

      _ ->
        nil
    end

    {:noreply, state}
  end

  def available do
    if File.exists?("/dev/ttyUSB0") do
      :ok
    else
      {:error, :modem_not_connected}
    end
  end

  @impl VintageNetLTE.Modem
  def spec(provider_info) do
    %{
      serial_port: "ttyUSB0",
      serial_speed: 115_200,
      chatscript: chatscript(provider_info),
      command_port: "ttyUSB0"
    }
  end

  defp chatscript(provider_info) do
    """
    # Exit execution if module receives any of the following strings:
    ABORT 'BUSY'
    ABORT 'NO CARRIER'
    ABORT 'NO DIALTONE'
    ABORT 'NO DIAL TONE'
    ABORT 'NO ANSWER'
    ABORT 'DELAYED'
    TIMEOUT 10
    REPORT CONNECT

    # Module will send the string AT regardless of the string it receives
    "" AT

    # Instructs the modem to disconnect from the line, terminating any call in progress. All of the functions of the command shall be completed before the modem returns a result code.
    OK ATH

    # Instructs the modem to set all parameters to the factory defaults.
    OK ATZ

    # Result codes are sent to the Data Terminal Equipment (DTE).
    OK ATQ0

    # Define PDP context
    OK AT+CGDCONT=1,"IP","#{provider_info.apn}"

    # ATDT = Attention Dial Tone
    OK ATDT#{provider_info.dial_tone}

    # Don't send any more strings when it receives the string CONNECT. Module considers the data links as having been set up.
    CONNECT ''
    """
  end
end
fhunleth commented 4 years ago

Thanks. Regarding modems and providers, we have an example where the service provider settings are not easily transferred between modems. Our hope is that that's an exception (or we learn of a way that this can be done). The goal of the lookup table is that it will first try to find a module that is specifically for a modem/provider combination. If that doesn't exist, then it should look up the modem by itself. In that second case, the module will need to look at the service provider settings and "do the right thing". And to re-iterate, hopefully nearly everything is the second case, but it gives a migration path if we don't know how to do it generically at first.

mattludwigs commented 4 years ago

Just to be clear right now we don't do that fallback. I was hoping to do that in a follow up PR to try to keep things small. I don't think that it will be much work, just mentally to be able separate that out would nice, and the follow up PR "should be" small. However, if there are reservations I can add support for that functionality here.

fhunleth commented 4 years ago

Good point. I don't think it's necessary to do the fall back in this PR, but given that it's planned to be the 99% case, it feels important to mention.