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


Buildroot - 打造Embedded Linux 工作環境

Buildroot for ARM


Buildroot 是一套用來打造 Embedded Linux System工具, 可以在此平台上編譯產生不同系統的Root File System, Toolchain。

打造工作環境:Embedded Linux for ARM

1. 下載Buildroot
     官方網站: https://buildroot.org/download.html

      下載檔案:buildroot-2012.11.1.tar.bz2 / buildroot-2012.11.1.tar.gz

2. 解壓縮
     $tar zxvf buildroot-2012.11.1.tar.gz
2.1安裝其它package

EX:     [GM8138S install package: 64bit ubuntu]
$ sudo apt-get install g++

$ sudo apt-get install lib32z1-dev //compression library - 32 bit runtime

$ sudo apt-get install lib32z1
$ sudo apt-get install libncurses5-dev //shared libraries for terminal handling
$ sudo apt-get install libncurses5
$ sudo apt-get install bison //YACC-compatible parser generator - development library
$ sudo apt-get install flex //A fast lexical analyzer generator.
$ sudo apt-get install texinfo //Documentation system for on-line information and printed output

EX:     [GM8138S install package: 32bit ubuntu]
$ sudo apt-get install g++
$ sudo apt-get install bison
$ sudo apt-get install flex
$ sudo apt-get install texinfo


3. 設定組態
  
      $ make menuconfig
    #組態選擇
    (1)選擇 arm
Target Architecture (arm)  ---> 
    (2)選擇 generic_arm
Target Architecture Variant (generic_arm)  ---> 
    (3)選擇 EABI
Target ABI (EABI)  --->

   ABI:  application binary interface
OABI: old application binary interface
    (4)選擇 Linux kernel 3.2 / busybox 1.19.x/ binutils 2.21.1 / uClibc 0.9.32.x /gcc 4.6.4 


    $ make
    $ ls -al output/                             //編譯成功輸出於output folder
   
   

[Note ***]
   #if 編譯錯誤1:
   buildroot You may have to install 'g++' on your build machine
   
   #[解決1] 安裝上述提示軟體套件:
   $ sudo apt-get install g++
   $ sudo apt-get install texinfo
   $ sudo apt-get install unzip

  #if 編譯錯誤2: ./output/build/host-m4-1.4.16/lib/stdio.in.h

In file included from clean-temp.h:22:0,
                 from clean-temp.c:23:
./stdio.h:477:1: error: 'gets' undeclared here (not in a function)
 _GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
 ^
mv -f .deps/c-stack.Tpo .deps/c-stack.Po
/usr/bin/gcc -std=gnu99  -I.     -O2 -I/home/paddy/workspace/project/GM/gm8138s/arm-linux-3.3/buildroot-2012.02/output/host/include -I/home/paddy/workspace/project/GM/gm8138s/arm-linux-3.3/buildroot-2012.02/output/host/usr/include -MT close-hook.o -MD -MP -MF .deps/close-hook.Tpo -c -o close-hook.o close-hook.c
mv -f .deps/close-hook.Tpo .deps/close-hook.Po
/usr/bin/gcc -std=gnu99  -I.     -O2 -I/home/paddy/workspace/project/GM/gm8138s/arm-linux-3.3/buildroot-2012.02/output/host/include -I/home/paddy/workspace/project/GM/gm8138s/arm-linux-3.3/buildroot-2012.02/output/host/usr/include -MT execute.o -MD -MP -MF .deps/execute.Tpo -c -o execute.o execute.c
make[4]: *** [clean-temp.o] Error 1
make[4]: *** Waiting for unfinished jobs....
mv -f .deps/execute.Tpo .deps/execute.Po

   #[解決2]patch file下:
   +diff -purN host-m4-1.4.16.orig/lib/stdio.in.h host-m4-1.4.16/lib/stdio.in.h
+--- host-m4-1.4.16.orig/lib/stdio.in.h 2012-07-21 19:11:40.196541826 +0200
++++ host-m4-1.4.16/lib/stdio.in.h 2012-07-21 20:46:05.405850751 +0200
+@@ -162,7 +162,9 @@ _GL_WARN_ON_USE (fflush, "fflush is not
+ so any use of gets warrants an unconditional warning. Assume it is
+ always declared, since it is required by C89. */
+ #undef gets
++#if defined(__GLIBC__) && !defined(__UCLIBC__) && !__GLIBC_PREREQ(2, 16)
+ _GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
++#endif

  #if 編譯錯誤3: ./output/build/host-autoconf-2.65/doc/autoconf.texi 
conftest.c:14625: must be after `@defmac' to use `@defmacx'
make[3]: *** [autoconf.info] Error 1
make[3]: Leaving directory `/home/paddy/workspace/project/GM/gm8138s/arm-linux-3.3/buildroot-2012.02/output/build/host-autoconf-2.65/doc'
make[2]: *** [install-recursive] Error 1
make[2]: Leaving directory `/home/paddy/workspace/project/GM/gm8138s/arm-linux-3.3/buildroot-2012.02/output/build/host-autoconf-2.65'
make[1]: *** [install] Error 2

 #[解決3]patch file下:
--- autoconf-2.65/doc/autoconf.texi 2009-11-05 10:42:15.000000000 +0800
+++ autoconf-2.65/doc/autoconf.texi.new 2013-05-28 05:41:09.243770263 +0800
@@ -15,7 +15,7 @@
 @c The ARG is an optional argument.  To be used for macro arguments in
 @c their documentation (@defmac).
 @macro ovar{varname}
-@r{[}@var{\varname\}@r{]}@c
+@r{[}@var{\varname\}@r{]}
 @end macro
 @c @dvar(ARG, DEFAULT)
@@ -23,7 +23,7 @@
 @c The ARG is an optional argument, defaulting to DEFAULT.  To be used
 @c for macro arguments in their documentation (@defmac).
 @macro dvar{varname, default}
-@r{[}@var{\varname\} = @samp{\default\}@r{]}@c
+@r{[}@var{\varname\} = @samp{\default\}@r{]}
 @end macro

以上!