Add wireless status detection and monitoring functions

This commit is contained in:
Sriman Achanta 2026-01-23 12:02:17 -05:00 committed by Sriman Achanta
parent 4564f76627
commit 9bde703969

View file

@ -276,6 +276,168 @@ static void unescape(const char *src, char *dst)
*d = 0;
}
static enum wireless_status read_wireless_status(const char *sysfs_path)
{
char buf[16];
ssize_t sz;
int fd;
fd = open(sysfs_path, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return WIRELESS_STATUS_UNKNOWN;
sz = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (sz <= 0)
return WIRELESS_STATUS_UNKNOWN;
buf[sz] = '\0';
if (buf[sz - 1] == '\n')
buf[sz - 1] = '\0';
if (spa_streq(buf, "connected"))
return WIRELESS_STATUS_CONNECTED;
if (spa_streq(buf, "disconnected"))
return WIRELESS_STATUS_DISCONNECTED;
return WIRELESS_STATUS_UNKNOWN;
}
static bool is_usb_interface_dir(const char *name)
{
const char *colon = strchr(name, ':');
if (!colon || !strchr(name, '-'))
return false;
for (const char *p = colon + 1; *p; p++)
if (*p != '.' && !(*p >= '0' && *p <= '9'))
return false;
return true;
}
static char *check_wireless_status_at_path(struct impl *this, const char *path)
{
char wireless_path[PATH_MAX];
char *result;
int res;
res = spa_scnprintf(wireless_path, sizeof(wireless_path),
"%s/wireless_status", path);
if (res < 0 || (size_t)res >= sizeof(wireless_path))
return NULL;
if (access(wireless_path, R_OK) < 0)
return NULL;
result = strdup(wireless_path);
if (!result)
spa_log_error(this->log, "failed to allocate wireless_status path");
return result;
}
static char *search_siblings_for_wireless_status(struct impl *this, const char *parent_path)
{
struct dirent *entry;
char *result = NULL;
int count = 0;
spa_autoptr(DIR) parent_dir = opendir(parent_path);
if (!parent_dir)
return NULL;
while ((entry = readdir(parent_dir)) != NULL) {
char sibling_path[PATH_MAX];
char *path;
int res;
if (entry->d_name[0] == '.')
continue;
if (!is_usb_interface_dir(entry->d_name))
continue;
if (entry->d_type != DT_UNKNOWN && entry->d_type != DT_DIR)
continue;
res = spa_scnprintf(sibling_path, sizeof(sibling_path),
"%s/%s", parent_path, entry->d_name);
if (res < 0 || (size_t)res >= sizeof(sibling_path))
continue;
path = check_wireless_status_at_path(this, sibling_path);
if (path) {
count++;
if (!result)
result = path;
else
free(path);
}
}
if (count > 1)
spa_log_info(this->log, "found %d wireless_status files, using first one", count);
return result;
}
static char *find_wireless_status_path(struct impl *this, const char *card_syspath)
{
char usb_device_path[PATH_MAX];
char *last_slash, *result;
int i, res;
res = spa_scnprintf(usb_device_path, sizeof(usb_device_path), "%s", card_syspath);
if (res < 0 || (size_t)res >= sizeof(usb_device_path))
return NULL;
result = check_wireless_status_at_path(this, usb_device_path);
if (result)
return result;
for (i = 0; i < MAX_PARENT_TRAVERSAL_DEPTH; i++) {
last_slash = strrchr(usb_device_path, '/');
if (!last_slash || last_slash == usb_device_path)
break;
*last_slash = '\0';
result = search_siblings_for_wireless_status(this, usb_device_path);
if (result)
return result;
}
return NULL;
}
static void update_wireless_status(struct impl *this, struct card *card)
{
enum wireless_status status;
bool disconnected;
if (!card->wireless_status_path)
return;
status = read_wireless_status(card->wireless_status_path);
if (status == WIRELESS_STATUS_UNKNOWN) {
spa_log_info(this->log, "card %u wireless_status unreadable, removing monitor",
card->card_nr);
free(card->wireless_status_path);
card->wireless_status_path = NULL;
card->wireless_disconnected = false;
return;
}
disconnected = (status == WIRELESS_STATUS_DISCONNECTED);
if (disconnected != card->wireless_disconnected) {
spa_log_info(this->log, "card %u wireless device %s",
card->card_nr, disconnected ? "disconnected" : "connected");
card->wireless_disconnected = disconnected;
}
}
static int check_device_pcm_class(const char *devname)
{
char path[PATH_MAX];
@ -906,6 +1068,23 @@ static int start_inotify(struct impl *this)
return 0;
}
static struct card *find_card_by_wireless_status_path(struct impl *this, const char *syspath)
{
char wireless_path[PATH_MAX];
int res;
res = spa_scnprintf(wireless_path, sizeof(wireless_path), "%s/wireless_status", syspath);
if (res < 0 || (size_t)res >= sizeof(wireless_path))
return NULL;
for (unsigned int i = 0; i < this->n_cards; i++) {
if (this->cards[i].wireless_status_path &&
spa_streq(this->cards[i].wireless_status_path, wireless_path))
return &this->cards[i];
}
return NULL;
}
static void impl_on_fd_events(struct spa_source *source)
{
struct impl *this = source->data;