--- /dev/null
+/*
+ * 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);