[Title] add maru_touchscreen file
authorgiwoong.kim <giwoong.kim@samsung.com>
Thu, 16 Feb 2012 05:30:41 +0000 (14:30 +0900)
committergiwoong.kim <giwoong.kim@samsung.com>
Thu, 16 Feb 2012 05:32:59 +0000 (14:32 +0900)
[Type] feature
[Module] emulator / touch
[Priority] major
[Jira#]
[Redmine#]
[Problem]
[Cause]
[Solution]
[TestCase]

Change-Id: I9fde4c5635163ba76c5ae162ec54ee50abf5ec10

drivers/maru/maru_touchscreen.c [new file with mode: 0755]

diff --git a/drivers/maru/maru_touchscreen.c b/drivers/maru/maru_touchscreen.c
new file mode 100755 (executable)
index 0000000..d3a4eed
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * Virtual USB Touchscreen device driver
+ * Based on drivers/input/tablet/wacom_sys.c:
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: 
+ *  GiWoong Kim <giwoong.kim@samsung.com>\r
+ *  Hyunjun Son <hj79.son@samsung.com>\r
+ *  DongKyun Yun <dk77.yun@samsung.com>\r
+ *  YeongKyoon Lee <yeongkyoon.lee@samsung.com>
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb/input.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("s-core");
+MODULE_DESCRIPTION("Emulator Touchscreen driver for x86");
+
+/* touchscreen device features */
+#define USB_VENDOR_ID_SAMSUNG 0x0419
+#define EMUL_TOUCHSCREEN_PACKET_LEN 7
+#define TOUCHSCREEN_RESOLUTION_X 5040
+#define TOUCHSCREEN_RESOLUTION_Y 3780
+#define ABS_PRESSURE_MAX 255
+
+struct emul_touchscreen {
+    dma_addr_t data_dma;
+    struct input_dev *emuldev;
+    struct usb_device *usbdev;
+    struct usb_interface *intf;
+    struct urb *irq;
+    unsigned char *data;
+    struct mutex lock;
+    unsigned int open:1;
+    char phys[32];
+};
+
+/* These structure must match the qemu definitions */
+typedef struct USBEmulTouchscreenPacket {
+    uint16_t x, y, z;
+    uint8_t state;
+} USBEmulTouchscreenPacket;
+
+
+static void emul_touchscreen_sys_irq(struct urb *urb)
+{
+    struct emul_touchscreen *usb_ts = urb->context;
+    struct input_dev *input_dev = usb_ts->emuldev;
+    USBEmulTouchscreenPacket *packet = (USBEmulTouchscreenPacket *)usb_ts->data;
+    int retval;
+
+    switch (urb->status) {
+        case 0:
+            /* success */
+            break;
+        case -ECONNRESET:
+        case -ENOENT:
+        case -ESHUTDOWN:
+            /* this urb is terminated, clean up */
+            dbg("%s - urb shutting down with status: %d", __func__, urb->status);
+            return;
+        default:
+            dbg("%s - nonzero urb status received: %d", __func__, urb->status);
+            goto exit;
+    }
+
+    if (packet->state != 0) { //pressed
+        input_report_abs(input_dev, ABS_MT_TRACKING_ID, packet->z);
+        input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 5);
+        input_report_abs(input_dev, ABS_MT_POSITION_X, packet->x);
+        input_report_abs(input_dev, ABS_MT_POSITION_Y, packet->y);
+        input_mt_sync(input_dev);
+    } else { //release
+        if (packet->z == 1) {
+            input_report_abs(input_dev, ABS_MT_TRACKING_ID, 1);
+            input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 0);
+            input_mt_sync(input_dev);
+        }
+        input_report_abs(input_dev, ABS_MT_TRACKING_ID, 0);
+        input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 0);
+        input_mt_sync(input_dev);
+    }
+
+    input_sync(input_dev);
+
+ exit:
+    usb_mark_last_busy(usb_ts->usbdev);
+    retval = usb_submit_urb(urb, GFP_ATOMIC);
+    if (retval) {
+        err("%s - usb_submit_urb failed with result %d", __func__, retval);
+    }
+}
+
+static int emul_touchscreen_open(struct input_dev *dev)
+{
+    struct emul_touchscreen *usb_ts = input_get_drvdata(dev);
+
+    mutex_lock(&usb_ts->lock);
+    usb_ts->irq->dev = usb_ts->usbdev;
+
+    if (usb_autopm_get_interface(usb_ts->intf) < 0) {
+        mutex_unlock(&usb_ts->lock);
+        return -EIO;
+    }
+
+    if (usb_submit_urb(usb_ts->irq, GFP_KERNEL)) {
+        usb_autopm_put_interface(usb_ts->intf);
+        mutex_unlock(&usb_ts->lock);
+        return -EIO;
+    }
+
+    usb_ts->open = 1;
+    usb_ts->intf->needs_remote_wakeup = 1;
+
+    mutex_unlock(&usb_ts->lock);
+    return 0;
+}
+
+static void emul_touchscreen_close(struct input_dev *dev)
+{
+    struct emul_touchscreen *usb_ts = input_get_drvdata(dev);
+
+    mutex_lock(&usb_ts->lock);
+    usb_kill_urb(usb_ts->irq);
+    usb_ts->open = 0;
+    usb_ts->intf->needs_remote_wakeup = 0;
+    mutex_unlock(&usb_ts->lock);
+}
+
+static int emul_touchscreen_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+    struct usb_endpoint_descriptor *endpoint;
+    struct emul_touchscreen *usb_ts;
+    int error = -ENOMEM;
+
+    usb_ts = kzalloc(sizeof(struct emul_touchscreen), GFP_KERNEL);
+    if (!usb_ts) {
+        goto fail1;
+    }
+
+    usb_ts->usbdev = interface_to_usbdev(intf);
+    usb_ts->data = usb_buffer_alloc(usb_ts->usbdev, 10, GFP_KERNEL, &usb_ts->data_dma);
+    if (!usb_ts->data) {
+        goto fail1;
+    }
+
+    usb_ts->irq = usb_alloc_urb(0, GFP_KERNEL);
+    if (!usb_ts->irq) {
+        goto fail2;
+    }
+
+    usb_ts->emuldev = input_allocate_device();
+    if (!usb_ts->emuldev) {
+        goto fail1;
+    }
+    
+    usb_ts->intf = intf;
+
+    mutex_init(&usb_ts->lock);
+    usb_make_path(usb_ts->usbdev, usb_ts->phys, sizeof(usb_ts->phys));
+    strlcat(usb_ts->phys, "/input0", sizeof(usb_ts->phys));
+
+    usb_ts->emuldev->name = "Tizen Touchscreen";
+    usb_to_input_id(usb_ts->usbdev, &usb_ts->emuldev->id);
+
+    usb_ts->emuldev->dev.parent = &intf->dev;
+
+    input_set_drvdata(usb_ts->emuldev, usb_ts);
+
+    usb_ts->emuldev->open = emul_touchscreen_open;
+    usb_ts->emuldev->close = emul_touchscreen_close;
+
+    endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+    usb_ts->emuldev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+    usb_ts->emuldev->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
+    usb_ts->emuldev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
+
+    input_set_abs_params(usb_ts->emuldev, ABS_X, 0, TOUCHSCREEN_RESOLUTION_X, 4, 0);
+    input_set_abs_params(usb_ts->emuldev, ABS_Y, 0, TOUCHSCREEN_RESOLUTION_Y, 4, 0);
+
+    /* for multitouch */
+    input_set_abs_params(usb_ts->emuldev, ABS_MT_TRACKING_ID, 0, 1, 0, 0);
+    input_set_abs_params(usb_ts->emuldev, ABS_MT_TOUCH_MAJOR, 0, ABS_PRESSURE_MAX, 0, 0);
+    input_set_abs_params(usb_ts->emuldev, ABS_MT_POSITION_X, 0, TOUCHSCREEN_RESOLUTION_X, 0, 0);
+    input_set_abs_params(usb_ts->emuldev, ABS_MT_POSITION_Y, 0, TOUCHSCREEN_RESOLUTION_Y, 0, 0);
+
+    usb_fill_int_urb(usb_ts->irq, usb_ts->usbdev,
+             usb_rcvintpipe(usb_ts->usbdev, endpoint->bEndpointAddress),
+             usb_ts->data, EMUL_TOUCHSCREEN_PACKET_LEN,
+             emul_touchscreen_sys_irq, usb_ts, endpoint->bInterval);
+    usb_ts->irq->transfer_dma = usb_ts->data_dma;
+    usb_ts->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+    error = input_register_device(usb_ts->emuldev);
+    if (error) {
+        goto fail3;
+    }
+
+    usb_set_intfdata(intf, usb_ts);
+    return 0;
+
+ fail3:    usb_free_urb(usb_ts->irq);
+ fail2:    usb_buffer_free(usb_ts->usbdev, 10, usb_ts->data, usb_ts->data_dma);
+ fail1:    input_free_device(usb_ts->emuldev);
+    kfree(usb_ts);
+    return error;
+}
+
+static void emul_touchscreen_disconnect(struct usb_interface *intf)
+{
+    struct emul_touchscreen *usb_ts = usb_get_intfdata(intf);
+
+    usb_set_intfdata(intf, NULL);
+    if (usb_ts) {
+        usb_kill_urb(usb_ts->irq);
+        input_unregister_device(usb_ts->emuldev);
+        usb_free_urb(usb_ts->irq);
+        usb_buffer_free(interface_to_usbdev(intf), 10, usb_ts->data, usb_ts->data_dma);
+        kfree(usb_ts);
+    }
+}
+
+static int emul_touchscreen_suspend(struct usb_interface *intf, pm_message_t message)
+{
+    struct emul_touchscreen *usb_ts = usb_get_intfdata(intf);
+
+    mutex_lock(&usb_ts->lock);
+    usb_kill_urb(usb_ts->irq);
+    mutex_unlock(&usb_ts->lock);
+
+    return 0;
+}
+
+static int emul_touchscreen_resume(struct usb_interface *intf)
+{
+    struct emul_touchscreen *usb_ts = usb_get_intfdata(intf);
+    int rv;
+
+    mutex_lock(&usb_ts->lock);
+    if (usb_ts->open) {
+        rv = usb_submit_urb(usb_ts->irq, GFP_NOIO);
+    } else {
+        rv = 0;
+    }
+    mutex_unlock(&usb_ts->lock);
+
+    return rv;
+}
+
+static struct usb_device_id emul_usb_touchscreen_table[] = {
+    { USB_DEVICE(USB_VENDOR_ID_SAMSUNG, 0x00) },
+    { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, emul_usb_touchscreen_table);
+
+static struct usb_driver emul_touchscreen_driver = {
+    .name                 = "usb_emul_touchscreen",
+    .id_table             = emul_usb_touchscreen_table,
+    .probe                = emul_touchscreen_probe,
+    .disconnect           = emul_touchscreen_disconnect,
+    .suspend              = emul_touchscreen_suspend,
+    .resume               = emul_touchscreen_resume,
+    .reset_resume         = emul_touchscreen_resume,
+    .supports_autosuspend = 1,
+};
+
+static int __init emul_touchscreen_init(void)
+{
+    int result = usb_register(&emul_touchscreen_driver);
+    if (result == 0) {
+        printk(KERN_ERR "emul_touchscreen_init: usb_register=%d\n", result);
+    }
+
+    return result;
+}
+
+static void __exit emul_touchscreen_exit(void)
+{
+    usb_deregister(&emul_touchscreen_driver);
+}
+
+module_init(emul_touchscreen_init);
+module_exit(emul_touchscreen_exit);