MTK有一个和其他android不一样的无线热点的实现。现在让我们从最顶层的界面到最底层的驱动来看看这个启动过程是如何的吧。
1. 界面上启动无线热点(应用程序部分)
我使用的界面是联想的个人热点(Personal Hotspot)的界面。源代码是反编译得到的。代码位于WifiApEnabler这个类里
public class WifiApEnabler {
public void setSoftapEnabled(boolean enable) {
}
}
第一步是停用wifi
final ContentResolver cr = mContext.getContentResolver();
/**
* Disable Wifi if enabling tethering
*/
int wifiState = mWifiManager.getWifiState();
if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) ||
(wifiState == WifiManager.WIFI_STATE_ENABLED))) {
mWifiManager.setWifiEnabled(false);
Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 1);
}
这一步有两个问题。wifi_saved_state是干什么的。另外一个是this.mWifiManager哪里来的
this.mWifiManager = ((WifiManager)paramTetherSettings.getSystemService("wifi"));
// public class TetherSettings extends PreferenceActivity
// 等价于 Context.getSystemService(Context.WIFI_SERVICE)
paramTetherSettings是一个TetherSettings的实例,所以有系统提供的getSystemService。
而WIFI_SAVED_STATE是用来保存在热点开启之前,wifi是开的还是关,以便在热点关闭之后决定是不是要重新把wifi打开。
第二步是开启热点
this.mWifiManagerEx.setRealObject(this.mWifiManager);
if (this.mWifiManagerEx.setWifiApEnabled(null, enable)) {
this.mCheckBox.setEnabled(false);
}
这一步是是把wifiManager的实例传递给WifiManagerEx,然后通过WifiManagerEx用反射去调用wifiManager的私有api,setWifiApEnabled。如果开启成功则把启用热点的checkbox禁用掉,防止重新启用。注意到setWifiApEnabled并没有把configuration做为参数传递过去。因为之前已经通过setWifiApConfiguration设置过了。
2. WifiManager 的 setWifiApEnabled 的实现 (框架的Java部分)
这个不是官方API的一部分。但是好像官方的实现里就有这个函数:
public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
try {
mService.setWifiApEnabled(wifiConfig, enabled);
return true;
} catch (RemoteException e) {
return false;
}
}
那么mService是什么呢?是getSystemService(Context.WIFI_SERVICE)
public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
enforceChangePermission();
mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
}
那mWifiStateMachine是什么呢?就是WifiStateMachine。
public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
mLastApEnableUid.set(Binder.getCallingUid());
if (enable) {
/* Argument is the state that is entered prior to load */
sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
} else {
sendMessage(CMD_STOP_AP);
/* Argument is the state that is entered upon success */
sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
}
}
看代码还是很清晰的。连发了两个message,一个加载驱动。一个是启动热点。那么是谁在监听这两个发出去的消息呢?
关于第一个CMD_LOAD_DRIVER这个命令,找到的实现是没有进行任何处理。
case CMD_START_AP:
transitionTo(mSoftApStartingState);
break;
那么应该就是调用了SoftApStartingState的enter方法了
class SoftApStartingState extends State {
@Override
public void enter() {
if (DBG) log(getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
final Message message = getCurrentMessage();
if (message.what == CMD_START_AP) {
final WifiConfiguration config = (WifiConfiguration) message.obj;
if (config == null) {
mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
} else {
mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
startSoftApWithConfig(config);
}
} else {
throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
}
}
}
居然又转了一道手,这次是交给了mWifiApConfigChannel。这个是什么?
private AsyncChannel mWifiApConfigChannel = new AsyncChannel();
mWifiApConfigChannel.connectSync(mContext, getHandler(), wifiApConfigStore.getMessenger());
那这消息过去到底是谁接收的啊?还是当前这个state machine接收的,因为getHandler返回的还是自己的handler。
mSmHandler = new SmHandler(looper, this);
那在softApStartingState这个状态下是如何处理CMD_REQUEST_AP_CONFIG的?我们来看一下:
case WifiStateMachine.CMD_REQUEST_AP_CONFIG:
mReplyChannel.replyToMessage(message,
WifiStateMachine.CMD_RESPONSE_AP_CONFIG, mWifiApConfig);
break;
挺简单的WifiApConfigStore监听了这个消息,然后把之前setWifiApConfiguration存着的mWifiApConfig用消息发回去了。
case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
WifiConfiguration config = (WifiConfiguration) message.obj;
if (config != null) {
startSoftApWithConfig(config);
} else {
loge("Softap config is null!");
sendMessage(CMD_START_AP_FAILURE);
}
break;
这次接收消息的是SoftApStartingState这个类,不过还是再次转手:
private void startSoftApWithConfig(final WifiConfiguration config) {
// start hostapd on a seperate thread
new Thread(new Runnable() {
public void run() {
if (DBG) Xlog.d(TAG, "startSoftApWithConfig, config:" + config);
try {
mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
} catch (Exception e) {
loge("Exception in softap start " + e);
try {
mNwService.stopAccessPoint(mInterfaceName);
mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
} catch (Exception e1) {
loge("Exception in softap re-start " + e1);
sendMessage(CMD_START_AP_FAILURE);
return;
}
}
if (DBG) log("Soft AP start successful");
sendMessage(CMD_START_AP_SUCCESS);
}
}, "startSoftApThread").start();
}
mNwService是NetworkManagementService的实例,继续:
public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
mStartRequest = true;
mStopRequest = false;
try {
//wifiFirmwareReload(wlanIface, "AP");
mConnector.doCommand(String.format("softap start " + wlanIface));
if (wifiConfig == null) {
mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
} else {
/**
* softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
* argv1 - wlan interface
* argv2 - softap interface
* argv3 - SSID
* argv4 - Security
* argv5 - Key
* argv6 - Channel
* argv7 - Channel Width
* argv8 - Preamble
* argv9 - Max SCB
*/
String str = String.format("softap set " + wlanIface + " " + softapIface +
" %s %s %s %s %s", convertQuotedString(wifiConfig.SSID),
getSecurityType(wifiConfig),
convertQuotedString(wifiConfig.preSharedKey),
wifiConfig.channel,
wifiConfig.channelWidth
);
mConnector.doCommand(str);
}
mConnector.doCommand(String.format("softap startap"));
} catch (NativeDaemonConnectorException e) {
throw new IllegalStateException("Error communicating to native daemon to start softap", e);
}
}
可以看到wifiFirmwareReload被注释掉了,看来不需要切换无线网卡的固件就可以启动AP。然后转交给了mConnector,这又是什么?
/**
* connector object for communicating with netd
*/
private NativeDaemonConnector mConnector;
看来是给netd了。看看怎么给的:
socket = new LocalSocket();
LocalSocketAddress address = new LocalSocketAddress(“netd”,
LocalSocketAddress.Namespace.RESERVED);
socket.connect(address);
mOutputStream = socket.getOutputStream();
command是通过这个socket写出去的,结果也是在这个socket的输出里读出来的。看来搞了半天就是开了一个socket连上了netd,然后给netd发了一条softap start的命令啊。然后又发了一条softap set命令做设置。
3. netd的softap start/softap set的实现 (框架的C++部分)
在netd的CommandListener.cpp里找到了命令派发的实现
if (!strcmp(argv[1], "start")) {
rc = sSoftapCtrl->startDriver(argv[2]);
} else if (!strcmp(argv[1], "set")) {
rc = sSoftapCtrl->setSoftap(argc, argv);
}
找到SoftApController.cpp:
int SoftapController::startDriver(char *iface)
{
int connectTries = 0;
// load p2p driver,
// WifiStateMachine will load wifi driver before starting softAP
::wifi_hotspot_load_driver();
// start the p2p_supplicant
LOGD("start the p2p_supplicant");
#if 0
#else
if (::wifi_hotspot_start_supplicant() < 0) {
LOGE("Softap driver start - failed to start p2p_supplicant");
return -1;
}
#endif
// connect to the p2p_supplicant
while (true) {
LOGD("try to connect to p2p_supplicant");
if (::wifi_hotspot_connect_to_supplicant() == 0) {
LOGD("connect to p2p_supplicant");
::wifi_hotspot_close_supplicant_connection_no_wait();
return 0;
}
//maximum delay 12s
if (connectTries++ < 40) {
sched_yield();
LOGD("softap sleep %d us\n", AP_CONNECT_TO_SUPPLICANT_DELAY);
usleep(AP_CONNECT_TO_SUPPLICANT_DELAY);
} else {
break;
}
}
LOGD("Softap driver start - failed to connect to p2p_supplicant");
return -1;
}
wifi_hotspot_load_driver是由libhardware_legacy提供的函数实现是:
int wifi_hotspot_load_driver()
{
property_get(
rHotspotSuppPara.acIfPropName,
rHotspotSuppInfo.acIfName,
rHotspotSuppPara.acIfDefName);
return halDoCommand("load hotspot");
}
看来又转了一道手给hald,怎么转的?
int halDoCommand(const char *cmd)
{
int sock;
char *final_cmd;
if ((sock = socket_local_client(HAL_DAEMON_NAME,
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM)) < 0) {
LOGE("Error connecting (%s)", strerror(errno));
//exit(4);
/*return error if hald is not existing*/
return errno;
}
asprintf(&final_cmd, "%s %s", HAL_DAEMON_CMD, cmd);
LOGD("Hal cmd: \"%s\"", final_cmd);
if (write(sock, final_cmd, strlen(final_cmd) + 1) < 0) {
free(final_cmd);
close(sock);
LOGE("Hal cmd error: \"%s\"", final_cmd);
return errno;
}
free(final_cmd);
return halDoMonitor(sock);
}
又是一个socket,实现挺简单的。hald收到了命令之后呢?先找到派发命令的代码:
if (!strcmp(argv[1], "load")) {
pthread_mutex_lock(&mLock);
rc = sHaldCtrl->loadDriver(argv[2]);
pthread_mutex_unlock(&mLock);
}
这个loadDriver的实现还挺长的:
int HaldController::loadDriver(const char *ifname){
#ifdef CFG_ENABLE_NVRAM_CHECK
int nvram_restore_ready_retry = 0;
char nvram_init_val[32];
static bool fg_is_nvram_chk_failed = false;
LOGD("Check NvRAM status.");
while(nvram_restore_ready_retry < MAX_NVRAM_RESTORE_READY_RETRY_NUM) {
nvram_restore_ready_retry++;
property_get("nvram_init", nvram_init_val, NULL);
if(strcmp(nvram_init_val, "Ready") == 0) {
LOGD("NvRAM is READY!");
fg_is_nvram_chk_failed = false;
break;
} else if (fg_is_nvram_chk_failed){
LOGE("NvRAM status check is still failed! NvRAM content may be WRONG!");
break;
} else {
usleep(NVRAM_RESTORE_POLLING_TIME_USEC);
}
}
if(nvram_restore_ready_retry >= MAX_NVRAM_RESTORE_READY_RETRY_NUM) {
fg_is_nvram_chk_failed = true;
LOGE("NvRAM status check timeout(%dus)! NvRAM content may be WRONG!", MAX_NVRAM_RESTORE_READY_RETRY_NUM * NVRAM_RESTORE_POLLING_TIME_USEC);
}
#endif
#ifdef MTK_SDIORETRY_SUPPORT
//write the sdio retry setting. add by mtk80743
{
sdio_retry = NVM_GetFileDesc(iFileSDIO_RETRYLID, &rec_size, &rec_num, true);
if(read(sdio_retry, &sdioRetrySetting, rec_num*rec_size) < 0){
LOGD("read iFileSDIO_RETRYLID failed %s\n", strerror(errno));
}else{
if(sdioRetrySetting.retry_flag == 0 && sdioRetrySetting.sdio.clk_src == 0){
needReadSetting = 1;
}else{
needReadSetting = 0;
writeRetrySetting();
}
}
NVM_CloseFileDesc(sdio_retry);
}
#endif
/*LOAD WIFI*/
if (!strcmp(ifname, "wifi")) {
/*if wifi or its sub function is on, no need to load wifi again*/
if(isHotspotActive || isP2pActive || isWifiActive) {
/*do nothing*/
LOGE("Wifi driver is already loaded, no need to load again.");
isWifiActive = true;
return 0;
/*load wifi driver*/
} else {
LOGD("Start load wifi driver.");
/*load wifi driver*/
sDriverCtrl->load(NETWORK_IFACE_WIFI);
/*turn on wifi power*/
powerOn();
/*set flag*/
isWifiActive = true;
return 0;
}
/*LOAD HOTSPOT*/
} else if (!strcmp(ifname, "hotspot")) {
if(isHotspotActive) {
LOGE("Hotspot driver is already loaded, no need to load again.");
return 0;
}
/*if wifi is not on, MUST load wifi and turn on wifi power first*/
if(!isWifiActive){
sDriverCtrl->load(NETWORK_IFACE_WIFI);
powerOn();
}
/*if p2p is on, unload p2p driver*/
if(isP2pActive) {
LOGE("Unload P2P driver first.");
sDriverCtrl->unload(NETWORK_IFACE_P2P);
isP2pActive = false;
}
/*load hotspot driver*/
LOGD("Start load hotspot driver.");
sDriverCtrl->load(NETWORK_IFACE_HOTSPOT);
isHotspotActive = true;
#ifdef CFG_ENABLE_RFKILL_IF_FOR_CFG80211
/*enable hotspot rfkill interface for cfg80211*/
sRfkillCtrl->setAllState(1);
#endif
return 0;
/*LOAD P2P*/
} else if (!strcmp(ifname, "p2p")) {
if(isP2pActive) {
LOGE("P2P driver is already loaded, no need to load again.");
return 0;
}
/*if wifi is not on, MUST load wifi and turn on wifi power first*/
if(!isWifiActive){
sDriverCtrl->load(NETWORK_IFACE_WIFI);
powerOn();
}
/*if hotspot is on, unload hotspot driver*/
if(isHotspotActive) {
LOGE("Unload Hotspot driver first.");
sDriverCtrl->unload(NETWORK_IFACE_HOTSPOT);
isHotspotActive = false;
}
/*load p2p driver*/
LOGD("Start load P2P driver.");
sDriverCtrl->load(NETWORK_IFACE_P2P);
isP2pActive = true;
#ifdef CFG_ENABLE_RFKILL_IF_FOR_CFG80211
/*enable p2p rfkill interface for cfg80211*/
sRfkillCtrl->setAllState(1);
#endif
return 0;
}
return -1;
}
反正最后就是按照hotspot模式加载了驱动。最终调用的命令是insmod,rmmod。
回过头去看加载完驱动之后SoftApController还干什么事情:
if (::wifi_hotspot_start_supplicant() < 0) {
LOGE("Softap driver start - failed to start p2p_supplicant");
return -1;
}
这个命令也是libhardware_legacy提供的,实现是:
int wifi_hotspot_start_supplicant()
{
return start_supplicant(&rHotspotSuppPara, rHotspotSuppInfo.acIfName);
}
int start_supplicant(const struct _SUPPLICANT_PARA_T * prTar, const char *pcIface)
{
char daemon_cmd[PROPERTY_VALUE_MAX];
char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
int count = 200; /* wait at most 20 seconds for completion */
#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
const prop_info *pi;
unsigned serial = 0;
#endif
/* Check whether already running */
if (property_get(prTar->acSuppPropName, supp_status, NULL)
&& strcmp(supp_status, "running") == 0) {
return 0;
}
/* Before starting the daemon, make sure its config file exists */
if (ensure_config_file_exists(prTar->acSuppConfigFile, prTar->acSuppConfigTemplate) < 0) {
LOGE("[%s] %s will not be enabled", pcIface, prTar->acSuppName);
return -1;
}
/*Set interface UP (ifconfig "iface" up)*/
if(set_iface(pcIface, 1) < 0 ) {
/*If interface up failed, skip the following actions*/
return -1;
}
/* Clear out any stale socket files that might be left over. */
LOGD("[%s] clear out stale sockets with prefix \"%s\" in %s", pcIface, prTar->acSuppCtrlPrefix, CONFIG_CTRL_IFACE_CLIENT_DIR);
ctrl_cleanup(prTar->acSuppCtrlPrefix);
LOGI("[%s] start %s", pcIface, prTar->acSuppName);
#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
/*
* Get a reference to the status property, so we can distinguish
* the case where it goes stopped => running => stopped (i.e.,
* it start up, but fails right away) from the case in which
* it starts in the stopped state and never manages to start
* running at all.
*/
pi = __system_property_find(prTar->acSuppPropName);
if (pi != NULL) {
serial = pi->serial;
}
#endif
property_get(prTar->acIfPropName, (char *)pcIface, prTar->acIfDefName);
snprintf(daemon_cmd, PROPERTY_VALUE_MAX, prTar->acSuppDeamonCmd, prTar->acSuppName, pcIface, prTar->acSuppConfigFile);
property_set("ctl.start", daemon_cmd);
LOGD("[%s] supplicant start command: \"%s\"", pcIface, daemon_cmd);
//property_set("ctl.start", prTar->acSuppName);
sched_yield();
while (count-- > 0) {
#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
if (pi == NULL) {
pi = __system_property_find(prTar->acSuppPropName);
}
if (pi != NULL) {
__system_property_read(pi, NULL, supp_status);
if (strcmp(supp_status, "running") == 0) {
return 0;
} else if (pi->serial != serial &&
strcmp(supp_status, "stopped") == 0) {
return -1;
}
}
#else
if (property_get(prTar->acSuppPropName, supp_status, NULL)) {
if (strcmp(supp_status, "running") == 0)
return 0;
}
#endif
usleep(100000);
}
return -1;
}
最终调用的命令是什么得看下面这个结构体的内容:
static const SUPPLICANT_PARA_T rHotspotSuppPara = {
"ap0",
"wifi.tethering.interface",
"/data/misc/p2p_supplicant",
"p2p_supplicant",
"init.svc.p2p_supplicant",
"/system/etc/wifi/p2p_supplicant.conf",
"/data/misc/wifi/p2p_supplicant.conf",
"%s:-Dnl80211 -i%s -c%s -dd",
#ifdef CFG_SUPPORT_CONCURRENT_NETWORK
"p2p_ctrl"
#else
"wpa_ctrl"
#endif
};
typedef struct _SUPPLICANT_PARA_T {
char acIfDefName[PARA_LENGTH]; //Default interface name
char acIfPropName[PARA_LENGTH]; //Interface property name
char acIfDir[PARA_LENGTH]; //Supplicant path
char acSuppName[PARA_LENGTH]; //Supplicant name
char acSuppPropName[PARA_LENGTH]; //Supplicant property name
char acSuppConfigTemplate[PARA_LENGTH]; //Supplicant config template file path
char acSuppConfigFile[PARA_LENGTH]; //Supplicant config file path
char acSuppDeamonCmd[PROPERTY_VALUE_MAX]; //Supplicant deamon start command
char acSuppCtrlPrefix[PARA_LENGTH]; //Ctrl socket prefix
} SUPPLICANT_PARA_T, *P_SUPPLICANT_PARA_T;
还有这个:
static SUPPLICANT_INFO_T rHotspotSuppInfo = {
"ap0",
NULL,
NULL,
#ifdef CFG_SUPPORT_CONCURRENT_NETWORK
{
p2p_ctrl_open,
p2p_ctrl_close,
p2p_ctrl_request,
p2p_ctrl_recv,
p2p_ctrl_attach
},
#else
{ //Supplicant ctrl API
wpa_ctrl_open,
wpa_ctrl_close,
wpa_ctrl_request,
wpa_ctrl_recv,
wpa_ctrl_attach
},
#endif
{ //exit socket pair
-1,
-1
}
};
typedef struct _SUPPLICANT_INFO_T {
char acIfName[PROPERTY_VALUE_MAX]; //Interface name for using
P_WPA_CTRL_T prCtrlConn; //Supplicant ctrl connection
P_WPA_CTRL_T prMonitorConn; //Supplicant monitor connection
SUPPLICANT_CTRL_T rSuppCtrl; //Supplicant ctrl api
int aucExitSockets[2]; /* socket pair used to exit from a blocking read */
} SUPPLICANT_INFO_T, *P_SUPPLICANT_INFO_T;
最终是启动了一个wpa_supplicant进程。SoftApController会在启动之后等待一段时间,直到wpa_supplicant的socket可以连上了。检查wpa_supplicant连接的代码有:
int connect_to_supplicant(const struct _SUPPLICANT_PARA_T *prTar, P_SUPPLICANT_INFO_T prTarInfo)
{
char ifname[256];
char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
/* Make sure supplicant is running */
if (!property_get(prTar->acSuppPropName, supp_status, NULL)
|| strcmp(supp_status, "running") != 0) {
LOGE("[%s] %s not running, cannot connect", prTarInfo->acIfName, prTar->acSuppName);
return -1;
}
LOGI("[%s] Connect to %s", prTarInfo->acIfName, prTar->acSuppName);
snprintf(ifname, sizeof(ifname), "%s/%s", prTar->acIfDir, prTarInfo->acIfName);
LOGD("[%s] ctrl_open %s", prTarInfo->acIfName, ifname);
{
int count = CONNECT_TO_SUPP_POLLING_LOOP;
while (count-- > 0) {
prTarInfo->prCtrlConn = prTarInfo->rSuppCtrl.ctrl_open(ifname);
if (prTarInfo->prCtrlConn) {
LOGD("[%s] Open control connection to %s successfully\n", prTarInfo->acIfName, ifname);
break;
}
LOGD("[%s] Open control connection fail (%s). Sleep %dus\n", prTarInfo->acIfName, ifname, CONNECT_TO_SUPP_DELAY);
sched_yield();
usleep(CONNECT_TO_SUPP_DELAY);
}
}
if (prTarInfo->prCtrlConn == NULL) {
LOGE("[%s] Unable to open connection to supplicant on \"%s\": %s"
, prTarInfo->acIfName
, ifname
, strerror(errno));
return -1;
}
prTarInfo->prMonitorConn = prTarInfo->rSuppCtrl.ctrl_open(ifname);
if (prTarInfo->prMonitorConn == NULL) {
prTarInfo->rSuppCtrl.ctrl_close(prTarInfo->prCtrlConn);
prTarInfo->prCtrlConn = NULL;
return -1;
}
if (prTarInfo->rSuppCtrl.ctrl_attach(prTarInfo->prMonitorConn) != 0) {
prTarInfo->rSuppCtrl.ctrl_close(prTarInfo->prMonitorConn);
prTarInfo->rSuppCtrl.ctrl_close(prTarInfo->prCtrlConn);
prTarInfo->prCtrlConn = prTarInfo->prMonitorConn = NULL;
return -1;
}
if (socketpair(AF_UNIX, SOCK_STREAM, 0, prTarInfo->aucExitSockets) == -1) {
prTarInfo->rSuppCtrl.ctrl_close(prTarInfo->prMonitorConn);
prTarInfo->rSuppCtrl.ctrl_close(prTarInfo->prCtrlConn);
prTarInfo->prCtrlConn = prTarInfo->prMonitorConn = NULL;
return -1;
}
LOGD("[%s] Connect_to_supplicant %s successfully.\n", prTarInfo->acIfName, ifname);
return 0;
}
最终还是要调用wpa_ctrl_open或者p2p_ctrl_open的。这两个函数只有so文件,没有源代码:libwpa_client.so和libp2p_client.so。
再回过头去看setSoftAp的实现:
/*
* Arguments:
* argv[2] - wlan interface
* argv[3] - softap interface
* argv[4] - SSID
* argv[5] - Security
* argv[6] - Key
* argv[7] - Channel
* argv[8] - Preamble
* argv[9] - Max SCB
*/
int SoftapController::setSoftap(int argc, char *argv[]) {
char psk_str[2*SHA256_DIGEST_LENGTH+1];
int ret = 0, i = 0, fd;
char *ssid, *iface;
if (mSock < 0) {
LOGE("Softap set - failed to open socket");
return -1;
}
if (argc < 4) {
LOGE("Softap set - missing arguments");
return -1;
}
strncpy(mIface, argv[3], sizeof(mIface));
iface = argv[2];
/* Create command line */
i = addParam(i, "ASCII_CMD", "AP_CFG");
if (argc > 4) {
ssid = argv[4];
} else {
ssid = (char *)"AndroidAP";
}
i = addParam(i, "SSID", ssid);
if (argc > 5) {
i = addParam(i, "SEC", argv[5]);
} else {
i = addParam(i, "SEC", "open");
}
if (argc > 6) {
generatePsk(ssid, argv[6], psk_str);
i = addParam(i, "KEY", psk_str);
} else {
i = addParam(i, "KEY", "12345678");
}
if (argc > 7) {
i = addParam(i, "CHANNEL", argv[7]);
} else {
i = addParam(i, "CHANNEL", "6");
}
if (argc > 8) {
i = addParam(i, "PREAMBLE", argv[8]);
} else {
i = addParam(i, "PREAMBLE", "0");
}
if (argc > 9) {
i = addParam(i, "MAX_SCB", argv[9]);
} else {
i = addParam(i, "MAX_SCB", "8");
}
if ((i < 0) || ((unsigned)(i + 4) >= sizeof(mBuf))) {
LOGE("Softap set - command is too big");
return i;
}
sprintf(&mBuf[i], "END");
/* system("iwpriv eth0 WL_AP_CFG ASCII_CMD=AP_CFG,SSID=\"AndroidAP\",SEC=\"open\",KEY=12345,CHANNEL=1,PREAMBLE=0,MAX_SCB=8,END"); */
ret = setCommand(iface, "AP_SET_CFG");
if (ret) {
LOGE("Softap set - failed: %d", ret);
}
else {
LOGD("Softap set - Ok");
usleep(AP_SET_CFG_DELAY);
}
return ret;
}
什么是setCommand?这个非常有意思了:
int SoftapController::setCommand(char *iface, const char *fname, unsigned buflen) {
#ifdef HAVE_HOSTAPD
return 0;
#else
char tBuf[SOFTAP_MAX_BUFFER_SIZE];
struct iwreq wrq;
struct iw_priv_args *priv_ptr;
int i, j, ret;
int cmd = 0, sub_cmd = 0;
strncpy(wrq.ifr_name, iface, sizeof(wrq.ifr_name));
wrq.u.data.pointer = tBuf;
wrq.u.data.length = sizeof(tBuf) / sizeof(struct iw_priv_args);
wrq.u.data.flags = 0;
if ((ret = ioctl(mSock, SIOCGIWPRIV, &wrq)) < 0) {
LOGE("SIOCGIPRIV failed: %d", ret);
return ret;
}
priv_ptr = (struct iw_priv_args *)wrq.u.data.pointer;
for(i=0; i < wrq.u.data.length;i++) {
if (strcmp(priv_ptr[i].name, fname) == 0) {
cmd = priv_ptr[i].cmd;
break;
}
}
if (i == wrq.u.data.length) {
LOGE("iface:%s, fname: %s - function not supported", iface, fname);
return -1;
}
if (cmd < SIOCDEVPRIVATE) {
for(j=0; j < i; j++) {
if ((priv_ptr[j].set_args == priv_ptr[i].set_args) &&
(priv_ptr[j].get_args == priv_ptr[i].get_args) &&
(priv_ptr[j].name[0] == '\0'))
break;
}
if (j == i) {
LOGE("iface:%s, fname: %s - invalid private ioctl", iface, fname);
return -1;
}
sub_cmd = cmd;
cmd = priv_ptr[j].cmd;
}
strncpy(wrq.ifr_name, iface, sizeof(wrq.ifr_name));
if ((buflen == 0) && (*mBuf != 0))
wrq.u.data.length = strlen(mBuf) + 1;
else
wrq.u.data.length = buflen;
wrq.u.data.pointer = mBuf;
wrq.u.data.flags = sub_cmd;
ret = ioctl(mSock, cmd, &wrq);
return ret;
#endif
}
其实就是一个对ioctl的封装,标准的wireless extension(wext)的private command实现方式。
再来看startSoftAp:
int SoftapController::startSoftap() {
pid_t pid = 1;
int ret = 0;
if (mPid) {
LOGE("Softap already started");
return 0;
}
if (mSock < 0) {
LOGE("Softap startap - failed to open socket");
return -1;
}
if (!pid) {
LOGE("Should never get here!");
return -1;
} else {
*mBuf = 0;
ret = setCommand(mIface, "AP_BSS_START");
if (ret) {
LOGE("Softap startap - failed: %d", ret);
}
else {
mPid = pid;
LOGD("Softap startap - Ok");
usleep(AP_BSS_START_DELAY);
}
}
return ret;
}
同样是一个private command发给驱动,至此启动热点过程结束。
驱动没有源代码就没有办法分析下去了。我们可以断定的是驱动内部实现了一个类似hostapd的东西,处理了AP模式。