added a lot of printk output to ease writing of emulator
[linux-2.4.21-pre4.git] / drivers / block / swim_iop.c
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
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 }