aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/rc/iguanair.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/rc/iguanair.c')
-rw-r--r--drivers/media/rc/iguanair.c85
1 files changed, 41 insertions, 44 deletions
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index b99b096d8a8..627ddfd6198 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -58,6 +58,7 @@ struct iguanair {
char phys[64];
};
+#define CMD_NOP 0x00
#define CMD_GET_VERSION 0x01
#define CMD_GET_BUFSIZE 0x11
#define CMD_GET_FEATURES 0x10
@@ -196,13 +197,17 @@ static void iguanair_irq_out(struct urb *urb)
if (urb->status)
dev_dbg(ir->dev, "Error: out urb status = %d\n", urb->status);
+
+ /* if we sent an nop packet, do not expect a response */
+ if (urb->status == 0 && ir->packet->header.cmd == CMD_NOP)
+ complete(&ir->completion);
}
static int iguanair_send(struct iguanair *ir, unsigned size)
{
int rc;
- INIT_COMPLETION(ir->completion);
+ reinit_completion(&ir->completion);
ir->urb_out->transfer_buffer_length = size;
rc = usb_submit_urb(ir->urb_out, GFP_KERNEL);
@@ -219,10 +224,17 @@ static int iguanair_get_features(struct iguanair *ir)
{
int rc;
+ /*
+ * On cold boot, the iguanair initializes on the first packet
+ * received but does not process that packet. Send an empty
+ * packet.
+ */
ir->packet->header.start = 0;
ir->packet->header.direction = DIR_OUT;
- ir->packet->header.cmd = CMD_GET_VERSION;
+ ir->packet->header.cmd = CMD_NOP;
+ iguanair_send(ir, sizeof(ir->packet->header));
+ ir->packet->header.cmd = CMD_GET_VERSION;
rc = iguanair_send(ir, sizeof(ir->packet->header));
if (rc) {
dev_info(ir->dev, "failed to get version\n");
@@ -255,19 +267,14 @@ static int iguanair_get_features(struct iguanair *ir)
ir->packet->header.cmd = CMD_GET_FEATURES;
rc = iguanair_send(ir, sizeof(ir->packet->header));
- if (rc) {
+ if (rc)
dev_info(ir->dev, "failed to get features\n");
- goto out;
- }
-
out:
return rc;
}
static int iguanair_receiver(struct iguanair *ir, bool enable)
{
- int rc;
-
ir->packet->header.start = 0;
ir->packet->header.direction = DIR_OUT;
ir->packet->header.cmd = enable ? CMD_RECEIVER_ON : CMD_RECEIVER_OFF;
@@ -275,16 +282,14 @@ static int iguanair_receiver(struct iguanair *ir, bool enable)
if (enable)
ir_raw_event_reset(ir->rc);
- rc = iguanair_send(ir, sizeof(ir->packet->header));
-
- return rc;
+ return iguanair_send(ir, sizeof(ir->packet->header));
}
/*
- * 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
+ * The iguanair creates the carrier by busy spinning after each half period.
+ * 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.
+ * 4-cycle delays, minus the overhead of the loop itself (cycle_overhead).
*/
static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
{
@@ -303,25 +308,22 @@ static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t 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;
- }
-
+ /*
+ * Calculate minimum number of 7 cycles needed so
+ * we are left with a multiple of 4; so we want to have
+ * (sevens * 7) & 3 == cycles & 3
+ */
+ sevens = (4 - cycles) & 3;
fours = (cycles - sevens * 7) / 4;
- /* magic happens here */
+ /*
+ * The firmware interprets these values as a relative offset
+ * for a branch. Immediately following the branches, there
+ * 4 instructions of 7 cycles (2 bytes each) and 110
+ * instructions of 4 cycles (1 byte each). A relative branch
+ * of 0 will execute all of them, branch further for less
+ * cycle burning.
+ */
ir->packet->busy7 = (4 - sevens) * 2;
ir->packet->busy4 = 110 - fours;
}
@@ -359,23 +361,17 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
bytes = DIV_ROUND_UP(periods, 127);
if (size + bytes > ir->bufsize) {
- count = i;
- break;
+ rc = -EINVAL;
+ goto out;
}
- while (periods > 127) {
- ir->packet->payload[size++] = 127 | space;
- periods -= 127;
+ while (periods) {
+ unsigned p = min(periods, 127u);
+ ir->packet->payload[size++] = p | space;
+ periods -= p;
}
-
- ir->packet->payload[size++] = periods | space;
space ^= 0x80;
}
- if (count == 0) {
- rc = -EINVAL;
- goto out;
- }
-
ir->packet->header.start = 0;
ir->packet->header.direction = DIR_OUT;
ir->packet->header.cmd = CMD_SEND;
@@ -499,7 +495,7 @@ static int iguanair_probe(struct usb_interface *intf,
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_BIT_ALL;
+ rc_set_allowed_protocols(rc, RC_BIT_ALL);
rc->priv = ir;
rc->open = iguanair_open;
rc->close = iguanair_close;
@@ -512,6 +508,7 @@ static int iguanair_probe(struct usb_interface *intf,
rc->rx_resolution = RX_RESOLUTION;
iguanair_set_tx_carrier(rc, 38000);
+ iguanair_set_tx_mask(rc, 0);
ret = rc_register_device(rc);
if (ret < 0) {