linux驱动之tty子系统框架

先熟悉几个重要的结构体:

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_register_driver初始化好的tty_driver便于后面取出真正注册cdev
};
struct uart_state {
	struct tty_port		port;//这个不清楚是干嘛

	enum uart_pm_state	pm_state;
	struct circ_buf		xmit;//这个好像是环形缓冲区,用于收发数据

	struct uart_port	*uart_port;//这个应该是指一个具体的端口
};
struct tty_port {
	.....
	const struct tty_port_operations *ops;	//重要
	.....
};
struct uart_port {
	.....
	const struct uart_ops	*ops;//重要,这个结构体应该是最底层的ops,上层的还有tty_operations
    unsigned int		line;			
    struct uart_state	*state;			//所以uart_port与uart_state两者相互连系
	.....
};
2.tty_driver下的
struct tty_driver {
	......
    struct tty_port **ports;
	/*
	 * Driver methods
	 */
	const struct tty_operations *ops;//重要
	struct list_head tty_drivers;
};
3.厂家的(包含uart_port)
struct imx_port {
	struct uart_port	port;//重要
	......
};

首先我们知道uart核心层调用

static int __init imx_serial_init(void)
{
	int ret = uart_register_driver(&imx_reg);

	if (ret)
		return ret;

	ret = platform_driver_register(&serial_imx_driver);
	if (ret != 0)
		uart_unregister_driver(&imx_reg);

	return ret;
}
uart_register_driver
int uart_register_driver(struct uart_driver *drv)
{
	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 = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
	if (!drv->state)
		goto out;

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

	drv->tty_driver = normal;//这里tty_driver赋值给uart_driver是为了后面真正注册cdev用
	//初始化tty_driver
	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);//设置tty_ops (内核uart子系统实现)

	/*
	 * Initialise the UART state(s).
	 */
	for (i = 0; i < drv->nr; i++) {//一个nr代表一个端口,这里初始化nr个端口,并设置其tty_port_ops
		struct uart_state *state = drv->state + i;
		struct tty_port *port = &state->port;

		tty_port_init(port);
		port->ops = &uart_port_ops;
	}

	retval = tty_register_driver(normal);//详细分析到底有没有在这里注册了好了cdev
	if (retval >= 0)
		return retval;

	for (i = 0; i < drv->nr; i++)
		tty_port_destroy(&drv->state[i].port);
	put_tty_driver(normal);
out_kfree:
	kfree(drv->state);
out:
	return -ENOMEM;
}

tty_register_driver:

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

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

	if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {
		error = tty_cdev_add(driver, dev, 0, driver->num);
		if (error)
			goto err_unreg_char;
	}

	mutex_lock(&tty_mutex);
	list_add(&driver->tty_drivers, &tty_drivers);
	mutex_unlock(&tty_mutex);

	if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {//前面flag给了TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV,这里又与TTY_DRIVER_DYNAMIC_DEV位与,结果非0,不进去。
		for (i = 0; i < driver->num; i++) {
			d = tty_register_device(driver, i, NULL);//没有真正执行
			if (IS_ERR(d)) {
				error = PTR_ERR(d);
				goto err_unreg_devs;
			}
		}
	}
	proc_tty_register_driver(driver);
	driver->flags |= TTY_DRIVER_INSTALLED;
	return 0;

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

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

err_unreg_char:
	unregister_chrdev_region(dev, driver->num);
err:
	return error;
}

经过分析得:真正注册tty_driver的地方是probe里面的uart_add_one_port里面的tty_port_register_device_attr函数。

而这里真正的作用是在于初始化好tty_driver(将内核实现的tty_ops赋值给tty_driver)并且将tty_driver存于uart_driver中,以便后面真正注册cdev使用。

实现platform的probe,并注册platform_driver
static struct platform_driver serial_imx_driver = {
	.probe		= serial_imx_probe,
	.remove		= serial_imx_remove,

	.suspend	= serial_imx_suspend,
	.resume		= serial_imx_resume,
	.id_table	= imx_uart_devtype,
	.driver		= {
		.name	= "imx-uart",
		.of_match_table = imx_uart_dt_ids,
	},
};

