2011/08/19

Linux Kernel Initcall

Linux kernel module的module init function 如下,本文將以kernel module為例,說明他們是怎麼被kernel核心呼叫到。
static int __devinit tegra_kbc_init(void) {	
    pr_debug("KBC: tegra_kbc_init\n")
	return platform_driver_register(&tegra_kbc_driver)
}
module_init(tegra_kbc_init)

module_init

隨便找到有呼叫module_init的kernel module往上追,可以找到最後是呼叫到__define_inicall(level,fn,id)這個macro,如下
// File: include/linux/init.h
#define module_init(x)	__initcall(x)
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn)		__define_initcall("6",fn,6)
#define __define_initcall(level,fn,id) \
    static initcall_t __initcall_##fn##id __used \
    __attribute__((__section__(".initcall" level ".init"))) = fn
其中以module_init(tegra_kbc_init)會被展開成
static initcall_t __initcall_tegra_kbc_init6 __used 
__attribute__((__section__(".initcall6.init"))) = tegra_kbc_init 
由__attribute__得知,它最後會被放到initcall6.init這個section裡。而linker連結時會根據vmlinux.lds.S來將它放到指定的section。如下的INIT_CALLS。
// File: arch/arm/kernel/vmlinux.lds.S
SECTIONS {		
    INIT_SETUP(16)
    INIT_CALLS
    CON_INITCALL
    SECURITY_INITCALL
    INIT_RAM_FS
再往下追就看到了.initcall6.init。
// File: include/asm-generic/vmlinux.lds.h
#define INIT_CALLS							\
		VMLINUX_SYMBOL(__initcall_start) = .;			\
		INITCALLS						\
		VMLINUX_SYMBOL(__initcall_end) = .;

#define INITCALLS							\
	*(.initcallearly.init)						\
	VMLINUX_SYMBOL(__early_initcall_end) = .;			\
  	*(.initcall0.init)						\
  	*(.initcall0s.init)						\
  	*(.initcall1.init)						\
  	*(.initcall1s.init)						\
  	*(.initcall2.init)						\
  	*(.initcall2s.init)						\
  	*(.initcall3.init)						\
  	*(.initcall3s.init)						\
  	*(.initcall4.init)						\
  	*(.initcall4s.init)						\
  	*(.initcall5.init)						\
  	*(.initcall5s.init)						\
	*(.initcallrootfs.init)						\
  	*(.initcall6.init)						\
  	*(.initcall6s.init)						\
  	*(.initcall7.init)						\
  	*(.initcall7s.init)

do_initcalls

kernel是從那裡呼叫到init calls,可以從kernel的entry point,init/main.c,找到下面這個函式。
// File: init/main.c
extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
static void __init do_initcalls(void) {	
    initcall_t *fn;
	for (fn = __early_initcall_end; fn < __initcall_end; fn++)
        do_one_initcall(*fn);
	/* Make sure there is no pending stuff from the initcall sequence */	
    flush_scheduled_work();
}
一路往上追可以知道do_initcalls()在start_kernel()的最後的函式裡面用一個kernel thread間接呼叫,這裡很容易就可以追到了,我就不再列出。

2011/08/17

Android上的LAN device

最近要在Android上加LAN device support,該LAN device 是USB interface的SMSC9514。

ifconfig


adb shell後用以下指令可以看到目前板子上的網路裝置
# busybox-armv6l ifconfig -a

busybox可自行上網下載執行檔

ifconfig 運作方式


追busybox的code可以找到這段程式

// file: busybox-1.18.4/networking/interface.c
#define _PATH_PROCNET_DEV               "/proc/net/dev"

static int if_readlist_proc(char *target)
{
	static smallint proc_read;

	FILE *fh;
	char buf[512];
	struct interface *ife;
	int err, procnetdev_vsn;

	if (proc_read)
		return 0;
	if (!target)
		proc_read = 1;

	fh = fopen_or_warn(_PATH_PROCNET_DEV, "r");
	if (!fh) {
		return if_readconf();
	}

可以看到它open了一個檔案_PATH_PROCNET_DEV,這個檔案為"/proc/net/dev",所以得知ifconfig是從那裡抓network devices name

driver


秀出/proc/net/dev訊息


至於driver如何將這些資訊秀出可以參考 dev_seq_show() 如下

// file: kernel/net/core/dev.c
static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
{
	struct rtnl_link_stats64 temp;
	const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);

	seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
		   "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
		   dev->name, stats->rx_bytes, stats->rx_packets,
		   stats->rx_errors,
		   stats->rx_dropped + stats->rx_missed_errors,
		   stats->rx_fifo_errors,
		   stats->rx_length_errors + stats->rx_over_errors +
		    stats->rx_crc_errors + stats->rx_frame_errors,
		   stats->rx_compressed, stats->multicast,
		   stats->tx_bytes, stats->tx_packets,
		   stats->tx_errors, stats->tx_dropped,
		   stats->tx_fifo_errors, stats->collisions,
		   stats->tx_carrier_errors +
		    stats->tx_aborted_errors +
		    stats->tx_window_errors +
		    stats->tx_heartbeat_errors,
		   stats->tx_compressed);
}

