media/rc: Add iguanair driver from Linux 3.7 (Closes: #696925)

svn path=/dists/sid/linux/; revision=19663
This commit is contained in:
Ben Hutchings 2012-12-29 13:57:44 +00:00
parent 494dc867ec
commit ac15a4b9c5
14 changed files with 1944 additions and 0 deletions

1
debian/changelog vendored
View File

@ -2,6 +2,7 @@ linux (3.2.35-3) UNRELEASED; urgency=low
[ Ben Hutchings ]
* Input: wacom - fix touch support for Bamboo Fun CTH-461
* media/rc: Add iguanair driver from Linux 3.7 (Closes: #696925)
-- Ben Hutchings <ben@decadent.org.uk> Thu, 27 Dec 2012 02:17:44 +0100

View File

@ -1144,6 +1144,7 @@ CONFIG_IR_MCEUSB=m
# CONFIG_IR_NUVOTON is not set
CONFIG_IR_REDRAT3=m
CONFIG_IR_STREAMZAP=m
CONFIG_IR_IGUANA=m
CONFIG_RC_LOOPBACK=m
##

View File

@ -0,0 +1,684 @@
From: Sean Young <sean@mess.org>
Date: Sun, 15 Jul 2012 13:31:00 -0300
Subject: [01/11] [media] Add support for the IguanaWorks USB IR Transceiver
commit 26ff63137c45886169ed102bddd6e90d6c27f00d upstream.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
drivers/media/rc/Kconfig | 11 +
drivers/media/rc/Makefile | 1 +
drivers/media/rc/iguanair.c | 639 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 651 insertions(+)
create mode 100644 drivers/media/rc/iguanair.c
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -243,6 +243,17 @@ config IR_WINBOND_CIR
To compile this driver as a module, choose M here: the module will
be called winbond_cir.
+config IR_IGUANA
+ tristate "IguanaWorks USB IR Transceiver"
+ depends on RC_CORE
+ select USB
+ ---help---
+ Say Y here if you want to use the IgaunaWorks USB IR Transceiver.
+ Both infrared receive and send are supported.
+
+ To compile this driver as a module, choose M here: the module will
+ be called iguanair.
+
config RC_LOOPBACK
tristate "Remote Control Loopback Driver"
depends on RC_CORE
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_IR_REDRAT3) += redrat3.o
obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
+obj-$(CONFIG_IR_IGUANA) += iguanair.o
--- /dev/null
+++ b/drivers/media/rc/iguanair.c
@@ -0,0 +1,639 @@
+/*
+ * IguanaWorks USB IR Transceiver support
+ *
+ * Copyright (C) 2012 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME "iguanair"
+
+struct iguanair {
+ struct rc_dev *rc;
+
+ struct device *dev;
+ struct usb_device *udev;
+
+ int pipe_in, pipe_out;
+ uint8_t bufsize;
+ uint8_t version[2];
+
+ struct mutex lock;
+
+ /* receiver support */
+ bool receiver_on;
+ dma_addr_t dma_in;
+ uint8_t *buf_in;
+ struct urb *urb_in;
+ struct completion completion;
+
+ /* transmit support */
+ bool tx_overflow;
+ uint32_t carrier;
+ uint8_t cycle_overhead;
+ uint8_t channels;
+ uint8_t busy4;
+ uint8_t busy7;
+
+ char name[64];
+ char phys[64];
+};
+
+#define CMD_GET_VERSION 0x01
+#define CMD_GET_BUFSIZE 0x11
+#define CMD_GET_FEATURES 0x10
+#define CMD_SEND 0x15
+#define CMD_EXECUTE 0x1f
+#define CMD_RX_OVERFLOW 0x31
+#define CMD_TX_OVERFLOW 0x32
+#define CMD_RECEIVER_ON 0x12
+#define CMD_RECEIVER_OFF 0x14
+
+#define DIR_IN 0xdc
+#define DIR_OUT 0xcd
+
+#define MAX_PACKET_SIZE 8u
+#define TIMEOUT 1000
+
+struct packet {
+ uint16_t start;
+ uint8_t direction;
+ uint8_t cmd;
+};
+
+struct response_packet {
+ struct packet header;
+ uint8_t data[4];
+};
+
+struct send_packet {
+ struct packet header;
+ uint8_t length;
+ uint8_t channels;
+ uint8_t busy7;
+ uint8_t busy4;
+ uint8_t payload[0];
+};
+
+static void process_ir_data(struct iguanair *ir, unsigned len)
+{
+ if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
+ switch (ir->buf_in[3]) {
+ case CMD_TX_OVERFLOW:
+ ir->tx_overflow = true;
+ case CMD_RECEIVER_OFF:
+ case CMD_RECEIVER_ON:
+ case CMD_SEND:
+ complete(&ir->completion);
+ break;
+ case CMD_RX_OVERFLOW:
+ dev_warn(ir->dev, "receive overflow\n");
+ break;
+ default:
+ dev_warn(ir->dev, "control code %02x received\n",
+ ir->buf_in[3]);
+ break;
+ }
+ } else if (len >= 7) {
+ DEFINE_IR_RAW_EVENT(rawir);
+ unsigned i;
+
+ init_ir_raw_event(&rawir);
+
+ for (i = 0; i < 7; i++) {
+ if (ir->buf_in[i] == 0x80) {
+ rawir.pulse = false;
+ rawir.duration = US_TO_NS(21845);
+ } else {
+ rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
+ rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
+ 21330;
+ }
+
+ ir_raw_event_store_with_filter(ir->rc, &rawir);
+ }
+
+ ir_raw_event_handle(ir->rc);
+ }
+}
+
+static void iguanair_rx(struct urb *urb)
+{
+ struct iguanair *ir;
+
+ if (!urb)
+ return;
+
+ ir = urb->context;
+ if (!ir) {
+ usb_unlink_urb(urb);
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ process_ir_data(ir, urb->actual_length);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+ case -EPIPE:
+ default:
+ dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
+ break;
+ }
+
+ usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int iguanair_send(struct iguanair *ir, void *data, unsigned size,
+ struct response_packet *response, unsigned *res_len)
+{
+ unsigned offset, len;
+ int rc, transferred;
+
+ for (offset = 0; offset < size; offset += MAX_PACKET_SIZE) {
+ len = min(size - offset, MAX_PACKET_SIZE);
+
+ if (ir->tx_overflow)
+ return -EOVERFLOW;
+
+ rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data + offset,
+ len, &transferred, TIMEOUT);
+ if (rc)
+ return rc;
+
+ if (transferred != len)
+ return -EIO;
+ }
+
+ if (response) {
+ rc = usb_interrupt_msg(ir->udev, ir->pipe_in, response,
+ sizeof(*response), res_len, TIMEOUT);
+ }
+
+ return rc;
+}
+
+static int iguanair_get_features(struct iguanair *ir)
+{
+ struct packet packet;
+ struct response_packet response;
+ int rc, len;
+
+ packet.start = 0;
+ packet.direction = DIR_OUT;
+ packet.cmd = CMD_GET_VERSION;
+
+ rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ if (rc) {
+ dev_info(ir->dev, "failed to get version\n");
+ goto out;
+ }
+
+ if (len != 6) {
+ dev_info(ir->dev, "failed to get version\n");
+ rc = -EIO;
+ goto out;
+ }
+
+ ir->version[0] = response.data[0];
+ ir->version[1] = response.data[1];
+ ir->bufsize = 150;
+ ir->cycle_overhead = 65;
+
+ packet.cmd = CMD_GET_BUFSIZE;
+
+ rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ if (rc) {
+ dev_info(ir->dev, "failed to get buffer size\n");
+ goto out;
+ }
+
+ if (len != 5) {
+ dev_info(ir->dev, "failed to get buffer size\n");
+ rc = -EIO;
+ goto out;
+ }
+
+ ir->bufsize = response.data[0];
+
+ if (ir->version[0] == 0 || ir->version[1] == 0)
+ goto out;
+
+ packet.cmd = CMD_GET_FEATURES;
+
+ rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ if (rc) {
+ dev_info(ir->dev, "failed to get features\n");
+ goto out;
+ }
+
+ if (len < 5) {
+ dev_info(ir->dev, "failed to get features\n");
+ rc = -EIO;
+ goto out;
+ }
+
+ if (len > 5 && ir->version[0] >= 4)
+ ir->cycle_overhead = response.data[1];
+
+out:
+ return rc;
+}
+
+static int iguanair_receiver(struct iguanair *ir, bool enable)
+{
+ struct packet packet = { 0, DIR_OUT, enable ?
+ CMD_RECEIVER_ON : CMD_RECEIVER_OFF };
+ int rc;
+
+ INIT_COMPLETION(ir->completion);
+
+ rc = iguanair_send(ir, &packet, sizeof(packet), NULL, NULL);
+ if (rc)
+ return rc;
+
+ wait_for_completion_timeout(&ir->completion, TIMEOUT);
+
+ return 0;
+}
+
+/*
+ * The iguana ir creates the carrier by busy spinning after each pulse or
+ * space. This is counted in CPU cycles, with the CPU running at 24MHz. It is
+ * broken down into 7-cycles and 4-cyles delays, with a preference for
+ * 4-cycle delays.
+ */
+static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
+{
+ struct iguanair *ir = dev->priv;
+
+ if (carrier < 25000 || carrier > 150000)
+ return -EINVAL;
+
+ mutex_lock(&ir->lock);
+
+ if (carrier != ir->carrier) {
+ uint32_t cycles, fours, sevens;
+
+ ir->carrier = carrier;
+
+ cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) -
+ ir->cycle_overhead;
+
+ /* make up the the remainer of 4-cycle blocks */
+ switch (cycles & 3) {
+ case 0:
+ sevens = 0;
+ break;
+ case 1:
+ sevens = 3;
+ break;
+ case 2:
+ sevens = 2;
+ break;
+ case 3:
+ sevens = 1;
+ break;
+ }
+
+ fours = (cycles - sevens * 7) / 4;
+
+ /* magic happens here */
+ ir->busy7 = (4 - sevens) * 2;
+ ir->busy4 = 110 - fours;
+ }
+
+ mutex_unlock(&ir->lock);
+
+ return carrier;
+}
+
+static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
+{
+ struct iguanair *ir = dev->priv;
+
+ if (mask > 15)
+ return 4;
+
+ mutex_lock(&ir->lock);
+ ir->channels = mask;
+ mutex_unlock(&ir->lock);
+
+ return 0;
+}
+
+static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
+{
+ struct iguanair *ir = dev->priv;
+ uint8_t space, *payload;
+ unsigned i, size, rc;
+ struct send_packet *packet;
+
+ mutex_lock(&ir->lock);
+
+ /* convert from us to carrier periods */
+ for (i = size = 0; i < count; i++) {
+ txbuf[i] = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
+ size += (txbuf[i] + 126) / 127;
+ }
+
+ packet = kmalloc(sizeof(*packet) + size, GFP_KERNEL);
+ if (!packet) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (size > ir->bufsize) {
+ rc = -E2BIG;
+ goto out;
+ }
+
+ packet->header.start = 0;
+ packet->header.direction = DIR_OUT;
+ packet->header.cmd = CMD_SEND;
+ packet->length = size;
+ packet->channels = ir->channels << 4;
+ packet->busy7 = ir->busy7;
+ packet->busy4 = ir->busy4;
+
+ space = 0;
+ payload = packet->payload;
+
+ for (i = 0; i < count; i++) {
+ unsigned periods = txbuf[i];
+
+ while (periods > 127) {
+ *payload++ = 127 | space;
+ periods -= 127;
+ }
+
+ *payload++ = periods | space;
+ space ^= 0x80;
+ }
+
+ if (ir->receiver_on) {
+ rc = iguanair_receiver(ir, false);
+ if (rc) {
+ dev_warn(ir->dev, "disable receiver before transmit failed\n");
+ goto out;
+ }
+ }
+
+ ir->tx_overflow = false;
+
+ INIT_COMPLETION(ir->completion);
+
+ rc = iguanair_send(ir, packet, size + 8, NULL, NULL);
+
+ if (rc == 0) {
+ wait_for_completion_timeout(&ir->completion, TIMEOUT);
+ if (ir->tx_overflow)
+ rc = -EOVERFLOW;
+ }
+
+ ir->tx_overflow = false;
+
+ if (ir->receiver_on) {
+ if (iguanair_receiver(ir, true))
+ dev_warn(ir->dev, "re-enable receiver after transmit failed\n");
+ }
+
+out:
+ mutex_unlock(&ir->lock);
+ kfree(packet);
+
+ return rc;
+}
+
+static int iguanair_open(struct rc_dev *rdev)
+{
+ struct iguanair *ir = rdev->priv;
+ int rc;
+
+ mutex_lock(&ir->lock);
+
+ usb_submit_urb(ir->urb_in, GFP_KERNEL);
+
+ BUG_ON(ir->receiver_on);
+
+ rc = iguanair_receiver(ir, true);
+ if (rc == 0)
+ ir->receiver_on = true;
+
+ mutex_unlock(&ir->lock);
+
+ return rc;
+}
+
+static void iguanair_close(struct rc_dev *rdev)
+{
+ struct iguanair *ir = rdev->priv;
+ int rc;
+
+ mutex_lock(&ir->lock);
+
+ rc = iguanair_receiver(ir, false);
+ ir->receiver_on = false;
+ if (rc)
+ dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
+
+ usb_kill_urb(ir->urb_in);
+
+ mutex_unlock(&ir->lock);
+}
+
+static int __devinit iguanair_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct iguanair *ir;
+ struct rc_dev *rc;
+ int ret;
+ struct usb_host_interface *idesc;
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ rc = rc_allocate_device();
+ if (!ir || !rc) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_ATOMIC,
+ &ir->dma_in);
+ ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!ir->buf_in || !ir->urb_in) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ idesc = intf->altsetting;
+
+ if (idesc->desc.bNumEndpoints < 2) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ ir->rc = rc;
+ ir->dev = &intf->dev;
+ ir->udev = udev;
+ ir->pipe_in = usb_rcvintpipe(udev,
+ idesc->endpoint[0].desc.bEndpointAddress);
+ ir->pipe_out = usb_sndintpipe(udev,
+ idesc->endpoint[1].desc.bEndpointAddress);
+ mutex_init(&ir->lock);
+ init_completion(&ir->completion);
+
+ ret = iguanair_get_features(ir);
+ if (ret) {
+ dev_warn(&intf->dev, "failed to get device features");
+ goto out;
+ }
+
+ usb_fill_int_urb(ir->urb_in, ir->udev, ir->pipe_in, ir->buf_in,
+ MAX_PACKET_SIZE, iguanair_rx, ir,
+ idesc->endpoint[0].desc.bInterval);
+ ir->urb_in->transfer_dma = ir->dma_in;
+ ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ snprintf(ir->name, sizeof(ir->name),
+ "IguanaWorks USB IR Transceiver version %d.%d",
+ ir->version[0], ir->version[1]);
+
+ usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));
+
+ rc->input_name = ir->name;
+ rc->input_phys = ir->phys;
+ usb_to_input_id(ir->udev, &rc->input_id);
+ rc->dev.parent = &intf->dev;
+ rc->driver_type = RC_DRIVER_IR_RAW;
+ rc->allowed_protos = RC_TYPE_ALL;
+ rc->priv = ir;
+ rc->open = iguanair_open;
+ rc->close = iguanair_close;
+ rc->s_tx_mask = iguanair_set_tx_mask;
+ rc->s_tx_carrier = iguanair_set_tx_carrier;
+ rc->tx_ir = iguanair_tx;
+ rc->driver_name = DRIVER_NAME;
+ rc->map_name = RC_MAP_EMPTY;
+
+ iguanair_set_tx_carrier(rc, 38000);
+
+ ret = rc_register_device(rc);
+ if (ret < 0) {
+ dev_err(&intf->dev, "failed to register rc device %d", ret);
+ goto out;
+ }
+
+ usb_set_intfdata(intf, ir);
+
+ dev_info(&intf->dev, "Registered %s", ir->name);
+
+ return 0;
+out:
+ if (ir) {
+ usb_free_urb(ir->urb_in);
+ usb_free_coherent(udev, MAX_PACKET_SIZE, ir->buf_in,
+ ir->dma_in);
+ }
+ rc_free_device(rc);
+ kfree(ir);
+ return ret;
+}
+
+static void __devexit iguanair_disconnect(struct usb_interface *intf)
+{
+ struct iguanair *ir = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+
+ usb_kill_urb(ir->urb_in);
+ usb_free_urb(ir->urb_in);
+ usb_free_coherent(ir->udev, MAX_PACKET_SIZE, ir->buf_in, ir->dma_in);
+ rc_unregister_device(ir->rc);
+ kfree(ir);
+}
+
+static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct iguanair *ir = usb_get_intfdata(intf);
+ int rc = 0;
+
+ mutex_lock(&ir->lock);
+
+ if (ir->receiver_on) {
+ rc = iguanair_receiver(ir, false);
+ if (rc)
+ dev_warn(ir->dev, "failed to disable receiver for suspend\n");
+ }
+
+ mutex_unlock(&ir->lock);
+
+ return rc;
+}
+
+static int iguanair_resume(struct usb_interface *intf)
+{
+ struct iguanair *ir = usb_get_intfdata(intf);
+ int rc = 0;
+
+ mutex_lock(&ir->lock);
+
+ if (ir->receiver_on) {
+ rc = iguanair_receiver(ir, true);
+ if (rc)
+ dev_warn(ir->dev, "failed to enable receiver after resume\n");
+ }
+
+ mutex_unlock(&ir->lock);
+
+ return rc;
+}
+
+static const struct usb_device_id iguanair_table[] = {
+ { USB_DEVICE(0x1781, 0x0938) },
+ { }
+};
+
+static struct usb_driver iguanair_driver = {
+ .name = DRIVER_NAME,
+ .probe = iguanair_probe,
+ .disconnect = __devexit_p(iguanair_disconnect),
+ .suspend = iguanair_suspend,
+ .resume = iguanair_resume,
+ .reset_resume = iguanair_resume,
+ .id_table = iguanair_table
+};
+
+module_usb_driver(iguanair_driver);
+
+MODULE_DESCRIPTION("IguanaWorks USB IR Transceiver");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, iguanair_table);
+

View File

@ -0,0 +1,313 @@
From: Sean Young <sean@mess.org>
Date: Mon, 13 Aug 2012 08:59:39 -0300
Subject: [02/11] [media] iguanair: reuse existing urb callback for command
responses
commit e99a7cfe93fd9b853d80e7dda8b86ecca71c22bb upstream.
Rather than using usb_interrupt_msg() to receive responses, reuse the
urb callback we already have in place.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
drivers/media/rc/iguanair.c | 147 +++++++++++++++++--------------------------
1 file changed, 56 insertions(+), 91 deletions(-)
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index 5e2eaf8..bdd526d 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -35,7 +35,7 @@ struct iguanair {
struct device *dev;
struct usb_device *udev;
- int pipe_in, pipe_out;
+ int pipe_out;
uint8_t bufsize;
uint8_t version[2];
@@ -82,11 +82,6 @@ struct packet {
uint8_t cmd;
};
-struct response_packet {
- struct packet header;
- uint8_t data[4];
-};
-
struct send_packet {
struct packet header;
uint8_t length;
@@ -100,6 +95,26 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
{
if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
switch (ir->buf_in[3]) {
+ case CMD_GET_VERSION:
+ if (len == 6) {
+ ir->version[0] = ir->buf_in[4];
+ ir->version[1] = ir->buf_in[5];
+ complete(&ir->completion);
+ }
+ break;
+ case CMD_GET_BUFSIZE:
+ if (len >= 5) {
+ ir->bufsize = ir->buf_in[4];
+ complete(&ir->completion);
+ }
+ break;
+ case CMD_GET_FEATURES:
+ if (len > 5) {
+ if (ir->version[0] >= 4)
+ ir->cycle_overhead = ir->buf_in[5];
+ complete(&ir->completion);
+ }
+ break;
case CMD_TX_OVERFLOW:
ir->tx_overflow = true;
case CMD_RECEIVER_OFF:
@@ -169,31 +184,22 @@ static void iguanair_rx(struct urb *urb)
usb_submit_urb(urb, GFP_ATOMIC);
}
-static int iguanair_send(struct iguanair *ir, void *data, unsigned size,
- struct response_packet *response, unsigned *res_len)
+static int iguanair_send(struct iguanair *ir, void *data, unsigned size)
{
- unsigned offset, len;
int rc, transferred;
- for (offset = 0; offset < size; offset += MAX_PACKET_SIZE) {
- len = min(size - offset, MAX_PACKET_SIZE);
-
- if (ir->tx_overflow)
- return -EOVERFLOW;
+ INIT_COMPLETION(ir->completion);
- rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data + offset,
- len, &transferred, TIMEOUT);
- if (rc)
- return rc;
+ rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data, size,
+ &transferred, TIMEOUT);
+ if (rc)
+ return rc;
- if (transferred != len)
- return -EIO;
- }
+ if (transferred != size)
+ return -EIO;
- if (response) {
- rc = usb_interrupt_msg(ir->udev, ir->pipe_in, response,
- sizeof(*response), res_len, TIMEOUT);
- }
+ if (wait_for_completion_timeout(&ir->completion, TIMEOUT) == 0)
+ return -ETIMEDOUT;
return rc;
}
@@ -201,66 +207,40 @@ static int iguanair_send(struct iguanair *ir, void *data, unsigned size,
static int iguanair_get_features(struct iguanair *ir)
{
struct packet packet;
- struct response_packet response;
- int rc, len;
+ int rc;
packet.start = 0;
packet.direction = DIR_OUT;
packet.cmd = CMD_GET_VERSION;
- rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ rc = iguanair_send(ir, &packet, sizeof(packet));
if (rc) {
dev_info(ir->dev, "failed to get version\n");
goto out;
}
- if (len != 6) {
- dev_info(ir->dev, "failed to get version\n");
- rc = -EIO;
- goto out;
- }
-
- ir->version[0] = response.data[0];
- ir->version[1] = response.data[1];
ir->bufsize = 150;
ir->cycle_overhead = 65;
packet.cmd = CMD_GET_BUFSIZE;
- rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ rc = iguanair_send(ir, &packet, sizeof(packet));
if (rc) {
dev_info(ir->dev, "failed to get buffer size\n");
goto out;
}
- if (len != 5) {
- dev_info(ir->dev, "failed to get buffer size\n");
- rc = -EIO;
- goto out;
- }
-
- ir->bufsize = response.data[0];
-
if (ir->version[0] == 0 || ir->version[1] == 0)
goto out;
packet.cmd = CMD_GET_FEATURES;
- rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ rc = iguanair_send(ir, &packet, sizeof(packet));
if (rc) {
dev_info(ir->dev, "failed to get features\n");
goto out;
}
- if (len < 5) {
- dev_info(ir->dev, "failed to get features\n");
- rc = -EIO;
- goto out;
- }
-
- if (len > 5 && ir->version[0] >= 4)
- ir->cycle_overhead = response.data[1];
-
out:
return rc;
}
@@ -269,17 +249,8 @@ static int iguanair_receiver(struct iguanair *ir, bool enable)
{
struct packet packet = { 0, DIR_OUT, enable ?
CMD_RECEIVER_ON : CMD_RECEIVER_OFF };
- int rc;
-
- INIT_COMPLETION(ir->completion);
-
- rc = iguanair_send(ir, &packet, sizeof(packet), NULL, NULL);
- if (rc)
- return rc;
-
- wait_for_completion_timeout(&ir->completion, TIMEOUT);
- return 0;
+ return iguanair_send(ir, &packet, sizeof(packet));
}
/*
@@ -406,17 +377,10 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
ir->tx_overflow = false;
- INIT_COMPLETION(ir->completion);
-
- rc = iguanair_send(ir, packet, size + 8, NULL, NULL);
+ rc = iguanair_send(ir, packet, size + 8);
- if (rc == 0) {
- wait_for_completion_timeout(&ir->completion, TIMEOUT);
- if (ir->tx_overflow)
- rc = -EOVERFLOW;
- }
-
- ir->tx_overflow = false;
+ if (rc == 0 && ir->tx_overflow)
+ rc = -EOVERFLOW;
if (ir->receiver_on) {
if (iguanair_receiver(ir, true))
@@ -437,8 +401,6 @@ static int iguanair_open(struct rc_dev *rdev)
mutex_lock(&ir->lock);
- usb_submit_urb(ir->urb_in, GFP_KERNEL);
-
BUG_ON(ir->receiver_on);
rc = iguanair_receiver(ir, true);
@@ -462,8 +424,6 @@ static void iguanair_close(struct rc_dev *rdev)
if (rc)
dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
- usb_kill_urb(ir->urb_in);
-
mutex_unlock(&ir->lock);
}
@@ -473,7 +433,7 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
struct usb_device *udev = interface_to_usbdev(intf);
struct iguanair *ir;
struct rc_dev *rc;
- int ret;
+ int ret, pipein;
struct usb_host_interface *idesc;
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
@@ -483,7 +443,7 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
goto out;
}
- ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_ATOMIC,
+ ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_KERNEL,
&ir->dma_in);
ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
@@ -502,25 +462,28 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
ir->rc = rc;
ir->dev = &intf->dev;
ir->udev = udev;
- ir->pipe_in = usb_rcvintpipe(udev,
- idesc->endpoint[0].desc.bEndpointAddress);
ir->pipe_out = usb_sndintpipe(udev,
idesc->endpoint[1].desc.bEndpointAddress);
mutex_init(&ir->lock);
init_completion(&ir->completion);
- ret = iguanair_get_features(ir);
- if (ret) {
- dev_warn(&intf->dev, "failed to get device features");
- goto out;
- }
-
- usb_fill_int_urb(ir->urb_in, ir->udev, ir->pipe_in, ir->buf_in,
+ pipein = usb_rcvintpipe(udev, idesc->endpoint[0].desc.bEndpointAddress);
+ usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in,
MAX_PACKET_SIZE, iguanair_rx, ir,
idesc->endpoint[0].desc.bInterval);
ir->urb_in->transfer_dma = ir->dma_in;
ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ ret = usb_submit_urb(ir->urb_in, GFP_KERNEL);
+ if (ret) {
+ dev_warn(&intf->dev, "failed to submit urb: %d\n", ret);
+ goto out;
+ }
+
+ ret = iguanair_get_features(ir);
+ if (ret)
+ goto out2;
+
snprintf(ir->name, sizeof(ir->name),
"IguanaWorks USB IR Transceiver version %d.%d",
ir->version[0], ir->version[1]);
@@ -547,7 +510,7 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
ret = rc_register_device(rc);
if (ret < 0) {
dev_err(&intf->dev, "failed to register rc device %d", ret);
- goto out;
+ goto out2;
}
usb_set_intfdata(intf, ir);
@@ -555,6 +518,8 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
dev_info(&intf->dev, "Registered %s", ir->name);
return 0;
+out2:
+ usb_kill_urb(ir->urb_in);
out:
if (ir) {
usb_free_urb(ir->urb_in);

View File

@ -0,0 +1,103 @@
From: Sean Young <sean@mess.org>
Date: Mon, 13 Aug 2012 08:59:40 -0300
Subject: [03/11] [media] iguanair: ignore unsupported firmware versions
commit 0797b4802b57ce88e9361c8d64b0980508370649 upstream.
Firmware versions lower than 0x0205 use a different interface which is not
supported. Also report the firmware version in the standard format.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
drivers/media/rc/Kconfig | 8 ++++++--
drivers/media/rc/iguanair.c | 21 +++++++++++----------
2 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 5180390..2e91e66 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -264,8 +264,12 @@ config IR_IGUANA
depends on RC_CORE
select USB
---help---
- Say Y here if you want to use the IgaunaWorks USB IR Transceiver.
- Both infrared receive and send are supported.
+ Say Y here if you want to use the IguanaWorks USB IR Transceiver.
+ Both infrared receive and send are supported. If you want to
+ change the ID or the pin config, use the user space driver from
+ IguanaWorks.
+
+ Only firmware 0x0205 and later is supported.
To compile this driver as a module, choose M here: the module will
be called iguanair.
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index bdd526d..5885400 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -36,8 +36,8 @@ struct iguanair {
struct usb_device *udev;
int pipe_out;
+ uint16_t version;
uint8_t bufsize;
- uint8_t version[2];
struct mutex lock;
@@ -97,8 +97,8 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
switch (ir->buf_in[3]) {
case CMD_GET_VERSION:
if (len == 6) {
- ir->version[0] = ir->buf_in[4];
- ir->version[1] = ir->buf_in[5];
+ ir->version = (ir->buf_in[5] << 8) |
+ ir->buf_in[4];
complete(&ir->completion);
}
break;
@@ -110,8 +110,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
break;
case CMD_GET_FEATURES:
if (len > 5) {
- if (ir->version[0] >= 4)
- ir->cycle_overhead = ir->buf_in[5];
+ ir->cycle_overhead = ir->buf_in[5];
complete(&ir->completion);
}
break;
@@ -219,6 +218,12 @@ static int iguanair_get_features(struct iguanair *ir)
goto out;
}
+ if (ir->version < 0x205) {
+ dev_err(ir->dev, "firmware 0x%04x is too old\n", ir->version);
+ rc = -ENODEV;
+ goto out;
+ }
+
ir->bufsize = 150;
ir->cycle_overhead = 65;
@@ -230,9 +235,6 @@ static int iguanair_get_features(struct iguanair *ir)
goto out;
}
- if (ir->version[0] == 0 || ir->version[1] == 0)
- goto out;
-
packet.cmd = CMD_GET_FEATURES;
rc = iguanair_send(ir, &packet, sizeof(packet));
@@ -485,8 +487,7 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
goto out2;
snprintf(ir->name, sizeof(ir->name),
- "IguanaWorks USB IR Transceiver version %d.%d",
- ir->version[0], ir->version[1]);
+ "IguanaWorks USB IR Transceiver version 0x%04x", ir->version);
usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));

View File

@ -0,0 +1,99 @@
From: Sean Young <sean@mess.org>
Date: Mon, 13 Aug 2012 08:59:43 -0300
Subject: [04/11] [media] iguanair: support suspend and resume
commit 7c0bd96b767da250e5aa5ee135c530d87454d9fe upstream.
Now unbind also stops the receiver.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
drivers/media/rc/iguanair.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index 5885400..f1c5846 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -155,6 +155,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
static void iguanair_rx(struct urb *urb)
{
struct iguanair *ir;
+ int rc;
if (!urb)
return;
@@ -180,7 +181,9 @@ static void iguanair_rx(struct urb *urb)
break;
}
- usb_submit_urb(urb, GFP_ATOMIC);
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (rc && rc != -ENODEV)
+ dev_warn(ir->dev, "failed to resubmit urb: %d\n", rc);
}
static int iguanair_send(struct iguanair *ir, void *data, unsigned size)
@@ -423,7 +426,7 @@ static void iguanair_close(struct rc_dev *rdev)
rc = iguanair_receiver(ir, false);
ir->receiver_on = false;
- if (rc)
+ if (rc && rc != -ENODEV)
dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
mutex_unlock(&ir->lock);
@@ -516,8 +519,6 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
usb_set_intfdata(intf, ir);
- dev_info(&intf->dev, "Registered %s", ir->name);
-
return 0;
out2:
usb_kill_urb(ir->urb_in);
@@ -536,12 +537,11 @@ static void __devexit iguanair_disconnect(struct usb_interface *intf)
{
struct iguanair *ir = usb_get_intfdata(intf);
+ rc_unregister_device(ir->rc);
usb_set_intfdata(intf, NULL);
-
usb_kill_urb(ir->urb_in);
usb_free_urb(ir->urb_in);
usb_free_coherent(ir->udev, MAX_PACKET_SIZE, ir->buf_in, ir->dma_in);
- rc_unregister_device(ir->rc);
kfree(ir);
}
@@ -558,6 +558,8 @@ static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
dev_warn(ir->dev, "failed to disable receiver for suspend\n");
}
+ usb_kill_urb(ir->urb_in);
+
mutex_unlock(&ir->lock);
return rc;
@@ -570,6 +572,10 @@ static int iguanair_resume(struct usb_interface *intf)
mutex_lock(&ir->lock);
+ rc = usb_submit_urb(ir->urb_in, GFP_KERNEL);
+ if (rc)
+ dev_warn(&intf->dev, "failed to submit urb: %d\n", rc);
+
if (ir->receiver_on) {
rc = iguanair_receiver(ir, true);
if (rc)
@@ -593,7 +599,8 @@ static struct usb_driver iguanair_driver = {
.suspend = iguanair_suspend,
.resume = iguanair_resume,
.reset_resume = iguanair_resume,
- .id_table = iguanair_table
+ .id_table = iguanair_table,
+ .soft_unbind = 1 /* we want to disable receiver on unbind */
};
module_usb_driver(iguanair_driver);

View File

@ -0,0 +1,98 @@
From: Sean Young <sean@mess.org>
Date: Mon, 13 Aug 2012 08:59:42 -0300
Subject: [05/11] [media] iguanair: fix return value for transmit
commit 884bfd08e3d4f694574e54bd1886cd8817d9b986 upstream.
Also fix error codes returned from open.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
drivers/media/rc/iguanair.c | 30 ++++++++++++++++++------------
1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index f1c5846..6817258 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -327,7 +327,7 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
{
struct iguanair *ir = dev->priv;
uint8_t space, *payload;
- unsigned i, size, rc;
+ unsigned i, size, rc, bytes;
struct send_packet *packet;
mutex_lock(&ir->lock);
@@ -335,17 +335,22 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
/* convert from us to carrier periods */
for (i = size = 0; i < count; i++) {
txbuf[i] = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
- size += (txbuf[i] + 126) / 127;
+ bytes = (txbuf[i] + 126) / 127;
+ if (size + bytes > ir->bufsize) {
+ count = i;
+ break;
+ }
+ size += bytes;
}
- packet = kmalloc(sizeof(*packet) + size, GFP_KERNEL);
- if (!packet) {
- rc = -ENOMEM;
+ if (count == 0) {
+ rc = -EINVAL;
goto out;
}
- if (size > ir->bufsize) {
- rc = -E2BIG;
+ packet = kmalloc(sizeof(*packet) + size, GFP_KERNEL);
+ if (!packet) {
+ rc = -ENOMEM;
goto out;
}
@@ -376,7 +381,7 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
rc = iguanair_receiver(ir, false);
if (rc) {
dev_warn(ir->dev, "disable receiver before transmit failed\n");
- goto out;
+ goto out_kfree;
}
}
@@ -392,11 +397,12 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
dev_warn(ir->dev, "re-enable receiver after transmit failed\n");
}
+out_kfree:
+ kfree(packet);
out:
mutex_unlock(&ir->lock);
- kfree(packet);
- return rc;
+ return rc ? rc : count;
}
static int iguanair_open(struct rc_dev *rdev)
@@ -444,7 +450,7 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
rc = rc_allocate_device();
if (!ir || !rc) {
- ret = ENOMEM;
+ ret = -ENOMEM;
goto out;
}
@@ -453,7 +459,7 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
if (!ir->buf_in || !ir->urb_in) {
- ret = ENOMEM;
+ ret = -ENOMEM;
goto out;
}

View File

@ -0,0 +1,35 @@
From: Sean Young <sean@mess.org>
Date: Mon, 13 Aug 2012 08:59:44 -0300
Subject: [06/11] [media] iguanair: reset the IR state after rx overflow or
receiver enable
commit 116e4f592a4d207031b2eaf9dacb57de587146ef upstream.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
drivers/media/rc/iguanair.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index 6817258..8de690a 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -123,6 +123,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
break;
case CMD_RX_OVERFLOW:
dev_warn(ir->dev, "receive overflow\n");
+ ir_raw_event_reset(ir->rc);
break;
default:
dev_warn(ir->dev, "control code %02x received\n",
@@ -255,6 +256,9 @@ static int iguanair_receiver(struct iguanair *ir, bool enable)
struct packet packet = { 0, DIR_OUT, enable ?
CMD_RECEIVER_ON : CMD_RECEIVER_OFF };
+ if (enable)
+ ir_raw_event_reset(ir->rc);
+
return iguanair_send(ir, &packet, sizeof(packet));
}

View File

@ -0,0 +1,48 @@
From: Sean Young <sean@mess.org>
Date: Mon, 13 Aug 2012 08:59:41 -0300
Subject: [07/11] [media] iguanair: advertise the resolution and timeout
properly
commit 2eec676cd3097bc37618e57c19b17cbeac13cdf4 upstream.
With the timeout supplied the interface can go idle. The keymap is
the same one as other drivers which do not come with a remote.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
drivers/media/rc/iguanair.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index 8de690a..9810008 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -75,6 +75,7 @@ struct iguanair {
#define MAX_PACKET_SIZE 8u
#define TIMEOUT 1000
+#define RX_RESOLUTION 21333
struct packet {
uint16_t start;
@@ -143,7 +144,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
} else {
rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
- 21330;
+ RX_RESOLUTION;
}
ir_raw_event_store_with_filter(ir->rc, &rawir);
@@ -517,7 +518,9 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
rc->s_tx_carrier = iguanair_set_tx_carrier;
rc->tx_ir = iguanair_tx;
rc->driver_name = DRIVER_NAME;
- rc->map_name = RC_MAP_EMPTY;
+ rc->map_name = RC_MAP_RC6_MCE;
+ rc->timeout = MS_TO_NS(100);
+ rc->rx_resolution = RX_RESOLUTION;
iguanair_set_tx_carrier(rc, 38000);

View File

@ -0,0 +1,36 @@
From: Sean Young <sean@mess.org>
Date: Mon, 13 Aug 2012 08:59:45 -0300
Subject: [08/11] [media] iguanair: fix receiver overflow
commit 640583836b9488c806349e3839c9d8b0f15da1fa upstream.
The Pioneer CU-700 remote causes receiver overflows if you hold down any
button. The remote does not send NEC IR repeats, it repeats the entire
NEC code after 20ms.
The iguanair hardware advertises an interval of 10 which just not enough;
with 100 URBs per second and at most 7 edges per URB, we handle at most
700 edges per second. The remote generates about 900.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
drivers/media/rc/iguanair.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index 9810008..6a09c2e 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -484,9 +484,8 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
init_completion(&ir->completion);
pipein = usb_rcvintpipe(udev, idesc->endpoint[0].desc.bEndpointAddress);
- usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in,
- MAX_PACKET_SIZE, iguanair_rx, ir,
- idesc->endpoint[0].desc.bInterval);
+ usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in, MAX_PACKET_SIZE,
+ iguanair_rx, ir, 1);
ir->urb_in->transfer_dma = ir->dma_in;
ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

View File

@ -0,0 +1,42 @@
From: Sean Young <sean@mess.org>
Date: Mon, 13 Aug 2012 08:59:47 -0300
Subject: [09/12] [media] rc: do not wake up rc thread unless there is
something to do
commit b83bfd1b0127b0963fcac39280280e365e7e04d8 upstream.
The TechnoTrend USB IR Receiver sends 125 ISO URBs per second, even when
there is no IR activity. Reduce the number of wake ups from the other
drivers too.
This saves about 0.25ms/s on a 2.4GHz Core 2 according to powertop.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
[bwh: Restricted to iguanair driver]
---
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -134,6 +134,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
} else if (len >= 7) {
DEFINE_IR_RAW_EVENT(rawir);
unsigned i;
+ bool event = false;
init_ir_raw_event(&rawir);
@@ -147,10 +148,12 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
RX_RESOLUTION;
}
- ir_raw_event_store_with_filter(ir->rc, &rawir);
+ if (ir_raw_event_store_with_filter(ir->rc, &rawir))
+ event = true;
}
- ir_raw_event_handle(ir->rc);
+ if (event)
+ ir_raw_event_handle(ir->rc);
}
}

View File

@ -0,0 +1,112 @@
From: Sean Young <sean@mess.org>
Date: Sat, 25 Aug 2012 07:01:45 -0300
Subject: [10/11] [media] iguanair: do not modify transmit buffer
commit 3920631c4b8af70893fe70df53e94adb0cd66f71 upstream.
Since commit "[media] rc-core: move timeout and checks to lirc", the
incoming buffer is used after the driver transmits.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
drivers/media/rc/iguanair.c | 51 ++++++++++++++++++-------------------------
1 file changed, 21 insertions(+), 30 deletions(-)
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index 66ba237..1e4c68a 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -334,21 +334,34 @@ static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
{
struct iguanair *ir = dev->priv;
- uint8_t space, *payload;
- unsigned i, size, rc, bytes;
+ uint8_t space;
+ unsigned i, size, periods, bytes;
+ int rc;
struct send_packet *packet;
mutex_lock(&ir->lock);
+ packet = kmalloc(sizeof(*packet) + ir->bufsize, GFP_KERNEL);
+ if (!packet) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
/* convert from us to carrier periods */
- for (i = size = 0; i < count; i++) {
- txbuf[i] = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
- bytes = (txbuf[i] + 126) / 127;
+ for (i = space = size = 0; i < count; i++) {
+ periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
+ bytes = DIV_ROUND_UP(periods, 127);
if (size + bytes > ir->bufsize) {
count = i;
break;
}
- size += bytes;
+ while (periods > 127) {
+ packet->payload[size++] = 127 | space;
+ periods -= 127;
+ }
+
+ packet->payload[size++] = periods | space;
+ space ^= 0x80;
}
if (count == 0) {
@@ -356,12 +369,6 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
goto out;
}
- packet = kmalloc(sizeof(*packet) + size, GFP_KERNEL);
- if (!packet) {
- rc = -ENOMEM;
- goto out;
- }
-
packet->header.start = 0;
packet->header.direction = DIR_OUT;
packet->header.cmd = CMD_SEND;
@@ -370,26 +377,11 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
packet->busy7 = ir->busy7;
packet->busy4 = ir->busy4;
- space = 0;
- payload = packet->payload;
-
- for (i = 0; i < count; i++) {
- unsigned periods = txbuf[i];
-
- while (periods > 127) {
- *payload++ = 127 | space;
- periods -= 127;
- }
-
- *payload++ = periods | space;
- space ^= 0x80;
- }
-
if (ir->receiver_on) {
rc = iguanair_receiver(ir, false);
if (rc) {
dev_warn(ir->dev, "disable receiver before transmit failed\n");
- goto out_kfree;
+ goto out;
}
}
@@ -405,9 +397,8 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
dev_warn(ir->dev, "re-enable receiver after transmit failed\n");
}
-out_kfree:
- kfree(packet);
out:
+ kfree(packet);
mutex_unlock(&ir->lock);
return rc ? rc : count;

View File

@ -0,0 +1,359 @@
From: Sean Young <sean@mess.org>
Date: Fri, 28 Sep 2012 04:44:29 -0300
Subject: [11/11] [media] iguanair: cannot send data from the stack
commit 48b0fa6afa7ee6a274e060564d2389ffea413762 upstream.
Note that the firmware already disables the receiver before transmit,
there is no need to do this from the driver.
Reported-by: Fengguang Wu <fengguang.wu@intel.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
drivers/media/rc/iguanair.c | 147 ++++++++++++++++++++++---------------------
1 file changed, 75 insertions(+), 72 deletions(-)
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index 1e4c68a..51d7057 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -28,6 +28,7 @@
#include <media/rc-core.h>
#define DRIVER_NAME "iguanair"
+#define BUF_SIZE 152
struct iguanair {
struct rc_dev *rc;
@@ -35,26 +36,23 @@ struct iguanair {
struct device *dev;
struct usb_device *udev;
- int pipe_out;
uint16_t version;
uint8_t bufsize;
+ uint8_t cycle_overhead;
struct mutex lock;
/* receiver support */
bool receiver_on;
- dma_addr_t dma_in;
+ dma_addr_t dma_in, dma_out;
uint8_t *buf_in;
- struct urb *urb_in;
+ struct urb *urb_in, *urb_out;
struct completion completion;
/* transmit support */
bool tx_overflow;
uint32_t carrier;
- uint8_t cycle_overhead;
- uint8_t channels;
- uint8_t busy4;
- uint8_t busy7;
+ struct send_packet *packet;
char name[64];
char phys[64];
@@ -73,7 +71,8 @@ struct iguanair {
#define DIR_IN 0xdc
#define DIR_OUT 0xcd
-#define MAX_PACKET_SIZE 8u
+#define MAX_IN_PACKET 8u
+#define MAX_OUT_PACKET (sizeof(struct send_packet) + BUF_SIZE)
#define TIMEOUT 1000
#define RX_RESOLUTION 21333
@@ -191,20 +190,25 @@ static void iguanair_rx(struct urb *urb)
dev_warn(ir->dev, "failed to resubmit urb: %d\n", rc);
}
-static int iguanair_send(struct iguanair *ir, void *data, unsigned size)
+static void iguanair_irq_out(struct urb *urb)
{
- int rc, transferred;
+ struct iguanair *ir = urb->context;
+
+ if (urb->status)
+ dev_dbg(ir->dev, "Error: out urb status = %d\n", urb->status);
+}
+
+static int iguanair_send(struct iguanair *ir, unsigned size)
+{
+ int rc;
INIT_COMPLETION(ir->completion);
- rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data, size,
- &transferred, TIMEOUT);
+ ir->urb_out->transfer_buffer_length = size;
+ rc = usb_submit_urb(ir->urb_out, GFP_KERNEL);
if (rc)
return rc;
- if (transferred != size)
- return -EIO;
-
if (wait_for_completion_timeout(&ir->completion, TIMEOUT) == 0)
return -ETIMEDOUT;
@@ -213,14 +217,13 @@ static int iguanair_send(struct iguanair *ir, void *data, unsigned size)
static int iguanair_get_features(struct iguanair *ir)
{
- struct packet packet;
int rc;
- packet.start = 0;
- packet.direction = DIR_OUT;
- packet.cmd = CMD_GET_VERSION;
+ ir->packet->header.start = 0;
+ ir->packet->header.direction = DIR_OUT;
+ ir->packet->header.cmd = CMD_GET_VERSION;
- rc = iguanair_send(ir, &packet, sizeof(packet));
+ rc = iguanair_send(ir, sizeof(ir->packet->header));
if (rc) {
dev_info(ir->dev, "failed to get version\n");
goto out;
@@ -235,17 +238,23 @@ static int iguanair_get_features(struct iguanair *ir)
ir->bufsize = 150;
ir->cycle_overhead = 65;
- packet.cmd = CMD_GET_BUFSIZE;
+ ir->packet->header.cmd = CMD_GET_BUFSIZE;
- rc = iguanair_send(ir, &packet, sizeof(packet));
+ rc = iguanair_send(ir, sizeof(ir->packet->header));
if (rc) {
dev_info(ir->dev, "failed to get buffer size\n");
goto out;
}
- packet.cmd = CMD_GET_FEATURES;
+ if (ir->bufsize > BUF_SIZE) {
+ dev_info(ir->dev, "buffer size %u larger than expected\n",
+ ir->bufsize);
+ ir->bufsize = BUF_SIZE;
+ }
+
+ ir->packet->header.cmd = CMD_GET_FEATURES;
- rc = iguanair_send(ir, &packet, sizeof(packet));
+ rc = iguanair_send(ir, sizeof(ir->packet->header));
if (rc) {
dev_info(ir->dev, "failed to get features\n");
goto out;
@@ -257,13 +266,18 @@ out:
static int iguanair_receiver(struct iguanair *ir, bool enable)
{
- struct packet packet = { 0, DIR_OUT, enable ?
- CMD_RECEIVER_ON : CMD_RECEIVER_OFF };
+ int rc;
+
+ ir->packet->header.start = 0;
+ ir->packet->header.direction = DIR_OUT;
+ ir->packet->header.cmd = enable ? CMD_RECEIVER_ON : CMD_RECEIVER_OFF;
if (enable)
ir_raw_event_reset(ir->rc);
- return iguanair_send(ir, &packet, sizeof(packet));
+ rc = iguanair_send(ir, sizeof(ir->packet->header));
+
+ return rc;
}
/*
@@ -308,8 +322,8 @@ static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
fours = (cycles - sevens * 7) / 4;
/* magic happens here */
- ir->busy7 = (4 - sevens) * 2;
- ir->busy4 = 110 - fours;
+ ir->packet->busy7 = (4 - sevens) * 2;
+ ir->packet->busy4 = 110 - fours;
}
mutex_unlock(&ir->lock);
@@ -325,7 +339,7 @@ static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
return 4;
mutex_lock(&ir->lock);
- ir->channels = mask;
+ ir->packet->channels = mask << 4;
mutex_unlock(&ir->lock);
return 0;
@@ -337,16 +351,9 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
uint8_t space;
unsigned i, size, periods, bytes;
int rc;
- struct send_packet *packet;
mutex_lock(&ir->lock);
- packet = kmalloc(sizeof(*packet) + ir->bufsize, GFP_KERNEL);
- if (!packet) {
- rc = -ENOMEM;
- goto out;
- }
-
/* convert from us to carrier periods */
for (i = space = size = 0; i < count; i++) {
periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
@@ -356,11 +363,11 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
break;
}
while (periods > 127) {
- packet->payload[size++] = 127 | space;
+ ir->packet->payload[size++] = 127 | space;
periods -= 127;
}
- packet->payload[size++] = periods | space;
+ ir->packet->payload[size++] = periods | space;
space ^= 0x80;
}
@@ -369,36 +376,19 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
goto out;
}
- packet->header.start = 0;
- packet->header.direction = DIR_OUT;
- packet->header.cmd = CMD_SEND;
- packet->length = size;
- packet->channels = ir->channels << 4;
- packet->busy7 = ir->busy7;
- packet->busy4 = ir->busy4;
-
- if (ir->receiver_on) {
- rc = iguanair_receiver(ir, false);
- if (rc) {
- dev_warn(ir->dev, "disable receiver before transmit failed\n");
- goto out;
- }
- }
+ ir->packet->header.start = 0;
+ ir->packet->header.direction = DIR_OUT;
+ ir->packet->header.cmd = CMD_SEND;
+ ir->packet->length = size;
ir->tx_overflow = false;
- rc = iguanair_send(ir, packet, size + 8);
+ rc = iguanair_send(ir, sizeof(*ir->packet) + size);
if (rc == 0 && ir->tx_overflow)
rc = -EOVERFLOW;
- if (ir->receiver_on) {
- if (iguanair_receiver(ir, true))
- dev_warn(ir->dev, "re-enable receiver after transmit failed\n");
- }
-
out:
- kfree(packet);
mutex_unlock(&ir->lock);
return rc ? rc : count;
@@ -411,8 +401,6 @@ static int iguanair_open(struct rc_dev *rdev)
mutex_lock(&ir->lock);
- BUG_ON(ir->receiver_on);
-
rc = iguanair_receiver(ir, true);
if (rc == 0)
ir->receiver_on = true;
@@ -443,7 +431,7 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
struct usb_device *udev = interface_to_usbdev(intf);
struct iguanair *ir;
struct rc_dev *rc;
- int ret, pipein;
+ int ret, pipein, pipeout;
struct usb_host_interface *idesc;
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
@@ -453,11 +441,14 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
goto out;
}
- ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_KERNEL,
+ ir->buf_in = usb_alloc_coherent(udev, MAX_IN_PACKET, GFP_KERNEL,
&ir->dma_in);
+ ir->packet = usb_alloc_coherent(udev, MAX_OUT_PACKET, GFP_KERNEL,
+ &ir->dma_out);
ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+ ir->urb_out = usb_alloc_urb(0, GFP_KERNEL);
- if (!ir->buf_in || !ir->urb_in) {
+ if (!ir->buf_in || !ir->packet || !ir->urb_in || !ir->urb_out) {
ret = -ENOMEM;
goto out;
}
@@ -472,13 +463,18 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
ir->rc = rc;
ir->dev = &intf->dev;
ir->udev = udev;
- ir->pipe_out = usb_sndintpipe(udev,
- idesc->endpoint[1].desc.bEndpointAddress);
mutex_init(&ir->lock);
+
init_completion(&ir->completion);
+ pipeout = usb_sndintpipe(udev,
+ idesc->endpoint[1].desc.bEndpointAddress);
+ usb_fill_int_urb(ir->urb_out, udev, pipeout, ir->packet, MAX_OUT_PACKET,
+ iguanair_irq_out, ir, 1);
+ ir->urb_out->transfer_dma = ir->dma_out;
+ ir->urb_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
pipein = usb_rcvintpipe(udev, idesc->endpoint[0].desc.bEndpointAddress);
- usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in, MAX_PACKET_SIZE,
+ usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in, MAX_IN_PACKET,
iguanair_rx, ir, 1);
ir->urb_in->transfer_dma = ir->dma_in;
ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
@@ -528,11 +524,14 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
return 0;
out2:
usb_kill_urb(ir->urb_in);
+ usb_kill_urb(ir->urb_out);
out:
if (ir) {
usb_free_urb(ir->urb_in);
- usb_free_coherent(udev, MAX_PACKET_SIZE, ir->buf_in,
- ir->dma_in);
+ usb_free_urb(ir->urb_out);
+ usb_free_coherent(udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
+ usb_free_coherent(udev, MAX_OUT_PACKET, ir->packet,
+ ir->dma_out);
}
rc_free_device(rc);
kfree(ir);
@@ -546,8 +545,11 @@ static void __devexit iguanair_disconnect(struct usb_interface *intf)
rc_unregister_device(ir->rc);
usb_set_intfdata(intf, NULL);
usb_kill_urb(ir->urb_in);
+ usb_kill_urb(ir->urb_out);
usb_free_urb(ir->urb_in);
- usb_free_coherent(ir->udev, MAX_PACKET_SIZE, ir->buf_in, ir->dma_in);
+ usb_free_urb(ir->urb_out);
+ usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
+ usb_free_coherent(ir->udev, MAX_OUT_PACKET, ir->packet, ir->dma_out);
kfree(ir);
}
@@ -565,6 +567,7 @@ static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
}
usb_kill_urb(ir->urb_in);
+ usb_kill_urb(ir->urb_out);
mutex_unlock(&ir->lock);

13
debian/patches/series vendored
View File

@ -440,3 +440,16 @@ features/all/drivercore-Generalize-module_platform_driver.patch
features/all/I2C-Add-helper-macro-for-i2c_driver-boilerplate.patch
features/all/USB-Add-helper-macro-for-usb_driver-boilerplate.patch
features/all/PCI-Add-helper-macro-for-pci_register_driver-boilerp.patch
# iguanair driver from 3.7
features/all/iguanair/0001-media-Add-support-for-the-IguanaWorks-USB-IR-Transce.patch
features/all/iguanair/0002-media-iguanair-reuse-existing-urb-callback-for-comma.patch
features/all/iguanair/0003-media-iguanair-ignore-unsupported-firmware-versions.patch
features/all/iguanair/0004-media-iguanair-support-suspend-and-resume.patch
features/all/iguanair/0005-media-iguanair-fix-return-value-for-transmit.patch
features/all/iguanair/0006-media-iguanair-reset-the-IR-state-after-rx-overflow-.patch
features/all/iguanair/0007-media-iguanair-advertise-the-resolution-and-timeout-.patch
features/all/iguanair/0008-media-iguanair-fix-receiver-overflow.patch
features/all/iguanair/0009-media-rc-do-not-wake-up-rc-thread-unless-there-is-so.patch
features/all/iguanair/0010-media-iguanair-do-not-modify-transmit-buffer.patch
features/all/iguanair/0011-media-iguanair-cannot-send-data-from-the-stack.patch