static ssize_t regmap_reg_ranges_read_file()

in regmap/regmap-debugfs.c [360:426]


static ssize_t regmap_reg_ranges_read_file(struct file *file,
					   char __user *user_buf, size_t count,
					   loff_t *ppos)
{
	struct regmap *map = file->private_data;
	struct regmap_debugfs_off_cache *c;
	loff_t p = 0;
	size_t buf_pos = 0;
	char *buf;
	char *entry;
	int ret;
	unsigned int entry_len;

	if (*ppos < 0 || !count)
		return -EINVAL;

	if (count > (PAGE_SIZE << (MAX_ORDER - 1)))
		count = PAGE_SIZE << (MAX_ORDER - 1);

	buf = kmalloc(count, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	entry = kmalloc(PAGE_SIZE, GFP_KERNEL);
	if (!entry) {
		kfree(buf);
		return -ENOMEM;
	}

	/* While we are at it, build the register dump cache
	 * now so the read() operation on the `registers' file
	 * can benefit from using the cache.  We do not care
	 * about the file position information that is contained
	 * in the cache, just about the actual register blocks */
	regmap_calc_tot_len(map, buf, count);
	regmap_debugfs_get_dump_start(map, 0, *ppos, &p);

	/* Reset file pointer as the fixed-format of the `registers'
	 * file is not compatible with the `range' file */
	p = 0;
	mutex_lock(&map->cache_lock);
	list_for_each_entry(c, &map->debugfs_off_cache, list) {
		entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n",
				     c->base_reg, c->max_reg);
		if (p >= *ppos) {
			if (buf_pos + entry_len > count)
				break;
			memcpy(buf + buf_pos, entry, entry_len);
			buf_pos += entry_len;
		}
		p += entry_len;
	}
	mutex_unlock(&map->cache_lock);

	kfree(entry);
	ret = buf_pos;

	if (copy_to_user(user_buf, buf, buf_pos)) {
		ret = -EFAULT;
		goto out_buf;
	}

	*ppos += buf_pos;
out_buf:
	kfree(buf);
	return ret;
}