您的位置 首页 IOT

驱动之路-高档字符设备驱动程序

驱动之路-高级字符设备驱动程序-高级字符设备驱动在简单字符驱动的基础上添加ioctl方法、阻塞非阻塞读写、poll方法、和自动创建设备文件的功能。

高档字符设备驱动在简略字符驱动的基础上增加ioctl办法、堵塞非堵塞读写、poll办法、和主动创立设备文件的功用。

一、重要知识点

1.ioctl

ioctl指令:运用4个字段界说一个ioctl指令,包含

type: 幻数,一般运用一个字符界说,在内核中仅有。

number: 序数。

direction: 数据传输方向,当不触及数据传输时,此字段无效。

size: 所触及用户数据的巨细,当不触及数据传输时,此字段无效。

_IOC_NONE

_IOC_READ

_IOC_WRITE

“方向”字段的或许值。“读”和“写”是不同的位,能够用“OR”在一起指定读写。

_IOC(dir, type, size)

_IO(type,nr)

_IOR(type, nr, size)

_IOW(type, nr, size)

用于出产ioctl指令的宏

_IOC_DIR(cmd)

_IOC_TYPE(cmd)

_IOC_NR(cmd)

_IOC_SIZE(cmd)

用于解码ioctl指令的宏

intaccess_ok(int type, const void *addr, unsigned long size)

这个函数验证指向用户空间的指针是否可用,假如答应拜访,access_ok回来非0值。

int put_user(datum, ptr)

int get_user(local, ptr)

int __put_user(datum, ptr)

int __get_user(local, ptr)

用于向(或从)用户空间保存(或获取)单个数据项的宏。传送的字节数目由sizeof(*ptr)决议。前两个要先调用access_ok,后两个(__put_user和__get_user)则假定access_ok现已被调用过了。

2.堵塞型I/O

typedef struct {/*…..*/} wait_queue_head_t

void init_waitqueue_head(wait_queue_head_t*queue)

DECLARE_WAIT_QUEUE_HEAD(queue)

预先界说的Linux内核等候行列类型。wait_queue_head_t类型有必要显现地初始化,初始化办法能够在运行时调用init_waitqueue_head,或在编译时DECLARE_WAIT_QUEUE_HEAD。

void wait_event((wait_queue_head_t q, intcondiTIon)

int wait_event_interrupTIble(wait_queue_head_tq, int condiTIon)

int wait_event_TImeout(wait_queue_head_t q,int condition, int time)

int wait_event_interruptible_timeout(wait_queue_head_tq, int condition, int time)

使进程在指定的行列上休眠,直到给定的condition值为真。

void wake_up(struct wait_queue **q)

void wake_up_interruptible(structwait_queue **q)

这些函数唤醒休眠在行列q上的进程。_interruptible方式的函数只能唤醒可中止的进程。在实践中约好做法是在运用wait_event时用wake_up,而在运用wait_event_interruptible时运用wake_up_interruptible。

3.poll办法

poll办法分两步处理,第一步调用poll_wait指定等候行列,第二步回来是否可操作的掩码。

POLLIN表明设备可读的掩码,POLLRDORM表明数据可读的掩码。POLLOUT表明设备可写的掩码,POLLWRNORM表明数据可读的掩码。一般一起回来POLLIN和POLLRDORM或许POLLOUT和POLLWRNORM。

4.select体系调用

原型为intselect(int mafdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set*restrict exceptfds, struct timeval *restrict tvptr)

回来值:安排妥当的描述符数,若超时则回来0,若犯错则回来-1

void FD_ISSET(int fd, fd_set *fdset)

void FD_CLR(int fd, fd_set *fdset)

void FD_SET(int fd, fd_set *fdset)

void FD_ZERO(fd_set *fdset)

调用FD_ZERO将一个指定的fd_set变量的一切位设置为0。调用FD_SET设置一个fd_set变量指定位。调用FD_CLR则将一指定位铲除。最终,调用FD_ISSET测验一指定位是否设置。

5.主动创立设备文件

struct class *class_create(struct module*owner, const char *name)

struct device *device_create(struct class*class, struct device *parent, dev_t devt, const char *fmt, …)          

经过这两个函数能够专门用来创立一个字符设备文件节点,class_create 第一个参数指定一切者,第二参数指定类得姓名。class_device_create第一个参数指定第一个参数指定所要创立的设备所隶属的类,第二个参数是这个设备的父设备,假如没有就指定为NULL,第三个参数是设备号,第四个参数是设备称号。

二、驱动代码

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#include   

#define MEMDEV_MAJOR 251  

#define MEMDEV_NUM 2  

#define MEMDEV_SIZE 1024  

//界说设备IOCTL指令  

#define MEMDEV_IOC_MAGIC 'k'  

#define MEMDEV_IOC_NR 2  

#define MEMDEV_IOC_PRINT_IO(MEMDEV_IOC_MAGIC, 0)  

#define MEMDEV_IOC_RD_IOR(MEMDEV_IOC_MAGIC, 1, int)  

#define MEMDEV_IOC_WT_IOW(MEMDEV_IOC_MAGIC, 2, char)  

struct mem_dev  

{  

unsignedint size;  

char*data;  

structsemaphore sem;  

wait_queue_head_t  inque;  

};  

static int mem_major = MEMDEV_MAJOR;  

struct cdev mem_cdev;  

struct mem_dev *mem_devp;  

bool havedata = false;  

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;  

while(!havedata)  

{  

up(&dev->sem);  

if(filp->f_flags& O_NONBLOCK)  

return-EAGAIN;  

printk(“readyto go sleep”);  

if(wait_event_interruptible(dev->inque,havedata))//等候数据  

return-ERESTARTSYS;  

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%ld bytes from dev\n”, count);  

havedata= false;//数据现已读出  

}  

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%ld bytes to dev\n”, count);  

havedata= true;  

wake_up_interruptible(&dev->inque);//唤醒等候数据的行列  

}  

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 int mem_ioctl(struct inode *inode,struct file *filp, unsigned int cmd, unsigned long arg)  

{  

interr = 0, ret = 0;  

intioarg = 0;  

charrdarg = '0';  

//参数查看  

if(_IOC_TYPE(cmd)!= MEMDEV_IOC_MAGIC)//参数类型查看  

return-ENOTTY;  

if(_IOC_NR(cmd)> MEMDEV_IOC_NR)//参数指令号查看  

return-ENOTTY;  

//用户空间指针有效性查看  

if(_IOC_DIR(cmd)& _IOC_READ)  

err= !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));  

elseif(_IOC_DIR(cmd) & _IOC_WRITE)  

err= !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));  

if(err)  

return-ENOTTY;  

//依据指令履行操作  

switch(cmd)  

{  

case MEMDEV_IOC_PRINT:  

printk(“memdevioctl print excuting…\n”);  

break;  

caseMEMDEV_IOC_RD:  

ioarg= 1024;  

ret  = __put_user(ioarg, (int *)arg);//用户空间向内核空间取得数据  

printk(“memdevioctl read excuting… \n”);  

break;  

caseMEMDEV_IOC_WT:  

ret= __get_user(rdarg, (char *)arg);//用户空间向内核空间传输数据  

printk(“memdevioctl write excuting… arg:%c\n”, rdarg);  

break;  

default:  

return-ENOTTY;  

}  

returnret;  

}  

static unsigned int mem_poll(struct file*filp, poll_table *wait)  

{  

structmem_dev *dev;  

unsignedint mask = 0;  

dev= filp->private_data;  

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

return-ERESTARTSYS;  

poll_wait(filp,&dev->inque, wait);  

if(havedata)  

mask|= POLLIN | POLLRDNORM;//回来可读掩码  

up(&dev->sem);//开释信号量  

returnmask;  

}  

static const struct file_operationsmem_fops = {  

.owner= THIS_MODULE,  

.open= mem_open,  

.write= mem_write,  

.read= mem_read,  

.release= mem_release,  

.llseek= mem_llseek,  

.ioctl= mem_ioctl,  

.poll= mem_poll,  

};  

static int __init memdev_init(void)  

{  

intresult;  

interr;  

inti;  

structclass *memdev_class;  

//请求设备号  

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(“add cdev 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);//初始化互斥锁  

//初始化等候行列  

init_waitqueue_head(&mem_devp[i].inque);  

}  

//主动创立设备文件  

memdev_class= class_create(THIS_MODULE, “memdev_driver”);  

device_create(memdev_class,NULL, MKDEV(mem_major, 0), NULL, “memdev0”);  

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”);  

 

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

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

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

微信扫一扫关注我们

返回顶部