File: /usr/src/linux/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     	        err = copy_to_user((void *) param, (void *) &floppy_type,
363     				   sizeof(struct floppy_struct));
364     		return err;
365     	}
366     	return -ENOTTY;
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