|
|
1.1 ! root 1: #include <linux/config.h> ! 2: #include <linux/sched.h> ! 3: #include <linux/fs.h> ! 4: #include <linux/kernel.h> ! 5: #include <linux/hdreg.h> ! 6: #include <asm/system.h> ! 7: #include <asm/io.h> ! 8: #include <asm/segment.h> ! 9: ! 10: /* ! 11: * This code handles all hd-interrupts, and read/write requests to ! 12: * the hard-disk. It is relatively straigthforward (not obvious maybe, ! 13: * but interrupts never are), while still being efficient, and never ! 14: * disabling interrupts (except to overcome possible race-condition). ! 15: * The elevator block-seek algorithm doesn't need to disable interrupts ! 16: * due to clever programming. ! 17: */ ! 18: ! 19: /* Max read/write errors/sector */ ! 20: #define MAX_ERRORS 5 ! 21: #define MAX_HD 2 ! 22: #define NR_REQUEST 32 ! 23: ! 24: /* ! 25: * This struct defines the HD's and their types. ! 26: * Currently defined for CP3044's, ie a modified ! 27: * type 17. ! 28: */ ! 29: static struct hd_i_struct{ ! 30: int head,sect,cyl,wpcom,lzone,ctl; ! 31: } hd_info[]= { HD_TYPE }; ! 32: ! 33: #define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct))) ! 34: ! 35: static struct hd_struct { ! 36: long start_sect; ! 37: long nr_sects; ! 38: } hd[5*MAX_HD]={{0,0},}; ! 39: ! 40: static struct hd_request { ! 41: int hd; /* -1 if no request */ ! 42: int nsector; ! 43: int sector; ! 44: int head; ! 45: int cyl; ! 46: int cmd; ! 47: int errors; ! 48: struct buffer_head * bh; ! 49: struct hd_request * next; ! 50: } request[NR_REQUEST]; ! 51: ! 52: #define IN_ORDER(s1,s2) \ ! 53: ((s1)->hd<(s2)->hd || (s1)->hd==(s2)->hd && \ ! 54: ((s1)->cyl<(s2)->cyl || (s1)->cyl==(s2)->cyl && \ ! 55: ((s1)->head<(s2)->head || (s1)->head==(s2)->head && \ ! 56: ((s1)->sector<(s2)->sector)))) ! 57: ! 58: static struct hd_request * this_request = NULL; ! 59: ! 60: static int sorting=0; ! 61: ! 62: static void do_request(void); ! 63: static void reset_controller(void); ! 64: static void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head, ! 65: unsigned int cyl,struct buffer_head * bh); ! 66: void hd_init(void); ! 67: ! 68: #define port_read(port,buf,nr) \ ! 69: __asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di") ! 70: ! 71: #define port_write(port,buf,nr) \ ! 72: __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si") ! 73: ! 74: extern void hd_interrupt(void); ! 75: ! 76: static struct task_struct * wait_for_request=NULL; ! 77: ! 78: static inline void lock_buffer(struct buffer_head * bh) ! 79: { ! 80: if (bh->b_lock) ! 81: printk("hd.c: buffer multiply locked\n"); ! 82: bh->b_lock=1; ! 83: } ! 84: ! 85: static inline void unlock_buffer(struct buffer_head * bh) ! 86: { ! 87: if (!bh->b_lock) ! 88: printk("hd.c: free buffer being unlocked\n"); ! 89: bh->b_lock=0; ! 90: wake_up(&bh->b_wait); ! 91: } ! 92: ! 93: static inline void wait_on_buffer(struct buffer_head * bh) ! 94: { ! 95: cli(); ! 96: while (bh->b_lock) ! 97: sleep_on(&bh->b_wait); ! 98: sti(); ! 99: } ! 100: ! 101: void rw_hd(int rw, struct buffer_head * bh) ! 102: { ! 103: unsigned int block,dev; ! 104: unsigned int sec,head,cyl; ! 105: ! 106: block = bh->b_blocknr << 1; ! 107: dev = MINOR(bh->b_dev); ! 108: if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) ! 109: return; ! 110: block += hd[dev].start_sect; ! 111: dev /= 5; ! 112: __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0), ! 113: "r" (hd_info[dev].sect)); ! 114: __asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0), ! 115: "r" (hd_info[dev].head)); ! 116: rw_abs_hd(rw,dev,sec+1,head,cyl,bh); ! 117: } ! 118: ! 119: /* This may be used only once, enforced by 'static int callable' */ ! 120: int sys_setup(void) ! 121: { ! 122: static int callable = 1; ! 123: int i,drive; ! 124: struct partition *p; ! 125: ! 126: if (!callable) ! 127: return -1; ! 128: callable = 0; ! 129: for (drive=0 ; drive<NR_HD ; drive++) { ! 130: rw_abs_hd(READ,drive,1,0,0,(struct buffer_head *) start_buffer); ! 131: if (!start_buffer->b_uptodate) { ! 132: printk("Unable to read partition table of drive %d\n\r", ! 133: drive); ! 134: panic(""); ! 135: } ! 136: if (start_buffer->b_data[510] != 0x55 || (unsigned char) ! 137: start_buffer->b_data[511] != 0xAA) { ! 138: printk("Bad partition table on drive %d\n\r",drive); ! 139: panic(""); ! 140: } ! 141: p = 0x1BE + (void *)start_buffer->b_data; ! 142: for (i=1;i<5;i++,p++) { ! 143: hd[i+5*drive].start_sect = p->start_sect; ! 144: hd[i+5*drive].nr_sects = p->nr_sects; ! 145: } ! 146: } ! 147: printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":""); ! 148: mount_root(); ! 149: return (0); ! 150: } ! 151: ! 152: /* ! 153: * This is the pointer to a routine to be executed at every hd-interrupt. ! 154: * Interesting way of doing things, but should be rather practical. ! 155: */ ! 156: void (*do_hd)(void) = NULL; ! 157: ! 158: static int controller_ready(void) ! 159: { ! 160: int retries=1000; ! 161: ! 162: while (--retries && (inb(HD_STATUS)&0xc0)!=0x40); ! 163: return (retries); ! 164: } ! 165: ! 166: static int win_result(void) ! 167: { ! 168: int i=inb(HD_STATUS); ! 169: ! 170: if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)) ! 171: == (READY_STAT | SEEK_STAT)) ! 172: return(0); /* ok */ ! 173: if (i&1) i=inb(HD_ERROR); ! 174: return (1); ! 175: } ! 176: ! 177: static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, ! 178: unsigned int head,unsigned int cyl,unsigned int cmd, ! 179: void (*intr_addr)(void)) ! 180: { ! 181: register int port asm("dx"); ! 182: ! 183: if (drive>1 || head>15) ! 184: panic("Trying to write bad sector"); ! 185: if (!controller_ready()) ! 186: panic("HD controller not ready"); ! 187: do_hd = intr_addr; ! 188: outb(_CTL,HD_CMD); ! 189: port=HD_DATA; ! 190: outb_p(_WPCOM,++port); ! 191: outb_p(nsect,++port); ! 192: outb_p(sect,++port); ! 193: outb_p(cyl,++port); ! 194: outb_p(cyl>>8,++port); ! 195: outb_p(0xA0|(drive<<4)|head,++port); ! 196: outb(cmd,++port); ! 197: } ! 198: ! 199: static int drive_busy(void) ! 200: { ! 201: unsigned int i; ! 202: ! 203: for (i = 0; i < 100000; i++) ! 204: if (READY_STAT == (inb(HD_STATUS) & (BUSY_STAT | READY_STAT))) ! 205: break; ! 206: i = inb(HD_STATUS); ! 207: i &= BUSY_STAT | READY_STAT | SEEK_STAT; ! 208: if (i == READY_STAT | SEEK_STAT) ! 209: return(0); ! 210: printk("HD controller times out\n\r"); ! 211: return(1); ! 212: } ! 213: ! 214: static void reset_controller(void) ! 215: { ! 216: int i; ! 217: ! 218: outb(4,HD_CMD); ! 219: for(i = 0; i < 1000; i++) nop(); ! 220: outb(0,HD_CMD); ! 221: for(i = 0; i < 10000 && drive_busy(); i++) /* nothing */; ! 222: if (drive_busy()) ! 223: printk("HD-controller still busy\n\r"); ! 224: if((i = inb(ERR_STAT)) != 1) ! 225: printk("HD-controller reset failed: %02x\n\r",i); ! 226: } ! 227: ! 228: static void reset_hd(int nr) ! 229: { ! 230: reset_controller(); ! 231: hd_out(nr,_SECT,_SECT,_HEAD-1,_CYL,WIN_SPECIFY,&do_request); ! 232: } ! 233: ! 234: void unexpected_hd_interrupt(void) ! 235: { ! 236: panic("Unexpected HD interrupt\n\r"); ! 237: } ! 238: ! 239: static void bad_rw_intr(void) ! 240: { ! 241: int i = this_request->hd; ! 242: ! 243: if (this_request->errors++ >= MAX_ERRORS) { ! 244: this_request->bh->b_uptodate = 0; ! 245: unlock_buffer(this_request->bh); ! 246: wake_up(&wait_for_request); ! 247: this_request->hd = -1; ! 248: this_request=this_request->next; ! 249: } ! 250: reset_hd(i); ! 251: } ! 252: ! 253: static void read_intr(void) ! 254: { ! 255: if (win_result()) { ! 256: bad_rw_intr(); ! 257: return; ! 258: } ! 259: port_read(HD_DATA,this_request->bh->b_data+ ! 260: 512*(this_request->nsector&1),256); ! 261: this_request->errors = 0; ! 262: if (--this_request->nsector) ! 263: return; ! 264: this_request->bh->b_uptodate = 1; ! 265: this_request->bh->b_dirt = 0; ! 266: wake_up(&wait_for_request); ! 267: unlock_buffer(this_request->bh); ! 268: this_request->hd = -1; ! 269: this_request=this_request->next; ! 270: do_request(); ! 271: } ! 272: ! 273: static void write_intr(void) ! 274: { ! 275: if (win_result()) { ! 276: bad_rw_intr(); ! 277: return; ! 278: } ! 279: if (--this_request->nsector) { ! 280: port_write(HD_DATA,this_request->bh->b_data+512,256); ! 281: return; ! 282: } ! 283: this_request->bh->b_uptodate = 1; ! 284: this_request->bh->b_dirt = 0; ! 285: wake_up(&wait_for_request); ! 286: unlock_buffer(this_request->bh); ! 287: this_request->hd = -1; ! 288: this_request=this_request->next; ! 289: do_request(); ! 290: } ! 291: ! 292: static void do_request(void) ! 293: { ! 294: int i,r; ! 295: ! 296: if (sorting) ! 297: return; ! 298: if (!this_request) { ! 299: do_hd=NULL; ! 300: return; ! 301: } ! 302: if (this_request->cmd == WIN_WRITE) { ! 303: hd_out(this_request->hd,this_request->nsector,this_request-> ! 304: sector,this_request->head,this_request->cyl, ! 305: this_request->cmd,&write_intr); ! 306: for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++) ! 307: /* nothing */ ; ! 308: if (!r) { ! 309: reset_hd(this_request->hd); ! 310: return; ! 311: } ! 312: port_write(HD_DATA,this_request->bh->b_data+ ! 313: 512*(this_request->nsector&1),256); ! 314: } else if (this_request->cmd == WIN_READ) { ! 315: hd_out(this_request->hd,this_request->nsector,this_request-> ! 316: sector,this_request->head,this_request->cyl, ! 317: this_request->cmd,&read_intr); ! 318: } else ! 319: panic("unknown hd-command"); ! 320: } ! 321: ! 322: /* ! 323: * add-request adds a request to the linked list. ! 324: * It sets the 'sorting'-variable when doing something ! 325: * that interrupts shouldn't touch. ! 326: */ ! 327: static void add_request(struct hd_request * req) ! 328: { ! 329: struct hd_request * tmp; ! 330: ! 331: if (req->nsector != 2) ! 332: panic("nsector!=2 not implemented"); ! 333: /* ! 334: * Not to mess up the linked lists, we never touch the two first ! 335: * entries (not this_request, as it is used by current interrups, ! 336: * and not this_request->next, as it can be assigned to this_request). ! 337: * This is not too high a price to pay for the ability of not ! 338: * disabling interrupts. ! 339: */ ! 340: sorting=1; ! 341: if (!(tmp=this_request)) ! 342: this_request=req; ! 343: else { ! 344: if (!(tmp->next)) ! 345: tmp->next=req; ! 346: else { ! 347: tmp=tmp->next; ! 348: for ( ; tmp->next ; tmp=tmp->next) ! 349: if ((IN_ORDER(tmp,req) || ! 350: !IN_ORDER(tmp,tmp->next)) && ! 351: IN_ORDER(req,tmp->next)) ! 352: break; ! 353: req->next=tmp->next; ! 354: tmp->next=req; ! 355: } ! 356: } ! 357: sorting=0; ! 358: /* ! 359: * NOTE! As a result of sorting, the interrupts may have died down, ! 360: * as they aren't redone due to locking with sorting=1. They might ! 361: * also never have started, if this is the first request in the queue, ! 362: * so we restart them if necessary. ! 363: */ ! 364: if (!do_hd) ! 365: do_request(); ! 366: } ! 367: ! 368: void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head, ! 369: unsigned int cyl,struct buffer_head * bh) ! 370: { ! 371: struct hd_request * req; ! 372: ! 373: if (rw!=READ && rw!=WRITE) ! 374: panic("Bad hd command, must be R/W"); ! 375: lock_buffer(bh); ! 376: repeat: ! 377: for (req=0+request ; req<NR_REQUEST+request ; req++) ! 378: if (req->hd<0) ! 379: break; ! 380: if (req==NR_REQUEST+request) { ! 381: sleep_on(&wait_for_request); ! 382: goto repeat; ! 383: } ! 384: req->hd=nr; ! 385: req->nsector=2; ! 386: req->sector=sec; ! 387: req->head=head; ! 388: req->cyl=cyl; ! 389: req->cmd = ((rw==READ)?WIN_READ:WIN_WRITE); ! 390: req->bh=bh; ! 391: req->errors=0; ! 392: req->next=NULL; ! 393: add_request(req); ! 394: wait_on_buffer(bh); ! 395: } ! 396: ! 397: void hd_init(void) ! 398: { ! 399: int i; ! 400: ! 401: for (i=0 ; i<NR_REQUEST ; i++) { ! 402: request[i].hd = -1; ! 403: request[i].next = NULL; ! 404: } ! 405: for (i=0 ; i<NR_HD ; i++) { ! 406: hd[i*5].start_sect = 0; ! 407: hd[i*5].nr_sects = hd_info[i].head* ! 408: hd_info[i].sect*hd_info[i].cyl; ! 409: } ! 410: set_trap_gate(0x2E,&hd_interrupt); ! 411: outb_p(inb_p(0x21)&0xfb,0x21); ! 412: outb(inb_p(0xA1)&0xbf,0xA1); ! 413: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.