您的位置:首页 > 其它

2.字符设备驱动

2016-04-13 16:52 495 查看
一.字符设备开发的基本步骤

  1.确定主设备号和次设备号

  2.实现初始化函数,注册字符设备

  3.实现字符驱动程序

  4.实现file_operations结构体

  5.实现销毁函数,释放字符设备

  6.创建设备文件节点

二.常用概念

1.主设备号是内核识别一个设备的标识。整数(占12bits),范围从0到4095,通常使用1到255

2.次设备号由内核使用,用于正确确定设备文件所指的设备。整数(占20bits),范围从0到1048575,一般使用0到255。

3.inode与file的区别

  file表示打开的文件描述符

  多个表示打开的文件描述符的file结构,可以指向单个inode结构。

三.驱动相关结构体

1.inode 结构体

inode类型作用:存在于磁盘的文件系统中,内核用inode结构在内部表示文件

inode成员:

dev_t        i_rdev;对表示设备文件的inode结构,该字段包含了真正的设备编号。

struct cdev *i_cdev;

struct cdev是表示字符设备的内核的内部结构。当inode指向一个字符设备文件时,该字段包含了指向struct cdev结构的指针。

inode成员函数:

unsigned int iminor(struct inode *inode);

unsigned int imajor(struct inode *inode);

2.file 结构体 

file类型作用:file_operations结构相关的一个结构体。内核系统描述一个正在打开的设备文件。

file成员:

loff_t f_pos: 当前读/写位置

unsigned int f_flags:O_RDONLY,O_NONBLOCK,O_SYNC标识文件打开时,是否可读或可写

struct file_operations *f_op:struct file_operations 文件相关的操作,指向所实现的

void *private_data: 私有数据指针。驱动程序可以将这个字段用于任何目的或者忽略这个字段。 

3.file_operations 结构体:字符驱动和内核的接口,在#include <linux/fs.h>定义

字符驱动只要实现一个file_operations结构体,并注册到内核中,内核就有了操作此设备的能力。

file_operations成员:

struct module *owner: 指向模块自身

open:打开设备

release:关闭设备

read:从设备上读数据

write:向设备上写数据

ioctl:I/O控制函数

llseek:定位读写指针

mmap:映射设备空间到进程的地址空间

4.dev_t类型(32位):#include <linux/types.h>
<
4000
/span>

dev_t类型作用:对应于驱动(write/read),向系统申请设备号,并注册。设备编号的内部表达,用来保存设备编号(包括主设备号(12位)和次设备号(20位))

dev_t成员函数:#include <linux/fs.h>

初始化:在内核系统中注册申请驱动设备号
a.静态 

MAJOR(dev_t);获得主设备号

MINOR(dev_t);获得次设备号

MKDEV(int major,int minor);将主设备号和次设备号转换成dev_t类型

intregister_chrdev_region( dev_t first,unsigned int count,char *name );手工分配主设备号:找一个内核没有使用的主设备号来使用

b.动态

int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);动态分配主设备号

释放资源:释放在内核系统中的资源(设备号)
void unregister_chrdev_region(dev_t first, unsigned int count);//释放设备号

5.cdev 结构体:

struct cdev

 {

 struct kobject kobj;          /*内嵌的kobject 对象 */

 struct module *owner;         /*所属模块*/

 struct file_operations *ops;  /*文件操作结构体*/

 struct list_head list; //与cdev 对应的字符设备文件的inode->i_devices 的链表头

 dev_t dev;                    /*设备号*/

 unsigned int count; //设备范围号大小

 };

cdev 结构体作用:对应于具体设备,将具体的设备注册到kernel中。

cdev成员函数:#include <linux/cdev.h>

初始化:定义并初始化cdev 结构体
a.静态
static struct cdvm_cdev;
void cdev_init( struct cdev *, struc t file_operations *);//静态初始化
m_cdev.owner= THIS_MODULE;

b.动态
struct cdev *cdev_alloc(void);//动态初始化

m_cdev->owner= THIS_MODULE;

m_cdev->ops= &fop;

功能实现函数:cdev 结构体添加到内核系统中

int cdev_add(struct cdev *p,dev_t dev,unsigned count) ;//将具体设备对应到设备号,最终对应到设备驱动(read/write)。

释放资源:cdev 结构体移除内核系统
void cdev_del(struct cdev *);//模块卸载,释放 cdev 占用的内存

6.class结构体:

class结构体作用:对应于设备文件,最终功能在/dev/下添加一个设备文件

class成员函数:#include <linux/device.h>

初始化:在内核资源中定义并初始化class

static struct class*m_pcls;
m_pcls = #define   class_create(owner,name)

功能实现函数:在/dev/下添加一个设备文件

struct device *device_create(struct class*    pcls,struct device*   parent,dev_t   devt,void  *   devdata,const char *   fmt,    ...  );

释放资源:删除/dev/下添加一个设备文件和内核资源class
void device_destroy(struct class *pcls,dev_t   devt);
void class_destroy(struct class *m_pcls);

四.其他功能函数

读写用户区函数<asm/uaccess.h>
unsignedlong copy_from_user(void *to,  const void __user *from,    unsigned long count)
;

unsignedlong
copy_to_user(void __user *to,  const void *from,unsigned long count );
put_user()和get_user();

五.字符设备源代码

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/cdev.h>//struct cdev等函数

#include <linux/types.h> //dev_t类型(32位)

MODULE_LICENSE("GPL");

static int first_open(struct inode *inode,struct file *file)

{

printk(KERN_ALERT "first_open\n");

return 0;

}

static ssize_t first_write(struct file *file,const char *buf,size_t count,loff_t *ppos)

{

printk("first_writre\n");

return 0;

}

static struct file_operations first_drv_fops={

.owner = THIS_MODULE,

.open  = first_open,

.write = first_write,

};

dev_t ret_t;

struct cdev lz_dev;

static int __init first_drv_init(void)

{

ret_t = MKDEV(125,0);

register_chrdev_region(ret_t,1,"first");

cdev_init(&lz_dev, &first_drv_fops); //初始化cdev

lz_dev.owner = THIS_MODULE;

cdev_add(&lz_dev,ret_t,1) ;

printk(KERN_ALERT "lz_drv_int\n");

//ret=register_chrdev(ret,"first_drv",&first_drv_fops);

return 0;

}

static void __exit first_drv_exit(void)

{

unregister_chrdev_region(ret_t, 1); //释放占用的设备号

cdev_del(&lz_dev); //注销设备

printk("lz_drv_exit\n");

//unregister_chrdev(ret,"first_drv");

}

module_init(first_drv_init);

module_exit(first_drv_exit);

六.Makefile

ifneq ($(KERNELRELEASE),)

obj-m := lz_dev.o

#module-objs := first_dev.o

else

#KERNELDIR ?=/lib/modules/$(shell uname -r)/build

KERNELDIR ?=/home/lz/linux/2.0android-kernel-samsung-dev

PWD := $(shell pwd)

all:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:

rm *.o *.mod.c *.order *.symvers -rf

endif

cdev_init(&lz_dev, &first_drv_fops); //初始化cdev

lz_dev.owner = THIS_MODULE;

cdev_add(&lz_dev,ret_t,1) ;

printk(KERN_ALERT "lz_drv_int\n");

//ret=register_chrdev(ret,"first_drv",&first_drv_fops);

return 0;

}

static void __exit first_drv_exit(void)

{

unregister_chrdev_region(ret_t, 1); //释放占用的设备号

cdev_del(&lz_dev); //注销设备

printk("lz_drv_exit\n");

//unregister_chrdev(ret,"first_drv");

}

module_init(first_drv_init);

module_exit(first_drv_exit);

六.Makefile

KERN_RELEASE = 1

ifeq ($(KERN_RELEASE),)

KERN_DIR = /lib/modules/$(shell uname -r)/build/

else

KERN_DIR = /mnt/share/port/kernel/

endif

obj-m = mque.o

mque-objs = que.o

default:
make -C $(KERN_DIR) M=$(shell pwd) modules
gcc read.c -o read
gcc write.c -o write 

clean:
make -C $(KERN_DIR) M=$(shell pwd) clean
rm write read
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: