--- /dev/null
+/*
+ * Block devices
+ * Copyright (c) 2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_BLOCKDEV_H
+#define GEEKOS_BLOCKDEV_H
+
+#include <geekos/ktypes.h>
+#include <geekos/kthread.h>
+#include <geekos/list.h>
+#include <geekos/fileio.h>
+
+#ifdef GEEKOS
+
+/*
+ * Type of block device request.
+ */
+enum Request_Type {
+ BLOCK_READ, BLOCK_WRITE
+};
+
+/*
+ * State of a block I/O request.
+ */
+enum Request_State {
+ PENDING, COMPLETED, ERROR
+};
+
+struct Block_Request;
+
+/*
+ * List of block I/O requests.
+ */
+DEFINE_LIST(Block_Request_List, Block_Request);
+
+/*
+ * An I/O request for a block device.
+ */
+struct Block_Request {
+ struct Block_Device *dev;
+ enum Request_Type type;
+ int blockNum;
+ void *buf;
+ volatile enum Request_State state;
+ volatile int errorCode;
+ struct Thread_Queue waitQueue;
+
+ DEFINE_LINK(Block_Request_List, Block_Request);
+};
+
+IMPLEMENT_LIST(Block_Request_List, Block_Request);
+
+struct Block_Device;
+struct Block_Device_Ops;
+
+/*
+ * A block device.
+ */
+struct Block_Device {
+ char name[BLOCKDEV_MAX_NAME_LEN];
+ struct Block_Device_Ops *ops;
+ int unit;
+ bool inUse;
+ void *driverData;
+ struct Thread_Queue *waitQueue;
+ struct Block_Request_List *requestQueue;
+
+ DEFINE_LINK(Block_Device_List, Block_Device);
+};
+
+/*
+ * Operations that may be requested on block devices.
+ */
+struct Block_Device_Ops {
+ int (*Open)(struct Block_Device *dev);
+ int (*Close)(struct Block_Device *dev);
+ int (*Get_Num_Blocks)(struct Block_Device *dev);
+};
+
+/*
+ * Low level block device API.
+ * Only block device drivers need to use these functions.
+ */
+int Register_Block_Device(const char *name, struct Block_Device_Ops *ops,
+ int unit, void *driverData, struct Thread_Queue *waitQueue,
+ struct Block_Request_List *requestQueue);
+int Open_Block_Device(const char *name, struct Block_Device **pDev);
+int Close_Block_Device(struct Block_Device *dev);
+struct Block_Request *Create_Request(struct Block_Device *dev, enum Request_Type type,
+ int blockNum, void *buf);
+void Post_Request_And_Wait(struct Block_Request *request);
+struct Block_Request *Dequeue_Request(struct Block_Request_List *requestQueue,
+ struct Thread_Queue *waitQueue);
+void Notify_Request_Completion(struct Block_Request *request, enum Request_State state, int errorCode);
+
+/*
+ * High level block device API.
+ * For use by filesystem and disk paging code.
+ */
+int Block_Read(struct Block_Device *dev, int blockNum, void *buf);
+int Block_Write(struct Block_Device *dev, int blockNum, void *buf);
+int Get_Num_Blocks(struct Block_Device *dev);
+
+/*
+ * Misc. routines
+ */
+
+/*
+ * Round offset up to nearest sector.
+ */
+static __inline__ ulong_t Round_Up_To_Block(ulong_t offset)
+{
+ return (offset % SECTOR_SIZE) == 0
+ ? offset
+ : offset + (SECTOR_SIZE - (offset % SECTOR_SIZE));
+}
+
+/*
+ * Round offset down to nearest sector.
+ */
+static __inline__ ulong_t Round_Down_To_Block(ulong_t offset)
+{
+ return (offset % SECTOR_SIZE) == 0
+ ? offset
+ : offset - (offset % SECTOR_SIZE);
+}
+
+#endif /* GEEKOS */
+
+#endif
+
--- /dev/null
+/*
+ * Block devices
+ * Copyright (c) 2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/errno.h>
+#include <geekos/screen.h>
+#include <geekos/string.h>
+#include <geekos/malloc.h>
+#include <geekos/int.h>
+#include <geekos/kthread.h>
+#include <geekos/synch.h>
+#include <geekos/blockdev.h>
+
+/*#define BLOCKDEV_DEBUG */
+#ifdef BLOCKDEV_DEBUG
+# define Debug(args...) Print(args)
+#else
+# define Debug(args...)
+#endif
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Lock protecting access/modification of block device list.
+ */
+static struct Mutex s_blockdevLock;
+
+/*
+ * List datatype for list of block devices.
+ */
+DEFINE_LIST(Block_Device_List, Block_Device);
+IMPLEMENT_LIST(Block_Device_List, Block_Device);
+
+/*
+ * The list in which all block devices in the system
+ * are registered.
+ */
+static struct Block_Device_List s_deviceList;
+
+/*
+ * Perform a block IO request.
+ * Returns 0 if successful, error code on failure.
+ */
+static int Do_Request(struct Block_Device *dev, enum Request_Type type, int blockNum, void *buf)
+{
+ struct Block_Request *request;
+ int rc;
+
+ request = Create_Request(dev, type, blockNum, buf);
+ if (request == 0)
+ return ENOMEM;
+ Post_Request_And_Wait(request);
+ rc = request->errorCode;
+ Free(request);
+ return rc;
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Register a block device.
+ * This should be called by device drivers in their Init
+ * functions to register all detected devices.
+ * Returns 0 if successful, error code otherwise.
+ */
+int Register_Block_Device(const char *name, struct Block_Device_Ops *ops,
+ int unit, void *driverData, struct Thread_Queue *waitQueue,
+ struct Block_Request_List *requestQueue)
+{
+ struct Block_Device *dev;
+
+ KASSERT(ops != 0);
+ KASSERT(waitQueue != 0);
+ KASSERT(requestQueue != 0);
+
+ dev = (struct Block_Device*) Malloc(sizeof(*dev));
+ if (dev == 0)
+ return ENOMEM;
+
+ strcpy(dev->name, name);
+ dev->ops = ops;
+ dev->unit = unit;
+ dev->inUse = false;
+ dev->driverData = driverData;
+ dev->waitQueue = waitQueue;
+ dev->requestQueue = requestQueue;
+
+ Mutex_Lock(&s_blockdevLock);
+ /* FIXME: handle name conflict with existing device */
+ Debug("Registering block device %s\n", dev->name);
+ Add_To_Back_Of_Block_Device_List(&s_deviceList, dev);
+ Mutex_Unlock(&s_blockdevLock);
+
+ return 0;
+}
+
+/*
+ * Open a named block device.
+ * Return 0 if successful, error code on error.
+ */
+int Open_Block_Device(const char *name, struct Block_Device **pDev)
+{
+ struct Block_Device *dev;
+ int rc = 0;
+
+ Mutex_Lock(&s_blockdevLock);
+
+ dev = Get_Front_Of_Block_Device_List(&s_deviceList);
+ while (dev != 0) {
+ if (strcmp(dev->name, name) == 0)
+ break;
+ dev = Get_Next_In_Block_Device_List(dev);
+ }
+
+ if (dev == 0)
+ rc = ENODEV;
+ else if (dev->inUse)
+ rc = EBUSY;
+ else {
+ rc = dev->ops->Open(dev);
+ if (rc == 0) {
+ *pDev = dev;
+ dev->inUse = true;
+ }
+ }
+
+ Mutex_Unlock(&s_blockdevLock);
+
+ return rc;
+}
+
+/*
+ * Close given block device.
+ * Return 0 if successful, error code on error.
+ */
+int Close_Block_Device(struct Block_Device *dev)
+{
+ int rc;
+
+ Mutex_Lock(&s_blockdevLock);
+
+ KASSERT(dev->inUse);
+ rc = dev->ops->Close(dev);
+ if (rc == 0)
+ dev->inUse = false;
+
+ Mutex_Unlock(&s_blockdevLock);
+
+ return rc;
+}
+
+/*
+ * Create a block device request to transfer a single block.
+ */
+struct Block_Request *Create_Request(struct Block_Device *dev, enum Request_Type type,
+ int blockNum, void *buf)
+{
+ struct Block_Request *request = Malloc(sizeof(*request));
+ if (request != 0) {
+ request->dev = dev;
+ request->type = type;
+ request->blockNum = blockNum;
+ request->buf = buf;
+ request->state = PENDING;
+ Clear_Thread_Queue(&request->waitQueue);
+ }
+ return request;
+}
+
+/*
+ * Send a block IO request to a device and wait for it to be handled.
+ * Returns when the driver completes the requests or signals
+ * an error.
+ */
+void Post_Request_And_Wait(struct Block_Request *request)
+{
+ struct Block_Device *dev;
+
+ KASSERT(request != 0);
+
+ dev = request->dev;
+ KASSERT(dev != 0);
+
+ /* Send request to the driver */
+ Debug("Posting block device request [@%x]...\n", request);
+ Disable_Interrupts();
+ Add_To_Back_Of_Block_Request_List(dev->requestQueue, request);
+ Wake_Up(dev->waitQueue);
+ Enable_Interrupts();
+
+ /* Wait for request to be processed */
+ Disable_Interrupts();
+ while (request->state == PENDING) {
+ Debug("Waiting, state=%d\n", request->state);
+ Wait(&request->waitQueue);
+ }
+ Debug("Wait completed!\n");
+ Enable_Interrupts();
+}
+
+/*
+ * Wait for a block request to arrive.
+ */
+struct Block_Request *Dequeue_Request(struct Block_Request_List *requestQueue,
+ struct Thread_Queue *waitQueue)
+{
+ struct Block_Request *request;
+
+ Disable_Interrupts();
+ while (Is_Block_Request_List_Empty(requestQueue))
+ Wait(waitQueue);
+ request = Get_Front_Of_Block_Request_List(requestQueue);
+ Remove_From_Front_Of_Block_Request_List(requestQueue);
+ Enable_Interrupts();
+
+ return request;
+}
+
+/*
+ * Signal the completion of a block request.
+ */
+void Notify_Request_Completion(struct Block_Request *request, enum Request_State state, int errorCode)
+{
+ Disable_Interrupts();
+ request->state = state;
+ request->errorCode = errorCode;
+ Wake_Up(&request->waitQueue);
+ Enable_Interrupts();
+}
+
+/*
+ * Read a block from given device.
+ * Return 0 if successful, error code on error.
+ */
+int Block_Read(struct Block_Device *dev, int blockNum, void *buf)
+{
+ return Do_Request(dev, BLOCK_READ, blockNum, buf);
+}
+
+/*
+ * Write a block to given device.
+ * Return 0 if successful, error code on error.
+ */
+int Block_Write(struct Block_Device *dev, int blockNum, void *buf)
+{
+ return Do_Request(dev, BLOCK_WRITE, blockNum, buf);
+}
+
+/*
+ * Get number of blocks in given device.
+ */
+int Get_Num_Blocks(struct Block_Device *dev)
+{
+ return dev->ops->Get_Num_Blocks(dev);
+}
+
--- /dev/null
+/*
+ * ATA (aka IDE) driver.
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * NOTES:
+ * 12/22/03 - Converted to use new block device layer with queued requests
+ * 1/20/04 - Changed probing of drives to work on Bochs 2.0 with 2 drives
+ */
+
+#include <geekos/serial.h>
+
+#include <geekos/ktypes.h>
+#include <geekos/kassert.h>
+#include <geekos/errno.h>
+#include <geekos/malloc.h>
+#include <geekos/string.h>
+#include <geekos/io.h>
+#include <geekos/int.h>
+#include <geekos/screen.h>
+#include <geekos/timer.h>
+#include <geekos/kthread.h>
+#include <geekos/blockdev.h>
+#include <geekos/ide.h>
+
+/* Registers */
+#define IDE_DATA_REGISTER 0x1f0
+#define IDE_ERROR_REGISTER 0x1f1
+#define IDE_FEATURE_REG IDE_ERROR_REGISTER
+#define IDE_SECTOR_COUNT_REGISTER 0x1f2
+#define IDE_SECTOR_NUMBER_REGISTER 0x1f3
+#define IDE_CYLINDER_LOW_REGISTER 0x1f4
+#define IDE_CYLINDER_HIGH_REGISTER 0x1f5
+#define IDE_DRIVE_HEAD_REGISTER 0x1f6
+#define IDE_STATUS_REGISTER 0x1f7
+#define IDE_COMMAND_REGISTER 0x1f7
+#define IDE_DEVICE_CONTROL_REGISTER 0x3F6
+
+/* Drives */
+#define IDE_DRIVE_0 0xa0
+#define IDE_DRIVE_1 0xb0
+
+/* Commands */
+#define IDE_COMMAND_IDENTIFY_DRIVE 0xEC
+#define IDE_COMMAND_SEEK 0x70
+#define IDE_COMMAND_READ_SECTORS 0x21
+#define IDE_COMMAND_READ_BUFFER 0xE4
+#define IDE_COMMAND_WRITE_SECTORS 0x30
+#define IDE_COMMAND_WRITE_BUFFER 0xE8
+#define IDE_COMMAND_DIAGNOSTIC 0x90
+#define IDE_COMMAND_ATAPI_IDENT_DRIVE 0xA1
+
+/* Results words from Identify Drive Request */
+#define IDE_INDENTIFY_NUM_CYLINDERS 0x01
+#define IDE_INDENTIFY_NUM_HEADS 0x03
+#define IDE_INDENTIFY_NUM_BYTES_TRACK 0x04
+#define IDE_INDENTIFY_NUM_BYTES_SECTOR 0x05
+#define IDE_INDENTIFY_NUM_SECTORS_TRACK 0x06
+
+/* bits of Status Register */
+#define IDE_STATUS_DRIVE_BUSY 0x80
+#define IDE_STATUS_DRIVE_READY 0x40
+#define IDE_STATUS_DRIVE_WRITE_FAULT 0x20
+#define IDE_STATUS_DRIVE_SEEK_COMPLETE 0x10
+#define IDE_STATUS_DRIVE_DATA_REQUEST 0x08
+#define IDE_STATUS_DRIVE_CORRECTED_DATA 0x04
+#define IDE_STATUS_DRIVE_INDEX 0x02
+#define IDE_STATUS_DRIVE_ERROR 0x01
+
+/* Bits of Device Control Register */
+#define IDE_DCR_NOINTERRUPT 0x02
+#define IDE_DCR_RESET 0x04
+
+/* Return codes from various IDE_* functions */
+#define IDE_ERROR_NO_ERROR 0
+#define IDE_ERROR_BAD_DRIVE -1
+#define IDE_ERROR_INVALID_BLOCK -2
+#define IDE_ERROR_DRIVE_ERROR -3
+
+/* Control register bits */
+#define IDE_CONTROL_REGISTER 0x3F6
+#define IDE_CONTROL_SOFTWARE_RESET 0x04
+#define IDE_CONTROL_INT_DISABLE 0x02
+
+#define LOW_BYTE(x) (x & 0xff)
+#define HIGH_BYTE(x) ((x >> 8) & 0xff)
+
+#define IDE_MAX_DRIVES 2
+
+typedef struct {
+ short num_Cylinders;
+ short num_Heads;
+ short num_SectorsPerTrack;
+ short num_BytesPerSector;
+} ideDisk;
+
+int ideDebug = 99;
+static int numDrives;
+static ideDisk drives[IDE_MAX_DRIVES];
+
+struct Thread_Queue s_ideWaitQueue;
+struct Block_Request_List s_ideRequestQueue;
+
+/*
+ * return the number of logical blocks for a particular drive.
+ *
+ */
+static int IDE_getNumBlocks(int driveNum)
+{
+ if (driveNum < 0 || driveNum > IDE_MAX_DRIVES) {
+ return IDE_ERROR_BAD_DRIVE;
+ }
+
+ return (drives[driveNum].num_Heads *
+ drives[driveNum].num_SectorsPerTrack *
+ drives[driveNum].num_Cylinders);
+}
+
+/*
+ * Read a block at the logical block number indicated.
+ */
+static int IDE_Read(int driveNum, int blockNum, char *buffer)
+{
+ int i;
+ int head;
+ int sector;
+ int cylinder;
+ short *bufferW;
+ int reEnable = 0;
+
+ if (driveNum < 0 || driveNum > (numDrives-1)) {
+ if (ideDebug) Print("ide: invalid drive %d\n", driveNum);
+ return IDE_ERROR_BAD_DRIVE;
+ }
+
+ if (blockNum < 0 || blockNum >= IDE_getNumBlocks(driveNum)) {
+ if (ideDebug) Print("ide: invalid block %d\n", blockNum);
+ return IDE_ERROR_INVALID_BLOCK;
+ }
+
+ if (Interrupts_Enabled()) {
+ Disable_Interrupts();
+ reEnable = 1;
+ }
+
+ /* now compute the head, cylinder, and sector */
+ sector = blockNum % drives[driveNum].num_SectorsPerTrack + 1;
+ cylinder = blockNum / (drives[driveNum].num_Heads *
+ drives[driveNum].num_SectorsPerTrack);
+ head = (blockNum / drives[driveNum].num_SectorsPerTrack) %
+ drives[driveNum].num_Heads;
+
+ if (ideDebug >= 2) {
+ Print ("request to read block %d\n", blockNum);
+ Print (" head %d\n", head);
+ Print (" cylinder %d\n", cylinder);
+ Print (" sector %d\n", sector);
+ }
+
+ Out_Byte(IDE_SECTOR_COUNT_REGISTER, 1);
+ Out_Byte(IDE_SECTOR_NUMBER_REGISTER, sector);
+ Out_Byte(IDE_CYLINDER_LOW_REGISTER, LOW_BYTE(cylinder));
+ Out_Byte(IDE_CYLINDER_HIGH_REGISTER, HIGH_BYTE(cylinder));
+ if (driveNum == 0) {
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_0 | head);
+ } else if (driveNum == 1) {
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_1 | head);
+ }
+
+ Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_READ_SECTORS);
+
+ if (ideDebug > 2) Print("About to wait for Read \n");
+
+ /* wait for the drive */
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+ if (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_ERROR) {
+ Print("ERROR: Got Read %d\n", In_Byte(IDE_STATUS_REGISTER));
+ return IDE_ERROR_DRIVE_ERROR;
+ }
+
+ if (ideDebug > 2) Print("got buffer \n");
+
+ bufferW = (short *) buffer;
+ for (i=0; i < 256; i++) {
+ bufferW[i] = In_Word(IDE_DATA_REGISTER);
+ }
+
+ if (reEnable) Enable_Interrupts();
+
+ return IDE_ERROR_NO_ERROR;
+}
+
+/*
+ * Write a block at the logical block number indicated.
+ */
+static int IDE_Write(int driveNum, int blockNum, char *buffer)
+{
+ int i;
+ int head;
+ int sector;
+ int cylinder;
+ short *bufferW;
+ int reEnable = 0;
+
+ if (driveNum < 0 || driveNum > (numDrives-1)) {
+ return IDE_ERROR_BAD_DRIVE;
+ }
+
+ if (blockNum < 0 || blockNum >= IDE_getNumBlocks(driveNum)) {
+ return IDE_ERROR_INVALID_BLOCK;
+ }
+
+ if (Interrupts_Enabled()) {
+ Disable_Interrupts();
+ reEnable = 1;
+ }
+
+ /* now compute the head, cylinder, and sector */
+ sector = blockNum % drives[driveNum].num_SectorsPerTrack + 1;
+ cylinder = blockNum / (drives[driveNum].num_Heads *
+ drives[driveNum].num_SectorsPerTrack);
+ head = (blockNum / drives[driveNum].num_SectorsPerTrack) %
+ drives[driveNum].num_Heads;
+
+ if (ideDebug) {
+ Print ("request to write block %d\n", blockNum);
+ Print (" head %d\n", head);
+ Print (" cylinder %d\n", cylinder);
+ Print (" sector %d\n", sector);
+ }
+
+ Out_Byte(IDE_SECTOR_COUNT_REGISTER, 1);
+ Out_Byte(IDE_SECTOR_NUMBER_REGISTER, sector);
+ Out_Byte(IDE_CYLINDER_LOW_REGISTER, LOW_BYTE(cylinder));
+ Out_Byte(IDE_CYLINDER_HIGH_REGISTER, HIGH_BYTE(cylinder));
+ if (driveNum == 0) {
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_0 | head);
+ } else if (driveNum == 1) {
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_1 | head);
+ }
+
+ Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_WRITE_SECTORS);
+
+
+ /* wait for the drive */
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+ bufferW = (short *) buffer;
+ for (i=0; i < 256; i++) {
+ Out_Word(IDE_DATA_REGISTER, bufferW[i]);
+ }
+
+ if (ideDebug) Print("About to wait for Write \n");
+
+ /* wait for the drive */
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+ if (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_ERROR) {
+ Print("ERROR: Got Read %d\n", In_Byte(IDE_STATUS_REGISTER));
+ return IDE_ERROR_DRIVE_ERROR;
+ }
+
+ if (reEnable) Enable_Interrupts();
+
+ return IDE_ERROR_NO_ERROR;
+}
+
+static int IDE_Open(struct Block_Device *dev)
+{
+ KASSERT(!dev->inUse);
+ return 0;
+}
+
+static int IDE_Close(struct Block_Device *dev)
+{
+ KASSERT(dev->inUse);
+ return 0;
+}
+
+static int IDE_Get_Num_Blocks(struct Block_Device *dev)
+{
+ return IDE_getNumBlocks(dev->unit);
+}
+
+static struct Block_Device_Ops s_ideDeviceOps = {
+ IDE_Open,
+ IDE_Close,
+ IDE_Get_Num_Blocks,
+};
+
+static void IDE_Request_Thread(ulong_t arg)
+{
+ for (;;) {
+ struct Block_Request *request;
+ int rc;
+
+ /* Wait for a request to arrive */
+ request = Dequeue_Request(&s_ideRequestQueue, &s_ideWaitQueue);
+
+ /* Do the I/O */
+ if (request->type == BLOCK_READ)
+ rc = IDE_Read(request->dev->unit, request->blockNum, request->buf);
+ else
+ rc = IDE_Write(request->dev->unit, request->blockNum, request->buf);
+
+ /* Notify requesting thread of final status */
+ Notify_Request_Completion(request, rc == 0 ? COMPLETED : ERROR, rc);
+ }
+}
+
+static int readDriveConfig(int drive)
+{
+ int i;
+ int status;
+ short info[256];
+ char devname[BLOCKDEV_MAX_NAME_LEN];
+ int rc;
+
+ if (ideDebug) Print("ide: about to read drive config for drive #%d\n", drive);
+
+
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, (drive == 0) ? IDE_DRIVE_0 : IDE_DRIVE_1);
+ Print("Set head register\n");
+
+ Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_IDENTIFY_DRIVE);
+
+ Print ("identify drive\n");
+
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+ Print ("Status no longer busy\n");
+
+ status = In_Byte(IDE_STATUS_REGISTER);
+
+ Print ("Status is %x\n",status);
+ /*
+ * simulate failure
+ * status = 0x50;
+ */
+ if ((status & IDE_STATUS_DRIVE_DATA_REQUEST)) {
+ Print("ide: probe found ATA drive\n");
+ /* drive responded to ATA probe */
+ for (i=0; i < 256; i++) {
+ info[i] = In_Word(IDE_DATA_REGISTER);
+ }
+
+ drives[drive].num_Cylinders = info[IDE_INDENTIFY_NUM_CYLINDERS];
+ drives[drive].num_Heads = info[IDE_INDENTIFY_NUM_HEADS];
+ drives[drive].num_SectorsPerTrack = info[IDE_INDENTIFY_NUM_SECTORS_TRACK];
+ drives[drive].num_BytesPerSector = info[IDE_INDENTIFY_NUM_BYTES_SECTOR];
+ } else {
+ /* try for ATAPI */
+ Print("Trying for ATAPI\n");
+ Out_Byte(IDE_FEATURE_REG, 0); /* disable dma & overlap */
+ Print("Out FEATURE REG\n");
+
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, (drive == 0) ? IDE_DRIVE_0 : IDE_DRIVE_1);
+ Print("Out Head Select\n");
+ Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_ATAPI_IDENT_DRIVE);
+ Print("Out Ident drive\n");
+
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+ Print("No longer busy\n");
+
+ status = In_Byte(IDE_STATUS_REGISTER);
+
+ Print("status is %x\n",status);
+ Print("ide: found atapi drive\n");
+ return -1;
+ }
+
+ Print(" ide%d: cyl=%d, heads=%d, sectors=%d\n", drive, drives[drive].num_Cylinders,
+ drives[drive].num_Heads, drives[drive].num_SectorsPerTrack);
+
+ /* Register the drive as a block device */
+ snprintf(devname, sizeof(devname), "ide%d", drive);
+ rc = Register_Block_Device(devname, &s_ideDeviceOps, drive, 0, &s_ideWaitQueue, &s_ideRequestQueue);
+ if (rc != 0)
+ Print(" Error: could not create block device for %s\n", devname);
+
+ return 0;
+}
+
+
+void Init_IDE(void)
+{
+ int errorCode;
+
+ // Check to see if controller 0 is present
+ Out_Byte(0x1f3,0x88);
+ if (In_Byte(0x1f3)==0x88) {
+ Print("IDE Controller 0 is present\n");
+ }
+
+ // Check to see if controller 1 is present
+ Out_Byte(0x173,0x88);
+ if (In_Byte(0x173)==0x88) {
+ Print("IDE Controller 1 is present\n");
+ }
+
+ Print("Initializing IDE controller (0x1f0)...\n");
+
+ /* Reset the controller and drives */
+ Out_Byte(IDE_DEVICE_CONTROL_REGISTER, IDE_DCR_NOINTERRUPT | IDE_DCR_RESET);
+ Micro_Delay(100);
+ Out_Byte(IDE_DEVICE_CONTROL_REGISTER, IDE_DCR_NOINTERRUPT);
+
+/*
+ * FIXME: This code doesn't work on Bochs 2.0.
+ * while ((In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_READY) == 0)
+ * ;
+ */
+
+ /* This code does work on Bochs 2.0. */
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY)
+ ;
+
+ if (ideDebug) Print("About to run drive Diagnosis\n");
+
+ Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_DIAGNOSTIC);
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+ errorCode = In_Byte(IDE_ERROR_REGISTER);
+ if (ideDebug) Print("ide: ide error register = %x\n", errorCode);
+
+ /* Probe and register drives */
+ if (readDriveConfig(0) == 0)
+ ++numDrives;
+ if (readDriveConfig(1) == 0)
+ ++numDrives;
+ if (ideDebug) Print("Found %d IDE drives\n", numDrives);
+
+ /* Start request thread */
+ if (numDrives > 0)
+ Start_Kernel_Thread(IDE_Request_Thread, 0, PRIORITY_NORMAL, true);
+}