您的位置:首页 > 其它

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

2017-05-11 14:32 267 查看
之前已经把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是用来外接控制台的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: