From ef142d71a50c742ee9e4e9c53052a53d8bbfffcf Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Mon, 28 Oct 2002 14:00:43 +0000 Subject: 2002-10-28 Eugeny S. Mints * Added ATA support. * include/rtems/blkdev.h: Added last IO status. * include/rtems/ata.h, include/rtems/ata_internal.h, include/rtems/ide_part_table.h, src/ata.c, src/ide_part_table.c: New files. --- cpukit/libblock/ChangeLog | 8 + cpukit/libblock/include/rtems/ata.h | 55 + cpukit/libblock/include/rtems/ata_internal.h | 328 ++++++ cpukit/libblock/include/rtems/blkdev.h | 4 + cpukit/libblock/include/rtems/ide_part_table.h | 186 ++++ cpukit/libblock/src/ata.c | 1319 ++++++++++++++++++++++++ cpukit/libblock/src/ide_part_table.c | 518 ++++++++++ 7 files changed, 2418 insertions(+) create mode 100644 cpukit/libblock/include/rtems/ata.h create mode 100644 cpukit/libblock/include/rtems/ata_internal.h create mode 100644 cpukit/libblock/include/rtems/ide_part_table.h create mode 100644 cpukit/libblock/src/ata.c create mode 100644 cpukit/libblock/src/ide_part_table.c (limited to 'cpukit') diff --git a/cpukit/libblock/ChangeLog b/cpukit/libblock/ChangeLog index 0865f89195..dff0893498 100644 --- a/cpukit/libblock/ChangeLog +++ b/cpukit/libblock/ChangeLog @@ -1,3 +1,11 @@ +2002-10-28 Eugeny S. Mints + + * Added ATA support. + * include/rtems/blkdev.h: Added last IO status. + * include/rtems/ata.h, include/rtems/ata_internal.h, + include/rtems/ide_part_table.h, src/ata.c, src/ide_part_table.c: New + files. + 2002-10-25 Ralf Corsepius * configure.ac: Add nostdinc to AM_INIT_AUTOMAKE. diff --git a/cpukit/libblock/include/rtems/ata.h b/cpukit/libblock/include/rtems/ata.h new file mode 100644 index 0000000000..d77a96a7e3 --- /dev/null +++ b/cpukit/libblock/include/rtems/ata.h @@ -0,0 +1,55 @@ +/* + * ata.h + * + * ATA RTEMS driver header file. This file should be included from an + * application. + * + * Copyright (C) 2002 OKTET Ltd., St.-Petersburg, Russia + * Author: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * $Id$ + */ +#ifndef __ATA_H__ +#define __ATA_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +rtems_device_driver ata_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *args); + +#define ATA_DRIVER_TABLE_ENTRY \ + {ata_initialize, GENERIC_BLOCK_DEVICE_DRIVER_ENTRIES} + +/* ATA IOCTL request codes */ +#define ATAIO_SET_MULTIPLE_MODE _IO('A', 1) + +/* + * ATA driver configuration parameters + * FIXME: should be configured more easy... + */ +#define ATA_DRIVER_MESSAGE_QUEUE_SIZE 50 +#define ATA_DRIVER_TASK_PRIORITY 140 +#define ATA_DRIVER_TASK_STACK_SIZE 16*1024 + +#ifdef __cplusplus +} +#endif + + +#endif /* __ATA_H__ */ + + + diff --git a/cpukit/libblock/include/rtems/ata_internal.h b/cpukit/libblock/include/rtems/ata_internal.h new file mode 100644 index 0000000000..0b17703ede --- /dev/null +++ b/cpukit/libblock/include/rtems/ata_internal.h @@ -0,0 +1,328 @@ +/* + * ata_internal.h + * + * ATA RTEMS driver internal header file + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Authors: Eugeny S. Mints + * Alexandra Kossovsky + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * $Id$ + * + */ +#ifndef __ATA_INTERNAL_H__ +#define __ATA_INTERNAL_H__ + +#include +#include +#include +#include + +#include +#include + +/* + * Conversion from and to little-endian byte order. (no-op on i386/i486) + * + * Naming: Ca_b_c, where a: F = from, T = to, b: LE = little-endian, + * BE = big-endian, c: W = word (16 bits), L = longword (32 bits) + */ +#if (CPU_BIG_ENDIAN == TRUE) +# define CF_LE_W(v) CPU_swap_u16(v) +# define CF_LE_L(v) CPU_swap_u32(v) +# define CT_LE_W(v) CPU_swap_u16(v) +# define CT_LE_L(v) CPU_swap_u32(v) +#else +# define CF_LE_W(v) (v) +# define CF_LE_L(v) (v) +# define CT_LE_W(v) (v) +# define CT_LE_L(v) (v) +#endif + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define ATA_UNDEFINED_VALUE (-1) + +/* Sector size for all ATA devices */ +#define ATA_SECTOR_SIZE 512 + + +#define ATA_MAX_CMD_REG_OFFSET 8 + +/* ATA modes */ +#define ATA_MODES_PIO3 0x001 +#define ATA_MODES_PIO4 0x002 + +#define ATA_MODES_PIO 0x003 + +#define ATA_MODES_DMA0 0x004 +#define ATA_MODES_DMA1 0x008 +#define ATA_MODES_DMA2 0x010 + +#define ATA_MODES_UDMA0 0x020 +#define ATA_MODES_UDMA1 0x040 +#define ATA_MODES_UDMA2 0x080 +#define ATA_MODES_UDMA3 0x100 +#define ATA_MODES_UDMA4 0x200 +#define ATA_MODES_UDMA5 0x400 + +#define ATA_MODES_UDMA 0x7e0 +#define ATA_MODES_DMA 0x7fc + + +/* ATA Commands */ + +/* Types of ATA commands */ +#define ATA_COMMAND_TYPE_NON_DATA 0 +#define ATA_COMMAND_TYPE_PIO_IN 1 +#define ATA_COMMAND_TYPE_PIO_OUT 2 +#define ATA_COMMAND_TYPE_DMA 3 + +/* ATA commands opcodes */ +/* + * Commands present in both ATA-2 and ATA-4 specs. + * Some commands have two values in ATA-2, + * in such case value from ATA-4 used. + * Some commands have slightly different names in these specifications, + * so names from ATA-4 are used. + */ +#define ATA_COMMAND_NOP 0x00 +#define ATA_COMMAND_READ_SECTORS 0x20 +#define ATA_COMMAND_WRITE_SECTORS 0x30 +#define ATA_COMMAND_READ_VERIFY_SECTORS 0x40 +#define ATA_COMMAND_SEEK 0x70 /* or 0x7. */ +#define ATA_COMMAND_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define ATA_COMMAND_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define ATA_COMMAND_DOWNLOAD_MICROCODE 0x92 +#define ATA_COMMAND_READ_MULTIPLE 0xc4 +#define ATA_COMMAND_WRITE_MULTIPLE 0xc5 +#define ATA_COMMAND_SET_MULTIPLE_MODE 0xc6 +#define ATA_COMMAND_READ_DMA 0xc8 +#define ATA_COMMAND_WRITE_DMA 0xca +#define ATA_COMMAND_STANDBY_IMMEDIATE 0xe0 /* or 0x94 */ +#define ATA_COMMAND_IDLE_IMMEDIATE 0xe1 /* or 0x95 */ +#define ATA_COMMAND_STANDBY 0xe2 /* or 0x96 */ +#define ATA_COMMAND_IDLE 0xe3 /* or 0x97 */ +#define ATA_COMMAND_READ_BUFFER 0xe4 +#define ATA_COMMAND_CHECK_POWER_MODE 0xe5 /* or 0x98 in ATA-2 */ +#define ATA_COMMAND_SLEEP 0xe6 /* or 0x99 */ +#define ATA_COMMAND_WRITE_BUFFER 0xe8 +#define ATA_COMMAND_IDENTIFY_DEVICE 0xec +#define ATA_COMMAND_SET_FEATURES 0xef + +/* Commands present in both ATA-2 and ATA-4 specs: removable media */ +#define ATA_COMMAND_MEDIA_LOCK 0xde +#define ATA_COMMAND_MEDIA_UNLOCK 0xdf +#define ATA_COMMAND_MEDIA_EJECT 0xed + + +/* Commands present in ATA-2, but not in ATA-4 (not used) */ +#define ATA_COMMAND_RECALIBRATE 0x10 /* or 0x1. */ +#define ATA_COMMAND_READ_SECTOR_NON_RETRY 0x21 +#define ATA_COMMAND_READ_LONG_RETRY 0x22 +#define ATA_COMMAND_READ_LONG_NON_RETRY 0x23 +#define ATA_COMMAND_WRITE_SECTOR_NON_RETRY 0x31 +#define ATA_COMMAND_WRITE_LONG_RETRY 0x32 +#define ATA_COMMAND_WRITE_LONG_NON_RETRY 0x33 +#define ATA_COMMAND_WRITE_VERIFY 0x3c +#define ATA_COMMAND_READ_VERIFY_SECTOR_NON_RETRY 0x41 +#define ATA_COMMAND_FORMAT_TRACK 0x50 +#define ATA_COMMAND_READ_DMA_NON_RETRY 0xc9 +#define ATA_COMMAND_WRITE_DMA_NON_RETRY 0xcb +#define ATA_COMMAND_ACKNOWLEGE_MEDIA_CHANGE 0xdb +#define ATA_COMMAND_BOOT_POST_BOOT 0xdc +#define ATA_COMMAND_BOOT_PRE_BOOT 0xdd +#define ATA_COMMAND_WRITE_SAME 0xe9 + +/* Commands from ATA-4 specification: CFA feature set */ +#define ATA_COMMAND_CFA_REQUEST_EXTENDED_ERROR_CODE 0x03 +#define ATA_COMMAND_CFA_WRITE_SECTORS_WITHOUT_ERASE 0x38 +#define ATA_COMMAND_CFA_TRANSLATE_SECTOR 0x87 +#define ATA_COMMAND_CFA_ERASE_SECTORS 0xc0 +#define ATA_COMMAND_CFA_WRITE_MULTIPLE_WITHOUT_ERASE 0xcd + +/* Commands from ATA-4 specification: commands to use with PACKET command */ +#define ATA_COMMAND_DEVICE_RESET 0x08 +#define ATA_COMMAND_PACKET 0xa0 +#define ATA_COMMAND_IDENTIFY_PACKET_DEVICE 0xa1 +#define ATA_COMMAND_SERVICE 0xa2 + +/* Commands from ATA-4 specification: SECURITY commands */ +#define ATA_COMMAND_SECURITY_SET_PASSWORD 0xf1 +#define ATA_COMMAND_SECURITY_UNLOCK 0xf2 +#define ATA_COMMAND_SECURITY_ERASE_PREPARE 0xf3 +#define ATA_COMMAND_SECURITY_ERASE_UNIT 0xf4 +#define ATA_COMMAND_SECURITY_FREEZE_LOCK 0xf5 +#define ATA_COMMAND_SECURITY_DISABLE_PASSWORD 0xf6 + +/* Commands from ATA-4 specification: other commands */ +#define ATA_COMMAND_SMART 0xb0 +#define ATA_COMMAND_READ_DMA_QUEUED 0xc7 +#define ATA_COMMAND_WRITE_DMA_QUEUED 0xcc +#define ATA_COMMAND_GET_MEDIA_STATUS 0xda +#define ATA_COMMAND_FLUSH_CACHE 0xe7 +#define ATA_COMMAND_READ_NATIVE_MAX_ADDRESS 0xf8 +#define ATA_COMMAND_SET_MAX_ADDRESS 0xf9 + +#define ATA_REGISTERS_VALUE(reg) (1 << (reg)) + +/* ATA IDENTIFY DEVICE command words and bits */ +#define ATA_IDENT_WORD_RW_MULT 47 +#define ATA_IDENT_WORD_CAPABILITIES 49 +#define ATA_IDENT_WORD_FIELD_VALIDITY 53 +#define ATA_IDENT_WORD_NUM_OF_CURR_LOG_CLNDS 54 +#define ATA_IDENT_WORD_NUM_OF_CURR_LOG_HEADS 55 +#define ATA_IDENT_WORD_NUM_OF_CURR_LOG_SECS 56 +#define ATA_IDENT_WORD_MULT_SECS 59 +#define ATA_IDENT_WORD_NUM_OF_USR_SECS0 60 +#define ATA_IDENT_WORD_NUM_OF_USR_SECS1 61 +#define ATA_IDENT_WORD_PIO_SPPRTD 64 + +#define ATA_IDENT_BIT_VALID 0x02 + +/* + * It is OR for all ATA_REGISTERS_VALUE(reg), where reg is neccessary + * for setting block position + */ +#define ATA_REGISTERS_POSITION 0xfc + +#define ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE 64 + +#define ATA_MAX_RTEMS_INT_VEC_NUMBER 255 + +#define ATA_MAX_NAME_LENGTH 10 + +/* diagnostic codes */ +#define ATA_DEV0_PASSED_DEV1_PASSED_OR_NOT_PRSNT 0x01 +#define ATA_DEV0_PASSED_DEV1_FAILED 0x81 +#define ATA_DEV1_PASSED_DEV0_FAILED 0x80 + +/* + * Obtain ata device parameters by controller minor number and device number + */ +#define ATA_DEV_INFO(controller_minor, dev) \ + ata_ide_ctrls[controller_minor].device[dev] + +/* ATA RTEMS driver internal data stuctures */ + +/* Command block registers */ +typedef struct ata_registers_s { + unsigned16 regs[8]; /* command block registers */ + unsigned16 to_read; /* mask: which ata registers should be read */ + unsigned16 to_write; /* mask: which ata registers should be written */ +} ata_registers_t; + +/* ATA request */ +typedef struct ata_req_s { + Chain_Node link; /* link in requests chain */ + char type; /* request type */ + ata_registers_t regs; /* ATA command */ + rtems_unsigned32 cnt; /* Number of sectors to be exchanged */ + rtems_unsigned32 cbuf; /* number of current buffer from breq in use */ + rtems_unsigned32 pos; /* current position in 'cbuf' */ + blkdev_request *breq; /* blkdev_request which corresponds to the + * ata request + */ + rtems_id sema; /* semaphore which is used if synchronous + * processing of the ata request is required + */ + rtems_status_code status; /* status of ata request processing */ + int error; /* device error code */ +} ata_req_t; + +/* call callback provided by block device request if it is defined */ +#define ATA_EXEC_CALLBACK(areq, status, error) \ + do {\ + if (((areq)->breq != NULL) && ((areq)->breq->req_done != NULL)) \ + (areq)->breq->req_done((areq)->breq->done_arg, status, error); \ + } while (0) + +/* ATA RTEMS driver events types */ +typedef enum ata_msg_type_s { + ATA_MSG_GEN_EVT = 1, /* general event */ + ATA_MSG_SUCCESS_EVT, /* success event */ + ATA_MSG_ERROR_EVT, /* error event */ + ATA_MSG_PROCESS_NEXT_EVT /* process next request event */ +} ata_msg_type_t; + +/* ATA RTEMS driver message */ +typedef struct ata_queue_msg_s { + ata_msg_type_t type; /* message type */ + rtems_device_minor_number ctrl_minor; /* IDE controller minor number */ + int error; /* error code */ +} ata_queue_msg_t; + +/* macros for messages processing */ +#define ATA_FILL_MSG(msg, evt_type, ctrl, err)\ + do {\ + msg.type = evt_type;\ + msg.ctrl_minor = ctrl;\ + msg.error = err;\ + } while (0) + +#define ATA_SEND_EVT(msg, type, ctrl, err)\ + do {\ + rtems_status_code rc;\ + ATA_FILL_MSG(msg, type, ctrl, err);\ + rc = rtems_message_queue_send(ata_queue_id, &msg,\ + sizeof(ata_queue_msg_t));\ + if (rc != RTEMS_SUCCESSFUL)\ + rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR);\ + } while (0) + +/* + * Array of such structures is indexed by interrupt vecotrs and used for + * mapping of IDE controllers and interrupt vectors + */ +typedef struct ata_int_st_s { + Chain_Node link; + rtems_device_minor_number ctrl_minor; +} ata_int_st_t; + +/* + * Mapping of rtems ATA devices to the following pairs: + * (IDE controller number served the device, device number on the controller) + */ +typedef struct ata_ide_dev_s { + int ctrl_minor;/* minor number of IDE controller served rtems ATA device */ + int device; /* device number on IDE controller (0 or 1) */ +} ata_ide_dev_t; + +/* + * ATA device description + */ +typedef struct ata_dev_s { + signed8 present; /* 1 -- present, 0 -- not present, */ + /* -1 -- non-initialized */ + unsigned16 cylinders; + unsigned16 heads; + unsigned16 sectors; + unsigned32 lba_sectors; /* for small disk */ + /* == cylinders * heads * sectors */ + + unsigned8 lba_avaible; /* 0 - CHS mode, 1 - LBA mode */ + + unsigned8 max_multiple; /* 0 if READ/WRITE MULTIPLE is unsupported */ + unsigned8 current_multiple; + + unsigned8 modes_avaible; /* OR of values for this modes */ + unsigned8 mode_active; +} ata_dev_t; + +/* + * This structure describes controller state, devices configuration on the + * controller and chain of ATA requests to the controller. Array of such + * structures is indexed by controller minor number + */ +typedef struct ata_ide_ctrl_s { + rtems_boolean present; /* controller state */ + ata_dev_t device[2]; /* ata diveces description */ + Chain_Control reqs; /* requests chain */ +} ata_ide_ctrl_t; + +#endif /* __ATA_INTERNAL_H__ */ + + \ No newline at end of file diff --git a/cpukit/libblock/include/rtems/blkdev.h b/cpukit/libblock/include/rtems/blkdev.h index ca3b5ba5e2..5f3f6159ea 100644 --- a/cpukit/libblock/include/rtems/blkdev.h +++ b/cpukit/libblock/include/rtems/blkdev.h @@ -62,6 +62,10 @@ typedef struct blkdev_request { blkdev_request_op req; /* Block device operation (read or write) */ blkdev_request_cb req_done; /* Callback function */ void *done_arg; /* Argument to be passed to callback function*/ + rtems_status_code status; /* Last I/O operation completion status */ + int error; /* If status != RTEMS_SUCCESSFUL, this field + * contains error code + */ blkdev_bnum start; /* Start block number */ rtems_unsigned32 count; /* Number of blocks to be exchanged */ rtems_unsigned32 bufnum; /* Number of buffers provided */ diff --git a/cpukit/libblock/include/rtems/ide_part_table.h b/cpukit/libblock/include/rtems/ide_part_table.h new file mode 100644 index 0000000000..79d3f7710a --- /dev/null +++ b/cpukit/libblock/include/rtems/ide_part_table.h @@ -0,0 +1,186 @@ +/***************************************************************************** + * + * ide_part_table.h + * + * The header file for library supporting "MS-DOS-style" partition table + * + * + * Copyright (C) 2002 OKTET Ltd., St.-Petersburg, Russia + * + * Author: Konstantin Abramenko + * Alexander Kukuta + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * $Id$ + * + *****************************************************************************/ + +#ifndef __RTEMS_IDE_PART_TABLE_H__ +#define __RTEMS_IDE_PART_TABLE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Minor base number for all logical devices */ +#define RTEMS_IDE_SECTOR_BITS 9 +#define RTEMS_IDE_SECTOR_SIZE 512 +#define RTEMS_IDE_PARTITION_DESCRIPTOR_SIZE 16 +#define RTEMS_IDE_PARTITION_MAX_PARTITION_NUMBER 63 +#define RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER 4 +#define RTEMS_IDE_PARTITION_DEV_NAME_LENGTH_MAX 16 + +#define RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_DATA1 0x55 +#define RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_DATA2 0xaa +#define RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_OFFSET 0x1fe +#define RTEMS_IDE_PARTITION_TABLE_OFFSET 0x1be +#define RTEMS_IDE_PARTITION_BOOTABLE_OFFSET 0 +#define RTEMS_IDE_PARTITION_SYS_TYPE_OFFSET 4 +#define RTEMS_IDE_PARTITION_START_OFFSET 8 +#define RTEMS_IDE_PARTITION_SIZE_OFFSET 12 + +/* + * Conversion from and to little-endian byte order. (no-op on i386/i486) + */ + +#if (CPU_BIG_ENDIAN == TRUE) +# define LE_TO_CPU_U16(v) CPU_swap_u16(v) +# define LE_TO_CPU_U32(v) CPU_swap_u32(v) +# define CPU_TO_LE_U16(v) CPU_swap_u16(v) +# define CPU_TO_LE_U32(v) CPU_swap_u32(v) +#else +# define LE_TO_CPU_U16(v) (v) +# define LE_TO_CPU_U32(v) (v) +# define CPU_TO_LE_U16(v) (v) +# define CPU_TO_LE_U32(v) (v) +#endif + + +/* + * sector_data_t -- + * corresponds to the sector on the device + */ +typedef struct sector_data_s +{ + unsigned32 sector_num; /* sector number on the device */ + unsigned8 data[0]; /* raw sector data */ +} sector_data_t; + + +/* + * Enum partition types + */ +enum { + EMPTY_PARTITION = 0, + EXTENDED_PARTITION = 5, + DM6_PARTITION = 0x54, + EZD_PARTITION = 0x55, + DM6_AUX1PARTITION = 0x51, + DM6_AUX3PARTITION = 0x53, + LINUX_SWAP = 0x82, + LINUX_NATIVE = 0x83, + LINUX_EXTENDED = 0x85 +}; + + +/* Forward declaration */ +struct disk_desc_s; + +/* + * part_desc_t -- + * contains all neccessary information about partition + */ +typedef struct part_desc_s { + unsigned8 bootable; /* is the partition active */ + unsigned8 sys_type; /* type of partition */ + unsigned8 log_id; /* logical number of partition */ + unsigned32 start; /* first partition sector, in absolute numeration */ + unsigned32 size; /* size in sectors */ + unsigned32 end; /* last partition sector, end = start + size - 1 */ + struct disk_desc_s *disk_desc; /* descriptor of disk, partition contains in */ + struct part_desc_s *ext_part; /* extended partition containing this one */ + + /* partitions, containing in this one */ + struct part_desc_s *sub_part[RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER]; +} part_desc_t; + + + +typedef struct disk_desc_s { + dev_t dev; /* device number */ + + /* device name in /dev filesystem */ + unsigned8 dev_name[RTEMS_IDE_PARTITION_DEV_NAME_LENGTH_MAX]; + + unsigned32 sector_size; /* size of sector */ + unsigned32 sector_bits; /* the base-2 logarithm of sector_size */ + unsigned32 lba_size; /* total amount of sectors in lba address mode */ + int last_log_id; /* used for logical disks enumerating */ + + /* primary partition descriptors */ + part_desc_t *partitions[RTEMS_IDE_PARTITION_MAX_PARTITION_NUMBER]; +} disk_desc_t; + + +/* + * rtems_ide_part_table_free -- + * frees disk descriptor structure + * + * PARAMETERS: + * disk_desc - disc descriptor structure to free + * + * RETURNS: + * N/A + */ +void +rtems_ide_part_table_free(disk_desc_t *disk_desc); + + +/* + * rtems_ide_part_table_get -- + * reads partition table structure from the device + * and creates disk description structure + * + * PARAMETERS: + * dev_name - path to physical device in /dev filesystem + * disk_desc - returned disc description structure + * + * RETURNS: + * RTEMS_SUCCESSFUL if success, or -1 and corresponding errno else + */ +rtems_status_code +rtems_ide_part_table_get(const char *dev_name, disk_desc_t *disk_desc); + + +/* + * rtems_ide_part_table_initialize -- + * initializes logical devices on the physical IDE drive + * + * PARAMETERS: + * dev_name - path to physical device in /dev filesystem + * + * RETURNS: + * RTEMS_SUCCESSFUL if success, or -1 and corresponding errno else + */ +rtems_status_code +rtems_ide_part_table_initialize(char *dev_name); + + +#endif /* __RTEMS_IDE_PART_TABLE_H__ */ diff --git a/cpukit/libblock/src/ata.c b/cpukit/libblock/src/ata.c new file mode 100644 index 0000000000..3087d62141 --- /dev/null +++ b/cpukit/libblock/src/ata.c @@ -0,0 +1,1319 @@ +/* + * ata.c + * + * ATA RTEMS driver. ATA driver is hardware independant implementation of + * ATA-2 standart, working draft X3T10/0948D, revision 4c. ATA driver bases + * on RTEMS IDE controller driver. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Authors: Eugeny S. Mints + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * $Id$ + * + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#ifdef DEBUG +#include +#endif + +#define SAFE +#ifdef SAFE +typedef rtems_mode preemption_key; + +#define DISABLE_PREEMPTION(key) \ + do { \ + rtems_task_mode(RTEMS_PREEMPT_MASK, RTEMS_NO_PREEMPT, &(key)); \ + } while (0) + +#define ENABLE_PREEMPTION(key) \ + do { \ + rtems_mode temp; \ + rtems_task_mode(RTEMS_PREEMPT_MASK, (key), &temp); \ + } while (0) + +#else + +typedef boolean preemption_key; + +#define DISABLE_PREEMPTION(key) \ + do { \ + (key) = _Thread_Executing->is_preemptible; \ + _Thread_Executing->is_preemptible = 0; \ + } while (0) + +#define ENABLE_PREEMPTION(key) \ + do { \ + _Thread_Executing->is_preemptible = (key); \ + if (_Thread_Evaluate_mode()) \ + _Thread_Dispatch(); \ + } while (0) + +#endif + +/* FIXME: case if ATA device is FLASH device need more attention */ +#undef ATA_DEV_IS_FLASH_DISK + +/* Block device request with a single buffer provided */ +typedef struct blkdev_request1 { + blkdev_request req; + blkdev_sg_buffer sg[1]; +} blkdev_request1; + + +/* Array indexed by controllers minor number */ +static ata_ide_ctrl_t ata_ide_ctrls[IDE_CTRL_MAX_MINOR_NUMBER]; + +/* + * Mapping from ATA-minor numbers to + * controller-minor and device on this controller. + */ +static ata_ide_dev_t ata_devs[2 * IDE_CTRL_MAX_MINOR_NUMBER]; +static int ata_devs_number; + +/* Flag meaning that ATA driver has already been initialized */ +static rtems_boolean ata_initialized = FALSE; + + +/* task and queue used for asynchronous I/O operations */ +static rtems_id ata_task_id; +static rtems_id ata_queue_id; + +/* Mapping of interrupt vectors to devices */ +static Chain_Control ata_int_vec[ATA_MAX_RTEMS_INT_VEC_NUMBER + 1]; + +static void +ata_process_request(rtems_device_minor_number ctrl_minor); + +static void +ata_process_request_on_init_phase(rtems_device_minor_number ctrl_minor, + ata_req_t *areq); + +static void +ata_add_to_controller_queue(rtems_device_minor_number ctrl_minor, + ata_req_t *areq); + +/* + * read/write, open/close and ioctl are provided by general block device + * driver. Only initialization and ata-specific ioctl are here. + */ + +/* ata_io_data_request -- + * Form read/write request for an ATA device and enqueue it to + * IDE controller. + * + * PARAMETERS: + * device - device identifier + * req - read/write request from block device driver + * + * RETURNS: + * RTEMS_SUCCESSFUL on success, or error code if + * error occured + */ +static rtems_status_code +ata_io_data_request(dev_t device, blkdev_request *req) +{ + ata_req_t *areq; /* ATA request */ + rtems_device_minor_number rel_minor; /* relative minor which indexes + * ata_devs array + */ + rtems_device_minor_number ctrl_minor; + unsigned8 dev; + + rel_minor = (rtems_filesystem_dev_minor_t(device)) / + ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE; + + /* get controller which serves the ATA device */ + ctrl_minor = ata_devs[rel_minor].ctrl_minor; + + /* get ATA device identifier (0 or 1) */ + dev = ata_devs[rel_minor].device; + + areq = malloc(sizeof(ata_req_t)); + if (areq == NULL) + { + return RTEMS_NO_MEMORY; + } + + areq->breq = req; + areq->cnt = req->count; + areq->cbuf = 0; + areq->pos = 0; + + /* set up registers masks */ + areq->regs.to_write = ATA_REGISTERS_POSITION; + areq->regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_STATUS); + + /* choose device on the controller for which the command will be issued */ + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] = + (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS); + + /* Find ATA command and its type */ + if (ATA_DEV_INFO(ctrl_minor, dev).mode_active & ATA_MODES_DMA) + { + /* XXX: never has been tested */ + areq->type = ATA_COMMAND_TYPE_DMA; + if (req->req == BLKDEV_REQ_READ) + areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_READ_DMA; + else + areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_WRITE_DMA; + } + else + { + if (req->req == BLKDEV_REQ_READ) + { + areq->type = ATA_COMMAND_TYPE_PIO_IN; + + /* + * choose command to issue: if the number of blocks to be + * exchanged is greater then 1 and for ATA command READ MULTIPLE + * data block consists of more then 1 sector choose READ MULTIPLE + * otherwise READ SECTORS + */ + areq->regs.regs[IDE_REGISTER_COMMAND] = + ((ATA_DEV_INFO(ctrl_minor, dev).max_multiple) && + (req->count > 1) && + (ATA_DEV_INFO(ctrl_minor, dev).current_multiple > 1)) ? + ATA_COMMAND_READ_MULTIPLE : + ATA_COMMAND_READ_SECTORS; + } + else + { + areq->type = ATA_COMMAND_TYPE_PIO_OUT; + + /* + * choose command to issue: if the number of blocks to be + * exchanged is greater then 1 and for ATA command WRITE MULTIPLE + * data block consists of more then 1 sector choose WRITE MULTIPLE + * otherwise WRITE SECTORS + */ + areq->regs.regs[IDE_REGISTER_COMMAND] = + ((ATA_DEV_INFO(ctrl_minor, dev).max_multiple) && + (req->count > 1) && + (ATA_DEV_INFO(ctrl_minor, dev).current_multiple > 1)) ? + ATA_COMMAND_WRITE_MULTIPLE : + ATA_COMMAND_WRITE_SECTORS; + } + } + + /* + * Fill position registers + */ + if (ATA_DEV_INFO(ctrl_minor, dev).lba_avaible) + { + areq->regs.regs[IDE_REGISTER_LBA0] = (unsigned8)req->start; + areq->regs.regs[IDE_REGISTER_LBA1] = (unsigned8)(req->start >> 8); + areq->regs.regs[IDE_REGISTER_LBA2] = (unsigned8)(req->start >> 16); + areq->regs.regs[IDE_REGISTER_LBA3] |= (unsigned8) (req->start >> 24); + areq->regs.regs[IDE_REGISTER_LBA3] |= IDE_REGISTER_LBA3_L; + } + else + { + unsigned32 count = req->start; + + areq->regs.regs[IDE_REGISTER_SECTOR_NUMBER] = + (count % ATA_DEV_INFO(ctrl_minor, dev).sectors) + 1; + + /* now count = number of tracks: */ + count /= ATA_DEV_INFO(ctrl_minor, dev).sectors; + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] |= + (count / ATA_DEV_INFO(ctrl_minor, dev).cylinders); + + /* now count = number of cylinders */ + count %= ATA_DEV_INFO(ctrl_minor, dev).cylinders; + areq->regs.regs[IDE_REGISTER_CYLINDER_LOW] = (unsigned8)count; + areq->regs.regs[IDE_REGISTER_CYLINDER_HIGH] = (unsigned8)(count >> 8); + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] &= + ~IDE_REGISTER_DEVICE_HEAD_L; + } + + /* fill sector count register */ + areq->regs.regs[IDE_REGISTER_SECTOR_COUNT] = areq->breq->count; + + /* add request to the queue of awaiting requests to the controller */ + ata_add_to_controller_queue(ctrl_minor, areq); + + return RTEMS_SUCCESSFUL; +} + +/* ata_non_data_request -- + * Form and serve request of NON DATA type for an ATA device. + * Processing of NON DATA request is SYNChronous operation. + * + * PARAMETERS: + * device - device identifier + * cmd - command + * argp - arguments for command + * + * RETURNS: + * RTEMS_SUCCESSFUL on success, or error code if + * error occured + */ +static rtems_status_code +ata_non_data_request(dev_t device, int cmd, void *argp) +{ + rtems_status_code rc; + ata_req_t *areq; /* ATA request */ + rtems_device_minor_number rel_minor; /* relative minor which indexes + * ata_devs array + */ + rtems_device_minor_number ctrl_minor; + unsigned8 dev; + ata_queue_msg_t msg; + + rel_minor = (rtems_filesystem_dev_minor_t(device)) / + ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE; + + /* get controller which serves the ATA device */ + ctrl_minor = ata_devs[rel_minor].ctrl_minor; + + /* get ATA device identifier (0 or 1) */ + dev = ata_devs[rel_minor].device; + + /* form the request */ + areq = malloc(sizeof(ata_req_t)); + if (areq == NULL) + { + return RTEMS_NO_MEMORY; + } + memset(areq, 0, sizeof(ata_req_t)); + + areq->type = ATA_COMMAND_TYPE_NON_DATA; + areq->regs.to_write = ATA_REGISTERS_VALUE(IDE_REGISTER_COMMAND); + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] |= + (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS); + areq->breq = NULL; + areq->regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_ERROR); + + /* + * depending on command fill command register and additional registers + * which are needed for command execution + */ + switch(cmd) + { + case ATAIO_SET_MULTIPLE_MODE: + areq->regs.regs[IDE_REGISTER_COMMAND] = + ATA_COMMAND_SET_MULTIPLE_MODE; + areq->regs.to_write |= + ATA_REGISTERS_VALUE(IDE_REGISTER_SECTOR_COUNT); + areq->regs.regs[IDE_REGISTER_SECTOR_COUNT] = *(unsigned8 *)argp; + break; + + default: + free(areq); + return RTEMS_INVALID_NUMBER; + break; + } + + rc = rtems_semaphore_create(rtems_build_name('I', 'D', 'E', 'S'), + 0, + RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, + 0, + &(areq->sema)); + if (rc != RTEMS_SUCCESSFUL) + { + free(areq); + return rc; + } + + ata_add_to_controller_queue(ctrl_minor, areq); + + /* wait for request processing... */ + rc = rtems_semaphore_obtain(areq->sema, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + { + free(areq); + return rc; + } + + rtems_semaphore_delete(areq->sema); + + /* + * if no error occurred and if necessary, update internal ata driver data + * structures to reflect changes (in device configuration, for example) + */ + if (areq->status == RTEMS_SUCCESSFUL) + { + switch(cmd) + { + case ATAIO_SET_MULTIPLE_MODE: + ATA_DEV_INFO(ctrl_minor, dev).current_multiple = + *(unsigned8 *)argp; + break; + + default: + rc = RTEMS_INVALID_NUMBER; + break; + } + } + else + { + /* XXX: should be correct error processing: for ex, may be + * ABRT and then we should return RTEMS_NOT_IMPLEMENTED + */ + rc = RTEMS_IO_ERROR; + } + + /* tell ata driver that controller ready to serve next request */ + ATA_SEND_EVT(msg, ATA_MSG_SUCCESS_EVT, ctrl_minor, 0); + + return rc; +} + +/* ata_process_request -- + * Get first request from controller's queue and process it. + * + * PARAMETERS: + * ctrl_minor - controller identifier + * + * RETURNS: + * NONE + */ +static void +ata_process_request(rtems_device_minor_number ctrl_minor) +{ + ata_req_t *areq; + unsigned16 byte; /* emphasize that only 8 low bits is meaningful */ + ata_queue_msg_t msg; + unsigned8 i, dev; + unsigned16 val; + unsigned16 data_bs; /* the number of 512-bytes sectors in one + * data block + */ + ISR_Level level; + + /* if no requests to controller then do nothing */ + if (Chain_Is_empty(&ata_ide_ctrls[ctrl_minor].reqs)) + return; + + /* get first request in the controller's queue */ + _ISR_Disable(level); + areq = (ata_req_t *)(ata_ide_ctrls[ctrl_minor].reqs.first); + _ISR_Enable(level); + + /* get ATA device identifier (0 or 1) */ + dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] & + IDE_REGISTER_DEVICE_HEAD_DEV; + + /* get data block size */ + data_bs = ATA_DEV_INFO(ctrl_minor, dev).current_multiple ? + ATA_DEV_INFO(ctrl_minor, dev).current_multiple : 1; + + /* execute device select protocol */ + ide_controller_write_register(ctrl_minor, IDE_REGISTER_DEVICE_HEAD, + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD]); + + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &byte); + } while ((byte & IDE_REGISTER_STATUS_BSY) || + (!(byte & IDE_REGISTER_STATUS_DRDY))); + + /* fill in all necessary registers on the controller */ + for (i=0; i< ATA_MAX_CMD_REG_OFFSET; i++) + { + unsigned32 reg = (1 << i); + if (areq->regs.to_write & reg) + ide_controller_write_register(ctrl_minor, i, areq->regs.regs[i]); + } + + /* continue to execute ATA protocols depending on type of request */ + if (areq->type == ATA_COMMAND_TYPE_PIO_OUT) + { + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, + &byte); + } while (byte & IDE_REGISTER_STATUS_BSY); + + if (byte & IDE_REGISTER_STATUS_DRQ) + { + ide_controller_write_data_block( + ctrl_minor, + MIN(data_bs, areq->cnt) * ATA_SECTOR_SIZE, + areq->breq->bufs, &areq->cbuf, + &areq->pos); + areq->cnt -= MIN(data_bs, areq->cnt); + } + else + { + if (IDE_Controller_Table[ctrl_minor].int_driven == 0) + { + ide_controller_read_register( + ctrl_minor, + IDE_REGISTER_ALTERNATE_STATUS_OFFSET, + &val); + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, + &val); + + ATA_SEND_EVT(msg, ATA_MSG_ERROR_EVT, ctrl_minor, + RTEMS_IO_ERROR); + } + } + } + + if (IDE_Controller_Table[ctrl_minor].int_driven == 0) + { + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, + &byte); + } while (byte & IDE_REGISTER_STATUS_BSY); + + ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, ctrl_minor, 0); + } +} + +/* ata_request_done -- + * Extract request from controller queue, execute callback if necessary + * and process next request for the controller. + * + * PARAMETERS: + * areq - ATA request + * ctrl_minor - controller identifier + * status - status with which request has been done + * error - error, if status != RTEMS_SUCCESSFUL + * + * RETURNS: + * NONE + */ +static inline void +ata_request_done(ata_req_t *areq, rtems_device_minor_number ctrl_minor, + rtems_status_code status, int error) +{ + preemption_key key; + + assert(areq); + + DISABLE_PREEMPTION(key); + ATA_EXEC_CALLBACK(areq, status, error); + Chain_Extract(&areq->link); + free(areq); + + if (!Chain_Is_empty(&ata_ide_ctrls[ctrl_minor].reqs)) + { + ENABLE_PREEMPTION(key); + ata_process_request(ctrl_minor); + return; + } + ENABLE_PREEMPTION(key); +} + +/* ata_non_data_request_done -- + * Set up request status and release request's semaphore. + * + * PARAMETERS: + * areq - ATA request + * ctrl_minor - controller identifier + * status - status with which request has been done + * error - error, if status != RTEMS_SUCCESSFUL + * + * RETURNS: + * NONE + */ +static inline void +ata_non_data_request_done(ata_req_t *areq, + rtems_device_minor_number ctrl_minor, + rtems_status_code status, int error) +{ + areq->status = status; + areq->error = error; + rtems_semaphore_release(areq->sema); +} + + +/* ata_add_to_controller_queue -- + * Add request to the controller's queue. + * + * PARAMETERS: + * ctrl_minor - controller identifier + * areq - ATA request + * + * RETURNS: + * NONE + */ +static void +ata_add_to_controller_queue(rtems_device_minor_number ctrl_minor, + ata_req_t *areq) +{ + Chain_Append(&ata_ide_ctrls[ctrl_minor].reqs, &areq->link); + if (Chain_Has_only_one_node(&ata_ide_ctrls[ctrl_minor].reqs)) + { + unsigned16 val; + ata_queue_msg_t msg; + +#ifdef DEBUG + /* + * read IDE_REGISTER_ALTERNATE_STATUS instead IDE_REGISTER_STATUS + * to prevent clearing of pending interrupt + */ + ide_controller_read_register(ctrl_minor, + IDE_REGISTER_ALTERNATE_STATUS, + &val); + if (val & IDE_REGISTER_STATUS_BSY) + return; +#endif + ATA_SEND_EVT(msg, ATA_MSG_PROCESS_NEXT_EVT, ctrl_minor, 0); + } +} + + +/* ata_interrupt_handler -- + * ATA driver interrrupt handler. If interrrupt happend it mapped it to + * controller (controllerS, if a number of controllers share one int line) + * and generates ATA event(s). + * + * PARAMETERS: + * vec - interrupt vector + * + * RETURNS: + * NONE + */ +rtems_isr +ata_interrupt_handler(rtems_vector_number vec) +{ + Chain_Node *the_node = ((Chain_Control *)(&ata_int_vec[vec]))->first; + ata_queue_msg_t msg; + unsigned16 byte; /* emphasize that only 8 low bits is meaningful */ + + for ( ; !Chain_Is_tail(&ata_int_vec[vec], the_node) ; ) + { + /* if (1) - is temporary hack - currently I don't know how to identify + * controller which asserted interrupt if few controllers share one + * interrupt line + */ + if (1) + { + msg.ctrl_minor = ((ata_int_st_t *)the_node)->ctrl_minor; + ide_controller_read_register(msg.ctrl_minor, IDE_REGISTER_STATUS, + &byte); + ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, msg.ctrl_minor, 0); + } + the_node = the_node->next; + } +} + +/* ata_pio_in_protocol -- + * ATA PIO_IN protocol implementation, see specification + * + * PARAMETERS: + * ctrl_minor - controller identifier + * areq - ATA request + * + * RETURNS: + * NONE + */ +static inline void +ata_pio_in_protocol(rtems_device_minor_number ctrl_minor, ata_req_t *areq) +{ + unsigned16 bs, val; + unsigned8 dev; + unsigned32 min_val; + ata_queue_msg_t msg; + + dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] & + IDE_REGISTER_DEVICE_HEAD_DEV; + + bs = ATA_DEV_INFO(ctrl_minor, dev).current_multiple ? + ATA_DEV_INFO(ctrl_minor, dev).current_multiple : 1; + min_val = MIN(bs, areq->cnt); + + ide_controller_read_data_block(ctrl_minor, min_val * ATA_SECTOR_SIZE, + areq->breq->bufs, &areq->cbuf, &areq->pos); + + areq->cnt -= min_val; + if (areq->cnt == 0) + { + ata_request_done(areq, ctrl_minor, RTEMS_SUCCESSFUL, RTEMS_SUCCESSFUL); + } + else if (IDE_Controller_Table[ctrl_minor].int_driven == 0) + { + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &val); + } while (val & IDE_REGISTER_STATUS_BSY); + + ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, ctrl_minor, 0); + } +} + +/* ata_pio_out_protocol -- + * ATA PIO_OUT protocol implementation, see specification + * + * PARAMETERS: + * ctrl_minor - controller identifier + * areq - ATA request + * + * RETURNS: + * NONE + */ +static inline void +ata_pio_out_protocol(rtems_device_minor_number ctrl_minor, ata_req_t *areq) +{ + unsigned16 bs, val; + unsigned8 dev; + unsigned32 min_val; + ata_queue_msg_t msg; + + dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] & + IDE_REGISTER_DEVICE_HEAD_DEV; + + bs = ATA_DEV_INFO(ctrl_minor, dev).current_multiple ? + ATA_DEV_INFO(ctrl_minor, dev).current_multiple : 1; + + min_val = MIN(bs, areq->cnt); + + if (areq->cnt == 0) + { + ata_request_done(areq, ctrl_minor, RTEMS_SUCCESSFUL, RTEMS_SUCCESSFUL); + } + else + { + ide_controller_write_data_block(ctrl_minor, min_val * ATA_SECTOR_SIZE, + areq->breq->bufs, &areq->cbuf, + &areq->pos); + areq->cnt -= min_val; + if (IDE_Controller_Table[ctrl_minor].int_driven == 0) + { + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, + &val); + } while (val & IDE_REGISTER_STATUS_BSY); + + ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, ctrl_minor, 0); + } + } +} + +/* ata_queue_task -- + * Task which manages ATA driver events queue. + * + * PARAMETERS: + * arg - ignored + * + * RETURNS: + * NONE + * + * NOTES: + * should be non-preemptive + */ +static rtems_task +ata_queue_task(rtems_task_argument arg) +{ + ata_queue_msg_t msg; + rtems_unsigned32 size; + ata_req_t *areq; + rtems_device_minor_number ctrl_minor; + unsigned16 val; + unsigned16 val1; + rtems_status_code rc; + ISR_Level level; + + while (1) + { + /* get event which has happend */ + rc = rtems_message_queue_receive(ata_queue_id, &msg, &size, RTEMS_WAIT, + RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR); + + /* get controller on which event has happend */ + ctrl_minor = msg.ctrl_minor; + + /* get current request to the controller */ + _ISR_Disable(level); + areq = (ata_req_t *)(ata_ide_ctrls[ctrl_minor].reqs.first); + _ISR_Enable(level); + + switch(msg.type) + { + case ATA_MSG_PROCESS_NEXT_EVT: + /* process next request in the controller queue */ + ata_process_request(ctrl_minor); + break; + + case ATA_MSG_SUCCESS_EVT: + /* + * finish processing of current request with successful + * status and start processing of the next request in the + * controller queue + */ + ata_request_done(areq, ctrl_minor, RTEMS_SUCCESSFUL, + msg.error); + break; + + case ATA_MSG_ERROR_EVT: + /* + * finish processing of current request with error + * status and start processing of the next request in the + * controller queue + */ + ata_request_done(areq, ctrl_minor, RTEMS_UNSATISFIED, + msg.error); + break; + + case ATA_MSG_GEN_EVT: + /* + * continue processing of the current request to the + * controller according to current request state and + * ATA protocol + */ + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, + &val); + /* process error case */ + if (val & IDE_REGISTER_STATUS_ERR) + { + ide_controller_read_register(ctrl_minor, + IDE_REGISTER_ERROR, + &val); + if (areq->type == ATA_COMMAND_TYPE_NON_DATA) + ata_non_data_request_done(areq, ctrl_minor, + RTEMS_UNSATISFIED, + RTEMS_IO_ERROR); + else + ata_request_done(areq, ctrl_minor, RTEMS_UNSATISFIED, + RTEMS_IO_ERROR); + break; + } + + switch(areq->type) + { + case ATA_COMMAND_TYPE_PIO_IN: + ata_pio_in_protocol(ctrl_minor, areq); + break; + + case ATA_COMMAND_TYPE_PIO_OUT: + ata_pio_out_protocol(ctrl_minor, areq); + break; + + case ATA_COMMAND_TYPE_NON_DATA: + ide_controller_read_register(ctrl_minor, + IDE_REGISTER_ERROR, + &val1); + ata_non_data_request_done(areq, ctrl_minor, + RTEMS_SUCCESSFUL, + val1); + break; + + default: + printf("ata_queue_task: non-supported command type\n"); + ata_request_done(areq, ctrl_minor, + RTEMS_UNSATISFIED, + RTEMS_NOT_IMPLEMENTED); + break; + } + break; + + default: + rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR); + break; + } + } +} + +/* ata_ioctl -- + * ATA driver ioctl interface. + * + * PARAMETERS: + * device - device identifier + * cmd - command + * argp - arguments + * + * RETURNS: + * depend on 'cmd' + */ +int +ata_ioctl(dev_t device, int cmd, void *argp) +{ + rtems_status_code status; + rtems_device_minor_number rel_minor; + + rel_minor = (rtems_filesystem_dev_minor_t(device)) / + ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE; + + /* + * in most cases this means that device 'device' is not an registred ATA + * device + */ + if (ata_devs[rel_minor].device == ATA_UNDEFINED_VALUE) + { + errno = ENODEV; + return -1; + } + + switch (cmd) + { + case BLKIO_REQUEST: + status = ata_io_data_request(device, (blkdev_request *)argp); + break; + + case ATAIO_SET_MULTIPLE_MODE: + status = ata_non_data_request(device, cmd, argp); + break; + + default: + errno = EBADRQC; + return -1; + break; + } + + if (status != RTEMS_SUCCESSFUL) + { + errno = EIO; + return -1; + } + return 0; +} + +/* + * ata_initialize -- + * Initializes all ATA devices found on initialized IDE controllers. + * + * PARAMETERS: + * major - device major number + * minor - device minor number + * args - arguments + * + * RETURNS: + * RTEMS_SUCCESSFUL on success, or error code if + * error occured + */ +rtems_device_driver +ata_initialize(rtems_device_major_number major, + rtems_device_minor_number minor_arg, + void *args) +{ + unsigned32 ctrl_minor; + rtems_status_code status; + ata_req_t areq; + blkdev_request1 breq; + unsigned8 i, dev = 0; + unsigned16 *buffer; + unsigned16 ec; + char name[ATA_MAX_NAME_LENGTH]; + dev_t device; + ata_int_st_t *int_st; + rtems_isr_entry old_isr; + + if (ata_initialized) + return RTEMS_SUCCESSFUL; + + /* initialization of disk devices library */ + status = rtems_disk_io_initialize(); + if (status != RTEMS_SUCCESSFUL) + return status; + + /* create queue for asynchronous requests handling */ + status = rtems_message_queue_create( + rtems_build_name('A', 'T', 'A', 'Q'), + ATA_DRIVER_MESSAGE_QUEUE_SIZE, + sizeof(ata_queue_msg_t), + RTEMS_FIFO | RTEMS_LOCAL, + &ata_queue_id); + if (status != RTEMS_SUCCESSFUL) + { + rtems_disk_io_done(); + return status; + } + + /* + * create ATA driver task, see comments for task implementation for + * details + */ + status = rtems_task_create( + rtems_build_name ('A', 'T', 'A', 'T'), + ATA_DRIVER_TASK_PRIORITY, + ATA_DRIVER_TASK_STACK_SIZE, + RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_ASR | + RTEMS_INTERRUPT_LEVEL(0), + RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL, + &ata_task_id); + if (status != RTEMS_SUCCESSFUL) + { + rtems_message_queue_delete(ata_queue_id); + rtems_disk_io_done(); + return status; + } + + /* + * start ATA driver task. Actually the task will not start immediately - + * it will start only after multitasking support will be started + */ + status = rtems_task_start(ata_task_id, ata_queue_task, 0); + if (status != RTEMS_SUCCESSFUL) + { + rtems_task_delete(ata_task_id); + rtems_message_queue_delete(ata_queue_id); + rtems_disk_io_done(); + return status; + } + + buffer = (unsigned16 *)malloc(ATA_SECTOR_SIZE); + if (buffer == NULL) + { + rtems_task_delete(ata_task_id); + rtems_message_queue_delete(ata_queue_id); + rtems_disk_io_done(); + return RTEMS_NO_MEMORY; + } + + ata_devs_number = 0; + + for (i = 0; i < (2 * IDE_CTRL_MAX_MINOR_NUMBER); i++) + ata_devs[i].device = ATA_UNDEFINED_VALUE; + + /* prepare ATA driver for handling interrupt driven devices */ + for (i = 0; i < ATA_MAX_RTEMS_INT_VEC_NUMBER; i++) + Chain_Initialize_empty(&ata_int_vec[i]); + + /* + * during ATA driver initialization EXECUTE DEVICE DIAGNOSTIC and + * IDENTIFY DEVICE ATA command should be issued; for these purposes ATA + * requests should be formed; ATA requests contain block device request, + * so form block device request first + */ + memset(&breq, 0, sizeof(blkdev_request1)); + breq.req.req_done = NULL; + breq.req.done_arg = &breq; + breq.req.bufnum = 1; + breq.req.count = 1; + breq.req.bufs[0].length = ATA_SECTOR_SIZE; + breq.req.bufs[0].buffer = buffer; + + /* + * for each presented IDE controller execute EXECUTE DEVICE DIAGNOSTIC + * ATA command; for each found device execute IDENTIFY DEVICE ATA + * command + */ + for (ctrl_minor = 0; ctrl_minor < IDE_Controller_Count; ctrl_minor++) + if (IDE_Controller_Table[ctrl_minor].status == IDE_CTRL_INITIALIZED) + { + Chain_Initialize_empty(&ata_ide_ctrls[ctrl_minor].reqs); + + if (IDE_Controller_Table[ctrl_minor].int_driven == TRUE) + { + int_st = malloc(sizeof(ata_int_st_t)); + if (int_st == NULL) + { + free(buffer); + rtems_task_delete(ata_task_id); + rtems_message_queue_delete(ata_queue_id); + rtems_disk_io_done(); + return RTEMS_NO_MEMORY; + } + + int_st->ctrl_minor = ctrl_minor; + + status = rtems_interrupt_catch( + ata_interrupt_handler, + IDE_Controller_Table[ctrl_minor].int_vec, + &old_isr); + if (status != RTEMS_SUCCESSFUL) + { + free(int_st); + free(buffer); + rtems_task_delete(ata_task_id); + rtems_message_queue_delete(ata_queue_id); + rtems_disk_io_done(); + return status; + } + Chain_Append( + &ata_int_vec[IDE_Controller_Table[ctrl_minor].int_vec], + &int_st->link); + + /* disable interrupts */ + ide_controller_write_register(ctrl_minor, + IDE_REGISTER_DEVICE_CONTROL_OFFSET, + IDE_REGISTER_DEVICE_CONTROL_nIEN); + } + + /* + * Issue EXECUTE DEVICE DIAGNOSTIC ATA command for explore is + * there any ATA device on the controller. + */ + memset(&areq, 0, sizeof(ata_req_t)); + areq.type = ATA_COMMAND_TYPE_NON_DATA; + areq.regs.to_write = ATA_REGISTERS_VALUE(IDE_REGISTER_COMMAND); + areq.regs.regs[IDE_REGISTER_COMMAND] = + ATA_COMMAND_EXECUTE_DEVICE_DIAGNOSTIC; + areq.regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_ERROR); + + areq.breq = (blkdev_request *)&breq; + + /* + * Process the request. Special processing of requests on + * initialization phase is needed because at this moment there + * is no multitasking enviroment + */ + ata_process_request_on_init_phase(ctrl_minor, &areq); + + /* + * check status of I/O operation + */ + if (breq.req.status != RTEMS_SUCCESSFUL) + continue; + + /* disassemble returned diagnostic codes */ + if (breq.req.error == ATA_DEV0_PASSED_DEV1_PASSED_OR_NOT_PRSNT) + { + ATA_DEV_INFO(ctrl_minor, 0).present = 1; + ATA_DEV_INFO(ctrl_minor,1).present = 1; + } + else if (breq.req.error == ATA_DEV0_PASSED_DEV1_FAILED) + { + ATA_DEV_INFO(ctrl_minor,0).present = 1; + ATA_DEV_INFO(ctrl_minor,1).present = 0; + } + else if (breq.req.error < ATA_DEV1_PASSED_DEV0_FAILED) + { + ATA_DEV_INFO(ctrl_minor,0).present = 0; + ATA_DEV_INFO(ctrl_minor,1).present = 1; + } + else + { + ATA_DEV_INFO(ctrl_minor, 0).present = 0; + ATA_DEV_INFO(ctrl_minor, 1).present = 0; + } + + /* refine the returned codes */ + if (ATA_DEV_INFO(ctrl_minor, 1).present != 0) + { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_ERROR, &ec); + if (ec & ATA_DEV1_PASSED_DEV0_FAILED) + ATA_DEV_INFO(ctrl_minor, 1).present = 1; + else + ATA_DEV_INFO(ctrl_minor, 1).present = 0; + } + + /* for each found ATA device obtain it configuration */ + for (dev = 0; dev < 2; dev++) + if (ATA_DEV_INFO(ctrl_minor, dev).present) + { + /* + * Issue DEVICE IDENTIFY ATA command and get device + * configuration + */ + memset(&areq, 0, sizeof(ata_req_t)); + areq.type = ATA_COMMAND_TYPE_PIO_IN; + areq.regs.to_write = ATA_REGISTERS_VALUE(IDE_REGISTER_COMMAND); + areq.regs.regs[IDE_REGISTER_COMMAND] = + ATA_COMMAND_IDENTIFY_DEVICE; + areq.regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_STATUS); + areq.breq = (blkdev_request *)&breq; + + areq.cnt = breq.req.count; + + areq.regs.regs[IDE_REGISTER_DEVICE_HEAD] |= + (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS); + + /* + * Process the request. Special processing of requests on + * initialization phase is needed because at this moment there + * is no multitasking enviroment + */ + ata_process_request_on_init_phase(ctrl_minor, &areq); + + /* check status of I/O operation */ + if (breq.req.status != RTEMS_SUCCESSFUL) + continue; + + /* + * Parse returned device configuration and fill in ATA internal + * device info structure + */ + ATA_DEV_INFO(ctrl_minor, dev).cylinders = + CF_LE_W(buffer[ATA_IDENT_WORD_NUM_OF_CURR_LOG_CLNDS]); + ATA_DEV_INFO(ctrl_minor, dev).heads = + CF_LE_W(buffer[ATA_IDENT_WORD_NUM_OF_CURR_LOG_HEADS]); + ATA_DEV_INFO(ctrl_minor, dev).sectors = + CF_LE_W(buffer[ATA_IDENT_WORD_NUM_OF_CURR_LOG_SECS]); + ATA_DEV_INFO(ctrl_minor, dev).lba_sectors = + (CF_LE_W(buffer[ATA_IDENT_WORD_NUM_OF_USR_SECS0]) << 16) + + CF_LE_W(buffer[ATA_IDENT_WORD_NUM_OF_USR_SECS1]); + ATA_DEV_INFO(ctrl_minor, dev).lba_avaible = + (CF_LE_W(buffer[ATA_IDENT_WORD_CAPABILITIES]) >> 9) & 0x1; + ATA_DEV_INFO(ctrl_minor, dev).max_multiple = + (unsigned8) (CF_LE_W(buffer[ATA_IDENT_WORD_RW_MULT])); + ATA_DEV_INFO(ctrl_minor, dev).current_multiple = + (CF_LE_W(buffer[ATA_IDENT_WORD_MULT_SECS]) & 0x100) ? + (unsigned8)(CF_LE_W(buffer[ATA_IDENT_WORD_MULT_SECS])) : + 0; + +#ifndef ATA_DEV_IS_FLASH_DISK + if ((CF_LE_W(buffer[ATA_IDENT_WORD_FIELD_VALIDITY]) & + ATA_IDENT_BIT_VALID) == 0) + continue; +#endif + ATA_DEV_INFO(ctrl_minor, dev).modes_avaible = + ((CF_LE_W(buffer[64]) & 0x1) ? ATA_MODES_PIO3 : 0) | + ((CF_LE_W(buffer[64]) & 0x2) ? ATA_MODES_PIO4 : 0) | + ((CF_LE_W(buffer[63]) & 0x1) ? ATA_MODES_DMA0 : 0) | + ((CF_LE_W(buffer[63]) & 0x2) ? + ATA_MODES_DMA0 | ATA_MODES_DMA1 : 0) | + ((CF_LE_W(buffer[63]) & 0x4) ? + ATA_MODES_DMA0 | ATA_MODES_DMA1 | ATA_MODES_DMA2 : 0); + + if (ATA_DEV_INFO(ctrl_minor, dev).modes_avaible == 0) + continue; + + /* + * choose most appropriate ATA device data I/O speed supported by + * the controller + */ + status = ide_controller_config_io_speed( + ctrl_minor, + ATA_DEV_INFO(ctrl_minor, dev).modes_avaible); + if (status != RTEMS_SUCCESSFUL) + continue; + +#ifdef ATA_DEV_IS_FLASH_DISK + ATA_DEV_INFO(ctrl_minor, dev).mode_active = ATA_MODES_PIO3; +#endif + /* + * Ok, let register new ATA device in the system + */ + ata_devs[ata_devs_number].ctrl_minor = ctrl_minor; + ata_devs[ata_devs_number].device = dev; + + snprintf(name, ATA_MAX_NAME_LENGTH, "/dev/hd%c", + 'a' + 2 * ctrl_minor + dev); + device = rtems_filesystem_make_dev_t( + major, + (ata_devs_number * + ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE)); + + status = rtems_disk_create_phys(device, ATA_SECTOR_SIZE, + ATA_DEV_INFO(ctrl_minor, dev).lba_avaible ? + ATA_DEV_INFO(ctrl_minor, dev).lba_sectors : + (ATA_DEV_INFO(ctrl_minor, dev).heads * + ATA_DEV_INFO(ctrl_minor, dev).cylinders * + ATA_DEV_INFO(ctrl_minor, dev).sectors), + (block_device_ioctl) ata_ioctl, name); + if (status != RTEMS_SUCCESSFUL) + { + ata_devs[ata_devs_number].device = ATA_UNDEFINED_VALUE; + continue; + } + ata_devs_number++; + } + if (IDE_Controller_Table[ctrl_minor].int_driven == TRUE) + { + ide_controller_write_register(ctrl_minor, + IDE_REGISTER_DEVICE_CONTROL_OFFSET, + 0x00); + } + } + + free(buffer); + ata_initialized = TRUE; + return RTEMS_SUCCESSFUL; +} + +/* ata_process_request_on_init_phase -- + * Process the ATA request during system initialization. Request + * processing is syncronous and doesn't use multiprocessing enviroment. + * + * PARAMETERS: + * ctrl_minor - controller identifier + * areq - ATA request + * + * RETURNS: + * NONE + */ +static void +ata_process_request_on_init_phase(rtems_device_minor_number ctrl_minor, + ata_req_t *areq) +{ + unsigned16 byte;/* emphasize that only 8 low bits is meaningful */ + unsigned8 i, dev; + unsigned16 val, val1; + unsigned16 data_bs; /* the number of 512 bytes sectors into one + * data block + */ + assert(areq); + + dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] & + IDE_REGISTER_DEVICE_HEAD_DEV; + + data_bs = ATA_DEV_INFO(ctrl_minor, dev).current_multiple ? + ATA_DEV_INFO(ctrl_minor, dev).current_multiple : 1; + + ide_controller_write_register(ctrl_minor, IDE_REGISTER_DEVICE_HEAD, + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD]); + + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &byte); + } while ((byte & IDE_REGISTER_STATUS_BSY) || + (!(byte & IDE_REGISTER_STATUS_DRDY))); + + for (i=0; i< ATA_MAX_CMD_REG_OFFSET; i++) + { + unsigned32 reg = (1 << i); + if (areq->regs.to_write & reg) + ide_controller_write_register(ctrl_minor, i, + areq->regs.regs[i]); + } + + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &byte); + } while (byte & IDE_REGISTER_STATUS_BSY); + + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &val); + ide_controller_read_register(ctrl_minor, IDE_REGISTER_ERROR, &val1); + + if (val & IDE_REGISTER_STATUS_ERR) + { + areq->breq->status = RTEMS_UNSATISFIED; + areq->breq->error = RTEMS_IO_ERROR; + return; + } + + switch(areq->type) + { + case ATA_COMMAND_TYPE_PIO_IN: + ide_controller_read_data_block( + ctrl_minor, + MIN(data_bs, areq->cnt) * ATA_SECTOR_SIZE, + areq->breq->bufs, &areq->cbuf, + &areq->pos); + + areq->cnt -= MIN(data_bs, areq->cnt); + if (areq->cnt == 0) + { + areq->breq->status = RTEMS_SUCCESSFUL; + } + else + { + /* + * this shouldn't happend on the initialization + * phase! + */ + rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR); + } + break; + + case ATA_COMMAND_TYPE_NON_DATA: + areq->breq->status = RTEMS_SUCCESSFUL; + areq->breq->error = val1; + break; + + default: + printf("ata_queue_task: non-supported command type\n"); + areq->breq->status = RTEMS_UNSATISFIED; + areq->breq->error = RTEMS_NOT_IMPLEMENTED; + break; + } +} diff --git a/cpukit/libblock/src/ide_part_table.c b/cpukit/libblock/src/ide_part_table.c new file mode 100644 index 0000000000..ec5460ff4c --- /dev/null +++ b/cpukit/libblock/src/ide_part_table.c @@ -0,0 +1,518 @@ +/***************************************************************************** + * + * ide_part_table.c + * + * The implementation of library supporting "MS-DOS-style" partition table + * + * + * Copyright (C) 2002 OKTET Ltd., St.-Petersburg, Russia + * + * Author: Konstantin Abramenko + * Alexander Kukuta + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * $Id$ + * + *****************************************************************************/ + +#include + + +/* + * get_sector -- + * gets sector from the disk + * + * PARAMETERS: + * dev - device number + * sector_num - number of sector to read + * sector - returned pointer to pointer to allocated + * sector_data_t structure + * + * RETURNS: + * RTEMS_SUCCESSFUL, if success; + * RTEMS_NO_MEMORY, if canot allocate memory for sector data; + * other error codes returned by rtems_bdbuf_read(). + * + * NOTES: + * get_sector() operates with device via bdbuf library, + * and does not support devices with sector size other than 512 bytes + */ +static rtems_status_code +get_sector(dev_t dev, unsigned32 sector_num, sector_data_t **sector) +{ + sector_data_t *s; + bdbuf_buffer *buf; + rtems_status_code rc; + + if (sector == NULL) + { + return RTEMS_INTERNAL_ERROR; + } + + s = (sector_data_t *) malloc(sizeof(sector_data_t) + RTEMS_IDE_SECTOR_SIZE); + if (s == NULL) + { + return RTEMS_NO_MEMORY; + } + + rc = rtems_bdbuf_read(dev, sector_num, &buf); + if (rc != RTEMS_SUCCESSFUL) + { + free(s); + return rc; + } + + memcpy(s->data, buf->buffer, RTEMS_IDE_SECTOR_SIZE); + s->sector_num = sector_num; + + *sector = s; + + rtems_bdbuf_release(buf); + + return RTEMS_SUCCESSFUL; +} + + +/* + * msdos_signature_check -- + * checks if the partition table sector has msdos signature + * + * PARAMETERS: + * sector - sector to check + * + * RETURNS: + * TRUE if sector has msdos signature, FALSE otherwise + */ +static rtems_boolean +msdos_signature_check (sector_data_t *sector) +{ + unsigned8 *p = sector->data + RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_OFFSET; + + return ((p[0] == RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_DATA1) && + (p[1] == RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_DATA2)); +} + + +/* + * is_extended -- + * checks if the partition type is extended + * + * PARAMETERS: + * type - type of partition to check + * + * RETURNS: + * TRUE if partition type is extended, FALSE otherwise + */ +static rtems_boolean +is_extended(unsigned8 type) +{ + return ((type == EXTENDED_PARTITION) || (type == LINUX_EXTENDED)); +} + + +/* + * data_to_part_desc -- + * parses raw partition table sector data + * to partition description structure + * + * PARAMETERS: + * data - raw partition table sector data + * new_part_desc - pointer to returned partition description structure + * + * RETURNS: + * RTEMS_SUCCESSFUL, if success; + * RTEMS_NO_MEMOTY, if cannot allocate memory for part_desc_t strucure; + * RTEMS_INTERNAL_ERROR, if other error occurs. + */ +static rtems_status_code +data_to_part_desc(unsigned8 *data, part_desc_t **new_part_desc) +{ + part_desc_t *part_desc; + unsigned32 temp; + + if (new_part_desc == NULL) + { + return RTEMS_INTERNAL_ERROR; + } + + *new_part_desc = NULL; + + if ((part_desc = calloc(1, sizeof(part_desc_t))) == NULL) + { + return RTEMS_NO_MEMORY; + } + + part_desc->bootable = *(data + RTEMS_IDE_PARTITION_BOOTABLE_OFFSET); + part_desc->sys_type = *(data + RTEMS_IDE_PARTITION_SYS_TYPE_OFFSET); + + /* read the offset start position and partition size in sectors */ + + /* due to incorrect data alignment one have to align data first */ + memcpy(&temp, data + RTEMS_IDE_PARTITION_START_OFFSET, sizeof(unsigned32)); + part_desc->start = LE_TO_CPU_U32(temp); + + memcpy(&temp, data + RTEMS_IDE_PARTITION_SIZE_OFFSET, sizeof(unsigned32)); + part_desc->size = LE_TO_CPU_U32(temp); + + if ((part_desc->sys_type == EMPTY_PARTITION) || + ((part_desc->size == 0) && (!is_extended(part_desc->sys_type)))) + { + /* empty partition */ + free(part_desc); + return RTEMS_SUCCESSFUL; + } + + *new_part_desc = part_desc; + + return RTEMS_SUCCESSFUL; +} + + +/* + * read_extended_partition -- + * recursively reads extended partition sector from the device + * and constructs the partition table tree + * + * PARAMETERS: + * start - start sector of primary extended partition, used for + * calculation of absolute partition sector address + * ext_part - description of extended partition to process + * + * RETURNS: + * RTEMS_SUCCESSFUL if success, + * RTEMS_NO_MEMOTY if cannot allocate memory for part_desc_t strucure, + * RTEMS_INTERNAL_ERROR if other error occurs. + */ +static rtems_status_code +read_extended_partition(unsigned32 start, part_desc_t *ext_part) +{ + int i; + dev_t dev; + sector_data_t *sector; + unsigned32 here; + unsigned8 *data; + part_desc_t *new_part_desc; + rtems_status_code rc; + + if ((ext_part == NULL) || (ext_part->disk_desc == NULL)) + { + return RTEMS_INTERNAL_ERROR; + } + + dev = ext_part->disk_desc->dev; + + /* get start sector of current extended partition */ + here = ext_part->start; + + /* get first extended partition sector */ + + rc = get_sector(dev, here, §or); + if (rc != RTEMS_SUCCESSFUL) + { + return rc; + } + + if (!msdos_signature_check(sector)) + { + return RTEMS_INTERNAL_ERROR; + } + + /* read and process up to 4 logical partition descriptors */ + + data = sector->data + RTEMS_IDE_PARTITION_TABLE_OFFSET; + + for (i = 0; i < RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER; i++) + { + /* if data_to_part_desc fails skip this partition + * and parse the next one + */ + rc = data_to_part_desc(data, &new_part_desc); + if (rc != RTEMS_SUCCESSFUL) + { + free(sector); + return rc; + } + + if (new_part_desc == NULL) + { + data += RTEMS_IDE_PARTITION_DESCRIPTOR_SIZE; + continue; + } + + ext_part->sub_part[i] = new_part_desc; + new_part_desc->ext_part = ext_part; + new_part_desc->disk_desc = ext_part->disk_desc; + + if (is_extended(new_part_desc->sys_type)) + { + new_part_desc->log_id = EMPTY_PARTITION; + new_part_desc->start += start; + read_extended_partition(start, new_part_desc); + } + else + { + disk_desc_t *disk_desc = new_part_desc->disk_desc; + disk_desc->partitions[disk_desc->last_log_id] = new_part_desc; + new_part_desc->log_id = ++disk_desc->last_log_id; + new_part_desc->start += here; + new_part_desc->end = new_part_desc->start + new_part_desc->size - 1; + } + data += RTEMS_IDE_PARTITION_DESCRIPTOR_SIZE; + } + + free(sector); + + return RTEMS_SUCCESSFUL; +} + + +/* + * read_mbr -- + * reads Master Boot Record (sector 0) of physical device and + * constructs disk description structure + * + * PARAMETERS: + * disk_desc - returned disc description structure + * + * RETURNS: + * RTEMS_SUCCESSFUL if success, + * RTEMS_INTERNAL_ERROR otherwise + */ +static rtems_status_code +read_mbr(disk_desc_t *disk_desc) +{ + int part_num; + sector_data_t *sector; + part_desc_t *part_desc; + unsigned8 *data; + rtems_status_code rc; + dev_t dev = disk_desc->dev; + + /* get MBR sector */ + rc = get_sector(dev, 0, §or); + if (rc != RTEMS_SUCCESSFUL) + { + return rc; + } + + /* check if the partition table structure is MS-DOS style */ + if (!msdos_signature_check(sector)) + { + return RTEMS_INTERNAL_ERROR; + } + + /* read and process 4 primary partition descriptors */ + + data = sector->data + RTEMS_IDE_PARTITION_TABLE_OFFSET; + + for (part_num = 0; + part_num < RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER; + part_num++) + { + rc = data_to_part_desc(data, &part_desc); + if (rc != RTEMS_SUCCESSFUL) + { + free(sector); + return rc; + } + + if (part_desc != NULL) + { + part_desc->log_id = part_num + 1; + part_desc->disk_desc = disk_desc; + part_desc->end = part_desc->start + part_desc->size - 1; + disk_desc->partitions[part_num] = part_desc; + } + else + { + disk_desc->partitions[part_num] = NULL; + } + + data += RTEMS_IDE_PARTITION_DESCRIPTOR_SIZE; + } + + free(sector); + + disk_desc->last_log_id = RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER; + + /* There cannot be more than one extended partition, + but we are to process each primary partition */ + for (part_num = 0; + part_num < RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER; + part_num++) + { + if (is_extended(disk_desc->partitions[part_num]->sys_type)) + { + read_extended_partition(disk_desc->partitions[part_num]->start, + disk_desc->partitions[part_num]); + } + } + + return RTEMS_SUCCESSFUL; +} + + +/* + * partition free -- + * frees partition description structure + * + * PARAMETERS: + * part_desc - returned disc description structure + * + * RETURNS: + * N/A + */ +static void +partition_free(part_desc_t *part_desc) +{ + int part_num; + + if (part_desc == NULL) + return; + + if (is_extended(part_desc->sys_type)) + { + for (part_num = 0; + part_num < RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER; + part_num++) + { + partition_free(part_desc->sub_part[part_num]); + } + } + + free(part_desc); +} + + +/* + * rtems_ide_part_table_free - frees disk descriptor structure + * + * PARAMETERS: + * disk_desc - disc descriptor structure to free + * + * RETURNS: + * N/A + */ +void +rtems_ide_part_table_free(disk_desc_t *disk_desc) +{ + int part_num; + + for (part_num = 0; part_num < RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER; part_num++) + { + partition_free(disk_desc->partitions[part_num]); + } + + free(disk_desc); +} + + +/* + * rtems_ide_part_table_get - reads partition table structure from the device + * and creates disk description structure + * + * PARAMETERS: + * dev_name - path to physical device in /dev filesystem + * disk_desc - returned disc description structure + * + * RETURNS: + * RTEMS_SUCCESSFUL if success, + * RTEMS_INTERNAL_ERROR otherwise + */ +rtems_status_code +rtems_ide_part_table_get(const char *dev_name, disk_desc_t *disk_desc) +{ + struct stat dev_stat; + rtems_status_code rc; + + rc = stat(dev_name, &dev_stat); + if (rc != RTEMS_SUCCESSFUL) + { + return RTEMS_INTERNAL_ERROR; + } + + strncpy (disk_desc->dev_name, dev_name, 15); + disk_desc->dev = dev_stat.st_dev; + disk_desc->sector_size = (dev_stat.st_blksize) ? dev_stat.st_blksize : + RTEMS_IDE_SECTOR_SIZE; + + rc = read_mbr(disk_desc); + + return rc; +} + + +/* + * rtems_ide_part_table_initialize - initializes logical devices + * on the physical IDE drive + * + * PARAMETERS: + * dev_name - path to physical device in /dev filesystem + * + * RETURNS: + * RTEMS_SUCCESSFUL if success, + * RTEMS_NO_MEMOTY if cannot have not enough memory, + * RTEMS_INTERNAL_ERROR if other error occurs. + */ +rtems_status_code +rtems_ide_part_table_initialize(char *dev_name) +{ + int part_num; + dev_t dev; + disk_desc_t *disk_desc; + rtems_device_major_number major; + rtems_device_minor_number minor; + rtems_status_code rc; + part_desc_t *part_desc; + + /* logical device name /dev/hdxyy */ + char name[RTEMS_IDE_PARTITION_DEV_NAME_LENGTH_MAX]; + + disk_desc = (disk_desc_t *) calloc(1, sizeof(disk_desc_t)); + if (disk_desc == NULL) + { + return RTEMS_NO_MEMORY; + } + + /* get partition table */ + rc = rtems_ide_part_table_get(dev_name, disk_desc); + if (rc != RTEMS_SUCCESSFUL) + { + return rc; + } + + /* To avoid device numbers conflicts we have to use for logic disk the same + * device major number as ATA device has, and minor number that equals to + * sum of logic disk partition number and the minor number of physical disk + */ + + rtems_filesystem_split_dev_t (disk_desc->dev, major, minor); + + /* create logical disks on the physical one */ + for (part_num = 0; part_num < disk_desc->last_log_id; part_num++) + { + sprintf(name, "%s%d", dev_name, part_num + 1); + dev = rtems_filesystem_make_dev_t(major, ++minor); + + part_desc = disk_desc->partitions[part_num]; + if (part_desc == NULL) + { + continue; + } + + rc = rtems_disk_create_log(dev, disk_desc->dev, part_desc->start, + part_desc->size, name); + if (rc != RTEMS_SUCCESSFUL) + { + printf("Cannot create device %s, error code %d\n", name, rc); + continue; + } + } + + rtems_ide_part_table_free(disk_desc); + + return RTEMS_SUCCESSFUL; +} -- cgit v1.2.3