| ======================= | 
 | 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. |