|
|
1.1 ! root 1: /* ! 2: * linux/kernel/hd.c ! 3: * ! 4: * (C) 1991 Linus Torvalds ! 5: */ ! 6: ! 7: /* ! 8: * This is the low-level hd interrupt support. It traverses the ! 9: * request-list, using interrupts to jump between functions. As ! 10: * all the functions are called within interrupts, we may not ! 11: * sleep. Special care is recommended. ! 12: */ ! 13: ! 14: #include <linux/config.h> ! 15: #include <linux/sched.h> ! 16: #include <linux/fs.h> ! 17: #include <linux/kernel.h> ! 18: #include <linux/hdreg.h> ! 19: #include <asm/system.h> ! 20: #include <asm/io.h> ! 21: #include <asm/segment.h> ! 22: ! 23: #define MAJOR_NR 3 ! 24: #include "blk.h" ! 25: ! 26: /* Max read/write errors/sector */ ! 27: #define MAX_ERRORS 5 ! 28: #define MAX_HD 2 ! 29: ! 30: /* ! 31: * This struct defines the HD's and their types. ! 32: */ ! 33: struct hd_i_struct { ! 34: int head,sect,cyl,wpcom,lzone,ctl; ! 35: }; ! 36: #ifdef HD_TYPE ! 37: struct hd_i_struct hd_info[] = { HD_TYPE }; ! 38: #define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct))) ! 39: #else ! 40: struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} }; ! 41: static int NR_HD = 0; ! 42: #endif ! 43: ! 44: static struct hd_struct { ! 45: long start_sect; ! 46: long nr_sects; ! 47: } hd[5*MAX_HD]={{0,0},}; ! 48: ! 49: #define port_read(port,buf,nr) \ ! 50: __asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di") ! 51: ! 52: #define port_write(port,buf,nr) \ ! 53: __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si") ! 54: ! 55: extern void hd_interrupt(void); ! 56: ! 57: /* This may be used only once, enforced by 'static int callable' */ ! 58: int sys_setup(void * BIOS) ! 59: { ! 60: static int callable = 1; ! 61: int i,drive; ! 62: struct partition *p; ! 63: struct buffer_head * bh; ! 64: ! 65: if (!callable) ! 66: return -1; ! 67: callable = 0; ! 68: #ifndef HD_TYPE ! 69: for (drive=0 ; drive<2 ; drive++) { ! 70: hd_info[drive].cyl = *(unsigned short *) BIOS; ! 71: hd_info[drive].head = *(unsigned char *) (2+BIOS); ! 72: hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); ! 73: hd_info[drive].ctl = *(unsigned char *) (8+BIOS); ! 74: hd_info[drive].lzone = *(unsigned short *) (12+BIOS); ! 75: hd_info[drive].sect = *(unsigned char *) (14+BIOS); ! 76: BIOS += 16; ! 77: } ! 78: if (hd_info[1].cyl) ! 79: NR_HD=2; ! 80: else ! 81: NR_HD=1; ! 82: #endif ! 83: for (i=0 ; i<NR_HD ; i++) { ! 84: hd[i*5].start_sect = 0; ! 85: hd[i*5].nr_sects = hd_info[i].head* ! 86: hd_info[i].sect*hd_info[i].cyl; ! 87: } ! 88: for (drive=0 ; drive<NR_HD ; drive++) { ! 89: if (!(bh = bread(0x300 + drive*5,0))) { ! 90: printk("Unable to read partition table of drive %d\n\r", ! 91: drive); ! 92: panic(""); ! 93: } ! 94: if (bh->b_data[510] != 0x55 || (unsigned char) ! 95: bh->b_data[511] != 0xAA) { ! 96: printk("Bad partition table on drive %d\n\r",drive); ! 97: panic(""); ! 98: } ! 99: p = 0x1BE + (void *)bh->b_data; ! 100: for (i=1;i<5;i++,p++) { ! 101: hd[i+5*drive].start_sect = p->start_sect; ! 102: hd[i+5*drive].nr_sects = p->nr_sects; ! 103: } ! 104: brelse(bh); ! 105: } ! 106: printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":""); ! 107: mount_root(); ! 108: return (0); ! 109: } ! 110: ! 111: static int controller_ready(void) ! 112: { ! 113: int retries=1000; ! 114: ! 115: while (--retries && (inb(HD_STATUS)&0xc0)!=0x40); ! 116: return (retries); ! 117: } ! 118: ! 119: static int win_result(void) ! 120: { ! 121: int i=inb(HD_STATUS); ! 122: ! 123: if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)) ! 124: == (READY_STAT | SEEK_STAT)) ! 125: return(0); /* ok */ ! 126: if (i&1) i=inb(HD_ERROR); ! 127: return (1); ! 128: } ! 129: ! 130: static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, ! 131: unsigned int head,unsigned int cyl,unsigned int cmd, ! 132: void (*intr_addr)(void)) ! 133: { ! 134: register int port asm("dx"); ! 135: ! 136: if (drive>1 || head>15) ! 137: panic("Trying to write bad sector"); ! 138: if (!controller_ready()) ! 139: panic("HD controller not ready"); ! 140: do_hd = intr_addr; ! 141: outb(hd_info[drive].ctl,HD_CMD); ! 142: port=HD_DATA; ! 143: outb_p(hd_info[drive].wpcom>>2,++port); ! 144: outb_p(nsect,++port); ! 145: outb_p(sect,++port); ! 146: outb_p(cyl,++port); ! 147: outb_p(cyl>>8,++port); ! 148: outb_p(0xA0|(drive<<4)|head,++port); ! 149: outb(cmd,++port); ! 150: } ! 151: ! 152: static int drive_busy(void) ! 153: { ! 154: unsigned int i; ! 155: ! 156: for (i = 0; i < 100000; i++) ! 157: if (READY_STAT == (inb(HD_STATUS) & (BUSY_STAT | READY_STAT))) ! 158: break; ! 159: i = inb(HD_STATUS); ! 160: i &= BUSY_STAT | READY_STAT | SEEK_STAT; ! 161: if (i == READY_STAT | SEEK_STAT) ! 162: return(0); ! 163: printk("HD controller times out\n\r"); ! 164: return(1); ! 165: } ! 166: ! 167: static void reset_controller(void) ! 168: { ! 169: int i; ! 170: ! 171: outb(4,HD_CMD); ! 172: for(i = 0; i < 1000; i++) nop(); ! 173: outb(0,HD_CMD); ! 174: for(i = 0; i < 10000 && drive_busy(); i++) /* nothing */; ! 175: if (drive_busy()) ! 176: printk("HD-controller still busy\n\r"); ! 177: if((i = inb(ERR_STAT)) != 1) ! 178: printk("HD-controller reset failed: %02x\n\r",i); ! 179: } ! 180: ! 181: static void redo_hd_request(void) ! 182: { ! 183: do_hd = NULL; ! 184: do_hd_request(); ! 185: } ! 186: ! 187: static void reset_hd(int nr) ! 188: { ! 189: reset_controller(); ! 190: hd_out(nr,hd_info[nr].sect,hd_info[nr].sect,hd_info[nr].head-1, ! 191: hd_info[nr].cyl,WIN_SPECIFY,&redo_hd_request); ! 192: } ! 193: ! 194: void unexpected_hd_interrupt(void) ! 195: { ! 196: printk("Unexpected HD interrupt\n\r"); ! 197: } ! 198: ! 199: static void bad_rw_intr(void) ! 200: { ! 201: int i = CURRENT_DEV; ! 202: ! 203: if (CURRENT->errors++ >= MAX_ERRORS) ! 204: end_request(0); ! 205: reset_hd(i); ! 206: } ! 207: ! 208: static void read_intr(void) ! 209: { ! 210: if (win_result()) { ! 211: bad_rw_intr(); ! 212: return; ! 213: } ! 214: port_read(HD_DATA,CURRENT->buffer,256); ! 215: CURRENT->errors = 0; ! 216: CURRENT->buffer += 512; ! 217: CURRENT->sector++; ! 218: if (--CURRENT->nr_sectors) ! 219: return; ! 220: end_request(1); ! 221: do_hd_request(); ! 222: } ! 223: ! 224: static void write_intr(void) ! 225: { ! 226: if (win_result()) { ! 227: bad_rw_intr(); ! 228: return; ! 229: } ! 230: if (--CURRENT->nr_sectors) { ! 231: CURRENT->sector++; ! 232: CURRENT->buffer += 512; ! 233: port_write(HD_DATA,CURRENT->buffer,256); ! 234: return; ! 235: } ! 236: end_request(1); ! 237: do_hd_request(); ! 238: } ! 239: ! 240: void do_hd_request(void) ! 241: { ! 242: int i,r; ! 243: unsigned int block,dev; ! 244: unsigned int sec,head,cyl; ! 245: unsigned int nsect; ! 246: ! 247: INIT_REQUEST; ! 248: dev = MINOR(CURRENT->dev); ! 249: block = CURRENT->sector; ! 250: if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) { ! 251: end_request(0); ! 252: goto repeat; ! 253: } ! 254: block += hd[dev].start_sect; ! 255: dev /= 5; ! 256: __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0), ! 257: "r" (hd_info[dev].sect)); ! 258: __asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0), ! 259: "r" (hd_info[dev].head)); ! 260: sec++; ! 261: nsect = CURRENT->nr_sectors; ! 262: if (CURRENT->cmd == WRITE) { ! 263: hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr); ! 264: for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++) ! 265: /* nothing */ ; ! 266: if (!r) { ! 267: reset_hd(CURRENT_DEV); ! 268: return; ! 269: } ! 270: port_write(HD_DATA,CURRENT->buffer,256); ! 271: } else if (CURRENT->cmd == READ) { ! 272: hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr); ! 273: } else ! 274: panic("unknown hd-command"); ! 275: } ! 276: ! 277: void hd_init(void) ! 278: { ! 279: blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; ! 280: set_trap_gate(0x2E,&hd_interrupt); ! 281: outb_p(inb_p(0x21)&0xfb,0x21); ! 282: outb(inb_p(0xA1)&0xbf,0xA1); ! 283: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.