Linux设备驱动分析之热拔插

一、kernel

内核2个分支到达device_register(),之后相同;


(1) device_create()//creates a device and registers it with sysfs

    device_create_vargs()

        device_register(dev);

(2) class_dev_create()

    device_register(dev);

        device_add(dev);

            ...

            device_create_file(dev, &uevent_attr);

            ...

            error = device_create_file(dev, &devt_attr);

            ...

            device_create_sys_dev_entry(dev);

            devtmpfs_create_node(dev);

            ...

           device_add_class_symlinks(dev);

           device_add_attrs(dev);

           bus_add_device(dev);

           dpm_sysfs_add(dev);

           device_pm_add(dev);

           kobject_uevent(&dev->kobj, KOBJ_ADD);//热拔插(uevent)

               kobject_uevent_env(kobj, action, NULL);

                   /* environment buffer 用于保存环境变量 */
                   env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);

                    ...

                   /* default keys 用户保存设备驱动动作(加载add/卸载remove) */
                   retval = add_uevent_var(env, "ACTION=%s", action_string);
                    ...
                   retval = add_uevent_var(env, "DEVPATH=%s", devpath);//设备驱动路径
                    ...
                   retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);//子系统

                    ...

                   #if defined(CONFIG_NET)//实现内核态与应用态通讯--netlink

                        skb = alloc_skb(len + env->buflen, GFP_KERNEL);

                        ...

                       retval = netlink_broadcast_filtered(uevent_sock, skb,
                                                                             0, 1, GFP_KERNEL,
                                                                              kobj_bcast_filter,
                                                                              kobj);

                   #endif

                   ...

                   /* call uevent_helper, usually only enabled during early boot */

                   /*辅助函数uevent_helper(/sbin/mdev,mdev是busybox的杰作)用于对热拔插设备进行创建及删除 */
                    if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
                       char *argv [3];
                       argv [0] = uevent_helper;
                       argv [1] = (char *)subsystem;
                       argv [2] = NULL;
                       retval = add_uevent_var(env, "HOME=/");
                       if (retval)
                           goto exit;
                       retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
                       if (retval)
                           goto exit;

                       /*调用热拔插辅助函数uevent_helper(即/sbin/mdev */
                       retval = call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC);
                   }

           bus_probe_device(dev);



其中:

uevent_helper为:/sbin/mdev(根据启动脚本/etc/init.d/rcS中的echo /sbin/mdev > /proc/sys/kernel/hotplug)

uevent_helper根据envp[]中环境变量(如:HOME, ACTION, PATH, DEVPATH, SUBSYSTEM, SEQNUM, MAJOR, MINOR)


假设打印环境变量(envp)时终端输出内容如下:

HOME = /

PATH=/sbin:/bin:/usr/sbin;/usr/bin

ACTION=add                                    //表明驱动要执行加载动作

DEVPATH=/module/mytest_driver    //mytest_driver为设备名

SUBSYSTEM=module

SEQNUM=500

MAJOR=250                                     //主设备号

MINOR=0                                          //次设备号



二、/etc/init.d/rcS:

mount -t sysfs sysfs /sys    //busybox将会使用/sys目录,因此/sys必须要挂载(见下文)

mdev -s//-s: 在系统启动时扫描/sys目录,在/dev下创建设备节点

echo /sbin/mdev > /proc/sys/kernel/hotplug//热拔插由/sbin/mdev支持,内核讲调用/sbin/mdev创建及删除设备节点



三、busybox:

mdev.c


mdev_main()

{

    /* Scan扫描 */

    if (argc == 2 && !strcmp(argv[1], "-s")){//如果是系统启动时被调用,则执行mdev -s

    }

    /* Hotplug热拔插 */

    else {

        action = getenv("ACTION");

        evn_path = getevn("DEVPATH");

        sprintf(temp, "/sys%s", evn_path);

        if (!strcmp(action,"remove"))//卸载

            make_device(tmp, 1);//删除设备

        else if (!strcmp(action,"add"))//装载

            make_device(tmp, 0);//创建设备

    }

}


make_device(char *path, int delete)

{

    if (!delete) {//添加设备

        strcat(path, "/dev");

        ...

    }

    /* detemine device name, type, major and minor设备名,类型,主次设备号 */

    /* 假设path=/sys/class/dev/mytest_driverbb_basename从path末尾查找‘/’,并找到‘/’后面的mytest_driver字串 */

    device_name = bb_basename(path);


    /* /sys/class都是字符设备驱动,因此第5个字符为‘c’时,该设备为字符设备 */

    type = path[5]=='c' ?S_IFCHR : S_IFBLK;//如果path[5]为字符c,则该设备类型为字符设备,否则为块设备


        /* 通过cat  /sys/class/dev/mytest_driver/dev命令可以得到设备的主次设备号 */


    /* If we have a config file, look up permissions for this device */

    /* 如果/etc目录中有mdev.conf配置文件,则根据配置文件执行相应脚本创建/删除设备 */

    if (ENABLE_FEATURE_MDEV_CONFG) {//见mdev.conf

        ...

        /* mmap the config file */

        fd = open("/etc/mdev.conf", O_RDONLY);

        ...

    }

   

    if (!delete) {//添加设备

        if (sscanf(tmp, "%d:%d", &major, &minor) != 2)    return;//tmp=/sys/class/dev/mytest_driver/dev

        mknod(device_name, mode | type, mkdev(major, minor))//创建设备节点

    }

    ...

}



四、mdev.conf:


busybox/doc/mdev.txt


mdev.conf的格式:
<device regex> <uid>:<gid> <octal permissions> [<@|$|*> <command>]

device regex:正则表达式,表示哪一个设备
uid: owner
gid: 组ID
octal permissions:以八进制表示的属性
@:创建设备节点之后执行命令
$:删除设备节点之前执行命令
*: 创建设备节点之后 和 删除设备节点之前 执行命令
command:要执行的命令



例1:leds 0:0 777

例2:leds?[123]? 0:0 777

例3:leds?[123]? 0:0 777 @ echo create /dev/$MDEV > /dev/console

例4:leds?[123]? 0:0 777 * if [ $ACTION = "add" ]; then echo create /dev/$MDEV > /dev/console; else echo remove /dev/$MDEV > /dev/console; fi

例5:leds?[123]? 0:0 777 * /bin/add_remove_led.sh

add_remove_led.sh内容如下:

#!/bin/sh
if [ $ACTION = "add" ];
then
    echo create /dev/$MDEV > /dev/console;
else
    echo remove /dev/$MDEV > /dev/console;
fi

例6:U盘自动加载

sda[1-9]+ 0:0 777 * /bin/add_remove_udisk.sh

add_remove_udisk.sh
#!/bin/sh
if [ $ACTION = "add" ];
then
    mount /dev/$MDEV /mnt;
else
    umount /mnt;
fi



五、通配符:


. : 表示除换行符以外的任意字符

* : 重复0次或跟多次

+ : 重复1次或更多次

? :重复0次或1次

[abc] : 表示abc三个字符中的某一个,又如[1-9]表示1至9中的某一个




例1:leds? :

表示?前面的字符s重复0次或1次


例2:leds?[123]?:

表示?(左起第1个)前面的字符s可能重复出现0次或1次,?(左起第2个)前面[123]中1、2、3可能会出现0次或1次



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
将可移动设备连入系统时,系统的后台中会依次发生如下事件: l 内核检测到新硬件入,然后分别通知hotplug和udev。前者用来装入相应的内核模块(如usb-storage),而后者用来在/dev中创建相应的设备节点(如/dev/sda1)。 l udev创建了相应的设备节点之后,会将这一消息通知hal的守护程序(hald)。当然udev还得保证新创建的设备节点可以被普通用户访问。 l hotplug装入了相应的内核模块之后,会把这一消息通知给hald。 l hald在受到hotplug和udev发出的消息之后,认为新硬件已经正式被系统认可了。此时它会通过一系列精心编写的规则文件(就是传说中的xxx-policy.fdi),把发现新硬件的消息通过dbus发送出去,同时还会调用update-fstab或fstab-sync来更新/etc/fstab,为相应的设备节点创建适合的挂载点。 l 卷管理器会监听dbus中发现新硬件的消息。根据所入的硬件(区分U盘和数码相机等)不同,卷管理器会先将相应的设备节点挂载到hald创建的挂载点上,然后再打开不同的应用程序。 当然,如果是在CDROM中入光盘,过程可能比较简单。因为CDROM本身就是一个固定的硬件,无需hotplug和udev的协助: l hald会自己监视CDROM,并且将光盘托架开合的消息通过dbus发出去。 l 卷管理器负责检查CDROM中的盘片内容,进行挂载,并调用合适的应用程序。 要注意,hald的工作是从上游得到硬件就绪的消息,然后将这个消息转发到dbus中。尽管它会调用程序来更新fstab,但实际上它自己并不执行挂载的工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值