summaryrefslogtreecommitdiffstats
path: root/drivers/hid/wacom_sys.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/wacom_sys.c')
-rw-r--r--drivers/hid/wacom_sys.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 6edb7d136476..5f6e48e55df9 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -23,9 +23,13 @@
#define WAC_CMD_ICON_XFER 0x23
#define WAC_CMD_ICON_BT_XFER 0x26
#define WAC_CMD_RETRIES 10
+#define WAC_CMD_DELETE_PAIRING 0x20
+#define WAC_CMD_UNPAIR_ALL 0xFF
+#define WAC_REMOTE_SERIAL_MAX_STRLEN 9
#define DEV_ATTR_RW_PERM (S_IRUGO | S_IWUSR | S_IWGRP)
#define DEV_ATTR_WO_PERM (S_IWUSR | S_IWGRP)
+#define DEV_ATTR_RO_PERM (S_IRUSR | S_IRGRP)
static int wacom_get_report(struct hid_device *hdev, u8 type, u8 *buf,
size_t size, unsigned int retries)
@@ -1119,6 +1123,189 @@ static ssize_t wacom_store_speed(struct device *dev,
static DEVICE_ATTR(speed, DEV_ATTR_RW_PERM,
wacom_show_speed, wacom_store_speed);
+
+static ssize_t wacom_show_remote_mode(struct kobject *kobj,
+ struct kobj_attribute *kattr,
+ char *buf, int index)
+{
+ struct device *dev = container_of(kobj->parent, struct device, kobj);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct wacom *wacom = hid_get_drvdata(hdev);
+ u8 mode;
+
+ mode = wacom->led.select[index];
+ if (mode >= 0 && mode < 3)
+ return snprintf(buf, PAGE_SIZE, "%d\n", mode);
+ else
+ return snprintf(buf, PAGE_SIZE, "%d\n", -1);
+}
+
+#define DEVICE_EKR_ATTR_GROUP(SET_ID) \
+static ssize_t wacom_show_remote##SET_ID##_mode(struct kobject *kobj, \
+ struct kobj_attribute *kattr, char *buf) \
+{ \
+ return wacom_show_remote_mode(kobj, kattr, buf, SET_ID); \
+} \
+static struct kobj_attribute remote##SET_ID##_mode_attr = { \
+ .attr = {.name = "remote_mode", \
+ .mode = DEV_ATTR_RO_PERM}, \
+ .show = wacom_show_remote##SET_ID##_mode, \
+}; \
+static struct attribute *remote##SET_ID##_serial_attrs[] = { \
+ &remote##SET_ID##_mode_attr.attr, \
+ NULL \
+}; \
+static struct attribute_group remote##SET_ID##_serial_group = { \
+ .name = NULL, \
+ .attrs = remote##SET_ID##_serial_attrs, \
+}
+
+DEVICE_EKR_ATTR_GROUP(0);
+DEVICE_EKR_ATTR_GROUP(1);
+DEVICE_EKR_ATTR_GROUP(2);
+DEVICE_EKR_ATTR_GROUP(3);
+DEVICE_EKR_ATTR_GROUP(4);
+
+int wacom_remote_create_attr_group(struct wacom *wacom, __u32 serial, int index)
+{
+ int error = 0;
+ char *buf;
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
+ wacom_wac->serial[index] = serial;
+
+ buf = kzalloc(WAC_REMOTE_SERIAL_MAX_STRLEN, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ snprintf(buf, WAC_REMOTE_SERIAL_MAX_STRLEN, "%d", serial);
+ wacom->remote_group[index].name = buf;
+
+ error = sysfs_create_group(wacom->remote_dir,
+ &wacom->remote_group[index]);
+ if (error) {
+ hid_err(wacom->hdev,
+ "cannot create sysfs group err: %d\n", error);
+ kobject_put(wacom->remote_dir);
+ return error;
+ }
+
+ return 0;
+}
+
+void wacom_remote_destroy_attr_group(struct wacom *wacom, __u32 serial)
+{
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ int i;
+
+ if (!serial)
+ return;
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ if (wacom_wac->serial[i] == serial) {
+ wacom_wac->serial[i] = 0;
+ wacom->led.select[i] = WACOM_STATUS_UNKNOWN;
+ if (wacom->remote_group[i].name) {
+ sysfs_remove_group(wacom->remote_dir,
+ &wacom->remote_group[i]);
+ kfree(wacom->remote_group[i].name);
+ wacom->remote_group[i].name = NULL;
+ }
+ }
+ }
+}
+
+static int wacom_cmd_unpair_remote(struct wacom *wacom, unsigned char selector)
+{
+ const size_t buf_size = 2;
+ unsigned char *buf;
+ int retval;
+
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = WAC_CMD_DELETE_PAIRING;
+ buf[1] = selector;
+
+ retval = wacom_set_report(wacom->hdev, HID_OUTPUT_REPORT, buf,
+ buf_size, WAC_CMD_RETRIES);
+ kfree(buf);
+
+ return retval;
+}
+
+static ssize_t wacom_store_unpair_remote(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned char selector = 0;
+ struct device *dev = container_of(kobj->parent, struct device, kobj);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct wacom *wacom = hid_get_drvdata(hdev);
+ int err;
+
+ if (!strncmp(buf, "*\n", 2)) {
+ selector = WAC_CMD_UNPAIR_ALL;
+ } else {
+ hid_info(wacom->hdev, "remote: unrecognized unpair code: %s\n",
+ buf);
+ return -1;
+ }
+
+ mutex_lock(&wacom->lock);
+
+ err = wacom_cmd_unpair_remote(wacom, selector);
+ mutex_unlock(&wacom->lock);
+
+ return err < 0 ? err : count;
+}
+
+static struct kobj_attribute unpair_remote_attr = {
+ .attr = {.name = "unpair_remote", .mode = 0200},
+ .store = wacom_store_unpair_remote,
+};
+
+static const struct attribute *remote_unpair_attrs[] = {
+ &unpair_remote_attr.attr,
+ NULL
+};
+
+static int wacom_initialize_remote(struct wacom *wacom)
+{
+ int error = 0;
+ struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
+ int i;
+
+ if (wacom->wacom_wac.features.type != REMOTE)
+ return 0;
+
+ wacom->remote_group[0] = remote0_serial_group;
+ wacom->remote_group[1] = remote1_serial_group;
+ wacom->remote_group[2] = remote2_serial_group;
+ wacom->remote_group[3] = remote3_serial_group;
+ wacom->remote_group[4] = remote4_serial_group;
+
+ wacom->remote_dir = kobject_create_and_add("wacom_remote",
+ &wacom->hdev->dev.kobj);
+ if (!wacom->remote_dir)
+ return -ENOMEM;
+
+ error = sysfs_create_files(wacom->remote_dir, remote_unpair_attrs);
+
+ if (error) {
+ hid_err(wacom->hdev,
+ "cannot create sysfs group err: %d\n", error);
+ return error;
+ }
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ wacom->led.select[i] = WACOM_STATUS_UNKNOWN;
+ wacom_wac->serial[i] = 0;
+ }
+
+ return 0;
+}
+
static struct input_dev *wacom_allocate_input(struct wacom *wacom)
{
struct input_dev *input_dev;
@@ -1164,6 +1351,8 @@ static void wacom_clean_inputs(struct wacom *wacom)
else
input_free_device(wacom->wacom_wac.pad_input);
}
+ if (wacom->remote_dir)
+ kobject_put(wacom->remote_dir);
wacom->wacom_wac.pen_input = NULL;
wacom->wacom_wac.touch_input = NULL;
wacom->wacom_wac.pad_input = NULL;
@@ -1243,10 +1432,16 @@ static int wacom_register_inputs(struct wacom *wacom)
error = wacom_initialize_leds(wacom);
if (error)
goto fail_leds;
+
+ error = wacom_initialize_remote(wacom);
+ if (error)
+ goto fail_remote;
}
return 0;
+fail_remote:
+ wacom_destroy_leds(wacom);
fail_leds:
input_unregister_device(pad_input_dev);
pad_input_dev = NULL;