fs: Use filesystem methods instead of switch()
[oweals/u-boot.git] / fs / fs.c
1 /*
2  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16
17 #include <config.h>
18 #include <common.h>
19 #include <part.h>
20 #include <ext4fs.h>
21 #include <fat.h>
22 #include <fs.h>
23
24 DECLARE_GLOBAL_DATA_PTR;
25
26 static block_dev_desc_t *fs_dev_desc;
27 static disk_partition_t fs_partition;
28 static int fs_type = FS_TYPE_ANY;
29
30 static inline int fs_probe_unsupported(void)
31 {
32         printf("** Unrecognized filesystem type **\n");
33         return -1;
34 }
35
36 static inline int fs_ls_unsupported(const char *dirname)
37 {
38         return -1;
39 }
40
41 static inline int fs_read_unsupported(const char *filename, ulong addr,
42                                       int offset, int len)
43 {
44         return -1;
45 }
46
47 static inline void fs_close_unsupported(void)
48 {
49 }
50
51 #ifdef CONFIG_FS_FAT
52 static int fs_probe_fat(void)
53 {
54         return fat_set_blk_dev(fs_dev_desc, &fs_partition);
55 }
56
57 static void fs_close_fat(void)
58 {
59 }
60
61 #define fs_ls_fat file_fat_ls
62
63 static int fs_read_fat(const char *filename, ulong addr, int offset, int len)
64 {
65         int len_read;
66
67         len_read = file_fat_read_at(filename, offset,
68                                     (unsigned char *)addr, len);
69         if (len_read == -1) {
70                 printf("** Unable to read file %s **\n", filename);
71                 return -1;
72         }
73
74         return len_read;
75 }
76 #else
77 static inline int fs_probe_fat(void)
78 {
79         return -1;
80 }
81
82 static inline void fs_close_fat(void)
83 {
84 }
85
86 #define fs_ls_fat fs_ls_unsupported
87 #define fs_read_fat fs_read_unsupported
88 #endif
89
90 #ifdef CONFIG_FS_EXT4
91 static int fs_probe_ext(void)
92 {
93         ext4fs_set_blk_dev(fs_dev_desc, &fs_partition);
94
95         if (!ext4fs_mount(fs_partition.size)) {
96                 ext4fs_close();
97                 return -1;
98         }
99
100         return 0;
101 }
102
103 static void fs_close_ext(void)
104 {
105         ext4fs_close();
106 }
107
108 #define fs_ls_ext ext4fs_ls
109
110 static int fs_read_ext(const char *filename, ulong addr, int offset, int len)
111 {
112         int file_len;
113         int len_read;
114
115         if (offset != 0) {
116                 printf("** Cannot support non-zero offset **\n");
117                 return -1;
118         }
119
120         file_len = ext4fs_open(filename);
121         if (file_len < 0) {
122                 printf("** File not found %s **\n", filename);
123                 ext4fs_close();
124                 return -1;
125         }
126
127         if (len == 0)
128                 len = file_len;
129
130         len_read = ext4fs_read((char *)addr, len);
131         ext4fs_close();
132
133         if (len_read != len) {
134                 printf("** Unable to read file %s **\n", filename);
135                 return -1;
136         }
137
138         return len_read;
139 }
140 #else
141 static inline int fs_probe_ext(void)
142 {
143         return -1;
144 }
145
146 static inline void fs_close_ext(void)
147 {
148 }
149
150 #define fs_ls_ext fs_ls_unsupported
151 #define fs_read_ext fs_read_unsupported
152 #endif
153
154 struct fstype_info {
155         int fstype;
156         int (*probe)(void);
157         int (*ls)(const char *dirname);
158         int (*read)(const char *filename, ulong addr, int offset, int len);
159         void (*close)(void);
160 };
161
162 static struct fstype_info fstypes[] = {
163 #ifdef CONFIG_FS_FAT
164         {
165                 .fstype = FS_TYPE_FAT,
166                 .probe = fs_probe_fat,
167                 .close = fs_close_fat,
168                 .ls = file_fat_ls,
169                 .read = fs_read_fat,
170         },
171 #endif
172 #ifdef CONFIG_FS_EXT4
173         {
174                 .fstype = FS_TYPE_EXT,
175                 .probe = fs_probe_ext,
176                 .close = fs_close_ext,
177                 .ls = ext4fs_ls,
178                 .read = fs_read_ext,
179         },
180 #endif
181         {
182                 .fstype = FS_TYPE_ANY,
183                 .probe = fs_probe_unsupported,
184                 .close = fs_close_unsupported,
185                 .ls = fs_ls_unsupported,
186                 .read = fs_read_unsupported,
187         },
188 };
189
190 static struct fstype_info *fs_get_info(int fstype)
191 {
192         struct fstype_info *info;
193         int i;
194
195         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) {
196                 if (fstype == info->fstype)
197                         return info;
198         }
199
200         /* Return the 'unsupported' sentinel */
201         return info;
202 }
203
204 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
205 {
206         struct fstype_info *info;
207         int part, i;
208 #ifdef CONFIG_NEEDS_MANUAL_RELOC
209         static int relocated;
210
211         if (!relocated) {
212                 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes);
213                                 i++, info++) {
214                         info->probe += gd->reloc_off;
215                         info->close += gd->reloc_off;
216                         info->ls += gd->reloc_off;
217                         info->read += gd->reloc_off;
218                 }
219                 relocated = 1;
220         }
221 #endif
222
223         part = get_device_and_partition(ifname, dev_part_str, &fs_dev_desc,
224                                         &fs_partition, 1);
225         if (part < 0)
226                 return -1;
227
228         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
229                 if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY &&
230                                 fstype != info->fstype)
231                         continue;
232
233                 if (!info->probe()) {
234                         fs_type = info->fstype;
235                         return 0;
236                 }
237         }
238
239         return -1;
240 }
241
242 static void fs_close(void)
243 {
244         struct fstype_info *info = fs_get_info(fs_type);
245
246         info->close();
247         fs_type = FS_TYPE_ANY;
248 }
249
250 int fs_ls(const char *dirname)
251 {
252         int ret;
253
254         struct fstype_info *info = fs_get_info(fs_type);
255
256         ret = info->ls(dirname);
257
258         fs_close();
259
260         return ret;
261 }
262
263 int fs_read(const char *filename, ulong addr, int offset, int len)
264 {
265         struct fstype_info *info = fs_get_info(fs_type);
266         int ret;
267
268         ret = info->read(filename, addr, offset, len);
269
270         /* If we requested a specific number of bytes, check we got it */
271         if (ret >= 0 && len && ret != len) {
272                 printf("** Unable to read file %s **\n", filename);
273                 ret = -1;
274         }
275         fs_close();
276
277         return ret;
278 }
279
280 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
281                 int fstype, int cmdline_base)
282 {
283         unsigned long addr;
284         const char *addr_str;
285         const char *filename;
286         unsigned long bytes;
287         unsigned long pos;
288         int len_read;
289         unsigned long time;
290
291         if (argc < 2)
292                 return CMD_RET_USAGE;
293         if (argc > 7)
294                 return CMD_RET_USAGE;
295
296         if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
297                 return 1;
298
299         if (argc >= 4) {
300                 addr = simple_strtoul(argv[3], NULL, cmdline_base);
301         } else {
302                 addr_str = getenv("loadaddr");
303                 if (addr_str != NULL)
304                         addr = simple_strtoul(addr_str, NULL, 16);
305                 else
306                         addr = CONFIG_SYS_LOAD_ADDR;
307         }
308         if (argc >= 5) {
309                 filename = argv[4];
310         } else {
311                 filename = getenv("bootfile");
312                 if (!filename) {
313                         puts("** No boot file defined **\n");
314                         return 1;
315                 }
316         }
317         if (argc >= 6)
318                 bytes = simple_strtoul(argv[5], NULL, cmdline_base);
319         else
320                 bytes = 0;
321         if (argc >= 7)
322                 pos = simple_strtoul(argv[6], NULL, cmdline_base);
323         else
324                 pos = 0;
325
326         time = get_timer(0);
327         len_read = fs_read(filename, addr, pos, bytes);
328         time = get_timer(time);
329         if (len_read <= 0)
330                 return 1;
331
332         printf("%d bytes read in %lu ms", len_read, time);
333         if (time > 0) {
334                 puts(" (");
335                 print_size(len_read / time * 1000, "/s");
336                 puts(")");
337         }
338         puts("\n");
339
340         setenv_hex("filesize", len_read);
341
342         return 0;
343 }
344
345 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
346         int fstype)
347 {
348         if (argc < 2)
349                 return CMD_RET_USAGE;
350         if (argc > 4)
351                 return CMD_RET_USAGE;
352
353         if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
354                 return 1;
355
356         if (fs_ls(argc >= 4 ? argv[3] : "/"))
357                 return 1;
358
359         return 0;
360 }