[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
The need for an IDE driver
From: |
Matthieu Lemerre |
Subject: |
The need for an IDE driver |
Date: |
Tue, 31 May 2005 22:11:17 +0200 |
User-agent: |
Gnus/5.11 (Gnus v5.11) Emacs/22.0.50 (gnu/linux) |
Hi,
There is a need to access a hard drive disk from the Hurd, so a need
for an IDE driver.
The best would be to have it available in the device driver framework;
however the device driver framework is not ready yet.
I invistigated the different possibilities we have:
* The first solution would be to have a Linux kernel running in
parallel of the Hurd. Once we get that, we would just have to
implement threads waiting for RPCs (from deva) and processing the
request.
** This could be done by having L4Linux running as a normal task,
launched by wortel. To do that, it seems that there is just a set of
interfaces (l4lxlib) to implement. This require a substancial amount
of work and maintainance. Moreover, it seems that it would be more
difficult to do this for l4linux2.6; and the l4linux project is not
supported anymore by the L4 team.
** The most promising approach would be to "reuse unmodified device
drivers by running them within their original operating system in a
virtual machine" from the L4ka project
(http://www.l4ka.org/projects/virtualization/drivers.php), maybe in
conjonction with their previrtualization project. But source code
for this project does not seem to be available;
* Other possibilities would be to use the different device driver
libraries for L4 that exist. There is at least two:
** L4-env and their DDE
(http://os.inf.tu-dresden.de/l4env/download.xml)
** Kenge (http://www.disy.cse.unsw.edu.au/Software/Kenge/)
I don't know how difficult it would be to link them to deva; I can
investigate more on this.
For the moment, I hacked a tiny IDE driver from the KOS project
(http://kos.enix.org). It works: I can read blocs of my emulated hard
drive with qemu. However, as its original authors (Thomas Petazonni
and David Decotigny) says, there is no manegement for DMA, IRQ,
multi-bloc requests, and the detection of the IDE controller and disks
do not always work.
I don't have real hardware to do the test; maybe there would be timers
to adapt. There should be a better timer function; it is rather
imprecise.
The original authors are willing to give their copyright to the FSF if
necessary.
I put the source code files as an attachment. Communication with this
driver from deva can be done by including "ide.h".
Thanks,
Matthieu
/* ide.c - Interface between KOS's ide driver and deva.
Copyright (C) 2005 Free Software Foundation, Inc.
Adapted for the Hurd by Matthieu Lemerre.
Derived from _ide_karm.c from the KOS project, which is
Copyright (C) 2000-2005, Thomas Petazzoni <address@hidden>
Copyright (C) 2000-2005, David Decotigny <address@hidden>
This file is part of the GNU Hurd.
The GNU Hurd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU Hurd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include "env.h"
#include "_ide.h"
#define _GNU_SOURCE 1
#include <errno.h>
#include <assert.h>
extern ide_device_t *ide_devices[IDE_MAX_CTRL][IDE_MAX_DEV_PER_CTRL];
error_t ide_block_read(int controller_nb, int device_nb,
char *buffer, count_t block_start,
count_t *inout_block_count)
{
ide_device_t *device;
count_t block_i;
result_t result;
device = (ide_device_t *) ide_devices[controller_nb][device_nb];
assert (device != NULL);
for (block_i = block_start ;
(block_i < (block_start + *inout_block_count))
&& (block_i < device->block_count) ;
block_i++, buffer += device->blk_size)
{
result = _ide_io_polled_operation(device, IDE_READ, block_i,
(k_ui16_t *) buffer);
if(result < 0)
{
return result;
}
}
*inout_block_count = block_i - block_start;
return ESUCCESS;
}
error_t ide_block_write(int controller_nb, int device_nb,
const char *buffer, count_t block_start,
count_t *inout_block_count)
{
ide_device_t *device;
count_t block_i;
result_t result;
device = (ide_device_t *) ide_devices[controller_nb][device_nb];
assert (device != NULL);
for (block_i = block_start ;
(block_i < (block_start + *inout_block_count))
&& (block_i < device->block_count) ;
block_i++, buffer += device->blk_size)
{
result = _ide_io_polled_operation(device, IDE_WRITE, block_i,
(k_ui16_t *) buffer);
if(result < 0)
{
return result;
}
}
*inout_block_count = block_i - block_start;
return ESUCCESS;
}
error_t ide_get_device_size(int controller_nb, int device_nb,
size_t *out_block_size,
count_t *out_block_count)
{
ide_device_t *device;
device = (ide_device_t *) ide_devices[controller_nb][device_nb];
assert (device != NULL);
*out_block_size = device->blk_size;
*out_block_count = device->block_count;
return ESUCCESS;
}
error_t ide_init (void)
{
_init_ide ();
}
/* ide.h - Access to ide devices.
Copyright (C) 2005 Free Software Foundation, Inc.
Written by Matthieu Lemerre.
This file is part of the GNU Hurd.
The GNU Hurd 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, or (at
your option) any later version.
The GNU Hurd 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, USA. */
#ifndef __IDE_H__
#define __IDE_H__
#include <errno.h>
/* Initialize the IDE subsystem, detecting all the controllers and the
devices. */
error_t ide_init (void);
/* Read *INOUT_BLOCK_COUNT blocks from device DEVICE_NB on controller
CONTROLLER_NB to BUFFER, starting at block BLOCK_START. After the
call, *INOUT_BLOCK_COUNT contains the number of blocks really
read. */
error_t ide_block_read(int controller_nb, int device_nb,
const char *buffer, unsigned int block_start,
unsigned int *inout_block_count);
/* Write *INOUT_BLOCK_COUNT blocks from BUFFER to device DEVICE_NB on
controller CONTROLLER_NB, starting at block BLOCK_START. After the
call, *INOUT_BLOCK_COUNT contains the number of blocks really
written. */
error_t ide_block_write(int controller_nb, int device_nb,
const char *buffer, unsigned int block_start,
unsigned int *inout_block_count);
/* Return the block size (in *OUT_BLOCK_SIZE) and number of blocks (in
OUT_BLOCK_COUNT) of the device DEVICE_NB on controller
CONTROLLER_NB. */
error_t ide_get_device_size(int controller_nb, int device_nb,
size_t *out_block_size,
unsigned int *out_block_count);
#endif /* __IDE_H__ */
/* env.c - Functions needed by KOS' IDE driver.
Copyright (C) 2005 Free Software Foundation, Inc.
Written by Matthieu Lemerre.
arch_loop_delay and udelay functions are from the KOS project;
Copyright (C) 2000-2005, Thomas Petazzoni <address@hidden>
Copyright (C) 2000-2005, David Decotigny <address@hidden>
This file is part of the GNU Hurd.
The GNU Hurd 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, or (at
your option) any later version.
The GNU Hurd 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, USA. */
#include "env.h"
#include "_ide.h"
#include <l4/ipc.h>
static unsigned long loops_per_sec = 1 << 12;
void
arch_loop_delay(unsigned loop)
{
int d0;
__asm__ __volatile__("jmp 1f\n"
".align 16\n"
"1: jmp 2f\n"
".align 16\n"
"2: decl %0\n"
"jns 2b\n"
:"=&a"(d0)
:"0"(loop));
}
void
udelay(unsigned long usec)
{
usec = (usec * loops_per_sec) / 1000000;
arch_loop_delay (usec);
}
void
usleep (unsigned long usec)
{
l4_sleep (l4_time_period (usec));
}
ide_device_t *ide_devices[IDE_MAX_CTRL][IDE_MAX_DEV_PER_CTRL] = {{NULL, NULL},
{NULL, NULL}};
result_t _ide_register_disk(ide_device_t *device, int controller_number,
int device_number)
{
debug ("[_ide_register_disk] called for controller %d device %d \n",
controller_number, device_number);
ide_devices[controller_number][device_number] = device;
return ESUCCESS;
}
/* env.h - Definitions needed by KOS' IDE driver.
Copyright (C) 2005 Free Software Foundation, Inc.
Written by Matthieu Lemerre.
This file is part of the GNU Hurd.
The GNU Hurd 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, or (at
your option) any later version.
The GNU Hurd 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, USA. */
#ifndef __ENV_H__
#define __ENV_H__
#include "../output.h"
#define _GNU_SOURCE 1
#include <stdlib.h>
#include <errno.h>
#include <sys/io.h>
typedef unsigned int count_t;
typedef unsigned long long k_ui64_t;
typedef long long k_si64_t;
typedef unsigned long k_ui32_t;
typedef long k_si32_t;
typedef unsigned short k_ui16_t;
typedef short k_si16_t;
typedef unsigned char k_ui8_t;
typedef char k_si8_t;
typedef error_t result_t;
#define ESUCCESS 0
/* Actively waits USECS microseconds. */
void
udelay (unsigned long usecs);
/* Really sleep for DELAY microseconds. */
void
usleep(unsigned long usec);
#define DEBUG_PRINT(format) \
debug (format)
void
kmalloc (size_t size);
#define CONCEPTION_ASSERT(assertion) assert (assertion)
#define __dbg_printk debug
#define FAILED_VERBOSE(format) \
panic (format)
#endif /* __ENV_H__ */
/* _ide.h - IDE internal declarations.
Copyright (C) 2000-2005, Thomas Petazzoni <address@hidden>
Copyright (C) 2005 Free Software Foundation, Inc.
Modified by Matthieu Lemerre.
arch_loop_delay and udelay functions are from the KOS project;
Copyright (C) 2000-2005, David Decotigny <address@hidden>
This file is part of the GNU Hurd.
The GNU Hurd 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, or (at
your option) any later version.
The GNU Hurd 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, USA. */
#ifndef ___IDE_H__
#define ___IDE_H__
/* Registers */
#define ATA_DATA 0x00 /* (R/W) Data register */
#define ATA_ERROR 0x01 /* (R) error register */
#define ATA_PRECOMP 0x01 /* (W) precompensation */
#define ATA_SECTOR_COUNT 0x02 /* (W) sector count register */
#define ATA_SECTOR_NUMBER 0x03 /* (W) sector number register */
#define ATA_CYL_LSB 0x04 /* (W) cylinder# LSB */
#define ATA_CYL_MSB 0x05 /* (W) cylinder# MSB */
#define ATA_DRIVE 0x06 /* (W) drive/head/lba */
#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 */
#define ATA_STATUS 0x07 /* (R) status register */
#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 */
#define ATA_CMD 0x07 /* (W) command register */
#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 */
#define ATA_ALTPORT 0x206 /* (R) alternate
Status register */
#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) */
typedef struct ide_device_info
{
k_ui16_t general_config_info; /* 0 */
k_ui16_t nb_logical_cylinders; /* 1 */
k_ui16_t reserved1; /* 2 */
k_ui16_t nb_logical_heads; /* 3 */
k_ui16_t unformatted_bytes_track; /* 4 */
k_ui16_t unformatted_bytes_sector; /* 5 */
k_ui16_t nb_logical_sectors; /* 6 */
k_ui16_t vendor1[3]; /* 7-9 */
k_ui8_t serial_number[20]; /* 10-19 */
k_ui16_t buffer_type; /* 20 */
k_ui16_t buffer_size; /* 21 */
k_ui16_t ecc_bytes; /* 22 */
k_ui8_t firmware_revision[8]; /* 23-26 */
k_ui8_t model_number[40]; /* 27-46 */
k_ui8_t max_multisect; /* 47 */
k_ui8_t vendor2;
k_ui16_t dword_io; /* 48 */
k_ui8_t vendor3; /* 49 */
k_ui8_t capabilities;
k_ui16_t reserved2; /* 50 */
k_ui8_t vendor4; /* 51 */
k_ui8_t pio_trans_mode;
k_ui8_t vendor5; /* 52 */
k_ui8_t dma_trans_mode;
k_ui16_t fields_valid; /* 53 */
k_ui16_t cur_logical_cylinders; /* 54 */
k_ui16_t cur_logical_heads; /* 55 */
k_ui16_t cur_logical_sectors; /* 56 */
k_ui16_t capacity1; /* 57 */
k_ui16_t capacity0; /* 58 */
k_ui8_t multsect; /* 59 */
k_ui8_t multsect_valid;
k_ui32_t lba_capacity; /* 60-61 */
k_ui16_t dma_1word; /* 62 */
k_ui16_t dma_multiword; /* 63 */
k_ui16_t pio_modes; /* 64 */
k_ui16_t min_mword_dma; /* 65 */
k_ui16_t recommended_mword_dma; /* 66 */
k_ui16_t min_pio_cycle_time; /* 67 */
k_ui16_t min_pio_cycle_time_iordy; /* 68 */
k_ui16_t reserved3[11]; /* 69-79 */
k_ui16_t major_version; /* 80 */
k_ui16_t minor_version; /* 81 */
k_ui16_t command_sets1; /* 82 */
k_ui16_t command_sets2; /* 83 dixit lk : b14 (smart enabled) */
k_ui16_t reserved4[4]; /* 84-87 */
k_ui16_t dma_ultra; /* 88 dixit lk */
k_ui16_t reserved5[37]; /* 89-125 */
k_ui16_t last_lun; /* 126 */
k_ui16_t reserved6; /* 127 */
k_ui16_t security; /* 128 */
k_ui16_t reserved7[127];
} __attribute__((packed)) ide_device_info_t ;
#define IDE_MAX_CTRL 2
#define IDE_MAX_DEV_PER_CTRL 2
#define IDE_PRIMARY_CONTROLLER 0
#define IDE_SECONDARY_CONTROLLER 1
#define IDE_MASTER 0
#define IDE_SLAVE 1
#define IDE_READ 0
#define IDE_WRITE 1
typedef enum { IDE_DEV_NOT_PRESENT=0x42,
IDE_DEV_ATA, IDE_DEV_ATAPI } ide_device_type_t;
struct ide_controller;
typedef struct ide_device
{
uint id;
dev_t devfs_id;
ide_device_type_t type;
k_ui16_t cylinder_nb;
k_ui16_t head_nb;
k_ui16_t sector_per_track;
count_t block_count;
size_t blk_size;
char model_name[41];
struct ide_controller *ctrl;
#define HARDDISK_LBA_CAPABLE 0x1
int flags;
} ide_device_t;
typedef struct ide_controller
{
uint id;
k_ui16_t ioaddr;
k_ui16_t irq;
enum { IDE_CTRL_NOT_PRESENT, IDE_CTRL_READY } state;
struct ide_device devices[IDE_MAX_DEV_PER_CTRL];
} ide_controller_t;
/* _ide.c */
/**
* Initialize IDE controllers and devices
*
* @return ESUCCESS or error code
*/
result_t _init_ide(void);
/**
* Perform an I/O operation in polled mode.
*
* @param dev The device
*
* @param op_type Type of operation to be performed (IDE_READ or IDE_WRITE)
*
* @param block_start The sector number at which the operation starts
*
* @param buffer The buffer to be used for the operation (as input or output)
*
* @todo Use IRQ
* @todo Implement multiple sector operations
*
* @result ESUCCESS or error code
*/
result_t _ide_io_polled_operation(ide_device_t *dev, int op_type,
count_t block_start, k_ui16_t *buffer);
#ifdef KOS
/* _ide_karm.c */
result_t _ide_register_disk(char *name, ide_device_t *device);
#else
result_t _ide_register_disk(ide_device_t *device, int controller_number,
int device_number);
#endif /* KOS */
#endif /* ___IDE_H__ */
/*
* Copyright (C) 2000, Thomas Petazzoni <address@hidden>
* http://kos.enix.org
*
* ATA driver
* with the help of Soren Schmidt, <address@hidden>
*
* Copyright (C) 2005 Free Software Foundation, Inc.
* Modified by Matthieu Lemerre <address@hidden>
*
* @(#) $Id: _ide.c,v 1.17 2003/08/20 22:12:21 d2 Exp $
*/
#if KOS
#include <kos/asm.h>
#include <scheduler/scheduler.h>
#include <idt/irq.h>
#include <ide/_ide.h>
#include <lib/list/liblist.h>
#include <kos/spinlock.h>
#include <lib/std/string.h>
#include <kmem/kmem.h>
#include <debug/debug.h>
#endif
#include "env.h"
#include "_ide.h"
/**
* Description of all IDE controllers and their devices.
*/
static ide_controller_t controllers[IDE_MAX_CTRL] =
{
{ .id = IDE_PRIMARY_CONTROLLER,
.ioaddr = 0x1F0,
.irq = 14,
.state = IDE_CTRL_NOT_PRESENT,
.devices = {
[IDE_MASTER] = {
.id = IDE_MASTER,
.type = IDE_DEV_NOT_PRESENT,
.ctrl = & controllers[IDE_PRIMARY_CONTROLLER]
},
[IDE_SLAVE] = {
.id = IDE_SLAVE,
.type = IDE_DEV_NOT_PRESENT,
.ctrl = & controllers[IDE_PRIMARY_CONTROLLER]
}
}
},
{ .id = IDE_SECONDARY_CONTROLLER,
.ioaddr = 0x170,
.irq = 15,
.state = IDE_CTRL_NOT_PRESENT,
.devices = {
[IDE_MASTER] = {
.id = IDE_MASTER,
.type = IDE_DEV_NOT_PRESENT,
.ctrl = & controllers[IDE_SECONDARY_CONTROLLER]
},
[IDE_SLAVE] = {
.id = IDE_SLAVE,
.type = IDE_DEV_NOT_PRESENT,
.ctrl = & controllers[IDE_SECONDARY_CONTROLLER]
}
}
}
};
/**
` * Initialize a controller :
* -# Make sure controller is present
* -# See which devices are present on the controller
*
* @param ctrl The controller to initialize
*
* @result 0 on success, error code on failure
*/
static result_t _ide_init_controller(ide_controller_t *ctrl)
{
int status0, status1;
int mask = 0;
int timeout;
/* Get status of controllers */
outb(ATA_D_IBM | ATA_D_MASTER, ctrl->ioaddr + ATA_DRIVE);
udelay(1);
status0 = inb(ctrl->ioaddr + ATA_STATUS);
outb(ATA_D_IBM | ATA_D_SLAVE, ctrl->ioaddr + ATA_DRIVE);
udelay(1);
status1 = inb(ctrl->ioaddr + ATA_STATUS);
if((status0 & 0xf8) != 0xf8)
mask |= 0x01;
if((status1 & 0xf8) != 0xf8)
mask |= 0x02;
/* If both BSY bit are set => leave */
if((status0 & ATA_S_BSY) && (status1 & ATA_S_BSY))
return ESUCCESS;
/* no device present */
if(!mask)
return ESUCCESS;
/* select the master */
outb(ATA_D_IBM | ATA_D_MASTER, ctrl->ioaddr + ATA_DRIVE);
usleep(1);
/* send reset */
outb(ATA_A_nIEN | ATA_A_RESET, ctrl->ioaddr + ATA_DEVICE_CONTROL);
udelay(1);
outb(ATA_A_nIEN, ctrl->ioaddr + ATA_DEVICE_CONTROL);
udelay(1);
inb(ctrl->ioaddr + ATA_ERROR);
outb(ATA_A_4BIT, ctrl->ioaddr + ATA_DEVICE_CONTROL);
udelay(1);
/* wait busy */
for(timeout = 0; timeout < 300000; timeout++)
{
/* select master and get status register */
outb(ATA_D_IBM | ATA_D_MASTER, ctrl->ioaddr + ATA_DRIVE);
udelay(1);
status0 = inb(ctrl->ioaddr + ATA_STATUS);
/* select slave and get status register */
outb(ATA_D_IBM | ATA_D_SLAVE, ctrl->ioaddr + ATA_DRIVE);
udelay(1);
status1 = inb(ctrl->ioaddr + ATA_STATUS);
if(mask == 0x01)
if(!(status0 & ATA_S_BSY))
break;
if(mask == 0x02)
if(!(status1 & ATA_S_BSY))
break;
if(mask == 0x03)
if(!(status0 & ATA_S_BSY) && !(status1 & ATA_S_BSY))
break;
udelay(1);
}
if(status0 & ATA_S_BSY)
mask &= ~0x01;
if(status1 & ATA_S_BSY)
mask &= ~0x02;
/* no device */
if(!mask)
return ESUCCESS;
/* there's at least one device, initialize state */
ctrl->state = IDE_CTRL_READY;
/* Detect devices on the master */
outb(ATA_D_IBM | ATA_D_MASTER, ctrl->ioaddr + ATA_DRIVE);
udelay(1);
/* ATAPI cdrom ? */
if(inb(ctrl->ioaddr + ATA_CYL_LSB) == ATAPI_MAGIC_LSB &&
inb(ctrl->ioaddr + ATA_CYL_MSB) == ATAPI_MAGIC_MSB)
{
ctrl->devices[IDE_MASTER].type = IDE_DEV_ATAPI;
}
/* Detect devices on the slave */
outb(ATA_D_IBM | ATA_D_SLAVE, ctrl->ioaddr + ATA_DRIVE);
udelay(1);
/* ATAPI cdrom ? */
if(inb(ctrl->ioaddr + ATA_CYL_LSB) == ATAPI_MAGIC_LSB &&
inb(ctrl->ioaddr + ATA_CYL_MSB) == ATAPI_MAGIC_MSB)
{
ctrl->devices[IDE_SLAVE].type = IDE_DEV_ATAPI;
}
/* Wasn't an ATAPI device on master, but there is a device -> hard
disk */
if((status0 != 0x00) &&
(ctrl->devices[IDE_MASTER].type == IDE_DEV_NOT_PRESENT))
{
outb(ATA_D_IBM | ATA_D_MASTER, ctrl->ioaddr + ATA_DRIVE);
usleep(1);
outb(0x58, ctrl->ioaddr + ATA_ERROR);
outb(0xa5, ctrl->ioaddr + ATA_CYL_LSB);
if(inb(ctrl->ioaddr + ATA_ERROR) != 0x58 &&
inb(ctrl->ioaddr + ATA_CYL_LSB) == 0xa5)
{
ctrl->devices[IDE_MASTER].type = IDE_DEV_ATA;
}
}
/* Wasn't an ATAPI device on slave, but there is a device -> hard
disk */
if((status1 != 0x00) &&
(ctrl->devices[IDE_SLAVE].type == IDE_DEV_NOT_PRESENT))
{
outb(ATA_D_IBM | ATA_D_SLAVE, ctrl->ioaddr + ATA_DRIVE);
usleep(1);
outb(0x58, ctrl->ioaddr + ATA_ERROR);
outb(0xa5, ctrl->ioaddr + ATA_CYL_LSB);
if(inb(ctrl->ioaddr + ATA_ERROR) != 0x58 &&
inb(ctrl->ioaddr + ATA_CYL_LSB) == 0xa5)
{
ctrl->devices[IDE_MASTER].type = IDE_DEV_ATA;
}
}
return ESUCCESS;
}
/**
* Convert an IDE string (as returned by the controller) to a regular
* ASCII string.
*
* @param dst The address where to store the resulting ASCII string
*
* @param src The source IDE string
*
* @param len The length of the conversion (destination string must be
* long enough)
*/
static void _ide_convert_to_ascii(char *dst, char *src, int len)
{
int i;
for(i = 0; i < len; i=i+2)
{
dst[i] = (src[i+1] & 0xff);
dst[i+1] = (src[i] & 0xff);
}
dst[len] = '\0';
for(i = len; i > 0; i--)
{
if(dst[i] == 0)
continue;
else if(dst[i] == ' ')
dst[i] = '\0';
else
break;
}
}
/**
* Get device informations for ATA devices. This function sends the
* "Identify drive" command, and wait for the result in polled mode.
*
* @param dev The device for which we want to get informations
*
* @result ESUCCESS or error code
*/
static result_t _ide_get_device_info(ide_device_t *dev)
{
int timeout, status=0;
k_ui16_t buffer[256];
int ioaddr = dev->ctrl->ioaddr;
ide_device_info_t *dinfo;
int i;
/* Disable interrupts, set device, and send IDENTIFY command */
outb(ATA_A_nIEN | ATA_A_4BIT, ioaddr + ATA_DEVICE_CONTROL);
udelay(1);
outb(ATA_D_IBM | (dev->id == IDE_MASTER) ? ATA_D_MASTER : ATA_D_SLAVE,
ioaddr + ATA_DRIVE);
outb(ATA_C_ATA_IDENTIFY, ioaddr + ATA_CMD);
/* Wait for command completion */
for(timeout = 0; timeout < 30000; timeout++)
{
status = inb(ioaddr + ATA_STATUS);
if(!(status & ATA_S_BSY))
break;
udelay(1);
}
/* Check for DRQ to be set */
if(!(status & ATA_S_DRQ))
{
__dbg_printk("[get_drv_infos] DRQ wasn't set after IDENTIFY\n");
return -1;
}
/* Read data */
for(i = 0; i < 256; i++)
buffer[i] = inw(ioaddr);
dinfo = (ide_device_info_t *) buffer;
dev->cylinder_nb = dinfo->nb_logical_cylinders;
dev->head_nb = dinfo->nb_logical_heads;
dev->sector_per_track = dinfo->nb_logical_sectors;
dev->blk_size = 512;
if(dev->sector_per_track == 0 || dev->head_nb == 0 ||
dev->cylinder_nb == 0)
{
return -ENOENT;
}
dev->flags = 0;
/* Does this device really support LBA */
if(dinfo->capabilities & (1<<1) /* LBA bit must be set */
&& dinfo->major_version /* major version must be non 0 */
&& (dinfo->fields_valid & 1) /* lba fields must be valid */
&& (dinfo->lba_capacity)) /* lba capacity must be non 0 */
dev->flags |= HARDDISK_LBA_CAPABLE;
/* Find real capacity */
if(dev->head_nb == 16 &&
dev->sector_per_track == 63 &&
dev->cylinder_nb == 16383)
{
if(dev->flags & HARDDISK_LBA_CAPABLE)
dev->block_count = dinfo->lba_capacity;
else
FAILED_VERBOSE("Large hd without LBA ...\n");
}
else
dev->block_count =
dev->cylinder_nb *
dev->sector_per_track *
dev->head_nb;
/* Convert different strings */
_ide_convert_to_ascii(dev->model_name, dinfo->model_number, 40);
return ESUCCESS;
}
/**
* Register IDE devices
* -# Get device informations
* -# Register them to the devfs
*
* @param ctrl The controller in which the devices to be registered
* are located
*
* @result ESUCCESS on success, an error code on failure
*/
static result_t _ide_register_devices(ide_controller_t *ctrl)
{
uint i;
if(ctrl->state == IDE_CTRL_NOT_PRESENT)
return ESUCCESS;
for (i = 0; i < IDE_MAX_DEV_PER_CTRL; i++)
{
#if KOS
char name[16];
#endif
ide_device_t *dev;
result_t result;
dev = & ctrl->devices[i];
if (dev->type == IDE_DEV_NOT_PRESENT)
{
__dbg_printk("[ide] Device %d on ide%d not present\n", i, ctrl->id);
continue;
}
else if (dev->type == IDE_DEV_ATA)
{
#if KOS
snprintf(name, sizeof(name), "ide%dhd%d", ctrl->id, i);
#endif
result = _ide_get_device_info(dev);
if (result < 0)
{
dev->type = IDE_DEV_NOT_PRESENT;
continue;
}
__dbg_printk("[ide] Detected Disk device, C/H/S=%d/%d/%d,
size=%dMB\n",
dev->cylinder_nb, dev->head_nb,
dev->sector_per_track, dev->block_count >> 11);
#if KOS
result = _ide_register_disk(name, dev);
#else
result = _ide_register_disk(dev, ctrl->id, i);
#endif
if (result < 0)
{
continue;
}
}
else if (ctrl->devices[i].type == IDE_DEV_ATAPI)
{
#if KOS
snprintf(name, sizeof(name), "ide%dcd%d", ctrl->id, i);
#endif
__dbg_printk("[ide] Detected CDROM device\n");
}
}
return ESUCCESS;
}
result_t _init_ide(void)
{
uint i;
for (i = 0; i < IDE_MAX_CTRL; i++)
{
ide_controller_t *ctrl;
result_t result;
ctrl = & controllers[i];
result = _ide_init_controller(ctrl);
if(result < 0)
{
return result;
}
result = _ide_register_devices(ctrl);
if(result < 0)
{
return result;
}
}
return ESUCCESS;
}
result_t _ide_io_polled_operation(ide_device_t *dev, int op_type,
count_t block_start, k_ui16_t *buffer)
{
int timeout, status = 0;
int i;
k_ui8_t cyl_lo, cyl_hi, sect, head;
ide_controller_t *ctrl;
ctrl = dev->ctrl;
if(op_type != IDE_READ && op_type != IDE_WRITE)
{
DEBUG_PRINT("type invalid\n");
return -EINVAL;
}
if (ctrl->state == IDE_CTRL_NOT_PRESENT)
{
return -ENXIO;
}
if (ctrl->state != IDE_CTRL_READY)
{
return -EBUSY;
}
/* Does harddisk support LBA ? */
if(dev->flags & HARDDISK_LBA_CAPABLE)
{
sect = (block_start & 0xff);
cyl_lo = (block_start >> 8) & 0xff;
cyl_hi = (block_start >> 16) & 0xff;
head = ((block_start >> 24) & 0x7) | 0x40;
}
else
{
int cylinder = block_start /
(dev->head_nb * dev->sector_per_track);
int temp = block_start %
(dev->head_nb * dev->sector_per_track);
cyl_lo = cylinder & 0xff;
cyl_hi = (cylinder >> 8) & 0xff;
head = temp / dev->sector_per_track;
sect = (temp % dev->sector_per_track) + 1;
}
/* I don't know if we really need this, but without this, the Error
bit of the status register is set, even before sending the
command ! */
outb(ATA_D_IBM | ATA_D_MASTER, ctrl->ioaddr + ATA_DRIVE);
udelay(100);
outb(ATA_A_nIEN | ATA_A_RESET, ctrl->ioaddr + ATA_DEVICE_CONTROL);
udelay(100);
outb(ATA_A_nIEN, ctrl->ioaddr + ATA_DEVICE_CONTROL);
inb(ctrl->ioaddr + ATA_ERROR);
/* Waiting for controller to be ready (BSY=0) */
for(timeout = 0; timeout < 30000; timeout++)
{
status = inb(ctrl->ioaddr + ATA_STATUS);
if(!(status & ATA_S_BSY))
break;
udelay(100);
}
if(timeout == 30000)
{
DEBUG_PRINT("[ide] timeout 1\n");
return -EIO;
}
/* Check for ERR bit to be cleared */
if(inb(ctrl->ioaddr + ATA_STATUS) & ATA_S_ERROR)
{
DEBUG_PRINT("err bit set\n");
return -EIO;
}
/* Write to registers */
outb(ATA_A_nIEN | ATA_A_4BIT, ctrl->ioaddr + ATA_DEVICE_CONTROL);
outb(1, ctrl->ioaddr + 1);
outb(0, ctrl->ioaddr + ATA_PRECOMP);
outb(1, ctrl->ioaddr + ATA_SECTOR_COUNT);
outb(sect, ctrl->ioaddr + ATA_SECTOR_NUMBER);
outb(cyl_lo, ctrl->ioaddr + ATA_CYL_LSB);
outb(cyl_hi, ctrl->ioaddr + ATA_CYL_MSB);
outb(ATA_D_IBM
| ((dev->id == IDE_MASTER) ? ATA_D_MASTER : ATA_D_SLAVE)
| head,
ctrl->ioaddr + ATA_DRIVE);
/* ATA-4 says to wait 400nsec here, but it doesn't change anything ! */
udelay(100);
/* For the moment, we don't care about the operation type => always
read */
if(op_type == IDE_READ)
{
outb(ATA_C_READ, ctrl->ioaddr + ATA_CMD);
}
else if(op_type == IDE_WRITE)
{
outb(ATA_C_WRITE, ctrl->ioaddr + ATA_CMD);
}
/* Wait for controller to be ready (BSY=0) */
for(timeout = 0; timeout < 30000; timeout++)
{
status = inb(ctrl->ioaddr + ATA_STATUS);
if(!(status & ATA_S_BSY))
break;
udelay(100);
}
if(timeout >= 30000)
{
DEBUG_PRINT("timeout 2\n");
return -EIO;
}
/* Check for ERR to be cleared */
if(inb(ctrl->ioaddr + ATA_STATUS) & ATA_S_ERROR)
{
DEBUG_PRINT("err bit set\n");
return -EIO;
}
/* Check for DRQ to be set */
if(!(inb(ctrl->ioaddr + ATA_STATUS) & ATA_S_DRQ))
{
DEBUG_PRINT("drq not set\n");
return -EIO;
}
/* Read the data */
if(op_type == IDE_READ)
{
for(i = 0; i < 256; i++)
buffer[i] = inw(ctrl->ioaddr);
}
else if(op_type == IDE_WRITE)
{
for (i = 0; i < 256; i++)
outw(buffer[i], ctrl->ioaddr);
}
return ESUCCESS;
}
- The need for an IDE driver,
Matthieu Lemerre <=