现在的位置: 首页 > 综合 > 正文

字符设备驱动学习(一)

2013年07月12日 ⁄ 综合 ⁄ 共 3969字 ⁄ 字号 评论关闭

一般的流程都是,呵呵,其实也不算是一般的流程了,只是手动加载字符设备驱动的一种方法吧,

1.    先使用register_chrdev_region或alloc_chrdev_region来注册一个字符设备的设备号,大家都知道Linux是通过设备号来找到相应的驱动程序的,所以你要注册字符设备的时候,需要一个设备号或系统为你指定一个设备号。这两函数的差别就是register_chrdev_region是注册指定设备号的字符设备,这个如果注册设备号已经存在则有可能会失败,而alloc_register_region是系统分配一个未使用的设备号然后再注册

2.    再使用cdev_add函数,把该设备号相应的文件操作file_operation文件操作入口表添加到内核的散列表里面,其中file_operation是需要你在驱动里面实现的。然后使用命令mknod,创建与该字符设备(设备号)对应的设备节点,当有用户程序打开这个设备并对其操作时候,系统会调用kobj_lookup() 函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops (file_operation)字段,跳到相应的驱动入口去执行。

3.    当需要卸载驱动的时候(手动加载),需要做一些清理工作,cdev_del函数把散列表里面的数据清除,再调用unregister_chrdev_region.

 

 

下面简单介绍一下相关的函数:

int register_chrdev_region(dev_t first,
unsigned int
count,
char *name);   //指定设备编号

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);      //释放设备编号

上面有个count参数,文字意义上是数量的意思,而从函数名中的_region可以看出是注册某一范围的设备号的

把这两个结合起来就是说,注册使用从first[或dev]到(frist+count)[或dev+count],这个编号范围的设备使用该驱动。。。

 

内核用dev_t类型(<linux/types.h>)来保存设备编号,dev_t是一个32位的数,12位表示主设备号,20为表示次设备号。
在实际使用中,是通过<linux/kdev_t.h>中定义的宏来转换格式。

 (dev_t)-->主设备号、次设备号  MAJOR(dev_t dev)
 MINOR(dev_t dev)
 主设备号、次设备号-->(dev_t)  MKDEV(int major,int minor) 

 

如果只是在module_init函数里面做上面的这一步,加载内核模块的时候,会可以cat /proc/devices里面看到你注册的设备和主设备号。

 

大家应该都知道file_operation函数的,它在<linux/fs.h>文件里面有定义,是一个保存驱动操作函数入口的结构体,我们要做的工作基本都在这里,完成这一步之后,使用cdev_add函数,把设备号与相应的file_operation指针添加到系统cdev_map 散列表里面去,

初始化struct cdev
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
初始化cdev.owner
cdev.owner = THIS_MODULE;

cdev.ops = &fops;
cdev设置完成,通知内核struct cdev的信息(在执行这步之前必须确定你对struct cdev的以上设置已经完成!)
int cdev_add(struct cdev *p, dev_t dev, unsigned count)

这里面的count的意义跟上面register_chrdev_region中的是一样的。
从系统中移除一个字符设备:void cdev_del(struct cdev *p)

 

完成这一步之后,你已经注册了字符设备,但是你还不能对它进行操作(打开,关闭读,写等),你需要使用mknod命令手动的去生成一个设备号对应的索引节点,这个需要管理员权限的,你的主设备号可以通过cat /proc/devices看到

mknod - make block or character special files
mknod [OPTION]... NAME TYPE [MAJOR MINOR]
    option 有用的就是 -m 了
    name   自定义
    type   有 b 和 c 还有 p
    主设备号
    次设备号

sudo mknod /dev/char01 c 250 0

 

最后你可以在你的用户程序里面使用open("/dev/char01", O_RDONLY);去打开这个设备了,

由于创建/dev/*节点的时候,是只有管理员有权限的,所以你的测试程序也需要管理员权限执行。

下面是相应的源文件:

char01.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
/////////////////////////////////////////////////
MODULE_LICENSE("Dual BSD/GPL");
/////////////////////////////////////////////////
int char01_open(struct inode *inode, struct file *filp)
{
    printk("char01 open!/n");
    return 0;
}

int char01_release(struct inode *inode, struct file filp)
{
    printk("char01 release!/n");
    return 0;
}

ssize_t char01_read(struct file *filp, char *buf,
        size_t count, loff_t f_pos)
{
    printk("char01 read!/n");
    return 0;
}

ssize_t char01_write(struct file *filp, char *buf,
        size_t count, loff_t f_pos)
{
    printk("char01 write!/n");
    return 0;
}
/////////////////////////////////////////////////
struct file_operations fops = 
{
    .owner  =   THIS_MODULE,
    .open   =   char01_open,
    .release=   char01_release,
    .read   =   char01_read,
    .write  =   char01_write
};

dev_t devno;
int nr = 5;
struct cdev dev;
int setup_char01(void)
{
    int result;
    int major;
    result = alloc_chrdev_region(&devno, 0, nr, "char01");
    if (result != 0)
    {
        printk("alloc_chrdev_region failed!/n");
        return -1;
    }
    printk("alloc_chrdev_region ok!/n");
    
    major = MAJOR(devno);

    cdev_init(&dev, &fops);
    dev.owner = THIS_MODULE;
    dev.ops = &fops;
    dev.dev = devno;
    if (cdev_add(&dev, devno, nr) < 0)
    {
        printk("cdev_add failed!/n");
        unregister_chrdev_region(devno, nr);
        return -1;
    }
    printk("cdev_add ok!/n");
    
    return 0;
}
/////////////////////////////////////////////////
static int __init char01_init(void)
{
    printk("char01 init!/n");
    return setup_char01();
}

static void __exit char01_exit(void)
{
    printk("char01 exit!/n");
    unregister_chrdev_region(devno, nr);
    cdev_del(&dev);
}

module_init(char01_init);
module_exit(char01_exit);

测试文件,char01_test.c

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
/////////////////////////////////////////////////
int main(int argc, char **argv)
{
    int fd;
    fd = open("/dev/char01", O_RDONLY);
    if (fd < 0)
    {
        printf("open /dev/char01 failed!/n");
        printf("%s/n", strerror(errno));
        return -1;
    }
    printf("open /dev/char01 ok!/n");
    
    close(fd);
    return 0;
}

抱歉!评论已关闭.