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 
swimiop_init(void)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 
swimiop_init_request(struct swim_iop_req * req)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 
swimiop_send_request(struct swim_iop_req * req)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 
swimiop_receive(struct iop_msg * msg,struct pt_regs * regs)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 
swimiop_status_update(int drive_num,struct swim_drvstatus * ds)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 
swimiop_eject(struct floppy_state * fs)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 
floppy_ioctl(struct inode * inode,struct file * filp,unsigned int cmd,unsigned long param)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 
floppy_open(struct inode * inode,struct file * filp)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 
floppy_release(struct inode * inode,struct file * filp)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 
floppy_check_change(kdev_t dev)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 
floppy_revalidate(kdev_t dev)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 
floppy_off(unsigned int nr)449 static void floppy_off(unsigned int nr)
450 {
451 }
452 
grab_drive(struct floppy_state * fs,enum swim_state state,int interruptible)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 
release_drive(struct floppy_state * fs)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 
set_timeout(struct floppy_state * fs,int nticks,void (* proc)(unsigned long))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 
do_fd_request(request_queue_t * q)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 
fd_request_complete(struct swim_iop_req * req)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 
fd_request_timeout(unsigned long data)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 
start_request(struct floppy_state * fs)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 }
610