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