<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>-EMAINLINE</title>
	<subtitle>Things about porting Linux to mobile devices</subtitle>
	<id isPermaLink="true">https://emainline.gitlab.io/</id>
	<link>https://emainline.gitlab.io/</link>
	<updated>Mon, 05 Dec 2022 17:48:10 +0000</updated>
	<generator>Jekyll v4.2.2</generator>
	
	<entry>
		
		
		<title>A first photo on mainline Linux</title>
		<description>What it takes to enable a camera of an Android phone on mainline Linux and start capturing photos</description>
		<author>Yassine Oudjana</author>
		<published>Wed, 24 Aug 2022 00:00:00 +0000</published>
		<content type="html">&lt;p&gt;&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/cover/camera.jpg&quot; width=&quot;100%&quot; /&gt;
Cameras on phones have gotten so good that they are often taken for granted. They are expected to always work and produce good quality photos with fairly accurate colors. Those trying to make them work on a Linux phone for the first time would not have it as easy though, as they get to discover the several components involved in long complicated processes to arrive at a ready-to-use JPEG image. This is why cameras remain one of, if not the least supported feature of all Linux phones, whether originally made to run Linux or had it ported to them.&lt;/p&gt;

&lt;p&gt;To find out how it all works, an attempt is made to enable the main camera on a Xiaomi Mi Note 2 on mainline Linux.&lt;/p&gt;

&lt;h2 id=&quot;understanding-the-hardware&quot;&gt;Understanding the hardware&lt;/h2&gt;
&lt;p&gt;What the average end-user might know about this phone is that it has a Sony IMX318 main camera with a native resolution of 5488x4112 adding up to 22.56 MP. Some might also know that the SoC on the phone, the Qualcomm Snapdragon 821, has an ISP – short for &lt;em&gt;Image Signal Processor&lt;/em&gt; – that helps with processing photos. To enable the camera on Linux for the first time would require a much lower-level understanding of the hardware, however. A quick look at the schematics of this phone would be a good way to begin.&lt;/p&gt;

&lt;p&gt;Searching for “camera” shows a few interesting blocks. This one labeled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REAR CAMERA&lt;/code&gt; seems like a good starting point:
&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/content/rear_camera_block.png&quot; width=&quot;100%&quot; /&gt;
This show several lines coming out of a socket, which the camera module presumably plugs into. Starting with the obvious, there are a few lines going up. These are rails that supply power to the camera module. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CAM_MCLK0&lt;/code&gt; must be a clock, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CAM1_RST_N&lt;/code&gt; a reset line. As for data, two categories of lines can be seen: One with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CCI_I2C&lt;/code&gt; prefix, and another with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MIPI_CSI&lt;/code&gt;. CCI, or &lt;em&gt;Camera Control Interface&lt;/em&gt;, is just an I&lt;sup&gt;2&lt;/sup&gt;C bus dedicated to controlling the camera module. Since I&lt;sup&gt;2&lt;/sup&gt;C has quite a low bandwidth, it would not be adequate to carry image data from the sensor, hence the use of the &lt;a href=&quot;https://www.mipi.org/specifications/csi-2&quot;&gt;&lt;em&gt;MIPI Camera Serial Interface&lt;/em&gt;&lt;/a&gt;. MIPI CSI is a high bandwidth serial bus designed specifically for transferring image data from cameras.&lt;/p&gt;

&lt;p&gt;Another block labeled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FRONT CAMERA&lt;/code&gt; is also shown in the schematic diagram:
 &lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/content/front_camera_block.png&quot; width=&quot;100%&quot; /&gt;
It shows a similar setup, with some power rails, a clock line, a reset line, as well as CCI and CSI lines.&lt;/p&gt;
&lt;h2&gt;&lt;TODO maybe=&quot;&quot; put=&quot;&quot; SoC=&quot;&quot; part=&quot;&quot; here=&quot;&quot;&gt;&lt;/TODO&gt;&lt;/h2&gt;

&lt;h2 id=&quot;a-closer-look-at-the-camera-module&quot;&gt;A closer look at the camera module&lt;/h2&gt;
&lt;p&gt;A camera module consists of an image sensor that collects light and outputs signals to recreate the image digitally, as well as a lens to focus light falling on the sensor. Depending on the camera module, there can also be an actuator that physically moves the lens to change the focus distance. Many camera modules found on phones these days have multiple sets of sensors and lenses with different focal lengths for added flexibility. The main camera on the Mi Note 2 – as mentioned previously – has a Sony IMX318 image sensor. This camera can also change focus distance both automatically and manually, so it must have a lens actuator. The image sensor is the most important part of the operation, so it will be the focus target*.&lt;/p&gt;

&lt;figcaption&gt;*pun intended.&lt;/figcaption&gt;

&lt;p&gt;Documentation would become necessary to learn more about the image sensor. To write a driver for the sensor would of course require at least a register map with definitions, which is sometimes included in the datasheet, or found in a separate &lt;em&gt;Technical Reference Manual&lt;/em&gt; or &lt;em&gt;Functional Specification&lt;/em&gt;. Unfortunately, there are no publicly available documents to be found, other than a product description including general specifications of the sensor.&lt;/p&gt;

&lt;p&gt;With this phone originally running Android, the downstream kernel can be taken as an alternative source of information. In this case however, the vendor decided to use a proprietary userspace driver, meaning there is no source code showing how the sensor is controlled.&lt;/p&gt;

&lt;h2 id=&quot;dead-end&quot;&gt;Dead end?&lt;/h2&gt;
&lt;p&gt;Having no documentation, will the camera require yet another reverse engineering
effort? We must go deeper.&lt;/p&gt;

&lt;p&gt;Having a proprietary driver for the camera on this phone does not necessarily mean that it is done similarly in other phones, so there might be an open source kernel driver in another downstream kernel for a different device. Searching for “IMX318” across GitHub reveals a couple of drivers, &lt;a href=&quot;https://github.com/150balbes/Jetson-Nano/blob/19a504de0ae7045bd5c8741871f7f7d4c3dfa3de/nvidia/drivers/media/i2c/imx318.c&quot;&gt;one&lt;/a&gt; belonging to a NVIDIA Jetson Nano kernel, and &lt;a href=&quot;https://github.com/mediatek-android-development/android_kernel_mediatek_mt6757CD-4.4/blob/79bd1255c4d403669d3e607c58f318a124337118/drivers/misc/mediatek/imgsensor/src/mt6757/imx318_mipi_raw/imx318mipi_Sensor.c&quot;&gt;the other&lt;/a&gt; to some common MediaTek MT6757 kernel.&lt;/p&gt;

&lt;figcaption&gt;&lt;i&gt;
Who knew that two of the least open source-friendly vendors would become the most helpful one day...
&lt;/i&gt;&lt;/figcaption&gt;

&lt;p&gt;Before looking at the downstream drivers however, it would be better to take a look at an existing mainline driver for a similar sensor to gain a general understanding of how these drivers are constructed, and figure out what needs to be known to write a new one. I&lt;sup&gt;2&lt;/sup&gt;C-controlled camera sensor drivers are located in &lt;a href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/media/i2c?h=v5.19&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;drivers/media/i2c&lt;/code&gt;&lt;/a&gt;. Here, several drivers for Sony sensors are found, distingushed by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imx&lt;/code&gt; prefix in their names.&lt;/p&gt;

&lt;h2 id=&quot;a-look-at-an-existing-driver&quot;&gt;A look at an existing driver&lt;/h2&gt;
&lt;p&gt;Almost every Linux driver starts at a probe function where all necessary initialization steps are made to prepare the hardware for operation, as well as to register the driver to the relevant kernel subsystems. Looking at the &lt;a href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/media/i2c/imx214.c?h=v5.19#n945&quot;&gt;IMX214 driver&lt;/a&gt;, the general flow of events goes like this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The device tree node is first parsed, endpoints are identified and link frequency compatibility is tested:
    &lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imx214_parse_fwnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;imx214_parse_fwnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fwnode_handle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v4l2_fwnode_endpoint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bus_cfg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bus_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;V4L2_MBUS_CSI2_DPHY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
      &lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;endpoint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fwnode_graph_get_next_endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev_fwnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;endpoint node not found&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EINVAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v4l2_fwnode_endpoint_alloc_parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bus_cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;parsing endpoint node failed&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bus_cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nr_of_link_frequencies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bus_cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link_frequencies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMX214_DEFAULT_LINK_FREQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bus_cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nr_of_link_frequencies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;link-frequencies %d not supported, Please review your DT&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;IMX214_DEFAULT_LINK_FREQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EINVAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nl&quot;&gt;done:&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;v4l2_fwnode_endpoint_free&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bus_cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fwnode_handle_put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;The external clock is located and configured:
    &lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xclk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devm_clk_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_ERR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xclk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;could not get xclk&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PTR_ERR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xclk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clk_set_rate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xclk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMX214_DEFAULT_CLK_FREQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;could not set xclk frequency&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Regulators and GPIOs are parsed:
    &lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imx214_get_regulators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cannot get regulators&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enable_gpio&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devm_gpiod_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;enable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GPIOD_OUT_LOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_ERR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enable_gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cannot get enable gpio&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PTR_ERR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enable_gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;A V4L2 subdevice is initialized,
    &lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;n&quot;&gt;v4l2_i2c_subdev_init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214_subdev_ops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;and its controls are configured:&lt;/p&gt;
    &lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;n&quot;&gt;v4l2_ctrl_handler_init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pixel_rate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v4l2_ctrl_new_std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;n&quot;&gt;V4L2_CID_PIXEL_RATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;n&quot;&gt;IMX214_DEFAULT_PIXEL_RATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;n&quot;&gt;IMX214_DEFAULT_PIXEL_RATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link_freq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v4l2_ctrl_new_int_menu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;n&quot;&gt;V4L2_CID_LINK_FREQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;n&quot;&gt;ARRAY_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link_freq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link_freq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link_freq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link_freq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;V4L2_CTRL_FLAG_READ_ONLY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exposure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v4l2_ctrl_new_std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214_ctrl_ops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                       &lt;span class=&quot;n&quot;&gt;V4L2_CID_EXPOSURE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                       &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3184&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x0c70&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v4l2_ctrl_new_std_compound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;V4L2_CID_UNIT_CELL_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;v4l2_ctrl_ptr_create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%s control init failed (%d)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;__func__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;free_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrl_handler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mutex_init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Then finally a media entity is configured and the V4L2 subdevice is registered:
    &lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;V4L2_SUBDEV_FL_HAS_DEVNODE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MEDIA_PAD_FL_SOURCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MEDIA_ENT_F_CAM_SENSOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;media_entity_pads_init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;could not register media entity&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;free_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;imx214_entity_init_cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v4l2_async_register_subdev_sensor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx214&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;could not register v4l2 device&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;free_entity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;infrastructure&quot;&gt;Infrastructure&lt;/h3&gt;
&lt;p&gt;These steps show some basic elements the sensor needs, namely clocks and regulators. The sensor takes in one clock signal and uses it internally to generate all clock signals it needs to function. This is shown as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xclk&lt;/code&gt; (short for &lt;em&gt;external clock&lt;/em&gt;) in the driver. A second clock that is part of the MIPI CSI bus is used to synchronize the CSI links, the frequency of which is referred to as &lt;em&gt;link frequency&lt;/em&gt; in the driver. The frequency read from the device tree must be supported by the sensor, hence the compatibility checking. Link frequency will become important later.&lt;/p&gt;

&lt;p&gt;As for power, the sensor needs three supplies: one to power the digital part of the IC, one to power its analog part, and one to pull-up I/O lines.&lt;/p&gt;

&lt;h3 id=&quot;v4l2&quot;&gt;V4L2&lt;/h3&gt;
&lt;p&gt;Short for &lt;a href=&quot;https://www.kernel.org/doc/html/stable/media/uapi/v4l/v4l2.html&quot;&gt;Video for Linux version 2&lt;/a&gt;, V4L2 is a Linux API for managing video devices of all kinds, including codecs, TV tuners, multiplexers and of course, cameras. It allows for dealing with complicated devices as sets of simpler subdevices, each having its own controls, as well as &lt;em&gt;source&lt;/em&gt; and &lt;em&gt;sink&lt;/em&gt; pads to link it with other subdevices and control the flow of data through a &lt;em&gt;pipeline&lt;/em&gt;. The kernel side of V4L2 is used throughout the sensor driver.&lt;/p&gt;

&lt;h2 id=&quot;back-to-the-imx318&quot;&gt;Back to the IMX318&lt;/h2&gt;
&lt;p&gt;The first thing to do would be to power on the sensor. To do so, the power rails it is hooked up to must be brought up, the external clock must be enabled and set to a suitable frequency, and the reset line must be asserted then deasserted to reset the sensor. It might seem simple at first, but as it would turn out later, it needs to be done in a specific way in order to get the sensor into a working state.&lt;/p&gt;

&lt;p&gt;A good first step to find the correct power on sequence would be to see how the stock driver does it. Although the vendor decided to use a proprietary blob to drive the sensor, the kernel remains open-source, and anything regarding clocks, regulators or GPIOs goes through it at some point. Conveniently, they decided to have the userspace driver give the kernel instructions on a power sequence which it would then execute. Even more so, the parsing and execution of the instructions happens in a single function named &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c#L1531-L1733&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msm_camera_power_up&lt;/code&gt;&lt;/a&gt;. It even has dynamic debug messages that if enabled, would allow for monitoring of the power on process as it happens. This function calls &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c#L1410-L1462&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msm_cam_sensor_handle_reg_gpio&lt;/code&gt;&lt;/a&gt; for enabling regulators, so debug messages must be enabled there too. Once all relevant messages are enabled, the sequence can be seen in the kernel log:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[   12.194390] msm_camera_power_up:1538
[   12.194989] msm_camera_power_up index 0
[   12.194994] msm_camera_power_up type 1
[   12.194998] msm_camera_power_up:1607 gpio set 86 val 0
[   12.196751] msm_camera_power_up index 1
[   12.196758] msm_camera_power_up type 1
[   12.196761] msm_camera_power_up:1607 gpio set 30 val 0
[   12.198801] msm_camera_power_up index 2
[   12.198808] msm_camera_power_up type 2
[   12.199424] msm_cam_sensor_handle_reg_gpio: 1420 Seq val: 2, config: 1
[   12.199429] msm_cam_sensor_handle_reg_gpio: 1454 GPIO offset: 4, seq_val: 2
[   12.199433] msm_camera_power_up index 3
[   12.199435] msm_camera_power_up type 2
[   12.199888] msm_cam_sensor_handle_reg_gpio: 1420 Seq val: 0, config: 1
[   12.199892] msm_cam_sensor_handle_reg_gpio: 1454 GPIO offset: 5, seq_val: 0
[   12.199895] msm_camera_power_up index 4
[   12.199898] msm_camera_power_up type 2
[   12.201948] msm_cam_sensor_handle_reg_gpio: 1420 Seq val: 1, config: 1
[   12.201954] msm_cam_sensor_handle_reg_gpio: 1454 GPIO offset: 3, seq_val: 1
[   12.201960] msm_camera_power_up index 5
[   12.201963] msm_camera_power_up type 2
[   12.202577] msm_cam_sensor_handle_reg_gpio: 1420 Seq val: 3, config: 1
[   12.202582] msm_cam_sensor_handle_reg_gpio: 1454 GPIO offset: 6, seq_val: 3
[   12.202587] msm_camera_power_up index 6
[   12.202590] msm_camera_power_up type 0
[   12.203887] msm_camera_power_up index 7
[   12.203894] msm_camera_power_up type 1
[   12.203898] msm_camera_power_up:1607 gpio set 86 val 2
[   12.205735] MSM-CPP cpp_init_hardware:1011 CPP HW Version: 0x60000000
[   12.205745] MSM-CPP cpp_init_hardware:1029 stream_cnt:0
[   12.214102] msm_camera_power_up index 8
[   12.214109] msm_camera_power_up type 1
[   12.214113] msm_camera_power_up:1607 gpio set 30 val 2
[   12.219075] msm_pm_qos_update_request: update request -1
[   12.219080] msm_pm_qos_add_request: add request
[   12.225387] msm_cci_init:1434: hw_version = 0x10040000
[   12.225466] msm_camera_power_up exit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This might look like a lot at first glance, but it basically does the following:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Set GPIO 86 to LOW,&lt;/li&gt;
  &lt;li&gt;Set GPIO 30 to LOW,&lt;/li&gt;
  &lt;li&gt;Enable V&lt;sub&gt;ANA&lt;/sub&gt; supply,&lt;/li&gt;
  &lt;li&gt;Enable V&lt;sub&gt;DIG&lt;/sub&gt; supply,&lt;/li&gt;
  &lt;li&gt;Enable V&lt;sub&gt;I/O&lt;/sub&gt; supply,&lt;/li&gt;
  &lt;li&gt;Enable V&lt;sub&gt;AF&lt;/sub&gt; supply,&lt;/li&gt;
  &lt;li&gt;Enable external clock,&lt;/li&gt;
  &lt;li&gt;Set GPIO 86 to HIGH,&lt;/li&gt;
  &lt;li&gt;Set GPIO 30 to HIGH.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Supplies are found using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GPIO offset&lt;/code&gt; values shown in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msm_cam_sensor_handle_reg_gpio&lt;/code&gt; messages, which correspond to entries in an &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/include/uapi/media/msm_camsensor_sdk.h#L108-L111&quot;&gt;enum&lt;/a&gt; defined in a separate header file.&lt;/p&gt;

&lt;p&gt;Looking at schematics, GPIO 30 is found to be the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CAM1_RST_N&lt;/code&gt; reset pin seen before:
&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/content/rear_camera_reset_pin.png&quot; width=&quot;80%&quot; /&gt;
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_N&lt;/code&gt; suffix suggests that it is active low, which is to say that it is asserted by setting it to LOW, and deasserted by setting it to HIGH.&lt;/p&gt;

