iguanair.c 13.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*
 * IguanaWorks USB IR Transceiver support
 *
 * Copyright (C) 2012 Sean Young <sean@mess.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#include <linux/slab.h>
#include <linux/completion.h>
#include <media/rc-core.h>

#define DRIVER_NAME "iguanair"
31
#define BUF_SIZE 152
32 33 34 35 36 37 38

struct iguanair {
	struct rc_dev *rc;

	struct device *dev;
	struct usb_device *udev;

39
	uint16_t version;
40
	uint8_t bufsize;
41
	uint8_t cycle_overhead;
42 43 44 45 46

	struct mutex lock;

	/* receiver support */
	bool receiver_on;
47
	dma_addr_t dma_in, dma_out;
48
	uint8_t *buf_in;
49
	struct urb *urb_in, *urb_out;
50 51 52 53 54
	struct completion completion;

	/* transmit support */
	bool tx_overflow;
	uint32_t carrier;
55
	struct send_packet *packet;
56 57 58 59 60

	char name[64];
	char phys[64];
};

61
#define CMD_NOP			0x00
62 63 64 65 66 67 68 69 70 71 72 73 74
#define CMD_GET_VERSION		0x01
#define CMD_GET_BUFSIZE		0x11
#define CMD_GET_FEATURES	0x10
#define CMD_SEND		0x15
#define CMD_EXECUTE		0x1f
#define CMD_RX_OVERFLOW		0x31
#define CMD_TX_OVERFLOW		0x32
#define CMD_RECEIVER_ON		0x12
#define CMD_RECEIVER_OFF	0x14

#define DIR_IN			0xdc
#define DIR_OUT			0xcd

75 76
#define MAX_IN_PACKET		8u
#define MAX_OUT_PACKET		(sizeof(struct send_packet) + BUF_SIZE)
77
#define TIMEOUT			1000
78
#define RX_RESOLUTION		21333
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98

struct packet {
	uint16_t start;
	uint8_t direction;
	uint8_t cmd;
};

struct send_packet {
	struct packet header;
	uint8_t length;
	uint8_t channels;
	uint8_t busy7;
	uint8_t busy4;
	uint8_t payload[0];
};

static void process_ir_data(struct iguanair *ir, unsigned len)
{
	if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
		switch (ir->buf_in[3]) {
99 100
		case CMD_GET_VERSION:
			if (len == 6) {
101 102
				ir->version = (ir->buf_in[5] << 8) |
							ir->buf_in[4];
103 104 105 106 107 108 109 110 111 112 113
				complete(&ir->completion);
			}
			break;
		case CMD_GET_BUFSIZE:
			if (len >= 5) {
				ir->bufsize = ir->buf_in[4];
				complete(&ir->completion);
			}
			break;
		case CMD_GET_FEATURES:
			if (len > 5) {
114
				ir->cycle_overhead = ir->buf_in[5];
115 116 117
				complete(&ir->completion);
			}
			break;
118 119 120 121 122 123 124 125 126
		case CMD_TX_OVERFLOW:
			ir->tx_overflow = true;
		case CMD_RECEIVER_OFF:
		case CMD_RECEIVER_ON:
		case CMD_SEND:
			complete(&ir->completion);
			break;
		case CMD_RX_OVERFLOW:
			dev_warn(ir->dev, "receive overflow\n");
127
			ir_raw_event_reset(ir->rc);
128 129 130 131 132 133 134 135 136
			break;
		default:
			dev_warn(ir->dev, "control code %02x received\n",
							ir->buf_in[3]);
			break;
		}
	} else if (len >= 7) {
		DEFINE_IR_RAW_EVENT(rawir);
		unsigned i;
137
		bool event = false;
138 139 140 141 142 143 144 145 146 147

		init_ir_raw_event(&rawir);

		for (i = 0; i < 7; i++) {
			if (ir->buf_in[i] == 0x80) {
				rawir.pulse = false;
				rawir.duration = US_TO_NS(21845);
			} else {
				rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
				rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
148
								 RX_RESOLUTION;
149 150
			}

151 152
			if (ir_raw_event_store_with_filter(ir->rc, &rawir))
				event = true;
153 154
		}

155 156
		if (event)
			ir_raw_event_handle(ir->rc);
157 158 159 160 161 162
	}
}

static void iguanair_rx(struct urb *urb)
{
	struct iguanair *ir;
163
	int rc;
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

	if (!urb)
		return;

	ir = urb->context;
	if (!ir) {
		usb_unlink_urb(urb);
		return;
	}

	switch (urb->status) {
	case 0:
		process_ir_data(ir, urb->actual_length);
		break;
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		usb_unlink_urb(urb);
		return;
	case -EPIPE:
	default:
		dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
		break;
	}

189 190 191
	rc = usb_submit_urb(urb, GFP_ATOMIC);
	if (rc && rc != -ENODEV)
		dev_warn(ir->dev, "failed to resubmit urb: %d\n", rc);
192 193
}

194
static void iguanair_irq_out(struct urb *urb)
195
{
196 197 198 199
	struct iguanair *ir = urb->context;

	if (urb->status)
		dev_dbg(ir->dev, "Error: out urb status = %d\n", urb->status);
200 201 202 203

	/* if we sent an nop packet, do not expect a response */
	if (urb->status == 0 && ir->packet->header.cmd == CMD_NOP)
		complete(&ir->completion);
204 205 206 207 208
}

static int iguanair_send(struct iguanair *ir, unsigned size)
{
	int rc;
209

210
	INIT_COMPLETION(ir->completion);
211

212 213
	ir->urb_out->transfer_buffer_length = size;
	rc = usb_submit_urb(ir->urb_out, GFP_KERNEL);
214 215
	if (rc)
		return rc;
216

217 218
	if (wait_for_completion_timeout(&ir->completion, TIMEOUT) == 0)
		return -ETIMEDOUT;
219 220 221 222 223 224

	return rc;
}

static int iguanair_get_features(struct iguanair *ir)
{
225
	int rc;
226

227 228 229 230 231
	/*
	 * On cold boot, the iguanair initializes on the first packet
	 * received but does not process that packet. Send an empty
	 * packet.
	 */
232 233
	ir->packet->header.start = 0;
	ir->packet->header.direction = DIR_OUT;
234 235
	ir->packet->header.cmd = CMD_NOP;
	iguanair_send(ir, sizeof(ir->packet->header));
236

237
	ir->packet->header.cmd = CMD_GET_VERSION;
238
	rc = iguanair_send(ir, sizeof(ir->packet->header));
239 240 241 242 243
	if (rc) {
		dev_info(ir->dev, "failed to get version\n");
		goto out;
	}

244 245 246 247 248 249
	if (ir->version < 0x205) {
		dev_err(ir->dev, "firmware 0x%04x is too old\n", ir->version);
		rc = -ENODEV;
		goto out;
	}

250 251 252
	ir->bufsize = 150;
	ir->cycle_overhead = 65;

253
	ir->packet->header.cmd = CMD_GET_BUFSIZE;
254

255
	rc = iguanair_send(ir, sizeof(ir->packet->header));
256 257 258 259 260
	if (rc) {
		dev_info(ir->dev, "failed to get buffer size\n");
		goto out;
	}

261 262 263 264 265 266 267
	if (ir->bufsize > BUF_SIZE) {
		dev_info(ir->dev, "buffer size %u larger than expected\n",
								ir->bufsize);
		ir->bufsize = BUF_SIZE;
	}

	ir->packet->header.cmd = CMD_GET_FEATURES;
268

269
	rc = iguanair_send(ir, sizeof(ir->packet->header));
270
	if (rc)
271 272 273 274 275 276 277
		dev_info(ir->dev, "failed to get features\n");
out:
	return rc;
}

static int iguanair_receiver(struct iguanair *ir, bool enable)
{
278 279 280
	ir->packet->header.start = 0;
	ir->packet->header.direction = DIR_OUT;
	ir->packet->header.cmd = enable ? CMD_RECEIVER_ON : CMD_RECEIVER_OFF;
281

282 283 284
	if (enable)
		ir_raw_event_reset(ir->rc);

285
	return iguanair_send(ir, sizeof(ir->packet->header));
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
}

/*
 * The iguana ir creates the carrier by busy spinning after each pulse or
 * space. This is counted in CPU cycles, with the CPU running at 24MHz. It is
 * broken down into 7-cycles and 4-cyles delays, with a preference for
 * 4-cycle delays.
 */
static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
{
	struct iguanair *ir = dev->priv;

	if (carrier < 25000 || carrier > 150000)
		return -EINVAL;

	mutex_lock(&ir->lock);

	if (carrier != ir->carrier) {
		uint32_t cycles, fours, sevens;

		ir->carrier = carrier;

		cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) -
							ir->cycle_overhead;

		/*  make up the the remainer of 4-cycle blocks */
		switch (cycles & 3) {
		case 0:
			sevens = 0;
			break;
		case 1:
			sevens = 3;
			break;
		case 2:
			sevens = 2;
			break;
		case 3:
			sevens = 1;
			break;
		}

		fours = (cycles - sevens * 7) / 4;

		/* magic happens here */
330 331
		ir->packet->busy7 = (4 - sevens) * 2;
		ir->packet->busy4 = 110 - fours;
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
	}

	mutex_unlock(&ir->lock);

	return carrier;
}

