From: Alexander Couzens Date: Tue, 13 Aug 2024 11:05:13 +0000 (+0200) Subject: uqmid: add netifd protocol handler X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=58b9bd6629397715e53e165f0c2dd9091251cf07;p=project%2Fuqmi.git uqmid: add netifd protocol handler Allow to use /etc/config/network configuration similar to uqmi. The following qmi specific fields supported: * apn * username * password * pin * roaming Signed-off-by: Alexander Couzens --- diff --git a/uqmid/uqmid.hotplug.sh b/uqmid/uqmid.hotplug.sh new file mode 100644 index 0000000..5d8712c --- /dev/null +++ b/uqmid/uqmid.hotplug.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# based on wwan usbmisc +# install it to /etc/hotplug.d/usbmisc/01_uqmid.sh + +[ "$ACTION" = add ] || [ "$ACTION" = remove ] || exit 0 +[ "${DEVNAME/[0-9]/}" = cdc-wdm ] || exit 0 + +. /lib/functions.sh +. /lib/netifd/netifd-proto.sh + +find_uqmid_iface() { + local cfg="$1" + + local proto device + config_get proto "$cfg" proto + config_get device "$cfg" device + + [ "$proto" = uqmid ] || return 0 + [ "$device" = "/dev/$DEVNAME" ] || return 0 + if [ "$ACTION" = add ]; then + proto_set_available "$cfg" 1 + fi + if [ "$ACTION" = remove ]; then + proto_set_available "$cfg" 0 + fi + exit 0 +} + +config_load network +config_foreach find_uqmid_iface interface diff --git a/uqmid/uqmid.proto.sh b/uqmid/uqmid.proto.sh new file mode 100755 index 0000000..7b5a124 --- /dev/null +++ b/uqmid/uqmid.proto.sh @@ -0,0 +1,349 @@ +#!/bin/sh + +[ -n "$INCLUDE_ONLY" ] || { + . /lib/functions.sh + . ../netifd-proto.sh + init_proto "$@" +} + +_qdebug() { + logger -t qmi "$@" +} + +PROTO_OPTIONS="apn username password pin roaming" +proto_qmid_init_config() { + available=1 + no_device=1 + proto_config_add_string "device:device" + proto_config_add_string apn + proto_config_add_string username + proto_config_add_string password + proto_config_add_string pin + proto_config_add_string roaming + proto_config_add_defaults +} + +# convert /dev/cdc-wdm0 -> modem0 +_qmid_convert_devtoname() { + local device="$1" + + if echo "${device}" | grep -q "/dev/cdc-wdm"; then + echo "${device/\/dev\/cdc-wdm/modem}" + else + false + fi +} + +# check if uqmid already knows the device +_qmi_device_present() { + ubus list "uqmid.modem.$1" 2>/dev/null >/dev/null +} + +_qmi_ensure_device_present() { + local name="$1" + local device="$2" + + if _qmi_device_present "$name"; then + return 0 + fi + + ubus call uqmid add_modem "{'name':'$name','device':'$device'}" + _qmi_device_present "$name" +} + +_qmi_poll_state() { + local name=$1 + local timeout=$2 + + # in theory we should have multiple timeouts + # we should only have a long timeout if we reach netsearch + for _i in $(seq 1 "$timeout"); do + json_init + json_load "$(ubus call "uqmid.modem.$name" dump)" + json_get_var state_name state_name + case "$state_name" in + FAILED) + false + return + ;; + LIVE) + true + return + ;; + *) + # unknown state + sleep 1 + ;; + esac + done +} + +_qmi_poll_sim_state() { + local name=$1 + local timeout=$2 + + for _i in $(seq 1 "$timeout"); do + json_init + json_load "$(ubus call "uqmid.modem.$name" dump)" + json_get_var sim_state_name sim_state_name + case "$sim_state_name" in + IDLE|"WAIT UIM PRESENT"|GET_INFO) + # normal wait + sleep 1 + ;; + READY) + true + return + ;; + CHV_PIN|CHV_PUK|FAILED) + false + return + ;; + *) + # unknown state + sleep 1 + ;; + esac + done +} + +proto_qmid_setup() { + local interface="$1" + local device apn + local $PROTO_DEFAULT_OPTIONS + local ip4table ip6table + local ip_6 ip_prefix_length gateway_6 dns1_6 dns2_6 + + json_get_vars device $PROTO_OPTIONS + json_get_vars ip4table ip6table $PROTO_DEFAULT_OPTIONS + + [ "$timeout" = "" ] && timeout="10" + + [ "$metric" = "" ] && metric="0" + + [ -n "$ctl_device" ] && device=$ctl_device + + _qdebug "Setting up interface $interface" + + [ -n "$device" ] || { + _qdebug "No control device specified" + proto_notify_error "$interface" NO_DEVICE + proto_set_available "$interface" + return 1 + } + + [ -n "$delay" ] && sleep "$delay" + + device="$(readlink -f "$device")" + [ -c "$device" ] || { + _qdebug "The specified control device does not exist" + proto_notify_error "$interface" NO_DEVICE + return 1 + } + + devname="$(basename "$device")" + devpath="$(readlink -f "/sys/class/usbmisc/$devname/device/")" + ifname="$( ls "$devpath"/net )" + [ -n "$ifname" ] || { + _qdebug "The interface could not be found." + proto_notify_error "$interface" NO_IFACE + return 1 + } + + # check if uqmi already knows the device + [ -z "$name" ] && name=$(_qmid_convert_devtoname "$device") + + if [ -z "$name" ]; then + _qdebug "Name not set and can't derived from device $device." + proto_notify_error "$interface" NO_NAME + return 1 + fi + + if ! _qmi_ensure_device_present "$name" "$device"; then + # can't create a device + _qdebug "Can't ensure the device" + proto_notify_error "$interface" NO_IFACE_CREATABLE + return 1 + fi + + # pass configuration to it + ubus call "uqmid.modem.$name" configure "{'apn':'$apn', 'username': '$username', 'password': '$password', 'pin': '$pin', 'roaming':'$roaming'}" + + _qmi_poll_sim_state "$name" 30 + + # check if simcard is fine + json_init + json_load "$(ubus call "uqmid.modem.$name" dump)" + json_get_var state_name state_name + json_get_var sim_state_name sim_state_name + # use simstate as human readable to have more stable "api" + + case "$sim_state_name" in + IDLE | "WAIT UIM PRESENT" | GET_INFO) + _qdebug "SIM is still not ready after 30 seconds. Failing!" + proto_notify_error "$interface" SIM_NOT_READY + return 1 + ;; + CHV_PIN) + # TODO add support for pincode/unlock + _qdebug "SIM REQUIRED PIN! Failing!" + proto_notify_error "$interface" SIM_PIN_REQUIRED + proto_block_restart "$interface" + return 1 + ;; + CHV_PUK) + _qdebug "SIM REQUIRED PUK! Failing!" + proto_notify_error "$interface" SIM_PUK_REQUIRED + proto_block_restart "$interface" + return 1 + ;; + READY) + # continue to process + _qdebug "SIM is READY" + ;; + FAILED) + _qdebug "SIM is in FAILED state. Failing!" + # blocked state + proto_notify_error "$interface" SIM_FAILED + proto_block_restart "$interface" + return 1 + ;; + *) + _qdebug "UNKNOWN STATE $sim_state_name" + ubus call "uqmid.modem.$name" dump >> /tmp/qmi.log + # unknown sim state + proto_notify_error "$interface" SIM_STATE_UNKNOWN + return 1 + ;; + esac + + # poll here again for a state + # TODO: until it reaches LIVE or the FSM terminates in a failure mode + _qmi_poll_state "$name" 30 + + json_init + json_load "$(ubus call "uqmid.modem.$name" dump)" + json_get_var state_name state_name + case "$state_name" in + LIVE) + # found our correct state + ;; + *) + _qdebug "Modem $interface didn't entered LIVE. Instead modem is in state $state_name" + proto_notify_error "$interface" NOT_READY_YET + return 1 + ;; + esac + + json_get_var ipv4_addr ipv4_addr + json_get_var ipv4_netmask ipv4_netmask + json_get_var ipv4_gateway ipv4_gateway + json_get_var dns1 dns1 + json_get_var dns2 dns2 + + proto_init_update "$ifname" 1 + proto_set_keep 1 + + proto_add_ipv4_address "$ipv4_addr" "$ipv4_netmask" + proto_add_ipv4_route "$ipv4_gateway" "32" + [ "$defaultroute" = 0 ] || proto_add_ipv4_route "0.0.0.0" 0 "$gateway_4" + + [ "$peerdns" = 0 ] || { + [ -n "$dns1" ] && proto_add_dns_server "$dns1" + [ -n "$dns2" ] && proto_add_dns_server "$dns2" + } + [ -n "$zone" ] && { + proto_add_data + json_add_string zone "$zone" + proto_close_data + } + proto_send_update "$interface" + + ## state: + ## - last signal strength <- last, dump from internal state, not query it + ## - last network state + ## - + + # TODO: check if SIM is initialized + # echo "SIM not initialized" + # proto_notify_error "$interface" SIM_NOT_INITIALIZED + # proto_block_restart "$interface" + # return 1 + + # Check if UIM application is stuck in illegal state + # TODO: proto_notify_error "$interface" NETWORK_REGISTRATION_FAILED + ## proto_init_update "$ifname" 1 + ## proto_set_keep 1 + ## proto_add_data + ## [ -n "$pdh_4" ] && { + ## json_add_string "cid_4" "$cid_4" + ## json_add_string "pdh_4" "$pdh_4" + ## } + ## [ -n "$pdh_6" ] && { + ## json_add_string "cid_6" "$cid_6" + ## json_add_string "pdh_6" "$pdh_6" + ## } + ## proto_close_data + ## proto_send_update "$interface" + + # if [ -z "$dhcpv6" -o "$dhcpv6" = 0 ]; then + # json_load "$(uqmi -s -d $device -t 1000 --set-client-id wds,$cid_6 --get-current-settings)" + # json_select ipv6 + # json_get_var ip_6 ip + # json_get_var gateway_6 gateway + # json_get_var dns1_6 dns1 + # json_get_var dns2_6 dns2 + # json_get_var ip_prefix_length ip-prefix-length + # + # proto_init_update "$ifname" 1 + # proto_set_keep 1 + # proto_add_ipv6_address "$ip_6" "128" + # proto_add_ipv6_prefix "${ip_6}/${ip_prefix_length}" + # proto_add_ipv6_route "$gateway_6" "128" + # [ "$defaultroute" = 0 ] || proto_add_ipv6_route "::0" 0 "$gateway_6" "" "" "${ip_6}/${ip_prefix_length}" + # [ "$peerdns" = 0 ] || { + # proto_add_dns_server "$dns1_6" + # proto_add_dns_server "$dns2_6" + # } + # [ -n "$zone" ] && { + # proto_add_data + # json_add_string zone "$zone" + # proto_close_data + # } + # proto_send_update "$interface" + # else + # json_init + # json_add_string name "${interface}_6" + # json_add_string ifname "@$interface" + # [ "$pdptype" = "ipv4v6" ] && json_add_string iface_464xlat "0" + # json_add_string proto "dhcpv6" + # [ -n "$ip6table" ] && json_add_string ip6table "$ip6table" + # proto_add_dynamic_defaults + # # RFC 7278: Extend an IPv6 /64 Prefix to LAN + # json_add_string extendprefix 1 + # [ -n "$zone" ] && json_add_string zone "$zone" + # json_close_object + # ubus call network add_dynamic "$(json_dump)" + # fi +} + +proto_qmid_teardown() { + local interface="$1" + local device + + json_get_vars device + + [ -n "$ctl_device" ] && device=$ctl_device + [ -z "$name" ] && name=$(_qmid_convert_devtoname "$device") + + echo "Stopping network $interface" + + ubus call uqmid remove_modem "{ 'name': '$name' }" + + proto_init_update "*" 0 + proto_send_update "$interface" +} + +[ -n "$INCLUDE_ONLY" ] || { + add_protocol qmid +}