昨天我们说了一些简单模块编写方法,但是终归没有涉及到设备的编写内容,我们可以了解一下相关方面的内容,并且用一个实例来说明在Linux上面设备是如何编写的。虽然我不是专门做Linux驱动的,却也经常收到一些朋友们的来信。在信件中,很多做驱动的朋友对自己的工作不是很满意,认为自己的工作是把代码拷贝来拷贝去,或者说是改来改去,没有什么技术含量。有这种想法的朋友不在少数,我想这主要还是因为他们对自己的工作缺少了解导致。如果有可能,我们可以问问自己这样几个问题:

  (1)我真的搞懂设备的开发驱动流程了吗?我是否可以从0开始,编写一个独立的驱动代码呢?

  (2)我真的了解设备的初始化、关闭、运行的流程吗?

  (3)当前的设备驱动流程是否合理,有没有可以改进的地方?

  (4)对于内核开发中涉及的api调用,我自己是否真正了解、是否明白它们在使用上有什么区别?

  (5)如果我要驱动的设备只是在一个前后台系统中运行,在没有框架帮助的情况下,我是否有信心把它启动和运行起来?

  当然,上面的内容只是我个人的想法,也不一定都正确。但是,知其然,更要知其所以然,熟悉了当前开发流程的优缺点才能真正掌握和了解驱动开发的本质。这听上去有些玄乎,其实也很简单,是要有一种刨根问底、不断改进的精神,这样才能做好自己的工作。因为我们是在pc Linux上学习驱动的,因此暂时没有真实的外接设备可以使用,但是这丝毫不影响我们学习的热情。通过定时器、进程,我们可以仿真出真实设备的各种需求,所以对于系统来说,它是无所谓真设备、假设备的,基本的处理流程对它来说都是一样的。只要大家一步一步做下去,肯定可以了解Linux驱动设备的开发工程的。

  下面,为了说明问题,我们可以编写一段简单的char设备驱动代码,文件名为char.c,

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>

static struct cdev chr_dev;
static dev_t ndev;

static int chr_open(struct inode* nd, struct file* filp)
{
 int major ;
 int minor;
 
 major = MAJOR(nd->i_rdev);
 minor = MINOR(nd->i_rdev);
 
 printk("chr_open, major = %d, minor = %d ", major, minor);
 return 0;
}

static ssize_t chr_read(struct file* filp, char __user* u, size_t sz, loff_t* off)
{
 printk("chr_read process! ");
 return 0;
}

struct file_operations chr_ops = {
 .owner = THIS_MODULE,
 .open = chr_open,
 .read = chr_read
};

static int demo_init(void)
{
 int ret;
 
 cdev_init(&chr_dev, &chr_ops);
 ret = alloc_chrdev_region(&ndev, 0, 1, "chr_dev");
 if(ret < 0 )
 {
  return ret;
 }
 
 printk("demo_init(): major = %d, minor = %d ", MAJOR(ndev), MINOR(ndev));
 ret = cdev_add(&chr_dev, ndev, 1);
 if(ret < 0)
 {
  return ret;
 }
 
 return 0;
}

static void demo_exit(void)
{
 printk("demo_exit process! ");
 cdev_del(&chr_dev);
 unregister_chrdev_region(ndev, 1);
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR(feixiaoxing@163.com);
MODULE_DESCRIPTION("A simple device example!");