Skip to content

Commit 4922cd2

Browse files
jigpuJiri Kosina
authored and
Jiri Kosina
committed
HID: wacom: Support 2nd-gen Intuos Pro's Bluetooth classic interface
In addition to its USB interface, the second-generation Intuos Pro includes a Bluetooth radio that offers two pairing interfaces: classic and low-energy. The classic interface functions just like the earlier Bluetooth-enabled Intuos4 and Graphire4 tablets, appearing as a HID device that our driver can work with. The low-energy interface is intented to be used by userspace applications that make use of its paper-to-digital capabilities. Despite the USB interface using Wacom's new vendor-defined HID usages, the Bluetooth interface provides us with useless black-box "blob" report descriptors like past devices. We thus have to explicitly add support for the PIDs and reports used. These devices pack a /lot/ of information into a single Bluetooth input report. Each report contains up to seven snapshots of the pen state, four snapshots of the touch state (of five touches each), pad state, and battery data. Thankfully this isn't too hard for the driver to report -- it just takes a fair amount of code to extract! Signed-off-by: Jason Gerecke <jason.gerecke@wacom.com> Reviewed-by: Ping Cheng <pingc@wacom.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
1 parent 5ba13c6 commit 4922cd2

File tree

3 files changed

+226
-2
lines changed

3 files changed

+226
-2
lines changed

drivers/hid/wacom_sys.c

+25
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,10 @@ static int wacom_led_control(struct wacom *wacom)
756756
report_id = WAC_CMD_WL_LED_CONTROL;
757757
buf_size = 13;
758758
}
759+
else if (wacom->wacom_wac.features.type == INTUOSP2_BT) {
760+
report_id = WAC_CMD_WL_INTUOSP2;
761+
buf_size = 51;
762+
}
759763
buf = kzalloc(buf_size, GFP_KERNEL);
760764
if (!buf)
761765
return -ENOMEM;
@@ -781,6 +785,16 @@ static int wacom_led_control(struct wacom *wacom)
781785
} else
782786
buf[1] = led_bits;
783787
}
788+
else if (wacom->wacom_wac.features.type == INTUOSP2_BT) {
789+
buf[0] = report_id;
790+
buf[4] = 100; // Power Connection LED (ORANGE)
791+
buf[5] = 100; // BT Connection LED (BLUE)
792+
buf[6] = 100; // Paper Mode (RED?)
793+
buf[7] = 100; // Paper Mode (GREEN?)
794+
buf[8] = 100; // Paper Mode (BLUE?)
795+
buf[9] = wacom->led.llv;
796+
buf[10] = wacom->led.groups[0].select & 0x03;
797+
}
784798
else {
785799
int led = wacom->led.groups[0].select | 0x4;
786800

@@ -1409,6 +1423,17 @@ static int wacom_initialize_leds(struct wacom *wacom)
14091423
&intuos5_led_attr_group);
14101424
break;
14111425

1426+
case INTUOSP2_BT:
1427+
wacom->led.llv = 50;
1428+
wacom->led.max_llv = 100;
1429+
error = wacom_leds_alloc_and_register(wacom, 1, 4, false);
1430+
if (error) {
1431+
hid_err(wacom->hdev,
1432+
"cannot create leds err: %d\n", error);
1433+
return error;
1434+
}
1435+
return 0;
1436+
14121437
case REMOTE:
14131438
wacom->led.llv = 255;
14141439
wacom->led.max_llv = 255;

drivers/hid/wacom_wac.c

+197
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,161 @@ static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
11901190
return count;
11911191
}
11921192

