GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lx_nand_flash_open_extended.c Lines: 97 130 74.6 %
Date: 2026-03-06 18:45:40 Branches: 46 77 59.7 %

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

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)
116
    {
117
118
        /* Return an error.  */
119
        return(LX_ERROR);
120
    }
121
122
    /* Check if it is new LevelX NAND driver.  */
123

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

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

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

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