From: Sean Young <sean@mess.org>
To: Oliver Neukum <oneukum@suse.com>
Cc: linux-media@vger.kernel.org, linux-usb@vger.kernel.org,
Greg KH <gregkh@linuxfoundation.org>
Subject: Re: [PATCH v2 1/3] media: rc: add support for Infrared Toy and IR Droid devices
Date: Wed, 27 May 2020 13:28:22 +0100 [thread overview]
Message-ID: <20200527122822.GA14488@gofer.mess.org> (raw)
In-Reply-To: <1590578201.2838.69.camel@suse.com>
Hi Oliver,
On Wed, May 27, 2020 at 01:16:41PM +0200, Oliver Neukum wrote:
> Am Mittwoch, den 27.05.2020, 10:41 +0100 schrieb Sean Young:
>
> Hi,
>
> thank you for the driver. Much cleaner than routing this through
> CDC_ACM.
Yes, it certainly is. It took some time for that insight to sink in (for me).
> I am afraid there are a few issues though. Nothing major.
> I've added remarks directly to the code. Would you care to
> fix them up?
Of course, thank you for reviewing it. The driver is much better for it.
I'll send out a v3 shortly, response and a question below.
Thanks,
Sean
>
> Regards
> Oliver
>
> > +static const u8 COMMAND_VERSION[] = { 'v' };
> > +// End transmit and repeat reset command so we exit sump mode
> > +static const u8 COMMAND_RESET[] = { 0xff, 0xff, 0, 0, 0, 0, 0 };
> > +static const u8 COMMAND_SMODE_ENTER[] = { 's' };
> > +static const u8 COMMAND_TXSTART[] = { 0x26, 0x24, 0x25, 0x03 };
>
> Using these directly as buffers is based on the assuption that the
> kernel code is accessable by DMA. On some architectures that is
> false. You need to use a bounce buffer.
This is true, however these arrays are not directly accessed, they're
memcpy'ed into irtoy->out.
>
> > +
> > +#define REPLY_XMITCOUNT 't'
> > +#define REPLY_XMITSUCCESS 'C'
> > +#define REPLY_VERSION 'V'
> > +#define REPLY_SAMPLEMODEPROTO 'S'
> > +
> > +#define TIMEOUT 500
> > +
> > +#define LEN_XMITRES 3
> > +#define LEN_VERSION 4
> > +#define LEN_SAMPLEMODEPROTO 3
> > +
> > +#define MIN_FW_VERSION 20
> > +#define UNIT_NS 21333
> > +#define MAX_TIMEOUT_NS (UNIT_NS * U16_MAX)
> > +
> > +#define MAX_PACKET 64
> > +
> > +enum state {
> > + STATE_IRDATA,
> > + STATE_RESET,
> > + STATE_COMMAND,
> > + STATE_TX,
> > +};
> > +
> > +struct irtoy {
> > + struct device *dev;
> > + struct usb_device *usbdev;
> > +
> > + struct rc_dev *rc;
> > + struct urb *urb_in, *urb_out;
> > +
> > + u8 in[MAX_PACKET];
> > + u8 out[MAX_PACKET];
>
> This violates the DMA coherency rules. The buffers must be
> allocated separately with kmalloc().
Right, I'll fix this and send out a v3. There are other usb drivers in
drivers/media/rc/.. that break this rule too.
> > + case STATE_IRDATA: {
> > + struct ir_raw_event rawir = { .pulse = irtoy->pulse };
> > + __be16 *in = (__be16 *)irtoy->in;
> > + int i;
> > +
> > + for (i = 0; i < len / sizeof(__be16); i++) {
> > + u32 v = be16_to_cpup(in + i);
>
> Is this 16 or 32 bit?
It's 16 bit but I would like it up-cast so that v * UNIT_NS is a 32 bit
multiply. This could do with a comment. Also could be be16_to_cpu(in[i]).
>
> > +
> > + if (v == 0xffff) {
> > + rawir.pulse = false;
> > + } else {
> > + rawir.duration = v * UNIT_NS;
> > + ir_raw_event_store_with_timeout(irtoy->rc,
> > + &rawir);
> > + }
> > +
> > + rawir.pulse = !rawir.pulse;
> > + }
> > +
> > + irtoy->pulse = rawir.pulse;
> > +
> > + ir_raw_event_handle(irtoy->rc);
> > + break;
> > + }
> > + case STATE_TX:
> > + if (irtoy->tx_len == 0) {
> > + if (len == LEN_XMITRES &&
> > + irtoy->in[0] == REPLY_XMITCOUNT) {
>
> Endianness?
Single byte.
> > + __be16 *emitted = (__be16 *)(irtoy->in + 1);
> > +
> > + irtoy->emitted = be16_to_cpup(emitted);
>
> Reason you are using cpup versions?
emitted is not aligned. So, I'm wrong and this should be get_unaligned_be16().
>
> > + } else if (len == 1 &&
> > + irtoy->in[0] == REPLY_XMITSUCCESS) {
> > + complete(&irtoy->rx_done);
> > + irtoy->state = STATE_IRDATA;
>
> Race condition. Whoever you wake up with that complete could read
> the old state.
irtoy->state is only read in this function, the urb callback handler. However,
as you noticed, this is looks wrong so it should be re-ordered.
> > + }
> > + } else {
> > + // send next part of tx buffer
> > + uint max_send = irtoy->in[0];
> > + uint buf_len = min(max_send, irtoy->tx_len);
> > + int err;
> > +
> > + dev_dbg(irtoy->dev, "ready to receive: 0x%02x\n",
> > + max_send);
> > +
> > + memcpy(irtoy->out, irtoy->tx_buf, buf_len);
> > + irtoy->urb_out->transfer_buffer_length = buf_len;
> > + err = usb_submit_urb(irtoy->urb_out, GFP_ATOMIC);
> > + if (err != 0) {
> > + dev_err(irtoy->dev, "fail to submit tx buf urb: %d\n",
> > + err);
> > + complete(&irtoy->rx_done);
> > + irtoy->state = STATE_IRDATA;
>
> Same race condition as above.
>
> > + }
> > +
> > + irtoy->tx_buf += buf_len;
> > + irtoy->tx_len -= buf_len;
> > + break;
> > + }
> > + break;
> > + case STATE_RESET:
> > + dev_err(irtoy->dev, "unexpected response to reset: %*phN\n",
> > + len, irtoy->in);
> > + }
> > +}
> > +
> > +static void irtoy_out_callback(struct urb *urb)
> > +{
> > + struct irtoy *irtoy = urb->context;
> > +
> > + switch (urb->status) {
> > + case 0:
> > + if (irtoy->state == STATE_RESET)
> > + complete(&irtoy->rx_done);
> > + break;
> > +
> > + case -ECONNRESET:
> > + case -ENOENT:
> > + case -ESHUTDOWN:
> > + case -EPROTO:
> > + usb_unlink_urb(urb);
>
> Redundant.
Removed, thanks.
> > + return;
> > +
> > + default:
> > + dev_warn(irtoy->dev, "out urb status: %d\n", urb->status);
> > + }
> > +}
> > +
> > +static void irtoy_in_callback(struct urb *urb)
> > +{
> > + struct irtoy *irtoy = urb->context;
> > + int ret;
> > +
> > + switch (urb->status) {
> > + case 0:
> > + irtoy_response(irtoy, urb->actual_length);
> > + break;
> > +
> > + case -ECONNRESET:
> > + case -ENOENT:
> > + case -ESHUTDOWN:
> > + case -EPROTO:
> > + usb_unlink_urb(urb);
>
> Redundant.
Removed, thanks.
> > + return;
> > +
> > + default:
> > + dev_warn(irtoy->dev, "in urb status: %d\n", urb->status);
> > + }
> > +
> > + ret = usb_submit_urb(urb, GFP_ATOMIC);
> > + if (ret && ret != -ENODEV)
> > + dev_warn(irtoy->dev, "failed to resubmit urb: %d\n", ret);
> > +}
> > +
> > +static int irtoy_command(struct irtoy *irtoy, const u8 *cmd, int cmd_len)
> > +{
> > + int err;
> > +
> > + init_completion(&irtoy->rx_done);
> > +
> > + memcpy(irtoy->out, cmd, cmd_len);
> > + irtoy->urb_out->transfer_buffer_length = cmd_len;
> > +
> > + err = usb_submit_urb(irtoy->urb_out, GFP_KERNEL);
> > + if (err != 0)
> > + return err;
> > +
> > + if (!wait_for_completion_timeout(&irtoy->rx_done,
> > + msecs_to_jiffies(TIMEOUT)))
> > + return -ETIMEDOUT;
>
> Wrong error handling. The URB is still active. You cannot free the
> buffer. The caller has no idea when that is safe to do so. You must
> kill the URB in the timeout case.
Good point, fixed.
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * When sending IR, it is imperative that we send the IR data as quickly
> > + * as possible to the device, so it does not run out of IR data and
> > + * introduce gaps. So, we feed the data from the urb callback handler
> > + */
> > +static int irtoy_tx(struct rc_dev *rc, uint *txbuf, uint count)
> > +{
> > + struct irtoy *irtoy = rc->priv;
> > + unsigned int i, size;
> > + __be16 *buf;
> > + int err;
> > +
> > + size = sizeof(u16) * (count + 1);
> > + buf = kmalloc(size, GFP_KERNEL);
>
> This is incompatible with the comment. If you are potentially in
> interrupt, you must use GFP_ATOMIC. Please clarify.
This function is called in process context, from a write() on a lirc chardev.
However, this buffer is stored in irtoy->tx_buf. It is then sent, in packets,
to the device (memcpy'ed to irtoy->out). This handled in irtoy_response() in
a response to the device sending a "you may send X bytes irdata now" message.
I'll update the comment.
> > +static int irtoy_suspend(struct usb_interface *intf, pm_message_t message)
> > +{
> > + struct irtoy *irtoy = usb_get_intfdata(intf);
> > +
> > + usb_kill_urb(irtoy->urb_in);
> > + usb_kill_urb(irtoy->urb_out);
>
> That is brutal. It could fail commands. Do you really want to
> do that?
Commands can only be sent during 1) device probe and 2) ir transmit. During
ir transmit we are non-interruptable process context, so we should not end up
here unless I'm mistaken.
When we're not issuing commands we're waiting for ir receive; should that
urb be killed for the duration of suspend?
>
> > +
> > + return 0;
> > +}
> > +
> > +static int irtoy_resume(struct usb_interface *intf)
> > +{
> > + struct irtoy *irtoy = usb_get_intfdata(intf);
> > + int err;
> > +
> > + err = usb_submit_urb(irtoy->urb_in, GFP_KERNEL);
>
> That should technically be GFP_NOIO
Fixed.
>
> > + if (err)
> > + dev_warn(&intf->dev, "failed to submit urb: %d\n", err);
> > +
> > + return err;
> > +}
next prev parent reply other threads:[~2020-05-27 12:28 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-05-27 9:41 [PATCH v2 0/3] IR Toy / IR Droid USB driver Sean Young
2020-05-27 9:41 ` [PATCH v2 1/3] media: rc: add support for Infrared Toy and IR Droid devices Sean Young
2020-05-27 10:17 ` Greg KH
2020-05-27 11:16 ` Oliver Neukum
2020-05-27 12:28 ` Sean Young [this message]
2020-05-27 13:00 ` Oliver Neukum
2020-05-27 15:06 ` Sean Young
2020-05-27 9:41 ` [PATCH v2 2/3] USB: cdc-acm: blacklist IR Droid / IR Toy device Sean Young
2020-05-27 10:16 ` Greg KH
2020-05-27 9:41 ` [PATCH v2 3/3] MAINTAINERS: rc core and lirc maintainership Sean Young
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=20200527122822.GA14488@gofer.mess.org \
--to=sean@mess.org \
--cc=gregkh@linuxfoundation.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=oneukum@suse.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.