Saturday, 18 April 2015

UART framwork in Linux


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

uart_register_driver:

Register a uart driver with the core driver. We in turn register with the tty layer, and initialise the core driver per-port state.
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);
}

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.  


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 !!!! 

No comments:

Post a Comment