mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-12-22 04:32:37 +01:00
device/netdev: Properly implement mode switching
This commit is contained in:
parent
b2045a309b
commit
5a6c19988c
107
src/device.c
107
src/device.c
@ -95,8 +95,6 @@ struct device {
|
||||
bool seen_hidden_networks : 1;
|
||||
|
||||
uint32_t ap_roam_watch;
|
||||
|
||||
enum device_mode mode;
|
||||
};
|
||||
|
||||
struct signal_agent {
|
||||
@ -454,11 +452,6 @@ enum device_state device_get_state(struct device *device)
|
||||
return device->state;
|
||||
}
|
||||
|
||||
enum device_mode device_get_mode(struct device *device)
|
||||
{
|
||||
return device->mode;
|
||||
}
|
||||
|
||||
static void periodic_scan_trigger(int err, void *user_data)
|
||||
{
|
||||
struct device *device = user_data;
|
||||
@ -2054,29 +2047,6 @@ static struct l_dbus_message *device_signal_agent_unregister(
|
||||
return l_dbus_message_new_method_return(message);
|
||||
}
|
||||
|
||||
static void device_prepare_adhoc_ap_mode(struct device *device)
|
||||
{
|
||||
periodic_scan_stop(device);
|
||||
|
||||
/* Drop all state we can related to client mode */
|
||||
|
||||
if (device->scan_pending)
|
||||
dbus_pending_reply(&device->scan_pending,
|
||||
dbus_error_aborted(device->scan_pending));
|
||||
|
||||
l_hashmap_foreach_remove(device->networks,
|
||||
device_remove_network, device);
|
||||
|
||||
l_queue_destroy(device->autoconnect_list, l_free);
|
||||
device->autoconnect_list = l_queue_new();
|
||||
|
||||
l_queue_destroy(device->bss_list, bss_free);
|
||||
device->bss_list = l_queue_new();
|
||||
|
||||
l_queue_destroy(device->networks_sorted, NULL);
|
||||
device->networks_sorted = l_queue_new();
|
||||
}
|
||||
|
||||
static void device_hidden_network_scan_triggered(int err, void *user_data)
|
||||
{
|
||||
struct device *device = user_data;
|
||||
@ -2472,35 +2442,19 @@ static bool device_property_get_mode(struct l_dbus *dbus,
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct l_dbus_message *device_change_mode(struct device *device,
|
||||
struct l_dbus_message *message, enum device_mode mode)
|
||||
static void set_mode_cb(struct netdev *netdev, int result, void *user_data)
|
||||
{
|
||||
if (device->mode == mode)
|
||||
return dbus_error_already_exists(message);
|
||||
struct set_generic_cb_data *cb_data = user_data;
|
||||
struct l_dbus_message *reply = NULL;
|
||||
|
||||
/* ensure correct connection state in AP/AdHoc mode */
|
||||
if ((mode == DEVICE_MODE_AP || mode == DEVICE_MODE_ADHOC) &&
|
||||
(device->state != DEVICE_STATE_DISCONNECTED &&
|
||||
device->state != DEVICE_STATE_AUTOCONNECT))
|
||||
return dbus_error_busy(message);
|
||||
if (result < 0)
|
||||
reply = dbus_error_from_errno(result, cb_data->message);
|
||||
|
||||
switch (mode) {
|
||||
case DEVICE_MODE_AP:
|
||||
device_prepare_adhoc_ap_mode(device);
|
||||
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_AP);
|
||||
break;
|
||||
case DEVICE_MODE_ADHOC:
|
||||
device_prepare_adhoc_ap_mode(device);
|
||||
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_ADHOC);
|
||||
break;
|
||||
case DEVICE_MODE_STATION:
|
||||
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_STATION);
|
||||
break;
|
||||
}
|
||||
cb_data->complete(cb_data->dbus, cb_data->message, reply);
|
||||
cb_data->message = NULL;
|
||||
|
||||
device->mode = mode;
|
||||
|
||||
return NULL;
|
||||
l_dbus_property_changed(cb_data->dbus, device_get_path(cb_data->device),
|
||||
IWD_DEVICE_INTERFACE, "Mode");
|
||||
}
|
||||
|
||||
static struct l_dbus_message *device_property_set_mode(struct l_dbus *dbus,
|
||||
@ -2510,27 +2464,45 @@ static struct l_dbus_message *device_property_set_mode(struct l_dbus *dbus,
|
||||
void *user_data)
|
||||
{
|
||||
struct device *device = user_data;
|
||||
struct l_dbus_message *reply;
|
||||
struct netdev *netdev = device->netdev;
|
||||
const char *mode;
|
||||
enum device_mode change;
|
||||
enum netdev_iftype iftype;
|
||||
int r;
|
||||
struct set_generic_cb_data *cb_data;
|
||||
|
||||
if (!l_dbus_message_iter_get_variant(new_value, "s", &mode))
|
||||
return dbus_error_invalid_args(message);
|
||||
|
||||
if (!strcmp(mode, "station"))
|
||||
change = DEVICE_MODE_STATION;
|
||||
iftype = NETDEV_IFTYPE_STATION;
|
||||
else if (!strcmp(mode, "ap"))
|
||||
change = DEVICE_MODE_AP;
|
||||
iftype = NETDEV_IFTYPE_AP;
|
||||
else if (!strcmp(mode, "ad-hoc"))
|
||||
change = DEVICE_MODE_ADHOC;
|
||||
iftype = NETDEV_IFTYPE_ADHOC;
|
||||
else
|
||||
return dbus_error_invalid_args(message);
|
||||
|
||||
reply = device_change_mode(device, message, change);
|
||||
if (reply)
|
||||
return reply;
|
||||
|
||||
if (iftype == netdev_get_iftype(netdev)) {
|
||||
complete(dbus, message, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* TODO: Special case, remove when Device/Station split is made */
|
||||
if (iftype != NETDEV_IFTYPE_STATION && device_is_busy(device))
|
||||
return dbus_error_busy(message);
|
||||
|
||||
cb_data = l_new(struct set_generic_cb_data, 1);
|
||||
cb_data->device = device;
|
||||
cb_data->dbus = dbus;
|
||||
cb_data->message = message;
|
||||
cb_data->complete = complete;
|
||||
|
||||
r = netdev_set_iftype(device->netdev, iftype, set_mode_cb,
|
||||
cb_data, set_generic_destroy);
|
||||
if (r < 0) {
|
||||
l_free(cb_data);
|
||||
return dbus_error_from_errno(r, message);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -2591,6 +2563,10 @@ static void device_netdev_notify(struct netdev *netdev,
|
||||
l_dbus_property_changed(dbus, device_get_path(device),
|
||||
IWD_DEVICE_INTERFACE, "Powered");
|
||||
|
||||
/* TODO: Remove when Device/Station split is done */
|
||||
if (netdev_get_iftype(device->netdev) != NETDEV_IFTYPE_STATION)
|
||||
return;
|
||||
|
||||
if (device->autoconnect)
|
||||
device_enter_state(device, DEVICE_STATE_AUTOCONNECT);
|
||||
else
|
||||
@ -2677,7 +2653,8 @@ struct device *device_create(struct wiphy *wiphy, struct netdev *netdev)
|
||||
action_ap_roam_prefix, sizeof(action_ap_roam_prefix),
|
||||
device_ap_roam_frame_event, device);
|
||||
|
||||
if (netdev_get_is_up(netdev))
|
||||
if (netdev_get_is_up(netdev) &&
|
||||
netdev_get_iftype(netdev) == NETDEV_IFTYPE_STATION)
|
||||
device_enter_state(device, DEVICE_STATE_AUTOCONNECT);
|
||||
|
||||
return device;
|
||||
|
@ -44,12 +44,6 @@ enum device_state {
|
||||
DEVICE_STATE_ROAMING
|
||||
};
|
||||
|
||||
enum device_mode {
|
||||
DEVICE_MODE_STATION,
|
||||
DEVICE_MODE_AP,
|
||||
DEVICE_MODE_ADHOC,
|
||||
};
|
||||
|
||||
typedef void (*device_watch_func_t)(struct device *device,
|
||||
enum device_event event,
|
||||
void *userdata);
|
||||
@ -64,7 +58,6 @@ struct netdev *device_get_netdev(struct device *device);
|
||||
uint32_t device_get_ifindex(struct device *device);
|
||||
const uint8_t *device_get_address(struct device *device);
|
||||
enum device_state device_get_state(struct device *device);
|
||||
enum device_mode device_get_mode(struct device *device);
|
||||
|
||||
uint32_t device_add_state_watch(struct device *device,
|
||||
device_state_watch_func_t func,
|
||||
|
160
src/netdev.c
160
src/netdev.c
@ -92,6 +92,7 @@ struct netdev {
|
||||
uint32_t disconnect_cmd_id;
|
||||
uint32_t join_adhoc_cmd_id;
|
||||
uint32_t leave_adhoc_cmd_id;
|
||||
uint32_t set_interface_cmd_id;
|
||||
enum netdev_result result;
|
||||
struct l_timeout *neighbor_report_timeout;
|
||||
struct l_timeout *sa_query_timeout;
|
||||
@ -3823,10 +3824,126 @@ static int netdev_cqm_rssi_update(struct netdev *netdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netdev_set_iftype(struct netdev *netdev, enum netdev_iftype type)
|
||||
static struct l_genl_msg *netdev_build_cmd_set_interface(struct netdev *netdev,
|
||||
uint32_t iftype)
|
||||
{
|
||||
struct l_genl_msg *msg =
|
||||
l_genl_msg_new_sized(NL80211_CMD_SET_INTERFACE, 32);
|
||||
|
||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFTYPE, 4, &iftype);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
struct netdev_set_iftype_request {
|
||||
netdev_command_cb_t cb;
|
||||
void *user_data;
|
||||
netdev_destroy_func_t destroy;
|
||||
uint32_t pending_type;
|
||||
uint32_t ref;
|
||||
struct netdev *netdev;
|
||||
bool bring_up;
|
||||
};
|
||||
|
||||
static void netdev_set_iftype_request_destroy(void *user_data)
|
||||
{
|
||||
struct netdev_set_iftype_request *req = user_data;
|
||||
struct netdev *netdev = req->netdev;
|
||||
|
||||
req->ref--;
|
||||
if (req->ref)
|
||||
return;
|
||||
|
||||
netdev->set_powered_cmd_id = 0;
|
||||
netdev->set_interface_cmd_id = 0;
|
||||
|
||||
if (req->destroy)
|
||||
req->destroy(req->user_data);
|
||||
|
||||
l_free(req);
|
||||
}
|
||||
|
||||
static void netdev_set_iftype_up_cb(int error, uint16_t type,
|
||||
const void *data,
|
||||
uint32_t len, void *user_data)
|
||||
{
|
||||
struct netdev_set_iftype_request *req = user_data;
|
||||
struct netdev *netdev = req->netdev;
|
||||
|
||||
if (req->cb)
|
||||
req->cb(netdev, error, req->user_data);
|
||||
}
|
||||
|
||||
static void netdev_set_iftype_cb(struct l_genl_msg *msg, void *user_data)
|
||||
{
|
||||
struct netdev_set_iftype_request *req = user_data;
|
||||
struct netdev *netdev = req->netdev;
|
||||
int error = l_genl_msg_get_error(msg);
|
||||
|
||||
if (error != 0)
|
||||
goto done;
|
||||
|
||||
netdev->type = req->pending_type;
|
||||
|
||||
/* If the netdev was down originally, we're done */
|
||||
if (!req->bring_up)
|
||||
goto done;
|
||||
|
||||
netdev->set_powered_cmd_id =
|
||||
rtnl_set_powered(netdev->index, true,
|
||||
netdev_set_iftype_up_cb, req,
|
||||
netdev_set_iftype_request_destroy);
|
||||
if (!netdev->set_powered_cmd_id) {
|
||||
error = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
req->ref++;
|
||||
netdev->set_interface_cmd_id = 0;
|
||||
return;
|
||||
|
||||
done:
|
||||
if (req->cb)
|
||||
req->cb(netdev, error, req->user_data);
|
||||
}
|
||||
|
||||
static void netdev_set_iftype_down_cb(int error, uint16_t type,
|
||||
const void *data,
|
||||
uint32_t len, void *user_data)
|
||||
{
|
||||
struct netdev_set_iftype_request *req = user_data;
|
||||
struct netdev *netdev = req->netdev;
|
||||
struct l_genl_msg *msg;
|
||||
|
||||
if (error != 0)
|
||||
goto error;
|
||||
|
||||
msg = netdev_build_cmd_set_interface(netdev, req->pending_type);
|
||||
netdev->set_interface_cmd_id =
|
||||
l_genl_family_send(nl80211, msg, netdev_set_iftype_cb, req,
|
||||
netdev_set_iftype_request_destroy);
|
||||
if (!netdev->set_interface_cmd_id) {
|
||||
l_genl_msg_unref(msg);
|
||||
error = -EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
req->ref++;
|
||||
netdev->set_powered_cmd_id = 0;
|
||||
return;
|
||||
|
||||
error:
|
||||
if (req->cb)
|
||||
req->cb(netdev, error, req->user_data);
|
||||
}
|
||||
|
||||
int netdev_set_iftype(struct netdev *netdev, enum netdev_iftype type,
|
||||
netdev_command_cb_t cb, void *user_data,
|
||||
netdev_destroy_func_t destroy)
|
||||
{
|
||||
uint32_t iftype;
|
||||
struct netdev_set_iftype_request *req;
|
||||
|
||||
switch (type) {
|
||||
case NETDEV_IFTYPE_AP:
|
||||
@ -3843,21 +3960,42 @@ int netdev_set_iftype(struct netdev *netdev, enum netdev_iftype type)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msg = l_genl_msg_new_sized(NL80211_CMD_SET_INTERFACE, 32);
|
||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFTYPE, 4, &iftype);
|
||||
if (netdev->set_powered_cmd_id ||
|
||||
netdev->set_interface_cmd_id)
|
||||
return -EBUSY;
|
||||
|
||||
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL)) {
|
||||
l_error("CMD_SET_INTERFACE failed");
|
||||
req = l_new(struct netdev_set_iftype_request, 1);
|
||||
req->cb = cb;
|
||||
req->user_data = user_data;
|
||||
req->destroy = destroy;
|
||||
req->pending_type = iftype;
|
||||
req->netdev = netdev;
|
||||
req->ref = 1;
|
||||
req->bring_up = netdev_get_is_up(netdev);
|
||||
|
||||
if (!req->bring_up) {
|
||||
struct l_genl_msg *msg =
|
||||
netdev_build_cmd_set_interface(netdev, iftype);
|
||||
|
||||
netdev->set_interface_cmd_id =
|
||||
l_genl_family_send(nl80211, msg,
|
||||
netdev_set_iftype_cb, req,
|
||||
netdev_set_iftype_request_destroy);
|
||||
if (netdev->set_interface_cmd_id)
|
||||
return 0;
|
||||
|
||||
l_genl_msg_unref(msg);
|
||||
|
||||
return -EIO;
|
||||
} else {
|
||||
netdev->set_powered_cmd_id =
|
||||
rtnl_set_powered(netdev->index, false,
|
||||
netdev_set_iftype_down_cb, req,
|
||||
netdev_set_iftype_request_destroy);
|
||||
if (netdev->set_powered_cmd_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
netdev->type = iftype;
|
||||
|
||||
return 0;
|
||||
l_free(req);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void netdev_bridge_port_event(const struct ifinfomsg *ifi, int bytes,
|
||||
|
@ -99,7 +99,9 @@ struct wiphy *netdev_get_wiphy(struct netdev *netdev);
|
||||
const uint8_t *netdev_get_address(struct netdev *netdev);
|
||||
uint32_t netdev_get_ifindex(struct netdev *netdev);
|
||||
enum netdev_iftype netdev_get_iftype(struct netdev *netdev);
|
||||
int netdev_set_iftype(struct netdev *netdev, enum netdev_iftype type);
|
||||
int netdev_set_iftype(struct netdev *netdev, enum netdev_iftype type,
|
||||
netdev_command_cb_t cb, void *user_data,
|
||||
netdev_destroy_func_t destroy);
|
||||
int netdev_set_4addr(struct netdev *netdev, bool use_4addr,
|
||||
netdev_command_cb_t cb, void *user_data,
|
||||
netdev_destroy_func_t destroy);
|
||||
|
Loading…
Reference in New Issue
Block a user