Annotation of linux/kernel/blk_drv/floppy.c, revision 1.1

1.1     ! root        1: /*
        !             2:  *  linux/kernel/floppy.c
        !             3:  *
        !             4:  *  (C) 1991  Linus Torvalds
        !             5:  */
        !             6: 
        !             7: /*
        !             8:  * As with hd.c, all routines within this file can (and will) be called
        !             9:  * by interrupts, so extreme caution is needed. A hardware interrupt
        !            10:  * handler may not sleep, or a kernel panic will happen. Thus I cannot
        !            11:  * call "floppy-on" directly, but have to set a special timer interrupt
        !            12:  * etc.
        !            13:  *
        !            14:  * Also, I'm not certain this works on more than 1 floppy. Bugs may
        !            15:  * abund.
        !            16:  */
        !            17: 
        !            18: #include <linux/sched.h>
        !            19: #include <linux/fs.h>
        !            20: #include <linux/kernel.h>
        !            21: #include <linux/fdreg.h>
        !            22: #include <asm/system.h>
        !            23: #include <asm/io.h>
        !            24: #include <asm/segment.h>
        !            25: 
        !            26: #define MAJOR_NR 2
        !            27: #include "blk.h"
        !            28: 
        !            29: static void reset_floppy(void);
        !            30: static void seek_interrupt(void);
        !            31: static void rw_interrupt(void);
        !            32: 
        !            33: extern unsigned char current_DOR;
        !            34: extern unsigned char selected;
        !            35: 
        !            36: #define immoutb_p(val,port) \
        !            37: __asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port))
        !            38: 
        !            39: #define TYPE(x) ((x)>>2)
        !            40: #define DRIVE(x) ((x)&0x03)
        !            41: /*
        !            42:  * Note that MAX_ERRORS=10 doesn't imply that we retry every bad read
        !            43:  * max 10 times - some types of errors increase the errorcount by 2,
        !            44:  * so we might actually retry only 6-7 times before giving up.
        !            45:  */
        !            46: #define MAX_ERRORS 10
        !            47: 
        !            48: /*
        !            49:  * globals used by 'result()'
        !            50:  */
        !            51: #define MAX_REPLIES 7
        !            52: static unsigned char reply_buffer[MAX_REPLIES];
        !            53: #define ST0 (reply_buffer[0])
        !            54: #define ST1 (reply_buffer[1])
        !            55: #define ST2 (reply_buffer[2])
        !            56: #define ST3 (reply_buffer[3])
        !            57: 
        !            58: /*
        !            59:  * This struct defines the different floppy types. Unlike minix
        !            60:  * linux doesn't have a "search for right type"-type, as the code
        !            61:  * for that is convoluted and weird.
        !            62:  *
        !            63:  * The 'stretch' tells if the tracks need to be boubled for some
        !            64:  * types (ie 360kB diskette in 1.2MB drive etc). Others should
        !            65:  * be self-explanatory.
        !            66:  */
        !            67: static struct floppy_struct {
        !            68:        int size, sect, head, track, stretch;
        !            69:        unsigned char gap,rate,spec1;
        !            70: } floppy_type[] = {
        !            71:        {    0, 0,0, 0,0,0x00,0x00,0x00 },      /* no testing */
        !            72:        {  720, 9,2,40,0,0x2A,0x02,0xDF },      /* 360kB PC diskettes */
        !            73:        { 2400,15,2,80,0,0x1B,0x00,0xDF },      /* 1.2 MB AT-diskettes */
        !            74:        {  720, 9,2,40,1,0x2A,0x02,0xDF },      /* 360kB in 720kB drive */
        !            75:        { 1440, 9,2,80,0,0x2A,0x02,0xDF },      /* 3.5" 720kB diskette */
        !            76:        {  720, 9,2,40,1,0x23,0x01,0xDF },      /* 360kB in 1.2MB drive */
        !            77:        { 1440, 9,2,80,0,0x23,0x01,0xDF },      /* 720kB in 1.2MB drive */
        !            78:        { 2880,18,2,80,0,0x1B,0x00,0xCF },      /* 1.44MB diskette */
        !            79: };
        !            80: /*
        !            81:  * Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps
        !            82:  * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
        !            83:  * H is head unload time (1=16ms, 2=32ms, etc)
        !            84:  *
        !            85:  * Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc)
        !            86:  * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA).
        !            87:  */
        !            88: 
        !            89: extern void floppy_interrupt(void);
        !            90: extern char tmp_floppy_area[1024];
        !            91: 
        !            92: /*
        !            93:  * These are global variables, as that's the easiest way to give
        !            94:  * information to interrupts. They are the data used for the current
        !            95:  * request.
        !            96:  */
        !            97: static int cur_spec1 = -1;
        !            98: static int cur_rate = -1;
        !            99: static struct floppy_struct * floppy = floppy_type;
        !           100: static unsigned char current_drive = 0;
        !           101: static unsigned char sector = 0;
        !           102: static unsigned char head = 0;
        !           103: static unsigned char track = 0;
        !           104: static unsigned char seek_track = 0;
        !           105: static unsigned char command = 0;
        !           106: 
        !           107: /*
        !           108:  * floppy-change is never called from an interrupt, so we can relax a bit
        !           109:  * here.
        !           110:  */
        !           111: int floppy_change(unsigned int nr)
        !           112: {
        !           113:        floppy_on(nr);
        !           114:        floppy_select(nr);
        !           115:        if (inb(FD_DIR) & 0x80) {
        !           116:                floppy_off(nr);
        !           117:                return 1;
        !           118:        }
        !           119:        floppy_off(nr);
        !           120:        return 0;
        !           121: }
        !           122: 
        !           123: #define copy_buffer(from,to) \
        !           124: __asm__("cld ; rep ; movsl" \
        !           125:        ::"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \
        !           126:        :"cx","di","si")
        !           127: 
        !           128: static void setup_DMA(void)
        !           129: {
        !           130:        long addr = (long) CURRENT->buffer;
        !           131: 
        !           132:        if (addr >= 0x100000) {
        !           133:                addr = (long) tmp_floppy_area;
        !           134:                if (command == FD_WRITE)
        !           135:                        copy_buffer(CURRENT->buffer,tmp_floppy_area);
        !           136:        }
        !           137: /* mask DMA 2 */
        !           138:        immoutb_p(4|2,10);
        !           139: /* output command byte. I don't know why, but everyone (minix, */
        !           140: /* sanches & canton) output this twice, first to 12 then to 11 */
        !           141:        __asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t"
        !           142:        "outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:"::
        !           143:        "a" ((char) ((command == FD_READ)?DMA_READ:DMA_WRITE)));
        !           144: /* 8 low bits of addr */
        !           145:        immoutb_p(addr,4);
        !           146:        addr >>= 8;
        !           147: /* bits 8-15 of addr */
        !           148:        immoutb_p(addr,4);
        !           149:        addr >>= 8;
        !           150: /* bits 16-19 of addr */
        !           151:        immoutb_p(addr,0x81);
        !           152: /* low 8 bits of count-1 (1024-1=0x3ff) */
        !           153:        immoutb_p(0xff,5);
        !           154: /* high 8 bits of count-1 */
        !           155:        immoutb_p(3,5);
        !           156: /* activate DMA 2 */
        !           157:        immoutb_p(0|2,10);
        !           158: }
        !           159: 
        !           160: static void output_byte(char byte)
        !           161: {
        !           162:        int counter;
        !           163:        unsigned char status;
        !           164: 
        !           165:        for(counter = 0 ; counter < 10000 ; counter++) {
        !           166:                status = inb(FD_STATUS) & (STATUS_READY | STATUS_DIR);
        !           167:                if (status == STATUS_READY) {
        !           168:                        outb(byte,FD_DATA);
        !           169:                        return;
        !           170:                }
        !           171:        }
        !           172:        printk("Unable to send byte to FDC\n\r");
        !           173: }
        !           174: 
        !           175: static int result(void)
        !           176: {
        !           177:        int i = 0, counter, status;
        !           178: 
        !           179:        for (counter = 0 ; counter < 10000 ; counter++) {
        !           180:                status = inb(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY);
        !           181:                if (status == STATUS_READY)
        !           182:                        return i;
        !           183:                if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
        !           184:                        if (i >= MAX_REPLIES)
        !           185:                                break;
        !           186:                        reply_buffer[i++] = inb(FD_DATA);
        !           187:                }
        !           188:        }
        !           189:        printk("Getstatus times out\n\r");
        !           190:        return -1;
        !           191: }
        !           192: 
        !           193: /*
        !           194:  * This is the routine called after every seek (or recalibrate) interrupt
        !           195:  * from the floppy controller. Note that the "unexpected interrupt" routine
        !           196:  * also does a recalibrate, but doesn't come here.
        !           197:  */
        !           198: static void seek_interrupt(void)
        !           199: {
        !           200: /* sense drive status */
        !           201:        output_byte(FD_SENSEI);
        !           202:        if (result() != 2 || (ST0 & 0xF8) != 0x20) {
        !           203:                CURRENT->errors++;
        !           204:                if (CURRENT->errors > MAX_ERRORS) {
        !           205:                        floppy_deselect(current_drive);
        !           206:                        end_request(0);
        !           207:                        reset_floppy();
        !           208:                        return;
        !           209:                }
        !           210:                output_byte(FD_RECALIBRATE);
        !           211:                output_byte(head<<2 | current_drive);
        !           212:                return;
        !           213:        }
        !           214: /* are we on the right track? */
        !           215:        if (ST1 != seek_track) {
        !           216:                CURRENT->errors++;
        !           217:                if (CURRENT->errors > MAX_ERRORS) {
        !           218:                        floppy_deselect(current_drive);
        !           219:                        end_request(0);
        !           220:                        reset_floppy();
        !           221:                        return;
        !           222:                }
        !           223:                output_byte(FD_SEEK);
        !           224:                output_byte(head<<2 | current_drive);
        !           225:                output_byte(seek_track);
        !           226:                return;
        !           227:        }
        !           228: /* yes - set up DMA and read/write command */
        !           229:        setup_DMA();
        !           230:        do_floppy = rw_interrupt;
        !           231:        output_byte(command);
        !           232:        output_byte(head<<2 | current_drive);
        !           233:        output_byte(track);
        !           234:        output_byte(head);
        !           235:        output_byte(sector);
        !           236:        output_byte(2);         /* sector size = 512 */
        !           237:        output_byte(floppy->sect);
        !           238:        output_byte(floppy->gap);
        !           239:        output_byte(0xFF);      /* sector size (0xff when n!=0 ?) */
        !           240: }
        !           241: 
        !           242: /*
        !           243:  * Ok, this interrupt is called after a DMA read/write has succeeded,
        !           244:  * so we check the results, and copy any buffers.
        !           245:  */
        !           246: static void rw_interrupt(void)
        !           247: {
        !           248:        if (result() != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) ||
        !           249:            (ST2 & 0x73)) {
        !           250:                CURRENT->errors++;
        !           251:                if (CURRENT->errors > MAX_ERRORS || (ST1 & 0x02)) {
        !           252:                        if (ST1 & 0x02)
        !           253:                                printk("Drive %d is write protected\n\r",
        !           254:                                current_drive);
        !           255:                        floppy_deselect(current_drive);
        !           256:                        end_request(0);
        !           257:                        do_fd_request();
        !           258:                        return;
        !           259:                }
        !           260:                do_floppy = seek_interrupt;
        !           261:                output_byte(FD_RECALIBRATE);
        !           262:                output_byte(head<<2 | current_drive);
        !           263:                return;
        !           264:        }
        !           265:        if (command == FD_READ && (long)(CURRENT->buffer) >= 0x100000)
        !           266:                copy_buffer(tmp_floppy_area,CURRENT->buffer);
        !           267:        floppy_deselect(current_drive);
        !           268:        end_request(1);
        !           269:        do_fd_request();
        !           270: }
        !           271: 
        !           272: /*
        !           273:  * This routine is called when everything should be correctly set up
        !           274:  * for the transfer (ie floppy motor is on and the correct floppy is
        !           275:  * selected).
        !           276:  */
        !           277: static void transfer(void)
        !           278: {
        !           279:        if (cur_spec1 != floppy->spec1) {
        !           280:                cur_spec1 = floppy->spec1;
        !           281:                output_byte(FD_SPECIFY);
        !           282:                output_byte(cur_spec1);         /* hut etc */
        !           283:                output_byte(6);                 /* Head load time =6ms, DMA */
        !           284:        }
        !           285:        if (cur_rate != floppy->rate)
        !           286:                outb_p(cur_rate = floppy->rate,FD_DCR);
        !           287:        do_floppy = seek_interrupt;
        !           288:        if (seek_track) {
        !           289:                output_byte(FD_SEEK);
        !           290:                output_byte(head<<2 | current_drive);
        !           291:                output_byte(seek_track);
        !           292:        } else {
        !           293:                output_byte(FD_RECALIBRATE);
        !           294:                output_byte(head<<2 | current_drive);
        !           295:        }
        !           296: }
        !           297: 
        !           298: /*
        !           299:  * Special case - used after a unexpected interrupt (or reset)
        !           300:  */
        !           301: static void recal_interrupt(void)
        !           302: {
        !           303:        do_floppy = NULL;
        !           304:        output_byte(FD_SENSEI);
        !           305:        if (result()!=2 || (ST0 & 0xE0) == 0x60) {
        !           306:                reset_floppy();
        !           307:                return;
        !           308:        }
        !           309:        do_fd_request();
        !           310: }
        !           311: 
        !           312: void unexpected_floppy_interrupt(void)
        !           313: {
        !           314:        output_byte(FD_SENSEI);
        !           315:        if (result()!=2 || (ST0 & 0xE0) == 0x60) {
        !           316:                reset_floppy();
        !           317:                return;
        !           318:        }
        !           319:        do_floppy = recal_interrupt;
        !           320:        output_byte(FD_RECALIBRATE);
        !           321:        output_byte(head<<2 | current_drive);
        !           322: }
        !           323: 
        !           324: static void reset_interrupt(void)
        !           325: {
        !           326:        output_byte(FD_SENSEI);
        !           327:        (void) result();
        !           328:        do_floppy = recal_interrupt;
        !           329:        output_byte(FD_RECALIBRATE);
        !           330:        output_byte(head<<2 | current_drive);
        !           331: }
        !           332: 
        !           333: static void reset_floppy(void)
        !           334: {
        !           335:        printk("Reset-floppy called\n\r");
        !           336:        do_floppy = reset_interrupt;
        !           337:        outb_p(0,FD_DOR);
        !           338:        outb(current_DOR,FD_DOR);
        !           339: }
        !           340: 
        !           341: static void floppy_on_interrupt(void)
        !           342: {
        !           343: /* We cannot do a floppy-select, as that might sleep. We just force it */
        !           344:        selected = 1;
        !           345:        current_DOR &= 0xFC;
        !           346:        current_DOR |= current_drive;
        !           347:        transfer();
        !           348: }
        !           349: 
        !           350: void do_fd_request(void)
        !           351: {
        !           352:        unsigned int block;
        !           353: 
        !           354:        INIT_REQUEST;
        !           355:        floppy = (MINOR(CURRENT->dev)>>2) + floppy_type;
        !           356:        current_drive = CURRENT_DEV;
        !           357:        block = CURRENT->sector;
        !           358:        if (block+2 > floppy->size) {
        !           359:                end_request(0);
        !           360:                goto repeat;
        !           361:        }
        !           362:        sector = block % floppy->sect;
        !           363:        block /= floppy->sect;
        !           364:        head = block % floppy->head;
        !           365:        track = block / floppy->head;
        !           366:        seek_track = track << floppy->stretch;
        !           367:        sector++;
        !           368:        if (CURRENT->cmd == READ)
        !           369:                command = FD_READ;
        !           370:        else if (CURRENT->cmd == WRITE)
        !           371:                command = FD_WRITE;
        !           372:        else
        !           373:                panic("do_fd_request: unknown command");
        !           374:        add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt);
        !           375: }
        !           376: 
        !           377: void floppy_init(void)
        !           378: {
        !           379:        blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
        !           380:        set_intr_gate(0x26,&floppy_interrupt);
        !           381:        outb(inb_p(0x21)&~0x40,0x21);
        !           382: }

unix.superglobalmegacorp.com

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