lkml.org 
[lkml]   [2010]   [Feb]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCHv2 6/6] pps: add parallel port PPS client
    Date
    Add parallel port PPS client. It uses a standard method for capturing
    timestamps for assert edge transitions: getting a timestamp soon after
    an interrupt has happened. This is not a very precise source of time
    information due to interrupt handling delays. However, timestamps for
    clear edge transitions are much more precise because the interrupt
    handler continuously polls hardware port until the transition is done.
    Hardware port operations require only about 1us so the maximum error
    should not exceed this value. This was my primary goal when developing
    this client.

    Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su>
    ---
    drivers/pps/Kconfig | 2 +
    drivers/pps/Makefile | 2 +-
    drivers/pps/clients/Kconfig | 16 +++
    drivers/pps/clients/Makefile | 9 ++
    drivers/pps/clients/pps_parport.c | 211 +++++++++++++++++++++++++++++++++++++
    5 files changed, 239 insertions(+), 1 deletions(-)
    create mode 100644 drivers/pps/clients/Kconfig
    create mode 100644 drivers/pps/clients/Makefile
    create mode 100644 drivers/pps/clients/pps_parport.c

    diff --git a/drivers/pps/Kconfig b/drivers/pps/Kconfig
    index f3b14cd..3b70d9e 100644
    --- a/drivers/pps/Kconfig
    +++ b/drivers/pps/Kconfig
    @@ -31,6 +31,8 @@ config PPS_DEBUG
    messages to the system log. Select this if you are having a
    problem with PPS support and want to see more of what is going on.

    +source drivers/pps/clients/Kconfig
    +
    source drivers/pps/generators/Kconfig

    endmenu
    diff --git a/drivers/pps/Makefile b/drivers/pps/Makefile
    index b299814..1906eb7 100644
    --- a/drivers/pps/Makefile
    +++ b/drivers/pps/Makefile
    @@ -4,6 +4,6 @@

    pps_core-y := pps.o kapi.o sysfs.o
    obj-$(CONFIG_PPS) := pps_core.o
    -obj-y += generators/
    +obj-y += clients/ generators/

    ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG
    diff --git a/drivers/pps/clients/Kconfig b/drivers/pps/clients/Kconfig
    new file mode 100644
    index 0000000..969a486
    --- /dev/null
    +++ b/drivers/pps/clients/Kconfig
    @@ -0,0 +1,16 @@
    +#
    +# PPS clients configuration
    +#
    +
    +if PPS
    +
    +comment "PPS clients support"
    +
    +config PPS_CLIENT_PARPORT
    + tristate "Parallel port PPS client"
    + depends on PPS && PARPORT
    + help
    + If you say yes here you get support for a PPS source connected
    + with the interrupt pin of your parallel port.
    +
    +endif
    diff --git a/drivers/pps/clients/Makefile b/drivers/pps/clients/Makefile
    new file mode 100644
    index 0000000..95f6fc3
    --- /dev/null
    +++ b/drivers/pps/clients/Makefile
    @@ -0,0 +1,9 @@
    +#
    +# Makefile for PPS clients.
    +#
    +
    +obj-$(CONFIG_PPS_CLIENT_PARPORT) += pps_parport.o
    +
    +ifeq ($(CONFIG_PPS_DEBUG),y)
    +EXTRA_CFLAGS += -DDEBUG
    +endif
    diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c
    new file mode 100644
    index 0000000..390f8e4
    --- /dev/null
    +++ b/drivers/pps/clients/pps_parport.c
    @@ -0,0 +1,211 @@
    +/*
    + * pps_parport.c -- kernel parallel port PPS client
    + *
    + *
    + * Copyright (C) 2009 Alexander Gordeev <lasaine@lvk.cs.msu.su>
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program 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 General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    + */
    +
    +
    +/*
    + * TODO:
    + * 1. try using SA_NODELAY for parport irq handler
    + * 2. test under heavy load
    + * 3. implement echo over SEL pin
    + * 4. module parameters
    + */
    +
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/init.h>
    +#include <linux/irqnr.h>
    +#include <linux/time.h>
    +#include <linux/parport.h>
    +#include <linux/pps_kernel.h>
    +
    +#define DRVNAME "pps_parport"
    +#define DRVDESC "parallel port PPS client"
    +
    +/* maximum number of port reads when polling for signal clear */
    +#define RECEIVE_TIMEOUT 100
    +
    +/* internal per port structure */
    +struct pps_client_pp {
    + struct pardevice *pardev; /* parport device */
    + int source; /* PPS source number */
    +};
    +
    +#define SIGNAL_IS_SET(port) \
    + ((port->ops->read_status(port) & PARPORT_STATUS_ACK) != 0)
    +
    +/* parport interrupt handler */
    +static void parport_irq(void *handle)
    +{
    + struct pps_event_time ts_assert, ts_clear;
    + struct pps_client_pp *dev = handle;
    + struct parport *port = dev->pardev->port;
    + int i;
    + unsigned long flags;
    +
    + /* first of all we get the time stamp... */
    + pps_get_ts(&ts_assert);
    +
    + /* check the signal (no signal means the pulse is lost this time) */
    + if (!SIGNAL_IS_SET(port)) {
    + pr_err(DRVNAME ": lost the signal\n");
    + return;
    + }
    +
    + /* FIXME: this is here until we have a fast interrupt */
    + local_irq_save(flags);
    + /* poll the port until the signal is unset */
    + for (i = RECEIVE_TIMEOUT; i; i--)
    + if (!SIGNAL_IS_SET(port)) {
    + pps_get_ts(&ts_clear);
    + local_irq_restore(flags);
    +
    + /* FIXME: move these two calls to workqueue? */
    + /* fire assert event */
    + pps_event(dev->source, &ts_assert,
    + PPS_CAPTUREASSERT, NULL);
    + /* fire clear event */
    + pps_event(dev->source, &ts_clear,
    + PPS_CAPTURECLEAR, NULL);
    +
    + return;
    + }
    + local_irq_restore(flags);
    +
    + /* timeout */
    + pr_err(DRVNAME ": timeout in interrupt handler while waiting"
    + " for signal clear\n");
    +}
    +
    +/* the PPS echo function */
    +static void pps_echo(int source, int event, void *data)
    +{
    + pr_info("echo %s %s for source %d\n",
    + event & PPS_CAPTUREASSERT ? "assert" : "",
    + event & PPS_CAPTURECLEAR ? "clear" : "",
    + source);
    +}
    +
    +static void parport_attach(struct parport *port)
    +{
    + struct pps_client_pp *device;
    + struct pps_source_info info = {
    + .name = DRVNAME,
    + .path = "",
    + .mode = PPS_CAPTUREBOTH | \
    + PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
    + PPS_ECHOASSERT | PPS_ECHOCLEAR | \
    + PPS_CANWAIT | PPS_TSFMT_TSPEC,
    + .echo = pps_echo,
    + .owner = THIS_MODULE,
    + };
    +
    + device = kzalloc(sizeof(struct pps_client_pp), GFP_KERNEL);
    + if (!device) {
    + pr_err(DRVNAME ": memory allocation failed, not attaching\n");
    + return;
    + }
    +
    + device->pardev = parport_register_device(port, DRVNAME,
    + NULL, NULL, parport_irq, 0, device);
    + if (!device->pardev) {
    + pr_err(DRVNAME ": couldn't register with %s\n", port->name);
    + goto err_free;
    + }
    +
    + if (parport_claim_or_block(device->pardev) < 0) {
    + pr_err(DRVNAME ": couldn't claim %s\n", port->name);
    + goto err_unregister_dev;
    + }
    +
    + device->source = pps_register_source(&info,
    + PPS_CAPTUREBOTH | PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
    + if (device->source < 0) {
    + pr_err(DRVNAME ": couldn't register PPS source\n");
    + goto err_release_dev;
    + }
    +
    + port->ops->enable_irq(port);
    +
    + pr_info(DRVNAME ": attached to %s\n", port->name);
    +
    + return;
    +
    +err_release_dev:
    + parport_release(device->pardev);
    +err_unregister_dev:
    + parport_unregister_device(device->pardev);
    +err_free:
    + kfree(device);
    +}
    +
    +static void parport_detach(struct parport *port)
    +{
    + struct pardevice *pardev = port->cad;
    + struct pps_client_pp *device;
    +
    + /* oooh, this is ugly! */
    + if (strcmp(pardev->name, DRVNAME))
    + /* not our port */
    + return;
    +
    + device = pardev->private;
    +
    + port->ops->disable_irq(port);
    + pps_unregister_source(device->source);
    + parport_release(pardev);
    + parport_unregister_device(pardev);
    + kfree(device);
    +}
    +
    +static struct parport_driver pps_parport_driver = {
    + .name = DRVNAME,
    + .attach = parport_attach,
    + .detach = parport_detach,
    +};
    +
    +/* module staff */
    +
    +static int __init pps_parport_init(void)
    +{
    + int ret;
    +
    + pr_info(DRVNAME ": " DRVDESC "\n");
    +
    + ret = parport_register_driver(&pps_parport_driver);
    + if (ret) {
    + pr_err(DRVNAME ": unable to register with parport\n");
    + return ret;
    + }
    +
    + return 0;
    +}
    +
    +static void __exit pps_parport_exit(void)
    +{
    + parport_unregister_driver(&pps_parport_driver);
    +}
    +
    +module_init(pps_parport_init);
    +module_exit(pps_parport_exit);
    +
    +MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>");
    +MODULE_DESCRIPTION(DRVDESC);
    +MODULE_LICENSE("GPL");
    --
    1.6.6.1


    \
     
     \ /
      Last update: 2010-02-24 13:31    [W:0.036 / U:0.196 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site