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。


2011/07/25

Synology DSM 3.2 Beta 之夜


上週應以前大學同學之邀再加上抽獎獎項的誘惑,參加了Synology Beta之夜。其間為了抽獎非常認真做了筆記(沒抽到我,完全用不到^^||),今天整理了一下筆記,順道介紹一下當天聽到了什麼新功能。

圖:真是人山人海

先說明一下我大概是半年前買了他們家的DS211j,目的是1)拿來抓迷之片,2)當網芳使用,3)備份我重要的文件。使用到現在以現有功能我是很滿意,但是我還是覺得缺了一些功能,像是dropbox的功能,本來還期待這次發表會會發表,都只能怪主持人一直喊private cloud,讓我抱了一絲絲的期待。

dropbox,不要說用NFS or curlftpfs or sshfs來做類似的應用(它們家的ssh service也沒有技援sshfs,這我也要抱怨一下),但還是有很多dropbox可以達到的方便性是上述方案做不到。此外,現階段已經有很多open source的dropbox相關軟體,其實群暉只要花點時間改一下應該就可以用到他們的產品上 :)。

上面講了那麼多不相關的東西,我還是要稱讚一下當天presenter的功力很棒,真是完全沒有冷場,2個小時根本就在歡笑中渡過。這應該算是我以前到現在所有參加過的宅宅活動裡頭,氣氛我覺得數一數二的好。此外當天是在君品酒店辦的,還有豐富的餐點,有考慮到有人是下班不吃飯直接衝去。


圖:少不了要有豐盛的食物

廢話到此,我還是稍微來看圖說故事以及部分的新功能介紹。請讓我以Ubuntu/Android user and programmer的角度來看這些新功能,由喜好程度不需要->很需要來介紹。

以下是「我不是企業用戶不需要這個功能」的功能
  • LDAP (還好我家只有5個人)
  • syslog aggregative (有經驗的阿宅網管應該會有自己的一套方法 )
  • Google cloud printer (我覺得有點不太實用,他有技援PDF or DOC?不論實用性,我是覺得不錯玩~ 好笑的是當天demo失敗,最後的presenter還補一槍)
  • 監控系統,跨瀏覽器、方便佈署、效能提升 (如果那天我家遭小偷了或許我會買個ip cam試試)
圖:新版支援統一管理LDAP

圖:demo google cloud print結果突搥了 XDDD

以下有了它「世界更美好」的功能
  • mount everything,簡單的說就是支援把別台電腦的samba資料匣mount到NAS上,還有mount iso檔。(很實用的功能,實作起來又非常非常非常簡單)
  • mobile app,也就是iOS or Android app也有支援軟體。(雖然還用不到,但上面可以做的東西大有可為)
  • 全新的相簿介面,又可同步於facebook相簿。(我家頻寬自己都不夠用了,還要給別人用)

圖:報告Mobile App的RD講者,可能原本就想說RD來報告一定很無聊,我想這位RD可以轉行做PM。

圖:PM在講相簿新增的功能

以下是「考試100分」的功能
  • 拖拉檔案上傳功能
  • Youtube下載功能
  • 免空下載功能
這三個功能是個人覺得比較有機會用到的功能,尤其是平常我常常在用bt抓ubuntu iso image(??),如果可以用拖拉方式上傳種子真是非常方便。(我還是想要有個人的dropbox啦)

講到拖拉種子進去網頁,如果Synology可以固定一個資料匣,讓我用網芳(NFS...)的方式拖拉進去,它就會幫我自動啟動Download Station抓資料,那我會覺得更實用。因為我根本就不想開網頁的管理介面。

結論

Synology的管理介面真的是獨樹一格,功能性也是NAS廠商中的佼佼者,我用了半年下來十分的滿意,我也因此慫恿了一些有相關需求的人買它們家的東西。

這次藉由參加這個活動,也開始思考我會希望他們再提供什麼功能,列舉如下
  • dropbox
  • version control file folder (具有版本控制的資料匣)
  • open api
  • 部分功能與nautilus or explorer結合

最後,若有興趣瞭解更多DSM 3.2 Beta功能的人可以參考當天投影片[這裡下載]

2011/06/09

zim plugins: 個人筆記軟體

Why zim?

前陣子開始習慣用zim來當做個人筆記的軟體,在使用zim之前我survey了許多其他筆記軟體,像是Evernote,Nevernote,Tomboy,gnote等。最後我還是選擇了zim,主要有以下原因
  • wiki語法
  • 所見即所得的介面
  • 筆記以文字檔儲存
  • 內建常用格式 (h1, h2, bold, italy, code...)
  • 容易hack (python)
  • 配合dokuwiki使用,可當公司內的文件分享系統
  • 配合dropbox,便成雲端筆記本
  • 配合shutter,螢幕截圖超方便
這篇文章,主要會介紹一個搜尋文件程式及如何將zim的文件發佈要dokuwiki上。


zim search

zim search是一個視窗程式,以wxPython寫成,輸入欲搜尋字串,它會將有此字串的文件列出,並依照權重排序,雙點任何列便可直接在zim中開啟該文件。此工具截圖如下



設定

這隻程式接受2個參數,第一為zim筆記本名,第二為筆記本路徑,zim裡的tools -> custom tools設定如下




源碼find.py可由此抓到 https://github.com/fcwu/zim-plugins

zim wiki deploy

為了分享我的筆記給其他同仁觀看,我架了dokuwiki當做分享平台,dokuwiki的儲存格式(文字檔)及wiki語法跟zim只有些許的不同,為了做語法的轉換及附件(圖片,或附檔)的發佈,因此我寫了這個腳本。

用此腳本發佈在dokuwiki的結果如下圖:



zim上觀看如下




設定

此腳本接受的參數如下
  1. 筆記本名
  2. dokuwiki root path
  3. user name
  4. 附件路徑
  5. 文本路徑
其中user name是用來區別你的文件應該存放的位置。舉例來說dokuwiki root path為/var/www/wiki/,user name為doro則文件會發佈放在/var/www/wiki/data/pages/doro,附件會發佈在/var/www/wiki/data/media/doro

使用前請先確定已建立這些路徑,且擁有寫入權限。zim設定如下圖:



源碼deploy_wiki.py可由此抓到 https://github.com/fcwu/zim-plugins

2010/09/14

Publish Your WxWidget Program


Publish Your wxWidget Program

初入wxWidget時,是使用wxDev-C++當作開發編譯環境,後來不經意的使用了VS2005的環境,便開始愛上cl.exe那麼飛快編譯速度、Visual Studio良好Debug環境。

但最近在發行wxWidget程式(Release Version)時,在其他未安裝Visual Studio的電腦上無法執行的問題,將錯誤訊息google過後發現是編譯參數/MD的關係,將/MD改成/MT便行(在project屬性頁的組態屬性-->C/C++-->程式碼產生裡)。

改完重編後,又發生
------ 已啟動全部重新生成: 項目: WxWidgetsWizardTest, 配置: Debug Win32 ------
正在刪除項目「WxWidgetsWizardTest」(配置「Debug|Win32」)的中間文件和輸出文件
正在編譯...
WxWidgetsWizardTestFrm.cpp
正在編譯...
WxWidgetsWizardTestApp.cpp
正在編譯資源清單...
正在鏈接...
MSVCRTD.lib(MSVCR80D.dll) : error LNK2005: _free 已經在 LIBCMT.lib(free.obj) 中定義
MSVCRTD.lib(ti_inst.obj) : error LNK2005: "private: __thiscall type_info::type_info(class type_info const &)" (??0type_info@@AAE@ABV0@@Z) 已經在 LIBCMT.lib(typinfo.obj) 中定義
MSVCRTD.lib(ti_inst.obj) : error LNK2005: "private: class type_info & __thiscall type_info::operator=(class type_info const &)" (??4type_info@@AAEAAV0@ABV0@@Z) 已經在 LIBCMT.lib(typinfo.obj) 中定義
LINK : warning LNK4098: 默認庫「LIBCMT」與其他庫的使用衝突;請使用 /NODEFAULTLIB:library
LINK : warning LNK4098: 默認庫「MSVCRTD」與其他庫的使用衝突;請使用 /NODEFAULTLIB:library
LINK : warning LNK4098: 默認庫「LIBCMTD」與其他庫的使用衝突;請使用 /NODEFAULTLIB:library
D:\WxWidgetsWizardTest\Debug\WxWidgetsWizardTest.exe : fatal error LNK1169: 找到一個或多個多重定義的符號
生成日誌保存在「file://d:\WxWidgetsWizardTest\WxWidgetsWizardTest\Debug\BuildLog.htm」
WxWidgetsWizardTest - 4 個錯誤,3 個警告
========== 全部重新生成: 0 已成功, 1 已失敗, 0 已跳過 ==========
所以我又去google這個錯誤,發現是library部分也要以維持一致性,順手將C:\wxWidgets-2.9.0\build\msw\wx_vc8.sln打開後,並將所有的/MD改成/MT,重編後便OK了。

若還是有遇到問題,不彷注意一下自己連結的library裡,是否有用到debug的版本,library及程式編譯參數最好一模一樣,這樣碰到的問題會較少。