123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- // ata.c: ATA simulator for vblade
- #include "config.h"
- #include <string.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include "dat.h"
- #include "fns.h"
- enum {
- // err bits
- UNC = 1<<6,
- MC = 1<<5,
- IDNF = 1<<4,
- MCR = 1<<3,
- ABRT = 1<<2,
- NM = 1<<1,
- // status bits
- BSY = 1<<7,
- DRDY = 1<<6,
- DF = 1<<5,
- DRQ = 1<<3,
- ERR = 1<<0,
- };
- static ushort ident[256];
- static void
- setfld(ushort *a, int idx, int len, char *str) // set field in ident
- {
- uchar *p;
- p = (uchar *)(a+idx);
- while (len > 0) {
- if (*str == 0)
- p[1] = ' ';
- else
- p[1] = *str++;
- if (*str == 0)
- p[0] = ' ';
- else
- p[0] = *str++;
- p += 2;
- len -= 2;
- }
- }
- static void
- setlba28(ushort *ident, vlong lba)
- {
- uchar *cp;
- cp = (uchar *) &ident[60];
- *cp++ = lba;
- *cp++ = lba >>= 8;
- *cp++ = lba >>= 8;
- *cp++ = (lba >>= 8) & 0xf;
- }
- static void
- setlba48(ushort *ident, vlong lba)
- {
- uchar *cp;
- cp = (uchar *) &ident[100];
- *cp++ = lba;
- *cp++ = lba >>= 8;
- *cp++ = lba >>= 8;
- *cp++ = lba >>= 8;
- *cp++ = lba >>= 8;
- *cp++ = lba >>= 8;
- }
- static void
- setushort(ushort *a, int i, ushort n)
- {
- uchar *p;
- p = (uchar *)(a+i);
- *p++ = n & 0xff;
- *p++ = n >> 8;
- }
- void
- atainit(void)
- {
- char buf[64];
- setushort(ident, 47, 0x8000);
- setushort(ident, 49, 0x0200);
- setushort(ident, 50, 0x4000);
- setushort(ident, 83, 0x5400);
- setushort(ident, 84, 0x4000);
- setushort(ident, 86, 0x1400);
- setushort(ident, 87, 0x4000);
- setushort(ident, 93, 0x400b);
- setfld(ident, 27, 40, "Coraid EtherDrive vblade");
- sprintf(buf, "V%d", VBLADE_VERSION);
- setfld(ident, 23, 8, buf);
- setfld(ident, 10, 20, serial);
- }
- /* The ATA spec is weird in that you specify the device size as number
- * of sectors and then address the sectors with an offset. That means
- * with LBA 28 you shouldn't see an LBA of all ones. Still, we don't
- * check for that.
- */
- int
- atacmd(Ataregs *p, uchar *dp, int ndp, int payload) // do the ata cmd
- {
- vlong lba;
- ushort *ip;
- int n;
- enum { MAXLBA28SIZE = 0x0fffffff };
- extern int maxscnt;
- p->status = 0;
- switch (p->cmd) {
- default:
- p->status = DRDY | ERR;
- p->err = ABRT;
- return 0;
- case 0xe7: // flush cache
- return 0;
- case 0xec: // identify device
- if (p->sectors != 1 || ndp < 512)
- return -1;
- memmove(dp, ident, 512);
- ip = (ushort *)dp;
- if (size & ~MAXLBA28SIZE)
- setlba28(ip, MAXLBA28SIZE);
- else
- setlba28(ip, size);
- setlba48(ip, size);
- p->err = 0;
- p->status = DRDY;
- p->sectors = 0;
- return 0;
- case 0xe5: // check power mode
- p->err = 0;
- p->sectors = 0xff; // the device is active or idle
- p->status = DRDY;
- return 0;
- case 0x20: // read sectors
- case 0x30: // write sectors
- lba = p->lba & MAXLBA28SIZE;
- break;
- case 0x24: // read sectors ext
- case 0x34: // write sectors ext
- lba = p->lba & 0x0000ffffffffffffLL; // full 48
- break;
- }
- // we ought not be here unless we are a read/write
- if (p->sectors > maxscnt || p->sectors*512 > ndp)
- return -1;
- if (lba + p->sectors > size) {
- p->err = IDNF;
- p->status = DRDY | ERR;
- p->lba = lba;
- return 0;
- }
- if (p->cmd == 0x20 || p->cmd == 0x24)
- n = getsec(bfd, dp, lba+offset, p->sectors);
- else {
- // packet should be big enough to contain the data
- if (payload < 512 * p->sectors)
- return -1;
- n = putsec(bfd, dp, lba+offset, p->sectors);
- }
- n /= 512;
- if (n != p->sectors) {
- p->err = ABRT;
- p->status = ERR;
- } else
- p->err = 0;
- p->status |= DRDY;
- p->lba += n;
- p->sectors -= n;
- return 0;
- }
|