GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lx_nand_flash_open_extended.c Lines: 97 130 74.6 %
Date: 2026-01-12 23:08:29 Branches: 46 77 59.7 %

Line Branch Exec Source
1
/***************************************************************************
2
 * Copyright (c) 2024 Microsoft Corporation
3
 *
4
 * This program and the accompanying materials are made available under the
5
 * terms of the MIT License which is available at
6
 * https://opensource.org/licenses/MIT.
7
 *
8
 * SPDX-License-Identifier: MIT
9
 **************************************************************************/
10
11
12
/**************************************************************************/
13
/**************************************************************************/
14
/**                                                                       */
15
/** LevelX Component                                                      */
16
/**                                                                       */
17
/**   NAND Flash                                                          */
18
/**                                                                       */
19
/**************************************************************************/
20
/**************************************************************************/
21
22
#define LX_SOURCE_CODE
23
24
25
/* Disable ThreadX error checking.  */
26
27
#ifndef LX_DISABLE_ERROR_CHECKING
28
#define LX_DISABLE_ERROR_CHECKING
29
#endif
30
31
32
/* Include necessary system files.  */
33
34
#include "lx_api.h"
35
36
37
/**************************************************************************/
38
/*                                                                        */
39
/*  FUNCTION                                               RELEASE        */
40
/*                                                                        */
41
/*    _lx_nand_flash_open                                 PORTABLE C      */
42
/*                                                           6.3.0        */
43
/*  AUTHOR                                                                */
44
/*                                                                        */
45
/*    Xiuwen Cai, Microsoft Corporation                                   */
46
/*                                                                        */
47
/*  DESCRIPTION                                                           */
48
/*                                                                        */
49
/*    This function opens a NAND flash instance and ensures the           */
50
/*    NAND flash is in a coherent state.                                  */
51
/*                                                                        */
52
/*  INPUT                                                                 */
53
/*                                                                        */
54
/*    nand_flash                            NAND flash instance           */
55
/*    name                                  Name of NAND flash instance   */
56
/*    nand_driver_initialize                Driver initialize             */
57
/*    memory_ptr                            Pointer to memory used by the */
58
/*                                            LevelX for this NAND.       */
59
/*    memory_size                           Size of memory - must at least*/
60
/*                                            7 * total block count +     */
61
/*                                            2 * page size               */
62
/*                                                                        */
63
/*  OUTPUT                                                                */
64
/*                                                                        */
65
/*    return status                                                       */
66
/*                                                                        */
67
/*  CALLS                                                                 */
68
/*                                                                        */
69
/*    (nand_driver_initialize)              Driver initialize             */
70
/*    _lx_nand_flash_memory_initialize      Initialize buffer             */
71
/*    _lx_nand_flash_driver_block_status_get                              */
72
/*                                          Get block status              */
73
/*    lx_nand_flash_driver_pages_read       Read pages                    */
74
/*    _lx_nand_flash_free_block_list_add    Add free block to list        */
75
/*    _lx_nand_flash_mapped_block_list_add  Add mapped block to list      */
76
/*    _lx_nand_flash_system_error           System error handler          */
77
/*    tx_mutex_create                       Create thread-safe mutex      */
78
/*                                                                        */
79
/*  CALLED BY                                                             */
80
/*                                                                        */
81
/*    Application Code                                                    */
82
/*                                                                        */
83
/*  RELEASE HISTORY                                                       */
84
/*                                                                        */
85
/*    DATE              NAME                      DESCRIPTION             */
86
/*                                                                        */
87
/*  03-08-2023     Xiuwen Cai               Initial Version 6.2.1         */
88
/*  10-31-2023     Xiuwen Cai               Modified comment(s),          */
89
/*                                            avoided clearing user       */
90
/*                                            extension in flash control  */
91
/*                                            block,                      */
92
/*                                            resulting in version 6.3.0  */
93
/*                                                                        */
94
/**************************************************************************/
95
96
5
UINT  _lx_nand_flash_open_extended(LX_NAND_FLASH  *nand_flash, CHAR *name, UINT (*nand_driver_initialize)(LX_NAND_FLASH *),
97
                          VOID *nand_driver_info_ptr, ULONG* memory_ptr, UINT memory_size)
98
{
99
100
ULONG                       block;
101
ULONG                       page;
102
UCHAR                       block_status;
103
ULONG                       block_count;
104
UINT                        status;
105
LX_NAND_FLASH               *tail_ptr;
106
LX_NAND_DEVICE_INFO         *nand_device_info_page;
107
UCHAR                       *spare_buffer_ptr;
108
UCHAR                       *page_buffer_ptr;
109
ULONG                       page_type;
110
UCHAR                       page_index;
111
LX_INTERRUPT_SAVE_AREA
112
113
    LX_PARAMETER_NOT_USED(name);
114
115
    /* Clear the NAND flash control block. User extension is not cleared.  */
116
1525
    LX_MEMSET(nand_flash, 0, (ULONG)((UCHAR*)&(nand_flash -> lx_nand_flash_open_previous) - (UCHAR*)nand_flash) + sizeof(nand_flash -> lx_nand_flash_open_previous));
117
118
    /* Pass the driver_info_ptr */
119
5
    nand_flash -> lx_nand_flash_driver_info_ptr = nand_driver_info_ptr;
120
121
    /* Call the flash driver's initialization function.  */
122
5
    (nand_driver_initialize)(nand_flash);
123
124
    /* Determine if we can support this NAND flash size.  */
125

5
    if (nand_flash -> lx_nand_flash_pages_per_block > LX_NAND_MAX_PAGE_PER_BLOCK || nand_flash -> lx_nand_flash_total_blocks > LX_NAND_MAX_BLOCK_COUNT)
126
    {
127
128
        /* Return an error.  */
129
        return(LX_ERROR);
130
    }
131
132
    /* Check if it is new LevelX NAND driver.  */
133

5
    if (nand_flash -> lx_nand_flash_driver_pages_read == LX_NULL || nand_flash -> lx_nand_flash_driver_pages_write == LX_NULL || nand_flash -> lx_nand_flash_driver_pages_copy == LX_NULL)
134
    {
135
136
        /* Return an error.  */
137
        return(LX_ERROR);
138
    }
139
140
    /* Check the spare data length.  */
141
5
    if (nand_flash -> lx_nand_flash_spare_data1_length < sizeof(ULONG))
142
    {
143
144
        /* Return an error.  */
145
        return(LX_ERROR);
146
    }
147
148
    /* Calculate the number of words per block and per page.  */
149
5
    nand_flash -> lx_nand_flash_words_per_page =   (nand_flash -> lx_nand_flash_bytes_per_page / sizeof(ULONG));
150
5
    nand_flash -> lx_nand_flash_words_per_block =  (nand_flash -> lx_nand_flash_words_per_page * nand_flash -> lx_nand_flash_pages_per_block);
151
152
    /* Calculate the total pages.  */
153
5
    nand_flash -> lx_nand_flash_total_pages =   nand_flash -> lx_nand_flash_total_blocks * nand_flash -> lx_nand_flash_pages_per_block;
154
155
    /* Initialize memory buffer.  */
156
5
    status = _lx_nand_flash_memory_initialize(nand_flash, memory_ptr, memory_size);
157
5
    if (status != LX_SUCCESS)
158
    {
159
        return(status);
160
    }
161
162
    /* Initialize block numbers.  */
163
5
    nand_flash -> lx_nand_flash_metadata_block_number = LX_NAND_BLOCK_UNMAPPED;
164
5
    nand_flash -> lx_nand_flash_backup_metadata_block_number = LX_NAND_BLOCK_UNMAPPED;
165
166
    /* Setup page buffer and spare buffer pointers.  */
167
5
    page_buffer_ptr = nand_flash -> lx_nand_flash_page_buffer;
168
5
    spare_buffer_ptr = page_buffer_ptr + nand_flash -> lx_nand_flash_bytes_per_page;
169
170
    /* Loop through the blocks to check for bad blocks and determine the minimum and maximum erase count for each good block.  */
171
5
    for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++)
172
    {
173
174
        /* First, check to make sure this block is good.  */
175
5
        status =  _lx_nand_flash_driver_block_status_get(nand_flash, block, &block_status);
176
177
        /* Check for an error from flash driver.   */
178
5
        if (status)
179
        {
180
181
            /* Call system error handler.  */
182
            _lx_nand_flash_system_error(nand_flash, status, block, 0);
183
184
            /* Return an error.  */
185
            return(LX_ERROR);
186
        }
187
188
        /* Is this block bad?  */
189
5
        if (block_status != LX_NAND_GOOD_BLOCK)
190
        {
191
192
            /* Yes, this block is bad.  */
193
194
            /* Increment the number of bad blocks.  */
195
            nand_flash -> lx_nand_flash_bad_blocks++;
196
197
            /* Save the block status.  */
198
            nand_flash -> lx_nand_flash_block_status_table[block] = LX_NAND_BLOCK_STATUS_BAD;
199
200
            /* Continue to the next block.  */
201
            continue;
202
        }
203
204
        /* Call driver read function to read page 0.  */
205
#ifdef LX_NAND_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE
206
        status = (nand_flash -> lx_nand_flash_driver_pages_read)(nand_flash, block, 0, page_buffer_ptr, spare_buffer_ptr, 1);
207
#else
208
5
        status = (nand_flash -> lx_nand_flash_driver_pages_read)(block, 0, page_buffer_ptr, spare_buffer_ptr, 1);
209
#endif
210
211
        /* Check for an error from flash driver.   */
212
5
        if (status)
213
        {
214
215
            /* Call system error handler.  */
216
            _lx_nand_flash_system_error(nand_flash, status, block, 0);
217
218
            /* Determine if the error is fatal.  */
219
            if (status != LX_NAND_ERROR_CORRECTED)
220
            {
221
222
                /* Return an error.  */
223
                return(LX_ERROR);
224
            }
225
        }
226
227
        /* Get the page type.  */
228
5
        page_type = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]);
229
230
        /* Check if the type is device info.  */
231
5
        if (page_type == LX_NAND_PAGE_TYPE_DEVICE_INFO)
232
        {
233
234
            /* Get the device info page.  */
235
5
            nand_device_info_page = (LX_NAND_DEVICE_INFO*)page_buffer_ptr;
236
237
            /* Check signature.  */
238
5
            if (nand_device_info_page -> lx_nand_device_info_signature1 == LX_NAND_DEVICE_INFO_SIGNATURE1 &&
239
5
                nand_device_info_page -> lx_nand_device_info_signature2 == LX_NAND_DEVICE_INFO_SIGNATURE2)
240
            {
241
242
                /* Save the block numbers.  */
243
5
                nand_flash -> lx_nand_flash_metadata_block_number = nand_device_info_page -> lx_nand_device_info_metadata_block_number;
244
5
                nand_flash -> lx_nand_flash_backup_metadata_block_number = nand_device_info_page -> lx_nand_device_info_backup_metadata_block_number;
245
5
                break;
246
            }
247
248
        }
249
250
    }
251
252
    /* Check if we have found the metadata block.  */
253
5
    if (nand_flash -> lx_nand_flash_metadata_block_number == LX_NAND_BLOCK_UNMAPPED)
254
    {
255
256
        /* Not found, return an error.  */
257
        return (LX_ERROR);
258
    }
259
260
    /* Initialize metadata block numbers and lists.  */
261
5
    nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number;
262
5
    nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number;
263
5
    nand_flash -> lx_nand_flash_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_metadata_block_number;
264
5
    nand_flash -> lx_nand_flash_backup_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_backup_metadata_block_number;
265
266
    /* Found one metadata block.  */
267
5
    nand_flash -> lx_nand_flash_metadata_block_count = 1;
268
269
    /* Clear searched block count.  */
270
5
    block_count = 0;
271
272
    do
273
    {
274
275
        /* Initialize next block to unmapped.  */
276
7
        nand_flash -> lx_nand_flash_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED;
277
7
        nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED;
278
279
        /* Loop to read pages in the metadata block.  */
280
601
        for (page = 0; page < nand_flash -> lx_nand_flash_pages_per_block ; page++)
281
        {
282
283
            /* Call driver read function to read page.  */
284
#ifdef LX_NAND_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE
285
            status = (nand_flash -> lx_nand_flash_driver_pages_read)(nand_flash, block, page, page_buffer_ptr, spare_buffer_ptr, 1);
286
#else
287
594
            status = (nand_flash -> lx_nand_flash_driver_pages_read)(block, page, page_buffer_ptr, spare_buffer_ptr, 1);
288
#endif
289
290
            /* Check for an error from flash driver.   */
291
594
            if (status)
292
            {
293
294
                /* Call system error handler.  */
295
                _lx_nand_flash_system_error(nand_flash, status, block, 0);
296
297
                /* Determine if the error is fatal.  */
298
                if (status != LX_NAND_ERROR_CORRECTED)
299
                {
300
301
                    /* Return an error.  */
302
                    return(LX_ERROR);
303
                }
304
            }
305
306
            /* Get page type and page index.  */
307
594
            page_type = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]) & (~LX_NAND_PAGE_TYPE_PAGE_NUMBER_MASK);
308
594
            page_index = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]) & LX_NAND_PAGE_TYPE_PAGE_NUMBER_MASK;
309
310
            /* Process metadata page by type.  */
311

594
            switch (page_type)
312
            {
313
5
            case LX_NAND_PAGE_TYPE_DEVICE_INFO:
314
315
                /* This page is for device info.  */
316
5
                nand_device_info_page = (LX_NAND_DEVICE_INFO*)page_buffer_ptr;
317
318
                /* Get the base erase count.  */
319
5
                nand_flash -> lx_nand_flash_base_erase_count = nand_device_info_page -> lx_nand_device_info_base_erase_count;
320
5
                break;
321
322
10
            case LX_NAND_PAGE_TYPE_ERASE_COUNT_TABLE:
323
324
                /* Check if page index is valid.  */
325
10
                if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_erase_count_table_size)
326
                {
327
328
                    /* Invalid page index. Return an error.  */
329
                    status = LX_ERROR;
330
                    break;
331
                }
332
333
                /* Copy page data to erase count table.  */
334
10
                LX_MEMCPY(nand_flash -> lx_nand_flash_erase_count_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page, /* Use case of memcpy is verified. */
335
                            page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page);
336
10
                break;
337
338
23
            case LX_NAND_PAGE_TYPE_BLOCK_MAPPING_TABLE:
339
340
                /* Check if page index is valid.  */
341
23
                if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_block_mapping_table_size)
342
                {
343
344
                    /* Invalid page index. Return an error.  */
345
                    status = LX_ERROR;
346
                    break;
347
                }
348
349
                /* Copy page data to block mapping table.  */
350
23
                LX_MEMCPY(nand_flash -> lx_nand_flash_block_mapping_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page / sizeof(*nand_flash -> lx_nand_flash_block_mapping_table), /* Use case of memcpy is verified. */
351
                    page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page);
352
23
                break;
353
354
544
            case LX_NAND_PAGE_TYPE_BLOCK_STATUS_TABLE:
355
356
                /* Check if page index is valid.  */
357
544
                if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_block_status_table_size)
358
                {
359
360
                    /* Invalid page index. Return an error.  */
361
                    status = LX_ERROR;
362
                    break;
363
                }
364
365
                /* Copy page data to block status table.  */
366
544
                LX_MEMCPY(nand_flash -> lx_nand_flash_block_status_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page / sizeof(*nand_flash -> lx_nand_flash_block_status_table), /* Use case of memcpy is verified. */
367
                    page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page);
368
544
                break;
369
370
5
            case LX_NAND_PAGE_TYPE_FREE_PAGE:
371
372
                /* Found a free page. Update current page.  */
373
5
                nand_flash -> lx_nand_flash_metadata_block_current_page = page;
374
5
                nand_flash -> lx_nand_flash_backup_metadata_block_current_page = page;
375
376
                /* Skip all the remaining pages.  */
377
5
                page = nand_flash -> lx_nand_flash_pages_per_block;
378
5
                break;
379
380
7
            case LX_NAND_PAGE_TYPE_BLOCK_LINK:
381
382
                /* Found next blocks. Update next block numbers.  */
383
7
                nand_flash -> lx_nand_flash_metadata_block_number_next = LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET]);
384
7
                nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET]);
385
386
                /* Save block numbers in metadata block lists.  */
387
7
                nand_flash -> lx_nand_flash_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET]);
388
7
                nand_flash -> lx_nand_flash_backup_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET]);
389
390
                /* Increase metadata block count.  */
391
7
                nand_flash -> lx_nand_flash_metadata_block_count++;
392
393
7
                break;
394
395
            default:
396
397
                /* Unknown type, return error.  */
398
                status = LX_ERROR;
399
            }
400
401
            /* Check status.  */
402
594
            if (status == LX_ERROR)
403
            {
404
405
                /* Error, break the loop.  */
406
                break;
407
            }
408
409
        }
410
411
        /* Check if we have reached the last page.  */
412
7
        if (page == nand_flash -> lx_nand_flash_pages_per_block)
413
        {
414
415
            /* Move to next block.  */
416
2
            nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number_next;
417
2
            nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number_next;
418
419
            /* Make sure the block is valid.  */
420
2
            if (nand_flash -> lx_nand_flash_metadata_block_number_current == LX_NAND_BLOCK_UNMAPPED)
421
            {
422
423
                /* Error, break the loop.  */
424
                break;
425
            }
426
427
            /* Get the block to process.  */
428
2
            block = nand_flash -> lx_nand_flash_metadata_block_number_current;
429
        }
430
431
        /* Increase the processed block number.  */
432
7
        block_count++;
433
434
        /* If block count is larger than total blocks, there is an error.  */
435
7
        if (block_count >= nand_flash -> lx_nand_flash_total_blocks)
436
        {
437
438
            /* Break the loop.  */
439
            break;
440
        }
441

7
    } while (nand_flash -> lx_nand_flash_metadata_block_current_page == 0 && status != LX_ERROR);
442
443
    /* Check if metadata page is processed correctly.  */
444

5
    if (nand_flash -> lx_nand_flash_metadata_block_current_page == 0 || status == LX_ERROR)
445
    {
446
447
        /* Return an error.  */
448
        return(LX_ERROR);
449
    }
450
451
    /* Loop to build free and mapped block lists.  */
452
5125
    for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++)
453
    {
454
455
        /* Check for free blocks.  */
456
5120
        if (nand_flash -> lx_nand_flash_block_status_table[block] == LX_NAND_BLOCK_STATUS_FREE)
457
        {
458
459
            /* Add the block to free block list.  */
460
5093
            _lx_nand_flash_free_block_list_add(nand_flash, block);
461
        }
462
463
        /* Check for mapped blocks.  */
464
5120
        if (nand_flash -> lx_nand_flash_block_mapping_table[block] != LX_NAND_BLOCK_UNMAPPED)
465
        {
466
467
            /* Add the block to free block list.  */
468
3
            _lx_nand_flash_mapped_block_list_add(nand_flash, block);
469
        }
470
    }
471
472
473
#ifdef LX_THREAD_SAFE_ENABLE
474
475
    /* If the thread safe option is enabled, create a ThreadX mutex that will be used in all external APIs
476
       in order to provide thread-safe operation.  */
477
    status =  tx_mutex_create(&nand_flash -> lx_nand_flash_mutex, "NAND Flash Mutex", TX_NO_INHERIT);
478
479
    /* Determine if the mutex creation encountered an error.  */
480
    if (status != LX_SUCCESS)
481
    {
482
483
        /* Call system error handler, since this should not happen.  */
484
        _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_MUTEX_CREATE_FAILED, 0, 0);
485
486
        /* Return error to caller.  */
487
        return(LX_ERROR);
488
    }
489
#endif
490
491
    /* Lockout interrupts.  */
492
5
    LX_DISABLE
493
494
    /* At this point, the NAND flash has been opened successfully.  Place the
495
       NAND flash control block on the linked list of currently opened NAND flashes.  */
496
497
    /* Set the NAND flash state to open.  */
498
5
    nand_flash -> lx_nand_flash_state =  LX_NAND_FLASH_OPENED;
499
500
    /* Place the NAND flash control block on the list of opened NAND flashes.  First,
501
       check for an empty list.  */
502
5
    if (_lx_nand_flash_opened_count)
503
    {
504
505
        /* List is not empty - other NAND flashes are open.  */
506
507
        /* Pickup tail pointer.  */
508
        tail_ptr =  _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous;
509
510
        /* Place the new NAND flash control block in the list.  */
511
        _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous =  nand_flash;
512
        tail_ptr -> lx_nand_flash_open_next =                       nand_flash;
513
514
        /* Setup this NAND flash's opened links.  */
515
        nand_flash -> lx_nand_flash_open_previous =  tail_ptr;
516
        nand_flash -> lx_nand_flash_open_next =      _lx_nand_flash_opened_ptr;
517
    }
518
    else
519
    {
520
521
        /* The opened NAND flash list is empty.  Add the NAND flash to empty list.  */
522
5
        _lx_nand_flash_opened_ptr =                 nand_flash;
523
5
        nand_flash -> lx_nand_flash_open_next =      nand_flash;
524
5
        nand_flash -> lx_nand_flash_open_previous =  nand_flash;
525
    }
526
527
    /* Increment the opened NAND flash counter.  */
528
5
    _lx_nand_flash_opened_count++;
529
530
    /* Restore interrupts.  */
531
5
    LX_RESTORE
532
533
    /* Return a successful completion.  */
534
5
    return(LX_SUCCESS);
535
}
536