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

* 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