• R/O
  • HTTP
  • SSH
  • HTTPS

提交

標籤
無標籤

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

device/generic/x86


Commit MetaInfo

修訂4721c4db1245134babbeac866bfe5d002e63d9ca (tree)
時間2014-06-04 10:24:41
作者twisteroid ambassador <twisteroid.ambassador@gmai...>
CommiterChih-Wei Huang

Log Message

android_x86: eliminate device/ibm/thinkpad

The code is moved from device/ibm/thinkpad.

Change Summary

差異

--- /dev/null
+++ b/tablet-mode/Android.mk
@@ -0,0 +1,21 @@
1+# Copyright 2012 The Android-x86 Open Source Project
2+
3+LOCAL_PATH := $(call my-dir)
4+include $(CLEAR_VARS)
5+
6+LOCAL_SRC_FILES:= tablet-mode.c
7+
8+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
9+LOCAL_SHARED_LIBRARIES := liblog
10+
11+LOCAL_CFLAGS := -O2 -Wall
12+
13+ifeq ($(TARGET_ARCH),x86)
14+LOCAL_CFLAGS += -Ulinux
15+endif
16+
17+LOCAL_MODULE := tablet-mode
18+LOCAL_MODULE_TAGS := optional
19+#LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
20+
21+include $(BUILD_EXECUTABLE)
--- /dev/null
+++ b/tablet-mode/Makefile
@@ -0,0 +1,16 @@
1+CC=gcc
2+CFLAGS=-c -Wall -O2
3+SOURCES=tablet-mode.c
4+OBJECTS=$(SOURCES:.c=.o)
5+EXECUTABLE=tablet-mode
6+
7+all: $(SOURCES) $(EXECUTABLE)
8+
9+$(EXECUTABLE): $(OBJECTS)
10+ $(CC) $(LDFLAGS) $(OBJECTS) -o $@
11+
12+.c.o:
13+ $(CC) $(CFLAGS) $< -o $@
14+
15+clean:
16+ rm $(OBJECTS) $(EXECUTABLE)
--- /dev/null
+++ b/tablet-mode/tablet-mode.c
@@ -0,0 +1,125 @@
1+/**
2+ * Convert SW_TABLET_MODE events to SW_LID events for Android
3+ *
4+ * Copyright 2012 The Android-x86 Open Source Project
5+ *
6+ * Author: Stefan Seidel <android@stefanseidel.info>
7+ *
8+ * Licensed under GPLv2 or later
9+ *
10+ **/
11+
12+#define LOG_TAG "tablet-mode"
13+
14+#include <sys/stat.h>
15+#include <poll.h>
16+#include <fcntl.h>
17+#include <errno.h>
18+#include <dirent.h>
19+#include <cutils/log.h>
20+#include <linux/input.h>
21+#include <linux/uinput.h>
22+
23+/* we must use this kernel-compatible implementation */
24+#define BITS_PER_LONG (sizeof(long) * 8)
25+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
26+#define OFF(x) ((x)%BITS_PER_LONG)
27+#define BIT(x) (1UL<<OFF(x))
28+#define LONG(x) ((x)/BITS_PER_LONG)
29+#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
30+
31+int openfd(void)
32+{
33+ int fd;
34+ const char *dirname = "/dev/input";
35+ DIR *dir;
36+ if ((dir = opendir(dirname))) {
37+ struct dirent *de;
38+ unsigned long caps[NBITS(SW_TABLET_MODE+1)];
39+ while ((de = readdir(dir))) {
40+ if (de->d_name[0] != 'e') // eventX
41+ continue;
42+ char name[PATH_MAX];
43+ snprintf(name, PATH_MAX, "%s/%s", dirname, de->d_name);
44+ fd = open(name, O_RDONLY);
45+ if (fd < 0) {
46+ ALOGE("could not open %s, %s", name, strerror(errno));
47+ continue;
48+ }
49+ memset(caps, 0, sizeof(caps));
50+ if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(caps)), caps) < 1) {
51+ ALOGE("could not get device caps for %s, %s\n", name, strerror(errno));
52+ continue;
53+ }
54+ if (test_bit(SW_TABLET_MODE, caps)) {
55+ ALOGI("open %s(%s) ok", de->d_name, name);
56+ return fd;
57+ }
58+ close(fd);
59+ }
60+ closedir(dir);
61+ }
62+ return -1;
63+}
64+
65+void send_switch(int ufd, int state) {
66+ struct input_event nev;
67+ ALOGI("Tablet Mode Switch to %d\n", state);
68+ memset(&nev, 0, sizeof(struct input_event));
69+ nev.type = EV_SW;
70+ nev.code = SW_LID;
71+ nev.value = !!state;
72+ write(ufd, &nev, sizeof(struct input_event));
73+ nev.type = EV_SYN;
74+ nev.code = SYN_REPORT;
75+ nev.value = 0;
76+ write(ufd, &nev, sizeof(struct input_event));
77+}
78+
79+int main(void)
80+{
81+ int ifd = openfd();
82+ if (ifd < 0) {
83+ ALOGE("could not find any tablet mode switch, exiting.");
84+ return -1;
85+ }
86+
87+ sleep(10); //wait some time or otherwise EventHub might not pick up our events correctly!?
88+
89+ int ufd = open("/dev/uinput", O_WRONLY | O_NDELAY);
90+ if (ufd >= 0) {
91+ struct uinput_user_dev ud;
92+ memset(&ud, 0, sizeof(struct uinput_user_dev));
93+ strcpy(ud.name, "Android Tablet Lid Switch");
94+ write(ufd, &ud, sizeof(struct uinput_user_dev));
95+ ioctl(ufd, UI_SET_EVBIT, EV_SW);
96+ ioctl(ufd, UI_SET_SWBIT, SW_LID);
97+ ioctl(ufd, UI_DEV_CREATE, 0);
98+ } else {
99+ ALOGE("could not open uinput device: %s", strerror(errno));
100+ return -1;
101+ }
102+
103+ // send initial switch state
104+ unsigned long sw_state[NBITS(SW_TABLET_MODE+1)];
105+ memset(sw_state, 0, sizeof(sw_state));
106+ if (ioctl(ifd, EVIOCGSW(sizeof(sw_state)), sw_state) >= 0) {
107+ send_switch(ufd, test_bit(SW_TABLET_MODE, sw_state) ? 1 : 0);
108+ }
109+
110+ // read events and pass them on modified
111+ while (1) {
112+ struct input_event iev;
113+ size_t res = read(ifd, &iev, sizeof(struct input_event));
114+ if (res < sizeof(struct input_event)) {
115+ ALOGW("insufficient input data(%d)? fd=%d", res, ifd);
116+ continue;
117+ }
118+ //LOGV("type=%d scancode=%d value=%d from fd=%d", iev.type, iev.code, iev.value, ifd);
119+ if (iev.type == EV_SW && iev.code == SW_TABLET_MODE) {
120+ send_switch(ufd, iev.value);
121+ }
122+ }
123+
124+ return 0;
125+}
--- /dev/null
+++ b/tp_smapi/Android.mk
@@ -0,0 +1,15 @@
1+#
2+# Copyright (C) 2009-2011 The Android-x86 Open Source Project
3+#
4+# Licensed under the Apache License, Version 2.0 (the "License");
5+# you may not use this file except in compliance with the License.
6+# You may obtain a copy of the License at
7+#
8+# http://www.apache.org/licenses/LICENSE-2.0
9+#
10+
11+LOCAL_PATH := $(my-dir)
12+include $(CLEAR_VARS)
13+
14+LOCAL_MODULE := tp_smapi
15+EXTRA_KERNEL_MODULE_PATH_$(LOCAL_MODULE) := $(LOCAL_PATH)
--- /dev/null
+++ b/tp_smapi/Makefile
@@ -0,0 +1 @@
1+obj-m += thinkpad_ec.o tp_smapi.o hdaps.o
--- /dev/null
+++ b/tp_smapi/hdaps.c
@@ -0,0 +1,891 @@
1+/*
2+ * drivers/platform/x86/hdaps.c - driver for IBM's Hard Drive Active Protection System
3+ *
4+ * Copyright (C) 2005 Robert Love <rml@novell.com>
5+ * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>
6+ *
7+ * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads
8+ * starting with the R40, T41, and X40. It provides a basic two-axis
9+ * accelerometer and other data, such as the device's temperature.
10+ *
11+ * This driver is based on the document by Mark A. Smith available at
12+ * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial
13+ * and error.
14+ *
15+ * This program is free software; you can redistribute it and/or modify it
16+ * under the terms of the GNU General Public License v2 as published by the
17+ * Free Software Foundation.
18+ *
19+ * This program is distributed in the hope that it will be useful, but WITHOUT
20+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22+ * more details.
23+ *
24+ * You should have received a copy of the GNU General Public License along with
25+ * this program; if not, write to the Free Software Foundation, Inc.,
26+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27+ */
28+
29+#include <linux/delay.h>
30+#include <linux/platform_device.h>
31+#include <linux/input.h>
32+#include <linux/kernel.h>
33+#include <linux/module.h>
34+#include <linux/timer.h>
35+#include <linux/dmi.h>
36+#include <linux/jiffies.h>
37+#include "thinkpad_ec.h"
38+#include <linux/pci_ids.h>
39+#include <linux/version.h>
40+
41+/* Embedded controller accelerometer read command and its result: */
42+static const struct thinkpad_ec_row ec_accel_args =
43+ { .mask = 0x0001, .val = {0x11} };
44+#define EC_ACCEL_IDX_READOUTS 0x1 /* readouts included in this read */
45+ /* First readout, if READOUTS>=1: */
46+#define EC_ACCEL_IDX_YPOS1 0x2 /* y-axis position word */
47+#define EC_ACCEL_IDX_XPOS1 0x4 /* x-axis position word */
48+#define EC_ACCEL_IDX_TEMP1 0x6 /* device temperature in Celsius */
49+ /* Second readout, if READOUTS>=2: */
50+#define EC_ACCEL_IDX_XPOS2 0x7 /* y-axis position word */
51+#define EC_ACCEL_IDX_YPOS2 0x9 /* x-axis position word */
52+#define EC_ACCEL_IDX_TEMP2 0xb /* device temperature in Celsius */
53+#define EC_ACCEL_IDX_QUEUED 0xc /* Number of queued readouts left */
54+#define EC_ACCEL_IDX_KMACT 0xd /* keyboard or mouse activity */
55+#define EC_ACCEL_IDX_RETVAL 0xf /* command return value, good=0x00 */
56+
57+#define KEYBD_MASK 0x20 /* set if keyboard activity */
58+#define MOUSE_MASK 0x40 /* set if mouse activity */
59+
60+#define READ_TIMEOUT_MSECS 100 /* wait this long for device read */
61+#define RETRY_MSECS 3 /* retry delay */
62+
63+#define HDAPS_INPUT_FUZZ 4 /* input event threshold */
64+#define HDAPS_INPUT_FLAT 4
65+#define KMACT_REMEMBER_PERIOD (HZ/10) /* keyboard/mouse persistance */
66+
67+/* Input IDs */
68+#define HDAPS_INPUT_VENDOR PCI_VENDOR_ID_IBM
69+#define HDAPS_INPUT_PRODUCT 0x5054 /* "TP", shared with thinkpad_acpi */
70+#define HDAPS_INPUT_JS_VERSION 0x6801 /* Joystick emulation input device */
71+#define HDAPS_INPUT_RAW_VERSION 0x4801 /* Raw accelerometer input device */
72+
73+/* Axis orientation. */
74+/* The unnatural bit-representation of inversions is for backward
75+ * compatibility with the"invert=1" module parameter. */
76+#define HDAPS_ORIENT_INVERT_XY 0x01 /* Invert both X and Y axes. */
77+#define HDAPS_ORIENT_INVERT_X 0x02 /* Invert the X axis (uninvert if
78+ * already inverted by INVERT_XY). */
79+#define HDAPS_ORIENT_SWAP 0x04 /* Swap the axes. The swap occurs
80+ * before inverting X or Y. */
81+#define HDAPS_ORIENT_MAX 0x07
82+#define HDAPS_ORIENT_UNDEFINED 0xFF /* Placeholder during initialization */
83+#define HDAPS_ORIENT_INVERT_Y (HDAPS_ORIENT_INVERT_XY | HDAPS_ORIENT_INVERT_X)
84+
85+static struct timer_list hdaps_timer;
86+static struct platform_device *pdev;
87+static struct input_dev *hdaps_idev; /* joystick-like device with fuzz */
88+static struct input_dev *hdaps_idev_raw; /* raw hdaps sensor readouts */
89+static unsigned int hdaps_invert = HDAPS_ORIENT_UNDEFINED;
90+static int needs_calibration;
91+
92+/* Configuration: */
93+static int sampling_rate = 50; /* Sampling rate */
94+static int oversampling_ratio = 5; /* Ratio between our sampling rate and
95+ * EC accelerometer sampling rate */
96+static int running_avg_filter_order = 2; /* EC running average filter order */
97+
98+/* Latest state readout: */
99+static int pos_x, pos_y; /* position */
100+static int temperature; /* temperature */
101+static int stale_readout = 1; /* last read invalid */
102+static int rest_x, rest_y; /* calibrated rest position */
103+
104+/* Last time we saw keyboard and mouse activity: */
105+static u64 last_keyboard_jiffies = INITIAL_JIFFIES;
106+static u64 last_mouse_jiffies = INITIAL_JIFFIES;
107+static u64 last_update_jiffies = INITIAL_JIFFIES;
108+
109+/* input device use count */
110+static int hdaps_users;
111+static DEFINE_MUTEX(hdaps_users_mtx);
112+
113+/* Some models require an axis transformation to the standard representation */
114+static void transform_axes(int *x, int *y)
115+{
116+ if (hdaps_invert & HDAPS_ORIENT_SWAP) {
117+ int z;
118+ z = *x;
119+ *x = *y;
120+ *y = z;
121+ }
122+ if (hdaps_invert & HDAPS_ORIENT_INVERT_XY) {
123+ *x = -*x;
124+ *y = -*y;
125+ }
126+ if (hdaps_invert & HDAPS_ORIENT_INVERT_X)
127+ *x = -*x;
128+}
129+
130+/**
131+ * __hdaps_update - query current state, with locks already acquired
132+ * @fast: if nonzero, do one quick attempt without retries.
133+ *
134+ * Query current accelerometer state and update global state variables.
135+ * Also prefetches the next query. Caller must hold controller lock.
136+ */
137+static int __hdaps_update(int fast)
138+{
139+ /* Read data: */
140+ struct thinkpad_ec_row data;
141+ int ret;
142+
143+ data.mask = (1 << EC_ACCEL_IDX_READOUTS) | (1 << EC_ACCEL_IDX_KMACT) |
144+ (3 << EC_ACCEL_IDX_YPOS1) | (3 << EC_ACCEL_IDX_XPOS1) |
145+ (1 << EC_ACCEL_IDX_TEMP1) | (1 << EC_ACCEL_IDX_RETVAL);
146+ if (fast)
147+ ret = thinkpad_ec_try_read_row(&ec_accel_args, &data);
148+ else
149+ ret = thinkpad_ec_read_row(&ec_accel_args, &data);
150+ thinkpad_ec_prefetch_row(&ec_accel_args); /* Prefetch even if error */
151+ if (ret)
152+ return ret;
153+
154+ /* Check status: */
155+ if (data.val[EC_ACCEL_IDX_RETVAL] != 0x00) {
156+ printk(KERN_WARNING "hdaps: read RETVAL=0x%02x\n",
157+ data.val[EC_ACCEL_IDX_RETVAL]);
158+ return -EIO;
159+ }
160+
161+ if (data.val[EC_ACCEL_IDX_READOUTS] < 1)
162+ return -EBUSY; /* no pending readout, try again later */
163+
164+ /* Parse position data: */
165+ pos_x = *(s16 *)(data.val+EC_ACCEL_IDX_XPOS1);
166+ pos_y = *(s16 *)(data.val+EC_ACCEL_IDX_YPOS1);
167+ transform_axes(&pos_x, &pos_y);
168+
169+ /* Keyboard and mouse activity status is cleared as soon as it's read,
170+ * so applications will eat each other's events. Thus we remember any
171+ * event for KMACT_REMEMBER_PERIOD jiffies.
172+ */
173+ if (data.val[EC_ACCEL_IDX_KMACT] & KEYBD_MASK)
174+ last_keyboard_jiffies = get_jiffies_64();
175+ if (data.val[EC_ACCEL_IDX_KMACT] & MOUSE_MASK)
176+ last_mouse_jiffies = get_jiffies_64();
177+
178+ temperature = data.val[EC_ACCEL_IDX_TEMP1];
179+
180+ last_update_jiffies = get_jiffies_64();
181+ stale_readout = 0;
182+ if (needs_calibration) {
183+ rest_x = pos_x;
184+ rest_y = pos_y;
185+ needs_calibration = 0;
186+ }
187+
188+ return 0;
189+}
190+
191+/**
192+ * hdaps_update - acquire locks and query current state
193+ *
194+ * Query current accelerometer state and update global state variables.
195+ * Also prefetches the next query.
196+ * Retries until timeout if the accelerometer is not in ready status (common).
197+ * Does its own locking.
198+ */
199+static int hdaps_update(void)
200+{
201+ u64 age = get_jiffies_64() - last_update_jiffies;
202+ int total, ret;
203+
204+ if (!stale_readout && age < (9*HZ)/(10*sampling_rate))
205+ return 0; /* already updated recently */
206+ for (total = 0; total < READ_TIMEOUT_MSECS; total += RETRY_MSECS) {
207+ ret = thinkpad_ec_lock();
208+ if (ret)
209+ return ret;
210+ ret = __hdaps_update(0);
211+ thinkpad_ec_unlock();
212+
213+ if (!ret)
214+ return 0;
215+ if (ret != -EBUSY)
216+ break;
217+ msleep(RETRY_MSECS);
218+ }
219+ return ret;
220+}
221+
222+/**
223+ * hdaps_set_power - enable or disable power to the accelerometer.
224+ * Returns zero on success and negative error code on failure. Can sleep.
225+ */
226+static int hdaps_set_power(int on)
227+{
228+ struct thinkpad_ec_row args =
229+ { .mask = 0x0003, .val = {0x14, on?0x01:0x00} };
230+ struct thinkpad_ec_row data = { .mask = 0x8000 };
231+ int ret = thinkpad_ec_read_row(&args, &data);
232+ if (ret)
233+ return ret;
234+ if (data.val[0xF] != 0x00)
235+ return -EIO;
236+ return 0;
237+}
238+
239+/**
240+ * hdaps_set_ec_config - set accelerometer parameters.
241+ * @ec_rate: embedded controller sampling rate
242+ * @order: embedded controller running average filter order
243+ * (Normally we have @ec_rate = sampling_rate * oversampling_ratio.)
244+ * Returns zero on success and negative error code on failure. Can sleep.
245+ */
246+static int hdaps_set_ec_config(int ec_rate, int order)
247+{
248+ struct thinkpad_ec_row args = { .mask = 0x000F,
249+ .val = {0x10, (u8)ec_rate, (u8)(ec_rate>>8), order} };
250+ struct thinkpad_ec_row data = { .mask = 0x8000 };
251+ int ret = thinkpad_ec_read_row(&args, &data);
252+ printk(KERN_DEBUG "hdaps: setting ec_rate=%d, filter_order=%d\n",
253+ ec_rate, order);
254+ if (ret)
255+ return ret;
256+ if (data.val[0xF] == 0x03) {
257+ printk(KERN_WARNING "hdaps: config param out of range\n");
258+ return -EINVAL;
259+ }
260+ if (data.val[0xF] == 0x06) {
261+ printk(KERN_WARNING "hdaps: config change already pending\n");
262+ return -EBUSY;
263+ }
264+ if (data.val[0xF] != 0x00) {
265+ printk(KERN_WARNING "hdaps: config change error, ret=%d\n",
266+ data.val[0xF]);
267+ return -EIO;
268+ }
269+ return 0;
270+}
271+
272+/**
273+ * hdaps_get_ec_config - get accelerometer parameters.
274+ * @ec_rate: embedded controller sampling rate
275+ * @order: embedded controller running average filter order
276+ * Returns zero on success and negative error code on failure. Can sleep.
277+ */
278+static int hdaps_get_ec_config(int *ec_rate, int *order)
279+{
280+ const struct thinkpad_ec_row args =
281+ { .mask = 0x0003, .val = {0x17, 0x82} };
282+ struct thinkpad_ec_row data = { .mask = 0x801F };
283+ int ret = thinkpad_ec_read_row(&args, &data);
284+ if (ret)
285+ return ret;
286+ if (data.val[0xF] != 0x00)
287+ return -EIO;
288+ if (!(data.val[0x1] & 0x01))
289+ return -ENXIO; /* accelerometer polling not enabled */
290+ if (data.val[0x1] & 0x02)
291+ return -EBUSY; /* config change in progress, retry later */
292+ *ec_rate = data.val[0x2] | ((int)(data.val[0x3]) << 8);
293+ *order = data.val[0x4];
294+ return 0;
295+}
296+
297+/**
298+ * hdaps_get_ec_mode - get EC accelerometer mode
299+ * Returns zero on success and negative error code on failure. Can sleep.
300+ */
301+static int hdaps_get_ec_mode(u8 *mode)
302+{
303+ const struct thinkpad_ec_row args =
304+ { .mask = 0x0001, .val = {0x13} };
305+ struct thinkpad_ec_row data = { .mask = 0x8002 };
306+ int ret = thinkpad_ec_read_row(&args, &data);
307+ if (ret)
308+ return ret;
309+ if (data.val[0xF] != 0x00) {
310+ printk(KERN_WARNING
311+ "accelerometer not implemented (0x%02x)\n",
312+ data.val[0xF]);
313+ return -EIO;
314+ }
315+ *mode = data.val[0x1];
316+ return 0;
317+}
318+
319+/**
320+ * hdaps_check_ec - checks something about the EC.
321+ * Follows the clean-room spec for HDAPS; we don't know what it means.
322+ * Returns zero on success and negative error code on failure. Can sleep.
323+ */
324+static int hdaps_check_ec(void)
325+{
326+ const struct thinkpad_ec_row args =
327+ { .mask = 0x0003, .val = {0x17, 0x81} };
328+ struct thinkpad_ec_row data = { .mask = 0x800E };
329+ int ret = thinkpad_ec_read_row(&args, &data);
330+ if (ret)
331+ return ret;
332+ if (!((data.val[0x1] == 0x00 && data.val[0x2] == 0x60) || /* cleanroom spec */
333+ (data.val[0x1] == 0x01 && data.val[0x2] == 0x00)) || /* seen on T61 */
334+ data.val[0x3] != 0x00 || data.val[0xF] != 0x00) {
335+ printk(KERN_WARNING
336+ "hdaps_check_ec: bad response (0x%x,0x%x,0x%x,0x%x)\n",
337+ data.val[0x1], data.val[0x2],
338+ data.val[0x3], data.val[0xF]);
339+ return -EIO;
340+ }
341+ return 0;
342+}
343+
344+/**
345+ * hdaps_device_init - initialize the accelerometer.
346+ *
347+ * Call several embedded controller functions to test and initialize the
348+ * accelerometer.
349+ * Returns zero on success and negative error code on failure. Can sleep.
350+ */
351+#define FAILED_INIT(msg) printk(KERN_ERR "hdaps init failed at: %s\n", msg)
352+static int hdaps_device_init(void)
353+{
354+ int ret;
355+ u8 mode;
356+
357+ ret = thinkpad_ec_lock();
358+ if (ret)
359+ return ret;
360+
361+ if (hdaps_get_ec_mode(&mode))
362+ { FAILED_INIT("hdaps_get_ec_mode failed"); goto bad; }
363+
364+ printk(KERN_DEBUG "hdaps: initial mode latch is 0x%02x\n", mode);
365+ if (mode == 0x00)
366+ { FAILED_INIT("accelerometer not available"); goto bad; }
367+
368+ if (hdaps_check_ec())
369+ { FAILED_INIT("hdaps_check_ec failed"); goto bad; }
370+
371+ if (hdaps_set_power(1))
372+ { FAILED_INIT("hdaps_set_power failed"); goto bad; }
373+
374+ if (hdaps_set_ec_config(sampling_rate*oversampling_ratio,
375+ running_avg_filter_order))
376+ { FAILED_INIT("hdaps_set_ec_config failed"); goto bad; }
377+
378+ thinkpad_ec_invalidate();
379+ udelay(200);
380+
381+ /* Just prefetch instead of reading, to avoid ~1sec delay on load */
382+ ret = thinkpad_ec_prefetch_row(&ec_accel_args);
383+ if (ret)
384+ { FAILED_INIT("initial prefetch failed"); goto bad; }
385+ goto good;
386+bad:
387+ thinkpad_ec_invalidate();
388+ ret = -ENXIO;
389+good:
390+ stale_readout = 1;
391+ thinkpad_ec_unlock();
392+ return ret;
393+}
394+
395+/**
396+ * hdaps_device_shutdown - power off the accelerometer
397+ * Returns nonzero on failure. Can sleep.
398+ */
399+static int hdaps_device_shutdown(void)
400+{
401+ int ret;
402+ ret = hdaps_set_power(0);
403+ if (ret) {
404+ printk(KERN_WARNING "hdaps: cannot power off\n");
405+ return ret;
406+ }
407+ ret = hdaps_set_ec_config(0, 1);
408+ if (ret)
409+ printk(KERN_WARNING "hdaps: cannot stop EC sampling\n");
410+ return ret;
411+}
412+
413+/* Device model stuff */
414+
415+static int hdaps_probe(struct platform_device *dev)
416+{
417+ int ret;
418+
419+ ret = hdaps_device_init();
420+ if (ret)
421+ return ret;
422+
423+ printk(KERN_INFO "hdaps: device successfully initialized.\n");
424+ return 0;
425+}
426+
427+static int hdaps_suspend(struct platform_device *dev, pm_message_t state)
428+{
429+ /* Don't do hdaps polls until resume re-initializes the sensor. */
430+ del_timer_sync(&hdaps_timer);
431+ hdaps_device_shutdown(); /* ignore errors, effect is negligible */
432+ return 0;
433+}
434+
435+static int hdaps_resume(struct platform_device *dev)
436+{
437+ int ret = hdaps_device_init();
438+ if (ret)
439+ return ret;
440+
441+ mutex_lock(&hdaps_users_mtx);
442+ if (hdaps_users)
443+ mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
444+ mutex_unlock(&hdaps_users_mtx);
445+ return 0;
446+}
447+
448+static struct platform_driver hdaps_driver = {
449+ .probe = hdaps_probe,
450+ .suspend = hdaps_suspend,
451+ .resume = hdaps_resume,
452+ .driver = {
453+ .name = "hdaps",
454+ .owner = THIS_MODULE,
455+ },
456+};
457+
458+/**
459+ * hdaps_calibrate - set our "resting" values.
460+ * Does its own locking.
461+ */
462+static void hdaps_calibrate(void)
463+{
464+ needs_calibration = 1;
465+ hdaps_update();
466+ /* If that fails, the mousedev poll will take care of things later. */
467+}
468+
469+/* Timer handler for updating the input device. Runs in softirq context,
470+ * so avoid lenghty or blocking operations.
471+ */
472+static void hdaps_mousedev_poll(unsigned long unused)
473+{
474+ int ret;
475+
476+ stale_readout = 1;
477+
478+ /* Cannot sleep. Try nonblockingly. If we fail, try again later. */
479+ if (thinkpad_ec_try_lock())
480+ goto keep_active;
481+
482+ ret = __hdaps_update(1); /* fast update, we're in softirq context */
483+ thinkpad_ec_unlock();
484+ /* Any of "successful", "not yet ready" and "not prefetched"? */
485+ if (ret != 0 && ret != -EBUSY && ret != -ENODATA) {
486+ printk(KERN_ERR
487+ "hdaps: poll failed, disabling updates\n");
488+ return;
489+ }
490+
491+keep_active:
492+ /* Even if we failed now, pos_x,y may have been updated earlier: */
493+ input_report_abs(hdaps_idev, ABS_X, pos_x - rest_x);
494+ input_report_abs(hdaps_idev, ABS_Y, pos_y - rest_y);
495+ input_sync(hdaps_idev);
496+ input_report_abs(hdaps_idev_raw, ABS_X, pos_x);
497+ input_report_abs(hdaps_idev_raw, ABS_Y, pos_y);
498+ input_sync(hdaps_idev_raw);
499+ mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
500+}
501+
502+
503+/* Sysfs Files */
504+
505+static ssize_t hdaps_position_show(struct device *dev,
506+ struct device_attribute *attr, char *buf)
507+{
508+ int ret = hdaps_update();
509+ if (ret)
510+ return ret;
511+ return sprintf(buf, "(%d,%d)\n", pos_x, pos_y);
512+}
513+
514+static ssize_t hdaps_temp1_show(struct device *dev,
515+ struct device_attribute *attr, char *buf)
516+{
517+ int ret = hdaps_update();
518+ if (ret)
519+ return ret;
520+ return sprintf(buf, "%d\n", temperature);
521+}
522+
523+static ssize_t hdaps_keyboard_activity_show(struct device *dev,
524+ struct device_attribute *attr,
525+ char *buf)
526+{
527+ int ret = hdaps_update();
528+ if (ret)
529+ return ret;
530+ return sprintf(buf, "%u\n",
531+ get_jiffies_64() < last_keyboard_jiffies + KMACT_REMEMBER_PERIOD);
532+}
533+
534+static ssize_t hdaps_mouse_activity_show(struct device *dev,
535+ struct device_attribute *attr,
536+ char *buf)
537+{
538+ int ret = hdaps_update();
539+ if (ret)
540+ return ret;
541+ return sprintf(buf, "%u\n",
542+ get_jiffies_64() < last_mouse_jiffies + KMACT_REMEMBER_PERIOD);
543+}
544+
545+static ssize_t hdaps_calibrate_show(struct device *dev,
546+ struct device_attribute *attr, char *buf)
547+{
548+ return sprintf(buf, "(%d,%d)\n", rest_x, rest_y);
549+}
550+
551+static ssize_t hdaps_calibrate_store(struct device *dev,
552+ struct device_attribute *attr,
553+ const char *buf, size_t count)
554+{
555+ hdaps_calibrate();
556+ return count;
557+}
558+
559+static ssize_t hdaps_invert_show(struct device *dev,
560+ struct device_attribute *attr, char *buf)
561+{
562+ return sprintf(buf, "%u\n", hdaps_invert);
563+}
564+
565+static ssize_t hdaps_invert_store(struct device *dev,
566+ struct device_attribute *attr,
567+ const char *buf, size_t count)
568+{
569+ int invert;
570+
571+ if (sscanf(buf, "%d", &invert) != 1 ||
572+ invert < 0 || invert > HDAPS_ORIENT_MAX)
573+ return -EINVAL;
574+
575+ hdaps_invert = invert;
576+ hdaps_calibrate();
577+
578+ return count;
579+}
580+
581+static ssize_t hdaps_sampling_rate_show(
582+ struct device *dev, struct device_attribute *attr, char *buf)
583+{
584+ return sprintf(buf, "%d\n", sampling_rate);
585+}
586+
587+static ssize_t hdaps_sampling_rate_store(
588+ struct device *dev, struct device_attribute *attr,
589+ const char *buf, size_t count)
590+{
591+ int rate, ret;
592+ if (sscanf(buf, "%d", &rate) != 1 || rate > HZ || rate <= 0) {
593+ printk(KERN_WARNING
594+ "must have 0<input_sampling_rate<=HZ=%d\n", HZ);
595+ return -EINVAL;
596+ }
597+ ret = hdaps_set_ec_config(rate*oversampling_ratio,
598+ running_avg_filter_order);
599+ if (ret)
600+ return ret;
601+ sampling_rate = rate;
602+ return count;
603+}
604+
605+static ssize_t hdaps_oversampling_ratio_show(
606+ struct device *dev, struct device_attribute *attr, char *buf)
607+{
608+ int ec_rate, order;
609+ int ret = hdaps_get_ec_config(&ec_rate, &order);
610+ if (ret)
611+ return ret;
612+ return sprintf(buf, "%u\n", ec_rate / sampling_rate);
613+}
614+
615+static ssize_t hdaps_oversampling_ratio_store(
616+ struct device *dev, struct device_attribute *attr,
617+ const char *buf, size_t count)
618+{
619+ int ratio, ret;
620+ if (sscanf(buf, "%d", &ratio) != 1 || ratio < 1)
621+ return -EINVAL;
622+ ret = hdaps_set_ec_config(sampling_rate*ratio,
623+ running_avg_filter_order);
624+ if (ret)
625+ return ret;
626+ oversampling_ratio = ratio;
627+ return count;
628+}
629+
630+static ssize_t hdaps_running_avg_filter_order_show(
631+ struct device *dev, struct device_attribute *attr, char *buf)
632+{
633+ int rate, order;
634+ int ret = hdaps_get_ec_config(&rate, &order);
635+ if (ret)
636+ return ret;
637+ return sprintf(buf, "%u\n", order);
638+}
639+
640+static ssize_t hdaps_running_avg_filter_order_store(
641+ struct device *dev, struct device_attribute *attr,
642+ const char *buf, size_t count)
643+{
644+ int order, ret;
645+ if (sscanf(buf, "%d", &order) != 1)
646+ return -EINVAL;
647+ ret = hdaps_set_ec_config(sampling_rate*oversampling_ratio, order);
648+ if (ret)
649+ return ret;
650+ running_avg_filter_order = order;
651+ return count;
652+}
653+
654+static int hdaps_mousedev_open(struct input_dev *dev)
655+{
656+ if (!try_module_get(THIS_MODULE))
657+ return -ENODEV;
658+
659+ mutex_lock(&hdaps_users_mtx);
660+ if (hdaps_users++ == 0) /* first input user */
661+ mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
662+ mutex_unlock(&hdaps_users_mtx);
663+ return 0;
664+}
665+
666+static void hdaps_mousedev_close(struct input_dev *dev)
667+{
668+ mutex_lock(&hdaps_users_mtx);
669+ if (--hdaps_users == 0) /* no input users left */
670+ del_timer_sync(&hdaps_timer);
671+ mutex_unlock(&hdaps_users_mtx);
672+
673+ module_put(THIS_MODULE);
674+}
675+
676+static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL);
677+static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL);
678+ /* "temp1" instead of "temperature" is hwmon convention */
679+static DEVICE_ATTR(keyboard_activity, 0444,
680+ hdaps_keyboard_activity_show, NULL);
681+static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL);
682+static DEVICE_ATTR(calibrate, 0644,
683+ hdaps_calibrate_show, hdaps_calibrate_store);
684+static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store);
685+static DEVICE_ATTR(sampling_rate, 0644,
686+ hdaps_sampling_rate_show, hdaps_sampling_rate_store);
687+static DEVICE_ATTR(oversampling_ratio, 0644,
688+ hdaps_oversampling_ratio_show,
689+ hdaps_oversampling_ratio_store);
690+static DEVICE_ATTR(running_avg_filter_order, 0644,
691+ hdaps_running_avg_filter_order_show,
692+ hdaps_running_avg_filter_order_store);
693+
694+static struct attribute *hdaps_attributes[] = {
695+ &dev_attr_position.attr,
696+ &dev_attr_temp1.attr,
697+ &dev_attr_keyboard_activity.attr,
698+ &dev_attr_mouse_activity.attr,
699+ &dev_attr_calibrate.attr,
700+ &dev_attr_invert.attr,
701+ &dev_attr_sampling_rate.attr,
702+ &dev_attr_oversampling_ratio.attr,
703+ &dev_attr_running_avg_filter_order.attr,
704+ NULL,
705+};
706+
707+static struct attribute_group hdaps_attribute_group = {
708+ .attrs = hdaps_attributes,
709+};
710+
711+
712+/* Module stuff */
713+
714+/* hdaps_dmi_match_invert - found an inverted match. */
715+static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id)
716+{
717+ unsigned int orient = (kernel_ulong_t) id->driver_data;
718+ hdaps_invert = orient;
719+ printk(KERN_INFO "hdaps: %s detected, setting orientation %u\n",
720+ id->ident, orient);
721+ return 1; /* stop enumeration */
722+}
723+
724+#define HDAPS_DMI_MATCH_INVERT(vendor, model, orient) { \
725+ .ident = vendor " " model, \
726+ .callback = hdaps_dmi_match_invert, \
727+ .driver_data = (void *)(orient), \
728+ .matches = { \
729+ DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
730+ DMI_MATCH(DMI_PRODUCT_VERSION, model) \
731+ } \
732+}
733+
734+/* List of models with abnormal axis configuration.
735+ Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
736+ "ThinkPad T42p", and enumeration stops after first match,
737+ so the order of the entries matters. */
738+struct dmi_system_id __initdata hdaps_whitelist[] = {
739+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_ORIENT_INVERT_XY),
740+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R60", HDAPS_ORIENT_INVERT_XY),
741+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_ORIENT_INVERT_XY),
742+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_ORIENT_INVERT_XY),
743+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X40", HDAPS_ORIENT_INVERT_Y),
744+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_ORIENT_INVERT_Y),
745+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R60", HDAPS_ORIENT_INVERT_XY),
746+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_ORIENT_INVERT_XY),
747+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_ORIENT_INVERT_XY),
748+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_ORIENT_INVERT_XY),
749+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60 Tablet", HDAPS_ORIENT_INVERT_Y),
750+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60s", HDAPS_ORIENT_INVERT_Y),
751+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X),
752+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X),
753+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400s", HDAPS_ORIENT_INVERT_X),
754+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_ORIENT_INVERT_XY),
755+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T410s", HDAPS_ORIENT_SWAP),
756+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T410", HDAPS_ORIENT_INVERT_XY),
757+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T500", HDAPS_ORIENT_INVERT_XY),
758+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad W51O", HDAPS_ORIENT_MAX),
759+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X200", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X | HDAPS_ORIENT_INVERT_Y),
760+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201 Tablet", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_XY),
761+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201s", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_XY),
762+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X),
763+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X220", HDAPS_ORIENT_SWAP),
764+ { .ident = NULL }
765+};
766+
767+static int __init hdaps_init(void)
768+{
769+ int ret;
770+
771+ /* Determine axis orientation orientation */
772+ if (hdaps_invert == HDAPS_ORIENT_UNDEFINED) /* set by module param? */
773+ if (dmi_check_system(hdaps_whitelist) < 1) /* in whitelist? */
774+ hdaps_invert = 0; /* default */
775+
776+ /* Init timer before platform_driver_register, in case of suspend */
777+ init_timer(&hdaps_timer);
778+ hdaps_timer.function = hdaps_mousedev_poll;
779+ ret = platform_driver_register(&hdaps_driver);
780+ if (ret)
781+ goto out;
782+
783+ pdev = platform_device_register_simple("hdaps", -1, NULL, 0);
784+ if (IS_ERR(pdev)) {
785+ ret = PTR_ERR(pdev);
786+ goto out_driver;
787+ }
788+
789+ ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group);
790+ if (ret)
791+ goto out_device;
792+
793+ hdaps_idev = input_allocate_device();
794+ if (!hdaps_idev) {
795+ ret = -ENOMEM;
796+ goto out_group;
797+ }
798+
799+ hdaps_idev_raw = input_allocate_device();
800+ if (!hdaps_idev_raw) {
801+ ret = -ENOMEM;
802+ goto out_idev_first;
803+ }
804+
805+ /* calibration for the input device (deferred to avoid delay) */
806+ needs_calibration = 1;
807+
808+ /* initialize the joystick-like fuzzed input device */
809+ hdaps_idev->name = "hdaps";
810+ hdaps_idev->phys = "hdaps/input0";
811+ hdaps_idev->id.bustype = BUS_HOST;
812+ hdaps_idev->id.vendor = HDAPS_INPUT_VENDOR;
813+ hdaps_idev->id.product = HDAPS_INPUT_PRODUCT;
814+ hdaps_idev->id.version = HDAPS_INPUT_JS_VERSION;
815+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
816+ hdaps_idev->cdev.dev = &pdev->dev;
817+#endif
818+ hdaps_idev->evbit[0] = BIT(EV_ABS);
819+ hdaps_idev->open = hdaps_mousedev_open;
820+ hdaps_idev->close = hdaps_mousedev_close;
821+ input_set_abs_params(hdaps_idev, ABS_X,
822+ -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
823+ input_set_abs_params(hdaps_idev, ABS_Y,
824+ -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
825+
826+ ret = input_register_device(hdaps_idev);
827+ if (ret)
828+ goto out_idev;
829+
830+ /* initialize the raw data input device */
831+ hdaps_idev_raw->name = "raw_hdaps_data";
832+ hdaps_idev_raw->phys = "hdaps/input1";
833+ hdaps_idev_raw->id.bustype = BUS_HOST;
834+ hdaps_idev_raw->id.vendor = HDAPS_INPUT_VENDOR;
835+ hdaps_idev_raw->id.product = HDAPS_INPUT_PRODUCT;
836+ hdaps_idev_raw->id.version = HDAPS_INPUT_RAW_VERSION;
837+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
838+ hdaps_idev_raw->cdev.dev = &pdev->dev;
839+#endif
840+ hdaps_idev_raw->evbit[0] = BIT(EV_ABS);
841+ hdaps_idev_raw->open = hdaps_mousedev_open;
842+ hdaps_idev_raw->close = hdaps_mousedev_close;
843+ input_set_abs_params(hdaps_idev_raw, ABS_X, -32768, 32767, 0, 0);
844+ input_set_abs_params(hdaps_idev_raw, ABS_Y, -32768, 32767, 0, 0);
845+
846+ ret = input_register_device(hdaps_idev_raw);
847+ if (ret)
848+ goto out_idev_reg_first;
849+
850+ printk(KERN_INFO "hdaps: driver successfully loaded.\n");
851+ return 0;
852+
853+out_idev_reg_first:
854+ input_unregister_device(hdaps_idev);
855+out_idev:
856+ input_free_device(hdaps_idev_raw);
857+out_idev_first:
858+ input_free_device(hdaps_idev);
859+out_group:
860+ sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
861+out_device:
862+ platform_device_unregister(pdev);
863+out_driver:
864+ platform_driver_unregister(&hdaps_driver);
865+ hdaps_device_shutdown();
866+out:
867+ printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret);
868+ return ret;
869+}
870+
871+static void __exit hdaps_exit(void)
872+{
873+ input_unregister_device(hdaps_idev_raw);
874+ input_unregister_device(hdaps_idev);
875+ hdaps_device_shutdown(); /* ignore errors, effect is negligible */
876+ sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
877+ platform_device_unregister(pdev);
878+ platform_driver_unregister(&hdaps_driver);
879+
880+ printk(KERN_INFO "hdaps: driver unloaded.\n");
881+}
882+
883+module_init(hdaps_init);
884+module_exit(hdaps_exit);
885+
886+module_param_named(invert, hdaps_invert, uint, 0);
887+MODULE_PARM_DESC(invert, "axis orientation code");
888+
889+MODULE_AUTHOR("Robert Love");
890+MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver");
891+MODULE_LICENSE("GPL v2");
--- /dev/null
+++ b/tp_smapi/thinkpad_ec.c
@@ -0,0 +1,513 @@
1+/*
2+ * thinkpad_ec.c - ThinkPad embedded controller LPC3 functions
3+ *
4+ * The embedded controller on ThinkPad laptops has a non-standard interface,
5+ * where LPC channel 3 of the H8S EC chip is hooked up to IO ports
6+ * 0x1600-0x161F and implements (a special case of) the H8S LPC protocol.
7+ * The EC LPC interface provides various system management services (currently
8+ * known: battery information and accelerometer readouts). This driver
9+ * provides access and mutual exclusion for the EC interface.
10+*
11+ * The LPC protocol and terminology are documented here:
12+ * "H8S/2104B Group Hardware Manual",
13+ * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
14+ *
15+ * Copyright (C) 2006-2007 Shem Multinymous <multinymous@gmail.com>
16+ *
17+ * This program is free software; you can redistribute it and/or modify
18+ * it under the terms of the GNU General Public License as published by
19+ * the Free Software Foundation; either version 2 of the License, or
20+ * (at your option) any later version.
21+ *
22+ * This program is distributed in the hope that it will be useful,
23+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
24+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25+ * GNU General Public License for more details.
26+ *
27+ * You should have received a copy of the GNU General Public License
28+ * along with this program; if not, write to the Free Software
29+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30+ */
31+
32+#include <linux/kernel.h>
33+#include <linux/module.h>
34+#include <linux/dmi.h>
35+#include <linux/ioport.h>
36+#include <linux/delay.h>
37+#include "thinkpad_ec.h"
38+#include <linux/jiffies.h>
39+#include <asm/io.h>
40+
41+#include <linux/version.h>
42+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
43+ #include <asm/semaphore.h>
44+#else
45+ #include <linux/semaphore.h>
46+#endif
47+
48+#define TP_VERSION "0.41"
49+
50+MODULE_AUTHOR("Shem Multinymous");
51+MODULE_DESCRIPTION("ThinkPad embedded controller hardware access");
52+MODULE_VERSION(TP_VERSION);
53+MODULE_LICENSE("GPL");
54+
55+/* IO ports used by embedded controller LPC channel 3: */
56+#define TPC_BASE_PORT 0x1600
57+#define TPC_NUM_PORTS 0x20
58+#define TPC_STR3_PORT 0x1604 /* Reads H8S EC register STR3 */
59+#define TPC_TWR0_PORT 0x1610 /* Mapped to H8S EC register TWR0MW/SW */
60+#define TPC_TWR15_PORT 0x161F /* Mapped to H8S EC register TWR15. */
61+ /* (and port TPC_TWR0_PORT+i is mapped to H8S reg TWRi for 0<i<16) */
62+
63+/* H8S STR3 status flags (see "H8S/2104B Group Hardware Manual" p.549) */
64+#define H8S_STR3_IBF3B 0x80 /* Bidi. Data Register Input Buffer Full */
65+#define H8S_STR3_OBF3B 0x40 /* Bidi. Data Register Output Buffer Full */
66+#define H8S_STR3_MWMF 0x20 /* Master Write Mode Flag */
67+#define H8S_STR3_SWMF 0x10 /* Slave Write Mode Flag */
68+#define H8S_STR3_MASK 0xF0 /* All bits we care about in STR3 */
69+
70+/* Timeouts and retries */
71+#define TPC_READ_RETRIES 150
72+#define TPC_READ_NDELAY 500
73+#define TPC_REQUEST_RETRIES 1000
74+#define TPC_REQUEST_NDELAY 10
75+#define TPC_PREFETCH_TIMEOUT (HZ/10) /* invalidate prefetch after 0.1sec */
76+
77+/* A few macros for printk()ing: */
78+#define MSG_FMT(fmt, args...) \
79+ "thinkpad_ec: %s: " fmt "\n", __func__, ## args
80+#define REQ_FMT(msg, code) \
81+ MSG_FMT("%s: (0x%02x:0x%02x)->0x%02x", \
82+ msg, args->val[0x0], args->val[0xF], code)
83+
84+/* State of request prefetching: */
85+static u8 prefetch_arg0, prefetch_argF; /* Args of last prefetch */
86+static u64 prefetch_jiffies; /* time of prefetch, or: */
87+#define TPC_PREFETCH_NONE INITIAL_JIFFIES /* No prefetch */
88+#define TPC_PREFETCH_JUNK (INITIAL_JIFFIES+1) /* Ignore prefetch */
89+
90+/* Locking: */
91+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
92+static DECLARE_MUTEX(thinkpad_ec_mutex);
93+#else
94+static DEFINE_SEMAPHORE(thinkpad_ec_mutex);
95+#endif
96+
97+/* Kludge in case the ACPI DSDT reserves the ports we need. */
98+static int force_io; /* Willing to do IO to ports we couldn't reserve? */
99+static int reserved_io; /* Successfully reserved the ports? */
100+module_param_named(force_io, force_io, bool, 0600);
101+MODULE_PARM_DESC(force_io, "Force IO even if region already reserved (0=off, 1=on)");
102+
103+/**
104+ * thinkpad_ec_lock - get lock on the ThinkPad EC
105+ *
106+ * Get exclusive lock for accesing the ThinkPad embedded controller LPC3
107+ * interface. Returns 0 iff lock acquired.
108+ */
109+int thinkpad_ec_lock(void)
110+{
111+ int ret;
112+ ret = down_interruptible(&thinkpad_ec_mutex);
113+ return ret;
114+}
115+EXPORT_SYMBOL_GPL(thinkpad_ec_lock);
116+
117+/**
118+ * thinkpad_ec_try_lock - try getting lock on the ThinkPad EC
119+ *
120+ * Try getting an exclusive lock for accesing the ThinkPad embedded
121+ * controller LPC3. Returns immediately if lock is not available; neither
122+ * blocks nor sleeps. Returns 0 iff lock acquired .
123+ */
124+int thinkpad_ec_try_lock(void)
125+{
126+ return down_trylock(&thinkpad_ec_mutex);
127+}
128+EXPORT_SYMBOL_GPL(thinkpad_ec_try_lock);
129+
130+/**
131+ * thinkpad_ec_unlock - release lock on ThinkPad EC
132+ *
133+ * Release a previously acquired exclusive lock on the ThinkPad ebmedded
134+ * controller LPC3 interface.
135+ */
136+void thinkpad_ec_unlock(void)
137+{
138+ up(&thinkpad_ec_mutex);
139+}
140+EXPORT_SYMBOL_GPL(thinkpad_ec_unlock);
141+
142+/**
143+ * thinkpad_ec_request_row - tell embedded controller to prepare a row
144+ * @args Input register arguments
145+ *
146+ * Requests a data row by writing to H8S LPC registers TRW0 through TWR15 (or
147+ * a subset thereof) following the protocol prescribed by the "H8S/2104B Group
148+ * Hardware Manual". Does sanity checks via status register STR3.
149+ */
150+static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
151+{
152+ u8 str3;
153+ int i;
154+
155+ /* EC protocol requires write to TWR0 (function code): */
156+ if (!(args->mask & 0x0001)) {
157+ printk(KERN_ERR MSG_FMT("bad args->mask=0x%02x", args->mask));
158+ return -EINVAL;
159+ }
160+
161+ /* Check initial STR3 status: */
162+ str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
163+ if (str3 & H8S_STR3_OBF3B) { /* data already pending */
164+ inb(TPC_TWR15_PORT); /* marks end of previous transaction */
165+ if (prefetch_jiffies == TPC_PREFETCH_NONE)
166+ printk(KERN_WARNING REQ_FMT(
167+ "EC has result from unrequested transaction",
168+ str3));
169+ return -EBUSY; /* EC will be ready in a few usecs */
170+ } else if (str3 == H8S_STR3_SWMF) { /* busy with previous request */
171+ if (prefetch_jiffies == TPC_PREFETCH_NONE)
172+ printk(KERN_WARNING REQ_FMT(
173+ "EC is busy with unrequested transaction",
174+ str3));
175+ return -EBUSY; /* data will be pending in a few usecs */
176+ } else if (str3 != 0x00) { /* unexpected status? */
177+ printk(KERN_WARNING REQ_FMT("unexpected initial STR3", str3));
178+ return -EIO;
179+ }
180+
181+ /* Send TWR0MW: */
182+ outb(args->val[0], TPC_TWR0_PORT);
183+ str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
184+ if (str3 != H8S_STR3_MWMF) { /* not accepted? */
185+ printk(KERN_WARNING REQ_FMT("arg0 rejected", str3));
186+ return -EIO;
187+ }
188+
189+ /* Send TWR1 through TWR14: */
190+ for (i = 1; i < TP_CONTROLLER_ROW_LEN-1; i++)
191+ if ((args->mask>>i)&1)
192+ outb(args->val[i], TPC_TWR0_PORT+i);
193+
194+ /* Send TWR15 (default to 0x01). This marks end of command. */
195+ outb((args->mask & 0x8000) ? args->val[0xF] : 0x01, TPC_TWR15_PORT);
196+
197+ /* Wait until EC starts writing its reply (~60ns on average).
198+ * Releasing locks before this happens may cause an EC hang
199+ * due to firmware bug!
200+ */
201+ for (i = 0; i < TPC_REQUEST_RETRIES; i++) {
202+ str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
203+ if (str3 & H8S_STR3_SWMF) /* EC started replying */
204+ return 0;
205+ else if (!(str3 & ~(H8S_STR3_IBF3B|H8S_STR3_MWMF)))
206+ /* Normal progress (the EC hasn't seen the request
207+ * yet, or is processing it). Wait it out. */
208+ ndelay(TPC_REQUEST_NDELAY);
209+ else { /* weird EC status */
210+ printk(KERN_WARNING
211+ REQ_FMT("bad end STR3", str3));
212+ return -EIO;
213+ }
214+ }
215+ printk(KERN_WARNING REQ_FMT("EC is mysteriously silent", str3));
216+ return -EIO;
217+}
218+
219+/**
220+ * thinkpad_ec_read_data - read pre-requested row-data from EC
221+ * @args Input register arguments of pre-requested rows
222+ * @data Output register values
223+ *
224+ * Reads current row data from the controller, assuming it's already
225+ * requested. Follows the H8S spec for register access and status checks.
226+ */
227+static int thinkpad_ec_read_data(const struct thinkpad_ec_row *args,
228+ struct thinkpad_ec_row *data)
229+{
230+ int i;
231+ u8 str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
232+ /* Once we make a request, STR3 assumes the sequence of values listed
233+ * in the following 'if' as it reads the request and writes its data.
234+ * It takes about a few dozen nanosecs total, with very high variance.
235+ */
236+ if (str3 == (H8S_STR3_IBF3B|H8S_STR3_MWMF) ||
237+ str3 == 0x00 || /* the 0x00 is indistinguishable from idle EC! */
238+ str3 == H8S_STR3_SWMF)
239+ return -EBUSY; /* not ready yet */
240+ /* Finally, the EC signals output buffer full: */
241+ if (str3 != (H8S_STR3_OBF3B|H8S_STR3_SWMF)) {
242+ printk(KERN_WARNING
243+ REQ_FMT("bad initial STR3", str3));
244+ return -EIO;
245+ }
246+
247+ /* Read first byte (signals start of read transactions): */
248+ data->val[0] = inb(TPC_TWR0_PORT);
249+ /* Optionally read 14 more bytes: */
250+ for (i = 1; i < TP_CONTROLLER_ROW_LEN-1; i++)
251+ if ((data->mask >> i)&1)
252+ data->val[i] = inb(TPC_TWR0_PORT+i);
253+ /* Read last byte from 0x161F (signals end of read transaction): */
254+ data->val[0xF] = inb(TPC_TWR15_PORT);
255+
256+ /* Readout still pending? */
257+ str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
258+ if (str3 & H8S_STR3_OBF3B)
259+ printk(KERN_WARNING
260+ REQ_FMT("OBF3B=1 after read", str3));
261+ /* If port 0x161F returns 0x80 too often, the EC may lock up. Warn: */
262+ if (data->val[0xF] == 0x80)
263+ printk(KERN_WARNING
264+ REQ_FMT("0x161F reports error", data->val[0xF]));
265+ return 0;
266+}
267+
268+/**
269+ * thinkpad_ec_is_row_fetched - is the given row currently prefetched?
270+ *
271+ * To keep things simple we compare only the first and last args;
272+ * this suffices for all known cases.
273+ */
274+static int thinkpad_ec_is_row_fetched(const struct thinkpad_ec_row *args)
275+{
276+ return (prefetch_jiffies != TPC_PREFETCH_NONE) &&
277+ (prefetch_jiffies != TPC_PREFETCH_JUNK) &&
278+ (prefetch_arg0 == args->val[0]) &&
279+ (prefetch_argF == args->val[0xF]) &&
280+ (get_jiffies_64() < prefetch_jiffies + TPC_PREFETCH_TIMEOUT);
281+}
282+
283+/**
284+ * thinkpad_ec_read_row - request and read data from ThinkPad EC
285+ * @args Input register arguments
286+ * @data Output register values
287+ *
288+ * Read a data row from the ThinkPad embedded controller LPC3 interface.
289+ * Does fetching and retrying if needed. The row is specified by an
290+ * array of 16 bytes, some of which may be undefined (but the first is
291+ * mandatory). These bytes are given in @args->val[], where @args->val[i] is
292+ * used iff (@args->mask>>i)&1). The resulting row data is stored in
293+ * @data->val[], but is only guaranteed to be valid for indices corresponding
294+ * to set bit in @data->mask. That is, if @data->mask&(1<<i)==0 then
295+ * @data->val[i] is undefined.
296+ *
297+ * Returns -EBUSY on transient error and -EIO on abnormal condition.
298+ * Caller must hold controller lock.
299+ */
300+int thinkpad_ec_read_row(const struct thinkpad_ec_row *args,
301+ struct thinkpad_ec_row *data)
302+{
303+ int retries, ret;
304+
305+ if (thinkpad_ec_is_row_fetched(args))
306+ goto read_row; /* already requested */
307+
308+ /* Request the row */
309+ for (retries = 0; retries < TPC_READ_RETRIES; ++retries) {
310+ ret = thinkpad_ec_request_row(args);
311+ if (!ret)
312+ goto read_row;
313+ if (ret != -EBUSY)
314+ break;
315+ ndelay(TPC_READ_NDELAY);
316+ }
317+ printk(KERN_ERR REQ_FMT("failed requesting row", ret));
318+ goto out;
319+
320+read_row:
321+ /* Read the row's data */
322+ for (retries = 0; retries < TPC_READ_RETRIES; ++retries) {
323+ ret = thinkpad_ec_read_data(args, data);
324+ if (!ret)
325+ goto out;
326+ if (ret != -EBUSY)
327+ break;
328+ ndelay(TPC_READ_NDELAY);
329+ }
330+
331+ printk(KERN_ERR REQ_FMT("failed waiting for data", ret));
332+
333+out:
334+ prefetch_jiffies = TPC_PREFETCH_JUNK;
335+ return ret;
336+}
337+EXPORT_SYMBOL_GPL(thinkpad_ec_read_row);
338+
339+/**
340+ * thinkpad_ec_try_read_row - try reading prefetched data from ThinkPad EC
341+ * @args Input register arguments
342+ * @data Output register values
343+ *
344+ * Try reading a data row from the ThinkPad embedded controller LPC3
345+ * interface, if this raw was recently prefetched using
346+ * thinkpad_ec_prefetch_row(). Does not fetch, retry or block.
347+ * The parameters have the same meaning as in thinkpad_ec_read_row().
348+ *
349+ * Returns -EBUSY is data not ready and -ENODATA if row not prefetched.
350+ * Caller must hold controller lock.
351+ */
352+int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args,
353+ struct thinkpad_ec_row *data)
354+{
355+ int ret;
356+ if (!thinkpad_ec_is_row_fetched(args)) {
357+ ret = -ENODATA;
358+ } else {
359+ ret = thinkpad_ec_read_data(args, data);
360+ if (!ret)
361+ prefetch_jiffies = TPC_PREFETCH_NONE; /* eaten up */
362+ }
363+ return ret;
364+}
365+EXPORT_SYMBOL_GPL(thinkpad_ec_try_read_row);
366+
367+/**
368+ * thinkpad_ec_prefetch_row - prefetch data from ThinkPad EC
369+ * @args Input register arguments
370+ *
371+ * Prefetch a data row from the ThinkPad embedded controller LCP3
372+ * interface. A subsequent call to thinkpad_ec_read_row() with the
373+ * same arguments will be faster, and a subsequent call to
374+ * thinkpad_ec_try_read_row() stands a good chance of succeeding if
375+ * done neither too soon nor too late. See
376+ * thinkpad_ec_read_row() for the meaning of @args.
377+ *
378+ * Returns -EBUSY on transient error and -EIO on abnormal condition.
379+ * Caller must hold controller lock.
380+ */
381+int thinkpad_ec_prefetch_row(const struct thinkpad_ec_row *args)
382+{
383+ int ret;
384+ ret = thinkpad_ec_request_row(args);
385+ if (ret) {
386+ prefetch_jiffies = TPC_PREFETCH_JUNK;
387+ } else {
388+ prefetch_jiffies = get_jiffies_64();
389+ prefetch_arg0 = args->val[0x0];
390+ prefetch_argF = args->val[0xF];
391+ }
392+ return ret;
393+}
394+EXPORT_SYMBOL_GPL(thinkpad_ec_prefetch_row);
395+
396+/**
397+ * thinkpad_ec_invalidate - invalidate prefetched ThinkPad EC data
398+ *
399+ * Invalidate the data prefetched via thinkpad_ec_prefetch_row() from the
400+ * ThinkPad embedded controller LPC3 interface.
401+ * Must be called before unlocking by any code that accesses the controller
402+ * ports directly.
403+ */
404+void thinkpad_ec_invalidate(void)
405+{
406+ prefetch_jiffies = TPC_PREFETCH_JUNK;
407+}
408+EXPORT_SYMBOL_GPL(thinkpad_ec_invalidate);
409+
410+
411+/*** Checking for EC hardware ***/
412+
413+/**
414+ * thinkpad_ec_test - verify the EC is present and follows protocol
415+ *
416+ * Ensure the EC LPC3 channel really works on this machine by making
417+ * an EC request and seeing if the EC follows the documented H8S protocol.
418+ * The requested row just reads battery status, so it should be harmless to
419+ * access it (on a correct EC).
420+ * This test writes to IO ports, so execute only after checking DMI.
421+ */
422+static int __init thinkpad_ec_test(void)
423+{
424+ int ret;
425+ const struct thinkpad_ec_row args = /* battery 0 basic status */
426+ { .mask = 0x8001, .val = {0x01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x00} };
427+ struct thinkpad_ec_row data = { .mask = 0x0000 };
428+ ret = thinkpad_ec_lock();
429+ if (ret)
430+ return ret;
431+ ret = thinkpad_ec_read_row(&args, &data);
432+ thinkpad_ec_unlock();
433+ return ret;
434+}
435+
436+/* Search all DMI device names of a given type for a substring */
437+static int __init dmi_find_substring(int type, const char *substr)
438+{
439+ const struct dmi_device *dev = NULL;
440+ while ((dev = dmi_find_device(type, NULL, dev))) {
441+ if (strstr(dev->name, substr))
442+ return 1;
443+ }
444+ return 0;
445+}
446+
447+#define TP_DMI_MATCH(vendor,model) { \
448+ .ident = vendor " " model, \
449+ .matches = { \
450+ DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
451+ DMI_MATCH(DMI_PRODUCT_VERSION, model) \
452+ } \
453+}
454+
455+/* Check DMI for existence of ThinkPad embedded controller */
456+static int __init check_dmi_for_ec(void)
457+{
458+ /* A few old models that have a good EC but don't report it in DMI */
459+ struct dmi_system_id tp_whitelist[] = {
460+ TP_DMI_MATCH("IBM", "ThinkPad A30"),
461+ TP_DMI_MATCH("IBM", "ThinkPad T23"),
462+ TP_DMI_MATCH("IBM", "ThinkPad X24"),
463+ TP_DMI_MATCH("LENOVO", "ThinkPad"),
464+ { .ident = NULL }
465+ };
466+ return dmi_find_substring(DMI_DEV_TYPE_OEM_STRING,
467+ "IBM ThinkPad Embedded Controller") ||
468+ dmi_check_system(tp_whitelist);
469+}
470+
471+/*** Init and cleanup ***/
472+
473+static int __init thinkpad_ec_init(void)
474+{
475+ if (!check_dmi_for_ec()) {
476+ printk(KERN_WARNING
477+ "thinkpad_ec: no ThinkPad embedded controller!\n");
478+ return -ENODEV;
479+ }
480+
481+ if (request_region(TPC_BASE_PORT, TPC_NUM_PORTS, "thinkpad_ec")) {
482+ reserved_io = 1;
483+ } else {
484+ printk(KERN_ERR "thinkpad_ec: cannot claim IO ports %#x-%#x... ",
485+ TPC_BASE_PORT,
486+ TPC_BASE_PORT + TPC_NUM_PORTS - 1);
487+ if (force_io) {
488+ printk("forcing use of unreserved IO ports.\n");
489+ } else {
490+ printk("consider using force_io=1.\n");
491+ return -ENXIO;
492+ }
493+ }
494+ prefetch_jiffies = TPC_PREFETCH_JUNK;
495+ if (thinkpad_ec_test()) {
496+ printk(KERN_ERR "thinkpad_ec: initial ec test failed\n");
497+ if (reserved_io)
498+ release_region(TPC_BASE_PORT, TPC_NUM_PORTS);
499+ return -ENXIO;
500+ }
501+ printk(KERN_INFO "thinkpad_ec: thinkpad_ec " TP_VERSION " loaded.\n");
502+ return 0;
503+}
504+
505+static void __exit thinkpad_ec_exit(void)
506+{
507+ if (reserved_io)
508+ release_region(TPC_BASE_PORT, TPC_NUM_PORTS);
509+ printk(KERN_INFO "thinkpad_ec: unloaded.\n");
510+}
511+
512+module_init(thinkpad_ec_init);
513+module_exit(thinkpad_ec_exit);
--- /dev/null
+++ b/tp_smapi/thinkpad_ec.h
@@ -0,0 +1,47 @@
1+/*
2+ * thinkpad_ec.h - interface to ThinkPad embedded controller LPC3 functions
3+ *
4+ * Copyright (C) 2005 Shem Multinymous <multinymous@gmail.com>
5+ *
6+ * This program is free software; you can redistribute it and/or modify
7+ * it under the terms of the GNU General Public License as published by
8+ * the Free Software Foundation; either version 2 of the License, or
9+ * (at your option) any later version.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program; if not, write to the Free Software
18+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19+ */
20+
21+#ifndef _THINKPAD_EC_H
22+#define _THINKPAD_EC_H
23+
24+#ifdef __KERNEL__
25+
26+#define TP_CONTROLLER_ROW_LEN 16
27+
28+/* EC transactions input and output (possibly partial) vectors of 16 bytes. */
29+struct thinkpad_ec_row {
30+ u16 mask; /* bitmap of which entries of val[] are meaningful */
31+ u8 val[TP_CONTROLLER_ROW_LEN];
32+};
33+
34+extern int __must_check thinkpad_ec_lock(void);
35+extern int __must_check thinkpad_ec_try_lock(void);
36+extern void thinkpad_ec_unlock(void);
37+
38+extern int thinkpad_ec_read_row(const struct thinkpad_ec_row *args,
39+ struct thinkpad_ec_row *data);
40+extern int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args,
41+ struct thinkpad_ec_row *mask);
42+extern int thinkpad_ec_prefetch_row(const struct thinkpad_ec_row *args);
43+extern void thinkpad_ec_invalidate(void);
44+
45+
46+#endif /* __KERNEL */
47+#endif /* _THINKPAD_EC_H */
--- /dev/null
+++ b/tp_smapi/tp_smapi.c
@@ -0,0 +1,1493 @@
1+/*
2+ * tp_smapi.c - ThinkPad SMAPI support
3+ *
4+ * This driver exposes some features of the System Management Application
5+ * Program Interface (SMAPI) BIOS found on ThinkPad laptops. It works on
6+ * models in which the SMAPI BIOS runs in SMM and is invoked by writing
7+ * to the APM control port 0xB2.
8+ * It also exposes battery status information, obtained from the ThinkPad
9+ * embedded controller (via the thinkpad_ec module).
10+ * Ancient ThinkPad models use a different interface, supported by the
11+ * "thinkpad" module from "tpctl".
12+ *
13+ * Many of the battery status values obtained from the EC simply mirror
14+ * values provided by the battery's Smart Battery System (SBS) interface, so
15+ * their meaning is defined by the Smart Battery Data Specification (see
16+ * http://sbs-forum.org/specs/sbdat110.pdf). References to this SBS spec
17+ * are given in the code where relevant.
18+ *
19+ * Copyright (C) 2006 Shem Multinymous <multinymous@gmail.com>.
20+ * SMAPI access code based on the mwave driver by Mike Sullivan.
21+ *
22+ * This program is free software; you can redistribute it and/or modify
23+ * it under the terms of the GNU General Public License as published by
24+ * the Free Software Foundation; either version 2 of the License, or
25+ * (at your option) any later version.
26+ *
27+ * This program is distributed in the hope that it will be useful,
28+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
29+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30+ * GNU General Public License for more details.
31+ *
32+ * You should have received a copy of the GNU General Public License
33+ * along with this program; if not, write to the Free Software
34+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35+ */
36+
37+#include <linux/kernel.h>
38+#include <linux/module.h>
39+#include <linux/init.h>
40+#include <linux/types.h>
41+#include <linux/proc_fs.h>
42+#include <linux/mc146818rtc.h> /* CMOS defines */
43+#include <linux/delay.h>
44+#include <linux/version.h>
45+#include "thinkpad_ec.h"
46+#include <linux/platform_device.h>
47+#include <asm/uaccess.h>
48+#include <asm/io.h>
49+
50+#define TP_VERSION "0.41"
51+#define TP_DESC "ThinkPad SMAPI Support"
52+#define TP_DIR "smapi"
53+
54+MODULE_AUTHOR("Shem Multinymous");
55+MODULE_DESCRIPTION(TP_DESC);
56+MODULE_VERSION(TP_VERSION);
57+MODULE_LICENSE("GPL");
58+
59+static struct platform_device *pdev;
60+
61+static int tp_debug;
62+module_param_named(debug, tp_debug, int, 0600);
63+MODULE_PARM_DESC(debug, "Debug level (0=off, 1=on)");
64+
65+/* A few macros for printk()ing: */
66+#define TPRINTK(level, fmt, args...) \
67+ dev_printk(level, &(pdev->dev), "%s: " fmt "\n", __func__, ## args)
68+#define DPRINTK(fmt, args...) \
69+ do { if (tp_debug) TPRINTK(KERN_DEBUG, fmt, ## args); } while (0)
70+
71+/*********************************************************************
72+ * SMAPI interface
73+ */
74+
75+/* SMAPI functions (register BX when making the SMM call). */
76+#define SMAPI_GET_INHIBIT_CHARGE 0x2114
77+#define SMAPI_SET_INHIBIT_CHARGE 0x2115
78+#define SMAPI_GET_THRESH_START 0x2116
79+#define SMAPI_SET_THRESH_START 0x2117
80+#define SMAPI_GET_FORCE_DISCHARGE 0x2118
81+#define SMAPI_SET_FORCE_DISCHARGE 0x2119
82+#define SMAPI_GET_THRESH_STOP 0x211a
83+#define SMAPI_SET_THRESH_STOP 0x211b
84+
85+/* SMAPI error codes (see ThinkPad 770 Technical Reference Manual p.83 at
86+ http://www-307.ibm.com/pc/support/site.wss/document.do?lndocid=PFAN-3TUQQD */
87+#define SMAPI_RETCODE_EOF 0xff
88+static struct { u8 rc; char *msg; int ret; } smapi_retcode[] =
89+{
90+ {0x00, "OK", 0},
91+ {0x53, "SMAPI function is not available", -ENXIO},
92+ {0x81, "Invalid parameter", -EINVAL},
93+ {0x86, "Function is not supported by SMAPI BIOS", -EOPNOTSUPP},
94+ {0x90, "System error", -EIO},
95+ {0x91, "System is invalid", -EIO},
96+ {0x92, "System is busy, -EBUSY"},
97+ {0xa0, "Device error (disk read error)", -EIO},
98+ {0xa1, "Device is busy", -EBUSY},
99+ {0xa2, "Device is not attached", -ENXIO},
100+ {0xa3, "Device is disbled", -EIO},
101+ {0xa4, "Request parameter is out of range", -EINVAL},
102+ {0xa5, "Request parameter is not accepted", -EINVAL},
103+ {0xa6, "Transient error", -EBUSY}, /* ? */
104+ {SMAPI_RETCODE_EOF, "Unknown error code", -EIO}
105+};
106+
107+
108+#define SMAPI_MAX_RETRIES 10
109+#define SMAPI_PORT2 0x4F /* fixed port, meaning unclear */
110+static unsigned short smapi_port; /* APM control port, normally 0xB2 */
111+
112+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
113+static DECLARE_MUTEX(smapi_mutex);
114+#else
115+static DEFINE_SEMAPHORE(smapi_mutex);
116+#endif
117+
118+/**
119+ * find_smapi_port - read SMAPI port from NVRAM
120+ */
121+static int __init find_smapi_port(void)
122+{
123+ u16 smapi_id = 0;
124+ unsigned short port = 0;
125+ unsigned long flags;
126+
127+ spin_lock_irqsave(&rtc_lock, flags);
128+ smapi_id = CMOS_READ(0x7C);
129+ smapi_id |= (CMOS_READ(0x7D) << 8);
130+ spin_unlock_irqrestore(&rtc_lock, flags);
131+
132+ if (smapi_id != 0x5349) {
133+ printk(KERN_ERR "SMAPI not supported (ID=0x%x)\n", smapi_id);
134+ return -ENXIO;
135+ }
136+ spin_lock_irqsave(&rtc_lock, flags);
137+ port = CMOS_READ(0x7E);
138+ port |= (CMOS_READ(0x7F) << 8);
139+ spin_unlock_irqrestore(&rtc_lock, flags);
140+ if (port == 0) {
141+ printk(KERN_ERR "unable to read SMAPI port number\n");
142+ return -ENXIO;
143+ }
144+ return port;
145+}
146+
147+/**
148+ * smapi_request - make a SMAPI call
149+ * @inEBX, @inECX, @inEDI, @inESI: input registers
150+ * @outEBX, @outECX, @outEDX, @outEDI, @outESI: outputs registers
151+ * @msg: textual error message
152+ * Invokes the SMAPI SMBIOS with the given input and outpu args.
153+ * All outputs are optional (can be %NULL).
154+ * Returns 0 when successful, and a negative errno constant
155+ * (see smapi_retcode above) upon failure.
156+ */
157+static int smapi_request(u32 inEBX, u32 inECX,
158+ u32 inEDI, u32 inESI,
159+ u32 *outEBX, u32 *outECX, u32 *outEDX,
160+ u32 *outEDI, u32 *outESI, const char **msg)
161+{
162+ int ret = 0;
163+ int i;
164+ int retries;
165+ u8 rc;
166+ /* Must use local vars for output regs, due to reg pressure. */
167+ u32 tmpEAX, tmpEBX, tmpECX, tmpEDX, tmpEDI, tmpESI;
168+
169+ for (retries = 0; retries < SMAPI_MAX_RETRIES; ++retries) {
170+ DPRINTK("req_in: BX=%x CX=%x DI=%x SI=%x",
171+ inEBX, inECX, inEDI, inESI);
172+
173+ /* SMAPI's SMBIOS call and thinkpad_ec end up using use
174+ * different interfaces to the same chip, so play it safe. */
175+ ret = thinkpad_ec_lock();
176+ if (ret)
177+ return ret;
178+
179+ __asm__ __volatile__(
180+ "movl $0x00005380,%%eax\n\t"
181+ "movl %6,%%ebx\n\t"
182+ "movl %7,%%ecx\n\t"
183+ "movl %8,%%edi\n\t"
184+ "movl %9,%%esi\n\t"
185+ "xorl %%edx,%%edx\n\t"
186+ "movw %10,%%dx\n\t"
187+ "out %%al,%%dx\n\t" /* trigger SMI to SMBIOS */
188+ "out %%al,$0x4F\n\t"
189+ "movl %%eax,%0\n\t"
190+ "movl %%ebx,%1\n\t"
191+ "movl %%ecx,%2\n\t"
192+ "movl %%edx,%3\n\t"
193+ "movl %%edi,%4\n\t"
194+ "movl %%esi,%5\n\t"
195+ :"=m"(tmpEAX),
196+ "=m"(tmpEBX),
197+ "=m"(tmpECX),
198+ "=m"(tmpEDX),
199+ "=m"(tmpEDI),
200+ "=m"(tmpESI)
201+ :"m"(inEBX), "m"(inECX), "m"(inEDI), "m"(inESI),
202+ "m"((u16)smapi_port)
203+ :"%eax", "%ebx", "%ecx", "%edx", "%edi",
204+ "%esi");
205+
206+ thinkpad_ec_invalidate();
207+ thinkpad_ec_unlock();
208+
209+ /* Don't let the next SMAPI access happen too quickly,
210+ * may case problems. (We're hold smapi_mutex). */
211+ msleep(50);
212+
213+ if (outEBX) *outEBX = tmpEBX;
214+ if (outECX) *outECX = tmpECX;
215+ if (outEDX) *outEDX = tmpEDX;
216+ if (outESI) *outESI = tmpESI;
217+ if (outEDI) *outEDI = tmpEDI;
218+
219+ /* Look up error code */
220+ rc = (tmpEAX>>8)&0xFF;
221+ for (i = 0; smapi_retcode[i].rc != SMAPI_RETCODE_EOF &&
222+ smapi_retcode[i].rc != rc; ++i) {}
223+ ret = smapi_retcode[i].ret;
224+ if (msg)
225+ *msg = smapi_retcode[i].msg;
226+
227+ DPRINTK("req_out: AX=%x BX=%x CX=%x DX=%x DI=%x SI=%x r=%d",
228+ tmpEAX, tmpEBX, tmpECX, tmpEDX, tmpEDI, tmpESI, ret);
229+ if (ret)
230+ TPRINTK(KERN_NOTICE, "SMAPI error: %s (func=%x)",
231+ smapi_retcode[i].msg, inEBX);
232+
233+ if (ret != -EBUSY)
234+ return ret;
235+ }
236+ return ret;
237+}
238+
239+/* Convenience wrapper: discard output arguments */
240+static int smapi_write(u32 inEBX, u32 inECX,
241+ u32 inEDI, u32 inESI, const char **msg)
242+{
243+ return smapi_request(inEBX, inECX, inEDI, inESI,
244+ NULL, NULL, NULL, NULL, NULL, msg);
245+}
246+
247+
248+/*********************************************************************
249+ * Specific SMAPI services
250+ * All of these functions return 0 upon success, and a negative errno
251+ * constant (see smapi_retcode) on failure.
252+ */
253+
254+enum thresh_type {
255+ THRESH_STOP = 0, /* the code assumes this is 0 for brevity */
256+ THRESH_START
257+};
258+#define THRESH_NAME(which) ((which == THRESH_START) ? "start" : "stop")
259+
260+/**
261+ * __get_real_thresh - read battery charge start/stop threshold from SMAPI
262+ * @bat: battery number (0 or 1)
263+ * @which: THRESH_START or THRESH_STOP
264+ * @thresh: 1..99, 0=default 1..99, 0=default (pass this as-is to SMAPI)
265+ * @outEDI: some additional state that needs to be preserved, meaning unknown
266+ * @outESI: some additional state that needs to be preserved, meaning unknown
267+ */
268+static int __get_real_thresh(int bat, enum thresh_type which, int *thresh,
269+ u32 *outEDI, u32 *outESI)
270+{
271+ u32 ebx = (which == THRESH_START) ? SMAPI_GET_THRESH_START
272+ : SMAPI_GET_THRESH_STOP;
273+ u32 ecx = (bat+1)<<8;
274+ const char *msg;
275+ int ret = smapi_request(ebx, ecx, 0, 0, NULL,
276+ &ecx, NULL, outEDI, outESI, &msg);
277+ if (ret) {
278+ TPRINTK(KERN_NOTICE, "cannot get %s_thresh of bat=%d: %s",
279+ THRESH_NAME(which), bat, msg);
280+ return ret;
281+ }
282+ if (!(ecx&0x00000100)) {
283+ TPRINTK(KERN_NOTICE, "cannot get %s_thresh of bat=%d: ecx=0%x",
284+ THRESH_NAME(which), bat, ecx);
285+ return -EIO;
286+ }
287+ if (thresh)
288+ *thresh = ecx&0xFF;
289+ return 0;
290+}
291+
292+/**
293+ * get_real_thresh - read battery charge start/stop threshold from SMAPI
294+ * @bat: battery number (0 or 1)
295+ * @which: THRESH_START or THRESH_STOP
296+ * @thresh: 1..99, 0=default (passes as-is to SMAPI)
297+ */
298+static int get_real_thresh(int bat, enum thresh_type which, int *thresh)
299+{
300+ return __get_real_thresh(bat, which, thresh, NULL, NULL);
301+}
302+
303+/**
304+ * set_real_thresh - write battery start/top charge threshold to SMAPI
305+ * @bat: battery number (0 or 1)
306+ * @which: THRESH_START or THRESH_STOP
307+ * @thresh: 1..99, 0=default (passes as-is to SMAPI)
308+ */
309+static int set_real_thresh(int bat, enum thresh_type which, int thresh)
310+{
311+ u32 ebx = (which == THRESH_START) ? SMAPI_SET_THRESH_START
312+ : SMAPI_SET_THRESH_STOP;
313+ u32 ecx = ((bat+1)<<8) + thresh;
314+ u32 getDI, getSI;
315+ const char *msg;
316+ int ret;
317+
318+ /* verify read before writing */
319+ ret = __get_real_thresh(bat, which, NULL, &getDI, &getSI);
320+ if (ret)
321+ return ret;
322+
323+ ret = smapi_write(ebx, ecx, getDI, getSI, &msg);
324+ if (ret)
325+ TPRINTK(KERN_NOTICE, "set %s to %d for bat=%d failed: %s",
326+ THRESH_NAME(which), thresh, bat, msg);
327+ else
328+ TPRINTK(KERN_INFO, "set %s to %d for bat=%d",
329+ THRESH_NAME(which), thresh, bat);
330+ return ret;
331+}
332+
333+/**
334+ * __get_inhibit_charge_minutes - get inhibit charge period from SMAPI
335+ * @bat: battery number (0 or 1)
336+ * @minutes: period in minutes (1..65535 minutes, 0=disabled)
337+ * @outECX: some additional state that needs to be preserved, meaning unknown
338+ * Note that @minutes is the originally set value, it does not count down.
339+ */
340+static int __get_inhibit_charge_minutes(int bat, int *minutes, u32 *outECX)
341+{
342+ u32 ecx = (bat+1)<<8;
343+ u32 esi;
344+ const char *msg;
345+ int ret = smapi_request(SMAPI_GET_INHIBIT_CHARGE, ecx, 0, 0,
346+ NULL, &ecx, NULL, NULL, &esi, &msg);
347+ if (ret) {
348+ TPRINTK(KERN_NOTICE, "failed for bat=%d: %s", bat, msg);
349+ return ret;
350+ }
351+ if (!(ecx&0x0100)) {
352+ TPRINTK(KERN_NOTICE, "bad ecx=0x%x for bat=%d", ecx, bat);
353+ return -EIO;
354+ }
355+ if (minutes)
356+ *minutes = (ecx&0x0001)?esi:0;
357+ if (outECX)
358+ *outECX = ecx;
359+ return 0;
360+}
361+
362+/**
363+ * get_inhibit_charge_minutes - get inhibit charge period from SMAPI
364+ * @bat: battery number (0 or 1)
365+ * @minutes: period in minutes (1..65535 minutes, 0=disabled)
366+ * Note that @minutes is the originally set value, it does not count down.
367+ */
368+static int get_inhibit_charge_minutes(int bat, int *minutes)
369+{
370+ return __get_inhibit_charge_minutes(bat, minutes, NULL);
371+}
372+
373+/**
374+ * set_inhibit_charge_minutes - write inhibit charge period to SMAPI
375+ * @bat: battery number (0 or 1)
376+ * @minutes: period in minutes (1..65535 minutes, 0=disabled)
377+ */
378+static int set_inhibit_charge_minutes(int bat, int minutes)
379+{
380+ u32 ecx;
381+ const char *msg;
382+ int ret;
383+
384+ /* verify read before writing */
385+ ret = __get_inhibit_charge_minutes(bat, NULL, &ecx);
386+ if (ret)
387+ return ret;
388+
389+ ecx = ((bat+1)<<8) | (ecx&0x00FE) | (minutes > 0 ? 0x0001 : 0x0000);
390+ if (minutes > 0xFFFF)
391+ minutes = 0xFFFF;
392+ ret = smapi_write(SMAPI_SET_INHIBIT_CHARGE, ecx, 0, minutes, &msg);
393+ if (ret)
394+ TPRINTK(KERN_NOTICE,
395+ "set to %d failed for bat=%d: %s", minutes, bat, msg);
396+ else
397+ TPRINTK(KERN_INFO, "set to %d for bat=%d\n", minutes, bat);
398+ return ret;
399+}
400+
401+
402+/**
403+ * get_force_discharge - get status of forced discharging from SMAPI
404+ * @bat: battery number (0 or 1)
405+ * @enabled: 1 if forced discharged is enabled, 0 if not
406+ */
407+static int get_force_discharge(int bat, int *enabled)
408+{
409+ u32 ecx = (bat+1)<<8;
410+ const char *msg;
411+ int ret = smapi_request(SMAPI_GET_FORCE_DISCHARGE, ecx, 0, 0,
412+ NULL, &ecx, NULL, NULL, NULL, &msg);
413+ if (ret) {
414+ TPRINTK(KERN_NOTICE, "failed for bat=%d: %s", bat, msg);
415+ return ret;
416+ }
417+ *enabled = (!(ecx&0x00000100) && (ecx&0x00000001))?1:0;
418+ return 0;
419+}
420+
421+/**
422+ * set_force_discharge - write status of forced discharging to SMAPI
423+ * @bat: battery number (0 or 1)
424+ * @enabled: 1 if forced discharged is enabled, 0 if not
425+ */
426+static int set_force_discharge(int bat, int enabled)
427+{
428+ u32 ecx = (bat+1)<<8;
429+ const char *msg;
430+ int ret = smapi_request(SMAPI_GET_FORCE_DISCHARGE, ecx, 0, 0,
431+ NULL, &ecx, NULL, NULL, NULL, &msg);
432+ if (ret) {
433+ TPRINTK(KERN_NOTICE, "get failed for bat=%d: %s", bat, msg);
434+ return ret;
435+ }
436+ if (ecx&0x00000100) {
437+ TPRINTK(KERN_NOTICE, "cannot force discharge bat=%d", bat);
438+ return -EIO;
439+ }
440+
441+ ecx = ((bat+1)<<8) | (ecx&0x000000FA) | (enabled?0x00000001:0);
442+ ret = smapi_write(SMAPI_SET_FORCE_DISCHARGE, ecx, 0, 0, &msg);
443+ if (ret)
444+ TPRINTK(KERN_NOTICE, "set to %d failed for bat=%d: %s",
445+ enabled, bat, msg);
446+ else
447+ TPRINTK(KERN_INFO, "set to %d for bat=%d", enabled, bat);
448+ return ret;
449+}
450+
451+
452+/*********************************************************************
453+ * Wrappers to threshold-related SMAPI functions, which handle default
454+ * thresholds and related quirks.
455+ */
456+
457+/* Minimum, default and minimum difference for battery charging thresholds: */
458+#define MIN_THRESH_DELTA 4 /* Min delta between start and stop thresh */
459+#define MIN_THRESH_START 2
460+#define MAX_THRESH_START (100-MIN_THRESH_DELTA)
461+#define MIN_THRESH_STOP (MIN_THRESH_START + MIN_THRESH_DELTA)
462+#define MAX_THRESH_STOP 100
463+#define DEFAULT_THRESH_START MAX_THRESH_START
464+#define DEFAULT_THRESH_STOP MAX_THRESH_STOP
465+
466+/* The GUI of IBM's Battery Maximizer seems to show a start threshold that
467+ * is 1 more than the value we set/get via SMAPI. Since the threshold is
468+ * maintained across reboot, this can be confusing. So we kludge our
469+ * interface for interoperability: */
470+#define BATMAX_FIX 1
471+
472+/* Get charge start/stop threshold (1..100),
473+ * substituting default values if needed and applying BATMAT_FIX. */
474+static int get_thresh(int bat, enum thresh_type which, int *thresh)
475+{
476+ int ret = get_real_thresh(bat, which, thresh);
477+ if (ret)
478+ return ret;
479+ if (*thresh == 0)
480+ *thresh = (which == THRESH_START) ? DEFAULT_THRESH_START
481+ : DEFAULT_THRESH_STOP;
482+ else if (which == THRESH_START)
483+ *thresh += BATMAX_FIX;
484+ return 0;
485+}
486+
487+
488+/* Set charge start/stop threshold (1..100),
489+ * substituting default values if needed and applying BATMAT_FIX. */
490+static int set_thresh(int bat, enum thresh_type which, int thresh)
491+{
492+ if (which == THRESH_STOP && thresh == DEFAULT_THRESH_STOP)
493+ thresh = 0; /* 100 is out of range, but default means 100 */
494+ if (which == THRESH_START)
495+ thresh -= BATMAX_FIX;
496+ return set_real_thresh(bat, which, thresh);
497+}
498+
499+/*********************************************************************
500+ * ThinkPad embedded controller readout and basic functions
501+ */
502+
503+/**
504+ * read_tp_ec_row - read data row from the ThinkPad embedded controller
505+ * @arg0: EC command code
506+ * @bat: battery number, 0 or 1
507+ * @j: the byte value to be used for "junk" (unused) input/outputs
508+ * @dataval: result vector
509+ */
510+static int read_tp_ec_row(u8 arg0, int bat, u8 j, u8 *dataval)
511+{
512+ int ret;
513+ const struct thinkpad_ec_row args = { .mask = 0xFFFF,
514+ .val = {arg0, j,j,j,j,j,j,j,j,j,j,j,j,j,j, (u8)bat} };
515+ struct thinkpad_ec_row data = { .mask = 0xFFFF };
516+
517+ ret = thinkpad_ec_lock();
518+ if (ret)
519+ return ret;
520+ ret = thinkpad_ec_read_row(&args, &data);
521+ thinkpad_ec_unlock();
522+ memcpy(dataval, &data.val, TP_CONTROLLER_ROW_LEN);
523+ return ret;
524+}
525+
526+/**
527+ * power_device_present - check for presence of battery or AC power
528+ * @bat: 0 for battery 0, 1 for battery 1, otherwise AC power
529+ * Returns 1 if present, 0 if not present, negative if error.
530+ */
531+static int power_device_present(int bat)
532+{
533+ u8 row[TP_CONTROLLER_ROW_LEN];
534+ u8 test;
535+ int ret = read_tp_ec_row(1, bat, 0, row);
536+ if (ret)
537+ return ret;
538+ switch (bat) {
539+ case 0: test = 0x40; break; /* battery 0 */
540+ case 1: test = 0x20; break; /* battery 1 */
541+ default: test = 0x80; /* AC power */
542+ }
543+ return (row[0] & test) ? 1 : 0;
544+}
545+
546+/**
547+ * bat_has_status - check if battery can report detailed status
548+ * @bat: 0 for battery 0, 1 for battery 1
549+ * Returns 1 if yes, 0 if no, negative if error.
550+ */
551+static int bat_has_status(int bat)
552+{
553+ u8 row[TP_CONTROLLER_ROW_LEN];
554+ int ret = read_tp_ec_row(1, bat, 0, row);
555+ if (ret)
556+ return ret;
557+ if ((row[0] & (bat?0x20:0x40)) == 0) /* no battery */
558+ return 0;
559+ if ((row[1] & (0x60)) == 0) /* no status */
560+ return 0;
561+ return 1;
562+}
563+
564+/**
565+ * get_tp_ec_bat_16 - read a 16-bit value from EC battery status data
566+ * @arg0: first argument to EC
567+ * @off: offset in row returned from EC
568+ * @bat: battery (0 or 1)
569+ * @val: the 16-bit value obtained
570+ * Returns nonzero on error.
571+ */
572+static int get_tp_ec_bat_16(u8 arg0, int offset, int bat, u16 *val)
573+{
574+ u8 row[TP_CONTROLLER_ROW_LEN];
575+ int ret;
576+ if (bat_has_status(bat) != 1)
577+ return -ENXIO;
578+ ret = read_tp_ec_row(arg0, bat, 0, row);
579+ if (ret)
580+ return ret;
581+ *val = *(u16 *)(row+offset);
582+ return 0;
583+}
584+
585+/*********************************************************************
586+ * sysfs attributes for batteries -
587+ * definitions and helper functions
588+ */
589+
590+/* A custom device attribute struct which holds a battery number */
591+struct bat_device_attribute {
592+ struct device_attribute dev_attr;
593+ int bat;
594+};
595+
596+/**
597+ * attr_get_bat - get the battery to which the attribute belongs
598+ */
599+static int attr_get_bat(struct device_attribute *attr)
600+{
601+ return container_of(attr, struct bat_device_attribute, dev_attr)->bat;
602+}
603+
604+/**
605+ * show_tp_ec_bat_u16 - show an unsigned 16-bit battery attribute
606+ * @arg0: specified 1st argument of EC raw to read
607+ * @offset: byte offset in EC raw data
608+ * @mul: correction factor to multiply by
609+ * @na_msg: string to output is value not available (0xFFFFFFFF)
610+ * @attr: battery attribute
611+ * @buf: output buffer
612+ * The 16-bit value is read from the EC, treated as unsigned,
613+ * transformed as x->mul*x, and printed to the buffer.
614+ * If the value is 0xFFFFFFFF and na_msg!=%NULL, na_msg is printed instead.
615+ */
616+static ssize_t show_tp_ec_bat_u16(u8 arg0, int offset, int mul,
617+ const char *na_msg,
618+ struct device_attribute *attr, char *buf)
619+{
620+ u16 val;
621+ int ret = get_tp_ec_bat_16(arg0, offset, attr_get_bat(attr), &val);
622+ if (ret)
623+ return ret;
624+ if (na_msg && val == 0xFFFF)
625+ return sprintf(buf, "%s\n", na_msg);
626+ else
627+ return sprintf(buf, "%u\n", mul*(unsigned int)val);
628+}
629+
630+/**
631+ * show_tp_ec_bat_s16 - show an signed 16-bit battery attribute
632+ * @arg0: specified 1st argument of EC raw to read
633+ * @offset: byte offset in EC raw data
634+ * @mul: correction factor to multiply by
635+ * @add: correction term to add after multiplication
636+ * @attr: battery attribute
637+ * @buf: output buffer
638+ * The 16-bit value is read from the EC, treated as signed,
639+ * transformed as x->mul*x+add, and printed to the buffer.
640+ */
641+static ssize_t show_tp_ec_bat_s16(u8 arg0, int offset, int mul, int add,
642+ struct device_attribute *attr, char *buf)
643+{
644+ u16 val;
645+ int ret = get_tp_ec_bat_16(arg0, offset, attr_get_bat(attr), &val);
646+ if (ret)
647+ return ret;
648+ return sprintf(buf, "%d\n", mul*(s16)val+add);
649+}
650+
651+/**
652+ * show_tp_ec_bat_str - show a string from EC battery status data
653+ * @arg0: specified 1st argument of EC raw to read
654+ * @offset: byte offset in EC raw data
655+ * @maxlen: maximum string length
656+ * @attr: battery attribute
657+ * @buf: output buffer
658+ */
659+static ssize_t show_tp_ec_bat_str(u8 arg0, int offset, int maxlen,
660+ struct device_attribute *attr, char *buf)
661+{
662+ int bat = attr_get_bat(attr);
663+ u8 row[TP_CONTROLLER_ROW_LEN];
664+ int ret;
665+ if (bat_has_status(bat) != 1)
666+ return -ENXIO;
667+ ret = read_tp_ec_row(arg0, bat, 0, row);
668+ if (ret)
669+ return ret;
670+ strncpy(buf, (char *)row+offset, maxlen);
671+ buf[maxlen] = 0;
672+ strcat(buf, "\n");
673+ return strlen(buf);
674+}
675+
676+/**
677+ * show_tp_ec_bat_power - show a power readout from EC battery status data
678+ * @arg0: specified 1st argument of EC raw to read
679+ * @offV: byte offset of voltage in EC raw data
680+ * @offI: byte offset of current in EC raw data
681+ * @attr: battery attribute
682+ * @buf: output buffer
683+ * Computes the power as current*voltage from the two given readout offsets.
684+ */
685+static ssize_t show_tp_ec_bat_power(u8 arg0, int offV, int offI,
686+ struct device_attribute *attr, char *buf)
687+{
688+ u8 row[TP_CONTROLLER_ROW_LEN];
689+ int milliamp, millivolt, ret;
690+ int bat = attr_get_bat(attr);
691+ if (bat_has_status(bat) != 1)
692+ return -ENXIO;
693+ ret = read_tp_ec_row(1, bat, 0, row);
694+ if (ret)
695+ return ret;
696+ millivolt = *(u16 *)(row+offV);
697+ milliamp = *(s16 *)(row+offI);
698+ return sprintf(buf, "%d\n", milliamp*millivolt/1000); /* units: mW */
699+}
700+
701+/**
702+ * show_tp_ec_bat_date - decode and show a date from EC battery status data
703+ * @arg0: specified 1st argument of EC raw to read
704+ * @offset: byte offset in EC raw data
705+ * @attr: battery attribute
706+ * @buf: output buffer
707+ */
708+static ssize_t show_tp_ec_bat_date(u8 arg0, int offset,
709+ struct device_attribute *attr, char *buf)
710+{
711+ u8 row[TP_CONTROLLER_ROW_LEN];
712+ u16 v;
713+ int ret;
714+ int day, month, year;
715+ int bat = attr_get_bat(attr);
716+ if (bat_has_status(bat) != 1)
717+ return -ENXIO;
718+ ret = read_tp_ec_row(arg0, bat, 0, row);
719+ if (ret)
720+ return ret;
721+
722+ /* Decode bit-packed: v = day | (month<<5) | ((year-1980)<<9) */
723+ v = *(u16 *)(row+offset);
724+ day = v & 0x1F;
725+ month = (v >> 5) & 0xF;
726+ year = (v >> 9) + 1980;
727+
728+ return sprintf(buf, "%04d-%02d-%02d\n", year, month, day);
729+}
730+
731+
732+/*********************************************************************
733+ * sysfs attribute I/O for batteries -
734+ * the actual attribute show/store functions
735+ */
736+
737+static ssize_t show_battery_start_charge_thresh(struct device *dev,
738+ struct device_attribute *attr, char *buf)
739+{
740+ int thresh;
741+ int bat = attr_get_bat(attr);
742+ int ret = get_thresh(bat, THRESH_START, &thresh);
743+ if (ret)
744+ return ret;
745+ return sprintf(buf, "%d\n", thresh); /* units: percent */
746+}
747+
748+static ssize_t show_battery_stop_charge_thresh(struct device *dev,
749+ struct device_attribute *attr, char *buf)
750+{
751+ int thresh;
752+ int bat = attr_get_bat(attr);
753+ int ret = get_thresh(bat, THRESH_STOP, &thresh);
754+ if (ret)
755+ return ret;
756+ return sprintf(buf, "%d\n", thresh); /* units: percent */
757+}
758+
759+/**
760+ * store_battery_start_charge_thresh - store battery_start_charge_thresh attr
761+ * Since this is a kernel<->user interface, we ensure a valid state for
762+ * the hardware. We do this by clamping the requested threshold to the
763+ * valid range and, if necessary, moving the other threshold so that
764+ * it's MIN_THRESH_DELTA away from this one.
765+ */
766+static ssize_t store_battery_start_charge_thresh(struct device *dev,
767+ struct device_attribute *attr, const char *buf, size_t count)
768+{
769+ int thresh, other_thresh, ret;
770+ int bat = attr_get_bat(attr);
771+
772+ if (sscanf(buf, "%d", &thresh) != 1 || thresh < 1 || thresh > 100)
773+ return -EINVAL;
774+
775+ if (thresh < MIN_THRESH_START) /* clamp up to MIN_THRESH_START */
776+ thresh = MIN_THRESH_START;
777+ if (thresh > MAX_THRESH_START) /* clamp down to MAX_THRESH_START */
778+ thresh = MAX_THRESH_START;
779+
780+ down(&smapi_mutex);
781+ ret = get_thresh(bat, THRESH_STOP, &other_thresh);
782+ if (ret != -EOPNOTSUPP && ret != -ENXIO) {
783+ if (ret) /* other threshold is set? */
784+ goto out;
785+ ret = get_real_thresh(bat, THRESH_START, NULL);
786+ if (ret) /* this threshold is set? */
787+ goto out;
788+ if (other_thresh < thresh+MIN_THRESH_DELTA) {
789+ /* move other thresh to keep it above this one */
790+ ret = set_thresh(bat, THRESH_STOP,
791+ thresh+MIN_THRESH_DELTA);
792+ if (ret)
793+ goto out;
794+ }
795+ }
796+ ret = set_thresh(bat, THRESH_START, thresh);
797+out:
798+ up(&smapi_mutex);
799+ return count;
800+
801+}
802+
803+/**
804+ * store_battery_stop_charge_thresh - store battery_stop_charge_thresh attr
805+ * Since this is a kernel<->user interface, we ensure a valid state for
806+ * the hardware. We do this by clamping the requested threshold to the
807+ * valid range and, if necessary, moving the other threshold so that
808+ * it's MIN_THRESH_DELTA away from this one.
809+ */
810+static ssize_t store_battery_stop_charge_thresh(struct device *dev,
811+ struct device_attribute *attr, const char *buf, size_t count)
812+{
813+ int thresh, other_thresh, ret;
814+ int bat = attr_get_bat(attr);
815+
816+ if (sscanf(buf, "%d", &thresh) != 1 || thresh < 1 || thresh > 100)
817+ return -EINVAL;
818+
819+ if (thresh < MIN_THRESH_STOP) /* clamp up to MIN_THRESH_STOP */
820+ thresh = MIN_THRESH_STOP;
821+
822+ down(&smapi_mutex);
823+ ret = get_thresh(bat, THRESH_START, &other_thresh);
824+ if (ret != -EOPNOTSUPP && ret != -ENXIO) { /* other threshold exists? */
825+ if (ret)
826+ goto out;
827+ /* this threshold exists? */
828+ ret = get_real_thresh(bat, THRESH_STOP, NULL);
829+ if (ret)
830+ goto out;
831+ if (other_thresh >= thresh-MIN_THRESH_DELTA) {
832+ /* move other thresh to be below this one */
833+ ret = set_thresh(bat, THRESH_START,
834+ thresh-MIN_THRESH_DELTA);
835+ if (ret)
836+ goto out;
837+ }
838+ }
839+ ret = set_thresh(bat, THRESH_STOP, thresh);
840+out:
841+ up(&smapi_mutex);
842+ return count;
843+}
844+
845+static ssize_t show_battery_inhibit_charge_minutes(struct device *dev,
846+ struct device_attribute *attr, char *buf)
847+{
848+ int minutes;
849+ int bat = attr_get_bat(attr);
850+ int ret = get_inhibit_charge_minutes(bat, &minutes);
851+ if (ret)
852+ return ret;
853+ return sprintf(buf, "%d\n", minutes); /* units: minutes */
854+}
855+
856+static ssize_t store_battery_inhibit_charge_minutes(struct device *dev,
857+ struct device_attribute *attr,
858+ const char *buf, size_t count)
859+{
860+ int ret;
861+ int minutes;
862+ int bat = attr_get_bat(attr);
863+ if (sscanf(buf, "%d", &minutes) != 1 || minutes < 0) {
864+ TPRINTK(KERN_ERR, "inhibit_charge_minutes: "
865+ "must be a non-negative integer");
866+ return -EINVAL;
867+ }
868+ ret = set_inhibit_charge_minutes(bat, minutes);
869+ if (ret)
870+ return ret;
871+ return count;
872+}
873+
874+static ssize_t show_battery_force_discharge(struct device *dev,
875+ struct device_attribute *attr, char *buf)
876+{
877+ int enabled;
878+ int bat = attr_get_bat(attr);
879+ int ret = get_force_discharge(bat, &enabled);
880+ if (ret)
881+ return ret;
882+ return sprintf(buf, "%d\n", enabled); /* type: boolean */
883+}
884+
885+static ssize_t store_battery_force_discharge(struct device *dev,
886+ struct device_attribute *attr, const char *buf, size_t count)
887+{
888+ int ret;
889+ int enabled;
890+ int bat = attr_get_bat(attr);
891+ if (sscanf(buf, "%d", &enabled) != 1 || enabled < 0 || enabled > 1)
892+ return -EINVAL;
893+ ret = set_force_discharge(bat, enabled);
894+ if (ret)
895+ return ret;
896+ return count;
897+}
898+
899+static ssize_t show_battery_installed(
900+ struct device *dev, struct device_attribute *attr, char *buf)
901+{
902+ int bat = attr_get_bat(attr);
903+ int ret = power_device_present(bat);
904+ if (ret < 0)
905+ return ret;
906+ return sprintf(buf, "%d\n", ret); /* type: boolean */
907+}
908+
909+static ssize_t show_battery_state(
910+ struct device *dev, struct device_attribute *attr, char *buf)
911+{
912+ u8 row[TP_CONTROLLER_ROW_LEN];
913+ const char *txt;
914+ int ret;
915+ int bat = attr_get_bat(attr);
916+ if (bat_has_status(bat) != 1)
917+ return sprintf(buf, "none\n");
918+ ret = read_tp_ec_row(1, bat, 0, row);
919+ if (ret)
920+ return ret;
921+ switch (row[1] & 0xf0) {
922+ case 0xc0: txt = "idle"; break;
923+ case 0xd0: txt = "discharging"; break;
924+ case 0xe0: txt = "charging"; break;
925+ default: return sprintf(buf, "unknown (0x%x)\n", row[1]);
926+ }
927+ return sprintf(buf, "%s\n", txt); /* type: string from fixed set */
928+}
929+
930+static ssize_t show_battery_manufacturer(
931+ struct device *dev, struct device_attribute *attr, char *buf)
932+{
933+ /* type: string. SBS spec v1.1 p34: ManufacturerName() */
934+ return show_tp_ec_bat_str(4, 2, TP_CONTROLLER_ROW_LEN-2, attr, buf);
935+}
936+
937+static ssize_t show_battery_model(
938+ struct device *dev, struct device_attribute *attr, char *buf)
939+{
940+ /* type: string. SBS spec v1.1 p34: DeviceName() */
941+ return show_tp_ec_bat_str(5, 2, TP_CONTROLLER_ROW_LEN-2, attr, buf);
942+}
943+
944+static ssize_t show_battery_barcoding(
945+ struct device *dev, struct device_attribute *attr, char *buf)
946+{
947+ /* type: string */
948+ return show_tp_ec_bat_str(7, 2, TP_CONTROLLER_ROW_LEN-2, attr, buf);
949+}
950+
951+static ssize_t show_battery_chemistry(
952+ struct device *dev, struct device_attribute *attr, char *buf)
953+{
954+ /* type: string. SBS spec v1.1 p34-35: DeviceChemistry() */
955+ return show_tp_ec_bat_str(6, 2, 5, attr, buf);
956+}
957+
958+static ssize_t show_battery_voltage(
959+ struct device *dev, struct device_attribute *attr, char *buf)
960+{
961+ /* units: mV. SBS spec v1.1 p24: Voltage() */
962+ return show_tp_ec_bat_u16(1, 6, 1, NULL, attr, buf);
963+}
964+
965+static ssize_t show_battery_design_voltage(
966+ struct device *dev, struct device_attribute *attr, char *buf)
967+{
968+ /* units: mV. SBS spec v1.1 p32: DesignVoltage() */
969+ return show_tp_ec_bat_u16(3, 4, 1, NULL, attr, buf);
970+}
971+
972+static ssize_t show_battery_charging_max_voltage(
973+ struct device *dev, struct device_attribute *attr, char *buf)
974+{
975+ /* units: mV. SBS spec v1.1 p37,39: ChargingVoltage() */
976+ return show_tp_ec_bat_u16(9, 8, 1, NULL, attr, buf);
977+}
978+
979+static ssize_t show_battery_group0_voltage(
980+ struct device *dev, struct device_attribute *attr, char *buf)
981+{
982+ /* units: mV */
983+ return show_tp_ec_bat_u16(0xA, 12, 1, NULL, attr, buf);
984+}
985+
986+static ssize_t show_battery_group1_voltage(
987+ struct device *dev, struct device_attribute *attr, char *buf)
988+{
989+ /* units: mV */
990+ return show_tp_ec_bat_u16(0xA, 10, 1, NULL, attr, buf);
991+}
992+
993+static ssize_t show_battery_group2_voltage(
994+ struct device *dev, struct device_attribute *attr, char *buf)
995+{
996+ /* units: mV */
997+ return show_tp_ec_bat_u16(0xA, 8, 1, NULL, attr, buf);
998+}
999+
1000+static ssize_t show_battery_group3_voltage(
1001+ struct device *dev, struct device_attribute *attr, char *buf)
1002+{
1003+ /* units: mV */
1004+ return show_tp_ec_bat_u16(0xA, 6, 1, NULL, attr, buf);
1005+}
1006+
1007+static ssize_t show_battery_current_now(
1008+ struct device *dev, struct device_attribute *attr, char *buf)
1009+{
1010+ /* units: mA. SBS spec v1.1 p24: Current() */
1011+ return show_tp_ec_bat_s16(1, 8, 1, 0, attr, buf);
1012+}
1013+
1014+static ssize_t show_battery_current_avg(
1015+ struct device *dev, struct device_attribute *attr, char *buf)
1016+{
1017+ /* units: mA. SBS spec v1.1 p24: AverageCurrent() */
1018+ return show_tp_ec_bat_s16(1, 10, 1, 0, attr, buf);
1019+}
1020+
1021+static ssize_t show_battery_charging_max_current(
1022+ struct device *dev, struct device_attribute *attr, char *buf)
1023+{
1024+ /* units: mA. SBS spec v1.1 p36,38: ChargingCurrent() */
1025+ return show_tp_ec_bat_s16(9, 6, 1, 0, attr, buf);
1026+}
1027+
1028+static ssize_t show_battery_power_now(
1029+ struct device *dev, struct device_attribute *attr, char *buf)
1030+{
1031+ /* units: mW. SBS spec v1.1: Voltage()*Current() */
1032+ return show_tp_ec_bat_power(1, 6, 8, attr, buf);
1033+}
1034+
1035+static ssize_t show_battery_power_avg(
1036+ struct device *dev, struct device_attribute *attr, char *buf)
1037+{
1038+ /* units: mW. SBS spec v1.1: Voltage()*AverageCurrent() */
1039+ return show_tp_ec_bat_power(1, 6, 10, attr, buf);
1040+}
1041+
1042+static ssize_t show_battery_remaining_percent(
1043+ struct device *dev, struct device_attribute *attr, char *buf)
1044+{
1045+ /* units: percent. SBS spec v1.1 p25: RelativeStateOfCharge() */
1046+ return show_tp_ec_bat_u16(1, 12, 1, NULL, attr, buf);
1047+}
1048+
1049+static ssize_t show_battery_remaining_percent_error(
1050+ struct device *dev, struct device_attribute *attr, char *buf)
1051+{
1052+ /* units: percent. SBS spec v1.1 p25: MaxError() */
1053+ return show_tp_ec_bat_u16(9, 4, 1, NULL, attr, buf);
1054+}
1055+
1056+static ssize_t show_battery_remaining_charging_time(
1057+ struct device *dev, struct device_attribute *attr, char *buf)
1058+{
1059+ /* units: minutes. SBS spec v1.1 p27: AverageTimeToFull() */
1060+ return show_tp_ec_bat_u16(2, 8, 1, "not_charging", attr, buf);
1061+}
1062+
1063+static ssize_t show_battery_remaining_running_time(
1064+ struct device *dev, struct device_attribute *attr, char *buf)
1065+{
1066+ /* units: minutes. SBS spec v1.1 p27: RunTimeToEmpty() */
1067+ return show_tp_ec_bat_u16(2, 6, 1, "not_discharging", attr, buf);
1068+}
1069+
1070+static ssize_t show_battery_remaining_running_time_now(
1071+ struct device *dev, struct device_attribute *attr, char *buf)
1072+{
1073+ /* units: minutes. SBS spec v1.1 p27: RunTimeToEmpty() */
1074+ return show_tp_ec_bat_u16(2, 4, 1, "not_discharging", attr, buf);
1075+}
1076+
1077+static ssize_t show_battery_remaining_capacity(
1078+ struct device *dev, struct device_attribute *attr, char *buf)
1079+{
1080+ /* units: mWh. SBS spec v1.1 p26. */
1081+ return show_tp_ec_bat_u16(1, 14, 10, "", attr, buf);
1082+}
1083+
1084+static ssize_t show_battery_last_full_capacity(
1085+ struct device *dev, struct device_attribute *attr, char *buf)
1086+{
1087+ /* units: mWh. SBS spec v1.1 p26: FullChargeCapacity() */
1088+ return show_tp_ec_bat_u16(2, 2, 10, "", attr, buf);
1089+}
1090+
1091+static ssize_t show_battery_design_capacity(
1092+ struct device *dev, struct device_attribute *attr, char *buf)
1093+{
1094+ /* units: mWh. SBS spec v1.1 p32: DesignCapacity() */
1095+ return show_tp_ec_bat_u16(3, 2, 10, "", attr, buf);
1096+}
1097+
1098+static ssize_t show_battery_cycle_count(
1099+ struct device *dev, struct device_attribute *attr, char *buf)
1100+{
1101+ /* units: ordinal. SBS spec v1.1 p32: CycleCount() */
1102+ return show_tp_ec_bat_u16(2, 12, 1, "", attr, buf);
1103+}
1104+
1105+static ssize_t show_battery_temperature(
1106+ struct device *dev, struct device_attribute *attr, char *buf)
1107+{
1108+ /* units: millicelsius. SBS spec v1.1: Temperature()*10 */
1109+ return show_tp_ec_bat_s16(1, 4, 100, -273100, attr, buf);
1110+}
1111+
1112+static ssize_t show_battery_serial(
1113+ struct device *dev, struct device_attribute *attr, char *buf)
1114+{
1115+ /* type: int. SBS spec v1.1 p34: SerialNumber() */
1116+ return show_tp_ec_bat_u16(3, 10, 1, "", attr, buf);
1117+}
1118+
1119+static ssize_t show_battery_manufacture_date(
1120+ struct device *dev, struct device_attribute *attr, char *buf)
1121+{
1122+ /* type: YYYY-MM-DD. SBS spec v1.1 p34: ManufactureDate() */
1123+ return show_tp_ec_bat_date(3, 8, attr, buf);
1124+}
1125+
1126+static ssize_t show_battery_first_use_date(
1127+ struct device *dev, struct device_attribute *attr, char *buf)
1128+{
1129+ /* type: YYYY-MM-DD */
1130+ return show_tp_ec_bat_date(8, 2, attr, buf);
1131+}
1132+
1133+/**
1134+ * show_battery_dump - show the battery's dump attribute
1135+ * The dump attribute gives a hex dump of all EC readouts related to a
1136+ * battery. Some of the enumerated values don't really exist (i.e., the
1137+ * EC function just leaves them untouched); we use a kludge to detect and
1138+ * denote these.
1139+ */
1140+#define MIN_DUMP_ARG0 0x00
1141+#define MAX_DUMP_ARG0 0x0a /* 0x0b is useful too but hangs old EC firmware */
1142+static ssize_t show_battery_dump(
1143+ struct device *dev, struct device_attribute *attr, char *buf)
1144+{
1145+ int i;
1146+ char *p = buf;
1147+ int bat = attr_get_bat(attr);
1148+ u8 arg0; /* first argument to EC */
1149+ u8 rowa[TP_CONTROLLER_ROW_LEN],
1150+ rowb[TP_CONTROLLER_ROW_LEN];
1151+ const u8 junka = 0xAA,
1152+ junkb = 0x55; /* junk values for testing changes */
1153+ int ret;
1154+
1155+ for (arg0 = MIN_DUMP_ARG0; arg0 <= MAX_DUMP_ARG0; ++arg0) {
1156+ if ((p-buf) > PAGE_SIZE-TP_CONTROLLER_ROW_LEN*5)
1157+ return -ENOMEM; /* don't overflow sysfs buf */
1158+ /* Read raw twice with different junk values,
1159+ * to detect unused output bytes which are left unchaged: */
1160+ ret = read_tp_ec_row(arg0, bat, junka, rowa);
1161+ if (ret)
1162+ return ret;
1163+ ret = read_tp_ec_row(arg0, bat, junkb, rowb);
1164+ if (ret)
1165+ return ret;
1166+ for (i = 0; i < TP_CONTROLLER_ROW_LEN; i++) {
1167+ if (rowa[i] == junka && rowb[i] == junkb)
1168+ p += sprintf(p, "-- "); /* unused by EC */
1169+ else
1170+ p += sprintf(p, "%02x ", rowa[i]);
1171+ }
1172+ p += sprintf(p, "\n");
1173+ }
1174+ return p-buf;
1175+}
1176+
1177+
1178+/*********************************************************************
1179+ * sysfs attribute I/O, other than batteries
1180+ */
1181+
1182+static ssize_t show_ac_connected(
1183+ struct device *dev, struct device_attribute *attr, char *buf)
1184+{
1185+ int ret = power_device_present(0xFF);
1186+ if (ret < 0)
1187+ return ret;
1188+ return sprintf(buf, "%d\n", ret); /* type: boolean */
1189+}
1190+
1191+/*********************************************************************
1192+ * The the "smapi_request" sysfs attribute executes a raw SMAPI call.
1193+ * You write to make a request and read to get the result. The state
1194+ * is saved globally rather than per fd (sysfs limitation), so
1195+ * simultaenous requests may get each other's results! So this is for
1196+ * development and debugging only.
1197+ */
1198+#define MAX_SMAPI_ATTR_ANSWER_LEN 128
1199+static char smapi_attr_answer[MAX_SMAPI_ATTR_ANSWER_LEN] = "";
1200+
1201+static ssize_t show_smapi_request(struct device *dev,
1202+ struct device_attribute *attr, char *buf)
1203+{
1204+ int ret = snprintf(buf, PAGE_SIZE, "%s", smapi_attr_answer);
1205+ smapi_attr_answer[0] = '\0';
1206+ return ret;
1207+}
1208+
1209+static ssize_t store_smapi_request(struct device *dev,
1210+ struct device_attribute *attr,
1211+ const char *buf, size_t count)
1212+{
1213+ unsigned int inEBX, inECX, inEDI, inESI;
1214+ u32 outEBX, outECX, outEDX, outEDI, outESI;
1215+ const char *msg;
1216+ int ret;
1217+ if (sscanf(buf, "%x %x %x %x", &inEBX, &inECX, &inEDI, &inESI) != 4) {
1218+ smapi_attr_answer[0] = '\0';
1219+ return -EINVAL;
1220+ }
1221+ ret = smapi_request(
1222+ inEBX, inECX, inEDI, inESI,
1223+ &outEBX, &outECX, &outEDX, &outEDI, &outESI, &msg);
1224+ snprintf(smapi_attr_answer, MAX_SMAPI_ATTR_ANSWER_LEN,
1225+ "%x %x %x %x %x %d '%s'\n",
1226+ (unsigned int)outEBX, (unsigned int)outECX,
1227+ (unsigned int)outEDX, (unsigned int)outEDI,
1228+ (unsigned int)outESI, ret, msg);
1229+ if (ret)
1230+ return ret;
1231+ else
1232+ return count;
1233+}
1234+
1235+/*********************************************************************
1236+ * Power management: the embedded controller forgets the battery
1237+ * thresholds when the system is suspended to disk and unplugged from
1238+ * AC and battery, so we restore it upon resume.
1239+ */
1240+
1241+static int saved_threshs[4] = {-1, -1, -1, -1}; /* -1 = don't know */
1242+
1243+static int tp_suspend(struct platform_device *dev, pm_message_t state)
1244+{
1245+ int restore = (state.event == PM_EVENT_HIBERNATE ||
1246+ state.event == PM_EVENT_FREEZE);
1247+ if (!restore || get_real_thresh(0, THRESH_STOP , &saved_threshs[0]))
1248+ saved_threshs[0] = -1;
1249+ if (!restore || get_real_thresh(0, THRESH_START, &saved_threshs[1]))
1250+ saved_threshs[1] = -1;
1251+ if (!restore || get_real_thresh(1, THRESH_STOP , &saved_threshs[2]))
1252+ saved_threshs[2] = -1;
1253+ if (!restore || get_real_thresh(1, THRESH_START, &saved_threshs[3]))
1254+ saved_threshs[3] = -1;
1255+ DPRINTK("suspend saved: %d %d %d %d", saved_threshs[0],
1256+ saved_threshs[1], saved_threshs[2], saved_threshs[3]);
1257+ return 0;
1258+}
1259+
1260+static int tp_resume(struct platform_device *dev)
1261+{
1262+ DPRINTK("resume restoring: %d %d %d %d", saved_threshs[0],
1263+ saved_threshs[1], saved_threshs[2], saved_threshs[3]);
1264+ if (saved_threshs[0] >= 0)
1265+ set_real_thresh(0, THRESH_STOP , saved_threshs[0]);
1266+ if (saved_threshs[1] >= 0)
1267+ set_real_thresh(0, THRESH_START, saved_threshs[1]);
1268+ if (saved_threshs[2] >= 0)
1269+ set_real_thresh(1, THRESH_STOP , saved_threshs[2]);
1270+ if (saved_threshs[3] >= 0)
1271+ set_real_thresh(1, THRESH_START, saved_threshs[3]);
1272+ return 0;
1273+}
1274+
1275+
1276+/*********************************************************************
1277+ * Driver model
1278+ */
1279+
1280+static struct platform_driver tp_driver = {
1281+ .suspend = tp_suspend,
1282+ .resume = tp_resume,
1283+ .driver = {
1284+ .name = "smapi",
1285+ .owner = THIS_MODULE
1286+ },
1287+};
1288+
1289+
1290+/*********************************************************************
1291+ * Sysfs device model
1292+ */
1293+
1294+/* Attributes in /sys/devices/platform/smapi/ */
1295+
1296+static DEVICE_ATTR(ac_connected, 0444, show_ac_connected, NULL);
1297+static DEVICE_ATTR(smapi_request, 0600, show_smapi_request,
1298+ store_smapi_request);
1299+
1300+static struct attribute *tp_root_attributes[] = {
1301+ &dev_attr_ac_connected.attr,
1302+ &dev_attr_smapi_request.attr,
1303+ NULL
1304+};
1305+static struct attribute_group tp_root_attribute_group = {
1306+ .attrs = tp_root_attributes
1307+};
1308+
1309+/* Attributes under /sys/devices/platform/smapi/BAT{0,1}/ :
1310+ * Every attribute needs to be defined (i.e., statically allocated) for
1311+ * each battery, and then referenced in the attribute list of each battery.
1312+ * We use preprocessor voodoo to avoid duplicating the list of attributes 4
1313+ * times. The preprocessor output is just normal sysfs attributes code.
1314+ */
1315+
1316+/**
1317+ * FOREACH_BAT_ATTR - invoke the given macros on all our battery attributes
1318+ * @_BAT: battery number (0 or 1)
1319+ * @_ATTR_RW: macro to invoke for each read/write attribute
1320+ * @_ATTR_R: macro to invoke for each read-only attribute
1321+ */
1322+#define FOREACH_BAT_ATTR(_BAT, _ATTR_RW, _ATTR_R) \
1323+ _ATTR_RW(_BAT, start_charge_thresh) \
1324+ _ATTR_RW(_BAT, stop_charge_thresh) \
1325+ _ATTR_RW(_BAT, inhibit_charge_minutes) \
1326+ _ATTR_RW(_BAT, force_discharge) \
1327+ _ATTR_R(_BAT, installed) \
1328+ _ATTR_R(_BAT, state) \
1329+ _ATTR_R(_BAT, manufacturer) \
1330+ _ATTR_R(_BAT, model) \
1331+ _ATTR_R(_BAT, barcoding) \
1332+ _ATTR_R(_BAT, chemistry) \
1333+ _ATTR_R(_BAT, voltage) \
1334+ _ATTR_R(_BAT, group0_voltage) \
1335+ _ATTR_R(_BAT, group1_voltage) \
1336+ _ATTR_R(_BAT, group2_voltage) \
1337+ _ATTR_R(_BAT, group3_voltage) \
1338+ _ATTR_R(_BAT, current_now) \
1339+ _ATTR_R(_BAT, current_avg) \
1340+ _ATTR_R(_BAT, charging_max_current) \
1341+ _ATTR_R(_BAT, power_now) \
1342+ _ATTR_R(_BAT, power_avg) \
1343+ _ATTR_R(_BAT, remaining_percent) \
1344+ _ATTR_R(_BAT, remaining_percent_error) \
1345+ _ATTR_R(_BAT, remaining_charging_time) \
1346+ _ATTR_R(_BAT, remaining_running_time) \
1347+ _ATTR_R(_BAT, remaining_running_time_now) \
1348+ _ATTR_R(_BAT, remaining_capacity) \
1349+ _ATTR_R(_BAT, last_full_capacity) \
1350+ _ATTR_R(_BAT, design_voltage) \
1351+ _ATTR_R(_BAT, charging_max_voltage) \
1352+ _ATTR_R(_BAT, design_capacity) \
1353+ _ATTR_R(_BAT, cycle_count) \
1354+ _ATTR_R(_BAT, temperature) \
1355+ _ATTR_R(_BAT, serial) \
1356+ _ATTR_R(_BAT, manufacture_date) \
1357+ _ATTR_R(_BAT, first_use_date) \
1358+ _ATTR_R(_BAT, dump)
1359+
1360+/* Define several macros we will feed into FOREACH_BAT_ATTR: */
1361+
1362+#define DEFINE_BAT_ATTR_RW(_BAT,_NAME) \
1363+ static struct bat_device_attribute dev_attr_##_NAME##_##_BAT = { \
1364+ .dev_attr = __ATTR(_NAME, 0644, show_battery_##_NAME, \
1365+ store_battery_##_NAME), \
1366+ .bat = _BAT \
1367+ };
1368+
1369+#define DEFINE_BAT_ATTR_R(_BAT,_NAME) \
1370+ static struct bat_device_attribute dev_attr_##_NAME##_##_BAT = { \
1371+ .dev_attr = __ATTR(_NAME, 0644, show_battery_##_NAME, 0), \
1372+ .bat = _BAT \
1373+ };
1374+
1375+#define REF_BAT_ATTR(_BAT,_NAME) \
1376+ &dev_attr_##_NAME##_##_BAT.dev_attr.attr,
1377+
1378+/* This provide all attributes for one battery: */
1379+
1380+#define PROVIDE_BAT_ATTRS(_BAT) \
1381+ FOREACH_BAT_ATTR(_BAT, DEFINE_BAT_ATTR_RW, DEFINE_BAT_ATTR_R) \
1382+ static struct attribute *tp_bat##_BAT##_attributes[] = { \
1383+ FOREACH_BAT_ATTR(_BAT, REF_BAT_ATTR, REF_BAT_ATTR) \
1384+ NULL \
1385+ }; \
1386+ static struct attribute_group tp_bat##_BAT##_attribute_group = { \
1387+ .name = "BAT" #_BAT, \
1388+ .attrs = tp_bat##_BAT##_attributes \
1389+ };
1390+
1391+/* Finally genereate the attributes: */
1392+
1393+PROVIDE_BAT_ATTRS(0)
1394+PROVIDE_BAT_ATTRS(1)
1395+
1396+/* List of attribute groups */
1397+
1398+static struct attribute_group *attr_groups[] = {
1399+ &tp_root_attribute_group,
1400+ &tp_bat0_attribute_group,
1401+ &tp_bat1_attribute_group,
1402+ NULL
1403+};
1404+
1405+
1406+/*********************************************************************
1407+ * Init and cleanup
1408+ */
1409+
1410+static struct attribute_group **next_attr_group; /* next to register */
1411+
1412+static int __init tp_init(void)
1413+{
1414+ int ret;
1415+ printk(KERN_INFO "tp_smapi " TP_VERSION " loading...\n");
1416+
1417+ ret = find_smapi_port();
1418+ if (ret < 0)
1419+ goto err;
1420+ else
1421+ smapi_port = ret;
1422+
1423+ if (!request_region(smapi_port, 1, "smapi")) {
1424+ printk(KERN_ERR "tp_smapi cannot claim port 0x%x\n",
1425+ smapi_port);
1426+ ret = -ENXIO;
1427+ goto err;
1428+ }
1429+
1430+ if (!request_region(SMAPI_PORT2, 1, "smapi")) {
1431+ printk(KERN_ERR "tp_smapi cannot claim port 0x%x\n",
1432+ SMAPI_PORT2);
1433+ ret = -ENXIO;
1434+ goto err_port1;
1435+ }
1436+
1437+ ret = platform_driver_register(&tp_driver);
1438+ if (ret)
1439+ goto err_port2;
1440+
1441+ pdev = platform_device_alloc("smapi", -1);
1442+ if (!pdev) {
1443+ ret = -ENOMEM;
1444+ goto err_driver;
1445+ }
1446+
1447+ ret = platform_device_add(pdev);
1448+ if (ret)
1449+ goto err_device_free;
1450+
1451+ for (next_attr_group = attr_groups; *next_attr_group;
1452+ ++next_attr_group) {
1453+ ret = sysfs_create_group(&pdev->dev.kobj, *next_attr_group);
1454+ if (ret)
1455+ goto err_attr;
1456+ }
1457+
1458+ printk(KERN_INFO "tp_smapi successfully loaded (smapi_port=0x%x).\n",
1459+ smapi_port);
1460+ return 0;
1461+
1462+err_attr:
1463+ while (--next_attr_group >= attr_groups)
1464+ sysfs_remove_group(&pdev->dev.kobj, *next_attr_group);
1465+ platform_device_unregister(pdev);
1466+err_device_free:
1467+ platform_device_put(pdev);
1468+err_driver:
1469+ platform_driver_unregister(&tp_driver);
1470+err_port2:
1471+ release_region(SMAPI_PORT2, 1);
1472+err_port1:
1473+ release_region(smapi_port, 1);
1474+err:
1475+ printk(KERN_ERR "tp_smapi init failed (ret=%d)!\n", ret);
1476+ return ret;
1477+}
1478+
1479+static void __exit tp_exit(void)
1480+{
1481+ while (next_attr_group && --next_attr_group >= attr_groups)
1482+ sysfs_remove_group(&pdev->dev.kobj, *next_attr_group);
1483+ platform_device_unregister(pdev);
1484+ platform_driver_unregister(&tp_driver);
1485+ release_region(SMAPI_PORT2, 1);
1486+ if (smapi_port)
1487+ release_region(smapi_port, 1);
1488+
1489+ printk(KERN_INFO "tp_smapi unloaded.\n");
1490+}
1491+
1492+module_init(tp_init);
1493+module_exit(tp_exit);
--- /dev/null
+++ b/wacom/Android.mk
@@ -0,0 +1,24 @@
1+# Copyright 2011 The Android-x86 Open Source Project
2+
3+#ifeq ($(BOARD_USES_WACOMINPUT),true)
4+
5+LOCAL_PATH := $(call my-dir)
6+include $(CLEAR_VARS)
7+
8+LOCAL_SRC_FILES:= wactablet.c wacserial.c wacusb.c wacom-input.c
9+
10+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
11+
12+LOCAL_CFLAGS := -O2 -Wall -Wno-unused-parameter
13+
14+ifeq ($(TARGET_ARCH),x86)
15+LOCAL_CFLAGS += -Ulinux
16+endif
17+
18+LOCAL_MODULE := wacom-input
19+LOCAL_MODULE_TAGS := optional
20+#LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
21+
22+include $(BUILD_EXECUTABLE)
23+
24+#endif
--- /dev/null
+++ b/wacom/Makefile
@@ -0,0 +1,17 @@
1+CC=gcc
2+CFLAGS=-c -Wall -O2
3+#LDFLAGS=-lncurses
4+SOURCES=wactablet.c wacserial.c wacusb.c wacom-input.c
5+OBJECTS=$(SOURCES:.c=.o)
6+EXECUTABLE=wacom-input
7+
8+all: $(SOURCES) $(EXECUTABLE)
9+
10+$(EXECUTABLE): $(OBJECTS)
11+ $(CC) $(LDFLAGS) $(OBJECTS) -o $@
12+
13+.c.o:
14+ $(CC) $(CFLAGS) $< -o $@
15+
16+clean:
17+ rm $(OBJECTS) $(EXECUTABLE)
--- /dev/null
+++ b/wacom/wacom-input.c
@@ -0,0 +1,241 @@
1+/*****************************************************************************
2+ ** wacom-input.c
3+ **
4+ ** Copyright (C) 2011 Stefan Seidel
5+ **
6+ ** This program is free software; you can redistribute it and/or
7+ ** modify it under the terms of the GNU General Public License
8+ ** as published by the Free Software Foundation; either version 2
9+ ** of the License, or (at your option) any later version.
10+ **
11+ ** This program is distributed in the hope that it will be useful,
12+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ ** GNU General Public License for more details.
15+ **
16+ ** You should have received a copy of the GNU General Public License
17+ ** along with this program; if not, write to the Free Software
18+ ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19+ **
20+ **
21+ ** Code inspired by wacdump.c from http://linuxwacom.sourceforge.net and
22+ ** uniput-sample.c from http://thiemonge.org/getting-started-with-uinput
23+ **
24+ **
25+ ** Version history:
26+ ** 0.1 - 2011-03-29 - initial support for "tpc" device
27+ ** 0.2 - 2011-04-13 - support command-line options for device type, node, pressure
28+ **
29+ ****************************************************************************/
30+
31+#include "wactablet.h"
32+#include "wacserial.h"
33+#include "wacusb.h"
34+
35+#include <ctype.h>
36+#include <time.h>
37+#include <stdio.h>
38+#include <stdlib.h>
39+#include <string.h>
40+#include <unistd.h>
41+#include <fcntl.h>
42+#include <errno.h>
43+#include <signal.h>
44+#include <linux/input.h>
45+#include <linux/uinput.h>
46+
47+#define die(str, args...) do { \
48+ perror(str, ## args); \
49+ exit(EXIT_FAILURE); \
50+ } while(0)
51+
52+int fd;
53+WACOMENGINE hEngine = NULL;
54+WACOMTABLET hTablet = NULL;
55+
56+void wacom_report_event(__u16 type, __u16 code, __s32 value) {
57+ struct input_event ev;
58+ if (value == -1) {
59+ return;
60+ }
61+ memset(&ev, 0, sizeof(struct input_event));
62+ ev.type = type;
63+ ev.code = code;
64+ ev.value = value;
65+ if (write(fd, &ev, sizeof(struct input_event)) < 0)
66+ perror("error: write");
67+}
68+
69+static void signal_handler(int signo) {
70+ if (ioctl(fd, UI_DEV_DESTROY) < 0) {
71+ die("error: cannot destroy uinput device\n");
72+ }
73+ close(fd);
74+ WacomCloseTablet(hTablet);
75+ WacomTermEngine(hEngine);
76+ exit(EXIT_SUCCESS);
77+}
78+
79+int main(int argc, char** argv) {
80+ const char* arg;
81+ const char* devName = "tpc";
82+ const char* devNode = "/dev/ttyS0";
83+ struct uinput_user_dev uidev;
84+ WACOMSTATE state = WACOMSTATE_INIT;
85+ WACOMMODEL model = { 0 };
86+ unsigned char uchBuf[64];
87+ int nLength = 0;
88+ int minPress = 20;
89+
90+ // parse arguments
91+ while (*argv) {
92+ arg = *(argv++);
93+
94+ /* handle options */
95+ if (arg[0] == '-') {
96+ /* device type */
97+ if (strcmp(arg, "-h") == 0) {
98+ fprintf(
99+ stderr,
100+ "Usage: wacom-input [-t DeviceType] [-d DeviceNode] [-p PressureThreshold]\n\t-t defaults to \"tpc\"\n\t-d defaults to \"/dev/ttyS0\"\n\t-p defaults to 40\n");
101+ exit(0);
102+ /* device type */
103+ } else if (strcmp(arg, "-t") == 0) {
104+ arg = *(argv++);
105+ if (arg == NULL || arg[0] == '-') {
106+ die("Missing device type");
107+ }
108+ devName = arg;
109+ /* device node */
110+ } else if (strcmp(arg, "-d") == 0) {
111+ arg = *(argv++);
112+ if (arg == NULL || arg[0] == '-') {
113+ die("Missing device node");
114+ }
115+ devNode = arg;
116+ /* pressure */
117+ } else if (strcmp(arg, "-p") == 0) {
118+ arg = *(argv++);
119+ if (arg == NULL || (minPress = atoi(arg)) == 0) {
120+ die("Wrong pressure threshold");
121+ }
122+ }
123+ }
124+ }
125+ // end parse arguments
126+
127+ if (signal(SIGINT, signal_handler) == SIG_ERR) {
128+ die("error registering signal handler\n");
129+ }
130+
131+ // connect to wacom device
132+ hEngine = WacomInitEngine();
133+ if (!hEngine) {
134+ close(fd);
135+ die("failed to open tablet engine");
136+ }
137+
138+ /* open tablet */
139+ model.uClass = WACOMCLASS_SERIAL;
140+ model.uDevice = WacomGetDeviceFromName(devName, model.uClass);
141+ hTablet = WacomOpenTablet(hEngine, devNode, &model);
142+ if (!hTablet) {
143+ close(fd);
144+ WacomTermEngine(hEngine);
145+ die ("WacomOpenTablet");
146+ }
147+ WacomGetState(hTablet, &state);
148+ // wacom device is set up properly
149+
150+ // set up uinput
151+ fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
152+ if (fd < 0) {
153+ fd = open("/dev/input/uinput", O_WRONLY | O_NONBLOCK);
154+ }
155+
156+ if (fd < 0)
157+ die("error: opening /dev/[input/]uinput failed");
158+
159+ // report that we have TOUCH events ...
160+ if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0)
161+ die("error: ioctl");
162+ if (ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH) < 0)
163+ die("error: ioctl");
164+
165+ // and absolute x, y, pressure data
166+ if (ioctl(fd, UI_SET_EVBIT, EV_ABS) < 0)
167+ die("error: ioctl");
168+ if (ioctl(fd, UI_SET_ABSBIT, ABS_X) < 0)
169+ die("error: ioctl");
170+ if (ioctl(fd, UI_SET_ABSBIT, ABS_Y) < 0)
171+ die("error: ioctl");
172+ if (ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE) < 0)
173+ die("error: ioctl");
174+
175+ // this is for simulated mouse middle/right button
176+ // if(ioctl(fd, UI_SET_KEYBIT, BTN_MOUSE) < 0)
177+ // die("error: ioctl");
178+ // if(ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT) < 0)
179+ // die("error: ioctl");
180+ // if(ioctl(fd, UI_SET_KEYBIT, BTN_LEFT) < 0)
181+ // die("error: ioctl");
182+
183+ // register uinput device
184+ memset(&uidev, 0, sizeof(uidev));
185+ uidev.absmin[ABS_X] = state.values[WACOMFIELD_POSITION_X].nMin;
186+ uidev.absmax[ABS_X] = state.values[WACOMFIELD_POSITION_X].nMax;
187+ uidev.absmin[ABS_Y] = state.values[WACOMFIELD_POSITION_Y].nMin;
188+ uidev.absmax[ABS_Y] = state.values[WACOMFIELD_POSITION_Y].nMax;
189+ uidev.absmin[ABS_PRESSURE] = state.values[WACOMFIELD_PRESSURE].nMin;
190+ uidev.absmax[ABS_PRESSURE] = state.values[WACOMFIELD_PRESSURE].nMax;
191+ // this could be more detailed, but in the end, who cares?
192+ snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "wacom-input");
193+ uidev.id.bustype = BUS_RS232;
194+ uidev.id.vendor = 0x056a;
195+ uidev.id.product = 0xffff;
196+ uidev.id.version = 1;
197+
198+ if (write(fd, &uidev, sizeof(uidev)) < 0)
199+ die("error: set virtual device info 1");
200+
201+ if (ioctl(fd, UI_DEV_CREATE) < 0)
202+ die("error: create uinput device 1");
203+ // uinput is set up
204+
205+ while (1) {
206+ if ((nLength = WacomReadRaw(hTablet, uchBuf, sizeof(uchBuf))) < 0) {
207+ continue;
208+ }
209+ if (WacomParseData(hTablet, uchBuf, nLength, &state)) {
210+ continue;
211+ }
212+ if (!state.values[WACOMFIELD_PROXIMITY].nValue) {
213+ // no tool in proximity
214+ wacom_report_event(EV_ABS, ABS_PRESSURE, 0);
215+ wacom_report_event(EV_KEY, BTN_TOUCH, 0);
216+ // wacom_report_event(EV_KEY, BTN_RIGHT, 0);
217+ // wacom_report_event(EV_KEY, BTN_MIDDLE, 0);
218+ wacom_report_event(EV_SYN, SYN_REPORT, 0);
219+ continue;
220+ }
221+
222+ wacom_report_event(EV_ABS, ABS_X,
223+ state.values[WACOMFIELD_POSITION_X].nValue);
224+ wacom_report_event(EV_ABS, ABS_Y,
225+ state.values[WACOMFIELD_POSITION_Y].nValue);
226+ wacom_report_event(EV_ABS, ABS_PRESSURE,
227+ state.values[WACOMFIELD_PRESSURE].nValue);
228+ wacom_report_event(EV_KEY, BTN_TOUCH,
229+ state.values[WACOMFIELD_PRESSURE].nValue > minPress);
230+ // wacom_report_event(EV_KEY, BTN_RIGHT, state.values[WACOMFIELD_BUTTONS].nValue == WACOMBUTTON_STYLUS);
231+ // wacom_report_event(EV_KEY, BTN_MIDDLE, state.values[WACOMFIELD_TOOLTYPE].nValue == WACOMTOOLTYPE_ERASER);
232+ wacom_report_event(EV_SYN, SYN_REPORT, 0);
233+ }
234+
235+ if (ioctl(fd, UI_DEV_DESTROY) < 0)
236+ die("error: ioctl");
237+
238+ close(fd);
239+
240+ return 0;
241+}
--- /dev/null
+++ b/wacom/wacserial.c
@@ -0,0 +1,1711 @@
1+/*****************************************************************************
2+** wacserial.c
3+**
4+** Copyright (C) 2002, 2003 - John E. Joganic
5+** Copyright (C) 2002 - 2008 - Ping Cheng
6+**
7+** This program is free software; you can redistribute it and/or
8+** modify it under the terms of the GNU General Public License
9+** as published by the Free Software Foundation; either version 2
10+** of the License, or (at your option) any later version.
11+**
12+** This program is distributed in the hope that it will be useful,
13+** but WITHOUT ANY WARRANTY; without even the implied warranty of
14+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+** GNU General Public License for more details.
16+**
17+** You should have received a copy of the GNU General Public License
18+** along with this program; if not, write to the Free Software
19+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20+**
21+****************************************************************************/
22+
23+#include "wacserial.h"
24+
25+#include <stdio.h>
26+#include <stdlib.h>
27+#include <string.h>
28+#include <errno.h>
29+#include <termios.h>
30+#include <ctype.h>
31+#include <unistd.h>
32+#include <assert.h>
33+
34+/*****************************************************************************
35+** Serial Tablet Object
36+*****************************************************************************/
37+
38+typedef struct _SERIALTABLET SERIALTABLET;
39+typedef struct _SERIALSUBTYPE SERIALSUBTYPE;
40+typedef struct _SERIALDEVICE SERIALDEVICE;
41+typedef struct _SERIALVENDOR SERIALVENDOR;
42+
43+typedef int (*IDENTFUNC)(SERIALTABLET* pSerial);
44+typedef int (*INITFUNC)(SERIALTABLET* pSerial);
45+typedef int (*PARSEFUNC)(SERIALTABLET* pSerial, const unsigned char* puchData,
46+ unsigned int uLength, WACOMSTATE* pState);
47+
48+struct _SERIALTABLET
49+{
50+ WACOMTABLET_PRIV tablet;
51+ WACOMENGINE hEngine;
52+ int fd;
53+ SERIALVENDOR* pVendor;
54+ SERIALDEVICE* pDevice;
55+ SERIALSUBTYPE* pSubType;
56+ unsigned int uPacketLength;
57+ int nVerMajor, nVerMinor, nVerRelease;
58+ IDENTFUNC pfnIdent;
59+ INITFUNC pfnInit;
60+ PARSEFUNC pfnParse;
61+ int nToolID;
62+ WACOMSTATE state;
63+ int nBitErrors;
64+ WACOMMODEL modelRequested;
65+};
66+
67+/*****************************************************************************
68+** Internal structures
69+*****************************************************************************/
70+
71+struct _SERIALSUBTYPE
72+{
73+ const char* pszName;
74+ const char* pszDesc;
75+ unsigned int uSubType;
76+ const char* pszIdent;
77+ INITFUNC pfnInit;
78+};
79+
80+struct _SERIALDEVICE
81+{
82+ const char* pszName;
83+ const char* pszDesc;
84+ unsigned int uDevice;
85+ SERIALSUBTYPE* pSubTypes;
86+ int nProtocol;
87+ unsigned int uPacketLength;
88+ unsigned int uCaps;
89+ int nMinBaudRate;
90+ IDENTFUNC pfnIdent;
91+};
92+
93+struct _SERIALVENDOR
94+{
95+ const char* pszName;
96+ const char* pszDesc;
97+ unsigned int uVendor;
98+ SERIALDEVICE* pDevices;
99+};
100+
101+/*****************************************************************************
102+** Static operations
103+*****************************************************************************/
104+
105+static void SerialClose(WACOMTABLET_PRIV* pTablet);
106+static WACOMMODEL SerialGetModel(WACOMTABLET_PRIV* pTablet);
107+static const char* SerialGetVendorName(WACOMTABLET_PRIV* pTablet);
108+static const char* SerialGetClassName(WACOMTABLET_PRIV* pTablet);
109+static const char* SerialGetDeviceName(WACOMTABLET_PRIV* pTablet);
110+static const char* SerialGetSubTypeName(WACOMTABLET_PRIV* pTablet);
111+static const char* SerialGetModelName(WACOMTABLET_PRIV* pTablet);
112+static int SerialGetROMVer(WACOMTABLET_PRIV* pTablet, int* pnMajor,
113+ int* pnMinor, int* pnRelease);
114+static int SerialGetCaps(WACOMTABLET_PRIV* pTablet);
115+static int SerialGetState(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState);
116+static int SerialGetFD(WACOMTABLET_PRIV* pTablet);
117+static int SerialReadRaw(WACOMTABLET_PRIV* pTablet, unsigned char* puchData,
118+ unsigned int uSize);
119+static int SerialParseData(WACOMTABLET_PRIV* pTablet,
120+ const unsigned char* puchData, unsigned int uLength,
121+ WACOMSTATE* pState);
122+
123+static int SerialReset(SERIALTABLET* pSerial, WACOMMODEL* pModel);
124+static int SerialConfigTTY(SERIALTABLET* pSerial);
125+static int SerialResetAtBaud(SERIALTABLET* pSerial, struct termios* pTIOS,
126+ int nBaud);
127+static int SerialSetDevice(SERIALTABLET* pSerial, SERIALVENDOR* pVendor,
128+ SERIALDEVICE* pDevice, SERIALSUBTYPE* pSubType);
129+
130+static int SerialIdentDefault(SERIALTABLET* pSerial);
131+static int SerialIdentTabletPC(SERIALTABLET* pSerial);
132+static int SerialInitTabletPC(SERIALTABLET* pSerial);
133+static int SerialIdentWacom(SERIALTABLET* pSerial);
134+static int SerialInitWacom(SERIALTABLET* pSerial);
135+
136+static int SerialParseWacomV(SERIALTABLET* pSerial,
137+ const unsigned char* puchData, unsigned int uLength,
138+ WACOMSTATE* pState);
139+static int SerialParseWacomIV_1_4(SERIALTABLET* pSerial,
140+ const unsigned char* puchData, unsigned int uLength,
141+ WACOMSTATE* pState);
142+static int SerialParseWacomIV_1_3(SERIALTABLET* pSerial,
143+ const unsigned char* puchData, unsigned int uLength,
144+ WACOMSTATE* pState);
145+static int SerialParseWacomIV_1_2(SERIALTABLET* pSerial,
146+ const unsigned char* puchData, unsigned int uLength,
147+ WACOMSTATE* pState);
148+static int SerialParseTabletPC(SERIALTABLET* pSerial,
149+ const unsigned char* puchData, unsigned int uLength,
150+ WACOMSTATE* pState);
151+
152+static void SerialError(SERIALTABLET* pSerial, const char* pszFmt, ...);
153+static void SerialWarn(SERIALTABLET* pSerial, const char* pszFmt, ...);
154+static void SerialInfo(SERIALTABLET* pSerial, const char* pszFmt, ...);
155+static void SerialDump(SERIALTABLET* pSerial, const void* pvData, int nCnt);
156+/* NOT USED, YET
157+static void SerialCritical(SERIALTABLET* pSerial, const char* pszFmt, ...);
158+static void SerialDebug(SERIALTABLET* pSerial, const char* pszFmt, ...);
159+static void SerialTrace(SERIALTABLET* pSerial, const char* pszFmt, ...);
160+*/
161+
162+/*****************************************************************************
163+** Defines
164+*****************************************************************************/
165+
166+#ifndef BIT
167+#undef BIT
168+#define BIT(x) (1<<(x))
169+#endif
170+
171+#define WACOMVALID(x) BIT(WACOMFIELD_##x)
172+
173+#define ARTPAD_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
174+ WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
175+ WACOMVALID(PRESSURE))
176+
177+#define ARTPADII_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
178+ WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
179+ WACOMVALID(PRESSURE))
180+
181+#define DIGITIZER_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
182+ WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
183+ WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y))
184+
185+#define DIGITIZERII_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
186+ WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
187+ WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y))
188+
189+#define PENPARTNER_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
190+ WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
191+ WACOMVALID(PRESSURE))
192+
193+#define GRAPHIRE_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
194+ WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
195+ WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y))
196+
197+#define CINTIQ_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
198+ WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
199+ WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y))
200+
201+#define INTUOS_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(SERIAL)| \
202+ WACOMVALID(PROXIMITY)|WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)| \
203+ WACOMVALID(POSITION_Y)|WACOMVALID(ROTATION_Z)|WACOMVALID(DISTANCE)| \
204+ WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y)| \
205+ WACOMVALID(ABSWHEEL)|WACOMVALID(RELWHEEL)|WACOMVALID(THROTTLE))
206+
207+#define TABLETPC_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(SERIAL)| \
208+ WACOMVALID(PROXIMITY)|WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)| \
209+ WACOMVALID(POSITION_Y)|WACOMVALID(PRESSURE))
210+
211+#define INTUOS2_CAPS INTUOS_CAPS
212+
213+#define PROTOCOL_4 4
214+#define PROTOCOL_5 5
215+
216+#define WACOM_SUBTYPE(id,d,s) \
217+ { id, d, s, id, SerialInitWacom }
218+#define TPC_SUBTYPE(id,d,s) \
219+ { id, d, s, id, SerialInitTabletPC }
220+
221+#define WACOM_DEVICE_P4(n,d,i,s,c) \
222+ { n, d, i, s, PROTOCOL_4, 7, c, 9600, SerialIdentWacom }
223+#define WACOM_DEVICE_P5(n,d,i,s,c) \
224+ { n, d, i, s, PROTOCOL_5, 9, c, 9600, SerialIdentWacom }
225+#define TPC_DEVICE(n,d,i,s,c) \
226+ { n, d, i, s, 0, 9, c, 19200, SerialIdentTabletPC }
227+
228+/*****************************************************************************
229+** Globals
230+*****************************************************************************/
231+
232+ static SERIALSUBTYPE xArtPadII[] =
233+ {
234+ WACOM_SUBTYPE("KT-0405-R", "Wacom ArtPadII 4x5", 1),
235+ { NULL }
236+ };
237+
238+ static SERIALSUBTYPE xDigitizerII[] =
239+ {
240+ WACOM_SUBTYPE("UD-0608-R", "Wacom DigitizerII 6x8", 1),
241+ WACOM_SUBTYPE("UD-1212-R", "Wacom DigitizerII 12x12", 2),
242+ WACOM_SUBTYPE("UD-1218-R", "Wacom DigitizerII 12x18", 3),
243+ WACOM_SUBTYPE("UD-1825-R", "Wacom DigitizerII 18x25", 4),
244+ { NULL }
245+ };
246+
247+ static SERIALSUBTYPE xPenPartner[] =
248+ {
249+ WACOM_SUBTYPE("CT-0405-R", "Wacom PenPartner", 1),
250+ { NULL }
251+ };
252+
253+ static SERIALSUBTYPE xGraphire[] =
254+ {
255+ WACOM_SUBTYPE("ET-0405-R", "Wacom Graphire", 1),
256+ { NULL }
257+ };
258+
259+ static SERIALSUBTYPE xIntuos[] =
260+ {
261+ WACOM_SUBTYPE("GD-0405-R", "Wacom Intuos 4x5", 1),
262+ WACOM_SUBTYPE("GD-0608-R", "Wacom Intuos 6x8", 2),
263+ WACOM_SUBTYPE("GD-0912-R", "Wacom Intuos 9x12", 3),
264+ WACOM_SUBTYPE("GD-1212-R", "Wacom Intuos 12x12", 4),
265+ WACOM_SUBTYPE("GD-1218-R", "Wacom Intuos 12x18", 5),
266+ { NULL }
267+ };
268+
269+ static SERIALSUBTYPE xIntuos2[] =
270+ {
271+ WACOM_SUBTYPE("XD-0405-R", "Wacom Intuos2 4x5", 1),
272+ WACOM_SUBTYPE("XD-0608-R", "Wacom Intuos2 6x8", 2),
273+ WACOM_SUBTYPE("XD-0912-R", "Wacom Intuos2 9x12", 3),
274+ WACOM_SUBTYPE("XD-1212-R", "Wacom Intuos2 12x12", 4),
275+ WACOM_SUBTYPE("XD-1218-R", "Wacom Intuos2 12x18", 5),
276+ { NULL }
277+ };
278+
279+ static SERIALSUBTYPE xCintiq[] =
280+ {
281+ WACOM_SUBTYPE("PL-250", "Wacom PL-250", 1),
282+ WACOM_SUBTYPE("PL-270", "Wacom PL-270", 2),
283+ WACOM_SUBTYPE("PL-400", "Wacom PL-400", 3),
284+ WACOM_SUBTYPE("PL-500", "Wacom PL-500", 4),
285+ WACOM_SUBTYPE("PL-550", "Wacom PL-550", 5),
286+ WACOM_SUBTYPE("PL-600", "Wacom PL-600", 6),
287+ WACOM_SUBTYPE("PL-600SX", "Wacom PL-600SX", 7),
288+ WACOM_SUBTYPE("PL-800", "Wacom PL-800", 8),
289+ { NULL }
290+ };
291+
292+ static SERIALDEVICE xWacomDevices[] =
293+ {
294+ WACOM_DEVICE_P4("art", "ArtPad", WACOMDEVICE_ARTPAD,
295+ NULL, ARTPAD_CAPS),
296+ WACOM_DEVICE_P4("art2", "ArtPadII", WACOMDEVICE_ARTPADII,
297+ xArtPadII, ARTPADII_CAPS),
298+ WACOM_DEVICE_P4("dig", "Digitizer", WACOMDEVICE_DIGITIZER,
299+ NULL, DIGITIZERII_CAPS),
300+ WACOM_DEVICE_P4("dig2", "Digitizer II", WACOMDEVICE_DIGITIZERII,
301+ xDigitizerII, DIGITIZERII_CAPS),
302+ WACOM_DEVICE_P4("pp", "PenPartner", WACOMDEVICE_PENPARTNER,
303+ xPenPartner, PENPARTNER_CAPS),
304+ WACOM_DEVICE_P4("gr", "Graphire", WACOMDEVICE_GRAPHIRE,
305+ xGraphire, GRAPHIRE_CAPS),
306+ WACOM_DEVICE_P4("pl", "Cintiq (PL)", WACOMDEVICE_CINTIQ,
307+ xCintiq, CINTIQ_CAPS),
308+ WACOM_DEVICE_P5("int", "Intuos", WACOMDEVICE_INTUOS,
309+ xIntuos, INTUOS_CAPS),
310+ WACOM_DEVICE_P5("int2", "Intuos2", WACOMDEVICE_INTUOS2,
311+ xIntuos2, INTUOS2_CAPS),
312+ { NULL }
313+ };
314+
315+ /* This one is reverse engineered at this point */
316+ static SERIALSUBTYPE xTabletPC[] =
317+ {
318+ TPC_SUBTYPE("tpc", "Tablet PC Screen", 1),
319+ { NULL }
320+ };
321+
322+ static SERIALDEVICE xtpcDevices[] =
323+ {
324+ TPC_DEVICE("tpc", "TabletPC", WACOMDEVICE_TPC,
325+ xTabletPC, TABLETPC_CAPS),
326+ { NULL }
327+ };
328+
329+ static SERIALVENDOR xWacomVendor =
330+ { "wacom", "Wacom", WACOMVENDOR_WACOM, xWacomDevices };
331+
332+ static SERIALVENDOR xtpcVendor =
333+ { "Wacom", "Wacom", WACOMVENDOR_TPC, xtpcDevices };
334+
335+ static SERIALVENDOR* xVendors[] =
336+ {
337+ &xWacomVendor,
338+ &xtpcVendor,
339+ NULL
340+ };
341+
342+/*****************************************************************************
343+** Static Prototypes
344+*****************************************************************************/
345+
346+static int SerialSendReset(SERIALTABLET* pSerial);
347+static int SerialSendStop(SERIALTABLET* pSerial);
348+static int SerialSendStart(SERIALTABLET* pSerial);
349+
350+static int SerialSend(SERIALTABLET* pSerial, const char* pszData);
351+static int SerialSendRaw(SERIALTABLET* pSerial, const void* pvData,
352+ unsigned int uSize);
353+static int WacomFlush(SERIALTABLET* pSerial);
354+static int SerialSendRequest(SERIALTABLET* pSerial, const char* pszRequest,
355+ char* pchResponse, unsigned int uSize);
356+
357+/*****************************************************************************
358+** Public Functions
359+*****************************************************************************/
360+
361+typedef struct
362+{
363+ void (*pfnFree)(void* pv);
364+} DEVLIST_INTERNAL;
365+
366+static void SerialFreeDeviceList(void* pv)
367+{
368+ DEVLIST_INTERNAL* pInt = ((DEVLIST_INTERNAL*)pv) - 1;
369+ free(pInt);
370+}
371+
372+int WacomGetSupportedSerialDeviceList(WACOMDEVICEREC** ppList, int* pnSize)
373+{
374+ int nIndex=0, nCnt=0;
375+ DEVLIST_INTERNAL* pInt;
376+ SERIALDEVICE* pDev;
377+ SERIALVENDOR** ppVendor;
378+ WACOMDEVICEREC* pRec;
379+
380+ if (!ppList || !pnSize) { errno = EINVAL; return 1; }
381+
382+ /* for each vendor, count up devices */
383+ for (ppVendor=xVendors; *ppVendor; ++ppVendor)
384+ {
385+ /* count up devices */
386+ for (pDev=(*ppVendor)->pDevices; pDev->pszName; ++pDev, ++nCnt) ;
387+ }
388+
389+ /* allocate enough memory to hold internal structure and all records */
390+ pInt = (DEVLIST_INTERNAL*)malloc(sizeof(DEVLIST_INTERNAL) +
391+ (sizeof(WACOMDEVICEREC) * nCnt));
392+
393+ pInt->pfnFree = SerialFreeDeviceList;
394+ pRec = (WACOMDEVICEREC*)(pInt + 1);
395+
396+ /* for each vendor, add devices */
397+ for (ppVendor=xVendors; *ppVendor; ++ppVendor)
398+ {
399+ for (pDev=(*ppVendor)->pDevices; pDev->pszName; ++pDev, ++nIndex)
400+ {
401+ pRec[nIndex].pszName = pDev->pszName;
402+ pRec[nIndex].pszDesc = pDev->pszDesc;
403+ pRec[nIndex].pszVendorName = (*ppVendor)->pszName;
404+ pRec[nIndex].pszVendorDesc = (*ppVendor)->pszDesc;
405+ pRec[nIndex].pszClass = "serial";
406+ pRec[nIndex].model.uClass = WACOMCLASS_SERIAL;
407+ pRec[nIndex].model.uVendor = (*ppVendor)->uVendor;
408+ pRec[nIndex].model.uDevice = pDev->uDevice;
409+ pRec[nIndex].model.uSubType = 0;
410+ }
411+ }
412+ assert(nIndex == nCnt);
413+
414+ *ppList = pRec;
415+ *pnSize = nCnt;
416+ return 0;
417+}
418+
419+unsigned int WacomGetSerialDeviceFromName(const char* pszName)
420+{
421+ SERIALDEVICE* pDev;
422+ SERIALVENDOR** ppVendor;
423+
424+ if (!pszName) { errno = EINVAL; return 0; }
425+
426+ /* for each vendor, look for device */
427+ for (ppVendor=xVendors; *ppVendor; ++ppVendor)
428+ {
429+ /* count up devices */
430+ for (pDev=(*ppVendor)->pDevices; pDev->pszName; ++pDev)
431+ {
432+ if (strcasecmp(pszName,pDev->pszName) == 0)
433+ return pDev->uDevice;
434+ }
435+ }
436+
437+ errno = ENOENT;
438+ return 0;
439+}
440+
441+static int SerialFindModel(WACOMMODEL* pModel, SERIALVENDOR** ppVendor,
442+ SERIALDEVICE** ppDevice, SERIALSUBTYPE** ppSubType)
443+{
444+ SERIALVENDOR** ppPos;
445+ SERIALDEVICE* pDev;
446+ SERIALSUBTYPE* pSub;
447+
448+ /* device type must be specified */
449+ if (!pModel)
450+ { errno = EINVAL; return 1; }
451+
452+ /* no device specified, nothing found. */
453+ if (!pModel->uDevice)
454+ {
455+ *ppVendor = NULL;
456+ *ppDevice = NULL;
457+ *ppSubType = NULL;
458+ return 0;
459+ }
460+
461+ /* for each vendor */
462+ for (ppPos=xVendors; *ppPos; ++ppPos)
463+ {
464+ /* check vendor */
465+ if (!pModel->uVendor || (pModel->uVendor == (*ppPos)->uVendor))
466+ {
467+ /* for each device */
468+ for (pDev=(*ppPos)->pDevices; pDev->pszName; ++pDev)
469+ {
470+ /* if device matches */
471+ if (pModel->uDevice == pDev->uDevice)
472+ {
473+ /* no subtype specified, use it */
474+ if (!pModel->uSubType)
475+ {
476+ *ppVendor = *ppPos;
477+ *ppDevice = pDev;
478+ *ppSubType = NULL;
479+ return 0;
480+ }
481+
482+ /* for each subtype */
483+ for (pSub=pDev->pSubTypes; pSub->pszName; ++pSub)
484+ {
485+ /* if subtype matches */
486+ if (pModel->uSubType == pSub->uSubType)
487+ {
488+ *ppVendor = *ppPos;
489+ *ppDevice = pDev;
490+ *ppSubType = pSub;
491+ return 0;
492+ }
493+ }
494+
495+ /* wrong subtype? maybe try another vendor */
496+ if (!pModel->uVendor) break;
497+
498+ /* otherwise, no match. */
499+ errno = ENOENT;
500+ return 1;
501+ }
502+ } /* next device */
503+
504+ /* if vendor matches, but device does not, no match. */
505+ if (pModel->uVendor)
506+ {
507+ errno = ENOENT;
508+ return 1;
509+ }
510+ }
511+ } /* next vendor */
512+
513+ /* no match */
514+ errno = ENOENT;
515+ return 1;
516+}
517+
518+WACOMTABLET WacomOpenSerialTablet(WACOMENGINE hEngine, int fd,
519+ WACOMMODEL* pModel)
520+{
521+ SERIALTABLET* pSerial = NULL;
522+
523+ /* Allocate tablet */
524+ pSerial = (SERIALTABLET*)malloc(sizeof(SERIALTABLET));
525+ memset(pSerial,0,sizeof(*pSerial));
526+ pSerial->tablet.Close = SerialClose;
527+ pSerial->tablet.GetModel = SerialGetModel;
528+ pSerial->tablet.GetVendorName = SerialGetVendorName;
529+ pSerial->tablet.GetClassName = SerialGetClassName;
530+ pSerial->tablet.GetDeviceName = SerialGetDeviceName;
531+ pSerial->tablet.GetSubTypeName = SerialGetSubTypeName;
532+ pSerial->tablet.GetModelName = SerialGetModelName;
533+ pSerial->tablet.GetROMVer = SerialGetROMVer;
534+ pSerial->tablet.GetCaps = SerialGetCaps;
535+ pSerial->tablet.GetState = SerialGetState;
536+ pSerial->tablet.GetFD = SerialGetFD;
537+ pSerial->tablet.ReadRaw = SerialReadRaw;
538+ pSerial->tablet.ParseData = SerialParseData;
539+
540+ pSerial->hEngine = hEngine;
541+ pSerial->fd = fd;
542+ pSerial->state.uValueCnt = WACOMFIELD_MAX;
543+
544+ /* remember what model was request */
545+ if (pModel)
546+ pSerial->modelRequested = *pModel;
547+
548+ if (SerialReset(pSerial,pModel))
549+ {
550+ free(pSerial);
551+ return NULL;
552+ }
553+
554+ return (WACOMTABLET)pSerial;
555+}
556+
557+static int SerialReset(SERIALTABLET* pSerial, WACOMMODEL* pModel)
558+{
559+ SERIALVENDOR* pVendor = NULL;
560+ SERIALDEVICE* pDevice = NULL;
561+ SERIALSUBTYPE* pSubType = NULL;
562+
563+ /* If model is specified, break it down into vendor, device, and subtype */
564+ if (pModel && SerialFindModel(pModel,&pVendor,&pDevice,&pSubType))
565+ return 1;
566+
567+ /* Set the tablet device */
568+ if (SerialSetDevice(pSerial,pVendor,pDevice,pSubType))
569+ return 1;
570+
571+ /* configure the TTY for initial operation */
572+ if (SerialConfigTTY(pSerial))
573+ return 1;
574+
575+ /* Identify the tablet */
576+ if (!pSerial->pfnIdent || pSerial->pfnIdent(pSerial))
577+ return 1;
578+
579+ /* Initialize the tablet */
580+ if (!pSerial->pfnInit || pSerial->pfnInit(pSerial))
581+ return 1;
582+
583+ /* Send start */
584+ SerialSendStart(pSerial);
585+
586+ return 0;
587+}
588+
589+/*****************************************************************************
590+** Serial Tablet Functions
591+*****************************************************************************/
592+
593+static int SerialConfigTTY(SERIALTABLET* pSerial)
594+{
595+ struct termios tios;
596+ int nBaudRate = 9600;
597+
598+ /* configure tty */
599+ if (isatty(pSerial->fd))
600+ {
601+ /* set up default port parameters */
602+ if (tcgetattr (pSerial->fd, &tios))
603+ {
604+ SerialError(pSerial,"Failed to get port params: %s",
605+ strerror(errno));
606+ return 1;
607+ }
608+
609+ tios.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
610+ tios.c_oflag &= ~OPOST;
611+ tios.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
612+ tios.c_cflag &= ~(CSIZE|PARENB);
613+ tios.c_cflag |= CS8|CLOCAL;
614+ tios.c_cflag &= ~(CSTOPB); /* 1 stop bit */
615+ tios.c_cflag &= ~(CSIZE); /* 8 data bits */
616+ tios.c_cflag |= CS8;
617+ tios.c_cflag &= ~(PARENB); /* no parity */
618+ tios.c_iflag |= IXOFF; /* flow control XOff */
619+ tios.c_cc[VMIN] = 1; /* vmin value */
620+ tios.c_cc[VTIME] = 0; /* vtime value */
621+
622+ if (tcsetattr (pSerial->fd, TCSANOW, &tios))
623+ {
624+ SerialError(pSerial,"Failed to set port params: %s",
625+ strerror(errno));
626+ return 1;
627+ }
628+
629+ /* get minumum baud rate for given device, if specified */
630+ if (pSerial->pDevice)
631+ nBaudRate = pSerial->pDevice->nMinBaudRate;
632+
633+ /* set 38400 baud and reset */
634+ if (SerialResetAtBaud(pSerial,&tios,38400))
635+ return 1;
636+
637+ /* if valid, set 19200 baud and reset */
638+ if ((nBaudRate <= 19200) && (SerialResetAtBaud(pSerial,&tios,19200)))
639+ return 1;
640+
641+ /* if valid, set 9600 baud and reset */
642+ if ((nBaudRate <= 9600) && (SerialResetAtBaud(pSerial,&tios,9600)))
643+ return 1;
644+
645+ /* lower than 9600 baud? for testing, maybe */
646+ if ((nBaudRate < 9600) && (SerialResetAtBaud(pSerial,&tios,nBaudRate)))
647+ return 1;
648+ }
649+ else /* not tty */
650+ {
651+ if (SerialSendReset(pSerial)) return 1;
652+ }
653+
654+ /* Send stop */
655+ if (SerialSendStop(pSerial) || WacomFlush(pSerial))
656+ return 1;
657+
658+ return 0;
659+}
660+
661+
662+static int SerialSetDevice(SERIALTABLET* pSerial, SERIALVENDOR* pVendor,
663+ SERIALDEVICE* pDevice, SERIALSUBTYPE* pSubType)
664+{
665+ pSerial->pVendor = pVendor;
666+ pSerial->pDevice = pDevice;
667+ pSerial->pSubType = pSubType;
668+
669+ /* if we know the device, use its functions */
670+ if (pSerial->pDevice)
671+ {
672+ pSerial->pfnIdent = pSerial->pDevice->pfnIdent;
673+ if (!pSerial->pfnIdent) { errno = EPERM; return 1; }
674+ }
675+ else
676+ pSerial->pfnIdent = SerialIdentDefault;
677+
678+ return 0;
679+}
680+
681+static int SerialIdentDefault(SERIALTABLET* pSerial)
682+{
683+ return SerialIdentWacom(pSerial);
684+}
685+
686+static int SerialIdentWacom(SERIALTABLET* pSerial)
687+{
688+ char* pszPos;
689+ SERIALVENDOR* pVendor = &xWacomVendor;
690+ SERIALDEVICE* pDev;
691+ SERIALSUBTYPE* pSub;
692+ char chResp[64];
693+
694+ /* send wacom identification request */
695+ if (SerialSendRequest(pSerial,"~#\r",chResp,sizeof(chResp)))
696+ {
697+ if (errno != ETIMEDOUT) return 1;
698+
699+ /* try again, sometimes the first one gets garbled */
700+ if (SerialSendRequest(pSerial,"~#\r",chResp,sizeof(chResp)))
701+ return 1;
702+ }
703+
704+ /* look through device table for information */
705+ for (pDev=pVendor->pDevices; pDev->pszName; ++pDev)
706+ {
707+ for (pSub=pDev->pSubTypes; pSub && pSub->pszName; ++pSub)
708+ {
709+ if (strncmp(chResp,pSub->pszIdent, strlen(pSub->pszIdent)) == 0)
710+ {
711+ pSerial->pVendor = pVendor;
712+ pSerial->pDevice = pDev;
713+ pSerial->pSubType = pSub;
714+ pSerial->state.uValid = pDev->uCaps;
715+ pSerial->uPacketLength = pDev->uPacketLength;
716+ pSerial->pfnInit = pSub->pfnInit ?
717+ pSub->pfnInit : SerialInitWacom;
718+
719+ /* get version number */
720+ pszPos = chResp;
721+ while (*pszPos) ++pszPos;
722+ while ((pszPos > chResp) && (pszPos[-1] != 'V')) --pszPos;
723+ if (sscanf(pszPos,"%d.%d-%d",&pSerial->nVerMajor,
724+ &pSerial->nVerMinor,&pSerial->nVerRelease) != 3)
725+ {
726+ pSerial->nVerRelease = 0;
727+ if (sscanf(pszPos,"%d.%d",&pSerial->nVerMajor,
728+ &pSerial->nVerMinor) != 2)
729+ {
730+ errno = EINVAL;
731+ SerialError(pSerial,"bad version number: %s",pszPos);
732+ return 1;
733+ }
734+ }
735+ return 0;
736+ }
737+ }
738+ }
739+
740+ SerialError(pSerial,"UNIDENTIFIED TABLET: %s",chResp);
741+ return 1;
742+}
743+
744+static int SerialInitWacom(SERIALTABLET* pSerial)
745+{
746+ char chResp[32];
747+
748+ /* Request tablet dimensions */
749+ if (SerialSendRequest(pSerial,"~C\r",chResp,sizeof(chResp)))
750+ return 1;
751+
752+ /* parse position range */
753+ if (sscanf(chResp,"%d,%d",
754+ &pSerial->state.values[WACOMFIELD_POSITION_X].nMax,
755+ &pSerial->state.values[WACOMFIELD_POSITION_Y].nMax) != 2)
756+ {
757+ errno=EINVAL;
758+ SerialError(pSerial,"Bad dimension response [%s]",chResp);
759+ return 1;
760+ }
761+
762+ /* tablet specific initialization */
763+ switch (pSerial->pDevice->uDevice)
764+ {
765+ case WACOMDEVICE_PENPARTNER:
766+ /* pressure mode */
767+ SerialSend(pSerial, "PH1\r");
768+ break;
769+
770+ case WACOMDEVICE_INTUOS:
771+ case WACOMDEVICE_INTUOS2:
772+ /* multi-mode, max-rate */
773+ SerialSend(pSerial, "MT1\rID1\rIT0\r");
774+ }
775+
776+ if (pSerial->pDevice->nProtocol == PROTOCOL_4)
777+ {
778+ /* multi-mode (MU), upper-origin (OC), all-macro (M0),
779+ * no-macro1 (M1), max-rate (IT), no-inc (IN),
780+ * stream-mode (SR), Z-filter (ZF) */
781+
782+/* if (SerialSend(pSerial->fd, "MU1\rOC1\r~M0\r~M1\rIT0\rIN0\rSR\rZF1\r"))
783+ return 1;
784+ */
785+
786+ if (pSerial->nVerMajor == 1)
787+ {
788+ if (pSerial->nVerMinor >= 4)
789+ {
790+ /* enable tilt mode */
791+ if (SerialSend(pSerial,"FM1\r")) return 1;
792+
793+ pSerial->pfnParse = SerialParseWacomIV_1_4;
794+ pSerial->uPacketLength = 9;
795+ pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255;
796+ pSerial->state.values[WACOMFIELD_TILT_X].nMin = -64;
797+ pSerial->state.values[WACOMFIELD_TILT_X].nMax = 63;
798+ pSerial->state.values[WACOMFIELD_TILT_Y].nMin = -64;
799+ pSerial->state.values[WACOMFIELD_TILT_Y].nMax = 63;
800+ }
801+ else if (pSerial->nVerMinor == 3)
802+ {
803+ pSerial->pfnParse = SerialParseWacomIV_1_3;
804+ pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255;
805+ }
806+ else if (pSerial->nVerMinor == 2)
807+ {
808+ pSerial->pfnParse = SerialParseWacomIV_1_2;
809+ pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255;
810+ }
811+ else if (pSerial->nVerMinor < 2)
812+ {
813+ pSerial->pfnParse = SerialParseWacomIV_1_2;
814+ pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 120;
815+ }
816+ }
817+ }
818+ else if (pSerial->pDevice->nProtocol == PROTOCOL_5)
819+ {
820+ pSerial->pfnParse = SerialParseWacomV;
821+ pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 1023;
822+ pSerial->state.values[WACOMFIELD_ABSWHEEL].nMax = 1023;
823+ pSerial->state.values[WACOMFIELD_ROTATION_Z].nMin = -900;
824+ pSerial->state.values[WACOMFIELD_ROTATION_Z].nMax = 899;
825+ pSerial->state.values[WACOMFIELD_THROTTLE].nMin = -1023;
826+ pSerial->state.values[WACOMFIELD_THROTTLE].nMax = 1023;
827+ pSerial->state.values[WACOMFIELD_TILT_X].nMin = -64;
828+ pSerial->state.values[WACOMFIELD_TILT_X].nMax = 63;
829+ pSerial->state.values[WACOMFIELD_TILT_Y].nMin = -64;
830+ pSerial->state.values[WACOMFIELD_TILT_Y].nMax = 63;
831+ }
832+ else { errno=EINVAL; return 1; }
833+
834+ return 0;
835+}
836+
837+
838+static int SerialIdentTabletPC(SERIALTABLET* pSerial)
839+{
840+ /* sanity check */
841+ if ((pSerial->pVendor != &xtpcVendor) ||
842+ (pSerial->pDevice == NULL)) { return EPERM; return 1; }
843+
844+ /* use first one */
845+ pSerial->pSubType = pSerial->pDevice->pSubTypes;
846+
847+ /* sanity check again */
848+ if (pSerial->pSubType->pszName == NULL) { return EPERM; return 1; }
849+
850+ pSerial->state.uValid = pSerial->pDevice->uCaps;
851+ pSerial->uPacketLength = pSerial->pDevice->uPacketLength;
852+ pSerial->pfnInit = pSerial->pSubType->pfnInit;
853+ pSerial->nVerMajor = 0;
854+ pSerial->nVerMinor = 0;
855+ pSerial->nVerRelease = 0;
856+
857+ return 0;
858+}
859+
860+static int SerialInitTabletPC(SERIALTABLET* pSerial)
861+{
862+ pSerial->pfnParse = SerialParseTabletPC;
863+ pSerial->state.values[WACOMFIELD_POSITION_X].nMax = 0x6000;
864+ pSerial->state.values[WACOMFIELD_POSITION_Y].nMax = 0x4800;
865+ pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255;
866+ return 0;
867+}
868+
869+static int SerialParseWacomV(SERIALTABLET* pSerial,
870+ const unsigned char* puchData, unsigned int uLength,
871+ WACOMSTATE* pState)
872+{
873+ int x=0, y=0, rot=0, tiltx=0, tilty=0, wheel=0,
874+ tool=WACOMTOOLTYPE_NONE, button=0, press=0, throttle=0,
875+ nButtonValue;
876+
877+ /* Wacom V
878+ * Supports: 1024 pressure, eraser, 2 side-switch, tilt, throttle, wheel
879+ * Limitation: no tilt */
880+
881+ if (uLength != 9) { errno=EINVAL; return 1; }
882+
883+ /* in */
884+ if ((puchData[0] & 0xFC) == 0xC0)
885+ {
886+ int toolid = (((int)puchData[1]&0x7F) << 5) |
887+ (((int)puchData[2]&0x7C) >> 2);
888+
889+ int serial = ((((int)puchData[2] & 0x03) << 30) |
890+ (((int)puchData[3] & 0x7f) << 23) |
891+ (((int)puchData[4] & 0x7f) << 16) |
892+ (((int)puchData[5] & 0x7f) << 9) |
893+ (((int)puchData[6] & 0x7f) << 2) |
894+ (((int)puchData[7] & 0x60) >> 5));
895+
896+ switch (toolid)
897+ {
898+ case 0x812: /* Intuos2 ink pen XP-110-00A */
899+ case 0x012: /* Inking pen */
900+ tool = WACOMTOOLTYPE_PENCIL; break;
901+
902+ case 0x822: /* Intuos Pen GP-300E-01H */
903+ case 0x852: /* Intuos2 Grip Pen XP-501E-00A */
904+ case 0x842: /* added from Cheng */
905+ case 0x022:
906+ tool = WACOMTOOLTYPE_PEN; break;
907+
908+ case 0x832: /* Intuos2 stroke pen XP-120-00A */
909+ case 0x032: /* Stroke pen */
910+ tool = WACOMTOOLTYPE_BRUSH; break;
911+
912+ case 0x007: /* 2D Mouse */
913+ case 0x09C: /* ?? Mouse */
914+ case 0x094: /* 4D Mouse */
915+ tool = WACOMTOOLTYPE_MOUSE; break;
916+
917+ case 0x096: /* Lens cursor */
918+ tool = WACOMTOOLTYPE_LENS; break;
919+
920+ case 0x82a:
921+ case 0x85a:
922+ case 0x91a:
923+ case 0x0fa: /* Eraser */
924+ tool = WACOMTOOLTYPE_ERASER; break;
925+
926+ case 0x112: /* Airbrush */
927+ tool = WACOMTOOLTYPE_AIRBRUSH; break;
928+
929+ default: /* Unknown tool */
930+ tool = WACOMTOOLTYPE_PEN; break;
931+ }
932+
933+ pSerial->nToolID = toolid;
934+ pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = 1;
935+ pSerial->state.values[WACOMFIELD_SERIAL].nValue = serial;
936+ pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool;
937+ return pState ? WacomCopyState(pState,&pSerial->state) : 0;
938+ }
939+
940+ /* out */
941+ if ((puchData[0] & 0xFE) == 0x80)
942+ {
943+ pSerial->nToolID = 0;
944+ memset(&pSerial->state.values, 0,
945+ pSerial->state.uValueCnt * sizeof(WACOMVALUE));
946+ return pState ? WacomCopyState(pState,&pSerial->state) : 0;
947+ }
948+
949+ /* pen data */
950+ if (((puchData[0] & 0xB8) == 0xA0) || ((puchData[0] & 0xBE) == 0xB4))
951+ {
952+ x = ((((int)puchData[1] & 0x7f) << 9) |
953+ (((int)puchData[2] & 0x7f) << 2) |
954+ (((int)puchData[3] & 0x60) >> 5));
955+ y = ((((int)puchData[3] & 0x1f) << 11) |
956+ (((int)puchData[4] & 0x7f) << 4) |
957+ (((int)puchData[5] & 0x78) >> 3));
958+ tiltx = (puchData[7] & 0x3F);
959+ tilty = (puchData[8] & 0x3F);
960+ if (puchData[7] & 0x40) tiltx -= 0x40;
961+ if (puchData[8] & 0x40) tilty -= 0x40;
962+
963+ /* pen packet */
964+ if ((puchData[0] & 0xB8) == 0xA0)
965+ {
966+ press = ((((int)puchData[5] & 0x07) << 7) | ((int)puchData[6] & 0x7f));
967+ button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0;
968+ button |= (puchData[0] & 0x02) ? BIT(WACOMBUTTON_STYLUS) : 0;
969+ button |= (puchData[0] & 0x04) ? BIT(WACOMBUTTON_STYLUS2) : 0;
970+ }
971+
972+ /* 2nd airbrush packet */
973+ else
974+ {
975+ wheel = ((((int)puchData[5] & 0x07) << 7) |
976+ ((int)puchData[6] & 0x7f));
977+ }
978+
979+ pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
980+ pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
981+ pSerial->state.values[WACOMFIELD_TILT_X].nValue = tiltx;
982+ pSerial->state.values[WACOMFIELD_TILT_Y].nValue = tilty;
983+ pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press;
984+ pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button;
985+ pSerial->state.values[WACOMFIELD_ABSWHEEL].nValue = wheel;
986+ return pState ? WacomCopyState(pState,&pSerial->state) : 0;
987+ }
988+
989+ /* mouse packet */
990+ if (((puchData[0] & 0xBE) == 0xA8) || ((puchData[0] & 0xBE) == 0xB0))
991+ {
992+ x = ((((int)puchData[1] & 0x7f) << 9) |
993+ (((int)puchData[2] & 0x7f) << 2) |
994+ (((int)puchData[3] & 0x60) >> 5));
995+ y = ((((int)puchData[3] & 0x1f) << 11) |
996+ (((int)puchData[4] & 0x7f) << 4) |
997+ (((int)puchData[5] & 0x78) >> 3));
998+ throttle = ((((int)puchData[5] & 0x07) << 7) | (puchData[6] & 0x7f));
999+ if (puchData[8] & 0x08) throttle = -throttle;
1000+
1001+ /* 4D mouse */
1002+ if (pSerial->nToolID == 0x094)
1003+ {
1004+ button = (((puchData[8] & 0x70) >> 1) | (puchData[8] & 0x07));
1005+ }
1006+ /* lens cursor */
1007+ else if (pSerial->nToolID == 0x096)
1008+ {
1009+ button = puchData[8] & 0x1F;
1010+ }
1011+ /* 2D mouse */
1012+ else
1013+ {
1014+ button = (puchData[8] & 0x1C) >> 2;
1015+ wheel = - (puchData[8] & 1) + ((puchData[8] & 2) >> 1);
1016+ }
1017+
1018+ pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
1019+ pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
1020+ pSerial->state.values[WACOMFIELD_RELWHEEL].nValue = wheel;
1021+ pSerial->state.values[WACOMFIELD_THROTTLE].nValue = throttle;
1022+
1023+ /* button values */
1024+ nButtonValue = pSerial->state.values[WACOMFIELD_BUTTONS].nValue &
1025+ ~(BIT(WACOMBUTTON_LEFT) |
1026+ BIT(WACOMBUTTON_RIGHT) | BIT(WACOMBUTTON_MIDDLE) |
1027+ BIT(WACOMBUTTON_EXTRA) | BIT(WACOMBUTTON_SIDE));
1028+ if (button & 1) nButtonValue |= BIT(WACOMBUTTON_LEFT);
1029+ if (button & 2) nButtonValue |= BIT(WACOMBUTTON_MIDDLE);
1030+ if (button & 4) nButtonValue |= BIT(WACOMBUTTON_RIGHT);
1031+ if (button & 8) nButtonValue |= BIT(WACOMBUTTON_EXTRA);
1032+ if (button & 16) nButtonValue |= BIT(WACOMBUTTON_SIDE);
1033+ pSerial->state.values[WACOMFIELD_BUTTONS].nValue = nButtonValue;
1034+
1035+ return pState ? WacomCopyState(pState,&pSerial->state) : 0;
1036+ }
1037+
1038+ /* 2nd 4D mouse packet */
1039+ if ((puchData[0] & 0xBE) == 0xAA)
1040+ {
1041+ x = ((((int)puchData[1] & 0x7f) << 9) |
1042+ (((int)puchData[2] & 0x7f) << 2) |
1043+ (((int)puchData[3] & 0x60) >> 5));
1044+ y = ((((int)puchData[3] & 0x1f) << 11) |
1045+ (((int)puchData[4] & 0x7f) << 4) |
1046+ (((int)puchData[5] & 0x78) >> 3));
1047+ rot = ((((int)puchData[6] & 0x0f) << 7) |
1048+ ((int)puchData[7] & 0x7f));
1049+
1050+ /* FIX ROT */
1051+ if (rot < 900) rot = -rot;
1052+ else rot = 1799 - rot;
1053+
1054+ pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
1055+ pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
1056+ pSerial->state.values[WACOMFIELD_ROTATION_Z].nValue = rot;
1057+ return pState ? WacomCopyState(pState,&pSerial->state) : 0;
1058+ }
1059+
1060+ errno = EINVAL;
1061+ return 1;
1062+}
1063+
1064+static int SerialParseWacomIV_1_4(SERIALTABLET* pSerial,
1065+ const unsigned char* puchData, unsigned int uLength,
1066+ WACOMSTATE* pState)
1067+{
1068+ /* Wacom IV, Rom 1.4
1069+ * Supports: 256 pressure, eraser, 2 side-switch, tilt */
1070+
1071+ if ((uLength != 7) && (uLength != 9)) { errno=EINVAL; return 1; }
1072+
1073+ if (SerialParseWacomIV_1_3(pSerial,puchData,7,pState))
1074+ return 1;
1075+
1076+ /* tilt mode */
1077+ if (uLength == 9)
1078+ {
1079+ int tiltx, tilty;
1080+
1081+ tiltx = puchData[7] & 0x3F;
1082+ tilty = puchData[8] & 0x3F;
1083+ if (puchData[7] & 0x40) tiltx -= 64;
1084+ if (puchData[8] & 0x40) tilty -= 64;
1085+
1086+ pSerial->state.values[WACOMFIELD_TILT_X].nValue = tiltx;
1087+ pSerial->state.values[WACOMFIELD_TILT_Y].nValue = tilty;
1088+
1089+ if (pState)
1090+ {
1091+ pState->values[WACOMFIELD_TILT_X].nValue = tiltx;
1092+ pState->values[WACOMFIELD_TILT_Y].nValue = tilty;
1093+ }
1094+ }
1095+
1096+ return 0;
1097+}
1098+
1099+static int SerialParseWacomIV_1_3(SERIALTABLET* pSerial,
1100+ const unsigned char* puchData, unsigned int uLength,
1101+ WACOMSTATE* pState)
1102+{
1103+ int x=0, y=0, prox=0, tool=WACOMTOOLTYPE_NONE,
1104+ button=0, press=0, stylus, eraser;
1105+
1106+ /* Wacom IV, Rom 1.3 (ArtPadII)
1107+ * Supports: 256 pressure, eraser, 2 side-switch
1108+ * Limitation: no tilt */
1109+
1110+ if (uLength != 7) { errno=EINVAL; return 1; }
1111+
1112+ prox = puchData[0] & 0x40 ? 1 : 0;
1113+ if (prox)
1114+ {
1115+ stylus = puchData[0] & 0x20 ? 1 : 0;
1116+ press = (puchData[6] & 0x3F) << 1 | ((puchData[3] & 0x4) >> 2);
1117+ press |= (puchData[6] & 0x40) ? 0 : 0x80;
1118+ eraser = (puchData[3] & 0x20) ? 1 : 0;
1119+
1120+ if (stylus)
1121+ {
1122+ /* if entering proximity, choose eraser or stylus2 for bit */
1123+ if (pSerial->state.values[WACOMFIELD_PROXIMITY].nValue == 0)
1124+ {
1125+ if (eraser) tool = WACOMTOOLTYPE_ERASER;
1126+ else tool = WACOMTOOLTYPE_PEN;
1127+ }
1128+
1129+ /* otherwise, keep the last tool */
1130+ else tool = pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue;
1131+
1132+ button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0;
1133+
1134+ /* pen has 2 side-switch, eraser has none */
1135+ if (tool == WACOMTOOLTYPE_PEN)
1136+ {
1137+ button |= (puchData[3] & 0x10) ?
1138+ BIT(WACOMBUTTON_STYLUS) : 0;
1139+ button |= (eraser) ? BIT(WACOMBUTTON_STYLUS2) : 0;
1140+ }
1141+ }
1142+ else
1143+ {
1144+ tool = WACOMTOOLTYPE_MOUSE;
1145+ button = (puchData[3] & 0x78) >> 3; /* not tested */
1146+ }
1147+
1148+ x = puchData[2] | ((int)puchData[1] << 7) |
1149+ (((int)puchData[0] & 0x3) << 14);
1150+ y = puchData[5] | ((int)puchData[4] << 7) |
1151+ (((int)puchData[3] & 0x3) << 14);
1152+ }
1153+
1154+ /* set valid fields */
1155+ pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = prox;
1156+ pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool;
1157+ pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
1158+ pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
1159+ pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press;
1160+ pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button;
1161+
1162+ return pState ? WacomCopyState(pState,&pSerial->state) : 0;
1163+}
1164+
1165+static int SerialParseWacomIV_1_2(SERIALTABLET* pSerial,
1166+ const unsigned char* puchData, unsigned int uLength,
1167+ WACOMSTATE* pState)
1168+{
1169+ int x=0, y=0, prox=0, tool=0, button=WACOMTOOLTYPE_NONE,
1170+ press=0, stylus;
1171+
1172+ /* Wacom IV, Rom 1.2, 1.1, and 1.0
1173+ * Supports: 256 pressure (120 for 1.1 and 1.0), multi-mode
1174+ * Limitation: no stylus2, no tilt, no eraser */
1175+
1176+ if (uLength != 7) { errno=EINVAL; return 1; }
1177+
1178+ prox = puchData[0] & 0x40 ? 1 : 0;
1179+ if (prox)
1180+ {
1181+ stylus = puchData[0] & 0x20 ? 1 : 0;
1182+ if (pSerial->nVerMinor == 2)
1183+ press = ((puchData[6] & 0x3F) << 1) | ((puchData[3] & 0x4) >> 2) |
1184+ ((puchData[6] & 0x40) ? 0 : 0x80);
1185+ else
1186+ press = (puchData[6] & 0x3F) + ((puchData[6] & 0x40) ? 0 : 64);
1187+
1188+ if (stylus)
1189+ {
1190+ tool = WACOMTOOLTYPE_PEN;
1191+ button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0;
1192+ button |= (puchData[3] & 0x10) ? BIT(WACOMBUTTON_STYLUS) : 0;
1193+ }
1194+ else
1195+ {
1196+ tool = WACOMTOOLTYPE_MOUSE;
1197+ button = (puchData[3] & 0x78) >> 3; /* not tested */
1198+ }
1199+
1200+ x = puchData[2] | ((int)puchData[1] << 7) |
1201+ (((int)puchData[0] & 0x3) << 14);
1202+ y = puchData[5] | ((int)puchData[4] << 7) |
1203+ (((int)puchData[3] & 0x3) << 14);
1204+ }
1205+
1206+ /* set valid fields */
1207+ pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = prox;
1208+ pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool;
1209+ pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
1210+ pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
1211+ pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press;
1212+ pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button;
1213+
1214+ return pState ? WacomCopyState(pState,&pSerial->state) : 0;
1215+}
1216+
1217+static int SerialParseTabletPC(SERIALTABLET* pSerial,
1218+ const unsigned char* puchData, unsigned int uLength,
1219+ WACOMSTATE* pState)
1220+{
1221+ int x=0, y=0, prox=0, tool=WACOMTOOLTYPE_NONE,
1222+ button=0, press=0, eraser;
1223+
1224+ /* Tablet PC Supports: 256 pressure, eraser, 1/2 side-switch */
1225+
1226+ if (uLength != 9) { errno=EINVAL; return 1; }
1227+
1228+ prox = puchData[0] & 0x20 ? 1 : 0;
1229+ if (prox)
1230+ {
1231+ eraser = (puchData[0] & 0x04) ? 1 : 0;
1232+ press = ((puchData[6] & 0x01) << 7) | (puchData[5] & 0x7F);
1233+
1234+ /* tools are distinguishable */
1235+ if (eraser) tool = WACOMTOOLTYPE_ERASER;
1236+ else tool = WACOMTOOLTYPE_PEN;
1237+
1238+ button = (puchData[0] & 0x01) ? BIT(WACOMBUTTON_TOUCH) : 0;
1239+
1240+ /* pen has side-switch(es), eraser has none */
1241+ if (tool == WACOMTOOLTYPE_PEN)
1242+ {
1243+ button |= (puchData[0] & 0x02) ?
1244+ BIT(WACOMBUTTON_STYLUS) : 0;
1245+ button |= (puchData[0] & 0x04) ?
1246+ BIT(WACOMBUTTON_STYLUS2) : 0;
1247+ }
1248+
1249+ x = (((int)puchData[6] & 0x60) >> 5) |
1250+ ((int)puchData[2] << 2) |
1251+ ((int)puchData[1] << 9);
1252+ y = (((int)puchData[6] & 0x18) >> 3) |
1253+ ((int)puchData[4] << 2) |
1254+ ((int)puchData[3] << 9);
1255+ }
1256+
1257+ /* set valid fields */
1258+ pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = prox;
1259+ pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool;
1260+ pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
1261+ pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
1262+ pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press;
1263+ pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button;
1264+
1265+ return pState ? WacomCopyState(pState,&pSerial->state) : 0;
1266+}
1267+
1268+
1269+
1270+/*****************************************************************************
1271+** Internal Functions
1272+*****************************************************************************/
1273+
1274+static int SerialSendReset(SERIALTABLET* pSerial)
1275+{
1276+ SerialInfo(pSerial,"Sending reset");
1277+
1278+ /* reset to Wacom II-S command set, and factory defaults */
1279+ if (SerialSend(pSerial,"\r$\r")) return 1;
1280+ usleep(250000); /* 250 milliseconds */
1281+
1282+ /* reset tablet to Wacom IV command set */
1283+ if (SerialSend(pSerial,"#\r")) return 1;
1284+ usleep(75000); /* 75 milliseconds */
1285+
1286+ return 0;
1287+}
1288+
1289+static int SerialSendStop(SERIALTABLET* pSerial)
1290+{
1291+ if (SerialSend(pSerial,"\rSP\r")) return 1;
1292+ usleep(100000);
1293+ return 0;
1294+}
1295+
1296+static int SerialSendStart(SERIALTABLET* pSerial)
1297+{
1298+ return SerialSend(pSerial,"ST\r");
1299+}
1300+
1301+static int SerialSend(SERIALTABLET* pSerial, const char* pszMsg)
1302+{
1303+ return SerialSendRaw(pSerial,pszMsg,(unsigned)strlen(pszMsg));
1304+}
1305+
1306+static int SerialSendRaw(SERIALTABLET* pSerial, const void* pvData,
1307+ unsigned int uSize)
1308+{
1309+ int nXfer;
1310+ unsigned int uCnt=0;
1311+ unsigned char* puchData = (unsigned char*)pvData;
1312+
1313+ while (uCnt < uSize)
1314+ {
1315+ nXfer = write(pSerial->fd,puchData+uCnt,uSize-uCnt);
1316+ if (nXfer <= 0)
1317+ {
1318+ SerialError(pSerial,"Failed to write to port: %s",strerror(errno));
1319+ return 1;
1320+ }
1321+ uCnt += nXfer;
1322+ }
1323+
1324+ return 0;
1325+}
1326+
1327+static int WacomFlush(SERIALTABLET* pSerial)
1328+{
1329+ char ch[16];
1330+ fd_set fdsRead;
1331+ struct timeval timeout;
1332+
1333+ if (tcflush(pSerial->fd, TCIFLUSH) == 0)
1334+ return 0;
1335+
1336+ timeout.tv_sec = 0;
1337+ timeout.tv_usec = 0;
1338+
1339+ while (1)
1340+ {
1341+ FD_ZERO(&fdsRead);
1342+ FD_SET(pSerial->fd, &fdsRead);
1343+ if (select(FD_SETSIZE,&fdsRead,NULL,NULL,&timeout) <= 0)
1344+ break;
1345+ read(pSerial->fd,&ch,sizeof(ch));
1346+ }
1347+
1348+ return 0;
1349+}
1350+
1351+static int SerialSendRequest(SERIALTABLET* pSerial, const char* pszRequest,
1352+ char* pchResponse, unsigned int uSize)
1353+{
1354+ int nXfer;
1355+ fd_set fdsRead;
1356+ unsigned int uLen, uCnt;
1357+ struct timeval timeout;
1358+
1359+ uLen = strlen(pszRequest);
1360+ if (SerialSendRaw(pSerial,pszRequest,uLen)) return 1;
1361+ --uLen;
1362+
1363+ if (uSize < uLen)
1364+ {
1365+ errno=EINVAL;
1366+ SerialError(pSerial,"Invalid size to SerialSendRequest: %u < %u",
1367+ uSize,uLen);
1368+ return 1;
1369+ }
1370+
1371+ /* read until first header character */
1372+ while (1)
1373+ {
1374+ timeout.tv_sec = 0;
1375+ timeout.tv_usec = 500000;
1376+
1377+ FD_ZERO(&fdsRead);
1378+ FD_SET(pSerial->fd, &fdsRead);
1379+ if (select(FD_SETSIZE,&fdsRead,NULL,NULL,&timeout) <= 0)
1380+ {
1381+ errno = ETIMEDOUT;
1382+ return 1;
1383+ }
1384+
1385+ nXfer = read(pSerial->fd,pchResponse,1);
1386+ if (nXfer <= 0)
1387+ {
1388+ SerialError(pSerial,"Truncated response header");
1389+ return 1;
1390+ }
1391+ if (*pchResponse == *pszRequest) break;
1392+ SerialWarn(pSerial,"Discarding %02X", *((unsigned char*)pchResponse));
1393+ }
1394+
1395+ /* read response header */
1396+ for (uCnt=1; uCnt<uLen; uCnt+=nXfer)
1397+ {
1398+ nXfer = read(pSerial->fd,pchResponse+uCnt,uLen-uCnt);
1399+ if (nXfer <= 0)
1400+ {
1401+ SerialError(pSerial,"Truncated response header (2)");
1402+ return 1;
1403+ }
1404+ }
1405+
1406+ /* check the header */
1407+ if (strncmp(pszRequest,pchResponse,uLen) != 0)
1408+ {
1409+ SerialError(pSerial,"Incorrect response [%s,%s]",
1410+ pszRequest,pchResponse);
1411+ return 1;
1412+ }
1413+
1414+ /* get the rest of the response */
1415+ for (uCnt=0; uCnt<uSize; ++uCnt)
1416+ {
1417+ nXfer = read(pSerial->fd,pchResponse+uCnt,1);
1418+ if (nXfer <= 0)
1419+ {
1420+ SerialError(pSerial,"Failed to read response: %s",strerror(errno));
1421+ return 1;
1422+ }
1423+
1424+ /* stop on CR */
1425+ if (pchResponse[uCnt] == '\r')
1426+ {
1427+ pchResponse[uCnt] = '\0';
1428+ return 0;
1429+ }
1430+ }
1431+
1432+ errno = EINVAL;
1433+ SerialError(pSerial,"Invalid response");
1434+ return 1;
1435+}
1436+
1437+/*****************************************************************************
1438+** Virtual Functions
1439+*****************************************************************************/
1440+
1441+static void SerialClose(WACOMTABLET_PRIV* pTablet)
1442+{
1443+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1444+ close(pSerial->fd);
1445+ free(pSerial);
1446+}
1447+
1448+static WACOMMODEL SerialGetModel(WACOMTABLET_PRIV* pTablet)
1449+{
1450+ WACOMMODEL model = { 0 };
1451+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1452+ model.uClass = WACOMCLASS_SERIAL;
1453+ model.uVendor = pSerial->pVendor->uVendor;
1454+ model.uDevice = pSerial->pDevice->uDevice;
1455+ model.uSubType = pSerial->pSubType->uSubType;
1456+ return model;
1457+}
1458+
1459+static const char* SerialGetVendorName(WACOMTABLET_PRIV* pTablet)
1460+{
1461+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1462+ return pSerial->pVendor->pszDesc;
1463+}
1464+
1465+static const char* SerialGetClassName(WACOMTABLET_PRIV* pTablet)
1466+{
1467+ return "Serial";
1468+}
1469+
1470+static const char* SerialGetDeviceName(WACOMTABLET_PRIV* pTablet)
1471+{
1472+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1473+ return pSerial->pDevice->pszDesc;
1474+}
1475+
1476+static const char* SerialGetSubTypeName(WACOMTABLET_PRIV* pTablet)
1477+{
1478+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1479+ return pSerial->pSubType->pszName;
1480+}
1481+
1482+static const char* SerialGetModelName(WACOMTABLET_PRIV* pTablet)
1483+{
1484+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1485+ return pSerial->pSubType->pszDesc;
1486+}
1487+
1488+static int SerialGetROMVer(WACOMTABLET_PRIV* pTablet, int* pnMajor,
1489+ int* pnMinor, int* pnRelease)
1490+{
1491+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1492+ if (!pnMajor) { errno=EINVAL; return 1; }
1493+ *pnMajor = pSerial->nVerMajor;
1494+ if (pnMinor) *pnMinor = pSerial->nVerMinor;
1495+ if (pnRelease) *pnRelease = pSerial->nVerRelease;
1496+ return 0;
1497+}
1498+
1499+static int SerialGetCaps(WACOMTABLET_PRIV* pTablet)
1500+{
1501+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1502+ return pSerial->pDevice->uCaps;
1503+}
1504+
1505+static int SerialGetState(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState)
1506+{
1507+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1508+ return WacomCopyState(pState,&pSerial->state);
1509+}
1510+
1511+static int SerialGetFD(WACOMTABLET_PRIV* pTablet)
1512+{
1513+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1514+ return pSerial->fd;
1515+}
1516+
1517+static int SerialReadRaw(WACOMTABLET_PRIV* pTablet, unsigned char* puchData,
1518+ unsigned int uSize)
1519+{
1520+ int nXfer;
1521+ fd_set fdsRead;
1522+ unsigned char* pPos, *pEnd;
1523+ struct timeval timeout;
1524+ unsigned int uCnt, uPacketLength;
1525+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1526+
1527+ if (!pSerial) { errno=EBADF; return 0; }
1528+ uPacketLength = pSerial->uPacketLength;
1529+
1530+ /* check size of buffer */
1531+ if (uSize < uPacketLength) { errno=EINVAL; return 0; }
1532+
1533+ /* check for errors, reset after 3 */
1534+ if (pSerial->nBitErrors > 3)
1535+ {
1536+ pSerial->nBitErrors = 0;
1537+ SerialWarn(pSerial,"Resetting tablet due to bit errors");
1538+ (void)SerialReset(pSerial,&pSerial->modelRequested);
1539+ }
1540+
1541+ timeout.tv_sec = 0;
1542+ timeout.tv_usec = 500000; /* 0.5 seconds for now */
1543+
1544+ for (uCnt=0; uCnt<uPacketLength; uCnt+=nXfer)
1545+ {
1546+ FD_ZERO(&fdsRead);
1547+ FD_SET(pSerial->fd, &fdsRead);
1548+ if (select(FD_SETSIZE,&fdsRead,NULL,NULL,&timeout) <= 0)
1549+ break;
1550+
1551+ nXfer = read(pSerial->fd,puchData+uCnt,uPacketLength-uCnt);
1552+ if (nXfer <= 0) return nXfer;
1553+
1554+ /* look for high-bit */
1555+ pEnd = puchData + nXfer;
1556+ for (pPos=puchData; pPos<pEnd; ++pPos)
1557+ {
1558+ if (*pPos & 0x80) break;
1559+ }
1560+
1561+ /* not where it was expected? fix it. */
1562+ if (pPos != puchData)
1563+ {
1564+ ++pSerial->nBitErrors;
1565+ SerialWarn(pSerial,"Bad high bit, discarding %d bytes",
1566+ pPos - puchData);
1567+
1568+ /* copy remaining bytes down, if any */
1569+ memmove(puchData,pPos,(nXfer - (pPos-puchData)));
1570+ nXfer -= pPos - puchData;
1571+ }
1572+ }
1573+
1574+ if (uCnt < uPacketLength)
1575+ {
1576+ errno = ETIMEDOUT;
1577+ return -1;
1578+ }
1579+
1580+ return (signed)uCnt;
1581+}
1582+
1583+static int SerialParseData(WACOMTABLET_PRIV* pTablet,
1584+ const unsigned char* puchData, unsigned int uLength,
1585+ WACOMSTATE* pState)
1586+{
1587+ int i;
1588+ SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
1589+ if (!pSerial) { errno=EBADF; return 1; }
1590+
1591+ /* check synchronization */
1592+ if (!(puchData[0] & 0x80))
1593+ {
1594+ ++pSerial->nBitErrors;
1595+ SerialError(pSerial,"HIBIT FAIL");
1596+ errno=EINVAL;
1597+ return 1;
1598+ }
1599+ for (i=1; i<uLength; ++i)
1600+ {
1601+ if (puchData[i] & 0x80)
1602+ {
1603+ ++pSerial->nBitErrors;
1604+ SerialError(pSerial,"LOBIT FAIL");
1605+ SerialDump(pSerial,puchData,uLength);
1606+ errno=EINVAL;
1607+ return 1;
1608+ }
1609+ }
1610+
1611+ /* reset bit error count */
1612+ pSerial->nBitErrors = 0;
1613+
1614+ /* dispatch to parser */
1615+ if (pSerial->pfnParse)
1616+ return (*pSerial->pfnParse)(pSerial,puchData,uLength,pState);
1617+
1618+ errno=EINVAL;
1619+ return 1;
1620+}
1621+
1622+static int SerialResetAtBaud(SERIALTABLET* pSerial, struct termios* pTIOS,
1623+ int nBaud)
1624+{
1625+ /* conver baud rate to tios macro */
1626+ int baudRate = B9600;
1627+ switch (nBaud)
1628+ {
1629+ case 38400: baudRate = B38400; break;
1630+ case 19200: baudRate = B19200; break;
1631+ case 9600: baudRate = B9600; break;
1632+ case 4800: baudRate = B4800; break; /* for testing, maybe */
1633+ case 2400: baudRate = B2400; break; /* for testing, maybe */
1634+ case 1200: baudRate = B1200; break; /* for testing, maybe */
1635+ }
1636+
1637+ SerialInfo(pSerial,"Setting baud rate to %d",nBaud);
1638+
1639+ /* change baud rate */
1640+ cfsetispeed(pTIOS, baudRate);
1641+ cfsetospeed(pTIOS, baudRate);
1642+ if (tcsetattr (pSerial->fd, TCSANOW, pTIOS))
1643+ return 1;
1644+
1645+ /* send reset command */
1646+ return SerialSendReset(pSerial);
1647+}
1648+
1649+/*****************************************************************************
1650+** Log Functions
1651+*****************************************************************************/
1652+
1653+#define SERIALLOG(l) do { \
1654+ va_list a; \
1655+ va_start(a,pszFmt); \
1656+ WacomLogV(pSerial->hEngine,l,pszFmt,a); \
1657+ va_end(a); } while (0)
1658+
1659+static void SerialError(SERIALTABLET* pSerial, const char* pszFmt, ...)
1660+ { SERIALLOG(WACOMLOGLEVEL_ERROR); }
1661+static void SerialWarn(SERIALTABLET* pSerial, const char* pszFmt, ...)
1662+ { SERIALLOG(WACOMLOGLEVEL_WARN); }
1663+static void SerialInfo(SERIALTABLET* pSerial, const char* pszFmt, ...)
1664+ { SERIALLOG(WACOMLOGLEVEL_INFO); }
1665+
1666+static void SerialDump(SERIALTABLET* pSerial, const void* pvData, int nCnt)
1667+{
1668+ int i;
1669+ const unsigned char* pData = (const unsigned char*)pvData;
1670+ char chLine[80];
1671+ unsigned int uAddr = 0;
1672+ int nPos = 0;
1673+
1674+ while (nCnt > 0)
1675+ {
1676+ nPos = 0;
1677+ for (i=0; i<16; ++i)
1678+ {
1679+ if (i < nCnt)
1680+ nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos,
1681+ "%02X",pData[i]);
1682+ else
1683+ nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos,
1684+ " ");
1685+ }
1686+ nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos," - ");
1687+ for (i=0; i<16; ++i)
1688+ {
1689+ if (i < nCnt)
1690+ nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos,
1691+ "%c",isprint(pData[i]) ? pData[i] : '.');
1692+ else
1693+ nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos,
1694+ " ");
1695+ }
1696+
1697+ WacomLog(pSerial->hEngine,WACOMLOGLEVEL_DEBUG,"%04X: %s",uAddr,chLine);
1698+ uAddr += 16;
1699+ nCnt -= 16;
1700+ pData += 16;
1701+ }
1702+}
1703+
1704+/* NOT USED, YET
1705+static void SerialCritical(SERIALTABLET* pSerial, const char* pszFmt, ...)
1706+ { SERIALLOG(WACOMLOGLEVEL_CRITICAL); }
1707+static void SerialDebug(SERIALTABLET* pSerial, const char* pszFmt, ...)
1708+ { SERIALLOG(WACOMLOGLEVEL_DEBUG); }
1709+static void SerialTrace(SERIALTABLET* pSerial, const char* pszFmt, ...)
1710+ { SERIALLOG(WACOMLOGLEVEL_TRACE); }
1711+*/
--- /dev/null
+++ b/wacom/wacserial.h
@@ -0,0 +1,32 @@
1+/*****************************************************************************
2+** wacserial.h
3+**
4+** Copyright (C) 2002,2003 - John E. Joganic
5+**
6+** This program is free software; you can redistribute it and/or
7+** modify it under the terms of the GNU General Public License
8+** as published by the Free Software Foundation; either version 2
9+** of the License, or (at your option) any later version.
10+**
11+** This program is distributed in the hope that it will be useful,
12+** but WITHOUT ANY WARRANTY; without even the implied warranty of
13+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+** GNU General Public License for more details.
15+**
16+** You should have received a copy of the GNU General Public License
17+** along with this program; if not, write to the Free Software
18+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19+**
20+****************************************************************************/
21+
22+#ifndef __LINUXWACOM_WACSERIAL_H
23+#define __LINUXWACOM_WACSERIAL_H
24+
25+#include "wactablet.h"
26+
27+int WacomGetSupportedSerialDeviceList(WACOMDEVICEREC** ppList, int* pnSize);
28+unsigned int WacomGetSerialDeviceFromName(const char* pszName);
29+WACOMTABLET WacomOpenSerialTablet(WACOMENGINE hEngine, int fd,
30+ WACOMMODEL* pModel);
31+
32+#endif /* __LINUXWACOM_WACSERIAL_H */
--- /dev/null
+++ b/wacom/wactablet.c
@@ -0,0 +1,441 @@
1+/*****************************************************************************
2+** wactablet.c
3+**
4+** Copyright (C) 2002 - 2003 - John E. Joganic
5+** Copyright (C) 2004 - 2005 - Ping Cheng
6+**
7+** This program is free software; you can redistribute it and/or
8+** modify it under the terms of the GNU General Public License
9+** as published by the Free Software Foundation; either version 2
10+** of the License, or (at your option) any later version.
11+**
12+** This program is distributed in the hope that it will be useful,
13+** but WITHOUT ANY WARRANTY; without even the implied warranty of
14+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+** GNU General Public License for more details.
16+**
17+** You should have received a copy of the GNU General Public License
18+** along with this program; if not, write to the Free Software
19+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20+**
21+****************************************************************************/
22+
23+#include "wactablet.h"
24+#include "wacserial.h"
25+#include "wacusb.h"
26+
27+#include <stdio.h>
28+#include <stdlib.h>
29+#include <errno.h>
30+#include <memory.h>
31+#include <fcntl.h>
32+#include <unistd.h>
33+#include <assert.h>
34+#include <stdarg.h>
35+
36+#ifdef WCM_ENABLE_LINUXINPUT
37+#include <linux/input.h>
38+#endif
39+
40+typedef void (*FREEFUNC)(void* pv);
41+
42+typedef struct
43+{
44+ FREEFUNC pfnFree;
45+} CLSLIST_INTERNAL;
46+
47+typedef struct
48+{
49+ WACOMDEVICEREC* pSerialList;
50+ WACOMDEVICEREC* pUSBList;
51+ FREEFUNC pfnFree;
52+} DEVICELIST_INTERNAL;
53+
54+typedef struct _ENGINE_PRIV ENGINE_PRIV;
55+
56+struct _ENGINE_PRIV
57+{
58+ WACOMLOGFUNC pfnLog;
59+ WACOMLOGLEVEL level;
60+ char chLogBuf[1024];
61+};
62+
63+/*****************************************************************************
64+** Implementation
65+*****************************************************************************/
66+
67+WACOMENGINE WacomInitEngine(void)
68+{
69+ ENGINE_PRIV* pEngine = NULL;
70+ pEngine = (ENGINE_PRIV*)malloc(sizeof(ENGINE_PRIV));
71+ memset(pEngine,0,sizeof(*pEngine));
72+ pEngine->level = WACOMLOGLEVEL_WARN;
73+ return (WACOMENGINE)pEngine;
74+}
75+
76+void WacomTermEngine(WACOMENGINE hEngine)
77+{
78+ ENGINE_PRIV* pEngine = (ENGINE_PRIV*)hEngine;
79+ if (!pEngine) return;
80+
81+ free(pEngine);
82+}
83+
84+void WacomSetLogFunc(WACOMENGINE hEngine, WACOMLOGFUNC pfnLog)
85+{
86+ ENGINE_PRIV* pEngine = (ENGINE_PRIV*)hEngine;
87+ if (!pEngine) return;
88+ pEngine->pfnLog = pfnLog;
89+}
90+
91+void WacomSetLogLevel(WACOMENGINE hEngine, WACOMLOGLEVEL level)
92+{
93+ ENGINE_PRIV* pEngine = (ENGINE_PRIV*)hEngine;
94+ if (!pEngine) return;
95+
96+ if (level < WACOMLOGLEVEL_CRITICAL)
97+ level = WACOMLOGLEVEL_CRITICAL;
98+ if (level > WACOMLOGLEVEL_TRACE)
99+ level = WACOMLOGLEVEL_TRACE;
100+
101+ pEngine->level = level;
102+}
103+
104+void WacomLogV(WACOMENGINE hEngine, WACOMLOGLEVEL level, const char* pszFmt,
105+ va_list args)
106+{
107+ struct timeval tv;
108+ ENGINE_PRIV* pEngine = (ENGINE_PRIV*)hEngine;
109+ if (!pEngine || !pEngine->pfnLog || (pEngine->level < level)) return;
110+
111+ gettimeofday(&tv,NULL);
112+
113+ vsnprintf(pEngine->chLogBuf,sizeof(pEngine->chLogBuf),pszFmt,args);
114+ pEngine->pfnLog(tv,level,pEngine->chLogBuf);
115+}
116+
117+void WacomLog(WACOMENGINE hEngine, WACOMLOGLEVEL level, const char* pszFmt, ...)
118+{
119+ va_list args;
120+ va_start(args, pszFmt);
121+ WacomLogV(hEngine,level,pszFmt,args);
122+ va_end(args);
123+}
124+
125+static void FreeClassList(void* pv)
126+{
127+ CLSLIST_INTERNAL* pInt = ((CLSLIST_INTERNAL*)pv) - 1;
128+ free(pInt);
129+}
130+
131+int WacomGetSupportedClassList(WACOMCLASSREC** ppList, int* pnSize)
132+{
133+ int nIndex=0, nCnt=0;
134+ CLSLIST_INTERNAL* pInt;
135+ WACOMCLASSREC* pRec;
136+
137+ if (!ppList || !pnSize) { errno = EINVAL; return 1; }
138+
139+ /* serial */
140+ ++nCnt;
141+
142+ /* USB */
143+ #ifdef WCM_ENABLE_LINUXINPUT
144+ ++nCnt;
145+ #endif
146+
147+ /* allocate enough memory to hold internal structure and all records */
148+ pInt = (CLSLIST_INTERNAL*)malloc(sizeof(CLSLIST_INTERNAL) +
149+ (sizeof(WACOMCLASSREC) * nCnt));
150+
151+ pInt->pfnFree = FreeClassList;
152+ pRec = (WACOMCLASSREC*)(pInt + 1);
153+
154+ /* serial */
155+ pRec[nIndex].pszName = "serial";
156+ pRec[nIndex].pszDesc = "Serial TTY interface";
157+ pRec[nIndex].uDeviceClass = WACOMCLASS_SERIAL;
158+ ++nIndex;
159+
160+ /* USB */
161+ #ifdef WCM_ENABLE_LINUXINPUT
162+ pRec[nIndex].pszName = "usb";
163+ pRec[nIndex].pszDesc = "Linux USB event interface";
164+ pRec[nIndex].uDeviceClass = WACOMCLASS_USB;
165+ ++nIndex;
166+ #endif
167+
168+ assert(nIndex == nCnt);
169+ *ppList = pRec;
170+ *pnSize = nCnt;
171+ return 0;
172+}
173+
174+static void FreeDeviceList(void* pv)
175+{
176+ DEVICELIST_INTERNAL* pInt = ((DEVICELIST_INTERNAL*)pv) - 1;
177+ WacomFreeList(pInt->pSerialList);
178+ WacomFreeList(pInt->pUSBList);
179+ free(pInt);
180+}
181+
182+int WacomGetSupportedDeviceList(unsigned int uDeviceClass,
183+ WACOMDEVICEREC** ppList, int* pnSize)
184+{
185+ int nSerialCnt=0, nUSBCnt=0, nTotalBytes;
186+ WACOMDEVICEREC* pSerial=NULL, *pUSB=NULL, *pList;
187+ DEVICELIST_INTERNAL* pInt;
188+
189+ if (!ppList || !pnSize) { errno = EINVAL; return 1; }
190+
191+ /* get serial list */
192+ if (((!uDeviceClass) || (uDeviceClass == WACOMCLASS_SERIAL)) &&
193+ WacomGetSupportedSerialDeviceList(&pSerial, &nSerialCnt)) return 1;
194+
195+ /* get usb list */
196+ if (((!uDeviceClass) || (uDeviceClass == WACOMCLASS_USB)) &&
197+ WacomGetSupportedUSBDeviceList(&pUSB, &nUSBCnt))
198+ {
199+ if (pSerial) WacomFreeList(pSerial);
200+ return 1;
201+ }
202+
203+ /* need memory for duplicate records and list internal structure */
204+ nTotalBytes = sizeof(WACOMDEVICEREC) * (nSerialCnt + nUSBCnt) +
205+ sizeof(DEVICELIST_INTERNAL);
206+
207+ /* allocate memory */
208+ pInt = (DEVICELIST_INTERNAL*)malloc(nTotalBytes);
209+
210+ /* copy initial list pointers */
211+ pInt->pSerialList = pSerial;
212+ pInt->pUSBList = pUSB;
213+ pInt->pfnFree = FreeDeviceList;
214+
215+ /* copy records */
216+ pList = (WACOMDEVICEREC*)(pInt + 1);
217+ if (pSerial)
218+ memcpy(pList,pSerial,sizeof(WACOMDEVICEREC) * nSerialCnt);
219+ if (pUSB)
220+ memcpy(pList + nSerialCnt, pUSB, sizeof(WACOMDEVICEREC) * nUSBCnt);
221+
222+ *ppList = pList;
223+ *pnSize = nSerialCnt + nUSBCnt;
224+
225+ return 0;
226+}
227+
228+void WacomFreeList(void* pvList)
229+{
230+ FREEFUNC pfnFree;
231+ if (!pvList) return;
232+ pfnFree = ((FREEFUNC*)pvList)[-1];
233+ (*pfnFree)(pvList);
234+}
235+
236+unsigned int WacomGetClassFromName(const char* pszName)
237+{
238+ if (strcasecmp(pszName, "serial") == 0)
239+ return WACOMCLASS_SERIAL;
240+ else if (strcasecmp(pszName, "usb") == 0)
241+ return WACOMCLASS_USB;
242+ return 0;
243+}
244+
245+unsigned int WacomGetDeviceFromName(const char* pszName,
246+ unsigned int uDeviceClass)
247+{
248+ unsigned int uDeviceType = 0;
249+
250+ if (!uDeviceClass || (uDeviceClass == WACOMCLASS_SERIAL))
251+ {
252+ uDeviceType = WacomGetSerialDeviceFromName(pszName);
253+ if (uDeviceType) return uDeviceType;
254+ }
255+
256+ if (!uDeviceClass || (uDeviceClass == WACOMCLASS_USB))
257+ {
258+ uDeviceType = WacomGetUSBDeviceFromName(pszName);
259+ if (uDeviceType) return uDeviceType;
260+ }
261+
262+ errno = ENOENT;
263+ return 0;
264+}
265+
266+static int WacomIsSerial(int fd)
267+{
268+ return isatty(fd);
269+}
270+
271+static int WacomIsUSB(int fd)
272+{
273+#ifdef WCM_ENABLE_LINUXINPUT
274+ short sID[4];
275+ if (ioctl(fd,EVIOCGID,sID) < 0) return 0;
276+ return 1;
277+#else
278+ return 0;
279+#endif
280+}
281+
282+WACOMTABLET WacomOpenTablet(WACOMENGINE hEngine, const char* pszDevice,
283+ WACOMMODEL* pModel)
284+{
285+ int fd, e;
286+ WACOMTABLET hTablet = NULL;
287+ unsigned int uClass = pModel ? pModel->uClass : 0;
288+
289+ /* open device for read/write access */
290+ fd = open(pszDevice,O_RDWR);
291+ if (fd < 0)
292+ {
293+ e = errno;
294+ WacomLog(hEngine,WACOMLOGLEVEL_ERROR,"Failed to open %s: %s",
295+ pszDevice, strerror(errno));
296+ errno = e;
297+ return NULL;
298+ }
299+
300+ /* configure serial */
301+ if ((!uClass || (uClass == WACOMCLASS_SERIAL)) && WacomIsSerial(fd))
302+ {
303+ hTablet = WacomOpenSerialTablet(hEngine,fd,pModel);
304+ if (!hTablet) { e=errno; close(fd); errno=e; return NULL; }
305+ }
306+
307+ /* configure usb */
308+ else if ((!uClass || (uClass == WACOMCLASS_USB)) && WacomIsUSB(fd))
309+ {
310+ hTablet = WacomOpenUSBTablet(hEngine,fd,pModel);
311+ if (!hTablet) { e=errno; close(fd); errno=e; return NULL; }
312+ }
313+
314+ /* give up */
315+ else
316+ {
317+ close(fd);
318+ errno = EINVAL;
319+ return NULL;
320+ }
321+
322+ return hTablet;
323+}
324+
325+int WacomCopyState(WACOMSTATE* pDest, WACOMSTATE* pSrc)
326+{
327+ unsigned int uCnt;
328+ if (!pSrc || !pDest || !pSrc->uValueCnt || !pDest->uValueCnt)
329+ { errno=EINVAL; return 1; }
330+
331+ /* copy valid bits over */
332+ pDest->uValid = pSrc->uValid;
333+
334+ /* determine how many values to transfer */
335+ uCnt = (pDest->uValueCnt < pSrc->uValueCnt) ?
336+ pDest->uValueCnt : pSrc->uValueCnt;
337+
338+ /* copy them over */
339+ memcpy(pDest->values,pSrc->values,uCnt*sizeof(WACOMVALUE));
340+
341+ return 0;
342+}
343+
344+/*****************************************************************************
345+** Virtual Functions
346+*****************************************************************************/
347+
348+void WacomCloseTablet(WACOMTABLET hTablet)
349+{
350+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
351+ if (!pTablet || !pTablet->Close) return;
352+ pTablet->Close(pTablet);
353+}
354+
355+WACOMMODEL WacomGetModel(WACOMTABLET hTablet)
356+{
357+ WACOMMODEL xBadModel = { 0 };
358+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
359+ if (!pTablet || !pTablet->GetModel) { errno=EBADF; return xBadModel; }
360+ return pTablet->GetModel(pTablet);
361+}
362+
363+const char* WacomGetVendorName(WACOMTABLET hTablet)
364+{
365+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
366+ if (!pTablet || !pTablet->GetVendorName) { errno=EBADF; return NULL; }
367+ return pTablet->GetVendorName(pTablet);
368+}
369+
370+const char* WacomGetClassName(WACOMTABLET hTablet)
371+{
372+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
373+ if (!pTablet || !pTablet->GetClassName) { errno=EBADF; return NULL; }
374+ return pTablet->GetClassName(pTablet);
375+}
376+
377+const char* WacomGetDeviceName(WACOMTABLET hTablet)
378+{
379+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
380+ if (!pTablet || !pTablet->GetDeviceName) { errno=EBADF; return NULL; }
381+ return pTablet->GetDeviceName(pTablet);
382+}
383+
384+const char* WacomGetSubTypeName(WACOMTABLET hTablet)
385+{
386+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
387+ if (!pTablet || !pTablet->GetSubTypeName) { errno=EBADF; return NULL; }
388+ return pTablet->GetSubTypeName(pTablet);
389+}
390+
391+const char* WacomGetModelName(WACOMTABLET hTablet)
392+{
393+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
394+ if (!pTablet || !pTablet->GetModelName) { errno=EBADF; return NULL; }
395+ return pTablet->GetModelName(pTablet);
396+}
397+
398+int WacomGetROMVersion(WACOMTABLET hTablet, int* pnMajor, int* pnMinor,
399+ int* pnRelease)
400+{
401+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
402+ if (!pTablet || !pTablet->GetROMVer) { errno=EBADF; return 0; }
403+ return pTablet->GetROMVer(pTablet,pnMajor,pnMinor,pnRelease);
404+}
405+
406+int WacomGetCapabilities(WACOMTABLET hTablet)
407+{
408+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
409+ if (!pTablet || !pTablet->GetCaps) { errno=EBADF; return 0; }
410+ return pTablet->GetCaps(pTablet);
411+}
412+
413+int WacomGetState(WACOMTABLET hTablet, WACOMSTATE* pState)
414+{
415+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
416+ if (!pTablet || !pTablet->GetState) { errno=EBADF; return 0; }
417+ return pTablet->GetState(pTablet,pState);
418+}
419+
420+int WacomGetFileDescriptor(WACOMTABLET hTablet)
421+{
422+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
423+ if (!pTablet || !pTablet->GetFD) { errno=EBADF; return -1; }
424+ return pTablet->GetFD(pTablet);
425+}
426+
427+int WacomReadRaw(WACOMTABLET hTablet, unsigned char* puchData,
428+ unsigned int uSize)
429+{
430+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
431+ if (!pTablet || !pTablet->ReadRaw) { errno=EBADF; return 0; }
432+ return pTablet->ReadRaw(pTablet,puchData,uSize);
433+}
434+
435+int WacomParseData(WACOMTABLET hTablet, const unsigned char* puchData,
436+ unsigned int uLength, WACOMSTATE* pState)
437+{
438+ WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet;
439+ if (!pTablet || !pTablet->ParseData) { errno=EBADF; return 0; }
440+ return pTablet->ParseData(pTablet,puchData,uLength,pState);
441+}
--- /dev/null
+++ b/wacom/wactablet.h
@@ -0,0 +1,328 @@
1+/*****************************************************************************
2+** wactablet.h
3+**
4+** Copyright (C) 2002 - 2004 - John E. Joganic
5+** Copyright (C) 2003 - 2008 - Ping Cheng
6+**
7+** This program is free software; you can redistribute it and/or
8+** modify it under the terms of the GNU General Public License
9+** as published by the Free Software Foundation; either version 2
10+** of the License, or (at your option) any later version.
11+**
12+** This program is distributed in the hope that it will be useful,
13+** but WITHOUT ANY WARRANTY; without even the implied warranty of
14+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+** GNU General Public License for more details.
16+**
17+** You should have received a copy of the GNU General Public License
18+** along with this program; if not, write to the Free Software
19+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20+**
21+****************************************************************************/
22+
23+#ifndef __LINUXWACOM_WACTABLET_H
24+#define __LINUXWACOM_WACTABLET_H
25+
26+#include <stdarg.h>
27+#include <sys/time.h>
28+
29+#define WACOMVENDOR_UNKNOWN 0x0000
30+#define WACOMVENDOR_WACOM 0x056A
31+#define WACOMVENDOR_TPC 0xFFFFFF01
32+
33+#define WACOMCLASS_SERIAL 0x0001
34+#define WACOMCLASS_USB 0x0002
35+
36+#define WACOMDEVICE_UNKNOWN 0x0000
37+#define WACOMDEVICE_ARTPAD 0x0001
38+#define WACOMDEVICE_ARTPADII 0x0002
39+#define WACOMDEVICE_DIGITIZER 0x0003
40+#define WACOMDEVICE_DIGITIZERII 0x0004
41+#define WACOMDEVICE_PENPARTNER 0x0005
42+#define WACOMDEVICE_GRAPHIRE 0x0006
43+#define WACOMDEVICE_GRAPHIRE2 0x0007
44+#define WACOMDEVICE_GRAPHIRE3 0x0008
45+#define WACOMDEVICE_GRAPHIRE4 0x0009
46+#define WACOMDEVICE_INTUOS 0x000A
47+#define WACOMDEVICE_INTUOS2 0x000B
48+#define WACOMDEVICE_CINTIQ 0x000C
49+#define WACOMDEVICE_PTU 0x000D
50+#define WACOMDEVICE_VOLITO 0x000E
51+#define WACOMDEVICE_VOLITO2 0x000F
52+#define WACOMDEVICE_TPC 0x0010
53+#define WACOMDEVICE_INTUOS3 0x0011
54+#define WACOMDEVICE_CINTIQV5 0x0012
55+#define WACOMDEVICE_MO 0x0013
56+
57+typedef struct _WACOMMODEL WACOMMODEL;
58+struct _WACOMMODEL
59+{
60+ unsigned int uClass;
61+ unsigned int uVendor;
62+ unsigned int uDevice;
63+ unsigned int uSubType;
64+};
65+
66+#define WACOMTOOLTYPE_NONE 0x00
67+#define WACOMTOOLTYPE_PEN 0x01
68+#define WACOMTOOLTYPE_PENCIL 0x02
69+#define WACOMTOOLTYPE_BRUSH 0x03
70+#define WACOMTOOLTYPE_ERASER 0x04
71+#define WACOMTOOLTYPE_AIRBRUSH 0x05
72+#define WACOMTOOLTYPE_MOUSE 0x06
73+#define WACOMTOOLTYPE_LENS 0x07
74+#define WACOMTOOLTYPE_PAD 0x08
75+#define WACOMTOOLTYPE_TOUCH 0x09
76+#define WACOMTOOLTYPE_MAX 0x0A
77+
78+#define WACOMBUTTON_LEFT 0
79+#define WACOMBUTTON_MIDDLE 1
80+#define WACOMBUTTON_RIGHT 2
81+#define WACOMBUTTON_EXTRA 3
82+#define WACOMBUTTON_SIDE 4
83+#define WACOMBUTTON_TOUCH 5
84+#define WACOMBUTTON_STYLUS 6
85+#define WACOMBUTTON_STYLUS2 7
86+#define WACOMBUTTON_BT0 8
87+#define WACOMBUTTON_BT1 9
88+#define WACOMBUTTON_BT2 10
89+#define WACOMBUTTON_BT3 11
90+#define WACOMBUTTON_BT4 12
91+#define WACOMBUTTON_BT5 13
92+#define WACOMBUTTON_BT6 14
93+#define WACOMBUTTON_BT7 15
94+#define WACOMBUTTON_BT8 16
95+#define WACOMBUTTON_BT9 17
96+#define WACOMBUTTON_BT10 18
97+#define WACOMBUTTON_BT11 19
98+#define WACOMBUTTON_BT12 20
99+#define WACOMBUTTON_BT13 21
100+#define WACOMBUTTON_BT14 22
101+#define WACOMBUTTON_BT15 23
102+#define WACOMBUTTON_BT16 24
103+#define WACOMBUTTON_BT17 25
104+#define WACOMBUTTON_BT18 26
105+#define WACOMBUTTON_BT19 27
106+#define WACOMBUTTON_BT20 28
107+#define WACOMBUTTON_BT21 29
108+#define WACOMBUTTON_BT22 30
109+#define WACOMBUTTON_BT23 31
110+#define WACOMBUTTON_MAX 32
111+
112+#define WACOMFIELD_TOOLTYPE 0
113+#define WACOMFIELD_SERIAL 1
114+#define WACOMFIELD_PROXIMITY 2
115+#define WACOMFIELD_BUTTONS 3
116+#define WACOMFIELD_POSITION_X 4
117+#define WACOMFIELD_POSITION_Y 5
118+#define WACOMFIELD_ROTATION_Z 6
119+#define WACOMFIELD_DISTANCE 7
120+#define WACOMFIELD_PRESSURE 8
121+#define WACOMFIELD_TILT_X 9
122+#define WACOMFIELD_TILT_Y 10
123+#define WACOMFIELD_ABSWHEEL 11
124+#define WACOMFIELD_RELWHEEL 12
125+#define WACOMFIELD_THROTTLE 13
126+#define WACOMFIELD_MAX 14
127+
128+typedef struct
129+{
130+ int nValue;
131+ int nMin;
132+ int nMax;
133+ int nReserved;
134+} WACOMVALUE;
135+
136+typedef struct
137+{
138+ unsigned int uValueCnt; /* This MUST be set to WACOMFIELD_MAX. */
139+ unsigned int uValid; /* Bit mask of WACOMFIELD_xxx bits. */
140+ WACOMVALUE values[WACOMFIELD_MAX];
141+} WACOMSTATE;
142+
143+#define WACOMSTATE_INIT { WACOMFIELD_MAX }
144+
145+typedef struct
146+{
147+ const char* pszName;
148+ const char* pszDesc;
149+ unsigned int uDeviceClass;
150+} WACOMCLASSREC;
151+
152+typedef struct
153+{
154+ const char* pszName;
155+ const char* pszDesc;
156+ const char* pszVendorName;
157+ const char* pszVendorDesc;
158+ const char* pszClass;
159+ WACOMMODEL model;
160+} WACOMDEVICEREC;
161+
162+typedef enum
163+{
164+ WACOMLOGLEVEL_NONE,
165+ WACOMLOGLEVEL_CRITICAL,
166+ WACOMLOGLEVEL_ERROR,
167+ WACOMLOGLEVEL_WARN,
168+ WACOMLOGLEVEL_INFO,
169+ WACOMLOGLEVEL_DEBUG,
170+ WACOMLOGLEVEL_TRACE,
171+ WACOMLOGLEVEL_MAX
172+} WACOMLOGLEVEL;
173+
174+typedef void (*WACOMLOGFUNC)(struct timeval tv, WACOMLOGLEVEL level,
175+ const char* pszLog);
176+
177+/*****************************************************************************
178+** Public structures
179+*****************************************************************************/
180+
181+typedef struct { int __unused; } *WACOMENGINE;
182+typedef struct { int __unused; } *WACOMTABLET;
183+
184+/*****************************************************************************
185+** Public API
186+*****************************************************************************/
187+
188+ WACOMENGINE WacomInitEngine(void);
189+ /* Initialize the tablet engine */
190+
191+ void WacomTermEngine(WACOMENGINE hEngine);
192+ /* Shutdown the tablet engine */
193+
194+ void WacomSetLogFunc(WACOMENGINE hEngine, WACOMLOGFUNC pfnLog);
195+ void WacomSetLogLevel(WACOMENGINE hEngine, WACOMLOGLEVEL level);
196+ void WacomLogV(WACOMENGINE hEngine, WACOMLOGLEVEL level,
197+ const char* pszFmt, va_list args);
198+ void WacomLog(WACOMENGINE hEngine, WACOMLOGLEVEL level,
199+ const char* pszFmt, ...);
200+ /* Logging functions. Default log function does nothing. */
201+
202+ int WacomGetSupportedClassList(WACOMCLASSREC** ppList, int* pnSize);
203+ /* Returns 0 on success. Pointer to class record list is returned to
204+ * ppList, and size of list to pnSize. Use WacomFreeList to release. */
205+
206+ int WacomGetSupportedDeviceList(unsigned int uDeviceClass,
207+ WACOMDEVICEREC** ppList, int* pnSize);
208+ /* Returns 0 on success. If device class is specified, only devices
209+ * of the request type are returned. A value of 0 will return all
210+ * devices for all classes. Pointer to device record list is returned to
211+ * ppList, and size of list to pnSize. Use WacomFreeList to release. */
212+
213+ void WacomFreeList(void* pvList);
214+ /* Releases list memory. */
215+
216+ int WacomCopyState(WACOMSTATE* pDest, WACOMSTATE* pSrc);
217+ /* Returns 0 on success. Copies tablet state structures.
218+ * Source and destination structures must be properly initialized,
219+ * particularly the uValueCnt field must be set WACOMFIELD_MAX.
220+ * Returns 0 on success. */
221+
222+ unsigned int WacomGetClassFromName(const char* pszName);
223+ /* Returns the device class for a given name. Returns 0, if unknown. */
224+
225+ unsigned int WacomGetDeviceFromName(const char* pszName,
226+ unsigned int uDeviceClass);
227+ /* Returns the device type for a given device name. If the
228+ * device class is specified, only that class will be searched.
229+ * Returns 0, if unknown. */
230+
231+ WACOMTABLET WacomOpenTablet(WACOMENGINE hEngine,
232+ const char* pszDevice, WACOMMODEL* pModel);
233+ /* Returns tablet handle on success, NULL otherwise.
234+ * pszDevice is pathname to device. Model may be NULL; if any model
235+ * parameters are 0, detection is automatic. */
236+
237+ void WacomCloseTablet(WACOMTABLET hTablet);
238+ /* Releases all resource associated with tablet and closes device. */
239+
240+ WACOMMODEL WacomGetModel(WACOMTABLET hTablet);
241+ /* Returns model (vendor, class, and device) of specified tablet. */
242+
243+ const char* WacomGetVendorName(WACOMTABLET hTablet);
244+ /* Returns vendor name as human-readable string. String is valid as
245+ * long as tablet handle is valid and does not need to be freed. */
246+
247+ const char* WacomGetClassName(WACOMTABLET hTablet);
248+ /* Returns class name as human-readable string. String is valid as
249+ * long as tablet handle is valid and does not need to be freed. */
250+
251+ const char* WacomGetDeviceName(WACOMTABLET hTablet);
252+ /* Returns device name as human-readable string. String is valid as
253+ * long as tablet handle is valid and does not need to be freed. */
254+
255+ const char* WacomGetSubTypeName(WACOMTABLET hTablet);
256+ /* Returns subtype name as human-readable string. This is typically
257+ * the model number (eg. ABC-1234). String is valid as long as tablet
258+ * handle is valid and does not need to be freed. */
259+
260+ const char* WacomGetModelName(WACOMTABLET hTablet);
261+ /* Returns model name as human-readable string. This is typically
262+ * the full model name (eg. FooCo FooModel 9x12). String is valid as
263+ * long as tablet handle is valid and does not need to be freed. */
264+
265+ int WacomGetROMVersion(WACOMTABLET hTablet, int* pnMajor, int* pnMinor,
266+ int* pnRelease);
267+ /* Returns 0 on success. ROM version of the tablet firmware is returned
268+ * in major, minor, and release values. If the caller needs to
269+ * distinguish between ROM versions to work around a bug, please consider
270+ * submitting a patch to this library. All tablets should appear
271+ * equivalent through this interface. This call is provided for
272+ * information purposes. */
273+
274+ int WacomGetCapabilities(WACOMTABLET hTablet);
275+ /* Returns bitmask of valid fields. Use (1 << WACOMFIELD_xxx) to
276+ * translate field identifiers to bit positions. This API will only
277+ * support 32 capabilities. */
278+
279+ int WacomGetState(WACOMTABLET hTablet, WACOMSTATE* pState);
280+ /* Returns 0 on success. Tablet state is copied to specified structure.
281+ * Data is only a snapshot of the device and may not accurately reflect
282+ * the position of any specific tool. This is particularly true of
283+ * multi-tool mode tablets. NOTE: pState must point to an initialized
284+ * structure; the uValueCnt field must be correctly set to
285+ * WACOMFIELD_MAX. */
286+
287+ int WacomGetFileDescriptor(WACOMTABLET hTablet);
288+ /* Returns the file descriptor of the tablet or -1 on error. */
289+
290+ int WacomReadRaw(WACOMTABLET hTablet, unsigned char* puchData,
291+ unsigned int uSize);
292+ /* Returns number of bytes read, typically a single packet. Call will
293+ * block. uSize should be the maximum size of the given data buffer. */
294+
295+ int WacomParseData(WACOMTABLET hTablet, const unsigned char* puchData,
296+ unsigned int uLength, WACOMSTATE* pState);
297+ /* Updates the tablet state from a given packet. If pState is specified,
298+ * the tablet state is copied to the given structure. This structure
299+ * must be correctly initialized; the uValueCnt member must be set to
300+ * WACOMFIELD_MAX. Returns 0 on success. */
301+
302+/*****************************************************************************
303+** Private structures
304+*****************************************************************************/
305+
306+typedef struct _WACOMTABLET_PRIV WACOMTABLET_PRIV;
307+
308+struct _WACOMTABLET_PRIV
309+{
310+ void (*Close)(WACOMTABLET_PRIV* pTablet);
311+ WACOMMODEL (*GetModel)(WACOMTABLET_PRIV* pTablet);
312+ const char* (*GetVendorName)(WACOMTABLET_PRIV* pTablet);
313+ const char* (*GetClassName)(WACOMTABLET_PRIV* pTablet);
314+ const char* (*GetDeviceName)(WACOMTABLET_PRIV* pTablet);
315+ const char* (*GetSubTypeName)(WACOMTABLET_PRIV* pTablet);
316+ const char* (*GetModelName)(WACOMTABLET_PRIV* pTablet);
317+ int (*GetROMVer)(WACOMTABLET_PRIV* pTablet, int* pnMajor, int* pnMinor,
318+ int* pnRelease);
319+ int (*GetCaps)(WACOMTABLET_PRIV* pTablet);
320+ int (*GetState)(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState);
321+ int (*GetFD)(WACOMTABLET_PRIV* pTablet);
322+ int (*ReadRaw)(WACOMTABLET_PRIV* pTablet, unsigned char* puchData,
323+ unsigned int uSize);
324+ int (*ParseData)(WACOMTABLET_PRIV* pTablet, const unsigned char* puchData,
325+ unsigned int uLength, WACOMSTATE* pState);
326+};
327+
328+#endif /* __LINUXWACOM_WACTABLET_H */
--- /dev/null
+++ b/wacom/wacusb.c
@@ -0,0 +1,1054 @@
1+/*****************************************************************************
2+** wacusb.c
3+**
4+** Copyright (C) 2002 - 2004 - John E. Joganic
5+** Copyright (C) 2003 - 2009 - Ping Cheng
6+**
7+** This program is free software; you can redistribute it and/or
8+** modify it under the terms of the GNU General Public License
9+** as published by the Free Software Foundation; either version 2
10+** of the License, or (at your option) any later version.
11+**
12+** This program is distributed in the hope that it will be useful,
13+** but WITHOUT ANY WARRANTY; without even the implied warranty of
14+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+** GNU General Public License for more details.
16+**
17+** You should have received a copy of the GNU General Public License
18+** along with this program; if not, write to the Free Software
19+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20+**
21+****************************************************************************/
22+
23+#include "wacusb.h"
24+
25+#include <stdio.h>
26+#include <stdlib.h>
27+#include <string.h>
28+#include <errno.h>
29+#include <fcntl.h>
30+#include <unistd.h>
31+#include <sys/ioctl.h>
32+#include <assert.h>
33+
34+/*****************************************************************************
35+** Begin USB Linux Input Subsystem
36+*****************************************************************************/
37+
38+#ifdef WCM_ENABLE_LINUXINPUT
39+#include <linux/input.h>
40+
41+#ifndef EV_MSC
42+#define EV_MSC 0x04
43+#endif
44+
45+#ifndef MSC_SERIAL
46+#define MSC_SERIAL 0x00
47+#endif
48+
49+#ifndef BTN_TOOL_DOUBLETAP
50+#define BTN_TOOL_DOUBLETAP 0x14d
51+#endif
52+
53+/*****************************************************************************
54+** Defines
55+*****************************************************************************/
56+
57+/* from linux/input.h */
58+#define BITS_PER_LONG (sizeof(long) * 8)
59+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
60+#define BIT(x) (1UL<<((x)%BITS_PER_LONG))
61+#define LONG(x) ((x)/BITS_PER_LONG)
62+#define ISBITSET(x,y) ((x)[LONG(y)] & BIT(y))
63+
64+#define MAX_CHANNELS 2
65+#define MAX_USB_EVENTS 32
66+
67+/*****************************************************************************
68+** Structures
69+*****************************************************************************/
70+
71+typedef struct _USBSUBTYPE USBSUBTYPE;
72+typedef struct _USBDEVICE USBDEVICE;
73+typedef struct _USBVENDOR USBVENDOR;
74+
75+struct _USBSUBTYPE
76+{
77+ const char* pszName;
78+ const char* pszDesc;
79+ unsigned int uSubType;
80+ unsigned int uProduct;
81+};
82+
83+struct _USBDEVICE
84+{
85+ const char* pszName;
86+ const char* pszDesc;
87+ unsigned int uDevice;
88+ USBSUBTYPE* pSubTypes;
89+ unsigned int uChannelCnt;
90+};
91+
92+struct _USBVENDOR
93+{
94+ const char* pszName;
95+ const char* pszDesc;
96+ unsigned int uVendor;
97+ USBDEVICE* pDevices;
98+};
99+
100+/*****************************************************************************
101+** USB Tablet object
102+*****************************************************************************/
103+
104+typedef struct _USBTABLET USBTABLET;
105+
106+struct _USBTABLET
107+{
108+ WACOMTABLET_PRIV tablet;
109+ WACOMENGINE hEngine;
110+ int fd;
111+ USBVENDOR* pVendor;
112+ USBDEVICE* pDevice;
113+ USBSUBTYPE* pSubType;
114+ int nBus;
115+ int nVersion;
116+ WACOMSTATE state[MAX_CHANNELS];
117+ int nToolProx[MAX_CHANNELS];
118+ int nChannel;
119+ int nLastToolSerial;
120+ int nEventCnt;
121+ struct input_event events[MAX_USB_EVENTS];
122+};
123+
124+/*****************************************************************************
125+** Autodetect pad key codes
126+*****************************************************************************/
127+
128+static unsigned short padkey_codes [] =
129+{
130+ BTN_0, BTN_1, BTN_2, BTN_3, BTN_4,
131+ BTN_5, BTN_6, BTN_7, BTN_8, BTN_9,
132+ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z,
133+ BTN_BASE, BTN_BASE2, BTN_BASE3,
134+ BTN_BASE4, BTN_BASE5, BTN_BASE6,
135+ BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_SELECT
136+};
137+
138+int gPadKeys [WACOMBUTTON_MAX];
139+int gNumPadKeys;
140+
141+/*****************************************************************************
142+** Static Prototypes
143+*****************************************************************************/
144+
145+static void USBClose(WACOMTABLET_PRIV* pTablet);
146+static WACOMMODEL USBGetModel(WACOMTABLET_PRIV* pTablet);
147+static const char* USBGetVendorName(WACOMTABLET_PRIV* pTablet);
148+static const char* USBGetClassName(WACOMTABLET_PRIV* pTablet);
149+static const char* USBGetDeviceName(WACOMTABLET_PRIV* pTablet);
150+static const char* USBGetSubTypeName(WACOMTABLET_PRIV* pTablet);
151+static const char* USBGetModelName(WACOMTABLET_PRIV* pTablet);
152+static int USBGetROMVer(WACOMTABLET_PRIV* pTablet, int* pnMajor,
153+ int* pnMinor, int* pnRelease);
154+static int USBGetCaps(WACOMTABLET_PRIV* pTablet);
155+static int USBGetState(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState);
156+static int USBGetFD(WACOMTABLET_PRIV* pTablet);
157+static int USBReadRaw(WACOMTABLET_PRIV* pTablet, unsigned char* puchData,
158+ unsigned int uSize);
159+static int USBParseData(WACOMTABLET_PRIV* pTablet,
160+ const unsigned char* puchData, unsigned int uLength,
161+ WACOMSTATE* pState);
162+
163+static int USBFindModel(USBTABLET* pUSB, unsigned int uVendor,
164+ unsigned int uProduct);
165+static int USBIdentifyModel(USBTABLET* pUSB);
166+
167+/*****************************************************************************
168+** Globals
169+*****************************************************************************/
170+
171+ static USBSUBTYPE xPenPartner[] =
172+ {
173+ { "MODEL_PP_0405", "Wacom PenPartner", 1, 0x00 },
174+ { NULL }
175+ };
176+
177+ static USBSUBTYPE xGraphire[] =
178+ {
179+ { "ET_0405", "Wacom Graphire", 1, 0x10 },
180+ { NULL }
181+ };
182+
183+ static USBSUBTYPE xGraphire2[] =
184+ {
185+ { "ET_0405", "Wacom Graphire2 4x5", 1, 0x11 },
186+ { "ET_0507", "Wacom Graphire2 5x7", 2, 0x12 },
187+ { NULL }
188+ };
189+
190+ static USBSUBTYPE xGraphire3[] =
191+ {
192+ { "ET_0405", "Wacom Graphire3 4x5", 1, 0x13 },
193+ { "ET_0608", "Wacom Graphire3 6x8", 2, 0x14 },
194+ { NULL }
195+ };
196+
197+ static USBSUBTYPE xGraphire4[] =
198+ {
199+ { "CTE_440", "Wacom Graphire4 4x5", 1, 0x15 },
200+ { "CTE_640", "Wacom Graphire4 6x8", 2, 0x16 },
201+ { NULL }
202+ };
203+
204+ static USBSUBTYPE xIntuos[] =
205+ {
206+ { "GD_0405-U", "Wacom Intuos 4x5", 1, 0x20 },
207+ { "GD_0608-U", "Wacom Intuos 6x8", 2, 0x21 },
208+ { "GD_0912-U", "Wacom Intuos 9x12", 3, 0x22 },
209+ { "GD_1212-U", "Wacom Intuos 12x12", 4, 0x23 },
210+ { "GD_1218-U", "Wacom Intuos 12x18", 5, 0x24 },
211+ { NULL }
212+ };
213+
214+ static USBSUBTYPE xCintiq[] =
215+ {
216+ { "PL400", "Wacom PL400", 1, 0x30 },
217+ { "PL500", "Wacom PL500", 2, 0x31 },
218+ { "PL600", "Wacom PL600", 3, 0x32 },
219+ { "PL600SX", "Wacom PL600SX", 4, 0x33 },
220+ { "PL550", "Wacom PL550", 5, 0x34 },
221+ { "PL800", "Wacom PL800", 6, 0x35 },
222+ { "PL700", "Wacom PL700", 7, 0x37 },
223+ { "PL510", "Wacom PL510", 8, 0x38 },
224+ { "DTU710", "Wacom PL710", 9, 0x39 },
225+ { "DTF720", "Wacom DTF720", 10, 0xC0 },
226+ { "DTF521", "Wacom DTF521", 11, 0xC4 },
227+ { "DTU1931", "Wacom DTU1931", 12, 0xC7 },
228+ { "DTU2231", "Wacom DTU2231", 13, 0xCE },
229+ { "DTU1631", "Wacom DTU1631", 14, 0xF0 },
230+ { NULL }
231+ };
232+
233+ static USBSUBTYPE xIntuos2[] =
234+ {
235+ { "XD-0405-U", "Wacom Intuos2 4x5", 1, 0x41 },
236+ { "XD-0608-U", "Wacom Intuos2 6x8", 2, 0x42 },
237+ { "XD-0912-U", "Wacom Intuos2 9x12", 3, 0x43 },
238+ { "XD-1212-U", "Wacom Intuos2 12x12", 4, 0x44 },
239+ { "XD-1218-U", "Wacom Intuos2 12x18", 5, 0x45 },
240+
241+ /* fix for I2 6x8's reporting as 0x47 */
242+ { "XD-0608-U", "Wacom Intuos2 6x8", 2, 0x47 },
243+
244+ { NULL }
245+ };
246+
247+ static USBSUBTYPE xVolito[] =
248+ {
249+ { "MODEL-VOL", "Wacom Volito", 1, 0x60 },
250+ { NULL }
251+ };
252+
253+ static USBSUBTYPE xVolito2[] =
254+ {
255+ { "FT-0203-U", "Wacom PenStation", 1, 0x61 },
256+ { "CTF-420-U", "Wacom Volito2 4x5", 2, 0x62 },
257+ { "CTF-220-U", "Wacom Volito2 2x3", 3, 0x63 },
258+ { "CTF-421-U", "Wacom PenPartner2", 4, 0x64 },
259+ { "CTF_430-U", "Wacom Bamboo1", 5, 0x69 },
260+ { NULL }
261+ };
262+
263+ static USBSUBTYPE xBamboo[] =
264+ {
265+ { "MTE_450", "Wacom Bamboo", 1, 0x65 },
266+ { "CTE_450", "Wacom BambooFun 4x5", 2, 0x17 },
267+ { "CTE_650", "Wacom BambooFun 6x8", 3, 0x18 },
268+ { "CTE_631", "Wacom Bamboo1 Medium", 4, 0x19 },
269+ { NULL }
270+ };
271+
272+ static USBSUBTYPE xCintiqPartner[] =
273+ {
274+ { "PTU-600", "Wacom Cintiq Partner", 1, 0x03 },
275+ { NULL }
276+ };
277+
278+ static USBSUBTYPE xTabletPC[] =
279+ {
280+ { "TPC-090", "Wacom Tablet PC90", 1, 0x90 },
281+ { "TPC-093", "Wacom Tablet PC93", 2, 0x93 },
282+ { "TPC-09A", "Wacom Tablet PC9A", 3, 0x9A },
283+ { NULL }
284+ };
285+
286+ static USBSUBTYPE xCintiqV5[] =
287+ {
288+ { "DTZ-21ux", "Wacom Cintiq 21UX", 1, 0x3F },
289+ { "DTZ-20wsx", "Wacom Cintiq 20WSX", 2, 0xC5 },
290+ { "DTZ-12wx", "Wacom Cintiq 12WX", 3, 0xC6 },
291+ { "DTZ-21ux2", "Wacom Cintiq 21UX2", 4, 0xCC },
292+ { NULL }
293+ };
294+
295+ static USBSUBTYPE xIntuos3[] =
296+ {
297+ { "PTZ-430", "Wacom Intuos3 4x5", 1, 0xB0 },
298+ { "PTZ-630", "Wacom Intuos3 6x8", 2, 0xB1 },
299+ { "PTZ-930", "Wacom Intuos3 9x12", 3, 0xB2 },
300+ { "PTZ-1230", "Wacom Intuos3 12x12", 4, 0xB3 },
301+ { "PTZ-1231W", "Wacom Intuos3 12x19", 5, 0xB4 },
302+ { "PTZ-631W", "Wacom Intuos3 6x11", 6, 0xB5 },
303+ { "PTZ-431W", "Wacom Intuos3 4x6", 7, 0xB7 },
304+ { NULL }
305+ };
306+
307+ static USBSUBTYPE xIntuos4[] =
308+ {
309+ { "PTK-440", "Wacom Intuos4 4x6", 1, 0xB8 },
310+ { "PTK-640", "Wacom Intuos4 6x9", 2, 0xB9 },
311+ { "PTK-840", "Wacom Intuos4 8x13", 3, 0xBA },
312+ { "PTK-1240", "Wacom Intuos4 12x19", 4, 0xBB },
313+ { "PTK-540WL", "Wacom Intuos4 WLUSB", 5, 0xBC },
314+ { "PTK-540WL", "Wacom Intuos4 WLBT", 6, 0xBD },
315+ { NULL }
316+ };
317+
318+ static USBDEVICE xWacomDevices[] =
319+ {
320+ { "pp", "PenPartner", WACOMDEVICE_PENPARTNER, xPenPartner, 1 },
321+ { "gr", "Graphire", WACOMDEVICE_GRAPHIRE, xGraphire, 1 },
322+ { "gr2", "Graphire2", WACOMDEVICE_GRAPHIRE2, xGraphire2, 1 },
323+ { "gr3", "Graphire3", WACOMDEVICE_GRAPHIRE3, xGraphire3, 1 },
324+ { "gr4", "Graphire4", WACOMDEVICE_GRAPHIRE4, xGraphire4, 2 },
325+ { "int", "Intuos", WACOMDEVICE_INTUOS, xIntuos, 2 },
326+ { "int2", "Intuos2", WACOMDEVICE_INTUOS2, xIntuos2, 2 },
327+ { "int3", "Intuos3", WACOMDEVICE_INTUOS3, xIntuos3, 2 },
328+ { "int4", "Intuos4", WACOMDEVICE_INTUOS3, xIntuos4, 2 },
329+ { "ctq", "Cintiq (V5)", WACOMDEVICE_CINTIQV5, xCintiqV5, 2 },
330+ { "pl", "Cintiq (PL)", WACOMDEVICE_CINTIQ, xCintiq, 1 },
331+ { "ptu", "Cintiq Partner (PTU)", WACOMDEVICE_PTU, xCintiqPartner, 1 },
332+ { "vol", "Volito", WACOMDEVICE_VOLITO, xVolito, 1 },
333+ { "vol2", "Volito2", WACOMDEVICE_VOLITO2, xVolito2, 1 },
334+ { "mo", "Bamboo", WACOMDEVICE_MO, xBamboo, 2 },
335+ { "tpc", "Tablet PC", WACOMDEVICE_TPC, xTabletPC, 1 },
336+ { NULL }
337+ };
338+
339+ static USBVENDOR xVendors[] =
340+ {
341+ { "wacom", "Wacom", WACOMVENDOR_WACOM, xWacomDevices },
342+ { NULL },
343+ };
344+
345+/*****************************************************************************
346+** Public Functions
347+*****************************************************************************/
348+
349+typedef struct
350+{
351+ void (*pfnFree)(void* pv);
352+} DEVLIST_INTERNAL;
353+
354+static void USBFreeDeviceList(void* pv)
355+{
356+ DEVLIST_INTERNAL* pInt = ((DEVLIST_INTERNAL*)pv) - 1;
357+ free(pInt);
358+}
359+
360+int WacomGetSupportedUSBDeviceList(WACOMDEVICEREC** ppList, int* pnSize)
361+{
362+ int nIndex=0, nCnt=0;
363+ DEVLIST_INTERNAL* pInt;
364+ USBDEVICE* pDev;
365+ USBVENDOR* pVendor;
366+ WACOMDEVICEREC* pRec;
367+
368+ if (!ppList || !pnSize) { errno = EINVAL; return 1; }
369+
370+ /* for each vendor, count up devices */
371+ for (pVendor=xVendors; pVendor->pszName; ++pVendor)
372+ {
373+ /* count up devices */
374+ for (pDev=pVendor->pDevices; pDev->pszName; ++pDev, ++nCnt) ;
375+ }
376+
377+ /* allocate enough memory to hold internal structure and all records */
378+ pInt = (DEVLIST_INTERNAL*)malloc(sizeof(DEVLIST_INTERNAL) +
379+ (sizeof(WACOMDEVICEREC) * nCnt));
380+
381+ pInt->pfnFree = USBFreeDeviceList;
382+ pRec = (WACOMDEVICEREC*)(pInt + 1);
383+
384+ /* for each vendor, add devices */
385+ for (pVendor=xVendors; pVendor->pszName; ++pVendor)
386+ {
387+ for (pDev=pVendor->pDevices; pDev->pszName; ++pDev, ++nIndex)
388+ {
389+ pRec[nIndex].pszName = pDev->pszName;
390+ pRec[nIndex].pszDesc = pDev->pszDesc;
391+ pRec[nIndex].pszVendorName = pVendor->pszName;
392+ pRec[nIndex].pszVendorDesc = pVendor->pszDesc;
393+ pRec[nIndex].pszClass = "usb";
394+ pRec[nIndex].model.uClass = WACOMCLASS_USB;
395+ pRec[nIndex].model.uVendor = pVendor->uVendor;
396+ pRec[nIndex].model.uDevice = pDev->uDevice;
397+ pRec[nIndex].model.uSubType = 0;
398+ }
399+ }
400+ assert(nIndex == nCnt);
401+
402+ *ppList = pRec;
403+ *pnSize = nCnt;
404+ return 0;
405+}
406+
407+unsigned int WacomGetUSBDeviceFromName(const char* pszName)
408+{
409+ USBDEVICE* pDev;
410+ USBVENDOR* pVendor;
411+
412+ if (!pszName) { errno = EINVAL; return 0; }
413+
414+ /* for each vendor, look for device */
415+ for (pVendor=xVendors; pVendor->pszName; ++pVendor)
416+ {
417+ /* count up devices */
418+ for (pDev=pVendor->pDevices; pDev->pszName; ++pDev)
419+ {
420+ if (strcasecmp(pszName,pDev->pszName) == 0)
421+ return pDev->uDevice;
422+ }
423+ }
424+
425+ errno = ENOENT;
426+ return 0;
427+}
428+
429+WACOMTABLET WacomOpenUSBTablet(WACOMENGINE hEngine, int fd, WACOMMODEL* pModel)
430+{
431+ USBTABLET* pUSB = NULL;
432+
433+ /* Allocate tablet */
434+ pUSB = (USBTABLET*)malloc(sizeof(USBTABLET));
435+ memset(pUSB,0,sizeof(*pUSB));
436+ pUSB->tablet.Close = USBClose;
437+ pUSB->tablet.GetModel = USBGetModel;
438+ pUSB->tablet.GetVendorName = USBGetVendorName;
439+ pUSB->tablet.GetClassName = USBGetClassName;
440+ pUSB->tablet.GetDeviceName = USBGetDeviceName;
441+ pUSB->tablet.GetSubTypeName = USBGetSubTypeName;
442+ pUSB->tablet.GetModelName = USBGetModelName;
443+ pUSB->tablet.GetROMVer = USBGetROMVer;
444+ pUSB->tablet.GetCaps = USBGetCaps;
445+ pUSB->tablet.GetState = USBGetState;
446+ pUSB->tablet.GetFD = USBGetFD;
447+ pUSB->tablet.ReadRaw = USBReadRaw;
448+ pUSB->tablet.ParseData = USBParseData;
449+ pUSB->hEngine = hEngine;
450+ pUSB->fd = fd;
451+
452+ /* Identify and initialize the model */
453+ if (USBIdentifyModel(pUSB))
454+ {
455+ WacomLog(pUSB->hEngine,WACOMLOGLEVEL_ERROR,
456+ "Failed to identify model: %s",strerror(errno));
457+ free(pUSB);
458+ return NULL;
459+ }
460+
461+ return (WACOMTABLET)pUSB;
462+}
463+
464+/*****************************************************************************
465+** Tablet Functions
466+*****************************************************************************/
467+
468+static void USBClose(WACOMTABLET_PRIV* pTablet)
469+{
470+ USBTABLET* pUSB = (USBTABLET*)pTablet;
471+ close(pUSB->fd);
472+ free(pUSB);
473+}
474+
475+static WACOMMODEL USBGetModel(WACOMTABLET_PRIV* pTablet)
476+{
477+ WACOMMODEL model = { 0 };
478+ USBTABLET* pUSB = (USBTABLET*)pTablet;
479+ model.uClass = WACOMCLASS_USB;
480+ model.uVendor = pUSB->pVendor->uVendor;
481+ model.uDevice = pUSB->pDevice->uDevice;
482+ model.uSubType = pUSB->pSubType->uSubType;
483+ return model;
484+}
485+
486+static int USBGetRange(USBTABLET* pUSB, unsigned long* pBits, int nAbsField,
487+ unsigned int uField)
488+{
489+ int nAbs[5];
490+
491+ if (!ISBITSET(pBits,nAbsField))
492+ return 0;
493+ if (ioctl(pUSB->fd, EVIOCGABS(nAbsField), nAbs) != 0)
494+ return 1;
495+
496+ pUSB->state[0].values[uField].nMin = nAbs[1];
497+ pUSB->state[0].values[uField].nMax = nAbs[2];
498+ pUSB->state[0].uValid |= BIT(uField);
499+ return 0;
500+}
501+
502+static int USBFindModel(USBTABLET* pUSB, unsigned int uVendor,
503+ unsigned int uProduct)
504+{
505+ USBVENDOR* pVendor = NULL;
506+ USBDEVICE* pDev = NULL;
507+ USBSUBTYPE* pSub = NULL;
508+
509+ static USBSUBTYPE xUnkSub[] =
510+ {
511+ { "UNKNOWN", "Unknown", 1, 0x00 },
512+ { NULL }
513+ };
514+
515+ static USBDEVICE xUnkDev[] =
516+ {
517+ { "unk", "Unknown", WACOMDEVICE_UNKNOWN, xUnkSub, MAX_CHANNELS },
518+ { NULL }
519+ };
520+
521+ static USBVENDOR xUnkVendor =
522+ { "unknown", "Unknown", WACOMVENDOR_UNKNOWN, xUnkDev };
523+
524+ for (pVendor=xVendors; pVendor->pszName; ++pVendor)
525+ {
526+ if (pVendor->uVendor == uVendor)
527+ {
528+ for (pDev=pVendor->pDevices; pDev->pszName; ++pDev)
529+ {
530+ for (pSub=pDev->pSubTypes; pSub->pszName; ++pSub)
531+ {
532+ if (pSub->uProduct == uProduct)
533+ {
534+ pUSB->pVendor = pVendor;
535+ pUSB->pDevice = pDev;
536+ pUSB->pSubType = pSub;
537+ return 0;
538+ }
539+ }
540+ }
541+
542+ /* only one vendor of this type, so we're done */
543+ break;
544+ }
545+ }
546+
547+ /* unknown vendor */
548+ pUSB->pVendor = &xUnkVendor;
549+ pUSB->pDevice = pUSB->pVendor->pDevices;
550+ pUSB->pSubType = pUSB->pDevice->pSubTypes;
551+ return 0;
552+}
553+
554+static int USBIdentifyModel(USBTABLET* pUSB)
555+{
556+ int nCnt, chcnt;
557+ short sID[4];
558+ unsigned int uVendor, uProduct;
559+ unsigned long evbits[NBITS(EV_MAX)];
560+ unsigned long absbits[NBITS(ABS_MAX)];
561+ unsigned long relbits[NBITS(REL_MAX)];
562+ unsigned long keybits[NBITS(KEY_MAX)];
563+
564+ /* Get device name and id */
565+ if (ioctl(pUSB->fd,EVIOCGID,sID) < 0)
566+ return 1;
567+
568+ /* initialize state structure */
569+ pUSB->state[0].uValueCnt = WACOMFIELD_MAX;
570+
571+ /* Get event types supported */
572+ nCnt = ioctl(pUSB->fd,EVIOCGBIT(0 /*EV*/,sizeof(evbits)),evbits);
573+ if (nCnt < 0)
574+ {
575+ WacomLog(pUSB->hEngine,WACOMLOGLEVEL_ERROR,
576+ "Failed to CGBIT ev: %s",strerror(errno));
577+ return 1;
578+ }
579+ assert(nCnt == sizeof(evbits));
580+
581+ /* absolute events */
582+ if (ISBITSET(evbits,EV_ABS))
583+ {
584+ nCnt = ioctl(pUSB->fd,EVIOCGBIT(EV_ABS,sizeof(absbits)),absbits);
585+ if (nCnt < 0)
586+ {
587+ WacomLog(pUSB->hEngine,WACOMLOGLEVEL_ERROR,
588+ "Failed to CGBIT abs: %s",strerror(errno));
589+ return 1;
590+ }
591+
592+ /* the following line has problem on Debian systems
593+ assert(nCnt == sizeof(absbits));
594+ */
595+
596+ if (USBGetRange(pUSB,absbits,ABS_X,WACOMFIELD_POSITION_X) ||
597+ USBGetRange(pUSB,absbits,ABS_Y,WACOMFIELD_POSITION_Y) ||
598+ USBGetRange(pUSB,absbits,ABS_RZ,WACOMFIELD_ROTATION_Z) ||
599+ USBGetRange(pUSB,absbits,ABS_DISTANCE,WACOMFIELD_DISTANCE) ||
600+ USBGetRange(pUSB,absbits,ABS_PRESSURE,WACOMFIELD_PRESSURE) ||
601+ USBGetRange(pUSB,absbits,ABS_TILT_X,WACOMFIELD_TILT_X) ||
602+ USBGetRange(pUSB,absbits,ABS_TILT_Y,WACOMFIELD_TILT_Y) ||
603+ USBGetRange(pUSB,absbits,ABS_WHEEL,WACOMFIELD_ABSWHEEL) ||
604+ USBGetRange(pUSB,absbits,ABS_THROTTLE,WACOMFIELD_THROTTLE))
605+ return 1;
606+
607+ }
608+
609+ /* relative events */
610+ if (ISBITSET(evbits,EV_REL))
611+ {
612+ nCnt = ioctl(pUSB->fd,EVIOCGBIT(EV_REL,sizeof(relbits)),relbits);
613+ if (nCnt < 0)
614+ {
615+ WacomLog(pUSB->hEngine,WACOMLOGLEVEL_ERROR,
616+ "Failed to CGBIT rel: %s",strerror(errno));
617+ return 1;
618+ }
619+ assert(nCnt == sizeof(relbits));
620+
621+ if (ISBITSET(relbits,REL_WHEEL))
622+ {
623+ pUSB->state[0].uValid |= BIT(WACOMFIELD_RELWHEEL);
624+ pUSB->state[0].values[WACOMFIELD_RELWHEEL].nMin = -1;
625+ pUSB->state[0].values[WACOMFIELD_RELWHEEL].nMax = 1;
626+ }
627+ }
628+
629+ /* key events */
630+ gNumPadKeys = 0;
631+ if (ISBITSET(evbits,EV_KEY))
632+ {
633+ nCnt = ioctl(pUSB->fd,EVIOCGBIT(EV_KEY,sizeof(keybits)),keybits);
634+ if (nCnt < 0)
635+ {
636+ WacomLog(pUSB->hEngine,WACOMLOGLEVEL_ERROR,
637+ "Failed to CGBIT key: %s",strerror(errno));
638+ return 1;
639+ }
640+ assert(nCnt == sizeof(keybits));
641+
642+ /* button events */
643+ if (ISBITSET(keybits,BTN_LEFT) ||
644+ ISBITSET(keybits,BTN_RIGHT) ||
645+ ISBITSET(keybits,BTN_MIDDLE) ||
646+ ISBITSET(keybits,BTN_SIDE) ||
647+ ISBITSET(keybits,BTN_EXTRA))
648+ pUSB->state[0].uValid |= BIT(WACOMFIELD_BUTTONS);
649+
650+ /* tool events */
651+ if (ISBITSET(keybits,BTN_TOOL_PEN) ||
652+ ISBITSET(keybits,BTN_TOOL_RUBBER) ||
653+ ISBITSET(keybits,BTN_TOOL_BRUSH) ||
654+ ISBITSET(keybits,BTN_TOOL_PENCIL) ||
655+ ISBITSET(keybits,BTN_TOOL_AIRBRUSH) ||
656+ ISBITSET(keybits,BTN_TOOL_FINGER) ||
657+ ISBITSET(keybits,BTN_TOOL_MOUSE) ||
658+ ISBITSET(keybits,BTN_TOOL_LENS) ||
659+ ISBITSET(keybits,BTN_TOOL_DOUBLETAP))
660+ pUSB->state[0].uValid |= BIT(WACOMFIELD_PROXIMITY) |
661+ BIT(WACOMFIELD_TOOLTYPE);
662+
663+ /* Detect button codes */
664+ for (nCnt = 0; nCnt < WACOMBUTTON_MAX; nCnt++)
665+ if (ISBITSET (keybits, padkey_codes [nCnt]))
666+ gPadKeys [gNumPadKeys++] = padkey_codes [nCnt];
667+ }
668+
669+ /* set identification */
670+ pUSB->nBus = sID[0];
671+ uVendor = sID[1];
672+ uProduct = sID[2];
673+ pUSB->nVersion = sID[3];
674+
675+ if (USBFindModel(pUSB,uVendor,uProduct))
676+ return 1;
677+
678+ /* add additional capabilities by device type */
679+ switch (pUSB->pDevice->uDevice)
680+ {
681+ case WACOMDEVICE_MO:
682+ case WACOMDEVICE_GRAPHIRE4:
683+ case WACOMDEVICE_INTUOS:
684+ case WACOMDEVICE_INTUOS2:
685+ case WACOMDEVICE_INTUOS3:
686+ case WACOMDEVICE_CINTIQV5:
687+ pUSB->state[0].uValid |= BIT(WACOMFIELD_SERIAL);
688+ default: ;
689+ }
690+
691+ /* Initialize all channels with the same values */
692+ for(chcnt=1; chcnt<MAX_CHANNELS; chcnt++)
693+ pUSB->state[chcnt] = pUSB->state[0];
694+
695+ return 0;
696+}
697+
698+static const char* USBGetVendorName(WACOMTABLET_PRIV* pTablet)
699+{
700+ USBTABLET* pUSB = (USBTABLET*)pTablet;
701+ return pUSB->pVendor->pszDesc;
702+}
703+
704+static const char* USBGetClassName(WACOMTABLET_PRIV* pTablet)
705+{
706+ return "USB";
707+}
708+
709+static const char* USBGetDeviceName(WACOMTABLET_PRIV* pTablet)
710+{
711+ USBTABLET* pUSB = (USBTABLET*)pTablet;
712+ return pUSB->pDevice->pszDesc;
713+}
714+
715+static const char* USBGetSubTypeName(WACOMTABLET_PRIV* pTablet)
716+{
717+ USBTABLET* pUSB = (USBTABLET*)pTablet;
718+ return pUSB->pSubType->pszName;
719+}
720+
721+static const char* USBGetModelName(WACOMTABLET_PRIV* pTablet)
722+{
723+ USBTABLET* pUSB = (USBTABLET*)pTablet;
724+ return pUSB->pSubType->pszDesc;
725+}
726+
727+static int USBGetROMVer(WACOMTABLET_PRIV* pTablet, int* pnMajor,
728+ int* pnMinor, int* pnRelease)
729+{
730+ USBTABLET* pUSB = (USBTABLET*)pTablet;
731+ if (!pnMajor) { errno=EINVAL; return 1; }
732+
733+ /* how is the version number broken down?
734+ * mine says 0x115. is that 1.1.5 or 1.15? */
735+ *pnMajor = pUSB->nVersion >> 8;
736+ *pnMinor = (pUSB->nVersion >> 4) & 0xF;
737+ *pnRelease = (pUSB->nVersion & 0xF);
738+ return 0;
739+}
740+
741+static int USBGetCaps(WACOMTABLET_PRIV* pTablet)
742+{
743+ USBTABLET* pUSB = (USBTABLET*)pTablet;
744+ return pUSB->state[0].uValid;
745+}
746+
747+static int USBGetState(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState)
748+{
749+ USBTABLET* pUSB = (USBTABLET*)pTablet;
750+ return WacomCopyState(pState,&pUSB->state[pUSB->nChannel]);
751+}
752+
753+static int USBGetFD(WACOMTABLET_PRIV* pTablet)
754+{
755+ USBTABLET* pUSB = (USBTABLET*)pTablet;
756+ return pUSB->fd;
757+}
758+
759+static int USBReadRaw(WACOMTABLET_PRIV* pTablet, unsigned char* puchData,
760+ unsigned int uSize)
761+{
762+ int nXfer;
763+ unsigned int uCnt, uPacketLength;
764+ USBTABLET* pUSB = (USBTABLET*)pTablet;
765+ uPacketLength = sizeof(struct input_event);
766+
767+ /* check size of buffer */
768+ if (uSize < uPacketLength) { errno=EINVAL; return 0; }
769+
770+ for (uCnt=0; uCnt<uPacketLength; uCnt+=nXfer)
771+ {
772+ nXfer = read(pUSB->fd,puchData+uCnt,uPacketLength-uCnt);
773+ if (nXfer <= 0) return nXfer;
774+ }
775+
776+ return (signed)uCnt;
777+}
778+
779+static int USBParseMSC(USBTABLET* pUSB, struct input_event* pEv)
780+{
781+ if (pEv->code == MSC_SERIAL && pEv->value)
782+ pUSB->state[pUSB->nChannel].values[WACOMFIELD_SERIAL].nValue = pEv->value;
783+ if (!pUSB->state[pUSB->nChannel].values[WACOMFIELD_PROXIMITY].nValue)
784+ pUSB->state[pUSB->nChannel].values[WACOMFIELD_SERIAL].nValue = 0;
785+ return 0;
786+}
787+
788+static int USBParseKEY(USBTABLET* pUSB, struct input_event* pEv)
789+{
790+ int i, button=-1, tool=0;
791+ switch (pEv->code)
792+ {
793+ case BTN_LEFT: button = WACOMBUTTON_LEFT; break;
794+ case BTN_RIGHT: button = WACOMBUTTON_RIGHT; break;
795+ case BTN_MIDDLE: button = WACOMBUTTON_MIDDLE; break;
796+ case BTN_SIDE: button = WACOMBUTTON_SIDE; break;
797+ case BTN_EXTRA: button = WACOMBUTTON_EXTRA; break;
798+ case BTN_TOUCH: button = WACOMBUTTON_TOUCH; break;
799+ case BTN_STYLUS: button = WACOMBUTTON_STYLUS; break;
800+ case BTN_STYLUS2: button = WACOMBUTTON_STYLUS2; break;
801+ case BTN_TOOL_PEN: tool = WACOMTOOLTYPE_PEN; break;
802+ case BTN_TOOL_PENCIL: tool = WACOMTOOLTYPE_PENCIL; break;
803+ case BTN_TOOL_BRUSH: tool = WACOMTOOLTYPE_BRUSH; break;
804+ case BTN_TOOL_RUBBER: tool = WACOMTOOLTYPE_ERASER; break;
805+ case BTN_TOOL_AIRBRUSH: tool = WACOMTOOLTYPE_AIRBRUSH; break;
806+ case BTN_TOOL_MOUSE: tool = WACOMTOOLTYPE_MOUSE; break;
807+ case BTN_TOOL_FINGER: tool = WACOMTOOLTYPE_PAD; break;
808+ case BTN_TOOL_LENS: tool = WACOMTOOLTYPE_LENS; break;
809+ case BTN_TOOL_DOUBLETAP: tool = WACOMTOOLTYPE_TOUCH; break;
810+ default:
811+ for (i = 0; i < gNumPadKeys; i++)
812+ if (pEv->code == gPadKeys [i])
813+ {
814+ button = WACOMBUTTON_BT0 + i;
815+ break;
816+ }
817+ }
818+
819+ /* if button was specified */
820+ if (button != -1)
821+ {
822+ /* button state change */
823+ if (pEv->value)
824+ pUSB->state[pUSB->nChannel].values[WACOMFIELD_BUTTONS].nValue |= BIT(button);
825+ else
826+ pUSB->state[pUSB->nChannel].values[WACOMFIELD_BUTTONS].nValue &= ~BIT(button);
827+ }
828+
829+ /* if a tool was specified */
830+ if (tool)
831+ {
832+ /* coming into proximity */
833+ if (pEv->value)
834+ {
835+ /* no prior tool in proximity */
836+ if (!(pUSB->nToolProx[pUSB->nChannel] & BIT(tool)))
837+ {
838+ pUSB->state[pUSB->nChannel].values[WACOMFIELD_PROXIMITY].nValue = 1;
839+ }
840+
841+ /* remember tool in prox */
842+ pUSB->nToolProx[pUSB->nChannel] |= BIT(tool);
843+ pUSB->state[pUSB->nChannel].values[WACOMFIELD_TOOLTYPE].nValue = tool;
844+ }
845+
846+ /* otherwise, going out of proximity */
847+ else
848+ {
849+ /* forget tool in prox */
850+ if (pUSB->nToolProx[pUSB->nChannel] & BIT(tool))
851+ {
852+ pUSB->nToolProx[pUSB->nChannel] &= ~BIT(tool);
853+ }
854+
855+ /* single input */
856+ if (!(pUSB->state[pUSB->nChannel].uValid & BIT(WACOMFIELD_SERIAL)))
857+ pUSB->nToolProx[pUSB->nChannel] = 0;
858+
859+ pUSB->state[pUSB->nChannel].values[WACOMFIELD_PROXIMITY].nValue = 0;
860+ pUSB->state[pUSB->nChannel].values[WACOMFIELD_TOOLTYPE].nValue = 0;
861+
862+ /* nobody left? out of proximity */
863+ if (!pUSB->nToolProx[pUSB->nChannel])
864+ memset(pUSB->state[pUSB->nChannel].values, 0,
865+ pUSB->state[pUSB->nChannel].uValueCnt * sizeof(WACOMVALUE));
866+ /* otherwise, switch to next tool */
867+ else
868+ {
869+ for (i=WACOMTOOLTYPE_PEN; i<WACOMTOOLTYPE_MAX; ++i)
870+ {
871+ if (pUSB->nToolProx[pUSB->nChannel] & BIT(i))
872+ {
873+ pUSB->state[pUSB->nChannel].values[WACOMFIELD_TOOLTYPE].nValue = i;
874+ }
875+ }
876+ }
877+ } /* out of prox */
878+ } /* end if tool */
879+
880+ return 0;
881+}
882+
883+static int USBParseABS(USBTABLET* pUSB, struct input_event* pEv)
884+{
885+ int field = 0;
886+ switch (pEv->code)
887+ {
888+ case ABS_X: field = WACOMFIELD_POSITION_X; break;
889+ case ABS_Y: field = WACOMFIELD_POSITION_Y; break;
890+ case ABS_RZ: field = WACOMFIELD_ROTATION_Z; break;
891+ case ABS_DISTANCE: field = WACOMFIELD_DISTANCE; break;
892+ case ABS_PRESSURE: field = WACOMFIELD_PRESSURE; break;
893+ case ABS_TILT_X: field = WACOMFIELD_TILT_X; break;
894+ case ABS_TILT_Y: field = WACOMFIELD_TILT_Y; break;
895+ case ABS_WHEEL: field = WACOMFIELD_ABSWHEEL; break;
896+ case ABS_THROTTLE: field = WACOMFIELD_THROTTLE; break;
897+ }
898+
899+ if (field)
900+ pUSB->state[pUSB->nChannel].values[field].nValue = pEv->value;
901+
902+ return 0;
903+}
904+
905+static int USBParseREL(USBTABLET* pUSB, struct input_event* pEv)
906+{
907+ int field = 0;
908+ switch (pEv->code)
909+ {
910+ case REL_WHEEL: field = WACOMFIELD_RELWHEEL; break;
911+ }
912+
913+ if (field)
914+ pUSB->state[pUSB->nChannel].values[field].nValue = pEv->value;
915+
916+ return 0;
917+}
918+
919+static int USBParseData(WACOMTABLET_PRIV* pTablet,
920+ const unsigned char* puchData, unsigned int uLength,
921+ WACOMSTATE* pState)
922+{
923+ int evcnt, chcnt;
924+ USBTABLET* pUSB = (USBTABLET*)pTablet;
925+ struct input_event* pEv = (struct input_event*)puchData;
926+ if (uLength != sizeof(struct input_event)) return 1;
927+
928+ /* store event until we receive a MSC_SERIAL/SYN_REPORT
929+ * so we can figure out with channel to use */
930+ if (pUSB->nEventCnt >= MAX_USB_EVENTS)
931+ {
932+ /* no more buffer space */
933+ pUSB->nEventCnt = 0;
934+ pUSB->nLastToolSerial = 0;
935+ errno = ENOBUFS;
936+ return -1;
937+ }
938+ pUSB->events[pUSB->nEventCnt++] = *pEv;
939+
940+ if ((pEv->type == EV_MSC) && (pEv->code == MSC_SERIAL))
941+ {
942+ /* store the serial for the tool for later use */
943+ pUSB->nLastToolSerial = pEv->value;
944+#ifdef EV_SYN
945+ /* Kernel 2.4 uses MSC_SERIAL as an end-of-report, 2.6
946+ * don't so we're not done yet */
947+ return 0;
948+ }
949+ else if ((pEv->type == EV_SYN) && (pEv->code == SYN_REPORT))
950+ {
951+ /* Kernel 2.6 used SYN_REPORT as an end-of-record,
952+ * fall through */
953+#endif
954+ }
955+ else
956+ {
957+ /* Not a MSC_SERIAL or SYN_REPORT, we're not done yet */
958+ return 0;
959+ }
960+
961+ /* Select channel here based on the serial number, tablets
962+ * with only one channel will always use channel 0, so no
963+ * check.
964+ */
965+ if (pUSB->pDevice->uChannelCnt > 1)
966+ {
967+ pUSB->nChannel = -1;
968+ /* Find existing channel */
969+ for (chcnt=0; chcnt<pUSB->pDevice->uChannelCnt; chcnt++)
970+ {
971+ if (pUSB->state[chcnt].values[WACOMFIELD_SERIAL].nValue == pUSB->nLastToolSerial)
972+ {
973+ pUSB->nChannel = chcnt;
974+ break;
975+ }
976+ }
977+
978+ /* Find en empty channel */
979+ if(pUSB->nChannel == -1)
980+ {
981+ for (chcnt=0; chcnt<pUSB->pDevice->uChannelCnt; chcnt++)
982+ {
983+ if(!pUSB->nToolProx[chcnt])
984+ {
985+ pUSB->nChannel = chcnt;
986+ break;
987+ }
988+ }
989+ }
990+
991+ /* no more channels?? */
992+ if(pUSB->nChannel == -1)
993+ {
994+ pUSB->nChannel = 0;
995+ pUSB->nEventCnt = 0;
996+ pUSB->nLastToolSerial = 0;
997+ errno = ENOBUFS;
998+ return -1;
999+ }
1000+ }
1001+
1002+ /* Channel found/allocated, lets process events */
1003+ pUSB->state[pUSB->nChannel].values[WACOMFIELD_RELWHEEL].nValue = 0;
1004+
1005+ for (evcnt=0; evcnt<pUSB->nEventCnt; evcnt++)
1006+ {
1007+ pEv = pUSB->events + evcnt;
1008+ /* dispatch event */
1009+ switch (pEv->type)
1010+ {
1011+#ifdef EV_SYN
1012+ case EV_SYN: /* kernel 2.6 */
1013+#endif
1014+ case EV_MSC: /* kernel 2.4 */
1015+ if (USBParseMSC(pUSB,pEv)) return pEv->type; break;
1016+ case EV_KEY: if (USBParseKEY(pUSB,pEv)) return pEv->type; break;
1017+ case EV_ABS: if (USBParseABS(pUSB,pEv)) return pEv->type; break;
1018+ case EV_REL: if (USBParseREL(pUSB,pEv)) return pEv->type; break;
1019+ default: errno = EINVAL; return pEv->type;
1020+ }
1021+ }
1022+
1023+ pUSB->nEventCnt = 0;
1024+ pUSB->nLastToolSerial = 0;
1025+ return pState ? WacomCopyState(pState,pUSB->state + pUSB->nChannel) : 0;
1026+}
1027+
1028+/*****************************************************************************
1029+** End USB Linux Input Subsystem
1030+*****************************************************************************/
1031+
1032+#else /* WCM_ENABLE_LINUXWACOM */
1033+
1034+WACOMTABLET WacomOpenUSBTablet(WACOMENGINE hEngine, int fd, WACOMMODEL* pModel)
1035+{
1036+ errno = EPERM;
1037+ return NULL;
1038+}
1039+
1040+unsigned int WacomGetUSBDeviceFromName(const char* pszName)
1041+{
1042+ errno = ENOENT;
1043+ return 0;
1044+}
1045+
1046+int WacomGetSupportedUSBDeviceList(WACOMDEVICEREC** ppList, int* pnSize)
1047+{
1048+ if (!ppList || !pnSize) { errno = EINVAL; return 1; }
1049+ *ppList = NULL;
1050+ *pnSize = 0;
1051+ return 0;
1052+}
1053+
1054+#endif /* WCM_ENABLE_LINUXWACOM */
--- /dev/null
+++ b/wacom/wacusb.h
@@ -0,0 +1,31 @@
1+/*****************************************************************************
2+** wacusb.h
3+**
4+** Copyright (C) 2002, 2003 - John E. Joganic
5+**
6+** This program is free software; you can redistribute it and/or
7+** modify it under the terms of the GNU General Public License
8+** as published by the Free Software Foundation; either version 2
9+** of the License, or (at your option) any later version.
10+**
11+** This program is distributed in the hope that it will be useful,
12+** but WITHOUT ANY WARRANTY; without even the implied warranty of
13+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+** GNU General Public License for more details.
15+**
16+** You should have received a copy of the GNU General Public License
17+** along with this program; if not, write to the Free Software
18+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19+**
20+****************************************************************************/
21+
22+#ifndef __LINUXWACOM_WACUSB_H
23+#define __LINUXWACOM_WACUSB_H
24+
25+#include "wactablet.h"
26+
27+int WacomGetSupportedUSBDeviceList(WACOMDEVICEREC** ppList, int* pnSize);
28+unsigned int WacomGetUSBDeviceFromName(const char* pszName);
29+WACOMTABLET WacomOpenUSBTablet(WACOMENGINE hEngine, int fd, WACOMMODEL* pModel);
30+
31+#endif /* __LINUXWACOM_WACUSB_H */