root/kernel/blk_drv/hd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CMOS_READ
  2. extended_partition
  3. check_partition
  4. sys_setup
  5. read_timer
  6. controller_ready
  7. win_result
  8. hd_out
  9. drive_busy
  10. reset_controller
  11. reset_hd
  12. unexpected_hd_interrupt
  13. bad_rw_intr
  14. read_intr
  15. write_intr
  16. recal_intr
  17. hd_times_out
  18. do_hd_request
  19. hd_ioctl
  20. hd_release
  21. hd_init

   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  *  modified by Drew Eckhardt to check nr of hd's from the CMOS.
  14  *
  15  *  Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
  16  *  in the early extended-partition checks and added DM partitions
  17  */
  18 
  19 #include <errno.h>
  20 
  21 #include <linux/config.h>
  22 #include <linux/sched.h>
  23 #include <linux/timer.h>
  24 #include <linux/fs.h>
  25 #include <linux/kernel.h>
  26 #include <linux/hdreg.h>
  27 
  28 #define REALLY_SLOW_IO
  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 
  36 static inline unsigned char CMOS_READ(unsigned char addr)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38         outb_p(0x80|addr,0x70);
  39         return inb_p(0x71);
  40 }
  41 
  42 #define HD_DELAY        0
  43 
  44 /* Max read/write errors/sector */
  45 #define MAX_ERRORS      7
  46 #define MAX_HD          2
  47 
  48 static void recal_intr(void);
  49 static void bad_rw_intr(void);
  50 
  51 static int recalibrate = 0;
  52 static int reset = 0;
  53 
  54 #if (HD_DELAY > 0)
  55 unsigned long last_req, read_timer();
  56 #endif
  57 
  58 /*
  59  *  This struct defines the HD's and their types.
  60  */
  61 struct hd_i_struct {
  62         unsigned int head,sect,cyl,wpcom,lzone,ctl;
  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;
  75 } hd[MAX_HD<<6]={{0,0},};
  76 
  77 static int hd_sizes[MAX_HD<<6] = {0, };
  78 
  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);
  86 extern void rd_load(void);
  87 
  88 static unsigned int current_minor;
  89 
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 + 
 136                                hd[current_minor].nr_sects - 1);
 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 
 160 static void check_partition(unsigned int dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 161 {
 162         int i, minor = current_minor;
 163         struct buffer_head *bh;
 164         struct partition *p;
 165         unsigned long first_sector;
 166 
 167         first_sector = hd[MINOR(dev)].start_sect;
 168         if (!(bh = bread(dev,0))) {
 169                 printk("Unable to read partition table of device %04x\n",dev);
 170                 return;
 171         }
 172         printk("Drive %d:\n\r",minor >> 6);
 173         current_minor += 4;  /* first "extra" minor */
 174         if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
 175                 p = 0x1BE + (void *)bh->b_data;
 176                 for (i=1 ; i<=4 ; minor++,i++,p++) {
 177                         if (!(hd[minor].nr_sects = p->nr_sects))
 178                                 continue;
 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, 
 182                                hd[minor].start_sect + hd[minor].nr_sects - 1);
 183                         if ((current_minor & 0x3f) >= 60)
 184                                 continue;
 185                         if (p->sys_ind == EXTENDED_PARTITION) {
 186                                 extended_partition(0x0300 | minor);
 187                         }
 188                 }
 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;
 194                         for (i = 4 ; i < 16 ; i++, current_minor++) {
 195                                 p--;
 196                                 if ((current_minor & 0x3f) >= 60)
 197                                         break;
 198                                 if (!(p->start_sect && p->nr_sects))
 199                                         continue;
 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 + 
 207                                        hd[current_minor].nr_sects - 1);
 208                         }
 209                 }
 210         } else
 211                 printk("Bad partition table on dev %04x\n",dev);
 212         brelse(bh);
 213 }
 214 
 215 /* This may be used only once, enforced by 'static int callable' */
 216 int sys_setup(void * BIOS)
     /* [previous][next][first][last][top][bottom][index][help] */
 217 {
 218         static int callable = 1;
 219         int i,drive;
 220         unsigned char cmos_disks;
 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         }
 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;
 265 #endif
 266         for (i = 0 ; i < (MAX_HD<<6) ; i++) {
 267                 hd[i].start_sect = 0;
 268                 hd[i].nr_sects = 0;
 269         }
 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;
 273         for (drive=0 ; drive<NR_HD ; drive++) {
 274                 current_minor = 1+(drive<<6);
 275                 check_partition(0x0300+(drive<<6));
 276         }
 277         for (i=0 ; i<(MAX_HD<<6) ; i++)
 278                 hd_sizes[i] = hd[i].nr_sects>>1 ;
 279         blk_size[MAJOR_NR] = hd_sizes;
 280         if (NR_HD)
 281                 printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
 282         rd_load();
 283         mount_root();
 284         return (0);
 285 }
 286 
 287 #if (HD_DELAY > 0)
 288 unsigned long read_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 303 static int controller_ready(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 304 {
 305         int retries = 100000;
 306 
 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));
 312         return (retries);
 313 }
 314 
 315 static int win_result(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 316 {
 317         int i=inb_p(HD_STATUS);
 318 
 319         if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
 320                 == (READY_STAT | SEEK_STAT))
 321                 return(0); /* ok */
 322         if (i&1)
 323                 i=inb(HD_ERROR);
 324         return (1);
 325 }
 326 
 327 static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
     /* [previous][next][first][last][top][bottom][index][help] */
 328                 unsigned int head,unsigned int cyl,unsigned int cmd,
 329                 void (*intr_addr)(void))
 330 {
 331         unsigned short port;
 332 
 333         if (drive>1 || head>15)
 334                 panic("Trying to write bad sector");
 335 #if (HD_DELAY > 0)
 336         while (read_timer() - last_req < HD_DELAY)
 337                 /* nothing */;
 338 #endif
 339         if (reset || !controller_ready()) {
 340                 reset = 1;
 341                 return;
 342         }
 343         SET_INTR(intr_addr);
 344         outb_p(hd_info[drive].ctl,HD_CMD);
 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);
 352         outb_p(cmd,++port);
 353 }
 354 
 355 static int drive_busy(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 356 {
 357         unsigned int i;
 358         unsigned char c;
 359 
 360         for (i = 0; i < 500000 ; i++) {
 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         }
 366         printk("HD controller times out, c=%02x\n\r",c);
 367         return(1);
 368 }
 369 
 370 static void reset_controller(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 371 {
 372         int     i;
 373 
 374         printk("HD-controller reset\r\n");
 375         outb(4,HD_CMD);
 376         for(i = 0; i < 1000; i++) nop();
 377         outb(hd_info[0].ctl & 0x0f ,HD_CMD);
 378         if (drive_busy())
 379                 printk("HD-controller still busy\n\r");
 380         if ((i = inb(HD_ERROR)) != 1)
 381                 printk("HD-controller reset failed: %02x\n\r",i);
 382 }
 383 
 384 static void reset_hd(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 385 {
 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);
 402                 if (reset)
 403                         goto repeat;
 404         } else
 405                 do_hd_request();
 406 }
 407 
 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  */
 413 void unexpected_hd_interrupt(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 414 {
 415         printk("Unexpected HD interrupt\n\r");
 416         SET_TIMER;
 417 #if 0
 418         reset = 1;
 419         do_hd_request();
 420 #endif
 421 }
 422 
 423 static void bad_rw_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 424 {
 425         if (!CURRENT)
 426                 return;
 427         if (++CURRENT->errors >= MAX_ERRORS)
 428                 end_request(0);
 429         else if (CURRENT->errors > MAX_ERRORS/2)
 430                 reset = 1;
 431         else
 432                 recalibrate = 1;
 433 }
 434 
 435 #define STAT_MASK (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)
 436 #define STAT_OK (READY_STAT | SEEK_STAT)
 437 
 438 static void read_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 439 {
 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;
 447         port_read(HD_DATA,CURRENT->buffer,256);
 448         i = (unsigned) inb_p(HD_STATUS);
 449         if (!(i & BUSY_STAT))
 450                 if ((i & STAT_MASK) != STAT_OK)
 451                         goto bad_read;
 452         CURRENT->errors = 0;
 453         CURRENT->buffer += 512;
 454         CURRENT->sector++;
 455         i = --CURRENT->nr_sectors;
 456         if (!i || (CURRENT->bh && !(i&1)))
 457                 end_request(1);
 458         if (i > 0) {
 459                 SET_INTR(&read_intr);
 460                 return;
 461         }
 462 #if (HD_DELAY > 0)
 463         last_req = read_timer();
 464 #endif
 465         do_hd_request();
 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;
 473 }
 474 
 475 static void write_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 476 {
 477         int i;
 478 
 479         i = (unsigned) inb_p(HD_STATUS);
 480         if ((i & STAT_MASK) != STAT_OK)
 481                 goto bad_write;
 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)))
 488                 end_request(1);
 489         if (i > 0) {
 490                 SET_INTR(&write_intr);
 491                 port_write(HD_DATA,CURRENT->buffer,256);
 492         } else {
 493 #if (HD_DELAY > 0)
 494                 last_req = read_timer();
 495 #endif
 496                 do_hd_request();
 497         }
 498         return;
 499 bad_write:
 500         if (i & ERR_STAT)
 501                 i = (unsigned) inb(HD_ERROR);
 502         bad_rw_intr();
 503         do_hd_request();
 504         return;
 505 }
 506 
 507 static void recal_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 508 {
 509         if (win_result())
 510                 bad_rw_intr();
 511         do_hd_request();
 512 }
 513 
 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  */
 518 static void hd_times_out(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 519 {       
 520         do_hd = NULL;
 521         reset = 1;
 522         if (!CURRENT)
 523                 return;
 524         printk("HD timeout\n\r");
 525         cli();
 526         if (++CURRENT->errors >= MAX_ERRORS)
 527                 end_request(0);
 528         do_hd_request();
 529 }
 530 
 531 static void do_hd_request(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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;
 541         nsect = CURRENT->nr_sectors;
 542         if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects) {
 543                 end_request(0);
 544                 goto repeat;
 545         }
 546         block += hd[dev].start_sect;
 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;
 552         sec++;
 553         if (reset) {
 554                 recalibrate = 1;
 555                 reset_hd();
 556                 return;
 557         }
 558         if (recalibrate) {
 559                 recalibrate = 0;
 560                 hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
 561                 if (reset)
 562                         goto repeat;
 563                 return;
 564         }       
 565         if (CURRENT->cmd == WRITE) {
 566                 hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
 567                 if (reset)
 568                         goto repeat;
 569                 for(i=0 ; i<10000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
 570                         /* nothing */ ;
 571                 if (!r) {
 572                         bad_rw_intr();
 573                         goto repeat;
 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);
 578                 if (reset)
 579                         goto repeat;
 580         } else
 581                 panic("unknown hd-command");
 582 }
 583 
 584 static int hd_ioctl(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 585         unsigned int cmd, unsigned int arg)
 586 {
 587         struct hd_geometry *loc = (void *) arg;
 588         int dev;
 589 
 590         if (!loc || !inode)
 591                 return -EINVAL;
 592         dev = MINOR(inode->i_rdev) >> 6;
 593         if (dev >= NR_HD)
 594                 return -EINVAL;
 595         switch (cmd) {
 596                 case HDIO_REQ:
 597                         verify_area(loc, sizeof(*loc));
 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 }
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

/* [previous][next][first][last][top][bottom][index][help] */