diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index c0077a193b66..aa4ef729c9df 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -49,6 +49,7 @@ MAN= aac.4 \ ch.4 \ ciss.4 \ cm.4 \ + cmx.4 \ cnw.4 \ coda.4 \ ${_coretemp.4} \ diff --git a/share/man/man4/cmx.4 b/share/man/man4/cmx.4 new file mode 100644 index 000000000000..6db6a1d2d18b --- /dev/null +++ b/share/man/man4/cmx.4 @@ -0,0 +1,95 @@ +.\" +.\" $FreeBSD$ +.\" +.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 /usr/ports/security/openct -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 . +.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/NOTES b/sys/conf/NOTES index 815349b17843..dfa3d8d8a065 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2115,6 +2115,7 @@ device tnt4882 # rc: RISCom/8 multiport card # rp: Comtrol Rocketport(ISA/PCI) - single card # si: Specialix SI/XIO 4-32 port terminal multiplexor +# cmx: OmniKey CardMan 4040 pccard smartcard reader # Notes on the Comtrol Rocketport driver: # @@ -2170,6 +2171,7 @@ options SI_DEBUG hint.si.0.at="isa" hint.si.0.maddr="0xd0000" hint.si.0.irq="12" +device cmx # # The 'bktr' device is a PCI video capture device using the Brooktree diff --git a/sys/conf/files b/sys/conf/files index aa79fd807cd8..f203ed0c7513 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -508,6 +508,8 @@ dev/cardbus/cardbus_cis.c optional cardbus dev/cardbus/cardbus_device.c optional cardbus dev/ciss/ciss.c optional ciss dev/cm/smc90cx6.c optional cm +dev/cmx/cmx.c optional cmx +dev/cmx/cmx_pccard.c optional cmx pccard dev/cnw/if_cnw.c optional cnw pccard dev/cpufreq/ichss.c optional cpufreq dev/cs/if_cs.c optional cs diff --git a/sys/dev/cmx/cmx.c b/sys/dev/cmx/cmx.c new file mode 100644 index 000000000000..69cf8921690a --- /dev/null +++ b/sys/dev/cmx/cmx.c @@ -0,0 +1,701 @@ +/*- + * 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. + */ + +#include +__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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#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 000000000000..32c48727abee --- /dev/null +++ b/sys/dev/cmx/cmx_pccard.c @@ -0,0 +1,115 @@ +/*- + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#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 000000000000..a96a662d9709 --- /dev/null +++ b/sys/dev/cmx/cmxreg.h @@ -0,0 +1,66 @@ +/*- + * 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$ + */ + +/* 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 000000000000..1567c5eb00a7 --- /dev/null +++ b/sys/dev/cmx/cmxvar.h @@ -0,0 +1,99 @@ +/*- + * 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$ + */ + +/*#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 a6359d5da01c..8c64a5ad42c2 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 diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 4634ff11185e..d350c33ff56c 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -52,6 +52,7 @@ SUBDIR= ${_3dfx} \ ${_ce} \ ${_ciss} \ ${_cm} \ + ${_cmx} \ coda \ coda5 \ ${_coff} \ @@ -446,6 +447,7 @@ _asr= asr _bios= bios _ciss= ciss _cm= cm +_cmx= cmx _coretemp= coretemp _ctau= ctau _cx= cx @@ -503,6 +505,7 @@ _ath_rate_onoe= ath_rate_onoe _ath_rate_sample=ath_rate_sample _cardbus= cardbus _cbb= cbb +_cmx= cmx _ciss= ciss _coretemp= coretemp _cpufreq= cpufreq @@ -568,6 +571,7 @@ _cardbus= cardbus _cbb= cbb _ciss= ciss _cm= cm +_cmx= cmx _coff= coff _cpufreq= cpufreq _em= em diff --git a/sys/modules/cmx/Makefile b/sys/modules/cmx/Makefile new file mode 100644 index 000000000000..9da17b20bfa9 --- /dev/null +++ b/sys/modules/cmx/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ +# +MAINTAINER= daniel@roe.ch + +.PATH: ${.CURDIR}/../../dev/cmx + +KMOD= cmx +SRCS= cmx.c cmxvar.h cmxreg.h \ + cmx_pccard.c \ + pccarddevs.h device_if.h bus_if.h card_if.h + +.include