linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
@ 2008-06-27 12:24 Oliver Neukum
  2008-06-28 15:03 ` Alfred E. Heggestad
  0 siblings, 1 reply; 15+ messages in thread
From: Oliver Neukum @ 2008-06-27 12:24 UTC (permalink / raw)
  To: Alfred E. Heggestad; +Cc: Dmitry Torokhov, linux-input, linux-kernel

Am Mittwoch 25 Juni 2008 22:07:34 schrieb Alfred E. Heggestad:

Very well

- urb->status is about to go away and replaced by a parameter
- reliably kill two URBs submitting each other cannot be done with
  usb_kill_urb() alone
- you close the device but leave the buzzer on
- no support for suspend/resume
- no support for pre/post_reset

Could you test this new additional patch? The original patch had some
issues I corrected.

	Regards
		Oliver

---

--- linux-2.6.26-sierra/drivers/input/misc/cm109.alt.c	2008-06-26 08:15:16.000000000 +0200
+++ linux-2.6.26-sierra/drivers/input/misc/cm109.c	2008-06-27 14:14:54.000000000 +0200
@@ -93,6 +93,7 @@ enum {USB_PKT_LEN = sizeof(struct cm109_
 struct cm109_dev {
 	struct input_dev *idev;	 /* input device */
 	struct usb_device *udev; /* usb device */
+	struct usb_interface *intf;
 
 	/* irq input channel */
 	struct cm109_ctl_packet *irq_data;
@@ -107,7 +108,12 @@ struct cm109_dev {
 	struct urb *urb_ctl;
 
 	spinlock_t submit_lock;
-	int disconnecting;
+	char disconnecting:1;
+	char shutting_down:1;
+	char buzz_state:1;
+	char open:1;
+	char resetting:1;
+	wait_queue_head_t wait;
 
 	char phys[64];		/* physical device path */
 	int key_code;		/* last reported key */
@@ -115,6 +121,9 @@ struct cm109_dev {
 	u8 gpi;			/* Cached value of GPI (high nibble) */
 };
 
+static DEFINE_MUTEX(reset_mutex);
+static int buzz(struct cm109_dev *dev, int on);
+
 /******************************************************************************
  * CM109 key interface
  *****************************************************************************/
@@ -279,7 +288,7 @@ static void report_key(struct cm109_dev
 static void urb_irq_callback(struct urb *urb)
 {
 	struct cm109_dev *dev = urb->context;
-	int ret;
+	int ret, status = urb->status;
 
 #if CM109_DEBUG
 	info("### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x",
@@ -290,10 +299,10 @@ static void urb_irq_callback(struct urb
 	     dev->keybit);
 #endif
 
-	if (urb->status) {
-		if (-ESHUTDOWN == urb->status)
+	if (status) {
+		if (-ESHUTDOWN == status)
 			return;
-		err("%s - urb status %d", __FUNCTION__, urb->status);
+		err("%s - urb status %d", __func__, status);
 	}
 
 	/* Scan key column */
@@ -319,15 +328,19 @@ static void urb_irq_callback(struct urb
 	dev->ctl_data->byte[HID_OR2] = dev->keybit;
 
       out:
-	ret = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
-	if (ret)
-		err("%s - usb_submit_urb failed %d", __FUNCTION__, ret);
+	spin_lock(&dev->submit_lock);
+	if (!dev->shutting_down) {
+		ret = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+		if (ret)
+			err("%s - usb_submit_urb failed %d", __func__, ret);
+	}
+	spin_unlock(&dev->submit_lock);
 }
 
 static void urb_ctl_callback(struct urb *urb)
 {
 	struct cm109_dev *dev = urb->context;
-	int ret = 0;
+	int ret = 0, status = urb->status;
 
 #if CM109_DEBUG
 	info("### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]",
@@ -337,24 +350,37 @@ static void urb_ctl_callback(struct urb
 	     dev->ctl_data->byte[3]);
 #endif
 
-	if (urb->status)
-		err("%s - urb status %d", __FUNCTION__, urb->status);
+	if (status)
+		err("%s - urb status %d", __func__, status);
 
 	spin_lock(&dev->submit_lock);
 	/* ask for a response */
-	if (!dev->disconnecting)
+	if (!dev->shutting_down)
 		ret = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
 	spin_unlock(&dev->submit_lock);
 
 	if (ret)
 		err("%s - usb_submit_urb failed %d", __FUNCTION__, ret);
+	wake_up(&dev->wait);
 }
 
 /******************************************************************************
  * input event interface
  *****************************************************************************/
 
-static DEFINE_SPINLOCK(cm109_buzz_lock);
+static void stop_traffic(struct cm109_dev *dev)
+{
+	spin_lock_irq(&dev->submit_lock);
+	dev->shutting_down = 1;
+	spin_unlock_irq(&dev->submit_lock);
+
+	usb_kill_urb(dev->urb_ctl);
+	usb_kill_urb(dev->urb_irq);
+
+	spin_lock_irq(&dev->submit_lock);
+	dev->shutting_down = 0;
+	spin_unlock_irq(&dev->submit_lock);
+}
 
 static int input_open(struct input_dev *idev)
 {
@@ -370,48 +396,85 @@ static int input_open(struct input_dev *
 	dev->ctl_data->byte[HID_OR2] = dev->keybit;
 	dev->ctl_data->byte[HID_OR3] = 0x00;
 
+	ret = usb_autopm_get_interface(dev->intf);
+	if (ret < 0) {
+		err("%s - cannot autoresume, result %d",
+		    __func__, ret);
+		return ret;
+	}
+
+	mutex_lock(&reset_mutex);
 	if ((ret = usb_submit_urb(dev->urb_ctl, GFP_KERNEL)) != 0) {
 		err("%s - usb_submit_urb failed with result %d",
-		    __FUNCTION__, ret);
+		    __func__, ret);
+		usb_autopm_put_interface(dev->intf);
+		mutex_unlock(&reset_mutex);
 		return ret;
 	}
 
+	dev->open = 1;
+	mutex_unlock(&reset_mutex);
+
 	return 0;
 }
 
 static void input_close(struct input_dev *idev)
 {
 	struct cm109_dev *dev = input_get_drvdata(idev);
+	int traffic = 0;
+	int r;
 
-	usb_kill_urb(dev->urb_ctl);
-	usb_kill_urb(dev->urb_irq);
+	dev->open = 0;
+	stop_traffic(dev);
+
+	spin_lock_irq(&dev->submit_lock);
+	if (dev->buzz_state) {
+		r = buzz(dev, 0);
+		spin_unlock_irq(&dev->submit_lock);
+		if (!r) {
+			wait_event(dev->wait, !dev->buzz_state);
+			traffic = 1;
+		}
+	} else {
+		spin_unlock_irq(&dev->submit_lock);
+	}
+	if (traffic)
+		stop_traffic(dev);
+
+	usb_autopm_put_interface(dev->intf);
 }
 
-static void buzz(struct cm109_dev *dev, int on)
+static int buzz(struct cm109_dev *dev, int on)
 {
-	int ret;
+	int ret = 0;
 
 	if (dev == NULL) {
 		err("buzz: dev is NULL");
-		return;
+		return -EINVAL;
 	}
 
 	dbg("Buzzer %s", on ? "on" : "off");
+	if (dev->resetting)
+		goto skip_io;
 	if (on)
 		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
 	else
 		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
 
 	ret = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
-	if (ret)
-		err("%s - usb_submit_urb failed %d", __FUNCTION__, ret);
+	if (ret) {
+		err("%s - usb_submit_urb failed %d", __func__, ret);
+	} else {
+skip_io:
+		dev->buzz_state = on ? 1 : 0;
+	}
+	return ret;
 }
 
 static int input_ev(struct input_dev *idev, unsigned int type,
 		    unsigned int code, int value)
 {
 	struct cm109_dev *dev = input_get_drvdata(idev);
-	unsigned long flags;
 
 #if CM109_DEBUG
 	info("input_ev: type=%u code=%u value=%d", type, code, value);
@@ -428,11 +491,7 @@ static int input_ev(struct input_dev *id
 		return -EINVAL;
 	}
 
-	spin_lock_irqsave(&cm109_buzz_lock, flags);
-	buzz(dev, value);
-	spin_unlock_irqrestore(&cm109_buzz_lock, flags);
-
-	return 0;
+	return buzz(dev, value);
 }
 
 
@@ -468,13 +527,12 @@ static const struct usb_device_id usb_ta
 	{}
 };
 
-static int usb_cleanup(struct cm109_dev *dev, int err)
+static void usb_cleanup(struct cm109_dev *dev, int err)
 {
 	if (dev == NULL)
-		return err;
+		return;
 
-	usb_kill_urb(dev->urb_irq);	/* parameter validation in core/urb */
-	usb_kill_urb(dev->urb_ctl);	/* parameter validation in core/urb */
+	stop_traffic(dev);
 
 	if (dev->idev) {
 		if (err)
@@ -495,8 +553,6 @@ static int usb_cleanup(struct cm109_dev
 	usb_free_urb(dev->urb_irq);	/* parameter validation in core/urb */
 	usb_free_urb(dev->urb_ctl);	/* parameter validation in core/urb */
 	kfree(dev);
-
-	return err;
 }
 
 static void usb_disconnect(struct usb_interface *interface)
@@ -505,11 +561,6 @@ static void usb_disconnect(struct usb_in
 
 	dev = usb_get_intfdata(interface);
 
-	/* Wait for URB idle */
-	spin_lock_irq(&dev->submit_lock);
-	dev->disconnecting = 1;
-	spin_unlock_irq(&dev->submit_lock);
-
 	usb_set_intfdata(interface, NULL);
 
 	usb_cleanup(dev, 0);
@@ -536,9 +587,9 @@ static int usb_probe(struct usb_interfac
 		return -ENOMEM;
 
 	spin_lock_init(&dev->submit_lock);
-	dev->disconnecting = 0;
 
 	dev->udev = udev;
+	dev->intf = intf;
 
 	dev->idev = input_dev = input_allocate_device();
 	if (!input_dev)
@@ -638,14 +689,81 @@ static int usb_probe(struct usb_interfac
 	return 0;
 
       err:
-	return usb_cleanup(dev, -ENOMEM);
+	usb_cleanup(dev, 1);
+	return -ENOMEM;
+}
+
+static int restore_state(struct cm109_dev *dev)
+{
+	int rv;
+
+	spin_lock_irq(&dev->submit_lock);
+	/* if not open, just restore buzz, else submit urb */
+	dev->shutting_down = dev->open;
+	spin_unlock_irq(&dev->submit_lock);
+	rv = buzz(dev, dev->buzz_state);
+	spin_lock_irq(&dev->submit_lock);
+	dev->shutting_down = 0;
+	spin_unlock_irq(&dev->submit_lock);
+
+	return rv;
+}
+
+static int usb_resume(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+	int rv;
+
+	rv = restore_state(dev);
+
+	return rv;
+}
+
+static int usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	stop_traffic(dev);
+	return 0;
+}
+
+static int usb_pre_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	mutex_lock(&reset_mutex);
+	spin_lock_irq(&dev->submit_lock);
+	dev->resetting = 1;
+	spin_unlock_irq(&dev->submit_lock);
+	stop_traffic(dev);
+
+	return 0;
+}
+
+static int usb_post_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+	int rv;
+
+	spin_lock_irq(&dev->submit_lock);
+	dev->resetting = 0;
+	spin_unlock_irq(&dev->submit_lock);
+	rv = restore_state(dev);
+	mutex_unlock(&reset_mutex);
+	return rv;
 }
 
 static struct usb_driver cm109_driver = {
-	.name       = "cm109",
-	.probe      = usb_probe,
-	.disconnect = usb_disconnect,
-	.id_table   = usb_table,
+	.name		= "cm109",
+	.probe		= usb_probe,
+	.disconnect	= usb_disconnect,
+	.resume		= usb_resume,
+	.reset_resume	= usb_resume,
+	.suspend	= usb_suspend,
+	.pre_reset	= usb_pre_reset,
+	.post_reset	= usb_post_reset,
+	.id_table	= usb_table,
+	.supports_autosuspend = 1,
 };
 
 static int __init select_keymap(void)
@@ -685,7 +803,7 @@ static int __init cm109_dev_init(void)
 
 	info(DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR);
 
-	return err;
+	return 0;
 }
 
 static void __exit cm109_dev_exit(void)

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-06-27 12:24 [PATCH] input: driver for USB VoIP phones with CM109 chipset #2 Oliver Neukum
@ 2008-06-28 15:03 ` Alfred E. Heggestad
  2008-06-30 11:06   ` Oliver Neukum
  2008-07-25 14:14   ` Dmitry Torokhov
  0 siblings, 2 replies; 15+ messages in thread
From: Alfred E. Heggestad @ 2008-06-28 15:03 UTC (permalink / raw)
  To: Oliver Neukum; +Cc: Dmitry Torokhov, linux-input, linux-kernel

Oliver Neukum wrote:
> Am Mittwoch 25 Juni 2008 22:07:34 schrieb Alfred E. Heggestad:
> 
> Very well
> 
> - urb->status is about to go away and replaced by a parameter
> - reliably kill two URBs submitting each other cannot be done with
>   usb_kill_urb() alone
> - you close the device but leave the buzzer on
> - no support for suspend/resume
> - no support for pre/post_reset
> 
> Could you test this new additional patch? The original patch had some
> issues I corrected.
> 

Hi Oliver,

many many thanks for your patch and your suggested code improvements.
I have tested your patch and I have some comments:

* The first version of your patch was tested, and it applied cleanly
   and compiled with out any errors. The module was loaded OK, but when
   I inserted the CM109-device into the USB-port, the machine crashed.
   I fixed that issue by adding this to the usb_probe() function:

	init_waitqueue_head(&dev->wait);

* The second version of your patch was applied with some local mods.
   This is now working as expected.

* I have tested suspend/resume with 2.6.25.9 and TuxOnIce 3.0-rc7
   and it works fine (cm109 device was plugged in during suspend).
   During suspend I get this in syslog:

     drivers/input/misc/cm109.c: cm109: usb_suspend (event=1)
     drivers/input/misc/cm109.c: urb_irq_callback: urb status -2


* The flag "char disconnecting:1" is no longer used in your second
   patch, and has been removed from the struct.

* When the buzzer is triggered by SND_TONE/SND_BELL, I get these
   warnings in syslog:

     drivers/input/misc/cm109.c: urb_ctl_callback: usb_submit_urb failed -22
     drivers/input/misc/cm109.c: buzz: usb_submit_urb failed -22
     drivers/input/misc/cm109.c: urb_ctl_callback: usb_submit_urb failed -22
     drivers/input/misc/cm109.c: buzz: usb_submit_urb failed -22

   It seems that the ctl URB is shared between buzzer and keypress
   handling. I am thinking about adding a new URB just for the buzzer,
   but I am not sure if that really is necesary. Any suggestions?


OK, once again - many thanks for your input


/alfred


> 	Regards
> 		Oliver
> 
> ---
> 

new patch:



diff -uprN -X linux-2.6.25/Documentation/dontdiff linux-2.6.25-orig/drivers/input/misc/cm109.c linux-2.6.25/drivers/input/misc/cm109.c
--- linux-2.6.25-orig/drivers/input/misc/cm109.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25/drivers/input/misc/cm109.c	2008-06-28 16:46:36.000000000 +0200
@@ -0,0 +1,829 @@
+/*
+ * Driver for the VoIP USB phones with CM109 chipsets.
+ *
+ * Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2.
+ */
+
+/*
+ *   Tested devices:
+ *	- Komunikate KIP1000
+ *	- Genius G-talk
+ *	- Allied-Telesis Corega USBPH01
+ *	- ...
+ *
+ * This driver is based on the yealink.c driver
+ *
+ * Thanks to:
+ *   - Authors of yealink.c
+ *   - Thomas Reitmayr
+ *   - Oliver Neukum for good review comments and code
+ *   - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
+ *
+ * Todo:
+ *   - Fix bug with buzz and urb_ctl_callback:usb_submit_urb() -EINVAL
+ *   - Fix KEY_KPPOUND
+ *   - Read/write EEPROM
+ *   - Report input events volume up/down
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+
+#define CM109_DEBUG 0
+
+#define DRIVER_VERSION "20080628"
+#define DRIVER_AUTHOR  "Alfred E. Heggestad"
+#define DRIVER_DESC    "CM109 phone driver"
+
+static char *phone = "kip1000";
+module_param(phone, charp, S_IRUSR);
+MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01}");
+
+
+enum {
+	/* HID Registers */
+	HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down  */
+	HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0       */
+	HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1            */
+	HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL             */
+	HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
+	HID_OR1 = 0x01, /* GPO - General Purpose Output                 */
+	HID_OR2 = 0x02, /* Set GPIO to input/output mode                */
+	HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL          */
+
+	/* HID_IR0 */
+	RECORD_MUTE   = 1 << 3,
+	PLAYBACK_MUTE = 1 << 2,
+	VOLUME_DOWN   = 1 << 1,
+	VOLUME_UP     = 1 << 0,
+
+	/* HID_OR0 */
+	/* bits 7-6
+	   0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
+	      and SPDIF
+	   1: HID_OR0-3 are used as generic HID registers
+	   2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
+	      EEPROM_DATA0-1, EEPROM_CTRL (see Note)
+	   3: Reserved
+	 */
+	HID_OR_GPO_BUZ_SPDIF   = 0 << 6,
+	HID_OR_GENERIC_HID_REG = 1 << 6,
+	HID_OR_MAP_MCU_EEPROM  = 2 << 6,
+
+	BUZZER_ON = 1 << 5,
+};
+
+/* CM109 protocol packet */
+struct cm109_ctl_packet {
+	u8 byte[4];
+} __attribute__ ((packed));
+
+enum {USB_PKT_LEN = sizeof(struct cm109_ctl_packet)};
+
+/* CM109 device structure */
+struct cm109_dev {
+	struct input_dev *idev;	 /* input device */
+	struct usb_device *udev; /* usb device */
+	struct usb_interface *intf;
+
+	/* irq input channel */
+	struct cm109_ctl_packet *irq_data;
+	dma_addr_t irq_dma;
+	struct urb *urb_irq;
+
+	/* control output channel */
+	struct cm109_ctl_packet *ctl_data;
+	dma_addr_t ctl_dma;
+	struct usb_ctrlrequest *ctl_req;
+	dma_addr_t ctl_req_dma;
+	struct urb *urb_ctl;
+
+	spinlock_t submit_lock;
+	wait_queue_head_t wait;
+
+	/* flags */
+	char shutting_down:1;
+	char buzz_state:1;
+	char open:1;
+	char resetting:1;
+
+	char phys[64];		/* physical device path */
+	int key_code;		/* last reported key */
+	int keybit;		/* 0=new scan  1,2,4,8=scan columns  */
+	u8 gpi;			/* Cached value of GPI (high nibble) */
+};
+
+static DEFINE_MUTEX(reset_mutex);
+static int buzz(struct cm109_dev *dev, int on);
+
+/******************************************************************************
+ * CM109 key interface
+ *****************************************************************************/
+
+/* TODO: remove this code when KEY_NUMERIC_POUND is defined in linux/input.h
+ *
+ * if KEY_NUMERIC_POUND is not defined, we define our own version which
+ * is a rather dirty hack.
+ */
+#ifndef KEY_NUMERIC_POUND
+#warning "using dirty hack for pound key"
+#define KEY_NUMERIC_POUND (KEY_LEFTSHIFT | KEY_3 << 8)
+#endif
+
+
+/* Map device buttons to internal key events.
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+
+ Komunikate KIP1000 Keyboard Matrix
+
+     -> -- 1 -- 2 -- 3  --> GPI pin 4 (0x10)
+      |    |    |    |
+     <- -- 4 -- 5 -- 6  --> GPI pin 5 (0x20)
+      |    |    |    |
+     END - 7 -- 8 -- 9  --> GPI pin 6 (0x40)
+      |    |    |    |
+     OK -- * -- 0 -- #  --> GPI pin 7 (0x80)
+      |    |    |    |
+
+     /|\  /|\  /|\  /|\
+      |    |    |    |
+GPO
+pin:  3    2    1    0
+     0x8  0x4  0x2  0x1
+
+ */
+static int keymap_kip1000(int scancode)
+{
+	switch (scancode) {				/* phone key:   */
+	case 0x82: return KEY_0;			/*   0          */
+	case 0x14: return KEY_1;			/*   1          */
+	case 0x12: return KEY_2;			/*   2          */
+	case 0x11: return KEY_3;			/*   3          */
+	case 0x24: return KEY_4;			/*   4          */
+	case 0x22: return KEY_5;			/*   5          */
+	case 0x21: return KEY_6;			/*   6          */
+	case 0x44: return KEY_7;			/*   7          */
+	case 0x42: return KEY_8;			/*   8          */
+	case 0x41: return KEY_9;			/*   9          */
+	case 0x81: return KEY_NUMERIC_POUND;		/*   #          */
+	case 0x84: return KEY_KPASTERISK;		/*   *          */
+	case 0x88: return KEY_ENTER;			/*   pickup     */
+	case 0x48: return KEY_ESC;			/*   hangup     */
+	case 0x28: return KEY_LEFT;			/*   IN         */
+	case 0x18: return KEY_RIGHT;			/*   OUT        */
+	}
+	return -EINVAL;
+}
+
+/*
+  Contributed by Shaun Jackman <sjackman@gmail.com>
+
+  Genius G-Talk keyboard matrix
+     0 1 2 3
+  4: 0 4 8 Talk
+  5: 1 5 9 End
+  6: 2 6 # Up
+  7: 3 7 * Down
+*/
+static int keymap_gtalk(int scancode)
+{
+	switch (scancode) {
+	case 0x11: return KEY_0;
+	case 0x21: return KEY_1;
+	case 0x41: return KEY_2;
+	case 0x81: return KEY_3;
+	case 0x12: return KEY_4;
+	case 0x22: return KEY_5;
+	case 0x42: return KEY_6;
+	case 0x82: return KEY_7;
+	case 0x14: return KEY_8;
+	case 0x24: return KEY_9;
+	case 0x44: return KEY_NUMERIC_POUND; /* # */
+	case 0x84: return KEY_KPASTERISK;
+	case 0x18: return KEY_ENTER; /* Talk (green handset) */
+	case 0x28: return KEY_ESC; /* End (red handset) */
+	case 0x48: return KEY_UP; /* Menu up (rocker switch) */
+	case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */
+	}
+	return -EINVAL;
+}
+
+/*
+ * Keymap for Allied-Telesis Corega USBPH01
+ * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
+ *
+ * Contributed by july@nat.bg
+ */
+static int keymap_usbph01(int scancode)
+{
+	switch (scancode) {
+	case 0x11: return KEY_0;			/*   0          */
+	case 0x21: return KEY_1;			/*   1          */
+	case 0x41: return KEY_2;			/*   2          */
+	case 0x81: return KEY_3;			/*   3          */
+	case 0x12: return KEY_4;			/*   4          */
+	case 0x22: return KEY_5;			/*   5          */
+	case 0x42: return KEY_6;			/*   6          */
+	case 0x82: return KEY_7;			/*   7          */
+	case 0x14: return KEY_8;			/*   8          */
+	case 0x24: return KEY_9;			/*   9          */
+	case 0x44: return KEY_NUMERIC_POUND;		/*   #          */
+	case 0x84: return KEY_KPASTERISK;		/*   *          */
+	case 0x18: return KEY_ENTER;			/*   pickup     */
+	case 0x28: return KEY_ESC;			/*   hangup     */
+	case 0x48: return KEY_LEFT;			/*   IN         */
+	case 0x88: return KEY_RIGHT;			/*   OUT        */
+	}
+	return -EINVAL;
+}
+
+static int (*keymap)(int) = keymap_kip1000;
+
+
+/* Completes a request by converting the data into events for the
+ * input subsystem.
+ *
+ * The key parameter can be cascaded: key2 << 8 | key1
+ */
+static void report_key(struct cm109_dev *dev, int key)
+{
+	struct input_dev *idev = dev->idev;
+
+	if (dev->key_code >= 0) {
+		/* old key up */
+		input_report_key(idev, dev->key_code & 0xff, 0);
+		if (dev->key_code >> 8)
+			input_report_key(idev, dev->key_code >> 8, 0);
+	}
+
+	dev->key_code = key;
+	if (key >= 0) {
+		/* new valid key */
+		input_report_key(idev, key & 0xff, 1);
+		if (key >> 8)
+			input_report_key(idev, key >> 8, 1);
+	}
+	input_sync(idev);
+}
+
+/******************************************************************************
+ * CM109 usb communication interface
+ *****************************************************************************/
+
+
+/*
+ * IRQ handler
+ */
+static void urb_irq_callback(struct urb *urb)
+{
+	struct cm109_dev *dev = urb->context;
+	const int status = urb->status;
+	int ret;
+
+#if CM109_DEBUG
+	info("### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x",
+	     dev->irq_data->byte[0],
+	     dev->irq_data->byte[1],
+	     dev->irq_data->byte[2],
+	     dev->irq_data->byte[3],
+	     dev->keybit);
+#endif
+
+	if (status) {
+		if (-ESHUTDOWN == status)
+			return;
+		err("%s: urb status %d", __func__, status);
+	}
+
+	/* Scan key column */
+	if (0xf == dev->keybit) {
+
+		/* Any changes ? */
+		if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0)) {
+			goto out;
+		}
+
+		dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
+
+		dev->keybit = 0x1;
+	} else {
+		report_key(dev, keymap(dev->irq_data->byte[HID_IR1]));
+
+		dev->keybit <<= 1;
+		if (dev->keybit > 0x8)
+			dev->keybit = 0xf;
+	}
+
+	dev->ctl_data->byte[HID_OR1] = dev->keybit;
+	dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
+      out:
+	spin_lock(&dev->submit_lock);
+	if (!dev->shutting_down) {
+		ret = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+		if (ret)
+			err("%s: usb_submit_urb failed %d", __func__, ret);
+	}
+	spin_unlock(&dev->submit_lock);
+}
+
+static void urb_ctl_callback(struct urb *urb)
+{
+	struct cm109_dev *dev = urb->context;
+	const int status = urb->status;
+	int ret = 0;
+
+#if CM109_DEBUG
+	info("### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]",
+	     dev->ctl_data->byte[0],
+	     dev->ctl_data->byte[1],
+	     dev->ctl_data->byte[2],
+	     dev->ctl_data->byte[3]);
+#endif
+
+	if (status)
+		err("%s: urb status %d", __func__, status);
+
+	spin_lock(&dev->submit_lock);
+	/* ask for a response */
+	if (!dev->shutting_down)
+		ret = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
+	spin_unlock(&dev->submit_lock);
+
+	if (ret)
+		err("%s: usb_submit_urb failed %d", __func__, ret);
+	wake_up(&dev->wait);
+}
+
+/******************************************************************************
+ * input event interface
+ *****************************************************************************/
+
+static void stop_traffic(struct cm109_dev *dev)
+{
+	spin_lock_irq(&dev->submit_lock);
+	dev->shutting_down = 1;
+	spin_unlock_irq(&dev->submit_lock);
+
+	usb_kill_urb(dev->urb_ctl);
+	usb_kill_urb(dev->urb_irq);
+
+	spin_lock_irq(&dev->submit_lock);
+	dev->shutting_down = 0;
+	spin_unlock_irq(&dev->submit_lock);
+}
+
+static int input_open(struct input_dev *idev)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+	int ret;
+
+	dev->key_code = -1;	/* no keys pressed */
+	dev->keybit = 0xf;
+
+	/* issue INIT */
+	dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
+	dev->ctl_data->byte[HID_OR1] = dev->keybit;
+	dev->ctl_data->byte[HID_OR2] = dev->keybit;
+	dev->ctl_data->byte[HID_OR3] = 0x00;
+
+	ret = usb_autopm_get_interface(dev->intf);
+	if (ret < 0) {
+		err("%s - cannot autoresume, result %d",
+		    __func__, ret);
+		return ret;
+	}
+
+	mutex_lock(&reset_mutex);
+	if ((ret = usb_submit_urb(dev->urb_ctl, GFP_KERNEL)) != 0) {
+		err("%s: usb_submit_urb failed with result %d",
+		    __func__, ret);
+		usb_autopm_put_interface(dev->intf);
+		mutex_unlock(&reset_mutex);
+		return ret;
+	}
+
+	dev->open = 1;
+	mutex_unlock(&reset_mutex);
+
+	return 0;
+}
+
+static void input_close(struct input_dev *idev)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+	int traffic = 0;
+	int r;
+
+	dev->open = 0;
+	stop_traffic(dev);
+
+	spin_lock_irq(&dev->submit_lock);
+	if (dev->buzz_state) {
+		r = buzz(dev, 0);
+		spin_unlock_irq(&dev->submit_lock);
+		if (!r) {
+			wait_event(dev->wait, !dev->buzz_state);
+			traffic = 1;
+		}
+	} else {
+		spin_unlock_irq(&dev->submit_lock);
+	}
+	if (traffic)
+		stop_traffic(dev);
+
+	usb_autopm_put_interface(dev->intf);
+}
+
+static int buzz(struct cm109_dev *dev, int on)
+{
+	int ret = 0;
+
+	if (dev == NULL) {
+		err("buzz: dev is NULL");
+		return -EINVAL;
+	}
+
+	dbg("Buzzer %s", on ? "on" : "off");
+	if (dev->resetting)
+		goto skip_io;
+	if (on)
+		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+	else
+		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+	ret = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+	if (ret) {
+		err("%s: usb_submit_urb failed %d", __func__, ret);
+	} else {
+skip_io:
+		dev->buzz_state = on ? 1 : 0;
+	}
+	return ret;
+}
+
+static int input_ev(struct input_dev *idev, unsigned int type,
+		    unsigned int code, int value)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+
+#if CM109_DEBUG
+	info("input_ev: type=%u code=%u value=%d", type, code, value);
+#endif
+
+	if (type != EV_SND)
+		return -EINVAL;
+
+	switch (code) {
+	case SND_TONE:
+	case SND_BELL:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return buzz(dev, value);
+}
+
+
+/******************************************************************************
+ * Linux interface and usb initialisation
+ *****************************************************************************/
+
+struct driver_info {
+	char *name;
+};
+
+static const struct driver_info info_cm109 = {
+	.name = "CM109 USB driver",
+};
+
+enum {
+	VENDOR_ID        = 0x0d8c, /* C-Media Electronics */
+	PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
+};
+
+/* table of devices that work with this driver */
+static const struct usb_device_id usb_table[] = {
+	{
+	 .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+	 USB_DEVICE_ID_MATCH_INT_INFO,
+	 .idVendor = VENDOR_ID,
+	 .idProduct = PRODUCT_ID_CM109,
+	 .bInterfaceClass = USB_CLASS_HID,
+	 .bInterfaceSubClass = 0,
+	 .bInterfaceProtocol = 0,
+	 .driver_info = (kernel_ulong_t) & info_cm109},
+	/* you can add more devices here with product ID 0x0008 - 0x000f */
+	{}
+};
+
+static void usb_cleanup(struct cm109_dev *dev, int err)
+{
+	if (dev == NULL)
+		return;
+
+	stop_traffic(dev);
+
+	if (dev->idev) {
+		if (err)
+			input_free_device(dev->idev);
+		else
+			input_unregister_device(dev->idev);
+	}
+	if (dev->ctl_req)
+		usb_buffer_free(dev->udev, sizeof(*(dev->ctl_req)),
+				dev->ctl_req, dev->ctl_req_dma);
+	if (dev->ctl_data)
+		usb_buffer_free(dev->udev, USB_PKT_LEN,
+				dev->ctl_data, dev->ctl_dma);
+	if (dev->irq_data)
+		usb_buffer_free(dev->udev, USB_PKT_LEN,
+				dev->irq_data, dev->irq_dma);
+
+	usb_free_urb(dev->urb_irq);	/* parameter validation in core/urb */
+	usb_free_urb(dev->urb_ctl);	/* parameter validation in core/urb */
+	kfree(dev);
+}
+
+static void usb_disconnect(struct usb_interface *interface)
+{
+	struct cm109_dev *dev;
+
+	dev = usb_get_intfdata(interface);
+
+	usb_set_intfdata(interface, NULL);
+
+	usb_cleanup(dev, 0);
+}
+
+static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct driver_info *nfo = (struct driver_info *)id->driver_info;
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct cm109_dev *dev;
+	struct input_dev *input_dev;
+	int ret, pipe, i;
+
+	interface = intf->cur_altsetting;
+	endpoint = &interface->endpoint[0].desc;
+
+	if (!usb_endpoint_is_int_in(endpoint))
+		return -ENODEV;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	spin_lock_init(&dev->submit_lock);
+	init_waitqueue_head(&dev->wait);
+
+	dev->udev = udev;
+	dev->intf = intf;
+
+	dev->idev = input_dev = input_allocate_device();
+	if (!input_dev)
+		goto err;
+
+	/* allocate usb buffers */
+	dev->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+					 GFP_KERNEL, &dev->irq_dma);
+	if (dev->irq_data == NULL)
+		goto err;
+
+	dev->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+					 GFP_KERNEL, &dev->ctl_dma);
+	if (!dev->ctl_data)
+		goto err;
+
+	dev->ctl_req = usb_buffer_alloc(udev, sizeof(*(dev->ctl_req)),
+					GFP_KERNEL, &dev->ctl_req_dma);
+	if (dev->ctl_req == NULL)
+		goto err;
+
+	/* allocate urb structures */
+	dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (dev->urb_irq == NULL)
+		goto err;
+
+	dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+	if (dev->urb_ctl == NULL)
+		goto err;
+
+	/* get a handle to the interrupt data pipe */
+	pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+	ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	if (ret != USB_PKT_LEN)
+		err("invalid payload size %d, expected %d", ret, USB_PKT_LEN);
+
+	/* initialise irq urb */
+	usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
+			 USB_PKT_LEN,
+			 urb_irq_callback, dev, endpoint->bInterval);
+	dev->urb_irq->transfer_dma = dev->irq_dma;
+	dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	dev->urb_irq->dev = udev;
+
+	/* initialise ctl urb */
+	dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+	    USB_DIR_OUT;
+	dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
+	dev->ctl_req->wValue = cpu_to_le16(0x200);
+	dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+	dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
+
+	usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+			     (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
+			     urb_ctl_callback, dev);
+	dev->urb_ctl->setup_dma = dev->ctl_req_dma;
+	dev->urb_ctl->transfer_dma = dev->ctl_dma;
+	dev->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP |
+	    URB_NO_TRANSFER_DMA_MAP;
+	dev->urb_ctl->dev = udev;
+
+	/* find out the physical bus location */
+	usb_make_path(udev, dev->phys, sizeof(dev->phys));
+	strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+	/* register settings for the input device */
+	input_dev->name = nfo->name;
+	input_dev->phys = dev->phys;
+	usb_to_input_id(udev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, dev);
+	input_dev->open = input_open;
+	input_dev->close = input_close;
+	input_dev->event = input_ev;
+
+	/* register available key events */
+	input_dev->evbit[0] = BIT_MASK(EV_KEY);
+	for (i = 0; i < 256; i++) {
+		int k = keymap(i);
+		if (k >= 0) {
+			set_bit(k & 0xff, input_dev->keybit);
+			if (k >> 8)
+				set_bit(k >> 8, input_dev->keybit);
+		}
+	}
+
+	input_dev->evbit[0] |= BIT_MASK(EV_SND);
+	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+
+	ret = input_register_device(dev->idev);
+	if (ret)
+		goto err;
+
+	usb_set_intfdata(intf, dev);
+
+	return 0;
+
+      err:
+	usb_cleanup(dev, 1);
+	return -ENOMEM;
+}
+
+static int restore_state(struct cm109_dev *dev)
+{
+	int rv;
+
+	spin_lock_irq(&dev->submit_lock);
+	/* if not open, just restore buzz, else submit urb */
+	dev->shutting_down = dev->open;
+	spin_unlock_irq(&dev->submit_lock);
+	rv = buzz(dev, dev->buzz_state);
+	spin_lock_irq(&dev->submit_lock);
+	dev->shutting_down = 0;
+	spin_unlock_irq(&dev->submit_lock);
+
+	return rv;
+}
+
+static int usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	info("cm109: usb_suspend (event=%d)", message.event);
+
+	stop_traffic(dev);
+	return 0;
+}
+
+static int usb_resume(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+	int rv;
+
+	info("cm109: usb_resume");
+
+	rv = restore_state(dev);
+
+	return rv;
+}
+
+static int usb_pre_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	mutex_lock(&reset_mutex);
+	spin_lock_irq(&dev->submit_lock);
+	dev->resetting = 1;
+	spin_unlock_irq(&dev->submit_lock);
+	stop_traffic(dev);
+
+	return 0;
+}
+
+static int usb_post_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+	int rv;
+
+	spin_lock_irq(&dev->submit_lock);
+	dev->resetting = 0;
+	spin_unlock_irq(&dev->submit_lock);
+	rv = restore_state(dev);
+	mutex_unlock(&reset_mutex);
+	return rv;
+}
+
+static struct usb_driver cm109_driver = {
+	.name		= "cm109",
+	.probe		= usb_probe,
+	.disconnect	= usb_disconnect,
+	.suspend	= usb_suspend,
+	.resume		= usb_resume,
+	.reset_resume	= usb_resume,
+	.pre_reset	= usb_pre_reset,
+	.post_reset	= usb_post_reset,
+	.id_table	= usb_table,
+	.supports_autosuspend = 1,
+};
+
+static int __init select_keymap(void)
+{
+	/* Load the phone keymap */
+	if (0 == strcasecmp(phone, "kip1000")) {
+		keymap = keymap_kip1000;
+		info("Keymap for Komunikate KIP1000 phone loaded");
+	}
+	else if (0 == strcasecmp(phone, "gtalk")) {
+		keymap = keymap_gtalk;
+		info("Keymap for Genius G-talk phone loaded");
+	}
+	else if (0 == strcasecmp(phone, "usbph01")) {
+		keymap = keymap_usbph01;
+		info("Keymap for Allied-Telesis Corega USBPH01 phone loaded");
+	}
+	else {
+		err("Unsupported phone: %s", phone);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __init cm109_dev_init(void)
+{
+	int err;
+
+	err = select_keymap();
+	if (err)
+		return err;
+
+	err = usb_register(&cm109_driver);
+	if (err)
+		return err;
+
+	info(DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR);
+
+	return 0;
+}
+
+static void __exit cm109_dev_exit(void)
+{
+	usb_deregister(&cm109_driver);
+}
+
+module_init(cm109_dev_init);
+module_exit(cm109_dev_exit);
+
+MODULE_DEVICE_TABLE(usb, usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff -uprN -X linux-2.6.25/Documentation/dontdiff linux-2.6.25-orig/drivers/input/misc/Kconfig linux-2.6.25/drivers/input/misc/Kconfig
--- linux-2.6.25-orig/drivers/input/misc/Kconfig	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25/drivers/input/misc/Kconfig	2008-06-21 23:11:48.000000000 +0200
@@ -180,6 +180,18 @@ config INPUT_YEALINK
  	  To compile this driver as a module, choose M here: the module will be
  	  called yealink.

+config INPUT_CM109
+	tristate "C-Media CM109 USB I/O Controller"
+	depends on INPUT && EXPERIMENTAL
+	select USB
+	---help---
+	  Say Y here if you want to enable keyboard and buzzer functions of the
+	  C-Media CM109 usb phones. The audio part is enabled by the generic
+	  usb sound driver, so you might want to enable that as well.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called cm109.
+
  config INPUT_UINPUT
  	tristate "User level driver support"
  	help
diff -uprN -X linux-2.6.25/Documentation/dontdiff linux-2.6.25-orig/drivers/input/misc/Makefile linux-2.6.25/drivers/input/misc/Makefile
--- linux-2.6.25-orig/drivers/input/misc/Makefile	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25/drivers/input/misc/Makefile	2008-06-21 23:11:48.000000000 +0200
@@ -16,6 +16,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_
  obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)	+= keyspan_remote.o
  obj-$(CONFIG_INPUT_POWERMATE)		+= powermate.o
  obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
+obj-$(CONFIG_INPUT_CM109)		+= cm109.o
  obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
  obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
  obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
diff -uprN -X linux-2.6.25/Documentation/dontdiff linux-2.6.25-orig/drivers/input/misc/modules.order linux-2.6.25/drivers/input/misc/modules.order
--- linux-2.6.25-orig/drivers/input/misc/modules.order	2008-06-21 23:05:28.000000000 +0200
+++ linux-2.6.25/drivers/input/misc/modules.order	2008-06-28 15:56:50.000000000 +0200
@@ -1,2 +1,3 @@
  kernel/drivers/input/misc/yealink.ko
+kernel/drivers/input/misc/cm109.ko
  kernel/drivers/input/misc/uinput.ko
diff -uprN -X linux-2.6.25/Documentation/dontdiff linux-2.6.25-orig/drivers/input/modules.order linux-2.6.25/drivers/input/modules.order
--- linux-2.6.25-orig/drivers/input/modules.order	2008-06-21 23:05:28.000000000 +0200
+++ linux-2.6.25/drivers/input/modules.order	2008-06-28 15:56:58.000000000 +0200
@@ -1,3 +1,4 @@
  kernel/drivers/input/misc/yealink.ko
+kernel/drivers/input/misc/cm109.ko
  kernel/drivers/input/misc/uinput.ko
  kernel/drivers/input/input-polldev.ko

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-06-28 15:03 ` Alfred E. Heggestad
@ 2008-06-30 11:06   ` Oliver Neukum
  2008-07-25 14:14   ` Dmitry Torokhov
  1 sibling, 0 replies; 15+ messages in thread
From: Oliver Neukum @ 2008-06-30 11:06 UTC (permalink / raw)
  To: Alfred E. Heggestad; +Cc: Dmitry Torokhov, linux-input, linux-kernel

Am Samstag 28 Juni 2008 17:03:45 schrieb Alfred E. Heggestad:
 
Hello,

> many many thanks for your patch and your suggested code improvements.
> I have tested your patch and I have some comments:
> 
> * The first version of your patch was tested, and it applied cleanly
>    and compiled with out any errors. The module was loaded OK, but when
>    I inserted the CM109-device into the USB-port, the machine crashed.
>    I fixed that issue by adding this to the usb_probe() function:
> 
> 	init_waitqueue_head(&dev->wait);

Sorry about that.

> * The second version of your patch was applied with some local mods.
>    This is now working as expected.

Good.

> * I have tested suspend/resume with 2.6.25.9 and TuxOnIce 3.0-rc7
>    and it works fine (cm109 device was plugged in during suspend).
>    During suspend I get this in syslog:
> 
>      drivers/input/misc/cm109.c: cm109: usb_suspend (event=1)
>      drivers/input/misc/cm109.c: urb_irq_callback: urb status -2

That is normal. It indicates that usb_kill_urb() had terminated the URB.
Which logging level did you use?

> * The flag "char disconnecting:1" is no longer used in your second
>    patch, and has been removed from the struct.

Sorry again.

> * When the buzzer is triggered by SND_TONE/SND_BELL, I get these
>    warnings in syslog:
> 
>      drivers/input/misc/cm109.c: urb_ctl_callback: usb_submit_urb failed -22
>      drivers/input/misc/cm109.c: buzz: usb_submit_urb failed -22
>      drivers/input/misc/cm109.c: urb_ctl_callback: usb_submit_urb failed -22
>      drivers/input/misc/cm109.c: buzz: usb_submit_urb failed -22
> 
>    It seems that the ctl URB is shared between buzzer and keypress
>    handling. I am thinking about adding a new URB just for the buzzer,
>    but I am not sure if that really is necesary. Any suggestions?

Under which conditions did you get this? Was the device opened or not?

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-06-28 15:03 ` Alfred E. Heggestad
  2008-06-30 11:06   ` Oliver Neukum
@ 2008-07-25 14:14   ` Dmitry Torokhov
  2008-07-26 15:55     ` Alfred E. Heggestad
  1 sibling, 1 reply; 15+ messages in thread
From: Dmitry Torokhov @ 2008-07-25 14:14 UTC (permalink / raw)
  To: Alfred E. Heggestad; +Cc: Oliver Neukum, linux-input, linux-kernel

On Sat, Jun 28, 2008 at 05:03:45PM +0200, Alfred E. Heggestad wrote:
> Oliver Neukum wrote:
>> Am Mittwoch 25 Juni 2008 22:07:34 schrieb Alfred E. Heggestad:
>>
>> Very well
>>
>> - urb->status is about to go away and replaced by a parameter
>> - reliably kill two URBs submitting each other cannot be done with
>>   usb_kill_urb() alone
>> - you close the device but leave the buzzer on
>> - no support for suspend/resume
>> - no support for pre/post_reset
>>
>> Could you test this new additional patch? The original patch had some
>> issues I corrected.
>>
>
> Hi Oliver,
>
> many many thanks for your patch and your suggested code improvements.
> I have tested your patch and I have some comments:
>
> * The first version of your patch was tested, and it applied cleanly
>   and compiled with out any errors. The module was loaded OK, but when
>   I inserted the CM109-device into the USB-port, the machine crashed.
>   I fixed that issue by adding this to the usb_probe() function:
>
> 	init_waitqueue_head(&dev->wait);
>
> * The second version of your patch was applied with some local mods.
>   This is now working as expected.
>
> * I have tested suspend/resume with 2.6.25.9 and TuxOnIce 3.0-rc7
>   and it works fine (cm109 device was plugged in during suspend).
>   During suspend I get this in syslog:
>
>     drivers/input/misc/cm109.c: cm109: usb_suspend (event=1)
>     drivers/input/misc/cm109.c: urb_irq_callback: urb status -2
>
>
> * The flag "char disconnecting:1" is no longer used in your second
>   patch, and has been removed from the struct.
>
> * When the buzzer is triggered by SND_TONE/SND_BELL, I get these
>   warnings in syslog:
>
>     drivers/input/misc/cm109.c: urb_ctl_callback: usb_submit_urb failed -22
>     drivers/input/misc/cm109.c: buzz: usb_submit_urb failed -22
>     drivers/input/misc/cm109.c: urb_ctl_callback: usb_submit_urb failed -22
>     drivers/input/misc/cm109.c: buzz: usb_submit_urb failed -22
>
>   It seems that the ctl URB is shared between buzzer and keypress
>   handling. I am thinking about adding a new URB just for the buzzer,
>   but I am not sure if that really is necesary. Any suggestions?
>
>
> OK, once again - many thanks for your input
>

Hi Alfred,

I tried to adjust the patch to fix isues with CTL URB sharing between
keys and buzzer, bitfield access from process context and IRQ and keymap
loading support. The result is below. Could you please try it and see if
I broke it completely or if it indeed works. The patch depends on some
new key definitions so you probbaly want to fetch 'master' branch form
my tree on kernel.org before applying it.

BTW, can I please have your 'Signed-off-by:' string so I can add it to
the commit?

Thanks!

-- 
Dmitry

Input: add drievr for USB VoIP phones with CM109 chipset

From: Alfred E. Heggestad <aeh@db.org>

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---

 drivers/input/misc/Kconfig  |   12 +
 drivers/input/misc/Makefile |    1 
 drivers/input/misc/cm109.c  |  862 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 875 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/misc/cm109.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index e99b788..987e4b7 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -180,6 +180,18 @@ config INPUT_YEALINK
 	  To compile this driver as a module, choose M here: the module will be
 	  called yealink.
 
+config INPUT_CM109
+	tristate "C-Media CM109 USB I/O Controller"
+	depends on INPUT && EXPERIMENTAL
+	select USB
+	---help---
+	  Say Y here if you want to enable keyboard and buzzer functions of the
+	  C-Media CM109 usb phones. The audio part is enabled by the generic
+	  usb sound driver, so you might want to enable that as well.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called cm109.
+
 config INPUT_UINPUT
 	tristate "User level driver support"
 	help
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index f48009b..d7db2ae 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_remote2.o
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)	+= keyspan_remote.o
 obj-$(CONFIG_INPUT_POWERMATE)		+= powermate.o
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
+obj-$(CONFIG_INPUT_CM109)		+= cm109.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
 obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
new file mode 100644
index 0000000..d1ef394
--- /dev/null
+++ b/drivers/input/misc/cm109.c
@@ -0,0 +1,862 @@
+/*
+ * Driver for the VoIP USB phones with CM109 chipsets.
+ *
+ * Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2.
+ */
+
+/*
+ *   Tested devices:
+ *	- Komunikate KIP1000
+ *	- Genius G-talk
+ *	- Allied-Telesis Corega USBPH01
+ *	- ...
+ *
+ * This driver is based on the yealink.c driver
+ *
+ * Thanks to:
+ *   - Authors of yealink.c
+ *   - Thomas Reitmayr
+ *   - Oliver Neukum for good review comments and code
+ *   - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
+ *
+ * Todo:
+ *   - Fix bug with buzz and urb_ctl_callback:usb_submit_urb() -EINVAL
+ *   - Fix KEY_KPPOUND
+ *   - Read/write EEPROM
+ *   - Report input events volume up/down
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+
+#define CM109_DEBUG 0
+
+#define DRIVER_VERSION "20080628"
+#define DRIVER_AUTHOR  "Alfred E. Heggestad"
+#define DRIVER_DESC    "CM109 phone driver"
+
+static char *phone = "kip1000";
+module_param(phone, charp, S_IRUSR);
+MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01}");
+
+enum {
+	/* HID Registers */
+	HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down  */
+	HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0       */
+	HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1            */
+	HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL             */
+	HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
+	HID_OR1 = 0x01, /* GPO - General Purpose Output                 */
+	HID_OR2 = 0x02, /* Set GPIO to input/output mode                */
+	HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL          */
+
+	/* HID_IR0 */
+	RECORD_MUTE   = 1 << 3,
+	PLAYBACK_MUTE = 1 << 2,
+	VOLUME_DOWN   = 1 << 1,
+	VOLUME_UP     = 1 << 0,
+
+	/* HID_OR0 */
+	/* bits 7-6
+	   0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
+	      and SPDIF
+	   1: HID_OR0-3 are used as generic HID registers
+	   2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
+	      EEPROM_DATA0-1, EEPROM_CTRL (see Note)
+	   3: Reserved
+	 */
+	HID_OR_GPO_BUZ_SPDIF   = 0 << 6,
+	HID_OR_GENERIC_HID_REG = 1 << 6,
+	HID_OR_MAP_MCU_EEPROM  = 2 << 6,
+
+	BUZZER_ON = 1 << 5,
+};
+
+/* CM109 protocol packet */
+struct cm109_ctl_packet {
+	u8 byte[4];
+} __attribute__ ((packed));
+
+enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
+
+/* CM109 device structure */
+struct cm109_dev {
+	struct input_dev *idev;	 /* input device */
+	struct usb_device *udev; /* usb device */
+	struct usb_interface *intf;
+
+	/* irq input channel */
+	struct cm109_ctl_packet *irq_data;
+	dma_addr_t irq_dma;
+	struct urb *urb_irq;
+
+	/* control output channel */
+	struct cm109_ctl_packet *ctl_data;
+	dma_addr_t ctl_dma;
+	struct usb_ctrlrequest *ctl_req;
+	dma_addr_t ctl_req_dma;
+	struct urb *urb_ctl;
+	/*
+	 * The 2 bitfields below are protected by ctl_submit_lock.
+	 * They have to be separate since they are accessed from IRQ
+	 * context.
+	 */
+	unsigned ctl_urb_pending:1;	/* ctl_urb is in flight */
+	unsigned buzzer_pending:1;	/* need to issue buzz command */
+	spinlock_t ctl_submit_lock;
+
+	unsigned char buzzer_state;	/* on/off */
+
+	/* flags */
+	unsigned open:1;
+	unsigned resetting:1;
+	unsigned shutting_down:1;
+
+	/* This mutex protects writes to the above flags */
+	struct mutex pm_mutex;
+
+	unsigned short keymap[256];
+
+	char phys[64];		/* physical device path */
+	int key_code;		/* last reported key */
+	int keybit;		/* 0=new scan  1,2,4,8=scan columns  */
+	u8 gpi;			/* Cached value of GPI (high nibble) */
+};
+
+/******************************************************************************
+ * CM109 key interface
+ *****************************************************************************/
+
+/* Map device buttons to internal key events.
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+
+ Komunikate KIP1000 Keyboard Matrix
+
+     -> -- 1 -- 2 -- 3  --> GPI pin 4 (0x10)
+      |    |    |    |
+     <- -- 4 -- 5 -- 6  --> GPI pin 5 (0x20)
+      |    |    |    |
+     END - 7 -- 8 -- 9  --> GPI pin 6 (0x40)
+      |    |    |    |
+     OK -- * -- 0 -- #  --> GPI pin 7 (0x80)
+      |    |    |    |
+
+     /|\  /|\  /|\  /|\
+      |    |    |    |
+GPO
+pin:  3    2    1    0
+     0x8  0x4  0x2  0x1
+
+ */
+static unsigned short keymap_kip1000(int scancode)
+{
+	switch (scancode) {				/* phone key:   */
+	case 0x82: return KEY_NUMERIC_0;		/*   0          */
+	case 0x14: return KEY_NUMERIC_1;		/*   1          */
+	case 0x12: return KEY_NUMERIC_2;		/*   2          */
+	case 0x11: return KEY_NUMERIC_3;		/*   3          */
+	case 0x24: return KEY_NUMERIC_4;		/*   4          */
+	case 0x22: return KEY_NUMERIC_5;		/*   5          */
+	case 0x21: return KEY_NUMERIC_6;		/*   6          */
+	case 0x44: return KEY_NUMERIC_7;		/*   7          */
+	case 0x42: return KEY_NUMERIC_8;		/*   8          */
+	case 0x41: return KEY_NUMERIC_9;		/*   9          */
+	case 0x81: return KEY_NUMERIC_POUND;		/*   #          */
+	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */
+	case 0x88: return KEY_ENTER;			/*   pickup     */
+	case 0x48: return KEY_ESC;			/*   hangup     */
+	case 0x28: return KEY_LEFT;			/*   IN         */
+	case 0x18: return KEY_RIGHT;			/*   OUT        */
+	default:   return KEY_RESERVED;
+	}
+}
+
+/*
+  Contributed by Shaun Jackman <sjackman@gmail.com>
+
+  Genius G-Talk keyboard matrix
+     0 1 2 3
+  4: 0 4 8 Talk
+  5: 1 5 9 End
+  6: 2 6 # Up
+  7: 3 7 * Down
+*/
+static unsigned short keymap_gtalk(int scancode)
+{
+	switch (scancode) {
+	case 0x11: return KEY_NUMERIC_0;
+	case 0x21: return KEY_NUMERIC_1;
+	case 0x41: return KEY_NUMERIC_2;
+	case 0x81: return KEY_NUMERIC_3;
+	case 0x12: return KEY_NUMERIC_4;
+	case 0x22: return KEY_NUMERIC_5;
+	case 0x42: return KEY_NUMERIC_6;
+	case 0x82: return KEY_NUMERIC_7;
+	case 0x14: return KEY_NUMERIC_8;
+	case 0x24: return KEY_NUMERIC_9;
+	case 0x44: return KEY_NUMERIC_POUND;	/* # */
+	case 0x84: return KEY_NUMERIC_STAR;	/* * */
+	case 0x18: return KEY_ENTER;		/* Talk (green handset) */
+	case 0x28: return KEY_ESC;		/* End (red handset) */
+	case 0x48: return KEY_UP;		/* Menu up (rocker switch) */
+	case 0x88: return KEY_DOWN;		/* Menu down (rocker switch) */
+	default:   return KEY_RESERVED;
+	}
+}
+
+/*
+ * Keymap for Allied-Telesis Corega USBPH01
+ * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
+ *
+ * Contributed by july@nat.bg
+ */
+static unsigned short keymap_usbph01(int scancode)
+{
+	switch (scancode) {
+	case 0x11: return KEY_NUMERIC_0;		/*   0          */
+	case 0x21: return KEY_NUMERIC_1;		/*   1          */
+	case 0x41: return KEY_NUMERIC_2;		/*   2          */
+	case 0x81: return KEY_NUMERIC_3;		/*   3          */
+	case 0x12: return KEY_NUMERIC_4;		/*   4          */
+	case 0x22: return KEY_NUMERIC_5;		/*   5          */
+	case 0x42: return KEY_NUMERIC_6;		/*   6          */
+	case 0x82: return KEY_NUMERIC_7;		/*   7          */
+	case 0x14: return KEY_NUMERIC_8;		/*   8          */
+	case 0x24: return KEY_NUMERIC_9;		/*   9          */
+	case 0x44: return KEY_NUMERIC_POUND;		/*   #          */
+	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */
+	case 0x18: return KEY_ENTER;			/*   pickup     */
+	case 0x28: return KEY_ESC;			/*   hangup     */
+	case 0x48: return KEY_LEFT;			/*   IN         */
+	case 0x88: return KEY_RIGHT;			/*   OUT        */
+	default:   return KEY_RESERVED;
+	}
+}
+
+static unsigned short (*keymap)(int) = keymap_kip1000;
+
+/*
+ * Completes a request by converting the data into events for the
+ * input subsystem.
+ */
+static void report_key(struct cm109_dev *dev, int key)
+{
+	struct input_dev *idev = dev->idev;
+
+	if (dev->key_code >= 0) {
+		/* old key up */
+		input_report_key(idev, dev->key_code, 0);
+	}
+
+	dev->key_code = key;
+	if (key >= 0) {
+		/* new valid key */
+		input_report_key(idev, key, 1);
+	}
+
+	input_sync(idev);
+}
+
+/******************************************************************************
+ * CM109 usb communication interface
+ *****************************************************************************/
+
+static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
+{
+	int error;
+
+	if (dev->buzzer_state)
+		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+	else
+		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+	error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+	if (error)
+		err("%s: usb_submit_urb failed %d", __func__, error);
+}
+
+/*
+ * IRQ handler
+ */
+static void cm109_urb_irq_callback(struct urb *urb)
+{
+	struct cm109_dev *dev = urb->context;
+	const int status = urb->status;
+	int error;
+
+#if CM109_DEBUG
+	info("### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x",
+	     dev->irq_data->byte[0],
+	     dev->irq_data->byte[1],
+	     dev->irq_data->byte[2],
+	     dev->irq_data->byte[3],
+	     dev->keybit);
+#endif
+
+	if (status) {
+		if (status == -ESHUTDOWN)
+			return;
+		err("%s: urb status %d", __func__, status);
+	}
+
+	/* Scan key column */
+	if (dev->keybit == 0x0f) {
+
+		/* Any changes ? */
+		if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
+			goto out;
+
+		dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
+		dev->keybit = 0x1;
+	} else {
+		report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
+
+		dev->keybit <<= 1;
+		if (dev->keybit > 0x8)
+			dev->keybit = 0xf;
+	}
+
+ out:
+
+	spin_lock(&dev->ctl_submit_lock);
+
+	if (likely(!dev->shutting_down)) {
+
+		if (dev->buzzer_state)
+			dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+		else
+			dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+		dev->ctl_data->byte[HID_OR1] = dev->keybit;
+		dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
+		dev->buzzer_pending = 0;
+		dev->ctl_urb_pending = 1;
+
+		error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+		if (error)
+			err("%s: usb_submit_urb failed %d", __func__, error);
+	}
+
+	spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_urb_ctl_callback(struct urb *urb)
+{
+	struct cm109_dev *dev = urb->context;
+	const int status = urb->status;
+	int error;
+
+#if CM109_DEBUG
+	info("### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]",
+	     dev->ctl_data->byte[0],
+	     dev->ctl_data->byte[1],
+	     dev->ctl_data->byte[2],
+	     dev->ctl_data->byte[3]);
+#endif
+
+	if (status)
+		err("%s: urb status %d", __func__, status);
+
+	spin_lock(&dev->ctl_submit_lock);
+
+	dev->ctl_urb_pending = 0;
+
+	if (likely(!dev->shutting_down)) {
+
+		if (dev->buzzer_pending) {
+			dev->buzzer_pending = 0;
+			dev->ctl_urb_pending = 1;
+			cm109_submit_buzz_toggle(dev);
+		} else {
+			/* ask for key data */
+			error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
+			if (error)
+				err("%s: usb_submit_urb (urb_irq) failed %d",
+					__func__, error);
+		}
+	}
+
+	spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
+{
+	unsigned long flags;
+
+	dbg("Buzzer %s", on ? "on" : "off");
+
+	spin_lock_irqsave(&dev->ctl_submit_lock, flags);
+
+	if (dev->ctl_urb_pending) {
+		/* URB completion will resubmit */
+		dev->buzzer_pending = 1;
+	} else {
+		dev->ctl_urb_pending = 1;
+		cm109_submit_buzz_toggle(dev);
+	}
+
+	spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
+}
+
+static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
+{
+	int error;
+
+	if (on)
+		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+	else
+		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+	error = usb_control_msg(dev->udev,
+				usb_sndctrlpipe(dev->udev, 0),
+				dev->ctl_req->bRequest,
+				dev->ctl_req->bRequestType,
+				le16_to_cpu(dev->ctl_req->wValue),
+				le16_to_cpu(dev->ctl_req->wIndex),
+				dev->ctl_data,
+				USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
+	if (error)
+		err("%s: usb_control_msg) failed %d", __func__, error);
+}
+
+static void cm109_stop_traffic(struct cm109_dev *dev)
+{
+	dev->shutting_down = 1;
+	/*
+	 * Make sure other CPUs see this
+	 */
+	smp_wmb();
+
+	usb_kill_urb(dev->urb_ctl);
+	usb_kill_urb(dev->urb_irq);
+
+	cm109_toggle_buzzer_sync(dev, 0);
+
+	dev->shutting_down = 0;
+	smp_wmb();
+}
+
+static void cm109_restore_state(struct cm109_dev *dev)
+{
+	if (dev->open) {
+		/*
+		 * Restore buzzer state.
+		 * This will also kick regular URB submission
+		 */
+		cm109_toggle_buzzer_async(dev);
+	}
+}
+
+/******************************************************************************
+ * input event interface
+ *****************************************************************************/
+
+static int cm109_input_open(struct input_dev *idev)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+	int error;
+
+	error = usb_autopm_get_interface(dev->intf);
+	if (error < 0) {
+		err("%s - cannot autoresume, result %d",
+		    __func__, error);
+		return error;
+	}
+
+	mutex_lock(&dev->pm_mutex);
+
+	dev->buzzer_state = 0;
+	dev->key_code = -1;	/* no keys pressed */
+	dev->keybit = 0xf;
+
+	/* issue INIT */
+	dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
+	dev->ctl_data->byte[HID_OR1] = dev->keybit;
+	dev->ctl_data->byte[HID_OR2] = dev->keybit;
+	dev->ctl_data->byte[HID_OR3] = 0x00;
+
+	error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
+	if (error) {
+		err("%s: usb_submit_urb failed with result %d",
+		    __func__, error);
+	} else
+		dev->open = 1;
+
+	mutex_unlock(&dev->pm_mutex);
+
+	if (error)
+		usb_autopm_put_interface(dev->intf);
+
+	return error;
+}
+
+static void cm109_input_close(struct input_dev *idev)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+
+	mutex_lock(&dev->pm_mutex);
+
+	/*
+	 * Once we are here event delivery is stopped so we
+	 * don't need to worry about someone starting buzzer
+	 * again
+	 */
+	cm109_stop_traffic(dev);
+	dev->open = 0;
+
+	mutex_unlock(&dev->pm_mutex);
+
+	usb_autopm_put_interface(dev->intf);
+}
+
+static int cm109_input_ev(struct input_dev *idev, unsigned int type,
+			  unsigned int code, int value)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+
+#if CM109_DEBUG
+	info("input_ev: type=%u code=%u value=%d", type, code, value);
+#endif
+
+	if (type != EV_SND)
+		return -EINVAL;
+
+	switch (code) {
+	case SND_TONE:
+	case SND_BELL:
+		dev->buzzer_state = !!value;
+		if (!dev->resetting)
+			cm109_toggle_buzzer_async(dev);
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+
+/******************************************************************************
+ * Linux interface and usb initialisation
+ *****************************************************************************/
+
+struct driver_info {
+	char *name;
+};
+
+static const struct driver_info info_cm109 = {
+	.name = "CM109 USB driver",
+};
+
+enum {
+	VENDOR_ID        = 0x0d8c, /* C-Media Electronics */
+	PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
+};
+
+/* table of devices that work with this driver */
+static const struct usb_device_id cm109_usb_table[] = {
+	{
+		.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+				USB_DEVICE_ID_MATCH_INT_INFO,
+		.idVendor = VENDOR_ID,
+		.idProduct = PRODUCT_ID_CM109,
+		.bInterfaceClass = USB_CLASS_HID,
+		.bInterfaceSubClass = 0,
+		.bInterfaceProtocol = 0,
+		.driver_info = (kernel_ulong_t) &info_cm109
+	},
+	/* you can add more devices here with product ID 0x0008 - 0x000f */
+	{ }
+};
+
+static void cm109_usb_cleanup(struct cm109_dev *dev)
+{
+	if (dev->ctl_req)
+		usb_buffer_free(dev->udev, sizeof(*(dev->ctl_req)),
+				dev->ctl_req, dev->ctl_req_dma);
+	if (dev->ctl_data)
+		usb_buffer_free(dev->udev, USB_PKT_LEN,
+				dev->ctl_data, dev->ctl_dma);
+	if (dev->irq_data)
+		usb_buffer_free(dev->udev, USB_PKT_LEN,
+				dev->irq_data, dev->irq_dma);
+
+	usb_free_urb(dev->urb_irq);	/* parameter validation in core/urb */
+	usb_free_urb(dev->urb_ctl);	/* parameter validation in core/urb */
+	kfree(dev);
+}
+
+static void cm109_usb_disconnect(struct usb_interface *interface)
+{
+	struct cm109_dev *dev = usb_get_intfdata(interface);
+
+	usb_set_intfdata(interface, NULL);
+	input_unregister_device(dev->idev);
+	cm109_usb_cleanup(dev);
+}
+
+static int cm109_usb_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct driver_info *nfo = (struct driver_info *)id->driver_info;
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct cm109_dev *dev;
+	struct input_dev *input_dev = NULL;
+	int ret, pipe, i;
+	int error = -ENOMEM;
+
+	interface = intf->cur_altsetting;
+	endpoint = &interface->endpoint[0].desc;
+
+	if (!usb_endpoint_is_int_in(endpoint))
+		return -ENODEV;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	spin_lock_init(&dev->ctl_submit_lock);
+	mutex_init(&dev->pm_mutex);
+
+	dev->udev = udev;
+	dev->intf = intf;
+
+	dev->idev = input_dev = input_allocate_device();
+	if (!input_dev)
+		goto err_out;
+
+	/* allocate usb buffers */
+	dev->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+					 GFP_KERNEL, &dev->irq_dma);
+	if (!dev->irq_data)
+		goto err_out;
+
+	dev->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+					 GFP_KERNEL, &dev->ctl_dma);
+	if (!dev->ctl_data)
+		goto err_out;
+
+	dev->ctl_req = usb_buffer_alloc(udev, sizeof(*(dev->ctl_req)),
+					GFP_KERNEL, &dev->ctl_req_dma);
+	if (!dev->ctl_req)
+		goto err_out;
+
+	/* allocate urb structures */
+	dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->urb_irq)
+		goto err_out;
+
+	dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->urb_ctl)
+		goto err_out;
+
+	/* get a handle to the interrupt data pipe */
+	pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+	ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	if (ret != USB_PKT_LEN)
+		err("invalid payload size %d, expected %d", ret, USB_PKT_LEN);
+
+	/* initialise irq urb */
+	usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
+			 USB_PKT_LEN,
+			 cm109_urb_irq_callback, dev, endpoint->bInterval);
+	dev->urb_irq->transfer_dma = dev->irq_dma;
+	dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	dev->urb_irq->dev = udev;
+
+	/* initialise ctl urb */
+	dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+					USB_DIR_OUT;
+	dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
+	dev->ctl_req->wValue = cpu_to_le16(0x200);
+	dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+	dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
+
+	usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+			     (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
+			     cm109_urb_ctl_callback, dev);
+	dev->urb_ctl->setup_dma = dev->ctl_req_dma;
+	dev->urb_ctl->transfer_dma = dev->ctl_dma;
+	dev->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP |
+					URB_NO_TRANSFER_DMA_MAP;
+	dev->urb_ctl->dev = udev;
+
+	/* find out the physical bus location */
+	usb_make_path(udev, dev->phys, sizeof(dev->phys));
+	strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+	/* register settings for the input device */
+	input_dev->name = nfo->name;
+	input_dev->phys = dev->phys;
+	usb_to_input_id(udev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, dev);
+	input_dev->open = cm109_input_open;
+	input_dev->close = cm109_input_close;
+	input_dev->event = cm109_input_ev;
+
+	input_dev->keycode = dev->keymap;
+	input_dev->keycodesize = sizeof(unsigned char);
+	input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
+	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+
+	/* register available key events */
+	for (i = 0; i < 256; i++) {
+		unsigned short k = keymap(i);
+		dev->keymap[i] = k;
+		__set_bit(k, input_dev->keybit);
+	}
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	error = input_register_device(dev->idev);
+	if (error)
+		goto err_out;
+
+	usb_set_intfdata(intf, dev);
+
+	return 0;
+
+ err_out:
+	input_free_device(input_dev);
+	cm109_usb_cleanup(dev);
+	return error;
+}
+
+static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	info("cm109: usb_suspend (event=%d)", message.event);
+
+	mutex_lock(&dev->pm_mutex);
+	cm109_stop_traffic(dev);
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static int cm109_usb_resume(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	info("cm109: usb_resume");
+
+	mutex_lock(&dev->pm_mutex);
+	cm109_restore_state(dev);
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static int cm109_usb_pre_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	mutex_lock(&dev->pm_mutex);
+
+	/*
+	 * Make sure input events don't try to toggle buzzer
+	 * while we are resetting
+	 */
+	dev->resetting = 1;
+	smp_wmb();
+
+	cm109_stop_traffic(dev);
+
+	return 0;
+}
+
+static int cm109_usb_post_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	dev->resetting = 0;
+	smp_wmb();
+
+	cm109_restore_state(dev);
+
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static struct usb_driver cm109_driver = {
+	.name		= "cm109",
+	.probe		= cm109_usb_probe,
+	.disconnect	= cm109_usb_disconnect,
+	.suspend	= cm109_usb_suspend,
+	.resume		= cm109_usb_resume,
+	.reset_resume	= cm109_usb_resume,
+	.pre_reset	= cm109_usb_pre_reset,
+	.post_reset	= cm109_usb_post_reset,
+	.id_table	= cm109_usb_table,
+	.supports_autosuspend = 1,
+};
+
+static int __init cm109_select_keymap(void)
+{
+	/* Load the phone keymap */
+	if (!strcasecmp(phone, "kip1000")) {
+		keymap = keymap_kip1000;
+		info("Keymap for Komunikate KIP1000 phone loaded");
+	} else if (!strcasecmp(phone, "gtalk")) {
+		keymap = keymap_gtalk;
+		info("Keymap for Genius G-talk phone loaded");
+	} else if (!strcasecmp(phone, "usbph01")) {
+		keymap = keymap_usbph01;
+		info("Keymap for Allied-Telesis Corega USBPH01 phone loaded");
+	} else {
+		err("Unsupported phone: %s", phone);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __init cm109_init(void)
+{
+	int err;
+
+	err = cm109_select_keymap();
+	if (err)
+		return err;
+
+	err = usb_register(&cm109_driver);
+	if (err)
+		return err;
+
+	info(DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR);
+
+	return 0;
+}
+
+static void __exit cm109_exit(void)
+{
+	usb_deregister(&cm109_driver);
+}
+
+module_init(cm109_init);
+module_exit(cm109_exit);
+
+MODULE_DEVICE_TABLE(usb, cm109_usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-07-25 14:14   ` Dmitry Torokhov
@ 2008-07-26 15:55     ` Alfred E. Heggestad
  2008-07-26 18:45       ` Oliver Neukum
  2008-07-29  5:47       ` Dmitry Torokhov
  0 siblings, 2 replies; 15+ messages in thread
From: Alfred E. Heggestad @ 2008-07-26 15:55 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Oliver Neukum, linux-input, linux-kernel

Dmitry Torokhov wrote:
> On Sat, Jun 28, 2008 at 05:03:45PM +0200, Alfred E. Heggestad wrote:
>> Oliver Neukum wrote:
>>> Am Mittwoch 25 Juni 2008 22:07:34 schrieb Alfred E. Heggestad:
>>>
>>> Very well
>>>
>>> - urb->status is about to go away and replaced by a parameter
>>> - reliably kill two URBs submitting each other cannot be done with
>>>   usb_kill_urb() alone
>>> - you close the device but leave the buzzer on
>>> - no support for suspend/resume
>>> - no support for pre/post_reset
>>>
>>> Could you test this new additional patch? The original patch had some
>>> issues I corrected.
>>>
>> Hi Oliver,
>>
>> many many thanks for your patch and your suggested code improvements.
>> I have tested your patch and I have some comments:
>>
>> * The first version of your patch was tested, and it applied cleanly
>>   and compiled with out any errors. The module was loaded OK, but when
>>   I inserted the CM109-device into the USB-port, the machine crashed.
>>   I fixed that issue by adding this to the usb_probe() function:
>>
>> 	init_waitqueue_head(&dev->wait);
>>
>> * The second version of your patch was applied with some local mods.
>>   This is now working as expected.
>>
>> * I have tested suspend/resume with 2.6.25.9 and TuxOnIce 3.0-rc7
>>   and it works fine (cm109 device was plugged in during suspend).
>>   During suspend I get this in syslog:
>>
>>     drivers/input/misc/cm109.c: cm109: usb_suspend (event=1)
>>     drivers/input/misc/cm109.c: urb_irq_callback: urb status -2
>>
>>
>> * The flag "char disconnecting:1" is no longer used in your second
>>   patch, and has been removed from the struct.
>>
>> * When the buzzer is triggered by SND_TONE/SND_BELL, I get these
>>   warnings in syslog:
>>
>>     drivers/input/misc/cm109.c: urb_ctl_callback: usb_submit_urb failed -22
>>     drivers/input/misc/cm109.c: buzz: usb_submit_urb failed -22
>>     drivers/input/misc/cm109.c: urb_ctl_callback: usb_submit_urb failed -22
>>     drivers/input/misc/cm109.c: buzz: usb_submit_urb failed -22
>>
>>   It seems that the ctl URB is shared between buzzer and keypress
>>   handling. I am thinking about adding a new URB just for the buzzer,
>>   but I am not sure if that really is necesary. Any suggestions?
>>
>>
>> OK, once again - many thanks for your input
>>
> 
> Hi Alfred,
> 
> I tried to adjust the patch to fix isues with CTL URB sharing between
> keys and buzzer, bitfield access from process context and IRQ and keymap
> loading support. The result is below. Could you please try it and see if
> I broke it completely or if it indeed works. The patch depends on some
> new key definitions so you probbaly want to fetch 'master' branch form
> my tree on kernel.org before applying it.
> 
> BTW, can I please have your 'Signed-off-by:' string so I can add it to
> the commit?
> 
> Thanks!
> 

Hi Dmitry

thanks for your patch, I have merged it into my working copy and the
updated patch is below.

Your patch is working and keys are reported as expected, buzzer is
also working, although with some syslog warnings:


Jul 26 17:32:22 io kernel: cm109: Keymap for Komunikate KIP1000 phone loaded
Jul 26 17:32:22 io kernel: input: CM109 USB driver as /class/input/input14
Jul 26 17:32:22 io kernel: usbcore: registered new interface driver cm109
Jul 26 17:32:22 io kernel: cm109: CM109 phone driver: 20080726 (C) Alfred E. Heggestad

-> key is pressed here ..

Jul 26 17:32:33 io kernel: hald-addon-keyb[23629]: segfault at 6f6f6c5f ip b7e9855b sp bf9d89dc error 4 in libc-2.6.1.so[b7e28000+144000]
Jul 26 17:32:43 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22
Jul 26 17:33:21 io last message repeated 6 times
Jul 26 17:34:28 io last message repeated 17 times


Jul 26 17:35:02 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22
Jul 26 17:35:21 io last message repeated 2 times
Jul 26 17:35:55 io last message repeated 2 times

-> module is unloaded here:

Jul 26 17:35:59 io kernel: usbcore: deregistering interface driver cm109
Jul 26 17:35:59 io kernel: cm109: cm109_toggle_buzzer_sync: usb_control_msg) failed 4


In addition the latest patch also adds support for special keys like volume up/down
and mute, and preliminary keymap for carpophone II.


it is my understanding that all user-space applications must be updated
or rewritten to consume KEY_NUMERIC_xyz ? I have written a simple application
that reads input from /dev/input/event8 and handles KEY_NUMERIC_xyz correctly.
this application is working with your input-branch kernel version.


--
Alfred

Input: add driver for USB VoIP phones with CM109 chipset

From: Alfred E. Heggestad <aeh@db.org>

Signed-off-by: Alfred E. Heggestad <aeh@db.org>
---
diff -uprN -X linux-input/Documentation/dontdiff linux-input-orig/drivers/input/misc/cm109.c linux-input/drivers/input/misc/cm109.c
--- linux-input-orig/drivers/input/misc/cm109.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-input/drivers/input/misc/cm109.c	2008-07-26 17:31:22.000000000 +0200
@@ -0,0 +1,959 @@
+/*
+ * Driver for the VoIP USB phones with CM109 chipsets.
+ *
+ * Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2.
+ */
+
+/*
+ *   Tested devices:
+ *	- Komunikate KIP1000
+ *	- Genius G-talk
+ *	- Allied-Telesis Corega USBPH01
+ *	- carpophone II
+ *	- ...
+ *
+ * This driver is based on the yealink.c driver
+ *
+ * Thanks to:
+ *   - Authors of yealink.c
+ *   - Thomas Reitmayr
+ *   - Oliver Neukum for good review comments and code
+ *   - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
+ *   - Dmitry Torokhov for valuable input and review
+ *
+ * Todo:
+ *   - Fix bug with buzz and urb_ctl_callback:usb_submit_urb() -EINVAL
+ *   - Read/write EEPROM
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+
+#define CM109_DEBUG 0
+
+#define DRIVER_VERSION "20080726"
+#define DRIVER_AUTHOR  "Alfred E. Heggestad"
+#define DRIVER_DESC    "CM109 phone driver"
+
+static char *phone = "kip1000";
+module_param(phone, charp, S_IRUSR);
+MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, carpo}");
+
+enum {
+	/* HID Registers */
+	HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down  */
+	HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0       */
+	HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1            */
+	HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL             */
+	HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
+	HID_OR1 = 0x01, /* GPO - General Purpose Output                 */
+	HID_OR2 = 0x02, /* Set GPIO to input/output mode                */
+	HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL          */
+
+	/* HID_IR0 */
+	RECORD_MUTE   = 1 << 3,
+	PLAYBACK_MUTE = 1 << 2,
+	VOLUME_DOWN   = 1 << 1,
+	VOLUME_UP     = 1 << 0,
+
+	/* HID_OR0 */
+	/* bits 7-6
+	   0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
+	      and SPDIF
+	   1: HID_OR0-3 are used as generic HID registers
+	   2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
+	      EEPROM_DATA0-1, EEPROM_CTRL (see Note)
+	   3: Reserved
+	 */
+	HID_OR_GPO_BUZ_SPDIF   = 0 << 6,
+	HID_OR_GENERIC_HID_REG = 1 << 6,
+	HID_OR_MAP_MCU_EEPROM  = 2 << 6,
+
+	BUZZER_ON = 1 << 5,
+};
+
+/* CM109 protocol packet */
+struct cm109_ctl_packet {
+	u8 byte[4];
+} __attribute__ ((packed));
+
+enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
+
+/* CM109 device structure */
+struct cm109_dev {
+	struct input_dev *idev;	 /* input device */
+	struct usb_device *udev; /* usb device */
+	struct usb_interface *intf;
+
+	/* irq input channel */
+	struct cm109_ctl_packet *irq_data;
+	dma_addr_t irq_dma;
+	struct urb *urb_irq;
+
+	/* control output channel */
+	struct cm109_ctl_packet *ctl_data;
+	dma_addr_t ctl_dma;
+	struct usb_ctrlrequest *ctl_req;
+	dma_addr_t ctl_req_dma;
+	struct urb *urb_ctl;
+	/*
+	 * The 2 bitfields below are protected by ctl_submit_lock.
+	 * They have to be separate since they are accessed from IRQ
+	 * context.
+	 */
+	unsigned ctl_urb_pending:1;	/* ctl_urb is in flight */
+	unsigned buzzer_pending:1;	/* need to issue buzz command */
+	spinlock_t ctl_submit_lock;
+
+	unsigned char buzzer_state;	/* on/off */
+
+	/* flags */
+	unsigned open:1;
+	unsigned resetting:1;
+	unsigned shutdown:1;
+
+	/* This mutex protects writes to the above flags */
+	struct mutex pm_mutex;
+
+	unsigned short keymap[256];
+
+	char phys[64];		/* physical device path */
+	int key_code;		/* last reported key */
+	int keybit;		/* 0=new scan  1,2,4,8=scan columns  */
+	u8 gpi;			/* Cached value of GPI (high nibble) */
+};
+
+/******************************************************************************
+ * CM109 key interface
+ *****************************************************************************/
+
+/* Map device buttons to internal key events.
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+
+ Komunikate KIP1000 Keyboard Matrix
+
+     -> -- 1 -- 2 -- 3  --> GPI pin 4 (0x10)
+      |    |    |    |
+     <- -- 4 -- 5 -- 6  --> GPI pin 5 (0x20)
+      |    |    |    |
+     END - 7 -- 8 -- 9  --> GPI pin 6 (0x40)
+      |    |    |    |
+     OK -- * -- 0 -- #  --> GPI pin 7 (0x80)
+      |    |    |    |
+
+     /|\  /|\  /|\  /|\
+      |    |    |    |
+GPO
+pin:  3    2    1    0
+     0x8  0x4  0x2  0x1
+
+ */
+static unsigned short keymap_kip1000(int scancode)
+{
+	switch (scancode) {				/* phone key:   */
+	case 0x82: return KEY_NUMERIC_0;		/*   0          */
+	case 0x14: return KEY_NUMERIC_1;		/*   1          */
+	case 0x12: return KEY_NUMERIC_2;		/*   2          */
+	case 0x11: return KEY_NUMERIC_3;		/*   3          */
+	case 0x24: return KEY_NUMERIC_4;		/*   4          */
+	case 0x22: return KEY_NUMERIC_5;		/*   5          */
+	case 0x21: return KEY_NUMERIC_6;		/*   6          */
+	case 0x44: return KEY_NUMERIC_7;		/*   7          */
+	case 0x42: return KEY_NUMERIC_8;		/*   8          */
+	case 0x41: return KEY_NUMERIC_9;		/*   9          */
+	case 0x81: return KEY_NUMERIC_POUND;		/*   #          */
+	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */
+	case 0x88: return KEY_ENTER;			/*   pickup     */
+	case 0x48: return KEY_ESC;			/*   hangup     */
+	case 0x28: return KEY_LEFT;			/*   IN         */
+	case 0x18: return KEY_RIGHT;			/*   OUT        */
+	default:   return KEY_RESERVED;
+	}
+}
+
+/*
+  Contributed by Shaun Jackman <sjackman@gmail.com>
+
+  Genius G-Talk keyboard matrix
+     0 1 2 3
+  4: 0 4 8 Talk
+  5: 1 5 9 End
+  6: 2 6 # Up
+  7: 3 7 * Down
+*/
+static unsigned short keymap_gtalk(int scancode)
+{
+	switch (scancode) {
+	case 0x11: return KEY_NUMERIC_0;
+	case 0x21: return KEY_NUMERIC_1;
+	case 0x41: return KEY_NUMERIC_2;
+	case 0x81: return KEY_NUMERIC_3;
+	case 0x12: return KEY_NUMERIC_4;
+	case 0x22: return KEY_NUMERIC_5;
+	case 0x42: return KEY_NUMERIC_6;
+	case 0x82: return KEY_NUMERIC_7;
+	case 0x14: return KEY_NUMERIC_8;
+	case 0x24: return KEY_NUMERIC_9;
+	case 0x44: return KEY_NUMERIC_POUND;	/* # */
+	case 0x84: return KEY_NUMERIC_STAR;	/* * */
+	case 0x18: return KEY_ENTER;		/* Talk (green handset) */
+	case 0x28: return KEY_ESC;		/* End (red handset) */
+	case 0x48: return KEY_UP;		/* Menu up (rocker switch) */
+	case 0x88: return KEY_DOWN;		/* Menu down (rocker switch) */
+	default:   return KEY_RESERVED;
+	}
+}
+
+/*
+ * Keymap for Allied-Telesis Corega USBPH01
+ * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
+ *
+ * Contributed by july@nat.bg
+ */
+static unsigned short keymap_usbph01(int scancode)
+{
+	switch (scancode) {
+	case 0x11: return KEY_NUMERIC_0;		/*   0          */
+	case 0x21: return KEY_NUMERIC_1;		/*   1          */
+	case 0x41: return KEY_NUMERIC_2;		/*   2          */
+	case 0x81: return KEY_NUMERIC_3;		/*   3          */
+	case 0x12: return KEY_NUMERIC_4;		/*   4          */
+	case 0x22: return KEY_NUMERIC_5;		/*   5          */
+	case 0x42: return KEY_NUMERIC_6;		/*   6          */
+	case 0x82: return KEY_NUMERIC_7;		/*   7          */
+	case 0x14: return KEY_NUMERIC_8;		/*   8          */
+	case 0x24: return KEY_NUMERIC_9;		/*   9          */
+	case 0x44: return KEY_NUMERIC_POUND;		/*   #          */
+	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */
+	case 0x18: return KEY_ENTER;			/*   pickup     */
+	case 0x28: return KEY_ESC;			/*   hangup     */
+	case 0x48: return KEY_LEFT;			/*   IN         */
+	case 0x88: return KEY_RIGHT;			/*   OUT        */
+	default:   return KEY_RESERVED;
+	}
+}
+
+#if 1
+/*
+ * NOTE: THIS IS WORK-IN-PROGRESS!
+ *
+ * Keymap for carpophone II
+ * http://www.carpo.de/voip/cms/de/en/page_3633.jsp
+ *
+ * Contributed by Stephan Berberig <s.berberig@arcor.de>
+ *
+ * carpophone II keyboard matrix:
+ *
+ * +            UP
+ * -   Vol_up  Carpo  Vol_down  Vol_mute
+ *             DOWN
+ *
+ *     Talk    Hold    End
+ *      1       2       3
+ *      4       5       6
+ *      7       8       9
+ *      *       0       #
+ *
+ *
+ * GPIO pin mapping:
+
+      ? -- 8 -- 5 -- 2   --> GPI pin 4 (0x10)
+      |    |    |    |
+      ? -- 9 -- 6 -- 3   --> GPI pin 5 (0x20)
+      |    |    |    |
+      ? --down--BS-- up  --> GPI pin 6 (0x40)
+      |    |    |    |
+      ? -volup-ESC-ENTER --> GPI pin 7 (0x80)
+      |    |    |    |
+
+     /|\  /|\  /|\  /|\
+      |    |    |    |
+GPO
+pin:  3    2    1    0
+     0x8  0x4  0x2  0x1
+ */
+static unsigned short keymap_carpo(int scancode)
+{
+	switch (scancode) {				/* phone key:   */
+	//case 0xZZ: return KEY_NUMERIC_0;		/*   0          */
+	//case 0xZZ: return KEY_NUMERIC_1;		/*   1          */
+	case 0x11: return KEY_NUMERIC_2;		/*   2          */
+	case 0x21: return KEY_NUMERIC_3;		/*   3          */
+	//case 0xZZ: return KEY_NUMERIC_4;		/*   4          */
+	case 0x12: return KEY_NUMERIC_5;		/*   5          */
+	case 0x22: return KEY_NUMERIC_6;		/*   6          */
+	//case 0xZZ: return KEY_NUMERIC_7;		/*   7          */
+	case 0x14: return KEY_NUMERIC_8;		/*   8          */
+	case 0x24: return KEY_NUMERIC_9;		/*   9          */
+	case 0x81: return KEY_ENTER;			/*   Talk       */
+	case 0x82: return KEY_ESC;			/*   End        */
+	//case 0xZZ: return KEY_ZZZ;			/*   Hold       */
+	case 0x41: return KEY_UP;			/*   up         */
+	case 0x44: return KEY_DOWN;			/*   down       */
+	case 0x42: return KEY_BACKSPACE;		/*   Carpo      */
+	case 0x84: return KEY_VOLUMEUP;			/*   +          */
+	//case 0xZZ: return KEY_VOLUMEDOWN;		/*   -          */
+	//case 0xZZ: return KEY_VOLUMEUP;		/*   Vol_up     */
+	//case 0xZZ: return KEY_VOLUMEDOWN;		/*   Vol_down   */
+	//case 0xZZ: return KEY_MUTE;			/*   Vol_mute   */
+	//case 0xZZ: return KEY_NUMERIC_POUND;		/*   #          */
+	//case 0xZZ: return KEY_NUMERIC_STAR;		/*   *          */
+	default:   return KEY_RESERVED;
+	}
+}
+#endif
+
+static unsigned short (*keymap)(int) = keymap_kip1000;
+
+/*
+ * Completes a request by converting the data into events for the
+ * input subsystem.
+ */
+static void report_key(struct cm109_dev *dev, int key)
+{
+	struct input_dev *idev = dev->idev;
+
+	if (dev->key_code >= 0) {
+		/* old key up */
+		input_report_key(idev, dev->key_code, 0);
+	}
+
+	dev->key_code = key;
+	if (key >= 0) {
+		/* new valid key */
+		input_report_key(idev, key, 1);
+	}
+
+	input_sync(idev);
+}
+
+static unsigned short special_keymap(int code)
+{
+	switch (code) {
+	case RECORD_MUTE:   return KEY_MUTE;
+	case PLAYBACK_MUTE: return KEY_MUTE;
+	case VOLUME_DOWN:   return KEY_VOLUMEDOWN;
+	case VOLUME_UP:     return KEY_VOLUMEUP;
+	default:            return KEY_RESERVED;
+	}
+}
+
+
+/******************************************************************************
+ * CM109 usb communication interface
+ *****************************************************************************/
+
+static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
+{
+	int error;
+
+	if (dev->buzzer_state)
+		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+	else
+		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+	error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+	if (error)
+		err("%s: usb_submit_urb failed %d", __func__, error);
+}
+
+/*
+ * IRQ handler
+ */
+static void cm109_urb_irq_callback(struct urb *urb)
+{
+	struct cm109_dev *dev = urb->context;
+	const int status = urb->status;
+	int error;
+
+#if CM109_DEBUG
+	info("### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x",
+	     dev->irq_data->byte[0],
+	     dev->irq_data->byte[1],
+	     dev->irq_data->byte[2],
+	     dev->irq_data->byte[3],
+	     dev->keybit);
+#endif
+
+	if (status) {
+		if (status == -ESHUTDOWN)
+			return;
+		err("%s: urb status %d", __func__, status);
+	}
+
+	/* Special keys */
+	if (dev->irq_data->byte[HID_IR0] & 0x0f) {
+		const int code = (dev->irq_data->byte[HID_IR0] & 0x0f);
+		report_key(dev, special_keymap(code));
+	}
+
+	/* Scan key column */
+	if (dev->keybit == 0xf) {
+
+		/* Any changes ? */
+		if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
+			goto out;
+
+		dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
+		dev->keybit = 0x1;
+	} else {
+		report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
+
+		dev->keybit <<= 1;
+		if (dev->keybit > 0x8)
+			dev->keybit = 0xf;
+	}
+
+ out:
+
+	spin_lock(&dev->ctl_submit_lock);
+
+	if (likely(!dev->shutdown)) {
+
+		if (dev->buzzer_state)
+			dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+		else
+			dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+		dev->ctl_data->byte[HID_OR1] = dev->keybit;
+		dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
+		dev->buzzer_pending = 0;
+		dev->ctl_urb_pending = 1;
+
+		error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+		if (error)
+			err("%s: usb_submit_urb failed %d", __func__, error);
+	}
+
+	spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_urb_ctl_callback(struct urb *urb)
+{
+	struct cm109_dev *dev = urb->context;
+	const int status = urb->status;
+	int error;
+
+#if CM109_DEBUG
+	info("### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]",
+	     dev->ctl_data->byte[0],
+	     dev->ctl_data->byte[1],
+	     dev->ctl_data->byte[2],
+	     dev->ctl_data->byte[3]);
+#endif
+
+	if (status)
+		err("%s: urb status %d", __func__, status);
+
+	spin_lock(&dev->ctl_submit_lock);
+
+	dev->ctl_urb_pending = 0;
+
+	if (likely(!dev->shutdown)) {
+
+		if (dev->buzzer_pending) {
+			dev->buzzer_pending = 0;
+			dev->ctl_urb_pending = 1;
+			cm109_submit_buzz_toggle(dev);
+		} else {
+			/* ask for key data */
+			error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
+			if (error)
+				err("%s: usb_submit_urb (urb_irq) failed %d",
+					__func__, error);
+		}
+	}
+
+	spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
+{
+	unsigned long flags;
+
+	dbg("Buzzer %s", on ? "on" : "off");
+
+	spin_lock_irqsave(&dev->ctl_submit_lock, flags);
+
+	if (dev->ctl_urb_pending) {
+		/* URB completion will resubmit */
+		dev->buzzer_pending = 1;
+	} else {
+		dev->ctl_urb_pending = 1;
+		cm109_submit_buzz_toggle(dev);
+	}
+
+	spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
+}
+
+static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
+{
+	int error;
+
+	if (on)
+		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+	else
+		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+	error = usb_control_msg(dev->udev,
+				usb_sndctrlpipe(dev->udev, 0),
+				dev->ctl_req->bRequest,
+				dev->ctl_req->bRequestType,
+				le16_to_cpu(dev->ctl_req->wValue),
+				le16_to_cpu(dev->ctl_req->wIndex),
+				dev->ctl_data,
+				USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
+	if (error)
+		err("%s: usb_control_msg) failed %d", __func__, error);
+}
+
+static void cm109_stop_traffic(struct cm109_dev *dev)
+{
+	dev->shutdown = 1;
+	/*
+	 * Make sure other CPUs see this
+	 */
+	smp_wmb();
+
+	usb_kill_urb(dev->urb_ctl);
+	usb_kill_urb(dev->urb_irq);
+
+	cm109_toggle_buzzer_sync(dev, 0);
+
+	dev->shutdown = 0;
+	smp_wmb();
+}
+
+static void cm109_restore_state(struct cm109_dev *dev)
+{
+	if (dev->open) {
+		/*
+		 * Restore buzzer state.
+		 * This will also kick regular URB submission
+		 */
+		cm109_toggle_buzzer_async(dev);
+	}
+}
+
+/******************************************************************************
+ * input event interface
+ *****************************************************************************/
+
+static int cm109_input_open(struct input_dev *idev)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+	int error;
+
+	error = usb_autopm_get_interface(dev->intf);
+	if (error < 0) {
+		err("%s - cannot autoresume, result %d",
+		    __func__, error);
+		return error;
+	}
+
+	mutex_lock(&dev->pm_mutex);
+
+	dev->buzzer_state = 0;
+	dev->key_code = -1;	/* no keys pressed */
+	dev->keybit = 0xf;
+
+	/* issue INIT */
+	dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
+	dev->ctl_data->byte[HID_OR1] = dev->keybit;
+	dev->ctl_data->byte[HID_OR2] = dev->keybit;
+	dev->ctl_data->byte[HID_OR3] = 0x00;
+
+	error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
+	if (error) {
+		err("%s: usb_submit_urb failed with result %d",
+		    __func__, error);
+	} else
+		dev->open = 1;
+
+	mutex_unlock(&dev->pm_mutex);
+
+	if (error)
+		usb_autopm_put_interface(dev->intf);
+
+	return error;
+}
+
+static void cm109_input_close(struct input_dev *idev)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+
+	mutex_lock(&dev->pm_mutex);
+
+	/*
+	 * Once we are here event delivery is stopped so we
+	 * don't need to worry about someone starting buzzer
+	 * again
+	 */
+	cm109_stop_traffic(dev);
+	dev->open = 0;
+
+	mutex_unlock(&dev->pm_mutex);
+
+	usb_autopm_put_interface(dev->intf);
+}
+
+static int cm109_input_ev(struct input_dev *idev, unsigned int type,
+			  unsigned int code, int value)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+
+#if CM109_DEBUG
+	info("input_ev: type=%u code=%u value=%d", type, code, value);
+#endif
+
+	if (type != EV_SND)
+		return -EINVAL;
+
+	switch (code) {
+	case SND_TONE:
+	case SND_BELL:
+		dev->buzzer_state = !!value;
+		if (!dev->resetting)
+			cm109_toggle_buzzer_async(dev);
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+
+/******************************************************************************
+ * Linux interface and usb initialisation
+ *****************************************************************************/
+
+struct driver_info {
+	char *name;
+};
+
+static const struct driver_info info_cm109 = {
+	.name = "CM109 USB driver",
+};
+
+enum {
+	VENDOR_ID        = 0x0d8c, /* C-Media Electronics */
+	PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
+};
+
+/* table of devices that work with this driver */
+static const struct usb_device_id cm109_usb_table[] = {
+	{
+		.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+				USB_DEVICE_ID_MATCH_INT_INFO,
+		.idVendor = VENDOR_ID,
+		.idProduct = PRODUCT_ID_CM109,
+		.bInterfaceClass = USB_CLASS_HID,
+		.bInterfaceSubClass = 0,
+		.bInterfaceProtocol = 0,
+		.driver_info = (kernel_ulong_t) &info_cm109
+	},
+	/* you can add more devices here with product ID 0x0008 - 0x000f */
+	{ }
+};
+
+static void cm109_usb_cleanup(struct cm109_dev *dev)
+{
+	if (dev->ctl_req)
+		usb_buffer_free(dev->udev, sizeof(*(dev->ctl_req)),
+				dev->ctl_req, dev->ctl_req_dma);
+	if (dev->ctl_data)
+		usb_buffer_free(dev->udev, USB_PKT_LEN,
+				dev->ctl_data, dev->ctl_dma);
+	if (dev->irq_data)
+		usb_buffer_free(dev->udev, USB_PKT_LEN,
+				dev->irq_data, dev->irq_dma);
+
+	usb_free_urb(dev->urb_irq);	/* parameter validation in core/urb */
+	usb_free_urb(dev->urb_ctl);	/* parameter validation in core/urb */
+	kfree(dev);
+}
+
+static void cm109_usb_disconnect(struct usb_interface *interface)
+{
+	struct cm109_dev *dev = usb_get_intfdata(interface);
+
+	usb_set_intfdata(interface, NULL);
+	input_unregister_device(dev->idev);
+	cm109_usb_cleanup(dev);
+}
+
+static int cm109_usb_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct driver_info *nfo = (struct driver_info *)id->driver_info;
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct cm109_dev *dev;
+	struct input_dev *input_dev = NULL;
+	int ret, pipe, i;
+	int error = -ENOMEM;
+
+	interface = intf->cur_altsetting;
+	endpoint = &interface->endpoint[0].desc;
+
+	if (!usb_endpoint_is_int_in(endpoint))
+		return -ENODEV;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	spin_lock_init(&dev->ctl_submit_lock);
+	mutex_init(&dev->pm_mutex);
+
+	dev->udev = udev;
+	dev->intf = intf;
+
+	dev->idev = input_dev = input_allocate_device();
+	if (!input_dev)
+		goto err_out;
+
+	/* allocate usb buffers */
+	dev->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+					 GFP_KERNEL, &dev->irq_dma);
+	if (!dev->irq_data)
+		goto err_out;
+
+	dev->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+					 GFP_KERNEL, &dev->ctl_dma);
+	if (!dev->ctl_data)
+		goto err_out;
+
+	dev->ctl_req = usb_buffer_alloc(udev, sizeof(*(dev->ctl_req)),
+					GFP_KERNEL, &dev->ctl_req_dma);
+	if (!dev->ctl_req)
+		goto err_out;
+
+	/* allocate urb structures */
+	dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->urb_irq)
+		goto err_out;
+
+	dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->urb_ctl)
+		goto err_out;
+
+	/* get a handle to the interrupt data pipe */
+	pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+	ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	if (ret != USB_PKT_LEN)
+		err("invalid payload size %d, expected %d", ret, USB_PKT_LEN);
+
+	/* initialise irq urb */
+	usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
+			 USB_PKT_LEN,
+			 cm109_urb_irq_callback, dev, endpoint->bInterval);
+	dev->urb_irq->transfer_dma = dev->irq_dma;
+	dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	dev->urb_irq->dev = udev;
+
+	/* initialise ctl urb */
+	dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+					USB_DIR_OUT;
+	dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
+	dev->ctl_req->wValue = cpu_to_le16(0x200);
+	dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+	dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
+
+	usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+			     (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
+			     cm109_urb_ctl_callback, dev);
+	dev->urb_ctl->setup_dma = dev->ctl_req_dma;
+	dev->urb_ctl->transfer_dma = dev->ctl_dma;
+	dev->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP |
+					URB_NO_TRANSFER_DMA_MAP;
+	dev->urb_ctl->dev = udev;
+
+	/* find out the physical bus location */
+	usb_make_path(udev, dev->phys, sizeof(dev->phys));
+	strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+	/* register settings for the input device */
+	input_dev->name = nfo->name;
+	input_dev->phys = dev->phys;
+	usb_to_input_id(udev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, dev);
+	input_dev->open = cm109_input_open;
+	input_dev->close = cm109_input_close;
+	input_dev->event = cm109_input_ev;
+
+	input_dev->keycode = dev->keymap;
+	input_dev->keycodesize = sizeof(unsigned char);
+	input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
+	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+
+	/* register available key events */
+	for (i = 0; i < 256; i++) {
+		unsigned short k = keymap(i);
+		dev->keymap[i] = k;
+		__set_bit(k, input_dev->keybit);
+	}
+
+	/* special keys */
+	for (i = 0; i < 16; i++) {
+		unsigned short k = special_keymap(i);
+		__set_bit(k, input_dev->keybit);
+	}
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	error = input_register_device(dev->idev);
+	if (error)
+		goto err_out;
+
+	usb_set_intfdata(intf, dev);
+
+	return 0;
+
+ err_out:
+	input_free_device(input_dev);
+	cm109_usb_cleanup(dev);
+	return error;
+}
+
+static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	info("cm109: usb_suspend (event=%d)", message.event);
+
+	mutex_lock(&dev->pm_mutex);
+	cm109_stop_traffic(dev);
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static int cm109_usb_resume(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	info("cm109: usb_resume");
+
+	mutex_lock(&dev->pm_mutex);
+	cm109_restore_state(dev);
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static int cm109_usb_pre_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	mutex_lock(&dev->pm_mutex);
+
+	/*
+	 * Make sure input events don't try to toggle buzzer
+	 * while we are resetting
+	 */
+	dev->resetting = 1;
+	smp_wmb();
+
+	cm109_stop_traffic(dev);
+
+	return 0;
+}
+
+static int cm109_usb_post_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	dev->resetting = 0;
+	smp_wmb();
+
+	cm109_restore_state(dev);
+
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static struct usb_driver cm109_driver = {
+	.name		= "cm109",
+	.probe		= cm109_usb_probe,
+	.disconnect	= cm109_usb_disconnect,
+	.suspend	= cm109_usb_suspend,
+	.resume		= cm109_usb_resume,
+	.reset_resume	= cm109_usb_resume,
+	.pre_reset	= cm109_usb_pre_reset,
+	.post_reset	= cm109_usb_post_reset,
+	.id_table	= cm109_usb_table,
+	.supports_autosuspend = 1,
+};
+
+static int __init cm109_select_keymap(void)
+{
+	/* Load the phone keymap */
+	if (!strcasecmp(phone, "kip1000")) {
+		keymap = keymap_kip1000;
+		info("Keymap for Komunikate KIP1000 phone loaded");
+	} else if (!strcasecmp(phone, "gtalk")) {
+		keymap = keymap_gtalk;
+		info("Keymap for Genius G-talk phone loaded");
+	} else if (!strcasecmp(phone, "usbph01")) {
+		keymap = keymap_usbph01;
+		info("Keymap for Allied-Telesis Corega USBPH01 phone loaded");
+	} else if (!strcasecmp(phone, "carpo")) {
+		keymap = keymap_carpo;
+		info("Keymap for carpophone II loaded");
+	} else {
+		err("Unsupported phone: %s", phone);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __init cm109_init(void)
+{
+	int err;
+
+	err = cm109_select_keymap();
+	if (err)
+		return err;
+
+	err = usb_register(&cm109_driver);
+	if (err)
+		return err;
+
+	info(DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR);
+
+	return 0;
+}
+
+static void __exit cm109_exit(void)
+{
+	usb_deregister(&cm109_driver);
+}
+
+module_init(cm109_init);
+module_exit(cm109_exit);
+
+MODULE_DEVICE_TABLE(usb, cm109_usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff -uprN -X linux-input/Documentation/dontdiff linux-input-orig/drivers/input/misc/Kconfig linux-input/drivers/input/misc/Kconfig
--- linux-input-orig/drivers/input/misc/Kconfig	2008-07-26 16:30:52.000000000 +0200
+++ linux-input/drivers/input/misc/Kconfig	2008-07-26 13:34:56.000000000 +0200
@@ -180,6 +180,18 @@ config INPUT_YEALINK
 	  To compile this driver as a module, choose M here: the module will be
 	  called yealink.
 
+config INPUT_CM109
+	tristate "C-Media CM109 USB I/O Controller"
+	depends on INPUT && EXPERIMENTAL
+	select USB
+	---help---
+	  Say Y here if you want to enable keyboard and buzzer functions of the
+	  C-Media CM109 usb phones. The audio part is enabled by the generic
+	  usb sound driver, so you might want to enable that as well.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called cm109.
+
 config INPUT_UINPUT
 	tristate "User level driver support"
 	help
diff -uprN -X linux-input/Documentation/dontdiff linux-input-orig/drivers/input/misc/Makefile linux-input/drivers/input/misc/Makefile
--- linux-input-orig/drivers/input/misc/Makefile	2008-07-26 16:30:52.000000000 +0200
+++ linux-input/drivers/input/misc/Makefile	2008-07-26 13:34:56.000000000 +0200
@@ -16,6 +16,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)	+= keyspan_remote.o
 obj-$(CONFIG_INPUT_POWERMATE)		+= powermate.o
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
+obj-$(CONFIG_INPUT_CM109)		+= cm109.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
 obj-$(CONFIG_INPUT_APANEL)		+= apanel.o

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-07-26 15:55     ` Alfred E. Heggestad
@ 2008-07-26 18:45       ` Oliver Neukum
  2008-08-03 22:04         ` Alfred E. Heggestad
  2008-07-29  5:47       ` Dmitry Torokhov
  1 sibling, 1 reply; 15+ messages in thread
From: Oliver Neukum @ 2008-07-26 18:45 UTC (permalink / raw)
  To: Alfred E. Heggestad; +Cc: Dmitry Torokhov, linux-input, linux-kernel

Am Samstag 26 Juli 2008 17:55:35 schrieb Alfred E. Heggestad:
> Jul 26 17:32:33 io kernel: hald-addon-keyb[23629]: segfault at 6f6f6c5f ip b7e9855b sp bf9d89dc error 4 in libc-2.6.1.so[b7e28000+144000]
> Jul 26 17:32:43 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22
> Jul 26 17:33:21 io last message repeated 6 times
> Jul 26 17:34:28 io last message repeated 17 times

This should not happen. Please provide more context. If possible
recompile your kernel with CONFIG_USB_DEBUG.

	Regards
		Oliver


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-07-26 15:55     ` Alfred E. Heggestad
  2008-07-26 18:45       ` Oliver Neukum
@ 2008-07-29  5:47       ` Dmitry Torokhov
  1 sibling, 0 replies; 15+ messages in thread
From: Dmitry Torokhov @ 2008-07-29  5:47 UTC (permalink / raw)
  To: Alfred E. Heggestad; +Cc: Oliver Neukum, linux-input, linux-kernel

On Sat, Jul 26, 2008 at 05:55:35PM +0200, Alfred E. Heggestad wrote:
> Dmitry Torokhov wrote:
>>
>> Hi Alfred,
>>
>> I tried to adjust the patch to fix isues with CTL URB sharing between
>> keys and buzzer, bitfield access from process context and IRQ and keymap
>> loading support. The result is below. Could you please try it and see if
>> I broke it completely or if it indeed works. The patch depends on some
>> new key definitions so you probbaly want to fetch 'master' branch form
>> my tree on kernel.org before applying it.
>>
>> BTW, can I please have your 'Signed-off-by:' string so I can add it to
>> the commit?
>>
>> Thanks!
>>
>
> Hi Dmitry
>
> thanks for your patch, I have merged it into my working copy and the
> updated patch is below.
>
> Your patch is working and keys are reported as expected, buzzer is
> also working, although with some syslog warnings:
>
>
> Jul 26 17:32:22 io kernel: cm109: Keymap for Komunikate KIP1000 phone loaded
> Jul 26 17:32:22 io kernel: input: CM109 USB driver as /class/input/input14
> Jul 26 17:32:22 io kernel: usbcore: registered new interface driver cm109
> Jul 26 17:32:22 io kernel: cm109: CM109 phone driver: 20080726 (C) Alfred E. Heggestad
>
> -> key is pressed here ..
>
> Jul 26 17:32:33 io kernel: hald-addon-keyb[23629]: segfault at 6f6f6c5f ip b7e9855b sp bf9d89dc error 4 in libc-2.6.1.so[b7e28000+144000]
> Jul 26 17:32:43 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22
> Jul 26 17:33:21 io last message repeated 6 times
> Jul 26 17:34:28 io last message repeated 17 times
>
>
> Jul 26 17:35:02 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22
> Jul 26 17:35:21 io last message repeated 2 times
> Jul 26 17:35:55 io last message repeated 2 times
>
> -> module is unloaded here:
>
> Jul 26 17:35:59 io kernel: usbcore: deregistering interface driver cm109
> Jul 26 17:35:59 io kernel: cm109: cm109_toggle_buzzer_sync: usb_control_msg) failed 4
>

Hmm, should not be happening, I don't quite see anything wrong.
Hopefully USB debug that Oliver mentioned will help us figure this out.

>
> In addition the latest patch also adds support for special keys like volume up/down
> and mute, and preliminary keymap for carpophone II.
>

I wonder if we should keep adding new keymaps to the driver... Now that
setkeycode [supposedly] works we can offload this task to userspace.

>
> it is my understanding that all user-space applications must be updated
> or rewritten to consume KEY_NUMERIC_xyz ? I have written a simple application
> that reads input from /dev/input/event8 and handles KEY_NUMERIC_xyz correctly.
> this application is working with your input-branch kernel version.
>

Well, kind of. You have the option of resetting the keymap to old KEY_1
(by means of setkeycode) or you can load keymap that maps KEY_NUMERIC_1
to '1' etc for legacy console. But since you need to handle new keycodes
KEY_NUMERIC_POUND/STAR in applications anyway (we really don't want the
shift tricks because they break in sertain keyboard layouts) you may add
support for KEY_NUMERIC_[0-9] as well.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-07-26 18:45       ` Oliver Neukum
@ 2008-08-03 22:04         ` Alfred E. Heggestad
  2008-08-04 11:56           ` Oliver Neukum
  0 siblings, 1 reply; 15+ messages in thread
From: Alfred E. Heggestad @ 2008-08-03 22:04 UTC (permalink / raw)
  To: Oliver Neukum; +Cc: Dmitry Torokhov, linux-input, linux-kernel

Oliver Neukum wrote:
> Am Samstag 26 Juli 2008 17:55:35 schrieb Alfred E. Heggestad:
>> Jul 26 17:32:33 io kernel: hald-addon-keyb[23629]: segfault at 6f6f6c5f ip b7e9855b sp bf9d89dc error 4 in libc-2.6.1.so[b7e28000+144000]
>> Jul 26 17:32:43 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22
>> Jul 26 17:33:21 io last message repeated 6 times
>> Jul 26 17:34:28 io last message repeated 17 times
> 
> This should not happen. Please provide more context. If possible
> recompile your kernel with CONFIG_USB_DEBUG.
> 

Hi Oliver

sorry for the delay, I was out travelling with no access to CM109 hardware..

I recompiled the latest kernel from input-branch with CONFIG_USB_DEBUG enabled,
the version reported is 2.6.27-rc1. Below you can see the syslog and the
context of events:



;;;
;;; Insert CM109 device here:
;;;

Aug  3 23:42:49 io kernel: usb usb1: usb resume
Aug  3 23:42:49 io kernel: usb usb1: wakeup_rh
Aug  3 23:42:49 io kernel: hub 1-0:1.0: hub_resume
Aug  3 23:42:49 io kernel: uhci_hcd 0000:00:1f.2: port 2 portsc 0093,00
Aug  3 23:42:49 io kernel: hub 1-0:1.0: port 2: status 0101 change 0001
Aug  3 23:42:49 io kernel: hub 1-0:1.0: state 7 ports 2 chg 0004 evt 0000
Aug  3 23:42:49 io kernel: hub 1-0:1.0: port 2, status 0101, change 0000, 12 Mb/s
Aug  3 23:42:49 io kernel: usb 1-2: new full speed USB device using uhci_hcd and address 3
Aug  3 23:42:49 io kernel: usb 1-2: skipped 7 descriptors after interface
Aug  3 23:42:49 io kernel: usb 1-2: skipped 2 descriptors after interface
Aug  3 23:42:49 io kernel: usb 1-2: skipped 1 descriptor after endpoint
Aug  3 23:42:49 io kernel: usb 1-2: skipped 2 descriptors after interface
Aug  3 23:42:49 io kernel: usb 1-2: skipped 1 descriptor after endpoint
Aug  3 23:42:49 io kernel: usb 1-2: skipped 1 descriptor after interface
Aug  3 23:42:49 io kernel: usb 1-2: default language 0x0409
Aug  3 23:42:49 io kernel: usb 1-2: uevent
Aug  3 23:42:49 io kernel: usb 1-2: usb_probe_device
Aug  3 23:42:49 io kernel: usb 1-2: configuration #1 chosen from 1 choice
Aug  3 23:42:49 io kernel: usb 1-2: adding 1-2:1.0 (config #1, interface 0)
Aug  3 23:42:49 io kernel: usb 1-2:1.0: uevent
Aug  3 23:42:49 io kernel: snd-usb-audio 1-2:1.0: usb_probe_interface
Aug  3 23:42:49 io kernel: snd-usb-audio 1-2:1.0: usb_probe_interface - got id
Aug  3 23:42:49 io kernel: usb 1-2: adding 1-2:1.1 (config #1, interface 1)
Aug  3 23:42:49 io kernel: usb 1-2:1.1: uevent
Aug  3 23:42:49 io kernel: usb 1-2: adding 1-2:1.2 (config #1, interface 2)
Aug  3 23:42:49 io kernel: usb 1-2:1.2: uevent
Aug  3 23:42:49 io kernel: usb 1-2: adding 1-2:1.3 (config #1, interface 3)
Aug  3 23:42:49 io kernel: usb 1-2:1.3: uevent
Aug  3 23:42:49 io kernel: cm109 1-2:1.3: usb_probe_interface
Aug  3 23:42:49 io kernel: cm109 1-2:1.3: usb_probe_interface - got id
Aug  3 23:42:49 io kernel: input: CM109 USB driver as /class/input/input9
Aug  3 23:42:49 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 3 ep83-INT, period 32, phase 16, 14 us
Aug  3 23:42:49 io kernel: uhci_hcd 0000:00:1f.2: release dev 3 ep83-INT, period 32, phase 16, 14 us
Aug  3 23:42:49 io kernel: drivers/usb/core/inode.c: creating file '003'
Aug  3 23:42:49 io kernel: usb 1-2: New USB device found, idVendor=0d8c, idProduct=000e
Aug  3 23:42:49 io kernel: usb 1-2: New USB device strings: Mfr=0, Product=1, SerialNumber=0
Aug  3 23:42:49 io kernel: usb 1-2: Product: Generic USB Audio Device   
Aug  3 23:42:49 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 3 ep83-INT, period 32, phase 16, 14 us

;;;
;;; Buzzer is actived here (twice):
;;;

Aug  3 23:42:54 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22
Aug  3 23:42:55 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22

;;;
;;; CM109 Device unplugged here:
;;;

Aug  3 23:43:14 io kernel: usb 1-2: uhci_result_common: failed with status 440000
Aug  3 23:43:14 io kernel: cm109: cm109_urb_irq_callback: urb status -84
Aug  3 23:43:14 io kernel: uhci_hcd 0000:00:1f.2: release dev 3 ep83-INT, period 32, phase 16, 14 us
Aug  3 23:43:14 io kernel: usb 1-2: uhci_result_common: failed with status 440000
Aug  3 23:43:14 io kernel: cm109: cm109_urb_ctl_callback: urb status -71
Aug  3 23:43:14 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 3 ep83-INT, period 32, phase 16, 14 us
Aug  3 23:43:14 io kernel: usb 1-2: uhci_result_common: failed with status 440000
Aug  3 23:43:14 io kernel: cm109: cm109_urb_irq_callback: urb status -84
Aug  3 23:43:14 io kernel: uhci_hcd 0000:00:1f.2: release dev 3 ep83-INT, period 32, phase 16, 14 us
Aug  3 23:43:14 io kernel: usb 1-2: uhci_result_common: failed with status 440000
Aug  3 23:43:14 io kernel: cm109: cm109_urb_ctl_callback: urb status -71
Aug  3 23:43:14 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 3 ep83-INT, period 32, phase 16, 14 us
Aug  3 23:43:14 io kernel: hub 1-0:1.0: state 7 ports 2 chg 0000 evt 0004
Aug  3 23:43:14 io kernel: uhci_hcd 0000:00:1f.2: port 2 portsc 008a,00
Aug  3 23:43:14 io kernel: hub 1-0:1.0: port 2, status 0100, change 0003, 12 Mb/s
Aug  3 23:43:14 io kernel: usb 1-2: uhci_result_common: failed with status 440000
Aug  3 23:43:14 io kernel: cm109: cm109_urb_irq_callback: urb status -84
Aug  3 23:43:14 io kernel: uhci_hcd 0000:00:1f.2: release dev 3 ep83-INT, period 32, phase 16, 14 us
Aug  3 23:43:14 io kernel: usb 1-2: uhci_result_common: failed with status 440000
Aug  3 23:43:14 io kernel: cm109: cm109_urb_ctl_callback: urb status -71
Aug  3 23:43:14 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 3 ep83-INT, period 32, phase 16, 14 us
Aug  3 23:43:14 io kernel: hub 1-0:1.0: debounce: port 2: total 100ms stable 100ms status 0x100
Aug  3 23:43:14 io kernel: usb 1-2: USB disconnect, address 3
Aug  3 23:43:14 io kernel: usb 1-2: unregistering device
Aug  3 23:43:14 io kernel: usb 1-2: usb_disable_device nuking all URBs
Aug  3 23:43:14 io kernel: uhci_hcd 0000:00:1f.2: shutdown urb ceeaff40 ep3in-intr
Aug  3 23:43:14 io kernel: uhci_hcd 0000:00:1f.2: release dev 3 ep83-INT, period 32, phase 16, 14 us
Aug  3 23:43:14 io kernel: usb 1-2: unregistering interface 1-2:1.0
Aug  3 23:43:14 io kernel: usb 1-2:1.0: uevent
Aug  3 23:43:14 io kernel: usb 1-2: unregistering interface 1-2:1.1
Aug  3 23:43:14 io kernel: usb 1-2:1.1: uevent
Aug  3 23:43:14 io kernel: usb 1-2: unregistering interface 1-2:1.2
Aug  3 23:43:14 io kernel: usb 1-2:1.2: uevent
Aug  3 23:43:14 io kernel: usb 1-2: unregistering interface 1-2:1.3
Aug  3 23:43:14 io kernel: cm109: cm109_toggle_buzzer_sync: usb_control_msg) failed -19
Aug  3 23:43:14 io kernel: usb 1-2:1.3: uevent
Aug  3 23:43:14 io kernel: usb 1-2: uevent
Aug  3 23:43:15 io kernel: usb usb1: suspend_rh (auto-stop)
Aug  3 23:43:16 io kernel: hub 1-0:1.0: hub_suspend
Aug  3 23:43:16 io kernel: usb usb1: bus auto-suspend
Aug  3 23:43:16 io kernel: usb usb1: suspend_rh




/alfred

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-08-03 22:04         ` Alfred E. Heggestad
@ 2008-08-04 11:56           ` Oliver Neukum
  2008-08-04 20:34             ` Alfred E. Heggestad
  0 siblings, 1 reply; 15+ messages in thread
From: Oliver Neukum @ 2008-08-04 11:56 UTC (permalink / raw)
  To: Alfred E. Heggestad; +Cc: Dmitry Torokhov, linux-input, linux-kernel

Am Montag 04 August 2008 00:04:58 schrieb Alfred E. Heggestad:
> Oliver Neukum wrote:
> > Am Samstag 26 Juli 2008 17:55:35 schrieb Alfred E. Heggestad:
> >> Jul 26 17:32:33 io kernel: hald-addon-keyb[23629]: segfault at 6f6f6c5f ip b7e9855b sp bf9d89dc error 4 in libc-2.6.1.so[b7e28000+144000]
> >> Jul 26 17:32:43 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22
> >> Jul 26 17:33:21 io last message repeated 6 times
> >> Jul 26 17:34:28 io last message repeated 17 times
> > 
> > This should not happen. Please provide more context. If possible
> > recompile your kernel with CONFIG_USB_DEBUG.
> > 
> 
> Hi Oliver
> 
> sorry for the delay, I was out travelling with no access to CM109 hardware..
> 
> I recompiled the latest kernel from input-branch with CONFIG_USB_DEBUG enabled,
> the version reported is 2.6.27-rc1. Below you can see the syslog and the
> context of events:

Looks like urb_irq is not properly initialised. Is there really nothing in the syslog?
Please print out urb->interval

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-08-04 11:56           ` Oliver Neukum
@ 2008-08-04 20:34             ` Alfred E. Heggestad
  2008-08-04 22:08               ` Oliver Neukum
  0 siblings, 1 reply; 15+ messages in thread
From: Alfred E. Heggestad @ 2008-08-04 20:34 UTC (permalink / raw)
  To: Oliver Neukum; +Cc: Dmitry Torokhov, linux-input, linux-kernel

Oliver Neukum wrote:
> Am Montag 04 August 2008 00:04:58 schrieb Alfred E. Heggestad:
>> Oliver Neukum wrote:
>>> Am Samstag 26 Juli 2008 17:55:35 schrieb Alfred E. Heggestad:
>>>> Jul 26 17:32:33 io kernel: hald-addon-keyb[23629]: segfault at 6f6f6c5f ip b7e9855b sp bf9d89dc error 4 in libc-2.6.1.so[b7e28000+144000]
>>>> Jul 26 17:32:43 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22
>>>> Jul 26 17:33:21 io last message repeated 6 times
>>>> Jul 26 17:34:28 io last message repeated 17 times
>>> This should not happen. Please provide more context. If possible
>>> recompile your kernel with CONFIG_USB_DEBUG.
>>>
>> Hi Oliver
>>
>> sorry for the delay, I was out travelling with no access to CM109 hardware..
>>
>> I recompiled the latest kernel from input-branch with CONFIG_USB_DEBUG enabled,
>> the version reported is 2.6.27-rc1. Below you can see the syslog and the
>> context of events:
> 
> Looks like urb_irq is not properly initialised. Is there really nothing in the syslog?
> Please print out urb->interval
> 

urb->interval == 32

I added some more debugging in the driver, and after the module is loaded,
I press "TAB" in a shell to force Bell/Buzzer.


the syslog reports:


;;;
;;; Insert device:
;;;

Aug  4 22:20:43 io kernel: cm109: Keymap for Komunikate KIP1000 phone loaded
Aug  4 22:20:43 io kernel: cm109 1-2:1.3: usb_probe_interface
Aug  4 22:20:43 io kernel: cm109 1-2:1.3: usb_probe_interface - got id
Aug  4 22:20:43 io kernel: cm109: urb_irq init: interval=32
Aug  4 22:20:43 io kernel: input: CM109 USB driver as /class/input/input13
Aug  4 22:20:43 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=0
Aug  4 22:20:43 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  4 22:20:43 io kernel: usbcore: registered new interface driver cm109
Aug  4 22:20:43 io kernel: cm109: CM109 phone driver: 20080726 (C) Alfred E. Heggestad

;;;
;;; Bell/Alarm -> buzzer
;;;

Aug  4 22:20:50 io kernel: cm109: cm109_toggle_buzzer_async: ctl_urb_pending=0 buzzer_pending=0
Aug  4 22:20:50 io kernel: cm109: cm109_submit_buzz_toggle: buzzer_state=1
Aug  4 22:20:50 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  4 22:20:50 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22     <----- error -EINVAL
Aug  4 22:20:50 io kernel: cm109: cm109_toggle_buzzer_async: ctl_urb_pending=0 buzzer_pending=0
Aug  4 22:20:50 io kernel: cm109: cm109_submit_buzz_toggle: buzzer_state=0
Aug  4 22:20:50 io kernel: cm109: cm109_toggle_buzzer_async: ctl_urb_pending=1 buzzer_pending=0
Aug  4 22:20:50 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=1, ctl_urb_pending=1
Aug  4 22:20:50 io kernel: cm109: cm109_submit_buzz_toggle: buzzer_state=0
Aug  4 22:20:50 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  4 22:20:50 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22     <---- error -EINVAL

;;;
;;; unplug device:
;;;

Aug  4 22:20:58 io kernel: usbcore: deregistering interface driver cm109
Aug  4 22:20:58 io kernel: uhci_hcd 0000:00:1f.2: shutdown urb cec1a760 ep3in-intr
Aug  4 22:20:58 io kernel: cm109: urb_irq_cb: urb_irq->interval=32
Aug  4 22:20:58 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  4 22:20:58 io kernel: cm109: cm109_toggle_buzzer_sync: usb_control_msg() failed 4




/alfred

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-08-04 20:34             ` Alfred E. Heggestad
@ 2008-08-04 22:08               ` Oliver Neukum
  2008-08-05 19:23                 ` Alfred E. Heggestad
  0 siblings, 1 reply; 15+ messages in thread
From: Oliver Neukum @ 2008-08-04 22:08 UTC (permalink / raw)
  To: Alfred E. Heggestad; +Cc: Dmitry Torokhov, linux-input, linux-kernel

Am Montag 04 August 2008 22:34:27 schrieb Alfred E. Heggestad:
> > 
> > Looks like urb_irq is not properly initialised. Is there really nothing in the syslog?
> > Please print out urb->interval
> > 
> 
> urb->interval == 32
> 
> I added some more debugging in the driver, and after the module is loaded,
> I press "TAB" in a shell to force Bell/Buzzer.

Can you printk the interval and the dev pointer in 
cm109_urb_irq_callback?

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-08-04 22:08               ` Oliver Neukum
@ 2008-08-05 19:23                 ` Alfred E. Heggestad
  2008-08-05 20:14                   ` Dmitry Torokhov
  0 siblings, 1 reply; 15+ messages in thread
From: Alfred E. Heggestad @ 2008-08-05 19:23 UTC (permalink / raw)
  To: Oliver Neukum; +Cc: Dmitry Torokhov, linux-input, linux-kernel

Oliver Neukum wrote:
> Am Montag 04 August 2008 22:34:27 schrieb Alfred E. Heggestad:
>>> Looks like urb_irq is not properly initialised. Is there really nothing in the syslog?
>>> Please print out urb->interval
>>>
>> urb->interval == 32
>>
>> I added some more debugging in the driver, and after the module is loaded,
>> I press "TAB" in a shell to force Bell/Buzzer.
> 
> Can you printk the interval and the dev pointer in 
> cm109_urb_irq_callback?
> 

sure. it looks like the interval remains unchanged at 32.

from the log below you can see two scenarios; first the key '1' is pressed on the
CM109 device. then TAB is pressed in shell to get the Buzzer.

in the first case the two URBs urb_irq and urb_ctl are called every other
time, to scan the whole key matrix. calling usb_submit_urb() does not give
-EINVAL error here.

in the second case the URB is triggered by input-subsystem and cm109_input_ev()
and finally calls usb_submit_urb() on urb_ctl, but in the callback handler
of urb_ctl, the usb_submit_urb() on urb_irq fails with -EINVAL.


log:

;;;
;;; CM109 device inserted
;;;
Aug  5 21:11:08 io kernel: cm109: Keymap for Komunikate KIP1000 phone loaded
Aug  5 21:11:08 io kernel: cm109 1-2:1.3: usb_probe_interface
Aug  5 21:11:08 io kernel: cm109 1-2:1.3: usb_probe_interface - got id
Aug  5 21:11:08 io kernel: cm109: urb_irq init: interval=32
Aug  5 21:11:08 io kernel: input: CM109 USB driver as /class/input/input9
Aug  5 21:11:08 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=0
Aug  5 21:11:08 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:08 io kernel: usbcore: registered new interface driver cm109
Aug  5 21:11:08 io kernel: cm109: CM109 phone driver: 20080726 (C) Alfred E. Heggestad

;;;
;;; Press key '1' on CM109 device
;;;

Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us
Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us

;;;
;;; Press 'TAB' in shell to force Bell/Buzzer
;;;

Aug  5 21:11:22 io kernel: cm109: cm109_toggle_buzzer_async: ctl_urb_pending=0 buzzer_pending=0
Aug  5 21:11:22 io kernel: cm109: cm109_submit_buzz_toggle: buzzer_state=1
Aug  5 21:11:22 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:22 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22     <--- note -EINVAL
Aug  5 21:11:22 io kernel: cm109: cm109_toggle_buzzer_async: ctl_urb_pending=0 buzzer_pending=0
Aug  5 21:11:22 io kernel: cm109: cm109_submit_buzz_toggle: buzzer_state=0
Aug  5 21:11:22 io kernel: cm109: cm109_toggle_buzzer_async: ctl_urb_pending=1 buzzer_pending=0
Aug  5 21:11:22 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=1, ctl_urb_pending=1
Aug  5 21:11:22 io kernel: cm109: cm109_submit_buzz_toggle: buzzer_state=0
Aug  5 21:11:22 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
Aug  5 21:11:22 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22     <--- note -EINVAL

;;;
;;; modprobe -r cm109
;;;

Aug  5 21:11:28 io kernel: usbcore: deregistering interface driver cm109
Aug  5 21:11:28 io kernel: uhci_hcd 0000:00:1f.2: shutdown urb cef647e0 ep3in-intr
Aug  5 21:11:28 io kernel: cm109: cm109_urb_irq_callback: dev=cd7a4400 urb_irq->interval=32
Aug  5 21:11:28 io kernel: uhci_hcd 0000:00:1f.2: release dev 2 ep83-INT, period 32, phase 16, 14 us

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-08-05 19:23                 ` Alfred E. Heggestad
@ 2008-08-05 20:14                   ` Dmitry Torokhov
  2008-08-05 21:19                     ` Alfred E. Heggestad
  0 siblings, 1 reply; 15+ messages in thread
From: Dmitry Torokhov @ 2008-08-05 20:14 UTC (permalink / raw)
  To: Alfred E. Heggestad; +Cc: Oliver Neukum, linux-input, linux-kernel

On Tue, Aug 05, 2008 at 09:23:46PM +0200, Alfred E. Heggestad wrote:
> Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
> Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
>

Ok, so urb_irq is pending here...

> ;;;
> ;;; Press 'TAB' in shell to force Bell/Buzzer
> ;;;
>
> Aug  5 21:11:22 io kernel: cm109: cm109_toggle_buzzer_async: ctl_urb_pending=0 buzzer_pending=0
> Aug  5 21:11:22 io kernel: cm109: cm109_submit_buzz_toggle: buzzer_state=1
> Aug  5 21:11:22 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
> Aug  5 21:11:22 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22     <--- note -EINVAL

And we trying to re-submit it.... How about the patch below?

Input: cm109 - fix issue with submitting urb_irq

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---

 drivers/input/misc/cm109.c |    7 +++++--
 1 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index d1ef394..a97f038 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -106,10 +106,11 @@ struct cm109_dev {
 	dma_addr_t ctl_req_dma;
 	struct urb *urb_ctl;
 	/*
-	 * The 2 bitfields below are protected by ctl_submit_lock.
+	 * The 3 bitfields below are protected by ctl_submit_lock.
 	 * They have to be separate since they are accessed from IRQ
 	 * context.
 	 */
+	unsigned irq_urb_pending:1;	/* irq_urb is in flight */
 	unsigned ctl_urb_pending:1;	/* ctl_urb is in flight */
 	unsigned buzzer_pending:1;	/* need to issue buzz command */
 	spinlock_t ctl_submit_lock;
@@ -332,6 +333,8 @@ static void cm109_urb_irq_callback(struct urb *urb)
 
 	spin_lock(&dev->ctl_submit_lock);
 
+	dev->irq_urb_pending = 0;
+
 	if (likely(!dev->shutting_down)) {
 
 		if (dev->buzzer_state)
@@ -380,7 +383,7 @@ static void cm109_urb_ctl_callback(struct urb *urb)
 			dev->buzzer_pending = 0;
 			dev->ctl_urb_pending = 1;
 			cm109_submit_buzz_toggle(dev);
-		} else {
+		} else if (likely(!dev->irq_urb_pending)) {
 			/* ask for key data */
 			error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
 			if (error)



-- 
Dmitry

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-08-05 20:14                   ` Dmitry Torokhov
@ 2008-08-05 21:19                     ` Alfred E. Heggestad
  2008-08-18 17:47                       ` Dmitry Torokhov
  0 siblings, 1 reply; 15+ messages in thread
From: Alfred E. Heggestad @ 2008-08-05 21:19 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Oliver Neukum, linux-input, linux-kernel

Dmitry Torokhov wrote:
> On Tue, Aug 05, 2008 at 09:23:46PM +0200, Alfred E. Heggestad wrote:
>> Aug  5 21:11:13 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
>> Aug  5 21:11:13 io kernel: uhci_hcd 0000:00:1f.2: reserve dev 2 ep83-INT, period 32, phase 16, 14 us
>>
> 
> Ok, so urb_irq is pending here...
> 
>> ;;;
>> ;;; Press 'TAB' in shell to force Bell/Buzzer
>> ;;;
>>
>> Aug  5 21:11:22 io kernel: cm109: cm109_toggle_buzzer_async: ctl_urb_pending=0 buzzer_pending=0
>> Aug  5 21:11:22 io kernel: cm109: cm109_submit_buzz_toggle: buzzer_state=1
>> Aug  5 21:11:22 io kernel: cm109: cm109_urb_ctl_callback: buzzer_pending=0, ctl_urb_pending=1
>> Aug  5 21:11:22 io kernel: cm109: cm109_urb_ctl_callback: usb_submit_urb (urb_irq) failed -22     <--- note -EINVAL
> 
> And we trying to re-submit it.... How about the patch below?
> 

thanks, that fixes the problem. the error from usb_submit_urb() is gone..

although in your original patch, the irq_urb_pending flag was never set :)
I set it before calling usb_submit_urb(dev->urb_irq, GFP_ATOMIC); and now
it works very well.

I send a new complete CM109 patch below, hope things are working better
this time.. your input has been appreciated!


Input: driver for USB VoIP phones with CM109 chipset

Signed-off-by: Alfred E. Heggestad <aeh@db.org>
---

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index e99b788..987e4b7 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -180,6 +180,18 @@ config INPUT_YEALINK
 	  To compile this driver as a module, choose M here: the module will be
 	  called yealink.
 
+config INPUT_CM109
+	tristate "C-Media CM109 USB I/O Controller"
+	depends on INPUT && EXPERIMENTAL
+	select USB
+	---help---
+	  Say Y here if you want to enable keyboard and buzzer functions of the
+	  C-Media CM109 usb phones. The audio part is enabled by the generic
+	  usb sound driver, so you might want to enable that as well.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called cm109.
+
 config INPUT_UINPUT
 	tristate "User level driver support"
 	help
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index f48009b..d7db2ae 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_remote2.o
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)	+= keyspan_remote.o
 obj-$(CONFIG_INPUT_POWERMATE)		+= powermate.o
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
+obj-$(CONFIG_INPUT_CM109)		+= cm109.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
 obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
new file mode 100644
index 0000000..13698c9
--- /dev/null
+++ b/drivers/input/misc/cm109.c
@@ -0,0 +1,959 @@
+/*
+ * Driver for the VoIP USB phones with CM109 chipsets.
+ *
+ * Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2.
+ */
+
+/*
+ *   Tested devices:
+ *	- Komunikate KIP1000
+ *	- Genius G-talk
+ *	- Allied-Telesis Corega USBPH01
+ *	- carpophone II
+ *	- ...
+ *
+ * This driver is based on the yealink.c driver
+ *
+ * Thanks to:
+ *   - Authors of yealink.c
+ *   - Thomas Reitmayr
+ *   - Oliver Neukum for good review comments and code
+ *   - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
+ *   - Dmitry Torokhov for valuable input and review
+ *
+ * Todo:
+ *   - Read/write EEPROM
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+
+#define CM109_DEBUG 0
+
+#define DRIVER_VERSION "20080805"
+#define DRIVER_AUTHOR  "Alfred E. Heggestad"
+#define DRIVER_DESC    "CM109 phone driver"
+
+static char *phone = "kip1000";
+module_param(phone, charp, S_IRUSR);
+MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, carpo}");
+
+enum {
+	/* HID Registers */
+	HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down  */
+	HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0       */
+	HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1            */
+	HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL             */
+	HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
+	HID_OR1 = 0x01, /* GPO - General Purpose Output                 */
+	HID_OR2 = 0x02, /* Set GPIO to input/output mode                */
+	HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL          */
+
+	/* HID_IR0 */
+	RECORD_MUTE   = 1 << 3,
+	PLAYBACK_MUTE = 1 << 2,
+	VOLUME_DOWN   = 1 << 1,
+	VOLUME_UP     = 1 << 0,
+
+	/* HID_OR0 */
+	/* bits 7-6
+	   0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
+	      and SPDIF
+	   1: HID_OR0-3 are used as generic HID registers
+	   2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
+	      EEPROM_DATA0-1, EEPROM_CTRL (see Note)
+	   3: Reserved
+	 */
+	HID_OR_GPO_BUZ_SPDIF   = 0 << 6,
+	HID_OR_GENERIC_HID_REG = 1 << 6,
+	HID_OR_MAP_MCU_EEPROM  = 2 << 6,
+
+	BUZZER_ON = 1 << 5,
+};
+
+/* CM109 protocol packet */
+struct cm109_ctl_packet {
+	u8 byte[4];
+} __attribute__ ((packed));
+
+enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
+
+/* CM109 device structure */
+struct cm109_dev {
+	struct input_dev *idev;	 /* input device */
+	struct usb_device *udev; /* usb device */
+	struct usb_interface *intf;
+
+	/* irq input channel */
+	struct cm109_ctl_packet *irq_data;
+	dma_addr_t irq_dma;
+	struct urb *urb_irq;
+
+	/* control output channel */
+	struct cm109_ctl_packet *ctl_data;
+	dma_addr_t ctl_dma;
+	struct usb_ctrlrequest *ctl_req;
+	dma_addr_t ctl_req_dma;
+	struct urb *urb_ctl;
+	/*
+	 * The 3 bitfields below are protected by ctl_submit_lock.
+	 * They have to be separate since they are accessed from IRQ
+	 * context.
+	 */
+	unsigned irq_urb_pending:1;	/* irq_urb is in flight */
+	unsigned ctl_urb_pending:1;	/* ctl_urb is in flight */
+	unsigned buzzer_pending:1;	/* need to issue buzz command */
+	spinlock_t ctl_submit_lock;
+
+	unsigned char buzzer_state;	/* on/off */
+
+	/* flags */
+	unsigned open:1;
+	unsigned resetting:1;
+	unsigned shutdown:1;
+
+	/* This mutex protects writes to the above flags */
+	struct mutex pm_mutex;
+
+	unsigned short keymap[256];
+
+	char phys[64];		/* physical device path */
+	int key_code;		/* last reported key */
+	int keybit;		/* 0=new scan  1,2,4,8=scan columns  */
+	u8 gpi;			/* Cached value of GPI (high nibble) */
+};
+
+/******************************************************************************
+ * CM109 key interface
+ *****************************************************************************/
+
+/* Map device buttons to internal key events.
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+
+ Komunikate KIP1000 Keyboard Matrix
+
+     -> -- 1 -- 2 -- 3  --> GPI pin 4 (0x10)
+      |    |    |    |
+     <- -- 4 -- 5 -- 6  --> GPI pin 5 (0x20)
+      |    |    |    |
+     END - 7 -- 8 -- 9  --> GPI pin 6 (0x40)
+      |    |    |    |
+     OK -- * -- 0 -- #  --> GPI pin 7 (0x80)
+      |    |    |    |
+
+     /|\  /|\  /|\  /|\
+      |    |    |    |
+GPO
+pin:  3    2    1    0
+     0x8  0x4  0x2  0x1
+
+ */
+static unsigned short keymap_kip1000(int scancode)
+{
+	switch (scancode) {				/* phone key:   */
+	case 0x82: return KEY_NUMERIC_0;		/*   0          */
+	case 0x14: return KEY_NUMERIC_1;		/*   1          */
+	case 0x12: return KEY_NUMERIC_2;		/*   2          */
+	case 0x11: return KEY_NUMERIC_3;		/*   3          */
+	case 0x24: return KEY_NUMERIC_4;		/*   4          */
+	case 0x22: return KEY_NUMERIC_5;		/*   5          */
+	case 0x21: return KEY_NUMERIC_6;		/*   6          */
+	case 0x44: return KEY_NUMERIC_7;		/*   7          */
+	case 0x42: return KEY_NUMERIC_8;		/*   8          */
+	case 0x41: return KEY_NUMERIC_9;		/*   9          */
+	case 0x81: return KEY_NUMERIC_POUND;		/*   #          */
+	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */
+	case 0x88: return KEY_ENTER;			/*   pickup     */
+	case 0x48: return KEY_ESC;			/*   hangup     */
+	case 0x28: return KEY_LEFT;			/*   IN         */
+	case 0x18: return KEY_RIGHT;			/*   OUT        */
+	default:   return KEY_RESERVED;
+	}
+}
+
+/*
+  Contributed by Shaun Jackman <sjackman@gmail.com>
+
+  Genius G-Talk keyboard matrix
+     0 1 2 3
+  4: 0 4 8 Talk
+  5: 1 5 9 End
+  6: 2 6 # Up
+  7: 3 7 * Down
+*/
+static unsigned short keymap_gtalk(int scancode)
+{
+	switch (scancode) {
+	case 0x11: return KEY_NUMERIC_0;
+	case 0x21: return KEY_NUMERIC_1;
+	case 0x41: return KEY_NUMERIC_2;
+	case 0x81: return KEY_NUMERIC_3;
+	case 0x12: return KEY_NUMERIC_4;
+	case 0x22: return KEY_NUMERIC_5;
+	case 0x42: return KEY_NUMERIC_6;
+	case 0x82: return KEY_NUMERIC_7;
+	case 0x14: return KEY_NUMERIC_8;
+	case 0x24: return KEY_NUMERIC_9;
+	case 0x44: return KEY_NUMERIC_POUND;	/* # */
+	case 0x84: return KEY_NUMERIC_STAR;	/* * */
+	case 0x18: return KEY_ENTER;		/* Talk (green handset) */
+	case 0x28: return KEY_ESC;		/* End (red handset) */
+	case 0x48: return KEY_UP;		/* Menu up (rocker switch) */
+	case 0x88: return KEY_DOWN;		/* Menu down (rocker switch) */
+	default:   return KEY_RESERVED;
+	}
+}
+
+/*
+ * Keymap for Allied-Telesis Corega USBPH01
+ * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
+ *
+ * Contributed by july@nat.bg
+ */
+static unsigned short keymap_usbph01(int scancode)
+{
+	switch (scancode) {
+	case 0x11: return KEY_NUMERIC_0;		/*   0          */
+	case 0x21: return KEY_NUMERIC_1;		/*   1          */
+	case 0x41: return KEY_NUMERIC_2;		/*   2          */
+	case 0x81: return KEY_NUMERIC_3;		/*   3          */
+	case 0x12: return KEY_NUMERIC_4;		/*   4          */
+	case 0x22: return KEY_NUMERIC_5;		/*   5          */
+	case 0x42: return KEY_NUMERIC_6;		/*   6          */
+	case 0x82: return KEY_NUMERIC_7;		/*   7          */
+	case 0x14: return KEY_NUMERIC_8;		/*   8          */
+	case 0x24: return KEY_NUMERIC_9;		/*   9          */
+	case 0x44: return KEY_NUMERIC_POUND;		/*   #          */
+	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */
+	case 0x18: return KEY_ENTER;			/*   pickup     */
+	case 0x28: return KEY_ESC;			/*   hangup     */
+	case 0x48: return KEY_LEFT;			/*   IN         */
+	case 0x88: return KEY_RIGHT;			/*   OUT        */
+	default:   return KEY_RESERVED;
+	}
+}
+
+#if 1
+/*
+ * NOTE: THIS IS WORK-IN-PROGRESS!
+ *
+ * Keymap for carpophone II
+ * http://www.carpo.de/voip/cms/de/en/page_3633.jsp
+ *
+ * Contributed by Stephan Berberig <s.berberig@arcor.de>
+ *
+ * carpophone II keyboard matrix:
+ *
+ * +            UP
+ * -   Vol_up  Carpo  Vol_down  Vol_mute
+ *             DOWN
+ *
+ *     Talk    Hold    End
+ *      1       2       3
+ *      4       5       6
+ *      7       8       9
+ *      *       0       #
+ *
+ *
+ * The following buttons are handled by the special_keymap() function:
+ * Hold, Vol_down, Vol_up, Vol_mute
+ *
+ * GPIO pin mapping:
+
+      ? -- 8 -- 5 -- 2   --> GPI pin 4 (0x10)
+      |    |    |    |
+      ? -- 9 -- 6 -- 3   --> GPI pin 5 (0x20)
+      |    |    |    |
+      ? --down--BS-- up  --> GPI pin 6 (0x40)
+      |    |    |    |
+      ? -volup-ESC-ENTER --> GPI pin 7 (0x80)
+      |    |    |    |
+
+     /|\  /|\  /|\  /|\
+      |    |    |    |
+GPO
+pin:  3    2    1    0
+     0x8  0x4  0x2  0x1
+ */
+static unsigned short keymap_carpo(int scancode)
+{
+	switch (scancode) {				/* phone key:   */
+	//case 0xZZ: return KEY_NUMERIC_0;		/*   0          */
+	//case 0xZZ: return KEY_NUMERIC_1;		/*   1          */
+	case 0x11: return KEY_NUMERIC_2;		/*   2          */
+	case 0x21: return KEY_NUMERIC_3;		/*   3          */
+	//case 0xZZ: return KEY_NUMERIC_4;		/*   4          */
+	case 0x12: return KEY_NUMERIC_5;		/*   5          */
+	case 0x22: return KEY_NUMERIC_6;		/*   6          */
+	//case 0xZZ: return KEY_NUMERIC_7;		/*   7          */
+	case 0x14: return KEY_NUMERIC_8;		/*   8          */
+	case 0x24: return KEY_NUMERIC_9;		/*   9          */
+	case 0x81: return KEY_ENTER;			/*   Talk       */
+	case 0x82: return KEY_ESC;			/*   End        */
+	case 0x41: return KEY_UP;			/*   up         */
+	case 0x44: return KEY_DOWN;			/*   down       */
+	case 0x42: return KEY_BACKSPACE;		/*   Carpo      */
+	case 0x84: return KEY_VOLUMEUP;			/*   +          */
+	//case 0xZZ: return KEY_VOLUMEDOWN;		/*   -          */
+	//case 0xZZ: return KEY_NUMERIC_POUND;		/*   #          */
+	//case 0xZZ: return KEY_NUMERIC_STAR;		/*   *          */
+	default:   return KEY_RESERVED;
+	}
+}
+#endif
+
+static unsigned short (*keymap)(int) = keymap_kip1000;
+
+/*
+ * Completes a request by converting the data into events for the
+ * input subsystem.
+ */
+static void report_key(struct cm109_dev *dev, int key)
+{
+	struct input_dev *idev = dev->idev;
+
+	if (dev->key_code >= 0) {
+		/* old key up */
+		input_report_key(idev, dev->key_code, 0);
+	}
+
+	dev->key_code = key;
+	if (key >= 0) {
+		/* new valid key */
+		input_report_key(idev, key, 1);
+	}
+
+	input_sync(idev);
+}
+
+static unsigned short special_keymap(int code)
+{
+	switch (code) {
+	case RECORD_MUTE:   return KEY_MUTE;
+	case PLAYBACK_MUTE: return KEY_MUTE;
+	case VOLUME_DOWN:   return KEY_VOLUMEDOWN;
+	case VOLUME_UP:     return KEY_VOLUMEUP;
+	default:            return KEY_RESERVED;
+	}
+}
+
+
+/******************************************************************************
+ * CM109 usb communication interface
+ *****************************************************************************/
+
+static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
+{
+	int error;
+
+	if (dev->buzzer_state)
+		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+	else
+		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+	error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+	if (error)
+		err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error);
+}
+
+/*
+ * IRQ handler
+ */
+static void cm109_urb_irq_callback(struct urb *urb)
+{
+	struct cm109_dev *dev = urb->context;
+	const int status = urb->status;
+	int error;
+
+#if CM109_DEBUG
+	info("### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x",
+	     dev->irq_data->byte[0],
+	     dev->irq_data->byte[1],
+	     dev->irq_data->byte[2],
+	     dev->irq_data->byte[3],
+	     dev->keybit);
+#endif
+
+	if (status) {
+		if (status == -ESHUTDOWN)
+			return;
+		err("%s: urb status %d", __func__, status);
+	}
+
+	/* Special keys */
+	if (dev->irq_data->byte[HID_IR0] & 0x0f) {
+		const int code = (dev->irq_data->byte[HID_IR0] & 0x0f);
+		report_key(dev, special_keymap(code));
+	}
+
+	/* Scan key column */
+	if (dev->keybit == 0xf) {
+
+		/* Any changes ? */
+		if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
+			goto out;
+
+		dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
+		dev->keybit = 0x1;
+	} else {
+		report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
+
+		dev->keybit <<= 1;
+		if (dev->keybit > 0x8)
+			dev->keybit = 0xf;
+	}
+
+ out:
+
+	spin_lock(&dev->ctl_submit_lock);
+
+	dev->irq_urb_pending = 0;
+
+	if (likely(!dev->shutdown)) {
+
+		if (dev->buzzer_state)
+			dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+		else
+			dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+		dev->ctl_data->byte[HID_OR1] = dev->keybit;
+		dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
+		dev->buzzer_pending = 0;
+		dev->ctl_urb_pending = 1;
+
+		error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+		if (error)
+			err("%s: usb_submit_urb (urb_ctl) failed %d",
+				__func__, error);
+	}
+
+	spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_urb_ctl_callback(struct urb *urb)
+{
+	struct cm109_dev *dev = urb->context;
+	const int status = urb->status;
+	int error;
+
+#if CM109_DEBUG
+	info("### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]",
+	     dev->ctl_data->byte[0],
+	     dev->ctl_data->byte[1],
+	     dev->ctl_data->byte[2],
+	     dev->ctl_data->byte[3]);
+#endif
+
+	if (status)
+		err("%s: urb status %d", __func__, status);
+
+	spin_lock(&dev->ctl_submit_lock);
+
+	dev->ctl_urb_pending = 0;
+
+	if (likely(!dev->shutdown)) {
+
+		if (dev->buzzer_pending) {
+			dev->buzzer_pending = 0;
+			dev->ctl_urb_pending = 1;
+			cm109_submit_buzz_toggle(dev);
+		} else if (likely(!dev->irq_urb_pending)) {
+			/* ask for key data */
+			dev->irq_urb_pending = 1;
+			error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
+			if (error)
+				err("%s: usb_submit_urb (urb_irq) failed %d",
+					__func__, error);
+		}
+	}
+
+	spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->ctl_submit_lock, flags);
+
+	if (dev->ctl_urb_pending) {
+		/* URB completion will resubmit */
+		dev->buzzer_pending = 1;
+	} else {
+		dev->ctl_urb_pending = 1;
+		cm109_submit_buzz_toggle(dev);
+	}
+
+	spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
+}
+
+static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
+{
+	int error;
+
+	if (on)
+		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+	else
+		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+	error = usb_control_msg(dev->udev,
+				usb_sndctrlpipe(dev->udev, 0),
+				dev->ctl_req->bRequest,
+				dev->ctl_req->bRequestType,
+				le16_to_cpu(dev->ctl_req->wValue),
+				le16_to_cpu(dev->ctl_req->wIndex),
+				dev->ctl_data,
+				USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
+	if (error && error != EINTR)
+		err("%s: usb_control_msg() failed %d", __func__, error);
+}
+
+static void cm109_stop_traffic(struct cm109_dev *dev)
+{
+	dev->shutdown = 1;
+	/*
+	 * Make sure other CPUs see this
+	 */
+	smp_wmb();
+
+	usb_kill_urb(dev->urb_ctl);
+	usb_kill_urb(dev->urb_irq);
+
+	cm109_toggle_buzzer_sync(dev, 0);
+
+	dev->shutdown = 0;
+	smp_wmb();
+}
+
+static void cm109_restore_state(struct cm109_dev *dev)
+{
+	if (dev->open) {
+		/*
+		 * Restore buzzer state.
+		 * This will also kick regular URB submission
+		 */
+		cm109_toggle_buzzer_async(dev);
+	}
+}
+
+/******************************************************************************
+ * input event interface
+ *****************************************************************************/
+
+static int cm109_input_open(struct input_dev *idev)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+	int error;
+
+	error = usb_autopm_get_interface(dev->intf);
+	if (error < 0) {
+		err("%s - cannot autoresume, result %d",
+		    __func__, error);
+		return error;
+	}
+
+	mutex_lock(&dev->pm_mutex);
+
+	dev->buzzer_state = 0;
+	dev->key_code = -1;	/* no keys pressed */
+	dev->keybit = 0xf;
+
+	/* issue INIT */
+	dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
+	dev->ctl_data->byte[HID_OR1] = dev->keybit;
+	dev->ctl_data->byte[HID_OR2] = dev->keybit;
+	dev->ctl_data->byte[HID_OR3] = 0x00;
+
+	error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
+	if (error) {
+		err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error);
+	} else
+		dev->open = 1;
+
+	mutex_unlock(&dev->pm_mutex);
+
+	if (error)
+		usb_autopm_put_interface(dev->intf);
+
+	return error;
+}
+
+static void cm109_input_close(struct input_dev *idev)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+
+	mutex_lock(&dev->pm_mutex);
+
+	/*
+	 * Once we are here event delivery is stopped so we
+	 * don't need to worry about someone starting buzzer
+	 * again
+	 */
+	cm109_stop_traffic(dev);
+	dev->open = 0;
+
+	mutex_unlock(&dev->pm_mutex);
+
+	usb_autopm_put_interface(dev->intf);
+}
+
+static int cm109_input_ev(struct input_dev *idev, unsigned int type,
+			  unsigned int code, int value)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+
+#if CM109_DEBUG
+	info("input_ev: type=%u code=%u value=%d", type, code, value);
+#endif
+
+	if (type != EV_SND)
+		return -EINVAL;
+
+	switch (code) {
+	case SND_TONE:
+	case SND_BELL:
+		dev->buzzer_state = !!value;
+		if (!dev->resetting)
+			cm109_toggle_buzzer_async(dev);
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+
+/******************************************************************************
+ * Linux interface and usb initialisation
+ *****************************************************************************/
+
+struct driver_info {
+	char *name;
+};
+
+static const struct driver_info info_cm109 = {
+	.name = "CM109 USB driver",
+};
+
+enum {
+	VENDOR_ID        = 0x0d8c, /* C-Media Electronics */
+	PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
+};
+
+/* table of devices that work with this driver */
+static const struct usb_device_id cm109_usb_table[] = {
+	{
+		.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+				USB_DEVICE_ID_MATCH_INT_INFO,
+		.idVendor = VENDOR_ID,
+		.idProduct = PRODUCT_ID_CM109,
+		.bInterfaceClass = USB_CLASS_HID,
+		.bInterfaceSubClass = 0,
+		.bInterfaceProtocol = 0,
+		.driver_info = (kernel_ulong_t) &info_cm109
+	},
+	/* you can add more devices here with product ID 0x0008 - 0x000f */
+	{ }
+};
+
+static void cm109_usb_cleanup(struct cm109_dev *dev)
+{
+	if (dev->ctl_req)
+		usb_buffer_free(dev->udev, sizeof(*(dev->ctl_req)),
+				dev->ctl_req, dev->ctl_req_dma);
+	if (dev->ctl_data)
+		usb_buffer_free(dev->udev, USB_PKT_LEN,
+				dev->ctl_data, dev->ctl_dma);
+	if (dev->irq_data)
+		usb_buffer_free(dev->udev, USB_PKT_LEN,
+				dev->irq_data, dev->irq_dma);
+
+	usb_free_urb(dev->urb_irq);	/* parameter validation in core/urb */
+	usb_free_urb(dev->urb_ctl);	/* parameter validation in core/urb */
+	kfree(dev);
+}
+
+static void cm109_usb_disconnect(struct usb_interface *interface)
+{
+	struct cm109_dev *dev = usb_get_intfdata(interface);
+
+	usb_set_intfdata(interface, NULL);
+	input_unregister_device(dev->idev);
+	cm109_usb_cleanup(dev);
+}
+
+static int cm109_usb_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct driver_info *nfo = (struct driver_info *)id->driver_info;
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct cm109_dev *dev;
+	struct input_dev *input_dev = NULL;
+	int ret, pipe, i;
+	int error = -ENOMEM;
+
+	interface = intf->cur_altsetting;
+	endpoint = &interface->endpoint[0].desc;
+
+	if (!usb_endpoint_is_int_in(endpoint))
+		return -ENODEV;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	spin_lock_init(&dev->ctl_submit_lock);
+	mutex_init(&dev->pm_mutex);
+
+	dev->udev = udev;
+	dev->intf = intf;
+
+	dev->idev = input_dev = input_allocate_device();
+	if (!input_dev)
+		goto err_out;
+
+	/* allocate usb buffers */
+	dev->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+					 GFP_KERNEL, &dev->irq_dma);
+	if (!dev->irq_data)
+		goto err_out;
+
+	dev->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+					 GFP_KERNEL, &dev->ctl_dma);
+	if (!dev->ctl_data)
+		goto err_out;
+
+	dev->ctl_req = usb_buffer_alloc(udev, sizeof(*(dev->ctl_req)),
+					GFP_KERNEL, &dev->ctl_req_dma);
+	if (!dev->ctl_req)
+		goto err_out;
+
+	/* allocate urb structures */
+	dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->urb_irq)
+		goto err_out;
+
+	dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->urb_ctl)
+		goto err_out;
+
+	/* get a handle to the interrupt data pipe */
+	pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+	ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	if (ret != USB_PKT_LEN)
+		err("invalid payload size %d, expected %d", ret, USB_PKT_LEN);
+
+	/* initialise irq urb */
+	usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
+			 USB_PKT_LEN,
+			 cm109_urb_irq_callback, dev, endpoint->bInterval);
+	dev->urb_irq->transfer_dma = dev->irq_dma;
+	dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	dev->urb_irq->dev = udev;
+
+	/* initialise ctl urb */
+	dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+					USB_DIR_OUT;
+	dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
+	dev->ctl_req->wValue = cpu_to_le16(0x200);
+	dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+	dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
+
+	usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+			     (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
+			     cm109_urb_ctl_callback, dev);
+	dev->urb_ctl->setup_dma = dev->ctl_req_dma;
+	dev->urb_ctl->transfer_dma = dev->ctl_dma;
+	dev->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP |
+					URB_NO_TRANSFER_DMA_MAP;
+	dev->urb_ctl->dev = udev;
+
+	/* find out the physical bus location */
+	usb_make_path(udev, dev->phys, sizeof(dev->phys));
+	strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+	/* register settings for the input device */
+	input_dev->name = nfo->name;
+	input_dev->phys = dev->phys;
+	usb_to_input_id(udev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, dev);
+	input_dev->open = cm109_input_open;
+	input_dev->close = cm109_input_close;
+	input_dev->event = cm109_input_ev;
+
+	input_dev->keycode = dev->keymap;
+	input_dev->keycodesize = sizeof(unsigned char);
+	input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
+	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+
+	/* register available key events */
+	for (i = 0; i < 256; i++) {
+		unsigned short k = keymap(i);
+		dev->keymap[i] = k;
+		__set_bit(k, input_dev->keybit);
+	}
+
+	/* special keys */
+	for (i = 0; i < 16; i++) {
+		unsigned short k = special_keymap(i);
+		__set_bit(k, input_dev->keybit);
+	}
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	error = input_register_device(dev->idev);
+	if (error)
+		goto err_out;
+
+	usb_set_intfdata(intf, dev);
+
+	return 0;
+
+ err_out:
+	input_free_device(input_dev);
+	cm109_usb_cleanup(dev);
+	return error;
+}
+
+static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	info("cm109: usb_suspend (event=%d)", message.event);
+
+	mutex_lock(&dev->pm_mutex);
+	cm109_stop_traffic(dev);
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static int cm109_usb_resume(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	info("cm109: usb_resume");
+
+	mutex_lock(&dev->pm_mutex);
+	cm109_restore_state(dev);
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static int cm109_usb_pre_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	mutex_lock(&dev->pm_mutex);
+
+	/*
+	 * Make sure input events don't try to toggle buzzer
+	 * while we are resetting
+	 */
+	dev->resetting = 1;
+	smp_wmb();
+
+	cm109_stop_traffic(dev);
+
+	return 0;
+}
+
+static int cm109_usb_post_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	dev->resetting = 0;
+	smp_wmb();
+
+	cm109_restore_state(dev);
+
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static struct usb_driver cm109_driver = {
+	.name		= "cm109",
+	.probe		= cm109_usb_probe,
+	.disconnect	= cm109_usb_disconnect,
+	.suspend	= cm109_usb_suspend,
+	.resume		= cm109_usb_resume,
+	.reset_resume	= cm109_usb_resume,
+	.pre_reset	= cm109_usb_pre_reset,
+	.post_reset	= cm109_usb_post_reset,
+	.id_table	= cm109_usb_table,
+	.supports_autosuspend = 1,
+};
+
+static int __init cm109_select_keymap(void)
+{
+	/* Load the phone keymap */
+	if (!strcasecmp(phone, "kip1000")) {
+		keymap = keymap_kip1000;
+		info("Keymap for Komunikate KIP1000 phone loaded");
+	} else if (!strcasecmp(phone, "gtalk")) {
+		keymap = keymap_gtalk;
+		info("Keymap for Genius G-talk phone loaded");
+	} else if (!strcasecmp(phone, "usbph01")) {
+		keymap = keymap_usbph01;
+		info("Keymap for Allied-Telesis Corega USBPH01 phone loaded");
+	} else if (!strcasecmp(phone, "carpo")) {
+		keymap = keymap_carpo;
+		info("Keymap for carpophone II loaded (Incomplete)");
+	} else {
+		err("Unsupported phone: %s", phone);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __init cm109_init(void)
+{
+	int err;
+
+	err = cm109_select_keymap();
+	if (err)
+		return err;
+
+	err = usb_register(&cm109_driver);
+	if (err)
+		return err;
+
+	info(DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR);
+
+	return 0;
+}
+
+static void __exit cm109_exit(void)
+{
+	usb_deregister(&cm109_driver);
+}
+
+module_init(cm109_init);
+module_exit(cm109_exit);
+
+MODULE_DEVICE_TABLE(usb, cm109_usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH] input: driver for USB VoIP phones with CM109 chipset #2
  2008-08-05 21:19                     ` Alfred E. Heggestad
@ 2008-08-18 17:47                       ` Dmitry Torokhov
  0 siblings, 0 replies; 15+ messages in thread
From: Dmitry Torokhov @ 2008-08-18 17:47 UTC (permalink / raw)
  To: Alfred E. Heggestad; +Cc: Oliver Neukum, linux-input, linux-kernel

Hi Alfred,

On Tue, Aug 05, 2008 at 11:19:20PM +0200, Alfred E. Heggestad wrote:
>
> I send a new complete CM109 patch below, hope things are working better
> this time.. your input has been appreciated!
>

I reworked support for sepcial keys so they can also be set via
setkeycode and dropped the keymap for carpophone since is was marked
as incomplete. The driver is in the 'next' branch of my tree and will
be merged when 2.6.28 opens.

Thank you for your patience.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2008-08-18 17:47 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-06-27 12:24 [PATCH] input: driver for USB VoIP phones with CM109 chipset #2 Oliver Neukum
2008-06-28 15:03 ` Alfred E. Heggestad
2008-06-30 11:06   ` Oliver Neukum
2008-07-25 14:14   ` Dmitry Torokhov
2008-07-26 15:55     ` Alfred E. Heggestad
2008-07-26 18:45       ` Oliver Neukum
2008-08-03 22:04         ` Alfred E. Heggestad
2008-08-04 11:56           ` Oliver Neukum
2008-08-04 20:34             ` Alfred E. Heggestad
2008-08-04 22:08               ` Oliver Neukum
2008-08-05 19:23                 ` Alfred E. Heggestad
2008-08-05 20:14                   ` Dmitry Torokhov
2008-08-05 21:19                     ` Alfred E. Heggestad
2008-08-18 17:47                       ` Dmitry Torokhov
2008-07-29  5:47       ` Dmitry Torokhov

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