/*
 *	Called from the PROCfs module. This now uses the new arbitrary sized
 *	/proc/net interface to create /proc/net/dev
 */
static int dev_seq_show(struct seq_file *seq, void *v)
{
	if (v == SEQ_START_TOKEN)
		seq_puts(seq, "Inter-|   Receive                            "
			      "                    |  Transmit\n"
			      " face |bytes    packets errs drop fifo frame "
			      "compressed multicast|bytes    packets errs "
			      "drop fifo colls carrier compressed\n");
	else
		dev_seq_printf_stats(seq, v);
	return 0;
}

driver註冊network device


driver要跟kernel註冊自己是個network device,註冊時最底層是用到 list_netdevice

// file: kernel/net/core/dev.c
/* Device list insertion */
static int list_netdevice(struct net_device *dev)
{
	struct net *net = dev_net(dev);

	ASSERT_RTNL();

	write_lock_bh(&dev_base_lock);
	list_add_tail_rcu(&dev->dev_list, &net->dev_base_head);
	hlist_add_head_rcu(&dev->name_hlist, dev_name_hash(net, dev->name));
	hlist_add_head_rcu(&dev->index_hlist,
			   dev_index_hash(net, dev->ifindex));
	write_unlock_bh(&dev_base_lock);
	return 0;
}

最外層是用

