diff --git a/debian/changelog b/debian/changelog index 6cc9561ed..3b6d73f50 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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 Thu, 27 Dec 2012 02:17:44 +0100 diff --git a/debian/config/config b/debian/config/config index f8e198d9a..eaca49a7d 100644 --- a/debian/config/config +++ b/debian/config/config @@ -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 ## diff --git a/debian/patches/features/all/iguanair/0001-media-Add-support-for-the-IguanaWorks-USB-IR-Transce.patch b/debian/patches/features/all/iguanair/0001-media-Add-support-for-the-IguanaWorks-USB-IR-Transce.patch new file mode 100644 index 000000000..e1c4ab13d --- /dev/null +++ b/debian/patches/features/all/iguanair/0001-media-Add-support-for-the-IguanaWorks-USB-IR-Transce.patch @@ -0,0 +1,684 @@ +From: Sean Young +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 +Signed-off-by: Mauro Carvalho Chehab +--- + 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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++MODULE_LICENSE("GPL"); ++MODULE_DEVICE_TABLE(usb, iguanair_table); ++ diff --git a/debian/patches/features/all/iguanair/0002-media-iguanair-reuse-existing-urb-callback-for-comma.patch b/debian/patches/features/all/iguanair/0002-media-iguanair-reuse-existing-urb-callback-for-comma.patch new file mode 100644 index 000000000..cf9d9d650 --- /dev/null +++ b/debian/patches/features/all/iguanair/0002-media-iguanair-reuse-existing-urb-callback-for-comma.patch @@ -0,0 +1,313 @@ +From: Sean Young +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 +Signed-off-by: Mauro Carvalho Chehab +--- + 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); diff --git a/debian/patches/features/all/iguanair/0003-media-iguanair-ignore-unsupported-firmware-versions.patch b/debian/patches/features/all/iguanair/0003-media-iguanair-ignore-unsupported-firmware-versions.patch new file mode 100644 index 000000000..0ab272037 --- /dev/null +++ b/debian/patches/features/all/iguanair/0003-media-iguanair-ignore-unsupported-firmware-versions.patch @@ -0,0 +1,103 @@ +From: Sean Young +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 +Signed-off-by: Mauro Carvalho Chehab +--- + 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)); + diff --git a/debian/patches/features/all/iguanair/0004-media-iguanair-support-suspend-and-resume.patch b/debian/patches/features/all/iguanair/0004-media-iguanair-support-suspend-and-resume.patch new file mode 100644 index 000000000..ada3ce8d8 --- /dev/null +++ b/debian/patches/features/all/iguanair/0004-media-iguanair-support-suspend-and-resume.patch @@ -0,0 +1,99 @@ +From: Sean Young +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 +Signed-off-by: Mauro Carvalho Chehab +--- + 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); diff --git a/debian/patches/features/all/iguanair/0005-media-iguanair-fix-return-value-for-transmit.patch b/debian/patches/features/all/iguanair/0005-media-iguanair-fix-return-value-for-transmit.patch new file mode 100644 index 000000000..071208f0d --- /dev/null +++ b/debian/patches/features/all/iguanair/0005-media-iguanair-fix-return-value-for-transmit.patch @@ -0,0 +1,98 @@ +From: Sean Young +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 +Signed-off-by: Mauro Carvalho Chehab +--- + 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; + } + diff --git a/debian/patches/features/all/iguanair/0006-media-iguanair-reset-the-IR-state-after-rx-overflow-.patch b/debian/patches/features/all/iguanair/0006-media-iguanair-reset-the-IR-state-after-rx-overflow-.patch new file mode 100644 index 000000000..3d3558dfe --- /dev/null +++ b/debian/patches/features/all/iguanair/0006-media-iguanair-reset-the-IR-state-after-rx-overflow-.patch @@ -0,0 +1,35 @@ +From: Sean Young +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 +Signed-off-by: Mauro Carvalho Chehab +--- + 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)); + } + diff --git a/debian/patches/features/all/iguanair/0007-media-iguanair-advertise-the-resolution-and-timeout-.patch b/debian/patches/features/all/iguanair/0007-media-iguanair-advertise-the-resolution-and-timeout-.patch new file mode 100644 index 000000000..951a3222a --- /dev/null +++ b/debian/patches/features/all/iguanair/0007-media-iguanair-advertise-the-resolution-and-timeout-.patch @@ -0,0 +1,48 @@ +From: Sean Young +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 +Signed-off-by: Mauro Carvalho Chehab +--- + 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); + diff --git a/debian/patches/features/all/iguanair/0008-media-iguanair-fix-receiver-overflow.patch b/debian/patches/features/all/iguanair/0008-media-iguanair-fix-receiver-overflow.patch new file mode 100644 index 000000000..b4618c0f4 --- /dev/null +++ b/debian/patches/features/all/iguanair/0008-media-iguanair-fix-receiver-overflow.patch @@ -0,0 +1,36 @@ +From: Sean Young +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 +Signed-off-by: Mauro Carvalho Chehab +--- + 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; + diff --git a/debian/patches/features/all/iguanair/0009-media-rc-do-not-wake-up-rc-thread-unless-there-is-so.patch b/debian/patches/features/all/iguanair/0009-media-rc-do-not-wake-up-rc-thread-unless-there-is-so.patch new file mode 100644 index 000000000..9f02c49fd --- /dev/null +++ b/debian/patches/features/all/iguanair/0009-media-rc-do-not-wake-up-rc-thread-unless-there-is-so.patch @@ -0,0 +1,42 @@ +From: Sean Young +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 +Signed-off-by: Mauro Carvalho Chehab +[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); + } + } + diff --git a/debian/patches/features/all/iguanair/0010-media-iguanair-do-not-modify-transmit-buffer.patch b/debian/patches/features/all/iguanair/0010-media-iguanair-do-not-modify-transmit-buffer.patch new file mode 100644 index 000000000..530de4238 --- /dev/null +++ b/debian/patches/features/all/iguanair/0010-media-iguanair-do-not-modify-transmit-buffer.patch @@ -0,0 +1,112 @@ +From: Sean Young +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 +Signed-off-by: Mauro Carvalho Chehab +--- + 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; diff --git a/debian/patches/features/all/iguanair/0011-media-iguanair-cannot-send-data-from-the-stack.patch b/debian/patches/features/all/iguanair/0011-media-iguanair-cannot-send-data-from-the-stack.patch new file mode 100644 index 000000000..111aee4e5 --- /dev/null +++ b/debian/patches/features/all/iguanair/0011-media-iguanair-cannot-send-data-from-the-stack.patch @@ -0,0 +1,359 @@ +From: Sean Young +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 +Signed-off-by: Sean Young +Signed-off-by: Mauro Carvalho Chehab +--- + 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 + + #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); + diff --git a/debian/patches/series b/debian/patches/series index 45e74f15f..6fcd59cc6 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -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