|
| 1 | +======================= |
| 2 | +Extcon Device Subsystem |
| 3 | +======================= |
| 4 | + |
| 5 | +Overview |
| 6 | +======== |
| 7 | + |
| 8 | +The Extcon (External Connector) subsystem provides a unified framework for |
| 9 | +managing external connectors in Linux systems. It allows drivers to report |
| 10 | +the state of external connectors and provides a standardized interface for |
| 11 | +userspace to query and monitor these states. |
| 12 | + |
| 13 | +Extcon is particularly useful in modern devices with multiple connectivity |
| 14 | +options, such as smartphones, tablets, and laptops. It helps manage various |
| 15 | +types of connectors, including: |
| 16 | + |
| 17 | +1. USB connectors (e.g., USB-C, micro-USB) |
| 18 | +2. Charging ports (e.g., fast charging, wireless charging) |
| 19 | +3. Audio jacks (e.g., 3.5mm headphone jack) |
| 20 | +4. Video outputs (e.g., HDMI, DisplayPort) |
| 21 | +5. Docking stations |
| 22 | + |
| 23 | +Real-world examples: |
| 24 | + |
| 25 | +1. Smartphone USB-C port: |
| 26 | + A single USB-C port on a smartphone can serve multiple functions. Extcon |
| 27 | + can manage the different states of this port, such as: |
| 28 | + - USB data connection |
| 29 | + - Charging (various types like fast charging, USB Power Delivery) |
| 30 | + - Audio output (USB-C headphones) |
| 31 | + - Video output (USB-C to HDMI adapter) |
| 32 | + |
| 33 | +2. Laptop docking station: |
| 34 | + When a laptop is connected to a docking station, multiple connections are |
| 35 | + made simultaneously. Extcon can handle the state changes for: |
| 36 | + - Power delivery |
| 37 | + - External displays |
| 38 | + - USB hub connections |
| 39 | + - Ethernet connectivity |
| 40 | + |
| 41 | +3. Wireless charging pad: |
| 42 | + Extcon can manage the state of a wireless charging connection, allowing |
| 43 | + the system to respond appropriately when a device is placed on or removed |
| 44 | + from the charging pad. |
| 45 | + |
| 46 | +4. Smart TV HDMI ports: |
| 47 | + In a smart TV, Extcon can manage multiple HDMI ports, detecting when |
| 48 | + devices are connected or disconnected, and potentially identifying the |
| 49 | + type of device (e.g., gaming console, set-top box, Blu-ray player). |
| 50 | + |
| 51 | +The Extcon framework simplifies the development of drivers for these complex |
| 52 | +scenarios by providing a standardized way to report and query connector |
| 53 | +states, handle mutually exclusive connections, and manage connector |
| 54 | +properties. This allows for more robust and flexible handling of external |
| 55 | +connections in modern devices. |
| 56 | + |
| 57 | +Key Components |
| 58 | +============== |
| 59 | + |
| 60 | +extcon_dev |
| 61 | +---------- |
| 62 | + |
| 63 | +The core structure representing an Extcon device:: |
| 64 | + |
| 65 | + struct extcon_dev { |
| 66 | + const char *name; |
| 67 | + const unsigned int *supported_cable; |
| 68 | + const u32 *mutually_exclusive; |
| 69 | + |
| 70 | + /* Internal data */ |
| 71 | + struct device dev; |
| 72 | + unsigned int id; |
| 73 | + struct raw_notifier_head nh_all; |
| 74 | + struct raw_notifier_head *nh; |
| 75 | + struct list_head entry; |
| 76 | + int max_supported; |
| 77 | + spinlock_t lock; |
| 78 | + u32 state; |
| 79 | + |
| 80 | + /* Sysfs related */ |
| 81 | + struct device_type extcon_dev_type; |
| 82 | + struct extcon_cable *cables; |
| 83 | + struct attribute_group attr_g_muex; |
| 84 | + struct attribute **attrs_muex; |
| 85 | + struct device_attribute *d_attrs_muex; |
| 86 | + }; |
| 87 | + |
| 88 | +Key fields: |
| 89 | + |
| 90 | +- ``name``: Name of the Extcon device |
| 91 | +- ``supported_cable``: Array of supported cable types |
| 92 | +- ``mutually_exclusive``: Array defining mutually exclusive cable types |
| 93 | + This field is crucial for enforcing hardware constraints. It's an array of |
| 94 | + 32-bit unsigned integers, where each element represents a set of mutually |
| 95 | + exclusive cable types. The array should be terminated with a 0. |
| 96 | + |
| 97 | + For example: |
| 98 | + |
| 99 | + :: |
| 100 | + |
| 101 | + static const u32 mutually_exclusive[] = { |
| 102 | + BIT(0) | BIT(1), /* Cable 0 and 1 are mutually exclusive */ |
| 103 | + BIT(2) | BIT(3) | BIT(4), /* Cables 2, 3, and 4 are mutually exclusive */ |
| 104 | + 0 /* Terminator */ |
| 105 | + }; |
| 106 | + |
| 107 | + In this example, cables 0 and 1 cannot be connected simultaneously, and |
| 108 | + cables 2, 3, and 4 are also mutually exclusive. This is useful for |
| 109 | + scenarios like a single port that can either be USB or HDMI, but not both |
| 110 | + at the same time. |
| 111 | + |
| 112 | + The Extcon core uses this information to prevent invalid combinations of |
| 113 | + cable states, ensuring that the reported states are always consistent |
| 114 | + with the hardware capabilities. |
| 115 | + |
| 116 | +- ``state``: Current state of the device (bitmap of connected cables) |
| 117 | + |
| 118 | + |
| 119 | +extcon_cable |
| 120 | +------------ |
| 121 | + |
| 122 | +Represents an individual cable managed by an Extcon device:: |
| 123 | + |
| 124 | + struct extcon_cable { |
| 125 | + struct extcon_dev *edev; |
| 126 | + int cable_index; |
| 127 | + struct attribute_group attr_g; |
| 128 | + struct device_attribute attr_name; |
| 129 | + struct device_attribute attr_state; |
| 130 | + struct attribute *attrs[3]; |
| 131 | + union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT]; |
| 132 | + union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT]; |
| 133 | + union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT]; |
| 134 | + union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT]; |
| 135 | + DECLARE_BITMAP(usb_bits, EXTCON_PROP_USB_CNT); |
| 136 | + DECLARE_BITMAP(chg_bits, EXTCON_PROP_CHG_CNT); |
| 137 | + DECLARE_BITMAP(jack_bits, EXTCON_PROP_JACK_CNT); |
| 138 | + DECLARE_BITMAP(disp_bits, EXTCON_PROP_DISP_CNT); |
| 139 | + }; |
| 140 | + |
| 141 | +Core Functions |
| 142 | +============== |
| 143 | + |
| 144 | +.. kernel-doc:: drivers/extcon/extcon.c |
| 145 | + :identifiers: extcon_get_state |
| 146 | + |
| 147 | +.. kernel-doc:: drivers/extcon/extcon.c |
| 148 | + :identifiers: extcon_set_state |
| 149 | + |
| 150 | +.. kernel-doc:: drivers/extcon/extcon.c |
| 151 | + :identifiers: extcon_set_state_sync |
| 152 | + |
| 153 | +.. kernel-doc:: drivers/extcon/extcon.c |
| 154 | + :identifiers: extcon_get_property |
| 155 | + |
| 156 | + |
| 157 | +Sysfs Interface |
| 158 | +=============== |
| 159 | + |
| 160 | +Extcon devices expose the following sysfs attributes: |
| 161 | + |
| 162 | +- ``name``: Name of the Extcon device |
| 163 | +- ``state``: Current state of all supported cables |
| 164 | +- ``cable.N/name``: Name of the Nth supported cable |
| 165 | +- ``cable.N/state``: State of the Nth supported cable |
| 166 | + |
| 167 | +Usage Example |
| 168 | +------------- |
| 169 | + |
| 170 | +.. code-block:: c |
| 171 | +
|
| 172 | + #include <linux/module.h> |
| 173 | + #include <linux/platform_device.h> |
| 174 | + #include <linux/extcon.h> |
| 175 | +
|
| 176 | + struct my_extcon_data { |
| 177 | + struct extcon_dev *edev; |
| 178 | + struct device *dev; |
| 179 | + }; |
| 180 | +
|
| 181 | + static const unsigned int my_extcon_cable[] = { |
| 182 | + EXTCON_USB, |
| 183 | + EXTCON_USB_HOST, |
| 184 | + EXTCON_NONE, |
| 185 | + }; |
| 186 | +
|
| 187 | + static int my_extcon_probe(struct platform_device *pdev) |
| 188 | + { |
| 189 | + struct my_extcon_data *data; |
| 190 | + int ret; |
| 191 | +
|
| 192 | + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); |
| 193 | + if (!data) |
| 194 | + return -ENOMEM; |
| 195 | +
|
| 196 | + data->dev = &pdev->dev; |
| 197 | +
|
| 198 | + /* Initialize extcon device */ |
| 199 | + data->edev = devm_extcon_dev_allocate(data->dev, my_extcon_cable); |
| 200 | + if (IS_ERR(data->edev)) { |
| 201 | + dev_err(data->dev, "Failed to allocate extcon device\n"); |
| 202 | + return PTR_ERR(data->edev); |
| 203 | + } |
| 204 | +
|
| 205 | + /* Register extcon device */ |
| 206 | + ret = devm_extcon_dev_register(data->dev, data->edev); |
| 207 | + if (ret < 0) { |
| 208 | + dev_err(data->dev, "Failed to register extcon device\n"); |
| 209 | + return ret; |
| 210 | + } |
| 211 | +
|
| 212 | + platform_set_drvdata(pdev, data); |
| 213 | +
|
| 214 | + /* Example: Set initial state */ |
| 215 | + extcon_set_state_sync(data->edev, EXTCON_USB, true); |
| 216 | +
|
| 217 | + dev_info(data->dev, "My extcon driver probed successfully\n"); |
| 218 | + return 0; |
| 219 | + } |
| 220 | +
|
| 221 | + static int my_extcon_remove(struct platform_device *pdev) |
| 222 | + { |
| 223 | + struct my_extcon_data *data = platform_get_drvdata(pdev); |
| 224 | +
|
| 225 | + /* Example: Clear state before removal */ |
| 226 | + extcon_set_state_sync(data->edev, EXTCON_USB, false); |
| 227 | +
|
| 228 | + dev_info(data->dev, "My extcon driver removed\n"); |
| 229 | + return 0; |
| 230 | + } |
| 231 | +
|
| 232 | + static const struct of_device_id my_extcon_of_match[] = { |
| 233 | + { .compatible = "my,extcon-device", }, |
| 234 | + { }, |
| 235 | + }; |
| 236 | + MODULE_DEVICE_TABLE(of, my_extcon_of_match); |
| 237 | +
|
| 238 | + static struct platform_driver my_extcon_driver = { |
| 239 | + .driver = { |
| 240 | + .name = "my-extcon-driver", |
| 241 | + .of_match_table = my_extcon_of_match, |
| 242 | + }, |
| 243 | + .probe = my_extcon_probe, |
| 244 | + .remove = my_extcon_remove, |
| 245 | + }; |
| 246 | +
|
| 247 | + module_platform_driver(my_extcon_driver); |
| 248 | +
|
| 249 | +This example demonstrates: |
| 250 | +--------------------------- |
| 251 | + |
| 252 | +- Defining supported cable types (USB and USB Host in this case). |
| 253 | +- Allocating and registering an extcon device. |
| 254 | +- Setting an initial state for a cable (USB connected in this example). |
| 255 | +- Clearing the state when the driver is removed. |
0 commit comments