summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/staging/lirc/lirc_zilog.c344
1 files changed, 212 insertions, 132 deletions
diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c
index 00f2e7d3b3e5..f2e8c63fa5bd 100644
--- a/drivers/staging/lirc/lirc_zilog.c
+++ b/drivers/staging/lirc/lirc_zilog.c
@@ -94,11 +94,13 @@ struct IR {
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 */
@@ -131,10 +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_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 tx_only; /* only handle the IR Tx function */
static int minor = -1; /* minor number */
#define dprintk(fmt, args...) \
@@ -252,9 +255,6 @@ static int lirc_thread(void *arg)
struct IR *ir = arg;
struct IR_rx *rx = ir->rx;
- if (rx == NULL)
- return -ENXIO;
-
if (rx->t_notify != NULL)
complete(rx->t_notify);
@@ -296,6 +296,7 @@ static int lirc_thread(void *arg)
complete(rx->t_notify);
dprintk("poll thread ended\n");
+ /* FIXME - investigate if this is the proper way to shutdown a kthread*/
return 0;
}
@@ -1058,6 +1059,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
@@ -1066,15 +1076,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);
@@ -1159,136 +1169,203 @@ static const struct file_operations lirc_fops = {
.release = close
};
-static int ir_remove(struct i2c_client *client)
+/* FIXME - investigate if this is the proper way to shutdown a kthread */
+static void destroy_rx_kthread(struct IR_rx *rx)
{
- struct IR *ir = i2c_get_clientdata(client);
- struct IR_rx *rx = ir->rx;
- struct IR_tx *tx = ir->tx;
+ DECLARE_COMPLETION(tn);
+ DECLARE_COMPLETION(tn2);
- /* FIXME make tx, rx senitive */
- mutex_lock(&ir->ir_lock);
+ if (rx == NULL)
+ return;
+
+ /* end up polling thread */
+ if (rx->task && !IS_ERR(rx->task)) {
+ rx->t_notify = &tn;
+ rx->t_notify2 = &tn2;
+ rx->shutdown = 1;
+ wake_up_process(rx->task);
+ complete(&tn2);
+ wait_for_completion(&tn);
+ rx->t_notify = NULL;
+ rx->t_notify2 = NULL;
+ }
+}
- if (rx != NULL || tx != NULL) {
- DECLARE_COMPLETION(tn);
- DECLARE_COMPLETION(tn2);
-
- /* end up polling thread */
- if (rx->task && !IS_ERR(rx->task)) {
- rx->t_notify = &tn;
- rx->t_notify2 = &tn2;
- rx->shutdown = 1;
- wake_up_process(rx->task);
- complete(&tn2);
- wait_for_completion(&tn);
- rx->t_notify = NULL;
- rx->t_notify2 = NULL;
+/* ir_devices_lock must be held */
+static int add_ir_device(struct IR *ir)
+{
+ int i;
+
+ 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 */
- /* FIXME make tx, rx senitive */
- 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 */
- /* FIXME make tx, rx senitive */
- lirc_buffer_free(&rx->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;
int ret;
- int have_rx = 0, have_tx = 0;
+ bool tx_probe = false;
dprintk("%s: %s on i2c-%d (%s), client addr=0x%02x\n",
__func__, id->name, adap->nr, adap->name, client->addr);
/*
- * FIXME - This probe function probes both the Tx and Rx
- * addresses of the IR microcontroller.
- *
- * However, the I2C subsystem is passing along one I2C client at a
- * time, based on matches to the ir_transceiver_id[] table above.
- * The expectation is that each i2c_client address will be probed
- * individually by drivers so the I2C subsystem can mark all client
- * addresses as claimed or not.
- *
- * This probe routine causes only one of the client addresses, TX or RX,
- * to be claimed. This will cause a problem if the I2C subsystem is
- * subsequently triggered to probe unclaimed clients again.
- */
- /*
- * 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.
*/
- if (id->driver_data & ID_FLAG_TX) {
- have_tx = 1;
- } else if (!disable_rx) {
- have_rx = 1;
- } else {
+ if (id->driver_data & ID_FLAG_TX)
+ tx_probe = true;
+ else if (tx_only) /* module option */
return -ENXIO;
- }
- printk(KERN_INFO "lirc_zilog: chip found with %s\n",
- have_rx && have_tx ? "RX and TX" :
- have_rx ? "RX only" : "TX only");
+ zilog_info("%s: probing IR %s on %s (i2c-%d)\n",
+ __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr);
- ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
- if (!ir)
- goto out_nomem;
+ mutex_lock(&ir_devices_lock);
- if (have_tx) {
- ir->tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL);
- if (ir->tx != NULL) {
- ir->tx->c = client;
- ir->tx->need_boot = 1;
- ir->tx->post_tx_ready_poll =
- (id->driver_data & ID_FLAG_HDPVR) ? false : true;
+ /* 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;
}
- if (have_rx) {
- ir->rx = kzalloc(sizeof(struct IR_rx), 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;
+ }
+ 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;
- } else {
- ir->rx->c = client;
- ir->rx->hdpvr_data_fmt =
- (id->driver_data & ID_FLAG_HDPVR) ? true : false;
- mutex_init(&ir->rx->buf_lock);
- ret = lirc_buffer_init(&ir->rx->buf, 2, BUFLEN / 2);
+ goto out_free_xx;
}
- if (ret && (ir->rx != NULL)) {
- kfree(ir->rx);
- ir->rx = NULL;
- }
- }
+ ret = lirc_buffer_init(&ir->rx->buf, 2, BUFLEN / 2);
+ if (ret)
+ goto out_free_xx;
- mutex_init(&ir->ir_lock);
+ 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);
+ /* 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("%s: probe of IR %s on %s (i2c-%d) done, waiting on "
+ "IR %s\n", __func__, tx_probe ? "Tx" : "Rx",
+ adap->name, adap->nr, tx_probe ? "Rx" : "Tx");
+ goto out_ok;
+ }
+
/* initialise RX device */
if (ir->rx != NULL) {
DECLARE_COMPLETION(tn);
@@ -1298,35 +1375,23 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
ir->rx->task = kthread_run(lirc_thread, ir, "lirc_zilog");
if (IS_ERR(ir->rx->task)) {
ret = PTR_ERR(ir->rx->task);
- zilog_error("lirc_register_driver: cannot run "
- "poll thread %d\n", ret);
- goto err;
+ zilog_error("%s: could not start IR Rx polling thread"
+ "\n", __func__);
+ goto out_free_xx;
}
wait_for_completion(&tn);
ir->rx->t_notify = NULL;
}
- /* set lirc_dev stuff */
- ir->l.code_length = 13;
- ir->l.rbuf = (ir->rx == NULL) ? NULL : &ir->rx->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
@@ -1336,25 +1401,39 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* Special TX init */
ret = tx_init(ir->tx);
if (ret != 0)
- goto err;
+ goto out_unregister;
}
+out_ok:
+ mutex_unlock(&ir_devices_lock);
return 0;
-err:
- /* FIXME - memory deallocation for all error cases needs work */
- /* undo everything, hopefully... */
- if (ir->rx != NULL)
- ir_remove(ir->rx->c);
- if (ir->tx != NULL)
- ir_remove(ir->tx->c);
- return ret;
-
-out_nomem:
- /* FIXME - memory deallocation for all error cases needs work */
- 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;
+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 ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
@@ -1370,6 +1449,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");
@@ -1406,5 +1486,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(tx_only, bool, 0644);
+MODULE_PARM_DESC(tx_only, "Only handle the IR transmit function");