📄 ide-taskfile.c
字号:
/* * linux/drivers/ide/ide-taskfile.c Version 0.20 Oct 11, 2000 * * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org> * * May be copied or modified under the terms of the GNU General Public License * * IDE_DEBUG(__LINE__); */#include <linux/config.h>#define __NO_VERSION__#include <linux/module.h>#include <linux/types.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/major.h>#include <linux/errno.h>#include <linux/genhd.h>#include <linux/blkpg.h>#include <linux/slab.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/hdreg.h>#include <linux/ide.h>#include <asm/byteorder.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/bitops.h>#ifdef CONFIG_IDE_TASKFILE_IO# define __TASKFILE__IO#else /* CONFIG_IDE_TASKFILE_IO */# undef __TASKFILE__IO#endif /* CONFIG_IDE_TASKFILE_IO */#define DEBUG_TASKFILE 0 /* unset when fixed */#if DEBUG_TASKFILE#define DTF(x...) printk(##x)#else#define DTF(x...)#endifinline u32 task_read_24 (ide_drive_t *drive){ return (IN_BYTE(IDE_HCYL_REG)<<16) | (IN_BYTE(IDE_LCYL_REG)<<8) | IN_BYTE(IDE_SECTOR_REG);}static void ata_bswap_data (void *buffer, int wcount){ u16 *p = buffer; while (wcount--) { *p = *p << 8 | *p >> 8; p++; *p = *p << 8 | *p >> 8; p++; }}#if SUPPORT_VLB_SYNC/* * Some localbus EIDE interfaces require a special access sequence * when using 32-bit I/O instructions to transfer data. We call this * the "vlb_sync" sequence, which consists of three successive reads * of the sector count register location, with interrupts disabled * to ensure that the reads all happen together. */static inline void task_vlb_sync (ide_ioreg_t port) { (void) inb (port); (void) inb (port); (void) inb (port);}#endif /* SUPPORT_VLB_SYNC *//* * This is used for most PIO data transfers *from* the IDE interface */void ata_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount){ byte io_32bit = drive->io_32bit; if (io_32bit) {#if SUPPORT_VLB_SYNC if (io_32bit & 2) { unsigned long flags; __save_flags(flags); /* local CPU only */ __cli(); /* local CPU only */ task_vlb_sync(IDE_NSECTOR_REG); insl(IDE_DATA_REG, buffer, wcount); __restore_flags(flags); /* local CPU only */ } else#endif /* SUPPORT_VLB_SYNC */ insl(IDE_DATA_REG, buffer, wcount); } else {#if SUPPORT_SLOW_DATA_PORTS if (drive->slow) { unsigned short *ptr = (unsigned short *) buffer; while (wcount--) { *ptr++ = inw_p(IDE_DATA_REG); *ptr++ = inw_p(IDE_DATA_REG); } } else#endif /* SUPPORT_SLOW_DATA_PORTS */ insw(IDE_DATA_REG, buffer, wcount<<1); }}/* * This is used for most PIO data transfers *to* the IDE interface */void ata_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount){ byte io_32bit = drive->io_32bit; if (io_32bit) {#if SUPPORT_VLB_SYNC if (io_32bit & 2) { unsigned long flags; __save_flags(flags); /* local CPU only */ __cli(); /* local CPU only */ task_vlb_sync(IDE_NSECTOR_REG); outsl(IDE_DATA_REG, buffer, wcount); __restore_flags(flags); /* local CPU only */ } else#endif /* SUPPORT_VLB_SYNC */ outsl(IDE_DATA_REG, buffer, wcount); } else {#if SUPPORT_SLOW_DATA_PORTS if (drive->slow) { unsigned short *ptr = (unsigned short *) buffer; while (wcount--) { outw_p(*ptr++, IDE_DATA_REG); outw_p(*ptr++, IDE_DATA_REG); } } else#endif /* SUPPORT_SLOW_DATA_PORTS */ outsw(IDE_DATA_REG, buffer, wcount<<1); }}static inline void taskfile_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount){ ata_input_data(drive, buffer, wcount); if (drive->bswap) ata_bswap_data(buffer, wcount);}static inline void taskfile_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount){ if (drive->bswap) { ata_bswap_data(buffer, wcount); ata_output_data(drive, buffer, wcount); ata_bswap_data(buffer, wcount); } else { ata_output_data(drive, buffer, wcount); }}ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task){ task_struct_t *taskfile = (task_struct_t *) task->tfRegister; hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister; struct hd_driveid *id = drive->id; byte HIHI = (drive->addressing) ? 0xE0 : 0xEF; /* (ks/hs): Moved to start, do not use for multiple out commands */ if (task->handler != task_mulout_intr) { if (IDE_CONTROL_REG) OUT_BYTE(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ SELECT_MASK(HWIF(drive), drive, 0); } if ((id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400) && (drive->addressing == 1)) { OUT_BYTE(hobfile->feature, IDE_FEATURE_REG); OUT_BYTE(hobfile->sector_count, IDE_NSECTOR_REG); OUT_BYTE(hobfile->sector_number, IDE_SECTOR_REG); OUT_BYTE(hobfile->low_cylinder, IDE_LCYL_REG); OUT_BYTE(hobfile->high_cylinder, IDE_HCYL_REG); } OUT_BYTE(taskfile->feature, IDE_FEATURE_REG); OUT_BYTE(taskfile->sector_count, IDE_NSECTOR_REG); /* refers to number of sectors to transfer */ OUT_BYTE(taskfile->sector_number, IDE_SECTOR_REG); /* refers to sector offset or start sector */ OUT_BYTE(taskfile->low_cylinder, IDE_LCYL_REG); OUT_BYTE(taskfile->high_cylinder, IDE_HCYL_REG); OUT_BYTE((taskfile->device_head & HIHI) | drive->select.all, IDE_SELECT_REG); if (task->handler != NULL) {#if 0 ide_set_handler (drive, task->handler, WAIT_CMD, NULL); OUT_BYTE(taskfile->command, IDE_COMMAND_REG); /* * warning check for race between handler and prehandler for * writing first block of data. however since we are well * inside the boundaries of the seek, we should be okay. */ if (task->prehandler != NULL) { return task->prehandler(drive, task->rq); }#else ide_startstop_t startstop; ide_set_handler (drive, task->handler, WAIT_CMD, NULL); OUT_BYTE(taskfile->command, IDE_COMMAND_REG); if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name, drive->mult_count ? "MULTWRITE" : "WRITE"); return startstop; } /* (ks/hs): Fixed Multi Write */ if ((taskfile->command != WIN_MULTWRITE) && (taskfile->command != WIN_MULTWRITE_EXT)) { struct request *rq = HWGROUP(drive)->rq; /* For Write_sectors we need to stuff the first sector */ taskfile_output_data(drive, rq->buffer, SECTOR_WORDS); rq->current_nr_sectors--; } else { /* Stuff first sector(s) by implicitly calling the handler */ if (!(drive_is_ready(drive))) { /* FIXME: Replace hard-coded 100, error handling? */ int i; for (i=0; i<100; i++) { if (drive_is_ready(drive)) break; } } return task->handler(drive); }#endif } else { /* for dma commands we down set the handler */ if (drive->using_dma && !(HWIF(drive)->dmaproc(((taskfile->command == WIN_WRITEDMA) || (taskfile->command == WIN_WRITEDMA_EXT)) ? ide_dma_write : ide_dma_read, drive))); } return ide_started;}void do_taskfile (ide_drive_t *drive, struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile, ide_handler_t *handler){ struct hd_driveid *id = drive->id; byte HIHI = (drive->addressing) ? 0xE0 : 0xEF; /* (ks/hs): Moved to start, do not use for multiple out commands */ if (*handler != task_mulout_intr) { if (IDE_CONTROL_REG) OUT_BYTE(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ SELECT_MASK(HWIF(drive), drive, 0); } if ((id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400) && (drive->addressing == 1)) { OUT_BYTE(hobfile->feature, IDE_FEATURE_REG); OUT_BYTE(hobfile->sector_count, IDE_NSECTOR_REG); OUT_BYTE(hobfile->sector_number, IDE_SECTOR_REG); OUT_BYTE(hobfile->low_cylinder, IDE_LCYL_REG); OUT_BYTE(hobfile->high_cylinder, IDE_HCYL_REG); } OUT_BYTE(taskfile->feature, IDE_FEATURE_REG); OUT_BYTE(taskfile->sector_count, IDE_NSECTOR_REG); /* refers to number of sectors to transfer */ OUT_BYTE(taskfile->sector_number, IDE_SECTOR_REG); /* refers to sector offset or start sector */ OUT_BYTE(taskfile->low_cylinder, IDE_LCYL_REG); OUT_BYTE(taskfile->high_cylinder, IDE_HCYL_REG); OUT_BYTE((taskfile->device_head & HIHI) | drive->select.all, IDE_SELECT_REG); if (handler != NULL) { ide_set_handler (drive, handler, WAIT_CMD, NULL); OUT_BYTE(taskfile->command, IDE_COMMAND_REG); } else { /* for dma commands we down set the handler */ if (drive->using_dma && !(HWIF(drive)->dmaproc(((taskfile->command == WIN_WRITEDMA) || (taskfile->command == WIN_WRITEDMA_EXT)) ? ide_dma_write : ide_dma_read, drive))); }}#if 0ide_startstop_t flagged_taskfile (ide_drive_t *drive, ide_task_t *task){ task_struct_t *taskfile = (task_struct_t *) task->tfRegister; hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister; struct hd_driveid *id = drive->id; /* * (KS) Check taskfile in/out flags. * If set, then execute as it is defined. * If not set, then define default settings. * The default values are: * write and read all taskfile registers (except data) * write and read the hob registers (sector,nsector,lcyl,hcyl) */ if (task->tf_out_flags.all == 0) { task->tf_out_flags.all = IDE_TASKFILE_STD_OUT_FLAGS; if ((id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400) && (drive->addressing == 1)) { task->tf_out_flags.all != (IDE_HOB_STD_OUT_FLAGS << 8); } } if (task->tf_in_flags.all == 0) { task->tf_in_flags.all = IDE_TASKFILE_STD_IN_FLAGS; if ((id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400) && (drive->addressing == 1)) { task->tf_in_flags.all != (IDE_HOB_STD_IN_FLAGS << 8); } } if (IDE_CONTROL_REG) OUT_BYTE(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ SELECT_MASK(HWIF(drive), drive, 0); if (task->tf_out_flags.b.data) { unsigned short data = taskfile->data + (hobfile->data << 8); OUT_WORD (data, IDE_DATA_REG); } /* (KS) send hob registers first */ if (task->tf_out_flags.b.nsector_hob) OUT_BYTE(hobfile->sector_count, IDE_NSECTOR_REG); if (task->tf_out_flags.b.sector_hob) OUT_BYTE(hobfile->sector_number, IDE_SECTOR_REG); if (task->tf_out_flags.b.lcyl_hob) OUT_BYTE(hobfile->low_cylinder, IDE_LCYL_REG); if (task->tf_out_flags.b.hcyl_hob) OUT_BYTE(hobfile->high_cylinder, IDE_HCYL_REG); /* (KS) Send now the standard registers */ if (task->tf_out_flags.b.error_feature) OUT_BYTE(taskfile->feature, IDE_FEATURE_REG); /* refers to number of sectors to transfer */ if (task->tf_out_flags.b.nsector) OUT_BYTE(taskfile->sector_count, IDE_NSECTOR_REG); /* refers to sector offset or start sector */ if (task->tf_out_flags.b.sector) OUT_BYTE(taskfile->sector_number, IDE_SECTOR_REG); if (task->tf_out_flags.b.lcyl) OUT_BYTE(taskfile->low_cylinder, IDE_LCYL_REG); if (task->tf_out_flags.b.hcyl) OUT_BYTE(taskfile->high_cylinder, IDE_HCYL_REG); /* * (KS) Do not modify the specified taskfile. We want to have a * universal pass through, so we must execute ALL specified values. * * (KS) The drive head register is mandatory. * Don't care about the out flags ! */ OUT_BYTE(taskfile->device_head | drive->select.all, IDE_SELECT_REG); if (task->handler != NULL) {#if 0 ide_set_handler (drive, task->handler, WAIT_CMD, NULL); OUT_BYTE(taskfile->command, IDE_COMMAND_REG); /* * warning check for race between handler and prehandler for * writing first block of data. however since we are well * inside the boundaries of the seek, we should be okay. */ if (task->prehandler != NULL) { return task->prehandler(drive, task->rq); }#else ide_startstop_t startstop; ide_set_handler (drive, task->handler, WAIT_CMD, NULL); /* * (KS) The drive command register is also mandatory. * Don't care about the out flags ! */ OUT_BYTE(taskfile->command, IDE_COMMAND_REG); if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name, drive->mult_count ? "MULTWRITE" : "WRITE"); return startstop; } /* (ks/hs): Fixed Multi Write */ if ((taskfile->command != WIN_MULTWRITE) && (taskfile->command != WIN_MULTWRITE_EXT)) { struct request *rq = HWGROUP(drive)->rq; /* For Write_sectors we need to stuff the first sector */ taskfile_output_data(drive, rq->buffer, SECTOR_WORDS); rq->current_nr_sectors--; } else { /* Stuff first sector(s) by implicitly calling the handler */ if (!(drive_is_ready(drive))) { /* FIXME: Replace hard-coded 100, error handling? */ int i; for (i=0; i<100; i++) { if (drive_is_ready(drive)) break; } } return task->handler(drive); }#endif } else { /* for dma commands we down set the handler */ if (drive->using_dma && !(HWIF(drive)->dmaproc(((taskfile->command == WIN_WRITEDMA) || (taskfile->command == WIN_WRITEDMA_EXT)) ? ide_dma_write : ide_dma_read, drive))); } return ide_started;}#endif#if 0/* * Error reporting, in human readable form (luxurious, but a memory hog). */byte taskfile_dump_status (ide_drive_t *drive, const char *msg, byte stat){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -