分类: 嵌入式
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, ...)