您的位置:首页 > 运维架构 > Linux

linux驱动学习(四) linux字符设备驱动 cdev

2014-12-04 11:20 441 查看
字符设备驱动cdev中用到的两个重要的结构体如下,现补充下基本知识

一、cdev

[html] view
plaincopy

/*

*内核源码位置

*linux2.6.38/include/linux/cdev.h

*/

struct cdev {

struct kobject kobj;

struct module *owner; //一般初始化为:THIS_MODULE

const struct file_operations *ops; //字符设备用到的例外一个重要的结构体file_operations,cdev初始化时与之绑定

struct list_head list;

dev_t dev; //主设备号24位 与次设备号8位,dev_t为32位整形

unsigned int count;

};

二、file_operations

熟悉c语言文件编程的应该知道 read write等函数,这些函数都在file_operations中声明,在read等函数中实现与硬件相关的操作,这样就让具体的硬件设备与操作系统联系在了一起

[cpp] view
plaincopy

/*

~/include/linux/fs.h

*/

struct file_operations {

struct module *owner;

loff_t (*llseek) (struct file *, loff_t, int);

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

int (*readdir) (struct file *, void *, filldir_t);

unsigned int (*poll) (struct file *, struct poll_table_struct *);

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

int (*mmap) (struct file *, struct vm_area_struct *);

int (*open) (struct inode *, struct file *);

int (*flush) (struct file *, fl_owner_t id);

int (*release) (struct inode *, struct file *);

int (*fsync) (struct file *, int datasync);

int (*aio_fsync) (struct kiocb *, int datasync);

int (*fasync) (int, struct file *, int);

int (*lock) (struct file *, int, struct file_lock *);

ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

int (*check_flags)(int);

int (*flock) (struct file *, int, struct file_lock *);

ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);

ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);

int (*setlease)(struct file *, long, struct file_lock **);

long (*fallocate)(struct file *file, int mode, loff_t offset,

loff_t len);

};

光有这cdev与file_operations定义的结构体变量是不行的,显然要让他们做一些初始化工作,然后通过某个函数,让我们定义的这两个结构体变量与内核联系在一起,所以,调用内核的下列函数

文件名:char_dev.c

[cpp] view
plaincopy

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

{

memset(cdev, 0, sizeof *cdev);

INIT_LIST_HEAD(&cdev->list);

kobject_init(&cdev->kobj, &ktype_cdev_default);

cdev->ops = fops;

}

为cdev开辟内存空间,然后,将file_operations定义的变量fops赋值给cdev中的ops成员变量,这样,他们就紧密的连在一起了

以上才仅仅将cdev初始化,还未将其真正的添加到系统内核中,因此调用下列函数:

文件名:char_dev.c

[cpp] view
plaincopy

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

{

p->dev = dev;

p->count = count;

return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);

}

有了以上的字符设备基础时候后,在开始看一下字符设备的基本结构,其实就是在hello word的基础之上添加了设备读、写、控制的函数。

头文件:一般包含下面几个

[cpp] view
plaincopy

#include<linux/cdev.h>

#include<linux/module.h>

#include<linux/types.h>

#include<linux/fs.h>

#include<linux/errno.h>

#include<linux/mm.h>

#include<linux/sched.h>

#include<linux/init.h>

#include<asm/io.h>

#include<asm/system.h>

#include<asm/uaccess.h>

定义的cdev结构体与设备空间数据

[cpp] view
plaincopy

/*习惯上将内部数据空间与cdev 绑定,与其封装*/

struct mychar_dev{

struct cdev cdev;

unsigned char mem[MYCHAR_MEM_SIZE];

};

/*一个实例*/

struct mychar_dev* mychar_devp;

然后是读、写、ioctl函数的实现

[cpp] view
plaincopy

/*实现file_operations结构体体的open函数*/

int mychar_open(struct inode *inode,struct file * filp)

int mychar_release(struct inode *inode,struct file* filp);

/*read*/

ssize_t mychar_read(struct file *filp,char __user *buf,size_t size ,loff_t *ppos );

/*write*/

ssize_t mychar_write(struct file *filp ,const char __user *buf,size_t size,loff_t *ppos);

/*llseek*/

static loff_t mychar_llseek(struct file *filp,loff_t offset,int orig);

/*ioctl*/

int mychar_ioctl(struct inode * inodep ,struct file *filp ,unsigned int cmd ,unsigned long arg);

以上函数实现后将里赋值到file_operations中相应的函数成员变量

[cpp] view
plaincopy

static const struct file_operations mychar_fops = {

.owner = THIS_MODULE,

.llseek = mychar_llseek,

.read = mychar_read,

.write = mychar_write,

.ioctl = mychar_ioctl,

.open = mychar_open,

.release =mychar_release,

};

最后是init 与exit函数

/*init*/

static int __init mychar_init(void);

/*exit*/

static void __exit mychar_exit(void);

最后是

[cpp] view
plaincopy

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("ghostyu");

module_param(mychar_major,int,S_IRUGO);

module_init(mychar_init);

module_exit(mychar_exit);

下面看一下完整的源码:

[cpp] view
plaincopy

/*在内存中申请1k 大小的内存做为简单的一个设备来访问*/

/*一般包含的头文件*/

#include<linux/cdev.h>

#include<linux/module.h>

#include<linux/types.h>

#include<linux/fs.h>

#include<linux/errno.h>

#include<linux/mm.h>

#include<linux/sched.h>

#include<linux/init.h>

#include<asm/io.h>

#include<asm/system.h>

#include<asm/uaccess.h>

/*设备空间*/

#define MYCHAR_MEM_SIZE 0x0400

/*主设备号*/

#define MYCHAR_MAJOR 260

/*自定义的清除内存的命令*/

#define MYCHAR_MEN_CLR 0x01

/*主设备号变量*/

static int mychar_major = MYCHAR_MAJOR;

/*习惯上将内部数据空间与cdev 绑定,与其封装*/

struct mychar_dev{

struct cdev cdev;

unsigned char mem[MYCHAR_MEM_SIZE];

};

/*一个实例*/

struct mychar_dev* mychar_devp;

/*实现file_operations结构体体的open函数*/

int mychar_open(struct inode *inode,struct file * filp)

{

filp->private_data = mychar_devp;

return 0;

}

/*同上*/

int mychar_release(struct inode *inode,struct file* filp)

{

return 0;

}

/*read*/

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

{

unsigned long p=*ppos;

unsigned int count = size;

int ret = 0;

struct mychar_dev *dev = filp->private_data;

if(p>MYCHAR_MEM_SIZE)

return 0;

if(count > MYCHAR_MEM_SIZE-p)

count = MYCHAR_MEM_SIZE-p;

if( copy_to_user(buf,(void*)(dev->mem+p),count)){

ret= -EFAULT;

}else{

*ppos +=count;

ret = count;

printk(KERN_INFO "read %u bytes(s) from %1u\n",count,p);

}

return ret;

}

/*write*/

ssize_t mychar_write(struct file *filp ,const char __user *buf,size_t size,loff_t *ppos)

{

unsigned long p=*ppos;

unsigned int count=size;

int ret = 0;

struct mychar_dev *dev = filp->private_data;

if(p > MYCHAR_MEM_SIZE)

return 0;

if(count > MYCHAR_MEM_SIZE-p)

count = MYCHAR_MEM_SIZE-p;

if(copy_from_user((void*)(dev->mem),buf,count)){

ret = -EFAULT;

}else{

*ppos +=count;

ret = count;

printk(KERN_INFO "written %u byte(s) from %1u\n",count,p);

}

return ret;

}

/*llseek*/

static loff_t mychar_llseek(struct file *filp,loff_t offset,int orig)

{

loff_t ret = 0;

switch(orig){

case 0: /*相对于文件开始位置偏移*/

if(offset < 0)

ret = -EINVAL;

break;

if((unsigned int)offset > MYCHAR_MEM_SIZE){

ret = -EINVAL;

break;

}

filp->f_pos =(unsigned int )offset;

ret = filp->f_pos;

break;

case 1: /*相对于文件当前位置*/

if((filp->f_pos+offset)>MYCHAR_MEM_SIZE){

ret = -EINVAL;

break;

}

if((filp->f_pos+offset)< 0){

ret = -EINVAL;

break;

}

filp->f_pos +=offset;

ret = filp->f_pos;

break;

default:

ret = - EINVAL;

break;

}

return ret;

}

/*ioctl*/

int mychar_ioctl(struct inode * inodep ,struct file *filp ,unsigned int cmd ,unsigned long arg)

{

struct mychar_dev *dev =filp->private_data;

switch(cmd){

case MYCHAR_MEM_CLR:

memset(dev->mem,0,MYCHAR_MEM_SIZE);

printk(KERN_INFO "mychar memery is set to zero\n");

break;

default:

return -EINVAL;

}

return 0;

}

static const struct file_operations mychar_fops = {

.owner = THIS_MODULE,

.llseek = mychar_llseek,

.read = mychar_read,

.write = mychar_write,

.ioctl = mychar_ioctl,

.open = mychar_open,

.release =mychar_release,

};

/*cdev结构初始化*/

static void mychar_setup_cdev(struct mychar_dev *dev,int index)

{

int err;

int devno = MKDEV(mychar_major,index);

cdev_init(&dev->cdev,&mychar_fops);

dev->cdev.owner = THIS_MODULE;

err = cdev_add(&dev->cdev,devno,1);

if(err)

printk(KERN_NOTICE " Error %d adding mychar %d",err,index);

}

/*init*/

static int __init mychar_init(void)

{

int result;

dev_t devno = MKDEV(mychar_major,0);

if(mychar_major)

result = register_chrdev_region(devno,1,"mychar");

else{

result = alloc_chrdev_region(&devno,0,1,"mychar");

mychar_major = MAJOR(devno);

}

if(result<0)

return result;

mychar_devp = kmalloc(sizeof(struct mychar_dev),GFP_KERNEL);

if(!mychar_devp){

result = -ENOMEM;

goto fall_malloc;

}

memset(mychar_devp,0,sizeof(struct mychar_dev));

mychar_setup_cdev(mychar_devp,0);

return 0;

fall_malloc:

unregister_chrdev_region(devno,1);

return result;

}

/*exit*/

static void __exit mychar_exit(void)

{

cdev_del(&mychar_devp->cdev);

kfree(mychar_devp);

unregister_chrdev_region(MKDEV(mychar_major,0),1);

}

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("ghostyu");

module_param(mychar_major,int,S_IRUGO);

module_init(mychar_init);

module_exit(mychar_exit);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: