Module: xenomai-abe Branch: analogy Commit: 350062394f8d6d5f851fb6db7e9dc16a6f95b648 URL: http://git.xenomai.org/?p=xenomai-abe.git;a=commit;h=350062394f8d6d5f851fb6db7e9dc16a6f95b648
Author: Alexis Berlemont <alexis.berlem...@gmail.com> Date: Sun Nov 8 02:20:48 2009 +0100 analogy: add an analogy driver for standard parallel port WARNING: the arguments management in some instruction functions is not coherent with the one in the NI PCIMIO driver. This issue has to be fixed shortly. --- ksrc/drivers/analogy/intel/Kconfig | 9 +- ksrc/drivers/analogy/intel/Makefile | 15 +- ksrc/drivers/analogy/intel/parport.c | 428 ++++++++++++++++++++++++++++++++++ 3 files changed, 447 insertions(+), 5 deletions(-) diff --git a/ksrc/drivers/analogy/intel/Kconfig b/ksrc/drivers/analogy/intel/Kconfig index 6271d19..193c9da 100644 --- a/ksrc/drivers/analogy/intel/Kconfig +++ b/ksrc/drivers/analogy/intel/Kconfig @@ -1,5 +1,10 @@ config XENO_DRIVERS_ANALOGY_8255 - depends on XENO_DRIVERS_ANALOGY && EXPERIMENTAL + depends on XENO_DRIVERS_ANALOGY tristate "8255 driver" - default n \ No newline at end of file + default n + +config XENO_DRIVERS_ANALOGY_PARPORT + depends on XENO_DRIVERS_ANALOGY + tristate "Standard parallel port driver" + default n diff --git a/ksrc/drivers/analogy/intel/Makefile b/ksrc/drivers/analogy/intel/Makefile index 57ea839..e708c39 100644 --- a/ksrc/drivers/analogy/intel/Makefile +++ b/ksrc/drivers/analogy/intel/Makefile @@ -6,8 +6,13 @@ EXTRA_CFLAGS += -D__IN_XENOMAI__ -Iinclude/xenomai obj-$(CONFIG_XENO_DRIVERS_ANALOGY_8255) += analogy_8255.o +obj-$(CONFIG_XENO_DRIVERS_ANALOGY_PARPORT) += analogy_parport.o + analogy_8255-y := 8255.o +analogy_parport-y := parport.o + + else # Makefile frag for Linux v2.4 @@ -16,15 +21,19 @@ O_TARGET := built-in.o obj-$(CONFIG_XENO_DRIVERS_ANALOGY_8255) += analogy_8255.o +obj-$(CONFIG_XENO_DRIVERS_ANALOGY_PARPORT) += analogy_parport.o + analogy_8255-objs := 8255.o -export-objs := $(analgoy_8255-objs) +analogy_parport-objs := parport.o + +export-objs := $(analgoy_8255-objs) $(analgoy_parport-objs) EXTRA_CFLAGS += -D__IN_XENOMAI__ -I$(TOPDIR)/include/xenomai -I$(TOPDIR)/include/xenomai/compat include $(TOPDIR)/Rules.make -analogy_8255.o: $(analogy_8255-objs) - $(LD) -r -o $@ $(analogy_8255-objs) +analogy_8255.o: $(analogy_8255-objs) $(analgoy_parport-objs) + $(LD) -r -o $@ $(analogy_8255-objs) $(analgoy_parport-objs) endif diff --git a/ksrc/drivers/analogy/intel/parport.c b/ksrc/drivers/analogy/intel/parport.c new file mode 100644 index 0000000..6abc5b4 --- /dev/null +++ b/ksrc/drivers/analogy/intel/parport.c @@ -0,0 +1,428 @@ +/** + * @file + * Analogy driver for standard parallel port + * @note Copyright (C) 1998,2001 David A. Schleef <d...@schleef.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +/* + A cheap and easy way to get a few more digital I/O lines. Steal + additional parallel ports from old computers or your neighbors' + computers. + + Option list: + 0: I/O port base for the parallel port. + 1: IRQ + + Parallel Port Lines: + + pin subdev chan aka + --- ------ ---- --- + 1 2 0 strobe + 2 0 0 data 0 + 3 0 1 data 1 + 4 0 2 data 2 + 5 0 3 data 3 + 6 0 4 data 4 + 7 0 5 data 5 + 8 0 6 data 6 + 9 0 7 data 7 + 10 1 3 acknowledge + 11 1 4 busy + 12 1 2 output + 13 1 1 printer selected + 14 2 1 auto LF + 15 1 0 error + 16 2 2 init + 17 2 3 select printer + 18-25 ground + + Notes: + + Subdevices 0 is digital I/O, subdevice 1 is digital input, and + subdevice 2 is digital output. Unlike other Analogy devices, + subdevice 0 defaults to output. + + Pins 13 and 14 are inverted once by Comedi and once by the + hardware, thus cancelling the effect. + + Pin 1 is a strobe, thus acts like one. There's no way in software + to change this, at least on a standard parallel port. + + Subdevice 3 pretends to be a digital input subdevice, but it always + returns 0 when read. However, if you run a command with + scan_begin_src=TRIG_EXT, it uses pin 10 as a external triggering + pin, which can be used to wake up tasks. + + see http://www.beyondlogic.org/ for information. + or http://www.linux-magazin.de/ausgabe/1999/10/IO/io.html +*/ + +#include <linux/module.h> +#include <linux/ioport.h> +#include <analogy/analogy_driver.h> + +#define PARPORT_SIZE 3 + +#define PARPORT_A 0 +#define PARPORT_B 1 +#define PARPORT_C 2 + +typedef struct parport_subd_priv { + unsigned long io_bits; +} parport_spriv_t; + +typedef struct parport_priv { + unsigned long io_base; + unsigned int a_data; + unsigned int c_data; + int enable_irq; +} parport_priv_t; + +#define devpriv ((parport_priv_t *)(dev->priv)) + +static int parport_insn_a(a4l_subd_t *subd, a4l_kinsn_t *insn) +{ + a4l_dev_t *dev = subd->dev; + + if (insn->data[0]) { + devpriv->a_data &= ~insn->data[0]; + devpriv->a_data |= (insn->data[0] & insn->data[1]); + + outb(devpriv->a_data, devpriv->io_base + PARPORT_A); + } + + insn->data[1] = inb(devpriv->io_base + PARPORT_A); + + return 0; +} + +static int parport_insn_config_a(a4l_subd_t *subd, a4l_kinsn_t *insn) +{ + a4l_dev_t *dev = subd->dev; + parport_spriv_t *spriv = (parport_spriv_t *)subd->priv; + + if (insn->data[0]) { + spriv->io_bits = 0xff; + devpriv->c_data &= ~(1 << 5); + } else { + spriv->io_bits = 0; + devpriv->c_data |= (1 << 5); + } + outb(devpriv->c_data, devpriv->io_base + PARPORT_C); + + return 0; +} + +static int parport_insn_b(a4l_subd_t *subd, a4l_kinsn_t *insn) +{ + a4l_dev_t *dev = subd->dev; + + if (insn->data[0]) { + /* should writes be ignored? */ + } + + insn->data[1] = (inb(devpriv->io_base + PARPORT_B) >> 3); + + return 0; +} + +static int parport_insn_c(a4l_subd_t *subd, a4l_kinsn_t *insn) +{ + a4l_dev_t *dev = subd->dev; + + insn->data[0] &= 0x0f; + if (insn->data[0]) { + devpriv->c_data &= ~insn->data[0]; + devpriv->c_data |= (insn->data[0] & insn->data[1]); + + outb(devpriv->c_data, devpriv->io_base + PARPORT_C); + } + + insn->data[1] = devpriv->c_data & 0xf; + + return 2; +} + +static int parport_intr_insn(a4l_subd_t *subd, a4l_kinsn_t *insn) +{ + if (insn->data_size < 1) + return -EINVAL; + + insn->data[1] = 0; + return 0; +} + +static a4l_cmd_t parport_intr_cmd_mask = { + .idx_subd = 0, + .start_src = TRIG_NOW, + .scan_begin_src = TRIG_EXT, + .convert_src = TRIG_FOLLOW, + .scan_end_src = TRIG_COUNT, + .stop_src = TRIG_NONE, +}; + +static int parport_intr_cmdtest(a4l_subd_t *subd, a4l_cmd_t * cmd) +{ + + if (cmd->start_arg != 0) { + return -EINVAL; + } + if (cmd->scan_begin_arg != 0) { + return -EINVAL; + } + if (cmd->convert_arg != 0) { + return -EINVAL; + } + if (cmd->scan_end_arg != 1) { + return -EINVAL; + } + if (cmd->stop_arg != 0) { + return -EINVAL; + } + + return 0; +} + +static int parport_intr_cmd(a4l_subd_t *subd, a4l_cmd_t *cmd) +{ + a4l_dev_t *dev = subd->dev; + + devpriv->c_data |= 0x10; + outb(devpriv->c_data, devpriv->io_base + PARPORT_C); + + devpriv->enable_irq = 1; + + return 0; +} + +static int parport_intr_cancel(a4l_subd_t *subd) +{ + a4l_dev_t *dev = subd->dev; + + a4l_info(dev, "parport_intr_cancel: cancel in progress\n"); + + devpriv->c_data &= ~0x10; + outb(devpriv->c_data, devpriv->io_base + PARPORT_C); + + devpriv->enable_irq = 0; + + return 0; +} + +static int parport_interrupt(unsigned int irq, void *d) +{ + a4l_dev_t *dev = d; + a4l_subd_t *subd = a4l_get_subd(dev, 3); + + if (!devpriv->enable_irq) { + a4l_err(dev, "parport_interrupt: bogus irq, ignored\n"); + return IRQ_NONE; + } + + a4l_buf_put(subd, 0, sizeof(unsigned int)); + a4l_buf_evt(subd, 0); + + return 0; +} + + +/* --- Channels descriptor --- */ + +static a4l_chdesc_t parport_chan_desc_a = { + .mode = A4L_CHAN_GLOBAL_CHANDESC, + .length = 8, + .chans = { + {A4L_CHAN_AREF_GROUND, 1}, + }, +}; + +static a4l_chdesc_t parport_chan_desc_b = { + .mode = A4L_CHAN_GLOBAL_CHANDESC, + .length = 5, + .chans = { + {A4L_CHAN_AREF_GROUND, 1}, + }, +}; + +static a4l_chdesc_t parport_chan_desc_c = { + .mode = A4L_CHAN_GLOBAL_CHANDESC, + .length = 4, + .chans = { + {A4L_CHAN_AREF_GROUND, 1}, + }, +}; + +static a4l_chdesc_t parport_chan_desc_intr = { + .mode = A4L_CHAN_GLOBAL_CHANDESC, + .length = 1, + .chans = { + {A4L_CHAN_AREF_GROUND, 1}, + }, +}; + +/* --- Subdevice initialization functions --- */ + +static void setup_subd_a(a4l_subd_t *subd) +{ + subd->flags = A4L_SUBD_DIO; + subd->chan_desc = &parport_chan_desc_a; + subd->rng_desc = &range_digital; + subd->insn_bits = parport_insn_a; + subd->insn_config = parport_insn_config_a; +} + +static void setup_subd_b(a4l_subd_t *subd) +{ + subd->flags = A4L_SUBD_DI; + subd->chan_desc = &parport_chan_desc_b; + subd->rng_desc = &range_digital; + subd->insn_bits = parport_insn_b; +} + +static void setup_subd_c(a4l_subd_t *subd) +{ + subd->flags = A4L_SUBD_DO; + subd->chan_desc = &parport_chan_desc_c; + subd->rng_desc = &range_digital; + subd->insn_bits = parport_insn_c; +} + +static void setup_subd_intr(a4l_subd_t *subd) +{ + subd->flags = A4L_SUBD_DI; + subd->chan_desc = &parport_chan_desc_intr; + subd->rng_desc = &range_digital; + subd->insn_bits = parport_intr_insn; + subd->cmd_mask = &parport_intr_cmd_mask; + subd->do_cmdtest = parport_intr_cmdtest; + subd->do_cmd = parport_intr_cmd; + subd->cancel = parport_intr_cancel; +} + +static void (*setup_subds[3])(a4l_subd_t *) = { + setup_subd_a, + setup_subd_b, + setup_subd_c +}; + +static int dev_parport_attach(a4l_dev_t *dev, a4l_lnkdesc_t *arg) +{ + int i, err = 0, irq = A4L_IRQ_UNUSED; + unsigned long io_base; + + if(arg->opts == NULL || arg->opts_size < sizeof(unsigned long)) { + a4l_err(dev, + "dev_parport_attach: " + "unable to detect any parallel port, " + "no addresses / IRQ passed as attach arguments\n"); + return -EINVAL; + } + + io_base = ((unsigned long *)arg->opts)[0]; + + if (!request_region(io_base, PARPORT_SIZE, "analogy_parport")) { + a4l_err(dev, "dev_parport_attach: I/O port conflict"); + return -EIO; + } + + a4l_info(dev, "dev_parport_attach: address = 0x%lx\n", io_base); + + if (arg->opts_size == 2 * sizeof(unsigned long)) + irq = (int) ((unsigned long *)arg->opts)[1]; + + for (i = 0; i < 3; i++) { + + a4l_subd_t *subd = a4l_alloc_subd(sizeof(parport_spriv_t), + setup_subds[i]); + if (subd == NULL) + return -ENOMEM; + + err = a4l_add_subd(dev, subd); + if (err != i) + return err; + } + + if (irq != A4L_IRQ_UNUSED) { + + a4l_subd_t *subd; + + a4l_info(dev, "dev_parport_attach: irq = 0x%d\n", irq); + + err = a4l_request_irq(dev, irq, parport_interrupt, 0, dev); + if (err < 0) { + a4l_err(dev, "dev_parport_attach: irq not available\n"); + return err; + } + + subd = a4l_alloc_subd(0, setup_subd_intr); + if (subd == NULL) + return -ENOMEM; + + err = a4l_add_subd(dev, subd); + if (err < 0) + return err; + } + + devpriv->io_base = io_base; + + devpriv->a_data = 0; + outb(devpriv->a_data, devpriv->io_base + PARPORT_A); + + devpriv->c_data = 0; + outb(devpriv->c_data, devpriv->io_base + PARPORT_C); + + return 0; +} + +static int dev_parport_detach(a4l_dev_t *dev) +{ + int err = 0; + + if (devpriv->io_base != 0) + release_region(devpriv->io_base, PARPORT_SIZE); + + if (a4l_get_irq(dev) != A4L_IRQ_UNUSED) { + a4l_free_irq(dev, a4l_get_irq(dev)); + } + + + return err; +} + +static a4l_drv_t drv_parport = { + .owner = THIS_MODULE, + .board_name = "analogy_parport", + .attach = dev_parport_attach, + .detach = dev_parport_detach, + .privdata_size = sizeof(parport_priv_t), +}; + +static int __init drv_parport_init(void) +{ + return a4l_register_drv(&drv_parport); +} + +static void __exit drv_parport_cleanup(void) +{ + a4l_unregister_drv(&drv_parport); +} + +MODULE_DESCRIPTION("Analogy driver for standard parallel port"); +MODULE_LICENSE("GPL"); + +module_init(drv_parport_init); +module_exit(drv_parport_cleanup); _______________________________________________ Xenomai-git mailing list Xenomai-git@gna.org https://mail.gna.org/listinfo/xenomai-git