2. 遊戲埠驅動程式程式設計¶
2.1. 一個基本的經典遊戲埠¶
如果遊戲埠不提供超過 inb()/outb() 功能,則將它註冊到操縱桿驅動程式所需的程式碼很簡單
struct gameport gameport;
gameport.io = MY_IO_ADDRESS;
gameport_register_port(&gameport);
確保 struct gameport 中的所有其他欄位都初始化為 0。遊戲埠通用程式碼將處理其餘部分。
如果您的硬體支援多個 io 地址,並且您的驅動程式可以選擇要將硬體程式設計到哪個地址,那麼首選從更異構的地址開始,因為與標準 0x201 地址衝突的可能性較小。
例如,如果您的驅動程式支援地址 0x200、0x208、0x210 和 0x218,那麼 0x218 將是首選地址。
如果您的硬體支援未對映到 ISA io 空間(高於 0x1000)的遊戲埠地址,請使用該地址,並且不要對映 ISA 映象。
此外,始終對遊戲端口占用的整個 io 空間執行 request_region()。雖然只有一個 ioport 真正被使用,但遊戲埠通常佔用 io 空間中的一個到十六個地址。
還請考慮在 ->open() 回撥中啟用卡上的遊戲埠(如果 io 對映到 ISA 空間)- 這樣它只會在真正使用時才佔用 io 空間。在 ->close() 回撥中再次停用它。您還可以在 ->open() 回撥中選擇 io 地址,這樣如果某些可能的地址已經被其他遊戲端口占用,它就不會失敗。
2.2. 記憶體對映遊戲埠¶
當可以透過 MMIO 訪問遊戲埠時,首選這種方式,因為它更快,允許每秒進行更多讀取。註冊這樣的遊戲埠不像基本的 IO 那樣容易,但也不是很複雜
struct gameport gameport;
void my_trigger(struct gameport *gameport)
{
my_mmio = 0xff;
}
unsigned char my_read(struct gameport *gameport)
{
return my_mmio;
}
gameport.read = my_read;
gameport.trigger = my_trigger;
gameport_register_port(&gameport);
2.3. Cooked 模式遊戲埠¶
有一些遊戲埠可以將軸值報告為數字,這意味著驅動程式不必以舊方式測量它們 - ADC 內置於遊戲埠中。要註冊 cooked 遊戲埠
struct gameport gameport;
int my_cooked_read(struct gameport *gameport, int *axes, int *buttons)
{
int i;
for (i = 0; i < 4; i++)
axes[i] = my_mmio[i];
buttons[0] = my_mmio[4];
}
int my_open(struct gameport *gameport, int mode)
{
return -(mode != GAMEPORT_MODE_COOKED);
}
gameport.cooked_read = my_cooked_read;
gameport.open = my_open;
gameport.fuzz = 8;
gameport_register_port(&gameport);
這裡唯一令人困惑的是 fuzz 值。最好透過實驗確定,它是 ADC 資料中的噪聲量。完美的遊戲埠可以將此設定為零,最常見的 fuzz 在 8 到 32 之間。有關處理 fuzz 的資訊,請參見 analog.c 和 input.c - fuzz 值確定用於消除資料中噪聲的高斯濾波器視窗的大小。
2.4. 更復雜的遊戲埠¶
遊戲埠可以支援原始模式和 cooked 模式。在這種情況下,結合示例 1+2 或 1+3。遊戲埠可以支援內部校準 - 請參見下文,以及 lightning.c 和 analog.c 中有關其工作方式的資訊。如果您的驅動程式同時支援多個遊戲埠例項,請使用 gameport 結構的 ->private 成員指向您的資料。
2.5. 登出遊戲埠¶
簡單
gameport_unregister_port(&gameport);
2.6. 遊戲埠結構¶
struct gameport {
void *port_data;
一個私有指標,供遊戲埠驅動程式免費使用。(不是操縱桿驅動程式!)
char name[32];
驅動程式透過呼叫 gameport_set_name() 設定的驅動程式名稱。僅用於資訊目的。
char phys[32];
驅動程式透過呼叫 gameport_set_phys() 設定的遊戲埠的物理名稱/描述。僅用於資訊目的。
int io;
用於原始模式的 I/O 地址。如果您的遊戲埠支援原始模式,您必須設定此地址,或者將 ->read() 設定為某個值。
int speed;
每秒數千次的原始模式遊戲埠讀取速度。
int fuzz;
如果遊戲埠支援 cooked 模式,則應將其設定為表示資料中噪聲量的值。參見Cooked 模式遊戲埠。
void (*trigger)(struct gameport *);
觸發器。此函式應觸發 ns558 oneshots。如果設定為 NULL,則將使用 outb(0xff, io)。
unsigned char (*read)(struct gameport *);
讀取按鈕和 ns558 oneshot 位。如果設定為 NULL,則將改用 inb(io)。
int (*cooked_read)(struct gameport *, int *axes, int *buttons);
如果遊戲埠支援 cooked 模式,則應將其指向其 cooked 讀取函式。它應該用操縱桿軸的四個值填充 axes[0..3],並用四個位(代表按鈕)填充 buttons[0]。
int (*calibrate)(struct gameport *, int *axes, int *max);
用於校準 ADC 硬體的函式。呼叫時,呼叫者應使用 cooked 資料預填充 axes[0..3],max[0..3] 應預先填充每個軸的預期最大值。 calibrate() 函式應設定 ADC 硬體的靈敏度,以便最大值適合其範圍,並重新計算 axes[] 值以匹配新的靈敏度,或從硬體重新讀取它們,以便它們給出有效值。
int (*open)(struct gameport *, int mode);
Open() 有兩個目的。首先,驅動程式以原始模式或 cooked 模式開啟埠,open() 回撥可以決定支援哪些模式。其次,資源分配可以在這裡發生。埠也可以在這裡啟用。在此呼叫之前,遊戲埠結構的其他欄位(即 io 成員)不必有效。
void (*close)(struct gameport *);
Close() 應該釋放 open 分配的資源,可能會停用遊戲埠。
struct timer_list poll_timer;
unsigned int poll_interval; /* in msecs */
spinlock_t timer_lock;
unsigned int poll_cnt;
void (*poll_handler)(struct gameport *);
struct gameport *parent, *child;
struct gameport_driver *drv;
struct mutex drv_mutex; /* protects serio->drv so attributes can pin driver */
struct device dev;
struct list_head node;
供遊戲埠層內部使用。
};
祝你玩得開心!