V4L/DVB: IR/mceusb: add tx callback functions and wire up
authorJarod Wilson <jarod@redhat.com>
Wed, 16 Jun 2010 20:55:52 +0000 (17:55 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 2 Aug 2010 17:58:58 +0000 (14:58 -0300)
mchehab: merged with IR/mceusb: userspace buffer copy moved out of driver

    Userspace buffer copy moved out of driver and into lirc bridge driver

[mchehab@redhat.com: merged the patch to avoid compilation errors with allyesconfig ]
Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/IR/mceusb.c

index 708a71a384431402adefedc1cd2dfe4a409cd155..cc8e2ef28b66deec00f08222fc7d476c75ca6811 100644 (file)
  * Jon Smirl, which included enhancements and simplifications to the
  * incoming IR buffer parsing routines.
  *
- * TODO:
- * - add rc-core transmit support, once available
- * - enable support for forthcoming ir-lirc-codec interface
- *
  *
  * 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
@@ -51,7 +47,6 @@
 #define DRIVER_NAME    "mceusb"
 
 #define USB_BUFLEN     32      /* USB reception buffer length */
-#define IRBUF_SIZE     256     /* IR work buffer length */
 #define USB_CTRL_MSG_SZ        2       /* Size of usb ctrl msg on gen1 hw */
 #define MCE_G1_INIT_MSGS 40    /* Init messages on gen1 hw to throw out */
 
@@ -263,14 +258,13 @@ struct mceusb_dev {
                u32 reserved:28;
        } flags;
 
-       /* handle sending (init strings) */
+       /* transmit support */
        int send_flags;
-       int carrier;
+       u32 carrier;
+       unsigned char tx_mask;
 
        char name[128];
        char phys[64];
-
-       unsigned char tx_mask;
 };
 
 /*
@@ -520,6 +514,91 @@ static void mce_sync_in(struct mceusb_dev *ir, unsigned char *data, int size)
        mce_request_packet(ir, ir->usb_ep_in, data, size, MCEUSB_RX);
 }
 
+/* Send data out the IR blaster port(s) */
+static int mceusb_tx_ir(void *priv, int *txbuf, u32 n)
+{
+       struct mceusb_dev *ir = priv;
+       int i, ret = 0;
+       int count, cmdcount = 0;
+       unsigned char *cmdbuf; /* MCE command buffer */
+       long signal_duration = 0; /* Singnal length in us */
+       struct timeval start_time, end_time;
+
+       do_gettimeofday(&start_time);
+
+       count = n / sizeof(int);
+
+       cmdbuf = kzalloc(sizeof(int) * MCE_CMDBUF_SIZE, GFP_KERNEL);
+       if (!cmdbuf)
+               return -ENOMEM;
+
+       /* MCE tx init header */
+       cmdbuf[cmdcount++] = MCE_CONTROL_HEADER;
+       cmdbuf[cmdcount++] = 0x08;
+       cmdbuf[cmdcount++] = ir->tx_mask;
+
+       /* Generate mce packet data */
+       for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
+               signal_duration += txbuf[i];
+               txbuf[i] = txbuf[i] / MCE_TIME_UNIT;
+
+               do { /* loop to support long pulses/spaces > 127*50us=6.35ms */
+
+                       /* Insert mce packet header every 4th entry */
+                       if ((cmdcount < MCE_CMDBUF_SIZE) &&
+                           (cmdcount - MCE_TX_HEADER_LENGTH) %
+                            MCE_CODE_LENGTH == 0)
+                               cmdbuf[cmdcount++] = MCE_PACKET_HEADER;
+
+                       /* Insert mce packet data */
+                       if (cmdcount < MCE_CMDBUF_SIZE)
+                               cmdbuf[cmdcount++] =
+                                       (txbuf[i] < MCE_PULSE_BIT ?
+                                        txbuf[i] : MCE_MAX_PULSE_LENGTH) |
+                                        (i & 1 ? 0x00 : MCE_PULSE_BIT);
+                       else {
+                               ret = -EINVAL;
+                               goto out;
+                       }
+
+               } while ((txbuf[i] > MCE_MAX_PULSE_LENGTH) &&
+                        (txbuf[i] -= MCE_MAX_PULSE_LENGTH));
+       }
+
+       /* Fix packet length in last header */
+       cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] =
+               0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1;
+
+       /* Check if we have room for the empty packet at the end */
+       if (cmdcount >= MCE_CMDBUF_SIZE) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* All mce commands end with an empty packet (0x80) */
+       cmdbuf[cmdcount++] = 0x80;
+
+       /* Transmit the command to the mce device */
+       mce_async_out(ir, cmdbuf, cmdcount);
+
+       /*
+        * The lircd gap calculation expects the write function to
+        * wait the time it takes for the ircommand to be sent before
+        * it returns.
+        */
+       do_gettimeofday(&end_time);
+       signal_duration -= (end_time.tv_usec - start_time.tv_usec) +
+                          (end_time.tv_sec - start_time.tv_sec) * 1000000;
+
+       /* delay with the closest number of ticks */
+       set_current_state(TASK_INTERRUPTIBLE);
+       schedule_timeout(usecs_to_jiffies(signal_duration));
+
+out:
+       kfree(cmdbuf);
+       return ret ? ret : n;
+}
+
 /* Sets active IR outputs -- mce devices typically (all?) have two */
 static int mceusb_set_tx_mask(void *priv, u32 mask)
 {
@@ -533,6 +612,49 @@ static int mceusb_set_tx_mask(void *priv, u32 mask)
        return 0;
 }
 
