Logo Search packages:      
Sourcecode: aes2501-wy version File versions  Download package

aes2501.c

/*
 * Copyright (C) 2006 Wittawat Yamwong <wittawat@web.de>
 *
 * 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 ``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 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <sys/time.h>
#include <stdint.h>
#include <unistd.h>

#include "usb.h"


/**********************************************************
 ** Utility funtions
 **********************************************************/
static int debug_level = 20;
static time_t tstart_sec = 0;
static uint32_t tstart_usec = 0;


static
void dbg(int level, const char *format, ...)
{
    va_list ap;
    char buf[400];

    if (level > debug_level)
      return;
    snprintf(buf, sizeof(buf), "[aes2501] %s", format);
    va_start(ap, format);
    vfprintf(stderr, buf, ap);
    va_end(ap);
}

static
void u8tohex(uint8_t x, char *str)
{
    static const char hdigit[16] =
    { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
    str[0] = hdigit[(x >> 4) & 0xf];
    str[1] = hdigit[ x       & 0xf];
    str[2] = '\0';
}

static
void u32tohex(uint32_t x, char *str)
{
    u8tohex(x >> 24, str);
    u8tohex(x >> 16, str+2);
    u8tohex(x >> 8,  str+4);
    u8tohex(x,       str+6);
}

void hexdump(int level, const void *d_, unsigned len)
{
    const uint8_t *d = (const uint8_t *)(d_);
    unsigned ofs, c;
    char line[100]; /* actually only 1+8+1+8*3+1+8*3+1 = 61 bytes needed */

    if (level > debug_level)
      return;
    ofs = 0;
    while (ofs < len) {
        char *p;
        line[0] = ' ';
        u32tohex(ofs, line + 1);
        line[9] = ':';
      p = line + 10;
      for (c = 0; c != 16 && (ofs + c) < len; c++) {
            u8tohex(d[ofs + c], p);
            p[2] = ' ';
            p += 3;
          if (c == 7) {
                p[0] = ' ';
                p++;
            }
      }
      p[0] = '\0';
      dbg(level, "%s\n", line);
      ofs += c;
    }
}

static
void get_time(time_t *sec, uint32_t *usec)
{
    struct timeval tv;

    gettimeofday(&tv, NULL);
    if (sec)
      *sec = tv.tv_sec;
    if (usec)
      *usec = tv.tv_usec;
}

static
void time2str(char *buf, unsigned size)
{
    time_t sec;
    uint32_t usec;

    get_time(&sec, &usec);
    sec -= tstart_sec;
    if (usec >= tstart_usec) {
        usec -= tstart_usec;
    } else {
        usec = 1000000 + usec - tstart_usec;
        sec--;
    }
    snprintf(buf, size, "%lu.%03u", (unsigned long) sec,
             (unsigned) (usec/1000));
}

static
void dump(int level, const char *type, const void *data, int len,
        int size, int max)
{
    int actual_len, print_len;
    char buf[20];

    if (level > debug_level)
      return;
    if (debug_level >= 20)
      max = -1; /* dump every bytes */

    time2str(buf, sizeof(buf));
    dbg(level, "%s T=%s len=%d\n", type, buf, len);

    actual_len = (size >= 0) ? size : len;
    print_len  = (max >= 0 && max < actual_len) ? max : actual_len;
    if (print_len >= 0) {
      hexdump(level, data, print_len);
      if (print_len < actual_len)
          dbg(level, " ...\n");
    }
    if (len < 0)
      dbg(level, "  ERROR: %s\n", strerror(-len));
    dbg(level, "\n");
}


/**********************************************************
 ** Device driver
 **********************************************************/

typedef struct AES AES;
struct AES {
    USBDevice *usb;
    USBEndpoint epOut, epIn;
    unsigned fingerTh1, fingerTh2;
};

typedef struct {
    int length;
    uint8_t *buf;
} AESReplyBuffer;

void aesClose(struct AES *aes);

struct AES *aesOpen(USBDeviceLocation dl)
{
    struct AES *aes;
    USBDevice *usb;

    aes = (struct AES *) calloc(1, sizeof(*aes));
    if (!aes)
      return NULL;
    aes->usb = usbGetDevice(dl);
    if (!aes->usb)
      goto rollback;
    usb = aes->usb;
    aes->epOut = 2;
    aes->epIn = 0x81;
    aes->fingerTh1 = 20;
    aes->fingerTh2 = 0;
    usbClaimInterface(usb, 0);
    usbSetInterface(usb, 0, 0);
    return aes;

 rollback:
    aesClose(aes);
    return NULL;
}

void aesClose(struct AES *aes)
{
    if (!aes)
      return;
    if (aes->usb) {
      usbReleaseInterface(aes->usb, 0);
      usbFreeDevice(aes->usb);
    }
    memset(aes, 0, sizeof(*aes));
    free(aes);
}

static
int aesWrite(struct AES *aes, const void *data, unsigned len)
{
    int res;

    res = usbSendBulkMsg(aes->usb, aes->epOut, (void *) data, len, 1000);
    dump(10, "OUT ", data, res, len, -1);
    return res;
}

static
int aesRead(struct AES *aes, void *data, unsigned size)
{
    int res;

    res = usbSendBulkMsg(aes->usb, aes->epIn, data, size, 1000);
    dump(10, "IN  ", data, res, -1, 64);
    return res;
}

/* Send the following n bytes */
#define SEND(n) n

/* Sleep ms milliseconds */
#define MSLEEP(ms) 0xff, (ms) & 0xff, ((ms) >> 8) & 0xff

/* End of command sequence */
#define ENDCMD 0

/* Read n bytes and store data in the next AESReplyBuffer */
#define RECV(n) 0xfe, (n) & 0xff, ((n) >> 8) & 0xff

static
void aesExec(struct AES *aes, const uint8_t *cmd, AESReplyBuffer rbuf[])
{
    while (cmd[0] != 0) {
      unsigned len = cmd[0];
      if (len == 0xff) {
            /* MSLEEP */
          usleep((cmd[1] + 256*(unsigned)cmd[2]) * 1000);
          cmd += 3;
      } else if (len == 0xfe) {
            /* RECV */
          int count = cmd[1] + 256*(int)cmd[2];
          rbuf->length = aesRead(aes, rbuf->buf, count);
          if (count != rbuf->length) {
            dbg(1, "Request %d bytes but got %d bytes.\n",
                count, rbuf->length);
          }
          cmd += 3;
          rbuf++;
      } else {
            /* SEND */
          aesWrite(aes, cmd+1, len);
          cmd += len + 1;
      }
    }
}

void aesStandby(struct AES *aes)
{
    static const uint8_t cmd[] = {
      MSLEEP(800),
      SEND(6), 0xac,0x01,0xad,0x1a,0x81,0x02,
      RECV(2),
      ENDCMD,
    };
    uint8_t tmp[2];
    AESReplyBuffer rbuf[] = { { 2, tmp } };

    aesExec(aes, cmd, rbuf);
}

/* aesSetup() needs to be called only once after the device is plugged in.
   Anyway, calling it everytime after aesOpen() shouldn't harm anything. */
void aesSetup(struct AES *aes)
{
    static const uint8_t cmd1[] = {
      SEND(4), 0x80,0x01,0x81,0x02,
      RECV(126),
      SEND(2), 0xb0,0x27,
      SEND(4), 0x80,0x01,0x82,0x40,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(100),
      SEND(42), 0x80,0x01,0x82,0x40,0x83,0x00,0x88,0x02,
                0x89,0x10,0x8a,0x05,0x8c,0x00,0x8e,0x13,
                0x91,0x44,0x92,0x34,0x95,0x16,0x96,0x16,
                0x97,0x18,0xa1,0x70,0xa2,0x02,0xa7,0x00,
                0xac,0x01,0xad,0x1a,0x80,0x04,0x81,0x04,
                0xb4,0x00,
      RECV(20),
      SEND(4), 0x80,0x01,0x82,0x40,
      ENDCMD
    };
    static const uint8_t cmd2[] = {
      MSLEEP(100),
      SEND(12), 0x80,0x01,0xa8,0x41,0x82,0x42,0x83,0x53,
                0x80,0x04,0x81,0x02,
      RECV(126),
      ENDCMD
    };
    static const uint8_t cmd3[] = {
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      MSLEEP(80),
      SEND(12), 0x80,0x01,0xa8,0x41,0x82,0x42,0x83,0x53,
                0x80,0x04,0x81,0x02,
      RECV(126),
      ENDCMD
    };
    static const uint8_t cmd4[] = {
      SEND(14), 0x80,0x01,0x82,0x40,0xb0,0x27,0x94,0x0a,
                0x80,0x04,0x83,0x45,0xa8,0x41,
      ENDCMD
    };
    uint8_t tmp[200];
    AESReplyBuffer rbuf[] = {
      { 126, tmp },
      { 20, tmp+126 }
    };

    aesExec(aes, cmd1, rbuf);
    aesExec(aes, cmd2, rbuf);
    while (1) {
      dbg(3, "reg 0xaf = 0x%x\n", rbuf[0].buf[0x5f]);
      if (rbuf[0].buf[0x5f] != 0x6b)
          break;
      aesExec(aes, cmd3, rbuf);
    }
    aesExec(aes, cmd4, NULL);
    aesStandby(aes);
}

static
void aesStartScan(struct AES *aes)
{
    static const uint8_t cmd1[] = {
      SEND(2), 0xb0,0x27,
      SEND(4), 0x80,0x01,0x82,0x40,
      MSLEEP(200),
      SEND(2), 0xff,0x00,
      SEND(4), 0x80,0x01,0x82,0x40,
      SEND(4), 0x80,0x01,0x82,0x40,
      MSLEEP(50),
      SEND(4), 0x80,0x01,0x82,0x40,
      SEND(4), 0x80,0x01,0x82,0x40,
      SEND(4), 0x80,0x01,0x82,0x40,
      SEND(2), 0x80,0x02,
      SEND(2), 0x80,0x02,
      SEND(2), 0x81,0x02,
      RECV(126),
      ENDCMD
    };
    uint8_t buf[126];
    AESReplyBuffer rbuf[] = { { 126, buf } };

    aesExec(aes, cmd1, rbuf);
}

/* 1. Read one column (16 pixels)
   2. Calculate the sum of the pixel values
   3. If the sum is above fingerTh1, a finger touch is considered detected. */
int aesDetectFinger(struct AES *aes)
{
    static const uint8_t cmd[] = {
      SEND(4), 0x80,0x01,0x82,0x40,
      MSLEEP(30),
      SEND(42), 0x80,0x01,0x82,0x40,0x83,0x00,0x88,0x02,
                0x89,0x10,0x8a,0x05,0x8c,0x00,0x8e,0x13,
                0x91,0x44,0x92,0x34,0x95,0x16,0x96,0x16,
                0x97,0x18,0xa1,0x70,0xa2,0x02,0xa7,0x00,
                0xac,0x01,0xad,0x1a,0x80,0x04,0x81,0x04,
                0xb4,0x00,
      RECV(20),
      ENDCMD
    };
    uint8_t buf[20];
    AESReplyBuffer rbuf[] = { { 20, buf } };
    unsigned i, sum;

    aesExec(aes, cmd, rbuf);
    /* One column is returned here but I don't know which one.
       Maybe an average over the whole sensor area. */
    sum = 0;
    for (i = 1; i != 9; i++) {
      sum += (buf[i] & 0xf) + (buf[i] >> 4);
    }
    dbg(3, "sum = %u\n", sum);
    return (sum > aes->fingerTh1);
}

unsigned aesReadFingerprint(struct AES *aes, void *raw_, unsigned maxstrip)
{
    /* 8e xx = set gain */
    static const uint8_t cmd1[] = {
      SEND(4), 0x80,0x01,0x82,0x40,
      SEND(50), 0x80,0x01,0x82,0x40,0x83,0x00,0x88,0x02,
                0x8c,0x7c,0x89,0x10,0x8d,0x24,0x9b,0x00,
                0x9c,0x6c,0x9d,0x09,0x9e,0x54,0x9f,0x78,
                0xa2,0x02,0xa7,0x00,0xb6,0x26,0xb7,0x1a,
                0x80,0x04,0x98,0x23,0x95,0x10,0x96,0x1f,
                0x8e,0x00,0x91,0x70,0x92,0x20,0x81,0x04,
                0xb4,0x00,
      RECV(159),
      ENDCMD
    };
    static const uint8_t cmd2[] = {
      SEND(14), 0x98,0x23,0x95,0x10,0x96,0x1f,0x8e,0x03/*Gain*/,
                0x91,0x70/*HiRef*/,0x92,0x20/*LoRef*/,0x81,0x04,
      RECV(159),
      ENDCMD
    };
    static const uint8_t cmd3[] = {
      SEND(14), 0x98,0x22,0x95,0x00,0x96,0x2f,0x8e,0x03,
                0x91,0x5b,0x92,0x20,0x81,0x04,
      RECV(1705),
      ENDCMD
    };
    uint8_t buf[1705];
    AESReplyBuffer rbuf[] = { { 1705, buf } };
    uint8_t *raw = (uint8_t *) raw_;
    unsigned nstrips, sum;

    aesExec(aes, cmd1, rbuf);
    /* TODO: calibration e.g setting gain (0x8e xx) */
    aesExec(aes, cmd2, rbuf);
    nstrips = 0;
    do {
        /* Timing in this loop is critical. It decides how fast you can move your finger.
           If one loop takes tl second, the maximum speed is:
               (16/500 * 25.4) / tl    [mm per sec]
         */
      int i;
      uint16_t *histogram;

      aesExec(aes, cmd3, rbuf);
      memcpy(raw, buf+1, 192*8);
      raw += 192*8;

      sum = 0;
      /* only work with little-endian machine. */
      histogram = (uint16_t *)(buf + 1 + 192*8 + 1);
      for (i = 10; i != 16; i++) {
            /* histogram[i] = number of pixels of value i
               Only the pixel values from 10 to 15 are used to detect finger. */
          sum += histogram[i];
      }
      dbg(3, "sum = %u\n", sum);
      nstrips++;
    } while (sum > aes->fingerTh2 && nstrips < maxstrip);
    dbg(3, "nstrips = %u\n", nstrips);
    if (nstrips == maxstrip)
      dbg(2, "nstrips == %u, swiping the finger too slowly?\n", maxstrip);
    return nstrips;
}


/**********************************************************
 ** Image processing
 **********************************************************/
static
unsigned rms2_diff(const uint8_t *img1, const uint8_t *img2,
               unsigned n)
{
    unsigned d = 0;
    unsigned i;

    for (i = 0; i != n; i++) {
      int temp = img1[i] - img2[i];
      d += temp*temp;
    }
    return (d*1024) / n;
}


/* Return number of lines the finger moved last time. */
static
unsigned findMovement(const uint8_t *img1, const uint8_t *img2, unsigned h)
{
    unsigned mindiff, shift, i;

    shift = 0;
    mindiff = rms2_diff(img1, img2, 192*h);
    for (i = 1; i != h; i++) {
      unsigned d = rms2_diff(img1 + 192*i, img2, 192*(h-i));
      if (d < mindiff) {
          mindiff = d;
          shift = i;
      }
    }
    dbg(3, " shift = %u, mindiff = %u\n", shift, mindiff);
    return shift;
}

static
unsigned assemble(const uint8_t *raw, uint8_t *img, unsigned nstrips,
              int overlap)
{
    uint8_t *img2 = img;
    unsigned s,r,c,h;

    /* Transpose: convert column-wise to row-wise data. */
    for (s = 0; s != nstrips; s++) {
      for (c = 0; c != 192; c++) {
          for (r = 0; r != 8; r++) {
            img[192*(2*r+0) + c] = raw[r + 8*c] & 0xf;
            img[192*(2*r+1) + c] = raw[r + 8*c] >> 4;
          }
      }
      img += 192*16;
      raw += 192*8;
    }

    if (overlap) {
      img = img2;
      img2 = img + 192*16;
      h = 0;
      for (s = 1; s != nstrips; s++) {
          unsigned o = findMovement(img, img2, 16);
          img += 192*o;
          memcpy(img, img2, 192*16);
          img2 += 192*16;
          h += o;
      }
      return h+16;
    } else {
      return nstrips * 16;
    }
}

static
void toPNM(const void *img_, unsigned w, unsigned h, const char *fn)
{
    FILE *f;
    const uint8_t *img = (const uint8_t *) img_;
    unsigned i;
    uint8_t temp;

    f = fopen(fn, "wb");
    fprintf(f, "P5\n%u %u\n255\n", w, h);
    for (i = 0; i != w*h; i++) {
      temp = (img[i] << 4) + 0xf;
      fwrite(&temp, 1, 1, f);
    }
    fclose(f);
}


/**********************************************************
 ** Usage example
 **********************************************************/

static USBDeviceLocation deviceLocation;
static unsigned ndevices;

static
int attach(USBDeviceLocation dl, void *dummy)
{
    deviceLocation = dl;
    ndevices = 1;
    return 1;
}

static
void init(void)
{
    get_time(&tstart_sec, &tstart_usec);
    usbInit(NULL);
    ndevices = 0;
    usbFindDevices(0x08ff, 0x2580, attach, NULL);
}

static
void cleanup(void)
{
    usbCleanup();
}

int main(int argc, char **argv)
{
    AES *aes;
    char fp_pnm[] = "fp.pnm";
    char fporig_pnm[] = "fporig.pnm";
    char displayCmd[100];
    uint8_t *raw, *cooked;
    unsigned swidth = 192;
    unsigned sheight = 16;
    unsigned maxstrip = 150;
    unsigned nstrips, height;

    printf("Initializing, please standby...\n");
    init();
    if (ndevices == 0) {
      printf("No device found\n");
      return 1;
    }
    raw = (uint8_t *) malloc((3 * maxstrip * sheight * swidth)/2);
    cooked = raw + (maxstrip * sheight * swidth)/2;
    snprintf(displayCmd, sizeof(displayCmd), "display %s %s", fp_pnm, fporig_pnm);
    aes = aesOpen(deviceLocation);
    if (aes) {
      printf("aesSetup()...\n");
      aesSetup(aes);
      printf("aesStartScan()...\n");
      aesStartScan(aes);
      while (1) {
          printf("READY (touch the sensor to stop)\n");
          while (!aesDetectFinger(aes)) {}

          printf("Scanning...\n");
          nstrips = aesReadFingerprint(aes, raw, maxstrip);

          printf("Assembling...\n");
          height = assemble(raw, cooked, nstrips, 1);
          if (height < 100) {
                /* It was a "touch", not a finger scan. */
                break;
            }
          toPNM(cooked, swidth, height, fp_pnm);
          height = assemble(raw, cooked, nstrips, 0);
          toPNM(cooked, swidth, height, fporig_pnm);

          system(displayCmd);
          printf("\n");
      }
      printf("Terminating...\n");
      aesStandby(aes);
      aesClose(aes);
    }
    cleanup();
    free(raw);

    return 0;
}

Generated by  Doxygen 1.6.0   Back to index