Uart驱动小结
前段时间由于工作需要,接触了下ATMEL 的sam d20g18, cortex-m0,用到了片子上的i2c和uart,使用SDK,对一些低层的细节可以考虑的少一点,开发进度也快了不少;由于正在看Linux,所以顺便也看了下Linux的tty框架,记录下来打个标记。
Uart驱动是紧紧围绕数据结构tty_driver的。
一.UART字符设备
1.1UART字符设备操作函数
Tty设备也算是字符设备,设备的操作函数在驱动加载的时候被注册,具体的操作函数如下:
static const struct file_operations tty_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = tty_write,
.poll = tty_poll,
.unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
.open = tty_open,
.release = tty_release,
.fasync = tty_fasync,
};
在以上的函数中有一个重要的数据结构tty_struct
struct tty_struct {
int magic;
structkref kref;
structdevice *dev;
structtty_driver *driver;
conststruct tty_operations *ops;
structtty_ldisc *ldisc;
……
struct tty_port*port;
}
该结构是在使用设备打开时候被初始化分配的
static inttty_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty;
int noctty, retval;
dev_t device = inode->i_rdev;
unsigned saved_flags =filp->f_flags;
nonseekable_open(inode, filp);
retry_open:
retval = tty_alloc_file(filp); //分配file->private_data=tty_file_private
if (retval)
return -ENOMEM;
tty = tty_open_current_tty(device,filp);
if (!tty)
tty =tty_open_by_driver(device, inode, filp);
//内部有1.tty_lookup_driver根据设备节点,在链表上取得tty_driver 2.初始化tty的函数tty = tty_init_dev(driver, index);
if (IS_ERR(tty)) {
tty_free_file(filp);
retval = PTR_ERR(tty);
if (retval != -EAGAIN ||signal_pending(current))
return retval;
schedule();
goto retry_open;
}
tty_add_file(tty, filp); //赋值,file->private_data->tty= tty;
check_tty_count(tty, __func__);
tty_debug_hangup(tty, "opening(count=%d)\n", tty->count);
if (tty->ops->open)
retval =tty->ops->open(tty, filp);
else
retval = -ENODEV;
filp->f_flags = saved_flags;
if (retval) {
tty_debug_hangup(tty,"open error %d, releasing\n", retval);
tty_unlock(tty); /* need tocall tty_release without BTM */
tty_release(inode, filp);
if (retval != -ERESTARTSYS)
return retval;
if (signal_pending(current))
return retval;
schedule();
/*
* Need to reset f_op in case a hanguphappened.
*/
if (tty_hung_up_p(filp))
filp->f_op =&tty_fops;
goto retry_open;
}
clear_bit(TTY_HUPPED,&tty->flags);
noctty = (filp->f_flags & O_NOCTTY)||
(IS_ENABLED(CONFIG_VT) && device ==MKDEV(TTY_MAJOR, 0)) ||
device == MKDEV(TTYAUX_MAJOR, 1) ||
(tty->driver->type ==TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype ==PTY_TYPE_MASTER);
if (!noctty)
tty_open_proc_set_tty(filp,tty);
tty_unlock(tty);
return 0;
}
1.2 tty_struct的初始化
structtty_struct *tty_init_dev(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
int retval;
tty = alloc_tty_struct(driver, idx);//初始化tty_struct
if (!tty) {
retval = -ENOMEM;
goto err_module_put;
}
……
tty_lock(tty);
retval = tty_driver_install_tty(driver,tty);
if (retval < 0)
goto err_free_tty;
if (!tty->port)
tty->port =driver->ports[idx];//将driver定义的port和tty_struct的port联系起来,tty_port结构中有串口的收发数据的缓存
……
tty->port->itty = tty;
}
structtty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
tty = kzalloc(sizeof(*tty),GFP_KERNEL);
if (!tty)
return NULL;
kref_init(&tty->kref);
tty->magic = TTY_MAGIC;
tty_ldisc_init(tty);//初始化线路规程,/
tty->session = NULL;
tty->pgrp = NULL;
mutex_init(&tty->legacy_mutex);
mutex_init(&tty->throttle_mutex);
init_rwsem(&tty->termios_rwsem);
mutex_init(&tty->winsize_mutex);
init_ldsem(&tty->ldisc_sem);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
INIT_WORK(&tty->hangup_work,do_tty_hangup);
mutex_init(&tty->atomic_write_lock);
spin_lock_init(&tty->ctrl_lock);
spin_lock_init(&tty->flow_lock);
spin_lock_init(&tty->files_lock);
INIT_LIST_HEAD(&tty->tty_files);
INIT_WORK(&tty->SAK_work,do_SAK_work);
tty->driver = driver;
tty->ops = driver->ops; // struct tty_operations *ops;
//即为在intuart_register_driver(struct uart_driver *drv)初始化的static const struct tty_operations uart_ops
tty->index = idx;
tty_line_name(driver, idx,tty->name);
tty->dev = tty_get_device(tty);
return tty;
}
void tty_ldisc_init(struct tty_struct *tty)
{
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
if (IS_ERR(ld))
panic("n_tty: init_tty");
tty->ldisc = ld;
}
1.3写数据
ssize_t tty_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
struct tty_struct *tty = file_tty(file);
ld = tty_ldisc_ref_wait(tty);
ret = do_tty_write(ld->ops->write, tty, file, buf, count); //发送数据
}
ld是这样注册的
void tty_ldisc_init(struct tty_struct *tty)
{
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
if (IS_ERR(ld))
panic("n_tty: init_tty");
tty->ldisc = ld;
}
static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)//此处disc是N_TTY
{
ldops = get_ldops(disc);
ld->ops = ldops;
}
N_TTY的注册的ldops结构是
static struct tty_ldisc_ops n_tty_ops = {//lieye
.magic = TTY_LDISC_MAGIC,
.name = "n_tty",
.open = n_tty_open,
.close = n_tty_close,
.write = n_tty_write,
};
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
c = tty->ops->write(tty, b, nr);
// 此处为上文提到的tty->ops = driver->ops; struct tty_operations *ops;
// driver->ops又来自于下文提到的normal->ops = uart_ops, normal的类型是tty_driver, uart_ops的类型是tty_operations
//uart_ops结构中的函数int uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
}
二.UART以平台设备形式注册的驱动
2.1 UART平台设备驱动
static struct platform_driversamsung_serial_driver = {
.probe = s3c24xx_serial_probe,
.remove = s3c24xx_serial_remove,
.id_table = s3c24xx_serial_driver_ids,
.driver = {
.name = "samsung-uart",
.pm = SERIAL_SAMSUNG_PM_OPS,
.of_match_table = of_match_ptr(s3c24xx_uart_dt_match),
},
};
2.2 设备驱动的注册
static int s3c24xx_serial_probe(structplatform_device *pdev)
{
struct s3c24xx_uart_port *ourport;
ourport= &s3c24xx_serial_ports[index];
ret = s3c24xx_serial_init_port(ourport,pdev);
if(ret < 0)
returnret;
if(!s3c24xx_uart_drv.state) {
ret= uart_register_driver(&s3c24xx_uart_drv);//里面会层层调用,注册真实的字符设备。
if(ret < 0) {
pr_err("Failedto register Samsung UART driver\n");
returnret;
}
}
dbg("%s:adding port\n", __func__);
uart_add_one_port(&s3c24xx_uart_drv,&ourport->port);//注册个port
}
s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = {
.lock = __PORT_LOCK_UNLOCKED(0),
.iotype = UPIO_MEM,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,/port的ops
.flags = UPF_BOOT_AUTOCONF,
.line = 0,
}
},
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
state = drv->state + uport->line;
port = &state->port;
state->uart_port = uport;
uport->state = state;
}
2.3 uart_register_driver
int uart_register_driver(struct uart_driver*drv)
{
structtty_driver *normal;
inti, retval;
BUG_ON(drv->state);
/*
* Maybe we should be using a slab cache forthis, especially if
* we have a large number of ports to handle.
*/
drv->state= kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
if(!drv->state)
gotoout;
normal= alloc_tty_driver(drv->nr);
if(!normal)
gotoout_kfree;
drv->tty_driver= normal;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start = drv->minor;
normal->type = TTY_DRIVER_TYPE_SERIAL;
normal->subtype = SERIAL_TYPE_NORMAL;
normal->init_termios = tty_std_termios;
normal->init_termios.c_cflag= B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->init_termios.c_ispeed= normal->init_termios.c_ospeed = 9600;
normal->flags = TTY_DRIVER_REAL_RAW |TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv;
tty_set_operations(normal,&uart_ops);//此处初始化tty_driver->ops
//normal->ops = uart_ops, normal的类型是tty_driver, uart_ops的类型是tty_operations
/*
* Initialise the UART state(s).
*/
for(i = 0; i < drv->nr; i++) {
struct uart_state *state =drv->state + i;
structtty_port *port = &state->port;
tty_port_init(port);
port->ops= &uart_port_ops;
}
retval= tty_register_driver(normal);//注册字符驱动
if(retval >= 0)
returnretval;
for(i = 0; i < drv->nr; i++)
tty_port_destroy(&drv->state[i].port);
put_tty_driver(normal);
out_kfree:
kfree(drv->state);
out:
return-ENOMEM;
}
2.4 struct tty_operations uart_ops
static const struct tty_operations uart_ops= {
.open = uart_open,
.close = uart_close,
.write = uart_write,
.put_char = uart_put_char,
.flush_chars = uart_flush_chars,
.write_room = uart_write_room,
.chars_in_buffer=uart_chars_in_buffer,
.flush_buffer = uart_flush_buffer,
.ioctl = uart_ioctl,
.throttle = uart_throttle,
.unthrottle = uart_unthrottle,
.send_xchar = uart_send_xchar,
.set_termios = uart_set_termios,
.set_ldisc = uart_set_ldisc,
.stop = uart_stop,
.start = uart_start,
.hangup = uart_hangup,
.break_ctl = uart_break_ctl,
.wait_until_sent=uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
.proc_fops = &uart_proc_fops,
#endif
.tiocmget = uart_tiocmget,
.tiocmset = uart_tiocmset,
.get_icount = uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = uart_poll_init,
.poll_get_char = uart_poll_get_char,
.poll_put_char = uart_poll_put_char,
#endif
};
uart_write()一个实际的发送函数:
static int uart_write(struct tty_struct *tty,
const unsigned char *buf, int count)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port;
struct circ_buf *circ;
unsigned long flags;
int c, ret = 0;
/*
* This means you called this function _after_ the port was
* closed. No cookie for you.
*/
if (!state) {
WARN_ON(1);
return -EL3HLT;
}
circ = &state->xmit;
if (!circ->buf)
return 0;
port = uart_port_lock(state, flags);
while (port) {
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;
}
__uart_start(tty);
uart_port_unlock(port, flags);
return ret;
}
static void __uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
if (port && !uart_tx_stopped(port))
port->ops->start_tx(port);
}
struct uart_ops s3c24xx_serial_ops = {
.pm = s3c24xx_serial_pm,
.tx_empty = s3c24xx_serial_tx_empty,
.get_mctrl = s3c24xx_serial_get_mctrl,
.set_mctrl = s3c24xx_serial_set_mctrl,
.stop_tx = s3c24xx_serial_stop_tx,
.start_tx = s3c24xx_serial_start_tx,
.stop_rx = s3c24xx_serial_stop_rx,
.break_ctl = s3c24xx_serial_break_ctl,
.startup = s3c24xx_serial_startup,
.shutdown = s3c24xx_serial_shutdown,
.set_termios = s3c24xx_serial_set_termios,
.type = s3c24xx_serial_type,
.release_port = s3c24xx_serial_release_port,
.request_port = s3c24xx_serial_request_port,
.config_port = s3c24xx_serial_config_port,
.verify_port = s3c24xx_serial_verify_port,
#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL)
.poll_get_char = s3c24xx_serial_get_poll_char,
.poll_put_char = s3c24xx_serial_put_poll_char,
#endif
};
static void s3c24xx_serial_start_tx(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
struct circ_buf *xmit = &port->state->xmit;
if (!tx_enabled(port)) {
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_disable(port);
tx_enabled(port) = 1;
if (!ourport->dma || !ourport->dma->tx_chan)
s3c24xx_serial_start_tx_pio(ourport);
}
if (ourport->dma && ourport->dma->tx_chan) {
if (!uart_circ_empty(xmit) && !ourport->tx_in_progress)
s3c24xx_serial_start_next_tx(ourport);
}
}