s3c6410控制台驱动

来源:互联网 发布:中国统计 城市数据分析 编辑:程序博客网 时间:2024/04/30 18:31

开始接触时没弄清楚方向,把tty驱动全部浏览了一片,最后发现控制台驱动只和3个文件特别相关:

(1)s3c6400.c

(2)samsung.c

(3)serial_core.c

先说下这些文件的各自功能:

(1)看看这个文件的关键代码

static int __init s3c6400_serial_init(void)
{
 return s3c24xx_serial_init(&s3c6400_serial_driver, &s3c6400_uart_inf);//samsung.c函数定义
}

static void __exit s3c6400_serial_exit(void)
{
 platform_driver_unregister(&s3c6400_serial_driver);
}

module_init(s3c6400_serial_init);

module_exit(s3c6400_serial_exit);

这个C文件完成平台函数的卸载工作,并在

static struct platform_driver s3c6400_serial_driver = {
 .probe  = s3c6400_serial_probe,
 .remove  = __devexit_p(s3c24xx_serial_remove),
 .driver  = {
  .name = "s3c6400-uart",
  .owner = THIS_MODULE,
 },
 };
声明平台操作函数。

s3c6400_serial_init转向另一个文件samsung.c

由第一个C文件的s3c24xx_serial_init这个函数跳到第二个C文件的

int s3c24xx_serial_init(struct platform_driver *drv,
   struct s3c24xx_uart_info *info)
{
 dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);

#ifdef CONFIG_PM
 drv->suspend = s3c24xx_serial_suspend;
 drv->resume = s3c24xx_serial_resume;
#endif

 return platform_driver_register(drv);//平台驱动注册
}此时在第二个C完成两个重要的功能:

1:平台侦测函数

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

 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;

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

(2):在此C文件完成模块加载

static int __init s3c24xx_serial_modinit(void)//串口初始化
{
 int ret;

 ret = uart_register_driver(&s3c24xx_uart_drv);////注册uart驱动,在serial_core.c中实现 
 if (ret < 0) {
  printk(KERN_ERR "failed to register UART driver\n");
  return -1;
 }

 return 0;
}

module_init(s3c24xx_serial_modinit);

继续追踪串口驱动注册函数到第三个C文件:

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;

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

uart操作赋值给tty,定义在tty_io.c

 /*
  * 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);//初始化tty端口,添加端口,在核心层的平台侦测函数里
  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);//tty驱动注册
 if (retval >= 0)
  return retval;

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

在这一层实现最底层数据收发功能

static int s3c24xx_serial_startup(struct uart_port *port)//申请中断资源,初始化底层驱动状态,并开启端口为可接收数据状态,并连接收发的中断服务函数
{
 struct s3c24xx_uart_port *ourport = to_ourport(port);
 int ret;

 dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
     port->mapbase, port->membase);

 rx_enabled(port) = 1;

 ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,//申请中断资源
     s3c24xx_serial_portname(port), ourport);

 if (ret != 0) {
  printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
  return ret;
 }

 ourport->rx_claimed = 1;

 dbg("requesting tx irq...\n");

 tx_enabled(port) = 1;

 ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,//申请中断资源
     s3c24xx_serial_portname(port), ourport);

 if (ret) {
  printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
  goto err;
 }

 ourport->tx_claimed = 1;

 dbg("s3c24xx_serial_startup ok\n");

 /* the port reset code should have done the correct
  * register setup for the port controls */

 return ret;

 err:
 s3c24xx_serial_shutdown(port);
 return ret;
}

到目前为止完成了平台、TTY、uart驱动注册,第一次写有点乱。