数据读取数据流:
读数据流分为两段,第一段为用户空间从环形缓冲read_buf中获取数据;第二段为
硬件设备获取数据写到环形缓冲read_buf中。
一:
(1) tty_io.c
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
{
int i;
struct tty_struct *tty;
struct inode *inode;
struct tty_ldisc *ld;
tty = (struct tty_struct *)file->private_data;
inode = file->f_path.dentry->d_inode;
if (tty_paranoia_check(tty, inode, "tty_read"))
return -EIO;
if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* We want to wait for the line discipline to sort out in this
situation */
ld = tty_ldisc_ref_wait(tty);//获取线路规程操作函数
if (ld->ops->read)
i = (ld->ops->read)(tty, file, buf, count);//将环形缓冲区read_buf的数据读取到用户空间
else
i = -EIO;
tty_ldisc_deref(ld);
if (i > 0)
inode->i_atime = current_fs_time(inode->i_sb);
return i;
}
(2) N_tty.c
函数n_tty_read即为(1)中调用函数ld->ops->read 。
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
unsigned char __user *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
int minimum, time;
ssize_t retval = 0;
ssize_t size;
long timeout;
unsigned long flags;
int packet;
do_it_again:
BUG_ON(!tty->read_buf);
c = job_control(tty, file);
if (c < 0)
return c;
minimum = time = 0;
timeout = MAX_SCHEDULE_TIMEOUT;
//
//函数n_tty_receive_buf将数据接收缓存链中接收到的数据读到tty->read_buf中,
//当tty->read_buf中的数据量达到tty->read_cnt >= tty->minimum_to_wake时
//调用函数kill_fasync(&tty->fasync, SIGIO, POLL_IN);向用户空间发出异步通知。
//wake_up_interruptible(&tty->read_wait);唤醒用户空间的读进程。
if (!tty->icanon) {
time = (HZ / 10) * TIME_CHAR(tty);
minimum = MIN_CHAR(tty);
if (minimum) {
if (time)
tty->minimum_to_wake = 1;
else if (!waitqueue_active(&tty->read_wait) ||
(tty->minimum_to_wake > minimum))
tty->minimum_to_wake = minimum;
} else {
timeout = 0;
if (time) {
timeout = time;
time = 0;
}
tty->minimum_to_wake = minimum = 1;
}
}
/*
* Internal serialization of reads.
*/
if (file->f_flags & O_NONBLOCK) {
if (!mutex_trylock(&tty->atomic_read_lock))
return -EAGAIN;
} else {
if (mutex_lock_interruptible(&tty->atomic_read_lock))
return -ERESTARTSYS;
}
packet = tty->packet;
add_wait_queue(&tty->read_wait, &wait);
while (nr) {
/* First test for status change. */
if (packet && tty->link->ctrl_status) {
unsigned char cs;
if (b != buf)
break;
spin_lock_irqsave(&tty->link->ctrl_lock, flags);
cs = tty->link->ctrl_status;
tty->link->ctrl_status = 0;
spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
if (tty_put_user(tty, cs, b++)) {
retval = -EFAULT;
b--;
break;
}
nr--;
break;
}
/* This statement must be first before checking for input
so that any interrupt will set the state back to
TASK_RUNNING. */
set_current_state(TASK_INTERRUPTIBLE);
if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
((minimum - (b - buf)) >= 1))
tty->minimum_to_wake = (minimum - (b - buf));
if (!input_available_p(tty, 0)) {
if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
retval = -EIO;
break;
}
if (tty_hung_up_p(file))
break;
if (!timeout)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
/* FIXME: does n_tty_set_room need locking ? */
n_tty_set_room(tty);
timeout = schedule_timeout(timeout);
continue;
}
__set_current_state(TASK_RUNNING);
/* Deal with packet mode. */
if (packet && b == buf) {
if (tty_put_user(tty, TIOCPKT_DATA, b++)) {
retval = -EFAULT;
b--;
break;
}
nr--;
}
if (tty->icanon) {//如果开启了tty->icanon则数据需要逐个获取
/* N.B. avoid overrun if nr == 0 */
while (nr && tty->read_cnt) {
int eol;
//eol为0表示对应数据是一个正确的数据。
eol = test_and_clear_bit(tty->read_tail,
tty->read_flags);
c = tty->read_buf[tty->read_tail];//从数据缓存中逐个数据读出
spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = ((tty->read_tail+1) &
(N_TTY_BUF_SIZE-1));//避免环形缓存区回绕
tty->read_cnt--;
if (eol) {
/* this test should be redundant:
* we shouldn't be reading data if
* canon_data is 0
*/
if (--tty->canon_data < 0)
tty->canon_data = 0;
}
spin_unlock_irqrestore(&tty->read_lock, flags);
if (!eol || (c != __DISABLED_CHAR)) {
if (tty_put_user(tty, c, b++)) {
retval = -EFAULT;
b--;
break;
}
nr--;
}
if (eol) {
tty_audit_push(tty);
break;
}
}
if (retval)
break;
} else {
int uncopied;
/* The copy function takes the read lock and handles
locking internally for this case */
/*
数据读取缓存read_buf(0~N_TTY_BUF_SIZE)为一环形缓冲区。tty->read_tail,
tty->read_tail指向第一个未被读取的数据, tty->read_cnt缓存中的数据,tty->read_head指向
第一个未被占用的空间。由于是环形缓存tty->read_cnt不一定等于tty->read_head - tty->read_tail。
tty->read_head可能小于tty->read_tail所以可能有以下关系:
tty->read_cnt = N_TTY_BUF_SIZE - tty->read_tail + tty->read_head。
所以将read_buf中的值考到用户空间需要考两次,*nr的值可能大于N_TTY_BUF_SIZE - tty->read_tail
而小于tty->read_cnt。拷数据时是从tty->read_tail开始,第一次考取N_TTY_BUF_SIZE - tty->read_tail,
第二次在read_buf的开始位置到tty->read_head之间获取还需的数据。
*/
uncopied = copy_from_read_buf(tty, &b, &nr);//将多个数据读到用户空间
uncopied += copy_from_read_buf(tty, &b, &nr);
if (uncopied) {
retval = -EFAULT;
break;
}
}
/* If there is enough space in the read buffer now, let the
* low-level driver know. We use n_tty_chars_in_buffer() to
* check the buffer, as it now knows about canonical mode.
* Otherwise, if the driver is throttled and the line is
* longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,
* we won't get any more characters.
*/
if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) {
n_tty_set_room(tty);
check_unthrottle(tty);
}
if (b - buf >= minimum)
break;
if (time)
timeout = time;
}
mutex_unlock(&tty->atomic_read_lock);
remove_wait_queue(&tty->read_wait, &wait);
if (!waitqueue_active(&tty->read_wait))
tty->minimum_to_wake = minimum;
__set_current_state(TASK_RUNNING);
size = b - buf;
if (size) {
retval = size;
if (nr)
clear_bit(TTY_PUSH, &tty->flags);
} else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
goto do_it_again;
n_tty_set_room(tty);//将环形缓冲区中的剩余空间量存于tty->receive_room。
return retval;
}
二:
(1)samsung.c
数据接收中断函数将接收到的数据通过函数uart_insert_char放到数据接收缓存链:
static irqreturn_t
s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
struct s3c24xx_uart_port *ourport = dev_id;
struct uart_port *port = &ourport->port;
struct tty_struct *tty = port->info->port.tty;
unsigned int ufcon, ch, flag, ufstat, uerstat;
int max_count = 64;//一次中断接收的最大数据量
while (max_count-- > 0) {
ufcon = rd_regl(port, S3C2410_UFCON);
ufstat = rd_regl(port, S3C2410_UFSTAT);
if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)//看接收fifo中是否有数据
break;
uerstat = rd_regl(port, S3C2410_UERSTAT);
ch = rd_regb(port, S3C2410_URXH);//读取一个字符
if (port->flags & UPF_CONS_FLOW) {//自动流控制
int txe = s3c24xx_serial_txempty_nofifo(port);
//如果发送器不为空则清零接收标志continue,下一次循环进入else如果发送器为空
//复位接收fifo后置接收标志然后goto out;
if (rx_enabled(port)) {
if (!txe) {
rx_enabled(port) = 0;
continue;
}
} else {
if (txe) {
ufcon |= S3C2410_UFCON_RESETRX;
wr_regl(port, S3C2410_UFCON, ufcon);
rx_enabled(port) = 1;
goto out;
}
continue;
}
}
/* insert the character into the buffer */
flag = TTY_NORMAL;//接收到的是正确的数据
port->icount.rx++;//接收数据个数加
if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {//判断是否有错误标志被置位,并做相应处理
dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
ch, uerstat);
/* check for break */
if (uerstat & S3C2410_UERSTAT_BREAK) {
dbg("break!\n");
port->icount.brk++;
if (uart_handle_break(port))
goto ignore_char;
}
if (uerstat & S3C2410_UERSTAT_FRAME)
port->icount.frame++;
if (uerstat & S3C2410_UERSTAT_OVERRUN)
port->icount.overrun++;
uerstat &= port->read_status_mask;
if (uerstat & S3C2410_UERSTAT_BREAK)
flag = TTY_BREAK;
else if (uerstat & S3C2410_UERSTAT_PARITY)
flag = TTY_PARITY;
else if (uerstat & (S3C2410_UERSTAT_FRAME |
S3C2410_UERSTAT_OVERRUN))
flag = TTY_FRAME;
}
if (uart_handle_sysrq_char(port, ch))
goto ignore_char;
/*
struct tty_buffer {
struct tty_buffer *next;//连接指针链表
char *char_buf_ptr;//指向数据缓存
unsigned char *flag_buf_ptr;//指向存放标志的缓存
int used;//该段缓存第一个未被占用的数据位置,该段缓存中数据被读取该值不减
int size;//该段数据缓存的大小
int commit;//该段缓存中最后一次写入时,数据缓存中的数据量,数据被读取时该值不减
int read;//指向第一个未被读取的数据,数据被读取其值加。commit - read表示缓存中的数据量
// Data points here
//缓存被分配时数据存放首地址放于此处,让char_buf_ptr指向这里,
//flag_buf_ptr指向data[0] + (size + 0xFF)处。
unsigned long data[0];
};
将接收到的标志flag放入tty->buf.tail->flag_buf_ptr[tb->used](flag表明数据是否正常在数据读取时查看),
将数据ch放入tty->buf.tail->char_buf_ptr[tb->used++].
接收数据存放的缓存链由结构体struct 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
};
head指向缓存链的头,在数据读取时从此开始;tail指向缓存链的尾,当数据写入时写到此处;
free指向已被读取数据的缓存。每次存入数据时放到tail指向的缓存,
如果该段缓存已用完将free指向的缓存链中的一段缓存添加到tail。如果
free指向的缓存链没有足够大的缓存或没有缓存,则分配二倍(size + 0xFF)(size为要存入数据的大小)
的缓存分别给tty->buf.tail->flag_buf_ptr和tty->buf.tail->char_buf_ptr,然后挂到tail。
*/
uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,
ch, flag);
ignore_char:
continue;
}
/*
将tail指向的数据缓存已被占用的值used赋给commit,tty->buf.tail->commit = tty->buf.tail->used;
调用函数flush_to_ldisc。
flush_to_ldisc:
如果head指向的数据缓存的数据已被完全读取,则将此缓存挂到free上。调用函数disc->ops->receive_buf
disc->ops->receive_buf:
将数据拷贝到读数据缓存器中tty->read_buf,在函数 ld->ops->read 拷贝到用户空间copy_from_read_buf(tty, &b, &nr);
*/
tty_flip_buffer_push(tty);
out:
return IRQ_HANDLED;
}
(2)tty_buffer.c
void tty_flip_buffer_push(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
//将tail指向的数据缓存已被占用的值used赋给commit
if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;
spin_unlock_irqrestore(&tty->buf.lock, flags);
// 如果head指向的数据缓存的数据已被完全读取,则将此缓存挂到free上。调用函数//disc->ops->receive_buf将接收数据缓存链的数据拷贝到环形缓冲tty->read_buf
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work.work);
else
schedule_delayed_work(&tty->buf.work, 1);
}
(3)tty_buffer.c
static void flush_to_ldisc(struct work_struct *work)
{
struct tty_struct *tty =
container_of(work, struct tty_struct, buf.work.work);
unsigned long flags;
struct tty_ldisc *disc;
struct tty_buffer *tbuf, *head;
char *char_buf;
unsigned char *flag_buf;
disc = tty_ldisc_ref(tty);//获取线路规程操作函数并增加引用计数
if (disc == NULL) /* !TTY_LDISC */
return;
spin_lock_irqsave(&tty->buf.lock, flags);
/* So we know a flush is running */
set_bit(TTY_FLUSHING, &tty->flags);
head = tty->buf.head;//获取接收数据缓存链的头
if (head != NULL) {
tty->buf.head = NULL;
for (;;) {
int count = head->commit - head->read;
if (!count) {//如果该段缓存中的数据已被完全读出,则将这段缓存挂到free上。
if (head->next == NULL)
break;
tbuf = head;
head = head->next;
tty_buffer_free(tty, tbuf);
continue;
}
/* Ldisc or user is trying to flush the buffers
we are feeding to the ldisc, stop feeding the
line discipline as we want to empty the queue */
if (test_bit(TTY_FLUSHPENDING, &tty->flags))
break;
if (!tty->receive_room) {
schedule_delayed_work(&tty->buf.work, 1);
break;
}
if (count > tty->receive_room)
count = tty->receive_room;
char_buf = head->char_buf_ptr + head->read;//确定数据读取的起始位置
flag_buf = head->flag_buf_ptr + head->read;//确定标志读取的起始位置
head->read += count;
spin_unlock_irqrestore(&tty->buf.lock, flags);
disc->ops->receive_buf(tty, char_buf,
flag_buf, count);//将数据拷贝到读数据缓存器中tty->read_buf,
spin_lock_irqsave(&tty->buf.lock, flags);
}
/* Restore the queue head */
tty->buf.head = head;
}
/* We may have a deferred request to flush the input buffer,
if so pull the chain under the lock and empty the queue */
if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {
__tty_buffer_flush(tty);
clear_bit(TTY_FLUSHPENDING, &tty->flags);
wake_up(&tty->read_wait);
}
clear_bit(TTY_FLUSHING, &tty->flags);
spin_unlock_irqrestore(&tty->buf.lock, flags);
tty_ldisc_deref(disc);//减少线路规程操作函数的引用计数
}
(4)N_tty.c
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
const unsigned char *p;
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
unsigned long cpuflags;
if (!tty->read_buf)//如果数据读取缓存为空则返回
return;
if (tty->real_raw) {//批量读取
spin_lock_irqsave(&tty->read_lock, cpuflags);
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
//将数据接收缓存链中接收到的数据拷贝到环形数据接收缓存。
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
cp += i;
count -= i;
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
spin_unlock_irqrestore(&tty->read_lock, cpuflags);
} else {
//逐个数据读取并判断每个数据对应的标志位
for (i = count, p = cp, f = fp; i; i--, p++) {
if (f)
flags = *f++;
switch (flags) {
case TTY_NORMAL:
n_tty_receive_char(tty, *p);
break;
case TTY_BREAK:
n_tty_receive_break(tty);
break;
case TTY_PARITY:
case TTY_FRAME:
n_tty_receive_parity_error(tty, *p);
break;
case TTY_OVERRUN:
n_tty_receive_overrun(tty);
break;
default:
printk(KERN_ERR "%s: unknown flag %d\n",
tty_name(tty, buf), flags);
break;
}
}
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
//将环形数据缓存tty->read_cnt还剩余的空间量存到tty->receive_room
n_tty_set_room(tty);
//如果tty->icanon关闭,即数据批量读取到用户空间且接收到的数据量达到设定阀值,
//则发出异步通知并唤醒读进程。
if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
}
/*
* Check the remaining room for the input canonicalization
* mode. We don't want to throttle the driver if we're in
* canonical mode and don't have a newline yet!
*/
if (tty->receive_room < TTY_THRESHOLD_THROTTLE)
tty_throttle(tty);//如果唤醒缓冲剩余空间达到阀值,则向设备发出通知,停止数据发送。
}
数据写入数据流:
(1)tty_io.c
static ssize_t tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct tty_struct *tty;
struct inode *inode = file->f_path.dentry->d_inode;
ssize_t ret;
struct tty_ldisc *ld;
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode, "tty_write"))
return -EIO;
if (!tty || !tty->ops->write ||
(test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* Short term debug to catch buggy drivers */
if (tty->ops->write_room == NULL)
printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
tty->driver->name);
ld = tty_ldisc_ref_wait(tty);//获取线路规程操作函数结构体
if (!ld->ops->write)
ret = -EIO;
else
//将要写入的数据考到写入缓存,调用线路规程写函数ld->ops->write,在该函数中 再调用函数
//tty->ops->write将写入缓存中的数据考到uart_info.xmit中,再经数据发送中断处理函数写入发送器。
ret = do_tty_write(ld->ops->write, tty, file, buf, count);
tty_ldisc_deref(ld);
return ret;
}
(2)tty_io.c
static inline ssize_t do_tty_write(
ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
struct tty_struct *tty,
struct file *file,
const char __user *buf,
size_t count)
{
ssize_t ret, written = 0;
unsigned int chunk;
ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
if (ret < 0)
return ret;
/*
* We chunk up writes into a temporary buffer. This
* simplifies low-level drivers immensely, since they
* don't have locking issues and user mode accesses.
*
* But if TTY_NO_WRITE_SPLIT is set, we should use a
* big chunk-size..
*
* The default chunk-size is 2kB, because the NTTY
* layer has problems with bigger chunks. It will
* claim to be able to handle more characters than
* it actually does.
*
* FIXME: This can probably go away now except that 64K chunks
* are too likely to fail unless switched to vmalloc...
*/
chunk = 2048;
if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
chunk = 65536;
if (count < chunk)
chunk = count;
/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
if (tty->write_cnt < chunk) {//如果数据写入缓存空间大小小于要写入数据,则重新分配内存并释放原有内存
unsigned char *buf_chunk;
if (chunk < 1024)
chunk = 1024;
buf_chunk = kmalloc(chunk, GFP_KERNEL);
if (!buf_chunk) {
ret = -ENOMEM;
goto out;
}
kfree(tty->write_buf);
tty->write_cnt = chunk;
tty->write_buf = buf_chunk;
}
/* Do the write .. */
for (;;) {
size_t size = count;
if (size > chunk)
size = chunk;
ret = -EFAULT;
if (copy_from_user(tty->write_buf, buf, size))//将要写入的数据考到写入缓存
break;
//调用线路规程写函数ld->ops->write,在该函数中 再调用函数tty->ops->write将写入缓存中的数据
//考到uart_info.xmit中,再经数据发送中断处理函数写入发送器。
ret = write(tty, file, tty->write_buf, size);
if (ret <= 0)
break;
written += ret;
buf += ret;
count -= ret;
if (!count)
break;
ret = -ERESTARTSYS;
if (signal_pending(current))
break;
cond_resched();
}
if (written) {
struct inode *inode = file->f_path.dentry->d_inode;
inode->i_mtime = current_fs_time(inode->i_sb);
ret = written;
}
out:
tty_write_unlock(tty);
return ret;
}
(3)samsung.c
//数据发送中断处理函数。
static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
struct circ_buf *xmit = &port->info->xmit;
int count = 256;
//当存放接收数据的缓存满或空时xof/fxon,通知设备不要发送更多数据了,或通知设备开始发送数据
if (port->x_char) {//发送特殊字符xon/xoff
wr_regb(port, S3C2410_UTXH, port->x_char);
port->icount.tx++;//数据发送计数加
port->x_char = 0;
goto out;
}
/* if there isnt anything more to transmit, or the uart is now
* stopped, disable the uart and exit
*/
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
s3c24xx_serial_stop_tx(port);//如果存放发送数据的缓存为空
goto out;
}
/* try and drain the buffer... */
while (!uart_circ_empty(xmit) && count-- > 0) {//发送完指定的数据量
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
break;
wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);//如果数据缓存中的数据量小于设定值则唤醒写进程
if (uart_circ_empty(xmit))//如果数据缓存中的数据已被全部发送,则停止
s3c24xx_serial_stop_tx(port);
out:
return IRQ_HANDLED;
}