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

 一、重要知识点

1. 主次设备号

dev_t

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

int MAJOR(dev_t dev)

int     MI   NOR(dev_t dev)

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

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

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

2. 分配和释放设备号

int regis     te   r_chardev_region(dev_t fi     rs   t,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_opera     ti   ons

file_opera  TI ons结构保存了     字符   设备驱动程序的方法。

4. 字符设备的注册和注销

struct cdev *cdev_alloc(void);

void cdev_init(struct cdev *dev, structfile_opera  TI ons *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_interrup  TI ble(struct semaphore*sem);

int down_trylock(struct semahpore *sem);

void up(struct semaphore *sem);

锁定和解锁信号量。如果必要,down会将调用进程置于不可中断的休眠状态;相反,down_interrup  TI ble可被信号中断。down_trylock不会休眠,并且会在信号量不可用时立即返回。锁定信号量的代码最后必须使用up解锁该信号量。

2)自旋锁

s     pi   onlock_t lock = SPIN_LOCK_UNLOCKED;

spin_lock_init(spinlock_t *lock);

初始化自旋锁的两种方式。

voi     dsp   in_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("copyf     rom   user f     ai   led\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<memdev_num; i++){  < p="">

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没有任何效果,但是可由外部检查软件使用,用来寻找对用户空间地址错误使用。


驱动之路_简单字符设备驱动程序_设计制作_RF/无线
18
11
0
22

相关资讯

  1. 1、汽车电商没有这么美好2743
  2. 2、朋友圈:我们需要一些社交压力2250
  3. 3、媒体+如何助推汽车产业实现弯道超车?3572
  4. 4、超8300万人收听疗愈音乐,喜马拉雅如何帮助纯音乐人打开变现大门?2992
  5. 5、思维盘点:产品第一性原理3107
  6. 6、小程序已成企业新标配,BAT决战小程序的商业逻辑3711
  7. 7、未来微信畅想:社交场景、工作场景和支付2664
  8. 8、社交电商拼多多无社交1423
  9. 9、推出“不常联系人”功能背后,微信已成商业社交的基础设施1044
  10. 10、马斯克、霍金等联合背书:人工智能该遵守的23条基本原则1348
全部评论(0)
我也有话说
0
收藏
点赞
顶部