14.3 终端设备驱动初始化与释放
14.3.1 模块加载与卸载函数
tty 驱动的模块加载函数中通常需要分配、初始化 tty_driver 结构体并申请必要的硬件资源,如代码清单 14.4。tty 驱动的模块卸载函数完成与模块加载函数相反的工作。
代码清单 14.4 终端设备驱动模块加载函数
/* tty 驱动模块加载函数 */
static int __init tty3270_init(void)
{
struct tty_driver *driver;
int ret;
driver = alloc_tty_driver(RAW3270_MAXDEVS);/* 分配 tty_driver 结构体 */
if (!driver)
return -ENOMEM;
/* 初始化 tty_driver 结构体 */
driver->owner = THIS_MODULE;
driver->driver_name = "ttyTUB";
driver->name = "ttyTUB";
driver->major = IBM_TTY3270_MAJOR;
driver->minor_start = RAW3270_FIRSTMINOR;
driver->type = TTY_DRIVER_TYPE_SYSTEM;
driver->subtype = SYSTEM_TYPE_TTY;
driver->init_termios = tty_std_termios;
driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_DYNAMIC_DEV;
tty_set_operations(driver, &tty3270_ops);
ret = tty_register_driver(driver);
if (ret) {
printk(KERN_ERR "tty3270 registration failed with %d\n", ret);
put_tty_driver(driver);
return ret;
}
tty3270_driver = driver;
ret = raw3270_register_notifier(tty3270_notifier);
if (ret) {
printk(KERN_ERR "tty3270 notifier registration failed " "with %d\n", ret);
put_tty_driver(driver);
return ret;
}
.....................................................
ret = request_irq(...); /* 硬件资源申请 */
.....................................................
}
14.3.2 打开与关闭函数
当用户对 tty 驱动所分配的设备节点进行 open()系统调用时,tty_driver 所拥有的 tty_operations中的 open()成员函数将被 tty 核心调用。tty 驱动必须设置 open()成员,否则,-ENODEV 将被返回给调用 open()的用户。
open()成员函数的第 1 个参数为一个指向分配给这个设备的 tty_struct 结构体的指针,第 2 个参数为文件指针。
tty_struct 结构体被 tty 核心用来保存当前 tty 端口的状态,大多数成员只被 tty 核心使用。 tty_struct 结构体的定义如下:
include/linux/tty.h
struct tty_struct {
int magic;
struct tty_driver *driver;
int index;
struct tty_ldisc ldisc;
struct mutex termios_mutex;
struct ktermios *termios, *termios_locked;
char name[64];
struct pid *pgrp;
struct pid *session;
unsigned long flags;
int count;
struct winsize winsize;
unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
unsigned char low_latency:1, warned:1;
unsigned char ctrl_status;
unsigned int receive_room; /* Bytes free for queue */
struct tty_struct *link;
struct fasync_struct *fasync;
struct tty_bufhead buf;
int alt_speed; /* For magic substitution of 38400 bps */
wait_queue_head_t write_wait;
wait_queue_head_t read_wait;
struct work_struct hangup_work;
void *disc_data;
void *driver_data;
struct list_head tty_files;
#define N_TTY_BUF_SIZE 4096
/*
* The following is data for the N_TTY line discipline. For
* historical reasons, this is included in the tty structure.
*/
unsigned int column;
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
unsigned char closing:1;
unsigned short minimum_to_wake;
unsigned long overrun_time;
int num_overrun;
unsigned long process_char_map[256/(8*sizeof(unsigned long))];
char *read_buf;
int read_head;
int read_tail;
int read_cnt;
unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
int canon_data;
unsigned long canon_head;
unsigned int canon_column;
struct mutex atomic_read_lock;
struct mutex atomic_write_lock;
unsigned char *write_buf;
int write_cnt;
spinlock_t read_lock;
/* If the tty has a pending do_SAK, queue it here - akpm */
struct work_struct SAK_work;
};
分析:
(1)flags 标示tty 设备的当前状态
(2)ldisc 为给 tty 设备的线路规程
(3)write_wait、read_wait 为给 tty 写/读函数的等待队列,tty 驱动应在合适的时机唤醒对应的等待队列。
(4)termios 为指向 tty 设备的当前 termios 设置的指针
(5)stopped:1 指示是否停止 tty 设备,tty 驱动可以设置这个值;hw_stopped:1 指示是否 tty设备已经被停止,tty 驱动可以设置这个值;flow_stopped:1 指示是否 tty 设备数据流停止。
(6)driver_data、disc_data 为数据指针,用于存储 tty 驱动和线路规程的“私有”数据。
驱动中可以定义一个设备相关的结构体,并在open()函数中将其赋值给tty_struct的driver_data成员,如代码清单 14.5。
代码清单 14.5 在 tty 驱动打开函数中赋值tty_struct 的 driver_data 成员
/* 设备“私有”数据结构体 */
struct xxx_tty {
struct tty_struct *tty; /* tty_struct 指针 */
int open_count; /* 打开次数 */
struct semaphore sem; /* 结构体锁定信号量 */
intxmit_buf; /* 传输缓冲区 */
...
}
/* 打开函数 */
static int xxx_open(struct tty_struct *tty, struct file *file)
{
struct xxx_tty *xxx;
/* 分配 xxx_tty 内存*/
xxx = kmalloc(sizeof(*xxx), GFP_KERNEL);
if (!xxx)
return - ENOMEM;
/* 初始化 xxx_tty 中的成员 */
init_MUTEX(&xxx->sem);
xxx->open_count = 0;
...
/* 让 tty_struct 中的 driver_data 指向 xxx_tty */
tty->driver_data = xxx;
xxx->tty = tty;
...
return 0;
}
在用户对前面使用 open()系统调用而创建的文件句柄进行 close()系统调用时,tty_driver 所拥有的 tty_operations 中的 close()成员函数将被 tty 核心调用。