Linux UART 驱动 Part-1 (底层对接)

目录

1、底层对接

1.1、uart_driver 结构

1.2、注册 uart_driver

1.2.1、uart_state 结构

1.3、增加 uart_port

1.3.1、uart_port 结构

1.3.2、uart_ops 结构

1.4、小结

1.5、注意


 

串口驱动,内核启动的时候,便初始化完成,后期i应用层用到的时候,访问设备节点便,获取串口设备的 fd 句柄,然后设置相关的参数(波特率,停止位,校验位等等)便可以使用串口进行数据收发。(比如,Bluetooth 芯片通过 UART 挂到 CPU 上,应用程序通过 HCI 命令,用串口给 Bluetooth 芯片进行交互)。

Linux Kernel 的 UART 串口分为几层,tty 核心层,tty 线路规程,串行驱动核心层,以及下面对接具体芯片的 ops:

整个流程走下来,有太多的数据结构和 ops,要分析清楚整个流程,有两种方式:

1、至上而下,即,从用户空间调用串口配置以及串口读写的角度来分析

2、从底层注册开始分析

本文先从底层注册开始分析,根据内核已知提供的接口,逐步打开魔盒,力求扩散到每一个常用的地方;读者也可以按照这个脉络,自行查看 Source code,毕竟某大神说过,最快的深入理解一个模块的方法便是 ”Read The F**king Source Code“,这一点我坚信不疑;

 

1、底层对接

Kernel 对 UART 设备提供了一套底层对接的接口,接口中涉及到一些结构体和函数,首先让我们来分析一下相关的结构体和函数,这样咱们便知道如何去实现一款芯片的 UART 并对接到 Kernel Driver 中去;

 

1.1、uart_driver 结构

首先映入眼帘的是这个 uart_driver  结构,每一款芯片的 UART 都需要去实现并定义个属于他自己的 uart_driver 结构(注意,这里的对接的芯片,所以相关的东西,都是代表着最底层的芯片信息),他定义在:< include/linux/serial_core.h >文件中,我们来看看他的定义:

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 的成员和 tty_driver 的成员,你看他的注释,说的够明确了把,让你别动他们俩!这里,我们暂且知道他们两个的存在,后面马上来讲。那么换句话来说,除了这两个成员,其他成员都是需要被初始化赋值的。打个比方来说:

static struct uart_driver s3c24xx_uart_drv = {
	.owner		= THIS_MODULE,
	.driver_name	= "s3c2410_serial",
	.nr		= CONFIG_SERIAL_SAMSUNG_UARTS,
	.cons		= S3C24XX_SERIAL_CONSOLE,
	.dev_name	= S3C24XX_SERIAL_NAME,
	.major		= S3C24XX_SERIAL_MAJOR,
	.minor		= S3C24XX_SERIAL_MINOR,
};

 

1.2、注册 uart_driver

我们准备好了一个 uart_drvier 呢,我们需要将它注册到 Kernel 中去,这个是内核开出来让芯片厂家使用的接口,叫做 uart_register_driver ,这个接口的实现在 < drivers/tty/serial/serial_core.c >

int uart_register_driver(struct uart_driver *drv)

他的具体实现是:

int uart_register_driver(struct uart_driver *drv)
{
---------------- Part 1 Start ----------------
	struct tty_driver *normal;
	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 = kcalloc(drv->nr, sizeof(struct uart_state), GFP_KERNEL);
	if (!drv->state)
		goto out;

---------------- Part 1 End ----------------

---------------- Part 2 Start ----------------
	normal = alloc_tty_driver(drv->nr);
	if (!normal)
		goto out_kfree;

	drv->tty_driver = normal;

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

---------------- Part 2 End ----------------

---------------- Part 3 Start ----------------
    /*
     * 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->ops = &uart_port_ops;
        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);
    if (retval >= 0)
        return retval;

    put_tty_driver(normal);
out_kfree:
    kfree(drv->state);
out:
    return -ENOMEM;
---------------- Part 3 End ----------------
}

代码不算多,根据芯片厂家自己定义的 uart_driver 传入结构体,做了几件事情:

Part 1、根据传入的 uart_driver 结构体中的 nr 成员,分配对应个数的 uart_state 成员

uart_driver 中的 nr 是什么含义呢?它其实代表了你一个 uart_driver 对应的实体硬件 UART 的个数!比如,某某芯片的介绍:”多达 3 个 UART 接口“,那么这个 nr 指的就是这个 3;那么这样看来,这个 uart_state 的含义就是对应了芯片的每一个 UART (比如 uart_state[0] 对应了芯片的 UART0 以此类推)。那么我们来看看这个 uart_state 结构具体长啥样:

 

1.2.1、uart_state 结构

一个 uart_state 定义在 < include/linux/serial_core.h > 文件中:

struct uart_state {
    struct tty_port     port;

    int         pm_state;
    struct circ_buf     xmit;

    struct tasklet_struct   tlet;
    struct uart_port    *uart_port;
};

他定义了一个 tty_port,pm 操作,环形 buffer,tasklet,和一个 uart_port;

注意,这里只有 uart_port 是指针类型,这意味着后面会给他分配空间

好了,回到 uart_register_driver 继续分析;

 

Part2、根据传入的 uart_driver 结构体,分配一个 tty_driver 成员,并对其进行赋值初始化

这部分,调用了 alloc_tty_driver 函数,分配一个 tty_driver,在 < drivers/tty/tty_io.c > 

struct tty_driver *alloc_tty_driver(int lines)
{
    struct tty_driver *driver;

    driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
    if (driver) {
        kref_init(&driver->kref);
        driver->magic = TTY_DRIVER_MAGIC;
        driver->num = lines;
        /* later we'll move allocation of tables here */
    }
    return driver;
}
EXPORT_SYMBOL(alloc_tty_driver);

对传入的 uart_drvier 下的 tty_drvier 分配了空间,并做了一下 tty 层的初始化;

在 Part 2 中有一个关键的操作:

tty_set_operations(normal, &uart_ops);

这里的 normal 是我们上面分配的 tty 层的 tty_driver,在这里设置了 tty_driver 的 ops 为 serial_core.c 中的 uart_ops,也就是 tty_driver 的 ops 调用,会走到这里来,他是一个 tty_operations 结构:

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_show	= uart_proc_show,
#endif
	.tiocmget	= uart_tiocmget,
	.tiocmset	= uart_tiocmset,
	.set_serial	= uart_set_info_user,
	.get_serial	= uart_get_info_user,
	.get_icount	= uart_get_icount,
#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_register_driver;

Part 3、根据传入的 uart_driver 结构体中的 state (前面已经分配了内存),初始化他的 tty_port ,并注册 tty_driver

可以看到,调用了 tty_port_init 来初始化了相关的 tty_port 结构:

void tty_port_init(struct tty_port *port)
{
	memset(port, 0, sizeof(*port));
	init_waitqueue_head(&port->open_wait);
	init_waitqueue_head(&port->close_wait);
	init_waitqueue_head(&port->delta_msr_wait);
	mutex_init(&port->mutex);
	mutex_init(&port->buf_mutex);
	spin_lock_init(&port->lock);
	port->close_delay = (50 * HZ) / 100;
	port->closing_wait = (3000 * HZ) / 100;
	kref_init(&port->kref);
}
EXPORT_SYMBOL(tty_port_init);

然后调用 tasklet_init 注册了 tasklet;

并最终调用 tty_register_driver 注册到了 tty 层,这 tty_register_driver 的操作,其实就是注册了字符设备,因为串口 UART 本质上来说就是一个字符设备:

