swim_iop.c 14 KB

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