1 /*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without specific prior written
16 * permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "errno.h"
32 #include "fs/fs.h"
33 #include "los_config.h"
34 #include "los_mux.h"
35 #include "stdio.h"
36 #include "string.h"
37 #include "user_copy.h"
38
39 #include "hdf_log.h"
40 #include "mtd_char.h"
41 #include "mtd_core.h"
42 #include "mtd_legacy_lite.h"
43 #include "mtd_partition.h"
44 #include "mtd_user.h"
45 #include "osal_mem.h"
46
47 struct MtdFileInfo {
48 unsigned int partIndex;
49 int mode;
50 };
51
52 /*
53 * open device interface
54 */
MtdCharOpen(FAR struct file * filep)55 static int MtdCharOpen(FAR struct file *filep)
56 {
57 struct drv_data *drv = NULL;
58
59 if (filep == NULL || filep->f_vnode == NULL || filep->f_vnode->data == NULL) {
60 HDF_LOGE("MtdCharOpen: filep is NULL or f_vnode of filep is null or data of f_vnode is null!");
61 return -EINVAL;
62 }
63 drv = (struct drv_data *)filep->f_vnode->data;
64 mtd_partition *partition = (mtd_partition *)drv->priv;
65 if (partition == NULL) {
66 HDF_LOGE("MtdCharOpen: partition is null!");
67 return -EINVAL;
68 }
69 struct MtdFileInfo *mfi = NULL;
70
71 if (partition->user_num != 0) { // be opened
72 return -EBUSY;
73 }
74
75 mfi = (struct MtdFileInfo *)malloc(sizeof(*mfi));
76 if (mfi == NULL) {
77 PRINTK("MtdCharOpen: malloc mtd file info fail!");
78 return -ENOMEM;
79 }
80
81 (void)LOS_MuxLock(&partition->lock, LOS_WAIT_FOREVER);
82
83 partition->user_num = 1;
84
85 (void)LOS_MuxUnlock(&partition->lock);
86 mfi->partIndex = partition->patitionnum;
87 filep->f_pos = 0;
88 filep->f_priv = (void *)mfi;
89
90 return ENOERR;
91 }
92
93 /*
94 * close device interface
95 */
MtdCharClose(FAR struct file * filep)96 static int MtdCharClose(FAR struct file *filep)
97 {
98 struct drv_data *drv = (struct drv_data *)filep->f_vnode->data;
99 mtd_partition *partition = (mtd_partition *)drv->priv;
100 struct MtdFileInfo *mfi = (struct MtdFileInfo *)(filep->f_priv);
101
102 (void)LOS_MuxLock(&partition->lock, LOS_WAIT_FOREVER);
103
104 partition->user_num = 0;
105
106 (void)LOS_MuxUnlock(&partition->lock);
107 free(mfi);
108 filep->f_priv = NULL;
109
110 return ENOERR;
111 }
112
113 /*
114 * read device interface
115 */
MtdCharRead(FAR struct file * filep,FAR char * buffer,size_t buflen)116 static ssize_t MtdCharRead(FAR struct file *filep, FAR char *buffer, size_t buflen)
117 {
118 ssize_t ret = 0;
119 off_t ppos = filep->f_pos;
120 struct drv_data *drv = (struct drv_data *)filep->f_vnode->data;
121 mtd_partition *partition = (mtd_partition *)drv->priv;
122 struct MtdFileInfo *mfi = (struct MtdFileInfo *)filep->f_priv;
123 struct MtdDev *mtdDev = (struct MtdDev *)(partition->mtd_info);
124 struct MtdDevice *mtdDevice = (struct MtdDevice *)mtdDev->priv;
125 size_t blockSize = mtdDevice->eraseSize;
126 size_t partStart = partition->start_block * blockSize;
127 size_t partSize = (partition->end_block + 1 - partition->start_block) * blockSize;
128 size_t partOobSize = (partSize >> mtdDevice->writeSizeShift) * mtdDevice->oobSize;
129
130 if (buffer == NULL) {
131 HDF_LOGE("MtdCharRead: buffer is null!");
132 return -EINVAL;
133 }
134
135 (void)LOS_MuxLock(&partition->lock, LOS_WAIT_FOREVER);
136 if (ppos < 0 || ppos > partSize) {
137 PRINTK("MtdCharRead: current file offset:0x%x invalid!\n", ppos);
138 ret = -EINVAL;
139 goto out1;
140 }
141
142 if ((mfi->mode == MTD_FILE_MODE_OOB && (ppos + buflen) > (partSize + partOobSize)) ||
143 (mfi->mode != MTD_FILE_MODE_OOB && (ppos + buflen) > partSize)) {
144 PRINTK("MtdCharRead: buffer to large, buflen:0x%x!\n", buflen);
145 ret = -EINVAL;
146 goto out1;
147 }
148
149 if (!buflen) {
150 ret = ENOERR;
151 goto out1;
152 }
153
154 if (mfi->mode == MTD_FILE_MODE_OOB) {
155 ret = MtdDeviceReadWithOob(mtdDevice, partStart + ppos, buflen, (uint8_t *)buffer);
156 } else {
157 ret = MtdDeviceRead(mtdDevice, partStart + ppos, buflen, (uint8_t *)buffer);
158 }
159
160 if (ret != HDF_SUCCESS) {
161 PRINTK("MtdCharRead: read err in mode %d!\n", mfi->mode);
162 goto out1;
163 }
164
165 filep->f_pos += (mfi->mode == MTD_FILE_MODE_OOB) ?
166 (buflen - (buflen >> mtdDevice->writeSizeShift) * mtdDevice->oobSize) : buflen;
167
168 out1:
169 (void)LOS_MuxUnlock(&partition->lock);
170 return ret;
171 }
172
173 /*
174 * write device interface
175 */
MtdCharWrite(FAR struct file * filep,FAR const char * buffer,size_t buflen)176 static ssize_t MtdCharWrite(FAR struct file *filep, FAR const char *buffer, size_t buflen)
177 {
178 ssize_t ret = 0;
179 off_t ppos = filep->f_pos;
180 struct drv_data *drv = (struct drv_data *)filep->f_vnode->data;
181 mtd_partition *partition = (mtd_partition *)drv->priv;
182 struct MtdFileInfo *mfi = (struct MtdFileInfo *)filep->f_priv;
183 struct MtdDev *mtdDev = (struct MtdDev *)(partition->mtd_info);
184 struct MtdDevice *mtdDevice = (struct MtdDevice *)mtdDev->priv;
185 size_t blockSize = mtdDevice->eraseSize;
186 size_t partStart = partition->start_block * blockSize;
187 size_t partSize = (partition->end_block + 1 - partition->start_block) * blockSize;
188 size_t partOobSize = (partSize >> mtdDevice->writeSizeShift) * mtdDevice->oobSize;
189
190 if (buffer == NULL) {
191 HDF_LOGE("MtdCharWrite: buffer is null!");
192 return -EINVAL;
193 }
194
195 (void)LOS_MuxLock(&partition->lock, LOS_WAIT_FOREVER);
196 if (ppos < 0 || ppos > partSize) {
197 PRINTK("MtdCharWrite: current file offset:0x%x invalid!\n", ppos);
198 ret = -EINVAL;
199 goto out1;
200 }
201
202 if ((mfi->mode == MTD_FILE_MODE_OOB && (ppos + buflen) > (partSize + partOobSize)) ||
203 (mfi->mode != MTD_FILE_MODE_OOB && (ppos + buflen) > partSize)) {
204 PRINTK("MtdCharWrite: buffer to large, buflen:0x%x!\n", buflen);
205 ret = -EINVAL;
206 goto out1;
207 }
208
209 if (!buflen) {
210 ret = 0;
211 goto out1;
212 }
213
214 if (mfi->mode == MTD_FILE_MODE_OOB) {
215 ret = MtdDeviceWriteWithOob(mtdDevice, partStart + ppos, buflen, (const uint8_t *)buffer);
216 } else {
217 ret = MtdDeviceWrite(mtdDevice, partStart + ppos, buflen, (const uint8_t *)buffer);
218 }
219
220 if (ret != HDF_SUCCESS) {
221 PRINTK("MtdCharWrite: write err in mode %d!\n", mfi->mode);
222 goto out1;
223 }
224
225 filep->f_pos += (mfi->mode == MTD_FILE_MODE_OOB) ?
226 (buflen - (buflen >> mtdDevice->writeSizeShift) * mtdDevice->oobSize) : buflen;
227
228 out1:
229 (void)LOS_MuxUnlock(&partition->lock);
230 return ret;
231 }
232
233 /*
234 * lseek device interface
235 */
MtdCharLseek(FAR struct file * filep,off_t offset,int whence)236 static off_t MtdCharLseek(FAR struct file *filep, off_t offset, int whence)
237 {
238 struct drv_data *drv = (struct drv_data *)filep->f_vnode->data;
239 mtd_partition *partition = (mtd_partition *)drv->priv;
240
241 (void)LOS_MuxLock(&partition->lock, LOS_WAIT_FOREVER);
242
243 struct MtdDev *mtdDev = (struct MtdDev *)(partition->mtd_info);
244 size_t blockSize = mtdDev->eraseSize;
245 size_t endAddr = (partition->end_block + 1) * blockSize;
246 size_t startAddr = partition->start_block * blockSize;
247
248 switch (whence) {
249 case SEEK_SET:
250 if (offset >= 0 && (size_t)offset < endAddr - startAddr) {
251 filep->f_pos = (size_t)offset;
252 goto out1;
253 } else {
254 goto err1;
255 }
256
257 case SEEK_CUR:
258 if (offset + (size_t)filep->f_pos >= 0 &&
259 (size_t)(offset + filep->f_pos) < (endAddr - startAddr)) {
260 filep->f_pos += offset;
261 goto out1;
262 } else {
263 goto err1;
264 }
265
266 case SEEK_END:
267 if (offset < 0 && (offset + endAddr) >= startAddr) {
268 filep->f_pos = (off_t)(offset + endAddr - startAddr);
269 goto out1;
270 } else {
271 goto err1;
272 }
273
274 default:
275 goto err1;
276 }
277 err1:
278 (void)LOS_MuxUnlock(&partition->lock);
279 return -EINVAL;
280 out1:
281 (void)LOS_MuxUnlock(&partition->lock);
282 return filep->f_pos;
283 }
284
MtdCharGetMtdInfo(const struct MtdDevice * mtdDevice,struct MtdInfo * mtdInfo)285 static void MtdCharGetMtdInfo(const struct MtdDevice *mtdDevice, struct MtdInfo *mtdInfo)
286 {
287 mtdInfo->type = (mtdDevice->type == MTD_TYPE_NOR || mtdDevice->type == MTD_TYPE_SPI_NOR) ?
288 MTD_NORFLASH : MTD_NANDFLASH;
289 mtdInfo->size = mtdDevice->capacity;
290 mtdInfo->erasesize = mtdDevice->eraseSize;
291 mtdInfo->writesize = mtdDevice->writeSize;
292 mtdInfo->oobsize = mtdDevice->oobSize;
293 }
294
MtdCharIoctlGetInfo(const mtd_partition * part,const struct MtdDevice * mtdDevice,int cmd,unsigned long arg)295 static int MtdCharIoctlGetInfo(const mtd_partition *part, const struct MtdDevice *mtdDevice,
296 int cmd, unsigned long arg)
297 {
298 int ret;
299 struct MtdInfo mtdInfo;
300 size_t startAddr;
301 size_t endAddr;
302
303 (void)cmd;
304 MtdCharGetMtdInfo(mtdDevice, &mtdInfo);
305 startAddr = part->start_block * mtdDevice->eraseSize;
306 endAddr = (part->end_block + 1) * mtdDevice->eraseSize;
307 ret = LOS_CopyFromKernel((void *)(uintptr_t)arg, sizeof(mtdInfo), (void *)&mtdInfo, sizeof(mtdInfo));
308 if (ret == 0) {
309 ((struct MtdInfo *)(uintptr_t)arg)->size = endAddr - startAddr;
310 }
311 return ret;
312 }
313
MtdCharIoctlErase(const mtd_partition * part,struct MtdDevice * mtdDevice,int cmd,unsigned long arg)314 static int MtdCharIoctlErase(const mtd_partition *part, struct MtdDevice *mtdDevice,
315 int cmd, unsigned long arg)
316 {
317 int ret;
318 struct EraseInfo erase;
319 size_t startAddr;
320
321 (void)cmd;
322 ret = LOS_CopyToKernel((void *)&erase, sizeof(erase), (void *)(uintptr_t)arg, sizeof(erase));
323 if (ret != 0) {
324 return -EINVAL;
325 }
326
327 startAddr = part->start_block * mtdDevice->eraseSize;
328 return (int)MtdDeviceErase(mtdDevice, startAddr + erase.start, startAddr + erase.length, NULL);
329 }
330
MtdCharIoctlGetBadBlock(const mtd_partition * part,struct MtdDevice * mtdDevice,int cmd,unsigned long arg)331 static int MtdCharIoctlGetBadBlock(const mtd_partition *part, struct MtdDevice *mtdDevice,
332 int cmd, unsigned long arg)
333 {
334 int ret;
335 loff_t offs;
336 size_t startAddr;
337
338 (void)cmd;
339 ret = LOS_CopyToKernel((void *)&offs, sizeof(offs), (void *)(uintptr_t)arg, sizeof(offs));
340 if (ret != 0) {
341 return -EINVAL;
342 }
343
344 startAddr = part->start_block * mtdDevice->eraseSize;
345 return (int)MtdDeviceIsBadBlock(mtdDevice, (loff_t)startAddr + offs);
346 }
347
MtdCharIoctlSetBadBlock(const mtd_partition * part,struct MtdDevice * mtdDevice,int cmd,unsigned long arg)348 static int MtdCharIoctlSetBadBlock(const mtd_partition *part, struct MtdDevice *mtdDevice,
349 int cmd, unsigned long arg)
350 {
351 int ret;
352 loff_t offs;
353 size_t startAddr;
354
355 (void)cmd;
356 ret = LOS_CopyToKernel((void *)&offs, sizeof(offs), (void *)(uintptr_t)arg, sizeof(offs));
357 if (ret != 0) {
358 return -EINVAL;
359 }
360
361 startAddr = part->start_block * mtdDevice->eraseSize;
362 return (int)MtdDeviceMarkBadBlock(mtdDevice, (loff_t)startAddr + offs);
363 }
364
365 /*
366 * ioctl device interface
367 */
MtdCharIoctl(FAR struct file * filep,int cmd,unsigned long arg)368 static int MtdCharIoctl(FAR struct file *filep, int cmd, unsigned long arg)
369 {
370 int ret = ENOERR;
371 struct drv_data *drv = (struct drv_data *)filep->f_vnode->data;
372 mtd_partition *partition = (mtd_partition *)drv->priv;
373 struct MtdFileInfo *mfi = (struct MtdFileInfo *)filep->f_priv;
374 struct MtdDev *mtdDev = (struct MtdDev *)(partition->mtd_info);
375 struct MtdDevice *mtdDevice = (struct MtdDevice *)mtdDev->priv;
376
377 if (mtdDevice == NULL || arg == 0) {
378 HDF_LOGE("MtdCharIoctl: mtdDevice is NULL or arg is 0!");
379 return -EINVAL;
380 }
381
382 (void)LOS_MuxLock(&partition->lock, LOS_WAIT_FOREVER);
383
384 switch (cmd) {
385 case MTD_IOC_GETINFO: {
386 ret = MtdCharIoctlGetInfo(partition, mtdDevice, cmd, arg);
387 break;
388 }
389 case MTD_IOC_ERASE:
390 case MTD_IOC_ERASE64: {
391 ret = MtdCharIoctlErase(partition, mtdDevice, cmd, arg);
392 break;
393 }
394 case MTD_IOC_GETBADBLOCK: {
395 ret = MtdCharIoctlGetBadBlock(partition, mtdDevice, cmd, arg);
396 break;
397 }
398 case MTD_IOC_SETBADBLOCK: {
399 ret = MtdCharIoctlSetBadBlock(partition, mtdDevice, cmd, arg);
400 break;
401 }
402 case MTD_IOC_SETFILEMODE:
403 mfi->mode = 0;
404 if (arg >= MTD_FILE_MODE_MAX) {
405 PRINTK("MtdCharIoctl: invalid mtd file mode:%d!\n", arg);
406 ret = -EINVAL;
407 break;
408 }
409 mfi->mode = (int)arg;
410 break;
411 default:
412 ret = -EINVAL;
413 }
414
415 (void)LOS_MuxUnlock(&partition->lock);
416
417 return ret;
418 }
419
MtdCharMap(FAR struct file * filep,FAR LosVmMapRegion * region)420 static ssize_t MtdCharMap(FAR struct file* filep, FAR LosVmMapRegion *region)
421 {
422 (void)filep;
423 (void)region;
424
425 PRINTK("MtdCharMap: %d, mmap is not support!\n", __LINE__);
426 return 0;
427 }
428
429 static const struct file_operations_vfs g_MtdCharFops = {
430 .open = MtdCharOpen,
431 .close = MtdCharClose,
432 .read = MtdCharRead,
433 .write = MtdCharWrite,
434 .seek = MtdCharLseek,
435 .ioctl = MtdCharIoctl,
436 .mmap = MtdCharMap,
437 #ifndef CONFIG_DISABLE_POLL
438 .poll = NULL,
439 #endif
440 .unlink = NULL,
441 };
442
ToLiteOsMtdType(enum MtdDevType hdfType)443 static int ToLiteOsMtdType(enum MtdDevType hdfType)
444 {
445 int ret = HDF_FAILURE;
446
447 switch (hdfType) {
448 case MTD_TYPE_NAND:
449 case MTD_TYPE_SPI_NAND:
450 ret = MTD_NANDFLASH;
451 break;
452 case MTD_TYPE_NOR:
453 case MTD_TYPE_SPI_NOR:
454 ret = MTD_NORFLASH;
455 break;
456 default:
457 ret = MTD_TYPE_MAX;
458 break;
459 }
460 return ret;
461 }
462
HdfMtdDevErase(struct MtdDev * mtdDev,UINT64 start,UINT64 len,UINT64 * failAddr)463 static int HdfMtdDevErase(struct MtdDev *mtdDev, UINT64 start, UINT64 len, UINT64 *failAddr)
464 {
465 int ret;
466 off_t failPos;
467
468 if (mtdDev == NULL) {
469 HDF_LOGE("HdfMtdDevErase: mtdDev is null!");
470 return HDF_ERR_INVALID_OBJECT;
471 }
472 if (failAddr == NULL) {
473 HDF_LOGE("HdfMtdDevErase: failAddr is null!");
474 return -EINVAL;
475 }
476 ret = MtdDeviceErase((struct MtdDevice *)mtdDev->priv, start, len, &failPos);
477 if (ret != HDF_SUCCESS) {
478 *failAddr = failPos;
479 }
480 return ret;
481 }
482
HdfMtdDevRead(struct MtdDev * mtdDev,UINT64 start,UINT64 len,const char * buf)483 static int HdfMtdDevRead(struct MtdDev *mtdDev, UINT64 start, UINT64 len, const char *buf)
484 {
485 if (mtdDev == NULL) {
486 HDF_LOGE("HdfMtdDevRead: mtdDev is null!");
487 return HDF_ERR_INVALID_OBJECT;
488 }
489 return MtdDeviceRead((struct MtdDevice *)mtdDev->priv, start, len, (uint8_t *)buf);
490 }
491
HdfMtdDevWrite(struct MtdDev * mtdDev,UINT64 start,UINT64 len,const char * buf)492 static int HdfMtdDevWrite(struct MtdDev *mtdDev, UINT64 start, UINT64 len, const char *buf)
493 {
494 if (mtdDev == NULL) {
495 HDF_LOGE("HdfMtdDevWrite: mtdDev is null!");
496 return HDF_ERR_INVALID_OBJECT;
497 }
498 return MtdDeviceWrite((struct MtdDevice *)mtdDev->priv, start, len, (const uint8_t *)buf);
499 }
500
MtdCharGetName(struct MtdDevice * mtdDevice)501 static const char *MtdCharGetName(struct MtdDevice *mtdDevice)
502 {
503 switch (mtdDevice->type) {
504 case MTD_TYPE_SPI_NOR:
505 return "spinor";
506 case MTD_TYPE_SPI_NAND:
507 return "nand";
508 default:
509 HDF_LOGE("MtdCharGetName: mtd type:%d not support!", mtdDevice->type);
510 return NULL;
511 }
512 }
513
MtdCharOsInit(struct MtdDevice * mtdDevice)514 int32_t MtdCharOsInit(struct MtdDevice *mtdDevice)
515 {
516 struct MtdDev *mtdDev = NULL;
517 const char *devName = NULL;
518
519 if (mtdDevice == NULL) {
520 HDF_LOGE("MtdCharOsInit: mtdDevice is null!");
521 return HDF_ERR_INVALID_OBJECT;
522 }
523
524 devName = MtdCharGetName(mtdDevice);
525 if (devName == NULL) {
526 HDF_LOGE("MtdCharOsInit: devName is null!");
527 return HDF_FAILURE;
528 }
529
530 if (GetMtdInfo(devName) == 0) {
531 HDF_LOGE("MtdCharOsInit: MtdDev(%s) already been added!", devName);
532 return HDF_ERR_NOT_SUPPORT;
533 }
534
535 mtdDev = (struct MtdDev *)OsalMemCalloc(sizeof(*mtdDev));
536 if (mtdDev == NULL) {
537 HDF_LOGE("MtdCharOsInit: mtdDev is null!");
538 return HDF_ERR_IO;
539 }
540 mtdDev->priv = mtdDevice;
541 mtdDev->type = ToLiteOsMtdType(mtdDevice->type);
542 mtdDev->size = mtdDevice->capacity;
543 mtdDev->eraseSize = mtdDevice->eraseSize;
544 mtdDev->erase = HdfMtdDevErase;
545 mtdDev->read = HdfMtdDevRead;
546 mtdDev->write = HdfMtdDevWrite;
547 AddMtdList((char *)devName, mtdDev);
548 HDF_LOGI("MtdCharOsInit: add MtdDev(%s) done!", devName);
549 mtdDevice->osData = mtdDev;
550
551 MtdDeviceLegacyFillMtdInfo(mtdDevice);
552 return HDF_SUCCESS;
553 }
554
MtdCharOsUninit(struct MtdDevice * mtdDevice)555 void MtdCharOsUninit(struct MtdDevice *mtdDevice)
556 {
557 struct MtdDev *mtdDev = NULL;
558 const char *devName = NULL;
559
560 if (mtdDevice == NULL) {
561 HDF_LOGE("MtdCharOsUninit: mtdDevice is null!");
562 return;
563 }
564
565 devName = MtdCharGetName(mtdDevice);
566 if (devName == NULL) {
567 HDF_LOGE("MtdCharOsUninit: devName is null!");
568 return;
569 }
570
571 mtdDev = (struct MtdDev *)mtdDevice->osData;
572 (void)DelMtdList(mtdDev);
573 HDF_LOGI("MtdCharOsUninit: remove MtdDev(%s) done!", devName);
574 }
575
GetMtdCharFops(VOID)576 const struct file_operations_vfs *GetMtdCharFops(VOID)
577 {
578 return &g_MtdCharFops;
579 }
580