From: Johan Hovold <johan@kernel.org>
To: Mathieu OTHACEHE <m.othacehe@gmail.com>
Cc: johan@kernel.org, gregkh@linuxfoundation.org,
linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org
Subject: Re: [PATCH v6] USB: serial: add Moxa UPORT 11x0 driver
Date: Tue, 29 Dec 2015 13:29:56 +0100 [thread overview]
Message-ID: <20151229122956.GB25531@localhost> (raw)
In-Reply-To: <1451334085-23464-1-git-send-email-m.othacehe@gmail.com>
On Mon, Dec 28, 2015 at 09:21:25PM +0100, Mathieu OTHACEHE wrote:
> Add a driver which supports :
>
> - UPort 1110 : 1 port RS-232 USB to Serial Hub.
> - UPort 1130 : 1 port RS-422/485 USB to Serial Hub.
> - UPort 1130I : 1 port RS-422/485 USB to Serial Hub with Isolation.
> - UPort 1150 : 1 port RS-232/422/485 USB to Serial Hub.
> - UPort 1150I : 1 port RS-232/422/485 USB to Serial Hub with Isolation.
>
> This driver is based on GPL MOXA driver written by Hen Huang and available
> on MOXA website. The original driver was based on io_ti serial driver.
>
> Signed-off-by: Mathieu OTHACEHE <m.othacehe@gmail.com>
> ---
>
> Hi Johan,
>
> Here is the v6 of the driver.
>
> I understand the problems with the TIOCSRS485/TIOCGRS485 ioctls in
> this driver.
> For now, I prefer dropping mode switching support from the driver as
> you suggested.
>
> So UPORT 1110 and 1150 only support RS232 and UPORT 1130 only support
> RS-485-2W.
> If a new interface is developped, I'll restore mode switching code.
Sounds good.
> About firmware images, I just sent a patch to linux-firmware. Here is the link :
> https://lkml.org/lkml/2015/12/28/239
Thanks, I've done some quick tests using a 1150 now. When looking
through the code one last time I found a few issues that I fixed up. I'll
submit patches for these to the USB list.
But there are couple of things you could consider to do as follow ups as
well. Details below.
> +static int mxu1_port_probe(struct usb_serial_port *port)
> +{
> + struct mxu1_port *mxport;
> + struct mxu1_device *mxdev;
> + struct urb *urb;
> +
> + mxport = kzalloc(sizeof(struct mxu1_port), GFP_KERNEL);
> + if (!mxport)
> + return -ENOMEM;
> +
> + spin_lock_init(&mxport->spinlock);
> + mutex_init(&mxport->mutex);
> +
> + mxdev = usb_get_serial_data(port->serial);
> +
> + urb = port->interrupt_in_urb;
> + if (!urb) {
You are leaking the port private data here (fixed).
> + dev_err(&port->dev, "%s - no interrupt urb\n", __func__);
> + return -EINVAL;
> + }
> +
> + switch (mxdev->mxd_model) {
> + case MXU1_1110_PRODUCT_ID:
> + case MXU1_1150_PRODUCT_ID:
> + case MXU1_1151_PRODUCT_ID:
> + mxport->uart_mode = MXU1_UART_232;
> + break;
> + case MXU1_1130_PRODUCT_ID:
> + case MXU1_1131_PRODUCT_ID:
> + mxport->uart_mode = MXU1_UART_485_RECEIVER_DISABLED;
> + break;
> + }
> +
> + usb_set_serial_port_data(port, mxport);
> +
> + port->port.closing_wait =
> + msecs_to_jiffies(MXU1_DEFAULT_CLOSING_WAIT * 10);
> + port->port.drain_delay = 1;
> +
> + return 0;
> +}
> +
> +static int mxu1_startup(struct usb_serial *serial)
> +{
> + struct mxu1_device *mxdev;
> + struct usb_device *dev = serial->dev;
> + struct usb_host_interface *cur_altsetting;
> + char fw_name[32];
> + const struct firmware *fw_p = NULL;
> + int err;
> + int status = 0;
> +
> + dev_dbg(&serial->interface->dev, "%s - product 0x%04X, num configurations %d, configuration value %d\n",
> + __func__, le16_to_cpu(dev->descriptor.idProduct),
> + dev->descriptor.bNumConfigurations,
> + dev->actconfig->desc.bConfigurationValue);
> +
> + /* create device structure */
> + mxdev = kzalloc(sizeof(struct mxu1_device), GFP_KERNEL);
> + if (!mxdev)
> + return -ENOMEM;
> +
> + usb_set_serial_data(serial, mxdev);
> +
> + mxdev->mxd_model = le16_to_cpu(dev->descriptor.idProduct);
> +
> + cur_altsetting = serial->interface->cur_altsetting;
> +
> + /* if we have only 1 configuration, download firmware */
> + if (cur_altsetting->desc.bNumEndpoints == 1) {
> +
> + snprintf(fw_name,
> + sizeof(fw_name),
> + "moxa/moxa-%04x.fw",
> + mxdev->mxd_model);
> +
> + err = request_firmware(&fw_p, fw_name, &serial->interface->dev);
> + if (err) {
> + dev_err(&serial->interface->dev, "failed to request firmware: %d\n",
> + err);
> + kfree(mxdev);
> + return err;
> + }
> +
> + err = mxu1_download_firmware(serial, fw_p);
> + if (err) {
> + release_firmware(fw_p);
> + kfree(mxdev);
> + return err;
> + }
> +
> + status = -ENODEV;
> + release_firmware(fw_p);
And you're leaking the interface private data here as well (also fixed).
What you could consider doing as a follow up it so move both the
interrupt-in urb test in port_probe and the firmware download to a new
probe callback. That way you avoid the unnecessary memory allocations
done by core before attach is called, and you can verify and refuse to
bind to a device in case the expected endpoints are missing.
> + }
> +
> + return status;
> +}
> + if (C_BAUD(tty) == B0)
> + mcr &= ~(MXU1_MCR_DTR | MXU1_MCR_RTS);
> + else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
> + mcr |= ~(MXU1_MCR_DTR | MXU1_MCR_RTS);
This should have been
mcr |= MXU1_MCR_DTR | MXU1_MCR_RTS;
Also fixed.
> +static int mxu1_open(struct tty_struct *tty, struct usb_serial_port *port)
> +{
> + struct mxu1_port *mxport = usb_get_serial_port_data(port);
> + struct usb_serial *serial = port->serial;
> + int status;
> + u16 open_settings;
> +
> + open_settings = (MXU1_PIPE_MODE_CONTINUOUS |
> + MXU1_PIPE_TIMEOUT_ENABLE |
> + (MXU1_TRANSFER_TIMEOUT << 2));
> +
> + mxport->msr = 0;
> +
> + dev_dbg(&port->dev, "%s - start interrupt in urb\n", __func__);
> + status = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
> + if (status) {
> + dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
> + status);
> + return status;
> + }
> +
> + if (tty)
> + mxu1_set_termios(tty, port, NULL);
> +
> + status = mxu1_send_ctrl_urb(serial, MXU1_OPEN_PORT,
> + open_settings, MXU1_UART1_PORT);
> + if (status) {
> + dev_err(&port->dev, "%s - cannot send open command: %d\n",
> + __func__, status);
> + goto unlink_int_urb;
> + }
> +
> + status = mxu1_send_ctrl_urb(serial, MXU1_START_PORT,
> + 0, MXU1_UART1_PORT);
> + if (status) {
> + dev_err(&port->dev, "%s - cannot send start command: %d\n",
> + __func__, status);
> + goto unlink_int_urb;
> + }
> +
> + status = mxu1_send_ctrl_urb(serial, MXU1_PURGE_PORT,
> + MXU1_PURGE_INPUT, MXU1_UART1_PORT);
> + if (status) {
> + dev_err(&port->dev, "%s - cannot clear input buffers: %d\n",
> + __func__, status);
> +
> + goto unlink_int_urb;
> + }
> +
> + status = mxu1_send_ctrl_urb(serial, MXU1_PURGE_PORT,
> + MXU1_PURGE_OUTPUT, MXU1_UART1_PORT);
> + if (status) {
> + dev_err(&port->dev, "%s - cannot clear output buffers: %d\n",
> + __func__, status);
> +
> + goto unlink_int_urb;
> + }
> +
> + /*
> + * reset the data toggle on the bulk endpoints to work around bug in
> + * host controllers where things get out of sync some times
> + */
> + usb_clear_halt(serial->dev, port->write_urb->pipe);
> + usb_clear_halt(serial->dev, port->read_urb->pipe);
> +
> + if (tty)
> + mxu1_set_termios(tty, port, NULL);
> +
> + status = mxu1_send_ctrl_urb(serial, MXU1_OPEN_PORT,
> + open_settings, MXU1_UART1_PORT);
> + if (status) {
> + dev_err(&port->dev, "%s - cannot send open command: %d\n",
> + __func__, status);
> + goto unlink_int_urb;
> + }
> +
> + status = mxu1_send_ctrl_urb(serial, MXU1_START_PORT,
> + 0, MXU1_UART1_PORT);
> + if (status) {
> + dev_err(&port->dev, "%s - cannot send start command: %d\n",
> + __func__, status);
> + goto unlink_int_urb;
> + }
I noticed that you send OPEN and START twice during probe -- is that
really necessary?
> + status = usb_serial_generic_open(tty, port);
> + if (status) {
> + dev_err(&port->dev, "%s - submit read urb failed: %d\n",
> + __func__, status);
> + goto unlink_int_urb;
> + }
> +
> + return status;
> +
> +unlink_int_urb:
> + usb_kill_urb(port->interrupt_in_urb);
> +
> + return status;
> +}
> +
> +static void mxu1_close(struct usb_serial_port *port)
> +{
> + int status;
> +
> + usb_serial_generic_close(port);
> + usb_kill_urb(port->interrupt_in_urb);
> +
> + status = mxu1_send_ctrl_urb(port->serial, MXU1_CLOSE_PORT,
> + 0, MXU1_UART1_PORT);
> + if (status)
> + dev_err(&port->dev, "failed to send close port command: %d\n",
> + status);
And should there not be a matching STOP request at close?
> +static struct usb_serial_driver mxuport11_device = {
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = "mxuport11",
I also decided to rename the usb-serial driver mxu11x0 to match the
module and USB driver name.
I have applied the patch now and will post my fixes and a couple of
clean ups to the list.
Thanks,
Johan
next prev parent reply other threads:[~2015-12-29 12:29 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-12-06 11:29 [PATCH v5] USB: serial: add Moxa UPORT 11x0 driver Mathieu OTHACEHE
2015-12-28 14:06 ` Johan Hovold
2015-12-28 14:39 ` One Thousand Gnomes
2015-12-28 15:32 ` Johan Hovold
2015-12-28 20:21 ` [PATCH v6] " Mathieu OTHACEHE
2015-12-29 12:29 ` Johan Hovold [this message]
2016-01-03 14:22 ` Mathieu OTHACEHE
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20151229122956.GB25531@localhost \
--to=johan@kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=m.othacehe@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.