static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
{
	struct iguanair *ir = dev->priv;

	if (mask > 15)
		return 4;

	mutex_lock(&ir->lock);
347
	ir->packet->channels = mask << 4;
348 349 350 351 352 353 354 355
	mutex_unlock(&ir->lock);

	return 0;
}

static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
{
	struct iguanair *ir = dev->priv;
356 357 358
	uint8_t space;
	unsigned i, size, periods, bytes;
	int rc;
359 360 361 362

	mutex_lock(&ir->lock);

	/* convert from us to carrier periods */
363 364 365
	for (i = space = size = 0; i < count; i++) {
		periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
		bytes = DIV_ROUND_UP(periods, 127);
366 367 368 369
		if (size + bytes > ir->bufsize) {
			count = i;
			break;
		}
370
		while (periods > 127) {
371
			ir->packet->payload[size++] = 127 | space;
372 373 374
			periods -= 127;
		}

375
		ir->packet->payload[size++] = periods | space;
376
		space ^= 0x80;
377 378
	}

379 380
	if (count == 0) {
		rc = -EINVAL;
381 382 383
		goto out;
	}

384 385 386 387
	ir->packet->header.start = 0;
	ir->packet->header.direction = DIR_OUT;
	ir->packet->header.cmd = CMD_SEND;
	ir->packet->length = size;
388 389 390

	ir->tx_overflow = false;

391
	rc = iguanair_send(ir, sizeof(*ir->packet) + size);
392

393 394
	if (rc == 0 && ir->tx_overflow)
		rc = -EOVERFLOW;
395 396 397 398

out:
	mutex_unlock(&ir->lock);

399
	return rc ? rc : count;
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
}

static int iguanair_open(struct rc_dev *rdev)
{
	struct iguanair *ir = rdev->priv;
	int rc;

	mutex_lock(&ir->lock);

	rc = iguanair_receiver(ir, true);
	if (rc == 0)
		ir->receiver_on = true;

	mutex_unlock(&ir->lock);

	return rc;
}

static void iguanair_close(struct rc_dev *rdev)
{
	struct iguanair *ir = rdev->priv;
	int rc;

	mutex_lock(&ir->lock);

	rc = iguanair_receiver(ir, false);
	ir->receiver_on = false;
427
	if (rc && rc != -ENODEV)
428 429 430 431 432
		dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);

	mutex_unlock(&ir->lock);
}

433 434
static int iguanair_probe(struct usb_interface *intf,
			  const struct usb_device_id *id)
435 436 437 438
{
	struct usb_device *udev = interface_to_usbdev(intf);
	struct iguanair *ir;
	struct rc_dev *rc;
439
	int ret, pipein, pipeout;
440 441 442 443 444
	struct usb_host_interface *idesc;

	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
	rc = rc_allocate_device();
	if (!ir || !rc) {
445
		ret = -ENOMEM;
446 447 448
		goto out;
	}

449
	ir->buf_in = usb_alloc_coherent(udev, MAX_IN_PACKET, GFP_KERNEL,
450
								&ir->dma_in);
451 452
	ir->packet = usb_alloc_coherent(udev, MAX_OUT_PACKET, GFP_KERNEL,
								&ir->dma_out);
453
	ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
454
	ir->urb_out = usb_alloc_urb(0, GFP_KERNEL);
455

456
	if (!ir->buf_in || !ir->packet || !ir->urb_in || !ir->urb_out) {
457
		ret = -ENOMEM;
458 459 460 461 462 463 464 465 466 467 468 469 470 471
		goto out;
	}

	idesc = intf->altsetting;

	if (idesc->desc.bNumEndpoints < 2) {
		ret = -ENODEV;
		goto out;
	}

	ir->rc = rc;
	ir->dev = &intf->dev;
	ir->udev = udev;
	mutex_init(&ir->lock);
472

473
	init_completion(&ir->completion);
474 475 476 477 478 479
	pipeout = usb_sndintpipe(udev,
				idesc->endpoint[1].desc.bEndpointAddress);
	usb_fill_int_urb(ir->urb_out, udev, pipeout, ir->packet, MAX_OUT_PACKET,
						iguanair_irq_out, ir, 1);
	ir->urb_out->transfer_dma = ir->dma_out;
	ir->urb_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
480

481
	pipein = usb_rcvintpipe(udev, idesc->endpoint[0].desc.bEndpointAddress);
482
	usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in, MAX_IN_PACKET,
483
							 iguanair_rx, ir, 1);
484 485 486
	ir->urb_in->transfer_dma = ir->dma_in;
	ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

487 488 489 490 491 492 493 494 495 496
	ret = usb_submit_urb(ir->urb_in, GFP_KERNEL);
	if (ret) {
		dev_warn(&intf->dev, "failed to submit urb: %d\n", ret);
		goto out;
	}

	ret = iguanair_get_features(ir);
	if (ret)
		goto out2;

