新接口函数的用法:
主次设备号的理解:
主设备号是:某一类设备
次设备号是:这一类中的某一个设备
主设备号 次设备号 设备三者之间的关系
#define MAJOR(dev)((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
整个构成:
整个的驱动注册分为两步: 第一步:主设备号的申请 第二步file_operation这个结构体的注册
第一步:手动申请主设备号(方式一)
1.1dev_t devid; //定义这个变量将来作为输入型参数
1.2devid = MKDEV(major, 0); //从 主设备号major 从设备号为0开始 (可以写程序验证)
register_chrdev_region(devid, count, name); 相当于向内核申请一个主设备号
count 表示个数
name 表示名字
1.3到达这一步:主设备+次设备号申请结束
第一步:系统自动分配主设备号(方式二)
1.int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
例如:alloc_chrdev_region(&devid, unsigned baseminor, unsigned count, DEVNAME);
2.获得主设备号
major = MAJOR(devid); //主设备号
--------------------------------达到这理 主次设备号已经解决主次设备号的问题:
第二步: 向系统注册设备驱动 (file_opretion)
static struct cdev pc8736x_gpio_cdev;//作为输入性参数 在函数退出的事情需要使用
cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fileops);
cdev_add(&pc8736x_gpio_cdev, devid, count);
第一步和第二步这个注册结束
第三步:
退出函数:
cdev_del(&fif_cdev);
unregister_chrdev_region(devid, 255);
//void unregister_chrdev_region(dev_t from, unsigned count) 函数原型:count: the number of device numbers to unregister
----------------------------------------------------------------------------------------------------------------------------------------------------------
源码分析:
老接口的源码分析: register_chrdev (0, "twl", &twl_fops) 这个例子作为函数的嵌入点:
参数:major=0 name = "twl" fops = &twl_fops
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
参数:major = 0 name = "twl" fops = &twl_fops
//这里名字并没有在/dev下创建设备, 某块的名字
__register_chrdev(major, 0, 256, name, fops);
struct char_device_struct *cd; //表示主次设备号
struct cdev *cdev;//
/*
//
static struct char_device_struct {
struct char_device_struct *next; //用来做链表
unsigned int major;//主设备号
unsigned int baseminor;//次设备号头
int minorct; //次设备号有多少个
char name[64];//驱动的名字
struct cdev *cdev;/* will die *///存储内部的东西
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
每一个结构体表示一个设备
*/
/*
struct cdev {
struct kobject kobj;
struct module *owner; //用来记录把模块挂钩
const struct file_operations *ops; //file_operations
struct list_head list;
dev_t dev; //内核中的设备号 主设备号 + 次设备号
unsigned int count; //计数 主要是用来记录 这个设备被调用了几次
};
*/ 以上都是相应的数据结构
cd = __register_chrdev_region(major, baseminor, count, name);
struct char_device_struct *cd, **cp; //定义一个指针
//分配一个内存空间(类型是: struct char_device_struct)
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
找到之后返回
填充这个结构体
cdev = cdev_alloc(); //创建一个struct cdev 这才是表示一个设备
//填充这个结构体
cdev->owner = fops->owner;
cdev->ops = fops;
kobject_set_name(&cdev->kobj, "%s", name);
//用激活这个设备主次设备号 和 file_operations结构体挂接
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);//激活
cd->cdev = cdev;
return major ? 0 : cd->major;
总结:
第一步:创建struct char_device_struct *cd; //表示主次设备号 这样的结构体
第二步:在系统中256中寻找一个空位。
---------这样就可以填充主次设备号了----
第三步:struct cdev *cdev;创建这一个并且填充
第四步:把cdev和第二步创建的主次设备号挂接
return
核心:struct char_device_struct *cd;这个结构体
新接口的源码分析:
MKDEV(171, 0)
举例:register_chrdev_region(MKDEV(171, 0), 256, "ieee1394")
参数: from = MKDEV(171, 0)
参数: count= 256
参数: name = "ieee1394"
第一步主次设备号的问题:
int register_chrdev_region(dev_t from, unsigned count, const char *name)
参数: from = MKDEV(171, 0)
参数: count= 256
参数: name = "ieee1394"
//注意:dev_t的数据结构是一个 u32 long型
for (n =from ; n < to; n = next)
{
循环一次调用:__register_chrdev_region(MAJOR(n), MINOR(n),
next - n, name);
}
第二步:
static struct cdev fif_cdev; //创建
cdev_init(&fif_cdev, &seconde_fops);
//主要是把seconde_fops 挂接到了fif_cdev上
adev->cdev.owner = THIS_MODULE;
cdev_add(&fif_cdev, devid, 255);//激活
总结:内核是如何申请和释放资源的:
思想:
内核中有kzalloc分配内存并且free,但是kzalloc和free并不在一个函数中,
甚至不是由同一个人写的。所以kzalloc分配了一段内存,但是这段内存一般都要等到
其他代码,或者其他人来释放。 kzalloc 和 free很难对应。这样内核就无法维护。
例如:只要你定义了一个内存(通过kzalloc),如果一不小心没有释放,那么就会导致
内存空间浪费
内核提供的解决思路:
当有人用的时候,这个资源+1 ,当没有人用的时候 这个资源减1
当这个资源减到0时,就会把这个资源删除
每次使用的时候就会来检测。这就的优势,导致你可以不用再管谁创建和删除了
这样内存释放和回收都可以进行自动化了