2018-01-05 14:58:51 -05:00
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright (C) 2018 Sean Young <sean@mess.org>
# include <linux/module.h>
# include <linux/usb.h>
# include <linux/usb/input.h>
# include <media/rc-core.h>
/* Each bit is 250us */
# define BIT_DURATION 250000
struct imon {
struct device * dev ;
struct urb * ir_urb ;
struct rc_dev * rcdev ;
2018-10-18 12:06:05 -04:00
u8 ir_buf [ 8 ] __aligned ( __alignof__ ( u64 ) ) ;
2018-01-05 14:58:51 -05:00
char phys [ 64 ] ;
} ;
/*
2018-10-18 12:06:05 -04:00
* The first 5 bytes of data represent IR pulse or space . Each bit , starting
* from highest bit in the first byte , represents 250 µ s of data . It is 1
* for space and 0 for pulse .
*
* The station sends 10 packets , and the 7 th byte will be number 1 to 10 , so
* when we receive 10 we assume all the data has arrived .
2018-01-05 14:58:51 -05:00
*/
static void imon_ir_data ( struct imon * imon )
{
2018-08-21 15:57:52 -04:00
struct ir_raw_event rawir = { } ;
2018-10-18 12:06:05 -04:00
u64 d = be64_to_cpup ( ( __be64 * ) imon - > ir_buf ) > > 24 ;
int offset = 40 ;
2018-01-05 14:58:51 -05:00
int bit ;
dev_dbg ( imon - > dev , " data: %*ph " , 8 , imon - > ir_buf ) ;
2018-10-18 12:06:05 -04:00
do {
bit = fls64 ( d & ( BIT_ULL ( offset ) - 1 ) ) ;
if ( bit < offset ) {
dev_dbg ( imon - > dev , " pulse: %d bits " , offset - bit ) ;
2018-01-05 14:58:51 -05:00
rawir . pulse = true ;
2018-10-18 12:06:05 -04:00
rawir . duration = ( offset - bit ) * BIT_DURATION ;
2018-01-05 14:58:51 -05:00
ir_raw_event_store_with_filter ( imon - > rcdev , & rawir ) ;
2018-10-18 12:06:05 -04:00
if ( bit = = 0 )
break ;
2018-01-05 14:58:51 -05:00
2018-10-18 12:06:05 -04:00
offset = bit ;
}
bit = fls64 ( ~ d & ( BIT_ULL ( offset ) - 1 ) ) ;
dev_dbg ( imon - > dev , " space: %d bits " , offset - bit ) ;
2018-01-05 14:58:51 -05:00
rawir . pulse = false ;
2018-10-18 12:06:05 -04:00
rawir . duration = ( offset - bit ) * BIT_DURATION ;
2018-01-05 14:58:51 -05:00
ir_raw_event_store_with_filter ( imon - > rcdev , & rawir ) ;
offset = bit ;
2018-10-18 12:06:05 -04:00
} while ( offset > 0 ) ;
2018-01-05 14:58:51 -05:00
if ( imon - > ir_buf [ 7 ] = = 0x0a ) {
ir_raw_event_set_idle ( imon - > rcdev , true ) ;
ir_raw_event_handle ( imon - > rcdev ) ;
}
}
static void imon_ir_rx ( struct urb * urb )
{
struct imon * imon = urb - > context ;
int ret ;
switch ( urb - > status ) {
case 0 :
if ( imon - > ir_buf [ 7 ] ! = 0xff )
imon_ir_data ( imon ) ;
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
usb_unlink_urb ( urb ) ;
return ;
case - EPIPE :
default :
dev_dbg ( imon - > dev , " error: urb status = %d " , urb - > status ) ;
break ;
}
ret = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( ret & & ret ! = - ENODEV )
dev_warn ( imon - > dev , " failed to resubmit urb: %d " , ret ) ;
}
static int imon_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
struct usb_endpoint_descriptor * ir_ep = NULL ;
struct usb_host_interface * idesc ;
struct usb_device * udev ;
struct rc_dev * rcdev ;
struct imon * imon ;
int i , ret ;
udev = interface_to_usbdev ( intf ) ;
idesc = intf - > cur_altsetting ;
for ( i = 0 ; i < idesc - > desc . bNumEndpoints ; i + + ) {
struct usb_endpoint_descriptor * ep = & idesc - > endpoint [ i ] . desc ;
if ( usb_endpoint_is_int_in ( ep ) ) {
ir_ep = ep ;
break ;
}
}
if ( ! ir_ep ) {
dev_err ( & intf - > dev , " IR endpoint missing " ) ;
return - ENODEV ;
}
imon = devm_kmalloc ( & intf - > dev , sizeof ( * imon ) , GFP_KERNEL ) ;
if ( ! imon )
return - ENOMEM ;
imon - > ir_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! imon - > ir_urb )
return - ENOMEM ;
imon - > dev = & intf - > dev ;
usb_fill_int_urb ( imon - > ir_urb , udev ,
usb_rcvintpipe ( udev , ir_ep - > bEndpointAddress ) ,
imon - > ir_buf , sizeof ( imon - > ir_buf ) ,
imon_ir_rx , imon , ir_ep - > bInterval ) ;
rcdev = devm_rc_allocate_device ( & intf - > dev , RC_DRIVER_IR_RAW ) ;
if ( ! rcdev ) {
ret = - ENOMEM ;
goto free_urb ;
}
usb_make_path ( udev , imon - > phys , sizeof ( imon - > phys ) ) ;
rcdev - > device_name = " iMON Station " ;
rcdev - > driver_name = KBUILD_MODNAME ;
rcdev - > input_phys = imon - > phys ;
usb_to_input_id ( udev , & rcdev - > input_id ) ;
rcdev - > dev . parent = & intf - > dev ;
rcdev - > allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER ;
rcdev - > map_name = RC_MAP_IMON_RSC ;
rcdev - > rx_resolution = BIT_DURATION ;
rcdev - > priv = imon ;
ret = devm_rc_register_device ( & intf - > dev , rcdev ) ;
if ( ret )
goto free_urb ;
imon - > rcdev = rcdev ;
ret = usb_submit_urb ( imon - > ir_urb , GFP_KERNEL ) ;
if ( ret )
goto free_urb ;
usb_set_intfdata ( intf , imon ) ;
return 0 ;
free_urb :
usb_free_urb ( imon - > ir_urb ) ;
return ret ;
}
static void imon_disconnect ( struct usb_interface * intf )
{
struct imon * imon = usb_get_intfdata ( intf ) ;
usb_kill_urb ( imon - > ir_urb ) ;
usb_free_urb ( imon - > ir_urb ) ;
}
static const struct usb_device_id imon_table [ ] = {
/* SoundGraph iMON (IR only) -- sg_imon.inf */
{ USB_DEVICE ( 0x04e8 , 0xff30 ) } ,
{ }
} ;
static struct usb_driver imon_driver = {
. name = KBUILD_MODNAME ,
. probe = imon_probe ,
. disconnect = imon_disconnect ,
. id_table = imon_table
} ;
module_usb_driver ( imon_driver ) ;
MODULE_DESCRIPTION ( " Early raw iMON IR devices " ) ;
MODULE_AUTHOR ( " Sean Young <sean@mess.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DEVICE_TABLE ( usb , imon_table ) ;