1193+
static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
1194+
{
1195+
const int pen_frame_len = 14;
1196+
const int pen_frames = 7;
1197+
1198+
struct input_dev *pen_input = wacom->pen_input;
1199+
unsigned char *data = wacom->data;
1200+
int i;
1201+
1202+
wacom->serial[0] = get_unaligned_le64(&data[99]);
1203+
wacom->id[0] = get_unaligned_le16(&data[107]);
1204+
if (wacom->serial[0] >> 52 == 1) {
1205+
/* Add back in missing bits of ID for non-USI pens */
1206+
wacom->id[0] |= (wacom->serial[0] >> 32) & 0xFFFFF;
1207+
}
1208+
wacom->tool[0] = wacom_intuos_get_tool_type(wacom_intuos_id_mangle(wacom->id[0]));
1209+
1210+
for (i = 0; i < pen_frames; i++) {
1211+
unsigned char *frame = &data[i*pen_frame_len + 1];
1212+
1213+
if (!(frame[0] & 0x80))
1214+
continue;
1215+
1216+
input_report_abs(pen_input, ABS_X, get_unaligned_le16(&frame[1]));
1217+
input_report_abs(pen_input, ABS_Y, get_unaligned_le16(&frame[3]));
1218+
input_report_abs(pen_input, ABS_PRESSURE, get_unaligned_le16(&frame[5]));
1219+
input_report_abs(pen_input, ABS_TILT_X, frame[7]);
1220+
input_report_abs(pen_input, ABS_TILT_Y, frame[8]);
1221+
input_report_abs(pen_input, ABS_Z, get_unaligned_le16(&frame[9]));
1222+
input_report_abs(pen_input, ABS_WHEEL, get_unaligned_le16(&frame[11]));
1223+
input_report_abs(pen_input, ABS_DISTANCE, frame[13]);
1224+
1225+
input_report_key(pen_input, BTN_TOUCH, frame[0] & 0x01);
1226+
input_report_key(pen_input, BTN_STYLUS, frame[0] & 0x02);
1227+
input_report_key(pen_input, BTN_STYLUS2, frame[0] & 0x04);
1228+
1229+
input_report_key(pen_input, wacom->tool[0], 1);
1230+
input_event(pen_input, EV_MSC, MSC_SERIAL, wacom->serial[0]);
1231+
input_report_abs(pen_input, ABS_MISC,
1232+
wacom_intuos_id_mangle(wacom->id[0])); /* report tool id */
1233+
1234+
wacom->shared->stylus_in_proximity = frame[0] & 0x40;
1235+
1236+
input_sync(pen_input);
1237+
}
1238+
}
1239+
1240+
static void wacom_intuos_pro2_bt_touch(struct wacom_wac *wacom)
1241+
{
1242+
const int finger_touch_len = 8;
1243+
const int finger_frames = 4;
1244+
const int finger_frame_len = 43;
1245+
1246+
struct input_dev *touch_input = wacom->touch_input;
1247+
unsigned char *data = wacom->data;
1248+
int num_contacts_left = 5;
1249+
int i, j;
1250+
1251+
for (i = 0; i < finger_frames; i++) {
1252+
unsigned char *frame = &data[i*finger_frame_len + 109];
1253+
int current_num_contacts = frame[0] & 0x7F;
1254+
int contacts_to_send;
1255+
1256+
if (!(frame[0] & 0x80))
1257+
continue;
1258+
1259+
/*
1260+
* First packet resets the counter since only the first
1261+
* packet in series will have non-zero current_num_contacts.
1262+
*/
1263+
if (current_num_contacts)
1264+
wacom->num_contacts_left = current_num_contacts;
1265+
1266+
contacts_to_send = min(num_contacts_left, wacom->num_contacts_left);
1267+
1268+
for (j = 0; j < contacts_to_send; j++) {
1269+
unsigned char *touch = &frame[j*finger_touch_len + 1];
1270+
int slot = input_mt_get_slot_by_key(touch_input, touch[0]);
1271+
int x = get_unaligned_le16(&touch[2]);
1272+
int y = get_unaligned_le16(&touch[4]);
1273+
int w = touch[6] * input_abs_get_res(touch_input, ABS_MT_POSITION_X);
1274+
int h = touch[7] * input_abs_get_res(touch_input, ABS_MT_POSITION_Y);
1275+
1276+
if (slot < 0)
1277+
continue;
1278+
1279+
input_mt_slot(touch_input, slot);
1280+
input_mt_report_slot_state(touch_input, MT_TOOL_FINGER, touch[1] & 0x01);
1281+
input_report_abs(touch_input, ABS_MT_POSITION_X, x);
1282+
input_report_abs(touch_input, ABS_MT_POSITION_Y, y);
1283+
input_report_abs(touch_input, ABS_MT_TOUCH_MAJOR, max(w, h));
1284+
input_report_abs(touch_input, ABS_MT_TOUCH_MINOR, min(w, h));
1285+
input_report_abs(touch_input, ABS_MT_ORIENTATION, w > h);
1286+
}
1287+
1288+
input_mt_sync_frame(touch_input);
1289+
1290+
wacom->num_contacts_left -= contacts_to_send;
1291+
if (wacom->num_contacts_left <= 0) {
1292+
wacom->num_contacts_left = 0;
1293+
wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
1294+
}
1295+
}
1296+
1297+
input_report_switch(touch_input, SW_MUTE_DEVICE, !(data[281] >> 7));
1298+
input_sync(touch_input);
1299+
}
1300+
1301+
static void wacom_intuos_pro2_bt_pad(struct wacom_wac *wacom)
1302+
{
1303+
struct input_dev *pad_input = wacom->pad_input;
1304+
unsigned char *data = wacom->data;
1305+
1306+
int buttons = (data[282] << 1) | ((data[281] >> 6) & 0x01);
1307+
int ring = data[285];
1308+
int prox = buttons | (ring & 0x80);
1309+
1310+
wacom_report_numbered_buttons(pad_input, 9, buttons);
1311+
1312+
input_report_abs(pad_input, ABS_WHEEL, (ring & 0x80) ? (ring & 0x7f) : 0);
1313+
1314+
input_report_key(pad_input, wacom->tool[1], prox ? 1 : 0);
1315+
input_report_abs(pad_input, ABS_MISC, prox ? PAD_DEVICE_ID : 0);
1316+
input_event(pad_input, EV_MSC, MSC_SERIAL, 0xffffffff);
1317+
1318+
input_sync(pad_input);
1319+
}
1320+
1321+
static void wacom_intuos_pro2_bt_battery(struct wacom_wac *wacom)
1322+
{
1323+
unsigned char *data = wacom->data;
1324+
1325+
bool chg = data[284] & 0x80;
1326+
int battery_status = data[284] & 0x7F;
1327+
1328+
wacom_notify_battery(wacom, battery_status, chg, 1, chg);
1329+
}
1330+
1331+
static int wacom_intuos_pro2_bt_irq(struct wacom_wac *wacom, size_t len)
1332+
{
1333+
unsigned char *data = wacom->data;
1334+
1335+
if (data[0] != 0x80) {
1336+
dev_dbg(wacom->pen_input->dev.parent,
1337+
"%s: received unknown report #%d\n", __func__, data[0]);
1338+
return 0;
1339+
}
1340+
1341+
wacom_intuos_pro2_bt_pen(wacom);
1342+
wacom_intuos_pro2_bt_touch(wacom);
1343+
wacom_intuos_pro2_bt_pad(wacom);
1344+
wacom_intuos_pro2_bt_battery(wacom);
1345+
return 0;
1346+
}
1347+
11931348
static int wacom_24hdt_irq(struct wacom_wac *wacom)
11941349
{
11951350
struct input_dev *input = wacom->touch_input;
@@ -2667,6 +2822,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
26672822
sync = wacom_intuos_irq(wacom_wac);
26682823
break;
26692824

2825+
case INTUOSP2_BT:
2826+
sync = wacom_intuos_pro2_bt_irq(wacom_wac, len);
2827+
break;
2828+
26702829
case TABLETPC:
26712830
case TABLETPCE:
26722831
case TABLETPC2FG:
@@ -2838,6 +2997,13 @@ void wacom_setup_device_quirks(struct wacom *wacom)
28382997
if (features->type == REMOTE)
28392998
features->device_type = WACOM_DEVICETYPE_PAD;
28402999

3000+
if (features->type == INTUOSP2_BT) {
3001+
features->device_type |= WACOM_DEVICETYPE_PEN |
3002+
WACOM_DEVICETYPE_PAD |
3003+
WACOM_DEVICETYPE_TOUCH;
3004+
features->quirks |= WACOM_QUIRK_BATTERY;
3005+
}
3006+
28413007
switch (features->type) {
28423008
case PL:
28433009
case DTU:
@@ -2984,6 +3150,7 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
29843150
case INTUOSPL:
29853151
case INTUOS5S:
29863152
case INTUOSPS:
3153+
case INTUOSP2_BT:
29873154
input_set_abs_params(input_dev, ABS_DISTANCE, 0,
29883155
features->distance_max,
29893156
features->distance_fuzz, 0);
@@ -3092,6 +3259,27 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
30923259
}
30933260

30943261
switch (features->type) {
3262+
case INTUOSP2_BT:
3263+
input_dev->evbit[0] |= BIT_MASK(EV_SW);
3264+
__set_bit(SW_MUTE_DEVICE, input_dev->swbit);
3265+
3266+
if (wacom_wac->shared->touch->product == 0x361) {
3267+
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
3268+
0, 12440, 4, 0);
3269+
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
3270+
0, 8640, 4, 0);
3271+
}
3272+
else if (wacom_wac->shared->touch->product == 0x360) {
3273+
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
3274+
0, 8960, 4, 0);
3275+
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
3276+
0, 5920, 4, 0);
3277+
}
3278+
input_abs_set_res(input_dev, ABS_MT_POSITION_X, 40);
3279+
input_abs_set_res(input_dev, ABS_MT_POSITION_X, 40);
3280+
3281+
/* fall through */
3282+
30953283
case INTUOS5:
30963284
case INTUOS5L:
30973285
case INTUOSPM:
@@ -3389,6 +3577,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
33893577
case INTUOSPL:
33903578
case INTUOS5S:
33913579
case INTUOSPS:
3580+
case INTUOSP2_BT:
33923581
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
33933582
break;
33943583

