Re: [PATCH v3 2/4] tty/serial: ttvys: add null modem driver for emulation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Apr 16, 2020 at 10:26:12AM +0530, Rishi Gupta wrote:
> The ttyvs driver creates virtual tty devices that can be
> used with standard POSIX APIs for serial port based applications.
> The driver is used mainly for testing user space applications.
> 
> Devices can be created through device tree and through configfs.
> Various serial port events are emulated through a sysfs file.

...

> +TTYVS VIRTUAL SERIAL DRIVER
> +M:	Rishi Gupta <gupt21@xxxxxxxxx>
> +L:	linux-serial@xxxxxxxxxxxxxxx

> +L:	linux-kernel@xxxxxxxxxxxxxxx

Redundant. It's default for all.

> +S:	Maintained
> +F:	Documentation/admin-guide/virtual-tty-ttyvs.rst
> +F:	Documentation/devicetree/bindings/serial/ttyvs.yaml
> +F:	drivers/tty/ttyvs.c

...

> +#include <linux/init.h>
> +#include <linux/idr.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +#include <linux/tty.h>
> +#include <linux/tty_driver.h>
> +#include <linux/tty_flip.h>
> +#include <linux/serial.h>
> +#include <linux/sched.h>
> +#include <linux/version.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/uaccess.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/configfs.h>

Perhaps ordered?

...

> +#define VS_CON_CTS    0x0001
> +#define VS_CON_DCD    0x0002
> +#define VS_CON_DSR    0x0004
> +#define VS_CON_RI     0x0008

> +#define VS_MCR_DTR    0x0001
> +#define VS_MCR_RTS    0x0002
> +#define VS_MCR_LOOP   0x0004

> +#define VS_MSR_CTS    0x0008
> +#define VS_MSR_DCD    0x0010
> +#define VS_MSR_RI     0x0020
> +#define VS_MSR_DSR    0x0040

> +#define VS_CRTSCTS       0x0001
> +#define VS_XON           0x0002
> +#define VS_NONE          0x0004
> +#define VS_DATA_5        0x0008
> +#define VS_DATA_6        0x0010
> +#define VS_DATA_7        0x0020
> +#define VS_DATA_8        0x0040
> +#define VS_PARITY_NONE   0x0080
> +#define VS_PARITY_ODD    0x0100
> +#define VS_PARITY_EVEN   0x0200
> +#define VS_PARITY_MARK   0x0400
> +#define VS_PARITY_SPACE  0x0800
> +#define VS_STOP_1        0x1000
> +#define VS_STOP_2        0x2000

> +#define VS_SNM 0x0001
> +#define VS_CNM 0x0002
> +#define VS_SLB 0x0003
> +#define VS_CLB 0x0004

Can you use TABs to indent?

...

> +/* Represents a virtual tty device in this virtual card */
> +struct vs_dev {
> +	/* index for this device in tty core */

Convert these comments to kernel-doc.

> +	unsigned int own_index;
> +	/* index of the device to which this device is connected */
> +	unsigned int peer_index;
> +	/* shadow modem status register */
> +	int msr_reg;
> +	/* shadow modem control register */
> +	int mcr_reg;
> +	/* rts line connections for this device */
> +	int rts_mappings;
> +	/* dtr line connections for this device */
> +	int dtr_mappings;
> +	int set_odtr_at_open;
> +	int set_pdtr_at_open;
> +	int odevtyp;
> +	/* mutual exclusion at device level */
> +	struct mutex lock;
> +	int is_break_on;
> +	/* currently active baudrate */
> +	int baud;
> +	int uart_frame;
> +	int waiting_msr_chg;
> +	int tx_paused;
> +	int faulty_cable;
> +	struct tty_struct *own_tty;
> +	struct tty_struct *peer_tty;
> +	struct serial_struct serial;
> +	struct async_icount icount;
> +	struct device *device;
> +};

...

> +static ssize_t event_store(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	int ret, push = 1;
> +	struct vs_dev *local_vsdev = dev_get_drvdata(dev);
> +	struct tty_struct *tty_to_write = local_vsdev->own_tty;
> +

> +	if (!buf || (count <= 0))

On which circumstances the count can be < 0 ?!
Have you checked when it can be 0 here? Can it at all?

> +		return -EINVAL;
> +
> +	/*
> +	 * Ensure required structure has been allocated, initialized and
> +	 * port has been opened.
> +	 */
> +	if (!tty_to_write || (tty_to_write->port == NULL)
> +			|| (tty_to_write->port->count <= 0))

Better formatting style and indentation, please.

> +		return -EIO;

When port->count can be less than zero?

> +	if (!test_bit(ASYNCB_INITIALIZED, &tty_to_write->port->flags))
> +		return -EIO;
> +
> +	mutex_lock(&local_vsdev->lock);
> +
> +	switch (buf[0]) {
> +	case '1':
> +		ret = tty_insert_flip_char(tty_to_write->port, -7, TTY_FRAME);
> +		if (ret < 0)
> +			goto fail;
> +		local_vsdev->icount.frame++;
> +		break;
> +	case '2':
> +		ret = tty_insert_flip_char(tty_to_write->port, -8, TTY_PARITY);
> +		if (ret < 0)
> +			goto fail;
> +		local_vsdev->icount.parity++;
> +		break;
> +	case '3':
> +		ret = tty_insert_flip_char(tty_to_write->port, 0, TTY_OVERRUN);
> +		if (ret < 0)
> +			goto fail;
> +		local_vsdev->icount.overrun++;
> +		break;
> +	case '4':
> +		local_vsdev->msr_reg |= VS_MSR_RI;
> +		local_vsdev->icount.rng++;
> +		push = -1;
> +		break;
> +	case '5':
> +		local_vsdev->msr_reg &= ~VS_MSR_RI;
> +		local_vsdev->icount.rng++;
> +		push = -1;
> +		break;
> +	case '6':
> +		ret = tty_insert_flip_char(tty_to_write->port, 0, TTY_BREAK);
> +		if (ret < 0)
> +			goto fail;
> +		local_vsdev->icount.brk++;
> +		break;
> +	case '7':
> +		local_vsdev->faulty_cable = 1;
> +		push = -1;
> +		break;
> +	case '8':
> +		local_vsdev->faulty_cable = 0;
> +		push = -1;
> +		break;
> +	default:
> +		mutex_unlock(&local_vsdev->lock);
> +		return -EINVAL;
> +	}
> +
> +	if (push)
> +		tty_flip_buffer_push(tty_to_write->port);
> +	ret = count;
> +
> +fail:
> +	mutex_unlock(&local_vsdev->lock);
> +	return ret;
> +}
> +static DEVICE_ATTR_WO(event);
> +
> +static struct attribute *ttyvs_attrs[] = {
> +	&dev_attr_event.attr,

> +	NULL,

No comma for terminator line.

> +};
> +ATTRIBUTE_GROUPS(ttyvs);
> +
> +/*
> + * Checks if the given serial port has received its carrier detect
> + * line raised or not. Return 1 if the carrier is raised otherwise 0.
> + */
> +static int vs_port_carrier_raised(struct tty_port *port)
> +{
> +	struct vs_dev *local_vsdev = idr_find(&db, port->tty->index);
> +

> +	return (local_vsdev->msr_reg & VS_MSR_DCD) ? 1 : 0;

Redundant ternary. Use !! if you wish to tight the values to [0..1] range, but
rather simple drop the ternary.

> +}
> +
> +/* Shutdown the given serial port */
> +static void vs_port_shutdown(struct tty_port *port)
> +{
> +	pr_debug("shutting down the port!\n");

dev_dbg()
Everywhere where you have struct device available use dev_*() instead of pr_*().

> +}

...

> +/*
> + * Update modem control and status registers according to the bit
> + * mask(s) provided. The RTS and DTR values can be set only if the
> + * current handshaking state of the tty device allows direct control
> + * of the modem control lines. The pin mappings are honoured.
> + *
> + * Caller holds lock of thegiven virtual tty device.
> + */
> +static int vs_update_modem_lines(struct tty_struct *tty,
> +			unsigned int set, unsigned int clear)
> +{
> +	int ctsint = 0;
> +	int dcdint = 0;
> +	int dsrint = 0;
> +	int rngint = 0;

> +	int mcr_ctrl_reg = 0;

Redundant assignment.
Also check other variables here, and in entire code.

> +	int wakeup_blocked_open = 0;
> +	int rts_mappings, dtr_mappings, msr_state_reg;
> +	struct async_icount *evicount;
> +	struct vs_dev *vsdev, *local_vsdev, *remote_vsdev;
> +
> +	local_vsdev = idr_find(&db, tty->index);
> +
> +	/* Read modify write MSR register */
> +	if (tty->index != local_vsdev->peer_index) {
> +		remote_vsdev = idr_find(&db, local_vsdev->peer_index);
> +		msr_state_reg = remote_vsdev->msr_reg;
> +		vsdev = remote_vsdev;
> +	} else {
> +		msr_state_reg = local_vsdev->msr_reg;
> +		vsdev = local_vsdev;
> +	}
> +
> +	rts_mappings = local_vsdev->rts_mappings;
> +	dtr_mappings = local_vsdev->dtr_mappings;
> +
> +	if (set & TIOCM_RTS) {
> +		mcr_ctrl_reg |= VS_MCR_RTS;
> +		if ((rts_mappings & VS_CON_CTS) == VS_CON_CTS) {
> +			msr_state_reg |= VS_MSR_CTS;
> +			ctsint++;
> +		}
> +		if ((rts_mappings & VS_CON_DCD) == VS_CON_DCD) {
> +			msr_state_reg |= VS_MSR_DCD;
> +			dcdint++;
> +			wakeup_blocked_open = 1;
> +		}
> +		if ((rts_mappings & VS_CON_DSR) == VS_CON_DSR) {
> +			msr_state_reg |= VS_MSR_DSR;
> +			dsrint++;
> +		}
> +		if ((rts_mappings & VS_CON_RI) == VS_CON_RI) {
> +			msr_state_reg |= VS_MSR_RI;
> +			rngint++;
> +		}
> +	}
> +
> +	if (set & TIOCM_DTR) {
> +		mcr_ctrl_reg |= VS_MCR_DTR;
> +		if ((dtr_mappings & VS_CON_CTS) == VS_CON_CTS) {
> +			msr_state_reg |= VS_MSR_CTS;
> +			ctsint++;
> +		}
> +		if ((dtr_mappings & VS_CON_DCD) == VS_CON_DCD) {
> +			msr_state_reg |= VS_MSR_DCD;
> +			dcdint++;
> +			wakeup_blocked_open = 1;
> +		}
> +		if ((dtr_mappings & VS_CON_DSR) == VS_CON_DSR) {
> +			msr_state_reg |= VS_MSR_DSR;
> +			dsrint++;
> +		}
> +		if ((dtr_mappings & VS_CON_RI) == VS_CON_RI) {
> +			msr_state_reg |= VS_MSR_RI;
> +			rngint++;
> +		}
> +	}
> +
> +	if (clear & TIOCM_RTS) {
> +		mcr_ctrl_reg &= ~VS_MCR_RTS;
> +		if ((rts_mappings & VS_CON_CTS) == VS_CON_CTS) {
> +			msr_state_reg &= ~VS_MSR_CTS;
> +			ctsint++;
> +		}
> +		if ((rts_mappings & VS_CON_DCD) == VS_CON_DCD) {
> +			msr_state_reg &= ~VS_MSR_DCD;
> +			dcdint++;
> +		}
> +		if ((rts_mappings & VS_CON_DSR) == VS_CON_DSR) {
> +			msr_state_reg &= ~VS_MSR_DSR;
> +			dsrint++;
> +		}
> +		if ((rts_mappings & VS_CON_RI) == VS_CON_RI) {
> +			msr_state_reg &= ~VS_MSR_RI;
> +			rngint++;
> +		}
> +	}
> +
> +	if (clear & TIOCM_DTR) {
> +		mcr_ctrl_reg &= ~VS_MCR_DTR;
> +		if ((dtr_mappings & VS_CON_CTS) == VS_CON_CTS) {
> +			msr_state_reg &= ~VS_MSR_CTS;
> +			ctsint++;
> +		}
> +		if ((dtr_mappings & VS_CON_DCD) == VS_CON_DCD) {
> +			msr_state_reg &= ~VS_MSR_DCD;
> +			dcdint++;
> +		}
> +		if ((dtr_mappings & VS_CON_DSR) == VS_CON_DSR) {
> +			msr_state_reg &= ~VS_MSR_DSR;
> +			dsrint++;
> +		}
> +		if ((dtr_mappings & VS_CON_RI) == VS_CON_RI) {
> +			msr_state_reg &= ~VS_MSR_RI;
> +			rngint++;
> +		}
> +	}
> +
> +	local_vsdev->mcr_reg = mcr_ctrl_reg;
> +	vsdev->msr_reg = msr_state_reg;
> +
> +	evicount = &vsdev->icount;
> +	evicount->cts += ctsint;
> +	evicount->dsr += dsrint;
> +	evicount->dcd += dcdint;
> +	evicount->rng += rngint;
> +
> +	if (vsdev->own_tty && vsdev->own_tty->port) {
> +		/* Wake up process blocked on TIOCMIWAIT ioctl */
> +		if ((vsdev->waiting_msr_chg == 1) &&
> +				(vsdev->own_tty->port->count > 0)) {
> +			wake_up_interruptible(
> +					&vsdev->own_tty->port->delta_msr_wait);
> +		}
> +
> +		/* Wake up application blocked on carrier detect signal */
> +		if ((wakeup_blocked_open == 1) &&
> +				(vsdev->own_tty->port->blocked_open > 0)) {
> +			wake_up_interruptible(&vsdev->own_tty->port->open_wait);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Invoked when user space process opens a serial port. The tty core
> + * calls this to install tty and initialize the required resources.
> + */
> +static int vs_install(struct tty_driver *drv, struct tty_struct *tty)
> +{
> +	int ret;
> +	struct tty_port *port;
> +

> +	port = kcalloc(1, sizeof(struct tty_port), GFP_KERNEL);

What the point of kcalloc(1, ...) ?

> +	if (!port)
> +		return -ENOMEM;
> +
> +	/* First initialize and then set port operations */
> +	tty_port_init(port);
> +	port->ops = &vs_port_ops;
> +
> +	ret = tty_port_install(port, drv, tty);
> +	if (ret) {
> +		kfree(port);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Invoked when there exist no user process or tty is to be
> + * released explicitly for whatever reason.
> + */
> +static void vs_cleanup(struct tty_struct *tty)
> +{
> +	tty_port_put(tty->port);
> +}
> +
> +/*
> + * Called when open system call is called on virtual tty device node.
> + * The tty core allocates 'struct tty_struct' for this device and
> + * set up various resources, sets up line discipline and call this
> + * function. For first time allocation happens and from next time
> + * onwards only re-opening happens.
> + *
> + * The tty core finds the tty driver serving this device node and the
> + * index of this tty device as registered by this driver with tty core.
> + * From this inded we retrieve the virtual tty device to work on.
> + *
> + * If the same serial port is opened more than once, the tty structure
> + * passed to this function will be same but filp structure will be
> + * different every time. Caller holds tty lock.
> + *
> + * This driver does not set CLOCAL by default. This means that the
> + * open() system call will block until it find its carrier detect
> + * line raised. Application should use O_NONBLOCK/O_NDELAY flag if
> + * it does not want to wait for DCD line change.
> + */
> +static int vs_open(struct tty_struct *tty, struct file *filp)
> +{
> +	int ret;
> +	struct vs_dev *remote_vsdev;
> +	struct vs_dev *local_vsdev = idr_find(&db, tty->index);
> +
> +	local_vsdev->own_tty = tty;
> +
> +	/*
> +	 * If this device is one end of a null modem connection,
> +	 * provide its address to remote end.
> +	 */
> +	if (tty->index != local_vsdev->peer_index) {
> +		remote_vsdev = idr_find(&db, local_vsdev->peer_index);
> +		remote_vsdev->peer_tty = tty;
> +	}
> +
> +	memset(&local_vsdev->serial, 0, sizeof(struct serial_struct));
> +	memset(&local_vsdev->icount, 0, sizeof(struct async_icount));
> +
> +	/*
> +	 * Handle DTR raising logic ourselve instead of tty_port helpers
> +	 * doing it. Locking virtual tty is not required here.
> +	 */
> +	if (local_vsdev->set_odtr_at_open == 1)
> +		vs_update_modem_lines(tty, TIOCM_DTR | TIOCM_RTS, 0);
> +
> +	/* Associate tty with port and do port level opening. */
> +	ret = tty_port_open(tty->port, tty, filp);
> +	if (ret)
> +		return ret;
> +
> +	tty->port->close_delay  = 0;
> +	tty->port->closing_wait = ASYNC_CLOSING_WAIT_NONE;
> +	tty->port->drain_delay  = 0;
> +
> +	return ret;
> +}
> +
> +/*
> + * Invoked by tty layer when release() is called on the file pointer
> + * that was previously created with a call to open().
> + */
> +static void vs_close(struct tty_struct *tty, struct file *filp)
> +{
> +	if (test_bit(TTY_IO_ERROR, &tty->flags))
> +		return;
> +
> +	if (tty && filp && tty->port && (tty->port->count > 0))
> +		tty_port_close(tty->port, tty, filp);
> +
> +	if (tty && C_HUPCL(tty) && tty->port && (tty->port->count < 1))
> +		vs_update_modem_lines(tty, 0, TIOCM_DTR | TIOCM_RTS);
> +}
> +
> +/*
> + * Invoked when write() system call is invoked on device node.
> + * This function constructs evry byte as per the current uart
> + * frame settings. Finally, the data is inserted into the tty
> + * buffer of the receiver tty device.
> + */
> +static int vs_write(struct tty_struct *tty,
> +			const unsigned char *buf, int count)
> +{
> +	int x;
> +	unsigned char *data = NULL;
> +	struct tty_struct *tty_to_write = NULL;
> +	struct vs_dev *rx_vsdev = NULL;
> +	struct vs_dev *tx_vsdev = idr_find(&db, tty->index);

> +	if (tx_vsdev->tx_paused || !tty || tty->stopped
> +			|| (count < 1) || !buf || tty->hw_stopped)

Indentation issue.
Fix in entire code.

> +		return 0;
> +
> +	if (tx_vsdev->is_break_on == 1) {
> +		pr_debug("break condition is on!\n");
> +		return -EIO;
> +	}
> +
> +	if (tx_vsdev->faulty_cable == 1)
> +		return count;
> +
> +	if (tty->index != tx_vsdev->peer_index) {
> +		/* Null modem */
> +		tty_to_write = tx_vsdev->peer_tty;
> +		rx_vsdev = idr_find(&db, tx_vsdev->peer_index);
> +
> +		if ((tx_vsdev->baud != rx_vsdev->baud) ||
> +			(tx_vsdev->uart_frame != rx_vsdev->uart_frame)) {
> +			/*
> +			 * Emulate data sent but not received due to
> +			 * mismatched baudrate/framing.
> +			 */
> +			pr_debug("mismatched serial port settings!\n");
> +			tx_vsdev->icount.tx++;
> +			return count;
> +		}
> +	} else {
> +		/* Loop back */
> +		tty_to_write = tty;
> +		rx_vsdev = tx_vsdev;
> +	}
> +
> +	if (tty_to_write) {
> +		if ((tty_to_write->termios.c_cflag & CSIZE) == CS8) {
> +			data = (unsigned char *)buf;
> +		} else {
> +			data = kcalloc(count, sizeof(char), GFP_KERNEL);
> +			if (!data)
> +				return -ENOMEM;
> +
> +			/* Emulate correct number of data bits */
> +			switch (tty_to_write->termios.c_cflag & CSIZE) {
> +			case CS7:
> +				for (x = 0; x < count; x++)
> +					data[x] = buf[x] & 0x7F;
> +				break;
> +			case CS6:
> +				for (x = 0; x < count; x++)
> +					data[x] = buf[x] & 0x3F;
> +				break;
> +			case CS5:
> +				for (x = 0; x < count; x++)
> +					data[x] = buf[x] & 0x1F;
> +				break;

> +			default:
> +				data = (unsigned char *)buf;

When this possible?

> +			}
> +		}
> +
> +		tty_insert_flip_string(tty_to_write->port, data, count);
> +		tty_flip_buffer_push(tty_to_write->port);
> +		tx_vsdev->icount.tx++;
> +		rx_vsdev->icount.rx++;
> +

> +		if (data != buf)
> +			kfree(data);

> +	} else {
> +		/*
> +		 * Other end is still not opened, emulate transmission from
> +		 * local end but don't make other end receive it as is the
> +		 * case in real world.
> +		 */
> +		tx_vsdev->icount.tx++;
> +	}
> +
> +	return count;
> +}

...

> +	info.type		    = PORT_UNKNOWN;
> +	info.line		    = serial.line;
> +	info.port		    = tty->index;
> +	info.irq			= 0;
> +	info.flags		    = tty->port->flags;
> +	info.xmit_fifo_size = 0;
> +	info.baud_base	    = 0;
> +	info.close_delay	= tty->port->close_delay;
> +	info.closing_wait   = tty->port->closing_wait;
> +	info.custom_divisor = 0;
> +	info.hub6		    = 0;
> +	info.io_type		= SERIAL_IO_MEM;

Full of indentation issues.

> +
> +	ret = copy_to_user((void __user *)arg, &info,
> +				sizeof(struct serial_struct));

Wouldn't

	if (copy_to_user(...))
		return -EFAULT;
	return 0;

work better?

> +
> +	return ret ? -EFAULT : 0;

...

> +	u32 baud;

u32? Why?

...

> +static int vs_ioctl(struct tty_struct *tty,
> +				unsigned int cmd, unsigned long arg)
> +{
> +	switch (cmd) {
> +	case TIOCGSERIAL:
> +		return vs_get_serinfo(tty, arg);
> +	case TIOCMIWAIT:
> +		return vs_wait_change(tty, arg);
> +	}
> +

> +	return -ENOIOCTLCMD;

Perhaps this should be default case above.

> +}

...

> +static void vs_throttle(struct tty_struct *tty)
> +{
> +	struct vs_dev *local_vsdev = idr_find(&db, tty->index);
> +	struct vs_dev *remote_vsdev = idr_find(&db, local_vsdev->peer_index);
> +
> +	if (tty->termios.c_cflag & CRTSCTS) {
> +		mutex_lock(&local_vsdev->lock);
> +		remote_vsdev->tx_paused = 1;
> +		vs_update_modem_lines(tty, 0, TIOCM_RTS);
> +		mutex_unlock(&local_vsdev->lock);

> +	} else if ((tty->termios.c_iflag & IXON) ||
> +				(tty->termios.c_iflag & IXOFF)) {

Indentation issues. Fix in every alike places.

> +		vs_put_char(tty, STOP_CHAR(tty));
> +	} else {
> +		/* do nothing */
> +	}
> +}

...

> +static int vs_tiocmget(struct tty_struct *tty)
> +{
> +	int status, msr_reg, mcr_reg;
> +	struct vs_dev *local_vsdev = idr_find(&db, tty->index);
> +
> +	mutex_lock(&local_vsdev->lock);
> +	mcr_reg = local_vsdev->mcr_reg;
> +	msr_reg = local_vsdev->msr_reg;
> +	mutex_unlock(&local_vsdev->lock);
> +

> +	status = ((mcr_reg & VS_MCR_DTR)  ? TIOCM_DTR  : 0) |
> +			 ((mcr_reg & VS_MCR_RTS)  ? TIOCM_RTS  : 0) |
> +			 ((mcr_reg & VS_MCR_LOOP) ? TIOCM_LOOP : 0) |
> +			 ((msr_reg & VS_MSR_DCD)  ? TIOCM_CAR  : 0) |
> +			 ((msr_reg & VS_MSR_RI)   ? TIOCM_RI   : 0) |
> +			 ((msr_reg & VS_MSR_CTS)  ? TIOCM_CTS  : 0) |
> +			 ((msr_reg & VS_MSR_DSR)  ? TIOCM_DSR  : 0);

Why not to indent by first line properly?
Fix this in all similar places.

> +	return status;
> +}

...

> +static int vs_break_ctl(struct tty_struct *tty, int break_state)
> +{
> +	struct tty_struct *tty_to_write;
> +	struct vs_dev *brk_rx_vsdev;
> +	struct vs_dev *brk_tx_vsdev = idr_find(&db, tty->index);
> +
> +	if (tty->index != brk_tx_vsdev->peer_index) {
> +		tty_to_write = brk_tx_vsdev->peer_tty;
> +		brk_rx_vsdev = idr_find(&db, brk_tx_vsdev->peer_index);
> +	} else {
> +		tty_to_write = tty;
> +		brk_rx_vsdev = brk_tx_vsdev;
> +	}
> +
> +	mutex_lock(&brk_tx_vsdev->lock);
> +

> +	if (break_state != 0) {

	if (break_state) {

> +		if (brk_tx_vsdev->is_break_on == 1)
> +			return 0;
> +
> +		brk_tx_vsdev->is_break_on = 1;
> +		if (tty_to_write != NULL) {
> +			tty_insert_flip_char(tty_to_write->port, 0, TTY_BREAK);
> +			tty_flip_buffer_push(tty_to_write->port);
> +			brk_rx_vsdev->icount.brk++;
> +		}
> +	} else {
> +		brk_tx_vsdev->is_break_on = 0;
> +	}
> +
> +	mutex_unlock(&brk_tx_vsdev->lock);
> +	return 0;
> +}

...

> +static void vs_send_xchar(struct tty_struct *tty, char ch)
> +{
> +	int was_paused;
> +	struct vs_dev *local_vsdev = idr_find(&db, tty->index);
> +
> +	was_paused = local_vsdev->tx_paused;
> +	if (was_paused)
> +		local_vsdev->tx_paused = 0;
> +
> +	vs_put_char(tty, ch);
> +	if (was_paused)
> +		local_vsdev->tx_paused = 1;


Can it be refactored like

	if (local_vsdev->tx_paused) {
		local_vsdev->tx_paused = 0;
		vs_put_char(tty, ch);
		local_vsdev->tx_paused = 1;
	} else {
		vs_put_char(tty, ch);
	}

?

> +}

...

> +static int vs_del_specific_devs(int ownidx, int free_idr)
> +{
> +	struct vs_dev *vsdev1, *vsdev2;
> +
> +	/*
> +	 * If user just created configfs item but did not populated valid
> +	 * index, device will not exist, so bail out early.
> +	 */
> +	vsdev1 = idr_find(&db, ownidx);
> +	if (!vsdev1)
> +		return 0;
> +
> +	vs_unreg_one_dev(ownidx, vsdev1);
> +
> +	/* If this device is part of a null modem, delete peer also */
> +	if (vsdev1->own_index != vsdev1->peer_index) {
> +		vsdev2 = idr_find(&db, vsdev1->peer_index);
> +		if (vsdev2) {
> +			vs_unreg_one_dev(vsdev2->own_index, vsdev2);

> +			if (free_idr)

This...

> +				idr_remove(&db, vsdev2->own_index);
> +			kfree(vsdev2);
> +		}
> +	}

> +	if (free_idr)

...and this. Can you elaborate in which case we won't free IDR?

> +		idr_remove(&db, ownidx);
> +	kfree(vsdev1);
> +
> +	return 0;
> +}

...

> +static int vs_alloc_reg_one_dev(int oidx, int pidx, int rtsmap,
> +			int dtrmap, int dtropn)
> +{
> +	int ret, id;
> +	struct vs_dev *vsdev;
> +	struct device *dev;
> +
> +	/* Allocate and init virtual tty device's private data */

> +	vsdev = kcalloc(1, sizeof(struct vs_dev), GFP_KERNEL);

What the point of kcalloc(1, ...)?

> +	if (!vsdev)
> +		return -ENOMEM;
> +
> +	id = idr_alloc(&db, vsdev, oidx, oidx + 1, GFP_KERNEL);
> +	if (id < 0) {
> +		ret = id;
> +		goto fail_id;
> +	}
> +
> +	vsdev->own_tty = NULL;
> +	vsdev->peer_tty = NULL;
> +	vsdev->own_index = oidx;
> +	vsdev->peer_index =  pidx;
> +	vsdev->rts_mappings = rtsmap;
> +	vsdev->dtr_mappings = dtrmap;
> +	vsdev->set_odtr_at_open = dtropn;
> +	vsdev->msr_reg = 0;
> +	vsdev->mcr_reg = 0;
> +	vsdev->waiting_msr_chg = 0;
> +	vsdev->tx_paused = 0;
> +	vsdev->faulty_cable = 0;
> +	mutex_init(&vsdev->lock);
> +
> +	/*
> +	 * Register with tty core with a specific minor number.
> +	 * Driver core itself will create sysfs nodes (ttyvs_groups).
> +	 */
> +	dev = tty_register_device_attr(ttyvs_driver, oidx, NULL,
> +				vsdev, ttyvs_groups);
> +	if (!dev) {
> +		ret = -ENOMEM;
> +		goto fail_reg;
> +	}
> +
> +	vsdev->device = dev;
> +	return 0;
> +
> +fail_reg:
> +	idr_remove(&db, id);
> +fail_id:
> +	kfree(vsdev);
> +	return ret;
> +}

...

> +		*dtratopen = di->pdtratopn ? 1 : 0;

> +		*dtratopen = di->odtratopn  ? 1 : 0;

Do you need ternary? (Btw, second one has indentation issues)

...

> +static int vs_extract_dev_param_dt(const struct device_node *np,
> +			unsigned int *idx, int *rtsmap, int *dtrmap,
> +			int *dtratopen, int exclude)
> +{
> +	int ret;
> +
> +	ret = of_property_read_u32(np, "dev-num", idx);
> +	if (ret)
> +		return ret;
> +
> +	if (*idx >= max_num_vs_devs)
> +		return -EINVAL;
> +
> +	ret = vs_parse_dt_get_map(np, "rtsmap", rtsmap);
> +	if (ret)
> +		return ret;
> +
> +	ret = vs_parse_dt_get_map(np, "dtrmap", dtrmap);
> +	if (ret)
> +		return ret;
> +

> +	*dtratopen = of_property_read_bool(np,
> +						"set-dtr-at-open") ? 1 : 0;

Why ternary, why two lines?

> +
> +	return 0;
> +}

...

> +fail:

fail_unlock: will better describe what you are doing here.
Same applies to other labels (revisit them all).

> +	mutex_unlock(&card_lock);
> +	return ret;

...

> +static const struct tty_operations vs_serial_ops = {
> +	.install	     = vs_install,
> +	.cleanup	     = vs_cleanup,
> +	.open	         = vs_open,
> +	.close	         = vs_close,
> +	.write	         = vs_write,
> +	.put_char	     = vs_put_char,
> +	.flush_chars     = vs_flush_chars,
> +	.write_room      = vs_write_room,
> +	.chars_in_buffer = vs_chars_in_buffer,
> +	.ioctl	         = vs_ioctl,
> +	.set_termios     = vs_set_termios,
> +	.throttle	     = vs_throttle,
> +	.unthrottle      = vs_unthrottle,
> +	.stop	         = vs_stop,
> +	.start	         = vs_start,
> +	.hangup	         = vs_hangup,
> +	.break_ctl       = vs_break_ctl,
> +	.flush_buffer    = vs_flush_buffer,
> +	.wait_until_sent = vs_wait_until_sent,
> +	.send_xchar      = vs_send_xchar,
> +	.tiocmget	     = vs_tiocmget,
> +	.tiocmset	     = vs_tiocmset,
> +	.get_icount      = vs_get_icount,
> +};

Your code has enormous amount of indentation issues. Please, fix your editor
settings or do something about it.

...

> +		if (of_property_read_u32(child, "peer-dev", &peer)) {
> +			ret = vs_add_lb(NULL, child);
> +			if (ret) {
> +				pr_err("can't create lb %s %d\n",
> +						child->name, ret);
> +				continue;
> +			}
> +		} else {

> +			peer_node = of_find_node_by_phandle(peer);
> +			if (peer_node) {
> +				of_node_set_flag(peer_node, OF_POPULATED);
> +				ret = vs_add_nm(NULL, child, peer_node);
> +				if (ret) {
> +					pr_err("can't create nm %s <-> %s %d\n",
> +						child->name, peer_node->name,
> +						ret);
> +					continue;
> +				}
> +			} else {
> +				pr_err("can't find peer for %s %d\n",
> +						child->name, ret);
> +			}

Besides pr_err(), I guess should be dev_err() or so, above looks like OF voodoo
magic which I believe already implemented in OF framework. Care to think about
it?

> +		}

...

> +	return container_of(to_config_group(item),
> +				struct vs_cfs_dev_info, grp);

It's perfectly one line. Why two?

...

> +static ssize_t vs_dev_create_store(struct config_item *item,
> +		const char *page, size_t len)
> +{
> +	u8 val;
> +	int ret;
> +	struct vs_cfs_dev_info *di;
> +
> +	ret = kstrtou8(page, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	/* User must write 1 to this node create device */
> +	if (val != 1)
> +		return -EINVAL;

Why above it's not boolean? Why this doesn't accept 0?
Can't you simple ignore 'false' case?

> +
> +	di = to_vs_dinfo(item);
> +
> +	/* devtype must be defined to proceed further */
> +	if (!di->devtype)
> +		return -EINVAL;
> +
> +	if (strncmp(di->devtype, "lb", 2) == 0)
> +		ret = vs_add_lb(di, NULL);
> +	else if (strncmp(di->devtype, "nm", 2) == 0)
> +		ret = vs_add_nm(di, NULL, NULL);
> +	else
> +		return -EINVAL;


match_string() / sysfs_match_string() ?

> +	if (ret)
> +		return ret;
> +	return len;
> +}

...

> +VS_DEV_ATTR_WR_STR(devtype)
> +VS_DEV_ATTR_WR_U16(ownidx)
> +VS_DEV_ATTR_WR_U16(peeridx)
> +VS_DEV_ATTR_WR_U8(ortsmap)
> +VS_DEV_ATTR_WR_U8(odtrmap)
> +VS_DEV_ATTR_WR_U8(odtratopn)
> +VS_DEV_ATTR_WR_U8(prtsmap)
> +VS_DEV_ATTR_WR_U8(pdtrmap)
> +VS_DEV_ATTR_WR_U8(pdtratopn)

Where are semicolons? Above looks fragile.

...

> +static struct configfs_attribute *vs_dev_attrs[] = {
> +	&vs_dev_attr_devtype,
> +	&vs_dev_attr_ownidx,
> +	&vs_dev_attr_ortsmap,
> +	&vs_dev_attr_odtrmap,
> +	&vs_dev_attr_odtratopn,
> +	&vs_dev_attr_peeridx,
> +	&vs_dev_attr_prtsmap,
> +	&vs_dev_attr_pdtrmap,
> +	&vs_dev_attr_pdtratopn,
> +	&vs_dev_attr_create,

> +	NULL,

No comma for terminator line.

> +};

> +/*
> + * By default this driver supports upto 64 virtual devices. This
> + * can be overridden through max_num_vs_devs module parameter or
> + * through max-num-vs-devs device tree property.
> + */
> +module_param(max_num_vs_devs, ushort, 0);
> +MODULE_PARM_DESC(max_num_vs_devs,
> +		"Maximum virtual tty devices to be supported");

Can't you update this dynamically thru sysfs?

-- 
With Best Regards,
Andy Shevchenko





[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux