Ok, I've broken it up it as many reasonable patches as I can think of,
plus I included my script for exercising the LEDs (light shows are fun).
The patches are in the order I applied them. I've tried to incorporate
all suggestions and cleaned up some things. I can't really tell if
powersave is doing anything, it might wake up to fast for me to notice,
but my stop watch says the sleep timer works now :)
input-wacom-0.20.0-1-sys-reading:
- adds show functions for all sysfs attributes that make sense
- adds sane defaults for all sysfs permissions, which turns out to be
important for the gnome control panel later on.
input-wacom-0.20.0-2-crop-mark:
- adds a fully functional crop_marks attribute to wacom_led sysfs group
input-wacom-0.20.0-3-wireless-leds
- adds support for LEDs on the wireless kit
- consolidated build up and tear down code for interfaces, removes the
need clean up at every call-site
input-wacom-0.20.0-4-wireless-timers
- adds support for powersave and sleep timers on the wireless kit
- adds wacom_wireless sysfs group, which contains powersave_timer and
sleep_timer
- ranges follow previous suggestions
input-wacom-0.20.0-5-wireless-id
- adds sysfs attribute dev_match in each interface with the vid:pid of
the connected wireless tablet
libwacom-0.9-1-wireless-id
- requires input-wacom-0.20.0-5-wireless-id to work
- adds reading of dev_match from sysfs when the tablet is wireless
test-leds
- ooh, pretty lights ... just do: test-leds $(find /sys/ -name wacom_led)
I think that covers everything.
On 03/27/2014 01:36 PM, Jason Gerecke wrote:
On Wed, Mar 26, 2014 at 8:38 PM, Paul A. Tessier <phern...@gmail.com> wrote:
The X driver is a little hard to grok, but once you know where to look
adding extra properties isn't too bad. And the userspace pop-up
overlays sounds like something right up GNOME's alley given all the
interesting work they've done in writing a control panel. The
trickiest part I think is simply getting the data out of the kernel in
a clean way. I had patches to get the touch-sensitivity working with
the Intuos5, but they were rejected by the kernel maintainers. If you
have any thoughts on how to accomplish it, I'd be glad to hear them :)
In all things I defer to your expertise on this, as I haven't done much
system programming or contributions to open source projects.
I agree about using the uniq field was poorly thought out, but that was the
very first thing I did to get the tablet to be recognized in ubuntu
properly. Then I realized that the leds weren't working so I spent some time
learning about sysfs, and using wireshark to do some USB sniffing.
Everything I got was figured out by sniffing the USB going to a windows VM.
So I really have no idea about what the protocols are supposed to be.
The protocol docs aren't generally available (I've got access to them,
but only under NDA...), so I was scratching my head quite a bit when I
saw you adding support for the sleep timers! You can learn quite a bit
from doing traces and twiddling bits in the driver if you have the
will and patience.
On 03/26/2014 07:31 PM, Jason Gerecke wrote:
Thanks for the patch! I've added my comments in-line below :)
Firstly though, it looks like your patch was mangled along the way, so
I haven't been able to give it a whirl... In the future I'd suggest
either attaching the patches as files, or using `git send-email`.
I've attached the original two patches as you suggested.
You should introduce an e.g. WAC_CMD_WIRELESS_CONTROL macro rather
than use a magic constant.
Agreed.
Way more complex than it needs to be. `int crop_lum =
wacom->led.crop_lum & 0x03` should be all you need.
I gave crop_lum the same range as ring_lum since they are controlled by the
same slider in windows, which seems to have a large range even though the
Intuos Pro only support 4 values. I wasn't sure if limiting the range would
be too premature, plus I liked keeping it consistent with the other ranges
of values for led brightness.
Ah, I remember now. We specify sysfs will take a brightness in range
0..127 for the ring, even though the hardware can't support all those
levels. For the sake of consistency, you're probably right in copying
the range.
This doesn't quite jibe with the protocol docs that I have. buf[2]
contains a "powersave" (EMR low-power mode) timer. The "sleep"
(EMR/MFT/wireless minimum power) timer is controlled by buf[1]. If
you'd like to export both timers through sysfs, that'd be awesome :)
The value in buf[2] was exactly the same as the number of minutes that the
windows driver displayed. The windows driver is also probably wrong if what
you say is correct. Which is most likely as setting it in windows didn't
really seem to have the correct effect. Setting it to 1 minute, it didn't go
to sleep for 30 minutes some times, regardless of being left alone. Other
times is slept quite quickly.
Full "sleep" mode is admittedly somewhat more annoying to leave since
you have to press a button to wake the tablet. The "powersave" mode by
contrast should be exited within a fraction of a second of using the
tablet. The Windows driver might only be changing the powersave for
user-experience reasons I suppose. I'd definitely see what difference
you can find between buf[1] and buf[2].
- return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]); \
+ return snprintf(buf, 3, "%d\n", wacom->led.select[SET_ID]); \
Keen eye.
For the sysfs show functions I would have prefered to do snprintf(buf,
PAGE_SIZE, ... ) like sysfs.txt suggests, but when in rome ...
In that case, I'm tempted to say "even Rome eventually fell" and start
using PAGE_SIZE if that's what sysfs.txt suggests.
+ wacom->sleep_timer = clamp_val(time, 1, 20);
Valid values are from 5 to 60 (below the tablet defaults to 15 minute
sleep; above it disables sleep). For the powersave timer, they're 1 to
60 (below, 1 minute default; above disabled)
We might want to have seperate attributes that allow the user to
explicitely disable/default the timers, or possibly allow these to
take special values (0 = disabled, -1 = default?). Whatever the
decision, our sysfs documentaiton should be updated to reflect it.
The values for the sleep timer only goes from 1 to 20 in windows, so I
copied that. I do like your suggestions much better.
Good to know what the range used on Windows is as well; control panel
developers might be interested in knowing what Windows and Mac have by
default.
+ return snprintf(buf, 5, "%d\n", wacom->sleep_timer);
Only need 4, not 5.
Yes, bad math ... should have just used PAGE_SIZE.
+ wacom->led.crop_lum = 32;
Should be `wacom->led.crop_lum = 2`
Staying consistent with llv and hlv.
+ if (wacom_wac->uniq[0] == 0) {
+ if (wacom_wac->features.quirks & WACOM_QUIRK_WIRELESS_KIT)
+ snprintf(wacom_wac->uniq, WACOM_UNIQ_MAX, "056a:%04x",
+ wacom_wac->pid);
Instead of hardcoding "056a" into the string, use USB_VENDOR_ID_WACOM.
Doh!
+ else
+ snprintf(wacom_wac->uniq, WACOM_UNIQ_MAX, "%04x:%04x",
+ dev->descriptor.idVendor, dev->descriptor.idProduct);
+ }
+
input_dev->name = wacom_wac->name;
+ input_dev->uniq = wacom_wac->uniq;
The tablet VID:PID isn't a unique enough identifier to be stored in
the `uniq` field. You should add a sysfs attribute to the wireless
group which exports the information.
Agree completely, but after going through the trouble of adding the
wacom_wireless sysfs group, I realized I should move the wireless tablet pid
there, but the sleep timer was the last thing I did, and redoing previous
work when I finally got everything working was too much.
I know exactly how you feel :D Unfortunately, the upstream kernel
would never accept a patch which did that :(
+
+ switch (wacom_features->type) {
+ case INTUOSPS:
+ case INTUOSPM:
+ case INTUOSPL:
All wireless tablets should have this particular group, IIRC. Bamboo
PIDs 0xDC-0xDF, Intuos, Intuos5, and Intuos Pro.
Wasn't sure if they worked the same.
+ if (wacom_features->device_type == BTN_TOOL_PEN) {
+ wacom->sleep_timer = 2;
The default value for this and the powersave timer are 15 minutes and
1 minute respectively. You should set them to zero to use the hardware
default.
The windows default was 2. I wasn't sure if 0 meant never sleep and I don't
like killing my batteries.
Jason
---
Now instead of four in the eights place /
you’ve got three, ‘Cause you added one /
(That is to say, eight) to the two, /
But you can’t take seven from three, /
So you look at the sixty-fours....
Jason
---
Now instead of four in the eights place /
you’ve got three, ‘Cause you added one /
(That is to say, eight) to the two, /
But you can’t take seven from three, /
So you look at the sixty-fours....
#!/bin/bash
err()
{
echo "$@" >&2
exit 1
}
set_led()
{
echo $1 > "$led_sel"
}
set_rng()
{
echo $(expr $1 \* 32 ) > "$rng_lum"
}
set_crp()
{
echo $(expr $1 \* 32 ) > "$crp_lum"
}
[ ! -d "$1" ] && err "Not a folder: $1"
led_sel="$1/status_led0_select"
rng_lum="$1/status0_luminance"
crp_lum="$1/crop_marks_luminance"
delay=0.1
[ ! -f "$led_sel" ] && err "No led selector: $led_sel"
[ ! -f "$rng_lum" ] && err "No ring leds: $rng_lum"
[ ! -f "$crp_lum" ] && err "No crop leds: $crp_lum"
old_led=$(cat "$led_sel")
old_rng=$(expr $(cat "$rng_lum") / 32)
old_crp=$(expr $(cat "$crp_lum") / 32)
set_led 0
set_crp 0
set_rng 0
for j in {0..2}; do
for i in {1..3}; do
set_crp $i
sleep $delay
done
for i in {2..0}; do
set_crp $i
sleep $delay
done
done
for k in {0..3}; do
set_led $k
for j in {0..2}; do
for i in {1..3}; do
set_rng $i
sleep $delay
done
for i in {2..0}; do
set_rng $i
sleep $delay
done
done
done
set_led $old_led
set_crp $old_crp
set_rng $old_rng
diff --git a/3.7-0/wacom_sys.c b/3.7-1-sysfs-reading/wacom_sys.c
index 8de1cc3..553521a 100644
--- a/3.7-0/wacom_sys.c
+++ b/3.7-1-sysfs-reading/wacom_sys.c
@@ -60,6 +60,10 @@ struct hid_descriptor {
#define WAC_CMD_ICON_XFER 0x23
#define WAC_CMD_RETRIES 10
+#define DEV_ATTR_RW (S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH)
+#define DEV_ATTR_RO (S_IRUSR | S_IRGRP | S_IROTH)
+#define DEV_ATTR_WO (S_IWUSR | S_IWGRP)
+
static int wacom_get_report(struct usb_interface *intf, u8 type, u8 id,
void *buf, size_t size, unsigned int retries)
{
@@ -825,9 +829,10 @@ static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct wacom *wacom = dev_get_drvdata(dev); \
- return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]); \
+ return scnprintf(buf, PAGE_SIZE, "%d\n", \
+ wacom->led.select[SET_ID]); \
} \
-static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR, \
+static DEVICE_ATTR(status_led##SET_ID##_select, DEV_ATTR_RW, \
wacom_led##SET_ID##_select_show, \
wacom_led##SET_ID##_select_store)
@@ -863,8 +868,15 @@ static ssize_t wacom_##name##_luminance_store(struct device *dev, \
return wacom_luminance_store(wacom, &wacom->led.field, \
buf, count); \
} \
-static DEVICE_ATTR(name##_luminance, S_IWUSR, \
- NULL, wacom_##name##_luminance_store)
+static ssize_t wacom_##name##_luminance_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct wacom *wacom = dev_get_drvdata(dev); \
+ return scnprintf(buf, PAGE_SIZE, "%d\n", wacom->led.field); \
+} \
+static DEVICE_ATTR(name##_luminance, DEV_ATTR_RW, \
+ wacom_##name##_luminance_show, \
+ wacom_##name##_luminance_store)
DEVICE_LUMINANCE_ATTR(status0, llv);
DEVICE_LUMINANCE_ATTR(status1, hlv);
@@ -894,7 +906,7 @@ static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev, \
{ \
return wacom_button_image_store(dev, BUTTON_ID, buf, count); \
} \
-static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR, \
+static DEVICE_ATTR(button##BUTTON_ID##_rawimg, DEV_ATTR_WO, \
NULL, wacom_btnimg##BUTTON_ID##_store)
DEVICE_BTNIMG_ATTR(0);
diff --git a/3.7-1-sysfs-reading/wacom.h b/3.7-2-crop-marks/wacom.h
index b79d451..19ab360 100644
--- a/3.7-1-sysfs-reading/wacom.h
+++ b/3.7-2-crop-marks/wacom.h
@@ -121,6 +121,7 @@ struct wacom {
u8 llv; /* status led brightness no button (1..127) */
u8 hlv; /* status led brightness button pressed (1..127) */
u8 img_lum; /* OLED matrix display brightness */
+ u8 crop_lum; /* crop marks led brightness (1..127) */
} led;
struct power_supply battery;
};
diff --git a/3.7-1-sysfs-reading/wacom_sys.c b/3.7-2-crop-marks/wacom_sys.c
index 553521a..1a3e488 100644
--- a/3.7-1-sysfs-reading/wacom_sys.c
+++ b/3.7-2-crop-marks/wacom_sys.c
@@ -732,7 +732,7 @@ static int wacom_led_control(struct wacom *wacom)
*/
int ring_led = wacom->led.select[0] & 0x03;
int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03;
- int crop_lum = 0;
+ int crop_lum = (((wacom->led.crop_lum & 0x60) >> 5) - 1) & 0x03;
buf[0] = WAC_CMD_LED_CONTROL;
buf[1] = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
@@ -880,6 +880,7 @@ static DEVICE_ATTR(name##_luminance, DEV_ATTR_RW, \
DEVICE_LUMINANCE_ATTR(status0, llv);
DEVICE_LUMINANCE_ATTR(status1, hlv);
+DEVICE_LUMINANCE_ATTR(crop_marks, crop_lum);
DEVICE_LUMINANCE_ATTR(buttons, img_lum);
static ssize_t wacom_button_image_store(struct device *dev, int button_id,
@@ -952,6 +953,7 @@ static struct attribute_group intuos4_led_attr_group = {
static struct attribute *intuos5_led_attrs[] = {
&dev_attr_status0_luminance.attr,
+ &dev_attr_crop_marks_luminance.attr,
&dev_attr_status_led0_select.attr,
NULL
};
@@ -1002,6 +1004,7 @@ static int wacom_initialize_leds(struct wacom *wacom)
wacom->led.select[1] = 0;
wacom->led.llv = 32;
wacom->led.hlv = 0;
+ wacom->led.crop_lum = 32;
wacom->led.img_lum = 0;
error = sysfs_create_group(&wacom->intf->dev.kobj,
diff --git a/3.7-2-crop-marks/wacom_sys.c b/3.7-3-wireless_leds/wacom_sys.c
index 1a3e488..19df51d 100644
--- a/3.7-2-crop-marks/wacom_sys.c
+++ b/3.7-3-wireless_leds/wacom_sys.c
@@ -55,10 +55,11 @@ struct hid_descriptor {
#define WAC_HID_FEATURE_REPORT 0x03
#define WAC_MSG_RETRIES 5
-#define WAC_CMD_LED_CONTROL 0x20
-#define WAC_CMD_ICON_START 0x21
-#define WAC_CMD_ICON_XFER 0x23
-#define WAC_CMD_RETRIES 10
+#define WAC_CMD_WIRELESS_CONTROL 0x03
+#define WAC_CMD_LED_CONTROL 0x20
+#define WAC_CMD_ICON_START 0x21
+#define WAC_CMD_ICON_XFER 0x23
+#define WAC_CMD_RETRIES 10
#define DEV_ATTR_RW (S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH)
#define DEV_ATTR_RO (S_IRUSR | S_IRGRP | S_IROTH)
@@ -717,9 +718,19 @@ static void wacom_remove_shared_data(struct wacom_wac *wacom)
static int wacom_led_control(struct wacom *wacom)
{
unsigned char *buf;
+ int buf_size;
+ int command;
int retval;
+
+ if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_WIRELESS_KIT) {
+ command = WAC_CMD_WIRELESS_CONTROL;
+ buf_size = 13;
+ } else {
+ command = WAC_CMD_LED_CONTROL;
+ buf_size = 9;
+ }
- buf = kzalloc(9, GFP_KERNEL);
+ buf = kzalloc(buf_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -733,9 +744,17 @@ static int wacom_led_control(struct wacom *wacom)
int ring_led = wacom->led.select[0] & 0x03;
int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03;
int crop_lum = (((wacom->led.crop_lum & 0x60) >> 5) - 1) & 0x03;
+ int bits = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
- buf[0] = WAC_CMD_LED_CONTROL;
- buf[1] = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
+ if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_WIRELESS_KIT) {
+ buf[3] = 0x04;
+ buf[4] = 0x40 | bits;
+ } else {
+ buf[1] = bits;
+
+ if (wacom->wacom_wac.features.type >= INTUOSPS)
+ buf[2] = ring_lum;
+ }
}
else {
int led = wacom->led.select[0] | 0x4;
@@ -744,15 +763,15 @@ static int wacom_led_control(struct wacom *wacom)
wacom->wacom_wac.features.type == WACOM_24HD)
led |= (wacom->led.select[1] << 4) | 0x40;
- buf[0] = WAC_CMD_LED_CONTROL;
buf[1] = led;
buf[2] = wacom->led.llv;
buf[3] = wacom->led.hlv;
buf[4] = wacom->led.img_lum;
}
- retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL,
- buf, 9, WAC_CMD_RETRIES);
+ buf[0] = command;
+ retval = wacom_set_report(wacom->intf, 0x03, command, buf, buf_size,
+ WAC_CMD_RETRIES);
kfree(buf);
return retval;
@@ -1019,7 +1038,7 @@ static int wacom_initialize_leds(struct wacom *wacom)
if (error) {
dev_err(&wacom->intf->dev,
- "cannot create sysfs group err: %d\n", error);
+ "cannot create 'wacom_led' sysfs group: err %d\n", error);
return error;
}
wacom_led_control(wacom);
@@ -1151,6 +1170,37 @@ fail1:
return error;
}
+static void wacom_unregister(struct wacom *wacom)
+{
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
+ if (wacom_wac->input) {
+ input_unregister_device(wacom_wac->input);
+ wacom_destroy_leds(wacom);
+ }
+
+ wacom_wac->input = NULL;
+}
+
+static int wacom_register(struct wacom *wacom)
+{
+ int error;
+
+ error = wacom_initialize_leds(wacom);
+ if (error)
+ goto fail1;
+
+ error = wacom_register_input(wacom);
+ if (error)
+ goto fail2;
+
+ return 0;
+
+fail2: wacom_destroy_leds(wacom);
+fail1:
+ return error;
+}
+
static void wacom_wireless_work(struct work_struct *work)
{
struct wacom *wacom = container_of(work, struct wacom, work);
@@ -1170,16 +1220,12 @@ static void wacom_wireless_work(struct work_struct *work)
/* Stylus interface */
wacom1 = usb_get_intfdata(usbdev->config->interface[1]);
wacom_wac1 = &(wacom1->wacom_wac);
- if (wacom_wac1->input)
- input_unregister_device(wacom_wac1->input);
- wacom_wac1->input = NULL;
+ wacom_unregister(wacom1);
/* Touch interface */
wacom2 = usb_get_intfdata(usbdev->config->interface[2]);
wacom_wac2 = &(wacom2->wacom_wac);
- if (wacom_wac2->input)
- input_unregister_device(wacom_wac2->input);
- wacom_wac2->input = NULL;
+ wacom_unregister(wacom2);
if (wacom_wac->pid == 0) {
dev_info(&wacom->intf->dev, "wireless tablet disconnected\n");
@@ -1211,7 +1257,8 @@ static void wacom_wireless_work(struct work_struct *work)
wacom_wac1->features.name);
wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max;
wacom_wac1->shared->type = wacom_wac1->features.type;
- error = wacom_register_input(wacom1);
+ wacom_wac1->features.quirks |= WACOM_QUIRK_WIRELESS_KIT;
+ error = wacom_register(wacom1);
if (error)
goto fail;
@@ -1229,7 +1276,8 @@ static void wacom_wireless_work(struct work_struct *work)
else
snprintf(wacom_wac2->name, WACOM_NAME_MAX,
"%s (WL) Pad",wacom_wac2->features.name);
- error = wacom_register_input(wacom2);
+ wacom_wac2->features.quirks |= WACOM_QUIRK_WIRELESS_KIT;
+ error = wacom_register(wacom2);
if (error)
goto fail;
@@ -1246,15 +1294,8 @@ static void wacom_wireless_work(struct work_struct *work)
return;
fail:
- if (wacom_wac2->input) {
- input_unregister_device(wacom_wac2->input);
- wacom_wac2->input = NULL;
- }
-
- if (wacom_wac1->input) {
- input_unregister_device(wacom_wac1->input);
- wacom_wac1->input = NULL;
- }
+ wacom_unregister(wacom2);
+ wacom_unregister(wacom1);
return;
}
@@ -1389,14 +1430,10 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
wacom->irq->transfer_dma = wacom->data_dma;
wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- error = wacom_initialize_leds(wacom);
- if (error)
- goto fail4;
-
if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
- error = wacom_register_input(wacom);
+ error = wacom_register(wacom);
if (error)
- goto fail5;
+ goto fail4;
}
/* Note that if query fails it is not a hard failure */
@@ -1418,7 +1455,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
return 0;
- fail5: wacom_destroy_leds(wacom);
+ fail5: wacom_unregister(wacom);
fail4: wacom_remove_shared_data(wacom_wac);
fail3: usb_free_urb(wacom->irq);
fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);
@@ -1434,10 +1471,8 @@ static void wacom_disconnect(struct usb_interface *intf)
usb_kill_urb(wacom->irq);
cancel_work_sync(&wacom->work);
- if (wacom->wacom_wac.input)
- input_unregister_device(wacom->wacom_wac.input);
+ wacom_unregister(wacom);
wacom_destroy_battery(wacom);
- wacom_destroy_leds(wacom);
usb_free_urb(wacom->irq);
usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
wacom->wacom_wac.data, wacom->data_dma);
diff --git a/3.7-2-crop-marks/wacom_wac.h b/3.7-3-wireless_leds/wacom_wac.h
index f69c0eb..2ec64de 100644
--- a/3.7-2-crop-marks/wacom_wac.h
+++ b/3.7-3-wireless_leds/wacom_wac.h
@@ -65,6 +65,7 @@
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002
#define WACOM_QUIRK_NO_INPUT 0x0004
#define WACOM_QUIRK_MONITOR 0x0008
+#define WACOM_QUIRK_WIRELESS_KIT 0x0016
enum {
PENPARTNER = 0,
diff --git a/3.7-3-wireless_leds/wacom.h b/3.7-4-wireless-timers/wacom.h
index 19ab360..797f2b2 100644
--- a/3.7-3-wireless_leds/wacom.h
+++ b/3.7-4-wireless-timers/wacom.h
@@ -124,6 +124,11 @@ struct wacom {
u8 crop_lum; /* crop marks led brightness (1..127) */
} led;
struct power_supply battery;
+ struct wacom_wireless {
+ /* -1 = default, 0 = off, X = mintues */
+ u8 sleep_timer; /* power off timer (-1,0,5..60) */
+ u8 powersave_timer; /* low power mode timer (-1..60) */
+ } wireless;
};
static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
diff --git a/3.7-3-wireless_leds/wacom_sys.c b/3.7-4-wireless-timers/wacom_sys.c
index 19df51d..115b8b1 100644
--- a/3.7-3-wireless_leds/wacom_sys.c
+++ b/3.7-4-wireless-timers/wacom_sys.c
@@ -61,6 +61,22 @@ struct hid_descriptor {
#define WAC_CMD_ICON_XFER 0x23
#define WAC_CMD_RETRIES 10
+/* Wacom Wireless Kit */
+#define WWK_SLEEP_MIN 5
+#define WWK_SLEEP_MAX 60
+#define WWK_POWERSAVE_MIN 1
+#define WWK_POWERSAVE_MAX 60
+
+/* WWK_HARD_TIMER_DEFAULTS forces the use of the actual hardware default in the
+tablet otherwise we use a consistent default across all connected devices. */
+#ifdef WWK_HARD_TIMER_DEFAULTS
+#define WWK_SLEEP_DEFAULT 0
+#define WWK_POWERSAVE_DEFAULT 0
+#else
+#define WWK_SLEEP_DEFAULT 15
+#define WWK_POWERSAVE_DEFAULT 1
+#endif
+
#define DEV_ATTR_RW (S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH)
#define DEV_ATTR_RO (S_IRUSR | S_IRGRP | S_IROTH)
#define DEV_ATTR_WO (S_IWUSR | S_IWGRP)
@@ -747,6 +763,8 @@ static int wacom_led_control(struct wacom *wacom)
int bits = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_WIRELESS_KIT) {
+ buf[1] = wacom->wireless.sleep_timer;
+ buf[2] = wacom->wireless.powersave_timer;
buf[3] = 0x04;
buf[4] = 0x40 | bits;
} else {
@@ -777,6 +795,12 @@ static int wacom_led_control(struct wacom *wacom)
return retval;
}
+static int wacom_wireless_control(struct wacom *wacom)
+{
+ /* currently the wireless timers ride along with the led controls */
+ return wacom_led_control(wacom);
+}
+
static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img)
{
unsigned char *buf;
@@ -938,6 +962,61 @@ DEVICE_BTNIMG_ATTR(5);
DEVICE_BTNIMG_ATTR(6);
DEVICE_BTNIMG_ATTR(7);
+#define DEVICE_WIRELESS_TIMER_ATTR(name, def) \
+static ssize_t wacom_wireless_##name##_timer_store(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct wacom *wacom = dev_get_drvdata(dev); \
+ int time, err; \
+ err = kstrtoint(buf, 10, &time); \
+ if (err) \
+ return err; \
+ if (time > WWK_##def##_MAX) \
+ return -ERANGE; \
+ if (time < WWK_##def##_MIN) { \
+ if(time == -1) { \
+ time = WWK_##def##_DEFAULT; \
+ } else if(time == 0) { \
+ time = WWK_##def##_MAX + 1; \
+ } else { \
+ return -ERANGE; \
+ } \
+ } \
+ mutex_lock(&wacom->lock); \
+ wacom->wireless.name##_timer = time; \
+ err = wacom_wireless_control(wacom); \
+ mutex_unlock(&wacom->lock); \
+ return err < 0 ? err : count; \
+} \
+static ssize_t wacom_wireless_##name##_timer_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct wacom *wacom = dev_get_drvdata(dev); \
+ int time = wacom->wireless.name##_timer; \
+ if (time > WWK_##def##_MAX) \
+ time = 0; \
+ else if (time == WWK_##def##_DEFAULT) \
+ time = -1; \
+ return scnprintf(buf, PAGE_SIZE, "%d\n",time); \
+} \
+static DEVICE_ATTR(name##_timer, DEV_ATTR_RW, \
+ wacom_wireless_##name##_timer_show, \
+ wacom_wireless_##name##_timer_store)
+
+DEVICE_WIRELESS_TIMER_ATTR(sleep, SLEEP);
+DEVICE_WIRELESS_TIMER_ATTR(powersave, POWERSAVE);
+
+static struct attribute *wireless_attrs[] = {
+ &dev_attr_sleep_timer.attr,
+ &dev_attr_powersave_timer.attr,
+ NULL
+};
+
+static struct attribute_group wireless_attr_group = {
+ .name = "wacom_wireless",
+ .attrs = wireless_attrs,
+};
+
static struct attribute *cintiq_led_attrs[] = {
&dev_attr_status_led0_select.attr,
&dev_attr_status_led1_select.attr,
@@ -1170,12 +1249,52 @@ fail1:
return error;
}
+static void wacom_unregister_wireless(struct wacom *wacom)
+{
+ struct wacom_features *wacom_features = &wacom->wacom_wac.features;
+
+ if (!(wacom_features->quirks & WACOM_QUIRK_WIRELESS_KIT) ||
+ wacom_features->device_type != BTN_TOOL_PEN)
+ return;
+
+ sysfs_remove_group(&wacom->intf->dev.kobj,
+ &wireless_attr_group);
+}
+
+static int wacom_register_wireless(struct wacom *wacom)
+{
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct wacom_features *wacom_features = &wacom_wac->features;
+ int error;
+
+ if (!(wacom_features->quirks & WACOM_QUIRK_WIRELESS_KIT) ||
+ wacom_features->device_type != BTN_TOOL_PEN)
+ return 0;
+
+ wacom->wireless.sleep_timer = WWK_SLEEP_DEFAULT;
+ wacom->wireless.powersave_timer = WWK_POWERSAVE_DEFAULT;
+
+ error = sysfs_create_group(&wacom->intf->dev.kobj,
+ &wireless_attr_group);
+ if (error) {
+ dev_err(&wacom->intf->dev,
+ "cannot create 'wacom_wireless' sysfs group: err %d\n",
+ error);
+ return error;
+ }
+
+ wacom_wireless_control(wacom);
+
+ return 0;
+}
+
static void wacom_unregister(struct wacom *wacom)
{
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
if (wacom_wac->input) {
input_unregister_device(wacom_wac->input);
+ wacom_unregister_wireless(wacom);
wacom_destroy_leds(wacom);
}
@@ -1190,12 +1309,17 @@ static int wacom_register(struct wacom *wacom)
if (error)
goto fail1;
- error = wacom_register_input(wacom);
+ error = wacom_register_wireless(wacom);
if (error)
goto fail2;
+
+ error = wacom_register_input(wacom);
+ if (error)
+ goto fail3;
return 0;
+fail3: wacom_unregister_wireless(wacom);
fail2: wacom_destroy_leds(wacom);
fail1:
return error;
diff --git a/3.7-4-wireless-timers/wacom_sys.c b/3.7-5-wireless-id/wacom_sys.c
index 115b8b1..d997def 100644
--- a/3.7-4-wireless-timers/wacom_sys.c
+++ b/3.7-5-wireless-id/wacom_sys.c
@@ -1006,6 +1006,18 @@ static DEVICE_ATTR(name##_timer, DEV_ATTR_RW, \
DEVICE_WIRELESS_TIMER_ATTR(sleep, SLEEP);
DEVICE_WIRELESS_TIMER_ATTR(powersave, POWERSAVE);
+static ssize_t wacom_wireless_dev_match_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct wacom *wacom = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%04x:%04x\n", USB_VENDOR_ID_WACOM,
+ wacom->wacom_wac.pid);
+}
+
+static DEVICE_ATTR(dev_match, DEV_ATTR_RO, wacom_wireless_dev_match_show, NULL);
+
+
static struct attribute *wireless_attrs[] = {
&dev_attr_sleep_timer.attr,
&dev_attr_powersave_timer.attr,
@@ -1253,8 +1265,12 @@ static void wacom_unregister_wireless(struct wacom *wacom)
{
struct wacom_features *wacom_features = &wacom->wacom_wac.features;
- if (!(wacom_features->quirks & WACOM_QUIRK_WIRELESS_KIT) ||
- wacom_features->device_type != BTN_TOOL_PEN)
+ if (!(wacom_features->quirks & WACOM_QUIRK_WIRELESS_KIT))
+ return;
+
+ device_remove_file(&wacom->intf->dev, &dev_attr_dev_match);
+
+ if (wacom_features->device_type != BTN_TOOL_PEN)
return;
sysfs_remove_group(&wacom->intf->dev.kobj,
@@ -1267,8 +1283,18 @@ static int wacom_register_wireless(struct wacom *wacom)
struct wacom_features *wacom_features = &wacom_wac->features;
int error;
- if (!(wacom_features->quirks & WACOM_QUIRK_WIRELESS_KIT) ||
- wacom_features->device_type != BTN_TOOL_PEN)
+ if (!(wacom_features->quirks & WACOM_QUIRK_WIRELESS_KIT))
+ return 0;
+
+ error = device_create_file(&wacom->intf->dev, &dev_attr_dev_match);
+ if (error) {
+ dev_err(&wacom->intf->dev,
+ "cannot create 'dev_match' sysfs file: err %d\n",
+ error);
+ return error;
+ }
+
+ if(wacom_features->device_type != BTN_TOOL_PEN)
return 0;
wacom->wireless.sleep_timer = WWK_SLEEP_DEFAULT;
@@ -1382,6 +1408,7 @@ static void wacom_wireless_work(struct work_struct *work)
wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max;
wacom_wac1->shared->type = wacom_wac1->features.type;
wacom_wac1->features.quirks |= WACOM_QUIRK_WIRELESS_KIT;
+ wacom_wac1->pid = wacom_wac->pid;
error = wacom_register(wacom1);
if (error)
goto fail;
@@ -1401,6 +1428,7 @@ static void wacom_wireless_work(struct work_struct *work)
snprintf(wacom_wac2->name, WACOM_NAME_MAX,
"%s (WL) Pad",wacom_wac2->features.name);
wacom_wac2->features.quirks |= WACOM_QUIRK_WIRELESS_KIT;
+ wacom_wac2->pid = wacom_wac->pid;
error = wacom_register(wacom2);
if (error)
goto fail;
diff --git a/libwacom-0/libwacom.c b/libwacom-1-wireless-id/libwacom.c
index 574e6f3..0381884 100644
--- a/libwacom-0/libwacom.c
+++ b/libwacom-1-wireless-id/libwacom.c
@@ -47,6 +47,16 @@
#define INPUT_PROP_DIRECT 0x01 /* direct input devices */
#endif
+#define WACOM_WIRELESS_KIT_VENDOR_ID 0x056a
+#define WACOM_WIRELESS_KIT_PRODUCT_ID 0x0084
+
+static gboolean
+is_wireless(int vendor_id, int product_id)
+{
+ return vendor_id == WACOM_WIRELESS_KIT_VENDOR_ID
+ && product_id == WACOM_WIRELESS_KIT_PRODUCT_ID;
+}
+
static const WacomDevice *
libwacom_get_device(const WacomDeviceDatabase *db, const char *match)
{
@@ -207,7 +217,7 @@ get_device_info (const char *path,
*bus = bus_from_str (bus_str);
if (*bus == WBUSTYPE_USB) {
- const char *vendor_str, *product_str;
+ const char *vendor_str, *product_str, *match_str;
GUdevDevice *parent;
vendor_str = g_udev_device_get_property (device, "ID_VENDOR_ID");
@@ -221,14 +231,33 @@ get_device_info (const char *path,
if (parent) {
vendor_str = g_udev_device_get_property (parent, "ID_VENDOR_ID");
product_str = g_udev_device_get_property (parent, "ID_MODEL_ID");
+ g_object_unref (parent);
}
}
*vendor_id = strtol (vendor_str, NULL, 16);
*product_id = strtol (product_str, NULL, 16);
-
- if (parent)
+
+ if (is_wireless (*vendor_id, *product_id)) {
+ parent = g_udev_device_get_parent_with_subsystem (
+ device, "usb", "usb_interface");
+ if (!parent)
+ goto bail;
+ match_str = g_udev_device_get_sysfs_attr (parent,
+ "dev_match");
g_object_unref (parent);
+
+ if (!match_str)
+ goto bail;
+
+ if (sscanf(match_str, "%x:%x", vendor_id, product_id)
+ != 2) {
+ libwacom_error_set(error, WERROR_UNKNOWN_MODEL,
+ "Unable to parse wireless tablet model");
+ goto bail;
+ }
+ }
+
} else if (*bus == WBUSTYPE_BLUETOOTH || *bus == WBUSTYPE_SERIAL) {
GUdevDevice *parent;
const char *product_str;
------------------------------------------------------------------------------
_______________________________________________
Linuxwacom-devel mailing list
Linuxwacom-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel