所以,驱动设备在linux系统里就会是一个文件的形式,eg:/dev/znr_dev就是把我的虚拟驱动程序假装一个文件展现
主设备号区分不同的驱动程序,相同类型的驱动程序主设备号相同(2个u盘主设备号相同)
从设备号区分具体的设备(2个u盘从设备号不同)
系统调用是内核 和 应用程序 的接口
设备驱动程序是内核 和 硬件 的接口
struct file
表示一个打开的文件,系统每次调用open就打开一次,在内核空间里创建关联的struct file,重要的成员:f_pos, f_op ..
struct inode
表示一个物理文件,记录文件上的物理信息。它和打开的文件结构不一样,一个文件可能有多个file结构体,但只有一个inode结构,在磁盘上存的时候的inode(操作系统文件系统部分)
struct file_operations
open/relase,这是一对,对应应用程序的open,close,这俩个函数会传入inode参数中。
在内核空间性,kmalloc/kfree申请释放空间,
kamlloc(sizeof(XX),GFP_KERNEL)
后面哪个我也不清楚是什么意思,不过需要加
主体需要实现open,release,这俩个函数是必须实现,并且绑定的
在file_operations结构体中,这俩个函数会传入inode参数。
其他的函数自己按需求写。
常用的read,write,ioctl,读,写,控制
static int dev_init()/static void dev_exit()
这俩个函数主要用于注册,删除设备
当注册模块与设备一样的主设备好,名称时,绑定为设备的驱动程序。
这俩个静态函数绑定在宏定义上,
module_init(dev_init);
module_exit(dev_exit);
引导驱动程序初始化
再加上程序的信息,就完成了驱动程序的基本功能。
MODULE_DESCRIPTION("znr experiment1 drive");
MODULE_LICENSE("GPL");
““c
void show_info(void) //show_info()这样写回报错,必须加void
{
printk(“******\n”);
printk(“\t%s\n”DEV_INFO);
printk(“******\n\n”);
}
size_t dev_read(struct file *file, char *buffer,size_t count, loff_t *f_ops) // f_ops 是偏移量
{
copy_to_user(des_buffer, src_buffer, count) //do read
}
size_t dev_write(struct file *file, char *buffer,size_t count, loff_t *f_ops)
{
copy_from_user(des_buffer, src_buffer, count) // do write
}
int dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data)
{
//io control
}
int dev_open(struct inode inode, struct file file)
{
//open dev
}
int dev_release(struct inode *inode, struct file *file)
{
//relase dev
}
//上下俩种写法都是有的,不过下面兼容性好一点,或者上面这种有时候根本编译不过
struct file_operations dev_operations=
{
open: dev_open,
read: dev_read,
write: dev_write,
ioctl: dev_ioctl,
release: dev_release,
};
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.compat_ioctl = device_ioctl,
.release = device_release
};
//关于这里详细可以看下面的链接,常用的就是这5个函数
static int dev_init(void)
{
//注册设备
register_chrdev(dev_main_num, dev_name, &fops);
}
static void dev_exit(void)
{
// 释放设备
unregister_chrdev(dev_main_num, dev_name);
}
MODULE_DESCRIPTION(“znr experiment1 drive”);
MODULE_LICENSE(“GPL”);
module_init(dev_init);
module_exit(dev_exit);
““
先注册驱动设备,在加载编译好的驱动程序模块,在驱动程序在module_init()向系统注册信息时候,绑定到创建的驱动设备上,实现驱动设备。
以下是示例过程。
““
mknod /dev/znr_dev c 83 0
ls -l /dev | grep znr_dev
chmod 777 /dev/znr_dev(可能需要)
insmod znr_dev.ko
现在就可以使用了,驱动程序加载好了
rm /dev/znr_dev
““
module_param(name,type, perm);
在全局变量里定义,然后再用这个宏定义定义。perm是权限的参数,一般用宏定义S_IRUSR即可。
宏MODULE_PARM_DESC()
用来注解该模块可以接收的参数。该宏两个参数:变量名和一个对该变量的描述。
加载内核模块时候,只要在后面跟上参数列表就行,
假设我定义了一个N的全局变量为模块参数,为int类型的,一个name,char*类型的
insmod drive.c N=1024name="my_drive_description"
如果需要声明数组参数:
module_param_array(test,int,num,S_IRUSR);
使用这个,name,type,元素个数,权限
static int test[5] = {1,2,3,4,5};
static int num =5;
module_param(num,int,S_IRUSR);
module_param_array(test,int,num,S_IRUSR);
MODULE_PARM_DESC(test, "test array");
参数数组的加载方式:
insmod test.ko test=6,7,8,9,10 num=5
关于register_chrdev注册字符设备的介绍:
http://book.51cto.com/art/201205/337666.htm
关于Liunx驱动开发比较详细的博客:
http://blog.csdn.net/xinyuwuxian/article/details/9345929
关于file_operations结构体的介绍:
http://blog.csdn.net/zkx1982/article/details/2540401
关于makefile编译的过程:
http://www.cnblogs.com/QuLory/archive/2012/10/23/2736339.html