---
tags: Linux Kernel Module, System Software, 作業系統, flash storage
---
# 今晚我想來點不一樣的 Driver: Flash Device
:::info
:lock: 進度: 先用不完整的英文快速記錄一下內容,有空再用中文把細節寫情楚
:::
## Quick introduction of flash
[SSD for beginners](https://hackmd.io/@RinHizakura/Hy89c4sFF)
- [ ] TODO: introduce some proper noun for flash
## MTD device
- [ ] TODO: Organize the following material
[Linux MTD架構下的nand flash驅動詳解](https://www.twblogs.net/a/5cbf46d9bd9eee397113c2e6)
[Linux mtd子系统专栏分析之三 MTD层相关接口说明(注册与注销等)](https://blog.csdn.net/lickylin/article/details/104760367)
[MTD Subsystem](https://blog.totorow.xyz/posts/mtd/)
[MTD技术介绍](https://www.cnblogs.com/cherishui/p/4232521.html)
## NAND flash driver example: nandsim
`nandsim` 是 kernel 中一個可以模擬 flash 運作的 module,我們可以把它掛起來並通過 `mtd-utils` 去做測試。
> * [linux-mtd.infradead](http://www.linux-mtd.infradead.org/faq/nand.html)
> * [MTD nandsim HOWTO for yaffs users](http://aleph1.co.uk/pipermail/yaffs/2005q3/001356.html)
> * [TC58TEG6DDKTA00 DDR1.0 Datasheet](https://datasheetspdf.com/datasheet/TC58TEG6DDKTA00.html)
```
sudo nandwrite -p /dev/mtd0 a.txt
flash_erase /dev/mtd0 0x5000 1
```
version based on linux-v5.16-rc6
### [`ns_init_module`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L2256)
```cpp
static int __init ns_init_module(void)
{
struct list_head *pos, *n;
struct nand_chip *chip;
struct nandsim *ns;
int ret;
if (bus_width != 8 && bus_width != 16) {
NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
return -EINVAL;
}
ns = kzalloc(sizeof(struct nandsim), GFP_KERNEL);
if (!ns) {
NS_ERR("unable to allocate core structures.\n");
return -ENOMEM;
}
chip = &ns->chip;
nsmtd = nand_to_mtd(chip);
nand_set_controller_data(chip, (void *)ns);
```
* `bus_width` is module parameters, default to `CONFIG_NANDSIM_BUS_WIDTH(== 8)`
* [`nandsim`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L286) is the main structure, which include
* [`nand_chip`](https://elixir.bootlin.com/linux/v5.16-rc6/source/include/linux/mtd/rawnand.h#L1255): NAND Private Flash Chip Data, describe the general feature of NAND
* [`nand_controller`](https://elixir.bootlin.com/linux/v5.16-rc6/source/include/linux/mtd/rawnand.h#L1098): Structure used to describe a NAND controller, contain some callback function to be registered
* `kzalloc` is required since [`nand_cleanup`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_base.c#L6353) free all the posible space point by the internal pointer. So we have to make sure each pointer is `NULL` at the beginning except of random values
* The following figure show the last 3 lines

```cpp
/* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
/* and 'badblocks' parameters to work */
chip->options |= NAND_SKIP_BBTSCAN;
switch (bbt) {
case 2:
chip->bbt_options |= NAND_BBT_NO_OOB;
fallthrough;
case 1:
chip->bbt_options |= NAND_BBT_USE_FLASH;
fallthrough;
case 0:
break;
default:
NS_ERR("bbt has to be 0..2\n");
ret = -EINVAL;
goto free_ns_struct;
}
```
* Since we may overide the size and allocate bad block table ourself, `chip->options |= NAND_SKIP_BBTSCAN` is requried to skip the bad block allocation in `nand_scan_tail`, or we may meet memory leak there
* Define the way of bad block table storage(which is [`nand_create_bbt`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_bbt.c#L1398))
* note for the `fallthrough`: if we set `bbt == 2`, `chip->bbt_options` will be `NAND_BBT_NO_OOB | NAND_BBT_USE_FLASH` (or we might meet this [BUG_ON](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_bbt.c#L1205))
```cpp
/*
* Perform minimum nandsim structure initialization to handle
* the initial ID read command correctly
*/
if (id_bytes[6] != 0xFF || id_bytes[7] != 0xFF)
ns->geom.idbytes = 8;
else if (id_bytes[4] != 0xFF || id_bytes[5] != 0xFF)
ns->geom.idbytes = 6;
else if (id_bytes[2] != 0xFF || id_bytes[3] != 0xFF)
ns->geom.idbytes = 4;
else
ns->geom.idbytes = 2;
ns->regs.status = NS_STATUS_OK(ns);
ns->nxstate = STATE_UNKNOWN;
ns->options |= OPT_PAGE512; /* temporary value */
memcpy(ns->ids, id_bytes, sizeof(ns->ids));
if (bus_width == 16) {
ns->busw = 16;
chip->options |= NAND_BUSWIDTH_16;
}
nsmtd->owner = THIS_MODULE;
```
more initialization here, some explaination:
* `ns->geom.idbytes` decides howmay `idbytes` are used for our simulated nand
* `ns->regs.status`: `NS_STATUS_OK == (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0)))`
* `NAND_STATUS_READY`: byte 6 is the busy/ready status
* `NAND_STATUS_WP`: byte 7 is write protect or not
* `ns->nxstate`: next state for state switch, initialized to `STATE_UNKNOWN`
* `ns->options`: only a temp value, will be initialized again in `ns_init`
```cpp
ret = ns_parse_weakblocks();
if (ret)
goto free_ns_struct;
ret = ns_parse_weakpages();
if (ret)
goto free_wb_list;
ret = ns_parse_gravepages();
if (ret)
goto free_wp_list;
```
* one can use `weakblocks` parameter to genearate a block which will soon be latter bad after few times of write(erase), [`ns_parse_weakblocks`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L827) response for the work
* [`ns_parse_weakpages`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L878) and [`ns_parse_gravepages`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L929) has similar behavior, see those [description](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L149) for more information
```cpp
nand_controller_init(&ns->base);
ns->base.ops = &ns_controller_ops;
chip->controller = &ns->base;
```
The following figure show the relationship after setting

```cpp
ret = nand_scan(chip, 1);
if (ret) {
NS_ERR("Could not scan NAND Simulator device\n");
goto free_gp_list;
}
```
In `nand_controller_ops` structure, we have [`nand_attach`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_base.c#L6294) which will be executed in the middle of scan stage. We also have [`ns_exec_op`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L2166), which response for any command sending.
```cpp
if (overridesize) {
uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize;
struct nand_memory_organization *memorg;
u64 targetsize;
memorg = nanddev_get_memorg(&chip->base);
if (new_size >> overridesize != nsmtd->erasesize) {
NS_ERR("overridesize is too big\n");
ret = -EINVAL;
goto cleanup_nand;
}
/* N.B. This relies on nand_scan not doing anything with the size before we change it */
nsmtd->size = new_size;
memorg->eraseblocks_per_lun = 1 << overridesize;
targetsize = nanddev_target_size(&chip->base);
chip->chip_shift = ffs(nsmtd->erasesize) + overridesize - 1;
chip->pagemask = (targetsize >> chip->page_shift) - 1;
}
```
`overridesize` is used for overide the NAND flash size which is configured by `idbytes`, which is the multiple of erase size(default block size).
* some field related to the change should be fixed
```cpp
ret = ns_setup_wear_reporting(nsmtd);
if (ret)
goto cleanup_nand;
ret = ns_init(nsmtd);
if (ret)
goto free_ebw;
ret = nand_create_bbt(chip);
if (ret)
goto free_ns_object;
ret = ns_parse_badblocks(ns, nsmtd);
if (ret)
goto free_ns_object;
/* Register NAND partitions */
ret = mtd_device_register(nsmtd, &ns->partitions[0], ns->nbparts);
if (ret)
goto free_ns_object;
ret = ns_debugfs_create(ns);
if (ret)
goto unregister_mtd;
return 0;
```
* [`ns_setup_wear_reporting`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L980): initialize the `erase_block_wear` for the recording of block wearing
* [`ns_init`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L637):
* Initialize the `geom` infomation from `mtd_info`
* Fill the `partition_info` structure
* [`ns_alloc_device`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L533) for the allocating of page pointers array
* [`nand_create_bbt`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_bbt.c#L1398): create bad block table and scan for the update
* [`ns_parse_badblocks`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L799): parse the badblocks paremters and update with [`mtd_block_markbad`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/mtdcore.c#L2185)
* [`mtd_device_register`](https://elixir.bootlin.com/linux/v5.16-rc6/source/include/linux/mtd/mtd.h#L679): finally, we parse partitions and register an MTD device.
* [`ns_debugfs_create`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L497): register debugfs to provide debug information
* where's the definition of `ns_fops`? It comes from [`DEFINE_SHOW_ATTRIBUTE(ns)`](https://elixir.bootlin.com/linux/v5.16-rc6/source/include/linux/seq_file.h#L195) which registers `ns_show` for debugfs
That's it! Then remain core is the function [`ns_exec_op`](#ns_exec_op).
### [`nand_scan`](https://elixir.bootlin.com/linux/v5.16-rc6/source/include/linux/mtd/rawnand.h#L1547)
After setting some members in `nand_chip` structure, we can start the scan process. It fills out all the uninitialized function pointers with the defaults value.
```cpp
static inline int nand_scan(struct nand_chip *chip, unsigned int max_chips)
{
return nand_scan_with_ids(chip, max_chips, NULL);
}
int nand_scan_with_ids(struct nand_chip *chip, unsigned int maxchips,
struct nand_flash_dev *ids)
{
int ret;
if (!maxchips)
return -EINVAL;
ret = nand_scan_ident(chip, maxchips, ids);
if (ret)
return ret;
ret = nand_attach(chip);
if (ret)
goto cleanup_ident;
ret = nand_scan_tail(chip);
if (ret)
goto detach_chip;
return 0;
detach_chip:
nand_detach(chip);
cleanup_ident:
nand_scan_ident_cleanup(chip);
return ret;
}
```
For `nand_scan`, it is actually seperated to two stage of scanning(`nand_scan_ident` and `nand_scan_tail`). The reason here is because:
> This separation prevented dynamic allocations during this phase which was unconvenient and as been banned for the benefit of the ->init_ecc()/cleanup_ecc() hooks.
The first stage of scanning is [`nand_scan_ident`](#nand_scan_ident).
For some ECC configuration, we may have to scan the flash device for some information first. Also, other informations may need the ECC configuration during scan. So, after the first stage scanning, we use [`nand_attach`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_base.c#L6294) callback to [`ns_attach_chip`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L2209) after the NAND detection phase after flash ID and MTD fields such as erase size, page size and OOB size have been set up.
Then we can continue for the second stage, [`nand_scan_tail`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_base.c#L5987):
* Allocate the `data_buf` in `nand_chip`
* oob buffer(`oob_poi`) will point to the position in `data_buf`(after the page data)
* `mtd_set_ooblayout` for how we use oob for ecc, and also other ecc setting is done here
* [`nanddev_init`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/core.c#L345) initialized the nand device
* Fill in remaining MTD driver data
* [`nand_create_bbt`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_bbt.c#L1398) create the bad block table
### [`nand_scan_ident`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_base.c#L5355)
```cpp
static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
struct nand_flash_dev *table)
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_memory_organization *memorg;
int nand_maf_id, nand_dev_id;
unsigned int i;
int ret;
memorg = nanddev_get_memorg(&chip->base);
/* Assume all dies are deselected when we enter nand_scan_ident(). */
chip->cur_cs = -1;
mutex_init(&chip->lock);
/* Enforce the right timings for reset/detection */
chip->current_interface_config = nand_get_reset_interface_config();
ret = rawnand_dt_init(chip);
if (ret)
return ret;
if (!mtd->name && mtd->dev.parent)
mtd->name = dev_name(mtd->dev.parent);
/* Set the default functions */
nand_set_defaults(chip);
ret = nand_legacy_check_hooks(chip);
if (ret)
return ret;
memorg->ntargets = maxchips;
```
* [`nand_get_reset_interface_config`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_timings.c#L551): All NAND chips share the same reset data interface: SDR mode 0?
* [`rawnand_dt_init`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_base.c#L5297): user can provide some config by device node
* always return 0?
* set `mtd->name` to its parent device if exist
* `nand_set_defaults`: If no controller is provided, use the dummy controller. Also init `chip->buf_align` if not provided, which is the minimum buffer alignment of `nand_chip`
* [`nand_legacy_check_hooks`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_legacy.c#L623): it just provide some checking for legacy API
```cpp
ret = nand_detect(chip, table);
if (ret) {
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
pr_warn("No NAND device found\n");
nand_deselect_target(chip);
return ret;
}
nand_maf_id = chip->id.data[0];
nand_dev_id = chip->id.data[1];
nand_deselect_target(chip);
```
* [`nand_detect`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_base.c#L4923): compare the manufacturer id and device id
* It will send **RESET** operation and other commands. Remember that all the command sending will fall in callback [`ns_exec_op`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L2166) finally. So we have to implemented those for our flash driver
* The comparison will use default table [`nand_flash_ids`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nand_ids.c#L23) if we don't specific any
```cpp
/* Check for a chip array */
for (i = 1; i < maxchips; i++) {
u8 id[2];
/* See comment in nand_get_flash_type for reset */
ret = nand_reset(chip, i);
if (ret)
break;
nand_select_target(chip, i);
/* Send the command for reading device ID */
ret = nand_readid_op(chip, 0, id, sizeof(id));
if (ret)
break;
/* Read manufacturer and device IDs */
if (nand_maf_id != id[0] || nand_dev_id != id[1]) {
nand_deselect_target(chip);
break;
}
nand_deselect_target(chip);
}
if (i > 1)
pr_info("%d chips detected\n", i);
/* Store the number of chips and calc total size for mtd */
memorg->ntargets = i;
mtd->size = i * nanddev_target_size(&chip->base);
return 0;
}
```
We have to handle every target.
### [`ns_exec_op`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L2166)
```cpp
static int ns_exec_op(struct nand_chip *chip, const struct nand_operation *op,
bool check_only)
{
int i;
unsigned int op_id;
const struct nand_op_instr *instr = NULL;
struct nandsim *ns = nand_get_controller_data(chip);
if (check_only)
return 0;
ns->lines.ce = 1;
```
When OS trigger a operation, `ns_exec_op` is called and do for the required task. First, nandsim pull high the chip enable(`ce`).
```cpp
for (op_id = 0; op_id < op->ninstrs; op_id++) {
instr = &op->instrs[op_id];
ns->lines.cle = 0;
ns->lines.ale = 0;
switch (instr->type) {
case NAND_OP_CMD_INSTR:
ns->lines.cle = 1;
ns_nand_write_byte(chip, instr->ctx.cmd.opcode);
break;
case NAND_OP_ADDR_INSTR:
ns->lines.ale = 1;
for (i = 0; i < instr->ctx.addr.naddrs; i++)
ns_nand_write_byte(chip, instr->ctx.addr.addrs[i]);
break;
case NAND_OP_DATA_IN_INSTR:
ns_nand_read_buf(chip, instr->ctx.data.buf.in, instr->ctx.data.len);
break;
case NAND_OP_DATA_OUT_INSTR:
ns_nand_write_buf(chip, instr->ctx.data.buf.out, instr->ctx.data.len);
break;
case NAND_OP_WAITRDY_INSTR:
/* we are always ready */
break;
}
}
return 0;
}
```
The remain conecept of each for loop is simple: first we pull low command latch enable(`cle`) and address latch enable(`ale`), then we figure the instruction type then do the corresponding work.
* `case NAND_OP_CMD_INSTR`: a command is ready to be input, raise the `cle` and [`ns_nand_write_byte`](#ns_nand_write_byte) (we'll only trace this and ignore other case now since some concept are very similar)
* `case NAND_OP_ADDR_INSTR`: a address is ready to be input, raise the `ale` and input every bytes one by one
* `case NAND_OP_DATA_IN_INSTR`: [`ns_nand_read_buf`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L2120) to data
* `case NAND_OP_DATA_OUT_INSTR`: [`ns_nand_write_buf`](https://elixir.bootlin.com/linux/v5.16-rc6/C/ident/ns_nand_write_buf) to write data
### [`ns_nand_write_byte`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L1935)
```cpp
static void ns_nand_write_byte(struct nand_chip *chip, u_char byte)
{
struct nandsim *ns = nand_get_controller_data(chip);
/* Sanity and correctness checks */
if (!ns->lines.ce) {
NS_ERR("write_byte: chip is disabled, ignore write\n");
return;
}
if (ns->lines.ale && ns->lines.cle) {
NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n");
return;
}
```
Check the signal first. We can see that `ale` and `cle` is not abled to be high at the same time.
```cpp
if (ns->lines.cle == 1) {
/*
* The byte written is a command.
*/
if (byte == NAND_CMD_RESET) {
NS_LOG("reset chip\n");
ns_switch_to_ready_state(ns, NS_STATUS_OK(ns));
return;
}
/* Check that the command byte is correct */
if (ns_check_command(byte)) {
NS_ERR("write_byte: unknown command %#x\n", (uint)byte);
return;
}
```
For `cle == 1` and `ale == 0`, we latch the command in our simulated flash.
* For commmand `NAND_CMD_RESET`, state machine change the state to `READY` with `OK` status. Also reset the register.
* Otherwise we check if the command is valid
```cpp
if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS
|| NS_STATE(ns->state) == STATE_DATAOUT) {
int row = ns->regs.row;
ns_switch_state(ns);
if (byte == NAND_CMD_RNDOUT)
ns->regs.row = row;
}
```
If the command if received in page output(`STATE_DATAOUT`) or status output(`STATE_DATAOUT_STATUS`) state
* if that's a random output command, we should restore the row address
```cpp
/* Check if chip is expecting command */
if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) {
/* Do not warn if only 2 id bytes are read */
if (!(ns->regs.command == NAND_CMD_READID &&
NS_STATE(ns->state) == STATE_DATAOUT_ID && ns->regs.count == 2)) {
/*
* We are in situation when something else (not command)
* was expected but command was input. In this case ignore
* previous command(s)/state(s) and accept the last one.
*/
NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, ignore previous states\n",
(uint)byte,
ns_get_state_name(ns->nxstate));
}
ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
}
NS_DBG("command byte corresponding to %s state accepted\n",
ns_get_state_name(ns_get_state_by_command(byte)));
ns->regs.command = byte;
ns_switch_state(ns);
```
* If we are in situation when something expect command was expected but command was input. We ignore previous command(s)/state(s) and accept the last/new one, trigger warning message.
* In general, latch the command and do state switching
```cpp
} else if (ns->lines.ale == 1) {
/*
* The byte written is an address.
*/
if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) {
NS_DBG("write_byte: operation isn't known yet, identify it\n");
if (ns_find_operation(ns, 1) < 0)
return;
if ((ns->state & ACTION_MASK) &&
ns_do_state_action(ns, ns->state) < 0) {
ns_switch_to_ready_state(ns,
NS_STATUS_FAILED(ns));
return;
}
ns->regs.count = 0;
switch (NS_STATE(ns->nxstate)) {
case STATE_ADDR_PAGE:
ns->regs.num = ns->geom.pgaddrbytes;
break;
case STATE_ADDR_SEC:
ns->regs.num = ns->geom.secaddrbytes;
break;
case STATE_ADDR_ZERO:
ns->regs.num = 1;
break;
default:
BUG();
}
}
```
For cle == 0 and ale == 1, we latch the address in our simulated flash.
* we'll have to figure the operation first if it is unknown
* task is similar to address state of `ns_switch_state`
```cpp
/* Check that chip is expecting address */
if (!(ns->nxstate & STATE_ADDR_MASK)) {
NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, switch to STATE_READY\n",
(uint)byte, ns_get_state_name(ns->nxstate));
ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
/* Check if this is expected byte */
if (ns->regs.count == ns->regs.num) {
NS_ERR("write_byte: no more address bytes expected\n");
ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
ns_accept_addr_byte(ns, byte);
ns->regs.count += 1;
NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n",
(uint)byte, ns->regs.count, ns->regs.num);
if (ns->regs.count == ns->regs.num) {
NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column);
ns_switch_state(ns);
}
```
* `ns->nxstate & STATE_ADDR_MASK` should have value for the right state
* note that here we check `nxstate` instead of `state`, see how [`ns_find_operation`](#ns_find_operation) change the state for address operation
* `ns->regs.count == ns->regs.num` should be false before we take the byte
* [`ns_accept_addr_byte`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L1142) take the bytes and store in the corresponding register
* update `ns->regs.count`(+1)
* if `ns->regs.count == ns->regs.num` is true, it means all of the address bytes are stored in register now and ready for the next state, switch
```cpp
} else {
/*
* The byte written is an input data.
*/
/* Check that chip is expecting data input */
if (!(ns->state & STATE_DATAIN_MASK)) {
NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, switch to %s\n",
(uint)byte, ns_get_state_name(ns->state),
ns_get_state_name(STATE_READY));
ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
/* Check if this is expected byte */
if (ns->regs.count == ns->regs.num) {
NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n",
ns->regs.num);
return;
}
if (ns->busw == 8) {
ns->buf.byte[ns->regs.count] = byte;
ns->regs.count += 1;
} else {
ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte);
ns->regs.count += 2;
}
}
return;
```
For cle == 0 and ale == 0, our simulated flash is ready to be input data.
* similarly, `ns->state & STATE_DATAIN_MASK` to check if we are in DATAIN state
* similarly, `ns->regs.count == ns->regs.num` should be false before we take the byte
* Then we can take bytes into our byte buffer!
### [`ns_switch_state`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L1725)
```cpp
static void ns_switch_state(struct nandsim *ns)
{
if (ns->op) {
/*
* The current operation have already been identified.
* Just follow the states chain.
*/
ns->stateidx += 1;
ns->state = ns->nxstate;
ns->nxstate = ns->op[ns->stateidx + 1];
NS_DBG("switch_state: operation is known, switch to the next state, "
"state: %s, nxstate: %s\n",
ns_get_state_name(ns->state),
ns_get_state_name(ns->nxstate));
/* See, whether we need to do some action */
if ((ns->state & ACTION_MASK) &&
ns_do_state_action(ns, ns->state) < 0) {
ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
```
* If the current operation have already been identified, we just follow the states chain.
* We should do some action if in the ACTION state(by [`ns_do_state_action`](#ns_do_state_action)), and if failed, report the fail status and reset state to ready
```cpp
else {
/*
* We don't yet know which operation we perform.
* Try to identify it.
*/
/*
* The only event causing the switch_state function to
* be called with yet unknown operation is new command.
*/
ns->state = ns_get_state_by_command(ns->regs.command);
NS_DBG("switch_state: operation is unknown, try to find it\n");
if (ns_find_operation(ns, 0))
return;
if ((ns->state & ACTION_MASK) &&
ns_do_state_action(ns, ns->state) < 0) {
ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
}
```
* If we don't know which operation is going to be done, we first initialize state by the command and try to find it.
```cpp
if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) {
NS_DBG("switch_state: double the column number for 16x device\n");
ns->regs.column <<= 1;
}
```
* 16 bits bus?
```c
if (NS_STATE(ns->nxstate) == STATE_READY) {
/*
* The current state is the last. Return to STATE_READY
*/
u_char status = NS_STATUS_OK(ns);
/* In case of data states, see if all bytes were input/output */
if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK))
&& ns->regs.count != ns->regs.num) {
NS_WARN("switch_state: not all bytes were processed, %d left\n",
ns->regs.num - ns->regs.count);
status = NS_STATUS_FAILED(ns);
}
NS_DBG("switch_state: operation complete, switch to STATE_READY state\n");
ns_switch_to_ready_state(ns, status);
return;
```
If the next state is ready, then we are in the last state. Check for the setting of final status.
```cpp
} else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) {
/*
* If the next state is data input/output, switch to it now
*/
ns->state = ns->nxstate;
ns->nxstate = ns->op[++ns->stateidx + 1];
ns->regs.num = ns->regs.count = 0;
NS_DBG("switch_state: the next state is data I/O, switch, "
"state: %s, nxstate: %s\n",
ns_get_state_name(ns->state),
ns_get_state_name(ns->nxstate));
/*
* Set the internal register to the count of bytes which
* are expected to be input or output
*/
switch (NS_STATE(ns->state)) {
case STATE_DATAIN:
case STATE_DATAOUT:
ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
break;
case STATE_DATAOUT_ID:
ns->regs.num = ns->geom.idbytes;
break;
case STATE_DATAOUT_STATUS:
ns->regs.count = ns->regs.num = 0;
break;
default:
NS_ERR("switch_state: BUG! unknown data state\n");
}
```
if the next is DATAIN / DATAOUT, switch to next state again. Also set `ns->regs.num`(following bytes for data) and reset `ns->regs.count`
```cpp
} else if (ns->nxstate & STATE_ADDR_MASK) {
/*
* If the next state is address input, set the internal
* register to the number of expected address bytes
*/
ns->regs.count = 0;
switch (NS_STATE(ns->nxstate)) {
case STATE_ADDR_PAGE:
ns->regs.num = ns->geom.pgaddrbytes;
break;
case STATE_ADDR_SEC:
ns->regs.num = ns->geom.secaddrbytes;
break;
case STATE_ADDR_ZERO:
ns->regs.num = 1;
break;
case STATE_ADDR_COLUMN:
/* Column address is always 2 bytes */
ns->regs.num = ns->geom.pgaddrbytes - ns->geom.secaddrbytes;
break;
default:
NS_ERR("switch_state: BUG! unknown address state\n");
}
```
* if the next state is ADDR, prepare `ns->regs.num`(following bytes for address) and reset `ns->regs.count`
* Do we repeat those work of `ns_nand_write_byte` when `ale==1`? (just `ns_switch_state`, the different could be the changing of state)
```cpp
} else {
/*
* Just reset internal counters.
*/
ns->regs.num = 0;
ns->regs.count = 0;
}
}
```
Or the state is `STATE_CMD_*`, we just reset some registers.
* maybe we don't have to do this?
### [`ns_find_operation`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L1220)
```cpp
static int ns_find_operation(struct nandsim *ns, uint32_t flag)
{
int opsfound = 0;
int i, j, idx = 0;
for (i = 0; i < NS_OPER_NUM; i++) {
int found = 1;
if (!(ns->options & ops[i].reqopts))
/* Ignore operations we can't perform */
continue;
if (flag) {
if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK))
continue;
} else {
if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates]))
continue;
}
for (j = 0; j < ns->npstates; j++)
if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j])
&& (ns->options & ops[idx].reqopts)) {
found = 0;
break;
}
if (found) {
idx = i;
opsfound += 1;
}
}
```
Update `ns->op` if possible
* [`ops`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L364) defines the required option and state chain for each operation.
* if flag:
* is 0: `ns_find_operation` called when non-address as the input, and the find will result in the following pattern `ns->pstates[0], ... ns->pstates[ns->npstates], ns->state`
* is 1: `ns_find_operation` called when address as the input, as soon as address command is accepted, the operation must be known, and the find will result in the pattern `ns->pstates[0], ... ns->pstates[ns->npstates], <address input>`
* We'll found the operation if the state chain is match
```cpp
if (opsfound == 1) {
/* Exact match */
ns->op = &ops[idx].states[0];
if (flag) {
/*
* In this case the find_operation function was
* called when address has just began input. But it isn't
* yet fully input and the current state must
* not be one of STATE_ADDR_*, but the STATE_ADDR_*
* state must be the next state (ns->nxstate).
*/
ns->stateidx = ns->npstates - 1;
} else {
ns->stateidx = ns->npstates;
}
ns->npstates = 0;
ns->state = ns->op[ns->stateidx];
ns->nxstate = ns->op[ns->stateidx + 1];
NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n",
idx, ns_get_state_name(ns->state),
ns_get_state_name(ns->nxstate));
return 0;
}
```
`if (opsfound == 1)`, we find thee exact operation, set `ns->state` and `ns->nxstate` so we can follow the state chain when `ns_switch_state`
* And reset `ns->npstates`
* If we are going to latch the address(just start), `STATE_ADDR_*` should be the next state
```cpp
if (opsfound == 0) {
/* Nothing was found. Try to ignore previous commands (if any) and search again */
if (ns->npstates != 0) {
NS_DBG("find_operation: no operation found, try again with state %s\n",
ns_get_state_name(ns->state));
ns->npstates = 0;
return ns_find_operation(ns, 0);
}
NS_DBG("find_operation: no operations found\n");
ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return -2;
}
```
* If we are in state chain but nothing was found. Take this command as the first command and search again
* If we are not take as first command but still find no operation, failed
```cpp
if (flag) {
/* This shouldn't happen */
NS_DBG("find_operation: BUG, operation must be known if address is input\n");
return -2;
}
NS_DBG("find_operation: there is still ambiguity\n");
ns->pstates[ns->npstates++] = ns->state;
return -1;
}
```
If several matches and we are taking non-address as the input, we save current state and wait for the next state change. Otherwise it fail.
### [`ns_do_state_action`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L1575)
In `state_switch` phase, after the state changing, we'll do some action first if the state require it.
```cpp
static int ns_do_state_action(struct nandsim *ns, uint32_t action)
{
int num;
int busdiv = ns->busw == 8 ? 1 : 2;
unsigned int erase_block_no, page_no;
action &= ACTION_MASK;
/* Check that page address input is correct */
if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) {
NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row);
return -1;
}
```
Check for some error condition.
```cpp
switch (action) {
case ACTION_CPY:
/*
* Copy page data to the internal buffer.
*/
/* Column shouldn't be very large */
if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) {
NS_ERR("do_state_action: column number is too large\n");
break;
}
num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
ns_read_page(ns, num);
NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
num, NS_RAW_OFFSET(ns) + ns->regs.off);
if (ns->regs.off == 0)
NS_LOG("read page %d\n", ns->regs.row);
else if (ns->regs.off < ns->geom.pgsz)
NS_LOG("read page %d (second half)\n", ns->regs.row);
else
NS_LOG("read OOB of page %d\n", ns->regs.row);
NS_UDELAY(access_delay);
NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv);
break;
```
This action copy page data to the internal buffer.
* `ns->geom.pgszoob - ns->regs.off - ns->regs.column` is the remaining bytes of page for reading
```cpp
case ACTION_SECERASE:
/*
* Erase sector.
*/
if (ns->lines.wp) {
NS_ERR("do_state_action: device is write-protected, ignore sector erase\n");
return -1;
}
if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec
|| (ns->regs.row & ~(ns->geom.secsz - 1))) {
NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row);
return -1;
}
ns->regs.row = (ns->regs.row <<
8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
ns->regs.column = 0;
erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift);
NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
ns->regs.row, NS_RAW_OFFSET(ns));
NS_LOG("erase sector %u\n", erase_block_no);
ns_erase_sector(ns);
NS_MDELAY(erase_delay);
if (erase_block_wear)
ns_update_wear(erase_block_no);
if (ns_erase_error(erase_block_no)) {
NS_WARN("simulating erase failure in erase block %u\n", erase_block_no);
return -1;
}
break;
```
To erase a block, we need to reconstruct the row address first. Then we bit shift to find the block index.
:::warning
The name `ns->regs.row` and `ns->regs.col` will refuse you. `nandsim` assign the received bytes to `row` or `col` according to its arrival count. [`ns_accept_addr_byte`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L1142) assumes the input pattern would be `ns->geom.pgaddrbytes - ns->geom.secaddrbytes` bytes for column address and the remaining for row address. This usually correct in normal situation.
However, for **ERASE** command, only three row address is input. This cause `ns_accept_addr_byte` wrongly assign the bytes to incorrect space. So we have to correct row and column after the following.
```cpp
ns->regs.row = (ns->regs.row <<
8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
ns->regs.column = 0;
```
:::
* The block index can be obtain by shifting `ns->geom.secshift - ns->geom.pgshift` bits, see the following figure and you can understand why

* [`ns_erase_sector`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L1472) erase several page in the block
* We also report the erase by updating [`ns_update_wear`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L998) if needed
```cpp
case ACTION_PRGPAGE:
/*
* Program page - move internal buffer data to the page.
*/
if (ns->lines.wp) {
NS_WARN("do_state_action: device is write-protected, programm\n");
return -1;
}
num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
if (num != ns->regs.count) {
NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n",
ns->regs.count, num);
return -1;
}
if (ns_prog_page(ns, num) == -1)
return -1;
page_no = ns->regs.row;
NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
NS_LOG("programm page %d\n", ns->regs.row);
NS_UDELAY(programm_delay);
NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
if (ns_write_error(page_no)) {
NS_WARN("simulating write failure in page %u\n", page_no);
return -1;
}
break;
```
Nothing special. We already explain many thing before! The core function is in [`ns_prog_page`](https://elixir.bootlin.com/linux/v5.16-rc6/source/drivers/mtd/nand/raw/nandsim.c#L1500).
```cpp
case ACTION_ZEROOFF:
NS_DBG("do_state_action: set internal offset to 0\n");
ns->regs.off = 0;
break;
case ACTION_HALFOFF:
if (!(ns->options & OPT_PAGE512_8BIT)) {
NS_ERR("do_state_action: BUG! can't skip half of page for non-512"
"byte page size 8x chips\n");
return -1;
}
NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2);
ns->regs.off = ns->geom.pgsz/2;
break;
case ACTION_OOBOFF:
NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz);
ns->regs.off = ns->geom.pgsz;
break;
default:
NS_DBG("do_state_action: BUG! unknown action\n");
}
return 0;
}
```
`ns->regs.off` help to shift page offset internally(note that we can shift the page offset by `ns->regs.column` and `ns->regs.off`), according to which command we are going to execute.