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;
//即为在intuart_register_driver(struct uart_driver *drv)初始化的static conststruct tty_operations uart_ops
tty->index = idx;
tty_line_name(driver, idx,tty->name);
tty->dev = tty_get_device(tty);
return tty;
}
二.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)
{
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
}
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
/*
* 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
};