new file mode 100644
@@ -0,0 +1,1193 @@
+/*
+ * HID driver for Cando dual-touch panels
+ *
+ * Copyright (c) 2010 Stephane Chatty <chatty@enac.fr>
+ *
+ */
+
+/*
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include "hid-ids.h"
+#include "hid-ubt880.h"
+
+#define UBT780_DEBUG
+
+#ifdef UBT780_DEBUG
+#define UBT_DUMMY_DEBUG if (ubt_debug) printk(KERN_DEBUG "ubt880: %s:%s line %i\n", __FILE__, __func__ , __LINE__);
+#else
+#define UBT_DUMMY_DEBUG
+#endif
+
+static int ubt_major;
+static struct cdev ubt_cdev;
+static struct class *ubt_class;
+static struct ubt_chrdev *ubt_table[UBT780_MAX_DEVICES];
+static DEFINE_MUTEX(minors_lock);
+static int ubt_debug = 0;
+module_param_named(debug_enabled, ubt_debug, int, 0600);
+MODULE_PARM_DESC(debug_enabled, "Toggle huge and ugly UBT debugging messages.");
+
+static int isCalibrated = 0;
+
+static int mode = 0;
+
+static int LTx = -1;
+static int LTy = -1;
+static int LBx = -1;
+static int LBy = -1;
+static int RTx = -1;
+static int RTy = -1;
+static int RBx = -1;
+static int RBy = -1;
+
+static int LTX = -1;
+static int LTY = -1;
+static int LBX = -1;
+static int LBY = -1;
+static int RTX = -1;
+static int RTY = -1;
+static int RBX = -1;
+static int RBY = -1;
+
+static int Xoff_A = -1;
+static int Xmag_A = -1;
+static int Xmag_B = -1;
+static int Yoff_A = -1;
+static int Ymag_A = -1;
+static int Ymag_B = -1;
+
+module_param(mode, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(mode, "1 - digitizer mode, 0 - mouse mode");
+
+module_param(isCalibrated, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(isCalibrated, "Calibration data is set to driver.");
+/*---------------------------------------------------------------------*/
+
+module_param(LTx, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(LTx, "point x of left-up corner, screen coordinate");
+
+module_param(LTy, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(LTy, "point y of left-up corner, screen coordinate");
+
+module_param(LBx, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(LBx, "point x of left-bot corner, screen coordinate");
+
+module_param(LBy, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(LBy, "point y of left-bot corner, screen coordinate");
+
+module_param(RTx, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(RTx, "point x of rigth-up corner, screen coordinate");
+
+module_param(RTy, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(RTy, "point y of rigth-up corner, screen coordinate");
+
+module_param(RBx, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(RBx, "point x of rigth-bot corner, screen coordinate");
+
+module_param(RBy, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(RBy, "point y of rigth-bot corner, screen coordinate");
+
+/*---------------------------------------------------------------------*/
+
+module_param(LTX, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(LTX, "point x of left-up corner, board coordinate");
+
+module_param(LTY, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(LTY, "point y of left-up corner, board coordinate");
+
+module_param(LBX, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(LBX, "point x of left-bot corner, board coordinate");
+
+module_param(LBY, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(LBY, "point y of left-bot corner, board coordinate");
+
+module_param(RTX, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(RTX, "point x of rigth-up corner, board coordinate");
+
+module_param(RTY, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(RTY, "point y of rigth-up corner, board coordinate");
+
+module_param(RBX, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(RBX, "point x of rigth-bot corner, board coordinate");
+
+module_param(RBY, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(RBY, "point y of rigth-bot corner, board coordinate");
+/*-----------------------------------------------------------------*/
+module_param(Xoff_A, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(Xoff_A, "point x of rigth-up corner, board coordinate");
+
+module_param(Xmag_A, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(Xmag_A, "point y of rigth-up corner, board coordinate");
+
+module_param(Xmag_B, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(Xmag_B, "point x of rigth-bot corner, board coordinate");
+
+module_param(Ymag_A, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(Ymag_A, "point y of rigth-bot corner, board coordinate");
+
+module_param(Ymag_B, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(Ymag_B, "point y of rigth-bot corner, board coordinate");
+
+module_param(Yoff_A, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+MODULE_PARM_DESC(Yoff_A, "point x of rigth-up corner, board coordinate");
+
+/*Character device forward declarations*/
+static int ubt880_open_io(struct inode *node, struct file *file);
+static int ubt880_release_io(struct inode *node, struct file *file);
+static long ubt880_ioctl(struct file *iofile, unsigned int command, unsigned long data);
+static int ubt_chrdev_connect(struct hid_device *, struct ubt880_data *);
+static void ubt_chrdev_disconnect(struct hid_device *hid);
+#ifdef UBT780_DEBUG
+static void send_test_packet(struct hid_device *hdev);
+static void fake_calibration(struct hid_device *hdev, unsigned short *coords);
+#endif
+static struct input_dev *ubt880_get_input(struct hid_device *hdev);
+static int ubt880_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size);
+/*We are using function from usbhid module*/
+extern void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir);
+static void set_calib_to_device(struct ubt880_data *drvdata);
+
+/*switch_mode : Our device has two modes: mouse mode in which it is acting like mouse, producing
+ * absolute coordinates [0 - 4095]
+ * The second mode is digitizer mode, which produce two raw clock measurments from ultrasound
+ * recievers in the top-left corner of the board. This mode more flexible and also reports batterey
+ * status of digitizer pen and penID.
+ */
+static int ubt880_switch_mode(struct hid_device *hid, unsigned char mode)
+{
+ int rc = 0;
+
+ struct hid_report *report = NULL;
+ struct hid_report *report_cur = NULL;
+ __s32 *val = NULL;
+ /** Packet forming */
+
+ UBT_DUMMY_DEBUG
+
+ list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
+ {
+ if (hid->report_enum[HID_FEATURE_REPORT].numbered) {
+ report_cur = report;
+ }
+ }
+
+ if (report_cur == NULL) {
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880 : switch mode : report_cur = 0");
+ return rc;
+ }
+
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880: switch mode: reportid = %d", report_cur->id);
+ val = report_cur->field[0]->value;
+
+ switch (mode) {
+ case MODE_MOUSE: {
+ mode = 0x0;
+ break;
+ }
+ case MODE_SINGLETOUCH: {
+ mode = 0x01;
+ break;
+ }
+ case MODE_DGTZR: {
+ mode = 0x02;
+ break;
+ }
+ default: {
+ rc = -EIO;
+ return rc;
+ }
+ }
+ val[0] = mode;
+ UBT_DUMMY_DEBUG
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880 :switch_mode: usbhid_submit_report drv=%p p=%p mode=%d", hid, report_cur, mode);
+ /*Send report to device*/
+
+ usbhid_submit_report(hid, report_cur, USB_DIR_OUT);
+ UBT_DUMMY_DEBUG
+ return rc;
+}
+
+static int ubt780_switch_mode(struct hid_device *hid, unsigned char mode)
+{
+ int rc = 0;
+
+ struct hid_report *report = NULL;
+ struct hid_report *report_cur = NULL;
+ __s32 *val = NULL;
+ /** Packet forming */
+ UBT_DUMMY_DEBUG
+
+ list_for_each_entry(report,
+ &hid->report_enum[HID_OUTPUT_REPORT].report_list,
+ list)
+ {
+ if (hid->report_enum[HID_OUTPUT_REPORT].numbered)
+ report_cur = report;
+ }
+
+ switch (mode) {
+ case MODE_MOUSE: {
+ mode = 0x00;
+ break;
+ }
+ case MODE_DGTZR: {
+ mode = 0x01;
+ break;
+ }
+ default: {
+ rc = -EIO;
+ return rc;
+ }
+ }
+ val = report_cur->field[0]->value;
+ val[0] = 0x7e;
+ val[1] = 0x04;
+ val[2] = 0x4d;
+ val[3] = mode;
+ val[4] = 0x00;
+ val[5] = 0x0a;
+ val[6] = 0x00;
+ val[7] = 0x00;
+
+ UBT_DUMMY_DEBUG
+
+ if (report_cur == NULL) {
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt780 : switch mode : report_cur = 0");
+ return rc;
+ }
+
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt780 :switch_mode:\
+ usbhid_submit_report\
+ drv=%p p=%p mode=%d",
+ hid, report_cur, mode);
+ /*Send report to device*/
+
+ usbhid_submit_report(hid, report_cur, USB_DIR_OUT);
+ UBT_DUMMY_DEBUG
+ return rc;
+}
+
+/* PenToInt : converts ultrasound clock measurements to screen coordinates.*/
+/*Apply calibration to ultrasound clock measurements*/
+int xold = 0, yold = 0;
+bool ubt_pen_to_int(int *pLeft, int *pRight)
+{
+ int left2, right2 , n, w_n, sqr, xx, yy;
+ bool bRet = false;
+ static int w = 1164;
+
+ left2 = (*pLeft)*(*pLeft);
+ right2 = (*pRight)*(*pRight);
+ UBT_DUMMY_DEBUG
+ if (left2 == 0 && right2 == 0) {
+ *pLeft = (int)xold;
+ *pRight = (int)yold;
+ if (ubt_debug)
+ printk(KERN_DEBUG
+ "ubt780_XY old values: %d,%d", xold, yold);
+ return true;
+ }
+
+ n = (right2 - left2) / (2*w);
+ w_n = w-n;
+
+ sqr = (2*left2 - (w_n * w_n));
+
+ if (sqr < 0)
+ return bRet;
+
+ xx = (w_n + int_sqrt(sqr)) / 2;
+ yy = xx + n;
+
+ if (xx < 0 || yy < 0)
+ return bRet;
+
+ *pLeft = (int)xx;
+ *pRight = (int)yy;
+ bRet = true;
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt780_XY: %d,%d", xx, yy);
+
+ return bRet;
+}
+
+static bool ubt_calibrate(int inX, int inY, int *outX, int *outY, struct ubt_calib *calib)
+{
+ int Y_LTY, X_LTX, Xoff, Xmag, Yoff, Ymag;
+ unsigned int x, y;
+ xold = inX;
+ yold = inY;
+
+ Y_LTY = inY - calib->LTY;
+ X_LTX = inX - calib->LTX;
+
+ Xoff = Y_LTY * calib->Xoff_A + calib->LTX * 10000;
+ Xmag = (Y_LTY * calib->Xmag_A) / 10000;
+ Xmag += calib->Xmag_B;
+ Yoff = X_LTX * calib->Yoff_A + calib->LTY * 10000;
+ Ymag = X_LTX * calib->Ymag_A / 10000;
+ Ymag += calib->Ymag_B;
+
+ if (!Xmag)
+ Xmag = 1;
+ x = (inX * 10000 - Xoff) / Xmag;
+ x += calib->LTx;
+
+ if (!Ymag)
+ Ymag = 1;
+ y = (inY * 10000 - Yoff) / Ymag;
+ y += calib->LTy;
+
+ if (x < 0)
+ x = 0;
+ if (x > UBT880_MAX_AXIS_X)
+ x = UBT880_MAX_AXIS_X;
+ if (y < 0)
+ y = 0;
+ if (y > UBT880_MAX_AXIS_Y)
+ y = UBT880_MAX_AXIS_Y;
+
+ if (ubt_debug)
+ printk(KERN_DEBUG "%d - %d", x, y);
+ *outX = x;
+ *outY = y;
+ return true;
+}
+
+/*
+input_mapping : Set appropriate bits for input device according to current field, usage and usage page.
+For info about valid usage pages and usages see include/linux/hid.h
+For more info on input see Documentation/input/input.txt and Documentation/input/input-programming.txt
+and of course include/linux/input.h
+What to return?
+return < 0 - field was ignored
+return > 0 - field was mapped
+return == 0 - let hid driver map this field for you
+*/
+static int ubt880_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ /*Just ignore all fields*/
+ return -1;
+}
+/*
+ * input_mapped : called from hidinput_configure_usage after input is mapped.
+ * Never called if you have returned negative value from input_mapping.
+ * Return values:
+ * return < 0 - ignore field
+ * return >= 0 - do further processing
+ */
+/*static int ubt880_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ return -1;
+}*/
+
+
+/* event : Called on every single report field.
+ * Return :
+ * return == 0 - do generic input hidinput and hiddev processing. See hid_process_event in hid-core.c.
+ * return > 0 - finish processing.
+ */
+static int ubt880_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ /*Just prevent further processing by hid*/
+ return 1;
+}
+static void ubt880_set_input(struct input_dev *input)
+{
+ /* Basics */
+ /*We have a key*/
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(BTN_RIGHT, input->keybit);
+ /*two absolute axis*/
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(ABS_X, input->absbit);
+ __set_bit(ABS_Y, input->absbit);
+ /*two absolute MT axis and tracking IDs*/
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, UBT880_MAX_AXIS_X, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, UBT880_MAX_AXIS_Y, 0, 0);
+ input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 3, 0, 0);
+ input_set_abs_params(input, ABS_X, 0, UBT880_MAX_AXIS_X, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, UBT880_MAX_AXIS_Y, 0, 0);
+}
+
+static void ubt780_set_input(struct input_dev *input)
+{
+ input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
+ input->absbit[0] |= BIT(ABS_X) | BIT(ABS_Y);
+ set_bit(BTN_LEFT, input->keybit);
+ set_bit(BTN_RIGHT, input->keybit);
+
+ input->absmax[ABS_X] = UBT780_MAX_AXIS_X;
+ input->absmax[ABS_Y] = UBT780_MAX_AXIS_Y;
+ input->absmin[ABS_X] = 0;
+ input->absmin[ABS_Y] = 0;
+}
+
+int ubt_set_device(struct ubt880_data *data, __u32 devid)
+{
+ int ret;
+ data->calib.calibrated = 0;
+ data->ubt_packet.report = 0;
+ set_calib_to_device(data);
+ switch (devid) {
+ case USB_DEVICE_ID_PANABOARD_UBT780: {
+ data->switch_mode = ubt780_switch_mode;
+ data->set_input = ubt780_set_input;
+ break;
+ }
+ case USB_DEVICE_ID_PANABOARD_UBT880: {
+ data->switch_mode = ubt880_switch_mode;
+ data->set_input = ubt880_set_input;
+ break;
+ }
+ default: {
+ ret = -1;
+ }
+ }
+ return 0;
+}
+static int ubt880_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+ struct ubt880_data *drvdata;
+ struct hid_input *hidinput = NULL;
+ struct input_dev *input;
+
+ UBT_DUMMY_DEBUG
+
+ drvdata = kmalloc(sizeof(struct ubt880_data), GFP_KERNEL);
+ if (!drvdata) {
+ dev_err(&hdev->dev, "cannot allocate UB-T880 data\n");
+ return -ENOMEM;
+ }
+
+ hid_set_drvdata(hdev, drvdata);
+
+ ret = hid_parse(hdev);
+ if (!ret) {
+ if (ubt_debug)
+ printk(KERN_DEBUG "Trying hid_hw_start");
+ /*We need HIDINPUT dev only. We have our own char device instead of hidraw*/
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDINPUT);
+ if (ubt_debug)
+ printk(KERN_DEBUG "hid_hw_start() returned : %d", ret);
+ }
+
+ if (ret) {
+ kfree(drvdata);
+ goto skip_input;
+ }
+ ubt_chrdev_connect(hdev, drvdata);
+ ubt_set_device(drvdata, hdev->product);
+
+ drvdata->switch_mode(hdev, MODE_MOUSE);
+ drvdata->current_mode = MODE_MOUSE;
+
+ UBT_DUMMY_DEBUG
+ hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
+ if (hidinput == NULL) {
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880: probe : Failed to get valid hidinput");
+ goto skip_input;
+ }
+ UBT_DUMMY_DEBUG
+ input = hidinput->input;
+
+ UBT_DUMMY_DEBUG
+ drvdata->set_input(input);
+skip_input:
+ return ret;
+}
+/* Our own char device for ioctls */
+/*---------------------------------------------------------------------------------------------*/
+
+
+static const struct file_operations ubt880_fops = {
+ .owner = THIS_MODULE,
+ .open = ubt880_open_io,
+ .release = ubt880_release_io,
+ .unlocked_ioctl = ubt880_ioctl,
+};
+
+static int ubt_chrdev_init(void)
+{
+ int result;
+ dev_t dev_id;
+
+ result = alloc_chrdev_region(&dev_id, UBT780_FIRST_MINOR,
+ UBT780_MAX_DEVICES, "ubt_chr");
+
+ ubt_major = MAJOR(dev_id);
+
+ if (result < 0) {
+ printk(KERN_WARNING "ubt: can't get major number\n");
+ result = 0;
+ goto out;
+ }
+
+ ubt_class = class_create(THIS_MODULE, "ubt_chr");
+ if (IS_ERR(ubt_class)) {
+ result = PTR_ERR(ubt_class);
+ unregister_chrdev(ubt_major, "ubt_chr");
+ goto out;
+ }
+
+ cdev_init(&ubt_cdev, &ubt880_fops);
+ cdev_add(&ubt_cdev, dev_id, UBT780_MAX_DEVICES);
+out:
+ return result;
+}
+
+static void ubt_chrdev_cleanup(void)
+{
+ dev_t dev_id = MKDEV(ubt_major, 0);
+ cdev_del(&ubt_cdev);
+ class_destroy(ubt_class);
+ unregister_chrdev_region(dev_id, UBT780_MAX_DEVICES);
+}
+static int ubt_chrdev_connect(struct hid_device *hid, struct ubt880_data *drvdata)
+{
+ int minor, result;
+ struct ubt_chrdev *dev;
+
+ dev = kzalloc(sizeof(struct ubt_chrdev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ result = -EINVAL;
+
+ mutex_lock(&minors_lock);
+
+ for (minor = 0; minor < UBT780_MAX_DEVICES; minor++) {
+ if (ubt_table[minor])
+ continue;
+ ubt_table[minor] = dev;
+ result = 0;
+ break;
+ }
+ UBT_DUMMY_DEBUG
+ printk(KERN_DEBUG "ubt_chrdev_connect : result = %X", result);
+
+ if (result) {
+ mutex_unlock(&minors_lock);
+ kfree(dev);
+ goto out;
+ }
+ printk(KERN_DEBUG "ubt : chrdev_connect : ubt_majo=%d, minor=%d", ubt_major, minor);
+ dev->dev = device_create(ubt_class, &hid->dev, MKDEV(ubt_major, minor),
+ NULL, "%s%d", "ubt_", minor);
+
+ if (IS_ERR(dev->dev)) {
+ ubt_table[minor] = NULL;
+ mutex_unlock(&minors_lock);
+ result = PTR_ERR(dev->dev);
+ kfree(dev);
+ drvdata->chrdev = NULL;
+ goto out;
+ }
+
+ UBT_DUMMY_DEBUG
+ printk(KERN_DEBUG "ubt_chrdev_connect : result = %X", result);
+
+ mutex_unlock(&minors_lock);
+
+ dev->hid = hid;
+ drvdata->chrdev = dev;
+ dev->minor = minor;
+
+ dev->exist = 1;
+
+out:
+ printk(KERN_DEBUG "ubt_chrdev_connect : result = %X", result);
+ return result;
+
+}
+
+static void ubt_chrdev_disconnect(struct hid_device *hid)
+{
+
+ struct ubt880_data *ubt880_drv = hid_get_drvdata(hid);
+ struct ubt_chrdev *ubt880 = ubt880_drv->chrdev;
+
+ if (!ubt880)
+ return;
+
+ ubt880->exist = 0;
+
+ mutex_lock(&minors_lock);
+ ubt_table[ubt880->minor] = NULL;
+ mutex_unlock(&minors_lock);
+
+ device_destroy(ubt_class, MKDEV(ubt_major, ubt880->minor));
+
+ if (ubt880->open) {
+ hid->ll_driver->close(hid);
+ } else {
+ kfree(ubt880);
+ }
+}
+
+
+static int ubt880_open_io(struct inode *node, struct file *file)
+{
+ unsigned int minor = iminor(node);
+ struct ubt_chrdev *device;
+ int error = 0;
+
+ mutex_lock(&minors_lock);
+ UBT_DUMMY_DEBUG
+ device = ubt_table[minor];
+
+ if (!device) {
+ printk(KERN_EMERG "ubt880 device with minor %d doesn't exist\n",
+ minor);
+ error = -ENODEV;
+ goto exit_unlock;
+ }
+
+ if (!device->open) {
+ device->open++;
+ /* power on device */
+ if (device->hid->ll_driver->power) {
+ error = device->hid->ll_driver->power(device->hid,
+ PM_HINT_FULLON);
+ if (error < 0) {
+ --device->open;
+ goto exit_unlock;
+ }
+ }
+ error = device->hid->ll_driver->open(device->hid);
+ if (error < 0) {
+ if (device->hid->ll_driver->power)
+ device->hid->ll_driver->power(device->hid,
+ PM_HINT_NORMAL);
+ --device->open;
+ goto exit_unlock;
+ }
+ } else {
+ device->open++;
+ /*error = -EBUSY;*/
+ }
+
+exit_unlock:
+ mutex_unlock(&minors_lock);
+ return error;
+}
+
+static int ubt880_release_io(struct inode *node, struct file *file)
+{
+ unsigned int minor = iminor(node);
+ struct ubt_chrdev *dev;
+ int ret = 0;
+ UBT_DUMMY_DEBUG
+ mutex_lock(&minors_lock);
+ if (!ubt_table[minor]) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+
+ dev = ubt_table[minor];
+ if (dev->open) {
+ dev->open--;
+ if (dev->open)
+ goto unlock;
+ if (dev->hid->ll_driver->power)
+ dev->hid->ll_driver->power(dev->hid, PM_HINT_NORMAL);
+ dev->hid->ll_driver->close(dev->hid);
+ }
+
+ ret = 0;
+unlock:
+ mutex_unlock(&minors_lock);
+
+ return ret;
+}
+static void set_calib_to_driver(struct ubt_calib *calib)
+{
+
+ if (!calib)
+ return;
+ LTx = calib->LTx;
+ LTy = calib->LTy;
+ LBx = calib->LBx;
+ LBy = calib->LBy;
+ RTx = calib->RTx;
+ RTy = calib->RTy;
+ RBx = calib->RBx;
+ RBy = calib->RBy;
+
+ LTX = calib->LTX;
+ LTY = calib->LTY;
+ LBX = calib->LBX;
+ LBY = calib->LBY;
+ RTX = calib->RTX;
+ RTY = calib->RTY;
+ RBX = calib->RBX;
+ RBY = calib->RBY;
+
+ Xoff_A = calib->Xoff_A;
+ Xmag_A = calib->Xmag_A;
+ Xmag_B = calib->Xmag_B;
+ Yoff_A = calib->Yoff_A;
+ Ymag_A = calib->Ymag_A;
+ Ymag_B = calib->Ymag_B;
+
+ isCalibrated = 1;
+}
+
+
+static void set_calib_to_device(struct ubt880_data *drvdata)
+{
+
+ if (!drvdata)
+ return;
+ if (!isCalibrated)
+ return;
+ drvdata->calib.LTx = LTx;
+ drvdata->calib.LTy = LTy;
+ drvdata->calib.LBx = LBx;
+ drvdata->calib.LBy = LBy;
+ drvdata->calib.RTx = RTx;
+ drvdata->calib.RTy = RTy;
+ drvdata->calib.RBx = RBx;
+ drvdata->calib.RBy = RBy;
+
+ drvdata->calib.LTX = LTX;
+ drvdata->calib.LTY = LTY;
+ drvdata->calib.LBX = LBX;
+ drvdata->calib.LBY = LBY;
+ drvdata->calib.RTX = RTX;
+ drvdata->calib.RTY = RTY;
+ drvdata->calib.RBX = RBX;
+ drvdata->calib.RBY = RBY;
+
+ drvdata->calib.Xoff_A = Xoff_A;
+ drvdata->calib.Xmag_A = Xmag_A;
+ drvdata->calib.Xmag_B = Xmag_B;
+ drvdata->calib.Yoff_A = Yoff_A;
+ drvdata->calib.Ymag_A = Ymag_A;
+ drvdata->calib.Ymag_B = Ymag_B;
+
+ drvdata->calib.calibrated = 1;
+}
+
+static long ubt880_ioctl(struct file *iofile, unsigned int command, unsigned long data)
+{
+
+ int retval = 0;
+ struct inode *inode = iofile->f_path.dentry->d_inode;
+ unsigned int minor = iminor(inode);
+ struct ubt_chrdev *dev;
+ struct ubt880_data *drvdata;
+ mutex_lock(&minors_lock);
+ dev = ubt_table[minor];
+ if (!dev) {
+ retval = -ENODEV;
+ goto out;
+ }
+ drvdata = hid_get_drvdata(dev->hid);
+ switch (command) {
+ case GET_BATTERY_STATUS:
+ {
+ int *in = (int *) data;
+ *in = 0;
+ retval = 0;
+ break;
+ }
+ case GET_LAST_PACKET_OLD:
+ case GET_LAST_PACKET:
+ {
+ struct ubt880_dgtzr *packsrc = &drvdata->ubt_packet;
+ struct ubt880_dgtzr *packdst = (struct ubt880_dgtzr *) data;
+
+ if (packsrc->report != 0x00) {
+ memcpy((void *)packdst, (void *)packsrc, sizeof(struct ubt880_dgtzr));
+ packsrc->report = 0;
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880_ioctl_io : GET_LAST_PACKET2");
+ }
+
+ retval = 0;
+ break;
+ }
+ case GET_MODE:
+ {
+ int *in = (int *) data;
+ printk(KERN_DEBUG "ubt880_ioctl_io : GET_MODE");
+ *in = drvdata->current_mode;
+ retval = 0;
+ break;
+ }
+ case SET_MODE:
+ {
+ int *dt = (int *)data;
+ printk(KERN_DEBUG "ubt_ioctl_io : SET_MODE");
+ drvdata->ubt_packet.report = 0;
+
+ if (drvdata->switch_mode(dev->hid, *dt) != 0) {
+ retval = -EIO;
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt_ioctl_io : SET_MODE error");
+ } else {
+ drvdata->current_mode = *dt;
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt_ioctl_io : SET_MODE ok, mode = %d", drvdata->current_mode);
+ retval = 0;
+ }
+ break;
+ }
+ case SET_CALIB: {
+ struct ubt_calib *calib = (struct ubt_calib *) data;
+
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880_ioctl_io : SET_CALIB");
+
+ set_calib_to_driver(calib);
+ set_calib_to_device(drvdata);
+
+ retval = 0;
+ break;
+ }
+ case GET_DEVICE_ID: {
+ int *in = (int *) data;
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt_ioctl_io : get device id");
+ *in = dev->hid->product;
+ retval = 0;
+ break;
+ }
+#ifdef UBT780_DEBUG
+ case TEST_MT_PACKET: {
+
+ send_test_packet(dev->hid);
+ retval = 0;
+ break;
+ }
+ case TEST_CALIBRATION: {
+
+ unsigned short *coord = (unsigned short *)data;
+ fake_calibration(dev->hid, coord);
+ retval = 0;
+ break;
+ }
+#endif
+ default:
+ {
+ retval = -EIO;
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880_ioctl_io : unknown command %d", command);
+ break;
+ }
+ }
+out:
+ mutex_unlock(&minors_lock);
+ return retval;
+}
+/*-----------------------------------------------------------------------------------------------*/
+static void ubt_report_input(struct input_dev *input, unsigned int x, unsigned int y, int btn_left, int btn_right)
+{
+ /* To prevent situation when new coords arrive before unpess */
+ /* and a line is drawn istead of 2 dots */
+ input_report_key(input, BTN_LEFT, btn_left);
+ input_report_key(input, BTN_RIGHT, btn_right);
+ /*if (btn)*/
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+
+ input_sync(input);
+}
+/* Debug output of raw MT packet from device */
+static void ubt880_print_multitouch(struct ubt_mt_contact *pack, int size)
+{
+ int i;
+ printk(KERN_DEBUG "-------------------------------------");
+ for (i = 0; i < size; i++) {
+ printk(KERN_DEBUG "-----------%d------------", i);
+ printk(KERN_DEBUG "ubt880: mt packet: flags : inrange %X | tipswitch %X", pack[i].flags & 0x02, pack[i].flags & 0x01);
+ printk(KERN_DEBUG "ubt880: mt packet: id | %d", pack[i].id);
+ printk(KERN_DEBUG "ubt880: mt packet: X | %d", pack[i].x);
+ printk(KERN_DEBUG "ubt880: mt packet: Y | %d", pack[i].y);
+ printk(KERN_DEBUG "-------------------------");
+ }
+ printk(KERN_DEBUG "ubt880: mt packet: count: %d", size);
+}
+/* Report MT to the input subsystem*/
+static void ubt880_report_contact(struct input_dev *input, struct ubt_mt_contact *contact)
+{
+ if (!contact || !input)
+ return;
+ input_report_abs(input, ABS_MT_TRACKING_ID, contact->id);
+ input_report_abs(input, ABS_MT_POSITION_X, contact->x);
+ input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
+ input_mt_sync(input);
+}
+static void ubt880_report_mt(struct input_dev *input, struct ubt_mt_contact *pack, int size, struct ubt_calib *calib)
+{
+ int i = 0;
+ /* Report MT contacts */
+ if (size < 1)
+ return;
+ if (!input || !pack || !calib)
+ return;
+
+ for (i = 0; i < size; i++) {
+ /* If Tip Switch bit is set */
+ if (pack[i].flags & 0x01) {
+ if (calib->calibrated) {
+ ubt_calibrate(pack[i].x,
+ pack[i].y,
+ (int *)&pack[i].x,
+ (int *)&pack[i].y,
+ calib);
+ }
+ ubt880_report_contact(input, &pack[i]);
+ }
+ }
+ input_sync(input);
+ /* Emulate single-touch device. Only first contact is used. */
+ /* Note that is already calibrated */
+ ubt_report_input(input, pack[0].x, pack[0].y, pack[0].flags & 0x01, 0);
+}
+
+#ifdef UBT780_DEBUG
+static void fake_calibration(struct hid_device *hdev, unsigned short *coord)
+{
+ char pack[6];
+ unsigned short *pack_coord = (unsigned short *)&pack[2];
+ pack[0] = 0x01;
+ pack[1] = coord[0];
+ pack_coord[0] = coord[1];
+ pack_coord[1] = coord[2];
+ ubt880_raw_event(hdev, 0, (u8 *)&pack, 6);
+}
+static void send_test_packet(struct hid_device *hdev)
+{
+ char testdata[20];
+ struct ubt_mt_contact *contacts;
+ struct ubt_calib calib;
+
+ calib.calibrated = 0;
+
+ testdata[0] = 0x03;
+ contacts = (struct ubt_mt_contact *)&testdata[1];
+ contacts[0].flags = 0x01;
+ contacts[0].id = 0x01;
+ contacts[0].x = 205;
+ contacts[0].y = 1023;
+
+ contacts[1].flags = 0x01;
+ contacts[1].id = 0x02;
+ contacts[1].x = 1283;
+ contacts[1].y = 1048;
+
+ contacts[2].flags = 0x01;
+ contacts[2].id = 0x03;
+ contacts[2].x = 508;
+ contacts[2].y = 2473;
+
+ testdata[19] = 3;
+
+ ubt880_raw_event(hdev, 0, (u8 *)&testdata, 20);
+}
+#endif
+
+static struct input_dev *ubt880_get_input(struct hid_device *hdev)
+{
+ struct hid_input *hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
+ struct input_dev *input;
+
+ if (!hidinput)
+ return 0;
+ input = hidinput->input;
+ return input;
+}
+static int ubt880_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
+{
+ struct ubt880_data *driver_data = hid_get_drvdata(hdev);
+ struct ubt880_dgtzr *pack = &driver_data->ubt_packet;
+
+ struct input_dev *input = ubt880_get_input(hdev);
+ if (!input)
+ return -1;
+
+ if (!driver_data)
+ return -1;
+ /*Save the packet for userspace processing*/
+ /*Mouse mode packet*/
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880:raw_event: report = %X", data[0]);
+ switch (data[0]) {
+ case 0x01: {
+ int X, Y;
+ memcpy((void *)pack, (void *)data, size);
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880_mode: Mouse");
+
+ if (driver_data->current_mode == MODE_UKN)
+ driver_data->current_mode = MODE_MOUSE;
+
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880: mouse packet: X=%d,Y=%d,Button=%d",
+ pack->data[0], pack->data[1], pack->command);
+ X = pack->data[0];
+ Y = pack->data[1];
+ if (driver_data->calib.calibrated) {
+ ubt_calibrate(X, Y, &X, &Y, &driver_data->calib);
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880: mouse packet calibrated: X=%d,Y=%d",
+ X, Y);
+ }
+
+ ubt_report_input(input, X, Y, pack->command & 0x01, pack->command & 0x02);
+ /*Switch to digitizer*/
+ if (driver_data->current_mode != MODE_MOUSE)
+ ubt880_switch_mode(hdev, MODE_MOUSE);
+ break;
+ }
+ case 0x02: {
+ struct ubt780_dgtzr *pack = (struct ubt780_dgtzr *)data;
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt780_mode: DIG");
+ if (driver_data->current_mode == MODE_UKN)
+ driver_data->current_mode = MODE_DGTZR;
+
+ if (driver_data->current_mode == MODE_MOUSE) {
+ pack->report = 0;
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt780_current_mode: MOUSE");
+ /*Switch to mouse*/
+ driver_data->switch_mode(hdev, MODE_MOUSE);
+ } else if (driver_data->current_mode == MODE_DGTZR) {
+ unsigned short *pCoord = (unsigned short *)(&pack->data[3]);
+ struct ubt_calib *calib = &driver_data->calib;
+ int X = (int) pCoord[0];
+ int Y = (int) pCoord[1];
+ int leftBtn = pack->data[2] >> 5 & 0x01;
+ int rightBtn = pack->data[2] >> 4 & 0x01;
+
+ ubt_pen_to_int(&X, &Y);
+
+ driver_data->ubt_packet.report = 0x02;
+ driver_data->ubt_packet.command = pack->data[2];
+ driver_data->ubt_packet.data[0] = X;
+ driver_data->ubt_packet.data[1] = Y;
+
+ if (driver_data->calib.calibrated) {
+ if (ubt_calibrate(X, Y, &X, &Y, calib)) {
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt780_mode:\
+ Calibrated;\
+ x=%d y=%d,left_btn=%d\n",
+ X, Y, leftBtn);
+ ubt_report_input(input, X, Y, leftBtn, rightBtn);
+ }
+ } else {
+ ubt_report_input(input, X, Y, leftBtn, rightBtn);
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt780_mode:\
+ Not calibrated;\
+ x=%d y=%d,left_btn=%d\n",
+ X, Y, leftBtn);
+ }
+ }
+ break;
+ }
+ case 0x03: {
+ struct ubt_mt_packet *packet = (struct ubt_mt_packet *)data;
+ if (ubt_debug) {
+ printk(KERN_DEBUG "ubt880_mode: Multitouch");
+ printk(KERN_DEBUG "ubt880_driver_mode : %d", driver_data->current_mode);
+ }
+ if (driver_data->current_mode == MODE_UKN)
+ driver_data->current_mode = MODE_DGTZR;
+
+ if (ubt_debug)
+ ubt880_print_multitouch((struct ubt_mt_contact *)&packet->data[1], packet->data[19]);
+
+ if (driver_data->current_mode == MODE_MOUSE) {
+ driver_data->switch_mode(hdev, MODE_MOUSE);
+ } else if (driver_data->current_mode == MODE_DGTZR) {
+ ubt880_report_mt(input, (struct ubt_mt_contact *)&packet->data[1], packet->data[19], &driver_data->calib);
+ }
+ break;
+ }
+ default:
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880: raw_report: Unknown report %X", pack->report);
+ }
+
+
+ UBT_DUMMY_DEBUG
+ return 0;
+}
+static void ubt880_remove(struct hid_device *hdev)
+{
+ UBT_DUMMY_DEBUG
+ ubt_chrdev_disconnect(hdev);
+ hid_hw_stop(hdev);
+ kfree(hid_get_drvdata(hdev));
+ hid_set_drvdata(hdev, NULL);
+}
+
+static struct hid_device_id ubt880_devices[] = {
+ { HID_USB_DEVICE(0x04da, 0x104d) },
+ { HID_USB_DEVICE(0x04da, 0x1044) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, ubt880_devices);
+
+static struct hid_driver ubt880_driver = {
+ .name = "ubt880",
+ .id_table = ubt880_devices,
+ .probe = ubt880_probe,
+ .remove = ubt880_remove,
+ .input_mapping = ubt880_input_mapping,
+ .raw_event = ubt880_raw_event,
+ .event = ubt880_event,
+};
+
+static int __init ubt880_init(void)
+{
+ int retval = ubt_chrdev_init();
+ retval = hid_register_driver(&ubt880_driver);
+ UBT_DUMMY_DEBUG
+ if (ubt_debug)
+ printk(KERN_DEBUG "ubt880 : init : chrdev_init : retval = %X", retval);
+ return retval;
+}
+
+static void __exit ubt880_exit(void)
+{
+ UBT_DUMMY_DEBUG
+ hid_unregister_driver(&ubt880_driver);
+ ubt_chrdev_cleanup();
+}
+
+module_init(ubt880_init);
+module_exit(ubt880_exit);
+
+MODULE_AUTHOR("Anton Chikin <kverlin@gmail.com>");
+MODULE_DESCRIPTION("Panasonic UB-T780 driver");
+MODULE_LICENSE("GPL");
new file mode 100644
@@ -0,0 +1,157 @@
+/*
+ * USB HID driver for Panasonic elite Panaboard UTB780
+ * Copyright (c) 2008 Igor Shakirov, Victor Grenke <comp.vision@gmail.com>
+
+ * Information.
+ * It's driver for supporting Panaboard elite USB device.
+ * The driver is redesigned using usb hid core driver:
+ * Copyright (c) 1999 Andreas Gal
+ * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ * Copyright (c) 2010 Panasonic
+ */
+
+/*
+ * 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.
+ */
+#include <linux/hid.h>
+#ifndef UBT780CTRL
+#define UBT780CTRL
+
+/*IOCTRL codes*/
+/** Battary status IOCTL defines */
+#define GET_BATTERY_STATUS 0x01 /*get battery status, return BATTERY_STATUS_UKN, BATTERY_STATUS_FINE, BATTERY_STATUS_WEAK*/
+#define GET_LAST_PACKET_OLD 0x02 /*get last packet (ubt780_dgtzr). It is necessary for calibration*/
+
+/** Modes of work IOCTL */
+#define GET_MODE 0x10 /*get current mode, return MODE_MOUSE, MODE_DGTZR*/
+#define SET_MODE 0x11 /*switch to mouse mode, MODE_MOUSE, MODE_DGTZR*/
+#define SET_CALIB 0x20 /*set calibration mode*/
+#define GET_LAST_PACKET 0x21 /*get last packet (ubt780_dgtzr). It is necessary for calibration*/
+#define GET_DEVICE_ID 0x22 /*get device id to recognize the device version*/
+#define TEST_MT_PACKET 0x23
+#define TEST_CALIBRATION 0x24
+/*return values for ioctrl*/
+/** Battary status defines */
+#define BATTERY_STATUS_FINE 0x00 /*battery is fine*/
+#define BATTERY_STATUS_WEAK 0x01 /*battery is weak*/
+#define BATTERY_STATUS_UKN 0x02 /*unknown status. Status will be known after the first pen touch*/
+
+/** Mode status defines */
+#define MODE_MOUSE 0x10 /*current mode is mouse*/
+#define MODE_DGTZR 0x11 /*current mode is digitizer*/
+#define MODE_UKN 0x12 /*current mode is unknown. It will be known after the first pen touch*/
+#define MODE_SINGLETOUCH 0x13
+
+/*Char device declarations*/
+#define UBT780_MAX_DEVICES 0x05
+#define UBT780_FIRST_MINOR 0
+
+#define UBT780_MAX_AXIS_X 4095
+#define UBT780_MAX_AXIS_Y 4095
+#define UBT880_MAX_AXIS_X 32767
+#define UBT880_MAX_AXIS_Y 32767
+#define NUM_CONTACTS 0x03
+/** Digitizer mode stucture: contains packet data */
+struct ubt880_dgtzr {
+ /** Report contains the type of packet: 0 - mouse, 1 - digitize */
+ unsigned char report;
+ /** Command is coming from device */
+ unsigned char command;
+ /** Packet size */
+ unsigned short data[2];
+};
+
+struct ubt780_dgtzr {
+ /** Report contains the type of packet: 0 - mouse, 1 - digitize */
+ unsigned char report;
+ /** Command is coming from device */
+ unsigned char command;
+ /** Packet size */
+ unsigned char number;
+ /** Data part of packet */
+ unsigned char data[17];
+};
+/** Calibration stucture: contains calibration data */
+struct ubt_calib {
+ /** Screen coordinates: Left Top X */
+ int LTx;
+ /** Screen coordinates: Left Top Y */
+ int LTy;
+ /** Screen coordinates: Left Bottom X */
+ int LBx;
+ /** Screen coordinates: Left Bottom Y */
+ int LBy;
+ /** Screen coordinates: Right Top X */
+ int RTx;
+ /** Screen coordinates: Right Top Y */
+ int RTy;
+ /** Screen coordinates: Right Bottom X */
+ int RBx;
+ /** Screen coordinates: Right Bottom Y */
+ int RBy;
+
+ /** Board coordinates: Left Top X */
+ int LTX;
+ /** Board coordinates: Left Top Y */
+ int LTY;
+ /** Board coordinates: Left Bottom X */
+ int LBX;
+ /** Board coordinates: Left Bottom Y */
+ int LBY;
+ /** Board coordinates: Right Top X */
+ int RTX;
+ /** Board coordinates: Right Top Y */
+ int RTY;
+ /** Board coordinates: Right Bottom X */
+ int RBX;
+ /** Board coordinates: Right Bottom Y */
+ int RBY;
+
+ /** Calculated values */
+ int Xoff_A;
+ int Xmag_A;
+ int Xmag_B;
+ int Yoff_A;
+ int Ymag_A;
+ int Ymag_B;
+ int calibrated;
+};
+/*Character device data structure*/
+struct ubt_chrdev {
+ unsigned int minor;
+ int exist;
+ int open;
+ struct hid_device *hid;
+ struct device *dev;
+};
+/*Driver data structure*/
+struct ubt780_data {
+ struct ubt_calib calib;
+ struct ubt780_dgtzr ubt_packet;
+ struct ubt_chrdev *chrdev;
+ unsigned char current_mode;
+};
+
+struct ubt880_data {
+ struct ubt_calib calib;
+ struct ubt880_dgtzr ubt_packet;
+ struct ubt_chrdev *chrdev;
+ unsigned char current_mode;
+ int (*switch_mode) (struct hid_device *hid, unsigned char mode);
+ void (*set_input) (struct input_dev *input);
+};
+/*ubt880 multitouch structures*/
+struct ubt_mt_contact {
+ unsigned char flags;
+ unsigned char id;
+ unsigned short x;
+ unsigned short y;
+};
+struct ubt_mt_packet {
+ unsigned char data[20];
+};
+#endif /* UBT780CTRL */