786 lines
23 KiB
C
786 lines
23 KiB
C
/* Copyright (C) 2005 Thomas Petazzoni, David Decotigny
|
|
|
|
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
USA.
|
|
*/
|
|
|
|
#include <sos/types.h>
|
|
#include <sos/errno.h>
|
|
#include <sos/assert.h>
|
|
#include <sos/ksynch.h>
|
|
#include <drivers/devices.h>
|
|
#include <drivers/bochs.h>
|
|
#include <drivers/part.h>
|
|
#include <hwcore/irq.h>
|
|
#include <hwcore/ioports.h>
|
|
|
|
|
|
/**
|
|
* @file ide.c
|
|
*
|
|
* Basic PIO IDE implementation based on the ATA standards
|
|
* http://www.t13.org/
|
|
*/
|
|
|
|
|
|
/**
|
|
* Busy-wait for a given time
|
|
*
|
|
* @param delay Delay to wait
|
|
*
|
|
* @todo Implement a calibration routine
|
|
*/
|
|
static void udelay(int delay)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < (delay * 1000) ; i++)
|
|
{
|
|
i++; i--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Each IDE controller is controlled through a set of 9 I/O ports,
|
|
* starting from a base address, which is different for each IDE
|
|
* controller. On a standard PC with two onboard IDE controllers, the
|
|
* I/O registers of the first controller start at 0x1F0
|
|
* (IDE_CONTROLLER_0_BASE), the I/O registers of the second controller
|
|
* start at 0x170 (IDE_CONTROLLER_1_BASE). These I/O registers are
|
|
* 8-bits wide, and can be read using the inb() macro, and written
|
|
* using the outb() macro.
|
|
*
|
|
* The first controller is generally known as "primary" controller,
|
|
* and the second one is know as "secondary" controller. Each of them
|
|
* can handle at most two devices : the "master" device and the
|
|
* "slave" device.
|
|
*
|
|
* The registers can be used to issue commands and send data to the
|
|
* IDE controller, for example to identify a device, to read or write
|
|
* a device. Then are different ways of transmitting data to the IDE
|
|
* controller :
|
|
*
|
|
* - program the IDE controller, send the data, and wait for the
|
|
* completion of the request. This method is called "polling".
|
|
*
|
|
* - program the IDE controller, send the data, block the current
|
|
* process and do something else. The completion of the request will
|
|
* be signaled asynchronously by an interrupt (IRQ), which will
|
|
* allow to restart the sleeping process.
|
|
*
|
|
* - program the IDE controller and the DMA controller. There's no
|
|
* need to transfer the data to the IDE controller : the DMA
|
|
* controller will do it. This allows to use the CPU to do something
|
|
* useful during the transfer of data between the main memory and
|
|
* the IDE controller.
|
|
*
|
|
* In this driver, we use the two first methods. The polling method is
|
|
* used to fetch the identification of the devices, while the IRQ
|
|
* method is used to read and write to devices.
|
|
*/
|
|
|
|
#define IDE_CONTROLLER_0_BASE 0x1F0
|
|
#define IDE_CONTROLLER_1_BASE 0x170
|
|
|
|
#define IDE_CONTROLLER_0_IRQ 14
|
|
#define IDE_CONTROLLER_1_IRQ 15
|
|
|
|
/*
|
|
* All defines below are relative to the base address of the I/O
|
|
* registers (IDE_CONTROLLER_0_BASE and IDE_CONTROLLER_1_BASE)
|
|
*/
|
|
|
|
/**
|
|
* Read/write register that allows to transfer the data to be written
|
|
* or to fetch the read data from the IDE controller.
|
|
*/
|
|
#define ATA_DATA 0x00
|
|
|
|
/**
|
|
* Read only register that gives information about errors that occured
|
|
* during operation
|
|
*/
|
|
#define ATA_ERROR 0x01
|
|
|
|
/**
|
|
* Write only register to set precompensation. It is in fact the same
|
|
* register as the ATA_ERROR register. It simply has a different
|
|
* behaviour when reading and writing it.
|
|
*/
|
|
#define ATA_PRECOMP 0x01
|
|
|
|
/**
|
|
* Write only register used to set the count of sectors on which the
|
|
* request applies.
|
|
*/
|
|
#define ATA_SECTOR_COUNT 0x02
|
|
|
|
/**
|
|
* Write only register used to set the number of the starting sector
|
|
* of the request.
|
|
*/
|
|
#define ATA_SECTOR_NUMBER 0x03
|
|
|
|
/**
|
|
* Write only register used to set the 8 lower bits of the starting
|
|
* cylinder number of the request
|
|
*/
|
|
#define ATA_CYL_LSB 0x04
|
|
|
|
/**
|
|
* Write only register used to set the 8 higher bits of the starting
|
|
* cylinder number of the request
|
|
*/
|
|
#define ATA_CYL_MSB 0x05
|
|
|
|
/**
|
|
* Write only register that allows to select whether the LBA mode
|
|
* should be used, and to select whether the request concerns the
|
|
* slave or master device.
|
|
*/
|
|
#define ATA_DRIVE 0x06
|
|
#define ATA_D_IBM 0xa0 /* bits that must be set */
|
|
#define ATA_D_LBA 0x40 /* use LBA ? */
|
|
#define ATA_D_MASTER 0x00 /* select master */
|
|
#define ATA_D_SLAVE 0x10 /* select slave */
|
|
|
|
/**
|
|
* Read only register that contains the status of the controller. Each
|
|
* bit of this register as a different signification.
|
|
*/
|
|
#define ATA_STATUS 0x07
|
|
#define ATA_S_ERROR 0x01 /* error */
|
|
#define ATA_S_INDEX 0x02 /* index */
|
|
#define ATA_S_CORR 0x04 /* data corrected */
|
|
#define ATA_S_DRQ 0x08 /* data request */
|
|
#define ATA_S_DSC 0x10 /* drive Seek Completed */
|
|
#define ATA_S_DWF 0x20 /* drive write fault */
|
|
#define ATA_S_DRDY 0x40 /* drive ready */
|
|
#define ATA_S_BSY 0x80 /* busy */
|
|
|
|
/**
|
|
* Write only register used to set the command that the IDE controller
|
|
* should process. In our driver, only ATA_C_ATA_IDENTIFY, ATA_C_READ
|
|
* and ATA_C_WRITE are used.
|
|
*/
|
|
#define ATA_CMD 0x07
|
|
#define ATA_C_ATA_IDENTIFY 0xec /* get ATA params */
|
|
#define ATA_C_ATAPI_IDENTIFY 0xa1 /* get ATAPI params*/
|
|
#define ATA_C_READ 0x20 /* read command */
|
|
#define ATA_C_WRITE 0x30 /* write command */
|
|
#define ATA_C_READ_MULTI 0xc4 /* read multi command */
|
|
#define ATA_C_WRITE_MULTI 0xc5 /* write multi command */
|
|
#define ATA_C_SET_MULTI 0xc6 /* set multi size command */
|
|
#define ATA_C_PACKET_CMD 0xa0 /* set multi size command */
|
|
|
|
/**
|
|
* Read register that contains more information about the status of
|
|
* the controller
|
|
*/
|
|
#define ATA_ALTPORT 0x206 /* (R) alternate
|
|
Status register */
|
|
|
|
/**
|
|
* Write only register that allows to control the controller
|
|
*/
|
|
#define ATA_DEVICE_CONTROL 0x206 /* (W) device control
|
|
register */
|
|
#define ATA_A_nIEN 0x02 /* disable interrupts */
|
|
#define ATA_A_RESET 0x04 /* RESET controller */
|
|
#define ATA_A_4BIT 0x08 /* 4 head bits */
|
|
|
|
/** Magic numbers used to detect ATAPI devices */
|
|
#define ATAPI_MAGIC_LSB 0x14
|
|
#define ATAPI_MAGIC_MSB 0xeb
|
|
|
|
/* This structure describe the informations returned by the Identify
|
|
Device command (imposed by the ATA standard) */
|
|
struct ide_device_info
|
|
{
|
|
sos_ui16_t general_config_info; /* 0 */
|
|
sos_ui16_t nb_logical_cylinders; /* 1 */
|
|
sos_ui16_t reserved1; /* 2 */
|
|
sos_ui16_t nb_logical_heads; /* 3 */
|
|
sos_ui16_t unformatted_bytes_track; /* 4 */
|
|
sos_ui16_t unformatted_bytes_sector; /* 5 */
|
|
sos_ui16_t nb_logical_sectors; /* 6 */
|
|
sos_ui16_t vendor1[3]; /* 7-9 */
|
|
sos_ui8_t serial_number[20]; /* 10-19 */
|
|
sos_ui16_t buffer_type; /* 20 */
|
|
sos_ui16_t buffer_size; /* 21 */
|
|
sos_ui16_t ecc_bytes; /* 22 */
|
|
sos_ui8_t firmware_revision[8]; /* 23-26 */
|
|
sos_ui8_t model_number[40]; /* 27-46 */
|
|
sos_ui8_t max_multisect; /* 47 */
|
|
sos_ui8_t vendor2;
|
|
sos_ui16_t dword_io; /* 48 */
|
|
sos_ui8_t vendor3; /* 49 */
|
|
sos_ui8_t capabilities;
|
|
sos_ui16_t reserved2; /* 50 */
|
|
sos_ui8_t vendor4; /* 51 */
|
|
sos_ui8_t pio_trans_mode;
|
|
sos_ui8_t vendor5; /* 52 */
|
|
sos_ui8_t dma_trans_mode;
|
|
sos_ui16_t fields_valid; /* 53 */
|
|
sos_ui16_t cur_logical_cylinders; /* 54 */
|
|
sos_ui16_t cur_logical_heads; /* 55 */
|
|
sos_ui16_t cur_logical_sectors; /* 56 */
|
|
sos_ui16_t capacity1; /* 57 */
|
|
sos_ui16_t capacity0; /* 58 */
|
|
sos_ui8_t multsect; /* 59 */
|
|
sos_ui8_t multsect_valid;
|
|
sos_ui32_t lba_capacity; /* 60-61 */
|
|
sos_ui16_t dma_1word; /* 62 */
|
|
sos_ui16_t dma_multiword; /* 63 */
|
|
sos_ui16_t pio_modes; /* 64 */
|
|
sos_ui16_t min_mword_dma; /* 65 */
|
|
sos_ui16_t recommended_mword_dma; /* 66 */
|
|
sos_ui16_t min_pio_cycle_time; /* 67 */
|
|
sos_ui16_t min_pio_cycle_time_iordy; /* 68 */
|
|
sos_ui16_t reserved3[11]; /* 69-79 */
|
|
sos_ui16_t major_version; /* 80 */
|
|
sos_ui16_t minor_version; /* 81 */
|
|
sos_ui16_t command_sets1; /* 82 */
|
|
sos_ui16_t command_sets2; /* 83 dixit lk : b14 (smart enabled) */
|
|
sos_ui16_t reserved4[4]; /* 84-87 */
|
|
sos_ui16_t dma_ultra; /* 88 dixit lk */
|
|
sos_ui16_t reserved5[37]; /* 89-125 */
|
|
sos_ui16_t last_lun; /* 126 */
|
|
sos_ui16_t reserved6; /* 127 */
|
|
sos_ui16_t security; /* 128 */
|
|
sos_ui16_t reserved7[127];
|
|
} __attribute__((packed));
|
|
|
|
#define MAX_IDE_CONTROLLERS 2
|
|
#define MAX_IDE_DEVICES 2
|
|
|
|
#define IDE_BLK_SIZE 512
|
|
|
|
#define IDE_DEVICE(ctrl,device) (((ctrl) * 2) + (device))
|
|
#define IDE_MINOR(ctrl,device) (IDE_DEVICE(ctrl,device)*16)
|
|
|
|
typedef enum { IDE_DEVICE_NONE,
|
|
IDE_DEVICE_HARDDISK,
|
|
IDE_DEVICE_CDROM } ide_device_type_t;
|
|
|
|
struct ide_controller;
|
|
struct ide_device {
|
|
int id;
|
|
ide_device_type_t type;
|
|
enum { IDE_DEVICE_MASTER, IDE_DEVICE_SLAVE } position;
|
|
int cyls;
|
|
int heads;
|
|
int sectors;
|
|
int blocks;
|
|
sos_bool_t support_lba;
|
|
struct ide_controller *ctrl;
|
|
};
|
|
|
|
struct ide_controller {
|
|
int id;
|
|
int ioaddr;
|
|
int irq;
|
|
enum { IDE_CTRL_NOT_PRESENT, IDE_CTRL_PRESENT } state;
|
|
struct ide_device devices[MAX_IDE_DEVICES];
|
|
struct sos_kmutex mutex;
|
|
|
|
struct sos_ksema ack_io_sem;
|
|
};
|
|
|
|
struct ide_controller ide_controllers[MAX_IDE_CONTROLLERS] = {
|
|
{
|
|
.id = 0,
|
|
.ioaddr = IDE_CONTROLLER_0_BASE,
|
|
.irq = IDE_CONTROLLER_0_IRQ,
|
|
.state = IDE_CTRL_NOT_PRESENT,
|
|
},
|
|
{
|
|
.id = 1,
|
|
.ioaddr = IDE_CONTROLLER_1_BASE,
|
|
.irq = IDE_CONTROLLER_1_IRQ,
|
|
.state = IDE_CTRL_NOT_PRESENT
|
|
}
|
|
};
|
|
|
|
void ide_irq_handler (int irq_level)
|
|
{
|
|
int i;
|
|
struct ide_controller *ctrl = NULL;
|
|
|
|
for (i = 0; i < MAX_IDE_CONTROLLERS ; i++)
|
|
{
|
|
if (ide_controllers[i].irq == irq_level)
|
|
{
|
|
ctrl = & ide_controllers[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
SOS_ASSERT_FATAL (ctrl != NULL);
|
|
|
|
sos_ksema_up (& ctrl->ack_io_sem);
|
|
}
|
|
|
|
static int ide_get_device_info (struct ide_device *dev)
|
|
{
|
|
int devselect;
|
|
sos_ui16_t buffer[256];
|
|
struct ide_device_info *info;
|
|
int timeout, i;
|
|
int status;
|
|
|
|
SOS_ASSERT_FATAL (dev->type == IDE_DEVICE_HARDDISK);
|
|
|
|
if (dev->position == IDE_DEVICE_MASTER)
|
|
devselect = ATA_D_MASTER;
|
|
else
|
|
devselect = ATA_D_SLAVE;
|
|
|
|
/* Ask the controller to NOT send interrupts to acknowledge
|
|
commands */
|
|
outb(ATA_A_nIEN | ATA_A_4BIT, dev->ctrl->ioaddr + ATA_DEVICE_CONTROL);
|
|
udelay(1);
|
|
|
|
/* Select the device (master or slave) */
|
|
outb(ATA_D_IBM | devselect, dev->ctrl->ioaddr + ATA_DRIVE);
|
|
|
|
/* Send the IDENTIFY command */
|
|
outb(ATA_C_ATA_IDENTIFY, dev->ctrl->ioaddr + ATA_CMD);
|
|
|
|
/* Wait for command completion (wait while busy bit is set) */
|
|
for(timeout = 0; timeout < 30000; timeout++)
|
|
{
|
|
status = inb(dev->ctrl->ioaddr + ATA_STATUS);
|
|
if(!(status & ATA_S_BSY))
|
|
break;
|
|
|
|
udelay(1);
|
|
}
|
|
|
|
/* DRQ bit indicates that data is ready to be read. If it is not set
|
|
after an IDENTIFY command, there is a problem */
|
|
if(! (status & ATA_S_DRQ))
|
|
{
|
|
return SOS_EFATAL;
|
|
}
|
|
|
|
/* Read data from the controller buffer to a temporary buffer */
|
|
for(i = 0; i < 256; i++)
|
|
buffer[i] = inw(dev->ctrl->ioaddr + ATA_DATA);
|
|
|
|
/* The buffer contains an information structure, defined by ATA
|
|
specification. To ease its access, we use the previously defined
|
|
ide_device_info structure. */
|
|
info = (struct ide_device_info *) buffer;
|
|
|
|
/* Fetch intersting informations from the structure */
|
|
dev->heads = info->nb_logical_heads;
|
|
dev->cyls = info->nb_logical_cylinders;
|
|
dev->sectors = info->nb_logical_sectors;
|
|
|
|
dev->support_lba = FALSE;
|
|
|
|
/* Determines if device supports LBA. The method is a bit ugly, but
|
|
there's no other way */
|
|
if (info->capabilities & (1 << 1)
|
|
&& info->major_version
|
|
&& (info->fields_valid & 1)
|
|
&& (info->lba_capacity & 1))
|
|
dev->support_lba = TRUE;
|
|
|
|
/* Determines the capacity of the device */
|
|
if (dev->heads == 16 &&
|
|
dev->sectors == 63 &&
|
|
dev->cyls == 16383)
|
|
{
|
|
if (dev->support_lba)
|
|
dev->blocks = info->lba_capacity;
|
|
else
|
|
return SOS_EFATAL;
|
|
}
|
|
else
|
|
dev->blocks = dev->cyls * dev->sectors * dev->heads;
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
static ide_device_type_t ide_probe_device (struct ide_device *dev)
|
|
{
|
|
int status;
|
|
int devselect;
|
|
|
|
if (dev->position == IDE_DEVICE_MASTER)
|
|
devselect = ATA_D_MASTER;
|
|
else
|
|
devselect = ATA_D_SLAVE;
|
|
|
|
/* Select the given device */
|
|
outb (ATA_D_IBM | devselect, dev->ctrl->ioaddr + ATA_DRIVE);
|
|
|
|
/* Read the status of the device */
|
|
status = inb(dev->ctrl->ioaddr + ATA_STATUS);
|
|
|
|
/* If status indicates a busy device, it means that there's no
|
|
device */
|
|
if (status & ATA_S_BSY)
|
|
return IDE_DEVICE_NONE;
|
|
|
|
/* If status indicates that drive is ready and drive has complete
|
|
seeking, then we've got an hard drive */
|
|
if (status & (ATA_S_DRDY | ATA_S_DSC))
|
|
return IDE_DEVICE_HARDDISK;
|
|
|
|
/* Detect CD-ROM drives by reading the cylinder low byte and
|
|
cylinder high byte, and check if they match magic values */
|
|
if(inb(dev->ctrl->ioaddr + ATA_CYL_LSB) == ATAPI_MAGIC_LSB &&
|
|
inb(dev->ctrl->ioaddr + ATA_CYL_MSB) == ATAPI_MAGIC_MSB)
|
|
return IDE_DEVICE_CDROM;
|
|
|
|
return IDE_DEVICE_NONE;
|
|
}
|
|
|
|
static sos_ret_t ide_probe_controller(struct ide_controller *ctrl)
|
|
{
|
|
sos_bool_t hasdevice = FALSE;
|
|
|
|
sos_kmutex_init (& ctrl->mutex, "ide-mutex", SOS_KWQ_ORDER_FIFO);
|
|
sos_ksema_init (& ctrl->ack_io_sem, "ide-sem", 0, SOS_KWQ_ORDER_FIFO);
|
|
|
|
/* Master */
|
|
ctrl->devices[0].id = 0;
|
|
ctrl->devices[0].position = IDE_DEVICE_MASTER;
|
|
ctrl->devices[0].ctrl = ctrl;
|
|
ctrl->devices[0].type = ide_probe_device (& ctrl->devices[0]);
|
|
|
|
if (ctrl->devices[0].type == IDE_DEVICE_HARDDISK)
|
|
{
|
|
ide_get_device_info (& ctrl->devices[0]);
|
|
hasdevice = TRUE;
|
|
}
|
|
|
|
/* Slave */
|
|
ctrl->devices[1].id = 1;
|
|
ctrl->devices[1].position = IDE_DEVICE_SLAVE;
|
|
ctrl->devices[1].ctrl = ctrl;
|
|
ctrl->devices[1].type = ide_probe_device (& ctrl->devices[1]);
|
|
|
|
if (ctrl->devices[1].type == IDE_DEVICE_HARDDISK)
|
|
{
|
|
ide_get_device_info (& ctrl->devices[1]);
|
|
hasdevice = TRUE;
|
|
}
|
|
|
|
if (hasdevice)
|
|
sos_irq_set_routine (ctrl->irq, ide_irq_handler);
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
|
|
static sos_ret_t ide_io_operation (struct ide_device *dev,
|
|
sos_vaddr_t buf, int block,
|
|
sos_bool_t iswrite)
|
|
{
|
|
sos_ui8_t cyl_lo, cyl_hi, sect, head, status;
|
|
int devselect, i;
|
|
|
|
SOS_ASSERT_FATAL (dev->type == IDE_DEVICE_HARDDISK);
|
|
|
|
if (block > dev->blocks)
|
|
return -SOS_EFATAL;
|
|
|
|
if (dev->position == IDE_DEVICE_MASTER)
|
|
devselect = ATA_D_MASTER;
|
|
else
|
|
devselect = ATA_D_SLAVE;
|
|
|
|
/* Compute the position of the block in the device in terms of
|
|
cylinders, sectors and heads. As cylinders must be sent in two
|
|
separate parts, we compute it that way. */
|
|
if (dev->support_lba)
|
|
{
|
|
sect = (block & 0xff);
|
|
cyl_lo = (block >> 8) & 0xff;
|
|
cyl_hi = (block >> 16) & 0xff;
|
|
head = ((block >> 24) & 0x7) | 0x40;
|
|
}
|
|
else
|
|
{
|
|
int cylinder = block /
|
|
(dev->heads * dev->sectors);
|
|
int temp = block %
|
|
(dev->heads * dev->sectors);
|
|
cyl_lo = cylinder & 0xff;
|
|
cyl_hi = (cylinder >> 8) & 0xff;
|
|
head = temp / dev->sectors;
|
|
sect = (temp % dev->sectors) + 1;
|
|
}
|
|
|
|
/* Make sure nobody is using the same controller at the same time */
|
|
sos_kmutex_lock (& dev->ctrl->mutex, NULL);
|
|
|
|
/* Select device */
|
|
outb(ATA_D_IBM | devselect, dev->ctrl->ioaddr + ATA_DRIVE);
|
|
udelay(100);
|
|
|
|
/* Write to registers */
|
|
outb(ATA_A_4BIT, dev->ctrl->ioaddr + ATA_DEVICE_CONTROL);
|
|
outb(1, dev->ctrl->ioaddr + ATA_ERROR);
|
|
outb(0, dev->ctrl->ioaddr + ATA_PRECOMP);
|
|
outb(1, dev->ctrl->ioaddr + ATA_SECTOR_COUNT);
|
|
outb(sect, dev->ctrl->ioaddr + ATA_SECTOR_NUMBER);
|
|
outb(cyl_lo, dev->ctrl->ioaddr + ATA_CYL_LSB);
|
|
outb(cyl_hi, dev->ctrl->ioaddr + ATA_CYL_MSB);
|
|
outb((ATA_D_IBM | devselect | head),
|
|
dev->ctrl->ioaddr + ATA_DRIVE);
|
|
|
|
/* Send the command, either read or write */
|
|
if (iswrite)
|
|
outb(ATA_C_WRITE, dev->ctrl->ioaddr + ATA_CMD);
|
|
else
|
|
outb(ATA_C_READ, dev->ctrl->ioaddr + ATA_CMD);
|
|
|
|
/* Wait for the device ready to transfer */
|
|
do { udelay(1); } while (inb(dev->ctrl->ioaddr + ATA_STATUS) & ATA_S_BSY);
|
|
|
|
/* If an error was detected, stop here */
|
|
if (inb(dev->ctrl->ioaddr + ATA_STATUS) & ATA_S_ERROR)
|
|
{
|
|
sos_kmutex_unlock (& dev->ctrl->mutex);
|
|
return -SOS_EFATAL;
|
|
}
|
|
|
|
/* If it's a write I/O, transfer the contents of the buffer provided
|
|
by the user to the controller internal buffer, so that the
|
|
controller can write the data to the disk */
|
|
if (iswrite)
|
|
{
|
|
/* Wait for the DRQ bit to be set */
|
|
while (1)
|
|
{
|
|
status = inb(dev->ctrl->ioaddr + ATA_STATUS);
|
|
if (status & ATA_S_ERROR)
|
|
{
|
|
sos_kmutex_unlock (& dev->ctrl->mutex);
|
|
return -SOS_EFATAL;
|
|
}
|
|
|
|
if (!(status & ATA_S_BSY) && (status & ATA_S_DRQ))
|
|
break;
|
|
}
|
|
|
|
/* Do the transfer to the device's buffer */
|
|
sos_ui16_t *buffer = (sos_ui16_t *) buf;
|
|
for (i = 0 ; i < 256 ; i++)
|
|
outw (buffer[i], dev->ctrl->ioaddr + ATA_DATA);
|
|
|
|
/* Wait for the device to have received all the data */
|
|
while (1)
|
|
{
|
|
status = inb(dev->ctrl->ioaddr + ATA_STATUS);
|
|
if (status & ATA_S_ERROR)
|
|
{
|
|
sos_kmutex_unlock (& dev->ctrl->mutex);
|
|
return -SOS_EFATAL;
|
|
}
|
|
|
|
if (!(status & ATA_S_BSY) && !(status & ATA_S_DRQ))
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Sleep until the IRQ wakes us up */
|
|
sos_ksema_down (& dev->ctrl->ack_io_sem, NULL);
|
|
|
|
/* ATA specs tell to read the alternate status reg and ignore its
|
|
result */
|
|
inb(dev->ctrl->ioaddr + ATA_ALTPORT);
|
|
|
|
if (! iswrite)
|
|
{
|
|
/* Wait for the DRQ bit to be set */
|
|
while (1)
|
|
{
|
|
status = inb(dev->ctrl->ioaddr + ATA_STATUS);
|
|
if (status & ATA_S_ERROR)
|
|
{
|
|
sos_kmutex_unlock (& dev->ctrl->mutex);
|
|
return -SOS_EFATAL;
|
|
}
|
|
|
|
if (!(status & ATA_S_BSY) && (status & ATA_S_DRQ))
|
|
break;
|
|
}
|
|
|
|
/* copy data from the controller internal buffer to the buffer
|
|
provided by the user */
|
|
sos_ui16_t *buffer = (sos_ui16_t *) buf;
|
|
for (i = 0 ; i < 256 ; i++)
|
|
buffer [i] = inw (dev->ctrl->ioaddr + ATA_DATA);
|
|
|
|
/* ATA specs tell to read the alternate status reg and ignore its
|
|
result */
|
|
inb(dev->ctrl->ioaddr + ATA_ALTPORT);
|
|
}
|
|
|
|
/* If an error was detected, stop here */
|
|
if (inb(dev->ctrl->ioaddr + ATA_STATUS) & ATA_S_ERROR)
|
|
{
|
|
sos_kmutex_unlock (& dev->ctrl->mutex);
|
|
return -SOS_EFATAL;
|
|
}
|
|
|
|
/* Someone else can safely use devices on this controller for other
|
|
requests */
|
|
sos_kmutex_unlock (& dev->ctrl->mutex);
|
|
return SOS_OK;
|
|
}
|
|
|
|
static sos_ret_t
|
|
ide_read_device (void *blkdev_instance, sos_vaddr_t dest_buf,
|
|
sos_luoffset_t block_offset)
|
|
{
|
|
struct ide_device *dev;
|
|
|
|
dev = (struct ide_device *) blkdev_instance;
|
|
|
|
return ide_io_operation (dev, dest_buf, block_offset, FALSE);
|
|
}
|
|
|
|
static sos_ret_t
|
|
ide_write_device (void *blkdev_instance, sos_vaddr_t src_buf,
|
|
sos_luoffset_t block_offset)
|
|
{
|
|
struct ide_device *dev;
|
|
|
|
dev = (struct ide_device *) blkdev_instance;
|
|
|
|
return ide_io_operation (dev, src_buf, block_offset, TRUE);
|
|
}
|
|
|
|
static struct sos_blockdev_operations ide_ops = {
|
|
.read_block = ide_read_device,
|
|
.write_block = ide_write_device,
|
|
.ioctl = NULL
|
|
};
|
|
|
|
|
|
static sos_ret_t
|
|
ide_register_devices (void)
|
|
{
|
|
int ctrl, dev;
|
|
sos_ret_t ret;
|
|
|
|
for (ctrl = 0; ctrl < MAX_IDE_CONTROLLERS ; ctrl++)
|
|
{
|
|
for (dev = 0; dev < MAX_IDE_DEVICES ; dev++)
|
|
{
|
|
char name[16];
|
|
struct ide_device *device;
|
|
|
|
device = & ide_controllers[ctrl].devices[dev];
|
|
|
|
snprintf (name, sizeof(name), "hd%c", ('a' + IDE_DEVICE(ctrl, dev)));
|
|
|
|
if (device->type == IDE_DEVICE_HARDDISK)
|
|
{
|
|
sos_bochs_printf("%s: harddisk %d Mb <", name,
|
|
(device->blocks * IDE_BLK_SIZE >> 20));
|
|
ret = sos_blockdev_register_disk (SOS_BLOCKDEV_IDE_MAJOR,
|
|
IDE_MINOR(ctrl, dev),
|
|
IDE_BLK_SIZE, device->blocks,
|
|
128, & ide_ops, device);
|
|
if (ret != SOS_OK)
|
|
{
|
|
sos_bochs_printf ("Warning: could not register disk>\n");
|
|
continue;
|
|
}
|
|
|
|
ret = sos_part_detect (SOS_BLOCKDEV_IDE_MAJOR,
|
|
IDE_MINOR(ctrl, dev), IDE_BLK_SIZE,
|
|
name);
|
|
if (ret != SOS_OK)
|
|
{
|
|
sos_bochs_printf ("Warning could not detect partitions (%d)>\n",
|
|
ret);
|
|
continue;
|
|
}
|
|
|
|
/* Finalize output */
|
|
sos_bochs_printf (">\n");
|
|
}
|
|
|
|
else if (device->type == IDE_DEVICE_CDROM)
|
|
sos_bochs_printf("%s: CDROM\n", name);
|
|
}
|
|
}
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
static sos_ret_t
|
|
ide_unregister_devices (void)
|
|
{
|
|
int ctrl, dev;
|
|
sos_ret_t ret;
|
|
|
|
for (ctrl = 0; ctrl < MAX_IDE_CONTROLLERS ; ctrl++)
|
|
{
|
|
for (dev = 0; dev < MAX_IDE_DEVICES ; dev++)
|
|
{
|
|
struct ide_device *device;
|
|
|
|
device = & ide_controllers[ctrl].devices[dev];
|
|
|
|
if (device->type != IDE_DEVICE_HARDDISK)
|
|
continue;
|
|
|
|
sos_part_undetect (SOS_BLOCKDEV_IDE_MAJOR, IDE_MINOR(ctrl,dev));
|
|
|
|
ret = sos_blockdev_unregister_device (SOS_BLOCKDEV_IDE_MAJOR,
|
|
IDE_MINOR(ctrl,dev));
|
|
if (ret != SOS_OK)
|
|
sos_bochs_printf ("Error while unregistering device %d:%d\n",
|
|
SOS_BLOCKDEV_IDE_MAJOR, IDE_MINOR(ctrl,dev));
|
|
}
|
|
}
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
|
|
sos_ret_t sos_ide_subsystem_setup (void)
|
|
{
|
|
sos_ret_t ret;
|
|
|
|
ret = ide_probe_controller(& ide_controllers[0]);
|
|
if (ret != SOS_OK)
|
|
sos_bochs_printf ("Error while probing IDE controller 0\n");
|
|
|
|
ret =ide_probe_controller(& ide_controllers[1]);
|
|
if (ret != SOS_OK)
|
|
sos_bochs_printf ("Error while probing IDE controller 1\n");
|
|
|
|
ide_register_devices ();
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
sos_ret_t ide_driver_cleanup (void)
|
|
{
|
|
return ide_unregister_devices ();
|
|
}
|
|
|