Annotation of linux/kernel/blk_drv/hd.c, revision 1.1.1.6

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.