xref: /OK3568_Linux_fs/kernel/drivers/media/rc/iguanair.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * IguanaWorks USB IR Transceiver support
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2012 Sean Young <sean@mess.org>
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/device.h>
9*4882a593Smuzhiyun #include <linux/kernel.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/usb.h>
12*4882a593Smuzhiyun #include <linux/usb/input.h>
13*4882a593Smuzhiyun #include <linux/slab.h>
14*4882a593Smuzhiyun #include <linux/completion.h>
15*4882a593Smuzhiyun #include <media/rc-core.h>
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #define BUF_SIZE 152
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun struct iguanair {
20*4882a593Smuzhiyun 	struct rc_dev *rc;
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun 	struct device *dev;
23*4882a593Smuzhiyun 	struct usb_device *udev;
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun 	uint16_t version;
26*4882a593Smuzhiyun 	uint8_t bufsize;
27*4882a593Smuzhiyun 	uint8_t cycle_overhead;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun 	/* receiver support */
30*4882a593Smuzhiyun 	bool receiver_on;
31*4882a593Smuzhiyun 	dma_addr_t dma_in, dma_out;
32*4882a593Smuzhiyun 	uint8_t *buf_in;
33*4882a593Smuzhiyun 	struct urb *urb_in, *urb_out;
34*4882a593Smuzhiyun 	struct completion completion;
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	/* transmit support */
37*4882a593Smuzhiyun 	bool tx_overflow;
38*4882a593Smuzhiyun 	uint32_t carrier;
39*4882a593Smuzhiyun 	struct send_packet *packet;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	char name[64];
42*4882a593Smuzhiyun 	char phys[64];
43*4882a593Smuzhiyun };
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #define CMD_NOP			0x00
46*4882a593Smuzhiyun #define CMD_GET_VERSION		0x01
47*4882a593Smuzhiyun #define CMD_GET_BUFSIZE		0x11
48*4882a593Smuzhiyun #define CMD_GET_FEATURES	0x10
49*4882a593Smuzhiyun #define CMD_SEND		0x15
50*4882a593Smuzhiyun #define CMD_EXECUTE		0x1f
51*4882a593Smuzhiyun #define CMD_RX_OVERFLOW		0x31
52*4882a593Smuzhiyun #define CMD_TX_OVERFLOW		0x32
53*4882a593Smuzhiyun #define CMD_RECEIVER_ON		0x12
54*4882a593Smuzhiyun #define CMD_RECEIVER_OFF	0x14
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun #define DIR_IN			0xdc
57*4882a593Smuzhiyun #define DIR_OUT			0xcd
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun #define MAX_IN_PACKET		8u
60*4882a593Smuzhiyun #define MAX_OUT_PACKET		(sizeof(struct send_packet) + BUF_SIZE)
61*4882a593Smuzhiyun #define TIMEOUT			1000
62*4882a593Smuzhiyun #define RX_RESOLUTION		21
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun struct packet {
65*4882a593Smuzhiyun 	uint16_t start;
66*4882a593Smuzhiyun 	uint8_t direction;
67*4882a593Smuzhiyun 	uint8_t cmd;
68*4882a593Smuzhiyun };
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun struct send_packet {
71*4882a593Smuzhiyun 	struct packet header;
72*4882a593Smuzhiyun 	uint8_t length;
73*4882a593Smuzhiyun 	uint8_t channels;
74*4882a593Smuzhiyun 	uint8_t busy7;
75*4882a593Smuzhiyun 	uint8_t busy4;
76*4882a593Smuzhiyun 	uint8_t payload[];
77*4882a593Smuzhiyun };
78*4882a593Smuzhiyun 
process_ir_data(struct iguanair * ir,unsigned len)79*4882a593Smuzhiyun static void process_ir_data(struct iguanair *ir, unsigned len)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun 	if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
82*4882a593Smuzhiyun 		switch (ir->buf_in[3]) {
83*4882a593Smuzhiyun 		case CMD_GET_VERSION:
84*4882a593Smuzhiyun 			if (len == 6) {
85*4882a593Smuzhiyun 				ir->version = (ir->buf_in[5] << 8) |
86*4882a593Smuzhiyun 							ir->buf_in[4];
87*4882a593Smuzhiyun 				complete(&ir->completion);
88*4882a593Smuzhiyun 			}
89*4882a593Smuzhiyun 			break;
90*4882a593Smuzhiyun 		case CMD_GET_BUFSIZE:
91*4882a593Smuzhiyun 			if (len >= 5) {
92*4882a593Smuzhiyun 				ir->bufsize = ir->buf_in[4];
93*4882a593Smuzhiyun 				complete(&ir->completion);
94*4882a593Smuzhiyun 			}
95*4882a593Smuzhiyun 			break;
96*4882a593Smuzhiyun 		case CMD_GET_FEATURES:
97*4882a593Smuzhiyun 			if (len > 5) {
98*4882a593Smuzhiyun 				ir->cycle_overhead = ir->buf_in[5];
99*4882a593Smuzhiyun 				complete(&ir->completion);
100*4882a593Smuzhiyun 			}
101*4882a593Smuzhiyun 			break;
102*4882a593Smuzhiyun 		case CMD_TX_OVERFLOW:
103*4882a593Smuzhiyun 			ir->tx_overflow = true;
104*4882a593Smuzhiyun 			fallthrough;
105*4882a593Smuzhiyun 		case CMD_RECEIVER_OFF:
106*4882a593Smuzhiyun 		case CMD_RECEIVER_ON:
107*4882a593Smuzhiyun 		case CMD_SEND:
108*4882a593Smuzhiyun 			complete(&ir->completion);
109*4882a593Smuzhiyun 			break;
110*4882a593Smuzhiyun 		case CMD_RX_OVERFLOW:
111*4882a593Smuzhiyun 			dev_warn(ir->dev, "receive overflow\n");
112*4882a593Smuzhiyun 			ir_raw_event_reset(ir->rc);
113*4882a593Smuzhiyun 			break;
114*4882a593Smuzhiyun 		default:
115*4882a593Smuzhiyun 			dev_warn(ir->dev, "control code %02x received\n",
116*4882a593Smuzhiyun 							ir->buf_in[3]);
117*4882a593Smuzhiyun 			break;
118*4882a593Smuzhiyun 		}
119*4882a593Smuzhiyun 	} else if (len >= 7) {
120*4882a593Smuzhiyun 		struct ir_raw_event rawir = {};
121*4882a593Smuzhiyun 		unsigned i;
122*4882a593Smuzhiyun 		bool event = false;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 		for (i = 0; i < 7; i++) {
125*4882a593Smuzhiyun 			if (ir->buf_in[i] == 0x80) {
126*4882a593Smuzhiyun 				rawir.pulse = false;
127*4882a593Smuzhiyun 				rawir.duration = 21845;
128*4882a593Smuzhiyun 			} else {
129*4882a593Smuzhiyun 				rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
130*4882a593Smuzhiyun 				rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
131*4882a593Smuzhiyun 								 RX_RESOLUTION;
132*4882a593Smuzhiyun 			}
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 			if (ir_raw_event_store_with_filter(ir->rc, &rawir))
135*4882a593Smuzhiyun 				event = true;
136*4882a593Smuzhiyun 		}
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 		if (event)
139*4882a593Smuzhiyun 			ir_raw_event_handle(ir->rc);
140*4882a593Smuzhiyun 	}
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun 
iguanair_rx(struct urb * urb)143*4882a593Smuzhiyun static void iguanair_rx(struct urb *urb)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun 	struct iguanair *ir;
146*4882a593Smuzhiyun 	int rc;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	if (!urb)
149*4882a593Smuzhiyun 		return;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	ir = urb->context;
152*4882a593Smuzhiyun 	if (!ir) {
153*4882a593Smuzhiyun 		usb_unlink_urb(urb);
154*4882a593Smuzhiyun 		return;
155*4882a593Smuzhiyun 	}
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	switch (urb->status) {
158*4882a593Smuzhiyun 	case 0:
159*4882a593Smuzhiyun 		process_ir_data(ir, urb->actual_length);
160*4882a593Smuzhiyun 		break;
161*4882a593Smuzhiyun 	case -ECONNRESET:
162*4882a593Smuzhiyun 	case -ENOENT:
163*4882a593Smuzhiyun 	case -ESHUTDOWN:
164*4882a593Smuzhiyun 		usb_unlink_urb(urb);
165*4882a593Smuzhiyun 		return;
166*4882a593Smuzhiyun 	case -EPIPE:
167*4882a593Smuzhiyun 	default:
168*4882a593Smuzhiyun 		dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
169*4882a593Smuzhiyun 		break;
170*4882a593Smuzhiyun 	}
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	rc = usb_submit_urb(urb, GFP_ATOMIC);
173*4882a593Smuzhiyun 	if (rc && rc != -ENODEV)
174*4882a593Smuzhiyun 		dev_warn(ir->dev, "failed to resubmit urb: %d\n", rc);
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun 
iguanair_irq_out(struct urb * urb)177*4882a593Smuzhiyun static void iguanair_irq_out(struct urb *urb)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun 	struct iguanair *ir = urb->context;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	if (urb->status)
182*4882a593Smuzhiyun 		dev_dbg(ir->dev, "Error: out urb status = %d\n", urb->status);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	/* if we sent an nop packet, do not expect a response */
185*4882a593Smuzhiyun 	if (urb->status == 0 && ir->packet->header.cmd == CMD_NOP)
186*4882a593Smuzhiyun 		complete(&ir->completion);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
iguanair_send(struct iguanair * ir,unsigned size)189*4882a593Smuzhiyun static int iguanair_send(struct iguanair *ir, unsigned size)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun 	int rc;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	reinit_completion(&ir->completion);
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	ir->urb_out->transfer_buffer_length = size;
196*4882a593Smuzhiyun 	rc = usb_submit_urb(ir->urb_out, GFP_KERNEL);
197*4882a593Smuzhiyun 	if (rc)
198*4882a593Smuzhiyun 		return rc;
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	if (wait_for_completion_timeout(&ir->completion, TIMEOUT) == 0)
201*4882a593Smuzhiyun 		return -ETIMEDOUT;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	return rc;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun 
iguanair_get_features(struct iguanair * ir)206*4882a593Smuzhiyun static int iguanair_get_features(struct iguanair *ir)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun 	int rc;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	/*
211*4882a593Smuzhiyun 	 * On cold boot, the iguanair initializes on the first packet
212*4882a593Smuzhiyun 	 * received but does not process that packet. Send an empty
213*4882a593Smuzhiyun 	 * packet.
214*4882a593Smuzhiyun 	 */
215*4882a593Smuzhiyun 	ir->packet->header.start = 0;
216*4882a593Smuzhiyun 	ir->packet->header.direction = DIR_OUT;
217*4882a593Smuzhiyun 	ir->packet->header.cmd = CMD_NOP;
218*4882a593Smuzhiyun 	iguanair_send(ir, sizeof(ir->packet->header));
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	ir->packet->header.cmd = CMD_GET_VERSION;
221*4882a593Smuzhiyun 	rc = iguanair_send(ir, sizeof(ir->packet->header));
222*4882a593Smuzhiyun 	if (rc) {
223*4882a593Smuzhiyun 		dev_info(ir->dev, "failed to get version\n");
224*4882a593Smuzhiyun 		goto out;
225*4882a593Smuzhiyun 	}
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	if (ir->version < 0x205) {
228*4882a593Smuzhiyun 		dev_err(ir->dev, "firmware 0x%04x is too old\n", ir->version);
229*4882a593Smuzhiyun 		rc = -ENODEV;
230*4882a593Smuzhiyun 		goto out;
231*4882a593Smuzhiyun 	}
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	ir->bufsize = 150;
234*4882a593Smuzhiyun 	ir->cycle_overhead = 65;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	ir->packet->header.cmd = CMD_GET_BUFSIZE;
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	rc = iguanair_send(ir, sizeof(ir->packet->header));
239*4882a593Smuzhiyun 	if (rc) {
240*4882a593Smuzhiyun 		dev_info(ir->dev, "failed to get buffer size\n");
241*4882a593Smuzhiyun 		goto out;
242*4882a593Smuzhiyun 	}
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	if (ir->bufsize > BUF_SIZE) {
245*4882a593Smuzhiyun 		dev_info(ir->dev, "buffer size %u larger than expected\n",
246*4882a593Smuzhiyun 								ir->bufsize);
247*4882a593Smuzhiyun 		ir->bufsize = BUF_SIZE;
248*4882a593Smuzhiyun 	}
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	ir->packet->header.cmd = CMD_GET_FEATURES;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	rc = iguanair_send(ir, sizeof(ir->packet->header));
253*4882a593Smuzhiyun 	if (rc)
254*4882a593Smuzhiyun 		dev_info(ir->dev, "failed to get features\n");
255*4882a593Smuzhiyun out:
256*4882a593Smuzhiyun 	return rc;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun 
iguanair_receiver(struct iguanair * ir,bool enable)259*4882a593Smuzhiyun static int iguanair_receiver(struct iguanair *ir, bool enable)
260*4882a593Smuzhiyun {
261*4882a593Smuzhiyun 	ir->packet->header.start = 0;
262*4882a593Smuzhiyun 	ir->packet->header.direction = DIR_OUT;
263*4882a593Smuzhiyun 	ir->packet->header.cmd = enable ? CMD_RECEIVER_ON : CMD_RECEIVER_OFF;
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	if (enable)
266*4882a593Smuzhiyun 		ir_raw_event_reset(ir->rc);
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	return iguanair_send(ir, sizeof(ir->packet->header));
269*4882a593Smuzhiyun }
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun /*
272*4882a593Smuzhiyun  * The iguanair creates the carrier by busy spinning after each half period.
273*4882a593Smuzhiyun  * This is counted in CPU cycles, with the CPU running at 24MHz. It is
274*4882a593Smuzhiyun  * broken down into 7-cycles and 4-cyles delays, with a preference for
275*4882a593Smuzhiyun  * 4-cycle delays, minus the overhead of the loop itself (cycle_overhead).
276*4882a593Smuzhiyun  */
iguanair_set_tx_carrier(struct rc_dev * dev,uint32_t carrier)277*4882a593Smuzhiyun static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun 	struct iguanair *ir = dev->priv;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	if (carrier < 25000 || carrier > 150000)
282*4882a593Smuzhiyun 		return -EINVAL;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	if (carrier != ir->carrier) {
285*4882a593Smuzhiyun 		uint32_t cycles, fours, sevens;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 		ir->carrier = carrier;
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 		cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) -
290*4882a593Smuzhiyun 							ir->cycle_overhead;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 		/*
293*4882a593Smuzhiyun 		 * Calculate minimum number of 7 cycles needed so
294*4882a593Smuzhiyun 		 * we are left with a multiple of 4; so we want to have
295*4882a593Smuzhiyun 		 * (sevens * 7) & 3 == cycles & 3
296*4882a593Smuzhiyun 		 */
297*4882a593Smuzhiyun 		sevens = (4 - cycles) & 3;
298*4882a593Smuzhiyun 		fours = (cycles - sevens * 7) / 4;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 		/*
301*4882a593Smuzhiyun 		 * The firmware interprets these values as a relative offset
302*4882a593Smuzhiyun 		 * for a branch. Immediately following the branches, there
303*4882a593Smuzhiyun 		 * 4 instructions of 7 cycles (2 bytes each) and 110
304*4882a593Smuzhiyun 		 * instructions of 4 cycles (1 byte each). A relative branch
305*4882a593Smuzhiyun 		 * of 0 will execute all of them, branch further for less
306*4882a593Smuzhiyun 		 * cycle burning.
307*4882a593Smuzhiyun 		 */
308*4882a593Smuzhiyun 		ir->packet->busy7 = (4 - sevens) * 2;
309*4882a593Smuzhiyun 		ir->packet->busy4 = 110 - fours;
310*4882a593Smuzhiyun 	}
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	return 0;
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun 
iguanair_set_tx_mask(struct rc_dev * dev,uint32_t mask)315*4882a593Smuzhiyun static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun 	struct iguanair *ir = dev->priv;
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	if (mask > 15)
320*4882a593Smuzhiyun 		return 4;
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	ir->packet->channels = mask << 4;
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	return 0;
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun 
iguanair_tx(struct rc_dev * dev,unsigned * txbuf,unsigned count)327*4882a593Smuzhiyun static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
328*4882a593Smuzhiyun {
329*4882a593Smuzhiyun 	struct iguanair *ir = dev->priv;
330*4882a593Smuzhiyun 	unsigned int i, size, p, periods;
331*4882a593Smuzhiyun 	int rc;
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	/* convert from us to carrier periods */
334*4882a593Smuzhiyun 	for (i = size = 0; i < count; i++) {
335*4882a593Smuzhiyun 		periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
336*4882a593Smuzhiyun 		while (periods) {
337*4882a593Smuzhiyun 			p = min(periods, 127u);
338*4882a593Smuzhiyun 			if (size >= ir->bufsize) {
339*4882a593Smuzhiyun 				rc = -EINVAL;
340*4882a593Smuzhiyun 				goto out;
341*4882a593Smuzhiyun 			}
342*4882a593Smuzhiyun 			ir->packet->payload[size++] = p | ((i & 1) ? 0x80 : 0);
343*4882a593Smuzhiyun 			periods -= p;
344*4882a593Smuzhiyun 		}
345*4882a593Smuzhiyun 	}
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	ir->packet->header.start = 0;
348*4882a593Smuzhiyun 	ir->packet->header.direction = DIR_OUT;
349*4882a593Smuzhiyun 	ir->packet->header.cmd = CMD_SEND;
350*4882a593Smuzhiyun 	ir->packet->length = size;
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	ir->tx_overflow = false;
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	rc = iguanair_send(ir, sizeof(*ir->packet) + size);
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	if (rc == 0 && ir->tx_overflow)
357*4882a593Smuzhiyun 		rc = -EOVERFLOW;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun out:
360*4882a593Smuzhiyun 	return rc ? rc : count;
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun 
iguanair_open(struct rc_dev * rdev)363*4882a593Smuzhiyun static int iguanair_open(struct rc_dev *rdev)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun 	struct iguanair *ir = rdev->priv;
366*4882a593Smuzhiyun 	int rc;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	rc = iguanair_receiver(ir, true);
369*4882a593Smuzhiyun 	if (rc == 0)
370*4882a593Smuzhiyun 		ir->receiver_on = true;
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	return rc;
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun 
iguanair_close(struct rc_dev * rdev)375*4882a593Smuzhiyun static void iguanair_close(struct rc_dev *rdev)
376*4882a593Smuzhiyun {
377*4882a593Smuzhiyun 	struct iguanair *ir = rdev->priv;
378*4882a593Smuzhiyun 	int rc;
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	rc = iguanair_receiver(ir, false);
381*4882a593Smuzhiyun 	ir->receiver_on = false;
382*4882a593Smuzhiyun 	if (rc && rc != -ENODEV)
383*4882a593Smuzhiyun 		dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun 
iguanair_probe(struct usb_interface * intf,const struct usb_device_id * id)386*4882a593Smuzhiyun static int iguanair_probe(struct usb_interface *intf,
387*4882a593Smuzhiyun 			  const struct usb_device_id *id)
388*4882a593Smuzhiyun {
389*4882a593Smuzhiyun 	struct usb_device *udev = interface_to_usbdev(intf);
390*4882a593Smuzhiyun 	struct iguanair *ir;
391*4882a593Smuzhiyun 	struct rc_dev *rc;
392*4882a593Smuzhiyun 	int ret, pipein, pipeout;
393*4882a593Smuzhiyun 	struct usb_host_interface *idesc;
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	idesc = intf->cur_altsetting;
396*4882a593Smuzhiyun 	if (idesc->desc.bNumEndpoints < 2)
397*4882a593Smuzhiyun 		return -ENODEV;
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
400*4882a593Smuzhiyun 	rc = rc_allocate_device(RC_DRIVER_IR_RAW);
401*4882a593Smuzhiyun 	if (!ir || !rc) {
402*4882a593Smuzhiyun 		ret = -ENOMEM;
403*4882a593Smuzhiyun 		goto out;
404*4882a593Smuzhiyun 	}
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	ir->buf_in = usb_alloc_coherent(udev, MAX_IN_PACKET, GFP_KERNEL,
407*4882a593Smuzhiyun 								&ir->dma_in);
408*4882a593Smuzhiyun 	ir->packet = usb_alloc_coherent(udev, MAX_OUT_PACKET, GFP_KERNEL,
409*4882a593Smuzhiyun 								&ir->dma_out);
410*4882a593Smuzhiyun 	ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
411*4882a593Smuzhiyun 	ir->urb_out = usb_alloc_urb(0, GFP_KERNEL);
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 	if (!ir->buf_in || !ir->packet || !ir->urb_in || !ir->urb_out ||
414*4882a593Smuzhiyun 	    !usb_endpoint_is_int_in(&idesc->endpoint[0].desc) ||
415*4882a593Smuzhiyun 	    !usb_endpoint_is_int_out(&idesc->endpoint[1].desc)) {
416*4882a593Smuzhiyun 		ret = -ENOMEM;
417*4882a593Smuzhiyun 		goto out;
418*4882a593Smuzhiyun 	}
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	ir->rc = rc;
421*4882a593Smuzhiyun 	ir->dev = &intf->dev;
422*4882a593Smuzhiyun 	ir->udev = udev;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	init_completion(&ir->completion);
425*4882a593Smuzhiyun 	pipeout = usb_sndintpipe(udev,
426*4882a593Smuzhiyun 				idesc->endpoint[1].desc.bEndpointAddress);
427*4882a593Smuzhiyun 	usb_fill_int_urb(ir->urb_out, udev, pipeout, ir->packet, MAX_OUT_PACKET,
428*4882a593Smuzhiyun 						iguanair_irq_out, ir, 1);
429*4882a593Smuzhiyun 	ir->urb_out->transfer_dma = ir->dma_out;
430*4882a593Smuzhiyun 	ir->urb_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	pipein = usb_rcvintpipe(udev, idesc->endpoint[0].desc.bEndpointAddress);
433*4882a593Smuzhiyun 	usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in, MAX_IN_PACKET,
434*4882a593Smuzhiyun 							 iguanair_rx, ir, 1);
435*4882a593Smuzhiyun 	ir->urb_in->transfer_dma = ir->dma_in;
436*4882a593Smuzhiyun 	ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 	ret = usb_submit_urb(ir->urb_in, GFP_KERNEL);
439*4882a593Smuzhiyun 	if (ret) {
440*4882a593Smuzhiyun 		dev_warn(&intf->dev, "failed to submit urb: %d\n", ret);
441*4882a593Smuzhiyun 		goto out;
442*4882a593Smuzhiyun 	}
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	ret = iguanair_get_features(ir);
445*4882a593Smuzhiyun 	if (ret)
446*4882a593Smuzhiyun 		goto out2;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	snprintf(ir->name, sizeof(ir->name),
449*4882a593Smuzhiyun 		"IguanaWorks USB IR Transceiver version 0x%04x", ir->version);
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun 	usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	rc->device_name = ir->name;
454*4882a593Smuzhiyun 	rc->input_phys = ir->phys;
455*4882a593Smuzhiyun 	usb_to_input_id(ir->udev, &rc->input_id);
456*4882a593Smuzhiyun 	rc->dev.parent = &intf->dev;
457*4882a593Smuzhiyun 	rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
458*4882a593Smuzhiyun 	rc->priv = ir;
459*4882a593Smuzhiyun 	rc->open = iguanair_open;
460*4882a593Smuzhiyun 	rc->close = iguanair_close;
461*4882a593Smuzhiyun 	rc->s_tx_mask = iguanair_set_tx_mask;
462*4882a593Smuzhiyun 	rc->s_tx_carrier = iguanair_set_tx_carrier;
463*4882a593Smuzhiyun 	rc->tx_ir = iguanair_tx;
464*4882a593Smuzhiyun 	rc->driver_name = KBUILD_MODNAME;
465*4882a593Smuzhiyun 	rc->map_name = RC_MAP_RC6_MCE;
466*4882a593Smuzhiyun 	rc->min_timeout = 1;
467*4882a593Smuzhiyun 	rc->timeout = IR_DEFAULT_TIMEOUT;
468*4882a593Smuzhiyun 	rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
469*4882a593Smuzhiyun 	rc->rx_resolution = RX_RESOLUTION;
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	iguanair_set_tx_carrier(rc, 38000);
472*4882a593Smuzhiyun 	iguanair_set_tx_mask(rc, 0);
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	ret = rc_register_device(rc);
475*4882a593Smuzhiyun 	if (ret < 0) {
476*4882a593Smuzhiyun 		dev_err(&intf->dev, "failed to register rc device %d", ret);
477*4882a593Smuzhiyun 		goto out2;
478*4882a593Smuzhiyun 	}
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	usb_set_intfdata(intf, ir);
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	return 0;
483*4882a593Smuzhiyun out2:
484*4882a593Smuzhiyun 	usb_kill_urb(ir->urb_in);
485*4882a593Smuzhiyun 	usb_kill_urb(ir->urb_out);
486*4882a593Smuzhiyun out:
487*4882a593Smuzhiyun 	if (ir) {
488*4882a593Smuzhiyun 		usb_free_urb(ir->urb_in);
489*4882a593Smuzhiyun 		usb_free_urb(ir->urb_out);
490*4882a593Smuzhiyun 		usb_free_coherent(udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
491*4882a593Smuzhiyun 		usb_free_coherent(udev, MAX_OUT_PACKET, ir->packet,
492*4882a593Smuzhiyun 								ir->dma_out);
493*4882a593Smuzhiyun 	}
494*4882a593Smuzhiyun 	rc_free_device(rc);
495*4882a593Smuzhiyun 	kfree(ir);
496*4882a593Smuzhiyun 	return ret;
497*4882a593Smuzhiyun }
498*4882a593Smuzhiyun 
iguanair_disconnect(struct usb_interface * intf)499*4882a593Smuzhiyun static void iguanair_disconnect(struct usb_interface *intf)
500*4882a593Smuzhiyun {
501*4882a593Smuzhiyun 	struct iguanair *ir = usb_get_intfdata(intf);
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 	rc_unregister_device(ir->rc);
504*4882a593Smuzhiyun 	usb_set_intfdata(intf, NULL);
505*4882a593Smuzhiyun 	usb_kill_urb(ir->urb_in);
506*4882a593Smuzhiyun 	usb_kill_urb(ir->urb_out);
507*4882a593Smuzhiyun 	usb_free_urb(ir->urb_in);
508*4882a593Smuzhiyun 	usb_free_urb(ir->urb_out);
509*4882a593Smuzhiyun 	usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
510*4882a593Smuzhiyun 	usb_free_coherent(ir->udev, MAX_OUT_PACKET, ir->packet, ir->dma_out);
511*4882a593Smuzhiyun 	kfree(ir);
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun 
iguanair_suspend(struct usb_interface * intf,pm_message_t message)514*4882a593Smuzhiyun static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
515*4882a593Smuzhiyun {
516*4882a593Smuzhiyun 	struct iguanair *ir = usb_get_intfdata(intf);
517*4882a593Smuzhiyun 	int rc = 0;
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	if (ir->receiver_on) {
520*4882a593Smuzhiyun 		rc = iguanair_receiver(ir, false);
521*4882a593Smuzhiyun 		if (rc)
522*4882a593Smuzhiyun 			dev_warn(ir->dev, "failed to disable receiver for suspend\n");
523*4882a593Smuzhiyun 	}
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	usb_kill_urb(ir->urb_in);
526*4882a593Smuzhiyun 	usb_kill_urb(ir->urb_out);
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	return rc;
529*4882a593Smuzhiyun }
530*4882a593Smuzhiyun 
iguanair_resume(struct usb_interface * intf)531*4882a593Smuzhiyun static int iguanair_resume(struct usb_interface *intf)
532*4882a593Smuzhiyun {
533*4882a593Smuzhiyun 	struct iguanair *ir = usb_get_intfdata(intf);
534*4882a593Smuzhiyun 	int rc;
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	rc = usb_submit_urb(ir->urb_in, GFP_KERNEL);
537*4882a593Smuzhiyun 	if (rc)
538*4882a593Smuzhiyun 		dev_warn(&intf->dev, "failed to submit urb: %d\n", rc);
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	if (ir->receiver_on) {
541*4882a593Smuzhiyun 		rc = iguanair_receiver(ir, true);
542*4882a593Smuzhiyun 		if (rc)
543*4882a593Smuzhiyun 			dev_warn(ir->dev, "failed to enable receiver after resume\n");
544*4882a593Smuzhiyun 	}
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	return rc;
547*4882a593Smuzhiyun }
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun static const struct usb_device_id iguanair_table[] = {
550*4882a593Smuzhiyun 	{ USB_DEVICE(0x1781, 0x0938) },
551*4882a593Smuzhiyun 	{ }
552*4882a593Smuzhiyun };
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun static struct usb_driver iguanair_driver = {
555*4882a593Smuzhiyun 	.name =	KBUILD_MODNAME,
556*4882a593Smuzhiyun 	.probe = iguanair_probe,
557*4882a593Smuzhiyun 	.disconnect = iguanair_disconnect,
558*4882a593Smuzhiyun 	.suspend = iguanair_suspend,
559*4882a593Smuzhiyun 	.resume = iguanair_resume,
560*4882a593Smuzhiyun 	.reset_resume = iguanair_resume,
561*4882a593Smuzhiyun 	.id_table = iguanair_table,
562*4882a593Smuzhiyun 	.soft_unbind = 1	/* we want to disable receiver on unbind */
563*4882a593Smuzhiyun };
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun module_usb_driver(iguanair_driver);
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun MODULE_DESCRIPTION("IguanaWorks USB IR Transceiver");
568*4882a593Smuzhiyun MODULE_AUTHOR("Sean Young <sean@mess.org>");
569*4882a593Smuzhiyun MODULE_LICENSE("GPL");
570*4882a593Smuzhiyun MODULE_DEVICE_TABLE(usb, iguanair_table);
571*4882a593Smuzhiyun 
572