int tty_register_driver(struct tty_driver *driver)
{
    int error;
    int i;
    dev_t dev;
    void **p = NULL;
    struct device *d;

    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++) {
            d = tty_register_device(driver, i, NULL);
            if (IS_ERR(d)) {
                error = PTR_ERR(d);
                goto err;
            }
        }
    }
    proc_tty_register_driver(driver);
    driver->flags |= TTY_DRIVER_INSTALLED;
    return 0;

err:
    for (i--; i >= 0; i--)
        tty_unregister_device(driver, i);

    mutex_lock(&tty_mutex);
    list_del(&driver->tty_drivers);
    mutex_unlock(&tty_mutex);

    unregister_chrdev_region(dev, driver->num);
    driver->ttys = NULL;
    driver->termios = NULL;
    kfree(p);
    return error;
}

EXPORT_SYMBOL(tty_register_driver);

通过调用 cdev_init(&driver->cdev, &tty_fops) 和 cdev_add 注册一个字符设备(字符设备的名字由我们驱动层的 uart_driver->name 指定),而这个字符设备的操作集为:tty_fops 请记住这个 ops,因为用户空间的 open ,write 都会直接对应到这里:

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

 

1.3、增加 uart_port

在 uart_register_driver 调用成功后,可以说,我们已经对 uart_drvier 进行了注册,并且实现了这个结构体中的绝大多数成员了,此刻,按照流程,需要调用 uart_add_one_port 结构来向已经注册进内核的 xxx uart_drvier 赋予其生命;

int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)

他有两个入参:

1、drv:为哪个 uart_driver 赋予一个 uart_port

2、uport:具体的 uart port

一个 uart_port 代表了一个硬件 UART,有多个 UART 的话,就要调用多次这个接口!

还记得之前那个 uart_state 结构么,他里面不就有一个 uart_port 么,没错,这里就是把这个 uport 赋值给对应的这个 state 下面的这个 uart_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);
    } else
        printk(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;
}

可以看到,这个 add one port 将一个 uart_port 添加到了传入的这个 uart_driver->state->uart_port 下面

那么这个 uart_port 又是从何而来的呢?既然是和芯片直接相关的,那么肯定是芯片厂家定义的。我们先看 uart_port 的定义:

 

1.3.1、uart_port 结构

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			(*set_ldisc)(struct uart_port *,
					     struct ktermios *);
	unsigned int		(*get_mctrl)(struct uart_port *);
	void			(*set_mctrl)(struct uart_port *, unsigned int);
	unsigned int		(*get_divisor)(struct uart_port *,
					       unsigned int baud,
					       unsigned int *frac);
	void			(*set_divisor)(struct uart_port *,
					       unsigned int baud,
					       unsigned int quot,

.................
	const struct uart_ops	*ops;
.................
}

他包含了这个 UART 的基地址,lock,等信息,当然,这个既然是和底层相关的,所以呢,芯片厂商需要定义自己的 uart_port 结构并填充他,比如:

struct s3c24xx_uart_port {
	unsigned char			rx_claimed;
	unsigned char			tx_claimed;
	unsigned int			pm_level;
	unsigned long			baudclk_rate;
	unsigned int			min_dma_size;

	unsigned int			rx_irq;
	unsigned int			tx_irq;

	unsigned int			tx_in_progress;
	unsigned int			tx_mode;
	unsigned int			rx_mode;

	struct s3c24xx_uart_info	*info;
	struct clk			*clk;
	struct clk			*baudclk;
	struct uart_port		port;
	struct s3c24xx_serial_drv_data	*drv_data;

	/* reference to platform data */
	struct s3c2410_uartcfg		*cfg;

	struct s3c24xx_uart_dma		*dma;

#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
	struct notifier_block		freq_transition;
#endif
};
static struct s3c24xx_uart_port
s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
	[0] = {
		.port = {
			.lock		= __PORT_LOCK_UNLOCKED(0),
			.iotype		= UPIO_MEM,
			.uartclk	= 0,
			.fifosize	= 16,
			.ops		= &s3c24xx_serial_ops,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 0,
		}
	},
	[1] = {
		.port = {
			.lock		= __PORT_LOCK_UNLOCKED(1),
			.iotype		= UPIO_MEM,
			.uartclk	= 0,
			.fifosize	= 16,
			.ops		= &s3c24xx_serial_ops,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 1,
		}
	},
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2

	[2] = {
		.port = {
			.lock		= __PORT_LOCK_UNLOCKED(2),
			.iotype		= UPIO_MEM,
			.uartclk	= 0,
			.fifosize	= 16,
			.ops		= &s3c24xx_serial_ops,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 2,
		}
	},
#endif
#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
	[3] = {
		.port = {
			.lock		= __PORT_LOCK_UNLOCKED(3),
			.iotype		= UPIO_MEM,
			.uartclk	= 0,
			.fifosize	= 16,
			.ops		= &s3c24xx_serial_ops,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 3,
		}
	}
#endif
};

这里我们主要关心后面的那个 uart_ops *ops 结构,在这里被赋值成为了 s3c24xx_serial_ops;

 

1.3.2、uart_ops 结构

uart_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		(*throttle)(struct uart_port *);
	void		(*unthrottle)(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 *, struct ktermios *);
	void		(*pm)(struct uart_port *, unsigned int state,
			      unsigned int oldstate);

	/*
	 * 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
	int		(*poll_init)(struct uart_port *);
	void		(*poll_put_char)(struct uart_port *, unsigned char);
	int		(*poll_get_char)(struct uart_port *);
#endif
};

 

一个芯片,需要实现 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,
	.set_mctrl	= s3c24xx_serial_set_mctrl,
	.stop_tx	= s3c24xx_serial_stop_tx,
	.start_tx	= s3c24xx_serial_start_tx,
	.stop_rx	= s3c24xx_serial_stop_rx,
	.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,
#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL)
	.poll_get_char = s3c24xx_serial_get_poll_char,
	.poll_put_char = s3c24xx_serial_put_poll_char,
#endif
};

挂接上去的函数都是寄存器级的硬件描述,到这里,对接底层的工作基本完成!

 

1.4、小结

对接底层的部分,Kernel 主要是提供了两个接口:

1、uart_register_driver (一次调用)

2、uart_add_one_port (多次调用)

通过这两个接口,实现了芯片将自己的 UART 对接到 Linux Kernel UART Driver 中。

芯片厂商需要自行设计并实现的部分有:

1、uart_drvier 结构(一个)

2、uart_port 结构(多个)

3、uart_ops 对串口的操作集(可能一个,可能多个)

所以从结构上来看,整个对接过程为:

从数据结构以及相互之间的关系来看:

 

 

1.5、注意

这里有一点需要特别注意,在对接底层的部分中,Kernel 定义了一个结构体叫:

struct uart_ops

在 tty 层,对 tty_driver 初始化的时候(serial_core.c),调用到:

tty_set_operations(normal, &uart_ops);

而他的实现是:

void tty_set_operations(struct tty_driver *driver,
			const struct tty_operations *op)
{
	driver->ops = op;
};
EXPORT_SYMBOL(tty_set_operations);

看到了么,传进去的是 tty_operations *op,所以,在 tty_driver 挂接的 uart_ops 并非那个 struct uart_ops,而是这个 serial_core.c 文件内定义的:

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_show	= uart_proc_show,
#endif
	.tiocmget	= uart_tiocmget,
	.tiocmset	= uart_tiocmset,
	.set_serial	= uart_set_info_user,
	.get_serial	= uart_get_info_user,
	.get_icount	= uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
	.poll_init	= uart_poll_init,
	.poll_get_char	= uart_poll_get_char,
	.poll_put_char	= uart_poll_put_char,
#endif
};

名字一样,但是不是同一个结构,容易让人眼花~~

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值