PWMDEV是一个典型的CDEV。
内核实现了PWMCORE,向上提供一个CDEV的用户接口,向下,调用不同的PWMCONTROLLER提供服务。
类似于GPIOCHIP,在kernel中,PWMCHIP就是用来控制PWMCONTROLLER的一个结构体。
struct pwm_chip{
struct device* dev;
const struct pwm_ops* ops;
int base;
unsigned int npwm;
struct pwm_device* pwms;
...
};
内核提供了PWM相关的API,
int pwmchip_add(struct pwm_chip* chip);
int pwmchip_remove(struct pwm_chip* chip);
PWMOPS是底层驱动的操作集,这是PWM的具体驱动需要实现的函数集。
struct pwm_ops{
int (*request)(struct pwm_chip* chip, struct pwm_device* pwm);
int (*free)(struct pwm_chip* chip, struct pwm_device* pwm);
int (*enable)(struct pwm_chip* chip, struct pwm_device* pwm);
int (*disable)(struct pwm_chip* chip, struct pwm_device* pwm);
int (*config)(struct pwm_chip* chip, struct pwm_device* pwm, int duty, int period);
...
};
这样,内核对PWMCTRL的管理,就转化成注册一个pwm_chip的问题。
如果DerivedPWMCHIP,同时实现为一个platform device。那么可以在probe中,只需要创建一个DerivedPWMCHIP,填充好后,将PWMCHIP利用pwmchip_add内核服务注册到内核中。
类似于CDEV,我们需要实例化pwm_ops,并编写具体函数。
由于使用了DerivedDEV,所以要配置好private_data。
例如:
struct fake_chip{
struct pwm_chip
int foo;
int bar;
...
};
static int fake_pwm_probe(struct platform_device* pdev)
{
struct fake_chip* priv;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
priv->chip.ops = &fake_pwm_ops;
priv->chip.dev = &pdev->dev;
...
platform_set_drv_data(pdev, priv);
ret = pwmchip_add(&priv->chip);
return ret;
}
从中可以看出,利用DEVICE对象,即pdev->dev句柄,创建了对应的DerivedDEV。
然后填充了PWMOPS。配置了PWMCHIP关联的DEVICE对象。
然后将创建的PWMCHIP的句柄,配置到drvdata中,建立回溯引用。
最后,将PWMCHIP注册到了内核中。
另外一个结构体就是pwm_device,这是用户程序需要使用的结构体。
struct pwm_device{
const char* name;
unsigned int hwpwm;
unsigned int pwm;
unsigned long flags;
struct pwm_args args;
struct pwm_state state;
struct pwm_chip* chip;
void* chip_data;
...
};
struct pwm_args{
unsigned int period;
...
};
struct pwm_state{
unsigned int period;
unsigned int duty_cycle;
bool enabled;
...
};
PWMDEV关联到一个PWMCHIP,所以,一个PWMDEV,是利用PWMCHIP完成的具体操作任务。
用户程序使用内核提供的API来操作pwm_device。
struct pwm_device* pwm_get(struct device* dev, const char* con_id);
struct pwm_device* pwm_put(struct pwm_device* pwm);
int pwm_config(struct pwm_device* pwm, int duty, int period);
int pwm_enable(struct pwm_device* pwm);
void pwm_disable(struct pwm_device* pwm);
驱动设计时,我们需要在probe中,向内核注册PWMDEV,即,利用pwm_get来完成注册。