[v4,1/2] media: rc: new driver for USB-UIRT device
Commit Message
This device uses an ftdi usb serial port, so this driver has a tiny
amount of usb ftdi code. It would be preferable to connect this driver via
serdev or line-discipline, but unfortunately neither support
hotplugging yet.
See http://www.usbuirt.com/
Signed-off-by: Sean Young <sean@mess.org>
---
drivers/media/rc/Kconfig | 11 +
drivers/media/rc/Makefile | 1 +
drivers/media/rc/uirt.c | 740 ++++++++++++++++++++++++++++++++++++++
3 files changed, 752 insertions(+)
create mode 100644 drivers/media/rc/uirt.c
Comments
Hi Sean,
I love your patch! Perhaps something to improve:
[auto build test WARNING on linuxtv-media/master]
[also build test WARNING on usb-serial/usb-next usb/usb-testing peter.chen-usb/for-usb-next v5.13-rc6 next-20210617]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Sean-Young/IR-driver-for-USB-UIRT-device/20210616-182135
base: git://linuxtv.org/media_tree.git master
config: powerpc64-randconfig-r012-20210617 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 64720f57bea6a6bf033feef4a5751ab9c0c3b401)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install powerpc64 cross compiling tool for clang build
# apt-get install binutils-powerpc64-linux-gnu
# https://github.com/0day-ci/linux/commit/17d3a0332baecb0359e05e8ae755478c7a1a4468
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Sean-Young/IR-driver-for-USB-UIRT-device/20210616-182135
git checkout 17d3a0332baecb0359e05e8ae755478c7a1a4468
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=powerpc64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All warnings (new ones prefixed by >>):
In file included from drivers/media/rc/uirt.c:11:
In file included from include/linux/completion.h:12:
In file included from include/linux/swait.h:5:
In file included from include/linux/list.h:9:
In file included from include/linux/kernel.h:12:
In file included from include/linux/bitops.h:32:
In file included from arch/powerpc/include/asm/bitops.h:62:
arch/powerpc/include/asm/barrier.h:49:9: warning: '__lwsync' macro redefined [-Wmacro-redefined]
#define __lwsync() __asm__ __volatile__ (stringify_in_c(LWSYNC) : : :"memory")
^
<built-in>:310:9: note: previous definition is here
#define __lwsync __builtin_ppc_lwsync
^
>> drivers/media/rc/uirt.c:639:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
if (!urb)
^~~~
drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
return err;
^~~
drivers/media/rc/uirt.c:639:2: note: remove the 'if' if its condition is always false
if (!urb)
^~~~~~~~~
drivers/media/rc/uirt.c:630:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
if (!urb)
^~~~
drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
return err;
^~~
drivers/media/rc/uirt.c:630:2: note: remove the 'if' if its condition is always false
if (!urb)
^~~~~~~~~
drivers/media/rc/uirt.c:626:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
if (!rc)
^~~
drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
return err;
^~~
drivers/media/rc/uirt.c:626:2: note: remove the 'if' if its condition is always false
if (!rc)
^~~~~~~~
drivers/media/rc/uirt.c:622:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
if (!uirt->out)
^~~~~~~~~~
drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
return err;
^~~
drivers/media/rc/uirt.c:622:2: note: remove the 'if' if its condition is always false
if (!uirt->out)
^~~~~~~~~~~~~~~
drivers/media/rc/uirt.c:618:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
if (!uirt->in)
^~~~~~~~~
drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
return err;
^~~
drivers/media/rc/uirt.c:618:2: note: remove the 'if' if its condition is always false
if (!uirt->in)
^~~~~~~~~~~~~~
drivers/media/rc/uirt.c:604:15: note: initialize the variable 'err' to silence this warning
int pipe, err;
^
= 0
6 warnings generated.
vim +639 drivers/media/rc/uirt.c
594
595 static int uirt_probe(struct usb_interface *intf,
596 const struct usb_device_id *id)
597 {
598 struct usb_device *usbdev = interface_to_usbdev(intf);
599 struct usb_endpoint_descriptor *ep_in;
600 struct usb_endpoint_descriptor *ep_out;
601 struct uirt *uirt;
602 struct rc_dev *rc;
603 struct urb *urb;
604 int pipe, err;
605
606 if (usb_find_common_endpoints(intf->cur_altsetting, &ep_in, &ep_out, NULL, NULL) ||
607 usb_endpoint_maxp(ep_in) != MAX_PACKET ||
608 usb_endpoint_maxp(ep_out) != MAX_PACKET) {
609 dev_err(&intf->dev, "required endpoints not found\n");
610 return -ENODEV;
611 }
612
613 uirt = kzalloc(sizeof(*uirt), GFP_KERNEL);
614 if (!uirt)
615 return -ENOMEM;
616
617 uirt->in = kmalloc(MAX_PACKET, GFP_KERNEL);
618 if (!uirt->in)
619 goto free_uirt;
620
621 uirt->out = kmalloc(MAX_PACKET, GFP_KERNEL);
622 if (!uirt->out)
623 goto free_uirt;
624
625 rc = rc_allocate_device(RC_DRIVER_IR_RAW);
626 if (!rc)
627 goto free_uirt;
628
629 urb = usb_alloc_urb(0, GFP_KERNEL);
630 if (!urb)
631 goto free_rcdev;
632
633 pipe = usb_rcvbulkpipe(usbdev, ep_in->bEndpointAddress);
634 usb_fill_bulk_urb(urb, usbdev, pipe, uirt->in, MAX_PACKET,
635 uirt_in_callback, uirt);
636 uirt->urb_in = urb;
637
638 urb = usb_alloc_urb(0, GFP_KERNEL);
> 639 if (!urb)
640 goto free_rcdev;
641
642 pipe = usb_sndbulkpipe(usbdev, ep_out->bEndpointAddress);
643 usb_fill_bulk_urb(urb, usbdev, pipe, uirt->out, MAX_PACKET,
644 uirt_out_callback, uirt);
645
646 uirt->dev = &intf->dev;
647 uirt->usbdev = usbdev;
648 uirt->rc = rc;
649 uirt->urb_out = urb;
650 uirt->rx_state = RX_STATE_INTERSPACE_HIGH;
651
652 err = usb_submit_urb(uirt->urb_in, GFP_KERNEL);
653 if (err != 0) {
654 dev_err(uirt->dev, "failed to submit read urb: %d\n", err);
655 goto free_rcdev;
656 }
657
658 err = init_ftdi(usbdev);
659 if (err) {
660 dev_err(uirt->dev, "failed to setup ftdi: %d\n", err);
661 goto kill_urbs;
662 }
663
664 err = uirt_setup(uirt);
665 if (err)
666 goto kill_urbs;
667
668 usb_make_path(usbdev, uirt->phys, sizeof(uirt->phys));
669
670 rc->device_name = "USB-UIRT";
671 rc->driver_name = KBUILD_MODNAME;
672 rc->input_phys = uirt->phys;
673 usb_to_input_id(usbdev, &rc->input_id);
674 rc->dev.parent = &intf->dev;
675 rc->priv = uirt;
676 rc->tx_ir = uirt_tx;
677 rc->s_tx_carrier = uirt_set_tx_carrier;
678 rc->s_learning_mode = uirt_set_rx_wideband;
679 rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
680 rc->map_name = RC_MAP_RC6_MCE;
681 rc->rx_resolution = UNIT_US;
682 rc->timeout = IR_TIMEOUT;
683
684 uirt_set_tx_carrier(rc, 38000);
685
686 err = rc_register_device(rc);
687 if (err)
688 goto kill_urbs;
689
690 usb_set_intfdata(intf, uirt);
691
692 return 0;
693
694 kill_urbs:
695 usb_kill_urb(uirt->urb_in);
696 usb_kill_urb(uirt->urb_out);
697 free_rcdev:
698 usb_free_urb(uirt->urb_in);
699 usb_free_urb(uirt->urb_out);
700 rc_free_device(rc);
701 free_uirt:
702 kfree(uirt->in);
703 kfree(uirt->out);
704 kfree(uirt);
705 return err;
706 }
707
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
On Fri, Jun 18, 2021 at 06:18:06AM +0800, kernel test robot wrote:
> Hi Sean,
>
> I love your patch! Perhaps something to improve:
>
> [auto build test WARNING on linuxtv-media/master]
> [also build test WARNING on usb-serial/usb-next usb/usb-testing peter.chen-usb/for-usb-next v5.13-rc6 next-20210617]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch]
>
> url: https://github.com/0day-ci/linux/commits/Sean-Young/IR-driver-for-USB-UIRT-device/20210616-182135
> base: git://linuxtv.org/media_tree.git master
> config: powerpc64-randconfig-r012-20210617 (attached as .config)
> compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 64720f57bea6a6bf033feef4a5751ab9c0c3b401)
> reproduce (this is a W=1 build):
> wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
> chmod +x ~/bin/make.cross
> # install powerpc64 cross compiling tool for clang build
> # apt-get install binutils-powerpc64-linux-gnu
> # https://github.com/0day-ci/linux/commit/17d3a0332baecb0359e05e8ae755478c7a1a4468
> git remote add linux-review https://github.com/0day-ci/linux
> git fetch --no-tags linux-review Sean-Young/IR-driver-for-USB-UIRT-device/20210616-182135
> git checkout 17d3a0332baecb0359e05e8ae755478c7a1a4468
> # save the attached .config to linux build tree
> COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=powerpc64
>
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <lkp@intel.com>
>
> All warnings (new ones prefixed by >>):
>
> In file included from drivers/media/rc/uirt.c:11:
> In file included from include/linux/completion.h:12:
> In file included from include/linux/swait.h:5:
> In file included from include/linux/list.h:9:
> In file included from include/linux/kernel.h:12:
> In file included from include/linux/bitops.h:32:
> In file included from arch/powerpc/include/asm/bitops.h:62:
> arch/powerpc/include/asm/barrier.h:49:9: warning: '__lwsync' macro redefined [-Wmacro-redefined]
> #define __lwsync() __asm__ __volatile__ (stringify_in_c(LWSYNC) : : :"memory")
> ^
> <built-in>:310:9: note: previous definition is here
> #define __lwsync __builtin_ppc_lwsync
> ^
> >> drivers/media/rc/uirt.c:639:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
> if (!urb)
> ^~~~
> drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
> return err;
This is interesting. clang is right here, there are error paths where err is
not initialized. gcc-11.1 does not pick this up for some reason. The error path
should be an immediate dominator so it shouldn't be complicated to detect.
I'll send out a v5 with this issue fixed.
Sean
> ^~~
> drivers/media/rc/uirt.c:639:2: note: remove the 'if' if its condition is always false
> if (!urb)
> ^~~~~~~~~
> drivers/media/rc/uirt.c:630:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
> if (!urb)
> ^~~~
> drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
> return err;
> ^~~
> drivers/media/rc/uirt.c:630:2: note: remove the 'if' if its condition is always false
> if (!urb)
> ^~~~~~~~~
> drivers/media/rc/uirt.c:626:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
> if (!rc)
> ^~~
> drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
> return err;
> ^~~
> drivers/media/rc/uirt.c:626:2: note: remove the 'if' if its condition is always false
> if (!rc)
> ^~~~~~~~
> drivers/media/rc/uirt.c:622:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
> if (!uirt->out)
> ^~~~~~~~~~
> drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
> return err;
> ^~~
> drivers/media/rc/uirt.c:622:2: note: remove the 'if' if its condition is always false
> if (!uirt->out)
> ^~~~~~~~~~~~~~~
> drivers/media/rc/uirt.c:618:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
> if (!uirt->in)
> ^~~~~~~~~
> drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
> return err;
> ^~~
> drivers/media/rc/uirt.c:618:2: note: remove the 'if' if its condition is always false
> if (!uirt->in)
> ^~~~~~~~~~~~~~
> drivers/media/rc/uirt.c:604:15: note: initialize the variable 'err' to silence this warning
> int pipe, err;
> ^
> = 0
> 6 warnings generated.
>
>
> vim +639 drivers/media/rc/uirt.c
>
> 594
> 595 static int uirt_probe(struct usb_interface *intf,
> 596 const struct usb_device_id *id)
> 597 {
> 598 struct usb_device *usbdev = interface_to_usbdev(intf);
> 599 struct usb_endpoint_descriptor *ep_in;
> 600 struct usb_endpoint_descriptor *ep_out;
> 601 struct uirt *uirt;
> 602 struct rc_dev *rc;
> 603 struct urb *urb;
> 604 int pipe, err;
> 605
> 606 if (usb_find_common_endpoints(intf->cur_altsetting, &ep_in, &ep_out, NULL, NULL) ||
> 607 usb_endpoint_maxp(ep_in) != MAX_PACKET ||
> 608 usb_endpoint_maxp(ep_out) != MAX_PACKET) {
> 609 dev_err(&intf->dev, "required endpoints not found\n");
> 610 return -ENODEV;
> 611 }
> 612
> 613 uirt = kzalloc(sizeof(*uirt), GFP_KERNEL);
> 614 if (!uirt)
> 615 return -ENOMEM;
> 616
> 617 uirt->in = kmalloc(MAX_PACKET, GFP_KERNEL);
> 618 if (!uirt->in)
> 619 goto free_uirt;
> 620
> 621 uirt->out = kmalloc(MAX_PACKET, GFP_KERNEL);
> 622 if (!uirt->out)
> 623 goto free_uirt;
> 624
> 625 rc = rc_allocate_device(RC_DRIVER_IR_RAW);
> 626 if (!rc)
> 627 goto free_uirt;
> 628
> 629 urb = usb_alloc_urb(0, GFP_KERNEL);
> 630 if (!urb)
> 631 goto free_rcdev;
> 632
> 633 pipe = usb_rcvbulkpipe(usbdev, ep_in->bEndpointAddress);
> 634 usb_fill_bulk_urb(urb, usbdev, pipe, uirt->in, MAX_PACKET,
> 635 uirt_in_callback, uirt);
> 636 uirt->urb_in = urb;
> 637
> 638 urb = usb_alloc_urb(0, GFP_KERNEL);
> > 639 if (!urb)
> 640 goto free_rcdev;
> 641
> 642 pipe = usb_sndbulkpipe(usbdev, ep_out->bEndpointAddress);
> 643 usb_fill_bulk_urb(urb, usbdev, pipe, uirt->out, MAX_PACKET,
> 644 uirt_out_callback, uirt);
> 645
> 646 uirt->dev = &intf->dev;
> 647 uirt->usbdev = usbdev;
> 648 uirt->rc = rc;
> 649 uirt->urb_out = urb;
> 650 uirt->rx_state = RX_STATE_INTERSPACE_HIGH;
> 651
> 652 err = usb_submit_urb(uirt->urb_in, GFP_KERNEL);
> 653 if (err != 0) {
> 654 dev_err(uirt->dev, "failed to submit read urb: %d\n", err);
> 655 goto free_rcdev;
> 656 }
> 657
> 658 err = init_ftdi(usbdev);
> 659 if (err) {
> 660 dev_err(uirt->dev, "failed to setup ftdi: %d\n", err);
> 661 goto kill_urbs;
> 662 }
> 663
> 664 err = uirt_setup(uirt);
> 665 if (err)
> 666 goto kill_urbs;
> 667
> 668 usb_make_path(usbdev, uirt->phys, sizeof(uirt->phys));
> 669
> 670 rc->device_name = "USB-UIRT";
> 671 rc->driver_name = KBUILD_MODNAME;
> 672 rc->input_phys = uirt->phys;
> 673 usb_to_input_id(usbdev, &rc->input_id);
> 674 rc->dev.parent = &intf->dev;
> 675 rc->priv = uirt;
> 676 rc->tx_ir = uirt_tx;
> 677 rc->s_tx_carrier = uirt_set_tx_carrier;
> 678 rc->s_learning_mode = uirt_set_rx_wideband;
> 679 rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
> 680 rc->map_name = RC_MAP_RC6_MCE;
> 681 rc->rx_resolution = UNIT_US;
> 682 rc->timeout = IR_TIMEOUT;
> 683
> 684 uirt_set_tx_carrier(rc, 38000);
> 685
> 686 err = rc_register_device(rc);
> 687 if (err)
> 688 goto kill_urbs;
> 689
> 690 usb_set_intfdata(intf, uirt);
> 691
> 692 return 0;
> 693
> 694 kill_urbs:
> 695 usb_kill_urb(uirt->urb_in);
> 696 usb_kill_urb(uirt->urb_out);
> 697 free_rcdev:
> 698 usb_free_urb(uirt->urb_in);
> 699 usb_free_urb(uirt->urb_out);
> 700 rc_free_device(rc);
> 701 free_uirt:
> 702 kfree(uirt->in);
> 703 kfree(uirt->out);
> 704 kfree(uirt);
> 705 return err;
> 706 }
> 707
>
> ---
> 0-DAY CI Kernel Test Service, Intel Corporation
> https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
On 6/18/2021 1:44 AM, Sean Young wrote:
> On Fri, Jun 18, 2021 at 06:18:06AM +0800, kernel test robot wrote:
>> Hi Sean,
>>
>> I love your patch! Perhaps something to improve:
>>
>> [auto build test WARNING on linuxtv-media/master]
>> [also build test WARNING on usb-serial/usb-next usb/usb-testing peter.chen-usb/for-usb-next v5.13-rc6 next-20210617]
>> [If your patch is applied to the wrong git tree, kindly drop us a note.
>> And when submitting patch, we suggest to use '--base' as documented in
>> https://git-scm.com/docs/git-format-patch]
>>
>> url: https://github.com/0day-ci/linux/commits/Sean-Young/IR-driver-for-USB-UIRT-device/20210616-182135
>> base: git://linuxtv.org/media_tree.git master
>> config: powerpc64-randconfig-r012-20210617 (attached as .config)
>> compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 64720f57bea6a6bf033feef4a5751ab9c0c3b401)
>> reproduce (this is a W=1 build):
>> wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
>> chmod +x ~/bin/make.cross
>> # install powerpc64 cross compiling tool for clang build
>> # apt-get install binutils-powerpc64-linux-gnu
>> # https://github.com/0day-ci/linux/commit/17d3a0332baecb0359e05e8ae755478c7a1a4468
>> git remote add linux-review https://github.com/0day-ci/linux
>> git fetch --no-tags linux-review Sean-Young/IR-driver-for-USB-UIRT-device/20210616-182135
>> git checkout 17d3a0332baecb0359e05e8ae755478c7a1a4468
>> # save the attached .config to linux build tree
>> COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=powerpc64
>>
>> If you fix the issue, kindly add following tag as appropriate
>> Reported-by: kernel test robot <lkp@intel.com>
>>
>> All warnings (new ones prefixed by >>):
>>
>> In file included from drivers/media/rc/uirt.c:11:
>> In file included from include/linux/completion.h:12:
>> In file included from include/linux/swait.h:5:
>> In file included from include/linux/list.h:9:
>> In file included from include/linux/kernel.h:12:
>> In file included from include/linux/bitops.h:32:
>> In file included from arch/powerpc/include/asm/bitops.h:62:
>> arch/powerpc/include/asm/barrier.h:49:9: warning: '__lwsync' macro redefined [-Wmacro-redefined]
>> #define __lwsync() __asm__ __volatile__ (stringify_in_c(LWSYNC) : : :"memory")
>> ^
>> <built-in>:310:9: note: previous definition is here
>> #define __lwsync __builtin_ppc_lwsync
>> ^
>>>> drivers/media/rc/uirt.c:639:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
>> if (!urb)
>> ^~~~
>> drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
>> return err;
>
> This is interesting. clang is right here, there are error paths where err is
> not initialized. gcc-11.1 does not pick this up for some reason. The error path
> should be an immediate dominator so it shouldn't be complicated to detect.
The reason GCC does not warn about this is due to commit 78a5255ffb6a
("Stop the ad-hoc games with -Wno-maybe-initialized"), which disables
the GCC version of this warning except with W=2, which very few people
use. You could use 'KCFLAGS=-Wmaybe-uninitialized' to try and see the
same warning.
Cheers,
Nathan
> I'll send out a v5 with this issue fixed.
>
> Sean
>
>> ^~~
>> drivers/media/rc/uirt.c:639:2: note: remove the 'if' if its condition is always false
>> if (!urb)
>> ^~~~~~~~~
>> drivers/media/rc/uirt.c:630:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
>> if (!urb)
>> ^~~~
>> drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
>> return err;
>> ^~~
>> drivers/media/rc/uirt.c:630:2: note: remove the 'if' if its condition is always false
>> if (!urb)
>> ^~~~~~~~~
>> drivers/media/rc/uirt.c:626:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
>> if (!rc)
>> ^~~
>> drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
>> return err;
>> ^~~
>> drivers/media/rc/uirt.c:626:2: note: remove the 'if' if its condition is always false
>> if (!rc)
>> ^~~~~~~~
>> drivers/media/rc/uirt.c:622:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
>> if (!uirt->out)
>> ^~~~~~~~~~
>> drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
>> return err;
>> ^~~
>> drivers/media/rc/uirt.c:622:2: note: remove the 'if' if its condition is always false
>> if (!uirt->out)
>> ^~~~~~~~~~~~~~~
>> drivers/media/rc/uirt.c:618:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
>> if (!uirt->in)
>> ^~~~~~~~~
>> drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
>> return err;
>> ^~~
>> drivers/media/rc/uirt.c:618:2: note: remove the 'if' if its condition is always false
>> if (!uirt->in)
>> ^~~~~~~~~~~~~~
>> drivers/media/rc/uirt.c:604:15: note: initialize the variable 'err' to silence this warning
>> int pipe, err;
>> ^
>> = 0
>> 6 warnings generated.
>>
>>
>> vim +639 drivers/media/rc/uirt.c
>>
>> 594
>> 595 static int uirt_probe(struct usb_interface *intf,
>> 596 const struct usb_device_id *id)
>> 597 {
>> 598 struct usb_device *usbdev = interface_to_usbdev(intf);
>> 599 struct usb_endpoint_descriptor *ep_in;
>> 600 struct usb_endpoint_descriptor *ep_out;
>> 601 struct uirt *uirt;
>> 602 struct rc_dev *rc;
>> 603 struct urb *urb;
>> 604 int pipe, err;
>> 605
>> 606 if (usb_find_common_endpoints(intf->cur_altsetting, &ep_in, &ep_out, NULL, NULL) ||
>> 607 usb_endpoint_maxp(ep_in) != MAX_PACKET ||
>> 608 usb_endpoint_maxp(ep_out) != MAX_PACKET) {
>> 609 dev_err(&intf->dev, "required endpoints not found\n");
>> 610 return -ENODEV;
>> 611 }
>> 612
>> 613 uirt = kzalloc(sizeof(*uirt), GFP_KERNEL);
>> 614 if (!uirt)
>> 615 return -ENOMEM;
>> 616
>> 617 uirt->in = kmalloc(MAX_PACKET, GFP_KERNEL);
>> 618 if (!uirt->in)
>> 619 goto free_uirt;
>> 620
>> 621 uirt->out = kmalloc(MAX_PACKET, GFP_KERNEL);
>> 622 if (!uirt->out)
>> 623 goto free_uirt;
>> 624
>> 625 rc = rc_allocate_device(RC_DRIVER_IR_RAW);
>> 626 if (!rc)
>> 627 goto free_uirt;
>> 628
>> 629 urb = usb_alloc_urb(0, GFP_KERNEL);
>> 630 if (!urb)
>> 631 goto free_rcdev;
>> 632
>> 633 pipe = usb_rcvbulkpipe(usbdev, ep_in->bEndpointAddress);
>> 634 usb_fill_bulk_urb(urb, usbdev, pipe, uirt->in, MAX_PACKET,
>> 635 uirt_in_callback, uirt);
>> 636 uirt->urb_in = urb;
>> 637
>> 638 urb = usb_alloc_urb(0, GFP_KERNEL);
>> > 639 if (!urb)
>> 640 goto free_rcdev;
>> 641
>> 642 pipe = usb_sndbulkpipe(usbdev, ep_out->bEndpointAddress);
>> 643 usb_fill_bulk_urb(urb, usbdev, pipe, uirt->out, MAX_PACKET,
>> 644 uirt_out_callback, uirt);
>> 645
>> 646 uirt->dev = &intf->dev;
>> 647 uirt->usbdev = usbdev;
>> 648 uirt->rc = rc;
>> 649 uirt->urb_out = urb;
>> 650 uirt->rx_state = RX_STATE_INTERSPACE_HIGH;
>> 651
>> 652 err = usb_submit_urb(uirt->urb_in, GFP_KERNEL);
>> 653 if (err != 0) {
>> 654 dev_err(uirt->dev, "failed to submit read urb: %d\n", err);
>> 655 goto free_rcdev;
>> 656 }
>> 657
>> 658 err = init_ftdi(usbdev);
>> 659 if (err) {
>> 660 dev_err(uirt->dev, "failed to setup ftdi: %d\n", err);
>> 661 goto kill_urbs;
>> 662 }
>> 663
>> 664 err = uirt_setup(uirt);
>> 665 if (err)
>> 666 goto kill_urbs;
>> 667
>> 668 usb_make_path(usbdev, uirt->phys, sizeof(uirt->phys));
>> 669
>> 670 rc->device_name = "USB-UIRT";
>> 671 rc->driver_name = KBUILD_MODNAME;
>> 672 rc->input_phys = uirt->phys;
>> 673 usb_to_input_id(usbdev, &rc->input_id);
>> 674 rc->dev.parent = &intf->dev;
>> 675 rc->priv = uirt;
>> 676 rc->tx_ir = uirt_tx;
>> 677 rc->s_tx_carrier = uirt_set_tx_carrier;
>> 678 rc->s_learning_mode = uirt_set_rx_wideband;
>> 679 rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
>> 680 rc->map_name = RC_MAP_RC6_MCE;
>> 681 rc->rx_resolution = UNIT_US;
>> 682 rc->timeout = IR_TIMEOUT;
>> 683
>> 684 uirt_set_tx_carrier(rc, 38000);
>> 685
>> 686 err = rc_register_device(rc);
>> 687 if (err)
>> 688 goto kill_urbs;
>> 689
>> 690 usb_set_intfdata(intf, uirt);
>> 691
>> 692 return 0;
>> 693
>> 694 kill_urbs:
>> 695 usb_kill_urb(uirt->urb_in);
>> 696 usb_kill_urb(uirt->urb_out);
>> 697 free_rcdev:
>> 698 usb_free_urb(uirt->urb_in);
>> 699 usb_free_urb(uirt->urb_out);
>> 700 rc_free_device(rc);
>> 701 free_uirt:
>> 702 kfree(uirt->in);
>> 703 kfree(uirt->out);
>> 704 kfree(uirt);
>> 705 return err;
>> 706 }
>> 707
>>
>> ---
>> 0-DAY CI Kernel Test Service, Intel Corporation
>> https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
>
>
On Fri, Jun 18, 2021 at 10:04:58AM -0700, Nathan Chancellor wrote:
> On 6/18/2021 1:44 AM, Sean Young wrote:
> > On Fri, Jun 18, 2021 at 06:18:06AM +0800, kernel test robot wrote:
> > > Hi Sean,
> > >
> > > I love your patch! Perhaps something to improve:
> > >
> > > [auto build test WARNING on linuxtv-media/master]
> > > [also build test WARNING on usb-serial/usb-next usb/usb-testing peter.chen-usb/for-usb-next v5.13-rc6 next-20210617]
> > > [If your patch is applied to the wrong git tree, kindly drop us a note.
> > > And when submitting patch, we suggest to use '--base' as documented in
> > > https://git-scm.com/docs/git-format-patch]
> > >
> > > url: https://github.com/0day-ci/linux/commits/Sean-Young/IR-driver-for-USB-UIRT-device/20210616-182135
> > > base: git://linuxtv.org/media_tree.git master
> > > config: powerpc64-randconfig-r012-20210617 (attached as .config)
> > > compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 64720f57bea6a6bf033feef4a5751ab9c0c3b401)
> > > reproduce (this is a W=1 build):
> > > wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
> > > chmod +x ~/bin/make.cross
> > > # install powerpc64 cross compiling tool for clang build
> > > # apt-get install binutils-powerpc64-linux-gnu
> > > # https://github.com/0day-ci/linux/commit/17d3a0332baecb0359e05e8ae755478c7a1a4468
> > > git remote add linux-review https://github.com/0day-ci/linux
> > > git fetch --no-tags linux-review Sean-Young/IR-driver-for-USB-UIRT-device/20210616-182135
> > > git checkout 17d3a0332baecb0359e05e8ae755478c7a1a4468
> > > # save the attached .config to linux build tree
> > > COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=powerpc64
> > >
> > > If you fix the issue, kindly add following tag as appropriate
> > > Reported-by: kernel test robot <lkp@intel.com>
> > >
> > > All warnings (new ones prefixed by >>):
> > >
> > > In file included from drivers/media/rc/uirt.c:11:
> > > In file included from include/linux/completion.h:12:
> > > In file included from include/linux/swait.h:5:
> > > In file included from include/linux/list.h:9:
> > > In file included from include/linux/kernel.h:12:
> > > In file included from include/linux/bitops.h:32:
> > > In file included from arch/powerpc/include/asm/bitops.h:62:
> > > arch/powerpc/include/asm/barrier.h:49:9: warning: '__lwsync' macro redefined [-Wmacro-redefined]
> > > #define __lwsync() __asm__ __volatile__ (stringify_in_c(LWSYNC) : : :"memory")
> > > ^
> > > <built-in>:310:9: note: previous definition is here
> > > #define __lwsync __builtin_ppc_lwsync
> > > ^
> > > > > drivers/media/rc/uirt.c:639:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
> > > if (!urb)
> > > ^~~~
> > > drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
> > > return err;
> >
> > This is interesting. clang is right here, there are error paths where err is
> > not initialized. gcc-11.1 does not pick this up for some reason. The error path
> > should be an immediate dominator so it shouldn't be complicated to detect.
>
> The reason GCC does not warn about this is due to commit 78a5255ffb6a ("Stop
> the ad-hoc games with -Wno-maybe-initialized"), which disables the GCC
> version of this warning except with W=2, which very few people use. You
> could use 'KCFLAGS=-Wmaybe-uninitialized' to try and see the same warning.
You're right, this is exactly the issue here. When running with W=2 or
KCFLAGS=-Wmaybe-uninitialized you do get the warning.
There are paths where err are initialized, so gcc correctly says:
warning: ‘err’ may be used uninitialized in this function [-Wmaybe-uninitialized]
Thanks,
Sean
>
> Cheers,
> Nathan
>
> > I'll send out a v5 with this issue fixed.
> >
> > Sean
> >
> > > ^~~
> > > drivers/media/rc/uirt.c:639:2: note: remove the 'if' if its condition is always false
> > > if (!urb)
> > > ^~~~~~~~~
> > > drivers/media/rc/uirt.c:630:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
> > > if (!urb)
> > > ^~~~
> > > drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
> > > return err;
> > > ^~~
> > > drivers/media/rc/uirt.c:630:2: note: remove the 'if' if its condition is always false
> > > if (!urb)
> > > ^~~~~~~~~
> > > drivers/media/rc/uirt.c:626:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
> > > if (!rc)
> > > ^~~
> > > drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
> > > return err;
> > > ^~~
> > > drivers/media/rc/uirt.c:626:2: note: remove the 'if' if its condition is always false
> > > if (!rc)
> > > ^~~~~~~~
> > > drivers/media/rc/uirt.c:622:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
> > > if (!uirt->out)
> > > ^~~~~~~~~~
> > > drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
> > > return err;
> > > ^~~
> > > drivers/media/rc/uirt.c:622:2: note: remove the 'if' if its condition is always false
> > > if (!uirt->out)
> > > ^~~~~~~~~~~~~~~
> > > drivers/media/rc/uirt.c:618:6: warning: variable 'err' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
> > > if (!uirt->in)
> > > ^~~~~~~~~
> > > drivers/media/rc/uirt.c:705:9: note: uninitialized use occurs here
> > > return err;
> > > ^~~
> > > drivers/media/rc/uirt.c:618:2: note: remove the 'if' if its condition is always false
> > > if (!uirt->in)
> > > ^~~~~~~~~~~~~~
> > > drivers/media/rc/uirt.c:604:15: note: initialize the variable 'err' to silence this warning
> > > int pipe, err;
> > > ^
> > > = 0
> > > 6 warnings generated.
> > >
> > >
> > > vim +639 drivers/media/rc/uirt.c
> > >
> > > 594
> > > 595 static int uirt_probe(struct usb_interface *intf,
> > > 596 const struct usb_device_id *id)
> > > 597 {
> > > 598 struct usb_device *usbdev = interface_to_usbdev(intf);
> > > 599 struct usb_endpoint_descriptor *ep_in;
> > > 600 struct usb_endpoint_descriptor *ep_out;
> > > 601 struct uirt *uirt;
> > > 602 struct rc_dev *rc;
> > > 603 struct urb *urb;
> > > 604 int pipe, err;
> > > 605
> > > 606 if (usb_find_common_endpoints(intf->cur_altsetting, &ep_in, &ep_out, NULL, NULL) ||
> > > 607 usb_endpoint_maxp(ep_in) != MAX_PACKET ||
> > > 608 usb_endpoint_maxp(ep_out) != MAX_PACKET) {
> > > 609 dev_err(&intf->dev, "required endpoints not found\n");
> > > 610 return -ENODEV;
> > > 611 }
> > > 612
> > > 613 uirt = kzalloc(sizeof(*uirt), GFP_KERNEL);
> > > 614 if (!uirt)
> > > 615 return -ENOMEM;
> > > 616
> > > 617 uirt->in = kmalloc(MAX_PACKET, GFP_KERNEL);
> > > 618 if (!uirt->in)
> > > 619 goto free_uirt;
> > > 620
> > > 621 uirt->out = kmalloc(MAX_PACKET, GFP_KERNEL);
> > > 622 if (!uirt->out)
> > > 623 goto free_uirt;
> > > 624
> > > 625 rc = rc_allocate_device(RC_DRIVER_IR_RAW);
> > > 626 if (!rc)
> > > 627 goto free_uirt;
> > > 628
> > > 629 urb = usb_alloc_urb(0, GFP_KERNEL);
> > > 630 if (!urb)
> > > 631 goto free_rcdev;
> > > 632
> > > 633 pipe = usb_rcvbulkpipe(usbdev, ep_in->bEndpointAddress);
> > > 634 usb_fill_bulk_urb(urb, usbdev, pipe, uirt->in, MAX_PACKET,
> > > 635 uirt_in_callback, uirt);
> > > 636 uirt->urb_in = urb;
> > > 637
> > > 638 urb = usb_alloc_urb(0, GFP_KERNEL);
> > > > 639 if (!urb)
> > > 640 goto free_rcdev;
> > > 641
> > > 642 pipe = usb_sndbulkpipe(usbdev, ep_out->bEndpointAddress);
> > > 643 usb_fill_bulk_urb(urb, usbdev, pipe, uirt->out, MAX_PACKET,
> > > 644 uirt_out_callback, uirt);
> > > 645
> > > 646 uirt->dev = &intf->dev;
> > > 647 uirt->usbdev = usbdev;
> > > 648 uirt->rc = rc;
> > > 649 uirt->urb_out = urb;
> > > 650 uirt->rx_state = RX_STATE_INTERSPACE_HIGH;
> > > 651
> > > 652 err = usb_submit_urb(uirt->urb_in, GFP_KERNEL);
> > > 653 if (err != 0) {
> > > 654 dev_err(uirt->dev, "failed to submit read urb: %d\n", err);
> > > 655 goto free_rcdev;
> > > 656 }
> > > 657
> > > 658 err = init_ftdi(usbdev);
> > > 659 if (err) {
> > > 660 dev_err(uirt->dev, "failed to setup ftdi: %d\n", err);
> > > 661 goto kill_urbs;
> > > 662 }
> > > 663
> > > 664 err = uirt_setup(uirt);
> > > 665 if (err)
> > > 666 goto kill_urbs;
> > > 667
> > > 668 usb_make_path(usbdev, uirt->phys, sizeof(uirt->phys));
> > > 669
> > > 670 rc->device_name = "USB-UIRT";
> > > 671 rc->driver_name = KBUILD_MODNAME;
> > > 672 rc->input_phys = uirt->phys;
> > > 673 usb_to_input_id(usbdev, &rc->input_id);
> > > 674 rc->dev.parent = &intf->dev;
> > > 675 rc->priv = uirt;
> > > 676 rc->tx_ir = uirt_tx;
> > > 677 rc->s_tx_carrier = uirt_set_tx_carrier;
> > > 678 rc->s_learning_mode = uirt_set_rx_wideband;
> > > 679 rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
> > > 680 rc->map_name = RC_MAP_RC6_MCE;
> > > 681 rc->rx_resolution = UNIT_US;
> > > 682 rc->timeout = IR_TIMEOUT;
> > > 683
> > > 684 uirt_set_tx_carrier(rc, 38000);
> > > 685
> > > 686 err = rc_register_device(rc);
> > > 687 if (err)
> > > 688 goto kill_urbs;
> > > 689
> > > 690 usb_set_intfdata(intf, uirt);
> > > 691
> > > 692 return 0;
> > > 693
> > > 694 kill_urbs:
> > > 695 usb_kill_urb(uirt->urb_in);
> > > 696 usb_kill_urb(uirt->urb_out);
> > > 697 free_rcdev:
> > > 698 usb_free_urb(uirt->urb_in);
> > > 699 usb_free_urb(uirt->urb_out);
> > > 700 rc_free_device(rc);
> > > 701 free_uirt:
> > > 702 kfree(uirt->in);
> > > 703 kfree(uirt->out);
> > > 704 kfree(uirt);
> > > 705 return err;
> > > 706 }
> > > 707
> > >
> > > ---
> > > 0-DAY CI Kernel Test Service, Intel Corporation
> > > https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
> >
> >
@@ -471,6 +471,17 @@ config IR_TOY
To compile this driver as a module, choose M here: the module will be
called ir_toy.
+config IR_UIRT
+ tristate "USB-UIRT"
+ depends on RC_CORE
+ depends on USB_ARCH_HAS_HCD
+ help
+ Say Y here if you want to use the USB-UIRT. See
+ http://www.usbuirt.com/
+
+ To compile this driver as a module, choose M here: the module will be
+ called uirt.
+
endif #RC_DEVICES
endif #RC_CORE
@@ -50,3 +50,4 @@ obj-$(CONFIG_IR_SIR) += sir_ir.o
obj-$(CONFIG_IR_MTK) += mtk-cir.o
obj-$(CONFIG_RC_XBOX_DVD) += xbox_remote.o
obj-$(CONFIG_IR_TOY) += ir_toy.o
+obj-$(CONFIG_IR_UIRT) += uirt.o
new file mode 100644
@@ -0,0 +1,740 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * USB-UIRT
+ *
+ * Copyright (C) 2021 Sean Young <sean@mess.org>
+ *
+ * See http://www.usbuirt.com/USB-UIRT%20Command%20Protocol.doc
+ */
+
+#include <linux/completion.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/usb/input.h>
+
+#include <media/rc-core.h>
+
+/* Copied from drivers/usb/serial/ftdi_sio.h */
+#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_BAUDRATE_REQUEST 3
+
+#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER
+#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40
+
+#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL
+
+#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */
+#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */
+
+#define FTDI_SIO_RTS_CTS_HS BIT(8)
+#define FTDI_RS0_CTS BIT(4)
+
+static const u8 CMD_GETVERSION[] = { 0x23, 0xdd };
+static const u8 CMD_SETMODERAW[] = { 0x21, 0xdf };
+static const u8 CMD_SETWIDEBAND[] = { 0x24, 0xdc };
+
+#define UNIT_US 50
+#define IR_TIMEOUT 12500
+#define MAX_PACKET 64
+
+enum cmd_state {
+ CMD_STATE_GETVERSION,
+ CMD_STATE_SETMODERAW,
+ CMD_STATE_SETMODEWIDEBAND,
+ CMD_STATE_IRDATA,
+ CMD_STATE_DOTXRAW,
+ CMD_STATE_STREAMING_TX,
+};
+
+enum rx_state {
+ RX_STATE_INTERSPACE_HIGH,
+ RX_STATE_INTERSPACE_LOW,
+ RX_STATE_ON_HIGH,
+ RX_STATE_ON_LOW,
+ RX_STATE_FREQ_HIGH,
+ RX_STATE_FREQ_LOW,
+ RX_STATE_OFF_HIGH,
+ RX_STATE_OFF_LOW,
+};
+
+struct uirt {
+ struct device *dev;
+ struct usb_device *usbdev;
+
+ struct rc_dev *rc;
+ struct urb *urb_in, *urb_out;
+
+ u8 *in;
+ u8 *out;
+ struct completion cmd_done;
+ u8 freq;
+ u8 high;
+ bool wideband;
+ u32 last_duration;
+
+ enum cmd_state cmd_state;
+ enum rx_state rx_state;
+
+ void *tx_buf;
+ u32 tx_len;
+
+ char phys[64];
+};
+
+// read IR in raw mode
+static void uirt_raw_mode(struct uirt *uirt, u32 offset, u32 len)
+{
+ uint i, duration;
+
+ for (i = offset; i < len; i++) {
+ switch (uirt->rx_state) {
+ case RX_STATE_INTERSPACE_HIGH:
+ uirt->rx_state = RX_STATE_INTERSPACE_LOW;
+ break;
+ case RX_STATE_INTERSPACE_LOW:
+ uirt->rx_state = RX_STATE_ON_HIGH;
+ break;
+ case RX_STATE_ON_HIGH:
+ duration = uirt->in[i];
+ if (duration == 0)
+ duration = 1;
+
+ ir_raw_event_store(uirt->rc, &((struct ir_raw_event) {
+ .duration = duration * UNIT_US,
+ .pulse = true,
+ }));
+
+ uirt->rx_state = RX_STATE_OFF_HIGH;
+ break;
+ case RX_STATE_OFF_HIGH:
+ if (uirt->in[i] == 0xff) {
+ ir_raw_event_store(uirt->rc, &((struct ir_raw_event) {
+ .duration = IR_TIMEOUT,
+ .timeout = true,
+ }));
+ uirt->rx_state = RX_STATE_INTERSPACE_HIGH;
+ break;
+ }
+
+ duration = uirt->in[i];
+ if (duration == 0)
+ duration = 1;
+
+ ir_raw_event_store(uirt->rc, &((struct ir_raw_event) {
+ .duration = duration * UNIT_US,
+ .pulse = false,
+ }));
+ uirt->rx_state = RX_STATE_ON_HIGH;
+ break;
+ default:
+ WARN(1, "unreachable state");
+ uirt->rx_state = RX_STATE_INTERSPACE_HIGH;
+ break;
+ }
+ }
+
+ ir_raw_event_handle(uirt->rc);
+}
+
+// Read IR in wideband mode. The byte stream is delivered in packets,
+// and the values which come in two bytes may straddle two packets
+static void uirt_wideband(struct uirt *uirt, u32 offset, u32 len)
+{
+ uint i, duration, carrier, pulses;
+
+ for (i = offset; i < len; i++) {
+ switch (uirt->rx_state) {
+ case RX_STATE_INTERSPACE_HIGH:
+ uirt->rx_state = RX_STATE_INTERSPACE_LOW;
+ break;
+ case RX_STATE_INTERSPACE_LOW:
+ uirt->rx_state = RX_STATE_ON_HIGH;
+ break;
+ case RX_STATE_ON_HIGH:
+ uirt->high = uirt->in[i];
+ uirt->rx_state = RX_STATE_ON_LOW;
+ break;
+ case RX_STATE_ON_LOW:
+ // duration is in 400ns units
+ duration = (uirt->high << 8) | uirt->in[i];
+ if (duration < 3)
+ duration = 3;
+ uirt->last_duration = duration;
+ ir_raw_event_store(uirt->rc, &((struct ir_raw_event) {
+ .duration = DIV_ROUND_CLOSEST(duration * 2, 5),
+ .pulse = true,
+ }));
+ uirt->rx_state = RX_STATE_FREQ_HIGH;
+ break;
+ case RX_STATE_FREQ_HIGH:
+ if (uirt->in[i] & 0x80) {
+ uirt->high = uirt->in[i] & 0x7f;
+ uirt->rx_state = RX_STATE_FREQ_LOW;
+ break;
+ }
+
+ uirt->high = 0;
+ fallthrough;
+ case RX_STATE_FREQ_LOW:
+ pulses = (uirt->high << 8) | uirt->in[i];
+ if (pulses && uirt->last_duration) {
+ dev_dbg(uirt->dev, "carrier duration %u pulses %u",
+ uirt->last_duration, pulses);
+
+ // calculate the Hz of pulses in duration 400ns units
+ carrier = DIV_ROUND_CLOSEST_ULL(pulses * 10000000ull,
+ uirt->last_duration * 4);
+ ir_raw_event_store(uirt->rc, &((struct ir_raw_event) {
+ .carrier = carrier,
+ .carrier_report = true,
+ }));
+ }
+ uirt->rx_state = RX_STATE_OFF_HIGH;
+ break;
+ case RX_STATE_OFF_HIGH:
+ if (uirt->in[i] == 0xff) {
+ ir_raw_event_store(uirt->rc, &((struct ir_raw_event) {
+ .duration = IR_TIMEOUT,
+ .timeout = true,
+ }));
+ uirt->rx_state = RX_STATE_INTERSPACE_HIGH;
+ } else {
+ uirt->high = uirt->in[i];
+ uirt->rx_state = RX_STATE_OFF_LOW;
+ }
+ break;
+ case RX_STATE_OFF_LOW:
+ // duration is in 400ns units
+ duration = (uirt->high << 8) | uirt->in[i];
+ if (duration < 3)
+ duration = 3;
+ ir_raw_event_store(uirt->rc, &((struct ir_raw_event) {
+ .duration = DIV_ROUND_CLOSEST(duration * 2, 5),
+ .pulse = false,
+ }));
+ uirt->rx_state = RX_STATE_ON_HIGH;
+ break;
+ }
+ }
+
+ ir_raw_event_handle(uirt->rc);
+}
+
+static void uirt_response(struct uirt *uirt, u32 len)
+{
+ int offset = 2;
+ int i;
+
+ dev_dbg(uirt->dev, "state:%d data: %*phN\n", uirt->cmd_state, len, uirt->in);
+
+ // Do we have more IR to transmit
+ if (uirt->cmd_state == CMD_STATE_STREAMING_TX && len >= 2 &&
+ uirt->tx_len && uirt->in[0] & FTDI_RS0_CTS) {
+ u32 len;
+ int err;
+
+ len = min_t(u32, uirt->tx_len, MAX_PACKET);
+
+ memcpy(uirt->out, uirt->tx_buf, len);
+ uirt->urb_out->transfer_buffer_length = len;
+
+ uirt->tx_len -= len;
+ uirt->tx_buf += len;
+
+ err = usb_submit_urb(uirt->urb_out, GFP_ATOMIC);
+ if (err != 0)
+ dev_warn(uirt->dev,
+ "failed to submit out urb: %d\n", err);
+ }
+
+ // if we only have two bytes, it just gives us the serial line status
+ if (len <= 2)
+ return;
+
+ switch (uirt->cmd_state) {
+ case CMD_STATE_GETVERSION:
+ if (len >= 10) {
+ // check checksum
+ u8 checksum = 0;
+
+ for (i = 2; i < len; i++)
+ checksum += uirt->in[i];
+
+ if (checksum != 0) {
+ dev_err(uirt->dev, "checksum does not match: %*phN\n",
+ len, uirt->in);
+ return;
+ }
+
+ dev_info(uirt->dev,
+ "USB-UIRT firmware v%u.%u protocol v%u.%u %02u-%02u-%04u",
+ uirt->in[2], uirt->in[3], uirt->in[4],
+ uirt->in[5], uirt->in[6], uirt->in[7],
+ 2000 + uirt->in[8]);
+
+ complete(&uirt->cmd_done);
+ uirt->cmd_state = CMD_STATE_IRDATA;
+ offset += 10;
+ }
+ break;
+ case CMD_STATE_DOTXRAW:
+ case CMD_STATE_STREAMING_TX:
+ case CMD_STATE_SETMODERAW:
+ case CMD_STATE_SETMODEWIDEBAND:
+ if (len >= 3) {
+ switch (uirt->in[2]) {
+ case 0x20:
+ // 0x20 transmitting is expected during streaming tx
+ if (uirt->cmd_state == CMD_STATE_STREAMING_TX)
+ return;
+
+ if (uirt->cmd_state == CMD_STATE_DOTXRAW)
+ complete(&uirt->cmd_done);
+ else
+ dev_err(uirt->dev, "device transmitting");
+ break;
+ case 0x21:
+ if (uirt->tx_len) {
+ dev_err(uirt->dev, "tx completed with %u left to send",
+ uirt->tx_len);
+ } else {
+ if (uirt->cmd_state == CMD_STATE_SETMODERAW)
+ uirt->wideband = false;
+ if (uirt->cmd_state == CMD_STATE_SETMODEWIDEBAND)
+ uirt->wideband = true;
+
+ complete(&uirt->cmd_done);
+ }
+ break;
+ case 0x80:
+ dev_err(uirt->dev, "checksum error");
+ break;
+ case 0x81:
+ dev_err(uirt->dev, "timeout");
+ break;
+ case 0x82:
+ dev_err(uirt->dev, "command error");
+ break;
+ default:
+ dev_err(uirt->dev, "unknown response");
+ }
+
+ uirt->cmd_state = CMD_STATE_IRDATA;
+ offset += 1;
+ }
+ default:
+ break;
+ }
+
+ if (uirt->wideband)
+ uirt_wideband(uirt, offset, len);
+ else
+ uirt_raw_mode(uirt, offset, len);
+}
+
+static void uirt_out_callback(struct urb *urb)
+{
+ struct uirt *uirt = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ case -EPIPE:
+ default:
+ dev_warn(uirt->dev, "out urb status: %d\n", urb->status);
+ break;
+ }
+}
+
+static void uirt_in_callback(struct urb *urb)
+{
+ struct uirt *uirt = urb->context;
+ int ret;
+
+ if (urb->status == 0)
+ uirt_response(uirt, urb->actual_length);
+ else
+ dev_dbg(uirt->dev, "in urb status: %d\n", urb->status);
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret && ret != -ENODEV)
+ dev_warn(uirt->dev, "failed to resubmit urb: %d\n", ret);
+}
+
+static int uirt_command(struct uirt *uirt, const u8 *cmd, u32 cmd_len,
+ enum cmd_state state)
+{
+ int err;
+
+ init_completion(&uirt->cmd_done);
+
+ uirt->cmd_state = state;
+
+ memcpy(uirt->out, cmd, cmd_len);
+ uirt->urb_out->transfer_buffer_length = cmd_len;
+
+ err = usb_submit_urb(uirt->urb_out, GFP_KERNEL);
+ if (err != 0) {
+ uirt->cmd_state = CMD_STATE_IRDATA;
+ return err;
+ }
+
+ if (!wait_for_completion_timeout(&uirt->cmd_done,
+ msecs_to_jiffies(USB_CTRL_SET_TIMEOUT))) {
+ usb_kill_urb(uirt->urb_out);
+ uirt->cmd_state = CMD_STATE_IRDATA;
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int uirt_setup(struct uirt *uirt)
+{
+ int err;
+
+ err = uirt_command(uirt, CMD_SETMODERAW, sizeof(CMD_SETMODERAW),
+ CMD_STATE_SETMODERAW);
+ if (err) {
+ dev_err(uirt->dev, "set mode raw command failed: %d\n",
+ err);
+ return err;
+ }
+
+ err = uirt_command(uirt, CMD_GETVERSION,
+ sizeof(CMD_GETVERSION), CMD_STATE_GETVERSION);
+ if (err != 0)
+ dev_err(uirt->dev, "get version command failed: %d\n",
+ err);
+
+ return err;
+}
+
+// IR TX when the data can fit into a single packet (i.e 64 bytes).
+// count must be no larger than 24, else we might overflow the buffer.
+static int uirt_short_tx(struct rc_dev *rc, uint *txbuf, uint count)
+{
+ struct uirt *uirt = rc->priv;
+ u8 out[MAX_PACKET], checksum;
+ u32 i, dest, width, freq;
+ int err;
+
+ out[0] = 0x36; // DOTXRAW
+ out[2] = uirt->freq; // carrier frequency
+ out[3] = 1; // number of repeats
+
+ dest = 7;
+
+ freq = uirt->freq & 0x7f;
+
+ for (i = 0; i < count; i++) {
+ // width = (us / freq) * 2.5
+ width = DIV_ROUND_CLOSEST(txbuf[i] * 5, freq * 2);
+
+ if (width == 0)
+ width = 1;
+ else if (width > 127)
+ out[dest++] = (width >> 8) | 0x80;
+
+ out[dest++] = width;
+ }
+
+ // length of RAWSTRUCT + 1
+ out[1] = dest - 1;
+ // number of bytes in encoded pulse/space
+ out[6] = dest - 7;
+
+ // checksum
+ for (i = 0, checksum = 0; i < dest; i++)
+ checksum -= out[i];
+
+ out[dest++] = checksum;
+
+ uirt->tx_buf = NULL;
+ uirt->tx_len = 0;
+
+ err = uirt_command(uirt, out, dest, CMD_STATE_DOTXRAW);
+ if (err != 0)
+ return err;
+
+ return count;
+}
+
+static int uirt_tx(struct rc_dev *rc, uint *txbuf, uint count)
+{
+ struct uirt *uirt = rc->priv;
+ u8 *out;
+ u32 i, dest, unit_raw, freq, len;
+ int err;
+
+ // streaming tx does not work for short IR; use non-streaming
+ // tx for short IR
+ if (count <= 24)
+ return uirt_short_tx(rc, txbuf, count);
+
+ out = kmalloc(count * 2 + 3, GFP_KERNEL);
+ if (!out)
+ return -ENOMEM;
+
+ out[0] = 0x25; // Streaming Transmit
+ out[1] = 0xdb; // checksum over command (just the previous byte)
+ out[2] = uirt->freq; // carrier frequency
+
+ dest = 3;
+
+ freq = uirt->freq & 0x7f;
+
+ for (i = 0; i < count; i++) {
+ // width = (us / freq) * 2.5
+ unit_raw = DIV_ROUND_CLOSEST(txbuf[i] * 5, freq * 2);
+
+ if (unit_raw == 0)
+ unit_raw = 1;
+ else if (unit_raw > 127)
+ out[dest++] = (unit_raw >> 8) | 0x80;
+
+ out[dest++] = unit_raw;
+ }
+
+ len = min_t(u32, dest, MAX_PACKET);
+
+ uirt->tx_buf = out + len;
+ uirt->tx_len = dest - len;
+
+ err = uirt_command(uirt, out, len, CMD_STATE_STREAMING_TX);
+ kfree(out);
+ if (err != 0)
+ return err;
+
+ return count;
+}
+
+static int uirt_set_tx_carrier(struct rc_dev *dev, u32 carrier)
+{
+ struct uirt *uirt = dev->priv;
+
+ if (carrier == 0)
+ // bit 7 must be 1 for unmodulated, lower bits need to
+ // be something that makes sense for tx
+ uirt->freq = 0xc0;
+ else if (carrier >= 20000 && carrier <= 500000)
+ // bit 7 must be 0 for modulated
+ uirt->freq = 2500000 / carrier;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int uirt_set_rx_wideband(struct rc_dev *dev, int enable)
+{
+ struct uirt *uirt = dev->priv;
+ int err;
+
+ if (enable)
+ err = uirt_command(uirt, CMD_SETWIDEBAND,
+ sizeof(CMD_SETWIDEBAND),
+ CMD_STATE_SETMODEWIDEBAND);
+ else
+ err = uirt_command(uirt, CMD_SETMODERAW,
+ sizeof(CMD_SETMODERAW),
+ CMD_STATE_SETMODERAW);
+
+ if (err) {
+ dev_err(uirt->dev, "set mode command failed: %d\n",
+ err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int init_ftdi(struct usb_device *udev)
+{
+ int err;
+
+ // set the baud rate
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ FTDI_SIO_SET_BAUDRATE_REQUEST,
+ FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
+ 0x4009, 0x0001,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (err)
+ return err;
+
+ // enabling rts/cts flow control
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+ 0, FTDI_SIO_RTS_CTS_HS,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (err)
+ return err;
+
+ // Set latency in milliseconds. The USB-UIRT will generate a
+ // urb every latency milliseconds (IR or not), so this should be
+ // set as high as possible to reduce interrupts. However, setting
+ // this value too high will mean there is a preceptible delay for
+ // IR being processed and a key press being registered.
+ //
+ // Choose 50ms as a compromise.
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
+ FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
+ 50, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static int uirt_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *ep_in;
+ struct usb_endpoint_descriptor *ep_out;
+ struct uirt *uirt;
+ struct rc_dev *rc;
+ struct urb *urb;
+ int pipe, err;
+
+ if (usb_find_common_endpoints(intf->cur_altsetting, &ep_in, &ep_out, NULL, NULL) ||
+ usb_endpoint_maxp(ep_in) != MAX_PACKET ||
+ usb_endpoint_maxp(ep_out) != MAX_PACKET) {
+ dev_err(&intf->dev, "required endpoints not found\n");
+ return -ENODEV;
+ }
+
+ uirt = kzalloc(sizeof(*uirt), GFP_KERNEL);
+ if (!uirt)
+ return -ENOMEM;
+
+ uirt->in = kmalloc(MAX_PACKET, GFP_KERNEL);
+ if (!uirt->in)
+ goto free_uirt;
+
+ uirt->out = kmalloc(MAX_PACKET, GFP_KERNEL);
+ if (!uirt->out)
+ goto free_uirt;
+
+ rc = rc_allocate_device(RC_DRIVER_IR_RAW);
+ if (!rc)
+ goto free_uirt;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ goto free_rcdev;
+
+ pipe = usb_rcvbulkpipe(usbdev, ep_in->bEndpointAddress);
+ usb_fill_bulk_urb(urb, usbdev, pipe, uirt->in, MAX_PACKET,
+ uirt_in_callback, uirt);
+ uirt->urb_in = urb;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ goto free_rcdev;
+
+ pipe = usb_sndbulkpipe(usbdev, ep_out->bEndpointAddress);
+ usb_fill_bulk_urb(urb, usbdev, pipe, uirt->out, MAX_PACKET,
+ uirt_out_callback, uirt);
+
+ uirt->dev = &intf->dev;
+ uirt->usbdev = usbdev;
+ uirt->rc = rc;
+ uirt->urb_out = urb;
+ uirt->rx_state = RX_STATE_INTERSPACE_HIGH;
+
+ err = usb_submit_urb(uirt->urb_in, GFP_KERNEL);
+ if (err != 0) {
+ dev_err(uirt->dev, "failed to submit read urb: %d\n", err);
+ goto free_rcdev;
+ }
+
+ err = init_ftdi(usbdev);
+ if (err) {
+ dev_err(uirt->dev, "failed to setup ftdi: %d\n", err);
+ goto kill_urbs;
+ }
+
+ err = uirt_setup(uirt);
+ if (err)
+ goto kill_urbs;
+
+ usb_make_path(usbdev, uirt->phys, sizeof(uirt->phys));
+
+ rc->device_name = "USB-UIRT";
+ rc->driver_name = KBUILD_MODNAME;
+ rc->input_phys = uirt->phys;
+ usb_to_input_id(usbdev, &rc->input_id);
+ rc->dev.parent = &intf->dev;
+ rc->priv = uirt;
+ rc->tx_ir = uirt_tx;
+ rc->s_tx_carrier = uirt_set_tx_carrier;
+ rc->s_learning_mode = uirt_set_rx_wideband;
+ rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
+ rc->map_name = RC_MAP_RC6_MCE;
+ rc->rx_resolution = UNIT_US;
+ rc->timeout = IR_TIMEOUT;
+
+ uirt_set_tx_carrier(rc, 38000);
+
+ err = rc_register_device(rc);
+ if (err)
+ goto kill_urbs;
+
+ usb_set_intfdata(intf, uirt);
+
+ return 0;
+
+kill_urbs:
+ usb_kill_urb(uirt->urb_in);
+ usb_kill_urb(uirt->urb_out);
+free_rcdev:
+ usb_free_urb(uirt->urb_in);
+ usb_free_urb(uirt->urb_out);
+ rc_free_device(rc);
+free_uirt:
+ kfree(uirt->in);
+ kfree(uirt->out);
+ kfree(uirt);
+ return err;
+}
+
+static void uirt_disconnect(struct usb_interface *intf)
+{
+ struct uirt *ir = usb_get_intfdata(intf);
+
+ rc_unregister_device(ir->rc);
+ usb_set_intfdata(intf, NULL);
+ usb_kill_urb(ir->urb_in);
+ usb_kill_urb(ir->urb_out);
+ usb_free_urb(ir->urb_in);
+ usb_free_urb(ir->urb_out);
+ kfree(ir->in);
+ kfree(ir->out);
+ kfree(ir);
+}
+
+static const struct usb_device_id uirt_table[] = {
+ { USB_DEVICE(0x0403, 0xf850) },
+ { }
+};
+
+static struct usb_driver uirt_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = uirt_probe,
+ .disconnect = uirt_disconnect,
+ .id_table = uirt_table,
+};
+
+module_usb_driver(uirt_driver);
+
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_DESCRIPTION("USB-UIRT driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, uirt_table);