PageRenderTime 995ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/staging/comedi/drivers/das16m1.c

https://github.com/Mengqi/linux-2.6
C | 794 lines | 534 code | 113 blank | 147 comment | 107 complexity | 61cbda448d5fc43fea922543e793017f MD5 | raw file
  1. /*
  2. comedi/drivers/das16m1.c
  3. CIO-DAS16/M1 driver
  4. Author: Frank Mori Hess, based on code from the das16
  5. driver.
  6. Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
  7. COMEDI - Linux Control and Measurement Device Interface
  8. Copyright (C) 2000 David A. Schleef <ds@schleef.org>
  9. This program is free software; you can redistribute it and/or modify
  10. it under the terms of the GNU General Public License as published by
  11. the Free Software Foundation; either version 2 of the License, or
  12. (at your option) any later version.
  13. This program is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. GNU General Public License for more details.
  17. You should have received a copy of the GNU General Public License
  18. along with this program; if not, write to the Free Software
  19. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. ************************************************************************
  21. */
  22. /*
  23. Driver: das16m1
  24. Description: CIO-DAS16/M1
  25. Author: Frank Mori Hess <fmhess@users.sourceforge.net>
  26. Devices: [Measurement Computing] CIO-DAS16/M1 (cio-das16/m1)
  27. Status: works
  28. This driver supports a single board - the CIO-DAS16/M1.
  29. As far as I know, there are no other boards that have
  30. the same register layout. Even the CIO-DAS16/M1/16 is
  31. significantly different.
  32. I was _barely_ able to reach the full 1 MHz capability
  33. of this board, using a hard real-time interrupt
  34. (set the TRIG_RT flag in your struct comedi_cmd and use
  35. rtlinux or RTAI). The board can't do dma, so the bottleneck is
  36. pulling the data across the ISA bus. I timed the interrupt
  37. handler, and it took my computer ~470 microseconds to pull 512
  38. samples from the board. So at 1 Mhz sampling rate,
  39. expect your CPU to be spending almost all of its
  40. time in the interrupt handler.
  41. This board has some unusual restrictions for its channel/gain list. If the
  42. list has 2 or more channels in it, then two conditions must be satisfied:
  43. (1) - even/odd channels must appear at even/odd indices in the list
  44. (2) - the list must have an even number of entries.
  45. Options:
  46. [0] - base io address
  47. [1] - irq (optional, but you probably want it)
  48. irq can be omitted, although the cmd interface will not work without it.
  49. */
  50. #include <linux/ioport.h>
  51. #include <linux/interrupt.h>
  52. #include "../comedidev.h"
  53. #include "8255.h"
  54. #include "8253.h"
  55. #include "comedi_fc.h"
  56. #define DAS16M1_SIZE 16
  57. #define DAS16M1_SIZE2 8
  58. #define DAS16M1_XTAL 100 /* 10 MHz master clock */
  59. #define FIFO_SIZE 1024 /* 1024 sample fifo */
  60. /*
  61. CIO-DAS16_M1.pdf
  62. "cio-das16/m1"
  63. 0 a/d bits 0-3, mux start 12 bit
  64. 1 a/d bits 4-11 unused
  65. 2 status control
  66. 3 di 4 bit do 4 bit
  67. 4 unused clear interrupt
  68. 5 interrupt, pacer
  69. 6 channel/gain queue address
  70. 7 channel/gain queue data
  71. 89ab 8254
  72. cdef 8254
  73. 400 8255
  74. 404-407 8254
  75. */
  76. #define DAS16M1_AI 0 /* 16-bit wide register */
  77. #define AI_CHAN(x) ((x) & 0xf)
  78. #define DAS16M1_CS 2
  79. #define EXT_TRIG_BIT 0x1
  80. #define OVRUN 0x20
  81. #define IRQDATA 0x80
  82. #define DAS16M1_DIO 3
  83. #define DAS16M1_CLEAR_INTR 4
  84. #define DAS16M1_INTR_CONTROL 5
  85. #define EXT_PACER 0x2
  86. #define INT_PACER 0x3
  87. #define PACER_MASK 0x3
  88. #define INTE 0x80
  89. #define DAS16M1_QUEUE_ADDR 6
  90. #define DAS16M1_QUEUE_DATA 7
  91. #define Q_CHAN(x) ((x) & 0x7)
  92. #define Q_RANGE(x) (((x) & 0xf) << 4)
  93. #define UNIPOLAR 0x40
  94. #define DAS16M1_8254_FIRST 0x8
  95. #define DAS16M1_8254_FIRST_CNTRL 0xb
  96. #define TOTAL_CLEAR 0x30
  97. #define DAS16M1_8254_SECOND 0xc
  98. #define DAS16M1_82C55 0x400
  99. #define DAS16M1_8254_THIRD 0x404
  100. static const struct comedi_lrange range_das16m1 = { 9,
  101. {
  102. BIP_RANGE(5),
  103. BIP_RANGE(2.5),
  104. BIP_RANGE(1.25),
  105. BIP_RANGE(0.625),
  106. UNI_RANGE(10),
  107. UNI_RANGE(5),
  108. UNI_RANGE(2.5),
  109. UNI_RANGE(1.25),
  110. BIP_RANGE(10),
  111. }
  112. };
  113. static int das16m1_do_wbits(struct comedi_device *dev,
  114. struct comedi_subdevice *s,
  115. struct comedi_insn *insn, unsigned int *data);
  116. static int das16m1_di_rbits(struct comedi_device *dev,
  117. struct comedi_subdevice *s,
  118. struct comedi_insn *insn, unsigned int *data);
  119. static int das16m1_ai_rinsn(struct comedi_device *dev,
  120. struct comedi_subdevice *s,
  121. struct comedi_insn *insn, unsigned int *data);
  122. static int das16m1_cmd_test(struct comedi_device *dev,
  123. struct comedi_subdevice *s, struct comedi_cmd *cmd);
  124. static int das16m1_cmd_exec(struct comedi_device *dev,
  125. struct comedi_subdevice *s);
  126. static int das16m1_cancel(struct comedi_device *dev,
  127. struct comedi_subdevice *s);
  128. static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s);
  129. static irqreturn_t das16m1_interrupt(int irq, void *d);
  130. static void das16m1_handler(struct comedi_device *dev, unsigned int status);
  131. static unsigned int das16m1_set_pacer(struct comedi_device *dev,
  132. unsigned int ns, int round_flag);
  133. static int das16m1_irq_bits(unsigned int irq);
  134. struct das16m1_board {
  135. const char *name;
  136. unsigned int ai_speed;
  137. };
  138. static const struct das16m1_board das16m1_boards[] = {
  139. {
  140. .name = "cio-das16/m1", /* CIO-DAS16_M1.pdf */
  141. .ai_speed = 1000, /* 1MHz max speed */
  142. },
  143. };
  144. static int das16m1_attach(struct comedi_device *dev,
  145. struct comedi_devconfig *it);
  146. static int das16m1_detach(struct comedi_device *dev);
  147. static struct comedi_driver driver_das16m1 = {
  148. .driver_name = "das16m1",
  149. .module = THIS_MODULE,
  150. .attach = das16m1_attach,
  151. .detach = das16m1_detach,
  152. .board_name = &das16m1_boards[0].name,
  153. .num_names = ARRAY_SIZE(das16m1_boards),
  154. .offset = sizeof(das16m1_boards[0]),
  155. };
  156. struct das16m1_private_struct {
  157. unsigned int control_state;
  158. volatile unsigned int adc_count; /* number of samples completed */
  159. /* initial value in lower half of hardware conversion counter,
  160. * needed to keep track of whether new count has been loaded into
  161. * counter yet (loaded by first sample conversion) */
  162. u16 initial_hw_count;
  163. short ai_buffer[FIFO_SIZE];
  164. unsigned int do_bits; /* saves status of digital output bits */
  165. unsigned int divisor1; /* divides master clock to obtain conversion speed */
  166. unsigned int divisor2; /* divides master clock to obtain conversion speed */
  167. };
  168. #define devpriv ((struct das16m1_private_struct *)(dev->private))
  169. #define thisboard ((const struct das16m1_board *)(dev->board_ptr))
  170. static int __init driver_das16m1_init_module(void)
  171. {
  172. return comedi_driver_register(&driver_das16m1);
  173. }
  174. static void __exit driver_das16m1_cleanup_module(void)
  175. {
  176. comedi_driver_unregister(&driver_das16m1);
  177. }
  178. module_init(driver_das16m1_init_module);
  179. module_exit(driver_das16m1_cleanup_module);
  180. static inline short munge_sample(short data)
  181. {
  182. return (data >> 4) & 0xfff;
  183. }
  184. static int das16m1_cmd_test(struct comedi_device *dev,
  185. struct comedi_subdevice *s, struct comedi_cmd *cmd)
  186. {
  187. unsigned int err = 0, tmp, i;
  188. /* make sure triggers are valid */
  189. tmp = cmd->start_src;
  190. cmd->start_src &= TRIG_NOW | TRIG_EXT;
  191. if (!cmd->start_src || tmp != cmd->start_src)
  192. err++;
  193. tmp = cmd->scan_begin_src;
  194. cmd->scan_begin_src &= TRIG_FOLLOW;
  195. if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
  196. err++;
  197. tmp = cmd->convert_src;
  198. cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
  199. if (!cmd->convert_src || tmp != cmd->convert_src)
  200. err++;
  201. tmp = cmd->scan_end_src;
  202. cmd->scan_end_src &= TRIG_COUNT;
  203. if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
  204. err++;
  205. tmp = cmd->stop_src;
  206. cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
  207. if (!cmd->stop_src || tmp != cmd->stop_src)
  208. err++;
  209. if (err)
  210. return 1;
  211. /* step 2: make sure trigger sources are unique and mutually compatible */
  212. if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
  213. err++;
  214. if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
  215. err++;
  216. if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
  217. err++;
  218. if (err)
  219. return 2;
  220. /* step 3: make sure arguments are trivially compatible */
  221. if (cmd->start_arg != 0) {
  222. cmd->start_arg = 0;
  223. err++;
  224. }
  225. if (cmd->scan_begin_src == TRIG_FOLLOW) {
  226. /* internal trigger */
  227. if (cmd->scan_begin_arg != 0) {
  228. cmd->scan_begin_arg = 0;
  229. err++;
  230. }
  231. }
  232. if (cmd->convert_src == TRIG_TIMER) {
  233. if (cmd->convert_arg < thisboard->ai_speed) {
  234. cmd->convert_arg = thisboard->ai_speed;
  235. err++;
  236. }
  237. }
  238. if (cmd->scan_end_arg != cmd->chanlist_len) {
  239. cmd->scan_end_arg = cmd->chanlist_len;
  240. err++;
  241. }
  242. if (cmd->stop_src == TRIG_COUNT) {
  243. /* any count is allowed */
  244. } else {
  245. /* TRIG_NONE */
  246. if (cmd->stop_arg != 0) {
  247. cmd->stop_arg = 0;
  248. err++;
  249. }
  250. }
  251. if (err)
  252. return 3;
  253. /* step 4: fix up arguments */
  254. if (cmd->convert_src == TRIG_TIMER) {
  255. tmp = cmd->convert_arg;
  256. /* calculate counter values that give desired timing */
  257. i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL,
  258. &(devpriv->divisor1),
  259. &(devpriv->divisor2),
  260. &(cmd->convert_arg),
  261. cmd->flags & TRIG_ROUND_MASK);
  262. if (tmp != cmd->convert_arg)
  263. err++;
  264. }
  265. if (err)
  266. return 4;
  267. /* check chanlist against board's peculiarities */
  268. if (cmd->chanlist && cmd->chanlist_len > 1) {
  269. for (i = 0; i < cmd->chanlist_len; i++) {
  270. /* even/odd channels must go into even/odd queue addresses */
  271. if ((i % 2) != (CR_CHAN(cmd->chanlist[i]) % 2)) {
  272. comedi_error(dev, "bad chanlist:\n"
  273. " even/odd channels must go have even/odd chanlist indices");
  274. err++;
  275. }
  276. }
  277. if ((cmd->chanlist_len % 2) != 0) {
  278. comedi_error(dev,
  279. "chanlist must be of even length or length 1");
  280. err++;
  281. }
  282. }
  283. if (err)
  284. return 5;
  285. return 0;
  286. }
  287. static int das16m1_cmd_exec(struct comedi_device *dev,
  288. struct comedi_subdevice *s)
  289. {
  290. struct comedi_async *async = s->async;
  291. struct comedi_cmd *cmd = &async->cmd;
  292. unsigned int byte, i;
  293. if (dev->irq == 0) {
  294. comedi_error(dev, "irq required to execute comedi_cmd");
  295. return -1;
  296. }
  297. /* disable interrupts and internal pacer */
  298. devpriv->control_state &= ~INTE & ~PACER_MASK;
  299. outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
  300. /* set software count */
  301. devpriv->adc_count = 0;
  302. /* Initialize lower half of hardware counter, used to determine how
  303. * many samples are in fifo. Value doesn't actually load into counter
  304. * until counter's next clock (the next a/d conversion) */
  305. i8254_load(dev->iobase + DAS16M1_8254_FIRST, 0, 1, 0, 2);
  306. /* remember current reading of counter so we know when counter has
  307. * actually been loaded */
  308. devpriv->initial_hw_count =
  309. i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
  310. /* setup channel/gain queue */
  311. for (i = 0; i < cmd->chanlist_len; i++) {
  312. outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
  313. byte =
  314. Q_CHAN(CR_CHAN(cmd->chanlist[i])) |
  315. Q_RANGE(CR_RANGE(cmd->chanlist[i]));
  316. outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
  317. }
  318. /* set counter mode and counts */
  319. cmd->convert_arg =
  320. das16m1_set_pacer(dev, cmd->convert_arg,
  321. cmd->flags & TRIG_ROUND_MASK);
  322. /* set control & status register */
  323. byte = 0;
  324. /* if we are using external start trigger (also board dislikes having
  325. * both start and conversion triggers external simultaneously) */
  326. if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT) {
  327. byte |= EXT_TRIG_BIT;
  328. }
  329. outb(byte, dev->iobase + DAS16M1_CS);
  330. /* clear interrupt bit */
  331. outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
  332. /* enable interrupts and internal pacer */
  333. devpriv->control_state &= ~PACER_MASK;
  334. if (cmd->convert_src == TRIG_TIMER) {
  335. devpriv->control_state |= INT_PACER;
  336. } else {
  337. devpriv->control_state |= EXT_PACER;
  338. }
  339. devpriv->control_state |= INTE;
  340. outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
  341. return 0;
  342. }
  343. static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
  344. {
  345. devpriv->control_state &= ~INTE & ~PACER_MASK;
  346. outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
  347. return 0;
  348. }
  349. static int das16m1_ai_rinsn(struct comedi_device *dev,
  350. struct comedi_subdevice *s,
  351. struct comedi_insn *insn, unsigned int *data)
  352. {
  353. int i, n;
  354. int byte;
  355. const int timeout = 1000;
  356. /* disable interrupts and internal pacer */
  357. devpriv->control_state &= ~INTE & ~PACER_MASK;
  358. outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
  359. /* setup channel/gain queue */
  360. outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
  361. byte =
  362. Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->chanspec));
  363. outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
  364. for (n = 0; n < insn->n; n++) {
  365. /* clear IRQDATA bit */
  366. outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
  367. /* trigger conversion */
  368. outb(0, dev->iobase);
  369. for (i = 0; i < timeout; i++) {
  370. if (inb(dev->iobase + DAS16M1_CS) & IRQDATA)
  371. break;
  372. }
  373. if (i == timeout) {
  374. comedi_error(dev, "timeout");
  375. return -ETIME;
  376. }
  377. data[n] = munge_sample(inw(dev->iobase));
  378. }
  379. return n;
  380. }
  381. static int das16m1_di_rbits(struct comedi_device *dev,
  382. struct comedi_subdevice *s,
  383. struct comedi_insn *insn, unsigned int *data)
  384. {
  385. unsigned int bits;
  386. bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
  387. data[1] = bits;
  388. data[0] = 0;
  389. return 2;
  390. }
  391. static int das16m1_do_wbits(struct comedi_device *dev,
  392. struct comedi_subdevice *s,
  393. struct comedi_insn *insn, unsigned int *data)
  394. {
  395. unsigned int wbits;
  396. /* only set bits that have been masked */
  397. data[0] &= 0xf;
  398. wbits = devpriv->do_bits;
  399. /* zero bits that have been masked */
  400. wbits &= ~data[0];
  401. /* set masked bits */
  402. wbits |= data[0] & data[1];
  403. devpriv->do_bits = wbits;
  404. data[1] = wbits;
  405. outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
  406. return 2;
  407. }
  408. static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
  409. {
  410. unsigned long flags;
  411. unsigned int status;
  412. /* prevent race with interrupt handler */
  413. spin_lock_irqsave(&dev->spinlock, flags);
  414. status = inb(dev->iobase + DAS16M1_CS);
  415. das16m1_handler(dev, status);
  416. spin_unlock_irqrestore(&dev->spinlock, flags);
  417. return s->async->buf_write_count - s->async->buf_read_count;
  418. }
  419. static irqreturn_t das16m1_interrupt(int irq, void *d)
  420. {
  421. int status;
  422. struct comedi_device *dev = d;
  423. if (dev->attached == 0) {
  424. comedi_error(dev, "premature interrupt");
  425. return IRQ_HANDLED;
  426. }
  427. /* prevent race with comedi_poll() */
  428. spin_lock(&dev->spinlock);
  429. status = inb(dev->iobase + DAS16M1_CS);
  430. if ((status & (IRQDATA | OVRUN)) == 0) {
  431. comedi_error(dev, "spurious interrupt");
  432. spin_unlock(&dev->spinlock);
  433. return IRQ_NONE;
  434. }
  435. das16m1_handler(dev, status);
  436. /* clear interrupt */
  437. outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
  438. spin_unlock(&dev->spinlock);
  439. return IRQ_HANDLED;
  440. }
  441. static void munge_sample_array(short *array, unsigned int num_elements)
  442. {
  443. unsigned int i;
  444. for (i = 0; i < num_elements; i++) {
  445. array[i] = munge_sample(array[i]);
  446. }
  447. }
  448. static void das16m1_handler(struct comedi_device *dev, unsigned int status)
  449. {
  450. struct comedi_subdevice *s;
  451. struct comedi_async *async;
  452. struct comedi_cmd *cmd;
  453. u16 num_samples;
  454. u16 hw_counter;
  455. s = dev->read_subdev;
  456. async = s->async;
  457. async->events = 0;
  458. cmd = &async->cmd;
  459. /* figure out how many samples are in fifo */
  460. hw_counter = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
  461. /* make sure hardware counter reading is not bogus due to initial value
  462. * not having been loaded yet */
  463. if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
  464. num_samples = 0;
  465. } else {
  466. /* The calculation of num_samples looks odd, but it uses the following facts.
  467. * 16 bit hardware counter is initialized with value of zero (which really
  468. * means 0x1000). The counter decrements by one on each conversion
  469. * (when the counter decrements from zero it goes to 0xffff). num_samples
  470. * is a 16 bit variable, so it will roll over in a similar fashion to the
  471. * hardware counter. Work it out, and this is what you get. */
  472. num_samples = -hw_counter - devpriv->adc_count;
  473. }
  474. /* check if we only need some of the points */
  475. if (cmd->stop_src == TRIG_COUNT) {
  476. if (num_samples > cmd->stop_arg * cmd->chanlist_len)
  477. num_samples = cmd->stop_arg * cmd->chanlist_len;
  478. }
  479. /* make sure we dont try to get too many points if fifo has overrun */
  480. if (num_samples > FIFO_SIZE)
  481. num_samples = FIFO_SIZE;
  482. insw(dev->iobase, devpriv->ai_buffer, num_samples);
  483. munge_sample_array(devpriv->ai_buffer, num_samples);
  484. cfc_write_array_to_buffer(s, devpriv->ai_buffer,
  485. num_samples * sizeof(short));
  486. devpriv->adc_count += num_samples;
  487. if (cmd->stop_src == TRIG_COUNT) {
  488. if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) { /* end of acquisition */
  489. das16m1_cancel(dev, s);
  490. async->events |= COMEDI_CB_EOA;
  491. }
  492. }
  493. /* this probably won't catch overruns since the card doesn't generate
  494. * overrun interrupts, but we might as well try */
  495. if (status & OVRUN) {
  496. das16m1_cancel(dev, s);
  497. async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
  498. comedi_error(dev, "fifo overflow");
  499. }
  500. comedi_event(dev, s);
  501. }
  502. /* This function takes a time in nanoseconds and sets the *
  503. * 2 pacer clocks to the closest frequency possible. It also *
  504. * returns the actual sampling period. */
  505. static unsigned int das16m1_set_pacer(struct comedi_device *dev,
  506. unsigned int ns, int rounding_flags)
  507. {
  508. i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1),
  509. &(devpriv->divisor2), &ns,
  510. rounding_flags & TRIG_ROUND_MASK);
  511. /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
  512. i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 1, devpriv->divisor1,
  513. 2);
  514. i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 2, devpriv->divisor2,
  515. 2);
  516. return ns;
  517. }
  518. static int das16m1_irq_bits(unsigned int irq)
  519. {
  520. int ret;
  521. switch (irq) {
  522. case 10:
  523. ret = 0x0;
  524. break;
  525. case 11:
  526. ret = 0x1;
  527. break;
  528. case 12:
  529. ret = 0x2;
  530. break;
  531. case 15:
  532. ret = 0x3;
  533. break;
  534. case 2:
  535. ret = 0x4;
  536. break;
  537. case 3:
  538. ret = 0x5;
  539. break;
  540. case 5:
  541. ret = 0x6;
  542. break;
  543. case 7:
  544. ret = 0x7;
  545. break;
  546. default:
  547. return -1;
  548. break;
  549. }
  550. return ret << 4;
  551. }
  552. /*
  553. * Options list:
  554. * 0 I/O base
  555. * 1 IRQ
  556. */
  557. static int das16m1_attach(struct comedi_device *dev,
  558. struct comedi_devconfig *it)
  559. {
  560. struct comedi_subdevice *s;
  561. int ret;
  562. unsigned int irq;
  563. unsigned long iobase;
  564. iobase = it->options[0];
  565. printk("comedi%d: das16m1:", dev->minor);
  566. ret = alloc_private(dev, sizeof(struct das16m1_private_struct));
  567. if (ret < 0)
  568. return ret;
  569. dev->board_name = thisboard->name;
  570. printk(" io 0x%lx-0x%lx 0x%lx-0x%lx",
  571. iobase, iobase + DAS16M1_SIZE,
  572. iobase + DAS16M1_82C55, iobase + DAS16M1_82C55 + DAS16M1_SIZE2);
  573. if (!request_region(iobase, DAS16M1_SIZE, driver_das16m1.driver_name)) {
  574. printk(" I/O port conflict\n");
  575. return -EIO;
  576. }
  577. if (!request_region(iobase + DAS16M1_82C55, DAS16M1_SIZE2,
  578. driver_das16m1.driver_name)) {
  579. release_region(iobase, DAS16M1_SIZE);
  580. printk(" I/O port conflict\n");
  581. return -EIO;
  582. }
  583. dev->iobase = iobase;
  584. /* now for the irq */
  585. irq = it->options[1];
  586. /* make sure it is valid */
  587. if (das16m1_irq_bits(irq) >= 0) {
  588. ret = request_irq(irq, das16m1_interrupt, 0,
  589. driver_das16m1.driver_name, dev);
  590. if (ret < 0) {
  591. printk(", irq unavailable\n");
  592. return ret;
  593. }
  594. dev->irq = irq;
  595. printk(", irq %u\n", irq);
  596. } else if (irq == 0) {
  597. printk(", no irq\n");
  598. } else {
  599. printk(", invalid irq\n"
  600. " valid irqs are 2, 3, 5, 7, 10, 11, 12, or 15\n");
  601. return -EINVAL;
  602. }
  603. ret = alloc_subdevices(dev, 4);
  604. if (ret < 0)
  605. return ret;
  606. s = dev->subdevices + 0;
  607. dev->read_subdev = s;
  608. /* ai */
  609. s->type = COMEDI_SUBD_AI;
  610. s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
  611. s->n_chan = 8;
  612. s->subdev_flags = SDF_DIFF;
  613. s->len_chanlist = 256;
  614. s->maxdata = (1 << 12) - 1;
  615. s->range_table = &range_das16m1;
  616. s->insn_read = das16m1_ai_rinsn;
  617. s->do_cmdtest = das16m1_cmd_test;
  618. s->do_cmd = das16m1_cmd_exec;
  619. s->cancel = das16m1_cancel;
  620. s->poll = das16m1_poll;
  621. s = dev->subdevices + 1;
  622. /* di */
  623. s->type = COMEDI_SUBD_DI;
  624. s->subdev_flags = SDF_READABLE;
  625. s->n_chan = 4;
  626. s->maxdata = 1;
  627. s->range_table = &range_digital;
  628. s->insn_bits = das16m1_di_rbits;
  629. s = dev->subdevices + 2;
  630. /* do */
  631. s->type = COMEDI_SUBD_DO;
  632. s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
  633. s->n_chan = 4;
  634. s->maxdata = 1;
  635. s->range_table = &range_digital;
  636. s->insn_bits = das16m1_do_wbits;
  637. s = dev->subdevices + 3;
  638. /* 8255 */
  639. subdev_8255_init(dev, s, NULL, dev->iobase + DAS16M1_82C55);
  640. /* disable upper half of hardware conversion counter so it doesn't mess with us */
  641. outb(TOTAL_CLEAR, dev->iobase + DAS16M1_8254_FIRST_CNTRL);
  642. /* initialize digital output lines */
  643. outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
  644. /* set the interrupt level */
  645. if (dev->irq)
  646. devpriv->control_state = das16m1_irq_bits(dev->irq);
  647. else
  648. devpriv->control_state = 0;
  649. outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
  650. return 0;
  651. }
  652. static int das16m1_detach(struct comedi_device *dev)
  653. {
  654. printk("comedi%d: das16m1: remove\n", dev->minor);
  655. /* das16m1_reset(dev); */
  656. if (dev->subdevices)
  657. subdev_8255_cleanup(dev, dev->subdevices + 3);
  658. if (dev->irq)
  659. free_irq(dev->irq, dev);
  660. if (dev->iobase) {
  661. release_region(dev->iobase, DAS16M1_SIZE);
  662. release_region(dev->iobase + DAS16M1_82C55, DAS16M1_SIZE2);
  663. }
  664. return 0;
  665. }
  666. MODULE_AUTHOR("Comedi http://www.comedi.org");
  667. MODULE_DESCRIPTION("Comedi low-level driver");
  668. MODULE_LICENSE("GPL");