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