From c4bf625e67439f34b29bfd33c4e2555ffea63ce9 Mon Sep 17 00:00:00 2001 From: Hasso Tepper Date: Wed, 23 Apr 2008 08:57:10 +0000 Subject: [PATCH] Add a driver for Omnikey CardMan 4040 smartcard reader - cmx(4). Obtained-from: FreeBSD --- share/man/man4/Makefile | 3 +- share/man/man4/cmx.4 | 123 ++++++ sys/conf/files | 4 +- sys/config/LINT | 7 +- sys/dev/misc/Makefile | 4 +- sys/dev/misc/cmx/Makefile | 9 + sys/dev/misc/cmx/cmx.c | 711 ++++++++++++++++++++++++++++++++++ sys/dev/misc/cmx/cmx_pccard.c | 111 ++++++ sys/dev/misc/cmx/cmxreg.h | 67 ++++ sys/dev/misc/cmx/cmxvar.h | 100 +++++ 10 files changed, 1134 insertions(+), 5 deletions(-) create mode 100644 share/man/man4/cmx.4 create mode 100644 sys/dev/misc/cmx/Makefile create mode 100644 sys/dev/misc/cmx/cmx.c create mode 100644 sys/dev/misc/cmx/cmx_pccard.c create mode 100644 sys/dev/misc/cmx/cmxreg.h create mode 100644 sys/dev/misc/cmx/cmxvar.h diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 8c0ac3c559..08ce69fc58 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 8.1 (Berkeley) 6/18/93 # $FreeBSD: src/share/man/man4/Makefile,v 1.83.2.66 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/share/man/man4/Makefile,v 1.80 2008/03/22 09:51:46 sephe Exp $ +# $DragonFly: src/share/man/man4/Makefile,v 1.81 2008/04/23 08:57:10 hasso Exp $ MAN= aac.4 \ acpi.4 \ @@ -41,6 +41,7 @@ MAN= aac.4 \ cd.4 \ ch.4 \ ciss.4 \ + cmx.4 \ coretemp.4 \ crypto.4 \ csa.4 \ diff --git a/share/man/man4/cmx.4 b/share/man/man4/cmx.4 new file mode 100644 index 0000000000..017a66757d --- /dev/null +++ b/share/man/man4/cmx.4 @@ -0,0 +1,123 @@ +.\" +.\" Copyright (c) 2006-2007 Daniel Roethlisberger +.\" 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. +.\" +.\" $FreeBSD: src/share/man/man4/cmx.4,v 1.2 2008/03/06 08:47:16 rink Exp $ +.\" $DragonFly: src/share/man/man4/cmx.4,v 1.1 2008/04/23 08:57:10 hasso Exp $ +.\" +.Dd July 7, 2007 +.Dt CMX 4 +.Os +.Sh NAME +.Nm cmx +.Nd Omnikey CardMan 4040 smartcard reader device driver +.Sh SYNOPSIS +.Cd device cmx +.Sh DESCRIPTION +The +.Nm +driver provides support for the PCCARD based +.Em Omnikey CardMan 4040 +smartcard reader. +The driver provides a character device special file based +.Em Chip/Smart Card Interface Devices (CCID) +interface. The driver implements what the vendor calls the +.Em Synchronious API +onto the smartcard reader device. +.Pp +Reading and writing is synchronious, meaning that a call to +.Xr write 2 +directly corresponds to a complete CCID command sent to the +device, while the following +.Xr read 2 +will return the complete answer from the reader. There is no +support for partial reads or writes. There is no upper limit on +CCID request or response sizes, but the complete CCID request +must be sent to the driver in +.Xr write 2 +and the complete CCID response must fit into the buffer +supplied to +.Xr read 2 . +.Pp +Non-blocking I/O, +.Xr select 2 +and +.Xr poll 2 +are supported and work as expected. An open file descriptor +will always be ready for writing, but only ready for reading +if the device indicates that it has data available. +.Sh COMPATIBILITY +Userland smartcard code written for the vendor's Linux drivers +should work with the +.Nm +driver without modification. +.Sh FILES +.Bl -tag -width /dev/cmxn -compact +.It Pa /dev/cmx\fBn\fP +Character device special file. +.\".It Pa /usr/ports/security/openct +.\"OpenCT, a userspace smartcard daemon containing a +.\".Em CCID +.\"driver which directly supports +.\".Nm +.\"devices. +.\".It Pa /usr/ports/devel/pcsc-lite +.\"PC/SC-Lite, a userspace smartcard daemon. +.\".It Pa /usr/ports/devel/libccid +.\"libccid, a generic +.\".Em CCID +.\"driver for use by PC/SC-Lite to interface to +.\".Nm +.\"devices. +.El +.Sh SEE ALSO +.Xr pccard 4 +.Sh HISTORY +The +.Nm cmx +driver first appeared in +.Fx 7.1 +and was imported into +.Dx 1.13 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Daniel Roethlisberger Aq daniel@roe.ch , +originally based on the Linux driver v1.1.0 by +.An Omnikey GmbH Aq www.omnikey.com . +Early testing and bug fixes by +.An Marcin Cieslak Aq saper@system.pl . +.Sh BUGS +.An -nosplit +The way the +.Nm +driver talks to the CardMan 4040 is a bit rough. Due to the +complete lack of hardware documentation other than vendor drivers +for other operating systems, the gory details of the device's +I/O registers are not understood very well. There may be error +conditions which can only be solved by physically reinserting the +reader. diff --git a/sys/conf/files b/sys/conf/files index c3c7eac59d..fef75fb737 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,5 +1,5 @@ # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/sys/conf/files,v 1.213 2008/04/06 18:58:12 dillon Exp $ +# $DragonFly: src/sys/conf/files,v 1.214 2008/04/23 08:57:10 hasso Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and @@ -328,6 +328,8 @@ dev/netif/plip/if_plip.c optional plip dev/misc/lpbb/lpbb.c optional lpbb dev/misc/lpt/lpt.c optional lpt dev/misc/pcfclock/pcfclock.c optional pcfclock +dev/misc/cmx/cmx.c optional cmx +dev/misc/cmx/cmx_pccard.c optional cmx pccard bus/ppbus/ppb_base.c optional ppbus bus/ppbus/ppb_1284.c optional ppbus bus/ppbus/ppb_msq.c optional ppbus diff --git a/sys/config/LINT b/sys/config/LINT index 4203677194..64b0751761 100644 --- a/sys/config/LINT +++ b/sys/config/LINT @@ -3,7 +3,7 @@ # as much of the source tree as it can. # # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $ -# $DragonFly: src/sys/config/LINT,v 1.155 2008/04/06 18:58:13 dillon Exp $ +# $DragonFly: src/sys/config/LINT,v 1.156 2008/04/23 08:57:10 hasso Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -2669,6 +2669,11 @@ device tdfxdrm options DRM_DEBUG options DRM_LINUX +# +# Misc devices +# +device cmx # Omnikey CardMan 4040 smartcard reader + # # Embedded system options: # diff --git a/sys/dev/misc/Makefile b/sys/dev/misc/Makefile index 503aae0eb1..ab0745ac84 100644 --- a/sys/dev/misc/Makefile +++ b/sys/dev/misc/Makefile @@ -1,6 +1,6 @@ -# $DragonFly: src/sys/dev/misc/Makefile,v 1.4 2006/05/20 18:26:35 dillon Exp $ +# $DragonFly: src/sys/dev/misc/Makefile,v 1.5 2008/04/23 08:57:10 hasso Exp $ # -SUBDIR=dcons joy pcfclock nmdm syscons snp +SUBDIR=cmx dcons joy pcfclock nmdm syscons snp .include diff --git a/sys/dev/misc/cmx/Makefile b/sys/dev/misc/cmx/Makefile new file mode 100644 index 0000000000..2abe256cb1 --- /dev/null +++ b/sys/dev/misc/cmx/Makefile @@ -0,0 +1,9 @@ +# $DragonFly: src/sys/dev/misc/cmx/Makefile,v 1.1 2008/04/23 08:57:10 hasso Exp $ + +KMOD= cmx +SRCS= cmx.c cmx_pccard.c +SRCS+= device_if.h bus_if.h card_if.h +NOMAN= + +.include + diff --git a/sys/dev/misc/cmx/cmx.c b/sys/dev/misc/cmx/cmx.c new file mode 100644 index 0000000000..373870f75f --- /dev/null +++ b/sys/dev/misc/cmx/cmx.c @@ -0,0 +1,711 @@ +/*- + * Copyright (c) 2006-2007 Daniel Roethlisberger + * 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. + * + * $FreeBSD: src/sys/dev/cmx/cmx.c,v 1.1 2008/03/06 08:09:45 rink Exp $ + * $DragonFly: src/sys/dev/misc/cmx/cmx.c,v 1.1 2008/04/23 08:57:10 hasso Exp $ + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cmxvar.h" +#include "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 + +#define CDEV_MAJOR 185 +static struct dev_ops cmx_ops = { + { "cmx", CDEV_MAJOR, 0 }, + .d_open = cmx_open, + .d_close = cmx_close, + .d_read = cmx_read, + .d_write = cmx_write, + .d_poll = cmx_poll, +}; + +/* + * 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, 0, cmx_intr, sc, + &sc->ih, NULL)) != 0) { + device_printf(dev, "failed to set up irq\n"); + return ENOMEM; + } +#endif + + lockinit(&sc->mtx, "cmx softc lock", 0, LK_CANRECURSE); + callout_init(&sc->ch); + + 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); + + lockuninit(&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; + + dev_ops_add(&cmx_ops, -1, device_get_unit(dev)); + sc->cdev = make_dev(&cmx_ops, 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); + selwakeup(&sc->sel); + } 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, 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; + selwakeup(&sc->sel); + } 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 dev_open_args *ap) +{ + cdev_t dev = ap->a_head.a_dev; + struct cmx_softc *sc; + + sc = devclass_get_softc(cmx_devclass, minor(dev)); + 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", + ap->a_oflags, MODEBITS, curthread); + return 0; +} + +/* + * Close the character device. + */ +static int +cmx_close(struct dev_close_args *ap) +{ + cdev_t dev = ap->a_head.a_dev; + struct cmx_softc *sc; + + sc = devclass_get_softc(cmx_devclass, minor(dev)); + 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); + selwakeup(&sc->sel); + CMX_LOCK(sc); + } + sc->open = 0; + CMX_UNLOCK(sc); + + DEBUG_printf(sc->dev, "close (flags=%b thread=%p)\n", + ap->a_fflag, MODEBITS, curthread); + 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 dev_read_args *ap) +{ + cdev_t dev = ap->a_head.a_dev; + struct cmx_softc *sc; + struct uio *uio = ap->a_uio; + unsigned long bytes_left; + uint8_t uc; + int rv, amnt, offset; + + sc = devclass_get_softc(cmx_devclass, minor(dev)); + if (sc == NULL || sc->dying) + return ENXIO; + + DEBUG_printf(sc->dev, "called (len=%d flag=%b)\n", + uio->uio_resid, ap->a_ioflag, MODEBITS); + + CMX_LOCK(sc); + if (sc->polling) { + DEBUG_printf(sc->dev, "disabling polling\n"); + callout_stop(&sc->ch); + sc->polling = 0; + CMX_UNLOCK(sc); + selwakeup(&sc->sel); + } else { + CMX_UNLOCK(sc); + } + + if (uio->uio_resid == 0) { + return 0; + } + + if (uio->uio_resid < CMX_MIN_RDLEN) { + return EINVAL; + } + + if (ap->a_ioflag & 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 dev_write_args *ap) +{ + cdev_t dev = ap->a_head.a_dev; + struct cmx_softc *sc; + struct uio *uio = ap->a_uio; + int rv, amnt; + + sc = devclass_get_softc(cmx_devclass, minor(dev)); + if (sc == NULL || sc->dying) + return ENXIO; + + DEBUG_printf(sc->dev, "called (len=%d flag=%b)\n", + uio->uio_resid, ap->a_ioflag, 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 dev_poll_args *ap) +{ + cdev_t dev = ap->a_head.a_dev; + struct cmx_softc *sc; + int revents = 0; + uint8_t bsr = 0; + + sc = devclass_get_softc(cmx_devclass, minor(dev)); + if (sc == NULL || sc->dying) + return ENXIO; + + bsr = cmx_read_BSR(sc); + DEBUG_printf(sc->dev, "called (events=%b BSR=%b)\n", + ap->a_events, POLLBITS, bsr, BSRBITS); + + revents = ap->a_events & (POLLOUT | POLLWRNORM); + if (ap->a_events & (POLLIN | POLLRDNORM)) { + if (cmx_test(bsr, BSR_BULK_IN_FULL, 1)) { + revents |= ap->a_events & (POLLIN | POLLRDNORM); + } else { + selrecord(curthread, &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/misc/cmx/cmx_pccard.c b/sys/dev/misc/cmx/cmx_pccard.c new file mode 100644 index 0000000000..f2c9dec2b2 --- /dev/null +++ b/sys/dev/misc/cmx/cmx_pccard.c @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 2006-2007 Daniel Roethlisberger + * 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. + * + * $FreeBSD: src/sys/dev/cmx/cmx_pccard.c,v 1.1 2008/03/06 08:09:45 rink Exp $ + * $DragonFly: src/sys/dev/misc/cmx/cmx_pccard.c,v 1.1 2008/04/23 08:57:10 hasso Exp $ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cmxvar.h" + +#include +#include +#include + +static const struct pccard_product cmx_pccard_products[] = { + PCMCIA_CARD(OMNIKEY, CM4040, 0), + { 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/misc/cmx/cmxreg.h b/sys/dev/misc/cmx/cmxreg.h new file mode 100644 index 0000000000..eaf9238065 --- /dev/null +++ b/sys/dev/misc/cmx/cmxreg.h @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2006-2007 Daniel Roethlisberger + * 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: src/sys/dev/cmx/cmxreg.h,v 1.1 2008/03/06 08:09:45 rink Exp $ + * $DragonFly: src/sys/dev/misc/cmx/cmxreg.h,v 1.1 2008/04/23 08:57:10 hasso Exp $ + */ + +/* 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/misc/cmx/cmxvar.h b/sys/dev/misc/cmx/cmxvar.h new file mode 100644 index 0000000000..dc5234eb1f --- /dev/null +++ b/sys/dev/misc/cmx/cmxvar.h @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 2006-2007 Daniel Roethlisberger + * 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: src/sys/dev/cmx/cmxvar.h,v 1.1 2008/03/06 08:09:45 rink Exp $ + * $DragonFly: src/sys/dev/misc/cmx/cmxvar.h,v 1.1 2008/04/23 08:57:10 hasso Exp $ + */ + +/*#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 lock 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) lockmgr(&(sc)->mtx, LK_EXCLUSIVE); +#define CMX_UNLOCK(sc) lockmgr(&(sc)->mtx, LK_RELEASE); +#define CMX_LOCK_ASSERT(sc, what) -- 2.41.0