ata.c revision a734c64bff58bda2fa48c2795453e092167b0ff7
/*
* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/interface.h>
#include <ipxe/blockdev.h>
/** @file
*
* ATA block device
*
*/
/******************************************************************************
*
* Interface methods
*
******************************************************************************
*/
/**
* Issue ATA command
*
* @v control ATA control interface
* @v data ATA data interface
* @v command ATA command
* @ret tag Command tag, or negative error
*/
ata_command_TYPE ( void * ) *op =
int tag;
if ( op ) {
} else {
/* Default is to fail to issue the command */
tag = -EOPNOTSUPP;
}
return tag;
}
/******************************************************************************
*
* ATA devices and commands
*
******************************************************************************
*/
/** List of all ATA commands */
static LIST_HEAD ( ata_commands );
/** An ATA device */
struct ata_device {
/** Reference count */
/** Block control interface */
/** ATA control interface */
/** Device number
*
* Must be ATA_DEV_MASTER or ATA_DEV_SLAVE.
*/
unsigned int device;
/** Maximum number of blocks per single transfer */
unsigned int max_count;
/** Device uses LBA48 extended addressing */
int lba48;
};
/** An ATA command */
struct ata_command {
/** Reference count */
/** ATA device */
struct ata_device *atadev;
/** List of ATA commands */
/** Block data interface */
/** ATA data interface */
/** Command type */
struct ata_command_type *type;
/** Command tag */
/** Private data */
};
/** An ATA command type */
struct ata_command_type {
/** Name */
const char *name;
/** Additional working space */
/** Command for non-LBA48-capable devices */
/** Command for LBA48-capable devices */
/**
* Calculate data-in buffer
*
* @v atacmd ATA command
* @v buffer Available buffer
* @v len Available buffer length
* @ret data_in Data-in buffer
* @ret data_in_len Data-in buffer length
*/
size_t *data_in_len );
/**
* Calculate data-out buffer
*
*
* @v atacmd ATA command
* @v buffer Available buffer
* @v len Available buffer length
* @ret data_out Data-out buffer
* @ret data_out_len Data-out buffer length
*/
size_t *data_out_len );
/**
* Handle ATA command completion
*
* @v atacmd ATA command
* @v rc Reason for completion
*/
};
/**
* Get reference to ATA device
*
* @v atadev ATA device
* @ret atadev ATA device
*/
return atadev;
}
/**
* Drop reference to ATA device
*
* @v atadev ATA device
*/
static inline __attribute__ (( always_inline )) void
}
/**
* Get reference to ATA command
*
* @v atacmd ATA command
* @ret atacmd ATA command
*/
return atacmd;
}
/**
* Drop reference to ATA command
*
* @v atacmd ATA command
*/
static inline __attribute__ (( always_inline )) void
}
/**
* Get ATA command private data
*
* @v atacmd ATA command
* @ret priv Private data
*/
static inline __attribute__ (( always_inline )) void *
}
/**
* Free ATA command
*
* @v refcnt Reference count
*/
struct ata_command *atacmd =
/* Remove from list of commands */
/* Free command */
}
/**
* Close ATA command
*
* @v atacmd ATA command
* @v rc Reason for close
*/
if ( rc != 0 ) {
}
/* Shut down interfaces */
}
/**
* Handle ATA command completion
*
* @v atacmd ATA command
* @v rc Reason for close
*/
/* Hand over to the command completion handler */
}
/**
* Use provided data buffer for ATA command
*
* @v atacmd ATA command
* @v buffer Available buffer
* @v len Available buffer length
* @ret data Data buffer
* @ret data_len Data buffer length
*/
}
/**
* Use no data buffer for ATA command
*
* @v atacmd ATA command
* @v buffer Available buffer
* @v len Available buffer length
* @ret data Data buffer
* @ret data_len Data buffer length
*/
/* Nothing to do */
}
/**
* Use private data buffer for ATA command
*
* @v atacmd ATA command
* @v buffer Available buffer
* @v len Available buffer length
* @ret data Data buffer
* @ret data_len Data buffer length
*/
}
/** ATA READ command type */
static struct ata_command_type atacmd_read = {
.name = "READ",
.cmd_lba = ATA_CMD_READ,
.done = atacmd_close,
};
/** ATA WRITE command type */
static struct ata_command_type atacmd_write = {
.name = "WRITE",
.cmd_lba = ATA_CMD_WRITE,
.done = atacmd_close,
};
/** ATA IDENTIFY private data */
struct ata_identify_private {
/** Identity data */
struct ata_identity identity;
};
/**
* Return ATA model string (for debugging)
*
* @v identify ATA identity data
* @ret model Model string
*/
static union {
} buf;
unsigned int i;
}
/**
* Handle ATA IDENTIFY command completion
*
* @v atacmd ATA command
* @v rc Reason for completion
*/
struct block_device_capacity capacity;
/* Close if command failed */
if ( rc != 0 ) {
return;
}
/* Extract capacity */
} else {
}
/* Return capacity to caller */
/* Close command */
atacmd_close ( atacmd, 0 );
}
/** ATA IDENTITY command type */
static struct ata_command_type atacmd_identify = {
.name = "IDENTIFY",
.priv_len = sizeof ( struct ata_identify_private ),
};
/** ATA command block interface operations */
static struct interface_operation atacmd_block_op[] = {
};
/** ATA command block interface descriptor */
static struct interface_descriptor atacmd_block_desc =
atacmd_block_op, ata );
/** ATA command ATA interface operations */
static struct interface_operation atacmd_ata_op[] = {
};
/** ATA command ATA interface descriptor */
static struct interface_descriptor atacmd_ata_desc =
atacmd_ata_op, block );
/**
* Create ATA command
*
* @v atadev ATA device
* @v block Block data interface
* @v type ATA command type
* @v lba Starting logical block address
* @v count Number of blocks to transfer
* @v buffer Data buffer
* @v len Length of data buffer
* @ret rc Return status code
*/
struct ata_command_type *type,
struct ata_command *atacmd;
int tag;
int rc;
/* Allocate and initialise structure */
if ( ! atacmd ) {
goto err_zalloc;
}
/* Sanity check */
goto err_len;
}
/* Construct command */
/* Issue command */
&command ) ) < 0 ) {
goto err_command;
}
/* Attach to parent interface, mortalise self, and return */
return 0;
return rc;
}
/**
* Issue ATA block read
*
* @v atadev ATA device
* @v block Block data interface
* @v lba Starting logical block address
* @v count Number of blocks to transfer
* @v buffer Data buffer
* @v len Length of data buffer
* @ret rc Return status code
*/
}
/**
* Issue ATA block write
*
* @v atadev ATA device
* @v block Block data interface
* @v lba Starting logical block address
* @v count Number of blocks to transfer
* @v buffer Data buffer
* @v len Length of data buffer
* @ret rc Return status code
*/
}
/**
* Read ATA device capacity
*
* @v atadev ATA device
* @v block Block data interface
* @ret rc Return status code
*/
struct ata_identity *identity;
}
/**
* Close ATA device
*
* @v atadev ATA device
* @v rc Reason for close
*/
struct ata_command *atacmd;
struct ata_command *tmp;
/* Shut down interfaces */
/* Shut down any remaining commands */
continue;
atacmd_get ( atacmd );
atacmd_put ( atacmd );
}
}
/**
* Describe ATA device using EDD
*
* @v atadev ATA device
* @v type EDD interface type
* @v path EDD device path
* @ret rc Return status code
*/
struct edd_interface_type *type,
union edd_device_path *path ) {
return 0;
}
/** ATA device block interface operations */
static struct interface_operation atadev_block_op[] = {
};
/** ATA device block interface descriptor */
static struct interface_descriptor atadev_block_desc =
atadev_block_op, ata );
/** ATA device ATA interface operations */
static struct interface_operation atadev_ata_op[] = {
};
/** ATA device ATA interface descriptor */
static struct interface_descriptor atadev_ata_desc =
atadev_ata_op, block );
/**
* Open ATA device
*
* @v block Block control interface
* @v ata ATA control interface
* @v device ATA device number
* @v max_count Maximum number of blocks per single transfer
* @ret rc Return status code
*/
struct ata_device *atadev;
/* Allocate and initialise structure */
if ( ! atadev )
return -ENOMEM;
/* Attach to ATA and parent and interfaces, mortalise self,
* and return
*/
return 0;
}