mini2440 串口和tty驱动的分析

周末闲得无聊,上周就看了看串口的驱动,串口的驱动和tty设备的驱动符合在一起,还是比较复杂的。其中串口驱动实现的是控制串口的收发,tty设备的驱动实现的是将串口虚拟成为一个文件,可以接收 用户的输入和输出,也就是我们看到的通过串口,连接显示器 操作嵌入式设备的实现。 另外还有tty规程,tty规程可以根据需要,有不同的实现,主要是控制输入和输出的格式的。
下面理一下串口和tty设备的驱动,为以后进一步研究留个念想。
 
在linux中串口设备本生是注册成为platformdevice的。首先在首先在mach-mini2440.c和devs.c文件中,列出了mini2440三个串口的配置信息。
 
// 这里主要是说明了三个串口各自的功能,因为在2440上面,串口也用作红外通信的功能。红外通信用到串口,难怪红外通信的速率很低。在这里三个串口都用作了串口通信的功能,没有用于红外通信。
static struct s3c2410_uartcfg mini2440_uartcfgs[] __initdata = {
     [0] = {
          .hwport          = 0,
          .flags          = 0,
          .ucon          = 0x3c5,
          .ulcon          = 0x03,
          .ufcon          = 0x51,
     },
     [1] = {
          .hwport          = 1,
          .flags          = 0,
          .ucon          = 0x3c5,
          .ulcon          = 0x03,
          .ufcon          = 0x51,
     },
     [2] = {
          .hwport          = 2,
          .flags          = 0,
          .ucon          = 0x3c5,
          .ulcon          = 0x03,
          .ufcon          = 0x51,
     }
};

// 下面的代码在devs.c中,这里主要是写明了串口要用到了的 寄存器地址 和 中断资源。
struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = {
     [0] = {
          .resources     = s3c2410_uart0_resource,
          .nr_resources     = ARRAY_SIZE(s3c2410_uart0_resource),
     },
     [1] = {
          .resources     = s3c2410_uart1_resource,
          .nr_resources     = ARRAY_SIZE(s3c2410_uart1_resource),
     },
     [2] = {
          .resources     = s3c2410_uart2_resource,
          .nr_resources     = ARRAY_SIZE(s3c2410_uart2_resource),
     },
     [3] = {
          .resources     = s3c2410_uart3_resource,
          .nr_resources     = ARRAY_SIZE(s3c2410_uart3_resource),
     },
};

static struct resource s3c2410_uart0_resource[] = {
     [0] = {
          .start = S3C2410_PA_UART0,
          .end   = S3C2410_PA_UART0 + 0x3fff,
          .flags = IORESOURCE_MEM,
     },
     [1] = {
          .start = IRQ_S3CUART_RX0,
          .end   = IRQ_S3CUART_ERR0,
          .flags = IORESOURCE_IRQ,
     }
};

static struct resource s3c2410_uart1_resource[] = {
     [0] = {
          .start = S3C2410_PA_UART1,
          .end   = S3C2410_PA_UART1 + 0x3fff,
          .flags = IORESOURCE_MEM,
     },
     [1] = {
          .start = IRQ_S3CUART_RX1,
          .end   = IRQ_S3CUART_ERR1,
          .flags = IORESOURCE_IRQ,
     }
};

static struct resource s3c2410_uart2_resource[] = {
     [0] = {
          .start = S3C2410_PA_UART2,
          .end   = S3C2410_PA_UART2 + 0x3fff,
          .flags = IORESOURCE_MEM,
     },
     [1] = {
          .start = IRQ_S3CUART_RX2,
          .end   = IRQ_S3CUART_ERR2,
          .flags = IORESOURCE_IRQ,
     }
};

static struct resource s3c2410_uart3_resource[] = {
     [0] = {
          .start = S3C2443_PA_UART3,
          .end   = S3C2443_PA_UART3 + 0x3fff,
          .flags = IORESOURCE_MEM,
     },
     [1] = {
          .start = IRQ_S3CUART_RX3,
          .end   = IRQ_S3CUART_ERR3,
          .flags = IORESOURCE_IRQ,
     },
};

以上都是需要用户在文件中写明的,下面程序开始执行后,就需要用以上的这些资源和配置信息。

我认为串口的驱动是从下面这个函数开始的,mini2440_map_io中调用了 

s3c24xx_init_uarts(mini2440_uartcfgs, ARRAY_SIZE(mini2440_uartcfgs));

void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no)
{
     if (cpu == NULL)
          return;

     if (cpu->init_uarts == NULL) {
          printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n");
     } else
          (cpu->init_uarts)(cfg, no);
}

cpu->init_uarts = s3c244x_init_uarts

void __init s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no)
{
     s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no);
}


void __init s3c24xx_init_uartdevs(char *name,
                      struct s3c24xx_uart_resources *res,
                      struct s3c2410_uartcfg *cfg, int no)
{
     struct platform_device *platdev;
     struct s3c2410_uartcfg *cfgptr = uart_cfgs;
     struct s3c24xx_uart_resources *resp;
     int uart;

     memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);

     for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {
          platdev = s3c24xx_uart_src[cfgptr->hwport];

          resp = res + cfgptr->hwport;

          s3c24xx_uart_devs[uart] = platdev;

          platdev->name = name;
          platdev->resource = resp->resources;
          platdev->num_resources = resp->nr_resources;

          platdev->dev.platform_data = cfgptr;
     }

     nr_uarts = no;
}

每个uart 都是一个platform device, 因此,在这里生成了 4 个 Platform device,其实在mini2440中应该是生成三个串口的,因为2440芯片上面只有三个串口。下面每个Platform device 都要调用他们的probe 函数。

下面就到了platformdevice的 probe 函数:在 s3c2440.c中

static struct s3c24xx_uart_info s3c2440_uart_inf = {
     .name          = "Samsung S3C2440 UART",
     .type          = PORT_S3C2440,
     .fifosize     = 64,
     .rx_fifomask     = S3C2440_UFSTAT_RXMASK,
     .rx_fifoshift     = S3C2440_UFSTAT_RXSHIFT,
     .rx_fifofull     = S3C2440_UFSTAT_RXFULL,
     .tx_fifofull     = S3C2440_UFSTAT_TXFULL,
     .tx_fifomask     = S3C2440_UFSTAT_TXMASK,
     .tx_fifoshift     = S3C2440_UFSTAT_TXSHIFT,
     .get_clksrc     = s3c2440_serial_getsource,
     .set_clksrc     = s3c2440_serial_setsource,
     .reset_port     = s3c2440_serial_resetport,
};

/* device management */

static int s3c2440_serial_probe(struct platform_device *dev)
{
     dbg("s3c2440_serial_probe: dev=%p\n", dev);
     return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
}


// samsung.c: 

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);  // 添加一个  uart 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;
}

// 申请资源等等。
static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
                        struct s3c24xx_uart_info *info,
                        struct platform_device *platdev)
{
     struct uart_port *port = &ourport->port;
     struct s3c2410_uartcfg *cfg;
     struct resource *res;
     int ret;

     dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);

     if (platdev == NULL)
          return -ENODEV;

     cfg = s3c24xx_dev_to_cfg(&platdev->dev);

     if (port->mapbase != 0)
          return 0;

     if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) {
          printk(KERN_ERR "%s: port %d bigger than %d\n", __func__,
                 cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS);
          return -ERANGE;
     }

     /* setup info for port */
     port->dev     = &platdev->dev;
     ourport->info     = info;

     /* copy the info in from provided structure */
     ourport->port.fifosize = info->fifosize;

     dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport);

     port->uartclk = 1;

     if (cfg->uart_flags & UPF_CONS_FLOW) {
          dbg("s3c24xx_serial_init_port: enabling flow control\n");
          port->flags |= UPF_CONS_FLOW;
     }

     /* sort our the physical and virtual addresses for each UART */

     res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
     if (res == NULL) {
          printk(KERN_ERR "failed to find memory resource for uart\n");
          return -EINVAL;
     }

     dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);

     port->mapbase = res->start;
     port->membase = S3C_VA_UART + res->start - (S3C_PA_UART & 0xfff00000);
     ret = platform_get_irq(platdev, 0);
     if (ret < 0)
          port->irq = 0;
     else {
          port->irq = ret;
          ourport->rx_irq = ret;
          ourport->tx_irq = ret + 1;
     }
    
     ret = platform_get_irq(platdev, 1);
     if (ret > 0)
          ourport->tx_irq = ret;

     ourport->clk     = clk_get(&platdev->dev, "uart");

     dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",
         port->mapbase, port->membase, port->irq,
         ourport->rx_irq, ourport->tx_irq, port->uartclk);

     /* reset the fifos (and setup the uart) */
     s3c24xx_serial_resetport(port, cfg);
     return 0;
}
前面的都是初始化,将串口设备实现为了platform device。下面开始出现了 ldd3上面介绍的uart_driver \ uart_port \ uart_ops等相关的结构体。
static struct uart_driver s3c24xx_uart_drv = {
     .owner          = THIS_MODULE,
     .dev_name     = "s3c2410_serial",
     .nr          = CONFIG_SERIAL_SAMSUNG_UARTS,
     .cons          = S3C24XX_SERIAL_CONSOLE,
     .driver_name     = S3C24XX_SERIAL_NAME,
     .major          = S3C24XX_SERIAL_MAJOR,
     .minor          = S3C24XX_SERIAL_MINOR,
};

static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
     [0] = {
          .port = {
               .lock          = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
               .iotype          = UPIO_MEM,
               .irq          = IRQ_S3CUART_RX0,
               .uartclk     = 0,
               .fifosize     = 16,
               .ops          = &s3c24xx_serial_ops,
               .flags          = UPF_BOOT_AUTOCONF,
               .line          = 0,
          }
     },
     [1] = {
          .port = {
               .lock          = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
               .iotype          = UPIO_MEM,
               .irq          = IRQ_S3CUART_RX1,
               .uartclk     = 0,
               .fifosize     = 16,
               .ops          = &s3c24xx_serial_ops,
               .flags          = UPF_BOOT_AUTOCONF,
               .line          = 1,
          }
     },
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2

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

// 这里实现了串口对应的操作函数。
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,
     .enable_ms     = s3c24xx_serial_enable_ms,
     .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,
};

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

static int __init s3c24xx_serial_modinit(void)
{
     int ret;

     ret = uart_register_driver(&s3c24xx_uart_drv);
     if (ret < 0) {
          printk(KERN_ERR "failed to register UART driver\n");
          return -1;
     }

     return 0;
}

上面这些应该是s3c2440 串口部分的主体。这里实现了 串口收发数据的主要函数,注册了uart_port。 其实我一直不明白 为什么在程序中,串口有时候是用serial表示,有时候是用uart来表示。

另外,对应于tty设备的串口部分驱动主要是在文件: drivers/serial/serial-core.c 中。 在这个文件中, 实现了串口和tty的挂钩:tty_set_operations(normal, &uart_ops);  

int uart_register_driver(struct uart_driver *drv)
{
     struct tty_driver *normal = NULL;
     int i, retval;
     BUG_ON(drv->state);
     drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
     retval = -ENOMEM;
     if (!drv->state)
          goto out;

     normal  = alloc_tty_driver(drv->nr);           // tty 的个数
     if (!normal)
          goto out;

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

     /*
     * 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->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);
out:
     if (retval < 0) {
          put_tty_driver(normal);
          kfree(drv->state);
     }
     return retval;
}

tty 的驱动位于 drivers/char/ttyio.c  ttyioctl.c tty_port.c。 在这些文件中将tty实现成一个字符设备,对应于tty字符设备实现了相应的read open write 函数。通过 tty_set_operations这个函数,就把tty设备的read write 函数 与串口设备的 read write函数 相挂钩,这样当用户 read write  tty文件时, tty文件的 read write 函数就会调用串口的 read write函数,这样就实现了整个的过程。

tty线路规程在driver/char/tty_ldisc.c tty_port.c中的内容以后再分析。









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值