AM335X 串口驱动学习(1)-基于linux3.8内核

来源:互联网 发布:魔方拼图软件 编辑:程序博客网 时间:2024/06/07 03:00

学习串口驱动,先从数据结构入手吧。串口驱动有3个核心数据结构:

(/drivers/tty/serial/omap-serial.c)
- UART特定的驱动程序结构定义:struct uart_driver serial_omap_reg;
- UART端口结构定义: struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];
- UART相关操作函数结构定义: struct uart_ops serial_omap_pops;

重要数据结构

1. uart_driver

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

#include <linux/serial_core.h>  struct uart_driver {      struct module    *owner;      /* 拥有该uart_driver的模块,一般为       THIS_MODULE */      const char    *driver_name; /* 串口驱动名,串口设备文件名以驱动名为基础 */      const char    *dev_name;      /* 串口设备名 */      int             major;          /* 主设备号 */      int             minor;          /* 次设备号 */      int             nr;          /* 该uart_driver支持的串口个数(最大) */      struct console    *cons;    /* 其对应的console.若该uart_driver支持serial console,否则为NULL */      /*       * 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;

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

#include<linux/serial_core.h>static struct uart_driver serial_omap_reg = {    .owner      = THIS_MODULE,    .driver_name    = "OMAP-SERIAL",    .dev_name   = OMAP_SERIAL_NAME,    .nr     = OMAP_MAX_HSUART_PORTS,    .cons       = OMAP_CONSOLE,};

一个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内存地址等信息;实际上,一个uart_port实例对应一个串口设备。

#include<linux/serial_core.h>  struct uart_port {      spinlock_t        lock;            /* port lock 串口端口锁 */      unsigned long        iobase;        /* in/out[bwl] io端口基地 */      unsigned char __iomem    *membase;/* read/write[bwl] IO内存基地址,经映射(如ioremap)后的IO内存虚拟基地址 */      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;      /*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        irq_wake;      unsigned char        unused[2];      void            *private_data;        /* generic platform data pointer */  };

uart_omap_port 封装了uart_port:

#inclide<linux/serial_core.h>struct uart_omap_port {    struct uart_port    port;    struct uart_omap_dma    uart_dma;    struct device       *dev;    unsigned char       ier;    unsigned char       lcr;    unsigned char       mcr;    unsigned char       fcr;    unsigned char       efr;    unsigned char       dll;    unsigned char       dlh;    unsigned char       mdr1;    unsigned char       scr;    int         use_dma;    /*     * Some bits in registers are cleared on a read, so they must     * be saved whenever the register is read but the bits will not     * be immediately processed.     */    unsigned int        lsr_break_flag;    unsigned char       msr_saved_flags;    char            name[20];    unsigned long       port_activity;    int         context_loss_cnt;    u32         errata;    u8          wakeups_enabled;    int         DTR_gpio;    int         DTR_inverted;    int         DTR_active;    struct pm_qos_request   pm_qos_request;    u32         latency;    u32         calc_latency;    struct work_struct  qos_work;    struct pinctrl      *pins;    struct serial_rs485 rs485;};
static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];

由此可见,对串口的一系列初始化,实际上落到了对ui结构体的填充上。

在serial_omap_probe(struct platform_device *pdev)中:
struct uart_omap_port up; /*uart_omap_port 封装了uart_port /
ui[up->port.line] = up;

serial_omap_add_console_port(up);
uart_add_one_port(&serial_omap_reg, &up->port);

  • uart_iconut为串口信息计数器,包含了发送字符计数、接收字符计数等。在串口的发送中断处理函数和接收中断处理函数中,我们需要管理这些计数;
   struct uart_icount {      __u32    cts;      __u32    dsr;      __u32    rng;      __u32    dcd;      __u32    rx;      /* 发送字符计数 */      __u32    tx;      /* 接受字符计数 */      __u32    frame;   /* 帧错误计数 */      __u32    overrun; /* Rx FIFO溢出计数 */      __u32    parity;  /* 帧校验错误计数 */      __u32    brk;     /* break计数 */      __u32    buf_overrun;   };
  • uart_stat有两个成员在底层串口驱动会用到:xmit和port。用户空间程序通过串口发送数据时,上层驱动将用户数据保存在xmit;而串口发送中断处理函数就是通过xmit获取到用户数据并将它们发送出去。串口接收中断处理函数需要通过port将接收到的数据传递给行规则层。
  /*   * This is the state information which is persistent across opens.   */  struct uart_state {      struct tty_port        port;      int            pm_state;      struct circ_buf        xmit;      struct tasklet_struct    tlet;      struct uart_port    *uart_port; };

3. uart_ops

uart_ops涵盖了串口驱动可对串口设备进行的所有操作

  /*   * This structure describes all the operations that can be   * done on the physical hardware.   */  struct uart_ops {      unsigned int    (*tx_empty)(struct uart_port *);/* 串口的Tx FIFO缓存是否为空 */      void        (*set_mctrl)(struct uart_port *, unsigned int mctrl);/* 设置串口modem控制 */      unsigned int    (*get_mctrl)(struct uart_port *); /* 获取串口modem控制 */      void        (*stop_tx)(struct uart_port *);/* 禁止串口发送数据 */      void        (*start_tx)(struct uart_port *);/* 使能串口发送数据 */      void        (*send_xchar)(struct uart_port *, char ch);/* 发送xChar */      void        (*stop_rx)(struct uart_port *);/* 禁止串口接收数据 */      void        (*enable_ms)(struct uart_port *);/* 使能modem的状态信号 */      void        (*break_ctl)(struct uart_port *, int ctl); /* 设置break信号 */      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);      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 *);/* 释放串口已申请的IO端口/IO内存资源,必要时还需iounmap */      /*       * Request IO and memory resources used by the port.       * This includes iomapping the port if necessary.       */      int        (*request_port)(struct uart_port *); /* 申请必要的IO端口/IO内存资源,必要时还可以重新映射串口端口 */      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驱动的主体工作通过下列函数来实现:

#include<>static struct uart_ops serial_omap_pops = {    .tx_empty   = serial_omap_tx_empty,/*检车发送FIFO缓冲区是否空*/    .set_mctrl  = serial_omap_set_mctrl,/*是否设置串口流控cts*/    .get_mctrl  = serial_omap_get_mctrl,/*是否串口流控*/    .stop_tx    = serial_omap_stop_tx,/*停止发送*/    .start_tx   = serial_omap_start_tx,/*启动发送*/    .throttle   = serial_omap_throttle,/**/    .unthrottle = serial_omap_unthrottle,/**/    .stop_rx    = serial_omap_stop_rx,/*停止接收*/    .enable_ms  = serial_omap_enable_ms,/**/    .break_ctl  = serial_omap_break_ctl,/*发送break信号*/    .startup    = serial_omap_startup,/*串口发送/接收,以及中断申请初始配置函数*/    .shutdown   = serial_omap_shutdown,/*关闭串口*/    .set_termios    = serial_omap_set_termios,/*串口clk,波特率,数据位等参数设置*/    .pm     = serial_omap_pm,/*电源管理函数*/    .set_wake   = serial_omap_set_wake,/**/    .type       = serial_omap_type,/*CPU类型关于串口*/    .release_port   = serial_omap_release_port,/*释放串口*/    .request_port   = serial_omap_request_port,/*申请串口*/    .config_port    = serial_omap_config_port,/*串口的一些配置信息info*/    .verify_port    = serial_omap_verify_port,/*串口检测*/    .ioctl      = serial_omap_ioctl,/**/#ifdef CONFIG_CONSOLE_POLL    .poll_put_char  = serial_omap_poll_put_char,/**/    .poll_get_char  = serial_omap_poll_get_char,/**/#endif};

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

(待续)

0 0
原创粉丝点击