📄 devio.c
字号:
/* * $QNXLicenseC: * Copyright 2007, QNX Software Systems. * * Licensed under the Apache License, Version 2.0 (the "License"). You * may not reproduce, modify or distribute this software except in * compliance with the License. You may obtain a copy of the License * at: http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OF ANY KIND, either express or implied. * * This file may contain contributions from others, either as * contributors under the License or as licensors under other terms. * Please review this entire file for other proprietary rights or license * notices, as well as the QNX Development Suite License Guide at * http://licensing.qnx.com/license-guide/ for other information. * $ */#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <stdlib.h>#include <string.h>#include <gulliver.h>#include <sys/slog.h>#include <sys/neutrino.h>#include <fs/etfs.h>#include "devio.h"//// Initialize the part and stuff physical paramaters for the part.//int devio_init(struct etfs_devio *dev) { uint8_t id[2]; CHIPIO *cio = dev->cio; // Allow IO operations if (ThreadCtl(_NTO_TCTL_IO, 0) != EOK) { dev->log(_SLOG_CRITICAL, "You must be root."); // We will not return } // Do anything special to get the part ready to use if (nand_init(dev) != 0) { dev->log(_SLOG_CRITICAL, "nand_init failed : %s", strerror(errno)); // We will not return } // Reset the part nand_write_cmd(cio, NANDCMD_RESET); // Read id info from the part nand_write_cmd(cio, NANDCMD_IDREAD); nand_write_pageaddr(cio, 0, 1); nand_read_data(cio, id, 2); switch (id[1]) { // 32M case 0x35: dev->numblks = 2048; cio->addrcycles = 3; break; // 64M case 0x36: dev->numblks = 4096; cio->addrcycles = 4; break; default: dev->log(_SLOG_CRITICAL, "Unsupported NAND device (%2.2x %2.2x)", id[0], id[1]); // We will not return } // These must be initialized here. All other numbers are built from them. sprintf(dev->id, "NAND%2.2x%2.2x", id[0], id[1]); dev->memtype = ETFS_MEMTYPE_NAND; // We glue to physical pages at the driver and report back their combined size dev->clustersize = DATASIZE * PAGES2CLUSTER; dev->sparesize = SPARESIZE * PAGES2CLUSTER; dev->clusters2blk = PAGES2BLK / PAGES2CLUSTER; dev->blksize = (dev->clustersize + SPARESIZE) * dev->clusters2blk; cio->lastpage = ~0; return (EOK);}//// Read a cluster of data.//// Verify crc for both the spare area and the data area.// The passed buffer "buf" is larger than the cluster size. It can hold// (PAGESIZE * PAGES2CLUSTER) bytes. This is for convienence when reading// data from the device ands calculating the crc. The work area after clustersize// bytes is ignored by the caller.//// We combine 2 pages to make a single cluster. This was necessary to get enough// spare bytes to store all the needed information. It would not fit in 16 bytes.//int devio_readcluster(struct etfs_devio *dev, unsigned cluster, uint8_t *buf, struct etfs_trans *trp) { struct spare1 *sp1, spare; struct spare2 *sp2; uint16_t status; unsigned page = cluster * PAGES2CLUSTER; CHIPIO *cio = dev->cio; /* * Read first page from the device */ nand_write_cmd(cio, NANDCMD_READ); cio->inspare = 0; nand_write_pageaddr(cio, page, cio->addrcycles); if (nand_wait_busy(cio, MAX_READ_USEC) != 0) dev->log(_SLOG_CRITICAL, "Timeout on READ"); nand_read_data(cio, buf, PAGESIZE); status = nand_read_status(cio); trp->tacode = ETFS_TRANS_OK; trp->dacode = ETFS_TRANS_OK; sp1 = (struct spare1 *)(buf + DATASIZE); if (sp1->status != 0xFF) { dev->log(_SLOG_ERROR, "readculster trans BADBLK on cluster %d", cluster); return (trp->tacode = ETFS_TRANS_BADBLK); } else if (sp1->unused1 == ~0 && sp1->cluster == ~0 && sp1->nclusters == 0xFF) { if (sp1->erasesig == ERASESIG) trp->tacode = ETFS_TRANS_ERASED; else trp->tacode = ETFS_TRANS_FOXES; return (trp->tacode); } else {#if 0 if ((status & 0x03) > 1) { /* Spare area ECC error */ dev->log(_SLOG_ERROR, "readcluster trans DATAERR on cluster %d", cluster); trp->tacode = ETFS_TRANS_DATAERR; }#endif if ((status & 0x0C) > 0x04) { /* Main area ECC error */ dev->log(_SLOG_ERROR, "readcluster DATAERR on cluster %d", cluster); trp->dacode = ETFS_TRANS_DATAERR; } } // Build transaction data from data in the spare area 1. trp->nclusters = sp1->nclusters; trp->cluster = sp1->cluster; memcpy(&spare, sp1, sizeof(spare)); /* * Read second page from device */ nand_write_cmd(cio, NANDCMD_READ); nand_write_pageaddr(cio, ++page, cio->addrcycles); if (nand_wait_busy(cio, MAX_READ_USEC) != 0) dev->log(_SLOG_CRITICAL, "Timeout on READ"); nand_read_data(cio, buf + DATASIZE, PAGESIZE); status = nand_read_status(cio); cio->lastpage = page; sp2 = (struct spare2 *) (buf + 2 * DATASIZE); if (sp2->status != 0xFF) { dev->log(_SLOG_ERROR, "readculster trans BADBLK on cluster %d", cluster); return (trp->tacode = ETFS_TRANS_BADBLK); } // Build transaction data from data in the spare area 2. trp->sequence = ENDIAN_LE32(sp2->sequence); trp->fid = ENDIAN_LE16(sp2->fid); if (trp->tacode == ETFS_TRANS_OK){ if ((status & 0x0C) > 0x04) { /* Main area ECC error */ dev->log(_SLOG_ERROR, "readcluster DATAERR on cluster %d", cluster); return (trp->dacode = ETFS_TRANS_DATAERR); } else if ((status & 0x03) > 1) { /* Spare area ECC error */ dev->log(_SLOG_ERROR, "readcluster ECCERR on cluster %d", cluster); return (trp->tacode = ETFS_TRANS_DATAERR); } memcpy(sp2->ecc, &spare.nclusters, 5); if (dev->crc16((uint8_t *)sp2, sizeof(*sp2) - sizeof(sp2->crctrans)) != sp2->crctrans) { dev->log(_SLOG_ERROR, "readtrans DATAERR on cluster %d", cluster); return (trp->tacode = ETFS_TRANS_DATAERR); } } return (trp->tacode);}//// Read the spare area of a page (not the data) to return transaction infoormation.//// This called is used heavily on startup to process the transactions. It is// a cheaper call than readcluster() since it reads less data and has a smaller// checksum to calculate.//int devio_readtrans(struct etfs_devio *dev, unsigned cluster, struct etfs_trans *trp) { struct spare1 spare1; struct spare2 spare2; unsigned page = cluster * PAGES2CLUSTER; CHIPIO *cio = dev->cio; uint16_t status; nand_write_cmd(cio, NANDCMD_SPAREREAD); cio->inspare = 1; nand_write_pageaddr(cio, page, cio->addrcycles); if (nand_wait_busy(cio, MAX_READ_USEC) != 0) dev->log(_SLOG_CRITICAL, "Timeout on READ"); nand_read_data(cio, (uint8_t *)&spare1, sizeof(spare1)); status = nand_read_status(cio); cio->lastpage = page; if (spare1.status != 0xFF) { dev->log(_SLOG_ERROR, "readtrans BADBLK on cluster %d", cluster); return (ETFS_TRANS_BADBLK); } if (spare1.unused1 == ~0 && spare1.cluster == ~0 && spare1.nclusters == 0xFF) { if (spare1.erasesig == ERASESIG) return (ETFS_TRANS_ERASED); else return (ETFS_TRANS_FOXES); }#if 0 if ((status & 0x03) > 1) { dev->log(_SLOG_ERROR, "readtrans DATAERR on cluster %d", cluster); return (ETFS_TRANS_DATAERR); }#endif trp->nclusters = spare1.nclusters; trp->cluster = spare1.cluster; /* * Second page read from device */ nand_write_cmd(cio, NANDCMD_SPAREREAD); nand_write_pageaddr(cio, ++page, cio->addrcycles); if (nand_wait_busy(cio, MAX_READ_USEC) != 0) dev->log(_SLOG_CRITICAL, "Timeout on READ"); nand_read_data(cio, (uint8_t *)&spare2, sizeof(spare2)); cio->lastpage = page; if (spare2.status != 0xFF) { dev->log(_SLOG_ERROR, "readtrans BADBLK on cluster %d", cluster); return (ETFS_TRANS_BADBLK); } status = nand_read_status(cio); if ((status & 0x03) > 1) { /* Spare area ECC error */ dev->log(_SLOG_ERROR, "readtrans DATAERR (ECC) on cluster %d", cluster); return (ETFS_TRANS_DATAERR); } /* * Check CRC for trans aera */ memcpy(spare2.ecc, &spare1.nclusters, 5); if (dev->crc16((uint8_t *)&spare2, sizeof(spare2) - sizeof(spare2.crctrans)) != spare2.crctrans) { dev->log(_SLOG_ERROR, "readtrans DATAERR on cluster %d", cluster); return (ETFS_TRANS_DATAERR); } trp->sequence = ENDIAN_LE32(spare2.sequence); trp->fid = ENDIAN_LE16(spare2.fid); return (ETFS_TRANS_OK);}//// Post a cluster of data.//// Set crc for both the spare area and the data area.// The passed buffer "buf" is larger than the cluster size. It can hold// (PAGESIZE * PAGES2CLUSTER) bytes. This is for convienence writing// data to the device ands calculating the crc. The work area after clustersize// bytes is ignored by the caller.//int devio_postcluster(struct etfs_devio *dev, unsigned cluster, uint8_t *buf, struct etfs_trans *trp) { struct spare1 *sp1; struct spare2 *sp2; uint8_t status; unsigned page = cluster * PAGES2CLUSTER; CHIPIO *cio = dev->cio; // Build second spare area sp2 = (struct spare2 *) (buf + 2 * DATASIZE); memset((void *)sp2, 0xff, sizeof(*sp2)); // Build spare areas sp1 = (struct spare1 *) (buf + 2 * DATASIZE + SPARESIZE); memset((void *)sp1, 0xff, sizeof(*sp1)); if (trp) { sp1->erasesig = 0; sp1->nclusters = trp->nclusters; sp1->cluster = trp->cluster; sp2->sequence = ENDIAN_LE32(trp->sequence); sp2->fid = ENDIAN_LE16((uint16_t) trp->fid); memcpy(sp2->ecc, &sp1->nclusters, 5); sp2->crctrans = dev->crc16((uint8_t *)sp2, sizeof(*sp2) - sizeof(sp2->crctrans)); } else sp1->erasesig = ERASESIG; // Write first page of data // This forces us out of the spare area. if (cio->inspare) { nand_write_cmd(cio, NANDCMD_READ); cio->inspare = 0; } nand_write_cmd(cio, NANDCMD_PROGRAM); nand_write_pageaddr(cio, page, cio->addrcycles); nand_write_data(cio, buf, (uint8_t *)sp1); nand_write_cmd(cio, NANDCMD_PROGRAMCONFIRM); if (nand_wait_busy(cio, MAX_POST_USEC) != 0) dev->log(_SLOG_CRITICAL, "Timeout on POST"); nand_write_cmd(cio, NANDCMD_STATUSREAD); nand_read_data(cio, &status, 1); if ((status & 0x01) != 0) { dev->log(_SLOG_ERROR, "Post error on page %d", page); return (ETFS_TRANS_DEVERR); } // Our work is done if called from devio_eraseblk() if (trp == NULL) return (ETFS_TRANS_OK); // Write second page of data nand_write_cmd(cio, NANDCMD_PROGRAM); nand_write_pageaddr(cio, ++page, cio->addrcycles); nand_write_data(cio, buf + DATASIZE, (uint8_t *)sp2); nand_write_cmd(cio, NANDCMD_PROGRAMCONFIRM); if (nand_wait_busy(cio, MAX_POST_USEC) != 0) dev->log(_SLOG_CRITICAL, "Timeout on POST"); nand_write_cmd(cio, NANDCMD_STATUSREAD); nand_read_data(cio, &status, 1); if ((status & 0x01) != 0) { dev->log(_SLOG_ERROR, "Post error on page %d", page); return (ETFS_TRANS_DEVERR); } return (ETFS_TRANS_OK);}//// Erase a block.//int devio_eraseblk(struct etfs_devio *dev, unsigned blk) { uint8_t status; CHIPIO *cio = dev->cio; uint8_t *buf = alloca(PAGESIZE * PAGES2CLUSTER); nand_write_cmd(cio, NANDCMD_ERASE); nand_write_blkaddr(cio, blk, cio->addrcycles - 1); nand_write_cmd(cio, NANDCMD_ERASECONFIRM); if (nand_wait_busy(cio, MAX_ERASE_USEC) != 0) dev->log(_SLOG_CRITICAL, "Timeout on ERASE"); nand_write_cmd(cio, NANDCMD_STATUSREAD); nand_read_data(cio, &status, 1); if ((status & 0xC1) != 0xC0) { dev->log(_SLOG_ERROR, "erase error on blk %d (%2.2x)", blk, status); return (ETFS_TRANS_DEVERR); } // Write the erase signature. We only write non FFs in the first 16 bytes of the // spare area and put FFs everywhere else. memset(buf, 0xFF, PAGESIZE * PAGES2CLUSTER); status = devio_postcluster(dev, blk * dev->clusters2blk, buf, NULL); return (status);}//// Called to allow the driver to flush any cached data that// has not be written to the device. The NAND class driver does// not need it.//int devio_sync(struct etfs_devio *dev) { return (-1);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -