iomemory地址被抢占(二)

阅读更多

上一篇中,我们分析了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进行赋值。

你可能感兴趣的:(iomemory地址被抢占(二))