menu: do not go to free if there's nothing to free
[barebox-mini2440.git] / fs / devfs.c
blob7019c8d9209841884579002351d40f3aea440252
1 /*
2 * devfs.c - a device file system for barebox
4 * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
6 * See file CREDITS for list of people who contributed to this
7 * project.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2
11 * as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <common.h>
24 #include <driver.h>
25 #include <init.h>
26 #include <malloc.h>
27 #include <fs.h>
28 #include <command.h>
29 #include <errno.h>
30 #include <xfuncs.h>
31 #include <linux/stat.h>
32 #include <ioctl.h>
33 #include <nand.h>
34 #include <linux/err.h>
35 #include <linux/mtd/mtd.h>
36 #include <linux/mtd/mtd-abi.h>
37 #include <partition.h>
39 static LIST_HEAD(cdev_list);
41 struct cdev *cdev_by_name(const char *filename)
43 struct cdev *cdev;
45 list_for_each_entry(cdev, &cdev_list, list) {
46 if (!strcmp(cdev->name, filename))
47 return cdev;
49 return NULL;
52 ssize_t cdev_read(struct cdev *cdev, void *buf, size_t count, ulong offset, ulong flags)
54 if (!cdev->ops->read)
55 return -ENOSYS;
57 return cdev->ops->read(cdev, buf, count, cdev->offset +offset, flags);
60 ssize_t cdev_write(struct cdev *cdev, const void *buf, size_t count, ulong offset, ulong flags)
62 if (!cdev->ops->write)
63 return -ENOSYS;
65 return cdev->ops->write(cdev, buf, count, cdev->offset + offset, flags);
68 static int devfs_read(struct device_d *_dev, FILE *f, void *buf, size_t size)
70 struct cdev *cdev = f->inode;
72 return cdev_read(cdev, buf, size, f->pos, f->flags);
75 static int devfs_write(struct device_d *_dev, FILE *f, const void *buf, size_t size)
77 struct cdev *cdev = f->inode;
79 return cdev_write(cdev, buf, size, f->pos, f->flags);
82 static off_t devfs_lseek(struct device_d *_dev, FILE *f, off_t pos)
84 struct cdev *cdev = f->inode;
85 off_t ret = -1;
87 if (cdev->ops->lseek)
88 ret = cdev->ops->lseek(cdev, pos + cdev->offset);
90 if (ret != -1)
91 f->pos = pos;
93 return ret - cdev->offset;
96 static int devfs_erase(struct device_d *_dev, FILE *f, size_t count, unsigned long offset)
98 struct cdev *cdev = f->inode;
100 if (!cdev->ops->erase)
101 return -ENOSYS;
103 return cdev->ops->erase(cdev, count, offset + cdev->offset);
106 static int devfs_protect(struct device_d *_dev, FILE *f, size_t count, unsigned long offset, int prot)
108 struct cdev *cdev = f->inode;
110 if (!cdev->ops->protect)
111 return -ENOSYS;
113 return cdev->ops->protect(cdev, count, offset + cdev->offset, prot);
116 static int devfs_memmap(struct device_d *_dev, FILE *f, void **map, int flags)
118 struct cdev *cdev = f->inode;
119 int ret = -ENOSYS;
121 if (!cdev->ops->memmap)
122 return -EINVAL;
124 ret = cdev->ops->memmap(cdev, map, flags);
126 if (!ret)
127 *map = (void *)((unsigned long)*map + cdev->offset);
129 return ret;
132 static int devfs_open(struct device_d *_dev, FILE *f, const char *filename)
134 struct cdev *cdev;
135 int ret;
137 cdev = cdev_by_name(filename + 1);
139 if (!cdev)
140 return -ENOENT;
142 f->size = cdev->size;
143 f->inode = cdev;
145 if (cdev->ops->open) {
146 ret = cdev->ops->open(cdev, f);
147 if (ret)
148 return ret;
151 cdev->open++;
153 return 0;
156 static int devfs_close(struct device_d *_dev, FILE *f)
158 struct cdev *cdev = f->inode;
159 int ret;
161 if (cdev->ops->close) {
162 ret = cdev->ops->close(cdev, f);
163 if (ret)
164 return ret;
167 cdev->open--;
169 return 0;
172 static int partition_ioctl(struct cdev *cdev, int request, void *buf)
174 size_t offset;
175 struct mtd_info_user *user = buf;
177 switch (request) {
178 case MEMSETBADBLOCK:
179 case MEMGETBADBLOCK:
180 offset = (off_t)buf;
181 offset += cdev->offset;
182 return cdev->ops->ioctl(cdev, request, (void *)offset);
183 case MEMGETINFO:
184 if (cdev->mtd) {
185 user->type = cdev->mtd->type;
186 user->flags = cdev->mtd->flags;
187 user->size = cdev->mtd->size;
188 user->erasesize = cdev->mtd->erasesize;
189 user->oobsize = cdev->mtd->oobsize;
190 user->mtd = cdev->mtd;
191 /* The below fields are obsolete */
192 user->ecctype = -1;
193 user->eccsize = 0;
194 return 0;
196 if (!cdev->ops->ioctl)
197 return -EINVAL;
198 return cdev->ops->ioctl(cdev, request, buf);
199 default:
200 return -EINVAL;
204 static int devfs_ioctl(struct device_d *_dev, FILE *f, int request, void *buf)
206 struct cdev *cdev = f->inode;
208 if (cdev->flags & DEVFS_IS_PARTITION)
209 return partition_ioctl(cdev, request, buf);
211 if (!cdev->ops->ioctl)
212 return -EINVAL;
214 return cdev->ops->ioctl(cdev, request, buf);
217 static int devfs_truncate(struct device_d *dev, FILE *f, ulong size)
219 if (size > f->dev->size)
220 return -ENOSPC;
221 return 0;
224 static DIR* devfs_opendir(struct device_d *dev, const char *pathname)
226 DIR *dir;
228 dir = xzalloc(sizeof(DIR));
230 if (!list_empty(&cdev_list))
231 dir->priv = list_first_entry(&cdev_list, struct cdev, list);
233 return dir;
236 static struct dirent* devfs_readdir(struct device_d *_dev, DIR *dir)
238 struct cdev *cdev = dir->priv;
240 if (!cdev)
241 return NULL;
243 list_for_each_entry_from(cdev, &cdev_list, list) {
244 strcpy(dir->d.d_name, cdev->name);
245 dir->priv = list_entry(cdev->list.next, struct cdev, list);
246 return &dir->d;
248 return NULL;
251 static int devfs_closedir(struct device_d *dev, DIR *dir)
253 free(dir);
254 return 0;
257 static int devfs_stat(struct device_d *_dev, const char *filename, struct stat *s)
259 struct cdev *cdev;
261 cdev = cdev_by_name(filename + 1);
262 if (!cdev)
263 return -ENOENT;
265 s->st_mode = S_IFCHR;
266 s->st_size = cdev->size;
267 if (cdev->ops->write)
268 s->st_mode |= S_IWUSR;
269 if (cdev->ops->read)
270 s->st_mode |= S_IRUSR;
272 return 0;
275 static int devfs_probe(struct device_d *dev)
277 return 0;
280 static void devfs_delete(struct device_d *dev)
284 static struct fs_driver_d devfs_driver = {
285 .type = FS_TYPE_DEVFS,
286 .read = devfs_read,
287 .write = devfs_write,
288 .lseek = devfs_lseek,
289 .open = devfs_open,
290 .close = devfs_close,
291 .ioctl = devfs_ioctl,
292 .opendir = devfs_opendir,
293 .readdir = devfs_readdir,
294 .truncate = devfs_truncate,
295 .closedir = devfs_closedir,
296 .stat = devfs_stat,
297 .erase = devfs_erase,
298 .protect = devfs_protect,
299 .memmap = devfs_memmap,
300 .flags = FS_DRIVER_NO_DEV,
301 .drv = {
302 .probe = devfs_probe,
303 .remove = devfs_delete,
304 .name = "devfs",
305 .type_data = &devfs_driver,
309 static int devfs_init(void)
311 return register_fs_driver(&devfs_driver);
314 coredevice_initcall(devfs_init);
316 int devfs_create(struct cdev *new)
318 struct cdev *cdev;
320 cdev = cdev_by_name(new->name);
321 if (cdev)
322 return -EEXIST;
324 list_add_tail(&new->list, &cdev_list);
325 if (new->dev)
326 list_add_tail(&new->devices_list, &new->dev->cdevs);
328 return 0;
331 int devfs_remove(struct cdev *cdev)
333 if (cdev->open)
334 return -EBUSY;
336 list_del(&cdev->list);
337 if (cdev->dev)
338 list_del(&cdev->devices_list);
340 return 0;
343 int devfs_add_partition(const char *devname, unsigned long offset, size_t size,
344 int flags, const char *name)
346 struct cdev *cdev, *new;
348 cdev = cdev_by_name(name);
349 if (cdev)
350 return -EEXIST;
352 cdev = cdev_by_name(devname);
353 if (!cdev)
354 return -ENOENT;
356 if (offset + size > cdev->size)
357 return -EINVAL;
359 new = xzalloc(sizeof (*new));
360 new->name = strdup(name);
361 new->ops = cdev->ops;
362 new->priv = cdev->priv;
363 new->size = size;
364 new->offset = offset + cdev->offset;
365 new->dev = cdev->dev;
366 new->flags = flags | DEVFS_IS_PARTITION;
368 #ifdef CONFIG_PARTITION_NEED_MTD
369 if (cdev->mtd) {
370 new->mtd = mtd_add_partition(cdev->mtd, offset, size, flags, name);
371 if (IS_ERR(new->mtd)) {
372 int ret = PTR_ERR(new->mtd);
373 free(new);
374 return ret;
377 #endif
379 devfs_create(new);
381 return 0;
384 int devfs_del_partition(const char *name)
386 struct cdev *cdev;
387 int ret;
389 cdev = cdev_by_name(name);
390 if (!cdev)
391 return -ENOENT;
393 if (!(cdev->flags & DEVFS_IS_PARTITION))
394 return -EINVAL;
395 if (cdev->flags & DEVFS_PARTITION_FIXED)
396 return -EPERM;
398 #ifdef CONFIG_PARTITION_NEED_MTD
399 if (cdev->mtd)
400 mtd_del_partition(cdev->mtd);
401 #endif
403 ret = devfs_remove(cdev);
404 if (ret)
405 return ret;
407 free(cdev->name);
408 free(cdev);
410 return 0;