1. 自定义设备私有数据: xxx_tty
//设备私有数据
struct xxx_tty
{
struct tty_struct *tty; //tty_struct 指针,描述一个tty端口
int open_count; //打开次数
struct semaphore sem; //结构体信号量锁
int xmit_bufl; //传输缓冲区
...
}
2. tty_struct ---描述一个tty端口(port)
定义如下:
struct tty_struct {
int magic;
struct kref kref;
struct device *dev;
struct tty_driver *driver;
const struct tty_operations *ops;
int index;
/* Protects ldisc changes: Lock tty not pty */
struct mutex ldisc_mutex;
struct tty_ldisc *ldisc; //给该tty设备使用的线路规程
struct mutex termios_mutex;
spinlock_t ctrl_lock;
/* Termios values are protected by the termios mutex */
struct ktermios *termios, *termios_locked; //tty设备的当前termios设置
struct termiox *termiox; /* May be NULL for unsupported */
char name[64];
struct pid *pgrp; /* Protected by ctrl lock */
struct pid *session;
unsigned long flags; //表示该tty设备当前的状态
int count;
struct winsize winsize; /* termios mutex */
unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
unsigned char low_latency:1, warned:1;
unsigned char ctrl_status; /* ctrl_lock */
unsigned int receive_room; /* Bytes free for queue */
struct tty_struct *link;
struct fasync_struct *fasync;
struct tty_bufhead buf; /* Locked internally */
int alt_speed; /* For magic substitution of 38400 bps */
wait_queue_head_t write_wait; //tty读/写函数等待队列,tty驱动在适当的时候唤醒对应的等待队列。
wait_queue_head_t read_wait;
struct work_struct hangup_work;
void *disc_data; //存储线路规程的私有数据
void *driver_data; //存储 tty 驱动的私有数据
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.
* Mostly locked by the BKL.
*/
unsigned int column;
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
unsigned char closing:1;
unsigned char echo_overrun: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))];
unsigned char *echo_buf;
unsigned int echo_pos;
unsigned int echo_cnt;
int canon_data;
unsigned long canon_head;
unsigned int canon_column;
struct mutex atomic_read_lock;
struct mutex atomic_write_lock;
struct mutex output_lock;
struct mutex echo_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;
struct tty_port *port;
};
--- flags :表示该 tty 设备的当前状态,可以有如下选项:
/*
* These bits are used in the flags field of the tty structure.
*
* So that interrupts won't be able to mess up the queues,
* copy_to_cooked must be atomic with respect to itself, as must
* tty->write. Thus, you must use the inline functions set_bit() and
* clear_bit() to make things atomic.
*/
#define TTY_THROTTLED 0 /* Call unthrottle() at threshold min */
#define TTY_IO_ERROR 1 /* Cause an I/O error (may be no ldisc too) */
#define TTY_OTHER_CLOSED 2 /* Other side (if any) has closed */
#define TTY_EXCLUSIVE 3 /* Exclusive open mode */
#define TTY_DEBUG 4 /* Debugging */
#define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */
#define TTY_PUSH 6 /* n_tty private */
#define TTY_CLOSING 7 /* ->close() in progress */
#define TTY_LDISC 9 /* Line discipline attached */
#define TTY_LDISC_CHANGING 10 /* Line discipline changing */
#define TTY_LDISC_OPEN 11 /* Line discipline is open */
#define TTY_HW_COOK_OUT 14 /* Hardware can do output cooking */
#define TTY_HW_COOK_IN 15 /* Hardware can do input cooking */
#define TTY_PTY_LOCK 16 /* pty private */
#define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */
#define TTY_HUPPED 18 /* Post driver->hangup() */
#define TTY_FLUSHING 19 /* Flushing to ldisc in progress */
#define TTY_FLUSHPENDING 20 /* Queued buffer flush pending */
#define TTY_HUPPING 21 /* ->hangup() in progress */
stopped:1 ---表示是否停止该 tty 设备, tty驱动可以设置该值。
hw_stopped:1 --- 指示该 tty 设备已经被停止,tty驱动可以设置该值。
flow_stopped:1 --- 表示tty设备数据流是否停止。
3. xxx_init(); --- tty 驱动的初始化函数:static int __init xxx_init(void)
{
...
//分配tty_driver结构体
xxx_tty_driver = alloc_tty_driver(XXX_PORTS);
//初始化tty_driver结构体
xxx_tty_driver->owner = THIS_MODULE;
xxx_tty_driver->devfs_name = "tts/";
xxx_tty_driver->name = "ttyS";
xxx_tty_driver->major = TTY_MAJOR;
xxx_tty_driver->minor_start = 64;
xxx_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
xxx_tty_driver->subtype = SERIAL_TYPE_NORMAL;
xxx_tty_driver->init_termios = tty_std_termios;
xxx_tty_driver->init_termios.c_cflags = B9600 |CS8 |CREAD |HUPCL |CLOCAL;
xxx_tty_driver->flags = TTY_DRIVER_REAL_RAW;
tty_set_operations(xxx_tty_driver, &xxx_ops);
ret = tty_register_driver(xxx_tty_driver);
if(ret) {
printk(KERN_ERR "Couldn't register xxx serial driver\n");
put_tty_driver(xxx_tty_driver);
return ret;
}
...
ret = request_irq(...); //申请硬件资源
...
}
4. tty_driver 中的 open()成员函数
当用户对tty驱动所分配的设备节点进行open()系统调用时,tty_driver中的open()成员函数将会被tty核心调用。
tty驱动必须设置open()成员函数,否则调用open()的用户会返回 -ENODEV.
open()成员函数的参数
--- *tty : 是指向分配给这个设备的tty_struct结构体的指针,
--- *file : 文件指针
//打开函数
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()成员函数,当用户进程close()系统调用时,tty_driver中的close()成员函数将被tty核心调用。
5. 数据的发送和接收
终端数据发送和接收过程中,数据流和函数调用关系,如下图所示:
5.1 数据发送
用户在发送数据给终端设备时,通过“write()系统调用 -> tty 核心 -> 线路规程” 层层调用,最终在tty_driver中的write()成员函数完成发送。
tty_driver的write()成员函数有3个参数:
--- tty_struct
--- 发送数据指针
--- 要发送的字节数
一般会先通过tty_struct 的 driver_data 成员得到设备私有信息结构体,然后进行必要的硬件操作,开始发送
xxx_write() 范例如下所示:
static int xxx_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
//获得tty设备私有数据
struct xxx_tty *xxx = (struct xxx_tty *)tty->driver_data;
...
//开始发送
while(1) {
local_irq_save(flags);
c = min_t(int, count, min(SERIAL_XMIT_SIZE - xxx->xmit_cnt -1,
SERIAL_XMIT_SIZE - xxx->xmit_head));
if (c <= 0) {
local_irq_restore(flags);
break;
}
//复制到发送缓冲区
memcpy(xxx->xmit_buf + xxx->xmit_head, buf, c);
xxx->xmit_head = (xxx->xmit_head + c) & (SERIAL_XMIT_SIZE - 1);
xxx->xmit_cnt += c;
local_irq_restore(flags);
buf += c;
count -= c;
total += c;
}
if (xxx->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
start_xmit(xxx); //开始发送
}
return total; //返回发送的字节数
}
5.2 数据接收
tty_driver 中没有提供 read() 函数。 因为发送(及用户调用write())是用户主动的,而接收(即用户调用read())是读一片缓冲区中已经放好的数据。
tty 核心在一个称为 struct tty_bufhead 的结构体中缓存数据,直到它被用户请求。
也就是 tty 核心已经提供了缓存逻辑,因此每个tty驱动不一定要实现自己的缓存逻辑。
tty_bufhead 定义如下:
struct tty_bufhead {
struct delayed_work work;
spinlock_t lock;
struct tty_buffer *head; /* Queue head */
struct tty_buffer *tail; /* Active buffer */
struct tty_buffer *free; /* Free queue head */
int memory_used; /* Buffer space used excluding
free queue */
};
tty_flip_buffer_push(tty_struct) --- 把缓冲区数据刷新到用户,
/**
* tty_flip_buffer_push - terminal
* @tty: tty to push
*
* Queue a push of the terminal flip buffers to the line discipline. This
* function must not be called from IRQ context if tty->low_latency is set.
*
* In the event of the queue being busy for flipping the work will be
* held off and retried later.
*
* Locking: tty buffer lock. Driver locks in low latency mode.
*/
void tty_flip_buffer_push(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;
spin_unlock_irqrestore(&tty->buf.lock, flags);
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work.work);
else
schedule_delayed_work(&tty->buf.work, 1);
}
EXPORT_SYMBOL(tty_flip_buffer_push);
tty_insert_flip_char() --- 将 tty 驱动接收到的字符插入到 flip 缓冲区。
static inline int tty_insert_flip_char(struct tty_struct *tty,
unsigned char ch, char flag)
{
struct tty_buffer *tb = tty->buf.tail;
if (tb && tb->used < tb->size) {
tb->flag_buf_ptr[tb->used] = flag;
tb->char_buf_ptr[tb->used++] = ch;
return 1;
}
return tty_insert_flip_string_flags(tty, &ch, &flag, 1);
}