18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) International Business Machines Corp., 2006
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Artem Bityutskiy (Битюцкий Артём), Joern Engel
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * This is a small driver which implements fake MTD devices on top of UBI
108c2ecf20Sopenharmony_ci * volumes. This sounds strange, but it is in fact quite useful to make
118c2ecf20Sopenharmony_ci * MTD-oriented software (including all the legacy software) work on top of
128c2ecf20Sopenharmony_ci * UBI.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit
158c2ecf20Sopenharmony_ci * size (@mtd->writesize) is equivalent to the UBI minimal I/O unit. The
168c2ecf20Sopenharmony_ci * eraseblock size is equivalent to the logical eraseblock size of the volume.
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/err.h>
208c2ecf20Sopenharmony_ci#include <linux/list.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/sched.h>
238c2ecf20Sopenharmony_ci#include <linux/math64.h>
248c2ecf20Sopenharmony_ci#include <linux/module.h>
258c2ecf20Sopenharmony_ci#include <linux/mutex.h>
268c2ecf20Sopenharmony_ci#include <linux/mtd/ubi.h>
278c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
288c2ecf20Sopenharmony_ci#include "ubi-media.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define err_msg(fmt, ...)                                   \
318c2ecf20Sopenharmony_ci	pr_err("gluebi (pid %d): %s: " fmt "\n",            \
328c2ecf20Sopenharmony_ci	       current->pid, __func__, ##__VA_ARGS__)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/**
358c2ecf20Sopenharmony_ci * struct gluebi_device - a gluebi device description data structure.
368c2ecf20Sopenharmony_ci * @mtd: emulated MTD device description object
378c2ecf20Sopenharmony_ci * @refcnt: gluebi device reference count
388c2ecf20Sopenharmony_ci * @desc: UBI volume descriptor
398c2ecf20Sopenharmony_ci * @ubi_num: UBI device number this gluebi device works on
408c2ecf20Sopenharmony_ci * @vol_id: ID of UBI volume this gluebi device works on
418c2ecf20Sopenharmony_ci * @list: link in a list of gluebi devices
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_cistruct gluebi_device {
448c2ecf20Sopenharmony_ci	struct mtd_info mtd;
458c2ecf20Sopenharmony_ci	int refcnt;
468c2ecf20Sopenharmony_ci	struct ubi_volume_desc *desc;
478c2ecf20Sopenharmony_ci	int ubi_num;
488c2ecf20Sopenharmony_ci	int vol_id;
498c2ecf20Sopenharmony_ci	struct list_head list;
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* List of all gluebi devices */
538c2ecf20Sopenharmony_cistatic LIST_HEAD(gluebi_devices);
548c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(devices_mutex);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/**
578c2ecf20Sopenharmony_ci * find_gluebi_nolock - find a gluebi device.
588c2ecf20Sopenharmony_ci * @ubi_num: UBI device number
598c2ecf20Sopenharmony_ci * @vol_id: volume ID
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci * This function seraches for gluebi device corresponding to UBI device
628c2ecf20Sopenharmony_ci * @ubi_num and UBI volume @vol_id. Returns the gluebi device description
638c2ecf20Sopenharmony_ci * object in case of success and %NULL in case of failure. The caller has to
648c2ecf20Sopenharmony_ci * have the &devices_mutex locked.
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistatic struct gluebi_device *find_gluebi_nolock(int ubi_num, int vol_id)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct gluebi_device *gluebi;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	list_for_each_entry(gluebi, &gluebi_devices, list)
718c2ecf20Sopenharmony_ci		if (gluebi->ubi_num == ubi_num && gluebi->vol_id == vol_id)
728c2ecf20Sopenharmony_ci			return gluebi;
738c2ecf20Sopenharmony_ci	return NULL;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/**
778c2ecf20Sopenharmony_ci * gluebi_get_device - get MTD device reference.
788c2ecf20Sopenharmony_ci * @mtd: the MTD device description object
798c2ecf20Sopenharmony_ci *
808c2ecf20Sopenharmony_ci * This function is called every time the MTD device is being opened and
818c2ecf20Sopenharmony_ci * implements the MTD get_device() operation. Returns zero in case of success
828c2ecf20Sopenharmony_ci * and a negative error code in case of failure.
838c2ecf20Sopenharmony_ci */
848c2ecf20Sopenharmony_cistatic int gluebi_get_device(struct mtd_info *mtd)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct gluebi_device *gluebi;
878c2ecf20Sopenharmony_ci	int ubi_mode = UBI_READONLY;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (mtd->flags & MTD_WRITEABLE)
908c2ecf20Sopenharmony_ci		ubi_mode = UBI_READWRITE;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	gluebi = container_of(mtd, struct gluebi_device, mtd);
938c2ecf20Sopenharmony_ci	mutex_lock(&devices_mutex);
948c2ecf20Sopenharmony_ci	if (gluebi->refcnt > 0) {
958c2ecf20Sopenharmony_ci		/*
968c2ecf20Sopenharmony_ci		 * The MTD device is already referenced and this is just one
978c2ecf20Sopenharmony_ci		 * more reference. MTD allows many users to open the same
988c2ecf20Sopenharmony_ci		 * volume simultaneously and do not distinguish between
998c2ecf20Sopenharmony_ci		 * readers/writers/exclusive/meta openers as UBI does. So we do
1008c2ecf20Sopenharmony_ci		 * not open the UBI volume again - just increase the reference
1018c2ecf20Sopenharmony_ci		 * counter and return.
1028c2ecf20Sopenharmony_ci		 */
1038c2ecf20Sopenharmony_ci		gluebi->refcnt += 1;
1048c2ecf20Sopenharmony_ci		mutex_unlock(&devices_mutex);
1058c2ecf20Sopenharmony_ci		return 0;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/*
1098c2ecf20Sopenharmony_ci	 * This is the first reference to this UBI volume via the MTD device
1108c2ecf20Sopenharmony_ci	 * interface. Open the corresponding volume in read-write mode.
1118c2ecf20Sopenharmony_ci	 */
1128c2ecf20Sopenharmony_ci	gluebi->desc = ubi_open_volume(gluebi->ubi_num, gluebi->vol_id,
1138c2ecf20Sopenharmony_ci				       ubi_mode);
1148c2ecf20Sopenharmony_ci	if (IS_ERR(gluebi->desc)) {
1158c2ecf20Sopenharmony_ci		mutex_unlock(&devices_mutex);
1168c2ecf20Sopenharmony_ci		return PTR_ERR(gluebi->desc);
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	gluebi->refcnt += 1;
1198c2ecf20Sopenharmony_ci	mutex_unlock(&devices_mutex);
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/**
1248c2ecf20Sopenharmony_ci * gluebi_put_device - put MTD device reference.
1258c2ecf20Sopenharmony_ci * @mtd: the MTD device description object
1268c2ecf20Sopenharmony_ci *
1278c2ecf20Sopenharmony_ci * This function is called every time the MTD device is being put. Returns
1288c2ecf20Sopenharmony_ci * zero in case of success and a negative error code in case of failure.
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_cistatic void gluebi_put_device(struct mtd_info *mtd)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct gluebi_device *gluebi;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	gluebi = container_of(mtd, struct gluebi_device, mtd);
1358c2ecf20Sopenharmony_ci	mutex_lock(&devices_mutex);
1368c2ecf20Sopenharmony_ci	gluebi->refcnt -= 1;
1378c2ecf20Sopenharmony_ci	if (gluebi->refcnt == 0)
1388c2ecf20Sopenharmony_ci		ubi_close_volume(gluebi->desc);
1398c2ecf20Sopenharmony_ci	mutex_unlock(&devices_mutex);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/**
1438c2ecf20Sopenharmony_ci * gluebi_read - read operation of emulated MTD devices.
1448c2ecf20Sopenharmony_ci * @mtd: MTD device description object
1458c2ecf20Sopenharmony_ci * @from: absolute offset from where to read
1468c2ecf20Sopenharmony_ci * @len: how many bytes to read
1478c2ecf20Sopenharmony_ci * @retlen: count of read bytes is returned here
1488c2ecf20Sopenharmony_ci * @buf: buffer to store the read data
1498c2ecf20Sopenharmony_ci *
1508c2ecf20Sopenharmony_ci * This function returns zero in case of success and a negative error code in
1518c2ecf20Sopenharmony_ci * case of failure.
1528c2ecf20Sopenharmony_ci */
1538c2ecf20Sopenharmony_cistatic int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len,
1548c2ecf20Sopenharmony_ci		       size_t *retlen, unsigned char *buf)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	int err = 0, lnum, offs, bytes_left;
1578c2ecf20Sopenharmony_ci	struct gluebi_device *gluebi;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	gluebi = container_of(mtd, struct gluebi_device, mtd);
1608c2ecf20Sopenharmony_ci	lnum = div_u64_rem(from, mtd->erasesize, &offs);
1618c2ecf20Sopenharmony_ci	bytes_left = len;
1628c2ecf20Sopenharmony_ci	while (bytes_left) {
1638c2ecf20Sopenharmony_ci		size_t to_read = mtd->erasesize - offs;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		if (to_read > bytes_left)
1668c2ecf20Sopenharmony_ci			to_read = bytes_left;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		err = ubi_read(gluebi->desc, lnum, buf, offs, to_read);
1698c2ecf20Sopenharmony_ci		if (err)
1708c2ecf20Sopenharmony_ci			break;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		lnum += 1;
1738c2ecf20Sopenharmony_ci		offs = 0;
1748c2ecf20Sopenharmony_ci		bytes_left -= to_read;
1758c2ecf20Sopenharmony_ci		buf += to_read;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	*retlen = len - bytes_left;
1798c2ecf20Sopenharmony_ci	return err;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/**
1838c2ecf20Sopenharmony_ci * gluebi_write - write operation of emulated MTD devices.
1848c2ecf20Sopenharmony_ci * @mtd: MTD device description object
1858c2ecf20Sopenharmony_ci * @to: absolute offset where to write
1868c2ecf20Sopenharmony_ci * @len: how many bytes to write
1878c2ecf20Sopenharmony_ci * @retlen: count of written bytes is returned here
1888c2ecf20Sopenharmony_ci * @buf: buffer with data to write
1898c2ecf20Sopenharmony_ci *
1908c2ecf20Sopenharmony_ci * This function returns zero in case of success and a negative error code in
1918c2ecf20Sopenharmony_ci * case of failure.
1928c2ecf20Sopenharmony_ci */
1938c2ecf20Sopenharmony_cistatic int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len,
1948c2ecf20Sopenharmony_ci			size_t *retlen, const u_char *buf)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	int err = 0, lnum, offs, bytes_left;
1978c2ecf20Sopenharmony_ci	struct gluebi_device *gluebi;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	gluebi = container_of(mtd, struct gluebi_device, mtd);
2008c2ecf20Sopenharmony_ci	lnum = div_u64_rem(to, mtd->erasesize, &offs);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (len % mtd->writesize || offs % mtd->writesize)
2038c2ecf20Sopenharmony_ci		return -EINVAL;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	bytes_left = len;
2068c2ecf20Sopenharmony_ci	while (bytes_left) {
2078c2ecf20Sopenharmony_ci		size_t to_write = mtd->erasesize - offs;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		if (to_write > bytes_left)
2108c2ecf20Sopenharmony_ci			to_write = bytes_left;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		err = ubi_leb_write(gluebi->desc, lnum, buf, offs, to_write);
2138c2ecf20Sopenharmony_ci		if (err)
2148c2ecf20Sopenharmony_ci			break;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		lnum += 1;
2178c2ecf20Sopenharmony_ci		offs = 0;
2188c2ecf20Sopenharmony_ci		bytes_left -= to_write;
2198c2ecf20Sopenharmony_ci		buf += to_write;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	*retlen = len - bytes_left;
2238c2ecf20Sopenharmony_ci	return err;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci/**
2278c2ecf20Sopenharmony_ci * gluebi_erase - erase operation of emulated MTD devices.
2288c2ecf20Sopenharmony_ci * @mtd: the MTD device description object
2298c2ecf20Sopenharmony_ci * @instr: the erase operation description
2308c2ecf20Sopenharmony_ci *
2318c2ecf20Sopenharmony_ci * This function calls the erase callback when finishes. Returns zero in case
2328c2ecf20Sopenharmony_ci * of success and a negative error code in case of failure.
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_cistatic int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	int err, i, lnum, count;
2378c2ecf20Sopenharmony_ci	struct gluebi_device *gluebi;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd))
2408c2ecf20Sopenharmony_ci		return -EINVAL;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	lnum = mtd_div_by_eb(instr->addr, mtd);
2438c2ecf20Sopenharmony_ci	count = mtd_div_by_eb(instr->len, mtd);
2448c2ecf20Sopenharmony_ci	gluebi = container_of(mtd, struct gluebi_device, mtd);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	for (i = 0; i < count - 1; i++) {
2478c2ecf20Sopenharmony_ci		err = ubi_leb_unmap(gluebi->desc, lnum + i);
2488c2ecf20Sopenharmony_ci		if (err)
2498c2ecf20Sopenharmony_ci			goto out_err;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci	/*
2528c2ecf20Sopenharmony_ci	 * MTD erase operations are synchronous, so we have to make sure the
2538c2ecf20Sopenharmony_ci	 * physical eraseblock is wiped out.
2548c2ecf20Sopenharmony_ci	 *
2558c2ecf20Sopenharmony_ci	 * Thus, perform leb_erase instead of leb_unmap operation - leb_erase
2568c2ecf20Sopenharmony_ci	 * will wait for the end of operations
2578c2ecf20Sopenharmony_ci	 */
2588c2ecf20Sopenharmony_ci	err = ubi_leb_erase(gluebi->desc, lnum + i);
2598c2ecf20Sopenharmony_ci	if (err)
2608c2ecf20Sopenharmony_ci		goto out_err;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return 0;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ciout_err:
2658c2ecf20Sopenharmony_ci	instr->fail_addr = (long long)lnum * mtd->erasesize;
2668c2ecf20Sopenharmony_ci	return err;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci/**
2708c2ecf20Sopenharmony_ci * gluebi_create - create a gluebi device for an UBI volume.
2718c2ecf20Sopenharmony_ci * @di: UBI device description object
2728c2ecf20Sopenharmony_ci * @vi: UBI volume description object
2738c2ecf20Sopenharmony_ci *
2748c2ecf20Sopenharmony_ci * This function is called when a new UBI volume is created in order to create
2758c2ecf20Sopenharmony_ci * corresponding fake MTD device. Returns zero in case of success and a
2768c2ecf20Sopenharmony_ci * negative error code in case of failure.
2778c2ecf20Sopenharmony_ci */
2788c2ecf20Sopenharmony_cistatic int gluebi_create(struct ubi_device_info *di,
2798c2ecf20Sopenharmony_ci			 struct ubi_volume_info *vi)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct gluebi_device *gluebi, *g;
2828c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	gluebi = kzalloc(sizeof(struct gluebi_device), GFP_KERNEL);
2858c2ecf20Sopenharmony_ci	if (!gluebi)
2868c2ecf20Sopenharmony_ci		return -ENOMEM;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	mtd = &gluebi->mtd;
2898c2ecf20Sopenharmony_ci	mtd->name = kmemdup(vi->name, vi->name_len + 1, GFP_KERNEL);
2908c2ecf20Sopenharmony_ci	if (!mtd->name) {
2918c2ecf20Sopenharmony_ci		kfree(gluebi);
2928c2ecf20Sopenharmony_ci		return -ENOMEM;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	gluebi->vol_id = vi->vol_id;
2968c2ecf20Sopenharmony_ci	gluebi->ubi_num = vi->ubi_num;
2978c2ecf20Sopenharmony_ci	mtd->type = MTD_UBIVOLUME;
2988c2ecf20Sopenharmony_ci	if (!di->ro_mode)
2998c2ecf20Sopenharmony_ci		mtd->flags = MTD_WRITEABLE;
3008c2ecf20Sopenharmony_ci	mtd->owner      = THIS_MODULE;
3018c2ecf20Sopenharmony_ci	mtd->writesize  = di->min_io_size;
3028c2ecf20Sopenharmony_ci	mtd->erasesize  = vi->usable_leb_size;
3038c2ecf20Sopenharmony_ci	mtd->_read       = gluebi_read;
3048c2ecf20Sopenharmony_ci	mtd->_write      = gluebi_write;
3058c2ecf20Sopenharmony_ci	mtd->_erase      = gluebi_erase;
3068c2ecf20Sopenharmony_ci	mtd->_get_device = gluebi_get_device;
3078c2ecf20Sopenharmony_ci	mtd->_put_device = gluebi_put_device;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/*
3108c2ecf20Sopenharmony_ci	 * In case of dynamic a volume, MTD device size is just volume size. In
3118c2ecf20Sopenharmony_ci	 * case of a static volume the size is equivalent to the amount of data
3128c2ecf20Sopenharmony_ci	 * bytes.
3138c2ecf20Sopenharmony_ci	 */
3148c2ecf20Sopenharmony_ci	if (vi->vol_type == UBI_DYNAMIC_VOLUME)
3158c2ecf20Sopenharmony_ci		mtd->size = (unsigned long long)vi->usable_leb_size * vi->size;
3168c2ecf20Sopenharmony_ci	else
3178c2ecf20Sopenharmony_ci		mtd->size = vi->used_bytes;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* Just a sanity check - make sure this gluebi device does not exist */
3208c2ecf20Sopenharmony_ci	mutex_lock(&devices_mutex);
3218c2ecf20Sopenharmony_ci	g = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
3228c2ecf20Sopenharmony_ci	if (g)
3238c2ecf20Sopenharmony_ci		err_msg("gluebi MTD device %d form UBI device %d volume %d already exists",
3248c2ecf20Sopenharmony_ci			g->mtd.index, vi->ubi_num, vi->vol_id);
3258c2ecf20Sopenharmony_ci	mutex_unlock(&devices_mutex);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (mtd_device_register(mtd, NULL, 0)) {
3288c2ecf20Sopenharmony_ci		err_msg("cannot add MTD device");
3298c2ecf20Sopenharmony_ci		kfree(mtd->name);
3308c2ecf20Sopenharmony_ci		kfree(gluebi);
3318c2ecf20Sopenharmony_ci		return -ENFILE;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	mutex_lock(&devices_mutex);
3358c2ecf20Sopenharmony_ci	list_add_tail(&gluebi->list, &gluebi_devices);
3368c2ecf20Sopenharmony_ci	mutex_unlock(&devices_mutex);
3378c2ecf20Sopenharmony_ci	return 0;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci/**
3418c2ecf20Sopenharmony_ci * gluebi_remove - remove a gluebi device.
3428c2ecf20Sopenharmony_ci * @vi: UBI volume description object
3438c2ecf20Sopenharmony_ci *
3448c2ecf20Sopenharmony_ci * This function is called when an UBI volume is removed and it removes
3458c2ecf20Sopenharmony_ci * corresponding fake MTD device. Returns zero in case of success and a
3468c2ecf20Sopenharmony_ci * negative error code in case of failure.
3478c2ecf20Sopenharmony_ci */
3488c2ecf20Sopenharmony_cistatic int gluebi_remove(struct ubi_volume_info *vi)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	int err = 0;
3518c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
3528c2ecf20Sopenharmony_ci	struct gluebi_device *gluebi;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	mutex_lock(&devices_mutex);
3558c2ecf20Sopenharmony_ci	gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
3568c2ecf20Sopenharmony_ci	if (!gluebi) {
3578c2ecf20Sopenharmony_ci		err_msg("got remove notification for unknown UBI device %d volume %d",
3588c2ecf20Sopenharmony_ci			vi->ubi_num, vi->vol_id);
3598c2ecf20Sopenharmony_ci		err = -ENOENT;
3608c2ecf20Sopenharmony_ci	} else if (gluebi->refcnt)
3618c2ecf20Sopenharmony_ci		err = -EBUSY;
3628c2ecf20Sopenharmony_ci	else
3638c2ecf20Sopenharmony_ci		list_del(&gluebi->list);
3648c2ecf20Sopenharmony_ci	mutex_unlock(&devices_mutex);
3658c2ecf20Sopenharmony_ci	if (err)
3668c2ecf20Sopenharmony_ci		return err;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	mtd = &gluebi->mtd;
3698c2ecf20Sopenharmony_ci	err = mtd_device_unregister(mtd);
3708c2ecf20Sopenharmony_ci	if (err) {
3718c2ecf20Sopenharmony_ci		err_msg("cannot remove fake MTD device %d, UBI device %d, volume %d, error %d",
3728c2ecf20Sopenharmony_ci			mtd->index, gluebi->ubi_num, gluebi->vol_id, err);
3738c2ecf20Sopenharmony_ci		mutex_lock(&devices_mutex);
3748c2ecf20Sopenharmony_ci		list_add_tail(&gluebi->list, &gluebi_devices);
3758c2ecf20Sopenharmony_ci		mutex_unlock(&devices_mutex);
3768c2ecf20Sopenharmony_ci		return err;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	kfree(mtd->name);
3808c2ecf20Sopenharmony_ci	kfree(gluebi);
3818c2ecf20Sopenharmony_ci	return 0;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci/**
3858c2ecf20Sopenharmony_ci * gluebi_updated - UBI volume was updated notifier.
3868c2ecf20Sopenharmony_ci * @vi: volume info structure
3878c2ecf20Sopenharmony_ci *
3888c2ecf20Sopenharmony_ci * This function is called every time an UBI volume is updated. It does nothing
3898c2ecf20Sopenharmony_ci * if te volume @vol is dynamic, and changes MTD device size if the
3908c2ecf20Sopenharmony_ci * volume is static. This is needed because static volumes cannot be read past
3918c2ecf20Sopenharmony_ci * data they contain. This function returns zero in case of success and a
3928c2ecf20Sopenharmony_ci * negative error code in case of error.
3938c2ecf20Sopenharmony_ci */
3948c2ecf20Sopenharmony_cistatic int gluebi_updated(struct ubi_volume_info *vi)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct gluebi_device *gluebi;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	mutex_lock(&devices_mutex);
3998c2ecf20Sopenharmony_ci	gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
4008c2ecf20Sopenharmony_ci	if (!gluebi) {
4018c2ecf20Sopenharmony_ci		mutex_unlock(&devices_mutex);
4028c2ecf20Sopenharmony_ci		err_msg("got update notification for unknown UBI device %d volume %d",
4038c2ecf20Sopenharmony_ci			vi->ubi_num, vi->vol_id);
4048c2ecf20Sopenharmony_ci		return -ENOENT;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	if (vi->vol_type == UBI_STATIC_VOLUME)
4088c2ecf20Sopenharmony_ci		gluebi->mtd.size = vi->used_bytes;
4098c2ecf20Sopenharmony_ci	mutex_unlock(&devices_mutex);
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci/**
4148c2ecf20Sopenharmony_ci * gluebi_resized - UBI volume was re-sized notifier.
4158c2ecf20Sopenharmony_ci * @vi: volume info structure
4168c2ecf20Sopenharmony_ci *
4178c2ecf20Sopenharmony_ci * This function is called every time an UBI volume is re-size. It changes the
4188c2ecf20Sopenharmony_ci * corresponding fake MTD device size. This function returns zero in case of
4198c2ecf20Sopenharmony_ci * success and a negative error code in case of error.
4208c2ecf20Sopenharmony_ci */
4218c2ecf20Sopenharmony_cistatic int gluebi_resized(struct ubi_volume_info *vi)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct gluebi_device *gluebi;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	mutex_lock(&devices_mutex);
4268c2ecf20Sopenharmony_ci	gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
4278c2ecf20Sopenharmony_ci	if (!gluebi) {
4288c2ecf20Sopenharmony_ci		mutex_unlock(&devices_mutex);
4298c2ecf20Sopenharmony_ci		err_msg("got update notification for unknown UBI device %d volume %d",
4308c2ecf20Sopenharmony_ci			vi->ubi_num, vi->vol_id);
4318c2ecf20Sopenharmony_ci		return -ENOENT;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci	gluebi->mtd.size = vi->used_bytes;
4348c2ecf20Sopenharmony_ci	mutex_unlock(&devices_mutex);
4358c2ecf20Sopenharmony_ci	return 0;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci/**
4398c2ecf20Sopenharmony_ci * gluebi_notify - UBI notification handler.
4408c2ecf20Sopenharmony_ci * @nb: registered notifier block
4418c2ecf20Sopenharmony_ci * @l: notification type
4428c2ecf20Sopenharmony_ci * @ptr: pointer to the &struct ubi_notification object
4438c2ecf20Sopenharmony_ci */
4448c2ecf20Sopenharmony_cistatic int gluebi_notify(struct notifier_block *nb, unsigned long l,
4458c2ecf20Sopenharmony_ci			 void *ns_ptr)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	struct ubi_notification *nt = ns_ptr;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	switch (l) {
4508c2ecf20Sopenharmony_ci	case UBI_VOLUME_ADDED:
4518c2ecf20Sopenharmony_ci		gluebi_create(&nt->di, &nt->vi);
4528c2ecf20Sopenharmony_ci		break;
4538c2ecf20Sopenharmony_ci	case UBI_VOLUME_REMOVED:
4548c2ecf20Sopenharmony_ci		gluebi_remove(&nt->vi);
4558c2ecf20Sopenharmony_ci		break;
4568c2ecf20Sopenharmony_ci	case UBI_VOLUME_RESIZED:
4578c2ecf20Sopenharmony_ci		gluebi_resized(&nt->vi);
4588c2ecf20Sopenharmony_ci		break;
4598c2ecf20Sopenharmony_ci	case UBI_VOLUME_UPDATED:
4608c2ecf20Sopenharmony_ci		gluebi_updated(&nt->vi);
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci	default:
4638c2ecf20Sopenharmony_ci		break;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci	return NOTIFY_OK;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic struct notifier_block gluebi_notifier = {
4698c2ecf20Sopenharmony_ci	.notifier_call	= gluebi_notify,
4708c2ecf20Sopenharmony_ci};
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic int __init ubi_gluebi_init(void)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	return ubi_register_volume_notifier(&gluebi_notifier, 0);
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic void __exit ubi_gluebi_exit(void)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	struct gluebi_device *gluebi, *g;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	list_for_each_entry_safe(gluebi, g, &gluebi_devices, list) {
4828c2ecf20Sopenharmony_ci		int err;
4838c2ecf20Sopenharmony_ci		struct mtd_info *mtd = &gluebi->mtd;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		err = mtd_device_unregister(mtd);
4868c2ecf20Sopenharmony_ci		if (err)
4878c2ecf20Sopenharmony_ci			err_msg("error %d while removing gluebi MTD device %d, UBI device %d, volume %d - ignoring",
4888c2ecf20Sopenharmony_ci				err, mtd->index, gluebi->ubi_num,
4898c2ecf20Sopenharmony_ci				gluebi->vol_id);
4908c2ecf20Sopenharmony_ci		kfree(mtd->name);
4918c2ecf20Sopenharmony_ci		kfree(gluebi);
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci	ubi_unregister_volume_notifier(&gluebi_notifier);
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cimodule_init(ubi_gluebi_init);
4978c2ecf20Sopenharmony_cimodule_exit(ubi_gluebi_exit);
4988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD emulation layer over UBI volumes");
4998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Artem Bityutskiy, Joern Engel");
5008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
501