Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at c016e2257acd00a7ffd87fa1eec896138563d1aa 579 lines 14 kB view raw
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#include <linux/stddef.h> 28#include <linux/kernel.h> 29#include <linux/sched.h> 30#include <linux/timer.h> 31#include <linux/delay.h> 32#include <linux/fd.h> 33#include <linux/ioctl.h> 34#include <linux/blkdev.h> 35#include <asm/io.h> 36#include <asm/uaccess.h> 37#include <asm/mac_iop.h> 38#include <asm/swim_iop.h> 39 40#define DRIVER_VERSION "Version 0.1 (1999-06-12)" 41 42#define MAX_FLOPPIES 4 43 44enum swim_state { 45 idle, 46 available, 47 revalidating, 48 transferring, 49 ejecting 50}; 51 52struct floppy_state { 53 enum swim_state state; 54 int drive_num; /* device number */ 55 int secpercyl; /* disk geometry information */ 56 int secpertrack; 57 int total_secs; 58 int write_prot; /* 1 if write-protected, 0 if not, -1 dunno */ 59 int ref_count; 60 struct timer_list timeout; 61 int ejected; 62 struct wait_queue *wait; 63 int wanted; 64 int timeout_pending; 65}; 66 67struct swim_iop_req { 68 int sent; 69 int complete; 70 __u8 command[32]; 71 struct floppy_state *fs; 72 void (*done)(struct swim_iop_req *); 73}; 74 75static struct swim_iop_req *current_req; 76static int floppy_count; 77 78static struct floppy_state floppy_states[MAX_FLOPPIES]; 79static DEFINE_SPINLOCK(swim_iop_lock); 80 81#define CURRENT elv_next_request(swim_queue) 82 83static char *drive_names[7] = { 84 "not installed", /* DRV_NONE */ 85 "unknown (1)", /* DRV_UNKNOWN */ 86 "a 400K drive", /* DRV_400K */ 87 "an 800K drive" /* DRV_800K */ 88 "unknown (4)", /* ???? */ 89 "an FDHD", /* DRV_FDHD */ 90 "unknown (6)", /* ???? */ 91 "an Apple HD20" /* DRV_HD20 */ 92}; 93 94int swimiop_init(void); 95static void swimiop_init_request(struct swim_iop_req *); 96static int swimiop_send_request(struct swim_iop_req *); 97static void swimiop_receive(struct iop_msg *, struct pt_regs *); 98static void swimiop_status_update(int, struct swim_drvstatus *); 99static int swimiop_eject(struct floppy_state *fs); 100 101static int floppy_ioctl(struct inode *inode, struct file *filp, 102 unsigned int cmd, unsigned long param); 103static int floppy_open(struct inode *inode, struct file *filp); 104static int floppy_release(struct inode *inode, struct file *filp); 105static int floppy_check_change(struct gendisk *disk); 106static int floppy_revalidate(struct gendisk *disk); 107static int grab_drive(struct floppy_state *fs, enum swim_state state, 108 int interruptible); 109static void release_drive(struct floppy_state *fs); 110static void set_timeout(struct floppy_state *fs, int nticks, 111 void (*proc)(unsigned long)); 112static void fd_request_timeout(unsigned long); 113static void do_fd_request(request_queue_t * q); 114static void start_request(struct floppy_state *fs); 115 116static struct block_device_operations floppy_fops = { 117 .open = floppy_open, 118 .release = floppy_release, 119 .ioctl = floppy_ioctl, 120 .media_changed = floppy_check_change, 121 .revalidate_disk= floppy_revalidate, 122}; 123 124static struct request_queue *swim_queue; 125/* 126 * SWIM IOP initialization 127 */ 128 129int swimiop_init(void) 130{ 131 volatile struct swim_iop_req req; 132 struct swimcmd_status *cmd = (struct swimcmd_status *) &req.command[0]; 133 struct swim_drvstatus *ds = &cmd->status; 134 struct floppy_state *fs; 135 int i; 136 137 current_req = NULL; 138 floppy_count = 0; 139 140 if (!iop_ism_present) 141 return -ENODEV; 142 143 if (register_blkdev(FLOPPY_MAJOR, "fd")) 144 return -EBUSY; 145 146 swim_queue = blk_init_queue(do_fd_request, &swim_iop_lock); 147 if (!swim_queue) { 148 unregister_blkdev(FLOPPY_MAJOR, "fd"); 149 return -ENOMEM; 150 } 151 152 printk("SWIM-IOP: %s by Joshua M. Thompson (funaho@jurai.org)\n", 153 DRIVER_VERSION); 154 155 if (iop_listen(SWIM_IOP, SWIM_CHAN, swimiop_receive, "SWIM") != 0) { 156 printk(KERN_ERR "SWIM-IOP: IOP channel already in use; can't initialize.\n"); 157 unregister_blkdev(FLOPPY_MAJOR, "fd"); 158 blk_cleanup_queue(swim_queue); 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 for (i = 0; i < floppy_count; i++) { 193 struct gendisk *disk = alloc_disk(1); 194 if (!disk) 195 continue; 196 disk->major = FLOPPY_MAJOR; 197 disk->first_minor = i; 198 disk->fops = &floppy_fops; 199 sprintf(disk->disk_name, "fd%d", i); 200 disk->private_data = &floppy_states[i]; 201 disk->queue = swim_queue; 202 set_capacity(disk, 2880 * 2); 203 add_disk(disk); 204 } 205 206 return 0; 207} 208 209static void swimiop_init_request(struct swim_iop_req *req) 210{ 211 req->sent = 0; 212 req->complete = 0; 213 req->done = NULL; 214} 215 216static int swimiop_send_request(struct swim_iop_req *req) 217{ 218 unsigned long flags; 219 int err; 220 221 /* It's doubtful an interrupt routine would try to send */ 222 /* a SWIM request, but I'd rather play it safe here. */ 223 224 local_irq_save(flags); 225 226 if (current_req != NULL) { 227 local_irq_restore(flags); 228 return -ENOMEM; 229 } 230 231 current_req = req; 232 233 /* Interrupts should be back on for iop_send_message() */ 234 235 local_irq_restore(flags); 236 237 err = iop_send_message(SWIM_IOP, SWIM_CHAN, (void *) req, 238 sizeof(req->command), (__u8 *) &req->command[0], 239 swimiop_receive); 240 241 /* No race condition here; we own current_req at this point */ 242 243 if (err) { 244 current_req = NULL; 245 } else { 246 req->sent = 1; 247 } 248 return err; 249} 250 251/* 252 * Receive a SWIM message from the IOP. 253 * 254 * This will be called in two cases: 255 * 256 * 1. A message has been successfully sent to the IOP. 257 * 2. An unsolicited message was received from the IOP. 258 */ 259 260void swimiop_receive(struct iop_msg *msg, struct pt_regs *regs) 261{ 262 struct swim_iop_req *req; 263 struct swimmsg_status *sm; 264 struct swim_drvstatus *ds; 265 266 req = current_req; 267 268 switch(msg->status) { 269 case IOP_MSGSTATUS_COMPLETE: 270 memcpy(&req->command[0], &msg->reply[0], sizeof(req->command)); 271 req->complete = 1; 272 if (req->done) (*req->done)(req); 273 current_req = NULL; 274 break; 275 case IOP_MSGSTATUS_UNSOL: 276 sm = (struct swimmsg_status *) &msg->message[0]; 277 ds = &sm->status; 278 swimiop_status_update(sm->drive_num, ds); 279 iop_complete_message(msg); 280 break; 281 } 282} 283 284static void swimiop_status_update(int drive_num, struct swim_drvstatus *ds) 285{ 286 struct floppy_state *fs = &floppy_states[drive_num]; 287 288 fs->write_prot = (ds->write_prot == 0x80); 289 if ((ds->disk_in_drive != 0x01) && (ds->disk_in_drive != 0x02)) { 290 fs->ejected = 1; 291 } else { 292 fs->ejected = 0; 293 } 294 switch(ds->info.type) { 295 case DRV_400K: 296 fs->secpercyl = 10; 297 fs->secpertrack = 10; 298 fs->total_secs = 800; 299 break; 300 case DRV_800K: 301 fs->secpercyl = 20; 302 fs->secpertrack = 10; 303 fs->total_secs = 1600; 304 break; 305 case DRV_FDHD: 306 fs->secpercyl = 36; 307 fs->secpertrack = 18; 308 fs->total_secs = 2880; 309 break; 310 default: 311 fs->secpercyl = 0; 312 fs->secpertrack = 0; 313 fs->total_secs = 0; 314 break; 315 } 316} 317 318static int swimiop_eject(struct floppy_state *fs) 319{ 320 int err, n; 321 struct swim_iop_req req; 322 struct swimcmd_eject *cmd = (struct swimcmd_eject *) &req.command[0]; 323 324 err = grab_drive(fs, ejecting, 1); 325 if (err) return err; 326 327 swimiop_init_request(&req); 328 cmd->code = CMD_EJECT; 329 cmd->drive_num = fs->drive_num; 330 err = swimiop_send_request(&req); 331 if (err) { 332 release_drive(fs); 333 return err; 334 } 335 for (n = 2*HZ; n > 0; --n) { 336 if (req.complete) break; 337 if (signal_pending(current)) { 338 err = -EINTR; 339 break; 340 } 341 current->state = TASK_INTERRUPTIBLE; 342 schedule_timeout(1); 343 } 344 release_drive(fs); 345 return cmd->error; 346} 347 348static struct floppy_struct floppy_type = 349 { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /* 7 1.44MB 3.5" */ 350 351static int floppy_ioctl(struct inode *inode, struct file *filp, 352 unsigned int cmd, unsigned long param) 353{ 354 struct floppy_state *fs = inode->i_bdev->bd_disk->private_data; 355 int err; 356 357 if ((cmd & 0x80) && !capable(CAP_SYS_ADMIN)) 358 return -EPERM; 359 360 switch (cmd) { 361 case FDEJECT: 362 if (fs->ref_count != 1) 363 return -EBUSY; 364 err = swimiop_eject(fs); 365 return err; 366 case FDGETPRM: 367 if (copy_to_user((void *) param, (void *) &floppy_type, 368 sizeof(struct floppy_struct))) 369 return -EFAULT; 370 return 0; 371 } 372 return -ENOTTY; 373} 374 375static int floppy_open(struct inode *inode, struct file *filp) 376{ 377 struct floppy_state *fs = inode->i_bdev->bd_disk->private_data; 378 379 if (fs->ref_count == -1 || filp->f_flags & O_EXCL) 380 return -EBUSY; 381 382 if ((filp->f_flags & O_NDELAY) == 0 && (filp->f_mode & 3)) { 383 check_disk_change(inode->i_bdev); 384 if (fs->ejected) 385 return -ENXIO; 386 } 387 388 if ((filp->f_mode & 2) && fs->write_prot) 389 return -EROFS; 390 391 if (filp->f_flags & O_EXCL) 392 fs->ref_count = -1; 393 else 394 ++fs->ref_count; 395 396 return 0; 397} 398 399static int floppy_release(struct inode *inode, struct file *filp) 400{ 401 struct floppy_state *fs = inode->i_bdev->bd_disk->private_data; 402 if (fs->ref_count > 0) 403 fs->ref_count--; 404 return 0; 405} 406 407static int floppy_check_change(struct gendisk *disk) 408{ 409 struct floppy_state *fs = disk->private_data; 410 return fs->ejected; 411} 412 413static int floppy_revalidate(struct gendisk *disk) 414{ 415 struct floppy_state *fs = disk->private_data; 416 grab_drive(fs, revalidating, 0); 417 /* yadda, yadda */ 418 release_drive(fs); 419 return 0; 420} 421 422static void floppy_off(unsigned int nr) 423{ 424} 425 426static int grab_drive(struct floppy_state *fs, enum swim_state state, 427 int interruptible) 428{ 429 unsigned long flags; 430 431 local_irq_save(flags); 432 if (fs->state != idle) { 433 ++fs->wanted; 434 while (fs->state != available) { 435 if (interruptible && signal_pending(current)) { 436 --fs->wanted; 437 local_irq_restore(flags); 438 return -EINTR; 439 } 440 interruptible_sleep_on(&fs->wait); 441 } 442 --fs->wanted; 443 } 444 fs->state = state; 445 local_irq_restore(flags); 446 return 0; 447} 448 449static void release_drive(struct floppy_state *fs) 450{ 451 unsigned long flags; 452 453 local_irq_save(flags); 454 fs->state = idle; 455 start_request(fs); 456 local_irq_restore(flags); 457} 458 459static void set_timeout(struct floppy_state *fs, int nticks, 460 void (*proc)(unsigned long)) 461{ 462 unsigned long flags; 463 464 local_irq_save(flags); 465 if (fs->timeout_pending) 466 del_timer(&fs->timeout); 467 init_timer(&fs->timeout); 468 fs->timeout.expires = jiffies + nticks; 469 fs->timeout.function = proc; 470 fs->timeout.data = (unsigned long) fs; 471 add_timer(&fs->timeout); 472 fs->timeout_pending = 1; 473 local_irq_restore(flags); 474} 475 476static void do_fd_request(request_queue_t * q) 477{ 478 int i; 479 480 for (i = 0 ; i < floppy_count ; i++) { 481 start_request(&floppy_states[i]); 482 } 483} 484 485static void fd_request_complete(struct swim_iop_req *req) 486{ 487 struct floppy_state *fs = req->fs; 488 struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req->command[0]; 489 490 del_timer(&fs->timeout); 491 fs->timeout_pending = 0; 492 fs->state = idle; 493 if (cmd->error) { 494 printk(KERN_ERR "SWIM-IOP: error %d on read/write request.\n", cmd->error); 495 end_request(CURRENT, 0); 496 } else { 497 CURRENT->sector += cmd->num_blocks; 498 CURRENT->current_nr_sectors -= cmd->num_blocks; 499 if (CURRENT->current_nr_sectors <= 0) { 500 end_request(CURRENT, 1); 501 return; 502 } 503 } 504 start_request(fs); 505} 506 507static void fd_request_timeout(unsigned long data) 508{ 509 struct floppy_state *fs = (struct floppy_state *) data; 510 511 fs->timeout_pending = 0; 512 end_request(CURRENT, 0); 513 fs->state = idle; 514} 515 516static void start_request(struct floppy_state *fs) 517{ 518 volatile struct swim_iop_req req; 519 struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req.command[0]; 520 521 if (fs->state == idle && fs->wanted) { 522 fs->state = available; 523 wake_up(&fs->wait); 524 return; 525 } 526 while (CURRENT && fs->state == idle) { 527 if (CURRENT->bh && !buffer_locked(CURRENT->bh)) 528 panic("floppy: block not locked"); 529#if 0 530 printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%ld buf=%p\n", 531 CURRENT->rq_disk->disk_name, CURRENT->cmd, 532 CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer); 533 printk(" rq_status=%d errors=%d current_nr_sectors=%ld\n", 534 CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors); 535#endif 536 537 if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) { 538 end_request(CURRENT, 0); 539 continue; 540 } 541 if (CURRENT->current_nr_sectors == 0) { 542 end_request(CURRENT, 1); 543 continue; 544 } 545 if (fs->ejected) { 546 end_request(CURRENT, 0); 547 continue; 548 } 549 550 swimiop_init_request(&req); 551 req.fs = fs; 552 req.done = fd_request_complete; 553 554 if (CURRENT->cmd == WRITE) { 555 if (fs->write_prot) { 556 end_request(CURRENT, 0); 557 continue; 558 } 559 cmd->code = CMD_WRITE; 560 } else { 561 cmd->code = CMD_READ; 562 563 } 564 cmd->drive_num = fs->drive_num; 565 cmd->buffer = CURRENT->buffer; 566 cmd->first_block = CURRENT->sector; 567 cmd->num_blocks = CURRENT->current_nr_sectors; 568 569 if (swimiop_send_request(&req)) { 570 end_request(CURRENT, 0); 571 continue; 572 } 573 574 set_timeout(fs, HZ*CURRENT->current_nr_sectors, 575 fd_request_timeout); 576 577 fs->state = transferring; 578 } 579}