From: Chih-Wei Huang Date: Wed, 5 Nov 2014 03:58:43 +0000 (+0800) Subject: Initial lollipop-x86 porting X-Git-Tag: android-x86-6.0-r1~63 X-Git-Url: http://git.osdn.net/view?p=android-x86%2Fdevice-generic-common.git;a=commitdiff_plain;h=1ed87a76add40dcde3d63ce80efb5da568f2a51f Initial lollipop-x86 porting --- diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..97d582d --- /dev/null +++ b/Android.mk @@ -0,0 +1,11 @@ +# +# Copyright (C) 2014 The Android-x86 Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +include $(call all-subdir-makefiles) diff --git a/BoardConfig.mk b/BoardConfig.mk new file mode 100644 index 0000000..55d4d12 --- /dev/null +++ b/BoardConfig.mk @@ -0,0 +1,74 @@ +# +# BoardConfig.mk for x86 platform +# + +TARGET_BOARD_PLATFORM := android-x86 + +# Some framework code requires this to enable BT +BOARD_HAVE_BLUETOOTH := true +BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := device/generic/common/bluetooth +BLUETOOTH_HCI_USE_USB := true +BOARD_HAVE_BLUETOOTH_BCM := true + +BOARD_USE_LEGACY_UI := true + +BOARD_SYSTEMIMAGE_PARTITION_SIZE = $(if $(MKSQUASHFS),0,1073741824) + +# customize the malloced address to be 16-byte aligned +BOARD_MALLOC_ALIGNMENT := 16 + +# Enable dex-preoptimization to speed up the first boot sequence +# of an SDK AVD. Note that this operation only works on Linux for now +ifeq ($(HOST_OS),linux) +WITH_DEXPREOPT := true +TARGET_GCC_VERSION_EXP := 4.9 +endif + +# the following variables could be overridden +TARGET_PRELINK_MODULE := false +TARGET_NO_KERNEL ?= false +TARGET_NO_RECOVERY ?= true +TARGET_EXTRA_KERNEL_MODULES := 8723au tp_smapi + +BOARD_USES_GENERIC_AUDIO ?= false +BOARD_USES_ALSA_AUDIO ?= true +BUILD_WITH_ALSA_UTILS ?= true +BOARD_HAS_GPS_HARDWARE ?= true + +# Don't build emulator +BUILD_EMULATOR ?= false +BUILD_STANDALONE_EMULATOR ?= false +BUILD_EMULATOR_QEMUD ?= false +BUILD_EMULATOR_OPENGL ?= false +BUILD_EMULATOR_OPENGL_DRIVER ?= false +BUILD_EMULATOR_QEMU_PROPS ?= false +BUILD_EMULATOR_CAMERA_HAL ?= false +BUILD_EMULATOR_GPS_MODULE ?= false +BUILD_EMULATOR_LIGHTS_MODULE ?= false +BUILD_EMULATOR_SENSORS_MODULE ?= false + +BOARD_USE_LIBVA_INTEL_DRIVER := true +BOARD_USE_LIBVA := true +BOARD_USE_LIBMIX := true +BOARD_USES_WRS_OMXIL_CORE := true +USE_INTEL_OMX_COMPONENTS := true + +USE_OPENGL_RENDERER := true +NUM_FRAMEBUFFER_SURFACE_BUFFERS ?= 3 + +USE_CAMERA_STUB ?= false + +# This enables the wpa wireless driver +BOARD_WPA_SUPPLICANT_DRIVER ?= NL80211 +BOARD_WPA_SUPPLICANT_PRIVATE_LIB ?= private_lib_driver_cmd +WPA_SUPPLICANT_VERSION ?= VER_2_1_DEVEL +WIFI_DRIVER_MODULE_PATH ?= auto + +#BOARD_GPU_DRIVERS ?= i915 i965 ilo r300g r600g nouveau vmwgfx +BOARD_GPU_DRIVERS ?= i915 i965 swrast r300g r600g +ifneq ($(strip $(BOARD_GPU_DRIVERS)),) +TARGET_HARDWARE_3D := true +BOARD_EGL_CFG ?= device/generic/common/gpu/egl_mesa.cfg +endif + +BOARD_KERNEL_CMDLINE := root=/dev/ram0 androidboot.hardware=$(TARGET_PRODUCT) video=-16 diff --git a/alsa/bytrt5640.state b/alsa/bytrt5640.state new file mode 100644 index 0000000..46a8cc5 --- /dev/null +++ b/alsa/bytrt5640.state @@ -0,0 +1,1361 @@ +state.bytrt5640 { + control.1 { + iface MIXER + name 'Mono Playback Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.2 { + iface MIXER + name 'Mono DAC Playback Volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 175' + dbmin -65625 + dbmax 0 + dbvalue.0 -65625 + dbvalue.1 -65625 + } + } + control.3 { + iface MIXER + name 'Speaker Channel Switch' + value.0 true + value.1 true + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.4 { + iface MIXER + name 'Speaker Playback Volume' + value.0 39 + value.1 39 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 39' + dbmin -4650 + dbmax 1200 + dbvalue.0 1200 + dbvalue.1 1200 + } + } + control.5 { + iface MIXER + name 'HP Channel Switch' + value.0 true + value.1 true + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.6 { + iface MIXER + name 'HP Playback Volume' + value.0 35 + value.1 35 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 39' + dbmin -4650 + dbmax 1200 + dbvalue.0 600 + dbvalue.1 600 + } + } + control.7 { + iface MIXER + name 'OUT Playback Switch' + value.0 true + value.1 true + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.8 { + iface MIXER + name 'OUT Channel Switch' + value.0 false + value.1 false + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.9 { + iface MIXER + name 'OUT Playback Volume' + value.0 0 + value.1 0 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 39' + dbmin -4650 + dbmax 1200 + dbvalue.0 -4650 + dbvalue.1 -4650 + } + } + control.10 { + iface MIXER + name 'DAC2 Playback Switch' + value.0 false + value.1 false + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.11 { + iface MIXER + name 'DAC1 Playback Volume' + value.0 173 + value.1 173 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 175' + dbmin -65625 + dbmax 0 + dbvalue.0 -750 + dbvalue.1 -750 + } + } + control.12 { + iface MIXER + name 'IN1 Boost' + value 8 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 8' + dbmin 0 + dbmax 5200 + dbvalue.0 5200 + } + } + control.13 { + iface MIXER + name 'IN2 Boost' + value 8 + comment { + access 'read write' + type INTEGER + count 1 + range '0 - 8' + dbmin 0 + dbmax 5200 + dbvalue.0 5200 + } + } + control.14 { + iface MIXER + name 'IN Capture Volume' + value.0 31 + value.1 31 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 31' + dbmin -3450 + dbmax 1200 + dbvalue.0 1200 + dbvalue.1 1200 + } + } + control.15 { + iface MIXER + name 'ADC Capture Switch' + value.0 true + value.1 true + comment { + access 'read write' + type BOOLEAN + count 2 + } + } + control.16 { + iface MIXER + name 'ADC Capture Volume' + value.0 127 + value.1 127 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 127' + dbmin -17625 + dbmax 30000 + dbvalue.0 30000 + dbvalue.1 30000 + } + } + control.17 { + iface MIXER + name 'Mono ADC Capture Volume' + value.0 127 + value.1 127 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 127' + dbmin -17625 + dbmax 30000 + dbvalue.0 30000 + dbvalue.1 30000 + } + } + control.18 { + iface MIXER + name 'ADC Boost Gain' + value.0 3 + value.1 3 + comment { + access 'read write' + type INTEGER + count 2 + range '0 - 3' + dbmin 0 + dbmax 3600 + dbvalue.0 3600 + dbvalue.1 3600 + } + } + control.19 { + iface MIXER + name 'Class D SPK Ratio Control' + value '2.77x' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1.66x' + item.1 '1.83x' + item.2 '1.94x' + item.3 '2x' + item.4 '2.11x' + item.5 '2.22x' + item.6 '2.33x' + item.7 '2.44x' + item.8 '2.55x' + item.9 '2.66x' + item.10 '2.77x' + } + } + control.20 { + iface MIXER + name 'ADC IF1 Data Switch' + value Normal + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Normal + item.1 'left copy to right' + item.2 'right copy to left' + item.3 Swap + } + } + control.21 { + iface MIXER + name 'DAC IF1 Data Switch' + value Normal + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Normal + item.1 'left copy to right' + item.2 'right copy to left' + item.3 Swap + } + } + control.22 { + iface MIXER + name 'ADC IF2 Data Switch' + value Normal + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Normal + item.1 'left copy to right' + item.2 'right copy to left' + item.3 Swap + } + } + control.23 { + iface MIXER + name 'DAC IF2 Data Switch' + value Normal + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 Normal + item.1 'left copy to right' + item.2 'right copy to left' + item.3 Swap + } + } + control.24 { + iface MIXER + name 'Headphone Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.25 { + iface MIXER + name 'Headset Mic Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.26 { + iface MIXER + name 'Internal Mic Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.27 { + iface MIXER + name 'Speaker Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.28 { + iface MIXER + name 'Mono MIX DAC R2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.29 { + iface MIXER + name 'Mono MIX DAC L2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.30 { + iface MIXER + name 'Mono MIX OUTVOL R Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.31 { + iface MIXER + name 'Mono MIX OUTVOL L Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.32 { + iface MIXER + name 'Mono MIX BST1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.33 { + iface MIXER + name 'HPO MIX DAC2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.34 { + iface MIXER + name 'HPO MIX DAC1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.35 { + iface MIXER + name 'HPO MIX HPVOL Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.36 { + iface MIXER + name 'OUT MIXR SPK MIXR Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.37 { + iface MIXER + name 'OUT MIXR BST2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.38 { + iface MIXER + name 'OUT MIXR BST1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.39 { + iface MIXER + name 'OUT MIXR INR Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.40 { + iface MIXER + name 'OUT MIXR REC MIXR Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.41 { + iface MIXER + name 'OUT MIXR DAC L2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.42 { + iface MIXER + name 'OUT MIXR DAC R2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.43 { + iface MIXER + name 'OUT MIXR DAC R1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.44 { + iface MIXER + name 'OUT MIXL SPK MIXL Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.45 { + iface MIXER + name 'OUT MIXL BST1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.46 { + iface MIXER + name 'OUT MIXL INL Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.47 { + iface MIXER + name 'OUT MIXL REC MIXL Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.48 { + iface MIXER + name 'OUT MIXL DAC R2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.49 { + iface MIXER + name 'OUT MIXL DAC L2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.50 { + iface MIXER + name 'OUT MIXL DAC L1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.51 { + iface MIXER + name 'Stereo DAC MIXR DAC R1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.52 { + iface MIXER + name 'Stereo DAC MIXR DAC R2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.53 { + iface MIXER + name 'Stereo DAC MIXR ANC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.54 { + iface MIXER + name 'Stereo DAC MIXL DAC L1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.55 { + iface MIXER + name 'Stereo DAC MIXL DAC L2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.56 { + iface MIXER + name 'Stereo DAC MIXL ANC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.57 { + iface MIXER + name 'DAC R2 Mux' + value IF2 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 IF2 + } + } + control.58 { + iface MIXER + name 'DAC L2 Mux' + value IF2 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 IF2 + item.1 'Base L/R' + } + } + control.59 { + iface MIXER + name 'HP R Playback Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.60 { + iface MIXER + name 'HP L Playback Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.61 { + iface MIXER + name 'Speaker R Playback Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.62 { + iface MIXER + name 'Speaker L Playback Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.63 { + iface MIXER + name 'LOUT MIX DAC L1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.64 { + iface MIXER + name 'LOUT MIX DAC R1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.65 { + iface MIXER + name 'LOUT MIX OUTVOL L Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.66 { + iface MIXER + name 'LOUT MIX OUTVOL R Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.67 { + iface MIXER + name 'SPOR MIX DAC R1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.68 { + iface MIXER + name 'SPOR MIX SPKVOL R Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.69 { + iface MIXER + name 'SPOR MIX BST1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.70 { + iface MIXER + name 'SPOL MIX DAC R1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.71 { + iface MIXER + name 'SPOL MIX DAC L1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.72 { + iface MIXER + name 'SPOL MIX SPKVOL R Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.73 { + iface MIXER + name 'SPOL MIX SPKVOL L Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.74 { + iface MIXER + name 'SPOL MIX BST1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.75 { + iface MIXER + name 'SPK MIXR REC MIXR Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.76 { + iface MIXER + name 'SPK MIXR INR Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.77 { + iface MIXER + name 'SPK MIXR DAC R1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.78 { + iface MIXER + name 'SPK MIXR DAC R2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.79 { + iface MIXER + name 'SPK MIXR OUT MIXR Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.80 { + iface MIXER + name 'SPK MIXL REC MIXL Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.81 { + iface MIXER + name 'SPK MIXL INL Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.82 { + iface MIXER + name 'SPK MIXL DAC L1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.83 { + iface MIXER + name 'SPK MIXL DAC L2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.84 { + iface MIXER + name 'SPK MIXL OUT MIXL Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.85 { + iface MIXER + name 'DIG MIXR DAC R1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.86 { + iface MIXER + name 'DIG MIXR DAC R2 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.87 { + iface MIXER + name 'DIG MIXL DAC L1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.88 { + iface MIXER + name 'DIG MIXL DAC L2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.89 { + iface MIXER + name 'Mono DAC MIXR DAC R1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.90 { + iface MIXER + name 'Mono DAC MIXR DAC R2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.91 { + iface MIXER + name 'Mono DAC MIXR DAC L2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.92 { + iface MIXER + name 'Mono DAC MIXL DAC L1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.93 { + iface MIXER + name 'Mono DAC MIXL DAC L2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.94 { + iface MIXER + name 'Mono DAC MIXL DAC R2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.95 { + iface MIXER + name 'DAC MIXR Stereo ADC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.96 { + iface MIXER + name 'DAC MIXR INF1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.97 { + iface MIXER + name 'DAC MIXL Stereo ADC Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.98 { + iface MIXER + name 'DAC MIXL INF1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.99 { + iface MIXER + name 'SDI select' + value IF1 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 IF1 + item.1 IF2 + } + } + control.100 { + iface MIXER + name 'DAI select' + value '1:1|2:2' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 '1:1|2:2' + item.1 '1:2|2:1' + item.2 '1:1|2:1' + item.3 '1:2|2:2' + } + } + control.101 { + iface MIXER + name 'Mono ADC MIXR ADC1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.102 { + iface MIXER + name 'Mono ADC MIXR ADC2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.103 { + iface MIXER + name 'Mono ADC MIXL ADC1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.104 { + iface MIXER + name 'Mono ADC MIXL ADC2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.105 { + iface MIXER + name 'Stereo ADC MIXR ADC1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.106 { + iface MIXER + name 'Stereo ADC MIXR ADC2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.107 { + iface MIXER + name 'Stereo ADC MIXL ADC1 Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.108 { + iface MIXER + name 'Stereo ADC MIXL ADC2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.109 { + iface MIXER + name 'Mono ADC R2 Mux' + value 'DMIC R1' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'DMIC R1' + item.1 'DMIC R2' + item.2 'Mono DAC MIXR' + } + } + control.110 { + iface MIXER + name 'Mono ADC R1 Mux' + value ADCR + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'Mono DAC MIXR' + item.1 ADCR + } + } + control.111 { + iface MIXER + name 'Mono ADC L1 Mux' + value ADCL + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'Mono DAC MIXL' + item.1 ADCL + } + } + control.112 { + iface MIXER + name 'Mono ADC L2 Mux' + value 'DMIC L1' + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'DMIC L1' + item.1 'DMIC L2' + item.2 'Mono DAC MIXL' + } + } + control.113 { + iface MIXER + name 'Stereo ADC1 Mux' + value ADC + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 'DIG MIX' + item.1 ADC + } + } + control.114 { + iface MIXER + name 'Stereo ADC2 Mux' + value DMIC1 + comment { + access 'read write' + type ENUMERATED + count 1 + item.0 DMIC1 + item.1 DMIC2 + item.2 'DIG MIX' + } + } + control.115 { + iface MIXER + name 'RECMIXR HPOR Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.116 { + iface MIXER + name 'RECMIXR INR Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.117 { + iface MIXER + name 'RECMIXR BST2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.118 { + iface MIXER + name 'RECMIXR BST1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.119 { + iface MIXER + name 'RECMIXR OUT MIXR Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.120 { + iface MIXER + name 'RECMIXL HPOL Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.121 { + iface MIXER + name 'RECMIXL INL Switch' + value true + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.122 { + iface MIXER + name 'RECMIXL BST2 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.123 { + iface MIXER + name 'RECMIXL BST1 Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } + control.124 { + iface MIXER + name 'RECMIXL OUT MIXL Switch' + value false + comment { + access 'read write' + type BOOLEAN + count 1 + } + } +} diff --git a/bluetooth/bdroid_buildcfg.h b/bluetooth/bdroid_buildcfg.h index 021f192..df91a8a 100644 --- a/bluetooth/bdroid_buildcfg.h +++ b/bluetooth/bdroid_buildcfg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 The Android-x86 Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,10 @@ #ifndef _BDROID_BUILDCFG_H #define _BDROID_BUILDCFG_H -#define BTM_DEF_LOCAL_NAME "Android Bluedroid" +#define BTM_DEF_LOCAL_NAME "Android-x86" +// At present either USB or UART is supported +#define BLUETOOTH_HCI_USE_USB TRUE +// Bluetooth Low Power Mode is supported on BT4.0 +#define HCILP_INCLUDED FALSE #endif diff --git a/device.mk b/device.mk new file mode 100644 index 0000000..605afaf --- /dev/null +++ b/device.mk @@ -0,0 +1,105 @@ +# +# Copyright (C) 2014 The Android-x86 Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +PRODUCT_DIR := $(dir $(lastword $(filter-out device/common/%,$(filter device/%,$(ALL_PRODUCTS))))) + +PRODUCT_PROPERTY_OVERRIDES := \ + ro.ril.hsxpa=1 \ + ro.ril.gprsclass=10 \ + keyguard.no_require_sim=true \ + ro.com.android.dataroaming=true + +PRODUCT_DEFAULT_PROPERTY_OVERRIDES := \ + ro.arch=x86 \ + ro.rtc_local_time=1 \ + +PRODUCT_COPY_FILES := \ + $(if $(wildcard $(PRODUCT_DIR)init.rc),$(PRODUCT_DIR)init.rc:root/init.rc) \ + $(if $(wildcard $(PRODUCT_DIR)init.sh),$(PRODUCT_DIR),$(LOCAL_PATH)/)init.sh:system/etc/init.sh \ + $(if $(wildcard $(PRODUCT_DIR)modules.blacklist),$(PRODUCT_DIR),$(LOCAL_PATH)/)modules.blacklist:system/etc/modules.blacklist \ + $(if $(wildcard $(PRODUCT_DIR)wpa_supplicant.conf),$(PRODUCT_DIR),$(LOCAL_PATH)/)wpa_supplicant.conf:system/etc/wifi/wpa_supplicant.conf \ + $(if $(wildcard $(PRODUCT_DIR)excluded-input-devices.xml),$(PRODUCT_DIR),$(LOCAL_PATH)/)excluded-input-devices.xml:system/etc/excluded-input-devices.xml \ + $(if $(wildcard $(PRODUCT_DIR)init.$(TARGET_PRODUCT).rc),$(PRODUCT_DIR)init.$(TARGET_PRODUCT).rc,$(LOCAL_PATH)/init.x86.rc):root/init.$(TARGET_PRODUCT).rc \ + $(if $(wildcard $(PRODUCT_DIR)ueventd.$(TARGET_PRODUCT).rc),$(PRODUCT_DIR)ueventd.$(TARGET_PRODUCT).rc,$(LOCAL_PATH)/ueventd.x86.rc):root/ueventd.$(TARGET_PRODUCT).rc \ + +PRODUCT_COPY_FILES += \ + $(LOCAL_PATH)/ppp/ip-up:system/etc/ppp/ip-up \ + $(LOCAL_PATH)/ppp/ip-down:system/etc/ppp/ip-down \ + $(LOCAL_PATH)/ppp/peers/gprs:system/etc/ppp/peers/gprs \ + $(LOCAL_PATH)/media_codecs.xml:system/etc/media_codecs.xml \ + $(LOCAL_PATH)/media_profiles.xml:system/etc/media_profiles.xml \ + device/sample/etc/apns-full-conf.xml:system/etc/apns-conf.xml \ + frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:system/etc/media_codecs_google_audio.xml \ + frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:system/etc/media_codecs_google_video.xml \ + frameworks/native/data/etc/tablet_core_hardware.xml:system/etc/permissions/tablet_core_hardware.xml \ + frameworks/native/data/etc/android.hardware.audio.low_latency.xml:system/etc/permissions/android.hardware.audio.low_latency.xml \ + frameworks/native/data/etc/android.hardware.bluetooth_le.xml:system/etc/permissions/android.hardware.bluetooth_le.xml \ + frameworks/native/data/etc/android.hardware.camera.flash-autofocus.xml:system/etc/permissions/android.hardware.camera.flash-autofocus.xml \ + frameworks/native/data/etc/android.hardware.camera.front.xml:system/etc/permissions/android.hardware.camera.front.xml \ + frameworks/native/data/etc/android.hardware.camera.xml:system/etc/permissions/android.hardware.camera.xml \ + frameworks/native/data/etc/android.hardware.ethernet.xml:system/etc/permissions/android.hardware.ethernet.xml \ + frameworks/native/data/etc/android.hardware.location.xml:system/etc/permissions/android.hardware.location.xml \ + frameworks/native/data/etc/android.hardware.location.gps.xml:system/etc/permissions/android.hardware.location.gps.xml \ + frameworks/native/data/etc/android.hardware.sensor.proximity.xml:system/etc/permissions/android.hardware.sensor.proximity.xml \ + frameworks/native/data/etc/android.hardware.sensor.light.xml:system/etc/permissions/android.hardware.sensor.light.xml \ + frameworks/native/data/etc/android.hardware.sensor.gyroscope.xml:system/etc/permissions/android.hardware.sensor.gyroscope.xml \ + frameworks/native/data/etc/android.hardware.sensor.barometer.xml:system/etc/permissions/android.hardware.sensor.barometer.xml \ + frameworks/native/data/etc/android.hardware.sensor.accelerometer.xml:system/etc/permissions/android.hardware.sensor.accelerometer.xml \ + frameworks/native/data/etc/android.hardware.touchscreen.multitouch.distinct.xml:system/etc/permissions/android.hardware.touchscreen.multitouch.distinct.xml \ + frameworks/native/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml:system/etc/permissions/android.hardware.touchscreen.multitouch.jazzhand.xml \ + frameworks/native/data/etc/android.hardware.usb.accessory.xml:system/etc/permissions/android.hardware.usb.accessory.xml \ + frameworks/native/data/etc/android.hardware.usb.host.xml:system/etc/permissions/android.hardware.usb.host.xml \ + frameworks/native/data/etc/android.hardware.wifi.xml:system/etc/permissions/android.hardware.wifi.xml \ + frameworks/native/data/etc/android.software.sip.voip.xml:system/etc/permissions/android.software.sip.voip.xml \ + frameworks/native/data/etc/android.software.sip.xml:system/etc/permissions/android.software.sip.xml \ + $(foreach f,$(wildcard $(LOCAL_PATH)/alsa/*),$(f):$(subst $(LOCAL_PATH),system/etc,$(f))) \ + $(foreach f,$(wildcard $(LOCAL_PATH)/idc/*.idc $(LOCAL_PATH)/keylayout/*.kl),$(f):$(subst $(LOCAL_PATH),system/usr,$(f))) + +PRODUCT_TAGS += dalvik.gc.type-precise + +PRODUCT_CHARACTERISTICS := tablet + +PRODUCT_AAPT_CONFIG := normal large xlarge mdpi hdpi +PRODUCT_AAPT_PREF_CONFIG := mdpi + +DEVICE_PACKAGE_OVERLAYS := $(LOCAL_PATH)/overlay + +# Get the firmwares +$(call inherit-product,$(LOCAL_PATH)/firmware.mk) + +# Get the touchscreen calibration tool +$(call inherit-product-if-exists,external/tslib/tslib.mk) + +# Get the alsa files +$(call inherit-product-if-exists,hardware/libaudio/alsa.mk) + +# Get GPS configuration +$(call inherit-product-if-exists,device/common/gps/gps_as.mk) + +# Get the hardware acceleration libraries +$(call inherit-product-if-exists,$(LOCAL_PATH)/gpu/gpu_mesa.mk) + +# Get the sensors hals +$(call inherit-product-if-exists,hardware/libsensors/sensors.mk) + +# Get tablet dalvik parameters +$(call inherit-product,frameworks/native/build/tablet-10in-xhdpi-2048-dalvik-heap.mk) + +# Get GMS +$(call inherit-product-if-exists,vendor/google/products/gms.mk) + +# Get Arm translator +$(call inherit-product-if-exists,vendor/intel/houdini/houdini.mk) diff --git a/excluded-input-devices.xml b/excluded-input-devices.xml new file mode 100644 index 0000000..c0d2e61 --- /dev/null +++ b/excluded-input-devices.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/firmware.mk b/firmware.mk new file mode 100644 index 0000000..37b38eb --- /dev/null +++ b/firmware.mk @@ -0,0 +1,15 @@ +# +# Copyright (C) 2012 The Android-x86 Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +LOCAL_FIRMWARES_DIR ?= /lib/firmware +LOCAL_FIRMWARES ?= $(filter-out %.mk,$(subst $(LOCAL_FIRMWARES_DIR)/,,$(shell find $(LOCAL_FIRMWARES_DIR) -type f))) + +PRODUCT_COPY_FILES := \ + $(foreach f,$(LOCAL_FIRMWARES),$(LOCAL_FIRMWARES_DIR)/$(f):system/lib/firmware/$(f)) diff --git a/gpu/egl_mesa.cfg b/gpu/egl_mesa.cfg new file mode 100644 index 0000000..4e9cb17 --- /dev/null +++ b/gpu/egl_mesa.cfg @@ -0,0 +1,2 @@ +0 0 android +0 1 mesa diff --git a/gpu/gpu_mesa.mk b/gpu/gpu_mesa.mk new file mode 100644 index 0000000..6d27c71 --- /dev/null +++ b/gpu/gpu_mesa.mk @@ -0,0 +1,16 @@ +# +# Copyright (C) 2011 The Android-x86 Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +PRODUCT_PACKAGES := \ + gralloc.drm \ + libGLES_mesa + +PRODUCT_PROPERTY_OVERRIDES := \ + ro.opengles.version = 196608 diff --git a/idc/ETPS_2_Elantech_Touchpad.idc b/idc/ETPS_2_Elantech_Touchpad.idc new file mode 100644 index 0000000..6b28c53 --- /dev/null +++ b/idc/ETPS_2_Elantech_Touchpad.idc @@ -0,0 +1,3 @@ +touch.deviceType = default +touch.orientationAware = 1 +touch.gestureMode = spots diff --git a/idc/IDEACOM__IDC_6680.idc b/idc/IDEACOM__IDC_6680.idc new file mode 100644 index 0000000..7b1c6ae --- /dev/null +++ b/idc/IDEACOM__IDC_6680.idc @@ -0,0 +1,34 @@ +# Copyright (C) 2012 The Android-x86 Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Basic Parameters +touch.deviceType = touchScreen +#touch.orientationAware = 1 + +# Touch Size +touch.touchSize.calibration = default + +# Tool Size +touch.toolSize.calibration = default + +# Pressure +touch.pressure.calibration = disable + +# Size +touch.size.calibration = default + +# Orientation +touch.orientation.calibration = none + +device.internal = 1 diff --git a/idc/SynPS_2_Synaptics_TouchPad.idc b/idc/SynPS_2_Synaptics_TouchPad.idc new file mode 100644 index 0000000..f6441fa --- /dev/null +++ b/idc/SynPS_2_Synaptics_TouchPad.idc @@ -0,0 +1,13 @@ +# touch.deviceType = touchScreen | touchPad | pointer | default +touch.deviceType = default + +# orientationAware +# touch.orientationAware = 0 or 1 +touch.orientationAware = 0 + +# gestureMode +# Definition: touch.gestureMode = pointer | spots | default +touch.gestureMode = pointer + +# Definition: device.internal = 0 | 1 +device.internal = 1 diff --git a/init.sh b/init.sh new file mode 100644 index 0000000..053f96f --- /dev/null +++ b/init.sh @@ -0,0 +1,396 @@ +# +# Copyright (C) 2013 The Android-x86 Open Source Project +# +# License: GNU Public License v2 or later +# + +function set_property() +{ + # this must be run before post-fs stage + echo $1=$2 >> /x86.prop +} + +function init_misc() +{ + # a hack for USB modem + lsusb | grep 1a8d:1000 && eject + + # in case no cpu governor driver autoloads + [ -d /sys/devices/system/cpu/cpu0/cpufreq ] || modprobe acpi-cpufreq + + case "$PRODUCT" in + T10*TA) + echo on > /sys/bus/platform/drivers/sdhci-acpi/INT33BB:00/power/control + ;; + *) + ;; + esac +} + +function init_hal_audio() +{ + case "$PRODUCT" in + VirtualBox*|Bochs*) + [ -d /proc/asound/card0 ] || modprobe snd-sb16 isapnp=0 irq=5 + ;; + *) + ;; + esac +} + +function init_hal_bluetooth() +{ + for r in /sys/class/rfkill/*; do + type=$(cat $r/type) + [ "$type" = "wlan" -o "$type" = "bluetooth" ] && echo 1 > $r/state + done + + # these modules are incompatible with bluedroid + rmmod ath3k + rmmod btusb + rmmod bluetooth +} + +function init_hal_camera() +{ + [ -c /dev/video0 ] || modprobe vivi +} + +function init_hal_gps() +{ + # TODO + return +} + +function set_drm_mode() +{ + case "$PRODUCT" in + ET1602*) + drm_mode=1366x768 + ;; + *) + ;; + esac + + [ -n "$drm_mode" ] && set_property debug.drm.mode.force $drm_mode +} + +function init_uvesafb() +{ + case "$PRODUCT" in + *Q550) + UVESA_MODE=${UVESA_MODE:-1280x800} + ;; + ET2002*) + UVESA_MODE=${UVESA_MODE:-1600x900} + ;; + T91*) + UVESA_MODE=${UVESA_MODE:-1024x600} + ;; + VirtualBox*|Bochs*) + UVESA_MODE=${UVESA_MODE:-1024x768} + ;; + *) + ;; + esac + + modprobe uvesafb mode_option=${UVESA_MODE:-800x600}-16 ${UVESA_OPTION:-mtrr=3 scroll=redraw} +} + +function init_hal_gralloc() +{ + case "$(cat /proc/fb | head -1)" in + 0*inteldrmfb|0*radeondrmfb) + set_property ro.hardware.gralloc drm + set_drm_mode + ;; + 0*svgadrmfb) + ;; + "") + init_uvesafb + ;& + 0*) + [ "$HWACCEL" = "1" ] || set_property debug.egl.hw 0 + ;; + esac +} + +function init_hal_hwcomposer() +{ + # TODO + return +} + +function init_hal_lights() +{ + chown 1000.1000 /sys/class/backlight/*/brightness +} + +function init_hal_power() +{ + for p in /sys/class/rtc/*; do + echo disabled > $p/device/power/wakeup + done + + # TODO + case "$PRODUCT" in + *) + ;; + esac +} + +function init_hal_sensors() +{ + case "$(cat $DMIPATH/uevent)" in + *T*00LA*) + modprobe kfifo-buf + modprobe industrialio-triggered-buffer + modprobe hid-sensor-hub + modprobe hid-sensor-iio-common + modprobe hid-sensor-trigger + modprobe hid-sensor-accel-3d + modprobe hid-sensor-gyro-3d + modprobe hid-sensor-als + modprobe hid-sensor-magn-3d + sleep 1; busybox chown -R 1000.1000 /sys/bus/iio/devices/iio:device?/ + set_property ro.hardware.sensors hsb + ;; + *Lucid-MWE*) + set_property ro.ignore_atkbd 1 + set_property ro.hardware.sensors hdaps + ;; + *ICONIA*W5*) + set_property ro.hardware.sensors w500 + ;; + *S10-3t*) + set_property ro.hardware.sensors s103t + ;; + *Inagua*) + #setkeycodes 0x62 29 + #setkeycodes 0x74 56 + set_property ro.ignore_atkbd 1 + set_property ro.hardware.sensors kbd + set_property hal.sensors.kbd.type 2 + ;; + *TEGA*|*2010:svnIntel:*) + set_property ro.ignore_atkbd 1 + set_property ro.hardware.sensors kbd + set_property hal.sensors.kbd.type 1 + io_switch 0x0 0x1 + setkeycodes 0x6d 125 + ;; + *tx2*) + setkeycodes 0xb1 138 + setkeycodes 0x8a 152 + set_property ro.hardware.sensors kbd + set_property hal.sensors.kbd.type 6 + set_property poweroff.doubleclick 0 + set_property qemu.hw.mainkeys 1 + ;; + *MS-N0E1*) + set_property ro.ignore_atkbd 1 + set_property poweroff.doubleclick 0 + ;; + *Aspire1*25*) + modprobe lis3lv02d_i2c + set_property ro.hardware.sensors hdaps + echo -n "enabled" > /sys/class/thermal/thermal_zone0/mode + ;; + *ThinkPad*Tablet*) + modprobe hdaps + set_property ro.hardware.sensors hdaps + ;; + *) + set_property ro.hardware.sensors kbd + ;; + esac +} + +function create_pointercal() +{ + if [ ! -e /data/misc/tscal/pointercal ]; then + mkdir -p /data/misc/tscal + touch /data/misc/tscal/pointercal + chown 1000.1000 /data/misc/tscal /data/misc/tscal/* + chmod 775 /data/misc/tscal + chmod 664 /data/misc/tscal/pointercal + fi +} + +function init_tscal() +{ + case "$PRODUCT" in + T91|T101|ET2002|74499FU|945GSE-ITE8712) + create_pointercal + return + ;; + *) + ;; + esac + + for usbts in $(lsusb | awk '{ print $6 }'); do + case "$usbts" in + 0596:0001|0eef:0001) + create_pointercal + return + ;; + *) + ;; + esac + done +} + +function init_ril() +{ + case "$(cat $DMIPATH/uevent)" in + *TEGA*|*2010:svnIntel:*|*Lucid-MWE*) + set_property rild.libpath /system/lib/libhuaweigeneric-ril.so + set_property rild.libargs "-d /dev/ttyUSB2 -v /dev/ttyUSB1" + ;; + *) + set_property rild.libpath /system/lib/libreference-ril.so + set_property rild.libargs "-d /dev/ttyUSB2" + ;; + esac +} + +function init_cpu_governor() +{ + governor=$(getprop cpu.governor) + + [ $governor ] && { + for cpu in $(ls -d /sys/devices/system/cpu/cpu?); do + echo $governor > $cpu/cpufreq/scaling_governor || return 1 + done + } +} + +function do_init() +{ + init_misc + init_hal_audio + init_hal_bluetooth + init_hal_camera + init_hal_gps + init_hal_gralloc + init_hal_hwcomposer + init_hal_lights + init_hal_power + init_hal_sensors + init_tscal + init_ril + chmod 640 /x86.prop + post_init +} + +function do_netconsole() +{ + modprobe netconsole netconsole="@/,@$(getprop dhcp.eth0.gateway)/" +} + +function do_bootcomplete() +{ + init_cpu_governor + + [ -z "$(getprop persist.sys.root_access)" ] && setprop persist.sys.root_access 3 + + # FIXME: autosleep works better on i965? + [ "$(getprop debug.mesa.driver)" = "i965" ] && setprop debug.autosleep 1 + + for bt in $(lsusb -v | awk ' /Class:.E0/ { print $9 } '); do + chown 1002.1002 $bt && chmod 660 $bt + done + + case "$PRODUCT" in + 1866???|1867???|1869???) # ThinkPad X41 Tablet + start tablet-mode + start wacom-input + setkeycodes 0x6d 115 + setkeycodes 0x6e 114 + setkeycodes 0x69 28 + setkeycodes 0x6b 158 + setkeycodes 0x68 172 + setkeycodes 0x6c 127 + setkeycodes 0x67 217 + ;; + 6363???|6364???|6366???) # ThinkPad X60 Tablet + ;& + 7762???|7763???|7767???) # ThinkPad X61 Tablet + start tablet-mode + start wacom-input + setkeycodes 0x6d 115 + setkeycodes 0x6e 114 + setkeycodes 0x69 28 + setkeycodes 0x6b 158 + setkeycodes 0x68 172 + setkeycodes 0x6c 127 + setkeycodes 0x67 217 + ;; + 7448???|7449???|7450???|7453???) # ThinkPad X200 Tablet + start tablet-mode + start wacom-input + setkeycodes 0xe012 158 + setkeycodes 0x66 172 + setkeycodes 0x6b 127 + ;; + *) + ;; + esac + +# [ -d /proc/asound/card0 ] || modprobe snd-dummy + for c in $(grep '\[.*\]' /proc/asound/cards | awk '{print $1}'); do + f=/system/etc/alsa/$(cat /proc/asound/card$c/id).state + if [ -e $f ]; then + alsa_ctl -f $f restore $c + else + alsa_ctl init $c + alsa_amixer -c $c set Master on + alsa_amixer -c $c set Master 100% + alsa_amixer -c $c set Headphone on + alsa_amixer -c $c set Headphone 100% + alsa_amixer -c $c set Speaker 100% + alsa_amixer -c $c set Capture 100% + alsa_amixer -c $c set Capture cap + alsa_amixer -c $c set PCM 100 unmute + alsa_amixer -c $c set 'Mic Boost' 3 + alsa_amixer -c $c set 'Internal Mic Boost' 3 + fi + done +} + +PATH=/system/bin:/system/xbin + +DMIPATH=/sys/class/dmi/id +BOARD=$(cat $DMIPATH/board_name) +PRODUCT=$(cat $DMIPATH/product_name) + +# import cmdline variables +for c in `cat /proc/cmdline`; do + case $c in + androidboot.hardware=*) + ;; + *=*) + eval $c + ;; + esac +done + +[ -n "$DEBUG" ] && set -x || exec &> /dev/null + +# import the vendor specific script +hw_sh=/vendor/etc/init.sh +[ -e $hw_sh ] && source $hw_sh + +case "$1" in + netconsole) + [ -n "$DEBUG" ] && do_netconsole + ;; + bootcomplete) + do_bootcomplete + ;; + init|"") + do_init + ;; +esac + +return 0 diff --git a/init.x86.rc b/init.x86.rc new file mode 100644 index 0000000..47022b4 --- /dev/null +++ b/init.x86.rc @@ -0,0 +1,181 @@ +import /init.superuser.rc + +on early-init + mount debugfs debugfs /sys/kernel/debug + chmod 755 /sys/kernel/debug + write /proc/sys/kernel/ctrl-alt-del 1 + + write /sys/module/xt_qtaguid/parameters/ctrl_write_limited N + + export force_s3tc_enable true +# export EGL_LOG_LEVEL info +# export EGL_DRIVERS egl_dri2 + +on init + mkdir /mnt/shell/emulated 0700 shell shell + mkdir /storage/emulated 0555 root root + mkdir /mnt/media_rw/usb0 0700 media_rw media_rw + mkdir /storage/usb0 0700 root root + mkdir /mnt/media_rw/usb1 0700 media_rw media_rw + mkdir /storage/usb1 0700 root root + mkdir /mnt/media_rw/usb2 0700 media_rw media_rw + mkdir /storage/usb2 0700 root root + mkdir /mnt/media_rw/usb3 0700 media_rw media_rw + mkdir /storage/usb3 0700 root root + mkdir /mnt/media_rw/sdcard1 0700 media_rw media_rw + mkdir /storage/sdcard1 0700 root root + + export EXTERNAL_STORAGE /storage/emulated/legacy + export EMULATED_STORAGE_SOURCE /mnt/shell/emulated + export EMULATED_STORAGE_TARGET /storage/emulated + export SECONDARY_STORAGE /storage/usb0:/storage/usb1:/storage/usb2:/storage/usb3:/storage/sdcard1 + + # Support legacy paths + symlink /storage/emulated/legacy /sdcard + symlink /storage/emulated/legacy /mnt/sdcard + symlink /storage/emulated/legacy /storage/sdcard0 + symlink /mnt/shell/emulated/0 /storage/emulated/legacy + + # Backward compatibility + symlink system/lib /lib + + # device information + setprop ro.product.manufacturer $[/sys/class/dmi/id/sys_vendor] + setprop ro.product.model $[/sys/class/dmi/id/product_name] + + exec /system/bin/logwrapper /system/bin/sh /system/etc/init.sh + +on post-fs-data + sysclktz 1 + + mkdir /data/media 0770 media_rw media_rw + + mkdir /data/misc/wifi 0770 wifi wifi + mkdir /data/misc/wifi/sockets 0770 wifi wifi + mkdir /data/misc/wifi/wpa_supplicant 0770 wifi wifi + mkdir /data/misc/dhcp 0770 dhcp dhcp + mkdir /data/system 0775 system system + + # Set indication (checked by vold) that we have finished this action + setprop vold.post_fs_data_done 1 + +on early-boot + readprops /x86.prop + +on boot + setprop ro.radio.use-ppp yes + setprop ro.radio.noril no + setprop status.battery.state Slow + setprop status.battery.level 5 + setprop status.battery.level_raw 50 + setprop status.battery.level_scale 9 + + # merge from system.prop + setprop ro.config.sync yes + setprop app.setupwizard.disable 0 + setprop ro.alarm.volume.adjustable true + setprop ro.simulated.phone false + # disable red frame boarder in eng build + setprop persist.sys.strictmode.visual 0 + setprop persist.sys.strictmode.disable 1 + # workaround for h.265 slowness + setprop sys.media.vdec.drop 0 + +on fs + setprop ro.crypto.fuse_sdcard true + # manually start class late_start since we don't use mount_all + class_start main + class_start late_start + +service wpa_supplicant /system/bin/wpa_supplicant -c/data/misc/wifi/wpa_supplicant.conf \ + -ddddd \ + -iwlan0 -Dnl80211 \ + -O/data/misc/wifi/sockets \ + -e/data/misc/wifi/entropy.bin -g@android:wpa_wlan0 + # we will start as root and wpa_supplicant will switch to user wifi + # after setting up the capabilities required for WEXT + # user wifi + # group wifi inet keystore + class main + socket wpa_wlan0 dgram 660 wifi wifi + disabled + +service dhcpcd_wlan0 /system/bin/dhcpcd -aABDKL + class main + disabled + oneshot + +service iprenew_wlan0 /system/bin/dhcpcd -n + class main + disabled + oneshot + +service dhcpcd_eth0 /system/bin/dhcpcd -ABDKL + class main + disabled + oneshot + +service iprenew_eth0 /system/bin/dhcpcd -n + class main + disabled + oneshot + +service enable_houdini /system/bin/enable_houdini + class main + oneshot + +service powerbtnd /system/bin/powerbtnd + class late_start + +service sdcard /system/bin/sdcard -u 1023 -g 1023 -l /data/media /mnt/shell/emulated + class late_start + +service fuse_sdcard1 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/sdcard1 /storage/sdcard1 + class late_start + disabled + +service fuse_usb0 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/usb0 /storage/usb0 + class late_start + disabled + +service fuse_usb1 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/usb1 /storage/usb1 + class late_start + disabled + +service fuse_usb2 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/usb2 /storage/usb2 + class late_start + disabled + +service fuse_usb3 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/usb3 /storage/usb3 + class late_start + disabled + +service logcat /system/bin/logcat -v threadtime -f /data/log.txt + class debug + +service wacom-input /system/bin/wacom-input + disabled + oneshot + +service tablet-mode /system/bin/tablet-mode + disabled + oneshot + +on property:init.svc.wpa_supplicant=stopped + stop dhcpcd + +on property:system_init.startsurfaceflinger=0 + # disable cursor blinking + write /dev/tty0 "[?17;0;0c" + start surfaceflinger + stop console + +on property:sys.boot_completed=1 + write /proc/sys/kernel/ctrl-alt-del 0 + exec /system/bin/logwrapper /system/bin/sh /system/etc/init.sh bootcomplete + +on property:net.dns1=* + exec /system/bin/logwrapper /system/bin/sh /system/etc/init.sh netconsole + +on property:debug.logcat=1 + class_start debug diff --git a/io_switch/Android.mk b/io_switch/Android.mk new file mode 100644 index 0000000..0af57fd --- /dev/null +++ b/io_switch/Android.mk @@ -0,0 +1,14 @@ +# Copyright (C) 2011 The Android-x86 Open Source Project + +LOCAL_PATH := $(my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := io_switch.c +LOCAL_CFLAGS := -Werror + +LOCAL_MODULE := io_switch +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) + +include $(BUILD_EXECUTABLE) diff --git a/io_switch/io_switch.c b/io_switch/io_switch.c new file mode 100644 index 0000000..e6d6d48 --- /dev/null +++ b/io_switch/io_switch.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 The Android-x86 Open Source Project + * + * by Chih-Wei Huang + * + * Licensed under GPLv2 or later + * + */ + +#include +#include +#include +#include +#include +#include + +#define IO_SHIFT 0x68 +#define IO_VALUE 0x62 + +void usage(int invalid) +{ + if (invalid) + fprintf(stderr, "Invalid parameter\n"); + fprintf(stderr, "Usage : io_switch 0x?? 0x??\n"); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + int fd, addr; + char val; + + if (argc < 3) + usage(0); + + if (sscanf(argv[1], "0x%x", &addr) <= 0) + usage(1); + + if (sscanf(argv[2], "0x%hhx", &val) <= 0) + usage(1); + + printf("Writing 0x%x : 0x%hhx\n", addr, val); + + fd = open("/dev/port", O_WRONLY); + if (fd < 0) { + fprintf(stderr, "Open file failed\n"); + } else { + val += IO_VALUE; + lseek(fd, addr + IO_SHIFT, SEEK_SET); + write(fd, &val, 1); + close(fd); + } + + return (fd < 0); +} diff --git a/keylayout/HP_WMI_hotkeys.kl b/keylayout/HP_WMI_hotkeys.kl new file mode 100644 index 0000000..9c41b94 --- /dev/null +++ b/keylayout/HP_WMI_hotkeys.kl @@ -0,0 +1,4 @@ +key 148 HOME WAKE +key 226 BACK WAKE_DROPPED +key 141 MENU WAKE_DROPPED +key 129 BACK WAKE_DROPPED diff --git a/media_codecs.xml b/media_codecs.xml new file mode 100644 index 0000000..ba1fbdb --- /dev/null +++ b/media_codecs.xml @@ -0,0 +1,83 @@ + + + + + + + + + diff --git a/media_profiles.xml b/media_profiles.xml new file mode 100644 index 0000000..8f4155c --- /dev/null +++ b/media_profiles.xml @@ -0,0 +1,485 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules.blacklist b/modules.blacklist new file mode 100644 index 0000000..6e9f9e2 --- /dev/null +++ b/modules.blacklist @@ -0,0 +1,3 @@ +blacklist evbug +blacklist btusb +blacklist bluetooth diff --git a/overlay/frameworks/base/core/res/res/values/config.xml b/overlay/frameworks/base/core/res/res/values/config.xml new file mode 100644 index 0000000..ec4dec5 --- /dev/null +++ b/overlay/frameworks/base/core/res/res/values/config.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + "wifi,1,1,1,-1,true" + "mobile,0,0,0,-1,true" + "mobile_mms,2,0,2,60000,true" + "mobile_supl,3,0,2,60000,true" + "mobile_hipri,5,0,3,60000,true" + "ethernet,9,9,1,-1,true" + "mobile_fota,10,0,2,60000,true" + "mobile_ims,11,0,2,60000,true" + "mobile_cbs,12,0,2,60000,true" + "wifi_p2p,13,1,0,-1,true" + + + + + + "1,1" + "0,1" + "9,1" + + + + + 0 + 2 + 3 + 4 + 5 + 9 + 10 + 11 + 12 + + + + true + + + true + + + 8 + + true + + + true + diff --git a/overlay/frameworks/base/core/res/res/xml/storage_list.xml b/overlay/frameworks/base/core/res/res/xml/storage_list.xml new file mode 100644 index 0000000..66984e8 --- /dev/null +++ b/overlay/frameworks/base/core/res/res/xml/storage_list.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + diff --git a/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults.xml b/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults.xml new file mode 100644 index 0000000..c51e028 --- /dev/null +++ b/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults.xml @@ -0,0 +1,22 @@ + + + + true + 600000 + diff --git a/overlay/packages/apps/Trebuchet/res/values-sw340dp/config.xml b/overlay/packages/apps/Trebuchet/res/values-sw340dp/config.xml new file mode 100644 index 0000000..c01c4df --- /dev/null +++ b/overlay/packages/apps/Trebuchet/res/values-sw340dp/config.xml @@ -0,0 +1,3 @@ + + false + diff --git a/packages.mk b/packages.mk new file mode 100644 index 0000000..934f235 --- /dev/null +++ b/packages.mk @@ -0,0 +1,80 @@ +# +# Copyright (C) 2014 The Android-x86 Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Common packages for Android-x86 platform. + +PRODUCT_PACKAGES := \ + BasicSmsReceiver \ + Development \ + Galaxy4 \ + GlobalTime \ + HoloSpiralWallpaper \ + LiveWallpapers \ + LiveWallpapersPicker \ + MagicSmokeWallpapers \ + NotePad \ + PhaseBeam \ + PinyinIME \ + Provision \ + RSSReader \ + VisualizationWallpapers \ + camera.x86 \ + chat \ + com.android.future.usb.accessory \ + drmserver \ + eject \ + gps.default \ + gps.huawei \ + hwcomposer.x86 \ + icu.dat \ + io_switch \ + libhuaweigeneric-ril \ + lights.default \ + make_ext4fs \ + parted \ + powerbtnd \ + scp \ + sensors.hsb \ + sftp \ + ssh \ + sshd \ + su \ + tablet-mode \ + wacom-input \ + +PRODUCT_PACKAGES += \ + libwpa_client \ + hostapd \ + wpa_supplicant \ + wpa_supplicant.conf \ + +PRODUCT_PACKAGES += \ + badblocks \ + e2fsck \ + mke2fs \ + mkntfs \ + mount.exfat \ + ntfs-3g \ + ntfsfix \ + resize2fs \ + tune2fs \ + +# Third party apps +PRODUCT_PACKAGES += \ + CMFileManager \ + Superuser \ + TSCalibration2 \ + Trebuchet \ diff --git a/ppp/ip-down b/ppp/ip-down new file mode 100644 index 0000000..e4a8f77 --- /dev/null +++ b/ppp/ip-down @@ -0,0 +1,16 @@ +#!/system/bin/sh +export PATH=/system/bin + +case $1 in + ppp1) + echo 0 > /proc/sys/net/ipv4/ip_forward + ;; +esac + +# Use interface name if linkname is not available +NAME=${LINKNAME:-"$1"} + +setprop "net.dns1" "" +setprop "net.dns2" "" +setprop "net.$NAME.local-ip" "" +setprop "net.$NAME.remote-ip" "" diff --git a/ppp/ip-up b/ppp/ip-up new file mode 100644 index 0000000..b6fc133 --- /dev/null +++ b/ppp/ip-up @@ -0,0 +1,24 @@ +#!/system/bin/sh +export PATH=/system/bin + +case $1 in + ppp1) + iptables --flush + iptables --table nat --flush + iptables --delete-chain + iptables --table nat --append POSTROUTING --out-interface ppp0 -j MASQUERADE + iptables --append FORWARD --in-interface ppp1 -j ACCEPT + echo 1 > /proc/sys/net/ipv4/ip_forward + ;; + ppp0) + setprop "net.interfaces.defaultroute" "gprs" + ;; +esac + +# Use interface name if linkname is not available +NAME=${LINKNAME:-"$1"} + +setprop "net.dns1" "$DNS1" +setprop "net.dns2" "$DNS2" +setprop "net.$NAME.local-ip" "$IPLOCAL" +setprop "net.$NAME.gw" "$IPREMOTE" diff --git a/ppp/peers/gprs b/ppp/peers/gprs new file mode 100644 index 0000000..b7e93d4 --- /dev/null +++ b/ppp/peers/gprs @@ -0,0 +1,11 @@ +#/etc/ppp/peers/gprs +# This is pppd script for China Mobile, used Huawei GTM900-B GPRS Module +# Usage: root>pppd call gprs + +460800 +nocrtscts +usepeerdns +debug +ipcp-accept-local +ipcp-accept-remote +defaultroute diff --git a/system.prop b/system.prop new file mode 100644 index 0000000..81cab8f --- /dev/null +++ b/system.prop @@ -0,0 +1,2 @@ +# system.prop for lollipop-x86 +#poweroff.doubleclick=1 diff --git a/tablet-mode/Android.mk b/tablet-mode/Android.mk new file mode 100644 index 0000000..e2dc636 --- /dev/null +++ b/tablet-mode/Android.mk @@ -0,0 +1,21 @@ +# Copyright 2012 The Android-x86 Open Source Project + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= tablet-mode.c + +LOCAL_C_INCLUDES := $(KERNEL_HEADERS) +LOCAL_SHARED_LIBRARIES := liblog + +LOCAL_CFLAGS := -O2 -Wall + +ifeq ($(TARGET_ARCH),x86) +LOCAL_CFLAGS += -Ulinux +endif + +LOCAL_MODULE := tablet-mode +LOCAL_MODULE_TAGS := optional +#LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) + +include $(BUILD_EXECUTABLE) diff --git a/tablet-mode/MODULE_LICENSE_GPL b/tablet-mode/MODULE_LICENSE_GPL new file mode 100644 index 0000000..e69de29 diff --git a/tablet-mode/Makefile b/tablet-mode/Makefile new file mode 100644 index 0000000..f59362b --- /dev/null +++ b/tablet-mode/Makefile @@ -0,0 +1,16 @@ +CC=gcc +CFLAGS=-c -Wall -O2 +SOURCES=tablet-mode.c +OBJECTS=$(SOURCES:.c=.o) +EXECUTABLE=tablet-mode + +all: $(SOURCES) $(EXECUTABLE) + +$(EXECUTABLE): $(OBJECTS) + $(CC) $(LDFLAGS) $(OBJECTS) -o $@ + +.c.o: + $(CC) $(CFLAGS) $< -o $@ + +clean: + rm $(OBJECTS) $(EXECUTABLE) diff --git a/tablet-mode/tablet-mode.c b/tablet-mode/tablet-mode.c new file mode 100644 index 0000000..aea8d97 --- /dev/null +++ b/tablet-mode/tablet-mode.c @@ -0,0 +1,125 @@ +/** + * Convert SW_TABLET_MODE events to SW_LID events for Android + * + * Copyright 2012 The Android-x86 Open Source Project + * + * Author: Stefan Seidel + * + * Licensed under GPLv2 or later + * + **/ + +#define LOG_TAG "tablet-mode" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* we must use this kernel-compatible implementation */ +#define BITS_PER_LONG (sizeof(long) * 8) +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define OFF(x) ((x)%BITS_PER_LONG) +#define BIT(x) (1UL<> OFF(bit)) & 1) + +int openfd(void) +{ + int fd; + const char *dirname = "/dev/input"; + DIR *dir; + if ((dir = opendir(dirname))) { + struct dirent *de; + unsigned long caps[NBITS(SW_TABLET_MODE+1)]; + while ((de = readdir(dir))) { + if (de->d_name[0] != 'e') // eventX + continue; + char name[PATH_MAX]; + snprintf(name, PATH_MAX, "%s/%s", dirname, de->d_name); + fd = open(name, O_RDONLY); + if (fd < 0) { + ALOGE("could not open %s, %s", name, strerror(errno)); + continue; + } + memset(caps, 0, sizeof(caps)); + if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(caps)), caps) < 1) { + ALOGE("could not get device caps for %s, %s\n", name, strerror(errno)); + continue; + } + if (test_bit(SW_TABLET_MODE, caps)) { + ALOGI("open %s(%s) ok", de->d_name, name); + return fd; + } + close(fd); + } + closedir(dir); + } + return -1; +} + +void send_switch(int ufd, int state) { + struct input_event nev; + ALOGI("Tablet Mode Switch to %d\n", state); + memset(&nev, 0, sizeof(struct input_event)); + nev.type = EV_SW; + nev.code = SW_LID; + nev.value = !!state; + write(ufd, &nev, sizeof(struct input_event)); + nev.type = EV_SYN; + nev.code = SYN_REPORT; + nev.value = 0; + write(ufd, &nev, sizeof(struct input_event)); +} + +int main(void) +{ + int ifd = openfd(); + if (ifd < 0) { + ALOGE("could not find any tablet mode switch, exiting."); + return -1; + } + + sleep(10); //wait some time or otherwise EventHub might not pick up our events correctly!? + + int ufd = open("/dev/uinput", O_WRONLY | O_NDELAY); + if (ufd >= 0) { + struct uinput_user_dev ud; + memset(&ud, 0, sizeof(struct uinput_user_dev)); + strcpy(ud.name, "Android Tablet Lid Switch"); + write(ufd, &ud, sizeof(struct uinput_user_dev)); + ioctl(ufd, UI_SET_EVBIT, EV_SW); + ioctl(ufd, UI_SET_SWBIT, SW_LID); + ioctl(ufd, UI_DEV_CREATE, 0); + } else { + ALOGE("could not open uinput device: %s", strerror(errno)); + return -1; + } + + // send initial switch state + unsigned long sw_state[NBITS(SW_TABLET_MODE+1)]; + memset(sw_state, 0, sizeof(sw_state)); + if (ioctl(ifd, EVIOCGSW(sizeof(sw_state)), sw_state) >= 0) { + send_switch(ufd, test_bit(SW_TABLET_MODE, sw_state) ? 1 : 0); + } + + // read events and pass them on modified + while (1) { + struct input_event iev; + size_t res = read(ifd, &iev, sizeof(struct input_event)); + if (res < sizeof(struct input_event)) { + ALOGW("insufficient input data(%d)? fd=%d", res, ifd); + continue; + } + //LOGV("type=%d scancode=%d value=%d from fd=%d", iev.type, iev.code, iev.value, ifd); + if (iev.type == EV_SW && iev.code == SW_TABLET_MODE) { + send_switch(ufd, iev.value); + } + } + + return 0; +} diff --git a/tp_smapi/Android.mk b/tp_smapi/Android.mk new file mode 100644 index 0000000..698dc22 --- /dev/null +++ b/tp_smapi/Android.mk @@ -0,0 +1,15 @@ +# +# Copyright (C) 2009-2011 The Android-x86 Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +LOCAL_PATH := $(my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := tp_smapi +EXTRA_KERNEL_MODULE_PATH_$(LOCAL_MODULE) := $(LOCAL_PATH) diff --git a/tp_smapi/Makefile b/tp_smapi/Makefile new file mode 100644 index 0000000..8747819 --- /dev/null +++ b/tp_smapi/Makefile @@ -0,0 +1 @@ +obj-m += thinkpad_ec.o tp_smapi.o hdaps.o diff --git a/tp_smapi/hdaps.c b/tp_smapi/hdaps.c new file mode 100644 index 0000000..f278564 --- /dev/null +++ b/tp_smapi/hdaps.c @@ -0,0 +1,891 @@ +/* + * drivers/platform/x86/hdaps.c - driver for IBM's Hard Drive Active Protection System + * + * Copyright (C) 2005 Robert Love + * Copyright (C) 2005 Jesper Juhl + * + * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads + * starting with the R40, T41, and X40. It provides a basic two-axis + * accelerometer and other data, such as the device's temperature. + * + * This driver is based on the document by Mark A. Smith available at + * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial + * and error. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License v2 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "thinkpad_ec.h" +#include +#include + +/* Embedded controller accelerometer read command and its result: */ +static const struct thinkpad_ec_row ec_accel_args = + { .mask = 0x0001, .val = {0x11} }; +#define EC_ACCEL_IDX_READOUTS 0x1 /* readouts included in this read */ + /* First readout, if READOUTS>=1: */ +#define EC_ACCEL_IDX_YPOS1 0x2 /* y-axis position word */ +#define EC_ACCEL_IDX_XPOS1 0x4 /* x-axis position word */ +#define EC_ACCEL_IDX_TEMP1 0x6 /* device temperature in Celsius */ + /* Second readout, if READOUTS>=2: */ +#define EC_ACCEL_IDX_XPOS2 0x7 /* y-axis position word */ +#define EC_ACCEL_IDX_YPOS2 0x9 /* x-axis position word */ +#define EC_ACCEL_IDX_TEMP2 0xb /* device temperature in Celsius */ +#define EC_ACCEL_IDX_QUEUED 0xc /* Number of queued readouts left */ +#define EC_ACCEL_IDX_KMACT 0xd /* keyboard or mouse activity */ +#define EC_ACCEL_IDX_RETVAL 0xf /* command return value, good=0x00 */ + +#define KEYBD_MASK 0x20 /* set if keyboard activity */ +#define MOUSE_MASK 0x40 /* set if mouse activity */ + +#define READ_TIMEOUT_MSECS 100 /* wait this long for device read */ +#define RETRY_MSECS 3 /* retry delay */ + +#define HDAPS_INPUT_FUZZ 4 /* input event threshold */ +#define HDAPS_INPUT_FLAT 4 +#define KMACT_REMEMBER_PERIOD (HZ/10) /* keyboard/mouse persistance */ + +/* Input IDs */ +#define HDAPS_INPUT_VENDOR PCI_VENDOR_ID_IBM +#define HDAPS_INPUT_PRODUCT 0x5054 /* "TP", shared with thinkpad_acpi */ +#define HDAPS_INPUT_JS_VERSION 0x6801 /* Joystick emulation input device */ +#define HDAPS_INPUT_RAW_VERSION 0x4801 /* Raw accelerometer input device */ + +/* Axis orientation. */ +/* The unnatural bit-representation of inversions is for backward + * compatibility with the"invert=1" module parameter. */ +#define HDAPS_ORIENT_INVERT_XY 0x01 /* Invert both X and Y axes. */ +#define HDAPS_ORIENT_INVERT_X 0x02 /* Invert the X axis (uninvert if + * already inverted by INVERT_XY). */ +#define HDAPS_ORIENT_SWAP 0x04 /* Swap the axes. The swap occurs + * before inverting X or Y. */ +#define HDAPS_ORIENT_MAX 0x07 +#define HDAPS_ORIENT_UNDEFINED 0xFF /* Placeholder during initialization */ +#define HDAPS_ORIENT_INVERT_Y (HDAPS_ORIENT_INVERT_XY | HDAPS_ORIENT_INVERT_X) + +static struct timer_list hdaps_timer; +static struct platform_device *pdev; +static struct input_dev *hdaps_idev; /* joystick-like device with fuzz */ +static struct input_dev *hdaps_idev_raw; /* raw hdaps sensor readouts */ +static unsigned int hdaps_invert = HDAPS_ORIENT_UNDEFINED; +static int needs_calibration; + +/* Configuration: */ +static int sampling_rate = 50; /* Sampling rate */ +static int oversampling_ratio = 5; /* Ratio between our sampling rate and + * EC accelerometer sampling rate */ +static int running_avg_filter_order = 2; /* EC running average filter order */ + +/* Latest state readout: */ +static int pos_x, pos_y; /* position */ +static int temperature; /* temperature */ +static int stale_readout = 1; /* last read invalid */ +static int rest_x, rest_y; /* calibrated rest position */ + +/* Last time we saw keyboard and mouse activity: */ +static u64 last_keyboard_jiffies = INITIAL_JIFFIES; +static u64 last_mouse_jiffies = INITIAL_JIFFIES; +static u64 last_update_jiffies = INITIAL_JIFFIES; + +/* input device use count */ +static int hdaps_users; +static DEFINE_MUTEX(hdaps_users_mtx); + +/* Some models require an axis transformation to the standard representation */ +static void transform_axes(int *x, int *y) +{ + if (hdaps_invert & HDAPS_ORIENT_SWAP) { + int z; + z = *x; + *x = *y; + *y = z; + } + if (hdaps_invert & HDAPS_ORIENT_INVERT_XY) { + *x = -*x; + *y = -*y; + } + if (hdaps_invert & HDAPS_ORIENT_INVERT_X) + *x = -*x; +} + +/** + * __hdaps_update - query current state, with locks already acquired + * @fast: if nonzero, do one quick attempt without retries. + * + * Query current accelerometer state and update global state variables. + * Also prefetches the next query. Caller must hold controller lock. + */ +static int __hdaps_update(int fast) +{ + /* Read data: */ + struct thinkpad_ec_row data; + int ret; + + data.mask = (1 << EC_ACCEL_IDX_READOUTS) | (1 << EC_ACCEL_IDX_KMACT) | + (3 << EC_ACCEL_IDX_YPOS1) | (3 << EC_ACCEL_IDX_XPOS1) | + (1 << EC_ACCEL_IDX_TEMP1) | (1 << EC_ACCEL_IDX_RETVAL); + if (fast) + ret = thinkpad_ec_try_read_row(&ec_accel_args, &data); + else + ret = thinkpad_ec_read_row(&ec_accel_args, &data); + thinkpad_ec_prefetch_row(&ec_accel_args); /* Prefetch even if error */ + if (ret) + return ret; + + /* Check status: */ + if (data.val[EC_ACCEL_IDX_RETVAL] != 0x00) { + printk(KERN_WARNING "hdaps: read RETVAL=0x%02x\n", + data.val[EC_ACCEL_IDX_RETVAL]); + return -EIO; + } + + if (data.val[EC_ACCEL_IDX_READOUTS] < 1) + return -EBUSY; /* no pending readout, try again later */ + + /* Parse position data: */ + pos_x = *(s16 *)(data.val+EC_ACCEL_IDX_XPOS1); + pos_y = *(s16 *)(data.val+EC_ACCEL_IDX_YPOS1); + transform_axes(&pos_x, &pos_y); + + /* Keyboard and mouse activity status is cleared as soon as it's read, + * so applications will eat each other's events. Thus we remember any + * event for KMACT_REMEMBER_PERIOD jiffies. + */ + if (data.val[EC_ACCEL_IDX_KMACT] & KEYBD_MASK) + last_keyboard_jiffies = get_jiffies_64(); + if (data.val[EC_ACCEL_IDX_KMACT] & MOUSE_MASK) + last_mouse_jiffies = get_jiffies_64(); + + temperature = data.val[EC_ACCEL_IDX_TEMP1]; + + last_update_jiffies = get_jiffies_64(); + stale_readout = 0; + if (needs_calibration) { + rest_x = pos_x; + rest_y = pos_y; + needs_calibration = 0; + } + + return 0; +} + +/** + * hdaps_update - acquire locks and query current state + * + * Query current accelerometer state and update global state variables. + * Also prefetches the next query. + * Retries until timeout if the accelerometer is not in ready status (common). + * Does its own locking. + */ +static int hdaps_update(void) +{ + u64 age = get_jiffies_64() - last_update_jiffies; + int total, ret; + + if (!stale_readout && age < (9*HZ)/(10*sampling_rate)) + return 0; /* already updated recently */ + for (total = 0; total < READ_TIMEOUT_MSECS; total += RETRY_MSECS) { + ret = thinkpad_ec_lock(); + if (ret) + return ret; + ret = __hdaps_update(0); + thinkpad_ec_unlock(); + + if (!ret) + return 0; + if (ret != -EBUSY) + break; + msleep(RETRY_MSECS); + } + return ret; +} + +/** + * hdaps_set_power - enable or disable power to the accelerometer. + * Returns zero on success and negative error code on failure. Can sleep. + */ +static int hdaps_set_power(int on) +{ + struct thinkpad_ec_row args = + { .mask = 0x0003, .val = {0x14, on?0x01:0x00} }; + struct thinkpad_ec_row data = { .mask = 0x8000 }; + int ret = thinkpad_ec_read_row(&args, &data); + if (ret) + return ret; + if (data.val[0xF] != 0x00) + return -EIO; + return 0; +} + +/** + * hdaps_set_ec_config - set accelerometer parameters. + * @ec_rate: embedded controller sampling rate + * @order: embedded controller running average filter order + * (Normally we have @ec_rate = sampling_rate * oversampling_ratio.) + * Returns zero on success and negative error code on failure. Can sleep. + */ +static int hdaps_set_ec_config(int ec_rate, int order) +{ + struct thinkpad_ec_row args = { .mask = 0x000F, + .val = {0x10, (u8)ec_rate, (u8)(ec_rate>>8), order} }; + struct thinkpad_ec_row data = { .mask = 0x8000 }; + int ret = thinkpad_ec_read_row(&args, &data); + printk(KERN_DEBUG "hdaps: setting ec_rate=%d, filter_order=%d\n", + ec_rate, order); + if (ret) + return ret; + if (data.val[0xF] == 0x03) { + printk(KERN_WARNING "hdaps: config param out of range\n"); + return -EINVAL; + } + if (data.val[0xF] == 0x06) { + printk(KERN_WARNING "hdaps: config change already pending\n"); + return -EBUSY; + } + if (data.val[0xF] != 0x00) { + printk(KERN_WARNING "hdaps: config change error, ret=%d\n", + data.val[0xF]); + return -EIO; + } + return 0; +} + +/** + * hdaps_get_ec_config - get accelerometer parameters. + * @ec_rate: embedded controller sampling rate + * @order: embedded controller running average filter order + * Returns zero on success and negative error code on failure. Can sleep. + */ +static int hdaps_get_ec_config(int *ec_rate, int *order) +{ + const struct thinkpad_ec_row args = + { .mask = 0x0003, .val = {0x17, 0x82} }; + struct thinkpad_ec_row data = { .mask = 0x801F }; + int ret = thinkpad_ec_read_row(&args, &data); + if (ret) + return ret; + if (data.val[0xF] != 0x00) + return -EIO; + if (!(data.val[0x1] & 0x01)) + return -ENXIO; /* accelerometer polling not enabled */ + if (data.val[0x1] & 0x02) + return -EBUSY; /* config change in progress, retry later */ + *ec_rate = data.val[0x2] | ((int)(data.val[0x3]) << 8); + *order = data.val[0x4]; + return 0; +} + +/** + * hdaps_get_ec_mode - get EC accelerometer mode + * Returns zero on success and negative error code on failure. Can sleep. + */ +static int hdaps_get_ec_mode(u8 *mode) +{ + const struct thinkpad_ec_row args = + { .mask = 0x0001, .val = {0x13} }; + struct thinkpad_ec_row data = { .mask = 0x8002 }; + int ret = thinkpad_ec_read_row(&args, &data); + if (ret) + return ret; + if (data.val[0xF] != 0x00) { + printk(KERN_WARNING + "accelerometer not implemented (0x%02x)\n", + data.val[0xF]); + return -EIO; + } + *mode = data.val[0x1]; + return 0; +} + +/** + * hdaps_check_ec - checks something about the EC. + * Follows the clean-room spec for HDAPS; we don't know what it means. + * Returns zero on success and negative error code on failure. Can sleep. + */ +static int hdaps_check_ec(void) +{ + const struct thinkpad_ec_row args = + { .mask = 0x0003, .val = {0x17, 0x81} }; + struct thinkpad_ec_row data = { .mask = 0x800E }; + int ret = thinkpad_ec_read_row(&args, &data); + if (ret) + return ret; + if (!((data.val[0x1] == 0x00 && data.val[0x2] == 0x60) || /* cleanroom spec */ + (data.val[0x1] == 0x01 && data.val[0x2] == 0x00)) || /* seen on T61 */ + data.val[0x3] != 0x00 || data.val[0xF] != 0x00) { + printk(KERN_WARNING + "hdaps_check_ec: bad response (0x%x,0x%x,0x%x,0x%x)\n", + data.val[0x1], data.val[0x2], + data.val[0x3], data.val[0xF]); + return -EIO; + } + return 0; +} + +/** + * hdaps_device_init - initialize the accelerometer. + * + * Call several embedded controller functions to test and initialize the + * accelerometer. + * Returns zero on success and negative error code on failure. Can sleep. + */ +#define FAILED_INIT(msg) printk(KERN_ERR "hdaps init failed at: %s\n", msg) +static int hdaps_device_init(void) +{ + int ret; + u8 mode; + + ret = thinkpad_ec_lock(); + if (ret) + return ret; + + if (hdaps_get_ec_mode(&mode)) + { FAILED_INIT("hdaps_get_ec_mode failed"); goto bad; } + + printk(KERN_DEBUG "hdaps: initial mode latch is 0x%02x\n", mode); + if (mode == 0x00) + { FAILED_INIT("accelerometer not available"); goto bad; } + + if (hdaps_check_ec()) + { FAILED_INIT("hdaps_check_ec failed"); goto bad; } + + if (hdaps_set_power(1)) + { FAILED_INIT("hdaps_set_power failed"); goto bad; } + + if (hdaps_set_ec_config(sampling_rate*oversampling_ratio, + running_avg_filter_order)) + { FAILED_INIT("hdaps_set_ec_config failed"); goto bad; } + + thinkpad_ec_invalidate(); + udelay(200); + + /* Just prefetch instead of reading, to avoid ~1sec delay on load */ + ret = thinkpad_ec_prefetch_row(&ec_accel_args); + if (ret) + { FAILED_INIT("initial prefetch failed"); goto bad; } + goto good; +bad: + thinkpad_ec_invalidate(); + ret = -ENXIO; +good: + stale_readout = 1; + thinkpad_ec_unlock(); + return ret; +} + +/** + * hdaps_device_shutdown - power off the accelerometer + * Returns nonzero on failure. Can sleep. + */ +static int hdaps_device_shutdown(void) +{ + int ret; + ret = hdaps_set_power(0); + if (ret) { + printk(KERN_WARNING "hdaps: cannot power off\n"); + return ret; + } + ret = hdaps_set_ec_config(0, 1); + if (ret) + printk(KERN_WARNING "hdaps: cannot stop EC sampling\n"); + return ret; +} + +/* Device model stuff */ + +static int hdaps_probe(struct platform_device *dev) +{ + int ret; + + ret = hdaps_device_init(); + if (ret) + return ret; + + printk(KERN_INFO "hdaps: device successfully initialized.\n"); + return 0; +} + +static int hdaps_suspend(struct platform_device *dev, pm_message_t state) +{ + /* Don't do hdaps polls until resume re-initializes the sensor. */ + del_timer_sync(&hdaps_timer); + hdaps_device_shutdown(); /* ignore errors, effect is negligible */ + return 0; +} + +static int hdaps_resume(struct platform_device *dev) +{ + int ret = hdaps_device_init(); + if (ret) + return ret; + + mutex_lock(&hdaps_users_mtx); + if (hdaps_users) + mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate); + mutex_unlock(&hdaps_users_mtx); + return 0; +} + +static struct platform_driver hdaps_driver = { + .probe = hdaps_probe, + .suspend = hdaps_suspend, + .resume = hdaps_resume, + .driver = { + .name = "hdaps", + .owner = THIS_MODULE, + }, +}; + +/** + * hdaps_calibrate - set our "resting" values. + * Does its own locking. + */ +static void hdaps_calibrate(void) +{ + needs_calibration = 1; + hdaps_update(); + /* If that fails, the mousedev poll will take care of things later. */ +} + +/* Timer handler for updating the input device. Runs in softirq context, + * so avoid lenghty or blocking operations. + */ +static void hdaps_mousedev_poll(unsigned long unused) +{ + int ret; + + stale_readout = 1; + + /* Cannot sleep. Try nonblockingly. If we fail, try again later. */ + if (thinkpad_ec_try_lock()) + goto keep_active; + + ret = __hdaps_update(1); /* fast update, we're in softirq context */ + thinkpad_ec_unlock(); + /* Any of "successful", "not yet ready" and "not prefetched"? */ + if (ret != 0 && ret != -EBUSY && ret != -ENODATA) { + printk(KERN_ERR + "hdaps: poll failed, disabling updates\n"); + return; + } + +keep_active: + /* Even if we failed now, pos_x,y may have been updated earlier: */ + input_report_abs(hdaps_idev, ABS_X, pos_x - rest_x); + input_report_abs(hdaps_idev, ABS_Y, pos_y - rest_y); + input_sync(hdaps_idev); + input_report_abs(hdaps_idev_raw, ABS_X, pos_x); + input_report_abs(hdaps_idev_raw, ABS_Y, pos_y); + input_sync(hdaps_idev_raw); + mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate); +} + + +/* Sysfs Files */ + +static ssize_t hdaps_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = hdaps_update(); + if (ret) + return ret; + return sprintf(buf, "(%d,%d)\n", pos_x, pos_y); +} + +static ssize_t hdaps_temp1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = hdaps_update(); + if (ret) + return ret; + return sprintf(buf, "%d\n", temperature); +} + +static ssize_t hdaps_keyboard_activity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret = hdaps_update(); + if (ret) + return ret; + return sprintf(buf, "%u\n", + get_jiffies_64() < last_keyboard_jiffies + KMACT_REMEMBER_PERIOD); +} + +static ssize_t hdaps_mouse_activity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret = hdaps_update(); + if (ret) + return ret; + return sprintf(buf, "%u\n", + get_jiffies_64() < last_mouse_jiffies + KMACT_REMEMBER_PERIOD); +} + +static ssize_t hdaps_calibrate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "(%d,%d)\n", rest_x, rest_y); +} + +static ssize_t hdaps_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + hdaps_calibrate(); + return count; +} + +static ssize_t hdaps_invert_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", hdaps_invert); +} + +static ssize_t hdaps_invert_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int invert; + + if (sscanf(buf, "%d", &invert) != 1 || + invert < 0 || invert > HDAPS_ORIENT_MAX) + return -EINVAL; + + hdaps_invert = invert; + hdaps_calibrate(); + + return count; +} + +static ssize_t hdaps_sampling_rate_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", sampling_rate); +} + +static ssize_t hdaps_sampling_rate_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int rate, ret; + if (sscanf(buf, "%d", &rate) != 1 || rate > HZ || rate <= 0) { + printk(KERN_WARNING + "must have 0driver_data; + hdaps_invert = orient; + printk(KERN_INFO "hdaps: %s detected, setting orientation %u\n", + id->ident, orient); + return 1; /* stop enumeration */ +} + +#define HDAPS_DMI_MATCH_INVERT(vendor, model, orient) { \ + .ident = vendor " " model, \ + .callback = hdaps_dmi_match_invert, \ + .driver_data = (void *)(orient), \ + .matches = { \ + DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ + DMI_MATCH(DMI_PRODUCT_VERSION, model) \ + } \ +} + +/* List of models with abnormal axis configuration. + Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match + "ThinkPad T42p", and enumeration stops after first match, + so the order of the entries matters. */ +struct dmi_system_id __initdata hdaps_whitelist[] = { + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R60", HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X40", HDAPS_ORIENT_INVERT_Y), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_ORIENT_INVERT_Y), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R60", HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60 Tablet", HDAPS_ORIENT_INVERT_Y), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60s", HDAPS_ORIENT_INVERT_Y), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400s", HDAPS_ORIENT_INVERT_X), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T410s", HDAPS_ORIENT_SWAP), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T410", HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T500", HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad W51O", HDAPS_ORIENT_MAX), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X200", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X | HDAPS_ORIENT_INVERT_Y), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201 Tablet", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201s", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_XY), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X220", HDAPS_ORIENT_SWAP), + { .ident = NULL } +}; + +static int __init hdaps_init(void) +{ + int ret; + + /* Determine axis orientation orientation */ + if (hdaps_invert == HDAPS_ORIENT_UNDEFINED) /* set by module param? */ + if (dmi_check_system(hdaps_whitelist) < 1) /* in whitelist? */ + hdaps_invert = 0; /* default */ + + /* Init timer before platform_driver_register, in case of suspend */ + init_timer(&hdaps_timer); + hdaps_timer.function = hdaps_mousedev_poll; + ret = platform_driver_register(&hdaps_driver); + if (ret) + goto out; + + pdev = platform_device_register_simple("hdaps", -1, NULL, 0); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); + goto out_driver; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group); + if (ret) + goto out_device; + + hdaps_idev = input_allocate_device(); + if (!hdaps_idev) { + ret = -ENOMEM; + goto out_group; + } + + hdaps_idev_raw = input_allocate_device(); + if (!hdaps_idev_raw) { + ret = -ENOMEM; + goto out_idev_first; + } + + /* calibration for the input device (deferred to avoid delay) */ + needs_calibration = 1; + + /* initialize the joystick-like fuzzed input device */ + hdaps_idev->name = "hdaps"; + hdaps_idev->phys = "hdaps/input0"; + hdaps_idev->id.bustype = BUS_HOST; + hdaps_idev->id.vendor = HDAPS_INPUT_VENDOR; + hdaps_idev->id.product = HDAPS_INPUT_PRODUCT; + hdaps_idev->id.version = HDAPS_INPUT_JS_VERSION; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + hdaps_idev->cdev.dev = &pdev->dev; +#endif + hdaps_idev->evbit[0] = BIT(EV_ABS); + hdaps_idev->open = hdaps_mousedev_open; + hdaps_idev->close = hdaps_mousedev_close; + input_set_abs_params(hdaps_idev, ABS_X, + -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); + input_set_abs_params(hdaps_idev, ABS_Y, + -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); + + ret = input_register_device(hdaps_idev); + if (ret) + goto out_idev; + + /* initialize the raw data input device */ + hdaps_idev_raw->name = "raw_hdaps_data"; + hdaps_idev_raw->phys = "hdaps/input1"; + hdaps_idev_raw->id.bustype = BUS_HOST; + hdaps_idev_raw->id.vendor = HDAPS_INPUT_VENDOR; + hdaps_idev_raw->id.product = HDAPS_INPUT_PRODUCT; + hdaps_idev_raw->id.version = HDAPS_INPUT_RAW_VERSION; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + hdaps_idev_raw->cdev.dev = &pdev->dev; +#endif + hdaps_idev_raw->evbit[0] = BIT(EV_ABS); + hdaps_idev_raw->open = hdaps_mousedev_open; + hdaps_idev_raw->close = hdaps_mousedev_close; + input_set_abs_params(hdaps_idev_raw, ABS_X, -32768, 32767, 0, 0); + input_set_abs_params(hdaps_idev_raw, ABS_Y, -32768, 32767, 0, 0); + + ret = input_register_device(hdaps_idev_raw); + if (ret) + goto out_idev_reg_first; + + printk(KERN_INFO "hdaps: driver successfully loaded.\n"); + return 0; + +out_idev_reg_first: + input_unregister_device(hdaps_idev); +out_idev: + input_free_device(hdaps_idev_raw); +out_idev_first: + input_free_device(hdaps_idev); +out_group: + sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); +out_device: + platform_device_unregister(pdev); +out_driver: + platform_driver_unregister(&hdaps_driver); + hdaps_device_shutdown(); +out: + printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret); + return ret; +} + +static void __exit hdaps_exit(void) +{ + input_unregister_device(hdaps_idev_raw); + input_unregister_device(hdaps_idev); + hdaps_device_shutdown(); /* ignore errors, effect is negligible */ + sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); + platform_device_unregister(pdev); + platform_driver_unregister(&hdaps_driver); + + printk(KERN_INFO "hdaps: driver unloaded.\n"); +} + +module_init(hdaps_init); +module_exit(hdaps_exit); + +module_param_named(invert, hdaps_invert, uint, 0); +MODULE_PARM_DESC(invert, "axis orientation code"); + +MODULE_AUTHOR("Robert Love"); +MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver"); +MODULE_LICENSE("GPL v2"); diff --git a/tp_smapi/thinkpad_ec.c b/tp_smapi/thinkpad_ec.c new file mode 100644 index 0000000..a1c94d9 --- /dev/null +++ b/tp_smapi/thinkpad_ec.c @@ -0,0 +1,513 @@ +/* + * thinkpad_ec.c - ThinkPad embedded controller LPC3 functions + * + * The embedded controller on ThinkPad laptops has a non-standard interface, + * where LPC channel 3 of the H8S EC chip is hooked up to IO ports + * 0x1600-0x161F and implements (a special case of) the H8S LPC protocol. + * The EC LPC interface provides various system management services (currently + * known: battery information and accelerometer readouts). This driver + * provides access and mutual exclusion for the EC interface. +* + * The LPC protocol and terminology are documented here: + * "H8S/2104B Group Hardware Manual", + * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf + * + * Copyright (C) 2006-2007 Shem Multinymous + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "thinkpad_ec.h" +#include +#include + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) + #include +#else + #include +#endif + +#define TP_VERSION "0.41" + +MODULE_AUTHOR("Shem Multinymous"); +MODULE_DESCRIPTION("ThinkPad embedded controller hardware access"); +MODULE_VERSION(TP_VERSION); +MODULE_LICENSE("GPL"); + +/* IO ports used by embedded controller LPC channel 3: */ +#define TPC_BASE_PORT 0x1600 +#define TPC_NUM_PORTS 0x20 +#define TPC_STR3_PORT 0x1604 /* Reads H8S EC register STR3 */ +#define TPC_TWR0_PORT 0x1610 /* Mapped to H8S EC register TWR0MW/SW */ +#define TPC_TWR15_PORT 0x161F /* Mapped to H8S EC register TWR15. */ + /* (and port TPC_TWR0_PORT+i is mapped to H8S reg TWRi for 00x%02x", \ + msg, args->val[0x0], args->val[0xF], code) + +/* State of request prefetching: */ +static u8 prefetch_arg0, prefetch_argF; /* Args of last prefetch */ +static u64 prefetch_jiffies; /* time of prefetch, or: */ +#define TPC_PREFETCH_NONE INITIAL_JIFFIES /* No prefetch */ +#define TPC_PREFETCH_JUNK (INITIAL_JIFFIES+1) /* Ignore prefetch */ + +/* Locking: */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) +static DECLARE_MUTEX(thinkpad_ec_mutex); +#else +static DEFINE_SEMAPHORE(thinkpad_ec_mutex); +#endif + +/* Kludge in case the ACPI DSDT reserves the ports we need. */ +static int force_io; /* Willing to do IO to ports we couldn't reserve? */ +static int reserved_io; /* Successfully reserved the ports? */ +module_param_named(force_io, force_io, bool, 0600); +MODULE_PARM_DESC(force_io, "Force IO even if region already reserved (0=off, 1=on)"); + +/** + * thinkpad_ec_lock - get lock on the ThinkPad EC + * + * Get exclusive lock for accesing the ThinkPad embedded controller LPC3 + * interface. Returns 0 iff lock acquired. + */ +int thinkpad_ec_lock(void) +{ + int ret; + ret = down_interruptible(&thinkpad_ec_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(thinkpad_ec_lock); + +/** + * thinkpad_ec_try_lock - try getting lock on the ThinkPad EC + * + * Try getting an exclusive lock for accesing the ThinkPad embedded + * controller LPC3. Returns immediately if lock is not available; neither + * blocks nor sleeps. Returns 0 iff lock acquired . + */ +int thinkpad_ec_try_lock(void) +{ + return down_trylock(&thinkpad_ec_mutex); +} +EXPORT_SYMBOL_GPL(thinkpad_ec_try_lock); + +/** + * thinkpad_ec_unlock - release lock on ThinkPad EC + * + * Release a previously acquired exclusive lock on the ThinkPad ebmedded + * controller LPC3 interface. + */ +void thinkpad_ec_unlock(void) +{ + up(&thinkpad_ec_mutex); +} +EXPORT_SYMBOL_GPL(thinkpad_ec_unlock); + +/** + * thinkpad_ec_request_row - tell embedded controller to prepare a row + * @args Input register arguments + * + * Requests a data row by writing to H8S LPC registers TRW0 through TWR15 (or + * a subset thereof) following the protocol prescribed by the "H8S/2104B Group + * Hardware Manual". Does sanity checks via status register STR3. + */ +static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args) +{ + u8 str3; + int i; + + /* EC protocol requires write to TWR0 (function code): */ + if (!(args->mask & 0x0001)) { + printk(KERN_ERR MSG_FMT("bad args->mask=0x%02x", args->mask)); + return -EINVAL; + } + + /* Check initial STR3 status: */ + str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK; + if (str3 & H8S_STR3_OBF3B) { /* data already pending */ + inb(TPC_TWR15_PORT); /* marks end of previous transaction */ + if (prefetch_jiffies == TPC_PREFETCH_NONE) + printk(KERN_WARNING REQ_FMT( + "EC has result from unrequested transaction", + str3)); + return -EBUSY; /* EC will be ready in a few usecs */ + } else if (str3 == H8S_STR3_SWMF) { /* busy with previous request */ + if (prefetch_jiffies == TPC_PREFETCH_NONE) + printk(KERN_WARNING REQ_FMT( + "EC is busy with unrequested transaction", + str3)); + return -EBUSY; /* data will be pending in a few usecs */ + } else if (str3 != 0x00) { /* unexpected status? */ + printk(KERN_WARNING REQ_FMT("unexpected initial STR3", str3)); + return -EIO; + } + + /* Send TWR0MW: */ + outb(args->val[0], TPC_TWR0_PORT); + str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK; + if (str3 != H8S_STR3_MWMF) { /* not accepted? */ + printk(KERN_WARNING REQ_FMT("arg0 rejected", str3)); + return -EIO; + } + + /* Send TWR1 through TWR14: */ + for (i = 1; i < TP_CONTROLLER_ROW_LEN-1; i++) + if ((args->mask>>i)&1) + outb(args->val[i], TPC_TWR0_PORT+i); + + /* Send TWR15 (default to 0x01). This marks end of command. */ + outb((args->mask & 0x8000) ? args->val[0xF] : 0x01, TPC_TWR15_PORT); + + /* Wait until EC starts writing its reply (~60ns on average). + * Releasing locks before this happens may cause an EC hang + * due to firmware bug! + */ + for (i = 0; i < TPC_REQUEST_RETRIES; i++) { + str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK; + if (str3 & H8S_STR3_SWMF) /* EC started replying */ + return 0; + else if (!(str3 & ~(H8S_STR3_IBF3B|H8S_STR3_MWMF))) + /* Normal progress (the EC hasn't seen the request + * yet, or is processing it). Wait it out. */ + ndelay(TPC_REQUEST_NDELAY); + else { /* weird EC status */ + printk(KERN_WARNING + REQ_FMT("bad end STR3", str3)); + return -EIO; + } + } + printk(KERN_WARNING REQ_FMT("EC is mysteriously silent", str3)); + return -EIO; +} + +/** + * thinkpad_ec_read_data - read pre-requested row-data from EC + * @args Input register arguments of pre-requested rows + * @data Output register values + * + * Reads current row data from the controller, assuming it's already + * requested. Follows the H8S spec for register access and status checks. + */ +static int thinkpad_ec_read_data(const struct thinkpad_ec_row *args, + struct thinkpad_ec_row *data) +{ + int i; + u8 str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK; + /* Once we make a request, STR3 assumes the sequence of values listed + * in the following 'if' as it reads the request and writes its data. + * It takes about a few dozen nanosecs total, with very high variance. + */ + if (str3 == (H8S_STR3_IBF3B|H8S_STR3_MWMF) || + str3 == 0x00 || /* the 0x00 is indistinguishable from idle EC! */ + str3 == H8S_STR3_SWMF) + return -EBUSY; /* not ready yet */ + /* Finally, the EC signals output buffer full: */ + if (str3 != (H8S_STR3_OBF3B|H8S_STR3_SWMF)) { + printk(KERN_WARNING + REQ_FMT("bad initial STR3", str3)); + return -EIO; + } + + /* Read first byte (signals start of read transactions): */ + data->val[0] = inb(TPC_TWR0_PORT); + /* Optionally read 14 more bytes: */ + for (i = 1; i < TP_CONTROLLER_ROW_LEN-1; i++) + if ((data->mask >> i)&1) + data->val[i] = inb(TPC_TWR0_PORT+i); + /* Read last byte from 0x161F (signals end of read transaction): */ + data->val[0xF] = inb(TPC_TWR15_PORT); + + /* Readout still pending? */ + str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK; + if (str3 & H8S_STR3_OBF3B) + printk(KERN_WARNING + REQ_FMT("OBF3B=1 after read", str3)); + /* If port 0x161F returns 0x80 too often, the EC may lock up. Warn: */ + if (data->val[0xF] == 0x80) + printk(KERN_WARNING + REQ_FMT("0x161F reports error", data->val[0xF])); + return 0; +} + +/** + * thinkpad_ec_is_row_fetched - is the given row currently prefetched? + * + * To keep things simple we compare only the first and last args; + * this suffices for all known cases. + */ +static int thinkpad_ec_is_row_fetched(const struct thinkpad_ec_row *args) +{ + return (prefetch_jiffies != TPC_PREFETCH_NONE) && + (prefetch_jiffies != TPC_PREFETCH_JUNK) && + (prefetch_arg0 == args->val[0]) && + (prefetch_argF == args->val[0xF]) && + (get_jiffies_64() < prefetch_jiffies + TPC_PREFETCH_TIMEOUT); +} + +/** + * thinkpad_ec_read_row - request and read data from ThinkPad EC + * @args Input register arguments + * @data Output register values + * + * Read a data row from the ThinkPad embedded controller LPC3 interface. + * Does fetching and retrying if needed. The row is specified by an + * array of 16 bytes, some of which may be undefined (but the first is + * mandatory). These bytes are given in @args->val[], where @args->val[i] is + * used iff (@args->mask>>i)&1). The resulting row data is stored in + * @data->val[], but is only guaranteed to be valid for indices corresponding + * to set bit in @data->mask. That is, if @data->mask&(1<val[i] is undefined. + * + * Returns -EBUSY on transient error and -EIO on abnormal condition. + * Caller must hold controller lock. + */ +int thinkpad_ec_read_row(const struct thinkpad_ec_row *args, + struct thinkpad_ec_row *data) +{ + int retries, ret; + + if (thinkpad_ec_is_row_fetched(args)) + goto read_row; /* already requested */ + + /* Request the row */ + for (retries = 0; retries < TPC_READ_RETRIES; ++retries) { + ret = thinkpad_ec_request_row(args); + if (!ret) + goto read_row; + if (ret != -EBUSY) + break; + ndelay(TPC_READ_NDELAY); + } + printk(KERN_ERR REQ_FMT("failed requesting row", ret)); + goto out; + +read_row: + /* Read the row's data */ + for (retries = 0; retries < TPC_READ_RETRIES; ++retries) { + ret = thinkpad_ec_read_data(args, data); + if (!ret) + goto out; + if (ret != -EBUSY) + break; + ndelay(TPC_READ_NDELAY); + } + + printk(KERN_ERR REQ_FMT("failed waiting for data", ret)); + +out: + prefetch_jiffies = TPC_PREFETCH_JUNK; + return ret; +} +EXPORT_SYMBOL_GPL(thinkpad_ec_read_row); + +/** + * thinkpad_ec_try_read_row - try reading prefetched data from ThinkPad EC + * @args Input register arguments + * @data Output register values + * + * Try reading a data row from the ThinkPad embedded controller LPC3 + * interface, if this raw was recently prefetched using + * thinkpad_ec_prefetch_row(). Does not fetch, retry or block. + * The parameters have the same meaning as in thinkpad_ec_read_row(). + * + * Returns -EBUSY is data not ready and -ENODATA if row not prefetched. + * Caller must hold controller lock. + */ +int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args, + struct thinkpad_ec_row *data) +{ + int ret; + if (!thinkpad_ec_is_row_fetched(args)) { + ret = -ENODATA; + } else { + ret = thinkpad_ec_read_data(args, data); + if (!ret) + prefetch_jiffies = TPC_PREFETCH_NONE; /* eaten up */ + } + return ret; +} +EXPORT_SYMBOL_GPL(thinkpad_ec_try_read_row); + +/** + * thinkpad_ec_prefetch_row - prefetch data from ThinkPad EC + * @args Input register arguments + * + * Prefetch a data row from the ThinkPad embedded controller LCP3 + * interface. A subsequent call to thinkpad_ec_read_row() with the + * same arguments will be faster, and a subsequent call to + * thinkpad_ec_try_read_row() stands a good chance of succeeding if + * done neither too soon nor too late. See + * thinkpad_ec_read_row() for the meaning of @args. + * + * Returns -EBUSY on transient error and -EIO on abnormal condition. + * Caller must hold controller lock. + */ +int thinkpad_ec_prefetch_row(const struct thinkpad_ec_row *args) +{ + int ret; + ret = thinkpad_ec_request_row(args); + if (ret) { + prefetch_jiffies = TPC_PREFETCH_JUNK; + } else { + prefetch_jiffies = get_jiffies_64(); + prefetch_arg0 = args->val[0x0]; + prefetch_argF = args->val[0xF]; + } + return ret; +} +EXPORT_SYMBOL_GPL(thinkpad_ec_prefetch_row); + +/** + * thinkpad_ec_invalidate - invalidate prefetched ThinkPad EC data + * + * Invalidate the data prefetched via thinkpad_ec_prefetch_row() from the + * ThinkPad embedded controller LPC3 interface. + * Must be called before unlocking by any code that accesses the controller + * ports directly. + */ +void thinkpad_ec_invalidate(void) +{ + prefetch_jiffies = TPC_PREFETCH_JUNK; +} +EXPORT_SYMBOL_GPL(thinkpad_ec_invalidate); + + +/*** Checking for EC hardware ***/ + +/** + * thinkpad_ec_test - verify the EC is present and follows protocol + * + * Ensure the EC LPC3 channel really works on this machine by making + * an EC request and seeing if the EC follows the documented H8S protocol. + * The requested row just reads battery status, so it should be harmless to + * access it (on a correct EC). + * This test writes to IO ports, so execute only after checking DMI. + */ +static int __init thinkpad_ec_test(void) +{ + int ret; + const struct thinkpad_ec_row args = /* battery 0 basic status */ + { .mask = 0x8001, .val = {0x01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x00} }; + struct thinkpad_ec_row data = { .mask = 0x0000 }; + ret = thinkpad_ec_lock(); + if (ret) + return ret; + ret = thinkpad_ec_read_row(&args, &data); + thinkpad_ec_unlock(); + return ret; +} + +/* Search all DMI device names of a given type for a substring */ +static int __init dmi_find_substring(int type, const char *substr) +{ + const struct dmi_device *dev = NULL; + while ((dev = dmi_find_device(type, NULL, dev))) { + if (strstr(dev->name, substr)) + return 1; + } + return 0; +} + +#define TP_DMI_MATCH(vendor,model) { \ + .ident = vendor " " model, \ + .matches = { \ + DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ + DMI_MATCH(DMI_PRODUCT_VERSION, model) \ + } \ +} + +/* Check DMI for existence of ThinkPad embedded controller */ +static int __init check_dmi_for_ec(void) +{ + /* A few old models that have a good EC but don't report it in DMI */ + struct dmi_system_id tp_whitelist[] = { + TP_DMI_MATCH("IBM", "ThinkPad A30"), + TP_DMI_MATCH("IBM", "ThinkPad T23"), + TP_DMI_MATCH("IBM", "ThinkPad X24"), + TP_DMI_MATCH("LENOVO", "ThinkPad"), + { .ident = NULL } + }; + return dmi_find_substring(DMI_DEV_TYPE_OEM_STRING, + "IBM ThinkPad Embedded Controller") || + dmi_check_system(tp_whitelist); +} + +/*** Init and cleanup ***/ + +static int __init thinkpad_ec_init(void) +{ + if (!check_dmi_for_ec()) { + printk(KERN_WARNING + "thinkpad_ec: no ThinkPad embedded controller!\n"); + return -ENODEV; + } + + if (request_region(TPC_BASE_PORT, TPC_NUM_PORTS, "thinkpad_ec")) { + reserved_io = 1; + } else { + printk(KERN_ERR "thinkpad_ec: cannot claim IO ports %#x-%#x... ", + TPC_BASE_PORT, + TPC_BASE_PORT + TPC_NUM_PORTS - 1); + if (force_io) { + printk("forcing use of unreserved IO ports.\n"); + } else { + printk("consider using force_io=1.\n"); + return -ENXIO; + } + } + prefetch_jiffies = TPC_PREFETCH_JUNK; + if (thinkpad_ec_test()) { + printk(KERN_ERR "thinkpad_ec: initial ec test failed\n"); + if (reserved_io) + release_region(TPC_BASE_PORT, TPC_NUM_PORTS); + return -ENXIO; + } + printk(KERN_INFO "thinkpad_ec: thinkpad_ec " TP_VERSION " loaded.\n"); + return 0; +} + +static void __exit thinkpad_ec_exit(void) +{ + if (reserved_io) + release_region(TPC_BASE_PORT, TPC_NUM_PORTS); + printk(KERN_INFO "thinkpad_ec: unloaded.\n"); +} + +module_init(thinkpad_ec_init); +module_exit(thinkpad_ec_exit); diff --git a/tp_smapi/thinkpad_ec.h b/tp_smapi/thinkpad_ec.h new file mode 100644 index 0000000..1b80d7e --- /dev/null +++ b/tp_smapi/thinkpad_ec.h @@ -0,0 +1,47 @@ +/* + * thinkpad_ec.h - interface to ThinkPad embedded controller LPC3 functions + * + * Copyright (C) 2005 Shem Multinymous + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _THINKPAD_EC_H +#define _THINKPAD_EC_H + +#ifdef __KERNEL__ + +#define TP_CONTROLLER_ROW_LEN 16 + +/* EC transactions input and output (possibly partial) vectors of 16 bytes. */ +struct thinkpad_ec_row { + u16 mask; /* bitmap of which entries of val[] are meaningful */ + u8 val[TP_CONTROLLER_ROW_LEN]; +}; + +extern int __must_check thinkpad_ec_lock(void); +extern int __must_check thinkpad_ec_try_lock(void); +extern void thinkpad_ec_unlock(void); + +extern int thinkpad_ec_read_row(const struct thinkpad_ec_row *args, + struct thinkpad_ec_row *data); +extern int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args, + struct thinkpad_ec_row *mask); +extern int thinkpad_ec_prefetch_row(const struct thinkpad_ec_row *args); +extern void thinkpad_ec_invalidate(void); + + +#endif /* __KERNEL */ +#endif /* _THINKPAD_EC_H */ diff --git a/tp_smapi/tp_smapi.c b/tp_smapi/tp_smapi.c new file mode 100644 index 0000000..f4d71a3 --- /dev/null +++ b/tp_smapi/tp_smapi.c @@ -0,0 +1,1493 @@ +/* + * tp_smapi.c - ThinkPad SMAPI support + * + * This driver exposes some features of the System Management Application + * Program Interface (SMAPI) BIOS found on ThinkPad laptops. It works on + * models in which the SMAPI BIOS runs in SMM and is invoked by writing + * to the APM control port 0xB2. + * It also exposes battery status information, obtained from the ThinkPad + * embedded controller (via the thinkpad_ec module). + * Ancient ThinkPad models use a different interface, supported by the + * "thinkpad" module from "tpctl". + * + * Many of the battery status values obtained from the EC simply mirror + * values provided by the battery's Smart Battery System (SBS) interface, so + * their meaning is defined by the Smart Battery Data Specification (see + * http://sbs-forum.org/specs/sbdat110.pdf). References to this SBS spec + * are given in the code where relevant. + * + * Copyright (C) 2006 Shem Multinymous . + * SMAPI access code based on the mwave driver by Mike Sullivan. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include /* CMOS defines */ +#include +#include +#include "thinkpad_ec.h" +#include +#include +#include + +#define TP_VERSION "0.41" +#define TP_DESC "ThinkPad SMAPI Support" +#define TP_DIR "smapi" + +MODULE_AUTHOR("Shem Multinymous"); +MODULE_DESCRIPTION(TP_DESC); +MODULE_VERSION(TP_VERSION); +MODULE_LICENSE("GPL"); + +static struct platform_device *pdev; + +static int tp_debug; +module_param_named(debug, tp_debug, int, 0600); +MODULE_PARM_DESC(debug, "Debug level (0=off, 1=on)"); + +/* A few macros for printk()ing: */ +#define TPRINTK(level, fmt, args...) \ + dev_printk(level, &(pdev->dev), "%s: " fmt "\n", __func__, ## args) +#define DPRINTK(fmt, args...) \ + do { if (tp_debug) TPRINTK(KERN_DEBUG, fmt, ## args); } while (0) + +/********************************************************************* + * SMAPI interface + */ + +/* SMAPI functions (register BX when making the SMM call). */ +#define SMAPI_GET_INHIBIT_CHARGE 0x2114 +#define SMAPI_SET_INHIBIT_CHARGE 0x2115 +#define SMAPI_GET_THRESH_START 0x2116 +#define SMAPI_SET_THRESH_START 0x2117 +#define SMAPI_GET_FORCE_DISCHARGE 0x2118 +#define SMAPI_SET_FORCE_DISCHARGE 0x2119 +#define SMAPI_GET_THRESH_STOP 0x211a +#define SMAPI_SET_THRESH_STOP 0x211b + +/* SMAPI error codes (see ThinkPad 770 Technical Reference Manual p.83 at + http://www-307.ibm.com/pc/support/site.wss/document.do?lndocid=PFAN-3TUQQD */ +#define SMAPI_RETCODE_EOF 0xff +static struct { u8 rc; char *msg; int ret; } smapi_retcode[] = +{ + {0x00, "OK", 0}, + {0x53, "SMAPI function is not available", -ENXIO}, + {0x81, "Invalid parameter", -EINVAL}, + {0x86, "Function is not supported by SMAPI BIOS", -EOPNOTSUPP}, + {0x90, "System error", -EIO}, + {0x91, "System is invalid", -EIO}, + {0x92, "System is busy, -EBUSY"}, + {0xa0, "Device error (disk read error)", -EIO}, + {0xa1, "Device is busy", -EBUSY}, + {0xa2, "Device is not attached", -ENXIO}, + {0xa3, "Device is disbled", -EIO}, + {0xa4, "Request parameter is out of range", -EINVAL}, + {0xa5, "Request parameter is not accepted", -EINVAL}, + {0xa6, "Transient error", -EBUSY}, /* ? */ + {SMAPI_RETCODE_EOF, "Unknown error code", -EIO} +}; + + +#define SMAPI_MAX_RETRIES 10 +#define SMAPI_PORT2 0x4F /* fixed port, meaning unclear */ +static unsigned short smapi_port; /* APM control port, normally 0xB2 */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) +static DECLARE_MUTEX(smapi_mutex); +#else +static DEFINE_SEMAPHORE(smapi_mutex); +#endif + +/** + * find_smapi_port - read SMAPI port from NVRAM + */ +static int __init find_smapi_port(void) +{ + u16 smapi_id = 0; + unsigned short port = 0; + unsigned long flags; + + spin_lock_irqsave(&rtc_lock, flags); + smapi_id = CMOS_READ(0x7C); + smapi_id |= (CMOS_READ(0x7D) << 8); + spin_unlock_irqrestore(&rtc_lock, flags); + + if (smapi_id != 0x5349) { + printk(KERN_ERR "SMAPI not supported (ID=0x%x)\n", smapi_id); + return -ENXIO; + } + spin_lock_irqsave(&rtc_lock, flags); + port = CMOS_READ(0x7E); + port |= (CMOS_READ(0x7F) << 8); + spin_unlock_irqrestore(&rtc_lock, flags); + if (port == 0) { + printk(KERN_ERR "unable to read SMAPI port number\n"); + return -ENXIO; + } + return port; +} + +/** + * smapi_request - make a SMAPI call + * @inEBX, @inECX, @inEDI, @inESI: input registers + * @outEBX, @outECX, @outEDX, @outEDI, @outESI: outputs registers + * @msg: textual error message + * Invokes the SMAPI SMBIOS with the given input and outpu args. + * All outputs are optional (can be %NULL). + * Returns 0 when successful, and a negative errno constant + * (see smapi_retcode above) upon failure. + */ +static int smapi_request(u32 inEBX, u32 inECX, + u32 inEDI, u32 inESI, + u32 *outEBX, u32 *outECX, u32 *outEDX, + u32 *outEDI, u32 *outESI, const char **msg) +{ + int ret = 0; + int i; + int retries; + u8 rc; + /* Must use local vars for output regs, due to reg pressure. */ + u32 tmpEAX, tmpEBX, tmpECX, tmpEDX, tmpEDI, tmpESI; + + for (retries = 0; retries < SMAPI_MAX_RETRIES; ++retries) { + DPRINTK("req_in: BX=%x CX=%x DI=%x SI=%x", + inEBX, inECX, inEDI, inESI); + + /* SMAPI's SMBIOS call and thinkpad_ec end up using use + * different interfaces to the same chip, so play it safe. */ + ret = thinkpad_ec_lock(); + if (ret) + return ret; + + __asm__ __volatile__( + "movl $0x00005380,%%eax\n\t" + "movl %6,%%ebx\n\t" + "movl %7,%%ecx\n\t" + "movl %8,%%edi\n\t" + "movl %9,%%esi\n\t" + "xorl %%edx,%%edx\n\t" + "movw %10,%%dx\n\t" + "out %%al,%%dx\n\t" /* trigger SMI to SMBIOS */ + "out %%al,$0x4F\n\t" + "movl %%eax,%0\n\t" + "movl %%ebx,%1\n\t" + "movl %%ecx,%2\n\t" + "movl %%edx,%3\n\t" + "movl %%edi,%4\n\t" + "movl %%esi,%5\n\t" + :"=m"(tmpEAX), + "=m"(tmpEBX), + "=m"(tmpECX), + "=m"(tmpEDX), + "=m"(tmpEDI), + "=m"(tmpESI) + :"m"(inEBX), "m"(inECX), "m"(inEDI), "m"(inESI), + "m"((u16)smapi_port) + :"%eax", "%ebx", "%ecx", "%edx", "%edi", + "%esi"); + + thinkpad_ec_invalidate(); + thinkpad_ec_unlock(); + + /* Don't let the next SMAPI access happen too quickly, + * may case problems. (We're hold smapi_mutex). */ + msleep(50); + + if (outEBX) *outEBX = tmpEBX; + if (outECX) *outECX = tmpECX; + if (outEDX) *outEDX = tmpEDX; + if (outESI) *outESI = tmpESI; + if (outEDI) *outEDI = tmpEDI; + + /* Look up error code */ + rc = (tmpEAX>>8)&0xFF; + for (i = 0; smapi_retcode[i].rc != SMAPI_RETCODE_EOF && + smapi_retcode[i].rc != rc; ++i) {} + ret = smapi_retcode[i].ret; + if (msg) + *msg = smapi_retcode[i].msg; + + DPRINTK("req_out: AX=%x BX=%x CX=%x DX=%x DI=%x SI=%x r=%d", + tmpEAX, tmpEBX, tmpECX, tmpEDX, tmpEDI, tmpESI, ret); + if (ret) + TPRINTK(KERN_NOTICE, "SMAPI error: %s (func=%x)", + smapi_retcode[i].msg, inEBX); + + if (ret != -EBUSY) + return ret; + } + return ret; +} + +/* Convenience wrapper: discard output arguments */ +static int smapi_write(u32 inEBX, u32 inECX, + u32 inEDI, u32 inESI, const char **msg) +{ + return smapi_request(inEBX, inECX, inEDI, inESI, + NULL, NULL, NULL, NULL, NULL, msg); +} + + +/********************************************************************* + * Specific SMAPI services + * All of these functions return 0 upon success, and a negative errno + * constant (see smapi_retcode) on failure. + */ + +enum thresh_type { + THRESH_STOP = 0, /* the code assumes this is 0 for brevity */ + THRESH_START +}; +#define THRESH_NAME(which) ((which == THRESH_START) ? "start" : "stop") + +/** + * __get_real_thresh - read battery charge start/stop threshold from SMAPI + * @bat: battery number (0 or 1) + * @which: THRESH_START or THRESH_STOP + * @thresh: 1..99, 0=default 1..99, 0=default (pass this as-is to SMAPI) + * @outEDI: some additional state that needs to be preserved, meaning unknown + * @outESI: some additional state that needs to be preserved, meaning unknown + */ +static int __get_real_thresh(int bat, enum thresh_type which, int *thresh, + u32 *outEDI, u32 *outESI) +{ + u32 ebx = (which == THRESH_START) ? SMAPI_GET_THRESH_START + : SMAPI_GET_THRESH_STOP; + u32 ecx = (bat+1)<<8; + const char *msg; + int ret = smapi_request(ebx, ecx, 0, 0, NULL, + &ecx, NULL, outEDI, outESI, &msg); + if (ret) { + TPRINTK(KERN_NOTICE, "cannot get %s_thresh of bat=%d: %s", + THRESH_NAME(which), bat, msg); + return ret; + } + if (!(ecx&0x00000100)) { + TPRINTK(KERN_NOTICE, "cannot get %s_thresh of bat=%d: ecx=0%x", + THRESH_NAME(which), bat, ecx); + return -EIO; + } + if (thresh) + *thresh = ecx&0xFF; + return 0; +} + +/** + * get_real_thresh - read battery charge start/stop threshold from SMAPI + * @bat: battery number (0 or 1) + * @which: THRESH_START or THRESH_STOP + * @thresh: 1..99, 0=default (passes as-is to SMAPI) + */ +static int get_real_thresh(int bat, enum thresh_type which, int *thresh) +{ + return __get_real_thresh(bat, which, thresh, NULL, NULL); +} + +/** + * set_real_thresh - write battery start/top charge threshold to SMAPI + * @bat: battery number (0 or 1) + * @which: THRESH_START or THRESH_STOP + * @thresh: 1..99, 0=default (passes as-is to SMAPI) + */ +static int set_real_thresh(int bat, enum thresh_type which, int thresh) +{ + u32 ebx = (which == THRESH_START) ? SMAPI_SET_THRESH_START + : SMAPI_SET_THRESH_STOP; + u32 ecx = ((bat+1)<<8) + thresh; + u32 getDI, getSI; + const char *msg; + int ret; + + /* verify read before writing */ + ret = __get_real_thresh(bat, which, NULL, &getDI, &getSI); + if (ret) + return ret; + + ret = smapi_write(ebx, ecx, getDI, getSI, &msg); + if (ret) + TPRINTK(KERN_NOTICE, "set %s to %d for bat=%d failed: %s", + THRESH_NAME(which), thresh, bat, msg); + else + TPRINTK(KERN_INFO, "set %s to %d for bat=%d", + THRESH_NAME(which), thresh, bat); + return ret; +} + +/** + * __get_inhibit_charge_minutes - get inhibit charge period from SMAPI + * @bat: battery number (0 or 1) + * @minutes: period in minutes (1..65535 minutes, 0=disabled) + * @outECX: some additional state that needs to be preserved, meaning unknown + * Note that @minutes is the originally set value, it does not count down. + */ +static int __get_inhibit_charge_minutes(int bat, int *minutes, u32 *outECX) +{ + u32 ecx = (bat+1)<<8; + u32 esi; + const char *msg; + int ret = smapi_request(SMAPI_GET_INHIBIT_CHARGE, ecx, 0, 0, + NULL, &ecx, NULL, NULL, &esi, &msg); + if (ret) { + TPRINTK(KERN_NOTICE, "failed for bat=%d: %s", bat, msg); + return ret; + } + if (!(ecx&0x0100)) { + TPRINTK(KERN_NOTICE, "bad ecx=0x%x for bat=%d", ecx, bat); + return -EIO; + } + if (minutes) + *minutes = (ecx&0x0001)?esi:0; + if (outECX) + *outECX = ecx; + return 0; +} + +/** + * get_inhibit_charge_minutes - get inhibit charge period from SMAPI + * @bat: battery number (0 or 1) + * @minutes: period in minutes (1..65535 minutes, 0=disabled) + * Note that @minutes is the originally set value, it does not count down. + */ +static int get_inhibit_charge_minutes(int bat, int *minutes) +{ + return __get_inhibit_charge_minutes(bat, minutes, NULL); +} + +/** + * set_inhibit_charge_minutes - write inhibit charge period to SMAPI + * @bat: battery number (0 or 1) + * @minutes: period in minutes (1..65535 minutes, 0=disabled) + */ +static int set_inhibit_charge_minutes(int bat, int minutes) +{ + u32 ecx; + const char *msg; + int ret; + + /* verify read before writing */ + ret = __get_inhibit_charge_minutes(bat, NULL, &ecx); + if (ret) + return ret; + + ecx = ((bat+1)<<8) | (ecx&0x00FE) | (minutes > 0 ? 0x0001 : 0x0000); + if (minutes > 0xFFFF) + minutes = 0xFFFF; + ret = smapi_write(SMAPI_SET_INHIBIT_CHARGE, ecx, 0, minutes, &msg); + if (ret) + TPRINTK(KERN_NOTICE, + "set to %d failed for bat=%d: %s", minutes, bat, msg); + else + TPRINTK(KERN_INFO, "set to %d for bat=%d\n", minutes, bat); + return ret; +} + + +/** + * get_force_discharge - get status of forced discharging from SMAPI + * @bat: battery number (0 or 1) + * @enabled: 1 if forced discharged is enabled, 0 if not + */ +static int get_force_discharge(int bat, int *enabled) +{ + u32 ecx = (bat+1)<<8; + const char *msg; + int ret = smapi_request(SMAPI_GET_FORCE_DISCHARGE, ecx, 0, 0, + NULL, &ecx, NULL, NULL, NULL, &msg); + if (ret) { + TPRINTK(KERN_NOTICE, "failed for bat=%d: %s", bat, msg); + return ret; + } + *enabled = (!(ecx&0x00000100) && (ecx&0x00000001))?1:0; + return 0; +} + +/** + * set_force_discharge - write status of forced discharging to SMAPI + * @bat: battery number (0 or 1) + * @enabled: 1 if forced discharged is enabled, 0 if not + */ +static int set_force_discharge(int bat, int enabled) +{ + u32 ecx = (bat+1)<<8; + const char *msg; + int ret = smapi_request(SMAPI_GET_FORCE_DISCHARGE, ecx, 0, 0, + NULL, &ecx, NULL, NULL, NULL, &msg); + if (ret) { + TPRINTK(KERN_NOTICE, "get failed for bat=%d: %s", bat, msg); + return ret; + } + if (ecx&0x00000100) { + TPRINTK(KERN_NOTICE, "cannot force discharge bat=%d", bat); + return -EIO; + } + + ecx = ((bat+1)<<8) | (ecx&0x000000FA) | (enabled?0x00000001:0); + ret = smapi_write(SMAPI_SET_FORCE_DISCHARGE, ecx, 0, 0, &msg); + if (ret) + TPRINTK(KERN_NOTICE, "set to %d failed for bat=%d: %s", + enabled, bat, msg); + else + TPRINTK(KERN_INFO, "set to %d for bat=%d", enabled, bat); + return ret; +} + + +/********************************************************************* + * Wrappers to threshold-related SMAPI functions, which handle default + * thresholds and related quirks. + */ + +/* Minimum, default and minimum difference for battery charging thresholds: */ +#define MIN_THRESH_DELTA 4 /* Min delta between start and stop thresh */ +#define MIN_THRESH_START 2 +#define MAX_THRESH_START (100-MIN_THRESH_DELTA) +#define MIN_THRESH_STOP (MIN_THRESH_START + MIN_THRESH_DELTA) +#define MAX_THRESH_STOP 100 +#define DEFAULT_THRESH_START MAX_THRESH_START +#define DEFAULT_THRESH_STOP MAX_THRESH_STOP + +/* The GUI of IBM's Battery Maximizer seems to show a start threshold that + * is 1 more than the value we set/get via SMAPI. Since the threshold is + * maintained across reboot, this can be confusing. So we kludge our + * interface for interoperability: */ +#define BATMAX_FIX 1 + +/* Get charge start/stop threshold (1..100), + * substituting default values if needed and applying BATMAT_FIX. */ +static int get_thresh(int bat, enum thresh_type which, int *thresh) +{ + int ret = get_real_thresh(bat, which, thresh); + if (ret) + return ret; + if (*thresh == 0) + *thresh = (which == THRESH_START) ? DEFAULT_THRESH_START + : DEFAULT_THRESH_STOP; + else if (which == THRESH_START) + *thresh += BATMAX_FIX; + return 0; +} + + +/* Set charge start/stop threshold (1..100), + * substituting default values if needed and applying BATMAT_FIX. */ +static int set_thresh(int bat, enum thresh_type which, int thresh) +{ + if (which == THRESH_STOP && thresh == DEFAULT_THRESH_STOP) + thresh = 0; /* 100 is out of range, but default means 100 */ + if (which == THRESH_START) + thresh -= BATMAX_FIX; + return set_real_thresh(bat, which, thresh); +} + +/********************************************************************* + * ThinkPad embedded controller readout and basic functions + */ + +/** + * read_tp_ec_row - read data row from the ThinkPad embedded controller + * @arg0: EC command code + * @bat: battery number, 0 or 1 + * @j: the byte value to be used for "junk" (unused) input/outputs + * @dataval: result vector + */ +static int read_tp_ec_row(u8 arg0, int bat, u8 j, u8 *dataval) +{ + int ret; + const struct thinkpad_ec_row args = { .mask = 0xFFFF, + .val = {arg0, j,j,j,j,j,j,j,j,j,j,j,j,j,j, (u8)bat} }; + struct thinkpad_ec_row data = { .mask = 0xFFFF }; + + ret = thinkpad_ec_lock(); + if (ret) + return ret; + ret = thinkpad_ec_read_row(&args, &data); + thinkpad_ec_unlock(); + memcpy(dataval, &data.val, TP_CONTROLLER_ROW_LEN); + return ret; +} + +/** + * power_device_present - check for presence of battery or AC power + * @bat: 0 for battery 0, 1 for battery 1, otherwise AC power + * Returns 1 if present, 0 if not present, negative if error. + */ +static int power_device_present(int bat) +{ + u8 row[TP_CONTROLLER_ROW_LEN]; + u8 test; + int ret = read_tp_ec_row(1, bat, 0, row); + if (ret) + return ret; + switch (bat) { + case 0: test = 0x40; break; /* battery 0 */ + case 1: test = 0x20; break; /* battery 1 */ + default: test = 0x80; /* AC power */ + } + return (row[0] & test) ? 1 : 0; +} + +/** + * bat_has_status - check if battery can report detailed status + * @bat: 0 for battery 0, 1 for battery 1 + * Returns 1 if yes, 0 if no, negative if error. + */ +static int bat_has_status(int bat) +{ + u8 row[TP_CONTROLLER_ROW_LEN]; + int ret = read_tp_ec_row(1, bat, 0, row); + if (ret) + return ret; + if ((row[0] & (bat?0x20:0x40)) == 0) /* no battery */ + return 0; + if ((row[1] & (0x60)) == 0) /* no status */ + return 0; + return 1; +} + +/** + * get_tp_ec_bat_16 - read a 16-bit value from EC battery status data + * @arg0: first argument to EC + * @off: offset in row returned from EC + * @bat: battery (0 or 1) + * @val: the 16-bit value obtained + * Returns nonzero on error. + */ +static int get_tp_ec_bat_16(u8 arg0, int offset, int bat, u16 *val) +{ + u8 row[TP_CONTROLLER_ROW_LEN]; + int ret; + if (bat_has_status(bat) != 1) + return -ENXIO; + ret = read_tp_ec_row(arg0, bat, 0, row); + if (ret) + return ret; + *val = *(u16 *)(row+offset); + return 0; +} + +/********************************************************************* + * sysfs attributes for batteries - + * definitions and helper functions + */ + +/* A custom device attribute struct which holds a battery number */ +struct bat_device_attribute { + struct device_attribute dev_attr; + int bat; +}; + +/** + * attr_get_bat - get the battery to which the attribute belongs + */ +static int attr_get_bat(struct device_attribute *attr) +{ + return container_of(attr, struct bat_device_attribute, dev_attr)->bat; +} + +/** + * show_tp_ec_bat_u16 - show an unsigned 16-bit battery attribute + * @arg0: specified 1st argument of EC raw to read + * @offset: byte offset in EC raw data + * @mul: correction factor to multiply by + * @na_msg: string to output is value not available (0xFFFFFFFF) + * @attr: battery attribute + * @buf: output buffer + * The 16-bit value is read from the EC, treated as unsigned, + * transformed as x->mul*x, and printed to the buffer. + * If the value is 0xFFFFFFFF and na_msg!=%NULL, na_msg is printed instead. + */ +static ssize_t show_tp_ec_bat_u16(u8 arg0, int offset, int mul, + const char *na_msg, + struct device_attribute *attr, char *buf) +{ + u16 val; + int ret = get_tp_ec_bat_16(arg0, offset, attr_get_bat(attr), &val); + if (ret) + return ret; + if (na_msg && val == 0xFFFF) + return sprintf(buf, "%s\n", na_msg); + else + return sprintf(buf, "%u\n", mul*(unsigned int)val); +} + +/** + * show_tp_ec_bat_s16 - show an signed 16-bit battery attribute + * @arg0: specified 1st argument of EC raw to read + * @offset: byte offset in EC raw data + * @mul: correction factor to multiply by + * @add: correction term to add after multiplication + * @attr: battery attribute + * @buf: output buffer + * The 16-bit value is read from the EC, treated as signed, + * transformed as x->mul*x+add, and printed to the buffer. + */ +static ssize_t show_tp_ec_bat_s16(u8 arg0, int offset, int mul, int add, + struct device_attribute *attr, char *buf) +{ + u16 val; + int ret = get_tp_ec_bat_16(arg0, offset, attr_get_bat(attr), &val); + if (ret) + return ret; + return sprintf(buf, "%d\n", mul*(s16)val+add); +} + +/** + * show_tp_ec_bat_str - show a string from EC battery status data + * @arg0: specified 1st argument of EC raw to read + * @offset: byte offset in EC raw data + * @maxlen: maximum string length + * @attr: battery attribute + * @buf: output buffer + */ +static ssize_t show_tp_ec_bat_str(u8 arg0, int offset, int maxlen, + struct device_attribute *attr, char *buf) +{ + int bat = attr_get_bat(attr); + u8 row[TP_CONTROLLER_ROW_LEN]; + int ret; + if (bat_has_status(bat) != 1) + return -ENXIO; + ret = read_tp_ec_row(arg0, bat, 0, row); + if (ret) + return ret; + strncpy(buf, (char *)row+offset, maxlen); + buf[maxlen] = 0; + strcat(buf, "\n"); + return strlen(buf); +} + +/** + * show_tp_ec_bat_power - show a power readout from EC battery status data + * @arg0: specified 1st argument of EC raw to read + * @offV: byte offset of voltage in EC raw data + * @offI: byte offset of current in EC raw data + * @attr: battery attribute + * @buf: output buffer + * Computes the power as current*voltage from the two given readout offsets. + */ +static ssize_t show_tp_ec_bat_power(u8 arg0, int offV, int offI, + struct device_attribute *attr, char *buf) +{ + u8 row[TP_CONTROLLER_ROW_LEN]; + int milliamp, millivolt, ret; + int bat = attr_get_bat(attr); + if (bat_has_status(bat) != 1) + return -ENXIO; + ret = read_tp_ec_row(1, bat, 0, row); + if (ret) + return ret; + millivolt = *(u16 *)(row+offV); + milliamp = *(s16 *)(row+offI); + return sprintf(buf, "%d\n", milliamp*millivolt/1000); /* units: mW */ +} + +/** + * show_tp_ec_bat_date - decode and show a date from EC battery status data + * @arg0: specified 1st argument of EC raw to read + * @offset: byte offset in EC raw data + * @attr: battery attribute + * @buf: output buffer + */ +static ssize_t show_tp_ec_bat_date(u8 arg0, int offset, + struct device_attribute *attr, char *buf) +{ + u8 row[TP_CONTROLLER_ROW_LEN]; + u16 v; + int ret; + int day, month, year; + int bat = attr_get_bat(attr); + if (bat_has_status(bat) != 1) + return -ENXIO; + ret = read_tp_ec_row(arg0, bat, 0, row); + if (ret) + return ret; + + /* Decode bit-packed: v = day | (month<<5) | ((year-1980)<<9) */ + v = *(u16 *)(row+offset); + day = v & 0x1F; + month = (v >> 5) & 0xF; + year = (v >> 9) + 1980; + + return sprintf(buf, "%04d-%02d-%02d\n", year, month, day); +} + + +/********************************************************************* + * sysfs attribute I/O for batteries - + * the actual attribute show/store functions + */ + +static ssize_t show_battery_start_charge_thresh(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int thresh; + int bat = attr_get_bat(attr); + int ret = get_thresh(bat, THRESH_START, &thresh); + if (ret) + return ret; + return sprintf(buf, "%d\n", thresh); /* units: percent */ +} + +static ssize_t show_battery_stop_charge_thresh(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int thresh; + int bat = attr_get_bat(attr); + int ret = get_thresh(bat, THRESH_STOP, &thresh); + if (ret) + return ret; + return sprintf(buf, "%d\n", thresh); /* units: percent */ +} + +/** + * store_battery_start_charge_thresh - store battery_start_charge_thresh attr + * Since this is a kernel<->user interface, we ensure a valid state for + * the hardware. We do this by clamping the requested threshold to the + * valid range and, if necessary, moving the other threshold so that + * it's MIN_THRESH_DELTA away from this one. + */ +static ssize_t store_battery_start_charge_thresh(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int thresh, other_thresh, ret; + int bat = attr_get_bat(attr); + + if (sscanf(buf, "%d", &thresh) != 1 || thresh < 1 || thresh > 100) + return -EINVAL; + + if (thresh < MIN_THRESH_START) /* clamp up to MIN_THRESH_START */ + thresh = MIN_THRESH_START; + if (thresh > MAX_THRESH_START) /* clamp down to MAX_THRESH_START */ + thresh = MAX_THRESH_START; + + down(&smapi_mutex); + ret = get_thresh(bat, THRESH_STOP, &other_thresh); + if (ret != -EOPNOTSUPP && ret != -ENXIO) { + if (ret) /* other threshold is set? */ + goto out; + ret = get_real_thresh(bat, THRESH_START, NULL); + if (ret) /* this threshold is set? */ + goto out; + if (other_thresh < thresh+MIN_THRESH_DELTA) { + /* move other thresh to keep it above this one */ + ret = set_thresh(bat, THRESH_STOP, + thresh+MIN_THRESH_DELTA); + if (ret) + goto out; + } + } + ret = set_thresh(bat, THRESH_START, thresh); +out: + up(&smapi_mutex); + return count; + +} + +/** + * store_battery_stop_charge_thresh - store battery_stop_charge_thresh attr + * Since this is a kernel<->user interface, we ensure a valid state for + * the hardware. We do this by clamping the requested threshold to the + * valid range and, if necessary, moving the other threshold so that + * it's MIN_THRESH_DELTA away from this one. + */ +static ssize_t store_battery_stop_charge_thresh(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int thresh, other_thresh, ret; + int bat = attr_get_bat(attr); + + if (sscanf(buf, "%d", &thresh) != 1 || thresh < 1 || thresh > 100) + return -EINVAL; + + if (thresh < MIN_THRESH_STOP) /* clamp up to MIN_THRESH_STOP */ + thresh = MIN_THRESH_STOP; + + down(&smapi_mutex); + ret = get_thresh(bat, THRESH_START, &other_thresh); + if (ret != -EOPNOTSUPP && ret != -ENXIO) { /* other threshold exists? */ + if (ret) + goto out; + /* this threshold exists? */ + ret = get_real_thresh(bat, THRESH_STOP, NULL); + if (ret) + goto out; + if (other_thresh >= thresh-MIN_THRESH_DELTA) { + /* move other thresh to be below this one */ + ret = set_thresh(bat, THRESH_START, + thresh-MIN_THRESH_DELTA); + if (ret) + goto out; + } + } + ret = set_thresh(bat, THRESH_STOP, thresh); +out: + up(&smapi_mutex); + return count; +} + +static ssize_t show_battery_inhibit_charge_minutes(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int minutes; + int bat = attr_get_bat(attr); + int ret = get_inhibit_charge_minutes(bat, &minutes); + if (ret) + return ret; + return sprintf(buf, "%d\n", minutes); /* units: minutes */ +} + +static ssize_t store_battery_inhibit_charge_minutes(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int minutes; + int bat = attr_get_bat(attr); + if (sscanf(buf, "%d", &minutes) != 1 || minutes < 0) { + TPRINTK(KERN_ERR, "inhibit_charge_minutes: " + "must be a non-negative integer"); + return -EINVAL; + } + ret = set_inhibit_charge_minutes(bat, minutes); + if (ret) + return ret; + return count; +} + +static ssize_t show_battery_force_discharge(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int enabled; + int bat = attr_get_bat(attr); + int ret = get_force_discharge(bat, &enabled); + if (ret) + return ret; + return sprintf(buf, "%d\n", enabled); /* type: boolean */ +} + +static ssize_t store_battery_force_discharge(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + int enabled; + int bat = attr_get_bat(attr); + if (sscanf(buf, "%d", &enabled) != 1 || enabled < 0 || enabled > 1) + return -EINVAL; + ret = set_force_discharge(bat, enabled); + if (ret) + return ret; + return count; +} + +static ssize_t show_battery_installed( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int bat = attr_get_bat(attr); + int ret = power_device_present(bat); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", ret); /* type: boolean */ +} + +static ssize_t show_battery_state( + struct device *dev, struct device_attribute *attr, char *buf) +{ + u8 row[TP_CONTROLLER_ROW_LEN]; + const char *txt; + int ret; + int bat = attr_get_bat(attr); + if (bat_has_status(bat) != 1) + return sprintf(buf, "none\n"); + ret = read_tp_ec_row(1, bat, 0, row); + if (ret) + return ret; + switch (row[1] & 0xf0) { + case 0xc0: txt = "idle"; break; + case 0xd0: txt = "discharging"; break; + case 0xe0: txt = "charging"; break; + default: return sprintf(buf, "unknown (0x%x)\n", row[1]); + } + return sprintf(buf, "%s\n", txt); /* type: string from fixed set */ +} + +static ssize_t show_battery_manufacturer( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* type: string. SBS spec v1.1 p34: ManufacturerName() */ + return show_tp_ec_bat_str(4, 2, TP_CONTROLLER_ROW_LEN-2, attr, buf); +} + +static ssize_t show_battery_model( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* type: string. SBS spec v1.1 p34: DeviceName() */ + return show_tp_ec_bat_str(5, 2, TP_CONTROLLER_ROW_LEN-2, attr, buf); +} + +static ssize_t show_battery_barcoding( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* type: string */ + return show_tp_ec_bat_str(7, 2, TP_CONTROLLER_ROW_LEN-2, attr, buf); +} + +static ssize_t show_battery_chemistry( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* type: string. SBS spec v1.1 p34-35: DeviceChemistry() */ + return show_tp_ec_bat_str(6, 2, 5, attr, buf); +} + +static ssize_t show_battery_voltage( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mV. SBS spec v1.1 p24: Voltage() */ + return show_tp_ec_bat_u16(1, 6, 1, NULL, attr, buf); +} + +static ssize_t show_battery_design_voltage( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mV. SBS spec v1.1 p32: DesignVoltage() */ + return show_tp_ec_bat_u16(3, 4, 1, NULL, attr, buf); +} + +static ssize_t show_battery_charging_max_voltage( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mV. SBS spec v1.1 p37,39: ChargingVoltage() */ + return show_tp_ec_bat_u16(9, 8, 1, NULL, attr, buf); +} + +static ssize_t show_battery_group0_voltage( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mV */ + return show_tp_ec_bat_u16(0xA, 12, 1, NULL, attr, buf); +} + +static ssize_t show_battery_group1_voltage( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mV */ + return show_tp_ec_bat_u16(0xA, 10, 1, NULL, attr, buf); +} + +static ssize_t show_battery_group2_voltage( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mV */ + return show_tp_ec_bat_u16(0xA, 8, 1, NULL, attr, buf); +} + +static ssize_t show_battery_group3_voltage( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mV */ + return show_tp_ec_bat_u16(0xA, 6, 1, NULL, attr, buf); +} + +static ssize_t show_battery_current_now( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mA. SBS spec v1.1 p24: Current() */ + return show_tp_ec_bat_s16(1, 8, 1, 0, attr, buf); +} + +static ssize_t show_battery_current_avg( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mA. SBS spec v1.1 p24: AverageCurrent() */ + return show_tp_ec_bat_s16(1, 10, 1, 0, attr, buf); +} + +static ssize_t show_battery_charging_max_current( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mA. SBS spec v1.1 p36,38: ChargingCurrent() */ + return show_tp_ec_bat_s16(9, 6, 1, 0, attr, buf); +} + +static ssize_t show_battery_power_now( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mW. SBS spec v1.1: Voltage()*Current() */ + return show_tp_ec_bat_power(1, 6, 8, attr, buf); +} + +static ssize_t show_battery_power_avg( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mW. SBS spec v1.1: Voltage()*AverageCurrent() */ + return show_tp_ec_bat_power(1, 6, 10, attr, buf); +} + +static ssize_t show_battery_remaining_percent( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: percent. SBS spec v1.1 p25: RelativeStateOfCharge() */ + return show_tp_ec_bat_u16(1, 12, 1, NULL, attr, buf); +} + +static ssize_t show_battery_remaining_percent_error( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: percent. SBS spec v1.1 p25: MaxError() */ + return show_tp_ec_bat_u16(9, 4, 1, NULL, attr, buf); +} + +static ssize_t show_battery_remaining_charging_time( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: minutes. SBS spec v1.1 p27: AverageTimeToFull() */ + return show_tp_ec_bat_u16(2, 8, 1, "not_charging", attr, buf); +} + +static ssize_t show_battery_remaining_running_time( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: minutes. SBS spec v1.1 p27: RunTimeToEmpty() */ + return show_tp_ec_bat_u16(2, 6, 1, "not_discharging", attr, buf); +} + +static ssize_t show_battery_remaining_running_time_now( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: minutes. SBS spec v1.1 p27: RunTimeToEmpty() */ + return show_tp_ec_bat_u16(2, 4, 1, "not_discharging", attr, buf); +} + +static ssize_t show_battery_remaining_capacity( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mWh. SBS spec v1.1 p26. */ + return show_tp_ec_bat_u16(1, 14, 10, "", attr, buf); +} + +static ssize_t show_battery_last_full_capacity( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mWh. SBS spec v1.1 p26: FullChargeCapacity() */ + return show_tp_ec_bat_u16(2, 2, 10, "", attr, buf); +} + +static ssize_t show_battery_design_capacity( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: mWh. SBS spec v1.1 p32: DesignCapacity() */ + return show_tp_ec_bat_u16(3, 2, 10, "", attr, buf); +} + +static ssize_t show_battery_cycle_count( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: ordinal. SBS spec v1.1 p32: CycleCount() */ + return show_tp_ec_bat_u16(2, 12, 1, "", attr, buf); +} + +static ssize_t show_battery_temperature( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* units: millicelsius. SBS spec v1.1: Temperature()*10 */ + return show_tp_ec_bat_s16(1, 4, 100, -273100, attr, buf); +} + +static ssize_t show_battery_serial( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* type: int. SBS spec v1.1 p34: SerialNumber() */ + return show_tp_ec_bat_u16(3, 10, 1, "", attr, buf); +} + +static ssize_t show_battery_manufacture_date( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* type: YYYY-MM-DD. SBS spec v1.1 p34: ManufactureDate() */ + return show_tp_ec_bat_date(3, 8, attr, buf); +} + +static ssize_t show_battery_first_use_date( + struct device *dev, struct device_attribute *attr, char *buf) +{ + /* type: YYYY-MM-DD */ + return show_tp_ec_bat_date(8, 2, attr, buf); +} + +/** + * show_battery_dump - show the battery's dump attribute + * The dump attribute gives a hex dump of all EC readouts related to a + * battery. Some of the enumerated values don't really exist (i.e., the + * EC function just leaves them untouched); we use a kludge to detect and + * denote these. + */ +#define MIN_DUMP_ARG0 0x00 +#define MAX_DUMP_ARG0 0x0a /* 0x0b is useful too but hangs old EC firmware */ +static ssize_t show_battery_dump( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int i; + char *p = buf; + int bat = attr_get_bat(attr); + u8 arg0; /* first argument to EC */ + u8 rowa[TP_CONTROLLER_ROW_LEN], + rowb[TP_CONTROLLER_ROW_LEN]; + const u8 junka = 0xAA, + junkb = 0x55; /* junk values for testing changes */ + int ret; + + for (arg0 = MIN_DUMP_ARG0; arg0 <= MAX_DUMP_ARG0; ++arg0) { + if ((p-buf) > PAGE_SIZE-TP_CONTROLLER_ROW_LEN*5) + return -ENOMEM; /* don't overflow sysfs buf */ + /* Read raw twice with different junk values, + * to detect unused output bytes which are left unchaged: */ + ret = read_tp_ec_row(arg0, bat, junka, rowa); + if (ret) + return ret; + ret = read_tp_ec_row(arg0, bat, junkb, rowb); + if (ret) + return ret; + for (i = 0; i < TP_CONTROLLER_ROW_LEN; i++) { + if (rowa[i] == junka && rowb[i] == junkb) + p += sprintf(p, "-- "); /* unused by EC */ + else + p += sprintf(p, "%02x ", rowa[i]); + } + p += sprintf(p, "\n"); + } + return p-buf; +} + + +/********************************************************************* + * sysfs attribute I/O, other than batteries + */ + +static ssize_t show_ac_connected( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret = power_device_present(0xFF); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", ret); /* type: boolean */ +} + +/********************************************************************* + * The the "smapi_request" sysfs attribute executes a raw SMAPI call. + * You write to make a request and read to get the result. The state + * is saved globally rather than per fd (sysfs limitation), so + * simultaenous requests may get each other's results! So this is for + * development and debugging only. + */ +#define MAX_SMAPI_ATTR_ANSWER_LEN 128 +static char smapi_attr_answer[MAX_SMAPI_ATTR_ANSWER_LEN] = ""; + +static ssize_t show_smapi_request(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = snprintf(buf, PAGE_SIZE, "%s", smapi_attr_answer); + smapi_attr_answer[0] = '\0'; + return ret; +} + +static ssize_t store_smapi_request(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int inEBX, inECX, inEDI, inESI; + u32 outEBX, outECX, outEDX, outEDI, outESI; + const char *msg; + int ret; + if (sscanf(buf, "%x %x %x %x", &inEBX, &inECX, &inEDI, &inESI) != 4) { + smapi_attr_answer[0] = '\0'; + return -EINVAL; + } + ret = smapi_request( + inEBX, inECX, inEDI, inESI, + &outEBX, &outECX, &outEDX, &outEDI, &outESI, &msg); + snprintf(smapi_attr_answer, MAX_SMAPI_ATTR_ANSWER_LEN, + "%x %x %x %x %x %d '%s'\n", + (unsigned int)outEBX, (unsigned int)outECX, + (unsigned int)outEDX, (unsigned int)outEDI, + (unsigned int)outESI, ret, msg); + if (ret) + return ret; + else + return count; +} + +/********************************************************************* + * Power management: the embedded controller forgets the battery + * thresholds when the system is suspended to disk and unplugged from + * AC and battery, so we restore it upon resume. + */ + +static int saved_threshs[4] = {-1, -1, -1, -1}; /* -1 = don't know */ + +static int tp_suspend(struct platform_device *dev, pm_message_t state) +{ + int restore = (state.event == PM_EVENT_HIBERNATE || + state.event == PM_EVENT_FREEZE); + if (!restore || get_real_thresh(0, THRESH_STOP , &saved_threshs[0])) + saved_threshs[0] = -1; + if (!restore || get_real_thresh(0, THRESH_START, &saved_threshs[1])) + saved_threshs[1] = -1; + if (!restore || get_real_thresh(1, THRESH_STOP , &saved_threshs[2])) + saved_threshs[2] = -1; + if (!restore || get_real_thresh(1, THRESH_START, &saved_threshs[3])) + saved_threshs[3] = -1; + DPRINTK("suspend saved: %d %d %d %d", saved_threshs[0], + saved_threshs[1], saved_threshs[2], saved_threshs[3]); + return 0; +} + +static int tp_resume(struct platform_device *dev) +{ + DPRINTK("resume restoring: %d %d %d %d", saved_threshs[0], + saved_threshs[1], saved_threshs[2], saved_threshs[3]); + if (saved_threshs[0] >= 0) + set_real_thresh(0, THRESH_STOP , saved_threshs[0]); + if (saved_threshs[1] >= 0) + set_real_thresh(0, THRESH_START, saved_threshs[1]); + if (saved_threshs[2] >= 0) + set_real_thresh(1, THRESH_STOP , saved_threshs[2]); + if (saved_threshs[3] >= 0) + set_real_thresh(1, THRESH_START, saved_threshs[3]); + return 0; +} + + +/********************************************************************* + * Driver model + */ + +static struct platform_driver tp_driver = { + .suspend = tp_suspend, + .resume = tp_resume, + .driver = { + .name = "smapi", + .owner = THIS_MODULE + }, +}; + + +/********************************************************************* + * Sysfs device model + */ + +/* Attributes in /sys/devices/platform/smapi/ */ + +static DEVICE_ATTR(ac_connected, 0444, show_ac_connected, NULL); +static DEVICE_ATTR(smapi_request, 0600, show_smapi_request, + store_smapi_request); + +static struct attribute *tp_root_attributes[] = { + &dev_attr_ac_connected.attr, + &dev_attr_smapi_request.attr, + NULL +}; +static struct attribute_group tp_root_attribute_group = { + .attrs = tp_root_attributes +}; + +/* Attributes under /sys/devices/platform/smapi/BAT{0,1}/ : + * Every attribute needs to be defined (i.e., statically allocated) for + * each battery, and then referenced in the attribute list of each battery. + * We use preprocessor voodoo to avoid duplicating the list of attributes 4 + * times. The preprocessor output is just normal sysfs attributes code. + */ + +/** + * FOREACH_BAT_ATTR - invoke the given macros on all our battery attributes + * @_BAT: battery number (0 or 1) + * @_ATTR_RW: macro to invoke for each read/write attribute + * @_ATTR_R: macro to invoke for each read-only attribute + */ +#define FOREACH_BAT_ATTR(_BAT, _ATTR_RW, _ATTR_R) \ + _ATTR_RW(_BAT, start_charge_thresh) \ + _ATTR_RW(_BAT, stop_charge_thresh) \ + _ATTR_RW(_BAT, inhibit_charge_minutes) \ + _ATTR_RW(_BAT, force_discharge) \ + _ATTR_R(_BAT, installed) \ + _ATTR_R(_BAT, state) \ + _ATTR_R(_BAT, manufacturer) \ + _ATTR_R(_BAT, model) \ + _ATTR_R(_BAT, barcoding) \ + _ATTR_R(_BAT, chemistry) \ + _ATTR_R(_BAT, voltage) \ + _ATTR_R(_BAT, group0_voltage) \ + _ATTR_R(_BAT, group1_voltage) \ + _ATTR_R(_BAT, group2_voltage) \ + _ATTR_R(_BAT, group3_voltage) \ + _ATTR_R(_BAT, current_now) \ + _ATTR_R(_BAT, current_avg) \ + _ATTR_R(_BAT, charging_max_current) \ + _ATTR_R(_BAT, power_now) \ + _ATTR_R(_BAT, power_avg) \ + _ATTR_R(_BAT, remaining_percent) \ + _ATTR_R(_BAT, remaining_percent_error) \ + _ATTR_R(_BAT, remaining_charging_time) \ + _ATTR_R(_BAT, remaining_running_time) \ + _ATTR_R(_BAT, remaining_running_time_now) \ + _ATTR_R(_BAT, remaining_capacity) \ + _ATTR_R(_BAT, last_full_capacity) \ + _ATTR_R(_BAT, design_voltage) \ + _ATTR_R(_BAT, charging_max_voltage) \ + _ATTR_R(_BAT, design_capacity) \ + _ATTR_R(_BAT, cycle_count) \ + _ATTR_R(_BAT, temperature) \ + _ATTR_R(_BAT, serial) \ + _ATTR_R(_BAT, manufacture_date) \ + _ATTR_R(_BAT, first_use_date) \ + _ATTR_R(_BAT, dump) + +/* Define several macros we will feed into FOREACH_BAT_ATTR: */ + +#define DEFINE_BAT_ATTR_RW(_BAT,_NAME) \ + static struct bat_device_attribute dev_attr_##_NAME##_##_BAT = { \ + .dev_attr = __ATTR(_NAME, 0644, show_battery_##_NAME, \ + store_battery_##_NAME), \ + .bat = _BAT \ + }; + +#define DEFINE_BAT_ATTR_R(_BAT,_NAME) \ + static struct bat_device_attribute dev_attr_##_NAME##_##_BAT = { \ + .dev_attr = __ATTR(_NAME, 0644, show_battery_##_NAME, 0), \ + .bat = _BAT \ + }; + +#define REF_BAT_ATTR(_BAT,_NAME) \ + &dev_attr_##_NAME##_##_BAT.dev_attr.attr, + +/* This provide all attributes for one battery: */ + +#define PROVIDE_BAT_ATTRS(_BAT) \ + FOREACH_BAT_ATTR(_BAT, DEFINE_BAT_ATTR_RW, DEFINE_BAT_ATTR_R) \ + static struct attribute *tp_bat##_BAT##_attributes[] = { \ + FOREACH_BAT_ATTR(_BAT, REF_BAT_ATTR, REF_BAT_ATTR) \ + NULL \ + }; \ + static struct attribute_group tp_bat##_BAT##_attribute_group = { \ + .name = "BAT" #_BAT, \ + .attrs = tp_bat##_BAT##_attributes \ + }; + +/* Finally genereate the attributes: */ + +PROVIDE_BAT_ATTRS(0) +PROVIDE_BAT_ATTRS(1) + +/* List of attribute groups */ + +static struct attribute_group *attr_groups[] = { + &tp_root_attribute_group, + &tp_bat0_attribute_group, + &tp_bat1_attribute_group, + NULL +}; + + +/********************************************************************* + * Init and cleanup + */ + +static struct attribute_group **next_attr_group; /* next to register */ + +static int __init tp_init(void) +{ + int ret; + printk(KERN_INFO "tp_smapi " TP_VERSION " loading...\n"); + + ret = find_smapi_port(); + if (ret < 0) + goto err; + else + smapi_port = ret; + + if (!request_region(smapi_port, 1, "smapi")) { + printk(KERN_ERR "tp_smapi cannot claim port 0x%x\n", + smapi_port); + ret = -ENXIO; + goto err; + } + + if (!request_region(SMAPI_PORT2, 1, "smapi")) { + printk(KERN_ERR "tp_smapi cannot claim port 0x%x\n", + SMAPI_PORT2); + ret = -ENXIO; + goto err_port1; + } + + ret = platform_driver_register(&tp_driver); + if (ret) + goto err_port2; + + pdev = platform_device_alloc("smapi", -1); + if (!pdev) { + ret = -ENOMEM; + goto err_driver; + } + + ret = platform_device_add(pdev); + if (ret) + goto err_device_free; + + for (next_attr_group = attr_groups; *next_attr_group; + ++next_attr_group) { + ret = sysfs_create_group(&pdev->dev.kobj, *next_attr_group); + if (ret) + goto err_attr; + } + + printk(KERN_INFO "tp_smapi successfully loaded (smapi_port=0x%x).\n", + smapi_port); + return 0; + +err_attr: + while (--next_attr_group >= attr_groups) + sysfs_remove_group(&pdev->dev.kobj, *next_attr_group); + platform_device_unregister(pdev); +err_device_free: + platform_device_put(pdev); +err_driver: + platform_driver_unregister(&tp_driver); +err_port2: + release_region(SMAPI_PORT2, 1); +err_port1: + release_region(smapi_port, 1); +err: + printk(KERN_ERR "tp_smapi init failed (ret=%d)!\n", ret); + return ret; +} + +static void __exit tp_exit(void) +{ + while (next_attr_group && --next_attr_group >= attr_groups) + sysfs_remove_group(&pdev->dev.kobj, *next_attr_group); + platform_device_unregister(pdev); + platform_driver_unregister(&tp_driver); + release_region(SMAPI_PORT2, 1); + if (smapi_port) + release_region(smapi_port, 1); + + printk(KERN_INFO "tp_smapi unloaded.\n"); +} + +module_init(tp_init); +module_exit(tp_exit); diff --git a/ueventd.x86.rc b/ueventd.x86.rc new file mode 100644 index 0000000..9c0f1a1 --- /dev/null +++ b/ueventd.x86.rc @@ -0,0 +1,9 @@ +/dev/rfkill 0660 bluetooth bluetooth +/dev/ttyS* 0660 system radio +/dev/ttyACM* 0660 system radio +/dev/ttyUSB* 0660 radio radio +/dev/video* 0660 media system +/dev/iio:device* 0660 system system + +# for bluetooth nodes +/dev/uhid 0660 bluetooth bluetooth diff --git a/wacom/Android.mk b/wacom/Android.mk new file mode 100644 index 0000000..577792d --- /dev/null +++ b/wacom/Android.mk @@ -0,0 +1,24 @@ +# Copyright 2011 The Android-x86 Open Source Project + +#ifeq ($(BOARD_USES_WACOMINPUT),true) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= wactablet.c wacserial.c wacusb.c wacom-input.c + +LOCAL_C_INCLUDES := $(KERNEL_HEADERS) + +LOCAL_CFLAGS := -O2 -Wall -Wno-unused-parameter + +ifeq ($(TARGET_ARCH),x86) +LOCAL_CFLAGS += -Ulinux +endif + +LOCAL_MODULE := wacom-input +LOCAL_MODULE_TAGS := optional +#LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) + +include $(BUILD_EXECUTABLE) + +#endif diff --git a/wacom/MODULE_LICENSE_GPL b/wacom/MODULE_LICENSE_GPL new file mode 100644 index 0000000..e69de29 diff --git a/wacom/Makefile b/wacom/Makefile new file mode 100644 index 0000000..f3db78f --- /dev/null +++ b/wacom/Makefile @@ -0,0 +1,17 @@ +CC=gcc +CFLAGS=-c -Wall -O2 +#LDFLAGS=-lncurses +SOURCES=wactablet.c wacserial.c wacusb.c wacom-input.c +OBJECTS=$(SOURCES:.c=.o) +EXECUTABLE=wacom-input + +all: $(SOURCES) $(EXECUTABLE) + +$(EXECUTABLE): $(OBJECTS) + $(CC) $(LDFLAGS) $(OBJECTS) -o $@ + +.c.o: + $(CC) $(CFLAGS) $< -o $@ + +clean: + rm $(OBJECTS) $(EXECUTABLE) diff --git a/wacom/wacom-input.c b/wacom/wacom-input.c new file mode 100644 index 0000000..d486a8a --- /dev/null +++ b/wacom/wacom-input.c @@ -0,0 +1,241 @@ +/***************************************************************************** + ** wacom-input.c + ** + ** Copyright (C) 2011 Stefan Seidel + ** + ** This program is free software; you can redistribute it and/or + ** modify it under the terms of the GNU General Public License + ** as published by the Free Software Foundation; either version 2 + ** of the License, or (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program; if not, write to the Free Software + ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + ** + ** + ** Code inspired by wacdump.c from http://linuxwacom.sourceforge.net and + ** uniput-sample.c from http://thiemonge.org/getting-started-with-uinput + ** + ** + ** Version history: + ** 0.1 - 2011-03-29 - initial support for "tpc" device + ** 0.2 - 2011-04-13 - support command-line options for device type, node, pressure + ** + ****************************************************************************/ + +#include "wactablet.h" +#include "wacserial.h" +#include "wacusb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define die(str, args...) do { \ + perror(str, ## args); \ + exit(EXIT_FAILURE); \ + } while(0) + +int fd; +WACOMENGINE hEngine = NULL; +WACOMTABLET hTablet = NULL; + +void wacom_report_event(__u16 type, __u16 code, __s32 value) { + struct input_event ev; + if (value == -1) { + return; + } + memset(&ev, 0, sizeof(struct input_event)); + ev.type = type; + ev.code = code; + ev.value = value; + if (write(fd, &ev, sizeof(struct input_event)) < 0) + perror("error: write"); +} + +static void signal_handler(int signo) { + if (ioctl(fd, UI_DEV_DESTROY) < 0) { + die("error: cannot destroy uinput device\n"); + } + close(fd); + WacomCloseTablet(hTablet); + WacomTermEngine(hEngine); + exit(EXIT_SUCCESS); +} + +int main(int argc, char** argv) { + const char* arg; + const char* devName = "tpc"; + const char* devNode = "/dev/ttyS0"; + struct uinput_user_dev uidev; + WACOMSTATE state = WACOMSTATE_INIT; + WACOMMODEL model = { 0 }; + unsigned char uchBuf[64]; + int nLength = 0; + int minPress = 20; + + // parse arguments + while (*argv) { + arg = *(argv++); + + /* handle options */ + if (arg[0] == '-') { + /* device type */ + if (strcmp(arg, "-h") == 0) { + fprintf( + stderr, + "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"); + exit(0); + /* device type */ + } else if (strcmp(arg, "-t") == 0) { + arg = *(argv++); + if (arg == NULL || arg[0] == '-') { + die("Missing device type"); + } + devName = arg; + /* device node */ + } else if (strcmp(arg, "-d") == 0) { + arg = *(argv++); + if (arg == NULL || arg[0] == '-') { + die("Missing device node"); + } + devNode = arg; + /* pressure */ + } else if (strcmp(arg, "-p") == 0) { + arg = *(argv++); + if (arg == NULL || (minPress = atoi(arg)) == 0) { + die("Wrong pressure threshold"); + } + } + } + } + // end parse arguments + + if (signal(SIGINT, signal_handler) == SIG_ERR) { + die("error registering signal handler\n"); + } + + // connect to wacom device + hEngine = WacomInitEngine(); + if (!hEngine) { + close(fd); + die("failed to open tablet engine"); + } + + /* open tablet */ + model.uClass = WACOMCLASS_SERIAL; + model.uDevice = WacomGetDeviceFromName(devName, model.uClass); + hTablet = WacomOpenTablet(hEngine, devNode, &model); + if (!hTablet) { + close(fd); + WacomTermEngine(hEngine); + die ("WacomOpenTablet"); + } + WacomGetState(hTablet, &state); + // wacom device is set up properly + + // set up uinput + fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + if (fd < 0) { + fd = open("/dev/input/uinput", O_WRONLY | O_NONBLOCK); + } + + if (fd < 0) + die("error: opening /dev/[input/]uinput failed"); + + // report that we have TOUCH events ... + if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0) + die("error: ioctl"); + if (ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH) < 0) + die("error: ioctl"); + + // and absolute x, y, pressure data + if (ioctl(fd, UI_SET_EVBIT, EV_ABS) < 0) + die("error: ioctl"); + if (ioctl(fd, UI_SET_ABSBIT, ABS_X) < 0) + die("error: ioctl"); + if (ioctl(fd, UI_SET_ABSBIT, ABS_Y) < 0) + die("error: ioctl"); + if (ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE) < 0) + die("error: ioctl"); + + // this is for simulated mouse middle/right button + // if(ioctl(fd, UI_SET_KEYBIT, BTN_MOUSE) < 0) + // die("error: ioctl"); + // if(ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT) < 0) + // die("error: ioctl"); + // if(ioctl(fd, UI_SET_KEYBIT, BTN_LEFT) < 0) + // die("error: ioctl"); + + // register uinput device + memset(&uidev, 0, sizeof(uidev)); + uidev.absmin[ABS_X] = state.values[WACOMFIELD_POSITION_X].nMin; + uidev.absmax[ABS_X] = state.values[WACOMFIELD_POSITION_X].nMax; + uidev.absmin[ABS_Y] = state.values[WACOMFIELD_POSITION_Y].nMin; + uidev.absmax[ABS_Y] = state.values[WACOMFIELD_POSITION_Y].nMax; + uidev.absmin[ABS_PRESSURE] = state.values[WACOMFIELD_PRESSURE].nMin; + uidev.absmax[ABS_PRESSURE] = state.values[WACOMFIELD_PRESSURE].nMax; + // this could be more detailed, but in the end, who cares? + snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "wacom-input"); + uidev.id.bustype = BUS_RS232; + uidev.id.vendor = 0x056a; + uidev.id.product = 0xffff; + uidev.id.version = 1; + + if (write(fd, &uidev, sizeof(uidev)) < 0) + die("error: set virtual device info 1"); + + if (ioctl(fd, UI_DEV_CREATE) < 0) + die("error: create uinput device 1"); + // uinput is set up + + while (1) { + if ((nLength = WacomReadRaw(hTablet, uchBuf, sizeof(uchBuf))) < 0) { + continue; + } + if (WacomParseData(hTablet, uchBuf, nLength, &state)) { + continue; + } + if (!state.values[WACOMFIELD_PROXIMITY].nValue) { + // no tool in proximity + wacom_report_event(EV_ABS, ABS_PRESSURE, 0); + wacom_report_event(EV_KEY, BTN_TOUCH, 0); + // wacom_report_event(EV_KEY, BTN_RIGHT, 0); + // wacom_report_event(EV_KEY, BTN_MIDDLE, 0); + wacom_report_event(EV_SYN, SYN_REPORT, 0); + continue; + } + + wacom_report_event(EV_ABS, ABS_X, + state.values[WACOMFIELD_POSITION_X].nValue); + wacom_report_event(EV_ABS, ABS_Y, + state.values[WACOMFIELD_POSITION_Y].nValue); + wacom_report_event(EV_ABS, ABS_PRESSURE, + state.values[WACOMFIELD_PRESSURE].nValue); + wacom_report_event(EV_KEY, BTN_TOUCH, + state.values[WACOMFIELD_PRESSURE].nValue > minPress); + // wacom_report_event(EV_KEY, BTN_RIGHT, state.values[WACOMFIELD_BUTTONS].nValue == WACOMBUTTON_STYLUS); + // wacom_report_event(EV_KEY, BTN_MIDDLE, state.values[WACOMFIELD_TOOLTYPE].nValue == WACOMTOOLTYPE_ERASER); + wacom_report_event(EV_SYN, SYN_REPORT, 0); + } + + if (ioctl(fd, UI_DEV_DESTROY) < 0) + die("error: ioctl"); + + close(fd); + + return 0; +} diff --git a/wacom/wacserial.c b/wacom/wacserial.c new file mode 100644 index 0000000..543949d --- /dev/null +++ b/wacom/wacserial.c @@ -0,0 +1,1711 @@ +/***************************************************************************** +** wacserial.c +** +** Copyright (C) 2002, 2003 - John E. Joganic +** Copyright (C) 2002 - 2008 - Ping Cheng +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +****************************************************************************/ + +#include "wacserial.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/***************************************************************************** +** Serial Tablet Object +*****************************************************************************/ + +typedef struct _SERIALTABLET SERIALTABLET; +typedef struct _SERIALSUBTYPE SERIALSUBTYPE; +typedef struct _SERIALDEVICE SERIALDEVICE; +typedef struct _SERIALVENDOR SERIALVENDOR; + +typedef int (*IDENTFUNC)(SERIALTABLET* pSerial); +typedef int (*INITFUNC)(SERIALTABLET* pSerial); +typedef int (*PARSEFUNC)(SERIALTABLET* pSerial, const unsigned char* puchData, + unsigned int uLength, WACOMSTATE* pState); + +struct _SERIALTABLET +{ + WACOMTABLET_PRIV tablet; + WACOMENGINE hEngine; + int fd; + SERIALVENDOR* pVendor; + SERIALDEVICE* pDevice; + SERIALSUBTYPE* pSubType; + unsigned int uPacketLength; + int nVerMajor, nVerMinor, nVerRelease; + IDENTFUNC pfnIdent; + INITFUNC pfnInit; + PARSEFUNC pfnParse; + int nToolID; + WACOMSTATE state; + int nBitErrors; + WACOMMODEL modelRequested; +}; + +/***************************************************************************** +** Internal structures +*****************************************************************************/ + +struct _SERIALSUBTYPE +{ + const char* pszName; + const char* pszDesc; + unsigned int uSubType; + const char* pszIdent; + INITFUNC pfnInit; +}; + +struct _SERIALDEVICE +{ + const char* pszName; + const char* pszDesc; + unsigned int uDevice; + SERIALSUBTYPE* pSubTypes; + int nProtocol; + unsigned int uPacketLength; + unsigned int uCaps; + int nMinBaudRate; + IDENTFUNC pfnIdent; +}; + +struct _SERIALVENDOR +{ + const char* pszName; + const char* pszDesc; + unsigned int uVendor; + SERIALDEVICE* pDevices; +}; + +/***************************************************************************** +** Static operations +*****************************************************************************/ + +static void SerialClose(WACOMTABLET_PRIV* pTablet); +static WACOMMODEL SerialGetModel(WACOMTABLET_PRIV* pTablet); +static const char* SerialGetVendorName(WACOMTABLET_PRIV* pTablet); +static const char* SerialGetClassName(WACOMTABLET_PRIV* pTablet); +static const char* SerialGetDeviceName(WACOMTABLET_PRIV* pTablet); +static const char* SerialGetSubTypeName(WACOMTABLET_PRIV* pTablet); +static const char* SerialGetModelName(WACOMTABLET_PRIV* pTablet); +static int SerialGetROMVer(WACOMTABLET_PRIV* pTablet, int* pnMajor, + int* pnMinor, int* pnRelease); +static int SerialGetCaps(WACOMTABLET_PRIV* pTablet); +static int SerialGetState(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState); +static int SerialGetFD(WACOMTABLET_PRIV* pTablet); +static int SerialReadRaw(WACOMTABLET_PRIV* pTablet, unsigned char* puchData, + unsigned int uSize); +static int SerialParseData(WACOMTABLET_PRIV* pTablet, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState); + +static int SerialReset(SERIALTABLET* pSerial, WACOMMODEL* pModel); +static int SerialConfigTTY(SERIALTABLET* pSerial); +static int SerialResetAtBaud(SERIALTABLET* pSerial, struct termios* pTIOS, + int nBaud); +static int SerialSetDevice(SERIALTABLET* pSerial, SERIALVENDOR* pVendor, + SERIALDEVICE* pDevice, SERIALSUBTYPE* pSubType); + +static int SerialIdentDefault(SERIALTABLET* pSerial); +static int SerialIdentTabletPC(SERIALTABLET* pSerial); +static int SerialInitTabletPC(SERIALTABLET* pSerial); +static int SerialIdentWacom(SERIALTABLET* pSerial); +static int SerialInitWacom(SERIALTABLET* pSerial); + +static int SerialParseWacomV(SERIALTABLET* pSerial, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState); +static int SerialParseWacomIV_1_4(SERIALTABLET* pSerial, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState); +static int SerialParseWacomIV_1_3(SERIALTABLET* pSerial, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState); +static int SerialParseWacomIV_1_2(SERIALTABLET* pSerial, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState); +static int SerialParseTabletPC(SERIALTABLET* pSerial, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState); + +static void SerialError(SERIALTABLET* pSerial, const char* pszFmt, ...); +static void SerialWarn(SERIALTABLET* pSerial, const char* pszFmt, ...); +static void SerialInfo(SERIALTABLET* pSerial, const char* pszFmt, ...); +static void SerialDump(SERIALTABLET* pSerial, const void* pvData, int nCnt); +/* NOT USED, YET +static void SerialCritical(SERIALTABLET* pSerial, const char* pszFmt, ...); +static void SerialDebug(SERIALTABLET* pSerial, const char* pszFmt, ...); +static void SerialTrace(SERIALTABLET* pSerial, const char* pszFmt, ...); +*/ + +/***************************************************************************** +** Defines +*****************************************************************************/ + +#ifndef BIT +#undef BIT +#define BIT(x) (1<<(x)) +#endif + +#define WACOMVALID(x) BIT(WACOMFIELD_##x) + +#define ARTPAD_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \ + WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \ + WACOMVALID(PRESSURE)) + +#define ARTPADII_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \ + WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \ + WACOMVALID(PRESSURE)) + +#define DIGITIZER_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \ + WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \ + WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y)) + +#define DIGITIZERII_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \ + WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \ + WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y)) + +#define PENPARTNER_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \ + WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \ + WACOMVALID(PRESSURE)) + +#define GRAPHIRE_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \ + WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \ + WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y)) + +#define CINTIQ_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \ + WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \ + WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y)) + +#define INTUOS_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(SERIAL)| \ + WACOMVALID(PROXIMITY)|WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)| \ + WACOMVALID(POSITION_Y)|WACOMVALID(ROTATION_Z)|WACOMVALID(DISTANCE)| \ + WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y)| \ + WACOMVALID(ABSWHEEL)|WACOMVALID(RELWHEEL)|WACOMVALID(THROTTLE)) + +#define TABLETPC_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(SERIAL)| \ + WACOMVALID(PROXIMITY)|WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)| \ + WACOMVALID(POSITION_Y)|WACOMVALID(PRESSURE)) + +#define INTUOS2_CAPS INTUOS_CAPS + +#define PROTOCOL_4 4 +#define PROTOCOL_5 5 + +#define WACOM_SUBTYPE(id,d,s) \ + { id, d, s, id, SerialInitWacom } +#define TPC_SUBTYPE(id,d,s) \ + { id, d, s, id, SerialInitTabletPC } + +#define WACOM_DEVICE_P4(n,d,i,s,c) \ + { n, d, i, s, PROTOCOL_4, 7, c, 9600, SerialIdentWacom } +#define WACOM_DEVICE_P5(n,d,i,s,c) \ + { n, d, i, s, PROTOCOL_5, 9, c, 9600, SerialIdentWacom } +#define TPC_DEVICE(n,d,i,s,c) \ + { n, d, i, s, 0, 9, c, 19200, SerialIdentTabletPC } + +/***************************************************************************** +** Globals +*****************************************************************************/ + + static SERIALSUBTYPE xArtPadII[] = + { + WACOM_SUBTYPE("KT-0405-R", "Wacom ArtPadII 4x5", 1), + { NULL } + }; + + static SERIALSUBTYPE xDigitizerII[] = + { + WACOM_SUBTYPE("UD-0608-R", "Wacom DigitizerII 6x8", 1), + WACOM_SUBTYPE("UD-1212-R", "Wacom DigitizerII 12x12", 2), + WACOM_SUBTYPE("UD-1218-R", "Wacom DigitizerII 12x18", 3), + WACOM_SUBTYPE("UD-1825-R", "Wacom DigitizerII 18x25", 4), + { NULL } + }; + + static SERIALSUBTYPE xPenPartner[] = + { + WACOM_SUBTYPE("CT-0405-R", "Wacom PenPartner", 1), + { NULL } + }; + + static SERIALSUBTYPE xGraphire[] = + { + WACOM_SUBTYPE("ET-0405-R", "Wacom Graphire", 1), + { NULL } + }; + + static SERIALSUBTYPE xIntuos[] = + { + WACOM_SUBTYPE("GD-0405-R", "Wacom Intuos 4x5", 1), + WACOM_SUBTYPE("GD-0608-R", "Wacom Intuos 6x8", 2), + WACOM_SUBTYPE("GD-0912-R", "Wacom Intuos 9x12", 3), + WACOM_SUBTYPE("GD-1212-R", "Wacom Intuos 12x12", 4), + WACOM_SUBTYPE("GD-1218-R", "Wacom Intuos 12x18", 5), + { NULL } + }; + + static SERIALSUBTYPE xIntuos2[] = + { + WACOM_SUBTYPE("XD-0405-R", "Wacom Intuos2 4x5", 1), + WACOM_SUBTYPE("XD-0608-R", "Wacom Intuos2 6x8", 2), + WACOM_SUBTYPE("XD-0912-R", "Wacom Intuos2 9x12", 3), + WACOM_SUBTYPE("XD-1212-R", "Wacom Intuos2 12x12", 4), + WACOM_SUBTYPE("XD-1218-R", "Wacom Intuos2 12x18", 5), + { NULL } + }; + + static SERIALSUBTYPE xCintiq[] = + { + WACOM_SUBTYPE("PL-250", "Wacom PL-250", 1), + WACOM_SUBTYPE("PL-270", "Wacom PL-270", 2), + WACOM_SUBTYPE("PL-400", "Wacom PL-400", 3), + WACOM_SUBTYPE("PL-500", "Wacom PL-500", 4), + WACOM_SUBTYPE("PL-550", "Wacom PL-550", 5), + WACOM_SUBTYPE("PL-600", "Wacom PL-600", 6), + WACOM_SUBTYPE("PL-600SX", "Wacom PL-600SX", 7), + WACOM_SUBTYPE("PL-800", "Wacom PL-800", 8), + { NULL } + }; + + static SERIALDEVICE xWacomDevices[] = + { + WACOM_DEVICE_P4("art", "ArtPad", WACOMDEVICE_ARTPAD, + NULL, ARTPAD_CAPS), + WACOM_DEVICE_P4("art2", "ArtPadII", WACOMDEVICE_ARTPADII, + xArtPadII, ARTPADII_CAPS), + WACOM_DEVICE_P4("dig", "Digitizer", WACOMDEVICE_DIGITIZER, + NULL, DIGITIZERII_CAPS), + WACOM_DEVICE_P4("dig2", "Digitizer II", WACOMDEVICE_DIGITIZERII, + xDigitizerII, DIGITIZERII_CAPS), + WACOM_DEVICE_P4("pp", "PenPartner", WACOMDEVICE_PENPARTNER, + xPenPartner, PENPARTNER_CAPS), + WACOM_DEVICE_P4("gr", "Graphire", WACOMDEVICE_GRAPHIRE, + xGraphire, GRAPHIRE_CAPS), + WACOM_DEVICE_P4("pl", "Cintiq (PL)", WACOMDEVICE_CINTIQ, + xCintiq, CINTIQ_CAPS), + WACOM_DEVICE_P5("int", "Intuos", WACOMDEVICE_INTUOS, + xIntuos, INTUOS_CAPS), + WACOM_DEVICE_P5("int2", "Intuos2", WACOMDEVICE_INTUOS2, + xIntuos2, INTUOS2_CAPS), + { NULL } + }; + + /* This one is reverse engineered at this point */ + static SERIALSUBTYPE xTabletPC[] = + { + TPC_SUBTYPE("tpc", "Tablet PC Screen", 1), + { NULL } + }; + + static SERIALDEVICE xtpcDevices[] = + { + TPC_DEVICE("tpc", "TabletPC", WACOMDEVICE_TPC, + xTabletPC, TABLETPC_CAPS), + { NULL } + }; + + static SERIALVENDOR xWacomVendor = + { "wacom", "Wacom", WACOMVENDOR_WACOM, xWacomDevices }; + + static SERIALVENDOR xtpcVendor = + { "Wacom", "Wacom", WACOMVENDOR_TPC, xtpcDevices }; + + static SERIALVENDOR* xVendors[] = + { + &xWacomVendor, + &xtpcVendor, + NULL + }; + +/***************************************************************************** +** Static Prototypes +*****************************************************************************/ + +static int SerialSendReset(SERIALTABLET* pSerial); +static int SerialSendStop(SERIALTABLET* pSerial); +static int SerialSendStart(SERIALTABLET* pSerial); + +static int SerialSend(SERIALTABLET* pSerial, const char* pszData); +static int SerialSendRaw(SERIALTABLET* pSerial, const void* pvData, + unsigned int uSize); +static int WacomFlush(SERIALTABLET* pSerial); +static int SerialSendRequest(SERIALTABLET* pSerial, const char* pszRequest, + char* pchResponse, unsigned int uSize); + +/***************************************************************************** +** Public Functions +*****************************************************************************/ + +typedef struct +{ + void (*pfnFree)(void* pv); +} DEVLIST_INTERNAL; + +static void SerialFreeDeviceList(void* pv) +{ + DEVLIST_INTERNAL* pInt = ((DEVLIST_INTERNAL*)pv) - 1; + free(pInt); +} + +int WacomGetSupportedSerialDeviceList(WACOMDEVICEREC** ppList, int* pnSize) +{ + int nIndex=0, nCnt=0; + DEVLIST_INTERNAL* pInt; + SERIALDEVICE* pDev; + SERIALVENDOR** ppVendor; + WACOMDEVICEREC* pRec; + + if (!ppList || !pnSize) { errno = EINVAL; return 1; } + + /* for each vendor, count up devices */ + for (ppVendor=xVendors; *ppVendor; ++ppVendor) + { + /* count up devices */ + for (pDev=(*ppVendor)->pDevices; pDev->pszName; ++pDev, ++nCnt) ; + } + + /* allocate enough memory to hold internal structure and all records */ + pInt = (DEVLIST_INTERNAL*)malloc(sizeof(DEVLIST_INTERNAL) + + (sizeof(WACOMDEVICEREC) * nCnt)); + + pInt->pfnFree = SerialFreeDeviceList; + pRec = (WACOMDEVICEREC*)(pInt + 1); + + /* for each vendor, add devices */ + for (ppVendor=xVendors; *ppVendor; ++ppVendor) + { + for (pDev=(*ppVendor)->pDevices; pDev->pszName; ++pDev, ++nIndex) + { + pRec[nIndex].pszName = pDev->pszName; + pRec[nIndex].pszDesc = pDev->pszDesc; + pRec[nIndex].pszVendorName = (*ppVendor)->pszName; + pRec[nIndex].pszVendorDesc = (*ppVendor)->pszDesc; + pRec[nIndex].pszClass = "serial"; + pRec[nIndex].model.uClass = WACOMCLASS_SERIAL; + pRec[nIndex].model.uVendor = (*ppVendor)->uVendor; + pRec[nIndex].model.uDevice = pDev->uDevice; + pRec[nIndex].model.uSubType = 0; + } + } + assert(nIndex == nCnt); + + *ppList = pRec; + *pnSize = nCnt; + return 0; +} + +unsigned int WacomGetSerialDeviceFromName(const char* pszName) +{ + SERIALDEVICE* pDev; + SERIALVENDOR** ppVendor; + + if (!pszName) { errno = EINVAL; return 0; } + + /* for each vendor, look for device */ + for (ppVendor=xVendors; *ppVendor; ++ppVendor) + { + /* count up devices */ + for (pDev=(*ppVendor)->pDevices; pDev->pszName; ++pDev) + { + if (strcasecmp(pszName,pDev->pszName) == 0) + return pDev->uDevice; + } + } + + errno = ENOENT; + return 0; +} + +static int SerialFindModel(WACOMMODEL* pModel, SERIALVENDOR** ppVendor, + SERIALDEVICE** ppDevice, SERIALSUBTYPE** ppSubType) +{ + SERIALVENDOR** ppPos; + SERIALDEVICE* pDev; + SERIALSUBTYPE* pSub; + + /* device type must be specified */ + if (!pModel) + { errno = EINVAL; return 1; } + + /* no device specified, nothing found. */ + if (!pModel->uDevice) + { + *ppVendor = NULL; + *ppDevice = NULL; + *ppSubType = NULL; + return 0; + } + + /* for each vendor */ + for (ppPos=xVendors; *ppPos; ++ppPos) + { + /* check vendor */ + if (!pModel->uVendor || (pModel->uVendor == (*ppPos)->uVendor)) + { + /* for each device */ + for (pDev=(*ppPos)->pDevices; pDev->pszName; ++pDev) + { + /* if device matches */ + if (pModel->uDevice == pDev->uDevice) + { + /* no subtype specified, use it */ + if (!pModel->uSubType) + { + *ppVendor = *ppPos; + *ppDevice = pDev; + *ppSubType = NULL; + return 0; + } + + /* for each subtype */ + for (pSub=pDev->pSubTypes; pSub->pszName; ++pSub) + { + /* if subtype matches */ + if (pModel->uSubType == pSub->uSubType) + { + *ppVendor = *ppPos; + *ppDevice = pDev; + *ppSubType = pSub; + return 0; + } + } + + /* wrong subtype? maybe try another vendor */ + if (!pModel->uVendor) break; + + /* otherwise, no match. */ + errno = ENOENT; + return 1; + } + } /* next device */ + + /* if vendor matches, but device does not, no match. */ + if (pModel->uVendor) + { + errno = ENOENT; + return 1; + } + } + } /* next vendor */ + + /* no match */ + errno = ENOENT; + return 1; +} + +WACOMTABLET WacomOpenSerialTablet(WACOMENGINE hEngine, int fd, + WACOMMODEL* pModel) +{ + SERIALTABLET* pSerial = NULL; + + /* Allocate tablet */ + pSerial = (SERIALTABLET*)malloc(sizeof(SERIALTABLET)); + memset(pSerial,0,sizeof(*pSerial)); + pSerial->tablet.Close = SerialClose; + pSerial->tablet.GetModel = SerialGetModel; + pSerial->tablet.GetVendorName = SerialGetVendorName; + pSerial->tablet.GetClassName = SerialGetClassName; + pSerial->tablet.GetDeviceName = SerialGetDeviceName; + pSerial->tablet.GetSubTypeName = SerialGetSubTypeName; + pSerial->tablet.GetModelName = SerialGetModelName; + pSerial->tablet.GetROMVer = SerialGetROMVer; + pSerial->tablet.GetCaps = SerialGetCaps; + pSerial->tablet.GetState = SerialGetState; + pSerial->tablet.GetFD = SerialGetFD; + pSerial->tablet.ReadRaw = SerialReadRaw; + pSerial->tablet.ParseData = SerialParseData; + + pSerial->hEngine = hEngine; + pSerial->fd = fd; + pSerial->state.uValueCnt = WACOMFIELD_MAX; + + /* remember what model was request */ + if (pModel) + pSerial->modelRequested = *pModel; + + if (SerialReset(pSerial,pModel)) + { + free(pSerial); + return NULL; + } + + return (WACOMTABLET)pSerial; +} + +static int SerialReset(SERIALTABLET* pSerial, WACOMMODEL* pModel) +{ + SERIALVENDOR* pVendor = NULL; + SERIALDEVICE* pDevice = NULL; + SERIALSUBTYPE* pSubType = NULL; + + /* If model is specified, break it down into vendor, device, and subtype */ + if (pModel && SerialFindModel(pModel,&pVendor,&pDevice,&pSubType)) + return 1; + + /* Set the tablet device */ + if (SerialSetDevice(pSerial,pVendor,pDevice,pSubType)) + return 1; + + /* configure the TTY for initial operation */ + if (SerialConfigTTY(pSerial)) + return 1; + + /* Identify the tablet */ + if (!pSerial->pfnIdent || pSerial->pfnIdent(pSerial)) + return 1; + + /* Initialize the tablet */ + if (!pSerial->pfnInit || pSerial->pfnInit(pSerial)) + return 1; + + /* Send start */ + SerialSendStart(pSerial); + + return 0; +} + +/***************************************************************************** +** Serial Tablet Functions +*****************************************************************************/ + +static int SerialConfigTTY(SERIALTABLET* pSerial) +{ + struct termios tios; + int nBaudRate = 9600; + + /* configure tty */ + if (isatty(pSerial->fd)) + { + /* set up default port parameters */ + if (tcgetattr (pSerial->fd, &tios)) + { + SerialError(pSerial,"Failed to get port params: %s", + strerror(errno)); + return 1; + } + + tios.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + tios.c_oflag &= ~OPOST; + tios.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + tios.c_cflag &= ~(CSIZE|PARENB); + tios.c_cflag |= CS8|CLOCAL; + tios.c_cflag &= ~(CSTOPB); /* 1 stop bit */ + tios.c_cflag &= ~(CSIZE); /* 8 data bits */ + tios.c_cflag |= CS8; + tios.c_cflag &= ~(PARENB); /* no parity */ + tios.c_iflag |= IXOFF; /* flow control XOff */ + tios.c_cc[VMIN] = 1; /* vmin value */ + tios.c_cc[VTIME] = 0; /* vtime value */ + + if (tcsetattr (pSerial->fd, TCSANOW, &tios)) + { + SerialError(pSerial,"Failed to set port params: %s", + strerror(errno)); + return 1; + } + + /* get minumum baud rate for given device, if specified */ + if (pSerial->pDevice) + nBaudRate = pSerial->pDevice->nMinBaudRate; + + /* set 38400 baud and reset */ + if (SerialResetAtBaud(pSerial,&tios,38400)) + return 1; + + /* if valid, set 19200 baud and reset */ + if ((nBaudRate <= 19200) && (SerialResetAtBaud(pSerial,&tios,19200))) + return 1; + + /* if valid, set 9600 baud and reset */ + if ((nBaudRate <= 9600) && (SerialResetAtBaud(pSerial,&tios,9600))) + return 1; + + /* lower than 9600 baud? for testing, maybe */ + if ((nBaudRate < 9600) && (SerialResetAtBaud(pSerial,&tios,nBaudRate))) + return 1; + } + else /* not tty */ + { + if (SerialSendReset(pSerial)) return 1; + } + + /* Send stop */ + if (SerialSendStop(pSerial) || WacomFlush(pSerial)) + return 1; + + return 0; +} + + +static int SerialSetDevice(SERIALTABLET* pSerial, SERIALVENDOR* pVendor, + SERIALDEVICE* pDevice, SERIALSUBTYPE* pSubType) +{ + pSerial->pVendor = pVendor; + pSerial->pDevice = pDevice; + pSerial->pSubType = pSubType; + + /* if we know the device, use its functions */ + if (pSerial->pDevice) + { + pSerial->pfnIdent = pSerial->pDevice->pfnIdent; + if (!pSerial->pfnIdent) { errno = EPERM; return 1; } + } + else + pSerial->pfnIdent = SerialIdentDefault; + + return 0; +} + +static int SerialIdentDefault(SERIALTABLET* pSerial) +{ + return SerialIdentWacom(pSerial); +} + +static int SerialIdentWacom(SERIALTABLET* pSerial) +{ + char* pszPos; + SERIALVENDOR* pVendor = &xWacomVendor; + SERIALDEVICE* pDev; + SERIALSUBTYPE* pSub; + char chResp[64]; + + /* send wacom identification request */ + if (SerialSendRequest(pSerial,"~#\r",chResp,sizeof(chResp))) + { + if (errno != ETIMEDOUT) return 1; + + /* try again, sometimes the first one gets garbled */ + if (SerialSendRequest(pSerial,"~#\r",chResp,sizeof(chResp))) + return 1; + } + + /* look through device table for information */ + for (pDev=pVendor->pDevices; pDev->pszName; ++pDev) + { + for (pSub=pDev->pSubTypes; pSub && pSub->pszName; ++pSub) + { + if (strncmp(chResp,pSub->pszIdent, strlen(pSub->pszIdent)) == 0) + { + pSerial->pVendor = pVendor; + pSerial->pDevice = pDev; + pSerial->pSubType = pSub; + pSerial->state.uValid = pDev->uCaps; + pSerial->uPacketLength = pDev->uPacketLength; + pSerial->pfnInit = pSub->pfnInit ? + pSub->pfnInit : SerialInitWacom; + + /* get version number */ + pszPos = chResp; + while (*pszPos) ++pszPos; + while ((pszPos > chResp) && (pszPos[-1] != 'V')) --pszPos; + if (sscanf(pszPos,"%d.%d-%d",&pSerial->nVerMajor, + &pSerial->nVerMinor,&pSerial->nVerRelease) != 3) + { + pSerial->nVerRelease = 0; + if (sscanf(pszPos,"%d.%d",&pSerial->nVerMajor, + &pSerial->nVerMinor) != 2) + { + errno = EINVAL; + SerialError(pSerial,"bad version number: %s",pszPos); + return 1; + } + } + return 0; + } + } + } + + SerialError(pSerial,"UNIDENTIFIED TABLET: %s",chResp); + return 1; +} + +static int SerialInitWacom(SERIALTABLET* pSerial) +{ + char chResp[32]; + + /* Request tablet dimensions */ + if (SerialSendRequest(pSerial,"~C\r",chResp,sizeof(chResp))) + return 1; + + /* parse position range */ + if (sscanf(chResp,"%d,%d", + &pSerial->state.values[WACOMFIELD_POSITION_X].nMax, + &pSerial->state.values[WACOMFIELD_POSITION_Y].nMax) != 2) + { + errno=EINVAL; + SerialError(pSerial,"Bad dimension response [%s]",chResp); + return 1; + } + + /* tablet specific initialization */ + switch (pSerial->pDevice->uDevice) + { + case WACOMDEVICE_PENPARTNER: + /* pressure mode */ + SerialSend(pSerial, "PH1\r"); + break; + + case WACOMDEVICE_INTUOS: + case WACOMDEVICE_INTUOS2: + /* multi-mode, max-rate */ + SerialSend(pSerial, "MT1\rID1\rIT0\r"); + } + + if (pSerial->pDevice->nProtocol == PROTOCOL_4) + { + /* multi-mode (MU), upper-origin (OC), all-macro (M0), + * no-macro1 (M1), max-rate (IT), no-inc (IN), + * stream-mode (SR), Z-filter (ZF) */ + +/* if (SerialSend(pSerial->fd, "MU1\rOC1\r~M0\r~M1\rIT0\rIN0\rSR\rZF1\r")) + return 1; + */ + + if (pSerial->nVerMajor == 1) + { + if (pSerial->nVerMinor >= 4) + { + /* enable tilt mode */ + if (SerialSend(pSerial,"FM1\r")) return 1; + + pSerial->pfnParse = SerialParseWacomIV_1_4; + pSerial->uPacketLength = 9; + pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255; + pSerial->state.values[WACOMFIELD_TILT_X].nMin = -64; + pSerial->state.values[WACOMFIELD_TILT_X].nMax = 63; + pSerial->state.values[WACOMFIELD_TILT_Y].nMin = -64; + pSerial->state.values[WACOMFIELD_TILT_Y].nMax = 63; + } + else if (pSerial->nVerMinor == 3) + { + pSerial->pfnParse = SerialParseWacomIV_1_3; + pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255; + } + else if (pSerial->nVerMinor == 2) + { + pSerial->pfnParse = SerialParseWacomIV_1_2; + pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255; + } + else if (pSerial->nVerMinor < 2) + { + pSerial->pfnParse = SerialParseWacomIV_1_2; + pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 120; + } + } + } + else if (pSerial->pDevice->nProtocol == PROTOCOL_5) + { + pSerial->pfnParse = SerialParseWacomV; + pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 1023; + pSerial->state.values[WACOMFIELD_ABSWHEEL].nMax = 1023; + pSerial->state.values[WACOMFIELD_ROTATION_Z].nMin = -900; + pSerial->state.values[WACOMFIELD_ROTATION_Z].nMax = 899; + pSerial->state.values[WACOMFIELD_THROTTLE].nMin = -1023; + pSerial->state.values[WACOMFIELD_THROTTLE].nMax = 1023; + pSerial->state.values[WACOMFIELD_TILT_X].nMin = -64; + pSerial->state.values[WACOMFIELD_TILT_X].nMax = 63; + pSerial->state.values[WACOMFIELD_TILT_Y].nMin = -64; + pSerial->state.values[WACOMFIELD_TILT_Y].nMax = 63; + } + else { errno=EINVAL; return 1; } + + return 0; +} + + +static int SerialIdentTabletPC(SERIALTABLET* pSerial) +{ + /* sanity check */ + if ((pSerial->pVendor != &xtpcVendor) || + (pSerial->pDevice == NULL)) { return EPERM; return 1; } + + /* use first one */ + pSerial->pSubType = pSerial->pDevice->pSubTypes; + + /* sanity check again */ + if (pSerial->pSubType->pszName == NULL) { return EPERM; return 1; } + + pSerial->state.uValid = pSerial->pDevice->uCaps; + pSerial->uPacketLength = pSerial->pDevice->uPacketLength; + pSerial->pfnInit = pSerial->pSubType->pfnInit; + pSerial->nVerMajor = 0; + pSerial->nVerMinor = 0; + pSerial->nVerRelease = 0; + + return 0; +} + +static int SerialInitTabletPC(SERIALTABLET* pSerial) +{ + pSerial->pfnParse = SerialParseTabletPC; + pSerial->state.values[WACOMFIELD_POSITION_X].nMax = 0x6000; + pSerial->state.values[WACOMFIELD_POSITION_Y].nMax = 0x4800; + pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255; + return 0; +} + +static int SerialParseWacomV(SERIALTABLET* pSerial, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState) +{ + int x=0, y=0, rot=0, tiltx=0, tilty=0, wheel=0, + tool=WACOMTOOLTYPE_NONE, button=0, press=0, throttle=0, + nButtonValue; + + /* Wacom V + * Supports: 1024 pressure, eraser, 2 side-switch, tilt, throttle, wheel + * Limitation: no tilt */ + + if (uLength != 9) { errno=EINVAL; return 1; } + + /* in */ + if ((puchData[0] & 0xFC) == 0xC0) + { + int toolid = (((int)puchData[1]&0x7F) << 5) | + (((int)puchData[2]&0x7C) >> 2); + + int serial = ((((int)puchData[2] & 0x03) << 30) | + (((int)puchData[3] & 0x7f) << 23) | + (((int)puchData[4] & 0x7f) << 16) | + (((int)puchData[5] & 0x7f) << 9) | + (((int)puchData[6] & 0x7f) << 2) | + (((int)puchData[7] & 0x60) >> 5)); + + switch (toolid) + { + case 0x812: /* Intuos2 ink pen XP-110-00A */ + case 0x012: /* Inking pen */ + tool = WACOMTOOLTYPE_PENCIL; break; + + case 0x822: /* Intuos Pen GP-300E-01H */ + case 0x852: /* Intuos2 Grip Pen XP-501E-00A */ + case 0x842: /* added from Cheng */ + case 0x022: + tool = WACOMTOOLTYPE_PEN; break; + + case 0x832: /* Intuos2 stroke pen XP-120-00A */ + case 0x032: /* Stroke pen */ + tool = WACOMTOOLTYPE_BRUSH; break; + + case 0x007: /* 2D Mouse */ + case 0x09C: /* ?? Mouse */ + case 0x094: /* 4D Mouse */ + tool = WACOMTOOLTYPE_MOUSE; break; + + case 0x096: /* Lens cursor */ + tool = WACOMTOOLTYPE_LENS; break; + + case 0x82a: + case 0x85a: + case 0x91a: + case 0x0fa: /* Eraser */ + tool = WACOMTOOLTYPE_ERASER; break; + + case 0x112: /* Airbrush */ + tool = WACOMTOOLTYPE_AIRBRUSH; break; + + default: /* Unknown tool */ + tool = WACOMTOOLTYPE_PEN; break; + } + + pSerial->nToolID = toolid; + pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = 1; + pSerial->state.values[WACOMFIELD_SERIAL].nValue = serial; + pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool; + return pState ? WacomCopyState(pState,&pSerial->state) : 0; + } + + /* out */ + if ((puchData[0] & 0xFE) == 0x80) + { + pSerial->nToolID = 0; + memset(&pSerial->state.values, 0, + pSerial->state.uValueCnt * sizeof(WACOMVALUE)); + return pState ? WacomCopyState(pState,&pSerial->state) : 0; + } + + /* pen data */ + if (((puchData[0] & 0xB8) == 0xA0) || ((puchData[0] & 0xBE) == 0xB4)) + { + x = ((((int)puchData[1] & 0x7f) << 9) | + (((int)puchData[2] & 0x7f) << 2) | + (((int)puchData[3] & 0x60) >> 5)); + y = ((((int)puchData[3] & 0x1f) << 11) | + (((int)puchData[4] & 0x7f) << 4) | + (((int)puchData[5] & 0x78) >> 3)); + tiltx = (puchData[7] & 0x3F); + tilty = (puchData[8] & 0x3F); + if (puchData[7] & 0x40) tiltx -= 0x40; + if (puchData[8] & 0x40) tilty -= 0x40; + + /* pen packet */ + if ((puchData[0] & 0xB8) == 0xA0) + { + press = ((((int)puchData[5] & 0x07) << 7) | ((int)puchData[6] & 0x7f)); + button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0; + button |= (puchData[0] & 0x02) ? BIT(WACOMBUTTON_STYLUS) : 0; + button |= (puchData[0] & 0x04) ? BIT(WACOMBUTTON_STYLUS2) : 0; + } + + /* 2nd airbrush packet */ + else + { + wheel = ((((int)puchData[5] & 0x07) << 7) | + ((int)puchData[6] & 0x7f)); + } + + pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x; + pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y; + pSerial->state.values[WACOMFIELD_TILT_X].nValue = tiltx; + pSerial->state.values[WACOMFIELD_TILT_Y].nValue = tilty; + pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press; + pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button; + pSerial->state.values[WACOMFIELD_ABSWHEEL].nValue = wheel; + return pState ? WacomCopyState(pState,&pSerial->state) : 0; + } + + /* mouse packet */ + if (((puchData[0] & 0xBE) == 0xA8) || ((puchData[0] & 0xBE) == 0xB0)) + { + x = ((((int)puchData[1] & 0x7f) << 9) | + (((int)puchData[2] & 0x7f) << 2) | + (((int)puchData[3] & 0x60) >> 5)); + y = ((((int)puchData[3] & 0x1f) << 11) | + (((int)puchData[4] & 0x7f) << 4) | + (((int)puchData[5] & 0x78) >> 3)); + throttle = ((((int)puchData[5] & 0x07) << 7) | (puchData[6] & 0x7f)); + if (puchData[8] & 0x08) throttle = -throttle; + + /* 4D mouse */ + if (pSerial->nToolID == 0x094) + { + button = (((puchData[8] & 0x70) >> 1) | (puchData[8] & 0x07)); + } + /* lens cursor */ + else if (pSerial->nToolID == 0x096) + { + button = puchData[8] & 0x1F; + } + /* 2D mouse */ + else + { + button = (puchData[8] & 0x1C) >> 2; + wheel = - (puchData[8] & 1) + ((puchData[8] & 2) >> 1); + } + + pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x; + pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y; + pSerial->state.values[WACOMFIELD_RELWHEEL].nValue = wheel; + pSerial->state.values[WACOMFIELD_THROTTLE].nValue = throttle; + + /* button values */ + nButtonValue = pSerial->state.values[WACOMFIELD_BUTTONS].nValue & + ~(BIT(WACOMBUTTON_LEFT) | + BIT(WACOMBUTTON_RIGHT) | BIT(WACOMBUTTON_MIDDLE) | + BIT(WACOMBUTTON_EXTRA) | BIT(WACOMBUTTON_SIDE)); + if (button & 1) nButtonValue |= BIT(WACOMBUTTON_LEFT); + if (button & 2) nButtonValue |= BIT(WACOMBUTTON_MIDDLE); + if (button & 4) nButtonValue |= BIT(WACOMBUTTON_RIGHT); + if (button & 8) nButtonValue |= BIT(WACOMBUTTON_EXTRA); + if (button & 16) nButtonValue |= BIT(WACOMBUTTON_SIDE); + pSerial->state.values[WACOMFIELD_BUTTONS].nValue = nButtonValue; + + return pState ? WacomCopyState(pState,&pSerial->state) : 0; + } + + /* 2nd 4D mouse packet */ + if ((puchData[0] & 0xBE) == 0xAA) + { + x = ((((int)puchData[1] & 0x7f) << 9) | + (((int)puchData[2] & 0x7f) << 2) | + (((int)puchData[3] & 0x60) >> 5)); + y = ((((int)puchData[3] & 0x1f) << 11) | + (((int)puchData[4] & 0x7f) << 4) | + (((int)puchData[5] & 0x78) >> 3)); + rot = ((((int)puchData[6] & 0x0f) << 7) | + ((int)puchData[7] & 0x7f)); + + /* FIX ROT */ + if (rot < 900) rot = -rot; + else rot = 1799 - rot; + + pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x; + pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y; + pSerial->state.values[WACOMFIELD_ROTATION_Z].nValue = rot; + return pState ? WacomCopyState(pState,&pSerial->state) : 0; + } + + errno = EINVAL; + return 1; +} + +static int SerialParseWacomIV_1_4(SERIALTABLET* pSerial, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState) +{ + /* Wacom IV, Rom 1.4 + * Supports: 256 pressure, eraser, 2 side-switch, tilt */ + + if ((uLength != 7) && (uLength != 9)) { errno=EINVAL; return 1; } + + if (SerialParseWacomIV_1_3(pSerial,puchData,7,pState)) + return 1; + + /* tilt mode */ + if (uLength == 9) + { + int tiltx, tilty; + + tiltx = puchData[7] & 0x3F; + tilty = puchData[8] & 0x3F; + if (puchData[7] & 0x40) tiltx -= 64; + if (puchData[8] & 0x40) tilty -= 64; + + pSerial->state.values[WACOMFIELD_TILT_X].nValue = tiltx; + pSerial->state.values[WACOMFIELD_TILT_Y].nValue = tilty; + + if (pState) + { + pState->values[WACOMFIELD_TILT_X].nValue = tiltx; + pState->values[WACOMFIELD_TILT_Y].nValue = tilty; + } + } + + return 0; +} + +static int SerialParseWacomIV_1_3(SERIALTABLET* pSerial, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState) +{ + int x=0, y=0, prox=0, tool=WACOMTOOLTYPE_NONE, + button=0, press=0, stylus, eraser; + + /* Wacom IV, Rom 1.3 (ArtPadII) + * Supports: 256 pressure, eraser, 2 side-switch + * Limitation: no tilt */ + + if (uLength != 7) { errno=EINVAL; return 1; } + + prox = puchData[0] & 0x40 ? 1 : 0; + if (prox) + { + stylus = puchData[0] & 0x20 ? 1 : 0; + press = (puchData[6] & 0x3F) << 1 | ((puchData[3] & 0x4) >> 2); + press |= (puchData[6] & 0x40) ? 0 : 0x80; + eraser = (puchData[3] & 0x20) ? 1 : 0; + + if (stylus) + { + /* if entering proximity, choose eraser or stylus2 for bit */ + if (pSerial->state.values[WACOMFIELD_PROXIMITY].nValue == 0) + { + if (eraser) tool = WACOMTOOLTYPE_ERASER; + else tool = WACOMTOOLTYPE_PEN; + } + + /* otherwise, keep the last tool */ + else tool = pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue; + + button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0; + + /* pen has 2 side-switch, eraser has none */ + if (tool == WACOMTOOLTYPE_PEN) + { + button |= (puchData[3] & 0x10) ? + BIT(WACOMBUTTON_STYLUS) : 0; + button |= (eraser) ? BIT(WACOMBUTTON_STYLUS2) : 0; + } + } + else + { + tool = WACOMTOOLTYPE_MOUSE; + button = (puchData[3] & 0x78) >> 3; /* not tested */ + } + + x = puchData[2] | ((int)puchData[1] << 7) | + (((int)puchData[0] & 0x3) << 14); + y = puchData[5] | ((int)puchData[4] << 7) | + (((int)puchData[3] & 0x3) << 14); + } + + /* set valid fields */ + pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = prox; + pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool; + pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x; + pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y; + pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press; + pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button; + + return pState ? WacomCopyState(pState,&pSerial->state) : 0; +} + +static int SerialParseWacomIV_1_2(SERIALTABLET* pSerial, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState) +{ + int x=0, y=0, prox=0, tool=0, button=WACOMTOOLTYPE_NONE, + press=0, stylus; + + /* Wacom IV, Rom 1.2, 1.1, and 1.0 + * Supports: 256 pressure (120 for 1.1 and 1.0), multi-mode + * Limitation: no stylus2, no tilt, no eraser */ + + if (uLength != 7) { errno=EINVAL; return 1; } + + prox = puchData[0] & 0x40 ? 1 : 0; + if (prox) + { + stylus = puchData[0] & 0x20 ? 1 : 0; + if (pSerial->nVerMinor == 2) + press = ((puchData[6] & 0x3F) << 1) | ((puchData[3] & 0x4) >> 2) | + ((puchData[6] & 0x40) ? 0 : 0x80); + else + press = (puchData[6] & 0x3F) + ((puchData[6] & 0x40) ? 0 : 64); + + if (stylus) + { + tool = WACOMTOOLTYPE_PEN; + button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0; + button |= (puchData[3] & 0x10) ? BIT(WACOMBUTTON_STYLUS) : 0; + } + else + { + tool = WACOMTOOLTYPE_MOUSE; + button = (puchData[3] & 0x78) >> 3; /* not tested */ + } + + x = puchData[2] | ((int)puchData[1] << 7) | + (((int)puchData[0] & 0x3) << 14); + y = puchData[5] | ((int)puchData[4] << 7) | + (((int)puchData[3] & 0x3) << 14); + } + + /* set valid fields */ + pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = prox; + pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool; + pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x; + pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y; + pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press; + pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button; + + return pState ? WacomCopyState(pState,&pSerial->state) : 0; +} + +static int SerialParseTabletPC(SERIALTABLET* pSerial, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState) +{ + int x=0, y=0, prox=0, tool=WACOMTOOLTYPE_NONE, + button=0, press=0, eraser; + + /* Tablet PC Supports: 256 pressure, eraser, 1/2 side-switch */ + + if (uLength != 9) { errno=EINVAL; return 1; } + + prox = puchData[0] & 0x20 ? 1 : 0; + if (prox) + { + eraser = (puchData[0] & 0x04) ? 1 : 0; + press = ((puchData[6] & 0x01) << 7) | (puchData[5] & 0x7F); + + /* tools are distinguishable */ + if (eraser) tool = WACOMTOOLTYPE_ERASER; + else tool = WACOMTOOLTYPE_PEN; + + button = (puchData[0] & 0x01) ? BIT(WACOMBUTTON_TOUCH) : 0; + + /* pen has side-switch(es), eraser has none */ + if (tool == WACOMTOOLTYPE_PEN) + { + button |= (puchData[0] & 0x02) ? + BIT(WACOMBUTTON_STYLUS) : 0; + button |= (puchData[0] & 0x04) ? + BIT(WACOMBUTTON_STYLUS2) : 0; + } + + x = (((int)puchData[6] & 0x60) >> 5) | + ((int)puchData[2] << 2) | + ((int)puchData[1] << 9); + y = (((int)puchData[6] & 0x18) >> 3) | + ((int)puchData[4] << 2) | + ((int)puchData[3] << 9); + } + + /* set valid fields */ + pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = prox; + pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool; + pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x; + pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y; + pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press; + pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button; + + return pState ? WacomCopyState(pState,&pSerial->state) : 0; +} + + + +/***************************************************************************** +** Internal Functions +*****************************************************************************/ + +static int SerialSendReset(SERIALTABLET* pSerial) +{ + SerialInfo(pSerial,"Sending reset"); + + /* reset to Wacom II-S command set, and factory defaults */ + if (SerialSend(pSerial,"\r$\r")) return 1; + usleep(250000); /* 250 milliseconds */ + + /* reset tablet to Wacom IV command set */ + if (SerialSend(pSerial,"#\r")) return 1; + usleep(75000); /* 75 milliseconds */ + + return 0; +} + +static int SerialSendStop(SERIALTABLET* pSerial) +{ + if (SerialSend(pSerial,"\rSP\r")) return 1; + usleep(100000); + return 0; +} + +static int SerialSendStart(SERIALTABLET* pSerial) +{ + return SerialSend(pSerial,"ST\r"); +} + +static int SerialSend(SERIALTABLET* pSerial, const char* pszMsg) +{ + return SerialSendRaw(pSerial,pszMsg,(unsigned)strlen(pszMsg)); +} + +static int SerialSendRaw(SERIALTABLET* pSerial, const void* pvData, + unsigned int uSize) +{ + int nXfer; + unsigned int uCnt=0; + unsigned char* puchData = (unsigned char*)pvData; + + while (uCnt < uSize) + { + nXfer = write(pSerial->fd,puchData+uCnt,uSize-uCnt); + if (nXfer <= 0) + { + SerialError(pSerial,"Failed to write to port: %s",strerror(errno)); + return 1; + } + uCnt += nXfer; + } + + return 0; +} + +static int WacomFlush(SERIALTABLET* pSerial) +{ + char ch[16]; + fd_set fdsRead; + struct timeval timeout; + + if (tcflush(pSerial->fd, TCIFLUSH) == 0) + return 0; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + while (1) + { + FD_ZERO(&fdsRead); + FD_SET(pSerial->fd, &fdsRead); + if (select(FD_SETSIZE,&fdsRead,NULL,NULL,&timeout) <= 0) + break; + read(pSerial->fd,&ch,sizeof(ch)); + } + + return 0; +} + +static int SerialSendRequest(SERIALTABLET* pSerial, const char* pszRequest, + char* pchResponse, unsigned int uSize) +{ + int nXfer; + fd_set fdsRead; + unsigned int uLen, uCnt; + struct timeval timeout; + + uLen = strlen(pszRequest); + if (SerialSendRaw(pSerial,pszRequest,uLen)) return 1; + --uLen; + + if (uSize < uLen) + { + errno=EINVAL; + SerialError(pSerial,"Invalid size to SerialSendRequest: %u < %u", + uSize,uLen); + return 1; + } + + /* read until first header character */ + while (1) + { + timeout.tv_sec = 0; + timeout.tv_usec = 500000; + + FD_ZERO(&fdsRead); + FD_SET(pSerial->fd, &fdsRead); + if (select(FD_SETSIZE,&fdsRead,NULL,NULL,&timeout) <= 0) + { + errno = ETIMEDOUT; + return 1; + } + + nXfer = read(pSerial->fd,pchResponse,1); + if (nXfer <= 0) + { + SerialError(pSerial,"Truncated response header"); + return 1; + } + if (*pchResponse == *pszRequest) break; + SerialWarn(pSerial,"Discarding %02X", *((unsigned char*)pchResponse)); + } + + /* read response header */ + for (uCnt=1; uCntfd,pchResponse+uCnt,uLen-uCnt); + if (nXfer <= 0) + { + SerialError(pSerial,"Truncated response header (2)"); + return 1; + } + } + + /* check the header */ + if (strncmp(pszRequest,pchResponse,uLen) != 0) + { + SerialError(pSerial,"Incorrect response [%s,%s]", + pszRequest,pchResponse); + return 1; + } + + /* get the rest of the response */ + for (uCnt=0; uCntfd,pchResponse+uCnt,1); + if (nXfer <= 0) + { + SerialError(pSerial,"Failed to read response: %s",strerror(errno)); + return 1; + } + + /* stop on CR */ + if (pchResponse[uCnt] == '\r') + { + pchResponse[uCnt] = '\0'; + return 0; + } + } + + errno = EINVAL; + SerialError(pSerial,"Invalid response"); + return 1; +} + +/***************************************************************************** +** Virtual Functions +*****************************************************************************/ + +static void SerialClose(WACOMTABLET_PRIV* pTablet) +{ + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + close(pSerial->fd); + free(pSerial); +} + +static WACOMMODEL SerialGetModel(WACOMTABLET_PRIV* pTablet) +{ + WACOMMODEL model = { 0 }; + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + model.uClass = WACOMCLASS_SERIAL; + model.uVendor = pSerial->pVendor->uVendor; + model.uDevice = pSerial->pDevice->uDevice; + model.uSubType = pSerial->pSubType->uSubType; + return model; +} + +static const char* SerialGetVendorName(WACOMTABLET_PRIV* pTablet) +{ + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + return pSerial->pVendor->pszDesc; +} + +static const char* SerialGetClassName(WACOMTABLET_PRIV* pTablet) +{ + return "Serial"; +} + +static const char* SerialGetDeviceName(WACOMTABLET_PRIV* pTablet) +{ + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + return pSerial->pDevice->pszDesc; +} + +static const char* SerialGetSubTypeName(WACOMTABLET_PRIV* pTablet) +{ + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + return pSerial->pSubType->pszName; +} + +static const char* SerialGetModelName(WACOMTABLET_PRIV* pTablet) +{ + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + return pSerial->pSubType->pszDesc; +} + +static int SerialGetROMVer(WACOMTABLET_PRIV* pTablet, int* pnMajor, + int* pnMinor, int* pnRelease) +{ + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + if (!pnMajor) { errno=EINVAL; return 1; } + *pnMajor = pSerial->nVerMajor; + if (pnMinor) *pnMinor = pSerial->nVerMinor; + if (pnRelease) *pnRelease = pSerial->nVerRelease; + return 0; +} + +static int SerialGetCaps(WACOMTABLET_PRIV* pTablet) +{ + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + return pSerial->pDevice->uCaps; +} + +static int SerialGetState(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState) +{ + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + return WacomCopyState(pState,&pSerial->state); +} + +static int SerialGetFD(WACOMTABLET_PRIV* pTablet) +{ + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + return pSerial->fd; +} + +static int SerialReadRaw(WACOMTABLET_PRIV* pTablet, unsigned char* puchData, + unsigned int uSize) +{ + int nXfer; + fd_set fdsRead; + unsigned char* pPos, *pEnd; + struct timeval timeout; + unsigned int uCnt, uPacketLength; + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + + if (!pSerial) { errno=EBADF; return 0; } + uPacketLength = pSerial->uPacketLength; + + /* check size of buffer */ + if (uSize < uPacketLength) { errno=EINVAL; return 0; } + + /* check for errors, reset after 3 */ + if (pSerial->nBitErrors > 3) + { + pSerial->nBitErrors = 0; + SerialWarn(pSerial,"Resetting tablet due to bit errors"); + (void)SerialReset(pSerial,&pSerial->modelRequested); + } + + timeout.tv_sec = 0; + timeout.tv_usec = 500000; /* 0.5 seconds for now */ + + for (uCnt=0; uCntfd, &fdsRead); + if (select(FD_SETSIZE,&fdsRead,NULL,NULL,&timeout) <= 0) + break; + + nXfer = read(pSerial->fd,puchData+uCnt,uPacketLength-uCnt); + if (nXfer <= 0) return nXfer; + + /* look for high-bit */ + pEnd = puchData + nXfer; + for (pPos=puchData; pPosnBitErrors; + SerialWarn(pSerial,"Bad high bit, discarding %d bytes", + pPos - puchData); + + /* copy remaining bytes down, if any */ + memmove(puchData,pPos,(nXfer - (pPos-puchData))); + nXfer -= pPos - puchData; + } + } + + if (uCnt < uPacketLength) + { + errno = ETIMEDOUT; + return -1; + } + + return (signed)uCnt; +} + +static int SerialParseData(WACOMTABLET_PRIV* pTablet, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState) +{ + int i; + SERIALTABLET* pSerial = (SERIALTABLET*)pTablet; + if (!pSerial) { errno=EBADF; return 1; } + + /* check synchronization */ + if (!(puchData[0] & 0x80)) + { + ++pSerial->nBitErrors; + SerialError(pSerial,"HIBIT FAIL"); + errno=EINVAL; + return 1; + } + for (i=1; inBitErrors; + SerialError(pSerial,"LOBIT FAIL"); + SerialDump(pSerial,puchData,uLength); + errno=EINVAL; + return 1; + } + } + + /* reset bit error count */ + pSerial->nBitErrors = 0; + + /* dispatch to parser */ + if (pSerial->pfnParse) + return (*pSerial->pfnParse)(pSerial,puchData,uLength,pState); + + errno=EINVAL; + return 1; +} + +static int SerialResetAtBaud(SERIALTABLET* pSerial, struct termios* pTIOS, + int nBaud) +{ + /* conver baud rate to tios macro */ + int baudRate = B9600; + switch (nBaud) + { + case 38400: baudRate = B38400; break; + case 19200: baudRate = B19200; break; + case 9600: baudRate = B9600; break; + case 4800: baudRate = B4800; break; /* for testing, maybe */ + case 2400: baudRate = B2400; break; /* for testing, maybe */ + case 1200: baudRate = B1200; break; /* for testing, maybe */ + } + + SerialInfo(pSerial,"Setting baud rate to %d",nBaud); + + /* change baud rate */ + cfsetispeed(pTIOS, baudRate); + cfsetospeed(pTIOS, baudRate); + if (tcsetattr (pSerial->fd, TCSANOW, pTIOS)) + return 1; + + /* send reset command */ + return SerialSendReset(pSerial); +} + +/***************************************************************************** +** Log Functions +*****************************************************************************/ + +#define SERIALLOG(l) do { \ + va_list a; \ + va_start(a,pszFmt); \ + WacomLogV(pSerial->hEngine,l,pszFmt,a); \ + va_end(a); } while (0) + +static void SerialError(SERIALTABLET* pSerial, const char* pszFmt, ...) + { SERIALLOG(WACOMLOGLEVEL_ERROR); } +static void SerialWarn(SERIALTABLET* pSerial, const char* pszFmt, ...) + { SERIALLOG(WACOMLOGLEVEL_WARN); } +static void SerialInfo(SERIALTABLET* pSerial, const char* pszFmt, ...) + { SERIALLOG(WACOMLOGLEVEL_INFO); } + +static void SerialDump(SERIALTABLET* pSerial, const void* pvData, int nCnt) +{ + int i; + const unsigned char* pData = (const unsigned char*)pvData; + char chLine[80]; + unsigned int uAddr = 0; + int nPos = 0; + + while (nCnt > 0) + { + nPos = 0; + for (i=0; i<16; ++i) + { + if (i < nCnt) + nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos, + "%02X",pData[i]); + else + nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos, + " "); + } + nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos," - "); + for (i=0; i<16; ++i) + { + if (i < nCnt) + nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos, + "%c",isprint(pData[i]) ? pData[i] : '.'); + else + nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos, + " "); + } + + WacomLog(pSerial->hEngine,WACOMLOGLEVEL_DEBUG,"%04X: %s",uAddr,chLine); + uAddr += 16; + nCnt -= 16; + pData += 16; + } +} + +/* NOT USED, YET +static void SerialCritical(SERIALTABLET* pSerial, const char* pszFmt, ...) + { SERIALLOG(WACOMLOGLEVEL_CRITICAL); } +static void SerialDebug(SERIALTABLET* pSerial, const char* pszFmt, ...) + { SERIALLOG(WACOMLOGLEVEL_DEBUG); } +static void SerialTrace(SERIALTABLET* pSerial, const char* pszFmt, ...) + { SERIALLOG(WACOMLOGLEVEL_TRACE); } +*/ diff --git a/wacom/wacserial.h b/wacom/wacserial.h new file mode 100644 index 0000000..d44299d --- /dev/null +++ b/wacom/wacserial.h @@ -0,0 +1,32 @@ +/***************************************************************************** +** wacserial.h +** +** Copyright (C) 2002,2003 - John E. Joganic +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +****************************************************************************/ + +#ifndef __LINUXWACOM_WACSERIAL_H +#define __LINUXWACOM_WACSERIAL_H + +#include "wactablet.h" + +int WacomGetSupportedSerialDeviceList(WACOMDEVICEREC** ppList, int* pnSize); +unsigned int WacomGetSerialDeviceFromName(const char* pszName); +WACOMTABLET WacomOpenSerialTablet(WACOMENGINE hEngine, int fd, + WACOMMODEL* pModel); + +#endif /* __LINUXWACOM_WACSERIAL_H */ diff --git a/wacom/wactablet.c b/wacom/wactablet.c new file mode 100644 index 0000000..3e56ca9 --- /dev/null +++ b/wacom/wactablet.c @@ -0,0 +1,441 @@ +/***************************************************************************** +** wactablet.c +** +** Copyright (C) 2002 - 2003 - John E. Joganic +** Copyright (C) 2004 - 2005 - Ping Cheng +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +****************************************************************************/ + +#include "wactablet.h" +#include "wacserial.h" +#include "wacusb.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WCM_ENABLE_LINUXINPUT +#include +#endif + +typedef void (*FREEFUNC)(void* pv); + +typedef struct +{ + FREEFUNC pfnFree; +} CLSLIST_INTERNAL; + +typedef struct +{ + WACOMDEVICEREC* pSerialList; + WACOMDEVICEREC* pUSBList; + FREEFUNC pfnFree; +} DEVICELIST_INTERNAL; + +typedef struct _ENGINE_PRIV ENGINE_PRIV; + +struct _ENGINE_PRIV +{ + WACOMLOGFUNC pfnLog; + WACOMLOGLEVEL level; + char chLogBuf[1024]; +}; + +/***************************************************************************** +** Implementation +*****************************************************************************/ + +WACOMENGINE WacomInitEngine(void) +{ + ENGINE_PRIV* pEngine = NULL; + pEngine = (ENGINE_PRIV*)malloc(sizeof(ENGINE_PRIV)); + memset(pEngine,0,sizeof(*pEngine)); + pEngine->level = WACOMLOGLEVEL_WARN; + return (WACOMENGINE)pEngine; +} + +void WacomTermEngine(WACOMENGINE hEngine) +{ + ENGINE_PRIV* pEngine = (ENGINE_PRIV*)hEngine; + if (!pEngine) return; + + free(pEngine); +} + +void WacomSetLogFunc(WACOMENGINE hEngine, WACOMLOGFUNC pfnLog) +{ + ENGINE_PRIV* pEngine = (ENGINE_PRIV*)hEngine; + if (!pEngine) return; + pEngine->pfnLog = pfnLog; +} + +void WacomSetLogLevel(WACOMENGINE hEngine, WACOMLOGLEVEL level) +{ + ENGINE_PRIV* pEngine = (ENGINE_PRIV*)hEngine; + if (!pEngine) return; + + if (level < WACOMLOGLEVEL_CRITICAL) + level = WACOMLOGLEVEL_CRITICAL; + if (level > WACOMLOGLEVEL_TRACE) + level = WACOMLOGLEVEL_TRACE; + + pEngine->level = level; +} + +void WacomLogV(WACOMENGINE hEngine, WACOMLOGLEVEL level, const char* pszFmt, + va_list args) +{ + struct timeval tv; + ENGINE_PRIV* pEngine = (ENGINE_PRIV*)hEngine; + if (!pEngine || !pEngine->pfnLog || (pEngine->level < level)) return; + + gettimeofday(&tv,NULL); + + vsnprintf(pEngine->chLogBuf,sizeof(pEngine->chLogBuf),pszFmt,args); + pEngine->pfnLog(tv,level,pEngine->chLogBuf); +} + +void WacomLog(WACOMENGINE hEngine, WACOMLOGLEVEL level, const char* pszFmt, ...) +{ + va_list args; + va_start(args, pszFmt); + WacomLogV(hEngine,level,pszFmt,args); + va_end(args); +} + +static void FreeClassList(void* pv) +{ + CLSLIST_INTERNAL* pInt = ((CLSLIST_INTERNAL*)pv) - 1; + free(pInt); +} + +int WacomGetSupportedClassList(WACOMCLASSREC** ppList, int* pnSize) +{ + int nIndex=0, nCnt=0; + CLSLIST_INTERNAL* pInt; + WACOMCLASSREC* pRec; + + if (!ppList || !pnSize) { errno = EINVAL; return 1; } + + /* serial */ + ++nCnt; + + /* USB */ + #ifdef WCM_ENABLE_LINUXINPUT + ++nCnt; + #endif + + /* allocate enough memory to hold internal structure and all records */ + pInt = (CLSLIST_INTERNAL*)malloc(sizeof(CLSLIST_INTERNAL) + + (sizeof(WACOMCLASSREC) * nCnt)); + + pInt->pfnFree = FreeClassList; + pRec = (WACOMCLASSREC*)(pInt + 1); + + /* serial */ + pRec[nIndex].pszName = "serial"; + pRec[nIndex].pszDesc = "Serial TTY interface"; + pRec[nIndex].uDeviceClass = WACOMCLASS_SERIAL; + ++nIndex; + + /* USB */ + #ifdef WCM_ENABLE_LINUXINPUT + pRec[nIndex].pszName = "usb"; + pRec[nIndex].pszDesc = "Linux USB event interface"; + pRec[nIndex].uDeviceClass = WACOMCLASS_USB; + ++nIndex; + #endif + + assert(nIndex == nCnt); + *ppList = pRec; + *pnSize = nCnt; + return 0; +} + +static void FreeDeviceList(void* pv) +{ + DEVICELIST_INTERNAL* pInt = ((DEVICELIST_INTERNAL*)pv) - 1; + WacomFreeList(pInt->pSerialList); + WacomFreeList(pInt->pUSBList); + free(pInt); +} + +int WacomGetSupportedDeviceList(unsigned int uDeviceClass, + WACOMDEVICEREC** ppList, int* pnSize) +{ + int nSerialCnt=0, nUSBCnt=0, nTotalBytes; + WACOMDEVICEREC* pSerial=NULL, *pUSB=NULL, *pList; + DEVICELIST_INTERNAL* pInt; + + if (!ppList || !pnSize) { errno = EINVAL; return 1; } + + /* get serial list */ + if (((!uDeviceClass) || (uDeviceClass == WACOMCLASS_SERIAL)) && + WacomGetSupportedSerialDeviceList(&pSerial, &nSerialCnt)) return 1; + + /* get usb list */ + if (((!uDeviceClass) || (uDeviceClass == WACOMCLASS_USB)) && + WacomGetSupportedUSBDeviceList(&pUSB, &nUSBCnt)) + { + if (pSerial) WacomFreeList(pSerial); + return 1; + } + + /* need memory for duplicate records and list internal structure */ + nTotalBytes = sizeof(WACOMDEVICEREC) * (nSerialCnt + nUSBCnt) + + sizeof(DEVICELIST_INTERNAL); + + /* allocate memory */ + pInt = (DEVICELIST_INTERNAL*)malloc(nTotalBytes); + + /* copy initial list pointers */ + pInt->pSerialList = pSerial; + pInt->pUSBList = pUSB; + pInt->pfnFree = FreeDeviceList; + + /* copy records */ + pList = (WACOMDEVICEREC*)(pInt + 1); + if (pSerial) + memcpy(pList,pSerial,sizeof(WACOMDEVICEREC) * nSerialCnt); + if (pUSB) + memcpy(pList + nSerialCnt, pUSB, sizeof(WACOMDEVICEREC) * nUSBCnt); + + *ppList = pList; + *pnSize = nSerialCnt + nUSBCnt; + + return 0; +} + +void WacomFreeList(void* pvList) +{ + FREEFUNC pfnFree; + if (!pvList) return; + pfnFree = ((FREEFUNC*)pvList)[-1]; + (*pfnFree)(pvList); +} + +unsigned int WacomGetClassFromName(const char* pszName) +{ + if (strcasecmp(pszName, "serial") == 0) + return WACOMCLASS_SERIAL; + else if (strcasecmp(pszName, "usb") == 0) + return WACOMCLASS_USB; + return 0; +} + +unsigned int WacomGetDeviceFromName(const char* pszName, + unsigned int uDeviceClass) +{ + unsigned int uDeviceType = 0; + + if (!uDeviceClass || (uDeviceClass == WACOMCLASS_SERIAL)) + { + uDeviceType = WacomGetSerialDeviceFromName(pszName); + if (uDeviceType) return uDeviceType; + } + + if (!uDeviceClass || (uDeviceClass == WACOMCLASS_USB)) + { + uDeviceType = WacomGetUSBDeviceFromName(pszName); + if (uDeviceType) return uDeviceType; + } + + errno = ENOENT; + return 0; +} + +static int WacomIsSerial(int fd) +{ + return isatty(fd); +} + +static int WacomIsUSB(int fd) +{ +#ifdef WCM_ENABLE_LINUXINPUT + short sID[4]; + if (ioctl(fd,EVIOCGID,sID) < 0) return 0; + return 1; +#else + return 0; +#endif +} + +WACOMTABLET WacomOpenTablet(WACOMENGINE hEngine, const char* pszDevice, + WACOMMODEL* pModel) +{ + int fd, e; + WACOMTABLET hTablet = NULL; + unsigned int uClass = pModel ? pModel->uClass : 0; + + /* open device for read/write access */ + fd = open(pszDevice,O_RDWR); + if (fd < 0) + { + e = errno; + WacomLog(hEngine,WACOMLOGLEVEL_ERROR,"Failed to open %s: %s", + pszDevice, strerror(errno)); + errno = e; + return NULL; + } + + /* configure serial */ + if ((!uClass || (uClass == WACOMCLASS_SERIAL)) && WacomIsSerial(fd)) + { + hTablet = WacomOpenSerialTablet(hEngine,fd,pModel); + if (!hTablet) { e=errno; close(fd); errno=e; return NULL; } + } + + /* configure usb */ + else if ((!uClass || (uClass == WACOMCLASS_USB)) && WacomIsUSB(fd)) + { + hTablet = WacomOpenUSBTablet(hEngine,fd,pModel); + if (!hTablet) { e=errno; close(fd); errno=e; return NULL; } + } + + /* give up */ + else + { + close(fd); + errno = EINVAL; + return NULL; + } + + return hTablet; +} + +int WacomCopyState(WACOMSTATE* pDest, WACOMSTATE* pSrc) +{ + unsigned int uCnt; + if (!pSrc || !pDest || !pSrc->uValueCnt || !pDest->uValueCnt) + { errno=EINVAL; return 1; } + + /* copy valid bits over */ + pDest->uValid = pSrc->uValid; + + /* determine how many values to transfer */ + uCnt = (pDest->uValueCnt < pSrc->uValueCnt) ? + pDest->uValueCnt : pSrc->uValueCnt; + + /* copy them over */ + memcpy(pDest->values,pSrc->values,uCnt*sizeof(WACOMVALUE)); + + return 0; +} + +/***************************************************************************** +** Virtual Functions +*****************************************************************************/ + +void WacomCloseTablet(WACOMTABLET hTablet) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->Close) return; + pTablet->Close(pTablet); +} + +WACOMMODEL WacomGetModel(WACOMTABLET hTablet) +{ + WACOMMODEL xBadModel = { 0 }; + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->GetModel) { errno=EBADF; return xBadModel; } + return pTablet->GetModel(pTablet); +} + +const char* WacomGetVendorName(WACOMTABLET hTablet) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->GetVendorName) { errno=EBADF; return NULL; } + return pTablet->GetVendorName(pTablet); +} + +const char* WacomGetClassName(WACOMTABLET hTablet) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->GetClassName) { errno=EBADF; return NULL; } + return pTablet->GetClassName(pTablet); +} + +const char* WacomGetDeviceName(WACOMTABLET hTablet) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->GetDeviceName) { errno=EBADF; return NULL; } + return pTablet->GetDeviceName(pTablet); +} + +const char* WacomGetSubTypeName(WACOMTABLET hTablet) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->GetSubTypeName) { errno=EBADF; return NULL; } + return pTablet->GetSubTypeName(pTablet); +} + +const char* WacomGetModelName(WACOMTABLET hTablet) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->GetModelName) { errno=EBADF; return NULL; } + return pTablet->GetModelName(pTablet); +} + +int WacomGetROMVersion(WACOMTABLET hTablet, int* pnMajor, int* pnMinor, + int* pnRelease) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->GetROMVer) { errno=EBADF; return 0; } + return pTablet->GetROMVer(pTablet,pnMajor,pnMinor,pnRelease); +} + +int WacomGetCapabilities(WACOMTABLET hTablet) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->GetCaps) { errno=EBADF; return 0; } + return pTablet->GetCaps(pTablet); +} + +int WacomGetState(WACOMTABLET hTablet, WACOMSTATE* pState) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->GetState) { errno=EBADF; return 0; } + return pTablet->GetState(pTablet,pState); +} + +int WacomGetFileDescriptor(WACOMTABLET hTablet) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->GetFD) { errno=EBADF; return -1; } + return pTablet->GetFD(pTablet); +} + +int WacomReadRaw(WACOMTABLET hTablet, unsigned char* puchData, + unsigned int uSize) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->ReadRaw) { errno=EBADF; return 0; } + return pTablet->ReadRaw(pTablet,puchData,uSize); +} + +int WacomParseData(WACOMTABLET hTablet, const unsigned char* puchData, + unsigned int uLength, WACOMSTATE* pState) +{ + WACOMTABLET_PRIV* pTablet = (WACOMTABLET_PRIV*)hTablet; + if (!pTablet || !pTablet->ParseData) { errno=EBADF; return 0; } + return pTablet->ParseData(pTablet,puchData,uLength,pState); +} diff --git a/wacom/wactablet.h b/wacom/wactablet.h new file mode 100644 index 0000000..9dce960 --- /dev/null +++ b/wacom/wactablet.h @@ -0,0 +1,328 @@ +/***************************************************************************** +** wactablet.h +** +** Copyright (C) 2002 - 2004 - John E. Joganic +** Copyright (C) 2003 - 2008 - Ping Cheng +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +****************************************************************************/ + +#ifndef __LINUXWACOM_WACTABLET_H +#define __LINUXWACOM_WACTABLET_H + +#include +#include + +#define WACOMVENDOR_UNKNOWN 0x0000 +#define WACOMVENDOR_WACOM 0x056A +#define WACOMVENDOR_TPC 0xFFFFFF01 + +#define WACOMCLASS_SERIAL 0x0001 +#define WACOMCLASS_USB 0x0002 + +#define WACOMDEVICE_UNKNOWN 0x0000 +#define WACOMDEVICE_ARTPAD 0x0001 +#define WACOMDEVICE_ARTPADII 0x0002 +#define WACOMDEVICE_DIGITIZER 0x0003 +#define WACOMDEVICE_DIGITIZERII 0x0004 +#define WACOMDEVICE_PENPARTNER 0x0005 +#define WACOMDEVICE_GRAPHIRE 0x0006 +#define WACOMDEVICE_GRAPHIRE2 0x0007 +#define WACOMDEVICE_GRAPHIRE3 0x0008 +#define WACOMDEVICE_GRAPHIRE4 0x0009 +#define WACOMDEVICE_INTUOS 0x000A +#define WACOMDEVICE_INTUOS2 0x000B +#define WACOMDEVICE_CINTIQ 0x000C +#define WACOMDEVICE_PTU 0x000D +#define WACOMDEVICE_VOLITO 0x000E +#define WACOMDEVICE_VOLITO2 0x000F +#define WACOMDEVICE_TPC 0x0010 +#define WACOMDEVICE_INTUOS3 0x0011 +#define WACOMDEVICE_CINTIQV5 0x0012 +#define WACOMDEVICE_MO 0x0013 + +typedef struct _WACOMMODEL WACOMMODEL; +struct _WACOMMODEL +{ + unsigned int uClass; + unsigned int uVendor; + unsigned int uDevice; + unsigned int uSubType; +}; + +#define WACOMTOOLTYPE_NONE 0x00 +#define WACOMTOOLTYPE_PEN 0x01 +#define WACOMTOOLTYPE_PENCIL 0x02 +#define WACOMTOOLTYPE_BRUSH 0x03 +#define WACOMTOOLTYPE_ERASER 0x04 +#define WACOMTOOLTYPE_AIRBRUSH 0x05 +#define WACOMTOOLTYPE_MOUSE 0x06 +#define WACOMTOOLTYPE_LENS 0x07 +#define WACOMTOOLTYPE_PAD 0x08 +#define WACOMTOOLTYPE_TOUCH 0x09 +#define WACOMTOOLTYPE_MAX 0x0A + +#define WACOMBUTTON_LEFT 0 +#define WACOMBUTTON_MIDDLE 1 +#define WACOMBUTTON_RIGHT 2 +#define WACOMBUTTON_EXTRA 3 +#define WACOMBUTTON_SIDE 4 +#define WACOMBUTTON_TOUCH 5 +#define WACOMBUTTON_STYLUS 6 +#define WACOMBUTTON_STYLUS2 7 +#define WACOMBUTTON_BT0 8 +#define WACOMBUTTON_BT1 9 +#define WACOMBUTTON_BT2 10 +#define WACOMBUTTON_BT3 11 +#define WACOMBUTTON_BT4 12 +#define WACOMBUTTON_BT5 13 +#define WACOMBUTTON_BT6 14 +#define WACOMBUTTON_BT7 15 +#define WACOMBUTTON_BT8 16 +#define WACOMBUTTON_BT9 17 +#define WACOMBUTTON_BT10 18 +#define WACOMBUTTON_BT11 19 +#define WACOMBUTTON_BT12 20 +#define WACOMBUTTON_BT13 21 +#define WACOMBUTTON_BT14 22 +#define WACOMBUTTON_BT15 23 +#define WACOMBUTTON_BT16 24 +#define WACOMBUTTON_BT17 25 +#define WACOMBUTTON_BT18 26 +#define WACOMBUTTON_BT19 27 +#define WACOMBUTTON_BT20 28 +#define WACOMBUTTON_BT21 29 +#define WACOMBUTTON_BT22 30 +#define WACOMBUTTON_BT23 31 +#define WACOMBUTTON_MAX 32 + +#define WACOMFIELD_TOOLTYPE 0 +#define WACOMFIELD_SERIAL 1 +#define WACOMFIELD_PROXIMITY 2 +#define WACOMFIELD_BUTTONS 3 +#define WACOMFIELD_POSITION_X 4 +#define WACOMFIELD_POSITION_Y 5 +#define WACOMFIELD_ROTATION_Z 6 +#define WACOMFIELD_DISTANCE 7 +#define WACOMFIELD_PRESSURE 8 +#define WACOMFIELD_TILT_X 9 +#define WACOMFIELD_TILT_Y 10 +#define WACOMFIELD_ABSWHEEL 11 +#define WACOMFIELD_RELWHEEL 12 +#define WACOMFIELD_THROTTLE 13 +#define WACOMFIELD_MAX 14 + +typedef struct +{ + int nValue; + int nMin; + int nMax; + int nReserved; +} WACOMVALUE; + +typedef struct +{ + unsigned int uValueCnt; /* This MUST be set to WACOMFIELD_MAX. */ + unsigned int uValid; /* Bit mask of WACOMFIELD_xxx bits. */ + WACOMVALUE values[WACOMFIELD_MAX]; +} WACOMSTATE; + +#define WACOMSTATE_INIT { WACOMFIELD_MAX } + +typedef struct +{ + const char* pszName; + const char* pszDesc; + unsigned int uDeviceClass; +} WACOMCLASSREC; + +typedef struct +{ + const char* pszName; + const char* pszDesc; + const char* pszVendorName; + const char* pszVendorDesc; + const char* pszClass; + WACOMMODEL model; +} WACOMDEVICEREC; + +typedef enum +{ + WACOMLOGLEVEL_NONE, + WACOMLOGLEVEL_CRITICAL, + WACOMLOGLEVEL_ERROR, + WACOMLOGLEVEL_WARN, + WACOMLOGLEVEL_INFO, + WACOMLOGLEVEL_DEBUG, + WACOMLOGLEVEL_TRACE, + WACOMLOGLEVEL_MAX +} WACOMLOGLEVEL; + +typedef void (*WACOMLOGFUNC)(struct timeval tv, WACOMLOGLEVEL level, + const char* pszLog); + +/***************************************************************************** +** Public structures +*****************************************************************************/ + +typedef struct { int __unused; } *WACOMENGINE; +typedef struct { int __unused; } *WACOMTABLET; + +/***************************************************************************** +** Public API +*****************************************************************************/ + + WACOMENGINE WacomInitEngine(void); + /* Initialize the tablet engine */ + + void WacomTermEngine(WACOMENGINE hEngine); + /* Shutdown the tablet engine */ + + void WacomSetLogFunc(WACOMENGINE hEngine, WACOMLOGFUNC pfnLog); + void WacomSetLogLevel(WACOMENGINE hEngine, WACOMLOGLEVEL level); + void WacomLogV(WACOMENGINE hEngine, WACOMLOGLEVEL level, + const char* pszFmt, va_list args); + void WacomLog(WACOMENGINE hEngine, WACOMLOGLEVEL level, + const char* pszFmt, ...); + /* Logging functions. Default log function does nothing. */ + + int WacomGetSupportedClassList(WACOMCLASSREC** ppList, int* pnSize); + /* Returns 0 on success. Pointer to class record list is returned to + * ppList, and size of list to pnSize. Use WacomFreeList to release. */ + + int WacomGetSupportedDeviceList(unsigned int uDeviceClass, + WACOMDEVICEREC** ppList, int* pnSize); + /* Returns 0 on success. If device class is specified, only devices + * of the request type are returned. A value of 0 will return all + * devices for all classes. Pointer to device record list is returned to + * ppList, and size of list to pnSize. Use WacomFreeList to release. */ + + void WacomFreeList(void* pvList); + /* Releases list memory. */ + + int WacomCopyState(WACOMSTATE* pDest, WACOMSTATE* pSrc); + /* Returns 0 on success. Copies tablet state structures. + * Source and destination structures must be properly initialized, + * particularly the uValueCnt field must be set WACOMFIELD_MAX. + * Returns 0 on success. */ + + unsigned int WacomGetClassFromName(const char* pszName); + /* Returns the device class for a given name. Returns 0, if unknown. */ + + unsigned int WacomGetDeviceFromName(const char* pszName, + unsigned int uDeviceClass); + /* Returns the device type for a given device name. If the + * device class is specified, only that class will be searched. + * Returns 0, if unknown. */ + + WACOMTABLET WacomOpenTablet(WACOMENGINE hEngine, + const char* pszDevice, WACOMMODEL* pModel); + /* Returns tablet handle on success, NULL otherwise. + * pszDevice is pathname to device. Model may be NULL; if any model + * parameters are 0, detection is automatic. */ + + void WacomCloseTablet(WACOMTABLET hTablet); + /* Releases all resource associated with tablet and closes device. */ + + WACOMMODEL WacomGetModel(WACOMTABLET hTablet); + /* Returns model (vendor, class, and device) of specified tablet. */ + + const char* WacomGetVendorName(WACOMTABLET hTablet); + /* Returns vendor name as human-readable string. String is valid as + * long as tablet handle is valid and does not need to be freed. */ + + const char* WacomGetClassName(WACOMTABLET hTablet); + /* Returns class name as human-readable string. String is valid as + * long as tablet handle is valid and does not need to be freed. */ + + const char* WacomGetDeviceName(WACOMTABLET hTablet); + /* Returns device name as human-readable string. String is valid as + * long as tablet handle is valid and does not need to be freed. */ + + const char* WacomGetSubTypeName(WACOMTABLET hTablet); + /* Returns subtype name as human-readable string. This is typically + * the model number (eg. ABC-1234). String is valid as long as tablet + * handle is valid and does not need to be freed. */ + + const char* WacomGetModelName(WACOMTABLET hTablet); + /* Returns model name as human-readable string. This is typically + * the full model name (eg. FooCo FooModel 9x12). String is valid as + * long as tablet handle is valid and does not need to be freed. */ + + int WacomGetROMVersion(WACOMTABLET hTablet, int* pnMajor, int* pnMinor, + int* pnRelease); + /* Returns 0 on success. ROM version of the tablet firmware is returned + * in major, minor, and release values. If the caller needs to + * distinguish between ROM versions to work around a bug, please consider + * submitting a patch to this library. All tablets should appear + * equivalent through this interface. This call is provided for + * information purposes. */ + + int WacomGetCapabilities(WACOMTABLET hTablet); + /* Returns bitmask of valid fields. Use (1 << WACOMFIELD_xxx) to + * translate field identifiers to bit positions. This API will only + * support 32 capabilities. */ + + int WacomGetState(WACOMTABLET hTablet, WACOMSTATE* pState); + /* Returns 0 on success. Tablet state is copied to specified structure. + * Data is only a snapshot of the device and may not accurately reflect + * the position of any specific tool. This is particularly true of + * multi-tool mode tablets. NOTE: pState must point to an initialized + * structure; the uValueCnt field must be correctly set to + * WACOMFIELD_MAX. */ + + int WacomGetFileDescriptor(WACOMTABLET hTablet); + /* Returns the file descriptor of the tablet or -1 on error. */ + + int WacomReadRaw(WACOMTABLET hTablet, unsigned char* puchData, + unsigned int uSize); + /* Returns number of bytes read, typically a single packet. Call will + * block. uSize should be the maximum size of the given data buffer. */ + + int WacomParseData(WACOMTABLET hTablet, const unsigned char* puchData, + unsigned int uLength, WACOMSTATE* pState); + /* Updates the tablet state from a given packet. If pState is specified, + * the tablet state is copied to the given structure. This structure + * must be correctly initialized; the uValueCnt member must be set to + * WACOMFIELD_MAX. Returns 0 on success. */ + +/***************************************************************************** +** Private structures +*****************************************************************************/ + +typedef struct _WACOMTABLET_PRIV WACOMTABLET_PRIV; + +struct _WACOMTABLET_PRIV +{ + void (*Close)(WACOMTABLET_PRIV* pTablet); + WACOMMODEL (*GetModel)(WACOMTABLET_PRIV* pTablet); + const char* (*GetVendorName)(WACOMTABLET_PRIV* pTablet); + const char* (*GetClassName)(WACOMTABLET_PRIV* pTablet); + const char* (*GetDeviceName)(WACOMTABLET_PRIV* pTablet); + const char* (*GetSubTypeName)(WACOMTABLET_PRIV* pTablet); + const char* (*GetModelName)(WACOMTABLET_PRIV* pTablet); + int (*GetROMVer)(WACOMTABLET_PRIV* pTablet, int* pnMajor, int* pnMinor, + int* pnRelease); + int (*GetCaps)(WACOMTABLET_PRIV* pTablet); + int (*GetState)(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState); + int (*GetFD)(WACOMTABLET_PRIV* pTablet); + int (*ReadRaw)(WACOMTABLET_PRIV* pTablet, unsigned char* puchData, + unsigned int uSize); + int (*ParseData)(WACOMTABLET_PRIV* pTablet, const unsigned char* puchData, + unsigned int uLength, WACOMSTATE* pState); +}; + +#endif /* __LINUXWACOM_WACTABLET_H */ diff --git a/wacom/wacusb.c b/wacom/wacusb.c new file mode 100644 index 0000000..22e895a --- /dev/null +++ b/wacom/wacusb.c @@ -0,0 +1,1054 @@ +/***************************************************************************** +** wacusb.c +** +** Copyright (C) 2002 - 2004 - John E. Joganic +** Copyright (C) 2003 - 2009 - Ping Cheng +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +****************************************************************************/ + +#include "wacusb.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/***************************************************************************** +** Begin USB Linux Input Subsystem +*****************************************************************************/ + +#ifdef WCM_ENABLE_LINUXINPUT +#include + +#ifndef EV_MSC +#define EV_MSC 0x04 +#endif + +#ifndef MSC_SERIAL +#define MSC_SERIAL 0x00 +#endif + +#ifndef BTN_TOOL_DOUBLETAP +#define BTN_TOOL_DOUBLETAP 0x14d +#endif + +/***************************************************************************** +** Defines +*****************************************************************************/ + +/* from linux/input.h */ +#define BITS_PER_LONG (sizeof(long) * 8) +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define BIT(x) (1UL<<((x)%BITS_PER_LONG)) +#define LONG(x) ((x)/BITS_PER_LONG) +#define ISBITSET(x,y) ((x)[LONG(y)] & BIT(y)) + +#define MAX_CHANNELS 2 +#define MAX_USB_EVENTS 32 + +/***************************************************************************** +** Structures +*****************************************************************************/ + +typedef struct _USBSUBTYPE USBSUBTYPE; +typedef struct _USBDEVICE USBDEVICE; +typedef struct _USBVENDOR USBVENDOR; + +struct _USBSUBTYPE +{ + const char* pszName; + const char* pszDesc; + unsigned int uSubType; + unsigned int uProduct; +}; + +struct _USBDEVICE +{ + const char* pszName; + const char* pszDesc; + unsigned int uDevice; + USBSUBTYPE* pSubTypes; + unsigned int uChannelCnt; +}; + +struct _USBVENDOR +{ + const char* pszName; + const char* pszDesc; + unsigned int uVendor; + USBDEVICE* pDevices; +}; + +/***************************************************************************** +** USB Tablet object +*****************************************************************************/ + +typedef struct _USBTABLET USBTABLET; + +struct _USBTABLET +{ + WACOMTABLET_PRIV tablet; + WACOMENGINE hEngine; + int fd; + USBVENDOR* pVendor; + USBDEVICE* pDevice; + USBSUBTYPE* pSubType; + int nBus; + int nVersion; + WACOMSTATE state[MAX_CHANNELS]; + int nToolProx[MAX_CHANNELS]; + int nChannel; + int nLastToolSerial; + int nEventCnt; + struct input_event events[MAX_USB_EVENTS]; +}; + +/***************************************************************************** +** Autodetect pad key codes +*****************************************************************************/ + +static unsigned short padkey_codes [] = +{ + BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, + BTN_5, BTN_6, BTN_7, BTN_8, BTN_9, + BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, + BTN_BASE, BTN_BASE2, BTN_BASE3, + BTN_BASE4, BTN_BASE5, BTN_BASE6, + BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_SELECT +}; + +int gPadKeys [WACOMBUTTON_MAX]; +int gNumPadKeys; + +/***************************************************************************** +** Static Prototypes +*****************************************************************************/ + +static void USBClose(WACOMTABLET_PRIV* pTablet); +static WACOMMODEL USBGetModel(WACOMTABLET_PRIV* pTablet); +static const char* USBGetVendorName(WACOMTABLET_PRIV* pTablet); +static const char* USBGetClassName(WACOMTABLET_PRIV* pTablet); +static const char* USBGetDeviceName(WACOMTABLET_PRIV* pTablet); +static const char* USBGetSubTypeName(WACOMTABLET_PRIV* pTablet); +static const char* USBGetModelName(WACOMTABLET_PRIV* pTablet); +static int USBGetROMVer(WACOMTABLET_PRIV* pTablet, int* pnMajor, + int* pnMinor, int* pnRelease); +static int USBGetCaps(WACOMTABLET_PRIV* pTablet); +static int USBGetState(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState); +static int USBGetFD(WACOMTABLET_PRIV* pTablet); +static int USBReadRaw(WACOMTABLET_PRIV* pTablet, unsigned char* puchData, + unsigned int uSize); +static int USBParseData(WACOMTABLET_PRIV* pTablet, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState); + +static int USBFindModel(USBTABLET* pUSB, unsigned int uVendor, + unsigned int uProduct); +static int USBIdentifyModel(USBTABLET* pUSB); + +/***************************************************************************** +** Globals +*****************************************************************************/ + + static USBSUBTYPE xPenPartner[] = + { + { "MODEL_PP_0405", "Wacom PenPartner", 1, 0x00 }, + { NULL } + }; + + static USBSUBTYPE xGraphire[] = + { + { "ET_0405", "Wacom Graphire", 1, 0x10 }, + { NULL } + }; + + static USBSUBTYPE xGraphire2[] = + { + { "ET_0405", "Wacom Graphire2 4x5", 1, 0x11 }, + { "ET_0507", "Wacom Graphire2 5x7", 2, 0x12 }, + { NULL } + }; + + static USBSUBTYPE xGraphire3[] = + { + { "ET_0405", "Wacom Graphire3 4x5", 1, 0x13 }, + { "ET_0608", "Wacom Graphire3 6x8", 2, 0x14 }, + { NULL } + }; + + static USBSUBTYPE xGraphire4[] = + { + { "CTE_440", "Wacom Graphire4 4x5", 1, 0x15 }, + { "CTE_640", "Wacom Graphire4 6x8", 2, 0x16 }, + { NULL } + }; + + static USBSUBTYPE xIntuos[] = + { + { "GD_0405-U", "Wacom Intuos 4x5", 1, 0x20 }, + { "GD_0608-U", "Wacom Intuos 6x8", 2, 0x21 }, + { "GD_0912-U", "Wacom Intuos 9x12", 3, 0x22 }, + { "GD_1212-U", "Wacom Intuos 12x12", 4, 0x23 }, + { "GD_1218-U", "Wacom Intuos 12x18", 5, 0x24 }, + { NULL } + }; + + static USBSUBTYPE xCintiq[] = + { + { "PL400", "Wacom PL400", 1, 0x30 }, + { "PL500", "Wacom PL500", 2, 0x31 }, + { "PL600", "Wacom PL600", 3, 0x32 }, + { "PL600SX", "Wacom PL600SX", 4, 0x33 }, + { "PL550", "Wacom PL550", 5, 0x34 }, + { "PL800", "Wacom PL800", 6, 0x35 }, + { "PL700", "Wacom PL700", 7, 0x37 }, + { "PL510", "Wacom PL510", 8, 0x38 }, + { "DTU710", "Wacom PL710", 9, 0x39 }, + { "DTF720", "Wacom DTF720", 10, 0xC0 }, + { "DTF521", "Wacom DTF521", 11, 0xC4 }, + { "DTU1931", "Wacom DTU1931", 12, 0xC7 }, + { "DTU2231", "Wacom DTU2231", 13, 0xCE }, + { "DTU1631", "Wacom DTU1631", 14, 0xF0 }, + { NULL } + }; + + static USBSUBTYPE xIntuos2[] = + { + { "XD-0405-U", "Wacom Intuos2 4x5", 1, 0x41 }, + { "XD-0608-U", "Wacom Intuos2 6x8", 2, 0x42 }, + { "XD-0912-U", "Wacom Intuos2 9x12", 3, 0x43 }, + { "XD-1212-U", "Wacom Intuos2 12x12", 4, 0x44 }, + { "XD-1218-U", "Wacom Intuos2 12x18", 5, 0x45 }, + + /* fix for I2 6x8's reporting as 0x47 */ + { "XD-0608-U", "Wacom Intuos2 6x8", 2, 0x47 }, + + { NULL } + }; + + static USBSUBTYPE xVolito[] = + { + { "MODEL-VOL", "Wacom Volito", 1, 0x60 }, + { NULL } + }; + + static USBSUBTYPE xVolito2[] = + { + { "FT-0203-U", "Wacom PenStation", 1, 0x61 }, + { "CTF-420-U", "Wacom Volito2 4x5", 2, 0x62 }, + { "CTF-220-U", "Wacom Volito2 2x3", 3, 0x63 }, + { "CTF-421-U", "Wacom PenPartner2", 4, 0x64 }, + { "CTF_430-U", "Wacom Bamboo1", 5, 0x69 }, + { NULL } + }; + + static USBSUBTYPE xBamboo[] = + { + { "MTE_450", "Wacom Bamboo", 1, 0x65 }, + { "CTE_450", "Wacom BambooFun 4x5", 2, 0x17 }, + { "CTE_650", "Wacom BambooFun 6x8", 3, 0x18 }, + { "CTE_631", "Wacom Bamboo1 Medium", 4, 0x19 }, + { NULL } + }; + + static USBSUBTYPE xCintiqPartner[] = + { + { "PTU-600", "Wacom Cintiq Partner", 1, 0x03 }, + { NULL } + }; + + static USBSUBTYPE xTabletPC[] = + { + { "TPC-090", "Wacom Tablet PC90", 1, 0x90 }, + { "TPC-093", "Wacom Tablet PC93", 2, 0x93 }, + { "TPC-09A", "Wacom Tablet PC9A", 3, 0x9A }, + { NULL } + }; + + static USBSUBTYPE xCintiqV5[] = + { + { "DTZ-21ux", "Wacom Cintiq 21UX", 1, 0x3F }, + { "DTZ-20wsx", "Wacom Cintiq 20WSX", 2, 0xC5 }, + { "DTZ-12wx", "Wacom Cintiq 12WX", 3, 0xC6 }, + { "DTZ-21ux2", "Wacom Cintiq 21UX2", 4, 0xCC }, + { NULL } + }; + + static USBSUBTYPE xIntuos3[] = + { + { "PTZ-430", "Wacom Intuos3 4x5", 1, 0xB0 }, + { "PTZ-630", "Wacom Intuos3 6x8", 2, 0xB1 }, + { "PTZ-930", "Wacom Intuos3 9x12", 3, 0xB2 }, + { "PTZ-1230", "Wacom Intuos3 12x12", 4, 0xB3 }, + { "PTZ-1231W", "Wacom Intuos3 12x19", 5, 0xB4 }, + { "PTZ-631W", "Wacom Intuos3 6x11", 6, 0xB5 }, + { "PTZ-431W", "Wacom Intuos3 4x6", 7, 0xB7 }, + { NULL } + }; + + static USBSUBTYPE xIntuos4[] = + { + { "PTK-440", "Wacom Intuos4 4x6", 1, 0xB8 }, + { "PTK-640", "Wacom Intuos4 6x9", 2, 0xB9 }, + { "PTK-840", "Wacom Intuos4 8x13", 3, 0xBA }, + { "PTK-1240", "Wacom Intuos4 12x19", 4, 0xBB }, + { "PTK-540WL", "Wacom Intuos4 WLUSB", 5, 0xBC }, + { "PTK-540WL", "Wacom Intuos4 WLBT", 6, 0xBD }, + { NULL } + }; + + static USBDEVICE xWacomDevices[] = + { + { "pp", "PenPartner", WACOMDEVICE_PENPARTNER, xPenPartner, 1 }, + { "gr", "Graphire", WACOMDEVICE_GRAPHIRE, xGraphire, 1 }, + { "gr2", "Graphire2", WACOMDEVICE_GRAPHIRE2, xGraphire2, 1 }, + { "gr3", "Graphire3", WACOMDEVICE_GRAPHIRE3, xGraphire3, 1 }, + { "gr4", "Graphire4", WACOMDEVICE_GRAPHIRE4, xGraphire4, 2 }, + { "int", "Intuos", WACOMDEVICE_INTUOS, xIntuos, 2 }, + { "int2", "Intuos2", WACOMDEVICE_INTUOS2, xIntuos2, 2 }, + { "int3", "Intuos3", WACOMDEVICE_INTUOS3, xIntuos3, 2 }, + { "int4", "Intuos4", WACOMDEVICE_INTUOS3, xIntuos4, 2 }, + { "ctq", "Cintiq (V5)", WACOMDEVICE_CINTIQV5, xCintiqV5, 2 }, + { "pl", "Cintiq (PL)", WACOMDEVICE_CINTIQ, xCintiq, 1 }, + { "ptu", "Cintiq Partner (PTU)", WACOMDEVICE_PTU, xCintiqPartner, 1 }, + { "vol", "Volito", WACOMDEVICE_VOLITO, xVolito, 1 }, + { "vol2", "Volito2", WACOMDEVICE_VOLITO2, xVolito2, 1 }, + { "mo", "Bamboo", WACOMDEVICE_MO, xBamboo, 2 }, + { "tpc", "Tablet PC", WACOMDEVICE_TPC, xTabletPC, 1 }, + { NULL } + }; + + static USBVENDOR xVendors[] = + { + { "wacom", "Wacom", WACOMVENDOR_WACOM, xWacomDevices }, + { NULL }, + }; + +/***************************************************************************** +** Public Functions +*****************************************************************************/ + +typedef struct +{ + void (*pfnFree)(void* pv); +} DEVLIST_INTERNAL; + +static void USBFreeDeviceList(void* pv) +{ + DEVLIST_INTERNAL* pInt = ((DEVLIST_INTERNAL*)pv) - 1; + free(pInt); +} + +int WacomGetSupportedUSBDeviceList(WACOMDEVICEREC** ppList, int* pnSize) +{ + int nIndex=0, nCnt=0; + DEVLIST_INTERNAL* pInt; + USBDEVICE* pDev; + USBVENDOR* pVendor; + WACOMDEVICEREC* pRec; + + if (!ppList || !pnSize) { errno = EINVAL; return 1; } + + /* for each vendor, count up devices */ + for (pVendor=xVendors; pVendor->pszName; ++pVendor) + { + /* count up devices */ + for (pDev=pVendor->pDevices; pDev->pszName; ++pDev, ++nCnt) ; + } + + /* allocate enough memory to hold internal structure and all records */ + pInt = (DEVLIST_INTERNAL*)malloc(sizeof(DEVLIST_INTERNAL) + + (sizeof(WACOMDEVICEREC) * nCnt)); + + pInt->pfnFree = USBFreeDeviceList; + pRec = (WACOMDEVICEREC*)(pInt + 1); + + /* for each vendor, add devices */ + for (pVendor=xVendors; pVendor->pszName; ++pVendor) + { + for (pDev=pVendor->pDevices; pDev->pszName; ++pDev, ++nIndex) + { + pRec[nIndex].pszName = pDev->pszName; + pRec[nIndex].pszDesc = pDev->pszDesc; + pRec[nIndex].pszVendorName = pVendor->pszName; + pRec[nIndex].pszVendorDesc = pVendor->pszDesc; + pRec[nIndex].pszClass = "usb"; + pRec[nIndex].model.uClass = WACOMCLASS_USB; + pRec[nIndex].model.uVendor = pVendor->uVendor; + pRec[nIndex].model.uDevice = pDev->uDevice; + pRec[nIndex].model.uSubType = 0; + } + } + assert(nIndex == nCnt); + + *ppList = pRec; + *pnSize = nCnt; + return 0; +} + +unsigned int WacomGetUSBDeviceFromName(const char* pszName) +{ + USBDEVICE* pDev; + USBVENDOR* pVendor; + + if (!pszName) { errno = EINVAL; return 0; } + + /* for each vendor, look for device */ + for (pVendor=xVendors; pVendor->pszName; ++pVendor) + { + /* count up devices */ + for (pDev=pVendor->pDevices; pDev->pszName; ++pDev) + { + if (strcasecmp(pszName,pDev->pszName) == 0) + return pDev->uDevice; + } + } + + errno = ENOENT; + return 0; +} + +WACOMTABLET WacomOpenUSBTablet(WACOMENGINE hEngine, int fd, WACOMMODEL* pModel) +{ + USBTABLET* pUSB = NULL; + + /* Allocate tablet */ + pUSB = (USBTABLET*)malloc(sizeof(USBTABLET)); + memset(pUSB,0,sizeof(*pUSB)); + pUSB->tablet.Close = USBClose; + pUSB->tablet.GetModel = USBGetModel; + pUSB->tablet.GetVendorName = USBGetVendorName; + pUSB->tablet.GetClassName = USBGetClassName; + pUSB->tablet.GetDeviceName = USBGetDeviceName; + pUSB->tablet.GetSubTypeName = USBGetSubTypeName; + pUSB->tablet.GetModelName = USBGetModelName; + pUSB->tablet.GetROMVer = USBGetROMVer; + pUSB->tablet.GetCaps = USBGetCaps; + pUSB->tablet.GetState = USBGetState; + pUSB->tablet.GetFD = USBGetFD; + pUSB->tablet.ReadRaw = USBReadRaw; + pUSB->tablet.ParseData = USBParseData; + pUSB->hEngine = hEngine; + pUSB->fd = fd; + + /* Identify and initialize the model */ + if (USBIdentifyModel(pUSB)) + { + WacomLog(pUSB->hEngine,WACOMLOGLEVEL_ERROR, + "Failed to identify model: %s",strerror(errno)); + free(pUSB); + return NULL; + } + + return (WACOMTABLET)pUSB; +} + +/***************************************************************************** +** Tablet Functions +*****************************************************************************/ + +static void USBClose(WACOMTABLET_PRIV* pTablet) +{ + USBTABLET* pUSB = (USBTABLET*)pTablet; + close(pUSB->fd); + free(pUSB); +} + +static WACOMMODEL USBGetModel(WACOMTABLET_PRIV* pTablet) +{ + WACOMMODEL model = { 0 }; + USBTABLET* pUSB = (USBTABLET*)pTablet; + model.uClass = WACOMCLASS_USB; + model.uVendor = pUSB->pVendor->uVendor; + model.uDevice = pUSB->pDevice->uDevice; + model.uSubType = pUSB->pSubType->uSubType; + return model; +} + +static int USBGetRange(USBTABLET* pUSB, unsigned long* pBits, int nAbsField, + unsigned int uField) +{ + int nAbs[5]; + + if (!ISBITSET(pBits,nAbsField)) + return 0; + if (ioctl(pUSB->fd, EVIOCGABS(nAbsField), nAbs) != 0) + return 1; + + pUSB->state[0].values[uField].nMin = nAbs[1]; + pUSB->state[0].values[uField].nMax = nAbs[2]; + pUSB->state[0].uValid |= BIT(uField); + return 0; +} + +static int USBFindModel(USBTABLET* pUSB, unsigned int uVendor, + unsigned int uProduct) +{ + USBVENDOR* pVendor = NULL; + USBDEVICE* pDev = NULL; + USBSUBTYPE* pSub = NULL; + + static USBSUBTYPE xUnkSub[] = + { + { "UNKNOWN", "Unknown", 1, 0x00 }, + { NULL } + }; + + static USBDEVICE xUnkDev[] = + { + { "unk", "Unknown", WACOMDEVICE_UNKNOWN, xUnkSub, MAX_CHANNELS }, + { NULL } + }; + + static USBVENDOR xUnkVendor = + { "unknown", "Unknown", WACOMVENDOR_UNKNOWN, xUnkDev }; + + for (pVendor=xVendors; pVendor->pszName; ++pVendor) + { + if (pVendor->uVendor == uVendor) + { + for (pDev=pVendor->pDevices; pDev->pszName; ++pDev) + { + for (pSub=pDev->pSubTypes; pSub->pszName; ++pSub) + { + if (pSub->uProduct == uProduct) + { + pUSB->pVendor = pVendor; + pUSB->pDevice = pDev; + pUSB->pSubType = pSub; + return 0; + } + } + } + + /* only one vendor of this type, so we're done */ + break; + } + } + + /* unknown vendor */ + pUSB->pVendor = &xUnkVendor; + pUSB->pDevice = pUSB->pVendor->pDevices; + pUSB->pSubType = pUSB->pDevice->pSubTypes; + return 0; +} + +static int USBIdentifyModel(USBTABLET* pUSB) +{ + int nCnt, chcnt; + short sID[4]; + unsigned int uVendor, uProduct; + unsigned long evbits[NBITS(EV_MAX)]; + unsigned long absbits[NBITS(ABS_MAX)]; + unsigned long relbits[NBITS(REL_MAX)]; + unsigned long keybits[NBITS(KEY_MAX)]; + + /* Get device name and id */ + if (ioctl(pUSB->fd,EVIOCGID,sID) < 0) + return 1; + + /* initialize state structure */ + pUSB->state[0].uValueCnt = WACOMFIELD_MAX; + + /* Get event types supported */ + nCnt = ioctl(pUSB->fd,EVIOCGBIT(0 /*EV*/,sizeof(evbits)),evbits); + if (nCnt < 0) + { + WacomLog(pUSB->hEngine,WACOMLOGLEVEL_ERROR, + "Failed to CGBIT ev: %s",strerror(errno)); + return 1; + } + assert(nCnt == sizeof(evbits)); + + /* absolute events */ + if (ISBITSET(evbits,EV_ABS)) + { + nCnt = ioctl(pUSB->fd,EVIOCGBIT(EV_ABS,sizeof(absbits)),absbits); + if (nCnt < 0) + { + WacomLog(pUSB->hEngine,WACOMLOGLEVEL_ERROR, + "Failed to CGBIT abs: %s",strerror(errno)); + return 1; + } + + /* the following line has problem on Debian systems + assert(nCnt == sizeof(absbits)); + */ + + if (USBGetRange(pUSB,absbits,ABS_X,WACOMFIELD_POSITION_X) || + USBGetRange(pUSB,absbits,ABS_Y,WACOMFIELD_POSITION_Y) || + USBGetRange(pUSB,absbits,ABS_RZ,WACOMFIELD_ROTATION_Z) || + USBGetRange(pUSB,absbits,ABS_DISTANCE,WACOMFIELD_DISTANCE) || + USBGetRange(pUSB,absbits,ABS_PRESSURE,WACOMFIELD_PRESSURE) || + USBGetRange(pUSB,absbits,ABS_TILT_X,WACOMFIELD_TILT_X) || + USBGetRange(pUSB,absbits,ABS_TILT_Y,WACOMFIELD_TILT_Y) || + USBGetRange(pUSB,absbits,ABS_WHEEL,WACOMFIELD_ABSWHEEL) || + USBGetRange(pUSB,absbits,ABS_THROTTLE,WACOMFIELD_THROTTLE)) + return 1; + + } + + /* relative events */ + if (ISBITSET(evbits,EV_REL)) + { + nCnt = ioctl(pUSB->fd,EVIOCGBIT(EV_REL,sizeof(relbits)),relbits); + if (nCnt < 0) + { + WacomLog(pUSB->hEngine,WACOMLOGLEVEL_ERROR, + "Failed to CGBIT rel: %s",strerror(errno)); + return 1; + } + assert(nCnt == sizeof(relbits)); + + if (ISBITSET(relbits,REL_WHEEL)) + { + pUSB->state[0].uValid |= BIT(WACOMFIELD_RELWHEEL); + pUSB->state[0].values[WACOMFIELD_RELWHEEL].nMin = -1; + pUSB->state[0].values[WACOMFIELD_RELWHEEL].nMax = 1; + } + } + + /* key events */ + gNumPadKeys = 0; + if (ISBITSET(evbits,EV_KEY)) + { + nCnt = ioctl(pUSB->fd,EVIOCGBIT(EV_KEY,sizeof(keybits)),keybits); + if (nCnt < 0) + { + WacomLog(pUSB->hEngine,WACOMLOGLEVEL_ERROR, + "Failed to CGBIT key: %s",strerror(errno)); + return 1; + } + assert(nCnt == sizeof(keybits)); + + /* button events */ + if (ISBITSET(keybits,BTN_LEFT) || + ISBITSET(keybits,BTN_RIGHT) || + ISBITSET(keybits,BTN_MIDDLE) || + ISBITSET(keybits,BTN_SIDE) || + ISBITSET(keybits,BTN_EXTRA)) + pUSB->state[0].uValid |= BIT(WACOMFIELD_BUTTONS); + + /* tool events */ + if (ISBITSET(keybits,BTN_TOOL_PEN) || + ISBITSET(keybits,BTN_TOOL_RUBBER) || + ISBITSET(keybits,BTN_TOOL_BRUSH) || + ISBITSET(keybits,BTN_TOOL_PENCIL) || + ISBITSET(keybits,BTN_TOOL_AIRBRUSH) || + ISBITSET(keybits,BTN_TOOL_FINGER) || + ISBITSET(keybits,BTN_TOOL_MOUSE) || + ISBITSET(keybits,BTN_TOOL_LENS) || + ISBITSET(keybits,BTN_TOOL_DOUBLETAP)) + pUSB->state[0].uValid |= BIT(WACOMFIELD_PROXIMITY) | + BIT(WACOMFIELD_TOOLTYPE); + + /* Detect button codes */ + for (nCnt = 0; nCnt < WACOMBUTTON_MAX; nCnt++) + if (ISBITSET (keybits, padkey_codes [nCnt])) + gPadKeys [gNumPadKeys++] = padkey_codes [nCnt]; + } + + /* set identification */ + pUSB->nBus = sID[0]; + uVendor = sID[1]; + uProduct = sID[2]; + pUSB->nVersion = sID[3]; + + if (USBFindModel(pUSB,uVendor,uProduct)) + return 1; + + /* add additional capabilities by device type */ + switch (pUSB->pDevice->uDevice) + { + case WACOMDEVICE_MO: + case WACOMDEVICE_GRAPHIRE4: + case WACOMDEVICE_INTUOS: + case WACOMDEVICE_INTUOS2: + case WACOMDEVICE_INTUOS3: + case WACOMDEVICE_CINTIQV5: + pUSB->state[0].uValid |= BIT(WACOMFIELD_SERIAL); + default: ; + } + + /* Initialize all channels with the same values */ + for(chcnt=1; chcntstate[chcnt] = pUSB->state[0]; + + return 0; +} + +static const char* USBGetVendorName(WACOMTABLET_PRIV* pTablet) +{ + USBTABLET* pUSB = (USBTABLET*)pTablet; + return pUSB->pVendor->pszDesc; +} + +static const char* USBGetClassName(WACOMTABLET_PRIV* pTablet) +{ + return "USB"; +} + +static const char* USBGetDeviceName(WACOMTABLET_PRIV* pTablet) +{ + USBTABLET* pUSB = (USBTABLET*)pTablet; + return pUSB->pDevice->pszDesc; +} + +static const char* USBGetSubTypeName(WACOMTABLET_PRIV* pTablet) +{ + USBTABLET* pUSB = (USBTABLET*)pTablet; + return pUSB->pSubType->pszName; +} + +static const char* USBGetModelName(WACOMTABLET_PRIV* pTablet) +{ + USBTABLET* pUSB = (USBTABLET*)pTablet; + return pUSB->pSubType->pszDesc; +} + +static int USBGetROMVer(WACOMTABLET_PRIV* pTablet, int* pnMajor, + int* pnMinor, int* pnRelease) +{ + USBTABLET* pUSB = (USBTABLET*)pTablet; + if (!pnMajor) { errno=EINVAL; return 1; } + + /* how is the version number broken down? + * mine says 0x115. is that 1.1.5 or 1.15? */ + *pnMajor = pUSB->nVersion >> 8; + *pnMinor = (pUSB->nVersion >> 4) & 0xF; + *pnRelease = (pUSB->nVersion & 0xF); + return 0; +} + +static int USBGetCaps(WACOMTABLET_PRIV* pTablet) +{ + USBTABLET* pUSB = (USBTABLET*)pTablet; + return pUSB->state[0].uValid; +} + +static int USBGetState(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState) +{ + USBTABLET* pUSB = (USBTABLET*)pTablet; + return WacomCopyState(pState,&pUSB->state[pUSB->nChannel]); +} + +static int USBGetFD(WACOMTABLET_PRIV* pTablet) +{ + USBTABLET* pUSB = (USBTABLET*)pTablet; + return pUSB->fd; +} + +static int USBReadRaw(WACOMTABLET_PRIV* pTablet, unsigned char* puchData, + unsigned int uSize) +{ + int nXfer; + unsigned int uCnt, uPacketLength; + USBTABLET* pUSB = (USBTABLET*)pTablet; + uPacketLength = sizeof(struct input_event); + + /* check size of buffer */ + if (uSize < uPacketLength) { errno=EINVAL; return 0; } + + for (uCnt=0; uCntfd,puchData+uCnt,uPacketLength-uCnt); + if (nXfer <= 0) return nXfer; + } + + return (signed)uCnt; +} + +static int USBParseMSC(USBTABLET* pUSB, struct input_event* pEv) +{ + if (pEv->code == MSC_SERIAL && pEv->value) + pUSB->state[pUSB->nChannel].values[WACOMFIELD_SERIAL].nValue = pEv->value; + if (!pUSB->state[pUSB->nChannel].values[WACOMFIELD_PROXIMITY].nValue) + pUSB->state[pUSB->nChannel].values[WACOMFIELD_SERIAL].nValue = 0; + return 0; +} + +static int USBParseKEY(USBTABLET* pUSB, struct input_event* pEv) +{ + int i, button=-1, tool=0; + switch (pEv->code) + { + case BTN_LEFT: button = WACOMBUTTON_LEFT; break; + case BTN_RIGHT: button = WACOMBUTTON_RIGHT; break; + case BTN_MIDDLE: button = WACOMBUTTON_MIDDLE; break; + case BTN_SIDE: button = WACOMBUTTON_SIDE; break; + case BTN_EXTRA: button = WACOMBUTTON_EXTRA; break; + case BTN_TOUCH: button = WACOMBUTTON_TOUCH; break; + case BTN_STYLUS: button = WACOMBUTTON_STYLUS; break; + case BTN_STYLUS2: button = WACOMBUTTON_STYLUS2; break; + case BTN_TOOL_PEN: tool = WACOMTOOLTYPE_PEN; break; + case BTN_TOOL_PENCIL: tool = WACOMTOOLTYPE_PENCIL; break; + case BTN_TOOL_BRUSH: tool = WACOMTOOLTYPE_BRUSH; break; + case BTN_TOOL_RUBBER: tool = WACOMTOOLTYPE_ERASER; break; + case BTN_TOOL_AIRBRUSH: tool = WACOMTOOLTYPE_AIRBRUSH; break; + case BTN_TOOL_MOUSE: tool = WACOMTOOLTYPE_MOUSE; break; + case BTN_TOOL_FINGER: tool = WACOMTOOLTYPE_PAD; break; + case BTN_TOOL_LENS: tool = WACOMTOOLTYPE_LENS; break; + case BTN_TOOL_DOUBLETAP: tool = WACOMTOOLTYPE_TOUCH; break; + default: + for (i = 0; i < gNumPadKeys; i++) + if (pEv->code == gPadKeys [i]) + { + button = WACOMBUTTON_BT0 + i; + break; + } + } + + /* if button was specified */ + if (button != -1) + { + /* button state change */ + if (pEv->value) + pUSB->state[pUSB->nChannel].values[WACOMFIELD_BUTTONS].nValue |= BIT(button); + else + pUSB->state[pUSB->nChannel].values[WACOMFIELD_BUTTONS].nValue &= ~BIT(button); + } + + /* if a tool was specified */ + if (tool) + { + /* coming into proximity */ + if (pEv->value) + { + /* no prior tool in proximity */ + if (!(pUSB->nToolProx[pUSB->nChannel] & BIT(tool))) + { + pUSB->state[pUSB->nChannel].values[WACOMFIELD_PROXIMITY].nValue = 1; + } + + /* remember tool in prox */ + pUSB->nToolProx[pUSB->nChannel] |= BIT(tool); + pUSB->state[pUSB->nChannel].values[WACOMFIELD_TOOLTYPE].nValue = tool; + } + + /* otherwise, going out of proximity */ + else + { + /* forget tool in prox */ + if (pUSB->nToolProx[pUSB->nChannel] & BIT(tool)) + { + pUSB->nToolProx[pUSB->nChannel] &= ~BIT(tool); + } + + /* single input */ + if (!(pUSB->state[pUSB->nChannel].uValid & BIT(WACOMFIELD_SERIAL))) + pUSB->nToolProx[pUSB->nChannel] = 0; + + pUSB->state[pUSB->nChannel].values[WACOMFIELD_PROXIMITY].nValue = 0; + pUSB->state[pUSB->nChannel].values[WACOMFIELD_TOOLTYPE].nValue = 0; + + /* nobody left? out of proximity */ + if (!pUSB->nToolProx[pUSB->nChannel]) + memset(pUSB->state[pUSB->nChannel].values, 0, + pUSB->state[pUSB->nChannel].uValueCnt * sizeof(WACOMVALUE)); + /* otherwise, switch to next tool */ + else + { + for (i=WACOMTOOLTYPE_PEN; inToolProx[pUSB->nChannel] & BIT(i)) + { + pUSB->state[pUSB->nChannel].values[WACOMFIELD_TOOLTYPE].nValue = i; + } + } + } + } /* out of prox */ + } /* end if tool */ + + return 0; +} + +static int USBParseABS(USBTABLET* pUSB, struct input_event* pEv) +{ + int field = 0; + switch (pEv->code) + { + case ABS_X: field = WACOMFIELD_POSITION_X; break; + case ABS_Y: field = WACOMFIELD_POSITION_Y; break; + case ABS_RZ: field = WACOMFIELD_ROTATION_Z; break; + case ABS_DISTANCE: field = WACOMFIELD_DISTANCE; break; + case ABS_PRESSURE: field = WACOMFIELD_PRESSURE; break; + case ABS_TILT_X: field = WACOMFIELD_TILT_X; break; + case ABS_TILT_Y: field = WACOMFIELD_TILT_Y; break; + case ABS_WHEEL: field = WACOMFIELD_ABSWHEEL; break; + case ABS_THROTTLE: field = WACOMFIELD_THROTTLE; break; + } + + if (field) + pUSB->state[pUSB->nChannel].values[field].nValue = pEv->value; + + return 0; +} + +static int USBParseREL(USBTABLET* pUSB, struct input_event* pEv) +{ + int field = 0; + switch (pEv->code) + { + case REL_WHEEL: field = WACOMFIELD_RELWHEEL; break; + } + + if (field) + pUSB->state[pUSB->nChannel].values[field].nValue = pEv->value; + + return 0; +} + +static int USBParseData(WACOMTABLET_PRIV* pTablet, + const unsigned char* puchData, unsigned int uLength, + WACOMSTATE* pState) +{ + int evcnt, chcnt; + USBTABLET* pUSB = (USBTABLET*)pTablet; + struct input_event* pEv = (struct input_event*)puchData; + if (uLength != sizeof(struct input_event)) return 1; + + /* store event until we receive a MSC_SERIAL/SYN_REPORT + * so we can figure out with channel to use */ + if (pUSB->nEventCnt >= MAX_USB_EVENTS) + { + /* no more buffer space */ + pUSB->nEventCnt = 0; + pUSB->nLastToolSerial = 0; + errno = ENOBUFS; + return -1; + } + pUSB->events[pUSB->nEventCnt++] = *pEv; + + if ((pEv->type == EV_MSC) && (pEv->code == MSC_SERIAL)) + { + /* store the serial for the tool for later use */ + pUSB->nLastToolSerial = pEv->value; +#ifdef EV_SYN + /* Kernel 2.4 uses MSC_SERIAL as an end-of-report, 2.6 + * don't so we're not done yet */ + return 0; + } + else if ((pEv->type == EV_SYN) && (pEv->code == SYN_REPORT)) + { + /* Kernel 2.6 used SYN_REPORT as an end-of-record, + * fall through */ +#endif + } + else + { + /* Not a MSC_SERIAL or SYN_REPORT, we're not done yet */ + return 0; + } + + /* Select channel here based on the serial number, tablets + * with only one channel will always use channel 0, so no + * check. + */ + if (pUSB->pDevice->uChannelCnt > 1) + { + pUSB->nChannel = -1; + /* Find existing channel */ + for (chcnt=0; chcntpDevice->uChannelCnt; chcnt++) + { + if (pUSB->state[chcnt].values[WACOMFIELD_SERIAL].nValue == pUSB->nLastToolSerial) + { + pUSB->nChannel = chcnt; + break; + } + } + + /* Find en empty channel */ + if(pUSB->nChannel == -1) + { + for (chcnt=0; chcntpDevice->uChannelCnt; chcnt++) + { + if(!pUSB->nToolProx[chcnt]) + { + pUSB->nChannel = chcnt; + break; + } + } + } + + /* no more channels?? */ + if(pUSB->nChannel == -1) + { + pUSB->nChannel = 0; + pUSB->nEventCnt = 0; + pUSB->nLastToolSerial = 0; + errno = ENOBUFS; + return -1; + } + } + + /* Channel found/allocated, lets process events */ + pUSB->state[pUSB->nChannel].values[WACOMFIELD_RELWHEEL].nValue = 0; + + for (evcnt=0; evcntnEventCnt; evcnt++) + { + pEv = pUSB->events + evcnt; + /* dispatch event */ + switch (pEv->type) + { +#ifdef EV_SYN + case EV_SYN: /* kernel 2.6 */ +#endif + case EV_MSC: /* kernel 2.4 */ + if (USBParseMSC(pUSB,pEv)) return pEv->type; break; + case EV_KEY: if (USBParseKEY(pUSB,pEv)) return pEv->type; break; + case EV_ABS: if (USBParseABS(pUSB,pEv)) return pEv->type; break; + case EV_REL: if (USBParseREL(pUSB,pEv)) return pEv->type; break; + default: errno = EINVAL; return pEv->type; + } + } + + pUSB->nEventCnt = 0; + pUSB->nLastToolSerial = 0; + return pState ? WacomCopyState(pState,pUSB->state + pUSB->nChannel) : 0; +} + +/***************************************************************************** +** End USB Linux Input Subsystem +*****************************************************************************/ + +#else /* WCM_ENABLE_LINUXWACOM */ + +WACOMTABLET WacomOpenUSBTablet(WACOMENGINE hEngine, int fd, WACOMMODEL* pModel) +{ + errno = EPERM; + return NULL; +} + +unsigned int WacomGetUSBDeviceFromName(const char* pszName) +{ + errno = ENOENT; + return 0; +} + +int WacomGetSupportedUSBDeviceList(WACOMDEVICEREC** ppList, int* pnSize) +{ + if (!ppList || !pnSize) { errno = EINVAL; return 1; } + *ppList = NULL; + *pnSize = 0; + return 0; +} + +#endif /* WCM_ENABLE_LINUXWACOM */ diff --git a/wacom/wacusb.h b/wacom/wacusb.h new file mode 100644 index 0000000..230051f --- /dev/null +++ b/wacom/wacusb.h @@ -0,0 +1,31 @@ +/***************************************************************************** +** wacusb.h +** +** Copyright (C) 2002, 2003 - John E. Joganic +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +****************************************************************************/ + +#ifndef __LINUXWACOM_WACUSB_H +#define __LINUXWACOM_WACUSB_H + +#include "wactablet.h" + +int WacomGetSupportedUSBDeviceList(WACOMDEVICEREC** ppList, int* pnSize); +unsigned int WacomGetUSBDeviceFromName(const char* pszName); +WACOMTABLET WacomOpenUSBTablet(WACOMENGINE hEngine, int fd, WACOMMODEL* pModel); + +#endif /* __LINUXWACOM_WACUSB_H */ diff --git a/wpa_supplicant.conf b/wpa_supplicant.conf new file mode 100644 index 0000000..7328388 --- /dev/null +++ b/wpa_supplicant.conf @@ -0,0 +1,5 @@ +ctrl_interface=wlan0 +update_config=1 +eapol_version=1 +ap_scan=1 +fast_reauth=1 diff --git a/x86.mk b/x86.mk new file mode 100644 index 0000000..a852186 --- /dev/null +++ b/x86.mk @@ -0,0 +1,34 @@ +# +# Copyright (C) 2014 The Android-x86 Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is a generic product that isn't specialized for a specific device. +# It includes the base Android-x86 platform. + +# Additional settings used in all AOSP builds +PRODUCT_PROPERTY_OVERRIDES := \ + ro.com.android.dateformat=MM-dd-yyyy \ + +# Get a list of languages. +$(call inherit-product,$(SRC_TARGET_DIR)/product/locales_full.mk) + +# Get everything else from the parent package +$(call inherit-product,$(SRC_TARGET_DIR)/product/generic.mk) + +# Get some sounds +$(call inherit-product-if-exists,frameworks/base/data/sounds/AudioPackage6.mk) + +$(call inherit-product,$(LOCAL_PATH)/device.mk) +$(call inherit-product,$(LOCAL_PATH)/packages.mk)