Linux串口驱动(1) - serial层

1. serial 层的初始化

        以IMX6的串口驱动为例,文件在drivers/tty/serial/imx.c,初始化概述如下:

module_init(imx_serial_init)
    -->uart_register_driver(&imx_reg);
		-->tty_set_operations(normal, &uart_ops);
			-->driver->ops = op;
		-->tty_register_driver(normal);
			-->dev = MKDEV(driver->major, driver->minor_start); 
			-->error = register_chrdev_region(dev, driver->num, driver->name);     //注册字符设备ttymxc0~4
			-->tty_cdev_add(driver, dev, 0, driver->num);
				-->cdev_init(&driver->cdevs[index], &tty_fops);					      
					-->cdev->ops = fops;		//将我们定义好的file_operaionts与cdev关联起来
	-->platform_driver_register(&serial_imx_driver);
		-->driver_register(&drv->driver);

2. 具体代码分析

        初始化过程中注册和关联的五个重要结构体

#define DEV_NAME                "ttymxc"
static struct uart_driver imx_reg = {
	.owner          = THIS_MODULE,
	.driver_name    = DRIVER_NAME,
	.dev_name       = DEV_NAME,				//ttymxc
	.major          = SERIAL_IMX_MAJOR,
	.minor          = MINOR_START,
	.nr             = ARRAY_SIZE(imx_ports),
	.cons           = IMX_CONSOLE,
};

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,
	.enable_ms	= imx_enable_ms,
	.break_ctl	= imx_break_ctl,
	.startup	= imx_startup,
	.shutdown	= imx_shutdown,
	.flush_buffer	= imx_flush_buffer,
	.set_termios	= imx_set_termios,
	.type		= imx_type,
	.config_port	= imx_config_port,
	.verify_port	= imx_verify_port,
#if defined(CONFIG_CONSOLE_POLL)
	.poll_get_char  = imx_poll_get_char,
	.poll_put_char  = imx_poll_put_char,
#endif
};

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",
		.owner	= THIS_MODULE,
		.of_match_table = imx_uart_dt_ids,
	},
};

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

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

        驱动入口函数

static int __init imx_serial_init(void)
{
	ret = uart_register_driver(&imx_reg);  //1.关键结构体的注册
	ret = platform_driver_register(&serial_imx_driver);  //2.关键结构体的注册
     ······
	return ret;
}
module_init(imx_serial_init);

        注册串口驱动 imx_reg

//serial_core.c
int uart_register_driver(struct uart_driver *drv)
{
    struct tty_driver *normal;
	normal = alloc_tty_driver(drv->nr);
	drv->tty_driver = normal;
    //drv是传进来的imx_reg
	normal->driver_name	= drv->driver_name;   //即imx_reg->driver_name
	normal->name		= drv->dev_name;      //即imx_reg->dev_name
	normal->major		= drv->major;         //即imx_reg->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);     //3.关键结构体的赋值,设置normal->ops = uart_ops
    ······
	retval = tty_register_driver(normal);
}

//tty_io.c
int tty_register_driver(struct tty_driver *driver)
{
	if (!driver->major) //imx_reg->major有设置,所以这里不成立
     ······
   else {
		dev = MKDEV(driver->major, driver->minor_start);
		error = register_chrdev_region(dev, driver->num, driver->name);  //注册字符设备ttymxc0~4
	}

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

	list_add(&driver->tty_drivers, &tty_drivers);  //将tty_driver(即传进来的normal)添加到全局链表tty_drivers

	if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
		for (i = 0; i < driver->num; i++) 
			d = tty_register_device(driver, i, NULL);   //暂时没分析到
	}
	proc_tty_register_driver(driver);
	driver->flags |= TTY_DRIVER_INSTALLED;
	return 0;
}

static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
		unsigned int index, unsigned int count)
{
	cdev_init(&driver->cdevs[index], &tty_fops);  //4. 关键结构体的赋值,将tty_fops和字符设备cdev联系起来,注意tty_fops是file_operaionts类型哦,操作字符设备的接口集
	driver->cdevs[index].owner = driver->owner;
	return cdev_add(&driver->cdevs[index], dev, count);
}

//char_dev.c
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
	memset(cdev, 0, sizeof *cdev);
	INIT_LIST_HEAD(&cdev->list);
	kobject_init(&cdev->kobj, &ktype_cdev_default);
	cdev->ops = fops;   //关键操作
}

        注册平台驱动 serial_imx_driver

int platform_driver_register(struct platform_driver *drv)
{
	drv->driver.bus = &platform_bus_type;
	if (drv->probe)
		drv->driver.probe = platform_drv_probe;
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值