comedi_parport.c revision 241ab6ad7108e51c67881f7881c5e46f0400cdb1
1/* 2 comedi/drivers/comedi_parport.c 3 hardware driver for standard parallel port 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 1998,2001 David A. Schleef <ds@schleef.org> 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 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 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 22*/ 23/* 24Driver: comedi_parport 25Description: Standard PC parallel port 26Author: ds 27Status: works in immediate mode 28Devices: [standard] parallel port (comedi_parport) 29Updated: Tue, 30 Apr 2002 21:11:45 -0700 30 31A cheap and easy way to get a few more digital I/O lines. Steal 32additional parallel ports from old computers or your neighbors' 33computers. 34 35Option list: 36 0: I/O port base for the parallel port. 37 1: IRQ 38 39Parallel Port Lines: 40 41pin subdev chan aka 42--- ------ ---- --- 431 2 0 strobe 442 0 0 data 0 453 0 1 data 1 464 0 2 data 2 475 0 3 data 3 486 0 4 data 4 497 0 5 data 5 508 0 6 data 6 519 0 7 data 7 5210 1 3 acknowledge 5311 1 4 busy 5412 1 2 output 5513 1 1 printer selected 5614 2 1 auto LF 5715 1 0 error 5816 2 2 init 5917 2 3 select printer 6018-25 ground 61 62Notes: 63 64Subdevices 0 is digital I/O, subdevice 1 is digital input, and 65subdevice 2 is digital output. Unlike other Comedi devices, 66subdevice 0 defaults to output. 67 68Pins 13 and 14 are inverted once by Comedi and once by the 69hardware, thus cancelling the effect. 70 71Pin 1 is a strobe, thus acts like one. There's no way in software 72to change this, at least on a standard parallel port. 73 74Subdevice 3 pretends to be a digital input subdevice, but it always 75returns 0 when read. However, if you run a command with 76scan_begin_src=TRIG_EXT, it uses pin 10 as a external triggering 77pin, which can be used to wake up tasks. 78*/ 79/* 80 see http://www.beyondlogic.org/ for information. 81 or http://www.linux-magazin.de/ausgabe/1999/10/IO/io.html 82 */ 83 84#include "../comedidev.h" 85#include <linux/ioport.h> 86 87#define PARPORT_SIZE 3 88 89#define PARPORT_A 0 90#define PARPORT_B 1 91#define PARPORT_C 2 92 93static int parport_attach(comedi_device * dev, comedi_devconfig * it); 94static int parport_detach(comedi_device * dev); 95static comedi_driver driver_parport = { 96 driver_name:"comedi_parport", 97 module:THIS_MODULE, 98 attach:parport_attach, 99 detach:parport_detach, 100}; 101 102COMEDI_INITCLEANUP(driver_parport); 103 104typedef struct parport_private_struct { 105 unsigned int a_data; 106 unsigned int c_data; 107 int enable_irq; 108} parport_private; 109#define devpriv ((parport_private *)(dev->private)) 110 111static int parport_insn_a(comedi_device * dev, comedi_subdevice * s, 112 comedi_insn * insn, lsampl_t * data) 113{ 114 if (data[0]) { 115 devpriv->a_data &= ~data[0]; 116 devpriv->a_data |= (data[0] & data[1]); 117 118 outb(devpriv->a_data, dev->iobase + PARPORT_A); 119 } 120 121 data[1] = inb(dev->iobase + PARPORT_A); 122 123 return 2; 124} 125 126static int parport_insn_config_a(comedi_device * dev, comedi_subdevice * s, 127 comedi_insn * insn, lsampl_t * data) 128{ 129 if (data[0]) { 130 s->io_bits = 0xff; 131 devpriv->c_data &= ~(1 << 5); 132 } else { 133 s->io_bits = 0; 134 devpriv->c_data |= (1 << 5); 135 } 136 outb(devpriv->c_data, dev->iobase + PARPORT_C); 137 138 return 1; 139} 140 141static int parport_insn_b(comedi_device * dev, comedi_subdevice * s, 142 comedi_insn * insn, lsampl_t * data) 143{ 144 if (data[0]) { 145 // should writes be ignored? 146 } 147 148 data[1] = (inb(dev->iobase + PARPORT_B) >> 3); 149 150 return 2; 151} 152 153static int parport_insn_c(comedi_device * dev, comedi_subdevice * s, 154 comedi_insn * insn, lsampl_t * data) 155{ 156 data[0] &= 0x0f; 157 if (data[0]) { 158 devpriv->c_data &= ~data[0]; 159 devpriv->c_data |= (data[0] & data[1]); 160 161 outb(devpriv->c_data, dev->iobase + PARPORT_C); 162 } 163 164 data[1] = devpriv->c_data & 0xf; 165 166 return 2; 167} 168 169static int parport_intr_insn(comedi_device * dev, comedi_subdevice * s, 170 comedi_insn * insn, lsampl_t * data) 171{ 172 if (insn->n < 1) 173 return -EINVAL; 174 175 data[1] = 0; 176 return 2; 177} 178 179static int parport_intr_cmdtest(comedi_device * dev, comedi_subdevice * s, 180 comedi_cmd * cmd) 181{ 182 int err = 0; 183 int tmp; 184 185 /* step 1 */ 186 187 tmp = cmd->start_src; 188 cmd->start_src &= TRIG_NOW; 189 if (!cmd->start_src || tmp != cmd->start_src) 190 err++; 191 192 tmp = cmd->scan_begin_src; 193 cmd->scan_begin_src &= TRIG_EXT; 194 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) 195 err++; 196 197 tmp = cmd->convert_src; 198 cmd->convert_src &= TRIG_FOLLOW; 199 if (!cmd->convert_src || tmp != cmd->convert_src) 200 err++; 201 202 tmp = cmd->scan_end_src; 203 cmd->scan_end_src &= TRIG_COUNT; 204 if (!cmd->scan_end_src || tmp != cmd->scan_end_src) 205 err++; 206 207 tmp = cmd->stop_src; 208 cmd->stop_src &= TRIG_NONE; 209 if (!cmd->stop_src || tmp != cmd->stop_src) 210 err++; 211 212 if (err) 213 return 1; 214 215 /* step 2: ignored */ 216 217 if (err) 218 return 2; 219 220 /* step 3: */ 221 222 if (cmd->start_arg != 0) { 223 cmd->start_arg = 0; 224 err++; 225 } 226 if (cmd->scan_begin_arg != 0) { 227 cmd->scan_begin_arg = 0; 228 err++; 229 } 230 if (cmd->convert_arg != 0) { 231 cmd->convert_arg = 0; 232 err++; 233 } 234 if (cmd->scan_end_arg != 1) { 235 cmd->scan_end_arg = 1; 236 err++; 237 } 238 if (cmd->stop_arg != 0) { 239 cmd->stop_arg = 0; 240 err++; 241 } 242 243 if (err) 244 return 3; 245 246 /* step 4: ignored */ 247 248 if (err) 249 return 4; 250 251 return 0; 252} 253 254static int parport_intr_cmd(comedi_device * dev, comedi_subdevice * s) 255{ 256 devpriv->c_data |= 0x10; 257 outb(devpriv->c_data, dev->iobase + PARPORT_C); 258 259 devpriv->enable_irq = 1; 260 261 return 0; 262} 263 264static int parport_intr_cancel(comedi_device * dev, comedi_subdevice * s) 265{ 266 printk("parport_intr_cancel()\n"); 267 268 devpriv->c_data &= ~0x10; 269 outb(devpriv->c_data, dev->iobase + PARPORT_C); 270 271 devpriv->enable_irq = 0; 272 273 return 0; 274} 275 276static irqreturn_t parport_interrupt(int irq, void *d PT_REGS_ARG) 277{ 278 comedi_device *dev = d; 279 comedi_subdevice *s = dev->subdevices + 3; 280 281 if (!devpriv->enable_irq) { 282 printk("comedi_parport: bogus irq, ignored\n"); 283 return IRQ_NONE; 284 } 285 286 comedi_buf_put(s->async, 0); 287 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; 288 289 comedi_event(dev, s); 290 return IRQ_HANDLED; 291} 292 293static int parport_attach(comedi_device * dev, comedi_devconfig * it) 294{ 295 int ret; 296 unsigned int irq; 297 unsigned long iobase; 298 comedi_subdevice *s; 299 300 iobase = it->options[0]; 301 printk("comedi%d: parport: 0x%04lx ", dev->minor, iobase); 302 if (!request_region(iobase, PARPORT_SIZE, "parport (comedi)")) { 303 printk("I/O port conflict\n"); 304 return -EIO; 305 } 306 dev->iobase = iobase; 307 308 irq = it->options[1]; 309 if (irq) { 310 printk(" irq=%u", irq); 311 ret = comedi_request_irq(irq, parport_interrupt, 0, 312 "comedi_parport", dev); 313 if (ret < 0) { 314 printk(" irq not available\n"); 315 return -EINVAL; 316 } 317 dev->irq = irq; 318 } 319 dev->board_name = "parport"; 320 321 if ((ret = alloc_subdevices(dev, 4)) < 0) 322 return ret; 323 if ((ret = alloc_private(dev, sizeof(parport_private))) < 0) 324 return ret; 325 326 s = dev->subdevices + 0; 327 s->type = COMEDI_SUBD_DIO; 328 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 329 s->n_chan = 8; 330 s->maxdata = 1; 331 s->range_table = &range_digital; 332 s->insn_bits = parport_insn_a; 333 s->insn_config = parport_insn_config_a; 334 335 s = dev->subdevices + 1; 336 s->type = COMEDI_SUBD_DI; 337 s->subdev_flags = SDF_READABLE; 338 s->n_chan = 5; 339 s->maxdata = 1; 340 s->range_table = &range_digital; 341 s->insn_bits = parport_insn_b; 342 343 s = dev->subdevices + 2; 344 s->type = COMEDI_SUBD_DO; 345 s->subdev_flags = SDF_WRITABLE; 346 s->n_chan = 4; 347 s->maxdata = 1; 348 s->range_table = &range_digital; 349 s->insn_bits = parport_insn_c; 350 351 s = dev->subdevices + 3; 352 if (irq) { 353 dev->read_subdev = s; 354 s->type = COMEDI_SUBD_DI; 355 s->subdev_flags = SDF_READABLE | SDF_CMD_READ; 356 s->n_chan = 1; 357 s->maxdata = 1; 358 s->range_table = &range_digital; 359 s->insn_bits = parport_intr_insn; 360 s->do_cmdtest = parport_intr_cmdtest; 361 s->do_cmd = parport_intr_cmd; 362 s->cancel = parport_intr_cancel; 363 } else { 364 s->type = COMEDI_SUBD_UNUSED; 365 } 366 367 devpriv->a_data = 0; 368 outb(devpriv->a_data, dev->iobase + PARPORT_A); 369 devpriv->c_data = 0; 370 outb(devpriv->c_data, dev->iobase + PARPORT_C); 371 372 printk("\n"); 373 return 1; 374} 375 376static int parport_detach(comedi_device * dev) 377{ 378 printk("comedi%d: parport: remove\n", dev->minor); 379 380 if (dev->iobase) 381 release_region(dev->iobase, PARPORT_SIZE); 382 383 if (dev->irq) 384 comedi_free_irq(dev->irq, dev); 385 386 return 0; 387} 388