按照数据流过程对函数进行分析。
在linux中,串口字符的发送是通过中断来驱动的。
终端设备层的文件:linux-2.6.22.6\drivers\char\tty_io.c
/**
* tty_write - write method for tty device file
* @file: tty file pointer
* @buf: user data to write
* @count: bytes to write
* @ppos: unused
*
* Write data to a tty device via the line discipline.
*
* Locking:
* Locks the line discipline as required
* Writes to the tty driver are serialized by the atomic_write_lock
* and are then processed in chunks to the device. The line discipline
* write method will not be involked in parallel for each device
* The line discipline write method is called under the big
* kernel lock for historical reasons. New code should not rely on this.
*/
static ssize_t tty_write(struct file * file, const char __user * buf, size_t count,
loff_t *ppos)/*终端设备层*/
{/*类似于字符设备的write函数Write data to a tty device via the line discipline*/
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->driver->write || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
ld = tty_ldisc_ref_wait(tty);
if (!ld->write)
ret = -EIO;
else
ret = do_tty_write(ld->write, tty, file, buf, count);/*具体执行写操作*/
tty_ldisc_deref(ld);
return ret;
}
ret = do_tty_write(ld->write, tty, file, buf, count);/*具体执行写操作*/
终端设备层的文件:linux-2.6.22.6\drivers\char\tty_io.c
/*
* Split writes up in sane blocksizes to avoid
* denial-of-service type attacks
*/
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)/*终端设备层*/
{/*具体执行函数tty_write的写操作*/
ssize_t ret = 0, written = 0;
unsigned int chunk;
/* FIXME: O_NDELAY ... */
if (mutex_lock_interruptible(&tty->atomic_write_lock)) {
return -ERESTARTSYS;
}
/*
* 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;
if (chunk < 1024)
chunk = 1024;
buf = kmalloc(chunk, GFP_KERNEL);
if (!buf) {
mutex_unlock(&tty->atomic_write_lock);
return -ENOMEM;
}
kfree(tty->write_buf);
tty->write_cnt = chunk;
tty->write_buf = buf;
}
/*以上代码是对chunk,buf,tty->write_cnt = chunk,tty->write_buf = buf等进行调整(如果不在某个范围内的话)*/
/* 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;
lock_kernel();
ret = write(tty, file, tty->write_buf, size);/*把数据写到tty设备*/
unlock_kernel();
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;
}
mutex_unlock(&tty->atomic_write_lock);
return ret;
}
ret = write(tty, file, tty->write_buf, size)即
static ssize_t write_chan(struct tty_struct * tty, struct file * file,
const unsigned char * buf, size_t nr)/*行规层*/
行规层的文件:linux-2.6.22.6\drivers\char\n_tty.c
/**
* write_chan - write function for tty
* @tty: tty device
* @file: file object
* @buf: userspace buffer pointer
* @nr: size of I/O
*
* Write function of the terminal device. This is serialized with
* respect to other write callers but not to termios changes, reads
* and other such events. We must be careful with N_TTY as the receive
* code will echo characters, thus calling driver write methods.
*
* This code must be sure never to sleep through a hangup.
*/
static ssize_t write_chan(struct tty_struct * tty, struct file * file,
const unsigned char * buf, size_t nr)/*行规层*/
{/*根据不同的tty进行不同的操作*/
const unsigned char *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
ssize_t retval = 0;
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {/*L_TOSTOP(tty)判断标志位是否是TOSTOP*/
retval = tty_check_change(tty);
if (retval)
return retval;
}
add_wait_queue(&tty->write_wait, &wait);/*把当前进程加入等待队列*/
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {/*tty_hung_up_p(file)作用:Return true if the tty has been subject to a vhangup or a carrier loss*/
retval = -EIO;
break;
}
if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {/*O_OPOST(tty)判断标志位是否是OPOST*/
while (nr > 0) {
ssize_t num = opost_block(tty, b, nr);
if (num < 0) {
if (num == -EAGAIN)
break;
retval = num;
goto break_out;
}
b += num;
nr -= num;
if (nr == 0)
break;
c = *b;
if (opost(c, tty) < 0)
break;
b++; nr--;
}
if (tty->driver->flush_chars)
tty->driver->flush_chars(tty);
} else {
while (nr > 0) {
c = tty->driver->write(tty, b, nr);/*如果tty没有标志位TOSTOP和OPOST也没被挂起则调用,即uart_write串口抽象层函数*/
if (c < 0) {
retval = c;
goto break_out;
}
if (!c)
break;
b += c;
nr -= c;
}
}
if (!nr)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
schedule();/*如果uart_write(即c = tty->driver->write(tty, b, nr);)没有立刻发送完数据,进程休眠*/
}
break_out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty->write_wait, &wait);
return (b - buf) ? b - buf : retval;
}
c = tty->driver->write(tty, b, nr)即
串口抽象层函数uart_write
串口抽象层文件:linux-2.6.22.6\drivers\serial\
serial_core.c
static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count)/*串口抽象层*/
{/*数据先被保存在串口端口(port)的缓冲区中,然后启动发送*/
struct uart_state *state = tty->driver_data;/*
struct uart_state如下*/
struct uart_port *port;
struct circ_buf *circ;/*
struct circ_buf如下*/
unsigned long flags;
int c, ret = 0;
/*
* This means you called this function _after_ the port was
* closed. No cookie for you.
*/
if (!state || !state->info) {
WARN_ON(1);
return -EL3HLT;
}
port = state->port;
circ = &state->info->xmit;
if (!circ->buf)
return 0;
spin_lock_irqsave(&port->lock, flags);
while (1) {
c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0)
break;
memcpy(circ->buf + circ->head, buf, c);
circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
buf += c;
count -= c;
ret += c;
}
spin_unlock_irqrestore(&port->lock, flags);
/*前面都是准备工作,检查发送字符串的条件是否满足*/
uart_start(tty);/*启动发送*/
return ret;
}
/*
* This is the state information which is persistent across opens.
* The low level driver must not to touch any elements contained
* within.
*/
struct uart_state {
unsigned int close_delay; /* msec */
unsigned int closing_wait; /* msec */
#define USF_CLOSING_WAIT_INF (0)
#define USF_CLOSING_WAIT_NONE (~0U)
int count;
int pm_state;
struct uart_info *info;
struct uart_port *port;
struct mutex mutex;
};
struct circ_buf {
char *buf;
int head;
int tail;
};
串口抽象层文件:
linux-2.6.22.6\drivers\serial\serial_core.c
static void uart_start(struct tty_struct *tty)/*串口抽象层*/
{/*启动发送*/
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->port;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
__uart_start(tty);/*具体执行启动发送*/
spin_unlock_irqrestore(&port->lock, flags);
}
串口抽象层文件:linux-2.6.22.6\drivers\serial\serial_core.c
static void __uart_start(struct tty_struct *tty)/*串口抽象层*/
{/*启动发送*/
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->port;
if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf &&
!tty->stopped && !tty->hw_stopped)
port->ops->start_tx(port);/*串口芯片层函数,即serial8250_start_tx*/
}
串口芯片层文件:linux-2.6.22.6\drivers\serial\8250.c
static void serial8250_start_tx(struct uart_port *port)/*串口芯片层*/
{/*使能串口中断,发送也是由中断来完成的*/
struct uart_8250_port *up = (struct uart_8250_port *)port;
if (!(up->ier & UART_IER_THRI)) {
up->ier |= UART_IER_THRI;/*使能串口发送中断*/
serial_out(up, UART_IER, up->ier);/*static void serial_out(struct uart_8250_port *up, int offset, int value)使能发送中断*/
/*字符的发送在中断函数中进行*/
if (up->bugs & UART_BUG_TXEN) {
unsigned char lsr, iir;
lsr = serial_in(up, UART_LSR);
iir = serial_in(up, UART_IIR) & 0x0f;
if ((up->port.type == PORT_RM9000) ?
(lsr & UART_LSR_THRE &&
(iir == UART_IIR_NO_INT || iir == UART_IIR_THRI)) :
(lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT))
transmit_chars(up);
}
}
/*
* Re-enable the transmitter if we disabled it.
*/
if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) {
up->acr &= ~UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
}
发送数据只是把数据保存起来,然后开启发送中断;当串口芯片内部的发送缓冲区可以再次存入数据时,这个中断被触发;在中断处理函数中将数据一点点地发送给串口芯片。
发送中断处理函数发送数据过程:
中断处理函数
static irqreturn_t serial8250_interrupt(int irq, void *dev_id)/*串口芯片层*/略
【因为已经在数据接收过程中分析过】
串口芯片层文件:linux-2.6.22.6\drivers\serial\
8250.c
/*
* This handles the interrupt from one port.
*/
static inline void serial8250_handle_port(struct uart_8250_port *up)/*串口芯片层*/
{/*处理来自某个口的中断:读或者写*/
unsigned int status;
unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
status = serial_inp(up, UART_LSR);/*读状态寄存器*/
DEBUG_INTR("status = %x...", status);
if (status & UART_LSR_DR)/*接收数据ready*/
receive_chars(up, &status);/*接收n个字符,并把它们一个个传到串口抽象层**/
check_modem_status(up);
if (status & UART_LSR_THRE)/*传输寄存器为空*/
transmit_chars(up);/*发送*/
spin_unlock_irqrestore(&up->port.lock, flags);
}
串口芯片层文件:linux-2.6.22.6\drivers\serial\8250.c
static void transmit_chars(struct uart_8250_port *up)/*串口芯片层*/
{/*发送字符串*/
struct circ_buf *xmit = &up->port.info->xmit;
int count;
if (up->port.x_char) {
serial_outp(up, UART_TX, up->port.x_char);
up->port.icount.tx++;
up->port.x_char = 0;
return;
}
if (uart_tx_stopped(&up->port)) {
serial8250_stop_tx(&up->port);
return;
}
if (uart_circ_empty(xmit)) {
__stop_tx(up);
return;
}
count = up->tx_loadsz;
do {
serial_out(up, UART_TX, xmit->buf[xmit->tail]);/*将数据写入串口芯片*/
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
up->port.icount.tx++;
if (uart_circ_empty(xmit))
break;
} while (--count > 0);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);/*如果已经发送完毕,唤醒进程*/
DEBUG_INTR("THRE...");
if (uart_circ_empty(xmit))
__stop_tx(up);/*串口芯片层,如果已经发送完毕,则禁止发送中断*/
}
串口芯片层文件:linux-2.6.22.6\drivers\serial\8250.c
static void serial_out(struct uart_8250_port *up, int offset, int value)/*串口芯片层*/
{/*把数据value写给串口芯片*/
/* Save the offset before it's remapped */
int save_offset = offset;
offset = map_8250_out_reg(up, offset) << up->port.regshift;
switch (up->port.iotype) {
case UPIO_HUB6:
outb(up->port.hub6 - 1 + offset, up->port.iobase);
outb(value, up->port.iobase + 1);
break;
case UPIO_MEM:
writeb(value, up->port.membase + offset);
break;
case UPIO_RM9000:
case UPIO_MEM32:
writel(value, up->port.membase + offset);
break;
#ifdef CONFIG_SERIAL_8250_AU1X00
case UPIO_AU:
__raw_writel(value, up->port.membase + offset);
break;
#endif
case UPIO_TSI:
if (!((offset == UART_IER) && (value & UART_IER_UUE)))
writeb(value, up->port.membase + offset);
break;
case UPIO_DWAPB:
/* Save the LCR value so it can be re-written when a
* Busy Detect interrupt occurs. */
if (save_offset == UART_LCR)
up->lcr = value;
writeb(value, up->port.membase + offset);
/* Read the IER to ensure any interrupt is cleared before
* returning from ISR. */
if (save_offset == UART_TX || save_offset == UART_IER)
value = serial_in(up, UART_IER);
break;
default:
outb(value, up->port.iobase + offset);
}
}
uart_write_wakeup(&up->port);/*如果已经发送完毕,唤醒进程*/将调用uart_tasklet_action(串口抽象层)
串口抽象层文件:linux-2.6.22.6\drivers\serial\serial_core.c
/*
* This routine is used by the interrupt handler to schedule processing in
* the software interrupt portion of the driver.
*/
void uart_write_wakeup(struct uart_port *port)
{
struct uart_info *info = port->info;
/*
* This means you called this function _after_ the port was
* closed. No cookie for you.
*/
BUG_ON(!info);
tasklet_schedule(&info->tlet);
}
串口抽象层文件:linux-2.6.22.6\drivers\serial\serial_core.c
static void uart_tasklet_action(unsigned long data)/*串口抽象层*/
{
struct uart_state *state = (struct uart_state *)data;
tty_wakeup(state->info->tty);/*终端设备层*/
}
终端设备层文件:linux-2.6.22.6\drivers\char\tty_io.c
/**
* tty_wakeup - request more data
* @tty: terminal
*
* Internal and external helper for wakeups of tty. This function
* informs the line discipline if present that the driver is ready
* to receive more output data.
*/
void tty_wakeup(struct tty_struct *tty)/*终端设备层*/
{
struct tty_ldisc *ld;
if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
ld = tty_ldisc_ref(tty);
if(ld) {
if(ld->write_wakeup)
ld->write_wakeup(tty);
tty_ldisc_deref(ld);
}
}
wake_up_interruptible(&tty->write_wait);/*与前面write_chan中的add_wait_queue(&tty->write_wait,&wait)对应;唤醒等待发送完毕的进程*/
}
串口芯片层文件:linux-2.6.22.6\drivers\serial\8250.c
static inline void __stop_tx(struct uart_8250_port *p)/*串口芯片层*/
{/*禁止发送中断*/
if (p->ier & UART_IER_THRI) {/*避免重复禁止*/
p->ier &= ~UART_IER_THRI;/*禁止发送保持寄存器*/
serial_out(p, UART_IER, p->ier);/*关闭发送中断使能*/
}
}