cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

comedi_parport.c (7975B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * comedi_parport.c
      4 * Comedi driver for standard parallel port
      5 *
      6 * For more information see:
      7 *	http://retired.beyondlogic.org/spp/parallel.htm
      8 *
      9 * COMEDI - Linux Control and Measurement Device Interface
     10 * Copyright (C) 1998,2001 David A. Schleef <ds@schleef.org>
     11 */
     12
     13/*
     14 * Driver: comedi_parport
     15 * Description: Standard PC parallel port
     16 * Author: ds
     17 * Status: works in immediate mode
     18 * Devices: [standard] parallel port (comedi_parport)
     19 * Updated: Tue, 30 Apr 2002 21:11:45 -0700
     20 *
     21 * A cheap and easy way to get a few more digital I/O lines. Steal
     22 * additional parallel ports from old computers or your neighbors'
     23 * computers.
     24 *
     25 * Option list:
     26 *   0: I/O port base for the parallel port.
     27 *   1: IRQ (optional)
     28 *
     29 * Parallel Port Lines:
     30 *
     31 *	 pin   subdev  chan  type  name
     32 *	-----  ------  ----  ----  --------------
     33 *	  1      2       0    DO   strobe
     34 *	  2      0       0    DIO  data 0
     35 *	  3      0       1    DIO  data 1
     36 *	  4      0       2    DIO  data 2
     37 *	  5      0       3    DIO  data 3
     38 *	  6      0       4    DIO  data 4
     39 *	  7      0       5    DIO  data 5
     40 *	  8      0       6    DIO  data 6
     41 *	  9      0       7    DIO  data 7
     42 *	 10      1       3    DI   ack
     43 *	 11      1       4    DI   busy
     44 *	 12      1       2    DI   paper out
     45 *	 13      1       1    DI   select in
     46 *	 14      2       1    DO   auto LF
     47 *	 15      1       0    DI   error
     48 *	 16      2       2    DO   init
     49 *	 17      2       3    DO   select printer
     50 *	18-25                      ground
     51 *
     52 * When an IRQ is configured subdevice 3 pretends to be a digital
     53 * input subdevice, but it always returns 0 when read. However, if
     54 * you run a command with scan_begin_src=TRIG_EXT, it uses pin 10
     55 * as a external trigger, which can be used to wake up tasks.
     56 */
     57
     58#include <linux/module.h>
     59#include <linux/interrupt.h>
     60#include <linux/comedi/comedidev.h>
     61
     62/*
     63 * Register map
     64 */
     65#define PARPORT_DATA_REG	0x00
     66#define PARPORT_STATUS_REG	0x01
     67#define PARPORT_CTRL_REG	0x02
     68#define PARPORT_CTRL_IRQ_ENA	BIT(4)
     69#define PARPORT_CTRL_BIDIR_ENA	BIT(5)
     70
     71static int parport_data_reg_insn_bits(struct comedi_device *dev,
     72				      struct comedi_subdevice *s,
     73				      struct comedi_insn *insn,
     74				      unsigned int *data)
     75{
     76	if (comedi_dio_update_state(s, data))
     77		outb(s->state, dev->iobase + PARPORT_DATA_REG);
     78
     79	data[1] = inb(dev->iobase + PARPORT_DATA_REG);
     80
     81	return insn->n;
     82}
     83
     84static int parport_data_reg_insn_config(struct comedi_device *dev,
     85					struct comedi_subdevice *s,
     86					struct comedi_insn *insn,
     87					unsigned int *data)
     88{
     89	unsigned int ctrl;
     90	int ret;
     91
     92	ret = comedi_dio_insn_config(dev, s, insn, data, 0xff);
     93	if (ret)
     94		return ret;
     95
     96	ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
     97	if (s->io_bits)
     98		ctrl &= ~PARPORT_CTRL_BIDIR_ENA;
     99	else
    100		ctrl |= PARPORT_CTRL_BIDIR_ENA;
    101	outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
    102
    103	return insn->n;
    104}
    105
    106static int parport_status_reg_insn_bits(struct comedi_device *dev,
    107					struct comedi_subdevice *s,
    108					struct comedi_insn *insn,
    109					unsigned int *data)
    110{
    111	data[1] = inb(dev->iobase + PARPORT_STATUS_REG) >> 3;
    112
    113	return insn->n;
    114}
    115
    116static int parport_ctrl_reg_insn_bits(struct comedi_device *dev,
    117				      struct comedi_subdevice *s,
    118				      struct comedi_insn *insn,
    119				      unsigned int *data)
    120{
    121	unsigned int ctrl;
    122
    123	if (comedi_dio_update_state(s, data)) {
    124		ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
    125		ctrl &= (PARPORT_CTRL_IRQ_ENA | PARPORT_CTRL_BIDIR_ENA);
    126		ctrl |= s->state;
    127		outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
    128	}
    129
    130	data[1] = s->state;
    131
    132	return insn->n;
    133}
    134
    135static int parport_intr_insn_bits(struct comedi_device *dev,
    136				  struct comedi_subdevice *s,
    137				  struct comedi_insn *insn,
    138				  unsigned int *data)
    139{
    140	data[1] = 0;
    141	return insn->n;
    142}
    143
    144static int parport_intr_cmdtest(struct comedi_device *dev,
    145				struct comedi_subdevice *s,
    146				struct comedi_cmd *cmd)
    147{
    148	int err = 0;
    149
    150	/* Step 1 : check if triggers are trivially valid */
    151
    152	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
    153	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
    154	err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
    155	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
    156	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
    157
    158	if (err)
    159		return 1;
    160
    161	/* Step 2a : make sure trigger sources are unique */
    162	/* Step 2b : and mutually compatible */
    163
    164	/* Step 3: check if arguments are trivially valid */
    165
    166	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
    167	err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
    168	err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
    169	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
    170					   cmd->chanlist_len);
    171	err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
    172
    173	if (err)
    174		return 3;
    175
    176	/* Step 4: fix up any arguments */
    177
    178	/* Step 5: check channel list if it exists */
    179
    180	return 0;
    181}
    182
    183static int parport_intr_cmd(struct comedi_device *dev,
    184			    struct comedi_subdevice *s)
    185{
    186	unsigned int ctrl;
    187
    188	ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
    189	ctrl |= PARPORT_CTRL_IRQ_ENA;
    190	outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
    191
    192	return 0;
    193}
    194
    195static int parport_intr_cancel(struct comedi_device *dev,
    196			       struct comedi_subdevice *s)
    197{
    198	unsigned int ctrl;
    199
    200	ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
    201	ctrl &= ~PARPORT_CTRL_IRQ_ENA;
    202	outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
    203
    204	return 0;
    205}
    206
    207static irqreturn_t parport_interrupt(int irq, void *d)
    208{
    209	struct comedi_device *dev = d;
    210	struct comedi_subdevice *s = dev->read_subdev;
    211	unsigned int ctrl;
    212	unsigned short val = 0;
    213
    214	ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
    215	if (!(ctrl & PARPORT_CTRL_IRQ_ENA))
    216		return IRQ_NONE;
    217
    218	comedi_buf_write_samples(s, &val, 1);
    219	comedi_handle_events(dev, s);
    220
    221	return IRQ_HANDLED;
    222}
    223
    224static int parport_attach(struct comedi_device *dev,
    225			  struct comedi_devconfig *it)
    226{
    227	struct comedi_subdevice *s;
    228	int ret;
    229
    230	ret = comedi_request_region(dev, it->options[0], 0x03);
    231	if (ret)
    232		return ret;
    233
    234	if (it->options[1]) {
    235		ret = request_irq(it->options[1], parport_interrupt, 0,
    236				  dev->board_name, dev);
    237		if (ret == 0)
    238			dev->irq = it->options[1];
    239	}
    240
    241	ret = comedi_alloc_subdevices(dev, dev->irq ? 4 : 3);
    242	if (ret)
    243		return ret;
    244
    245	/* Digial I/O subdevice - Parallel port DATA register */
    246	s = &dev->subdevices[0];
    247	s->type		= COMEDI_SUBD_DIO;
    248	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
    249	s->n_chan	= 8;
    250	s->maxdata	= 1;
    251	s->range_table	= &range_digital;
    252	s->insn_bits	= parport_data_reg_insn_bits;
    253	s->insn_config	= parport_data_reg_insn_config;
    254
    255	/* Digial Input subdevice - Parallel port STATUS register */
    256	s = &dev->subdevices[1];
    257	s->type		= COMEDI_SUBD_DI;
    258	s->subdev_flags	= SDF_READABLE;
    259	s->n_chan	= 5;
    260	s->maxdata	= 1;
    261	s->range_table	= &range_digital;
    262	s->insn_bits	= parport_status_reg_insn_bits;
    263
    264	/* Digial Output subdevice - Parallel port CONTROL register */
    265	s = &dev->subdevices[2];
    266	s->type		= COMEDI_SUBD_DO;
    267	s->subdev_flags	= SDF_WRITABLE;
    268	s->n_chan	= 4;
    269	s->maxdata	= 1;
    270	s->range_table	= &range_digital;
    271	s->insn_bits	= parport_ctrl_reg_insn_bits;
    272
    273	if (dev->irq) {
    274		/* Digial Input subdevice - Interrupt support */
    275		s = &dev->subdevices[3];
    276		dev->read_subdev = s;
    277		s->type		= COMEDI_SUBD_DI;
    278		s->subdev_flags	= SDF_READABLE | SDF_CMD_READ;
    279		s->n_chan	= 1;
    280		s->maxdata	= 1;
    281		s->range_table	= &range_digital;
    282		s->insn_bits	= parport_intr_insn_bits;
    283		s->len_chanlist	= 1;
    284		s->do_cmdtest	= parport_intr_cmdtest;
    285		s->do_cmd	= parport_intr_cmd;
    286		s->cancel	= parport_intr_cancel;
    287	}
    288
    289	outb(0, dev->iobase + PARPORT_DATA_REG);
    290	outb(0, dev->iobase + PARPORT_CTRL_REG);
    291
    292	return 0;
    293}
    294
    295static struct comedi_driver parport_driver = {
    296	.driver_name	= "comedi_parport",
    297	.module		= THIS_MODULE,
    298	.attach		= parport_attach,
    299	.detach		= comedi_legacy_detach,
    300};
    301module_comedi_driver(parport_driver);
    302
    303MODULE_AUTHOR("Comedi https://www.comedi.org");
    304MODULE_DESCRIPTION("Comedi: Standard parallel port driver");
    305MODULE_LICENSE("GPL");