Search

Child Character Device

8 min read 0 views
Child Character Device

Introduction

A child character device is a node in the Linux kernel’s device model that represents a character device whose logical or physical existence is tied to a parent device. While a character device exposes a file‑system interface for byte‑stream I/O, the “child” designation refers to its placement within the hierarchical structure of sysfs. Such devices are created by drivers when the device they represent is a subordinate component of a larger hardware or virtual subsystem. Typical examples include USB serial ports, pseudo‑terminals, input devices, and the FUSE file‑system interface. The concept of child character devices is central to the modular design of the kernel, allowing drivers to expose functionality without needing to manage the entire device tree themselves.

Definition

In the Linux kernel, a device is an abstract representation of a hardware or virtual component. The kernel organizes devices in a tree, with root devices (e.g., bus controllers) at the top and individual components as leaves. A character device is a type of device that provides unstructured, stream‑based I/O. A child character device is one whose sysfs parent entry is another device node, meaning that it exists only in the context of its parent. The child status does not alter the device’s I/O semantics; it merely dictates how the kernel presents the device to user space and how the driver registers and manages it.

Historical Context

The character device abstraction dates back to early Unix systems, where devices such as /dev/tty and /dev/ttyS0 were exposed as simple file nodes. With the advent of the Linux kernel in the early 1990s, the device model evolved to include a more robust sysfs hierarchy. The 2.6 kernel series introduced the struct device and struct cdev APIs, which formalized the creation of device nodes and their placement in sysfs. The concept of parent‑child relationships in sysfs emerged to reflect the physical topology of buses (PCI, USB, I²C) and to facilitate coherent driver interaction. As drivers grew in complexity, the need to expose multiple logical functions under a single physical device led to the widespread use of child character devices.

Linux Device Model

Device Hierarchy

The kernel’s device model presents a tree of struct device objects. Each object can have zero or more children and a single parent. The root of the tree is the root device, and bus devices such as PCI controllers or USB hubs are typically added as intermediate nodes. Devices attached to a bus are registered as children of the corresponding bus device. This hierarchical organization is reflected in the /sys/class and /sys/bus directories, where each device has a symlinked representation. For example, a USB serial port’s device node resides under /sys/bus/usb/devices/ and appears as a child of the USB hub node.

Character Devices

A character device provides sequential or random access to a device through a file descriptor. The kernel’s register_chrdev_region function reserves a range of major and minor numbers, while cdev_add associates a struct cdev with the device’s file operations. Once registered, the kernel creates a device node in /dev using device_create or class_create. If the device’s struct device has a parent, the device node is considered a child character device.

Child Character Device Registration

Major/Minor Number Allocation

Drivers allocate a major number (identifying the driver) and a minor number (identifying a specific device). The API alloc_chrdev_region can allocate a contiguous range, while register_chrdev_region assigns a specific range. For child devices, drivers often use alloc_chrdev_region once and then offset the minor number for each child.

Creating the cdev Object

  1. Initialize a struct cdev with cdev_init, passing the driver’s file_operations.
  2. Set the device’s owner using cdev.owner = THIS_MODULE.
  3. Add the character device to the system with cdev_add, supplying the device’s dev_t.

Sysfs and Device Creation

To expose the child device to user space, drivers use the following steps:

  • Instantiate a struct class for the device type, typically via class_create.
  • For each child device, call device_create (or devicecreatewith_groups) passing the parent struct device * obtained from the bus driver.
  • The device_create function automatically creates the device node in /dev and populates the sysfs entry under /sys/class.

Example code for a USB serial driver:

dev_t devt = MKDEV(major, minor);
struct cdev *cdev = &usb_serial_cdev[dev_index];
cdev_init(cdev, &usb_serial_fops);
cdev_add(cdev, devt, 1);

struct device *dev = device_create(usb_serial_class,
                                   usb_device,
                                   devt,
                                   NULL,
                                   "ttyUSB%d",
                                   dev_index);

Permissions and Security

Udev Rules

When the device node is created, udev processes the node’s major/minor pair and applies rules from /etc/udev/rules.d or /lib/udev/rules.d. Rules can set ownership, permissions, and even symlink names. For child devices, the parent’s device type can influence the rule set. Example rule: SUBSYSTEM=="tty", ACTION=="add", ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", MODE="0666" sets all Prolific USB serial ports to be world‑readable.

SELinux Contexts

SELinux or AppArmor can restrict access to child devices. The kernel automatically tags the device node with a security context based on the device’s class and the parent’s context. For example, /dev/ttyUSB0 inherits the “tty” type from its parent, allowing services to interact with the device according to policy.

Examples

/dev/ttyUSBx (USB Serial Ports)

USB serial adapters appear as child devices of the USB bus. The driver registers a major number for serial devices (typically 4) and assigns minor numbers 64–79 for each USB serial port. Each port is a child character device with a sysfs entry under /sys/bus/usb/devices/. The device node’s permissions can be set by udev rules that match the vendor/product IDs.

/dev/input/eventX (Input Devices)

The input subsystem creates a separate child character device for each logical input device. For instance, a USB keyboard creates /dev/input/event3 as a child of the USB keyboard’s device node. The driver provides input event reporting through the input_report_key API.

/dev/ptmx and /dev/pts/* (Pseudo‑Terminals)

The PTY subsystem uses a master device (/dev/ptmx) and creates a pool of slave devices (/dev/pts/0, /dev/pts/1, …). Each slave is a child character device whose parent is the PTY master. The kernel dynamically creates the slave node when a process opens /dev/ptmx and attaches it to the appropriate master. Sysfs entries appear under /sys/dev/pts with symlinks named by the slave number.

/dev/fuse (Filesystem in Userspace)

The FUSE kernel module provides a single character device (/dev/fuse) that serves as the gateway for all FUSE file‑system mounts. Internally, the module creates child devices for each user‑space mount point, which appear as entries under /sys/dev/fuse. These child devices expose the same file operations but are isolated per mount.

/dev/tty0 (Console)

The console driver creates /dev/tty0 and /dev/tty1, etc. Each console is a child of the console subsystem. While not a hierarchical bus, the console devices are logically grouped and share the same major number (4).

Use Cases

Multiplexed Interfaces

Drivers for multiplexed hardware often expose multiple logical devices as children. For example, a network adapter with multiple virtual interfaces registers a separate child character device for each interface. This design allows user space to interact with each interface independently while sharing the same physical device.

Virtualization and Hypervisors

Virtual machines expose guest devices as child character devices of the hypervisor’s bus. A virtual GPU driver may create a child /dev/tty for each virtual display. The hypervisor manages the bus hierarchy, and the guest sees each device as a standard character device.

Device Grouping and Attribute Exposure

Child devices can expose additional attributes via sysfs files. For instance, a USB storage driver creates a serial attribute for each child device that lists the device’s serial number. Applications can read these attributes without parsing the device node directly.

Troubleshooting

Examining Device Hierarchy

  • Use ls -l /sys/class/tty to view the symlinks to /sys/devices and identify parent devices.
  • Navigate to the parent’s directory and inspect child symlinks: cd /sys/devices/usb//tty.

Checking udev Rules

Run udevadm info -a -n /dev/ttyUSB0 to see which rules applied and the resulting attributes. If the node is missing or has incorrect permissions, adjust the rule or re‑load udev: udevadm control --reload-rules && udevadm trigger.

Kernel Logs

The kernel’s dmesg buffer contains messages about device registration and errors. Use dmesg | grep ttyUSB to filter for serial driver messages. If a child device fails to register, the log may indicate “Cannot allocate minor number” or “Device busy.”

Advanced Topics

Kernel Configuration Options

To enable child character device support, the following kernel configuration options are typically required:

  • CONFIG_SYSFS – Provides the sysfs interface.
  • CONFIGDEVICECLASS – Enables the class interface for device creation.
  • CONFIGSYSFSDEPRECATED – For backward compatibility.

Device Groups and Attribute Files

Drivers can define attribute_group structures that group multiple sysfs attributes. When creating a child device, the driver can pass a pointer to the attribute group, and udev can use these attributes to generate naming conventions or apply rules. Example: the usb_serial_driver exposes a manufacturer and product attribute for each child serial port.

Interacting with Device Hierarchy Programmatically

Applications can traverse the sysfs hierarchy using the libudev library. The udev_device_get_parent function retrieves the parent device of a given child, allowing programs to discover device relationships and gather metadata.

Comparison with Block Devices

While character devices provide byte‑stream I/O, block devices expose block‑based I/O and are represented by a separate struct block_device type. Child block devices are common as well, e.g., a RAID array has individual disks as child block devices. The registration process is similar, but block devices require additional support for buffering and caching. Unlike character devices, block devices often create partitions that appear as further child devices (e.g., /dev/sda1). The sysfs representation for block devices appears under /sys/block rather than /sys/class/tty.

Conclusion

Child character devices are a fundamental mechanism in Linux for representing logical sub‑devices of a larger hardware bus or subsystem. Their integration with sysfs, udev, and security frameworks allows flexible, dynamic, and secure device exposure to user space. Understanding the registration workflow, permissions management, and troubleshooting methods is essential for both driver developers and system administrators dealing with complex device hierarchies.

References & Further Reading

References / Further Reading

Sources

The following sources were referenced in the creation of this article. Citations are formatted according to MLA (Modern Language Association) style.

  1. 1.
    "man udevadm(8)." man7.org, https://man7.org/linux/man-pages/man8/udevadm.8.html. Accessed 16 Apr. 2026.
  2. 2.
    "Linux Kernel Source – TTY Drivers." github.com, https://github.com/torvalds/linux/tree/master/drivers/tty. Accessed 16 Apr. 2026.
Was this helpful?

Share this article

See Also

Suggest a Correction

Found an error or have a suggestion? Let us know and we'll review it.

Comments (0)

Please sign in to leave a comment.

No comments yet. Be the first to comment!