From 1d2d4cb343e6d8b290e801a7dc776f9fb0aa8999 Mon Sep 17 00:00:00 2001 From: John Klug Date: Fri, 3 Sep 2021 04:59:39 -0500 Subject: radio reset monitor feature and MTRV1-0.4 hardware --- io-module/radio_udev_discovery.c | 308 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 io-module/radio_udev_discovery.c (limited to 'io-module/radio_udev_discovery.c') diff --git a/io-module/radio_udev_discovery.c b/io-module/radio_udev_discovery.c new file mode 100644 index 0000000..eb6c1aa --- /dev/null +++ b/io-module/radio_udev_discovery.c @@ -0,0 +1,308 @@ +#include +#include +#include +#include "mts_io_module.h" +#include "radio_udev_discovery.h" + +/* + * radio-reset in the mts-io driver sets radio_udev_discovery to 0. + * Then it returns early if radio_udev_discovery is set to 1. The + * UDEV daemon is used to set radio_udev_discovery. Notifications + * are sent if radio_udev_discovery goes from 0 to 1. + */ + +int radio_udev_discovery = 1; + + +struct sig_pid_radio_reset_monitor_s { + pid_t pid; + int signal; + struct pid *vpid; +}; + +#define PID_MAX_COUNT 20 +static struct sig_pid_radio_reset_monitor_s sig_pid_radio_reset_monitor[PID_MAX_COUNT]; + + +/* Clear an entry in the table that no longer exists. + * unlocks the mts_io_lock, toggles the rcu_read_lock, + * then puts back the mts_io_lock. + */ +static int +verify_vpid(struct sig_pid_radio_reset_monitor_s *p) +{ + pid_t pid0 = p->pid; + struct pid *vpid1; + struct pid *vpid0 = p->vpid; + unsigned int vcount; + + // Check to see if vpid and pid still match + if (p->pid == 0) + return 0; + if (vpid0 == NULL) { + p->pid = 0; + return 0; + } + + mutex_unlock(&mts_io_mutex); + rcu_read_lock(); + vpid1 = find_vpid((pid_t)pid0); + if(vpid1 != NULL) + vcount = atomic_read(&vpid1->count); + else + vcount = 0; + rcu_read_unlock(); + mutex_lock(&mts_io_mutex); + if ((vpid1 != vpid0) || (vpid1 == NULL)) + p->pid = 0; + pr_debug("%s: verify_vpid: vpid0 0x%x vpid1 0x%x vcount %d for pid %d\n", + __FUNCTION__, + (unsigned int)vpid0, (unsigned int)vpid1, vcount, (int)pid0); + return p->pid; +} + +// Need to find existing entries and allow updates. Signal 0 removes a value. +ssize_t mts_attr_store_radio_reset_monitor(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + long pid0; + int sig; + int i; + int found; + struct pid *vpid1, *vpid0; + int retval; + + retval = sscanf(buf, "%ld %d", &pid0, &sig); + if (retval != 2) + return -EINVAL; + + if (pid0 < 0) + return -EINVAL; + + rcu_read_lock(); + vpid0 = find_vpid((pid_t)pid0); + rcu_read_unlock(); + pr_debug("%s: Try to store %ld %d 0x%x\n", + __FUNCTION__, + pid0,sig,(unsigned int)vpid0); + if (vpid0) { + /* Note that since we no longer hold the rcu lock, vpid + * could become invalid, unless it is ours. + */ + mutex_lock(&mts_io_mutex); + /* First see if we are already in the table -- search whole table */ + found = -1; + for (i=0; i < PID_MAX_COUNT; i++) { + if ((sig_pid_radio_reset_monitor[i].pid == (pid_t)pid0) && + (sig_pid_radio_reset_monitor[i].vpid == vpid0)) { + if (sig == 0) { + sig_pid_radio_reset_monitor[i].pid = 0; + sig_pid_radio_reset_monitor[i].vpid = NULL; + } else { + sig_pid_radio_reset_monitor[i].signal = sig; + mutex_unlock(&mts_io_mutex); + rcu_read_lock(); + vpid0 = find_vpid((pid_t)pid0); + rcu_read_unlock(); + pr_debug("%s: slot %d: pid %d, vpid 0x%x/vpid 0x%x\n", + __FUNCTION__, + i,pid0,vpid0,sig_pid_radio_reset_monitor[i].vpid); + mutex_lock(&mts_io_mutex); + if (vpid0 != sig_pid_radio_reset_monitor[i].vpid) { + sig_pid_radio_reset_monitor[i].pid = 0; /* Maybe exited? */ + sig_pid_radio_reset_monitor[i].vpid = NULL; + pr_debug("%s: mts-io: vpid mismatch pid %d, vpid 0x%x/0x%x, sig %d, cleared %d\n", + __FUNCTION__, + (int)sig_pid_radio_reset_monitor[i].pid, + (unsigned int)sig_pid_radio_reset_monitor[i].vpid,(unsigned int)vpid0, + (unsigned int)sig_pid_radio_reset_monitor[i].signal,i); + } else { + found = i; + pr_debug("%s: mts-io: found pid %d, vpid 0x%x, sig %d\n", + __FUNCTION__, + (int)sig_pid_radio_reset_monitor[i].pid, + (unsigned int)sig_pid_radio_reset_monitor[i].vpid, + (unsigned int)sig_pid_radio_reset_monitor[i].signal); + sig = 0; // Clear any more that we find. + } + + } + } // Location matches our pid + } // Loop through table + + pr_debug("%s: found=%d, sig=%d\n",__FUNCTION__,sig); + if (found > -1) { + mutex_unlock(&mts_io_mutex); + return count; + } + + if (sig == 0) { + // Nothing to clear out. + mutex_unlock(&mts_io_mutex); + return count; + } + + /* Need to find an unused slot and save our signal and PID. + * Still holding mutex_lock(&mts_io_mutex) + * Find first open slot + */ + found = -1; + for (i=0; i < PID_MAX_COUNT; i++) { + if (sig_pid_radio_reset_monitor[i].pid == 0) { + sig_pid_radio_reset_monitor[i].pid = (pid_t)pid0; + sig_pid_radio_reset_monitor[i].signal = sig; + sig_pid_radio_reset_monitor[i].vpid = vpid0; + pr_debug("%s: mts-io: open slot: pid %d, vpid 0x%x, sig %d, slot %d\n", + __FUNCTION__, + (int)pid0, + (unsigned int)vpid0, + (unsigned int)sig, i); + found = i; + break; + } else { + // See if this slot may be used. + pid_t pid1 = sig_pid_radio_reset_monitor[i].pid; + if (pid1 > 0) { + mutex_unlock(&mts_io_mutex); + rcu_read_lock(); + vpid1 = find_vpid((pid_t)pid1); + rcu_read_unlock(); + mutex_lock(&mts_io_mutex); + // See if pid and vpid still exists + if (!vpid1 || (vpid1 != sig_pid_radio_reset_monitor[i].vpid)) { + // Make sure no one else has claimed this slot + if (pid1 == sig_pid_radio_reset_monitor[i].pid) { + sig_pid_radio_reset_monitor[i].pid = 0; + sig_pid_radio_reset_monitor[i].vpid = NULL; + i--; // Should be able to use this slot. + continue; + } // Has not been re-claimed. + } // Slot has no user or exited user + } // Verify that slot in use is still in use + } // Found an empty slot + } // Loop through all the slots. + mutex_unlock(&mts_io_mutex); + } // pid is in pid table + return count; +} + +// Examples say buf is PAGE_SIZE long +ssize_t mts_attr_show_radio_reset_monitor(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int count; + int max = PAGE_SIZE; + int ret; + int i; + pid_t pid1; + + mutex_lock(&mts_io_mutex); + + count = 0; + for (i=0; i < PID_MAX_COUNT; i++) { + pid1 = verify_vpid(sig_pid_radio_reset_monitor + i); + if (pid1 == 0) { + sig_pid_radio_reset_monitor[i].pid = 0; + sig_pid_radio_reset_monitor[i].vpid = NULL; + continue; + } + pr_debug("%s: found a pid in slot %d\n",__FUNCTION__,i); + ret = snprintf(buf+count, max, "%lu %d\n", + (unsigned long)pid1, + sig_pid_radio_reset_monitor[i].signal); + if (ret > 0) { + max -= ret; + count += ret; + } + if (max == 0) + break; + if(ret == 0) + break; + } // Loop through all the slots until we are full. + + mutex_unlock(&mts_io_mutex); + + return count; +} + + +/* + * After a radio reset, prime the flag, so if it is set to one + * after this, then we wake the processes waiting on the flag. + * Caller holds the mts_io_mutex lock. + */ +void +reset_radio_udev_discovery(void) +{ + radio_udev_discovery = 0; +} + +int +udev_discovered_radio(void) +{ + return radio_udev_discovery; +} + +ssize_t mts_attr_show_radio_udev_discovery(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", radio_udev_discovery); +} + + +ssize_t mts_attr_store_radio_udev_discovery(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = -1; + pid_t pid1; + struct pid *vpid0, *vpid1; + int sig, i; + int retval; + + retval = sscanf(buf, "%i", &value); + if (retval != 1) + return -EINVAL; + + if (radio_udev_discovery == -1) { + // Not ready + return -EPROTO; + } + + // Only driver can set this to zero. + if (value == 0) + return -EINVAL; + + mutex_lock(&mts_io_mutex); + if((value == 1) && (radio_udev_discovery == 0)) { + radio_udev_discovery = 1; + pr_debug("%s: mts-io: UDEV discovered cellular modem after radio-reset so signal registered processes", + __FUNCTION__); + // kill all processes that are queued + for (i=0; i < PID_MAX_COUNT; i++) { + pid1 = verify_vpid(sig_pid_radio_reset_monitor + i); + if (pid1 == 0) continue; + sig = sig_pid_radio_reset_monitor[i].signal; + vpid0 = sig_pid_radio_reset_monitor[i].vpid; + mutex_unlock(&mts_io_mutex); + rcu_read_lock(); + vpid1 = find_vpid(pid1); + if (vpid1 == vpid0) { + printk(KERN_ALERT + "mts-io: %s: Sending signal %d to pid %d due to UDEV radio discovery\n", + __FUNCTION__, + sig,pid1); + kill_pid(vpid1,sig,1); + } // vpid is valid, same as registered + else { + printk(KERN_ALERT + "mts-io: %s: mismatched vpid %x/%x\n", __FUNCTION__, (unsigned int)vpid1, (unsigned int) vpid0); + } + + rcu_read_unlock(); + mutex_lock(&mts_io_mutex); + } // Find empty slot + } // udev wants us to wake everybody up after reset + mutex_unlock(&mts_io_mutex); + return count; +} -- cgit v1.2.3