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
========
init script
========
根據busybox說明文件(參考文獻1),需要在初始化過程執行script中加入底下設定!
event_helper 變數;當熱插拔動作發生時,Linux kernel會呼叫event_helper所指的user mode程式;
如此mdev便會被執行並判斷哪一個裝置發生熱插拔,並且mdev會根據mdev.conf執行內含的command.所以我們只要將對應的動作告訴mdev.conf,它就會完成我們想要完成的熱插拔動作.
=========
made.conf
=========
根據busybox說明文件(參考文獻1),檔案format如下:
我的範例:/etc/mdev.conf
<note>當熱插拔事件發生,mdev便會根據mdev.conf敘述進而執行automount.sh
我的範例:/etc/init.d/automount.sh
<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流程!
/*./lib/kobject_uevent.c*/
程式分析當下熱插拔是由哪一個subsystem發生;發生什麼事情;並將熱插拔事件所有參數搜集後呼叫call_usermodehelper.
/*./include/linux/kmod.h*/
接著經過一些work queue初始化過程;程式呼叫如下:
呼叫>> ____call_usermodehelper(void *data) // ./kernel/kmod.c
呼叫>> kernel_execve( ) //./arch/arm/kernel/sys_arm.c
呼叫>> do_execve_common( ) /* ./fs/exec.c */
以上!
[參考文獻]
1. made.txt
[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
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, ®s);
...
}
呼叫>> 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