您的位置:首页 > 其它

19.串口驱动程序学习(一)

2016-04-09 17:14 225 查看
            串口驱动程序学习

  本文主要实现对串口驱动程序初始化的分析

一、串口驱动中的数据结构

  尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实现了UART设备的通用tty驱动层,称为串口核心层,这样,UART驱动的主要任务变成了实现serial_core.c中定义的一组uart_xxx接口而非tty_xxx接口。

1.1下图描述了串行系统间的层次结构关系,可以概括为:

  用户应用层 --> 线路规划层 --> TTY层 --> 底层驱动层 --> 物理硬件层

  


1.2下图是串口核心层在整个tty源文件关系及数据流向中的位置:

  


其中的xxx_uart.c在此处就是drivers/tty/serial/samsung.c和s3c6400.c

二、串口驱动中的数据结构

  


1.4使用到的数据结构

Uart驱动程序主要围绕三个关键的数据结构展开(include/linux/serial_core.h中定义):

UART驱动程序结构:struct uart_driver

UART端口结构: struct uart_port

UART相关操作函数结构: struct uart_ops

1.4.1其中一个串口驱动对应一个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_state是设备状态结构结构体:

struct uart_state {
struct tty_port        port;
int            pm_state;
struct circ_buf        xmit;
struct tasklet_struct    tlet;
struct uart_port    *uart_port;
};


  在uart_open()中:

   tty->driver_data = state;

  在其他uart_xxx()中:

   struct uart_state *state = tty->driver_data;

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

1.4.2uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或者IO内存地址等信息--->即一个uart_port对应一个端口

struct uart_port {
spinlock_t        lock;            /* port lock */
unsigned long        iobase;            /* in/out[bwl] */
unsigned char __iomem    *membase;        /* read/write[bwl] */
unsigned int        (*serial_in)(struct uart_port *, int);
void            (*serial_out)(struct uart_port *, int, int);
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;
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 */
};


1.4.3uart_ops定义了针对UART的一系列操作

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 *, int new);
void        (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
int        (*set_wake)(struct uart_port *, unsigned int state);

/*
* Return a string describing the type of the port
*/
const char *(*type)(struct uart_port *);

/*
* Release IO and memory resources used by the port.
* This includes iounmap if necessary.
*/
void        (*release_port)(struct uart_port *);

/*
* Request IO and memory resources used by the port.
* This includes iomapping the port if necessary.
*/
int        (*request_port)(struct uart_port *);
void        (*config_port)(struct uart_port *, int);
int        (*verify_port)(struct uart_port *, struct serial_struct *);
int        (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
void    (*poll_put_char)(struct uart_port *, unsigned char);
int        (*poll_get_char)(struct uart_port *);
#endif
};


UART信息结构: struct uart_info

二、串口初始化分析

  


分析过程:

2.1进入到内核的Samsung.c文件中

  找到内核模块加载函数:module_init(s3c24xx_serial_modinit);

  可以看到加载模块直接调用platform_driver_register,注册了 开发板串口这个平台驱动。

  因为把uart驱动注册为platform驱动,当平台驱动与平台设备进行匹配的时候会调用平台总线的match函数,匹配成功后就会调用平台驱动的xxx_probe()函数来进行一系列的初始化工作。

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: