网名 鱼树 的学员聂龙浩, 学习 韦东山 Linux 视频第 2 期 时所写的笔记很详细, 供大家参考 也许有错漏, 请自行分辨 目录 驱动框架分析... 2 1. 2.4 内核中的理解 :... 2 2. 2.6 内核这样理解 :... 2 RTC... 6 1. 测试 RTC:... 11 1. 1. 修改 arch\arm\plat-s3c24xx\common-smdk.c... 11 2. 2. make uimage, 使用新内核启动... 12 3. 3. ls /dev/rtc* -l... 12
驱动框架分析 1. 2.4 内核中的理解 : 1, 确定主设备号 2,file_operations 结构 3,register_chrdev( 主设备号, 名字,file_operations 结构 ); 4, 入口函数 5, 出口函数 chrdevs 数组, 以 主设备号 为下标的 file_operations 结构数组 2. 2.6 内核这样理解 : 1,chrdevs 数组只用 255, 则一个内核只能支持 255 个字符设备驱动 以前 2.4 内核确实是有这样的缺点 但 2.6 内核中很多书上建议不再用 register_chrdev() 了 2, 以前 open 一个字符设备时, 虚拟文件系统 (VFS) 层, 有 sys_open, 以前是以主设备号为下标, 在 chrdevs 数中找到以前注册的 file_oprerations 结构体, 现在变化了, 是以 主设备号 和 次设备号 两个作为一个整体来找到 file_operations 结构体 分析 drivers/rtc/rtc-s3c.c : int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops) -->cdev = cdev_alloc(); 搜索 cdev_alloc(), 可以见到别人的展开, 在 scx200_gpio.c int init scx200_gpio_init(void) -->rc = register_chrdev_region(devid, MAX_PINS, "scx200_gpio"); 参 1, 参 2 的意思 : 从哪里开始, 共有多少个 -->cd = register_chrdev_region(major(n), MINOR(n),next - n, name); -->rc = alloc_chrdev_region(&devid, 0, MAX_PINS, "scx200_gpio"); -->cdev_init(&scx200_gpio_cdev, &scx200_gpio_fileops); -->cdev_add(&scx200_gpio_cdev, devid, MAX_PINS); 3,2.6 内核中对注册字符设备的扩展 : 对 "register_chrdev" 的拆分 ❶, 若确定了 主设备号 时用 register_chrdev_region(). 没有确定主设备号时用 alloc_chrdev_region() 区域是指从( 某个主设备号 某个次设备号 )~ ( 某主设备号, 某次设备号 +n) 都对应于这个 file_operations 结构体 而 register_chrdev() 是从 主设备号 0 到 主设备号 255 都对应 file_operations" 结构体 ❷,cdev_init(); ❸,cdev_add(); 以上 ❶~❸ 步, 在 "register_chrdev()" 中可以见到此过程 :
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) -->cd = register_chrdev_region(major, 0, 256, name); -->if (major == 0) -->cdev = cdev_alloc(); --> 开始设置 cdev->owner = fops->owner; cdev->ops = fops; kobject_set_name(&cdev->kobj, "%s", name); -->err = cdev_add(cdev, MKDEV(cd->major, 0), 256); int register_chrdev_region(dev_t from, unsigned count, const char *name) 有主设备号时, 有多少个次设备号的范围的字符设备都对些相同 file_operations 结构 参 1, 从哪里开始 参 2, 有多少个 参 3, 名字 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name) 若没有分配主设备号时, 先分配一个主设备号放到 &dev 中, 参 1, 存放主设备号 参 2, 次设备号的基地址 参 3, 个数 参 4, 名字 若 devid = MKDEV (major, 0); 时,register_chrdev_region (devid, 2, "hello"); 是指 (major,0~1) 对应 'hello_fops',(major,2~255) 不对应 helle_fops register_chrdev_region() 参 2 是指在有主设备号的情况下, 这里有 2 个次设备号 (major,0~1) 对应于这个 file_operations 结构体 -- hello_fops 若 devid = MKDEV (major, 1); 时,register_chrdev_region (devid, 2, "hello"); 是指 (major,1~2) 对应 'hello_fops',(major,3~255) 不对应 helle_fops 编译测试 : 这是缺少头文件 :#include <linux/cdev.h>
注册时, 就要求了设备号的区域 devid = MKDEV (major, 0); 表示了从主设备 major 和次设备号 0 开始 #define HELLO_CNT 2; /* 对应次设备号的个数 */ 规定了次设备号的个数为 2 个 现在是以 主设备号 和 次设备号 一起从内核 chrdevs[] 中找到相应的 file_operations 结构体 而 2.4 内核中的方法是以主设备号为下标从 chrdevs[] 中找到相应的 file_operations 结构 2.4 中最多有 255 个驱动程序 而且现在的 2.6 内核中, 用了 20 位表示次设备号, 主设备号是用了 12 位来表示, 即有 2^12 * 2^20 个驱动程序 理论上有 4G 个驱动程序
第二个测试程序 : 以相同的主设备号, 不同的次设备号注册一个新的 file_operations 结构体
RTC 实时时钟, 断电后可以维持 就像手机拔断电池后里面还有个备份电源在维持里面的一个 时钟在运行 给时钟模块使用的晶振
RTC 时钟的电源 有块电池 BT1 使得开发板断电后, 里面的 RTC 模块还是可以运行 的 RTC 模块耗电量很少, 一小块电池它就能维持一 二年 开发板上电后说无法打开 RTC 设备 开发板上电后, 时间总是从 1970 年开始 内核中有带 RTC 模块的驱动 : drivers/rtc/rtc-s3c.c int init s3c_rtc_init(void) -->platform_driver_register(&s3c2410_rtcdrv); 注册一个平台驱动 内核中有同名 s3c2410-rtc 的设备时, 则.probe 函数才会被调用
int s3c_rtc_probe(struct platform_device *pdev) -->s3c_rtc_tickno = platform_get_irq(pdev, 1); 从平台设备里获得某些信息 -->rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, THIS_MODULE); 注册一个 RTC 设备 linux-2.6.22.6\drivers\rtc\class.c rtc-dev,c 这是 RTC 的上一层 : -->rtc_class = class_create(this_module, "rtc"); 创建类 -->rtc_dev_init(); 在 rtc-dev.c 中, -->err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); 最多有 16 个次设备号 Class.c 中也实现了 rtc_device_register() rtc_device *rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops, struct module *owner) -->rtc_dev_prepare(rtc); 准备. -->rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id); -->cdev_init(&rtc->char_dev, &rtc_dev_fops); -->rtc_dev_add_device(rtc); -->cdev_add(&rtc->char_dev, rtc->dev.devt, 1); drivers\rtc\rtc-s3c.c s3c_rtc_init platform_driver_register s3c_rtc_probe rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, THIS_MODULE) rtc_dev_prepare cdev_init(&rtc->char_dev, &rtc_dev_fops); rtc_dev_add_device cdev_add
看 RTC 的读写操作 : static ssize_t rtc_dev_read(struct file *file, char user *buf, size_t count, loff_t *ppos) -->struct rtc_device *rtc = to_rtc_device(file->private_data); 用 private_data 私有数据得到一个 rtc_device 结构体 app: open("/dev/rtc0"); ------------------------------------------- kernel: sys_open rtc_dev_fops.open rtc_dev_open // 根据次设备号找到以前用 "rtc_device_register" 注册的 rtc_device struct rtc_device *rtc = container_of(inode->i_cdev,struct rtc_device, char_dev); const struct rtc_class_ops *ops = rtc->ops; err = ops->open? ops->open(rtc->dev.parent) : 0; s3c_rtc_open 关于 rtc_dev_fops 中的读写比较复杂, 可以看 busybox 中的 hwclock 的实现 : 使 用 ioctl() app: ioctl(fd, RTC_RD_TIME,...) ------------------------------------------- kernel: sys_ioctl rtc_dev_fops.ioctl
rtc_dev_ioctl struct rtc_device *rtc = file->private_data; 得到 rtc_device 结构体 rtc_read_time(rtc, &tm); err = rtc->ops->read_time(rtc->dev.parent, tm); s3c_rtc_gettime rtc_device 结构中有一个 const struct rtc_class_ops *ops; int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) --> 读相关寄存器 retry_get_time: rtc_tm->tm_min = readb(base + S3C2410_RTCMIN); 分 rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR); 时 rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE); 日期 rtc_tm->tm_mon = readb(base + S3C2410_RTCMON); 月 rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR); 年 rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC); 秒 在开发板启动后, 并没有加载 RTC 驱动, 但内核中其实已经有了驱动, 只是没有加载平台 设备
要注册这个 s3c_device_rtc 平台设备 可以看到这个 平台设备 并没有使用, 上面都是声明和引用 可以把这个结构体使用起来 :common-smdk.c arch\arm\plat-s3c24xx\common-smdk.c 内核中其他函数会把这个数据 smkd_devs[] 添加进去 : 修改这个文件后, 重新编译内核 :make uimage 1. 测试 RTC: 1. 1. 修改 arch\arm\plat-s3c24xx\common-smdk.c static struct platform_device initdata *smdk_devs[] = { &s3c_device_nand,
&smdk_led4, &smdk_led5, &smdk_led6, &smdk_led7, 改为 ( 在数组 smdk_devs 里加上 s3c_device_rtc): static struct platform_device initdata *smdk_devs[] = { &s3c_device_nand, &smdk_led4, &smdk_led5, &smdk_led6, &smdk_led7, &s3c_device_rtc, 2. 2. make uimage, 使用新内核启动 3. 3. ls /dev/rtc* -l date /* 显示系统时间 */ date 123015402011.30 /* 设置系统时间 date [MMDDhhmm[[CC]YY][.ss]] */ hwclock -w /* 把系统时间写入 RTC */ 短电, 重启, 执行 date