&lt;p&gt;As for GPIO 86, its name suggests that it is an enable pin of a regulator:
&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/content/rear_camera_ldo_en_pin.png&quot; width=&quot;80%&quot; /&gt;
Following it, we end up at what seems to be a LDO (short for &lt;a href=&quot;https://en.wikipedia.org/wiki/Low-dropout_regulator&quot;&gt;&lt;em&gt;Low-Dropout Regulator&lt;/em&gt;&lt;/a&gt;):
&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/content/rear_camera_ldo.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With the GPIOs figured out, now come regulators, which can be found in &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/msm8996-camera-sensor-a4.dtsi#L353-L357&quot;&gt;downstream DTS&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cam_vdig-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pm8994_s3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cam_vio-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pm8994_lvs1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cam_vana-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pm8994_l17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cam_vmipi-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pm8994_l11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;qcom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cam-vreg-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cam_vdig&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cam_vio&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cam_vana&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cam_vmipi&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;V&lt;sub&gt;AF&lt;/sub&gt; supply can be found separately in the &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/msm8996-camera-sensor-a4.dtsi#L31-L32&quot;&gt;actuator node&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cam_vaf-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pm8994_l23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;qcom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cam-vreg-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cam_vaf&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There does not seem to be any references to the LDO found in schematics earlier. However, when looking closely, it can be noticed that V&lt;sub&gt;DIG&lt;/sub&gt; supply is the same as V&lt;sub&gt;IN&lt;/sub&gt; of the LDO. This would suggest that V&lt;sub&gt;DIG&lt;/sub&gt; is actually the LDO output rather than S3 of the PM8994 PMIC. In fact, this pattern is frequently seen in the downstream kernel, where an LDO (or any kind of external regulator with fixed voltage output and an enable pin) is not described in DTS, and its enable pin is treated as if it were of the component the regulator supplies power to.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/msm8996-camera-sensor-a4.dtsi#L379-L382&quot;&gt;external clock and its frequency&lt;/a&gt; are also found in downstream DTS:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;clocks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clock_mmss&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clk_mclk0_clk_src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;,&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clock_mmss&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clk_camss_mclk0_clk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;clock-names&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cam_src_clk&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cam_clk&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;qcom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clock-rates&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;24000000&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Putting all of this together, the power sequence would look like this:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Disable V&lt;sub&gt;DIG&lt;/sub&gt; supply,&lt;/li&gt;
  &lt;li&gt;Assert reset&lt;/li&gt;
  &lt;li&gt;Enable V&lt;sub&gt;ANA&lt;/sub&gt; supply,&lt;/li&gt;
  &lt;li&gt;Enable parent supply of V&lt;sub&gt;DIG&lt;/sub&gt; supply,&lt;/li&gt;
  &lt;li&gt;Enable V&lt;sub&gt;I/O&lt;/sub&gt; supply,&lt;/li&gt;
  &lt;li&gt;Enable V&lt;sub&gt;AF&lt;/sub&gt; supply,&lt;/li&gt;
  &lt;li&gt;Enable external clock and set it to 24MHz,&lt;/li&gt;
  &lt;li&gt;Enable V&lt;sub&gt;DIG&lt;/sub&gt; supply,&lt;/li&gt;
  &lt;li&gt;Deassert reset.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sequence would prove to be ineffective, however. Fortunately, there is source code to refer to in this case: The driver from the Jetson Nano kernel. This driver’s &lt;a href=&quot;https://github.com/150balbes/Jetson-Nano/blob/19a504de0ae7045bd5c8741871f7f7d4c3dfa3de/nvidia/drivers/media/i2c/imx318.c#L255-L307&quot;&gt;power on function&lt;/a&gt; suggests &lt;a href=&quot;https://github.com/150balbes/Jetson-Nano/blob/19a504de0ae7045bd5c8741871f7f7d4c3dfa3de/nvidia/drivers/media/i2c/imx318.c#L293&quot;&gt;adding a 19ms delay&lt;/a&gt; after deasserting reset.&lt;/p&gt;

&lt;figcaption&gt;&lt;i&gt;
There was also a mistake I made in the way I described the external clock in DTS, which might have had an even bigger hand at preventing the sensor from powering up at first. The reason I made it to begin with is a bit complicated and shouldn&apos;t be an issue for anyone else, so I will not talk about it for brevity&apos;s sake...
&lt;/i&gt;&lt;/figcaption&gt;

&lt;p&gt;Combining information from the sequence captured on the running downstream kernel and the driver in the Jetson Nano kernel tree, and after some experimentation, a working power on function is made:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;imx318_power_on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i2c_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to_i2c_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v4l2_subdev&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i2c_get_clientdata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imx318&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx318&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to_imx318&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;gpiod_set_value_cansleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx318&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reset_gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regulator_bulk_enable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMX318_NUM_SUPPLIES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imx318&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;supplies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx318&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to enable regulators: %d&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;regulator_bulk_disable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMX318_NUM_SUPPLIES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imx318&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;supplies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clk_prepare_enable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx318&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extclk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx318&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to enable external clock: %d&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;gpiod_set_value_cansleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx318&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reset_gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

	&lt;span class=&quot;cm&quot;&gt;/* Wait for sensor to power on */&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;usleep_range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;19000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;dev_dbg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx318&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Powered on&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;sensor-initialization&quot;&gt;Sensor initialization&lt;/h2&gt;
&lt;p&gt;The sensor has to be initialized first before it can capture any images. There are many registers in the sensor that are written to during initialization, some of which are related to the internal clock tree, while others configure image properties, timing, and so on.&lt;/p&gt;

&lt;p&gt;Ideally, each of those registers should be labeled clearly in the driver, then written to individually with also clearly labeled values, or if a bitmask, constructed at the time of writing. However, due to lack of documentation, and sometimes simply due to the sheer number of registers that need to be written, many sensor drivers just have a bunch of magic numbers arranged in tables of register-value pairs, which then get applied as needed. Going back to the downstream drivers found previously, such register tables can be seen. In the Jetson Nano driver, the tables are placed in a &lt;a href=&quot;https://github.com/150balbes/Jetson-Nano/blob/19a504de0ae7045bd5c8741871f7f7d4c3dfa3de/nvidia/drivers/media/i2c/imx318_mode_tbls.h&quot;&gt;separate header file&lt;/a&gt;, while in the MT6757 driver, they are placed &lt;a href=&quot;https://github.com/mediatek-android-development/android_kernel_mediatek_mt6757CD-4.4/blob/79bd1255c4d403669d3e607c58f318a124337118/drivers/misc/mediatek/imgsensor/src/mt6757/imx318_mipi_raw/imx318mipi_Sensor.c#L884-L1393&quot;&gt;in the source file of the driver&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In both drivers, there is a common table that is quite long, and a few smaller mode tables. The common table is used once for initialization, while the mode tables are used to switch between, well, different modes.&lt;/p&gt;

&lt;h2 id=&quot;modes&quot;&gt;Modes?&lt;/h2&gt;
&lt;p&gt;While the IMX318 sensor has a native resolution of 5488x4112, it can also operate at other resolutions with various refresh rates. The combination of resolution and refresh rate* is referred to as a &lt;em&gt;mode&lt;/em&gt;.&lt;/p&gt;

&lt;figcaption&gt;
*While other aspects such as formats (more on that later) and crop factors can differ between modes, we will stick to resolutions and refresh rates only to keep things simple.
&lt;/figcaption&gt;

&lt;p&gt;Due to the difference in bits per image and the number of images that need to be transmitted per second for each mode, The rate at which the sensor has to transmit signals depends on the mode configured. This is where link frequency comes in. Theoretically speaking, the minimum link frequency of a MIPI CSI bus required at a certain mode can be calculated as follows:&lt;/p&gt;

\[f = {whbr \over 2n}\]

&lt;p&gt;Where:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;\(f\): Link frequency;&lt;/li&gt;
  &lt;li&gt;\(w\): Image width;&lt;/li&gt;
  &lt;li&gt;\(h\): Image height;&lt;/li&gt;
  &lt;li&gt;\(b\): Bit depth (bits per pixel);&lt;/li&gt;
  &lt;li&gt;\(r\): Refresh rate; and&lt;/li&gt;
  &lt;li&gt;\(n\): CSI link count.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The MIPI CSI bus operates with &lt;a href=&quot;https://en.wikipedia.org/wiki/Double_data_rate&quot;&gt;&lt;em&gt;double data rate&lt;/em&gt;&lt;/a&gt; (or DDR for short). This means that two bits are transferred every clock cycle, one on the rising edge, and another on the falling edge:&lt;/p&gt;
&lt;object class=&quot;post_image&quot; data=&quot;https://emainline.gitlab.io/assets/images/content/ddr.svg&quot; width=&quot;60%&quot;&gt;&lt;/object&gt;
&lt;p&gt;Furthermore, the bus may consist of multiple links, each transmitting bits separately. These two facts make us arrive at the denominator \(2n\) shown in the equation above, meaning two bits per link transferred in a clock cycle. Meanwhile, the numerator is the number of bits that need to be transferred every second: The number of pixels in an image (\(w*h\)) times the number of bits per pixel (\(b\)), multiplied by the number of images transferred each second (\(r\)).&lt;/p&gt;

&lt;figcaption&gt;&lt;i&gt;
Alright, math class is over. Back to the fun stuff :)
&lt;/i&gt;&lt;/figcaption&gt;

&lt;h2 id=&quot;formats&quot;&gt;Formats&lt;/h2&gt;
&lt;p&gt;An image can be digitally represented in countless ways. The method used to arrange image data into a sequence of bytes is known as an &lt;em&gt;image format&lt;/em&gt;. Before looking into formats, it would help to understand how image sensors are constructed.&lt;/p&gt;

&lt;p&gt;To put simply, image sensors – like displays – have pixels, lots of pixels, in a grid array. While a display emits light from its pixels, image sensors detect it. Displays usually have subpixels within each pixel, each emitting one of the additive primary colors (red - green - blue). However, image sensors do not have subpixels, but rather a different color filter on each pixel in a certain pattern known as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Bayer_filter&quot;&gt;&lt;em&gt;Bayer filter&lt;/em&gt;&lt;/a&gt;. While this makes every pixel only sensitive* to one of the primary colors, it is possible to achieve a full color image by interpolating color values from neighboring pixels for each pixel.&lt;/p&gt;

&lt;figcaption&gt;
*mostly sensitive, but that is not important right now.
&lt;/figcaption&gt;

&lt;p&gt;The Sony IMX318 sensor, like many other Sony image sensors used on phones, has a 10-bit RGGB bayer format. This means that each pixel has a 10-bit value, and that a 2x2 grid has red, green, green then blue pixels left to right, top to bottom:&lt;/p&gt;

&lt;object class=&quot;post_image&quot; data=&quot;https://emainline.gitlab.io/assets/images/content/rggb.svg&quot; width=&quot;40%&quot;&gt;&lt;/object&gt;

&lt;p&gt;Coming back to the bit depth, one might wonder how pixel values are stored in bytes, considering that each pixel has 10 bits while a byte only has 8. The easiest way would be to just store each pixel value in two bytes (numbers represent pixel indices, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt; represents unused bits):&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;00000000 00XXXXXX 11111111 11XXXXXX 22222222 22XXXXXX 33333333 33XXXXXX
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is known as an &lt;em&gt;unpacked&lt;/em&gt; 10-bit format. This method is quite inefficient though; as it leaves many bits unused. Due to that, it is rarely used outside of processing stages, where the data would likely need to be unpacked anyway.&lt;/p&gt;

&lt;p&gt;A much efficient method to handle 10-bit pixel values is to use a &lt;em&gt;packed&lt;/em&gt; 10-bit format, in which the bits from 4 pixels (\(4 * 10 = 40\)) fit perfectly into 5 bytes (\(5 * 8 = 40\)). It may sound logical at first to pack the bits sequencially, where bits in a row of 4 pixels would be placed in sequence like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;00000000 00111111 11112222 22222233 33333333
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But as it turns out, the bits are usually packed like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;00000000 11111111 22222222 33333333 00112233
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;While this may look strange, it is actually a clever way of packing 10-bit pixel values into bytes. In this arrangement, the eight most significant bits of the values are stored in the first 4 bytes, while the two least significant bits of every value are put together in the fifth byte. This allows for converting a 10-bit image into an 8-bit one by simply discarding every fifth byte in the sequence, leaving only the most significant bits behind. This feature will help with decoding raw sensor data later.&lt;/p&gt;

&lt;h2 id=&quot;putting-everything-together&quot;&gt;Putting everything together&lt;/h2&gt;
&lt;p&gt;With all necessary elements found, it is now possible to assemble a &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/v5.18.15-msm8996/drivers/media/i2c/imx318.c&quot;&gt;functional driver for the IMX318 sensor&lt;/a&gt;.&lt;/p&gt;

&lt;figcaption&gt;&lt;i&gt;
The driver is quite big and has many boring details mostly taken from other similar drivers, so I decided not to explain it in detail. Feel free to go through it here.
&lt;/i&gt;&lt;/figcaption&gt;

&lt;h3 id=&quot;device-tree&quot;&gt;Device tree&lt;/h3&gt;
&lt;p&gt;To bring the driver to life, it is necessary to describe the camera hardware in the device tree.&lt;/p&gt;

&lt;figcaption&gt;&lt;i&gt;
This was done throughout the making of the driver, including testing the power on sequence shown earlier, but I decided to leave it all until the end to limit confusion.
&lt;/i&gt;&lt;/figcaption&gt;

&lt;p&gt;To begin with, the camera subsystem is enabled, and its analog supply is specified:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;camss&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;okay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;vdda-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vreg_l2a_1p25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The analog supply can be found throughout the &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/msm8996-camera.dtsi&quot;&gt;downstream device tree&lt;/a&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qcom,mipi-csi-vdd-supply&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CCI&lt;/code&gt; block of the camera subsystem is also enabled:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cci&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;okay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A node for the V&lt;sub&gt;DIG&lt;/sub&gt; supply LDO is added under the root node:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;vdd_cam_1p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;vdd-cam-1p1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;compatible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;regulator-fixed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;regulator-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;vdd_cam_1p1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;regulator-min-microvolt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;regulator-max-microvolt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;vin-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vreg_s3a_1p3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;regulator-allow-set-load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;gpio&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tlmm&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;86&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;enable-active-high&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pinctrl-names&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pinctrl-0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vdd_cam_1p1_default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Along with a pin state for its enable pin in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tlmm&lt;/code&gt; node (GPIO controller of the SoC):&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tlmm&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;vdd_cam_1p1_default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;vdd_cam_1p1_default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pins&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio86&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;drive-strength&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;bias-disable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now, the sensor can be added to the node of the CCI bus it is connected to:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cci_i2c0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;clock-frequency&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;

	&lt;span class=&quot;cm&quot;&gt;/* Rear camera */&lt;/span&gt;
	&lt;span class=&quot;nd&quot;&gt;camera-sensor@&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;compatible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;sony,imx318&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;reg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x1a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;vana-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vreg_l17a_2p8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;vio-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vreg_lvs1a_1p8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;vmipi-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vreg_l11a_1p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;vdig-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vdd_cam_1p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;reset-gpios&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tlmm&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GPIO_ACTIVE_LOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;clocks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mmcc&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CAMSS_MCLK0_CLK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;clock-frequency&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;24000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Here, all needed supplies, clocks, GPIOs are listed, all of which were found previously. The clock frequency of the CCI bus is also configured. The address of the sensor (as seen in the node name and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reg&lt;/code&gt; property) is found by setting a random address at first then running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2cdetect&lt;/code&gt; on this CCI bus after the driver is probed and the power on sequence is executed.&lt;/p&gt;

&lt;p&gt;Pin states for the reset and clock pins of the sensor are also added to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tlmm&lt;/code&gt; node:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tlmm&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cam_sensor_rear_default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cam_sensor_rear_default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pins&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio30&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;drive-strength&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;bias-disable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;cam_sensor_rear_sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cam_sensor_rear_sleep&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pins&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio30&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;drive-strength&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;bias-pull-up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;mclk0_default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mclk0_default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pins&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio13&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cam_mclk&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;drive-strength&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;bias-disable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;mclk0_sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mclk0_sleep&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pins&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio13&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;drive-strength&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;bias-pull-down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;function = &quot;cam_mclk&quot;&lt;/code&gt; is needed for GPIO 13 to make it output a clock signal for the camera rather than functioning as a regular GPIO.&lt;/p&gt;

&lt;p&gt;So far, the camera subsystem has been enabled and the image sensor added, but they have not been linked together. To do so, &lt;em&gt;endpoints&lt;/em&gt; – as seen before in the IMX214 driver – are used. An endpoint is described in the sensor node:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cci_i2c0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nd&quot;&gt;camera-sensor@&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nf&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;imx318_ep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;endpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;data-lanes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;link-frequencies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;211562400&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;693600000&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;846249600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;remote-endpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csiphy0_ep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Here, CSI data lanes used and supported link frequencies are specified. The equation shown previously is used to calculate those frequencies based on &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/msm8996-staging/drivers/media/i2c/imx318.c#L937-965&quot;&gt;modes supported by the driver&lt;/a&gt;. Finally, the endpoint at the other end of the communication channel is specified. In this case, the CSI physical layer lies at the other end of the CSI bus.&lt;/p&gt;

&lt;p&gt;Likewise, an endpoint is descibed in the camera subsystem node:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;camss&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nf&quot;&gt;ports&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nd&quot;&gt;port@&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;reg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;csiphy0_ep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;endpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;clock-lanes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;data-lanes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;remote-endpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imx318_ep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Used data lanes are also specified here, as well as the physical index of the clock lane. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remote-endpoint&lt;/code&gt; property is used here too, this time pointing towards the sensor endpoint.&lt;/p&gt;

&lt;p&gt;With that done, it is time to put the image sensor to use.&lt;/p&gt;

&lt;h2 id=&quot;getting-data-out-of-the-sensor&quot;&gt;Getting data out of the sensor&lt;/h2&gt;
&lt;p&gt;Now that we have a driver, the sensor can be instructed to start capturing images (or &lt;em&gt;frames&lt;/em&gt;) and transmitting them over the MIPI CSI bus. However, we still cannot get the data it outputs; as it has to get through the SoC camera pipeline.&lt;/p&gt;

&lt;p&gt;The MSM8996 SoC has a camera subsystem (abbreviated to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CAMSS&lt;/code&gt; as seen before) with several stages, starting at the CSI physical layer (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CSIPHY&lt;/code&gt;) which brings in electrical signals from outside the SoC, going through the CSI decoder (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CSID&lt;/code&gt;) which decodes data carried in those signals, then the ISP interface (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISPIF&lt;/code&gt;) which provides access to the ISP for processing, arriving finally at the video frontend (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VFE&lt;/code&gt;) at which image data is exported from the camera subsystem to other parts of the SoC by pixel interface (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PIX&lt;/code&gt;) and raw dump interface (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RDI&lt;/code&gt;) blocks. A great &lt;a href=&quot;https://www.linaro.org/blog/upstream-camera-support-for-qualcomm-platforms/&quot;&gt;blog post&lt;/a&gt; by Linaro, as well as the &lt;a href=&quot;https://www.kernel.org/doc/html/v4.14/media/v4l-drivers/qcom_camss.html&quot;&gt;Linux Kernel Documentation&lt;/a&gt; explain this in more detail.&lt;/p&gt;

&lt;p&gt;In order to retrieve data from the sensor, all of these stages must be configured and linked together. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;media-ctl&lt;/code&gt; tool can be used for that. First, the stages are linked together:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# media-ctl -d /dev/media0 -l &apos;&quot;msm_csiphy0&quot;:1-&amp;gt;&quot;msm_csid0&quot;:0[1],&quot;msm_csid0&quot;:1-&amp;gt;&quot;msm_ispif0&quot;:0[1],&quot;msm_ispif0&quot;:1-&amp;gt;&quot;msm_vfe0_rdi0&quot;:0[1]&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A visual representation can be obtained with the same tool, with solid lines showing established links, and dashed lines showing possible ones:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ media-ctl --print-dot | dot -Tpng
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/content/camss.png&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then, source and sink pads of all stages are configured to a certain image format:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# media-ctl -d /dev/media0 -V &apos;&quot;imx318 3-001a&quot;:0[fmt:SRGGB10_1X10/3840x2160 field:none],&quot;msm_csiphy0&quot;:0[fmt:SRGGB10_1X10/3840x2160 field:none],&quot;msm_csid0&quot;:0[fmt:SRGGB10_1X10/3840x2160 field:none],&quot;msm_ispif0&quot;:0[fmt:SRGGB10_1X10/3840x2160 field:none],&quot;msm_vfe0_rdi0&quot;:0[fmt:SRGGB10_1X10/3840x2160 field:none]&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now the pipeline is ready to start &lt;em&gt;streaming&lt;/em&gt;, which is to say begin capturing frames from the sensor and passing them through the rest of the pipeline until they reach memory where they can be accessed. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v4l2-ctl&lt;/code&gt; is used to capture a single frame:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# v4l2-ctl -d /dev/video0 --set-fmt-video=width=3840,height=2160,pixelformat=&apos;pRAA&apos; --stream-mmap --stream-to=img --stream-count=1 --verbose

VIDIOC_QUERYCAP: ok
VIDIOC_G_FMT: ok
VIDIOC_S_FMT: ok
Format Video Capture Multiplanar:
	Width/Height      : 3840/2160
	Pixel Format      : &apos;pRAA&apos; (10-bit Bayer RGRG/GBGB Packed)
	Field             : None
	Number of planes  : 1
	Flags             : 
	Colorspace        : sRGB
	Transfer Function : sRGB
	YCbCr/HSV Encoding: ITU-R 601
	Quantization      : Full Range
	Plane 0           :
	   Bytes per Line : 4800
	   Size Image     : 10368000
		VIDIOC_REQBUFS returned 0 (Success)
		VIDIOC_QUERYBUF returned 0 (Success)
		VIDIOC_QUERYBUF returned 0 (Success)
		VIDIOC_QUERYBUF returned 0 (Success)
		VIDIOC_QUERYBUF returned 0 (Success)
		VIDIOC_QBUF returned 0 (Success)
		VIDIOC_QBUF returned 0 (Success)
		VIDIOC_QBUF returned 0 (Success)
		VIDIOC_QBUF returned 0 (Success)
		VIDIOC_STREAMON returned 0 (Success)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now we have a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;img&lt;/code&gt; that contains raw data captured from the sensor.&lt;/p&gt;

&lt;h2 id=&quot;processing-data-into-a-viewable-image&quot;&gt;Processing data into a viewable image&lt;/h2&gt;
&lt;p&gt;To get an actual image would require processing the captured sensor data first. The easy part would be to convert pixels into 8-bit depth thanks to the packed 10-bit format. However, due to the bayer filter used on the sensor, getting proper color values at capture resolution would require interpolating values of several pixels in a process called &lt;a href=&quot;https://en.wikipedia.org/wiki/Demosaicing&quot;&gt;&lt;em&gt;demosaicing&lt;/em&gt;&lt;/a&gt;. A much easier way to get an image however would be to treat every 4 pixels in a 2x2 square grid as subpixels of a single bigger pixel. This will effectively result in an image with 1/4 the capture resolution, but for testing purposes that would still be more than enough. Putting all of this in a Python script:&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;PIL&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3840&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2160&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Each group of 4 pixels is 5 bytes long
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line_length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;rb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Preview image will have 1/2 the dimensions, 1/4 the resolution
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preview_img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RGB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;preview_pixels&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preview_img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pixel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Index of top-left corner of pixel in original array
&lt;/span&gt;		&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Skip 5th bytes to get 8-bit pixels
&lt;/span&gt;		&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;

		&lt;span class=&quot;c1&quot;&gt;# Red
&lt;/span&gt;		&lt;span class=&quot;n&quot;&gt;pixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line_length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

		&lt;span class=&quot;c1&quot;&gt;# Green
&lt;/span&gt;		&lt;span class=&quot;n&quot;&gt;pixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line_length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Average value of second green pixel with first one
&lt;/span&gt;		&lt;span class=&quot;n&quot;&gt;pixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line_length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;

		&lt;span class=&quot;c1&quot;&gt;# Blue
&lt;/span&gt;		&lt;span class=&quot;n&quot;&gt;pixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line_length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;preview_pixels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;preview_img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;img.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And finally, after some manual adjustments:&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-dnt=&quot;true&quot; data-theme=&quot;dark&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;The Mi Note 2 is starting to open its eye...&lt;br /&gt;&lt;br /&gt;First image ever taken while running the mainline Linux kernel, with a fully open-source camera stack from the camera sensor and SoC camera subsystem drivers to the userspace tools. &lt;a href=&quot;https://t.co/JfB1P8yrK2&quot;&gt;pic.twitter.com/JfB1P8yrK2&lt;/a&gt;&lt;/p&gt;&amp;mdash; Yassine Oudjana (@y_oudjana) &lt;a href=&quot;https://twitter.com/y_oudjana/status/1471534020714450949?ref_src=twsrc%5Etfw&quot;&gt;December 16, 2021&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/content/camera_first_image.jpg&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;
&lt;p&gt;While getting the camera sensor to work on mainline Linux at all is a great achievement, it is quite useless if the images it captures are underexposed, have inaccurate colors and are out of focus. In order to achieve proper camera support, at least a few things must be taken care of:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Gain and exposure controls must be added to the sensor driver. Part of this has already been &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/msm8996-staging/drivers/media/i2c/imx318.c#L1000-1019&quot;&gt;done&lt;/a&gt; at the time of writing.&lt;/li&gt;
  &lt;li&gt;The lens actuator needs to be enabled as well. This has also been &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/commit/85b4ce425a1d789020cebb8f41d94e816aba5f03&quot;&gt;done&lt;/a&gt;. Auto-focus has not been implemented yet, however.&lt;/li&gt;
  &lt;li&gt;Some color calibration data must be collected to post-process the raw data coming out of the sensor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now, mostly unprocessed images can be captured using &lt;a href=&quot;https://gitlab.com/postmarketOS/megapixels/-/merge_requests/13&quot;&gt;Megapixels&lt;/a&gt;.&lt;/p&gt;
</content>
		<link
			href="https://emainline.gitlab.io/2022/08/24/camera.html"
			rel="alternate" type="text/html" title="A first photo on mainline Linux"
		/>
		<id isPermaLink="true">https://emainline.gitlab.io/2022/08/24/camera.html</id>
		
		
	</entry>
	
	<entry>
		
		
		<title>Unlocking the Qualcomm Snapdragon Sensor Core (Part 2)</title>
		<description>Deciphering the language of the SSC, one step at a time.</description>
		<author>Yassine Oudjana</author>
		<published>Fri, 08 Apr 2022 00:00:00 +0000</published>
		<content type="html">&lt;p&gt;&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/cover/ssc_p2.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;a href=&quot;/2021/10/06/Unlocking_SSC_P1.html&quot;&gt;Last time&lt;/a&gt; we got a step closer to working sensors on mainline Linux by powering on and booting the SLPI successfully. With that easy part out of the way, we can now deal with the hard part: Finding out how to communicate with it and getting something meaningful out of it. Usually when bringing up hardware in the mainline kernel, the downstream kernel drivers, and/or if available, datasheets are used to understand how the hardware works and how to operate it. With it having no open-source drivers nor public documentation however, figuring out how the SSC works will be a challenge on a whole new level.&lt;/p&gt;

&lt;p&gt;Without any direct reference material, it would seem to be an impossible task at first, but when looking at it from a different perspective, even the proprietary HAL driver can be used as reference. After all, we know for a fact that it works, so there must be something that can be learned from it.&lt;/p&gt;

&lt;p&gt;There are a few ways to understand how the HAL driver works without having its source code on hand. It is possible to disassemble the binary and try to read through assembly code, but getting any meaningful information that way will be quite cumbersome, not to mention the legal uncertainties surrounding this method. A much better alternative in this case would be to run the driver normally, then monitor its interactions with the SSC. After understanding what it does and what for, It should be possible to then imitate it and get identical results from the SSC.&lt;/p&gt;

&lt;p&gt;Since it has to talk to hardware, it will have to interact with the kernel using syscalls. Tracing the syscalls it makes will be a good starting point, and for that, the &lt;a href=&quot;https://strace.io/&quot;&gt;Linux syscall tracer&lt;/a&gt; – usually known as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; – is the perfect tool. Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; will allow for monitoring all interactions between the HAL driver and the Linux kernel, and by extension, the SLPI. It can also attach to already running processes, making it easy to collect some data without any preparation, as long as root privileges are available.&lt;/p&gt;

&lt;h2 id=&quot;watching-the-ssc&quot;&gt;Watching the SSC&lt;/h2&gt;
&lt;p&gt;The main part of the HAL driver is the sensor service &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android.hardware.sensors@1.0-service&lt;/code&gt;, which is started at boot by the init system. Before attaching &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; to it, its PID has to be found:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# pidof android.hardware.sensors@1.0-service
582
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&lt;/code&gt; argument can be used to attach &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; to it:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# strace -p 582
strace: Process 582 attached
ioctl(3, BINDER_WRITE_READ, 0x7fc7f43d20) = 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Quite underwhelming. Could this be the wrong process? Perhaps the right one can be found using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;htop&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
610 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:01.05 /vendor/bin/hw/android.hardware.sensors@1.0-service
612 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:00.76 /vendor/bin/hw/android.hardware.sensors@1.0-service
613 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:00.76 /vendor/bin/hw/android.hardware.sensors@1.0-service
771 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:35.28 /vendor/bin/hw/android.hardware.sensors@1.0-service
773 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:00.00 /vendor/bin/hw/android.hardware.sensors@1.0-service
774 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:00.70 /vendor/bin/hw/android.hardware.sensors@1.0-service
780 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:00.19 /vendor/bin/hw/android.hardware.sensors@1.0-service
818 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:00.00 /vendor/bin/hw/android.hardware.sensors@1.0-service
819 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:00.67 /vendor/bin/hw/android.hardware.sensors@1.0-service
820 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:00.00 /vendor/bin/hw/android.hardware.sensors@1.0-service
821 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:00.66 /vendor/bin/hw/android.hardware.sensors@1.0-service
827 system     20   0 11.4G  2732  2076 S  0.0  0.1  0:00.00 /vendor/bin/hw/android.hardware.sensors@1.0-service
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;There are actually many &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android.hardware.sensors@1.0-service&lt;/code&gt; processes, so it appears that the sensor service has multiple threads running. Fortunately, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f&lt;/code&gt; argument to follow forks, allowing us to attach to all running threads of the sensor service at the same time:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# strace -p 582 -f
strace: Process 582 attached with 70 threads
[pid  2475] ppoll([{fd=107, events=POLLIN}, {fd=106, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid  2473] ppoll([{fd=104, events=POLLIN}, {fd=103, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid  1735] ppoll([{fd=101, events=POLLIN}, {fd=100, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid  1734] ppoll([{fd=15, events=POLLIN}, {fd=14, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   982] futex(0x715777ad00, FUTEX_WAIT_BITSET_PRIVATE, 747816, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   980] futex(0x71576f55e8, FUTEX_WAIT_BITSET_PRIVATE, 1219316, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   977] ppoll([{fd=98, events=POLLIN}, {fd=97, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   973] ppoll([{fd=95, events=POLLIN}, {fd=94, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   972] futex(0x72078ff150, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   976] rt_sigtimedwait([RTMIN],  &amp;lt;unfinished ...&amp;gt;
[pid   975] futex(0x72078ff610, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   970] ppoll([{fd=92, events=POLLIN}, {fd=91, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   969] futex(0x72078ff650, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   966] futex(0x72078fd790, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   967] ppoll([{fd=89, events=POLLIN}, {fd=88, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   963] ppoll([{fd=86, events=POLLIN}, {fd=85, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   962] futex(0x72078fd550, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   958] futex(0x72078fdf90, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   959] ppoll([{fd=83, events=POLLIN}, {fd=82, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   954] ppoll([{fd=80, events=POLLIN}, {fd=79, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   953] futex(0x72078ff310, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   948] ppoll([{fd=77, events=POLLIN}, {fd=76, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   947] futex(0x72078ff710, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   939] ppoll([{fd=71, events=POLLIN}, {fd=70, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   944] ppoll([{fd=74, events=POLLIN}, {fd=73, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   943] futex(0x72078fec10, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   942] futex(0x72078ff010, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   938] futex(0x72078ff090, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   933] ppoll([{fd=68, events=POLLIN}, {fd=67, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   932] futex(0x72078ff1d0, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   927] ppoll([{fd=65, events=POLLIN}, {fd=64, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   921] ppoll([{fd=62, events=POLLIN}, {fd=61, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   926] futex(0x72078fed50, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   920] futex(0x72078fec90, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   916] ppoll([{fd=56, events=POLLIN}, {fd=55, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   917] futex(0x72078fe150, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   918] ppoll([{fd=59, events=POLLIN}, {fd=58, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   915] futex(0x72078fe790, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   913] ppoll([{fd=53, events=POLLIN}, {fd=52, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   912] futex(0x72078fe350, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   903] ppoll([{fd=50, events=POLLIN}, {fd=49, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   902] futex(0x72078fe010, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   895] ppoll([{fd=47, events=POLLIN}, {fd=46, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   894] futex(0x72078fe8d0, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   885] ppoll([{fd=44, events=POLLIN}, {fd=43, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   884] futex(0x72078febd0, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   876] ppoll([{fd=41, events=POLLIN}, {fd=40, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   875] futex(0x72078fea90, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   854] ppoll([{fd=35, events=POLLIN}, {fd=34, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   857] futex(0x72078feb90, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   853] futex(0x72078fdfd0, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   858] ppoll([{fd=38, events=POLLIN}, {fd=37, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   846] ppoll([{fd=32, events=POLLIN}, {fd=31, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   845] futex(0x72078fe410, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   832] futex(0x72078fe290, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   828] ppoll([{fd=26, events=POLLIN}, {fd=25, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   833] ppoll([{fd=29, events=POLLIN}, {fd=28, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   827] futex(0x72078fe250, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   821] ppoll([{fd=23, events=POLLIN}, {fd=22, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   820] futex(0x72078fe0d0, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   819] ppoll([{fd=20, events=POLLIN}, {fd=19, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   780] futex(0x72078fd410, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   774] ppoll([{fd=12, events=POLLIN}, {fd=11, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   773] futex(0x72078fd8d0, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   771] futex(0x72078fd4d0, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid   613] ppoll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   612] read(6,  &amp;lt;unfinished ...&amp;gt;
[pid   610] pselect6(6, [5], NULL, [5], NULL, NULL &amp;lt;unfinished ...&amp;gt;
[pid   582] ioctl(3, BINDER_WRITE_READ &amp;lt;unfinished ...&amp;gt;
[pid   818] futex(0x72078fe210, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid  1734] &amp;lt;... ppoll resumed&amp;gt; )       = 1 ([{fd=14, revents=POLLIN}])
[pid  1734] recvfrom(14, &quot;\4\226\5\3\0$\0\1\4\0\275\23zo\2\10\0\331\0\200R\203`\336\26\3\4\0\0\0\0\0&quot;..., 56, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\0\0\2\0\0\0\1\0\0\0)\0\0\0\0\0\0\0&quot;}, [20]) = 43
[pid  1734] rt_sigprocmask(0x539a1968 /* SIG_??? */, NULL, [USR2 RTMIN], 8) = 0
[pid  1734] rt_sigprocmask(0x539a1958 /* SIG_??? */, NULL, [USR2 RTMIN], 8) = 0
[pid  1734] futex(0x72078fd410, FUTEX_WAKE_PRIVATE, 2147483647) = 1
[pid   780] &amp;lt;... futex resumed&amp;gt; )       = 0
[pid  1734] ppoll([{fd=15, events=POLLIN}, {fd=14, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   780] futex(0x72078fd410, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid  1734] &amp;lt;... ppoll resumed&amp;gt; )       = 1 ([{fd=14, revents=POLLIN}])
[pid  1734] recvfrom(14, &quot;\4\227\5\3\0$\0\1\4\0\23\24\177o\2\10\0\263\217\263\246\205`\336\26\3\4\0\0\0\0\0&quot;..., 56, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\0\0\2\0\0\0\1\0\0\0)\0\0\0\0\0\0\0&quot;}, [20]) = 43
[pid  1734] rt_sigprocmask(0x539a1968 /* SIG_??? */, NULL, [USR2 RTMIN], 8) = 0
[pid  1734] rt_sigprocmask(0x539a1958 /* SIG_??? */, NULL, [USR2 RTMIN], 8) = 0
[pid  1734] futex(0x72078fd410, FUTEX_WAKE_PRIVATE, 2147483647) = 1
[pid  1734] ppoll([{fd=15, events=POLLIN}, {fd=14, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   780] &amp;lt;... futex resumed&amp;gt; )       = 0
[pid   780] futex(0x72078fd410, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid  1734] &amp;lt;... ppoll resumed&amp;gt; )       = 1 ([{fd=14, revents=POLLIN}])
[pid  1734] recvfrom(14, &quot;\4\230\5\3\0$\0\1\4\0o\24\204o\2\10\0\22\216\352\372\207`\336\26\3\4\0\0\0\0\0&quot;..., 56, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\0\0\2\0\0\0\1\0\0\0)\0\0\0\0\0\0\0&quot;}, [20]) = 43
[pid  1734] rt_sigprocmask(0x539a1968 /* SIG_??? */, NULL, [USR2 RTMIN], 8) = 0
[pid  1734] rt_sigprocmask(0x539a1958 /* SIG_??? */, NULL, [USR2 RTMIN], 8) = 0
[pid  1734] futex(0x72078fd410, FUTEX_WAKE_PRIVATE, 2147483647) = 1
[pid   780] &amp;lt;... futex resumed&amp;gt; )       = 0
[pid  1734] ppoll([{fd=15, events=POLLIN}, {fd=14, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   780] futex(0x72078fd410, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid  1734] &amp;lt;... ppoll resumed&amp;gt; )       = 1 ([{fd=14, revents=POLLIN}])
[pid  1734] recvfrom(14, &quot;\4\231\5\3\0$\0\1\4\0\374\24\211o\2\10\0 \3357O\212`\336\26\3\4\0\0\0\0\0&quot;..., 56, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\0\0\2\0\0\0\1\0\0\0)\0\0\0\0\0\0\0&quot;}, [20]) = 43
[pid  1734] rt_sigprocmask(0x539a1968 /* SIG_??? */, NULL, [USR2 RTMIN], 8) = 0
[pid  1734] rt_sigprocmask(0x539a1958 /* SIG_??? */, NULL, [USR2 RTMIN], 8) = 0
[pid  1734] futex(0x72078fd410, FUTEX_WAKE_PRIVATE, 2147483647) = 1
[pid  1734] ppoll([{fd=15, events=POLLIN}, {fd=14, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   780] &amp;lt;... futex resumed&amp;gt; )       = 0
[pid   780] futex(0x72078fd410, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY &amp;lt;unfinished ...&amp;gt;
[pid  1734] &amp;lt;... ppoll resumed&amp;gt; )       = 1 ([{fd=14, revents=POLLIN}])
[pid  1734] recvfrom(14, &quot;\4\235\5\3\0$\0\1\4\0-\26\235o\2\10\0\5\224\365\237\223`\336\26\3\4\0\0\0\0\0&quot;..., 56, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\0\0\2\0\0\0\1\0\0\0)\0\0\0\0\0\0\0&quot;}, [20]) = 43
[pid  1734] rt_sigprocmask(0x539a1968 /* SIG_??? */, NULL, [USR2 RTMIN], 8) = 0
[pid  1734] rt_sigprocmask(0x539a1958 /* SIG_??? */, NULL, [USR2 RTMIN], 8) = 0
[pid  1734] futex(0x72078fd410, FUTEX_WAKE_PRIVATE, 2147483647) = 1
[pid  1734] ppoll([{fd=15, events=POLLIN}, {fd=14, events=POLLIN}], 2, NULL, NULL, 0 &amp;lt;unfinished ...&amp;gt;
[pid   780] &amp;lt;... futex resumed&amp;gt; )       = 0
[pid   780] futex(0x72078fd410, FUTEX_WAIT_BITSET_PRIVATE, 4294967294, NULL, FUTEX_BITSET_MATCH_ANY
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now that is more like it. There is a lot going on, so filtering the syscalls would be a good idea. Since we are looking for data transferred between SSC and the sensor service, we can focus on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sendto&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;recvfrom&lt;/code&gt; calls. This can be done using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e&lt;/code&gt; argument. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-xx&lt;/code&gt; argument is also added to print all strings in hexadecimal format, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-s 1024&lt;/code&gt; to increase the string size limit so that no characters get truncated:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# strace -p 582 -f -e trace=sendto,recvfrom -xx -s 1024
strace: Process 582 attached with 70 threads
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Nothing?&lt;/p&gt;

&lt;p&gt;This is being done with the screen off, so the silence might be due to the sensors being suspended to save power. One sensor should be active however; as the phone can be woken up by waving a hand in front of the proximity sensor. To confirm, the proximity sensor is covered then uncovered a few times:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[pid   977] recvfrom(97, &quot;\x04\xad\x00\x05\x00\x1a\x00\x01\x01\x00\x1a\x02\x04\x00\xf2\xfe\xf4\x71\x03\x0c\x00\x00\x00\x01\x00\xa0\x3a\x29\xb2\x00\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 33
[pid   977] recvfrom(97, &quot;\x04\xae\x00\x05\x00\x1a\x00\x01\x01\x00\x1a\x02\x04\x00\x09\x26\xf5\x71\x03\x0c\x00\x00\x00\x00\x00\xa0\x3a\x29\xb2\x00\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 33
[pid   582] sendto(14, &quot;\x00\xc0\x01\x02\x00\x04\x00\x10\x01\x00\x01&quot;, 11, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x29\x00\x00\x00\xd8\x33\xf4\xc7&quot;}, 20) = 11
[pid  1734] recvfrom(14, &quot;\x02\xc0\x01\x02\x00\x29\x00\x02\x02\x00\x00\x00\x10\x04\x00\xe0\x2d\xf5\x71\x11\x08\x00\xfb\xcb\x93\x10\xab\x61\xde\x16\x13\x04\x00\x00\x00\x00\x00\x14\x08\x00\xa9\x13\x2e\xdc\x0e\x35\x00\x00&quot;, 56, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x29\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 48
[pid   582] sendto(94, &quot;\x00\x7e\x00\x02\x00\x26\x00\x01\x01\x00\x28\x02\x01\x00\x00\x03\x04\x00\x00\x00\x05\x00\x04\x0c\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x05\x00\x00\x00\x00\x00\x01&quot;, 45, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x58\x34\xf4\xc7&quot;}, 20) = 45
[pid   973] recvfrom(94, &quot;\x04\x3f\x00\x05\x00\x1a\x00\x01\x01\x00\x1f\x02\x04\x00\x09\x26\xf5\x71\x03\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 33
[pid   973] recvfrom(94, &quot;\x02\x7e\x00\x02\x00\x09\x00\x02\x02\x00\x00\x00\x10\x01\x00\x1f&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 16
[pid  1734] recvfrom(14, &quot;\x04\x15\x06\x03\x00\x24\x00\x01\x04\x00\x62\x3a\xf5\x71\x02\x08\x00\xa6\xa0\x66\x16\xab\x61\xde\x16\x03\x04\x00\x00\x00\x00\x00\x10\x08\x00\x0a\xe3\x00\xe2\x0e\x35\x00\x00&quot;, 56, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x29\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 43
[pid   982] sendto(94, &quot;\x00\x7f\x00\x00\x00\x00\x00&quot;, 7, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\xb8\xf0\xa9\x53&quot;}, 20) = 7
[pid   973] recvfrom(94, &quot;\x02\x7f\x00\x00\x00\x05\x00\x02\x02\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 12
[pid   977] recvfrom(97, &quot;\x04\xaf\x00\x05\x00\x1a\x00\x01\x01\x00\x1a\x02\x04\x00\x69\x8f\xf5\x71\x03\x0c\x00\x00\x00\x01\x00\xa0\x3a\x29\xb2\x00\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 33
[pid   977] recvfrom(97, &quot;\x04\xb0\x00\x05\x00\x1a\x00\x01\x01\x00\x1a\x02\x04\x00\xce\xa3\xf5\x71\x03\x0c\x00\x00\x00\x00\x00\xa0\x3a\x29\xb2\x00\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 33
[pid   977] recvfrom(97, &quot;\x04\xb1\x00\x05\x00\x1a\x00\x01\x01\x00\x1a\x02\x04\x00\x94\x10\xf6\x71\x03\x0c\x00\x00\x00\x01\x00\xa0\x3a\x29\xb2\x00\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 33
[pid   977] recvfrom(97, &quot;\x04\xb2\x00\x05\x00\x1a\x00\x01\x01\x00\x1a\x02\x04\x00\x13\x19\xf6\x71\x03\x0c\x00\x00\x00\x00\x00\xa0\x3a\x29\xb2\x00\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 33
[pid   977] recvfrom(97, &quot;\x04\xb3\x00\x05\x00\x1a\x00\x01\x01\x00\x1a\x02\x04\x00\xdf\x1f\xf6\x71\x03\x0c\x00\x00\x00\x01\x00\xa0\x3a\x29\xb2\x00\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 33
[pid   977] recvfrom(97, &quot;\x04\xb4\x00\x05\x00\x1a\x00\x01\x01\x00\x1a\x02\x04\x00\x5f\x28\xf6\x71\x03\x0c\x00\x00\x00\x00\x00\xa0\x3a\x29\xb2\x00\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 33
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And sure enough, data starts coming in. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;recvfrom&lt;/code&gt; call is traced each time the proximity sensor is covered or uncovered. There are a few extra &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sendto&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;recvfrom&lt;/code&gt; calls after the first time the sensor is covered, so it seems that something happens when the screen turns on. That will not be important now though.&lt;/p&gt;

&lt;h2 id=&quot;reading-the-data&quot;&gt;Reading the data&lt;/h2&gt;
&lt;p&gt;Right now all we have is a bunch of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; output lines with traced syscalls and the arguments passed through them. Since we are after raw data, some work has to be done on those lines.
To start analyzing the data format, a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;recvfrom&lt;/code&gt; is taken:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[pid   977] recvfrom(97, &quot;\x04\xb1\x00\x05\x00\x1a\x00\x01\x01\x00\x1a\x02\x04\x00\x94\x10\xf6\x71\x03\x0c\x00\x00\x00\x01\x00\xa0\x3a\x29\xb2\x00\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 33
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then the data, which is &lt;a href=&quot;https://linux.die.net/man/2/recvfrom&quot;&gt;the second argument of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;recvfrom&lt;/code&gt;&lt;/a&gt;, is extracted and formatted nicely:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# printf &quot;\x04\xb1\x00\x05\x00\x1a\x00\x01\x01\x00\x1a\x02\x04\x00\x94\x10\xf6\x71\x03\x0c\x00\x00\x00\x01\x00\xa0\x3a\x29\xb2\x00\x00\x00\x00&quot; | xxd -g 1
00000000: 04 b1 00 05 00 1a 00 01 01 00 1a 02 04 00 94 10  ................
00000010: f6 71 03 0c 00 00 00 01 00 a0 3a 29 b2 00 00 00  .q........:)....
00000020: 00                                               .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;These are the bytes received, put in a single line:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;04 b1 00 05 00 1a 00 01 01 00 1a 02 04 00 94 10 f6 71 03 0c 00 00 00 01 00 a0 3a 29 b2 00 00 00 00
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;There is a prominent pattern that can be quickly identified here: There are several non-zero bytes followed by zeroes, such as  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b1 00&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;05 00&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1a 00&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0c 00 00 00&lt;/code&gt;. This is a strong indicator that this data is little-endian, meaning that integers that are 2 bytes or longer are represented in such a way that their least significant byte comes first, followed by the second least significant byte, and so on.&lt;/p&gt;

&lt;p&gt;To interpret the data, it might help to start off with some guesses on the fields it is composed of. At a first glance, it looks like it can be split into these fields, resembled with a C struct:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prox_pkt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 04&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// b1 00&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 05 00&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 1a 00&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 01&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 01 00&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 1a&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 02&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 04 00&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 94&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 10&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// f6&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 71&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 03&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 0c 00 00 00&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 01 00&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// a0&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 3a&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 29&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// b2&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;// 00 00 00 00&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prox_pkt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkt1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x04&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xb1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x05&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x1a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x1a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x04&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x94&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xf6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x71&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x03&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x0c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xa0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x3a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x29&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xb2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Again, the little-endianness of the data is clearly seen in every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u16&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u32&lt;/code&gt; field, which are 2 bytes and 4 bytes long respectively. The zeroes helped with identifying the sizes of those fields. There might be additional 2 or 4 byte long fields that are holding large integers where their most significant bytes are non-zero, making them difficult to identify at this stage.&lt;/p&gt;

&lt;p&gt;Next, the data is analyzed further by comparing 2 consecutive lines. The steps above are repeated with the next line:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[pid   977] recvfrom(97, &quot;\x04\xb2\x00\x05\x00\x1a\x00\x01\x01\x00\x1a\x02\x04\x00\x13\x19\xf6\x71\x03\x0c\x00\x00\x00\x00\x00\xa0\x3a\x29\xb2\x00\x00\x00\x00&quot;, 820, MSG_DONTWAIT, {sa_family=AF_IB, sa_data=&quot;\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00&quot;}, [20]) = 33
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;04 b2 00 05 00 1a 00 01 01 00 1a 02 04 00 13 19 f6 71 03 0c 00 00 00 00 00 a0 3a 29 b2 00 00 00 00
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The format seems identical, so it should be possible to compare this with the previous line directly.&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prox_pkt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkt1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x04&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xb1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x05&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x1a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x1a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x04&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x94&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xf6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x71&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x03&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x0c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xa0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x3a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x29&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xb2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prox_pkt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkt2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x04&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xb2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x05&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x1a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x1a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x04&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xf6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x71&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x03&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x0c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xa0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x3a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x29&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xb2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Most of the values remained constant. The only values that changed are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var2&lt;/code&gt; which increased by 1, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var10&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var11&lt;/code&gt; which changed somewhat significantly, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var16&lt;/code&gt; which changed from 1 to 0.&lt;/p&gt;

&lt;p&gt;Looking at a larger set of lines should make it possible to identify patterns and find constant values with more certainty, as well as find some of the hiding large integers mentioned earlier.&lt;/p&gt;

&lt;p&gt;Formatting each line from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; output manually will take a lot of time. With some quick Python magic though:&lt;/p&gt;
&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;re&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ilines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readlines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iline&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ilines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ENOMSG&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;sendto&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iline&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;recvfrom&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\d+&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;sendto&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;send&quot;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;recvfrom&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;recv&quot;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\(\d\d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;rawdata&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;][x][0-9a-f][0-9a-f][^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]+&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawdata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;rawdata&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawdata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;x&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawbyte&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawdata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawbyte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;|&quot;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;chr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;127&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;|&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;{:02x}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; output can be quickly converted into a nice and easy to read format, including an ASCII representation that should reveal any text possibly carried in the packets:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;977	recv	97	|.................q........:).....|		04 ad 00 05 00 1a 00 01 01 00 1a 02 04 00 f2 fe f4 71 03 0c 00 00 00 01 00 a0 3a 29 b2 00 00 00 00
977	recv	97	|...............&amp;amp;.q........:).....|		04 ae 00 05 00 1a 00 01 01 00 1a 02 04 00 09 26 f5 71 03 0c 00 00 00 00 00 a0 3a 29 b2 00 00 00 00
582	send	14	|...........|		00 c0 01 02 00 04 00 10 01 00 01
1734	recv	14	|.....)..........-.q........a.................5..|		02 c0 01 02 00 29 00 02 02 00 00 00 10 04 00 e0 2d f5 71 11 08 00 fb cb 93 10 ab 61 de 16 13 04 00 00 00 00 00 14 08 00 a9 13 2e dc 0e 35 00 00
582	send	94	|.~...&amp;amp;....(..................................|		00 7e 00 02 00 26 00 01 01 00 28 02 01 00 00 03 04 00 00 00 05 00 04 0c 00 ff ff 00 00 00 00 00 00 00 00 00 00 11 05 00 00 00 00 00 01
973	recv	94	|.?.............&amp;amp;.q...............|		04 3f 00 05 00 1a 00 01 01 00 1f 02 04 00 09 26 f5 71 03 0c 00 00 00 00 00 00 00 00 00 00 00 00 00
973	recv	94	|.~..............|		02 7e 00 02 00 09 00 02 02 00 00 00 10 01 00 1f
1734	recv	14	|.....$....b:.q.....f..a.................5..|		04 15 06 03 00 24 00 01 04 00 62 3a f5 71 02 08 00 a6 a0 66 16 ab 61 de 16 03 04 00 00 00 00 00 10 08 00 0a e3 00 e2 0e 35 00 00
982	send	94	|......|		00 7f 00 00 00 00 00
973	recv	94	|...........|		02 7f 00 00 00 05 00 02 02 00 00 00
977	recv	97	|..............i..q........:).....|		04 af 00 05 00 1a 00 01 01 00 1a 02 04 00 69 8f f5 71 03 0c 00 00 00 01 00 a0 3a 29 b2 00 00 00 00
977	recv	97	|.................q........:).....|		04 b0 00 05 00 1a 00 01 01 00 1a 02 04 00 ce a3 f5 71 03 0c 00 00 00 00 00 a0 3a 29 b2 00 00 00 00
977	recv	97	|.................q........:).....|		04 b1 00 05 00 1a 00 01 01 00 1a 02 04 00 94 10 f6 71 03 0c 00 00 00 01 00 a0 3a 29 b2 00 00 00 00
977	recv	97	|.................q........:).....|		04 b2 00 05 00 1a 00 01 01 00 1a 02 04 00 13 19 f6 71 03 0c 00 00 00 00 00 a0 3a 29 b2 00 00 00 00
977	recv	97	|.................q........:).....|		04 b3 00 05 00 1a 00 01 01 00 1a 02 04 00 df 1f f6 71 03 0c 00 00 00 01 00 a0 3a 29 b2 00 00 00 00
977	recv	97	|.............._(.q........:).....|		04 b4 00 05 00 1a 00 01 01 00 1a 02 04 00 5f 28 f6 71 03 0c 00 00 00 00 00 a0 3a 29 b2 00 00 00 00
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;figcaption&gt;
Columns from left to right: PID, direction (send/receive), socket file descriptor, ASCII representation, bytes in hexadecimal.
&lt;/figcaption&gt;

&lt;p&gt;Looking at the last six packets, some interesting patterns are found:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The second byte in each packet (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var2&lt;/code&gt;) is incremented by 1 on each packet. It might be some sort of a counter.&lt;/li&gt;
  &lt;li&gt;Bytes 14, 15 and 16 seem to be part of a single variable that is always increasing. Along with byte 17, they likely make up an unsigned 32-bit timestamp, as knowing the time when a sensor reading was taken is needed for calculating relative changes among other things, and there are no other bytes that might represent time here.&lt;/li&gt;
  &lt;li&gt;The 24&lt;sup&gt;th&lt;/sup&gt; Byte alternates between 1 and 0. This makes it almost certainly a boolean carrying the proximity sensor data, as it exactly matches the covering/uncovering of the sensor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another pattern can be found by taking a look at all the packets including the ones in the first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sendto&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;recvfrom&lt;/code&gt; syscalls:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The first byte is always 0 when a packet is sent, and either 2 or 4 when it is received. This might be a header with some specific meaning for each of those three values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The other values remain constant, so it will not be possible to understand their purpose without some external reference or context that would give them meaning.&lt;/p&gt;

&lt;p&gt;With the new findings, an updated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct prox_pkt&lt;/code&gt; would look like this:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prox_pkt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sensor_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Some important fields have been identified so far, but the purpose of most still remains unknown.&lt;/p&gt;

&lt;p&gt;The next step would be to identify the format of this packet, and for that, there are a couple of clues. First of all, the packets traced are moving to and from a Hexagon DSP on a Qualcomm SoC. These tend to use a certain protocol, whose packets – or &lt;em&gt;messages&lt;/em&gt; – all have a unique header section. One that has, like in the beginning of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct prox_pkt&lt;/code&gt;, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u8&lt;/code&gt; followed by three &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u16&lt;/code&gt;s…&lt;/p&gt;

&lt;h2 id=&quot;introducing-qmi&quot;&gt;Introducing QMI&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;Qualcomm MSM Interface&lt;/em&gt; is a protocol originally developed for interfacing with baseband processors on the MSM line of SoCs, that eventually ended up as the primary interface between the application processor and remote processors on Qualcomm SoCs such as MPSS, ADSP, and now SLPI.&lt;/p&gt;

&lt;h3 id=&quot;qmi-messages&quot;&gt;QMI messages&lt;/h3&gt;
&lt;p&gt;Processors on the SoC communicate by sending messages over QMI, which are transferred through shared memory. As mentioned earlier, QMI messages have a &lt;a href=&quot;https://elixir.bootlin.com/linux/latest/source/include/linux/soc/qcom/qmi.h#L25&quot;&gt;common header&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/**
 * struct qmi_header - wireformat header of QMI messages
 * @type:	type of message
 * @txn_id:	transaction id
 * @msg_id:	message id
 * @msg_len:	length of message payload following header
 */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qmi_header&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;txn_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__packed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To understand what these fields mean, it would help to understand a couple of things about QMI first:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;QMI messages are classified into three types:
    &lt;ol&gt;
      &lt;li&gt;Request, which is sent from a processor to another, which is expected to send back a message of the second type:&lt;/li&gt;
      &lt;li&gt;Response, Sent after receiving a request.&lt;/li&gt;
      &lt;li&gt;Indication, which is sent without expecting a response.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;A pair of request and response or a single indication is called a &lt;em&gt;transmission&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Going back to the QMI header:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt; can be one of &lt;a href=&quot;https://elixir.bootlin.com/linux/latest/source/include/linux/soc/qcom/qmi.h#L32&quot;&gt;three possible values&lt;/a&gt;, which match the three QMI message types:
    &lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define QMI_REQUEST	0
#define QMI_RESPONSE	2
#define QMI_INDICATION	4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;txn_id&lt;/code&gt; is a unique identifier for a transmission. It is generally incremented after each transmission, causing the counting pattern observed in the proximity sensor messages. This identifier allows for pairing requests with their responses; as matching pairs have the same identifier.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msg_id&lt;/code&gt; is used to identify a specific type of message. Messages of a certain type have the same format and purpose. This property will be explored later.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msg_len&lt;/code&gt; is the length of the message in bytes, header excluded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Adjusting for the QMI header, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct prox_pkt&lt;/code&gt; will look like this:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prox_pkt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qmi_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var5&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sensor_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;reading-a-qmi-header&quot;&gt;Reading a QMI header&lt;/h3&gt;
&lt;p&gt;Taking the first proximity sensor message from before:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;04 b2 00 05 00 1a 00 01 01 00 1a 02 04 00 13 19 f6 71 03 0c 00 00 00 00 00 a0 3a 29 b2 00 00 00 00
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The header, which is the first seven bytes, can be extracted:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;04 b2 00 05 00 1a 00
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To read the header, the bytes are put in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct qmi_header&lt;/code&gt;. The first byte is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u8 type&lt;/code&gt;, and the following three pairs of bytes make up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u16 txn_id, msg_id, msg_len&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qmi_header&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prox_msg_hdr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QMI_INDICATION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;txn_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xb2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;msg_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x05&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;msg_len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x1a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This message is SSC &lt;em&gt;indicating&lt;/em&gt; the state of the proximity sensor, so its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt; being &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QMI_INDICATION&lt;/code&gt; makes sense as it would not need a response. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;txn_id&lt;/code&gt; does not carry any significant meaning; as it is only used to track the message as it moves around, and since this message is an indication, there is no request to pair it to. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msg_id&lt;/code&gt; specifies the type of this message and identifies its format so that it can be decoded properly. This property will become useful at a later stage. Finally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msg_len&lt;/code&gt; tells us that the following message should be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x1a&lt;/code&gt;, or 26 bytes long.&lt;/p&gt;

&lt;h3 id=&quot;qmi-encoding-scheme&quot;&gt;QMI encoding scheme&lt;/h3&gt;
&lt;p&gt;QMI messages are encoded in a &lt;a href=&quot;https://en.wikipedia.org/wiki/Type%E2%80%93length%E2%80%93value&quot;&gt;Type–Length–Value&lt;/a&gt; (TLV for short) scheme, where data fields, which are usually called &lt;em&gt;elements&lt;/em&gt; in QMI, are put in blocks, each having a &lt;em&gt;type&lt;/em&gt; value that is used to identify the field, a &lt;em&gt;length&lt;/em&gt; value specifying the length of the data carried in the field, and a &lt;em&gt;value&lt;/em&gt; which holds the actual data.&lt;/p&gt;

&lt;h3 id=&quot;decoding-qmi-messages&quot;&gt;Decoding QMI messages&lt;/h3&gt;
&lt;p&gt;Since QMI messages are TLV-encoded, many of the unknown variables in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct prox_pkt&lt;/code&gt; guessed earlier are in fact types and lengths rather than actual values. Now that the encoding scheme is known, it should be possible to decode the data captured in the previous stage. The message elements can be discovered by going through the data byte by byte and splitting them into TLV blocks.&lt;/p&gt;

&lt;p&gt;Looking at the &lt;a href=&quot;https://elixir.bootlin.com/linux/latest/source/drivers/soc/qcom/qmi_encdec.c&quot;&gt;QMI encoding/decoding helper in Linux&lt;/a&gt;, it can be understood how the TLV blocks are encoded. The first thing in the file is a macro used to encode a TLV block:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define QMI_ENCDEC_ENCODE_TLV(type, length, p_dst) do { \
	*p_dst++ = type; \
	*p_dst++ = ((u8)((length) &amp;amp; 0xFF)); \
	*p_dst++ = ((u8)(((length) &amp;gt;&amp;gt; 8) &amp;amp; 0xFF)); \
} while (0)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This writes one byte for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt; into a buffer, followed by two bytes for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;length&lt;/code&gt;. These sizes are further confirmed after looking a bit deeper into the file:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define TLV_LEN_SIZE sizeof(u16)
#define TLV_TYPE_SIZE sizeof(u8)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A message is now taken from before&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;04 b1 00 05 00 1a 00 01 01 00 1a 02 04 00 94 10 f6 71 03 0c 00 00 00 01 00 a0 3a 29 b2 00 00 00 00
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;and is split into TLV blocks by taking one byte for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt;, two bytes for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;length&lt;/code&gt;, then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;length&lt;/code&gt; bytes for the actual value. This is repeated until all blocks are found:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;04 b1 00 05 00 1a 00

01 01 00 1a
02 04 00 94 10 f6 71
03 0c 00 00 00 01 00 a0 3a 29 b2 00 00 00 00
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The first line is the header which was discussed earlier. The following lines are the TLV blocks of elements carried in the message, with the first byte being the type, the next 2 bytes the length, and the remaining bytes the value. A closer look is taken at each element:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The first element is a single byte, which makes it likely to be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u8&lt;/code&gt;. Its value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x1a&lt;/code&gt;, which remains constant across all messages traced before. Being the first element in the message, it might be some sort of sensor identifier. This can be confirmed later by capturing messages carrying data from other sensors, then comparing this value to the one in those messages.&lt;/li&gt;
  &lt;li&gt;The second element is 4 bytes long. This is the timestamp from before, which is now confirmed to be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u32&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;The third element is 12 bytes long. It might be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u8&lt;/code&gt; array with a size of 12, but it could also be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u16&lt;/code&gt; array with a size of 6, or a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u32&lt;/code&gt; array with a size of 3. Part of this element’s value is the proximity sensor value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sensor_val&lt;/code&gt; that was discovered earlier. Considering that this message carries sensor data, and many sensors have 3 axes, the latter possibility is more likely even if the proximity sensor only outputs a boolean; as the message format might be the same for several, if not all sensor types.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now to put these elements in a new struct:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prox_ind&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sensor_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That looks much better than the previous &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct prox_pkt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now to create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct prox_ind&lt;/code&gt; with the data from the message above:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prox_ind&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ind1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sensor_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x1a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1911951508&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;mi&quot;&gt;2989046432&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The consecutive message can also be decoded for comparison:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;04 b2 00 05 00 1a 00 01 01 00 1a 02 04 00 13 19 f6 71 03 0c 00 00 00 00 00 a0 3a 29 b2 00 00 00 00
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;04 b2 00 05 00 1a 00

01 01 00 1a
02 04 00 13 19 f6 71
03 0c 00 00 00 00 00 a0 3a 29 b2 00 00 00 00
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prox_ind&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ind2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sensor_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x1a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1911953683&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;mi&quot;&gt;2989046432&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The timestamp increased in the second message, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data[0]&lt;/code&gt; changed to 0 indicating that the proximity sensor was uncovered. There is a strange large constant value in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data[1]&lt;/code&gt; though. It might be good to try to interpret that at some later stage.&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;
&lt;p&gt;After developing a method to capture and decode messages transferred between the android sensor service and the SSC, more information can be collected on their operation by analyzing messages at different times, such as with the screen on or with an app using data from specific sensors. However, all of these discoveries would be useless without figuring out how the sensor service sends and receives these messages, and how to do the same thing on mainline Linux. This, along with a second method to capture messages, will be explored next time.&lt;/p&gt;
</content>
		<link
			href="https://emainline.gitlab.io/2022/04/08/Unlocking_SSC_P2.html"
			rel="alternate" type="text/html" title="Unlocking the Qualcomm Snapdragon Sensor Core (Part 2)"
		/>
		<id isPermaLink="true">https://emainline.gitlab.io/2022/04/08/Unlocking_SSC_P2.html</id>
		
		
	</entry>
	
	<entry>
		
		
		<title>Fingerprint sensors on tuxified Android phones: Impossible? (Part 1)</title>
		<description>Finding ways to enable the fingerprint sensor of the Xiaomi Mi Note 2 on Linux</description>
		<author>Yassine Oudjana</author>
		<published>Sun, 12 Dec 2021 00:00:00 +0000</published>
		<content type="html">&lt;p&gt;&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/cover/fingerprint_p1.jpg&quot; width=&quot;100%&quot; /&gt;
Fast authentication is one aspect where Linux phones are still lacking. As of now, the only common way to unlock a phone running Linux is to use a PIN or password. While that may have security benefits to some, it is inconvenient for many, who would rather use a faster method to unlock their phones such as a fingerprint sensor. Possibly the only phone running Linux that can be unlocked with a fingerprint at the moment is a PinePhone fitted with a &lt;a href=&quot;https://www.reddit.com/r/PINE64official/comments/l40esv/fingerprint_scanner_update_the_new_prototype_is&quot;&gt;prototype fingerprint sensor case&lt;/a&gt;. Linux phones such as the PinePhone (by default at least) and the Librem 5 might not have fingerprint sensors, but what about Android phones running Linux? Nowadays even the cheapest Android phones have some sort of fingerprint sensor, so why don’t we simply run Linux on those to get fingerprint authentication on a Linux phone? Making an attempt at enabling the fingerprint sensor of an actual Android device on the mainline Linux kernel should provide us with some answers.&lt;/p&gt;

&lt;p&gt;The Xiaomi Mi Note 2 will be used as a subject in this experiment. It has a fingerprint sensor built into its home button, and it can already run the mainline Linux kernel to a great extent, so enabling the fingerprint sensor would not take long, or would it?&lt;/p&gt;

&lt;h2 id=&quot;enabling-the-fingerprint-sensor&quot;&gt;Enabling the fingerprint sensor&lt;/h2&gt;
&lt;h3 id=&quot;collecting-information&quot;&gt;Collecting information&lt;/h3&gt;
&lt;p&gt;As usual, the first place to look for hardware information is the vendor device tree found in the downstream kernel source. &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/a4-msm8996-mtp.dtsi#L96-L106&quot;&gt;This node&lt;/a&gt; seems interesting:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;nf&quot;&gt;fpc_fpc1020&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;compatible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;fpc,fpc1020&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;fpc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irq-gpio&lt;/span&gt;	&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tlmm&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;121&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x2001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;fpc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fp-id-gpio&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pm8994_gpios&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pinctrl-names&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pmx_fp_active&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pmx_fp_suspend&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pinctrl-0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fp_active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pinctrl-1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fp_suspend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;vcc_spi-supply&lt;/span&gt;	&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pm8994_s4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;vdd_ana-supply&lt;/span&gt;	&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pm8994_s4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;vdd_io-supply&lt;/span&gt;	&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pm8994_s4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.fingerprints.com/technology/hardware/sensors/fpc1020/&quot;&gt;official product page from Fingerprint Cards&lt;/a&gt; is the first match when searching for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fpc1020&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The FPC1020 is a new compact CMOS fingerprint sensor with several significant advantages. It delivers best-in-class image quality with 256 gray-scale values in every single programmable pixel. The sensor with its 3D pixel-sensing technology can read virtually any finger – dry or wet. Thanks to the extremely hard and durable surface coating capable of withstanding more than 10 million finger placements, the FPC1020 is protected against ESD well above 30 kV as well as against scratches and everyday wear and tear.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Looks well enough like a fingerprint sensor.&lt;/p&gt;

&lt;p&gt;This line gives us another hint:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;		vcc_spi-supply	= &amp;lt;&amp;amp;pm8994_s4&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It makes clear that this sensor communicates over a &lt;em&gt;Serial Peripheral Interface&lt;/em&gt;, or &lt;em&gt;SPI&lt;/em&gt; for short. Strangely however, this node is a child of the root node, when it is supposed to be a subnode of the SPI master node the sensor is wired up to. In fact, there is no indication to be found in this device tree of which SPI master this sensor is connected to. This leaves us with the other main source of hardware information: Schematics.&lt;/p&gt;

&lt;p&gt;Looking at the SoC GPIO page, the connection between fingerprint sensor and SoC is quickly identified by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FP&lt;/code&gt; prefix on the pin names:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/content/fingerprint_gpio.png&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/msm8996-staging/drivers/pinctrl/qcom/pinctrl-msm8996.c#L1204-1206&quot;&gt;MSM8996 pin controller driver&lt;/a&gt; then makes it clear that SPI 5 is exposed on these pins:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blsp_spi5_groups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;gpio81&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio82&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio83&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gpio84&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;enabling-the-spi-master&quot;&gt;Enabling the SPI master&lt;/h3&gt;
&lt;p&gt;If this works, it should become possible to communicate with the fingerprint sensor.&lt;/p&gt;

&lt;p&gt;The SoC DTS is lacking a node for SPI 5, so it is the first thing that should be added. Pin state nodes are added to configure the pin controller, then a node for the SPI master is added:&lt;/p&gt;
&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;--- a/arch/arm64/boot/dts/qcom/msm8996.dtsi
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -1507,6 +1507,30 @@&lt;/span&gt; cdc_reset_sleep: cdc-reset-sleep {
 				output-low;
 			};
 
&lt;span class=&quot;gi&quot;&gt;+			blsp1_spi5_default: blsp1-spi5-default {
+				spi {
+					pins = &quot;gpio81&quot;, &quot;gpio82&quot;, &quot;gpio84&quot;;
+					function = &quot;blsp_spi5&quot;;
+					drive-strength = &amp;lt;12&amp;gt;;
+					bias-disable;
+				};
+
+				cs {
+					pins = &quot;gpio83&quot;;
+					function = &quot;gpio&quot;;
+					drive-strength = &amp;lt;16&amp;gt;;
+					bias-disable;
+					output-high;
+				};
+			};
+
+			blsp1_spi5_sleep: blsp1-spi5-sleep {
+				pins = &quot;gpio81&quot;, &quot;gpio82&quot;, &quot;gpio83&quot;, &quot;gpio84&quot;;
+				function = &quot;gpio&quot;;
+				drive-strength = &amp;lt;2&amp;gt;;
+				bias-pull-down;
+			};
+
&lt;/span&gt; 			blsp2_spi6_default: blsp2-spi5-default {
 				spi {
 					pins = &quot;gpio85&quot;, &quot;gpio86&quot;, &quot;gpio88&quot;;
&lt;span class=&quot;p&quot;&gt;@@ -3093,6 +3117,23 @@&lt;/span&gt; blsp1_i2c5: i2c@757a000 {
 			status = &quot;disabled&quot;;
 		};
 
&lt;span class=&quot;gi&quot;&gt;+		blsp1_spi5: spi@7579000 {
+			compatible = &quot;qcom,spi-qup-v2.2.1&quot;;
+			reg = &amp;lt;0x07579000 0x600&amp;gt;;
+			interrupts = &amp;lt;GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH&amp;gt;;
+			clocks = &amp;lt;&amp;amp;gcc GCC_BLSP1_QUP5_SPI_APPS_CLK&amp;gt;,
+				 &amp;lt;&amp;amp;gcc GCC_BLSP1_AHB_CLK&amp;gt;;
+			clock-names = &quot;core&quot;, &quot;iface&quot;;
+			pinctrl-names = &quot;default&quot;, &quot;sleep&quot;;
+			pinctrl-0 = &amp;lt;&amp;amp;blsp1_spi5_default&amp;gt;;
+			pinctrl-1 = &amp;lt;&amp;amp;blsp1_spi5_sleep&amp;gt;;
+			dmas = &amp;lt;&amp;amp;blsp1_dma 20&amp;gt;, &amp;lt;&amp;amp;blsp1_dma 21&amp;gt;;
+			dma-names = &quot;tx&quot;, &quot;rx&quot;;
+			#address-cells = &amp;lt;1&amp;gt;;
+			#size-cells = &amp;lt;0&amp;gt;;
+			status = &quot;disabled&quot;;
+		};
+
&lt;/span&gt; 		blsp2_dma: dma@7584000 {
 			compatible = &quot;qcom,bam-v1.7.0&quot;;
 			reg = &amp;lt;0x07584000 0x2b000&amp;gt;;
&lt;span class=&quot;gd&quot;&gt;--
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now to enable it in the device DTS:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blsp1_spi5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;okay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Enabling this SPI master ends up freezing the device as soon as the its driver probes and tries to initialize it. But why?&lt;/p&gt;

&lt;p&gt;Perhaps taking a closer look at how fingerprint authentication is handled in Android would provide some answers as to why this happened.&lt;/p&gt;

&lt;h2 id=&quot;fingerprint-sensors-in-android&quot;&gt;Fingerprint sensors in Android&lt;/h2&gt;
&lt;p&gt;Fingerprint sensors are generally managed in Android’s &lt;em&gt;Hardware Abstraction Layer&lt;/em&gt;, or &lt;em&gt;HAL&lt;/em&gt;, which mostly consists of proprietary software provided by the vendor. The HAL has some sort of daemon which communicates with the &lt;em&gt;Trusted Execution Environment&lt;/em&gt; (&lt;em&gt;TEE&lt;/em&gt; for short), where all the real magic happens. The daemon loads a trustlet—a small signed program into the TEE where it is executed. This trustlet includes the actual driver for the fingerprint sensor, and is what scans and matches fingerprints. The daemon would request authentication from the trustlet, which would then scan and match a fingerprint in the secure world, and send a message back to the normal world indicating whether authentication was successful or not. So the real work happens in the secure world, and the normal world only gets to know whether to grant or deny access.&lt;/p&gt;

&lt;h2 id=&quot;tee-secure-world-normal-world-what&quot;&gt;TEE? Secure world? Normal world? What?&lt;/h2&gt;
&lt;p&gt;To put simply, ARM CPUs have a hardware mechanism known as &lt;em&gt;TrustZone&lt;/em&gt; that allows for running two operating systems in parallel, one being the normal OS (Linux in this case) running in the &lt;em&gt;normal world&lt;/em&gt;, as well as a &lt;em&gt;trusted OS&lt;/em&gt; running in the &lt;em&gt;secure world&lt;/em&gt; and providing the system with a &lt;em&gt;trusted execution environment&lt;/em&gt;. The normal world’s bus and memory access can be restricted so that select busses and memory ranges become only accessible in the secure world. This is the case with the SPI master that we tried to access from Linux earlier, the access of which was restricted to the secure world. Trying to access it from the normal world caused an exception which halted the execution of Linux. The trusted OS might have printed some error messages over debug UART when that happened, but it is unfortunately inaccessible on this production device, at least without some delicate hardware modifications, so there is no way to confirm that at the moment.&lt;/p&gt;

&lt;h2 id=&quot;what-can-be-done&quot;&gt;What can be done?&lt;/h2&gt;
&lt;p&gt;It would seem that the only option to make use of the fingerprint sensor on Linux would be to do it the Android way. Similar to other TEE implementations, the &lt;em&gt;Qualcomm Secure Execution Environment&lt;/em&gt; – or &lt;em&gt;QSEE&lt;/em&gt; – has a mechanism to handle communications with the normal OS called &lt;em&gt;QSEECOM&lt;/em&gt;, short for &lt;em&gt;QSEE COMmunicator&lt;/em&gt;, which the mainline Linux kernel is lacking a driver for. Writing a driver in the mainline kernel for QSEECOM should be possible thanks to the availability of downstream driver source code, but another driver, possibly in userspace, will be needed to replace the Android HAL daemon and handle loading and communicating with the proprietary fingerprint sensor trustlet provided by the vendor. Unfortunately, it would be quite difficult to make this second driver due to the lack of open source code or documentation.&lt;/p&gt;

&lt;h2 id=&quot;the-end&quot;&gt;The end?&lt;/h2&gt;
&lt;p&gt;Could this really be a dead end? Or is there still something that can be done? Backtracking a bit might help us find a way.&lt;/p&gt;

&lt;p&gt;The SPI master inside the SoC connects to the fingerprint sensor through a set of GPIO pins. These pins are also usually secured on Android phones, making it a necessary step when porting the mainline kernel to a phone with a fingerprint sensor to mark them in the device tree as reserved GPIOs, so that the pin controller driver does not try to initialize them or modify their states and end up causing something similar to what happened after enabling the secured SPI master. Somehow, the Mi Note 2 can run the mainline kernel just fine without reserving any pins. Could this actually mean that the pins the fingerprint sensor is wired to are not secured on this device?&lt;/p&gt;

&lt;h2 id=&quot;skipping-the-hardware-spi-master&quot;&gt;Skipping the hardware SPI master&lt;/h2&gt;
&lt;p&gt;If the only thing that is secured is the SPI master, then it should be possible to simply skip it all together and use the GPIO pins directly using &lt;a href=&quot;https://en.wikipedia.org/wiki/Bit_banging&quot;&gt;bit banging&lt;/a&gt; to form a software-based SPI master. Software SPI is much slower than its hardware counterpart, but it should still be more than enough for a fingerprint sensor.&lt;/p&gt;

&lt;p&gt;Linux has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spi-gpio&lt;/code&gt; driver that can do this, and all that is needed to use it is to add a node to the device tree, specifying the GPIO pins to use for SPI. In this case, the four GPIO pins originally used by SPI 5 are used:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;nf&quot;&gt;spi&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;compatible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;spi-gpio&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;mosi-gpios&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tlmm&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;81&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;miso-gpios&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tlmm&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;82&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;cs-gpios&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tlmm&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;83&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GPIO_ACTIVE_LOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;sck-gpios&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tlmm&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;84&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;num-chipselects&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Booting with this added to the device tree caused no issues. This does not mean it will necessarily work, but is a good sign nevertheless. Now we can proceed to write a driver for the fingerprint sensor.&lt;/p&gt;

&lt;h2 id=&quot;writing-a-driver&quot;&gt;Writing a driver&lt;/h2&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ls linux/drivers
accessibility  char         dio       greybus     interconnect  memory    nvdimm    pnp         rtc        target       virtio
acpi           clk          dma       hid         iommu         memstick  nvme      power       s390       tc           visorbus
amba           clocksource  dma-buf   hsi         ipack         message   nvmem     powercap    sbus       tee          vlynq
android        comedi       edac      hv          irqchip       mfd       of        pps         scsi       thermal      vme
ata            connector    eisa      hwmon       isdn          misc      opp       ps3         sh         thunderbolt  w1
atm            counter      extcon    hwspinlock  Kconfig       mmc       parisc    ptp         siox       tty          watchdog
auxdisplay     cpufreq      firewire  hwtracing   leds          most      parport   pwm         slimbus    uio          xen
base           cpuidle      firmware  i2c         macintosh     mtd       pci       rapidio     soc        usb          zorro
bcma           crypto       fpga      i3c         mailbox       mux       pcmcia    ras         soundwire  vdpa
block          cxl          fsi       idle        Makefile      net       perf      regulator   spi        vfio
bluetooth      dax          gnss      iio         mcb           nfc       phy       remoteproc  spmi       vhost
bus            dca          gpio      infiniband  md            ntb       pinctrl   reset       ssb        video
cdrom          devfreq      gpu       input       media         nubus     platform  rpmsg       staging    virt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Wait, where do fingerprint sensor drivers go?&lt;/p&gt;

&lt;p&gt;It appears that the Linux kernel does not have a fingeprint subsystem, and the only way to use a fingeprint sensor at the moment is through userspace drivers, most of which are part of &lt;a href=&quot;https://gitlab.freedesktop.org/libfprint/libfprint/-/tree/master/libfprint/drivers&quot;&gt;libfprint&lt;/a&gt;. While userspace drivers might be good enough for polled USB sensors, they would not be enough for this device where handling interrupts and regulators is necessary, something that is not easily done in userspace. Libfprint has a few drivers for SPI sensors, but they are all made for ACPI platforms where the driver does not have to worry about regulators and can simply rely on ACPI to do the work. The current aim is to find out if it is possible at all to communicate with the sensor from Linux, so for now, a small SPI class driver placed anywhere in the kernel should be enough.&lt;/p&gt;

&lt;h3 id=&quot;collecting-information-1&quot;&gt;Collecting information&lt;/h3&gt;
&lt;p&gt;As always, the main sources of information about the hardware are datasheets in first place, followed by downstream driver source code. A quick search for FPC1020 reveals &lt;a href=&quot;https://github.com/SanniZ/fpc1020-driver&quot;&gt;source code of a downstream driver&lt;/a&gt;. Not only that, but this repository happens to also include a &lt;a href=&quot;https://github.com/SanniZ/fpc1020-driver/blob/master/Documentation/710-FPC1140A_ASIC_Product%20Specification_Rev.PA3_ES1%20(1).pdf&quot;&gt;datasheet for FPC1140&lt;/a&gt; which is part of the FPC1020 series. Perfect.&lt;/p&gt;

&lt;p&gt;The datasheet includes a register map containing information about all the available registers on the sensor. This register looks like a good starting point:
&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/content/fpc1140_hwid.png&quot; width=&quot;100%&quot; /&gt;
If the sensor is working, reading this register would succeed, and the read value should match the format shown in the datasheet.&lt;/p&gt;

&lt;h3 id=&quot;writing-the-initial-driver&quot;&gt;Writing the initial driver&lt;/h3&gt;
&lt;p&gt;Being a temporary driver, diving into the details at this point is unnecessary. Initialization steps and other parts such as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fpc1020_access_reg&lt;/code&gt; function used to handle SPI transfers needed for accessing registers are not covered here. The final revision of this temporary driver can be found &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/msm8996-staging-fpc1020/drivers/input/misc/fpc1020.c&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, to &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/msm8996-staging-fpc1020/drivers/input/misc/fpc1020.c#L306-313&quot;&gt;read the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hwID&lt;/code&gt; register&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fpc1020_access_reg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FPC102X_REG_HWID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FPC102X_REG_HWID_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hw_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to read HW ID: %d&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;dev_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Chip type: FPC1%x%X&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hw_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hw_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here comes the moment of truth:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[   50.284353] fpc1020 spi0.0: Chip type: FPC1140C
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No crashes, no freezes and no errors so far. Looks like it worked, and now we know that this phone has a FPC1140 revision C.&lt;/p&gt;

&lt;p&gt;In fact, after adding some extra code, a fingerprint can be captured and printed as kernel messages, a tiny section of which can be seen in the cover image of this post.&lt;/p&gt;

&lt;p&gt;So these four GPIO pins are definitely not secured. But why? Was it a workaround for a last-minute bug that the engineers at Xiaomi did not have the time to fix properly? Did someone simply forget to secure them? Perhaps we will never know. This also seems to be the case on the Mi 5, another Xiaomi device with the MSM8996 SoC which was able to boot the mainline kernel without reserving any pins. Following this pattern, this might also be the case in the remaining three devices of the Xiaomi MSM8996 platform: Mi 5s, Mi Mix and Mi 5s Plus.&lt;/p&gt;

&lt;p&gt;The existence of a downstream driver for this fingeprint sensor is also interesting, as it could mean that there are Android phones where the fingerprint sensor is driven by Linux in the normal world by default.&lt;/p&gt;

&lt;h2 id=&quot;what-comes-next&quot;&gt;What comes next&lt;/h2&gt;
&lt;p&gt;Now that a way to communicate with the sensor is found, it is time to write a proper driver, and eventually be able to authenticate using a fingerprint. As mentioned earlier, Linux has no fingerprint subsystem, and this sensor will require handling regulators and interrupts that must be done in kernel space. This leaves us with two options: either to write a small kernel driver only to handle regulators and somehow also deal with interrupts, then do the rest in userspace, or to make a fingerprint subsystem, which would provide a base to support this and other fingerprint sensors in the kernel. This choice will, however, have to wait for the next part of this series.&lt;/p&gt;
</content>
		<link
			href="https://emainline.gitlab.io/2021/12/12/fingerprint_P1.html"
			rel="alternate" type="text/html" title="Fingerprint sensors on tuxified Android phones: Impossible? (Part 1)"
		/>
		<id isPermaLink="true">https://emainline.gitlab.io/2021/12/12/fingerprint_P1.html</id>
		
		
	</entry>
	
	<entry>
		
		
		<title>Enabling hardware navigation buttons on Linux</title>
		<description>Writing a driver to make use of the navigation buttons on the Xiaomi Mi Note 2</description>
		<author>Yassine Oudjana</author>
		<published>Wed, 27 Oct 2021 00:00:00 +0000</published>
		<content type="html">&lt;p&gt;&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/cover/touchkey.jpg&quot; width=&quot;100%&quot; /&gt;
The Xiaomi Mi Note 2 is old enough to (fortunately) have hardware navigation buttons. Let us see what it takes to make them work on the mainline Linux kernel.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the first driver I ever wrote. The simplicity of I&lt;sup&gt;2&lt;/sup&gt;C input devices, especially one with just 2 buttons made it a perfect entry point into driver development.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;collecting-information&quot;&gt;Collecting information&lt;/h2&gt;
&lt;p&gt;Usually the first step in enabling hardware on the mainline kernel is to take a look at the stock device tree. For the un-initiated, a &lt;a href=&quot;https://elinux.org/Device_Tree_Reference&quot;&gt;Device Tree&lt;/a&gt; is a method of describing hardware. The OS reads a device tree to find out what hardware is available on the device it is running on and how it is wired up so that it can properly operate it.&lt;/p&gt;

&lt;p&gt;The chip that controls hardware navigation buttons is usually called a &lt;em&gt;touchkey controller&lt;/em&gt;. Many devices combine that functionality into the touchscreen, placing the navigation buttons in a specific zone, then treating touches in that zone as key presses.&lt;/p&gt;

&lt;p&gt;However, that does not seem to be the case in this device.&lt;/p&gt;

&lt;p&gt;Looking at the stock device tree, &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/a4-msm8996-mtp.dtsi#L291-L314&quot;&gt;this node&lt;/a&gt; is found:
&lt;!-- replace with dts once support for dts highlighting is added --&gt;&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;cyttsp_streetfighter_p2@&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;compatible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cypress,sf3155&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;reg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;interrupt-parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tlmm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;interrupts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;77&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x2002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;soft-reset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irq-gpio&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tlmm&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;78&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x2002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irqflags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x2002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cyttsp_button&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key-num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key-codes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;139&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;158&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button-status-reg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x4A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bootloader-addr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x56&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config-array-size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;standby-reg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;softreset-reg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x05&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;pinctrl-names&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pmx_btn_active&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pmx_btn_suspend&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;pinctrl-0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;btn_active_a4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;pinctrl-1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;btn_suspend_a4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cfg_1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hw-version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x84&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fw-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cyttsp_button_no_shielding.fw&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It is likely to be the touchkey controller; since it is on &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/a4-msm8996-mtp.dtsi#L121-L315&quot;&gt;the same I&lt;sup&gt;2&lt;/sup&gt;C bus&lt;/a&gt; as the touchscreen. This line, however, makes it certain:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key-codes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;139&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;158&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Looking at &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/msm8996-staging/include/uapi/linux/input-event-codes.h&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux-event-codes.h&lt;/code&gt;&lt;/a&gt;, these key codes translate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEY_MENU&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEY_BACK&lt;/code&gt;, which are the functions of the navigation buttons.&lt;/p&gt;

&lt;p&gt;Using the compatible string, the driver that matches this node in the downstream kernel can be found:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/drivers/input/touchscreen/cyttsp_button.c#L1562&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;drivers/input/touchscreen/cyttsp_button.c:1561&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of_device_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cyttsp_match_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compatible&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cypress,sf3155&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,},&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;cypress-streetfighter&quot;&gt;Cypress Streetfighter&lt;/h2&gt;
&lt;p&gt;&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/content/cypress-sf.svg&quot; width=&quot;100%&quot; /&gt;
&lt;em&gt;Cool name, right? You don’t see components named like this everyday…&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;writing-a-driver&quot;&gt;Writing a driver&lt;/h2&gt;
&lt;p&gt;We start off with the general driver structure, which is similar across all kinds of drivers:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;device_probe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class_device&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;device_remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class_device&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of_device_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compatible&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;vendor,device&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* sentinel */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MODULE_DEVICE_TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class_driver&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_driver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;driver_name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;of_match_table&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id_table&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;probe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_probe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_remove&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;module_class_driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device_driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now to put it in more detail:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is an optional struct. It is conventionally used to hold all information about the device, usually collected during probing, and pass it to functions in the driver.&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_probe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class_device&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is the probe function. It is the first function called in any driver, and is used to allocate resources for the driver, collect information about the hardware and initialize it.&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class_device&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;An optional function used to deinitialize the hardware if needed, as well as free any resources that have to be explicitly freed.&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of_device_id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compatible&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;vendor,device&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* sentinel */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MODULE_DEVICE_TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This table contains compatible strings used to match nodes in the device tree with the driver. When Linux finds a node with a matching compatible string, it loads an instance of the driver and passes the device tree node to it.&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class_driver&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_driver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;driver_name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;of_match_table&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id_table&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;probe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_probe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_remove&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;module_class_driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device_driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This last struct holds information about the driver, as well as pointers to the probe and remove functions. it is used to register the driver.&lt;/p&gt;

&lt;p&gt;There are several driver classes, which are part of the multiple subsystems of the kernel. Each one has a slightly different driver structure. The main differences are marked with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class&lt;/code&gt; placeholder above.&lt;/p&gt;

&lt;p&gt;The Linux Kernel Documentation is quite useful when it comes to writing a driver. It always helps to read about the subsystems and frameworks needed for a specific device when writing a driver for it. For this device, the Input and I&lt;sup&gt;2&lt;/sup&gt;C subsystems are the ones of interest to us:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.kernel.org/doc/html/latest/i2c/writing-clients.html&quot;&gt;Implementing I&lt;sup&gt;2&lt;/sup&gt;C device drivers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.kernel.org/doc/html/latest/input/input-programming.html&quot;&gt;Creating an input device driver&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This driver will follow the structure shown in the &lt;em&gt;Implementing I&lt;sup&gt;2&lt;/sup&gt;C device drivers&lt;/em&gt; page, and use bits from the &lt;em&gt;Creating an input device driver&lt;/em&gt; page as needed.&lt;/p&gt;
&lt;h3 id=&quot;first-steps&quot;&gt;First steps&lt;/h3&gt;
&lt;p&gt;The first step is to define a data struct to hold information that has to be moved around. Initially, this will hold pointers to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2c_client&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input_dev&lt;/code&gt; of this device. It will be later expanded as needed.&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cypress_sf_data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i2c_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, a probe function is added:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cypress_sf_probe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i2c_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i2c_device_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This will contain everything required to acquire resources for the driver as well as to initialize the hardware.&lt;/p&gt;

&lt;p&gt;First, memory is allocated for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct cypress_sf_data&lt;/code&gt;. This struct will be used throughout the life of the driver.&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cypress_sf_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devm_kzalloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GFP_KERNEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENOMEM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devm_kzalloc&lt;/code&gt; is more or less the kernel equivalent of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;calloc&lt;/code&gt;. It allocates memory and initializes it to zero. This function can fail and return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NULL&lt;/code&gt;, in which case the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ENOMEM&lt;/code&gt; error code must be returned to signal inability to allocate memory&lt;/p&gt;

&lt;p&gt;Now the struct can be filled with data. A pointer to the  I&lt;sup&gt;2&lt;/sup&gt;C client is kept for later use:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;i2c_set_clientdata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2c_set_clientdata&lt;/code&gt; stores a pointer in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;touchkey&lt;/code&gt;, which can later be retrieved using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2c_get_clientdata&lt;/code&gt;. This is useful in functions where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;touchkey&lt;/code&gt; cannot be passed as an argument, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2c_client&lt;/code&gt; is passed.&lt;/p&gt;

&lt;p&gt;With all of this out of the way, we can start to focus on initializing the hardware.&lt;/p&gt;

&lt;p&gt;Unfortunately, datasheets are nowhere to be found, so the only sources of information about this touchkey controller are the downstream device tree and driver written by Xiaomi.&lt;/p&gt;

&lt;h3 id=&quot;analyzing-the-downstream-driver&quot;&gt;Analyzing the downstream driver&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/drivers/input/touchscreen/cyttsp_button.c#L1336-L1548&quot;&gt;probe function of the downstream &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp-button&lt;/code&gt; driver&lt;/a&gt; should contain everything we need to know about initializing the touchkey controller.&lt;/p&gt;

&lt;p&gt;It starts off by initializing a data struct, similar to our driver:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;of_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devm_kzalloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cyttsp_button_platform_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GFP_KERNEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to allocate memroy for pdata&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENOMEM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, it parses information from the device tree:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;		&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cyttsp_button_parse_dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;parsing-the-device-tree-node&quot;&gt;Parsing the device tree node&lt;/h4&gt;
&lt;p&gt;Looking at &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/drivers/input/touchscreen/cyttsp_button.c#L461-L582&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp_button_parse_dt&lt;/code&gt;&lt;/a&gt;, the main device tree properties are found.
It starts off by reading the Interrupt Request (IRQ) GPIO pin:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irq_gpio&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of_get_named_gpio_flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cyttsp,irq-gpio&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irq_gpio_flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is the matching property in the device tree:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;cyttsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irq-gpio&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tlmm&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;78&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x2002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When a change occurs in the state of the buttons, this pin becomes active and triggers an interrupt to handle the change.&lt;/p&gt;

&lt;p&gt;It then goes on to read other properties such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp,input-name&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp,cut-off-power&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp,soft-reset&lt;/code&gt;. These properties are not essential so they can be skipped for now.&lt;/p&gt;

&lt;p&gt;After that, it reads addresses of a few registers:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of_property_read_u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cyttsp,button-status-reg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of_property_read_u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cyttsp,standby-reg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;soft_reset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of_property_read_u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cyttsp,softreset-reg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Getting button status is done by reading the status register, so this register is important.&lt;/p&gt;

&lt;p&gt;Next, it reads a bootloader address:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of_property_read_u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cyttsp,bootloader-addr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Unable to read bootloader address&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bootloader_addr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This touchkey controller has some on-chip flash memory to store firmware, and this address is used in a firmware flashing routine. The chip comes with firmware preinstalled and there is no need to update it, so this is not necessary.&lt;/p&gt;

&lt;p&gt;Finally, It reads information about the available keys:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of_property_read_u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cyttsp,key-num&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Unable to read key num&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nbuttons&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nbuttons&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devm_kzalloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nbuttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GFP_KERNEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENOMEM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of_property_read_u32_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cyttsp,key-codes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
						&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nbuttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Unable to read key codes&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Here it gets the number of keys available and their codes. This is important, so it will be implemented in our driver.&lt;/p&gt;

&lt;p&gt;It also reads a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp,config-array-size&lt;/code&gt; property, which has the number of available configurations. It seems that muliple configurations can be used with a set of firmware files, by loading the corresponding file for the needed configuration. This device only has one configuration, so we do not have to worry about this.&lt;/p&gt;

&lt;h4 id=&quot;regulators&quot;&gt;Regulators&lt;/h4&gt;
&lt;p&gt;Once it is done parsing the device tree node, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp_button_probe&lt;/code&gt; calls &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/drivers/input/touchscreen/cyttsp_button.c#L1283-L1333&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp_initialize_regulator&lt;/code&gt;&lt;/a&gt;. These lines summarize the function:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulator_vdd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devm_regulator_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;vdd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulator_avdd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devm_regulator_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;avdd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regulator_enable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulator_vdd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regulator_enable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulator_avdd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It gets two regulators: a digital supply &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vdd&lt;/code&gt; and an analog supply &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;avdd&lt;/code&gt;. These regulators supply the digital and analog parts of the IC. It then enables them to power on the chip.&lt;/p&gt;

&lt;p&gt;After that, it invokes a firmware update routine. As said before, this is not necessary for now.&lt;/p&gt;

&lt;h4 id=&quot;interrupts&quot;&gt;Interrupts&lt;/h4&gt;
&lt;p&gt;With regulators initialized, it goes on to request a GPIO pin for an interrupt:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpio_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irq_gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cyttsp_button_irq_gpio&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then sets its direction:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpio_direction_input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irq_gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A while later, it allocates an IRQ using the GPIO pin it got:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	error = request_threaded_irq(client-&amp;gt;irq, NULL, cyttsp_button_interrupt,
					pdata-&amp;gt;irqflags, client-&amp;gt;dev.driver-&amp;gt;name, data);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now, an interrupt will be triggered whenever a signal is received on the GPIO pin. This allows for handling changes in button states as they happen. A more in-depth look at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp_button_interrupt&lt;/code&gt;, which is the handler for this interrupt, will come later.&lt;/p&gt;

&lt;p&gt;This amount of information should be enough to complete the probe function of our new driver.&lt;/p&gt;

&lt;h3 id=&quot;probing-the-device&quot;&gt;Probing the device&lt;/h3&gt;
&lt;h4 id=&quot;regulators-1&quot;&gt;Regulators&lt;/h4&gt;
&lt;p&gt;Before anything else, the regulators are acquired. A new struct is added to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cypress_sf_data&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cypress_sf_data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regulator_bulk_data&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regulators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This struct will hold names of the required supplies. Back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cypress_sf_probe&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;supply&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;vdd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;supply&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;avdd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now they can be acquired using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devm_regulator_bulk_get&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;devm_regulator_bulk_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;ARRAY_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/msm8996-staging/drivers/regulator/devres.c#L130-167&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devm_regulator_bulk_get&lt;/code&gt;&lt;/a&gt; is a device resource managed (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devm&lt;/code&gt;) wrapper of &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/msm8996-staging/drivers/regulator/core.c#L4714-4762&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;regulator_bulk_get&lt;/code&gt;&lt;/a&gt; that automatically frees resources when they are not needed, such as when this driver instance is destroyed, or when it fails to acquire all resources and returns an error code. That makes it unnecessary to manually free them in a remove function. It takes a pointer to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct device&lt;/code&gt; of this device, number of regulators, and a pointer to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct regulator_bulk_data&lt;/code&gt; filled with names earlier. It may fail and return an error code, so that has to be handled correctly:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devm_regulator_bulk_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;ARRAY_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to get regulators: %d&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If an error occurs, this will print an error message in the kernel log, then return the error code.&lt;/p&gt;

&lt;h4 id=&quot;key-codes&quot;&gt;Key codes&lt;/h4&gt;
&lt;p&gt;Next, the key codes are read from the device tree. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct cypress_sf_data&lt;/code&gt; is further expanded with two new elements:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct cypress_sf_data {
	...
	u32 *keycodes;
	int num_keys;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then those elements are set:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_property_read_u32_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
						&lt;span class=&quot;s&quot;&gt;&quot;linux,keycodes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;device_property_read_u32_array&lt;/code&gt; returns the number of elements in an array property when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NULL&lt;/code&gt; is passed 
A vendor-specific property name like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp,key-codes&lt;/code&gt; should not be used here; since a &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/msm8996-staging/Documentation/devicetree/bindings/input/input.yaml#L17-24&quot;&gt;common &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux,keycodes&lt;/code&gt; property&lt;/a&gt; exists. Common property names are preferred over vendor-specific names (see &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/msm8996-staging/Documentation/devicetree/bindings/writing-bindings.rst&quot;&gt;“DOs and DON’Ts for designing and writing Devicetree bindings”&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Also, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp,key-num&lt;/code&gt; property is not necessary; passing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NULL&lt;/code&gt; instead of an array makes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;device_property_read_u32_array&lt;/code&gt; return the number of elements in the device tree array.&lt;/p&gt;

&lt;p&gt;It can also return an error code, such as when it fails to find the property in the device tree node. Instead of just stopping at it though, it is possible to add a default to fall back to:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;cm&quot;&gt;/* Default key count */&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the key code count known, memory is allocated for an array to hold the key codes:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keycodes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devm_kzalloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GFP_KERNEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keycodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENOMEM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The codes are then read from the device tree through a second call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;device_property_read_u32_array&lt;/code&gt;, passing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;touchkey-&amp;gt;keycodes&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NULL&lt;/code&gt; this time:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device_property_read_u32_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;linux,keycodes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
						&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keycodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
						&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Again, this can fail, in which case a warning message is printed in the kernel log and default key codes are used.&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_warn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;s&quot;&gt;&quot;Failed to read keycodes: %d, using defaults&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

		&lt;span class=&quot;cm&quot;&gt;/* Default keycodes */&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keycodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_BACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keycodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_MENU&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;powering-up-the-ic&quot;&gt;Powering up the IC&lt;/h4&gt;
&lt;p&gt;Now would be a good time to power up the chip. That is done by calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;regulator_bulk_enable&lt;/code&gt; which enables all regulators in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct regulator_bulk_data&lt;/code&gt; passed to it (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vdd&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;avdd&lt;/code&gt; in this case).&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regulator_bulk_enable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ARRAY_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regulators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to enable regulators: %d&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;the-input-subsystem&quot;&gt;The input subsystem&lt;/h4&gt;
&lt;p&gt;With most of the initializion done, it is time to register an input device. The &lt;a href=&quot;https://www.kernel.org/doc/html/latest/input/input-programming.html&quot;&gt;&lt;em&gt;Creating an input device driver&lt;/em&gt;&lt;/a&gt; describes the process in detail, as well as other input-related functions which will be used later. For now, the process will be as follows:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Allocate an input device structure (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct input_dev&lt;/code&gt;);&lt;/li&gt;
  &lt;li&gt;Add some general information about the device to the structure;&lt;/li&gt;
  &lt;li&gt;Set device capabilites;&lt;/li&gt;
  &lt;li&gt;Register the input device.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct *input_dev&lt;/code&gt; was added to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct cypress_sf_data&lt;/code&gt; at the beginning. A call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devm_input_allocate_device&lt;/code&gt; would allocate an input device and return a pointer to it, which will be stored in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct *input_dev&lt;/code&gt; added to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct cypress_sf_data&lt;/code&gt; earlier:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devm_input_allocate_device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to allocate input device&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENOMEM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then, the name and bus type of the device are set. The device name will be used in other places, so it is better to define it:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define CYPRESS_SF_DEV_NAME &quot;cypress-sf&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CYPRESS_SF_DEV_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bustype&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BUS_I2C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The third step is to set the capabilities of the input device. This is a touchkey controller, so it has a set of keys, each with its own key code:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;input_set_capability&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EV_KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keycodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This iterates through the key codes acquired from the device tree earlier, and adds a key capability with a key code to the input device.&lt;/p&gt;

&lt;p&gt;Finally, the input device is registered:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_register_device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;s&quot;&gt;&quot;Failed to register input device: %d&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;At this point, the driver is ready to signal input events to the input subsystem, but it still has no way of detecting button state changes. This is where the status interrupt comes into play.&lt;/p&gt;

&lt;p&gt;An interrupt line is now allocated for the status interrupt:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devm_request_threaded_irq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cypress_sf_irq_handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;IRQF_ONESHOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;CYPRESS_SF_DEV_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;s&quot;&gt;&quot;Failed to register threaded irq: %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devm_request_threaded_irq&lt;/code&gt; summarizes what the downstream driver does to get the interrupt line in one function. It reads the interrupt line from the device tree &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interrupts&lt;/code&gt; property, and does everything needed to allocate an IRQ. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cypress_sf_irq_handler&lt;/code&gt; is the handler function for this interrupt, which will be called every time an IRQ is received.&lt;/p&gt;

&lt;p&gt;This sums up the probe function. Now the driver is ready to receive button state changes then signal input events correspondingly, the thing it does in the status interrupt handler.&lt;/p&gt;

&lt;h3 id=&quot;handling-the-status-interrupt&quot;&gt;Handling the status interrupt&lt;/h3&gt;
&lt;p&gt;The touchkey controller sends an IRQ when any of the keys is pressed or released. This allows the driver to check the button states only when they change. The downstream driver has &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/drivers/input/touchscreen/cyttsp_button.c#L761-L810&quot;&gt;this interrupt handler&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irqreturn_t&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cyttsp_button_interrupt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cyttsp_button_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dev_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cyttsp_button_platform_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;curr_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sync&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cyttsp_read_reg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CYTTSP_REG_TOUCHMODE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to read touch mode reg&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IRQ_NONE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;mutex_lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;glove_mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CYTTSP_GLOVE_MODE_SHIFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;mutex_unlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cyttsp_read_reg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button_status_reg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to read status!&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IRQ_NONE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nbuttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;curr_state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test_bit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keystatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;new_state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test_bit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;curr_state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;cyttsp_report_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
						&lt;span class=&quot;o&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;sync&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keystatus&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;input_sync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IRQ_HANDLED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Before doing anything, it checks an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enable&lt;/code&gt; element in its data struct. Our driver does not have one, so that can be skipped.&lt;/p&gt;

&lt;p&gt;Then it checks if the touch mode changed:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;		&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cyttsp_read_reg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CYTTSP_REG_TOUCHMODE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to read touch mode reg&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IRQ_NONE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;mutex_lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;glove_mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CYTTSP_GLOVE_MODE_SHIFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;mutex_unlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This touchkey controller has a normal mode and a glove mode, possibly for better touch detection with gloved fingertips. Modes are not implemented in our driver as they are not essential, so this part can be skipped too.&lt;/p&gt;

&lt;p&gt;Now the important part begins. It first reads the status register, which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x4a&lt;/code&gt; according to the &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/a4-msm8996-mtp.dtsi#L302&quot;&gt;downstream device tree&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;		&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cyttsp_read_reg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button_status_reg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then it casts the value it reads to an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsigned long&lt;/code&gt; variable named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keystates&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The status register stores button states in a bit field, where each bit corresponds to a button. 1 is a pressed button, and 0 is the opposite.&lt;/p&gt;

&lt;p&gt;Now it iterates through registered keys (read earlier from the device tree), and gets the old and new states of each. After that, it is just a simple XOR operation to determine which ones changed.&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;		&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nbuttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;curr_state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test_bit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keystatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;new_state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test_bit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;curr_state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;cyttsp_report_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
						&lt;span class=&quot;o&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;sync&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then for each key that had a state change, it calls &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/drivers/input/touchscreen/cyttsp_button.c#L748-L759&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cyttsp_report_key&lt;/code&gt;&lt;/a&gt;, and sets &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sync = true&lt;/code&gt; for a reason seen later.&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cyttsp_report_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cyttsp_button_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_MENU&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;input_report_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enable_reversed_keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_MENU&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_BACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_BACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;input_report_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enable_reversed_keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_BACK&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_MENU&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;input_report_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This function is just a wrapper for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input_report_key&lt;/code&gt;, with the sole purpose of allowing the back and menu buttons to be swapped. There should be better ways to implement this – possibly in userspace – so it will not be implemented it in our driver.&lt;/p&gt;

&lt;p&gt;Finally, it stores the new states to use as old states in the next status interrupt,&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;		&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keystatus&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;then calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input_sync&lt;/code&gt;, which makes the input subsystem send input events according to the previous calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input_report_key&lt;/code&gt;. It uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sync&lt;/code&gt; variable from earlier to check if the status had changed:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;		if (sync)
			input_sync(data-&amp;gt;input_dev);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;writing-an-interrupt-handler&quot;&gt;Writing an interrupt handler&lt;/h3&gt;
&lt;p&gt;Using the previous findings, a new interrupt handler is written.&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irqreturn_t&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cypress_sf_irq_handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;devid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IRQ_HANDLED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Interrupt handlers have an &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/blob/msm8996-staging/include/linux/irqreturn.h#L5-17&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;irqreturn_t&lt;/code&gt;&lt;/a&gt; return type, which is an enum with a few entries:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/**
 * enum irqreturn
 * @IRQ_NONE		interrupt was not from this device or was not handled
 * @IRQ_HANDLED		interrupt was handled by this device
 * @IRQ_WAKE_THREAD	handler requests to wake the handler thread
 */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irqreturn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;IRQ_NONE&lt;/span&gt;		&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;IRQ_HANDLED&lt;/span&gt;		&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;IRQ_WAKE_THREAD&lt;/span&gt;		&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irqreturn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irqreturn_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;and as arguments, they take &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int irq&lt;/code&gt;, the interrupt line that signaled the IRQ, and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void *dev_id&lt;/code&gt;, which can be used to pass data to the interrupt handler. These arguments correspond to what was passed when allocating the interrupt, which were &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client-&amp;gt;irq&lt;/code&gt; and the data struct &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;touchkey&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As such, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void *devid&lt;/code&gt; can be casted to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct cypress_sf_data *&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cypress_sf_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;devid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;reading-the-status-register&quot;&gt;Reading the status register&lt;/h4&gt;
&lt;p&gt;The I&lt;sup&gt;2&lt;/sup&gt;C subsystem provides a function to read a byte from a register: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2c_smbus_read_byte_data&lt;/code&gt;. It takes the I&lt;sup&gt;2&lt;/sup&gt;C client and the register address as arguments, and returns the read value, or a negative error code when it fails.&lt;/p&gt;

&lt;p&gt;The register address will be defined in the driver instead of the device tree this time:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define CYPRESS_SF_REG_BUTTON_STATUS	0x4a
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i2c_smbus_read_byte_data&lt;/code&gt; can be used to read the status register, and store the value in an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int val&lt;/code&gt; variable:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i2c_smbus_read_byte_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;CYPRESS_SF_REG_BUTTON_STATUS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As usual, errors must be handled properly. Since this is an interrupt handler, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IRQ_NONE&lt;/code&gt; must be returned after an error to signal the inability to handle the interrupt:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to read button status: %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IRQ_NONE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;val&lt;/code&gt; can then be casted to an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsigned long keystates&lt;/code&gt; for later operations:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;(I am not quite sure if this is necessary, but it is what I did in the original driver, so I will proceed with it)&lt;/em&gt;&lt;/p&gt;

&lt;h4 id=&quot;finding-status-changes&quot;&gt;Finding status changes&lt;/h4&gt;
&lt;p&gt;Similar to the downstream driver, an XOR operation is used to find which button states changed. For this, keeping track of the status history is needed, so a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keystates&lt;/code&gt; element is added to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct cypress_sf_data&lt;/code&gt; to hold the current key states (which become the old key states when the status interrupt handler is called):&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cypress_sf_data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then, an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsigned long changed&lt;/code&gt; is defined to hold the XOR result:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Instead of using the XOR operator (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^&lt;/code&gt;), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bitmap_xor&lt;/code&gt; can be used:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;bitmap_xor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		   &lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This will perform an XOR operation between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keystates&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;touchkey-&amp;gt;keystates&lt;/code&gt;, or in other words, the new and old states. The result will then be stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;changed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for_each_set_bit&lt;/code&gt; to iterate through each bit (or key state), the new state is reported using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input_report_key&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;for_each_set_bit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;new_state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dev_dbg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;s&quot;&gt;&quot;Key %d changed to %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;input_report_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keycodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;new_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For debugging purposes, a debug message is added as well.&lt;/p&gt;

&lt;p&gt;Finally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input_sync&lt;/code&gt; is called to synchronize the states with the input subsystem and let it send input events:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;input_sync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then the new key states are stored in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keystates&lt;/code&gt; element of the data struct to be used in the next status interrupt:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;touchkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keystates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With that, the driver becomes mostly complete.&lt;/p&gt;

&lt;h3 id=&quot;final-touches&quot;&gt;Final touches&lt;/h3&gt;
&lt;p&gt;Adding a kconfig entry and a line to the Makefile is needed to build it. That and other additions such as power managment callbacks and a device tree schema are included in &lt;a href=&quot;https://lore.kernel.org/linux-input/20210907174341.422013-1-y.oudjana@protonmail.com/T/#m2d49731f83e83026a52a914cce01fbf81d2f928b&quot;&gt;the final patchset&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;enabling-the-touchkey-controller&quot;&gt;Enabling the touchkey controller&lt;/h2&gt;
&lt;p&gt;With a driver written, all it would take to make the navigation buttons work is to add a device tree node.&lt;/p&gt;

&lt;p&gt;The touchkey controller is connected to the 6&lt;sup&gt;th&lt;/sup&gt; I&lt;sup&gt;2&lt;/sup&gt;C bus of BLSP 2 on the MSM8996 SoC, so a node is added there:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blsp2_i2c6&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;touchkeys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;cypress-sf@&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;compatible&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cypress,sf3155&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;reg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compatible&lt;/code&gt; is the same string added to the match table in the driver, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reg&lt;/code&gt;, which specifies the I&lt;sup&gt;2&lt;/sup&gt;C address, is taken from the downstream device tree.&lt;/p&gt;

&lt;p&gt;Next, the status interrupt is added:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;		&lt;span class=&quot;n&quot;&gt;interrupt-parent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tlmm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;interrupts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;77&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IRQ_TYPE_EDGE_FALLING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interrupt-parent&lt;/code&gt; specifies the interrupt controller that provides this interrupt, which in this case is TLMM, the GPIO controller.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interrupts&lt;/code&gt; specify the interrupt lines used, and their types. Here, 77 resembles GPIO pin 77, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IRQ_TYPE_EDGE_FALLING&lt;/code&gt; makes it so that the interrupt is triggered in the falling edge of the signal, or in other words, during the transition from high to low.&lt;/p&gt;

&lt;p&gt;Next, the digital and analog power supplies are added:&lt;/p&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;		&lt;span class=&quot;n&quot;&gt;avdd-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vreg_l6a_1p8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;vdd-supply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vdd_3v2_tp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;avdd&lt;/code&gt; is powered by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;l6&lt;/code&gt;, which is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Low-dropout_regulator&quot;&gt;Low-dropout regulator&lt;/a&gt;, or LDO for short, built into the Power Managment IC (PMIC). Meanwhile, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vdd&lt;/code&gt; is powered by a separate LDO.&lt;/p&gt;

&lt;p&gt;Finally, the key codes are specified using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux,keycodes&lt;/code&gt; property:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;		linux,keycodes = &amp;lt;KEY_BACK KEY_MENU&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux/-/commit/dc7dde00b16d514ca22c931eec7db3f4c29de0e7&quot;&gt;This commit&lt;/a&gt; adds the device tree node, in addition to pin states used for suspend/resume&lt;/p&gt;

&lt;p&gt;And just like that, we get working navigation buttons on Linux.&lt;/p&gt;

&lt;p&gt;Unfortunately those buttons are not utilized much in userspace yet. The only things I found that react to pressing them are Firefox, which uses the back key to – you guessed it – go back, and GNOME Control Center, where pressing the back key causes some strange behavior, making it randomly jump to the power or network section possibly due to a bug. This, however, is a topic for another post.&lt;/p&gt;
</content>
		<link
			href="https://emainline.gitlab.io/2021/10/27/cypress-sf.html"
			rel="alternate" type="text/html" title="Enabling hardware navigation buttons on Linux"
		/>
		<id isPermaLink="true">https://emainline.gitlab.io/2021/10/27/cypress-sf.html</id>
		
		
	</entry>
	
	<entry>
		
		
		<title>Unlocking the Qualcomm Snapdragon Sensor Core (Part 1)</title>
		<description>Preparing the SLPI remote processor on the Snapdragon 820 SoC to be used on mainline Linux</description>
		<author>Yassine Oudjana</author>
		<published>Wed, 06 Oct 2021 00:00:00 +0000</published>
		<content type="html">&lt;p&gt;&lt;img class=&quot;post_image&quot; src=&quot;https://emainline.gitlab.io/assets/images/cover/ssc_p1.jpg&quot; width=&quot;100%&quot; /&gt;
Sensors have generally been some of the easier components to support on the mainline Linux kernel; considering that on most devices, all it takes to enable a sensor is to enable the I&lt;sup&gt;2&lt;/sup&gt;C or SPI interface it is connected to, describe it in the device tree, and in the worst case scenario, write a driver for it with the help of detailed, publicly available datasheets.&lt;/p&gt;

&lt;p&gt;This, however, became longer the case in devices using Qualcomm SoCs. With the release of the Snapdragon 820, the Snapdragon Sensor Core (SSC) – sometimes also referred to as the Sensor Low Power Island (SLPI) – was introduced. It is a sensor hub consisting of GPIOs that provide I&lt;sup&gt;2&lt;/sup&gt;C, SPI and serial interfaces to receive data from sensors, and a Hexagon core to process the data. It was introduced as a power-saving measure which reduces power consumption by offloading the processing of sensor data to a low-power DSP, allowing the more power-hungry CPUs to work less and even be put to deeper sleep states when idle.&lt;/p&gt;

&lt;p&gt;Unfortunately, this means that handling sensors is entirely done in proprietary firmware which communicates with a proprietary HAL driver on Android. With no source code nor documentation, we are left with nothing to work with on Linux. What makes matters worse is that there is no way, at least on consumer devices, to bypass SSC entirely and directly access the GPIOs to which the sensors are connected. If this was possible, it would have at least allowed for driving them similar to older devices, albeit with a degradation in idle battery life.&lt;/p&gt;

&lt;p&gt;This series will go through the process of reverse engineering the proprietary driver for SSC on Snapdragon 820, and in the end, hopefully writing an open source driver to make use of SSC on Linux.&lt;/p&gt;

&lt;p&gt;I will be testing on the &lt;a href=&quot;https://emainline.gitlab.io/about.html#xiaomi-mi-note-2&quot;&gt;Xiaomi Mi Note 2&lt;/a&gt; in this series.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The model name of Snapdragon 820 is MSM8996, and as so it will be referred to as that throughout this series.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;first-steps&quot;&gt;First steps&lt;/h2&gt;
&lt;p&gt;Before doing any actual reversing, it must be made sure that SLPI can be booted on Linux.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qcom_q6v5_pas&lt;/code&gt; kernel driver handles loading firmware into and booting QDSP6 – or Hexagon – remote processors such as SLPI and ADSP. The firmware is first loaded into a predefined region of memory and authenticated in the secure world, then the remote processor gets reset and starts executing it. These memory regions should be reserved so that Linux does not map them, and leaves them to be exclusively used by the remote processor and the driver that loads its firmware. These are the reserved memory regions originally defined in the MSM8996 device tree:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;reserved-memory {
	#address-cells = &amp;lt;2&amp;gt;;
	#size-cells = &amp;lt;2&amp;gt;;
	ranges;

	mba_region: mba@91500000 {
		reg = &amp;lt;0x0 0x91500000 0x0 0x200000&amp;gt;;
		no-map;
	};

	slpi_region: slpi@90b00000 {
		reg = &amp;lt;0x0 0x90b00000 0x0 0xa00000&amp;gt;;
		no-map;
	};

	venus_region: venus@90400000 {
		reg = &amp;lt;0x0 0x90400000 0x0 0x700000&amp;gt;;
		no-map;
	};

	adsp_region: adsp@8ea00000 {
		reg = &amp;lt;0x0 0x8ea00000 0x0 0x1a00000&amp;gt;;
		no-map;
	};

	mpss_region: mpss@88800000 {
		reg = &amp;lt;0x0 0x88800000 0x0 0x6200000&amp;gt;;
		no-map;
	};

	smem_mem: smem-mem@86000000 {
		reg = &amp;lt;0x0 0x86000000 0x0 0x200000&amp;gt;;
		no-map;
	};

	memory@85800000 {
		reg = &amp;lt;0x0 0x85800000 0x0 0x800000&amp;gt;;
		no-map;
	};

	memory@86200000 {
		reg = &amp;lt;0x0 0x86200000 0x0 0x2600000&amp;gt;;
		no-map;
	};

	rmtfs@86700000 {
		compatible = &quot;qcom,rmtfs-mem&quot;;

		size = &amp;lt;0x0 0x200000&amp;gt;;
		alloc-ranges = &amp;lt;0x0 0xa0000000 0x0 0x2000000&amp;gt;;
		no-map;

		qcom,client-id = &amp;lt;1&amp;gt;;
		qcom,vmid = &amp;lt;15&amp;gt;;
	};

	zap_shader_region: gpu@8f200000 {
		compatible = &quot;shared-dma-pool&quot;;
		reg = &amp;lt;0x0 0x90b00000 0x0 0xa00000&amp;gt;;
		no-map;
	};
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Taking a closer look at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slpi_region&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zap_shader_region&lt;/code&gt;, it seems that somehow they ended
up having the same address:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_region: slpi@90b00000 {
	reg = &amp;lt;0x0 0x90b00000 0x0 0xa00000&amp;gt;;
	no-map;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;zap_shader_region: gpu@8f200000 {
	compatible = &quot;shared-dma-pool&quot;;
	reg = &amp;lt;0x0 0x90b00000 0x0 0xa00000&amp;gt;;
	no-map;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This will cause problems, as both the GPU and SLPI drivers will try to load firmware into the same region. Fixing this is required before doing anything about SLPI.&lt;/p&gt;

&lt;p&gt;Looking at the downstream kernel, there are no specific memory regions for each remote processor, but rather &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-17.1/arch/arm/boot/dts/qcom/msm8996.dtsi#L189-L193&quot;&gt;one big shared region&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;peripheral_mem: peripheral_region@8ea00000 {
	compatible = &quot;removed-dma-pool&quot;;
	no-map;
	reg = &amp;lt;0 0x8ea00000 0 0x2d00000&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Fortunately though, the downstream drivers report the memory ranges they load firmware into in the kernel log:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# dmesg | grep &quot;loading from&quot;
[   10.272396] subsys-pil-tz 1c00000.qcom,ssc: slpi: loading from 0x000000008fe00000 to 0x0000000090800000
[   10.273749] subsys-pil-tz 9300000.qcom,lpass: adsp: loading from 0x0000000090800000 to 0x0000000092300000
[   11.229705] pil-q6v5-mss 2080000.qcom,mss: modem: loading from 0x0000000089c00000 to 0x000000008fe00000
[   11.751973] subsys-pil-tz soc:qcom,kgsl-hyp: a530_zap: loading from 0x0000000092300000 to 0x0000000092302000
[   26.233731] subsys-pil-tz ce0000.qcom,venus: venus: loading from 0x0000000092400000 to 0x0000000092900000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Comparing those ranges to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;peripheral_mem&lt;/code&gt; above, it is found that they are actually shifted forward by 0x1400000 on this device, and this is further confirmed by taking a quick look at &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-17.1/arch/arm/boot/dts/qcom/msm8996-xiaomi-common.dtsi#L1815-L1820&quot;&gt;the device DTS&lt;/a&gt;, where the original region defined in the SoC DTS is overwritten:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/delete-node/ peripheral_region@8ea00000;
peripheral_mem: peripheral_region@8fe00000 {
	compatible = &quot;removed-dma-pool&quot;;
	no-map;
	reg = &amp;lt;0x0 0x8fe00000 0x0 0x2b00000&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Putting all of this information together gives the final regions:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Region&lt;/th&gt;
      &lt;th&gt;Address&lt;/th&gt;
      &lt;th&gt;Size&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;MPSS&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x88800000&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x6200000&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ADSP&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x8ea00000&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x1b00000&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SLPI&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x90500000&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xa00000&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GPU (Zap Shader)&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x90f00000&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x100000&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Venus&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x91000000&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x500000&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MBA&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x91500000&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x200000&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;a href=&quot;https://lore.kernel.org/linux-arm-msm/20210926190555.278589-2-y.oudjana@protonmail.com/&quot;&gt;This patch&lt;/a&gt; takes those regions and defines them in the SoC DTS, and updates the shifted ranges in the device DTS to match.&lt;/p&gt;

&lt;p&gt;With reserved memory taken care of, now we can start working on booting SLPI.&lt;/p&gt;

&lt;h2 id=&quot;adding-slpi-to-the-device-tree&quot;&gt;Adding SLPI to the device tree&lt;/h2&gt;

&lt;p&gt;Following the &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux-msm8996/-/blob/msm8996-staging/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml&quot;&gt;device tree schema for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qcom_q6v5_pas&lt;/code&gt;&lt;/a&gt;, we can start writing a node for SLPI, with &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-17.1/arch/arm/boot/dts/qcom/msm8996.dtsi#L2355&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reg&lt;/code&gt; taken from the downstream device tree&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_pil: remoteproc@1c00000 {
	compatible = &quot;qcom,msm8996-slpi-pil&quot;;
	reg = &amp;lt;0x01c00000 0x4000&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;interrupts&quot;&gt;Interrupts&lt;/h3&gt;

&lt;p&gt;There are five interrupts in total: &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-17.1/arch/arm/boot/dts/qcom/msm8996.dtsi#L2356&quot;&gt;One real interrupt&lt;/a&gt;  belonging to the global interrupt controller used for a watchdog signal, and &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-17.1/arch/arm/boot/dts/qcom/msm8996.dtsi#L2379-L2382&quot;&gt;four virtual SMP2P (Shared Memory Point to Point) interrupts&lt;/a&gt; used to signal various events. Details about shared memory will come later.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_pil: remoteproc@1c00000 {
...
	interrupts-extended = &amp;lt;&amp;amp;intc 0 390 IRQ_TYPE_EDGE_RISING&amp;gt;,
			&amp;lt;&amp;amp;slpi_smp2p_in 0 IRQ_TYPE_EDGE_RISING&amp;gt;,
			&amp;lt;&amp;amp;slpi_smp2p_in 1 IRQ_TYPE_EDGE_RISING&amp;gt;,
			&amp;lt;&amp;amp;slpi_smp2p_in 2 IRQ_TYPE_EDGE_RISING&amp;gt;,
			&amp;lt;&amp;amp;slpi_smp2p_in 3 IRQ_TYPE_EDGE_RISING&amp;gt;;
	interrupt-names = &quot;wdog&quot;,
			&quot;fatal&quot;,
			&quot;ready&quot;,
			&quot;handover&quot;,
			&quot;stop-ack&quot;;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;clocks&quot;&gt;Clocks&lt;/h3&gt;

&lt;p&gt;There are &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-17.1/arch/arm/boot/dts/qcom/msm8996.dtsi#L2364-L2366&quot;&gt;two clocks&lt;/a&gt; driving SLPI: One is the on-board oscillator (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xo_board&lt;/code&gt;), and the other is the bus clock of the NoC (Network-on-Chip) SLPI is connected to (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aggre2&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_pil: remoteproc@1c00000 {
...
	clocks = &amp;lt;&amp;amp;xo_board&amp;gt;,
		&amp;lt;&amp;amp;rpmcc RPM_SMD_AGGR2_NOC_CLK&amp;gt;;
	clock-names = &quot;xo&quot;, &quot;aggre2&quot;;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;memory-region&quot;&gt;Memory region&lt;/h3&gt;
&lt;p&gt;A reference to the reserved memory region defined earlier is needed to load firmware into:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_pil: remoteproc@1c00000 {
	...
	memory-region = &amp;lt;&amp;amp;slpi_mem&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;shared-memory&quot;&gt;Shared memory&lt;/h3&gt;

&lt;p&gt;Qualcomm SoCs make use of a region in memory that is shared between multiple processors on the SoC, through which the CPU – usually referred to as the AP, or Application Processor – can communicate with remote processors on the chip. Each processor, or shared memory device (SMD), is called an “edge”, and has a unique identifier &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;smd-edge&lt;/code&gt;, as well as a remote identifier &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remote-pid&lt;/code&gt; used by other processors to refer to it. They also use interrupts and mailbox doorbells to signal events such as opening a SMD channel or receiving a message.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remote-pid&lt;/code&gt; has alredy been added to the &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux-msm8996/-/blob/v5.14/arch/arm64/boot/dts/qcom/msm8996.dtsi#L562&quot;&gt;mainline DTS in the SLPI SMP2P node&lt;/a&gt;, so it can be simply copied over:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_pil: remoteproc@1c00000 {
	...
	smd-edge {
		qcom,remote-pid = &amp;lt;3&amp;gt;;
	};
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Searching for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dsps&lt;/code&gt; which is the SMD label for SLPI, the remaining properties are found:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;qcom,smd-dsps {
	compatible = &quot;qcom,smd&quot;;
	qcom,smd-edge = &amp;lt;3&amp;gt;;
	qcom,smd-irq-offset = &amp;lt;0x0&amp;gt;;
	qcom,smd-irq-bitmask = &amp;lt;0x2000000&amp;gt;;
	interrupts = &amp;lt;0 176 1&amp;gt;;
	label = &quot;dsps&quot;;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;smd-edge&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;label&lt;/code&gt;, as well as a single interrupt are all clear, so they can be directly added to our SLPI node:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_pil: remoteproc@1c00000 {
	...
	smd-edge {
		interrupts = &amp;lt;GIC_SPI 176 IRQ_TYPE_EDGE_RISING&amp;gt;;

		label = &quot;dsps&quot;;
		qcom,smd-edge = &amp;lt;3&amp;gt;;
		qcom,remote-pid = &amp;lt;3&amp;gt;;
	};
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The mailbox doorbell was not obvious though; it took me a while to figure out that it was actually the position of the enabled bit in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;smd-irq-bitmask&lt;/code&gt;, which is 25 here. Adding that completes the smd-edge subnode:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_pil: remoteproc@1c00000 {
	...
	smd-edge {
		interrupts = &amp;lt;GIC_SPI 176 IRQ_TYPE_EDGE_RISING&amp;gt;;

		label = &quot;dsps&quot;;
		mboxes = &amp;lt;&amp;amp;apcs_glb 25&amp;gt;;
		qcom,smd-edge = &amp;lt;3&amp;gt;;
		qcom,remote-pid = &amp;lt;3&amp;gt;;
	};
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The last thing to add is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qcom,smem-states&lt;/code&gt;, which are according to the &lt;a href=&quot;https://gitlab.com/msm8996-mainline/linux-msm8996/-/blob/msm8996-staging/Documentation/devicetree/bindings/remoteproc/qcom%2Cadsp.yaml#L98&quot;&gt;DT schema&lt;/a&gt;, “states used by the AP to signal the Hexagon core”. In this case, there is only one state:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_pil: remoteproc@1c00000 {
	...
	qcom,smem-states = &amp;lt;&amp;amp;slpi_smp2p_out 0&amp;gt;;
	qcom,smem-state-names = &quot;stop&quot;;

	smd-edge {
		...
	};
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;final-touches&quot;&gt;Final Touches&lt;/h3&gt;

&lt;p&gt;To finish it off, the node is disabled by default to make SLPI optional since not all devices use it:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_pil: remoteproc@1c00000 {
	...
	status = &quot;disabled&quot;;
	...
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And with that done, the node is complete:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_pil: remoteproc@1c00000 {
	compatible = &quot;qcom,msm8996-slpi-pil&quot;;
	reg = &amp;lt;0x01c00000 0x4000&amp;gt;;

	interrupts-extended = &amp;lt;&amp;amp;intc 0 390 IRQ_TYPE_EDGE_RISING&amp;gt;,
			      &amp;lt;&amp;amp;slpi_smp2p_in 0 IRQ_TYPE_EDGE_RISING&amp;gt;,
			      &amp;lt;&amp;amp;slpi_smp2p_in 1 IRQ_TYPE_EDGE_RISING&amp;gt;,
			      &amp;lt;&amp;amp;slpi_smp2p_in 2 IRQ_TYPE_EDGE_RISING&amp;gt;,
			      &amp;lt;&amp;amp;slpi_smp2p_in 3 IRQ_TYPE_EDGE_RISING&amp;gt;;
	interrupt-names = &quot;wdog&quot;,
			  &quot;fatal&quot;,
			  &quot;ready&quot;,
			  &quot;handover&quot;,
			  &quot;stop-ack&quot;;

	clocks = &amp;lt;&amp;amp;xo_board&amp;gt;,
		 &amp;lt;&amp;amp;rpmcc RPM_SMD_AGGR2_NOC_CLK&amp;gt;;
	clock-names = &quot;xo&quot;, &quot;aggre2&quot;;

	memory-region = &amp;lt;&amp;amp;slpi_mem&amp;gt;;

	qcom,smem-states = &amp;lt;&amp;amp;slpi_smp2p_out 0&amp;gt;;
	qcom,smem-state-names = &quot;stop&quot;;

	status = &quot;disabled&quot;;

	smd-edge {
		interrupts = &amp;lt;GIC_SPI 176 IRQ_TYPE_EDGE_RISING&amp;gt;;

		label = &quot;dsps&quot;;
		mboxes = &amp;lt;&amp;amp;apcs_glb 25&amp;gt;;
		qcom,smd-edge = &amp;lt;3&amp;gt;;
		qcom,remote-pid = &amp;lt;3&amp;gt;;
	};
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;enabling-slpi&quot;&gt;Enabling SLPI&lt;/h3&gt;

&lt;p&gt;With the SLPI node added to the SoC DTS, what remains is to enable it in the device DTS and add a firmware path to load device-specific firmware, as well as the PX supply, which can be found in the &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-17.1/arch/arm/boot/dts/qcom/msm8996.dtsi#L2359&quot;&gt;downstream DTS&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;amp;slpi_pil {
	status = &quot;okay&quot;;

	px-supply = &amp;lt;&amp;amp;vreg_lvs2a_1p8&amp;gt;;
	firmware-name = &quot;qcom/msm8996/scorpio/slpi.mbn&quot;;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;getting-firmware&quot;&gt;Getting firmware&lt;/h2&gt;
&lt;p&gt;SLPI firmware can be found in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/vendor/firmware_mnt/image&lt;/code&gt; on this device:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# ls /vendor/firmware_mnt/image | grep slpi
slpi_a1.b00
slpi_a1.b01
slpi_a1.b02
slpi_a1.b03
slpi_a1.b04
slpi_a1.b05
slpi_a1.b06
slpi_a1.b07
slpi_a1.b08
slpi_a1.b09
slpi_a1.b10
slpi_a1.b11
slpi_a1.b12
slpi_a1.b13
slpi_a1.b14
slpi_a1.mdt
slpi_a4.b00
slpi_a4.b01
slpi_a4.b02
slpi_a4.b03
slpi_a4.b04
slpi_a4.b05
slpi_a4.b06
slpi_a4.b07
slpi_a4.b08
slpi_a4.b09
slpi_a4.b10
slpi_a4.b11
slpi_a4.b12
slpi_a4.b13
slpi_a4.b14
slpi_a4.mdt
slpi_a7.b00
slpi_a7.b01
slpi_a7.b02
slpi_a7.b03
slpi_a7.b04
slpi_a7.b05
slpi_a7.b06
slpi_a7.b07
slpi_a7.b08
slpi_a7.b09
slpi_a7.b10
slpi_a7.b11
slpi_a7.b12
slpi_a7.b13
slpi_a7.b14
slpi_a7.mdt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For some reason, Xiaomi decided to ship SLPI firmware for all of their MSM8996 devices on each one of them. The Mi Note 2 is A4 according to the &lt;a href=&quot;https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-17.1/drivers/platform/xiaomi/Kconfig#L24-L28&quot;&gt;downstream kernel&lt;/a&gt;, so the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slpi_a4.*&lt;/code&gt; files are what we are looking for.&lt;/p&gt;

&lt;p&gt;While it is possible to use the firmware in its current segmented form, combining the segments into one file is preferred. This can be done using &lt;a href=&quot;https://github.com/andersson/pil-squasher&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pil-squasher&lt;/code&gt;&lt;/a&gt;. The result is a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.mbn&lt;/code&gt; file.&lt;/p&gt;

&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;

&lt;p&gt;With all of that done, it is time to test everything added so far. If all goes well, a message confirming the successful boot of SLPI should be found in the kernel log.&lt;/p&gt;

&lt;p&gt;…But of course, things do not always go as planned.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# dmesg | grep 1c00000
[    6.952588] qcom_q6v5_pas 1c00000.remoteproc: supply cx not found, using dummy regulator
[    7.065288] remoteproc remoteproc0: 1c00000.remoteproc is available
[    7.107279] remoteproc remoteproc0: powering up 1c00000.remoteproc
[    7.189399] qcom_q6v5_pas 1c00000.remoteproc: failed to authenticate image and release reset
[    7.192502] remoteproc remoteproc0: can&apos;t start rproc 1c00000.remoteproc: -22
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;troubleshooting&quot;&gt;Troubleshooting&lt;/h3&gt;

&lt;p&gt;Upon further investigation, I found that there is a missing power domain that has to be voted on. Power domains are basically regulators or other resources used by multiple parts of the system usually having different power requirements. Consumers &lt;em&gt;attach&lt;/em&gt; to the power domain, then request it to be turned on, and in some cases, set the performance level to fulfill their needs. The power domain then is set to the highest performance level requested in order to statisfy all consumers.&lt;/p&gt;

&lt;p&gt;A &lt;a href=&quot;https://lore.kernel.org/linux-arm-msm/20210926190555.278589-3-y.oudjana@protonmail.com/&quot;&gt;patch&lt;/a&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qcom_q6v5_pas&lt;/code&gt; was needed to make it attach to the power domain that powers SLPI. Once that was done, the power domain had to be added to the DTS node:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slpi_pil: remoteproc@1c00000 {
	...
	power-domains = &amp;lt;&amp;amp;rpmpd MSM8996_VDDSSCX&amp;gt;;
	power-domain-names = &quot;ssc_cx&quot;;
	...
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now SLPI boots successfully:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# dmesg | grep 1c00000
[    6.952588] qcom_q6v5_pas 1c00000.remoteproc: supply cx not found, using dummy regulator
[    7.065288] remoteproc remoteproc0: 1c00000.remoteproc is available
[    7.107279] remoteproc remoteproc0: powering up 1c00000.remoteproc
[   15.117415] remoteproc remoteproc1: remote processor 1c00000.remoteproc is now up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;QMI services provided by SLPI can be found using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qrtr-lookup&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ qrtr-lookup
  Service Version Instance Node  Port
  ...
       15       1        8    9     1 Test service
       66       1       20    9     4 Service registry notification service
       43       2       22    9     6 Subsystem control service
       15       1        9    9     7 Test service
      771       1        1    9     9 Peripheral Access Control Manager service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://lore.kernel.org/linux-arm-msm/20210926190555.278589-1-y.oudjana@protonmail.com/&quot;&gt;The final patchset&lt;/a&gt; also includes bringing up the modem, which is done in a similar way.&lt;/p&gt;

&lt;p&gt;With SLPI booting on the mainline kernel, it is time to start communicating with it.
Part 2 will cover decoding messages dumped from the proprietary HAL driver in order to understand the way it talks to the remote processor, then attempt to do the same in the open source driver.&lt;/p&gt;
</content>
		<link
			href="https://emainline.gitlab.io/2021/10/06/Unlocking_SSC_P1.html"
			rel="alternate" type="text/html" title="Unlocking the Qualcomm Snapdragon Sensor Core (Part 1)"
		/>
		<id isPermaLink="true">https://emainline.gitlab.io/2021/10/06/Unlocking_SSC_P1.html</id>
		
		
	</entry>
	
</feed>
