Re: [PATCH] USB: serial: mos7840: add support for setting custom baud rate

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

 



Hi Johan,

Could you, please, give some feedback on the patch I've send recently?

Regards,
Peter

On Thu, May 11, 2017 at 09:12:48PM +0300, Peter Mamonov wrote:
> The patch implements TIOCSSERIAL ioctl call. The following fields of the
> `struct serial_struct` are processed:
> 
> - flags: ASYNC_SPD_MASK bits are processed.
> 
> - baud_base: allow a user to specify arbitrary value less or equal the
>   maximum port speed. Use it later in conjunction with custom_divisor to
>   calculate the desired baud rate.
> 
> - custom_divisor: save a user supplied value, use it later for baud rate
>   calculation.
> 
> Custom baud rate may be applied using any combination of baud_base /
> custom_divisor as follows:
> 
> 	# stty -F /dev/ttyUSBX 38400
> 	# setserial /dev/ttyUSBX baud_base 1000 divisor 1 spd_cust # 1 kBaud
> 	# setserial /dev/ttyUSBX baud_base 42000 divisor 42 spd_cust # 1 kBaud
> 
> The patch is based on the code from the drivers/usb/serial/ftdi_sio.c.
> 
> Signed-off-by: Peter Mamonov <pmamonov@xxxxxxxxx>
> ---
>  drivers/usb/serial/mos7840.c | 99 ++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 95 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
> index e8669aae14b3..93093e27d6b5 100644
> --- a/drivers/usb/serial/mos7840.c
> +++ b/drivers/usb/serial/mos7840.c
> @@ -244,6 +244,12 @@ struct moschip_port {
>  	struct usb_ctrlrequest *led_dr;
>  
>  	unsigned long flags;
> +
> +	int serial_flags;
> +	int baud_base;
> +	int custom_divisor;
> +
> +	struct mutex cfg_lock;
>  };
>  
>  /*
> @@ -1554,6 +1560,7 @@ static int mos7840_tiocmset(struct tty_struct *tty,
>   *	this function calculates the proper baud rate divisor for the specified
>   *	baud rate.
>   *****************************************************************************/
> +#define MAX_BAUD_RATE 3145728
>  static int mos7840_calc_baud_rate_divisor(struct usb_serial_port *port,
>  					  int baudRate, int *divisor,
>  					  __u16 *clk_sel_val)
> @@ -1582,8 +1589,8 @@ static int mos7840_calc_baud_rate_divisor(struct usb_serial_port *port,
>  	} else if ((baudRate > 921600) && (baudRate <= 1572864)) {
>  		*divisor = 1572864 / baudRate;
>  		*clk_sel_val = 0x60;
> -	} else if ((baudRate > 1572864) && (baudRate <= 3145728)) {
> -		*divisor = 3145728 / baudRate;
> +	} else if ((baudRate > 1572864) && (baudRate <= MAX_BAUD_RATE)) {
> +		*divisor = MAX_BAUD_RATE / baudRate;
>  		*clk_sel_val = 0x70;
>  	}
>  	return 0;
> @@ -1733,6 +1740,8 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
>  		return;
>  	}
>  
> +	mutex_lock(&mos7840_port->cfg_lock);
> +
>  	lData = LCR_BITS_8;
>  	lStop = LCR_STOP_1;
>  	lParity = LCR_PAR_NONE;
> @@ -1831,6 +1840,16 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
>  	/* Determine divisor based on baud rate */
>  	baud = tty_get_baud_rate(tty);
>  
> +	if (baud == 38400 &&
> +	    (mos7840_port->serial_flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
> +	    mos7840_port->custom_divisor) {
> +		baud = mos7840_port->baud_base / mos7840_port->custom_divisor;
> +		dev_dbg(&port->dev, "Set custom baudrate (%d / %d) = %d",
> +			mos7840_port->baud_base,
> +			mos7840_port->custom_divisor,
> +			baud);
> +	}
> +
>  	if (!baud) {
>  		/* pick a default, any default... */
>  		dev_dbg(&port->dev, "%s", "Picked default baud...\n");
> @@ -1855,6 +1874,8 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
>  	}
>  	dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is End %x\n", __func__,
>  		mos7840_port->shadowLCR);
> +
> +	mutex_unlock(&mos7840_port->cfg_lock);
>  }
>  
>  /*****************************************************************************
> @@ -1950,20 +1971,86 @@ static int mos7840_get_serial_info(struct moschip_port *mos7840_port,
>  
>  	memset(&tmp, 0, sizeof(tmp));
>  
> +	mutex_lock(&mos7840_port->cfg_lock);
> +
>  	tmp.type = PORT_16550A;
>  	tmp.line = mos7840_port->port->minor;
>  	tmp.port = mos7840_port->port->port_number;
>  	tmp.irq = 0;
> +	tmp.flags = mos7840_port->serial_flags;
>  	tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
> -	tmp.baud_base = 9600;
> +	tmp.baud_base = mos7840_port->baud_base;
> +	tmp.custom_divisor = mos7840_port->custom_divisor;
>  	tmp.close_delay = 5 * HZ;
>  	tmp.closing_wait = 30 * HZ;
>  
> +	mutex_unlock(&mos7840_port->cfg_lock);
> +
>  	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
>  		return -EFAULT;
>  	return 0;
>  }
>  
> +static int mos7840_set_serial_info(struct tty_struct *tty,
> +			   struct moschip_port *priv,
> +			   struct serial_struct __user *newinfo)
> +{
> +	struct serial_struct new_serial;
> +	int old_flags = priv->serial_flags;
> +	int old_divisor = priv->custom_divisor;
> +	int old_base = priv->baud_base;
> +
> +	if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
> +		return -EFAULT;
> +	mutex_lock(&priv->cfg_lock);
> +
> +	if (!capable(CAP_SYS_ADMIN)) {
> +		if (((new_serial.flags & ~ASYNC_USR_MASK) !=
> +		     (priv->serial_flags & ~ASYNC_USR_MASK))) {
> +			mutex_unlock(&priv->cfg_lock);
> +			return -EPERM;
> +		}
> +		priv->serial_flags = ((priv->serial_flags & ~ASYNC_USR_MASK) |
> +			       (new_serial.flags & ASYNC_USR_MASK));
> +		priv->custom_divisor = new_serial.custom_divisor;
> +		goto check_and_exit;
> +	}
> +
> +	if (new_serial.baud_base > MAX_BAUD_RATE) {
> +		mutex_unlock(&priv->cfg_lock);
> +		return -EINVAL;
> +	}
> +	/* Save user supplied value, use it later to calculate the baudrate. */
> +	priv->baud_base = new_serial.baud_base;
> +
> +	priv->serial_flags = ((priv->serial_flags & ~ASYNC_FLAGS) |
> +					(new_serial.flags & ASYNC_FLAGS));
> +	priv->custom_divisor = new_serial.custom_divisor;
> +check_and_exit:
> +	if ((priv->serial_flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
> +		tty->alt_speed = 57600;
> +	else if ((priv->serial_flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
> +		tty->alt_speed = 115200;
> +	else if ((priv->serial_flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
> +		tty->alt_speed = 230400;
> +	else if ((priv->serial_flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
> +		tty->alt_speed = 460800;
> +	else
> +		tty->alt_speed = 0;
> +
> +	if (((old_flags & ASYNC_SPD_MASK) !=
> +	     (priv->serial_flags & ASYNC_SPD_MASK)) ||
> +	    (((priv->serial_flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) &&
> +	     ((old_divisor != priv->custom_divisor) ||
> +			old_base != priv->baud_base))) {
> +		mutex_unlock(&priv->cfg_lock);
> +		mos7840_change_port_settings(tty, priv, &tty->termios);
> +	}
> +
> +	mutex_unlock(&priv->cfg_lock);
> +	return 0;
> +}
> +
>  /*****************************************************************************
>   * SerialIoctl
>   *	this function handles any ioctl calls to the driver
> @@ -1997,7 +2084,7 @@ static int mos7840_ioctl(struct tty_struct *tty,
>  
>  	case TIOCSSERIAL:
>  		dev_dbg(&port->dev, "%s TIOCSSERIAL\n", __func__);
> -		break;
> +		return mos7840_set_serial_info(tty, mos7840_port, argp);
>  	default:
>  		break;
>  	}
> @@ -2136,6 +2223,10 @@ static int mos7840_port_probe(struct usb_serial_port *port)
>  	if (!mos7840_port)
>  		return -ENOMEM;
>  
> +	mutex_init(&mos7840_port->cfg_lock);
> +	mos7840_port->baud_base = MAX_BAUD_RATE;
> +	mos7840_port->custom_divisor = 1;
> +
>  	/* Initialize all port interrupt end point to port 0 int
>  	 * endpoint. Our device has only one interrupt end point
>  	 * common to all port */
> -- 
> 2.11.0
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux