From 8e4bff4cab0ddac6060645b0715210484d02ff40 Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Wed, 30 Aug 2017 08:25:59 +0000 Subject: Initial file dump from open source release --- drivers/misc/gpsip/VirtualCom/virt_uart.c | 666 ++++++++++++++++++++++++++++++ 1 file changed, 666 insertions(+) create mode 100644 drivers/misc/gpsip/VirtualCom/virt_uart.c (limited to 'drivers/misc/gpsip/VirtualCom/virt_uart.c') diff --git a/drivers/misc/gpsip/VirtualCom/virt_uart.c b/drivers/misc/gpsip/VirtualCom/virt_uart.c new file mode 100644 index 00000000..780fa454 --- /dev/null +++ b/drivers/misc/gpsip/VirtualCom/virt_uart.c @@ -0,0 +1,666 @@ +/****************************************************************************** + * LEGAL NOTICE * + * * + * USE OF THIS SOFTWARE (including any copy or compiled version thereof) AND * + * DOCUMENTATION IS SUBJECT TO THE SOFTWARE LICENSE AND RESTRICTIONS AND THE * + * WARRANTY DISLCAIMER SET FORTH IN LEGAL_NOTICE.TXT FILE. IF YOU DO NOT * + * FULLY ACCEPT THE TERMS, YOU MAY NOT INSTALL OR OTHERWISE USE THE SOFTWARE * + * OR DOCUMENTATION. * + * NOTWITHSTANDING ANYTHING TO THE CONTRARY IN THIS NOTICE, INSTALLING OR * + * OTHERISE USING THE SOFTWARE OR DOCUMENTATION INDICATES YOUR ACCEPTANCE OF * + * THE LICENSE TERMS AS STATED. * + * * + ******************************************************************************/ +/* Version: 1.8.9\3686 */ +/* Build : 13 */ +/* Date : 12/08/2012 */ + +#define __VIRT_UART_C__ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "virt_uart" +#define DRIVER_NAME "virt_uart" +#define DEVICE_NAME "ttyV" + +/* 0 = dynamic major */ +#define XVU_MAJOR 0 + +/* Number of Virtual UART pairs */ +#define XVU_PAIRS 1 + +#define XVU_ERR( fmt, arg...) do { printk(KERN_ERR MODULE_NAME " %s:%d [%s]... " fmt, __FILE__, __LINE__, __func__, ## arg ); } while (0) + +#if defined(CGTEST) || defined(DEBUG) +# define XVU_DBG( fmt, arg...) do { printk(KERN_ERR MODULE_NAME " %s:%d [%s]... " fmt, __FILE__, __LINE__, __func__, ## arg ); } while (0) +#else +# define XVU_DBG( fmt, arg...) do { } while (0) +#endif + +typedef struct xvu_endpoint_s { + int open_count; + struct tty_struct *tty; + int mcr; /* Modem Control Register */ + int msr; /* Modem Status Register */ + wait_queue_head_t msr_wait; + spinlock_t sercnt_lock; + struct serial_icounter_struct ser_cnt; +} xvu_endpoint_t; + +typedef struct xvu_dev_s { + struct semaphore sem; + xvu_endpoint_t endpoint[XVU_PAIRS * 2]; +} xvu_dev_t; + + +static struct tty_driver *xvu_ttydriver = NULL; +static xvu_dev_t xvu_dev; +static struct tty_port xvu_port[2]; + + +#define __XVU_GetPeerIdx(idx) ( (idx) ^ 0x01 ) + +//bxd +#define init_MUTEX(sem) sema_init(sem, 1) + +/* + * __XVU_SetMSR + * + * This sets the MSR (Modem Status Register), upon its peer's MCR (Modem Control + * Register). + * It also updates counters accordingly, and awakes potential TIOCMIWAITers. + * **** It should be called with the xvu_dev.sem held ! **** + */ +static void __XVU_SetMSR( unsigned int idx ) +{ + unsigned int peermcr = 0; + unsigned int oldmsr = 0; + unsigned int msr = 0; + unsigned long flags = 0; + + peermcr = xvu_dev.endpoint[__XVU_GetPeerIdx(idx)].mcr; + msr = oldmsr = xvu_dev.endpoint[idx].msr; + + XVU_DBG( "msr[%d] = 0x%08x\n", idx, msr ); + + /* Peer is closed, set msr to 0 */ + if ( !xvu_dev.endpoint[__XVU_GetPeerIdx(idx)].open_count) { + XVU_DBG( "Peer(%d) is closed... Setting msr as 0\n", __XVU_GetPeerIdx(idx) ); + msr = 0; + goto out; + } + + /* + * Peer is open. + * Update MSR upon peer's MCR + */ + if ( peermcr & TIOCM_DTR ) + msr |= (TIOCM_DSR | TIOCM_CAR); + else + msr &= ~(TIOCM_DSR | TIOCM_CAR); + + if ( peermcr & TIOCM_RTS ) + msr |= (TIOCM_CTS); + else + msr &= ~(TIOCM_CTS); + +out: + xvu_dev.endpoint[idx].msr = msr; + + /* Mask transitions (unchanged bits will be 0) */ + msr ^= oldmsr; + if (msr) { + XVU_DBG( "Transitioning...\n" ); + /* Update counters for TIOCGICOUNT */ + spin_lock_irqsave(&xvu_dev.endpoint[idx].sercnt_lock, flags); + if ( msr & TIOCM_DSR ) xvu_dev.endpoint[idx].ser_cnt.dsr++; + if ( msr & TIOCM_CAR ) xvu_dev.endpoint[idx].ser_cnt.dcd++; + if ( msr & TIOCM_CTS ) xvu_dev.endpoint[idx].ser_cnt.cts++; + spin_unlock_irqrestore(&xvu_dev.endpoint[idx].sercnt_lock, flags); + + /* MSR changed. Awake TIOCMIWAITers */ + wake_up_interruptible(&xvu_dev.endpoint[idx].msr_wait); + } +} + + +/* + * __XVU_SetMCR + * + * This sets the MCR (Modem Control Register), and updates its peer's MSR (Modem + * Status Register). + * **** It should be called with the xvu_dev.sem held ! **** + */ +static void __XVU_SetMCR( unsigned int idx, unsigned int val ) +{ + /* Set MSR */ + xvu_dev.endpoint[idx].mcr = val; + + XVU_DBG( "Setting MCR[%d]=0x%08x\n", idx, val ); + + /* Update peer's MSR */ + __XVU_SetMSR( __XVU_GetPeerIdx(idx) ); +} + +static int XVU_write(struct tty_struct *tty, const unsigned char *buffer, + int count) +{ + int idx = 0; + struct tty_struct *peer = NULL; + int rv = -EINVAL; + unsigned long flags = 0; + + if (down_interruptible(&xvu_dev.sem)) + return -ERESTARTSYS; + + if (tty == NULL) { + XVU_ERR( "Invalid (NULL) tty_struct\n"); + goto out; + } + + if (buffer == NULL) { + XVU_ERR( "Invalid (NULL) buffer\n"); + goto out; + } + + if (count <= 0 ) { + XVU_ERR( ": Invalid count = 0\n"); + goto out; + } + + idx = tty->index; + + if ( (idx<0) || (idx >= XVU_PAIRS*2) || (idx != (int)tty->driver_data) ) { + XVU_ERR( "Index %d out of range.\n", idx); + goto out; + } + + + /* Is endpoint open ? */ + if (! xvu_dev.endpoint[idx].open_count ) { + XVU_ERR( "Endpoint %d doesn't seem to be open.\n", idx); + goto out; + } + + /* + * Send string to peer + * If peer is closed, discard the string... + * ...but return count as if we sent it. + */ + + /* Is peer open ? */ + if (! xvu_dev.endpoint[__XVU_GetPeerIdx(idx)].open_count ) { + /* Discard sent bytes ! */ + rv = count; + goto out; + } + + /* Safety ! Shouldn't happen !!! */ + peer = xvu_dev.endpoint[__XVU_GetPeerIdx(idx)].tty; + if (! peer ) { + /* Discard sent bytes ! */ + XVU_ERR( "endpoint[%d].tty is NULL\n", __XVU_GetPeerIdx(idx)); + rv = count; + goto out; + } + + rv = tty_buffer_request_room(tty->port, count); + if (unlikely(rv == 0)) { + XVU_ERR("tty_buffer_request_room rv:%d\n",rv); + } + rv = tty_insert_flip_string(peer->port, buffer, count); + tty_schedule_flip(peer->port); + +out: + /* Update counters */ + if (rv > 0) { + spin_lock_irqsave(&xvu_dev.endpoint[idx].sercnt_lock, flags); + xvu_dev.endpoint[ idx ].ser_cnt.tx += rv; + spin_unlock_irqrestore(&xvu_dev.endpoint[idx].sercnt_lock, flags); + + spin_lock_irqsave(&xvu_dev.endpoint[__XVU_GetPeerIdx(idx)].sercnt_lock, flags); + xvu_dev.endpoint[__XVU_GetPeerIdx(idx)].ser_cnt.rx += rv; + spin_unlock_irqrestore(&xvu_dev.endpoint[__XVU_GetPeerIdx(idx)].sercnt_lock, flags); + } + + up(&xvu_dev.sem); + return rv; +} + + +static int XVU_open(struct tty_struct *tty, struct file *filp) +{ + int idx = 0; + int rv = -EINVAL; + int idx2 = 0; + + if (down_interruptible(&xvu_dev.sem)) + return -ERESTARTSYS; + + if (tty == NULL) { + XVU_ERR( ": Invalid (NULL) tty_struct\n"); + goto out; + } + + idx = tty->index; + idx2 = (int)(idx/2); + + if ( (idx<0) || (idx >= XVU_PAIRS*2) ) { + XVU_ERR( "Index %d out of range.\n", idx); + goto out; + } + + tty->driver_data = (void *)idx; + + /* Safety ! Shouldn't happen !!! */ + if ( (xvu_dev.endpoint[idx].tty) && (xvu_dev.endpoint[idx].tty != tty) ) { + XVU_ERR( "Endpoint[%d].tty=%p != tty=%p\n", + idx, xvu_dev.endpoint[idx].tty, tty); + } + + xvu_dev.endpoint[idx].tty = tty; + xvu_dev.endpoint[idx].open_count++; + + XVU_DBG( "Opening %d(count=%d)... TTY=%p\n", idx, xvu_dev.endpoint[idx].open_count, tty ); + + __XVU_SetMCR(idx, TIOCM_DTR | TIOCM_RTS); + rv = 0; + +out: + up(&xvu_dev.sem); + + + return rv; +} + + +static void XVU_close(struct tty_struct *tty, struct file *filp) +{ + int idx = 0; + int idx2 = 0; + down(&xvu_dev.sem); + + if (tty == NULL) { + XVU_ERR( "Invalid (NULL) tty_struct\n"); + goto out; + } + + idx = tty->index; + + if ( (idx<0) || (idx >= XVU_PAIRS*2) || (idx != (int)tty->driver_data) ) { + XVU_ERR( "Index %d out of range.\n", idx); + goto out; + } + + /* Is endpoint open ? */ + if (! xvu_dev.endpoint[idx].open_count ) { + XVU_ERR( "Endpoint %d doesn't seem to be open.\n", idx); + goto out; + } + + XVU_DBG( "Closing %d\n", idx ); + xvu_dev.endpoint[idx].open_count--; + + idx2 = (int)(idx/2); + if ((xvu_dev.endpoint[idx2].open_count <= 0) && (xvu_dev.endpoint[idx2+1].open_count <= 0)) { + /* No more users holding this device */ + XVU_ERR( "No more users holding this device\n"); + xvu_dev.endpoint[idx].tty = NULL; + wake_up_interruptible(&xvu_dev.endpoint[idx].msr_wait); + __XVU_SetMCR(idx, 0); + } + +out: + up(&xvu_dev.sem); +} + + +static int XVU_write_room(struct tty_struct *tty) +{ + int idx = 0; + struct tty_struct *peer = NULL; + struct tty_buffer *b = NULL; + unsigned long flags = 0; + int rv = -EINVAL; + + down(&xvu_dev.sem); + + if (tty == NULL) { + XVU_ERR( "Invalid (NULL) tty_struct\n"); + goto out; + } + + idx = tty->index; + + if ( (idx<0) || (idx >= XVU_PAIRS*2) || (idx != (int)tty->driver_data) ) { + XVU_ERR( "Index %d out of range.\n", idx); + goto out; + } + + /* is endpoint open ? */ + if (! xvu_dev.endpoint[idx].open_count ) { + XVU_ERR( "Endpoint %d doesn't seem to be open.\n", idx); + goto out; + } + + /* Is peer open ? */ + if (! xvu_dev.endpoint[__XVU_GetPeerIdx(idx)].open_count ) { + /* We'll discard sent bytes anyway, so... lie ! */ + rv = 255; + goto out; + } + + /* Safety ! Shouldn't happen !!! */ + peer = xvu_dev.endpoint[__XVU_GetPeerIdx(idx)].tty; + if (! peer ) { + /* We'll discard sent bytes anyway, so... lie ! */ + XVU_ERR( "Endpoint[%d].tty is NULL\n", __XVU_GetPeerIdx(idx)); + rv = 255; + goto out; + } + + /* + * Calculate how much room is left in the peer buffer + */ + spin_lock_irqsave(&peer->port->buf.lock, flags); + + b = peer->port->buf.tail; + if (b != NULL) + rv = b->size - b->used; + else + rv = 0; + + spin_unlock_irqrestore(&peer->port->buf.lock, flags); + +out: + up(&xvu_dev.sem); + + return rv; +} + + +/* + * Needed to avoid Oops on poll ! + */ +static int XVU_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + + +//static int XVU_tiocmget(struct tty_struct *tty, struct file *filp) +static int XVU_tiocmget(struct tty_struct *tty) +{ + int idx = 0; + int rv = -EINVAL; + + if (down_interruptible(&xvu_dev.sem)) + return -ERESTARTSYS; + + if (tty == NULL) { + XVU_ERR( "Invalid (NULL) tty_struct\n"); + goto out; + } + + idx = tty->index; + + if ( (idx<0) || (idx >= XVU_PAIRS*2) || (idx != (int)tty->driver_data) ) { + XVU_ERR( ": Index %d out of range.\n", idx); + goto out; + } + + rv = (xvu_dev.endpoint[idx].mcr | xvu_dev.endpoint[idx].msr); + + XVU_DBG( "In tiocmget(%d) returning: 0x%08x\n", idx, rv ); + +out: + up(&xvu_dev.sem); + + return rv; +} + + +//static int XVU_tiocmset(struct tty_struct *tty, struct file *filp, +// unsigned int set, unsigned int clear) +static int XVU_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) + +{ + int idx = 0; + + if (tty == NULL) { + XVU_ERR( "Invalid (NULL) tty_struct\n"); + return -EINVAL; + } + + idx = tty->index; + + if ( (idx<0) || (idx >= XVU_PAIRS*2) || (idx != (int)tty->driver_data) ) { + XVU_ERR( "Index %d out of range.\n", idx); + return -EINVAL; + } + + /* Only accept RTS and DTR flag changes */ + set &= (TIOCM_RTS | TIOCM_DTR); + clear &= (TIOCM_RTS | TIOCM_DTR); + + XVU_DBG( "In tiocmset(%d) SET=0x%08x CLR=0x%08x\n", idx, set, clear ); + + if (down_interruptible(&xvu_dev.sem)) + return -ERESTARTSYS; + __XVU_SetMCR( idx, ( (xvu_dev.endpoint[idx].mcr | set) & (~clear) ) ); + up(&xvu_dev.sem); + + return 0; +} + + +static int __XVU_tiocmiwait(struct tty_struct *tty, unsigned long arg) +{ + int idx = 0; + int rv= -EINVAL; + struct serial_icounter_struct cnow = {0}; + struct serial_icounter_struct cprev = {0}; + unsigned long flags = 0; + + if (tty == NULL) { + XVU_ERR( "Invalid (NULL) tty_struct\n"); + return -EINVAL; + } + + idx = tty->index; + + if ( (idx<0) || (idx >= XVU_PAIRS*2) || (idx != (int)tty->driver_data) ) { + XVU_ERR( "Index %d out of range.\n", idx); + return -EINVAL; + } + + spin_lock_irqsave(&xvu_dev.endpoint[idx].sercnt_lock, flags); + cnow = xvu_dev.endpoint[idx].ser_cnt; + + XVU_DBG( "In tiocmiwait(%d) waiting: 0x%08lx now={CTS=%d, DCD=%d, RNG=%d, DSR=%d}\n", idx, arg, cnow.cts, cnow.dcd, cnow.rng, cnow.dsr ); + + spin_unlock_irqrestore(&xvu_dev.endpoint[idx].sercnt_lock, flags); + + rv = wait_event_interruptible( xvu_dev.endpoint[idx].msr_wait, ({ + cprev = cnow; + spin_lock_irqsave(&xvu_dev.endpoint[idx].sercnt_lock, flags); + cnow = xvu_dev.endpoint[idx].ser_cnt; + spin_unlock_irqrestore(&xvu_dev.endpoint[idx].sercnt_lock, flags); + + /* Device closed or serial counters changed */ + (xvu_dev.endpoint[idx].open_count == 0) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) || + ((arg & TIOCM_CAR) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)); + })); + + XVU_DBG( "In tiocmiwait(%d) done wait: 0x%08lx now={CTS=%d, DCD=%d, RNG=%d, DSR=%d}\n", idx, arg, cnow.cts, cnow.dcd, cnow.rng, cnow.dsr ); + + if (rv < 0) { + XVU_DBG( "In tiocmiwait(%d) rv=%d, returning: ERESTARTSYS\n", idx, rv ); + return -ERESTARTSYS; + } + + /* + * return -EIO if we were awoken by device being closed + */ + if (xvu_dev.endpoint[idx].open_count == 0) { + XVU_DBG( "In tiocmiwait(%d) returning: EIO\n", idx ); + return -EIO; + } + + return 0; +} + + +//static int XVU_ioctl(struct tty_struct *tty, struct file *filp, +// unsigned int cmd, unsigned long arg) +static int XVU_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) + +{ + int idx = 0; + struct serial_icounter_struct icnt = {0}; + unsigned long flags = 0; + + if (tty == NULL) { + XVU_ERR( "Invalid (NULL) tty_struct\n"); + return -EINVAL; + } + + idx = tty->index; + + if ( (idx<0) || (idx >= XVU_PAIRS*2) || (idx != (int)tty->driver_data) ) { + XVU_ERR( "Index %d out of range.\n", idx); + return -EINVAL; + } + + if (cmd == TIOCMIWAIT) + return __XVU_tiocmiwait( tty, arg ); + + if (cmd == TIOCGICOUNT) { + spin_lock_irqsave(&xvu_dev.endpoint[idx].sercnt_lock, flags); + icnt = xvu_dev.endpoint[idx].ser_cnt; + spin_unlock_irqrestore(&xvu_dev.endpoint[idx].sercnt_lock, flags); + + if ( copy_to_user( (void __user *)arg, &icnt, sizeof(icnt)) ) + return -EFAULT; + else + return 0; + } + + return -ENOIOCTLCMD; +} + + +static struct tty_operations serial_ops = { + open: XVU_open, + close: XVU_close, + write: XVU_write, + write_room: XVU_write_room, + chars_in_buffer: XVU_chars_in_buffer, + tiocmget: XVU_tiocmget, + tiocmset: XVU_tiocmset, + ioctl: XVU_ioctl, +}; + + +/* + * Module Initialization and Cleanup + */ +static int __init XVU_Init(void) +{ + int rv = -EINVAL; + int i = 0; + + memset(&xvu_dev, 0, sizeof(xvu_dev)); + init_MUTEX(&xvu_dev.sem); + for (i=0;iowner = THIS_MODULE; + xvu_ttydriver->driver_name = DRIVER_NAME; + xvu_ttydriver->name = DEVICE_NAME; + xvu_ttydriver->major = XVU_MAJOR; + xvu_ttydriver->minor_start = 0; + xvu_ttydriver->type = TTY_DRIVER_TYPE_SERIAL; + xvu_ttydriver->subtype = SERIAL_TYPE_NORMAL; + xvu_ttydriver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW; + xvu_ttydriver->init_termios = tty_std_termios; + xvu_ttydriver->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; + tty_set_operations(xvu_ttydriver, &serial_ops); + tty_port_link_device(&xvu_port[0], xvu_ttydriver, 0); + tty_port_link_device(&xvu_port[1], xvu_ttydriver, 1); + + /* register the tty driver */ + rv = tty_register_driver(xvu_ttydriver); + if (rv) { + printk(KERN_ERR MODULE_NAME ": Failed to register Virtual UART tty driver"); + put_tty_driver(xvu_ttydriver); + return rv; + } + + down(&xvu_dev.sem); + for (i=0;imajor ); + printk(KERN_INFO MODULE_NAME ": Created %d Virtual UARTs. /dev/ttyV0 -> /dev/ttyV%d\n", XVU_PAIRS*2, XVU_PAIRS*2 -1 ); + return 0; +} + + +static void __exit XVU_Deinit(void) +{ + int idx = 0; + + down(&xvu_dev.sem); + + /* Report unclosed pending endpoints (in case of forced unload) */ + for (idx = 0; idx < XVU_PAIRS * 2; idx++) + if ( xvu_dev.endpoint[idx].open_count ) + XVU_ERR( "Endpoint[%d].open_count=%d != 0\n", + idx, xvu_dev.endpoint[idx].open_count ); + + if (xvu_ttydriver) + tty_unregister_driver(xvu_ttydriver); + + put_tty_driver(xvu_ttydriver); + + up(&xvu_dev.sem); + + printk(KERN_INFO MODULE_NAME ": Virtual UART Device Unloaded...\n" ); +} + +module_init(XVU_Init); +module_exit(XVU_Deinit); + +MODULE_AUTHOR("Pablo 'merKur' Kohan "); +MODULE_DESCRIPTION("Virtual UART-pair"); +MODULE_LICENSE("GPL v2"); -- cgit v1.3.1