uart linux device driver architecture
model of analysis
Introduction
In linux in, serial port terminal is
often referred to in the shell, we see / dev / ttyS * is the
corresponding serial terminal device nodes. Before analyzing the
specific serial driver. It is necessary to analyze uart driven
architecture . it is a serial device driver
package layers.
Two: uart driver architecture overview
As shown below:
When uart_port receiving data from
serial devices, the device will be placed in the corresponding cache
line discipline area.
Such user at the time of writing the
serial driver, only first to register a uart_driver. Its main role is
to define the device node number. Then the operation of packaging
equipment will be in uart_port. Drive engineers do not need to be
concerned about the upper flow, just by hardware specification
uart_port the interface functions can be completed.
Three: uart driver important data
structures and their associated
We can consider under their own, based
on the above framework code should first consider how to write the
following:
1: a uart_driver registration period is
usually the device number that corresponds to the user space will see
uart_driver multiple device nodes, for example.:
/ dev / ttyS0 / dev / ttyS1 each device
node corresponds to a specific hardware architecture point of view
from above, each device file should correspond to a uart_port ie:.
uart_device how relations with more than one uart_port how to go up?
operation is to distinguish which device file?
one for each uart_port -circ_buf, so
uart_port must have this relationship up cache
Memories tty driver architecture
.tty_driver member points to a call of an array that tty-> ttys.
Each device file corresponds to a set array while the array data
structure of the code for tty_struct. Corresponding tty_struct will
tty_driver and ldisc associate.
That in uart driver, whether it can be
used in the same way to deal with it?
The uart drive common data structures
expressed as follows:
Combined with the above questions
raised can clearly understand the design of these structures.
uart_driver registration operation
Uart_driver registered corresponding
function: uart_register_driver () code is as follows:
int
uart_register_driver(struct
uart_driver
*drv)
{
struct tty_driver *normal = NULL;
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);
retval = -ENOMEM;
if (!drv→state)
goto out;
{
struct tty_driver *normal = NULL;
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);
retval = -ENOMEM;
if (!drv→state)
goto out;
normal
=
alloc_tty_driver(drv->nr);
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 nr; i++) {
struct uart_state *state = drv->state + i;
state->close_delay = 500; /* .5 seconds */
state->closing_wait = 30000; /* 30 seconds */
mutex_init(&state->mutex);
}
retval = tty_register_driver(normal);
out:
if (retval
put_tty_driver(normal);
kfree(drv->state);
}
return retval;
}
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 nr; i++) {
struct uart_state *state = drv->state + i;
state->close_delay = 500; /* .5 seconds */
state->closing_wait = 30000; /* 30 seconds */
mutex_init(&state->mutex);
}
retval = tty_register_driver(normal);
out:
if (retval
put_tty_driver(normal);
kfree(drv->state);
}
return retval;
}
uart_register_driver:
We have a proc file in /proc/tty/driver which is named after the normal driver.
drv->port should be NULL, and the per-port structures should be registered using uart_add_one_port after this call has succeeded.
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->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->ops = &uart_port_ops; } retval = tty_register_driver(normal); if (retval >= 0) return retval; for (i = 0; i < drv->nr; i++) tty_port_destroy(&drv->state[i].port); put_tty_driver(normal); out_kfree: kfree(drv->state); out: return -ENOMEM; } /** * uart_unregister_driver - remove a driver from the uart core layer * @drv: low level driver structure * * Remove all references to a driver from the core driver. The low * level driver must have removed all its ports via the * uart_remove_one_port() if it registered them with uart_add_one_port(). * (ie, drv->port == NULL) */ void uart_unregister_driver(struct uart_driver *drv) { struct tty_driver *p = drv->tty_driver; unsigned int i; tty_unregister_driver(p); put_tty_driver(p); for (i = 0; i < drv->nr; i++) tty_port_destroy(&drv->state[i].port); kfree(drv->state); drv->state = NULL; drv->tty_driver = NULL; 442 }
-
uart_add_one_port()
operations
/** * 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; int num_groups; 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; } /* Link the port to the driver state table and vice versa */ state->uart_port = uport; uport->state = state; state->pm_state = UART_PM_STATE_UNDEFINED; uport->cons = drv->cons; /* * 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); } if (uport->cons && uport->dev) of_console_check(uport->dev->of_node, uport->cons->name, uport->line); uart_configure_port(drv, state, uport); num_groups = 2; if (uport->attr_group) num_groups++; uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups), GFP_KERNEL); if (!uport->tty_groups) { ret = -ENOMEM; goto out; } uport->tty_groups[0] = &tty_dev_attr_group; if (uport->attr_group) uport->tty_groups[1] = uport->attr_group; /* * Register the port whether it's detected or not. This allows * setserial to be used to alter this port's parameters. */ tty_dev = tty_port_register_device_attr(port, drv->tty_driver, uport->line, uport->dev, port, uport->tty_groups); if (likely(!IS_ERR(tty_dev))) { device_set_wakeup_capable(tty_dev, 1); } else { dev_err(uport->dev, "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; } /** * uart_remove_one_port - detach a driver defined port structure * @drv: pointer to the uart low level driver structure for this port * @uport: uart port structure for this port * * This unhooks (and hangs up) the specified port structure from the * core driver. No further calls will be made to the low-level code * for this port. */ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) { struct uart_state *state = drv->state + uport->line; struct tty_port *port = &state->port; struct tty_struct *tty; int ret = 0; BUG_ON(in_interrupt()); if (state->uart_port != uport) dev_alert(uport->dev, "Removing wrong port: %p != %p\n", state->uart_port, uport); mutex_lock(&port_mutex); /* * Mark the port "dead" - this prevents any opens from * succeeding while we shut down the port. */ mutex_lock(&port->mutex); if (!state->uart_port) { mutex_unlock(&port->mutex); ret = -EINVAL; goto out; } uport->flags |= UPF_DEAD; mutex_unlock(&port->mutex); /* * Remove the devices from the tty layer */ tty_unregister_device(drv->tty_driver, uport->line); tty = tty_port_tty_get(port); if (tty) { tty_vhangup(port->tty); tty_kref_put(tty); } /* * If the port is used as a console, unregister it */ if (uart_console(uport)) unregister_console(uport->cons); /* * Free the port IO and memory resources, if any. */ if (uport->type != PORT_UNKNOWN) uport->ops->release_port(uport); kfree(uport->tty_groups); /* * Indicate that there isn't a port here anymore. */ uport->type = PORT_UNKNOWN; state->uart_port = NULL; out: mutex_unlock(&port_mutex); return ret; }
First,
this function can not be used. Uart_port-> line is to uart device
file number in interrupt context. It is the corresponding
uart_driver-> state array uart_port-> line items.
It
is mainly the corresponding initialization uart_driver-> state
items. Then call uart_configure_port () to automatically configure
the port and then registered tty_device. If the user space to run the
udev or has been configured hotplug. Will automatically generate
device files in / dev up.
Operational
flowchart is as follows:
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 };
open
operating device node
When
users open space to perform the operation, it will perform uart_ops->
open Uart_ops definition as follows:
/* * Calls to uart_open are serialised by the tty_lock in * drivers/tty/tty_io.c:tty_open() * Note that if this fails, then uart_close() _will_ be called. * * In time, we want to scrap the "opening nonpresent ports" * behaviour and implement an alternative way for setserial * to set base addresses/ports/types. This will allow us to * get rid of a certain amount of extra tests. */ static int uart_open(struct tty_struct *tty, struct file *filp) { struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_tate; int retval, line = tty->index; struct uart_state *state = drv->state + line; struct tty_port *port = &state->port; pr_debug("uart_open(%d) called\n", line); spin_lock_irq(&port->lock); ++port->count; spin_unlock_irq(&port->lock); /* * We take the semaphore here to guarantee that we won't be re-entered * while allocating the state structure, or while we request any IRQ * that the driver may need. This also has the nice side-effect tha * it delays the action of uart_hangup, so we can guarantee that * state->port.tty will always contain something reasonable. */ if (mutex_lock_interruptible(&port->mutex)) { retval = -ERESTARTSYS; goto end; } if (!state->uart_port || state->uart_port->flags & UPF_DEAD) { retval = -ENXIO; goto err_unlock; } tty->driver_data = state; state->uart_port->state = state; state->port.low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty_port_tty_set(port, tty); /* * Start up the serial port. */ retval = uart_startup(tty, state, 0); /* * If we succeeded, wait until the port is ready. */ mutex_unlock(&port->mutex); if (retval == 0) retval = tty_port_block_til_ready(port, tty, filp); end: return retval; err_unlock: mutex_unlock(&port->mutex); goto end; }
Here,
Note To complete the ring buffer that info-> xmit initialization.
Then call the port-> ops-> startup () will bring the port to a
working state. Other is an adjustable parameter settings, do not
explained in detail.
device
node write operation
Write
operation corresponds to the operation of the interface for
uart_write () code is as follows:
static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct uart_state *state = tty->driver_data; struct uart_port *port; struct circ_buf *circ; unsigned long flags; int c, ret = 0; /* * This means you called this function _after_ the port was * closed. No cookie for you. */ if (!state) { WARN_ON(1); return -EL3HLT; } port = state->uart_port; circ = &state->xmit; if (!circ->buf) return 0; spin_lock_irqsave(&port->lock, flags); while (1) { c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); if (count < c) c = count; if (c <= 0) break; memcpy(circ->buf + circ->head, buf, c); circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); buf += c; count -= c; ret += c; } __uart_start(tty); spin_unlock_irqrestore(&port->lock, flags); return ret; }
static
void
uart_start(struct
tty_struct
*tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->port;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
__uart_start(tty);
spin_unlock_irqrestore(&port->lock, flags);
}
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->port;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
__uart_start(tty);
spin_unlock_irqrestore(&port->lock, flags);
}
static void __uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->port;
if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf &&
!tty->stopped && !tty->hw_stopped)
port->ops->start_tx(port);
}
Note:
As
can be seen from the code here note if the operation is to initialize
the state-> info. Note port-> info is state-> a copy of the
info. That port directly through port-> info can be found in the
cache it is to operate.
/* * Startup the port. This will be called once per open. All calls * will be serialised by the per-port mutex. */ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state int init_hw) { struct uart_port *uport = state->uart_port; unsigned long page; int retval = 0; if (uport->type == PORT_UNKNOWN) return 1; /* * Make sure the device is in D0 state. */ uart_change_pm(state, UART_PM_STATE_ON); /* * Initialise and allocate the transmit and temporary * buffer. */ if (!state->xmit.buf) { /* This is protected by the per port mutex */ page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; state->xmit.buf = (unsigned char *) page; uart_circ_clear(&state->xmit); } retval = uport->ops->startup(uport); if (retval == 0) { if (uart_console(uport) && uport->cons->cflag) { tty->termios.c_cflag = uport->cons->cflag; uport->cons->cflag = 0; } /* * Initialise the hardware port settings. */ uart_change_speed(tty, state, NULL); if (init_hw) { /* * Setup the RTS and DTR signals once the * port is open and ready to respond. */ if (tty->termios.c_cflag & CBAUD) uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } spin_lock_irq(&uport->lock); if (uart_cts_enabled(uport) && !(uport->ops->get_mctrl(uport) & TIOCM_CTS)) uport->hw_stopped = 1; else uport->hw_stopped = 0; spin_unlock_irq(&uport->lock); } /* * This is to allow setserial on this port. People may want to set * port/irq/type and then reconfigure the port properly if it failed * now. */ if (retval && capable(CAP_SYS_ADMIN)) return 1; return retval; } static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw) { struct tty_port *port = &state->port; int retval; if (port->flags & ASYNC_INITIALIZED) return 0; /* * Set the TTY IO error marker - we will only clear this * once we have successfully opened the port. */ set_bit(TTY_IO_ERROR, &tty->flags); retval = uart_port_startup(tty, state, init_hw); if (!retval) { set_bit(ASYNCB_INITIALIZED, &port->flags); clear_bit(TTY_IO_ERROR, &tty->flags); } else if (retval > 0) retval = 0; return retval; }
In
this function, the continued operation of the device to complete the
initialization file corresponding state. Now open the device the user
space. That is to be operated on this file. It uart_port also began
to work. That call uart_startup () make it into work state. of
course, you need to initialize the corresponding ring buffer
uart_port circ_buf. that state-> info-> xmit.
Pay
particular attention to where the tty-> driver_data = state; this
is because after only port-related operations, do not need to
understand uart_driver information.
Tracking
a look at two important subroutine which calls uart_get () and
uart_startup () first analyze uart_get () code is as follows...:
static
struct
uart_state
*uart_get
(struct
uart_driver
*drv,
int
line)
{
struct uart_state *state;
int ret = 0;
state = drv->state + line;
if (mutex_lock_interruptible(&state->mutex)) {
ret = -ERESTARTSYS;
goto err;
}
state->count++;
if (!state->port || state->port->flags & UPF_DEAD) {
ret = -ENXIO;
goto err_unlock;
}
if (!state->info) {
state->info = kzalloc(sizeof(struct uart_info), GFP_KERNEL);
if (state->info) {
init_waitqueue_head(&state->info->open_wait);
init_waitqueue_head(&state->info->delta_msr_wait);
/*
* Link the info into the other structures.
*/
state->port->info = state->info;
tasklet_init(&state->info->tlet, uart_tasklet_action,
(unsigned long)state);
} else {
ret = -ENOMEM;
goto err_unlock;
}
}
return state;
err_unlock:
state->count--;
mutex_unlock(&state->mutex);
err:
return ERR_PTR(ret);
}
UART In BeagleBone Black:
RX TX CTS RTS Device Remark
UART0 J1_4 J1_5 /dev/ttyO0 BeagleBone Black only
UART1 P9_26 P9_24 P9_20 P9_19 /dev/ttyO1
UART2 P9_22 P9_21 P8_37 P8_38 /dev/ttyO2
UART3 P9_42 P8_36 P8_34 /dev/ttyO3 TX only
UART4 P9_11 P9_13 P8_35 P8_33 /dev/ttyO4
UART5 P8_38 P8_37 P8_31 P8_32 /dev/ttyO5
Device tree would be having different name for these so look into the base address and identify UARTs and then change them.
{
struct uart_state *state;
int ret = 0;
state = drv->state + line;
if (mutex_lock_interruptible(&state->mutex)) {
ret = -ERESTARTSYS;
goto err;
}
state->count++;
if (!state->port || state->port->flags & UPF_DEAD) {
ret = -ENXIO;
goto err_unlock;
}
if (!state->info) {
state->info = kzalloc(sizeof(struct uart_info), GFP_KERNEL);
if (state->info) {
init_waitqueue_head(&state->info->open_wait);
init_waitqueue_head(&state->info->delta_msr_wait);
/*
* Link the info into the other structures.
*/
state->port->info = state->info;
tasklet_init(&state->info->tlet, uart_tasklet_action,
(unsigned long)state);
} else {
ret = -ENOMEM;
goto err_unlock;
}
}
return state;
err_unlock:
state->count--;
mutex_unlock(&state->mutex);
err:
return ERR_PTR(ret);
}
UART In BeagleBone Black:
RX TX CTS RTS Device Remark
UART0 J1_4 J1_5 /dev/ttyO0 BeagleBone Black only
UART1 P9_26 P9_24 P9_20 P9_19 /dev/ttyO1
UART2 P9_22 P9_21 P8_37 P8_38 /dev/ttyO2
UART3 P9_42 P8_36 P8_34 /dev/ttyO3 TX only
UART4 P9_11 P9_13 P8_35 P8_33 /dev/ttyO4
UART5 P8_38 P8_37 P8_31 P8_32 /dev/ttyO5
Device tree would be having different name for these so look into the base address and identify UARTs and then change them.
Summary
After
this section is to analyze the serial-driven basis in the
understanding of the tty driver architecture, come to understand uart
driver architecture should not be difficult. With our in-depth
analysis in linux device driver, more deeply appreciate, linux device
driver architecture many of which are interlinked. As long as a deep
understanding of a driver architecture. replicability. it is easy to
analyze the drivers of other architectures.
Reference
1)Essential Linux Device driver chapter 6
2)serial drivers PPT by free-electrons
For any query post a comment
Thank You Enjoy !!!!
Reference
1)Essential Linux Device driver chapter 6
2)serial drivers PPT by free-electrons
For any query post a comment
Thank You Enjoy !!!!
No comments:
Post a Comment