123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- /* $OpenBSD: gpr.c,v 1.16 2011/07/03 15:47:17 matthew Exp $ */
- /*
- * Copyright (c) 2002, Federico G. Schwindt
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER 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.
- */
- /*
- * A driver for the Gemplus GPR400 SmartCard reader.
- *
- * The gpr400 driver written by Wolf Geldmacher <wgeldmacher@paus.ch> for
- * Linux was used as documentation.
- */
- #include <sys/param.h>
- #include <sys/kernel.h>
- #include <sys/device.h>
- #include <sys/systm.h>
- #include <sys/conf.h>
- #include <sys/file.h>
- #include <sys/ioctl.h>
- #include <sys/proc.h>
- #include <dev/pcmcia/pcmciavar.h>
- #include <dev/pcmcia/pcmciareg.h>
- #include <dev/pcmcia/pcmciadevs.h>
- #include <dev/pcmcia/gprio.h>
- /* Registers in I/O space (32 bytes) */
- #define GPR400_HAP_CTRL 0x00 /* Handshake and PRG Control */
- #define GPR400_RESET 0x01 /* Master reset */
- #define GPR400_IREQ 0x02 /* Interrupt request */
- #define GPR400_INTR 0x04 /* Interrupt */
- /* bits 3..8 PRG control */
- #define GPR400_PD_CTRL 0x01 /* PRG data */
- /* bytes 3..32 used for data exchange */
- /* Registers in attribute memory (read only) */
- #define GPR400_SETUP 0x018 /* General Setup */
- #define GPR400_LOCK_MASK 0x08 /* 0: locked, 1: unlocked */
- #define GPR400_REG1 0x01a /* SmartCard Reg. 1 */
- #define GPR400_DET_MASK 0x08 /* 0: in the reader, 1: removed */
- #define GPR400_INS_MASK 0x80 /* 0: not inserted, 1: inserted */
- #define GPR400_REG2 0x01c /* SmartCard Reg. 2 */
- #define GPR400_CAC 0x01e /* Clock and Control */
- /* TLV */
- #define GPR400_CLOSE 0x10 /* Close session */
- #define GPR400_OPEN 0x20 /* Open session */
- #define GPR400_APDU 0x30 /* APDU exchange */
- #define GPR400_POWER 0x40 /* Power down/Standby */
- /* 0: Power down, 1: Standby */
- #define GPR400_SELECT 0x50 /* Select card */
- #define GPR400_DRV0 0x00 /* Downloaded driver 0 */
- #define GPR400_ISODRV 0x02 /* ISO7816-3 driver */
- #define GPR400_CLK_MASK 0x08 /* 0: 3.68MHz, 1: 7.36MHz */
- #define GPR400_STATUS 0xA0 /* Reader status */
- #define GPR400_CONT 0x04 /* Chain block */
- #define GPR400_MEM_LEN 0x1000
- #define GPRUNIT(x) (minor(x) & 0x0f)
- #ifdef GPRDEBUG
- int gprdebug;
- #define DPRINTF(x) if (gprdebug) printf x
- #else
- #define DPRINTF(x)
- #endif
- struct gpr_softc {
- struct device sc_dev;
- struct pcmcia_function *sc_pf;
- bus_space_handle_t sc_ioh;
- bus_space_tag_t sc_iot;
- struct pcmcia_io_handle sc_pioh;
- int sc_iowin;
- bus_space_handle_t sc_memh;
- bus_space_tag_t sc_memt;
- struct pcmcia_mem_handle sc_pmemh;
- int sc_memwin;
- bus_addr_t sc_offset;
- void * sc_ih;
- };
- int gpr_match(struct device *, void *, void *);
- void gpr_attach(struct device *, struct device *, void *);
- int gpr_detach(struct device *, int);
- int gpr_activate(struct device *, int);
- int gpropen(dev_t, int, int, struct proc *);
- int gprclose(dev_t, int, int, struct proc *);
- int gprioctl(dev_t, u_long, caddr_t, int, struct proc *);
- int gpr_intr(void *);
- int tlvput(struct gpr_softc *, int, u_int8_t *, int);
- struct cfattach gpr_ca = {
- sizeof(struct gpr_softc), gpr_match, gpr_attach, gpr_detach,
- gpr_activate
- };
- struct cfdriver gpr_cd = {
- NULL, "gpr", DV_DULL
- };
- int
- gpr_match(struct device *parent, void *match, void *aux)
- {
- struct pcmcia_attach_args *pa = aux;
- if (pa->manufacturer == PCMCIA_VENDOR_GEMPLUS &&
- pa->product == PCMCIA_PRODUCT_GEMPLUS_GPR400)
- return (1);
- return (0);
- }
- void
- gpr_attach(struct device *parent, struct device *self, void *aux)
- {
- struct gpr_softc *sc = (void *)self;
- struct pcmcia_attach_args *pa = aux;
- struct pcmcia_config_entry *cfe;
- const char *intrstr;
- for (cfe = SIMPLEQ_FIRST(&pa->pf->cfe_head); cfe;
- cfe = SIMPLEQ_NEXT(cfe, cfe_list)) {
- if (!pcmcia_io_alloc(pa->pf, cfe->iospace[0].start,
- cfe->iospace[0].length, cfe->iospace[0].length,
- &sc->sc_pioh))
- break;
- }
- if (cfe == NULL) {
- printf(": can't alloc i/o space\n");
- goto fail_io_alloc;
- }
- pcmcia_function_init(pa->pf, cfe);
- if (pcmcia_function_enable(pa->pf)) {
- printf(": function enable failed\n");
- goto fail_enable;
- }
- if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0,
- sc->sc_pioh.size, &sc->sc_pioh, &sc->sc_iowin)) {
- printf(": can't map i/o space\n");
- goto fail_io_map;
- }
- /*
- * GPR400 has some registers in attribute memory as well.
- */
- if (pcmcia_mem_alloc(pa->pf, GPR400_MEM_LEN, &sc->sc_pmemh)) {
- printf(": can't map mem space\n");
- goto fail_mem_alloc;
- }
- if (pcmcia_mem_map(pa->pf, PCMCIA_MEM_ATTR, pa->pf->ccr_base,
- GPR400_MEM_LEN, &sc->sc_pmemh, &sc->sc_offset, &sc->sc_memwin)) {
- printf(": can't map memory\n");
- goto fail_mem_map;
- }
- sc->sc_pf = pa->pf;
- sc->sc_iot = sc->sc_pioh.iot;
- sc->sc_ioh = sc->sc_pioh.ioh;
- sc->sc_memt = sc->sc_pmemh.memt;
- sc->sc_memh = sc->sc_pmemh.memh;
- printf(" port 0x%lx/%d", sc->sc_pioh.addr, sc->sc_pioh.size);
- sc->sc_ih = pcmcia_intr_establish(pa->pf, IPL_TTY, gpr_intr, sc,
- sc->sc_dev.dv_xname);
- intrstr = pcmcia_intr_string(sc->sc_pf, sc->sc_ih);
- printf("%s%s\n", *intrstr ? ", " : "", intrstr);
- if (sc->sc_ih != NULL)
- return;
- pcmcia_mem_unmap(pa->pf, sc->sc_memwin);
- fail_mem_map:
- pcmcia_mem_free(pa->pf, &sc->sc_pmemh);
- fail_mem_alloc:
- pcmcia_io_unmap(pa->pf, sc->sc_iowin);
- fail_io_map:
- pcmcia_function_disable(pa->pf);
- fail_enable:
- pcmcia_io_free(pa->pf, &sc->sc_pioh);
- fail_io_alloc:
- return;
- }
- int
- gpr_detach(struct device *dev, int flags)
- {
- struct gpr_softc *sc = (struct gpr_softc *)dev;
- pcmcia_io_unmap(sc->sc_pf, sc->sc_iowin);
- pcmcia_io_free(sc->sc_pf, &sc->sc_pioh);
- pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwin);
- pcmcia_mem_free(sc->sc_pf, &sc->sc_pmemh);
- return (0);
- }
- int
- gpr_activate(struct device *dev, int act)
- {
- struct gpr_softc *sc = (struct gpr_softc *)dev;
- switch (act) {
- case DVACT_DEACTIVATE:
- if (sc->sc_ih)
- pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
- sc->sc_ih = NULL;
- pcmcia_function_disable(sc->sc_pf);
- break;
- }
- return (0);
- }
- int
- gpropen(dev_t dev, int flags, int mode, struct proc *p)
- {
- int unit = GPRUNIT(dev);
- struct gpr_softc *sc;
- DPRINTF(("%s: flags %d, mode %d\n", __func__, flags, mode));
- if (unit >= gpr_cd.cd_ndevs ||
- (sc = gpr_cd.cd_devs[unit]) == NULL)
- return (ENXIO);
- return (tlvput(sc, GPR400_SELECT, "\x02", 1));
- }
- int
- gprclose(dev_t dev, int flags, int mode, struct proc *p)
- {
- int unit = GPRUNIT(dev);
- struct gpr_softc *sc = gpr_cd.cd_devs[unit];
- DPRINTF(("%s: flags %d, mode %d\n", __func__, flags, mode));
- (void)tlvput(sc, GPR400_CLOSE, (u_int8_t *)0, 0);
- return (0);
- }
- int
- gprioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
- {
- int unit = GPRUNIT(dev);
- struct gpr_softc *sc = gpr_cd.cd_devs[unit];
- int error;
- DPRINTF(("%s: cmd %d, flags 0x%x\n", __func__, cmd, flags));
- switch (cmd) {
- case GPR_RESET:
- case GPR_SELECT:
- case GPR_POWER:
- case GPR_CLOSE:
- if ((flags & FWRITE) == 0)
- return (EACCES);
- default:
- break;
- }
- switch (cmd) {
- case GPR_RESET:
- /*
- * To reset and power up the reader, set bit 0 in the
- * HAP register for at least 5us and wait for 20ms.
- */
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, GPR400_HAP_CTRL,
- GPR400_RESET);
- delay(10);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, GPR400_HAP_CTRL, 0);
- tsleep(sc, PWAIT, "gpreset", hz / 40);
- /* FALLTHROUGH */
- case GPR_SELECT:
- error = tlvput(sc, GPR400_SELECT, "\x02", 1);
- break;
- case GPR_POWER:
- {
- u_int8_t *mode;
- if (*(int *)addr)
- mode = "\x01"; /* Standby */
- else
- mode = "\x00"; /* Power down */
- error = tlvput(sc, GPR400_POWER, mode, 1);
- }
- break;
- case GPR_CLOSE:
- error = tlvput(sc, GPR400_CLOSE, (u_int8_t *)0, 0);
- break;
- case GPR_RAM:
- {
- struct gpr400_ram r;
- bus_space_read_region_1(sc->sc_memt, sc->sc_memh,
- sc->sc_offset, (u_int8_t *)&r,
- sizeof(struct gpr400_ram));
- error = copyout(&r, addr, sizeof(struct gpr400_ram));
- }
- break;
- case GPR_CMD:
- case GPR_OPEN:
- case GPR_STATUS:
- case GPR_TLV:
- default:
- error = EINVAL;
- break;
- };
- return (error);
- }
- int
- gpr_intr(void *arg)
- {
- struct gpr_softc *sc = arg;
- u_int8_t val;
- DPRINTF(("%s: got interrupt\n", __func__));
- /* Ack interrupt */
- val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, GPR400_HAP_CTRL);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, GPR400_HAP_CTRL,
- val & ~GPR400_INTR);
- wakeup(sc);
- return (1);
- }
- int
- tlvput(struct gpr_softc *sc, int cmd, u_int8_t *data, int len)
- {
- int resid, ret;
- DPRINTF(("%s: cmd 0x%x, data %p, len %d\n", __func__,
- cmd, data, len));
- resid = len;
- do {
- int n, s;
- n = min(resid, 28);
- resid -= n;
- if (resid)
- cmd |= GPR400_CONT;
- else
- cmd &= ~GPR400_CONT;
- DPRINTF(("%s: sending cmd 0x%x, len %d, left %d\n",
- __func__, cmd, n, resid));
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, 0x02, cmd);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, 0x03, n);
- if (n) {
- bus_space_write_region_1(sc->sc_iot, sc->sc_ioh,
- 0x04, data, n);
- data += n;
- }
- s = spltty();
- /* Tell the reader to process this command. */
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, GPR400_HAP_CTRL,
- GPR400_IREQ);
- tsleep(sc, PCATCH, "tlvput", 0);
- splx(s);
- /* Read the status. */
- ret = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 0x04);
- DPRINTF(("%s: ret %d\n", __func__, ret));
- if (ret != 0x00 || (!resid && ret != 0xe7))
- return (EIO);
- } while (resid > 0);
- return (0);
- }
|