/*
* Copyright (c) 2009-2010 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Manual pulse generator with quadrature signal output and optional
* axis selection.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MPGUNIT(x) minor(x)
int mpg_match(struct device *, void *, void *);
void mpg_attach(struct device *, struct device *, void *);
int mpg_detach(struct device *, int);
int mpg_activate(struct device *, enum devact);
struct cfattach mpg_ca = {
sizeof(struct mpg_softc),
mpg_match,
mpg_attach,
mpg_detach,
mpg_activate
};
struct cfdriver mpg_cd = {
NULL, "mpg", DV_DULL
};
int
mpg_match(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
return (strcmp(cf->cf_driver->cd_name, "mpg") == 0);
}
void
mpg_attach(struct device *parent, struct device *self, void *aux)
{
struct mpg_softc *sc = (struct mpg_softc *)self;
struct gpio_attach_args *ga = aux;
int npins, i;
/* Generic CNC device initialization. */
if (cnc_device_attach(sc, CNC_DEVICE_MPG) == -1)
return;
/* Check that we have enough pins */
npins = gpio_npins(ga->ga_mask);
if (npins < 2 || npins > MPG_PIN_LAST) {
printf(": invalid pin count\n");
return;
}
/* Map pins */
sc->sc_gpio = ga->ga_gpio;
sc->sc_map.pm_map = sc->__map;
if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask, &sc->sc_map)) {
printf(": can't map pins\n");
return;
}
/* Configure quadrature signal input pins. */
CNC_MAP_INPUT(sc, MPG_PIN_A, "A");
CNC_MAP_INPUT(sc, MPG_PIN_B, "B");
/* Use the remaining pins for axis selection. */
sc->sc_naxes = npins - 2;
/* Optional axis selection pins. */
if (sc->sc_naxes >= 1) { CNC_MAP_INPUT(sc, MPG_PIN_SELX, "SELX"); }
if (sc->sc_naxes >= 2) { CNC_MAP_INPUT(sc, MPG_PIN_SELY, "SELY"); }
if (sc->sc_naxes >= 3) { CNC_MAP_INPUT(sc, MPG_PIN_SELZ, "SELZ"); }
if (sc->sc_naxes >= 4) { CNC_MAP_INPUT(sc, MPG_PIN_SELA, "SELA"); }
if (sc->sc_naxes >= 5) { CNC_MAP_INPUT(sc, MPG_PIN_SELB, "SELB"); }
if (sc->sc_naxes >= 6) { CNC_MAP_INPUT(sc, MPG_PIN_SELC, "SELC"); }
for (i = 0; i < sc->sc_naxes; i++) {
struct mpg_axis *axis = &sc->sc_axes[i];
axis->A = 0;
axis->B = 0;
axis->Aprev = gpio_pin_read(sc->sc_gpio, &sc->sc_map, MPG_PIN_A);
axis->Bprev = gpio_pin_read(sc->sc_gpio, &sc->sc_map, MPG_PIN_B);
sc->sc_pulses[i] = 0;
}
sc->sc_sel_axis = 0;
sc->sc_mult = 1000;
sc->sc_open = 0;
printf("\n");
return;
fail:
gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
}
int
mpg_detach(struct device *self, int flags)
{
return (cnc_device_detach(self));
}
int
mpg_activate(struct device *self, enum devact act)
{
return (cnc_device_activate(self));
}
/* Return the currently selected axis */
int
mpg_get_axis(struct mpg_softc *sc)
{
int x, y, z;
switch (sc->sc_naxes) {
case 1:
x = gpio_pin_read(sc->sc_gpio, &sc->sc_map, MPG_PIN_SELX);
if (x) { return (CNC_X); }
break;
case 2:
x = gpio_pin_read(sc->sc_gpio, &sc->sc_map, MPG_PIN_SELX);
y = gpio_pin_read(sc->sc_gpio, &sc->sc_map, MPG_PIN_SELY);
if (x) { return (CNC_X); }
else if (y) { return (CNC_Y); }
break;
case 3:
x = gpio_pin_read(sc->sc_gpio, &sc->sc_map, MPG_PIN_SELX);
y = gpio_pin_read(sc->sc_gpio, &sc->sc_map, MPG_PIN_SELY);
z = gpio_pin_read(sc->sc_gpio, &sc->sc_map, MPG_PIN_SELZ);
if (x) { return (CNC_X); }
else if (y) { return (CNC_Y); }
else if (z) { return (CNC_Z); }
break;
default:
break;
}
return (CNC_X);
}
int
mpgopen(dev_t dev, int flag, int mode, struct proc *p)
{
int unit = MPGUNIT(dev);
struct mpg_softc *sc;
if (unit >= mpg_cd.cd_ndevs)
return (ENXIO);
sc = mpg_cd.cd_devs[unit];
if (sc == NULL) {
return (ENXIO);
}
if (sc->sc_open) {
return (EBUSY);
}
return (0);
}
int
mpgclose(dev_t dev, int flag, int mode, struct proc *p)
{
int unit = MPGUNIT(dev);
struct mpg_softc *sc = mpg_cd.cd_devs[unit];
sc->sc_open = 0;
return (0);
}
int
mpgread(dev_t dev, struct uio *uio, int flag)
{
int unit = MPGUNIT(dev);
struct mpg_softc *sc = mpg_cd.cd_devs[unit];
struct cnc_input_event ev;
struct mpg_axis *axis;
int s;
s = splhigh();
ev.which = mpg_get_axis(sc);
ev.data = 0;
axis = &sc->sc_axes[ev.which];
axis->A = gpio_pin_read(sc->sc_gpio, &sc->sc_map, MPG_PIN_A);
axis->B = gpio_pin_read(sc->sc_gpio, &sc->sc_map, MPG_PIN_B);
if (axis->A == axis->Aprev &&
axis->B == axis->Bprev) {
goto out;
}
if (( axis->A && !axis->B && !axis->Aprev && !axis->Bprev) ||
( axis->A && axis->B && axis->Aprev && !axis->Bprev) ||
(!axis->A && axis->B && axis->Aprev && axis->Bprev) ||
(!axis->A && !axis->B && !axis->Aprev && axis->Bprev)) {
ev.data++;
ev.type = CNC_EVENT_WHEELUP;
} else {
ev.data--;
ev.type = CNC_EVENT_WHEELDOWN;
}
axis->Aprev = axis->A;
axis->Bprev = axis->B;
out:
splx(s);
return uiomove((caddr_t)&ev, sizeof(struct cnc_input_event), uio);
}
int
mpgioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
return (ENXIO);
}