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
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
31 #include <linux/stat.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
)
45 list_for_each_entry(cdev
, &cdev_list
, list
) {
46 if (!strcmp(cdev
->name
, filename
))
52 ssize_t
cdev_read(struct cdev
*cdev
, void *buf
, size_t count
, ulong offset
, ulong flags
)
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
)
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
;
88 ret
= cdev
->ops
->lseek(cdev
, pos
+ cdev
->offset
);
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
)
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
)
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
;
121 if (!cdev
->ops
->memmap
)
124 ret
= cdev
->ops
->memmap(cdev
, map
, flags
);
127 *map
= (void *)((unsigned long)*map
+ cdev
->offset
);
132 static int devfs_open(struct device_d
*_dev
, FILE *f
, const char *filename
)
137 cdev
= cdev_by_name(filename
+ 1);
142 f
->size
= cdev
->size
;
145 if (cdev
->ops
->open
) {
146 ret
= cdev
->ops
->open(cdev
, f
);
156 static int devfs_close(struct device_d
*_dev
, FILE *f
)
158 struct cdev
*cdev
= f
->inode
;
161 if (cdev
->ops
->close
) {
162 ret
= cdev
->ops
->close(cdev
, f
);
172 static int partition_ioctl(struct cdev
*cdev
, int request
, void *buf
)
175 struct mtd_info_user
*user
= buf
;
181 offset
+= cdev
->offset
;
182 return cdev
->ops
->ioctl(cdev
, request
, (void *)offset
);
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 */
196 if (!cdev
->ops
->ioctl
)
198 return cdev
->ops
->ioctl(cdev
, request
, buf
);
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
)
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
)
224 static DIR* devfs_opendir(struct device_d
*dev
, const char *pathname
)
228 dir
= xzalloc(sizeof(DIR));
230 if (!list_empty(&cdev_list
))
231 dir
->priv
= list_first_entry(&cdev_list
, struct cdev
, list
);
236 static struct dirent
* devfs_readdir(struct device_d
*_dev
, DIR *dir
)
238 struct cdev
*cdev
= dir
->priv
;
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
);
251 static int devfs_closedir(struct device_d
*dev
, DIR *dir
)
257 static int devfs_stat(struct device_d
*_dev
, const char *filename
, struct stat
*s
)
261 cdev
= cdev_by_name(filename
+ 1);
265 s
->st_mode
= S_IFCHR
;
266 s
->st_size
= cdev
->size
;
267 if (cdev
->ops
->write
)
268 s
->st_mode
|= S_IWUSR
;
270 s
->st_mode
|= S_IRUSR
;
275 static int devfs_probe(struct device_d
*dev
)
280 static void devfs_delete(struct device_d
*dev
)
284 static struct fs_driver_d devfs_driver
= {
285 .type
= FS_TYPE_DEVFS
,
287 .write
= devfs_write
,
288 .lseek
= devfs_lseek
,
290 .close
= devfs_close
,
291 .ioctl
= devfs_ioctl
,
292 .opendir
= devfs_opendir
,
293 .readdir
= devfs_readdir
,
294 .truncate
= devfs_truncate
,
295 .closedir
= devfs_closedir
,
297 .erase
= devfs_erase
,
298 .protect
= devfs_protect
,
299 .memmap
= devfs_memmap
,
300 .flags
= FS_DRIVER_NO_DEV
,
302 .probe
= devfs_probe
,
303 .remove
= devfs_delete
,
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)
320 cdev
= cdev_by_name(new->name
);
324 list_add_tail(&new->list
, &cdev_list
);
326 list_add_tail(&new->devices_list
, &new->dev
->cdevs
);
331 int devfs_remove(struct cdev
*cdev
)
336 list_del(&cdev
->list
);
338 list_del(&cdev
->devices_list
);
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
);
352 cdev
= cdev_by_name(devname
);
356 if (offset
+ size
> cdev
->size
)
359 new = xzalloc(sizeof (*new));
360 new->name
= strdup(name
);
361 new->ops
= cdev
->ops
;
362 new->priv
= cdev
->priv
;
364 new->offset
= offset
+ cdev
->offset
;
365 new->dev
= cdev
->dev
;
366 new->flags
= flags
| DEVFS_IS_PARTITION
;
368 #ifdef CONFIG_PARTITION_NEED_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
);
384 int devfs_del_partition(const char *name
)
389 cdev
= cdev_by_name(name
);
393 if (!(cdev
->flags
& DEVFS_IS_PARTITION
))
395 if (cdev
->flags
& DEVFS_PARTITION_FIXED
)
398 #ifdef CONFIG_PARTITION_NEED_MTD
400 mtd_del_partition(cdev
->mtd
);
403 ret
= devfs_remove(cdev
);