aeslib.c 4.83 KB
Newer Older
1 2
/*
 * Shared functions between libfprint Authentec drivers
3
 * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#define FP_COMPONENT "aeslib"

22 23
#include "fp_internal.h"

24
#include <errno.h>
25
#include <string.h>
26 27 28

#include <glib.h>

29
#include "fpi-usb.h"
30
#include "fpi-assembling.h"
31 32 33 34 35
#include "aeslib.h"

#define MAX_REGWRITES_PER_REQUEST	16

#define BULK_TIMEOUT	4000
36 37
#define EP_IN			(1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT			(2 | LIBUSB_ENDPOINT_OUT)
38

39 40 41 42 43 44
struct write_regv_data {
	struct fp_img_dev *imgdev;
	unsigned int num_regs;
	const struct aes_regwrite *regs;
	unsigned int offset;
	aes_write_regv_cb callback;
45
	void *user_data;
46 47 48 49 50 51
};

static void continue_write_regv(struct write_regv_data *wdata);

/* libusb bulk callback for regv write completion transfer. continues the
 * transaction */
52
static void write_regv_trf_complete(struct libusb_transfer *transfer)
53
{
54
	struct write_regv_data *wdata = transfer->user_data;
55

56
	if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
57
		wdata->callback(wdata->imgdev, -EIO, wdata->user_data);
58
	else if (transfer->length != transfer->actual_length)
59
		wdata->callback(wdata->imgdev, -EPROTO, wdata->user_data);
60 61
	else
		continue_write_regv(wdata);
62 63 64

	g_free(transfer->buffer);
	libusb_free_transfer(transfer);
65 66 67 68 69 70 71
}

/* write from wdata->offset to upper_bound (inclusive) of wdata->regs */
static int do_write_regv(struct write_regv_data *wdata, int upper_bound)
{
	unsigned int offset = wdata->offset;
	unsigned int num = upper_bound - offset + 1;
72 73 74
	size_t alloc_size = num * 2;
	unsigned char *data = g_malloc(alloc_size);
	unsigned int i;
75
	size_t data_offset = 0;
76
	struct libusb_transfer *transfer = fpi_usb_alloc();
77 78
	int r;

79 80 81 82
	for (i = offset; i < offset + num; i++) {
		const struct aes_regwrite *regwrite = &wdata->regs[i];
		data[data_offset++] = regwrite->reg;
		data[data_offset++] = regwrite->value;
83 84
	}

85
	libusb_fill_bulk_transfer(transfer, FP_DEV(wdata->imgdev)->udev, EP_OUT, data,
86 87 88
		alloc_size, write_regv_trf_complete, wdata, BULK_TIMEOUT);
	r = libusb_submit_transfer(transfer);
	if (r < 0) {
89
		g_free(data);
90
		libusb_free_transfer(transfer);
91 92
	}

93
	return r;
94 95
}

96 97 98
/* write the next batch of registers to be written, or if there are no more,
 * indicate completion to the caller */
static void continue_write_regv(struct write_regv_data *wdata)
99
{
100 101 102 103 104 105 106 107 108 109 110
	unsigned int offset = wdata->offset;
	unsigned int regs_remaining;
	unsigned int limit;
	unsigned int upper_bound;
	int i;
	int r;

	/* skip all zeros and ensure there is still work to do */
	while (TRUE) {
		if (offset >= wdata->num_regs) {
			fp_dbg("all registers written");
111
			wdata->callback(wdata->imgdev, 0, wdata->user_data);
112
			return;
113
		}
114 115 116 117
		if (wdata->regs[offset].reg)
			break;
		offset++;
	}
118

119
	wdata->offset = offset;
120 121
	regs_remaining = wdata->num_regs - offset;
	limit = MIN(regs_remaining, MAX_REGWRITES_PER_REQUEST);
122
	upper_bound = offset + limit - 1;
123

124 125
	/* determine if we can write the entire of the regs at once, or if there
	 * is a zero dividing things up */
126
	for (i = offset; i <= upper_bound; i++)
127 128 129 130 131 132 133
		if (!wdata->regs[i].reg) {
			upper_bound = i - 1;
			break;
		}

	r = do_write_regv(wdata, upper_bound);
	if (r < 0) {
134
		wdata->callback(wdata->imgdev, r, wdata->user_data);
135
		return;
136 137
	}

138 139 140 141 142 143 144
	wdata->offset = upper_bound + 1;
}

/* write a load of registers to the device, combining multiple writes in a
 * single URB up to a limit. insert writes to non-existent register 0 to force
 * specific groups of writes to be separated by different URBs. */
void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
145
	unsigned int num_regs, aes_write_regv_cb callback, void *user_data)
146
{
147 148
	struct write_regv_data *wdata;

149
	fp_dbg("write %d regs", num_regs);
150
	wdata = g_malloc(sizeof(*wdata));
151 152 153 154 155
	wdata->imgdev = dev;
	wdata->num_regs = num_regs;
	wdata->regs = regs;
	wdata->offset = 0;
	wdata->callback = callback;
156
	wdata->user_data = user_data;
157
	continue_write_regv(wdata);
158 159

	g_free(wdata);
160 161
}

162 163
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
					  struct fpi_frame *frame,
164
					  unsigned int x,
165
					  unsigned int y)
Daniel Drake's avatar
Daniel Drake committed
166
{
167
	unsigned char ret;
Daniel Drake's avatar
Daniel Drake committed
168

169
	ret = frame->data[x * (ctx->frame_height >> 1) + (y >> 1)];
170 171 172 173
	ret = y % 2 ? ret >> 4 : ret & 0xf;
	ret *= 17;

	return ret;
Daniel Drake's avatar
Daniel Drake committed
174
}