📜 ⬆️ ⬇️

We write the driver of the user environment for uinput on Raspberry Pi

Display photography For the sake of one of my small projects on the Raspberry Pi 2, I purchased a Waveshare capacitive touchscreen display with affordable pricing, modest resolution and questionable support. In the box with the display lay a DVD-R DL, and according to the seller, there were images of systems based on Raspbian. I could not read them, searching for solutions on the Internet suggested that the driver that lay there was not the best solution anyway (already compiled kernel without source codes).

In the process of searching, I stumbled upon the project of one guy from friendly China. Thanks to him, I was able to come to my decision.

What's the matter


The fact is that the company provided only a binary driver for its display, linking it with the raspbian kernel. This is good, as long as you stay on your own core and do not want to change anything and not conduct serious embedded-development. But as soon as you switch to buildroot instead of Debian, change the compiler, rebuild your kernel, and so on, you will not have any driver compatible with your newest OS.

In such cases, the user environment driver helps out, which is a program that transmits data to the kernel module in a form that is understandable to Linux.
')

Finding a solution


If we look at the dmesg log, we will see lines that are interesting to us:

[ 3.518144] usb 1-1.5: new full-speed USB device number 4 using dwc_otg [ 3.606036] udevd[174]: starting version 175 [ 3.631476] usb 1-1.5: New USB device found, idVendor=0eef, idProduct=0005 [ 3.641195] usb 1-1.5: New USB device strings: Mfr=1, Product=2, SerialNumber=3 [ 3.653540] usb 1-1.5: Product: By ZH851 [ 3.659956] usb 1-1.5: Manufacturer: RPI_TOUCH [ 3.659967] usb 1-1.5: SerialNumber: \xffffffc2\xffffff84\xffffff84\xffffffc2\xffffffa0\xffffffa0B54711U335 [ 3.678577] hid-generic 0003:0EEF:0005.0001: hiddev0,hidraw0: USB HID v1.10 Device [RPI_TOUCH By ZH851] on usb-bcm2708_usb-1.5/input0 

Mr. derekhe from the githab pointed to the kernel build flag (I, unfortunately, did not have a kernel):

 CONFIG_USB_EGALAX_YZH=y 

This leads to the conclusion that the device is (or imitates) a clone of the eGalaxy sensor, and Waveshare simply interrupted the USB VendorId: ProductId. One way or another, the kernel creates a device like hidraw, into which the touch screen spits data of 25 bytes.

 i@raspberrypi ~ $ sudo xxd -c 25 /dev/hidraw0 0000000: aa01 01bf 00bc bb01 0056 0019 018c 00ef 00b4 02ce 01d7 0182 cc .........V............... 00000af: aa01 01bf 00bc bb01 0056 0019 018c 00ef 00b4 02ce 01d7 0182 cc .........V............... ... 00000fa: aa00 0000 0000 bb00 0000 0000 0000 0000 0000 0000 0000 0000 00 ......................... 

Poking fingers into the screen, you can parse the message format:


The driver found may be a quick fix, but I didn’t like it for the following reasons:

  1. I think writing drivers in an interpreted language in Embedded is not the best idea.
  2. Driver simulates mouse behavior and ignores multitouch

It was decided to arrange a challenge and write my own driver for uinput on C with multitouch and udev.

For the lazy: the source code for the raw driver version is here .

Solution to the problem


First of all, you need to realize that everything that is plugged into the USB can be poked out, and therefore you need to take into account the hot connection to the port.
Linux has a wonderful libudev library that allows you to enumerate connected devices and monitor the addition / removal of devices.

One of the drawbacks of our case is that the hidraw driver does not carry information about the VendorId: ProductId of the USB device to which it is attached. Therefore, you need to do a device enumeration by the hidraw driver, and then look for the parent USB device with the identifiers specified by us.
Important: if we want to check the already connected devices and monitor the addition / removal, you must perform the steps in the following order (and nothing else):

  1. Initialize udev_monitor
  2. Request enumeration of connected udev_enumerate_get_list_entry devices
  3. Start udev_monitor polling cycle

Getting a list of devices


With the help of the udev_enumerate_add_match_subsystem function and udev_monitor_filter_add_match_subsystem_devtype, we eliminate some of the devices that are irrelevant to us. When we receive a pointer to the hidraw device we need, we need to check whether it is ours:

 int try_init_device(struct udev_device *dev) { struct udev_device *devusb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"); if (devusb) { if (strcmp(udev_device_get_sysattr_value(devusb, "idVendor"), DEVICE_ID_VENDOR) == 0 && strcmp(udev_device_get_sysattr_value(devusb, "idProduct"), DEVICE_ID_PRODUCT) == 0) { return try_start_device_loop(udev_device_get_devnode(dev)); } } return -2; } 

If the condition is fulfilled, then we start in a separate thread the event processing cycle from the touch screen. The loop reads data from / dev / hidraw * and writes commands to the / dev / input / eventX allocated to it.
The Linux documentation describes two options for implementing the uinput driver for the touch panel:

Type A:

  ABS_MT_POSITION_X x[0] ABS_MT_POSITION_Y y[0] SYN_MT_REPORT ABS_MT_POSITION_X x[1] ABS_MT_POSITION_Y y[1] SYN_MT_REPORT SYN_REPORT 

Type B:

  ABS_MT_SLOT 0 ABS_MT_TRACKING_ID 45 ABS_MT_POSITION_X x[0] ABS_MT_POSITION_Y y[0] ABS_MT_SLOT 1 ABS_MT_TRACKING_ID 46 ABS_MT_POSITION_X x[1] ABS_MT_POSITION_Y y[1] SYN_REPORT 

Since it is long and algorithmically difficult to implement the second protocol, we will implement the first one. You must declare device characteristics, such as the name, bus, vendor and product ID, axis resolution, and allowable events for the device.

  struct uinput_user_dev user_dev; memset(&user_dev, 0, sizeof(struct uinput_user_dev)); user_dev.absmin[ABS_MT_POSITION_X] = 0; user_dev.absmax[ABS_MT_POSITION_X] = DEVICE_WIDTH; user_dev.absmin[ABS_MT_POSITION_Y] = 0; user_dev.absmax[ABS_MT_POSITION_Y] = DEVICE_HEIGHT; user_dev.id.bustype = BUS_USB; user_dev.id.vendor = DEVICE_ID_VENDOR_HEX; user_dev.id.product = DEVICE_ID_PRODUCT_HEX; user_dev.id.version = 1; strcpy(user_dev.name, "Waveshare multitouch screen"); 

Since we are working with a multitouch device, we have ABS_MT_POSITION_X and ABS_MT_POSITION_Y axes, respectively. After opening the device, we declare the types of events:

  int abs_axes[] = { ABS_MT_POSITION_X, ABS_MT_POSITION_Y }; int i; for (i = 0; i < 2; ++i) { if (suinput_enable_event(uinput_fd, EV_ABS, abs_axes[i]) == -1) { close(uinput_fd); entry.thread = 0; pthread_exit(NULL); return 0; } } 

We read our port in a loop and, in accordance with the protocol parsed above, create events for uinput. Each pressed point requires the event ABS_MT_POSITION_X, ABS_MT_POSITION_Y and SYN_MT_REPORT. If there are no more clicks, then SYN_MT_REPORT is transmitted. At the end of each packet (a set of points or an event stating that there are no more), you must call SYN_REPORT.

Assembly


The driver depends on libsuinput , pthreads, libudev and the C99 compiler. For assembly, all of this must be present in the build environment:

 gcc -std=c99 -Wall ./waveshare.c -pthread -lsuinput -ludev -o waveshare-touch-driver 

Run the application as the superuser in the background:

 sudo waveshare-touch-driver & 

And we check the created device (neither keyboard nor mouse was connected to my raspberry, therefore / dev / input / event0):

Hidden text
 pi@raspberrypi ~ $ evtest /dev/input/event0 Input driver version is 1.0.1 Input device ID: bus 0x3 vendor 0xeef product 0x5 version 0x1 Input device name: "Waveshare multitouch screen" Supported events: Event type 0 (EV_SYN) Event type 3 (EV_ABS) Event code 53 (ABS_MT_POSITION_X) Value 0 Min 0 Max 800 Event code 54 (ABS_MT_POSITION_Y) Value 0 Min 0 Max 480 Properties: Testing ... (interrupt to exit) ... Event: time 20159.696497, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 728 Event: time 20159.696497, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 41 Event: time 20159.696497, ++++++++++++++ SYN_MT_REPORT ++++++++++++ Event: time 20159.696497, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 595 Event: time 20159.696497, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 154 Event: time 20159.696497, ++++++++++++++ SYN_MT_REPORT ++++++++++++ Event: time 20159.696497, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 456 Event: time 20159.696497, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 145 Event: time 20159.696497, ++++++++++++++ SYN_MT_REPORT ++++++++++++ Event: time 20159.696497, -------------- SYN_REPORT ------------ Event: time 20159.728497, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 728 Event: time 20159.728497, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 41 Event: time 20159.728497, ++++++++++++++ SYN_MT_REPORT ++++++++++++ Event: time 20159.728497, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 595 Event: time 20159.728497, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 154 Event: time 20159.728497, ++++++++++++++ SYN_MT_REPORT ++++++++++++ Event: time 20159.728497, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 456 Event: time 20159.728497, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 145 Event: time 20159.728497, ++++++++++++++ SYN_MT_REPORT ++++++++++++ Event: time 20159.728497, -------------- SYN_REPORT ------------ Event: time 20159.760360, ++++++++++++++ SYN_MT_REPORT ++++++++++++ Event: time 20159.760360, -------------- SYN_REPORT ------------ 


The device operates in accordance with the standard Linux Kernel.

Existing problems


At the moment there are two problems:


Sample application from examples for Qt 5.5 quick / touchinteractions (I apologize for the disgusting quality of the photo).

example

If you find errors in the driver / design jambs, I will be glad to accept them, since this is my first “serious” program written in C.

Once again the link to the githab.

Source: https://habr.com/ru/post/267655/


All Articles