swim_iop.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:14k
源码类别:

嵌入式Linux

开发平台:

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