S3C2410 UART 的驱动实例

S3C2410 串口硬件描述

S3C2410 内部具有 3 个独立的 UART 控制器,每个控制器都可以工作在 Interrupt(中断)模式或 DMA(直接内存访问)模式,也就是说 UART 控制器可以在 CPU 与UART 控制器传送资料的时候产生中断或 DMA 请求。S3C2410 集成的每个 UART 均具有 16 字节的 FIFO,支持的最高波特率可达到 230.4Kbit/s。

ULCONn(UART Line Control Register)寄存器用于 S3C2410 UART 的线路控制,用于设置模式、每帧的数据位数、停止位数及奇偶校验,如下表所示。


UCONn(UART Control Register)寄存器用于从整体上控制 S3C2410 UART 的中断模式及工作模式(DMA、中断、轮询)等,如下表所示。


UFCONn (UART FIFO Conrtol Register)寄存器用于 S3C2410 UART 的 FIFO 控制,用于控制 FIFO 中断的触发级别以及复位时是否清空 FIFO 中的内容,如下表所示


下面代码所示为 UFCONn 寄存器的位掩码和默认设置(使能 FIFO、 FIFO Tx为空时触发中断,Rx FIFO 中包含 8 个字节时触发中断)。

#define S3C2410_UFCON_FIFOMODE (1<<0)
#define S3C2410_UFCON_TXTRIG0 (0<<6)
#define S3C2410_UFCON_RXTRIG8 (1<<4)
#define S3C2410_UFCON_RXTRIG12 (2<<4)
#define S3C2410_UFCON_RESETBOTH (3<<1)
#define S3C2410_UFCON_RESETTX (1<<2)
#define S3C2410_UFCON_RESETRX (1<<1)
#define S3C2410_UFCON_DEFAULT  (S3C2410_UFCON_FIFOMODE | \
                                  S3C2410_UFCON_TXTRIG0 | \
                                  S3C2410_UFCON_RXTRIG8 )
UFSTATn(UART FIFO Status Register)寄存器用于表示 UART FIFO 的状态,如下表所示。

由于 UFSTATn 寄存器中的 Tx FIFO 中数据的数量和 Rx FIFO 中数据的数量分别占 据 [7:4] 和 [3:0] 位 , 因 此 定 义 S3C2410_UFSTAT_TXSHIFT 和S3C2410_UFSTAT_RXSHIFT 分别为 4 和 0,下面代码所示为 UFSTATn 寄存器的位掩码等信息。

#define S3C2410_UFSTAT_TXFULL (1<<9)
#define S3C2410_UFSTAT_RXFULL (1<<8)
#define S3C2410_UFSTAT_TXMASK (15<<4)
#define S3C2410_UFSTAT_TXSHIFT (4)
#define S3C2410_UFSTAT_RXMASK  (15<<0)
#define S3C2410_UFSTAT_RXSHIFT (0)
UTXHn(UART Transmit Buffer Register)和 URXHn(UART Receive Buffer Register)分别是 UART 发送和接收数据寄存器,这两个寄存器存放着发送和接收的数据。
UTRSTATn(UART TX/RX Status Register)寄存器反映了发送和接收的状态,通过这个寄存器,驱动程序可以判断 URXHn 中是否有数据接收到或 UTXHn 是否为空,这个寄存器主要在非 FIFO 模式时使用。
UMCONn(UART Modem Control Register)用于 S3C2410 UART 的 modem 控制,设置是否使用 RTS 流控,若采用流控,可选择自动流控(Auto Flow Control,AFC)或由软件控制 RTS 信号的“高”或“低”电平。

S3C2410 串口驱动的数据结构
S3C2410 串口驱动中 uart_driver 结构体实例的定义如下面代码所示,设备名为“s3c2410_serial”,驱动名为“ttySAC”。

#define S3C24XX_SERIAL_NAME "ttySAC"
#define S3C24XX_SERIAL_DEVFS "tts/"
#define S3C24XX_SERIAL_MAJOR 204
#define S3C24XX_SERIAL_MINOR 64

