Linux字符设备驱动程序的设计(初级)

一、字符设备驱动程序分析

1、cdev

cdev是描述字符设备的结构体,要设计一个简单的linux字符设备,就要先定义一个cdev结构体,然后对结构体进行初始化,初始化结束后再将cdev注册到内核,这样内核就添加了一个字符设备驱动。

cdev的原型如下:

#include <linux/cdev.h>

struct cdev {

struct kobject kobj;

struct module *owner;

const struct file_operations *ops;

struct list_head list;

dev_t dev;

unsigned int count;

};

cdev结构体成员的分析

1)struct kobject kobj;

    linux在做字符设备管理的时候,使用的驱动模型,利用kobject生成/sys下的驱动信息;

2)truct module *owner;

  这个字符设备输入到那一个module中,一般设为THIS_MODULE

3)const struct file_operations *ops;

文件操作集,给应用程序提供API接口,应用程序通过系统调用来访问文件操作集中的接口函数;

文件操作集中有很多函数,下面几种较为常用:

struct file_operations {

struct module *owner;

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

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

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

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

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

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

};

注意:

文件IO函数原型的参数,与文件操作集中对应的函数的参数是否一致?

不一致,因为系统调用过程中间经过vfs华为linux内核,过程较复杂,与函数的参数调用不同。

4) struct list_head list;

    内核双向链表,向内核中注册一个字符设备时,链表增加一项

5)dev_t dev;

设备号


2、设备号

1)设备号的定义

dev_t  dev_num;

在linux内核中,每一个设备驱动都有一个设备号,设备号由主设备号和次设备号组成,个人理解如下:

主设备号:描述设备驱动的类型;

次设备号:描述具体设备驱动类型的一个应用实例;

例如:嵌入式开发板中nand flash的5个分区

[root@Huang /]# ls /dev/mtdblock* -l

brw-rw----    1 root     root       31,   0 Jan  2 11:21 /dev/mtdblock0

brw-rw----    1 root     root       31,   1 Jan  2 11:21 /dev/mtdblock1

brw-rw----    1 root     root       31,   2 Jan  2 11:21 /dev/mtdblock2

brw-rw----    1 root     root       31,   3 Jan  2 11:21 /dev/mtdblock3

brw-rw----    1 root     root       31,   4 Jan  2 11:21 /dev/mtdblock4

设备号是一个32位无符号的整型值,其中高12位是主设备号,低20位是次设备号

设备号与主次设备号之间的转换:

dev_num = MKDEV(major, minor);            /*由主次设备号得到设备号*/

major = MAJOR(dev_num);                    /*由设备号得到主设备号*/

minor = MINOR(dev_num);        /*由设备号得到次设备号*/

2)如何获得一个设备号

设计设备驱动时,需要获得一个可用的设备号,一般有两种方法:静态注册设备号、动态分配设备号。所谓静态注册就是我们给定一个设备号,如果该设备号在内核中 没被使用,那我们通过静态注册就可以用该设备号作为驱动的设备号;动态分配是指让内核给我们分配一个可用的设备号。

·静态注册函数:

int register_chrdev_region(dev_t from, unsigned int count, const char *name);

参数分析:

dev_t from:想要注册的设备号;

unsigned int count:次设备的个数;

const char *name:设备的名称;

返回值:

   注册成功,返回0;

    注册失败,返回一个负数的错误码;

动态分配函数:

int alloc_chrdev_region(dev_t *dev, unsigned int baseminor, unsigned int count, const char *name);

参数分析:

dev_t *dev:动态分配后的设备号;

unsigned int baseminor:第一个次设备号;

unsigned int count:次设备的个数;

char *name:设备的名称;

返回值:

分配成功,返回0;

分配失败,返回一个负数的错误码;


二、字符设备驱动程序设计

设计思路定义字符设备(cdev)-->定义设备号(cdev_num)-->申请设备号(静态或动态)-->定义并设计文件操作集函数-->初始化字符设备-->将字符设备添加到内核中-->出错或退出时要注销字符设备和设备号;

1)各种定义:

static struct cdev cdev_xxx;

static dev_t dev_num;

static cdev_num = “xxx”;

static int cdev_major = 0;

static int cdev_minor = 0;

......

static struct file_operations cdev_fops = {

.open = test_open,

.read = test_read,

.write = test_write,

.iocti = test_ioctl,

.release = test_release,

};

注意:文件操作集的定义及初始化要放在各种接口函数的设计之后;

2)申请设备号;

if(cdev_major)/*若定义的主设备号不为0,则静态注册设备号*/

{

cdev_num = MKDEV(cdev_maor, cdev_minor);

cdev_num = register_chrdev_region(cdev_num, 1, cdev_name );

}

else/*若定义的主设备号为0,则动态分配设备号*/

{

ret = alloc_chrdev_region(&cdev_num, cdev_minor, 1, cdev_name);

cdev_major = MAJOR(cdev_num);

if(ret < 0)

{

     printk(KERN_WARNING“alloc chrdev_num error,cannot get cdev_major %d\n”, cdev_major);

     goto err_chrdev_region;   /*出错处理*/

 }

}

3)初始化字符设备:

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

参数:

   struct cdev *cdev:    /*定义的字符设备*/

    const struct file_operations *fops: /*已经完成定义和初始化的一个文件操作集*/

 

 cdev_init(&cdev_xxx, &cdev_fops);


4)将字符设备加入到内核中

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

参数:

struct cdev *p :定义并初始化的字符设备cdev

dev_t dev:已经申请到的设备号

unsigned count:次设备的数量

返回值:

 注册成功,返回0

 注册失败,返回一个负数的错误码

 代码:

ret = cdev_add(&cdev_xxx, cdev_num, 1);

if(ret < 0)

{

printk(“cdev_add error!\n”);

goto err_cdev_add;

}

5)注销字符设备

void cdev_del(struct cdev *p);

参数:

   struct cdev *p:注册的设备;

6)注销设备号

void unregister_chrdev_region(dev_t from, unsigned count)

   参数:

   dev_t from:注册的设备号(注册的一个设备号的开始值);

    unsigned count: 次设备的个数;

三、字符设备驱动的调试

写一个应用程序,利用应用程序调试驱动程序。

1、安装驱动

[root@Huang /test]# insmod demo1.ko 

[24273.778642] 

[24273.779733] this is the first demo of driver,  inserting successful !

 

2、查看内核模块

[root@Huang /test]# lsmod 

demo1 2589 0 - Live 0xbf000000

 

3、查看字符设备的主设备号和设备名称

char drv_name[ ]="chrtest";

int TestMajor = 0;//主设备号

int TestMinor = 0;//次设备号

 

[root@Huang /test]# cat /proc/devices 

Character devices:

250 chrtest   --->主设备号和设备名称

4、手动创建设备文件(可以自动创建设备文件,后面再写)

[root@Huang /test]# mknod /dev/chr_test1 c 250 0

5、查看设备文件

[root@Huang /test]# ls /dev/chr_test1 -l

crw-r--r--    1 root     root      250,   0 Jan  2 18:14 /dev/chr_test1

6、执行应用程序

7、驱动的卸载

[root@Huang /]# rmmod demo1

[25293.152108] the driver is exiting


哈,一个简单的字符设备驱动就写完啦,代码比较简单,就不全部贴不来啦。

本人新手,请多指教哈。



  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值