2 # Copyright (C) 2016 Velocloud Inc
3 # Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
5 ################################################################################
8 .
/lib
/netifd
/netifd-proto.sh
10 ################################################################################
13 MODEMMANAGER_RUNDIR
="/var/run/modemmanager"
14 MODEMMANAGER_PID_FILE
="${MODEMMANAGER_RUNDIR}/modemmanager.pid"
15 MODEMMANAGER_CDCWDM_CACHE
="${MODEMMANAGER_RUNDIR}/cdcwdm.cache"
16 MODEMMANAGER_SYSFS_CACHE
="${MODEMMANAGER_RUNDIR}/sysfs.cache"
17 MODEMMANAGER_EVENTS_CACHE
="${MODEMMANAGER_RUNDIR}/events.cache"
19 ################################################################################
23 local level
="$1"; shift
24 logger
-p "daemon.${level}" -t "ModemManager[$$]" "hotplug: $*"
27 ################################################################################
28 # Receives as input argument the full sysfs path of the device
29 # Returns the physical device sysfs path
31 # NOTE: this method only works when the device exists, i.e. it cannot be used
32 # on removal hotplug events
34 mm_find_physdev_sysfs_path
() {
38 tmp_path
=$
(dirname "${tmp_path}")
40 # avoid infinite loops iterating
41 [ -z "${tmp_path}" ] ||
[ "${tmp_path}" = "/" ] && return
43 # For USB devices, the physical device will be that with a idVendor
44 # and idProduct pair of files
45 [ -f "${tmp_path}"/idVendor
] && [ -f "${tmp_path}"/idProduct
] && {
46 tmp_path
=$
(readlink
-f "$tmp_path")
51 # For PCI devices, the physical device will be that with a vendor
52 # and device pair of files
53 [ -f "${tmp_path}"/vendor
] && [ -f "${tmp_path}"/device
] && {
54 tmp_path
=$
(readlink
-f "$tmp_path")
61 ################################################################################
63 # Returns the cdc-wdm name retrieved from sysfs
68 cdcwdm
=$
(ls "/sys/class/net/${wwan}/device/usbmisc/")
69 [ -n "${cdcwdm}" ] ||
return
71 # We have to cache it for later, as we won't be able to get the
72 # associated cdc-wdm device on a remove event
73 echo "${wwan} ${cdcwdm}" >> "${MODEMMANAGER_CDCWDM_CACHE}"
78 # Returns the cdc-wdm name retrieved from the cache
83 # Look for the cached associated cdc-wdm device
84 [ -f "${MODEMMANAGER_CDCWDM_CACHE}" ] ||
return
86 cdcwdm
=$
(awk -v wwan
="${wwan}" '!/^#/ && $0 ~ wwan { print $2 }' "${MODEMMANAGER_CDCWDM_CACHE}")
87 [ -n "${cdcwdm}" ] ||
return
90 sed -i "/${wwan} ${cdcwdm}/d" "${MODEMMANAGER_CDCWDM_CACHE}"
95 ################################################################################
96 # ModemManager needs some time from the ports being added until a modem object
97 # is exposed in DBus. With the logic here we do an explicit wait of N seconds
98 # for ModemManager to expose the new modem object, making sure that the wait is
99 # unique per device (i.e. per physical device sysfs path).
101 # Gets the modem wait status as retrieved from the cache
102 mm_get_modem_wait_status
() {
105 # If no sysfs cache file, we're done
106 [ -f "${MODEMMANAGER_SYSFS_CACHE}" ] ||
return
108 # Get status of the sysfs path
109 awk -v sysfspath
="${sysfspath}" '!/^#/ && $0 ~ sysfspath { print $2 }' "${MODEMMANAGER_SYSFS_CACHE}"
112 # Clear the modem wait status from the cache, if any
113 mm_clear_modem_wait_status
() {
116 local escaped_sysfspath
118 [ -f "${MODEMMANAGER_SYSFS_CACHE}" ] && {
119 # escape '/', '\' and '&' for sed...
120 escaped_sysfspath
=$
(echo "$sysfspath" |
sed -e 's/[\/&]/\\&/g')
121 sed -i "/${escaped_sysfspath}/d" "${MODEMMANAGER_SYSFS_CACHE}"
125 # Sets the modem wait status in the cache
126 mm_set_modem_wait_status
() {
130 # Remove sysfs line before adding the new one with the new state
131 mm_clear_modem_wait_status
"${sysfspath}"
134 echo "${sysfspath} ${status}" >> "${MODEMMANAGER_SYSFS_CACHE}"
137 # Callback for config_foreach()
138 mm_get_modem_config_foreach_cb
() {
143 config_get proto
"${cfg}" proto
144 [ "${proto}" = modemmanager
] ||
return 0
147 dev
=$
(uci_get network
"${cfg}" device
)
148 [ "${dev}" = "${sysfspath}" ] ||
return 0
153 # Returns the name of the interface configured for this device
154 mm_get_modem_config
() {
157 # Look for configuration for the given sysfs path
159 config_foreach mm_get_modem_config_foreach_cb interface
"${sysfspath}"
162 # Wait for a modem in the specified sysfspath
163 mm_wait_for_modem
() {
167 # TODO: config max wait
171 while [ $n -ge 0 ]; do
172 [ -d "${sysfspath}" ] ||
{
173 mm_log
"error" "ignoring modem detection request: no device at ${sysfspath}"
174 proto_set_available
"${cfg}" 0
178 # Check if the modem exists at the given sysfs path
179 if ! mmcli
-m "${sysfspath}" > /dev
/null
2>&1
181 mm_log
"error" "modem not detected at sysfs path"
183 mm_log
"info" "modem exported successfully at ${sysfspath}"
184 mm_log
"info" "setting interface '${cfg}' as available"
185 proto_set_available
"${cfg}" 1
193 mm_log
"error" "timed out waiting for the modem to get exported at ${sysfspath}"
194 proto_set_available
"${cfg}" 0
198 mm_report_modem_wait
() {
201 local parent_sysfspath status
203 parent_sysfspath
=$
(mm_find_physdev_sysfs_path
"$sysfspath")
204 [ -n "${parent_sysfspath}" ] ||
{
205 mm_log
"error" "parent device sysfspath not found"
209 status
=$
(mm_get_modem_wait_status
"${parent_sysfspath}")
214 cfg
=$
(mm_get_modem_config
"${parent_sysfspath}")
215 if [ -n "${cfg}" ]; then
216 mm_log
"info" "interface '${cfg}' is set to configure device '${parent_sysfspath}'"
217 mm_log
"info" "now waiting for modem at sysfs path ${parent_sysfspath}"
218 mm_set_modem_wait_status
"${parent_sysfspath}" "processed"
219 # Launch subshell for the explicit wait
220 ( mm_wait_for_modem
"${cfg}" "${parent_sysfspath}" ) > /dev
/null
2>&1 &
222 mm_log
"info" "no need to wait for modem at sysfs path ${parent_sysfspath}"
223 mm_set_modem_wait_status
"${parent_sysfspath}" "ignored"
227 mm_log
"info" "already waiting for modem at sysfs path ${parent_sysfspath}"
232 mm_log
"error" "unknown status read for device at sysfs path ${parent_sysfspath}"
237 ################################################################################
240 mm_cleanup_interface_cb
() {
244 config_get proto
"${cfg}" proto
245 [ "${proto}" = modemmanager
] ||
return 0
247 proto_set_available
"${cfg}" 0
250 mm_cleanup_interfaces
() {
252 config_foreach mm_cleanup_interface_cb interface
255 mm_cleanup_interface_by_sysfspath
() {
259 cfg
=$
(mm_get_modem_config
"$dev")
260 [ -n "${cfg}" ] ||
return
262 mm_log
"info" "setting interface '$cfg' as unavailable"
263 proto_set_available
"${cfg}" 0
266 ################################################################################
269 # Receives as input the action, the device name and the subsystem
276 # Track/untrack events in cache
279 # On add events, store event details in cache (if not exists yet)
280 grep -qs "${name},${subsystem}" "${MODEMMANAGER_EVENTS_CACHE}" || \
281 echo "${action},${name},${subsystem},${sysfspath}" >> "${MODEMMANAGER_EVENTS_CACHE}"
284 # On remove events, remove old events from cache (match by subsystem+name)
285 sed -i "/${name},${subsystem}/d" "${MODEMMANAGER_EVENTS_CACHE}"
290 mm_log
"debug" "event reported: action=${action}, name=${name}, subsystem=${subsystem}"
291 mmcli
--report-kernel-event="action=${action},name=${name},subsystem=${subsystem}" 1>/dev
/null
2>&1 &
293 # Wait for added modem if a sysfspath is given
294 [ -n "${sysfspath}" ] && [ "$action" = "add" ] && mm_report_modem_wait
"${sysfspath}"
297 mm_report_event_from_cache_line
() {
298 local event_line
="$1"
300 local action name subsystem sysfspath
301 action
=$
(echo "${event_line}" |
awk -F ',' '{ print $1 }')
302 name
=$
(echo "${event_line}" |
awk -F ',' '{ print $2 }')
303 subsystem
=$
(echo "${event_line}" |
awk -F ',' '{ print $3 }')
304 sysfspath
=$
(echo "${event_line}" |
awk -F ',' '{ print $4 }')
306 mm_log
"debug" "cached event found: action=${action}, name=${name}, subsystem=${subsystem}, sysfspath=${sysfspath}"
307 mm_report_event
"${action}" "${name}" "${subsystem}" "${sysfspath}"
310 mm_report_events_from_cache
() {
311 # Remove the sysfs cache
312 rm -f "${MODEMMANAGER_SYSFS_CACHE}"
318 # Wait for ModemManager to be available in the bus
319 while [ $n -ge 0 ]; do
321 mm_log
"info" "checking if ModemManager is available..."
323 if ! mmcli
-L >/dev
/null
2>&1
325 mm_log
"info" "ModemManager not yet available"
333 [ ${mmrunning} -eq 1 ] ||
{
334 mm_log
"error" "couldn't report initial kernel events: ModemManager not running"
338 # Report cached kernel events
339 while IFS
= read -r event_line
; do
340 mm_report_event_from_cache_line
"${event_line}"
341 done < ${MODEMMANAGER_EVENTS_CACHE}