code:
Touchscreen
===========

In the previous Section, we went
though the steps of transplanting the PinePhone's highly complex display
driver from the Linux kernel into a Genode driver component. Given the lessons
learned, porting over the touch screen driver should be a walk in the park,
shouldn't it? Let's see.

Information gathering
---------------------

As a pattern established by now, we first will build a minimal Linux kernel
that features only our driver of interest. Not before that works, we will
transplant the relevant code into our custom Genode component as a second
step. The challenge of the first step is finding the right knobs in the
Linux kernel configuration to make the driver and device come alive.

Analogously to the network and display drivers covered earlier, our trail head
is the device tree (Section [Identifying Linux source codes of interest])
shipped with the vendor Linux kernel. By searching for "touch" in the device
tree for the PinePhone, the following device node presents itself:

! touchscreen@5d {
!  compatible = "goodix,gt917s";
!  reg = <0x5d>;
!  interrupt-parent = <&pio>;
!  interrupts = <7 4 4>;
!  irq-gpios = <&pio 7 4 0>;
!  reset-gpios = <&pio 7 11 0>;
!  AVDD28-supply = <&reg_ldo_io0>;
!  VDDIO-supply = <&reg_ldo_io0>;
!  touchscreen-size-x = <720>;
!  touchscreen-size-y = <1440>;
! };

Given this anchor, we can use Genode's
[https://github.com/genodelabs/genode/tree/master/tool/dts - dts/extract]
tool to figure out the inter-device dependencies within the SoC. First, let's
determine the complete path of the device node within the device tree.

! genode$ ./tool/dts/extract --nodes flat_pinephone.dts | grep touch
!
! /soc/i2c@1c2ac00/touchscreen@5d

This path can now be supplied as '--select' argument to another call of the
extract tool to create pruned device tree that contains only nodes that are
related to the selected one. For further inspection, we redirect the pruned
device tree to the file _touch.dts_.

! genode$ ./tool/dts/extract --select /soc/i2c@1c2ac00/touchscreen@5d \
!                            flat_pinephone.dts > touch.dts

For us, the lines featuring a 'compatible' string are of immediate interest
because those strings draw a direct relation to Linux source codes.

! genode$ grep compatible touch.dts
!
!  compatible = "fixed-clock";
!  compatible = "fixed-clock";
!  compatible = "simple-bus";
!   compatible = "allwinner,sun50i-a64-ccu";
!   compatible = "allwinner,sun50i-a64-pinctrl";
!   compatible = "allwinner,sun6i-a31-i2c";
!   compatible = "arm,gic-400";
!   compatible = "allwinner,sun50i-a64-rtc",
!   compatible = "allwinner,sun50i-a64-r-intc",
!   compatible = "allwinner,sun50i-a64-r-ccu";
!   compatible = "allwinner,sun50i-a64-r-pinctrl";
!   compatible = "allwinner,sun8i-a23-rsb";
!  compatible = "goodix,gt917s";
!  compatible = "x-powers,axp803";
! compatible = "pine64,pinephone-1.2", "pine64,pinephone", "allwinner,sun50i-a64";

The actual touchscreen device is the "goodix,gt917s". A few other nodes
are already familiar from the work with the display driver.
All devices prefixed with "r-" belong to the so-called RTC-related part of
SoC, which can be powered independently from the application processor and
are designated for always-on functionality.
The "rsb" (reduced serial bus) is the two-wire bus that interconnects the SoC
with the power-management IC "x-powers,axp803".
The "i2c" and "pinctrl" relation becomes apparent when looking at the
schematics of the PinePhone or the datasheet of the Goodix touchscreen
controller.

[image pinephone_touch_schematics]

"CTP" presumably stands for capacitive touch panel. It is powered
by same "GPIO-LDO" voltage that we encountered for the display driver before.
As this signal comes from the AXP803 power-management IC and is disabled
by default, this explains the need for talking over the RSB bus with the PMIC.
The actual touch data travels via the signals "TWIO-SDA" and "TWIO-SCK", which
are the data and clock lines of an I2C connection. The separate "CTP-INT" signal
allows the touch panel to notify the application processor whenever something
interesting happens - that is - when it would be worthwhile to request new
data via I2C. Finally, the "CTP-RST" signal is a reset line driven by the
application processor.

So long for getting an overview of how the touch panel is integrated. Now,
let's figure out the Linux source code of interest. The procedure follows
the same pattern as employed for the display and network drivers.

First, we search the Linux source tree for the 'compatible' strings as present
in the device nodes.

! linux$ grep -r "goodix,gt917s"
!
! drivers/input/touchscreen/goodix.c: { .compatible = "goodix,gt917s" },

Having found an interesting location, look sideways, in particular at
_drivers/input/touchscreen/Makefile_ in this case. The following line draws
the connection to a kernel configuration option.

! obj-$(CONFIG_TOUCHSCREEN_GOODIX)	+= goodix.o

This gives us a new clue what to 'grep' for.

! src/linux$ grep -r TOUCHSCREEN_GOODIX
!
! drivers/input/touchscreen/Makefile:obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
! drivers/input/touchscreen/Kconfig:config TOUCHSCREEN_GOODIX

The 'Kconfig' file mentioning the kernel option gives us a few more hints.

! config TOUCHSCREEN_GOODIX
!         tristate "Goodix I2C touchscreen"
!         depends on I2C
!         depends on GPIOLIB || COMPILE_TEST
!         help
!           Say Y here if you have the Goodix touchscreen (such as one
!           installed in Onda v975w tablets) connected to your
!           system. It also supports 5-finger chip models, which can be
!           found on ARM tablets, like Wexler TAB7200 and MSI Primo73.
!
!           If unsure, say N.
!
!           To compile this driver as a module, choose M here: the
!           module will be called goodix.

Observing the driver in a minimal Linux kernel
----------------------------------------------

At this point, it is time to expand our bare-bones Linux configuration in
_a64_linux/target.inc_.

! LX_ENABLE += INPUT_TOUCHSCREEN
! LX_ENABLE += TOUCHSCREEN_GOODIX

To see if and how those options come into effect, it's best to build the
kernel with the changed configuration...

! build/arm_v8a$ make a64_linux

and then manually inspect the _a64_linux/.config_ file, in particular
searching for "TOUCHSCREEN_GOODIX" to see if all config dependencies are met.
If not, we have to study the Kconfig files to see missing options.
E.g., the "TOUCHSCREEN_GOODIX" option is evaluated only if "INPUT_TOUCHSCREEN"
is enabled.

This procedure needs to be repeated for all 'compatible' strings we
identified as interesting above.

E.g., the "allwinner,sun6i-a31-i2c" compatible string leads us to
_drivers/i2c/busses/i2c-mv64xxx.c_. The accompanied _Makefile_ speaks
of "CONFIG_I2C_MV64XXX". So we add this one to our kernel configuration.

! LX_ENABLE += I2C_MV64XXX

After a few iterations of enabling kernel options, building the kernel,
and test-driving it on the PinePhone, we are greeted by the driver:

! Goodix-TS 0-005d: ID 917S, version: 0200
! Goodix-TS 0-005d: Failed to invoke firmware loader: -22
! Goodix-TS: probe of 0-005d failed with error -22

Looking into the code that prints the message "Failed to invoke firmware
loader" reveals that a call to 'request_firmware_nowait' fails with the error
code EINVAL. This happens because the kernel falls back to the dummy function
at _include/linux/firmware.h_ unless the kernel option FW_LOADER is enabled.
I guess, you know what comes next:

! LX_ENABLE += FW_LOADER

On the next iteration, the output looks different.

! Goodix-TS 0-005d: ID 917S, version: 0200
! Goodix-TS 0-005d: Direct firmware load for goodix_917S_cfg.bin failed with error -2
! input: Goodix Capacitive TouchScreen as /devices/platform/soc/1c2ac00.i2c/i2c-0/0-005d/input/input0

In principle, we could add further kernel infrastructure to expose the driver
as input/event interface in order to access it from the Linux user land.
One useful tool is the 'evbug' kernel module, which prints each occurring input
event to the kernel log. It can be activated by enabling the
kernel-configuration option 'INPUT_EVBUG'.
Alternatively, an easy way to see the immediate driver responding to touch
input is to instrument the driver code directly. In the particular case, the
function 'goodix_process_events' is a suitable hook. Adding a 'printk' as
follows does the trick.

! static void goodix_process_events(struct goodix_ts_data *ts)
! {
!   ...
!   touch_num = goodix_ts_read_input_report(ts, point_data);
!   printk("goodix_process_events got %d touch_num events\n", touch_num);
!   ...

Upon the next try, we can see that the driver indeed receives touch events!

To crosscheck the minimal set of Linux configuration options that are required
for the touchscreen driver to work, it is useful to comment out all
'LX_ENABLE' lines in the a64_linux/target.inc file that are seemingly
unrelated to the touchscreen device and test the resulting Linux kernel.
This way, we end up reaching the following set of options.

! LX_ENABLE += MFD_AXP20X_RSB REGULATOR REGULATOR_AXP20X
! LX_ENABLE += INPUT_TOUCHSCREEN TOUCHSCREEN_GOODIX I2C I2C_MV64XXX FW_LOADER

Hosting the touchscreen driver code in a Genode component
---------------------------------------------------------

Equipped with the display driver as blue print, we can mirror the basic
structure of a DDE-Linux-based driver component from the display driver
to an appropriate location. In our case, this would be the
_src/drivers/touch/goodix/_
[https://github.com/genodelabs/genode-allwinner/tree/master/src/drivers/touch/goodix - directory]
in the
[https://github.com/genodelabs/genode-allwinner - genode-allwinner]
repository.

The development procedure follows the same lines as
described in Section [Executable testbed].
For testing the input driver in isolation without any GUI infrastructure, the
[https://github.com/genodelabs/genode/tree/master/repos/os/src/server/event_dump - event_dump]
server is a handy tool.

Bridging Genode's C++ world with the Linux world
------------------------------------------------

For bridging the gap between the Linux kernel and Genode's 'Event' session
interface, there are two pieces needed. First, the
[https://github.com/genodelabs/genode/blob/master/repos/os/include/genode_c_api/event.h - genode_c_api/event.h]
API provides a simple C API for generating events. As of now, the API
is limited to the few event type we have actual drivers for (touch).
This free-standing API depends neither on Genode nor on Linux headers.
The second piece is a custom emulation code for Linux' input subsystem
contained in
[https://github.com/genodelabs/genode-allwinner/blob/master/src/drivers/touch/goodix/input.c - input.c].
It responds to (Linux-internal) calls of the emulated input subsystem by
invoking the _genode_c_api/event.h_ API.

Caveats
-------

During the work on the driver, I learned a few unexpected lessons that are
worth sharing.

Apparently, *time-multiplexing* GPIO pins between input and output are a
thing, even outside I2C. In the concrete case of the Goodix touch panel,
I struggled matching the Linux driver code against the roles of the signals
depicted in the
[https://files.pine64.org/doc/datasheet/pinephone/GT917S-Datasheet.pdf - Goodix documentation].

[image goodix_reset]

According to this diagram, the RESET signal is driven by the host whereas
the INT signal is driven by the Goodix device, which makes perfect sense.
In the driver code, however, the INT signal is driven by the host as well!
It turns out that certain versions of the device scan the INT signal during
reset to obtain one bit of configuration information (choice between two
possible I2C addresses).

To accommodate the time multiplexed use of a pin as input or output, Genode's
pin driver (a64_pio for the PinePhone) switches an output pin to output mode
not before a pin-control client actually accesses the pin. This way, a driver
is able to toggle the direction by controlling the lifetime of its pin-control
session while sensing the pin via a separate pin-state session.

Device resources needed by the driver
-------------------------------------

As described in Section [One Platform driver to rule them all], Genode's platform
driver restricts access of drivers to devices. During the process of porting a
Linux driver as Genode component, one is repeatedly confronted with messages
like:

! Error: memory-mapped I/O resource ... unavailable

While addressing those messages by enhancing the platform driver's
configuration step by step, the following picture emerges. Note the
close correlation with the device-tree information we gathered initially.

! <device name="r_pio" type="allwinner,sun50i-a64-r-pinctrl">
!   <io_mem address="0x01f02c00" size="0x400"/>
!   <irq number="77"/>
! </device>
! <device name="r_ccu" type="allwinner,sun50i-a64-r-ccu">
!   <io_mem address="0x01f01400" size="0x100"/>
! </device>
! <device name="r_intc" type="allwinner,sun6i-a31-r-intc">
!   <io_mem address="0x01f00c00" size="0x400"/>
!   <irq number="64"/>
! </device>
! <device name="r_rsb" type="allwinner,sun8i-a23-rsb">
!   <io_mem address="0x01f03400" size="0x400"/>
!   <irq number="71"/>
! </device>
! <device name="ccu" type="allwinner,sun50i-a64-ccu">
!   <io_mem address="0x01c20000" size="0x400"/>
! </device>
! <device name="pio" type="allwinner,sun50i-a64-pinctrl">
!   <io_mem address="0x01c20800" size="0x400"/>
!   <irq number="43"/> <!-- Port B -->
!   <irq number="49"/> <!-- Port G -->
!   <irq number="53"/> <!-- Port H -->
! </device>
! <device name="i2c0" type="allwinner,sun6i-a31-i2c">
!   <io_mem address="0x01c2ac00" size="0x400"/>
!   <irq number="38"/>
! </device>

This picture is concerning because there is apparently a significant
overlap of resources accessed by the
display driver (Section [Display]) and those resources needed
by the touchscreen driver to operate. In Linux, both drivers are part of the
same program, the Linux kernel. But on Genode, we end up in the situation of
having two independent programs trying to drive the same parts of the
hardware.

[tikz img/pinephone_fb_touch_conflict]

The resolution of those conflicts is covered by the next Section.


code:
Cutting Linux-driver competencies
=================================

The previous sections covered the challenges of
transplanting complex driver code from the Linux kernel into Genode
components. Once running happily in its new habitat, however,
the driver code needs a heavy dose of domestication.
This section shows how to curb the driver code
from the overarching access of power, reset, pin, and clock controls.

At the end of the previous section, we encountered
conflicting hardware access as a hard problem when integrating multiple driver
components into one system. It naturally arises on the attempt to combine the
framebuffer driver with the touchscreen driver.

Each of both drivers assumes the responsibility of managing the clocks,
reset lines, pins, and power domains related to the driven devices.
As those low-level hardware resources are controlled via system-global
hardware-configuration registers, each driver tries to manipulate
those central registers. In the concrete scenario, we can observe the
following legitimate interplays.

* Each driver tries to enable an output at the power-management IC (PMIC)
  that happens to power both the LCD display and the touchscreen controller.
  The PMIC is accessed via a so-called reduced serial bus (RSB) two-wire bus.
  Therefore, both components concurrently try to drive the same RSB bus
  controller.

* The touchscreen driver modifies the SoC's pin configuration for the
  four pins connected to the Goodix touchscreen controller, in particular defining
  one pin as input signal for interrupt delivery, one pin as output signal for
  reset control, and selecting I2C as pin function for the two I2C wires.

  The framebuffer driver modifies the pin configuration to assign PWM as
  pin function for the brightness control, and defines two pins as outputs
  for controlling the backlight and LCD reset.

  In both cases, the pins are configured via the system-global PIO device.

* Both drivers interact with the clock and reset control unit (CCU).
  The touchscreen driver de-asserts the reset signal of the SoC's I2C
  controller and enables the corresponding bus clock, whereas the
  framebuffer driver controls the clocks and reset domains of the display
  engine, MIPI-DSI, DPHY, and the two TCON channels.

There are two principle approaches for the reconciliation of both drivers.
One could be tempted to co-locate both drivers into a single component.
But this is bad for two reasons. First, it would effectively turn this
highly complex component into the central authority over system-management
controls, literally yielding power over the whole system, including
low-complexity security-sensitive Genode components unrelated to the drivers.
Second, with each driver added, this component would grow bigger.
Down the line, we would ultimately end up with an all-powerful monolithic
driver subsystem that stands in the way of a clean separation of concerns.
E.g., in contrast to an individual framebuffer driver that can be started,
removed, and restarted on demand, a monolithic driver component that includes
drivers for persistent storage couldn't be restarted without risking data
loss.

The right way to go is the consequent removal of low-level system-control
access from the drivers. In Genode, the natural place for clock and power
management functionality is the platform driver we introduced in
Section [One Platform driver to rule them all],
whereas the pin-MUX configuration and GPIO access are covered by the
dedicated PIO driver component.
The following illustration shows the aspired architecture.

[tikz img/soc_platform_driver]

The framebuffer and touchscreen drivers no longer access the low-level
system-control registers directly. Instead, the platform driver controls
the reset, clock, and power states depending on the presence of its
clients. Analogously, the driver's direct GPIO access of the direct pin-MUX
manipulation is replaced by the use of the services provided by the PIO driver
component.

SoC-aware platform driver
~~~~~~~~~~~~~~~~~~~~~~~~~

The picture above calls for the enhancement of the platform driver with
SoC-specific driver code for controlling clocks, power, and reset lines.
Instead of laying those controls into the hands of the driver, the platform
driver implicitly drives them based on the mere presence of a related
platform client. For example, the following policy assigns the "tcon0"
device to the client labeled as the framebuffer driver.

! <policy label="fb_drv -> " info="yes">
!     ...
!     <device name="tcon0"/>
!     ...
! </policy>

The "tcon0" device is declared as follows. Note the declaration of the
'<clock>', '<power-domain>', and '<reset-domain>' sub nodes. The names of
listed resets, clocks, and power outputs are defined by the implementation of
the SoC-aware platform driver.

! <device name="tcon0" type="allwinner,sun8i-a83t-tcon-lcd">
!     <io_mem address="0x01c0c000" size="0x1000"/>
!     <irq number="118"/>
!     <reset-domain name="tcon0"/>
!     <reset-domain name="lvds"/>
!     <power-domain name="pmic-gpio0"/>
!     <clock name="bus-tcon0" driver_name="ahb"/>
!     <clock name="tcon0"     driver_name="tcon-ch0"/>
!     <clock name="dummy"     driver_name="tcon-pixel-clock"/>
! </device>

Given this information, the platform driver knows that the framebuffer driver
depends on the reset lines "tcon0" and "lvds" being de-asserted. It also knows
that the driver requires the powering of the "pmic-gpio0" output of the PMIC
chip. It also knows that the clocks "ahb", "tcon-ch0", and "tcon-pixel-clock"
must be set up. Once the framebuffer driver connects to the platform driver,
the platform driver can establish all those requirements implicitly while
establishing the connection. This not only relieves the actual driver from
those low-level peculiarities. It also enforces the proper reset of these
settings whenever a driver disappears - with no active participation of the
driver needed. For reference, the implementation of the A64-specific platform
driver can be found at the following link.

:SoC-aware platform driver for the Allwinner A64 SoC:

  [https://github.com/genodelabs/genode-allwinner/tree/master/src/drivers/platform/a64].

To simplify the implementation of the clock, reset, and power drivers within
the SoC-specific platform driver, the
[https://github.com/genodelabs/genode/tree/master/repos/os/src/drivers/platform - generic platform driver]
offers a few handy utilities in the form of the
[https://github.com/genodelabs/genode/blob/master/repos/os/src/drivers/platform/clock.h - clock.h],
[https://github.com/genodelabs/genode/blob/master/repos/os/src/drivers/platform/reset.h - reset.h], and
[https://github.com/genodelabs/genode/blob/master/repos/os/src/drivers/platform/power.h - power.h]
headers.
In practice, most system-control operations come down so toggling a single
bit for (de-)asserting a reset line, or for (un-)gating a clock.

Curbing the Linux driver code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The SoC-specific platform driver looks fairly simple. However, at the
client side - in the Linux driver component - two hairy questions
arise.

# How to remove the direct access of the low-level system-control registers
  while keeping the Linux code happy? Some the related Linux subsystems, in
  particular those related to pin-MUX configuration, are quite central to
  the healthy operation of the Linux kernel. They are mandatory and cannot be
  ripped out.

# Which clocks, resets, power domains are actually expected by the driver?
  Candidates are plenty. The answer seems rather vague and is scattered over
  many kernel modules.

Mimicking Linux subsystems
~~~~~~~~~~~~~~~~~~~~~~~~~~

We encounter the first problem when trying to remove the
_drivers/pinctrl/sunxi/*_ driver code, which interacts with the PIO device.
The Linux code briefly complains and just stops.

! mv64xxx_i2c 1c2ac00.i2c: can't get pinctrl, bus recovery not supported

To lift the clouds a bit, it helps to enable the debug messages in Linux
_drivers/base/dd.c_ and _drivers/base/core.c_ by placing the following
line at the top of those files.

! #define DEBUG

This results in a very plausible message.

! i2c 0-005d: probe deferral - supplier 1c20800.pinctrl not ready
! i2c 0-005d: Added to deferred list

The I2C subsystem depends on the pinctrl driver, which we just removed.
To satisfy this dependency without using the actual pinctrl driver, we have
to create a custom kernel module that looks like the original pinctrl but is
just an almost empty hull. To create such a stub driver, the easiest way
is to start looking at the original driver code and mirroring its basic
structure. The original driver code can be found by inspecting the device
tree, which contains the following node.

!  pio: pinctrl@1c20800 {
!   compatible = "allwinner,sun50i-a64-pinctrl";

By searching for the compatible string inside the _drivers/pinctrl/sunxi_
directory, we find the right spot.

! src/linux$ grep "allwinner,sun50i-a64-pinctrl" drivers/pinctrl/sunxi/*
! drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c:	{ .compatible = "allwinner,sun50i-a64-pinctrl", },

To mirror the driver's structure, it is good to start
looking at the "allwinner,sun50i-a64-pinctrl" string and follow its tracks.
It appears inside a table of 'of_device_id' entries.

! static const struct of_device_id a64_pinctrl_match[] = {
!   { .compatible = "allwinner,sun50i-a64-pinctrl", },
!   {}
! };

The table 'a64_pinctrl_match' is referenced by a 'struct' called
'a64_pinctrl_driver'.

! static struct platform_driver a64_pinctrl_driver = {
!   .probe  = a64_pinctrl_probe,
!   .driver = {
!     .name   = "sun50i-a64-pinctrl",
!     .of_match_table = a64_pinctrl_match,
!   },
! };
! builtin_platform_driver(a64_pinctrl_driver);

The struct refers to a probe function 'a64_pinctrl_probe'.

The implementation is merely a wrapper around 'sunxi_pinctrl_init'.

! static int a64_pinctrl_probe(struct platform_device *pdev)
! {
!   return sunxi_pinctrl_init(pdev,
!           &a64_pinctrl_data);
! }

Piece by piece, we assemble a custom puzzle of (almost) empty
functions and structures. By using the exact same symbol names as the
original driver, our stub driver nicely overtakes its position, in particular
our initcall is called at the appropriate time.

Fast forward, the complete stub driver for replacing the pinctrl driver can be
found at
[https://github.com/genodelabs/genode-allwinner/blob/master/src/lib/lx_emul/a64/pio.c - src/lib/lx_emul/a64/pio.c].
It is arguably not trivial, which is due to the fact that the touchscreen
driver uses one GPIO pin as interrupt source. Hence, our stub needs to mimick
an interrupt controller as well.
The interaction with Genode's PIO driver is done via calls to the
[https://github.com/genodelabs/genode/blob/master/repos/dde_linux/src/include/lx_emul/pin.h - lx_emul_pin_* API].

The clue for sneaking our stub driver into Linux as GPIO driver is the
function 'gpiochip_add_data', which takes a 'gpio_chip' struct as argument.
This struct defines a number callbacks that our stub driver provides for
interacting with the pins. The following two callbacks are especially
interesting.

:'of_xlate': is called with the coordinates of a GPIO pin as arguments
  and returns a pin number. The organization of the namespace for such
  pin numbers is up to us. As the PIO pins of the A64 SoC are organized in a number
  of banks with 32 pins per bank, a suitable naming scheme is
  ! number = bank*PINS_PER_BANK + pin_within_bank

:'set': sets an output pin of a given number (according the result of
  'of_xlate') to the specified level.
  This function triggers the physical effect.

Compared to the removal of the pinctrl subsystem, replacing the reset
(_drivers/reset/*_) and clock (_drivers/clk/*_) controls is relatively simple.
For stubbing the clock control, there already exists
a reusable stub driver within the _repos/dde_linux_ repository at
[https://github.com/genodelabs/genode/tree/master/repos/dde_linux/src/lib/lx_emul/shadow/drivers/clk - lx_emul/shadow/drivers/clk/].

Gathering the required clock, reset, and power controls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The second tricky question is to find out the few needles in the haystack of
clock, reset, and power controls that are required by the individual driver.
There may be more than a dozen of such prerequisites. When missing merely one,
the driver won't work.

Of course, the device tree is always a nice reference to start with.
Specifically the 'clocks' and 'reset' properties of the device tree provide
useful clues. For example, the device node for the 'tcon1' contains the
following declarations.

! tcon1: lcd-controller@1c0d000 {
!   ...
!   clocks = <&ccu 48>, <&ccu 101>;
!   clock-names = "ahb", "tcon-ch1";
!   resets = <&ccu 25>;
!   reset-names = "lcd";
!   ...

The numbers can be correlated in definitions found at
_include/dt-bindings/clock/sun50i-a64-ccu.h_ in the Linux source tree.

! ...
! #define CLK_BUS_TCON1 48
! ...

Those definitions, in turn, show up in the driver's source tree - in this
particular case _drivers/clk/sunxi-ng/ccu-sun50i-a64.c_ - which draws the
connection to the physical coordinates of the clock.

! ...
! static SUNXI_CCU_GATE(bus_tcon1_clk,  "bus-tcon1",  "ahb1",
!                       0x064, BIT(4), 0);
! ...
! static struct clk_hw_onecell_data sun50i_a64_hw_clks = {
!   .hws  = {
!     ...
!     [CLK_BUS_TCON1]   = &bus_tcon1_clk.common.hw,
!     ...

Now, a look into the CCU documentation for the 4th bit of the register 0x64
should close the circle, prompting us to add an appropriately named clock
definition Genode's platform driver.

I/O register tracing
--------------------

That said, unfortunately it is all too easy to miss one piece of the puzzle
when merely looking from _above_ (from the device tree).
In this case, a look from _below_ may help to complete the picture:
To find the right bits - and also to quickly rule out the wrong ones -
the low-level tracing of register accesses is sometimes inevitable.

Usually, Linux subsystems come with their own pieces of infrastructure, which
provide us with a convenient hook for instrumentation. For example, all
parts of the driver for the Allwinner clock and reset unit (CCU) happen to
include the file _linux/drivers/clk/sunxi-ng/ccu_common.h_. Hence, changes
of this file affect only the driver code we are interested in. So we can
add the following hillbilly I/O tracing facility that captures all 'writel'
and 'readl' operations.

! static inline void my_writel(u32 value, volatile void __iomem *addr)
! {
!   printk("::: writel 0x%x addr=0x%p\n", value, addr);
!   writel(value, addr);
! }
! #undef writel
! #define writel my_writel
!
! static inline u32 my_readl(volatile void __iomem *addr)
! {
!   u32 result = readl(addr);
!   printk("::: readl addr=0x%p -> 0x%x\n", addr, result);
!   return result;
! }
! #undef readl
! #define readl my_readl

The ":::" prefix is just a band aid to easily distinguish the trace output
from regular log output. Note that the instrumentation print virtual addresses
though.
To correlate those virtual addresses with physical addresses, we can add
an instrumentation to the 'lx_emul_io_mem_map' function in
[https://github.com/genodelabs/genode/blob/master/repos/dde_linux/src/lib/lx_emul/io_mem.cc - lx_emul/io_mem.cc].

! log("mapped memory-mapped I/O resource ", Hex(phys_addr),
!     " (size=", Hex(size), ") to ", ret);

This I/O tracing approach is extremely simple, yet surprisingly powerful.
Consider the following ideas.

* One can programmatically filter out superfluous noise by making the
  'printk' statements conditional. For example, skipping the output for
  certain uninteresting accesses (like polling a certain bit), or keeping a
  counter and starting the output not before the counter has reached a certain
  value.

* Even more interesting is the conditional skipping of write operations
  to confirm that certain register accesses are _really_ needed. One can even
  change the bits written to the hardware registers to see, e.g., the
  effect of different clock settings.

* As a sledgehammer approach, one can replay a once gathered register trace
  at the startup of Genode's platform driver and skip as many
  'writel' operations in the driver as possible.

By iteratively tweaking the filtering, thousands of register accesses during
the initialization of the touchscreen driver could be condensed to the
following few _interesting_ accesses. With the focus drawn to such a few
registers, the manual review of the bits suddenly becomes practical.

! writel 0x5514     addr=0x2008   PLL_AUDIO control register
! writel 0x515      addr=0x2040   PLL_MIPI Control Register
! writel 0x90001031 addr=0x2000   PLL_CPUX Control Register
! writel 0x90002d00 addr=0x204c   PLL_DDR1 Control Register
! writel 0x90041811 addr=0x2028   PLL_PERIPH0 Control Register
! writel 0x81000002 addr=0x215c   MBUS Clock Register
! writel 0x10001    addr=0x206c   Bus Clock Gating Register 3

That said, keep in mind that we can never be sure to capture all I/O accesses
this way. Drivers may operate in a way that bypass the 'readl' and
'writel' functions. Also, the place of the instrumentation is important.
For example, if a driver accesses registers indirectly using the
_drivers/base/regmap/_ utilities, one needs to the place the
instrumentation inside the regmap implementation.

The drivers reconciled
~~~~~~~~~~~~~~~~~~~~~~

The result of the described development step is best illustrated by the
[https://github.com/genodelabs/genode-allwinner/blob/master/recipes/raw/drivers_interactive-pinephone/drivers.config - configuration]
of the drivers subsystem for Genode's interactive system scenarios on the
PinePhone.
It clearly documents the _enforced_ relationship between the drivers and the
related hardware at an almost intuitive level of abstraction. For example, it
becomes perfectly clear, which driver has the authority over which GPIO pin.
The access to low-level system-management registers is exclusively guarded by
the platform driver. Isn't it beautiful?

[tikz img/pinephone_fb_touch_fine]

repo_name: Fhiss/genode


code:
Excursion to the user land
==========================

Equipped with the rudimentary debugging skills presented in the previous
section, it is time to conquer the remaining stumbling blocks on our way to
the user land.

To quickly recall, the starting point of our investigation was the following
error message.
! Error: Assertion failed: id < _count && _cpus[id].constructed()
! Error:   File: /.../repos/base-hw/src/core/kernel/cpu.cc:205
! Error:   Function: Kernel::Cpu& Kernel::Cpu_pool::cpu(unsigned int)

By following the call chain leading to this message in reverse,
we ultimately arrived at _base-hw/src/core/kernel/init.cc_ at line 64 right
in the middle of the function 'kernel_init':

!   pool_ready = cpu_pool().initialize();

To double check that the error indeed occurs somewhere in the 'initialize'
method, let's wrap the call with a bit of instrumentation.

!  Genode::log("call cpu_pool().initialize()");
!  pool_ready = cpu_pool().initialize();
!  Genode::log("pool_ready=", pool_ready);

The resulting output confirms our hypothesis.

! Starting kernel ...
!
! call cpu_pool().initialize()
! Error: Assertion failed: id < _count && _cpus[id].constructed()

It is always good to have the reassurance about still being on the right
track. As we suspected, 'cpu_pool().initialize()' is called but never returns.
So let's look at its implementation in _base-hw/src/core/kernel/cpu.cc_.

! bool Cpu_pool::initialize()
! {
!     unsigned id = Cpu::executing_id();
!     _cpus[id].construct(id, _global_work_list);
!     return --_initialized == 0;
! }

Each element of the '_cpus' array is a 'Constructible<Cpu>' object. The
'Constructible' pattern is used throughout Genode. It allows for the static
allocation of dynamically created objects. The 'construct' method triggers the
construction of a 'Cpu' object. We are ultimately faced with a general
question: How to instrument the construction of C++ objects?

Debugging the construction of C++ objects
-----------------------------------------

The lowest-hanging fruit is adding a message right at the beginning of the
constructor's body:

! Cpu::Cpu(unsigned const id, Inter_processor_work_list & global_work_list)
! :
!   ... plenty of initializers ...
! {
!   Genode::log(__PRETTY_FUNCTION__);
!   _arch_init();
! }

Upon the next run, we see no such message. So we can conclude that we get
stuck in the middle of the construction of one of the base classes or
aggregated members. As illustrated by the following picture, the body
of the constructor is called pretty late in the process of constructing
an object.

[tikz img/cxx_construction 50%]

Placing debug
messages gets a little bit more cumbersome now. We have to disguise such
messages as object attributes. For example, by placing the following line
right at the start of the class body, we can see whether we get stuck in the
construction of one of the base classes or - later - during the construction
of a member.

! bool _x1 = ( Genode::log(__FILE__, ":", __LINE__), true );

The effect of this instrumentation looks as follows.

[tikz img/cxx_construction_log 50%]

The trick is to wrap the 'log' call into an expression that can be used as
initialization of a dummy member. When the construction of the 'Cpu' object
reaches the point of the '_x1' member, we see the message as a side effect.
The member '_x1' is never actually used.

On the next run, we see the following:

! Starting kernel ...
!
! call Cpu_pool::initialize()
! /.../repos/base-hw/src/core/kernel/cpu.h:77
! Error: Assertion failed: id < _count && _cpus[id].constructed()

Since we see the message, we know that the problem occurs not in any of the
base classes but during the construction of a subsequent member. To find out
which one, we can spill dummy members in-between the various members, like so:

!     unsigned const _id;
!     bool _x2 = ( Genode::log(__FILE__, ":", __LINE__), true );
!     Board::Pic     _pic {};
!     bool _x3 = ( Genode::log(__FILE__, ":", __LINE__), true );
!     Timer          _timer;
!     bool _x4 = ( Genode::log(__FILE__, ":", __LINE__), true );
!     Cpu_scheduler  _scheduler;
!     bool _x5 = ( Genode::log(__FILE__, ":", __LINE__), true );
!     Idle_thread    _idle;
!     bool _x6 = ( Genode::log(__FILE__, ":", __LINE__), true );
!     Ipi            _ipi_irq;
!     bool _x7 = ( Genode::log(__FILE__, ":", __LINE__), true );

If this looks unsophisticated, it's because it is.
The next run reveals the following.

! call cpu_pool().initialize()
! bool Kernel::Cpu_pool::initialize()
! /plain/no/genode.git/repos/base-hw/src/core/kernel/cpu.h:78
! /plain/no/genode.git/repos/base-hw/src/core/kernel/cpu.h:117
! Error: Assertion failed: id < _count && _cpus[id].constructed()

From this message, we can conclude that the construction of the '_pic' member
is the problem. Does that ring a bell? In the backtrace we obtained
in Section [Option 3: Backtraces], observed the following line.

! /.../base-hw/src/core/spec/arm/virtualization/gicv2.h:22

We could have saved some time by following the output of the backtrace
utility more closely, but we would have missed our little excursion to the
C++ constructor instrumentation.

By continuing the manual instrumentation work, we end up in the 'Gicv2'
constructor, specifically in the initialization of the '_max_irq' member. The
'max_irq' function interacts with memory-mapped registers of the interrupt
controller. Recalling that we have merely provided dummy values of the register
addresses, the failure is no longer a mystery at all.

Let's revisit the corners that we cut while mirroring the i.MX8 EVK board
support:

* We kept the definitions for memory-mapped I/O regions for the IRQ
  controller's CPU_BASE and DISTR_BASE untouched, knowing
  that the values most certainly mismatch with the Allwinner SoC.

* We pruned the 'core_mmio' regions to cover only the UART. So even
  if core had the right numbers, it could not access the underlying
  hardware registers.

* We set NR_OF_CPUS to 4 but left 'Board::Cpu::wake_up_all_cpus' empty.

There are quite a few uncertainties. A good way to reduce them is to
first take the multi-core-related issues from the table. From experience,
we know that the bring-up of secondary CPU cores can be a pain. So let us safe
this topic for a later step.

By bringing up a single-processor variant of the kernel first, we will
certainly reach the state of a working kernel more quickly. Subsequent
user-level developments like driver-related work can then happen in parallel
with the fiddly work on the kernel's multi-processor support.
Disabling the kernel's multi-processor support comes down to changing the
'NR_OF_CPUS' definition from 4 to 1 in the two files
_lib/mk/spec/arm_v8/bootstrap-hw-pine_a64lts.mk_ and
_lib/mk/spec/arm_v8/core-hw-pine_a64lts.mk_.

Making the interrupt controller driver happy
--------------------------------------------

The ARM GIC interrupt controller consists of two parts. Similar to
distinction between the I/O APIC and local APIC on x86 hardware, there exists
a so-called distributor and a CPU-local interrupt controller. The distributor
is responsible for routing interrupts to CPU cores whereas the CPU-local
interrupt controller handles the interrupt delivery for an individual CPU. So
on a 4-core SoC, there are one distributor and four CPU-local interrupt
controllers. The memory-mapped registers of all CPU-local interrupt
controllers are the same whereas each CPU can access only its own local
controller.

To find out the addresses of both parts for the Allwinner SoC, there are two
convenient sources of information. First, the U-Boot boot loader that we
built in a Section [The U-Boot boot loader] comes with a huge
database of board specifications in the form of so-called _device tree_ (dts)
files inside the directory _u-boot/arch/arm/dts/_.
By grepping for "pine" we find many files referring to "sun50i". By grepping
for "gic" in all files named "sun50i", we end up at _sun50i-a64.dtsi_. In
there, the following snippet catches our attention:

!u-boot/arch/arm/dts$ vim sun50i-a64.dtsi
!
!    gic: interrupt-controller@1c81000 {
!      compatible = "arm,gic-400";
!      reg = <0x01c81000 0x1000>,
!            <0x01c82000 0x2000>,
!            <0x01c84000 0x2000>,
!            <0x01c86000 0x2000>;
!      interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
!      interrupt-controller;
!      #interrupt-cells = <3>;
!    };

By looking at the numbers, we unfortunately still don't know which register
ranges refers to the distributor and the CPU local controller. We could
consult ARM's official documentation.

Alternatively, we find the answer in the
[https://linux-sunxi.org/images/b/b4/Allwinner_A64_User_Manual_V1.1.pdf - Allwinner A64 user manual]
on page 74. It states the following:

! GIC_DIST: 0x01C80000 + 0x1000
! GIC_CPUIF:0x01C80000 + 0x2000

With this knowledge gained, we can change the definitions in our
_pine_a64lts_board.h_ file to the following.

! IRQ_CONTROLLER_DISTR_BASE = 0x01c81000,
! IRQ_CONTROLLER_DISTR_SIZE = 0x1000,
! IRQ_CONTROLLER_CPU_BASE   = 0x01c82000,
! IRQ_CONTROLLER_CPU_SIZE   = 0x2000,

Additionally, those resources must be registered as core's memory-mapped I/O
regions in _board/pine_a64lts/platform.cc_.

! Bootstrap::Platform::Board::Board()
! :
!   early_ram_regions(Memory_region { ::Board::RAM_BASE, ::Board::RAM_SIZE }),
!   late_ram_regions(Memory_region { }),
!   core_mmio(Memory_region { ::Board::UART_BASE, ::Board::UART_SIZE },
!             Memory_region { ::Board::Cpu_mmio::IRQ_CONTROLLER_DISTR_BASE,
!                             ::Board::Cpu_mmio::IRQ_CONTROLLER_DISTR_SIZE },
!             Memory_region { ::Board::Cpu_mmio::IRQ_CONTROLLER_CPU_BASE,
!                             ::Board::Cpu_mmio::IRQ_CONTROLLER_CPU_SIZE })
! {
!   ::Board::Pic pic {};
! }

When building and running the run/log system image the next time, we get
filled with joy:

!Starting kernel ...
!
!
!kernel initialized
!ROM modules:
! ROM: [000000004012c000,000000004012c156) config
! ROM: [0000000040006000,0000000040007000) core_log
! ROM: [00000000401eb000,000000004022c260) init
! ROM: [0000000040134000,00000000401eacb0) ld.lib.so
! ROM: [0000000040004000,0000000040005000) platform_info
! ROM: [000000004012d000,00000000401331e8) test-log
!
!Genode 20.11-197-g635985f542 <local changes>
!2010 MiB RAM and 64533 caps assigned to init
![init -> test-log] hex range:          [0e00,1680)
![init -> test-log] empty hex range:    [0abc0000,0abc0000) (empty!)
![init -> test-log] hex range to limit: [f8,ff]
![init -> test-log] invalid hex range:  [f8,08) (overflow!)
![init -> test-log] negative hex char:  0xfe
![init -> test-log] positive hex char:  0x02
![init -> test-log] floating point:     1.70
![init -> test-log] multiarg string:    "parent -> child.7"
![init -> test-log] String(Hex(3)):     0x3
![init -> test-log] Very long messages:
![init -> test-log -> log] 1.....................................................................................................................................................................................................................................2
![init -> test-log] 3.....................................................................................................................................................................................................................................4
![init -> test-log] 5.....................................................................................................................................................................................................................................6
![init -> test-log]
![init -> test-log] Test done.

We just witnessed the first successful excursion to the user land. The kernel
started the user-level init component, which in turn started the test-log
program as child component. The output of test program looks just perfect!
To truly appreciate what just happened, consider that the simple system
scenario already entails most of Genode's fundamental mechanisms:

* Transition between kernel and user land and vice versa
* Multiple protection domains protected by virtual memory
* Synchronous inter-component communication calls (RPC)
* Asynchronous notifications
* Shared memory between components
* The ELF loading of programs
* Handling of the system's configuration
* Multi-threading and inter-thread synchronization
* Dynamic linking

The simple log-test scenario above is just the beginning.
In the next section, we take the
board through the entire test suite of the Genode base framework.


code:
Getting acquainted with the target platform
===========================================

The undertaking of bringing Genode - and Sculpt OS in particular - to a new
ARM SoC comes with a great deal of uncertainties, namely the inner functioning
of overly complex hardware, picking appropriate tools and methodologies,
taking informed decisions about porting versus developing drivers, and
relating all this to Genode.

Combined, these uncertainties pose a huge barrier. At Genode Labs, we have
conquered this barrier a few times in the past, e.g.,
for supporting the NXP i.MX8 SoC. However, the porting of Genode to new
hardware should not be left as an activity exclusive to Genode Labs. In order
to assist developers outside of Genode's inner circle with joining the fun,
we'd like to share what we know. This sharing should have the form of profound
documentation that serves as a guide and removes points of friction as much as
possible.

To deliver substance, I figured that I should not merely talk the talk by
speaking from past experience, but also walk the walk again while writing down
my practical steps as I go. So I went forward looking around for tasty
hardware, when [https://www.pine64.org/ - Pine64] caught my eyes.

Why Pine64?
-----------

I got excited about Pine64 for several reasons.

First, devices in the form factors of the PinePhone and the A64 development
boards are readily available at affordable prices. The Pine64 website carries
a very positive message, highlighting community, openness, sustainability,
transparency, no marketing nonsense.

Second, the products are designed for hackability. This is evidenced by the
vibrant developer community, mainline Linux kernel support, and the
availability of literally more than a dozen Linux distributions. One can boot
the PinePhone directly from SD-card. How cool is that!

Third, the used Allwinner SoC - introduced as early as 2015 - is rather aged.
In contrast to bleeding-edge hardware, I would not need to explore unconquered
territory. Others have hopefully discovered most pitfalls before me. The SoC
seems to strike a nice balance of modern features (64 bit, multi core,
virtualization) with modest complexity. The performance of the SoC is notably
at the lower end of the smartphone product category. From the perspective of
an operating-systems developer, I don't see this as a con but more as a
welcome challenge. Will Genode be able to shine on such a constrained device?
Let's find out!

The only downside of the SoC worth mentioning is the lack of an IO-MMU as
protection mechanism against rampant I/O devices or drivers. So the sandboxing
of device drivers can never be water-tight.

Getting a first impression
~~~~~~~~~~~~~~~~~~~~~~~~~~

We ordered a
[https://pine64.com/product-category/pinephone/ - Pine64-LTS] board, a
[https://pine64.com/product-category/pinephone/ - PinePhone], and a
[https://pine64.com/product/pinebook-pinephone-pinetab-serial-console/ - serial cable]
for the PinePhone directly from the online store. For some kind
of safety reason, the phone had to be ordered separately. In hindsight, we
better had ordered a power supply for the Pine64-LTS board as well. We skipped
it as we already have kilograms of AC power supplies of other boards at hand.
However, it turned out that kilograms of power supplies with 5mm connectors
are of little use when the board features a less mainstream 3.5mm connector.
Such details matter sometimes.

For getting our hands dirty with technical work, we will have to leave the
PinePhone alone for a while and turn our attention to the *Pine-A64-LTS* board.
The [https://wiki.pine64.org/index.php/PINE_A64-LTS/SOPine_Main_Page - Pine64 wiki]
provides the perfect starting point.

Booting an officially supported GNU/Linux image
-----------------------------------------------

The wiki lists numerous ready-to-use Linux
[https://wiki.pine64.org/wiki/SOPINE_Software_Release - distributions].
I went for [https://www.armbian.com - Armbian]. Just a few minutes later,
after downloading the disk image from
[https://dl.armbian.com/pine64so/Buster_current], writing the image to an
SD card, connecting an HDMI display and a USB keyboard, and booting the
board with the SD card inserted, I was greeted with Armbian login, allowing
me to login as root user.

At this point, I'm most interested in getting a first overview of the
hardware. The following information are insightful:

! root@pine64so:/# cat /proc/cpuinfo
! ...
! root@pine64so:/# cat /proc/meminfo

Well, that is not too surprising. It's more like a ritual.

! root@pine64so:/# dmesg | less

The kernel boot log is quite chatty. The following lines caught my eyes.

! [    2.228675] sun4i-drm display-engine: bound 1100000.mixer...
! [    2.230477] sun4i-drm display-engine: bound 1200000.mixer...
! [    2.231001] sun4i-drm display-engine: No panel or bridge found...
! [    2.231018] sun4i-drm display-engine: bound 1c0c000.lcd-controller...
! [    2.231227] sun4i-drm display-engine: bound 1c0d000.lcd-controller...
! [    2.231293] sun8i-dw-hdmi 1ee0000.hdmi: Couldn't get regulator
! [    2.231734] sun4i-drm display-engine: Couldn't bind all pipelines...

...once we get to graphics, we have to grep the Linux kernel for "sun4i-drm"
and "sun8i-dw-hdmi". Whatever sun4i and sun8i means. Does "dw" stands for
Designware? I shudder for a moment...

! [    2.250163] 1c28000.serial: ttyS0 at MMIO 0x1c28000 (irq = 31,...
! [    2.250239] printk: console [ttyS0] enabled
! [    2.250893] sun50i-a64-pinctrl 1c20800.pinctrl: supply vcc-pg...
! [    2.251327] 1c28400.serial: ttyS1 at MMIO 0x1c28400 (irq = 32,...
! [    2.251471] serial serial0: tty port ttyS1 registered

...the Linux kernel uses the serial controller at 0x1c28000 by default. That
will be the first device we need a driver for. Never heard of a "16550A"
device though...

! [    2.277178] ehci-platform 1c1b000.usb: EHCI Host Controller
! [    2.277210] ehci-platform 1c1b000.usb: new USB bus registered,...
! [    2.277359] ehci-platform 1c1b000.usb: irq 22, io mem 0x01c1b000
! [    2.289613] ehci-platform 1c1b000.usb: USB 2.0 started, EHCI 1.00
! ...
! [    2.291208] ohci-platform 1c1b400.usb: Generic Platform OHCI controller
! [    2.291228] ohci-platform 1c1b400.usb: new USB bus registered,...
! [    2.291342] ohci-platform 1c1b400.usb: irq 23, io mem 0x01c1b400

...an OHCI USB controller, I get a little blast from the past...

! [    2.384988] sunxi-mmc 1c0f000.mmc: initialized,...
! [    2.410167] sunxi-mmc 1c10000.mmc: initialized,...
! [    2.422925] mmc0: Problem switching card into high-speed mode!
! [    2.423025] mmc0: new SDHC card at address 0001

...two multi-media card (MMC) devices, apparently driven by an
Allwinner-specific controller. "Problem switching card into high-speed mode!".
MMC and problem are almost synonymous. Allwinner will not positively surprise
us...

! [    3.412571] dwmac-sun8i 1c30000.ethernet: IRQ eth_wake_irq not found

...the good news is that there is a dedicated Ethernet controller, not merely
a USB-network device. The bad news is that the controller is an IP core
purchased from Designware. After the deep scars I got from USB on the
Raspberry Pi, I was hoping not to touch anything with "dw" in its name
again...

! [    9.189128] Call trace:
! [    9.191219]  ktime_get_update_offsets_now+0x5c/0x100
! [    9.193340]  hrtimer_interrupt+0xa0/0x2f0
! [    9.195466]  sun50i_a64_read_cntpct_el0+0x30/0x38
! [    9.197542]  arch_counter_read+0x18/0x28
! [    9.199712]  arch_timer_handler_phys+0x34/0x48
! [    9.201813]  handle_percpu_devid_irq+0x84/0x148
! [    9.203971]  ktime_get_update_offsets_now+0x5c/0x100
! [    9.206022]  hrtimer_interrupt+0xa0/0x2f0
! [    9.208071]  generic_handle_irq+0x30/0x48
! [    9.210150]  __handle_domain_irq+0x64/0xc0
! ... many more lines ...

...a Linux kernel thread died during boot. The "sun50i" symbol hints at an
Allwinner-related driver issue. The kernel marches on nevertheless...

! [    9.703995] lima 1c40000.gpu: gp - mali400 version major 1 minor 1
! ...

...it's really nice to have a GPU without the need for any proprietary
blobs, thanks to the reverse-engineering efforts by the Lima project.

The kernel log is not the only place revealing information about the hardware.

! root@pine64so:/# cat /proc/iomem
!
! 01000000-0100ffff : 1000000.clock clock@0
! 01100000-011fffff : 1100000.mixer mixer@100000
! 01200000-012fffff : 1200000.mixer mixer@200000
! ...
! ...
! 40000000-bdffffff : System RAM

Here, we get a complete view of the physical-memory layout, including
the locations of all memory-mapped devices as well as the actual RAM. The
(almost) 2 GiB of physical memory does not start at 0 but rather at 0x40000000.

! root@pine64so:/# cat /proc/interrupts

Here, we see how the relationship between devices, interrupt numbers, and
CPUs (interrupt routing) as configured by the Linux kernel.

Another point of interest is the device tree that can be found at
_/proc/device-tree_, which is actually a symbolic link to
_/sys/firmware/devicetree/base_.

At this point, it is too early to digest all this information. Let's save
it for later. The easiest way is storing data on a USB stick.

# When plugging in a USB stick to the second USB port, the kernel's 'dmesg'
  output tells us that it is detected as _/dev/sdb_ as well as the partitions,
  e.g., _/dev/sdb1_ for the first partition.

# Knowing the device name of the partition, we can mount its file system
  at _/mnt_ via 'mount /dev/sdb1 /mnt'.

# Now we can copy any files interest to '/mnt/'.

As an additional function test, one can quickly give the *network interface* a
try. Once when plugging in a network cable to our local network, the LED
on the network PHY starts blinking happily, and 'ifconfig' reveals that
the board got an IP address from our local DHCP server.
A quick 'wget https://genode.org' works just as expected.

Serial line
-----------

Knowing that the board is fully functional when running a Linux-based OS,
we have to work towards using the board as an embedded development target.
Textual output over *serial* is the most important prerequisite for that.
The times when development boards featured 9-pin D-SUB connectors is long
past. Nowadays, we need to look out for the right pins on one of the board's
expansion sockets. The board has several of them. So now is a good time
to get acquainted with the
[https://files.pine64.org/doc/SOPINE-A64/PINE%20A64-TLS-20180130.pdf - board's schematics].

The schematics hint at several serial devices (UART). E.g.,
UART1 at the SDIO WIFI + BT pin header. The go-to solution is not obvious.
Fortunately, a little web search later, we land on a nice wiki page describing
the [https://linux-sunxi.org/Pine64#Serial_port_.2F_UART - UART on Pine64].
In particular, we learn "Better always use UART0 on the EXP connector nearby,
accessible on pins 7 (TXD), 8 (RXD), 9 (GND)."

Everyone should have a few TTL-232R-RPi
cables at hand. If you don't, hurry up and order some.
Pay attention to signal level. In our case, the board needs a 3.3V cable.
All we need is cross-connecting TX to RX, RX to TX, and ground to
ground.

On Linux-based development machines, we usually use
[https://www.mankier.com/1/picocom - picocom] as serial terminal program. When
connecting the USB cable, the Linux kernel's 'dmesg' output tells us about the
new device _/dev/ttyUSB0_, which we can readily access with picocom.

! picocom --baud 115200 /dev/ttyUSB0

When pressing enter, we are greeted with the login of Armbian.

For the next steps, display and keyboard are no longer needed.
All we need is the serial line.

JTAG
----

I'm hopeful that serial output will suffice for most debugging work. However,
in desperate situations like when facing cache-coherency issues, a JTAG
debugger like Lauterbach or Flyswatter can really save the day (or the week).
So when encountering a new board, we always look out for JTAG debugging pins.
If present, we get the cozy feeling of having this option available as a last
resort.

In the case of the Pine64, we must live without this cozy feeling. While
searching the forum [https://forum.pine64.org], I learned that the SoC is
indeed equipped with JTAG pins but the wiring of the Pine board does not make
them accessible.
Apparently, there is too little interest in JTAG by the community at large,
which is perfectly understandable. Most users don't mess around at the low
level where JTAG becomes the tool of choice.

The U-Boot boot loader
~~~~~~~~~~~~~~~~~~~~~~

[https://www.denx.de/wiki/U-Boot - U-Boot] is widely regarded as _the_
canonical boot loader for ARM platforms, and we Genode developers agree.
The primary reason for our high opinion is U-Boot's ability to fetch boot
images over the network from a TFTP server, which is fundamental to our
work flows.

The secondary reason is that U-Boot brings the hardware into a state that is
convenient for the booted operating system. For example, since U-Boot prints
messages over serial, it needs to initialize the serial controller correctly,
fiddly stuff like setting up the baud rate or powering the USB FUE.
With those preparations done by the boot loader, Genode's drivers can
conveniently skip those steps and still work nicely.

The third great benefit of U-Boot to us is the arsenal of drivers supported by
the project. Granted, we don't actually use most of those drivers in practice.
But others are using them. So the drivers work reliably, are well maintained,
and are usually much less complex compared to drivers found in the Linux
kernel. This makes the drivers a very useful reference while developing
drivers for Genode.

Since Armbian uses U-Boot, we can in principle keep using it.
During the boot, one can press <space> at the serial terminal to intercept
the automated boot. This brings us to the interactive U-Boot prompt.

Building U-Boot from source
---------------------------

Building the boot loader from source is not just an affair of honor, it also
fosters our understanding and our full control over the boot process. The
ability to control the boot loader is empowering and can serve as an
experimentation ground.
The steps for building U-Boot manually for Allwinner-based devices are
described in the [https://linux-sunxi.org/Mainline_U-Boot - excellent documentation].

For reference, here are the steps I took.

# Cloning the git repository and checking a recent release branch:

  ! $ git clone git://git.denx.de/u-boot.git
  ! $ cd u-boot
  ! u-boot$ git checkout -b v2020.10 v2020.10

# Looking out for a suitable default configuration for the Pine64-LTS board,
  guessing it would have something like "pine" in the name:

  ! u-boot$ find configs/ | grep -i pine
  ! configs/pinebook-pro-rk3399_defconfig
  ! configs/sopine_baseboard_defconfig
  ! configs/pine64_plus_defconfig
  ! configs/pine64-lts_defconfig
  ! configs/pinebook_defconfig
  ! configs/pine_h64_defconfig

  Well, _pine64-lts_defconfig_ sounds like I'm lucky for the Pine64 board.
  But the PinePhone is notably absent.
  A look at [https://linux-sunxi.org/PinePhone] clarifies the situation:
  "As we currently do not have any specific U-Boot config for this device, Use
  the pine64-lts_defconfig build target temporarily as a hack." That's fine
  by me.

# Building the ARM Trusted Firmware

  The ARM Trusted Firmware is the effort to unify low-level firmware
  interfaces - think of the bring-up secondary CPU cores - across SoC vendors.
  A recent [http://genodians.org/skalk/2020-02-18-armv8-smp - article]
  by Stefan Kalkowski goes into more detail.

  The building steps described at linux-sunxi.org are easy to follow.
  For us, the build output is quite instructive for guiding our attention.

  ! $ make CROSS_COMPILE=aarch64-linux-gnu- PLAT=sun50i_a64 DEBUG=1 bl31
  ! ...
  ! CC      drivers/allwinner/axp/axp803.c
  ! CC      drivers/allwinner/axp/common.c
  ! CC      drivers/allwinner/sunxi_msgbox.c
  ! CC      drivers/allwinner/sunxi_rsb.c
  ! ...
  ! CC      plat/allwinner/sun50i_a64/sunxi_power.c
  ! CC      plat/common/plat_gicv2.c
  ! ...
  ! Built /home/no/pine64/arm-trusted-firmware/build/sun50i_a64/debug/bl31.bin successfully

  There are many more lines. They point us to interesting details. For example,
  _drivers/allwinner/axp/axp803.c_ contains the default settings of the AXP
  power-management chip,
  _plat/allwinner/sun50i_a64/sunxi_power.c_ tells us how the AXP chip is
  accessed via memory-mapped I/O.

# Installing the boot loader on the SD-card

  The steps are described in detail at
  [https://linux-sunxi.org/Bootable_SD_card].
  For me, it is great to see the option of using a GPT partitioning
  scheme, which we already use for Sculpt OS on PC hardware.
  This will hopefully become handy at a later stage.

A few useful U-Boot commands
----------------------------

When booting U-Boot from our freshly prepared SD card, we can see U-Boot
initializing and probing a bunch of devices.
In our current situation, *booting over the network* is the most important
functionality. So we turn our attention to the 'bootp' command.

! => help bootp
! bootp - boot image via network using BOOTP/TFTP protocol
!
! Usage:
! bootp [loadAddress] [[hostIPaddr:]bootfilename]

Let's give it a quick try. My development machine has the IP address
10.0.0.32 within the local network and happens to have a TFTP server running.
Just for the test, I put a little file called _something_ into the TFTP
directory and issue the following command to U-Boot:

! =>  bootp 10.0.0.32:/var/lib/tftpboot/something
!
! TFTP from server 10.0.0.32; our IP address is 10.0.0.178
! Filename '/var/lib/tftpboot/something'.
! Load address: 0x42000000

Of course, I don't want to manually type this command on every boot.
It is much better to tell U-Boot to execute the command automatically
for us. This is possible by customizing U-Boot's 'bootcmd' environment
variable.

! => help editenv
! editenv - edit environment variable
!
! Usage:
! editenv name
!     - edit environment variable 'name'
!
! => editenv bootcmd
! edit: bootp 10.0.0.32:/var/lib/tftpboot/something

With the 'bootcmd' customized to our liking, lets save the new setting.
U-Boot provides the command 'saveenv' for that, which stores the settings
at a predefined location on the MMC / SD card.

! => saveenv
! Saving Environment to FAT... Card did not respond to voltage select!
! Failed (1)

Well, this did not work as anticipated. The reason is that there are two
MMC devices present. The SD-card is connected to the first MMC controller
whereas U-Boot is apparently configured to store its environment via the
second MMC controller. Fortunately, the latter setting can be configured
in U-Boot's build configuration.

Inside the _u-boot/ .config_, we find a configuration variable
called CONFIG_ENV_FAT_DEVICE_AND_PART. In the interactive 'menuconfig', the
corresponding setting is located at
the _Environment_ sub menu:

! (1:auto) Device and partition for where to store the environemt in FAT

Changing the setting to 0:auto should do the trick.
Of course, we have to go again through the steps of building U-Boot and
writing it to the SD-card. But that is a small price to pay for the
convenience that awaits us.

Next time in U-Boot, editing the 'bootcmd' again to our liking and invoking
the 'saveenv' command makes us smile:

! => saveenv
! Saving Environment to FAT... OK

From now on, we can save a number of key strokes on each boot. One final
tweak would increase our comfort even more. By default, U-Boot initializes
the USB controller at boot time. This takes a few seconds, delaying our
boot time. Since we don't plan to boot from any USB device during our
development workflow, it is better to *skip the USB initialization*. This
can be done by changing the 'preboot' environment variable from "usb start" to
nothing, and of course make the change persistent via the 'saveenv' command.


code:
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

repo_name: MichaelBenning/Rotation

path: .gitignore

license: unknown


code:
# Caesar-Code
Hello, this is nothing special. Just a small project which should not have any major
functionality.


caesar-code

start: react-scripts start

build: react-scripts build

test: react-scripts test

eject: react-scripts eject

extends: react-app

production:
>0.2%
not dead
not op_mini all

development:
last 1 chrome version
last 1 firefox version
last 1 safari version


code:
function addiere(x,y) {
    return x+y;
}

function subtrahiere(x,y){
    return x-y;
}

const x = 5; // Konstante, deren Wert sich nicht ändert
let myFunction // Eine Variable, deren Wert sich ändern darf

myFunction = addiere;
// die ganze Funktion addiere()
//wurde jetzt in diese Variable eingespeichert.
// -> Funktioniert nur mit let!

console.log('Hallo');
console.log(addiere(1,2));
console.log(myFunction(3,5));

myFunction = subtrahiere;
console.log(myFunction(3,5));


code:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="logo192.png" />

    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <title>React App</title>

    You need to enable JavaScript to run this app.


Create React App Sample

short_name: React App

type: image/x-icon

type: image/png

display: standalone

background_color: #ffffff


code:
# https://www.robotstxt.org/robotstxt.html
User-agent: *


code:
import React, { Component } from 'react';

import { caesar } from './caesar.js'

import './css/App.css'

export default class App extends Component {
    constructor(props) {
        super(props);

        this.state = {
            message: 'Hier Text zum Verschlüsseln eingeben!',
            encryption: "TUQDFQJFLGYHQDEOTXLEEQXZQUZSQNQZ",
            shift: 13,
        }
        this.handleChange = this.handleChange.bind(this);
        this.handleSlider = this.handleSlider.bind(this);
        this.encrypt = this.encrypt.bind(this);

    };

// Handle change!!! -> React verhält sich wie ein DOM, deswegen benötigt man das
handleChange(event){
    this.setState({message: event.target.value}, this.encrypt);
}

handleSlider(event){
    this.setState({shift: Number.parseInt(event.target.value)}, this.encrypt); // !!! callback
}

encrypt() {
    const encryption = caesar(this.state.message, this.state.shift);
    this.setState({encryption: encryption});
}

render() {
        return (

Caesar Code

                <textarea
                onChange = {this.handleChange}
                value = {this.state.message} />

                <input onChange={this.handleSlider} type="range" min="0" max="25" value={this.state.shift} />

{this.state.shift}

                    {this.state.encryption}

        );
    }
}


code:
const ordA = 'A'.charCodeAt(0); // 65

function ordinal(charCode){ // Ordinalstelle im Alphabet
    return charCode - ordA;
}

function shiftLetter(ordinal, shift){
    const code = ordA + (ordinal+shift) % 25;
    return String.fromCharCode(code)
}

export function clean(input){
    return input
        .toUpperCase()
        .replace(/Ö/g, "OE")
        .replace(/Ä/g, "AE")
        .replace(/Ü/g, "UE")
        .replace(/ß/g, "SZ")
        .replace(/\s+/g, '')
}

export function caesar(input, shift) {
    const cleaned = clean(input);
    return cleaned;
    let out = "";
    for (let i = 0; i < input.length; i++){
    const code = input.charCodeAt(i);
    out += shiftLetter(code, shift);
    }
    return clean(out);
}


code:
* {
    text-align: center;
    margin: 1em auto;
    max-width: 40em;
    line-height: 1.6;
    font-family: 'Iosevka'
}

textarea {
    width: 90%;
    height: 6em;
}

pre > code {
    padding: 20px;
    white-space: pre-wrap;
    display: block;
    margin: 25px auto;
}

code {
    background: #222222;
    color: #fff;
    font-family: monospace;
}


code:
import React from 'react';
import ReactDOM from 'react-dom';

import App from './components/App'

ReactDOM.render(<App />, document.getElementById('root'));


code:
Here is an example of how to register a bow:

bows.register_bow("bow_wood",{
	description = "Wooden bow",
	texture = "bows_bow.png",
	texture_loaded = "bows_bow_loaded.png",
	uses = 50, -- How many time you can use a bow to shoot
	level = 1, -- Higher levels mean faster arrows

	-- Crafting recipe to make bow, can be left nil
	craft = {
		{"", "group:stick", "farming:string"},
		{"group:stick", "", "farming:string"},
		{"", "group:stick", "farming:string"}
	},
})

Here is an example of how to register an arrow:

bows.register_arrow("arrow_steel",{
	description = "Steel arrow",
	-- Arrow texture, the ^[colorize section changes the default arrows colour
	texture = "bows_arrow_wood.png^[colorize:#FFFFFFcc",
	damage = 8, -- How many 1/2 hearts damage it can do
	craft_count = 4, -- How many arrows are made from recipe below

	craft = {
		{"default:steel_ingot", "group:stick", bows.feather}
	},

	-- Special function when an entity or mob is hit
	on_hit_object = function(self, target, hp, user, lastpos)
		if target:get_luaentity().name == "mob_horse:horse" then
			print ("--- aww da horsey!!! " .. hp .. " damage points!")
		end
	end,

	-- Special function when arrow hits a node
	on_hit_node = function(self, pos, user, arrow_pos)
		if self.node.name == "default:glass" then
			minetest.sound_play("default_break_glass", {
				pos = pos, gain = 1.0, max_hear_distance = 10})
			minetest.remove_node(pos)
			minetest.add_item(pos, "vessels:glass_fragments")
		end
	end,
})

repo_name: freelikegnu/bows


code:
-- Localise functions
local floor, min, max = math.floor, math.min, math.max

--Should we use new collisionbox detection
local colbox = false
local radius = colbox and 3.0 or 1.0

--= Functions inspired from Kaeza's Firearms mod

local function minmax(x, y)
	return min(x, y), max(x, y)
end

local function pos_in_box(p, b1, b2)

	local xmin, xmax = minmax(b1.x, b2.x)
	local ymin, ymax = minmax(b1.y, b2.y)
	local zmin, zmax = minmax(b1.z, b2.z)

	return p.x >= xmin and p.x <= xmax
			and p.y >= ymin and p.y <= ymax
			and p.z >= zmin and p.z <= zmax
end

local function get_obj_box(obj)

	local box

	if obj:is_player() then
		box = {-.5, -.5, -.5, .5, 1.5, .5}
	else
		box = obj:get_luaentity().collisionbox
				or {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}
	end

	local p = obj:get_pos()
	local x1, y1, z1, x2, y2, z2 = unpack(box)

	return {x = x1 + p.x, y = y1 + p.y, z = z1 + p.z},
			{x = x2 + p.x, y = y2 + p.y, z = z2 + p.z}
end

--= END (Thanks Kaeza :)

local on_hit_remove = function(self)

	minetest.sound_play(
		bows.registed_arrows[self.name].on_hit_sound, {
			pos = self.object:get_pos(),
			gain = 1.0,
			max_hear_distance = 7
		})

	-- chance of dropping arrow
	local chance = minetest.registered_items[self.name].drop_chance

	if math.random(chance) == 1 then
		minetest.add_item(self.object:get_pos(), self.name)
	end

	self.object:remove()

	return self
end

local on_hit_object = function(self, target, hp, user, lastpos)

	target:punch(user, 0.1, {
		full_punch_interval = 0.1,
		damage_groups = {fleshy = hp},
	}, nil)

	if bows.registed_arrows[self.name].on_hit_object then

		bows.registed_arrows[self.name].on_hit_object(
			self, target, hp, user, lastpos)
	end

	on_hit_remove(self)

	return self
end

minetest.register_entity("bows:arrow",{

	hp_max = 10,
	visual = "wielditem",
	visual_size = {x = .20, y = .20},
	collisionbox = {0,0,0,0,0,0},
	physical = true,
	textures = {"air"},
	_is_arrow = true,
	timer = 10,
	oldvel = {x = 0, y = 0, z = 0},

	on_activate = function(self, staticdata)

		if not self then
			self.object:remove()
			return
		end

		if bows.tmp and bows.tmp.arrow ~= nil then

			self.arrow = bows.tmp.arrow
			self.user = bows.tmp.user
			self.name = bows.tmp.name
			self.dmg = bows.registed_arrows[self.name].damage

			bows.tmp = nil

			self.object:set_properties({textures = {self.arrow}})
		else
			self.object:remove()
		end
	end,

	on_step = function(self, dtime)

		self.timer = self.timer - dtime

		if self.timer < 0 then
			self.object:remove()
			return
		end

		local what_is, what_obj, ent

		for i, ob in pairs(minetest.get_objects_inside_radius(
				self.object:get_pos(), radius)) do

			what_obj = nil
			what_is = ""

			-- player
			if ob
			and bows.pvp
			and ob:is_player()
			and ob:get_player_name() ~= self.user:get_player_name() then

				what_obj = ob
				what_is = "player"
			end

			-- entity/mob
			if ob and not what_obj then

				ent = ob:get_luaentity()

				if ent
				and ent.physical
				and not ent._is_arrow
				and ent.name ~= "__builtin:item" then
					what_obj = ob
					what_is = "entity"
				end
			end

			if what_obj then

				if colbox then

					-- Object specific collision detection
					local p1, p2 = get_obj_box(what_obj)

					if pos_in_box(self.object:get_pos(), p1, p2) then

						on_hit_object(self, what_obj, self.dmg, self.user,
								self.object:get_pos())

						return self
					end
				else
					on_hit_object(self, what_obj, self.dmg, self.user,
							self.object:get_pos())

					return self
				end
			end
		end

		local vel = self.object:get_velocity()

		if vel.x == 0 or vel.y == 0 or vel.z == 0 then

			if bows.registed_arrows[self.name].on_hit_node then

				local pos = self.object:get_pos()
				local lastpos = {x = pos.x, y = pos.y, z = pos.z}

				pos.x = pos.x + (self.oldvel.x / 100)
				pos.y = pos.y + (self.oldvel.y / 100)
				pos.z = pos.z + (self.oldvel.z / 100)

				self.node = minetest.get_node(pos)

				bows.registed_arrows[self.name].on_hit_node(
						self, pos, self.user, lastpos)
			end

			on_hit_remove(self)

			return self
		end

		self.oldvel = vel
	end,
})


code:
default
mobs?
lucky_block?


code: Adds a selection of bows and arrows for players to use in-game.


code:
-- Bows Mod by UjEdwin

bows = {
	pvp = minetest.settings:get_bool("enable_pvp"),
	feather = minetest.get_modpath("mobs") and "mobs:chicken_feather" or "default:leaves",
	registed_arrows = {},
	registed_bows = {},
}

local creative_mode_cache = minetest.settings:get_bool("creative_mode")

function bows.is_creative(name)
	return creative_mode_cache or minetest.check_player_privs(name, {creative = true})
end

bows.register_arrow = function(name, def)

	if name == nil
	or name == "" then
		return false
	end

	def.damage = def.damage or 0
	def.name = "bows:" .. name
	def.level = def.level or 1
	def.on_hit_object = def.on_hit_object
	def.on_hit_node = def.on_hit_node
	def.on_hit_sound = def.on_hit_sound or "default_dig_dig_immediate"

	bows.registed_arrows[def.name] = def

	minetest.register_craftitem("bows:" .. name, {
		description = def.description or name,
		inventory_image = def.texture or "bows_arrow_wooden.png",
		groups = {arrow = 1},
		drop_chance = def.drop_chance
	})

	if def.craft then

		minetest.register_craft({
			output = def.name .." " .. (def.craft_count or 4),
			recipe = def.craft
		})
	end
end

bows.register_bow = function(name, def)

	if name == nil
	or name == "" then
		return false
	end

	def.replace = "bows:" .. name .. "_loaded"
	def.name = "bows:" .. name
	def.uses = def.uses - 1 or 49

	bows.registed_bows[def.replace] = def

	minetest.register_tool(def.name, {
		description = def.description or name,
		inventory_image = def.texture or "bows_bow.png",
		on_use = bows.load,
		groups = {bow = 1, stick = 1},
	})

	minetest.register_tool(def.replace, {
		description = def.description or name,
		inventory_image = def.texture_loaded or "bows_bow_loaded.png",
		on_use = bows.shoot,
		groups = {bow = 1, stick = 1, not_in_creative_inventory = 1},
	})

	if def.craft then
		minetest.register_craft({output = def.name,recipe = def.craft})
	end
end

bows.load = function(itemstack, user, pointed_thing)

	local inv = user:get_inventory()
	local index = user:get_wield_index() - 1
	local arrow = inv:get_stack("main", index)

	if minetest.get_item_group(arrow:get_name(), "arrow") == 0 then
		return itemstack
	end

	local item = itemstack:to_table()
	local meta = minetest.deserialize(item.metadata)

	meta = {arrow = arrow:get_name()}

	item.metadata = minetest.serialize(meta)
	item.name = item.name .. "_loaded"

	itemstack:replace(item)

	if not bows.is_creative(user:get_player_name()) then
		inv:set_stack("main", index,
				ItemStack(arrow:get_name() .. " " .. (arrow:get_count() - 1)))
	end

	return itemstack
end

bows.shoot = function(itemstack, user, pointed_thing)

	local item = itemstack:to_table()
	local meta = minetest.deserialize(item.metadata)

	if (not (meta and meta.arrow))
	or (not bows.registed_arrows[meta.arrow]) then
		return itemstack
	end

	local name = itemstack:get_name()
	local replace = bows.registed_bows[name].name
	local ar = bows.registed_bows[name].uses
	local wear = bows.registed_bows[name].uses
	local level = 19 + bows.registed_bows[name].level

	bows.tmp = {}
	bows.tmp.arrow = meta.arrow
	bows.tmp.user = user
	bows.tmp.name = meta.arrow

	item.arrow = ""
	item.metadata = minetest.serialize(meta)
	item.name = replace
	itemstack:replace(item)

	local pos = user:get_pos()
	local dir = user:get_look_dir()
	local e = minetest.add_entity({
		x = pos.x,
		y = pos.y + 1.5,
		z = pos.z
	}, "bows:arrow")

	e:set_velocity({x = dir.x * level, y = dir.y * level, z = dir.z * level})
	e:set_acceleration({x = dir.x * -3, y = -10, z = dir.z * -3})
	e:set_yaw(user:get_look_horizontal() - math.pi/2)

	if not bows.is_creative(user:get_player_name()) then
		itemstack:add_wear(65535 / wear)
	end

	minetest.sound_play("bows_shoot", {pos = pos})

	return itemstack
end

local path = minetest.get_modpath("bows")

dofile(path .. "/arrow.lua")
dofile(path .. "/items.lua")
dofile(path .. "/lucky_block.lua")

print ("[MOD] Bows loaded")


code:
minetest.register_craft({output = "default:flint",recipe = {{"default:gravel"},}})
minetest.register_craft({output = "farming:cotton 4",recipe = {{"group:wool"},}})

bows.register_bow("bow_wood",{
	description = "Wooden bow",
	texture = "bows_bow.png",
	texture_loaded = "bows_bow_loaded.png",
	uses = 50,
	level = 1,
	craft = {
		{"", "group:stick", "farming:string"},
		{"group:stick", "", "farming:string"},
		{"", "group:stick", "farming:string"}
	},
})

minetest.register_craft({
	type = "fuel",
	recipe = "bows:bow_wood",
	burntime = 3,
})

bows.register_bow("bow_steel",{
	description = "Steel bow",
	texture = "bows_bow_steel.png",
	texture_loaded = "bows_bow_loaded_steel.png",
	uses = 280, --140,
	level = 5, --8,
	craft = {
		{"", "default:steel_ingot", "farming:string"},
		{"default:steel_ingot", "", "farming:string"},
		{"", "default:steel_ingot", "farming:string"}
	},
})

bows.register_bow("bow_bronze",{
	description = "Bronze bow",
	texture = "bows_bow_bronze.png",
	texture_loaded = "bows_bow_loaded_bronze.png",
	uses = 140, --280,
	level = 3, --10,
	craft = {
		{"", "default:bronze_ingot", "farming:string"},
		{"default:bronze_ingot", "", "farming:string"},
		{"", "default:bronze_ingot", "farming:string"}
	},
})

bows.register_bow("bow_bowie",{
	description = "David BOWie",
	texture = "bows_bow_bowie.png",
	texture_loaded = "bows_bow_loaded_bowie.png",
	uses = 500,
	level = 7,
})

bows.register_arrow("arrow",{
	description = "Arrow",
	texture = "bows_arrow_wood.png",
	damage = 5,
	craft_count = 4,
	drop_chance = 10,
	craft = {
		{"default:flint", "group:stick", bows.feather}
	},
--[[
	on_hit_node = function(self, pos, user, arrow_pos)

		minetest.add_particle({
			pos = pos,
			velocity = {x=0, y=0, z=0},
			acceleration = {x=0, y=0, z=0},
			expirationtime = 1,
			size = 4,
			collisiondetection = false,
			vertical = false,
			texture = "heart.png",
		})
	end,
]]
})

minetest.register_craft({
	type = "fuel",
	recipe = "bows:arrow",
	burntime = 1,
})

bows.register_arrow("arrow_steel",{
	description = "Steel arrow",
	texture = "bows_arrow_wood.png^[colorize:#FFFFFFcc",
	damage = 8,
	craft_count = 4,
	drop_chance = 8,
	craft = {
		{"default:steel_ingot", "group:stick", bows.feather}
	},
	on_hit_object = function(self, target, hp, user, lastpos)
		if target
		and target:get_luaentity()
		and target:get_luaentity().name
		and target:get_luaentity().name == "mob_horse:horse" then
			print ("--- aww da horsey!!!")
		end
	end,
})

bows.register_arrow("arrow_mese",{
	description = "Mese arrow",
	texture = "bows_arrow_wood.png^[colorize:#e3ff00cc",
	damage = 12,
	craft_count = 4,
	drop_chance = 6,
	craft = {
		{"default:mese_crystal", "group:stick", bows.feather}
	},
	on_hit_node = function(self, pos, user, arrow_pos)

		if self.node.name == "mesecons_switch:mesecon_switch_on"
		or self.node.name == "mesecons_switch:mesecon_switch_off" then

			local def = minetest.registered_nodes[self.node.name]

			-- This toggles the mesecons switch on/off
			if def and def.on_rightclick then
				def.on_rightclick(vector.round(pos), self.node, user)
			end
		end
	end,
})

bows.register_arrow("arrow_diamond",{
	description = "Diamond arrow",
	texture = "bows_arrow_wood.png^[colorize:#15d7c2cc",
	damage = 15,
	craft_count = 4,
	drop_chance = 4,
	craft = {
		{"default:diamond", "group:stick", bows.feather}
	},
	on_hit_node = function(self, pos, user, arrow_pos)
		if self.node.name == "default:glass"
		and not minetest.is_protected(pos, user:get_player_name()) then
			minetest.sound_play("default_break_glass", {
				pos = pos, gain = 1.0, max_hear_distance = 10})
			minetest.remove_node(pos)
			minetest.add_item(pos, "vessels:glass_fragments")
		end
	end,
})


code:
if minetest.get_modpath("lucky_block") then

	lucky_block:add_blocks({
		{"dro", {"bows:bow_wood"}},
		{"dro", {"bows:bow_steel"}},
		{"dro", {"bows:bow_bronze"}},
		{"dro", {"bows:arrow"}, 5},
		{"dro", {"bows:arrow_steel"}, 5},
		{"dro", {"bows:arrow_mese"}, 5},
		{"dro", {"bows:arrow_diamond"}, 5},
		{"nod", "default:chest", 0, {
			{name = "default:stick", max = 5},
			{name = "default:flint", max = 3},
			{name = "default:steel_ingot", max = 3},
			{name = "default:bronze_ingot", max = 3},
			{name = "default:mese_crystal_fragment", max = 3},
			{name = "farming:string", max = 5},
			{name = bows.feather, max = 4},
			{name = "bows:bow_bowie", max = 1}
		}},
	})
end


code:
# Bows Mod for Minetest

### Originally by AiTechEye

### Forked and tweaked by TenPlus1

This mod allows the player to craft bows and arrows to be used in game that can hurt players and mobs as well as affect nodes hit when using special functions included in the arrow definition.

License: CC0

Lucky blocks: 8


code:
name: Python package

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      max-parallel: 4
      matrix:
        python-version: [3.5, 3.6, 3.7]

    steps:
    - uses: actions/checkout@v1
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v1
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Lint with flake8
      run: |
        pip install flake8
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
    - name: Test with pytest
      run: |
        cd tests
        python runTests.py

repo_name: SulinOS/inary

license: mit


code:
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
.idea

# C extensions
*.so

# Distribution / packaging
.Python
env/
myenv/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot
*.po~

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# IPython Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject

#2to3 files
*.bak

#patch outputs
*.orig
*.rej

#vi & vim swaps
*.swp
*.swo

#For helloworld
.deps/
CMakeCache.txt
CMakeFiles/
*.cmake
config/inary.conf
*.py___jb_tmp___

# inary files
*.inary


code:
language: python
python:
    - 3.4
    - 3.5
    - pypy

# command to install dependencies
install: "pip install -r requirements.txt"

script:
    - ./setup.py install --root=/tmp/install-root
    - ./tests/run.py


code:
Old_Author
-----------
Tubitak-UEKAE <uekae [at] tubitak.org,tr>

Updates and Developings
since 2016

Maintainers
-----------
Suleyman POYRAZ <nipalensisaquila [at] gmail.com>

Developers
----------

Suleyman POYRAZ <nipalensisaquila [at] gmail.com>
parduscix (Ali Rıza Keskin) <parduscix [at] pardustopluluk.org>

Contributors
------------

Suleyman POYRAZ <nipalensisaquila [at] gmail.com>
parduscix (Ali Rıza Keskin) <parduscix [at] pardustopluluk.org>
Fuat BOLUK

Thanks for:
-----------
Ilker MANAP: repodb.py, lambda function problem
Fuat BOLUK: type error(str-unicode type problem)

path: AUTHORS


code:
Copyright (C) 2005 - 2011, Tubitak/UEKAE

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.

Please read the COPYING file.

Author
-----------
Tubitak-UEKAE <uekae [at] tubitak.org,tr>

Maintainers
-----------
Eray Özkural <erayo [at] cs.bilkent.edu.tr>
Barış Metin <baris [at] pardus.org.tr>
A. Murat Eren <meren [at] pardus.org.tr>
Ozan Çağlayan <ozan [at] pardus.org.tr>
Fatih Aşıcı <fatih [at] pardus.org.tr>

Developers
----------
A. Murat Eren <meren [at] pardus.org.tr>
Bahadır Kandemir <bahadir [at] pardus.org.tr>
Barış Metin <baris [at] pardus.org.tr>
Eray Özkural <erayo [at] cs.bilkent.edu.tr>
Faik Uygur <faik [at] pardus.org.tr>
Fatih Aşıcı <fatih [at] pardus.org.tr>
Gürer Özen <gurer [at] pardus.org.tr>
İsmail Dönmez <ismail [at] pardus.org.tr>
Mehmet D. Akın <mdakin [at] pardus.org.tr>
Onur Küçük <onur [at] pardus.org.tr>
Ozan Çağlayan <ozan [at] pardus.org.tr>
S. Çağlar Onur <caglar [at] pardus.org.tr>
Serdar Dalgıç <serdar [at] pardus.org.tr>

Contributors
------------
Ahmet Aygun <ahmet [at] pardusman.org>
Ali Erdinç Köroğlu <erdinc [at] pardus.org.tr>
Amine Chadly <amine.chadly [at] gmail.com>
Andre van der Lubben <klikspin [at] gmail.com>
Andrea Decorte <adecorte [at] gmail.com>
Anton Tolboom <atolboo [at] gmail.com>
Beyza Ermiş <b_ermis [at] ug.bilkent.edu.tr>
Christian Faure <christian.faurebouvard [at] laposte.net>
Davide Siciliano <edivad95 [at] gmail.com>
Eren Türkay <eren [at] pardus.org.tr>
Furkan Duman <coderlord [at] gmail.com>
Gökhan Göktürk <ggokturk [at] sabanciuniv.edu>
Gökmen Göksel <gokmen [at] pardus.org.tr>
Gökmen Görgen <gkmngrgn [at] gmail.com>
Gökçen Eraslan <gokcen [at] pardus.org.tr>
Görkem Çetin <gorkem [at] pardus.org.tr>
H. İbrahim Güngör <h.ibrahim.gungor [at] gmail.com>
Klemens Häckel <click3d [at] linuxmail.org>
Mustafa Ölçerman <oelcerman [at] fussballkreis.org>
Necmettin Begiter <necmettin.begiter [at] gmail.com>
Patrik Karlsson <patrik [at] pardus.nu>
Pedro Leite <pedro.leite [at] gmail.com>
Philippe Svetoslavsky <philsvet [at] gmail.com>
Piotr Maliński <riklaunim [at] gmail.com>
Pınar Yanardağ <pinar [at] pardus.org.tr>
Rinse de Vries <rinsedevries [at] kde.nl>
Semen Cirit <scirit [at] pardus.org.tr>
Stefan Wilhelm <sw1976de [at] googlemail.com>
Uğur Çetin <ugur.jnmbk [at] gmail.com>
Willem Gielen <w.gielen [at] gmail.com>


code:
API Plan
========

Inary does not have a usable api. All the projects use its internal modules to do their
jobs. This file holds a list of these usages by project as a guide for a new competent
inary api.

Inary
====

Below are inary's internal calls that they may help to figure out common api calls.

* packagedb.remove_repo
* sourcedb.remove_repo
* packagedb.which_repo
* repodb.get_repo
* packagedb.get_package
* installdb.is_installed
* packagedb.get_rev_deps
* installdb.get_version
* packagedb.has_package
* packagedb.add_package
* componentdb.add_package
* filesdb.has_file
* filesdb.get_file
* installdb.get_info
* installdb.files
* installdb.pkg_dir
* installdb.install
* filesdb.add_files
* componentdb.add_spec
* sourcedb.add_spec
* componentdb.get_union_comp
* componentdb.remove_spec
* installdb.get_version
* componentdb.remove_repo
* componentdb.remove_package
* componentdb.update_component
* componentdb.add_package
* installdb.remove
* filesdb.remove_files
* sourcedb.pkgtosrc
* sourcedb.get_spec
* sourcedb.get_source
* sourcedb.get_spec_repo
* repodb.get_repo
* componentdb.has_component
* componentdb.get_component
* installdb.is_installed
* packagedb.list_packages
* installdb.list_installed
* componentdb.get_union_packages
* componentdb.list_components
* repodb.list
* sourcedb.list
* installdb.list_pending
* filesdb.match_files

Package Manager
===============

Below are the inary modules used internally by package-manager. Package-manager should use inary
api.

* repodb.get_repo
* packagedb.get_package
  pm still uses the old packagedb with inary.itemsbyrepo.installed or inary.itemsbyrepo.repos params
* componentdb.list_components
* repodb.list
* componentdb.get_union_comp
* componentdb.get_union_packages

Sul-Ins
====

Below are the inary modules used internally by Sul-Ins. Sul-Ins should use inary api.

* api.add_repo
* api.update_repo
* api.remove_repo
* api.install
* packagedb.list_packages
* installdb.list_pending
* api.configure_pending
* packagedb.get_package

Buildfarm
=========

Below are the inary modules used internally by Inaryfarm.

* api.create_delta_package

PackageKit
==========

Below are the inary modules used internally by PackageKit.

* installdb.has_package
* installdb.get_package
* packagedb.has_package
* packagedb.get_package
* installdb.get_rev_deps
* packagedb.get_rev_deps
* installdb.get_package.runtimeDependencies
* packagedb.get_package.runtimeDependencies
* api.install
* api.upgrade
* api.remove
* api.list_upgradable
* api.update_repo
* api.list_repos
* repodb.get_repo.indexuri.get_uri
* version.Version
* util.any

path: ApiPlan


code:
Like every serious project, there are guidelines.
"Coding Standards" for serious.

Guidelines
----------

0. Before reading any further please observe
   PEP 8: Style Guide for Python Code
   http://www.python.org/peps/pep-0008.html

   In particular this means no lameCaps

1. When using dirnames, don't expect the dir to end
   with a trailing slash, and please use the dirnames
   in inaryconfig. Use util.join_path instead of os.path.join
2. Python indentation is usually 4 spaces.
3. Follow python philosophy of 'batteries included'
4. Use exceptions, don't return error codes
5. Don't make the INARY code have runtime dependencies on
   a particular distribution (as much as possible).
6. Don't assume narrow use cases. Allow for a mediocre
   amount of generalization in your code, for pieces that
   will be required later.
7. If you are changing something, check if that change
   breaks anything and fix breakage. For instance a
   name. Running the tests is not always enough!
8. A good design ensures separation of concerns. Every module
   has a specific documented responsibility. Don't make the
   horse clean your windows.
9. To ensure readability avoid nesting python constructs
   more than 3 levels deep. Python is a good language (unlike C),
   so you can define inner functions in a convenient way, use
   such decomposition techniques to break down your code into
   manageable chunks. The worst code you can write is one huge
   procedure that goes on for 1000 (or more) lines.
10. Use a particular abstraction like a class or function only
   if it makes sense. Don't just define things because they can
   be defined. Define only things that will/may be used.
11. If you are doing an expensive task like searching through
   10000 text chunks, please use an efficient data structure
   and algorithm. We are not MS engineers who know no data
   structure beyond doubly linked lists and no algorithm beyond
   quicksort.
12. Resist the temptation to develop kludges and workarounds in
   response to pressure. Take your time to solve the problems by
   the book. The payoff comes later.
13. Same thing goes for premature optimizations. Knuth and Dijkstra
   are watching over your shoulder. :)

Branches and SVN
----------------

There are two branches of inary, one is called inary-devel and
new features that are large enough to cause instability go
into that branch. The trunk version is supposed to be stable at
all times. This means that you *must* run unit tests and other
test scripts after committing any change that cannot be tested
in isolation. Run the unit tests periodically to catch unseen
bugs. A release from the stable branch *must not* break any tests
whatsoever, so extensive use of the test suite must precede any
release.

Unit testing
------------

Unit tests are located in unittests directory. Running the tests is
trivial. But you must synchronize your code and data with the test
code, which can be a tedious work if you lose discipline.

Sample data files are located in the same directory with test modules.

For running the entire test suite, use the following command:

$ ./tests/run.py

The following command will run tests in specfiletests and archivetests
in unittests dir:

$ ./tests/run.py specfile archive

Do not depend on the output of unittests. Instead of producing an
output message/data in your tests, check the data internally. By
definition, unittest should just report succeeding and failing cases.

If you didn't, take a look at the links below for having an idea of
unit testing.
http://www.extremeprogramming.org/rules/unittests.html
http://www.extremeprogramming.org/rules/unittests2.html

Other tests
-----------

There are a couple of nice test scripts for testing the basic
capabilities of the command line interface such as building and
upgrading. Unlike unit tests, you have to take a look at the output
to understand that the scripts are doing well :)

Misc. Suggestions
-----------------

1. Demeter's Law

In OO programming, try to invoke Demeter's law.
One of the "rules" there is not directly accessing any
objects that are further than, 2/3 refs, away. So the
following code is OK.
   destroy_system(a.system().name())
but the following isn't as robust
   destroy_system(object_store.root().a.system.name())
As you can tell, this introduces too many implementation
dependencies. The rule of thumb is that, in these cases
this statement must have been elsewhere.... It may be a
good idea to not count the object scope in this case,
so in Python self.a means only one level of reference,
not two.

One quibble with this: it may be preferable not to insist
on this where it would be inefficient. So if everything
is neatly packed into one object contained in another
object, why replicate everything in the upper level? If
the semantics prevents dependency changes, then chains
of 3 or even 4 could be acceptable.

OTOH, in Python and C++, it's not always good to implement
accessor/modifier pairs for every property of an object.
It would be much simpler if you are not doing any special
processing on the property (e.g. if what the type system
does is sufficient).

The main rule of thumb in Demeter's Law is avoiding
putting more than, say, 10 methods in a class. That works
really well in practice, forcing refactoring every now
and then.

2. We all know, you're using LISP but didn't want to tell
us. Don't be scared, as a success story and for your encouragment
there are tens of people somewhere with LISP releated jobs.

3. If you are studying Data structures and Algorithms, and if
your first assignment is to implement a basic FIFO queue,
don't implement it. Just show your teacher the syntax of LISP,
tell him how beautiful it is, and show how an autistic person
can count lots of parenthesis with a "one second" look, you'll
probably get A+.

4. If you are interested in "Playstation 2 Linux Games Programming"
or "How to extend C programs with Guile", please don't exercise
your valuable skills in this project.

!!!PLEASE BEFORE FIRST INSTALLING START INARY-SYSTEM TEST!!!

path: CODING