cregit-Linux how code gets into the kernel

Release 4.18 drivers/staging/dgnc/dgnc_tty.c

// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2003 Digi International (www.digi.com)
 *      Scott H Kilau <Scott_Kilau at digi dot com>
 */

/*
 * This file implements the tty driver functionality for the
 * Neo and ClassicBoard PCI based product lines.
 */

#include <linux/kernel.h>
#include <linux/sched/signal.h>	/* For jiffies, task states, etc. */
#include <linux/interrupt.h>	/* For tasklet and interrupt structs/defines */
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/types.h>
#include <linux/serial_reg.h>
#include <linux/slab.h>
#include <linux/delay.h>	/* For udelay */
#include <linux/uaccess.h>	/* For copy_from_user/copy_to_user */
#include <linux/pci.h>
#include "dgnc_driver.h"
#include "dgnc_tty.h"
#include "dgnc_cls.h"

/* Default transparent print information. */


static const struct digi_t dgnc_digi_init = {
	.digi_flags =	DIGI_COOK,	/* Flags */
	.digi_maxcps =	100,		/* Max CPS */
	.digi_maxchar =	50,		/* Max chars in print queue */
	.digi_bufsize =	100,		/* Printer buffer size */
	.digi_onlen =	4,		/* size of printer on string */
	.digi_offlen =	4,		/* size of printer off string */
	.digi_onstr =	"\033[5i",	/* ANSI printer on string ] */
	.digi_offstr =	"\033[4i",	/* ANSI printer off string ] */
	.digi_term =	"ansi"		/* default terminal type */
};

static int dgnc_tty_open(struct tty_struct *tty, struct file *file);
static void dgnc_tty_close(struct tty_struct *tty, struct file *file);
static int dgnc_block_til_ready(struct tty_struct *tty, struct file *file,
				struct channel_t *ch);
static int dgnc_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
			  unsigned long arg);
static int dgnc_tty_digigeta(struct tty_struct *tty,
			     struct digi_t __user *retinfo);
static int dgnc_tty_digiseta(struct tty_struct *tty,
			     struct digi_t __user *new_info);
static int dgnc_tty_write_room(struct tty_struct *tty);
static int dgnc_tty_put_char(struct tty_struct *tty, unsigned char c);
static int dgnc_tty_chars_in_buffer(struct tty_struct *tty);
static void dgnc_tty_start(struct tty_struct *tty);
static void dgnc_tty_stop(struct tty_struct *tty);
static void dgnc_tty_throttle(struct tty_struct *tty);
static void dgnc_tty_unthrottle(struct tty_struct *tty);
static void dgnc_tty_flush_chars(struct tty_struct *tty);
static void dgnc_tty_flush_buffer(struct tty_struct *tty);
static void dgnc_tty_hangup(struct tty_struct *tty);
static int dgnc_set_modem_info(struct channel_t *ch, unsigned int command,
			       unsigned int __user *value);
static int dgnc_get_modem_info(struct channel_t *ch,
			       unsigned int __user *value);
static int dgnc_tty_tiocmget(struct tty_struct *tty);
static int dgnc_tty_tiocmset(struct tty_struct *tty, unsigned int set,
			     unsigned int clear);
static int dgnc_tty_send_break(struct tty_struct *tty, int msec);
static void dgnc_tty_wait_until_sent(struct tty_struct *tty, int timeout);
static int dgnc_tty_write(struct tty_struct *tty, const unsigned char *buf,
			  int count);
static void dgnc_tty_set_termios(struct tty_struct *tty,
				 struct ktermios *old_termios);
static void dgnc_tty_send_xchar(struct tty_struct *tty, char ch);
static void dgnc_set_signal_low(struct channel_t *ch, const unsigned char line);
static void dgnc_wake_up_unit(struct un_t *unit);


static const struct tty_operations dgnc_tty_ops = {
	.open = dgnc_tty_open,
	.close = dgnc_tty_close,
	.write = dgnc_tty_write,
	.write_room = dgnc_tty_write_room,
	.flush_buffer = dgnc_tty_flush_buffer,
	.chars_in_buffer = dgnc_tty_chars_in_buffer,
	.flush_chars = dgnc_tty_flush_chars,
	.ioctl = dgnc_tty_ioctl,
	.set_termios = dgnc_tty_set_termios,
	.stop = dgnc_tty_stop,
	.start = dgnc_tty_start,
	.throttle = dgnc_tty_throttle,
	.unthrottle = dgnc_tty_unthrottle,
	.hangup = dgnc_tty_hangup,
	.put_char = dgnc_tty_put_char,
	.tiocmget = dgnc_tty_tiocmget,
	.tiocmset = dgnc_tty_tiocmset,
	.break_ctl = dgnc_tty_send_break,
	.wait_until_sent = dgnc_tty_wait_until_sent,
	.send_xchar = dgnc_tty_send_xchar
};

/* TTY Initialization/Cleanup Functions */


static struct tty_driver *dgnc_tty_create(char *serial_name, uint maxports, int major, int minor) { int rc; struct tty_driver *drv; drv = tty_alloc_driver(maxports, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK); if (IS_ERR(drv)) return drv; drv->name = serial_name; drv->name_base = 0; drv->major = major; drv->minor_start = minor; drv->type = TTY_DRIVER_TYPE_SERIAL; drv->subtype = SERIAL_TYPE_NORMAL; drv->init_termios = tty_std_termios; drv->init_termios.c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL); drv->init_termios.c_ispeed = 9600; drv->init_termios.c_ospeed = 9600; drv->driver_name = DRVSTR; /* * Entry points for driver. Called by the kernel from * tty_io.c and n_tty.c. */ tty_set_operations(drv, &dgnc_tty_ops); rc = tty_register_driver(drv); if (rc < 0) { put_tty_driver(drv); return ERR_PTR(rc); } return drv; }

Contributors

PersonTokensPropCommitsCommitProp
Haim Daniel8549.42%125.00%
Lidza Louina6135.47%125.00%
DaeSeok Youn2615.12%250.00%
Total172100.00%4100.00%


static void dgnc_tty_free(struct tty_driver *drv) { tty_unregister_driver(drv); put_tty_driver(drv); }

Contributors

PersonTokensPropCommitsCommitProp
Haim Daniel21100.00%1100.00%
Total21100.00%1100.00%

/** * dgnc_tty_register() - Init the tty subsystem for this board. */
int dgnc_tty_register(struct dgnc_board *brd) { int rc; snprintf(brd->serial_name, MAXTTYNAMELEN, "tty_dgnc_%d_", brd->boardnum); brd->serial_driver = dgnc_tty_create(brd->serial_name, brd->maxports, 0, 0); if (IS_ERR(brd->serial_driver)) { rc = PTR_ERR(brd->serial_driver); dev_dbg(&brd->pdev->dev, "Can't register tty device (%d)\n", rc); return rc; } snprintf(brd->print_name, MAXTTYNAMELEN, "pr_dgnc_%d_", brd->boardnum); brd->print_driver = dgnc_tty_create(brd->print_name, brd->maxports, 0x80, brd->serial_driver->major); if (IS_ERR(brd->print_driver)) { rc = PTR_ERR(brd->print_driver); dev_dbg(&brd->pdev->dev, "Can't register Transparent Print device(%d)\n", rc); dgnc_tty_free(brd->serial_driver); return rc; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Haim Daniel6739.64%114.29%
Lidza Louina5532.54%114.29%
DaeSeok Youn3319.53%457.14%
Roberta Dobrescu148.28%114.29%
Total169100.00%7100.00%


void dgnc_tty_unregister(struct dgnc_board *brd) { dgnc_tty_free(brd->print_driver); dgnc_tty_free(brd->serial_driver); }

Contributors

PersonTokensPropCommitsCommitProp
DaeSeok Youn2291.67%150.00%
Haim Daniel28.33%150.00%
Total24100.00%2100.00%

/** * dgnc_tty_init() - Initialize the tty subsystem. * * Called once per board after board has been downloaded and initialized. */
int dgnc_tty_init(struct dgnc_board *brd) { int i; int rc; void __iomem *vaddr; struct channel_t *ch; if (!brd) return -ENXIO; /* Initialize board structure elements. */ vaddr = brd->re_map_membase; brd->nasync = brd->maxports; for (i = 0; i < brd->nasync; i++) { brd->channels[i] = kzalloc(sizeof(*brd->channels[i]), GFP_KERNEL); if (!brd->channels[i]) { rc = -ENOMEM; goto err_free_channels; } } ch = brd->channels[0]; vaddr = brd->re_map_membase; /* Set up channel variables */ for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { spin_lock_init(&ch->ch_lock); ch->ch_tun.un_ch = ch; ch->ch_tun.un_type = DGNC_SERIAL; ch->ch_tun.un_dev = i; ch->ch_pun.un_ch = ch; ch->ch_pun.un_type = DGNC_PRINT; ch->ch_pun.un_dev = i + 128; ch->ch_cls_uart = vaddr + (brd->bd_uart_offset * i); ch->ch_bd = brd; ch->ch_portnum = i; ch->ch_digi = dgnc_digi_init; /* .25 second delay */ ch->ch_close_delay = 250; init_waitqueue_head(&ch->ch_flags_wait); init_waitqueue_head(&ch->ch_tun.un_flags_wait); init_waitqueue_head(&ch->ch_pun.un_flags_wait); { struct device *classp; classp = tty_register_device(brd->serial_driver, i, &ch->ch_bd->pdev->dev); ch->ch_tun.un_sysfs = classp; classp = tty_register_device(brd->print_driver, i, &ch->ch_bd->pdev->dev); ch->ch_pun.un_sysfs = classp; } } return 0; err_free_channels: for (i = i - 1; i >= 0; --i) { kfree(brd->channels[i]); brd->channels[i] = NULL; } return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina31582.03%654.55%
Giedrius Statkevičius5313.80%19.09%
Tobin C Harding112.86%19.09%
Roberta Dobrescu20.52%19.09%
DaeSeok Youn20.52%19.09%
Walt Feasel10.26%19.09%
Total384100.00%11100.00%

/** * dgnc_cleanup_tty() - Cleanup driver. * * Uninitialize the TTY portion of this driver. Free all memory and * resources. */
void dgnc_cleanup_tty(struct dgnc_board *brd) { int i = 0; for (i = 0; i < brd->nasync; i++) tty_unregister_device(brd->serial_driver, i); tty_unregister_driver(brd->serial_driver); for (i = 0; i < brd->nasync; i++) tty_unregister_device(brd->print_driver, i); tty_unregister_driver(brd->print_driver); put_tty_driver(brd->serial_driver); put_tty_driver(brd->print_driver); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina7784.62%233.33%
DaeSeok Youn99.89%350.00%
Hari Prasath Gujulan Elango55.49%116.67%
Total91100.00%6100.00%

/** * dgnc_wmove() - Write data to transmit queue. * @ch: Pointer to channel structure. * @buf: Pointer to characters to be moved. * @n: Number of characters to move. */
static void dgnc_wmove(struct channel_t *ch, char *buf, uint n) { int remain; uint head; if (!ch) return; head = ch->ch_w_head & WQUEUEMASK; /* * If the write wraps over the top of the circular buffer, * move the portion up to the wrap point, and reset the * pointers to the bottom. */ remain = WQUEUESIZE - head; if (n >= remain) { n -= remain; memcpy(ch->ch_wqueue + head, buf, remain); head = 0; buf += remain; } if (n > 0) { /* Move rest of data. */ remain = n; memcpy(ch->ch_wqueue + head, buf, remain); head += remain; } head &= WQUEUEMASK; ch->ch_w_head = head; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina11799.15%150.00%
Walt Feasel10.85%150.00%
Total118100.00%2100.00%

/** * dgnc_input() - Process received data. * @ch: Pointer to channel structure. */
void dgnc_input(struct channel_t *ch) { struct dgnc_board *bd; struct tty_struct *tp; struct tty_ldisc *ld = NULL; uint rmask; ushort head; ushort tail; int data_len; unsigned long flags; int flip_len; int len = 0; int n = 0; int s = 0; int i = 0; if (!ch) return; tp = ch->ch_tun.un_tty; bd = ch->ch_bd; if (!bd) return; spin_lock_irqsave(&ch->ch_lock, flags); rmask = RQUEUEMASK; head = ch->ch_r_head & rmask; tail = ch->ch_r_tail & rmask; data_len = (head - tail) & rmask; if (data_len == 0) goto exit_unlock; /* * If the device is not open, or CREAD is off, * flush input data and return immediately. */ if (!tp || !(ch->ch_tun.un_flags & UN_ISOPEN) || !C_CREAD(tp) || (ch->ch_tun.un_flags & UN_CLOSING)) { ch->ch_r_head = tail; /* Force queue flow control to be released, if needed */ dgnc_check_queue_flow_control(ch); goto exit_unlock; } if (ch->ch_flags & CH_FORCED_STOPI) goto exit_unlock; flip_len = TTY_FLIPBUF_SIZE; len = min(data_len, flip_len); len = min(len, (N_TTY_BUF_SIZE - 1)); ld = tty_ldisc_ref(tp); if (!ld) { len = 0; } else { if (!ld->ops->receive_buf) { ch->ch_r_head = ch->ch_r_tail; len = 0; } } if (len <= 0) goto exit_unlock; /* * The tty layer in the kernel has changed in 2.6.16+. * * The flip buffers in the tty structure are no longer exposed, * and probably will be going away eventually. * * If we are completely raw, we don't need to go through a lot * of the tty layers that exist. * In this case, we take the shortest and fastest route we * can to relay the data to the user. * * On the other hand, if we are not raw, we need to go through * the new 2.6.16+ tty layer, which has its API more well defined. */ len = tty_buffer_request_room(tp->port, len); n = len; /* * n now contains the most amount of data we can copy, * bounded either by how much the Linux tty layer can handle, * or the amount of data the card actually has pending... */ while (n) { unsigned char *ch_pos = ch->ch_equeue + tail; s = ((head >= tail) ? head : RQUEUESIZE) - tail; s = min(s, n); if (s <= 0) break; /* * If conditions are such that ld needs to see all * UART errors, we will have to walk each character * and error byte and send them to the buffer one at * a time. */ if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { for (i = 0; i < s; i++) { unsigned char ch = *(ch_pos + i); char flag = TTY_NORMAL; if (ch & UART_LSR_BI) flag = TTY_BREAK; else if (ch & UART_LSR_PE) flag = TTY_PARITY; else if (ch & UART_LSR_FE) flag = TTY_FRAME; tty_insert_flip_char(tp->port, ch, flag); } } else { tty_insert_flip_string(tp->port, ch_pos, s); } tail += s; n -= s; /* Flip queue if needed */ tail &= rmask; } ch->ch_r_tail = tail & rmask; ch->ch_e_tail = tail & rmask; dgnc_check_queue_flow_control(ch); spin_unlock_irqrestore(&ch->ch_lock, flags); /* Tell the tty layer its okay to "eat" the data now */ tty_flip_buffer_push(tp->port); if (ld) tty_ldisc_deref(ld); return; exit_unlock: spin_unlock_irqrestore(&ch->ch_lock, flags); if (ld) tty_ldisc_deref(ld); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina46185.69%654.55%
Quentin Lambert366.69%218.18%
DaeSeok Youn295.39%19.09%
Roberta Dobrescu91.67%19.09%
Peter Hurley30.56%19.09%
Total538100.00%11100.00%

/** * dgnc_carrier() * * Determines when CARRIER changes state and takes appropriate * action. */
void dgnc_carrier(struct channel_t *ch) { int virt_carrier = 0; int phys_carrier = 0; if (!ch) return; if (ch->ch_mistat & UART_MSR_DCD) phys_carrier = 1; if (ch->ch_digi.digi_flags & DIGI_FORCEDCD) virt_carrier = 1; if (ch->ch_c_cflag & CLOCAL) virt_carrier = 1; /* Test for a VIRTUAL carrier transition to HIGH. */ if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { /* * When carrier rises, wake any threads waiting * for carrier in the open routine. */ if (waitqueue_active(&ch->ch_flags_wait)) wake_up_interruptible(&ch->ch_flags_wait); } /* Test for a PHYSICAL carrier transition to HIGH. */ if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { /* * When carrier rises, wake any threads waiting * for carrier in the open routine. */ if (waitqueue_active(&ch->ch_flags_wait)) wake_up_interruptible(&ch->ch_flags_wait); } /* * Test for a PHYSICAL transition to low, so long as we aren't * currently ignoring physical transitions (which is what "virtual * carrier" indicates). * * The transition of the virtual carrier to low really doesn't * matter... it really only means "ignore carrier state", not * "make pretend that carrier is there". */ if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) && (phys_carrier == 0)) { /* * When carrier drops: * * Drop carrier on all open units. * * Flush queues, waking up any task waiting in the * line discipline. * * Send a hangup to the control terminal. * * Enable all select calls. */ if (waitqueue_active(&ch->ch_flags_wait)) wake_up_interruptible(&ch->ch_flags_wait); if (ch->ch_tun.un_open_count > 0) tty_hangup(ch->ch_tun.un_tty); if (ch->ch_pun.un_open_count > 0) tty_hangup(ch->ch_pun.un_tty); } /* Make sure that our cached values reflect the current reality. */ if (virt_carrier == 1) ch->ch_flags |= CH_FCAR; else ch->ch_flags &= ~CH_FCAR; if (phys_carrier == 1) ch->ch_flags |= CH_CD; else ch->ch_flags &= ~CH_CD; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina27298.91%150.00%
Walt Feasel31.09%150.00%
Total275100.00%2100.00%

/* Assign the custom baud rate to the channel structure */
static void dgnc_set_custom_speed(struct channel_t *ch, uint newrate) { int testdiv; int testrate_high; int testrate_low; int deltahigh; int deltalow; if (newrate <= 0) { ch->ch_custom_speed = 0; return; } /* * Since the divisor is stored in a 16-bit integer, we make sure * we don't allow any rates smaller than a 16-bit integer would allow. * And of course, rates above the dividend won't fly. */ if (newrate && newrate < ((ch->ch_bd->bd_dividend / 0xFFFF) + 1)) newrate = (ch->ch_bd->bd_dividend / 0xFFFF) + 1; if (newrate && newrate > ch->ch_bd->bd_dividend) newrate = ch->ch_bd->bd_dividend; if (newrate > 0) { testdiv = ch->ch_bd->bd_dividend / newrate; /* * If we try to figure out what rate the board would use * with the test divisor, it will be either equal or higher * than the requested baud rate. If we then determine the * rate with a divisor one higher, we will get the next lower * supported rate below the requested. */ testrate_high = ch->ch_bd->bd_dividend / testdiv; testrate_low = ch->ch_bd->bd_dividend / (testdiv + 1); /* * If the rate for the requested divisor is correct, just * use it and be done. */ if (testrate_high != newrate) { /* * Otherwise, pick the rate that is closer * (i.e. whichever rate has a smaller delta). */ deltahigh = testrate_high - newrate; deltalow = newrate - testrate_low; if (deltahigh < deltalow) newrate = testrate_high; else newrate = testrate_low; } } ch->ch_custom_speed = newrate; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina17393.51%250.00%
DaeSeok Youn115.95%125.00%
Cristina Moraru10.54%125.00%
Total185100.00%4100.00%


void dgnc_check_queue_flow_control(struct channel_t *ch) { int qleft; qleft = ch->ch_r_tail - ch->ch_r_head - 1; if (qleft < 0) qleft += RQUEUEMASK + 1; /* * Check to see if we should enforce flow control on our queue because * the ld (or user) isn't reading data out of our queue fast enuf. * * NOTE: This is done based on what the current flow control of the * port is set for. * * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt. * This will cause the UART's FIFO to back up, and force * the RTS signal to be dropped. * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to * the other side, in hopes it will stop sending data to us. * 3) NONE - Nothing we can do. We will simply drop any extra data * that gets sent into us when the queue fills up. */ if (qleft < 256) { /* HWFLOW */ if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS) { if (!(ch->ch_flags & CH_RECEIVER_OFF)) { ch->ch_bd->bd_ops->disable_receiver(ch); ch->ch_flags |= (CH_RECEIVER_OFF); } } /* SWFLOW */ else if (ch->ch_c_iflag & IXOFF) { if (ch->ch_stops_sent <= MAX_STOPS_SENT) { ch->ch_bd->bd_ops->send_stop_character(ch); ch->ch_stops_sent++; } } } /* * Check to see if we should unenforce flow control because * ld (or user) finally read enuf data out of our queue. * * NOTE: This is done based on what the current flow control of the * port is set for. * * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt. * This will cause the UART's FIFO to raise RTS back up, * which will allow the other side to start sending data again. * 2) SWFLOW (IXOFF) - Send a start character to * the other side, so it will start sending data to us again. * 3) NONE - Do nothing. Since we didn't do anything to turn off the * other side, we don't need to do anything now. */ if (qleft > (RQUEUESIZE / 2)) { /* HWFLOW */ if (ch->ch_digi.digi_flags & RTSPACE || ch->ch_c_cflag & CRTSCTS) { if (ch->ch_flags & CH_RECEIVER_OFF) { ch->ch_bd->bd_ops->enable_receiver(ch); ch->ch_flags &= ~(CH_RECEIVER_OFF); } } /* SWFLOW */ else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) { ch->ch_stops_sent = 0; ch->ch_bd->bd_ops->send_start_character(ch); } } }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina22698.26%150.00%
Chi Pham41.74%150.00%
Total230100.00%2100.00%


static void dgnc_set_signal_low(struct channel_t *ch, const unsigned char sig) { ch->ch_mostat &= ~(sig); ch->ch_bd->bd_ops->assert_modem_signals(ch); }

Contributors

PersonTokensPropCommitsCommitProp
Fernando Apesteguia36100.00%1100.00%
Total36100.00%1100.00%


void dgnc_wakeup_writes(struct channel_t *ch) { int qlen = 0; unsigned long flags; if (!ch) return; spin_lock_irqsave(&ch->ch_lock, flags); /* If channel now has space, wake up anyone waiting on the condition. */ qlen = ch->ch_w_head - ch->ch_w_tail; if (qlen < 0) qlen += WQUEUESIZE; if (qlen >= (WQUEUESIZE - 256)) { spin_unlock_irqrestore(&ch->ch_lock, flags); return; } if (ch->ch_tun.un_flags & UN_ISOPEN) { tty_wakeup(ch->ch_tun.un_tty); /* * If unit is set to wait until empty, check to make sure * the queue AND FIFO are both empty. */ if (ch->ch_tun.un_flags & UN_EMPTY) { if ((qlen == 0) && (ch->ch_bd->bd_ops->get_uart_bytes_left(ch) == 0)) { ch->ch_tun.un_flags &= ~(UN_EMPTY); /* * If RTS Toggle mode is on, whenever * the queue and UART is empty, keep RTS low. */ if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) dgnc_set_signal_low(ch, UART_MCR_RTS); /* * If DTR Toggle mode is on, whenever * the queue and UART is empty, keep DTR low. */ if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) dgnc_set_signal_low(ch, UART_MCR_DTR); } } wake_up_interruptible(&ch->ch_tun.un_flags_wait); } if (ch->ch_pun.un_flags & UN_ISOPEN) { tty_wakeup(ch->ch_pun.un_tty); /* * If unit is set to wait until empty, check to make sure * the queue AND FIFO are both empty. */ if (ch->ch_pun.un_flags & UN_EMPTY) { if ((qlen == 0) && (ch->ch_bd->bd_ops->get_uart_bytes_left(ch) == 0)) ch->ch_pun.un_flags &= ~(UN_EMPTY); } wake_up_interruptible(&ch->ch_pun.un_flags_wait); } spin_unlock_irqrestore(&ch->ch_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina25891.17%116.67%
Roberta Dobrescu124.24%116.67%
Fernando Apesteguia62.12%116.67%
Chi Pham41.41%116.67%
Peter Hurley20.71%116.67%
Walt Feasel10.35%116.67%
Total283100.00%6100.00%


static struct dgnc_board *find_board_by_major(unsigned int major) { int i; for (i = 0; i < MAXBOARDS; i++) { struct dgnc_board *brd = dgnc_board[i]; if (!brd) return NULL; if (major == brd->serial_driver->major || major == brd->print_driver->major) return brd; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
DaeSeok Youn7198.61%150.00%
Benoit Hiller11.39%150.00%
Total72100.00%2100.00%

/* TTY Entry points and helper functions */
static int dgnc_tty_open(struct tty_struct *tty, struct file *file) { struct dgnc_board *brd; struct channel_t *ch; struct un_t *un; uint major = 0; uint minor = 0; int rc = 0; unsigned long flags; rc = 0; major = MAJOR(tty_devnum(tty)); minor = MINOR(tty_devnum(tty)); if (major > 255) return -ENXIO; brd = find_board_by_major(major); if (!brd) return -ENXIO; rc = wait_event_interruptible(brd->state_wait, (brd->state & BOARD_READY)); if (rc) return rc; spin_lock_irqsave(&brd->bd_lock, flags); if (PORT_NUM(minor) >= brd->nasync) { rc = -ENXIO; goto err_brd_unlock; } ch = brd->channels[PORT_NUM(minor)]; if (!ch) { rc = -ENXIO; goto err_brd_unlock; } spin_unlock_irqrestore(&brd->bd_lock, flags); spin_lock_irqsave(&ch->ch_lock, flags); /* Figure out our type */ if (!IS_PRINT(minor)) { un = &brd->channels[PORT_NUM(minor)]->ch_tun; un->un_type = DGNC_SERIAL; } else if (IS_PRINT(minor)) { un = &brd->channels[PORT_NUM(minor)]->ch_pun; un->un_type = DGNC_PRINT; } else { rc = -ENXIO; goto err_ch_unlock; } /* * If the port is still in a previous open, and in a state * where we simply cannot safely keep going, wait until the * state clears. */ spin_unlock_irqrestore(&ch->ch_lock, flags); rc = wait_event_interruptible(ch->ch_flags_wait, ((ch->ch_flags & CH_OPENING) == 0)); /* If ret is non-zero, user ctrl-c'ed us */ if (rc) return -EINTR; /* * If either unit is in the middle of the fragile part of close, * we just cannot touch the channel safely. * Go to sleep, knowing that when the channel can be * touched safely, the close routine will signal the * ch_flags_wait to wake us back up. */ rc = wait_event_interruptible( ch->ch_flags_wait, (((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_CLOSING) == 0)); /* If ret is non-zero, user ctrl-c'ed us */ if (rc) return -EINTR; spin_lock_irqsave(&ch->ch_lock, flags); tty->driver_data = un; /* Initialize tty's */ if (!(un->un_flags & UN_ISOPEN)) { un->un_tty = tty; /* Maybe do something here to the TTY struct as well? */ } /* * Allocate channel buffers for read/write/error. * Set flag, so we don't get trounced on. */ ch->ch_flags |= (CH_OPENING); spin_unlock_irqrestore(&ch->ch_lock, flags); if (!ch->ch_rqueue) ch->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL); if (!ch->ch_equeue) ch->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL); if (!ch->ch_wqueue) ch->ch_wqueue = kzalloc(WQUEUESIZE, GFP_KERNEL); if (!ch->ch_rqueue || !ch->ch_equeue || !ch->ch_wqueue) { kfree(ch->ch_rqueue); kfree(ch->ch_equeue); kfree(ch->ch_wqueue); return -ENOMEM; } spin_lock_irqsave(&ch->ch_lock, flags); ch->ch_flags &= ~(CH_OPENING); wake_up_interruptible(&ch->ch_flags_wait); /* Initialize if neither terminal or printer is open. */ if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) { /* Flush input queues. */ ch->ch_r_head = 0; ch->ch_r_tail = 0; ch->ch_e_head = 0; ch->ch_e_tail = 0; ch->ch_w_head = 0; ch->ch_w_tail = 0; brd->bd_ops->flush_uart_write(ch); brd->bd_ops->flush_uart_read(ch); ch->ch_flags = 0; ch->ch_cached_lsr = 0; ch->ch_stop_sending_break = 0; ch->ch_stops_sent = 0; ch->ch_c_cflag = tty->termios.c_cflag; ch->ch_c_iflag = tty->termios.c_iflag; ch->ch_c_oflag = tty->termios.c_oflag; ch->ch_c_lflag = tty->termios.c_lflag; ch->ch_startc = tty->termios.c_cc[VSTART]; ch->ch_stopc = tty->termios.c_cc[VSTOP]; /* * Bring up RTS and DTR... * Also handle RTS or DTR toggle if set. */ if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE)) ch->ch_mostat |= (UART_MCR_RTS); if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE)) ch->ch_mostat |= (UART_MCR_DTR); /* Tell UART to init itself */ brd->bd_ops->uart_init(ch); } brd->bd_ops->param(tty); dgnc_carrier(ch); spin_unlock_irqrestore(&ch->ch_lock, flags); rc = dgnc_block_til_ready(tty, file, ch); spin_lock_irqsave(&ch->ch_lock, flags); ch->ch_open_count++; un->un_open_count++; un->un_flags |= (UN_ISOPEN); spin_unlock_irqrestore(&ch->ch_lock, flags); return rc; err_brd_unlock: spin_unlock_irqrestore(&brd->bd_lock, flags); return rc; err_ch_unlock: spin_unlock_irqrestore(&ch->ch_lock, flags); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina72383.39%433.33%
Tobin C Harding485.54%18.33%
DaeSeok Youn475.42%216.67%
Roberta Dobrescu333.81%18.33%
SeungHun Lee121.38%216.67%
Walt Feasel30.35%18.33%
Dan Carpenter10.12%18.33%
Total867100.00%12100.00%

/* Wait for DCD, if needed. */
static int dgnc_block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch) { int rc = 0; struct un_t *un = tty->driver_data; unsigned long flags; uint old_flags = 0; int sleep_on_un_flags = 0; if (!file) return -ENXIO; spin_lock_irqsave(&ch->ch_lock, flags); ch->ch_wopen++; while (1) { sleep_on_un_flags = 0; if (ch->ch_bd->state == BOARD_FAILED) { rc = -ENXIO; break; } if (tty_hung_up_p(file)) { rc = -EAGAIN; break; } /* * If either unit is in the middle of the fragile part of close, * we just cannot touch the channel safely. * Go back to sleep, knowing that when the channel can be * touched safely, the close routine will signal the * ch_wait_flags to wake us back up. */ if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_CLOSING)) { /* * Our conditions to leave cleanly and happily: * 1) NONBLOCKING on the tty is set. * 2) CLOCAL is set. * 3) DCD (fake or real) is active. */ if (file->f_flags & O_NONBLOCK) break; if (tty_io_error(tty)) { rc = -EIO; break; } if (ch->ch_flags & CH_CD) break; if (ch->ch_flags & CH_FCAR) break; } else { sleep_on_un_flags = 1; } /* * If there is a signal pending, the user probably * interrupted (ctrl-c) us. */ if (signal_pending(current)) { rc = -ERESTARTSYS; break; } if (sleep_on_un_flags) old_flags = ch->ch_tun.un_flags | ch->ch_pun.un_flags; else old_flags = ch->ch_flags; /* * Let go of channel lock before calling schedule. * Our poller will get any FEP events and wake us up when DCD * eventually goes active. */ spin_unlock_irqrestore(&ch->ch_lock, flags); /* * Wait for something in the flags to change * from the current value. */ if (sleep_on_un_flags) rc = wait_event_interruptible (un->un_flags_wait, (old_flags != (ch->ch_tun.un_flags | ch->ch_pun.un_flags))); else rc = wait_event_interruptible( ch->ch_flags_wait, (old_flags != ch->ch_flags)); /* * We got woken up for some reason. * Before looping around, grab our channel lock. */ spin_lock_irqsave(&ch->ch_lock, flags); } ch->ch_wopen--; spin_unlock_irqrestore(&ch->ch_lock, flags); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina29090.34%225.00%
Roberta Dobrescu154.67%112.50%
Tobin C Harding92.80%225.00%
DaeSeok Youn30.93%112.50%
Peter Hurley30.93%112.50%
Cristina Moraru10.31%112.50%
Total321100.00%8100.00%

/* Hangup the port. Like a close, but don't wait for output to drain. */
static void dgnc_tty_hangup(struct tty_struct *tty) { if (!tty) return; /* flush the transmit queues */ dgnc_tty_flush_buffer(tty); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina23100.00%1100.00%
Total23100.00%1100.00%


static void dgnc_tty_close(struct tty_struct *tty, struct file *file) { struct dgnc_board *bd; struct channel_t *ch; struct un_t *un; unsigned long flags; if (!tty) return; un = tty->driver_data; if (!un) return; ch = un->un_ch; if (!ch) return; bd = ch->ch_bd; if (!bd) return; spin_lock_irqsave(&ch->ch_lock, flags); /* * Determine if this is the last close or not - and if we agree about * which type of close it is with the Line Discipline */ if ((tty->count == 1) && (un->un_open_count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. un_open_count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ dev_dbg(tty->dev, "tty->count is 1, un open count is %d\n", un->un_open_count); un->un_open_count = 1; } if (un->un_open_count) un->un_open_count--; else dev_dbg(tty->dev, "bad serial port open count of %d\n", un->un_open_count); ch->ch_open_count--; if (ch->ch_open_count && un->un_open_count) { spin_unlock_irqrestore(&ch->ch_lock, flags); return; } /* OK, its the last close on the unit */ un->un_flags |= UN_CLOSING; tty->closing = 1; /* * Only officially close channel if count is 0 and * DIGI_PRINTER bit is not set. */ if ((ch->ch_open_count == 0) && !(ch->ch_digi.digi_flags & DIGI_PRINTER)) { ch->ch_flags &= ~(CH_STOPI | CH_FORCED_STOPI); /* turn off print device when closing print device. */ if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON)) { dgnc_wmove(ch, ch->ch_digi.digi_offstr, (int)ch->ch_digi.digi_offlen); ch->ch_flags &= ~CH_PRON; } spin_unlock_irqrestore(&ch->ch_lock, flags); /* wait for output to drain */ /* This will also return if we take an interrupt */ bd->bd_ops->drain(tty, 0); dgnc_tty_flush_buffer(tty); tty_ldisc_flush(tty); spin_lock_irqsave(&ch->ch_lock, flags); tty->closing = 0; /* If we have HUPCL set, lower DTR and RTS */ if (ch->ch_c_cflag & HUPCL) { /* Drop RTS/DTR */ ch->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS); bd->bd_ops->assert_modem_signals(ch); /* * Go to sleep to ensure RTS/DTR * have been dropped for modems to see it. */ if (ch->ch_close_delay) { spin_unlock_irqrestore(&ch->ch_lock, flags); msleep_interruptible(ch->ch_close_delay); spin_lock_irqsave(&ch->ch_lock, flags); } } ch->ch_old_baud = 0; /* Turn off UART interrupts for this port */ ch->ch_bd->bd_ops->uart_off(ch); } else { /* turn off print device when closing print device. */ if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON)) { dgnc_wmove(ch, ch->ch_digi.digi_offstr, (int)ch->ch_digi.digi_offlen); ch->ch_flags &= ~CH_PRON; } } un->un_tty = NULL; un->un_flags &= ~(UN_ISOPEN | UN_CLOSING); wake_up_interruptible(&ch->ch_flags_wait); wake_up_interruptible(&un->un_flags_wait); spin_unlock_irqrestore(&ch->ch_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina46090.91%444.44%
Roberta Dobrescu367.11%222.22%
DaeSeok Youn61.19%111.11%
Walt Feasel30.59%111.11%
Greg Kroah-Hartman10.20%111.11%
Total506100.00%9100.00%

/* * Return number of characters that have not been transmitted yet. * * This routine is used by the line discipline to determine if there * is data waiting to be transmitted/drained/flushed or not. */
static int dgnc_tty_chars_in_buffer(struct tty_struct *tty) { struct channel_t *ch = NULL; struct un_t *un = NULL; ushort thead; ushort ttail; uint tmask; uint chars; unsigned long flags; if (!tty) return 0; un = tty->driver_data; if (!un) return 0; ch = un->un_ch; if (!ch) return 0; spin_lock_irqsave(&ch->ch_lock, flags); tmask = WQUEUEMASK; thead = ch->ch_w_head & tmask; ttail = ch->ch_w_tail & tmask; spin_unlock_irqrestore(&ch->ch_lock, flags); if (ttail == thead) chars = 0; else if (thead > ttail) chars = thead - ttail; else chars = thead - ttail + WQUEUESIZE; return chars; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina14092.11%125.00%
Roberta Dobrescu95.92%125.00%
Tobin C Harding21.32%125.00%
Sudip Mukherjee10.66%125.00%
Total152100.00%4100.00%

/* * Reduces bytes_available to the max number of characters * that can be sent currently given the maxcps value, and * returns the new bytes_available. This only affects printer * output. */
static int dgnc_maxcps_room(struct channel_t *ch, int bytes_available) { int rc = bytes_available; if (ch->ch_digi.digi_maxcps > 0 && ch->ch_digi.digi_bufsize > 0) { int cps_limit = 0; unsigned long current_time = jiffies; unsigned long buffer_time = current_time + (HZ * ch->ch_digi.digi_bufsize) / ch->ch_digi.digi_maxcps; if (ch->ch_cpstime < current_time) { /* buffer is empty */ ch->ch_cpstime = current_time; /* reset ch_cpstime */ cps_limit = ch->ch_digi.digi_bufsize; } else if (ch->ch_cpstime < buffer_time) { /* still room in the buffer */ cps_limit = ((buffer_time - ch->ch_cpstime) * ch->ch_digi.digi_maxcps) / HZ; } else { /* no room in the buffer */ cps_limit = 0; } rc = min(cps_limit, bytes_available); } return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina14194.00%133.33%
Tobin C Harding74.67%133.33%
DaeSeok Youn21.33%133.33%
Total150100.00%3100.00%

/* Return room available in Tx buffer */
static int dgnc_tty_write_room(struct tty_struct *tty) { struct channel_t *ch = NULL; struct un_t *un = NULL; ushort head; ushort tail; ushort tmask; int room = 0; unsigned long flags; if (!tty) return 0; un = tty->driver_data; if (!un) return 0; ch = un->un_ch; if (!ch) return 0; spin_lock_irqsave(&ch->ch_lock, flags); tmask = WQUEUEMASK; head = (ch->ch_w_head) & tmask; tail = (ch->ch_w_tail) & tmask; room = tail - head - 1; if (room < 0) room += WQUEUESIZE; /* Limit printer to maxcps */ if (un->un_type != DGNC_PRINT) room = dgnc_maxcps_room(ch, room); /* * If we are printer device, leave room for * possibly both the on and off strings. */ if (un->un_type == DGNC_PRINT) { if (!(ch->ch_flags & CH_PRON)) room -= ch->ch_digi.digi_onlen; room -= ch->ch_digi.digi_offlen; } else { if (ch->ch_flags & CH_PRON) room -= ch->ch_digi.digi_offlen; } if (room < 0) room = 0; spin_unlock_irqrestore(&ch->ch_lock, flags); return room; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina19484.72%116.67%
Tobin C Harding135.68%116.67%
DaeSeok Youn93.93%116.67%
Roberta Dobrescu93.93%116.67%
Chi Pham31.31%116.67%
Sudip Mukherjee10.44%116.67%
Total229100.00%6100.00%

/* * Put a character into ch->ch_buf * Used by the line discipline for OPOST processing */
static int dgnc_tty_put_char(struct tty_struct *tty, unsigned char c) { dgnc_tty_write(tty, &c, 1); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina28100.00%1100.00%
Total28100.00%1100.00%

/* * Take data from the user or kernel and send it out to the FEP. * In here exists all the Transparent Print magic as well. */
static int dgnc_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct channel_t *ch = NULL; struct un_t *un = NULL; int bufcount = 0, n = 0; unsigned long flags; ushort head; ushort tail; ushort tmask; uint remain; if (!tty) return 0; un = tty->driver_data; if (!un) return 0; ch = un->un_ch; if (!ch) return 0; if (!count) return 0; /* * Store original amount of characters passed in. * This helps to figure out if we should ask the FEP * to send us an event when it has more space available. */ spin_lock_irqsave(&ch->ch_lock, flags); tmask = WQUEUEMASK; head = (ch->ch_w_head) & tmask; tail = (ch->ch_w_tail) & tmask; bufcount = tail - head - 1; if (bufcount < 0) bufcount += WQUEUESIZE; /* * Limit printer output to maxcps overall, with bursts allowed * up to bufsize characters. */ if (un->un_type != DGNC_PRINT) bufcount = dgnc_maxcps_room(ch, bufcount); count = min(count, bufcount); if (count <= 0) goto exit_retry; /* * Output the printer ON string, if we are in terminal mode, but * need to be in printer mode. */ if ((un->un_type == DGNC_PRINT) && !(ch->ch_flags & CH_PRON)) { dgnc_wmove(ch, ch->ch_digi.digi_onstr, (int)ch->ch_digi.digi_onlen); head = (ch->ch_w_head) & tmask; ch->ch_flags |= CH_PRON; } /* * On the other hand, output the printer OFF string, if we are * currently in printer mode, but need to output to the terminal. */ if ((un->un_type != DGNC_PRINT) && (ch->ch_flags & CH_PRON)) { dgnc_wmove(ch, ch->ch_digi.digi_offstr, (int)ch->ch_digi.digi_offlen); head = (ch->ch_w_head) & tmask; ch->ch_flags &= ~CH_PRON; } n = count; /* * If the write wraps over the top of the circular buffer, * move the portion up to the wrap point, and reset the * pointers to the bottom. */ remain = WQUEUESIZE - head; if (n >= remain) { n -= remain; memcpy(ch->ch_wqueue + head, buf, remain); head = 0; buf += remain; } if (n > 0) { /* Move rest of data. */ remain = n; memcpy(ch->ch_wqueue + head, buf, remain); head += remain; } if (count) { head &= tmask; ch->ch_w_head = head; } /* Update printer buffer empty time. */ if ((un->un_type == DGNC_PRINT) && (ch->ch_digi.digi_maxcps > 0) && (ch->ch_digi.digi_bufsize > 0)) { ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps; } spin_unlock_irqrestore(&ch->ch_lock, flags); if (count) ch->ch_bd->bd_ops->copy_data_from_queue_to_uart(ch); return count; exit_retry: spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina44891.43%114.29%
Quentin Lambert183.67%114.29%
Roberta Dobrescu91.84%114.29%
DaeSeok Youn91.84%114.29%
Chi Pham40.82%114.29%
Sudip Mukherjee10.20%114.29%
Walt Feasel10.20%114.29%
Total490100.00%7100.00%

/* Return modem signals to ld. */
static int dgnc_tty_tiocmget(struct tty_struct *tty) { struct channel_t *ch; struct un_t *un; int rc; unsigned char mstat = 0; unsigned long flags; if (!tty) return -EIO; un = tty->driver_data; if (!un) return -EIO; ch = un->un_ch; if (!ch) return -EIO; spin_lock_irqsave(&ch->ch_lock, flags); mstat = ch->ch_mostat | ch->ch_mistat; spin_unlock_irqrestore(&ch->ch_lock, flags); rc = 0; if (mstat & UART_MCR_DTR) rc |= TIOCM_DTR; if (mstat & UART_MCR_RTS) rc |= TIOCM_RTS; if (mstat & UART_MSR_CTS) rc |= TIOCM_CTS; if (mstat & UART_MSR_DSR) rc |= TIOCM_DSR; if (mstat & UART_MSR_RI) rc |= TIOCM_RI; if (mstat & UART_MSR_DCD) rc |= TIOCM_CD; return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina14484.71%233.33%
Tobin C Harding158.82%233.33%
Roberta Dobrescu95.29%116.67%
Ebru Akagunduz21.18%116.67%
Total170100.00%6100.00%

/* Set modem signals, called by ld. */
static int dgnc_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct dgnc_board *bd; struct channel_t *ch; struct un_t *un; unsigned long flags; if (!tty) return -EIO; un = tty->driver_data; if (!un) return -EIO; ch = un->un_ch; if (!ch) return -EIO; bd = ch->ch_bd; if (!bd) return -EIO; spin_lock_irqsave(&ch->ch_lock, flags); if (set & TIOCM_RTS) ch->ch_mostat |= UART_MCR_RTS; if (set & TIOCM_DTR) ch->ch_mostat |= UART_MCR_DTR; if (clear & TIOCM_RTS) ch->ch_mostat &= ~(UART_MCR_RTS); if (clear & TIOCM_DTR) ch->ch_mostat &= ~(UART_MCR_DTR); bd->bd_ops->assert_modem_signals(ch); spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina16089.89%350.00%
Roberta Dobrescu95.06%116.67%
Tobin C Harding84.49%116.67%
DaeSeok Youn10.56%116.67%
Total178100.00%6100.00%

/* Send a Break, called by ld. */
static int dgnc_tty_send_break(struct tty_struct *tty, int msec) { struct dgnc_board *bd; struct channel_t *ch; struct un_t *un; unsigned long flags; if (!tty) return -EIO; un = tty->driver_data; if (!un) return -EIO; ch = un->un_ch; if (!ch) return -EIO; bd = ch->ch_bd; if (!bd) return -EIO; if (msec < 0) msec = 0xFFFF; spin_lock_irqsave(&ch->ch_lock, flags); bd->bd_ops->send_break(ch, msec); spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina11083.97%233.33%
Roberta Dobrescu96.87%116.67%
Tobin C Harding86.11%116.67%
DaeSeok Youn43.05%233.33%
Total131100.00%6100.00%

/* wait until data has been transmitted, called by ld. */
static void dgnc_tty_wait_until_sent(struct tty_struct *tty, int timeout) { struct dgnc_board *bd; struct channel_t *ch; struct un_t *un; if (!tty) return; un = tty->driver_data; if (!un) return; ch = un->un_ch; if (!ch) return; bd = ch->ch_bd; if (!bd) return; bd->bd_ops->drain(tty, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina82100.00%2100.00%
Total82100.00%2100.00%

/* send a high priority character, called by ld. */
static void dgnc_tty_send_xchar(struct tty_struct *tty, char c) { struct dgnc_board *bd; struct channel_t *ch; struct un_t *un; unsigned long flags; if (!tty) return; un = tty->driver_data; if (!un) return; ch = un->un_ch; if (!ch) return; bd = ch->ch_bd; if (!bd) return; spin_lock_irqsave(&ch->ch_lock, flags); bd->bd_ops->send_immediate_char(ch, c); spin_unlock_irqrestore(&ch->ch_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina9791.51%266.67%
Roberta Dobrescu98.49%133.33%
Total106100.00%3100.00%

/* Return modem signals to ld. */
static inline int dgnc_get_mstat(struct channel_t *ch) { unsigned char mstat; unsigned long flags; int rc; if (!ch) return -ENXIO; spin_lock_irqsave(&ch->ch_lock, flags); mstat = ch->ch_mostat | ch->ch_mistat; spin_unlock_irqrestore(&ch->ch_lock, flags); rc = 0; if (mstat & UART_MCR_DTR) rc |= TIOCM_DTR; if (mstat & UART_MCR_RTS) rc |= TIOCM_RTS; if (mstat & UART_MSR_CTS) rc |= TIOCM_CTS; if (mstat & UART_MSR_DSR) rc |= TIOCM_DSR; if (mstat & UART_MSR_RI) rc |= TIOCM_RI; if (mstat & UART_MSR_DCD) rc |= TIOCM_CD; return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina10480.62%125.00%
Tobin C Harding1612.40%250.00%
Roberta Dobrescu96.98%125.00%
Total129100.00%4100.00%

/* Return modem signals to ld. */
static int dgnc_get_modem_info(struct channel_t *ch, unsigned int __user *value) { return put_user(dgnc_get_mstat(ch), value); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina2485.71%133.33%
DaeSeok Youn27.14%133.33%
SeungHun Lee27.14%133.33%
Total28100.00%3100.00%

/* Set modem signals, called by ld. */
static int dgnc_set_modem_info(struct channel_t *ch, unsigned int command, unsigned int __user *value) { int rc; unsigned int arg = 0; unsigned long flags; rc = get_user(arg, value); if (rc) return rc; switch (command) { case TIOCMBIS: if (arg & TIOCM_RTS) ch->ch_mostat |= UART_MCR_RTS; if (arg & TIOCM_DTR) ch->ch_mostat |= UART_MCR_DTR; break; case TIOCMBIC: if (arg & TIOCM_RTS) ch->ch_mostat &= ~(UART_MCR_RTS); if (arg & TIOCM_DTR) ch->ch_mostat &= ~(UART_MCR_DTR); break; case TIOCMSET: if (arg & TIOCM_RTS) ch->ch_mostat |= UART_MCR_RTS; else ch->ch_mostat &= ~(UART_MCR_RTS); if (arg & TIOCM_DTR) ch->ch_mostat |= UART_MCR_DTR; else ch->ch_mostat &= ~(UART_MCR_DTR); break; default: return -EINVAL; } spin_lock_irqsave(&ch->ch_lock, flags); ch->ch_bd->bd_ops->assert_modem_signals(ch); spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina19092.68%125.00%
Roberta Dobrescu94.39%125.00%
Tobin C Harding41.95%125.00%
DaeSeok Youn20.98%125.00%
Total205100.00%4100.00%

/* Ioctl to get the information for ditty. */
static int dgnc_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo) { struct channel_t *ch; struct un_t *un; struct digi_t tmp; unsigned long flags; if (!retinfo) return -EFAULT; if (!tty) return -EFAULT; un = tty->driver_data; if (!un) return -EFAULT; ch = un->un_ch; if (!ch) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); spin_lock_irqsave(&ch->ch_lock, flags); memcpy(&tmp, &ch->ch_digi, sizeof(tmp)); spin_unlock_irqrestore(&ch->ch_lock, flags); if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina13687.74%133.33%
Tobin C Harding106.45%133.33%
Roberta Dobrescu95.81%133.33%
Total155100.00%3100.00%

/* Ioctl to set the information for ditty. */
static int dgnc_tty_digiseta(struct tty_struct *tty, struct digi_t __user *new_info) { struct dgnc_board *bd; struct channel_t *ch; struct un_t *un; struct digi_t new_digi; unsigned long flags; if (!tty) return -EFAULT; un = tty->driver_data; if (!un) return -EFAULT; ch = un->un_ch; if (!ch) return -EFAULT; bd = ch->ch_bd; if (!bd) return -EFAULT; if (copy_from_user(&new_digi, new_info, sizeof(new_digi))) return -EFAULT; spin_lock_irqsave(&ch->ch_lock, flags); /* Handle transitions to and from RTS Toggle. */ if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) && (new_digi.digi_flags & DIGI_RTS_TOGGLE)) ch->ch_mostat &= ~(UART_MCR_RTS); if ((ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) && !(new_digi.digi_flags & DIGI_RTS_TOGGLE)) ch->ch_mostat |= (UART_MCR_RTS); /* Handle transitions to and from DTR Toggle. */ if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) && (new_digi.digi_flags & DIGI_DTR_TOGGLE)) ch->ch_mostat &= ~(UART_MCR_DTR); if ((ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) && !(new_digi.digi_flags & DIGI_DTR_TOGGLE)) ch->ch_mostat |= (UART_MCR_DTR); memcpy(&ch->ch_digi, &new_digi, sizeof(new_digi)); if (ch->ch_digi.digi_maxcps < 1) ch->ch_digi.digi_maxcps = 1; if (ch->ch_digi.digi_maxcps > 10000) ch->ch_digi.digi_maxcps = 10000; if (ch->ch_digi.digi_bufsize < 10) ch->ch_digi.digi_bufsize = 10; if (ch->ch_digi.digi_maxchar < 1) ch->ch_digi.digi_maxchar = 1; if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize) ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize; if (ch->ch_digi.digi_onlen > DIGI_PLEN) ch->ch_digi.digi_onlen = DIGI_PLEN; if (ch->ch_digi.digi_offlen > DIGI_PLEN) ch->ch_digi.digi_offlen = DIGI_PLEN; bd->bd_ops->param(tty); spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina39394.70%342.86%
Tobin C Harding102.41%114.29%
Roberta Dobrescu92.17%114.29%
Walt Feasel20.48%114.29%
DaeSeok Youn10.24%114.29%
Total415100.00%7100.00%


static void dgnc_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct dgnc_board *bd; struct channel_t *ch; struct un_t *un; unsigned long flags; if (!tty) return; un = tty->driver_data; if (!un) return; ch = un->un_ch; if (!ch) return; bd = ch->ch_bd; if (!bd) return; spin_lock_irqsave(&ch->ch_lock, flags); ch->ch_c_cflag = tty->termios.c_cflag; ch->ch_c_iflag = tty->termios.c_iflag; ch->ch_c_oflag = tty->termios.c_oflag; ch->ch_c_lflag = tty->termios.c_lflag; ch->ch_startc = tty->termios.c_cc[VSTART]; ch->ch_stopc = tty->termios.c_cc[VSTOP]; bd->bd_ops->param(tty); dgnc_carrier(ch); spin_unlock_irqrestore(&ch->ch_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina16995.48%360.00%
Roberta Dobrescu73.95%120.00%
DaeSeok Youn10.56%120.00%
Total177100.00%5100.00%


static void dgnc_tty_throttle(struct tty_struct *tty) { struct channel_t *ch; struct un_t *un; unsigned long flags; if (!tty) return; un = tty->driver_data; if (!un) return; ch = un->un_ch; if (!ch) return; spin_lock_irqsave(&ch->ch_lock, flags); ch->ch_flags |= (CH_FORCED_STOPI); spin_unlock_irqrestore(&ch->ch_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina7489.16%150.00%
Roberta Dobrescu910.84%150.00%
Total83100.00%2100.00%


static void dgnc_tty_unthrottle(struct tty_struct *tty) { struct channel_t *ch; struct un_t *un; unsigned long flags; if (!tty) return; un = tty->driver_data; if (!un) return; ch = un->un_ch; if (!ch) return; spin_lock_irqsave(&ch->ch_lock, flags); ch->ch_flags &= ~(CH_FORCED_STOPI); spin_unlock_irqrestore(&ch->ch_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina7589.29%150.00%
Roberta Dobrescu910.71%150.00%
Total84100.00%2100.00%


static void dgnc_tty_start(struct tty_struct *tty) { struct dgnc_board *bd; struct channel_t *ch; struct un_t *un; unsigned long flags; if (!tty) return; un = tty->driver_data; if (!un) return; ch = un->un_ch; if (!ch) return; bd = ch->ch_bd; if (!bd) return; spin_lock_irqsave(&ch->ch_lock, flags); ch->ch_flags &= ~(CH_FORCED_STOP); spin_unlock_irqrestore(&ch->ch_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina9291.09%266.67%
Roberta Dobrescu98.91%133.33%
Total101100.00%3100.00%


static void dgnc_tty_stop(struct tty_struct *tty) { struct dgnc_board *bd; struct channel_t *ch; struct un_t *un; unsigned long flags; if (!tty) return; un = tty->driver_data; if (!un) return; ch = un->un_ch; if (!ch) return; bd = ch->ch_bd; if (!bd) return; spin_lock_irqsave(&ch->ch_lock, flags); ch->ch_flags |= (CH_FORCED_STOP); spin_unlock_irqrestore(&ch->ch_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina9191.00%266.67%
Roberta Dobrescu99.00%133.33%
Total100100.00%3100.00%

/* * Flush the cook buffer * * Note to self, and any other poor souls who venture here: * * flush in this case DOES NOT mean dispose of the data. * instead, it means "stop buffering and send it if you * haven't already." Just guess how I figured that out... SRW 2-Jun-98 * * It is also always called in interrupt context - JAR 8-Sept-99 */
static void dgnc_tty_flush_chars(struct tty_struct *tty) { struct dgnc_board *bd; struct channel_t *ch; struct un_t *un; unsigned long flags; if (!tty) return; un = tty->driver_data; if (!un) return; ch = un->un_ch; if (!ch) return; bd = ch->ch_bd; if (!bd) return; spin_lock_irqsave(&ch->ch_lock, flags); /* Do something maybe here */ spin_unlock_irqrestore(&ch->ch_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina8490.32%266.67%
Roberta Dobrescu99.68%133.33%
Total93100.00%3100.00%

/* Flush Tx buffer (make in == out) */
static void dgnc_tty_flush_buffer(struct tty_struct *tty) { struct channel_t *ch; struct un_t *un; unsigned long flags; if (!tty) return; un = tty->driver_data; if (!un) return; ch = un->un_ch; if (!ch) return; spin_lock_irqsave(&ch->ch_lock, flags); ch->ch_flags &= ~CH_STOP; /* Flush our write queue */ ch->ch_w_head = ch->ch_w_tail; /* Flush UARTs transmit FIFO */ ch->ch_bd->bd_ops->flush_uart_write(ch); if (ch->ch_tun.un_flags & (UN_LOW | UN_EMPTY)) { ch->ch_tun.un_flags &= ~(UN_LOW | UN_EMPTY); wake_up_interruptible(&ch->ch_tun.un_flags_wait); } if (ch->ch_pun.un_flags & (UN_LOW | UN_EMPTY)) { ch->ch_pun.un_flags &= ~(UN_LOW | UN_EMPTY); wake_up_interruptible(&ch->ch_pun.un_flags_wait); } spin_unlock_irqrestore(&ch->ch_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina17295.03%150.00%
Roberta Dobrescu94.97%150.00%
Total181100.00%2100.00%

/* Wakes up processes waiting in the unit's (teminal/printer) wait queue */
static void dgnc_wake_up_unit(struct un_t *unit) { unit->un_flags &= ~(UN_LOW | UN_EMPTY); wake_up_interruptible(&unit->un_flags_wait); }

Contributors

PersonTokensPropCommitsCommitProp
Fernando Apesteguia30100.00%1100.00%
Total30100.00%1100.00%

/* The IOCTL function and all of its helpers */ /* The usual assortment of ioctl's */
static int dgnc_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct dgnc_board *bd; struct board_ops *ch_bd_ops; struct channel_t *ch; struct un_t *un; int rc; unsigned long flags; void __user *uarg = (void __user *)arg; if (!tty) return -ENODEV; un = tty->driver_data; if (!un) return -ENODEV; ch = un->un_ch; if (!ch) return -ENODEV; bd = ch->ch_bd; if (!bd) return -ENODEV; ch_bd_ops = bd->bd_ops; spin_lock_irqsave(&ch->ch_lock, flags); if (un->un_open_count <= 0) { rc = -EIO; goto err_unlock; } switch (cmd) { /* Here are all the standard ioctl's that we MUST implement */ case TCSBRK: /* * TCSBRK is SVID version: non-zero arg --> no break * this behaviour is exploited by tcdrain(). * * According to POSIX.1 spec (7.2.2.1.2) breaks should be * between 0.25 and 0.5 seconds so we'll ask for something * in the middle: 0.375 seconds. */ rc = tty_check_change(tty); spin_unlock_irqrestore(&ch->ch_lock, flags); if (rc) return rc; rc = ch_bd_ops->drain(tty, 0); if (rc) return -EINTR; spin_lock_irqsave(&ch->ch_lock, flags); if (((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) ch_bd_ops->send_break(ch, 250); spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; case TCSBRKP: /* * support for POSIX tcsendbreak() * According to POSIX.1 spec (7.2.2.1.2) breaks should be * between 0.25 and 0.5 seconds so we'll ask for something * in the middle: 0.375 seconds. */ rc = tty_check_change(tty); spin_unlock_irqrestore(&ch->ch_lock, flags); if (rc) return rc; rc = ch_bd_ops->drain(tty, 0); if (rc) return -EINTR; spin_lock_irqsave(&ch->ch_lock, flags); ch_bd_ops->send_break(ch, 250); spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; case TIOCSBRK: rc = tty_check_change(tty); spin_unlock_irqrestore(&ch->ch_lock, flags); if (rc) return rc; rc = ch_bd_ops->drain(tty, 0); if (rc) return -EINTR; spin_lock_irqsave(&ch->ch_lock, flags); ch_bd_ops->send_break(ch, 250); spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; case TIOCCBRK: /* Do Nothing */ spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; case TIOCGSOFTCAR: spin_unlock_irqrestore(&ch->ch_lock, flags); return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *)arg); case TIOCSSOFTCAR: spin_unlock_irqrestore(&ch->ch_lock, flags); rc = get_user(arg, (unsigned long __user *)arg); if (rc) return rc; spin_lock_irqsave(&ch->ch_lock, flags); tty->termios.c_cflag = ((tty->termios.c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); ch_bd_ops->param(tty); spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; case TIOCMGET: spin_unlock_irqrestore(&ch->ch_lock, flags); return dgnc_get_modem_info(ch, uarg); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: spin_unlock_irqrestore(&ch->ch_lock, flags); return dgnc_set_modem_info(ch, cmd, uarg); /* Here are any additional ioctl's that we want to implement */ case TCFLSH: /* * The linux tty driver doesn't have a flush * input routine for the driver, assuming all backed * up data is in the line disc. buffers. However, * we all know that's not the case. Here, we * act on the ioctl, but then lie and say we didn't * so the line discipline will process the flush * also. */ rc = tty_check_change(tty); if (rc) goto err_unlock; if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) { ch->ch_r_head = ch->ch_r_tail; ch_bd_ops->flush_uart_read(ch); /* Force queue flow control to be released, if needed */ dgnc_check_queue_flow_control(ch); } if ((arg == TCOFLUSH) || (arg == TCIOFLUSH)) { if (!(un->un_type == DGNC_PRINT)) { ch->ch_w_head = ch->ch_w_tail; ch_bd_ops->flush_uart_write(ch); if (ch->ch_tun.un_flags & (UN_LOW | UN_EMPTY)) dgnc_wake_up_unit(&ch->ch_tun); if (ch->ch_pun.un_flags & (UN_LOW | UN_EMPTY)) dgnc_wake_up_unit(&ch->ch_pun); } } /* pretend we didn't recognize this IOCTL */ spin_unlock_irqrestore(&ch->ch_lock, flags); return -ENOIOCTLCMD; case TCSETSF: case TCSETSW: /* * The linux tty driver doesn't have a flush * input routine for the driver, assuming all backed * up data is in the line disc. buffers. However, * we all know that's not the case. Here, we * act on the ioctl, but then lie and say we didn't * so the line discipline will process the flush * also. */ if (cmd == TCSETSF) { /* flush rx */ ch->ch_flags &= ~CH_STOP; ch->ch_r_head = ch->ch_r_tail; ch_bd_ops->flush_uart_read(ch); /* Force queue flow control to be released, if needed */ dgnc_check_queue_flow_control(ch); } /* now wait for all the output to drain */ spin_unlock_irqrestore(&ch->ch_lock, flags); rc = ch_bd_ops->drain(tty, 0); if (rc) return -EINTR; /* pretend we didn't recognize this */ return -ENOIOCTLCMD; case TCSETAW: spin_unlock_irqrestore(&ch->ch_lock, flags); rc = ch_bd_ops->drain(tty, 0); if (rc) return -EINTR; /* pretend we didn't recognize this */ return -ENOIOCTLCMD; case TCXONC: spin_unlock_irqrestore(&ch->ch_lock, flags); /* Make the ld do it */ return -ENOIOCTLCMD; case DIGI_GETA: /* get information for ditty */ spin_unlock_irqrestore(&ch->ch_lock, flags); return dgnc_tty_digigeta(tty, uarg); case DIGI_SETAW: case DIGI_SETAF: /* set information for ditty */ if (cmd == (DIGI_SETAW)) { spin_unlock_irqrestore(&ch->ch_lock, flags); rc = ch_bd_ops->drain(tty, 0); if (rc) return -EINTR; spin_lock_irqsave(&ch->ch_lock, flags); } else { tty_ldisc_flush(tty); } /* fall thru */ case DIGI_SETA: spin_unlock_irqrestore(&ch->ch_lock, flags); return dgnc_tty_digiseta(tty, uarg); case DIGI_LOOPBACK: { uint loopback = 0; /* * Let go of locks when accessing user space, * could sleep */ spin_unlock_irqrestore(&ch->ch_lock, flags); rc = get_user(loopback, (unsigned int __user *)arg); if (rc) return rc; spin_lock_irqsave(&ch->ch_lock, flags); /* Enable/disable internal loopback for this port */ if (loopback) ch->ch_flags |= CH_LOOPBACK; else ch->ch_flags &= ~(CH_LOOPBACK); ch_bd_ops->param(tty); spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; } case DIGI_GETCUSTOMBAUD: spin_unlock_irqrestore(&ch->ch_lock, flags); return put_user(ch->ch_custom_speed, (unsigned int __user *)arg); case DIGI_SETCUSTOMBAUD: { int new_rate; spin_unlock_irqrestore(&ch->ch_lock, flags); rc = get_user(new_rate, (int __user *)arg); if (rc) return rc; spin_lock_irqsave(&ch->ch_lock, flags); dgnc_set_custom_speed(ch, new_rate); ch_bd_ops->param(tty); spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; } /* * This ioctl allows insertion of a character into the front * of any pending data to be transmitted. * * This ioctl is to satisfy the "Send Character Immediate" * call that the RealPort protocol spec requires. */ case DIGI_REALPORT_SENDIMMEDIATE: { unsigned char c; spin_unlock_irqrestore(&ch->ch_lock, flags); rc = get_user(c, (unsigned char __user *)arg); if (rc) return rc; spin_lock_irqsave(&ch->ch_lock, flags); ch_bd_ops->send_immediate_char(ch, c); spin_unlock_irqrestore(&ch->ch_lock, flags); return 0; } /* * This ioctl returns all the current counts for the port. * * This ioctl is to satisfy the "Line Error Counters" * call that the RealPort protocol spec requires. */ case DIGI_REALPORT_GETCOUNTERS: { struct digi_getcounter buf; buf.norun = ch->ch_err_overrun; buf.noflow = 0; /* The driver doesn't keep this stat */ buf.nframe = ch->ch_err_frame; buf.nparity = ch->ch_err_parity; buf.nbreak = ch->ch_err_break; buf.rbytes = ch->ch_rxcount; buf.tbytes = ch->ch_txcount; spin_unlock_irqrestore(&ch->ch_lock, flags); if (copy_to_user(uarg, &buf, sizeof(buf))) return -EFAULT; return 0; } /* * This ioctl returns all current events. * * This ioctl is to satisfy the "Event Reporting" * call that the RealPort protocol spec requires. */ case DIGI_REALPORT_GETEVENTS: { unsigned int events = 0; /* NOTE: MORE EVENTS NEEDS TO BE ADDED HERE */ if (ch->ch_flags & CH_BREAK_SENDING) events |= EV_TXB; if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_FORCED_STOP)) events |= (EV_OPU | EV_OPS); if ((ch->ch_flags & CH_STOPI) || (ch->ch_flags & CH_FORCED_STOPI)) events |= (EV_IPU | EV_IPS); spin_unlock_irqrestore(&ch->ch_lock, flags); return put_user(events, (unsigned int __user *)arg); } /* * This ioctl returns TOUT and TIN counters based * upon the values passed in by the RealPort Server. * It also passes back whether the UART Transmitter is * empty as well. */ case DIGI_REALPORT_GETBUFFERS: { struct digi_getbuffer buf; int tdist; int count; spin_unlock_irqrestore(&ch->ch_lock, flags); if (copy_from_user(&buf, uarg, sizeof(buf))) return -EFAULT; spin_lock_irqsave(&ch->ch_lock, flags); /* Figure out how much data is in our RX and TX queues. */ buf.rxbuf = (ch->ch_r_head - ch->ch_r_tail) & RQUEUEMASK; buf.txbuf = (ch->ch_w_head - ch->ch_w_tail) & WQUEUEMASK; /* * Is the UART empty? * Add that value to whats in our TX queue. */ count = buf.txbuf + ch_bd_ops->get_uart_bytes_left(ch); /* * Figure out how much data the RealPort Server believes should * be in our TX queue. */ tdist = (buf.tx_in - buf.tx_out) & 0xffff; /* * If we have more data than the RealPort Server believes we * should have, reduce our count to its amount. * * This count difference CAN happen because the Linux LD can * insert more characters into our queue for OPOST processing * that the RealPort Server doesn't know about. */ if (buf.txbuf > tdist) buf.txbuf = tdist; /* Report whether our queue and UART TX are completely empty. */ if (count) buf.txdone = 0; else buf.txdone = 1; spin_unlock_irqrestore(&ch->ch_lock, flags); if (copy_to_user(uarg, &buf, sizeof(buf))) return -EFAULT; return 0; } default: spin_unlock_irqrestore(&ch->ch_lock, flags); return -ENOIOCTLCMD; } err_unlock: spin_unlock_irqrestore(&ch->ch_lock, flags); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina148087.73%633.33%
Roberta Dobrescu1267.47%15.56%
Tobin C Harding352.07%316.67%
DaeSeok Youn321.90%422.22%
Walt Feasel80.47%211.11%
Rehas Sachdeva30.18%15.56%
Fernando Apesteguia30.18%15.56%
Total1687100.00%18100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Lidza Louina868485.79%1218.75%
Roberta Dobrescu4074.02%23.12%
DaeSeok Youn3243.20%1929.69%
Tobin C Harding2192.16%46.25%
Haim Daniel1761.74%11.56%
Fernando Apesteguia1000.99%11.56%
Giedrius Statkevičius560.55%23.12%
Quentin Lambert540.53%23.12%
Walt Feasel410.41%23.12%
Chi Pham150.15%11.56%
SeungHun Lee140.14%23.12%
Peter Hurley80.08%34.69%
Hari Prasath Gujulan Elango50.05%11.56%
Rehas Sachdeva30.03%11.56%
Sudip Mukherjee30.03%11.56%
Greg Kroah-Hartman30.03%34.69%
Ingo Molnar20.02%11.56%
Cristina Moraru20.02%11.56%
Ebru Akagunduz20.02%11.56%
Dan Carpenter10.01%11.56%
Benoit Hiller10.01%11.56%
Elise Lennion10.01%11.56%
Kieron Browne10.01%11.56%
Total10122100.00%64100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.