Fix unmounting flag
[yaffs/.git] / patches / mtdpart.c
1 /*
2  * Simple MTD partitioning layer
3  *
4  * (C) 2000 Nicolas Pitre <nico@cam.org>
5  *
6  * This code is GPL
7  *
8  * $Id: mtdpart.c,v 1.1 2002-05-22 12:38:56 wookey Exp $
9  */
10
11 #include <linux/module.h>
12 #include <linux/types.h>
13 #include <linux/kernel.h>
14 #include <linux/slab.h>
15 #include <linux/list.h>
16
17 #include <linux/mtd/mtd.h>
18 #include <linux/mtd/partitions.h>
19
20
21 /* Our partition linked list */
22 static LIST_HEAD(mtd_partitions);
23
24 /* Our partition node structure */
25 struct mtd_part {
26         struct mtd_info mtd;
27         struct mtd_info *master;
28         u_int32_t offset;
29         int index;
30         struct list_head list;
31 };
32
33 /*
34  * Given a pointer to the MTD object in the mtd_part structure, we can retrieve
35  * the pointer to that structure with this macro.
36  */
37 #define PART(x)  ((struct mtd_part *)(x))
38
39         
40 /* 
41  * MTD methods which simply translate the effective address and pass through
42  * to the _real_ device.
43  */
44
45 static int part_read (struct mtd_info *mtd, loff_t from, size_t len, 
46                         size_t *retlen, u_char *buf)
47 {
48         struct mtd_part *part = PART(mtd);
49         if (from >= mtd->size)
50                 len = 0;
51         else if (from + len > mtd->size)
52                 len = mtd->size - from;
53         return part->master->read (part->master, from + part->offset, 
54                                         len, retlen, buf);
55 }
56
57 static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
58                         size_t *retlen, const u_char *buf)
59 {
60         struct mtd_part *part = PART(mtd);
61         if (!(mtd->flags & MTD_WRITEABLE))
62                 return -EROFS;
63         if (to >= mtd->size)
64                 len = 0;
65         else if (to + len > mtd->size)
66                 len = mtd->size - to;
67         return part->master->write (part->master, to + part->offset, 
68                                         len, retlen, buf);
69 }
70
71 static int part_writev (struct mtd_info *mtd,  const struct iovec *vecs,
72                          unsigned long count, loff_t to, size_t *retlen)
73 {
74         struct mtd_part *part = PART(mtd);
75         if (!(mtd->flags & MTD_WRITEABLE))
76                 return -EROFS;
77         return part->master->writev (part->master, vecs, count,
78                                         to + part->offset, retlen);
79 }
80
81 static int part_readv (struct mtd_info *mtd,  struct iovec *vecs,
82                          unsigned long count, loff_t from, size_t *retlen)
83 {
84         struct mtd_part *part = PART(mtd);
85         return part->master->readv (part->master, vecs, count,
86                                         from + part->offset, retlen);
87 }
88
89 static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
90 {
91         struct mtd_part *part = PART(mtd);
92         if (!(mtd->flags & MTD_WRITEABLE))
93                 return -EROFS;
94         if (instr->addr >= mtd->size)
95                 return -EINVAL;
96         instr->addr += part->offset;
97         return part->master->erase(part->master, instr);
98 }
99
100 static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
101 {
102         struct mtd_part *part = PART(mtd);
103         if ((len + ofs) > mtd->size) 
104                 return -EINVAL;
105         return part->master->lock(part->master, ofs + part->offset, len);
106 }
107
108 static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
109 {
110         struct mtd_part *part = PART(mtd);
111         if ((len + ofs) > mtd->size) 
112                 return -EINVAL;
113         return part->master->unlock(part->master, ofs + part->offset, len);
114 }
115
116 static void part_sync(struct mtd_info *mtd)
117 {
118         struct mtd_part *part = PART(mtd);
119         part->master->sync(part->master);
120 }
121
122 static int part_suspend(struct mtd_info *mtd)
123 {
124         struct mtd_part *part = PART(mtd);
125         return part->master->suspend(part->master);
126 }
127
128 static void part_resume(struct mtd_info *mtd)
129 {
130         struct mtd_part *part = PART(mtd);
131         part->master->resume(part->master);
132 }
133
134 /* 
135  * This function unregisters and destroy all slave MTD objects which are 
136  * attached to the given master MTD object.
137  */
138
139 int del_mtd_partitions(struct mtd_info *master)
140 {
141         struct list_head *node;
142         struct mtd_part *slave;
143
144         for (node = mtd_partitions.next;
145              node != &mtd_partitions;
146              node = node->next) {
147                 slave = list_entry(node, struct mtd_part, list);
148                 if (slave->master == master) {
149                         struct list_head *prev = node->prev;
150                         __list_del(prev, node->next);
151                         del_mtd_device(&slave->mtd);
152                         kfree(slave);
153                         node = prev;
154                 }
155         }
156
157         return 0;
158 }
159
160 /*
161  * This function, given a master MTD object and a partition table, creates
162  * and registers slave MTD objects which are bound to the master according to
163  * the partition definitions.
164  * (Q: should we register the master MTD object as well?)
165  */
166
167 int add_mtd_partitions(struct mtd_info *master, 
168                        struct mtd_partition *parts,
169                        int nbparts)
170 {
171         struct mtd_part *slave;
172         u_int32_t cur_offset = 0;
173         int i;
174
175         printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
176
177         for (i = 0; i < nbparts; i++) {
178
179                 /* allocate the partition structure */
180                 slave = kmalloc (sizeof(*slave), GFP_KERNEL);
181                 if (!slave) {
182                         printk ("memory allocation error while creating partitions for \"%s\"\n",
183                                 master->name);
184                         del_mtd_partitions(master);
185                         return -ENOMEM;
186                 }
187                 memset(slave, 0, sizeof(*slave));
188                 list_add(&slave->list, &mtd_partitions);
189
190                 /* set up the MTD object for this partition */
191                 slave->mtd.type = master->type;
192                 slave->mtd.flags = master->flags & ~parts[i].mask_flags;
193                 slave->mtd.size = parts[i].size;
194                 slave->mtd.oobblock = master->oobblock;
195                 slave->mtd.oobsize = master->oobsize;
196                 slave->mtd.ecctype = master->ecctype;
197                 slave->mtd.eccsize = master->eccsize;
198
199                 slave->mtd.name = parts[i].name;
200                 slave->mtd.bank_size = master->bank_size;
201
202                 slave->mtd.module = master->module;
203
204                 slave->mtd.read = part_read;
205                 slave->mtd.write = part_write;
206                 if (master->sync)
207                         slave->mtd.sync = part_sync;
208                 if (!i && master->suspend && master->resume) {
209                                 slave->mtd.suspend = part_suspend;
210                                 slave->mtd.resume = part_resume;
211                 }
212
213                 if (master->writev)
214                         slave->mtd.writev = part_writev;
215                 if (master->readv)
216                         slave->mtd.readv = part_readv;
217                 if (master->lock)
218                         slave->mtd.lock = part_lock;
219                 if (master->unlock)
220                         slave->mtd.unlock = part_unlock;
221 /* cdhm: fix add oob functions to partitions */
222                 if (master->read_oob)
223                         slave->mtd.read_oob = master->read_oob;
224                 if (master->write_oob)
225                         slave->mtd.write_oob = master->write_oob;
226 /* cdhm: end of fix */
227                 slave->mtd.erase = part_erase;
228                 slave->master = master;
229                 slave->offset = parts[i].offset;
230                 slave->index = i;
231
232                 if (slave->offset == MTDPART_OFS_APPEND)
233                         slave->offset = cur_offset;
234                 if (slave->mtd.size == MTDPART_SIZ_FULL)
235                         slave->mtd.size = master->size - slave->offset;
236                 cur_offset = slave->offset + slave->mtd.size;
237         
238                 printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, 
239                         slave->offset + slave->mtd.size, slave->mtd.name);
240
241                 /* let's do some sanity checks */
242                 if (slave->offset >= master->size) {
243                                 /* let's register it anyway to preserve ordering */
244                         slave->offset = 0;
245                         slave->mtd.size = 0;
246                         printk ("mtd: partition \"%s\" is out of reach -- disabled\n",
247                                 parts[i].name);
248                 }
249                 if (slave->offset + slave->mtd.size > master->size) {
250                         slave->mtd.size = master->size - slave->offset;
251                         printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n",
252                                 parts[i].name, master->name, slave->mtd.size);
253                 }
254                 if (master->numeraseregions>1) {
255                         /* Deal with variable erase size stuff */
256                         int i;
257                         struct mtd_erase_region_info *regions = master->eraseregions;
258                         
259                         /* Find the first erase regions which is part of this partition. */
260                         for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++)
261                                 ;
262
263                         for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) {
264                                 if (slave->mtd.erasesize < regions[i].erasesize) {
265                                         slave->mtd.erasesize = regions[i].erasesize;
266                                 }
267                         }
268                 } else {
269                         /* Single erase size */
270                         slave->mtd.erasesize = master->erasesize;
271                 }
272
273                 if ((slave->mtd.flags & MTD_WRITEABLE) && 
274                     (slave->offset % slave->mtd.erasesize)) {
275                         /* Doesn't start on a boundary of major erase size */
276                         /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */
277                         slave->mtd.flags &= ~MTD_WRITEABLE;
278                         printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
279                                 parts[i].name);
280                 }
281                 if ((slave->mtd.flags & MTD_WRITEABLE) && 
282                     (slave->mtd.size % slave->mtd.erasesize)) {
283                         slave->mtd.flags &= ~MTD_WRITEABLE;
284                         printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
285                                 parts[i].name);
286                 }
287
288                 /* register our partition */
289                 add_mtd_device(&slave->mtd);
290         }
291
292         return 0;
293 }
294
295 EXPORT_SYMBOL(add_mtd_partitions);
296 EXPORT_SYMBOL(del_mtd_partitions);
297
298
299 MODULE_LICENSE("GPL");
300 MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
301 MODULE_DESCRIPTION("Generic support for partitioning of MTD devices");
302