一,前言
所谓学习,那么就和单纯完成任务是不同的,我的学习也是有目标的,那么从linux驱动开发的角度来说,若只是配置下就能用了,这样有点知其然而不知其所以然,所以我的目标就是先会用,然后学习框架及源码,便于将来遇到问题后调试定位和代码优化。另外,最主要的就是学习linux中面向对象的抽象设计思路。所以做完了SG90的PWM后,那么就要分析PWM源码了。并且发现了看源码有助于我在写驱动的时候对函数的理解及应用。
二,PWM源码框架
PWM框架算是比较简单的。我画了个图备忘下。
三,PWM源码分析
每个pwm-芯片.c都会调用pwmchip_add,比如ti的am335芯片,配置了pwm及使能设备树后。
1.ehrpwm_pwm_probe->pwmchip_add(pwm-tiehrpwm.c)添加PWM驱动。
2.pc->chip.ops = &ehrpwm_pwm_ops;将注册的芯片pwm操作挂到chip ops中。
3.pwmchip_add->pwmchip_sysfs_export(core.c)添加到pwm radix树中,以供将来使用时搜索请求设备。
4.pwmchip_sysfs_export->device_create(sysfs.c)进行设备文件创建。
pwm文件夹中core.c是中间代理,自制驱动开始使用pwm是通过调用pwm_request(request a PWM device)开始的,我觉得它就类似于gpio_request、gpio_free的作用。pwm子系统和i2c不同的原因在于i2c它还有一个总线的概念,所以多出来了adapter和algorithm进行i2c和SMBus的抽象。
pwm_request->pwm_device_request(core.c)
pwm->chip->ops->request会跳入具体注册的pwm中(在pwmchip_add中已经挂载绑定了关系)。
看到如下源码,我发现SG90驱动在open文件的时候也不需要加锁,因为request调用的时候会检查此pwm资源是否空闲,否则返回-EBUSY。我只要将-EBUSY返回给APP即可。
static int pwm_device_request(struct pwm_device *pwm, const char *label)
{
int err;
if (test_bit(PWMF_REQUESTED, &pwm->flags))
return -EBUSY;
if (!try_module_get(pwm->chip->ops->owner))
return -ENODEV;
if (pwm->chip->ops->request) {
err = pwm->chip->ops->request(pwm->chip, pwm);
if (err) {
module_put(pwm->chip->ops->owner);
return err;
}
}
set_bit(PWMF_REQUESTED, &pwm->flags);
pwm->label = label;
return 0;
}
四,遇到的问题
我对sysfs,debugfs,config文件系统不熟悉只是死板的使用,所以第一天我搭建pwm硬件环境的时候,没有写任何代码,直接设置pwm文件即可操作pwm,原理是什么呢?通过看源码也找到答案了。在pwm的core.c中,initail call会调用pwm_debugfs_init->debugfs_create_file
通过debugfs_create_file就可以在debugfs中建立一个文件结点,就像字符设备驱动那样,只需要对这个文件结点进行open就可以进行read、write、ioctl,等等操作。
static const struct file_operations pwm_debugfs_ops = {
.owner = THIS_MODULE,
.open = pwm_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};