Hi Loic

On 6/21/18, Loic Poulain <loic.poul...@linaro.org> wrote:
> Most of FTDI's devices have an EEPROM which records FTDI devices
> configuration setting (e.g. the VID, PID, I/O config...) and user
> data. FT230R chip integrates a 128-byte eeprom, FT230X a 2048-byte
> eeprom...
>
> This patch adds support for FTDI EEPROM read/write via USB control
> transfers and register a new nvm device to the nvmem core.
>
> This permits to expose the eeprom as a sysfs file, allowing userspace
> to read/modify FTDI configuration and its user data without having to
> rely on a specific userspace USB driver.
>
> Moreover, any upcoming new tentative to add CBUS GPIO support could
> integrate CBUS EEPROM configuration reading in order to determine
> which of the CBUS pins are available as GPIO.
>
> Signed-off-by: Loic Poulain <loic.poul...@linaro.org>
> ---
>  v2: Use ifdef instead of IS_ENABLED
>      error message in case of nvmem registering failure
>      Fix space/tab in Kconfig
>
>  drivers/usb/serial/Kconfig    |  13 ++++-
>  drivers/usb/serial/ftdi_sio.c | 111
> ++++++++++++++++++++++++++++++++++++++++++
>  drivers/usb/serial/ftdi_sio.h |  28 +++++++++++
>  3 files changed, 151 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
> index 533f127..f05af5f 100644
> --- a/drivers/usb/serial/Kconfig
> +++ b/drivers/usb/serial/Kconfig
> @@ -153,7 +153,7 @@ config USB_SERIAL_CYPRESS_M8
>
>         Supported microcontrollers in the CY4601 family are:
>               CY7C63741 CY7C63742 CY7C63743 CY7C64013
> -     
> +
There is no change here so please remove it.

>         To compile this driver as a module, choose M here: the
>         module will be called cypress_m8.
>
> @@ -181,6 +181,17 @@ config USB_SERIAL_FTDI_SIO
>         To compile this driver as a module, choose M here: the
>         module will be called ftdi_sio.
>
> +config USB_SERIAL_FTDI_SIO_NVMEM
> +     bool "FTDI MTP non-volatile memory support"
> +     depends on USB_SERIAL_FTDI_SIO
> +     select NVMEM
> +     default y
> +     help
> +       Say yes here to add support for the MTP non-volatile memory
> +       present on FTDI. Most of FTDI's devices have an EEPROM which
> +       records FTDI device's configuration setting as well as user
> +       data.
> +
>  config USB_SERIAL_VISOR
>       tristate "USB Handspring Visor / Palm m50x / Sony Clie Driver"
>       help
> diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
> index 7ea221d..9e242e8 100644
> --- a/drivers/usb/serial/ftdi_sio.c
> +++ b/drivers/usb/serial/ftdi_sio.c
> @@ -40,6 +40,7 @@
>  #include <linux/usb.h>
>  #include <linux/serial.h>
>  #include <linux/usb/serial.h>
> +#include <linux/nvmem-provider.h>
>  #include "ftdi_sio.h"
>  #include "ftdi_sio_ids.h"
>
> @@ -73,6 +74,8 @@ struct ftdi_private {
>       unsigned int latency;           /* latency setting in use */
>       unsigned short max_packet_size;
>       struct mutex cfg_lock; /* Avoid mess by parallel calls of config ioctl()
> and change_speed() */
> +
> +     struct nvmem_device *eeprom;
>  };
>
>  /* struct ftdi_sio_quirk is used by devices requiring special attention.
> */
> @@ -1529,6 +1532,104 @@ static int get_lsr_info(struct usb_serial_port
> *port,
>       return 0;
>  }
>
> +#ifdef CONFIG_USB_SERIAL_FTDI_SIO_NVMEM
> +
> +static int write_eeprom(void *priv, unsigned int off, void *_val, size_t
> bytes)
> +{
> +     struct usb_serial_port *port = priv;
> +     struct usb_serial *serial = port->serial;
> +     struct usb_device *udev = serial->dev;
> +     unsigned char *buf = _val;
> +
> +     while (bytes) { /* bytes value is always a multiple of 2 */

We should add check that 'bytes' is always multiple of 2 otherwise in
case its not then there will be memory overrun due to buf[1] access
below.
while (bytes / 2) {
}

> +             uint16_t val;
> +             int rv;
> +
> +             val = buf[0] + (buf[1] << 8);
> +
> +             rv = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
> +                                  FTDI_SIO_WRITE_EEPROM_REQUEST,
> +                                  FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE,
> +                                  val, off / 2, NULL, 0, WDR_TIMEOUT);
> +             if (rv < 0)
> +                     return rv;
> +
> +             off += 2;
> +             buf += 2;
> +             bytes -= 2;
> +     }
> +
> +     return 0;
> +}
> +
> +static int read_eeprom(void *priv, unsigned int off, void *val, size_t
> bytes)
> +{
> +     struct usb_serial_port *port = priv;
> +     struct usb_serial *serial = port->serial;
> +     struct usb_device *udev = serial->dev;
> +     unsigned char *buf = val;
> +
> +     while (bytes) { /* bytes value is always a multiple of 2 */
same here

> +             int rv;
> +
> +             rv = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
> +                                  FTDI_SIO_READ_EEPROM_REQUEST,
> +                                  FTDI_SIO_READ_EEPROM_REQUEST_TYPE,
> +                                  0, off / 2, buf, 2, WDR_TIMEOUT);
> +             if (rv < 0)
> +                     return rv;
> +
> +             off += 2;
> +             buf += 2;
> +             bytes -= 2;
> +     }
> +
> +     return 0;
> +}
> +
> +static int register_eeprom(struct usb_serial_port *port)
> +{
> +     struct ftdi_private *priv = usb_get_serial_port_data(port);
> +     struct nvmem_config nvmconf = {};
> +
> +     switch (priv->chip_type) {
> +     case FTX:
> +             nvmconf.size = 2048;
Better to have macro for 2048 and 128 instead of hardcoded value.

Thanks
Ajay
> +             break;
> +     case FT232RL:
> +             nvmconf.size = 128;
> +             break;
> +     default:
> +             return 0;
> +     }
> +
> +     nvmconf.word_size = 2;
> +     nvmconf.stride = 2;
> +     nvmconf.read_only = false;
> +     nvmconf.priv = port;
> +     nvmconf.dev = &port->dev;
> +     nvmconf.reg_read = read_eeprom;
> +     nvmconf.reg_write = write_eeprom;
> +     nvmconf.owner = THIS_MODULE;
> +
> +     priv->eeprom = nvmem_register(&nvmconf);
> +     if (IS_ERR(priv->eeprom)) {
> +             priv->eeprom = NULL;
> +             return -ENOMEM;
> +     }
> +
> +     return 0;
> +}
> +
> +static void unregister_eeprom(struct usb_serial_port *port)
> +{
> +     struct ftdi_private *priv = usb_get_serial_port_data(port);
> +
> +     if (priv->eeprom)
> +             nvmem_unregister(priv->eeprom);
> +}
> +
> +#endif /* CONFIG_USB_SERIAL_FTDI_SIO_NVMEM */
>
>  /* Determine type of FTDI chip based on USB config and descriptor. */
>  static void ftdi_determine_type(struct usb_serial_port *port)
> @@ -1814,6 +1915,13 @@ static int ftdi_sio_port_probe(struct usb_serial_port
> *port)
>               priv->latency = 16;
>       write_latency_timer(port);
>       create_sysfs_attrs(port);
> +#ifdef CONFIG_USB_SERIAL_FTDI_SIO_NVMEM
> +     if (register_eeprom(port)) {
> +             dev_err(&port->dev, "Unable to register FTDI EEPROM\n");
> +             /* non-fatal */
> +     }
> +#endif
> +
>       return 0;
>  }
>
> @@ -1931,6 +2039,9 @@ static int ftdi_sio_port_remove(struct usb_serial_port
> *port)
>  {
>       struct ftdi_private *priv = usb_get_serial_port_data(port);
>
> +#ifdef CONFIG_USB_SERIAL_FTDI_SIO_NVMEM
> +     unregister_eeprom(port);
> +#endif
>       remove_sysfs_attrs(port);
>
>       kfree(priv);
> diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
> index dcd0b6e..9eab961 100644
> --- a/drivers/usb/serial/ftdi_sio.h
> +++ b/drivers/usb/serial/ftdi_sio.h
> @@ -36,6 +36,8 @@
>  #define FTDI_SIO_SET_ERROR_CHAR              7 /* Set the error character */
>  #define FTDI_SIO_SET_LATENCY_TIMER   9 /* Set the latency timer */
>  #define FTDI_SIO_GET_LATENCY_TIMER   10 /* Get the latency timer */
> +#define FTDI_SIO_READ_EEPROM         0x90 /* Read eeprom */
> +#define FTDI_SIO_WRITE_EEPROM                0x91 /* Write eeprom */
>
>  /* Interface indices for FT2232, FT2232H and FT4232H devices */
>  #define INTERFACE_A          1
> @@ -400,6 +402,32 @@ enum ftdi_sio_baudrate {
>   *
>   */
>
> + /* FTDI_SIO_READ_EEPROM */
> +#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0
> +#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM
> +/*
> + *  BmRequestType:   1100 0000b
> + *  bRequest:        FTDI_SIO_READ_EEPROM
> + *  wValue:          0
> + *  wIndex:          Word Index
> + *  wLength:         2
> + *  Data:            return data (a word)
> + *
> + */
> +
> +/* FTDI_SIO_WRITE_EEPROM */
> +#define FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE 0x40
> +#define FTDI_SIO_WRITE_EEPROM_REQUEST FTDI_SIO_WRITE_EEPROM
> +/*
> + *  BmRequestType:   0100 0000b
> + *  bRequest:        FTDI_SIO_WRITE_EEPROM
> + *  wValue:          Data (word)
> + *  wIndex:          Word Index
> + *  wLength:         0
> + *  Data:            None
> + *
> + */
> +
>  /* FTDI_SIO_GET_MODEM_STATUS */
>  /* Retrieve the current value of the modem status register */
>
> --
> 2.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to