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