Package: lirc-modules-source
Version: 0.7.1pre2-9
Tags: patch
Followup-For: Bug #326672

I'm experiencing exactly the same problem with kernel 2.6.13.2
using the `lirc_serial' driver. The following kernel messages are
generated upon trying to load the relevant modules:

lirc_dev: Unknown symbol class_simple_device_add
lirc_dev: Unknown symbol class_simple_destroy
lirc_dev: Unknown symbol class_simple_device_remove
lirc_dev: Unknown symbol class_simple_create
lirc_serial: Unknown symbol lirc_unregister_plugin
lirc_serial: Unknown symbol lirc_register_plugin

This problem is due to a sysfs interface change in kernel 2.6.13
and has been fixed upstream (in version 0.7.2).

An interim solution would be to replace the files `drivers/kcompat.h'
and `lirc_dev/lirc_dev.c' with their respective versions from upstream
version 0.7.2. The attached patch resembles exactly these changes and
results in perfectly working modules for kernels above and below version
2.6.13.


-- System Information:
Debian Release: testing/unstable
  APT prefers unstable
  APT policy: (700, 'unstable')
Architecture: amd64 (x86_64)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.13.2-maia
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)

Versions of packages lirc-modules-source depends on:
ii  debconf [debconf-2.0]         1.4.58     Debian configuration management sy
ii  debhelper                     4.9.10     helper programs for debian/rules
ii  debianutils                   2.14.3     Miscellaneous utilities specific t

Versions of packages lirc-modules-source recommends:
ii  dpkg                          1.13.11    package maintenance system for Deb
ii  dpkg-dev                      1.13.11    package building tools for Debian
ii  gcc [c-compiler]              4:4.0.1-3  The GNU C compiler
ii  gcc-3.3 [c-compiler]          1:3.3.6-10 The GNU C compiler
ii  gcc-3.4 [c-compiler]          3.4.4-8    The GNU C compiler
ii  gcc-4.0 [c-compiler]          4.0.1-9    The GNU C compiler
ii  kernel-package                9.007      A utility for building Linux kerne
ii  make                          3.80-11    The GNU version of the "make" util

-- debconf information excluded
diff -urN lirc-0.7.1pre2_9/drivers/kcompat.h lirc-0.7.1pre2/drivers/kcompat.h
--- lirc-0.7.1pre2_9/drivers/kcompat.h	2005-02-19 16:12:58.000000000 +0100
+++ lirc-0.7.1pre2/drivers/kcompat.h	2005-08-01 22:34:27.000000000 +0200
@@ -1,4 +1,4 @@
-/*      $Id: kcompat.h,v 5.11 2005/02/19 15:12:58 lirc Exp $      */
+/*      $Id: kcompat.h,v 5.13 2005/08/01 20:34:27 lirc Exp $      */
 
 #ifndef _KCOMPAT_H
 #define _KCOMPAT_H
@@ -6,9 +6,36 @@
 #include <linux/version.h>
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+
+#include <linux/device.h>
+
 #define LIRC_HAVE_DEVFS
 #define LIRC_HAVE_DEVFS_26
 #define LIRC_HAVE_SYSFS
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+typedef struct class_simple lirc_class_t;
+
+static inline lirc_class_t *class_create(struct module *owner, char *name)
+{
+	return class_simple_create(owner, name);
+}
+
+static inline void class_destroy(lirc_class_t *cls)
+{
+	class_simple_destroy(cls);
+}
+
+#define class_device_create class_simple_device_add
+
+static inline void class_device_destroy(lirc_class_t *cls, dev_t devt)
+{
+	class_simple_device_remove(devt);
+}
+#else
+typedef struct class lirc_class_t;
+#endif
+
 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 #define LIRC_HAVE_DEVFS
 #define LIRC_HAVE_DEVFS_24
@@ -69,15 +96,15 @@
 #endif /* DEVFS 2.4 */
 
 #ifndef LIRC_HAVE_SYSFS
-#define class_simple_destroy(x) do { } while(0)
-#define class_simple_create(x,y) NULL
-#define class_simple_device_remove(x) do { } while(0)
-#define class_simple_device_add(x, y, z, xx, yy) 0
+#define class_destroy(x) do { } while(0)
+#define class_create(x,y) NULL
+#define class_device_destroy(x,y) do { } while(0)
+#define class_device_create(x, y, z, xx, yy) 0
 #define IS_ERR(x) 0
-struct class_simple 
+typedef struct class_simple 
 {
 	int notused;
-};	
+} lirc_class_t;	
 #endif /* No SYSFS */
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
diff -urN lirc-0.7.1pre2_9/drivers/lirc_dev/lirc_dev.c lirc-0.7.1pre2/drivers/lirc_dev/lirc_dev.c
--- lirc-0.7.1pre2_9/drivers/lirc_dev/lirc_dev.c	2005-02-19 22:30:04.000000000 +0100
+++ lirc-0.7.1pre2/drivers/lirc_dev/lirc_dev.c	2005-08-08 08:04:48.000000000 +0200
@@ -17,7 +17,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * $Id: lirc_dev.c,v 1.37 2005/02/19 15:12:59 lirc Exp $
+ * $Id: lirc_dev.c,v 1.44 2005/08/08 06:04:48 lirc Exp $
  *
  */
 
@@ -76,9 +76,12 @@
 struct irctl
 {
 	struct lirc_plugin p;
+	int attached;
 	int open;
 
+	struct semaphore buffer_sem;
 	struct lirc_buffer *buf;
+	unsigned int chunk_size;
 
 	int tpid;
 	struct semaphore *t_notify;
@@ -97,7 +100,7 @@
 static struct file_operations fops;
 
 /* Only used for sysfs but defined to void otherwise */
-static struct class_simple *lirc_class;
+static lirc_class_t *lirc_class;
 
 /*  helper function
  *  initializes the irctl structure
@@ -105,6 +108,7 @@
 static inline void init_irctl(struct irctl *ir)
 {
 	memset(&ir->p, 0, sizeof(struct lirc_plugin));
+	sema_init(&ir->buffer_sem, 1);
 	ir->p.minor = NOPLUG;
 
 	ir->tpid = -1;
@@ -114,8 +118,29 @@
 	ir->jiffies_to_wait = 0;
 
 	ir->open = 0;
+	ir->attached = 0;
 }
 
+static void cleanup(struct irctl *ir)
+{
+	dprintk(LOGHEAD "cleaning up\n", ir->p.name, ir->p.minor);
+
+#ifdef LIRC_HAVE_DEVFS_24
+	devfs_unregister(ir->devfs_handle);
+#endif
+#ifdef LIRC_HAVE_DEVFS_26
+	devfs_remove(DEV_LIRC "/%u", ir->p.minor);
+#endif
+	class_device_destroy(lirc_class,MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
+
+	if (ir->buf != ir->p.rbuf){
+		lirc_buffer_free(ir->buf);
+		kfree(ir->buf);
+	}
+	ir->buf = NULL;
+	
+	init_irctl(ir);
+}
 
 /*  helper function
  *  reads key codes from plugin and puts them into buffer
@@ -176,7 +201,7 @@
 	do {
 		if (ir->open) {
 			if (ir->jiffies_to_wait) {
-				current->state = TASK_INTERRUPTIBLE;
+				set_current_state(TASK_INTERRUPTIBLE);
 				schedule_timeout(ir->jiffies_to_wait);
 			} else {
 				interruptible_sleep_on(ir->p.get_queue(ir->p.data));
@@ -189,7 +214,7 @@
 			}
 		} else {
 			/* if device not opened so we can sleep half a second */
-			current->state = TASK_INTERRUPTIBLE;
+			set_current_state(TASK_INTERRUPTIBLE);
 			schedule_timeout(HZ/2);
 		}
 	} while (!ir->shutdown);
@@ -223,22 +248,22 @@
 	DECLARE_MUTEX_LOCKED(tn);
 
 	if (!p) {
-		printk("lirc_dev: lirc_register_plugin:"
+		printk("lirc_dev: lirc_register_plugin: "
 		       "plugin pointer must be not NULL!\n");
 		err = -EBADRQC;
 		goto out;
 	}
 
 	if (MAX_IRCTL_DEVICES <= p->minor) {
-		printk("lirc_dev: lirc_register_plugin:"
-		       "\" minor\" must be between 0 and %d (%d)!\n",
+		printk("lirc_dev: lirc_register_plugin: "
+		       "\"minor\" must be between 0 and %d (%d)!\n",
 		       MAX_IRCTL_DEVICES-1, p->minor);
 		err = -EBADRQC;
 		goto out;
 	}
 
 	if (1 > p->code_length || (BUFLEN*8) < p->code_length) {
-		printk("lirc_dev: lirc_register_plugin:"
+		printk("lirc_dev: lirc_register_plugin: "
 		       "code length in bits for minor (%d) "
 		       "must be less than %d!\n",
 		       p->minor, BUFLEN*8);
@@ -246,17 +271,17 @@
 		goto out;
 	}
 
-	printk("lirc_dev: lirc_register_plugin:"
+	printk("lirc_dev: lirc_register_plugin: "
 	       "sample_rate: %d\n",p->sample_rate);
 	if (p->sample_rate) {
 		if (2 > p->sample_rate || HZ < p->sample_rate) {
-			printk("lirc_dev: lirc_register_plugin:"
+			printk("lirc_dev: lirc_register_plugin: "
 			       "sample_rate must be between 2 and %d!\n", HZ);
 			err = -EBADRQC;
 			goto out;
 		}
 		if (!p->add_to_buf) {
-			printk("lirc_dev: lirc_register_plugin:"
+			printk("lirc_dev: lirc_register_plugin: "
 			       "add_to_buf cannot be NULL when "
 			       "sample_rate is set\n");
 			err = -EBADRQC;
@@ -264,7 +289,7 @@
 		}
 	} else if (!(p->fops && p->fops->read)
 		   && !p->get_queue && !p->rbuf) {
-		printk("lirc_dev: lirc_register_plugin:"
+		printk("lirc_dev: lirc_register_plugin: "
 		       "fops->read, get_queue and rbuf "
 		       "cannot all be NULL!\n");
 		err = -EBADRQC;
@@ -272,14 +297,21 @@
 	} else if (!p->get_queue && !p->rbuf) {
 		if (!(p->fops && p->fops->read && p->fops->poll) 
 		    || (!p->fops->ioctl && !p->ioctl)) {
-			printk("lirc_dev: lirc_register_plugin:"
+			printk("lirc_dev: lirc_register_plugin: "
 			       "neither read, poll nor ioctl can be NULL!\n");
 			err = -EBADRQC;
 			goto out;
 		}
 	}
 
-	down_interruptible(&plugin_lock);
+	if (p->owner == NULL) {
+		printk(KERN_WARNING "lirc_dev: lirc_register_plugin: "
+				    "no module owner registered\n");
+		err = -EBADRQC;
+		goto out;
+	}
+
+	down(&plugin_lock);
 
 	minor = p->minor;
 
@@ -295,7 +327,7 @@
 			goto out_lock;
 		}
 	} else if (irctls[minor].p.minor != NOPLUG) {
-		printk("lirc_dev: lirc_register_plugin:"
+		printk("lirc_dev: lirc_register_plugin: "
 		       "minor (%d) just registered!\n", minor);
 		err = -EBUSY;
 		goto out_lock;
@@ -330,6 +362,7 @@
 			goto out_lock;
 		}
 	}
+	ir->chunk_size = ir->buf->chunk_size;
 
 	if (p->features==0)
 		p->features = (p->code_length > 8) ?
@@ -349,7 +382,7 @@
 			S_IFCHR|S_IRUSR|S_IWUSR,
 			DEV_LIRC "/%u", ir->p.minor);
 #endif
-	(void) class_simple_device_add(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor),
+	(void) class_device_create(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor),
 				       NULL, "lirc%u", ir->p.minor);
 
 	if(p->sample_rate || p->get_queue) {
@@ -357,7 +390,7 @@
 		ir->t_notify = &tn;
 		ir->tpid = kernel_thread(lirc_thread, (void*)ir, 0);
 		if (ir->tpid < 0) {
-			printk("lirc_dev: lirc_register_plugin:"
+			printk("lirc_dev: lirc_register_plugin: "
 			       "cannot run poll thread for minor = %d\n",
 			       p->minor);
 			err = -ECHILD;
@@ -366,6 +399,7 @@
 		down(&tn);
 		ir->t_notify = NULL;
 	}
+	ir->attached = 1;
 	up(&plugin_lock);
 
 /*
@@ -377,10 +411,11 @@
 #endif
 	dprintk("lirc_dev: plugin %s registered at minor number = %d\n",
 		ir->p.name, ir->p.minor);
+	p->minor = minor;
 	return minor;
 	
 out_sysfs:
-	class_simple_device_remove(MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
+	class_device_destroy(lirc_class,MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
 #ifdef LIRC_HAVE_DEVFS_24
 	devfs_unregister(ir->devfs_handle);
 #endif
@@ -403,30 +438,23 @@
 	DECLARE_MUTEX_LOCKED(tn2);
 
 	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
-		printk("lirc_dev: lirc_unregister_plugin:"
-		       "\" minor\" must be between 0 and %d!\n",
+		printk("lirc_dev: lirc_unregister_plugin: "
+		       "\"minor\" must be between 0 and %d!\n",
 		       MAX_IRCTL_DEVICES-1);
 		return -EBADRQC;
 	}
 
 	ir = &irctls[minor];
 
-	down_interruptible(&plugin_lock);
+	down(&plugin_lock);
 
 	if (ir->p.minor != minor) {
-		printk("lirc_dev: lirc_unregister_plugin:"
+		printk("lirc_dev: lirc_unregister_plugin: "
 		       "minor (%d) device not registered!", minor);
 		up(&plugin_lock);
 		return -ENOENT;
 	}
 
-	if (ir->open) {
-		printk("lirc_dev: lirc_unregister_plugin:"
-		       "plugin %s[%d] in use!", ir->p.name, ir->p.minor);
-		up(&plugin_lock);
-		return -EBUSY;
-	}
-
 	/* end up polling thread */
 	if (ir->tpid >= 0) {
 		ir->t_notify = &tn;
@@ -452,20 +480,20 @@
 	dprintk("lirc_dev: plugin %s unregistered from minor number = %d\n",
 		ir->p.name, ir->p.minor);
 
-#ifdef LIRC_HAVE_DEVFS_24
-	devfs_unregister(ir->devfs_handle);
-#endif
-#ifdef LIRC_HAVE_DEVFS_26
-	devfs_remove(DEV_LIRC "/%u", ir->p.minor);
-#endif
-	class_simple_device_remove(MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
-
-	if (ir->buf != ir->p.rbuf){
-		lirc_buffer_free(ir->buf);
-		kfree(ir->buf);
+	ir->attached = 0;
+	if (ir->open) {
+		dprintk(LOGHEAD "releasing opened plugin\n",
+			ir->p.name, ir->p.minor);
+		wake_up_interruptible(&ir->buf->wait_poll);
+		down(&ir->buffer_sem);
+		ir->p.set_use_dec(ir->p.data);
+		module_put(ir->p.owner);
+		up(&ir->buffer_sem);
+	}
+	else
+	{
+		cleanup(ir);
 	}
-	ir->buf = NULL;
-	init_irctl(ir);
 	up(&plugin_lock);
 
 /*
@@ -501,7 +529,10 @@
 	if(ir->p.fops && ir->p.fops->open)
 		return ir->p.fops->open(inode, file);
 
-	down_interruptible(&plugin_lock);
+	if (down_interruptible(&plugin_lock))
+	{
+		return -ERESTARTSYS;
+	}
 
 	if (ir->p.minor == NOPLUG) {
 		up(&plugin_lock);
@@ -530,12 +561,9 @@
 		++ir->open;
 		retval = ir->p.set_use_inc(ir->p.data);
 		
-		up(&plugin_lock);
-		
 		if (retval != SUCCESS) {
 			module_put(ir->p.owner);
 			--ir->open;
-			return retval;
 		}
 	}
 	else
@@ -547,9 +575,10 @@
 		retval = -ENODEV;
 	}
 
-	dprintk(LOGHEAD "open result = %d\n", ir->p.name, ir->p.minor, SUCCESS);
+	dprintk(LOGHEAD "open result = %d\n", ir->p.name, ir->p.minor, retval);
+	up(&plugin_lock);
 
-	return SUCCESS;
+	return retval;
 }
 
 /*
@@ -565,11 +594,21 @@
 	if(ir->p.fops && ir->p.fops->release)
 		return ir->p.fops->release(inode, file);
 
-	down_interruptible(&plugin_lock);
+	if (down_interruptible(&plugin_lock))
+	{
+		return -ERESTARTSYS;
+	}
 
 	--ir->open;
-	ir->p.set_use_dec(ir->p.data);
-	module_put(ir->p.owner);
+	if(ir->attached)
+	{
+		ir->p.set_use_dec(ir->p.data);
+		module_put(ir->p.owner);
+	}
+	else
+	{
+		cleanup(ir);
+	}
 
 	up(&plugin_lock);
 
@@ -582,6 +621,7 @@
 static unsigned int irctl_poll(struct file *file, poll_table *wait)
 {
 	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+	unsigned int ret;
 
 	dprintk(LOGHEAD "poll called\n", ir->p.name, ir->p.minor);
 
@@ -589,13 +629,23 @@
 	if(ir->p.fops && ir->p.fops->poll)
 		return ir->p.fops->poll(file, wait);
 
+	down(&ir->buffer_sem);
+	if(!ir->attached)
+	{
+		up(&ir->buffer_sem);
+		return POLLERR;
+	}
+
 	poll_wait(file, &ir->buf->wait_poll, wait);
 
 	dprintk(LOGHEAD "poll result = %s\n",
 		ir->p.name, ir->p.minor, 
 		lirc_buffer_empty(ir->buf) ? "0" : "POLLIN|POLLRDNORM");
 
-	return lirc_buffer_empty(ir->buf) ? 0 : (POLLIN|POLLRDNORM);
+	ret = lirc_buffer_empty(ir->buf) ? 0 : (POLLIN|POLLRDNORM);
+	
+	up(&ir->buffer_sem);
+	return ret;
 }
 
 /*
@@ -608,14 +658,14 @@
 	int result;
 	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
 
-	dprintk(LOGHEAD "ioctl called (%u)\n",
+	dprintk(LOGHEAD "ioctl called (0x%x)\n",
 		ir->p.name, ir->p.minor, cmd);
 
 	/* if the plugin has a ioctl function use it instead */
 	if(ir->p.fops && ir->p.fops->ioctl)
 		return ir->p.fops->ioctl(inode, file, cmd, arg);
 
-	if (ir->p.minor == NOPLUG) {
+	if (ir->p.minor == NOPLUG || !ir->attached) {
 		dprintk(LOGHEAD "ioctl result = -ENODEV\n",
 			ir->p.name, ir->p.minor);
 		return -ENODEV;
@@ -678,7 +728,7 @@
 			  loff_t *ppos)     
 {
 	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
-	unsigned char buf[ir->buf->chunk_size];
+	unsigned char buf[ir->chunk_size];
 	int ret=0, written=0;
 	DECLARE_WAITQUEUE(wait, current);
 
@@ -688,9 +738,20 @@
 	if(ir->p.fops && ir->p.fops->read)
 		return ir->p.fops->read(file, buffer, length, ppos);
 
+	if(down_interruptible(&ir->buffer_sem))
+	{
+		return -ERESTARTSYS;
+	}
+	if(!ir->attached)
+	{
+		up(&ir->buffer_sem);
+		return -ENODEV;
+	}
+
 	if (length % ir->buf->chunk_size) {
 		dprintk(LOGHEAD "read result = -EINVAL\n",
 			ir->p.name, ir->p.minor);
+		up(&ir->buffer_sem);
 		return -EINVAL;
 	}
 
@@ -699,7 +760,7 @@
 	 * beetwen while condition checking and scheduling)
 	 */
 	add_wait_queue(&ir->buf->wait_poll, &wait);
-	current->state = TASK_INTERRUPTIBLE;
+	set_current_state(TASK_INTERRUPTIBLE);
 
 	/* while we did't provide 'length' bytes, device is opened in blocking
 	 * mode and 'copy_to_user' is happy, wait for data.
@@ -712,21 +773,20 @@
 			 * -ERESTARTSYS */
 			if (written) break;
 			if (file->f_flags & O_NONBLOCK) {
-				dprintk(LOGHEAD "read result = -EWOULDBLOCK\n", 
-						ir->p.name, ir->p.minor);
-				remove_wait_queue(&ir->buf->wait_poll, &wait);
-				current->state = TASK_RUNNING;
-				return -EWOULDBLOCK;
+				ret = -EWOULDBLOCK;
+				break;
 			}
 			if (signal_pending(current)) {
-				dprintk(LOGHEAD "read result = -ERESTARTSYS\n", 
-						ir->p.name, ir->p.minor);
-				remove_wait_queue(&ir->buf->wait_poll, &wait);
-				current->state = TASK_RUNNING;
-				return -ERESTARTSYS;
+				ret = -ERESTARTSYS;
+				break;
 			}
 			schedule();
-			current->state = TASK_INTERRUPTIBLE;
+			set_current_state(TASK_INTERRUPTIBLE);
+			if(!ir->attached)
+			{
+				ret = -ENODEV;
+				break;
+			}
 		} else {
 			lirc_buffer_read_1(ir->buf, buf);
 			ret = copy_to_user((void *)buffer+written, buf,
@@ -736,12 +796,13 @@
 	}
 
 	remove_wait_queue(&ir->buf->wait_poll, &wait);
-	current->state = TASK_RUNNING;
-
+	set_current_state(TASK_RUNNING);
+	up(&ir->buffer_sem);
+	
 	dprintk(LOGHEAD "read result = %s (%d)\n",
 		ir->p.name, ir->p.minor, ret ? "-EFAULT" : "OK", ret);
 
-	return ret ? -EFAULT : written;
+	return ret ? ret : written;
 }
 
 static ssize_t irctl_write(struct file *file, const char *buffer,
@@ -749,12 +810,17 @@
 {
 	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
 
-	dprintk(LOGHEAD "read called\n", ir->p.name, ir->p.minor);
+	dprintk(LOGHEAD "write called\n", ir->p.name, ir->p.minor);
 
 	/* if the plugin has a specific read function use it instead */
 	if(ir->p.fops && ir->p.fops->write)
 		return ir->p.fops->write(file, buffer, length, ppos);
 
+	if(!ir->attached)
+	{
+		return -ENODEV;
+	}
+
 	return -EINVAL;
 }
 
@@ -789,9 +855,9 @@
 		goto out;
 	}
 
-	lirc_class = class_simple_create(THIS_MODULE, "lirc");
+	lirc_class = class_create(THIS_MODULE, "lirc");
 	if(IS_ERR(lirc_class)) {
-		printk(KERN_ERR "lirc_dev: class_simple_create failed\n");
+		printk(KERN_ERR "lirc_dev: class_create failed\n");
 		goto out_unregister;
 	}
 
@@ -829,7 +895,7 @@
 	int ret;
 
 	ret = unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
-	class_simple_destroy(lirc_class);
+	class_destroy(lirc_class);
 
 	if(ret)
 		printk("lirc_dev: error in module_unregister_chrdev: %d\n", ret);

Reply via email to