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

学习串口驱动,先从数据结构入手吧。串口驱动有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
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值