Hi Ajay, Thanks for the review. On 21 June 2018 at 19:34, Ajay Gupta <ajaykuee@xxxxxxxxx> wrote: > Hi Loic > > On 6/21/18, Loic Poulain <loic.poulain@xxxxxxxxxx> 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.poulain@xxxxxxxxxx> >> --- >> 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) { > } > This is in theory something which is guaranteed by the nvmem core since we specified a 2-byte word size on registering. But looking at the code, word-size is only checked on userspace access, not with kernel API (e.g. nvmem_device_write). So yes It could be worth to double check here. >> + 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 Ok. > >> + 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. > Ok. > 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 majordomo@xxxxxxxxxxxxxxx >> More majordomo info at http://vger.kernel.org/majordomo-info.html >> Regards, Loic -- 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