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