linux内核调用串口,linux驱动之串口驱动框架
一、前言
前面介紹了 Linux內核 的 2 個驅動框架—— I2C 和 SPI ,這 2 個框架相對簡單一些,直來直去,沒有比較難以理解的點,層次分明。而今天我們要講述的是我們熟悉的 串口驅動,該驅動加框也較之之前的驅動來說,復雜了許多。串口 是我們常用的通訊手段,但其軟件框架在 Linux內核 中非常復雜。當然,這里面也有歷史原因在。本文將簡單地介紹 UART驅動框架,希望能夠幫助各位讀者。
二、UART驅動
2.1 tty簡介
串口(UART) 通常是以 tty驅動 的形式在用戶層表現(xiàn)出來,那么我們首先需要知道 tty 是什么東西。
tty 是 Linux系統(tǒng) 中出現(xiàn)得最多的一種設備類型,可以直接查看 /dev目錄 下,往往可以看到多個 tty設備節(jié)點。要了解 tty設備 就需要從歷史的角度說起。
在計算機系統(tǒng)中,終端 是 電子設備,用于向系統(tǒng)輸入數(shù)據(jù),或者接收系統(tǒng)發(fā)送的數(shù)據(jù)并顯示出來,所以該設備的作用是用于 人機交互。而 電傳打字機(Teletype) 是一種久遠的 遠距離信息傳送設備,其通過 鍵盤 輸入數(shù)據(jù),通過 印字機器 顯示接收到的數(shù)據(jù)。電傳打字機 就是一種典型的 終端,其英語簡稱即 tty。
按照筆者的理解,tty 即用于與 用戶 交互的設備,其向計算機系統(tǒng)輸入數(shù)據(jù),也接收來自計算機系統(tǒng)的數(shù)據(jù)。有興趣的讀者可以參考鏈接中的文章 Linux TTY framework(1)_基本概念。
對于 Linux系統(tǒng) 來說,通過 串口 連接的設備一般都可以作為 終端設備。經過日積月累,終端、tty、串口等概念逐漸不再去分。一般來講,所有 串口設備 都是 tty設備,而 tty設備 除了 串口外還有其他的具體形式,比如 虛擬終端 、偽終端等。
其實這里還涉及到一個概念 控制臺(console),但本文目前不做講述,后續(xù)有機會再寫相關文章。
2.2 串口tty框架
在講解相關細節(jié)之前,我們需要對 串口tty驅動 有個整體性的認識,這樣有非常助于后面理解代碼細節(jié)。在看細節(jié)之前,我們需要知道:
整個驅動的整體架構是如何組織的
數(shù)據(jù)結構之間的關系是如何
函數(shù)的傳遞是如何遞進的
下面將展示一張圖,該圖有助于我們理解
串口tty驅動框架圖
我們可以看到:
從左往右 看,整個驅動可以分為 3個層次,分為為 字符設備層、tty層 和 串口硬件層。
字符設備層 是 tty驅動 作為 字符設備 直接與應用層進行交互,中間通過 tty層 進行抽象,使得底層的具體形式可以被解耦。最終是底層硬件的 串口層,該層負責數(shù)據(jù)的設備之間的交互及一系列管理操作的具體實現(xiàn)。
從上往下 看,驅動主要由幾個數(shù)據(jù)結構組成,分別為 UART_driver、UART_state 和 tty_driver。UART_driver 是全局的 根數(shù)據(jù)結構,所有的結構體都由其來進行保存和控制。tty_driver 則是對 tty層的具體實現(xiàn),最后 UART_state 和 Uart_pot 則是底層驅動的具體實現(xiàn)。
2.3 串口tty驅動
本節(jié)將對代碼進行講述,其中我們選擇 8250 這個串口IP的代碼作為示例來進行講述。當然只是為了學習驅動的實現(xiàn),其他的串口驅動讀者可以按照自己的理解再去閱讀。
本節(jié)會分別按照 初始化 和 讀/寫 來進行講述。
2.3.1 串口硬件初始化
串口驅動的框架一大部分是在初始化階段進行,對 初始化 進行梳理是必要的階段。下面先來看看代碼調用圖譜。
首先是對 UART_driver結構體 進行創(chuàng)建和相關初始化,其負責高度的軟件抽象和相關軟件邏輯。其代碼調用圖譜如下:
->serial8250_init
->serial8250_isa_init_ports(初始化serial8250_ports,數(shù)據(jù)類型為uart_8250_port)
->serial8250_init_port(將serial8250_pops賦值給uart_port->ops)
->univ8250_rsa_support
->uart_register_driver(注冊uart_driver,即全局變量serial8250_reg,將uart_ops設置給tty_driver)
->alloc_tty_driver(分配uart_driver->uart_state)
->tty_port_init(初始化uart_driver->uart_state->tty_port,tty_port的操作集為uart_port_ops)
->tty_register_driver(注冊tty_driver,將tty_driver加入全局鏈表tty_drivers)
/* 8250_core */
/* 初始化8250串口框架 */
module_init(serial8250_init);
static int __init serial8250_init(void)
{
int ret;
......
/* 初始化各個串口的UART_port數(shù)據(jù)結構 */
serial8250_isa_init_ports();
......
/* 注冊UART驅動即UART_driver */
serial8250_reg.nr = UART_NR;
ret = uart_register_driver(&serial8250_reg);
......
serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
......
}
/* 初始化各個串口的UART_port數(shù)據(jù)結構 */
static void __init serial8250_isa_init_ports(void)
{
struct uart_8250_port *up;
static int first = 1;
int i, irqflag = 0;
......
/* 初始化每個UART_port的操作函數(shù)集 */
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
struct uart_port *port = &up->port;
/* 設置當前UART_port的編號 */
port->line = i;
/* 初始化UART_port的ops成員 */
serial8250_init_port(up);
{
struct uart_port *port = &up->port;
port->ops = &serial8250_pops;
......
}
/* 此時port->ops為serial8250_pops,所以base_ops指向了serial8250_pops */
if (!base_ops)
base_ops = port->ops;
/* 將UART_port的ops成員指向univ8250_port_ops */
port->ops = &univ8250_port_ops;
......
/* 設置uart_8250_port的ops成員指向了univ8250_driver_ops,該成員在設置中斷時有用到 */
up->ops = &univ8250_driver_ops;
......
}
/*
univ8250_port_ops的成員賦值為*base_ops指向的地址,即serial8250_pops
所以此時UART_port的ops成員指向serial8250_pops
*/
univ8250_port_ops = *base_ops;
/* 設置UART_port->ops的部分成員 */
univ8250_rsa_support(&univ8250_port_ops);
{
ops->config_port = univ8250_config_port;
ops->request_port = univ8250_request_port;
ops->release_port = univ8250_release_port;
}
......
}
/* 注冊UART_driver驅動 */
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal;
int i, retval;
/* 為UART_driver的每個UART_state分配空間,每個串口硬件都有UART_state */
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
......
/* 為串口驅動UART_driver分配一個tty_driver */
normal = alloc_tty_driver(drv->nr);
{
struct tty_driver *ret = tty_alloc_driver(lines, 0);
......
return ret;
}
......
/* 設置UART_driver的tty_driver成員 */
drv->tty_driver = normal;
/* 初始化相關成員 */
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
/* 這里的設備號會在后續(xù)的open過程中使用到 */
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_driver的ops成員指向struct tty_operations uart_ops */
tty_set_operations(normal, &uart_ops);
{
driver->ops = op;
};
/* 初始化每個UART_state的tty_port成員 */
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port;
/* 初始化每個tty_port */
tty_port_init(port);
{
memset(port, 0, sizeof(*port));
tty_buffer_init(port);
/* 以下為tty_buffer_init函數(shù)體 */
{
struct tty_bufhead *buf = &port->buf;
mutex_init(&buf->lock);
tty_buffer_reset(&buf->sentinel, 0);
buf->head = &buf->sentinel;
buf->tail = &buf->sentinel;
init_llist_head(&buf->free);
atomic_set(&buf->mem_used, 0);
atomic_set(&buf->priority, 0);
INIT_WORK(&buf->work, flush_to_ldisc);//該work_queue,后面的讀取數(shù)據(jù)的時候要用到
buf->mem_limit = TTYB_DEFAULT_MEM_LIMIT;
}
init_waitqueue_head(&port->open_wait);
init_waitqueue_head(&port->delta_msr_wait);
mutex_init(&port->mutex);
mutex_init(&port->buf_mutex);
spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100;
/* 設置tty_port的client_ops指向default_client_ops,會在接收和發(fā)送數(shù)據(jù)時使用到 */
port->client_ops = &default_client_ops;
kref_init(&port->kref);
}
/* 設置tty_port的ops成員指向uart_port_ops */
port->ops = &uart_port_ops;
}
retval = tty_register_driver(normal);
......
}
/* 注冊tty_driver驅動 */
int tty_register_driver(struct tty_driver *driver)
{
int error;
int i;
dev_t dev;
struct device *d;
/* 分配設備好 */
if (!driver->major) {
error = alloc_chrdev_region(&dev, driver->minor_start,
driver->num, driver->name);
if (!error) {
driver->major = MAJOR(dev);
driver->minor_start = MINOR(dev);
}
} else {
dev = MKDEV(driver->major, driver->minor_start);
error = register_chrdev_region(dev, driver->num, driver->name);
}
if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {
/* 添加tty的字符設備 */
error = tty_cdev_add(driver, dev, 0, driver->num);
{
int err;
driver->cdevs[index] = cdev_alloc();
......
/* 設置字符設備的操作集為tty_fops,該操作集用于tty設備和應用層交互 */
driver->cdevs[index]->ops = &tty_fops;
driver->cdevs[index]->owner = driver->owner;
/* 添加字符設備 */
err = cdev_add(driver->cdevs[index], dev, count);
......
return err;
}
......
}
/* 將當前的tty_driver添加全局的tty_drivers鏈表 */
mutex_lock(&tty_mutex);
list_add(&driver->tty_drivers, &tty_drivers);
mutex_unlock(&tty_mutex);
/* 注冊每一個tty設備 */
if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
for (i = 0; i < driver->num; i++) {
d = tty_register_device(driver, i, NULL);
......
}
}
}
......
return 0;
......
}
上面是針對 8250串口核心 做的一些基本的初始化。按照筆者理解,在實際應用中用戶有可能需要加載其他串口驅動來修改串口的一些屬性,所以當用戶執(zhí)行驅動裝載時有以下的初始化步驟,同樣也是針對 UART_port 和 UART_state 的初始化和操作。其調用譜圖和代碼示例如下:
dw8250_probe
->serial8250_register_8250_port
->serial8250_find_match_or_unused(從全局serial8250_ports找出可以使用的元素,該元素可以理解為uart_port)
->uart_add_one_port(填充uart_port)
->tty_port_register_device_attr_serdev
->tty_port_link_device
->tty_register_device_attr
->tty_cdev_add(將tty_ops賦給tty_driver的cdec成員)
/* 初始化用戶的串口硬件模塊 */
static int dw8250_probe(struct platform_device *pdev)
{
struct uart_8250_port uart = {};
struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int irq = platform_get_irq(pdev, 0);
struct uart_port *p = &uart.port;
struct device *dev = &pdev->dev;
struct dw8250_data *data;
int err;
u32 val;
......
/* 對p指向uart_port結構體進行初始化,根據(jù)用戶自己的進行設置 */
spin_lock_init(&p->lock);
p->mapbase = regs->start;
p->irq = irq; //串口中斷號
p->handle_irq = dw8250_handle_irq; //串口中斷處理函數(shù),會在串口中斷時執(zhí)行
p->pm = dw8250_do_pm;
p->type = PORT_8250;
p->flags = UPF_SHARE_IRQ | UPF_FIXED_PORT;
p->dev = dev;
p->iotype = UPIO_MEM;
p->serial_in = dw8250_serial_in; //串口輸入寄存器設置函數(shù)
p->serial_out = dw8250_serial_out; //串口輸出寄存器設置函數(shù)
p->set_ldisc = dw8250_set_ldisc; //串口線路規(guī)程設置函數(shù)
p->set_termios = dw8250_set_termios; //串口中斷參數(shù)規(guī)程設置函數(shù)
/* 設置寄存器地址 */
p->membase = devm_ioremap(dev, regs->start, resource_size(regs));
......
/* 分配私有數(shù)據(jù) */
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
......
data->dma.fn = dw8250_fallback_dma_filter;
data->usr_reg = DW_UART_USR;
p->private_data = data;
......
/* 注冊用戶自己的串口驅動 */
data->line = serial8250_register_8250_port(&uart);
if (data->line < 0) {
err = data->line;
goto err_reset;
}
......
}
/* 注冊用戶的串口驅動 */
int serial8250_register_8250_port(struct uart_8250_port *up)
{
struct uart_8250_port *uart;
int ret = -ENOSPC;
......
mutex_lock(&serial_mutex);
/* 從8250_core中找出一個沒有用且匹配的uart_8250_port */
uart = serial8250_find_match_or_unused(&up->port);
/* 對uart_8250_port 進行初始化 */
if (uart && uart->port.type != PORT_8250_CIR) {
......
/* 初始化找到的uart_port數(shù)據(jù)結構uart->port為uart_port數(shù)據(jù)結構 */
uart->port.iobase = up->port.iobase;
uart->port.membase = up->port.membase;
uart->port.irq = up->port.irq;
uart->port.irqflags = up->port.irqflags;
uart->port.uartclk = up->port.uartclk;
uart->port.fifosize = up->port.fifosize;
uart->port.regshift = up->port.regshift;
uart->port.iotype = up->port.iotype;
uart->port.flags = up->port.flags | UPF_BOOT_AUTOCONF;
uart->bugs = up->bugs;
uart->port.mapbase = up->port.mapbase;
uart->port.mapsize = up->port.mapsize;
uart->port.private_data = up->port.private_data;
uart->tx_loadsz = up->tx_loadsz;
uart->capabilities = up->capabilities;
uart->port.throttle = up->port.throttle;
uart->port.unthrottle = up->port.unthrottle;
uart->port.rs485_config = up->port.rs485_config;
uart->port.rs485 = up->port.rs485;
uart->dma = up->dma;
if (uart->port.fifosize && !uart->tx_loadsz)
uart->tx_loadsz = uart->port.fifosize;
if (up->port.dev)
uart->port.dev = up->port.dev;
if (up->port.flags & UPF_FIXED_TYPE)
uart->port.type = up->port.type;
/* 設置uart_port的默認屬性 */
serial8250_set_defaults(uart);
if (up->port.serial_in)
uart->port.serial_in = up->port.serial_in;
if (up->port.serial_out)
uart->port.serial_out = up->port.serial_out;
if (up->port.handle_irq)
uart->port.handle_irq = up->port.handle_irq;
if (up->port.set_termios)
uart->port.set_termios = up->port.set_termios;
if (up->port.set_ldisc)
uart->port.set_ldisc = up->port.set_ldisc;
if (up->port.get_mctrl)
uart->port.get_mctrl = up->port.get_mctrl;
if (up->port.set_mctrl)
uart->port.set_mctrl = up->port.set_mctrl;
if (up->port.startup)
uart->port.startup = up->port.startup;
if (up->port.shutdown)
uart->port.shutdown = up->port.shutdown;
if (up->port.pm)
uart->port.pm = up->port.pm;
if (up->port.handle_break)
uart->port.handle_break = up->port.handle_break;
if (up->dl_read)
uart->dl_read = up->dl_read;
if (up->dl_write)
uart->dl_write = up->dl_write;
if (uart->port.type != PORT_8250_CIR) {
if (serial8250_isa_config != NULL)
serial8250_isa_config(0, &uart->port,
&uart->capabilities);
.....
/* 將新的uart_port添加到uart_driver */
ret = uart_add_one_port(&serial8250_reg, &uart->port);
if (ret == 0)
ret = uart->port.line;
} else {
dev_info(uart->port.dev,
"skipping CIR port at 0x%lx / 0x%llx, IRQ %d\n",
uart->port.iobase,
(unsigned long long)uart->port.mapbase,
uart->port.irq);
ret = 0;
}
}
mutex_unlock(&serial_mutex);
return ret;
}
/* 把一個uart_port添加到uart_driver */
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state;
struct tty_port *port;
int ret = 0;
struct device *tty_dev;
int num_groups;
/* 根據(jù)串口編號獲取對應的uart_state和uart_port */
state = drv->state + uport->line;
port = &state->port;
......
/* 設置新的uart_port到uart_state中,并將對uart_port的state成員進行賦值 */
state->uart_port = uport;
uport->state = state;
......
/* 分配并設置屬性組 */
uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
GFP_KERNEL);
if (!uport->tty_groups) {
ret = -ENOMEM;
goto out;
}
uport->tty_groups[0] = &tty_dev_attr_group;
if (uport->attr_group)
uport->tty_groups[1] = uport->attr_group;
/* 將新的uart_port及其屬性注冊到內核中 */
tty_dev = tty_port_register_device_attr_serdev(port, drv->tty_driver,
uport->line, uport->dev, port, uport->tty_groups);
......
}
struct device *tty_port_register_device_attr_serdev(struct tty_port *port,
struct tty_driver *driver, unsigned index,
struct device *device, void *drvdata,
const struct attribute_group **attr_grp)
{
struct device *dev;
/* 該函數(shù)設置tty_drvier的tty_port成員,建立聯(lián)系 */
tty_port_link_device(port, driver, index);
{
if (WARN_ON(index >= driver->num))
return;
driver->ports[index] = port;
}
/* 該函數(shù)會設置tty_port的一部分成員 */
dev = serdev_tty_port_register(port, device, driver, index);
{
/* 設置tty_port的client_ops成員 */
port->client_ops = &client_ops;
}
/* 注冊tty_port對應的device */
return tty_register_device_attr(driver, index, device, drvdata,
attr_grp);
}
......
struct device *tty_register_device_attr(struct tty_driver *driver,
unsigned index, struct device *device,
void *drvdata,
const struct attribute_group **attr_grp)
{
char name[64];
dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
struct ktermios *tp;
struct device *dev;
int retval;
......
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
......
/* 設置dev的相關屬性 */
dev->devt = devt;
dev->class = tty_class;
dev->parent = device;
dev->release = tty_device_create_release;
dev_set_name(dev, "%s", name);
dev->groups = attr_grp;
dev_set_drvdata(dev, drvdata);
......
/* 注冊dev */
retval = device_register(dev);
if (retval)
goto err_put;
if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
......
/* 添加tty_driver的cdev,該函數(shù)在前面已經出現(xiàn)過,這里的cdev是對應新的tty_port */
retval = tty_cdev_add(driver, devt, index, 1);
if (retval)
goto err_del;
}
......
}
前面我們簡單的過了一遍 8250串口核心 和 8250串口硬件 的初始化流程。在初始化以后,數(shù)據(jù)結構就如 串口tty驅動框架圖 所示。當然以上內容刪減了許多細節(jié),有興趣的讀者可以閱讀源碼加深理解。
2.3.2 串口使用流程
相比于 初始化,理解 串口讀取 的難度會更加高一些,以為讀寫過程會更加復雜,希望筆者的一點筆墨可以幫助大家稍微理解一下其過程。
這里需要先知道,串口讀取 一般是由 2 個線程來完成:
前臺線程:即用戶在應用空間使用 read 等系統(tǒng)調用的線程,該線程在進入內核空間會執(zhí)行相關的函數(shù)進行等待。當時機成熟時由 后臺線程 進行喚醒并讀取相關數(shù)據(jù)到應用恐案件。
后臺線程:由 串口驅動 在初始化時會定義一個線程,由該線程在中斷有數(shù)據(jù)時進行讀寫。并把讀取到的數(shù)據(jù)存放在某個緩存中,最后會喚醒 前臺線程 去讀取這個緩存中的數(shù)據(jù)。
在講解線程的執(zhí)行之前需要知道 中斷的初始化及其處理函數(shù),因為 串口讀取 與 中斷 密不可分。下面會先講述 中斷初始化及其處理,然后再講述 讀取的前臺及后臺線程。
2.3.2.1 打開串口
串口中斷 的設置是在串口打開時設置的,所以我們直接查看串口打開時的調用圖譜及代碼即可知道相關內容,如下所示:
tty_open(struct file_operations tty_fops)
/* 申請并初始化相關結構體 */
->tty_open_by_driver
->tty_init_dev
->alloc_tty_struct
->tty_ldisc_init(設置默認線路規(guī)程為N_TTY)
/* 執(zhí)行open操作,比如設置寄存器或者中斷等 */
->uart_open
->tty_port_open
->uart_port_activate
->uart_startup
->uart_port_startup
->serial8250_startup
->serial8250_do_startup
->setup_irq(univ8250_setup_irq)
->serial_link_irq_chain
->request_irq(serial8250_interrupt)
可以看到,打開的流程很長,其調用圖譜也非常的深,我們逐步看一下代碼吧
/* 在前面的章節(jié)中可以看到,tty_open是tty設備使用字符設備cdev對外的接口,當我們打開 */
static int tty_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;
......
/* 分配一個tty_file_private結構體并賦值給文件的private_data成員 */
retval = tty_alloc_file(filp);
{
struct tty_file_private *priv;
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
......
file->private_data = priv;
return 0;
}
......
/*
根據(jù)內核注釋,tty_open_current_tty用于獲取當前tty設備的鎖,但從函數(shù)命名和返回值來看這應該是另一種說法
根據(jù)筆者理解,這里應該獲取當前tty設備的tty_struct結構體
如果獲取失敗則表明當前的tty設備沒有打開,需要調用tty_open_by_driver重新打開
*/
tty = tty_open_current_tty(device, filp);
if (!tty)
tty = tty_open_by_driver(device, inode, filp);
......
/* 將當前文件的私有數(shù)據(jù)即tty_file_private成員進行填充,再將其鏈入tty_stuct的tty_files鏈表 */
tty_add_file(tty, filp);
{
struct tty_file_private *priv = file->private_data;
priv->tty = tty;
priv->file = file;
spin_lock(&tty->files_lock);
list_add(&priv->list, &tty->tty_files);
spin_unlock(&tty->files_lock);
}
......
/* tty_struct結構題的ops成員(sturct tty_operations)中的open函數(shù),即uart_open */
if (tty->ops->open)
retval = tty->ops->open(tty, filp);
else
retval = -ENODEV;
filp->f_flags = saved_flags;
......
}
static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,
struct file *filp)
{
struct tty_struct *tty;
struct tty_driver *driver = NULL;
int index = -1;
int retval;
mutex_lock(&tty_mutex);
/* 根據(jù)設備好找到對應的tty_driver */
driver = tty_lookup_driver(device, filp, &index);
......
/* 檢查我們是否重復打開tty */
tty = tty_driver_lookup_tty(driver, filp, index);
......
/* 如果重復打開則執(zhí)行該分支 */
if (tty) {
if (tty_port_kopened(tty->port)) {
tty_kref_put(tty);
mutex_unlock(&tty_mutex);
tty = ERR_PTR(-EBUSY);
goto out;
}
mutex_unlock(&tty_mutex);
retval = tty_lock_interruptible(tty);
tty_kref_put(tty); /* drop kref from tty_driver_lookup_tty() */
if (retval) {
if (retval == -EINTR)
retval = -ERESTARTSYS;
tty = ERR_PTR(retval);
goto out;
}
retval = tty_reopen(tty);
if (retval < 0) {
tty_unlock(tty);
tty = ERR_PTR(retval);
}
} else {
/* 沒有重復打開則初始化出一個tty_struct */
tty = tty_init_dev(driver, index);
mutex_unlock(&tty_mutex);
}
......
return tty;
}
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
int retval;
......
/* 分配tty_struct結構體并初始化,該函數(shù)比較重要 */
......
tty_lock(tty);
/* 該函數(shù)主要講tty_struct的地址鏈接到tty_driver的成員中 */
retval = tty_driver_install_tty(driver, tty);
if (retval < 0)
goto err_free_tty;
/* 復制tty_struct中的tty_port成員 */
if (!tty->port)
tty->port = driver->ports[idx];
......
tty->port->itty = tty;
/* 調用線程規(guī)程并打開 */
retval = tty_ldisc_setup(tty, tty->link);
/* 以下為tty_ldisc_setup函數(shù)體 */
{
int retval = tty_ldisc_open(tty, tty->ldisc);
if (retval)
return retval;
if (o_tty) {
retval = tty_ldisc_open(o_tty, o_tty->ldisc);
if (retval) {
tty_ldisc_close(tty, tty->ldisc);
return retval;
}
}
return 0;
}
......
/* Return the tty locked so that it cannot vanish under the caller */
return tty;
......
}
/* 分配并初始化tty_struct */
struct tty_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;
/* 初始化線路規(guī)程 */
tty_ldisc_init(tty);
/* 以下為tty_ldisc_init函數(shù)體 */
{
/* 線路規(guī)程初始化為N_TTY */
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
if (IS_ERR(ld))
panic("n_tty: init_tty");
tty->ldisc = ld;
}
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;
tty->index = idx;
tty_line_name(driver, idx, tty->name);
tty->dev = tty_get_device(tty);
return tty;
}
static int uart_open(struct tty_struct *tty, struct file *filp)
{
struct uart_driver *drv = tty->driver->driver_state;
int retval, line = tty->index;
struct uart_state *state = drv->state + line;
/* 設置tty_struct的driver_data成員為uart_state */
tty->driver_data = state;
/* 打開tty_port */
retval = tty_port_open(&state->port, tty, filp);
if (retval > 0)
retval = 0;
return retval;
}
int tty_port_open(struct tty_port *port, struct tty_struct *tty,
struct file *filp)
{
spin_lock_irq(&port->lock);
++port->count;
spin_unlock_irq(&port->lock);
tty_port_tty_set(port, tty);
/* 只有在硬件沒有初始化好的情況才能打開設備 */
mutex_lock(&port->mutex);
/* 檢測tty_port是否初始化過 */
if (!tty_port_initialized(port)) {
clear_bit(TTY_IO_ERROR, &tty->flags);
if (port->ops->activate) {
/* 執(zhí)行tty_port的activate方法,即uart_port_ops中的uart_port_activate方法 */
int retval = port->ops->activate(port, tty);
if (retval) {
mutex_unlock(&port->mutex);
return retval;
}
}
tty_port_set_initialized(port, 1);
}
mutex_unlock(&port->mutex);
return tty_port_block_til_ready(port, tty, filp);
}
/* 使能uart_port */
static int uart_port_activate(struct tty_port *port, struct tty_struct *tty)
{
struct uart_state *state = container_of(port, struct uart_state, port);
struct uart_port *uport;
......
/* 啟動uart串口 */
return uart_startup(tty, state, 0);
}
/* 啟動串口 */
static int uart_startup(struct tty_struct *tty, struct uart_state *state,
int init_hw)
{
struct tty_port *port = &state->port;
int retval;
/* 檢查串口是否有初始化 */
if (tty_port_initialized(port))
return 0;
/* 執(zhí)行uart_port_startup */
retval = uart_port_startup(tty, state, init_hw);
......
return retval;
}
static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
int init_hw)
{
struct uart_port *uport = uart_port_check(state);
unsigned long page;
int retval = 0;
......
/* 確保已經開辟了傳輸緩存 */
if (!state->xmit.buf) {
/* 如果沒有則獲取并賦值 */
page = get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
state->xmit.buf = (unsigned char *) page;
uart_circ_clear(&state->xmit);
}
/* 執(zhí)行uart_port的startup函數(shù),即serial8250_pops的serial8250_startup */
retval = uport->ops->startup(uport);
/* 以下為serial8250_startup 函數(shù)體 */
{
if (port->startup)
return port->startup(port);
return serial8250_do_startup(port);
}
if (retval == 0) {
......
/* 初始化串口的硬件設置 */
uart_change_speed(tty, state, NULL);
/* 設置串口的RTS和DTR信號 */
if (init_hw && C_BAUD(tty))
uart_port_dtr_rts(uport, 1);
}
......
return retval;
}
int serial8250_do_startup(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
unsigned long flags;
unsigned char lsr, iir;
int retval;
/* 省略的部分語句為根據(jù)端口類型設置硬件寄存器,不在本文的講述范圍內,有興趣的讀者可以閱讀源碼 */
......
/*
* For the Altera 16550 variants, set TX threshold trigger level.
*/
if (((port->type == PORT_ALTR_16550_F32) ||
(port->type == PORT_ALTR_16550_F64) ||
(port->type == PORT_ALTR_16550_F128)) && (port->fifosize > 1)) {
/* Bounds checking of TX threshold (valid 0 to fifosize-2) */
if ((up->tx_loadsz < 2) || (up->tx_loadsz > port->fifosize)) {
pr_err("ttyS%d TX FIFO Threshold errors, skipping\n",
serial_index(port));
} else {
serial_port_out(port, UART_ALTR_AFR,
UART_ALTR_EN_TXFIFO_LW);
serial_port_out(port, UART_ALTR_TX_LOW,
port->fifosize - up->tx_loadsz);
port->handle_irq = serial8250_tx_threshold_handle_irq;
}
}
if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) {
unsigned char iir1;
/*
當發(fā)送器是空閑并且中斷已經被清除時,需要測試UART不要讓它再次聲明發(fā)送中斷。
實際上當發(fā)送器是空閑且允許中斷時,16500 應該總是重新聲明該中斷。
為了讓改變寄存器生效,需要加入必要的延時。
*/
/* 關閉中斷 */
spin_lock_irqsave(&port->lock, flags);
if (up->port.irqflags & IRQF_SHARED)
disable_irq_nosync(port->irq);
/* 設置寄存器 */
wait_for_xmitr(up, UART_LSR_THRE);
serial_port_out_sync(port, UART_IER, UART_IER_THRI);
/* 延遲并等待寄存器生效 */
udelay(1);
/* 設置寄存器 */
iir1 = serial_port_in(port, UART_IIR);
serial_port_out(port, UART_IER, 0);
serial_port_out_sync(port, UART_IER, UART_IER_THRI);
/* 延遲并等待寄存器生效 */
udelay(1);
iir = serial_port_in(port, UART_IIR);
serial_port_out(port, UART_IER, 0);
/* 使能中斷 */
if (port->irqflags & IRQF_SHARED)
enable_irq(port->irq);
spin_unlock_irqrestore(&port->lock, flags);
/*
* If the interrupt is not reasserted, or we otherwise
* don't trust the iir, setup a timer to kick the UART
* on a regular basis.
*/
if ((!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) ||
up->port.flags & UPF_BUG_THRE) {
up->bugs |= UART_BUG_THRE;
}
}
/* 調用uart_8250_port結構體中的setup_irq方法,即univ8250_driver_ops中的univ8250_setup_irq */
retval = up->ops->setup_irq(up);
if (retval)
goto out;
/* 初始化相關的硬件寄存器 */
......
}
/* 設置中斷 */
static int univ8250_setup_irq(struct uart_8250_port *up)
{
struct uart_port *port = &up->port;
int retval = 0;
......
if (!port->irq) {
} else
/* 如果設置了中斷號,則執(zhí)行serial_link_irq_chain */
retval = serial_link_irq_chain(up);
return retval;
}
static int serial_link_irq_chain(struct uart_8250_port *up)
{
struct hlist_head *h;
struct hlist_node *n;
struct irq_info *i;
int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
......
if (i->head) {
......
} else {
......
/* 申請中斷 */
ret = request_irq(up->port.irq, serial8250_interrupt,
irq_flags, up->port.name, i);
if (ret < 0)
serial_do_unlink(i, up);
}
return ret;
}
總結而言,open流程 可以分為 2個部分:
申請并初始化結構體,比如 tty_struct
設置相關寄存器和初始化中斷
2.3.2.1 打開串口
上面講述了串口的 open流程,完成了中斷設置和寄存器設置。此時我們的應用線程開始讀取串口數(shù)據(jù)。那么讀取流程需要分開 2 個部分來講述:
中斷后臺進程:負責從硬件讀取數(shù)據(jù)存放到 buffer 中,并通過機制通知 用戶前臺進程 獲取數(shù)據(jù)。
用戶前臺進程:負責等待接收 buffer 中的數(shù)據(jù)
下面先講述 后臺中斷讀取,其調用圖譜如下:
/* 中斷設置,該部分以前在前文中講過,不再贅述 */
serial8250_init
->serial8250_isa_init_ports(up->ops = &univ8250_driver_ops;)
/* 中斷處理函數(shù) */
serial8250_interrupt
->handle_irq(dw8250_handle_irq,是在dw8250_probe設置的)
->serial8250_handle_irq
->serial8250_rx_chars
|->serial8250_read_char//從硬件中讀取數(shù)據(jù)到tty_buffer中
| ->uart_insert_char
| ->tty_insert_flip_char
| ->__tty_insert_flip_char
| ->__tty_buffer_request_room
| ->tty_buffer_alloc
|->tty_flip_buffer_push//將tty_buffer中數(shù)據(jù)投放到線路規(guī)程層(ldsc)的緩沖區(qū)中
| ->tty_schedule_flip
| ->flush_to_ldisc
| ->receive_buf
| ->tty_port_default_receive_buf(tty_port)
| ->tty_ldisc_receive_buf
| ->n_tty_receive_buf
下面直接看中斷代碼:
static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
{
struct irq_info *i = dev_id;
struct list_head *l, *end = NULL;
int pass_counter = 0, handled = 0;
......
l = i->head;
/* 輪眉每個串口的中斷函數(shù) */
do {
struct uart_8250_port *up;
struct uart_port *port;
up = list_entry(l, struct uart_8250_port, list);
port = &up->port;
/* 執(zhí)行中斷函數(shù),也就是dw8250_handle_irq,該函數(shù)在dw8250_probe設置 */
if (port->handle_irq(port)) {
handled = 1;
end = NULL;
} else if (end == NULL)
end = l;
l = l->next;
......
} while (l != end);
......
return IRQ_RETVAL(handled);
}
static int dw8250_handle_irq(struct uart_port *p)
{
struct uart_8250_port *up = up_to_u8250p(p);
struct dw8250_data *d = p->private_data;
unsigned int iir = p->serial_in(p, UART_IIR);
unsigned int status;
unsigned long flags;
......
if (serial8250_handle_irq(p, iir))
return 1;
......
return 0;
}
int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
{
unsigned char status;
unsigned long flags;
struct uart_8250_port *up = up_to_u8250p(port);
......
if (status & (UART_LSR_DR | UART_LSR_BI)) {
if (!up->dma || handle_rx_dma(up, iir))
/* 從串口中讀取字節(jié) */
status = serial8250_rx_chars(up, status);
}
......
return 1;
}
unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr)
{
struct uart_port *port = &up->port;
int max_count = 256;
do {
/* 循環(huán)讀取字節(jié)到tty_buffer中,該buffer位于uart_port數(shù)據(jù)結構中 */
serial8250_read_char(up, lsr);
if (--max_count == 0)
break;
lsr = serial_in(up, UART_LSR);
} while (lsr & (UART_LSR_DR | UART_LSR_BI));
tty_flip_buffer_push(&port->state->port);
return lsr;
}
static void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr)
{
struct uart_port *port = &up->port;
unsigned char ch;
char flag = TTY_NORMAL;
if (likely(lsr & UART_LSR_DR))
/* 讀取串口接收到的字節(jié) */
ch = serial_in(up, UART_RX);
......
/* 將數(shù)據(jù)放入tty_buffer中 */
uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);
}
void uart_insert_char(struct uart_port *port, unsigned int status,
unsigned int overrun, unsigned int ch, unsigned int flag)
{
struct tty_port *tport = &port->state->port;
if ((status & port->ignore_status_mask & ~overrun) == 0)
/* 將字節(jié)ch保存到tty_buffer中 */
if (tty_insert_flip_char(tport, ch, flag) == 0)
++port->icount.buf_overrun;
if (status & ~port->ignore_status_mask & overrun)
/* 將字節(jié)ch保存到tty_buffer中 */
if (tty_insert_flip_char(tport, 0, TTY_OVERRUN) == 0)
++port->icount.buf_overrun;
}
static inline int tty_insert_flip_char(struct tty_port *port,
unsigned char ch, char flag)
{
/* 獲取tty_buffer的尾部 */
struct tty_buffer *tb = port->buf.tail;
int change;
......
if (!change && tb->used < tb->size) {
if (~tb->flags & TTYB_NORMAL)
*flag_buf_ptr(tb, tb->used) = flag;
/* 如果tty_buffer中還有空間,則將字節(jié)ch插入tty_buffer中尾部中并返回 */
*char_buf_ptr(tb, tb->used++) = ch;
return 1;
}
/* 如果沒有空間則執(zhí)行__tty_insert_flip_char */
return __tty_insert_flip_char(port, ch, flag);
}
int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag)
{
struct tty_buffer *tb;
int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0;
/* 開辟tty_buffer的空間 */
if (!__tty_buffer_request_room(port, 1, flags))
return 0;
/* 將字節(jié)ch插入剛剛開辟的tty_buffer中 */
tb = port->buf.tail;
if (~tb->flags & TTYB_NORMAL)
*flag_buf_ptr(tb, tb->used) = flag;
*char_buf_ptr(tb, tb->used++) = ch;
return 1;
}
static int __tty_buffer_request_room(struct tty_port *port, size_t size,
int flags)
{
struct tty_bufhead *buf = &port->buf;
struct tty_buffer *b, *n;
int left, change;
/* 獲取剩余空間 */
b = buf->tail;
if (b->flags & TTYB_NORMAL)
left = 2 * b->size - b->used;
else
left = b->size - b->used;
change = (b->flags & TTYB_NORMAL) && (~flags & TTYB_NORMAL);
if (change || left < size) {
/* 開辟tty_buffer的空間 */
n = tty_buffer_alloc(port, size);
if (n != NULL) {
n->flags = flags;
/* 設置緩沖區(qū)到尾部 */
buf->tail = n;
smp_store_release(&b->commit, b->used);
smp_store_release(&b->next, n);
} else if (change)
size = 0;
else
size = left;
}
return size;
}
static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size)
{
struct llist_node *free;
struct tty_buffer *p;
/* 對齊緩沖區(qū)大小 */
size = __ALIGN_MASK(size, TTYB_ALIGN_MASK);
if (size <= MIN_TTYB_SIZE) {
free = llist_del_first(&port->buf.free);
if (free) {
p = llist_entry(free, struct tty_buffer, free);
goto found;
}
}
......
/* 分配緩沖區(qū) */
p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
if (p == NULL)
return NULL;
found:
tty_buffer_reset(p, size);
atomic_add(size, &port->buf.mem_used);
return p;
}
到了這里,我們基本上就完成了 數(shù)據(jù)到tty_buffer 的填充,接下來我們就需要使用 tty_flip_buffer_push 喚醒 前臺線程,下面為代碼實例:
void tty_flip_buffer_push(struct tty_port *port)
{
tty_schedule_flip(port);
}
void tty_schedule_flip(struct tty_port *port)
{
struct tty_bufhead *buf = &port->buf;
/* paired w/ acquire in flush_to_ldisc(); ensures
* flush_to_ldisc() sees buffer data.
*/
smp_store_release(&buf->tail->commit, buf->tail->used);
/* 喚醒workqueue執(zhí)行處理,即調用flush_to_ldisc,該函數(shù)在tty_buffer_init時初始化 */
queue_work(system_unbound_wq, &buf->work);
}
static void flush_to_ldisc(struct work_struct *work)
{
struct tty_port *port = container_of(work, struct tty_port, buf.work);
struct tty_bufhead *buf = &port->buf;
mutex_lock(&buf->lock);
while (1) {
struct tty_buffer *head = buf->head;
struct tty_buffer *next;
int count;
.....
/* 判斷是否有數(shù)據(jù)可讀 */
next = smp_load_acquire(&head->next);
count = smp_load_acquire(&head->commit) - head->read;
if (!count) {
if (next == NULL)
break;
buf->head = next;
tty_buffer_free(port, head);
continue;
}
/* 接收數(shù)據(jù) */
count = receive_buf(port, head, count);
if (!count)
break;
head->read += count;
}
mutex_unlock(&buf->lock);
}
static int receive_buf(struct tty_port *port, struct tty_buffer *head, int count)
{
/* 獲取tty_buffer的緩沖區(qū)地址 */
unsigned char *p = char_buf_ptr(head, head->read);
char *f = NULL;
if (~head->flags & TTYB_NORMAL)
f = flag_buf_ptr(head, head->read);
/*
這里執(zhí)行的是tty_port的client_ops操作集
其初始化是在函數(shù)tty_port_init中進行初始化,該回調的函數(shù)名為tty_port_default_receive_buf
*/
return port->client_ops->receive_buf(port, p, f, count);
}
static int tty_port_default_receive_buf(struct tty_port *port,
const unsigned char *p,
const unsigned char *f, size_t count)
{
int ret;
struct tty_struct *tty;
struct tty_ldisc *disc;
......
/* 使用線路規(guī)程的接收方法對數(shù)據(jù)進行接收 */
ret = tty_ldisc_receive_buf(disc, p, (char *)f, count);
tty_ldisc_deref(disc);
return ret;
}
int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p,
char *f, int count)
{
/*
使用線路規(guī)程的接收方法,前面講說初始化使用的是ntty作為默認的線路規(guī)程
所以這里的回調receive_buf2為n_tty_receive_buf2函數(shù),執(zhí)行該函數(shù)接收數(shù)據(jù)
請助理,這里p是存放有接收數(shù)據(jù)的緩沖區(qū)
*/
if (ld->ops->receive_buf2)
count = ld->ops->receive_buf2(ld->tty, p, f, count);
else {
count = min_t(int, count, ld->tty->receive_room);
if (count && ld->ops->receive_buf)
ld->ops->receive_buf(ld->tty, p, f, count);
}
return count;
}
static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
return n_tty_receive_buf_common(tty, cp, fp, count, 1);
}
static int n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count, int flow)
{
struct n_tty_data *ldata = tty->disc_data;
int room, n, rcvd = 0, overflow;
......
while (1) {
......
/* 在這里對數(shù)據(jù)進行接收 */
/* ignore parity errors if handling overflow */
if (!overflow || !fp || *fp != TTY_PARITY)
__receive_buf(tty, cp, fp, n);
cp += n;
if (fp)
fp += n;
count -= n;
rcvd += n;
}
tty->receive_room = room;
/* Unthrottle if handling overflow on pty */
if (tty->driver->type == TTY_DRIVER_TYPE_PTY) {
if (overflow) {
tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
tty_unthrottle_safe(tty);
__tty_set_flow_change(tty, 0);
}
} else
n_tty_check_throttle(tty);
up_read(&tty->termios_rwsem);
return rcvd;
}
static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
struct n_tty_data *ldata = tty->disc_data;
bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty));
/*
這里判斷分支比較多,需要根據(jù)不同的設置執(zhí)行不同的接收函數(shù)。
不同的接收函數(shù)對數(shù)據(jù)的處理不同,但在最終都會調用put_tty_queue來投放數(shù)據(jù)
下面以n_tty_receive_char_fast函數(shù)為例子進行說明
*/
if (ldata->real_raw)
n_tty_receive_buf_real_raw(tty, cp, fp, count);
else if (ldata->raw || (L_EXTPROC(tty) && !preops))
n_tty_receive_buf_raw(tty, cp, fp, count);
else if (tty->closing && !L_EXTPROC(tty))
n_tty_receive_buf_closing(tty, cp, fp, count);
else {
if (ldata->lnext) {
char flag = TTY_NORMAL;
if (fp)
flag = *fp++;
n_tty_receive_char_lnext(tty, *cp++, flag);
count--;
}
if (!preops && !I_PARMRK(tty))
n_tty_receive_buf_fast(tty, cp, fp, count);
else
n_tty_receive_buf_standard(tty, cp, fp, count);
flush_echoes(tty);
/*
flush_chars回到調用的是uart_ops操作集的uart_flush_chars函數(shù)。
該函數(shù)使能串口發(fā)送應用程的數(shù)據(jù)
*/
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
if (ldata->icanon && !L_EXTPROC(tty))
return;
/* publish read_head to consumer */
smp_store_release(&ldata->commit_head, ldata->read_head);
/* 喚醒前臺進程來接收數(shù)據(jù) */
if (read_cnt(ldata)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
}
}
static void
n_tty_receive_buf_fast(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
struct n_tty_data *ldata = tty->disc_data;
char flag = TTY_NORMAL;
/* 循環(huán)接收字節(jié) */
while (count--) {
if (fp)
flag = *fp++;
if (likely(flag == TTY_NORMAL)) {
/* 從緩沖區(qū)中獲取字節(jié) */
unsigned char c = *cp++;
if (!test_bit(c, ldata->char_map))
/* 將接收到的字節(jié)投放到線路規(guī)程的數(shù)據(jù)緩沖區(qū)中 */
n_tty_receive_char_fast(tty, c);
......
}
}
}
static inline void n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c)
{
struct n_tty_data *ldata = tty->disc_data;
......
/* 投送字節(jié)到線路規(guī)程的數(shù)據(jù)中 */
put_tty_queue(c, ldata);
}
static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
{
/* 將字節(jié)賦值給線路規(guī)程中的read_buf中 */
*read_buf_addr(ldata, ldata->read_head) = c;
/* 以下為read_buf_addr函數(shù)體 */
{
return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
}
/* 記錄緩沖區(qū)當前的頭部下標 */
ldata->read_head++;
}
到了這里,數(shù)據(jù)的 中斷后臺接收流程 就已經完成了。這里我們還需要注意 前臺接收進程 的流程,求該流程比較簡單,相比于 后臺接收流程 來了說調用圖譜十分簡潔。其代碼實例和調用圖譜如下:
tty_read
->n_tty_read
->canon_copy_from_read_buf
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int i;
struct inode *inode = file_inode(file);
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
......
/* 調用線路規(guī)程的read方法,同理這里使用的是ntty作用默認線路規(guī)程,所以其函數(shù)名為n_tty_read */
if (ld->ops->read)
i = ld->ops->read(tty, file, buf, count);
......
return i;
}
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
struct n_tty_data *ldata = tty->disc_data;
unsigned char __user *b = buf;
/* 聲明一個等待隊列實例 */
DEFINE_WAIT_FUNC(wait, woken_wake_function);
int c;
int minimum, time;
ssize_t retval = 0;
long timeout;
int packet;
size_t tail;
......
packet = tty->packet;
tail = ldata->read_tail;
/* 加入等待隊列 */
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_irq(&tty->link->ctrl_lock);
cs = tty->link->ctrl_status;
tty->link->ctrl_status = 0;
spin_unlock_irq(&tty->link->ctrl_lock);
if (put_user(cs, b)) {
retval = -EFAULT;
break;
}
b++;
nr--;
break;
}
if (!input_available_p(tty, 0)) {
up_read(&tty->termios_rwsem);
tty_buffer_flush_work(tty->port);
down_read(&tty->termios_rwsem);
if (!input_available_p(tty, 0)) {
......
/* 如果當前由于某種原因不可讀,則進程讓出當前CPU,等待喚醒 */
timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
timeout);
down_read(&tty->termios_rwsem);
continue;
}
}
if (ldata->icanon && !L_EXTPROC(tty)) {
......
} else {
int uncopied;
if (packet && b == buf) {
if (put_user(TIOCPKT_DATA, b)) {
retval = -EFAULT;
break;
}
b++;
nr--;
}
/* 根據(jù)不同情況將數(shù)據(jù)投放到應用層 */
uncopied = copy_from_read_buf(tty, &b, &nr);
......
}
n_tty_check_unthrottle(tty);
if (b - buf >= minimum)
break;
if (time)
timeout = time;
}
if (tail != ldata->read_tail)
n_tty_kick_worker(tty);
up_read(&tty->termios_rwsem);
/* 移除出等待隊列 */
remove_wait_queue(&tty->read_wait, &wait);
mutex_unlock(&ldata->atomic_read_lock);
if (b - buf)
retval = b - buf;
return retval;
}
static int copy_from_read_buf(struct tty_struct *tty,
unsigned char __user **b,
size_t *nr)
{
struct n_tty_data *ldata = tty->disc_data;
int retval;
size_t n;
bool is_eof;
size_t head = smp_load_acquire(&ldata->commit_head);
size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
retval = 0;
n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail);
n = min(*nr, n);
if (n) {
/* 獲取當前數(shù)據(jù)包的所在地址 */
const unsigned char *from = read_buf_addr(ldata, tail);
/* 以下為read_buf_addr函數(shù)體 */
{
/* 獲取線路規(guī)程中的讀取buffer地址 */
return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
}
/* 將地址拷貝到應用空間,其中b為應用空間buffer地址,from為串口接收數(shù)據(jù)包所在地址 */
retval = copy_to_user(*b, from, n);
n -= retval;
......
*b += n;
*nr -= n;
}
return retval;
}
到了這里,串口讀流程 基本完成了。其 寫流程 框架類似,但數(shù)據(jù)流相反,這里不做講述,有興趣的讀者可以自行閱讀源碼理解。
三、結語
本文更新得有些許慢,主要是平時看代碼寫文章的時間少了,再加上 tty框架 復雜且代碼多,所以閱讀起來理解不是很容易。按照筆者理解,包括筆者在內大部分人一輩子都應該不會自己去寫一個 串口或者tty驅動,但學習 串口和tty驅動 有助于理解和使用 線路規(guī)程,想 串口藍牙 等使用方法相信不少人會遇到過,學習串口驅動框架有助于我們工作生活中開發(fā)和調試。
本文與該系列的其他文章類似,主要在于梳理驅動框架來幫助讀者理解,而非講述使用方法。所以對于很多不影響流程的細節(jié)都省略掉,筆者建議讀者有條件的可以閱讀一遍代碼可以加深理解。如果本文有誤或者有講得不好的地方,請各位讀者指出并海涵。
四、參考鏈接
總結
以上是生活随笔為你收集整理的linux内核调用串口,linux驱动之串口驱动框架的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Product Key Algorith
- 下一篇: Set集合练习题02