Hostapd Startup Process (I)
- An overview
- . -> setup_interface2
-
- hostapd_select_hw_mode
-
- hostapd_check_chans
-
- hostapd_is_usable_chans
-
- hostapd_is_usable_chan
- hostapd_check_ht_capab
-
- ieee80211n_check_40mhz
-
- ieee80211n_scan_channels_2g4
- hostapd_setup_interface_complete
An overview
Recently spent a lot of time to learn hostAPD 2.6 boot source code, with this blog to record the learning results. If have the wrong place of analysis, please big men point out to discuss together.
This series mainly analyzes the interface startup process of HostAPD, and the general function calls are as follows:
hostapd_setup_interface ->
setup_interface->
setup_interface2->
1.Hostapd_select_hw_mode ->hostapd_check_chans, check whether the ACS scan process is required. If you need an ACS scanreturn
2.Hostapd_check_ht_capab, which does not need to go to the branch of the ACS scan process3. hostapd_setup_interface_complete
->hostapd_setup_interface_complete_sync
Copy the code
Because involved in the function flow is very much, and want to analyze a little more detailed. Therefore, the whole process is divided into two parts to analyze:
The first part only looks at the acS-free startup process starting from hostAPd_setup_interface. The second part looks at the scan process starting with acs_init.
This article analyzes only the first part.
… -> setup_interface2
Hostapd_setup_interface will call setup_interface, which will then call setup_interface2. This section starts with setup_interface2.
static int setup_interface2(struct hostapd_iface *iface)
{
iface->wait_channel_update = 0;
if (hostapd_get_hw_features(iface)) {// Get hardware information
/* Not all drivers support this yet, so continue without hw * feature data. */
} else {// My device will go to the else branch
int ret = hostapd_select_hw_mode(iface);// Select the hardware mode
if (ret < 0) {// Exception handling
wpa_printf(MSG_ERROR, "Could not select hw_mode and "
"channel. (%d)", ret);
goto fail;
}
if (ret == 1) {// The ACS scan process is required under normal circumstances
wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
return 0;
}
ret = hostapd_check_ht_capab(iface);// The ACS scan process is not required, there are already selected channels
if (ret < 0)
goto fail;
if (ret == 1) {// After the ACS scan completes, the hostapd_setup_interface_complete function is also called
wpa_printf(MSG_DEBUG, "Interface initialization will "
"be completed in a callback");
return 0;
}
if (iface->conf->ieee80211h)
wpa_printf(MSG_DEBUG, "DFS support is enabled");
}
return hostapd_setup_interface_complete(iface, 0);// Initialize the end function
fail:
hostapd_set_state(iface, HAPD_IFACE_DISABLED);// Exception handling
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
if (iface->interfaces && iface->interfaces->terminate_on_error)
eloop_terminate();
return - 1;
}
Copy the code
See hostapd_select_hw_mode called by setup_interface2.
hostapd_select_hw_mode
int hostapd_select_hw_mode(struct hostapd_iface *iface)
{
int i;
if (iface->num_hw_features < 1)
return - 1;
if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G || iface->conf->ieee80211n || iface->conf->ieee80211ac) && iface->conf->channel == 14) {
wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT on channel 14");
iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
iface->conf->ieee80211n = 0;
iface->conf->ieee80211ac = 0;
}// Exception handling
iface->current_mode = NULL;// Set the value to NULL
for (i = 0; i < iface->num_hw_features; i++) {// Hw_features of the current interface are iterated
Struct hostapd_iface {struct hostapd_iface {... . struct hostapd_hw_modes *hw_features; // Hw_modes int num_hw_features; // The number of hw_modes supported by the interface... . } * /
struct hostapd_hw_modes *mode = &iface->hw_features[i];
if (mode->mode == iface->conf->hw_mode) {
// If hw_mode configured in hostapd.conf matches the mode supported by the current interface successfully,
// Assign this value to the current mode of the interface and break the loop
iface->current_mode = mode;
break; }}if (iface->current_mode == NULL) {// If there is no matching pattern, an error is reported, and -2 is returned for exception handling
if(! (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) || ! (iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) { wpa_printf(MSG_ERROR,"Hardware does not support configured mode");
hostapd_logger(iface->bss[0].NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING,
"Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)", (int) iface->conf->hw_mode);
return 2 -; }}switch (hostapd_check_chans(iface)) {// call hostapd_check_chans
// Determines whether acs scan is required to select channels. If the hostapd.conf channel is set to 0, the acs scanning algorithm flow is required
// If no channel is configured in hostapd.conf, the channel is selected and the channel is valid
case HOSTAPD_CHAN_VALID: // Channel is not configured
return 0;
case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
return 1;
case HOSTAPD_CHAN_INVALID: // Exception condition handling
default:
hostapd_notify_bad_chans(iface);
return - 3; }}Copy the code
Let’s see how we check the channel. hostapd_select_hw_mode->hostapd_check_chans
hostapd_check_chans
static enum hostapd_chan_status hostapd_check_chans(struct hostapd_iface *iface)
{
if (iface->conf->channel) {// If hostapd.conf is configured with a non-zero channel value, that is, the channel where the AP resides is specified
if (hostapd_is_usable_chans(iface))// Call hostapd_is_usable_chans to check whether the channel is available
return HOSTAPD_CHAN_VALID;
else
return HOSTAPD_CHAN_INVALID;
}
/* * The user set channel=0 or channel=acs_survey * which is used to trigger ACS. */
switch (acs_init(iface)) {// If the configured channel value is 0, the ACS algorithm flow needs to be initialized
case HOSTAPD_CHAN_ACS: // The ACS algorithm is required
return HOSTAPD_CHAN_ACS;
case HOSTAPD_CHAN_VALID: // If acs is not needed, an exception occurs, and INVALID is returned
case HOSTAPD_CHAN_INVALID:
default:
returnHOSTAPD_CHAN_INVALID; }}Copy the code
The hostapd_is_usable_chans function is used to check whether the channel specified by iface->conf->channel interface is available. This function internally checks whether the channel specified by iface->conf->channel interface is available.
hostapd_is_usable_chans
static int hostapd_is_usable_chans(struct hostapd_iface *iface)
{
if(! hostapd_is_usable_chan(iface, iface->conf->channel,1))// Check whether the primary channel is available
return 0;// Return 0 if not available
if(! iface->conf->secondary_channel){// If the primary channel is available, check whether the secondary channel in the CONF is configured
return 1;// If secondary_channel == 0, this is a 20M hotspot
}// Secondary_channel! =0, and you need to check whether the secondary channel is available
return hostapd_is_usable_chan(iface, iface->conf->channel +
iface->conf->secondary_channel * 4.0);
// If secondary_channel is not 0, check whether it is available
}
Copy the code
Iface ->conf->secondary_channel is assigned in hostapd_config_ht_capab. When the hot spot is first started, the hostapd.conf file is read and secondary_channel is assigned according to ht_capab.
Hostapd_config_ht_capab:
static int hostapd_config_ht_capab(struct hostapd_config *conf,
const char *capab)
{
if (os_strstr(capab, "[LDPC]"))
conf->ht_capab |= HT_CAP_INFO_LDPC_CODING_CAP;
if (os_strstr(capab, "[HT40-]")) {// If HT40- is configured, set secondary to -1
conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
conf->secondary_channel = - 1;
}
if (os_strstr(capab, "[HT40+]")) {// If HT40+ is configured, set secondary to 1
Ht_capab =[HT40-][HT40+] if (ht_capab=[HT40-][HT40+]
conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
conf->secondary_channel = 1; }... .// By default, it is 20M hotspot, secondary_channel = 0
return 0;
}
Copy the code
Moving on to channel availability judgments, hostAPd_is_usable_chans is called to hostapd_is_usable_chan to check for a single channel.
hostapd_is_usable_chan
static int hostapd_is_usable_chan(struct hostapd_iface *iface,
int channel, int primary)// Primary indicates whether it is a primary channel. A value of 1 indicates the primary channel
{
int i;
struct hostapd_channel_data *chan;
if(! iface->current_mode)// If current_mode == NULL, an exception occurs
return 0;
for (i = 0; i < iface->current_mode->num_channels; i++) {// Iterate through channels of the current interface
chan = &iface->current_mode->channels[i];
if(chan->chan ! = channel)// If the channel currently traversed is not the destination channel, skip it
continue;
if(! (chan->flag & HOSTAPD_CHAN_DISABLED))// If the destination channel is unavailable, return an error value of 1
return 1;
wpa_printf(MSG_DEBUG,
"%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s",
primary ? "" : "Configured HT40 secondary ",
i, chan->chan, chan->flag,
chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
}
return 0;
}
Copy the code
Back to the hostapd_check_chans function:
- After the channel check is complete, if acs channel selection is required, proceed to ACs_init.
- Otherwise, if the channel is specified, it returns VALID, but not INVALID.
Hostapd_select_hw_mode = hostapd_select_hw_mode
- Valid returns 0;
- Return 1 if ACS is required;
- Returns -3 if there is an exception.
To the upper setup_interface2:
- If ret is 1, the ACS scan is required. Return and wait for the process triggered after the scan is complete.
- Ret < 0, then an exception occurs.
- If ret is 0, the channel is normal and you can continue.
Moving on to the process without ACS scanning, enter the function hostAPd_check_ht_CAPab. (See HostAPD Startup Process (II) for the process requiring ACS)
hostapd_check_ht_capab
int hostapd_check_ht_capab(struct hostapd_iface *iface)
{
#ifdef CONFIG_IEEE80211N
int ret;
if(! iface->conf->ieee80211n)// If the interface does not support 802.11n, exit
return 0;
if(iface->current_mode->mode ! = HOSTAPD_MODE_IEEE80211B && iface->current_mode->mode ! = HOSTAPD_MODE_IEEE80211G && (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) { wpa_printf(MSG_DEBUG,"Disable HT capability [DSSS_CCK-40] on 5 GHz band");
iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ;
}
if(! ieee80211n_supported_ht_capab(iface))// Determine whether iface->current_mode->ht_capab and iface->conf->ht_capab support some HT attributes
return - 1;
#ifdef CONFIG_IEEE80211AC
if(! ieee80211ac_supported_vht_capab(iface))// Check whether some VHT attributes are supported
return - 1;
#endif /* CONFIG_IEEE80211AC */
ret = ieee80211n_check_40mhz(iface);// check whether 40M bandwidth is supported
if (ret)
return ret;
if(! ieee80211n_allowed_ht40_channel_pair(iface))return - 1;
#endif /* CONFIG_IEEE80211N */
return 0;
}
Copy the code
Continue to look at the IEEE80211N_check_40mhz function.
ieee80211n_check_40mhz
static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
{
struct wpa_driver_scan_params params;
int ret;
/* Check that HT40 is used and PRI / SEC switch is allowed */
if(! iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)return 0;// If hostapd.conf is configured with a bandwidth of 20M, no further check is required.
hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
"40 MHz channel");
os_memset(¶ms, 0.sizeof(params));
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
ieee80211n_scan_channels_2g4(iface, ¶ms);// Walk through the 2.4g channel and save the result to params
else
ieee80211n_scan_channels_5g(iface, ¶ms);// Walk through the channels on 5g
ret = hostapd_driver_scan(iface->bss[0], ¶ms);
os_free(params.freqs);
if (ret == -EBUSY) {
wpa_printf(MSG_ERROR,
"Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
ret, strerror(-ret));
iface->num_ht40_scan_tries = 1;
eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
eloop_register_timeout(1.0, ap_ht40_scan_retry, iface, NULL);
return 1;
}
if (ret < 0) {
wpa_printf(MSG_ERROR,
"Failed to request a scan of neighboring BSSes ret=%d (%s)",
ret, strerror(-ret));
return - 1;
}
iface->scan_cb = ieee80211n_check_scan;
return 1;
}
Copy the code
Let’s look at the traversal of the 2.4g channel. ieee80211n_check_40mhz->ieee80211n_scan_channels_2g4
ieee80211n_scan_channels_2g4
static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, struct wpa_driver_scan_params *params)
{
/* Scan only the affected frequency range */
int pri_freq, sec_freq;// Primary and secondary channel frequencies
int affected_start, affected_end;// The affected start and end frequencies
int i, pos;
struct hostapd_hw_modes *mode;
if (iface->current_mode == NULL)
return;
pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);// Get the frequency value corresponding to primary_channel number
if (iface->conf->secondary_channel > 0)// If secondary is set to 1, 40M will be added to pri_channel
sec_freq = pri_freq + 20;// Pri_channel and sec_channel occupy 20 MB bandwidth respectively. Therefore, calculate the frequency value of SEC according to the position between SEC and PRI
else// If secondary is set to -1, 40M will be added to pri_channel
sec_freq = pri_freq - 20;
/* * Note: Need to find the PRI channel also in cases where the affected * channel is the SEC channel of a 40 MHz BSS, So need to include the * scanning coverage here to be 40 MHz from the center frequency BSS SEC channel in this case, can also find PRI channel * therefore need to include from the center frequency to 40 MHz scan coverage */
affected_start = (pri_freq + sec_freq) / 2 - 40;
affected_end = (pri_freq + sec_freq) / 2 + 40;
wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
affected_start, affected_end);
mode = iface->current_mode;
params->freqs = os_calloc(mode->num_channels + 1.sizeof(int));
if (params->freqs == NULL)// Record the affected channel FREQ with params->freqs
return;
pos = 0;
for (i = 0; i < mode->num_channels; i++) {// Iterate over the list of channels supported by the current interface
struct hostapd_channel_data *chan = &mode->channels[i];
if (chan->flag & HOSTAPD_CHAN_DISABLED)// If the channel currently traversed is not available, skip it
continue;
if (chan->freq < affected_start ||
chan->freq > affected_end)// If the channel is not in the affected range, skip it
continue; params->freqs[pos++] = chan->freq; }}Copy the code
The calculation principle of the affected range in the code is shown below:
It can be seen from the above that this calculation method is feasible.
Ieee80211n_scan_channels_2g4 Returns:
- Params -> Freqs stores channel frequencies affected by the current 40M hot spot.
- Call hostapd_driver_scan to pass params in.
The hostapd_driver_scan function performs channel scanning. After that, determine the exception handling of the returned value. Assign the iface->scan operation to IEEE80211N_check_scan. (See the code analysis of HostAPD_DRIVER_SCAN in hostAPD Startup Process (2) for details.)
Return to hostapd_check_ht_capab:
- Ret is not 0 and returns directly to the upper layer, except when HT40 is not turned on or PRI/SEC switch does not allow.
- The ieEE80211N_ALLOwed_HT40_channel_pair function is generally not called here. The following process will also be called, and we will analyze it at that time.
Hostapd_check_ht_capab returns to setup_interface2:
-
If hostAPd_check_ht_CAPab returns a value less than zero, if -1, it indicates an exception.
-
IeEE80211N_check_40mhz
- If the value is 1, the value is 40M. Normally, wait for the scan check process.
- If the value is 0, the value is 20M. Hostapd_select_hw_mode -> hostapd_check_chans check whether the channel of 20M is available. If 0 is returned, the channel of 20M is directly returned.
If no scanning is required, the hostapd_setup_interface_complete function is directly called to end the interface initialization.
hostapd_setup_interface_complete
int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
{
struct hapd_interfaces *interfaces = iface->interfaces;
struct hostapd_data *hapd = iface->bss[0];
unsigned int i;
int not_ready_in_sync_ifaces = 0;
if(! iface->need_to_start_in_sync)returnhostapd_setup_interface_complete_sync(iface, err); . . .return 0;
}
Copy the code
Hostapd_setup_interface_complete will call hostapd_setup_interface_complete_sync, Hostapd_setup_interface_complete_sync “Completing interface initialization”. That’s it.
If a scan is required, the NL80211_CMD_TRIGGER_SCAN event will be triggered by hostAPD_driver_scan called in ieEE80211N_check_40mhz. The do_process_drv_event function receives the NL80211_CMD_TRIGGER_SCAN event, which enters a sequence of functions for the scan.
The specific scanning process and the checking process of scanning results can be found in the corresponding part of hostAPD startup Process (II).
After a scan process is complete, wPA_supplicant_Event receives an EVENT_SCAN_RESULTS event. Wpa_supplicant_event:
case EVENT_SCAN_RESULTS:
if (hapd->iface->scan_cb)
hapd->iface->scan_cb(hapd->iface);
break;
Copy the code
The iface->scan_cb function has been assigned to ieEE80211N_check_40mhz as ieEE80211N_check_scan That is, WPA_supplicant_Event calls ieEE80211N_check_SCAN to check the scan results.
For details about the IEEE80211N_CHECK_SCAN check process, see HostAPD Startup Process (2) in this article. After checking, the hostapd_setup_interface_complete function is called to end the interface initialization.