linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 6/7] usbhid: add /proc/usbhid/device_quirks handler
@ 2007-03-19  4:22 Paul Walmsley
  0 siblings, 0 replies; only message in thread
From: Paul Walmsley @ 2007-03-19  4:22 UTC (permalink / raw)
  To: linux-input


From: Paul Walmsley <paul@booyaka.com>

Add a procfs file, /proc/usbhid/device_quirks.  Reading this file returns
a list of all currently active USB HID quirks.  Writing a specially-formatted
string (documented in the Kconfig option USBHID_PROC_FS text) will add, 
replace, or remove existing quirks.

Signed-off-by: Paul Walmsley <paul@booyaka.com>

---
  dev/drivers/usb/input/hid-core.c |  188 ++++++++++++++++++++++++++++++++++++++-
  1 file changed, 185 insertions(+), 3 deletions(-)

Index: linux/dev/drivers/usb/input/hid-core.c
===================================================================
--- linux.orig/dev/drivers/usb/input/hid-core.c
+++ linux/dev/drivers/usb/input/hid-core.c
@@ -5,6 +5,7 @@
   *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
   *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
   *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
   */

  /*
@@ -27,6 +28,9 @@
  #include <linux/input.h>
  #include <linux/wait.h>
  #include <linux/rwsem.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>

  #include <linux/usb.h>

@@ -1167,20 +1171,192 @@ static int modify_quirk_list(const char
  	/* If we didn't edit the list, we mustn't have found an existing
  	 * entry, so add the new quirk to the end of the list
  	 */
-	if (!list_edited)
+	if (!list_edited) {
  		if (q_new->hid_bl_item.quirks != 0) {
  			list_add_tail(&q_new->node, &quirks_list);
  		} else {
-			/* Cannot add a quirk of 0x0 */
+			/* Adding a quirk of 0x0 is a no-op */
  			kfree(q_new);
-			return -EINVAL;
  		}
+	}

  	up_write(&quirks_list_rwsem);

  	return 0;
  }

+#ifdef CONFIG_USBHID_PROC_FS
+
+/* seq_file-related functions */
+
+static void *seq_device_quirks_start(struct seq_file *m,
+                                     loff_t *pos)
+{
+	struct quirks_list_struct *q;
+	loff_t off = 0;
+
+	down_read(&quirks_list_rwsem);
+
+	if (*pos == 0)
+		return SEQ_START_TOKEN;
+
+	/* Fast-forward through the list, looking for entry *pos */
+	list_for_each_entry(q, &quirks_list, node) {
+		if ((*pos - 1) == off++)
+			return q;
+	}
+
+	/* pos was larger than the length of the list */
+	return NULL;
+}
+
+static void *seq_device_quirks_next(struct seq_file *m,
+                                    void *v,
+                                    loff_t *pos)
+{
+	struct list_head *q;
+
+	if (v == SEQ_START_TOKEN)
+		q = &quirks_list;
+	else
+		q = &((struct quirks_list_struct *)v)->node;
+
+	if (list_is_last(q, &quirks_list))
+		return NULL;
+
+	++*pos;
+
+	return list_entry(q->next, struct quirks_list_struct, node);
+}
+
+static void seq_device_quirks_stop(struct seq_file *m,
+				   void *v)
+{
+	up_read(&quirks_list_rwsem);
+}
+
+static int seq_device_quirks_show(struct seq_file *m,
+                                  void *v)
+{
+	struct quirks_list_struct *q;
+
+	if (v == SEQ_START_TOKEN)
+		seq_printf(m, "vendor\tprod\tpmask\tquirks\n");
+	else {
+		q = v;
+		seq_printf(m, "0x%04hx\t0x%04hx\t0x%04hx\t0x%08x\n",
+			   q->hid_bl_item.idVendor,
+			   q->hid_bl_item.idProduct,
+			   q->hid_bl_item.idProductMask,
+			   q->hid_bl_item.quirks);
+	}
+
+	return 0;
+}
+
+static const struct seq_operations device_quirks_seqops = {
+	.start         = seq_device_quirks_start,
+	.next          = seq_device_quirks_next,
+	.stop          = seq_device_quirks_stop,
+	.show          = seq_device_quirks_show,
+};
+
+
+/* procfs-related functions */
+
+static int proc_open_device_quirks(struct inode *inode,
+                                   struct file *file)
+{
+	return seq_open(file, &device_quirks_seqops);
+}
+
+static ssize_t proc_write_device_quirks(struct file *file,
+                                        const char *buffer,
+                                        size_t count,
+                                        loff_t *ppos)
+{
+	char in_buf[MAX_USBHID_QUIRK_LEN + 1];
+	ssize_t len;
+
+	/* Copy the command string in from userspace, ensure that it is
+	 * null-terminated
+	 */
+	len = (count > MAX_USBHID_QUIRK_LEN) ? MAX_USBHID_QUIRK_LEN : count;
+
+	if (copy_from_user(in_buf, buffer, len))
+		return -EFAULT;
+
+	in_buf[len] = '\0';
+
+	modify_quirk_list(in_buf);
+
+	return len;
+}
+
+
+static const struct file_operations proc_device_quirks_fops = {
+	.open		= proc_open_device_quirks,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+	.write		= proc_write_device_quirks,
+};
+
+/**
+ * usbhid_procfs_init: set up the usbhid/, usbhid/device_quirks procfs entries
+ *
+ * Description:
+ *     Creates the procfs directory usbhid/ and the device_quirks
+ *     file inside it.
+ *
+ * Returns: 0 OK, -error on failure.
+ */
+static int usbhid_procfs_init(void)
+{
+	struct proc_dir_entry *qf, *usbhid_dir;
+
+	/* Create usbhid/ directory */
+	usbhid_dir = proc_mkdir("usbhid", NULL);
+	if (usbhid_dir == NULL) {
+		dbg("Could not create procfs directory");
+		return -ENOMEM;
+	}
+	usbhid_dir->owner = THIS_MODULE;
+
+	/* Create usbhid/device_quirks file */
+	qf = create_proc_entry("device_quirks", 0644, usbhid_dir);
+	if (qf == NULL) {
+		dbg("Could not create procfs usbhid/device_quirks file");
+		return -ENOMEM;
+	}
+	qf->owner = THIS_MODULE;
+	qf->proc_fops = &proc_device_quirks_fops;
+
+	return 0;
+}
+
+/**
+ * usbhid_procfs_exit: remove the usbhid/, usbhid/device_quirks procfs entries
+ *
+ * Description:
+ *     Removes the procfs directory usbhid/ and the device_quirks
+ *     file inside it.
+ *
+ * Returns: nothing
+ */
+static void usbhid_procfs_exit(void)
+{
+	remove_proc_entry("usbhid/device_quirks", NULL);
+	remove_proc_entry("usbhid", NULL);
+}
+
+#else /* CONFIG_USBHID_PROC_FS */
+
+static inline int usbhid_procfs_init(void) { return 0; }
+static inline void usbhid_procfs_exit(void) {}
+
+#endif /* CONFIG_USBHID_PROC_FS */
+

  /**
   * usbhid_quirks_init: copy the hid_blacklist[] into the quirks_list
@@ -1682,6 +1858,9 @@ static int __init hid_init(void)
  	retval = usbhid_quirks_init();
  	if (retval)
                  goto usbhid_quirks_init_fail;
+	retval = usbhid_procfs_init();
+	if (retval)
+                goto usbhid_procfs_init_fail;
  	retval = hiddev_init();
  	if (retval)
  		goto hiddev_init_fail;
@@ -1695,6 +1874,8 @@ usb_register_fail:
  	hiddev_exit();
  hiddev_init_fail:
          usbhid_quirks_exit();
+usbhid_procfs_init_fail:
+	usbhid_procfs_exit();
  usbhid_quirks_init_fail:
  	return retval;
  }
@@ -1703,6 +1884,7 @@ static void __exit hid_exit(void)
  {
  	usb_deregister(&hid_driver);
  	hiddev_exit();
+	usbhid_procfs_exit();
  	usbhid_quirks_exit();
  }

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2007-03-19  4:22 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-03-19  4:22 [PATCH 6/7] usbhid: add /proc/usbhid/device_quirks handler Paul Walmsley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).