diff options
author | Jarod Wilson <jarod@redhat.com> | 2010-10-19 14:25:29 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-10-22 20:05:37 -0200 |
commit | d653c0af49960631770962e30bb0aca67c4631a2 (patch) | |
tree | 4c05c54bee177742b5256c73f2e3a3dee737dac9 | |
parent | 917167e9a34f7577086ccfcbf07f6fb96a209a31 (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.c | 63 |
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); |