Annotation of sys/dev/pcmcia/gpr.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: gpr.c,v 1.12 2006/04/21 17:52:54 uwe Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2002, Federico G. Schwindt
! 5: * All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions are
! 9: * met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in
! 14: * the documentation and/or other materials provided with the
! 15: * distribution.
! 16: *
! 17: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
! 18: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
! 19: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
! 20: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
! 21: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
! 22: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
! 23: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 24: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 25: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 26: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
! 27: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 28: */
! 29:
! 30: /*
! 31: * A driver for the Gemplus GPR400 SmartCard reader.
! 32: *
! 33: * The gpr400 driver written by Wolf Geldmacher <wgeldmacher@paus.ch> for
! 34: * Linux was used as documentation.
! 35: */
! 36:
! 37: #include <sys/param.h>
! 38: #include <sys/kernel.h>
! 39: #include <sys/device.h>
! 40: #include <sys/systm.h>
! 41: #include <sys/proc.h>
! 42: #include <sys/ioctl.h>
! 43: #include <sys/conf.h>
! 44:
! 45: #include <dev/pcmcia/pcmciavar.h>
! 46: #include <dev/pcmcia/pcmciareg.h>
! 47: #include <dev/pcmcia/pcmciadevs.h>
! 48:
! 49: #include <dev/pcmcia/gprio.h>
! 50:
! 51: /* Registers in I/O space (32 bytes) */
! 52: #define GPR400_HAP_CTRL 0x00 /* Handshake and PRG Control */
! 53: #define GPR400_RESET 0x01 /* Master reset */
! 54: #define GPR400_IREQ 0x02 /* Interrupt request */
! 55: #define GPR400_INTR 0x04 /* Interrupt */
! 56: /* bits 3..8 PRG control */
! 57: #define GPR400_PD_CTRL 0x01 /* PRG data */
! 58: /* bytes 3..32 used for data exchange */
! 59:
! 60: /* Registers in attribute memory (read only) */
! 61: #define GPR400_SETUP 0x018 /* General Setup */
! 62: #define GPR400_LOCK_MASK 0x08 /* 0: locked, 1: unlocked */
! 63: #define GPR400_REG1 0x01a /* SmartCard Reg. 1 */
! 64: #define GPR400_DET_MASK 0x08 /* 0: in the reader, 1: removed */
! 65: #define GPR400_INS_MASK 0x80 /* 0: not inserted, 1: inserted */
! 66: #define GPR400_REG2 0x01c /* SmartCard Reg. 2 */
! 67: #define GPR400_CAC 0x01e /* Clock and Control */
! 68:
! 69: /* TLV */
! 70: #define GPR400_CLOSE 0x10 /* Close session */
! 71: #define GPR400_OPEN 0x20 /* Open session */
! 72: #define GPR400_APDU 0x30 /* APDU exchange */
! 73: #define GPR400_POWER 0x40 /* Power down/Standby */
! 74: /* 0: Power down, 1: Standby */
! 75: #define GPR400_SELECT 0x50 /* Select card */
! 76: #define GPR400_DRV0 0x00 /* Downloaded driver 0 */
! 77: #define GPR400_ISODRV 0x02 /* ISO7816-3 driver */
! 78: #define GPR400_CLK_MASK 0x08 /* 0: 3.68MHz, 1: 7.36MHz */
! 79: #define GPR400_STATUS 0xA0 /* Reader status */
! 80:
! 81: #define GPR400_CONT 0x04 /* Chain block */
! 82:
! 83: #define GPR400_MEM_LEN 0x1000
! 84:
! 85: #define GPRUNIT(x) (minor(x) & 0x0f)
! 86:
! 87: #ifdef GPRDEBUG
! 88: int gprdebug;
! 89: #define DPRINTF(x) if (gprdebug) printf x
! 90: #else
! 91: #define DPRINTF(x)
! 92: #endif
! 93:
! 94: struct gpr_softc {
! 95: struct device sc_dev;
! 96:
! 97: struct pcmcia_function *sc_pf;
! 98:
! 99: bus_space_handle_t sc_ioh;
! 100: bus_space_tag_t sc_iot;
! 101:
! 102: struct pcmcia_io_handle sc_pioh;
! 103: int sc_iowin;
! 104:
! 105: bus_space_handle_t sc_memh;
! 106: bus_space_tag_t sc_memt;
! 107:
! 108: struct pcmcia_mem_handle sc_pmemh;
! 109: int sc_memwin;
! 110: bus_addr_t sc_offset;
! 111:
! 112: void * sc_ih;
! 113: };
! 114:
! 115: int gpr_match(struct device *, void *, void *);
! 116: void gpr_attach(struct device *, struct device *, void *);
! 117: int gpr_detach(struct device *, int);
! 118: int gpr_activate(struct device *, enum devact);
! 119:
! 120: int gpropen(dev_t, int, int, struct proc *);
! 121: int gprclose(dev_t, int, int, struct proc *);
! 122: int gprioctl(dev_t, u_long, caddr_t, int, struct proc *);
! 123:
! 124: int gpr_intr(void *);
! 125:
! 126: int tlvput(struct gpr_softc *, int, u_int8_t *, int);
! 127:
! 128: struct cfattach gpr_ca = {
! 129: sizeof(struct gpr_softc), gpr_match, gpr_attach, gpr_detach,
! 130: gpr_activate
! 131: };
! 132:
! 133: struct cfdriver gpr_cd = {
! 134: NULL, "gpr", DV_DULL
! 135: };
! 136:
! 137: int
! 138: gpr_match(struct device *parent, void *match, void *aux)
! 139: {
! 140: struct pcmcia_attach_args *pa = aux;
! 141:
! 142: if (pa->manufacturer == PCMCIA_VENDOR_GEMPLUS &&
! 143: pa->product == PCMCIA_PRODUCT_GEMPLUS_GPR400)
! 144: return (1);
! 145:
! 146: return (0);
! 147: }
! 148:
! 149: void
! 150: gpr_attach(struct device *parent, struct device *self, void *aux)
! 151: {
! 152: struct gpr_softc *sc = (void *)self;
! 153: struct pcmcia_attach_args *pa = aux;
! 154: struct pcmcia_config_entry *cfe;
! 155: const char *intrstr;
! 156:
! 157: for (cfe = SIMPLEQ_FIRST(&pa->pf->cfe_head); cfe;
! 158: cfe = SIMPLEQ_NEXT(cfe, cfe_list)) {
! 159:
! 160: if (!pcmcia_io_alloc(pa->pf, cfe->iospace[0].start,
! 161: cfe->iospace[0].length, cfe->iospace[0].length,
! 162: &sc->sc_pioh))
! 163: break;
! 164: }
! 165:
! 166: if (cfe == NULL) {
! 167: printf(": can't alloc i/o space\n");
! 168: goto fail_io_alloc;
! 169: }
! 170:
! 171: pcmcia_function_init(pa->pf, cfe);
! 172: if (pcmcia_function_enable(pa->pf)) {
! 173: printf(": function enable failed\n");
! 174: goto fail_enable;
! 175: }
! 176:
! 177: if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0,
! 178: sc->sc_pioh.size, &sc->sc_pioh, &sc->sc_iowin)) {
! 179: printf(": can't map i/o space\n");
! 180: goto fail_io_map;
! 181: }
! 182:
! 183: /*
! 184: * GPR400 has some registers in attribute memory as well.
! 185: */
! 186: if (pcmcia_mem_alloc(pa->pf, GPR400_MEM_LEN, &sc->sc_pmemh)) {
! 187: printf(": can't map mem space\n");
! 188: goto fail_mem_alloc;
! 189: }
! 190:
! 191: if (pcmcia_mem_map(pa->pf, PCMCIA_MEM_ATTR, pa->pf->ccr_base,
! 192: GPR400_MEM_LEN, &sc->sc_pmemh, &sc->sc_offset, &sc->sc_memwin)) {
! 193: printf(": can't map memory\n");
! 194: goto fail_mem_map;
! 195: }
! 196:
! 197: sc->sc_pf = pa->pf;
! 198: sc->sc_iot = sc->sc_pioh.iot;
! 199: sc->sc_ioh = sc->sc_pioh.ioh;
! 200: sc->sc_memt = sc->sc_pmemh.memt;
! 201: sc->sc_memh = sc->sc_pmemh.memh;
! 202:
! 203: printf(" port 0x%lx/%d", sc->sc_pioh.addr, sc->sc_pioh.size);
! 204:
! 205: sc->sc_ih = pcmcia_intr_establish(pa->pf, IPL_TTY, gpr_intr, sc,
! 206: sc->sc_dev.dv_xname);
! 207: intrstr = pcmcia_intr_string(sc->sc_pf, sc->sc_ih);
! 208: printf("%s%s\n", *intrstr ? ", " : "", intrstr);
! 209: if (sc->sc_ih != NULL)
! 210: return;
! 211:
! 212: pcmcia_mem_unmap(pa->pf, sc->sc_memwin);
! 213: fail_mem_map:
! 214: pcmcia_mem_free(pa->pf, &sc->sc_pmemh);
! 215: fail_mem_alloc:
! 216: pcmcia_io_unmap(pa->pf, sc->sc_iowin);
! 217: fail_io_map:
! 218: pcmcia_function_disable(pa->pf);
! 219: fail_enable:
! 220: pcmcia_io_free(pa->pf, &sc->sc_pioh);
! 221: fail_io_alloc:
! 222: return;
! 223: }
! 224:
! 225: int
! 226: gpr_detach(struct device *dev, int flags)
! 227: {
! 228: struct gpr_softc *sc = (struct gpr_softc *)dev;
! 229:
! 230: pcmcia_io_unmap(sc->sc_pf, sc->sc_iowin);
! 231: pcmcia_io_free(sc->sc_pf, &sc->sc_pioh);
! 232: pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwin);
! 233: pcmcia_mem_free(sc->sc_pf, &sc->sc_pmemh);
! 234:
! 235: return (0);
! 236: }
! 237:
! 238: int
! 239: gpr_activate(struct device *dev, enum devact act)
! 240: {
! 241: struct gpr_softc *sc = (struct gpr_softc *)dev;
! 242:
! 243: switch (act) {
! 244: case DVACT_ACTIVATE:
! 245: pcmcia_function_enable(sc->sc_pf);
! 246: sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_TTY,
! 247: gpr_intr, sc, sc->sc_dev.dv_xname);
! 248: break;
! 249:
! 250: case DVACT_DEACTIVATE:
! 251: pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
! 252: pcmcia_function_disable(sc->sc_pf);
! 253: break;
! 254: }
! 255:
! 256: return (0);
! 257: }
! 258:
! 259: int
! 260: gpropen(dev_t dev, int flags, int mode, struct proc *p)
! 261: {
! 262: int unit = GPRUNIT(dev);
! 263: struct gpr_softc *sc;
! 264:
! 265: DPRINTF(("%s: flags %d, mode %d\n", __func__, flags, mode));
! 266:
! 267: if (unit >= gpr_cd.cd_ndevs ||
! 268: (sc = gpr_cd.cd_devs[unit]) == NULL)
! 269: return (ENXIO);
! 270:
! 271: return (tlvput(sc, GPR400_SELECT, "\x02", 1));
! 272: }
! 273:
! 274: int
! 275: gprclose(dev_t dev, int flags, int mode, struct proc *p)
! 276: {
! 277: int unit = GPRUNIT(dev);
! 278: struct gpr_softc *sc = gpr_cd.cd_devs[unit];
! 279:
! 280: DPRINTF(("%s: flags %d, mode %d\n", __func__, flags, mode));
! 281:
! 282: (void)tlvput(sc, GPR400_CLOSE, (u_int8_t *)0, 0);
! 283:
! 284: return (0);
! 285: }
! 286:
! 287: int
! 288: gprioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
! 289: {
! 290: int unit = GPRUNIT(dev);
! 291: struct gpr_softc *sc = gpr_cd.cd_devs[unit];
! 292: int error;
! 293:
! 294: DPRINTF(("%s: cmd %d, flags 0x%x\n", __func__, cmd, flags));
! 295:
! 296: switch (cmd) {
! 297: case GPR_RESET:
! 298: /*
! 299: * To reset and power up the reader, set bit 0 in the
! 300: * HAP register for at least 5us and wait for 20ms.
! 301: */
! 302: bus_space_write_1(sc->sc_iot, sc->sc_ioh, GPR400_HAP_CTRL,
! 303: GPR400_RESET);
! 304: delay(10);
! 305: bus_space_write_1(sc->sc_iot, sc->sc_ioh, GPR400_HAP_CTRL, 0);
! 306: tsleep(sc, PWAIT, "gpreset", hz / 40);
! 307: /* FALLTHROUGH */
! 308:
! 309: case GPR_SELECT:
! 310: error = tlvput(sc, GPR400_SELECT, "\x02", 1);
! 311: break;
! 312:
! 313: case GPR_POWER:
! 314: {
! 315: u_int8_t *mode;
! 316:
! 317: if (*(int *)addr)
! 318: mode = "\x01"; /* Standby */
! 319: else
! 320: mode = "\x00"; /* Power down */
! 321:
! 322: error = tlvput(sc, GPR400_POWER, mode, 1);
! 323: }
! 324: break;
! 325:
! 326: case GPR_CLOSE:
! 327: error = tlvput(sc, GPR400_CLOSE, (u_int8_t *)0, 0);
! 328: break;
! 329:
! 330: case GPR_RAM:
! 331: {
! 332: struct gpr400_ram r;
! 333:
! 334: bus_space_read_region_1(sc->sc_memt, sc->sc_memh,
! 335: sc->sc_offset, &r, sizeof(struct gpr400_ram));
! 336: error = copyout(&r, addr, sizeof(struct gpr400_ram));
! 337: }
! 338: break;
! 339:
! 340: case GPR_CMD:
! 341: case GPR_OPEN:
! 342: case GPR_STATUS:
! 343: case GPR_TLV:
! 344: default:
! 345: error = EINVAL;
! 346: break;
! 347: };
! 348:
! 349: return (error);
! 350: }
! 351:
! 352: int
! 353: gpr_intr(void *arg)
! 354: {
! 355: struct gpr_softc *sc = arg;
! 356: u_int8_t val;
! 357:
! 358: DPRINTF(("%s: got interrupt\n", __func__));
! 359:
! 360: /* Ack interrupt */
! 361: val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, GPR400_HAP_CTRL);
! 362: bus_space_write_1(sc->sc_iot, sc->sc_ioh, GPR400_HAP_CTRL,
! 363: val & ~GPR400_INTR);
! 364:
! 365: wakeup(sc);
! 366:
! 367: return (1);
! 368: }
! 369:
! 370: int
! 371: tlvput(struct gpr_softc *sc, int cmd, u_int8_t *data, int len)
! 372: {
! 373: int resid, ret;
! 374:
! 375: DPRINTF(("%s: cmd 0x%x, data %p, len %d\n", __func__,
! 376: cmd, data, len));
! 377:
! 378: resid = len;
! 379: do {
! 380: int n, s;
! 381:
! 382: n = min(resid, 28);
! 383: resid -= n;
! 384:
! 385: if (resid)
! 386: cmd |= GPR400_CONT;
! 387: else
! 388: cmd &= ~GPR400_CONT;
! 389:
! 390: DPRINTF(("%s: sending cmd 0x%x, len %d, left %d\n",
! 391: __func__, cmd, n, resid));
! 392:
! 393: bus_space_write_1(sc->sc_iot, sc->sc_ioh, 0x02, cmd);
! 394: bus_space_write_1(sc->sc_iot, sc->sc_ioh, 0x03, n);
! 395:
! 396: if (n) {
! 397: bus_space_write_region_1(sc->sc_iot, sc->sc_ioh,
! 398: 0x04, data, n);
! 399: data += n;
! 400: }
! 401:
! 402: s = spltty();
! 403:
! 404: /* Tell the reader to process this command. */
! 405: bus_space_write_1(sc->sc_iot, sc->sc_ioh, GPR400_HAP_CTRL,
! 406: GPR400_IREQ);
! 407:
! 408: tsleep(sc, PCATCH, "tlvput", 0);
! 409:
! 410: splx(s);
! 411:
! 412: /* Read the status. */
! 413: ret = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 0x04);
! 414:
! 415: DPRINTF(("%s: ret %d\n", __func__, ret));
! 416:
! 417: if (ret != 0x00 || (!resid && ret != 0xe7))
! 418: return (EIO);
! 419:
! 420: } while (resid > 0);
! 421:
! 422: return (0);
! 423: }
CVSweb