The following patch allows bridge drivers, with an I2C IR Tx/Rx transceiver, to pass a mutex for serializing access to a single I2C IR chip between separate IR Tx and Rx modules. The change modifies struct IR_i2c_init_data and struct IR_i2c to add struct mutex *transceiver_lock that ir-kbd-i2c and lirc_zilog will use, if provided by the bridge driver. The changes to ir-kbd-i2c.[ch] and lirc_zilog.c provide the functional change in the patch. This patch also modifies cx18, ivtv, and hdpvr (sans Jarrod's recent patches) to provide a transceiver_lock mutex to ir-kbd-i2c and lirc_zilog. I skimmed all the other modules that use IR_i2c_init_data. They all appear to zero-fill the init_data properly before handing the data over to ir-kbd-i2c.c. I did find that pvrusb2 IR Rx for address 0x71 was broken, due to my recommendation to remove automatic config for address 0x71 from ir-kbd-i2c.c. I'll fix that in another patch, if someone with a pvrusb2 device doesn't beat me to it. So without further ado... diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 676e5be..da1ef93 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -701,6 +701,7 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) mutex_init(&cx->serialize_lock); mutex_init(&cx->gpio_lock); + mutex_init(&cx->ir_transceiver_lock); mutex_init(&cx->epu2apu_mb_lock); mutex_init(&cx->epu2cpu_mb_lock); diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index f6f3e50..82dc747 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -626,6 +626,7 @@ struct cx18 { struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2]; struct IR_i2c_init_data ir_i2c_init_data; + struct mutex ir_transceiver_lock; /* gpio */ u32 gpio_dir; diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index c330fb9..7782de1 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -96,10 +96,12 @@ static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw, /* Our default information for ir-kbd-i2c.c to use */ switch (hw) { case CX18_HW_Z8F0811_IR_RX_HAUP: + case CX18_HW_Z8F0811_IR_TX_HAUP: init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = RC_TYPE_RC5; init_data->name = cx->card_name; + init_data->transceiver_lock = &cx->ir_transceiver_lock; info.platform_data = init_data; break; } diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c index f7d1ee5..df4a02a 100644 --- a/drivers/media/video/hdpvr/hdpvr-core.c +++ b/drivers/media/video/hdpvr/hdpvr-core.c @@ -304,6 +304,7 @@ static int hdpvr_probe(struct usb_interface *interface, mutex_init(&dev->io_mutex); mutex_init(&dev->i2c_mutex); + mutex_init(&dev->ir_transceiver_mutex); mutex_init(&dev->usbc_mutex); dev->usbc_buf = kmalloc(64, GFP_KERNEL); if (!dev->usbc_buf) { diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c index 24966aa..b1e68b8 100644 --- a/drivers/media/video/hdpvr/hdpvr-i2c.c +++ b/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -48,13 +48,15 @@ static int hdpvr_new_i2c_ir(struct hdpvr_device *dev, struct i2c_adapter *adap, memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, type, I2C_NAME_SIZE); - /* Our default information for ir-kbd-i2c.c to use */ + /* Our default information for ir-kbd-i2c.c and lirc_zilog.c to use */ switch (addr) { case Z8F0811_IR_RX_I2C_ADDR: + case Z8F0811_IR_TX_I2C_ADDR: init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = RC_TYPE_RC5; init_data->name = "HD PVR"; + init_data->transceiver_lock = &dev->ir_transceiver_mutex; info.platform_data = init_data; break; } diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h index 37f1e4c..00c8563 100644 --- a/drivers/media/video/hdpvr/hdpvr.h +++ b/drivers/media/video/hdpvr/hdpvr.h @@ -112,6 +112,7 @@ struct hdpvr_device { /* For passing data to ir-kbd-i2c */ struct IR_i2c_init_data ir_i2c_init_data; + struct mutex ir_transceiver_mutex; /* usb control transfer buffer and lock */ struct mutex usbc_mutex; diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index c87b6bc..6714e1e 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -245,7 +245,15 @@ static void ir_key_poll(struct IR_i2c *ir) int rc; dprintk(2,"ir_poll_key\n"); + + if (ir->transceiver_lock) + mutex_lock(ir->transceiver_lock); + rc = ir->get_key(ir, &ir_key, &ir_raw); + + if (ir->transceiver_lock) + mutex_unlock(ir->transceiver_lock); + if (rc < 0) { dprintk(2,"error\n"); return; @@ -362,6 +370,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->get_key = get_key_avermedia_cardbus; break; } + + ir->transceiver_lock = init_data->transceiver_lock; } if (!rc) { diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 3994642..ea2afeb 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -713,6 +713,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */ mutex_init(&itv->serialize_lock); + mutex_init(&itv->ir_transceiver_lock); mutex_init(&itv->i2c_bus_lock); mutex_init(&itv->udma.lock); diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 04bacdb..2c8d7c1 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -699,6 +699,7 @@ struct ivtv { struct mutex i2c_bus_lock; /* lock i2c bus */ struct IR_i2c_init_data ir_i2c_init_data; + struct mutex ir_transceiver_lock; /* Program Index information */ u32 pgm_info_offset; /* start of pgm info in encoder memory */ diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index e103b8f..4233b48 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -184,7 +184,21 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) if (hw & IVTV_HW_IR_TX_ANY) { if (itv->hw_flags & IVTV_HW_IR_TX_ANY) return -1; + + /* Our default information for lirc_zilog.c to use */ + switch (hw) { + case IVTV_HW_Z8F0811_IR_TX_HAUP: + /* Default to grey remote */ + init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; + init_data->internal_get_key_func = + IR_KBD_GET_KEY_HAUP_XVR; + init_data->type = RC_TYPE_RC5; + init_data->name = itv->card_name; + init_data->transceiver_lock = &itv->ir_transceiver_lock; + break; + } memset(&info, 0, sizeof(struct i2c_board_info)); + info.platform_data = init_data; strlcpy(info.type, type, I2C_NAME_SIZE); return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ? -1 : 0; @@ -217,6 +231,7 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = RC_TYPE_RC5; init_data->name = itv->card_name; + init_data->transceiver_lock = &itv->ir_transceiver_lock; break; case IVTV_HW_I2C_IR_RX_ADAPTEC: init_data->get_key = get_key_adaptec; diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 18fae54..8185581 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -59,6 +59,7 @@ #include <media/lirc_dev.h> #include <media/lirc.h> +#include <media/ir-kbd-i2c.h> struct IR_rx { /* RX device */ @@ -89,6 +90,7 @@ struct IR { struct lirc_driver l; struct mutex ir_lock; + struct mutex *xcvr_lock; int open; struct i2c_adapter *adapter; @@ -175,10 +177,10 @@ static int add_to_buf(struct IR *ir) * Lock i2c bus for the duration. RX/TX chips interfere so * this is worth it */ - mutex_lock(&ir->ir_lock); + mutex_lock(ir->xcvr_lock); if (kthread_should_stop()) { - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); return -ENODATA; } @@ -190,7 +192,7 @@ static int add_to_buf(struct IR *ir) if (ret != 1) { zilog_error("i2c_master_send failed with %d\n", ret); if (failures >= 3) { - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); zilog_error("unable to read from the IR chip " "after 3 resets, giving up\n"); return ret; @@ -202,23 +204,23 @@ static int add_to_buf(struct IR *ir) set_current_state(TASK_UNINTERRUPTIBLE); if (kthread_should_stop()) { - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); return -ENODATA; } schedule_timeout((100 * HZ + 999) / 1000); ir->tx->need_boot = 1; ++failures; - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); continue; } if (kthread_should_stop()) { - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); return -ENODATA; } ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf)); - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); if (ret != sizeof(keybuf)) { zilog_error("i2c_master_recv failed with %d -- " "keeping last read buffer\n", ret); @@ -920,7 +922,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, return -EINVAL; /* Lock i2c bus for the duration */ - mutex_lock(&ir->ir_lock); + mutex_lock(ir->xcvr_lock); /* Send each keypress */ for (i = 0; i < n;) { @@ -928,7 +930,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, int command; if (copy_from_user(&command, buf + i, sizeof(command))) { - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); return -EFAULT; } @@ -944,7 +946,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, ret = send_code(tx, (unsigned)command >> 16, (unsigned)command & 0xFFFF); if (ret == -EPROTO) { - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); return ret; } } @@ -961,7 +963,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, if (failures >= 3) { zilog_error("unable to send to the IR chip " "after 3 resets, giving up\n"); - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); return ret; } set_current_state(TASK_UNINTERRUPTIBLE); @@ -973,7 +975,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, } /* Release i2c bus */ - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); /* All looks good */ return n; @@ -1079,15 +1081,15 @@ static int open(struct inode *node, struct file *filep) return -ENODEV; /* increment in use count */ - mutex_lock(&ir->ir_lock); + mutex_lock(ir->xcvr_lock); ++ir->open; ret = set_use_inc(ir); if (ret != 0) { --ir->open; - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); return ret; } - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); /* stash our IR struct */ filep->private_data = ir; @@ -1106,10 +1108,10 @@ static int close(struct inode *node, struct file *filep) } /* decrement in use count */ - mutex_lock(&ir->ir_lock); + mutex_lock(ir->xcvr_lock); --ir->open; set_use_dec(ir); - mutex_unlock(&ir->ir_lock); + mutex_unlock(ir->xcvr_lock); return 0; } @@ -1251,6 +1253,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct IR *ir; struct i2c_adapter *adap = client->adapter; + const struct IR_i2c_init_data *init_data = client->dev.platform_data; int ret; bool tx_probe = false; @@ -1288,6 +1291,12 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->adapter = adap; mutex_init(&ir->ir_lock); + /* Use bridge driver's transceiver lock, if provided */ + if (init_data != NULL) + ir->xcvr_lock = init_data->transceiver_lock; + if (ir->xcvr_lock == NULL) + ir->xcvr_lock = &ir->ir_lock; + /* set lirc_dev stuff */ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); ir->l.minor = minor; /* module option */ diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h index 768aa77..5893fd8 100644 --- a/include/media/ir-kbd-i2c.h +++ b/include/media/ir-kbd-i2c.h @@ -21,6 +21,7 @@ struct IR_i2c { char name[32]; char phys[32]; int (*get_key)(struct IR_i2c*, u32*, u32*); + struct mutex *transceiver_lock; }; enum ir_kbd_get_key_fn { @@ -48,5 +49,7 @@ struct IR_i2c_init_data { enum ir_kbd_get_key_fn internal_get_key_func; struct rc_dev *rc_dev; + + struct mutex *transceiver_lock; }; #endif -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html