~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/drivers/block/swim_iop.c

Version: ~ [ 2.2.5 ] ~ [ 2.4.1 ] ~ [ 2.4.9 ] ~ [ 2.6.17.10 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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                 err = copy_to_user((void *) param, (void *) &floppy_type,
363                                    sizeof(struct floppy_struct));
364                 return err;
365         }
366         return -ENOIOCTLCMD;
367 }
368 
369 static int floppy_open(struct inode *inode, struct file *filp)
370 {
371         struct floppy_state *fs;
372         int err;
373         int devnum = MINOR(inode->i_rdev);
374 
375         if (devnum >= floppy_count)
376                 return -ENODEV;
377         if (filp == 0)
378                 return -EIO;
379                 
380         fs = &floppy_states[devnum];
381         err = 0;
382         if (fs->ref_count == -1 || filp->f_flags & O_EXCL) return -EBUSY;
383 
384         if (err == 0 && (filp->f_flags & O_NDELAY) == 0
385             && (filp->f_mode & 3)) {
386                 check_disk_change(inode->i_rdev);
387                 if (fs->ejected)
388                         err = -ENXIO;
389         }
390 
391         if (err == 0 && (filp->f_mode & 2)) {
392                 if (fs->write_prot)
393                         err = -EROFS;
394         }
395 
396         if (err) return err;
397 
398         if (filp->f_flags & O_EXCL)
399                 fs->ref_count = -1;
400         else
401                 ++fs->ref_count;
402 
403         return 0;
404 }
405 
406 static int floppy_release(struct inode *inode, struct file *filp)
407 {
408         struct floppy_state *fs;
409         int devnum = MINOR(inode->i_rdev);
410 
411         if (devnum >= floppy_count)
412                 return -ENODEV;
413 
414         fs = &floppy_states[devnum];
415         if (fs->ref_count > 0) fs->ref_count--;
416         return 0;
417 }
418 
419 static int floppy_check_change(kdev_t dev)
420 {
421         struct floppy_state *fs;
422         int devnum = MINOR(dev);
423 
424         if (MAJOR(dev) != MAJOR_NR || (devnum >= floppy_count))
425                 return 0;
426                 
427         fs = &floppy_states[devnum];
428         return fs->ejected;
429 }
430 
431 static int floppy_revalidate(kdev_t dev)
432 {
433         struct floppy_state *fs;
434         int devnum = MINOR(dev);
435 
436         if (MAJOR(dev) != MAJOR_NR || (devnum >= floppy_count))
437                 return 0;
438 
439         fs = &floppy_states[devnum];
440 
441         grab_drive(fs, revalidating, 0);
442         /* yadda, yadda */
443         release_drive(fs);
444 
445         return 0;
446 }
447 
448 static void floppy_off(unsigned int nr)
449 {
450 }
451 
452 static int grab_drive(struct floppy_state *fs, enum swim_state state,
453                       int interruptible)
454 {
455         unsigned long flags;
456 
457         save_flags(flags);
458         cli();
459         if (fs->state != idle) {
460                 ++fs->wanted;
461                 while (fs->state != available) {
462                         if (interruptible && signal_pending(current)) {
463                                 --fs->wanted;
464                                 restore_flags(flags);
465                                 return -EINTR;
466                         }
467                         interruptible_sleep_on(&fs->wait);
468                 }
469                 --fs->wanted;
470         }
471         fs->state = state;
472         restore_flags(flags);
473         return 0;
474 }
475 
476 static void release_drive(struct floppy_state *fs)
477 {
478         unsigned long flags;
479 
480         save_flags(flags);
481         cli();
482         fs->state = idle;
483         start_request(fs);
484         restore_flags(flags);
485 }
486 
487 static void set_timeout(struct floppy_state *fs, int nticks,
488                         void (*proc)(unsigned long))
489 {
490         unsigned long flags;
491 
492         save_flags(flags); cli();
493         if (fs->timeout_pending)
494                 del_timer(&fs->timeout);
495         fs->timeout.expires = jiffies + nticks;
496         fs->timeout.function = proc;
497         fs->timeout.data = (unsigned long) fs;
498         add_timer(&fs->timeout);
499         fs->timeout_pending = 1;
500         restore_flags(flags);
501 }
502 
503 static void do_fd_request(request_queue_t * q)
504 {
505         int i;
506 
507         for (i = 0 ; i < floppy_count ; i++) {
508                 start_request(&floppy_states[i]);
509         }
510 }
511 
512 static void fd_request_complete(struct swim_iop_req *req)
513 {
514         struct floppy_state *fs = req->fs;
515         struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req->command[0];
516 
517         del_timer(&fs->timeout);
518         fs->timeout_pending = 0;
519         fs->state = idle;
520         if (cmd->error) {
521                 printk(KERN_ERR "SWIM-IOP: error %d on read/write request.\n", cmd->error);
522                 end_request(0);
523         } else {
524                 CURRENT->sector += cmd->num_blocks;
525                 CURRENT->current_nr_sectors -= cmd->num_blocks;
526                 if (CURRENT->current_nr_sectors <= 0) {
527                         end_request(1);
528                         return;
529                 }
530         }
531         start_request(fs);
532 }
533 
534 static void fd_request_timeout(unsigned long data)
535 {
536         struct floppy_state *fs = (struct floppy_state *) data;
537 
538         fs->timeout_pending = 0;
539         end_request(0);
540         fs->state = idle;
541 }
542 
543 static void start_request(struct floppy_state *fs)
544 {
545         volatile struct swim_iop_req req;
546         struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req.command[0];
547 
548         if (fs->state == idle && fs->wanted) {
549                 fs->state = available;
550                 wake_up(&fs->wait);
551                 return;
552         }
553         while (!QUEUE_EMPTY && fs->state == idle) {
554                 if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
555                         panic(DEVICE_NAME ": request list destroyed");
556                 if (CURRENT->bh && !buffer_locked(CURRENT->bh))
557                         panic(DEVICE_NAME ": block not locked");
558 #if 0
559                 printk("do_fd_req: dev=%x cmd=%d sec=%ld nr_sec=%ld buf=%p\n",
560                        kdev_t_to_nr(CURRENT->rq_dev), CURRENT->cmd,
561                        CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer);
562                 printk("           rq_status=%d errors=%d current_nr_sectors=%ld\n",
563                        CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors);
564 #endif
565 
566                 if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) {
567                         end_request(0);
568                         continue;
569                 }
570                 if (CURRENT->current_nr_sectors == 0) {
571                         end_request(1);
572                         continue;
573                 }
574                 if (fs->ejected) {
575                         end_request(0);
576                         continue;
577                 }
578 
579                 swimiop_init_request(&req);
580                 req.fs = fs;
581                 req.done = fd_request_complete;
582 
583                 if (CURRENT->cmd == WRITE) {
584                         if (fs->write_prot) {
585                                 end_request(0);
586                                 continue;
587                         }
588                         cmd->code = CMD_WRITE;
589                 } else {
590                         cmd->code = CMD_READ;
591 
592                 }
593                 cmd->drive_num = fs->drive_num;
594                 cmd->buffer = CURRENT->buffer;
595                 cmd->first_block = CURRENT->sector;
596                 cmd->num_blocks = CURRENT->current_nr_sectors;
597 
598                 if (swimiop_send_request(&req)) {
599                         end_request(0);
600                         continue;
601                 }
602 
603                 set_timeout(fs, HZ*CURRENT->current_nr_sectors,
604                                 fd_request_timeout);
605 
606                 fs->state = transferring;
607         }
608 }
609 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.