18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation
48c2ecf20Sopenharmony_ci * Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
58c2ecf20Sopenharmony_ci * Copyright (C) 2009, 2010, 2011 Amit Shah <amit.shah@redhat.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/cdev.h>
88c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
98c2ecf20Sopenharmony_ci#include <linux/completion.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/freezer.h>
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci#include <linux/splice.h>
158c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/list.h>
188c2ecf20Sopenharmony_ci#include <linux/poll.h>
198c2ecf20Sopenharmony_ci#include <linux/sched.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
228c2ecf20Sopenharmony_ci#include <linux/virtio.h>
238c2ecf20Sopenharmony_ci#include <linux/virtio_console.h>
248c2ecf20Sopenharmony_ci#include <linux/wait.h>
258c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
288c2ecf20Sopenharmony_ci#include "../tty/hvc/hvc_console.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * This is a global struct for storing common data for all the devices
348c2ecf20Sopenharmony_ci * this driver handles.
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * Mainly, it has a linked list for all the consoles in one place so
378c2ecf20Sopenharmony_ci * that callbacks from hvc for get_chars(), put_chars() work properly
388c2ecf20Sopenharmony_ci * across multiple devices and multiple ports per device.
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_cistruct ports_driver_data {
418c2ecf20Sopenharmony_ci	/* Used for registering chardevs */
428c2ecf20Sopenharmony_ci	struct class *class;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/* Used for exporting per-port information to debugfs */
458c2ecf20Sopenharmony_ci	struct dentry *debugfs_dir;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/* List of all the devices we're handling */
488c2ecf20Sopenharmony_ci	struct list_head portdevs;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/*
518c2ecf20Sopenharmony_ci	 * This is used to keep track of the number of hvc consoles
528c2ecf20Sopenharmony_ci	 * spawned by this driver.  This number is given as the first
538c2ecf20Sopenharmony_ci	 * argument to hvc_alloc().  To correctly map an initial
548c2ecf20Sopenharmony_ci	 * console spawned via hvc_instantiate to the console being
558c2ecf20Sopenharmony_ci	 * hooked up via hvc_alloc, we need to pass the same vtermno.
568c2ecf20Sopenharmony_ci	 *
578c2ecf20Sopenharmony_ci	 * We also just assume the first console being initialised was
588c2ecf20Sopenharmony_ci	 * the first one that got used as the initial console.
598c2ecf20Sopenharmony_ci	 */
608c2ecf20Sopenharmony_ci	unsigned int next_vtermno;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* All the console devices handled by this driver */
638c2ecf20Sopenharmony_ci	struct list_head consoles;
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_cistatic struct ports_driver_data pdrvdata = { .next_vtermno = 1};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(pdrvdata_lock);
688c2ecf20Sopenharmony_cistatic DECLARE_COMPLETION(early_console_added);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* This struct holds information that's relevant only for console ports */
718c2ecf20Sopenharmony_cistruct console {
728c2ecf20Sopenharmony_ci	/* We'll place all consoles in a list in the pdrvdata struct */
738c2ecf20Sopenharmony_ci	struct list_head list;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* The hvc device associated with this console port */
768c2ecf20Sopenharmony_ci	struct hvc_struct *hvc;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* The size of the console */
798c2ecf20Sopenharmony_ci	struct winsize ws;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	/*
828c2ecf20Sopenharmony_ci	 * This number identifies the number that we used to register
838c2ecf20Sopenharmony_ci	 * with hvc in hvc_instantiate() and hvc_alloc(); this is the
848c2ecf20Sopenharmony_ci	 * number passed on by the hvc callbacks to us to
858c2ecf20Sopenharmony_ci	 * differentiate between the other console ports handled by
868c2ecf20Sopenharmony_ci	 * this driver
878c2ecf20Sopenharmony_ci	 */
888c2ecf20Sopenharmony_ci	u32 vtermno;
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistruct port_buffer {
928c2ecf20Sopenharmony_ci	char *buf;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* size of the buffer in *buf above */
958c2ecf20Sopenharmony_ci	size_t size;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* used length of the buffer */
988c2ecf20Sopenharmony_ci	size_t len;
998c2ecf20Sopenharmony_ci	/* offset in the buf from which to consume data */
1008c2ecf20Sopenharmony_ci	size_t offset;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* DMA address of buffer */
1038c2ecf20Sopenharmony_ci	dma_addr_t dma;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* Device we got DMA memory from */
1068c2ecf20Sopenharmony_ci	struct device *dev;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* List of pending dma buffers to free */
1098c2ecf20Sopenharmony_ci	struct list_head list;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* If sgpages == 0 then buf is used */
1128c2ecf20Sopenharmony_ci	unsigned int sgpages;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* sg is used if spages > 0. sg must be the last in is struct */
1158c2ecf20Sopenharmony_ci	struct scatterlist sg[];
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/*
1198c2ecf20Sopenharmony_ci * This is a per-device struct that stores data common to all the
1208c2ecf20Sopenharmony_ci * ports for that device (vdev->priv).
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_cistruct ports_device {
1238c2ecf20Sopenharmony_ci	/* Next portdev in the list, head is in the pdrvdata struct */
1248c2ecf20Sopenharmony_ci	struct list_head list;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/*
1278c2ecf20Sopenharmony_ci	 * Workqueue handlers where we process deferred work after
1288c2ecf20Sopenharmony_ci	 * notification
1298c2ecf20Sopenharmony_ci	 */
1308c2ecf20Sopenharmony_ci	struct work_struct control_work;
1318c2ecf20Sopenharmony_ci	struct work_struct config_work;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	struct list_head ports;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* To protect the list of ports */
1368c2ecf20Sopenharmony_ci	spinlock_t ports_lock;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* To protect the vq operations for the control channel */
1398c2ecf20Sopenharmony_ci	spinlock_t c_ivq_lock;
1408c2ecf20Sopenharmony_ci	spinlock_t c_ovq_lock;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* max. number of ports this device can hold */
1438c2ecf20Sopenharmony_ci	u32 max_nr_ports;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* The virtio device we're associated with */
1468c2ecf20Sopenharmony_ci	struct virtio_device *vdev;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/*
1498c2ecf20Sopenharmony_ci	 * A couple of virtqueues for the control channel: one for
1508c2ecf20Sopenharmony_ci	 * guest->host transfers, one for host->guest transfers
1518c2ecf20Sopenharmony_ci	 */
1528c2ecf20Sopenharmony_ci	struct virtqueue *c_ivq, *c_ovq;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/*
1558c2ecf20Sopenharmony_ci	 * A control packet buffer for guest->host requests, protected
1568c2ecf20Sopenharmony_ci	 * by c_ovq_lock.
1578c2ecf20Sopenharmony_ci	 */
1588c2ecf20Sopenharmony_ci	struct virtio_console_control cpkt;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* Array of per-port IO virtqueues */
1618c2ecf20Sopenharmony_ci	struct virtqueue **in_vqs, **out_vqs;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* Major number for this device.  Ports will be created as minors. */
1648c2ecf20Sopenharmony_ci	int chr_major;
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistruct port_stats {
1688c2ecf20Sopenharmony_ci	unsigned long bytes_sent, bytes_received, bytes_discarded;
1698c2ecf20Sopenharmony_ci};
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/* This struct holds the per-port data */
1728c2ecf20Sopenharmony_cistruct port {
1738c2ecf20Sopenharmony_ci	/* Next port in the list, head is in the ports_device */
1748c2ecf20Sopenharmony_ci	struct list_head list;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* Pointer to the parent virtio_console device */
1778c2ecf20Sopenharmony_ci	struct ports_device *portdev;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* The current buffer from which data has to be fed to readers */
1808c2ecf20Sopenharmony_ci	struct port_buffer *inbuf;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/*
1838c2ecf20Sopenharmony_ci	 * To protect the operations on the in_vq associated with this
1848c2ecf20Sopenharmony_ci	 * port.  Has to be a spinlock because it can be called from
1858c2ecf20Sopenharmony_ci	 * interrupt context (get_char()).
1868c2ecf20Sopenharmony_ci	 */
1878c2ecf20Sopenharmony_ci	spinlock_t inbuf_lock;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* Protect the operations on the out_vq. */
1908c2ecf20Sopenharmony_ci	spinlock_t outvq_lock;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* The IO vqs for this port */
1938c2ecf20Sopenharmony_ci	struct virtqueue *in_vq, *out_vq;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/* File in the debugfs directory that exposes this port's information */
1968c2ecf20Sopenharmony_ci	struct dentry *debugfs_file;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/*
1998c2ecf20Sopenharmony_ci	 * Keep count of the bytes sent, received and discarded for
2008c2ecf20Sopenharmony_ci	 * this port for accounting and debugging purposes.  These
2018c2ecf20Sopenharmony_ci	 * counts are not reset across port open / close events.
2028c2ecf20Sopenharmony_ci	 */
2038c2ecf20Sopenharmony_ci	struct port_stats stats;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/*
2068c2ecf20Sopenharmony_ci	 * The entries in this struct will be valid if this port is
2078c2ecf20Sopenharmony_ci	 * hooked up to an hvc console
2088c2ecf20Sopenharmony_ci	 */
2098c2ecf20Sopenharmony_ci	struct console cons;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	/* Each port associates with a separate char device */
2128c2ecf20Sopenharmony_ci	struct cdev *cdev;
2138c2ecf20Sopenharmony_ci	struct device *dev;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* Reference-counting to handle port hot-unplugs and file operations */
2168c2ecf20Sopenharmony_ci	struct kref kref;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* A waitqueue for poll() or blocking read operations */
2198c2ecf20Sopenharmony_ci	wait_queue_head_t waitqueue;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* The 'name' of the port that we expose via sysfs properties */
2228c2ecf20Sopenharmony_ci	char *name;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* We can notify apps of host connect / disconnect events via SIGIO */
2258c2ecf20Sopenharmony_ci	struct fasync_struct *async_queue;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* The 'id' to identify the port with the Host */
2288c2ecf20Sopenharmony_ci	u32 id;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	bool outvq_full;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* Is the host device open */
2338c2ecf20Sopenharmony_ci	bool host_connected;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* We should allow only one process to open a port */
2368c2ecf20Sopenharmony_ci	bool guest_connected;
2378c2ecf20Sopenharmony_ci};
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/* This is the very early arch-specified put chars function. */
2408c2ecf20Sopenharmony_cistatic int (*early_put_chars)(u32, const char *, int);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic struct port *find_port_by_vtermno(u32 vtermno)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct port *port;
2458c2ecf20Sopenharmony_ci	struct console *cons;
2468c2ecf20Sopenharmony_ci	unsigned long flags;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pdrvdata_lock, flags);
2498c2ecf20Sopenharmony_ci	list_for_each_entry(cons, &pdrvdata.consoles, list) {
2508c2ecf20Sopenharmony_ci		if (cons->vtermno == vtermno) {
2518c2ecf20Sopenharmony_ci			port = container_of(cons, struct port, cons);
2528c2ecf20Sopenharmony_ci			goto out;
2538c2ecf20Sopenharmony_ci		}
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	port = NULL;
2568c2ecf20Sopenharmony_ciout:
2578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pdrvdata_lock, flags);
2588c2ecf20Sopenharmony_ci	return port;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic struct port *find_port_by_devt_in_portdev(struct ports_device *portdev,
2628c2ecf20Sopenharmony_ci						 dev_t dev)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct port *port;
2658c2ecf20Sopenharmony_ci	unsigned long flags;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	spin_lock_irqsave(&portdev->ports_lock, flags);
2688c2ecf20Sopenharmony_ci	list_for_each_entry(port, &portdev->ports, list) {
2698c2ecf20Sopenharmony_ci		if (port->cdev->dev == dev) {
2708c2ecf20Sopenharmony_ci			kref_get(&port->kref);
2718c2ecf20Sopenharmony_ci			goto out;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci	port = NULL;
2758c2ecf20Sopenharmony_ciout:
2768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&portdev->ports_lock, flags);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return port;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic struct port *find_port_by_devt(dev_t dev)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct ports_device *portdev;
2848c2ecf20Sopenharmony_ci	struct port *port;
2858c2ecf20Sopenharmony_ci	unsigned long flags;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pdrvdata_lock, flags);
2888c2ecf20Sopenharmony_ci	list_for_each_entry(portdev, &pdrvdata.portdevs, list) {
2898c2ecf20Sopenharmony_ci		port = find_port_by_devt_in_portdev(portdev, dev);
2908c2ecf20Sopenharmony_ci		if (port)
2918c2ecf20Sopenharmony_ci			goto out;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci	port = NULL;
2948c2ecf20Sopenharmony_ciout:
2958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pdrvdata_lock, flags);
2968c2ecf20Sopenharmony_ci	return port;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic struct port *find_port_by_id(struct ports_device *portdev, u32 id)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct port *port;
3028c2ecf20Sopenharmony_ci	unsigned long flags;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&portdev->ports_lock, flags);
3058c2ecf20Sopenharmony_ci	list_for_each_entry(port, &portdev->ports, list)
3068c2ecf20Sopenharmony_ci		if (port->id == id)
3078c2ecf20Sopenharmony_ci			goto out;
3088c2ecf20Sopenharmony_ci	port = NULL;
3098c2ecf20Sopenharmony_ciout:
3108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&portdev->ports_lock, flags);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	return port;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic struct port *find_port_by_vq(struct ports_device *portdev,
3168c2ecf20Sopenharmony_ci				    struct virtqueue *vq)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	struct port *port;
3198c2ecf20Sopenharmony_ci	unsigned long flags;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&portdev->ports_lock, flags);
3228c2ecf20Sopenharmony_ci	list_for_each_entry(port, &portdev->ports, list)
3238c2ecf20Sopenharmony_ci		if (port->in_vq == vq || port->out_vq == vq)
3248c2ecf20Sopenharmony_ci			goto out;
3258c2ecf20Sopenharmony_ci	port = NULL;
3268c2ecf20Sopenharmony_ciout:
3278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&portdev->ports_lock, flags);
3288c2ecf20Sopenharmony_ci	return port;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic bool is_console_port(struct port *port)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	if (port->cons.hvc)
3348c2ecf20Sopenharmony_ci		return true;
3358c2ecf20Sopenharmony_ci	return false;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic bool is_rproc_serial(const struct virtio_device *vdev)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	return is_rproc_enabled && vdev->id.device == VIRTIO_ID_RPROC_SERIAL;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic inline bool use_multiport(struct ports_device *portdev)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	/*
3468c2ecf20Sopenharmony_ci	 * This condition can be true when put_chars is called from
3478c2ecf20Sopenharmony_ci	 * early_init
3488c2ecf20Sopenharmony_ci	 */
3498c2ecf20Sopenharmony_ci	if (!portdev->vdev)
3508c2ecf20Sopenharmony_ci		return false;
3518c2ecf20Sopenharmony_ci	return __virtio_test_bit(portdev->vdev, VIRTIO_CONSOLE_F_MULTIPORT);
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(dma_bufs_lock);
3558c2ecf20Sopenharmony_cistatic LIST_HEAD(pending_free_dma_bufs);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic void free_buf(struct port_buffer *buf, bool can_sleep)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	unsigned int i;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	for (i = 0; i < buf->sgpages; i++) {
3628c2ecf20Sopenharmony_ci		struct page *page = sg_page(&buf->sg[i]);
3638c2ecf20Sopenharmony_ci		if (!page)
3648c2ecf20Sopenharmony_ci			break;
3658c2ecf20Sopenharmony_ci		put_page(page);
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (!buf->dev) {
3698c2ecf20Sopenharmony_ci		kfree(buf->buf);
3708c2ecf20Sopenharmony_ci	} else if (is_rproc_enabled) {
3718c2ecf20Sopenharmony_ci		unsigned long flags;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		/* dma_free_coherent requires interrupts to be enabled. */
3748c2ecf20Sopenharmony_ci		if (!can_sleep) {
3758c2ecf20Sopenharmony_ci			/* queue up dma-buffers to be freed later */
3768c2ecf20Sopenharmony_ci			spin_lock_irqsave(&dma_bufs_lock, flags);
3778c2ecf20Sopenharmony_ci			list_add_tail(&buf->list, &pending_free_dma_bufs);
3788c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&dma_bufs_lock, flags);
3798c2ecf20Sopenharmony_ci			return;
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci		dma_free_coherent(buf->dev, buf->size, buf->buf, buf->dma);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		/* Release device refcnt and allow it to be freed */
3848c2ecf20Sopenharmony_ci		put_device(buf->dev);
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	kfree(buf);
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic void reclaim_dma_bufs(void)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	unsigned long flags;
3938c2ecf20Sopenharmony_ci	struct port_buffer *buf, *tmp;
3948c2ecf20Sopenharmony_ci	LIST_HEAD(tmp_list);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (list_empty(&pending_free_dma_bufs))
3978c2ecf20Sopenharmony_ci		return;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* Create a copy of the pending_free_dma_bufs while holding the lock */
4008c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dma_bufs_lock, flags);
4018c2ecf20Sopenharmony_ci	list_cut_position(&tmp_list, &pending_free_dma_bufs,
4028c2ecf20Sopenharmony_ci			  pending_free_dma_bufs.prev);
4038c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dma_bufs_lock, flags);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/* Release the dma buffers, without irqs enabled */
4068c2ecf20Sopenharmony_ci	list_for_each_entry_safe(buf, tmp, &tmp_list, list) {
4078c2ecf20Sopenharmony_ci		list_del(&buf->list);
4088c2ecf20Sopenharmony_ci		free_buf(buf, true);
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size,
4138c2ecf20Sopenharmony_ci				     int pages)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct port_buffer *buf;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	reclaim_dma_bufs();
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/*
4208c2ecf20Sopenharmony_ci	 * Allocate buffer and the sg list. The sg list array is allocated
4218c2ecf20Sopenharmony_ci	 * directly after the port_buffer struct.
4228c2ecf20Sopenharmony_ci	 */
4238c2ecf20Sopenharmony_ci	buf = kmalloc(struct_size(buf, sg, pages), GFP_KERNEL);
4248c2ecf20Sopenharmony_ci	if (!buf)
4258c2ecf20Sopenharmony_ci		goto fail;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	buf->sgpages = pages;
4288c2ecf20Sopenharmony_ci	if (pages > 0) {
4298c2ecf20Sopenharmony_ci		buf->dev = NULL;
4308c2ecf20Sopenharmony_ci		buf->buf = NULL;
4318c2ecf20Sopenharmony_ci		return buf;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (is_rproc_serial(vdev)) {
4358c2ecf20Sopenharmony_ci		/*
4368c2ecf20Sopenharmony_ci		 * Allocate DMA memory from ancestor. When a virtio
4378c2ecf20Sopenharmony_ci		 * device is created by remoteproc, the DMA memory is
4388c2ecf20Sopenharmony_ci		 * associated with the parent device:
4398c2ecf20Sopenharmony_ci		 * virtioY => remoteprocX#vdevYbuffer.
4408c2ecf20Sopenharmony_ci		 */
4418c2ecf20Sopenharmony_ci		buf->dev = vdev->dev.parent;
4428c2ecf20Sopenharmony_ci		if (!buf->dev)
4438c2ecf20Sopenharmony_ci			goto free_buf;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		/* Increase device refcnt to avoid freeing it */
4468c2ecf20Sopenharmony_ci		get_device(buf->dev);
4478c2ecf20Sopenharmony_ci		buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma,
4488c2ecf20Sopenharmony_ci					      GFP_KERNEL);
4498c2ecf20Sopenharmony_ci	} else {
4508c2ecf20Sopenharmony_ci		buf->dev = NULL;
4518c2ecf20Sopenharmony_ci		buf->buf = kmalloc(buf_size, GFP_KERNEL);
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (!buf->buf)
4558c2ecf20Sopenharmony_ci		goto free_buf;
4568c2ecf20Sopenharmony_ci	buf->len = 0;
4578c2ecf20Sopenharmony_ci	buf->offset = 0;
4588c2ecf20Sopenharmony_ci	buf->size = buf_size;
4598c2ecf20Sopenharmony_ci	return buf;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cifree_buf:
4628c2ecf20Sopenharmony_ci	kfree(buf);
4638c2ecf20Sopenharmony_cifail:
4648c2ecf20Sopenharmony_ci	return NULL;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci/* Callers should take appropriate locks */
4688c2ecf20Sopenharmony_cistatic struct port_buffer *get_inbuf(struct port *port)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	struct port_buffer *buf;
4718c2ecf20Sopenharmony_ci	unsigned int len;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (port->inbuf)
4748c2ecf20Sopenharmony_ci		return port->inbuf;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	buf = virtqueue_get_buf(port->in_vq, &len);
4778c2ecf20Sopenharmony_ci	if (buf) {
4788c2ecf20Sopenharmony_ci		buf->len = min_t(size_t, len, buf->size);
4798c2ecf20Sopenharmony_ci		buf->offset = 0;
4808c2ecf20Sopenharmony_ci		port->stats.bytes_received += len;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci	return buf;
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci/*
4868c2ecf20Sopenharmony_ci * Create a scatter-gather list representing our input buffer and put
4878c2ecf20Sopenharmony_ci * it in the queue.
4888c2ecf20Sopenharmony_ci *
4898c2ecf20Sopenharmony_ci * Callers should take appropriate locks.
4908c2ecf20Sopenharmony_ci */
4918c2ecf20Sopenharmony_cistatic int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct scatterlist sg[1];
4948c2ecf20Sopenharmony_ci	int ret;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	sg_init_one(sg, buf->buf, buf->size);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	ret = virtqueue_add_inbuf(vq, sg, 1, buf, GFP_ATOMIC);
4998c2ecf20Sopenharmony_ci	virtqueue_kick(vq);
5008c2ecf20Sopenharmony_ci	if (!ret)
5018c2ecf20Sopenharmony_ci		ret = vq->num_free;
5028c2ecf20Sopenharmony_ci	return ret;
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci/* Discard any unread data this port has. Callers lockers. */
5068c2ecf20Sopenharmony_cistatic void discard_port_data(struct port *port)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	struct port_buffer *buf;
5098c2ecf20Sopenharmony_ci	unsigned int err;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	if (!port->portdev) {
5128c2ecf20Sopenharmony_ci		/* Device has been unplugged.  vqs are already gone. */
5138c2ecf20Sopenharmony_ci		return;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci	buf = get_inbuf(port);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	err = 0;
5188c2ecf20Sopenharmony_ci	while (buf) {
5198c2ecf20Sopenharmony_ci		port->stats.bytes_discarded += buf->len - buf->offset;
5208c2ecf20Sopenharmony_ci		if (add_inbuf(port->in_vq, buf) < 0) {
5218c2ecf20Sopenharmony_ci			err++;
5228c2ecf20Sopenharmony_ci			free_buf(buf, false);
5238c2ecf20Sopenharmony_ci		}
5248c2ecf20Sopenharmony_ci		port->inbuf = NULL;
5258c2ecf20Sopenharmony_ci		buf = get_inbuf(port);
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci	if (err)
5288c2ecf20Sopenharmony_ci		dev_warn(port->dev, "Errors adding %d buffers back to vq\n",
5298c2ecf20Sopenharmony_ci			 err);
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic bool port_has_data(struct port *port)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	unsigned long flags;
5358c2ecf20Sopenharmony_ci	bool ret;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	ret = false;
5388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&port->inbuf_lock, flags);
5398c2ecf20Sopenharmony_ci	port->inbuf = get_inbuf(port);
5408c2ecf20Sopenharmony_ci	if (port->inbuf)
5418c2ecf20Sopenharmony_ci		ret = true;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&port->inbuf_lock, flags);
5448c2ecf20Sopenharmony_ci	return ret;
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id,
5488c2ecf20Sopenharmony_ci				  unsigned int event, unsigned int value)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci	struct scatterlist sg[1];
5518c2ecf20Sopenharmony_ci	struct virtqueue *vq;
5528c2ecf20Sopenharmony_ci	unsigned int len;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (!use_multiport(portdev))
5558c2ecf20Sopenharmony_ci		return 0;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	vq = portdev->c_ovq;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	spin_lock(&portdev->c_ovq_lock);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	portdev->cpkt.id = cpu_to_virtio32(portdev->vdev, port_id);
5628c2ecf20Sopenharmony_ci	portdev->cpkt.event = cpu_to_virtio16(portdev->vdev, event);
5638c2ecf20Sopenharmony_ci	portdev->cpkt.value = cpu_to_virtio16(portdev->vdev, value);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	sg_init_one(sg, &portdev->cpkt, sizeof(struct virtio_console_control));
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (virtqueue_add_outbuf(vq, sg, 1, &portdev->cpkt, GFP_ATOMIC) == 0) {
5688c2ecf20Sopenharmony_ci		virtqueue_kick(vq);
5698c2ecf20Sopenharmony_ci		while (!virtqueue_get_buf(vq, &len)
5708c2ecf20Sopenharmony_ci			&& !virtqueue_is_broken(vq))
5718c2ecf20Sopenharmony_ci			cpu_relax();
5728c2ecf20Sopenharmony_ci	}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	spin_unlock(&portdev->c_ovq_lock);
5758c2ecf20Sopenharmony_ci	return 0;
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic ssize_t send_control_msg(struct port *port, unsigned int event,
5798c2ecf20Sopenharmony_ci				unsigned int value)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	/* Did the port get unplugged before userspace closed it? */
5828c2ecf20Sopenharmony_ci	if (port->portdev)
5838c2ecf20Sopenharmony_ci		return __send_control_msg(port->portdev, port->id, event, value);
5848c2ecf20Sopenharmony_ci	return 0;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci/* Callers must take the port->outvq_lock */
5898c2ecf20Sopenharmony_cistatic void reclaim_consumed_buffers(struct port *port)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	struct port_buffer *buf;
5928c2ecf20Sopenharmony_ci	unsigned int len;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (!port->portdev) {
5958c2ecf20Sopenharmony_ci		/* Device has been unplugged.  vqs are already gone. */
5968c2ecf20Sopenharmony_ci		return;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci	while ((buf = virtqueue_get_buf(port->out_vq, &len))) {
5998c2ecf20Sopenharmony_ci		free_buf(buf, false);
6008c2ecf20Sopenharmony_ci		port->outvq_full = false;
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
6058c2ecf20Sopenharmony_ci			      int nents, size_t in_count,
6068c2ecf20Sopenharmony_ci			      void *data, bool nonblock)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	struct virtqueue *out_vq;
6098c2ecf20Sopenharmony_ci	int err;
6108c2ecf20Sopenharmony_ci	unsigned long flags;
6118c2ecf20Sopenharmony_ci	unsigned int len;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	out_vq = port->out_vq;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	spin_lock_irqsave(&port->outvq_lock, flags);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	reclaim_consumed_buffers(port);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	err = virtqueue_add_outbuf(out_vq, sg, nents, data, GFP_ATOMIC);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	/* Tell Host to go! */
6228c2ecf20Sopenharmony_ci	virtqueue_kick(out_vq);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (err) {
6258c2ecf20Sopenharmony_ci		in_count = 0;
6268c2ecf20Sopenharmony_ci		goto done;
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	if (out_vq->num_free == 0)
6308c2ecf20Sopenharmony_ci		port->outvq_full = true;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (nonblock)
6338c2ecf20Sopenharmony_ci		goto done;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	/*
6368c2ecf20Sopenharmony_ci	 * Wait till the host acknowledges it pushed out the data we
6378c2ecf20Sopenharmony_ci	 * sent.  This is done for data from the hvc_console; the tty
6388c2ecf20Sopenharmony_ci	 * operations are performed with spinlocks held so we can't
6398c2ecf20Sopenharmony_ci	 * sleep here.  An alternative would be to copy the data to a
6408c2ecf20Sopenharmony_ci	 * buffer and relax the spinning requirement.  The downside is
6418c2ecf20Sopenharmony_ci	 * we need to kmalloc a GFP_ATOMIC buffer each time the
6428c2ecf20Sopenharmony_ci	 * console driver writes something out.
6438c2ecf20Sopenharmony_ci	 */
6448c2ecf20Sopenharmony_ci	while (!virtqueue_get_buf(out_vq, &len)
6458c2ecf20Sopenharmony_ci		&& !virtqueue_is_broken(out_vq))
6468c2ecf20Sopenharmony_ci		cpu_relax();
6478c2ecf20Sopenharmony_cidone:
6488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&port->outvq_lock, flags);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	port->stats.bytes_sent += in_count;
6518c2ecf20Sopenharmony_ci	/*
6528c2ecf20Sopenharmony_ci	 * We're expected to return the amount of data we wrote -- all
6538c2ecf20Sopenharmony_ci	 * of it
6548c2ecf20Sopenharmony_ci	 */
6558c2ecf20Sopenharmony_ci	return in_count;
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci/*
6598c2ecf20Sopenharmony_ci * Give out the data that's requested from the buffer that we have
6608c2ecf20Sopenharmony_ci * queued up.
6618c2ecf20Sopenharmony_ci */
6628c2ecf20Sopenharmony_cistatic ssize_t fill_readbuf(struct port *port, char __user *out_buf,
6638c2ecf20Sopenharmony_ci			    size_t out_count, bool to_user)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	struct port_buffer *buf;
6668c2ecf20Sopenharmony_ci	unsigned long flags;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	if (!out_count || !port_has_data(port))
6698c2ecf20Sopenharmony_ci		return 0;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	buf = port->inbuf;
6728c2ecf20Sopenharmony_ci	out_count = min(out_count, buf->len - buf->offset);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	if (to_user) {
6758c2ecf20Sopenharmony_ci		ssize_t ret;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		ret = copy_to_user(out_buf, buf->buf + buf->offset, out_count);
6788c2ecf20Sopenharmony_ci		if (ret)
6798c2ecf20Sopenharmony_ci			return -EFAULT;
6808c2ecf20Sopenharmony_ci	} else {
6818c2ecf20Sopenharmony_ci		memcpy((__force char *)out_buf, buf->buf + buf->offset,
6828c2ecf20Sopenharmony_ci		       out_count);
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	buf->offset += out_count;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	if (buf->offset == buf->len) {
6888c2ecf20Sopenharmony_ci		/*
6898c2ecf20Sopenharmony_ci		 * We're done using all the data in this buffer.
6908c2ecf20Sopenharmony_ci		 * Re-queue so that the Host can send us more data.
6918c2ecf20Sopenharmony_ci		 */
6928c2ecf20Sopenharmony_ci		spin_lock_irqsave(&port->inbuf_lock, flags);
6938c2ecf20Sopenharmony_ci		port->inbuf = NULL;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci		if (add_inbuf(port->in_vq, buf) < 0)
6968c2ecf20Sopenharmony_ci			dev_warn(port->dev, "failed add_buf\n");
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&port->inbuf_lock, flags);
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci	/* Return the number of bytes actually copied */
7018c2ecf20Sopenharmony_ci	return out_count;
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci/* The condition that must be true for polling to end */
7058c2ecf20Sopenharmony_cistatic bool will_read_block(struct port *port)
7068c2ecf20Sopenharmony_ci{
7078c2ecf20Sopenharmony_ci	if (!port->guest_connected) {
7088c2ecf20Sopenharmony_ci		/* Port got hot-unplugged. Let's exit. */
7098c2ecf20Sopenharmony_ci		return false;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci	return !port_has_data(port) && port->host_connected;
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_cistatic bool will_write_block(struct port *port)
7158c2ecf20Sopenharmony_ci{
7168c2ecf20Sopenharmony_ci	bool ret;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (!port->guest_connected) {
7198c2ecf20Sopenharmony_ci		/* Port got hot-unplugged. Let's exit. */
7208c2ecf20Sopenharmony_ci		return false;
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci	if (!port->host_connected)
7238c2ecf20Sopenharmony_ci		return true;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	spin_lock_irq(&port->outvq_lock);
7268c2ecf20Sopenharmony_ci	/*
7278c2ecf20Sopenharmony_ci	 * Check if the Host has consumed any buffers since we last
7288c2ecf20Sopenharmony_ci	 * sent data (this is only applicable for nonblocking ports).
7298c2ecf20Sopenharmony_ci	 */
7308c2ecf20Sopenharmony_ci	reclaim_consumed_buffers(port);
7318c2ecf20Sopenharmony_ci	ret = port->outvq_full;
7328c2ecf20Sopenharmony_ci	spin_unlock_irq(&port->outvq_lock);
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	return ret;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cistatic ssize_t port_fops_read(struct file *filp, char __user *ubuf,
7388c2ecf20Sopenharmony_ci			      size_t count, loff_t *offp)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	struct port *port;
7418c2ecf20Sopenharmony_ci	ssize_t ret;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	port = filp->private_data;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	/* Port is hot-unplugged. */
7468c2ecf20Sopenharmony_ci	if (!port->guest_connected)
7478c2ecf20Sopenharmony_ci		return -ENODEV;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if (!port_has_data(port)) {
7508c2ecf20Sopenharmony_ci		/*
7518c2ecf20Sopenharmony_ci		 * If nothing's connected on the host just return 0 in
7528c2ecf20Sopenharmony_ci		 * case of list_empty; this tells the userspace app
7538c2ecf20Sopenharmony_ci		 * that there's no connection
7548c2ecf20Sopenharmony_ci		 */
7558c2ecf20Sopenharmony_ci		if (!port->host_connected)
7568c2ecf20Sopenharmony_ci			return 0;
7578c2ecf20Sopenharmony_ci		if (filp->f_flags & O_NONBLOCK)
7588c2ecf20Sopenharmony_ci			return -EAGAIN;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci		ret = wait_event_freezable(port->waitqueue,
7618c2ecf20Sopenharmony_ci					   !will_read_block(port));
7628c2ecf20Sopenharmony_ci		if (ret < 0)
7638c2ecf20Sopenharmony_ci			return ret;
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci	/* Port got hot-unplugged while we were waiting above. */
7668c2ecf20Sopenharmony_ci	if (!port->guest_connected)
7678c2ecf20Sopenharmony_ci		return -ENODEV;
7688c2ecf20Sopenharmony_ci	/*
7698c2ecf20Sopenharmony_ci	 * We could've received a disconnection message while we were
7708c2ecf20Sopenharmony_ci	 * waiting for more data.
7718c2ecf20Sopenharmony_ci	 *
7728c2ecf20Sopenharmony_ci	 * This check is not clubbed in the if() statement above as we
7738c2ecf20Sopenharmony_ci	 * might receive some data as well as the host could get
7748c2ecf20Sopenharmony_ci	 * disconnected after we got woken up from our wait.  So we
7758c2ecf20Sopenharmony_ci	 * really want to give off whatever data we have and only then
7768c2ecf20Sopenharmony_ci	 * check for host_connected.
7778c2ecf20Sopenharmony_ci	 */
7788c2ecf20Sopenharmony_ci	if (!port_has_data(port) && !port->host_connected)
7798c2ecf20Sopenharmony_ci		return 0;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	return fill_readbuf(port, ubuf, count, true);
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic int wait_port_writable(struct port *port, bool nonblock)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	int ret;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	if (will_write_block(port)) {
7898c2ecf20Sopenharmony_ci		if (nonblock)
7908c2ecf20Sopenharmony_ci			return -EAGAIN;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci		ret = wait_event_freezable(port->waitqueue,
7938c2ecf20Sopenharmony_ci					   !will_write_block(port));
7948c2ecf20Sopenharmony_ci		if (ret < 0)
7958c2ecf20Sopenharmony_ci			return ret;
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci	/* Port got hot-unplugged. */
7988c2ecf20Sopenharmony_ci	if (!port->guest_connected)
7998c2ecf20Sopenharmony_ci		return -ENODEV;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	return 0;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
8058c2ecf20Sopenharmony_ci			       size_t count, loff_t *offp)
8068c2ecf20Sopenharmony_ci{
8078c2ecf20Sopenharmony_ci	struct port *port;
8088c2ecf20Sopenharmony_ci	struct port_buffer *buf;
8098c2ecf20Sopenharmony_ci	ssize_t ret;
8108c2ecf20Sopenharmony_ci	bool nonblock;
8118c2ecf20Sopenharmony_ci	struct scatterlist sg[1];
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/* Userspace could be out to fool us */
8148c2ecf20Sopenharmony_ci	if (!count)
8158c2ecf20Sopenharmony_ci		return 0;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	port = filp->private_data;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	nonblock = filp->f_flags & O_NONBLOCK;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	ret = wait_port_writable(port, nonblock);
8228c2ecf20Sopenharmony_ci	if (ret < 0)
8238c2ecf20Sopenharmony_ci		return ret;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	count = min((size_t)(32 * 1024), count);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	buf = alloc_buf(port->portdev->vdev, count, 0);
8288c2ecf20Sopenharmony_ci	if (!buf)
8298c2ecf20Sopenharmony_ci		return -ENOMEM;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	ret = copy_from_user(buf->buf, ubuf, count);
8328c2ecf20Sopenharmony_ci	if (ret) {
8338c2ecf20Sopenharmony_ci		ret = -EFAULT;
8348c2ecf20Sopenharmony_ci		goto free_buf;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	/*
8388c2ecf20Sopenharmony_ci	 * We now ask send_buf() to not spin for generic ports -- we
8398c2ecf20Sopenharmony_ci	 * can re-use the same code path that non-blocking file
8408c2ecf20Sopenharmony_ci	 * descriptors take for blocking file descriptors since the
8418c2ecf20Sopenharmony_ci	 * wait is already done and we're certain the write will go
8428c2ecf20Sopenharmony_ci	 * through to the host.
8438c2ecf20Sopenharmony_ci	 */
8448c2ecf20Sopenharmony_ci	nonblock = true;
8458c2ecf20Sopenharmony_ci	sg_init_one(sg, buf->buf, count);
8468c2ecf20Sopenharmony_ci	ret = __send_to_port(port, sg, 1, count, buf, nonblock);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	if (nonblock && ret > 0)
8498c2ecf20Sopenharmony_ci		goto out;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_cifree_buf:
8528c2ecf20Sopenharmony_ci	free_buf(buf, true);
8538c2ecf20Sopenharmony_ciout:
8548c2ecf20Sopenharmony_ci	return ret;
8558c2ecf20Sopenharmony_ci}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_cistruct sg_list {
8588c2ecf20Sopenharmony_ci	unsigned int n;
8598c2ecf20Sopenharmony_ci	unsigned int size;
8608c2ecf20Sopenharmony_ci	size_t len;
8618c2ecf20Sopenharmony_ci	struct scatterlist *sg;
8628c2ecf20Sopenharmony_ci};
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_cistatic int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
8658c2ecf20Sopenharmony_ci			struct splice_desc *sd)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	struct sg_list *sgl = sd->u.data;
8688c2ecf20Sopenharmony_ci	unsigned int offset, len;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	if (sgl->n == sgl->size)
8718c2ecf20Sopenharmony_ci		return 0;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	/* Try lock this page */
8748c2ecf20Sopenharmony_ci	if (pipe_buf_try_steal(pipe, buf)) {
8758c2ecf20Sopenharmony_ci		/* Get reference and unlock page for moving */
8768c2ecf20Sopenharmony_ci		get_page(buf->page);
8778c2ecf20Sopenharmony_ci		unlock_page(buf->page);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci		len = min(buf->len, sd->len);
8808c2ecf20Sopenharmony_ci		sg_set_page(&(sgl->sg[sgl->n]), buf->page, len, buf->offset);
8818c2ecf20Sopenharmony_ci	} else {
8828c2ecf20Sopenharmony_ci		/* Failback to copying a page */
8838c2ecf20Sopenharmony_ci		struct page *page = alloc_page(GFP_KERNEL);
8848c2ecf20Sopenharmony_ci		char *src;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		if (!page)
8878c2ecf20Sopenharmony_ci			return -ENOMEM;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci		offset = sd->pos & ~PAGE_MASK;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci		len = sd->len;
8928c2ecf20Sopenharmony_ci		if (len + offset > PAGE_SIZE)
8938c2ecf20Sopenharmony_ci			len = PAGE_SIZE - offset;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci		src = kmap_atomic(buf->page);
8968c2ecf20Sopenharmony_ci		memcpy(page_address(page) + offset, src + buf->offset, len);
8978c2ecf20Sopenharmony_ci		kunmap_atomic(src);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci		sg_set_page(&(sgl->sg[sgl->n]), page, len, offset);
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci	sgl->n++;
9028c2ecf20Sopenharmony_ci	sgl->len += len;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	return len;
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci/* Faster zero-copy write by splicing */
9088c2ecf20Sopenharmony_cistatic ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
9098c2ecf20Sopenharmony_ci				      struct file *filp, loff_t *ppos,
9108c2ecf20Sopenharmony_ci				      size_t len, unsigned int flags)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci	struct port *port = filp->private_data;
9138c2ecf20Sopenharmony_ci	struct sg_list sgl;
9148c2ecf20Sopenharmony_ci	ssize_t ret;
9158c2ecf20Sopenharmony_ci	struct port_buffer *buf;
9168c2ecf20Sopenharmony_ci	struct splice_desc sd = {
9178c2ecf20Sopenharmony_ci		.total_len = len,
9188c2ecf20Sopenharmony_ci		.flags = flags,
9198c2ecf20Sopenharmony_ci		.pos = *ppos,
9208c2ecf20Sopenharmony_ci		.u.data = &sgl,
9218c2ecf20Sopenharmony_ci	};
9228c2ecf20Sopenharmony_ci	unsigned int occupancy;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	/*
9258c2ecf20Sopenharmony_ci	 * Rproc_serial does not yet support splice. To support splice
9268c2ecf20Sopenharmony_ci	 * pipe_to_sg() must allocate dma-buffers and copy content from
9278c2ecf20Sopenharmony_ci	 * regular pages to dma pages. And alloc_buf and free_buf must
9288c2ecf20Sopenharmony_ci	 * support allocating and freeing such a list of dma-buffers.
9298c2ecf20Sopenharmony_ci	 */
9308c2ecf20Sopenharmony_ci	if (is_rproc_serial(port->out_vq->vdev))
9318c2ecf20Sopenharmony_ci		return -EINVAL;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	pipe_lock(pipe);
9348c2ecf20Sopenharmony_ci	ret = 0;
9358c2ecf20Sopenharmony_ci	if (pipe_empty(pipe->head, pipe->tail))
9368c2ecf20Sopenharmony_ci		goto error_out;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK);
9398c2ecf20Sopenharmony_ci	if (ret < 0)
9408c2ecf20Sopenharmony_ci		goto error_out;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	occupancy = pipe_occupancy(pipe->head, pipe->tail);
9438c2ecf20Sopenharmony_ci	buf = alloc_buf(port->portdev->vdev, 0, occupancy);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	if (!buf) {
9468c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9478c2ecf20Sopenharmony_ci		goto error_out;
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	sgl.n = 0;
9518c2ecf20Sopenharmony_ci	sgl.len = 0;
9528c2ecf20Sopenharmony_ci	sgl.size = occupancy;
9538c2ecf20Sopenharmony_ci	sgl.sg = buf->sg;
9548c2ecf20Sopenharmony_ci	sg_init_table(sgl.sg, sgl.size);
9558c2ecf20Sopenharmony_ci	ret = __splice_from_pipe(pipe, &sd, pipe_to_sg);
9568c2ecf20Sopenharmony_ci	pipe_unlock(pipe);
9578c2ecf20Sopenharmony_ci	if (likely(ret > 0))
9588c2ecf20Sopenharmony_ci		ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	if (unlikely(ret <= 0))
9618c2ecf20Sopenharmony_ci		free_buf(buf, true);
9628c2ecf20Sopenharmony_ci	return ret;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_cierror_out:
9658c2ecf20Sopenharmony_ci	pipe_unlock(pipe);
9668c2ecf20Sopenharmony_ci	return ret;
9678c2ecf20Sopenharmony_ci}
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_cistatic __poll_t port_fops_poll(struct file *filp, poll_table *wait)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	struct port *port;
9728c2ecf20Sopenharmony_ci	__poll_t ret;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	port = filp->private_data;
9758c2ecf20Sopenharmony_ci	poll_wait(filp, &port->waitqueue, wait);
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	if (!port->guest_connected) {
9788c2ecf20Sopenharmony_ci		/* Port got unplugged */
9798c2ecf20Sopenharmony_ci		return EPOLLHUP;
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci	ret = 0;
9828c2ecf20Sopenharmony_ci	if (!will_read_block(port))
9838c2ecf20Sopenharmony_ci		ret |= EPOLLIN | EPOLLRDNORM;
9848c2ecf20Sopenharmony_ci	if (!will_write_block(port))
9858c2ecf20Sopenharmony_ci		ret |= EPOLLOUT;
9868c2ecf20Sopenharmony_ci	if (!port->host_connected)
9878c2ecf20Sopenharmony_ci		ret |= EPOLLHUP;
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	return ret;
9908c2ecf20Sopenharmony_ci}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_cistatic void remove_port(struct kref *kref);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic int port_fops_release(struct inode *inode, struct file *filp)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	struct port *port;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	port = filp->private_data;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	/* Notify host of port being closed */
10018c2ecf20Sopenharmony_ci	send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	spin_lock_irq(&port->inbuf_lock);
10048c2ecf20Sopenharmony_ci	port->guest_connected = false;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	discard_port_data(port);
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	spin_unlock_irq(&port->inbuf_lock);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	spin_lock_irq(&port->outvq_lock);
10118c2ecf20Sopenharmony_ci	reclaim_consumed_buffers(port);
10128c2ecf20Sopenharmony_ci	spin_unlock_irq(&port->outvq_lock);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	reclaim_dma_bufs();
10158c2ecf20Sopenharmony_ci	/*
10168c2ecf20Sopenharmony_ci	 * Locks aren't necessary here as a port can't be opened after
10178c2ecf20Sopenharmony_ci	 * unplug, and if a port isn't unplugged, a kref would already
10188c2ecf20Sopenharmony_ci	 * exist for the port.  Plus, taking ports_lock here would
10198c2ecf20Sopenharmony_ci	 * create a dependency on other locks taken by functions
10208c2ecf20Sopenharmony_ci	 * inside remove_port if we're the last holder of the port,
10218c2ecf20Sopenharmony_ci	 * creating many problems.
10228c2ecf20Sopenharmony_ci	 */
10238c2ecf20Sopenharmony_ci	kref_put(&port->kref, remove_port);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	return 0;
10268c2ecf20Sopenharmony_ci}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_cistatic int port_fops_open(struct inode *inode, struct file *filp)
10298c2ecf20Sopenharmony_ci{
10308c2ecf20Sopenharmony_ci	struct cdev *cdev = inode->i_cdev;
10318c2ecf20Sopenharmony_ci	struct port *port;
10328c2ecf20Sopenharmony_ci	int ret;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	/* We get the port with a kref here */
10358c2ecf20Sopenharmony_ci	port = find_port_by_devt(cdev->dev);
10368c2ecf20Sopenharmony_ci	if (!port) {
10378c2ecf20Sopenharmony_ci		/* Port was unplugged before we could proceed */
10388c2ecf20Sopenharmony_ci		return -ENXIO;
10398c2ecf20Sopenharmony_ci	}
10408c2ecf20Sopenharmony_ci	filp->private_data = port;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	/*
10438c2ecf20Sopenharmony_ci	 * Don't allow opening of console port devices -- that's done
10448c2ecf20Sopenharmony_ci	 * via /dev/hvc
10458c2ecf20Sopenharmony_ci	 */
10468c2ecf20Sopenharmony_ci	if (is_console_port(port)) {
10478c2ecf20Sopenharmony_ci		ret = -ENXIO;
10488c2ecf20Sopenharmony_ci		goto out;
10498c2ecf20Sopenharmony_ci	}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	/* Allow only one process to open a particular port at a time */
10528c2ecf20Sopenharmony_ci	spin_lock_irq(&port->inbuf_lock);
10538c2ecf20Sopenharmony_ci	if (port->guest_connected) {
10548c2ecf20Sopenharmony_ci		spin_unlock_irq(&port->inbuf_lock);
10558c2ecf20Sopenharmony_ci		ret = -EBUSY;
10568c2ecf20Sopenharmony_ci		goto out;
10578c2ecf20Sopenharmony_ci	}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	port->guest_connected = true;
10608c2ecf20Sopenharmony_ci	spin_unlock_irq(&port->inbuf_lock);
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	spin_lock_irq(&port->outvq_lock);
10638c2ecf20Sopenharmony_ci	/*
10648c2ecf20Sopenharmony_ci	 * There might be a chance that we missed reclaiming a few
10658c2ecf20Sopenharmony_ci	 * buffers in the window of the port getting previously closed
10668c2ecf20Sopenharmony_ci	 * and opening now.
10678c2ecf20Sopenharmony_ci	 */
10688c2ecf20Sopenharmony_ci	reclaim_consumed_buffers(port);
10698c2ecf20Sopenharmony_ci	spin_unlock_irq(&port->outvq_lock);
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	nonseekable_open(inode, filp);
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	/* Notify host of port being opened */
10748c2ecf20Sopenharmony_ci	send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	return 0;
10778c2ecf20Sopenharmony_ciout:
10788c2ecf20Sopenharmony_ci	kref_put(&port->kref, remove_port);
10798c2ecf20Sopenharmony_ci	return ret;
10808c2ecf20Sopenharmony_ci}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_cistatic int port_fops_fasync(int fd, struct file *filp, int mode)
10838c2ecf20Sopenharmony_ci{
10848c2ecf20Sopenharmony_ci	struct port *port;
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	port = filp->private_data;
10878c2ecf20Sopenharmony_ci	return fasync_helper(fd, filp, mode, &port->async_queue);
10888c2ecf20Sopenharmony_ci}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci/*
10918c2ecf20Sopenharmony_ci * The file operations that we support: programs in the guest can open
10928c2ecf20Sopenharmony_ci * a console device, read from it, write to it, poll for data and
10938c2ecf20Sopenharmony_ci * close it.  The devices are at
10948c2ecf20Sopenharmony_ci *   /dev/vport<device number>p<port number>
10958c2ecf20Sopenharmony_ci */
10968c2ecf20Sopenharmony_cistatic const struct file_operations port_fops = {
10978c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
10988c2ecf20Sopenharmony_ci	.open  = port_fops_open,
10998c2ecf20Sopenharmony_ci	.read  = port_fops_read,
11008c2ecf20Sopenharmony_ci	.write = port_fops_write,
11018c2ecf20Sopenharmony_ci	.splice_write = port_fops_splice_write,
11028c2ecf20Sopenharmony_ci	.poll  = port_fops_poll,
11038c2ecf20Sopenharmony_ci	.release = port_fops_release,
11048c2ecf20Sopenharmony_ci	.fasync = port_fops_fasync,
11058c2ecf20Sopenharmony_ci	.llseek = no_llseek,
11068c2ecf20Sopenharmony_ci};
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci/*
11098c2ecf20Sopenharmony_ci * The put_chars() callback is pretty straightforward.
11108c2ecf20Sopenharmony_ci *
11118c2ecf20Sopenharmony_ci * We turn the characters into a scatter-gather list, add it to the
11128c2ecf20Sopenharmony_ci * output queue and then kick the Host.  Then we sit here waiting for
11138c2ecf20Sopenharmony_ci * it to finish: inefficient in theory, but in practice
11148c2ecf20Sopenharmony_ci * implementations will do it immediately.
11158c2ecf20Sopenharmony_ci */
11168c2ecf20Sopenharmony_cistatic int put_chars(u32 vtermno, const char *buf, int count)
11178c2ecf20Sopenharmony_ci{
11188c2ecf20Sopenharmony_ci	struct port *port;
11198c2ecf20Sopenharmony_ci	struct scatterlist sg[1];
11208c2ecf20Sopenharmony_ci	void *data;
11218c2ecf20Sopenharmony_ci	int ret;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	if (unlikely(early_put_chars))
11248c2ecf20Sopenharmony_ci		return early_put_chars(vtermno, buf, count);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	port = find_port_by_vtermno(vtermno);
11278c2ecf20Sopenharmony_ci	if (!port)
11288c2ecf20Sopenharmony_ci		return -EPIPE;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	data = kmemdup(buf, count, GFP_ATOMIC);
11318c2ecf20Sopenharmony_ci	if (!data)
11328c2ecf20Sopenharmony_ci		return -ENOMEM;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	sg_init_one(sg, data, count);
11358c2ecf20Sopenharmony_ci	ret = __send_to_port(port, sg, 1, count, data, false);
11368c2ecf20Sopenharmony_ci	kfree(data);
11378c2ecf20Sopenharmony_ci	return ret;
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci/*
11418c2ecf20Sopenharmony_ci * get_chars() is the callback from the hvc_console infrastructure
11428c2ecf20Sopenharmony_ci * when an interrupt is received.
11438c2ecf20Sopenharmony_ci *
11448c2ecf20Sopenharmony_ci * We call out to fill_readbuf that gets us the required data from the
11458c2ecf20Sopenharmony_ci * buffers that are queued up.
11468c2ecf20Sopenharmony_ci */
11478c2ecf20Sopenharmony_cistatic int get_chars(u32 vtermno, char *buf, int count)
11488c2ecf20Sopenharmony_ci{
11498c2ecf20Sopenharmony_ci	struct port *port;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	/* If we've not set up the port yet, we have no input to give. */
11528c2ecf20Sopenharmony_ci	if (unlikely(early_put_chars))
11538c2ecf20Sopenharmony_ci		return 0;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	port = find_port_by_vtermno(vtermno);
11568c2ecf20Sopenharmony_ci	if (!port)
11578c2ecf20Sopenharmony_ci		return -EPIPE;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	/* If we don't have an input queue yet, we can't get input. */
11608c2ecf20Sopenharmony_ci	BUG_ON(!port->in_vq);
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	return fill_readbuf(port, (__force char __user *)buf, count, false);
11638c2ecf20Sopenharmony_ci}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_cistatic void resize_console(struct port *port)
11668c2ecf20Sopenharmony_ci{
11678c2ecf20Sopenharmony_ci	struct virtio_device *vdev;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	/* The port could have been hot-unplugged */
11708c2ecf20Sopenharmony_ci	if (!port || !is_console_port(port))
11718c2ecf20Sopenharmony_ci		return;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	vdev = port->portdev->vdev;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	/* Don't test F_SIZE at all if we're rproc: not a valid feature! */
11768c2ecf20Sopenharmony_ci	if (!is_rproc_serial(vdev) &&
11778c2ecf20Sopenharmony_ci	    virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE))
11788c2ecf20Sopenharmony_ci		hvc_resize(port->cons.hvc, port->cons.ws);
11798c2ecf20Sopenharmony_ci}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci/* We set the configuration at this point, since we now have a tty */
11828c2ecf20Sopenharmony_cistatic int notifier_add_vio(struct hvc_struct *hp, int data)
11838c2ecf20Sopenharmony_ci{
11848c2ecf20Sopenharmony_ci	struct port *port;
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	port = find_port_by_vtermno(hp->vtermno);
11878c2ecf20Sopenharmony_ci	if (!port)
11888c2ecf20Sopenharmony_ci		return -EINVAL;
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	hp->irq_requested = 1;
11918c2ecf20Sopenharmony_ci	resize_console(port);
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	return 0;
11948c2ecf20Sopenharmony_ci}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_cistatic void notifier_del_vio(struct hvc_struct *hp, int data)
11978c2ecf20Sopenharmony_ci{
11988c2ecf20Sopenharmony_ci	hp->irq_requested = 0;
11998c2ecf20Sopenharmony_ci}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci/* The operations for console ports. */
12028c2ecf20Sopenharmony_cistatic const struct hv_ops hv_ops = {
12038c2ecf20Sopenharmony_ci	.get_chars = get_chars,
12048c2ecf20Sopenharmony_ci	.put_chars = put_chars,
12058c2ecf20Sopenharmony_ci	.notifier_add = notifier_add_vio,
12068c2ecf20Sopenharmony_ci	.notifier_del = notifier_del_vio,
12078c2ecf20Sopenharmony_ci	.notifier_hangup = notifier_del_vio,
12088c2ecf20Sopenharmony_ci};
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci/*
12118c2ecf20Sopenharmony_ci * Console drivers are initialized very early so boot messages can go
12128c2ecf20Sopenharmony_ci * out, so we do things slightly differently from the generic virtio
12138c2ecf20Sopenharmony_ci * initialization of the net and block drivers.
12148c2ecf20Sopenharmony_ci *
12158c2ecf20Sopenharmony_ci * At this stage, the console is output-only.  It's too early to set
12168c2ecf20Sopenharmony_ci * up a virtqueue, so we let the drivers do some boutique early-output
12178c2ecf20Sopenharmony_ci * thing.
12188c2ecf20Sopenharmony_ci */
12198c2ecf20Sopenharmony_ciint __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
12208c2ecf20Sopenharmony_ci{
12218c2ecf20Sopenharmony_ci	early_put_chars = put_chars;
12228c2ecf20Sopenharmony_ci	return hvc_instantiate(0, 0, &hv_ops);
12238c2ecf20Sopenharmony_ci}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_cistatic int init_port_console(struct port *port)
12268c2ecf20Sopenharmony_ci{
12278c2ecf20Sopenharmony_ci	int ret;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	/*
12308c2ecf20Sopenharmony_ci	 * The Host's telling us this port is a console port.  Hook it
12318c2ecf20Sopenharmony_ci	 * up with an hvc console.
12328c2ecf20Sopenharmony_ci	 *
12338c2ecf20Sopenharmony_ci	 * To set up and manage our virtual console, we call
12348c2ecf20Sopenharmony_ci	 * hvc_alloc().
12358c2ecf20Sopenharmony_ci	 *
12368c2ecf20Sopenharmony_ci	 * The first argument of hvc_alloc() is the virtual console
12378c2ecf20Sopenharmony_ci	 * number.  The second argument is the parameter for the
12388c2ecf20Sopenharmony_ci	 * notification mechanism (like irq number).  We currently
12398c2ecf20Sopenharmony_ci	 * leave this as zero, virtqueues have implicit notifications.
12408c2ecf20Sopenharmony_ci	 *
12418c2ecf20Sopenharmony_ci	 * The third argument is a "struct hv_ops" containing the
12428c2ecf20Sopenharmony_ci	 * put_chars() get_chars(), notifier_add() and notifier_del()
12438c2ecf20Sopenharmony_ci	 * pointers.  The final argument is the output buffer size: we
12448c2ecf20Sopenharmony_ci	 * can do any size, so we put PAGE_SIZE here.
12458c2ecf20Sopenharmony_ci	 */
12468c2ecf20Sopenharmony_ci	port->cons.vtermno = pdrvdata.next_vtermno;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE);
12498c2ecf20Sopenharmony_ci	if (IS_ERR(port->cons.hvc)) {
12508c2ecf20Sopenharmony_ci		ret = PTR_ERR(port->cons.hvc);
12518c2ecf20Sopenharmony_ci		dev_err(port->dev,
12528c2ecf20Sopenharmony_ci			"error %d allocating hvc for port\n", ret);
12538c2ecf20Sopenharmony_ci		port->cons.hvc = NULL;
12548c2ecf20Sopenharmony_ci		return ret;
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci	spin_lock_irq(&pdrvdata_lock);
12578c2ecf20Sopenharmony_ci	pdrvdata.next_vtermno++;
12588c2ecf20Sopenharmony_ci	list_add_tail(&port->cons.list, &pdrvdata.consoles);
12598c2ecf20Sopenharmony_ci	spin_unlock_irq(&pdrvdata_lock);
12608c2ecf20Sopenharmony_ci	port->guest_connected = true;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	/*
12638c2ecf20Sopenharmony_ci	 * Start using the new console output if this is the first
12648c2ecf20Sopenharmony_ci	 * console to come up.
12658c2ecf20Sopenharmony_ci	 */
12668c2ecf20Sopenharmony_ci	if (early_put_chars)
12678c2ecf20Sopenharmony_ci		early_put_chars = NULL;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	/* Notify host of port being opened */
12708c2ecf20Sopenharmony_ci	send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	return 0;
12738c2ecf20Sopenharmony_ci}
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_cistatic ssize_t show_port_name(struct device *dev,
12768c2ecf20Sopenharmony_ci			      struct device_attribute *attr, char *buffer)
12778c2ecf20Sopenharmony_ci{
12788c2ecf20Sopenharmony_ci	struct port *port;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	port = dev_get_drvdata(dev);
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	return sprintf(buffer, "%s\n", port->name);
12838c2ecf20Sopenharmony_ci}
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, S_IRUGO, show_port_name, NULL);
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_cistatic struct attribute *port_sysfs_entries[] = {
12888c2ecf20Sopenharmony_ci	&dev_attr_name.attr,
12898c2ecf20Sopenharmony_ci	NULL
12908c2ecf20Sopenharmony_ci};
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_cistatic const struct attribute_group port_attribute_group = {
12938c2ecf20Sopenharmony_ci	.name = NULL,		/* put in device directory */
12948c2ecf20Sopenharmony_ci	.attrs = port_sysfs_entries,
12958c2ecf20Sopenharmony_ci};
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_cistatic int port_debugfs_show(struct seq_file *s, void *data)
12988c2ecf20Sopenharmony_ci{
12998c2ecf20Sopenharmony_ci	struct port *port = s->private;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	seq_printf(s, "name: %s\n", port->name ? port->name : "");
13028c2ecf20Sopenharmony_ci	seq_printf(s, "guest_connected: %d\n", port->guest_connected);
13038c2ecf20Sopenharmony_ci	seq_printf(s, "host_connected: %d\n", port->host_connected);
13048c2ecf20Sopenharmony_ci	seq_printf(s, "outvq_full: %d\n", port->outvq_full);
13058c2ecf20Sopenharmony_ci	seq_printf(s, "bytes_sent: %lu\n", port->stats.bytes_sent);
13068c2ecf20Sopenharmony_ci	seq_printf(s, "bytes_received: %lu\n", port->stats.bytes_received);
13078c2ecf20Sopenharmony_ci	seq_printf(s, "bytes_discarded: %lu\n", port->stats.bytes_discarded);
13088c2ecf20Sopenharmony_ci	seq_printf(s, "is_console: %s\n",
13098c2ecf20Sopenharmony_ci		   is_console_port(port) ? "yes" : "no");
13108c2ecf20Sopenharmony_ci	seq_printf(s, "console_vtermno: %u\n", port->cons.vtermno);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	return 0;
13138c2ecf20Sopenharmony_ci}
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(port_debugfs);
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_cistatic void set_console_size(struct port *port, u16 rows, u16 cols)
13188c2ecf20Sopenharmony_ci{
13198c2ecf20Sopenharmony_ci	if (!port || !is_console_port(port))
13208c2ecf20Sopenharmony_ci		return;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	port->cons.ws.ws_row = rows;
13238c2ecf20Sopenharmony_ci	port->cons.ws.ws_col = cols;
13248c2ecf20Sopenharmony_ci}
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_cistatic int fill_queue(struct virtqueue *vq, spinlock_t *lock)
13278c2ecf20Sopenharmony_ci{
13288c2ecf20Sopenharmony_ci	struct port_buffer *buf;
13298c2ecf20Sopenharmony_ci	int nr_added_bufs;
13308c2ecf20Sopenharmony_ci	int ret;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	nr_added_bufs = 0;
13338c2ecf20Sopenharmony_ci	do {
13348c2ecf20Sopenharmony_ci		buf = alloc_buf(vq->vdev, PAGE_SIZE, 0);
13358c2ecf20Sopenharmony_ci		if (!buf)
13368c2ecf20Sopenharmony_ci			return -ENOMEM;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci		spin_lock_irq(lock);
13398c2ecf20Sopenharmony_ci		ret = add_inbuf(vq, buf);
13408c2ecf20Sopenharmony_ci		if (ret < 0) {
13418c2ecf20Sopenharmony_ci			spin_unlock_irq(lock);
13428c2ecf20Sopenharmony_ci			free_buf(buf, true);
13438c2ecf20Sopenharmony_ci			return ret;
13448c2ecf20Sopenharmony_ci		}
13458c2ecf20Sopenharmony_ci		nr_added_bufs++;
13468c2ecf20Sopenharmony_ci		spin_unlock_irq(lock);
13478c2ecf20Sopenharmony_ci	} while (ret > 0);
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	return nr_added_bufs;
13508c2ecf20Sopenharmony_ci}
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_cistatic void send_sigio_to_port(struct port *port)
13538c2ecf20Sopenharmony_ci{
13548c2ecf20Sopenharmony_ci	if (port->async_queue && port->guest_connected)
13558c2ecf20Sopenharmony_ci		kill_fasync(&port->async_queue, SIGIO, POLL_OUT);
13568c2ecf20Sopenharmony_ci}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_cistatic int add_port(struct ports_device *portdev, u32 id)
13598c2ecf20Sopenharmony_ci{
13608c2ecf20Sopenharmony_ci	char debugfs_name[16];
13618c2ecf20Sopenharmony_ci	struct port *port;
13628c2ecf20Sopenharmony_ci	dev_t devt;
13638c2ecf20Sopenharmony_ci	int err;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	port = kmalloc(sizeof(*port), GFP_KERNEL);
13668c2ecf20Sopenharmony_ci	if (!port) {
13678c2ecf20Sopenharmony_ci		err = -ENOMEM;
13688c2ecf20Sopenharmony_ci		goto fail;
13698c2ecf20Sopenharmony_ci	}
13708c2ecf20Sopenharmony_ci	kref_init(&port->kref);
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	port->portdev = portdev;
13738c2ecf20Sopenharmony_ci	port->id = id;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	port->name = NULL;
13768c2ecf20Sopenharmony_ci	port->inbuf = NULL;
13778c2ecf20Sopenharmony_ci	port->cons.hvc = NULL;
13788c2ecf20Sopenharmony_ci	port->async_queue = NULL;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	port->cons.ws.ws_row = port->cons.ws.ws_col = 0;
13818c2ecf20Sopenharmony_ci	port->cons.vtermno = 0;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	port->host_connected = port->guest_connected = false;
13848c2ecf20Sopenharmony_ci	port->stats = (struct port_stats) { 0 };
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	port->outvq_full = false;
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	port->in_vq = portdev->in_vqs[port->id];
13898c2ecf20Sopenharmony_ci	port->out_vq = portdev->out_vqs[port->id];
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	port->cdev = cdev_alloc();
13928c2ecf20Sopenharmony_ci	if (!port->cdev) {
13938c2ecf20Sopenharmony_ci		dev_err(&port->portdev->vdev->dev, "Error allocating cdev\n");
13948c2ecf20Sopenharmony_ci		err = -ENOMEM;
13958c2ecf20Sopenharmony_ci		goto free_port;
13968c2ecf20Sopenharmony_ci	}
13978c2ecf20Sopenharmony_ci	port->cdev->ops = &port_fops;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	devt = MKDEV(portdev->chr_major, id);
14008c2ecf20Sopenharmony_ci	err = cdev_add(port->cdev, devt, 1);
14018c2ecf20Sopenharmony_ci	if (err < 0) {
14028c2ecf20Sopenharmony_ci		dev_err(&port->portdev->vdev->dev,
14038c2ecf20Sopenharmony_ci			"Error %d adding cdev for port %u\n", err, id);
14048c2ecf20Sopenharmony_ci		goto free_cdev;
14058c2ecf20Sopenharmony_ci	}
14068c2ecf20Sopenharmony_ci	port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev,
14078c2ecf20Sopenharmony_ci				  devt, port, "vport%up%u",
14088c2ecf20Sopenharmony_ci				  port->portdev->vdev->index, id);
14098c2ecf20Sopenharmony_ci	if (IS_ERR(port->dev)) {
14108c2ecf20Sopenharmony_ci		err = PTR_ERR(port->dev);
14118c2ecf20Sopenharmony_ci		dev_err(&port->portdev->vdev->dev,
14128c2ecf20Sopenharmony_ci			"Error %d creating device for port %u\n",
14138c2ecf20Sopenharmony_ci			err, id);
14148c2ecf20Sopenharmony_ci		goto free_cdev;
14158c2ecf20Sopenharmony_ci	}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	spin_lock_init(&port->inbuf_lock);
14188c2ecf20Sopenharmony_ci	spin_lock_init(&port->outvq_lock);
14198c2ecf20Sopenharmony_ci	init_waitqueue_head(&port->waitqueue);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	/* We can safely ignore ENOSPC because it means
14228c2ecf20Sopenharmony_ci	 * the queue already has buffers. Buffers are removed
14238c2ecf20Sopenharmony_ci	 * only by virtcons_remove(), not by unplug_port()
14248c2ecf20Sopenharmony_ci	 */
14258c2ecf20Sopenharmony_ci	err = fill_queue(port->in_vq, &port->inbuf_lock);
14268c2ecf20Sopenharmony_ci	if (err < 0 && err != -ENOSPC) {
14278c2ecf20Sopenharmony_ci		dev_err(port->dev, "Error allocating inbufs\n");
14288c2ecf20Sopenharmony_ci		goto free_device;
14298c2ecf20Sopenharmony_ci	}
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	if (is_rproc_serial(port->portdev->vdev))
14328c2ecf20Sopenharmony_ci		/*
14338c2ecf20Sopenharmony_ci		 * For rproc_serial assume remote processor is connected.
14348c2ecf20Sopenharmony_ci		 * rproc_serial does not want the console port, only
14358c2ecf20Sopenharmony_ci		 * the generic port implementation.
14368c2ecf20Sopenharmony_ci		 */
14378c2ecf20Sopenharmony_ci		port->host_connected = true;
14388c2ecf20Sopenharmony_ci	else if (!use_multiport(port->portdev)) {
14398c2ecf20Sopenharmony_ci		/*
14408c2ecf20Sopenharmony_ci		 * If we're not using multiport support,
14418c2ecf20Sopenharmony_ci		 * this has to be a console port.
14428c2ecf20Sopenharmony_ci		 */
14438c2ecf20Sopenharmony_ci		err = init_port_console(port);
14448c2ecf20Sopenharmony_ci		if (err)
14458c2ecf20Sopenharmony_ci			goto free_inbufs;
14468c2ecf20Sopenharmony_ci	}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	spin_lock_irq(&portdev->ports_lock);
14498c2ecf20Sopenharmony_ci	list_add_tail(&port->list, &port->portdev->ports);
14508c2ecf20Sopenharmony_ci	spin_unlock_irq(&portdev->ports_lock);
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	/*
14538c2ecf20Sopenharmony_ci	 * Tell the Host we're set so that it can send us various
14548c2ecf20Sopenharmony_ci	 * configuration parameters for this port (eg, port name,
14558c2ecf20Sopenharmony_ci	 * caching, whether this is a console port, etc.)
14568c2ecf20Sopenharmony_ci	 */
14578c2ecf20Sopenharmony_ci	send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	if (pdrvdata.debugfs_dir) {
14608c2ecf20Sopenharmony_ci		/*
14618c2ecf20Sopenharmony_ci		 * Finally, create the debugfs file that we can use to
14628c2ecf20Sopenharmony_ci		 * inspect a port's state at any time
14638c2ecf20Sopenharmony_ci		 */
14648c2ecf20Sopenharmony_ci		snprintf(debugfs_name, sizeof(debugfs_name), "vport%up%u",
14658c2ecf20Sopenharmony_ci			 port->portdev->vdev->index, id);
14668c2ecf20Sopenharmony_ci		port->debugfs_file = debugfs_create_file(debugfs_name, 0444,
14678c2ecf20Sopenharmony_ci							 pdrvdata.debugfs_dir,
14688c2ecf20Sopenharmony_ci							 port,
14698c2ecf20Sopenharmony_ci							 &port_debugfs_fops);
14708c2ecf20Sopenharmony_ci	}
14718c2ecf20Sopenharmony_ci	return 0;
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_cifree_inbufs:
14748c2ecf20Sopenharmony_cifree_device:
14758c2ecf20Sopenharmony_ci	device_destroy(pdrvdata.class, port->dev->devt);
14768c2ecf20Sopenharmony_cifree_cdev:
14778c2ecf20Sopenharmony_ci	cdev_del(port->cdev);
14788c2ecf20Sopenharmony_cifree_port:
14798c2ecf20Sopenharmony_ci	kfree(port);
14808c2ecf20Sopenharmony_cifail:
14818c2ecf20Sopenharmony_ci	/* The host might want to notify management sw about port add failure */
14828c2ecf20Sopenharmony_ci	__send_control_msg(portdev, id, VIRTIO_CONSOLE_PORT_READY, 0);
14838c2ecf20Sopenharmony_ci	return err;
14848c2ecf20Sopenharmony_ci}
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci/* No users remain, remove all port-specific data. */
14878c2ecf20Sopenharmony_cistatic void remove_port(struct kref *kref)
14888c2ecf20Sopenharmony_ci{
14898c2ecf20Sopenharmony_ci	struct port *port;
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	port = container_of(kref, struct port, kref);
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	kfree(port);
14948c2ecf20Sopenharmony_ci}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_cistatic void remove_port_data(struct port *port)
14978c2ecf20Sopenharmony_ci{
14988c2ecf20Sopenharmony_ci	spin_lock_irq(&port->inbuf_lock);
14998c2ecf20Sopenharmony_ci	/* Remove unused data this port might have received. */
15008c2ecf20Sopenharmony_ci	discard_port_data(port);
15018c2ecf20Sopenharmony_ci	spin_unlock_irq(&port->inbuf_lock);
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	spin_lock_irq(&port->outvq_lock);
15048c2ecf20Sopenharmony_ci	reclaim_consumed_buffers(port);
15058c2ecf20Sopenharmony_ci	spin_unlock_irq(&port->outvq_lock);
15068c2ecf20Sopenharmony_ci}
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci/*
15098c2ecf20Sopenharmony_ci * Port got unplugged.  Remove port from portdev's list and drop the
15108c2ecf20Sopenharmony_ci * kref reference.  If no userspace has this port opened, it will
15118c2ecf20Sopenharmony_ci * result in immediate removal the port.
15128c2ecf20Sopenharmony_ci */
15138c2ecf20Sopenharmony_cistatic void unplug_port(struct port *port)
15148c2ecf20Sopenharmony_ci{
15158c2ecf20Sopenharmony_ci	spin_lock_irq(&port->portdev->ports_lock);
15168c2ecf20Sopenharmony_ci	list_del(&port->list);
15178c2ecf20Sopenharmony_ci	spin_unlock_irq(&port->portdev->ports_lock);
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	spin_lock_irq(&port->inbuf_lock);
15208c2ecf20Sopenharmony_ci	if (port->guest_connected) {
15218c2ecf20Sopenharmony_ci		/* Let the app know the port is going down. */
15228c2ecf20Sopenharmony_ci		send_sigio_to_port(port);
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci		/* Do this after sigio is actually sent */
15258c2ecf20Sopenharmony_ci		port->guest_connected = false;
15268c2ecf20Sopenharmony_ci		port->host_connected = false;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci		wake_up_interruptible(&port->waitqueue);
15298c2ecf20Sopenharmony_ci	}
15308c2ecf20Sopenharmony_ci	spin_unlock_irq(&port->inbuf_lock);
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	if (is_console_port(port)) {
15338c2ecf20Sopenharmony_ci		spin_lock_irq(&pdrvdata_lock);
15348c2ecf20Sopenharmony_ci		list_del(&port->cons.list);
15358c2ecf20Sopenharmony_ci		spin_unlock_irq(&pdrvdata_lock);
15368c2ecf20Sopenharmony_ci		hvc_remove(port->cons.hvc);
15378c2ecf20Sopenharmony_ci	}
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	remove_port_data(port);
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	/*
15428c2ecf20Sopenharmony_ci	 * We should just assume the device itself has gone off --
15438c2ecf20Sopenharmony_ci	 * else a close on an open port later will try to send out a
15448c2ecf20Sopenharmony_ci	 * control message.
15458c2ecf20Sopenharmony_ci	 */
15468c2ecf20Sopenharmony_ci	port->portdev = NULL;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
15498c2ecf20Sopenharmony_ci	device_destroy(pdrvdata.class, port->dev->devt);
15508c2ecf20Sopenharmony_ci	cdev_del(port->cdev);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	debugfs_remove(port->debugfs_file);
15538c2ecf20Sopenharmony_ci	kfree(port->name);
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	/*
15568c2ecf20Sopenharmony_ci	 * Locks around here are not necessary - a port can't be
15578c2ecf20Sopenharmony_ci	 * opened after we removed the port struct from ports_list
15588c2ecf20Sopenharmony_ci	 * above.
15598c2ecf20Sopenharmony_ci	 */
15608c2ecf20Sopenharmony_ci	kref_put(&port->kref, remove_port);
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci/* Any private messages that the Host and Guest want to share */
15648c2ecf20Sopenharmony_cistatic void handle_control_message(struct virtio_device *vdev,
15658c2ecf20Sopenharmony_ci				   struct ports_device *portdev,
15668c2ecf20Sopenharmony_ci				   struct port_buffer *buf)
15678c2ecf20Sopenharmony_ci{
15688c2ecf20Sopenharmony_ci	struct virtio_console_control *cpkt;
15698c2ecf20Sopenharmony_ci	struct port *port;
15708c2ecf20Sopenharmony_ci	size_t name_size;
15718c2ecf20Sopenharmony_ci	int err;
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	cpkt = (struct virtio_console_control *)(buf->buf + buf->offset);
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	port = find_port_by_id(portdev, virtio32_to_cpu(vdev, cpkt->id));
15768c2ecf20Sopenharmony_ci	if (!port &&
15778c2ecf20Sopenharmony_ci	    cpkt->event != cpu_to_virtio16(vdev, VIRTIO_CONSOLE_PORT_ADD)) {
15788c2ecf20Sopenharmony_ci		/* No valid header at start of buffer.  Drop it. */
15798c2ecf20Sopenharmony_ci		dev_dbg(&portdev->vdev->dev,
15808c2ecf20Sopenharmony_ci			"Invalid index %u in control packet\n", cpkt->id);
15818c2ecf20Sopenharmony_ci		return;
15828c2ecf20Sopenharmony_ci	}
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	switch (virtio16_to_cpu(vdev, cpkt->event)) {
15858c2ecf20Sopenharmony_ci	case VIRTIO_CONSOLE_PORT_ADD:
15868c2ecf20Sopenharmony_ci		if (port) {
15878c2ecf20Sopenharmony_ci			dev_dbg(&portdev->vdev->dev,
15888c2ecf20Sopenharmony_ci				"Port %u already added\n", port->id);
15898c2ecf20Sopenharmony_ci			send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
15908c2ecf20Sopenharmony_ci			break;
15918c2ecf20Sopenharmony_ci		}
15928c2ecf20Sopenharmony_ci		if (virtio32_to_cpu(vdev, cpkt->id) >=
15938c2ecf20Sopenharmony_ci		    portdev->max_nr_ports) {
15948c2ecf20Sopenharmony_ci			dev_warn(&portdev->vdev->dev,
15958c2ecf20Sopenharmony_ci				"Request for adding port with "
15968c2ecf20Sopenharmony_ci				"out-of-bound id %u, max. supported id: %u\n",
15978c2ecf20Sopenharmony_ci				cpkt->id, portdev->max_nr_ports - 1);
15988c2ecf20Sopenharmony_ci			break;
15998c2ecf20Sopenharmony_ci		}
16008c2ecf20Sopenharmony_ci		add_port(portdev, virtio32_to_cpu(vdev, cpkt->id));
16018c2ecf20Sopenharmony_ci		break;
16028c2ecf20Sopenharmony_ci	case VIRTIO_CONSOLE_PORT_REMOVE:
16038c2ecf20Sopenharmony_ci		unplug_port(port);
16048c2ecf20Sopenharmony_ci		break;
16058c2ecf20Sopenharmony_ci	case VIRTIO_CONSOLE_CONSOLE_PORT:
16068c2ecf20Sopenharmony_ci		if (!cpkt->value)
16078c2ecf20Sopenharmony_ci			break;
16088c2ecf20Sopenharmony_ci		if (is_console_port(port))
16098c2ecf20Sopenharmony_ci			break;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci		init_port_console(port);
16128c2ecf20Sopenharmony_ci		complete(&early_console_added);
16138c2ecf20Sopenharmony_ci		/*
16148c2ecf20Sopenharmony_ci		 * Could remove the port here in case init fails - but
16158c2ecf20Sopenharmony_ci		 * have to notify the host first.
16168c2ecf20Sopenharmony_ci		 */
16178c2ecf20Sopenharmony_ci		break;
16188c2ecf20Sopenharmony_ci	case VIRTIO_CONSOLE_RESIZE: {
16198c2ecf20Sopenharmony_ci		struct {
16208c2ecf20Sopenharmony_ci			__u16 rows;
16218c2ecf20Sopenharmony_ci			__u16 cols;
16228c2ecf20Sopenharmony_ci		} size;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci		if (!is_console_port(port))
16258c2ecf20Sopenharmony_ci			break;
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci		memcpy(&size, buf->buf + buf->offset + sizeof(*cpkt),
16288c2ecf20Sopenharmony_ci		       sizeof(size));
16298c2ecf20Sopenharmony_ci		set_console_size(port, size.rows, size.cols);
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci		port->cons.hvc->irq_requested = 1;
16328c2ecf20Sopenharmony_ci		resize_console(port);
16338c2ecf20Sopenharmony_ci		break;
16348c2ecf20Sopenharmony_ci	}
16358c2ecf20Sopenharmony_ci	case VIRTIO_CONSOLE_PORT_OPEN:
16368c2ecf20Sopenharmony_ci		port->host_connected = virtio16_to_cpu(vdev, cpkt->value);
16378c2ecf20Sopenharmony_ci		wake_up_interruptible(&port->waitqueue);
16388c2ecf20Sopenharmony_ci		/*
16398c2ecf20Sopenharmony_ci		 * If the host port got closed and the host had any
16408c2ecf20Sopenharmony_ci		 * unconsumed buffers, we'll be able to reclaim them
16418c2ecf20Sopenharmony_ci		 * now.
16428c2ecf20Sopenharmony_ci		 */
16438c2ecf20Sopenharmony_ci		spin_lock_irq(&port->outvq_lock);
16448c2ecf20Sopenharmony_ci		reclaim_consumed_buffers(port);
16458c2ecf20Sopenharmony_ci		spin_unlock_irq(&port->outvq_lock);
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci		/*
16488c2ecf20Sopenharmony_ci		 * If the guest is connected, it'll be interested in
16498c2ecf20Sopenharmony_ci		 * knowing the host connection state changed.
16508c2ecf20Sopenharmony_ci		 */
16518c2ecf20Sopenharmony_ci		spin_lock_irq(&port->inbuf_lock);
16528c2ecf20Sopenharmony_ci		send_sigio_to_port(port);
16538c2ecf20Sopenharmony_ci		spin_unlock_irq(&port->inbuf_lock);
16548c2ecf20Sopenharmony_ci		break;
16558c2ecf20Sopenharmony_ci	case VIRTIO_CONSOLE_PORT_NAME:
16568c2ecf20Sopenharmony_ci		/*
16578c2ecf20Sopenharmony_ci		 * If we woke up after hibernation, we can get this
16588c2ecf20Sopenharmony_ci		 * again.  Skip it in that case.
16598c2ecf20Sopenharmony_ci		 */
16608c2ecf20Sopenharmony_ci		if (port->name)
16618c2ecf20Sopenharmony_ci			break;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci		/*
16648c2ecf20Sopenharmony_ci		 * Skip the size of the header and the cpkt to get the size
16658c2ecf20Sopenharmony_ci		 * of the name that was sent
16668c2ecf20Sopenharmony_ci		 */
16678c2ecf20Sopenharmony_ci		name_size = buf->len - buf->offset - sizeof(*cpkt) + 1;
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci		port->name = kmalloc(name_size, GFP_KERNEL);
16708c2ecf20Sopenharmony_ci		if (!port->name) {
16718c2ecf20Sopenharmony_ci			dev_err(port->dev,
16728c2ecf20Sopenharmony_ci				"Not enough space to store port name\n");
16738c2ecf20Sopenharmony_ci			break;
16748c2ecf20Sopenharmony_ci		}
16758c2ecf20Sopenharmony_ci		strncpy(port->name, buf->buf + buf->offset + sizeof(*cpkt),
16768c2ecf20Sopenharmony_ci			name_size - 1);
16778c2ecf20Sopenharmony_ci		port->name[name_size - 1] = 0;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci		/*
16808c2ecf20Sopenharmony_ci		 * Since we only have one sysfs attribute, 'name',
16818c2ecf20Sopenharmony_ci		 * create it only if we have a name for the port.
16828c2ecf20Sopenharmony_ci		 */
16838c2ecf20Sopenharmony_ci		err = sysfs_create_group(&port->dev->kobj,
16848c2ecf20Sopenharmony_ci					 &port_attribute_group);
16858c2ecf20Sopenharmony_ci		if (err) {
16868c2ecf20Sopenharmony_ci			dev_err(port->dev,
16878c2ecf20Sopenharmony_ci				"Error %d creating sysfs device attributes\n",
16888c2ecf20Sopenharmony_ci				err);
16898c2ecf20Sopenharmony_ci		} else {
16908c2ecf20Sopenharmony_ci			/*
16918c2ecf20Sopenharmony_ci			 * Generate a udev event so that appropriate
16928c2ecf20Sopenharmony_ci			 * symlinks can be created based on udev
16938c2ecf20Sopenharmony_ci			 * rules.
16948c2ecf20Sopenharmony_ci			 */
16958c2ecf20Sopenharmony_ci			kobject_uevent(&port->dev->kobj, KOBJ_CHANGE);
16968c2ecf20Sopenharmony_ci		}
16978c2ecf20Sopenharmony_ci		break;
16988c2ecf20Sopenharmony_ci	}
16998c2ecf20Sopenharmony_ci}
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_cistatic void control_work_handler(struct work_struct *work)
17028c2ecf20Sopenharmony_ci{
17038c2ecf20Sopenharmony_ci	struct ports_device *portdev;
17048c2ecf20Sopenharmony_ci	struct virtqueue *vq;
17058c2ecf20Sopenharmony_ci	struct port_buffer *buf;
17068c2ecf20Sopenharmony_ci	unsigned int len;
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	portdev = container_of(work, struct ports_device, control_work);
17098c2ecf20Sopenharmony_ci	vq = portdev->c_ivq;
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci	spin_lock(&portdev->c_ivq_lock);
17128c2ecf20Sopenharmony_ci	while ((buf = virtqueue_get_buf(vq, &len))) {
17138c2ecf20Sopenharmony_ci		spin_unlock(&portdev->c_ivq_lock);
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci		buf->len = min_t(size_t, len, buf->size);
17168c2ecf20Sopenharmony_ci		buf->offset = 0;
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci		handle_control_message(vq->vdev, portdev, buf);
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci		spin_lock(&portdev->c_ivq_lock);
17218c2ecf20Sopenharmony_ci		if (add_inbuf(portdev->c_ivq, buf) < 0) {
17228c2ecf20Sopenharmony_ci			dev_warn(&portdev->vdev->dev,
17238c2ecf20Sopenharmony_ci				 "Error adding buffer to queue\n");
17248c2ecf20Sopenharmony_ci			free_buf(buf, false);
17258c2ecf20Sopenharmony_ci		}
17268c2ecf20Sopenharmony_ci	}
17278c2ecf20Sopenharmony_ci	spin_unlock(&portdev->c_ivq_lock);
17288c2ecf20Sopenharmony_ci}
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_cistatic void flush_bufs(struct virtqueue *vq, bool can_sleep)
17318c2ecf20Sopenharmony_ci{
17328c2ecf20Sopenharmony_ci	struct port_buffer *buf;
17338c2ecf20Sopenharmony_ci	unsigned int len;
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	while ((buf = virtqueue_get_buf(vq, &len)))
17368c2ecf20Sopenharmony_ci		free_buf(buf, can_sleep);
17378c2ecf20Sopenharmony_ci}
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_cistatic void out_intr(struct virtqueue *vq)
17408c2ecf20Sopenharmony_ci{
17418c2ecf20Sopenharmony_ci	struct port *port;
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	port = find_port_by_vq(vq->vdev->priv, vq);
17448c2ecf20Sopenharmony_ci	if (!port) {
17458c2ecf20Sopenharmony_ci		flush_bufs(vq, false);
17468c2ecf20Sopenharmony_ci		return;
17478c2ecf20Sopenharmony_ci	}
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	wake_up_interruptible(&port->waitqueue);
17508c2ecf20Sopenharmony_ci}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_cistatic void in_intr(struct virtqueue *vq)
17538c2ecf20Sopenharmony_ci{
17548c2ecf20Sopenharmony_ci	struct port *port;
17558c2ecf20Sopenharmony_ci	unsigned long flags;
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci	port = find_port_by_vq(vq->vdev->priv, vq);
17588c2ecf20Sopenharmony_ci	if (!port) {
17598c2ecf20Sopenharmony_ci		flush_bufs(vq, false);
17608c2ecf20Sopenharmony_ci		return;
17618c2ecf20Sopenharmony_ci	}
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	spin_lock_irqsave(&port->inbuf_lock, flags);
17648c2ecf20Sopenharmony_ci	port->inbuf = get_inbuf(port);
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	/*
17678c2ecf20Sopenharmony_ci	 * Normally the port should not accept data when the port is
17688c2ecf20Sopenharmony_ci	 * closed. For generic serial ports, the host won't (shouldn't)
17698c2ecf20Sopenharmony_ci	 * send data till the guest is connected. But this condition
17708c2ecf20Sopenharmony_ci	 * can be reached when a console port is not yet connected (no
17718c2ecf20Sopenharmony_ci	 * tty is spawned) and the other side sends out data over the
17728c2ecf20Sopenharmony_ci	 * vring, or when a remote devices start sending data before
17738c2ecf20Sopenharmony_ci	 * the ports are opened.
17748c2ecf20Sopenharmony_ci	 *
17758c2ecf20Sopenharmony_ci	 * A generic serial port will discard data if not connected,
17768c2ecf20Sopenharmony_ci	 * while console ports and rproc-serial ports accepts data at
17778c2ecf20Sopenharmony_ci	 * any time. rproc-serial is initiated with guest_connected to
17788c2ecf20Sopenharmony_ci	 * false because port_fops_open expects this. Console ports are
17798c2ecf20Sopenharmony_ci	 * hooked up with an HVC console and is initialized with
17808c2ecf20Sopenharmony_ci	 * guest_connected to true.
17818c2ecf20Sopenharmony_ci	 */
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	if (!port->guest_connected && !is_rproc_serial(port->portdev->vdev))
17848c2ecf20Sopenharmony_ci		discard_port_data(port);
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	/* Send a SIGIO indicating new data in case the process asked for it */
17878c2ecf20Sopenharmony_ci	send_sigio_to_port(port);
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&port->inbuf_lock, flags);
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	wake_up_interruptible(&port->waitqueue);
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_ci	if (is_console_port(port) && hvc_poll(port->cons.hvc))
17948c2ecf20Sopenharmony_ci		hvc_kick();
17958c2ecf20Sopenharmony_ci}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_cistatic void control_intr(struct virtqueue *vq)
17988c2ecf20Sopenharmony_ci{
17998c2ecf20Sopenharmony_ci	struct ports_device *portdev;
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	portdev = vq->vdev->priv;
18028c2ecf20Sopenharmony_ci	schedule_work(&portdev->control_work);
18038c2ecf20Sopenharmony_ci}
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_cistatic void config_intr(struct virtio_device *vdev)
18068c2ecf20Sopenharmony_ci{
18078c2ecf20Sopenharmony_ci	struct ports_device *portdev;
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	portdev = vdev->priv;
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_ci	if (!use_multiport(portdev))
18128c2ecf20Sopenharmony_ci		schedule_work(&portdev->config_work);
18138c2ecf20Sopenharmony_ci}
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_cistatic void config_work_handler(struct work_struct *work)
18168c2ecf20Sopenharmony_ci{
18178c2ecf20Sopenharmony_ci	struct ports_device *portdev;
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	portdev = container_of(work, struct ports_device, config_work);
18208c2ecf20Sopenharmony_ci	if (!use_multiport(portdev)) {
18218c2ecf20Sopenharmony_ci		struct virtio_device *vdev;
18228c2ecf20Sopenharmony_ci		struct port *port;
18238c2ecf20Sopenharmony_ci		u16 rows, cols;
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci		vdev = portdev->vdev;
18268c2ecf20Sopenharmony_ci		virtio_cread(vdev, struct virtio_console_config, cols, &cols);
18278c2ecf20Sopenharmony_ci		virtio_cread(vdev, struct virtio_console_config, rows, &rows);
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci		port = find_port_by_id(portdev, 0);
18308c2ecf20Sopenharmony_ci		set_console_size(port, rows, cols);
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci		/*
18338c2ecf20Sopenharmony_ci		 * We'll use this way of resizing only for legacy
18348c2ecf20Sopenharmony_ci		 * support.  For newer userspace
18358c2ecf20Sopenharmony_ci		 * (VIRTIO_CONSOLE_F_MULTPORT+), use control messages
18368c2ecf20Sopenharmony_ci		 * to indicate console size changes so that it can be
18378c2ecf20Sopenharmony_ci		 * done per-port.
18388c2ecf20Sopenharmony_ci		 */
18398c2ecf20Sopenharmony_ci		resize_console(port);
18408c2ecf20Sopenharmony_ci	}
18418c2ecf20Sopenharmony_ci}
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_cistatic int init_vqs(struct ports_device *portdev)
18448c2ecf20Sopenharmony_ci{
18458c2ecf20Sopenharmony_ci	vq_callback_t **io_callbacks;
18468c2ecf20Sopenharmony_ci	char **io_names;
18478c2ecf20Sopenharmony_ci	struct virtqueue **vqs;
18488c2ecf20Sopenharmony_ci	u32 i, j, nr_ports, nr_queues;
18498c2ecf20Sopenharmony_ci	int err;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	nr_ports = portdev->max_nr_ports;
18528c2ecf20Sopenharmony_ci	nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2;
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci	vqs = kmalloc_array(nr_queues, sizeof(struct virtqueue *), GFP_KERNEL);
18558c2ecf20Sopenharmony_ci	io_callbacks = kmalloc_array(nr_queues, sizeof(vq_callback_t *),
18568c2ecf20Sopenharmony_ci				     GFP_KERNEL);
18578c2ecf20Sopenharmony_ci	io_names = kmalloc_array(nr_queues, sizeof(char *), GFP_KERNEL);
18588c2ecf20Sopenharmony_ci	portdev->in_vqs = kmalloc_array(nr_ports, sizeof(struct virtqueue *),
18598c2ecf20Sopenharmony_ci					GFP_KERNEL);
18608c2ecf20Sopenharmony_ci	portdev->out_vqs = kmalloc_array(nr_ports, sizeof(struct virtqueue *),
18618c2ecf20Sopenharmony_ci					 GFP_KERNEL);
18628c2ecf20Sopenharmony_ci	if (!vqs || !io_callbacks || !io_names || !portdev->in_vqs ||
18638c2ecf20Sopenharmony_ci	    !portdev->out_vqs) {
18648c2ecf20Sopenharmony_ci		err = -ENOMEM;
18658c2ecf20Sopenharmony_ci		goto free;
18668c2ecf20Sopenharmony_ci	}
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	/*
18698c2ecf20Sopenharmony_ci	 * For backward compat (newer host but older guest), the host
18708c2ecf20Sopenharmony_ci	 * spawns a console port first and also inits the vqs for port
18718c2ecf20Sopenharmony_ci	 * 0 before others.
18728c2ecf20Sopenharmony_ci	 */
18738c2ecf20Sopenharmony_ci	j = 0;
18748c2ecf20Sopenharmony_ci	io_callbacks[j] = in_intr;
18758c2ecf20Sopenharmony_ci	io_callbacks[j + 1] = out_intr;
18768c2ecf20Sopenharmony_ci	io_names[j] = "input";
18778c2ecf20Sopenharmony_ci	io_names[j + 1] = "output";
18788c2ecf20Sopenharmony_ci	j += 2;
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	if (use_multiport(portdev)) {
18818c2ecf20Sopenharmony_ci		io_callbacks[j] = control_intr;
18828c2ecf20Sopenharmony_ci		io_callbacks[j + 1] = NULL;
18838c2ecf20Sopenharmony_ci		io_names[j] = "control-i";
18848c2ecf20Sopenharmony_ci		io_names[j + 1] = "control-o";
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci		for (i = 1; i < nr_ports; i++) {
18878c2ecf20Sopenharmony_ci			j += 2;
18888c2ecf20Sopenharmony_ci			io_callbacks[j] = in_intr;
18898c2ecf20Sopenharmony_ci			io_callbacks[j + 1] = out_intr;
18908c2ecf20Sopenharmony_ci			io_names[j] = "input";
18918c2ecf20Sopenharmony_ci			io_names[j + 1] = "output";
18928c2ecf20Sopenharmony_ci		}
18938c2ecf20Sopenharmony_ci	}
18948c2ecf20Sopenharmony_ci	/* Find the queues. */
18958c2ecf20Sopenharmony_ci	err = virtio_find_vqs(portdev->vdev, nr_queues, vqs,
18968c2ecf20Sopenharmony_ci			      io_callbacks,
18978c2ecf20Sopenharmony_ci			      (const char **)io_names, NULL);
18988c2ecf20Sopenharmony_ci	if (err)
18998c2ecf20Sopenharmony_ci		goto free;
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	j = 0;
19028c2ecf20Sopenharmony_ci	portdev->in_vqs[0] = vqs[0];
19038c2ecf20Sopenharmony_ci	portdev->out_vqs[0] = vqs[1];
19048c2ecf20Sopenharmony_ci	j += 2;
19058c2ecf20Sopenharmony_ci	if (use_multiport(portdev)) {
19068c2ecf20Sopenharmony_ci		portdev->c_ivq = vqs[j];
19078c2ecf20Sopenharmony_ci		portdev->c_ovq = vqs[j + 1];
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci		for (i = 1; i < nr_ports; i++) {
19108c2ecf20Sopenharmony_ci			j += 2;
19118c2ecf20Sopenharmony_ci			portdev->in_vqs[i] = vqs[j];
19128c2ecf20Sopenharmony_ci			portdev->out_vqs[i] = vqs[j + 1];
19138c2ecf20Sopenharmony_ci		}
19148c2ecf20Sopenharmony_ci	}
19158c2ecf20Sopenharmony_ci	kfree(io_names);
19168c2ecf20Sopenharmony_ci	kfree(io_callbacks);
19178c2ecf20Sopenharmony_ci	kfree(vqs);
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	return 0;
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_cifree:
19228c2ecf20Sopenharmony_ci	kfree(portdev->out_vqs);
19238c2ecf20Sopenharmony_ci	kfree(portdev->in_vqs);
19248c2ecf20Sopenharmony_ci	kfree(io_names);
19258c2ecf20Sopenharmony_ci	kfree(io_callbacks);
19268c2ecf20Sopenharmony_ci	kfree(vqs);
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_ci	return err;
19298c2ecf20Sopenharmony_ci}
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_cistatic const struct file_operations portdev_fops = {
19328c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
19338c2ecf20Sopenharmony_ci};
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_cistatic void remove_vqs(struct ports_device *portdev)
19368c2ecf20Sopenharmony_ci{
19378c2ecf20Sopenharmony_ci	struct virtqueue *vq;
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci	virtio_device_for_each_vq(portdev->vdev, vq) {
19408c2ecf20Sopenharmony_ci		struct port_buffer *buf;
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci		flush_bufs(vq, true);
19438c2ecf20Sopenharmony_ci		while ((buf = virtqueue_detach_unused_buf(vq)))
19448c2ecf20Sopenharmony_ci			free_buf(buf, true);
19458c2ecf20Sopenharmony_ci	}
19468c2ecf20Sopenharmony_ci	portdev->vdev->config->del_vqs(portdev->vdev);
19478c2ecf20Sopenharmony_ci	kfree(portdev->in_vqs);
19488c2ecf20Sopenharmony_ci	kfree(portdev->out_vqs);
19498c2ecf20Sopenharmony_ci}
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_cistatic void virtcons_remove(struct virtio_device *vdev)
19528c2ecf20Sopenharmony_ci{
19538c2ecf20Sopenharmony_ci	struct ports_device *portdev;
19548c2ecf20Sopenharmony_ci	struct port *port, *port2;
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_ci	portdev = vdev->priv;
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci	spin_lock_irq(&pdrvdata_lock);
19598c2ecf20Sopenharmony_ci	list_del(&portdev->list);
19608c2ecf20Sopenharmony_ci	spin_unlock_irq(&pdrvdata_lock);
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci	/* Device is going away, exit any polling for buffers */
19638c2ecf20Sopenharmony_ci	virtio_break_device(vdev);
19648c2ecf20Sopenharmony_ci	if (use_multiport(portdev))
19658c2ecf20Sopenharmony_ci		flush_work(&portdev->control_work);
19668c2ecf20Sopenharmony_ci	else
19678c2ecf20Sopenharmony_ci		flush_work(&portdev->config_work);
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci	/* Disable interrupts for vqs */
19708c2ecf20Sopenharmony_ci	vdev->config->reset(vdev);
19718c2ecf20Sopenharmony_ci	/* Finish up work that's lined up */
19728c2ecf20Sopenharmony_ci	if (use_multiport(portdev))
19738c2ecf20Sopenharmony_ci		cancel_work_sync(&portdev->control_work);
19748c2ecf20Sopenharmony_ci	else
19758c2ecf20Sopenharmony_ci		cancel_work_sync(&portdev->config_work);
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci	list_for_each_entry_safe(port, port2, &portdev->ports, list)
19788c2ecf20Sopenharmony_ci		unplug_port(port);
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	unregister_chrdev(portdev->chr_major, "virtio-portsdev");
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	/*
19838c2ecf20Sopenharmony_ci	 * When yanking out a device, we immediately lose the
19848c2ecf20Sopenharmony_ci	 * (device-side) queues.  So there's no point in keeping the
19858c2ecf20Sopenharmony_ci	 * guest side around till we drop our final reference.  This
19868c2ecf20Sopenharmony_ci	 * also means that any ports which are in an open state will
19878c2ecf20Sopenharmony_ci	 * have to just stop using the port, as the vqs are going
19888c2ecf20Sopenharmony_ci	 * away.
19898c2ecf20Sopenharmony_ci	 */
19908c2ecf20Sopenharmony_ci	remove_vqs(portdev);
19918c2ecf20Sopenharmony_ci	kfree(portdev);
19928c2ecf20Sopenharmony_ci}
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci/*
19958c2ecf20Sopenharmony_ci * Once we're further in boot, we get probed like any other virtio
19968c2ecf20Sopenharmony_ci * device.
19978c2ecf20Sopenharmony_ci *
19988c2ecf20Sopenharmony_ci * If the host also supports multiple console ports, we check the
19998c2ecf20Sopenharmony_ci * config space to see how many ports the host has spawned.  We
20008c2ecf20Sopenharmony_ci * initialize each port found.
20018c2ecf20Sopenharmony_ci */
20028c2ecf20Sopenharmony_cistatic int virtcons_probe(struct virtio_device *vdev)
20038c2ecf20Sopenharmony_ci{
20048c2ecf20Sopenharmony_ci	struct ports_device *portdev;
20058c2ecf20Sopenharmony_ci	int err;
20068c2ecf20Sopenharmony_ci	bool multiport;
20078c2ecf20Sopenharmony_ci	bool early = early_put_chars != NULL;
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	/* We only need a config space if features are offered */
20108c2ecf20Sopenharmony_ci	if (!vdev->config->get &&
20118c2ecf20Sopenharmony_ci	    (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)
20128c2ecf20Sopenharmony_ci	     || virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT))) {
20138c2ecf20Sopenharmony_ci		dev_err(&vdev->dev, "%s failure: config access disabled\n",
20148c2ecf20Sopenharmony_ci			__func__);
20158c2ecf20Sopenharmony_ci		return -EINVAL;
20168c2ecf20Sopenharmony_ci	}
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	/* Ensure to read early_put_chars now */
20198c2ecf20Sopenharmony_ci	barrier();
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	portdev = kmalloc(sizeof(*portdev), GFP_KERNEL);
20228c2ecf20Sopenharmony_ci	if (!portdev) {
20238c2ecf20Sopenharmony_ci		err = -ENOMEM;
20248c2ecf20Sopenharmony_ci		goto fail;
20258c2ecf20Sopenharmony_ci	}
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	/* Attach this portdev to this virtio_device, and vice-versa. */
20288c2ecf20Sopenharmony_ci	portdev->vdev = vdev;
20298c2ecf20Sopenharmony_ci	vdev->priv = portdev;
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci	portdev->chr_major = register_chrdev(0, "virtio-portsdev",
20328c2ecf20Sopenharmony_ci					     &portdev_fops);
20338c2ecf20Sopenharmony_ci	if (portdev->chr_major < 0) {
20348c2ecf20Sopenharmony_ci		dev_err(&vdev->dev,
20358c2ecf20Sopenharmony_ci			"Error %d registering chrdev for device %u\n",
20368c2ecf20Sopenharmony_ci			portdev->chr_major, vdev->index);
20378c2ecf20Sopenharmony_ci		err = portdev->chr_major;
20388c2ecf20Sopenharmony_ci		goto free;
20398c2ecf20Sopenharmony_ci	}
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_ci	multiport = false;
20428c2ecf20Sopenharmony_ci	portdev->max_nr_ports = 1;
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci	/* Don't test MULTIPORT at all if we're rproc: not a valid feature! */
20458c2ecf20Sopenharmony_ci	if (!is_rproc_serial(vdev) &&
20468c2ecf20Sopenharmony_ci	    virtio_cread_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
20478c2ecf20Sopenharmony_ci				 struct virtio_console_config, max_nr_ports,
20488c2ecf20Sopenharmony_ci				 &portdev->max_nr_ports) == 0) {
20498c2ecf20Sopenharmony_ci		multiport = true;
20508c2ecf20Sopenharmony_ci	}
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	err = init_vqs(portdev);
20538c2ecf20Sopenharmony_ci	if (err < 0) {
20548c2ecf20Sopenharmony_ci		dev_err(&vdev->dev, "Error %d initializing vqs\n", err);
20558c2ecf20Sopenharmony_ci		goto free_chrdev;
20568c2ecf20Sopenharmony_ci	}
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci	spin_lock_init(&portdev->ports_lock);
20598c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&portdev->ports);
20608c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&portdev->list);
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci	virtio_device_ready(portdev->vdev);
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	INIT_WORK(&portdev->config_work, &config_work_handler);
20658c2ecf20Sopenharmony_ci	INIT_WORK(&portdev->control_work, &control_work_handler);
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	if (multiport) {
20688c2ecf20Sopenharmony_ci		spin_lock_init(&portdev->c_ivq_lock);
20698c2ecf20Sopenharmony_ci		spin_lock_init(&portdev->c_ovq_lock);
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci		err = fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
20728c2ecf20Sopenharmony_ci		if (err < 0) {
20738c2ecf20Sopenharmony_ci			dev_err(&vdev->dev,
20748c2ecf20Sopenharmony_ci				"Error allocating buffers for control queue\n");
20758c2ecf20Sopenharmony_ci			/*
20768c2ecf20Sopenharmony_ci			 * The host might want to notify mgmt sw about device
20778c2ecf20Sopenharmony_ci			 * add failure.
20788c2ecf20Sopenharmony_ci			 */
20798c2ecf20Sopenharmony_ci			__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
20808c2ecf20Sopenharmony_ci					   VIRTIO_CONSOLE_DEVICE_READY, 0);
20818c2ecf20Sopenharmony_ci			/* Device was functional: we need full cleanup. */
20828c2ecf20Sopenharmony_ci			virtcons_remove(vdev);
20838c2ecf20Sopenharmony_ci			return err;
20848c2ecf20Sopenharmony_ci		}
20858c2ecf20Sopenharmony_ci	} else {
20868c2ecf20Sopenharmony_ci		/*
20878c2ecf20Sopenharmony_ci		 * For backward compatibility: Create a console port
20888c2ecf20Sopenharmony_ci		 * if we're running on older host.
20898c2ecf20Sopenharmony_ci		 */
20908c2ecf20Sopenharmony_ci		add_port(portdev, 0);
20918c2ecf20Sopenharmony_ci	}
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_ci	spin_lock_irq(&pdrvdata_lock);
20948c2ecf20Sopenharmony_ci	list_add_tail(&portdev->list, &pdrvdata.portdevs);
20958c2ecf20Sopenharmony_ci	spin_unlock_irq(&pdrvdata_lock);
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
20988c2ecf20Sopenharmony_ci			   VIRTIO_CONSOLE_DEVICE_READY, 1);
20998c2ecf20Sopenharmony_ci
21008c2ecf20Sopenharmony_ci	/*
21018c2ecf20Sopenharmony_ci	 * If there was an early virtio console, assume that there are no
21028c2ecf20Sopenharmony_ci	 * other consoles. We need to wait until the hvc_alloc matches the
21038c2ecf20Sopenharmony_ci	 * hvc_instantiate, otherwise tty_open will complain, resulting in
21048c2ecf20Sopenharmony_ci	 * a "Warning: unable to open an initial console" boot failure.
21058c2ecf20Sopenharmony_ci	 * Without multiport this is done in add_port above. With multiport
21068c2ecf20Sopenharmony_ci	 * this might take some host<->guest communication - thus we have to
21078c2ecf20Sopenharmony_ci	 * wait.
21088c2ecf20Sopenharmony_ci	 */
21098c2ecf20Sopenharmony_ci	if (multiport && early)
21108c2ecf20Sopenharmony_ci		wait_for_completion(&early_console_added);
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	return 0;
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_cifree_chrdev:
21158c2ecf20Sopenharmony_ci	unregister_chrdev(portdev->chr_major, "virtio-portsdev");
21168c2ecf20Sopenharmony_cifree:
21178c2ecf20Sopenharmony_ci	kfree(portdev);
21188c2ecf20Sopenharmony_cifail:
21198c2ecf20Sopenharmony_ci	return err;
21208c2ecf20Sopenharmony_ci}
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_cistatic const struct virtio_device_id id_table[] = {
21238c2ecf20Sopenharmony_ci	{ VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID },
21248c2ecf20Sopenharmony_ci	{ 0 },
21258c2ecf20Sopenharmony_ci};
21268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table);
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_cistatic const unsigned int features[] = {
21298c2ecf20Sopenharmony_ci	VIRTIO_CONSOLE_F_SIZE,
21308c2ecf20Sopenharmony_ci	VIRTIO_CONSOLE_F_MULTIPORT,
21318c2ecf20Sopenharmony_ci};
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_cistatic const struct virtio_device_id rproc_serial_id_table[] = {
21348c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_REMOTEPROC)
21358c2ecf20Sopenharmony_ci	{ VIRTIO_ID_RPROC_SERIAL, VIRTIO_DEV_ANY_ID },
21368c2ecf20Sopenharmony_ci#endif
21378c2ecf20Sopenharmony_ci	{ 0 },
21388c2ecf20Sopenharmony_ci};
21398c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, rproc_serial_id_table);
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_cistatic const unsigned int rproc_serial_features[] = {
21428c2ecf20Sopenharmony_ci};
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
21458c2ecf20Sopenharmony_cistatic int virtcons_freeze(struct virtio_device *vdev)
21468c2ecf20Sopenharmony_ci{
21478c2ecf20Sopenharmony_ci	struct ports_device *portdev;
21488c2ecf20Sopenharmony_ci	struct port *port;
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_ci	portdev = vdev->priv;
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci	vdev->config->reset(vdev);
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci	if (use_multiport(portdev))
21558c2ecf20Sopenharmony_ci		virtqueue_disable_cb(portdev->c_ivq);
21568c2ecf20Sopenharmony_ci	cancel_work_sync(&portdev->control_work);
21578c2ecf20Sopenharmony_ci	cancel_work_sync(&portdev->config_work);
21588c2ecf20Sopenharmony_ci	/*
21598c2ecf20Sopenharmony_ci	 * Once more: if control_work_handler() was running, it would
21608c2ecf20Sopenharmony_ci	 * enable the cb as the last step.
21618c2ecf20Sopenharmony_ci	 */
21628c2ecf20Sopenharmony_ci	if (use_multiport(portdev))
21638c2ecf20Sopenharmony_ci		virtqueue_disable_cb(portdev->c_ivq);
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_ci	list_for_each_entry(port, &portdev->ports, list) {
21668c2ecf20Sopenharmony_ci		virtqueue_disable_cb(port->in_vq);
21678c2ecf20Sopenharmony_ci		virtqueue_disable_cb(port->out_vq);
21688c2ecf20Sopenharmony_ci		/*
21698c2ecf20Sopenharmony_ci		 * We'll ask the host later if the new invocation has
21708c2ecf20Sopenharmony_ci		 * the port opened or closed.
21718c2ecf20Sopenharmony_ci		 */
21728c2ecf20Sopenharmony_ci		port->host_connected = false;
21738c2ecf20Sopenharmony_ci		remove_port_data(port);
21748c2ecf20Sopenharmony_ci	}
21758c2ecf20Sopenharmony_ci	remove_vqs(portdev);
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_ci	return 0;
21788c2ecf20Sopenharmony_ci}
21798c2ecf20Sopenharmony_ci
21808c2ecf20Sopenharmony_cistatic int virtcons_restore(struct virtio_device *vdev)
21818c2ecf20Sopenharmony_ci{
21828c2ecf20Sopenharmony_ci	struct ports_device *portdev;
21838c2ecf20Sopenharmony_ci	struct port *port;
21848c2ecf20Sopenharmony_ci	int ret;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	portdev = vdev->priv;
21878c2ecf20Sopenharmony_ci
21888c2ecf20Sopenharmony_ci	ret = init_vqs(portdev);
21898c2ecf20Sopenharmony_ci	if (ret)
21908c2ecf20Sopenharmony_ci		return ret;
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci	virtio_device_ready(portdev->vdev);
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	if (use_multiport(portdev))
21958c2ecf20Sopenharmony_ci		fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	list_for_each_entry(port, &portdev->ports, list) {
21988c2ecf20Sopenharmony_ci		port->in_vq = portdev->in_vqs[port->id];
21998c2ecf20Sopenharmony_ci		port->out_vq = portdev->out_vqs[port->id];
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci		fill_queue(port->in_vq, &port->inbuf_lock);
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci		/* Get port open/close status on the host */
22048c2ecf20Sopenharmony_ci		send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci		/*
22078c2ecf20Sopenharmony_ci		 * If a port was open at the time of suspending, we
22088c2ecf20Sopenharmony_ci		 * have to let the host know that it's still open.
22098c2ecf20Sopenharmony_ci		 */
22108c2ecf20Sopenharmony_ci		if (port->guest_connected)
22118c2ecf20Sopenharmony_ci			send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
22128c2ecf20Sopenharmony_ci	}
22138c2ecf20Sopenharmony_ci	return 0;
22148c2ecf20Sopenharmony_ci}
22158c2ecf20Sopenharmony_ci#endif
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_cistatic struct virtio_driver virtio_console = {
22188c2ecf20Sopenharmony_ci	.feature_table = features,
22198c2ecf20Sopenharmony_ci	.feature_table_size = ARRAY_SIZE(features),
22208c2ecf20Sopenharmony_ci	.driver.name =	KBUILD_MODNAME,
22218c2ecf20Sopenharmony_ci	.driver.owner =	THIS_MODULE,
22228c2ecf20Sopenharmony_ci	.id_table =	id_table,
22238c2ecf20Sopenharmony_ci	.probe =	virtcons_probe,
22248c2ecf20Sopenharmony_ci	.remove =	virtcons_remove,
22258c2ecf20Sopenharmony_ci	.config_changed = config_intr,
22268c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
22278c2ecf20Sopenharmony_ci	.freeze =	virtcons_freeze,
22288c2ecf20Sopenharmony_ci	.restore =	virtcons_restore,
22298c2ecf20Sopenharmony_ci#endif
22308c2ecf20Sopenharmony_ci};
22318c2ecf20Sopenharmony_ci
22328c2ecf20Sopenharmony_cistatic struct virtio_driver virtio_rproc_serial = {
22338c2ecf20Sopenharmony_ci	.feature_table = rproc_serial_features,
22348c2ecf20Sopenharmony_ci	.feature_table_size = ARRAY_SIZE(rproc_serial_features),
22358c2ecf20Sopenharmony_ci	.driver.name =	"virtio_rproc_serial",
22368c2ecf20Sopenharmony_ci	.driver.owner =	THIS_MODULE,
22378c2ecf20Sopenharmony_ci	.id_table =	rproc_serial_id_table,
22388c2ecf20Sopenharmony_ci	.probe =	virtcons_probe,
22398c2ecf20Sopenharmony_ci	.remove =	virtcons_remove,
22408c2ecf20Sopenharmony_ci};
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_cistatic int __init virtio_console_init(void)
22438c2ecf20Sopenharmony_ci{
22448c2ecf20Sopenharmony_ci	int err;
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_ci	pdrvdata.class = class_create(THIS_MODULE, "virtio-ports");
22478c2ecf20Sopenharmony_ci	if (IS_ERR(pdrvdata.class)) {
22488c2ecf20Sopenharmony_ci		err = PTR_ERR(pdrvdata.class);
22498c2ecf20Sopenharmony_ci		pr_err("Error %d creating virtio-ports class\n", err);
22508c2ecf20Sopenharmony_ci		return err;
22518c2ecf20Sopenharmony_ci	}
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci	pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL);
22548c2ecf20Sopenharmony_ci	if (!pdrvdata.debugfs_dir)
22558c2ecf20Sopenharmony_ci		pr_warn("Error creating debugfs dir for virtio-ports\n");
22568c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&pdrvdata.consoles);
22578c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&pdrvdata.portdevs);
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci	err = register_virtio_driver(&virtio_console);
22608c2ecf20Sopenharmony_ci	if (err < 0) {
22618c2ecf20Sopenharmony_ci		pr_err("Error %d registering virtio driver\n", err);
22628c2ecf20Sopenharmony_ci		goto free;
22638c2ecf20Sopenharmony_ci	}
22648c2ecf20Sopenharmony_ci	err = register_virtio_driver(&virtio_rproc_serial);
22658c2ecf20Sopenharmony_ci	if (err < 0) {
22668c2ecf20Sopenharmony_ci		pr_err("Error %d registering virtio rproc serial driver\n",
22678c2ecf20Sopenharmony_ci		       err);
22688c2ecf20Sopenharmony_ci		goto unregister;
22698c2ecf20Sopenharmony_ci	}
22708c2ecf20Sopenharmony_ci	return 0;
22718c2ecf20Sopenharmony_ciunregister:
22728c2ecf20Sopenharmony_ci	unregister_virtio_driver(&virtio_console);
22738c2ecf20Sopenharmony_cifree:
22748c2ecf20Sopenharmony_ci	debugfs_remove_recursive(pdrvdata.debugfs_dir);
22758c2ecf20Sopenharmony_ci	class_destroy(pdrvdata.class);
22768c2ecf20Sopenharmony_ci	return err;
22778c2ecf20Sopenharmony_ci}
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_cistatic void __exit virtio_console_fini(void)
22808c2ecf20Sopenharmony_ci{
22818c2ecf20Sopenharmony_ci	reclaim_dma_bufs();
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_ci	unregister_virtio_driver(&virtio_console);
22848c2ecf20Sopenharmony_ci	unregister_virtio_driver(&virtio_rproc_serial);
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_ci	class_destroy(pdrvdata.class);
22878c2ecf20Sopenharmony_ci	debugfs_remove_recursive(pdrvdata.debugfs_dir);
22888c2ecf20Sopenharmony_ci}
22898c2ecf20Sopenharmony_cimodule_init(virtio_console_init);
22908c2ecf20Sopenharmony_cimodule_exit(virtio_console_fini);
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Virtio console driver");
22938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2294