Extcon 裝置子系統

概述

Extcon (外部聯結器) 子系統為 Linux 系統中管理外部聯結器提供了一個統一的框架。它允許驅動程式報告外部聯結器的狀態,併為使用者空間查詢和監控這些狀態提供標準化介面。

Extcon 在具有多種連線選項的現代裝置中特別有用,例如智慧手機、平板電腦和筆記型電腦。它有助於管理各種型別的聯結器,包括:

  1. USB 聯結器 (例如,USB-C, micro-USB)

  2. 充電埠 (例如,快速充電, 無線充電)

  3. 音訊插孔 (例如,3.5mm 耳機插孔)

  4. 影片輸出 (例如,HDMI, DisplayPort)

  5. 擴充套件塢

實際示例

  1. 智慧手機 USB-C 埠:智慧手機上的單個 USB-C 埠可以提供多種功能。Extcon 可以管理此埠的不同狀態,例如: - USB 資料連線 - 充電 (各種型別,如快速充電、USB 供電) - 音訊輸出 (USB-C 耳機) - 影片輸出 (USB-C 轉 HDMI 介面卡)

  2. 筆記型電腦擴充套件塢:當筆記型電腦連線到擴充套件塢時,會同時建立多個連線。Extcon 可以處理以下狀態變化: - 供電 - 外接顯示器 - USB 集線器連線 - 乙太網連線

  3. 無線充電板:Extcon 可以管理無線充電連線的狀態,允許系統在裝置放置到充電板上或從充電板上移開時做出適當的響應。

  4. 智慧電視 HDMI 埠:在智慧電視中,Extcon 可以管理多個 HDMI 埠,檢測裝置何時連線或斷開,並可能識別裝置型別 (例如,遊戲機、機頂盒、藍光播放器)。

Extcon 框架透過提供標準化方式來報告和查詢聯結器狀態、處理互斥連線以及管理聯結器屬性,簡化了這些複雜場景下驅動程式的開發。這使得現代裝置中外部連線的處理更加健壯和靈活。

關鍵元件

extcon_dev

表示 Extcon 裝置的核心結構

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;
};

關鍵欄位

  • name: Extcon 裝置的名稱

  • supported_cable: 支援的電纜型別陣列

  • mutually_exclusive: 定義互斥電纜型別的陣列。此欄位對於強制執行硬體約束至關重要。它是一個 32 位無符號整數陣列,其中每個元素代表一組互斥的電纜型別。陣列應以 0 終止。

    例如

    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 */
    };
    

    在此示例中,電纜 0 和 1 不能同時連線,電纜 2、3 和 4 也是互斥的。這對於單個埠既可以是 USB 也可以是 HDMI,但不能同時是兩者的場景非常有用。

    Extcon 核心使用此資訊來防止無效的電纜狀態組合,確保報告的狀態始終與硬體功能一致。

  • state: 裝置的當前狀態 (已連線電纜的點陣圖)

extcon_cable

表示 Extcon 裝置管理的單個電纜

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);
};

核心函式

int extcon_get_state(struct extcon_dev *edev, const unsigned int id)

獲取外部聯結器的狀態。

引數

struct extcon_dev *edev

extcon 裝置

const unsigned int id

指示外部聯結器的唯一 ID

描述

成功返回 0,失敗返回錯誤號。

int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool state)

設定外部聯結器的狀態。

引數

struct extcon_dev *edev

extcon 裝置

unsigned int id

指示外部聯結器的唯一 ID

bool state

外部聯結器的新狀態。預設語義為 true:已連線 / false:已分離。

描述

請注意,此函式在沒有通知的情況下設定外部聯結器的狀態。要同步外部聯結器的狀態,必須使用 extcon_set_state_sync() 和 extcon_sync()。

成功返回 0,失敗返回錯誤號。

int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state)

同步設定外部聯結器的狀態。

引數

struct extcon_dev *edev

extcon 裝置

unsigned int id

指示外部聯結器的唯一 ID

bool state

外部聯結器的新狀態。預設語義為 true:已連線 / false:已分離。

描述

請注意,此函式設定外部聯結器的狀態並透過傳送通知來同步狀態。

成功返回 0,失敗返回錯誤號。

int extcon_get_property(struct extcon_dev *edev, unsigned int id, unsigned int prop, union extcon_property_value *prop_val)

獲取外部聯結器的屬性值。

引數

struct extcon_dev *edev

extcon 裝置

unsigned int id

指示外部聯結器的唯一 ID

unsigned int prop

指示 extcon 屬性的屬性 ID

union extcon_property_value *prop_val

儲存 extcon 屬性值的指標

描述

請注意,獲取外部聯結器的屬性值時,外部聯結器應處於連線狀態。如果處於分離狀態,函式將返回 0 且無屬性值。此外,每個屬性應根據 extcon 型別包含在支援屬性列表中。

成功返回 0,失敗返回錯誤號。

Sysfs 介面

Extcon 裝置公開以下 sysfs 屬性

  • name: Extcon 裝置的名稱

  • state: 所有支援電纜的當前狀態

  • cable.N/name: 第 N 個受支援電纜的名稱

  • cable.N/state: 第 N 個受支援電纜的狀態

使用示例

#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);

此示例演示:

  • 定義支援的電纜型別(本例中為 USB 和 USB 主機)。

  • 分配和註冊 extcon 裝置。

  • 為電纜設定初始狀態(本例中為 USB 已連線)。

  • 移除驅動程式時清除狀態。