| ======================= |
| Extcon Device Subsystem |
| ======================= |
| |
| Overview |
| ======== |
| |
| The Extcon (External Connector) subsystem provides a unified framework for |
| managing external connectors in Linux systems. It allows drivers to report |
| the state of external connectors and provides a standardized interface for |
| userspace to query and monitor these states. |
| |
| Extcon is particularly useful in modern devices with multiple connectivity |
| options, such as smartphones, tablets, and laptops. It helps manage various |
| types of connectors, including: |
| |
| 1. USB connectors (e.g., USB-C, micro-USB) |
| 2. Charging ports (e.g., fast charging, wireless charging) |
| 3. Audio jacks (e.g., 3.5mm headphone jack) |
| 4. Video outputs (e.g., HDMI, DisplayPort) |
| 5. Docking stations |
| |
| Real-world examples: |
| |
| 1. Smartphone USB-C port: |
| A single USB-C port on a smartphone can serve multiple functions. Extcon |
| can manage the different states of this port, such as: |
| - USB data connection |
| - Charging (various types like fast charging, USB Power Delivery) |
| - Audio output (USB-C headphones) |
| - Video output (USB-C to HDMI adapter) |
| |
| 2. Laptop docking station: |
| When a laptop is connected to a docking station, multiple connections are |
| made simultaneously. Extcon can handle the state changes for: |
| - Power delivery |
| - External displays |
| - USB hub connections |
| - Ethernet connectivity |
| |
| 3. Wireless charging pad: |
| Extcon can manage the state of a wireless charging connection, allowing |
| the system to respond appropriately when a device is placed on or removed |
| from the charging pad. |
| |
| 4. Smart TV HDMI ports: |
| In a smart TV, Extcon can manage multiple HDMI ports, detecting when |
| devices are connected or disconnected, and potentially identifying the |
| type of device (e.g., gaming console, set-top box, Blu-ray player). |
| |
| The Extcon framework simplifies the development of drivers for these complex |
| scenarios by providing a standardized way to report and query connector |
| states, handle mutually exclusive connections, and manage connector |
| properties. This allows for more robust and flexible handling of external |
| connections in modern devices. |
| |
| Key Components |
| ============== |
| |
| extcon_dev |
| ---------- |
| |
| The core structure representing an Extcon device:: |
| |
| struct extcon_dev { |
| const char *name; |
| const unsigned int *supported_cable; |
| const u32 *mutually_exclusive; |
| |
| /* Internal data */ |
| struct device dev; |
| unsigned int id; |
| struct raw_notifier_head nh_all; |
| struct raw_notifier_head *nh; |
| struct list_head entry; |
| int max_supported; |
| spinlock_t lock; |
| u32 state; |
| |
| /* Sysfs related */ |
| struct device_type extcon_dev_type; |
| struct extcon_cable *cables; |
| struct attribute_group attr_g_muex; |
| struct attribute **attrs_muex; |
| struct device_attribute *d_attrs_muex; |
| }; |
| |
| Key fields: |
| |
| - ``name``: Name of the Extcon device |
| - ``supported_cable``: Array of supported cable types |
| - ``mutually_exclusive``: Array defining mutually exclusive cable types |
| This field is crucial for enforcing hardware constraints. It's an array of |
| 32-bit unsigned integers, where each element represents a set of mutually |
| exclusive cable types. The array should be terminated with a 0. |
| |
| For example: |
| |
| :: |
| |
| static const u32 mutually_exclusive[] = { |
| BIT(0) | BIT(1), /* Cable 0 and 1 are mutually exclusive */ |
| BIT(2) | BIT(3) | BIT(4), /* Cables 2, 3, and 4 are mutually exclusive */ |
| 0 /* Terminator */ |
| }; |
| |
| In this example, cables 0 and 1 cannot be connected simultaneously, and |
| cables 2, 3, and 4 are also mutually exclusive. This is useful for |
| scenarios like a single port that can either be USB or HDMI, but not both |
| at the same time. |
| |
| The Extcon core uses this information to prevent invalid combinations of |
| cable states, ensuring that the reported states are always consistent |
| with the hardware capabilities. |
| |
| - ``state``: Current state of the device (bitmap of connected cables) |
| |
| |
| extcon_cable |
| ------------ |
| |
| Represents an individual cable managed by an Extcon device:: |
| |
| struct extcon_cable { |
| struct extcon_dev *edev; |
| int cable_index; |
| struct attribute_group attr_g; |
| struct device_attribute attr_name; |
| struct device_attribute attr_state; |
| struct attribute *attrs[3]; |
| union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT]; |
| union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT]; |
| union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT]; |
| union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT]; |
| DECLARE_BITMAP(usb_bits, EXTCON_PROP_USB_CNT); |
| DECLARE_BITMAP(chg_bits, EXTCON_PROP_CHG_CNT); |
| DECLARE_BITMAP(jack_bits, EXTCON_PROP_JACK_CNT); |
| DECLARE_BITMAP(disp_bits, EXTCON_PROP_DISP_CNT); |
| }; |
| |
| Core Functions |
| ============== |
| |
| .. kernel-doc:: drivers/extcon/extcon.c |
| :identifiers: extcon_get_state |
| |
| .. kernel-doc:: drivers/extcon/extcon.c |
| :identifiers: extcon_set_state |
| |
| .. kernel-doc:: drivers/extcon/extcon.c |
| :identifiers: extcon_set_state_sync |
| |
| .. kernel-doc:: drivers/extcon/extcon.c |
| :identifiers: extcon_get_property |
| |
| |
| Sysfs Interface |
| =============== |
| |
| Extcon devices expose the following sysfs attributes: |
| |
| - ``name``: Name of the Extcon device |
| - ``state``: Current state of all supported cables |
| - ``cable.N/name``: Name of the Nth supported cable |
| - ``cable.N/state``: State of the Nth supported cable |
| |
| Usage Example |
| ------------- |
| |
| .. code-block:: c |
| |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/extcon.h> |
| |
| struct my_extcon_data { |
| struct extcon_dev *edev; |
| struct device *dev; |
| }; |
| |
| static const unsigned int my_extcon_cable[] = { |
| EXTCON_USB, |
| EXTCON_USB_HOST, |
| EXTCON_NONE, |
| }; |
| |
| static int my_extcon_probe(struct platform_device *pdev) |
| { |
| struct my_extcon_data *data; |
| int ret; |
| |
| data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); |
| if (!data) |
| return -ENOMEM; |
| |
| data->dev = &pdev->dev; |
| |
| /* Initialize extcon device */ |
| data->edev = devm_extcon_dev_allocate(data->dev, my_extcon_cable); |
| if (IS_ERR(data->edev)) { |
| dev_err(data->dev, "Failed to allocate extcon device\n"); |
| return PTR_ERR(data->edev); |
| } |
| |
| /* Register extcon device */ |
| ret = devm_extcon_dev_register(data->dev, data->edev); |
| if (ret < 0) { |
| dev_err(data->dev, "Failed to register extcon device\n"); |
| return ret; |
| } |
| |
| platform_set_drvdata(pdev, data); |
| |
| /* Example: Set initial state */ |
| extcon_set_state_sync(data->edev, EXTCON_USB, true); |
| |
| dev_info(data->dev, "My extcon driver probed successfully\n"); |
| return 0; |
| } |
| |
| static int my_extcon_remove(struct platform_device *pdev) |
| { |
| struct my_extcon_data *data = platform_get_drvdata(pdev); |
| |
| /* Example: Clear state before removal */ |
| extcon_set_state_sync(data->edev, EXTCON_USB, false); |
| |
| dev_info(data->dev, "My extcon driver removed\n"); |
| return 0; |
| } |
| |
| static const struct of_device_id my_extcon_of_match[] = { |
| { .compatible = "my,extcon-device", }, |
| { }, |
| }; |
| MODULE_DEVICE_TABLE(of, my_extcon_of_match); |
| |
| static struct platform_driver my_extcon_driver = { |
| .driver = { |
| .name = "my-extcon-driver", |
| .of_match_table = my_extcon_of_match, |
| }, |
| .probe = my_extcon_probe, |
| .remove = my_extcon_remove, |
| }; |
| |
| module_platform_driver(my_extcon_driver); |
| |
| This example demonstrates: |
| --------------------------- |
| |
| - Defining supported cable types (USB and USB Host in this case). |
| - Allocating and registering an extcon device. |
| - Setting an initial state for a cable (USB connected in this example). |
| - Clearing the state when the driver is removed. |