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