---
# System prepended metadata

title: Virtio-console
tags: [virtualization]

---

# Virtio-console
###### tags: `virtualization`
---
## TTY
* Flow chart
    * tty core (tty_io.*)
    * tty line discipline (n_tty.*)
    * tty driver (e.g. serial_core, hvc...)
        * console
        * serial port
        * pty (pseudotty) 
        ![](https://i.imgur.com/9vs6MNe.png)
        * reference :LDD3,ch18.
## virtio console
* Flow chart
    * console
    * serial port
    ```
                  User space

   +-----+--------------------------+------------------- vfs
          ^                          ^        kernel space
          |                          |
          |           +--------------+-----------------+
          |           |                                |
          |           |          tty                   |
          |           |                                |
          |           +--------------------------------+
          |           |                                |
          |           |  hvc(hotplug virtual console)  |
          |           |                                |
          |           +-----------------^--------------+
          |                             |
    +-----+---------+                   |
    |  port fops    +---------+---------v--------------+
    +- - - - - - - -+         |      hvc ops           |
    |  rproc serial |         +------------------------+
    +---------------+                                  |
    |                Virtue-console                    |
    +--------------------------------------------------+
    ```
## virtio console operation
---
```graphviz
digraph hierarchy {

                nodesep=2.0 // increases the separation between nodes
                
                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
                edge [color=Block, style=dashed] //All the lines look like this

                init->register_virtio->virtio_console,virtio_serial
}
```
```clike=
static struct virtio_driver virtio_console = {
         .feature_table = features,
         .feature_table_size = ARRAY_SIZE(features),
         .driver.name =  KBUILD_MODNAME,
         .driver.owner = THIS_MODULE,
         .id_table =     id_table,
         .probe =        virtcons_probe,
         .remove =       virtcons_remove,
         .config_changed = config_intr,
 #ifdef CONFIG_PM_SLEEP
         .freeze =       virtcons_freeze,
         .restore =      virtcons_restore,
 #endif
 };

 static struct virtio_driver virtio_rproc_serial = {
         .feature_table = rproc_serial_features,
         .feature_table_size = ARRAY_SIZE(rproc_serial_features),
         .driver.name =  "virtio_rproc_serial",
         .driver.owner = THIS_MODULE,
         .id_table =     rproc_serial_id_table,
         .probe =        virtcons_probe,
         .remove =       virtcons_remove,
 };
```
### Prob virtio-console device
```graphviz
digraph hierarchy {

                nodesep=2.0 // increases the separation between nodes
                
                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
                edge [color=Block]//, style=dashed] //All the lines look like this
        //graph [splines=ortho]
        "is multiport ?"[shape=diamond];
        "is rproc serial"[shape=diamond];
                subgraph prob{
                lable = probdev
                style = filled;
                fillcolor = block;
                
                "virtio_bus matched"[shape=circle];
                "is_rproc_serial(vdev)" [shape=diamond];
                "match?"[shape=diamond];

                "virtio_bus matched"->"vdrv->prob(vdev)"->"register a chrdev for portdev"->"is_rproc_serial(vdev)";
                "is_rproc_serial(vdev)"->"read feature bits for multiport"[label = "no"]
                "is_rproc_serial(vdev)"->"init virtqueues"[label = "yes"]
                "read feature bits for multiport"->"match?";
                "match?"->"init virtqueues"[label = "no"];
                "match?"->"set multiport flag"[label = "yes"];
                "set multiport flag"->"init virtqueues"
                "init virtqueues"->"init portdev"
                }
                "init virtqueues"->"is multiport?"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                subgraph init_vqs{
                
                    label = init_vqs1
                    "is multiport?"[shape=diamond];
                    "is multiport?"->"alloc (ports+1)*2 virtqueue buffer"[label = "yes"];
                    "is multiport?"->"alloc 2 virtqueue buffer"[label = "no"];
                    "alloc (ports+1)*2 virtqueue buffer"->"put control_intr() into io_callback"->"put in/out_intr() into io_callback";
                    "alloc 2 virtqueue buffer"->"put in/out_intr() into io_callback"->"rproc_virtio_find_vqs()";
                    "rproc_virtio_find_vqs()"->"rproc_alloc_vring"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                    "rproc_alloc_vring"->"vring_new_virtqueue()"->"using rvring to create vring_vritqueue \nand add rproc_virtio_notify \nand io_callback into vring_vritqueue->vq\nreturn vring_vritqueue->vq"->"init vring descriptor"
                    "rproc_virtio_find_vqs()"->"assign vq to portdev->*vqs[*]"
                }
                "init portdev"->"virtio_device_ready";
                "virtio_device_ready"->"rproc_virtio_set_status\nVIRTIO_CONFIG_S_DRIVER_OK"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                "virtio_device_ready"->"assign config/control _work_handler() to\n portdev->config/control _work"->"is multiport ?"
                "is multiport ?"->"add_port()"[label="no"];
                "is multiport ?"->"fill_queue()"[label="yes"];
                "fill_queue()"->"alloc_buf()"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                "alloc_buf()"->"is rproc serial"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                "is rproc serial"->"get_device(platform-dev)"[label = "yes"];
                "get_device(platform-dev)"->"dma_alloc_coherent()";
                "is rproc serial"->"kmalloc()"[label = "no"];
                "kmalloc()"->"return port_buf";
                "dma_alloc_coherent()"->"return port_buf"
                "alloc_buf()"->"add_inbuf()"
                "add_inbuf()"->"init sglist"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                "init sglist"->"virtqueue_add_inbuf"->virtqueue_add
                "is indirect and multibuffer?"[shape=diamond];
                virtqueue_add->"is indirect and multibuffer?"
                "is indirect and multibuffer?"->"alloc_indirect()"[label = "yes"]
                
                "is indirect and multibuffer?"->"setup descriptor"[label = "no"]
                "alloc_indirect()"->"setup descriptor"
                "fill_queue()"->"add portdev to portdriver_data_portdev tail"
                "add_port()"->"add portdev to portdriver_data_portdev tail"
                "add_port()"->"create port"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                "create port"->"init port"->"fill_queue & \n if not rproc serial and not multiport,\n init port console.\nhvc_alloc() and\n send_control_msg(VIRTIO_CONSOLE_PORT_OPEN)"->"send control msg \n(VIRTIO_CONSOLE_PORT_READY)"->"add port to portdriver_data_portdev tail"
                "add portdev to portdriver_data_portdev tail"->"if multiport and early console plz wait hvc matched"
                
            {rank=same;"create port","add_port()"}
            {rank=same;"fill_queue()","alloc_buf()"}
            {rank=same;"init sglist","add_inbuf()"}
            {rank=same;"is rproc serial","alloc_buf()"}
            {rank=same;"init virtqueues","is multiport?"}
            {rank=same;"rproc_virtio_find_vqs()","rproc_alloc_vring"}
            {rank=same;"virtio_device_ready","rproc_virtio_set_status\nVIRTIO_CONFIG_S_DRIVER_OK"}// Put them on the same level
}
```
* config_ops is set by remoteproc_virtio
### How to read data from the port?
```clike=
/*
 * after add_port() 
 * port->cdev->ops=&port_fops;
 */
static const struct file_operations port_fops = {
         .owner = THIS_MODULE,
         .open  = port_fops_open,
         .read  = port_fops_read,
         .write = port_fops_write,
         .splice_write = port_fops_splice_write,
         .poll  = port_fops_poll,
         .release = port_fops_release,
         .fasync = port_fops_fasync,
         .llseek = no_llseek,
 };
```
* Firstly, we focus what the thing before file read
    * look into virtqueue callback
    * when it did the `init_vqs()`, callback has been assigned `in_intr()` and `out_intr()` 
	```graphviz
	digraph hierarchy {
	
	                nodesep=2.0 // increases the separation between nodes
	                
	                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
	                edge [color=Block, style=dashed] //All the lines look like this
	                "has in_buffer?"[shape=diamond];
	                find_port->"get the port's in_buffer";
	                "get the port's in_buffer"->"has in_buffer?"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
	                "has in_buffer?"->"return inbuf"[label=yes]
	                "has in_buffer?"->"virtqueue_get_buf"[label=no]
	                "virtqueue_get_buf"->"get last used idx"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
	                "get last used idx"->detach_buf
	                "get the port's in_buffer"->"send SIGIO to port"->"wake up interruptible for port->waitqueue"->"if its hvc, kick hvc"
	                {rank=same;"get the port's in_buffer","has in_buffer?"}
	                {rank=same;"get last used idx","virtqueue_get_buf"}
	}
	```
    * Read it!
    ```graphviz
	digraph hierarchy {
	
	                nodesep=2.0 // increases the separation between nodes
	                
	                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
	                edge [color=Block, style=dashed] //All the lines look like this
	                port_has_data[shape=diamond];
                    "Block or nonblock"[shape=diamond];
                    "consume buffer?"[shape=diamond];
                    "user?"[shape=diamond];
	                port_has_data->"Block or nonblock"[label = no];
                    port_has_data->"fill_readbuf()"[label = yes];
                    "Block or nonblock"->"return -EAGAIN"[label = nonblocking];
                    "Block or nonblock"->"wait event freezable\ncondition is !will_read_block()\n(port has data or host connected)"[label = blocking];
                    "wait event freezable\ncondition is !will_read_block()\n(port has data or host connected)"->"fill_readbuf()"
           "fill_readbuf()"->"user?"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
           "user?"->"copy to user"[label=yes]
           "user?"->"memory copy"[label=no]
           {"copy to user","memory copy"}->"consume buffer?"
           "consume buffer?"->"finish"[label=no]
           "consume buffer?"->"add_inbuf"[label=yes]
           "add_inbuf"->"finish"
                    
	                {rank=same;"fill_readbuf()","user?"}
	                
	}
    ```
* Secondly, if the reader is tty
    * The tty will call hvc->get_char() to read buffer
    ```c
    /* The operations for console ports. */
    static const struct hv_ops hv_ops = {
	.get_chars = get_chars,
	.put_chars = put_chars,
	.notifier_add = notifier_add_vio,
	.notifier_del = notifier_del_vio,
	.notifier_hangup = notifier_del_vio,
    };
    ```
    ```graphviz
	digraph hierarchy {
	
	                nodesep=2.0 // increases the separation between nodes
	                
	                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
	                edge [color=Block, style=dashed] //All the lines look like this
	                
	                "find_port_by_vtermno"->"get the console's port"->"fill_readbuf()";
                    "find_port_by_vtermno"->"list for each entry \n(*console, pdrvdata.consoles, list)"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                    
                    
                    
	                {rank=same;"list for each entry \n(*console, pdrvdata.consoles, list)","find_port_by_vtermno"}
	                
	}
    ```
    ```clike=
    /*
     * This is a global struct for storing common data for all the devices
     * this driver handles.
     *
     * Mainly, it has a linked list for all the consoles in one place so
     * that callbacks from hvc for get_chars(), put_chars() work properly
     * across multiple devices and multiple ports per device.
     */
    struct ports_driver_data {
	/* Used for registering chardevs */
	struct class *class;

	/* Used for exporting per-port information to debugfs */
	struct dentry *debugfs_dir;

	/* List of all the devices we're handling */
	struct list_head portdevs;

	/*
	 * This is used to keep track of the number of hvc consoles
	 * spawned by this driver.  This number is given as the first
	 * argument to hvc_alloc().  To correctly map an initial
	 * console spawned via hvc_instantiate to the console being
	 * hooked up via hvc_alloc, we need to pass the same vtermno.
	 *
	 * We also just assume the first console being initialised was
	 * the first one that got used as the initial console.
	 */
	unsigned int next_vtermno;

	/* All the console devices handled by this driver */
	struct list_head consoles;
    };
    ```
    ```clike=
    /* This struct holds the per-port data */
    struct port {
	/* Next port in the list, head is in the ports_device */
	struct list_head list;

	/* Pointer to the parent virtio_console device */
	struct ports_device *portdev;

	/* The current buffer from which data has to be fed to readers */
	struct port_buffer *inbuf;

	/*
	 * To protect the operations on the in_vq associated with this
	 * port.  Has to be a spinlock because it can be called from
	 * interrupt context (get_char()).
	 */
	spinlock_t inbuf_lock;

	/* Protect the operations on the out_vq. */
	spinlock_t outvq_lock;

	/* The IO vqs for this port */
	struct virtqueue *in_vq, *out_vq;

	/* File in the debugfs directory that exposes this port's information */
	struct dentry *debugfs_file;

	/*
	 * Keep count of the bytes sent, received and discarded for
	 * this port for accounting and debugging purposes.  These
	 * counts are not reset across port open / close events.
	 */
	struct port_stats stats;

	/*
	 * The entries in this struct will be valid if this port is
	 * hooked up to an hvc console
	 */
	struct console cons;
    
	/* Each port associates with a separate char device */
	struct cdev *cdev;
	struct device *dev;

	/* Reference-counting to handle port hot-unplugs and file operations */
	struct kref kref;

	/* A waitqueue for poll() or blocking read operations */
	wait_queue_head_t waitqueue;

	/* The 'name' of the port that we expose via sysfs properties */
	char *name;

	/* We can notify apps of host connect / disconnect events via SIGIO */
	struct fasync_struct *async_queue;

	/* The 'id' to identify the port with the Host */
	u32 id;

	bool outvq_full;

	/* Is the host device open */
	bool host_connected;

	/* We should allow only one process to open a port */
	bool guest_connected;
    };
    ```
* OK, let's get down to the port fops.
    * What's about write data?
    * The writing virtqueue also has callback but we go through `port_fops_write` first.
    ```graphviz
	digraph hierarchy {
	
	                nodesep=2.0 // increases the separation between nodes
	                
	                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
	                edge [color=Block, style=dashed] //All the lines look like this

                    "Block or nonblock"[shape=diamond];
                    "Block or nonblock?"[shape=diamond];
                    "will_write_block()"[shape=diamond];
	                "wait_port_writable()"->"will_write_block()"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                    "will_write_block()"->"free used buf and get full flag"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                    "will_write_block()"->"Block or nonblock"[label = yes];
                    "will_write_block()"->"return 0"[label = no];
                    "Block or nonblock"->"return -EAGAIN,\n try again"[label = nonblock]
                    "Block or nonblock"->"wait event freezable\ncondition is !will_write_block()"[label = block]
                    "wait_port_writable()"->"if write size is more than 2^15, cutoff to 2^15."->"alloc_buf()"->"copy from user"->"init sg list for the buf page"->"send the sg_list to the port"
                    "send the sg_list to the port"->"virtqueue_add_outbuf()"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                    "virtqueue_add_outbuf()"->"virtqueue_add()"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                    "virtqueue_add_outbuf()"->"virtqueue kick"
                    "virtqueue kick"->"Block or nonblock?"
                    
                    "Block or nonblock?"->"done"[label = nonblock];
                    "Block or nonblock?"->"wait the date is pushed out"[label = block];
                    
	                {rank=same;"wait_port_writable()","will_write_block()","free used buf and get full flag"}
                    {rank=same;"send the sg_list to the port","virtqueue_add_outbuf()"}
                    {rank=same;"virtqueue_add()","virtqueue_add_outbuf()"}
	                
	}
    ```
    * The task is waiting for the vq will be free by out_vq callback, `out_intr`.
        * When the remote consumer consumes the vq, it will kick the vq. And the callback will be hooked up.
    ```graphviz
	digraph hierarchy {
	
	                nodesep=2.0 // increases the separation between nodes
	                
	                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
	                edge [color=Block, style=dashed] //All the lines look like this

                    "out_intr"->"wake up interruptible task\n in port->waitqueue"
	                
	}
    ```
* fops has general poll and faync.
    * poll
    ```clike=
    static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
    {
	struct port *port;
	unsigned int ret;

	port = filp->private_data;
	poll_wait(filp, &port->waitqueue, wait);

	if (!port->guest_connected) {
		/* Port got unplugged */
		return POLLHUP;
	}
	ret = 0;
	if (!will_read_block(port))
		ret |= POLLIN | POLLRDNORM;
	if (!will_write_block(port))
		ret |= POLLOUT;
	if (!port->host_connected)
		ret |= POLLHUP;

	return ret;
    }
    ```
    * fasync(asynchronous notification)
    ```clike=
    static int port_fops_fasync(int fd, struct file *filp, int mode)
    {
	struct port *port;

	port = filp->private_data;
	return fasync_helper(fd, filp, mode, &port->async_queue);
    }
    ```
    * How to use?
        * write the signal handler to process `SIGIO`
        * use `sigaction` to register signal handler(you also can use multi-thread to handle it)
        * write the pid to f_owner with fcntl (if pid is negative, signal will send to group of the pid)
        * if you have multi-fd or read/write, please using `epoll` to know where send the signal
* fops_open
    ```graphviz
	digraph hierarchy {
	
	                nodesep=2.0 // increases the separation between nodes
	                
	                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
	                edge [color=Block, style=dashed] //All the lines look like this
                "connected?"[shape=diamond];
                    "find port by devt"->"put the port to filp' private_data"->"connected?"
                    "connected?"->"kfer_put() \nreturn BUSY"[label = yes]
                    "connected?"->"set connected"[label = no]
                    "set connected"->"reclaim consumed buffer"->"use nonseekable_open() for the\n inode and filp"->"send VIRTIO_CONSOLE_PORT_OPEN msg"
                    "find port by devt"->"list for each entry for each port device"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                    "list for each entry for each port device"->"if matched, use kref_get() to add \nreference number of times"
	                {rank=same;"find port by devt","list for each entry for each port device"}
	}
    ```
* About DMA
    * `struct list_head pending_free_dma_bufs`
        * collect dma unsed buffer
        * global variable
    * reclaim_dma_bufs(void)
    ```graphviz
	digraph hierarchy {
	
	                nodesep=2.0 // increases the separation between nodes
	                
	                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
	                edge [color=Block, style=dashed] //All the lines look like this
            "empty(pending_free_dma_bufs)"[shape=diamond];
            "is_rproc_serial"[shape=diamond];
                   "can sleep?"[shape=diamond];"empty(pending_free_dma_bufs)"->"done"[label = yes]
                    "empty(pending_free_dma_bufs)"->"list_cut_position\n(temp,pending_free_dma_bufs,pending_free_dma_bufs.prev)"[label = no]
                    
                    "list_cut_position\n(temp,pending_free_dma_bufs,pending_free_dma_bufs.prev)"->"pending_free_dma_bufs"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                    "temp"->"pending_free_dma_bufs.next"[label = next]
                    "pending_free_dma_bufs.prev"->"temp"[label = prev]
                    "temp"->"pending_free_dma_bufs.prev"[label = next]
                    "pending_free_dma_bufs.next"->"temp"[label = prev]
                    "pending_free_dma_bufs"->"pending_free_dma_bufs.prev"[label = X];
                    "pending_free_dma_bufs.next"->"pending_free_dma_bufs"[label = X];
                    "list_cut_position\n(temp,pending_free_dma_bufs,pending_free_dma_bufs.prev)"->"list for each entry safe"->"delete list(pop the list)"->"free_buf(buf,can_sleep)\nbuf is the list owner"
                    "free_buf(buf,can_sleep)\nbuf is the list owner"->"free_buf()"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                    "free_buf()"->"put sg_page (free)"->"is_rproc_serial"
                    "is_rproc_serial"->"kfree()"[label = no]
                    "is_rproc_serial"->"can sleep?"[label = yes]
                    "can sleep?"->"dma_free_coherent"[label = yes]
                    "can sleep?"->"list_ad_tail(list,pending_free_dma_bufs)\nreturn; queue up and freed later"[label = no]
	                {rank=same;"list_cut_position\n(temp,pending_free_dma_bufs,pending_free_dma_bufs.prev)","pending_free_dma_bufs"}
                    {rank=same;"free_buf(buf,can_sleep)\nbuf is the list owner","free_buf()"}
	}
	}
    ```
    ```graphviz
	digraph hierarchy {
	
	                nodesep=2.0 // increases the separation between nodes
	                
	                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
	                edge [color=Block, style=dashed] //All the lines look like this
    "list_cut_position(list,head,entry)"->"head"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
                    
                    "head"->"head.next"[label=" X next"]
                    "head.next"->"head"[label=" X prev"]
                    "head.next"->"..."
                    "..."->"head.next"
                    "..."->"entry"[label=next]
                    "entry"->"..."[label=prev]
                    "entry"->"entry.next"[label=" X next"]
                    "entry"->"list"[label=next]
                    "entry.next"->"head"[label = prev]
                    "entry.next"->"entry"[label=" X prev"]
                    
                    "list"->"head.next"[label=next]
                    "head.next"->"list"[label=prev]
                    "list"->"entry"[label=prev]
                    
                    
                    "head"->"entry.next"[label = next]
                    {rank=same;"head","...","entry","head.next"}
                    {rank=same;"list_cut_position(list,head,entry)","list"}
                    
                    }
    ```
    
## modem uart
* plat uart api
```clike=
Plat_UART_Init();
INT32 Plat_UART_InitWithType(UART_Port_e port, const char *type);
/* 	follow LM3S6965EVB, return UARTERR_NOERR;
 * 	virtio-serial is not a real uart
 * 	start bit = none
 * 	stop bit = none
 * 	parity = none
 * 	baud rate = any
 * 	data bits = DATABITS 8
 * UARTErr_e Plat_UART_GetCfg(UART_Port_e port, UART_Cfg_t *pCfg);
 * UARTErr_e Plat_UART_SetCfg(UART_Port_e port, UART_Cfg_t *pCfg);
 * UARTErr_e Plat_UART_Reset(UART_Port_e port);
 * UARTErr_e Plat_UART_SET_BAUDRATE(UART_Port_e port, UINT32 Baudrate);
 */
INT32 Plat_UART_ReadData(UART_Port_e port, UINT8* buf, UINT32 buflen);
INT32 Plat_UART_WriteData(UART_Port_e port, const UINT8 *buf, UINT32 buflen);
void Plat_UART_LOOPBACK_MODE(UART_Port_e port);

/*
 * disable uart_port# interrupt
 * busy read/write data (while loop)
 * enable uart_port# interrupt
*/
INT32 Plat_UART_ReadData_Polling(UART_Port_e port, UINT8* buf, UINT32 buflen);
INT32 Plat_UART_WriteData_Polling(UART_Port_e port, UINT8* buf, UINT32 buflen);
void Plat_UART_EXIT_Polling(UART_Port_e port);
```
* How to
    * Create */driverlib/uart_virtiouart.c
        * for virtio-serial
    * Modify */plat/src/plat_uart.c
        * do not modify `/inc/`
        * if `port_number >= UART_NUM and port_number < VIRTIO_UART_NUM`
            * using `uart_virtiouart.c`

```
+-------------------------+             +-------------------------+
|                         |             |                         |
|      Plat_Uart          |             |      Plat_Uart          |
+-------------------------+             +-------------------------+
|                         |             |                         |
|    target plat_uart     |             |    target plat_uart     |
+--+--------------------+-+             +--+--------------------+-+
   |                    |     +------>     |                    |
   |  Uart driver lib   |                  |  virtio-uart lib   |
   |                    |                  |                    |
   +--+  +-+  +-+  +-+ ++                  +--+  +-+  +-+  +-+ ++
   |  Uart internal api |                  |                    |
   |       |            |                  |       VirtIO       |
   +-------|------------+                  +--------------------+
           v
         Hardware


```

## RX callback flowchart

```graphviz
	digraph hierarchy {
	
	                nodesep=2.0 // increases the separation between nodes
	                
	                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
	                edge [color=Block, style=dashed] //All the lines look like this
    
"While loop (1)"[shape=diamond];
"out_vring num >0"[shape=diamond];
"While loop for out_vring"[shape=diamond];
"rx_buff full ?"[shape=diamond];
"While loop (1)"->"reset vq buff"->"rx_buff full ?"
"rx_buff full ?"->"Vq_Pop"[label ="  has room"]
"rx_buff full ?"->"wakeup thread(osSignalSet)"[label ="   full"]
"wakeup thread(osSignalSet)"->"While loop (1)"[label = try_again]
"Vq_Pop"->"out_vring num >0"
"out_vring num >0"->"While loop for out_vring"[label = "  > 0"]
"out_vring num >0"->"break"[label= "equal 0"]
"While loop for out_vring"->"fill rx_buffer"[label = "   > 0"]
"fill rx_buffer"->"While loop for out_vring"
"While loop for out_vring"->"Vq_Push"[label= "equal 0"]
"Vq_Push"->"While loop (1)"
"break"->"total receive \nlength is more than 0"->"Virtio Notify"->"Wakeup thread(osSingnalSet)"->"end"
  

{rank=same;"fill rx_buffer","Vq_Push","break"}
                    }
```
---
## Failed address
* Over memory limited address
    * same situation in send and receive
```
+---------------+                                  +-------------------+
| Linux         |                                  | Modem             |
| virtio console+--------+                         | virtio uart       |
|               |        |                         |           ^       |
|               | Write len = 100                  |           |       |
|               | addr = 0xA10000000               |           |Get len = 100
|               |        |                         |           |addr = 0xA1000000
+---------------+        |                         +-------------------+
                         |                                     |
|---------------------+--|--------------------------+----------|-------+
|                     |  |         ...    virtqueue |          |       |
|                     |  +------>  addr +----------------------+       |
|                     |  +-------> len  +----------------------+       |
|                     |            ...              |                  |
|                     |                             |                  |
+---------------------+-----------------------------+------------------+


```
1. `/* Device we got DMA memory from */` (rproc->parent)
2. 
```graphviz
	digraph hierarchy {
    nodesep=2.0 // increases the separation between nodes
	                
	                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
	                edge [color=Block, style=dashed] //All the lines look like this
                    "is rproc serial"[shape=diamond];
    "alloc_buf()"->"is rproc serial"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
        "is rproc serial"->"get_device(platform-dev)"[label = "yes"];
        "get_device(platform-dev)"->"dma_alloc_coherent()";
        "is rproc serial"->"kmalloc()"[label = "no"];
        "kmalloc()"->"return port_buf";
        "dma_alloc_coherent()"->"return port_buf"
                }
```
```graphviz
	digraph hierarchy {
    nodesep=2.0 // increases the separation between nodes
	                
	                node [color=Gary,fontname=Courier,shape=box] //All nodes will this shape and colour
	                edge [color=Block, style=dashed] //All the lines look like this
                    "dev has archdata.dma.ops ?"[shape=diamond];
            "dev->dma_mem ?"[shape=diamond];
            "is_coherent or nommu"[shape=diamond];
    "dma_alloc_coherent"->"dev has archdata.dma.ops ?"[dir="forward",arrowhead="none",arrowtail="normal",style=dashed];
        "dev has archdata.dma.ops ?"->"uses archdata.dma.ops"[label = "yes"];
        "uses archdata.dma.ops"->"archdata.dma.ops->alloc()"
        "dev has archdata.dma.ops ?"->"arm_dma_ops"[label = "no"];
        
        "arm_dma_ops"->"dev->dma_mem ?";
        "dev->dma_mem ?"->"bitmap_find_free_region \nand return mem->virt_base"[label=yes]
        "dev->dma_mem ?"->"__dma_alloc"[label=no]
        "__dma_alloc"->"is_coherent or nommu"
        "is_coherent or nommu"->"simple_buff"[label=yes]
        "is_coherent or nommu"->"from dma pool ... else if..."[label=no]
        "from dma pool ... else if..."->"__alloc_remap_buff"->"..."->"dma_alloc_remap\n /* DMA allocation can be \nmapped to user space, so lets\nset VM_USERMAP flags too. */"
        
                }
```
1. Using kmalloc instead of dma_alloc_coherent
2. Setting set_arch_dma_coherent_ops(dev) when rproc is probed
    * let dma_alloc_coherent select simple_buff



## What's the virtio console name?
-> **vport#p#**
* first number is virtio device index
* last number is the port of the virtio console
    * In rproc_serial case, it is always be zero
* Currently, we just has a virtio_console
    * the name is `vport5p0`
    * the name is dynamic by virtio device

* the gpsd
    * it will send a passward to AT and get a certification through the virtio console
    * is AT supporting by modem side?
---
1. use dynamic name
    * do not change the linux
    * flexibility of new virtio console user
    * the user has to detect by itself
2. static name
    * modify linux kernel
    * it is hard for the new user
    * use vdev config by resource table