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