#define NO_RDTSC #include #include #include #include #include typedef int devclass_t; #include "/sys/dev/ata/ata-all.h" #define ATA_C_F_DIS_RCACHE 0x55 extern int MHz; extern int verbose; static int status = 0, error = 0; static const int32_t ioaddr[] = { 0x1f0, 0x170 }; /* XXX detect these! */ static void _ata_delay(int n) { quad_t t = rdtsc(); while (rdtsc() < t + n * MHz) ; } static int _ata_wait(u_int32_t device, int mask) { u_int32_t timeout = 0; _ata_delay(1); while (timeout < 5000000) { /* timeout 5 secs */ status = inb(ioaddr[device] + ATA_STATUS); /* if drive fails status, reselect the drive just to be sure */ if (status == 0xff) { printf("ad%d: no status, reselecting device\n", device); outb(ioaddr[device] + ATA_DRIVE, ATA_D_IBM | device); _ata_delay(1); status = inb(ioaddr[device] + ATA_STATUS); } /* are we done ? */ if (!(status & ATA_S_BUSY)) break; if (timeout > 1000) { timeout += 1000; _ata_delay(1000); } else { timeout += 10; _ata_delay(10); } } if (status & ATA_S_ERROR) error = inb(ioaddr[device] + ATA_ERROR); if (timeout >= 5000000) return -1; if (!mask) return (status & ATA_S_ERROR); /* Wait 50 msec for bits wanted. */ timeout = 5000; while (timeout--) { status = inb(ioaddr[device] + ATA_STATUS); if ((status & mask) == mask) { if (status & ATA_S_ERROR) error = inb(ioaddr[device] + ATA_ERROR); return (status & ATA_S_ERROR); } _ata_delay(10); } return -1; } static void _ata_setfeatures(u_int32_t device, u_int32_t cylinder, u_int32_t head, u_int32_t sector, u_int32_t count, u_int32_t feature) { if (_ata_wait(device, 0) < 0) die("ad%d: timeout waiting to give command, status=%02x error=%02x\n", device, status, error); outb(ioaddr[device] + ATA_FEATURE, feature); outb(ioaddr[device] + ATA_CYL_LSB, cylinder); outb(ioaddr[device] + ATA_CYL_MSB, cylinder >> 8); outb(ioaddr[device] + ATA_DRIVE, ATA_D_IBM | device | head); outb(ioaddr[device] + ATA_SECTOR, sector); outb(ioaddr[device] + ATA_COUNT, count); /* ought to wait for interrupt.. */ outb(ioaddr[device] + ATA_CMD, ATA_C_SETFEATURES); } void diskcache(const char *_device, int action) { int scsi; u_int32_t unit; char *p, *q; char *device = strdup(_device); if ((p = strrchr(device, '/')) != 0) *p++ = 0; else p = device; if ((p[0] == 'a' || p[0] == 'w') && p[1] == 'd') scsi = 0; else if ((p[0] == 's' && p[1] == 'd') || (p[0] == 'd' && p[1] == 'a')) scsi = 1; else die("first argument: {ad,da}N"); unit = strtoul(p+2, &q, 10); if (!p[2] || *q) die("first argument: {ad,da}N"); if (scsi) { if (action == -1) { die("getfeatures not implemented yet"); } else { char s[1024]; sprintf(s, "echo -e '1,$s/\\(RCD.*\\) [01]/\\\\1 %d/\\nw' | ed -s", action ? 0 : 1); setenv("EDITOR", s, 1); if (fork() == 0) { execlp("camcontrol", "camcontrol", "modepage", p, "-m", "8", "-e", 0); pdie("execlp(camcontrol) failed"); } wait(0); } } else { if (unit >= sizeof(ioaddr)/sizeof(ioaddr[0])) die("unknown disk %s", _device); if (open("/dev/io", O_RDWR, 0) < 0) die("can't get IO perms on /dev/io"); if (action == -1) die("getfeatures not implemented yet"); else _ata_setfeatures(unit, 0, 0, 0, 0, action ? ATA_C_F_ENAB_RCACHE : ATA_C_F_DIS_RCACHE); } free(device); if (verbose && action >= 0) fprintf(stderr, " turned o%s cache on %s disk %s\n", action ? "n" : "ff", scsi ? "SCSI" : "IDE", _device); } #ifdef MAIN int MHz = 550; int verbose = 0; int main(int argc, char *argv[]) { int action; argc--; argv++; if (argc != 1 && argc != 2) die("usage: diskcache device:\"{ad,da}N\" [on|off]"); if (argc == 1) action = -1; /* query */ else if (!strcmp(argv[1], "on")) action = 1; else if (!strcmp(argv[1], "off")) action = 0; else die("second argument: [on|off]"); diskcache(argv[0], action); return 0; } #endif