aes2501-wy/0000750000175000001440000000000010500303333012154 5ustar yamwongusersaes2501-wy/Makefile0000640000175000001440000000017310500267763013636 0ustar yamwongusersCFLAGS = -O2 -g -Wall LDFLAGS = -g all: aes2501 aes2501: aes2501.o usb.o usb.o: usb.c usb.h aes2501.o: aes2501.c usb.h aes2501-wy/aes2501.c0000640000175000001440000003755710500303307013423 0ustar yamwongusers/* * Copyright (C) 2006 Wittawat Yamwong * * 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 #include #include #include #include #include #include #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; } aes2501-wy/NOTES0000640000175000001440000000221110500270751012773 0ustar yamwongusersMedion MD85264 (AES2501 chip) ----------------------------- * the device has remote wakeup feature. WRITE: 80 01 = 80 04 = 81 xx = read registers 82 40 = b0 27 = b4 00 = ff 00 = reset? 8e xx = ADC Gain? 91 xx = ADC HiRef? 92 xx = ADC LoRef? READ: 80..be + 1 byte reg value ba + 1 byte relative Y coordinate bc + 1 byte relative Y coordinate f0 + 8 bytes image data for one column, 16 pixels df + 8 bytes ? e0 + 192*8 bytes image data for one strip, 16*192 pixels de + 16*2 bytes histogram? little endian, 16*uint16_t, sum = 0x780 = 1920 image data format: - column-wise, 192 columns/strip, 16 pixels/col, 4 bits/pixel - the first byte contains the first two pixels, etc. - lower nibble comes first. - e.g. raw data = 00 01 02 03 04 05 06 07 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 ... decoded image: x --> y 0 8 0 ... | 0 1 2 ... v 1 9 1 ... 0 1 2 ... 2 a 2 ... 0 1 2 ... 3 b 3 ... 0 1 2 ... 4 c 4 ... 0 1 2 ... 5 d 5 ... 0 1 2 ... 6 e 6 ... 0 1 2 ... 7 f 7 ... 0 1 2 ... aes2501-wy/usb.c0000640000175000001440000004541710500267726013144 0ustar yamwongusers/* * Userspace USB interface for Linux * Copyright (C) 2006 Wittawat Yamwong * * 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. */ /* TODO: iso transfer */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_RESMGR #include #endif #include "usb.h" #define INLINE __inline__ #define GET_DESC_TIMEOUT 1000/*ms*/ #define DEFAULT_UDEV_USB_ROOT "/dev/bus/usb" #define DEFAULT_USBFS_ROOT "/proc/bus/usb" #define DEFAULT_USBFS_PATTERN "%03u/%03u" /* This should be less than or equal MAX_USBFS_BUFFER_SIZE defined in * linux/drivers/usb/core/devio.c */ #define MAX_URB_BUFFER_SIZE (16*1024) #ifndef NAME_MAX # define NAME_MAX 255 #endif #ifndef timercmp # define timercmp(tvp, uvp, cmp) \ ((tvp)->tv_sec cmp (uvp)->tv_sec || \ ((tvp)->tv_sec == (uvp)->tv_sec && \ (tvp)->tv_usec cmp (uvp)->tv_usec)) #endif struct USB_device { USBDevice *next; USBDeviceLocation location; int fd, rdwr; USBDeviceDescriptor deviced; uint8_t **rawconfd; unsigned nconfd; }; typedef struct URBNode { struct URBNode *next; USBURB urb; } URBNode; static char usbRoot[NAME_MAX+1]; static const char *usbRootPattern; static const char *usbfsPattern; static const char *udevPattern; static const char *resmgrPattern; static USBDevice *firstDevice = NULL; static URBNode *firstURB = NULL; static int usbOpen(USBDevice *dev, int rdwr); static int usbClose(USBDevice *dev); static INLINE USBDeviceLocation usbLocation(uint8_t bus, uint8_t device) { return (bus << 8) | device; } static INLINE uint8_t usbBusNumber(USBDeviceLocation l) { return (uint8_t)(l >> 8); } static INLINE uint8_t usbDeviceNumber(USBDeviceLocation l) { return (uint8_t)l; } static INLINE uint16_t getLe16(const uint16_t *le) { const uint8_t *b = (const uint8_t *)le; return (b[1] << 8) | b[0]; } static INLINE void setLe16(uint16_t *le, uint16_t native) { uint8_t *b = (uint8_t *)le; b[0] = native; b[1] = native >> 8; } static const char *guessDirPattern(const char *root) { DIR *dir; const char *pat = NULL; struct dirent *de; char *endptr; int temp; char s[10]; dir = opendir(root); if (!dir) return NULL; while ((de = readdir(dir))) { if (de->d_name[0] == '.') continue; temp = strtol(de->d_name, &endptr, 10); if (endptr[0] == '\0' && temp >= 0) { sprintf(s, "%d", temp); if (strcmp(de->d_name, s) == 0) { pat = "%u/%u"; } else { pat = "%03u/%03u"; } break; } } closedir(dir); return pat; } static void makeDeviceName(char *path, unsigned size, const char *pattern, const char *root, USBDeviceLocation l) { unsigned len; snprintf(path, size, "%s/", root); len = strlen(path); snprintf(path + len, size - len, pattern, usbBusNumber(l), usbDeviceNumber(l)); } static int openInUsbfs(USBDeviceLocation l, int rdwr) { char path[NAME_MAX]; if (!usbfsPattern) { errno = ENOENT; return -1; } makeDeviceName(path, sizeof(path), usbfsPattern, DEFAULT_USBFS_ROOT, l); return open(path, rdwr ? O_RDWR : O_RDONLY); } static int openInUdev(USBDeviceLocation l, int rdwr) { char path[NAME_MAX]; if (!udevPattern) { errno = ENOENT; return -1; } makeDeviceName(path, sizeof(path), udevPattern, DEFAULT_UDEV_USB_ROOT, l); return open(path, rdwr ? O_RDWR : O_RDONLY); } static int openInResmgr(USBDeviceLocation l, int rdwr) { #ifdef WITH_RESMGR char path[NAME_MAX]; snprintf(path, sizeof(path), resmgrPattern, usbBusNumber(l), usbDeviceNumber(l)); return rsm_open_device(path, rdwr ? O_RDWR : O_RDONLY); #else (void) l; (void) rdwr; errno = ENOENT; return -1; #endif } static void freeConfigBuf(USBDevice *dev) { unsigned i; if (!dev->rawconfd) return; for (i=0; i != dev->nconfd; i++) free(dev->rawconfd[i]); free(dev->rawconfd); dev->rawconfd = NULL; dev->nconfd = 0; } static void readConfigurationDescriptor(USBDevice *dev, int idx) { int result; USBConfigurationDescriptor confd, *c; uint8_t *buf; result = usbGetDescriptor(dev, USB_DT_CONFIGURATION, idx, 0, sizeof(confd), &confd); if (result != sizeof(confd)) return; confd.wTotalLength = getLe16(&confd.wTotalLength); free(dev->rawconfd[idx]); buf = (uint8_t *) malloc(confd.wTotalLength); dev->rawconfd[idx] = buf; if (!buf) return; result = usbGetDescriptor(dev, USB_DT_CONFIGURATION, idx, 0, confd.wTotalLength, buf); if (result < (int)sizeof(USBConfigurationDescriptor)) { free(dev->rawconfd[idx]); dev->rawconfd[idx] = NULL; return; } c = (USBConfigurationDescriptor *) buf; setLe16(&c->wTotalLength, result); } static void readDescriptors(USBDevice *dev) { int result,i; result = usbGetDescriptor(dev, USB_DT_DEVICE, 0, 0, sizeof(dev->deviced), &dev->deviced); if (result != sizeof(dev->deviced)) { dev->deviced.bLength = 0; return; } dev->deviced.bcdUSB = getLe16(&dev->deviced.bcdUSB); dev->deviced.idVendor = getLe16(&dev->deviced.idVendor); dev->deviced.idProduct = getLe16(&dev->deviced.idProduct); dev->deviced.bcdDevice = getLe16(&dev->deviced.bcdDevice); freeConfigBuf(dev); dev->rawconfd = (uint8_t **) calloc(dev->deviced.bNumConfigurations, sizeof(*dev->rawconfd)); if (!dev->rawconfd) return; dev->nconfd = dev->deviced.bNumConfigurations; for (i=0; i != dev->deviced.bNumConfigurations; i++) { readConfigurationDescriptor(dev, i); } } static int usbOpen(USBDevice *dev, int rdwr) { int result = 0; char path[NAME_MAX]; if (dev->fd >= 0 && dev->rdwr < rdwr) { usbClose(dev); /* Open with O_RDWR later. */ } if (dev->fd == -1) { makeDeviceName(path, sizeof(path), usbRootPattern, usbRoot, dev->location); dev->fd = open(path, rdwr ? O_RDWR : O_RDONLY); if (dev->fd == -1) dev->fd = openInUdev(dev->location, rdwr); if (dev->fd == -1) dev->fd = openInUsbfs(dev->location, rdwr); if (dev->fd == -1) dev->fd = openInResmgr(dev->location, rdwr); if (dev->fd == -1) { result = -errno; } else { dev->rdwr = rdwr; readDescriptors(dev); } } return result; } static int usbClose(USBDevice *dev) { if (dev->fd != -1) { if (close(dev->fd) == -1) return -errno; dev->fd = -1; } return 0; } static int usbScanDevicesInBus(unsigned bus, const char *bus_name, uint16_t vid, uint16_t pid, USBFindDevicesCallback callback, void *data) { DIR *dir; char path[NAME_MAX+1]; struct dirent *de; USBDevice *dev; char *endptr; const USBDeviceDescriptor *ddesc; int stop = 0; unsigned idVendor, idProduct; snprintf(path, sizeof(path), "%s/%s", usbRoot, bus_name); dir = opendir(path); if (!dir) return stop; while (!stop && (de = readdir(dir))) { int devnum = strtol(de->d_name, &endptr, 10); if (*endptr != '\0' || devnum < 0) continue; dev = usbGetDevice(usbLocation(bus, devnum)); ddesc = usbGetDeviceDescriptor(dev); if (!ddesc) { usbFreeDevice(dev); continue; } idVendor = ddesc->idVendor; idProduct = ddesc->idProduct; usbFreeDevice(dev); if ((vid == 0 || (idVendor == vid)) && (pid == 0 || (idProduct == pid))) stop = callback(usbLocation(bus, devnum), data); } closedir(dir); return stop; } void usbInit(const char *root) { usbRoot[0] = '\0'; udevPattern = guessDirPattern(DEFAULT_UDEV_USB_ROOT); usbfsPattern = guessDirPattern(DEFAULT_USBFS_ROOT); resmgrPattern = "usb:%u,%u"; if (root) { strncpy(usbRoot, root, sizeof(usbRoot)); usbRootPattern = guessDirPattern(usbRoot); if (usbRootPattern) return; } if (udevPattern) { strncpy(usbRoot, DEFAULT_UDEV_USB_ROOT, sizeof(usbRoot)); usbRootPattern = udevPattern; return; } if (usbfsPattern) { strncpy(usbRoot, DEFAULT_USBFS_ROOT, sizeof(usbRoot)); usbRootPattern = usbfsPattern; return; } /* Fallback to usbfs even if it isn't mounted. */ strncpy(usbRoot, DEFAULT_USBFS_ROOT, sizeof(usbRoot)); usbRootPattern = DEFAULT_USBFS_PATTERN; } void usbCleanup(void) { while (firstDevice) usbFreeDevice(firstDevice); while (firstURB) { firstURB->urb.status = 0; usbDestroyUrb(&(firstURB->urb)); } } const char *usbGetRootDir(void) { return usbRoot; } void usbFindDevices(uint16_t vid, uint16_t pid, USBFindDevicesCallback callback, void *data) { DIR *dir; struct dirent *de; char *endptr; int stop = 0; dir = opendir(usbRoot); if (!dir) return; while (!stop && (de = readdir(dir))) { int bus = strtol(de->d_name, &endptr, 10); if (*endptr == '\0' && bus >= 0) stop = usbScanDevicesInBus(bus, de->d_name, vid, pid, callback, data); } closedir(dir); } void usbScanDevices(USBFindDevicesCallback callback, void *data) { usbFindDevices(0, 0, callback, data); } USBDevice *usbGetDevice(USBDeviceLocation l) { USBDevice *dev; dev = (USBDevice *) calloc(1,sizeof(dev[0])); if (dev) { dev->next = firstDevice; firstDevice = dev; dev->location = l; dev->fd = -1; usbOpen(dev, 0); } return dev; } void usbFreeDevice(USBDevice *dev) { USBDevice **p; if (!dev) return; for (p = &firstDevice; *p && *p != dev; p = &((*p)->next)) { } if (!(*p)) { fprintf(stderr,"BUG:usbFreeDevice():invalid device\n"); return; } *p = dev->next; freeConfigBuf(dev); usbClose(dev); free(dev); } const USBDeviceDescriptor *usbGetDeviceDescriptor(USBDevice *dev) { return (dev->deviced.bLength != 0) ? &dev->deviced : NULL; } const USBConfigurationDescriptor *usbGetConfigurationDescriptor( USBDevice *dev, unsigned idx) { return (idx < dev->nconfd) ? (USBConfigurationDescriptor *) dev->rawconfd[idx] : NULL; } USBDeviceLocation usbGetDeviceLocation(USBDevice *dev) { return dev->location; } int usbClaimInterface(USBDevice *dev, unsigned intf) { int result; result = usbOpen(dev, 1); if (result == 0 && ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &intf) == -1) result = -errno; return result; } int usbReleaseInterface(USBDevice *dev, unsigned intf) { int result = 0; if (dev->fd != -1 && ioctl(dev->fd, USBDEVFS_RELEASEINTERFACE, &intf) == -1) result = -errno; return result; } int usbSubmitUrb(USBDevice *dev, USBURB *urb) { struct usbdevfs_urb *lurb = NULL; int error = 0; error = usbOpen(dev, 1); if (error < 0) return error; if (!urb->priv || urb->status == -EINPROGRESS) { fprintf(stderr, "BUG:usbSubmitUrb():invalid urb %p %p %s\n", (void*)urb, urb->priv, strerror(-urb->status)); return -EINVAL; } lurb = (struct usbdevfs_urb *) urb->priv; memset(lurb, 0, sizeof(lurb[0])); switch (urb->type) { case USBControl: lurb->type = USBDEVFS_URB_TYPE_CONTROL; break; case USBBulk: lurb->type = USBDEVFS_URB_TYPE_BULK; break; case USBInterrupt: lurb->type = USBDEVFS_URB_TYPE_INTERRUPT; break; case USBIsochronous: default: return -EINVAL; } lurb->endpoint = urb->ep; lurb->buffer = urb->buffer; lurb->buffer_length = urb->bufferSize; lurb->usercontext = urb; urb->status = 0; if (ioctl(dev->fd, USBDEVFS_SUBMITURB, lurb) < 0) return -errno; urb->status = -EINPROGRESS; return 0; } int usbGetCompleteUrb(USBDevice *dev, USBURB **completed, int timeout/*[ms]*/) { int error; struct pollfd pf; struct usbdevfs_urb *lurb; USBURB *urb; *completed = NULL; error = usbOpen(dev, 1); if (error < 0) return error; do { pf.fd = dev->fd; pf.events = POLLOUT; pf.revents = 0; error = poll(&pf, 1, timeout); if (error == -1) return -errno; /* e.g. EINTR */ if (error == 0) return 0; /* timed out */ if ((pf.revents & POLLERR) != 0) return -ENODEV; if (ioctl(dev->fd, USBDEVFS_REAPURB, &lurb) == -1) return -errno; urb = (USBURB *)lurb->usercontext; if (urb) { urb->status = lurb->status; urb->actualLength = lurb->actual_length; if (urb->callback) urb->callback(urb); } else { /* USBURB has been already destroyed. */ free(lurb); } *completed = urb; } while (*completed == NULL); return 1; } /* A version of usbGetCompleteUrb() that is immune against signals. */ int usbGetCompleteUrbNoIntr(USBDevice *dev, USBURB **completed, int timeout) { struct timeval to,t; int sec,error; if (timeout < 0) { do { error = usbGetCompleteUrb(dev, completed, timeout); } while (error == -EINTR); return error; } else { gettimeofday(&to, NULL); sec = timeout / 1000; to.tv_usec += (timeout - 1000*sec) * 1000; to.tv_sec += sec; if (to.tv_usec >= 1000000) { to.tv_usec -= 1000000; to.tv_sec++; } error = usbGetCompleteUrb(dev, completed, timeout); while (error == -EINTR) { gettimeofday(&t, NULL); if (timercmp(&to, &t, <)) { error = 0; break; } timeout = (to.tv_sec - t.tv_sec)*1000 + (to.tv_usec - t.tv_usec)/1000; error = usbGetCompleteUrb(dev, completed, timeout); } return error; } } int usbCancelUrb(USBDevice *dev, USBURB *urb) { if (dev->fd < 0 || !urb || !urb->priv || urb->status != -EINPROGRESS) return -EINVAL; ioctl(dev->fd, USBDEVFS_DISCARDURB, urb->priv); return 0; } static int usbSendBulkMsg_i(USBDevice *dev, USBEndpoint ep, void *data, unsigned len, int timeout/*ms*/) { int error; struct usbdevfs_bulktransfer bulk; bulk.ep = ep; bulk.len = len; if (timeout < 0) timeout = 0; else if (timeout == 0) timeout = 1; bulk.timeout = timeout; bulk.data = data; error = ioctl(dev->fd, USBDEVFS_BULK, &bulk); if (error == -1) error = -errno; return error; } int usbSendBulkMsg(USBDevice *dev, USBEndpoint ep, void *data_, unsigned len, int timeout/*ms*/) { int error; uint8_t *data = (uint8_t *)data_; int pos, count, blocksize; error = usbOpen(dev, 1); if (error < 0) return error; pos = 0; do { blocksize = (len < MAX_URB_BUFFER_SIZE) ? len : MAX_URB_BUFFER_SIZE; count = usbSendBulkMsg_i(dev, ep, data+pos, blocksize, timeout); if (count < 0) return count; pos += count; len -= count; } while (len != 0 && count == blocksize); return pos; } int usbSendControlMsg(USBDevice *dev, USBEndpoint ep, uint8_t bRequest, uint8_t bmRequestType, uint16_t wValue, uint16_t wIndex, uint16_t wLength, void *data, int timeout) { int error; struct usbdevfs_ctrltransfer ctrl; error = usbOpen(dev, 1); if (error < 0) return error; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) /* FIXME: In which version has the naming convension changed? */ ctrl.request = bRequest; ctrl.requesttype = bmRequestType; ctrl.value = wValue; ctrl.index = wIndex; ctrl.length = wLength; #else ctrl.bRequest = bRequest; ctrl.bRequestType = bmRequestType; ctrl.wValue = wValue; ctrl.wIndex = wIndex; ctrl.wLength = wLength; #endif if (timeout < 0) timeout = 0; else if (timeout == 0) timeout = 1; ctrl.timeout = timeout; ctrl.data = data; error = ioctl(dev->fd, USBDEVFS_CONTROL, &ctrl); if (error == -1) error = -errno; return error; } USBURB *usbNewUrb(void) { URBNode *urbNode; struct usbdevfs_urb *lurb; urbNode = (URBNode *) calloc(1, sizeof(*urbNode)); if (!urbNode) return NULL; lurb = (struct usbdevfs_urb *) calloc(1, sizeof(*lurb)); if (!lurb) { free(urbNode); return NULL; } urbNode->next = firstURB; firstURB = urbNode; urbNode->urb.priv = lurb; return &urbNode->urb; } void usbDestroyUrb(USBURB *urb) { URBNode *urbNode, **p; struct usbdevfs_urb *lurb; if (!urb) return; for (p = &firstURB; *p && &((*p)->urb) != urb; p = &((*p)->next)) { } urbNode = *p; if (!urbNode) { fprintf(stderr,"BUG:usbDestroyUrb():invalid URB\n"); return; } lurb = (struct usbdevfs_urb *) urb->priv; urb->priv = NULL; if (lurb) lurb->usercontext = NULL; if (urb->status == -EINPROGRESS) { fprintf(stderr, "BUG:usbDestroyUrb(): destroying pending urb\n"); } else { free(lurb); } *p = urbNode->next; free(urbNode); } int usbGetDescriptor(USBDevice *dev, uint8_t type, uint8_t descidx, uint16_t wIndex, uint16_t wLength, void *data) { return usbSendControlMsg(dev, 0, 6 /*GET_DESCRIPTOR*/, 0x80, (type << 8) | descidx, wIndex, wLength, data, GET_DESC_TIMEOUT); } int usbGetFirstLanguageString(USBDevice *dev, unsigned idx, void *buf, unsigned bufsize) { struct PACKED { USBDescriptor h; uint16_t lang; } temp; int result; if (idx == 0) return 0; /* Read the first language code. Assumed that there is at least one. */ result = usbGetDescriptor(dev, USB_DT_STRING, 0, 0, 4, &temp); if (result != 4) return result; /* Then read the string. */ return usbGetDescriptor(dev, USB_DT_STRING, idx, getLe16(&temp.lang), bufsize, buf); } int usbResetDevice(USBDevice *dev) { int error; error = usbOpen(dev, 1); if (error < 0) return error; if (ioctl(dev->fd, USBDEVFS_RESET, NULL) == -1) return -errno; return 0; } int usbSetInterface(USBDevice *dev, uint8_t iface, uint8_t altsetting) { int error; struct usbdevfs_setinterface si; error = usbOpen(dev, 1); if (error < 0) return error; si.interface = iface; si.altsetting = altsetting; if (ioctl(dev->fd, USBDEVFS_SETINTERFACE, &si) == -1) return -errno; return 0; } aes2501-wy/usb.h0000640000175000001440000001324710500267726013145 0ustar yamwongusers/* * Userspace USB interface for Linux * Copyright (C) 2006 Wittawat Yamwong * * 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. */ #ifndef MY_USB_H #define MY_USB_H #include #define PACKED __attribute__((packed)) #define USB_DT_DEVICE 1 #define USB_DT_CONFIGURATION 2 #define USB_DT_STRING 3 #define USB_DT_INTERFACE 4 #define USB_DT_ENDPOINT 5 #define USB_DT_DEVICE_QUALIFIER 6 #define USB_DT_OTHER_SPEED_CONFIGURATION 7 #define USB_DT_INTERFACE_POWER 8 typedef struct PACKED USB_descriptor { uint8_t bLength; uint8_t bDescriptorType; } USBDescriptor; typedef struct PACKED USB_device_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint16_t bcdUSB; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; uint8_t bMaxPacketSize0; uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; uint8_t iManufacturer; uint8_t iProduct; uint8_t iSerialNumber; uint8_t bNumConfigurations; } USBDeviceDescriptor; typedef struct PACKED USB_configuration_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint16_t wTotalLength; uint8_t bNumInterfaces; uint8_t bConfigurationValue; uint8_t iConfiguration; uint8_t bmAttributes; uint8_t bMaxPower; } USBConfigurationDescriptor; typedef struct PACKED USB_interface_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bInterfaceNumber; uint8_t bAlternateSetting; uint8_t bNumEndpoints; uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; uint8_t iInterface; } USBInterfaceDescriptor; typedef struct PACKED USB_endpoint_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bEndpointAddress; uint8_t bmAttributes; uint16_t wMaxPacketSize; uint8_t bInterval; } USBEndpointDescriptor; struct USB_device; struct USB_urb; typedef struct USB_device USBDevice; typedef uint32_t USBDeviceLocation; typedef uint8_t USBEndpoint; typedef void (*USBURBCallback)(struct USB_urb *); typedef int (*USBFindDevicesCallback)(USBDeviceLocation, void *); typedef uint16_t USBChar; typedef enum { USBControl = 0, USBIsochronous = 1,/* TODO: */ USBBulk = 2, USBInterrupt = 3 } USBTransferType; typedef struct USB_urb { void *priv; /* Don't use this field! */ USBTransferType type; USBEndpoint ep; int status; void *buffer; unsigned bufferSize; unsigned actualLength; USBURBCallback callback; /* The callback will be called in the context of * usbGetCompleteUrb*() */ void *userContext; /* The usb library won't touch this field. */ } USBURB; /* timeout is in milliseconds. If timeout < 0, block until the operation * completes or an error occurs. */ void usbInit(const char *usbRoot); void usbCleanup(void); const char *usbGetRootDir(void); void usbFindDevices(uint16_t vid, uint16_t pid, USBFindDevicesCallback callback, void *data); void usbScanDevices(USBFindDevicesCallback callback, void *data); USBDevice *usbGetDevice(USBDeviceLocation); void usbFreeDevice(USBDevice*); const USBDeviceDescriptor *usbGetDeviceDescriptor(USBDevice *); const USBConfigurationDescriptor *usbGetConfigurationDescriptor( USBDevice *, unsigned idx); USBDeviceLocation usbGetDeviceLocation(USBDevice *); int usbClaimInterface(USBDevice *, unsigned inf); int usbReleaseInterface(USBDevice *, unsigned inf); USBURB *usbNewUrb(void); void usbDestroyUrb(USBURB *); int usbSubmitUrb(USBDevice *, USBURB *); int usbGetCompleteUrb(USBDevice *, USBURB **completed, int timeout); int usbGetCompleteUrbNoIntr(USBDevice *dev, USBURB **completed, int timeout); int usbCancelUrb(USBDevice *, USBURB *); int usbSendBulkMsg(USBDevice *, USBEndpoint, void *data, unsigned len, int timeout); int usbSendControlMsg(USBDevice *, USBEndpoint, uint8_t bRequest, uint8_t bmRequestType, uint16_t wValue, uint16_t wIndex, uint16_t wLength, void *data, int timeout); int usbGetDescriptor(USBDevice *, uint8_t type, uint8_t descidx, uint16_t wIndex, uint16_t wLength, void *data); int usbGetFirstLanguageString(USBDevice *, unsigned idx, void *buf, unsigned bufsize); int usbResetDevice(USBDevice *); int usbSetInterface(USBDevice *, uint8_t iface, uint8_t altsetting); /*int usbSetConfiguration(USBDevice *, uint8_t configuration);*/ /*int usbGetConfiguration(USBDevice *);*/ #endif