4. 遙控器裝置

4.1. 遙控器核心

遙控器核心實現了接收和傳送遙控器鍵盤按鍵和滑鼠事件的基礎設施。

每次按下遙控器上的按鍵時,都會產生一個掃描碼。此外,在大多數硬體上,持續按住一個按鍵超過幾十毫秒會產生一個重複按鍵事件。這有點類似於 Linux 內部處理普通鍵盤或滑鼠的方式[1]。因此,遙控器核心是在 Linux input/evdev 介面之上實現的。

然而,大多數遙控器使用紅外 (IR) 來傳輸訊號。由於有幾種協議用於調製紅外訊號,核心的一個重要部分專門用於調整驅動程式和核心繫統,以支援發射器使用的紅外協議。

紅外傳輸是透過使用載波閃爍紅外發射器來完成的。載波可以透過 IR 發射器硬體開啟或關閉。當載波開啟時,稱為脈衝。當載波關閉時,稱為空間

換句話說,典型的紅外傳輸可以看作是一系列脈衝空間事件,每個事件都有給定的持續時間。

載波引數(頻率、佔空比)以及脈衝空間事件的間隔取決於協議。例如,NEC 協議使用 38kHz 的載波,傳輸以 9ms 脈衝和 4.5ms 空間開始。然後它傳輸 16 位掃描碼,其中 8 位用於地址(通常對於給定的遙控器來說是一個固定的數字),後跟 8 位程式碼。“1”位用 560µs 脈衝後跟 1690µs 空間調製,“0”位用 560µs 脈衝後跟 560µs 空間調製。

在接收器端,可以使用簡單的低通濾波器將接收到的訊號轉換為一系列脈衝/空間事件,從而濾除載波頻率。因此,接收器並不關心載波的實際頻率引數:它所要做的就是測量它接收到脈衝/空間事件的時間量。因此,一個簡單的 IR 接收器硬體將只向核心提供這些事件的時序序列。具有此類接收器的硬體的驅動程式由 RC_DRIVER_IR_RAW 標識,如 rc_driver_type[2] 中定義的那樣。其他硬體配備了一個微控制器,可以解碼脈衝/空間序列並將掃描碼返回給核心。這種接收器由 RC_DRIVER_SCANCODE 標識。

當 RC 核心接收到由 RC_DRIVER_IR_RAW IR 接收器產生的事件時,它需要解碼 IR 協議,以便獲得相應的掃描碼。RC 核心支援的協議在列舉 rc_proto 中定義。

當 RC 程式碼接收到掃描碼時(無論是直接透過 RC_DRIVER_SCANCODE 型別的驅動程式,還是透過其 IR 解碼器),它需要將其轉換為 Linux 輸入事件程式碼。這是透過對映表完成的。

核心支援大多數媒體裝置上可用的對映表。它還支援透過一些 sysfs 節點在執行時載入表。有關更多詳細資訊,請參見 RC 使用者空間 API

4.1.1. 遙控器資料結構和函式

enum rc_driver_type

RC 驅動程式的型別。

常量

RC_DRIVER_SCANCODE

驅動程式或硬體生成掃描碼。

RC_DRIVER_IR_RAW

驅動程式或硬體生成脈衝/空間序列。它需要一個紅外脈衝/空間解碼器

RC_DRIVER_IR_RAW_TX

僅裝置發射器,驅動程式需要脈衝/空間資料序列。

struct rc_scancode_filter

過濾掃描碼。

定義:

struct rc_scancode_filter {
    u32 data;
    u32 mask;
};

成員

data

要匹配的掃描碼資料。

mask

要比較的掃描碼位的掩碼。

enum rc_filter_type

過濾器型別常量。

常量

RC_FILTER_NORMAL

用於正常操作的過濾器。

RC_FILTER_WAKEUP

用於從掛起狀態喚醒的過濾器。

RC_FILTER_MAX

過濾器型別的數量。

struct lirc_fh

表示一個開啟的 lirc 檔案

定義:

struct lirc_fh {
    struct list_head list;
    struct rc_dev *rc;
    unsigned int *rawir;
    struct lirc_scancode *scancodes;
    wait_queue_head_t wait_poll;
    u32 carrier_low;
    u8 send_mode;
    u8 rec_mode;
};

成員

list

開啟的檔案控制代碼列表

rc

此 lirc 字元裝置的 rcdev

rawir

傳入原始 IR 的佇列

scancodes

傳入解碼的掃描碼的佇列

wait_poll

lirc 裝置的輪詢結構

carrier_low

設定載波範圍時,必須先使用 ioctl 設定低端,然後使用另一個 ioctl 設定高階

send_mode

傳送的 lirc 模式,可以是 LIRC_MODE_SCANCODE 或 LIRC_MODE_PULSE

rec_mode

接收的 lirc 模式,可以是 LIRC_MODE_SCANCODE 或 LIRC_MODE_MODE2

struct rc_dev

表示遙控裝置

定義:

struct rc_dev {
    struct device                   dev;
    bool managed_alloc;
    bool registered;
    bool idle;
    bool encode_wakeup;
    unsigned int                    minor;
    const struct attribute_group    *sysfs_groups[5];
    const char                      *device_name;
    const char                      *input_phys;
    struct input_id                 input_id;
    const char                      *driver_name;
    const char                      *map_name;
    struct rc_map                   rc_map;
    struct mutex                    lock;
    struct ir_raw_event_ctrl        *raw;
    struct input_dev                *input_dev;
    enum rc_driver_type             driver_type;
    u32 users;
    u64 allowed_protocols;
    u64 enabled_protocols;
    u64 allowed_wakeup_protocols;
    enum rc_proto                   wakeup_protocol;
    struct rc_scancode_filter       scancode_filter;
    struct rc_scancode_filter       scancode_wakeup_filter;
    u32 scancode_mask;
    void *priv;
    spinlock_t keylock;
    bool keypressed;
    u8 last_toggle;
    u32 last_keycode;
    enum rc_proto                   last_protocol;
    u64 last_scancode;
    unsigned long                   keyup_jiffies;
    struct timer_list               timer_keyup;
    struct timer_list               timer_repeat;
    u32 timeout;
    u32 min_timeout;
    u32 max_timeout;
    u32 rx_resolution;
#ifdef CONFIG_LIRC;
    struct device                   lirc_dev;
    struct cdev                     lirc_cdev;
    ktime_t gap_start;
    spinlock_t lirc_fh_lock;
    struct list_head                lirc_fh;
#endif;
    int (*change_protocol)(struct rc_dev *dev, u64 *rc_proto);
    int (*open)(struct rc_dev *dev);
    void (*close)(struct rc_dev *dev);
    int (*s_tx_mask)(struct rc_dev *dev, u32 mask);
    int (*s_tx_carrier)(struct rc_dev *dev, u32 carrier);
    int (*s_tx_duty_cycle)(struct rc_dev *dev, u32 duty_cycle);
    int (*s_rx_carrier_range)(struct rc_dev *dev, u32 min, u32 max);
    int (*tx_ir)(struct rc_dev *dev, unsigned *txbuf, unsigned n);
    void (*s_idle)(struct rc_dev *dev, bool enable);
    int (*s_wideband_receiver)(struct rc_dev *dev, int enable);
    int (*s_carrier_report) (struct rc_dev *dev, int enable);
    int (*s_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter);
    int (*s_wakeup_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter);
    int (*s_timeout)(struct rc_dev *dev, unsigned int timeout);
};

成員

dev

驅動程式模型對此裝置的看法

managed_alloc

使用 devm_rc_allocate_device 建立 rc_dev

registered

rc_register_device() 設定為 true,由 rc_unregister_device 設定為 false

idle

用於跟蹤 RX 狀態

encode_wakeup

喚醒過濾使用 IR 編碼 API,因此允許的喚醒協議是所有原始編碼器的集合

minor

唯一的次要遙控裝置號

sysfs_groups

sysfs 屬性組

device_name

rc 子裝置的名稱

input_phys

輸入子裝置的物理路徑

input_id

輸入子裝置的 ID(結構 input_id)

driver_name

註冊此裝置的硬體驅動程式的名稱

map_name

預設鍵對映的名稱

rc_map

當前掃描/按鍵表

lock

用於確保我們在任何人呼叫 show_protocols 或 store_protocols 之前填寫了所有協議詳細資訊

raw

原始脈衝/空間裝置的附加資料

input_dev

用於將事件傳達給使用者空間的輸入子裝置

driver_type

指定協議解碼是在硬體還是軟體中完成

users

裝置的當前使用者數

allowed_protocols

帶有支援的 RC_PROTO_BIT_* 協議的位掩碼

enabled_protocols

帶有啟用的 RC_PROTO_BIT_* 協議的位掩碼

allowed_wakeup_protocols

帶有支援的 RC_PROTO_BIT_* 喚醒協議的位掩碼

wakeup_protocol

啟用的 RC_PROTO_* 喚醒協議,如果停用,則為 RC_PROTO_UNKNOWN。

scancode_filter

掃描碼過濾器

scancode_wakeup_filter

掃描碼喚醒過濾器

scancode_mask

某些硬體解碼器無法嚮應用程式提供完整的掃描碼。由於這是一個硬體限制,我們對此無能為力。然而,由於相同的按鍵程式碼表可以與其他裝置一起使用,因此提供了一個掩碼以允許其使用。驅動程式通常應將此欄位留空

priv

驅動程式特定資料

keylock

保護結構的其餘成員

keypressed

當前是否按下某個按鍵

last_toggle

最後一個命令的切換值

last_keycode

上次按鍵的按鍵程式碼

last_protocol

上次按鍵的協議

last_scancode

上次按鍵的掃描碼

keyup_jiffies

應釋放當前按鍵的時間(以 jiffies 為單位)

timer_keyup

用於釋放按鍵的計時器

timer_repeat

用於自動重複事件的計時器。這是 CEC 所必需的,CEC 具有非標準重複。

timeout

裝置停止傳送資料後的可選時間

min_timeout

裝置支援的最小超時

max_timeout

裝置支援的最大超時

rx_resolution

輸入取樣器的解析度(以微秒為單位)

lirc_dev

lirc 裝置

lirc_cdev

lirc 字元 cdev

gap_start

如果非零,則超時後間隙的開始時間

lirc_fh_lock

保護 lirc_fh 列表

lirc_fh

開啟的檔案列表

change_protocol

允許更改硬體解碼器上使用的協議

open

允許驅動程式在開啟 IR 輸入裝置時啟用輪詢/irq 的回撥。

close

允許驅動程式在開啟 IR 輸入裝置時停用輪詢/irq 的回撥。

s_tx_mask

設定發射器掩碼(對於具有多個 tx 輸出的裝置)

s_tx_carrier

設定發射載波頻率

s_tx_duty_cycle

設定發射佔空比 (0% - 100%)

s_rx_carrier_range

通知驅動程式它應該處理的載波

tx_ir

傳輸紅外

s_idle

啟用/停用硬體空閒模式,在此模式下,裝置不會中斷主機,直到它看到 IR 脈衝

s_wideband_receiver

啟用用於學習的寬頻接收器

s_carrier_report

啟用載波報告

s_filter

設定掃描碼過濾器

s_wakeup_filter

設定喚醒掃描碼過濾器。如果掩碼為零,則應停用喚醒。如果掩碼非零,則 wakeup_protocol 將設定為有效協議。

s_timeout

以微秒為單位設定硬體超時

struct rc_dev *rc_allocate_device(enum rc_driver_type)

分配一個 RC 裝置

引數

enum rc_driver_type

指定要分配的 RC 輸出型別,返回指向 struct rc_dev 的指標。

struct rc_dev *devm_rc_allocate_device(struct device *dev, enum rc_driver_type)

託管 RC 裝置分配

引數

struct device *dev

指向 struct device 的指標

enum rc_driver_type

指定要分配的 RC 輸出型別,返回指向 struct rc_dev 的指標。

void rc_free_device(struct rc_dev *dev)

釋放一個 RC 裝置

引數

struct rc_dev *dev

指向 struct rc_dev 的指標。

int rc_register_device(struct rc_dev *dev)

註冊一個 RC 裝置

引數

struct rc_dev *dev

指向 struct rc_dev 的指標。

int devm_rc_register_device(struct device *parent, struct rc_dev *dev)

管理 RC 裝置的註冊

引數

struct device *parent

指向 struct device 的指標。

struct rc_dev *dev

指向 struct rc_dev 的指標。

void rc_unregister_device(struct rc_dev *dev)

登出一個 RC 裝置

引數

struct rc_dev *dev

指向 struct rc_dev 的指標。

struct rc_map_table

表示掃描碼/按鍵程式碼對

定義:

struct rc_map_table {
    u64 scancode;
    u32 keycode;
};

成員

scancode

掃描碼 (u64)

keycode

Linux 輸入按鍵程式碼

struct rc_map

表示按鍵程式碼對映表

定義:

struct rc_map {
    struct rc_map_table     *scan;
    unsigned int            size;
    unsigned int            len;
    unsigned int            alloc;
    enum rc_proto           rc_proto;
    const char              *name;
    spinlock_t lock;
};

成員

scan

指向結構 rc_map_table 的指標

size

最大條目數

len

正在使用的條目數

alloc

*scan 的大小(以位元組為單位)

rc_proto

遙控器協議的型別,如列舉 rc_proto 中定義

name

按鍵對映表的名稱

lock

用於保護對此結構的訪問的鎖

struct rc_map_list

已註冊的 rc_map 對映的列表

定義:

struct rc_map_list {
    struct list_head         list;
    struct rc_map map;
};

成員

list

指向結構 list_head 的指標

map

指向結構 rc_map 的指標

int rc_map_register(struct rc_map_list *map)

註冊一個遙控器掃描碼對映

引數

struct rc_map_list *map

指向 struct rc_map_list 的指標

void rc_map_unregister(struct rc_map_list *map)

登出一個遙控器掃描碼對映

引數

struct rc_map_list *map

指向 struct rc_map_list 的指標

struct rc_map *rc_map_get(const char *name)

從其名稱獲取一個 RC 對映

引數

const char *name

RC 掃描碼對映的名稱