19.串口驱动程序学习(一)
2016-04-09 17:14
225 查看
串口驱动程序学习
本文主要实现对串口驱动程序初始化的分析
一、串口驱动中的数据结构
尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实现了UART设备的通用tty驱动层,称为串口核心层,这样,UART驱动的主要任务变成了实现serial_core.c中定义的一组uart_xxx接口而非tty_xxx接口。
1.1下图描述了串行系统间的层次结构关系,可以概括为:
用户应用层 --> 线路规划层 --> TTY层 --> 底层驱动层 --> 物理硬件层
1.2下图是串口核心层在整个tty源文件关系及数据流向中的位置:
其中的xxx_uart.c在此处就是drivers/tty/serial/samsung.c和s3c6400.c
二、串口驱动中的数据结构
1.4使用到的数据结构
Uart驱动程序主要围绕三个关键的数据结构展开(include/linux/serial_core.h中定义):
UART驱动程序结构:struct uart_driver
UART端口结构: struct uart_port
UART相关操作函数结构: struct uart_ops
1.4.1其中一个串口驱动对应一个struct uart_driver当然一个驱动是可以对应多个设备的:
其中的uart_state是设备状态结构结构体:
在uart_open()中:
tty->driver_data = state;
在其他uart_xxx()中:
struct uart_state *state = tty->driver_data;
就可以获取设备私有信息结构体。
1.4.2uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或者IO内存地址等信息--->即一个uart_port对应一个端口
1.4.3uart_ops定义了针对UART的一系列操作
UART信息结构: struct uart_info
二、串口初始化分析
分析过程:
2.1进入到内核的Samsung.c文件中
找到内核模块加载函数:module_init(s3c24xx_serial_modinit);
可以看到加载模块直接调用platform_driver_register,注册了 开发板串口这个平台驱动。
因为把uart驱动注册为platform驱动,当平台驱动与平台设备进行匹配的时候会调用平台总线的match函数,匹配成功后就会调用平台驱动的xxx_probe()函数来进行一系列的初始化工作。
本文主要实现对串口驱动程序初始化的分析
一、串口驱动中的数据结构
尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实现了UART设备的通用tty驱动层,称为串口核心层,这样,UART驱动的主要任务变成了实现serial_core.c中定义的一组uart_xxx接口而非tty_xxx接口。
1.1下图描述了串行系统间的层次结构关系,可以概括为:
用户应用层 --> 线路规划层 --> TTY层 --> 底层驱动层 --> 物理硬件层
1.2下图是串口核心层在整个tty源文件关系及数据流向中的位置:
其中的xxx_uart.c在此处就是drivers/tty/serial/samsung.c和s3c6400.c
二、串口驱动中的数据结构
1.4使用到的数据结构
Uart驱动程序主要围绕三个关键的数据结构展开(include/linux/serial_core.h中定义):
UART驱动程序结构:struct uart_driver
UART端口结构: struct uart_port
UART相关操作函数结构: struct uart_ops
1.4.1其中一个串口驱动对应一个struct uart_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是设备状态结构结构体:
struct uart_state { struct tty_port port; int pm_state; struct circ_buf xmit; struct tasklet_struct tlet; struct uart_port *uart_port; };
在uart_open()中:
tty->driver_data = state;
在其他uart_xxx()中:
struct uart_state *state = tty->driver_data;
就可以获取设备私有信息结构体。
1.4.2uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或者IO内存地址等信息--->即一个uart_port对应一个端口
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); void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); void (*pm)(struct uart_port *, unsigned int state, unsigned int old); 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 */ #define UPIO_DWAPB32 (8) /* DesignWare APB UART (32 bit accesses) */ 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; 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 irq_wake; unsigned char unused[2]; void *private_data; /* generic platform data pointer */ };
1.4.3uart_ops定义了针对UART的一系列操作
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 *, int new); void (*pm)(struct uart_port *, unsigned int state, unsigned int oldstate); int (*set_wake)(struct uart_port *, unsigned int state); /* * 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信息结构: struct uart_info
二、串口初始化分析
分析过程:
2.1进入到内核的Samsung.c文件中
找到内核模块加载函数:module_init(s3c24xx_serial_modinit);
可以看到加载模块直接调用platform_driver_register,注册了 开发板串口这个平台驱动。
因为把uart驱动注册为platform驱动,当平台驱动与平台设备进行匹配的时候会调用平台总线的match函数,匹配成功后就会调用平台驱动的xxx_probe()函数来进行一系列的初始化工作。
int s3c24xx_serial_probe(struct platform_device *dev, struct s3c24xx_uart_info *info) { struct s3c24xx_uart_port *ourport; int ret; dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index); ourport = &s3c24xx_serial_ports[probe_index]; probe_index++; dbg("%s: initialising port %p...\n", __func__, ourport); ret = s3c24xx_serial_init_port(ourport, info, dev); if (ret < 0) goto probe_err; dbg("%s: adding port\n", __func__); uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); platform_set_drvdata(dev, &ourport->port); ret = device_create_file(&dev->dev, &dev_attr_clock_source); if (ret < 0) printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); ret = s3c24xx_serial_cpufreq_register(ourport); if (ret < 0) dev_err(&dev->dev, "failed to add cpufreq notifier\n"); return 0; probe_err: return ret; }
相关文章推荐
- ZOJ Monthly, March 2013
- 隐藏(删除) Preference 小结
- DM9000网卡驱动简析
- C#窗体应用程序添加现有窗口项
- 使用spring自带定时器: @Scheduled
- C++ 读写文件
- 面试题五:从尾到头打印链表|剑指offer
- libgdx 源码导入 idea 工具
- LeetCode *** 112. Path Sum
- MATLAB 神经网络训练参数解释
- CentOS下使用crontab+mysqldump实现定时自动备份数据库
- 查看keystore,在Eclipse和Android Studio众
- 如何取消Excel中的自动超链接
- 优就业JS教程-JavaScript创建对象4种方法详解
- Received ZEND Certified PHP Engineer Certificate
- mongoDB--初识mongoDB&&安装过程
- Pioneer3dx机器人导航过程中转圈的解决方法
- 循环队列的探索
- Eclipse的一些常用命令
- 16链表的反转|剑指offer