在嵌入式或PC我们经常会遇到串行子系统,常遇到的场合如下:
- 通过RS-232串行链路运行终端会话;
- 通过拔号、小区蜂窝或软件调制解调器连接到因特网;
- 和触摸控制器、智能卡、蓝牙芯片或红外dongle等使用串行传输方式的设备接口;
- 使用USB-串口端口的转换器模拟串行端口;
- 通过RS-485通信,RS-485在RS-232的基础上支持多个节点,传输距离更远,抗噪性能更强.
1.LINUX下串行设备驱动程序的层次架构:
如图:
因此,以下而上可以分为:硬件->UART底层驱动->TTY驱动->线路规程->用户空间
关于其中串行子系统与内核源文件的映射关系如下图:
[注:]LINUX下串行设备驱动和用户之间的交互并不像单纯的字符驱动程序那样将接口直接暴露给内核的系统调用.UART驱动程序是提供服务给另TTY层,再由TTY层传递给线路规程层,由线路规程层和用户空间的I/O调用关联.
2.由上述可知,LINUX下串行口设备驱动主要包括三层:UART驱动层、TTY驱动层和线路规程三个层次组成.
2-1.UART驱动层:
UART驱动程序围绕3个关键的数据结构展开.这3个数据结构都定义于include/linux/serial_core.h
1).特定的UART相关的驱动程序结构,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设备赋值并进行一系列针对具体的UART的个性赋值.如S3C2440:
static struct uart_driver s3c24xx_uart_drv = {
.owner = THIS_MODULE,
.dev_name = "s3c2410_serial",
.nr = CONFIG_SERIAL_SAMSUNG_UARTS,
.cons = S3C24XX_SERIAL_CONSOLE,
.driver_name = S3C24XX_SERIAL_NAME,
.major = S3C24XX_SERIAL_MAJOR,
.minor = S3C24XX_SERIAL_MINOR,
};
2).struct uart_port.UART驱动程序拥有的每个端口都存在uart_port结构这样的一个实例.主要记录一些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);
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;
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 */
};
和struct uart_driver一样,会被具体的uart port口重载.如S3C2440:
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX0,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 0,
}
},
[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
};
3).struct uart_ops.这个结构是每个UART驱动程序必须支持的物理硬件上可完成的操作的入口函数的超集.如完成具体平台(如S3C2440)的寄存器配置、读写等动作.
struct uart_port;
struct serial_struct;
struct device;
/*
* 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);
/*
* 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
};
此结构体与具体的硬件平台有关.如S3C2440:
static 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,
.enable_ms = s3c24xx_serial_enable_ms,
.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,
};
UART驱动程序为了将自身和内核联系起来,必须完成两个重要的步骤:
1).通过uart_register_driver(struct uart_driver)函数向串行核心注册;
2).调用uart_add_one_port(struct uart_driver *,struct uart_port *)支持其支持的每个端口.
小结:
这些结构体数据及操作是LINUX平台UART驱动程序中最基本的共同点.
实例:RS-485
RS-232的传输距离只有数米,在嵌入式领域为了更长的传输距离,常常会采用RS-485,RS-485是半双工的UART操作.RS-485的常用电路图如下:
因此,从FIFO发送数据至电缆之前,UART设备驱动程序需要禁用接收器,激活发送器.这个动作只需要设备相应的GPIO即可.因此,在串行层中恰当的时机激活/禁用RS-485的接收器/发送器.如果太早禁用了发送器,它有可能没有足够的时间清空发送FIFO中最后的几字节数据,这将导致数据丢失;太晚禁用发送器,就会阻止此段时间的数据接收,这将导致接收数据丢失.
2-2.TTY驱动程序
TTY驱动层有三个结构非常重要.
1).定义于include/linux/tty.h中的struct tty_struct.此结构包含了打开的tty相关的所有状态信息.如下:
/*
* Where all of the state associated with a tty is kept while the tty
* is open. Since the termios state should be kept even if the tty
* has been closed --- for things like the baud rate, etc --- it is
* not stored here, but rather a pointer to the real state is stored
* here. Possible the winsize structure should have the same
* treatment, but (1) the default 80x24 is usually right and (2) it's
* most often used by a windowing system, which will set the correct
* size each time the window is created or resized anyway.
* - TYT, 9/14/92
*/
struct tty_operations;
struct tty_struct {
int magic;
struct kref kref;
struct tty_driver *driver;
const struct tty_operations *ops;
int index;
/* Protects ldisc changes: Lock tty not pty */
struct mutex ldisc_mutex;
struct tty_ldisc *ldisc;
struct mutex termios_mutex;
spinlock_t ctrl_lock;
/* Termios values are protected by the termios mutex */
struct ktermios *termios, *termios_locked;
struct termiox *termiox; /* May be NULL for unsupported */
char name[64];
struct pid *pgrp; /* Protected by ctrl lock */
struct pid *session;
unsigned long flags;
int count;
struct winsize winsize; /* termios mutex */
unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
unsigned char low_latency:1, warned:1;
unsigned char ctrl_status; /* ctrl_lock */
unsigned int receive_room; /* Bytes free for queue */
struct tty_struct *link;
struct fasync_struct *fasync;
struct tty_bufhead buf; /* Locked internally */
int alt_speed; /* For magic substitution of 38400 bps */
wait_queue_head_t write_wait;
wait_queue_head_t read_wait;
struct work_struct hangup_work;
void *disc_data;
void *driver_data;
struct list_head tty_files;
#define N_TTY_BUF_SIZE 4096
/*
* The following is data for the N_TTY line discipline. For
* historical reasons, this is included in the tty structure.
* Mostly locked by the BKL.
*/
unsigned int column;
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
unsigned char closing:1;
unsigned char echo_overrun:1;
unsigned short minimum_to_wake;
unsigned long overrun_time;
int num_overrun;
unsigned long process_char_map[256/(8*sizeof(unsigned long))];
char *read_buf;
int read_head;
int read_tail;
int read_cnt;
unsigned lon