Linux 设备驱动程序进阶 (1)

拖了这么久才开始设备驱动程序进阶的第二篇文章,有点汗颜,最近忙于毕业和工作的事情,使得自己无法专心于技术的进阶,现在学校的事情总算是忙完了,所以文章的写作也要开始了。

 

这篇文章主要是结合Scull设备的代码谈谈如何构建一个简单的字符设备。然后编写一些测试代码讲讲如何测试Scull设备。

 

我们设计字符设备最基本的操作就是打开一个字符设备(open),和向这个字符设备读取数据(read),以及向这个字符设备写数据(write)。所以要设计一个数据结构能够关联这些操作,统一调度。对于每一个打开的文件都有一个file结构表示,然后它的操作则关联一个file_operations的结果。

 

static struct file_operations scull_fops=
{
        .owner = THIS_MODULE,
//      .llseek = scull_llseek,
        .read = scull_read,
        .write = scull_write,
//      .ioctl = scull_ioctl,
        .open = scull_open,
        .release = scull_release,
};

 

对于上述的结果,.owner字段表明指向拥有该模块的指针,接下来的字段都是函数指针,表明对于Scull设备我们可以有那些操作,这个有点像对象编程里面对于对象行为的说明。这里我们实现了scull_read,scull_write,scull_open,和scull_release函数这些基本的操作。还有一些scull_ioctl等就是一些高级的话题,留待后续文章交代。

所谓万事开头难,首先要学会初始化一个设备。而要初始化一个设备首先要定义好它的设备结构。

struct scull_dev {
        struct scull_qset* data;
        int quantum;
        int qset;
        unsigned long size;
        unsigned int access_key;
        struct semaphore sem;
        struct cdev cdev;
};

这个设备结构中重要的就是struct cdev这个系统里定义的结构,在这里,cdev.ops可以设置为一个file_operations的数据结构。struct scull_qset 是一个链表的数据结构,用来存储数据的,其实可以用其他的数据结构来替代,但是内核里面的内存空间实际上要比用户空间来的紧张,所以一般采用动态的内存分配方法,所以这里采用链表的数据结构是合适的。

scull_init_module函数,这是一个初始化设备的函数,主要的目的是注册设备,然后分配在Linux系统中的设备号。一个设备在Linux中总是有主,次设备编号的,dev_t这样的数据结构在<linux/types.h>中定义,实际上是一个32位的数,其中12位用来表示主设备号,而其余20位用来表示次设备号。要获得主次设备号要使用以下的宏:

MAJOR(dev_t dev);

MINOR(dev_t dev);

反之,如果要把主次设备号转换成dev_t数据结构,要使用MKDEV(int major,int minor)的宏。

int scull_init_module(void)
{
        int result,i;
        dev_t dev =0;
        if(scull_major)
        {
                dev = MKDEV(scull_major,scull_minor);
                result = register_chrdev_region(dev,scull_nr_devs,"scull"); //静态分配一个设备号

        }
        else
        {
        result = alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull");
                scull_major = MAJOR(dev); //动态分配设备号。
        }
        if(result<0)
        {
                printk(KERN_WARNING "scull: can not get major %d/n",scull_major);
                return result;
        }
        scull_devices = kmalloc(scull_nr_devs*sizeof(struct scull_dev),GFP_KERNEL);
        if(!scull_devices)
        {
                result = -ENOMEM;
                goto fail;
        }
        memset(scull_devices,0,scull_nr_devs*sizeof(struct scull_dev));
        for(i=0;i<scull_nr_devs;i++)
        {
                scull_devices[i].quantum = scull_quantum;
                scull_devices[i].qset = scull_qset;
                init_MUTEX(&scull_devices[i].sem);
                scull_setup_cdev(&scull_devices[i],i);
        }
        dev = MKDEV(scull_major,scull_minor+scull_nr_devs);
        printk(KERN_ALERT "Scuccessful load scull device!");
        return 0;

fail:
        scull_cleanup_module();
        return result;
}

主要的scull_init_module代码在这里。我们可以用module_init(scull_init_module)加载这样的模块。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值