Chinaunix首页 | 论坛 | 博客
  • 博客访问: 264939
  • 博文数量: 25
  • 博客积分: 329
  • 博客等级: 一等列兵
  • 技术积分: 1380
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-24 09:43
文章分类

全部博文(25)

文章存档

2014年(4)

2013年(12)

2012年(9)

分类: 嵌入式

2013-03-03 17:50:31


MODULE_AUTHOR("xxxx"); //描述模块作者
MODULE_LICENSE("Dual BSD/GPL");//指定代码使用双重许可证
MODULE_VERSION("v1.0");        //模块版本
MODULE_DESCRIPTION("xxxx"); //说明模块用途
MODULE_ALIAS("xxx");    //模块别名
MODULE_DEVICE_TABLE();   //用来告诉内核空间模块所支持的设备


static int __init xxx_init(void)  //初始化函数
{
                                 //注意错误的处理,goto就比较有效
 return 0; 
}

static void __exit xxx_exit(void) //清除函数
{

}
 
module_init(xxx_init);//设备加载函数
module_exit(xxx_exit);//设备卸载函数

/*
在注册设备时, 注册可能失败. 即便最简单的动作常常需要内存分配, 分配的内存可能不可用.
因此模块代码必须一直检查返回值,并且确认要求的操作实际上已经成功.
当模块的初始化出现错误后,模块必须自行撤销已注册的设施,错误恢复处理有时goto语句比较有效

可通过命令:cat /proc/devices 来查看系统使用的设备号

*/

MAJOR(dev_t dev);  //将设备编号转换为主设备号  ,dev_t 是 32 位的数, 12 位用作主编号, 20位用作次编号.
MINOR(dev_t dev);  //将设备编号转换为次设备号
MKDEV(int major, int minor)//将主设备号转换为设备编号,minor一般取0,可查看Documentation/device.txt文件确定可用的设备号
int register_chrdev_region(dev_t first,unsigned int count, char *name) 
                                                                     //分配从first开始count个设备编号,first为预先设定的设备号,成功返回0,失败返回负的错误码,name 是应当连接到这个编号范围的
                                                                     //设备的名子; 它会出现在 /proc/devices 和 sysfs 中.
                                                                    
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name)//动态分配设备号, firstminor通常为0,分配的结果保存在第一个参数里
                                                                                          
void unregister_chrdev_region(dev_t first, unsigned int count)  //卸载设备号

//驱动程序需要将设备编号和内部函数连接起来,这些内部函数用来实现设备的操作
//一般采用一些方式分配设备号:
 if(xxx_major)
     {
       xxx_devno=MKDEV(xxx_major,xxx_minor);   //将主设备号和此设备号转换成设备编号,
       result=register_chrdev_region(xxx_devno,DEV_NUM,DEVICE_NAME);  //在指定设备主次设备号的情况下,通过这种方式分配设备编号
      }
   else
      {
       result=alloc_chrdev_region(&xxx_devno,xxx_minor,DEV_NUM,DEVICE_NAME);  //如果没有指定主次设备号,则采用这种方式进行动态分配主
       xxx_major=MAJOR(led_devno); 
      }
  /* 如果xxx_minor = 0   ;DEV_NUM= 3,则主设备好为xxx_major,次设备号为0、1、2均注册到内核中,
   *  (次设备号的初始值)  (要注册的设备的个数)
   * 主设备号为xxx_major,次设备号为3的设备节点没有注册到内核中,在应用程序中不能使用open函数打开
   */

/*
 * 文件结构代表一个打开的文件(文件描述符). (它不特定给设备驱动; 系统中每个打开的文件有一个关联的 struct file 在内核空间). 它由内核在 open 时创建, 并传递
 * 给在文件上操作的任何函数, 直到最后的关闭. 在文件的所有实例都关闭后,内核释放这个数据结构
 */
struct file
{
 ...................
 const struct file_operations *f_op;  //与文件相关操作,内核在执行open操作时对这个指针赋值,以后需要处理这些操作时就读取这个指针,filep->f_op中的值决不会为方便引用而保存起来
                                       //也就是说,我们可以在任何时候修改文件的关联操作,在返回给调用者之后,新的操作方法就会立即生效
 loff_t   f_pos;     //当前的读/写位置,loff_t是一个64位数(gcc中long long定义),如果驱动程序需要知道文件中的当前位置,可以读取这个值,但不需要修改它,read/write
                        //会使用它们接收到最后的那个指针参数来更新这一位置,而不是直接对filep->f_pos进行操作
 void   *private_data;   //open系统调用在调用驱动程序的open方法之前将这个指针设置为NULL,private_data在跨系统调用时保存状态信息是非常有用的资源,我们大部分示例都使用它
                            //struct xxx_dev *xxx_dev;   dev=container_of(inode->i_cdev,struct xxx_dev,cdev)  //xxx_dev是我们自己定义的设备私有结构体,其中包含 struct cdev cdev成员,
                            //这样今后就可以方便对该指针的访问了
                            //container_of是通过结构体内部成员的地址来获取整个结构体的地址
 ...................
};

//inode 结构由内核在内部用来表示文件. 因此, 它和代表打开文件描述符的文件结构是不同的. 可能有代表单个文件的多个打开的文件描述符的file结构, 但是它们都指向一个单个 inode 结构
struct inode {    //inode结构,此结构包含了大量的文件信息
 
 ...................
 dev_t   i_rdev;   //对表示设备文件的inode结构,该字段包含了真正的设备编号
 ...................
};

从inode中获得主设备号和次设备号:
unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode);

 // 创建类,ls /proc/class
 class_create(owner, name);
 
 // 在类下创建设备节点,在busybox下使用mdev创建
 struct device *device_create(struct class *class, struct device *parent,
        dev_t devt, void *drvdata, const char *fmt, ...)

阅读(2088) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~