Linux内核学习-字符设备驱动学习(一)

Linux内核学习-字符驱动学习(一)

 

现在学习一下Linux的字符设备驱动,参考的样本应该就是ldd3这书大概第3章的内容吧。

下面的所说的字符设备都是基于2.6内核的,

一般的流程都是,呵呵,其实也不算是一般的流程了,只是手动加载字符设备驱动的一种方法吧,

1.    先使用register_chrdev_region或alloc_chrdev_region来注册一个字符设备的设备号,大家都知道Linux是通过设备号来找到相应的驱动程序的,所以你要注册字符设备的时候,需要一个设备号或系统为你指定一个设备号。这两函数的差别就是register_chrdev_region是注册指定设备号的字符设备,这个如果注册设备号已经存在则有可能会失败,而alloc_register_region是系统分配一个未使用的设备号然后再注册

2.    再使用cdev_add函数,把该设备号相应的文件操作file_operation文件操作入口表添加到内核的散列表里面,其中file_operation是需要你在驱动里面实现的。然后使用命令mknod,创建与该字符设备(设备号)对应的设备节点,当有用户程序打开这个设备并对其操作时候,系统会调用kobj_lookup() 函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops (file_operation)字段,跳到相应的驱动入口去执行。

3.    当需要卸载驱动的时候(手动加载),需要做一些清理工作,cdev_del函数把散列表里面的数据清除,再调用unregister_chrdev_region.

 

 

下面简单介绍一下相关的函数:

int register_chrdev_region(dev_t first, unsigned int count,
char *name);   //指定设备编号

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,
unsigned int count, char *name);   //动态生成设备编号

void unregister_chrdev_region(dev_t first, unsigned int count);      //释放设备编号

上面有个count参数,文字意义上是数量的意思,而从函数名中的_region可以看出是注册某一范围的设备号的

把这两个结合起来就是说,注册使用从first[或dev]到(frist+count)[或dev+count],这个编号范围的设备使用该驱动。。。

 

内核用dev_t类型(<linux/types.h>)来保存设备编号,dev_t是一个32位的数,12位表示主设备号,20为表示次设备号。
在实际使用中,是通过<linux/kdev_t.h>中定义的宏来转换格式。

 (dev_t)-->主设备号、次设备号  MAJOR(dev_t dev)
 MINOR(dev_t dev)
 主设备号、次设备号-->(dev_t)  MKDEV(int major,int minor) 

 

如果只是在module_init函数里面做上面的这一步,加载内核模块的时候,会可以cat /proc/devices里面看到你注册的设备和主设备号。

 

大家应该都知道file_operation函数的,它在<linux/fs.h>文件里面有定义,是一个保存驱动操作函数入口的结构体,我们要做的工作基本都在这里,完成这一步之后,使用cdev_add函数,把设备号与相应的file_operation指针添加到系统cdev_map 散列表里面去,

初始化struct cdev
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
初始化cdev.owner
cdev.owner = THIS_MODULE;

cdev.ops = &fops;
cdev设置完成,通知内核struct cdev的信息(在执行这步之前必须确定你对struct cdev的以上设置已经完成!)
int cdev_add(struct cdev *p, dev_t dev, unsigned count)

这里面的count的意义跟上面register_chrdev_region中的是一样的。
从系统中移除一个字符设备:void cdev_del(struct cdev *p)

 

完成这一步之后,你已经注册了字符设备,但是你还不能对它进行操作(打开,关闭读,写等),你需要使用mknod命令手动的去生成一个设备号对应的索引节点,这个需要管理员权限的,你的主设备号可以通过cat /proc/devices看到

mknod - make block or character special files
mknod [OPTION]... NAME TYPE [MAJOR MINOR]
    option 有用的就是 -m 了
    name   自定义
    type   有 b 和 c 还有 p
    主设备号
    次设备号

sudo mknod /dev/char01 c 250 0

 

最后你可以在你的用户程序里面使用open("/dev/char01", O_RDONLY);去打开这个设备了,

由于创建/dev/*节点的时候,是只有管理员有权限的,所以你的测试程序也需要管理员权限执行。

下面是相应的源文件:

char01.c

#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/kdev_t.h> #include <linux/cdev.h> ///////////////////////////////////////////////// MODULE_LICENSE("Dual BSD/GPL"); ///////////////////////////////////////////////// int char01_open(struct inode *inode, struct file *filp) { printk("char01 open!/n"); return 0; } int char01_release(struct inode *inode, struct file filp) { printk("char01 release!/n"); return 0; } ssize_t char01_read(struct file *filp, char *buf, size_t count, loff_t f_pos) { printk("char01 read!/n"); return 0; } ssize_t char01_write(struct file *filp, char *buf, size_t count, loff_t f_pos) { printk("char01 write!/n"); return 0; } ///////////////////////////////////////////////// struct file_operations fops = { .owner = THIS_MODULE, .open = char01_open, .release= char01_release, .read = char01_read, .write = char01_write }; dev_t devno; int nr = 5; struct cdev dev; int setup_char01(void) { int result; int major; result = alloc_chrdev_region(&devno, 0, nr, "char01"); if (result != 0) { printk("alloc_chrdev_region failed!/n"); return -1; } printk("alloc_chrdev_region ok!/n"); major = MAJOR(devno); cdev_init(&dev, &fops); dev.owner = THIS_MODULE; dev.ops = &fops; dev.dev = devno; if (cdev_add(&dev, devno, nr) < 0) { printk("cdev_add failed!/n"); unregister_chrdev_region(devno, nr); return -1; } printk("cdev_add ok!/n"); return 0; } ///////////////////////////////////////////////// static int __init char01_init(void) { printk("char01 init!/n"); return setup_char01(); } static void __exit char01_exit(void) { printk("char01 exit!/n"); unregister_chrdev_region(devno, nr); cdev_del(&dev); } module_init(char01_init); module_exit(char01_exit);

Makefile:

obj-m := char01.o PWD := $(shell pwd) K_DIR := /lib/modules/$(shell uname -r)/build all: $(MAKE) -C $(K_DIR) M=$(PWD) modules clean: $(MAKE) -C $(K_DIR) M=$(PWD) clean test:char01_test.o gcc -o $@ $<

测试文件,char01_test.c

#include <stdio.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> ///////////////////////////////////////////////// int main(int argc, char **argv) { int fd; fd = open("/dev/char01", O_RDONLY); if (fd < 0) { printf("open /dev/char01 failed!/n"); printf("%s/n", strerror(errno)); return -1; } printf("open /dev/char01 ok!/n"); close(fd); return 0; }

 

参考文章:

http://blog.chinaunix.net/space.php?uid=20543672&do=blog&id=94290

你可能感兴趣的:(struct,File,Module,character,makefile,linux内核)