|
|
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.
1.1.1.2 root 12: *
13: * modified by Drew Eckhardt to check nr of hd's from the CMOS.
1.1.1.5 root 14: *
15: * Thanks to Branko Lankester, [email protected], who found a bug
16: * in the early extended-partition checks and added DM partitions
1.1 root 17: */
18:
1.1.1.6 root 19: #include <errno.h>
20:
1.1 root 21: #include <linux/config.h>
22: #include <linux/sched.h>
1.1.1.4 root 23: #include <linux/timer.h>
1.1 root 24: #include <linux/fs.h>
25: #include <linux/kernel.h>
26: #include <linux/hdreg.h>
1.1.1.8 ! root 27:
! 28: #define REALLY_SLOW_IO
1.1 root 29: #include <asm/system.h>
30: #include <asm/io.h>
31: #include <asm/segment.h>
32:
33: #define MAJOR_NR 3
34: #include "blk.h"
35:
1.1.1.5 root 36: static inline unsigned char CMOS_READ(unsigned char addr)
37: {
38: outb_p(0x80|addr,0x70);
39: return inb_p(0x71);
40: }
1.1.1.2 root 41:
1.1.1.7 root 42: #define HD_DELAY 0
43:
1.1 root 44: /* Max read/write errors/sector */
1.1.1.2 root 45: #define MAX_ERRORS 7
1.1 root 46: #define MAX_HD 2
47:
1.1.1.2 root 48: static void recal_intr(void);
1.1.1.3 root 49: static void bad_rw_intr(void);
1.1.1.2 root 50:
1.1.1.3 root 51: static int recalibrate = 0;
1.1.1.6 root 52: static int reset = 0;
1.1.1.2 root 53:
1.1.1.7 root 54: #if (HD_DELAY > 0)
55: unsigned long last_req, read_timer();
56: #endif
57:
1.1 root 58: /*
59: * This struct defines the HD's and their types.
60: */
61: struct hd_i_struct {
1.1.1.4 root 62: unsigned int head,sect,cyl,wpcom,lzone,ctl;
1.1 root 63: };
64: #ifdef HD_TYPE
65: struct hd_i_struct hd_info[] = { HD_TYPE };
66: #define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct)))
67: #else
68: struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
69: static int NR_HD = 0;
70: #endif
71:
72: static struct hd_struct {
73: long start_sect;
74: long nr_sects;
1.1.1.4 root 75: } hd[MAX_HD<<6]={{0,0},};
1.1 root 76:
1.1.1.4 root 77: static int hd_sizes[MAX_HD<<6] = {0, };
1.1.1.3 root 78:
1.1 root 79: #define port_read(port,buf,nr) \
80: __asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
81:
82: #define port_write(port,buf,nr) \
83: __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
84:
85: extern void hd_interrupt(void);
1.1.1.2 root 86: extern void rd_load(void);
1.1 root 87:
1.1.1.4 root 88: static unsigned int current_minor;
89:
1.1.1.6 root 90: /*
91: * Create devices for each logical partition in an extended partition.
92: * The logical partitions form a linked list, with each entry being
93: * a partition table with two entries. The first entry
94: * is the real data partition (with a start relative to the partition
95: * table start). The second is a pointer to the next logical partition
96: * (with a start relative to the entire extended partition).
97: * We do not create a Linux partition for the partition tables, but
98: * only for the actual data partitions.
99: */
100: static void extended_partition(unsigned int dev)
101: {
102: struct buffer_head *bh;
103: struct partition *p;
104: unsigned long first_sector, this_sector;
105:
106: first_sector = hd[MINOR(dev)].start_sect;
107: this_sector = first_sector;
108:
109: while (1) {
110: if ((current_minor & 0x3f) >= 60)
111: return;
112: if (!(bh = bread(dev,0))) {
113: printk("Unable to read partition table of device %04x\n",dev);
114: return;
115: }
116: /*
117: * This block is from a device that we're about to stomp on.
118: * So make sure nobody thinks this block is usable.
119: */
120: bh->b_dirt=0;
121: bh->b_uptodate=0;
122: if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
123: p = 0x1BE + (void *)bh->b_data;
124: /*
125: * Process the first entry, which should be the real
126: * data partition.
127: */
128: if (p->sys_ind == EXTENDED_PARTITION ||
129: !(hd[current_minor].nr_sects = p->nr_sects))
130: goto done; /* shouldn't happen */
131: hd[current_minor].start_sect = this_sector + p->start_sect;
132: printk(" Logical part %d start %d size %d end %d\n\r",
133: current_minor, hd[current_minor].start_sect,
134: hd[current_minor].nr_sects,
135: hd[current_minor].start_sect +
1.1.1.7 root 136: hd[current_minor].nr_sects - 1);
1.1.1.6 root 137: current_minor++;
138: p++;
139: /*
140: * Process the second entry, which should be a link
141: * to the next logical partition. Create a minor
142: * for this just long enough to get the next partition
143: * table. The minor will be reused for the real
144: * data partition.
145: */
146: if (p->sys_ind != EXTENDED_PARTITION ||
147: !(hd[current_minor].nr_sects = p->nr_sects))
148: goto done; /* no more logicals in this partition */
149: hd[current_minor].start_sect = first_sector + p->start_sect;
150: this_sector = first_sector + p->start_sect;
151: dev = 0x0300 | current_minor;
152: brelse(bh);
153: } else
154: goto done;
155: }
156: done:
157: brelse(bh);
158: }
159:
1.1.1.4 root 160: static void check_partition(unsigned int dev)
161: {
1.1.1.6 root 162: int i, minor = current_minor;
1.1.1.4 root 163: struct buffer_head *bh;
164: struct partition *p;
1.1.1.6 root 165: unsigned long first_sector;
1.1.1.4 root 166:
1.1.1.6 root 167: first_sector = hd[MINOR(dev)].start_sect;
1.1.1.4 root 168: if (!(bh = bread(dev,0))) {
169: printk("Unable to read partition table of device %04x\n",dev);
170: return;
171: }
1.1.1.6 root 172: printk("Drive %d:\n\r",minor >> 6);
173: current_minor += 4; /* first "extra" minor */
1.1.1.4 root 174: if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
175: p = 0x1BE + (void *)bh->b_data;
1.1.1.6 root 176: for (i=1 ; i<=4 ; minor++,i++,p++) {
177: if (!(hd[minor].nr_sects = p->nr_sects))
1.1.1.5 root 178: continue;
1.1.1.6 root 179: hd[minor].start_sect = first_sector + p->start_sect;
180: printk(" part %d start %d size %d end %d \n\r", i,
181: hd[minor].start_sect, hd[minor].nr_sects,
1.1.1.7 root 182: hd[minor].start_sect + hd[minor].nr_sects - 1);
1.1.1.5 root 183: if ((current_minor & 0x3f) >= 60)
184: continue;
185: if (p->sys_ind == EXTENDED_PARTITION) {
1.1.1.6 root 186: extended_partition(0x0300 | minor);
1.1.1.5 root 187: }
1.1.1.4 root 188: }
1.1.1.5 root 189: /*
190: * check for Disk Manager partition table
191: */
192: if (*(unsigned short *) (bh->b_data+0xfc) == 0x55AA) {
193: p = 0x1BE + (void *)bh->b_data;
1.1.1.6 root 194: for (i = 4 ; i < 16 ; i++, current_minor++) {
1.1.1.5 root 195: p--;
196: if ((current_minor & 0x3f) >= 60)
197: break;
1.1.1.6 root 198: if (!(p->start_sect && p->nr_sects))
1.1.1.5 root 199: continue;
1.1.1.6 root 200: hd[current_minor].start_sect = p->start_sect;
201: hd[current_minor].nr_sects = p->nr_sects;
202: printk(" DM part %d start %d size %d end %d\n\r",
203: current_minor,
204: hd[current_minor].start_sect,
205: hd[current_minor].nr_sects,
206: hd[current_minor].start_sect +
1.1.1.7 root 207: hd[current_minor].nr_sects - 1);
1.1.1.5 root 208: }
1.1.1.4 root 209: }
210: } else
211: printk("Bad partition table on dev %04x\n",dev);
212: brelse(bh);
213: }
214:
1.1 root 215: /* This may be used only once, enforced by 'static int callable' */
216: int sys_setup(void * BIOS)
217: {
218: static int callable = 1;
219: int i,drive;
1.1.1.2 root 220: unsigned char cmos_disks;
1.1 root 221:
222: if (!callable)
223: return -1;
224: callable = 0;
225: #ifndef HD_TYPE
226: for (drive=0 ; drive<2 ; drive++) {
227: hd_info[drive].cyl = *(unsigned short *) BIOS;
228: hd_info[drive].head = *(unsigned char *) (2+BIOS);
229: hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
230: hd_info[drive].ctl = *(unsigned char *) (8+BIOS);
231: hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
232: hd_info[drive].sect = *(unsigned char *) (14+BIOS);
233: BIOS += 16;
234: }
1.1.1.2 root 235:
236: /*
237: We querry CMOS about hard disks : it could be that
238: we have a SCSI/ESDI/etc controller that is BIOS
239: compatable with ST-506, and thus showing up in our
240: BIOS table, but not register compatable, and therefore
241: not present in CMOS.
242:
243: Furthurmore, we will assume that our ST-506 drives
244: <if any> are the primary drives in the system, and
245: the ones reflected as drive 1 or 2.
246:
247: The first drive is stored in the high nibble of CMOS
248: byte 0x12, the second in the low nibble. This will be
249: either a 4 bit drive type or 0xf indicating use byte 0x19
250: for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.
251:
252: Needless to say, a non-zero value means we have
253: an AT controller hard disk for that drive.
254:
255:
256: */
257:
258: if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
259: if (cmos_disks & 0x0f)
260: NR_HD = 2;
261: else
262: NR_HD = 1;
263: else
264: NR_HD = 0;
1.1.1.6 root 265: #endif
266: for (i = 0 ; i < (MAX_HD<<6) ; i++) {
267: hd[i].start_sect = 0;
268: hd[i].nr_sects = 0;
1.1.1.2 root 269: }
1.1.1.6 root 270: for (i = 0 ; i < NR_HD ; i++)
271: hd[i<<6].nr_sects = hd_info[i].head*
272: hd_info[i].sect*hd_info[i].cyl;
1.1 root 273: for (drive=0 ; drive<NR_HD ; drive++) {
1.1.1.4 root 274: current_minor = 1+(drive<<6);
275: check_partition(0x0300+(drive<<6));
1.1 root 276: }
1.1.1.4 root 277: for (i=0 ; i<(MAX_HD<<6) ; i++)
1.1.1.3 root 278: hd_sizes[i] = hd[i].nr_sects>>1 ;
279: blk_size[MAJOR_NR] = hd_sizes;
1.1.1.2 root 280: if (NR_HD)
281: printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
282: rd_load();
1.1 root 283: mount_root();
284: return (0);
285: }
286:
1.1.1.7 root 287: #if (HD_DELAY > 0)
288: unsigned long read_timer(void)
289: {
290: unsigned long t;
291: int i;
292:
293: cli();
294: outb_p(0xc2, 0x43);
295: t = jiffies * 11931 + (inb_p(0x40) & 0x80 ? 5966 : 11932);
296: i = inb_p(0x40);
297: i |= inb(0x40) << 8;
298: sti();
299: return(t - i / 2);
300: }
301: #endif
302:
1.1 root 303: static int controller_ready(void)
304: {
1.1.1.3 root 305: int retries = 100000;
1.1 root 306:
1.1.1.4 root 307: while (--retries && (inb_p(HD_STATUS)&0x80))
308: /* nothing */;
309: if (!retries)
310: printk("controller_ready: status = %02x\n\r",
311: (unsigned char) inb_p(HD_STATUS));
1.1 root 312: return (retries);
313: }
314:
315: static int win_result(void)
316: {
1.1.1.2 root 317: int i=inb_p(HD_STATUS);
1.1 root 318:
319: if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
320: == (READY_STAT | SEEK_STAT))
321: return(0); /* ok */
1.1.1.5 root 322: if (i&1)
323: i=inb(HD_ERROR);
1.1 root 324: return (1);
325: }
326:
327: static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
328: unsigned int head,unsigned int cyl,unsigned int cmd,
329: void (*intr_addr)(void))
330: {
1.1.1.4 root 331: unsigned short port;
1.1 root 332:
333: if (drive>1 || head>15)
334: panic("Trying to write bad sector");
1.1.1.7 root 335: #if (HD_DELAY > 0)
336: while (read_timer() - last_req < HD_DELAY)
337: /* nothing */;
338: #endif
1.1.1.4 root 339: if (reset || !controller_ready()) {
340: reset = 1;
341: return;
342: }
1.1.1.3 root 343: SET_INTR(intr_addr);
1.1.1.2 root 344: outb_p(hd_info[drive].ctl,HD_CMD);
1.1 root 345: port=HD_DATA;
346: outb_p(hd_info[drive].wpcom>>2,++port);
347: outb_p(nsect,++port);
348: outb_p(sect,++port);
349: outb_p(cyl,++port);
350: outb_p(cyl>>8,++port);
351: outb_p(0xA0|(drive<<4)|head,++port);
1.1.1.7 root 352: outb_p(cmd,++port);
1.1 root 353: }
354:
355: static int drive_busy(void)
356: {
357: unsigned int i;
1.1.1.3 root 358: unsigned char c;
1.1 root 359:
1.1.1.6 root 360: for (i = 0; i < 500000 ; i++) {
1.1.1.3 root 361: c = inb_p(HD_STATUS);
362: c &= (BUSY_STAT | READY_STAT | SEEK_STAT);
363: if (c == (READY_STAT | SEEK_STAT))
364: return 0;
365: }
1.1.1.4 root 366: printk("HD controller times out, c=%02x\n\r",c);
1.1 root 367: return(1);
368: }
369:
370: static void reset_controller(void)
371: {
372: int i;
373:
1.1.1.7 root 374: printk("HD-controller reset\r\n");
1.1 root 375: outb(4,HD_CMD);
1.1.1.3 root 376: for(i = 0; i < 1000; i++) nop();
1.1.1.2 root 377: outb(hd_info[0].ctl & 0x0f ,HD_CMD);
1.1 root 378: if (drive_busy())
379: printk("HD-controller still busy\n\r");
1.1.1.2 root 380: if ((i = inb(HD_ERROR)) != 1)
1.1 root 381: printk("HD-controller reset failed: %02x\n\r",i);
382: }
383:
1.1.1.3 root 384: static void reset_hd(void)
1.1 root 385: {
1.1.1.3 root 386: static int i;
387:
388: repeat:
389: if (reset) {
390: reset = 0;
391: i = -1;
392: reset_controller();
393: } else if (win_result()) {
394: bad_rw_intr();
395: if (reset)
396: goto repeat;
397: }
398: i++;
399: if (i < NR_HD) {
400: hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
401: hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
1.1.1.4 root 402: if (reset)
403: goto repeat;
1.1.1.3 root 404: } else
405: do_hd_request();
1.1 root 406: }
407:
1.1.1.6 root 408: /*
409: * Ok, don't know what to do with the unexpected interrupts: on some machines
410: * doing a reset and a retry seems to result in an eternal loop. Right now I
411: * ignore it, and just set the timeout.
412: */
1.1 root 413: void unexpected_hd_interrupt(void)
414: {
415: printk("Unexpected HD interrupt\n\r");
1.1.1.6 root 416: SET_TIMER;
417: #if 0
1.1.1.3 root 418: reset = 1;
419: do_hd_request();
1.1.1.6 root 420: #endif
1.1 root 421: }
422:
423: static void bad_rw_intr(void)
424: {
1.1.1.6 root 425: if (!CURRENT)
426: return;
1.1.1.2 root 427: if (++CURRENT->errors >= MAX_ERRORS)
1.1.1.8 ! root 428: end_request(0);
! 429: else if (CURRENT->errors > MAX_ERRORS/2)
1.1.1.2 root 430: reset = 1;
1.1.1.5 root 431: else
432: recalibrate = 1;
1.1 root 433: }
434:
1.1.1.7 root 435: #define STAT_MASK (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)
436: #define STAT_OK (READY_STAT | SEEK_STAT)
437:
1.1 root 438: static void read_intr(void)
439: {
1.1.1.7 root 440: int i;
441:
442: i = (unsigned) inb_p(HD_STATUS);
443: if (!(i & DRQ_STAT))
444: goto bad_read;
445: if ((i & STAT_MASK) != STAT_OK)
446: goto bad_read;
1.1 root 447: port_read(HD_DATA,CURRENT->buffer,256);
1.1.1.7 root 448: i = (unsigned) inb_p(HD_STATUS);
449: if (!(i & BUSY_STAT))
450: if ((i & STAT_MASK) != STAT_OK)
451: goto bad_read;
1.1 root 452: CURRENT->errors = 0;
1.1.1.8 ! root 453: CURRENT->buffer += 512;
1.1 root 454: CURRENT->sector++;
1.1.1.8 ! root 455: i = --CURRENT->nr_sectors;
! 456: if (!i || (CURRENT->bh && !(i&1)))
! 457: end_request(1);
! 458: if (i > 0) {
1.1.1.7 root 459: SET_INTR(&read_intr);
1.1 root 460: return;
1.1.1.7 root 461: }
462: #if (HD_DELAY > 0)
463: last_req = read_timer();
464: #endif
1.1 root 465: do_hd_request();
1.1.1.7 root 466: return;
467: bad_read:
468: if (i & ERR_STAT)
469: i = (unsigned) inb(HD_ERROR);
470: bad_rw_intr();
471: do_hd_request();
472: return;
1.1 root 473: }
474:
475: static void write_intr(void)
476: {
1.1.1.7 root 477: int i;
478:
479: i = (unsigned) inb_p(HD_STATUS);
480: if ((i & STAT_MASK) != STAT_OK)
481: goto bad_write;
1.1.1.8 ! root 482: if (CURRENT->nr_sectors > 1 && !(i & DRQ_STAT))
! 483: goto bad_write;
! 484: CURRENT->sector++;
! 485: i = --CURRENT->nr_sectors;
! 486: CURRENT->buffer += 512;
! 487: if (!i || (CURRENT->bh && !(i & 1)))
1.1.1.7 root 488: end_request(1);
1.1.1.8 ! root 489: if (i > 0) {
! 490: SET_INTR(&write_intr);
! 491: port_write(HD_DATA,CURRENT->buffer,256);
! 492: } else {
1.1.1.7 root 493: #if (HD_DELAY > 0)
494: last_req = read_timer();
495: #endif
1.1.1.2 root 496: do_hd_request();
1.1 root 497: }
1.1.1.7 root 498: return;
499: bad_write:
500: if (i & ERR_STAT)
501: i = (unsigned) inb(HD_ERROR);
502: bad_rw_intr();
1.1 root 503: do_hd_request();
1.1.1.7 root 504: return;
1.1 root 505: }
506:
1.1.1.2 root 507: static void recal_intr(void)
508: {
509: if (win_result())
510: bad_rw_intr();
511: do_hd_request();
512: }
513:
1.1.1.6 root 514: /*
515: * This is another of the error-routines I don't know what to do with. The
516: * best idea seems to just set reset, and start all over again.
517: */
1.1.1.4 root 518: static void hd_times_out(void)
1.1.1.5 root 519: {
520: do_hd = NULL;
521: reset = 1;
1.1.1.3 root 522: if (!CURRENT)
523: return;
1.1.1.6 root 524: printk("HD timeout\n\r");
525: cli();
1.1.1.3 root 526: if (++CURRENT->errors >= MAX_ERRORS)
1.1.1.8 ! root 527: end_request(0);
1.1.1.3 root 528: do_hd_request();
529: }
530:
1.1.1.6 root 531: static void do_hd_request(void)
1.1 root 532: {
533: int i,r;
534: unsigned int block,dev;
535: unsigned int sec,head,cyl;
536: unsigned int nsect;
537:
538: INIT_REQUEST;
539: dev = MINOR(CURRENT->dev);
540: block = CURRENT->sector;
1.1.1.5 root 541: nsect = CURRENT->nr_sectors;
1.1.1.8 ! root 542: if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects) {
1.1 root 543: end_request(0);
544: goto repeat;
545: }
546: block += hd[dev].start_sect;
1.1.1.4 root 547: dev >>= 6;
548: sec = block % hd_info[dev].sect;
549: block /= hd_info[dev].sect;
550: head = block % hd_info[dev].head;
551: cyl = block / hd_info[dev].head;
1.1 root 552: sec++;
1.1.1.2 root 553: if (reset) {
554: recalibrate = 1;
1.1.1.3 root 555: reset_hd();
1.1.1.2 root 556: return;
557: }
558: if (recalibrate) {
559: recalibrate = 0;
1.1.1.7 root 560: hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
1.1.1.4 root 561: if (reset)
562: goto repeat;
1.1.1.2 root 563: return;
564: }
1.1 root 565: if (CURRENT->cmd == WRITE) {
566: hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
1.1.1.4 root 567: if (reset)
568: goto repeat;
1.1.1.3 root 569: for(i=0 ; i<10000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
1.1 root 570: /* nothing */ ;
571: if (!r) {
1.1.1.2 root 572: bad_rw_intr();
573: goto repeat;
1.1 root 574: }
575: port_write(HD_DATA,CURRENT->buffer,256);
576: } else if (CURRENT->cmd == READ) {
577: hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
1.1.1.4 root 578: if (reset)
579: goto repeat;
1.1 root 580: } else
581: panic("unknown hd-command");
582: }
583:
1.1.1.7 root 584: static int hd_ioctl(struct inode * inode, struct file * file,
585: unsigned int cmd, unsigned int arg)
1.1.1.6 root 586: {
587: struct hd_geometry *loc = (void *) arg;
1.1.1.7 root 588: int dev;
1.1.1.6 root 589:
1.1.1.7 root 590: if (!loc || !inode)
1.1.1.6 root 591: return -EINVAL;
1.1.1.7 root 592: dev = MINOR(inode->i_rdev) >> 6;
1.1.1.6 root 593: if (dev >= NR_HD)
594: return -EINVAL;
595: switch (cmd) {
596: case HDIO_REQ:
1.1.1.7 root 597: verify_area(loc, sizeof(*loc));
1.1.1.6 root 598: put_fs_byte(hd_info[dev].head,
599: (char *) &loc->heads);
600: put_fs_byte(hd_info[dev].sect,
601: (char *) &loc->sectors);
602: put_fs_word(hd_info[dev].cyl,
603: (short *) &loc->cylinders);
604: return 0;
605: default:
606: return -EINVAL;
607: }
608: }
1.1.1.7 root 609:
610: /*
611: * Releasing a block device means we sync() it, so that it can safely
612: * be forgotten about...
613: */
614: static void hd_release(struct inode * inode, struct file * file)
615: {
616: sync_dev(inode->i_rdev);
617: }
618:
619: static struct file_operations hd_fops = {
620: NULL, /* lseek - default */
621: block_read, /* read - general block-dev read */
622: block_write, /* write - general block-dev write */
623: NULL, /* readdir - bad */
624: NULL, /* select */
625: hd_ioctl, /* ioctl */
626: NULL, /* no special open code */
627: hd_release /* release */
628: };
629:
630: void hd_init(void)
631: {
632: blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
633: blkdev_fops[MAJOR_NR] = &hd_fops;
634: set_intr_gate(0x2E,&hd_interrupt);
635: outb_p(inb_p(0x21)&0xfb,0x21);
636: outb(inb_p(0xA1)&0xbf,0xA1);
637: timer_table[HD_TIMER].fn = hd_times_out;
638: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.