2016年6月21日 星期二

mdev 實現 SD card 或 USB mass storage 自動掛載(automount)



Embedded system中實現 SD card 或 USB mass storage 自動掛載(automount) ,是透過
busybox mdev來達成.

我分成幾個部份來討論實做:1.編譯組態  2.設定檔  3.程式流程

[1.編譯組態]

    組態部份需要編譯linux kernel與busybox.
==========
linux kernel:
==========
1. 檢查kernel路徑下 .config當案;確定選取了底下組態:
CONFIG_HOTPLUG = y
CONFIG_PROC_FS=y
CONFIG_SYSFS=y
CONFIG_NET=y
CONFIG_PROC_SYSCTL=y

<note1>如果選項未選取,請經由make menuconfig選取;請不要編輯.config!
<note2> Kernel 選項:
>>> Generic Driver Options
() path to uevent helper    /* 可以不用現在更改;由設定檔來更改;下面說明.*/
[*] Maintain a devtmpfs filesystem to mount at /dev
[*] Automount devtmpfs at /dev, after the kernel mounted the rootfs



==========
busybox-1.19.4
==========
>>> Build Options (我將busybox編譯為靜態連結)
[*] Build BusyBox as a static binary (no shared libs)

>>> Linux System Utilities
[*] mdev
[*] Support /etc/mdev.conf
[*] Support command execution at device addition/removal


[2.設定檔]

在設定檔,首先要更改初始化的script;然後更改/etc/mdev.conf
========
 init script
========
根據busybox說明文件(參考文獻1),需要在初始化過程執行script中加入底下設定!
Here's a typical code snippet from the init script:
[0] mount -t proc proc /proc
[1] mount -t sysfs sysfs /sys
[2] echo /sbin/mdev > /proc/sys/kernel/hotplug
[3] mdev -s

Alternatively, without procfs the above becomes:
[1] mount -t sysfs sysfs /sys
[2] sysctl -w kernel.hotplug=/sbin/mdev
[3] mdev -s


Of course, a more "full" setup would entail executing this before the previous
code snippet:
[4] mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev
[5] mkdir /dev/pts
[6] mount -t devpts devpts /dev/pts

我的範例:/squashfs_init (我的embedded system根目錄下的init)
#!/bin/busybox ash
echo "run squashfs_init"
/bin/busybox mount -t tmpfs -o mode=0755 tmpfs /dev
/bin/busybox mount -t tmpfs -o mode=0777 tmpfs /tmp
/bin/busybox mount -t tmpfs -o mode=0755 tmpfs /var
/bin/busybox mount -t tmpfs -o mode=0755 tmpfs /bin
/bin/busybox mount -t tmpfs -o mode=0755 tmpfs /usr
/bin/busybox mount -t tmpfs -o mode=0755 tmpfs /sbin

/bin/busybox mkdir -p /var/run
/bin/busybox mkdir -p /var/locks

/bin/busybox mkdir -p /dev/sys
/bin/busybox mkdir -p /dev/pts
/bin/busybox mkdir -p /dev/shm
/bin/busybox mkdir -p /usr/bin
/bin/busybox mkdir -p /usr/sbin
/bin/busybox mount -t proc   proc   /proc
/bin/busybox mount -t devpts devpts /dev/pts

/bin/busybox mount -t sysfs sysfs /sys
# Populate /dev according to /sys
#echo /etc/dt_sd.sh  > /proc/sys/kernel/hotplug
/bin/busybox --install -s
/bin/echo /sbin/mdev  > /proc/sys/kernel/hotplug
/sbin/mdev -s
ln -sf /bin/busybox /bin/linuxrc
ln -s /bin/wpa_supplicant  /bin/wpa_supplicant
ln -s /bin/wpa_cli /bin/wpa_cli
/bin/linuxrc
exec /sbin/init "$@" </dev/console >/dev/console 2>&1



<note1> /bin/echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev使用event來實作熱插拔動作(hotplug),上述這個設定會改變Linux kernel中的
event_helper 變數;當熱插拔動作發生時,Linux kernel會呼叫event_helper所指的user mode程式;
如此mdev便會被執行並判斷哪一個裝置發生熱插拔,並且mdev會根據mdev.conf執行內含的command.所以我們只要將對應的動作告訴mdev.conf,它就會完成我們想要完成的熱插拔動作.

=========
made.conf
=========
根據busybox說明文件(參考文獻1),檔案format如下:
<device regex> <uid>:<gid> <permissions> [=path] [@|$|*<command>]
    or
<device regex> <uid>:<gid> <permissions> [>path] [@|$|*<command>]
    or
<device regex> <uid>:<gid> <permissions> [!] [@|$|*<command>]

我的範例:/etc/mdev.conf

sd[a-z][0-9] 0:0 660  */etc/init.d/automount.sh
mmcblk[0-9]p[0-9] 0:0 660 */etc/init.d/automount.sh


<note>當熱插拔事件發生,mdev便會根據mdev.conf敘述進而執行automount.sh

我的範例:/etc/init.d/automount.sh
#!/bin/sh 

/bin/touch /var/plog

if [ "$MDEV" == "" ]; then
   echo "automount unknow error!" >> /var/plog
   exit 1
elif [ "$MDEV" == "mmcblk0p1" ]; then
    mmtype=sdcard
elif [ "$MDEV" == "sda1" ]; then 
    mmtype=usb
else
   echo "automount unknow dev:" >> /var/plog
   echo $MDEV >> /var/plog
   exit 1
fi

echo $mmtype >> /var/plog

case "$ACTION" in  
    add|"") 
       echo “Add device $MDEV>> /var/plog
       mount -t vfat /dev/$MDEV /mnt/$mmtype
        ;;  
    remove) 
       echo “remove device $MDEV>> /var/plog 
       umount /mnt/$mmtype
       ;;  
esac


<note1>由於event_helper將輸出轉向至/dev/null,所以我建立一個plog;將輸出全部丟往plog.

<note2> ACTION/MDEV/SUBSYSTEM :是event_helper 參數.


ACTION: add / remove
MDEV: sda1 / mmcblk0p1 ...SUBSYSTEM: block /mmc /drivers /queues /module /ftgpio_pdp /misc /platform /i2C...

以上,熱插拔自動掛載已經完成;接下來要談談發生熱插拔uevent_helper流程!


[3.程式流程]

當熱插拔(hotplug)發生時;kernel會送一個event,是由底下kernel程式負責:
/*./lib/kobject_uevent.c*/

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
               char *envp_ext[])
{ ...

}


程式分析當下熱插拔是由哪一個subsystem發生;發生什麼事情;並將熱插拔事件所有參數搜集後呼叫call_usermodehelper.


   
    subsystem = uevent_ops->name(kset, kobj); 
...
    retval = add_uevent_var(env, "ACTION=%s", action_string);
    if (retval)
        goto exit;
    retval = add_uevent_var(env, "DEVPATH=%s", devpath);
    if (retval)
        goto exit;
    retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
    if (retail)
        goto exit;
...
    retval = call_usermodehelper(argv[0], argv,
                         env->envp, UMH_WAIT_EXEC);


...


/*./include/linux/kmod.h*/
call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
{
     return call_usermodehelper_fns(path, argv, envp, wait,
                        NULL, NULL, NULL);
}


接著經過一些work queue初始化過程;程式呼叫如下:

呼叫>> ____call_usermodehelper(void *data)                // ./kernel/kmod.c

呼叫>> kernel_execve( )                                                  //./arch/arm/kernel/sys_arm.c
int kernel_execve(const char *filename,
            const char *const argv[],
            const char *const envp[])
{
...
    ret = do_execve(filename,
              (const char __user *const __user *)argv,
              (const char __user *const __user *)envp, &regs);
...

}


呼叫>> do_execve_common(  )                                        /* ./fs/exec.c */
static int do_execve_common(const char *filename,
                 struct user_arg_ptr argv,
                 struct user_arg_ptr envy,
                 struct pt_regs *regs)
{
...

}

以上!

[參考文獻]
1. made.txt


沒有留言:

張貼留言