--- linux-2.6.7-rc1/drivers/input/serio/serio.c 2004/06/02 16:47:33 1.1.1.1 +++ linux-2.6.7-rc1.serio_switch/drivers/input/serio/serio.c 2004/06/02 16:55:34 1.1.1.2 @@ -35,20 +35,22 @@ #include #include #include #include #include #include #include #include #include #include +#include +#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Serio abstraction core"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(serio_interrupt); EXPORT_SYMBOL(serio_register_port); EXPORT_SYMBOL(serio_register_port_delayed); EXPORT_SYMBOL(__serio_register_port); EXPORT_SYMBOL(serio_unregister_port); @@ -66,20 +68,26 @@ struct serio *serio; struct list_head node; }; static DECLARE_MUTEX(serio_sem); static LIST_HEAD(serio_list); static LIST_HEAD(serio_dev_list); static LIST_HEAD(serio_event_list); static int serio_pid; +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_bus_input_ss_dir; +static struct proc_dir_entry *proc_bus_input_ss_ports_dir; +static struct proc_dir_entry *proc_bus_input_ss_devices_dir; +#endif + static void serio_find_dev(struct serio *serio) { struct serio_dev *dev; list_for_each_entry(dev, &serio_dev_list, node) { if (serio->dev) break; if (dev->connect) dev->connect(serio, dev); } @@ -215,28 +223,50 @@ /* * Submits register request to kseriod for subsequent execution. * Can be used when it is not obvious whether the serio_sem is * taken or not and when delayed execution is feasible. */ void serio_register_port_delayed(struct serio *serio) { serio_queue_event(serio, SERIO_REGISTER_PORT); } +#ifdef CONFIG_PROC_FS +static +int serio_switch_port_proc_file_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +static +int serio_switch_port_proc_file_write(struct file *file, + const char __user *buffer, + unsigned long count, void *data); +#endif + /* * Should only be called directly if serio_sem has already been taken, * for example when unregistering a serio from other input device's * connect() function. */ void __serio_register_port(struct serio *serio) { list_add_tail(&serio->node, &serio_list); + +#ifdef CONFIG_PROC_FS + if (proc_bus_input_ss_ports_dir) { + struct proc_dir_entry *proc_entry; + + proc_entry = create_proc_read_entry(serio->name, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH, proc_bus_input_ss_ports_dir, + serio_switch_port_proc_file_read, serio); + proc_entry->owner = THIS_MODULE; + proc_entry->write_proc = serio_switch_port_proc_file_write; + } +#endif + serio_find_dev(serio); } void serio_unregister_port(struct serio *serio) { down(&serio_sem); __serio_unregister_port(serio); up(&serio_sem); } @@ -251,40 +281,85 @@ } /* * Should only be called directly if serio_sem has already been taken, * for example when unregistering a serio from other input device's * disconnect() function. */ void __serio_unregister_port(struct serio *serio) { serio_invalidate_pending_events(serio); +#ifdef CONFIG_PROC_FS + if (proc_bus_input_ss_ports_dir) { + remove_proc_entry(serio->name, proc_bus_input_ss_ports_dir); + } +#endif list_del_init(&serio->node); if (serio->dev && serio->dev->disconnect) serio->dev->disconnect(serio); } +#ifdef CONFIG_PROC_FS +static +int serio_switch_device_proc_file_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +#endif + void serio_register_device(struct serio_dev *dev) { struct serio *serio; down(&serio_sem); list_add_tail(&dev->node, &serio_dev_list); list_for_each_entry(serio, &serio_list, node) if (!serio->dev && dev->connect) dev->connect(serio, dev); up(&serio_sem); + +#ifdef CONFIG_PROC_FS + if (!dev->name) { + /* some modules do not set the dev->name! :( */ + dev->name = kmalloc(16, GFP_KERNEL); + if (!dev->name) + dev->name = "out of memory"; + else + snprintf(dev->name, 16, "xx%p", dev); + } + + if (proc_bus_input_ss_devices_dir) { + struct proc_dir_entry *proc_entry; + + proc_entry = create_proc_read_entry(dev->name, 0, proc_bus_input_ss_devices_dir, + serio_switch_device_proc_file_read, dev); + proc_entry->owner = THIS_MODULE; + } +#endif } void serio_unregister_device(struct serio_dev *dev) { struct serio *serio; +#ifdef CONFIG_PROC_FS + if (proc_bus_input_ss_devices_dir) { + remove_proc_entry(dev->name, proc_bus_input_ss_devices_dir); + } + + if (dev->name[0] == 'x' && dev->name[1] == 'x') { + /* assume this name was generated by us */ + kfree(dev->name); + dev->name = NULL; + } else if (strcpy(dev->name, "out of memory")==0) { + /* couldn't kmalloc */ + dev->name = NULL; + } +#endif + down(&serio_sem); list_del_init(&dev->node); list_for_each_entry(serio, &serio_list, node) { if (serio->dev == dev && dev->disconnect) dev->disconnect(serio); serio_find_dev(serio); } up(&serio_sem); } @@ -300,34 +375,218 @@ return 0; } /* called from serio_dev->connect/disconnect methods under serio_sem */ void serio_close(struct serio *serio) { serio->close(serio); serio->dev = NULL; } + +#ifdef CONFIG_PROC_FS +static +struct proc_dir_entry *get_proc_bus_input(void) +{ + struct proc_dir_entry *entry; + for (entry = proc_bus->subdir; entry; entry = entry->next) + if (strcmp(entry->name, "input")==0) { + return entry; + } + return NULL; +} +#endif + +#ifdef CONFIG_PROC_FS +static +int serio_switch_port_proc_file_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct serio *serio = data; + struct serio_dev *dev = serio->dev; + int len, c; + + if (!dev) + strcpy(page, "(unconnected)\n"); + else + strcat(strcpy(page, dev->name), "\n"); + + len = strlen(page); + c = len - off; + if (c<0) c = 0; + *start = page + off; + *eof = 1; + return countdev && serio->dev->disconnect) + serio->dev->disconnect(serio); + goto success; + } else { + /* find the device to connect to */ + struct serio_dev *dev; + + down(&serio_sem); + list_for_each_entry(dev, &serio_dev_list, node) { + if (strcmp(kbuffer, dev->name) != 0) + continue; /* not match */ + + /* found */ + + /* disconnect old connect first */ + if (serio->dev && serio->dev->disconnect) + serio->dev->disconnect(serio); + + /* now, connect to the found device */ + if (dev->connect) + dev->connect(serio, dev); + + up(&serio_sem); + if (serio->dev == dev) + goto success; /* done */ + else { + printk(KERN_ERR "Inappropriate serio device '%s'\n", kbuffer); + r = -EINVAL; + goto out; + } + } + up(&serio_sem); + + /* not found */ + printk(KERN_ERR "Bad serio device '%s'\n", kbuffer); + r = -EINVAL; + goto out; + } +success: + r = count; +out: + kfree(kbuffer); + return r; +} + + +static +int serio_switch_device_proc_file_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct serio_dev *dev = data; + struct serio *serio; + int len, cnt = 0; + off_t at = 0; + + down(&serio_sem); + list_for_each_entry(serio, &serio_list, node) { + if (serio->dev != dev) + continue; + len = sprintf(page, "%s\n", serio->name); + at += len; + + if (at >= off) { + if (!*start) { + *start = page + (off - (at - len)); + cnt = at - off; + } else cnt += len; + page += len; + if (cnt >= count) + break; + } + } + + if (&serio->node == &serio_list) + *eof = 1; + up(&serio_sem); + + return (count > cnt) ? cnt : count; +} +#endif + + static int __init serio_init(void) { int pid; pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL); if (!pid) { printk(KERN_WARNING "serio: Failed to start kseriod\n"); return -1; } serio_pid = pid; +#ifdef CONFIG_PROC_FS + { + struct proc_dir_entry *proc_bus_input_dir = get_proc_bus_input(); + if (proc_bus_input_dir) { + proc_bus_input_ss_dir = proc_mkdir("serio_switch", proc_bus_input_dir); + if (proc_bus_input_ss_dir) { + proc_bus_input_ss_dir->owner = THIS_MODULE; + + proc_bus_input_ss_ports_dir = proc_mkdir("ports", proc_bus_input_ss_dir); + if (proc_bus_input_ss_ports_dir) + proc_bus_input_ss_ports_dir->owner = THIS_MODULE; + else + printk(KERN_WARNING "/proc/bus/input/serio/ports/ cannot be created\n"); + + proc_bus_input_ss_devices_dir = proc_mkdir("devices", proc_bus_input_ss_dir); + if (proc_bus_input_ss_devices_dir) + proc_bus_input_ss_devices_dir->owner = THIS_MODULE; + else + printk(KERN_WARNING "/proc/bus/input/serio/devices/ cannot be created\n"); + } else { + printk(KERN_WARNING "/proc/bus/input/serio/ cannot be created\n"); + } + } else { + printk(KERN_WARNING "/proc/bus/input/ not found\n"); + } + } +#endif + return 0; } static void __exit serio_exit(void) { +#ifdef CONFIG_PROC_FS + { + if (proc_bus_input_ss_devices_dir) { + proc_bus_input_ss_devices_dir->owner = NULL; + remove_proc_entry(proc_bus_input_ss_devices_dir->name, + proc_bus_input_ss_devices_dir->parent); + } + if (proc_bus_input_ss_ports_dir) { + proc_bus_input_ss_ports_dir->owner = NULL; + remove_proc_entry(proc_bus_input_ss_ports_dir->name, + proc_bus_input_ss_ports_dir->parent); + } + if (proc_bus_input_ss_dir) { + proc_bus_input_ss_dir->owner = NULL; + remove_proc_entry(proc_bus_input_ss_dir->name, + proc_bus_input_ss_dir->parent); + } + } +#endif kill_proc(serio_pid, SIGTERM, 1); wait_for_completion(&serio_exited); } module_init(serio_init); module_exit(serio_exit);