497
	snprintf(ir->name, sizeof(ir->name),
498
		"IguanaWorks USB IR Transceiver version 0x%04x", ir->version);
499 500 501 502 503 504 505 506

	usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));

	rc->input_name = ir->name;
	rc->input_phys = ir->phys;
	usb_to_input_id(ir->udev, &rc->input_id);
	rc->dev.parent = &intf->dev;
	rc->driver_type = RC_DRIVER_IR_RAW;
507
	rc->allowed_protos = RC_BIT_ALL;
508 509 510 511 512 513 514
	rc->priv = ir;
	rc->open = iguanair_open;
	rc->close = iguanair_close;
	rc->s_tx_mask = iguanair_set_tx_mask;
	rc->s_tx_carrier = iguanair_set_tx_carrier;
	rc->tx_ir = iguanair_tx;
	rc->driver_name = DRIVER_NAME;
515 516 517
	rc->map_name = RC_MAP_RC6_MCE;
	rc->timeout = MS_TO_NS(100);
	rc->rx_resolution = RX_RESOLUTION;
518 519

	iguanair_set_tx_carrier(rc, 38000);
520
	iguanair_set_tx_mask(rc, 0);
521 522 523 524

	ret = rc_register_device(rc);
	if (ret < 0) {
		dev_err(&intf->dev, "failed to register rc device %d", ret);
525
		goto out2;
526 527 528 529 530
	}

	usb_set_intfdata(intf, ir);

	return 0;
531 532
out2:
	usb_kill_urb(ir->urb_in);
533
	usb_kill_urb(ir->urb_out);
534 535 536
out:
	if (ir) {
		usb_free_urb(ir->urb_in);
537 538 539 540
		usb_free_urb(ir->urb_out);
		usb_free_coherent(udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
		usb_free_coherent(udev, MAX_OUT_PACKET, ir->packet,
								ir->dma_out);
541 542 543 544 545 546
	}
	rc_free_device(rc);
	kfree(ir);
	return ret;
}

547
static void iguanair_disconnect(struct usb_interface *intf)
548 549 550
{
	struct iguanair *ir = usb_get_intfdata(intf);

551
	rc_unregister_device(ir->rc);
552 553
	usb_set_intfdata(intf, NULL);
	usb_kill_urb(ir->urb_in);
554
	usb_kill_urb(ir->urb_out);
555
	usb_free_urb(ir->urb_in);
556 557 558
	usb_free_urb(ir->urb_out);
	usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
	usb_free_coherent(ir->udev, MAX_OUT_PACKET, ir->packet, ir->dma_out);
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
	kfree(ir);
}

static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct iguanair *ir = usb_get_intfdata(intf);
	int rc = 0;

	mutex_lock(&ir->lock);

	if (ir->receiver_on) {
		rc = iguanair_receiver(ir, false);
		if (rc)
			dev_warn(ir->dev, "failed to disable receiver for suspend\n");
	}

575
	usb_kill_urb(ir->urb_in);
576
	usb_kill_urb(ir->urb_out);
577

578 579 580 581 582 583 584 585 586 587 588 589
	mutex_unlock(&ir->lock);

	return rc;
}

static int iguanair_resume(struct usb_interface *intf)
{
	struct iguanair *ir = usb_get_intfdata(intf);
	int rc = 0;

	mutex_lock(&ir->lock);

590 591 592 593
	rc = usb_submit_urb(ir->urb_in, GFP_KERNEL);
	if (rc)
		dev_warn(&intf->dev, "failed to submit urb: %d\n", rc);

594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
	if (ir->receiver_on) {
		rc = iguanair_receiver(ir, true);
		if (rc)
			dev_warn(ir->dev, "failed to enable receiver after resume\n");
	}

	mutex_unlock(&ir->lock);

	return rc;
}

static const struct usb_device_id iguanair_table[] = {
	{ USB_DEVICE(0x1781, 0x0938) },
	{ }
};

static struct usb_driver iguanair_driver = {
	.name =	DRIVER_NAME,
	.probe = iguanair_probe,
613
	.disconnect = iguanair_disconnect,
614 615 616
	.suspend = iguanair_suspend,
	.resume = iguanair_resume,
	.reset_resume = iguanair_resume,
617 618
	.id_table = iguanair_table,
	.soft_unbind = 1	/* we want to disable receiver on unbind */
619 620 621 622 623 624 625 626 627
};

module_usb_driver(iguanair_driver);

MODULE_DESCRIPTION("IguanaWorks USB IR Transceiver");
MODULE_AUTHOR("Sean Young <sean@mess.org>");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(usb, iguanair_table);