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