18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (C) 2018 Sean Young <sean@mess.org> 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/module.h> 68c2ecf20Sopenharmony_ci#include <linux/usb.h> 78c2ecf20Sopenharmony_ci#include <linux/usb/input.h> 88c2ecf20Sopenharmony_ci#include <media/rc-core.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* Each bit is 250us */ 118c2ecf20Sopenharmony_ci#define BIT_DURATION 250 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistruct imon { 148c2ecf20Sopenharmony_ci struct device *dev; 158c2ecf20Sopenharmony_ci struct urb *ir_urb; 168c2ecf20Sopenharmony_ci struct rc_dev *rcdev; 178c2ecf20Sopenharmony_ci __be64 ir_buf; 188c2ecf20Sopenharmony_ci char phys[64]; 198c2ecf20Sopenharmony_ci}; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * The first 5 bytes of data represent IR pulse or space. Each bit, starting 238c2ecf20Sopenharmony_ci * from highest bit in the first byte, represents 250µs of data. It is 1 248c2ecf20Sopenharmony_ci * for space and 0 for pulse. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * The station sends 10 packets, and the 7th byte will be number 1 to 10, so 278c2ecf20Sopenharmony_ci * when we receive 10 we assume all the data has arrived. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistatic void imon_ir_data(struct imon *imon) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct ir_raw_event rawir = {}; 328c2ecf20Sopenharmony_ci u64 data = be64_to_cpu(imon->ir_buf); 338c2ecf20Sopenharmony_ci u8 packet_no = data & 0xff; 348c2ecf20Sopenharmony_ci int offset = 40; 358c2ecf20Sopenharmony_ci int bit; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (packet_no == 0xff) 388c2ecf20Sopenharmony_ci return; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci dev_dbg(imon->dev, "data: %*ph", 8, &imon->ir_buf); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* 438c2ecf20Sopenharmony_ci * Only the first 5 bytes contain IR data. Right shift so we move 448c2ecf20Sopenharmony_ci * the IR bits to the lower 40 bits. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci data >>= 24; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci do { 498c2ecf20Sopenharmony_ci /* 508c2ecf20Sopenharmony_ci * Find highest set bit which is less or equal to offset 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * offset is the bit above (base 0) where we start looking. 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * data & (BIT_ULL(offset) - 1) masks off any unwanted bits, 558c2ecf20Sopenharmony_ci * so we have just bits less than offset. 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * fls will tell us the highest bit set plus 1 (or 0 if no 588c2ecf20Sopenharmony_ci * bits are set). 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci rawir.pulse = !rawir.pulse; 618c2ecf20Sopenharmony_ci bit = fls64(data & (BIT_ULL(offset) - 1)); 628c2ecf20Sopenharmony_ci if (bit < offset) { 638c2ecf20Sopenharmony_ci dev_dbg(imon->dev, "%s: %d bits", 648c2ecf20Sopenharmony_ci rawir.pulse ? "pulse" : "space", offset - bit); 658c2ecf20Sopenharmony_ci rawir.duration = (offset - bit) * BIT_DURATION; 668c2ecf20Sopenharmony_ci ir_raw_event_store_with_filter(imon->rcdev, &rawir); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci offset = bit; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci data = ~data; 728c2ecf20Sopenharmony_ci } while (offset > 0); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (packet_no == 0x0a && !imon->rcdev->idle) { 758c2ecf20Sopenharmony_ci ir_raw_event_set_idle(imon->rcdev, true); 768c2ecf20Sopenharmony_ci ir_raw_event_handle(imon->rcdev); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void imon_ir_rx(struct urb *urb) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct imon *imon = urb->context; 838c2ecf20Sopenharmony_ci int ret; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci switch (urb->status) { 868c2ecf20Sopenharmony_ci case 0: 878c2ecf20Sopenharmony_ci imon_ir_data(imon); 888c2ecf20Sopenharmony_ci break; 898c2ecf20Sopenharmony_ci case -ECONNRESET: 908c2ecf20Sopenharmony_ci case -ENOENT: 918c2ecf20Sopenharmony_ci case -ESHUTDOWN: 928c2ecf20Sopenharmony_ci usb_unlink_urb(urb); 938c2ecf20Sopenharmony_ci return; 948c2ecf20Sopenharmony_ci case -EPIPE: 958c2ecf20Sopenharmony_ci default: 968c2ecf20Sopenharmony_ci dev_dbg(imon->dev, "error: urb status = %d", urb->status); 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci ret = usb_submit_urb(urb, GFP_ATOMIC); 1018c2ecf20Sopenharmony_ci if (ret && ret != -ENODEV) 1028c2ecf20Sopenharmony_ci dev_warn(imon->dev, "failed to resubmit urb: %d", ret); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int imon_probe(struct usb_interface *intf, 1068c2ecf20Sopenharmony_ci const struct usb_device_id *id) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ir_ep = NULL; 1098c2ecf20Sopenharmony_ci struct usb_host_interface *idesc; 1108c2ecf20Sopenharmony_ci struct usb_device *udev; 1118c2ecf20Sopenharmony_ci struct rc_dev *rcdev; 1128c2ecf20Sopenharmony_ci struct imon *imon; 1138c2ecf20Sopenharmony_ci int i, ret; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci udev = interface_to_usbdev(intf); 1168c2ecf20Sopenharmony_ci idesc = intf->cur_altsetting; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci for (i = 0; i < idesc->desc.bNumEndpoints; i++) { 1198c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ep = &idesc->endpoint[i].desc; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (usb_endpoint_is_int_in(ep)) { 1228c2ecf20Sopenharmony_ci ir_ep = ep; 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (!ir_ep) { 1288c2ecf20Sopenharmony_ci dev_err(&intf->dev, "IR endpoint missing"); 1298c2ecf20Sopenharmony_ci return -ENODEV; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci imon = devm_kmalloc(&intf->dev, sizeof(*imon), GFP_KERNEL); 1338c2ecf20Sopenharmony_ci if (!imon) 1348c2ecf20Sopenharmony_ci return -ENOMEM; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci imon->ir_urb = usb_alloc_urb(0, GFP_KERNEL); 1378c2ecf20Sopenharmony_ci if (!imon->ir_urb) 1388c2ecf20Sopenharmony_ci return -ENOMEM; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci imon->dev = &intf->dev; 1418c2ecf20Sopenharmony_ci usb_fill_int_urb(imon->ir_urb, udev, 1428c2ecf20Sopenharmony_ci usb_rcvintpipe(udev, ir_ep->bEndpointAddress), 1438c2ecf20Sopenharmony_ci &imon->ir_buf, sizeof(imon->ir_buf), 1448c2ecf20Sopenharmony_ci imon_ir_rx, imon, ir_ep->bInterval); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci rcdev = devm_rc_allocate_device(&intf->dev, RC_DRIVER_IR_RAW); 1478c2ecf20Sopenharmony_ci if (!rcdev) { 1488c2ecf20Sopenharmony_ci ret = -ENOMEM; 1498c2ecf20Sopenharmony_ci goto free_urb; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci usb_make_path(udev, imon->phys, sizeof(imon->phys)); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci rcdev->device_name = "iMON Station"; 1558c2ecf20Sopenharmony_ci rcdev->driver_name = KBUILD_MODNAME; 1568c2ecf20Sopenharmony_ci rcdev->input_phys = imon->phys; 1578c2ecf20Sopenharmony_ci usb_to_input_id(udev, &rcdev->input_id); 1588c2ecf20Sopenharmony_ci rcdev->dev.parent = &intf->dev; 1598c2ecf20Sopenharmony_ci rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; 1608c2ecf20Sopenharmony_ci rcdev->map_name = RC_MAP_IMON_RSC; 1618c2ecf20Sopenharmony_ci rcdev->rx_resolution = BIT_DURATION; 1628c2ecf20Sopenharmony_ci rcdev->priv = imon; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = devm_rc_register_device(&intf->dev, rcdev); 1658c2ecf20Sopenharmony_ci if (ret) 1668c2ecf20Sopenharmony_ci goto free_urb; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci imon->rcdev = rcdev; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci ret = usb_submit_urb(imon->ir_urb, GFP_KERNEL); 1718c2ecf20Sopenharmony_ci if (ret) 1728c2ecf20Sopenharmony_ci goto free_urb; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci usb_set_intfdata(intf, imon); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cifree_urb: 1798c2ecf20Sopenharmony_ci usb_free_urb(imon->ir_urb); 1808c2ecf20Sopenharmony_ci return ret; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void imon_disconnect(struct usb_interface *intf) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct imon *imon = usb_get_intfdata(intf); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci usb_kill_urb(imon->ir_urb); 1888c2ecf20Sopenharmony_ci usb_free_urb(imon->ir_urb); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic const struct usb_device_id imon_table[] = { 1928c2ecf20Sopenharmony_ci /* SoundGraph iMON (IR only) -- sg_imon.inf */ 1938c2ecf20Sopenharmony_ci { USB_DEVICE(0x04e8, 0xff30) }, 1948c2ecf20Sopenharmony_ci {} 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic struct usb_driver imon_driver = { 1988c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 1998c2ecf20Sopenharmony_ci .probe = imon_probe, 2008c2ecf20Sopenharmony_ci .disconnect = imon_disconnect, 2018c2ecf20Sopenharmony_ci .id_table = imon_table 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cimodule_usb_driver(imon_driver); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Early raw iMON IR devices"); 2078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sean Young <sean@mess.org>"); 2088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2098c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, imon_table); 210