ARM-Linux S5PV210 UART驱动(3)----串口核心层、关键结构体、接口关系

尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实现了UART设备的通用tty驱动层,称为串口核心层,这样,UART驱动的主要任务变成了实现serial_core.c中定义的一组uart_xxx接口而非tty_xxx接口。

  uart设备是继tty_driver的又一层封装.实际上uart_driver就是对应tty_driver.在它的操作函数中,将操作转入uart_port.在写操作的时候,先将数据放入一个叫做circ_buf的环形缓存区.然后uart_port从缓存区中取数据,将其写入到串口设备中.当uart_port从serial设备接收到数据时,会将设备放入对应line discipline的缓存区中.这样.用户在编写串口驱动的时候,只先要注册一个uart_driver.它的主要作用是定义设备节点号.然后将对设备的各项操作封装在uart_port.驱动工程师没必要关心上层的流程,只需按硬件规范将uart_port中的接口函数完成就可以了.

1.下图描述了串行系统间的层次结构关系,可以概括为:用户应用层 --> 线路规划层 --> TTY层 --> 底层驱动层 --> 物理硬件层

2.下图是串口核心层在整个tty源文件关系及数据流向中的位置:

  其中的xxx_uart.c在此处就是drivers/serial/samsung.c和s5pv210.c

3.接口关系:

从接口关系图可以看出,用户对uart设备操作的调用关系非常简单,

file_operations => [tty_ldisc_ops] => tty_operations => uart ops

其中tty_ldisc_ops线路规程并不是必要的,依赖于应用层设置是否使用ldisc处理数据。

4.UART驱动的总图:

5.uart驱动常用的数据结构表示如下:

6.Uart驱动程序主要围绕三个关键的数据结构展开(include/linux/serial_core.h中定义):

  UART特定的驱动程序结构定义:struct uart_driver s3c24xx_uart_drv;

  UART端口结构定义: struct s3c24xx_uart_port s3c24xx_serial_ports;

  UART相关操作函数结构定义: struct uart_ops s3c24xx_serial_ops;

【1】uart_driver 封装了tty_driver,使得底层的UART驱动无需关心tty_driver

