1801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy/* 2801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * Copyright (c) International Business Machines Corp., 2006 3801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 4801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * This program is free software; you can redistribute it and/or modify 5801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * it under the terms of the GNU General Public License as published by 6801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * the Free Software Foundation; either version 2 of the License, or 7801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * (at your option) any later version. 8801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 9801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * This program is distributed in the hope that it will be useful, 10801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * but WITHOUT ANY WARRANTY; without even the implied warranty of 11801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 12801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * the GNU General Public License for more details. 13801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 14801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * You should have received a copy of the GNU General Public License 15801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * along with this program; if not, write to the Free Software 16801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 18801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * Author: Artem Bityutskiy (Битюцкий Артём), Joern Engel 19801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 20801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 21801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy/* 222ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * This is a small driver which implements fake MTD devices on top of UBI 232ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * volumes. This sounds strange, but it is in fact quite useful to make 242ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * MTD-oriented software (including all the legacy software) work on top of 252ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * UBI. 26801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 27801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit 282ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * size (@mtd->writesize) is equivalent to the UBI minimal I/O unit. The 29801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * eraseblock size is equivalent to the logical eraseblock size of the volume. 30801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 31801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 322ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin#include <linux/err.h> 332ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin#include <linux/list.h> 345a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 352ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin#include <linux/sched.h> 363013ee31b6c5fd9a49a81816d6c13e1cdb7a1288Artem Bityutskiy#include <linux/math64.h> 372ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin#include <linux/module.h> 382ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin#include <linux/mutex.h> 392ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin#include <linux/mtd/ubi.h> 402ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin#include <linux/mtd/mtd.h> 412ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin#include "ubi-media.h" 422ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 432ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin#define err_msg(fmt, ...) \ 442ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin printk(KERN_DEBUG "gluebi (pid %d): %s: " fmt "\n", \ 452ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin current->pid, __func__, ##__VA_ARGS__) 462ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 472ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin/** 482ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * struct gluebi_device - a gluebi device description data structure. 492ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @mtd: emulated MTD device description object 502ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @refcnt: gluebi device reference count 512ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @desc: UBI volume descriptor 522ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @ubi_num: UBI device number this gluebi device works on 532ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @vol_id: ID of UBI volume this gluebi device works on 542ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @list: link in a list of gluebi devices 552ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin */ 562ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstruct gluebi_device { 572ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct mtd_info mtd; 582ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin int refcnt; 592ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct ubi_volume_desc *desc; 602ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin int ubi_num; 612ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin int vol_id; 622ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct list_head list; 632ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin}; 642ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 652ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin/* List of all gluebi devices */ 662ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstatic LIST_HEAD(gluebi_devices); 672ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstatic DEFINE_MUTEX(devices_mutex); 682ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 692ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin/** 702ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * find_gluebi_nolock - find a gluebi device. 712ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @ubi_num: UBI device number 722ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @vol_id: volume ID 732ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * 742ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * This function seraches for gluebi device corresponding to UBI device 752ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @ubi_num and UBI volume @vol_id. Returns the gluebi device description 762ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * object in case of success and %NULL in case of failure. The caller has to 772ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * have the &devices_mutex locked. 782ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin */ 792ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstatic struct gluebi_device *find_gluebi_nolock(int ubi_num, int vol_id) 802ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin{ 812ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct gluebi_device *gluebi; 822ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 832ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin list_for_each_entry(gluebi, &gluebi_devices, list) 842ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (gluebi->ubi_num == ubi_num && gluebi->vol_id == vol_id) 852ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return gluebi; 862ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return NULL; 872ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin} 88801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 89801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy/** 90801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * gluebi_get_device - get MTD device reference. 91801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @mtd: the MTD device description object 92801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 93801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * This function is called every time the MTD device is being opened and 94801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * implements the MTD get_device() operation. Returns zero in case of success 95801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * and a negative error code in case of failure. 96801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 97801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiystatic int gluebi_get_device(struct mtd_info *mtd) 98801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy{ 992ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct gluebi_device *gluebi; 1002ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin int ubi_mode = UBI_READONLY; 101801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 1022ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (!try_module_get(THIS_MODULE)) 1032ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return -ENODEV; 104801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 1052ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (mtd->flags & MTD_WRITEABLE) 1062ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin ubi_mode = UBI_READWRITE; 1072ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 1082ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi = container_of(mtd, struct gluebi_device, mtd); 1092ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_lock(&devices_mutex); 1102ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (gluebi->refcnt > 0) { 111801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy /* 112801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * The MTD device is already referenced and this is just one 113801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * more reference. MTD allows many users to open the same 114801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * volume simultaneously and do not distinguish between 115801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * readers/writers/exclusive openers as UBI does. So we do not 116801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * open the UBI volume again - just increase the reference 117801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * counter and return. 118801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 1192ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi->refcnt += 1; 1202ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 121801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return 0; 122801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy } 123801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 124801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy /* 125801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * This is the first reference to this UBI volume via the MTD device 126801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * interface. Open the corresponding volume in read-write mode. 127801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 1282ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi->desc = ubi_open_volume(gluebi->ubi_num, gluebi->vol_id, 1292ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin ubi_mode); 1302ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (IS_ERR(gluebi->desc)) { 1312ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 1322ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin module_put(THIS_MODULE); 1332ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return PTR_ERR(gluebi->desc); 1342ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin } 1352ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi->refcnt += 1; 1362ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 137801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return 0; 138801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy} 139801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 140801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy/** 141801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * gluebi_put_device - put MTD device reference. 142801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @mtd: the MTD device description object 143801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 144801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * This function is called every time the MTD device is being put. Returns 145801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * zero in case of success and a negative error code in case of failure. 146801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 147801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiystatic void gluebi_put_device(struct mtd_info *mtd) 148801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy{ 1492ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct gluebi_device *gluebi; 1502ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 1512ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi = container_of(mtd, struct gluebi_device, mtd); 1522ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_lock(&devices_mutex); 1532ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi->refcnt -= 1; 1542ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (gluebi->refcnt == 0) 1552ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin ubi_close_volume(gluebi->desc); 1562ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin module_put(THIS_MODULE); 1572ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 158801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy} 159801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 160801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy/** 161801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * gluebi_read - read operation of emulated MTD devices. 162801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @mtd: MTD device description object 163801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @from: absolute offset from where to read 164801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @len: how many bytes to read 165801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @retlen: count of read bytes is returned here 166801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @buf: buffer to store the read data 167801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 168801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * This function returns zero in case of success and a negative error code in 169801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * case of failure. 170801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 171801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiystatic int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, 172801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy size_t *retlen, unsigned char *buf) 173801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy{ 174801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy int err = 0, lnum, offs, total_read; 1752ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct gluebi_device *gluebi; 176801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 177801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy if (len < 0 || from < 0 || from + len > mtd->size) 178801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return -EINVAL; 179801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 1802ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi = container_of(mtd, struct gluebi_device, mtd); 181801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 1823013ee31b6c5fd9a49a81816d6c13e1cdb7a1288Artem Bityutskiy lnum = div_u64_rem(from, mtd->erasesize, &offs); 183801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy total_read = len; 184801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy while (total_read) { 185801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy size_t to_read = mtd->erasesize - offs; 186801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 187801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy if (to_read > total_read) 188801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy to_read = total_read; 189801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 1902ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err = ubi_read(gluebi->desc, lnum, buf, offs, to_read); 191801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy if (err) 192801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy break; 193801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 194801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy lnum += 1; 195801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy offs = 0; 196801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy total_read -= to_read; 197801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy buf += to_read; 198801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy } 199801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 200801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy *retlen = len - total_read; 201801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return err; 202801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy} 203801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 204801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy/** 205801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * gluebi_write - write operation of emulated MTD devices. 206801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @mtd: MTD device description object 207801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @to: absolute offset where to write 208801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @len: how many bytes to write 209801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @retlen: count of written bytes is returned here 210801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @buf: buffer with data to write 211801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 212801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * This function returns zero in case of success and a negative error code in 213801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * case of failure. 214801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 215801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiystatic int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, 2162ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin size_t *retlen, const u_char *buf) 217801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy{ 218801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy int err = 0, lnum, offs, total_written; 2192ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct gluebi_device *gluebi; 220801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 221801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy if (len < 0 || to < 0 || len + to > mtd->size) 222801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return -EINVAL; 223801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 2242ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi = container_of(mtd, struct gluebi_device, mtd); 225801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 2262ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (!(mtd->flags & MTD_WRITEABLE)) 227801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return -EROFS; 228801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 2293013ee31b6c5fd9a49a81816d6c13e1cdb7a1288Artem Bityutskiy lnum = div_u64_rem(to, mtd->erasesize, &offs); 230801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 231801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy if (len % mtd->writesize || offs % mtd->writesize) 232801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return -EINVAL; 233801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 234801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy total_written = len; 235801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy while (total_written) { 236801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy size_t to_write = mtd->erasesize - offs; 237801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 238801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy if (to_write > total_written) 239801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy to_write = total_written; 240801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 2412ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err = ubi_write(gluebi->desc, lnum, buf, offs, to_write); 242801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy if (err) 243801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy break; 244801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 245801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy lnum += 1; 246801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy offs = 0; 247801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy total_written -= to_write; 248801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy buf += to_write; 249801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy } 250801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 251801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy *retlen = len - total_written; 252801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return err; 253801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy} 254801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 255801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy/** 256801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * gluebi_erase - erase operation of emulated MTD devices. 257801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @mtd: the MTD device description object 258801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * @instr: the erase operation description 259801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 260801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * This function calls the erase callback when finishes. Returns zero in case 261801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * of success and a negative error code in case of failure. 262801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 263801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiystatic int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) 264801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy{ 265801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy int err, i, lnum, count; 2662ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct gluebi_device *gluebi; 267801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 268801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize) 269801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return -EINVAL; 270801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy if (instr->len < 0 || instr->addr + instr->len > mtd->size) 271801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return -EINVAL; 27269423d99fc182a81f3c5db3eb5c140acc6fc64beAdrian Hunter if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd)) 273801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return -EINVAL; 274801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 27569423d99fc182a81f3c5db3eb5c140acc6fc64beAdrian Hunter lnum = mtd_div_by_eb(instr->addr, mtd); 27669423d99fc182a81f3c5db3eb5c140acc6fc64beAdrian Hunter count = mtd_div_by_eb(instr->len, mtd); 277801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 2782ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi = container_of(mtd, struct gluebi_device, mtd); 279801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 2802ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (!(mtd->flags & MTD_WRITEABLE)) 281801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return -EROFS; 282801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 2832ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin for (i = 0; i < count - 1; i++) { 2842ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err = ubi_leb_unmap(gluebi->desc, lnum + i); 285801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy if (err) 286801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy goto out_err; 287801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy } 288801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy /* 289801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * MTD erase operations are synchronous, so we have to make sure the 290801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * physical eraseblock is wiped out. 2912ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * 2922ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * Thus, perform leb_erase instead of leb_unmap operation - leb_erase 2932ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * will wait for the end of operations 294801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 2952ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err = ubi_leb_erase(gluebi->desc, lnum + i); 296801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy if (err) 297801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy goto out_err; 298801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 2999c9ec147709e63e4e8ac6a037c6bb50688ff8e9cArtem Bityutskiy instr->state = MTD_ERASE_DONE; 3009c9ec147709e63e4e8ac6a037c6bb50688ff8e9cArtem Bityutskiy mtd_erase_callback(instr); 301801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return 0; 302801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 303801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiyout_err: 304801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy instr->state = MTD_ERASE_FAILED; 30569423d99fc182a81f3c5db3eb5c140acc6fc64beAdrian Hunter instr->fail_addr = (long long)lnum * mtd->erasesize; 306801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return err; 307801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy} 308801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 309801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy/** 3102ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * gluebi_create - create a gluebi device for an UBI volume. 3112ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @di: UBI device description object 3122ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @vi: UBI volume description object 313801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 3142ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * This function is called when a new UBI volume is created in order to create 315801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * corresponding fake MTD device. Returns zero in case of success and a 316801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * negative error code in case of failure. 317801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 3182ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstatic int gluebi_create(struct ubi_device_info *di, 3192ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct ubi_volume_info *vi) 320801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy{ 3212ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct gluebi_device *gluebi, *g; 3222ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct mtd_info *mtd; 323801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 3242ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi = kzalloc(sizeof(struct gluebi_device), GFP_KERNEL); 3252ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (!gluebi) 326801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return -ENOMEM; 327801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 3282ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mtd = &gluebi->mtd; 3292ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mtd->name = kmemdup(vi->name, vi->name_len + 1, GFP_KERNEL); 3302ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (!mtd->name) { 3312ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin kfree(gluebi); 3322ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return -ENOMEM; 3332ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin } 3342ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 3352ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi->vol_id = vi->vol_id; 336c8cc452501572d4a81331179b00a9bdd5d2bfadaArtem Bityutskiy gluebi->ubi_num = vi->ubi_num; 337801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy mtd->type = MTD_UBIVOLUME; 3382ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (!di->ro_mode) 339801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy mtd->flags = MTD_WRITEABLE; 340801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy mtd->owner = THIS_MODULE; 3412ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mtd->writesize = di->min_io_size; 3422ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mtd->erasesize = vi->usable_leb_size; 343801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy mtd->read = gluebi_read; 344801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy mtd->write = gluebi_write; 345801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy mtd->erase = gluebi_erase; 346801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy mtd->get_device = gluebi_get_device; 347801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy mtd->put_device = gluebi_put_device; 348801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 349941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy /* 3502ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * In case of dynamic a volume, MTD device size is just volume size. In 351941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy * case of a static volume the size is equivalent to the amount of data 352cbd8a9d2cd6f576ca41022599341bbd8be1b0b27Jan Altenberg * bytes. 353941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy */ 3542ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (vi->vol_type == UBI_DYNAMIC_VOLUME) 3552ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mtd->size = (unsigned long long)vi->usable_leb_size * vi->size; 356cbd8a9d2cd6f576ca41022599341bbd8be1b0b27Jan Altenberg else 3572ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mtd->size = vi->used_bytes; 3582ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 3592ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin /* Just a sanity check - make sure this gluebi device does not exist */ 3602ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_lock(&devices_mutex); 3612ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin g = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 3622ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (g) 3632ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err_msg("gluebi MTD device %d form UBI device %d volume %d " 3642ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin "already exists", g->mtd.index, vi->ubi_num, 3652ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin vi->vol_id); 3662ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 367941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy 368ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles if (mtd_device_register(mtd, NULL, 0)) { 3692ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err_msg("cannot add MTD device"); 370801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy kfree(mtd->name); 3712ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin kfree(gluebi); 372801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return -ENFILE; 373801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy } 374801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 3752ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_lock(&devices_mutex); 3762ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin list_add_tail(&gluebi->list, &gluebi_devices); 3772ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 378801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return 0; 379801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy} 380801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 381801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy/** 3822ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * gluebi_remove - remove a gluebi device. 3832ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @vi: UBI volume description object 384801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * 3852ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * This function is called when an UBI volume is removed and it removes 386801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * corresponding fake MTD device. Returns zero in case of success and a 387801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy * negative error code in case of failure. 388801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy */ 3892ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstatic int gluebi_remove(struct ubi_volume_info *vi) 390801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy{ 3912ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin int err = 0; 3922ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct mtd_info *mtd; 3932ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct gluebi_device *gluebi; 3942ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 3952ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_lock(&devices_mutex); 3962ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 3972ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (!gluebi) { 3982ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err_msg("got remove notification for unknown UBI device %d " 3992ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin "volume %d", vi->ubi_num, vi->vol_id); 4002ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err = -ENOENT; 4012ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin } else if (gluebi->refcnt) 4022ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err = -EBUSY; 4032ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin else 4042ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin list_del(&gluebi->list); 4052ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 4062ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (err) 4072ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return err; 408801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy 4092ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mtd = &gluebi->mtd; 410ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles err = mtd_device_unregister(mtd); 4112ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (err) { 4122ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err_msg("cannot remove fake MTD device %d, UBI device %d, " 4132ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin "volume %d, error %d", mtd->index, gluebi->ubi_num, 4142ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi->vol_id, err); 4152ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_lock(&devices_mutex); 4162ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin list_add_tail(&gluebi->list, &gluebi_devices); 4172ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 418801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return err; 4192ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin } 4202ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 421801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy kfree(mtd->name); 4222ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin kfree(gluebi); 423801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy return 0; 424801c135ce73d5df1caf3eca35b66a10824ae0707Artem B. Bityutskiy} 425941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy 426941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy/** 4272ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * gluebi_updated - UBI volume was updated notifier. 4282ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @vi: volume info structure 429941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy * 4302ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * This function is called every time an UBI volume is updated. It does nothing 4312ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * if te volume @vol is dynamic, and changes MTD device size if the 432941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy * volume is static. This is needed because static volumes cannot be read past 4332ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * data they contain. This function returns zero in case of success and a 4342ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * negative error code in case of error. 4352ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin */ 4362ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstatic int gluebi_updated(struct ubi_volume_info *vi) 4372ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin{ 4382ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct gluebi_device *gluebi; 4392ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 4402ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_lock(&devices_mutex); 4412ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 4422ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (!gluebi) { 4432ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 4442ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err_msg("got update notification for unknown UBI device %d " 4452ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin "volume %d", vi->ubi_num, vi->vol_id); 4462ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return -ENOENT; 4472ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin } 4482ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 4492ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (vi->vol_type == UBI_STATIC_VOLUME) 4502ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi->mtd.size = vi->used_bytes; 4512ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 4522ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return 0; 4532ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin} 4542ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 4552ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin/** 4562ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * gluebi_resized - UBI volume was re-sized notifier. 4572ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @vi: volume info structure 4582ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * 4592ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * This function is called every time an UBI volume is re-size. It changes the 4602ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * corresponding fake MTD device size. This function returns zero in case of 4612ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * success and a negative error code in case of error. 4622ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin */ 4632ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstatic int gluebi_resized(struct ubi_volume_info *vi) 4642ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin{ 4652ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct gluebi_device *gluebi; 4662ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 4672ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_lock(&devices_mutex); 4682ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 4692ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (!gluebi) { 4702ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 4712ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err_msg("got update notification for unknown UBI device %d " 4722ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin "volume %d", vi->ubi_num, vi->vol_id); 4732ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return -ENOENT; 4742ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin } 4752ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi->mtd.size = vi->used_bytes; 4762ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mutex_unlock(&devices_mutex); 4772ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return 0; 4782ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin} 4792ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 4802ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin/** 4812ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * gluebi_notify - UBI notification handler. 4822ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @nb: registered notifier block 4832ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @l: notification type 4842ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin * @ptr: pointer to the &struct ubi_notification object 485941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy */ 4862ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstatic int gluebi_notify(struct notifier_block *nb, unsigned long l, 4872ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin void *ns_ptr) 488941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy{ 4892ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct ubi_notification *nt = ns_ptr; 4902ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 4912ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin switch (l) { 4922ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin case UBI_VOLUME_ADDED: 4932ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi_create(&nt->di, &nt->vi); 4942ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin break; 4952ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin case UBI_VOLUME_REMOVED: 4962ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi_remove(&nt->vi); 4972ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin break; 4982ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin case UBI_VOLUME_RESIZED: 4992ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi_resized(&nt->vi); 5002ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin break; 5012ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin case UBI_VOLUME_UPDATED: 5022ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin gluebi_updated(&nt->vi); 5032ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin break; 5042ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin default: 5052ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin break; 5062ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin } 5072ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return NOTIFY_OK; 5082ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin} 509941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy 5102ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstatic struct notifier_block gluebi_notifier = { 5112ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin .notifier_call = gluebi_notify, 5122ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin}; 5132ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 5142ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstatic int __init ubi_gluebi_init(void) 5152ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin{ 5162ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin return ubi_register_volume_notifier(&gluebi_notifier, 0); 517941dfb07ed91451b1c58626a0d258dfdf468b593Artem Bityutskiy} 5182ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 5192ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinstatic void __exit ubi_gluebi_exit(void) 5202ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin{ 5212ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct gluebi_device *gluebi, *g; 5222ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 5232ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin list_for_each_entry_safe(gluebi, g, &gluebi_devices, list) { 5242ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin int err; 5252ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin struct mtd_info *mtd = &gluebi->mtd; 5262ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 527ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles err = mtd_device_unregister(mtd); 5282ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin if (err) 5292ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin err_msg("error %d while removing gluebi MTD device %d, " 5302ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin "UBI device %d, volume %d - ignoring", err, 5312ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin mtd->index, gluebi->ubi_num, gluebi->vol_id); 5322ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin kfree(mtd->name); 5332ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin kfree(gluebi); 5342ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin } 5352ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin ubi_unregister_volume_notifier(&gluebi_notifier); 5362ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin} 5372ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushin 5382ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinmodule_init(ubi_gluebi_init); 5392ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry Pervushinmodule_exit(ubi_gluebi_exit); 5402ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry PervushinMODULE_DESCRIPTION("MTD emulation layer over UBI volumes"); 5412ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry PervushinMODULE_AUTHOR("Artem Bityutskiy, Joern Engel"); 5422ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2baDmitry PervushinMODULE_LICENSE("GPL"); 543