static struct uart_driver s3c24xx_uart_drv =
{
  .owner = THIS_MODULE,
  .dev_name = "s3c2410_serial",
  .nr = 3,
  .cons  = S3C24XX_SERIAL_CONSOLE,
  .driver_name = S3C24XX_SERIAL_NAME,
  .devfs_name = S3C24XX_SERIAL_DEVFS,
  .major = S3C24XX_SERIAL_MAJOR,
  .minor = S3C24XX_SERIAL_MINOR,
};
S3C2410 串口驱动中定义了结构体 s3c24xx_uart_port,该结构体中封装了 uart_port结 构 体 及 一 些 针 对 S3C2410 UART 的 附 加 信 息 , 下面代码所 示 为s3c24xx_uart_port 结构体及其实例 s3c24xx_serial_ports[]数组。
struct s3c24xx_uart_port
{
  unsigned char rx_claimed;
  unsigned char tx_claimed;

  struct s3c24xx_uart_info *info;
  struct s3c24xx_uart_clksrc *clksrc;
  struct clk *clk;
  struct clk *baudclk;
  struct uart_port port;
};
static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
	[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 NR_PORTS > 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
};
S3C2410 串口驱动中 uart_ops 结构体实例的定义如代码如下所示,将一系列 s3c24xx_ serial_函数赋值给 uart_ops 结构体的成员。
static struct uart_ops s3c24xx_serial_ops = {
	.pm		= s3c24xx_serial_pm,
	.tx_empty	= s3c24xx_serial_tx_empty,//发送缓冲区空
	.get_mctrl	= s3c24xx_serial_get_mctrl,//得到 modem 控制设置
	.set_mctrl	= s3c24xx_serial_set_mctrl,//设置 modem 控制(MCR)
	.stop_tx	= s3c24xx_serial_stop_tx,//停止接收字符
	.start_tx	= s3c24xx_serial_start_tx,//开始传输字符
	.stop_rx	= s3c24xx_serial_stop_rx,//停止接收字符
	.enable_ms	= s3c24xx_serial_enable_ms,// modem 状态中断使能
	.break_ctl	= s3c24xx_serial_break_ctl,// 控制 break 信号的传输
	.startup	= s3c24xx_serial_startup,//启动端口
	.shutdown	= s3c24xx_serial_shutdown,// 禁用端口
	.set_termios	= s3c24xx_serial_set_termios,//改变端口参数
	.type		= s3c24xx_serial_type,//返回描述特定端口的常量字符串指针
	.release_port	= s3c24xx_serial_release_port,/释放端口占用的内存和I/O 资源
	.request_port	= s3c24xx_serial_request_port,
	.config_port	= s3c24xx_serial_config_port,
	.verify_port	= s3c24xx_serial_verify_port,//验证新的串行端口信息
};
回过头来看 s3c24xx_uart_port 结构体,其中的 s3c24xx_uart_info 成员是一些针对 S3C2410 UART 的信息,其定义如代码清单如下所示。
static struct s3c24xx_uart_info s3c2410_uart_inf = {
	.name		= "Samsung S3C2410 UART",
	.type		= PORT_S3C2410,
	.fifosize	= 16,
	.rx_fifomask	= S3C2410_UFSTAT_RXMASK,
	.rx_fifoshift	= S3C2410_UFSTAT_RXSHIFT,
	.rx_fifofull	= S3C2410_UFSTAT_RXFULL,
	.tx_fifofull	= S3C2410_UFSTAT_TXFULL,
	.tx_fifomask	= S3C2410_UFSTAT_TXMASK,
	.tx_fifoshift	= S3C2410_UFSTAT_TXSHIFT,
	.get_clksrc	= s3c2410_serial_getsource,
	.set_clksrc	= s3c2410_serial_setsource,
	.reset_port	= s3c2410_serial_resetport,
};
在 S3C2410 串口驱动中,针对 UART 的设置(UCONn、ULCONn、UFCONn 寄存器等)被封装到 s3c2410_uartcfg 结构体中,其定义如下所示。
struct s3c2410_uartcfg {
	unsigned char	   hwport;	 /* hardware port number */
	unsigned char	   unused;
	unsigned short	   flags;
	upf_t		   uart_flags;	 /* default uart flags */

	unsigned long	   ucon;	 /* value of ucon for port */
	unsigned long	   ulcon;	 /* value of ulcon for port */
	unsigned long	   ufcon;	 /* value of ufcon for port */

	struct s3c24xx_uart_clksrc *clocks;
	unsigned int		    clocks_size;
};
S3C2410 串口驱动的初始化与释放
在 S3C2410 串 口 驱 动 的 模 块 加 载 函 数 中 会 调 用 uart_register_driver() 注 册s3c24xx_uart_drv 这个 uart_driver,同时经过 s3c2410_serial_init()→s3c24xx_serial_init()→ platform_driver_register() 的 调 用 导 致 s3c24xx_serial_probe() 被 执 行 , 而
s3c24xx_serial_probe()函数中会调用 s3c24xx_serial_ init_port()初始化 UART 端口,并调用 uart_add_one_port()添加端口,整个过程的对应代码如下所示:

static int __init s3c24xx_serial_modinit(void)
{
	int ret;

	ret = uart_register_driver(&s3c24xx_uart_drv);
	if (ret < 0) {
		printk(KERN_ERR "failed to register UART driver\n");
		return -1;
	}

	s3c2400_serial_init();
	s3c2410_serial_init();
	s3c2412_serial_init();
	s3c2440_serial_init();

	return 0;
}
static inline int s3c2440_serial_init(void)
{
	return s3c24xx_serial_init(&s3c2440_serial_drv, &s3c2440_uart_inf);
}
static int s3c24xx_serial_init(struct platform_driver *drv,
			       struct s3c24xx_uart_info *info)
{
	dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);
	return platform_driver_register(drv);
}
probe函数:

static int s3c2440_serial_probe(struct platform_device *dev)
{
	dbg("s3c2440_serial_probe: dev=%p\n", dev);
	return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
}
static 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", __FUNCTION__, ourport);

	ret = s3c24xx_serial_init_port(ourport, info, dev);//初始化 UART 端口
	if (ret < 0)
		goto probe_err;

	dbg("%s: adding port\n", __FUNCTION__);
	uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);//添加 uart_port
	platform_set_drvdata(dev, &ourport->port);

	return 0;

 probe_err:
	return ret;
}
s3c24xx_serial_init_port
static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
				    struct s3c24xx_uart_info *info,
				    struct platform_device *platdev)
{
	struct uart_port *port = &ourport->port;
	struct s3c2410_uartcfg *cfg;
	struct resource *res;

	dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);

	if (platdev == NULL)
		return -ENODEV;

	cfg = s3c24xx_dev_to_cfg(&platdev->dev);

	if (port->mapbase != 0)
		return 0;

	if (cfg->hwport > 3)
		return -EINVAL;

	/* setup info for port *//* 为端口设置 info 成员 */
	port->dev	= &platdev->dev;
	ourport->info	= info;

	/* copy the info in from provided structure *//* 初始化 fifosize */
	ourport->port.fifosize = info->fifosize;

	dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport);

	port->uartclk = 1;

	if (cfg->uart_flags & UPF_CONS_FLOW) {/* 如果使用流控 */
		dbg("s3c24xx_serial_init_port: enabling flow control\n");
		port->flags |= UPF_CONS_FLOW;
	}

	/* sort our the physical and virtual addresses for each UART */
        /* 利用平台资源中记录的信息初始化 UART 端口的基地址、中断号 */
	res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		printk(KERN_ERR "failed to find memory resource for uart\n");
		return -EINVAL;
	}

	dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);

	port->mapbase	= res->start;
	port->membase	= S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART);
	port->irq	= platform_get_irq(platdev, 0);
	if (port->irq < 0)
		port->irq = 0;

	ourport->clk	= clk_get(&platdev->dev, "uart");

	dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n",
	    port->mapbase, port->membase, port->irq, port->uartclk);

	/* reset the fifos (and setup the uart) */
	s3c24xx_serial_resetport(port, cfg);/* 复位 fifo 并设置 UART */
	return 0;
}
s3c24xx_serial_resetport

static inline int s3c24xx_serial_resetport(struct uart_port * port,
					   struct s3c2410_uartcfg *cfg)
{
	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);

	return (info->reset_port)(port, cfg);
}
这个info_reset_port就是 s3c2410_serial_resetport

static int s3c2410_serial_resetport(struct uart_port *port,
				    struct s3c2410_uartcfg *cfg)
{
	dbg("s3c2410_serial_resetport: port=%p (%08lx), cfg=%p\n",
	    port, port->mapbase, cfg);

	wr_regl(port, S3C2410_UCON,  cfg->ucon);
	wr_regl(port, S3C2410_ULCON, cfg->ulcon);

	/* reset both fifos */
        /* 复位两个 fifo */
	wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
	wr_regl(port, S3C2410_UFCON, cfg->ufcon);

	return 0;
}
在 S3C2410 串口驱动模块被卸载时,它会最终调用 uart_remove_one_port()释放uart_port,并调用 uart_unregister_driver()注销 uart_driver,如代码如下所示。
static void _ _exit s3c24xx_serial_modexit(void)
{
   s3c2410_serial_exit();
   //注销 uart_driver
   uart_unregister_driver(&s3c24xx_uart_drv);
}

static inline void s3c2410_serial_exit(void)
{
   //注销平台驱动
   platform_driver_unregister(&s3c2410_serial_drv);
}

static int s3c24xx_serial_remove(struct platform_device *dev)
{
   struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
   //移除 UART 端口
   if (port)
     uart_remove_one_port(&s3c24xx_uart_drv, port);
   return 0;
}
上述代码中对 S3C24xx_serial_remove()的调用发生在 platform_driver_unregister()之后,由于 S3C2410 的 UART 是集成于 SoC 芯片内部的一个独立的硬件单元,因此也被作为一个平台设备而定义。
S3C2410 串口数据收发
S3C2410 串口驱动 uart_ops 结构体的 startup ()成员函数 s3c24xx_serial_startup()用于启动端口,申请端口的发送、接收中断,使能端口的发送和接收,其实现如代码所示。
static int s3c24xx_serial_startup(struct uart_port *port)
{
	struct s3c24xx_uart_port *ourport = to_ourport(port);
	int ret;

	dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
	    port->mapbase, port->membase);

	rx_enabled(port) = 1;//置接收使能状态为 1
        //申请接收中断
	ret = request_irq(RX_IRQ(port),
			  s3c24xx_serial_rx_chars, 0,
			  s3c24xx_serial_portname(port), ourport);

	if (ret != 0) {
		printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port));
		return ret;
	}

	ourport->rx_claimed = 1;

	dbg("requesting tx irq...\n");

	tx_enabled(port) = 1;//置发送使能状态为 1
        //申请发送中断
	ret = request_irq(TX_IRQ(port),
			  s3c24xx_serial_tx_chars, 0,
			  s3c24xx_serial_portname(port), ourport);

	if (ret) {
		printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port));
		goto err;
	}

	ourport->tx_claimed = 1;

	dbg("s3c24xx_serial_startup ok\n");

	/* the port reset code should have done the correct
	 * register setup for the port controls */
        /* 端口复位代码应该已经为端口控制设置了正确的寄存器 */
	return ret;

 err:
	s3c24xx_serial_shutdown(port);
	return ret;
}
s3c24xx_serial_startup()的“反函数”为 s3c24xx_serial_shutdown(),其释放中断,禁止发送和接收,实现如代码 所示。
static void s3c24xx_serial_shutdown(struct uart_port *port)
{
	struct s3c24xx_uart_port *ourport = to_ourport(port);

	if (ourport->tx_claimed) {
		free_irq(TX_IRQ(port), ourport);
		tx_enabled(port) = 0;//置发送使能状态为 0\
		ourport->tx_claimed = 0;
	}

	if (ourport->rx_claimed) {
		free_irq(RX_IRQ(port), ourport);
		ourport->rx_claimed = 0;
		rx_enabled(port) = 0;//置接收使能状态为 0
	}
}
S3C2410 串口驱动 uart_ops 结构体的 tx_empty()成员函数 s3c24xx_serial_tx_empty()用于判断发送缓冲区是否为空,其实现如代码所示,当使能 FIFO 模式的时候,判断 UFSTATn 寄存器,否则判断 UTRSTATn 寄存器的相应位。
static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
{       //fifo 模式,检查 UFSTATn 寄存器
	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
	unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
	unsigned long ufcon = rd_regl(port, S3C2410_UFCON);

	if (ufcon & S3C2410_UFCON_FIFOMODE) {
		if ((ufstat & info->tx_fifomask) != 0 ||   //Tx fifo 数据数非 0
		    (ufstat & info->tx_fifofull))  // Tx fifo 满
			return 0;   //0:非空

		return 1;   //1:空
	}

	return s3c24xx_serial_txempty_nofifo(port);
}
static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
{
	return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);//非 fifo 模式,检查 UTRSTATn 寄存器
}

S3C2410 串口驱动 uart_ops 结构体的 start_tx()成员函数 s3c24xx_serial_start_tx(),用于启动发送, stop_rx()成员函数 s3c24xx_serial_stop_rx()用于停止发送,代码如下所示:

static void s3c24xx_serial_start_tx(struct uart_port *port)
{
	if (!tx_enabled(port)) {//如果端口发送未使能
		if (port->flags & UPF_CONS_FLOW)
			s3c24xx_serial_rx_disable(port);

		enable_irq(TX_IRQ(port));//使能发送中断
		tx_enabled(port) = 1;//置端口发送使能状态为 1
	}
}
static void s3c24xx_serial_stop_rx(struct uart_port *port)
{
	if (rx_enabled(port)) {//如果端口发送已使能
		dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
		disable_irq(RX_IRQ(port));//禁止发送中断
		rx_enabled(port) = 0;//置端口发送使能状态为 0
	}
}
在 S3C2410 串口驱动中,与数据收发关系最密切的函数不是上述的 uart_ops 成员函 数 , 而 是 s3c24xx_serial_startup() 为 发 送 和 接 收 中 断 注 册 的 中 断 处 理 函 数 s3c24xx_serial_rx_chars()和 s3c24xx_ serial_tx_chars()。
s3c24xx_serial_rx_chars()读取 URXHn 寄存 器以获得 接收到 的字 符,并 调 用 uart_insert_char()将该字符添加到 tty 设备的 flip 缓冲区中,当接收到 64 个字符或者不再能接收字符后,调用 tty_flip_ buffer_push()函数向上层“推”tty 设备的 flip 缓冲,其实现如代码如下所示:

static irqreturn_t
s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
	struct s3c24xx_uart_port *ourport = dev_id;
	struct uart_port *port = &ourport->port;//获得 uart_port
	struct tty_struct *tty = port->info->tty;//获得 tty_struct
	unsigned int ufcon, ch, flag, ufstat, uerstat;
	int max_count = 64;

	while (max_count-- > 0) {
		ufcon = rd_regl(port, S3C2410_UFCON);
		ufstat = rd_regl(port, S3C2410_UFSTAT);
                //如果接收到 0 个字符
		if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
			break;

		uerstat = rd_regl(port, S3C2410_UERSTAT);
		ch = rd_regb(port, S3C2410_URXH);//读出字符

		if (port->flags & UPF_CONS_FLOW) {
			int txe = s3c24xx_serial_txempty_nofifo(port);

			if (rx_enabled(port)) { //如果端口为使能接收状态
				if (!txe) {//如果发送缓冲区为空
					rx_enabled(port) = 0;//置端口使能接收状态为 0
					continue;
				}
			} else {//端口为禁止接收状态
				if (txe) {//如果发送缓冲区非空
					ufcon |= S3C2410_UFCON_RESETRX;
					wr_regl(port, S3C2410_UFCON, ufcon);
					rx_enabled(port) = 1;//置端口使能接收状态为 1
					goto out;
				}
				continue;
			}
		}

		/* insert the character into the buffer */
                /* 将接收到的字符写入 buffer */
		flag = TTY_NORMAL;
		port->icount.rx++;

		if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
			dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
			    ch, uerstat);

			/* check for break */
			if (uerstat & S3C2410_UERSTAT_BREAK) {
				dbg("break!\n");
				port->icount.brk++;
				if (uart_handle_break(port))
				    goto ignore_char;
			}

			if (uerstat & S3C2410_UERSTAT_FRAME)
				port->icount.frame++;
			if (uerstat & S3C2410_UERSTAT_OVERRUN)
				port->icount.overrun++;

			uerstat &= port->read_status_mask;

			if (uerstat & S3C2410_UERSTAT_BREAK)
				flag = TTY_BREAK;
			else if (uerstat & S3C2410_UERSTAT_PARITY)
				flag = TTY_PARITY;
			else if (uerstat & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
				flag = TTY_FRAME;
		}

		if (uart_handle_sysrq_char(port, ch))
			goto ignore_char;
                //插入字符到 tty 设备的 flip 缓冲
		uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);

	ignore_char:
		continue;
	}
	tty_flip_buffer_push(tty);//刷新 tty 设备的 flip 缓冲

 out:
	return IRQ_HANDLED;
}
上述代码的 uart_insert_char()函数是串口核心层对 tty_insert_flip_char()的封装,它作为内联函数被定义于 serial_core.h 文件中。

s3c24xx_serial_tx_chars()读取 uart_info 中环形缓冲区中的字符,写入调用 UTXHn 寄存器。

static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
	struct s3c24xx_uart_port *ourport = id;
	struct uart_port *port = &ourport->port;
	struct circ_buf *xmit = &port->info->xmit;//得到环形缓冲区
	int count = 256;//最多一次发 256 个字符

	if (port->x_char) {//如果定义了 xchar,发送
		wr_regb(port, S3C2410_UTXH, port->x_char);
		port->icount.tx++;
		port->x_char = 0;
		goto out;
	}

	/* if there isnt anything more to transmit, or the uart is now
	 * stopped, disable the uart and exit
	*/
         /* 如果没有更多的字符需要发送,或者 UART Tx 停止,则停止 UART 并退出 */
	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
		s3c24xx_serial_stop_tx(port);
		goto out;
	}

	/*  try and drain the buffer... */
        /* 尝试把环行 buffer 中的数据发空 */
	while (!uart_circ_empty(xmit) && count-- > 0) {
		if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
			break;

		wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
		port->icount.tx++;
	}
        /* 如果环形缓冲区中剩余的字符少于 WAKEUP_CHARS,唤醒上层 */
	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
		uart_write_wakeup(port);
        //如果发送环形 buffer 为空
	if (uart_circ_empty(xmit))
		s3c24xx_serial_stop_tx(port);//停止发送

 out:
	return IRQ_HANDLED;
}
S3C2410 串口线路设置
S3C2410 串口驱动 uart_ops 结构体的 set_termios()成员函数用于改变端口的参数设置,包括波特率、字长、停止位、奇偶校验等,它会根据传递给它的 port、termios 参数成员的值设置 S3C2410 UART 的 ULCONn、UCONn、UMCONn 等寄存器,其实现如下代码所示。
static void s3c24xx_serial_set_termios(struct uart_port *port,
				       struct ktermios *termios,
				       struct ktermios *old)
{
	struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
	struct s3c24xx_uart_port *ourport = to_ourport(port);
	struct s3c24xx_uart_clksrc *clksrc = NULL;
	struct clk *clk = NULL;
	unsigned long flags;
	unsigned int baud, quot;
	unsigned int ulcon;
	unsigned int umcon;

	/*
	 * We don't support modem control lines.
	 *//* 不支持 modem 控制信号线 */
	termios->c_cflag &= ~(HUPCL | CMSPAR);
	termios->c_cflag |= CLOCAL;

	/*
	 * Ask the core to calculate the divisor for us.
	 */
        /* 请求内核计算分频以便产生对应的波特率 */
	baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);

	if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
		quot = port->custom_divisor;
	else
		quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);

	/* check to see if we need  to change clock source */
        /* 检查以确定是否需要改变时钟源 */
	if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
		s3c24xx_serial_setsource(port, clksrc);

		if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
			clk_disable(ourport->baudclk);
			ourport->baudclk  = NULL;
		}

		clk_enable(clk);

		ourport->clksrc = clksrc;
		ourport->baudclk = clk;
	}
        /* 设置字长 */
	switch (termios->c_cflag & CSIZE) {
	case CS5:
		dbg("config: 5bits/char\n");
		ulcon = S3C2410_LCON_CS5;
		break;
	case CS6:
		dbg("config: 6bits/char\n");
		ulcon = S3C2410_LCON_CS6;
		break;
	case CS7:
		dbg("config: 7bits/char\n");
		ulcon = S3C2410_LCON_CS7;
		break;
	case CS8:
	default:
		dbg("config: 8bits/char\n");
		ulcon = S3C2410_LCON_CS8;
		break;
	}

	/* preserve original lcon IR settings *//* 保留以前的 lcon IR 设置 */
	ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);

	if (termios->c_cflag & CSTOPB)
		ulcon |= S3C2410_LCON_STOPB;
        /* 设置是否采用 RTS、CTS 自动流空 */
	umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;

	if (termios->c_cflag & PARENB) {
		if (termios->c_cflag & PARODD)
			ulcon |= S3C2410_LCON_PODD;//奇校验
		else
			ulcon |= S3C2410_LCON_PEVEN;//偶校验
	} else {
		ulcon |= S3C2410_LCON_PNONE;//无校验
	}

	spin_lock_irqsave(&port->lock, flags);

	dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot);

	wr_regl(port, S3C2410_ULCON, ulcon);
	wr_regl(port, S3C2410_UBRDIV, quot);
	wr_regl(port, S3C2410_UMCON, umcon);

	dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
	    rd_regl(port, S3C2410_ULCON),
	    rd_regl(port, S3C2410_UCON),
	    rd_regl(port, S3C2410_UFCON));

	/*
	 * Update the per-port timeout./* 更新端口的超时 */
	 */
	uart_update_timeout(port, termios->c_cflag, baud);

	/*
	 * Which character status flags are we interested in?
	 *//*对什么字符状态标志感兴趣?*/
	port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
	if (termios->c_iflag & INPCK)
		port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;

	/*
	 * Which character status flags should we ignore?
	 *//* 要忽略什么字符状态标志?*/
	port->ignore_status_mask = 0;
	if (termios->c_iflag & IGNPAR)
		port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
	if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
		port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;

	/*
	 * Ignore all characters if CREAD is not set.
	 *//* 如果 CREAD 未设置,忽略所用字符 */
	if ((termios->c_cflag & CREAD) == 0)
		port->ignore_status_mask |= RXSTAT_DUMMY_READ;

	spin_unlock_irqrestore(&port->lock, flags);
}
由于 S3C2410 集成 UART 并不包含完整的 Modem 控制信号线,因此其 uart_ops结构体的 get_mctrl()、set_mctrl()成员函数的实现非常简单。
总结
TTY 设备驱动的主体工作围绕 tty_driver 这个结构体的成员函数展开,主要应实现其中的数据发送和接收流程以及 tty 设备线路设置接口函数。针对串口,内核实现了串口核心层,这个层实现了串口设备通用的 tty_driver。因此,串口设备驱动的主体工作从 tty_driver 转移到了 uart_driver。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值