Commit 16bf288c authored by Chris Bagwell's avatar Chris Bagwell Committed by Dmitry Torokhov

Input: wacom - create inputs when wireless connect

When a tablet connect or disconnect is detected, schedule
work queue to register or unregister related input devices.

When a wireless tablet connects, it reports same USB PID
used if tablet is connected with USB cable. Use this to
update features values, set input capabilities, and then
register device.  From there, the Pen and Touch interfaces
will reuse the existing tablet's IRQ routines.

Its possible that 1 receiver is shared with 2 tablets with
different PID (small and medium Bamboo for example) so the
input is unregister at disconnect to better support this case.
Signed-off-by: default avatarChris Bagwell <chris@cnpbagwell.com>
Tested-by: default avatarJason Gerecke <killertofu@gmail.com>
Acked-by: default avatarPing Cheng <pingc@wacom.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent d3825d51
...@@ -112,6 +112,7 @@ struct wacom { ...@@ -112,6 +112,7 @@ struct wacom {
struct urb *irq; struct urb *irq;
struct wacom_wac wacom_wac; struct wacom_wac wacom_wac;
struct mutex lock; struct mutex lock;
struct work_struct work;
bool open; bool open;
char phys[32]; char phys[32];
struct wacom_led { struct wacom_led {
...@@ -122,6 +123,12 @@ struct wacom { ...@@ -122,6 +123,12 @@ struct wacom {
} led; } led;
}; };
static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
{
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
schedule_work(&wacom->work);
}
extern const struct usb_device_id wacom_ids[]; extern const struct usb_device_id wacom_ids[];
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
......
...@@ -167,6 +167,19 @@ static void wacom_close(struct input_dev *dev) ...@@ -167,6 +167,19 @@ static void wacom_close(struct input_dev *dev)
usb_autopm_put_interface(wacom->intf); usb_autopm_put_interface(wacom->intf);
} }
/*
* Static values for max X/Y and resolution of Pen interface is stored in
* features. This mean physical size of active area can be computed.
* This is useful to do when Pen and Touch have same active area of tablet.
* This means for Touch device, we only need to find max X/Y value and we
* have enough information to compute resolution of touch.
*/
static void wacom_set_phy_from_res(struct wacom_features *features)
{
features->x_phy = (features->x_max * 100) / features->x_resolution;
features->y_phy = (features->y_max * 100) / features->y_resolution;
}
static int wacom_parse_logical_collection(unsigned char *report, static int wacom_parse_logical_collection(unsigned char *report,
struct wacom_features *features) struct wacom_features *features)
{ {
...@@ -178,15 +191,7 @@ static int wacom_parse_logical_collection(unsigned char *report, ...@@ -178,15 +191,7 @@ static int wacom_parse_logical_collection(unsigned char *report,
features->pktlen = WACOM_PKGLEN_BBTOUCH3; features->pktlen = WACOM_PKGLEN_BBTOUCH3;
features->device_type = BTN_TOOL_FINGER; features->device_type = BTN_TOOL_FINGER;
/* wacom_set_phy_from_res(features);
* Stylus and Touch have same active area
* so compute physical size based on stylus
* data before its overwritten.
*/
features->x_phy =
(features->x_max * features->x_resolution) / 100;
features->y_phy =
(features->y_max * features->y_resolution) / 100;
features->x_max = features->y_max = features->x_max = features->y_max =
get_unaligned_le16(&report[10]); get_unaligned_le16(&report[10]);
...@@ -869,6 +874,72 @@ static int wacom_register_input(struct wacom *wacom) ...@@ -869,6 +874,72 @@ static int wacom_register_input(struct wacom *wacom)
return error; return error;
} }
static void wacom_wireless_work(struct work_struct *work)
{
struct wacom *wacom = container_of(work, struct wacom, work);
struct usb_device *usbdev = wacom->usbdev;
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
/*
* Regardless if this is a disconnect or a new tablet,
* remove any existing input devices.
*/
/* Stylus interface */
wacom = usb_get_intfdata(usbdev->config->interface[1]);
if (wacom->wacom_wac.input)
input_unregister_device(wacom->wacom_wac.input);
wacom->wacom_wac.input = 0;
/* Touch interface */
wacom = usb_get_intfdata(usbdev->config->interface[2]);
if (wacom->wacom_wac.input)
input_unregister_device(wacom->wacom_wac.input);
wacom->wacom_wac.input = 0;
if (wacom_wac->pid == 0) {
printk(KERN_INFO "wacom: wireless tablet disconnected\n");
} else {
const struct usb_device_id *id = wacom_ids;
printk(KERN_INFO
"wacom: wireless tablet connected with PID %x\n",
wacom_wac->pid);
while (id->match_flags) {
if (id->idVendor == USB_VENDOR_ID_WACOM &&
id->idProduct == wacom_wac->pid)
break;
id++;
}
if (!id->match_flags) {
printk(KERN_INFO
"wacom: ignorning unknown PID.\n");
return;
}
/* Stylus interface */
wacom = usb_get_intfdata(usbdev->config->interface[1]);
wacom_wac = &wacom->wacom_wac;
wacom_wac->features =
*((struct wacom_features *)id->driver_info);
wacom_wac->features.device_type = BTN_TOOL_PEN;
wacom_register_input(wacom);
/* Touch interface */
wacom = usb_get_intfdata(usbdev->config->interface[2]);
wacom_wac = &wacom->wacom_wac;
wacom_wac->features =
*((struct wacom_features *)id->driver_info);
wacom_wac->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
wacom_wac->features.device_type = BTN_TOOL_FINGER;
wacom_set_phy_from_res(&wacom_wac->features);
wacom_wac->features.x_max = wacom_wac->features.y_max = 4096;
wacom_register_input(wacom);
}
}
static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
{ {
struct usb_device *dev = interface_to_usbdev(intf); struct usb_device *dev = interface_to_usbdev(intf);
...@@ -907,6 +978,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i ...@@ -907,6 +978,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
wacom->usbdev = dev; wacom->usbdev = dev;
wacom->intf = intf; wacom->intf = intf;
mutex_init(&wacom->lock); mutex_init(&wacom->lock);
INIT_WORK(&wacom->work, wacom_wireless_work);
usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
...@@ -977,6 +1049,7 @@ static void wacom_disconnect(struct usb_interface *intf) ...@@ -977,6 +1049,7 @@ static void wacom_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
usb_kill_urb(wacom->irq); usb_kill_urb(wacom->irq);
cancel_work_sync(&wacom->work);
if (wacom->wacom_wac.input) if (wacom->wacom_wac.input)
input_unregister_device(wacom->wacom_wac.input); input_unregister_device(wacom->wacom_wac.input);
wacom_destroy_leds(wacom); wacom_destroy_leds(wacom);
......
...@@ -1046,9 +1046,27 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) ...@@ -1046,9 +1046,27 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
{ {
if (len != WACOM_PKGLEN_WIRELESS) unsigned char *data = wacom->data;
int connected;
if (len != WACOM_PKGLEN_WIRELESS || data[0] != 0x80)
return 0; return 0;
connected = data[1] & 0x01;
if (connected) {
int pid;
pid = get_unaligned_be16(&data[6]);
if (wacom->pid != pid) {
wacom->pid = pid;
wacom_schedule_work(wacom);
}
} else if (wacom->pid != 0) {
/* disconnected while previously connected */
wacom->pid = 0;
wacom_schedule_work(wacom);
}
return 0; return 0;
} }
......
...@@ -111,6 +111,7 @@ struct wacom_wac { ...@@ -111,6 +111,7 @@ struct wacom_wac {
struct wacom_features features; struct wacom_features features;
struct wacom_shared *shared; struct wacom_shared *shared;
struct input_dev *input; struct input_dev *input;
int pid;
}; };
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment