设为首页 加入收藏

TOP

Linux 字符驱动开发心得(一)
2015-02-02 14:50:34 来源: 作者: 【 】 浏览:58
Tags:Linux 字符 驱动 开发 心得

Linux字符驱动框架相比初学还是比较难记的,在学了一阵子字符驱动的开发后对于框架的搭建总结出了几个字 。


对于框架来讲主要要完成两步。


申请设备号,注册字符驱动


其关键代码就两句



int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);//动态申请设备号
?
int cdev_add(struct cdev *, dev_t, unsigned);? ? ? ? ? ? ? ? ? ? ? //注册字符驱动


执行完次就可以将我们的驱动程序加载到内核里了


首先我们搭建主程序,字符驱动的名字就叫做"main"


首先先写下将要用到的头文件,以及一个宏定义,指明了我们驱动的名称,当然名称可以任意这里就取"main" 作为名字?


#include
#include
#include
#include
#include
#include
#define MUDULE_NAME "main"


驱动由于需要加载到内核里,所以我们需要声明一下我们驱动所遵循的协议,如果没有申明,那么加载内核的时候系统会提示一段信息。我们按照内核的风格来,就加一个GPL协议吧


MODULE_LICENSE("GPL");


我们要想将我们的驱动注册到内核里,就必须将我们的驱动本身作为一个抽象,抽象成一个struct cdev的结构体。因为我们系统内部有许多中字符驱动,为了将这些不同种类的驱动都能使用同一个函数进行注册,内核声明了一个结构体,不同的驱动通过这个结构体--变成了一个抽象的驱动供系统调用。这段有点罗嗦,我们来看一下cdev这个结构体吧。


//这段不属于主程序
struct cdev {
? ? struct kobject kobj;
? ? struct module *owner;
? ? const struct file_operations *ops;
? ? struct list_head list;
? ? dev_t dev;
? ? unsigned int count;
};


这个结构体就包含了一个驱动所应有的东西其中 kobj 不需要管它,我也没有仔细研究,owner指向模块的所有者,常常使用THIS_MODULE这个宏来赋值,ops是我们主要做的工作,其中定义了各种操作的接口。


下面我们定义了我们程序的抽象体mydev,以及他所需要的接口


struct cdev mydev;
struct file_operations ops;


struct file_operations这个结构有点庞大。


//不属于本程序
struct file_operations {
? ? struct module *owner;
? ? loff_t (*llseek) (struct file *, loff_t, int);
? ? ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
? ? ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
? ? ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
? ? ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
? ? int (*readdir) (struct file *, void *, filldir_t);
? ? unsigned int (*poll) (struct file *, struct poll_table_struct *);
? ? long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
? ? long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
? ? int (*mmap) (struct file *, struct vm_area_struct *);
? ? int (*open) (struct inode *, struct file *);
? ? int (*flush) (struct file *, fl_owner_t id);
? ? int (*release) (struct inode *, struct file *);
? ? int (*fsync) (struct file *, loff_t, loff_t, int datasync);
? ? int (*aio_fsync) (struct kiocb *, int datasync);
? ? int (*fasync) (int, struct file *, int);
? ? int (*lock) (struct file *, int, struct file_lock *);
? ? ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
? ? unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
? ? int (*check_flags)(int);
? ? int (*flock) (struct file *, int, struct file_lock *);
? ? ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
? ? ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
? ? int (*setlease)(struct file *, long, struct file_lock **);
? ? long (*fallocate)(struct file *file, int mode, loff_t offset,
? ? ? ? ? ? ? loff_t len);
};


上面看到,这个结构内部都是一些函数指针,相当与这个结构本身就是一个接口,在c语言中没有接口这个概念,使用这种方式来定义也是一种巧妙的用法。不过有所不同的是我们可以不完全实现其中的接口。


应用程序在使用驱动的时候常常需要open,write,read,close这几种操作,也就对应了file_operations结构中的open,write,read,release这几个函数指针。下面我们开始实现我们自己的函数体。注意:我们自己实现的函数必须满足接口函数所定义的形式。


static int main_open(struct inode* inode,struct file* filp)
{
? ? return 0;
}


这个教程里面的程序,我们就让驱动只能往里面写一个字符为例,读取也是只能读取一个字符。


?我们定义一个静态的字符类型的变量来当作我们的存储空间,通过copy_from_user来将用户空间的数据拷贝到我们驱动? 所在的内核空间。原型是:?


static inline long copy_from_user(void *to, const void __user * from, unsigned long n)


?类似地,我们使用copy_to_us

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇Ubuntu下制作Tiny6410烧写SD卡 下一篇Ztree + PHP 无限极节点 递归查找..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: