linux-old/drivers/block/swim_iop.c
<<
>>
Prefs
   1/*
   2 * Driver for the SWIM (Super Woz Integrated Machine) IOP
   3 * floppy controller on the Macintosh IIfx and Quadra 900/950
   4 *
   5 * Written by Joshua M. Thompson (funaho@jurai.org)
   6 * based on the SWIM3 driver (c) 1996 by Paul Mackerras.
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * as published by the Free Software Foundation; either version
  11 * 2 of the License, or (at your option) any later version.
  12 *
  13 * 1999-06-12 (jmt) - Initial implementation.
  14 */
  15
  16/*
  17 * -------------------
  18 * Theory of Operation
  19 * -------------------
  20 *
  21 * Since the SWIM IOP is message-driven we implement a simple request queue
  22 * system.  One outstanding request may be queued at any given time (this is
  23 * an IOP limitation); only when that request has completed can a new request
  24 * be sent.
  25 */
  26
  27/* This has to be defined before some of the #includes below */
  28
  29#define MAJOR_NR  FLOPPY_MAJOR
  30
  31#include <linux/stddef.h>
  32#include <linux/kernel.h>
  33#include <linux/sched.h>
  34#include <linux/timer.h>
  35#include <linux/delay.h>
  36#include <linux/fd.h>
  37#include <linux/blk.h>
  38#include <linux/ioctl.h>
  39#include <asm/io.h>
  40#include <asm/uaccess.h>
  41#include <asm/mac_iop.h>
  42#include <asm/swim_iop.h>
  43
  44#define DRIVER_VERSION "Version 0.1 (1999-06-12)"
  45
  46#define MAX_FLOPPIES    4
  47
  48enum swim_state {
  49        idle,
  50        available,
  51        revalidating,
  52        transferring,
  53        ejecting
  54};
  55
  56struct floppy_state {
  57        enum swim_state state;
  58        int     drive_num;      /* device number */
  59        int     secpercyl;      /* disk geometry information */
  60        int     secpertrack;
  61        int     total_secs;
  62        int     write_prot;     /* 1 if write-protected, 0 if not, -1 dunno */
  63        int     ref_count;
  64        struct timer_list timeout;
  65        int     ejected;
  66        struct wait_queue *wait;
  67        int     wanted;
  68        int     timeout_pending;
  69};
  70
  71struct swim_iop_req {
  72        int     sent;
  73        int     complete;
  74        __u8    command[32];
  75        struct floppy_state *fs;
  76        void    (*done)(struct swim_iop_req *);
  77};
  78
  79static struct swim_iop_req *current_req;
  80static int floppy_count;
  81
  82static struct floppy_state floppy_states[MAX_FLOPPIES];
  83
  84static int floppy_blocksizes[2] = {512,512};
  85static int floppy_sizes[2] = {2880,2880};
  86
  87static char *drive_names[7] = {
  88        "not installed",        /* DRV_NONE    */
  89        "unknown (1)",          /* DRV_UNKNOWN */
  90        "a 400K drive",         /* DRV_400K    */
  91        "an 800K drive"         /* DRV_800K    */
  92        "unknown (4)",          /* ????        */
  93        "an FDHD",              /* DRV_FDHD    */
  94        "unknown (6)",          /* ????        */
  95        "an Apple HD20"         /* DRV_HD20    */
  96};
  97
  98int swimiop_init(void);
  99static void swimiop_init_request(struct swim_iop_req *);
 100static int swimiop_send_request(struct swim_iop_req *);
 101static void swimiop_receive(struct iop_msg *, struct pt_regs *);
 102static void swimiop_status_update(int, struct swim_drvstatus *);
 103static int swimiop_eject(struct floppy_state *fs);
 104
 105static int floppy_ioctl(struct inode *inode, struct file *filp,
 106                        unsigned int cmd, unsigned long param);
 107static int floppy_open(struct inode *inode, struct file *filp);
 108static int floppy_release(struct inode *inode, struct file *filp);
 109static int floppy_check_change(kdev_t dev);
 110static int floppy_revalidate(kdev_t dev);
 111static int grab_drive(struct floppy_state *fs, enum swim_state state,
 112                      int interruptible);
 113static void release_drive(struct floppy_state *fs);
 114static void set_timeout(struct floppy_state *fs, int nticks,
 115                        void (*proc)(unsigned long));
 116static void fd_request_timeout(unsigned long);
 117static void do_fd_request(request_queue_t * q);
 118static void start_request(struct floppy_state *fs);
 119
 120static struct block_device_operations floppy_fops = {
 121        open:                   floppy_open,
 122        release:                floppy_release,
 123        ioctl:                  floppy_ioctl,
 124        check_media_change:     floppy_check_change,
 125        revalidate:             floppy_revalidate,
 126};
 127
 128/*
 129 * SWIM IOP initialization
 130 */
 131
 132int swimiop_init(void)
 133{
 134        volatile struct swim_iop_req req;
 135        struct swimcmd_status *cmd = (struct swimcmd_status *) &req.command[0];
 136        struct swim_drvstatus *ds = &cmd->status;
 137        struct floppy_state *fs;
 138        int i;
 139
 140        current_req = NULL;
 141        floppy_count = 0;
 142
 143        if (!iop_ism_present) return -ENODEV;
 144
 145        if (register_blkdev(MAJOR_NR, "fd", &floppy_fops)) {
 146                printk(KERN_ERR "SWIM-IOP: Unable to get major %d for floppy\n",
 147                       MAJOR_NR);
 148                return -EBUSY;
 149        }
 150        blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
 151        blksize_size[MAJOR_NR] = floppy_blocksizes;
 152        blk_size[MAJOR_NR] = floppy_sizes;
 153
 154        printk("SWIM-IOP: %s by Joshua M. Thompson (funaho@jurai.org)\n",
 155                DRIVER_VERSION);
 156
 157        if (iop_listen(SWIM_IOP, SWIM_CHAN, swimiop_receive, "SWIM") != 0) {
 158                printk(KERN_ERR "SWIM-IOP: IOP channel already in use; can't initialize.\n");
 159                return -EBUSY;
 160        }
 161
 162        printk(KERN_ERR "SWIM_IOP: probing for installed drives.\n");
 163
 164        for (i = 0 ; i < MAX_FLOPPIES ; i++) {
 165                memset(&floppy_states[i], 0, sizeof(struct floppy_state));
 166                fs = &floppy_states[floppy_count];
 167
 168                swimiop_init_request(&req);
 169                cmd->code = CMD_STATUS;
 170                cmd->drive_num = i + 1;
 171                if (swimiop_send_request(&req) != 0) continue;
 172                while (!req.complete);
 173                if (cmd->error != 0) {
 174                        printk(KERN_ERR "SWIM-IOP: probe on drive %d returned error %d\n", i, (uint) cmd->error);
 175                        continue;
 176                }
 177                if (ds->installed != 0x01) continue;
 178                printk("SWIM-IOP: drive %d is %s (%s, %s, %s, %s)\n", i,
 179                        drive_names[ds->info.type],
 180                        ds->info.external? "ext" : "int",
 181                        ds->info.scsi? "scsi" : "floppy",
 182                        ds->info.fixed? "fixed" : "removable",
 183                        ds->info.secondary? "secondary" : "primary");
 184                swimiop_status_update(floppy_count, ds);
 185                fs->state = idle;
 186
 187                init_timer(&fs->timeout);
 188                floppy_count++;
 189        }
 190        printk("SWIM-IOP: detected %d installed drives.\n", floppy_count);
 191
 192        do_floppy = NULL;
 193
 194        return 0;
 195}
 196
 197static void swimiop_init_request(struct swim_iop_req *req)
 198{
 199        req->sent = 0;
 200        req->complete = 0;
 201        req->done = NULL;
 202}
 203
 204static int swimiop_send_request(struct swim_iop_req *req)
 205{
 206        unsigned long cpu_flags;
 207        int err;
 208
 209        /* It's doubtful an interrupt routine would try to send */
 210        /* a SWIM request, but I'd rather play it safe here.    */
 211
 212        save_flags(cpu_flags);
 213        cli();
 214
 215        if (current_req != NULL) {
 216                restore_flags(cpu_flags);
 217                return -ENOMEM;
 218        }
 219
 220        current_req = req;
 221
 222        /* Interrupts should be back on for iop_send_message() */
 223
 224        restore_flags(cpu_flags);
 225
 226        err = iop_send_message(SWIM_IOP, SWIM_CHAN, (void *) req,
 227                                sizeof(req->command), (__u8 *) &req->command[0],
 228                                swimiop_receive);
 229
 230        /* No race condition here; we own current_req at this point */
 231
 232        if (err) {
 233                current_req = NULL;
 234        } else {
 235                req->sent = 1;
 236        }
 237        return err;
 238}
 239
 240/*
 241 * Receive a SWIM message from the IOP.
 242 *
 243 * This will be called in two cases:
 244 *
 245 * 1. A message has been successfully sent to the IOP.
 246 * 2. An unsolicited message was received from the IOP.
 247 */
 248
 249void swimiop_receive(struct iop_msg *msg, struct pt_regs *regs)
 250{
 251        struct swim_iop_req *req;
 252        struct swimmsg_status *sm;
 253        struct swim_drvstatus *ds;
 254
 255        req = current_req;
 256
 257        switch(msg->status) {
 258                case IOP_MSGSTATUS_COMPLETE:
 259                        memcpy(&req->command[0], &msg->reply[0], sizeof(req->command));
 260                        req->complete = 1;
 261                        if (req->done) (*req->done)(req);
 262                        current_req = NULL;
 263                        break;
 264                case IOP_MSGSTATUS_UNSOL:
 265                        sm = (struct swimmsg_status *) &msg->message[0];
 266                        ds = &sm->status;
 267                        swimiop_status_update(sm->drive_num, ds);
 268                        iop_complete_message(msg);
 269                        break;
 270        }
 271}
 272
 273static void swimiop_status_update(int drive_num, struct swim_drvstatus *ds)
 274{
 275        struct floppy_state *fs = &floppy_states[drive_num];
 276
 277        fs->write_prot = (ds->write_prot == 0x80);
 278        if ((ds->disk_in_drive != 0x01) && (ds->disk_in_drive != 0x02)) {
 279                fs->ejected = 1;
 280        } else {
 281                fs->ejected = 0;
 282        }
 283        switch(ds->info.type) {
 284                case DRV_400K:
 285                        fs->secpercyl = 10;
 286                        fs->secpertrack = 10;
 287                        fs->total_secs = 800;
 288                        break;
 289                case DRV_800K:
 290                        fs->secpercyl = 20;
 291                        fs->secpertrack = 10;
 292                        fs->total_secs = 1600;
 293                        break;
 294                case DRV_FDHD:
 295                        fs->secpercyl = 36;
 296                        fs->secpertrack = 18;
 297                        fs->total_secs = 2880;
 298                        break;
 299                default:
 300                        fs->secpercyl = 0;
 301                        fs->secpertrack = 0;
 302                        fs->total_secs = 0;
 303                        break;
 304        }
 305}
 306
 307static int swimiop_eject(struct floppy_state *fs)
 308{
 309        int err, n;
 310        struct swim_iop_req req;
 311        struct swimcmd_eject *cmd = (struct swimcmd_eject *) &req.command[0];
 312
 313        err = grab_drive(fs, ejecting, 1);
 314        if (err) return err;
 315
 316        swimiop_init_request(&req);
 317        cmd->code = CMD_EJECT;
 318        cmd->drive_num = fs->drive_num;
 319        err = swimiop_send_request(&req);
 320        if (err) {
 321                release_drive(fs);
 322                return err;
 323        }
 324        for (n = 2*HZ; n > 0; --n) {
 325                if (req.complete) break;
 326                if (signal_pending(current)) {
 327                        err = -EINTR;
 328                        break;
 329                }
 330                current->state = TASK_INTERRUPTIBLE;
 331                schedule_timeout(1);
 332        }
 333        release_drive(fs);
 334        return cmd->error;
 335}
 336
 337static struct floppy_struct floppy_type =
 338        { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL };    /*  7 1.44MB 3.5"   */
 339
 340static int floppy_ioctl(struct inode *inode, struct file *filp,
 341                        unsigned int cmd, unsigned long param)
 342{
 343        struct floppy_state *fs;
 344        int err;
 345        int devnum = MINOR(inode->i_rdev);
 346
 347        if (devnum >= floppy_count)
 348                return -ENODEV;
 349                
 350        if ((cmd & 0x80) && !suser())
 351                return -EPERM;
 352
 353        fs = &floppy_states[devnum];
 354
 355        switch (cmd) {
 356        case FDEJECT:
 357                if (fs->ref_count != 1)
 358                        return -EBUSY;
 359                err = swimiop_eject(fs);
 360                return err;
 361        case FDGETPRM:
 362                if (copy_to_user((void *) param, (void *) &floppy_type,
 363                                 sizeof(struct floppy_struct)))
 364                        return -EFAULT;
 365                return 0;
 366        }
 367        return -ENOTTY;
 368}
 369
 370static int floppy_open(struct inode *inode, struct file *filp)
 371{
 372        struct floppy_state *fs;
 373        int err;
 374        int devnum = MINOR(inode->i_rdev);
 375
 376        if (devnum >= floppy_count)
 377                return -ENODEV;
 378        if (filp == 0)
 379                return -EIO;
 380                
 381        fs = &floppy_states[devnum];
 382        err = 0;
 383        if (fs->ref_count == -1 || filp->f_flags & O_EXCL) return -EBUSY;
 384
 385        if (err == 0 && (filp->f_flags & O_NDELAY) == 0
 386            && (filp->f_mode & 3)) {
 387                check_disk_change(inode->i_rdev);
 388                if (fs->ejected)
 389                        err = -ENXIO;
 390        }
 391
 392        if (err == 0 && (filp->f_mode & 2)) {
 393                if (fs->write_prot)
 394                        err = -EROFS;
 395        }
 396
 397        if (err) return err;
 398
 399        if (filp->f_flags & O_EXCL)
 400                fs->ref_count = -1;
 401        else
 402                ++fs->ref_count;
 403
 404        return 0;
 405}
 406
 407static int floppy_release(struct inode *inode, struct file *filp)
 408{
 409        struct floppy_state *fs;
 410        int devnum = MINOR(inode->i_rdev);
 411
 412        if (devnum >= floppy_count)
 413                return -ENODEV;
 414
 415        fs = &floppy_states[devnum];
 416        if (fs->ref_count > 0) fs->ref_count--;
 417        return 0;
 418}
 419
 420static int floppy_check_change(kdev_t dev)
 421{
 422        struct floppy_state *fs;
 423        int devnum = MINOR(dev);
 424
 425        if (MAJOR(dev) != MAJOR_NR || (devnum >= floppy_count))
 426                return 0;
 427                
 428        fs = &floppy_states[devnum];
 429        return fs->ejected;
 430}
 431
 432static int floppy_revalidate(kdev_t dev)
 433{
 434        struct floppy_state *fs;
 435        int devnum = MINOR(dev);
 436
 437        if (MAJOR(dev) != MAJOR_NR || (devnum >= floppy_count))
 438                return 0;
 439
 440        fs = &floppy_states[devnum];
 441
 442        grab_drive(fs, revalidating, 0);
 443        /* yadda, yadda */
 444        release_drive(fs);
 445
 446        return 0;
 447}
 448
 449static void floppy_off(unsigned int nr)
 450{
 451}
 452
 453static int grab_drive(struct floppy_state *fs, enum swim_state state,
 454                      int interruptible)
 455{
 456        unsigned long flags;
 457
 458        save_flags(flags);
 459        cli();
 460        if (fs->state != idle) {
 461                ++fs->wanted;
 462                while (fs->state != available) {
 463                        if (interruptible && signal_pending(current)) {
 464                                --fs->wanted;
 465                                restore_flags(flags);
 466                                return -EINTR;
 467                        }
 468                        interruptible_sleep_on(&fs->wait);
 469                }
 470                --fs->wanted;
 471        }
 472        fs->state = state;
 473        restore_flags(flags);
 474        return 0;
 475}
 476
 477static void release_drive(struct floppy_state *fs)
 478{
 479        unsigned long flags;
 480
 481        save_flags(flags);
 482        cli();
 483        fs->state = idle;
 484        start_request(fs);
 485        restore_flags(flags);
 486}
 487
 488static void set_timeout(struct floppy_state *fs, int nticks,
 489                        void (*proc)(unsigned long))
 490{
 491        unsigned long flags;
 492
 493        save_flags(flags); cli();
 494        if (fs->timeout_pending)
 495                del_timer(&fs->timeout);
 496        fs->timeout.expires = jiffies + nticks;
 497        fs->timeout.function = proc;
 498        fs->timeout.data = (unsigned long) fs;
 499        add_timer(&fs->timeout);
 500        fs->timeout_pending = 1;
 501        restore_flags(flags);
 502}
 503
 504static void do_fd_request(request_queue_t * q)
 505{
 506        int i;
 507
 508        for (i = 0 ; i < floppy_count ; i++) {
 509                start_request(&floppy_states[i]);
 510        }
 511}
 512
 513static void fd_request_complete(struct swim_iop_req *req)
 514{
 515        struct floppy_state *fs = req->fs;
 516        struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req->command[0];
 517
 518        del_timer(&fs->timeout);
 519        fs->timeout_pending = 0;
 520        fs->state = idle;
 521        if (cmd->error) {
 522                printk(KERN_ERR "SWIM-IOP: error %d on read/write request.\n", cmd->error);
 523                end_request(0);
 524        } else {
 525                CURRENT->sector += cmd->num_blocks;
 526                CURRENT->current_nr_sectors -= cmd->num_blocks;
 527                if (CURRENT->current_nr_sectors <= 0) {
 528                        end_request(1);
 529                        return;
 530                }
 531        }
 532        start_request(fs);
 533}
 534
 535static void fd_request_timeout(unsigned long data)
 536{
 537        struct floppy_state *fs = (struct floppy_state *) data;
 538
 539        fs->timeout_pending = 0;
 540        end_request(0);
 541        fs->state = idle;
 542}
 543
 544static void start_request(struct floppy_state *fs)
 545{
 546        volatile struct swim_iop_req req;
 547        struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req.command[0];
 548
 549        if (fs->state == idle && fs->wanted) {
 550                fs->state = available;
 551                wake_up(&fs->wait);
 552                return;
 553        }
 554        while (!QUEUE_EMPTY && fs->state == idle) {
 555                if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
 556                        panic(DEVICE_NAME ": request list destroyed");
 557                if (CURRENT->bh && !buffer_locked(CURRENT->bh))
 558                        panic(DEVICE_NAME ": block not locked");
 559#if 0
 560                printk("do_fd_req: dev=%x cmd=%d sec=%ld nr_sec=%ld buf=%p\n",
 561                       kdev_t_to_nr(CURRENT->rq_dev), CURRENT->cmd,
 562                       CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer);
 563                printk("           rq_status=%d errors=%d current_nr_sectors=%ld\n",
 564                       CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors);
 565#endif
 566
 567                if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) {
 568                        end_request(0);
 569                        continue;
 570                }
 571                if (CURRENT->current_nr_sectors == 0) {
 572                        end_request(1);
 573                        continue;
 574                }
 575                if (fs->ejected) {
 576                        end_request(0);
 577                        continue;
 578                }
 579
 580                swimiop_init_request(&req);
 581                req.fs = fs;
 582                req.done = fd_request_complete;
 583
 584                if (CURRENT->cmd == WRITE) {
 585                        if (fs->write_prot) {
 586                                end_request(0);
 587                                continue;
 588                        }
 589                        cmd->code = CMD_WRITE;
 590                } else {
 591                        cmd->code = CMD_READ;
 592
 593                }
 594                cmd->drive_num = fs->drive_num;
 595                cmd->buffer = CURRENT->buffer;
 596                cmd->first_block = CURRENT->sector;
 597                cmd->num_blocks = CURRENT->current_nr_sectors;
 598
 599                if (swimiop_send_request(&req)) {
 600                        end_request(0);
 601                        continue;
 602                }
 603
 604                set_timeout(fs, HZ*CURRENT->current_nr_sectors,
 605                                fd_request_timeout);
 606
 607                fs->state = transferring;
 608        }
 609}
 610
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.