probe执行:

static int serial_imx_probe(struct platform_device *pdev)
{
	struct imx_port *sport;//里面包含uart_port
	void __iomem *base;
	int ret = 0;
	struct resource *res;
	int txirq, rxirq, rtsirq;

	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
	if (!sport)
		return -ENOMEM;

	ret = serial_imx_probe_dt(sport, pdev);
	if (ret > 0)
		serial_imx_probe_pdata(sport, pdev);
	else if (ret < 0)
		return ret;
	//获得硬件信息
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(base))
		return PTR_ERR(base);

	rxirq = platform_get_irq(pdev, 0);
	txirq = platform_get_irq(pdev, 1);
	rtsirq = platform_get_irq(pdev, 2);
	//初始化sport下的uart_port
	sport->port.dev = &pdev->dev;
	sport->port.mapbase = res->start;
	sport->port.membase = base;
	sport->port.type = PORT_IMX,
	sport->port.iotype = UPIO_MEM;
	sport->port.irq = rxirq;
	sport->port.fifosize = 32;
	sport->port.ops = &imx_pops;//重要:赋值uart_port下的ops,(由厂家实现)
	sport->port.rs485_config = imx_rs485_config;
	sport->port.rs485.flags =
		SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;
	sport->port.flags = UPF_BOOT_AUTOCONF;
	init_timer(&sport->timer);
	sport->timer.function = imx_timeout;
	sport->timer.data     = (unsigned long)sport;

	sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
	if (IS_ERR(sport->clk_ipg)) {
		ret = PTR_ERR(sport->clk_ipg);
		dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
		return ret;
	}

	sport->clk_per = devm_clk_get(&pdev->dev, "per");
	if (IS_ERR(sport->clk_per)) {
		ret = PTR_ERR(sport->clk_per);
		dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);
		return ret;
	}

	sport->port.uartclk = clk_get_rate(sport->clk_per);
	if (sport->port.uartclk > IMX_MODULE_MAX_CLK_RATE) {
		ret = clk_set_rate(sport->clk_per, IMX_MODULE_MAX_CLK_RATE);
		if (ret < 0) {
			dev_err(&pdev->dev, "clk_set_rate() failed\n");
			return ret;
		}
	}
	sport->port.uartclk = clk_get_rate(sport->clk_per);

	//注册中断
	if (txirq > 0) {
		ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0,
				       dev_name(&pdev->dev), sport);
		if (ret)
			return ret;

		ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0,
				       dev_name(&pdev->dev), sport);
		if (ret)
			return ret;
	} else {
		ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0,
				       dev_name(&pdev->dev), sport);
		if (ret)
			return ret;
	}
	//imx_ports 是一个全局imx_port类型结构体大小为UART_NR	(static struct imx_port *imx_ports[UART_NR];)
	imx_ports[sport->port.line] = sport;

	platform_set_drvdata(pdev, sport);
	//imx_reg就是前面uart_register_driver传入的uart_driver全局对象
	return uart_add_one_port(&imx_reg, &sport->port);//将probe初始化好的port加入到uart_driver中
}

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;
	int num_groups;

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

	/* Link the port to the driver state table and vice versa */
	state->uart_port = uport;//将probe初始化的具体端口赋值给state
	uport->state = state;//state与uart_port联系起来

	state->pm_state = UART_PM_STATE_UNDEFINED;
	uport->cons = drv->cons;
	uport->minor = drv->tty_driver->minor_start + uport->line;

	/*
	 * 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);
	}
	if (uport->cons && uport->dev)
		of_console_check(uport->dev->of_node, uport->cons->name, uport->line);

	uart_configure_port(drv, state, uport);//再配置一下uart_port

	num_groups = 2;
	if (uport->attr_group)
		num_groups++;

	uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
				    GFP_KERNEL);
	if (!uport->tty_groups) {
		ret = -ENOMEM;
		goto out;
	}
	uport->tty_groups[0] = &tty_dev_attr_group;
	if (uport->attr_group)
		uport->tty_groups[1] = uport->attr_group;

	//这里貌似真正注册cdev
	tty_dev = tty_port_register_device_attr(port, drv->tty_driver,
			uport->line, uport->dev, port, uport->tty_groups);
	if (likely(!IS_ERR(tty_dev))) {
		device_set_wakeup_capable(tty_dev, 1);
	} else {
		dev_err(uport->dev, "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;
}

tty_port_register_device_attr:(这里实现真正注册cdev,其tty_driver源于前面uart_register_driver初始化好的tty_driver)

struct device *tty_register_device_attr(struct tty_driver *driver,
				   unsigned index, struct device *device,
				   void *drvdata,
				   const struct attribute_group **attr_grp)
{
	char name[64];
	dev_t devt = MKDEV(driver->major, driver->minor_start) + index;//每porbe一次index就加1
	struct device *dev = NULL;
	int retval = -ENODEV;
	bool cdev = false;

	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);
	else
		tty_line_name(driver, index, name);

	if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
		retval = tty_cdev_add(driver, devt, index, 1);
		if (retval)
			goto error;
		cdev = true;
	}

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		retval = -ENOMEM;
		goto error;
	}

	dev->devt = devt;
	dev->class = tty_class;
	dev->parent = device;
	dev->release = tty_device_create_release;
	dev_set_name(dev, "%s", name);
	dev->groups = attr_grp;
	dev_set_drvdata(dev, drvdata);

	retval = device_register(dev);
	if (retval)
		goto error;

	return dev;

error:
	put_device(dev);
	if (cdev)
		cdev_del(&driver->cdevs[index]);
	return ERR_PTR(retval);
}

到这里我们基本上清楚了:当一个串口设备被match时,它的cdev是如何被注册的。

并且我们还知道了:tty_driver的ops是内核实现的,而uart_driver才是厂家实现的。

接着我们分析:用户如何通过cdev从应用层调用到最底层厂家实现的ops

static const struct tty_operations uart_ops = {//tty_ops linux tty子系统实现
	.open		= uart_open,
	.close		= uart_close,
	.write		= uart_write,
	.....
	}
static struct uart_ops imx_pops = {
	.tx_empty	= imx_tx_empty,
	.set_mctrl	= imx_set_mctrl,
	.get_mctrl	= imx_get_mctrl,
	.stop_tx	= imx_stop_tx,
	.start_tx	= imx_start_tx,
	.stop_rx	= imx_stop_rx,
	.....
	.startup	= imx_startup,
	.shutdown	= imx_shutdown,
	.....
};
open
static int uart_open(struct tty_struct *tty, struct file *filp)
{
	.....
	/*
	 * Start up the serial port.
	 */
	retval = uart_startup(tty, state, 0);//进入

	/*
	 * If we succeeded, wait until the port is ready.
	 */
	mutex_unlock(&port->mutex);
	if (retval == 0)
		retval = tty_port_block_til_ready(port, tty, filp);
	.....
}

uart_startup:

static int uart_startup(struct tty_struct *tty, struct uart_state *state,
		int init_hw)
{
	struct tty_port *port = &state->port;
	int retval;

	if (port->flags & ASYNC_INITIALIZED)
		return 0;

	/*
	 * Set the TTY IO error marker - we will only clear this
	 * once we have successfully opened the port.
	 */
	set_bit(TTY_IO_ERROR, &tty->flags);

	retval = uart_port_startup(tty, state, init_hw);//进入
	if (!retval) {
		set_bit(ASYNCB_INITIALIZED, &port->flags);
		clear_bit(TTY_IO_ERROR, &tty->flags);
	} else if (retval > 0)
		retval = 0;

	return retval;
}

uart_port_startup:

static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
		int init_hw)
{
	struct uart_port *uport = state->uart_port;
	unsigned long page;
	int retval = 0;

	if (uport->type == PORT_UNKNOWN)
		return 1;

	/*
	 * Make sure the device is in D0 state.
	 */
	uart_change_pm(state, UART_PM_STATE_ON);

	/*
	 * Initialise and allocate the transmit and temporary
	 * buffer.
	 */
	if (!state->xmit.buf) {
		/* This is protected by the per port mutex */
		page = get_zeroed_page(GFP_KERNEL);
		if (!page)
			return -ENOMEM;

		state->xmit.buf = (unsigned char *) page;
		uart_circ_clear(&state->xmit);
	}

	retval = uport->ops->startup(uport);//最终调用到uart_ops的startup
	if (retval == 0) {
		if (uart_console(uport) && uport->cons->cflag) {
			tty->termios.c_cflag = uport->cons->cflag;
			uport->cons->cflag = 0;
		}
		/*
		 * Initialise the hardware port settings.
		 */
		uart_change_speed(tty, state, NULL);

		if (init_hw) {
			/*
			 * Setup the RTS and DTR signals once the
			 * port is open and ready to respond.
			 */
			if (tty->termios.c_cflag & CBAUD)
				uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
		}
	}

	/*
	 * This is to allow setserial on this port. People may want to set
	 * port/irq/type and then reconfigure the port properly if it failed
	 * now.
	 */
	if (retval && capable(CAP_SYS_ADMIN))
		return 1;

	return retval;
}

imx_startup:

static int imx_startup(struct uart_port *port)
{
	struct imx_port *sport = (struct imx_port *)port;
	int retval, i;
	unsigned long flags, temp;

	retval = clk_prepare_enable(sport->clk_per);
	if (retval)
		return retval;
	retval = clk_prepare_enable(sport->clk_ipg);
	if (retval) {
		clk_disable_unprepare(sport->clk_per);
		return retval;
	}

	imx_setup_ufcr(sport, 0);

	/* disable the DREN bit (Data Ready interrupt enable) before
	 * requesting IRQs
	 */
	temp = readl(sport->port.membase + UCR4);

	/* set the trigger level for CTS */
	temp &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF);
	temp |= CTSTL << UCR4_CTSTL_SHF;

	writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);

	/* Reset fifo's and state machines */
	i = 100;

	temp = readl(sport->port.membase + UCR2);
	temp &= ~UCR2_SRST;
	writel(temp, sport->port.membase + UCR2);

	while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
		udelay(1);

	/* Can we enable the DMA support? */
	if (is_imx6q_uart(sport) && !uart_console(port)
		&& !sport->dma_is_inited)
		imx_uart_dma_init(sport);

	if (sport->dma_is_inited)
		INIT_DELAYED_WORK(&sport->tsk_dma_tx, dma_tx_work);//dma_tx_work是用于发送数据的工作队列handler

	spin_lock_irqsave(&sport->port.lock, flags);

	/*
	 * Finally, clear and enable interrupts
	 */
	writel(USR1_RTSD, sport->port.membase + USR1);
	writel(USR2_ORE, sport->port.membase + USR2);

	temp = readl(sport->port.membase + UCR1);
	if (!sport->dma_is_inited)
		temp |= UCR1_RRDYEN;
	if (sport->have_rtscts)
		temp |= UCR1_RTSDEN;
	temp |= UCR1_UARTEN;
	writel(temp, sport->port.membase + UCR1);

	temp = readl(sport->port.membase + UCR4);
	temp |= UCR4_OREN;
	writel(temp, sport->port.membase + UCR4);

	temp = readl(sport->port.membase + UCR2);
	temp |= (UCR2_RXEN | UCR2_TXEN);
	if (!sport->have_rtscts)
		temp |= UCR2_IRTS;
	writel(temp, sport->port.membase + UCR2);

	if (!is_imx1_uart(sport)) {
		temp = readl(sport->port.membase + UCR3);
		temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP;
		writel(temp, sport->port.membase + UCR3);
	}

	/*
	 * Enable modem status interrupts
	 */
	imx_enable_ms(&sport->port);
	spin_unlock_irqrestore(&sport->port.lock, flags);

	return 0;
}
write:

uart_write:

static int uart_write(struct tty_struct *tty,
					const unsigned char *buf, int count)
{
	struct uart_state *state = tty->driver_data;
	struct uart_port *port;
	struct circ_buf *circ;
	unsigned long flags;
	int c, ret = 0;

	/*
	 * This means you called this function _after_ the port was
	 * closed.  No cookie for you.
	 */
	if (!state) {
		WARN_ON(1);
		return -EL3HLT;
	}

	port = state->uart_port;
	circ = &state->xmit;

	if (!circ->buf)
		return 0;

	spin_lock_irqsave(&port->lock, flags);
	while (1) {
		c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
		if (count < c)
			c = count;
		if (c <= 0)
			break;
		memcpy(circ->buf + circ->head, buf, c);
		circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
		buf += c;
		count -= c;
		ret += c;
	}

	__uart_start(tty);
	spin_unlock_irqrestore(&port->lock, flags);

	return ret;
}

__uart_start:

static void __uart_start(struct tty_struct *tty)
{
	struct uart_state *state = tty->driver_data;
	struct uart_port *port = state->uart_port;

	if (!uart_tx_stopped(port))
		port->ops->start_tx(port);//最终调用到厂家实现的tx函数
}

如果启用了dma那么:在open时注册的dma_tx_work就会工作

static void dma_tx_work(struct work_struct *w)
{
	struct delayed_work *delay_work = to_delayed_work(w);
	struct imx_port *sport = container_of(delay_work, struct imx_port, tsk_dma_tx);
	struct circ_buf *xmit = &sport->port.state->xmit;
	struct scatterlist *sgl = sport->tx_sgl;
	struct dma_async_tx_descriptor *desc;
	struct dma_chan	*chan = sport->dma_chan_tx;
	struct device *dev = sport->port.dev;
	unsigned long flags;
	int ret;

	if (test_and_set_bit(DMA_TX_IS_WORKING, &sport->flags))
		return;

	spin_lock_irqsave(&sport->port.lock, flags);
	sport->tx_bytes = uart_circ_chars_pending(xmit);

	if (sport->tx_bytes > 0) {
		if (xmit->tail > xmit->head && xmit->head > 0) {
			sport->dma_tx_nents = 2;
			sg_init_table(sgl, 2);
			sg_set_buf(sgl, xmit->buf + xmit->tail,
					UART_XMIT_SIZE - xmit->tail);
			sg_set_buf(sgl + 1, xmit->buf, xmit->head);
		} else {
			sport->dma_tx_nents = 1;
			sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
		}
		spin_unlock_irqrestore(&sport->port.lock, flags);

		ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
		if (ret == 0) {
			dev_err(dev, "DMA mapping error for TX.\n");
			goto err_out;
		}
		desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
						DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
		if (!desc) {
			dev_err(dev, "We cannot prepare for the TX slave dma!\n");
			goto err_out;
		}
		desc->callback = dma_tx_callback;//dma接收中断回调函数
		desc->callback_param = sport;

		dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
				uart_circ_chars_pending(xmit));
		/* fire it */
		sport->dma_is_txing = 1;
		dmaengine_submit(desc);
		dma_async_issue_pending(chan);
		return;
	}
	spin_unlock_irqrestore(&sport->port.lock, flags);
err_out:
	clear_bit(DMA_TX_IS_WORKING, &sport->flags);
	smp_mb__after_atomic();
}
read:

同样read靠dma接收中断回调函数来接收数据:

像dma_tx_callback一样,在open中注册

static int uart_open(struct tty_struct *tty, struct file *filp)
{
	struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
	int retval, line = tty->index;
	struct uart_state *state = drv->state + line;
	struct tty_port *port = &state->port;

	pr_debug("uart_open(%d) called\n", line);

	spin_lock_irq(&port->lock);
	++port->count;
	spin_unlock_irq(&port->lock);

	/*
	 * We take the semaphore here 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.
	 */
	if (mutex_lock_interruptible(&port->mutex)) {
		retval = -ERESTARTSYS;
		goto end;
	}

	if (!state->uart_port || state->uart_port->flags & UPF_DEAD) {
		retval = -ENXIO;
		goto err_unlock;
	}

	tty->driver_data = state;
	state->uart_port->state = state;
	state->port.low_latency =
		(state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
	tty_port_tty_set(port, tty);

	/*
	 * Start up the serial port.
	 */
	retval = uart_startup(tty, state, 0);//进入

	/*
	 * If we succeeded, wait until the port is ready.
	 */
	mutex_unlock(&port->mutex);
	if (retval == 0)
		retval = tty_port_block_til_ready(port, tty, filp);

end:
	return retval;
err_unlock:
	mutex_unlock(&port->mutex);
	goto end;
}

uart_startup:

static int uart_startup(struct tty_struct *tty, struct uart_state *state,
		int init_hw)
{
	struct tty_port *port = &state->port;
	int retval;

	if (port->flags & ASYNC_INITIALIZED)
		return 0;

	/*
	 * Set the TTY IO error marker - we will only clear this
	 * once we have successfully opened the port.
	 */
	set_bit(TTY_IO_ERROR, &tty->flags);

	retval = uart_port_startup(tty, state, init_hw);
	if (!retval) {
		set_bit(ASYNCB_INITIALIZED, &port->flags);
		clear_bit(TTY_IO_ERROR, &tty->flags);
	} else if (retval > 0)
		retval = 0;

	return retval;
}

uart_port_startup:

static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
		int init_hw)
{
	......
		/*
		 * Initialise the hardware port settings.
		 */
		uart_change_speed(tty, state, NULL);//进入
	......
}

uart_change_speed:

static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
					struct ktermios *old_termios)
{
	struct uart_port *uport = state->uart_port;
	struct ktermios *termios;
	int hw_stopped;

	/*
	 * If we have no tty, termios, or the port does not exist,
	 * then we can't set the parameters for this port.
	 */
	if (!tty || uport->type == PORT_UNKNOWN)
		return;

	termios = &tty->termios;
	uport->ops->set_termios(uport, termios, old_termios);//重点,uart_ops的set_terminos
	......
}

imx_set_termios:

static void
imx_set_termios(struct uart_port *port, struct ktermios *termios,
		   struct ktermios *old)
{
	.....
	.....
	if (sport->dma_is_inited && !sport->dma_is_enabled) {
		imx_enable_dma(sport);
		start_rx_dma(sport);//进入
	}
	.....
}

start_rx_dma:

static int start_rx_dma(struct imx_port *sport)
{
	struct dma_chan	*chan = sport->dma_chan_rx;
	struct dma_async_tx_descriptor *desc;

	sport->rx_buf.periods = IMX_RXBD_NUM;
	sport->rx_buf.period_len = RX_BUF_SIZE;
	sport->rx_buf.buf_len = IMX_RXBD_NUM * RX_BUF_SIZE;
	sport->rx_buf.cur_idx = 0;
	sport->rx_buf.last_completed_idx = -1;
	desc = dmaengine_prep_dma_cyclic(chan, sport->rx_buf.dmaaddr,
		sport->rx_buf.buf_len, sport->rx_buf.period_len,
		DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
	if (!desc) {
		dev_err(sport->port.dev, "Prepare for the RX slave dma failed!\n");
		return -EINVAL;
	}

	desc->callback = dma_rx_callback;//设置dma接收中断回调函数
	desc->callback_param = sport;

	dev_dbg(sport->port.dev, "RX: prepare for the DMA.\n");
	sport->rx_buf.cookie = dmaengine_submit(desc);
	dma_async_issue_pending(chan);

	sport->dma_is_rxing = 1;
	return 0;
}

总结:
linux tty子系统主要分两层:

1.tty_driver核心层

2.uart_driver驱动层

1.其中tty_driver是用于

1.连接上层应用:通过注册cdev

2.连接底层驱动:tty_ops是调用了uart_driver的ops(前者是内核实现的,后者是厂家实现的)

2.uart_driver

1.像i2c/spi一样底层驱动是一个platform驱动

2.其probe用于注册tty_driver(注意:前面的uart_register_driver并没有真正注册tty驱动,只是初始化了,为后面真正注册做准备)

读写实现:

是靠uart_driver驱动实现的,在tty核心层最多起到配置筛选作用。

如果配置了dma那么就会有dma_tx_callback与dma_rx_callback来实现读写。(都是由厂家实现)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值