// file: kernel/drivers/net/usb/usbnet.c
int
usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
{
	status = register_netdev (net);

usbnet_probe又是誰呼叫了,我們就是我們要用的LAN chip SMSC95XX用到了

// file: kernel/drivers/net/usb/smsc95xx.c
static struct usb_driver smsc95xx_driver = {
	.name		= "smsc95xx",
	.id_table	= products,
	.probe		= usbnet_probe,
	.suspend	= usbnet_suspend,
	.resume		= usbnet_resume,
	.disconnect	= usbnet_disconnect,
};

SMSC9514

# $ adb shell
# lsusb
1d6b:0002 (bus 1, device 1)
1d6b:0002 (bus 2, device 1)
# # 插入裝置
# lsusb
1d6b:0002 (bus 1, device 1)
1d6b:0002 (bus 2, device 1)
0424:9514 (bus 1, device 2)
0424:ec00 (bus 1, device 3)
# busybox-armv6l ash
/ # # 設IP跟DNS
/ # busybox-armv6l ifconfig eth0 192.168.1.123
/ # busybox-armv6l route add default gw 192.168.1.1
/ # ping 168.95.1.1
PING 168.95.1.1 (168.95.1.1) 56(84) bytes of data.
64 bytes from 168.95.1.1: icmp_seq=1 ttl=242 time=670 ms
64 bytes from 168.95.1.1: icmp_seq=2 ttl=242 time=989 ms
64 bytes from 168.95.1.1: icmp_seq=3 ttl=242 time=589 ms
#setprop net.dns1 168.95.1.1

UI


這部分比較麻煩,可以自行參考android-x86的實作。

2011/08/15

Android WIFI Service

需控制Wifi的App會透過system service來呼叫到wifi service,像是在packages/apps/Settings裡的wifi設定頁面,它們會用類似這樣的語法來取得wifi system service。

mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
本文將探討由wifi system service到hardware做動的部分。


WifiService 註冊


ServiceManager.addService() 為所有service註冊都要用到的函式。WifiService也是,程式如下。

private ConnectivityService(Context context) {
        for (int netType : mPriorityList) {
            switch (mNetAttributes[netType].mRadio) {
            case ConnectivityManager.TYPE_WIFI:
                if (DBG) log("Starting Wifi Service.");
                WifiStateTracker wst = new WifiStateTracker();
                WifiService wifiService = new WifiService(context);
                ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
                wifiService.checkAndStartWifi();
                mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
                wst.startMonitoring(context, mHandler);

                //TODO: as part of WWS refactor, create only when needed
                mWifiWatchdogService = new WifiWatchdogService(context);
                break;
ConnectivityService是在SystemServer建構函式用到,至於SystemServer如何被叫到,自行去找Zygote的介紹

try {
                Slog.i(TAG, "Connectivity Service");
                connectivity = ConnectivityService.getInstance(context);
                ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
            } catch (Throwable e) {
                Slog.e(TAG, "Failure starting Connectivity Service", e);
            }

Service


開關Wifi是透過setWifiEnabled(),其Service內的實作程式如下

public synchronized boolean setWifiEnabled(boolean enable) {
        enforceChangePermission();

        if (DBG) {
            Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
        }

        if (enable) {
            reportStartWorkSource();
        }
        mWifiStateMachine.setWifiEnabled(enable);
最重要的是它用了WifiStateMachine::setWifiEnabled(),它實作如下

public void setWifiEnabled(boolean enable) {
        mLastEnableUid.set(Binder.getCallingUid());
        if (enable) {
            /* Argument is the state that is entered prior to load */
            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
            sendMessage(CMD_START_SUPPLICANT);
        } else {
            sendMessage(CMD_STOP_SUPPLICANT);
            /* Argument is the state that is entered upon success */
            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
        }
    }
可以看到它用了很多sendMessage(),這部分用了樹狀的有限狀態機,sendMessage是拿來叫目前所處的State物件,根據sendMessage要求的狀態來呼叫processMessage()處理。WifiStateMachine的樹狀結構如下

addState(mDefaultState);
            addState(mInitialState, mDefaultState);
            addState(mDriverUnloadingState, mDefaultState);
            addState(mDriverUnloadedState, mDefaultState);
                addState(mDriverFailedState, mDriverUnloadedState);
            addState(mDriverLoadingState, mDefaultState);
            addState(mDriverLoadedState, mDefaultState);
            addState(mSupplicantStartingState, mDefaultState);
            addState(mSupplicantStartedState, mDefaultState);
                addState(mDriverStartingState, mSupplicantStartedState);
                addState(mDriverStartedState, mSupplicantStartedState);
                    addState(mScanModeState, mDriverStartedState);
                    addState(mConnectModeState, mDriverStartedState);
                        addState(mConnectingState, mConnectModeState);
                        addState(mConnectedState, mConnectModeState);
                        addState(mDisconnectingState, mConnectModeState);
                        addState(mDisconnectedState, mConnectModeState);
                        addState(mWaitForWpsCompletionState, mConnectModeState);
                addState(mDriverStoppingState, mSupplicantStartedState);
                addState(mDriverStoppedState, mSupplicantStartedState);
            addState(mSupplicantStoppingState, mDefaultState);
            addState(mSoftApStartedState, mDefaultState);

        setInitialState(mInitialState);
要先確定自己目前的狀態才能知道程式會用那個State物件的processMessage()/enter()來處理。以上面的setWifiEnabled(true)為例,它會要求DriverUnloadedState::processMessage()處理CMD_LOAD_DRIVER。

class DriverUnloadedState extends HierarchicalState {
        @Override
        public void enter() {
            if (DBG) Log.d(TAG, getName() + "\n");
            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
        }
        @Override
        public boolean processMessage(Message message) {
            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
            switch (message.what) {
                case CMD_LOAD_DRIVER:
                    transitionTo(mDriverLoadingState);
                    break;
                default:
                    return NOT_HANDLED;
            }
            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
            return HANDLED;
        }
    }
DriverUnloadedState又轉換狀態到DriverLoadingState,轉過去時會先做enter() method如下

class DriverLoadingState extends HierarchicalState {
        @Override
        public void enter() {
            if (DBG) Log.d(TAG, getName() + "\n");
            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());

            final Message message = new Message();
            message.copyFrom(getCurrentMessage());
            /* TODO: add a timeout to fail when driver load is hung.
		        Similarly for driver unload.
             */
            new Thread(new Runnable() {
                public void run() {
                    mWakeLock.acquire();
                    //enabling state
                    switch(message.arg1) {
                        case WIFI_STATE_ENABLING:
                            setWifiState(WIFI_STATE_ENABLING);
                            break;
                        case WIFI_AP_STATE_ENABLING:
                            setWifiApState(WIFI_AP_STATE_ENABLING);
                            break;
                    }

                    if(WifiNative.loadDriver()) {
                        Log.d(TAG, "Driver load successful");
                        sendMessage(CMD_LOAD_DRIVER_SUCCESS);
在此method中呼叫了WifiNative.loadDriver(),它真正會呼叫到底層叫loading kernel module的動作。終於要進入JNI了。

public class WifiNative {

    static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
    static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1;
    static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2;

    public native static String getErrorString(int errorCode);

    public native static boolean loadDriver();

JNI


以上面的loadDriver()會呼叫到

static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject clazz)
{
    return (jboolean)(::wifi_load_driver() == 0);
}
wifi_load_driver()則是HAL提供。

HAL


HAL該提供的函式可參考hardware/libhardware_legacy/include/hardware_legacy/wifi.h,wifi_load_driver()的實作如下,可以看到insmod的關鍵system call。

int wifi_load_driver()
{
    char driver_status[PROPERTY_VALUE_MAX];
    int count = 100; /* wait at most 20 seconds for completion */

    if (is_wifi_driver_loaded()) {
        return 0;
    }

    if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0)
        return -1;

    if (strcmp(FIRMWARE_LOADER,"") == 0) {

其它

除了loading kernel driver外,其它有關wifi操作的部分皆是透過wpa_supplicant這套library,這套library很大,所以我不介紹了,有興趣的人可以參考external/wpa_supplicant。