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 <[email protected]>
> ---
>  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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to