终端设备驱动程序设计范例

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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值