comedi_parport.c revision 7114a28011f9d5f3d981731ad341177c21f9d948
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/interrupt.h> 86#include <linux/ioport.h> 87 88#define PARPORT_SIZE 3 89 90#define PARPORT_A 0 91#define PARPORT_B 1 92#define PARPORT_C 2 93 94static int parport_attach(struct comedi_device *dev, 95 struct comedi_devconfig *it); 96static int parport_detach(struct comedi_device *dev); 97static struct comedi_driver driver_parport = { 98 .driver_name = "comedi_parport", 99 .module = THIS_MODULE, 100 .attach = parport_attach, 101 .detach = parport_detach, 102}; 103 104static int __init driver_parport_init_module(void) 105{ 106 return comedi_driver_register(&driver_parport); 107} 108 109static void __exit driver_parport_cleanup_module(void) 110{ 111 comedi_driver_unregister(&driver_parport); 112} 113 114module_init(driver_parport_init_module); 115module_exit(driver_parport_cleanup_module); 116 117struct parport_private { 118 unsigned int a_data; 119 unsigned int c_data; 120 int enable_irq; 121}; 122#define devpriv ((struct parport_private *)(dev->private)) 123 124static int parport_insn_a(struct comedi_device *dev, struct comedi_subdevice *s, 125 struct comedi_insn *insn, unsigned int *data) 126{ 127 if (data[0]) { 128 devpriv->a_data &= ~data[0]; 129 devpriv->a_data |= (data[0] & data[1]); 130 131 outb(devpriv->a_data, dev->iobase + PARPORT_A); 132 } 133 134 data[1] = inb(dev->iobase + PARPORT_A); 135 136 return 2; 137} 138 139static int parport_insn_config_a(struct comedi_device *dev, 140 struct comedi_subdevice *s, 141 struct comedi_insn *insn, unsigned int *data) 142{ 143 if (data[0]) { 144 s->io_bits = 0xff; 145 devpriv->c_data &= ~(1 << 5); 146 } else { 147 s->io_bits = 0; 148 devpriv->c_data |= (1 << 5); 149 } 150 outb(devpriv->c_data, dev->iobase + PARPORT_C); 151 152 return 1; 153} 154 155static int parport_insn_b(struct comedi_device *dev, struct comedi_subdevice *s, 156 struct comedi_insn *insn, unsigned int *data) 157{ 158 if (data[0]) { 159 /* should writes be ignored? */ 160 /* anyone??? */ 161 } 162 163 data[1] = (inb(dev->iobase + PARPORT_B) >> 3); 164 165 return 2; 166} 167 168static int parport_insn_c(struct comedi_device *dev, struct comedi_subdevice *s, 169 struct comedi_insn *insn, unsigned int *data) 170{ 171 data[0] &= 0x0f; 172 if (data[0]) { 173 devpriv->c_data &= ~data[0]; 174 devpriv->c_data |= (data[0] & data[1]); 175 176 outb(devpriv->c_data, dev->iobase + PARPORT_C); 177 } 178 179 data[1] = devpriv->c_data & 0xf; 180 181 return 2; 182} 183 184static int parport_intr_insn(struct comedi_device *dev, 185 struct comedi_subdevice *s, 186 struct comedi_insn *insn, unsigned int *data) 187{ 188 if (insn->n < 1) 189 return -EINVAL; 190 191 data[1] = 0; 192 return 2; 193} 194 195static int parport_intr_cmdtest(struct comedi_device *dev, 196 struct comedi_subdevice *s, 197 struct comedi_cmd *cmd) 198{ 199 int err = 0; 200 int tmp; 201 202 /* step 1 */ 203 204 tmp = cmd->start_src; 205 cmd->start_src &= TRIG_NOW; 206 if (!cmd->start_src || tmp != cmd->start_src) 207 err++; 208 209 tmp = cmd->scan_begin_src; 210 cmd->scan_begin_src &= TRIG_EXT; 211 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) 212 err++; 213 214 tmp = cmd->convert_src; 215 cmd->convert_src &= TRIG_FOLLOW; 216 if (!cmd->convert_src || tmp != cmd->convert_src) 217 err++; 218 219 tmp = cmd->scan_end_src; 220 cmd->scan_end_src &= TRIG_COUNT; 221 if (!cmd->scan_end_src || tmp != cmd->scan_end_src) 222 err++; 223 224 tmp = cmd->stop_src; 225 cmd->stop_src &= TRIG_NONE; 226 if (!cmd->stop_src || tmp != cmd->stop_src) 227 err++; 228 229 if (err) 230 return 1; 231 232 /* step 2: ignored */ 233 234 if (err) 235 return 2; 236 237 /* step 3: */ 238 239 if (cmd->start_arg != 0) { 240 cmd->start_arg = 0; 241 err++; 242 } 243 if (cmd->scan_begin_arg != 0) { 244 cmd->scan_begin_arg = 0; 245 err++; 246 } 247 if (cmd->convert_arg != 0) { 248 cmd->convert_arg = 0; 249 err++; 250 } 251 if (cmd->scan_end_arg != 1) { 252 cmd->scan_end_arg = 1; 253 err++; 254 } 255 if (cmd->stop_arg != 0) { 256 cmd->stop_arg = 0; 257 err++; 258 } 259 260 if (err) 261 return 3; 262 263 /* step 4: ignored */ 264 265 if (err) 266 return 4; 267 268 return 0; 269} 270 271static int parport_intr_cmd(struct comedi_device *dev, 272 struct comedi_subdevice *s) 273{ 274 devpriv->c_data |= 0x10; 275 outb(devpriv->c_data, dev->iobase + PARPORT_C); 276 277 devpriv->enable_irq = 1; 278 279 return 0; 280} 281 282static int parport_intr_cancel(struct comedi_device *dev, 283 struct comedi_subdevice *s) 284{ 285 printk(KERN_DEBUG "parport_intr_cancel()\n"); 286 287 devpriv->c_data &= ~0x10; 288 outb(devpriv->c_data, dev->iobase + PARPORT_C); 289 290 devpriv->enable_irq = 0; 291 292 return 0; 293} 294 295static irqreturn_t parport_interrupt(int irq, void *d) 296{ 297 struct comedi_device *dev = d; 298 struct comedi_subdevice *s = dev->subdevices + 3; 299 300 if (!devpriv->enable_irq) { 301 printk(KERN_ERR "comedi_parport: bogus irq, ignored\n"); 302 return IRQ_NONE; 303 } 304 305 comedi_buf_put(s->async, 0); 306 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; 307 308 comedi_event(dev, s); 309 return IRQ_HANDLED; 310} 311 312static int parport_attach(struct comedi_device *dev, 313 struct comedi_devconfig *it) 314{ 315 int ret; 316 unsigned int irq; 317 unsigned long iobase; 318 struct comedi_subdevice *s; 319 320 iobase = it->options[0]; 321 printk(KERN_INFO "comedi%d: parport: 0x%04lx ", dev->minor, iobase); 322 if (!request_region(iobase, PARPORT_SIZE, "parport (comedi)")) { 323 printk(KERN_ERR "I/O port conflict\n"); 324 return -EIO; 325 } 326 dev->iobase = iobase; 327 328 irq = it->options[1]; 329 if (irq) { 330 printk(KERN_INFO " irq=%u", irq); 331 ret = request_irq(irq, parport_interrupt, 0, "comedi_parport", 332 dev); 333 if (ret < 0) { 334 printk(KERN_ERR " irq not available\n"); 335 return -EINVAL; 336 } 337 dev->irq = irq; 338 } 339 dev->board_name = "parport"; 340 341 ret = alloc_subdevices(dev, 4); 342 if (ret < 0) 343 return ret; 344 ret = alloc_private(dev, sizeof(struct parport_private)); 345 if (ret < 0) 346 return ret; 347 348 s = dev->subdevices + 0; 349 s->type = COMEDI_SUBD_DIO; 350 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 351 s->n_chan = 8; 352 s->maxdata = 1; 353 s->range_table = &range_digital; 354 s->insn_bits = parport_insn_a; 355 s->insn_config = parport_insn_config_a; 356 357 s = dev->subdevices + 1; 358 s->type = COMEDI_SUBD_DI; 359 s->subdev_flags = SDF_READABLE; 360 s->n_chan = 5; 361 s->maxdata = 1; 362 s->range_table = &range_digital; 363 s->insn_bits = parport_insn_b; 364 365 s = dev->subdevices + 2; 366 s->type = COMEDI_SUBD_DO; 367 s->subdev_flags = SDF_WRITABLE; 368 s->n_chan = 4; 369 s->maxdata = 1; 370 s->range_table = &range_digital; 371 s->insn_bits = parport_insn_c; 372 373 s = dev->subdevices + 3; 374 if (irq) { 375 dev->read_subdev = s; 376 s->type = COMEDI_SUBD_DI; 377 s->subdev_flags = SDF_READABLE | SDF_CMD_READ; 378 s->n_chan = 1; 379 s->maxdata = 1; 380 s->range_table = &range_digital; 381 s->insn_bits = parport_intr_insn; 382 s->do_cmdtest = parport_intr_cmdtest; 383 s->do_cmd = parport_intr_cmd; 384 s->cancel = parport_intr_cancel; 385 } else { 386 s->type = COMEDI_SUBD_UNUSED; 387 } 388 389 devpriv->a_data = 0; 390 outb(devpriv->a_data, dev->iobase + PARPORT_A); 391 devpriv->c_data = 0; 392 outb(devpriv->c_data, dev->iobase + PARPORT_C); 393 394 printk(KERN_INFO "\n"); 395 return 1; 396} 397 398static int parport_detach(struct comedi_device *dev) 399{ 400 printk(KERN_INFO "comedi%d: parport: remove\n", dev->minor); 401 402 if (dev->iobase) 403 release_region(dev->iobase, PARPORT_SIZE); 404 405 if (dev->irq) 406 free_irq(dev->irq, dev); 407 408 return 0; 409} 410 411MODULE_AUTHOR("Comedi http://www.comedi.org"); 412MODULE_DESCRIPTION("Comedi low-level driver"); 413MODULE_LICENSE("GPL"); 414