1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32#include <linux/err.h>
33#include <linux/list.h>
34#include <linux/slab.h>
35#include <linux/sched.h>
36#include <linux/math64.h>
37#include <linux/module.h>
38#include <linux/mutex.h>
39#include <linux/mtd/ubi.h>
40#include <linux/mtd/mtd.h>
41#include "ubi-media.h"
42
43#define err_msg(fmt, ...) \
44 pr_err("gluebi (pid %d): %s: " fmt "\n", \
45 current->pid, __func__, ##__VA_ARGS__)
46
47
48
49
50
51
52
53
54
55
56struct gluebi_device {
57 struct mtd_info mtd;
58 int refcnt;
59 struct ubi_volume_desc *desc;
60 int ubi_num;
61 int vol_id;
62 struct list_head list;
63};
64
65
66static LIST_HEAD(gluebi_devices);
67static DEFINE_MUTEX(devices_mutex);
68
69
70
71
72
73
74
75
76
77
78
79static struct gluebi_device *find_gluebi_nolock(int ubi_num, int vol_id)
80{
81 struct gluebi_device *gluebi;
82
83 list_for_each_entry(gluebi, &gluebi_devices, list)
84 if (gluebi->ubi_num == ubi_num && gluebi->vol_id == vol_id)
85 return gluebi;
86 return NULL;
87}
88
89
90
91
92
93
94
95
96
97static int gluebi_get_device(struct mtd_info *mtd)
98{
99 struct gluebi_device *gluebi;
100 int ubi_mode = UBI_READONLY;
101
102 if (!try_module_get(THIS_MODULE))
103 return -ENODEV;
104
105 if (mtd->flags & MTD_WRITEABLE)
106 ubi_mode = UBI_READWRITE;
107
108 gluebi = container_of(mtd, struct gluebi_device, mtd);
109 mutex_lock(&devices_mutex);
110 if (gluebi->refcnt > 0) {
111
112
113
114
115
116
117
118
119 gluebi->refcnt += 1;
120 mutex_unlock(&devices_mutex);
121 return 0;
122 }
123
124
125
126
127
128 gluebi->desc = ubi_open_volume(gluebi->ubi_num, gluebi->vol_id,
129 ubi_mode);
130 if (IS_ERR(gluebi->desc)) {
131 mutex_unlock(&devices_mutex);
132 module_put(THIS_MODULE);
133 return PTR_ERR(gluebi->desc);
134 }
135 gluebi->refcnt += 1;
136 mutex_unlock(&devices_mutex);
137 return 0;
138}
139
140
141
142
143
144
145
146
147static void gluebi_put_device(struct mtd_info *mtd)
148{
149 struct gluebi_device *gluebi;
150
151 gluebi = container_of(mtd, struct gluebi_device, mtd);
152 mutex_lock(&devices_mutex);
153 gluebi->refcnt -= 1;
154 if (gluebi->refcnt == 0)
155 ubi_close_volume(gluebi->desc);
156 module_put(THIS_MODULE);
157 mutex_unlock(&devices_mutex);
158}
159
160
161
162
163
164
165
166
167
168
169
170
171static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len,
172 size_t *retlen, unsigned char *buf)
173{
174 int err = 0, lnum, offs, bytes_left;
175 struct gluebi_device *gluebi;
176
177 gluebi = container_of(mtd, struct gluebi_device, mtd);
178 lnum = div_u64_rem(from, mtd->erasesize, &offs);
179 bytes_left = len;
180 while (bytes_left) {
181 size_t to_read = mtd->erasesize - offs;
182
183 if (to_read > bytes_left)
184 to_read = bytes_left;
185
186 err = ubi_read(gluebi->desc, lnum, buf, offs, to_read);
187 if (err)
188 break;
189
190 lnum += 1;
191 offs = 0;
192 bytes_left -= to_read;
193 buf += to_read;
194 }
195
196 *retlen = len - bytes_left;
197 return err;
198}
199
200
201
202
203
204
205
206
207
208
209
210
211static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len,
212 size_t *retlen, const u_char *buf)
213{
214 int err = 0, lnum, offs, bytes_left;
215 struct gluebi_device *gluebi;
216
217 gluebi = container_of(mtd, struct gluebi_device, mtd);
218 lnum = div_u64_rem(to, mtd->erasesize, &offs);
219
220 if (len % mtd->writesize || offs % mtd->writesize)
221 return -EINVAL;
222
223 bytes_left = len;
224 while (bytes_left) {
225 size_t to_write = mtd->erasesize - offs;
226
227 if (to_write > bytes_left)
228 to_write = bytes_left;
229
230 err = ubi_leb_write(gluebi->desc, lnum, buf, offs, to_write);
231 if (err)
232 break;
233
234 lnum += 1;
235 offs = 0;
236 bytes_left -= to_write;
237 buf += to_write;
238 }
239
240 *retlen = len - bytes_left;
241 return err;
242}
243
244
245
246
247
248
249
250
251
252static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
253{
254 int err, i, lnum, count;
255 struct gluebi_device *gluebi;
256
257 if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd))
258 return -EINVAL;
259
260 lnum = mtd_div_by_eb(instr->addr, mtd);
261 count = mtd_div_by_eb(instr->len, mtd);
262 gluebi = container_of(mtd, struct gluebi_device, mtd);
263
264 for (i = 0; i < count - 1; i++) {
265 err = ubi_leb_unmap(gluebi->desc, lnum + i);
266 if (err)
267 goto out_err;
268 }
269
270
271
272
273
274
275
276 err = ubi_leb_erase(gluebi->desc, lnum + i);
277 if (err)
278 goto out_err;
279
280 instr->state = MTD_ERASE_DONE;
281 mtd_erase_callback(instr);
282 return 0;
283
284out_err:
285 instr->state = MTD_ERASE_FAILED;
286 instr->fail_addr = (long long)lnum * mtd->erasesize;
287 return err;
288}
289
290
291
292
293
294
295
296
297
298
299static int gluebi_create(struct ubi_device_info *di,
300 struct ubi_volume_info *vi)
301{
302 struct gluebi_device *gluebi, *g;
303 struct mtd_info *mtd;
304
305 gluebi = kzalloc(sizeof(struct gluebi_device), GFP_KERNEL);
306 if (!gluebi)
307 return -ENOMEM;
308
309 mtd = &gluebi->mtd;
310 mtd->name = kmemdup(vi->name, vi->name_len + 1, GFP_KERNEL);
311 if (!mtd->name) {
312 kfree(gluebi);
313 return -ENOMEM;
314 }
315
316 gluebi->vol_id = vi->vol_id;
317 gluebi->ubi_num = vi->ubi_num;
318 mtd->type = MTD_UBIVOLUME;
319 if (!di->ro_mode)
320 mtd->flags = MTD_WRITEABLE;
321 mtd->owner = THIS_MODULE;
322 mtd->writesize = di->min_io_size;
323 mtd->erasesize = vi->usable_leb_size;
324 mtd->_read = gluebi_read;
325 mtd->_write = gluebi_write;
326 mtd->_erase = gluebi_erase;
327 mtd->_get_device = gluebi_get_device;
328 mtd->_put_device = gluebi_put_device;
329
330
331
332
333
334
335 if (vi->vol_type == UBI_DYNAMIC_VOLUME)
336 mtd->size = (unsigned long long)vi->usable_leb_size * vi->size;
337 else
338 mtd->size = vi->used_bytes;
339
340
341 mutex_lock(&devices_mutex);
342 g = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
343 if (g)
344 err_msg("gluebi MTD device %d form UBI device %d volume %d already exists",
345 g->mtd.index, vi->ubi_num, vi->vol_id);
346 mutex_unlock(&devices_mutex);
347
348 if (mtd_device_register(mtd, NULL, 0)) {
349 err_msg("cannot add MTD device");
350 kfree(mtd->name);
351 kfree(gluebi);
352 return -ENFILE;
353 }
354
355 mutex_lock(&devices_mutex);
356 list_add_tail(&gluebi->list, &gluebi_devices);
357 mutex_unlock(&devices_mutex);
358 return 0;
359}
360
361
362
363
364
365
366
367
368
369static int gluebi_remove(struct ubi_volume_info *vi)
370{
371 int err = 0;
372 struct mtd_info *mtd;
373 struct gluebi_device *gluebi;
374
375 mutex_lock(&devices_mutex);
376 gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
377 if (!gluebi) {
378 err_msg("got remove notification for unknown UBI device %d volume %d",
379 vi->ubi_num, vi->vol_id);
380 err = -ENOENT;
381 } else if (gluebi->refcnt)
382 err = -EBUSY;
383 else
384 list_del(&gluebi->list);
385 mutex_unlock(&devices_mutex);
386 if (err)
387 return err;
388
389 mtd = &gluebi->mtd;
390 err = mtd_device_unregister(mtd);
391 if (err) {
392 err_msg("cannot remove fake MTD device %d, UBI device %d, volume %d, error %d",
393 mtd->index, gluebi->ubi_num, gluebi->vol_id, err);
394 mutex_lock(&devices_mutex);
395 list_add_tail(&gluebi->list, &gluebi_devices);
396 mutex_unlock(&devices_mutex);
397 return err;
398 }
399
400 kfree(mtd->name);
401 kfree(gluebi);
402 return 0;
403}
404
405
406
407
408
409
410
411
412
413
414
415static int gluebi_updated(struct ubi_volume_info *vi)
416{
417 struct gluebi_device *gluebi;
418
419 mutex_lock(&devices_mutex);
420 gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
421 if (!gluebi) {
422 mutex_unlock(&devices_mutex);
423 err_msg("got update notification for unknown UBI device %d volume %d",
424 vi->ubi_num, vi->vol_id);
425 return -ENOENT;
426 }
427
428 if (vi->vol_type == UBI_STATIC_VOLUME)
429 gluebi->mtd.size = vi->used_bytes;
430 mutex_unlock(&devices_mutex);
431 return 0;
432}
433
434
435
436
437
438
439
440
441
442static int gluebi_resized(struct ubi_volume_info *vi)
443{
444 struct gluebi_device *gluebi;
445
446 mutex_lock(&devices_mutex);
447 gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id);
448 if (!gluebi) {
449 mutex_unlock(&devices_mutex);
450 err_msg("got update notification for unknown UBI device %d volume %d",
451 vi->ubi_num, vi->vol_id);
452 return -ENOENT;
453 }
454 gluebi->mtd.size = vi->used_bytes;
455 mutex_unlock(&devices_mutex);
456 return 0;
457}
458
459
460
461
462
463
464
465static int gluebi_notify(struct notifier_block *nb, unsigned long l,
466 void *ns_ptr)
467{
468 struct ubi_notification *nt = ns_ptr;
469
470 switch (l) {
471 case UBI_VOLUME_ADDED:
472 gluebi_create(&nt->di, &nt->vi);
473 break;
474 case UBI_VOLUME_REMOVED:
475 gluebi_remove(&nt->vi);
476 break;
477 case UBI_VOLUME_RESIZED:
478 gluebi_resized(&nt->vi);
479 break;
480 case UBI_VOLUME_UPDATED:
481 gluebi_updated(&nt->vi);
482 break;
483 default:
484 break;
485 }
486 return NOTIFY_OK;
487}
488
489static struct notifier_block gluebi_notifier = {
490 .notifier_call = gluebi_notify,
491};
492
493static int __init ubi_gluebi_init(void)
494{
495 return ubi_register_volume_notifier(&gluebi_notifier, 0);
496}
497
498static void __exit ubi_gluebi_exit(void)
499{
500 struct gluebi_device *gluebi, *g;
501
502 list_for_each_entry_safe(gluebi, g, &gluebi_devices, list) {
503 int err;
504 struct mtd_info *mtd = &gluebi->mtd;
505
506 err = mtd_device_unregister(mtd);
507 if (err)
508 err_msg("error %d while removing gluebi MTD device %d, UBI device %d, volume %d - ignoring",
509 err, mtd->index, gluebi->ubi_num,
510 gluebi->vol_id);
511 kfree(mtd->name);
512 kfree(gluebi);
513 }
514 ubi_unregister_volume_notifier(&gluebi_notifier);
515}
516
517module_init(ubi_gluebi_init);
518module_exit(ubi_gluebi_exit);
519MODULE_DESCRIPTION("MTD emulation layer over UBI volumes");
520MODULE_AUTHOR("Artem Bityutskiy, Joern Engel");
521MODULE_LICENSE("GPL");
522