函数wcd_swr_ctrl_add_devices()的主要作用是WCD device添加到platform device列表中,挂载在platform bus下。
此函数被wcd probe函数通过工作队列的方式呼叫,代码如下
schedule_work(&tasha->swr_add_devices_work);
下面一起看看这个函数做了什么,
1. 首先,从work队列中讲tasha结构体指针找到(container_of可真是万能啊,神一样的万能)
tasha = container_of(work, struct tasha_priv,
swr_add_devices_work);
if (!tasha) {
pr_err("%s: Memory for WCD9335 does not exist\n",
__func__);
return;
}
而之所以能从work队列中找到tasha结构体,是因为wcd proble函数在队列初始化的时候”swr_add_devices_work“是结构体”tasha“的一员,所以container_of可以根据指针”swr_add_devices_work“找到tasha结构体的指针。
INIT_WORK(&tasha->swr_add_devices_work, wcd_swr_ctrl_add_devices);
2. 下面是找到wcd9xxx结构体,并判断device node是否为空,如果为空,则返回;
wcd9xxx = tasha->wcd9xxx;
if (!wcd9xxx) {
pr_err("%s: Memory for WCD9XXX does not exist\n",
__func__);
return;
}
if (!wcd9xxx->dev->of_node) {
pr_err("%s: DT node for wcd9xxx does not exist\n",
__func__);
return;
}
关于wcd9xxx这个结构体,也是蛮奇怪的,在wcd probe函数中,它是从device父节点的driver data获得,代码如下。问题是,wcd probe函数刚刚启动,谁又给device父节点的driver data赋值?
tasha->wcd9xxx = dev_get_drvdata(pdev->dev.parent);
3. 下面是获取platform data
platdata = &tasha->swr_plat_data;
在wcd probe函数中,swr_plat_data已经赋值如下(一大堆回调函数)
tasha->swr_plat_data.handle = (void *) tasha;
tasha->swr_plat_data.read = tasha_swrm_read;
tasha->swr_plat_data.write = tasha_swrm_write;
tasha->swr_plat_data.bulk_write = tasha_swrm_bulk_write;
tasha->swr_plat_data.clk = tasha_swrm_clock;
tasha->swr_plat_data.handle_irq = tasha_swrm_handle_irq;
4. 下面是个循环,有多少个device node就有多少次循环
for_each_child_of_node(wcd9xxx->dev->of_node, node) {
5. 申请并创建platform device,
temp = krealloc(swr_ctrl_data,
(ctrl_num + 1) * sizeof(struct tasha_swr_ctrl_data),
GFP_KERNEL);
if (!temp) {
dev_err(wcd9xxx->dev, "out of memory\n");
ret = -ENOMEM;
goto err;
}
swr_ctrl_data = temp;
swr_ctrl_data[ctrl_num].swr_pdev = NULL;
pdev = platform_device_alloc("tasha_swr_ctrl", -1);
if (!pdev) {
dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n",
__func__);
ret = -ENOMEM;
goto err;
}
6. platform device创建好后,初始化parent device和device node,如下
pdev->dev.parent = tasha->dev;
pdev->dev.of_node = node;
7. 向platform deivce中添加platform data,
ret = platform_device_add_data(pdev, platdata,
sizeof(*platdata));
if (ret) {
dev_err(&pdev->dev, "%s: cannot add plat data for ctrl:%d\n",
__func__, ctrl_num);
goto fail_pdev_add;
}
8. 这一步,终于将platform device添加到了device list,
ret = platform_device_add(pdev);
if (ret) {
dev_err(&pdev->dev, "%s: Cannot add swr platform device\n",
__func__);
goto fail_pdev_add;
}
9. 将platform device赋值给swr_ctrl_data结构体的swr_pdev,并将ctrl_num自增1;至此,一轮循环结束。
swr_ctrl_data[ctrl_num].swr_pdev = pdev;
ctrl_num++;
10. 循环结束后,更新ctrl_num的数量和tasha->swr_ctrl_data的指向地址;注意,swr_ctrl_data是数组的首地址,tasha->swr_ctrl_data也是地址。
tasha->nr = ctrl_num;
tasha->swr_ctrl_data = swr_ctrl_data;
至此,函数wcd_swr_ctrl_add_devices()运行结束。
好了,我们再回顾一下这个函数用了哪些重要的结构体,
1. 包含本驱动的私有数据结构体tasha_priv, 和本设备的结构体wcd9xxx;
2. 基本类型的结构体有platform_device和device_node,这个函数就是要添加为platform类型的device;
3. 本函数相关的结构体包括 tasha_swr_ctrl_data 和 wcd_swr_ctrl_platform_data,具体定义如下,
/* Hold instance to soundwire platform device */
struct tasha_swr_ctrl_data {
struct platform_device *swr_pdev;
/* struct ida swr_ida; */
};
struct wcd_swr_ctrl_platform_data {
void *handle; /* holds codec private data */
int (*read)(void *handle, int reg);
int (*write)(void *handle, int reg, int val);
int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
int (*clk)(void *handle, bool enable);
int (*handle_irq)(void *handle,
irqreturn_t (*swrm_irq_handler)(int irq,
void *data),
void *swrm_handle,
int action);
};