Chinaunix首页 | 论坛 | 博客
  • 博客访问: 188443
  • 博文数量: 46
  • 博客积分: 1355
  • 博客等级: 中尉
  • 技术积分: 336
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-02 16:20
文章分类

全部博文(46)

文章存档

2017年(2)

2014年(12)

2012年(1)

2011年(5)

2010年(26)

我的朋友

分类: 嵌入式

2010-05-04 10:59:54

字符设备驱动程序框架
设备驱动程序看起来十分的难一个主要的原因是,在写设备驱动程序的时候开发者要遵循的内核规则太多了。一个又一个的struc看得人的头大。因为一般的驱动程序都没不编译进内核的而时动态的加载和卸载的,所以驱动程序的编写一般是有规律可循的,一个固定的模板,这样内核才能认识新加入的驱动。下面就是字符设备的一般的框架:(自己总结的,老是转别人的不好,也要做点贡献吧,哈哈。)

注册与注销:
//设备结构体,里面还可以定义信号量sem,scull.C里这是要样做的(以后把这个代码分析一下)。这是一种良好的编程习惯
struct xxx_dev_t
{
 struct cdev cdev;
....... 
}xxx_dev; //设备对像

//设备驱动程序模块加载函数
static int __init xxx_init(void)
{
  ......
cdev_init(&xxx_dev.cdev,&xxx_fops);  //和下面的add啊,dell一起定义在cdev.h里面

原型:

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

{

memeset(cdev,0,sizeof*cdev);//这个嘛,初始化为0,bzero()好像也可以

INIT_LIST_HEAD(&cdev->list);//初始化队列

kobject_init(&cdev->kobj, &ktype_cdev_default);


cdev->ops = fops;//把两个传入的参数给关联起来,

}

xxx_dev.cdev.owner=THIS_MODULE;  //任何驱动代码都一样,但是少不了,照做,哈哈
//
if(xxx_major)
{
   register_chrdev_region(xxx_dev_no,1,DEV_NAME);//如果自己定义了主设备号这个可能会成功
但是LDD3中是不推荐这种方法的,因为可能会产生冲突
}
else
{
   alloc_chrdev_region(xxx_dev_no,0,1,DEV_NAME);//动态分配的,一般都要用这种方法,申请的主设备号在xxx_dev_no中了
}
  ret=cdev_add(&xxx_dev.cdev,xxx_dev_no,1);//cdev.h
.....
}
//设置驱动模块卸载函数
static void __exit  xxx_exit(void)
{
  unregister_chrdev_region(xxx_dev_no,1);//释放设备
  cdev_del(&xxx_dev.cdev);   //cdev.h
.....
}
 

 
有关file operations:
ssize_t xxx_read(struct file *filp,char __user *buf ,size_t count, loff_t *f_pos)
{
.....            
copy_to_user();   //完成内核空间与用户空间的数据交互,这个Copy的数据到底是从那里来到那里去呢?一功都从用户出发。(内核到用户)
.....
}
 
ssize_t xxx_write(struct file *filp,const char __user *buf ,size_t count, loff_t *f_pos)
{
.....
copy_from_user();  // THE SAME
.....
}
int xxx_ioctl(struct inode *inode ,struct file *filp,unsigned int cmd,unsigned long arg)
{
  ....
  switch(cmd)// 这个嘛,一般要定义幻数(magic)。scull是定义在了本地的头文件中了,这是一个习惯哦,LDD3中定义了14个命令,my god!
{
  case xxx_cmd1:
     .....;
      break;
  case xxx_cmd2:
     .....;
     break;
  default:
   return -ENOTTY;//不支持的命令
}
return 0;
}
当然文件操作不止这么几个,在fs.h中可以看到里面的定义,本站也有有关三个数据结构的文章。
还可以多定义几个都没有问题的,看需要。
当然 open()
release()可不能少了。

把自己实现的函数关联起来:
 
strcut file_operations xxx_fops=
{
 .owner=THIS_MODULE,  //幸亏有了函数指针,不然一个一个对应写,累死人啊,
 .read =xxx_read,
 .write=xxx_write,
 .ioctl=xxx_ioctl,
......
};
 
 
模块的入口,必须要使用这两个宏来加载初始化和卸载模块
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");//这个得加上,不然内核认不了
 
 
 
 
 
 
 
 
 
阅读(1758) | 评论(0) | 转发(0) |
0

上一篇:uboot启动过程

下一篇:scull分析

给主人留下些什么吧!~~