18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * USB RedRat3 IR Transceiver rc-core driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011 by Jarod Wilson <jarod@redhat.com> 68c2ecf20Sopenharmony_ci * based heavily on the work of Stephen Cox, with additional 78c2ecf20Sopenharmony_ci * help from RedRat Ltd. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This driver began life based an an old version of the first-generation 108c2ecf20Sopenharmony_ci * lirc_mceusb driver from the lirc 0.7.2 distribution. It was then 118c2ecf20Sopenharmony_ci * significantly rewritten by Stephen Cox with the aid of RedRat Ltd's 128c2ecf20Sopenharmony_ci * Chris Dodge. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * The driver was then ported to rc-core and significantly rewritten again, 158c2ecf20Sopenharmony_ci * by Jarod, using the in-kernel mceusb driver as a guide, after an initial 168c2ecf20Sopenharmony_ci * port effort was started by Stephen. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * TODO LIST: 198c2ecf20Sopenharmony_ci * - fix lirc not showing repeats properly 208c2ecf20Sopenharmony_ci * -- 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * The RedRat3 is a USB transceiver with both send & receive, 238c2ecf20Sopenharmony_ci * with 2 separate sensors available for receive to enable 248c2ecf20Sopenharmony_ci * both good long range reception for general use, and good 258c2ecf20Sopenharmony_ci * short range reception when required for learning a signal. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * http://www.redrat.co.uk/ 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * It uses its own little protocol to communicate, the required 308c2ecf20Sopenharmony_ci * parts of which are embedded within this driver. 318c2ecf20Sopenharmony_ci * -- 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 358c2ecf20Sopenharmony_ci#include <linux/device.h> 368c2ecf20Sopenharmony_ci#include <linux/leds.h> 378c2ecf20Sopenharmony_ci#include <linux/module.h> 388c2ecf20Sopenharmony_ci#include <linux/slab.h> 398c2ecf20Sopenharmony_ci#include <linux/usb.h> 408c2ecf20Sopenharmony_ci#include <linux/usb/input.h> 418c2ecf20Sopenharmony_ci#include <media/rc-core.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Driver Information */ 448c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>" 458c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR2 "The Dweller, Stephen Cox" 468c2ecf20Sopenharmony_ci#define DRIVER_DESC "RedRat3 USB IR Transceiver Driver" 478c2ecf20Sopenharmony_ci#define DRIVER_NAME "redrat3" 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* bulk data transfer types */ 508c2ecf20Sopenharmony_ci#define RR3_ERROR 0x01 518c2ecf20Sopenharmony_ci#define RR3_MOD_SIGNAL_IN 0x20 528c2ecf20Sopenharmony_ci#define RR3_MOD_SIGNAL_OUT 0x21 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Get the RR firmware version */ 558c2ecf20Sopenharmony_ci#define RR3_FW_VERSION 0xb1 568c2ecf20Sopenharmony_ci#define RR3_FW_VERSION_LEN 64 578c2ecf20Sopenharmony_ci/* Send encoded signal bulk-sent earlier*/ 588c2ecf20Sopenharmony_ci#define RR3_TX_SEND_SIGNAL 0xb3 598c2ecf20Sopenharmony_ci#define RR3_SET_IR_PARAM 0xb7 608c2ecf20Sopenharmony_ci#define RR3_GET_IR_PARAM 0xb8 618c2ecf20Sopenharmony_ci/* Blink the red LED on the device */ 628c2ecf20Sopenharmony_ci#define RR3_BLINK_LED 0xb9 638c2ecf20Sopenharmony_ci/* Read serial number of device */ 648c2ecf20Sopenharmony_ci#define RR3_READ_SER_NO 0xba 658c2ecf20Sopenharmony_ci#define RR3_SER_NO_LEN 4 668c2ecf20Sopenharmony_ci/* Start capture with the RC receiver */ 678c2ecf20Sopenharmony_ci#define RR3_RC_DET_ENABLE 0xbb 688c2ecf20Sopenharmony_ci/* Stop capture with the RC receiver */ 698c2ecf20Sopenharmony_ci#define RR3_RC_DET_DISABLE 0xbc 708c2ecf20Sopenharmony_ci/* Start capture with the wideband receiver */ 718c2ecf20Sopenharmony_ci#define RR3_MODSIG_CAPTURE 0xb2 728c2ecf20Sopenharmony_ci/* Return the status of RC detector capture */ 738c2ecf20Sopenharmony_ci#define RR3_RC_DET_STATUS 0xbd 748c2ecf20Sopenharmony_ci/* Reset redrat */ 758c2ecf20Sopenharmony_ci#define RR3_RESET 0xa0 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Max number of lengths in the signal. */ 788c2ecf20Sopenharmony_ci#define RR3_IR_IO_MAX_LENGTHS 0x01 798c2ecf20Sopenharmony_ci/* Periods to measure mod. freq. */ 808c2ecf20Sopenharmony_ci#define RR3_IR_IO_PERIODS_MF 0x02 818c2ecf20Sopenharmony_ci/* Size of memory for main signal data */ 828c2ecf20Sopenharmony_ci#define RR3_IR_IO_SIG_MEM_SIZE 0x03 838c2ecf20Sopenharmony_ci/* Delta value when measuring lengths */ 848c2ecf20Sopenharmony_ci#define RR3_IR_IO_LENGTH_FUZZ 0x04 858c2ecf20Sopenharmony_ci/* Timeout for end of signal detection */ 868c2ecf20Sopenharmony_ci#define RR3_IR_IO_SIG_TIMEOUT 0x05 878c2ecf20Sopenharmony_ci/* Minimum value for pause recognition. */ 888c2ecf20Sopenharmony_ci#define RR3_IR_IO_MIN_PAUSE 0x06 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* Clock freq. of EZ-USB chip */ 918c2ecf20Sopenharmony_ci#define RR3_CLK 24000000 928c2ecf20Sopenharmony_ci/* Clock periods per timer count */ 938c2ecf20Sopenharmony_ci#define RR3_CLK_PER_COUNT 12 948c2ecf20Sopenharmony_ci/* (RR3_CLK / RR3_CLK_PER_COUNT) */ 958c2ecf20Sopenharmony_ci#define RR3_CLK_CONV_FACTOR 2000000 968c2ecf20Sopenharmony_ci/* USB bulk-in wideband IR data endpoint address */ 978c2ecf20Sopenharmony_ci#define RR3_WIDE_IN_EP_ADDR 0x81 988c2ecf20Sopenharmony_ci/* USB bulk-in narrowband IR data endpoint address */ 998c2ecf20Sopenharmony_ci#define RR3_NARROW_IN_EP_ADDR 0x82 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* Size of the fixed-length portion of the signal */ 1028c2ecf20Sopenharmony_ci#define RR3_DRIVER_MAXLENS 255 1038c2ecf20Sopenharmony_ci#define RR3_MAX_SIG_SIZE 512 1048c2ecf20Sopenharmony_ci#define RR3_TIME_UNIT 50 1058c2ecf20Sopenharmony_ci#define RR3_END_OF_SIGNAL 0x7f 1068c2ecf20Sopenharmony_ci#define RR3_TX_TRAILER_LEN 2 1078c2ecf20Sopenharmony_ci#define RR3_RX_MIN_TIMEOUT 5 1088c2ecf20Sopenharmony_ci#define RR3_RX_MAX_TIMEOUT 2000 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* The 8051's CPUCS Register address */ 1118c2ecf20Sopenharmony_ci#define RR3_CPUCS_REG_ADDR 0x7f92 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define USB_RR3USB_VENDOR_ID 0x112a 1148c2ecf20Sopenharmony_ci#define USB_RR3USB_PRODUCT_ID 0x0001 1158c2ecf20Sopenharmony_ci#define USB_RR3IIUSB_PRODUCT_ID 0x0005 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* 1198c2ecf20Sopenharmony_ci * The redrat3 encodes an IR signal as set of different lengths and a set 1208c2ecf20Sopenharmony_ci * of indices into those lengths. This sets how much two lengths must 1218c2ecf20Sopenharmony_ci * differ before they are considered distinct, the value is specified 1228c2ecf20Sopenharmony_ci * in microseconds. 1238c2ecf20Sopenharmony_ci * Default 5, value 0 to 127. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistatic int length_fuzz = 5; 1268c2ecf20Sopenharmony_cimodule_param(length_fuzz, uint, 0644); 1278c2ecf20Sopenharmony_ciMODULE_PARM_DESC(length_fuzz, "Length Fuzz (0-127)"); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * When receiving a continuous ir stream (for example when a user is 1318c2ecf20Sopenharmony_ci * holding a button down on a remote), this specifies the minimum size 1328c2ecf20Sopenharmony_ci * of a space when the redrat3 sends a irdata packet to the host. Specified 1338c2ecf20Sopenharmony_ci * in milliseconds. Default value 18ms. 1348c2ecf20Sopenharmony_ci * The value can be between 2 and 30 inclusive. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_cistatic int minimum_pause = 18; 1378c2ecf20Sopenharmony_cimodule_param(minimum_pause, uint, 0644); 1388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(minimum_pause, "Minimum Pause in ms (2-30)"); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* 1418c2ecf20Sopenharmony_ci * The carrier frequency is measured during the first pulse of the IR 1428c2ecf20Sopenharmony_ci * signal. The larger the number of periods used To measure, the more 1438c2ecf20Sopenharmony_ci * accurate the result is likely to be, however some signals have short 1448c2ecf20Sopenharmony_ci * initial pulses, so in some case it may be necessary to reduce this value. 1458c2ecf20Sopenharmony_ci * Default 8, value 1 to 255. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_cistatic int periods_measure_carrier = 8; 1488c2ecf20Sopenharmony_cimodule_param(periods_measure_carrier, uint, 0644); 1498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(periods_measure_carrier, "Number of Periods to Measure Carrier (1-255)"); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistruct redrat3_header { 1538c2ecf20Sopenharmony_ci __be16 length; 1548c2ecf20Sopenharmony_ci __be16 transfer_type; 1558c2ecf20Sopenharmony_ci} __packed; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* sending and receiving irdata */ 1588c2ecf20Sopenharmony_cistruct redrat3_irdata { 1598c2ecf20Sopenharmony_ci struct redrat3_header header; 1608c2ecf20Sopenharmony_ci __be32 pause; 1618c2ecf20Sopenharmony_ci __be16 mod_freq_count; 1628c2ecf20Sopenharmony_ci __be16 num_periods; 1638c2ecf20Sopenharmony_ci __u8 max_lengths; 1648c2ecf20Sopenharmony_ci __u8 no_lengths; 1658c2ecf20Sopenharmony_ci __be16 max_sig_size; 1668c2ecf20Sopenharmony_ci __be16 sig_size; 1678c2ecf20Sopenharmony_ci __u8 no_repeats; 1688c2ecf20Sopenharmony_ci __be16 lens[RR3_DRIVER_MAXLENS]; /* not aligned */ 1698c2ecf20Sopenharmony_ci __u8 sigdata[RR3_MAX_SIG_SIZE]; 1708c2ecf20Sopenharmony_ci} __packed; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* firmware errors */ 1738c2ecf20Sopenharmony_cistruct redrat3_error { 1748c2ecf20Sopenharmony_ci struct redrat3_header header; 1758c2ecf20Sopenharmony_ci __be16 fw_error; 1768c2ecf20Sopenharmony_ci} __packed; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* table of devices that work with this driver */ 1798c2ecf20Sopenharmony_cistatic const struct usb_device_id redrat3_dev_table[] = { 1808c2ecf20Sopenharmony_ci /* Original version of the RedRat3 */ 1818c2ecf20Sopenharmony_ci {USB_DEVICE(USB_RR3USB_VENDOR_ID, USB_RR3USB_PRODUCT_ID)}, 1828c2ecf20Sopenharmony_ci /* Second Version/release of the RedRat3 - RetRat3-II */ 1838c2ecf20Sopenharmony_ci {USB_DEVICE(USB_RR3USB_VENDOR_ID, USB_RR3IIUSB_PRODUCT_ID)}, 1848c2ecf20Sopenharmony_ci {} /* Terminating entry */ 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* Structure to hold all of our device specific stuff */ 1888c2ecf20Sopenharmony_cistruct redrat3_dev { 1898c2ecf20Sopenharmony_ci /* core device bits */ 1908c2ecf20Sopenharmony_ci struct rc_dev *rc; 1918c2ecf20Sopenharmony_ci struct device *dev; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* led control */ 1948c2ecf20Sopenharmony_ci struct led_classdev led; 1958c2ecf20Sopenharmony_ci atomic_t flash; 1968c2ecf20Sopenharmony_ci struct usb_ctrlrequest flash_control; 1978c2ecf20Sopenharmony_ci struct urb *flash_urb; 1988c2ecf20Sopenharmony_ci u8 flash_in_buf; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* learning */ 2018c2ecf20Sopenharmony_ci bool wideband; 2028c2ecf20Sopenharmony_ci struct usb_ctrlrequest learn_control; 2038c2ecf20Sopenharmony_ci struct urb *learn_urb; 2048c2ecf20Sopenharmony_ci u8 learn_buf; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* save off the usb device pointer */ 2078c2ecf20Sopenharmony_ci struct usb_device *udev; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* the receive endpoint */ 2108c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ep_narrow; 2118c2ecf20Sopenharmony_ci /* the buffer to receive data */ 2128c2ecf20Sopenharmony_ci void *bulk_in_buf; 2138c2ecf20Sopenharmony_ci /* urb used to read ir data */ 2148c2ecf20Sopenharmony_ci struct urb *narrow_urb; 2158c2ecf20Sopenharmony_ci struct urb *wide_urb; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* the send endpoint */ 2188c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ep_out; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* usb dma */ 2218c2ecf20Sopenharmony_ci dma_addr_t dma_in; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* Is the device currently transmitting?*/ 2248c2ecf20Sopenharmony_ci bool transmitting; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* store for current packet */ 2278c2ecf20Sopenharmony_ci struct redrat3_irdata irdata; 2288c2ecf20Sopenharmony_ci u16 bytes_read; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci u32 carrier; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci char name[64]; 2338c2ecf20Sopenharmony_ci char phys[64]; 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void redrat3_dump_fw_error(struct redrat3_dev *rr3, int code) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci if (!rr3->transmitting && (code != 0x40)) 2398c2ecf20Sopenharmony_ci dev_info(rr3->dev, "fw error code 0x%02x: ", code); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci switch (code) { 2428c2ecf20Sopenharmony_ci case 0x00: 2438c2ecf20Sopenharmony_ci pr_cont("No Error\n"); 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* Codes 0x20 through 0x2f are IR Firmware Errors */ 2478c2ecf20Sopenharmony_ci case 0x20: 2488c2ecf20Sopenharmony_ci pr_cont("Initial signal pulse not long enough to measure carrier frequency\n"); 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci case 0x21: 2518c2ecf20Sopenharmony_ci pr_cont("Not enough length values allocated for signal\n"); 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci case 0x22: 2548c2ecf20Sopenharmony_ci pr_cont("Not enough memory allocated for signal data\n"); 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci case 0x23: 2578c2ecf20Sopenharmony_ci pr_cont("Too many signal repeats\n"); 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci case 0x28: 2608c2ecf20Sopenharmony_ci pr_cont("Insufficient memory available for IR signal data memory allocation\n"); 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci case 0x29: 2638c2ecf20Sopenharmony_ci pr_cont("Insufficient memory available for IrDa signal data memory allocation\n"); 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Codes 0x30 through 0x3f are USB Firmware Errors */ 2678c2ecf20Sopenharmony_ci case 0x30: 2688c2ecf20Sopenharmony_ci pr_cont("Insufficient memory available for bulk transfer structure\n"); 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* 2728c2ecf20Sopenharmony_ci * Other error codes... These are primarily errors that can occur in 2738c2ecf20Sopenharmony_ci * the control messages sent to the redrat 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_ci case 0x40: 2768c2ecf20Sopenharmony_ci if (!rr3->transmitting) 2778c2ecf20Sopenharmony_ci pr_cont("Signal capture has been terminated\n"); 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci case 0x41: 2808c2ecf20Sopenharmony_ci pr_cont("Attempt to set/get and unknown signal I/O algorithm parameter\n"); 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci case 0x42: 2838c2ecf20Sopenharmony_ci pr_cont("Signal capture already started\n"); 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci default: 2878c2ecf20Sopenharmony_ci pr_cont("Unknown Error\n"); 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic u32 redrat3_val_to_mod_freq(struct redrat3_irdata *irdata) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci u32 mod_freq = 0; 2958c2ecf20Sopenharmony_ci u16 mod_freq_count = be16_to_cpu(irdata->mod_freq_count); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (mod_freq_count != 0) 2988c2ecf20Sopenharmony_ci mod_freq = (RR3_CLK * be16_to_cpu(irdata->num_periods)) / 2998c2ecf20Sopenharmony_ci (mod_freq_count * RR3_CLK_PER_COUNT); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return mod_freq; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/* this function scales down the figures for the same result... */ 3058c2ecf20Sopenharmony_cistatic u32 redrat3_len_to_us(u32 length) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci u32 biglen = length * 1000; 3088c2ecf20Sopenharmony_ci u32 divisor = (RR3_CLK_CONV_FACTOR) / 1000; 3098c2ecf20Sopenharmony_ci u32 result = (u32) (biglen / divisor); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* don't allow zero lengths to go back, breaks lirc */ 3128c2ecf20Sopenharmony_ci return result ? result : 1; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* 3168c2ecf20Sopenharmony_ci * convert us back into redrat3 lengths 3178c2ecf20Sopenharmony_ci * 3188c2ecf20Sopenharmony_ci * length * 1000 length * 1000000 3198c2ecf20Sopenharmony_ci * ------------- = ---------------- = micro 3208c2ecf20Sopenharmony_ci * rr3clk / 1000 rr3clk 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci * 6 * 2 4 * 3 micro * rr3clk micro * rr3clk / 1000 3238c2ecf20Sopenharmony_ci * ----- = 4 ----- = 6 -------------- = len --------------------- 3248c2ecf20Sopenharmony_ci * 3 2 1000000 1000 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_cistatic u32 redrat3_us_to_len(u32 microsec) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci u32 result; 3298c2ecf20Sopenharmony_ci u32 divisor; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci microsec = (microsec > IR_MAX_DURATION) ? IR_MAX_DURATION : microsec; 3328c2ecf20Sopenharmony_ci divisor = (RR3_CLK_CONV_FACTOR / 1000); 3338c2ecf20Sopenharmony_ci result = (u32)(microsec * divisor) / 1000; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* don't allow zero lengths to go back, breaks lirc */ 3368c2ecf20Sopenharmony_ci return result ? result : 1; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic void redrat3_process_ir_data(struct redrat3_dev *rr3) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct ir_raw_event rawir = {}; 3428c2ecf20Sopenharmony_ci struct device *dev; 3438c2ecf20Sopenharmony_ci unsigned int i, sig_size, offset, val; 3448c2ecf20Sopenharmony_ci u32 mod_freq; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci dev = rr3->dev; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci mod_freq = redrat3_val_to_mod_freq(&rr3->irdata); 3498c2ecf20Sopenharmony_ci dev_dbg(dev, "Got mod_freq of %u\n", mod_freq); 3508c2ecf20Sopenharmony_ci if (mod_freq && rr3->wideband) { 3518c2ecf20Sopenharmony_ci struct ir_raw_event ev = { 3528c2ecf20Sopenharmony_ci .carrier_report = 1, 3538c2ecf20Sopenharmony_ci .carrier = mod_freq 3548c2ecf20Sopenharmony_ci }; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ir_raw_event_store(rr3->rc, &ev); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* process each rr3 encoded byte into an int */ 3608c2ecf20Sopenharmony_ci sig_size = be16_to_cpu(rr3->irdata.sig_size); 3618c2ecf20Sopenharmony_ci for (i = 0; i < sig_size; i++) { 3628c2ecf20Sopenharmony_ci offset = rr3->irdata.sigdata[i]; 3638c2ecf20Sopenharmony_ci val = get_unaligned_be16(&rr3->irdata.lens[offset]); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* we should always get pulse/space/pulse/space samples */ 3668c2ecf20Sopenharmony_ci if (i % 2) 3678c2ecf20Sopenharmony_ci rawir.pulse = false; 3688c2ecf20Sopenharmony_ci else 3698c2ecf20Sopenharmony_ci rawir.pulse = true; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci rawir.duration = redrat3_len_to_us(val); 3728c2ecf20Sopenharmony_ci /* cap the value to IR_MAX_DURATION */ 3738c2ecf20Sopenharmony_ci rawir.duration = (rawir.duration > IR_MAX_DURATION) ? 3748c2ecf20Sopenharmony_ci IR_MAX_DURATION : rawir.duration; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci dev_dbg(dev, "storing %s with duration %d (i: %d)\n", 3778c2ecf20Sopenharmony_ci rawir.pulse ? "pulse" : "space", rawir.duration, i); 3788c2ecf20Sopenharmony_ci ir_raw_event_store_with_filter(rr3->rc, &rawir); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* add a trailing space */ 3828c2ecf20Sopenharmony_ci rawir.pulse = false; 3838c2ecf20Sopenharmony_ci rawir.timeout = true; 3848c2ecf20Sopenharmony_ci rawir.duration = rr3->rc->timeout; 3858c2ecf20Sopenharmony_ci dev_dbg(dev, "storing trailing timeout with duration %d\n", 3868c2ecf20Sopenharmony_ci rawir.duration); 3878c2ecf20Sopenharmony_ci ir_raw_event_store_with_filter(rr3->rc, &rawir); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci dev_dbg(dev, "calling ir_raw_event_handle\n"); 3908c2ecf20Sopenharmony_ci ir_raw_event_handle(rr3->rc); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* Util fn to send rr3 cmds */ 3948c2ecf20Sopenharmony_cistatic int redrat3_send_cmd(int cmd, struct redrat3_dev *rr3) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct usb_device *udev; 3978c2ecf20Sopenharmony_ci u8 *data; 3988c2ecf20Sopenharmony_ci int res; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci data = kzalloc(sizeof(u8), GFP_KERNEL); 4018c2ecf20Sopenharmony_ci if (!data) 4028c2ecf20Sopenharmony_ci return -ENOMEM; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci udev = rr3->udev; 4058c2ecf20Sopenharmony_ci res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), cmd, 4068c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 4078c2ecf20Sopenharmony_ci 0x0000, 0x0000, data, sizeof(u8), 10000); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (res < 0) { 4108c2ecf20Sopenharmony_ci dev_err(rr3->dev, "%s: Error sending rr3 cmd res %d, data %d", 4118c2ecf20Sopenharmony_ci __func__, res, *data); 4128c2ecf20Sopenharmony_ci res = -EIO; 4138c2ecf20Sopenharmony_ci } else 4148c2ecf20Sopenharmony_ci res = data[0]; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci kfree(data); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return res; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* Enables the long range detector and starts async receive */ 4228c2ecf20Sopenharmony_cistatic int redrat3_enable_detector(struct redrat3_dev *rr3) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct device *dev = rr3->dev; 4258c2ecf20Sopenharmony_ci u8 ret; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci ret = redrat3_send_cmd(RR3_RC_DET_ENABLE, rr3); 4288c2ecf20Sopenharmony_ci if (ret != 0) 4298c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: unexpected ret of %d\n", 4308c2ecf20Sopenharmony_ci __func__, ret); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci ret = redrat3_send_cmd(RR3_RC_DET_STATUS, rr3); 4338c2ecf20Sopenharmony_ci if (ret != 1) { 4348c2ecf20Sopenharmony_ci dev_err(dev, "%s: detector status: %d, should be 1\n", 4358c2ecf20Sopenharmony_ci __func__, ret); 4368c2ecf20Sopenharmony_ci return -EIO; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ret = usb_submit_urb(rr3->narrow_urb, GFP_KERNEL); 4408c2ecf20Sopenharmony_ci if (ret) { 4418c2ecf20Sopenharmony_ci dev_err(rr3->dev, "narrow band urb failed: %d", ret); 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci ret = usb_submit_urb(rr3->wide_urb, GFP_KERNEL); 4468c2ecf20Sopenharmony_ci if (ret) 4478c2ecf20Sopenharmony_ci dev_err(rr3->dev, "wide band urb failed: %d", ret); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return ret; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic inline void redrat3_delete(struct redrat3_dev *rr3, 4538c2ecf20Sopenharmony_ci struct usb_device *udev) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci usb_kill_urb(rr3->narrow_urb); 4568c2ecf20Sopenharmony_ci usb_kill_urb(rr3->wide_urb); 4578c2ecf20Sopenharmony_ci usb_kill_urb(rr3->flash_urb); 4588c2ecf20Sopenharmony_ci usb_kill_urb(rr3->learn_urb); 4598c2ecf20Sopenharmony_ci usb_free_urb(rr3->narrow_urb); 4608c2ecf20Sopenharmony_ci usb_free_urb(rr3->wide_urb); 4618c2ecf20Sopenharmony_ci usb_free_urb(rr3->flash_urb); 4628c2ecf20Sopenharmony_ci usb_free_urb(rr3->learn_urb); 4638c2ecf20Sopenharmony_ci usb_free_coherent(udev, le16_to_cpu(rr3->ep_narrow->wMaxPacketSize), 4648c2ecf20Sopenharmony_ci rr3->bulk_in_buf, rr3->dma_in); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci kfree(rr3); 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic u32 redrat3_get_timeout(struct redrat3_dev *rr3) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci __be32 *tmp; 4728c2ecf20Sopenharmony_ci u32 timeout = MS_TO_US(150); /* a sane default, if things go haywire */ 4738c2ecf20Sopenharmony_ci int len, ret, pipe; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci len = sizeof(*tmp); 4768c2ecf20Sopenharmony_ci tmp = kzalloc(len, GFP_KERNEL); 4778c2ecf20Sopenharmony_ci if (!tmp) 4788c2ecf20Sopenharmony_ci return timeout; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci pipe = usb_rcvctrlpipe(rr3->udev, 0); 4818c2ecf20Sopenharmony_ci ret = usb_control_msg(rr3->udev, pipe, RR3_GET_IR_PARAM, 4828c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 4838c2ecf20Sopenharmony_ci RR3_IR_IO_SIG_TIMEOUT, 0, tmp, len, 5000); 4848c2ecf20Sopenharmony_ci if (ret != len) 4858c2ecf20Sopenharmony_ci dev_warn(rr3->dev, "Failed to read timeout from hardware\n"); 4868c2ecf20Sopenharmony_ci else { 4878c2ecf20Sopenharmony_ci timeout = redrat3_len_to_us(be32_to_cpup(tmp)); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci dev_dbg(rr3->dev, "Got timeout of %d ms\n", timeout / 1000); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci kfree(tmp); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return timeout; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic int redrat3_set_timeout(struct rc_dev *rc_dev, unsigned int timeoutus) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct redrat3_dev *rr3 = rc_dev->priv; 5008c2ecf20Sopenharmony_ci struct usb_device *udev = rr3->udev; 5018c2ecf20Sopenharmony_ci struct device *dev = rr3->dev; 5028c2ecf20Sopenharmony_ci __be32 *timeout; 5038c2ecf20Sopenharmony_ci int ret; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci timeout = kmalloc(sizeof(*timeout), GFP_KERNEL); 5068c2ecf20Sopenharmony_ci if (!timeout) 5078c2ecf20Sopenharmony_ci return -ENOMEM; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci *timeout = cpu_to_be32(redrat3_us_to_len(timeoutus)); 5108c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), RR3_SET_IR_PARAM, 5118c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 5128c2ecf20Sopenharmony_ci RR3_IR_IO_SIG_TIMEOUT, 0, timeout, sizeof(*timeout), 5138c2ecf20Sopenharmony_ci 25000); 5148c2ecf20Sopenharmony_ci dev_dbg(dev, "set ir parm timeout %d ret 0x%02x\n", 5158c2ecf20Sopenharmony_ci be32_to_cpu(*timeout), ret); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (ret == sizeof(*timeout)) 5188c2ecf20Sopenharmony_ci ret = 0; 5198c2ecf20Sopenharmony_ci else if (ret >= 0) 5208c2ecf20Sopenharmony_ci ret = -EIO; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci kfree(timeout); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return ret; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void redrat3_reset(struct redrat3_dev *rr3) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct usb_device *udev = rr3->udev; 5308c2ecf20Sopenharmony_ci struct device *dev = rr3->dev; 5318c2ecf20Sopenharmony_ci int rc, rxpipe, txpipe; 5328c2ecf20Sopenharmony_ci u8 *val; 5338c2ecf20Sopenharmony_ci size_t const len = sizeof(*val); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci rxpipe = usb_rcvctrlpipe(udev, 0); 5368c2ecf20Sopenharmony_ci txpipe = usb_sndctrlpipe(udev, 0); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci val = kmalloc(len, GFP_KERNEL); 5398c2ecf20Sopenharmony_ci if (!val) 5408c2ecf20Sopenharmony_ci return; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci *val = 0x01; 5438c2ecf20Sopenharmony_ci rc = usb_control_msg(udev, rxpipe, RR3_RESET, 5448c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 5458c2ecf20Sopenharmony_ci RR3_CPUCS_REG_ADDR, 0, val, len, 25000); 5468c2ecf20Sopenharmony_ci dev_dbg(dev, "reset returned 0x%02x\n", rc); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci *val = length_fuzz; 5498c2ecf20Sopenharmony_ci rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, 5508c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 5518c2ecf20Sopenharmony_ci RR3_IR_IO_LENGTH_FUZZ, 0, val, len, 25000); 5528c2ecf20Sopenharmony_ci dev_dbg(dev, "set ir parm len fuzz %d rc 0x%02x\n", *val, rc); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci *val = (65536 - (minimum_pause * 2000)) / 256; 5558c2ecf20Sopenharmony_ci rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, 5568c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 5578c2ecf20Sopenharmony_ci RR3_IR_IO_MIN_PAUSE, 0, val, len, 25000); 5588c2ecf20Sopenharmony_ci dev_dbg(dev, "set ir parm min pause %d rc 0x%02x\n", *val, rc); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci *val = periods_measure_carrier; 5618c2ecf20Sopenharmony_ci rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, 5628c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 5638c2ecf20Sopenharmony_ci RR3_IR_IO_PERIODS_MF, 0, val, len, 25000); 5648c2ecf20Sopenharmony_ci dev_dbg(dev, "set ir parm periods measure carrier %d rc 0x%02x", *val, 5658c2ecf20Sopenharmony_ci rc); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci *val = RR3_DRIVER_MAXLENS; 5688c2ecf20Sopenharmony_ci rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, 5698c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 5708c2ecf20Sopenharmony_ci RR3_IR_IO_MAX_LENGTHS, 0, val, len, 25000); 5718c2ecf20Sopenharmony_ci dev_dbg(dev, "set ir parm max lens %d rc 0x%02x\n", *val, rc); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci kfree(val); 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic void redrat3_get_firmware_rev(struct redrat3_dev *rr3) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci int rc; 5798c2ecf20Sopenharmony_ci char *buffer; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci buffer = kcalloc(RR3_FW_VERSION_LEN + 1, sizeof(*buffer), GFP_KERNEL); 5828c2ecf20Sopenharmony_ci if (!buffer) 5838c2ecf20Sopenharmony_ci return; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci rc = usb_control_msg(rr3->udev, usb_rcvctrlpipe(rr3->udev, 0), 5868c2ecf20Sopenharmony_ci RR3_FW_VERSION, 5878c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 5888c2ecf20Sopenharmony_ci 0, 0, buffer, RR3_FW_VERSION_LEN, 5000); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (rc >= 0) 5918c2ecf20Sopenharmony_ci dev_info(rr3->dev, "Firmware rev: %s", buffer); 5928c2ecf20Sopenharmony_ci else 5938c2ecf20Sopenharmony_ci dev_err(rr3->dev, "Problem fetching firmware ID\n"); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci kfree(buffer); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic void redrat3_read_packet_start(struct redrat3_dev *rr3, unsigned len) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct redrat3_header *header = rr3->bulk_in_buf; 6018c2ecf20Sopenharmony_ci unsigned pktlen, pkttype; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* grab the Length and type of transfer */ 6048c2ecf20Sopenharmony_ci pktlen = be16_to_cpu(header->length); 6058c2ecf20Sopenharmony_ci pkttype = be16_to_cpu(header->transfer_type); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (pktlen > sizeof(rr3->irdata)) { 6088c2ecf20Sopenharmony_ci dev_warn(rr3->dev, "packet length %u too large\n", pktlen); 6098c2ecf20Sopenharmony_ci return; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci switch (pkttype) { 6138c2ecf20Sopenharmony_ci case RR3_ERROR: 6148c2ecf20Sopenharmony_ci if (len >= sizeof(struct redrat3_error)) { 6158c2ecf20Sopenharmony_ci struct redrat3_error *error = rr3->bulk_in_buf; 6168c2ecf20Sopenharmony_ci unsigned fw_error = be16_to_cpu(error->fw_error); 6178c2ecf20Sopenharmony_ci redrat3_dump_fw_error(rr3, fw_error); 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci break; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci case RR3_MOD_SIGNAL_IN: 6228c2ecf20Sopenharmony_ci memcpy(&rr3->irdata, rr3->bulk_in_buf, len); 6238c2ecf20Sopenharmony_ci rr3->bytes_read = len; 6248c2ecf20Sopenharmony_ci dev_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", 6258c2ecf20Sopenharmony_ci rr3->bytes_read, pktlen); 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci default: 6298c2ecf20Sopenharmony_ci dev_dbg(rr3->dev, "ignoring packet with type 0x%02x, len of %d, 0x%02x\n", 6308c2ecf20Sopenharmony_ci pkttype, len, pktlen); 6318c2ecf20Sopenharmony_ci break; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic void redrat3_read_packet_continue(struct redrat3_dev *rr3, unsigned len) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci void *irdata = &rr3->irdata; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (len + rr3->bytes_read > sizeof(rr3->irdata)) { 6408c2ecf20Sopenharmony_ci dev_warn(rr3->dev, "too much data for packet\n"); 6418c2ecf20Sopenharmony_ci rr3->bytes_read = 0; 6428c2ecf20Sopenharmony_ci return; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci memcpy(irdata + rr3->bytes_read, rr3->bulk_in_buf, len); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci rr3->bytes_read += len; 6488c2ecf20Sopenharmony_ci dev_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", rr3->bytes_read, 6498c2ecf20Sopenharmony_ci be16_to_cpu(rr3->irdata.header.length)); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci/* gather IR data from incoming urb, process it when we have enough */ 6538c2ecf20Sopenharmony_cistatic int redrat3_get_ir_data(struct redrat3_dev *rr3, unsigned len) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct device *dev = rr3->dev; 6568c2ecf20Sopenharmony_ci unsigned pkttype; 6578c2ecf20Sopenharmony_ci int ret = 0; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (rr3->bytes_read == 0 && len >= sizeof(struct redrat3_header)) { 6608c2ecf20Sopenharmony_ci redrat3_read_packet_start(rr3, len); 6618c2ecf20Sopenharmony_ci } else if (rr3->bytes_read != 0) { 6628c2ecf20Sopenharmony_ci redrat3_read_packet_continue(rr3, len); 6638c2ecf20Sopenharmony_ci } else if (rr3->bytes_read == 0) { 6648c2ecf20Sopenharmony_ci dev_err(dev, "error: no packet data read\n"); 6658c2ecf20Sopenharmony_ci ret = -ENODATA; 6668c2ecf20Sopenharmony_ci goto out; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (rr3->bytes_read < be16_to_cpu(rr3->irdata.header.length) + 6708c2ecf20Sopenharmony_ci sizeof(struct redrat3_header)) 6718c2ecf20Sopenharmony_ci /* we're still accumulating data */ 6728c2ecf20Sopenharmony_ci return 0; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* if we get here, we've got IR data to decode */ 6758c2ecf20Sopenharmony_ci pkttype = be16_to_cpu(rr3->irdata.header.transfer_type); 6768c2ecf20Sopenharmony_ci if (pkttype == RR3_MOD_SIGNAL_IN) 6778c2ecf20Sopenharmony_ci redrat3_process_ir_data(rr3); 6788c2ecf20Sopenharmony_ci else 6798c2ecf20Sopenharmony_ci dev_dbg(dev, "discarding non-signal data packet (type 0x%02x)\n", 6808c2ecf20Sopenharmony_ci pkttype); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ciout: 6838c2ecf20Sopenharmony_ci rr3->bytes_read = 0; 6848c2ecf20Sopenharmony_ci return ret; 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci/* callback function from USB when async USB request has completed */ 6888c2ecf20Sopenharmony_cistatic void redrat3_handle_async(struct urb *urb) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci struct redrat3_dev *rr3 = urb->context; 6918c2ecf20Sopenharmony_ci int ret; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci switch (urb->status) { 6948c2ecf20Sopenharmony_ci case 0: 6958c2ecf20Sopenharmony_ci ret = redrat3_get_ir_data(rr3, urb->actual_length); 6968c2ecf20Sopenharmony_ci if (!ret && rr3->wideband && !rr3->learn_urb->hcpriv) { 6978c2ecf20Sopenharmony_ci ret = usb_submit_urb(rr3->learn_urb, GFP_ATOMIC); 6988c2ecf20Sopenharmony_ci if (ret) 6998c2ecf20Sopenharmony_ci dev_err(rr3->dev, "Failed to submit learning urb: %d", 7008c2ecf20Sopenharmony_ci ret); 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (!ret) { 7048c2ecf20Sopenharmony_ci /* no error, prepare to read more */ 7058c2ecf20Sopenharmony_ci ret = usb_submit_urb(urb, GFP_ATOMIC); 7068c2ecf20Sopenharmony_ci if (ret) 7078c2ecf20Sopenharmony_ci dev_err(rr3->dev, "Failed to resubmit urb: %d", 7088c2ecf20Sopenharmony_ci ret); 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci break; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci case -ECONNRESET: 7138c2ecf20Sopenharmony_ci case -ENOENT: 7148c2ecf20Sopenharmony_ci case -ESHUTDOWN: 7158c2ecf20Sopenharmony_ci usb_unlink_urb(urb); 7168c2ecf20Sopenharmony_ci return; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci case -EPIPE: 7198c2ecf20Sopenharmony_ci default: 7208c2ecf20Sopenharmony_ci dev_warn(rr3->dev, "Error: urb status = %d\n", urb->status); 7218c2ecf20Sopenharmony_ci rr3->bytes_read = 0; 7228c2ecf20Sopenharmony_ci break; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic u16 mod_freq_to_val(unsigned int mod_freq) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci int mult = 6000000; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* Clk used in mod. freq. generation is CLK24/4. */ 7318c2ecf20Sopenharmony_ci return 65536 - (mult / mod_freq); 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cistatic int redrat3_set_tx_carrier(struct rc_dev *rcdev, u32 carrier) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct redrat3_dev *rr3 = rcdev->priv; 7378c2ecf20Sopenharmony_ci struct device *dev = rr3->dev; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci dev_dbg(dev, "Setting modulation frequency to %u", carrier); 7408c2ecf20Sopenharmony_ci if (carrier == 0) 7418c2ecf20Sopenharmony_ci return -EINVAL; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci rr3->carrier = carrier; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci return 0; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, 7498c2ecf20Sopenharmony_ci unsigned count) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci struct redrat3_dev *rr3 = rcdev->priv; 7528c2ecf20Sopenharmony_ci struct device *dev = rr3->dev; 7538c2ecf20Sopenharmony_ci struct redrat3_irdata *irdata = NULL; 7548c2ecf20Sopenharmony_ci int ret, ret_len; 7558c2ecf20Sopenharmony_ci int lencheck, cur_sample_len, pipe; 7568c2ecf20Sopenharmony_ci int *sample_lens = NULL; 7578c2ecf20Sopenharmony_ci u8 curlencheck = 0; 7588c2ecf20Sopenharmony_ci unsigned i, sendbuf_len; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (rr3->transmitting) { 7618c2ecf20Sopenharmony_ci dev_warn(dev, "%s: transmitter already in use\n", __func__); 7628c2ecf20Sopenharmony_ci return -EAGAIN; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci if (count > RR3_MAX_SIG_SIZE - RR3_TX_TRAILER_LEN) 7668c2ecf20Sopenharmony_ci return -EINVAL; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci /* rr3 will disable rc detector on transmit */ 7698c2ecf20Sopenharmony_ci rr3->transmitting = true; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci sample_lens = kcalloc(RR3_DRIVER_MAXLENS, 7728c2ecf20Sopenharmony_ci sizeof(*sample_lens), 7738c2ecf20Sopenharmony_ci GFP_KERNEL); 7748c2ecf20Sopenharmony_ci if (!sample_lens) 7758c2ecf20Sopenharmony_ci return -ENOMEM; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci irdata = kzalloc(sizeof(*irdata), GFP_KERNEL); 7788c2ecf20Sopenharmony_ci if (!irdata) { 7798c2ecf20Sopenharmony_ci ret = -ENOMEM; 7808c2ecf20Sopenharmony_ci goto out; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 7848c2ecf20Sopenharmony_ci cur_sample_len = redrat3_us_to_len(txbuf[i]); 7858c2ecf20Sopenharmony_ci if (cur_sample_len > 0xffff) { 7868c2ecf20Sopenharmony_ci dev_warn(dev, "transmit period of %uus truncated to %uus\n", 7878c2ecf20Sopenharmony_ci txbuf[i], redrat3_len_to_us(0xffff)); 7888c2ecf20Sopenharmony_ci cur_sample_len = 0xffff; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci for (lencheck = 0; lencheck < curlencheck; lencheck++) { 7918c2ecf20Sopenharmony_ci if (sample_lens[lencheck] == cur_sample_len) 7928c2ecf20Sopenharmony_ci break; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci if (lencheck == curlencheck) { 7958c2ecf20Sopenharmony_ci dev_dbg(dev, "txbuf[%d]=%u, pos %d, enc %u\n", 7968c2ecf20Sopenharmony_ci i, txbuf[i], curlencheck, cur_sample_len); 7978c2ecf20Sopenharmony_ci if (curlencheck < RR3_DRIVER_MAXLENS) { 7988c2ecf20Sopenharmony_ci /* now convert the value to a proper 7998c2ecf20Sopenharmony_ci * rr3 value.. */ 8008c2ecf20Sopenharmony_ci sample_lens[curlencheck] = cur_sample_len; 8018c2ecf20Sopenharmony_ci put_unaligned_be16(cur_sample_len, 8028c2ecf20Sopenharmony_ci &irdata->lens[curlencheck]); 8038c2ecf20Sopenharmony_ci curlencheck++; 8048c2ecf20Sopenharmony_ci } else { 8058c2ecf20Sopenharmony_ci ret = -EINVAL; 8068c2ecf20Sopenharmony_ci goto out; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci irdata->sigdata[i] = lencheck; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci irdata->sigdata[count] = RR3_END_OF_SIGNAL; 8138c2ecf20Sopenharmony_ci irdata->sigdata[count + 1] = RR3_END_OF_SIGNAL; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci sendbuf_len = offsetof(struct redrat3_irdata, 8168c2ecf20Sopenharmony_ci sigdata[count + RR3_TX_TRAILER_LEN]); 8178c2ecf20Sopenharmony_ci /* fill in our packet header */ 8188c2ecf20Sopenharmony_ci irdata->header.length = cpu_to_be16(sendbuf_len - 8198c2ecf20Sopenharmony_ci sizeof(struct redrat3_header)); 8208c2ecf20Sopenharmony_ci irdata->header.transfer_type = cpu_to_be16(RR3_MOD_SIGNAL_OUT); 8218c2ecf20Sopenharmony_ci irdata->pause = cpu_to_be32(redrat3_len_to_us(100)); 8228c2ecf20Sopenharmony_ci irdata->mod_freq_count = cpu_to_be16(mod_freq_to_val(rr3->carrier)); 8238c2ecf20Sopenharmony_ci irdata->no_lengths = curlencheck; 8248c2ecf20Sopenharmony_ci irdata->sig_size = cpu_to_be16(count + RR3_TX_TRAILER_LEN); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci pipe = usb_sndbulkpipe(rr3->udev, rr3->ep_out->bEndpointAddress); 8278c2ecf20Sopenharmony_ci ret = usb_bulk_msg(rr3->udev, pipe, irdata, 8288c2ecf20Sopenharmony_ci sendbuf_len, &ret_len, 10000); 8298c2ecf20Sopenharmony_ci dev_dbg(dev, "sent %d bytes, (ret %d)\n", ret_len, ret); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci /* now tell the hardware to transmit what we sent it */ 8328c2ecf20Sopenharmony_ci pipe = usb_rcvctrlpipe(rr3->udev, 0); 8338c2ecf20Sopenharmony_ci ret = usb_control_msg(rr3->udev, pipe, RR3_TX_SEND_SIGNAL, 8348c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 8358c2ecf20Sopenharmony_ci 0, 0, irdata, 2, 10000); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (ret < 0) 8388c2ecf20Sopenharmony_ci dev_err(dev, "Error: control msg send failed, rc %d\n", ret); 8398c2ecf20Sopenharmony_ci else 8408c2ecf20Sopenharmony_ci ret = count; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ciout: 8438c2ecf20Sopenharmony_ci kfree(irdata); 8448c2ecf20Sopenharmony_ci kfree(sample_lens); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci rr3->transmitting = false; 8478c2ecf20Sopenharmony_ci /* rr3 re-enables rc detector because it was enabled before */ 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return ret; 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic void redrat3_brightness_set(struct led_classdev *led_dev, enum 8538c2ecf20Sopenharmony_ci led_brightness brightness) 8548c2ecf20Sopenharmony_ci{ 8558c2ecf20Sopenharmony_ci struct redrat3_dev *rr3 = container_of(led_dev, struct redrat3_dev, 8568c2ecf20Sopenharmony_ci led); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (brightness != LED_OFF && atomic_cmpxchg(&rr3->flash, 0, 1) == 0) { 8598c2ecf20Sopenharmony_ci int ret = usb_submit_urb(rr3->flash_urb, GFP_ATOMIC); 8608c2ecf20Sopenharmony_ci if (ret != 0) { 8618c2ecf20Sopenharmony_ci dev_dbg(rr3->dev, "%s: unexpected ret of %d\n", 8628c2ecf20Sopenharmony_ci __func__, ret); 8638c2ecf20Sopenharmony_ci atomic_set(&rr3->flash, 0); 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic int redrat3_wideband_receiver(struct rc_dev *rcdev, int enable) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct redrat3_dev *rr3 = rcdev->priv; 8718c2ecf20Sopenharmony_ci int ret = 0; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci rr3->wideband = enable != 0; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (enable) { 8768c2ecf20Sopenharmony_ci ret = usb_submit_urb(rr3->learn_urb, GFP_KERNEL); 8778c2ecf20Sopenharmony_ci if (ret) 8788c2ecf20Sopenharmony_ci dev_err(rr3->dev, "Failed to submit learning urb: %d", 8798c2ecf20Sopenharmony_ci ret); 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci return ret; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic void redrat3_learn_complete(struct urb *urb) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci struct redrat3_dev *rr3 = urb->context; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci switch (urb->status) { 8908c2ecf20Sopenharmony_ci case 0: 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci case -ECONNRESET: 8938c2ecf20Sopenharmony_ci case -ENOENT: 8948c2ecf20Sopenharmony_ci case -ESHUTDOWN: 8958c2ecf20Sopenharmony_ci usb_unlink_urb(urb); 8968c2ecf20Sopenharmony_ci return; 8978c2ecf20Sopenharmony_ci case -EPIPE: 8988c2ecf20Sopenharmony_ci default: 8998c2ecf20Sopenharmony_ci dev_err(rr3->dev, "Error: learn urb status = %d", urb->status); 9008c2ecf20Sopenharmony_ci break; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic void redrat3_led_complete(struct urb *urb) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci struct redrat3_dev *rr3 = urb->context; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci switch (urb->status) { 9098c2ecf20Sopenharmony_ci case 0: 9108c2ecf20Sopenharmony_ci break; 9118c2ecf20Sopenharmony_ci case -ECONNRESET: 9128c2ecf20Sopenharmony_ci case -ENOENT: 9138c2ecf20Sopenharmony_ci case -ESHUTDOWN: 9148c2ecf20Sopenharmony_ci usb_unlink_urb(urb); 9158c2ecf20Sopenharmony_ci return; 9168c2ecf20Sopenharmony_ci case -EPIPE: 9178c2ecf20Sopenharmony_ci default: 9188c2ecf20Sopenharmony_ci dev_dbg(rr3->dev, "Error: urb status = %d\n", urb->status); 9198c2ecf20Sopenharmony_ci break; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci rr3->led.brightness = LED_OFF; 9238c2ecf20Sopenharmony_ci atomic_dec(&rr3->flash); 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci struct device *dev = rr3->dev; 9298c2ecf20Sopenharmony_ci struct rc_dev *rc; 9308c2ecf20Sopenharmony_ci int ret; 9318c2ecf20Sopenharmony_ci u16 prod = le16_to_cpu(rr3->udev->descriptor.idProduct); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci rc = rc_allocate_device(RC_DRIVER_IR_RAW); 9348c2ecf20Sopenharmony_ci if (!rc) 9358c2ecf20Sopenharmony_ci return NULL; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci snprintf(rr3->name, sizeof(rr3->name), 9388c2ecf20Sopenharmony_ci "RedRat3%s Infrared Remote Transceiver", 9398c2ecf20Sopenharmony_ci prod == USB_RR3IIUSB_PRODUCT_ID ? "-II" : ""); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci usb_make_path(rr3->udev, rr3->phys, sizeof(rr3->phys)); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci rc->device_name = rr3->name; 9448c2ecf20Sopenharmony_ci rc->input_phys = rr3->phys; 9458c2ecf20Sopenharmony_ci usb_to_input_id(rr3->udev, &rc->input_id); 9468c2ecf20Sopenharmony_ci rc->dev.parent = dev; 9478c2ecf20Sopenharmony_ci rc->priv = rr3; 9488c2ecf20Sopenharmony_ci rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; 9498c2ecf20Sopenharmony_ci rc->min_timeout = MS_TO_US(RR3_RX_MIN_TIMEOUT); 9508c2ecf20Sopenharmony_ci rc->max_timeout = MS_TO_US(RR3_RX_MAX_TIMEOUT); 9518c2ecf20Sopenharmony_ci rc->timeout = redrat3_get_timeout(rr3); 9528c2ecf20Sopenharmony_ci rc->s_timeout = redrat3_set_timeout; 9538c2ecf20Sopenharmony_ci rc->tx_ir = redrat3_transmit_ir; 9548c2ecf20Sopenharmony_ci rc->s_tx_carrier = redrat3_set_tx_carrier; 9558c2ecf20Sopenharmony_ci rc->s_carrier_report = redrat3_wideband_receiver; 9568c2ecf20Sopenharmony_ci rc->driver_name = DRIVER_NAME; 9578c2ecf20Sopenharmony_ci rc->rx_resolution = 2; 9588c2ecf20Sopenharmony_ci rc->map_name = RC_MAP_HAUPPAUGE; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci ret = rc_register_device(rc); 9618c2ecf20Sopenharmony_ci if (ret < 0) { 9628c2ecf20Sopenharmony_ci dev_err(dev, "remote dev registration failed\n"); 9638c2ecf20Sopenharmony_ci goto out; 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci return rc; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ciout: 9698c2ecf20Sopenharmony_ci rc_free_device(rc); 9708c2ecf20Sopenharmony_ci return NULL; 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_cistatic int redrat3_dev_probe(struct usb_interface *intf, 9748c2ecf20Sopenharmony_ci const struct usb_device_id *id) 9758c2ecf20Sopenharmony_ci{ 9768c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 9778c2ecf20Sopenharmony_ci struct device *dev = &intf->dev; 9788c2ecf20Sopenharmony_ci struct usb_host_interface *uhi; 9798c2ecf20Sopenharmony_ci struct redrat3_dev *rr3; 9808c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ep; 9818c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ep_narrow = NULL; 9828c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ep_wide = NULL; 9838c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ep_out = NULL; 9848c2ecf20Sopenharmony_ci u8 addr, attrs; 9858c2ecf20Sopenharmony_ci int pipe, i; 9868c2ecf20Sopenharmony_ci int retval = -ENOMEM; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci uhi = intf->cur_altsetting; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci /* find our bulk-in and bulk-out endpoints */ 9918c2ecf20Sopenharmony_ci for (i = 0; i < uhi->desc.bNumEndpoints; ++i) { 9928c2ecf20Sopenharmony_ci ep = &uhi->endpoint[i].desc; 9938c2ecf20Sopenharmony_ci addr = ep->bEndpointAddress; 9948c2ecf20Sopenharmony_ci attrs = ep->bmAttributes; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci if (((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) && 9978c2ecf20Sopenharmony_ci ((attrs & USB_ENDPOINT_XFERTYPE_MASK) == 9988c2ecf20Sopenharmony_ci USB_ENDPOINT_XFER_BULK)) { 9998c2ecf20Sopenharmony_ci dev_dbg(dev, "found bulk-in endpoint at 0x%02x\n", 10008c2ecf20Sopenharmony_ci ep->bEndpointAddress); 10018c2ecf20Sopenharmony_ci /* data comes in on 0x82, 0x81 is for learning */ 10028c2ecf20Sopenharmony_ci if (ep->bEndpointAddress == RR3_NARROW_IN_EP_ADDR) 10038c2ecf20Sopenharmony_ci ep_narrow = ep; 10048c2ecf20Sopenharmony_ci if (ep->bEndpointAddress == RR3_WIDE_IN_EP_ADDR) 10058c2ecf20Sopenharmony_ci ep_wide = ep; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci if ((ep_out == NULL) && 10098c2ecf20Sopenharmony_ci ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && 10108c2ecf20Sopenharmony_ci ((attrs & USB_ENDPOINT_XFERTYPE_MASK) == 10118c2ecf20Sopenharmony_ci USB_ENDPOINT_XFER_BULK)) { 10128c2ecf20Sopenharmony_ci dev_dbg(dev, "found bulk-out endpoint at 0x%02x\n", 10138c2ecf20Sopenharmony_ci ep->bEndpointAddress); 10148c2ecf20Sopenharmony_ci ep_out = ep; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (!ep_narrow || !ep_out || !ep_wide) { 10198c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't find all endpoints\n"); 10208c2ecf20Sopenharmony_ci retval = -ENODEV; 10218c2ecf20Sopenharmony_ci goto no_endpoints; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* allocate memory for our device state and initialize it */ 10258c2ecf20Sopenharmony_ci rr3 = kzalloc(sizeof(*rr3), GFP_KERNEL); 10268c2ecf20Sopenharmony_ci if (!rr3) 10278c2ecf20Sopenharmony_ci goto no_endpoints; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci rr3->dev = &intf->dev; 10308c2ecf20Sopenharmony_ci rr3->ep_narrow = ep_narrow; 10318c2ecf20Sopenharmony_ci rr3->ep_out = ep_out; 10328c2ecf20Sopenharmony_ci rr3->udev = udev; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci /* set up bulk-in endpoint */ 10358c2ecf20Sopenharmony_ci rr3->narrow_urb = usb_alloc_urb(0, GFP_KERNEL); 10368c2ecf20Sopenharmony_ci if (!rr3->narrow_urb) 10378c2ecf20Sopenharmony_ci goto redrat_free; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci rr3->wide_urb = usb_alloc_urb(0, GFP_KERNEL); 10408c2ecf20Sopenharmony_ci if (!rr3->wide_urb) 10418c2ecf20Sopenharmony_ci goto redrat_free; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci rr3->bulk_in_buf = usb_alloc_coherent(udev, 10448c2ecf20Sopenharmony_ci le16_to_cpu(ep_narrow->wMaxPacketSize), 10458c2ecf20Sopenharmony_ci GFP_KERNEL, &rr3->dma_in); 10468c2ecf20Sopenharmony_ci if (!rr3->bulk_in_buf) 10478c2ecf20Sopenharmony_ci goto redrat_free; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci pipe = usb_rcvbulkpipe(udev, ep_narrow->bEndpointAddress); 10508c2ecf20Sopenharmony_ci usb_fill_bulk_urb(rr3->narrow_urb, udev, pipe, rr3->bulk_in_buf, 10518c2ecf20Sopenharmony_ci le16_to_cpu(ep_narrow->wMaxPacketSize), 10528c2ecf20Sopenharmony_ci redrat3_handle_async, rr3); 10538c2ecf20Sopenharmony_ci rr3->narrow_urb->transfer_dma = rr3->dma_in; 10548c2ecf20Sopenharmony_ci rr3->narrow_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci pipe = usb_rcvbulkpipe(udev, ep_wide->bEndpointAddress); 10578c2ecf20Sopenharmony_ci usb_fill_bulk_urb(rr3->wide_urb, udev, pipe, rr3->bulk_in_buf, 10588c2ecf20Sopenharmony_ci le16_to_cpu(ep_narrow->wMaxPacketSize), 10598c2ecf20Sopenharmony_ci redrat3_handle_async, rr3); 10608c2ecf20Sopenharmony_ci rr3->wide_urb->transfer_dma = rr3->dma_in; 10618c2ecf20Sopenharmony_ci rr3->wide_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci redrat3_reset(rr3); 10648c2ecf20Sopenharmony_ci redrat3_get_firmware_rev(rr3); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* default.. will get overridden by any sends with a freq defined */ 10678c2ecf20Sopenharmony_ci rr3->carrier = 38000; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci atomic_set(&rr3->flash, 0); 10708c2ecf20Sopenharmony_ci rr3->flash_urb = usb_alloc_urb(0, GFP_KERNEL); 10718c2ecf20Sopenharmony_ci if (!rr3->flash_urb) 10728c2ecf20Sopenharmony_ci goto redrat_free; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci /* learn urb */ 10758c2ecf20Sopenharmony_ci rr3->learn_urb = usb_alloc_urb(0, GFP_KERNEL); 10768c2ecf20Sopenharmony_ci if (!rr3->learn_urb) 10778c2ecf20Sopenharmony_ci goto redrat_free; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* setup packet is 'c0 b2 0000 0000 0001' */ 10808c2ecf20Sopenharmony_ci rr3->learn_control.bRequestType = 0xc0; 10818c2ecf20Sopenharmony_ci rr3->learn_control.bRequest = RR3_MODSIG_CAPTURE; 10828c2ecf20Sopenharmony_ci rr3->learn_control.wLength = cpu_to_le16(1); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci usb_fill_control_urb(rr3->learn_urb, udev, usb_rcvctrlpipe(udev, 0), 10858c2ecf20Sopenharmony_ci (unsigned char *)&rr3->learn_control, 10868c2ecf20Sopenharmony_ci &rr3->learn_buf, sizeof(rr3->learn_buf), 10878c2ecf20Sopenharmony_ci redrat3_learn_complete, rr3); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* setup packet is 'c0 b9 0000 0000 0001' */ 10908c2ecf20Sopenharmony_ci rr3->flash_control.bRequestType = 0xc0; 10918c2ecf20Sopenharmony_ci rr3->flash_control.bRequest = RR3_BLINK_LED; 10928c2ecf20Sopenharmony_ci rr3->flash_control.wLength = cpu_to_le16(1); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci usb_fill_control_urb(rr3->flash_urb, udev, usb_rcvctrlpipe(udev, 0), 10958c2ecf20Sopenharmony_ci (unsigned char *)&rr3->flash_control, 10968c2ecf20Sopenharmony_ci &rr3->flash_in_buf, sizeof(rr3->flash_in_buf), 10978c2ecf20Sopenharmony_ci redrat3_led_complete, rr3); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci /* led control */ 11008c2ecf20Sopenharmony_ci rr3->led.name = "redrat3:red:feedback"; 11018c2ecf20Sopenharmony_ci rr3->led.default_trigger = "rc-feedback"; 11028c2ecf20Sopenharmony_ci rr3->led.brightness_set = redrat3_brightness_set; 11038c2ecf20Sopenharmony_ci retval = led_classdev_register(&intf->dev, &rr3->led); 11048c2ecf20Sopenharmony_ci if (retval) 11058c2ecf20Sopenharmony_ci goto redrat_free; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci rr3->rc = redrat3_init_rc_dev(rr3); 11088c2ecf20Sopenharmony_ci if (!rr3->rc) { 11098c2ecf20Sopenharmony_ci retval = -ENOMEM; 11108c2ecf20Sopenharmony_ci goto led_free; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci /* might be all we need to do? */ 11148c2ecf20Sopenharmony_ci retval = redrat3_enable_detector(rr3); 11158c2ecf20Sopenharmony_ci if (retval < 0) 11168c2ecf20Sopenharmony_ci goto led_free; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci /* we can register the device now, as it is ready */ 11198c2ecf20Sopenharmony_ci usb_set_intfdata(intf, rr3); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci return 0; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ciled_free: 11248c2ecf20Sopenharmony_ci led_classdev_unregister(&rr3->led); 11258c2ecf20Sopenharmony_ciredrat_free: 11268c2ecf20Sopenharmony_ci redrat3_delete(rr3, rr3->udev); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_cino_endpoints: 11298c2ecf20Sopenharmony_ci return retval; 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic void redrat3_dev_disconnect(struct usb_interface *intf) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 11358c2ecf20Sopenharmony_ci struct redrat3_dev *rr3 = usb_get_intfdata(intf); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 11388c2ecf20Sopenharmony_ci rc_unregister_device(rr3->rc); 11398c2ecf20Sopenharmony_ci led_classdev_unregister(&rr3->led); 11408c2ecf20Sopenharmony_ci redrat3_delete(rr3, udev); 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cistatic int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci struct redrat3_dev *rr3 = usb_get_intfdata(intf); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci led_classdev_suspend(&rr3->led); 11488c2ecf20Sopenharmony_ci usb_kill_urb(rr3->narrow_urb); 11498c2ecf20Sopenharmony_ci usb_kill_urb(rr3->wide_urb); 11508c2ecf20Sopenharmony_ci usb_kill_urb(rr3->flash_urb); 11518c2ecf20Sopenharmony_ci return 0; 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_cistatic int redrat3_dev_resume(struct usb_interface *intf) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci struct redrat3_dev *rr3 = usb_get_intfdata(intf); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (usb_submit_urb(rr3->narrow_urb, GFP_ATOMIC)) 11598c2ecf20Sopenharmony_ci return -EIO; 11608c2ecf20Sopenharmony_ci if (usb_submit_urb(rr3->wide_urb, GFP_ATOMIC)) 11618c2ecf20Sopenharmony_ci return -EIO; 11628c2ecf20Sopenharmony_ci led_classdev_resume(&rr3->led); 11638c2ecf20Sopenharmony_ci return 0; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic struct usb_driver redrat3_dev_driver = { 11678c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 11688c2ecf20Sopenharmony_ci .probe = redrat3_dev_probe, 11698c2ecf20Sopenharmony_ci .disconnect = redrat3_dev_disconnect, 11708c2ecf20Sopenharmony_ci .suspend = redrat3_dev_suspend, 11718c2ecf20Sopenharmony_ci .resume = redrat3_dev_resume, 11728c2ecf20Sopenharmony_ci .reset_resume = redrat3_dev_resume, 11738c2ecf20Sopenharmony_ci .id_table = redrat3_dev_table 11748c2ecf20Sopenharmony_ci}; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_cimodule_usb_driver(redrat3_dev_driver); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 11798c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 11808c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR2); 11818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 11828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, redrat3_dev_table); 1183