@@ -3947,6 +4136,12 @@ static const struct wacom_features wacom_features_0x343 =
39474136
DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4,
39484137
WACOM_DTU_OFFSET, WACOM_DTU_OFFSET,
39494138
WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
4139+
static const struct wacom_features wacom_features_0x360 =
4140+
{ "Wacom Intuos Pro M", 44800, 29600, 8191, 63,
4141+
INTUOSP2_BT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 9, .touch_max = 10 };
4142+
static const struct wacom_features wacom_features_0x361 =
4143+
{ "Wacom Intuos Pro L", 62200, 43200, 8191, 63,
4144+
INTUOSP2_BT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 9, .touch_max = 10 };
39504145

39514146
static const struct wacom_features wacom_features_HID_ANY_ID =
39524147
{ "Wacom HID", .type = HID_GENERIC, .oVid = HID_ANY_ID, .oPid = HID_ANY_ID };
@@ -4113,6 +4308,8 @@ const struct hid_device_id wacom_ids[] = {
41134308
{ USB_DEVICE_WACOM(0x33D) },
41144309
{ USB_DEVICE_WACOM(0x33E) },
41154310
{ USB_DEVICE_WACOM(0x343) },
4311+
{ BT_DEVICE_WACOM(0x360) },
4312+
{ BT_DEVICE_WACOM(0x361) },
41164313
{ USB_DEVICE_WACOM(0x4001) },
41174314
{ USB_DEVICE_WACOM(0x4004) },
41184315
{ USB_DEVICE_WACOM(0x5000) },

drivers/hid/wacom_wac.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
#include <linux/types.h>
1313
#include <linux/hid.h>
1414

15-
/* maximum packet length for USB devices */
16-
#define WACOM_PKGLEN_MAX 192
15+
/* maximum packet length for USB/BT devices */
16+
#define WACOM_PKGLEN_MAX 361
1717

1818
#define WACOM_NAME_MAX 64
1919
#define WACOM_MAX_REMOTES 5
@@ -80,6 +80,7 @@
8080
#define WAC_CMD_ICON_BT_XFER 0x26
8181
#define WAC_CMD_DELETE_PAIRING 0x20
8282
#define WAC_CMD_UNPAIR_ALL 0xFF
83+
#define WAC_CMD_WL_INTUOSP2 0x82
8384

8485
/* device quirks */
8586
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0001
@@ -179,6 +180,7 @@ enum {
179180
INTUOSPS,
180181
INTUOSPM,
181182
INTUOSPL,
183+
INTUOSP2_BT,
182184
WACOM_21UX2,
183185
WACOM_22HD,
184186
DTK,

0 commit comments

Comments
 (0)