0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019 #include <linux/err.h>
0020 #include <linux/list.h>
0021 #include <linux/slab.h>
0022 #include <linux/sched.h>
0023 #include <linux/math64.h>
0024 #include <linux/module.h>
0025 #include <linux/mutex.h>
0026 #include <linux/mtd/ubi.h>
0027 #include <linux/mtd/mtd.h>
0028 #include "ubi-media.h"
0029
0030 #define err_msg(fmt, ...) \
0031 pr_err("gluebi (pid %d): %s: " fmt "\n", \
0032 current->pid, __func__, ##__VA_ARGS__)
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043 struct gluebi_device {
0044 struct mtd_info mtd;
0045 int refcnt;
0046 struct ubi_volume_desc *desc;
0047 int ubi_num;
0048 int vol_id;
0049 struct list_head list;
0050 };
0051
0052
0053 static LIST_HEAD(gluebi_devices);
0054 static DEFINE_MUTEX(devices_mutex);
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066 static struct gluebi_device *find_gluebi_nolock(int ubi_num, int vol_id)
0067 {
0068 struct gluebi_device *gluebi;
0069
0070 list_for_each_entry(gluebi, &gluebi_devices, list)
0071 if (gluebi->ubi_num == ubi_num && gluebi->vol_id == vol_id)
0072 return gluebi;
0073 return NULL;
0074 }
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084 static int gluebi_get_device(struct mtd_info *mtd)
0085 {
0086 struct gluebi_device *gluebi;
0087 int ubi_mode = UBI_READONLY;
0088
0089 if (mtd->flags & MTD_WRITEABLE)
0090 ubi_mode = UBI_READWRITE;
0091
0092 gluebi = container_of(mtd, struct gluebi_device, mtd);
0093 mutex_lock(&devices_mutex);
0094 if (gluebi->refcnt > 0) {
0095
0096
0097
0098
0099
0100
0101
0102
0103 gluebi->refcnt += 1;
0104 mutex_unlock(&devices_mutex);
0105 return 0;
0106 }
0107
0108
0109
0110
0111
0112 gluebi->desc = ubi_open_volume(gluebi->ubi_num, gluebi->vol_id,
0113 ubi_mode);
0114 if (IS_ERR(gluebi->desc)) {
0115 mutex_unlock(&devices_mutex);
0116 return PTR_ERR(gluebi->desc);
0117 }
0118 gluebi->refcnt += 1;
0119 mutex_unlock(&devices_mutex);
0120 return 0;
0121 }
0122
0123
0124
0125
0126
0127
0128
0129
0130 static void gluebi_put_device(struct mtd_info *mtd)
0131 {
0132 struct gluebi_device *gluebi;
0133
0134 gluebi = container_of(mtd, struct gluebi_device, mtd);
0135 mutex_lock(&devices_mutex);
0136 gluebi->refcnt -= 1;
0137 if (gluebi->refcnt == 0)
0138 ubi_close_volume(gluebi->desc);
0139 mutex_unlock(&devices_mutex);
0140 }
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153 static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len,
0154 size_t *retlen, unsigned char *buf)
0155 {
0156 int err = 0, lnum, offs, bytes_left;
0157 struct gluebi_device *gluebi;
0158
0159 gluebi = container_of(mtd, struct gluebi_device, mtd);
0160 lnum = div_u64_rem(from, mtd->erasesize, &offs);
0161 bytes_left = len;
0162 while (bytes_left) {
0163 size_t to_read = mtd->erasesize - offs;
0164
0165 if (to_read > bytes_left)
0166 to_read = bytes_left;
0167
0168 err = ubi_read(gluebi->desc, lnum, buf, offs, to_read);
0169 if (err)
0170 break;
0171
0172 lnum += 1;
0173 offs = 0;
0174 bytes_left -= to_read;
0175 buf += to_read;
0176 }
0177
0178 *retlen = len - bytes_left;
0179 return err;
0180 }
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193 static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len,
0194 size_t *retlen, const u_char *buf)
0195 {
0196 int err = 0, lnum, offs, bytes_left;
0197 struct gluebi_device *gluebi;
0198
0199 gluebi = container_of(mtd, struct gluebi_device, mtd);
0200 lnum = div_u64_rem(to, mtd->erasesize, &offs);
0201
0202 if (len % mtd->writesize || offs % mtd->writesize)
0203 return -EINVAL;
0204
0205 bytes_left = len;
0206 while (bytes_left) {
0207 size_t to_write = mtd->erasesize - offs;
0208
0209 if (to_write > bytes_left)
0210 to_write = bytes_left;
0211
0212 err = ubi_leb_write(gluebi->desc, lnum, buf, offs, to_write);
0213 if (err)
0214 break;
0215
0216 lnum += 1;
0217 offs = 0;
0218 bytes_left -= to_write;
0219 buf += to_write;
0220 }
0221
0222 *retlen = len - bytes_left;
0223 return err;
0224 }
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234 static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
0235 {
0236 int err, i, lnum, count;
0237 struct gluebi_device *gluebi;
0238
0239 if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd))
0240 return -EINVAL;
0241
0242 lnum = mtd_div_by_eb(instr->addr, mtd);
0243 count = mtd_div_by_eb(instr->len, mtd);
0244 gluebi = container_of(mtd, struct gluebi_device, mtd);
0245
0246 for (i = 0; i < count - 1; i++) {
0247 err = ubi_leb_unmap(gluebi->desc, lnum + i);
0248 if (err)
0249 goto out_err;
0250 }
0251
0252
0253
0254
0255
0256
0257
0258 err = ubi_leb_erase(gluebi->desc, lnum + i);
0259 if (err)
0260 goto out_err;
0261
0262 return 0;
0263
0264 out_err:
0265 instr->fail_addr = (long long)lnum * mtd->erasesize;
0266 return err;
0267 }
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277
0278 static int gluebi_create(struct ubi_device_info *di,
0279 struct ubi_volume_info *vi)
0280 {
0281 struct gluebi_device *gluebi, *g;
0282 struct mtd_info *mtd;
0283
0284 gluebi = kzalloc(sizeof(struct gluebi_device), GFP_KERNEL);
0285 if (!gluebi)
0286 return -ENOMEM;
0287
0288 mtd = &gluebi->mtd;
0289 mtd->name = kmemdup(vi->name, vi->name_len + 1, GFP_KERNEL);
0290 if (!mtd->name) {
0291 kfree(gluebi);
0292 return -ENOMEM;
0293 }
0294
0295 gluebi->vol_id = vi->vol_id;
0296 gluebi->ubi_num = vi->ubi_num;
0297 mtd->type = MTD_UBIVOLUME;
0298 if (!di->ro_mode)
0299 mtd->flags = MTD_WRITEABLE;
0300 mtd->owner = THIS_MODULE;
0301 mtd->writesize = di->min_io_size;
0302 mtd->erasesize = vi->usable_leb_size;
0303 mtd->_read = gluebi_read;
0304 mtd->_write = gluebi_write;
0305 mtd->_erase = gluebi_erase;
0306 mtd->_get_device = gluebi_get_device;
0307 mtd->_put_device = gluebi_put_device;
0308
0309
0310
0311
0312
0313
0314 if (vi->vol_type == UBI_DYNAMIC_VOLUME)
0315 mtd->size = (unsigned long long)vi->usable_leb_size * vi->size;
0316 else
0317 mtd->size = vi->used_bytes;
0318
0319
0320 mutex_lock(&devices_mutex);
0321 g = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
0322 if (g)
0323 err_msg("gluebi MTD device %d form UBI device %d volume %d already exists",
0324 g->mtd.index, vi->ubi_num, vi->vol_id);
0325 mutex_unlock(&devices_mutex);
0326
0327 if (mtd_device_register(mtd, NULL, 0)) {
0328 err_msg("cannot add MTD device");
0329 kfree(mtd->name);
0330 kfree(gluebi);
0331 return -ENFILE;
0332 }
0333
0334 mutex_lock(&devices_mutex);
0335 list_add_tail(&gluebi->list, &gluebi_devices);
0336 mutex_unlock(&devices_mutex);
0337 return 0;
0338 }
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348 static int gluebi_remove(struct ubi_volume_info *vi)
0349 {
0350 int err = 0;
0351 struct mtd_info *mtd;
0352 struct gluebi_device *gluebi;
0353
0354 mutex_lock(&devices_mutex);
0355 gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
0356 if (!gluebi) {
0357 err_msg("got remove notification for unknown UBI device %d volume %d",
0358 vi->ubi_num, vi->vol_id);
0359 err = -ENOENT;
0360 } else if (gluebi->refcnt)
0361 err = -EBUSY;
0362 else
0363 list_del(&gluebi->list);
0364 mutex_unlock(&devices_mutex);
0365 if (err)
0366 return err;
0367
0368 mtd = &gluebi->mtd;
0369 err = mtd_device_unregister(mtd);
0370 if (err) {
0371 err_msg("cannot remove fake MTD device %d, UBI device %d, volume %d, error %d",
0372 mtd->index, gluebi->ubi_num, gluebi->vol_id, err);
0373 mutex_lock(&devices_mutex);
0374 list_add_tail(&gluebi->list, &gluebi_devices);
0375 mutex_unlock(&devices_mutex);
0376 return err;
0377 }
0378
0379 kfree(mtd->name);
0380 kfree(gluebi);
0381 return 0;
0382 }
0383
0384
0385
0386
0387
0388
0389
0390
0391
0392
0393
0394 static int gluebi_updated(struct ubi_volume_info *vi)
0395 {
0396 struct gluebi_device *gluebi;
0397
0398 mutex_lock(&devices_mutex);
0399 gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
0400 if (!gluebi) {
0401 mutex_unlock(&devices_mutex);
0402 err_msg("got update notification for unknown UBI device %d volume %d",
0403 vi->ubi_num, vi->vol_id);
0404 return -ENOENT;
0405 }
0406
0407 if (vi->vol_type == UBI_STATIC_VOLUME)
0408 gluebi->mtd.size = vi->used_bytes;
0409 mutex_unlock(&devices_mutex);
0410 return 0;
0411 }
0412
0413
0414
0415
0416
0417
0418
0419
0420
0421 static int gluebi_resized(struct ubi_volume_info *vi)
0422 {
0423 struct gluebi_device *gluebi;
0424
0425 mutex_lock(&devices_mutex);
0426 gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
0427 if (!gluebi) {
0428 mutex_unlock(&devices_mutex);
0429 err_msg("got update notification for unknown UBI device %d volume %d",
0430 vi->ubi_num, vi->vol_id);
0431 return -ENOENT;
0432 }
0433 gluebi->mtd.size = vi->used_bytes;
0434 mutex_unlock(&devices_mutex);
0435 return 0;
0436 }
0437
0438
0439
0440
0441
0442
0443
0444 static int gluebi_notify(struct notifier_block *nb, unsigned long l,
0445 void *ns_ptr)
0446 {
0447 struct ubi_notification *nt = ns_ptr;
0448
0449 switch (l) {
0450 case UBI_VOLUME_ADDED:
0451 gluebi_create(&nt->di, &nt->vi);
0452 break;
0453 case UBI_VOLUME_REMOVED:
0454 gluebi_remove(&nt->vi);
0455 break;
0456 case UBI_VOLUME_RESIZED:
0457 gluebi_resized(&nt->vi);
0458 break;
0459 case UBI_VOLUME_UPDATED:
0460 gluebi_updated(&nt->vi);
0461 break;
0462 default:
0463 break;
0464 }
0465 return NOTIFY_OK;
0466 }
0467
0468 static struct notifier_block gluebi_notifier = {
0469 .notifier_call = gluebi_notify,
0470 };
0471
0472 static int __init ubi_gluebi_init(void)
0473 {
0474 return ubi_register_volume_notifier(&gluebi_notifier, 0);
0475 }
0476
0477 static void __exit ubi_gluebi_exit(void)
0478 {
0479 struct gluebi_device *gluebi, *g;
0480
0481 list_for_each_entry_safe(gluebi, g, &gluebi_devices, list) {
0482 int err;
0483 struct mtd_info *mtd = &gluebi->mtd;
0484
0485 err = mtd_device_unregister(mtd);
0486 if (err)
0487 err_msg("error %d while removing gluebi MTD device %d, UBI device %d, volume %d - ignoring",
0488 err, mtd->index, gluebi->ubi_num,
0489 gluebi->vol_id);
0490 kfree(mtd->name);
0491 kfree(gluebi);
0492 }
0493 ubi_unregister_volume_notifier(&gluebi_notifier);
0494 }
0495
0496 module_init(ubi_gluebi_init);
0497 module_exit(ubi_gluebi_exit);
0498 MODULE_DESCRIPTION("MTD emulation layer over UBI volumes");
0499 MODULE_AUTHOR("Artem Bityutskiy, Joern Engel");
0500 MODULE_LICENSE("GPL");