+/* Sets the send carrier frequency and mode */
+static int mceusb_set_tx_carrier(void *priv, u32 carrier)
+{
+       struct mceusb_dev *ir = priv;
+       int clk = 10000000;
+       int prescaler = 0, divisor = 0;
+       unsigned char cmdbuf[4] = { 0x9f, 0x06, 0x00, 0x00 };
+
+       /* Carrier has changed */
+       if (ir->carrier != carrier) {
+
+               if (carrier == 0) {
+                       ir->carrier = carrier;
+                       cmdbuf[2] = 0x01;
+                       cmdbuf[3] = 0x80;
+                       dev_dbg(ir->dev, "%s: disabling carrier "
+                               "modulation\n", __func__);
+                       mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+                       return carrier;
+               }
+
+               for (prescaler = 0; prescaler < 4; ++prescaler) {
+                       divisor = (clk >> (2 * prescaler)) / carrier;
+                       if (divisor <= 0xFF) {
+                               ir->carrier = carrier;
+                               cmdbuf[2] = prescaler;
+                               cmdbuf[3] = divisor;
+                               dev_dbg(ir->dev, "%s: requesting %u HZ "
+                                       "carrier\n", __func__, carrier);
+
+                               /* Transmit new carrier to mce device */
+                               mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+                               return carrier;
+                       }
+               }
+
+               return -EINVAL;
+
+       }
+
+       return carrier;
+}
+
 static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
 {
        struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
@@ -724,6 +846,9 @@ static void mceusb_gen2_init(struct mceusb_dev *ir)
        mce_sync_in(ir, NULL, maxp);
        mce_sync_in(ir, NULL, maxp);
 
+       set_current_state(TASK_INTERRUPTIBLE);
+       schedule_timeout(msecs_to_jiffies(100));
+
        /* device reset */
        mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET));
        mce_sync_in(ir, NULL, maxp);
@@ -791,7 +916,7 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
                goto ir_dev_alloc_failed;
        }
 
-       snprintf(ir->name, sizeof(ir->name), "Media Center Edition eHome "
+       snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome "
                 "Infrared Remote Transceiver (%04x:%04x)",
                 le16_to_cpu(ir->usbdev->descriptor.idVendor),
                 le16_to_cpu(ir->usbdev->descriptor.idProduct));
@@ -804,6 +929,9 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
        props->priv = ir;
        props->driver_type = RC_DRIVER_IR_RAW;
        props->allowed_protos = IR_TYPE_ALL;
+       props->s_tx_mask = mceusb_set_tx_mask;
+       props->s_tx_carrier = mceusb_set_tx_carrier;
+       props->tx_ir = mceusb_tx_ir;
 
        ir->props = props;
        ir->irdev = irdev;