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