swim_iop.c
上传用户:ajay2009
上传日期:2009-05-22
资源大小:495k
文件大小:14k
源码类别:

驱动编程

开发平台:

Unix_Linux

  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 %dn", 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. schedule_timeout_interruptible(1);
  294. }
  295. release_drive(fs);
  296. return cmd->error;
  297. }
  298. static struct floppy_struct floppy_type =
  299. { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /*  7 1.44MB 3.5"   */
  300. static int floppy_ioctl(struct inode *inode, struct file *filp,
  301. unsigned int cmd, unsigned long param)
  302. {
  303. struct floppy_state *fs = inode->i_bdev->bd_disk->private_data;
  304. int err;
  305. if ((cmd & 0x80) && !capable(CAP_SYS_ADMIN))
  306. return -EPERM;
  307. switch (cmd) {
  308. case FDEJECT:
  309. if (fs->ref_count != 1)
  310. return -EBUSY;
  311. err = swimiop_eject(fs);
  312. return err;
  313. case FDGETPRM:
  314.         if (copy_to_user((void *) param, (void *) &floppy_type,
  315.  sizeof(struct floppy_struct)))
  316. return -EFAULT;
  317. return 0;
  318. }
  319. return -ENOTTY;
  320. }
  321. static int floppy_open(struct inode *inode, struct file *filp)
  322. {
  323. struct floppy_state *fs = inode->i_bdev->bd_disk->private_data;
  324. if (fs->ref_count == -1 || filp->f_flags & O_EXCL)
  325. return -EBUSY;
  326. if ((filp->f_flags & O_NDELAY) == 0 && (filp->f_mode & 3)) {
  327. check_disk_change(inode->i_bdev);
  328. if (fs->ejected)
  329. return -ENXIO;
  330. }
  331. if ((filp->f_mode & 2) && fs->write_prot)
  332. return -EROFS;
  333. if (filp->f_flags & O_EXCL)
  334. fs->ref_count = -1;
  335. else
  336. ++fs->ref_count;
  337. return 0;
  338. }
  339. static int floppy_release(struct inode *inode, struct file *filp)
  340. {
  341. struct floppy_state *fs = inode->i_bdev->bd_disk->private_data;
  342. if (fs->ref_count > 0)
  343. fs->ref_count--;
  344. return 0;
  345. }
  346. static int floppy_check_change(struct gendisk *disk)
  347. {
  348. struct floppy_state *fs = disk->private_data;
  349. return fs->ejected;
  350. }
  351. static int floppy_revalidate(struct gendisk *disk)
  352. {
  353. struct floppy_state *fs = disk->private_data;
  354. grab_drive(fs, revalidating, 0);
  355. /* yadda, yadda */
  356. release_drive(fs);
  357. return 0;
  358. }
  359. static void floppy_off(unsigned int nr)
  360. {
  361. }
  362. static int grab_drive(struct floppy_state *fs, enum swim_state state,
  363.       int interruptible)
  364. {
  365. unsigned long flags;
  366. local_irq_save(flags);
  367. if (fs->state != idle) {
  368. ++fs->wanted;
  369. while (fs->state != available) {
  370. if (interruptible && signal_pending(current)) {
  371. --fs->wanted;
  372. local_irq_restore(flags);
  373. return -EINTR;
  374. }
  375. interruptible_sleep_on(&fs->wait);
  376. }
  377. --fs->wanted;
  378. }
  379. fs->state = state;
  380. local_irq_restore(flags);
  381. return 0;
  382. }
  383. static void release_drive(struct floppy_state *fs)
  384. {
  385. unsigned long flags;
  386. local_irq_save(flags);
  387. fs->state = idle;
  388. start_request(fs);
  389. local_irq_restore(flags);
  390. }
  391. static void set_timeout(struct floppy_state *fs, int nticks,
  392. void (*proc)(unsigned long))
  393. {
  394. unsigned long flags;
  395. local_irq_save(flags);
  396. if (fs->timeout_pending)
  397. del_timer(&fs->timeout);
  398. init_timer(&fs->timeout);
  399. fs->timeout.expires = jiffies + nticks;
  400. fs->timeout.function = proc;
  401. fs->timeout.data = (unsigned long) fs;
  402. add_timer(&fs->timeout);
  403. fs->timeout_pending = 1;
  404. local_irq_restore(flags);
  405. }
  406. static void do_fd_request(request_queue_t * q)
  407. {
  408. int i;
  409. for (i = 0 ; i < floppy_count ; i++) {
  410. start_request(&floppy_states[i]);
  411. }
  412. }
  413. static void fd_request_complete(struct swim_iop_req *req)
  414. {
  415. struct floppy_state *fs = req->fs;
  416. struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req->command[0];
  417. del_timer(&fs->timeout);
  418. fs->timeout_pending = 0;
  419. fs->state = idle;
  420. if (cmd->error) {
  421. printk(KERN_ERR "SWIM-IOP: error %d on read/write request.n", cmd->error);
  422. end_request(CURRENT, 0);
  423. } else {
  424. CURRENT->sector += cmd->num_blocks;
  425. CURRENT->current_nr_sectors -= cmd->num_blocks;
  426. if (CURRENT->current_nr_sectors <= 0) {
  427. end_request(CURRENT, 1);
  428. return;
  429. }
  430. }
  431. start_request(fs);
  432. }
  433. static void fd_request_timeout(unsigned long data)
  434. {
  435. struct floppy_state *fs = (struct floppy_state *) data;
  436. fs->timeout_pending = 0;
  437. end_request(CURRENT, 0);
  438. fs->state = idle;
  439. }
  440. static void start_request(struct floppy_state *fs)
  441. {
  442. volatile struct swim_iop_req req;
  443. struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req.command[0];
  444. if (fs->state == idle && fs->wanted) {
  445. fs->state = available;
  446. wake_up(&fs->wait);
  447. return;
  448. }
  449. while (CURRENT && fs->state == idle) {
  450. if (CURRENT->bh && !buffer_locked(CURRENT->bh))
  451. panic("floppy: block not locked");
  452. #if 0
  453. printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%ld buf=%pn",
  454.        CURRENT->rq_disk->disk_name, CURRENT->cmd,
  455.        CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer);
  456. printk("           rq_status=%d errors=%d current_nr_sectors=%ldn",
  457.        CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors);
  458. #endif
  459. if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) {
  460. end_request(CURRENT, 0);
  461. continue;
  462. }
  463. if (CURRENT->current_nr_sectors == 0) {
  464. end_request(CURRENT, 1);
  465. continue;
  466. }
  467. if (fs->ejected) {
  468. end_request(CURRENT, 0);
  469. continue;
  470. }
  471. swimiop_init_request(&req);
  472. req.fs = fs;
  473. req.done = fd_request_complete;
  474. if (CURRENT->cmd == WRITE) {
  475. if (fs->write_prot) {
  476. end_request(CURRENT, 0);
  477. continue;
  478. }
  479. cmd->code = CMD_WRITE;
  480. } else {
  481. cmd->code = CMD_READ;
  482. }
  483. cmd->drive_num = fs->drive_num;
  484. cmd->buffer = CURRENT->buffer;
  485. cmd->first_block = CURRENT->sector;
  486. cmd->num_blocks = CURRENT->current_nr_sectors;
  487. if (swimiop_send_request(&req)) {
  488. end_request(CURRENT, 0);
  489. continue;
  490. }
  491. set_timeout(fs, HZ*CURRENT->current_nr_sectors,
  492. fd_request_timeout);
  493. fs->state = transferring;
  494. }
  495. }