cregit-Linux how code gets into the kernel

Release 4.14 drivers/tty/vcc.c

Directory: drivers/tty
/* vcc.c: sun4v virtual channel concentrator
 *
 * Copyright (C) 2017 Oracle. All rights reserved.
 */

#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <asm/vio.h>
#include <asm/ldc.h>


#define DRV_MODULE_NAME		"vcc"

#define DRV_MODULE_VERSION	"1.1"

#define DRV_MODULE_RELDATE	"July 1, 2017"


static char version[] =
	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")";

MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver");
MODULE_LICENSE("GPL");

MODULE_VERSION(DRV_MODULE_VERSION);


struct vcc_port {
	
struct vio_driver_state vio;

	
spinlock_t lock;
	
char *domain;
	
struct tty_struct *tty;	/* only populated while dev is open */
	
unsigned long index;	/* index into the vcc_table */

	
u64 refcnt;
	
bool excl_locked;

	
bool removed;

	/* This buffer is required to support the tty write_room interface
         * and guarantee that any characters that the driver accepts will
         * be eventually sent, either immediately or later.
         */
	
int chars_in_buffer;
	
struct vio_vcc buffer;

	
struct timer_list rx_timer;
	
struct timer_list tx_timer;
};

/* Microseconds that thread will delay waiting for a vcc port ref */

#define VCC_REF_DELAY		100


#define VCC_MAX_PORTS		1024

#define VCC_MINOR_START		0	
/* must be zero */

#define VCC_BUFF_LEN		VIO_VCC_MTU_SIZE


#define VCC_CTL_BREAK		-1

#define VCC_CTL_HUP		-2


static const char vcc_driver_name[] = "vcc";

static const char vcc_device_node[] = "vcc";

static struct tty_driver *vcc_tty_driver;


static struct vcc_port *vcc_table[VCC_MAX_PORTS];
static DEFINE_SPINLOCK(vcc_table_lock);


int vcc_dbg;

int vcc_dbg_ldc;

int vcc_dbg_vio;

module_param(vcc_dbg, uint, 0664);
module_param(vcc_dbg_ldc, uint, 0664);
module_param(vcc_dbg_vio, uint, 0664);


#define VCC_DBG_DRV	0x1

#define VCC_DBG_LDC	0x2

#define VCC_DBG_PKT	0x4


#define vccdbg(f, a...)						\
	do {                                                    \
                if (vcc_dbg & VCC_DBG_DRV)                      \
                        pr_info(f, ## a);                       \
        } while (0)                                             \

#define vccdbgl(l)                                              \
        do {                                                    \
                if (vcc_dbg & VCC_DBG_LDC)                      \
                        ldc_print(l);                           \
        } while (0)                                             \

#define vccdbgp(pkt)                                            \
        do {                                                    \
                if (vcc_dbg & VCC_DBG_PKT) {                    \
                        int i;                                  \
                        for (i = 0; i < pkt.tag.stype; i++)     \
                                pr_info("[%c]", pkt.data[i]);   \
                }                                               \
        } while (0)						\

/* Note: Be careful when adding flags to this line discipline.  Don't
 * add anything that will cause echoing or we'll go into recursive
 * loop echoing chars back and forth with the console drivers.
 */

static const struct ktermios vcc_tty_termios = {
	.c_iflag = IGNBRK | IGNPAR,
	.c_oflag = OPOST,
	.c_cflag = B38400 | CS8 | CREAD | HUPCL,
	.c_cc = INIT_C_CC,
	.c_ispeed = 38400,
	.c_ospeed = 38400
};

/**
 * vcc_table_add() - Add VCC port to the VCC table
 * @port: pointer to the VCC port
 *
 * Return: index of the port in the VCC table on success,
 *         -1 on failure
 */

static int vcc_table_add(struct vcc_port *port) { unsigned long flags; int i; spin_lock_irqsave(&vcc_table_lock, flags); for (i = VCC_MINOR_START; i < VCC_MAX_PORTS; i++) { if (!vcc_table[i]) { vcc_table[i] = port; break; } } spin_unlock_irqrestore(&vcc_table_lock, flags); if (i < VCC_MAX_PORTS) return i; else return -1; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman81100.00%3100.00%
Total81100.00%3100.00%

/** * vcc_table_remove() - Removes a VCC port from the VCC table * @index: Index into the VCC table */
static void vcc_table_remove(unsigned long index) { unsigned long flags; if (WARN_ON(index >= VCC_MAX_PORTS)) return; spin_lock_irqsave(&vcc_table_lock, flags); vcc_table[index] = NULL; spin_unlock_irqrestore(&vcc_table_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman47100.00%2100.00%
Total47100.00%2100.00%

/** * vcc_get() - Gets a reference to VCC port * @index: Index into the VCC table * @excl: Indicates if an exclusive access is requested * * Return: reference to the VCC port, if found * NULL, if port not found */
static struct vcc_port *vcc_get(unsigned long index, bool excl) { struct vcc_port *port; unsigned long flags; try_again: spin_lock_irqsave(&vcc_table_lock, flags); port = vcc_table[index]; if (!port) { spin_unlock_irqrestore(&vcc_table_lock, flags); return NULL; } if (!excl) { if (port->excl_locked) { spin_unlock_irqrestore(&vcc_table_lock, flags); udelay(VCC_REF_DELAY); goto try_again; } port->refcnt++; spin_unlock_irqrestore(&vcc_table_lock, flags); return port; } if (port->refcnt) { spin_unlock_irqrestore(&vcc_table_lock, flags); /* Threads wanting exclusive access will wait half the time, * probably giving them higher priority in the case of * multiple waiters. */ udelay(VCC_REF_DELAY/2); goto try_again; } port->refcnt++; port->excl_locked = true; spin_unlock_irqrestore(&vcc_table_lock, flags); return port; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman155100.00%2100.00%
Total155100.00%2100.00%

/** * vcc_put() - Returns a reference to VCC port * @port: pointer to VCC port * @excl: Indicates if the returned reference is an exclusive reference * * Note: It's the caller's responsibility to ensure the correct value * for the excl flag */
static void vcc_put(struct vcc_port *port, bool excl) { unsigned long flags; if (!port) return; spin_lock_irqsave(&vcc_table_lock, flags); /* check if caller attempted to put with the wrong flags */ if (WARN_ON((excl && !port->excl_locked) || (!excl && port->excl_locked))) goto done; port->refcnt--; if (excl) port->excl_locked = false; done: spin_unlock_irqrestore(&vcc_table_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman84100.00%2100.00%
Total84100.00%2100.00%

/** * vcc_get_ne() - Get a non-exclusive reference to VCC port * @index: Index into the VCC table * * Gets a non-exclusive reference to VCC port, if it's not removed * * Return: pointer to the VCC port, if found * NULL, if port not found */
static struct vcc_port *vcc_get_ne(unsigned long index) { struct vcc_port *port; port = vcc_get(index, false); if (port && port->removed) { vcc_put(port, false); return NULL; } return port; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman49100.00%1100.00%
Total49100.00%1100.00%


static void vcc_kick_rx(struct vcc_port *port) { struct vio_driver_state *vio = &port->vio; assert_spin_locked(&port->lock); if (!timer_pending(&port->rx_timer) && !port->removed) { disable_irq_nosync(vio->vdev->rx_irq); port->rx_timer.expires = (jiffies + 1); add_timer(&port->rx_timer); } }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman76100.00%1100.00%
Total76100.00%1100.00%


static void vcc_kick_tx(struct vcc_port *port) { assert_spin_locked(&port->lock); if (!timer_pending(&port->tx_timer) && !port->removed) { port->tx_timer.expires = (jiffies + 1); add_timer(&port->tx_timer); } }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman57100.00%1100.00%
Total57100.00%1100.00%


static int vcc_rx_check(struct tty_struct *tty, int size) { if (WARN_ON(!tty || !tty->port)) return 1; /* tty_buffer_request_room won't sleep because it uses * GFP_ATOMIC flag to allocate buffer */ if (test_bit(TTY_THROTTLED, &tty->flags) || (tty_buffer_request_room(tty->port, VCC_BUFF_LEN) < VCC_BUFF_LEN)) return 0; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman62100.00%1100.00%
Total62100.00%1100.00%


static int vcc_rx(struct tty_struct *tty, char *buf, int size) { int len = 0; if (WARN_ON(!tty || !tty->port)) return len; len = tty_insert_flip_string(tty->port, buf, size); if (len) tty_flip_buffer_push(tty->port); return len; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman66100.00%1100.00%
Total66100.00%1100.00%


static int vcc_ldc_read(struct vcc_port *port) { struct vio_driver_state *vio = &port->vio; struct tty_struct *tty; struct vio_vcc pkt; int rv = 0; tty = port->tty; if (!tty) { rv = ldc_rx_reset(vio->lp); vccdbg("VCC: reset rx q: rv=%d\n", rv); goto done; } /* Read as long as LDC has incoming data. */ while (1) { if (!vcc_rx_check(tty, VIO_VCC_MTU_SIZE)) { vcc_kick_rx(port); break; } vccdbgl(vio->lp); rv = ldc_read(vio->lp, &pkt, sizeof(pkt)); if (rv <= 0) break; vccdbg("VCC: ldc_read()=%d\n", rv); vccdbg("TAG [%02x:%02x:%04x:%08x]\n", pkt.tag.type, pkt.tag.stype, pkt.tag.stype_env, pkt.tag.sid); if (pkt.tag.type == VIO_TYPE_DATA) { vccdbgp(pkt); /* vcc_rx_check ensures memory availability */ vcc_rx(tty, pkt.data, pkt.tag.stype); } else { pr_err("VCC: unknown msg [%02x:%02x:%04x:%08x]\n", pkt.tag.type, pkt.tag.stype, pkt.tag.stype_env, pkt.tag.sid); rv = -ECONNRESET; break; } WARN_ON(rv != LDC_PACKET_SIZE); } done: return rv; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman242100.00%1100.00%
Total242100.00%1100.00%


static void vcc_rx_timer(unsigned long index) { struct vio_driver_state *vio; struct vcc_port *port; unsigned long flags; int rv; port = vcc_get_ne(index); if (!port) return; spin_lock_irqsave(&port->lock, flags); port->rx_timer.expires = 0; vio = &port->vio; enable_irq(vio->vdev->rx_irq); if (!port->tty || port->removed) goto done; rv = vcc_ldc_read(port); if (rv == -ECONNRESET) vio_conn_reset(vio); done: spin_unlock_irqrestore(&port->lock, flags); vcc_put(port, false); }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman126100.00%1100.00%
Total126100.00%1100.00%


static void vcc_tx_timer(unsigned long index) { struct vcc_port *port; struct vio_vcc *pkt; unsigned long flags; int tosend = 0; int rv; port = vcc_get_ne(index); if (!port) return; spin_lock_irqsave(&port->lock, flags); port->tx_timer.expires = 0; if (!port->tty || port->removed) goto done; tosend = min(VCC_BUFF_LEN, port->chars_in_buffer); if (!tosend) goto done; pkt = &port->buffer; pkt->tag.type = VIO_TYPE_DATA; pkt->tag.stype = tosend; vccdbgl(port->vio.lp); rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); WARN_ON(!rv); if (rv < 0) { vccdbg("VCC: ldc_write()=%d\n", rv); vcc_kick_tx(port); } else { struct tty_struct *tty = port->tty; port->chars_in_buffer = 0; if (tty) tty_wakeup(tty); } done: spin_unlock_irqrestore(&port->lock, flags); vcc_put(port, false); }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman219100.00%2100.00%
Total219100.00%2100.00%

/** * vcc_event() - LDC event processing engine * @arg: VCC private data * @event: LDC event * * Handles LDC events for VCC */
static void vcc_event(void *arg, int event) { struct vio_driver_state *vio; struct vcc_port *port; unsigned long flags; int rv; port = arg; vio = &port->vio; spin_lock_irqsave(&port->lock, flags); switch (event) { case LDC_EVENT_RESET: case LDC_EVENT_UP: vio_link_state_change(vio, event); break; case LDC_EVENT_DATA_READY: rv = vcc_ldc_read(port); if (rv == -ECONNRESET) vio_conn_reset(vio); break; default: pr_err("VCC: unexpected LDC event(%d)\n", event); } spin_unlock_irqrestore(&port->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman112100.00%2100.00%
Total112100.00%2100.00%

static struct ldc_channel_config vcc_ldc_cfg = { .event = vcc_event, .mtu = VIO_VCC_MTU_SIZE, .mode = LDC_MODE_RAW, .debug = 0, }; /* Ordered from largest major to lowest */ static struct vio_version vcc_versions[] = { { .major = 1, .minor = 0 }, }; static struct tty_port_operations vcc_port_ops = { 0 };
static ssize_t vcc_sysfs_domain_show(struct device *dev, struct device_attribute *attr, char *buf) { struct vcc_port *port; int rv; port = dev_get_drvdata(dev); if (!port) return -ENODEV; rv = scnprintf(buf, PAGE_SIZE, "%s\n", port->domain); return rv; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman62100.00%1100.00%
Total62100.00%1100.00%


static int vcc_send_ctl(struct vcc_port *port, int ctl) { struct vio_vcc pkt; int rv; pkt.tag.type = VIO_TYPE_CTRL; pkt.tag.sid = ctl; pkt.tag.stype = 0; rv = ldc_write(port->vio.lp, &pkt, sizeof(pkt.tag)); WARN_ON(!rv); vccdbg("VCC: ldc_write(%ld)=%d\n", sizeof(pkt.tag), rv); return rv; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman89100.00%1100.00%
Total89100.00%1100.00%


static ssize_t vcc_sysfs_break_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct vcc_port *port; unsigned long flags; int rv = count; int brk; port = dev_get_drvdata(dev); if (!port) return -ENODEV; spin_lock_irqsave(&port->lock, flags); if (sscanf(buf, "%ud", &brk) != 1 || brk != 1) rv = -EINVAL; else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) vcc_kick_tx(port); spin_unlock_irqrestore(&port->lock, flags); return rv; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman120100.00%2100.00%
Total120100.00%2100.00%

static DEVICE_ATTR(domain, 0400, vcc_sysfs_domain_show, NULL); static DEVICE_ATTR(break, 0200, NULL, vcc_sysfs_break_store); static struct attribute *vcc_sysfs_entries[] = { &dev_attr_domain.attr, &dev_attr_break.attr, NULL }; static struct attribute_group vcc_attribute_group = { .name = NULL, .attrs = vcc_sysfs_entries, }; /** * vcc_probe() - Initialize VCC port * @vdev: Pointer to VIO device of the new VCC port * @id: VIO device ID * * Initializes a VCC port to receive serial console data from * the guest domain. Sets up a TTY end point on the control * domain. Sets up VIO/LDC link between the guest & control * domain endpoints. * * Return: status of the probe */
static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) { struct mdesc_handle *hp; struct vcc_port *port; struct device *dev; const char *domain; char *name; u64 node; int rv; vccdbg("VCC: name=%s\n", dev_name(&vdev->dev)); if (!vcc_tty_driver) { pr_err("VCC: TTY driver not registered\n"); return -ENODEV; } port = kzalloc(sizeof(struct vcc_port), GFP_KERNEL); if (!port) return -ENOMEM; name = kstrdup(dev_name(&vdev->dev), GFP_KERNEL); rv = vio_driver_init(&port->vio, vdev, VDEV_CONSOLE_CON, vcc_versions, ARRAY_SIZE(vcc_versions), NULL, name); if (rv) goto free_port; port->vio.debug = vcc_dbg_vio; vcc_ldc_cfg.debug = vcc_dbg_ldc; rv = vio_ldc_alloc(&port->vio, &vcc_ldc_cfg, port); if (rv) goto free_port; spin_lock_init(&port->lock); port->index = vcc_table_add(port); if (port->index == -1) { pr_err("VCC: no more TTY indices left for allocation\n"); goto free_ldc; } /* Register the device using VCC table index as TTY index */ dev = tty_register_device(vcc_tty_driver, port->index, &vdev->dev); if (IS_ERR(dev)) { rv = PTR_ERR(dev); goto free_table; } hp = mdesc_grab(); node = vio_vdev_node(hp, vdev); if (node == MDESC_NODE_NULL) { rv = -ENXIO; mdesc_release(hp); goto unreg_tty; } domain = mdesc_get_property(hp, node, "vcc-domain-name", NULL); if (!domain) { rv = -ENXIO; mdesc_release(hp); goto unreg_tty; } port->domain = kstrdup(domain, GFP_KERNEL); mdesc_release(hp); rv = sysfs_create_group(&vdev->dev.kobj, &vcc_attribute_group); if (rv) goto free_domain; init_timer(&port->rx_timer); port->rx_timer.function = vcc_rx_timer; port->rx_timer.data = port->index; init_timer(&port->tx_timer); port->tx_timer.function = vcc_tx_timer; port->tx_timer.data = port->index; dev_set_drvdata(&vdev->dev, port); /* It's possible to receive IRQs in the middle of vio_port_up. Disable * IRQs until the port is up. */ disable_irq_nosync(vdev->rx_irq); vio_port_up(&port->vio); enable_irq(vdev->rx_irq); return 0; free_domain: kfree(port->domain); unreg_tty: tty_unregister_device(vcc_tty_driver, port->index); free_table: vcc_table_remove(port->index); free_ldc: vio_ldc_free(&port->vio); free_port: kfree(name); kfree(port); return rv; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman501100.00%3100.00%
Total501100.00%3100.00%

/** * vcc_remove() - Terminate a VCC port * @vdev: Pointer to VIO device of the VCC port * * Terminates a VCC port. Sets up the teardown of TTY and * VIO/LDC link between guest and primary domains. * * Return: status of removal */
static int vcc_remove(struct vio_dev *vdev) { struct vcc_port *port = dev_get_drvdata(&vdev->dev); if (!port) return -ENODEV; del_timer_sync(&port->rx_timer); del_timer_sync(&port->tx_timer); /* If there's a process with the device open, do a synchronous * hangup of the TTY. This *may* cause the process to call close * asynchronously, but it's not guaranteed. */ if (port->tty) tty_vhangup(port->tty); /* Get exclusive reference to VCC, ensures that there are no other * clients to this port */ port = vcc_get(port->index, true); if (WARN_ON(!port)) return -ENODEV; tty_unregister_device(vcc_tty_driver, port->index); del_timer_sync(&port->vio.timer); vio_ldc_free(&port->vio); sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); dev_set_drvdata(&vdev->dev, NULL); if (port->tty) { port->removed = true; vcc_put(port, true); } else { vcc_table_remove(port->index); kfree(port->vio.name); kfree(port->domain); kfree(port); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman192100.00%3100.00%
Total192100.00%3100.00%

static const struct vio_device_id vcc_match[] = { { .type = "vcc-port", }, {}, }; MODULE_DEVICE_TABLE(vio, vcc_match); static struct vio_driver vcc_driver = { .id_table = vcc_match, .probe = vcc_probe, .remove = vcc_remove, .name = "vcc", };
static int vcc_open(struct tty_struct *tty, struct file *vcc_file) { struct vcc_port *port; if (unlikely(!tty)) { pr_err("VCC: open: Invalid TTY handle\n"); return -ENXIO; } if (tty->count > 1) return -EBUSY; port = vcc_get_ne(tty->index); if (unlikely(!port)) { pr_err("VCC: open: Failed to find VCC port\n"); return -ENODEV; } if (unlikely(!port->vio.lp)) { pr_err("VCC: open: LDC channel not configured\n"); vcc_put(port, false); return -EPIPE; } vccdbgl(port->vio.lp); vcc_put(port, false); if (unlikely(!tty->port)) { pr_err("VCC: open: TTY port not found\n"); return -ENXIO; } if (unlikely(!tty->port->ops)) { pr_err("VCC: open: TTY ops not defined\n"); return -ENXIO; } return tty_port_open(tty->port, tty, vcc_file); }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman182100.00%1100.00%
Total182100.00%1100.00%


static void vcc_close(struct tty_struct *tty, struct file *vcc_file) { if (unlikely(!tty)) { pr_err("VCC: close: Invalid TTY handle\n"); return; } if (unlikely(tty->count > 1)) return; if (unlikely(!tty->port)) { pr_err("VCC: close: TTY port not found\n"); return; } tty_port_close(tty->port, tty, vcc_file); }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman73100.00%1100.00%
Total73100.00%1100.00%


static void vcc_ldc_hup(struct vcc_port *port) { unsigned long flags; spin_lock_irqsave(&port->lock, flags); if (vcc_send_ctl(port, VCC_CTL_HUP) < 0) vcc_kick_tx(port); spin_unlock_irqrestore(&port->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman51100.00%1100.00%
Total51100.00%1100.00%


static void vcc_hangup(struct tty_struct *tty) { struct vcc_port *port; if (unlikely(!tty)) { pr_err("VCC: hangup: Invalid TTY handle\n"); return; } port = vcc_get_ne(tty->index); if (unlikely(!port)) { pr_err("VCC: hangup: Failed to find VCC port\n"); return; } if (unlikely(!tty->port)) { pr_err("VCC: hangup: TTY port not found\n"); vcc_put(port, false); return; } vcc_ldc_hup(port); vcc_put(port, false); tty_port_hangup(tty->port); }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman101100.00%1100.00%
Total101100.00%1100.00%


static int vcc_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct vcc_port *port; struct vio_vcc *pkt; unsigned long flags; int total_sent = 0; int tosend = 0; int rv = -EINVAL; if (unlikely(!tty)) { pr_err("VCC: write: Invalid TTY handle\n"); return -ENXIO; } port = vcc_get_ne(tty->index); if (unlikely(!port)) { pr_err("VCC: write: Failed to find VCC port"); return -ENODEV; } spin_lock_irqsave(&port->lock, flags); pkt = &port->buffer; pkt->tag.type = VIO_TYPE_DATA; while (count > 0) { /* Minimum of data to write and space available */ tosend = min(count, (VCC_BUFF_LEN - port->chars_in_buffer)); if (!tosend) break; memcpy(&pkt->data[port->chars_in_buffer], &buf[total_sent], tosend); port->chars_in_buffer += tosend; pkt->tag.stype = tosend; vccdbg("TAG [%02x:%02x:%04x:%08x]\n", pkt->tag.type, pkt->tag.stype, pkt->tag.stype_env, pkt->tag.sid); vccdbg("DATA [%s]\n", pkt->data); vccdbgl(port->vio.lp); /* Since we know we have enough room in VCC buffer for tosend * we record that it was sent regardless of whether the * hypervisor actually took it because we have it buffered. */ rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); vccdbg("VCC: write: ldc_write(%d)=%d\n", (VIO_TAG_SIZE + tosend), rv); total_sent += tosend; count -= tosend; if (rv < 0) { vcc_kick_tx(port); break; } port->chars_in_buffer = 0; } spin_unlock_irqrestore(&port->lock, flags); vcc_put(port, false); vccdbg("VCC: write: total=%d rv=%d", total_sent, rv); return total_sent ? total_sent : rv; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman328100.00%1100.00%
Total328100.00%1100.00%


static int vcc_write_room(struct tty_struct *tty) { struct vcc_port *port; u64 num; if (unlikely(!tty)) { pr_err("VCC: write_room: Invalid TTY handle\n"); return -ENXIO; } port = vcc_get_ne(tty->index); if (unlikely(!port)) { pr_err("VCC: write_room: Failed to find VCC port\n"); return -ENODEV; } num = VCC_BUFF_LEN - port->chars_in_buffer; vcc_put(port, false); return num; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman84100.00%1100.00%
Total84100.00%1100.00%


static int vcc_chars_in_buffer(struct tty_struct *tty) { struct vcc_port *port; u64 num; if (unlikely(!tty)) { pr_err("VCC: chars_in_buffer: Invalid TTY handle\n"); return -ENXIO; } port = vcc_get_ne(tty->index); if (unlikely(!port)) { pr_err("VCC: chars_in_buffer: Failed to find VCC port\n"); return -ENODEV; } num = port->chars_in_buffer; vcc_put(port, false); return num; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman82100.00%1100.00%
Total82100.00%1100.00%


static int vcc_break_ctl(struct tty_struct *tty, int state) { struct vcc_port *port; unsigned long flags; if (unlikely(!tty)) { pr_err("VCC: break_ctl: Invalid TTY handle\n"); return -ENXIO; } port = vcc_get_ne(tty->index); if (unlikely(!port)) { pr_err("VCC: break_ctl: Failed to find VCC port\n"); return -ENODEV; } /* Turn off break */ if (state == 0) { vcc_put(port, false); return 0; } spin_lock_irqsave(&port->lock, flags); if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) vcc_kick_tx(port); spin_unlock_irqrestore(&port->lock, flags); vcc_put(port, false); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman135100.00%1100.00%
Total135100.00%1100.00%


static int vcc_install(struct tty_driver *driver, struct tty_struct *tty) { struct vcc_port *port_vcc; struct tty_port *port_tty; int ret; if (unlikely(!tty)) { pr_err("VCC: install: Invalid TTY handle\n"); return -ENXIO; } if (tty->index >= VCC_MAX_PORTS) return -EINVAL; ret = tty_standard_install(driver, tty); if (ret) return ret; port_tty = kzalloc(sizeof(struct tty_port), GFP_KERNEL); if (!port_tty) return -ENOMEM; port_vcc = vcc_get(tty->index, true); if (!port_vcc) { pr_err("VCC: install: Failed to find VCC port\n"); tty->port = NULL; kfree(port_tty); return -ENODEV; } tty_port_init(port_tty); port_tty->ops = &vcc_port_ops; tty->port = port_tty; port_vcc->tty = tty; vcc_put(port_vcc, true); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman170100.00%1100.00%
Total170100.00%1100.00%


static void vcc_cleanup(struct tty_struct *tty) { struct vcc_port *port; if (unlikely(!tty)) { pr_err("VCC: cleanup: Invalid TTY handle\n"); return; } port = vcc_get(tty->index, true); if (port) { port->tty = NULL; if (port->removed) { vcc_table_remove(tty->index); kfree(port->vio.name); kfree(port->domain); kfree(port); } else { vcc_put(port, true); } } tty_port_destroy(tty->port); kfree(tty->port); tty->port = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman121100.00%1100.00%
Total121100.00%1100.00%

static const struct tty_operations vcc_ops = { .open = vcc_open, .close = vcc_close, .hangup = vcc_hangup, .write = vcc_write, .write_room = vcc_write_room, .chars_in_buffer = vcc_chars_in_buffer, .break_ctl = vcc_break_ctl, .install = vcc_install, .cleanup = vcc_cleanup, }; #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW)
static int vcc_tty_init(void) { int rv; pr_info("VCC: %s\n", version); vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS); if (IS_ERR(vcc_tty_driver)) { pr_err("VCC: TTY driver alloc failed\n"); return PTR_ERR(vcc_tty_driver); } vcc_tty_driver->driver_name = vcc_driver_name; vcc_tty_driver->name = vcc_device_node; vcc_tty_driver->minor_start = VCC_MINOR_START; vcc_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; vcc_tty_driver->init_termios = vcc_tty_termios; tty_set_operations(vcc_tty_driver, &vcc_ops); rv = tty_register_driver(vcc_tty_driver); if (rv) { pr_err("VCC: TTY driver registration failed\n"); put_tty_driver(vcc_tty_driver); vcc_tty_driver = NULL; return rv; } vccdbg("VCC: TTY driver registered\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman11694.31%150.00%
Dan Carpenter75.69%150.00%
Total123100.00%2100.00%


static void vcc_tty_exit(void) { tty_unregister_driver(vcc_tty_driver); put_tty_driver(vcc_tty_driver); vccdbg("VCC: TTY driver unregistered\n"); vcc_tty_driver = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman27100.00%1100.00%
Total27100.00%1100.00%


static int __init vcc_init(void) { int rv; rv = vcc_tty_init(); if (rv) { pr_err("VCC: TTY init failed\n"); return rv; } rv = vio_register_driver(&vcc_driver); if (rv) { pr_err("VCC: VIO driver registration failed\n"); vcc_tty_exit(); } else { vccdbg("VCC: VIO driver registered successfully\n"); } return rv; }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman64100.00%1100.00%
Total64100.00%1100.00%


static void __exit vcc_exit(void) { vio_unregister_driver(&vcc_driver); vccdbg("VCC: VIO driver unregistered\n"); vcc_tty_exit(); vccdbg("VCC: TTY driver unregistered\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Jag Raman28100.00%3100.00%
Total28100.00%3100.00%

module_init(vcc_init); module_exit(vcc_exit);

Overall Contributors

PersonTokensPropCommitsCommitProp
Jag Raman443699.82%1386.67%
Dan Carpenter70.16%16.67%
Bhumika Goyal10.02%16.67%
Total4444100.00%15100.00%
Directory: drivers/tty
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.