http://blog.csdn.net/king_sundi/article/details/5972965 我用的是 Ralink 的网卡,所以 wifi 网卡的名字是 ra0, 在网上看了一些文章,要改很多地方。 不就是个网卡名字问题吗? 我有 驱动源码, 就直接在源码里把名字改成 mlan0 就得了。 在源码中找到注册网卡的 register_netdev 或者 register_netdevice 函数,直接在注册前把名字 给它改咯。。。 strcpy( pNetDev->name, "mlan0"); ret = register_netdev(pNetDev); 这样我们的网卡名字就成了 mlan0, 就不用那么麻烦去 改 android 源码中的那些东西了, 哈哈。。 另外 需要修改 : hardware/libhardware_legacy/wifi/wifi.c #define WIFI_DRIVER_MODULE_PATH "/system/lib/modules/wlan.ko" 可以让系统在需要时,自动加载 驱动模块。 转几篇文章: http://blog.chinaunix.net/u2/66024/showart_1933469.html 本文档分析了Android的WIFI功能代码,对Android的WIFI功能实现起到一定的参考作用。 在Linux中,wlan(无线局域网)设备驱动是网络设备,使用网络接口。Wlan 在用户空间使用标准的 socket 接口进行控制。 内核的移植(wifi驱动的加载): 一、WIFI 协议和驱动程序在内核进行 menuconfig 配置时,配置选项为: 1、“networking support ”>"wireless". 2.、“device drivers” > "network device support" >"wireless LAN" 二、android wifi 基本架构 JAVA应用层 Setting、WifiSwitcher等应用 上下的通讯为Binder机制 JAVA框架层 Wifi manager Wifi service 上下通讯为JNI C/C++ 框架层 Wifi的JNI WPA适配层 Wpa_supplicant程序 内核空间 Wifi的内核驱动程序 5. WIFI中间层的运行解析 1、android下如何通过jni监控wifi网络连接、dhcpcd执行和power电源控制 libs/android_runtime/android_net_wifi_Wifi.Cpp 部分jni接口 static JNINativeMethod gWifiMethods[] = { { "loadDriver", "()Z", (void *)android_net_wifi_loadDriver }, { "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },//电源管理 { "connectToSupplicant", "()Z", (void *)android_net_wifi_connectToSupplicant }, { "waitForEvent", "()Ljava/lang/String;", (void*) android_net_wifi_waitForEvent }, { "disconnectCommand", "()Z", (void *)android_net_wifi_disconnectCommand }, ... }; int register_android_net_wifi_WifiManager(JNIEnv* env) { ... return AndroidRuntime::registerNativeMethods(env, WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));//登记jni } libs/android_runtime/AndroidRuntime.cpp static const RegJNIRec gRegJNI[] = { ... REG_JNI(register_android_net_wifi_WifiManager), ... }; int AndroidRuntime::startReg(JNIEnv* env) { ... register_jni_procs(gRegJNI, NELEM(gRegJNI), env); ... } AndroidRuntime::start =>startReg(env)即调用方法int AndroidRuntime::startReg(JNIEnv* env) ============================================================================== wifi_load_driver wifi_start_supplicant =>ensure_config_file_exists //检查/data/misc/wifi/wpa_supplicant.conf文件是否存在,如果不存在,那么 从/system/etc/wifi/wpa_supplicant.conf动态拷贝一份 android_net_wifi_connectToSupplicant =>wifi_connect_to_supplicant => ctrl_conn = wpa_ctrl_open(ifname); monitor_conn = wpa_ctrl_open(ifname); wpa_ctrl_attach(monitor_conn); android_net_wifi_waitForEvent =>wifi_wait_for_event =>wpa_ctrl_recv(monitor_conn, buf, &nread); =>recv(ctrl->s, reply, *reply_len, 0);//阻塞等待wpa_supplicant的netlink数据过来 =>如果接收的buf数据区,buf[0]为'<',那么说明有level级别信息,所以将'<'...'>'数据剔除,然后 wifi_wait_for_event函数返回[luther.gliethttp]. java/android/android/net/wifi/WifiMonitor.java public class WifiMonitor { ... public void startMonitoring() { new MonitorThread().start();//启动java线程 } class MonitorThread extends Thread { public MonitorThread() { super("WifiMonitor"); } public void run() { for (;;) { ensureSupplicantConnection();//=>WifiNative.connectToSupplicant调用jni函数 android_net_wifi_connectToSupplicant String eventStr = WifiNative.waitForEvent();//=>调用jni函数android_net_wifi_waitForEvent //private static final int CONNECTED = 1; //private static final int DISCONNECTED = 2; //private static final String eventPrefix = "CTRL-EVENT-"; //private static final int eventPrefixLen = eventPrefix.length(); //private static final String connectedEvent = "CONNECTED"; //private static final String disconnectedEvent = "DISCONNECTED"; String eventName = eventStr.substring(eventPrefixLen);//去掉"CTRL-EVENT-"字符串 int nameEnd = eventName.indexOf(' ');//找到随后的空格位置,这在wpa_supplicant发送时 //#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "中,已经内置空格了. if (nameEnd != -1) eventName = eventName.substring(0, nameEnd); int event; if (eventName.equals(connectedEvent))//检测netlink过来的字符串action类型 event = CONNECTED; else if (eventName.equals(disconnectedEvent)) event = DISCONNECTED; ... int ind = eventStr.indexOf(" - ");//CTRL-EVENT-CONNECTED - Connection to ... if (ind != -1) eventData = eventStr.substring(ind + 3); //剔除前导控制字符,将" - "后面的描述字符串作为真实数据,继续处理 ... if (event == STATE_CHANGE) { handleSupplicantStateChange(eventData); } else if (event == DRIVER_STATE) { handleDriverEvent(eventData); } else { handleEvent(event, eventData);//对于CONNECTED和DISCONNECTED等netlink事件将执行此操作来处理 [luther.gliethttp] // If supplicant is gone, exit the thread if (event == TERMINATING) { break; } } ... void handleEvent(int event, String remainder) { switch (event) { case DISCONNECTED: handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder); break; case CONNECTED: handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);//控制界面显示 break; ... } public class WifiStateTracker extends NetworkStateTracker { ... public void startEventLoop() { mWifiMonitor.startMonitoring();//启动上面的MonitorThread线程 } ... } java/services/com/android/server/WifiService.java public class WifiService extends IWifiManager.Stub { ... private boolean setWifiEnabledBlocking(boolean enable) { final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED; ... if (enable) { if (WifiNative.loadDriver()) { Log.e(TAG, "Failed to load Wi-Fi driver."); updateWifiState(WIFI_STATE_UNKNOWN); return false; } if (WifiNative.startSupplicant()) { WifiNative.unloadDriver(); Log.e(TAG, "Failed to start supplicant daemon."); updateWifiState(WIFI_STATE_UNKNOWN); return false; } mWifiStateTracker.startEventLoop(); //启动MonitorThread线程,等待wpa_supplicant将netlink数据转发过来,然后根据netlink动作类型,进 一步影响界面显示[luther.gliethttp]. } ... } java/android/android/net/wifi/WifiStateTracker.java 电源管理 private void handleConnectedState() { ... mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget();//传递到下面的handleMessage方法 ... } public void onChange(boolean selfChange) { ... handleConnectedState(); ... } public class WifiStateTracker extends NetworkStateTracker { ... public void handleMessage(Message msg) { switch (msg.what) { case EVENT_SUPPLICANT_CONNECTION: case EVENT_NETWORK_STATE_CHANGED: handleConnectedState();//调用 ... private class DhcpHandler extends Handler { private Handler mTarget; public DhcpHandler(Looper looper, Handler target) { super(looper); mTarget = target; } public void handleMessage(Message msg) { int event; //private static final int DRIVER_POWER_MODE_AUTO = 0; //private static final int DRIVER_POWER_MODE_ACTIVE = 1; switch (msg.what) { case EVENT_DHCP_START: synchronized (this) { WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);//设置电源模式,调用 android_net_wifi_setPowerModeCommand } Log.d(TAG, "DhcpHandler: DHCP request started"); //libs/android_runtime/android_net_NetUtils.cpp //static JNINativeMethod gNetworkUtilMethods[] = { //{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z", (void *) android_net_utils_runDhcp }, // ... //}; if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {//执行dhcp申请ip地址操作 event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED; if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded"); } else { event = EVENT_INTERFACE_CONFIGURATION_FAILED; Log.i(TAG, "DhcpHandler: DHCP request failed: " + NetworkUtils.getDhcpError()); //如果dhcpcd分配ip失败,那么Message.obtain(mTarget, event).sendToTarget();将执行 //WifiNative.disconnectCommand();即:static JNINativeMethod gWifiMethods[] = { //android_net_wifi_disconnectCommand发送"DISCONNECT"字符串[luther.gliethttp] //然后在wpa_supplicant服务端执行wpa_supplicant_ctrl_iface_process //wpa_supplicant_disassociate } synchronized (this) { WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO); } Message.obtain(mTarget, event).sendToTarget(); break; } } } ... /** * Send the tracker a notification that a connection to the supplicant * daemon has been established. */ //在上面的public class WifiMonitor=>ensureSupplicantConnection //=> //while (!supplicantConnected) { // boolean connected; //synchronized (mWifiStateTracker) { //connected = WifiNative.connectToSupplicant();//如果没有连接成功,那么while循环尝试,直到尝 试成功,或者定义了oneShot,仅一次尝试 //=>mWifiStateTracker.notifySupplicantConnection();//如果WifiNative.connectToSupplicant()成 功,那么将执行 //mWifiStateTracker.notifySupplicantConnection();的调用. void notifySupplicantConnection() {//向对象发送message Message.obtain(this, EVENT_SUPPLICANT_CONNECTION).sendToTarget(); } void notifyStateChange(SupplicantState newState) { Message.obtain(this, EVENT_SUPPLICANT_STATE_CHANGED, newState).sendToTarget(); } ... } static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject clazz, jint mode) { char cmdstr[256]; sprintf(cmdstr, "DRIVER POWERMODE %d", mode); return doBooleanCommand(cmdstr, "OK"); } android_net_wifi_setPowerModeCommand =>doBooleanCommand =>doCommand =>wifi_command =>wifi_send_command =>wpa_ctrl_request =>send给wpa_supplicant 然后wpa_supplicant将做如下接收操作: system/extra/wpa_supplicant/main.c =>wpa_supplicant_add_iface =>wpa_supplicant_init_iface2 =>wpa_supplicant_ctrl_iface_init =>注册ctrl_conn控制端口和monitor_conn监听端口的处理函数 eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);//ctrl_conn端口的handler处理函数 wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);//monitor_conn端口的回调处理函数,处理 netlink数据到所有monitor_conn监听端口 =>wpa_supplicant_ctrl_iface_receive//对于unix通信方式 =>wpa_supplicant_ctrl_iface_process =>如果wpa_cli发送的是wpa_cli driver xxx形式的命令,那么调用这个函数 if (os_strncmp(buf, "DRIVER ", 7) == 0) {//掠过前7个,直接将命令传过去 reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply, reply_size); =>wpa_supplicant_driver_cmd =>wpa_drv_driver_cmd =>自定义DRIVER扩展处理函数,所以对于java传递过来的power电源管理命令,wpa_drv_driver_cmd将收到 "POWERMODE 0"或者"POWERMODE 1"字符串[luther.gliethttp] =========================================================================================== jni =>runDhcp =>android_net_utils_runDhcp libs/netutils/dhcp_utils.c =>dhcp_do_request => static const char DAEMON_NAME[] = "dhcpcd"; static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd"; static const char DHCP_PROP_NAME_PREFIX[] = "dhcp"; const char *ctrl_prop = "ctl.start"; const char *desired_status = "running"; snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", DHCP_PROP_NAME_PREFIX, interface); property_set(result_prop_name, "");//设置dhcp.eth0.result="";等到成功完成dhcp之后, property_set(ctrl_prop, DAEMON_NAME);//向名字为dhcpcd的service,发送"ctrl.start"启动命令字, 该service在init.rc中 //init.rc中dhcpcd服务进程命令字 //service dhcpcd /system/bin/dhcpcd eth0 // disabled // oneshot wait_for_property(DAEMON_PROP_NAME, desired_status, 10); //init.c=>init进程 //=>handle_property_set_fd因为是"ctrl.start"命令字,所以调用handle_control_message处理控制信 息 //=>handle_control_message //=>msg_start //=> // struct service *svc = service_find_by_name(name); // service_start(svc);//启动svc,即执行:/system/bin/dhcpcd eth0 //=>service_start //=>pid = fork(); // if(pid == 0)execve(svc->args[0], (char**) svc->args, (char**) ENV);子进程执行execve运 行/system/bin/dhcpcd,参数为eth0 //=>否则父进程,即init进程将 //=>notify_service_state(svc->name, "running");设置该svc的状态prop // snprintf(pname, sizeof(pname), "init.svc.%s", name); // property_set(pname, state);//所以这样上面wait_for_property(DAEMON_PROP_NAME, desired_status, 10);也才能够正常pass[luther.gliethttp]. wait_for_property(result_prop_name, NULL, 15);//等待dhcp.eth0.result=非空 =========================================================================================== system/extra/dhcpcd-4.0.0-beta9/dhcpcd.c dhcpcd =>main # define SYSCONFDIR "/system/etc/dhcpcd" #define PACKAGE "dhcpcd" # define CONFIG SYSCONFDIR "/" PACKAGE ".conf" # define LIBEXECDIR "/system/etc/dhcpcd" # define SCRIPT LIBEXECDIR "/" PACKAGE "-run-hooks" =>strlcpy(options->script, SCRIPT, sizeof(options->script));//默认的options->script= "/system/etc/dhcpcd/dhcpcd-run-hooks" =>f = fopen(cf ? cf : CONFIG, "r");//如果没有指定.conf文件,那么使用默认.conf文件 =>parse_config_line//解析"/system/etc/dhcpcd/dhcpcd.conf"默认配置文件 =>parse_option =>如果在"/system/etc/dhcpcd/dhcpcd.conf"有"script"这个节 =>那么执行strlcpy(options->script, oarg, sizeof(options->script));直接拷贝 /* {"script", required_argument, NULL, 'c'}, {"option", required_argument, NULL, 'o'}, "/system/etc/dhcpcd/dhcpcd.conf"中的部分内容如下: ... option domain_name_servers, domain_name, domain_search, host_name ... */ =>dhcp_run =>handle_dhcp_packet =>handle_dhcp =>bind_dhcp reason = "TIMEOUT";reason = "BOUND";reason = "REBIND";reason = "RENEW"; system/extra/dhcpcd-4.0.0-beta9/configure.c => configure(iface, reason, state->new, state->old, &state->lease, options, 1); //如果dhcp超时或者dhcp成功,都会调用exec_script来执行脚本, //执行setprop dhcp.${interface}.result "failed"或者 //执行setprop dhcp.${interface}.result "ok" =>exec_script(options, iface->name, reason, NULL, old); =>然后configure_env通过环境变量将reason传递到脚本中 int exec_script(const struct options *options, const char *iface, const char *reason, const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo) =>pid = fork(); =>if(pid == 0)execve(options->script, argv, env);//子进程执行脚本,默 认"/system/etc/dhcpcd/dhcpcd-run-hooks" //dhcpcd-run-hooks脚本会根据level值,决定是否执行system/etc/dhcpcd/dhcpcd-hook/*目录下的相应 文件 //我们的系统在该system/etc/dhcpcd/dhcpcd-hook/*目录下有如下3个文件 //95-configured //20-dns.conf //01-test =>父进程返回while (waitpid(pid, &status, 0) == -1)等待子进程脚本执行完成 system/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/20-dns.conf system/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/95-configured ... setprop dhcp.${interface}.ipaddress "${new_ip_address}" setprop dhcp.${interface}.result "ok"//设置属性为ok setprop dhcp.${interface}.result "failed" ... =========================================================================================== inet_init、tcp_prot sock->ops->sendmsg(iocb, sock, msg, size); =>inetsw_array[] =>inet_stream_ops =>tcp_sendmsg =========================================================================================== wpa_cli.c =>main =>wpa_cli_interactive =>wpa_cli_recv_pending(monitor_conn, 0, 0);//阻塞等待wpa_supplicant发送数据过来 =>如果action_monitor为true,那么将执行一些简单加工操作,否则将直接将wpa_supplicant发过来的数 据打印到console上[luther.gliethttp]. 参考文献 网络资源: http://osdir.com/ml/android-porting/2009-06/msg00714.html http://osdir.com/ml/android-porting/2009-07/msg00303.html http://bbs.imp3.net/redirect.php?tid=747705&goto=newpost http://osdir.com/ml/android-porting/2010-02/msg00152.html http://blog.chinaunix.net/u2/66024/showart_1926589.html 关于 wifi问题的处理 wifi porting文件和目录 porting wifi主要分为两个部分,源码的修改和配置文件的修改,其中配置文件的修改包括源码未编译 时配置文件的修改和源码编译后的配置文件修改,下面就这两部分分析: A:android未编译时的配置文件修改和源码修改 1:/android-cupcake/build/target/board/generic/ BoardConfig.mk 确定是否存在HAVE_CUSTOM_WIFI_DRIVER_2 := true ,如果没有则添加该选项 /android-eclair/external/wpa_supplicant/.config.h 确定.config.h中,是否存在以下3个选项 CONFIG_WIRELESS_EXTENSION=y CONFIG_CTRL_IFACE=y CONFIG_DRIVER_WEXT=y 以上是支持wifi驱动的选项! 2:修改的源码文件 2.1/android-cupcake/system/wlan/ti/sta_dk_4_0_4_32/CUDK/tiwlan_loader/tiwlan_loader.c 这个文件修改的tiwlan_loader服务,这个服务在android1.5中需要返回成功,表示加载wifi的固件到 eeprom中成功,而实际内核在加载wifi驱动的时候,同时加载了固件(即提供的bin文件)。但是在 android2.0中,这个服务不是必须的!在编译 tiwlan_loader.c时需要/android- cupcake/system/wlan/ti/sta_dk_4_0_4_32/CUDK/UtilityAdapter编译出来的库。 2.2/android-cupcake/hardware/libhardware_legacy/wifi/wifi.c 这个是porting wifi驱动的最重要的文件,其中包括驱动加载,连接wpa_supplicant服务都是在wifi.c中 完成。所以要修改驱动加载的网络接口名和相关的宏。 2.3/android-cupcake/frameworks/base/wifi/java/android/net/wifi 这个目录下是wifi中相关的java代码,其中修改的WifiStateTracker.java,这个主要修改dhcp时,获取 动态ip地址的网络接口名。 2.4 external/wpa_supplicant/ctrl_iface_unix.c 这个主要修改wpa_supplicatn连接时的权限,wpa_supplicant服务启动的时候客户端和服务端通过unix socket通信,JAVA UI 界面是通过此socket文件与驱动联系,此服务生成的socket 在/data/system/wpa_supplicant/目录下,如果涉及到权限问题,则需要修改 external/wpa_supplicant/ctrl_iface_unix.c中相关的目录的权限 2.5 frameworks/base/services/java/com/android/server/WifiService.java 这个在android1.5中,上传到BSSID,ISSID,java代码无法识别。在android2.0中无需修改。 B:android编译后的相关配置文件的修改 3.1/system/etc/wifi/wpa_supplicant.conf 看目录下是否存在该文件,如果不存在,则添加;并且添加wpa_supplicant服务socket的服务接口,如下 所示: ctrl_interface=/data/system/wpa_supplicant//默认的mlan0无线网络接口的目录 update_config=1 //这个可能是更新的配置,但不确认 3.2/system/etc/dhcpcd/dhcpcd.conf 看是否存在改文件,不存在则添加,并且修改无线网络接口的网络名字,如android默认的是tiwlan0 , 而我的无线网络接口是mlan0,则把interface 后面的接口改成mlan0 3.3 init.rc service wpa_supplicant /system/bin/wpa_supplicant -imlan0 - c/system/etc/wifi/wpa_supplicant.conf disable oneshot service dhcpcd /system/bin/dhcpcd -d -f /system/etc/dhcpcd/dhcpcd.conf mlan0 disable oneshot 以上是添加在wifi的服务。 mkdir /data/misc/wifi 0777 wifi wifi mkdir /data/misc/wifi/sockets 0777 wifi wifi mkdir /data/system/wpa_supplicant 0777 wifi wifi mkdir /data/misc/dhcp 0777 dhcp dhcp chown dhcp dhcp /data/misc/dhcp 新建以上的目录。 如果你不的平台不出稀奇古怪的问题的话,现在你已经可以ping通你想用的ip地址咯!