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 77b2555b52a894a2e39a42e43d993df875c46a6a 578 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 schedule_timeout_interruptible(1); 342 } 343 release_drive(fs); 344 return cmd->error; 345} 346 347static struct floppy_struct floppy_type = 348 { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /* 7 1.44MB 3.5" */ 349 350static int floppy_ioctl(struct inode *inode, struct file *filp, 351 unsigned int cmd, unsigned long param) 352{ 353 struct floppy_state *fs = inode->i_bdev->bd_disk->private_data; 354 int err; 355 356 if ((cmd & 0x80) && !capable(CAP_SYS_ADMIN)) 357 return -EPERM; 358 359 switch (cmd) { 360 case FDEJECT: 361 if (fs->ref_count != 1) 362 return -EBUSY; 363 err = swimiop_eject(fs); 364 return err; 365 case FDGETPRM: 366 if (copy_to_user((void *) param, (void *) &floppy_type, 367 sizeof(struct floppy_struct))) 368 return -EFAULT; 369 return 0; 370 } 371 return -ENOTTY; 372} 373 374static int floppy_open(struct inode *inode, struct file *filp) 375{ 376 struct floppy_state *fs = inode->i_bdev->bd_disk->private_data; 377 378 if (fs->ref_count == -1 || filp->f_flags & O_EXCL) 379 return -EBUSY; 380 381 if ((filp->f_flags & O_NDELAY) == 0 && (filp->f_mode & 3)) { 382 check_disk_change(inode->i_bdev); 383 if (fs->ejected) 384 return -ENXIO; 385 } 386 387 if ((filp->f_mode & 2) && fs->write_prot) 388 return -EROFS; 389 390 if (filp->f_flags & O_EXCL) 391 fs->ref_count = -1; 392 else 393 ++fs->ref_count; 394 395 return 0; 396} 397 398static int floppy_release(struct inode *inode, struct file *filp) 399{ 400 struct floppy_state *fs = inode->i_bdev->bd_disk->private_data; 401 if (fs->ref_count > 0) 402 fs->ref_count--; 403 return 0; 404} 405 406static int floppy_check_change(struct gendisk *disk) 407{ 408 struct floppy_state *fs = disk->private_data; 409 return fs->ejected; 410} 411 412static int floppy_revalidate(struct gendisk *disk) 413{ 414 struct floppy_state *fs = disk->private_data; 415 grab_drive(fs, revalidating, 0); 416 /* yadda, yadda */ 417 release_drive(fs); 418 return 0; 419} 420 421static void floppy_off(unsigned int nr) 422{ 423} 424 425static int grab_drive(struct floppy_state *fs, enum swim_state state, 426 int interruptible) 427{ 428 unsigned long flags; 429 430 local_irq_save(flags); 431 if (fs->state != idle) { 432 ++fs->wanted; 433 while (fs->state != available) { 434 if (interruptible && signal_pending(current)) { 435 --fs->wanted; 436 local_irq_restore(flags); 437 return -EINTR; 438 } 439 interruptible_sleep_on(&fs->wait); 440 } 441 --fs->wanted; 442 } 443 fs->state = state; 444 local_irq_restore(flags); 445 return 0; 446} 447 448static void release_drive(struct floppy_state *fs) 449{ 450 unsigned long flags; 451 452 local_irq_save(flags); 453 fs->state = idle; 454 start_request(fs); 455 local_irq_restore(flags); 456} 457 458static void set_timeout(struct floppy_state *fs, int nticks, 459 void (*proc)(unsigned long)) 460{ 461 unsigned long flags; 462 463 local_irq_save(flags); 464 if (fs->timeout_pending) 465 del_timer(&fs->timeout); 466 init_timer(&fs->timeout); 467 fs->timeout.expires = jiffies + nticks; 468 fs->timeout.function = proc; 469 fs->timeout.data = (unsigned long) fs; 470 add_timer(&fs->timeout); 471 fs->timeout_pending = 1; 472 local_irq_restore(flags); 473} 474 475static void do_fd_request(request_queue_t * q) 476{ 477 int i; 478 479 for (i = 0 ; i < floppy_count ; i++) { 480 start_request(&floppy_states[i]); 481 } 482} 483 484static void fd_request_complete(struct swim_iop_req *req) 485{ 486 struct floppy_state *fs = req->fs; 487 struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req->command[0]; 488 489 del_timer(&fs->timeout); 490 fs->timeout_pending = 0; 491 fs->state = idle; 492 if (cmd->error) { 493 printk(KERN_ERR "SWIM-IOP: error %d on read/write request.\n", cmd->error); 494 end_request(CURRENT, 0); 495 } else { 496 CURRENT->sector += cmd->num_blocks; 497 CURRENT->current_nr_sectors -= cmd->num_blocks; 498 if (CURRENT->current_nr_sectors <= 0) { 499 end_request(CURRENT, 1); 500 return; 501 } 502 } 503 start_request(fs); 504} 505 506static void fd_request_timeout(unsigned long data) 507{ 508 struct floppy_state *fs = (struct floppy_state *) data; 509 510 fs->timeout_pending = 0; 511 end_request(CURRENT, 0); 512 fs->state = idle; 513} 514 515static void start_request(struct floppy_state *fs) 516{ 517 volatile struct swim_iop_req req; 518 struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req.command[0]; 519 520 if (fs->state == idle && fs->wanted) { 521 fs->state = available; 522 wake_up(&fs->wait); 523 return; 524 } 525 while (CURRENT && fs->state == idle) { 526 if (CURRENT->bh && !buffer_locked(CURRENT->bh)) 527 panic("floppy: block not locked"); 528#if 0 529 printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%ld buf=%p\n", 530 CURRENT->rq_disk->disk_name, CURRENT->cmd, 531 CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer); 532 printk(" rq_status=%d errors=%d current_nr_sectors=%ld\n", 533 CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors); 534#endif 535 536 if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) { 537 end_request(CURRENT, 0); 538 continue; 539 } 540 if (CURRENT->current_nr_sectors == 0) { 541 end_request(CURRENT, 1); 542 continue; 543 } 544 if (fs->ejected) { 545 end_request(CURRENT, 0); 546 continue; 547 } 548 549 swimiop_init_request(&req); 550 req.fs = fs; 551 req.done = fd_request_complete; 552 553 if (CURRENT->cmd == WRITE) { 554 if (fs->write_prot) { 555 end_request(CURRENT, 0); 556 continue; 557 } 558 cmd->code = CMD_WRITE; 559 } else { 560 cmd->code = CMD_READ; 561 562 } 563 cmd->drive_num = fs->drive_num; 564 cmd->buffer = CURRENT->buffer; 565 cmd->first_block = CURRENT->sector; 566 cmd->num_blocks = CURRENT->current_nr_sectors; 567 568 if (swimiop_send_request(&req)) { 569 end_request(CURRENT, 0); 570 continue; 571 } 572 573 set_timeout(fs, HZ*CURRENT->current_nr_sectors, 574 fd_request_timeout); 575 576 fs->state = transferring; 577 } 578}