defmodule Eqmi do use GenServer @moduledoc """ Documentation for `Eqmi`. """ @sender_types %{0 => :control_point, 128 => :service} @service_types %{ 0 => :qmi_ctl, 1 => :qmi_wds, 2 => :qmi_dms, 3 => :qmi_nas, 4 => :qmi_qos, 5 => :qmi_wms, 6 => :qmi_pds, 9 => :qmi_voice, 10 => :qmi_cat, 11 => :qmi_uim, 12 => :qmi_pbm, 14 => :qmi_cat, 16 => :qmi_loc, 17 => :qmi_sar, 20 => :qmi_wda, 226 => :qmi_oma, 231 => :qmi_gms, 232 => :qmi_gas } @message_types %{0 => :request, 2 => :response, 4 => :indication} def get_sender_type(type_id) do @sender_types[type_id] end def get_service_type(svc_id) do @service_types[svc_id] end def get_message_type(msg_id) do @message_types[msg_id] end def message_type_id(msg_name) do @message_types |> get_id(msg_name) end def sender_type_id(msg_name) do @sender_types |> get_id(msg_name) end def service_type_id(msg_name) do @service_types |> get_id(msg_name) end defp get_id(types, name) do types |> Enum.find(fn {_key, val} -> val == name end) |> elem(0) end # to be deleted def qmux_message(header, payload) do if_type = <<1::little-unsigned-integer-size(8)>> [if_type, header, payload] |> :erlang.list_to_binary() end @ctl_id 0 defmodule ClientState do @moduledoc false defstruct type: nil, current_tx: 0, id: 0, pid: nil end def start_link(dev, opts \\ []) do GenServer.start_link(__MODULE__, dev, opts) end def stop(pid, reason \\ :shutdown, timeout \\ :infinity) do GenServer.stop(pid, reason, timeout) end def client(pid, type) do {:ok, ctl} = GenServer.call(pid, :get_ctl) case Eqmi.Control.allocate_cid(ctl, type) do {:ok, cid} -> GenServer.call(pid, {:new_client, type, cid}) _ -> {:error, "allocating control point"} end end def release_client(pid, ref) do with {:ok, ctl} = GenServer.call(pid, :get_ctl), {:ok, client} = GenServer.call(pid, {:get_client, ref}) do Eqmi.Control.release_cid(ctl, client.type, client.id) end GenServer.call(pid, {:release, ref}) end def qmux_message(pid, client_ref, ctrl_flag, service, messages) do GenServer.call(pid, {:qmux_message, client_ref, ctrl_flag, service, messages}) end def send_message(pid, ref, msg) do GenServer.call(pid, {:send_msg, ref, msg}) end def send_raw(pid, msg) do GenServer.call(pid, {:send_raw, msg}) end def init(device) do options = [:write, :raw] case File.open(device, options) do {:ok, dev} -> {:ok, reader} = Eqmi.Reader.start_link(self(), device) {:ok, ctl} = Eqmi.Control.start_link(self()) {:ok, %{ reader: reader, device: dev, control_points: %{}, clients: %{:qmi_ctl => %{@ctl_id => ctl}}, ctl: ctl }} {:error, reason} -> {:stop, reason} end end defp qmux_sdu(msg_type, transaction_id, messages) do msg_id = Eqmi.message_type_id(msg_type) ctrl_flag = <> tx_id = <> [ctrl_flag, tx_id, messages] |> :erlang.list_to_binary() end defp gen_tx_id(:request, id) do id + 1 end defp gen_tx_id(_, id) do id end def handle_call(:get_ctl, _from, s) do {:reply, {:ok, s.ctl}, s} end def handle_call({:new_client, type, cid}, from, %{clients: clients, control_points: ctrls} = s) do {pid, _} = from client_state = %ClientState{type: type, id: cid, current_tx: 0, pid: pid} ref = :erlang.make_ref() clients_ids = clients |> Map.get(type, %{}) |> Map.put(cid, pid) new_clients = Map.put(clients, type, clients_ids) new_ctrls = Map.put(ctrls, ref, client_state) state = %{s | clients: new_clients, control_points: new_ctrls} {:reply, ref, state} end # falta borrar def handle_call({:release, ref}, _from, %{clients: clients, control_points: controls} = s) do control_point = Map.get(controls, ref) client_list = Map.get(clients, control_point.type) new_controls = Map.delete(controls, ref) new_client_list = Map.delete(client_list, control_point.id) new_clients = Map.put(clients, control_point.type, new_client_list) new_state = %{s | clients: new_clients, control_points: new_controls} {:reply, :ok, new_state} end def handle_call({:get_client, ref}, _from, %{control_points: controls} = s) do client = Map.get(controls, ref) if client != nil do {:reply, {:ok, client}, s} else {:reply, {:error, "control_point not found"}, s} end end def handle_call( {:qmux_message, client_ref, ctrl_flag, service, messages}, _from, %{clients: clients} = s ) do result = Map.get(clients, client_ref) case result do {:ok, client} -> tx_id = gen_tx_id(ctrl_flag, client.current_tx) payload = qmux_sdu(ctrl_flag, tx_id, messages) header = Eqmi.QmuxHeader.new(client.type, client.id, service, byte_size(payload)) if_type = <<1::little-unsigned-integer-size(8)>> msg = [if_type, header, payload] |> :erlang.list_to_binary() updated_client = %{client | current_tx: tx_id} new_clients = Map.put(clients, client_ref, updated_client) state = %{s | clients: new_clients} {:reply, msg, state} _ -> {:reply, :error, s} end end def handle_call( {:send_msg, ref, msg}, _from, %{device: dev, control_points: controls} = s ) do client = Map.get(controls, ref) if client != nil do tx_id = client.current_tx + 1 payload = qmux_sdu(client.type, :request, tx_id, msg) header = Eqmi.QmuxHeader.new(:control_point, client.id, client.type, byte_size(payload)) qmux_msg = Eqmi.qmux_message(header, payload) new_client = %ClientState{client | current_tx: tx_id} new_ctrls = Map.put(controls, ref, new_client) res = IO.binwrite(dev, qmux_msg) {:reply, res, %{s | control_points: new_ctrls}} else {:reply, {:error, "control_point not found"}, s} end end def handle_call({:send_raw, msg}, _from, %{device: dev} = s) do res = IO.binwrite(dev, msg) {:reply, res, s} end def handle_info({:qmux, header, messages}, %{clients: clients} = s) do msg = process_service(header, messages) client = find_client(clients, header.service_type, header.client_id) if client != nil do send(client, {:qmux, msg}) end {:noreply, s} end def handle_info(msg, s) do IO.inspect(msg, label: "info in server") {:noreply, s} end def terminate(_reason, s) do File.close(s.device) end defp find_client(clients, service_type, client_id) do clients |> Map.get(service_type, %{}) |> Map.get(client_id) end defp process_service(%{service_type: :qmi_ctl} = payload, messages) do Eqmi.CTL.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_wds} = payload, messages) do Eqmi.WDS.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_dms} = payload, messages) do Eqmi.DMS.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_nas} = payload, messages) do Eqmi.NAS.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_qos} = payload, messages) do Eqmi.QOS.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_wms} = payload, messages) do Eqmi.WMS.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_pds} = payload, messages) do Eqmi.PDS.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_voice} = payload, messages) do Eqmi.VOICE.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_pbm} = payload, messages) do Eqmi.PBM.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_UIM} = payload, messages) do Eqmi.UIM.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_loc} = payload, messages) do Eqmi.LOC.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_sar} = payload, messages) do Eqmi.SAR.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_wda} = payload, messages) do Eqmi.WDA.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_oma} = payload, messages) do Eqmi.OMA.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_gms} = payload, messages) do Eqmi.GMS.process_qmux_sdu(messages, payload) end defp process_service(%{service_type: :qmi_gas} = payload, messages) do Eqmi.GAS.process_qmux_sdu(messages, payload) end defp qmux_sdu(:qmi_ctl, msg_type, tx_id, messages) do Eqmi.CTL.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_wds, msg_type, tx_id, messages) do Eqmi.WDS.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_dms, msg_type, tx_id, messages) do Eqmi.DMS.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_nas, msg_type, tx_id, messages) do Eqmi.NAS.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_qos, msg_type, tx_id, messages) do Eqmi.QOS.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_wms, msg_type, tx_id, messages) do Eqmi.WMS.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_pds, msg_type, tx_id, messages) do Eqmi.PDS.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_voice, msg_type, tx_id, messages) do Eqmi.VOICE.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_pbm, msg_type, tx_id, messages) do Eqmi.PBM.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_UIM, msg_type, tx_id, messages) do Eqmi.UIM.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_loc, msg_type, tx_id, messages) do Eqmi.LOC.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_sar, msg_type, tx_id, messages) do Eqmi.SAR.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_wda, msg_type, tx_id, messages) do Eqmi.WDA.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_oma, msg_type, tx_id, messages) do Eqmi.OMA.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_gms, msg_type, tx_id, messages) do Eqmi.GMS.qmux_sdu(msg_type, tx_id, messages) end defp qmux_sdu(:qmi_gas, msg_type, tx_id, messages) do Eqmi.GAS.qmux_sdu(msg_type, tx_id, messages) end end