summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarod Wilson <jarod@redhat.com>2010-10-19 14:25:29 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-10-22 20:05:37 -0200
commitd653c0af49960631770962e30bb0aca67c4631a2 (patch)
tree4c05c54bee177742b5256c73f2e3a3dee737dac9
parent917167e9a34f7577086ccfcbf07f6fb96a209a31 (diff)
[media] lirc_igorplugusb: handle hw buffer overruns better
Signed-off-by: Jarod Wilson <jarod@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/staging/lirc/lirc_igorplugusb.c63
1 files changed, 38 insertions, 25 deletions
diff --git a/drivers/staging/lirc/lirc_igorplugusb.c b/drivers/staging/lirc/lirc_igorplugusb.c
index b9a4ffd9c7a..380b8fee9bc 100644
--- a/drivers/staging/lirc/lirc_igorplugusb.c
+++ b/drivers/staging/lirc/lirc_igorplugusb.c
@@ -54,7 +54,7 @@
/* module identification */
-#define DRIVER_VERSION "0.1"
+#define DRIVER_VERSION "0.2"
#define DRIVER_AUTHOR \
"Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
#define DRIVER_DESC "Igorplug USB remote driver for LIRC"
@@ -276,6 +276,25 @@ static void set_use_dec(void *data)
dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
}
+static void send_fragment(struct igorplug *ir, struct lirc_buffer *buf,
+ int i, int max)
+{
+ int code;
+
+ /* MODE2: pulse/space (PULSE_BIT) in 1us units */
+ while (i < max) {
+ /* 1 Igor-tick = 85.333333 us */
+ code = (unsigned int)ir->buf_in[i] * 85 +
+ (unsigned int)ir->buf_in[i] / 3;
+ ir->last_time.tv_usec += code;
+ if (ir->in_space)
+ code |= PULSE_BIT;
+ lirc_buffer_write(buf, (unsigned char *)&code);
+ /* 1 chunk = CODE_LENGTH bytes */
+ ir->in_space ^= 1;
+ ++i;
+ }
+}
/**
* Called in user context.
@@ -299,24 +318,16 @@ static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf)
ir->buf_in, ir->len_in,
/*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
if (ret > 0) {
- int i = DEVICE_HEADERLEN;
int code, timediff;
struct timeval now;
- if (ret <= 1) /* ACK packet has 1 byte --> ignore */
+ /* ACK packet has 1 byte --> ignore */
+ if (ret < DEVICE_HEADERLEN)
return -ENODATA;
dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n",
ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]);
- if (ir->buf_in[2] != 0) {
- printk(DRIVER_NAME "[%d]: Device buffer overrun.\n",
- ir->devnum);
- /* start at earliest byte */
- i = DEVICE_HEADERLEN + ir->buf_in[2];
- /* where are we now? space, gap or pulse? */
- }
-
do_gettimeofday(&now);
timediff = now.tv_sec - ir->last_time.tv_sec;
if (timediff + 1 > PULSE_MASK / 1000000)
@@ -333,18 +344,20 @@ static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf)
lirc_buffer_write(buf, (unsigned char *)&code);
ir->in_space = 1; /* next comes a pulse */
- /* MODE2: pulse/space (PULSE_BIT) in 1us units */
-
- while (i < ret) {
- /* 1 Igor-tick = 85.333333 us */
- code = (unsigned int)ir->buf_in[i] * 85
- + (unsigned int)ir->buf_in[i] / 3;
- if (ir->in_space)
- code |= PULSE_BIT;
- lirc_buffer_write(buf, (unsigned char *)&code);
- /* 1 chunk = CODE_LENGTH bytes */
- ir->in_space ^= 1;
- ++i;
+ if (ir->buf_in[2] == 0)
+ send_fragment(ir, buf, DEVICE_HEADERLEN, ret);
+ else {
+ printk(KERN_WARNING DRIVER_NAME
+ "[%d]: Device buffer overrun.\n", ir->devnum);
+ /* HHHNNNNNNNNNNNOOOOOOOO H = header
+ <---[2]---> N = newer
+ <---------ret--------> O = older */
+ ir->buf_in[2] %= ret - DEVICE_HEADERLEN; /* sanitize */
+ /* keep even-ness to not desync pulse/pause */
+ send_fragment(ir, buf, DEVICE_HEADERLEN +
+ ir->buf_in[2] - (ir->buf_in[2] & 1), ret);
+ send_fragment(ir, buf, DEVICE_HEADERLEN,
+ DEVICE_HEADERLEN + ir->buf_in[2]);
}
ret = usb_control_msg(
@@ -444,7 +457,7 @@ mem_failure_switch:
switch (mem_failure) {
case 9:
- usb_free_coherent(dev, DEVICE_BUFLEN+DEVICE_HEADERLEN,
+ usb_free_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN,
ir->buf_in, ir->dma_in);
case 3:
kfree(driver);
@@ -460,7 +473,7 @@ mem_failure_switch:
ir->d = driver;
ir->devnum = devnum;
ir->usbdev = dev;
- ir->len_in = DEVICE_BUFLEN+DEVICE_HEADERLEN;
+ ir->len_in = DEVICE_BUFLEN + DEVICE_HEADERLEN;
ir->in_space = 1; /* First mode2 event is a space. */
do_gettimeofday(&ir->last_time);