Commit fc77786e authored by Daniel Drake's avatar Daniel Drake

uru4000: interrupt handling fixes

This should improve driver stability somewhat. Powerup problems seem to
happen when we receive 3 unrelated interrupts while waiting for the
power-on interrupt, so let's assume that the device can only buffer 3
interrupts, and when the buffer is full, it discards new ones.

When we detect a possible buffer overflow, ask the caller to retry the
operation.

Also, saw the interrupt of death a few times, so add in a warning when
this happens. Haven't seen it since handling interrupt overflows though.
parent 07a81b07
......@@ -44,6 +44,7 @@ enum {
IRQDATA_SCANPWR_ON = 0x56aa,
IRQDATA_FINGER_ON = 0x0101,
IRQDATA_FINGER_OFF = 0x0200,
IRQDATA_DEATH = 0x0800,
};
enum {
......@@ -100,6 +101,31 @@ struct uru4k_dev {
uint8_t interface;
};
/*
* HWSTAT
*
* This register has caused me a lot of headaches. It pretty much defines
* code flow, and if you don't get it right, the pretty lights don't come on.
* I think the situation is somewhat complicated by the fact that writing it
* doesn't affect the read results in the way you'd expect -- but then again
* it does have some obvious effects. Here's what we know
*
* BIT 7: LOW POWER MODE
* When this bit is set, the device is partially turned off or something. Some
* things, like firmware upload, need to be done in this state. But generally
* we want to clear this bit during late initialization, which can sometimes
* be tricky.
*
* BIT 2: SOMETHING WENT WRONG
* Not sure about this, but see the init function, as when we detect it,
* we reboot the device. Well, we mess with hwstat until this evil bit gets
* cleared.
*
* BIT 1: IRQ PENDING
* Just had a brainwave. This bit is set when the device is trying to deliver
* and interrupt to the host. Maybe?
*/
static int get_hwstat(struct fp_img_dev *dev, unsigned char *data)
{
int r;
......@@ -191,56 +217,86 @@ retry:
type = GUINT16_FROM_BE(*((uint16_t *) buf));
fp_dbg("irq type %04x", type);
/* The 0800 interrupt seems to indicate imminent failure (0 bytes transfer)
* of the next scan. I think I've stopped it from coming up, not sure
* though! */
if (type == IRQDATA_DEATH)
fp_warn("oh no! got the interrupt OF DEATH! expect things to go bad");
return 0;
}
static int get_irq_with_type(struct fp_img_dev *dev, uint16_t irqtype,
int timeout)
enum get_irq_status {
GET_IRQ_SUCCESS = 0,
GET_IRQ_OVERFLOW = 1,
};
static int get_irq_with_type(struct fp_img_dev *dev,
uint16_t irqtype, int timeout)
{
uint16_t hdr;
int discarded = -1;
int discarded = 0;
unsigned char irqbuf[IRQ_LENGTH];
fp_dbg("type=%04x", irqtype);
/* Sometimes we get an interrupt from a previous 'session' indicating
* finger-on-sensor, we ignore this and wait for the real interrupt */
do {
int r;
discarded++;
r = get_irq(dev, irqbuf, timeout);
int r = get_irq(dev, irqbuf, timeout);
if (r < 0)
return r;
hdr = GUINT16_FROM_BE(*((uint16_t *) irqbuf));
} while (hdr != irqtype);
if (hdr == irqtype)
break;
discarded++;
} while (discarded < 3);
if (discarded > 0)
fp_dbg("discarded %d interrupts", discarded);
return 0;
if (hdr == irqtype) {
return GET_IRQ_SUCCESS;
} else {
/* I've seen several cases where we're waiting for the 56aa powerup
* interrupt, but instead we just get three 0200 interrupts and then
* nothing. My theory is that the device can only queue 3 interrupts,
* or something. So, if we discard 3, ask the caller to retry whatever
* it was doing. */
fp_dbg("possible IRQ overflow detected!");
return GET_IRQ_OVERFLOW;
}
}
static int await_finger_on(struct fp_img_dev *dev)
{
int r;
retry:
r = set_mode(dev, MODE_AWAIT_FINGER_ON);
if (r < 0)
return r;
return get_irq_with_type(dev, IRQDATA_FINGER_ON, 0);
r = get_irq_with_type(dev, IRQDATA_FINGER_ON, 0);
if (r == GET_IRQ_OVERFLOW)
goto retry;
else
return r;
}
static int await_finger_off(struct fp_img_dev *dev)
{
int r;
retry:
r = set_mode(dev, MODE_AWAIT_FINGER_OFF);
if (r < 0)
return r;
return get_irq_with_type(dev, IRQDATA_FINGER_OFF, 0);
r = get_irq_with_type(dev, IRQDATA_FINGER_OFF, 0);
if (r == GET_IRQ_OVERFLOW)
goto retry;
else
return r;
}
static int capture(struct fp_img_dev *dev, gboolean unconditional,
......@@ -306,6 +362,7 @@ static int do_init(struct fp_img_dev *dev)
int i;
int r;
retry:
r = get_hwstat(dev, &status);
if (r < 0)
return r;
......@@ -376,10 +433,10 @@ static int do_init(struct fp_img_dev *dev)
}
r = get_irq_with_type(dev, IRQDATA_SCANPWR_ON, 5);
if (r < 0)
if (r == GET_IRQ_OVERFLOW)
goto retry;
else
return r;
return 0;
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment