diff mbox series

[v4,1/2] media: rc: new driver for USB-UIRT device

Message ID 8e380fbe6853bfebd067cdeba2e65e83a3df2922.1623318855.git.sean@mess.org
State Superseded
Headers show
Series IR driver for USB-UIRT device | expand

Commit Message

Sean Young June 10, 2021, 10:16 a.m. UTC
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

kernel test robot June 17, 2021, 10:18 p.m. UTC | #1
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
Sean Young June 18, 2021, 8:44 a.m. UTC | #2
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
Nathan Chancellor June 18, 2021, 5:04 p.m. UTC | #3
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

> 

>
Sean Young June 18, 2021, 9:19 p.m. UTC | #4
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
> > 
> >
diff mbox series

Patch

diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index d0a8326b75c2..cb1a2c65f920 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -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
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 692e9b6b203f..068036698747 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -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
diff --git a/drivers/media/rc/uirt.c b/drivers/media/rc/uirt.c
new file mode 100644
index 000000000000..975cf464113e
--- /dev/null
+++ b/drivers/media/rc/uirt.c
@@ -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);