summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorrink <rink@FreeBSD.org>2008-03-06 08:09:45 +0000
committerrink <rink@FreeBSD.org>2008-03-06 08:09:45 +0000
commitee4e7828cdb57507969deca833b219caff37cc93 (patch)
tree070f46e4e7f09e55dbbe8d10c82b57ff8da3a7cf /sys/dev
parent93eb2896b594fd48abfd536e527490d24931750e (diff)
downloadFreeBSD-src-ee4e7828cdb57507969deca833b219caff37cc93.zip
FreeBSD-src-ee4e7828cdb57507969deca833b219caff37cc93.tar.gz
Commit cmx(4), a driver for Omnikey CardMan 4040 PCMCIA smartcard readers.
PR: kern/114582 Submitted by: Daniel Roethlisberger <daniel@roe.ch> Reviewed by: imp, myself Tested by: johans, myself MFC after: 2 weeks
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/cmx/cmx.c701
-rw-r--r--sys/dev/cmx/cmx_pccard.c115
-rw-r--r--sys/dev/cmx/cmxreg.h66
-rw-r--r--sys/dev/cmx/cmxvar.h99
-rw-r--r--sys/dev/pccard/pccarddevs4
5 files changed, 985 insertions, 0 deletions
diff --git a/sys/dev/cmx/cmx.c b/sys/dev/cmx/cmx.c
new file mode 100644
index 0000000..69cf892
--- /dev/null
+++ b/sys/dev/cmx/cmx.c
@@ -0,0 +1,701 @@
+/*-
+ * Copyright (c) 2006-2007 Daniel Roethlisberger <daniel@roe.ch>
+ * Copyright (c) 2000-2004 OMNIKEY GmbH (www.omnikey.com)
+ * 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 unmodified, 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * OMNIKEY CardMan 4040 a.k.a. CardMan eXtended (cmx) driver.
+ * This is a PCMCIA based smartcard reader which seems to work
+ * like an I/O port mapped USB CCID smartcard device.
+ *
+ * I/O originally based on Linux driver version 1.1.0 by OMNIKEY.
+ * Dual GPL/BSD. Almost all of the code has been rewritten.
+ * $Omnikey: cm4040_cs.c,v 1.7 2004/10/04 09:08:50 jp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/poll.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/uio.h>
+#include <sys/selinfo.h>
+
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/cmx/cmxvar.h>
+#include <dev/cmx/cmxreg.h>
+
+#ifdef CMX_DEBUG
+#define DEBUG_printf(dev, fmt, args...) \
+ device_printf(dev, "%s: " fmt, __FUNCTION__, ##args)
+#else
+#define DEBUG_printf(dev, fmt, args...)
+#endif
+
+#define SPIN_COUNT 1000
+#define WAIT_TICKS (hz/100)
+#define POLL_TICKS (hz/10)
+
+/* possibly bogus */
+#define CCID_DRIVER_BULK_DEFAULT_TIMEOUT (150*hz)
+#define CCID_DRIVER_ASYNC_POWERUP_TIMEOUT (35*hz)
+#define CCID_DRIVER_MINIMUM_TIMEOUT (3*hz)
+
+#ifdef CMX_DEBUG
+static char BSRBITS[] = "\020"
+ "\01BULK_OUT_FULL" /* 0x01 */
+ "\02BULK_IN_FULL" /* 0x02 */
+ "\03(0x04)"; /* 0x04 */
+#ifdef CMX_INTR
+static char SCRBITS[] = "\020"
+ "\01POWER_DOWN" /* 0x01 */
+ "\02PULSE_INTERRUPT" /* 0x02 */
+ "\03HOST_TO_READER_DONE" /* 0x04 */
+ "\04READER_TO_HOST_DONE" /* 0x08 */
+ "\05ACK_NOTIFY" /* 0x10 */
+ "\06EN_NOTIFY" /* 0x20 */
+ "\07ABORT" /* 0x40 */
+ "\10HOST_TO_READER_START"; /* 0x80 */
+#endif /* CMX_INTR */
+static char POLLBITS[] = "\020"
+ "\01POLLIN" /* 0x0001 */
+ "\02POLLPRI" /* 0x0002 */
+ "\03POLLOUT" /* 0x0004 */
+ "\04POLLERR" /* 0x0008 */
+ "\05POLLHUP" /* 0x0010 */
+ "\06POLLINVAL" /* 0x0020 */
+ "\07POLLRDNORM" /* 0x0040 */
+ "\10POLLRDBAND" /* 0x0080 */
+ "\11POLLWRBAND"; /* 0x0100 */
+static char MODEBITS[] = "\020"
+ "\01READ" /* 0x0001 */
+ "\02WRITE" /* 0x0002 */
+ "\03NONBLOCK" /* 0x0004 */
+ "\04APPEND" /* 0x0008 */
+ "\05SHLOCK" /* 0x0010 */
+ "\06EXLOCK" /* 0x0020 */
+ "\07ASYNC" /* 0x0040 */
+ "\10FSYNC" /* 0x0080 */
+ "\11NOFOLLOW" /* 0x0100 */
+ "\12CREAT" /* 0x0200 */
+ "\13TRUNK" /* 0x0400 */
+ "\14EXCL" /* 0x0800 */
+ "\15(0x1000)" /* 0x1000 */
+ "\16(0x2000)" /* 0x2000 */
+ "\17HASLOCK" /* 0x4000 */
+ "\20NOCTTY" /* 0x8000 */
+ "\21DIRECT"; /* 0x00010000 */
+#endif /* CMX_DEBUG */
+
+devclass_t cmx_devclass;
+
+static d_open_t cmx_open;
+static d_close_t cmx_close;
+static d_read_t cmx_read;
+static d_write_t cmx_write;
+static d_poll_t cmx_poll;
+#ifdef CMX_INTR
+static void cmx_intr(void *arg);
+#endif
+
+static struct cdevsw cmx_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = cmx_open,
+ .d_close = cmx_close,
+ .d_read = cmx_read,
+ .d_write = cmx_write,
+ .d_poll = cmx_poll,
+ .d_name = "cmx",
+};
+
+/*
+ * Initialize the softc structure. Must be called from
+ * the bus specific device allocation routine.
+ */
+void
+cmx_init_softc(device_t dev)
+{
+ struct cmx_softc *sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
+}
+
+/*
+ * Allocate driver resources. Must be called from the
+ * bus specific device allocation routine. Caller must
+ * ensure to call cmx_release_resources to free the
+ * resources when detaching.
+ * Return zero if successful, and ENOMEM if the resources
+ * could not be allocated.
+ */
+int
+cmx_alloc_resources(device_t dev)
+{
+ struct cmx_softc *sc = device_get_softc(dev);
+#ifdef CMX_INTR
+ int rv;
+#endif
+
+ sc->ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
+ &sc->ioport_rid, RF_ACTIVE);
+ if (!sc->ioport) {
+ device_printf(dev, "failed to allocate io port\n");
+ return ENOMEM;
+ }
+ sc->bst = rman_get_bustag(sc->ioport);
+ sc->bsh = rman_get_bushandle(sc->ioport);
+
+#ifdef CMX_INTR
+ sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->irq_rid, RF_ACTIVE);
+ if (!sc->irq) {
+ device_printf(dev, "failed to allocate irq\n");
+ return ENOMEM;
+ }
+ if ((rv = bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY,
+ cmx_intr, sc, &sc->ih)) != 0) {
+ device_printf(dev, "failed to set up irq\n");
+ return ENOMEM;
+ }
+#endif
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev),
+ "cmx softc lock",
+ MTX_DEF | MTX_RECURSE);
+ callout_init_mtx(&sc->ch, &sc->mtx, 0);
+
+ return 0;
+}
+
+/*
+ * Release the resources allocated by cmx_allocate_resources.
+ */
+void
+cmx_release_resources(device_t dev)
+{
+ struct cmx_softc *sc = device_get_softc(dev);
+
+ mtx_destroy(&sc->mtx);
+
+#ifdef CMX_INTR
+ if (sc->ih) {
+ bus_teardown_intr(dev, sc->irq, sc->ih);
+ sc->ih = NULL;
+ }
+ if (sc->irq) {
+ bus_release_resource(dev, SYS_RES_IRQ,
+ sc->irq_rid, sc->irq);
+ sc->irq = NULL;
+ }
+#endif
+
+ if (sc->ioport) {
+ bus_deactivate_resource(dev, SYS_RES_IOPORT,
+ sc->ioport_rid, sc->ioport);
+ bus_release_resource(dev, SYS_RES_IOPORT,
+ sc->ioport_rid, sc->ioport);
+ sc->ioport = NULL;
+ }
+ return;
+}
+
+/*
+ * Bus independant device attachment routine. Creates the
+ * character device node.
+ */
+int
+cmx_attach(device_t dev)
+{
+ struct cmx_softc *sc = device_get_softc(dev);
+
+ if (!sc || sc->dying)
+ return ENXIO;
+
+ sc->cdev = make_dev(&cmx_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
+ "cmx%d", device_get_unit(dev));
+ if (!sc->cdev) {
+ device_printf(dev, "failed to create character device\n");
+ return ENOMEM;
+ }
+ sc->cdev->si_drv1 = sc;
+
+ return 0;
+}
+
+/*
+ * Bus independant device detachment routine. Makes sure all
+ * allocated resources are freed, callouts disabled and waiting
+ * processes unblocked.
+ */
+int
+cmx_detach(device_t dev)
+{
+ struct cmx_softc *sc = device_get_softc(dev);
+
+ DEBUG_printf(dev, "called\n");
+
+ sc->dying = 1;
+
+ CMX_LOCK(sc);
+ if (sc->polling) {
+ DEBUG_printf(sc->dev, "disabling polling\n");
+ callout_stop(&sc->ch);
+ sc->polling = 0;
+ CMX_UNLOCK(sc);
+ callout_drain(&sc->ch);
+ selwakeuppri(&sc->sel, PZERO);
+ } else {
+ CMX_UNLOCK(sc);
+ }
+
+ wakeup(sc);
+ destroy_dev(sc->cdev);
+
+ DEBUG_printf(dev, "releasing resources\n");
+ cmx_release_resources(dev);
+ return 0;
+}
+
+/*
+ * Wait for buffer status register events. If test is non-zero,
+ * wait until flags are set, otherwise wait until flags are unset.
+ * Will spin SPIN_COUNT times, then sleep until timeout is reached.
+ * Returns zero if event happened, EIO if the timeout was reached,
+ * and ENXIO if the device was detached in the meantime. When that
+ * happens, the caller must quit immediately, since a detach is
+ * in progress.
+ */
+static inline int
+cmx_wait_BSR(struct cmx_softc *sc, uint8_t flags, int test)
+{
+ int rv;
+
+ for (int i = 0; i < SPIN_COUNT; i++) {
+ if (cmx_test_BSR(sc, flags, test))
+ return 0;
+ }
+
+ for (int i = 0; i * WAIT_TICKS < sc->timeout; i++) {
+ if (cmx_test_BSR(sc, flags, test))
+ return 0;
+ rv = tsleep(sc, PWAIT|PCATCH, "cmx", WAIT_TICKS);
+ /*
+ * Currently, the only reason for waking up with
+ * rv == 0 is when we are detaching, in which
+ * case sc->dying is always 1.
+ */
+ if (sc->dying)
+ return ENXIO;
+ if (rv != EAGAIN)
+ return rv;
+ }
+
+ /* timeout */
+ return EIO;
+}
+
+/*
+ * Set the sync control register to val. Before and after writing
+ * to the SCR, we wait for the BSR to not signal BULK_OUT_FULL.
+ * Returns zero if successful, or whatever errors cmx_wait_BSR can
+ * return. ENXIO signals that the device has been detached in the
+ * meantime, and that we should leave the kernel immediately.
+ */
+static inline int
+cmx_sync_write_SCR(struct cmx_softc *sc, uint8_t val)
+{
+ int rv = 0;
+
+ if ((rv = cmx_wait_BSR(sc, BSR_BULK_OUT_FULL, 0)) != 0) {
+ return rv;
+ }
+
+ cmx_write_SCR(sc, val);
+
+ if ((rv = cmx_wait_BSR(sc, BSR_BULK_OUT_FULL, 0)) != 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+/*
+ * Returns a suitable timeout value based on the given command byte.
+ * Some commands appear to need longer timeout values than others.
+ */
+static inline unsigned long
+cmx_timeout_by_cmd(uint8_t cmd)
+{
+ switch (cmd) {
+ case CMD_PC_TO_RDR_XFRBLOCK:
+ case CMD_PC_TO_RDR_SECURE:
+ case CMD_PC_TO_RDR_TEST_SECURE:
+ case CMD_PC_TO_RDR_OK_SECURE:
+ return CCID_DRIVER_BULK_DEFAULT_TIMEOUT;
+
+ case CMD_PC_TO_RDR_ICCPOWERON:
+ return CCID_DRIVER_ASYNC_POWERUP_TIMEOUT;
+
+ case CMD_PC_TO_RDR_GETSLOTSTATUS:
+ case CMD_PC_TO_RDR_ICCPOWEROFF:
+ case CMD_PC_TO_RDR_GETPARAMETERS:
+ case CMD_PC_TO_RDR_RESETPARAMETERS:
+ case CMD_PC_TO_RDR_SETPARAMETERS:
+ case CMD_PC_TO_RDR_ESCAPE:
+ case CMD_PC_TO_RDR_ICCCLOCK:
+ default:
+ return CCID_DRIVER_MINIMUM_TIMEOUT;
+ }
+}
+
+/*
+ * Periodical callout routine, polling the reader for data
+ * availability. If the reader signals data ready for reading,
+ * wakes up the processes which are waiting in select()/poll().
+ * Otherwise, reschedules itself with a delay of POLL_TICKS.
+ */
+static void
+cmx_tick(void *xsc)
+{
+ struct cmx_softc *sc = xsc;
+ uint8_t bsr;
+
+ CMX_LOCK(sc);
+ if (sc->polling && !sc->dying) {
+ bsr = cmx_read_BSR(sc);
+ DEBUG_printf(sc->dev, "BSR=%b\n", bsr, BSRBITS);
+ if (cmx_test(bsr, BSR_BULK_IN_FULL, 1)) {
+ sc->polling = 0;
+ selwakeuppri(&sc->sel, PZERO);
+ } else {
+ callout_reset(&sc->ch, POLL_TICKS, cmx_tick, sc);
+ }
+ }
+ CMX_UNLOCK(sc);
+}
+
+/*
+ * Open the character device. Only a single process may open the
+ * device at a time.
+ */
+static int
+cmx_open(struct cdev *cdev, int flags, int fmt, struct thread *td)
+{
+ struct cmx_softc *sc = cdev->si_drv1;
+
+ if (sc == NULL || sc->dying)
+ return ENXIO;
+
+ CMX_LOCK(sc);
+ if (sc->open) {
+ CMX_UNLOCK(sc);
+ return EBUSY;
+ }
+ sc->open = 1;
+ CMX_UNLOCK(sc);
+
+ DEBUG_printf(sc->dev, "open (flags=%b thread=%p)\n",
+ flags, MODEBITS, td);
+ return 0;
+}
+
+/*
+ * Close the character device.
+ */
+static int
+cmx_close(struct cdev *cdev, int flags, int fmt, struct thread *td)
+{
+ struct cmx_softc *sc = cdev->si_drv1;
+
+ if (sc == NULL || sc->dying)
+ return ENXIO;
+
+ CMX_LOCK(sc);
+ if (!sc->open) {
+ CMX_UNLOCK(sc);
+ return EINVAL;
+ }
+ if (sc->polling) {
+ DEBUG_printf(sc->dev, "disabling polling\n");
+ callout_stop(&sc->ch);
+ sc->polling = 0;
+ CMX_UNLOCK(sc);
+ callout_drain(&sc->ch);
+ selwakeuppri(&sc->sel, PZERO);
+ CMX_LOCK(sc);
+ }
+ sc->open = 0;
+ CMX_UNLOCK(sc);
+
+ DEBUG_printf(sc->dev, "close (flags=%b thread=%p)\n",
+ flags, MODEBITS, td);
+ return 0;
+}
+
+/*
+ * Read from the character device.
+ * Returns zero if successful, ENXIO if dying, EINVAL if an attempt
+ * was made to read less than CMX_MIN_RDLEN bytes or less than the
+ * device has available, or any of the errors that cmx_sync_write_SCR
+ * can return. Partial reads are not supported.
+ */
+static int
+cmx_read(struct cdev *cdev, struct uio *uio, int flag)
+{
+ struct cmx_softc *sc = cdev->si_drv1;
+ unsigned long bytes_left;
+ uint8_t uc;
+ int rv, amnt, offset;
+
+ if (sc == NULL || sc->dying)
+ return ENXIO;
+
+ DEBUG_printf(sc->dev, "called (len=%d flag=%b)\n",
+ uio->uio_resid, flag, MODEBITS);
+
+ CMX_LOCK(sc);
+ if (sc->polling) {
+ DEBUG_printf(sc->dev, "disabling polling\n");
+ callout_stop(&sc->ch);
+ sc->polling = 0;
+ CMX_UNLOCK(sc);
+ callout_drain(&sc->ch);
+ selwakeuppri(&sc->sel, PZERO);
+ } else {
+ CMX_UNLOCK(sc);
+ }
+
+ if (uio->uio_resid == 0) {
+ return 0;
+ }
+
+ if (uio->uio_resid < CMX_MIN_RDLEN) {
+ return EINVAL;
+ }
+
+ if (flag & O_NONBLOCK) {
+ if (cmx_test_BSR(sc, BSR_BULK_IN_FULL, 0)) {
+ return EAGAIN;
+ }
+ }
+
+ for (int i = 0; i < 5; i++) {
+ if ((rv = cmx_wait_BSR(sc, BSR_BULK_IN_FULL, 1)) != 0) {
+ return rv;
+ }
+ sc->buf[i] = cmx_read_DTR(sc);
+ DEBUG_printf(sc->dev, "buf[%02x]=%02x\n", i, sc->buf[i]);
+ }
+
+ bytes_left = CMX_MIN_RDLEN +
+ (0x000000FF&((char)sc->buf[1])) +
+ (0x0000FF00&((char)sc->buf[2] << 8)) +
+ (0x00FF0000&((char)sc->buf[3] << 16)) +
+ (0xFF000000&((char)sc->buf[4] << 24));
+ DEBUG_printf(sc->dev, "msgsz=%lu\n", bytes_left);
+
+ if (uio->uio_resid < bytes_left) {
+ return EINVAL;
+ }
+
+ offset = 5; /* prefetched header */
+ while (bytes_left > 0) {
+ amnt = MIN(bytes_left, sizeof(sc->buf));
+
+ for (int i = offset; i < amnt; i++) {
+ if ((rv = cmx_wait_BSR(sc, BSR_BULK_IN_FULL, 1))!=0) {
+ return rv;
+ }
+ sc->buf[i] = cmx_read_DTR(sc);
+ DEBUG_printf(sc->dev, "buf[%02x]=%02x\n",
+ i, sc->buf[i]);
+ }
+
+ if ((rv = uiomove(sc->buf, amnt, uio)) != 0) {
+ DEBUG_printf(sc->dev, "uiomove failed (%d)\n", rv);
+ return rv;
+ }
+
+ if (offset)
+ offset = 0;
+ bytes_left -= amnt;
+ }
+
+ if ((rv = cmx_wait_BSR(sc, BSR_BULK_IN_FULL, 1)) != 0) {
+ return rv;
+ }
+
+ if ((rv = cmx_sync_write_SCR(sc, SCR_READER_TO_HOST_DONE)) != 0) {
+ return rv;
+ }
+
+ uc = cmx_read_DTR(sc);
+ DEBUG_printf(sc->dev, "success (DTR=%02x)\n", uc);
+ return 0;
+}
+
+/*
+ * Write to the character device.
+ * Returns zero if successful, NXIO if dying, EINVAL if less data
+ * written than CMX_MIN_WRLEN, or any of the errors that cmx_sync_SCR
+ * can return.
+ */
+static int
+cmx_write(struct cdev *cdev, struct uio *uio, int flag)
+{
+ struct cmx_softc *sc = cdev->si_drv1;
+ int rv, amnt;
+
+ if (sc == NULL || sc->dying)
+ return ENXIO;
+
+ DEBUG_printf(sc->dev, "called (len=%d flag=%b)\n",
+ uio->uio_resid, flag, MODEBITS);
+
+ if (uio->uio_resid == 0) {
+ return 0;
+ }
+
+ if (uio->uio_resid < CMX_MIN_WRLEN) {
+ return EINVAL;
+ }
+
+ if ((rv = cmx_sync_write_SCR(sc, SCR_HOST_TO_READER_START)) != 0) {
+ return rv;
+ }
+
+ sc->timeout = 0;
+ while (uio->uio_resid > 0) {
+ amnt = MIN(uio->uio_resid, sizeof(sc->buf));
+
+ if ((rv = uiomove(sc->buf, amnt, uio)) != 0) {
+ DEBUG_printf(sc->dev, "uiomove failed (%d)\n", rv);
+ /* wildly guessed attempt to notify device */
+ sc->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
+ cmx_sync_write_SCR(sc, SCR_HOST_TO_READER_DONE);
+ return rv;
+ }
+
+ if (sc->timeout == 0) {
+ sc->timeout = cmx_timeout_by_cmd(sc->buf[0]);
+ DEBUG_printf(sc->dev, "cmd=%02x timeout=%lu\n",
+ sc->buf[0], sc->timeout);
+ }
+
+ for (int i = 0; i < amnt; i++) {
+ if ((rv = cmx_wait_BSR(sc, BSR_BULK_OUT_FULL, 0))!=0) {
+ return rv;
+ }
+ cmx_write_DTR(sc, sc->buf[i]);
+ DEBUG_printf(sc->dev, "buf[%02x]=%02x\n",
+ i, sc->buf[i]);
+ }
+ }
+
+ if ((rv = cmx_sync_write_SCR(sc, SCR_HOST_TO_READER_DONE)) != 0) {
+ return rv;
+ }
+
+ DEBUG_printf(sc->dev, "success\n");
+ return 0;
+}
+
+/*
+ * Poll handler. Writing is always possible, reading is only possible
+ * if BSR_BULK_IN_FULL is set. Will start the cmx_tick callout and
+ * set sc->polling.
+ */
+static int
+cmx_poll(struct cdev *cdev, int events, struct thread *td)
+{
+ struct cmx_softc *sc = cdev->si_drv1;
+ int revents = 0;
+ uint8_t bsr = 0;
+
+ if (sc == NULL || sc->dying)
+ return ENXIO;
+
+ bsr = cmx_read_BSR(sc);
+ DEBUG_printf(sc->dev, "called (events=%b BSR=%b)\n",
+ events, POLLBITS, bsr, BSRBITS);
+
+ revents = events & (POLLOUT | POLLWRNORM);
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (cmx_test(bsr, BSR_BULK_IN_FULL, 1)) {
+ revents |= events & (POLLIN | POLLRDNORM);
+ } else {
+ selrecord(td, &sc->sel);
+ CMX_LOCK(sc);
+ if (!sc->polling) {
+ DEBUG_printf(sc->dev, "enabling polling\n");
+ sc->polling = 1;
+ callout_reset(&sc->ch, POLL_TICKS,
+ cmx_tick, sc);
+ } else {
+ DEBUG_printf(sc->dev, "already polling\n");
+ }
+ CMX_UNLOCK(sc);
+ }
+ }
+
+ DEBUG_printf(sc->dev, "success (revents=%b)\n", revents, POLLBITS);
+
+ return revents;
+}
+
+#ifdef CMX_INTR
+/*
+ * Interrupt handler. Currently has no function except to
+ * print register status (if debugging is also enabled).
+ */
+static void
+cmx_intr(void *arg)
+{
+ struct cmx_softc *sc = (struct cmx_softc *)arg;
+
+ if (sc == NULL || sc->dying)
+ return;
+
+ DEBUG_printf(sc->dev, "received interrupt (SCR=%b BSR=%b)\n",
+ cmx_read_SCR(sc), SCRBITS,
+ cmx_read_BSR(sc), BSRBITS);
+
+ return;
+}
+#endif
+
diff --git a/sys/dev/cmx/cmx_pccard.c b/sys/dev/cmx/cmx_pccard.c
new file mode 100644
index 0000000..32c4872
--- /dev/null
+++ b/sys/dev/cmx/cmx_pccard.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 2006-2007 Daniel Roethlisberger <daniel@roe.ch>
+ * 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 unmodified, 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/selinfo.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/cmx/cmxvar.h>
+
+#include <dev/pccard/pccardvar.h>
+#include <dev/pccard/pccard_cis.h>
+
+#include "pccarddevs.h"
+
+static const struct pccard_product cmx_pccard_products[] = {
+ PCMCIA_CARD(OMNIKEY, CM4040),
+ { NULL }
+};
+
+/*
+ * Probe for the card.
+ */
+static int
+cmx_pccard_probe(device_t dev)
+{
+ const struct pccard_product *pp;
+ if ((pp = pccard_product_lookup(dev, cmx_pccard_products,
+ sizeof(cmx_pccard_products[0]), NULL)) != NULL) {
+ if (pp->pp_name != NULL)
+ device_set_desc(dev, pp->pp_name);
+ return 0;
+ }
+ return EIO;
+}
+
+/*
+ * Attach to the pccard, and call bus independant attach and
+ * resource allocation routines.
+ */
+static int
+cmx_pccard_attach(device_t dev)
+{
+ int rv = 0;
+ cmx_init_softc(dev);
+
+ if ((rv = cmx_alloc_resources(dev)) != 0) {
+ device_printf(dev, "cmx_alloc_resources() failed!\n");
+ cmx_release_resources(dev);
+ return rv;
+ }
+
+ if ((rv = cmx_attach(dev)) != 0) {
+ device_printf(dev, "cmx_attach() failed!\n");
+ cmx_release_resources(dev);
+ return rv;
+ }
+
+ device_printf(dev, "attached\n");
+ return 0;
+}
+
+static device_method_t cmx_pccard_methods[] = {
+ DEVMETHOD(device_probe, cmx_pccard_probe),
+ DEVMETHOD(device_attach, cmx_pccard_attach),
+ DEVMETHOD(device_detach, cmx_detach),
+
+ { 0, 0 }
+};
+
+static driver_t cmx_pccard_driver = {
+ "cmx",
+ cmx_pccard_methods,
+ sizeof(struct cmx_softc),
+};
+
+DRIVER_MODULE(cmx, pccard, cmx_pccard_driver, cmx_devclass, 0, 0);
+
diff --git a/sys/dev/cmx/cmxreg.h b/sys/dev/cmx/cmxreg.h
new file mode 100644
index 0000000..a96a662
--- /dev/null
+++ b/sys/dev/cmx/cmxreg.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2006-2007 Daniel Roethlisberger <daniel@roe.ch>
+ * Copyright (c) 2000-2004 OMNIKEY GmbH (www.omnikey.com)
+ * 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+/* I/O port registers */
+#define REG_OFFSET_DTR 0 /* data transfer register */
+#define REG_OFFSET_BSR 1 /* buffer status register */
+#define REG_OFFSET_SCR 2 /* sync control register */
+
+/* buffer status register flags */
+#define BSR_BULK_OUT_FULL 0x01
+#define BSR_BULK_IN_FULL 0x02
+
+/* sync control register flags */
+#define SCR_POWER_DOWN 0x01
+#define SCR_PULSE_INTERRUPT 0x02
+#define SCR_HOST_TO_READER_DONE 0x04
+#define SCR_READER_TO_HOST_DONE 0x08
+#define SCR_ACK_NOTIFY 0x10
+#define SCR_EN_NOTIFY 0x20
+#define SCR_ABORT 0x40
+#define SCR_HOST_TO_READER_START 0x80
+
+/* CCID commands */
+#define CMD_PC_TO_RDR_SETPARAMETERS 0x61
+#define CMD_PC_TO_RDR_ICCPOWERON 0x62
+#define CMD_PC_TO_RDR_ICCPOWEROFF 0x63
+#define CMD_PC_TO_RDR_GETSLOTSTATUS 0x65
+#define CMD_PC_TO_RDR_SECURE 0x69
+#define CMD_PC_TO_RDR_ESCAPE 0x6B
+#define CMD_PC_TO_RDR_GETPARAMETERS 0x6C
+#define CMD_PC_TO_RDR_RESETPARAMETERS 0x6D
+#define CMD_PC_TO_RDR_ICCCLOCK 0x6E
+#define CMD_PC_TO_RDR_XFRBLOCK 0x6F
+#define CMD_PC_TO_RDR_TEST_SECURE 0x74
+#define CMD_PC_TO_RDR_OK_SECURE 0x89
+#define CMD_RDR_TO_PC_DATABLOCK 0x80
+#define CMD_RDR_TO_PC_SLOTSTATUS 0x81
+#define CMD_RDR_TO_PC_PARAMETERS 0x82
+#define CMD_RDR_TO_PC_ESCAPE 0x83
+#define CMD_RDR_TO_PC_OK_SECURE 0x89
diff --git a/sys/dev/cmx/cmxvar.h b/sys/dev/cmx/cmxvar.h
new file mode 100644
index 0000000..1567c5e
--- /dev/null
+++ b/sys/dev/cmx/cmxvar.h
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 2006-2007 Daniel Roethlisberger <daniel@roe.ch>
+ * 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+/*#define CMX_DEBUG*/
+/*#define CMX_INTR*/
+
+#define CMX_MIN_RDLEN 10 /* min read length */
+#define CMX_MIN_WRLEN 5 /* min write length */
+#define CMX_BUFSZ 512 /* I/O block size */
+
+struct cmx_softc {
+ device_t dev; /* pccard device */
+ struct cdev *cdev; /* character device */
+
+ struct resource *ioport; /* io port resource descriptor */
+ int ioport_rid; /* io port resource identification */
+
+ bus_space_tag_t bst; /* bus space tag */
+ bus_space_handle_t bsh; /* bus space handle */
+
+#ifdef CMX_INTR
+ struct resource* irq; /* irq resource descriptor */
+ int irq_rid; /* irq resource identification */
+ void *ih; /* intr handle */
+#endif
+
+ struct mtx mtx; /* per-unit lock */
+ struct callout ch; /* callout handle */
+ struct selinfo sel; /* select/poll queue handle */
+
+ int open; /* is chardev open? */
+ int polling; /* are we polling? */
+ int dying; /* are we detaching? */
+
+ unsigned long timeout; /* response timeout */
+
+ uint8_t buf[CMX_BUFSZ]; /* read/write buffer */
+};
+
+extern devclass_t cmx_devclass;
+
+void cmx_init_softc(device_t);
+int cmx_alloc_resources(device_t);
+void cmx_release_resources(device_t);
+int cmx_attach(device_t);
+int cmx_detach(device_t);
+
+#define CMX_READ_1(sc, off) \
+ (bus_space_read_1((sc)->bst, (sc)->bsh, off))
+#define CMX_WRITE_1(sc, off, val) \
+ (bus_space_write_1((sc)->bst, (sc)->bsh, off, val))
+
+#define cmx_read_BSR(sc) \
+ CMX_READ_1(sc, REG_OFFSET_BSR)
+#define cmx_write_BSR(sc, val) \
+ CMX_WRITE_1(sc, REG_OFFSET_BSR, val)
+#define cmx_read_SCR(sc) \
+ CMX_READ_1(sc, REG_OFFSET_SCR)
+#define cmx_write_SCR(sc, val) \
+ CMX_WRITE_1(sc, REG_OFFSET_SCR, val)
+#define cmx_read_DTR(sc) \
+ CMX_READ_1(sc, REG_OFFSET_DTR)
+#define cmx_write_DTR(sc, val) \
+ CMX_WRITE_1(sc, REG_OFFSET_DTR, val)
+
+#define cmx_test(byte, flags, test) \
+ (((byte) & (flags)) == ((test) ? (flags) : 0))
+
+#define cmx_test_BSR(sc, flags, test) \
+ cmx_test(cmx_read_BSR(sc), flags, test)
+
+#define CMX_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define CMX_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+#define CMX_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, (what))
diff --git a/sys/dev/pccard/pccarddevs b/sys/dev/pccard/pccarddevs
index a6359d5..8c64a5a 100644
--- a/sys/dev/pccard/pccarddevs
+++ b/sys/dev/pccard/pccarddevs
@@ -143,6 +143,7 @@ vendor BAY 0x01eb Bay Networks
vendor FARALLON 0x0200 Farallon Communications
vendor RELIA 0x0215 RELIA Technologies Corporation
vendor TELECOMDEVICE 0x021b Telecom Device
+vendor OMNIKEY 0x0223 OMNIKEY GmbH
vendor NOKIA 0x023d Nokia Communications
vendor SAMSUNG 0x0250 Samsung
vendor HWN 0x0261 Home Wireless Networks
@@ -471,6 +472,9 @@ product OLICOM OC3231 0x3132 GoCard Token Ring 16/4/Modem 288
product OLICOM OC2232 0x3222 GoCard Combo Eth/Modem 336
product OLICOM OC3232 0x3232 GoCard Token Ring 16/4/Modem 336
+/* OMNIKEY Products */
+product OMNIKEY CM4040 0x0200 OMNIKEY CardMan 4040
+
/* Ositech Products */
product OSITECH JACK_144 0x0001 Jack of Diamonds Ethernet/Modem 14.4
product OSITECH JACK_288 0x0002 Jack of Diamonds Ethernet/Modem 28.8
OpenPOWER on IntegriCloud