/* -*- linux-c -*- */ /* * * $Id: io.c,v 1.15 2002/09/20 20:49:36 germeier Exp $ * * Library for USB MPIO-* * * Markus Germeier (mager@tzi.de) * * uses code from mpio_stat.c by * Yuji Touya (salmoon@users.sourceforge.net) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * */ /* * * low level I/O * * */ #include #include #include #include #include "io.h" #include "debug.h" #include "ecc.h" BYTE model2externalmem(mpio_model_t); WORD index2blockaddress(WORD); WORD cluster2blockaddress(DWORD, BYTE); int cluster2block(int mem, int sector); void fatentry2hw(mpio_fatentry_t *, BYTE *, DWORD *); BYTE model2externalmem(mpio_model_t model) { BYTE m; switch(model) { case MPIO_MODEL_DMG: case MPIO_MODEL_DMG_PLUS: m = 0x80; break; default: m = 0x10; } return m; } void fatentry2hw(mpio_fatentry_t *f, BYTE *chip, DWORD *address) { mpio_smartmedia_t *sm; if (f->mem == MPIO_INTERNAL_MEM) { sm = &f->m->internal; /* hexdump((char *)&f->entry, 4); */ /* hexdump((char *)&f->hw_address, 4); */ *chip = f->hw_address / 0x1000000; *address = f->hw_address & 0x0ffffff; } if (f->mem == MPIO_EXTERNAL_MEM) { sm = &f->m->external; *chip = MPIO_EXTERNAL_MEM; *address = cluster2block(sm->size, f->entry); *address *= BLOCK_SECTORS; /* add offset to start of "data" area! */ *address += (sm->dir_offset + DIR_NUM - (2 * BLOCK_SECTORS)); } return; } /* * HELP! * * somebody explain me these values!!! * */ int cluster2block(int mem, int sector) { int a = sector; /* No Zone-based block management for SmartMedia below 32MB !!*/ /* The jumps are defect sectors on a specific SM medium and have to be handled with a proper logic block -> physical block which include a bad block management */ /* if (mem == 32) */ /* { */ /* if (sector >= 830) */ /* a++; */ /* if (a >= 1001) */ /* a += 21; */ /* } */ /* if (mem == 64) */ /* { */ /* /\* I'm so large in *not* knowing! *\/ */ /* if (sector >= 89) */ /* a++; */ /* if (a >= 1000) */ /* a += 21; */ /* if (a >= 2021) */ /* a += 24; */ /* if (a >= 3045) */ /* a += 24; */ /* /\* WHAT? *\/ */ /* if (a >= 3755) */ /* a++; */ /* } */ if (mem > 16) { /* two blocks are already spent elsewhere */ /* question is: where (CIS and ??) */ if (sector >= 998) a += 22; /* ... and then add 24 empty blocks every 1000 sectors */ a += ((sector - 998) / 1000 * 24); } return a; } WORD index2blockaddress(WORD ba) { WORD addr; BYTE p = 0, c = 0; BYTE high, low; high = 0x10 | ((ba / 0x80) & 0x07); low = (ba * 2) & 0xff; c = high; while (c) { if (c & 0x01) p ^= 1; c /= 2; } c = low; while (c) { if (c & 0x01) p ^= 0x01; c /= 2; } addr = (high * 0x100) + low + p; return addr; } WORD cluster2blockaddress(DWORD index, BYTE size) { DWORD ba; WORD block_address; /* The jumps are defect sectors on a specific SM medium and have to be handled with a proper logic block -> physical block which include a bad block management */ if (index<0x40) { block_address=0; } else { if (index >= 0x8000) { ba = ((index % 0x8000) / 0x20); /* if (size == 64) */ /* { */ /* if (index >= 0x1d5e0) */ /* ba--; */ /* } */ } else { ba = (index / 0x20) - 2; /* if (size == 32) */ /* { */ /* if (ba >= 0x33f) */ /* ba--; */ /* } */ /* if (size == 64) */ /* { */ /* if (ba >= 0x05b) */ /* ba--; */ /* } */ } block_address= index2blockaddress(ba); debugn(2, "block-foo: %06x %04x %04x\n", index, ba, block_address); } return block_address; } /* * low-low level functions */ /* * Set command packet * * parameter: * * cmd: commando code * mem: internal/external * index: sector/block * size: size of used memory chip * wsize: write size, only for PUT_BLOCK * buffer: output buffer of command packet * */ int mpio_io_set_cmdpacket(mpio_t *m, mpio_cmd_t cmd, mpio_mem_t mem, DWORD index, BYTE size, BYTE wsize, BYTE *buffer) { BYTE memory; /* clear cmdpacket*/ memset(buffer, 0, 0x40); *buffer = cmd; memory = mem; if (mem == MPIO_EXTERNAL_MEM) memory = model2externalmem(m->model); *(buffer + 0x01) = memory; *(buffer + 0x03) = (BYTE) (index & 0x00ff); *(buffer + 0x04) = (BYTE)((index & 0xff00) >> 8); /* SM cards with less or equal 32 MB only need 2 Bytes * to address sectors or blocks. * The highest byte has to be 0xff in that case! */ if (size <= 32) { *(buffer + 0x05) = 0xff; } else { *(buffer + 0x05) = (BYTE) (index >> 16); } /* is this always 0x48 in case of a block write ?? */ *(buffer + 0x06) = wsize; memcpy((buffer + 0x3b), "jykim", 5); return (0); } /* * write chunk of data to MPIO filedescriptor * * parameter: * * fd: filedescriptor * block: data buffer (has to be num_bytes) * num_bytes: size of data buffer * */ int mpio_io_bulk_write(int fd, BYTE *block, int num_bytes) { BYTE *p; int count, bytes_left, bytes_written; bytes_left = num_bytes; bytes_written = 0; p = block; do { count = write (fd, p, bytes_left); if (count > 0) { p += count; bytes_written += count; bytes_left -= count; } } while (bytes_left > 0 && count > 0); return bytes_written; } /* * read chunk of data from MPIO filedescriptor * * parameter: * * fd: filedescriptor * block: return buffer (has to be num_bytes) * num_bytes: size of return buffer * */ int mpio_io_bulk_read (int fd, BYTE *block, int num_bytes) { int total_read, count, bytes_left; BYTE *p; total_read = 0; bytes_left = num_bytes; p = block; do { count = read (fd, p, bytes_left); if (count > 0) { total_read += count; bytes_left -= count; p += count; } } while (total_read < num_bytes && count > 0); return total_read; } /* * low level functions */ /* * read version block from MPIO * * parameter: * * m: mpio context * buffer: return buffer (has to be CMD_SIZE) * */ int mpio_io_version_read(mpio_t *m, BYTE *buffer) { int nwrite, nread; BYTE cmdpacket[CMD_SIZE], status[CMD_SIZE]; /* Send command packet to MPIO */ mpio_io_set_cmdpacket (m, GET_VERSION, 0, 0, 0xff, 0, cmdpacket); debugn (5, ">>> MPIO\n"); hexdump (cmdpacket, sizeof(cmdpacket)); nwrite = mpio_io_bulk_write (m->fd, cmdpacket, 0x40); if (nwrite != CMD_SIZE) { debug ("Failed to send command.\n\n"); close (m->fd); return 0; } /* Receive packet from MPIO */ nread = mpio_io_bulk_read (m->fd, status, 0x40); if (nread == -1 || nread != 0x40) { debug ("Failed to read Sector.\n%x\n",nread); close (m->fd); return 0; } debugn (5, "<<< MPIO\n"); hexdump (status, 0x40); memcpy(buffer, status, 0x40); return CMD_SIZE; } /* * read sector from SmartMedia * * parameter: * * m: mpio context * mem: MPIO_{INTERNAL,EXTERNAL}_MEM * index: index number of sector to read * output: return buffer (has to be SECTOR_SIZE) * */ /* right now we assume we only want to read single sectors from * the _first_ internal memory chip */ int mpio_io_sector_read(mpio_t *m, BYTE mem, DWORD index, BYTE *output) { mpio_smartmedia_t *sm=0; int nwrite, nread; BYTE cmdpacket[CMD_SIZE], recvbuff[SECTOR_TRANS]; if (mem == MPIO_INTERNAL_MEM) sm = &m->internal; if (mem == MPIO_EXTERNAL_MEM) sm = &m->external; if (!sm) { debug("error in memory selection, aborting\n"); exit (-1); } mpio_io_set_cmdpacket (m, GET_SECTOR, mem, index, sm->size, 0, cmdpacket); debugn (5, "\n>>> MPIO\n"); hexdump (cmdpacket, sizeof(cmdpacket)); nwrite = mpio_io_bulk_write (m->fd, cmdpacket, 0x40); if(nwrite != CMD_SIZE) { debug ("\nFailed to send command.\n\n"); close (m->fd); return 1; } /* Receive packet from MPIO */ nread = mpio_io_bulk_read (m->fd, recvbuff, SECTOR_TRANS); if(nread != SECTOR_TRANS) { debug ("\nFailed to read Sector.\n%x\n", nread); close (m->fd); return 1; } /* check ECC Area information */ if (mem==MPIO_EXTERNAL_MEM) { if (mpio_ecc_256_check (recvbuff, (recvbuff + SECTOR_SIZE + 13)) || mpio_ecc_256_check ((recvbuff + (SECTOR_SIZE / 2)), (recvbuff + SECTOR_SIZE + 8)) ) debug ("ECC error @ (%02x : %06x)\n", mem, index); } /* This should not be needed: * we don't have ECC information for the internal memory * we only read the directory through this function */ /* if (mem==MPIO_INTERNAL_MEM) */ /* { */ /* debugn(2, "WARNING, code for internal FAT entry (in ECC area)" */ /* " not yet in place!!\n"); */ /* } */ debugn (5, "\n<<< MPIO\n"); hexdump (recvbuff, SECTOR_TRANS); memcpy(output, recvbuff, SECTOR_SIZE); return 0; } /* * write sector to SmartMedia * * parameter: * * m: mpio context * mem: MPIO_{INTERNAL,EXTERNAL}_MEM * index: index number of sector to read * input: data buffer (has to be SECTOR_SIZE) * */ /* right now we assume we only want to write single sectors from * the _first_ internal memory chip */ int mpio_io_sector_write(mpio_t *m, BYTE mem, DWORD index, BYTE *input) { int nwrite; mpio_smartmedia_t *sm; DWORD block_address, ba; BYTE cmdpacket[CMD_SIZE], sendbuff[SECTOR_TRANS]; if (mem == MPIO_INTERNAL_MEM) sm = &m->internal; if (mem == MPIO_EXTERNAL_MEM) sm = &m->external; if (!sm) { debug("error in memory selection, aborting\n"); exit (-1); } mpio_io_set_cmdpacket(m, PUT_SECTOR, mem, index, sm->size, 0, cmdpacket); debugn (5, "\n>>> MPIO\n"); hexdump (cmdpacket, sizeof(cmdpacket)); nwrite = mpio_io_bulk_write(m->fd, cmdpacket, 0x40); if(nwrite != CMD_SIZE) { debug ("\nFailed to send command.\n\n"); close (m->fd); return 1; } memset(sendbuff, 0, SECTOR_TRANS); memset(sendbuff + SECTOR_SIZE, 0xff, 0x10); memcpy(sendbuff, input, SECTOR_SIZE); if (mem==MPIO_EXTERNAL_MEM) block_address = cluster2blockaddress(index, sm->size); { /* generate ECC information for spare area ! */ mpio_ecc_256_gen(sendbuff, sendbuff + SECTOR_SIZE + 0x0d); mpio_ecc_256_gen(sendbuff + (SECTOR_SIZE / 2), sendbuff + SECTOR_SIZE + 0x08); ba = (block_address / 0x100) & 0xff; sendbuff[SECTOR_SIZE + 0x06] = ba; sendbuff[SECTOR_SIZE + 0x0b] = ba; ba = block_address & 0xff; sendbuff[SECTOR_SIZE + 0x07] = ba; sendbuff[SECTOR_SIZE + 0x0c] = ba; } /* easy but working, we write back the FAT info we read before */ if (mem==MPIO_INTERNAL_MEM) memcpy((sendbuff+SECTOR_SIZE), sm->fat, 0x10); debugn (5, "\n>>> MPIO\n"); hexdump(sendbuff, SECTOR_TRANS); /* write sector MPIO */ nwrite = mpio_io_bulk_write(m->fd, sendbuff, SECTOR_TRANS); if(nwrite != SECTOR_TRANS) { debug ("\nFailed to read Sector.\n%x\n", nwrite); close (m->fd); return 1; } return 0; } /* * read/write of blocks */ int mpio_io_block_read(mpio_t *m, BYTE mem, mpio_fatentry_t *f, BYTE *output) { int i=0; int nwrite, nread; mpio_smartmedia_t *sm; BYTE chip; DWORD address; BYTE cmdpacket[CMD_SIZE], recvbuff[BLOCK_TRANS]; DWORD block_address, ba; if (mem == MPIO_INTERNAL_MEM) sm = &m->internal; if (mem == MPIO_EXTERNAL_MEM) sm = &m->external; fatentry2hw(f, &chip, &address); mpio_io_set_cmdpacket(m, GET_BLOCK, chip, address, sm->size, 0, cmdpacket); debugn(5, "\n>>> MPIO\n"); hexdump(cmdpacket, sizeof(cmdpacket)); nwrite = mpio_io_bulk_write(m->fd, cmdpacket, CMD_SIZE); if(nwrite != CMD_SIZE) { debug ("\nFailed to send command.\n\n"); close (m->fd); return 1; } /* Receive packet from MPIO */ nread = mpio_io_bulk_read(m->fd, recvbuff, BLOCK_TRANS); if(nread != BLOCK_TRANS) { debug ("\nFailed to read Block.\n%x\n",nread); close (m->fd); return 1; } debugn(5, "\n<<< MPIO\n"); hexdump(recvbuff, BLOCK_TRANS); for (i = 0; i < BLOCK_SECTORS; i++) { /* check ECC Area information */ if (mem==MPIO_EXTERNAL_MEM) { if (mpio_ecc_256_check ((recvbuff + (i * SECTOR_TRANS)), ((recvbuff +(i * SECTOR_TRANS) + SECTOR_SIZE +13))) || mpio_ecc_256_check ((recvbuff + (i * SECTOR_TRANS) + (SECTOR_SIZE / 2)), ((recvbuff +(i * SECTOR_TRANS) + SECTOR_SIZE + 8)))) debug ("ECC error @ (%02x : %06x)\n", chip, address); if (i==0) { block_address = cluster2blockaddress(address, sm->size); ba = recvbuff[(i * SECTOR_TRANS) + SECTOR_SIZE + 0x06] * 0x100 + recvbuff[(i * SECTOR_TRANS) + SECTOR_SIZE + 0x07] ; if (block_address != ba) debugn(2,"different block_addresses during read: %04x vs. %04x\n", block_address, ba); } } memcpy(output + (i * SECTOR_SIZE), recvbuff + (i * SECTOR_TRANS), SECTOR_SIZE); } return 0; } /* Read spare is only usefull for the internal memory, * the FAT lies there. It is updated during the * mpio_io_{sector,block}_{read,write} operations. * * For external SmartMedia cards we have a "seperate" FAT * which is read and updated just like on a regular floppy * disc or hard drive. * */ int mpio_io_spare_read(mpio_t *m, BYTE area, DWORD index, BYTE size, BYTE wsize, BYTE *output, int toread, BYTE (*progress_callback)(int, int)) { int i; int nwrite, nread; int chip = 0; int chips = 0; BYTE cmdpacket[CMD_SIZE]; if (area != MPIO_INTERNAL_MEM) { debug("Something fishy happened, aborting!n"); exit(1); } chips = m->internal.chips; for (chip = 1; chip <= chips; chip++) { mpio_io_set_cmdpacket(m, GET_SPARE_AREA, chip, index, size, wsize, cmdpacket); debugn(5, "\n>>> MPIO\n"); hexdump(cmdpacket, sizeof(cmdpacket)); nwrite = mpio_io_bulk_write(m->fd, cmdpacket, CMD_SIZE); if(nwrite != CMD_SIZE) { debug ("\nFailed to send command.\n\n"); close (m->fd); return 1; } /* Receive packet from MPIO */ for (i = 0; i < (toread / chips / CMD_SIZE); i++) { nread = mpio_io_bulk_read (m->fd, output + (i * CMD_SIZE) + (toread / chips * (chip - 1)), CMD_SIZE); if ((progress_callback) && (i % 256)) (*progress_callback)((i*CMD_SIZE), toread); if(nread != CMD_SIZE) { debug ("\nFailed to read Block.\n%x\n",nread); close (m->fd); return 1; } debugn(5, "\n<<< MPIO\n"); hexdump(output + (i * CMD_SIZE) + (toread / chips * (chip - 1)), CMD_SIZE); } } if (progress_callback) (*progress_callback)(toread, toread); return 0; } int mpio_io_block_delete(mpio_t *m, BYTE mem, mpio_fatentry_t *f) { mpio_smartmedia_t *sm; int nwrite, nread; BYTE cmdpacket[CMD_SIZE], status[CMD_SIZE]; BYTE chip=0; DWORD address; if (mem == MPIO_INTERNAL_MEM) sm = &m->internal; if (mem == MPIO_EXTERNAL_MEM) sm = &m->external; fatentry2hw(f, &chip, &address); /* Send command packet to MPIO */ mpio_io_set_cmdpacket(m, DEL_BLOCK, chip, address, sm->size, 0, cmdpacket); debugn (5, ">>> MPIO\n"); hexdump (cmdpacket, sizeof(cmdpacket)); nwrite = mpio_io_bulk_write(m->fd, cmdpacket, 0x40); if (nwrite != CMD_SIZE) { debug ("Failed to send command.\n\n"); close (m->fd); return 0; } /* Receive packet from MPIO */ nread = mpio_io_bulk_read (m->fd, status, CMD_SIZE); if ((nread == -1) || (nread != CMD_SIZE)) { debug ("Failed to read Sector.\n%x\n",nread); close (m->fd); return 0; } debugn(5, "<<< MPIO\n"); hexdump(status, CMD_SIZE); if (status[0] != 0xc0) { debug ("error formatting Block (%04x) %02x:%06x\n", f->entry, chip, address); return 0; } return CMD_SIZE; } int mpio_io_block_write(mpio_t *m, BYTE mem, mpio_fatentry_t *f, BYTE *data) { mpio_smartmedia_t *sm; int nwrite; int i; DWORD block_address, ba; BYTE cmdpacket[CMD_SIZE], sendbuff[BLOCK_TRANS]; BYTE chip=0; DWORD address; if (mem == MPIO_INTERNAL_MEM) sm = &m->internal; if (mem == MPIO_EXTERNAL_MEM) sm = &m->external; fatentry2hw(f, &chip, &address); /* build block for transfer to MPIO */ for (i = 0; i < BLOCK_SECTORS; i++) { memcpy(sendbuff + (i * SECTOR_TRANS), data + (i * SECTOR_SIZE), SECTOR_SIZE); memset(sendbuff + (i * SECTOR_TRANS) + SECTOR_SIZE, 0xff, CMD_SIZE); if (mem == MPIO_INTERNAL_MEM) { if (i == 0) { memcpy((sendbuff+SECTOR_SIZE+(i * SECTOR_TRANS)), f->i_fat, 0x10); /* debug("address %02x:%06x\n", chip, address); */ /* hexdumpn(0, f->i_fat, 0x10); */ } } /* fill in block information */ if (mem == MPIO_EXTERNAL_MEM) { block_address = cluster2blockaddress(address, sm->size); ba = (block_address / 0x100) & 0xff; sendbuff[(i * SECTOR_TRANS) + SECTOR_SIZE + 0x06] = ba; sendbuff[(i * SECTOR_TRANS) + SECTOR_SIZE + 0x0b] = ba; ba = block_address & 0xff; sendbuff[(i * SECTOR_TRANS) + SECTOR_SIZE + 0x07] = ba; sendbuff[(i * SECTOR_TRANS) + SECTOR_SIZE + 0x0c] = ba; /* generate ECC Area information */ mpio_ecc_256_gen ((sendbuff + (i * SECTOR_TRANS)), ((sendbuff + (i * SECTOR_TRANS) + SECTOR_SIZE + 13))); mpio_ecc_256_gen ((sendbuff + (i * SECTOR_TRANS) + (SECTOR_SIZE / 2)), ((sendbuff + (i * SECTOR_TRANS) + SECTOR_SIZE + 8))); } } mpio_io_set_cmdpacket(m, PUT_BLOCK, chip, address, sm->size, 0x48 , cmdpacket); debugn(5, "\n>>> MPIO\n"); hexdump(cmdpacket, sizeof(cmdpacket)); nwrite = mpio_io_bulk_write(m->fd, cmdpacket, CMD_SIZE); if(nwrite != CMD_SIZE) { debug ("\nFailed to send command.\n\n"); close (m->fd); return 1; } /* send packet to MPIO */ debugn(5, "\n<<< MPIO\n"); hexdump(sendbuff, BLOCK_TRANS); nwrite = mpio_io_bulk_write (m->fd, sendbuff, BLOCK_TRANS); if(nwrite != BLOCK_TRANS) { debug ("\nFailed to read Block.\n%x\n",nwrite); close (m->fd); return 1; } return 0; }