55 */
66
77#define DT_DRV_COMPAT virtio_console
8- #include <stdio.h>
98#include <zephyr/device.h>
109#include <zephyr/drivers/virtio.h>
1110#include <zephyr/drivers/virtio/virtqueue.h>
@@ -35,6 +34,11 @@ struct _virtio_console_control {
3534 /* VIRTIO_CONSOLE_PORT_NAME immediately followed by a name */
3635 char name [CONFIG_UART_VIRTIO_CONSOLE_NAME_BUFSIZE ];
3736};
37+ struct _fifo_item_virtio_console_control {
38+ void * fifo_reserved ;
39+ bool pending ; /* true if message is awaiting transmission */
40+ struct _virtio_console_control msg ;
41+ };
3842#endif
3943
4044enum _flags {
@@ -112,8 +116,10 @@ struct virtconsole_data {
112116 int8_t n_console_ports ;
113117 struct k_spinlock ctlrxsl , ctltxsl ;
114118 size_t txctlcurrent ;
115- struct _virtio_console_control rx_ctlbuf [CONFIG_UART_VIRTIO_CONSOLE_RX_CONTROL_BUFSIZE ],
119+ struct _virtio_console_control rx_ctlbuf [CONFIG_UART_VIRTIO_CONSOLE_RX_CONTROL_BUFSIZE ];
120+ struct _fifo_item_virtio_console_control
116121 tx_ctlbuf [CONFIG_UART_VIRTIO_CONSOLE_TX_CONTROL_BUFSIZE ];
122+ struct k_fifo tx_ctlfifo ;
117123 struct _ctl_cb_data ctl_cb_data [CONFIG_UART_VIRTIO_CONSOLE_RX_CONTROL_BUFSIZE ];
118124 struct _rx_cb_data rx_cb_data [VIRTIO_CONSOLE_MAX_PORTS ];
119125#else
@@ -176,13 +182,42 @@ static void virtconsole_recv_setup(const struct device *dev, uint16_t q_no, void
176182 }
177183 struct virtq_buf vqbuf [] = {{.addr = addr , .len = len }};
178184
179- if (virtq_add_buffer_chain (vq , vqbuf , 1 , 0 , recv_cb , cb_data , K_FOREVER )) {
185+ if (virtq_add_buffer_chain (vq , vqbuf , 1 , 0 , recv_cb , cb_data , K_NO_WAIT )) {
180186 LOG_ERR ("could not set up virtqueue %u for receiving" , q_no );
181187 return ;
182188 }
183189 virtio_notify_virtqueue (config -> vdev , q_no );
184190}
185191#ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
192+ static void virtconsole_control_tx_flush (void * priv , uint32_t len )
193+ {
194+ struct virtconsole_data * data = priv ;
195+ const struct device * dev = data -> dev ;
196+ const struct virtconsole_config * config = dev -> config ;
197+ struct _fifo_item_virtio_console_control * item ;
198+ struct virtq * vq = virtio_get_virtqueue (config -> vdev , VIRTQ_CONTROL_TX );
199+
200+ if (vq == NULL ) {
201+ LOG_ERR ("could not access virtqueue 3" );
202+ return ;
203+ }
204+ int i = vq -> free_desc_n ;
205+
206+ while ((i -- > 0 ) && (item = k_fifo_get (& data -> tx_ctlfifo , K_NO_WAIT ))) {
207+ struct virtq_buf vqbuf = {.addr = & item -> msg , .len = sizeof (item -> msg )};
208+
209+ int ret = virtq_add_buffer_chain (vq , & vqbuf , 1 , 1 , virtconsole_control_tx_flush ,
210+ priv , K_NO_WAIT );
211+
212+ if (ret ) {
213+ LOG_ERR ("could not send control message" );
214+ return ;
215+ }
216+ virtio_notify_virtqueue (config -> vdev , VIRTQ_CONTROL_TX );
217+ item -> pending = false;
218+ }
219+ }
220+
186221static void virtconsole_send_control_msg (const struct device * dev , uint32_t port , uint16_t event ,
187222 uint16_t value )
188223{
@@ -196,17 +231,33 @@ static void virtconsole_send_control_msg(const struct device *dev, uint32_t port
196231 LOG_ERR ("could not access virtqueue 3" );
197232 K_SPINLOCK_BREAK ;
198233 }
199- data -> tx_ctlbuf [data -> txctlcurrent ].port = sys_cpu_to_le32 (port );
200- data -> tx_ctlbuf [data -> txctlcurrent ].event = sys_cpu_to_le16 (event );
201- data -> tx_ctlbuf [data -> txctlcurrent ].value = sys_cpu_to_le16 (value );
202- struct virtq_buf vqbuf = {.addr = data -> tx_ctlbuf + data -> txctlcurrent ,
203- .len = sizeof (data -> tx_ctlbuf [0 ])};
234+ struct _fifo_item_virtio_console_control * item =
235+ & (data -> tx_ctlbuf [data -> txctlcurrent ]);
236+ struct _virtio_console_control * msg = & (item -> msg );
204237
205- if (virtq_add_buffer_chain (vq , & vqbuf , 1 , 1 , NULL , NULL , K_FOREVER )) {
238+ if (item -> pending ) {
239+ LOG_ERR ("not enough free buffers for control message" );
240+ K_SPINLOCK_BREAK ;
241+ }
242+ msg -> port = sys_cpu_to_le32 (port );
243+ msg -> event = sys_cpu_to_le16 (event );
244+ msg -> value = sys_cpu_to_le16 (value );
245+ struct virtq_buf vqbuf = {.addr = msg , .len = sizeof (* msg )};
246+
247+ int ret = virtq_add_buffer_chain (vq , & vqbuf , 1 , 1 , virtconsole_control_tx_flush ,
248+ data , K_NO_WAIT );
249+
250+ if (ret == - EBUSY ) {
251+ /* put in FIFO to be sent later, mark buffer */
252+ /* as occupied to prevent overwriting */
253+ k_fifo_put (& data -> tx_ctlfifo , data -> tx_ctlbuf + data -> txctlcurrent );
254+ item -> pending = true;
255+ } else if (ret ) {
206256 LOG_ERR ("could not send control message" );
207257 K_SPINLOCK_BREAK ;
258+ } else {
259+ virtio_notify_virtqueue (config -> vdev , VIRTQ_CONTROL_TX );
208260 }
209- virtio_notify_virtqueue (config -> vdev , VIRTQ_CONTROL_TX );
210261 data -> txctlcurrent =
211262 (data -> txctlcurrent + 1 ) % CONFIG_UART_VIRTIO_CONSOLE_TX_CONTROL_BUFSIZE ;
212263 }
@@ -499,6 +550,7 @@ static int virtconsole_init(const struct device *dev)
499550 virtio_finalize_init (config -> vdev );
500551#ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
501552 if (multiport ) {
553+ k_fifo_init (& data -> tx_ctlfifo );
502554 for (int i = 0 ; i < CONFIG_UART_VIRTIO_CONSOLE_RX_CONTROL_BUFSIZE ; i ++ ) {
503555 data -> ctl_cb_data [i ].data = data ;
504556 data -> ctl_cb_data [i ].buf_no = i ;
0 commit comments