上一篇中,我们分析了request_mem_region的实现。
现在来看看board文件中是如何配置ssi和esai的。
现在做的项目是基于freescale平台的。
所以代码也是基于该平台代码进行分析。
先看看ssi。
首先定义了一个mxc_audio_platform_data结构体和一个platform_device结构体:
static struct mxc_audio_platform_data XXXX_data[] = { { .ssi_num = 1, .src_port = 2, .ext_port = 5, .init = xxxx_init0, .hp_gpio = -1, }, }; static struct platform_device xxxx_device = { .name = "xxxx", }; /* * This struct is to define the number of SSIs on a platform, * DAM source port config, DAM external port config, * regulator names, and other stuff audio needs. */ struct mxc_audio_platform_data { int ssi_num; int src_port; int ext_port; int intr_id_hp; int ext_ram; struct clk *ssi_clk[2]; int hp_gpio; int hp_active_low; /* headphone irq is active low */ int mic_gpio; int mic_active_low; /* micphone irq is active low */ int sysclk; const char *codec_name; int (*init) (void); /* board specific init */ int (*amp_enable) (int enable); int (*clock_enable) (int enable); int (*finit) (void); /* board specific finit */ void *priv; /* used by board specific functions */ }; struct platform_device { const char * name; int id; struct device dev; u32 num_resources; struct resource * resource; const struct platform_device_id *id_entry; /* MFD cell pointer */ struct mfd_cell *mfd_cell; /* arch specific additions */ struct pdev_archdata archdata; };
然后调用mxc_register_device将上面定义的两个结构体进行注册。
mxc_register_device的实现:
int __init mxc_register_device(struct platform_device *pdev, void *data) { int ret; // data即是前面的XXXX_data。pdev即是前面的xxxx_device pdev->dev.platform_data = data; /× /** * platform_device_register - add a platform-level device * @pdev: platform device we're adding */ int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); return platform_device_add(pdev); } /** * device_initialize - init device structure. * @dev: device. * * This prepares the device for use by other layers by initializing * its fields. * It is the first half of device_register(), if called by * that function, though it can also be called separately, so one * may use @dev's fields. In particular, get_device()/put_device() * may be used for reference counting of @dev after calling this * function. * * NOTE: Use put_device() to give up your reference instead of freeing * @dev directly once you have called this function. */ void device_initialize(struct device *dev) { dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); INIT_LIST_HEAD(&dev->dma_pools); mutex_init(&dev->mutex); lockdep_set_novalidate_class(&dev->mutex); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); // device_pm_init - Initialize the PM-related part of a device object. device_pm_init(dev); set_dev_node(dev, -1); } static inline void set_dev_node(struct device *dev, int node) { dev->numa_node = node; } /** * platform_device_add - add a platform device to device hierarchy * @pdev: platform device we're adding * * This is part 2 of platform_device_register(), though may be called * separately _iff_ pdev was allocated by platform_device_alloc(). */ int platform_device_add(struct platform_device *pdev) { int i, ret = 0; if (!pdev) return -EINVAL; if (!pdev->dev.parent) pdev->dev.parent = &platform_bus; pdev->dev.bus = &platform_bus_type; // 前面只指定了pdev->name没有指定pdev->id if (pdev->id != -1) dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); else dev_set_name(&pdev->dev, "%s", pdev->name); for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource[i]; if (r->name == NULL) r->name = dev_name(&pdev->dev); // 上一篇中,已经分析过resource的父子关系 p = r->parent; if (!p) { if (resource_type(r) == IORESOURCE_MEM) p = &iomem_resource; else if (resource_type(r) == IORESOURCE_IO) p = &ioport_resource; } // 将resource添加到resource tree中。 /× /** * insert_resource - Inserts a resource in the resource tree * @parent: parent of the new resource * @new: new resource to insert * * Returns 0 on success, -EBUSY if the resource can't be inserted. */ int insert_resource(struct resource *parent, struct resource *new) { struct resource *conflict; /× /** * insert_resource_conflict - Inserts resource in the resource tree * @parent: parent of the new resource * @new: new resource to insert * * Returns 0 on success, conflict resource if the resource can't be inserted. * * This function is equivalent to request_resource_conflict when no conflict * happens. If a conflict happens, and the conflicting resources * entirely fit within the range of the new resource, then the new * resource is inserted and the conflicting resources become children of * the new resource. */ struct resource *insert_resource_conflict(struct resource *parent, struct resource *new) { struct resource *conflict; write_lock(&resource_lock); conflict = __insert_resource(parent, new); write_unlock(&resource_lock); return conflict; } ×/ conflict = insert_resource_conflict(parent, new); return conflict ? -EBUSY : 0; } ×/ if (p && insert_resource(p, r)) { printk(KERN_ERR "%s: failed to claim resource %d\n", dev_name(&pdev->dev), i); ret = -EBUSY; goto failed; } } pr_debug("Registering platform device '%s'. Parent at %s\n", dev_name(&pdev->dev), dev_name(pdev->dev.parent)); ret = device_add(&pdev->dev); if (ret == 0) return ret; failed: while (--i >= 0) { struct resource *r = &pdev->resource[i]; unsigned long type = resource_type(r); if (type == IORESOURCE_MEM || type == IORESOURCE_IO) release_resource(r); } return ret; } ×/ ret = platform_device_register(pdev); if (ret) pr_debug("Unable to register platform device '%s': %d\n", pdev->name, ret); return ret; }
接下来定义了imx_add_platform_device结构体:
static struct imx_ssi_platform_data xxx_ssi0_pdata = { .flags = IMX_SSI_DMA | IMX_SSI_SYN, };
然后调用imx6q_add_imx_ssi进行注册:
imx6q_add_imx_ssi(0, &xxx_ssi0_pdata);
imx6q_add_imx_ssi的实现:
#define imx6q_add_imx_ssi(id, pdata) \ imx_add_imx_ssi(&imx6_imx_ssi_data[id], pdata)
其中imx6_imx_ssi_data的定义:
const struct imx_imx_ssi_data imx6_imx_ssi_data[] __initconst = { #define imx6q_imx_ssi_data_entry(_id, _hwid) \ imx_imx_ssi_data_entry(MX6Q, _id, _hwid, SZ_4K) imx6q_imx_ssi_data_entry(0, 1), imx6q_imx_ssi_data_entry(1, 2), imx6q_imx_ssi_data_entry(2, 3), }; #define imx_imx_ssi_data_entry(soc, _id, _hwid, _size) \ [_id] = { \ .id = _id, \ .iobase = soc ## _SSI ## _hwid ## _BASE_ADDR, \ .iosize = _size, \ .irq = soc ## _INT_SSI ## _hwid, \ .dmatx0 = soc ## _DMA_REQ_SSI ## _hwid ## _TX0, \ .dmarx0 = soc ## _DMA_REQ_SSI ## _hwid ## _RX0, \ .dmatx1 = soc ## _DMA_REQ_SSI ## _hwid ## _TX1, \ .dmarx1 = soc ## _DMA_REQ_SSI ## _hwid ## _RX1, \ }
soc ## _SSI ## _hwid ## _BASE_ADDR拼出来其实是:
MX6Q_SSI1_BASE_ADDR
MX6Q_SSI2_BASE_ADDR
MX6Q_SSI3_BASE_ADDR
看看它们的定义:
#define MX6Q_SSI1_BASE_ADDR (ATZ1_BASE_ADDR + 0x28000) /* slot 10 */ #define MX6Q_SSI2_BASE_ADDR (ATZ1_BASE_ADDR + 0x2C000) /* slot 11 */ #define MX6Q_SSI3_BASE_ADDR (ATZ1_BASE_ADDR + 0x30000) /* slot 12 */
与data sheet中一致。
再看imx_add_imx_ssi的实现:
struct platform_device *__init imx_add_imx_ssi( const struct imx_imx_ssi_data *data, const struct imx_ssi_platform_data *pdata) { struct resource res[] = { { .start = data->iobase, .end = data->iobase + data->iosize - 1, .flags = IORESOURCE_MEM, }, { .start = data->irq, .end = data->irq, .flags = IORESOURCE_IRQ, }, #define DMARES(_name) { \ .name = #_name, \ .start = data->dma ## _name, \ .end = data->dma ## _name, \ .flags = IORESOURCE_DMA, \ } DMARES(tx0), DMARES(rx0), DMARES(tx1), DMARES(rx1), }; // data->id是0.res就是前面定义的。pdata就是xxx_ssi0_pdata return imx_add_platform_device("imx-ssi", data->id, res, ARRAY_SIZE(res), pdata, sizeof(*pdata)); } static inline struct platform_device *imx_add_platform_device( const char *name, int id, const struct resource *res, unsigned int num_resources, const void *data, size_t size_data) { return imx_add_platform_device_dmamask( name, id, res, num_resources, data, size_data, 0); } struct platform_device *__init imx_add_platform_device_dmamask( const char *name, int id, const struct resource *res, unsigned int num_resources, const void *data, size_t size_data, u64 dmamask) { int ret = -ENOMEM; struct platform_device *pdev; /** * platform_device_alloc - create a platform device * @name: base name of the device we're adding * @id: instance id * * Create a platform device object which can have other objects attached * to it, and which will have attached objects freed when it is released. */ pdev = platform_device_alloc(name, id); if (!pdev) goto err; if (dmamask) { /* * This memory isn't freed when the device is put, * I don't have a nice idea for that though. Conceptually * dma_mask in struct device should not be a pointer. * See http://thread.gmane.org/gmane.linux.kernel.pci/9081 */ pdev->dev.dma_mask = kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL); if (!pdev->dev.dma_mask) /* ret is still -ENOMEM; */ goto err; *pdev->dev.dma_mask = dmamask; pdev->dev.coherent_dma_mask = dmamask; } /× /** * platform_device_add_resources - add resources to a platform device * @pdev: platform device allocated by platform_device_alloc to add resources to * @res: set of resources that needs to be allocated for the device * @num: number of resources * * Add a copy of the resources to the platform device. The memory * associated with the resources will be freed when the platform device is * released. */ int platform_device_add_resources(struct platform_device *pdev, const struct resource *res, unsigned int num) { struct resource *r = NULL; if (res) { r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL); if (!r) return -ENOMEM; } kfree(pdev->resource); pdev->resource = r; pdev->num_resources = num; return 0; } ×/ if (res) { // 此处add的resource在ssi的probe函数中调用platform_get_resource可以获取。 // platform_get_resource(pdev, IORESOURCE_MEM, 0) ret = platform_device_add_resources(pdev, res, num_resources); if (ret) goto err; } /* /** * platform_device_add_data - add platform-specific data to a platform device * @pdev: platform device allocated by platform_device_alloc to add resources to * @data: platform specific data for this platform device * @size: size of platform specific data * * Add a copy of platform specific data to the platform device's * platform_data pointer. The memory associated with the platform data * will be freed when the platform device is released. */ int platform_device_add_data(struct platform_device *pdev, const void *data, size_t size) { void *d = NULL; if (data) { d = kmemdup(data, size, GFP_KERNEL); if (!d) return -ENOMEM; } kfree(pdev->dev.platform_data); pdev->dev.platform_data = d; return 0; } */ if (data) { ret = platform_device_add_data(pdev, data, size_data); if (ret) goto err; } ret = platform_device_add(pdev); if (ret) { err: if (dmamask) kfree(pdev->dev.dma_mask); platform_device_put(pdev); return ERR_PTR(ret); } return pdev; }
从上面的代码看,imx6_imx_ssi_data只有3个成员。
当调用
imx6q_add_imx_ssi(3, &mx6_smartauto_ssi3_pdata);
时,访问了imx6_imx_ssi_data[3],超出了数组的界限。
难道刚好esai的data数组刚好在ssi的后面,所以imx6_imx_ssi_data[3]其实访问的是esai的数组?
但是为什么probe中打印出来的resource name是ssi-0呢?
看了下前面data数组的定义,其中没有name相关的内容。
再看res数组的定义,也没有name。
所以imx6_imx_ssi_data[3]访问了esai的data数组,但是data数组中并没有name信息,所以name还为ssi-0。
这个name从哪儿来的呢?
又看了一遍代码,也没发现哪儿对resource name进行赋值。