新路程------uart驱动和tty(2)

之前已经把device注册到了系统中,接下来就是对应的驱动程序

kernel/drivers/tty/serial/imx.c

先看init函数

static int __init imx_serial_init(void)
{
	int ret;

	printk(KERN_INFO "Serial: IMX driver\n");

	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 0;
}
这个imx_reg

static struct uart_driver imx_reg = {
.owner          = THIS_MODULE,
.driver_name    = DRIVER_NAME, #define DRIVER_NAME "IMX-uart"
.dev_name       = DEV_NAME, #define DEV_NAME "ttymxc"
.major          = SERIAL_IMX_MAJOR, 
.minor          = MINOR_START,
.nr             = ARRAY_SIZE(imx_ports),
.cons           = IMX_CONSOLE,
};
再看函数

nt uart_register_driver(struct uart_driver *drv)
{
	struct tty_driver *normal;  //tty驱动出现了,在上层调用串口的时候,其实只是和tty驱动打交道,而不是和uart 驱动打交道
	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;  //把uart drv和tty drv联系起来

	normal->owner		= drv->owner;
	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);  //关键中的关键,把硬件驱动的ops函数传给了tty驱动,这样上层可以通过tty操作uart驱动再到底层的硬件上

	/*
	 * 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;
}
然后再看下面的函数其实就是一个字符设备驱动而已

/*
 * Called by a tty driver to register itself.
 */
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 {
/* 为设备分配主设备号和次设备号                                          |  
         * 主设备号207,次设备号从16开始                                        |
         * 所以注册的串口设备次设备号是16,17,18,19...                            |  
         */     
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);
 
  



接着看imx_serial_init的下半部分也就是把uart的驱动加入linux系统的驱动列表方便device和driver匹配

static struct platform_driver serial_imx_driver = {
.probe = serial_imx_probe,
.remove = serial_imx_remove,


.suspend = serial_imx_suspend,
.resume = serial_imx_resume,
.driver = {
.name = "imx-uart",
.owner = THIS_MODULE,
},
};

probe如下

static int serial_imx_probe(struct platform_device *pdev)
{
	struct imx_port *sport;
	struct imxuart_platform_data *pdata;
	void __iomem *base;
	int ret = 0;
	struct resource *res;

	sport = kzalloc(sizeof(*sport), GFP_KERNEL);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	

	base = ioremap(res->start, PAGE_SIZE);
	
	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 = platform_get_irq(pdev, 0);
	sport->rxirq = platform_get_irq(pdev, 0);
	sport->txirq = platform_get_irq(pdev, 1);
	sport->rtsirq = platform_get_irq(pdev, 2);
	sport->port.fifosize = 32;
	sport->port.ops = &imx_pops;  //上层的tty read和write的最后实现就是这个pops
	sport->port.flags = UPF_BOOT_AUTOCONF;
	sport->port.line = pdev->id;
	init_timer(&sport->timer);
	sport->timer.function = imx_timeout;
	sport->timer.data     = (unsigned long)sport;

	sport->clk = clk_get(&pdev->dev, "uart");
	
	if (uart_at_24)
		clk_set_parent(sport->clk, clk_get(NULL, "osc"));

	clk_enable(sport->clk);

	sport->port.uartclk = clk_get_rate(sport->clk);

	imx_ports[pdev->id] = sport;

	pdata = pdev->dev.platform_data;
	if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
		sport->have_rtscts = 1;
	if (pdata && (pdata->flags & IMXUART_USE_DCEDTE))
		sport->use_dcedte = 1;
	if (pdata && (pdata->flags & IMXUART_SDMA))
		sport->enable_dma = 1;

#ifdef CONFIG_IRDA
	if (pdata && (pdata->flags & IMXUART_IRDA))
		sport->use_irda = 1;
#endif

	if (pdata && pdata->init) {
		ret = pdata->init(pdev);
		if (ret)
			goto clkput;
	}

	ret = uart_add_one_port(&imx_reg, &sport->port);
	if (ret)
		goto deinit;
	platform_set_drvdata(pdev, &sport->port);

	clk_disable(sport->clk);
	return 0;

	return ret;
}
其中的关键函数是uart_add_one_port(&imx_reg, &sport->port)

/**
 *	uart_add_one_port - attach a driver-defined port structure
 *	@drv: pointer to the uart low level driver structure for this port
 *	@uport: uart port structure to use for this port.
 *
 *	This allows the driver to register its own uart_port structure
 *	with the core driver.  The main purpose is to allow the low
 *	level uart drivers to expand uart_port, rather than having yet
 *	more levels of structures.
 */
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);  //tty driver只有一个,但是ttydevice有多个,这里生成了/dev/tty节点
	if (likely(!IS_ERR(tty_dev))) {
		device_set_wakeup_capable(tty_dev, 1);
	} 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;
}

在我的kernel中

cd dev

ls之后会看到

ttymxc0

ttymxc1

ttymxc2

ttymxc3

ttymxc4

这个就是对应的之前imx_reg的.dev_name       = DEV_NAME, #define DEV_NAME "ttymxc"

这里的0,1,2,3对应的是dos端也就是上位机的pc端的com0,1,2

关于/dev/console  应该来说更像一个缓冲结果吧,来实现对内核的打印,比如说内核把要打印的内容装入缓冲区,

然后由console来决定打印到哪里吧(比如是tty0还是串口等等吧)。所以说/dev/console是用来外接控制台的。

你可能感兴趣的:(linux驱动)