一、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_driver,bb_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次