Phase 1: implement iwd D-Bus core
iwd_dbus watches net.connman.iwd name ownership, calls GetManagedObjects, and dispatches InterfacesAdded/Removed to a callback consumer. iwd_manager owns hashes of Iwd_Device and Iwd_Network keyed by object path; sub-objects subscribe to their PropertiesChanged signals via Eldbus and ping the manager so listeners can refresh. Aggregated state (off/idle/scanning/ connecting/connected) is recomputed from the active station. iwd_device exposes Powered toggle plus Station Scan/Disconnect. iwd_network calls Network.Connect() (the iwd Agent will be wired in next) and Forget via the referenced KnownNetwork object. Builds against EFL 1.28 / Enlightenment 0.27. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b9eb5de878
commit
73d17ff21c
11 changed files with 699 additions and 54 deletions
|
|
@ -1,26 +1,159 @@
|
|||
#include "iwd_dbus.h"
|
||||
#include <Eina.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FDO_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
|
||||
|
||||
struct _Iwd_Dbus
|
||||
{
|
||||
Eldbus_Connection *conn;
|
||||
Eldbus_Object *root;
|
||||
Eldbus_Proxy *object_manager;
|
||||
Eldbus_Signal_Handler *name_owner_sh;
|
||||
Eldbus_Connection *conn;
|
||||
Iwd_Dbus_Callbacks cbs;
|
||||
void *data;
|
||||
|
||||
Eldbus_Object *root_obj;
|
||||
Eldbus_Proxy *root_om;
|
||||
Eldbus_Signal_Handler *sh_added;
|
||||
Eldbus_Signal_Handler *sh_removed;
|
||||
Eina_Bool present;
|
||||
};
|
||||
|
||||
/* TODO:
|
||||
* - watch IWD_BUS_NAME owner (NameOwnerChanged) for restart handling
|
||||
* - call org.freedesktop.DBus.ObjectManager.GetManagedObjects on "/"
|
||||
* - listen for InterfacesAdded / InterfacesRemoved
|
||||
* - dispatch new objects to iwd_manager
|
||||
*/
|
||||
Eldbus_Connection *
|
||||
iwd_dbus_conn(const Iwd_Dbus *d) { return d ? d->conn : NULL; }
|
||||
|
||||
/* Walk the a{oa{sa{sv}}} reply from GetManagedObjects, emitting iface_added
|
||||
* for every (path, interface) pair. */
|
||||
static void
|
||||
_emit_managed(Iwd_Dbus *d, Eldbus_Message_Iter *objects)
|
||||
{
|
||||
Eldbus_Message_Iter *entry;
|
||||
while (eldbus_message_iter_get_and_next(objects, 'e', &entry))
|
||||
{
|
||||
const char *path;
|
||||
Eldbus_Message_Iter *ifaces;
|
||||
if (!eldbus_message_iter_arguments_get(entry, "oa{sa{sv}}", &path, &ifaces))
|
||||
continue;
|
||||
|
||||
Eldbus_Message_Iter *iface_entry;
|
||||
while (eldbus_message_iter_get_and_next(ifaces, 'e', &iface_entry))
|
||||
{
|
||||
const char *iface;
|
||||
Eldbus_Message_Iter *props;
|
||||
if (!eldbus_message_iter_arguments_get(iface_entry, "sa{sv}", &iface, &props))
|
||||
continue;
|
||||
if (d->cbs.iface_added)
|
||||
d->cbs.iface_added(d->data, path, iface, props);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_on_get_managed(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED)
|
||||
{
|
||||
Iwd_Dbus *d = data;
|
||||
const char *errname, *errmsg;
|
||||
if (eldbus_message_error_get(msg, &errname, &errmsg))
|
||||
{
|
||||
/* iwd not present yet — name watcher will retry. */
|
||||
return;
|
||||
}
|
||||
Eldbus_Message_Iter *objects;
|
||||
if (!eldbus_message_arguments_get(msg, "a{oa{sa{sv}}}", &objects))
|
||||
return;
|
||||
_emit_managed(d, objects);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_iface_added(void *data, const Eldbus_Message *msg)
|
||||
{
|
||||
Iwd_Dbus *d = data;
|
||||
const char *path;
|
||||
Eldbus_Message_Iter *ifaces, *iface_entry;
|
||||
if (!eldbus_message_arguments_get(msg, "oa{sa{sv}}", &path, &ifaces))
|
||||
return;
|
||||
while (eldbus_message_iter_get_and_next(ifaces, 'e', &iface_entry))
|
||||
{
|
||||
const char *iface;
|
||||
Eldbus_Message_Iter *props;
|
||||
if (!eldbus_message_iter_arguments_get(iface_entry, "sa{sv}", &iface, &props))
|
||||
continue;
|
||||
if (d->cbs.iface_added)
|
||||
d->cbs.iface_added(d->data, path, iface, props);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_on_iface_removed(void *data, const Eldbus_Message *msg)
|
||||
{
|
||||
Iwd_Dbus *d = data;
|
||||
const char *path;
|
||||
Eldbus_Message_Iter *ifaces;
|
||||
if (!eldbus_message_arguments_get(msg, "oas", &path, &ifaces))
|
||||
return;
|
||||
const char *iface;
|
||||
while (eldbus_message_iter_get_and_next(ifaces, 's', &iface))
|
||||
{
|
||||
if (d->cbs.iface_removed)
|
||||
d->cbs.iface_removed(d->data, path, iface);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_bind_root(Iwd_Dbus *d)
|
||||
{
|
||||
if (d->root_om) return;
|
||||
d->root_obj = eldbus_object_get(d->conn, IWD_BUS_NAME, "/");
|
||||
if (!d->root_obj) return;
|
||||
d->root_om = eldbus_proxy_get(d->root_obj, FDO_OBJECT_MANAGER);
|
||||
if (!d->root_om) return;
|
||||
|
||||
d->sh_added = eldbus_proxy_signal_handler_add(d->root_om, "InterfacesAdded",
|
||||
_on_iface_added, d);
|
||||
d->sh_removed = eldbus_proxy_signal_handler_add(d->root_om, "InterfacesRemoved",
|
||||
_on_iface_removed, d);
|
||||
eldbus_proxy_call(d->root_om, "GetManagedObjects", _on_get_managed, d, -1, "");
|
||||
}
|
||||
|
||||
static void
|
||||
_unbind_root(Iwd_Dbus *d)
|
||||
{
|
||||
if (d->sh_added) { eldbus_signal_handler_del(d->sh_added); d->sh_added = NULL; }
|
||||
if (d->sh_removed) { eldbus_signal_handler_del(d->sh_removed); d->sh_removed = NULL; }
|
||||
if (d->root_om) { eldbus_proxy_unref(d->root_om); d->root_om = NULL; }
|
||||
if (d->root_obj) { eldbus_object_unref(d->root_obj); d->root_obj = NULL; }
|
||||
}
|
||||
|
||||
static void
|
||||
_on_name_owner(void *data, const char *bus EINA_UNUSED, const char *old_id, const char *new_id)
|
||||
{
|
||||
Iwd_Dbus *d = data;
|
||||
Eina_Bool now = (new_id && new_id[0]);
|
||||
Eina_Bool was = (old_id && old_id[0]);
|
||||
|
||||
if (now && !d->present)
|
||||
{
|
||||
d->present = EINA_TRUE;
|
||||
_bind_root(d);
|
||||
if (d->cbs.name_appeared) d->cbs.name_appeared(d->data);
|
||||
}
|
||||
else if (!now && (was || d->present))
|
||||
{
|
||||
d->present = EINA_FALSE;
|
||||
_unbind_root(d);
|
||||
if (d->cbs.name_vanished) d->cbs.name_vanished(d->data);
|
||||
}
|
||||
}
|
||||
|
||||
Iwd_Dbus *
|
||||
iwd_dbus_new(Eldbus_Connection *conn)
|
||||
iwd_dbus_new(Eldbus_Connection *conn, const Iwd_Dbus_Callbacks *cbs, void *data)
|
||||
{
|
||||
Iwd_Dbus *d = calloc(1, sizeof(*d));
|
||||
if (!d) return NULL;
|
||||
d->conn = conn;
|
||||
if (cbs) d->cbs = *cbs;
|
||||
d->data = data;
|
||||
|
||||
eldbus_name_owner_changed_callback_add(conn, IWD_BUS_NAME,
|
||||
_on_name_owner, d, EINA_TRUE);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
|
@ -28,5 +161,7 @@ void
|
|||
iwd_dbus_free(Iwd_Dbus *d)
|
||||
{
|
||||
if (!d) return;
|
||||
eldbus_name_owner_changed_callback_del(d->conn, IWD_BUS_NAME, _on_name_owner, d);
|
||||
_unbind_root(d);
|
||||
free(d);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define IWD_DBUS_H
|
||||
|
||||
#include <Eldbus.h>
|
||||
#include <Eina.h>
|
||||
|
||||
#define IWD_BUS_NAME "net.connman.iwd"
|
||||
#define IWD_IFACE_ADAPTER "net.connman.iwd.Adapter"
|
||||
|
|
@ -14,7 +15,21 @@
|
|||
|
||||
typedef struct _Iwd_Dbus Iwd_Dbus;
|
||||
|
||||
Iwd_Dbus *iwd_dbus_new (Eldbus_Connection *conn);
|
||||
/* Callbacks the consumer (iwd_manager) registers to react to bus state. */
|
||||
typedef struct _Iwd_Dbus_Callbacks
|
||||
{
|
||||
void (*name_appeared) (void *data);
|
||||
void (*name_vanished) (void *data);
|
||||
/* iface_props is an iter positioned on a{sv} for the given interface. */
|
||||
void (*iface_added) (void *data, const char *path, const char *iface,
|
||||
Eldbus_Message_Iter *iface_props);
|
||||
void (*iface_removed) (void *data, const char *path, const char *iface);
|
||||
} Iwd_Dbus_Callbacks;
|
||||
|
||||
Iwd_Dbus *iwd_dbus_new (Eldbus_Connection *conn,
|
||||
const Iwd_Dbus_Callbacks *cbs, void *data);
|
||||
void iwd_dbus_free(Iwd_Dbus *d);
|
||||
|
||||
Eldbus_Connection *iwd_dbus_conn(const Iwd_Dbus *d);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,26 +1,158 @@
|
|||
#include "iwd_device.h"
|
||||
#include "iwd_dbus.h"
|
||||
#include "iwd_props.h"
|
||||
#include "iwd_manager.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static Iwd_Station_State
|
||||
_state_from_str(const char *s)
|
||||
{
|
||||
if (!s) return IWD_STATION_DISCONNECTED;
|
||||
if (!strcmp(s, "connected")) return IWD_STATION_CONNECTED;
|
||||
if (!strcmp(s, "connecting")) return IWD_STATION_CONNECTING;
|
||||
if (!strcmp(s, "disconnecting")) return IWD_STATION_DISCONNECTING;
|
||||
if (!strcmp(s, "roaming")) return IWD_STATION_ROAMING;
|
||||
return IWD_STATION_DISCONNECTED;
|
||||
}
|
||||
|
||||
static void
|
||||
_dev_prop_cb(void *data, const char *key, Eldbus_Message_Iter *v)
|
||||
{
|
||||
Iwd_Device *d = data;
|
||||
if (!strcmp(key, "Name")) { free(d->name); d->name = iwd_props_str_dup(v); }
|
||||
else if (!strcmp(key, "Address")) { free(d->address); d->address = iwd_props_str_dup(v); }
|
||||
else if (!strcmp(key, "Adapter")) { free(d->adapter_path); d->adapter_path = iwd_props_str_dup(v); }
|
||||
else if (!strcmp(key, "Powered")) { d->powered = iwd_props_bool(v); }
|
||||
}
|
||||
|
||||
static void
|
||||
_sta_prop_cb(void *data, const char *key, Eldbus_Message_Iter *v)
|
||||
{
|
||||
Iwd_Device *d = data;
|
||||
if (!strcmp(key, "State"))
|
||||
{
|
||||
char *s = iwd_props_str_dup(v);
|
||||
d->station_state = _state_from_str(s);
|
||||
free(s);
|
||||
}
|
||||
else if (!strcmp(key, "Scanning")) { d->scanning = iwd_props_bool(v); }
|
||||
else if (!strcmp(key, "ConnectedNetwork")) { free(d->connected_network); d->connected_network = iwd_props_str_dup(v); }
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_apply_device_props(Iwd_Device *d, Eldbus_Message_Iter *props)
|
||||
{
|
||||
iwd_props_for_each(props, _dev_prop_cb, d);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_apply_station_props(Iwd_Device *d, Eldbus_Message_Iter *props)
|
||||
{
|
||||
d->has_station = EINA_TRUE;
|
||||
iwd_props_for_each(props, _sta_prop_cb, d);
|
||||
}
|
||||
|
||||
/* org.freedesktop.DBus.Properties.PropertiesChanged: (s, a{sv}, as) */
|
||||
static void
|
||||
_on_dev_props_changed(void *data, const Eldbus_Message *msg)
|
||||
{
|
||||
Iwd_Device *d = data;
|
||||
const char *iface;
|
||||
Eldbus_Message_Iter *changed, *invalidated;
|
||||
if (!eldbus_message_arguments_get(msg, "sa{sv}as", &iface, &changed, &invalidated))
|
||||
return;
|
||||
if (strcmp(iface, IWD_IFACE_DEVICE) != 0) return;
|
||||
iwd_props_for_each(changed, _dev_prop_cb, d);
|
||||
if (d->manager) iwd_manager_notify(d->manager);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_sta_props_changed(void *data, const Eldbus_Message *msg)
|
||||
{
|
||||
Iwd_Device *d = data;
|
||||
const char *iface;
|
||||
Eldbus_Message_Iter *changed, *invalidated;
|
||||
if (!eldbus_message_arguments_get(msg, "sa{sv}as", &iface, &changed, &invalidated))
|
||||
return;
|
||||
if (strcmp(iface, IWD_IFACE_STATION) != 0) return;
|
||||
iwd_props_for_each(changed, _sta_prop_cb, d);
|
||||
if (d->manager) iwd_manager_notify(d->manager);
|
||||
}
|
||||
|
||||
Iwd_Device *
|
||||
iwd_device_new(Eldbus_Connection *conn EINA_UNUSED, const char *path)
|
||||
iwd_device_new(Eldbus_Connection *conn, const char *path, void *manager)
|
||||
{
|
||||
Iwd_Device *d = calloc(1, sizeof(*d));
|
||||
if (path) d->path = strdup(path);
|
||||
/* TODO: build proxies for Device + Station; subscribe to PropertiesChanged */
|
||||
if (!d) return NULL;
|
||||
d->path = path ? strdup(path) : NULL;
|
||||
d->manager = manager;
|
||||
d->obj = eldbus_object_get(conn, IWD_BUS_NAME, path);
|
||||
if (d->obj)
|
||||
{
|
||||
d->device_proxy = eldbus_proxy_get(d->obj, IWD_IFACE_DEVICE);
|
||||
if (d->device_proxy)
|
||||
d->sh_dev_props = eldbus_proxy_properties_changed_callback_add(
|
||||
d->device_proxy, _on_dev_props_changed, d);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_attach_station(Iwd_Device *d)
|
||||
{
|
||||
if (!d || d->station_proxy || !d->obj) return;
|
||||
d->station_proxy = eldbus_proxy_get(d->obj, IWD_IFACE_STATION);
|
||||
if (d->station_proxy)
|
||||
{
|
||||
d->has_station = EINA_TRUE;
|
||||
d->sh_sta_props = eldbus_proxy_properties_changed_callback_add(
|
||||
d->station_proxy, _on_sta_props_changed, d);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_detach_station(Iwd_Device *d)
|
||||
{
|
||||
if (!d) return;
|
||||
if (d->sh_sta_props) { eldbus_signal_handler_del(d->sh_sta_props); d->sh_sta_props = NULL; }
|
||||
if (d->station_proxy) { eldbus_proxy_unref(d->station_proxy); d->station_proxy = NULL; }
|
||||
d->has_station = EINA_FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_free(Iwd_Device *d)
|
||||
{
|
||||
if (!d) return;
|
||||
iwd_device_detach_station(d);
|
||||
if (d->sh_dev_props) eldbus_signal_handler_del(d->sh_dev_props);
|
||||
if (d->device_proxy) eldbus_proxy_unref(d->device_proxy);
|
||||
if (d->obj) eldbus_object_unref(d->obj);
|
||||
free(d->path);
|
||||
free(d->name);
|
||||
free(d->address);
|
||||
free(d->adapter_path);
|
||||
free(d->connected_network);
|
||||
free(d);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_set_powered(Iwd_Device *d EINA_UNUSED, Eina_Bool on EINA_UNUSED)
|
||||
iwd_device_set_powered(Iwd_Device *d, Eina_Bool on)
|
||||
{
|
||||
/* TODO: Set("Powered") on Device interface */
|
||||
if (!d || !d->device_proxy) return;
|
||||
eldbus_proxy_property_set(d->device_proxy, "Powered", "b", &on, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_scan(Iwd_Device *d)
|
||||
{
|
||||
if (!d || !d->station_proxy) return;
|
||||
eldbus_proxy_call(d->station_proxy, "Scan", NULL, NULL, -1, "");
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_disconnect(Iwd_Device *d)
|
||||
{
|
||||
if (!d || !d->station_proxy) return;
|
||||
eldbus_proxy_call(d->station_proxy, "Disconnect", NULL, NULL, -1, "");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,17 +6,47 @@
|
|||
|
||||
typedef struct _Iwd_Device Iwd_Device;
|
||||
|
||||
typedef enum {
|
||||
IWD_STATION_DISCONNECTED,
|
||||
IWD_STATION_CONNECTING,
|
||||
IWD_STATION_CONNECTED,
|
||||
IWD_STATION_DISCONNECTING,
|
||||
IWD_STATION_ROAMING,
|
||||
} Iwd_Station_State;
|
||||
|
||||
struct _Iwd_Device
|
||||
{
|
||||
char *path; /* D-Bus object path */
|
||||
char *name;
|
||||
char *address;
|
||||
Eina_Bool powered;
|
||||
Eldbus_Proxy *station; /* may be NULL */
|
||||
char *path;
|
||||
char *name;
|
||||
char *address;
|
||||
char *adapter_path;
|
||||
Eina_Bool powered;
|
||||
|
||||
/* Station-side state, valid when station_proxy != NULL */
|
||||
Eina_Bool has_station;
|
||||
Iwd_Station_State station_state;
|
||||
Eina_Bool scanning;
|
||||
char *connected_network;
|
||||
|
||||
Eldbus_Object *obj;
|
||||
Eldbus_Proxy *device_proxy;
|
||||
Eldbus_Proxy *station_proxy;
|
||||
Eldbus_Signal_Handler *sh_dev_props;
|
||||
Eldbus_Signal_Handler *sh_sta_props;
|
||||
|
||||
void *manager; /* back-ref, opaque (Iwd_Manager *) */
|
||||
};
|
||||
|
||||
Iwd_Device *iwd_device_new (Eldbus_Connection *conn, const char *path);
|
||||
Iwd_Device *iwd_device_new (Eldbus_Connection *conn, const char *path, void *manager);
|
||||
void iwd_device_free(Iwd_Device *d);
|
||||
void iwd_device_set_powered(Iwd_Device *d, Eina_Bool on);
|
||||
|
||||
void iwd_device_apply_device_props (Iwd_Device *d, Eldbus_Message_Iter *props);
|
||||
void iwd_device_apply_station_props(Iwd_Device *d, Eldbus_Message_Iter *props);
|
||||
void iwd_device_attach_station (Iwd_Device *d);
|
||||
void iwd_device_detach_station (Iwd_Device *d);
|
||||
|
||||
void iwd_device_set_powered(Iwd_Device *d, Eina_Bool on);
|
||||
void iwd_device_scan (Iwd_Device *d);
|
||||
void iwd_device_disconnect (Iwd_Device *d);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,26 +1,189 @@
|
|||
#include "iwd_manager.h"
|
||||
#include "iwd_dbus.h"
|
||||
#include "iwd_device.h"
|
||||
#include "iwd_network.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct _Listener
|
||||
{
|
||||
Iwd_Manager_Cb cb;
|
||||
void *data;
|
||||
} Listener;
|
||||
|
||||
struct _Iwd_Manager
|
||||
{
|
||||
Iwd_Dbus *dbus;
|
||||
Eina_List *devices; /* Iwd_Device * */
|
||||
Eina_List *listeners;
|
||||
Iwd_State state;
|
||||
Iwd_Dbus *dbus;
|
||||
Eina_Hash *devices; /* path → Iwd_Device * */
|
||||
Eina_Hash *networks; /* path → Iwd_Network * */
|
||||
Eina_List *listeners; /* Listener * */
|
||||
Iwd_State state;
|
||||
Eina_Bool notify_pending;
|
||||
};
|
||||
|
||||
/* TODO:
|
||||
* - Build the device/station/network tree from iwd_dbus events
|
||||
* - Aggregate state from active station
|
||||
* - Notify listeners on any change
|
||||
*/
|
||||
static void _recompute_state(Iwd_Manager *m);
|
||||
|
||||
/* ----- listeners ------------------------------------------------------- */
|
||||
|
||||
void
|
||||
iwd_manager_listener_add(Iwd_Manager *m, Iwd_Manager_Cb cb, void *data)
|
||||
{
|
||||
if (!m || !cb) return;
|
||||
Listener *l = calloc(1, sizeof(*l));
|
||||
l->cb = cb; l->data = data;
|
||||
m->listeners = eina_list_append(m->listeners, l);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_manager_listener_del(Iwd_Manager *m, Iwd_Manager_Cb cb, void *data)
|
||||
{
|
||||
if (!m) return;
|
||||
Eina_List *l;
|
||||
Listener *li;
|
||||
EINA_LIST_FOREACH(m->listeners, l, li)
|
||||
if (li->cb == cb && li->data == data)
|
||||
{
|
||||
m->listeners = eina_list_remove_list(m->listeners, l);
|
||||
free(li);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
iwd_manager_notify(Iwd_Manager *m)
|
||||
{
|
||||
if (!m) return;
|
||||
_recompute_state(m);
|
||||
Eina_List *l;
|
||||
Listener *li;
|
||||
EINA_LIST_FOREACH(m->listeners, l, li)
|
||||
li->cb(li->data, m);
|
||||
}
|
||||
|
||||
/* ----- state aggregation ---------------------------------------------- */
|
||||
|
||||
static void
|
||||
_recompute_state(Iwd_Manager *m)
|
||||
{
|
||||
Iwd_State s = IWD_STATE_OFF;
|
||||
Eina_Iterator *it = eina_hash_iterator_data_new(m->devices);
|
||||
Iwd_Device *d;
|
||||
Eina_Bool any_powered = EINA_FALSE;
|
||||
EINA_ITERATOR_FOREACH(it, d)
|
||||
{
|
||||
if (d->powered) any_powered = EINA_TRUE;
|
||||
if (!d->has_station) continue;
|
||||
if (d->scanning && s < IWD_STATE_SCANNING) s = IWD_STATE_SCANNING;
|
||||
if (d->station_state == IWD_STATION_CONNECTING && s < IWD_STATE_CONNECTING) s = IWD_STATE_CONNECTING;
|
||||
if (d->station_state == IWD_STATION_CONNECTED && s < IWD_STATE_CONNECTED) s = IWD_STATE_CONNECTED;
|
||||
}
|
||||
eina_iterator_free(it);
|
||||
if (s == IWD_STATE_OFF && any_powered) s = IWD_STATE_IDLE;
|
||||
m->state = s;
|
||||
}
|
||||
|
||||
/* ----- D-Bus callbacks ------------------------------------------------- */
|
||||
|
||||
static void
|
||||
_on_iface_added(void *data, const char *path, const char *iface, Eldbus_Message_Iter *props)
|
||||
{
|
||||
Iwd_Manager *m = data;
|
||||
Eldbus_Connection *conn = iwd_dbus_conn(m->dbus);
|
||||
|
||||
if (!strcmp(iface, IWD_IFACE_DEVICE))
|
||||
{
|
||||
Iwd_Device *d = eina_hash_find(m->devices, path);
|
||||
if (!d)
|
||||
{
|
||||
d = iwd_device_new(conn, path, m);
|
||||
if (d) eina_hash_add(m->devices, path, d);
|
||||
}
|
||||
if (d) iwd_device_apply_device_props(d, props);
|
||||
}
|
||||
else if (!strcmp(iface, IWD_IFACE_STATION))
|
||||
{
|
||||
Iwd_Device *d = eina_hash_find(m->devices, path);
|
||||
if (!d)
|
||||
{
|
||||
d = iwd_device_new(conn, path, m);
|
||||
if (d) eina_hash_add(m->devices, path, d);
|
||||
}
|
||||
if (d)
|
||||
{
|
||||
iwd_device_attach_station(d);
|
||||
iwd_device_apply_station_props(d, props);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(iface, IWD_IFACE_NETWORK))
|
||||
{
|
||||
Iwd_Network *n = eina_hash_find(m->networks, path);
|
||||
if (!n)
|
||||
{
|
||||
n = iwd_network_new(conn, path, m);
|
||||
if (n) eina_hash_add(m->networks, path, n);
|
||||
}
|
||||
if (n) iwd_network_apply_props(n, props);
|
||||
}
|
||||
/* Adapter / KnownNetwork: TODO (not needed for first connect path) */
|
||||
|
||||
iwd_manager_notify(m);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_iface_removed(void *data, const char *path, const char *iface)
|
||||
{
|
||||
Iwd_Manager *m = data;
|
||||
|
||||
if (!strcmp(iface, IWD_IFACE_STATION))
|
||||
{
|
||||
Iwd_Device *d = eina_hash_find(m->devices, path);
|
||||
if (d) iwd_device_detach_station(d);
|
||||
}
|
||||
else if (!strcmp(iface, IWD_IFACE_DEVICE))
|
||||
{
|
||||
eina_hash_del(m->devices, path, NULL);
|
||||
}
|
||||
else if (!strcmp(iface, IWD_IFACE_NETWORK))
|
||||
{
|
||||
eina_hash_del(m->networks, path, NULL);
|
||||
}
|
||||
iwd_manager_notify(m);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_name_appeared(void *data EINA_UNUSED) { /* GetManagedObjects will populate */ }
|
||||
|
||||
static void
|
||||
_on_name_vanished(void *data)
|
||||
{
|
||||
Iwd_Manager *m = data;
|
||||
eina_hash_free_buckets(m->devices);
|
||||
eina_hash_free_buckets(m->networks);
|
||||
m->state = IWD_STATE_OFF;
|
||||
iwd_manager_notify(m);
|
||||
}
|
||||
|
||||
/* ----- lifecycle ------------------------------------------------------- */
|
||||
|
||||
static void _device_free_cb (void *d) { iwd_device_free(d); }
|
||||
static void _network_free_cb(void *d) { iwd_network_free(d); }
|
||||
|
||||
Iwd_Manager *
|
||||
iwd_manager_new(Eldbus_Connection *conn)
|
||||
{
|
||||
Iwd_Manager *m = calloc(1, sizeof(*m));
|
||||
m->dbus = iwd_dbus_new(conn);
|
||||
m->state = IWD_STATE_OFF;
|
||||
if (!m) return NULL;
|
||||
m->devices = eina_hash_string_superfast_new(_device_free_cb);
|
||||
m->networks = eina_hash_string_superfast_new(_network_free_cb);
|
||||
m->state = IWD_STATE_OFF;
|
||||
|
||||
Iwd_Dbus_Callbacks cbs = {
|
||||
.name_appeared = _on_name_appeared,
|
||||
.name_vanished = _on_name_vanished,
|
||||
.iface_added = _on_iface_added,
|
||||
.iface_removed = _on_iface_removed,
|
||||
};
|
||||
m->dbus = iwd_dbus_new(conn, &cbs, m);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
|
@ -29,16 +192,33 @@ iwd_manager_free(Iwd_Manager *m)
|
|||
{
|
||||
if (!m) return;
|
||||
iwd_dbus_free(m->dbus);
|
||||
eina_list_free(m->devices);
|
||||
eina_list_free(m->listeners);
|
||||
eina_hash_free(m->devices);
|
||||
eina_hash_free(m->networks);
|
||||
Listener *li;
|
||||
EINA_LIST_FREE(m->listeners, li) free(li);
|
||||
free(m);
|
||||
}
|
||||
|
||||
Iwd_State iwd_manager_state(const Iwd_Manager *m) { return m ? m->state : IWD_STATE_OFF; }
|
||||
const Eina_List *iwd_manager_devices(const Iwd_Manager *m) { return m ? m->devices : NULL; }
|
||||
Iwd_State iwd_manager_state (const Iwd_Manager *m) { return m ? m->state : IWD_STATE_OFF; }
|
||||
const Eina_Hash *iwd_manager_devices (const Iwd_Manager *m) { return m ? m->devices : NULL; }
|
||||
const Eina_Hash *iwd_manager_networks (const Iwd_Manager *m) { return m ? m->networks : NULL; }
|
||||
|
||||
void iwd_manager_scan_request(Iwd_Manager *m EINA_UNUSED) { /* TODO */ }
|
||||
void iwd_manager_set_powered (Iwd_Manager *m EINA_UNUSED, Eina_Bool on EINA_UNUSED) { /* TODO */ }
|
||||
void
|
||||
iwd_manager_scan_request(Iwd_Manager *m)
|
||||
{
|
||||
if (!m) return;
|
||||
Eina_Iterator *it = eina_hash_iterator_data_new(m->devices);
|
||||
Iwd_Device *d;
|
||||
EINA_ITERATOR_FOREACH(it, d) iwd_device_scan(d);
|
||||
eina_iterator_free(it);
|
||||
}
|
||||
|
||||
void iwd_manager_listener_add(Iwd_Manager *m EINA_UNUSED, Iwd_Manager_Cb cb EINA_UNUSED, void *data EINA_UNUSED) { /* TODO */ }
|
||||
void iwd_manager_listener_del(Iwd_Manager *m EINA_UNUSED, Iwd_Manager_Cb cb EINA_UNUSED, void *data EINA_UNUSED) { /* TODO */ }
|
||||
void
|
||||
iwd_manager_set_powered(Iwd_Manager *m, Eina_Bool on)
|
||||
{
|
||||
if (!m) return;
|
||||
Eina_Iterator *it = eina_hash_iterator_data_new(m->devices);
|
||||
Iwd_Device *d;
|
||||
EINA_ITERATOR_FOREACH(it, d) iwd_device_set_powered(d, on);
|
||||
eina_iterator_free(it);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,14 +19,20 @@ Iwd_Manager *iwd_manager_new (Eldbus_Connection *conn);
|
|||
void iwd_manager_free(Iwd_Manager *m);
|
||||
|
||||
Iwd_State iwd_manager_state (const Iwd_Manager *m);
|
||||
const Eina_List *iwd_manager_devices (const Iwd_Manager *m);
|
||||
|
||||
/* Hash<path, Iwd_Device*> */
|
||||
const Eina_Hash *iwd_manager_devices (const Iwd_Manager *m);
|
||||
/* Hash<path, Iwd_Network*> */
|
||||
const Eina_Hash *iwd_manager_networks(const Iwd_Manager *m);
|
||||
|
||||
void iwd_manager_scan_request (Iwd_Manager *m);
|
||||
void iwd_manager_set_powered (Iwd_Manager *m, Eina_Bool on);
|
||||
|
||||
/* Event callback signature for UI layer */
|
||||
typedef void (*Iwd_Manager_Cb)(void *data, Iwd_Manager *m);
|
||||
void iwd_manager_listener_add (Iwd_Manager *m, Iwd_Manager_Cb cb, void *data);
|
||||
void iwd_manager_listener_del (Iwd_Manager *m, Iwd_Manager_Cb cb, void *data);
|
||||
|
||||
/* Internal: invoked by sub-objects when their state changes. */
|
||||
void iwd_manager_notify (Iwd_Manager *m);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,11 +1,67 @@
|
|||
#include "iwd_network.h"
|
||||
#include "iwd_dbus.h"
|
||||
#include "iwd_props.h"
|
||||
#include "iwd_manager.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static Iwd_Security
|
||||
_sec_from_str(const char *s)
|
||||
{
|
||||
if (!s) return IWD_SEC_UNKNOWN;
|
||||
if (!strcmp(s, "open")) return IWD_SEC_OPEN;
|
||||
if (!strcmp(s, "psk")) return IWD_SEC_PSK;
|
||||
if (!strcmp(s, "8021x")) return IWD_SEC_8021X;
|
||||
if (!strcmp(s, "wep")) return IWD_SEC_WEP;
|
||||
return IWD_SEC_UNKNOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
_prop_cb(void *data, const char *key, Eldbus_Message_Iter *v)
|
||||
{
|
||||
Iwd_Network *n = data;
|
||||
if (!strcmp(key, "Name")) { free(n->ssid); n->ssid = iwd_props_str_dup(v); }
|
||||
else if (!strcmp(key, "Type")) { char *s = iwd_props_str_dup(v); n->security = _sec_from_str(s); free(s); }
|
||||
else if (!strcmp(key, "Connected")) { n->connected = iwd_props_bool(v); }
|
||||
else if (!strcmp(key, "Device")) { free(n->device_path); n->device_path = iwd_props_str_dup(v); }
|
||||
else if (!strcmp(key, "KnownNetwork")) { free(n->known_path); n->known_path = iwd_props_str_dup(v); }
|
||||
}
|
||||
|
||||
void
|
||||
iwd_network_apply_props(Iwd_Network *n, Eldbus_Message_Iter *props)
|
||||
{
|
||||
iwd_props_for_each(props, _prop_cb, n);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_props_changed(void *data, const Eldbus_Message *msg)
|
||||
{
|
||||
Iwd_Network *n = data;
|
||||
const char *iface;
|
||||
Eldbus_Message_Iter *changed, *invalidated;
|
||||
if (!eldbus_message_arguments_get(msg, "sa{sv}as", &iface, &changed, &invalidated))
|
||||
return;
|
||||
if (strcmp(iface, IWD_IFACE_NETWORK) != 0) return;
|
||||
iwd_props_for_each(changed, _prop_cb, n);
|
||||
if (n->manager) iwd_manager_notify(n->manager);
|
||||
}
|
||||
|
||||
Iwd_Network *
|
||||
iwd_network_new(Eldbus_Connection *conn EINA_UNUSED, const char *path)
|
||||
iwd_network_new(Eldbus_Connection *conn, const char *path, void *manager)
|
||||
{
|
||||
Iwd_Network *n = calloc(1, sizeof(*n));
|
||||
if (path) n->path = strdup(path);
|
||||
/* TODO: read Name, Type, KnownNetwork properties */
|
||||
if (!n) return NULL;
|
||||
n->path = path ? strdup(path) : NULL;
|
||||
n->manager = manager;
|
||||
n->security = IWD_SEC_UNKNOWN;
|
||||
n->obj = eldbus_object_get(conn, IWD_BUS_NAME, path);
|
||||
if (n->obj)
|
||||
{
|
||||
n->proxy = eldbus_proxy_get(n->obj, IWD_IFACE_NETWORK);
|
||||
if (n->proxy)
|
||||
n->sh_props = eldbus_proxy_properties_changed_callback_add(
|
||||
n->proxy, _on_props_changed, n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
|
@ -13,10 +69,37 @@ void
|
|||
iwd_network_free(Iwd_Network *n)
|
||||
{
|
||||
if (!n) return;
|
||||
if (n->sh_props) eldbus_signal_handler_del(n->sh_props);
|
||||
if (n->proxy) eldbus_proxy_unref(n->proxy);
|
||||
if (n->obj) eldbus_object_unref(n->obj);
|
||||
free(n->path);
|
||||
free(n->ssid);
|
||||
free(n->device_path);
|
||||
free(n->known_path);
|
||||
free(n);
|
||||
}
|
||||
|
||||
void iwd_network_connect(Iwd_Network *n EINA_UNUSED) { /* TODO: Network.Connect() */ }
|
||||
void iwd_network_forget (Iwd_Network *n EINA_UNUSED) { /* TODO: KnownNetwork.Forget() */ }
|
||||
void
|
||||
iwd_network_connect(Iwd_Network *n)
|
||||
{
|
||||
if (!n || !n->proxy) return;
|
||||
/* Network.Connect() takes no args; iwd will dial the registered Agent
|
||||
* for a passphrase if needed. */
|
||||
eldbus_proxy_call(n->proxy, "Connect", NULL, NULL, -1, "");
|
||||
}
|
||||
|
||||
void
|
||||
iwd_network_forget(Iwd_Network *n)
|
||||
{
|
||||
if (!n || !n->known_path || !n->obj) return;
|
||||
Eldbus_Connection *conn = eldbus_object_connection_get(n->obj);
|
||||
Eldbus_Object *kobj = eldbus_object_get(conn, IWD_BUS_NAME, n->known_path);
|
||||
if (!kobj) return;
|
||||
Eldbus_Proxy *kp = eldbus_proxy_get(kobj, IWD_IFACE_KNOWN_NETWORK);
|
||||
if (kp)
|
||||
{
|
||||
eldbus_proxy_call(kp, "Forget", NULL, NULL, -1, "");
|
||||
eldbus_proxy_unref(kp);
|
||||
}
|
||||
eldbus_object_unref(kobj);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ typedef enum {
|
|||
IWD_SEC_PSK,
|
||||
IWD_SEC_8021X,
|
||||
IWD_SEC_WEP,
|
||||
IWD_SEC_UNKNOWN,
|
||||
} Iwd_Security;
|
||||
|
||||
typedef struct _Iwd_Network Iwd_Network;
|
||||
|
|
@ -17,16 +18,25 @@ struct _Iwd_Network
|
|||
{
|
||||
char *path;
|
||||
char *ssid;
|
||||
char *device_path;
|
||||
char *known_path;
|
||||
Iwd_Security security;
|
||||
int signal; /* 0..100 */
|
||||
Eina_Bool known;
|
||||
Eina_Bool connected;
|
||||
|
||||
Eldbus_Object *obj;
|
||||
Eldbus_Proxy *proxy;
|
||||
Eldbus_Signal_Handler *sh_props;
|
||||
|
||||
void *manager;
|
||||
};
|
||||
|
||||
Iwd_Network *iwd_network_new (Eldbus_Connection *conn, const char *path);
|
||||
Iwd_Network *iwd_network_new (Eldbus_Connection *conn, const char *path, void *manager);
|
||||
void iwd_network_free(Iwd_Network *n);
|
||||
|
||||
void iwd_network_apply_props(Iwd_Network *n, Eldbus_Message_Iter *props);
|
||||
|
||||
void iwd_network_connect (Iwd_Network *n);
|
||||
/* Forget acts on the KnownNetwork object referenced by this network. */
|
||||
void iwd_network_forget (Iwd_Network *n);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
38
src/iwd/iwd_props.c
Normal file
38
src/iwd/iwd_props.c
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#include "iwd_props.h"
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
iwd_props_for_each(Eldbus_Message_Iter *dict, Iwd_Prop_Cb cb, void *data)
|
||||
{
|
||||
if (!dict || !cb) return;
|
||||
Eldbus_Message_Iter *entry;
|
||||
while (eldbus_message_iter_get_and_next(dict, 'e', &entry))
|
||||
{
|
||||
const char *key;
|
||||
Eldbus_Message_Iter *variant;
|
||||
if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &variant))
|
||||
continue;
|
||||
cb(data, key, variant);
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
iwd_props_str_dup(Eldbus_Message_Iter *variant)
|
||||
{
|
||||
if (!variant) return NULL;
|
||||
const char *sig = eldbus_message_iter_signature_get(variant);
|
||||
if (!sig || (sig[0] != 's' && sig[0] != 'o')) { free((void *)sig); return NULL; }
|
||||
const char *s = NULL;
|
||||
char want[2] = { sig[0], 0 };
|
||||
free((void *)sig);
|
||||
if (!eldbus_message_iter_arguments_get(variant, want, &s)) return NULL;
|
||||
return s ? strdup(s) : NULL;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
iwd_props_bool(Eldbus_Message_Iter *variant)
|
||||
{
|
||||
Eina_Bool b = EINA_FALSE;
|
||||
if (variant) eldbus_message_iter_arguments_get(variant, "b", &b);
|
||||
return b;
|
||||
}
|
||||
15
src/iwd/iwd_props.h
Normal file
15
src/iwd/iwd_props.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef IWD_PROPS_H
|
||||
#define IWD_PROPS_H
|
||||
|
||||
#include <Eldbus.h>
|
||||
#include <Eina.h>
|
||||
|
||||
/* Iterate an a{sv} message iter, calling cb for every entry. */
|
||||
typedef void (*Iwd_Prop_Cb)(void *data, const char *key, Eldbus_Message_Iter *value);
|
||||
void iwd_props_for_each(Eldbus_Message_Iter *dict, Iwd_Prop_Cb cb, void *data);
|
||||
|
||||
/* Extract a string from a variant iter ("s" or "o"). Returns strdup'd copy. */
|
||||
char *iwd_props_str_dup(Eldbus_Message_Iter *variant);
|
||||
Eina_Bool iwd_props_bool(Eldbus_Message_Iter *variant);
|
||||
|
||||
#endif
|
||||
|
|
@ -4,6 +4,7 @@ e_iwd_sources = [
|
|||
'e_mod_gadget.c',
|
||||
'e_mod_popup.c',
|
||||
'iwd/iwd_dbus.c',
|
||||
'iwd/iwd_props.c',
|
||||
'iwd/iwd_manager.c',
|
||||
'iwd/iwd_device.c',
|
||||
'iwd/iwd_network.c',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue