您的位置 首页 ADAS

驱动之路-简略字符设备驱动程序

驱动之路-简单字符设备驱动程序-内核用inode代表一个磁盘上的文件,它和file结构不同,后者表示打开的文件描述符。对于单个文件,可能会有许多个表示打开文件的文件描述符file结构,但他们都指单个inode结构。inode的dev_t i_rdev成员包含了真正的设备编号,struct cdev *i_cdev包含了指向struct cdev结构的指针。

一、重要知识点

1. 主次设备号

dev_t

dev_t是内核中用来表明设备编号的数据类型;

int MAJOR(dev_t dev)

int MINOR(dev_t dev)

这两个宏抽取主次设备号。

dev­_t MKDEV(unsigned int major, unsignedint minor)

这个宏由主/次设备号结构一个dev_t结构。

2. 分配和开释设备号

int register_chardev_region(dev_t first,unsigned int count, char *name)

静态请求设备号。

Int alloc_chardev_region(dev_t *dev,unsigned int firstminor, unsigned int count, char *name)

动态请求设备号,留意第一个参数是传地址,而静态则是传值。

3. 几种重要的数据结构

struct file

file结构代表一个翻开的文件,它由内核在open时创立,并传递给该文件上进行操作的一切函数,直到最终的close函数。

file结构private_data是跨体系调用时保存状况信息十分有用的资源。

file结构的f_ops 保存了文件的当时读写方位。

struct inode

内核用inode代表一个磁盘上的文件,它和file结构不同,后者表明翻开的文件描述符。关于单个文件,或许会有许多个表明翻开文件的文件描述符file结构,但他们都指单个inode结构。inode的dev_t i_rdev成员包含了真实的设备编号,struct cdev *i_cdev包含了指向struct cdev结构的指针。

struct file_operations

file_operaTIons结构保存了字符设备驱动程序的办法。

4. 字符设备的注册和刊出

struct cdev *cdev_alloc(void);

void cdev_init(struct cdev *dev, structfile_operaTIons *fops);

int cdev_add(struct cdev *dev, dev_t num,unsigned int count);

void cdev_del(struct cdev *dev);

用来办理cdev结构的函数,内核中运用该结构表明字符设备。留意cdev_add函数的count参数为次设备的个数,要想具有多个次设备,就有必要将该参数设为次设备的个数。

5. 并发处理

信号量和自旋的差异,运用信号量时当调用进程企图取得一个确定了的锁时会导致进程睡觉,而自旋锁则是一向循法的等候一向到该锁解锁了停止。

1)信号量

DECLARE_MUTEX(name);

DECLARE_MUTEX_LOCKED(name);

声明和初始化用在互斥方式中的信号量的两个宏

void init_MUTEX(struct semaphore *sem)

void init_MUTEX_LOCKER(struct semaphore*sem);

这两个函数能够在运行时初始化信号量

void down(struct semaphore *sem);

int down_interrupTIble(struct semaphore*sem);

int down_trylock(struct semahpore *sem);

void up(struct semaphore *sem);

确定和解锁信号量。假如必要,down会将调用进程置于不行中止的休眠状况;相反,down_interrupTIble可被信号中止。down_trylock不会休眠,而且会在信号量不行用时当即回来。确定信号量的代码最终有必要运用up解锁该信号量。

2)自旋锁

spionlock_t lock = SPIN_LOCK_UNLOCKED;

spin_lock_init(spinlock_t *lock);

初始化自旋锁的两种办法。

voidspin_lock(spinlock_t *lock);

确定自旋锁

voidspin_unlock(spinlock_t *lock);

解锁自旋锁

二、驱动代码

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#define MEMDEV_MAJOR 251  

#define MEMDEV_NUM 2  

#define MEMDEV_SIZE 1024  

struct mem_dev  

{  

unsignedint size;  

char*data;  

structsemaphore sem;  

};  

static int mem_major = MEMDEV_MAJOR;  

struct cdev mem_cdev;  

struct mem_dev *mem_devp;  

static int mem_open(struct inode *inode,struct file *filp)  

{  

structmem_dev *dev;  

unsignedint num;  

printk(“mem_open.\n”);  

num= MINOR(inode->i_rdev);//取得次设备号  

if(num> (MEMDEV_NUM -1))          //查看次设备号有效性  

return-ENODEV;  

dev= &mem_devp[num];  

filp->private_data= dev; //将设备结构保存为私有数据  

return0;  

}  

static int mem_release(struct inode *inode,struct file *filp)  

{  

printk(“mem_release.\n”);  

return0;  

}  

static ssize_t mem_read(struct file *filp,char __user *buf, size_t size, loff_t *ppos)  

{  

intret = 0;  

structmem_dev *dev;  

unsignedlong p;  

unsignedlong count;  

printk(“mem_read.\n”);  

dev= filp->private_data;//取得设备结构  

count= size;  

p= *ppos;  

//查看偏移量和数据巨细的有效性  

if(p> MEMDEV_SIZE)  

return0;  

if(count> (MEMDEV_SIZE-p))  

count= MEMDEV_SIZE – p;  

if(down_interruptible(&dev->sem))//确定互斥信号量  

return -ERESTARTSYS;  

//读取数据到用户空间  

if(copy_to_user(buf,dev->data+p, count)){  

ret= -EFAULT;  

printk(“copyfrom user failed\n”);  

}  

else{  

*ppos+= count;  

ret= count;  

printk(“read%d bytes from dev\n”, count);  

}  

up(&dev->sem);//解锁互斥信号量  

returnret;  

}  

static ssize_t mem_write(struct file *filp,const char __user *buf, size_t size, loff_t *ppos)//留意:第二个参数和read办法不同  

{  

intret = 0;  

structmem_dev *dev;  

unsignedlong p;  

unsignedlong count;  

printk(“mem_write.\n”);  

dev= filp->private_data;  

count= size;  

p= *ppos;  

if(p> MEMDEV_SIZE)  

return0;  

if(count> (MEMDEV_SIZE-p))  

count= MEMDEV_SIZE – p;  

if(down_interruptible(&dev->sem))//确定互斥信号量  

return-ERESTARTSYS;  

if(copy_from_user(dev->data+p,buf, count)){  

ret= -EFAULT;  

printk(“copyfrom user failed\n”);  

}  

else{  

*ppos+= count;  

ret= count;  

printk(“write%d bytes to dev\n”, count);  

}  

up(&dev->sem);//解锁互斥信号量  

returnret;  

}  

static loff_t mem_llseek(struct file *filp,loff_t offset, int whence)  

{  

intnewpos;  

printk(“mem_llseek.\n”);  

switch(whence)  

{  

case0:  

newpos= offset;  

break;  

case1:  

newpos= filp->f_pos + offset;  

break;  

case2:  

newpos= MEMDEV_SIZE – 1 + offset;  

break;  

default:  

return-EINVAL;  

}  

if((newpos<0)|| (newpos>(MEMDEV_SIZE – 1)))  

return-EINVAL;  

filp->f_pos= newpos;  

returnnewpos;  

}  

static const struct file_operationsmem_fops = {  

.owner= THIS_MODULE,  

.open= mem_open,  

.write= mem_write,  

.read= mem_read,  

.release= mem_release,  

.llseek= mem_llseek,  

};  

static int __init memdev_init(void)  

{  

intresult;  

interr;  

inti;  

//请求设备号  

dev_tdevno = MKDEV(mem_major, 0);  

if(mem_major)  

result= register_chrdev_region(devno, MEMDEV_NUM, “memdev”);//留意静态请求的dev_t参数和动态dev_t参数的差异  

else{                                                                                                                          //静态直接传变量,动态传变量指针  

result= alloc_chrdev_region(&devno, 0, MEMDEV_NUM, “memdev”);  

mem_major= MAJOR(devno);  

}  

if(result< 0){  

printk(“can'tget major devno:%d\n”, mem_major);  

returnresult;  

}  

//注册设备驱动  

cdev_init(&mem_cdev,&mem_fops);  

mem_cdev.owner= THIS_MODULE;  

err= cdev_add(&mem_cdev, MKDEV(mem_major, 0), MEMDEV_NUM);//假如有N个设备就要增加N个设备号  

if(err)  

printk(“addcdev faild,err is %d\n”, err);  

//分配设备内存  

mem_devp= kmalloc(MEMDEV_NUM*(sizeof(struct mem_dev)), GFP_KERNEL);  

if(!mem_devp){  

result = – ENOMEM;  

goto fail_malloc;  

}  

memset(mem_devp,0, MEMDEV_NUM*(sizeof(struct mem_dev)));  

for(i=0;i

mem_devp[i].size= MEMDEV_SIZE;  

mem_devp[i].data= kmalloc(MEMDEV_SIZE, GFP_KERNEL);  

memset(mem_devp[i].data,0, MEMDEV_SIZE);  

init_MUTEX(&mem_devp[i].sem);//初始化互斥锁  

}  

returnresult;  

fail_malloc:  

unregister_chrdev_region(MKDEV(mem_major,0), MEMDEV_NUM);  

returnresult;  

}  

static void memdev_exit(void)  

{  

cdev_del(&mem_cdev);  

unregister_chrdev_region(MKDEV(mem_major,0), MEMDEV_NUM);//留意开释的设备号个数必定要和请求的设备号个数保存共同  

//否则会导致设备号资源丢失  

printk(“memdev_exit\n”);  

}  

module_init(memdev_init);  

module_exit(memdev_exit);  

MODULE_AUTHOR(“Y-Kee”);  

MODULE_LICENSE(“GPL”);  

#include #include #include #include #include #include #include #include #include #include #include #define MEMDEV_MAJOR 251#define MEMDEV_NUM 2#define MEMDEV_SIZE 1024struct mem_dev{unsignedint size;char*data;structsemaphore sem;};static int mem_major = MEMDEV_MAJOR;struct cdev mem_cdev;struct mem_dev *mem_devp;static int mem_open(struct inode *inode,struct file *filp){structmem_dev *dev;unsignedint num;printk(“mem_open.\n”);num= MINOR(inode->i_rdev);//取得次设备号if(num> (MEMDEV_NUM -1)) //查看次设备号有效性return-ENODEV;dev= &mem_devp[num];filp->private_data= dev; //将设备结构保存为私有数据return0;}static int mem_release(struct inode *inode,struct file *filp){printk(“mem_release.\n”);return0;}static ssize_t mem_read(struct file *filp,char __user *buf, size_t size, loff_t *ppos){intret = 0;structmem_dev *dev;unsignedlong p;unsignedlong count;printk(“mem_read.\n”);dev= filp->private_data;//取得设备结构count= size;p= *ppos;//查看偏移量和数据巨细的有效性if(p> MEMDEV_SIZE)return0;if(count> (MEMDEV_SIZE-p))count= MEMDEV_SIZE – p;if(down_interruptible(&dev->sem))//确定互斥信号量return -ERESTARTSYS;//读取数据到用户空间if(copy_to_user(buf,dev->data+p, count)){ret= -EFAULT;printk(“copyfrom user failed\n”);}else{*ppos+= count;ret= count;printk(“read%d bytes from dev\n”, count);}up(&dev->sem);//解锁互斥信号量returnret;}static ssize_t mem_write(struct file *filp,const char __user *buf, size_t size, loff_t *ppos)//留意:第二个参数和read办法不同{intret = 0;structmem_dev *dev;unsignedlong p;unsignedlong count;printk(“mem_write.\n”);dev= filp->private_data;count= size;p= *ppos;if(p> MEMDEV_SIZE)return0;if(count> (MEMDEV_SIZE-p))count= MEMDEV_SIZE – p;if(down_interruptible(&dev->sem))//确定互斥信号量return-ERESTARTSYS;if(copy_from_user(dev->data+p,buf, count)){ret= -EFAULT;printk(“copyfrom user failed\n”);}else{*ppos+= count;ret= count;printk(“write%d bytes to dev\n”, count);}up(&dev->sem);//解锁互斥信号量returnret;}static loff_t mem_llseek(struct file *filp,loff_t offset, int whence){intnewpos;printk(“mem_llseek.\n”);switch(whence){case0:newpos= offset;break;case1:newpos= filp->f_pos + offset;break;case2:newpos= MEMDEV_SIZE – 1 + offset;break;default:return-EINVAL;}if((newpos<0)|| (newpos>(MEMDEV_SIZE – 1)))return-EINVAL;filp->f_pos= newpos;returnnewpos;}static const struct file_operationsmem_fops = {.owner= THIS_MODULE,.open= mem_open,.write= mem_write,.read= mem_read,.release= mem_release,.llseek= mem_llseek,};static int __init memdev_init(void){intresult;interr;inti;//请求设备号dev_tdevno = MKDEV(mem_major, 0);if(mem_major)result= register_chrdev_region(devno, MEMDEV_NUM, “memdev”);//留意静态请求的dev_t参数和动态dev_t参数的差异else{ //静态直接传变量,动态传变量指针result= alloc_chrdev_region(&devno, 0, MEMDEV_NUM, “memdev”);mem_major= MAJOR(devno);}if(result< 0){printk("can'tget major devno:%d\n", mem_major);returnresult;}//注册设备驱动cdev_init(&mem_cdev,&mem_fops);mem_cdev.owner= THIS_MODULE;err= cdev_add(&mem_cdev, MKDEV(mem_major, 0), MEMDEV_NUM);//假如有N个设备就要增加N个设备号if(err)printk("addcdev faild,err is %d\n", err);//分配设备内存mem_devp= kmalloc(MEMDEV_NUM*(sizeof(struct mem_dev)), GFP_KERNEL);if(!mem_devp){result = - ENOMEM;goto fail_malloc;}memset(mem_devp,0, MEMDEV_NUM*(sizeof(struct mem_dev)));for(i=0;i

三、疑点难点

1.

__init

__initdata

__exit

__exitdata

仅用于模块初始化或铲除阶段的函数(__init和__exit)和数据(__initdata和__exitdata)符号。符号为初始化项目会在初始化完毕后丢掉;而退出在内核未被装备为可卸载模块的情况下被简略的丢掉。被符号为__exit的函数只能在模块卸载或许体系封闭时被调用,其他任何用法都是过错的。内核经过对应的方针目标放置在可执行文件的特别ELF段中而让这些符号起作用。

2.static

初始化函数应该被声明为static,由于这种函数在特定文件之外没有其他含义。由于一个模块函数要对内核其他部分课件,则有必要显现导出,因而这并不是什么强制性规矩。

3. struct module *owner

内核运用这个字段以防止在模块操作正在运用时卸载该模块。简直在一切的情况下,该成员都会被初始化为THIS_MODULE。它是界说在中的一个宏。

4 __user

咱们会留意到许多参数包含含有__user字串,它其实是一种方式的文档罢了,外表指针是一个用户指针,因而不能被直接用。对一般的编译来讲,__user没有任何作用,可是可由外部查看软件运用,用来寻觅对用户空间地址过错运用。


 

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/qiche/adas/99929.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部