📄 ps2esdi.c
字号:
/* some annoyance in the above routine returns TWO drives? Is something else happining in the background? Regaurdless we fix the # of drives again. AJK */ /* Integrated ESDI Disk and Controller has only one drive! */ if (adapterID == INTG_ESDI_ID) /* if not "normal" PS2 ESDI adapter */ ps2esdi_drives = 1; /* Not three or two, ONE DAMNIT! */ current_int_handler = ps2esdi_normal_interrupt_handler; ps2esdi_gendisk.nr_real = ps2esdi_drives; for (i = 0; i < (MAX_HD << 6); i++) ps2esdi_blocksizes[i] = 1024; request_dma(dma_arb_level, "ed"); request_region(io_base, 4, "ed"); blksize_size[MAJOR_NR] = ps2esdi_blocksizes; for (i = 0; i < ps2esdi_drives; i++) { register_disk(&ps2esdi_gendisk,MKDEV(MAJOR_NR,i<<6),1<<6, &ps2esdi_fops, ps2esdi_info[i].head * ps2esdi_info[i].sect * ps2esdi_info[i].cyl); ps2esdi_valid[i] = 1; }}static void __init ps2esdi_get_device_cfg(void){ u_short cmd_blk[TYPE_0_CMD_BLK_LENGTH]; /*BA */ printk("%s: Drive 0\n", DEVICE_NAME); current_int_handler = ps2esdi_geometry_int_handler; cmd_blk[0] = CMD_GET_DEV_CONFIG | 0x600; cmd_blk[1] = 0; no_int_yet = TRUE; ps2esdi_out_cmd_blk(cmd_blk); if (no_int_yet) sleep_on(&ps2esdi_int); if (ps2esdi_drives > 1) { printk("%s: Drive 1\n", DEVICE_NAME); /*BA */ cmd_blk[0] = CMD_GET_DEV_CONFIG | (1 << 5) | 0x600; cmd_blk[1] = 0; no_int_yet = TRUE; ps2esdi_out_cmd_blk(cmd_blk); if (no_int_yet) sleep_on(&ps2esdi_int); } /* if second physical drive is present */ return;}/* strategy routine that handles most of the IO requests */static void do_ps2esdi_request(request_queue_t * q){ u_int block, count; /* since, this routine is called with interrupts cleared - they must be before it finishes */ sti();#if 0 printk("%s:got request. device : %d minor : %d command : %d sector : %ld count : %ld, buffer: %p\n", DEVICE_NAME, CURRENT_DEV, MINOR(CURRENT->rq_dev), CURRENT->cmd, CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer);#endif /* standard macro that ensures that requests are really on the list + sanity checks. */ INIT_REQUEST; if (virt_to_bus(CURRENT->buffer + CURRENT->nr_sectors * 512) > 16 * MB) { printk("%s: DMA above 16MB not supported\n", DEVICE_NAME); end_request(FAIL); if (!QUEUE_EMPTY) do_ps2esdi_request(q); return; } /* check for above 16Mb dmas */ if ((CURRENT_DEV < ps2esdi_drives) && (CURRENT->sector + CURRENT->nr_sectors <= ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects)) {#if 0 printk("%s:got request. device : %d minor : %d command : %d sector : %ld count : %ld\n", DEVICE_NAME, CURRENT_DEV, MINOR(CURRENT->dev), CURRENT->cmd, CURRENT->sector, CURRENT->nr_sectors);#endif block = CURRENT->sector + ps2esdi[MINOR(CURRENT->rq_dev)].start_sect;#if 0 printk("%s: blocknumber : %d\n", DEVICE_NAME, block);#endif count = CURRENT->nr_sectors; switch (CURRENT->cmd) { case READ: ps2esdi_readwrite(READ, CURRENT_DEV, block, count); return; break; case WRITE: ps2esdi_readwrite(WRITE, CURRENT_DEV, block, count); return; break; default: printk("%s: Unknown command\n", DEVICE_NAME); end_request(FAIL); if (!QUEUE_EMPTY) do_ps2esdi_request(q); break; } /* handle different commands */ } /* is request is valid */ else { printk("Grrr. error. ps2esdi_drives: %d, %lu %lu\n", ps2esdi_drives, CURRENT->sector, ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects); end_request(FAIL); if (!QUEUE_EMPTY) do_ps2esdi_request(q); }} /* main strategy routine *//* resets the ESDI adapter */static void reset_ctrl(void){ u_long expire; u_short status; /* enable interrupts on the controller */ status = inb(ESDI_INTRPT); outb((status & 0xe0) | ATT_EOI, ESDI_ATTN); /* to be sure we don't have any interrupt pending... */ outb_p(CTRL_ENABLE_INTR, ESDI_CONTROL); /* read the ESDI status port - if the controller is not busy, simply do a soft reset (fast) - otherwise we'll have to do a hard (slow) reset. */ if (!(inb_p(ESDI_STATUS) & STATUS_BUSY)) { /*BA */ printk("%s: soft reset...\n", DEVICE_NAME); outb_p(CTRL_SOFT_RESET, ESDI_ATTN); } /* soft reset */ else { /*BA */ printk("%s: hard reset...\n", DEVICE_NAME); outb_p(CTRL_HARD_RESET, ESDI_CONTROL); expire = jiffies + 2*HZ; while (time_before(jiffies, expire)); outb_p(1, ESDI_CONTROL); } /* hard reset */} /* reset the controller *//* called by the strategy routine to handle read and write requests */static void ps2esdi_readwrite(int cmd, u_char drive, u_int block, u_int count){ u_short track, head, cylinder, sector; u_short cmd_blk[TYPE_1_CMD_BLK_LENGTH]; /* do some relevant arithmatic */ CURRENT->current_nr_sectors = (count < (2 * MAX_16BIT / SECT_SIZE)) ? count : (2 * MAX_16BIT / SECT_SIZE); track = block / ps2esdi_info[drive].sect; head = track % ps2esdi_info[drive].head; cylinder = track / ps2esdi_info[drive].head; sector = block % ps2esdi_info[drive].sect;#if 0 printk("%s: cyl=%d head=%d sect=%d\n", DEVICE_NAME, cylinder, head, sector);#endif /* call the routine that actually fills out a command block */ ps2esdi_fill_cmd_block (cmd_blk, (cmd == READ) ? CMD_READ : CMD_WRITE, cylinder, head, sector, CURRENT->current_nr_sectors, drive); /* send the command block to the controller */ if (ps2esdi_out_cmd_blk(cmd_blk)) { printk("%s: Controller failed\n", DEVICE_NAME); if ((++CURRENT->errors) < MAX_RETRIES) return do_ps2esdi_request(NULL); else { end_request(FAIL); if (!QUEUE_EMPTY) do_ps2esdi_request(NULL); } } /* check for failure to put out the command block */ else {#if 0 printk("%s: waiting for xfer\n", DEVICE_NAME);#endif /* turn disk lights on */ LITE_ON; }} /* ps2esdi_readwrite *//* fill out the command block */static void ps2esdi_fill_cmd_block(u_short * cmd_blk, u_short cmd, u_short cyl, u_short head, u_short sector, u_short length, u_char drive){ cmd_blk[0] = (drive << 5) | cmd; cmd_blk[1] = length; cmd_blk[2] = ((cyl & 0x1f) << 11) | (head << 5) | sector; cmd_blk[3] = (cyl & 0x3E0) >> 5;} /* fill out the command block *//* write a command block to the controller */static int ps2esdi_out_cmd_blk(u_short * cmd_blk){ int i, j; u_char status; /* enable interrupts */ outb(CTRL_ENABLE_INTR, ESDI_CONTROL); /* do not write to the controller, if it is busy */ for (i = jiffies + ESDI_STAT_TIMEOUT; time_after(i, jiffies) && (inb(ESDI_STATUS) & STATUS_BUSY););#if 0 printk("%s: i(1)=%d\n", DEVICE_NAME, i);#endif /* if device is still busy - then just time out */ if (inb(ESDI_STATUS) & STATUS_BUSY) { printk("%s: ps2esdi_out_cmd timed out (1)\n", DEVICE_NAME); return ERROR; } /* timeout ??? */ /* Set up the attention register in the controller */ outb(((*cmd_blk) & 0xE0) | 1, ESDI_ATTN);#if 0 printk("%s: sending %d words to controller\n", DEVICE_NAME, (((*cmd_blk) >> 14) + 1) << 1);#endif /* one by one send each word out */ for (i = (((*cmd_blk) >> 14) + 1) << 1; i; i--) { status = inb(ESDI_STATUS); for (j = jiffies + ESDI_STAT_TIMEOUT; time_after(j, jiffies) && (status & STATUS_BUSY) && (status & STATUS_CMD_INF); status = inb(ESDI_STATUS)); if ((status & (STATUS_BUSY | STATUS_CMD_INF)) == STATUS_BUSY) {#if 0 printk("%s: sending %04X\n", DEVICE_NAME, *cmd_blk);#endif outw(*cmd_blk++, ESDI_CMD_INT); } else { printk("%s: ps2esdi_out_cmd timed out while sending command (status=%02X)\n", DEVICE_NAME, status); return ERROR; } } /* send all words out */ return OK;} /* send out the commands *//* prepare for dma - do all the necessary setup */static void ps2esdi_prep_dma(char *buffer, u_short length, u_char dma_xmode){ u_int tc; buffer=(char *)virt_to_bus(buffer);#if 0 printk("ps2esdi: b_wait: %p\n", CURRENT->bh->b_wait);#endif cli(); outb(dma_arb_level | DMA_MASK_CHAN, PORT_DMA_FN); outb(dma_arb_level | DMA_WRITE_ADDR, PORT_DMA_FN); outb((u_int) buffer & (u_int) 0xff, PORT_DMA_EX); outb(((u_int) buffer >> 8) & (u_int) 0xff, PORT_DMA_EX); outb(((u_int) buffer >> 16) & (u_int) 0xff, PORT_DMA_EX); outb(dma_arb_level | DMA_WRITE_TC, PORT_DMA_FN); tc = (length * SECT_SIZE / 2) - 1; outb(tc & 0xff, PORT_DMA_EX); outb((tc >> 8) & 0xff, PORT_DMA_EX); outb(dma_arb_level | DMA_WRITE_MODE, PORT_DMA_FN); outb(dma_xmode, PORT_DMA_EX); outb(dma_arb_level | DMA_UNMASK_CHAN, PORT_DMA_FN); sti();} /* prepare for dma */static void ps2esdi_interrupt_handler(int irq, void *dev_id, struct pt_regs *regs){ u_int int_ret_code; if (inb(ESDI_STATUS) & STATUS_INTR) { int_ret_code = inb(ESDI_INTRPT); if (current_int_handler) { /* Disable adapter interrupts till processing is finished */ outb(CTRL_DISABLE_INTR, ESDI_CONTROL); current_int_handler(int_ret_code); } else printk("%s: help ! No interrupt handler.\n", DEVICE_NAME); } else { return; }}static void ps2esdi_initial_reset_int_handler(u_int int_ret_code){ switch (int_ret_code & 0xf) { case INT_RESET: /*BA */ printk("%s: initial reset completed.\n", DEVICE_NAME); outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); wake_up(&ps2esdi_int); break; case INT_ATTN_ERROR: printk("%s: Attention error. interrupt status : %02X\n", DEVICE_NAME, int_ret_code); printk("%s: status: %02x\n", DEVICE_NAME, inb(ESDI_STATUS)); break; default: printk("%s: initial reset handler received interrupt: %02X\n", DEVICE_NAME, int_ret_code); outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); break; } outb(CTRL_ENABLE_INTR, ESDI_CONTROL);}static void ps2esdi_geometry_int_handler(u_int int_ret_code){ u_int status, drive_num; unsigned long rba; int i; drive_num = int_ret_code >> 5; switch (int_ret_code & 0xf) { case INT_CMD_COMPLETE: for (i = ESDI_TIMEOUT; i & !(inb(ESDI_STATUS) & STATUS_STAT_AVAIL); i--); if (!(inb(ESDI_STATUS) & STATUS_STAT_AVAIL)) { printk("%s: timeout reading status word\n", DEVICE_NAME); outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); break; } status = inw(ESDI_STT_INT); if ((status & 0x1F) == CMD_GET_DEV_CONFIG) {#define REPLY_WORDS 5 /* we already read word 0 */ u_short reply[REPLY_WORDS]; if (ps2esdi_read_status_words((status >> 8) - 1, REPLY_WORDS, reply)) { /*BA */ printk("%s: Device Configuration Status for drive %u\n", DEVICE_NAME, drive_num); printk("%s: Spares/cyls: %u", DEVICE_NAME, reply[0] >> 8); printk ("Config bits: %s%s%s%s%s\n", (reply[0] & CONFIG_IS) ? "Invalid Secondary, " : "", ((reply[0] & CONFIG_ZD) && !(reply[0] & CONFIG_IS)) ? "Zero Defect, " : "Defects Present, ", (reply[0] & CONFIG_SF) ? "Skewed Format, " : "", (reply[0] & CONFIG_FR) ? "Removable, " : "Non-Removable, ", (reply[0] & CONFIG_RT) ? "No Retries" : "Retries"); rba = reply[1] | ((unsigned long) reply[2] << 16); printk("%s: Number of RBA's: %lu\n", DEVICE_NAME, rba); printk("%s: Physical number of cylinders: %u, Sectors/Track: %u, Heads: %u\n", DEVICE_NAME, reply[3], reply[4] >> 8, reply[4] & 0xff); if (!ps2esdi_info[drive_num].head) { ps2esdi_info[drive_num].head = 64; ps2esdi_info[drive_num].sect = 32; ps2esdi_info[drive_num].cyl = rba / (64 * 32); ps2esdi_info[drive_num].wpcom = 0; ps2esdi_info[drive_num].lzone = ps2esdi_info[drive_num].cyl; ps2esdi_info[drive_num].ctl = 8; if (tp720esdi) { /* store the retrieved parameters */ ps2esdi_info[0].head = reply[4] & 0Xff; ps2esdi_info[0].sect = reply[4] >> 8; ps2esdi_info[0].cyl = reply[3]; ps2esdi_info[0].wpcom = 0; ps2esdi_info[0].lzone = reply[3]; } else { if (!intg_esdi) ps2esdi_drives++; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -