📄 at_wini.c
字号:
/* This file contains the device dependent part of a driver for the IBM-AT * winchester controller. Written by Adri Koppes. * * The file contains one entry point: * * at_winchester_task: main entry when system is brought up * * Changes: * Aug 19, 2005 ATA PCI support, supports SATA (Ben Gras) * Nov 18, 2004 moved AT disk driver to user-space (Jorrit N. Herder) * Aug 20, 2004 watchdogs replaced by sync alarms (Jorrit N. Herder) * Mar 23, 2000 added ATAPI CDROM support (Michael Temari) * May 14, 2000 d-d/i rewrite (Kees J. Bot) * Apr 13, 1992 device dependent/independent split (Kees J. Bot) */#include "at_wini.h"#include "../libpci/pci.h"#include <minix/sysutil.h>#include <minix/keymap.h>#include <sys/ioc_disk.h>#define ATAPI_DEBUG 0 /* To debug ATAPI code. *//* I/O Ports used by winchester disk controllers. *//* Read and write registers */#define REG_CMD_BASE0 0x1F0 /* command base register of controller 0 */#define REG_CMD_BASE1 0x170 /* command base register of controller 1 */#define REG_CTL_BASE0 0x3F6 /* control base register of controller 0 */#define REG_CTL_BASE1 0x376 /* control base register of controller 1 */#define REG_DATA 0 /* data register (offset from the base reg.) */#define REG_PRECOMP 1 /* start of write precompensation */#define REG_COUNT 2 /* sectors to transfer */#define REG_SECTOR 3 /* sector number */#define REG_CYL_LO 4 /* low byte of cylinder number */#define REG_CYL_HI 5 /* high byte of cylinder number */#define REG_LDH 6 /* lba, drive and head */#define LDH_DEFAULT 0xA0 /* ECC enable, 512 bytes per sector */#define LDH_LBA 0x40 /* Use LBA addressing */#define ldh_init(drive) (LDH_DEFAULT | ((drive) << 4))/* Read only registers */#define REG_STATUS 7 /* status */#define STATUS_BSY 0x80 /* controller busy */#define STATUS_RDY 0x40 /* drive ready */#define STATUS_WF 0x20 /* write fault */#define STATUS_SC 0x10 /* seek complete (obsolete) */#define STATUS_DRQ 0x08 /* data transfer request */#define STATUS_CRD 0x04 /* corrected data */#define STATUS_IDX 0x02 /* index pulse */#define STATUS_ERR 0x01 /* error */#define STATUS_ADMBSY 0x100 /* administratively busy (software) */#define REG_ERROR 1 /* error code */#define ERROR_BB 0x80 /* bad block */#define ERROR_ECC 0x40 /* bad ecc bytes */#define ERROR_ID 0x10 /* id not found */#define ERROR_AC 0x04 /* aborted command */#define ERROR_TK 0x02 /* track zero error */#define ERROR_DM 0x01 /* no data address mark *//* Write only registers */#define REG_COMMAND 7 /* command */#define CMD_IDLE 0x00 /* for w_command: drive idle */#define CMD_RECALIBRATE 0x10 /* recalibrate drive */#define CMD_READ 0x20 /* read data */#define CMD_READ_EXT 0x24 /* read data (LBA48 addressed) */#define CMD_WRITE 0x30 /* write data */#define CMD_WRITE_EXT 0x34 /* write data (LBA48 addressed) */#define CMD_READVERIFY 0x40 /* read verify */#define CMD_FORMAT 0x50 /* format track */#define CMD_SEEK 0x70 /* seek cylinder */#define CMD_DIAG 0x90 /* execute device diagnostics */#define CMD_SPECIFY 0x91 /* specify parameters */#define ATA_IDENTIFY 0xEC /* identify drive *//* #define REG_CTL 0x206 */ /* control register */#define REG_CTL 0 /* control register */#define CTL_NORETRY 0x80 /* disable access retry */#define CTL_NOECC 0x40 /* disable ecc retry */#define CTL_EIGHTHEADS 0x08 /* more than eight heads */#define CTL_RESET 0x04 /* reset controller */#define CTL_INTDISABLE 0x02 /* disable interrupts */#if ENABLE_ATAPI#define ERROR_SENSE 0xF0 /* sense key mask */#define SENSE_NONE 0x00 /* no sense key */#define SENSE_RECERR 0x10 /* recovered error */#define SENSE_NOTRDY 0x20 /* not ready */#define SENSE_MEDERR 0x30 /* medium error */#define SENSE_HRDERR 0x40 /* hardware error */#define SENSE_ILRQST 0x50 /* illegal request */#define SENSE_UATTN 0x60 /* unit attention */#define SENSE_DPROT 0x70 /* data protect */#define SENSE_ABRT 0xb0 /* aborted command */#define SENSE_MISCOM 0xe0 /* miscompare */#define ERROR_MCR 0x08 /* media change requested */#define ERROR_ABRT 0x04 /* aborted command */#define ERROR_EOM 0x02 /* end of media detected */#define ERROR_ILI 0x01 /* illegal length indication */#define REG_FEAT 1 /* features */#define FEAT_OVERLAP 0x02 /* overlap */#define FEAT_DMA 0x01 /* dma */#define REG_IRR 2 /* interrupt reason register */#define IRR_REL 0x04 /* release */#define IRR_IO 0x02 /* direction for xfer */#define IRR_COD 0x01 /* command or data */#define REG_SAMTAG 3#define REG_CNT_LO 4 /* low byte of cylinder number */#define REG_CNT_HI 5 /* high byte of cylinder number */#define REG_DRIVE 6 /* drive select */#endif#define REG_STATUS 7 /* status */#define STATUS_BSY 0x80 /* controller busy */#define STATUS_DRDY 0x40 /* drive ready */#define STATUS_DMADF 0x20 /* dma ready/drive fault */#define STATUS_SRVCDSC 0x10 /* service or dsc */#define STATUS_DRQ 0x08 /* data transfer request */#define STATUS_CORR 0x04 /* correctable error occurred */#define STATUS_CHECK 0x01 /* check error */#ifdef ENABLE_ATAPI#define ATAPI_PACKETCMD 0xA0 /* packet command */#define ATAPI_IDENTIFY 0xA1 /* identify drive */#define SCSI_READ10 0x28 /* read from disk */#define SCSI_SENSE 0x03 /* sense request */#define CD_SECTOR_SIZE 2048 /* sector size of a CD-ROM */#endif /* ATAPI *//* Interrupt request lines. */#define NO_IRQ 0 /* no IRQ set yet */#define ATAPI_PACKETSIZE 12#define SENSE_PACKETSIZE 18/* Common command block */struct command { u8_t precomp; /* REG_PRECOMP, etc. */ u8_t count; u8_t sector; u8_t cyl_lo; u8_t cyl_hi; u8_t ldh; u8_t command;};/* Error codes */#define ERR (-1) /* general error */#define ERR_BAD_SECTOR (-2) /* block marked bad detected *//* Some controllers don't interrupt, the clock will wake us up. */#define WAKEUP (32*HZ) /* drive may be out for 31 seconds max *//* Miscellaneous. */#define MAX_DRIVES 8#define COMPAT_DRIVES 4#if _WORD_SIZE > 2#define MAX_SECS 256 /* controller can transfer this many sectors */#else#define MAX_SECS 127 /* but not to a 16 bit process */#endif#define MAX_ERRORS 4 /* how often to try rd/wt before quitting */#define NR_MINORS (MAX_DRIVES * DEV_PER_DRIVE)#define SUB_PER_DRIVE (NR_PARTITIONS * NR_PARTITIONS)#define NR_SUBDEVS (MAX_DRIVES * SUB_PER_DRIVE)#define DELAY_USECS 1000 /* controller timeout in microseconds */#define DELAY_TICKS 1 /* controller timeout in ticks */#define DEF_TIMEOUT_TICKS 300 /* controller timeout in ticks */#define RECOVERY_USECS 500000 /* controller recovery time in microseconds */#define RECOVERY_TICKS 30 /* controller recovery time in ticks */#define INITIALIZED 0x01 /* drive is initialized */#define DEAF 0x02 /* controller must be reset */#define SMART 0x04 /* drive supports ATA commands */#if ENABLE_ATAPI#define ATAPI 0x08 /* it is an ATAPI device */#else#define ATAPI 0 /* don't bother with ATAPI; optimise out */#endif#define IDENTIFIED 0x10 /* w_identify done successfully */#define IGNORING 0x20 /* w_identify failed once *//* Timeouts and max retries. */int timeout_ticks = DEF_TIMEOUT_TICKS, max_errors = MAX_ERRORS;int wakeup_ticks = WAKEUP;long w_standard_timeouts = 0, w_pci_debug = 0, w_instance = 0, w_lba48 = 0, atapi_debug = 0;int w_testing = 0, w_silent = 0;int w_next_drive = 0;/* Variables. *//* The struct wini is indexed by controller first, then drive (0-3). * Controller 0 is always the 'compatability' ide controller, at * the fixed locations, whether present or not. */PRIVATE struct wini { /* main drive struct, one entry per drive */ unsigned state; /* drive state: deaf, initialized, dead */ unsigned w_status; /* device status register */ unsigned base_cmd; /* command base register */ unsigned base_ctl; /* control base register */ unsigned irq; /* interrupt request line */ unsigned irq_mask; /* 1 << irq */ unsigned irq_need_ack; /* irq needs to be acknowledged */ int irq_hook_id; /* id of irq hook at the kernel */ int lba48; /* supports lba48 */ unsigned lcylinders; /* logical number of cylinders (BIOS) */ unsigned lheads; /* logical number of heads */ unsigned lsectors; /* logical number of sectors per track */ unsigned pcylinders; /* physical number of cylinders (translated) */ unsigned pheads; /* physical number of heads */ unsigned psectors; /* physical number of sectors per track */ unsigned ldhpref; /* top four bytes of the LDH (head) register */ unsigned precomp; /* write precompensation cylinder / 4 */ unsigned max_count; /* max request for this drive */ unsigned open_ct; /* in-use count */ struct device part[DEV_PER_DRIVE]; /* disks and partitions */ struct device subpart[SUB_PER_DRIVE]; /* subpartitions */} wini[MAX_DRIVES], *w_wn;PRIVATE int w_device = -1;PRIVATE int w_controller = -1;PRIVATE int w_major = -1;PRIVATE char w_id_string[40];PRIVATE int win_tasknr; /* my task number */PRIVATE int w_command; /* current command in execution */PRIVATE u8_t w_byteval; /* used for SYS_IRQCTL */PRIVATE int w_drive; /* selected drive */PRIVATE int w_controller; /* selected controller */PRIVATE struct device *w_dv; /* device's base and size */FORWARD _PROTOTYPE( void init_params, (void) );FORWARD _PROTOTYPE( void init_drive, (struct wini *, int, int, int, int, int, int));FORWARD _PROTOTYPE( void init_params_pci, (int) );FORWARD _PROTOTYPE( int w_do_open, (struct driver *dp, message *m_ptr) );FORWARD _PROTOTYPE( struct device *w_prepare, (int dev) );FORWARD _PROTOTYPE( int w_identify, (void) );FORWARD _PROTOTYPE( char *w_name, (void) );FORWARD _PROTOTYPE( int w_specify, (void) );FORWARD _PROTOTYPE( int w_io_test, (void) );FORWARD _PROTOTYPE( int w_transfer, (int proc_nr, int opcode, off_t position, iovec_t *iov, unsigned nr_req) );FORWARD _PROTOTYPE( int com_out, (struct command *cmd) );FORWARD _PROTOTYPE( void w_need_reset, (void) );FORWARD _PROTOTYPE( void ack_irqs, (unsigned int) );FORWARD _PROTOTYPE( int w_do_close, (struct driver *dp, message *m_ptr) );FORWARD _PROTOTYPE( int w_other, (struct driver *dp, message *m_ptr) );FORWARD _PROTOTYPE( int w_hw_int, (struct driver *dp, message *m_ptr) );FORWARD _PROTOTYPE( int com_simple, (struct command *cmd) );FORWARD _PROTOTYPE( void w_timeout, (void) );FORWARD _PROTOTYPE( int w_reset, (void) );FORWARD _PROTOTYPE( void w_intr_wait, (void) );FORWARD _PROTOTYPE( int at_intr_wait, (void) );FORWARD _PROTOTYPE( int w_waitfor, (int mask, int value) );FORWARD _PROTOTYPE( void w_geometry, (struct partition *entry) );#if ENABLE_ATAPIFORWARD _PROTOTYPE( int atapi_sendpacket, (u8_t *packet, unsigned cnt) );FORWARD _PROTOTYPE( int atapi_intr_wait, (void) );FORWARD _PROTOTYPE( int atapi_open, (void) );FORWARD _PROTOTYPE( void atapi_close, (void) );FORWARD _PROTOTYPE( int atapi_transfer, (int proc_nr, int opcode, off_t position, iovec_t *iov, unsigned nr_req) );#endif/* Entry points to this driver. */PRIVATE struct driver w_dtab = { w_name, /* current device's name */ w_do_open, /* open or mount request, initialize device */ w_do_close, /* release device */ do_diocntl, /* get or set a partition's geometry */ w_prepare, /* prepare for I/O on a given minor device */ w_transfer, /* do the I/O */ nop_cleanup, /* nothing to clean up */ w_geometry, /* tell the geometry of the disk */ nop_signal, /* no cleanup needed on shutdown */ nop_alarm, /* ignore leftover alarms */ nop_cancel, /* ignore CANCELs */ nop_select, /* ignore selects */ w_other, /* catch-all for unrecognized commands and ioctls */ w_hw_int /* leftover hardware interrupts */};/*===========================================================================* * at_winchester_task * *===========================================================================*/PUBLIC int main(){/* Install signal handlers. Ask PM to transform signal into message. */ struct sigaction sa; sa.sa_handler = SIG_MESS; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGTERM,&sa,NULL)<0) panic("RS","sigaction failed", errno); /* Set special disk parameters then call the generic main loop. */ init_params(); signal(SIGTERM, SIG_IGN); driver_task(&w_dtab); return(OK);}/*===========================================================================* * init_params * *===========================================================================*/PRIVATE void init_params(){/* This routine is called at startup to initialize the drive parameters. */ u16_t parv[2]; unsigned int vector, size; int drive, nr_drives; struct wini *wn; u8_t params[16]; int s; /* Boot variables. */ env_parse("ata_std_timeout", "d", 0, &w_standard_timeouts, 0, 1); env_parse("ata_pci_debug", "d", 0, &w_pci_debug, 0, 1); env_parse("ata_instance", "d", 0, &w_instance, 0, 8); env_parse("ata_lba48", "d", 0, &w_lba48, 0, 1); env_parse("atapi_debug", "d", 0, &atapi_debug, 0, 1); if (w_instance == 0) { /* Get the number of drives from the BIOS data area */ if ((s=sys_vircopy(SELF, BIOS_SEG, NR_HD_DRIVES_ADDR, SELF, D, (vir_bytes) params, NR_HD_DRIVES_SIZE)) != OK) panic(w_name(), "Couldn't read BIOS", s); if ((nr_drives = params[0]) > 2) nr_drives = 2; for (drive = 0, wn = wini; drive < COMPAT_DRIVES; drive++, wn++) { if (drive < nr_drives) { /* Copy the BIOS parameter vector */ vector = (drive == 0) ? BIOS_HD0_PARAMS_ADDR:BIOS_HD1_PARAMS_ADDR; size = (drive == 0) ? BIOS_HD0_PARAMS_SIZE:BIOS_HD1_PARAMS_SIZE; if ((s=sys_vircopy(SELF, BIOS_SEG, vector, SELF, D, (vir_bytes) parv, size)) != OK) panic(w_name(), "Couldn't read BIOS", s); /* Calculate the address of the parameters and copy them */ if ((s=sys_vircopy( SELF, BIOS_SEG, hclick_to_physb(parv[1]) + parv[0], SELF, D, (phys_bytes) params, 16L))!=OK) panic(w_name(),"Couldn't copy parameters", s); /* Copy the parameters to the structures of the drive */ wn->lcylinders = bp_cylinders(params); wn->lheads = bp_heads(params); wn->lsectors = bp_sectors(params); wn->precomp = bp_precomp(params) >> 2; } /* Fill in non-BIOS parameters. */ init_drive(wn, drive < 2 ? REG_CMD_BASE0 : REG_CMD_BASE1, drive < 2 ? REG_CTL_BASE0 : REG_CTL_BASE1, NO_IRQ, 0, 0, drive); w_next_drive++; } } /* Look for controllers on the pci bus. Skip none the first instance, * skip one and then 2 for every instance, for every next instance. */ if (w_instance == 0) init_params_pci(0); else init_params_pci(w_instance*2-1);}#define ATA_IF_NOTCOMPAT1 (1L << 0)#define ATA_IF_NOTCOMPAT2 (1L << 2)/*===========================================================================* * init_drive * *===========================================================================*/PRIVATE void init_drive(struct wini *w, int base_cmd, int base_ctl, int irq, int ack, int hook, int drive){ w->state = 0; w->w_status = 0; w->base_cmd = base_cmd; w->base_ctl = base_ctl; w->irq = irq; w->irq_mask = 1 << irq; w->irq_need_ack = ack; w->irq_hook_id = hook; w->ldhpref = ldh_init(drive); w->max_count = MAX_SECS << SECTOR_SHIFT; w->lba48 = 0;}/*===========================================================================* * init_params_pci * *===========================================================================*/PRIVATE void init_params_pci(int skip){ int r, devind, drive; u16_t vid, did; pci_init(); for(drive = w_next_drive; drive < MAX_DRIVES; drive++) wini[drive].state = IGNORING; for(r = pci_first_dev(&devind, &vid, &did); r != 0 && w_next_drive < MAX_DRIVES; r = pci_next_dev(&devind, &vid, &did)) { int interface, irq, irq_hook; /* Base class must be 01h (mass storage), subclass must * be 01h (ATA). */ if (pci_attr_r8(devind, PCI_BCR) != 0x01 || pci_attr_r8(devind, PCI_SCR) != 0x01) { continue; } /* Found a controller. * Programming interface register tells us more. */ interface = pci_attr_r8(devind, PCI_PIFR); irq = pci_attr_r8(devind, PCI_ILR); /* Any non-compat drives? */ if (interface & (ATA_IF_NOTCOMPAT1 | ATA_IF_NOTCOMPAT2)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -