+static const struct reg_value f81534_pin_control[4][3] = {
+ /* M0_SD M1 M2 */
+ {{0x2ae8, 7}, {0x2a90, 5}, {0x2a90, 4}, }, /* port 0 pins */
+ {{0x2ae8, 6}, {0x2ae8, 0}, {0x2ae8, 3}, }, /* port 1 pins */
+ {{0x2a90, 0}, {0x2ae8, 2}, {0x2a80, 6}, }, /* port 2 pins */
+ {{0x2a90, 3}, {0x2a90, 2}, {0x2a90, 1}, }, /* port 3 pins */
+};
I thought we agreed to drop the transceiver configuration from the
driver in favour of a user-space tool?
+{
+ size_t count = F81534_USB_MAX_RETRY;
+ int status;
+ u8 *tmp;
+
+ tmp = kmalloc(sizeof(u8), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ *tmp = data;
+
+ /*
+ * Our device maybe not reply when heavily loading, We'll retry for
+ * F81534_USB_MAX_RETRY times.
+ */
+ while (count--) {
+ status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ F81534_SET_GET_REGISTER,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ reg, 0, tmp, sizeof(u8),
+ F81534_USB_TIMEOUT);
+ if (status > 0)
+ break;
+
+ if (status == 0)
+ status = -EIO;
+ }
+
+ if (status < 0) {
+ dev_err(&dev->dev, "%s: reg: %x data: %x failed: %d\n",
+ __func__, reg, data, status);
+ kfree(tmp);
+ return status;
I'd use a common exit path to free tmp, and just print an error here.
+static int f81534_command_delay(struct usb_serial *usbserial)
Please explain why and when you need to use this "delay" function, and
how the BUS_REG_STATUS register works.
Please use "serial" consistently throughout for usb_serial pointers
(instead of "usbserial").
+static int f81534_calc_num_ports(struct usb_serial *serial)
+{
+ unsigned char setting[F81534_CUSTOM_DATA_SIZE];
+ uintptr_t setting_idx;
+ u8 num_port = 0;
+ int status;
+ size_t i;
+
+ /* Check had custom setting */
+ status = f81534_find_config_idx(serial, &setting_idx);
+ if (status) {
+ dev_err(&serial->dev->dev, "%s: find idx failed: %d\n",
+ __func__, status);
+ return 0;
+ }
+
+ /* Save the configuration area idx as private data for attach() */
+ usb_set_serial_data(serial, (void *)setting_idx);
+
+ /* Read default board setting */
+ status = f81534_read_data(serial, F81534_DEF_CONF_ADDRESS_START,
+ F81534_NUM_PORT, setting);
+ if (status) {
+ dev_err(&serial->dev->dev, "%s: read failed: %d\n", __func__,
+ status);
+ return 0;
+ }
+
+ /*
+ * If had custom setting, override it. 1st byte is a indicator, 0xff
+ * is empty, F81534_CUSTOM_VALID_TOKEN is had data, then skip with 1st
+ * data
+ */
+ if (setting_idx != F81534_CUSTOM_NO_CUSTOM_DATA) {
+ status = f81534_read_data(serial, F81534_CUSTOM_ADDRESS_START +
+ F81534_CONF_OFFSET,
+ sizeof(setting), setting);
+ if (status) {
+ dev_err(&serial->dev->dev,
+ "%s: get custom data failed: %d\n",
+ __func__, status);
+ return 0;
+ }
+
+ dev_dbg(&serial->dev->dev,
+ "%s: read configure from block: %d\n",
+ __func__, (unsigned int)setting_idx);
+ } else {
+ dev_dbg(&serial->dev->dev, "%s: read configure default\n",
+ __func__);
+ }
+
+ /* New style, find all possible ports */
+ num_port = 0;
+ for (i = 0; i < F81534_NUM_PORT; ++i) {
+ if (setting[i] & F81534_PORT_UNAVAILABLE)
+ continue;
Looks like setting[] could be uninitialised here.
+ size_t i, read_size = 0;
+ unsigned long flags;
+ bool available;
+ char tty_flag;
+ int status;
+ u8 lsr;
+
+ available = !!atomic_read(&priv->port_active[phy_port_num]);
+
+ /*
+ * The block layout is 128 Bytes
+ * index 0: port phy idx (e.g., 0,1,2,3),
+ * index 1: It's could be
+ * F81534_TOKEN_RECEIVE
+ * F81534_TOKEN_TX_EMPTY
+ * F81534_TOKEN_MSR_CHANGE
+ * index 2: serial in size (data+lsr, must be even)
+ * meaningful for F81534_TOKEN_RECEIVE only
+ * index 3: current MSR with this device
+ * index 4~127: serial in data block (data+lsr, must be even)
+ */
+ switch (data[1]) {
+ case F81534_TOKEN_TX_EMPTY:
+ /*
+ * We should save TX_EMPTY flag even the port is not opened
+ */
+ spin_lock_irqsave(&priv->tx_empty_lock, flags);
+ priv->is_phy_port_not_empty[phy_port_num] = false;
+ spin_unlock_irqrestore(&priv->tx_empty_lock, flags);
Why not just keep a flag in the port private data?
Also could this end up racing with f81534_submit_writer() which could
have just set this flag?
+ tty_port_num = f81534_phy_to_logic_port(serial, phy_port_num);
+ port = serial->port[tty_port_num];
+
+ /*
+ * The device will send back all information when we submitted
+ * a read URB (MSR/DATA/TX_EMPTY). But it maybe get callback
+ * before port_probe() or after port_remove().
+ *
+ * So we'll verify the pointer. If the pointer is NULL, it's
+ * mean the port not init complete and the block will skip.
+ */
+ port_priv = usb_get_serial_port_data(port);
Check if the port has been opened here instead, no need to store MSR for
an unused port above.
+ if (!port_priv) {
+ dev_warn(&serial->dev->dev,
+ "%s: phy: %d not ready\n", __func__,
+ phy_port_num);
+ continue;
+ }
+
+ f81534_process_per_serial_block(port, &ch[i]);
Missing sanity check on size of the received data, which you access
unconditionally in the helper function.
+ if (status) {
+ dev_err(&serial->dev->dev,
+ "%s: idx: %d get data failed: %d\n", __func__,
+ serial_priv->setting_idx, status);
+ return status;
+ }
+
+ /*
+ * We'll register port 0 bulkin only once, It'll take all port received
+ * data, MSR register change and TX_EMPTY information.
+ */
+ status = f81534_submit_read_urb(serial, GFP_KERNEL);
+ if (status)
+ return status;
+
+ return 0;
+}
You need to stop the read urbs you submitted in attach in a matching
release() callback.
But as I've mentioned before, you should consider keeping an open-port
count and submit the reads urbs when the first port is opened and stop
them when the last port is closed instead.
+static int f81534_tiocmget(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ int r;
+ u8 msr, mcr;
+
+ /*
+ * We'll avoid to direct read MSR register without open(). The IC will
+ * read the MSR changed and report it f81534_process_per_serial_block()
+ * by F81534_TOKEN_MSR_CHANGE.
Why not read it directly from the chip if you can? This will never be
called for a closed port.