]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/staging/lirc/lirc_zilog.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / staging / lirc / lirc_zilog.c
index f0076eb025f1a0e9d412080caab87f627dda4970..0aad0d7a74a3789c6a416b9391e254d01843d51c 100644 (file)
@@ -20,6 +20,9 @@
  *
  * parts are cut&pasted from the lirc_i2c.c driver
  *
+ * Numerous changes updating lirc_zilog.c in kernel 2.6.38 and later are
+ * Copyright (C) 2011 Andy Walls <awalls@md.metrocast.net>
+ *
  *  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
 #include <media/lirc_dev.h>
 #include <media/lirc.h>
 
-struct IR {
-       struct lirc_driver l;
-
-       /* Device info */
-       struct mutex ir_lock;
-       int open;
-
+struct IR_rx {
        /* RX device */
-       struct i2c_client c_rx;
-       int have_rx;
+       struct i2c_client *c;
 
        /* RX device buffer & lock */
        struct lirc_buffer buf;
        struct mutex buf_lock;
 
        /* RX polling thread data */
-       struct completion *t_notify;
-       struct completion *t_notify2;
-       int shutdown;
        struct task_struct *task;
 
        /* RX read data */
        unsigned char b[3];
+       bool hdpvr_data_fmt;
+};
 
+struct IR_tx {
        /* TX device */
-       struct i2c_client c_tx;
+       struct i2c_client *c;
+
+       /* TX additional actions needed */
        int need_boot;
-       int have_tx;
+       bool post_tx_ready_poll;
+};
+
+struct IR {
+       struct lirc_driver l;
+
+       struct mutex ir_lock;
+       int open;
+
+       struct i2c_adapter *adapter;
+       struct IR_rx *rx;
+       struct IR_tx *tx;
 };
 
 /* Minor -> data mapping */
+static struct mutex ir_devices_lock;
 static struct IR *ir_devices[MAX_IRCTL_DEVICES];
 
 /* Block size for IR transmitter */
@@ -123,14 +133,11 @@ static struct mutex tx_data_lock;
 #define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \
                                        ## args)
 #define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
-
-#define ZILOG_HAUPPAUGE_IR_RX_NAME "Zilog/Hauppauge IR RX"
-#define ZILOG_HAUPPAUGE_IR_TX_NAME "Zilog/Hauppauge IR TX"
+#define zilog_info(s, args...) printk(KERN_INFO KBUILD_MODNAME ": " s, ## args)
 
 /* module parameters */
 static int debug;      /* debug output */
-static int disable_rx; /* disable RX device */
-static int disable_tx; /* disable TX device */
+static int tx_only;    /* only handle the IR Tx function */
 static int minor = -1; /* minor number */
 
 #define dprintk(fmt, args...)                                          \
@@ -149,8 +156,12 @@ static int add_to_buf(struct IR *ir)
        int ret;
        int failures = 0;
        unsigned char sendbuf[1] = { 0 };
+       struct IR_rx *rx = ir->rx;
 
-       if (lirc_buffer_full(&ir->buf)) {
+       if (rx == NULL)
+               return -ENXIO;
+
+       if (lirc_buffer_full(&rx->buf)) {
                dprintk("buffer overflow\n");
                return -EOVERFLOW;
        }
@@ -160,17 +171,25 @@ static int add_to_buf(struct IR *ir)
         * data and we have space
         */
        do {
+               if (kthread_should_stop())
+                       return -ENODATA;
+
                /*
                 * Lock i2c bus for the duration.  RX/TX chips interfere so
                 * this is worth it
                 */
                mutex_lock(&ir->ir_lock);
 
+               if (kthread_should_stop()) {
+                       mutex_unlock(&ir->ir_lock);
+                       return -ENODATA;
+               }
+
                /*
                 * Send random "poll command" (?)  Windows driver does this
                 * and it is a good point to detect chip failure.
                 */
-               ret = i2c_master_send(&ir->c_rx, sendbuf, 1);
+               ret = i2c_master_send(rx->c, sendbuf, 1);
                if (ret != 1) {
                        zilog_error("i2c_master_send failed with %d\n", ret);
                        if (failures >= 3) {
@@ -185,49 +204,53 @@ static int add_to_buf(struct IR *ir)
                                    "trying reset\n");
 
                        set_current_state(TASK_UNINTERRUPTIBLE);
+                       if (kthread_should_stop()) {
+                               mutex_unlock(&ir->ir_lock);
+                               return -ENODATA;
+                       }
                        schedule_timeout((100 * HZ + 999) / 1000);
-                       ir->need_boot = 1;
+                       ir->tx->need_boot = 1;
 
                        ++failures;
                        mutex_unlock(&ir->ir_lock);
                        continue;
                }
 
-               ret = i2c_master_recv(&ir->c_rx, keybuf, sizeof(keybuf));
+               if (kthread_should_stop()) {
+                       mutex_unlock(&ir->ir_lock);
+                       return -ENODATA;
+               }
+               ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf));
                mutex_unlock(&ir->ir_lock);
                if (ret != sizeof(keybuf)) {
                        zilog_error("i2c_master_recv failed with %d -- "
                                    "keeping last read buffer\n", ret);
                } else {
-                       ir->b[0] = keybuf[3];
-                       ir->b[1] = keybuf[4];
-                       ir->b[2] = keybuf[5];
-                       dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
+                       rx->b[0] = keybuf[3];
+                       rx->b[1] = keybuf[4];
+                       rx->b[2] = keybuf[5];
+                       dprintk("key (0x%02x/0x%02x)\n", rx->b[0], rx->b[1]);
                }
 
                /* key pressed ? */
-#ifdef I2C_HW_B_HDPVR
-               if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR) {
+               if (rx->hdpvr_data_fmt) {
                        if (got_data && (keybuf[0] == 0x80))
                                return 0;
                        else if (got_data && (keybuf[0] == 0x00))
                                return -ENODATA;
-               } else if ((ir->b[0] & 0x80) == 0)
-#else
-               if ((ir->b[0] & 0x80) == 0)
-#endif
+               } else if ((rx->b[0] & 0x80) == 0)
                        return got_data ? 0 : -ENODATA;
 
                /* look what we have */
-               code = (((__u16)ir->b[0] & 0x7f) << 6) | (ir->b[1] >> 2);
+               code = (((__u16)rx->b[0] & 0x7f) << 6) | (rx->b[1] >> 2);
 
                codes[0] = (code >> 8) & 0xff;
                codes[1] = code & 0xff;
 
                /* return it */
-               lirc_buffer_write(&ir->buf, codes);
+               lirc_buffer_write(&rx->buf, codes);
                ++got_data;
-       } while (!lirc_buffer_full(&ir->buf));
+       } while (!lirc_buffer_full(&rx->buf));
 
        return 0;
 }
@@ -245,46 +268,35 @@ static int add_to_buf(struct IR *ir)
 static int lirc_thread(void *arg)
 {
        struct IR *ir = arg;
-
-       if (ir->t_notify != NULL)
-               complete(ir->t_notify);
+       struct IR_rx *rx = ir->rx;
 
        dprintk("poll thread started\n");
 
-       do {
-               if (ir->open) {
-                       set_current_state(TASK_INTERRUPTIBLE);
+       while (!kthread_should_stop()) {
+               set_current_state(TASK_INTERRUPTIBLE);
 
-                       /*
-                        * This is ~113*2 + 24 + jitter (2*repeat gap +
-                        * code length).  We use this interval as the chip
-                        * resets every time you poll it (bad!).  This is
-                        * therefore just sufficient to catch all of the
-                        * button presses.  It makes the remote much more
-                        * responsive.  You can see the difference by
-                        * running irw and holding down a button.  With
-                        * 100ms, the old polling interval, you'll notice
-                        * breaks in the repeat sequence corresponding to
-                        * lost keypresses.
-                        */
-                       schedule_timeout((260 * HZ) / 1000);
-                       if (ir->shutdown)
-                               break;
-                       if (!add_to_buf(ir))
-                               wake_up_interruptible(&ir->buf.wait_poll);
-               } else {
-                       /* if device not opened so we can sleep half a second */
-                       set_current_state(TASK_INTERRUPTIBLE);
+               /* if device not opened, we can sleep half a second */
+               if (!ir->open) {
                        schedule_timeout(HZ/2);
+                       continue;
                }
-       } while (!ir->shutdown);
-
-       if (ir->t_notify2 != NULL)
-               wait_for_completion(ir->t_notify2);
 
-       ir->task = NULL;
-       if (ir->t_notify != NULL)
-               complete(ir->t_notify);
+               /*
+                * This is ~113*2 + 24 + jitter (2*repeat gap + code length).
+                * We use this interval as the chip resets every time you poll
+                * it (bad!).  This is therefore just sufficient to catch all
+                * of the button presses.  It makes the remote much more
+                * responsive.  You can see the difference by running irw and
+                * holding down a button.  With 100ms, the old polling
+                * interval, you'll notice breaks in the repeat sequence
+                * corresponding to lost keypresses.
+                */
+               schedule_timeout((260 * HZ) / 1000);
+               if (kthread_should_stop())
+                       break;
+               if (!add_to_buf(ir))
+                       wake_up_interruptible(&rx->buf.wait_poll);
+       }
 
        dprintk("poll thread ended\n");
        return 0;
@@ -302,10 +314,10 @@ static int set_use_inc(void *data)
         * this is completely broken code. lirc_unregister_driver()
         * must be possible even when the device is open
         */
-       if (ir->c_rx.addr)
-               i2c_use_client(&ir->c_rx);
-       if (ir->c_tx.addr)
-               i2c_use_client(&ir->c_tx);
+       if (ir->rx != NULL)
+               i2c_use_client(ir->rx->c);
+       if (ir->tx != NULL)
+               i2c_use_client(ir->tx->c);
 
        return 0;
 }
@@ -314,10 +326,10 @@ static void set_use_dec(void *data)
 {
        struct IR *ir = data;
 
-       if (ir->c_rx.addr)
-               i2c_release_client(&ir->c_rx);
-       if (ir->c_tx.addr)
-               i2c_release_client(&ir->c_tx);
+       if (ir->rx)
+               i2c_release_client(ir->rx->c);
+       if (ir->tx)
+               i2c_release_client(ir->tx->c);
        if (ir->l.owner != NULL)
                module_put(ir->l.owner);
 }
@@ -456,7 +468,7 @@ corrupt:
 }
 
 /* send a block of data to the IR TX device */
-static int send_data_block(struct IR *ir, unsigned char *data_block)
+static int send_data_block(struct IR_tx *tx, unsigned char *data_block)
 {
        int i, j, ret;
        unsigned char buf[5];
@@ -470,7 +482,7 @@ static int send_data_block(struct IR *ir, unsigned char *data_block)
                        buf[1 + j] = data_block[i + j];
                dprintk("%02x %02x %02x %02x %02x",
                        buf[0], buf[1], buf[2], buf[3], buf[4]);
-               ret = i2c_master_send(&ir->c_tx, buf, tosend + 1);
+               ret = i2c_master_send(tx->c, buf, tosend + 1);
                if (ret != tosend + 1) {
                        zilog_error("i2c_master_send failed with %d\n", ret);
                        return ret < 0 ? ret : -EFAULT;
@@ -481,38 +493,50 @@ static int send_data_block(struct IR *ir, unsigned char *data_block)
 }
 
 /* send boot data to the IR TX device */
-static int send_boot_data(struct IR *ir)
+static int send_boot_data(struct IR_tx *tx)
 {
-       int ret;
+       int ret, i;
        unsigned char buf[4];
 
        /* send the boot block */
-       ret = send_data_block(ir, tx_data->boot_data);
+       ret = send_data_block(tx, tx_data->boot_data);
        if (ret != 0)
                return ret;
 
-       /* kick it off? */
+       /* Hit the go button to activate the new boot data */
        buf[0] = 0x00;
        buf[1] = 0x20;
-       ret = i2c_master_send(&ir->c_tx, buf, 2);
+       ret = i2c_master_send(tx->c, buf, 2);
        if (ret != 2) {
                zilog_error("i2c_master_send failed with %d\n", ret);
                return ret < 0 ? ret : -EFAULT;
        }
-       ret = i2c_master_send(&ir->c_tx, buf, 1);
+
+       /*
+        * Wait for zilog to settle after hitting go post boot block upload.
+        * Without this delay, the HD-PVR and HVR-1950 both return an -EIO
+        * upon attempting to get firmware revision, and tx probe thus fails.
+        */
+       for (i = 0; i < 10; i++) {
+               ret = i2c_master_send(tx->c, buf, 1);
+               if (ret == 1)
+                       break;
+               udelay(100);
+       }
+
        if (ret != 1) {
                zilog_error("i2c_master_send failed with %d\n", ret);
                return ret < 0 ? ret : -EFAULT;
        }
 
        /* Here comes the firmware version... (hopefully) */
-       ret = i2c_master_recv(&ir->c_tx, buf, 4);
+       ret = i2c_master_recv(tx->c, buf, 4);
        if (ret != 4) {
                zilog_error("i2c_master_recv failed with %d\n", ret);
                return 0;
        }
-       if (buf[0] != 0x80) {
-               zilog_error("unexpected IR TX response: %02x\n", buf[0]);
+       if ((buf[0] != 0x80) && (buf[0] != 0xa0)) {
+               zilog_error("unexpected IR TX init response: %02x\n", buf[0]);
                return 0;
        }
        zilog_notify("Zilog/Hauppauge IR blaster firmware version "
@@ -546,7 +570,7 @@ static void fw_unload(void)
 }
 
 /* load "firmware" for the IR TX device */
-static int fw_load(struct IR *ir)
+static int fw_load(struct IR_tx *tx)
 {
        int ret;
        unsigned int i;
@@ -561,7 +585,7 @@ static int fw_load(struct IR *ir)
        }
 
        /* Request codeset data file */
-       ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &ir->c_tx.dev);
+       ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &tx->c->dev);
        if (ret != 0) {
                zilog_error("firmware haup-ir-blaster.bin not available "
                            "(%d)\n", ret);
@@ -688,20 +712,20 @@ out:
 }
 
 /* initialise the IR TX device */
-static int tx_init(struct IR *ir)
+static int tx_init(struct IR_tx *tx)
 {
        int ret;
 
        /* Load 'firmware' */
-       ret = fw_load(ir);
+       ret = fw_load(tx);
        if (ret != 0)
                return ret;
 
        /* Send boot block */
-       ret = send_boot_data(ir);
+       ret = send_boot_data(tx);
        if (ret != 0)
                return ret;
-       ir->need_boot = 0;
+       tx->need_boot = 0;
 
        /* Looks good */
        return 0;
@@ -717,20 +741,20 @@ static loff_t lseek(struct file *filep, loff_t offset, int orig)
 static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
 {
        struct IR *ir = filep->private_data;
-       unsigned char buf[ir->buf.chunk_size];
+       struct IR_rx *rx = ir->rx;
        int ret = 0, written = 0;
        DECLARE_WAITQUEUE(wait, current);
 
        dprintk("read called\n");
-       if (ir->c_rx.addr == 0)
+       if (rx == NULL)
                return -ENODEV;
 
-       if (mutex_lock_interruptible(&ir->buf_lock))
+       if (mutex_lock_interruptible(&rx->buf_lock))
                return -ERESTARTSYS;
 
-       if (n % ir->buf.chunk_size) {
+       if (n % rx->buf.chunk_size) {
                dprintk("read result = -EINVAL\n");
-               mutex_unlock(&ir->buf_lock);
+               mutex_unlock(&rx->buf_lock);
                return -EINVAL;
        }
 
@@ -739,7 +763,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
         * to avoid losing scan code (in case when queue is awaken somewhere
         * between while condition checking and scheduling)
         */
-       add_wait_queue(&ir->buf.wait_poll, &wait);
+       add_wait_queue(&rx->buf.wait_poll, &wait);
        set_current_state(TASK_INTERRUPTIBLE);
 
        /*
@@ -747,7 +771,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
         * mode and 'copy_to_user' is happy, wait for data.
         */
        while (written < n && ret == 0) {
-               if (lirc_buffer_empty(&ir->buf)) {
+               if (lirc_buffer_empty(&rx->buf)) {
                        /*
                         * According to the read(2) man page, 'written' can be
                         * returned as less than 'n', instead of blocking
@@ -767,16 +791,17 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
                        schedule();
                        set_current_state(TASK_INTERRUPTIBLE);
                } else {
-                       lirc_buffer_read(&ir->buf, buf);
+                       unsigned char buf[rx->buf.chunk_size];
+                       lirc_buffer_read(&rx->buf, buf);
                        ret = copy_to_user((void *)outbuf+written, buf,
-                                          ir->buf.chunk_size);
-                       written += ir->buf.chunk_size;
+                                          rx->buf.chunk_size);
+                       written += rx->buf.chunk_size;
                }
        }
 
-       remove_wait_queue(&ir->buf.wait_poll, &wait);
+       remove_wait_queue(&rx->buf.wait_poll, &wait);
        set_current_state(TASK_RUNNING);
-       mutex_unlock(&ir->buf_lock);
+       mutex_unlock(&rx->buf_lock);
 
        dprintk("read result = %s (%d)\n",
                ret ? "-EFAULT" : "OK", ret);
@@ -785,7 +810,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
 }
 
 /* send a keypress to the IR TX device */
-static int send_code(struct IR *ir, unsigned int code, unsigned int key)
+static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key)
 {
        unsigned char data_block[TX_BLOCK_SIZE];
        unsigned char buf[2];
@@ -802,26 +827,34 @@ static int send_code(struct IR *ir, unsigned int code, unsigned int key)
                return ret;
 
        /* Send the data block */
-       ret = send_data_block(ir, data_block);
+       ret = send_data_block(tx, data_block);
        if (ret != 0)
                return ret;
 
        /* Send data block length? */
        buf[0] = 0x00;
        buf[1] = 0x40;
-       ret = i2c_master_send(&ir->c_tx, buf, 2);
+       ret = i2c_master_send(tx->c, buf, 2);
        if (ret != 2) {
                zilog_error("i2c_master_send failed with %d\n", ret);
                return ret < 0 ? ret : -EFAULT;
        }
-       ret = i2c_master_send(&ir->c_tx, buf, 1);
+
+       /* Give the z8 a moment to process data block */
+       for (i = 0; i < 10; i++) {
+               ret = i2c_master_send(tx->c, buf, 1);
+               if (ret == 1)
+                       break;
+               udelay(100);
+       }
+
        if (ret != 1) {
                zilog_error("i2c_master_send failed with %d\n", ret);
                return ret < 0 ? ret : -EFAULT;
        }
 
        /* Send finished download? */
-       ret = i2c_master_recv(&ir->c_tx, buf, 1);
+       ret = i2c_master_recv(tx->c, buf, 1);
        if (ret != 1) {
                zilog_error("i2c_master_recv failed with %d\n", ret);
                return ret < 0 ? ret : -EFAULT;
@@ -835,21 +868,21 @@ static int send_code(struct IR *ir, unsigned int code, unsigned int key)
        /* Send prepare command? */
        buf[0] = 0x00;
        buf[1] = 0x80;
-       ret = i2c_master_send(&ir->c_tx, buf, 2);
+       ret = i2c_master_send(tx->c, buf, 2);
        if (ret != 2) {
                zilog_error("i2c_master_send failed with %d\n", ret);
                return ret < 0 ? ret : -EFAULT;
        }
 
-#ifdef I2C_HW_B_HDPVR
        /*
         * The sleep bits aren't necessary on the HD PVR, and in fact, the
         * last i2c_master_recv always fails with a -5, so for now, we're
         * going to skip this whole mess and say we're done on the HD PVR
         */
-       if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR)
-               goto done;
-#endif
+       if (!tx->post_tx_ready_poll) {
+               dprintk("sent code %u, key %u\n", code, key);
+               return 0;
+       }
 
        /*
         * This bit NAKs until the device is ready, so we retry it
@@ -860,7 +893,7 @@ static int send_code(struct IR *ir, unsigned int code, unsigned int key)
        for (i = 0; i < 20; ++i) {
                set_current_state(TASK_UNINTERRUPTIBLE);
                schedule_timeout((50 * HZ + 999) / 1000);
-               ret = i2c_master_send(&ir->c_tx, buf, 1);
+               ret = i2c_master_send(tx->c, buf, 1);
                if (ret == 1)
                        break;
                dprintk("NAK expected: i2c_master_send "
@@ -873,7 +906,7 @@ static int send_code(struct IR *ir, unsigned int code, unsigned int key)
        }
 
        /* Seems to be an 'ok' response */
-       i = i2c_master_recv(&ir->c_tx, buf, 1);
+       i = i2c_master_recv(tx->c, buf, 1);
        if (i != 1) {
                zilog_error("i2c_master_recv failed with %d\n", ret);
                return -EFAULT;
@@ -883,7 +916,6 @@ static int send_code(struct IR *ir, unsigned int code, unsigned int key)
                return -EFAULT;
        }
 
-done:
        /* Oh good, it worked */
        dprintk("sent code %u, key %u\n", code, key);
        return 0;
@@ -899,10 +931,11 @@ static ssize_t write(struct file *filep, const char *buf, size_t n,
                          loff_t *ppos)
 {
        struct IR *ir = filep->private_data;
+       struct IR_tx *tx = ir->tx;
        size_t i;
        int failures = 0;
 
-       if (ir->c_tx.addr == 0)
+       if (tx == NULL)
                return -ENODEV;
 
        /* Validate user parameters */
@@ -923,15 +956,15 @@ static ssize_t write(struct file *filep, const char *buf, size_t n,
                }
 
                /* Send boot data first if required */
-               if (ir->need_boot == 1) {
-                       ret = send_boot_data(ir);
+               if (tx->need_boot == 1) {
+                       ret = send_boot_data(tx);
                        if (ret == 0)
-                               ir->need_boot = 0;
+                               tx->need_boot = 0;
                }
 
                /* Send the code */
                if (ret == 0) {
-                       ret = send_code(ir, (unsigned)command >> 16,
+                       ret = send_code(tx, (unsigned)command >> 16,
                                            (unsigned)command & 0xFFFF);
                        if (ret == -EPROTO) {
                                mutex_unlock(&ir->ir_lock);
@@ -956,7 +989,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n,
                        }
                        set_current_state(TASK_UNINTERRUPTIBLE);
                        schedule_timeout((100 * HZ + 999) / 1000);
-                       ir->need_boot = 1;
+                       tx->need_boot = 1;
                        ++failures;
                } else
                        i += sizeof(int);
@@ -973,22 +1006,23 @@ static ssize_t write(struct file *filep, const char *buf, size_t n,
 static unsigned int poll(struct file *filep, poll_table *wait)
 {
        struct IR *ir = filep->private_data;
+       struct IR_rx *rx = ir->rx;
        unsigned int ret;
 
        dprintk("poll called\n");
-       if (ir->c_rx.addr == 0)
+       if (rx == NULL)
                return -ENODEV;
 
-       mutex_lock(&ir->buf_lock);
+       mutex_lock(&rx->buf_lock);
 
-       poll_wait(filep, &ir->buf.wait_poll, wait);
+       poll_wait(filep, &rx->buf.wait_poll, wait);
 
        dprintk("poll result = %s\n",
-               lirc_buffer_empty(&ir->buf) ? "0" : "POLLIN|POLLRDNORM");
+               lirc_buffer_empty(&rx->buf) ? "0" : "POLLIN|POLLRDNORM");
 
-       ret = lirc_buffer_empty(&ir->buf) ? 0 : (POLLIN|POLLRDNORM);
+       ret = lirc_buffer_empty(&rx->buf) ? 0 : (POLLIN|POLLRDNORM);
 
-       mutex_unlock(&ir->buf_lock);
+       mutex_unlock(&rx->buf_lock);
        return ret;
 }
 
@@ -998,10 +1032,9 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
        int result;
        unsigned long mode, features = 0;
 
-       if (ir->c_rx.addr != 0)
+       features |= LIRC_CAN_SEND_PULSE;
+       if (ir->rx != NULL)
                features |= LIRC_CAN_REC_LIRCCODE;
-       if (ir->c_tx.addr != 0)
-               features |= LIRC_CAN_SEND_PULSE;
 
        switch (cmd) {
        case LIRC_GET_LENGTH:
@@ -1028,15 +1061,9 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
                        result = -EINVAL;
                break;
        case LIRC_GET_SEND_MODE:
-               if (!(features&LIRC_CAN_SEND_MASK))
-                       return -ENOSYS;
-
                result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
                break;
        case LIRC_SET_SEND_MODE:
-               if (!(features&LIRC_CAN_SEND_MASK))
-                       return -ENOSYS;
-
                result = get_user(mode, (unsigned long *) arg);
                if (!result && mode != LIRC_MODE_PULSE)
                        return -EINVAL;
@@ -1047,6 +1074,15 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
        return result;
 }
 
+/* ir_devices_lock must be held */
+static struct IR *find_ir_device_by_minor(unsigned int minor)
+{
+       if (minor >= MAX_IRCTL_DEVICES)
+               return NULL;
+
+       return ir_devices[minor];
+}
+
 /*
  * Open the IR device.  Get hold of our IR structure and
  * stash it in private_data for the file
@@ -1055,15 +1091,15 @@ static int open(struct inode *node, struct file *filep)
 {
        struct IR *ir;
        int ret;
+       unsigned int minor = MINOR(node->i_rdev);
 
        /* find our IR struct */
-       unsigned minor = MINOR(node->i_rdev);
-       if (minor >= MAX_IRCTL_DEVICES) {
-               dprintk("minor %d: open result = -ENODEV\n",
-                       minor);
+       mutex_lock(&ir_devices_lock);
+       ir = find_ir_device_by_minor(minor);
+       mutex_unlock(&ir_devices_lock);
+
+       if (ir == NULL)
                return -ENODEV;
-       }
-       ir = ir_devices[minor];
 
        /* increment in use count */
        mutex_lock(&ir->ir_lock);
@@ -1110,14 +1146,15 @@ static struct lirc_driver lirc_template = {
 
 static int ir_remove(struct i2c_client *client);
 static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
-static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
+
+#define ID_FLAG_TX     0x01
+#define ID_FLAG_HDPVR  0x02
 
 static const struct i2c_device_id ir_transceiver_id[] = {
-       /* Generic entry for any IR transceiver */
-       { "ir_video", 0 },
-       /* IR device specific entries should be added here */
-       { "ir_tx_z8f0811_haup", 0 },
-       { "ir_rx_z8f0811_haup", 0 },
+       { "ir_tx_z8f0811_haup",  ID_FLAG_TX                 },
+       { "ir_rx_z8f0811_haup",  0                          },
+       { "ir_tx_z8f0811_hdpvr", ID_FLAG_HDPVR | ID_FLAG_TX },
+       { "ir_rx_z8f0811_hdpvr", ID_FLAG_HDPVR              },
        { }
 };
 
@@ -1128,7 +1165,6 @@ static struct i2c_driver driver = {
        },
        .probe          = ir_probe,
        .remove         = ir_remove,
-       .command        = ir_command,
        .id_table       = ir_transceiver_id,
 };
 
@@ -1146,198 +1182,253 @@ static const struct file_operations lirc_fops = {
        .release        = close
 };
 
-static int ir_remove(struct i2c_client *client)
+static void destroy_rx_kthread(struct IR_rx *rx)
 {
-       struct IR *ir = i2c_get_clientdata(client);
+       /* end up polling thread */
+       if (rx != NULL && !IS_ERR_OR_NULL(rx->task)) {
+               kthread_stop(rx->task);
+               rx->task = NULL;
+       }
+}
 
-       mutex_lock(&ir->ir_lock);
+/* ir_devices_lock must be held */
+static int add_ir_device(struct IR *ir)
+{
+       int i;
 
-       if (ir->have_rx || ir->have_tx) {
-               DECLARE_COMPLETION(tn);
-               DECLARE_COMPLETION(tn2);
-
-               /* end up polling thread */
-               if (ir->task && !IS_ERR(ir->task)) {
-                       ir->t_notify = &tn;
-                       ir->t_notify2 = &tn2;
-                       ir->shutdown = 1;
-                       wake_up_process(ir->task);
-                       complete(&tn2);
-                       wait_for_completion(&tn);
-                       ir->t_notify = NULL;
-                       ir->t_notify2 = NULL;
+       for (i = 0; i < MAX_IRCTL_DEVICES; i++)
+               if (ir_devices[i] == NULL) {
+                       ir_devices[i] = ir;
+                       break;
                }
 
-       } else {
-               mutex_unlock(&ir->ir_lock);
-               zilog_error("%s: detached from something we didn't "
-                           "attach to\n", __func__);
-               return -ENODEV;
+       return i == MAX_IRCTL_DEVICES ? -ENOMEM : i;
+}
+
+/* ir_devices_lock must be held */
+static void del_ir_device(struct IR *ir)
+{
+       int i;
+
+       for (i = 0; i < MAX_IRCTL_DEVICES; i++)
+               if (ir_devices[i] == ir) {
+                       ir_devices[i] = NULL;
+                       break;
+               }
+}
+
+static int ir_remove(struct i2c_client *client)
+{
+       struct IR *ir = i2c_get_clientdata(client);
+
+       mutex_lock(&ir_devices_lock);
+
+       if (ir == NULL) {
+               /* We destroyed everything when the first client came through */
+               mutex_unlock(&ir_devices_lock);
+               return 0;
        }
 
-       /* unregister lirc driver */
-       if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) {
-               lirc_unregister_driver(ir->l.minor);
-               ir_devices[ir->l.minor] = NULL;
+       /* Good-bye LIRC */
+       lirc_unregister_driver(ir->l.minor);
+
+       /* Good-bye Rx */
+       destroy_rx_kthread(ir->rx);
+       if (ir->rx != NULL) {
+               if (ir->rx->buf.fifo_initialized)
+                       lirc_buffer_free(&ir->rx->buf);
+               i2c_set_clientdata(ir->rx->c, NULL);
+               kfree(ir->rx);
        }
 
-       /* free memory */
-       lirc_buffer_free(&ir->buf);
-       mutex_unlock(&ir->ir_lock);
+       /* Good-bye Tx */
+       i2c_set_clientdata(ir->tx->c, NULL);
+       kfree(ir->tx);
+
+       /* Good-bye IR */
+       del_ir_device(ir);
        kfree(ir);
 
+       mutex_unlock(&ir_devices_lock);
        return 0;
 }
 
-static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
+
+/* ir_devices_lock must be held */
+static struct IR *find_ir_device_by_adapter(struct i2c_adapter *adapter)
 {
+       int i;
        struct IR *ir = NULL;
+
+       for (i = 0; i < MAX_IRCTL_DEVICES; i++)
+               if (ir_devices[i] != NULL &&
+                   ir_devices[i]->adapter == adapter) {
+                       ir = ir_devices[i];
+                       break;
+               }
+
+       return ir;
+}
+
+static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+       struct IR *ir;
        struct i2c_adapter *adap = client->adapter;
-       char buf;
        int ret;
-       int have_rx = 0, have_tx = 0;
+       bool tx_probe = false;
 
-       dprintk("%s: adapter id=0x%x, client addr=0x%02x\n",
-               __func__, adap->id, client->addr);
+       dprintk("%s: %s on i2c-%d (%s), client addr=0x%02x\n",
+               __func__, id->name, adap->nr, adap->name, client->addr);
 
        /*
-        * The external IR receiver is at i2c address 0x71.
-        * The IR transmitter is at 0x70.
+        * The IR receiver    is at i2c address 0x71.
+        * The IR transmitter is at i2c address 0x70.
         */
-       client->addr = 0x70;
 
-       if (!disable_tx) {
-               if (i2c_master_recv(client, &buf, 1) == 1)
-                       have_tx = 1;
-               dprintk("probe 0x70 @ %s: %s\n",
-                       adap->name, have_tx ? "success" : "failed");
-       }
+       if (id->driver_data & ID_FLAG_TX)
+               tx_probe = true;
+       else if (tx_only) /* module option */
+               return -ENXIO;
 
-       if (!disable_rx) {
-               client->addr = 0x71;
-               if (i2c_master_recv(client, &buf, 1) == 1)
-                       have_rx = 1;
-               dprintk("probe 0x71 @ %s: %s\n",
-                       adap->name, have_rx ? "success" : "failed");
-       }
+       zilog_info("probing IR %s on %s (i2c-%d)\n",
+                  tx_probe ? "Tx" : "Rx", adap->name, adap->nr);
 
-       if (!(have_rx || have_tx)) {
-               zilog_error("%s: no devices found\n", adap->name);
-               goto out_nodev;
-       }
+       mutex_lock(&ir_devices_lock);
 
-       printk(KERN_INFO "lirc_zilog: chip found with %s\n",
-               have_rx && have_tx ? "RX and TX" :
-                       have_rx ? "RX only" : "TX only");
+       /* Use a single struct IR instance for both the Rx and Tx functions */
+       ir = find_ir_device_by_adapter(adap);
+       if (ir == NULL) {
+               ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
+               if (ir == NULL) {
+                       ret = -ENOMEM;
+                       goto out_no_ir;
+               }
+               /* store for use in ir_probe() again, and open() later on */
+               ret = add_ir_device(ir);
+               if (ret)
+                       goto out_free_ir;
+
+               ir->adapter = adap;
+               mutex_init(&ir->ir_lock);
+
+               /* set lirc_dev stuff */
+               memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
+               ir->l.minor       = minor; /* module option */
+               ir->l.code_length = 13;
+               ir->l.rbuf        = NULL;
+               ir->l.fops        = &lirc_fops;
+               ir->l.data        = ir;
+               ir->l.dev         = &adap->dev;
+               ir->l.sample_rate = 0;
+       }
 
-       ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
+       if (tx_probe) {
+               /* Set up a struct IR_tx instance */
+               ir->tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL);
+               if (ir->tx == NULL) {
+                       ret = -ENOMEM;
+                       goto out_free_xx;
+               }
 
-       if (!ir)
-               goto out_nomem;
+               ir->tx->c = client;
+               ir->tx->need_boot = 1;
+               ir->tx->post_tx_ready_poll =
+                              (id->driver_data & ID_FLAG_HDPVR) ? false : true;
+       } else {
+               /* Set up a struct IR_rx instance */
+               ir->rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL);
+               if (ir->rx == NULL) {
+                       ret = -ENOMEM;
+                       goto out_free_xx;
+               }
 
-       ret = lirc_buffer_init(&ir->buf, 2, BUFLEN / 2);
-       if (ret)
-               goto out_nomem;
+               ret = lirc_buffer_init(&ir->rx->buf, 2, BUFLEN / 2);
+               if (ret)
+                       goto out_free_xx;
 
-       mutex_init(&ir->ir_lock);
-       mutex_init(&ir->buf_lock);
-       ir->need_boot = 1;
+               mutex_init(&ir->rx->buf_lock);
+               ir->rx->c = client;
+               ir->rx->hdpvr_data_fmt =
+                              (id->driver_data & ID_FLAG_HDPVR) ? true : false;
 
-       memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
-       ir->l.minor = -1;
+               /* set lirc_dev stuff */
+               ir->l.rbuf = &ir->rx->buf;
+       }
 
-       /* I2C attach to device */
        i2c_set_clientdata(client, ir);
 
-       /* initialise RX device */
-       if (have_rx) {
-               DECLARE_COMPLETION(tn);
-               memcpy(&ir->c_rx, client, sizeof(struct i2c_client));
-
-               ir->c_rx.addr = 0x71;
-               strlcpy(ir->c_rx.name, ZILOG_HAUPPAUGE_IR_RX_NAME,
-                       I2C_NAME_SIZE);
+       /* Proceed only if we have the required Tx and Rx clients ready to go */
+       if (ir->tx == NULL ||
+           (ir->rx == NULL && !tx_only)) {
+               zilog_info("probe of IR %s on %s (i2c-%d) done. Waiting on "
+                          "IR %s.\n", tx_probe ? "Tx" : "Rx", adap->name,
+                          adap->nr, tx_probe ? "Rx" : "Tx");
+               goto out_ok;
+       }
 
+       /* initialise RX device */
+       if (ir->rx != NULL) {
                /* try to fire up polling thread */
-               ir->t_notify = &tn;
-               ir->task = kthread_run(lirc_thread, ir, "lirc_zilog");
-               if (IS_ERR(ir->task)) {
-                       ret = PTR_ERR(ir->task);
-                       zilog_error("lirc_register_driver: cannot run "
-                                   "poll thread %d\n", ret);
-                       goto err;
+               ir->rx->task = kthread_run(lirc_thread, ir,
+                                          "zilog-rx-i2c-%d", adap->nr);
+               if (IS_ERR(ir->rx->task)) {
+                       ret = PTR_ERR(ir->rx->task);
+                       zilog_error("%s: could not start IR Rx polling thread"
+                                   "\n", __func__);
+                       goto out_free_xx;
                }
-               wait_for_completion(&tn);
-               ir->t_notify = NULL;
-               ir->have_rx = 1;
        }
 
-       /* initialise TX device */
-       if (have_tx) {
-               memcpy(&ir->c_tx, client, sizeof(struct i2c_client));
-               ir->c_tx.addr = 0x70;
-               strlcpy(ir->c_tx.name, ZILOG_HAUPPAUGE_IR_TX_NAME,
-                       I2C_NAME_SIZE);
-               ir->have_tx = 1;
-       }
-
-       /* set lirc_dev stuff */
-       ir->l.code_length = 13;
-       ir->l.rbuf        = &ir->buf;
-       ir->l.fops        = &lirc_fops;
-       ir->l.data        = ir;
-       ir->l.minor       = minor;
-       ir->l.dev         = &adap->dev;
-       ir->l.sample_rate = 0;
-
        /* register with lirc */
        ir->l.minor = lirc_register_driver(&ir->l);
        if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) {
-               zilog_error("ir_attach: \"minor\" must be between 0 and %d "
-                           "(%d)!\n", MAX_IRCTL_DEVICES-1, ir->l.minor);
+               zilog_error("%s: \"minor\" must be between 0 and %d (%d)!\n",
+                           __func__, MAX_IRCTL_DEVICES-1, ir->l.minor);
                ret = -EBADRQC;
-               goto err;
+               goto out_free_thread;
        }
 
-       /* store this for getting back in open() later on */
-       ir_devices[ir->l.minor] = ir;
-
        /*
         * if we have the tx device, load the 'firmware'.  We do this
         * after registering with lirc as otherwise hotplug seems to take
         * 10s to create the lirc device.
         */
-       if (have_tx) {
-               /* Special TX init */
-               ret = tx_init(ir);
-               if (ret != 0)
-                       goto err;
-       }
+       ret = tx_init(ir->tx);
+       if (ret != 0)
+               goto out_unregister;
 
+       zilog_info("probe of IR %s on %s (i2c-%d) done. IR unit ready.\n",
+                  tx_probe ? "Tx" : "Rx", adap->name, adap->nr);
+out_ok:
+       mutex_unlock(&ir_devices_lock);
        return 0;
 
-err:
-       /* undo everything, hopefully... */
-       if (ir->c_rx.addr)
-               ir_remove(&ir->c_rx);
-       if (ir->c_tx.addr)
-               ir_remove(&ir->c_tx);
-       return ret;
-
-out_nodev:
-       zilog_error("no device found\n");
-       return -ENODEV;
-
-out_nomem:
-       zilog_error("memory allocation failure\n");
+out_unregister:
+       lirc_unregister_driver(ir->l.minor);
+out_free_thread:
+       destroy_rx_kthread(ir->rx);
+out_free_xx:
+       if (ir->rx != NULL) {
+               if (ir->rx->buf.fifo_initialized)
+                       lirc_buffer_free(&ir->rx->buf);
+               if (ir->rx->c != NULL)
+                       i2c_set_clientdata(ir->rx->c, NULL);
+               kfree(ir->rx);
+       }
+       if (ir->tx != NULL) {
+               if (ir->tx->c != NULL)
+                       i2c_set_clientdata(ir->tx->c, NULL);
+               kfree(ir->tx);
+       }
+out_free_ir:
+       del_ir_device(ir);
        kfree(ir);
-       return -ENOMEM;
-}
-
-static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
-{
-       /* nothing */
-       return 0;
+out_no_ir:
+       zilog_error("%s: probing IR %s on %s (i2c-%d) failed with %d\n",
+                   __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr,
+                  ret);
+       mutex_unlock(&ir_devices_lock);
+       return ret;
 }
 
 static int __init zilog_init(void)
@@ -1347,6 +1438,7 @@ static int __init zilog_init(void)
        zilog_notify("Zilog/Hauppauge IR driver initializing\n");
 
        mutex_init(&tx_data_lock);
+       mutex_init(&ir_devices_lock);
 
        request_module("firmware_class");
 
@@ -1372,7 +1464,8 @@ module_exit(zilog_exit);
 
 MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
 MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
-             "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver");
+             "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, "
+             "Andy Walls");
 MODULE_LICENSE("GPL");
 /* for compat with old name, which isn't all that accurate anymore */
 MODULE_ALIAS("lirc_pvr150");
@@ -1383,8 +1476,5 @@ MODULE_PARM_DESC(minor, "Preferred minor device number");
 module_param(debug, bool, 0644);
 MODULE_PARM_DESC(debug, "Enable debugging messages");
 
-module_param(disable_rx, bool, 0644);
-MODULE_PARM_DESC(disable_rx, "Disable the IR receiver device");
-
-module_param(disable_tx, bool, 0644);
-MODULE_PARM_DESC(disable_tx, "Disable the IR transmitter device");
+module_param(tx_only, bool, 0644);
+MODULE_PARM_DESC(tx_only, "Only handle the IR transmit function");