1 /*
2  * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3  */
4 
5 #include <stdlib.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <aos/mtd.h>
9 #include <aos/mtdpart.h>
10 #include <drivers/u_ld.h>
11 
12 /**
13  * keep a mapping for partition name and device name,
14  * just in case users want to operation parition based
15  * on device name (such as dd command).
16  */
17 struct mtd_info_entry {
18     struct mtd_part_info pinfo;
19     aos_mtd_t *mtd;
20 };
21 
22 struct mtd_info_ctx {
23     int np;
24     struct mtd_info_entry *entries;
25 };
26 
27 static struct mtd_info_ctx g_mtd_info = {0};
28 
mtd_part_alloc(aos_mtd_t * master,const struct mtd_part * part)29 static aos_mtd_t* mtd_part_alloc(aos_mtd_t *master, const struct mtd_part *part)
30 {
31     aos_mtd_t *slave;
32 
33     slave = malloc(sizeof(aos_mtd_t));
34     if (slave == NULL)
35         goto out;
36 
37     slave->master = master;
38 
39     *slave = *master;
40     slave->size = part->size;
41     slave->offset = part->offset;
42 
43 out:
44 
45     return slave;
46 }
47 
48 /* Caller is responsible to free the info memery */
aos_mtd_part_info_get(struct mtd_part_info ** info,int * cnt)49 int aos_mtd_part_info_get(struct mtd_part_info **info, int *cnt)
50 {
51     if (g_mtd_info.np > 0) {
52         int np = g_mtd_info.np;
53         struct mtd_info_entry *entries = g_mtd_info.entries;
54         struct mtd_part_info *p;
55 
56         *info = calloc(sizeof(struct mtd_part_info), np);
57         if (*info == NULL) goto failure;
58 
59         p = *info;
60         for (int i = 0; i < np; i++, p++) {
61             memcpy(p, &(entries[i].pinfo), sizeof(struct mtd_part_info));
62         }
63 
64         *cnt = np;
65         return 0;
66     }
67 
68 failure:
69     if (*info) free(*info);
70     *info = NULL;
71     *cnt = 0;
72     return -1;
73 }
74 
75 #if 0
76 static void aos_show_mtdparts()
77 {
78     int cnt;
79     char name[32];
80     uint32_t size, offset;
81     struct mtd_part_info *info = NULL;
82 
83     if (aos_mtd_part_info_get(&info, &cnt) == 0) {
84         LOG("IDX :Name\tSize\tOffset\t\tDevice");
85         LOG("----------------------------------------------------\r\n");
86         for (int i = 0; i < cnt; i++) {
87             memset(name, 0, sizeof(name));
88             snprintf(name, sizeof(name), "MTD%d:%s\t", i+1, info[i].part_name);
89             if (strlen(name) <= 8) {
90                 snprintf(name+strlen(name), sizeof(name) - strlen(name), "\t");
91             }
92 
93             size = info[i].size >> 10; // in KB uint
94             offset = info[i].offset;
95 
96             LOG("%s%dKB\t0x%08x\t/dev/%s", name, size, offset, info[i].dev_name);
97         }
98 
99         free(info);
100     }
101 }
102 #endif
103 
aos_mtd_get_by_device(const char * device_name)104 aos_mtd_t* aos_mtd_get_by_device(const char *device_name)
105 {
106     aos_mtd_t *mtd;
107 
108     mtd = (aos_mtd_t *)aos_device_find(device_name);
109     if (mtd == NULL)
110         return NULL;
111 
112     if (mtd->parent.type != AOS_Device_Class_MTD)
113         return NULL;
114 
115     return mtd;
116 }
117 
aos_mtd_get_by_std_part(const char * partition_name)118 aos_mtd_t* aos_mtd_get_by_std_part(const char *partition_name)
119 {
120     char *name;
121     int index = 0;
122 
123     while (index < g_mtd_info.np) {
124         name = g_mtd_info.entries[index].pinfo.part_name_std;
125         if (strcmp(partition_name, name) == 0) break;
126         index++;
127     }
128 
129     if (index >= g_mtd_info.np) return NULL;
130 
131     return aos_mtd_get_by_device(g_mtd_info.entries[index].pinfo.dev_name);
132 }
133 
aos_mtd_get_by_vendor_part(const char * partition_name)134 aos_mtd_t* aos_mtd_get_by_vendor_part(const char *partition_name)
135 {
136     char *name;
137     int index = 0;
138 
139     while (index < g_mtd_info.np) {
140         name = g_mtd_info.entries[index].pinfo.part_name;
141         if (strcmp(partition_name, name) == 0) break;
142         index++;
143     }
144 
145     if (index >= g_mtd_info.np) return NULL;
146 
147     return aos_mtd_get_by_device(g_mtd_info.entries[index].pinfo.dev_name);
148 }
149 
mtd_init(aos_device_t dev)150 static int mtd_init(aos_device_t dev)
151 {
152     return 0;
153 }
154 
mtd_open(aos_device_t dev,uint16_t flags)155 static int mtd_open(aos_device_t dev, uint16_t flags)
156 {
157     return 0;
158 }
159 
mtd_close(aos_device_t dev)160 static int mtd_close(aos_device_t dev)
161 {
162     return 0;
163 }
164 
mtd_read(aos_device_t dev,long pos,void * buffer,uint32_t size)165 static size_t mtd_read(aos_device_t dev, long pos, void *buffer, uint32_t size)
166 {
167     return (size_t)aos_mtd_read((aos_mtd_t *)dev, pos, (uint8_t *)buffer, size);
168 }
169 
mtd_write(aos_device_t dev,long pos,const void * buffer,uint32_t size)170 static size_t mtd_write(aos_device_t dev, long pos, const void *buffer, uint32_t size)
171 {
172     return (size_t)aos_mtd_write((aos_mtd_t *)dev, pos, (const uint8_t *)buffer, size);
173 }
174 
mtd_ioctl(aos_device_t dev,int cmd,void * arg)175 static int mtd_ioctl(aos_device_t dev, int cmd, void *arg)
176 {
177     int ret = -1;
178     aos_mtd_t *mtd = (aos_mtd_t *)dev;
179     struct mtd_erase_info *einfo;
180 
181     switch (cmd) {
182         case IOC_MTD_GET_SIZE:
183             *((unsigned int *)arg) = mtd->size;
184             ret = 0;
185             break;
186         case IOC_MTD_GET_OFFSET:
187             *((off_t *)arg) = mtd->offset;
188             ret = 0;
189             break;
190         case IOC_MTD_ERASE:
191             einfo = (struct mtd_erase_info *)arg;
192             ret = aos_mtd_erase(mtd, einfo->offset, einfo->length);
193             break;
194         default:
195             ret = -1;
196             break;
197     }
198 
199     return ret;
200 }
201 
aos_mtd_register(aos_mtd_t * master,const struct mtd_part * parts,int np)202 int aos_mtd_register(aos_mtd_t *master, const struct mtd_part *parts, int np)
203 {
204     int ret;
205     aos_mtd_t *slave;
206     static int dev_idx = 0; // defined as static so as to support multiple chips
207     int index = 0;
208     char name[16];
209 
210     master->master = master;
211     master->parent.type = AOS_Device_Class_MTD;
212     master->parent.init = mtd_init;
213     master->parent.open = mtd_open;
214     master->parent.close = mtd_close;
215     master->parent.read = mtd_read;
216     master->parent.write = mtd_write;
217     master->parent.control = mtd_ioctl;
218 
219     if (np > 0) {
220         g_mtd_info.entries = calloc(np, sizeof(struct mtd_info_entry));
221         if (!g_mtd_info.entries) return -1;
222         g_mtd_info.np = np;
223 
224         master->offset = parts->offset;
225         master->size = parts->size;
226 
227         // generata device name
228         memset(name, 0, sizeof(name));
229         snprintf(name, sizeof(name), MTD_DEVICE_NAME_FORMAT, dev_idx++);
230 
231         // setup mtd information context
232         g_mtd_info.entries[index].pinfo.offset = parts->offset;
233         g_mtd_info.entries[index].pinfo.size = parts->size;
234         strncpy(g_mtd_info.entries[index].pinfo.part_name, parts->name, MTD_PARTITION_NAME_MAX);
235         strncpy(g_mtd_info.entries[index].pinfo.part_name_std, parts->name_std, MTD_PARTITION_NAME_MAX);
236         strncpy(g_mtd_info.entries[index].pinfo.dev_name, name, MTD_DEVICE_NAME_MAX);
237         g_mtd_info.entries[index].mtd = master;
238 
239         // register device
240         ret = aos_device_register((aos_device_t)master, name, 0);
241         if (ret != 0)
242             goto _out;
243 
244         np --;
245         parts ++;
246         index++;
247     }
248 
249     while (np > 0) {
250         slave = mtd_part_alloc(master, parts);
251         if (!slave)
252             break;
253 
254         slave->parent.init = mtd_init;
255         slave->parent.open = mtd_open;
256         slave->parent.close = mtd_close;
257         slave->parent.read = mtd_read;
258         slave->parent.write = mtd_write;
259         slave->parent.control = mtd_ioctl;
260 
261         // generate device name
262         memset(name, 0, sizeof(name));
263         snprintf(name, sizeof(name), MTD_DEVICE_NAME_FORMAT, dev_idx++);
264 
265         // setup name map
266         g_mtd_info.entries[index].pinfo.offset = parts->offset;
267         g_mtd_info.entries[index].pinfo.size = parts->size;
268         strncpy(g_mtd_info.entries[index].pinfo.part_name, parts->name, MTD_PARTITION_NAME_MAX);
269         strncpy(g_mtd_info.entries[index].pinfo.part_name_std, parts->name_std, MTD_PARTITION_NAME_MAX);
270         strncpy(g_mtd_info.entries[index].pinfo.dev_name, name, MTD_DEVICE_NAME_MAX);
271         g_mtd_info.entries[index].mtd = slave;
272 
273         // register device
274         ret = aos_device_register((aos_device_t)slave, name, 0);
275         if (ret)
276             break;
277 
278         parts ++;
279         np --;
280         index++;
281     }
282 
283 _out:
284     return np;
285 }
286 
aos_mtd_open(const char * name)287 aos_mtd_t* aos_mtd_open(const char *name)
288 {
289     aos_mtd_t *mtd_dev = NULL;
290 
291     if (name == NULL) {
292         return NULL;
293     }
294 
295     mtd_dev = aos_mtd_get_by_vendor_part(name);
296     if (mtd_dev != NULL) {
297         return mtd_dev;
298     }
299 
300     /* Try to find standard partition name if it can't be found in
301      * vendor partition name.
302      */
303     mtd_dev = aos_mtd_get_by_std_part(name);
304 
305     return mtd_dev;
306 }
307 
aos_mtd_close(aos_mtd_t * mtd)308 aos_status_t aos_mtd_close(aos_mtd_t *mtd)
309 {
310     return 0;
311 }
312 
aos_mtd_read(aos_mtd_t * mtd,off_t offset,void * buf,size_t size)313 ssize_t aos_mtd_read(aos_mtd_t *mtd, off_t offset, void *buf, size_t size)
314 {
315     struct mtd_io_desc info = {0};
316 
317     if (mtd == NULL || offset < 0 || offset >= (off_t)mtd->size ||
318         buf == NULL || size > mtd->size - offset || size <= 0) {
319         return -EINVAL;
320     }
321 
322     info.datbuf = buf;
323     info.datlen = size;
324 
325     return mtd->ops->read(mtd->master, offset + mtd->offset, &info);
326 }
327 
aos_mtd_oob_read(aos_mtd_t * mtd,off_t offset,struct mtd_io_desc * info)328 ssize_t aos_mtd_oob_read(aos_mtd_t *mtd, off_t offset, struct mtd_io_desc *info)
329 {
330     if (mtd == NULL || info == NULL) {
331         return -EINVAL;
332     }
333 
334     info->datretlen = 0;
335     info->oobretlen = 0;
336 
337     if (offset < 0 || offset >= (off_t)mtd->size)
338         return -EINVAL;
339 
340     if (info->datbuf && (info->datlen > (mtd->size - offset)))
341         return -EINVAL;
342 
343     return mtd->ops->read(mtd->master, offset + mtd->offset, info);
344 }
345 
aos_mtd_write(aos_mtd_t * mtd,off_t offset,const void * buf,size_t size)346 ssize_t aos_mtd_write(aos_mtd_t *mtd, off_t offset, const void *buf, size_t size)
347 {
348     struct mtd_io_desc info = {0};
349 
350     if (mtd == NULL || offset < 0 || offset >= (off_t)mtd->size ||
351         buf == NULL || size > mtd->size - offset || size <= 0) {
352         return -EINVAL;
353     }
354     if (mtd->ops->write == NULL) {
355         return -ENOTSUP;
356     }
357 
358     info.datbuf = (uint8_t*)buf;
359     info.datlen = size;
360 
361     return mtd->ops->write(mtd->master, offset + mtd->offset, &info);
362 }
363 
aos_mtd_oob_write(aos_mtd_t * mtd,off_t offset,struct mtd_io_desc * info)364 ssize_t aos_mtd_oob_write(aos_mtd_t *mtd, off_t offset, struct mtd_io_desc *info)
365 {
366     if ((mtd == NULL) || (info == NULL)) {
367         return -EINVAL;
368     }
369 
370     if (mtd->ops->write == NULL) {
371         return -ENOTSUP;
372     }
373 
374     info->datretlen = 0;
375     info->oobretlen = 0;
376 
377     if (offset < 0 || offset >= (off_t)mtd->size) {
378         return -EINVAL;
379     }
380 
381     if (info->datbuf && (info->datlen > (mtd->size - offset))) {
382         return -EINVAL;
383     }
384 
385     return mtd->ops->write(mtd->master, offset + mtd->offset, info);
386 }
387 
aos_mtd_erase(aos_mtd_t * mtd,off_t offset,size_t size)388 aos_status_t aos_mtd_erase(aos_mtd_t *mtd, off_t offset, size_t size)
389 {
390     if (mtd == NULL || size <= 0) {
391         return -EINVAL;
392     }
393 
394     if (mtd->ops->erase == NULL) {
395         return -ENOTSUP;
396     }
397 
398     if (offset < 0 || (offset + size) > mtd->size) {
399         return -EINVAL;
400     }
401     offset += mtd->offset;
402 
403     return mtd->ops->erase(mtd->master, offset, size);
404 }
405 
aos_mtd_block_erase(aos_mtd_t * mtd,uint32_t block)406 aos_status_t aos_mtd_block_erase(aos_mtd_t *mtd, uint32_t block)
407 {
408     uint32_t total_blks;
409     off_t offset;
410 
411     if (mtd == NULL) {
412         return -EINVAL;
413     }
414 
415     if (mtd->ops->erase == NULL) {
416         return -ENOTSUP;
417     }
418 
419     total_blks = mtd->size / mtd->block_size;
420     if (block >= total_blks) {
421         return -EINVAL;
422     }
423     offset = mtd->offset + mtd->block_size * block;
424 
425     return mtd->ops->erase(mtd->master, offset, mtd->block_size);
426 }
427 
aos_mtd_block_mark_bad(aos_mtd_t * mtd,uint32_t block)428 aos_status_t aos_mtd_block_mark_bad(aos_mtd_t *mtd, uint32_t block)
429 {
430     uint32_t total_blks, offset_blk;
431 
432     if (mtd == NULL) {
433         return -EINVAL;
434     }
435 
436     if (mtd->ops->markbad == NULL) {
437         return -ENOTSUP;
438     }
439 
440     total_blks = mtd->size / mtd->block_size;
441     if (block >= total_blks) {
442         return -EINVAL;
443     }
444     offset_blk = mtd->offset / mtd->block_size;
445 
446     return mtd->ops->markbad(mtd->master, block + offset_blk);
447 }
448 
aos_mtd_block_is_bad(aos_mtd_t * mtd,uint32_t block)449 aos_status_t aos_mtd_block_is_bad(aos_mtd_t *mtd, uint32_t block)
450 {
451     uint32_t total_blks, offset_blk;
452 
453     if (mtd == NULL) {
454         return -EINVAL;
455     }
456 
457     if (mtd->ops->isbad == NULL) {
458         return -ENOTSUP;
459     }
460 
461     total_blks = mtd->size / mtd->block_size;
462     if (block >= total_blks) {
463         return -EINVAL;
464     }
465     offset_blk = mtd->offset / mtd->block_size;
466 
467     return mtd->ops->isbad(mtd->master, block + offset_blk);
468 }
469 
aos_mtd_init()470 int aos_mtd_init()
471 {
472     return csi_flash_init();
473 }
474 
475 VFS_DRIVER_ENTRY(aos_mtd_init)
476