/* * Mediated virtual PCI serial host device driver * * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * Author: Neo Jia <cjia@nvidia.com> * Kirti Wankhede <kwankhede@nvidia.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Sample driver that creates mdev device that simulates serial port over PCI * card. * */ #include <linux/init.h>#include <linux/module.h>#include <linux/device.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/poll.h>#include <linux/slab.h>#include <linux/cdev.h>#include <linux/sched.h>#include <linux/wait.h>#include <linux/uuid.h>#include <linux/vfio.h>#include <linux/iommu.h>#include <linux/sysfs.h>#include <linux/ctype.h>#include <linux/file.h>#include <linux/mdev.h>#include <linux/pci.h>#include <linux/serial.h>#include <uapi/linux/serial_reg.h>#include <linux/eventfd.h>/* * #defines */ #define VERSION_STRING "0.1"#define DRIVER_AUTHOR "NVIDIA Corporation" #define MTTY_CLASS_NAME "mtty" #define MTTY_NAME "mtty" #define MTTY_STRING_LEN 16 #define MTTY_CONFIG_SPACE_SIZE 0xff#define MTTY_IO_BAR_SIZE 0x8#define MTTY_MMIO_BAR_SIZE 0x100000 #define STORE_LE16(addr, val) (*(u16 *)addr = val)#define STORE_LE32(addr, val) (*(u32 *)addr = val) #define MAX_FIFO_SIZE 16 #define CIRCULAR_BUF_INC_IDX(idx) (idx = (idx + 1) & (MAX_FIFO_SIZE - 1)) #define MTTY_VFIO_PCI_OFFSET_SHIFT 40 #define MTTY_VFIO_PCI_OFFSET_TO_INDEX(off) (off >> MTTY_VFIO_PCI_OFFSET_SHIFT)#define MTTY_VFIO_PCI_INDEX_TO_OFFSET(index) \ ((u64)(index) << MTTY_VFIO_PCI_OFFSET_SHIFT)#define MTTY_VFIO_PCI_OFFSET_MASK \ (((u64)(1) << MTTY_VFIO_PCI_OFFSET_SHIFT) - 1)#define MAX_MTTYS 24 /* * Global Structures */ struct mtty_dev { dev_t vd_devt; struct class *vd_class; struct cdev vd_cdev; struct idr vd_idr; struct device dev;} mtty_dev; struct mdev_region_info { u64 start; u64 phys_start; u32 size; u64 vfio_offset;}; #if defined(DEBUG_REGS)const char *wr_reg[] = { "TX", "IER", "FCR", "LCR", "MCR", "LSR", "MSR", "SCR"}; const char *rd_reg[] = { "RX", "IER", "IIR", "LCR", "MCR", "LSR", "MSR", "SCR"};#endif /* loop back buffer */struct rxtx { u8 fifo[MAX_FIFO_SIZE]; u8 head, tail; u8 count;}; struct serial_port { u8 uart_reg[8]; /* 8 registers */ struct rxtx rxtx; /* loop back buffer */ bool dlab; bool overrun; u16 divisor; u8 fcr; /* FIFO control register */ u8 max_fifo_size; u8 intr_trigger_level; /* interrupt trigger level */}; /* State of each mdev device */struct mdev_state { int irq_fd; struct eventfd_ctx *intx_evtfd; struct eventfd_ctx *msi_evtfd; int irq_index; u8 *vconfig; struct mutex ops_lock; struct mdev_device *mdev; struct mdev_region_info region_info[VFIO_PCI_NUM_REGIONS]; u32 bar_mask[VFIO_PCI_NUM_REGIONS]; struct list_head next; struct serial_port s[2]; struct mutex rxtx_lock; struct vfio_device_info dev_info; int nr_ports;}; struct mutex mdev_list_lock;struct list_head mdev_devices_list; static const struct file_operations vd_fops = { .owner = THIS_MODULE,}; /* function prototypes */ static int mtty_trigger_interrupt(uuid_le uuid); /* Helper functions */static struct mdev_state *find_mdev_state_by_uuid(uuid_le uuid){ struct mdev_state *mds; list_for_each_entry(mds, &mdev_devices_list, next) { if (uuid_le_cmp(mdev_uuid(mds->mdev), uuid) == 0) return mds; } return NULL;} void dump_buffer(char *buf, uint32_t count){#if defined(DEBUG) int i; pr_info("Buffer:\n"); for (i = 0; i < count; i++) { pr_info("%2x ", *(buf + i)); if ((i + 1) % 16 == 0) pr_info("\n"); }#endif} static void mtty_create_config_space(struct mdev_state *mdev_state){ /* PCI dev ID */ STORE_LE32((u32 *) &mdev_state->vconfig[0x0], 0x32534348); /* Control: I/O+, Mem-, BusMaster- */ STORE_LE16((u16 *) &mdev_state->vconfig[0x4], 0x0001); /* Status: capabilities list absent */ STORE_LE16((u16 *) &mdev_state->vconfig[0x6], 0x0200); /* Rev ID */ mdev_state->vconfig[0x8] = 0x10; /* programming interface class : 16550-compatible serial controller */ mdev_state->vconfig[0x9] = 0x02; /* Sub class : 00 */ mdev_state->vconfig[0xa] = 0x00; /* Base class : Simple Communication controllers */ mdev_state->vconfig[0xb] = 0x07; /* base address registers */ /* BAR0: IO space */ STORE_LE32((u32 *) &mdev_state->vconfig[0x10], 0x000001); mdev_state->bar_mask[0] = ~(MTTY_IO_BAR_SIZE) + 1; if (mdev_state->nr_ports == 2) { /* BAR1: IO space */ STORE_LE32((u32 *) &mdev_state->vconfig[0x14], 0x000001); mdev_state->bar_mask[1] = ~(MTTY_IO_BAR_SIZE) + 1; } /* Subsystem ID */ STORE_LE32((u32 *) &mdev_state->vconfig[0x2c], 0x32534348); mdev_state->vconfig[0x34] = 0x00; /* Cap Ptr */ mdev_state->vconfig[0x3d] = 0x01; /* interrupt pin (INTA#) */ /* Vendor specific data */ mdev_state->vconfig[0x40] = 0x23; mdev_state->vconfig[0x43] = 0x80; mdev_state->vconfig[0x44] = 0x23; mdev_state->vconfig[0x48] = 0x23; mdev_state->vconfig[0x4c] = 0x23; mdev_state->vconfig[0x60] = 0x50; mdev_state->vconfig[0x61] = 0x43; mdev_state->vconfig[0x62] = 0x49; mdev_state->vconfig[0x63] = 0x20; mdev_state->vconfig[0x64] = 0x53; mdev_state->vconfig[0x65] = 0x65; mdev_state->vconfig[0x66] = 0x72; mdev_state->vconfig[0x67] = 0x69; mdev_state->vconfig[0x68] = 0x61; mdev_state->vconfig[0x69] = 0x6c; mdev_state->vconfig[0x6a] = 0x2f; mdev_state->vconfig[0x6b] = 0x55; mdev_state->vconfig[0x6c] = 0x41; mdev_state->vconfig[0x6d] = 0x52; mdev_state->vconfig[0x6e] = 0x54;} static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset, char *buf, u32 count){ u32 cfg_addr, bar_mask, bar_index = 0; switch (offset) { case 0x04: /* device control */ case 0x06: /* device status */ /* do nothing */ break; case 0x3c: /* interrupt line */ mdev_state->vconfig[0x3c] = buf[0]; break; case 0x3d: /* * Interrupt Pin is hardwired to INTA. * This field is write protected by hardware */ break; case 0x10: /* BAR0 */ case 0x14: /* BAR1 */ if (offset == 0x10) bar_index = 0; else if (offset == 0x14) bar_index = 1; if ((mdev_state->nr_ports == 1) && (bar_index == 1)) { STORE_LE32(&mdev_state->vconfig[offset], 0); break; } cfg_addr = *(u32 *)buf; pr_info("BAR%d addr 0x%x\n", bar_index, cfg_addr); if (cfg_addr == 0xffffffff) { bar_mask = mdev_state->bar_mask[bar_index]; cfg_addr = (cfg_addr & bar_mask); } cfg_addr |= (mdev_state->vconfig[offset] & 0x3ul); STORE_LE32(&mdev_state->vconfig[offset], cfg_addr); break; case 0x18: /* BAR2 */ case 0x1c: /* BAR3 */ case 0x20: /* BAR4 */ STORE_LE32(&mdev_state->vconfig[offset], 0); break; default: pr_info("PCI config write @0x%x of %d bytes not handled\n", offset, count); break; }} static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state, u16 offset, char *buf, u32 count){ u8 data = *buf; /* Handle data written by guest */ switch (offset) { case UART_TX: /* if DLAB set, data is LSB of divisor */ if (mdev_state->s[index].dlab) { mdev_state->s[index].divisor |= data; break; } mutex_lock(&mdev_state->rxtx_lock); /* save in TX buffer */ if (mdev_state->s[index].rxtx.count < mdev_state->s[index].max_fifo_size) { mdev_state->s[index].rxtx.fifo[ mdev_state->s[index].rxtx.head] = data; mdev_state->s[index].rxtx.count++; CIRCULAR_BUF_INC_IDX(mdev_state->s[index].rxtx.head); mdev_state->s[index].overrun = false; /* * Trigger interrupt if receive data interrupt is * enabled and fifo reached trigger level */ if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_RDI) && (mdev_state->s[index].rxtx.count == mdev_state->s[index].intr_trigger_level)) { /* trigger interrupt */#if defined(DEBUG_INTR) pr_err("Serial port %d: Fifo level trigger\n", index);#endif mtty_trigger_interrupt( mdev_uuid(mdev_state->mdev)); } } else {#if defined(DEBUG_INTR) pr_err("Serial port %d: Buffer Overflow\n", index);#endif mdev_state->s[index].overrun = true; /* * Trigger interrupt if receiver line status interrupt * is enabled */ if (mdev_state->s[index].uart_reg[UART_IER] & UART_IER_RLSI) mtty_trigger_interrupt( mdev_uuid(mdev_state->mdev)); } mutex_unlock(&mdev_state->rxtx_lock); break; case UART_IER: /* if DLAB set, data is MSB of divisor */ if (mdev_state->s[index].dlab) mdev_state->s[index].divisor |= (u16)data << 8; else { mdev_state->s[index].uart_reg[offset] = data; mutex_lock(&mdev_state->rxtx_lock); if ((data & UART_IER_THRI) && (mdev_state->s[index].rxtx.head == mdev_state->s[index].rxtx.tail)) {#if defined(DEBUG_INTR) pr_err("Serial port %d: IER_THRI write\n", index);#endif mtty_trigger_interrupt( mdev_uuid(mdev_state->mdev)); } mutex_unlock(&mdev_state->rxtx_lock); } break; case UART_FCR: mdev_state->s[index].fcr = data; mutex_lock(&mdev_state->rxtx_lock); if (data & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)) { /* clear loop back FIFO */ mdev_state->s[index].rxtx.count = 0; mdev_state->s[index].rxtx.head = 0; mdev_state->s[index].rxtx.tail = 0; } mutex_unlock(&mdev_state->rxtx_lock); switch (data & UART_FCR_TRIGGER_MASK) { case UART_FCR_TRIGGER_1: mdev_state->s[index].intr_trigger_level = 1; break; case UART_FCR_TRIGGER_4: mdev_state->s[index].intr_trigger_level = 4; break; case UART_FCR_TRIGGER_8: mdev_state->s[index].intr_trigger_level = 8; break; case UART_FCR_TRIGGER_14: mdev_state->s[index].intr_trigger_level = 14; break; } /* * Set trigger level to 1 otherwise or implement timer with * timeout of 4 characters and on expiring that timer set * Recevice data timeout in IIR register */ mdev_state->s[index].intr_trigger_level = 1; if (data & UART_FCR_ENABLE_FIFO) mdev_state->s[index].max_fifo_size = MAX_FIFO_SIZE; else { mdev_state->s[index].max_fifo_size = 1; mdev_state->s[index].intr_trigger_level = 1; } break; case UART_LCR: if (data & UART_LCR_DLAB) { mdev_state->s[index].dlab = true; mdev_state->s[index].divisor = 0; } else mdev_state->s[index].dlab = false; mdev_state->s[index].uart_reg[offset] = data; break; case UART_MCR: mdev_state->s[index].uart_reg[offset] = data; if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_MSI) && (data & UART_MCR_OUT2)) {#if defined(DEBUG_INTR) pr_err("Serial port %d: MCR_OUT2 write\n", index);#endif mtty_trigger_interrupt(mdev_uuid(mdev_state->mdev)); } if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_MSI) && (data & (UART_MCR_RTS | UART_MCR_DTR))) {#if defined(DEBUG_INTR) pr_err("Serial port %d: MCR RTS/DTR write\n", index);#endif mtty_trigger_interrupt(mdev_uuid(mdev_state->mdev)); } break; case UART_LSR: case UART_MSR: /* do nothing */ break; case UART_SCR: mdev_state->s[index].uart_reg[offset] = data; break; default: break; }} static void handle_bar_read(unsigned int index, struct mdev_state *mdev_state, u16 offset, char *buf, u32 count){ /* Handle read requests by guest */ switch (offset) { case UART_RX: /* if DLAB set, data is LSB of divisor */ if (mdev_state->s[index].dlab) { *buf = (u8)mdev_state->s[index].divisor; break; } mutex_lock(&mdev_state->rxtx_lock); /* return data in tx buffer */ if (mdev_state->s[index].rxtx.head != mdev_state->s[index].rxtx.tail) { *buf = mdev_state->s[index].rxtx.fifo[ mdev_state->s[index].rxtx.tail]; mdev_state->s[index].rxtx.count--; CIRCULAR_BUF_INC_IDX(mdev_state->s[index].rxtx.tail); } if (mdev_state->s[index].rxtx.head == mdev_state->s[index].rxtx.tail) {Lines 1–500 of 1516. Load more
/* * Mediated virtual PCI serial host device driver * * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * Author: Neo Jia <cjia@nvidia.com> * Kirti Wankhede <kwankhede@nvidia.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Sample driver that creates mdev device that simulates serial port over PCI * card. * */ #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/poll.h> #include <linux/slab.h> #include <linux/cdev.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/uuid.h> #include <linux/vfio.h> #include <linux/iommu.h> #include <linux/sysfs.h> #include <linux/ctype.h> #include <linux/file.h> #include <linux/mdev.h> #include <linux/pci.h> #include <linux/serial.h> #include <uapi/linux/serial_reg.h> #include <linux/eventfd.h> /* * #defines */ #define VERSION_STRING "0.1" #define DRIVER_AUTHOR "NVIDIA Corporation" #define MTTY_CLASS_NAME "mtty" #define MTTY_NAME "mtty" #define MTTY_STRING_LEN 16 #define MTTY_CONFIG_SPACE_SIZE 0xff #define MTTY_IO_BAR_SIZE 0x8 #define MTTY_MMIO_BAR_SIZE 0x100000 #define STORE_LE16(addr, val) (*(u16 *)addr = val) #define STORE_LE32(addr, val) (*(u32 *)addr = val) #define MAX_FIFO_SIZE 16 #define CIRCULAR_BUF_INC_IDX(idx) (idx = (idx + 1) & (MAX_FIFO_SIZE - 1)) #define MTTY_VFIO_PCI_OFFSET_SHIFT 40 #define MTTY_VFIO_PCI_OFFSET_TO_INDEX(off) (off >> MTTY_VFIO_PCI_OFFSET_SHIFT) #define MTTY_VFIO_PCI_INDEX_TO_OFFSET(index) \ ((u64)(index) << MTTY_VFIO_PCI_OFFSET_SHIFT) #define MTTY_VFIO_PCI_OFFSET_MASK \ (((u64)(1) << MTTY_VFIO_PCI_OFFSET_SHIFT) - 1) #define MAX_MTTYS 24 /* * Global Structures */ struct mtty_dev { dev_t vd_devt; struct class *vd_class; struct cdev vd_cdev; struct idr vd_idr; struct device dev; } mtty_dev; struct mdev_region_info { u64 start; u64 phys_start; u32 size; u64 vfio_offset; }; #if defined(DEBUG_REGS) const char *wr_reg[] = { "TX", "IER", "FCR", "LCR", "MCR", "LSR", "MSR", "SCR" }; const char *rd_reg[] = { "RX", "IER", "IIR", "LCR", "MCR", "LSR", "MSR", "SCR" }; #endif /* loop back buffer */ struct rxtx { u8 fifo[MAX_FIFO_SIZE]; u8 head, tail; u8 count; }; struct serial_port { u8 uart_reg[8]; /* 8 registers */ struct rxtx rxtx; /* loop back buffer */ bool dlab; bool overrun; u16 divisor; u8 fcr; /* FIFO control register */ u8 max_fifo_size; u8 intr_trigger_level; /* interrupt trigger level */ }; /* State of each mdev device */ struct mdev_state { int irq_fd; struct eventfd_ctx *intx_evtfd; struct eventfd_ctx *msi_evtfd; int irq_index; u8 *vconfig; struct mutex ops_lock; struct mdev_device *mdev; struct mdev_region_info region_info[VFIO_PCI_NUM_REGIONS]; u32 bar_mask[VFIO_PCI_NUM_REGIONS]; struct list_head next; struct serial_port s[2]; struct mutex rxtx_lock; struct vfio_device_info dev_info; int nr_ports; }; struct mutex mdev_list_lock; struct list_head mdev_devices_list; static const struct file_operations vd_fops = { .owner = THIS_MODULE, }; /* function prototypes */ static int mtty_trigger_interrupt(uuid_le uuid); /* Helper functions */ static struct mdev_state *find_mdev_state_by_uuid(uuid_le uuid) { struct mdev_state *mds; list_for_each_entry(mds, &mdev_devices_list, next) { if (uuid_le_cmp(mdev_uuid(mds->mdev), uuid) == 0) return mds; } return NULL; } void dump_buffer(char *buf, uint32_t count) { #if defined(DEBUG) int i; pr_info("Buffer:\n"); for (i = 0; i < count; i++) { pr_info("%2x ", *(buf + i)); if ((i + 1) % 16 == 0) pr_info("\n"); } #endif } static void mtty_create_config_space(struct mdev_state *mdev_state) { /* PCI dev ID */ STORE_LE32((u32 *) &mdev_state->vconfig[0x0], 0x32534348); /* Control: I/O+, Mem-, BusMaster- */ STORE_LE16((u16 *) &mdev_state->vconfig[0x4], 0x0001); /* Status: capabilities list absent */ STORE_LE16((u16 *) &mdev_state->vconfig[0x6], 0x0200); /* Rev ID */ mdev_state->vconfig[0x8] = 0x10; /* programming interface class : 16550-compatible serial controller */ mdev_state->vconfig[0x9] = 0x02; /* Sub class : 00 */ mdev_state->vconfig[0xa] = 0x00; /* Base class : Simple Communication controllers */ mdev_state->vconfig[0xb] = 0x07; /* base address registers */ /* BAR0: IO space */ STORE_LE32((u32 *) &mdev_state->vconfig[0x10], 0x000001); mdev_state->bar_mask[0] = ~(MTTY_IO_BAR_SIZE) + 1; if (mdev_state->nr_ports == 2) { /* BAR1: IO space */ STORE_LE32((u32 *) &mdev_state->vconfig[0x14], 0x000001); mdev_state->bar_mask[1] = ~(MTTY_IO_BAR_SIZE) + 1; } /* Subsystem ID */ STORE_LE32((u32 *) &mdev_state->vconfig[0x2c], 0x32534348); mdev_state->vconfig[0x34] = 0x00; /* Cap Ptr */ mdev_state->vconfig[0x3d] = 0x01; /* interrupt pin (INTA#) */ /* Vendor specific data */ mdev_state->vconfig[0x40] = 0x23; mdev_state->vconfig[0x43] = 0x80; mdev_state->vconfig[0x44] = 0x23; mdev_state->vconfig[0x48] = 0x23; mdev_state->vconfig[0x4c] = 0x23; mdev_state->vconfig[0x60] = 0x50; mdev_state->vconfig[0x61] = 0x43; mdev_state->vconfig[0x62] = 0x49; mdev_state->vconfig[0x63] = 0x20; mdev_state->vconfig[0x64] = 0x53; mdev_state->vconfig[0x65] = 0x65; mdev_state->vconfig[0x66] = 0x72; mdev_state->vconfig[0x67] = 0x69; mdev_state->vconfig[0x68] = 0x61; mdev_state->vconfig[0x69] = 0x6c; mdev_state->vconfig[0x6a] = 0x2f; mdev_state->vconfig[0x6b] = 0x55; mdev_state->vconfig[0x6c] = 0x41; mdev_state->vconfig[0x6d] = 0x52; mdev_state->vconfig[0x6e] = 0x54; } static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset, char *buf, u32 count) { u32 cfg_addr, bar_mask, bar_index = 0; switch (offset) { case 0x04: /* device control */ case 0x06: /* device status */ /* do nothing */ break; case 0x3c: /* interrupt line */ mdev_state->vconfig[0x3c] = buf[0]; break; case 0x3d: /* * Interrupt Pin is hardwired to INTA. * This field is write protected by hardware */ break; case 0x10: /* BAR0 */ case 0x14: /* BAR1 */ if (offset == 0x10) bar_index = 0; else if (offset == 0x14) bar_index = 1; if ((mdev_state->nr_ports == 1) && (bar_index == 1)) { STORE_LE32(&mdev_state->vconfig[offset], 0); break; } cfg_addr = *(u32 *)buf; pr_info("BAR%d addr 0x%x\n", bar_index, cfg_addr); if (cfg_addr == 0xffffffff) { bar_mask = mdev_state->bar_mask[bar_index]; cfg_addr = (cfg_addr & bar_mask); } cfg_addr |= (mdev_state->vconfig[offset] & 0x3ul); STORE_LE32(&mdev_state->vconfig[offset], cfg_addr); break; case 0x18: /* BAR2 */ case 0x1c: /* BAR3 */ case 0x20: /* BAR4 */ STORE_LE32(&mdev_state->vconfig[offset], 0); break; default: pr_info("PCI config write @0x%x of %d bytes not handled\n", offset, count); break; } } static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state, u16 offset, char *buf, u32 count) { u8 data = *buf; /* Handle data written by guest */ switch (offset) { case UART_TX: /* if DLAB set, data is LSB of divisor */ if (mdev_state->s[index].dlab) { mdev_state->s[index].divisor |= data; break; } mutex_lock(&mdev_state->rxtx_lock); /* save in TX buffer */ if (mdev_state->s[index].rxtx.count < mdev_state->s[index].max_fifo_size) { mdev_state->s[index].rxtx.fifo[ mdev_state->s[index].rxtx.head] = data; mdev_state->s[index].rxtx.count++; CIRCULAR_BUF_INC_IDX(mdev_state->s[index].rxtx.head); mdev_state->s[index].overrun = false; /* * Trigger interrupt if receive data interrupt is * enabled and fifo reached trigger level */ if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_RDI) && (mdev_state->s[index].rxtx.count == mdev_state->s[index].intr_trigger_level)) { /* trigger interrupt */ #if defined(DEBUG_INTR) pr_err("Serial port %d: Fifo level trigger\n", index); #endif mtty_trigger_interrupt( mdev_uuid(mdev_state->mdev)); } } else { #if defined(DEBUG_INTR) pr_err("Serial port %d: Buffer Overflow\n", index); #endif mdev_state->s[index].overrun = true; /* * Trigger interrupt if receiver line status interrupt * is enabled */ if (mdev_state->s[index].uart_reg[UART_IER] & UART_IER_RLSI) mtty_trigger_interrupt( mdev_uuid(mdev_state->mdev)); } mutex_unlock(&mdev_state->rxtx_lock); break; case UART_IER: /* if DLAB set, data is MSB of divisor */ if (mdev_state->s[index].dlab) mdev_state->s[index].divisor |= (u16)data << 8; else { mdev_state->s[index].uart_reg[offset] = data; mutex_lock(&mdev_state->rxtx_lock); if ((data & UART_IER_THRI) && (mdev_state->s[index].rxtx.head == mdev_state->s[index].rxtx.tail)) { #if defined(DEBUG_INTR) pr_err("Serial port %d: IER_THRI write\n", index); #endif mtty_trigger_interrupt( mdev_uuid(mdev_state->mdev)); } mutex_unlock(&mdev_state->rxtx_lock); } break; case UART_FCR: mdev_state->s[index].fcr = data; mutex_lock(&mdev_state->rxtx_lock); if (data & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)) { /* clear loop back FIFO */ mdev_state->s[index].rxtx.count = 0; mdev_state->s[index].rxtx.head = 0; mdev_state->s[index].rxtx.tail = 0; } mutex_unlock(&mdev_state->rxtx_lock); switch (data & UART_FCR_TRIGGER_MASK) { case UART_FCR_TRIGGER_1: mdev_state->s[index].intr_trigger_level = 1; break; case UART_FCR_TRIGGER_4: mdev_state->s[index].intr_trigger_level = 4; break; case UART_FCR_TRIGGER_8: mdev_state->s[index].intr_trigger_level = 8; break; case UART_FCR_TRIGGER_14: mdev_state->s[index].intr_trigger_level = 14; break; } /* * Set trigger level to 1 otherwise or implement timer with * timeout of 4 characters and on expiring that timer set * Recevice data timeout in IIR register */ mdev_state->s[index].intr_trigger_level = 1; if (data & UART_FCR_ENABLE_FIFO) mdev_state->s[index].max_fifo_size = MAX_FIFO_SIZE; else { mdev_state->s[index].max_fifo_size = 1; mdev_state->s[index].intr_trigger_level = 1; } break; case UART_LCR: if (data & UART_LCR_DLAB) { mdev_state->s[index].dlab = true; mdev_state->s[index].divisor = 0; } else mdev_state->s[index].dlab = false; mdev_state->s[index].uart_reg[offset] = data; break; case UART_MCR: mdev_state->s[index].uart_reg[offset] = data; if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_MSI) && (data & UART_MCR_OUT2)) { #if defined(DEBUG_INTR) pr_err("Serial port %d: MCR_OUT2 write\n", index); #endif mtty_trigger_interrupt(mdev_uuid(mdev_state->mdev)); } if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_MSI) && (data & (UART_MCR_RTS | UART_MCR_DTR))) { #if defined(DEBUG_INTR) pr_err("Serial port %d: MCR RTS/DTR write\n", index); #endif mtty_trigger_interrupt(mdev_uuid(mdev_state->mdev)); } break; case UART_LSR: case UART_MSR: /* do nothing */ break; case UART_SCR: mdev_state->s[index].uart_reg[offset] = data; break; default: break; } } static void handle_bar_read(unsigned int index, struct mdev_state *mdev_state, u16 offset, char *buf, u32 count) { /* Handle read requests by guest */ switch (offset) { case UART_RX: /* if DLAB set, data is LSB of divisor */ if (mdev_state->s[index].dlab) { *buf = (u8)mdev_state->s[index].divisor; break; } mutex_lock(&mdev_state->rxtx_lock); /* return data in tx buffer */ if (mdev_state->s[index].rxtx.head != mdev_state->s[index].rxtx.tail) { *buf = mdev_state->s[index].rxtx.fifo[ mdev_state->s[index].rxtx.tail]; mdev_state->s[index].rxtx.count--; CIRCULAR_BUF_INC_IDX(mdev_state->s[index].rxtx.tail); } if (mdev_state->s[index].rxtx.head == mdev_state->s[index].rxtx.tail) {