OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [block/] [swim_iop.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
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
 
48
enum swim_state {
49
        idle,
50
        available,
51
        revalidating,
52
        transferring,
53
        ejecting
54
};
55
 
56
struct 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
 
71
struct 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
 
79
static struct swim_iop_req *current_req;
80
static int floppy_count;
81
 
82
static struct floppy_state floppy_states[MAX_FLOPPIES];
83
 
84
static int floppy_blocksizes[2] = {512,512};
85
static int floppy_sizes[2] = {2880,2880};
86
 
87
static 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
 
98
int swimiop_init(void);
99
static void swimiop_init_request(struct swim_iop_req *);
100
static int swimiop_send_request(struct swim_iop_req *);
101
static void swimiop_receive(struct iop_msg *, struct pt_regs *);
102
static void swimiop_status_update(int, struct swim_drvstatus *);
103
static int swimiop_eject(struct floppy_state *fs);
104
 
105
static int floppy_ioctl(struct inode *inode, struct file *filp,
106
                        unsigned int cmd, unsigned long param);
107
static int floppy_open(struct inode *inode, struct file *filp);
108
static int floppy_release(struct inode *inode, struct file *filp);
109
static int floppy_check_change(kdev_t dev);
110
static int floppy_revalidate(kdev_t dev);
111
static int grab_drive(struct floppy_state *fs, enum swim_state state,
112
                      int interruptible);
113
static void release_drive(struct floppy_state *fs);
114
static void set_timeout(struct floppy_state *fs, int nticks,
115
                        void (*proc)(unsigned long));
116
static void fd_request_timeout(unsigned long);
117
static void do_fd_request(request_queue_t * q);
118
static void start_request(struct floppy_state *fs);
119
 
120
static 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
 
132
int 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
 
197
static void swimiop_init_request(struct swim_iop_req *req)
198
{
199
        req->sent = 0;
200
        req->complete = 0;
201
        req->done = NULL;
202
}
203
 
204
static 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
 
249
void 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
 
273
static 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
 
307
static 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
 
337
static struct floppy_struct floppy_type =
338
        { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL };     /*  7 1.44MB 3.5"   */
339
 
340
static 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
 
370
static 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
 
407
static 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
 
420
static 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
 
432
static 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
 
449
static void floppy_off(unsigned int nr)
450
{
451
}
452
 
453
static 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
 
477
static 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
 
488
static 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
 
504
static 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
 
513
static 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
 
535
static 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
 
544
static 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
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.