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);