struct uart_driver {
struct module *owner;
const char *driver_name;
const char *dev_name;
int major;
int minor;
int nr;
struct console *cons; /*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};

其中的uart_state是设备私有信息结构体,

在uart_open()中:

tty->driver_data = state;

在其他uart_xxx()中:

struct uart_state *state = tty->driver_data;

就可以获取设备私有信息结构体。

static struct uart_driver s3c24xx_uart_drv= {
.owner =THIS_MODULE,
.dev_name = "s3c2440_serial", //具体设备名称
.nr =CONFIG_SERIAL_SAMSUNG_UARTS, //定义有几个端口
.cons = S3C24XX_SERIAL_CONSOLE, //console接口
.driver_name =S3C24XX_SERIAL_NAME, //串口名:ttySAC
.major =S3C24XX_SERIAL_MAJOR, //主设备号
.minor =S3C24XX_SERIAL_MINOR, //次设备号
};

一个tty驱动必须注册/注销tty_driver,而一个UART驱动则变为注册/注销uart_driver,使用如下接口:

int uart_register_driver(struct uart_driver *drv);

void uart_unregister_driver(struct uart_driver *drv);

【2】uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或者IO内存地址等信息。

struct uart_port {
spinlock_t lock; /* port lock */
unsigned long iobase; /* in/out[bwl] */
unsigned char __iomem *membase; /* read/write[bwl] */
unsigned int (*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
unsigned int irq; /* irq number */
unsigned long irqflags; /* irq flags */
unsigned int uartclk; /* base uart clock */
unsigned int fifosize; /* tx fifo size */
unsigned char x_char; /* xon/xoff char */
unsigned char regshift; /* reg offset shift */
unsigned char iotype; /* io access style */
unsigned char unused1; #define UPIO_PORT (0)
#define UPIO_HUB6 (1)
#define UPIO_MEM (2)
#define UPIO_MEM32 (3)
#define UPIO_AU (4) /* Au1x00 type IO */
#define UPIO_TSI (5) /* Tsi108/109 type IO */
#define UPIO_DWAPB (6) /* DesignWare APB UART */
#define UPIO_RM9000 (7) /* RM9000 type IO */ unsigned int read_status_mask; /* driver specific */
unsigned int ignore_status_mask; /* driver specific */
struct uart_state *state; /* pointer to parent state */
struct uart_icount icount; /* statistics */ struct console *cons; /* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
unsigned long sysrq; /* sysrq timeout */
#endif upf_t flags; #define UPF_FOURPORT ((__force upf_t) (1 << 1))
#define UPF_SAK ((__force upf_t) (1 << 2))
#define UPF_SPD_MASK ((__force upf_t) (0x1030))
#define UPF_SPD_HI ((__force upf_t) (0x0010))
#define UPF_SPD_VHI ((__force upf_t) (0x0020))
#define UPF_SPD_CUST ((__force upf_t) (0x0030))
#define UPF_SPD_SHI ((__force upf_t) (0x1000))
#define UPF_SPD_WARP ((__force upf_t) (0x1010))
#define UPF_SKIP_TEST ((__force upf_t) (1 << 6))
#define UPF_AUTO_IRQ ((__force upf_t) (1 << 7))
#define UPF_HARDPPS_CD ((__force upf_t) (1 << 11))
#define UPF_LOW_LATENCY ((__force upf_t) (1 << 13))
#define UPF_BUGGY_UART ((__force upf_t) (1 << 14))
#define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))
#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))
/* The exact UART type is known and should not be probed. */
#define UPF_FIXED_TYPE ((__force upf_t) (1 << 27))
#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))
#define UPF_FIXED_PORT ((__force upf_t) (1 << 29))
#define UPF_DEAD ((__force upf_t) (1 << 30))
#define UPF_IOREMAP ((__force upf_t) (1 << 31)) #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))
#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY)) unsigned int mctrl; /* current modem ctrl settings */
unsigned int timeout; /* character-based timeout */
unsigned int type; /* port type */
const struct uart_ops *ops;//UART操作集------->
unsigned int custom_divisor;
unsigned int line; /* port index */
resource_size_t mapbase; /* for ioremap */
struct device *dev; /* parent device */
unsigned char hub6; /* this should be in the 8250 driver */
unsigned char suspended;
unsigned char unused[2];
void *private_data; /* generic platform data pointer */
};
s3c24xx_uart_port 封装了uart_port:
struct s3c24xx_uart_port {
unsigned char rx_claimed;
unsigned char tx_claimed;
unsigned int pm_level;
unsigned long baudclk_rate; unsigned int rx_irq;
unsigned int tx_irq; struct s3c24xx_uart_info *info;
struct s3c24xx_uart_clksrc *clksrc;
struct clk *clk;
struct clk *baudclk;
struct uart_port port; #ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
int channelnum;
};
static struct s3c24xx_uart_port
s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {//串口0;
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX0,
.uartclk = 0,
.fifosize = 16,//定义FIFO缓存区大小 
.ops = &s3c24xx_serial_ops,//串口相关操作函数
.flags = UPF_BOOT_AUTOCONF,
.line = 0,//线路
}
},
[1] = {//串口1;
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX1,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 1,
}
},
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2 [2] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX2,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 2,
}
},
#endif
#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
[3] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX3,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 3,
}
}
#endif
};

在xxx_probe()中:

struct s3c24xx_uart_port *ourport;//s3c24xx_uart_port封装了uart_port
ourport = &s3c24xx_serial_ports[dev->id];//s3c24xx_serial_ports是s3c24xx_uart_port结构体类型的

【3】uart_ops定义了针对UART的一系列操作,

/*
* This structure describes all the operations that can be
* done on the physical hardware.
*/
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *);
void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
unsigned int (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *);
void (*start_tx)(struct uart_port *);
void (*send_xchar)(struct uart_port *, char ch);
void (*stop_rx)(struct uart_port *);
void (*enable_ms)(struct uart_port *);
void (*break_ctl)(struct uart_port *, int ctl);
int (*startup)(struct uart_port *);
void (*shutdown)(struct uart_port *);
void (*flush_buffer)(struct uart_port *);
void (*set_termios)(struct uart_port *, struct ktermios *new,
struct ktermios *old);
void (*set_ldisc)(struct uart_port *);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
int (*set_wake)(struct uart_port *, unsigned int state);
void (*wake_peer)(struct uart_port *); /*
* Return a string describing the type of the port
*/
const char *(*type)(struct uart_port *); /*
* Release IO and memory resources used by the port.
* This includes iounmap if necessary.
*/
void (*release_port)(struct uart_port *); /*
* Request IO and memory resources used by the port.
* This includes iomapping the port if necessary.
*/
int (*request_port)(struct uart_port *);
void (*config_port)(struct uart_port *, int);
int (*verify_port)(struct uart_port *, struct serial_struct *);
int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
};
//一般来说,实现下面的成员函数是UART驱动的主体工作
static struct uart_ops s3c24xx_serial_ops ={
.pm =s3c24xx_serial_pm, //电源管理函数
.tx_empty = s3c24xx_serial_tx_empty, //检车发送FIFO缓冲区是否空
.get_mctrl = s3c24xx_serial_get_mctrl, //是否串口流控
.set_mctrl = s3c24xx_serial_set_mctrl, //是否设置串口流控cts
.stop_tx =s3c24xx_serial_stop_tx, //停止发送
.start_tx =s3c24xx_serial_start_tx, //启动发送
.stop_rx =s3c24xx_serial_stop_rx, //停止接收
.enable_ms = s3c24xx_serial_enable_ms, //空函数
.break_ctl = s3c24xx_serial_break_ctl, //发送break信号
.startup =s3c24xx_serial_startup, //串口发送/接收,以及中断申请初始配置函数
.shutdown = s3c24xx_serial_shutdown, //关闭串口
.set_termios = s3c24xx_serial_set_termios,//串口clk,波特率,数据位等参数设置
.type = s3c24xx_serial_type, // CPU类型关于串口
.release_port =s3c24xx_serial_release_port, //释放串口
.request_port =s3c24xx_serial_request_port, //申请串口
.config_port = s3c24xx_serial_config_port, //串口的一些配置信息info
.verify_port = s3c24xx_serial_verify_port, //串口检测
.wake_peer = s3c24xx_serial_wake_peer,
};

而在serial_core.c中定义了tty_operations的实例,包含uart_open();uart_close();uart_send_xchar()等成员函数,这些函数借助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,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = uart_poll_init,
.poll_get_char = uart_poll_get_char,
.poll_put_char = uart_poll_put_char,
#endif
};

从下面的例子中可以看出串口核心层的tty_operations与uart_ops的关系:

/*
* This function is used to send a high-priority XON/XOFF character to
* the device
*/
static void uart_send_xchar(struct tty_struct *tty, char ch)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
unsigned long flags; if (port->ops->send_xchar)/*如果uart_ops中实现了send_xchar成员函数*/
port->ops->send_xchar(port, ch);
else {
port->x_char = ch;
if (ch) {
spin_lock_irqsave(&port->lock, flags);
port->ops->start_tx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
}
}

这个例子的调用关系如下:

send_xchar  ---->  uart_send_xchar ---->  start_tx  --->  s3c24xx_serial_start_tx

ARM-Linux S5PV210 UART驱动(转)的更多相关文章

  1. ARM-Linux S5PV210 UART驱动(4)----串口驱动初始化过程

    对于S5PV210 UART驱动来说,主要关心的就是drivers/serial下的samsung.c和s5pv210.c连个文件. 由drivers/serial/Kconfig: config S ...

  2. ARM-Linux S5PV210 UART驱动(3)----串口核心层、关键结构体、接口关系

    尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实 ...

  3. arm linux利用alsa驱动并使用usb音频设备

    一.背景: arm linux的内核版本是3.13.0 二.准备工作 添加alsa驱动到内核中,也就是在编译内核的时候加入以下选项: 接下来就重新编译内核即可 三.交叉编译alsa-lib和alsa- ...

  4. ARM-Linux S5PV210 UART驱动(2)---- 终端设备驱动

    在Linux中,UART串口驱动完全遵循tty驱动的框架结构,但是进行了底层操作的再次封装,所以先介绍tty终端设备驱动. 一.终端设备 1.串行端口终端(/dev/ttySACn) 2.伪终端(/d ...

  5. ARM-Linux S5PV210 UART驱动(6)----platform device的添加

    开发板是飞凌OK210 arch/arm/mach-s5pv210/mach-smdkc110.c 首先是UART的寄存器默认配置信息: /* Following are default values ...

  6. ARM-Linux S5PV210 UART驱动(5)----串口的open操作(tty_open、uart_open)

    串口驱动初始化后,串口作为字符驱动也已经注册到系统了,/dev目录下也有设备文件节点了. 那接下来uart的操作是如何进行的呢? 操作硬件之前都是要先open设备,先来分析下这里的open函数具体做了 ...

  7. ARM-Linux S5PV210 UART驱动(1)----用户手册中的硬件知识

    一.概述 The Universal Asynchronous Receiver and Transmitter (UART) in S5PV210 provide four independent ...

  8. Linux UART驱动分析

    1. 介绍 8250是IBM PC及兼容机使用的一种串口芯片; 16550是一种带先进先出(FIFO)功能的8250系列串口芯片; 16550A则是16550的升级版本, 修复了FIFO相关BUG, ...

  9. 调试exynos4412—ARM嵌入式Linux—LEDS/GPIO驱动之二

    /** ****************************************************************************** * @author    暴走的小 ...

随机推荐

  1. 10 router

    https://router.vuejs.org/zh/guide/advanced/navigation-guards.html 1.路由守卫beforeEach router.beforeEach ...

  2. String源码浅析

    如果问你,开发过程中用的最多的类是哪个?你可能回答是HashMap,一个原因就是HashMap的使用量的确很多,还有就是HashMap的内容在面试中经常被问起. 但是在开发过程中使用最多的类其实并不是 ...

  3. springboot2.x基础教程:动手制作一个starter包

    上一篇博客介绍了springboot自动装配的原理.springboot本身有丰富的spring-boot-starter-xx集成组件,这一篇趁热打铁加深理解,我们利用springboot自动装配的 ...

  4. JS基础回顾_滚动条

    // log function getScrollOffset() { if (window.pageXOffset) { return { x: window.pageXOffset, y: win ...

  5. (超详细)动手编写-链表(Java实现)

    目录 前言 概念 链表的设计 完整代码 List接口 抽象父类设计 链表-LinkedList 虚拟头结点 概念 结构设计 方法变动 双向链表 概念 双向链表设计 方法变动 循环链表 单向循环链表 双 ...

  6. oracle之三rman 不完全恢复

    rman 不完全恢复 9.1 rman 不完全恢复的三个标准模式:基于time.基于scn和基于sequence: 范例1:恢复过去某个时间点误操作,一般使用基于time或scn. 1)环境:有一套全 ...

  7. [Liunx]apt-get安装软件:依赖冲突问题及解决

    正常使用apt-get install安装出现依赖冲突问题: 大概是这样: ga@ubuntu:$ sudo apt-get install gcc-5-base:i386 正在读取软件包列表... ...

  8. [剑指Offer]56-数组中数字出现的次数(位运算)

    题目一 数组中只出现一次的数字 题目 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字 题解 异或. 先考虑:数组中只有一个数字只出现了一次,其他数字都出现了 ...

  9. vue父子组件状态同步的最佳方式续章(v-model篇)

    大家好!我是木瓜太香!一名前端工程师,之前写过一篇<vue父子组件状态同步的最佳方式>,这篇文章描述了大多数情况下的父子组件同步的最佳方式,也是被开源中国官方推荐了,在这里表示感谢! 这次 ...

  10. 【译】Visual Studio 2019 的 Local Process with Kubernetes

    今天,我们自豪地宣布 Local Process with Kubernetes 的预览版已加入到 Visual Studio 2019 16.7 Preview 2 中.  Local Proces ...