Menu

LIRCv2

Alec Leamas

This page is about redesigning lircd to a number smaller components. Other tools e. g., irrecord is not affected.

Why revise design?

In order to stay relevant, we need to modernize lirc:

  • To handle multiple input/output devices.
  • To handle hotplugging of new devices.
  • To add a modern, event-driven interface to make future application
    integration easier.
  • We need to become more open to be able cooperate with other communities.
  • Create a codebase which is more agile and flexible.
  • In the long run, there should be paths to use other rendering engines than
    current which is showing it's age.

Basic concepts for a new design:

  • We should be provide the same socket interface as current lircd w r t receiving
    and sending IR commands. We might drop other, non-documented features. This is
    about keeping the application support.
  • We should keep the driver interface. Keeping a stable ground for devs to integrate
    new hardware is essential.
  • We should move away from the basic model with one lircd process connected
    to one socket to a model where several lircd instances possibly are connected
    to a single socket.
  • Rather than to enhance the already complicated lircd process we should try to
    split it into smaller pieces with well-defined interfaces.
  • The synchronization now performed in the lircd main loop should if possible
    be delegated to some existing mechanism. This is the glue that connects all the
    lircd current functionality to one, single piece of tightly coupled code.

What is lircd?

In order to create a new design for lircd, we need to understand the basic blocks in
current code:

  • The core is IR decoding/encoding. There are really no well-defined interfaces to this.
    Basic concepts here is "IR commands", the user level representation of a remote
    button press and the low-level IR data.
    • When sending, lircd converts IR commmands to a sequence of low-level IR data
      using the database, usually in /etc/lircd.conf and/or /etc/lircd.conf.d/.
    • When receiving, lircd converts the low-level IR data to IR commands using the
      same database.
  • A driver adapter which can load and use a device-specific user-space driver.
    Drivers have an established API to lircd.
  • A socket interface which allows:
    • Listening to keypresses from remote.
    • Sending of IR commands.
  • A command handling interface implementing the socket commands. This interface is described
    in the manual ("Writing applications for LIRC.")
  • An API built on top of the socket interface providing the functions described in
    the manual ("The lirc_client API").
  • Code which either gets input from or sends data to a remote lircd
    instance over the network (the "--listen" and "--connect" runtime options).
  • Code which send all received data to the kernel uinput device, implementing the
    --uinput runtime opiton.
  • A main loop which handles both static parties (data from the kernel device,
    data from the socket interface, possibly TCP data) and dynamically connected
    socket clients.

Why LIRC?

If we change the overall design, we need to be careful not to harm LIRC's strong points:

  • The strong application support in the Linux HTPC area..
  • The only framework for handling IR remotes available in all Linux distros
    (besides the kernel).
  • The easiest way to handle remotes not supported by the kernel, attracting developers
    and power users.
  • Added functionality compared to the kernel device interface attracting applications.

Why dbus:

My idea is that we base a new design on dbus which acts as a dispatcher between multiple
clients and multiple lircd instances. There is a number or reasons to use Dbus:

  • Dbus solves the problem of passing messages between different users (i. e., the
    lircd user and the session.)
  • Dbus is fast enough to handle keypresses without user-visible lag (?).
  • It is language independent with bindings to most languages and the two common
    main loops (GLib and Qt).
  • It is already part of any Linux system we target. Even more so when it shortly will
    become part of the kernel(kdbus). There is also an OSX port.
  • The asynchronous, message based design fits the LIRC bill.

A new design

  • Use Dbus as dispatcher. All components and clients connects to the bus which
    passes messages and synchronizes. The only synchronization done within lircd is
    the full duplex send/receive conflict on the kernel device. This means that
    components are defined by their dbus interface.
  • Limit current lircd to
    • Decoding of data from kernel which are broadcasted as dbus events.
    • Handling dbus commands, notably for sending.
      Practically, this means that lircd exposes a dbus interface supporting
      listening to/sending IR commands.
  • Client connects to lircd either
    • Directly to the socket to send data.
    • Indirectly to the socket using the LIRC API to listen or send.
    • Directly to the Dbus interface to listen or send.
  • The TCP module is a full duplex module reading from the remote lircd instance
    providing data on dbus in the same way as a "normal" lircd instance,
    implementing the runtime --listen and --connect options.
  • The uinput module listens to dbus IR events a feeds them to the kernel
    uinput device, implementing the --uinput runtime option.
  • The socket interface + command handling is a separate module handling dbus-socket
    conversion. Each connected client is a separate process, dbus handles
    synchronization.
  • We can now start several lircd input instances, all connected to same dbus and
    socket interface -> solves multiple input devices. In other configurations we
    use different sockets as today.
  • The lircd instance is only defined by the dbus and kernel device interface. This
    means that we have a framework where we can test different implementations. We now
    need a new name for the kernel device/dbus thing. Let's call it 'connector'. Our
    first connector will be a stripped down lircd. However, we should design this so
    that we could use something enterely different later.
  • We need a new Manager module which loads/unloads correct connector + configuration when
    configuration changes or udev signals devices are removed/added. This exposes the primary
    Dbus client interface, since it's the only instance with a given name which always exists.
  • The configuration is a little tricky. However, with a little care, we should be able to
    do this in a more or less implementation-agnostic way. First shot is that a
    configuration is a dictionary of strings, a YAML or xml file or something similar.

Like this Design sketch

Harctoolbox notes

There are some thoughts on the same topic with a different perspective. In this parlance, a connector is basically a combined listener/sender with a Dbus interface.


Related

Wiki: Home