📄 hw_ide.c
字号:
/* This file is part of the program psim. Copyright (C) 1996, Andrew Cagney <cagney@highland.com.au> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#ifndef _HW_IDE_C_#define _HW_IDE_C_#include "device_table.h"/* DEVICE ide - Integrated Disk Electronics DESCRIPTION This device models the primary/secondary <<ide>> controller described in the [CHRPIO] document. The controller has separate independant interrupt outputs for each <<ide>> bus. PROPERTIES reg = ... (required) The <<reg>> property is described in the document [CHRPIO]. ready-delay = <integer> (optional) If present, this specifies the time that the <<ide>> device takes to complete an I/O operation. disk@?/ide-byte-count = <integer> (optional) disk@?/ide-sector-count = <integer> (optional) disk@?/ide-head-count = <integer> (optional) The <<ide>> device checks each child (disk device) node to see if it has the above properties. If present, these values will be used to compute the <<LBA>> address in <<CHS>> addressing mode. EXAMPLES Enable tracing: | -t ide-device \ Attach the <<ide>> device to the <<pci>> bus at slot one. Specify legacy I/O addresses: | -o '/phb/ide@1/assigned-addresses \ | ni0,0,10,1f0 8 \ | ni0,0,14,3f8 8 \ | ni0,0,18,170 8 \ | ni0,0,1c,378 8 \ | ni0,0,20,200 8' \ | -o '/phb@0x80000000/ide@1/reg \ | 1 0 \ | i0,0,10,0 8 \ | i0,0,18,0 8 \ | i0,0,14,6 1 \ | i0,0,1c,6 1 \ | i0,0,20,0 8' \ Note: the fouth and fifth reg entries specify that the register is at an offset into the address specified by the base register (<<assigned-addresses>>); Apart from restrictions placed by the <<pci>> specification, no restrictions are placed on the number of base registers specified by the <<assigned-addresses>> property. Attach a <<disk>> to the primary and a <<cdrom>> to the secondary <<ide>> controller. | -o '/phb@0x80000000/ide@1/disk@0/file "zero' \ | -o '/phb@0x80000000/ide@1/cdrom@2/file "/dev/cdrom"' \ Connect the two interrupt outputs (a and b) to a <<glue>> device to allow testing of the interrupt port. In a real simulation they would be wired to the interrupt controller. | -o '/phb@0x80000000/glue@2/reg 2 0 ni0,0,0,0 8' \ | -o '/phb@0x80000000/ide@1 > a 0 /phb@0x80000000/glue@2' \ | -o '/phb@0x80000000/ide@1 > b 1 /phb@0x80000000/glue@2' BUGS While the DMA registers are present, DMA support has not yet been implemented. The number of supported commands is very limited. The standards documents appear to be vague on how to specify the <<unit-address>> of disk devices devices being attached to the <<ide>> controller. I've chosen to use integers with devices zero and one going to the primary controller while two and three are connected to the secondary controller. REFERENCES [CHRPIO] PowerPC(tm) Microprocessor Common Hardware Reference Platform: I/O Device Reference. http://chrp.apple.com/???. [SCHMIDT] The SCSI Bus and IDE Interface - Protocols, Applications and Programming. Friedhelm Schmidt (translated by Michael Schultz). ISBN 0-201-42284-0. Addison-Wesley Publishing Company. */ typedef enum _io_direction { is_read, is_write,} io_direction;enum { nr_ide_controllers = 2, nr_ide_drives_per_controller = 2, nr_fifo_entries = 8192,};enum { /* command register block - read */ ide_data_reg, ide_error_reg, /*ide_feature_reg*/ ide_sector_count_reg, ide_sector_number_reg, ide_cylinder_reg0, ide_cylinder_reg1, ide_drive_head_reg, ide_status_reg, /*ide_command_reg*/ /* command register block - write */ ide_feature_reg, /*ide_error_reg*/ ide_command_reg, /*ide_status_reg*/ /* control register block - read */ ide_alternate_status_reg, /*ide_control_reg*/ ide_control_reg, /*ide_alternate_status_reg*/ /* dma register block */ ide_dma_command_reg, ide_dma_unused_1_reg, ide_dma_status_reg, ide_dma_unused_3_reg, ide_dma_prd_table_address_reg0, ide_dma_prd_table_address_reg1, ide_dma_prd_table_address_reg2, ide_dma_prd_table_address_reg3, nr_ide_registers,};typedef enum _ide_states { idle_state, busy_loaded_state, busy_drained_state, busy_dma_state, busy_command_state, loading_state, draining_state,} ide_states;static const char *ide_state_name(ide_states state){ switch (state) { case idle_state: return "idle"; case busy_loaded_state: return "busy_loaded_state"; case busy_drained_state: return "busy_drained_state"; case busy_dma_state: return "busy_dma_state"; case busy_command_state: return "busy_command_state"; case loading_state: return "loading_state"; case draining_state: return "draining_state"; default: return "illegal-state"; }}typedef struct _ide_geometry { int head; int sector; int byte;} ide_geometry;typedef struct _ide_drive { int nr; device *device; ide_geometry geometry; ide_geometry default_geometry;} ide_drive;typedef struct _ide_controller { int nr; ide_states state; unsigned8 reg[nr_ide_registers]; unsigned8 fifo[nr_fifo_entries]; int fifo_pos; int fifo_size; ide_drive *current_drive; int current_byte; int current_transfer; ide_drive drive[nr_ide_drives_per_controller]; device *me; event_entry_tag event_tag; int is_interrupting; signed64 ready_delay;} ide_controller;static voidset_interrupt(device *me, ide_controller *controller){ if ((controller->reg[ide_control_reg] & 0x2) == 0) { DTRACE(ide, ("controller %d - interrupt set\n", controller->nr)); device_interrupt_event(me, controller->nr, 1, NULL, 0); controller->is_interrupting = 1; }}static voidclear_interrupt(device *me, ide_controller *controller){ if (controller->is_interrupting) { DTRACE(ide, ("controller %d - interrupt clear\n", controller->nr)); device_interrupt_event(me, controller->nr, 0, NULL, 0); controller->is_interrupting = 0; }}static voiddo_event(void *data){ ide_controller *controller = data; device *me = controller->me; controller->event_tag = 0; switch (controller->state) { case busy_loaded_state: case busy_drained_state: if (controller->current_transfer > 0) { controller->state = (controller->state == busy_loaded_state ? loading_state : draining_state); } else { controller->state = idle_state; } set_interrupt(me, controller); break; default: device_error(me, "controller %d - unexpected event", controller->nr); break; }}static voidschedule_ready_event(device *me, ide_controller *controller){ if (controller->event_tag != 0) device_error(me, "controller %d - attempting to schedule multiple events", controller->nr); controller->event_tag = device_event_queue_schedule(me, controller->ready_delay, do_event, controller);}static voiddo_fifo_read(device *me, ide_controller *controller, void *dest, int nr_bytes){ if (controller->state != draining_state) device_error(me, "controller %d - reading fifo when not ready (%s)", controller->nr, ide_state_name(controller->state)); if (controller->fifo_pos + nr_bytes > controller->fifo_size) device_error(me, "controller %d - fifo underflow", controller->nr); if (nr_bytes > 0) { memcpy(dest, &controller->fifo[controller->fifo_pos], nr_bytes); controller->fifo_pos += nr_bytes; } if (controller->fifo_pos == controller->fifo_size) { controller->current_transfer -= 1; if (controller->current_transfer > 0 && controller->current_drive != NULL) { DTRACE(ide, ("controller %d:%d - reading %d byte block at 0x%x\n", controller->nr, controller->current_drive->nr, controller->fifo_size, controller->current_byte)); if (device_io_read_buffer(controller->current_drive->device, controller->fifo, 0, controller->current_byte, controller->fifo_size, NULL, 0) != controller->fifo_size) device_error(me, "controller %d - disk %s io read error", controller->nr, device_path(controller->current_drive->device)); } controller->state = busy_drained_state; controller->fifo_pos = 0; controller->current_byte += controller->fifo_size; schedule_ready_event(me, controller); }}static voiddo_fifo_write(device *me, ide_controller *controller, const void *source, int nr_bytes){ if (controller->state != loading_state) device_error(me, "controller %d - writing fifo when not ready (%s)", controller->nr, ide_state_name(controller->state)); if (controller->fifo_pos + nr_bytes > controller->fifo_size) device_error(me, "controller %d - fifo overflow", controller->nr); if (nr_bytes > 0) { memcpy(&controller->fifo[controller->fifo_pos], source, nr_bytes); controller->fifo_pos += nr_bytes; } if (controller->fifo_pos == controller->fifo_size) { if (controller->current_transfer > 0 && controller->current_drive != NULL) { DTRACE(ide, ("controller %d:%d - writing %d byte block at 0x%x\n", controller->nr, controller->current_drive->nr, controller->fifo_size, controller->current_byte)); if (device_io_write_buffer(controller->current_drive->device, controller->fifo, 0, controller->current_byte, controller->fifo_size, NULL, 0) != controller->fifo_size) device_error(me, "controller %d - disk %s io write error", controller->nr, device_path(controller->current_drive->device)); } controller->current_transfer -= 1; controller->fifo_pos = 0; controller->current_byte += controller->fifo_size; controller->state = busy_loaded_state; schedule_ready_event(me, controller); }}static voidsetup_fifo(device *me, ide_controller *controller, int is_simple, int is_with_disk, io_direction direction){ /* find the disk */ if (is_with_disk) { int drive_nr = (controller->reg[ide_drive_head_reg] & 0x10) != 0; controller->current_drive = &controller->drive[drive_nr]; } else { controller->current_drive = NULL; } /* number of transfers */ if (is_simple) controller->current_transfer = 1; else { int sector_count = controller->reg[ide_sector_count_reg]; if (sector_count == 0) controller->current_transfer = 256; else controller->current_transfer = sector_count; } /* the transfer size */ if (controller->current_drive == NULL) controller->fifo_size = 512; else controller->fifo_size = controller->current_drive->geometry.byte; /* empty the fifo */ controller->fifo_pos = 0; /* the starting address */ if (controller->current_drive == NULL) controller->current_byte = 0; else if (controller->reg[ide_drive_head_reg] & 0x40) { /* LBA addressing mode */ controller->current_byte = controller->fifo_size * (((controller->reg[ide_drive_head_reg] & 0xf) << 24) | (controller->reg[ide_cylinder_reg1] << 16)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -