LINUX下串行设备驱动程序

来源:互联网 发布:php加减乘除计算器 编辑:程序博客网 时间:2024/06/05 15:44

    在嵌入式或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_tlock;/* port lock */unsigned longiobase;/* 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 intirq;/* irq number */unsigned longirqflags;/* irq flags  */unsigned intuartclk;/* base uart clock */unsigned intfifosize;/* tx fifo size */unsigned charx_char;/* xon/xoff char */unsigned charregshift;/* reg offset shift */unsigned chariotype;/* io access style */unsigned charunused1;#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 intread_status_mask;/* driver specific */unsigned intignore_status_mask;/* driver specific */struct uart_state*state;/* pointer to parent state */struct uart_icounticount;/* statistics */struct console*cons;/* struct console, if any */#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)unsigned longsysrq;/* sysrq timeout */#endifupf_tflags;#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 intmctrl;/* current modem ctrl settings */unsigned inttimeout;/* character-based timeout */unsigned inttype;/* port type */const struct uart_ops*ops;unsigned intcustom_divisor;unsigned intline;/* port index */resource_size_tmapbase;/* for ioremap */struct device*dev;/* parent device */unsigned charhub6;/* this should be in the 8250 driver */unsigned charsuspended;unsigned charunused[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_POLLvoid(*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 {intmagic;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 long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];unsigned char *echo_buf;unsigned int echo_pos;unsigned int echo_cnt;int canon_data;unsigned long canon_head;unsigned int canon_column;struct mutex atomic_read_lock;struct mutex atomic_write_lock;struct mutex output_lock;struct mutex echo_lock;unsigned char *write_buf;int write_cnt;spinlock_t read_lock;/* If the tty has a pending do_SAK, queue it here - akpm */struct work_struct SAK_work;struct tty_port *port;};

    2).tty_struct中内嵌的struct tty_flip_buffer或flip缓冲区.这是数据收集和处理机制的中枢.底层品行驱动程序将flip缓冲区的一半用于数据收集,线路规程则使用另一半来进行数据处理.在高版本的内核对上述的flip缓冲区机制作了取代.由缓冲区头部(tty_bufhead)和缓冲区链表(tty_buffer)组成.见上述的结构体成员:
struct tty_bufhead buf;
    展开后如下:

struct tty_bufhead {struct delayed_work work;spinlock_t lock;struct tty_buffer *head;/* Queue head */struct tty_buffer *tail;/* Active buffer */struct tty_buffer *free;/* Free queue head */int memory_used;/* Buffer space used excludingfree queue */};

struct tty_buffer {struct tty_buffer *next;char *char_buf_ptr;unsigned char *flag_buf_ptr;int used;int size;int commit;int read;/* Data points here */unsigned long data[0];};

    3).定义于include/linux/tty_driver.h文件中的struct tty_driver.它规定了tty驱动程序和高层之间的编程接口:

struct tty_driver {intmagic;/* magic number for this structure */struct kref kref;/* Reference management */struct cdev cdev;struct module*owner;const char*driver_name;const char*name;intname_base;/* offset of printed name */intmajor;/* major device number */intminor_start;/* start of minor device number */intminor_num;/* number of *possible* devices */intnum;/* number of devices allocated */shorttype;/* type of tty driver */shortsubtype;/* subtype of tty driver */struct ktermios init_termios; /* Initial termios */intflags;/* tty driver flags */struct proc_dir_entry *proc_entry; /* /proc fs entry */struct tty_driver *other; /* only used for the PTY driver *//* * Pointer to the tty data structures */struct tty_struct **ttys;struct ktermios **termios;struct ktermios **termios_locked;void *driver_state;/* * Driver methods */const struct tty_operations *ops;struct list_head tty_drivers;};

    tty驱动程序需要完成两个步骤向内核注册自身:

    1).调用tty_register_driver(struct tty_driver *tty_d)向tty核心注册自身;

    2).调用以下代码注册它支持的每个单独的tty:

struct device *tty_register_device(struct tty_driver *driver, unsigned index,struct device *device)


    2-3.线路规程

    线路规程的意义:

        线路规程是一种策略:使得用户运行不同的应用程序时,可以使用相同的底层串行驱动程序.

    线路规程的具体工作内容:

        负责处理这些数据,其工作 内容就是以特殊的方式格式化从一个用户或者硬件收到的数据,这种格式化往往采用一个协议转换的形式,如PPP和Bluetooth.

    线路规程在LINUX UART子系统中的地位:

        tty核心从一个用户获取将要发送给一个tty设备的数据,tty核心将数据传递给tty线路规程驱动,接着将数据传递到tty驱动,tty驱动将数据转换为可以发送给硬件的格式.(下面将以三星S3C2440 UART为实例分析此过程).

    目前串行子系统支持17种标准的线路规程.常用的线路规程如下:

    N_TTY:默认的线路规程,主要实现字符文本的处理,如cat 、echo 字符;

    N_PPP:点对点协议;

    N_SLIP:串行线路网际协议;

    N_IRDA:红外数据;

    N_HCI:蓝牙主机控制接口.


3.UART驱动层、TTY驱动层和线路规程三个层次的关系:

    UART驱动层、TTY驱动层和线路规程组成了LINUX下的串行设备驱动程序.整个框架如下:


先从宏观的结构体看这三个层次的关系.

UART驱动层 -> TTY层:

include/linux/serial_core.h

struct uart_driver {        ... ...;struct tty_driver*tty_driver;};
struct uart_driver是一个具体特定的UART设备.里面封装了struct tty_driver,tty_driver是一个直接面向用户空间的字符设备.如下:

struct tty_driver {        ... ...;struct cdev cdev;        ... ...;};
struct tty_struct表征一个终端设备.它和线路规程密切关联:

struct tty_struct {        ... ...;struct tty_ldisc *ldisc;        ... ...;};
struct tty_ldisc最主要的是包含了对线路规程的设置操作集:

struct tty_ldisc {struct tty_ldisc_ops *ops;atomic_t users;};
线路规程的操作集都是面向TTY设备终端的:

struct tty_ldisc_ops {intmagic;char*name;intnum;intflags;/* * The following routines are called from above. */int(*open)(struct tty_struct *);void(*close)(struct tty_struct *);void(*flush_buffer)(struct tty_struct *tty);ssize_t(*chars_in_buffer)(struct tty_struct *tty);ssize_t(*read)(struct tty_struct * tty, struct file * file,unsigned char __user * buf, size_t nr);ssize_t(*write)(struct tty_struct * tty, struct file * file, const unsigned char * buf, size_t nr);int(*ioctl)(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg);long(*compat_ioctl)(struct tty_struct * tty, struct file * file,unsigned int cmd, unsigned long arg);void(*set_termios)(struct tty_struct *tty, struct ktermios * old);unsigned int (*poll)(struct tty_struct *, struct file *,     struct poll_table_struct *);int(*hangup)(struct tty_struct *tty);/* * The following routines are called from below. */void(*receive_buf)(struct tty_struct *, const unsigned char *cp,       char *fp, int count);void(*write_wakeup)(struct tty_struct *);struct  module *owner;int refcount;};
可见,其所有操作函数都以struct tty_struct作为参数.


4.实例分析:

    LINUX UART驱动子系统和大多数的LINUX子系统一样,比如说IIC、SPI、USB、FBI等.均采用了分层的架构思想.每一层都有其操作集.但是无论如何分层(各种子系统),暴露给用户空间的分类只有三种:字符设备、块设备、网络设备.LINUX内核之所以采用分层思想,只要是为了代码的重用性、可移植性.尽可能地把和具体平台相关的代码达到最大程序的隔离.但是这导致了初学者对内核阅读的难度.


======================================================== UART 驱 动 层 ================================================================

    下面以S3C2440的UART驱动对内核这种子系统分层系统进行宏观认识.从用户空间到最底层的寄存器设置进行代码跟踪分析.

    S3C2440 UART的入口:drivers/serial/samsung.c

module_init(s3c24xx_serial_modinit);
-->

static int __init s3c24xx_serial_modinit(void){        ... ...;ret = uart_register_driver(&s3c24xx_uart_drv);        ... ...;}
-->

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,};
-->    

    drivers/serial/serial_core.c

    上述分析了,struct uart_driver是表征一个具体的UART设备.因此,三星的S3C2440的UART是一个具体的UART设备.由于直接面向用户空间的字符设备是tty.因此,这个tty也必须是与S3C2440这个平台相关的.而在UART层与用户空间没有直接的关系.下面分析函数uart_register_driver()函数:

int uart_register_driver(struct uart_driver *drv){struct tty_driver *normal = NULL;int i, retval;BUG_ON(drv->state);/* * Maybe we should be using a slab cache for this, especially if * we have a large number of ports to handle. */drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);retval = -ENOMEM;if (!drv->state)goto out;normal  = alloc_tty_driver(drv->nr);if (!normal)goto out;drv->tty_driver = normal;normal->owner= drv->owner;normal->driver_name= drv->driver_name;normal->name= drv->dev_name;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_set_operations(normal, &uart_ops);/* * Initialise the UART state(s). */for (i = 0; i < drv->nr; i++) {struct uart_state *state = drv->state + i;struct tty_port *port = &state->port;tty_port_init(port);port->close_delay     = 500;/* .5 seconds */port->closing_wait    = 30000;/* 30 seconds */tasklet_init(&state->tlet, uart_tasklet_action,     (unsigned long)state);}retval = tty_register_driver(normal); out:if (retval < 0) {put_tty_driver(normal);kfree(drv->state);}return retval;}
    由此可见,根据S3C2440这个平台相关的UART信息初始化一个与S3C2440这个平台相关的TTY.比如,主次设备号、驱动名等.


===================================================== TTY 层 驱 动 =====================================================================

tty_set_operations(normal, &uart_ops);
   实现了对这个tty的操作集初始化.其中uar_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};
    既然uart_ops是一个全局变量,而且位置serail core.说明此变量是与平台无关的.这里的函数只是一种"形式".并不具备实际的硬件操作能力.只是为了软件上的统一组织.后面的分析将看到这一点.

-->

    drivers/char/tty_io.c

    用这个特定的UART信息初始完成的tty.便要注册到tty驱动层.下面分析函数tty_register_driver().

/* * Called by a tty driver to register itself. */int tty_register_driver(struct tty_driver *driver){int error;int i;dev_t dev;void **p = NULL;if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);if (!p)return -ENOMEM;}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 (error < 0) {kfree(p);return error;}if (p) {driver->ttys = (struct tty_struct **)p;driver->termios = (struct ktermios **)(p + driver->num);} else {driver->ttys = NULL;driver->termios = NULL;}cdev_init(&driver->cdev, &tty_fops);driver->cdev.owner = driver->owner;error = cdev_add(&driver->cdev, dev, driver->num);if (error) {unregister_chrdev_region(dev, driver->num);driver->ttys = NULL;driver->termios = NULL;kfree(p);return error;}mutex_lock(&tty_mutex);list_add(&driver->tty_drivers, &tty_drivers);mutex_unlock(&tty_mutex);if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {for (i = 0; i < driver->num; i++)    tty_register_device(driver, i, NULL);}proc_tty_register_driver(driver);driver->flags |= TTY_DRIVER_INSTALLED;return 0;}
    注意到上面代码中下面语句:

cdev_init(&driver->cdev, &tty_fops);
    这条语句的执行,tty正式以字符设备的形式暴露给用户空间.其中,tty_fops是一个全局变量,因此,它也是与具体平台无关的.因为此部分是tty驱动层的公共函数.如下:

static const struct file_operations tty_fops = {.llseek= no_llseek,.read= tty_read,.write= tty_write,.poll= tty_poll,.unlocked_ioctl= tty_ioctl,.compat_ioctl= tty_compat_ioctl,.open= tty_open,.release= tty_release,.fasync= tty_fasync,};
   

    上述已经确认是字符设备,动态生成字符设备节点由函数tty_register_device()完成.展开如下:

struct device *tty_register_device(struct tty_driver *driver, unsigned index,   struct device *device){char name[64];dev_t dev = MKDEV(driver->major, driver->minor_start) + index;if (index >= driver->num) {printk(KERN_ERR "Attempt to register invalid tty line number "       " (%d).\n", index);return ERR_PTR(-EINVAL);}if (driver->type == TTY_DRIVER_TYPE_PTY)pty_line_name(driver, index, name);elsetty_line_name(driver, index, name);return device_create(tty_class, device, dev, NULL, name);}
    此函数在此在一个for循环里面调用,里面封装了device_create()函数,在udev支持下动态生成设备节点如下:
/dev/ttySAC0 c 204 64/dev/ttySAC1 c 204 65/dev/ttySAC2 c 204 66

    但是真正生成字符设备节点的并不是在这里,因为上述的driver->flag标志了TTY_DRIVER_DYNAMIC_DEV.因此,这将在后续注册uart port的时候再动态生成,也就是说,这里的if条件是不成立的.

    那么,在什么时机生成特定的UART设备字符设备节点呢?下面跟踪此源码部分.此下又回到了UART驱动层.见源码位置:driver/serial/s3c2440.c

========================================================UART驱动层==================================================================

    在分析源码之前,有个结构体是不是不认识一下的,因为它关联了struct uart_driver、struct uart_port、struct tty_struct三者的关系.其中struct uart_driver是我们平台相关的UART驱动;struct uart_port是这个平台相关的UART驱动管辖的port口,这涉及到具体平台的硬件寄存器的配置;struct tty_struct是用户空间直接面向的tty设备.这个结构体就是struct uart_state.这个结构体的生成在uart_register_driver()函数.分配并赋予uart_driver的state域.

    下面分析struct uart_state的重大意义,源码入口位置为drivers/serial/s3c2440.c:

module_init(s3c2440_serial_init);
-->

static int __init s3c2440_serial_init(void){return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf);}
-->

int s3c24xx_serial_init(struct platform_driver *drv,struct s3c24xx_uart_info *info){dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);#ifdef CONFIG_PMdrv->suspend = s3c24xx_serial_suspend;drv->resume = s3c24xx_serial_resume;#endifreturn platform_driver_register(drv);}
    由于LINUX平台总线的规划,直接导致s3c2440_serial_driver的函数probe域的执行(过程的调用关系日后有空再作分析):

static struct platform_driver s3c2440_serial_driver = {.probe= s3c2440_serial_probe,.remove= __devexit_p(s3c24xx_serial_remove),.driver= {.name= "s3c2440-uart",.owner= THIS_MODULE,},};
-->

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);}
-->

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;}
    在这里出现一个极其重要的全局数组s3c24xx_serial_ports和一个极其重要的函数uart_add_one_port().把s3c2440_serial_ports展开如下:

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};
    见这数组里面有个重要的域ops.这就是与具体平台相关的串口操作集:s3c24xx_serial_ops.展开如下:

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,};
    至此,我们可以猜测,用户空间的系统调用(比如设置波特率)必定调用到此才算完成底层硬件的通讯协议的配置.那么,用户空间的系统调用如何路由到这里呢?通过struct uart_state来桥接.这种桥接关系在函数uart_add_one_port()里面实现.展开uart_add_one_port()函数:

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;BUG_ON(in_interrupt());if (uport->line >= drv->nr)return -EINVAL;state = drv->state + uport->line;port = &state->port;mutex_lock(&port_mutex);mutex_lock(&port->mutex);if (state->uart_port) {ret = -EINVAL;goto out;}state->uart_port = uport;state->pm_state = -1;uport->cons = drv->cons;uport->state = state;/* * If this port is a console, then the spinlock is already * initialised. */if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {spin_lock_init(&uport->lock);lockdep_set_class(&uport->lock, &port_lock_key);}uart_configure_port(drv, state, uport);/* * Register the port whether it's detected or not.  This allows * setserial to be used to alter this ports parameters. */tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);if (likely(!IS_ERR(tty_dev))) {device_init_wakeup(tty_dev, 1);device_set_wakeup_enable(tty_dev, 0);} elseprintk(KERN_ERR "Cannot register tty device on line %d\n",       uport->line);/* * Ensure UPF_DEAD is not set. */uport->flags &= ~UPF_DEAD; out:mutex_unlock(&port->mutex);mutex_unlock(&port_mutex);return ret;}
    下面大致针对struct uart_state这个结构体的桥接的意义分析一下相关代码:

struct uart_state *state;... ...;state = drv->state + uport->line;
    临时指针state关联了uart_driver的state域.

state->uart_port = uport;
    state和具体的uart port口关联起来了.实际上,是把uart_driver和其uart port口关联了.

此函数里面还有一个很重要的函数:tty_register_device().展开如下:

struct device *tty_register_device(struct tty_driver *driver, unsigned index,   struct device *device){char name[64];dev_t dev = MKDEV(driver->major, driver->minor_start) + index;if (index >= driver->num) {printk(KERN_ERR "Attempt to register invalid tty line number "       " (%d).\n", index);return ERR_PTR(-EINVAL);}if (driver->type == TTY_DRIVER_TYPE_PTY)pty_line_name(driver, index, name);elsetty_line_name(driver, index, name);return device_create(tty_class, device, dev, NULL, name);}
    在此,正式动态生成了设备节点,比如/dev/ttySAC0 c 204 64

    [注:]这里只生成一个设备节点,但是S3C2440有三个串口,为何只生成一个呢?其实,每个串口都被注册为平台设备,也就是说probe函数会被执行三次.每次生成一个设备节点,因此,一共会存在三个设备节点.


    至此,每一次probe,完成了一个字符设备的注册了.这三个个字符设备的设备节点为:

/dev/ttySAC0 c 204 64/dev/ttySAC1 c 204 65/dev/ttySAC2 c 204 66
    这三个字符设备关联的操作集是uart_ops(见上述函数uart_register_driver分析)而不是与具体uart port的操作集s3c24xx_serial_ops.因为分层结构导致了用户空间没办法直接面向底层硬件的操作.用户空间直接调用的是tty_fops,tty_fops会被uart_ops重载,而这过程中相应的s3c24xx_serial_ops会重载uart_ops相应的调用.实现用户到具体底层的间接调用.


====================================================== 从用户空间到底层===============================================================

    当用户空间对这个tty字符设备进行open()、read()等系统调用的时候.会导致tty_fops这些函数的直接运行.下面先以tty_read()函数分析:

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,loff_t *ppos){int i;struct tty_struct *tty;struct inode *inode;struct tty_ldisc *ld;tty = (struct tty_struct *)file->private_data;inode = file->f_path.dentry->d_inode;if (tty_paranoia_check(tty, inode, "tty_read"))return -EIO;if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))return -EIO;/* We want to wait for the line discipline to sort out in this   situation */ld = tty_ldisc_ref_wait(tty);if (ld->ops->read)i = (ld->ops->read)(tty, file, buf, count);elsei = -EIO;tty_ldisc_deref(ld);if (i > 0)inode->i_atime = current_fs_time(inode->i_sb);return i;}
    注意有上述有行代码:

tty = (struct tty_struct *)file->private_data;ld = tty_ldisc_ref_wait(tty);if (ld->ops->read)i = (ld->ops->read)(tty, file, buf, count);

    我们知道,inode是物理文件,存在唯一性;file是open时候动态创建的.大概猜测:open的时候把open的特定的tty设备保存在file->private_data.这个域是保存的是文件的私有信息.然后通过函数tty_ldisc_ref_wait()找到此tty的路线规程.然后再调用被重载的线路规程的read函数:ld->ops->read.因此,tty_open()函数必大有乾坤.下面分析tty_open()函数:

static int tty_open(struct inode *inode, struct file *filp){int ret;lock_kernel();ret = __tty_open(inode, filp);unlock_kernel();return ret;}
-->

static int __tty_open(struct inode *inode, struct file *filp){struct tty_struct *tty = NULL;int noctty, retval;struct tty_driver *driver;int index;dev_t device = inode->i_rdev;unsigned saved_flags = filp->f_flags;nonseekable_open(inode, filp);retry_open:noctty = filp->f_flags & O_NOCTTY;index  = -1;retval = 0;mutex_lock(&tty_mutex);if (device == MKDEV(TTYAUX_MAJOR, 0)) {tty = get_current_tty();if (!tty) {mutex_unlock(&tty_mutex);return -ENXIO;}driver = tty_driver_kref_get(tty->driver);index = tty->index;filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block *//* noctty = 1; *//* FIXME: Should we take a driver reference ? */tty_kref_put(tty);goto got_driver;}#ifdef CONFIG_VTif (device == MKDEV(TTY_MAJOR, 0)) {extern struct tty_driver *console_driver;driver = tty_driver_kref_get(console_driver);index = fg_console;noctty = 1;goto got_driver;}#endifif (device == MKDEV(TTYAUX_MAJOR, 1)) {struct tty_driver *console_driver = console_device(&index);if (console_driver) {driver = tty_driver_kref_get(console_driver);if (driver) {/* Don't let /dev/console block */filp->f_flags |= O_NONBLOCK;noctty = 1;goto got_driver;}}mutex_unlock(&tty_mutex);return -ENODEV;}driver = get_tty_driver(device, &index);if (!driver) {mutex_unlock(&tty_mutex);return -ENODEV;}got_driver:if (!tty) {/* check whether we're reopening an existing tty */tty = tty_driver_lookup_tty(driver, inode, index);if (IS_ERR(tty)) {mutex_unlock(&tty_mutex);return PTR_ERR(tty);}}if (tty) {retval = tty_reopen(tty);if (retval)tty = ERR_PTR(retval);} elsetty = tty_init_dev(driver, index, 0);mutex_unlock(&tty_mutex);tty_driver_kref_put(driver);if (IS_ERR(tty))return PTR_ERR(tty);filp->private_data = tty;file_move(filp, &tty->tty_files);check_tty_count(tty, "tty_open");if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&    tty->driver->subtype == PTY_TYPE_MASTER)noctty = 1;#ifdef TTY_DEBUG_HANGUPprintk(KERN_DEBUG "opening %s...", tty->name);#endifif (!retval) {if (tty->ops->open)retval = tty->ops->open(tty, filp);elseretval = -ENODEV;}filp->f_flags = saved_flags;if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&!capable(CAP_SYS_ADMIN))retval = -EBUSY;if (retval) {#ifdef TTY_DEBUG_HANGUPprintk(KERN_DEBUG "error %d in opening %s...", retval,       tty->name);#endiftty_release_dev(filp);if (retval != -ERESTARTSYS)return retval;if (signal_pending(current))return retval;schedule();/* * Need to reset f_op in case a hangup happened. */if (filp->f_op == &hung_up_tty_fops)filp->f_op = &tty_fops;goto retry_open;}mutex_lock(&tty_mutex);spin_lock_irq(¤t->sighand->siglock);if (!noctty &&    current->signal->leader &&    !current->signal->tty &&    tty->session == NULL)__proc_set_tty(current, tty);spin_unlock_irq(¤t->sighand->siglock);mutex_unlock(&tty_mutex);return 0;}
   

    注意到上述中下面代码:

tty = tty_init_dev(driver, index, 0);filp->private_data = tty;
    可见,open的时候,把这个tty保存在filp这个文件结构体中的私有域filp->private_data中.tty出生于函数tty_init_dev()函数里面:包括这个tty从无到有的内存分配、初始化、关联相应的线路规程等动作均在此函数完成.因此,此函数不得不窥视一下:

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,int first_ok){struct tty_struct *tty;int retval;/* Check if pty master is being opened multiple times */if (driver->subtype == PTY_TYPE_MASTER &&(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)return ERR_PTR(-EIO);/* * First time open is complex, especially for PTY devices. * This code guarantees that either everything succeeds and the * TTY is ready for operation, or else the table slots are vacated * and the allocated memory released.  (Except that the termios * and locked termios may be retained.) */if (!try_module_get(driver->owner))return ERR_PTR(-ENODEV);tty = alloc_tty_struct();if (!tty)goto fail_no_mem;initialize_tty_struct(tty, driver, idx);retval = tty_driver_install_tty(driver, tty);if (retval < 0) {free_tty_struct(tty);module_put(driver->owner);return ERR_PTR(retval);}/* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up.  No need * to decrement the use counts, as release_tty doesn't care. */retval = tty_ldisc_setup(tty, tty->link);if (retval)goto release_mem_out;return tty;fail_no_mem:module_put(driver->owner);return ERR_PTR(-ENOMEM);/* call the tty release_tty routine to clean out this slot */release_mem_out:if (printk_ratelimit())printk(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx);release_tty(tty, idx);return ERR_PTR(retval);}
    上面代码中,重点关注三个函数:

tty = alloc_tty_struct();
    动态分配了一个tty设备结构体,这里tty结构体就表征一个设备.接下来会必将会对此tty设备进行初始化,最重要的是初始化这个设备的操作集(一个设备不提供操作方法此设备又有何存在意义?).

initialize_tty_struct(tty, driver, idx);
    初始化tty设备,主要用driver(这里是tty_driver,是tty层的驱动.见更上面的uart_register_driver()函数分析)来初始化此设备:

void initialize_tty_struct(struct tty_struct *tty,struct tty_driver *driver, int idx){memset(tty, 0, sizeof(struct tty_struct));kref_init(&tty->kref);tty->magic = TTY_MAGIC;tty_ldisc_init(tty);tty->session = NULL;tty->pgrp = NULL;tty->overrun_time = jiffies;tty->buf.head = tty->buf.tail = NULL;tty_buffer_init(tty);mutex_init(&tty->termios_mutex);mutex_init(&tty->ldisc_mutex);init_waitqueue_head(&tty->write_wait);init_waitqueue_head(&tty->read_wait);INIT_WORK(&tty->hangup_work, do_tty_hangup);mutex_init(&tty->atomic_read_lock);mutex_init(&tty->atomic_write_lock);mutex_init(&tty->output_lock);mutex_init(&tty->echo_lock);spin_lock_init(&tty->read_lock);spin_lock_init(&tty->ctrl_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->ops = driver ->ops;
    用tty_driver->ops来初始化设备tty->ops.由前面分析,tty_driver->ops是一个平台无关的"虚操作集"--即其并没有具备实际的硬件平台操作能力,这纯粹是为了软件的统一管理.

那么,tty对应在驱动driver是从何处来呢?回到__tty_open(),见下面语句:

driver = get_tty_driver(device, &index);
此函数展开如下:

static struct tty_driver *get_tty_driver(dev_t device, int *index){struct tty_driver *p;list_for_each_entry(p, &tty_drivers, tty_drivers) {dev_t base = MKDEV(p->major, p->minor_start);if (device < base || device >= base + p->num)continue;*index = device - base;return tty_driver_kref_get(p);}return NULL;}
    这主要策略是通过设备文件的设备号来获取其相应的驱动,即在上述分析的函数uart_register_driver()中的全局变量的操作集uart_ops.因此,接下来的语句:

if (tty->ops->open)retval = tty->ops->open(tty, filp);
实际调用的是uart_ops中的open函数.如下:

static int uart_open(struct tty_struct *tty, struct file *filp){struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;struct uart_state *state;struct tty_port *port;int retval, line = tty->index;BUG_ON(!kernel_locked());pr_debug("uart_open(%d) called\n", line);/* * tty->driver->num won't change, so we won't fail here with * tty->driver_data set to something non-NULL (and therefore * we won't get caught by uart_close()). */retval = -ENODEV;if (line >= tty->driver->num)goto fail;/* * We take the semaphore inside uart_get to guarantee that we won't * be re-entered while allocating the state structure, or while we * request any IRQs that the driver may need.  This also has the nice * side-effect that it delays the action of uart_hangup, so we can * guarantee that state->port.tty will always contain something * reasonable. */state = uart_get(drv, line);if (IS_ERR(state)) {retval = PTR_ERR(state);goto fail;}port = &state->port;/* * Once we set tty->driver_data here, we are guaranteed that * uart_close() will decrement the driver module use count. * Any failures from here onwards should not touch the count. */tty->driver_data = state;state->uart_port->state = state;tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;tty->alt_speed = 0;tty_port_tty_set(port, tty);/* * If the port is in the middle of closing, bail out now. */if (tty_hung_up_p(filp)) {retval = -EAGAIN;port->count--;mutex_unlock(&port->mutex);goto fail;}/* * Make sure the device is in D0 state. */if (port->count == 1)uart_change_pm(state, 0);/* * Start up the serial port. */retval = uart_startup(state, 0);/* * If we succeeded, wait until the port is ready. */if (retval == 0)retval = uart_block_til_ready(filp, state);mutex_unlock(&port->mutex);/* * If this is the first open to succeed, adjust things to suit. */if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) {set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);uart_update_termios(state);}fail:return retval;}

    此函数的深入将涉及到tty设备与具体的硬件串口、寄存器配置关联.后面分析.

=========================================================线 路 规 程 =============================================================

    上述函数initialize_tty_struct()实现tty设备的初始化,其中就包括了线路规程的配置.

drivers/char/tty_ldisc.c

tty_ldisc_init(tty);
    对此tty设备的线路规程初始化:

void tty_ldisc_init(struct tty_struct *tty){struct tty_ldisc *ld = tty_ldisc_get(N_TTY);if (IS_ERR(ld))panic("n_tty: init_tty");tty_ldisc_assign(tty, ld);}
    因此,默认的线路规程是N_TTY.继承分析tty_ldisc_get()函数:

static struct tty_ldisc *tty_ldisc_get(int disc){        ... ...;ldops = get_ldops(disc);        ... ...;}
    我们给函数get_ldops()函数传递进去的参数是disc = N_TTY.展开get_ldops()函数:

static struct tty_ldisc_ops *get_ldops(int disc){unsigned long flags;struct tty_ldisc_ops *ldops, *ret;spin_lock_irqsave(&tty_ldisc_lock, flags);ret = ERR_PTR(-EINVAL);ldops = tty_ldiscs[disc];if (ldops) {ret = ERR_PTR(-EAGAIN);if (try_module_get(ldops->owner)) {ldops->refcount++;ret = ldops;}}spin_unlock_irqrestore(&tty_ldisc_lock, flags);return ret;}
    上述函数中,最重要的语句为:

ldops = tty_ldiscs[disc];
    其中tty_ldiscs是一个全局数组,此数组里面每个元素都是一种线路规程的操作集.LK2.6.32.2一共支持有20种线路规程.如下:

static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
    我们传递进去的数组的索引值是N_TTY(实际是数值0).但是N_TTY对应的实际的操作值是什么?在哪里面对这N_TTY这种线路规程初始化呢?答案是在内核启动过程对控制台初始化的时候对tty_ldiscs[N_TTY]初始化了.如下:

    init/main.c

void __init console_init(void){initcall_t *call;/* Setup the default TTY line discipline. */tty_ldisc_begin();/* * set up the console device so that later boot sequences can * inform about problems etc.. */call = __con_initcall_start;while (call < __con_initcall_end) {(*call)();call++;}}
展开函数tty_ldisc_begin():

void tty_ldisc_begin(void){/* Setup the default TTY line discipline. */(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);}
    其中,tty_ldisc_N_TTY便是N_TTY对应的线路规程.如下:

struct tty_ldisc_ops tty_ldisc_N_TTY = {.magic           = TTY_LDISC_MAGIC,.name            = "n_tty",.open            = n_tty_open,.close           = n_tty_close,.flush_buffer    = n_tty_flush_buffer,.chars_in_buffer = n_tty_chars_in_buffer,.read            = n_tty_read,.write           = n_tty_write,.ioctl           = n_tty_ioctl,.set_termios     = n_tty_set_termios,.poll            = n_tty_poll,.receive_buf     = n_tty_receive_buf,.write_wakeup    = n_tty_write_wakeup};
    至此,把这个tty设备的线路规程配置为N_TTY.用户空间操作tty这个字符设备时,比如open,会被这个tty设备的线路规程操作集重载.上述已经分析了.见上面的tty_read()函数的简析.

    我们open一个tty终端设备之后往往需要对其进行一些波特率之类的设置,便是对线路规程的设置.对应上述的set_termios域.

因此,到这里,我们应该明确.用户空间进行某tty设备这个字符设备进行系统调用的时候,首先这个tty设备相应的"虚操作集"(即tty_fops)会被调用,在这"虚操作集"中又被此tty设备的线路规程重载了,指定的线路规程相应的"操作集"被调用了.下面是示意代码:

open("/dev/ttyS0") -->tty_open()-->n_tty_open()

分别对应:user-space --> tty-core->tty-ldisc


======================================================== 具体平台操作===============================================================

    上面我们已经分析出TTY字符设备节点如何生成、用户空间的系统调用首先直接面向tty_fops的操作集、tty_fops又被线路规程的tty_ldisc_ops重载了.相当于用户空间间接调用了tty_ldisc_ops.但是在此我们并没有看到具体的uart port操作,因为如果至此为止的话,上面的所有功夫都没有意义,因为只有底层硬件协议生效(指配置寄存器)才可以驱动外设.下面从用户空间到最底层的寄存器操作流程进行分析.为了连贯性.下面的分析可能会和上述有重复.

    用户空间的open(),直接导致tty_fops中的tty_open()函数的执行:

static const struct file_operations tty_fops = {.llseek= no_llseek,.read= tty_read,.write= tty_write,.poll= tty_poll,.unlocked_ioctl= tty_ioctl,.compat_ioctl= tty_compat_ioctl,.open= tty_open,.release= tty_release,.fasync= tty_fasync,};
-->

/* BKL pushdown: scary code avoidance wrapper */static int tty_open(struct inode *inode, struct file *filp){int ret;lock_kernel();ret = __tty_open(inode, filp);unlock_kernel();return ret;}
-->

static int __tty_open(struct inode *inode, struct file *filp){... ...;driver = get_tty_driver(device, &index);... ...;        tty = tty_init_dev(driver, index, 0);        ... ...;filp->private_data = tty;        ... ...;if (tty->ops->open)retval = tty->ops->open(tty, filp);elseretval = -ENODEV;        ... ...;}
    追踪函数tty_init_driver()-->initialize_tty_struct()可以看到下面的语句:

tty->driver = driver;tty->ops = driver->ops;
    在这里把driver(即tty_driver)赋值给tty->driver,把这个tty设备和其驱动关联起来.而driver和具体的uart_driver也是关联的.见uart_register_driver()有下面语句:

normal->driver_state    = drv;

    回到__tty_open()函数,接下来有个判断语句:

if (tty->ops->open)retval = tty->ops->open(tty, filp);
    实际调用的是driver->ops的open函数.即又回到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};
    展开uart_open函数:

-->

static int uart_open(struct tty_struct *tty, struct file *filp){struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;struct uart_state *state;struct tty_port *port;int retval, line = tty->index;BUG_ON(!kernel_locked());pr_debug("uart_open(%d) called\n", line);/* * tty->driver->num won't change, so we won't fail here with * tty->driver_data set to something non-NULL (and therefore * we won't get caught by uart_close()). */retval = -ENODEV;if (line >= tty->driver->num)goto fail;/* * We take the semaphore inside uart_get to guarantee that we won't * be re-entered while allocating the state structure, or while we * request any IRQs that the driver may need.  This also has the nice * side-effect that it delays the action of uart_hangup, so we can * guarantee that state->port.tty will always contain something * reasonable. */state = uart_get(drv, line);if (IS_ERR(state)) {retval = PTR_ERR(state);goto fail;}port = &state->port;/* * Once we set tty->driver_data here, we are guaranteed that * uart_close() will decrement the driver module use count. * Any failures from here onwards should not touch the count. */tty->driver_data = state;state->uart_port->state = state;tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;tty->alt_speed = 0;tty_port_tty_set(port, tty);/* * If the port is in the middle of closing, bail out now. */if (tty_hung_up_p(filp)) {retval = -EAGAIN;port->count--;mutex_unlock(&port->mutex);goto fail;}/* * Make sure the device is in D0 state. */if (port->count == 1)uart_change_pm(state, 0);/* * Start up the serial port. */retval = uart_startup(state, 0);/* * If we succeeded, wait until the port is ready. */if (retval == 0)retval = uart_block_til_ready(filp, state);mutex_unlock(&port->mutex);/* * If this is the first open to succeed, adjust things to suit. */if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) {set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);uart_update_termios(state);}fail:return retval;}
    上面代码中,逻辑性如下:
tty->driver_data = state
    这个tty设备的driver_data域保存了state(即上述分析的struct uart_state,见函数uart_add_one_port()).state和具体的某个uart port是关联的.这在后续的write()、read()等系统调用有很重大的意义.下面分析另外一个系统调用函数:ioctl.

    ioctl()

    比如我们对设备节点进行ioctl的TIOCMGET命令获取MODEM的状态.流程分析如下:

    首先是tty_fops相应的ioctl被调用:

static const struct file_operations tty_fops = {.llseek= no_llseek,.read= tty_read,.write= tty_write,.poll= tty_poll,.unlocked_ioctl= tty_ioctl,.compat_ioctl= tty_compat_ioctl,.open= tty_open,.release= tty_release,.fasync= tty_fasync,};
    展开tty_ioctl:

long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg){... ...;        case TIOCMGET:            return tty_tiocmget(tty, file, p);        ... ...; }
    展开tty_tiocmget()函数:

static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p){int retval = -EINVAL;if (tty->ops->tiocmget) {retval = tty->ops->tiocmget(tty, file);if (retval >= 0)retval = put_user(retval, p);}return retval;}
    这里引发了uart_ops操作集的tiocmget函数的运行.因为tty在初始化的时候其tty->ops被初始化为uart_ops.追踪tty_init_driver()-->initialize_tty_struct()可以找到此依据.

    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};
    展开uart_tiocmget函数:

static int uart_tiocmget(struct tty_struct *tty, struct file *file){struct uart_state *state = tty->driver_data;struct tty_port *port = &state->port;struct uart_port *uport = state->uart_port;int result = -EIO;mutex_lock(&port->mutex);if ((!file || !tty_hung_up_p(file)) &&    !(tty->flags & (1 << TTY_IO_ERROR))) {result = uport->mctrl;spin_lock_irq(&uport->lock);result |= uport->ops->get_mctrl(uport);spin_unlock_irq(&uport->lock);}mutex_unlock(&port->mutex);return result;}
    语句uport->ops->get_mctrl被执行.这里是指具体平台相关的uart_port操作集:s3c24xx_serial_ops.

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,};
    展开s3c24xx_serial_get_mctrl函数:

static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port){unsigned int umstat = rd_regb(port, S3C2410_UMSTAT);if (umstat & S3C2410_UMSTAT_CTS)return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;elsereturn TIOCM_CAR | TIOCM_DSR;}

    匆忙略过,作笔记谨作备忘.错漏难免,多多指正.