设为首页 收藏本站
查看: 2412|回复: 0

[经验分享] Android的WIFI移植

[复制链接]

尚未签到

发表于 2015-10-2 12:54:41 | 显示全部楼层 |阅读模式
我用的是 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(&quot;WifiMonitor&quot;);
        }
    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 = &quot;CTRL-EVENT-&quot;;
           //private static final int eventPrefixLen = eventPrefix.length();
           //private static final String connectedEvent = &quot;CONNECTED&quot;;
           //private static final String disconnectedEvent = &quot;DISCONNECTED&quot;;
            String eventName = eventStr.substring(eventPrefixLen);//去掉&quot;CTRL-EVENT-&quot;字符串
            int nameEnd = eventName.indexOf(' ');//找到随后的空格位置,这在wpa_supplicant发送时
//#define WPA_EVENT_CONNECTED &quot;CTRL-EVENT-CONNECTED &quot;中,已经内置空格了.
                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(&quot; - &quot;);//CTRL-EVENT-CONNECTED - Connection to ...
                    if (ind != -1)
                        eventData = eventStr.substring(ind + 3);
//剔除前导控制字符,将&quot; - &quot;后面的描述字符串作为真实数据,继续处理
    ...
            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, &quot;Failed to load Wi-Fi driver.&quot;);
                updateWifiState(WIFI_STATE_UNKNOWN);
                return false;
            }
            if (WifiNative.startSupplicant()) {
                WifiNative.unloadDriver();
                Log.e(TAG, &quot;Failed to start supplicant daemon.&quot;);
                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, &quot;DhcpHandler: DHCP request started&quot;);
//libs/android_runtime/android_net_NetUtils.cpp
//static JNINativeMethod gNetworkUtilMethods[] = {
//{ &quot;runDhcp&quot;, &quot;(Ljava/lang/String;Landroid/net/DhcpInfo;)Z&quot;, (void *)android_net_utils_runDhcp },
// ...
//};
                    if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {//执行dhcp申请ip地址操作
                        event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
                        if (LOCAL_LOGD) Log.v(TAG, &quot;DhcpHandler: DHCP request succeeded&quot;);
                    } else {
                        event = EVENT_INTERFACE_CONFIGURATION_FAILED;
                        Log.i(TAG, &quot;DhcpHandler: DHCP request failed: &quot; +
                            NetworkUtils.getDhcpError());
//如果dhcpcd分配ip失败,那么Message.obtain(mTarget, event).sendToTarget();将执行
//WifiNative.disconnectCommand();即:static JNINativeMethod gWifiMethods[] = {
//android_net_wifi_disconnectCommand发送&quot;DISCONNECT&quot;字符串[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, &quot;DRIVER POWERMODE %d&quot;, mode);
    return doBooleanCommand(cmdstr, &quot;OK&quot;);
}
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, &quot;DRIVER &quot;, 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将收到&quot;POWERMODE 0&quot;或者&quot;POWERMODE 1&quot;字符串[luther.gliethttp]
===========================================================================================
jni
=>runDhcp
=>android_net_utils_runDhcp
libs/netutils/dhcp_utils.c
=>dhcp_do_request
=>
    static const char DAEMON_NAME[] = &quot;dhcpcd&quot;;
    static const char DAEMON_PROP_NAME[] = &quot;init.svc.dhcpcd&quot;;
    static const char DHCP_PROP_NAME_PREFIX[] = &quot;dhcp&quot;;
    const char *ctrl_prop = &quot;ctl.start&quot;;
    const char *desired_status = &quot;running&quot;;
    snprintf(result_prop_name, sizeof(result_prop_name), &quot;%s.%s.result&quot;,
            DHCP_PROP_NAME_PREFIX,
            interface);
    property_set(result_prop_name, &quot;&quot;);//设置dhcp.eth0.result=&quot;&quot;;等到成功完成dhcp之后,
    property_set(ctrl_prop, DAEMON_NAME);//向名字为dhcpcd的service,发送&quot;ctrl.start&quot;启动命令字,该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因为是&quot;ctrl.start&quot;命令字,所以调用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, &quot;running&quot;);设置该svc的状态prop
// snprintf(pname, sizeof(pname), &quot;init.svc.%s&quot;, 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        &quot;/system/etc/dhcpcd&quot;
#define PACKAGE            &quot;dhcpcd&quot;
# define CONFIG            SYSCONFDIR &quot;/&quot; PACKAGE &quot;.conf&quot;
# define LIBEXECDIR        &quot;/system/etc/dhcpcd&quot;
# define SCRIPT            LIBEXECDIR &quot;/&quot; PACKAGE &quot;-run-hooks&quot;
=>strlcpy(options->script, SCRIPT,sizeof(options->script));//默认的options->script=&quot;/system/etc/dhcpcd/dhcpcd-run-hooks&quot;
=>f = fopen(cf ? cf : CONFIG, &quot;r&quot;);//如果没有指定.conf文件,那么使用默认.conf文件
=>parse_config_line//解析&quot;/system/etc/dhcpcd/dhcpcd.conf&quot;默认配置文件
=>parse_option
=>如果在&quot;/system/etc/dhcpcd/dhcpcd.conf&quot;有&quot;script&quot;这个节
=>那么执行strlcpy(options->script, oarg, sizeof(options->script));直接拷贝
/*
{&quot;script&quot;, required_argument, NULL, 'c'},
{&quot;option&quot;, required_argument, NULL, 'o'},
&quot;/system/etc/dhcpcd/dhcpcd.conf&quot;中的部分内容如下:
...
option domain_name_servers, domain_name, domain_search, host_name
...
*/
=>dhcp_run
=>handle_dhcp_packet
=>handle_dhcp
=>bind_dhcp
  reason = &quot;TIMEOUT&quot;;reason = &quot;BOUND&quot;;reason = &quot;REBIND&quot;;reason = &quot;RENEW&quot;;
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 &quot;failed&quot;或者
//执行setprop dhcp.${interface}.result &quot;ok&quot;
=>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);//子进程执行脚本,默认&quot;/system/etc/dhcpcd/dhcpcd-run-hooks&quot;
//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 &quot;${new_ip_address}&quot;
    setprop dhcp.${interface}.result &quot;ok&quot;//设置属性为ok
    setprop dhcp.${interface}.result &quot;failed&quot;
    ...
===========================================================================================
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服务启动的时候客户端和服务端通过unixsocket通信,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地址咯!
  

  http://blog.iyunv.com/pottichu/article/details/5691896

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-121776-1-1.html 上篇帖子: [计算机联网故障]WIFI接入正常,但是上网不正常(两种情况) 下篇帖子: 使用Ubuntu12.04创建无线WiFi热点供手机上网
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表