wacom: create a uhid device on live mode
uhid code taken from https://github.com/bentiss/hid-tools and stripped out from the not required parts here
This commit is contained in:
parent
d4dd672b2f
commit
d295e12310
|
@ -0,0 +1,214 @@
|
|||
#!/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from gi.repository import GObject
|
||||
import os
|
||||
import struct
|
||||
import uuid
|
||||
|
||||
|
||||
class UHIDUncompleteException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UHIDDevice(GObject.Object):
|
||||
__UHID_LEGACY_CREATE = 0
|
||||
UHID_DESTROY = 1
|
||||
UHID_START = 2
|
||||
UHID_STOP = 3
|
||||
UHID_OPEN = 4
|
||||
UHID_CLOSE = 5
|
||||
UHID_OUTPUT = 6
|
||||
__UHID_LEGACY_OUTPUT_EV = 7
|
||||
__UHID_LEGACY_INPUT = 8
|
||||
UHID_GET_REPORT = 9
|
||||
UHID_GET_REPORT_REPLY = 10
|
||||
UHID_CREATE2 = 11
|
||||
UHID_INPUT2 = 12
|
||||
UHID_SET_REPORT = 13
|
||||
UHID_SET_REPORT_REPLY = 14
|
||||
|
||||
UHID_FEATURE_REPORT = 0
|
||||
UHID_OUTPUT_REPORT = 1
|
||||
UHID_INPUT_REPORT = 2
|
||||
|
||||
def __init__(self, fd=None):
|
||||
GObject.Object.__init__(self)
|
||||
self._name = None
|
||||
self._phys = ''
|
||||
self._rdesc = None
|
||||
self.parsed_rdesc = None
|
||||
self._info = None
|
||||
if fd is None:
|
||||
self._fd = os.open('/dev/uhid', os.O_RDWR)
|
||||
else:
|
||||
self._fd = fd
|
||||
self.uniq = f'uhid_{str(uuid.uuid4())}'
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc_details):
|
||||
os.close(self._fd)
|
||||
|
||||
@GObject.Property
|
||||
def fd(self):
|
||||
return self._fd
|
||||
|
||||
@GObject.Property
|
||||
def rdesc(self):
|
||||
return self._rdesc
|
||||
|
||||
@rdesc.setter
|
||||
def rdesc(self, rdesc):
|
||||
self._rdesc = rdesc
|
||||
|
||||
@GObject.Property
|
||||
def phys(self):
|
||||
return self._phys
|
||||
|
||||
@phys.setter
|
||||
def phys(self, phys):
|
||||
self._phys = phys
|
||||
|
||||
@GObject.Property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
self._name = name
|
||||
|
||||
@GObject.Property
|
||||
def info(self):
|
||||
return self._info
|
||||
|
||||
@info.setter
|
||||
def info(self, info):
|
||||
self._info = info
|
||||
|
||||
@GObject.Property
|
||||
def bus(self):
|
||||
return self._info[0]
|
||||
|
||||
@GObject.Property
|
||||
def vid(self):
|
||||
return self._info[1]
|
||||
|
||||
@GObject.Property
|
||||
def pid(self):
|
||||
return self._info[2]
|
||||
|
||||
def call_set_report(self, req, err):
|
||||
buf = struct.pack('< L L H',
|
||||
UHIDDevice.UHID_SET_REPORT_REPLY,
|
||||
req,
|
||||
err)
|
||||
os.write(self._fd, buf)
|
||||
|
||||
def call_get_report(self, req, data, err):
|
||||
data = bytes(data)
|
||||
buf = struct.pack('< L L H H 4096s',
|
||||
UHIDDevice.UHID_GET_REPORT_REPLY,
|
||||
req,
|
||||
err,
|
||||
len(data),
|
||||
data)
|
||||
os.write(self._fd, buf)
|
||||
|
||||
def call_input_event(self, data):
|
||||
data = bytes(data)
|
||||
buf = struct.pack('< L H 4096s',
|
||||
UHIDDevice.UHID_INPUT2,
|
||||
len(data),
|
||||
data)
|
||||
os.write(self._fd, buf)
|
||||
|
||||
def create_kernel_device(self):
|
||||
if (self._name is None or
|
||||
self._rdesc is None or
|
||||
self._info is None):
|
||||
raise UHIDUncompleteException("missing uhid initialization")
|
||||
|
||||
buf = struct.pack('< L 128s 64s 64s H H L L L L 4096s',
|
||||
UHIDDevice.UHID_CREATE2,
|
||||
bytes(self._name, 'utf-8'), # name
|
||||
bytes(self._phys, 'utf-8'), # phys
|
||||
bytes(self.uniq, 'utf-8'), # uniq
|
||||
len(self._rdesc), # rd_size
|
||||
self.bus, # bus
|
||||
self.vid, # vendor
|
||||
self.pid, # product
|
||||
0, # version
|
||||
0, # country
|
||||
bytes(self._rdesc)) # rd_data[HID_MAX_DESCRIPTOR_SIZE]
|
||||
|
||||
n = os.write(self._fd, buf)
|
||||
assert n == len(buf)
|
||||
self.ready = True
|
||||
|
||||
def destroy(self):
|
||||
self.ready = False
|
||||
buf = struct.pack('< L',
|
||||
UHIDDevice.UHID_DESTROY)
|
||||
os.write(self._fd, buf)
|
||||
|
||||
def start(self, flags):
|
||||
print('start')
|
||||
|
||||
def stop(self):
|
||||
print('stop')
|
||||
|
||||
def open(self):
|
||||
print('open', self.sys_path)
|
||||
|
||||
def close(self):
|
||||
print('close')
|
||||
|
||||
def set_report(self, req, rnum, rtype, size, data):
|
||||
print('set report', req, rtype, size, [f'{d:02x}' for d in data[:size]])
|
||||
self.call_set_report(req, 1)
|
||||
|
||||
def get_report(self, req, rnum, rtype):
|
||||
print('get report', req, rnum, rtype)
|
||||
self.call_get_report(req, [], 1)
|
||||
|
||||
def output_report(self, data, size, rtype):
|
||||
print('output', rtype, size, [f'{d:02x}' for d in data[:size]])
|
||||
|
||||
def process_one_event(self):
|
||||
buf = os.read(self._fd, 4380)
|
||||
assert len(buf) == 4380
|
||||
evtype = struct.unpack_from('< L', buf)[0]
|
||||
if evtype == UHIDDevice.UHID_START:
|
||||
ev, flags = struct.unpack_from('< L Q', buf)
|
||||
self.start(flags)
|
||||
elif evtype == UHIDDevice.UHID_OPEN:
|
||||
self.open()
|
||||
elif evtype == UHIDDevice.UHID_STOP:
|
||||
self.stop()
|
||||
elif evtype == UHIDDevice.UHID_CLOSE:
|
||||
self.close()
|
||||
elif evtype == UHIDDevice.UHID_SET_REPORT:
|
||||
ev, req, rnum, rtype, size, data = struct.unpack_from('< L L B B H 4096s', buf)
|
||||
self.set_report(req, rnum, rtype, size, data)
|
||||
elif evtype == UHIDDevice.UHID_GET_REPORT:
|
||||
ev, req, rnum, rtype = struct.unpack_from('< L L B B', buf)
|
||||
self.get_report(req, rnum, rtype)
|
||||
elif evtype == UHIDDevice.UHID_OUTPUT:
|
||||
ev, data, size, rtype = struct.unpack_from('< L 4096s H B', buf)
|
||||
self.output_report(data, size, rtype)
|
|
@ -22,6 +22,7 @@ import uuid
|
|||
import errno
|
||||
from gi.repository import GObject
|
||||
from .drawing import Drawing
|
||||
from .uhid import UHIDDevice
|
||||
|
||||
logger = logging.getLogger('tuhi.wacom')
|
||||
|
||||
|
@ -54,6 +55,43 @@ class DeviceMode(enum.Enum):
|
|||
LIVE = 3
|
||||
|
||||
|
||||
wacom_live_rdesc_template = [
|
||||
0x05, 0x0d, # Usage Page (Digitizers) 0
|
||||
0x09, 0x02, # Usage (Pen) 2
|
||||
0xa1, 0x01, # Collection (Application) 4
|
||||
0x85, 0x01, # .Report ID (1) 6
|
||||
0x09, 0x20, # .Usage (Stylus) 8
|
||||
0xa1, 0x00, # .Collection (Physical) 10
|
||||
0x09, 0x32, # ..Usage (In Range) 12
|
||||
0x15, 0x00, # ..Logical Minimum (0) 14
|
||||
0x25, 0x01, # ..Logical Maximum (1) 16
|
||||
0x95, 0x01, # ..Report Count (1) 18
|
||||
0x75, 0x01, # ..Report Size (1) 20
|
||||
0x81, 0x02, # ..Input (Data,Var,Abs) 22
|
||||
0x95, 0x07, # ..Report Count (7) 24
|
||||
0x81, 0x03, # ..Input (Cnst,Var,Abs) 26
|
||||
0x05, 0x01, # ..Usage Page (Generic Desktop) 43
|
||||
0x09, 0x30, # ..Usage (X) 45
|
||||
0x75, 0x10, # ..Report Size (16) 47
|
||||
0x95, 0x01, # ..Report Count (1) 49
|
||||
0x55, 0x0e, # ..Unit Exponent (-2) 51
|
||||
0x65, 0x11, # ..Unit (Centimeter,SILinear) 53
|
||||
0x46, 0xec, 0x09, # ..Physical Maximum (2540) 55
|
||||
0x26, 0x80, 0x25, # ..Logical Maximum (9600) 58
|
||||
0x81, 0x02, # ..Input (Data,Var,Abs) 61
|
||||
0x09, 0x31, # ..Usage (Y) 63
|
||||
0x46, 0x9d, 0x06, # ..Physical Maximum (1693) 65
|
||||
0x26, 0x20, 0x1c, # ..Logical Maximum (7200) 68
|
||||
0x81, 0x02, # ..Input (Data,Var,Abs) 71
|
||||
0x05, 0x0d, # ..Usage Page (Digitizers) 73
|
||||
0x09, 0x30, # ..Usage (Tip Pressure) 75
|
||||
0x26, 0x00, 0x01, # ..Logical Maximum (256) 77
|
||||
0x81, 0x02, # ..Input (Data,Var,Abs) 80
|
||||
0xc0, # .End Collection 82
|
||||
0xc0, # End Collection 83
|
||||
]
|
||||
|
||||
|
||||
def signed_char_to_int(v):
|
||||
return int.from_bytes([v], byteorder='little', signed=True)
|
||||
|
||||
|
@ -277,6 +315,7 @@ class WacomProtocolBase(WacomProtocolLowLevelComm):
|
|||
self._uuid = uuid
|
||||
self._timestamp = 0
|
||||
self.pen_data_buffer = []
|
||||
self._uhid_device = None
|
||||
|
||||
device.connect_gatt_value(WACOM_CHRC_LIVE_PEN_DATA_UUID,
|
||||
self._on_pen_data_changed)
|
||||
|
@ -307,11 +346,19 @@ class WacomProtocolBase(WacomProtocolLowLevelComm):
|
|||
while data:
|
||||
if bytes(data) == b'\xff\xff\xff\xff\xff\xff':
|
||||
logger.debug(f'Pen left proximity')
|
||||
|
||||
if self._uhid_device is not None:
|
||||
self._uhid_device.call_input_event([1, 0, 0, 0, 0, 0, 0, 0])
|
||||
|
||||
else:
|
||||
x = int.from_bytes(data[0:2], byteorder='little')
|
||||
y = int.from_bytes(data[2:4], byteorder='little')
|
||||
pressure = int.from_bytes(data[4:6], byteorder='little')
|
||||
logger.debug(f'New Pen Data: ({x},{y}), pressure: {pressure}')
|
||||
|
||||
if self._uhid_device is not None:
|
||||
self._uhid_device.call_input_event([1, 1, *data[:6]])
|
||||
|
||||
data = data[6:]
|
||||
self._timestamp += 5
|
||||
|
||||
|
@ -389,10 +436,18 @@ class WacomProtocolBase(WacomProtocolLowLevelComm):
|
|||
expected_opcode=0xb3,
|
||||
arguments=args)
|
||||
|
||||
def start_live(self, uhid):
|
||||
def start_live(self, fd):
|
||||
self.send_nordic_command_sync(command=0xb1,
|
||||
expected_opcode=0xb3)
|
||||
logger.debug(f'Starting wacom live mode on fd: {uhid}')
|
||||
logger.debug(f'Starting wacom live mode on fd: {fd}')
|
||||
|
||||
rdesc = wacom_live_rdesc_template
|
||||
uhid_device = UHIDDevice(fd)
|
||||
uhid_device.rdesc = rdesc
|
||||
uhid_device.name = self.device.name
|
||||
uhid_device.info = (5, 0x056a, 0x0001)
|
||||
uhid_device.create_kernel_device()
|
||||
self._uhid_device = uhid_device
|
||||
|
||||
def stop_live(self):
|
||||
args = [0x02]
|
||||
|
|
Loading…
Reference in New Issue