mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-25 17:59:25 +01:00
ap: Move AP parameters to a struct
To limit the number of ap_start parameters, group basic AP config parameters in the ap_config struct that is passed as a pointer and owned by the ap_state.
This commit is contained in:
parent
7713e00992
commit
d55e00b31d
126
src/ap.c
126
src/ap.c
@ -53,10 +53,7 @@ struct ap_state {
|
|||||||
ap_event_func_t event_func;
|
ap_event_func_t event_func;
|
||||||
ap_stopped_func_t stopped_func;
|
ap_stopped_func_t stopped_func;
|
||||||
void *user_data;
|
void *user_data;
|
||||||
char *ssid;
|
struct ap_config *config;
|
||||||
uint8_t channel;
|
|
||||||
uint8_t *authorized_macs;
|
|
||||||
int authorized_macs_num;
|
|
||||||
|
|
||||||
unsigned int ciphers;
|
unsigned int ciphers;
|
||||||
enum ie_rsn_cipher_suite group_cipher;
|
enum ie_rsn_cipher_suite group_cipher;
|
||||||
@ -73,7 +70,6 @@ struct ap_state {
|
|||||||
|
|
||||||
bool started : 1;
|
bool started : 1;
|
||||||
bool gtk_set : 1;
|
bool gtk_set : 1;
|
||||||
bool no_cck_rates : 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sta_state {
|
struct sta_state {
|
||||||
@ -94,6 +90,22 @@ struct sta_state {
|
|||||||
|
|
||||||
static uint32_t netdev_watch;
|
static uint32_t netdev_watch;
|
||||||
|
|
||||||
|
void ap_config_free(struct ap_config *config)
|
||||||
|
{
|
||||||
|
if (unlikely(!config))
|
||||||
|
return;
|
||||||
|
|
||||||
|
l_free(config->ssid);
|
||||||
|
|
||||||
|
if (config->psk) {
|
||||||
|
explicit_bzero(config->psk, strlen(config->psk));
|
||||||
|
l_free(config->psk);
|
||||||
|
}
|
||||||
|
|
||||||
|
l_free(config->authorized_macs);
|
||||||
|
l_free(config);
|
||||||
|
}
|
||||||
|
|
||||||
static void ap_sta_free(void *data)
|
static void ap_sta_free(void *data)
|
||||||
{
|
{
|
||||||
struct sta_state *sta = data;
|
struct sta_state *sta = data;
|
||||||
@ -121,8 +133,6 @@ static void ap_reset(struct ap_state *ap)
|
|||||||
{
|
{
|
||||||
struct netdev *netdev = ap->netdev;
|
struct netdev *netdev = ap->netdev;
|
||||||
|
|
||||||
l_free(ap->ssid);
|
|
||||||
|
|
||||||
explicit_bzero(ap->pmk, sizeof(ap->pmk));
|
explicit_bzero(ap->pmk, sizeof(ap->pmk));
|
||||||
|
|
||||||
if (ap->mlme_watch)
|
if (ap->mlme_watch)
|
||||||
@ -138,7 +148,9 @@ static void ap_reset(struct ap_state *ap)
|
|||||||
if (ap->rates)
|
if (ap->rates)
|
||||||
l_uintset_free(ap->rates);
|
l_uintset_free(ap->rates);
|
||||||
|
|
||||||
l_free(ap->authorized_macs);
|
ap_config_free(ap->config);
|
||||||
|
ap->config = NULL;
|
||||||
|
|
||||||
ap->started = false;
|
ap->started = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +330,8 @@ static size_t ap_build_beacon_pr_head(struct ap_state *ap,
|
|||||||
|
|
||||||
/* SSID IE */
|
/* SSID IE */
|
||||||
ie_tlv_builder_next(&builder, IE_TYPE_SSID);
|
ie_tlv_builder_next(&builder, IE_TYPE_SSID);
|
||||||
ie_tlv_builder_set_data(&builder, ap->ssid, strlen(ap->ssid));
|
ie_tlv_builder_set_data(&builder, ap->config->ssid,
|
||||||
|
strlen(ap->config->ssid));
|
||||||
|
|
||||||
/* Supported Rates IE */
|
/* Supported Rates IE */
|
||||||
ie_tlv_builder_next(&builder, IE_TYPE_SUPPORTED_RATES);
|
ie_tlv_builder_next(&builder, IE_TYPE_SUPPORTED_RATES);
|
||||||
@ -343,7 +356,7 @@ static size_t ap_build_beacon_pr_head(struct ap_state *ap,
|
|||||||
|
|
||||||
/* DSSS Parameter Set IE for DSSS, HR, ERP and HT PHY rates */
|
/* DSSS Parameter Set IE for DSSS, HR, ERP and HT PHY rates */
|
||||||
ie_tlv_builder_next(&builder, IE_TYPE_DSSS_PARAMETER_SET);
|
ie_tlv_builder_next(&builder, IE_TYPE_DSSS_PARAMETER_SET);
|
||||||
ie_tlv_builder_set_data(&builder, &ap->channel, 1);
|
ie_tlv_builder_set_data(&builder, &ap->config->channel, 1);
|
||||||
|
|
||||||
ie_tlv_builder_finalize(&builder, &len);
|
ie_tlv_builder_finalize(&builder, &len);
|
||||||
return 36 + len;
|
return 36 + len;
|
||||||
@ -375,7 +388,8 @@ static uint32_t ap_send_mgmt_frame(struct ap_state *ap,
|
|||||||
struct l_genl_msg *msg;
|
struct l_genl_msg *msg;
|
||||||
uint32_t ifindex = netdev_get_ifindex(ap->netdev);
|
uint32_t ifindex = netdev_get_ifindex(ap->netdev);
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint32_t ch_freq = scan_channel_to_freq(ap->channel, SCAN_BAND_2_4_GHZ);
|
uint32_t ch_freq = scan_channel_to_freq(ap->config->channel,
|
||||||
|
SCAN_BAND_2_4_GHZ);
|
||||||
|
|
||||||
msg = l_genl_msg_new_sized(NL80211_CMD_FRAME, 128 + frame_len);
|
msg = l_genl_msg_new_sized(NL80211_CMD_FRAME, 128 + frame_len);
|
||||||
|
|
||||||
@ -386,7 +400,7 @@ static uint32_t ap_send_mgmt_frame(struct ap_state *ap,
|
|||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK,
|
l_genl_msg_append_attr(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK,
|
||||||
0, NULL);
|
0, NULL);
|
||||||
|
|
||||||
if (ap->no_cck_rates)
|
if (ap->config->no_cck_rates)
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_TX_NO_CCK_RATE, 0,
|
l_genl_msg_append_attr(msg, NL80211_ATTR_TX_NO_CCK_RATE, 0,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
@ -446,7 +460,8 @@ static void ap_start_rsna(struct sta_state *sta, const uint8_t *gtk_rsc)
|
|||||||
sta->hs = netdev_handshake_state_new(netdev);
|
sta->hs = netdev_handshake_state_new(netdev);
|
||||||
|
|
||||||
handshake_state_set_event_func(sta->hs, ap_handshake_event, sta);
|
handshake_state_set_event_func(sta->hs, ap_handshake_event, sta);
|
||||||
handshake_state_set_ssid(sta->hs, (void *)ap->ssid, strlen(ap->ssid));
|
handshake_state_set_ssid(sta->hs, (void *) ap->config->ssid,
|
||||||
|
strlen(ap->config->ssid));
|
||||||
handshake_state_set_authenticator(sta->hs, true);
|
handshake_state_set_authenticator(sta->hs, true);
|
||||||
handshake_state_set_authenticator_ie(sta->hs, bss_rsne);
|
handshake_state_set_authenticator_ie(sta->hs, bss_rsne);
|
||||||
handshake_state_set_supplicant_ie(sta->hs, sta->assoc_rsne);
|
handshake_state_set_supplicant_ie(sta->hs, sta->assoc_rsne);
|
||||||
@ -900,8 +915,8 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rates || !ssid || !rsn || ssid_len != strlen(ap->ssid) ||
|
if (!rates || !ssid || !rsn || ssid_len != strlen(ap->config->ssid) ||
|
||||||
memcmp(ssid, ap->ssid, ssid_len)) {
|
memcmp(ssid, ap->config->ssid, ssid_len)) {
|
||||||
err = MMPDU_REASON_CODE_INVALID_IE;
|
err = MMPDU_REASON_CODE_INVALID_IE;
|
||||||
goto bad_frame;
|
goto bad_frame;
|
||||||
}
|
}
|
||||||
@ -1125,8 +1140,8 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
|
|||||||
|
|
||||||
if (!ssid || ssid_len == 0) /* Wildcard SSID */
|
if (!ssid || ssid_len == 0) /* Wildcard SSID */
|
||||||
match = true;
|
match = true;
|
||||||
else if (ssid && ssid_len == strlen(ap->ssid) && /* Specific SSID */
|
else if (ssid && ssid_len == strlen(ap->config->ssid) && /* One SSID */
|
||||||
!memcmp(ssid, ap->ssid, ssid_len))
|
!memcmp(ssid, ap->config->ssid, ssid_len))
|
||||||
match = true;
|
match = true;
|
||||||
else if (ssid_list) { /* SSID List */
|
else if (ssid_list) { /* SSID List */
|
||||||
ie_tlv_iter_init(&iter, ssid_list, ssid_list_len);
|
ie_tlv_iter_init(&iter, ssid_list, ssid_list_len);
|
||||||
@ -1138,15 +1153,16 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
|
|||||||
ssid = (const char *) ie_tlv_iter_get_data(&iter);
|
ssid = (const char *) ie_tlv_iter_get_data(&iter);
|
||||||
ssid_len = ie_tlv_iter_get_length(&iter);
|
ssid_len = ie_tlv_iter_get_length(&iter);
|
||||||
|
|
||||||
if (ssid_len == strlen(ap->ssid) &&
|
if (ssid_len == strlen(ap->config->ssid) &&
|
||||||
!memcmp(ssid, ap->ssid, ssid_len)) {
|
!memcmp(ssid, ap->config->ssid,
|
||||||
|
ssid_len)) {
|
||||||
match = true;
|
match = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dsss_channel != 0 && dsss_channel != ap->channel)
|
if (dsss_channel != 0 && dsss_channel != ap->config->channel)
|
||||||
match = false;
|
match = false;
|
||||||
|
|
||||||
if (!match)
|
if (!match)
|
||||||
@ -1247,14 +1263,15 @@ static void ap_auth_cb(const struct mmpdu_header *hdr, const void *body,
|
|||||||
memcmp(hdr->address_3, bssid, 6))
|
memcmp(hdr->address_3, bssid, 6))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ap->authorized_macs) {
|
if (ap->config->authorized_macs_num) {
|
||||||
int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < ap->authorized_macs_num; i++)
|
for (i = 0; i < ap->config->authorized_macs_num; i++)
|
||||||
if (!memcmp(from, ap->authorized_macs + i * 6, 6))
|
if (!memcmp(from, ap->config->authorized_macs + i * 6,
|
||||||
|
6))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (i == ap->authorized_macs_num) {
|
if (i == ap->config->authorized_macs_num) {
|
||||||
ap_auth_reply(ap, from, MMPDU_REASON_CODE_UNSPECIFIED);
|
ap_auth_reply(ap, from, MMPDU_REASON_CODE_UNSPECIFIED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1374,7 +1391,8 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
|
|||||||
uint32_t nl_akm = CRYPTO_AKM_PSK;
|
uint32_t nl_akm = CRYPTO_AKM_PSK;
|
||||||
uint32_t wpa_version = NL80211_WPA_VERSION_2;
|
uint32_t wpa_version = NL80211_WPA_VERSION_2;
|
||||||
uint32_t auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
|
uint32_t auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
|
||||||
uint32_t ch_freq = scan_channel_to_freq(ap->channel, SCAN_BAND_2_4_GHZ);
|
uint32_t ch_freq = scan_channel_to_freq(ap->config->channel,
|
||||||
|
SCAN_BAND_2_4_GHZ);
|
||||||
uint32_t ch_width = NL80211_CHAN_WIDTH_20;
|
uint32_t ch_width = NL80211_CHAN_WIDTH_20;
|
||||||
|
|
||||||
static const uint8_t bcast_addr[6] = {
|
static const uint8_t bcast_addr[6] = {
|
||||||
@ -1389,7 +1407,7 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
cmd = l_genl_msg_new_sized(NL80211_CMD_START_AP, 256 + head_len +
|
cmd = l_genl_msg_new_sized(NL80211_CMD_START_AP, 256 + head_len +
|
||||||
tail_len + strlen(ap->ssid));
|
tail_len + strlen(ap->config->ssid));
|
||||||
|
|
||||||
/* SET_BEACON attrs */
|
/* SET_BEACON attrs */
|
||||||
l_genl_msg_append_attr(cmd, NL80211_ATTR_BEACON_HEAD, head_len, head);
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_BEACON_HEAD, head_len, head);
|
||||||
@ -1403,8 +1421,8 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
|
|||||||
&ap->beacon_interval);
|
&ap->beacon_interval);
|
||||||
l_genl_msg_append_attr(cmd, NL80211_ATTR_DTIM_PERIOD, 4, &dtim_period);
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_DTIM_PERIOD, 4, &dtim_period);
|
||||||
l_genl_msg_append_attr(cmd, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
||||||
l_genl_msg_append_attr(cmd, NL80211_ATTR_SSID, strlen(ap->ssid),
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_SSID, strlen(ap->config->ssid),
|
||||||
ap->ssid);
|
ap->config->ssid);
|
||||||
l_genl_msg_append_attr(cmd, NL80211_ATTR_HIDDEN_SSID, 4,
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_HIDDEN_SSID, 4,
|
||||||
&hidden_ssid);
|
&hidden_ssid);
|
||||||
l_genl_msg_append_attr(cmd, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, 4,
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, 4,
|
||||||
@ -1462,11 +1480,13 @@ static void ap_mlme_notify(struct l_genl_msg *msg, void *user_data)
|
|||||||
* @event_func is required and must react to AP_EVENT_START_FAILED
|
* @event_func is required and must react to AP_EVENT_START_FAILED
|
||||||
* and AP_EVENT_STOPPING by forgetting the ap_state struct, which
|
* and AP_EVENT_STOPPING by forgetting the ap_state struct, which
|
||||||
* is going to be freed automatically.
|
* is going to be freed automatically.
|
||||||
* @channel is optional.
|
* In the @config struct only .ssid and .psk need to be non-NUL,
|
||||||
|
* other fields are optional. If @ap_start succeeds, the returned
|
||||||
|
* ap_state takes ownership of @config and the caller shouldn't
|
||||||
|
* free it or any of the memory pointed to by its members (they
|
||||||
|
* also can't be static).
|
||||||
*/
|
*/
|
||||||
struct ap_state *ap_start(struct netdev *netdev, const char *ssid,
|
struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
|
||||||
const char *psk, int channel, bool no_cck_rates,
|
|
||||||
const uint8_t **authorized_macs,
|
|
||||||
ap_event_func_t event_func, void *user_data)
|
ap_event_func_t event_func, void *user_data)
|
||||||
{
|
{
|
||||||
struct ap_state *ap;
|
struct ap_state *ap;
|
||||||
@ -1476,30 +1496,14 @@ struct ap_state *ap_start(struct netdev *netdev, const char *ssid,
|
|||||||
|
|
||||||
ap = l_new(struct ap_state, 1);
|
ap = l_new(struct ap_state, 1);
|
||||||
ap->nl80211 = l_genl_family_new(iwd_get_genl(), NL80211_GENL_NAME);
|
ap->nl80211 = l_genl_family_new(iwd_get_genl(), NL80211_GENL_NAME);
|
||||||
ap->ssid = l_strdup(ssid);
|
ap->config = config;
|
||||||
ap->netdev = netdev;
|
ap->netdev = netdev;
|
||||||
ap->no_cck_rates = no_cck_rates;
|
|
||||||
ap->event_func = event_func;
|
ap->event_func = event_func;
|
||||||
ap->user_data = user_data;
|
ap->user_data = user_data;
|
||||||
|
|
||||||
if (channel)
|
if (!config->channel)
|
||||||
ap->channel = channel;
|
|
||||||
else {
|
|
||||||
/* TODO: Start a Get Survey to decide the channel */
|
/* TODO: Start a Get Survey to decide the channel */
|
||||||
ap->channel = 6;
|
config->channel = 6;
|
||||||
}
|
|
||||||
|
|
||||||
if (authorized_macs) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; authorized_macs[i]; i++);
|
|
||||||
ap->authorized_macs = l_malloc(i * 6);
|
|
||||||
ap->authorized_macs_num = i;
|
|
||||||
|
|
||||||
for (i = 0; authorized_macs[i]; i++)
|
|
||||||
memcpy(ap->authorized_macs + i * 6, authorized_macs[i],
|
|
||||||
6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Add all ciphers supported by wiphy */
|
/* TODO: Add all ciphers supported by wiphy */
|
||||||
ap->ciphers = wiphy_select_cipher(wiphy, 0xffff);
|
ap->ciphers = wiphy_select_cipher(wiphy, 0xffff);
|
||||||
@ -1507,7 +1511,7 @@ struct ap_state *ap_start(struct netdev *netdev, const char *ssid,
|
|||||||
ap->beacon_interval = 100;
|
ap->beacon_interval = 100;
|
||||||
|
|
||||||
/* TODO: Pick from actual supported rates */
|
/* TODO: Pick from actual supported rates */
|
||||||
if (no_cck_rates) {
|
if (config->no_cck_rates) {
|
||||||
l_uintset_put(ap->rates, 12); /* 6 Mbps*/
|
l_uintset_put(ap->rates, 12); /* 6 Mbps*/
|
||||||
l_uintset_put(ap->rates, 18); /* 9 Mbps*/
|
l_uintset_put(ap->rates, 18); /* 9 Mbps*/
|
||||||
l_uintset_put(ap->rates, 24); /* 12 Mbps*/
|
l_uintset_put(ap->rates, 24); /* 12 Mbps*/
|
||||||
@ -1523,8 +1527,8 @@ struct ap_state *ap_start(struct netdev *netdev, const char *ssid,
|
|||||||
l_uintset_put(ap->rates, 22); /* 11 Mbps*/
|
l_uintset_put(ap->rates, 22); /* 11 Mbps*/
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crypto_psk_from_passphrase(psk, (uint8_t *) ssid, strlen(ssid),
|
if (crypto_psk_from_passphrase(config->psk, (uint8_t *) config->ssid,
|
||||||
ap->pmk) < 0)
|
strlen(config->ssid), ap->pmk) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
||||||
@ -1757,6 +1761,7 @@ static struct l_dbus_message *ap_dbus_start(struct l_dbus *dbus,
|
|||||||
{
|
{
|
||||||
struct ap_if_data *ap_if = user_data;
|
struct ap_if_data *ap_if = user_data;
|
||||||
const char *ssid, *wpa2_psk;
|
const char *ssid, *wpa2_psk;
|
||||||
|
struct ap_config *config;
|
||||||
|
|
||||||
if (ap_if->ap && ap_if->ap->started)
|
if (ap_if->ap && ap_if->ap->started)
|
||||||
return dbus_error_already_exists(message);
|
return dbus_error_already_exists(message);
|
||||||
@ -1767,10 +1772,15 @@ static struct l_dbus_message *ap_dbus_start(struct l_dbus *dbus,
|
|||||||
if (!l_dbus_message_get_arguments(message, "ss", &ssid, &wpa2_psk))
|
if (!l_dbus_message_get_arguments(message, "ss", &ssid, &wpa2_psk))
|
||||||
return dbus_error_invalid_args(message);
|
return dbus_error_invalid_args(message);
|
||||||
|
|
||||||
ap_if->ap = ap_start(ap_if->netdev, ssid, wpa2_psk, 0, false, NULL,
|
config = l_new(struct ap_config, 1);
|
||||||
ap_if_event_func, ap_if);
|
config->ssid = l_strdup(ssid);
|
||||||
if (!ap_if->ap)
|
config->psk = l_strdup(wpa2_psk);
|
||||||
|
|
||||||
|
ap_if->ap = ap_start(ap_if->netdev, config, ap_if_event_func, ap_if);
|
||||||
|
if (!ap_if->ap) {
|
||||||
|
ap_config_free(config);
|
||||||
return dbus_error_invalid_args(message);
|
return dbus_error_invalid_args(message);
|
||||||
|
}
|
||||||
|
|
||||||
ap_if->pending = l_dbus_message_ref(message);
|
ap_if->pending = l_dbus_message_ref(message);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
15
src/ap.h
15
src/ap.h
@ -45,9 +45,18 @@ typedef void (*ap_event_func_t)(enum ap_event_type type, const void *event_data,
|
|||||||
void *user_data);
|
void *user_data);
|
||||||
typedef void (*ap_stopped_func_t)(void *user_data);
|
typedef void (*ap_stopped_func_t)(void *user_data);
|
||||||
|
|
||||||
struct ap_state *ap_start(struct netdev *netdev, const char *ssid,
|
struct ap_config {
|
||||||
const char *psk, int channel, bool no_cck_rates,
|
char *ssid;
|
||||||
const uint8_t **authorized_macs,
|
char *psk;
|
||||||
|
uint8_t channel;
|
||||||
|
uint8_t *authorized_macs;
|
||||||
|
unsigned int authorized_macs_num;
|
||||||
|
bool no_cck_rates : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ap_config_free(struct ap_config *config);
|
||||||
|
|
||||||
|
struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
|
||||||
ap_event_func_t event_func, void *user_data);
|
ap_event_func_t event_func, void *user_data);
|
||||||
void ap_shutdown(struct ap_state *ap, ap_stopped_func_t stopped_func,
|
void ap_shutdown(struct ap_state *ap, ap_stopped_func_t stopped_func,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
Loading…
Reference in New Issue
Block a user