blob: 2e681cf3e85da8a957624467ff7d8c25feb605d2 [file] [log] [blame]
Jarod Wilsonc5ac4572010-07-26 20:31:11 -03001/*
2 * lirc_sasem.c - USB remote support for LIRC
3 * Version 0.5
4 *
5 * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de>
6 * Tim Davies <tim@opensystems.net.au>
7 *
8 * This driver was derived from:
9 * Venky Raju <dev@venky.ws>
10 * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD"
11 * Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
12 * "lirc_atiusb - USB remote support for LIRC"
13 * Culver Consulting Services <henry@culcon.com>'s 2003
14 * "Sasem OnAir VFD/IR USB driver"
15 *
16 *
17 * NOTE - The LCDproc iMon driver should work with this module. More info at
18 * http://www.frogstorm.info/sasem
19 */
20
21/*
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 */
36
37#include <linux/errno.h>
38#include <linux/init.h>
39#include <linux/kernel.h>
40#include <linux/module.h>
41#include <linux/slab.h>
42#include <linux/uaccess.h>
43#include <linux/usb.h>
44
45#include <media/lirc.h>
46#include <media/lirc_dev.h>
47
48
49#define MOD_AUTHOR "Oliver Stabel <oliver.stabel@gmx.de>, " \
50 "Tim Davies <tim@opensystems.net.au>"
51#define MOD_DESC "USB Driver for Sasem Remote Controller V1.1"
52#define MOD_NAME "lirc_sasem"
53#define MOD_VERSION "0.5"
54
55#define VFD_MINOR_BASE 144 /* Same as LCD */
56#define DEVICE_NAME "lcd%d"
57
58#define BUF_CHUNK_SIZE 8
59#define BUF_SIZE 128
60
61#define IOCTL_LCD_CONTRAST 1
62
63/*** P R O T O T Y P E S ***/
64
65/* USB Callback prototypes */
66static int sasem_probe(struct usb_interface *interface,
67 const struct usb_device_id *id);
68static void sasem_disconnect(struct usb_interface *interface);
69static void usb_rx_callback(struct urb *urb);
70static void usb_tx_callback(struct urb *urb);
71
72/* VFD file_operations function prototypes */
73static int vfd_open(struct inode *inode, struct file *file);
74static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg);
75static int vfd_close(struct inode *inode, struct file *file);
76static ssize_t vfd_write(struct file *file, const char *buf,
77 size_t n_bytes, loff_t *pos);
78
79/* LIRC driver function prototypes */
80static int ir_open(void *data);
81static void ir_close(void *data);
82
83/* Driver init/exit prototypes */
84static int __init sasem_init(void);
85static void __exit sasem_exit(void);
86
87/*** G L O B A L S ***/
88#define SASEM_DATA_BUF_SZ 32
89
90struct sasem_context {
91
92 struct usb_device *dev;
93 int vfd_isopen; /* VFD port has been opened */
94 unsigned int vfd_contrast; /* VFD contrast */
95 int ir_isopen; /* IR port has been opened */
96 int dev_present; /* USB device presence */
97 struct mutex ctx_lock; /* to lock this object */
98 wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
99
100 struct lirc_driver *driver;
101 struct usb_endpoint_descriptor *rx_endpoint;
102 struct usb_endpoint_descriptor *tx_endpoint;
103 struct urb *rx_urb;
104 struct urb *tx_urb;
105 unsigned char usb_rx_buf[8];
106 unsigned char usb_tx_buf[8];
107
108 struct tx_t {
109 unsigned char data_buf[SASEM_DATA_BUF_SZ]; /* user data buffer */
110 struct completion finished; /* wait for write to finish */
111 atomic_t busy; /* write in progress */
112 int status; /* status of tx completion */
113 } tx;
114
115 /* for dealing with repeat codes (wish there was a toggle bit!) */
116 struct timeval presstime;
117 char lastcode[8];
118 int codesaved;
119};
120
121/* VFD file operations */
Mauro Carvalho Chehab0f9313a2010-07-27 18:44:45 -0300122static const struct file_operations vfd_fops = {
Jarod Wilsonc5ac4572010-07-26 20:31:11 -0300123 .owner = THIS_MODULE,
124 .open = &vfd_open,
125 .write = &vfd_write,
126 .unlocked_ioctl = &vfd_ioctl,
127 .release = &vfd_close,
128};
129
130/* USB Device ID for Sasem USB Control Board */
131static struct usb_device_id sasem_usb_id_table[] = {
132 /* Sasem USB Control Board */
133 { USB_DEVICE(0x11ba, 0x0101) },
134 /* Terminating entry */
135 {}
136};
137
138/* USB Device data */
139static struct usb_driver sasem_driver = {
140 .name = MOD_NAME,
141 .probe = sasem_probe,
142 .disconnect = sasem_disconnect,
143 .id_table = sasem_usb_id_table,
144};
145
146static struct usb_class_driver sasem_class = {
147 .name = DEVICE_NAME,
148 .fops = &vfd_fops,
149 .minor_base = VFD_MINOR_BASE,
150};
151
152/* to prevent races between open() and disconnect() */
153static DEFINE_MUTEX(disconnect_lock);
154
155static int debug;
156
157
158/*** M O D U L E C O D E ***/
159
160MODULE_AUTHOR(MOD_AUTHOR);
161MODULE_DESCRIPTION(MOD_DESC);
162MODULE_LICENSE("GPL");
163module_param(debug, int, S_IRUGO | S_IWUSR);
164MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
165
166static void delete_context(struct sasem_context *context)
167{
168 usb_free_urb(context->tx_urb); /* VFD */
169 usb_free_urb(context->rx_urb); /* IR */
170 lirc_buffer_free(context->driver->rbuf);
171 kfree(context->driver->rbuf);
172 kfree(context->driver);
173 kfree(context);
174
175 if (debug)
176 printk(KERN_INFO "%s: context deleted\n", __func__);
177}
178
179static void deregister_from_lirc(struct sasem_context *context)
180{
181 int retval;
182 int minor = context->driver->minor;
183
184 retval = lirc_unregister_driver(minor);
185 if (retval)
186 err("%s: unable to deregister from lirc (%d)",
187 __func__, retval);
188 else
189 printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n",
190 minor);
191
192}
193
194/**
195 * Called when the VFD device (e.g. /dev/usb/lcd)
196 * is opened by the application.
197 */
198static int vfd_open(struct inode *inode, struct file *file)
199{
200 struct usb_interface *interface;
201 struct sasem_context *context = NULL;
202 int subminor;
203 int retval = 0;
204
205 /* prevent races with disconnect */
206 mutex_lock(&disconnect_lock);
207
208 subminor = iminor(inode);
209 interface = usb_find_interface(&sasem_driver, subminor);
210 if (!interface) {
211 err("%s: could not find interface for minor %d",
212 __func__, subminor);
213 retval = -ENODEV;
214 goto exit;
215 }
216 context = usb_get_intfdata(interface);
217
218 if (!context) {
219 err("%s: no context found for minor %d",
220 __func__, subminor);
221 retval = -ENODEV;
222 goto exit;
223 }
224
225 mutex_lock(&context->ctx_lock);
226
227 if (context->vfd_isopen) {
228 err("%s: VFD port is already open", __func__);
229 retval = -EBUSY;
230 } else {
231 context->vfd_isopen = 1;
232 file->private_data = context;
233 printk(KERN_INFO "VFD port opened\n");
234 }
235
236 mutex_unlock(&context->ctx_lock);
237
238exit:
239 mutex_unlock(&disconnect_lock);
240 return retval;
241}
242
243/**
244 * Called when the VFD device (e.g. /dev/usb/lcd)
245 * is closed by the application.
246 */
247static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg)
248{
249 struct sasem_context *context = NULL;
250
251 context = (struct sasem_context *) file->private_data;
252
253 if (!context) {
254 err("%s: no context for device", __func__);
255 return -ENODEV;
256 }
257
258 mutex_lock(&context->ctx_lock);
259
260 switch (cmd) {
261 case IOCTL_LCD_CONTRAST:
262 if (arg > 1000)
263 arg = 1000;
264 context->vfd_contrast = (unsigned int)arg;
265 break;
266 default:
267 printk(KERN_INFO "Unknown IOCTL command\n");
268 mutex_unlock(&context->ctx_lock);
269 return -ENOIOCTLCMD; /* not supported */
270 }
271
272 mutex_unlock(&context->ctx_lock);
273 return 0;
274}
275
276/**
277 * Called when the VFD device (e.g. /dev/usb/lcd)
278 * is closed by the application.
279 */
280static int vfd_close(struct inode *inode, struct file *file)
281{
282 struct sasem_context *context = NULL;
283 int retval = 0;
284
285 context = (struct sasem_context *) file->private_data;
286
287 if (!context) {
288 err("%s: no context for device", __func__);
289 return -ENODEV;
290 }
291
292 mutex_lock(&context->ctx_lock);
293
294 if (!context->vfd_isopen) {
295 err("%s: VFD is not open", __func__);
296 retval = -EIO;
297 } else {
298 context->vfd_isopen = 0;
299 printk(KERN_INFO "VFD port closed\n");
300 if (!context->dev_present && !context->ir_isopen) {
301
302 /* Device disconnected before close and IR port is
303 * not open. If IR port is open, context will be
304 * deleted by ir_close. */
305 mutex_unlock(&context->ctx_lock);
306 delete_context(context);
307 return retval;
308 }
309 }
310
311 mutex_unlock(&context->ctx_lock);
312 return retval;
313}
314
315/**
316 * Sends a packet to the VFD.
317 */
318static int send_packet(struct sasem_context *context)
319{
320 unsigned int pipe;
321 int interval = 0;
322 int retval = 0;
323
324 pipe = usb_sndintpipe(context->dev,
325 context->tx_endpoint->bEndpointAddress);
326 interval = context->tx_endpoint->bInterval;
327
328 usb_fill_int_urb(context->tx_urb, context->dev, pipe,
329 context->usb_tx_buf, sizeof(context->usb_tx_buf),
330 usb_tx_callback, context, interval);
331
332 context->tx_urb->actual_length = 0;
333
334 init_completion(&context->tx.finished);
335 atomic_set(&(context->tx.busy), 1);
336
337 retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
338 if (retval) {
339 atomic_set(&(context->tx.busy), 0);
340 err("%s: error submitting urb (%d)", __func__, retval);
341 } else {
342 /* Wait for transmission to complete (or abort) */
343 mutex_unlock(&context->ctx_lock);
344 wait_for_completion(&context->tx.finished);
345 mutex_lock(&context->ctx_lock);
346
347 retval = context->tx.status;
348 if (retval)
349 err("%s: packet tx failed (%d)", __func__, retval);
350 }
351
352 return retval;
353}
354
355/**
356 * Writes data to the VFD. The Sasem VFD is 2x16 characters
357 * and requires data in 9 consecutive USB interrupt packets,
358 * each packet carrying 8 bytes.
359 */
360static ssize_t vfd_write(struct file *file, const char *buf,
361 size_t n_bytes, loff_t *pos)
362{
363 int i;
364 int retval = 0;
365 struct sasem_context *context;
366 int *data_buf;
367
368 context = (struct sasem_context *) file->private_data;
369 if (!context) {
370 err("%s: no context for device", __func__);
371 return -ENODEV;
372 }
373
374 mutex_lock(&context->ctx_lock);
375
376 if (!context->dev_present) {
377 err("%s: no Sasem device present", __func__);
378 retval = -ENODEV;
379 goto exit;
380 }
381
382 if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) {
383 err("%s: invalid payload size", __func__);
384 retval = -EINVAL;
385 goto exit;
386 }
387
388 data_buf = memdup_user(buf, n_bytes);
Dan Carpenter79e7c562010-10-15 05:42:35 +0200389 if (IS_ERR(data_buf)) {
Jiri Slabyff7d3682010-09-04 14:16:26 +0200390 retval = PTR_ERR(data_buf);
391 goto exit;
392 }
Jarod Wilsonc5ac4572010-07-26 20:31:11 -0300393
394 memcpy(context->tx.data_buf, data_buf, n_bytes);
395
396 /* Pad with spaces */
397 for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i)
398 context->tx.data_buf[i] = ' ';
399
400 /* Nine 8 byte packets to be sent */
401 /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
402 * will clear the VFD */
403 for (i = 0; i < 9; i++) {
404 switch (i) {
405 case 0:
406 memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
407 context->usb_tx_buf[1] = (context->vfd_contrast) ?
408 (0x2B - (context->vfd_contrast - 1) / 250)
409 : 0x2B;
410 break;
411 case 1:
412 memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
413 break;
414 case 2:
415 memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
416 break;
417 case 3:
418 memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
419 break;
420 case 4:
421 memcpy(context->usb_tx_buf,
422 context->tx.data_buf + 8, 8);
423 break;
424 case 5:
425 memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
426 break;
427 case 6:
428 memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
429 break;
430 case 7:
431 memcpy(context->usb_tx_buf,
432 context->tx.data_buf + 16, 8);
433 break;
434 case 8:
435 memcpy(context->usb_tx_buf,
436 context->tx.data_buf + 24, 8);
437 break;
438 }
439 retval = send_packet(context);
440 if (retval) {
441
442 err("%s: send packet failed for packet #%d",
443 __func__, i);
444 goto exit;
445 }
446 }
447exit:
448
449 mutex_unlock(&context->ctx_lock);
450
451 return (!retval) ? n_bytes : retval;
452}
453
454/**
455 * Callback function for USB core API: transmit data
456 */
457static void usb_tx_callback(struct urb *urb)
458{
459 struct sasem_context *context;
460
461 if (!urb)
462 return;
463 context = (struct sasem_context *) urb->context;
464 if (!context)
465 return;
466
467 context->tx.status = urb->status;
468
469 /* notify waiters that write has finished */
470 atomic_set(&context->tx.busy, 0);
471 complete(&context->tx.finished);
472
473 return;
474}
475
476/**
477 * Called by lirc_dev when the application opens /dev/lirc
478 */
479static int ir_open(void *data)
480{
481 int retval = 0;
482 struct sasem_context *context;
483
484 /* prevent races with disconnect */
485 mutex_lock(&disconnect_lock);
486
487 context = (struct sasem_context *) data;
488
489 mutex_lock(&context->ctx_lock);
490
491 if (context->ir_isopen) {
492 err("%s: IR port is already open", __func__);
493 retval = -EBUSY;
494 goto exit;
495 }
496
497 usb_fill_int_urb(context->rx_urb, context->dev,
498 usb_rcvintpipe(context->dev,
499 context->rx_endpoint->bEndpointAddress),
500 context->usb_rx_buf, sizeof(context->usb_rx_buf),
501 usb_rx_callback, context, context->rx_endpoint->bInterval);
502
503 retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
504
505 if (retval)
506 err("%s: usb_submit_urb failed for ir_open (%d)",
507 __func__, retval);
508 else {
509 context->ir_isopen = 1;
510 printk(KERN_INFO "IR port opened\n");
511 }
512
513exit:
514 mutex_unlock(&context->ctx_lock);
515
516 mutex_unlock(&disconnect_lock);
Julia Lawall4d9db972010-08-16 18:26:36 +0200517 return retval;
Jarod Wilsonc5ac4572010-07-26 20:31:11 -0300518}
519
520/**
521 * Called by lirc_dev when the application closes /dev/lirc
522 */
523static void ir_close(void *data)
524{
525 struct sasem_context *context;
526
527 context = (struct sasem_context *)data;
528 if (!context) {
529 err("%s: no context for device", __func__);
530 return;
531 }
532
533 mutex_lock(&context->ctx_lock);
534
535 usb_kill_urb(context->rx_urb);
536 context->ir_isopen = 0;
537 printk(KERN_INFO "IR port closed\n");
538
539 if (!context->dev_present) {
540
541 /*
542 * Device disconnected while IR port was
543 * still open. Driver was not deregistered
544 * at disconnect time, so do it now.
545 */
546 deregister_from_lirc(context);
547
548 if (!context->vfd_isopen) {
549
550 mutex_unlock(&context->ctx_lock);
551 delete_context(context);
552 return;
553 }
554 /* If VFD port is open, context will be deleted by vfd_close */
555 }
556
557 mutex_unlock(&context->ctx_lock);
558 return;
559}
560
561/**
562 * Process the incoming packet
563 */
564static void incoming_packet(struct sasem_context *context,
565 struct urb *urb)
566{
567 int len = urb->actual_length;
568 unsigned char *buf = urb->transfer_buffer;
569 long ms;
570 struct timeval tv;
571
572 if (len != 8) {
573 printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n",
574 __func__, len);
575 return;
576 }
577
578#ifdef DEBUG
579 int i;
580 for (i = 0; i < 8; ++i)
581 printk(KERN_INFO "%02x ", buf[i]);
582 printk(KERN_INFO "\n");
583#endif
584
585 /*
586 * Lirc could deal with the repeat code, but we really need to block it
587 * if it arrives too late. Otherwise we could repeat the wrong code.
588 */
589
590 /* get the time since the last button press */
591 do_gettimeofday(&tv);
592 ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
593 (tv.tv_usec - context->presstime.tv_usec) / 1000;
594
595 if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
596 /*
597 * the repeat code is being sent, so we copy
598 * the old code to LIRC
599 */
600
601 /*
602 * NOTE: Only if the last code was less than 250ms ago
603 * - no one should be able to push another (undetected) button
604 * in that time and then get a false repeat of the previous
605 * press but it is long enough for a genuine repeat
606 */
607 if ((ms < 250) && (context->codesaved != 0)) {
608 memcpy(buf, &context->lastcode, 8);
609 context->presstime.tv_sec = tv.tv_sec;
610 context->presstime.tv_usec = tv.tv_usec;
611 }
612 } else {
613 /* save the current valid code for repeats */
614 memcpy(&context->lastcode, buf, 8);
615 /*
616 * set flag to signal a valid code was save;
617 * just for safety reasons
618 */
619 context->codesaved = 1;
620 context->presstime.tv_sec = tv.tv_sec;
621 context->presstime.tv_usec = tv.tv_usec;
622 }
623
624 lirc_buffer_write(context->driver->rbuf, buf);
625 wake_up(&context->driver->rbuf->wait_poll);
626}
627
628/**
629 * Callback function for USB core API: receive data
630 */
631static void usb_rx_callback(struct urb *urb)
632{
633 struct sasem_context *context;
634
635 if (!urb)
636 return;
637 context = (struct sasem_context *) urb->context;
638 if (!context)
639 return;
640
641 switch (urb->status) {
642
643 case -ENOENT: /* usbcore unlink successful! */
644 return;
645
646 case 0:
647 if (context->ir_isopen)
648 incoming_packet(context, urb);
649 break;
650
651 default:
652 printk(KERN_WARNING "%s: status (%d): ignored",
653 __func__, urb->status);
654 break;
655 }
656
657 usb_submit_urb(context->rx_urb, GFP_ATOMIC);
658 return;
659}
660
661
662
663/**
664 * Callback function for USB core API: Probe
665 */
666static int sasem_probe(struct usb_interface *interface,
667 const struct usb_device_id *id)
668{
669 struct usb_device *dev = NULL;
670 struct usb_host_interface *iface_desc = NULL;
671 struct usb_endpoint_descriptor *rx_endpoint = NULL;
672 struct usb_endpoint_descriptor *tx_endpoint = NULL;
673 struct urb *rx_urb = NULL;
674 struct urb *tx_urb = NULL;
675 struct lirc_driver *driver = NULL;
676 struct lirc_buffer *rbuf = NULL;
677 int lirc_minor = 0;
678 int num_endpoints;
679 int retval = 0;
680 int vfd_ep_found;
681 int ir_ep_found;
682 int alloc_status;
683 struct sasem_context *context = NULL;
684 int i;
685
686 printk(KERN_INFO "%s: found Sasem device\n", __func__);
687
688
689 dev = usb_get_dev(interface_to_usbdev(interface));
690 iface_desc = interface->cur_altsetting;
691 num_endpoints = iface_desc->desc.bNumEndpoints;
692
693 /*
694 * Scan the endpoint list and set:
695 * first input endpoint = IR endpoint
696 * first output endpoint = VFD endpoint
697 */
698
699 ir_ep_found = 0;
700 vfd_ep_found = 0;
701
702 for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
703
704 struct usb_endpoint_descriptor *ep;
705 int ep_dir;
706 int ep_type;
707 ep = &iface_desc->endpoint [i].desc;
708 ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
709 ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
710
711 if (!ir_ep_found &&
712 ep_dir == USB_DIR_IN &&
713 ep_type == USB_ENDPOINT_XFER_INT) {
714
715 rx_endpoint = ep;
716 ir_ep_found = 1;
717 if (debug)
718 printk(KERN_INFO "%s: found IR endpoint\n",
719 __func__);
720
721 } else if (!vfd_ep_found &&
722 ep_dir == USB_DIR_OUT &&
723 ep_type == USB_ENDPOINT_XFER_INT) {
724
725 tx_endpoint = ep;
726 vfd_ep_found = 1;
727 if (debug)
728 printk(KERN_INFO "%s: found VFD endpoint\n",
729 __func__);
730 }
731 }
732
733 /* Input endpoint is mandatory */
734 if (!ir_ep_found) {
735
736 err("%s: no valid input (IR) endpoint found.", __func__);
737 retval = -ENODEV;
738 goto exit;
739 }
740
741 if (!vfd_ep_found)
742 printk(KERN_INFO "%s: no valid output (VFD) endpoint found.\n",
743 __func__);
744
745
746 /* Allocate memory */
747 alloc_status = 0;
748
749 context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL);
750 if (!context) {
751 err("%s: kzalloc failed for context", __func__);
752 alloc_status = 1;
753 goto alloc_status_switch;
754 }
755 driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
756 if (!driver) {
757 err("%s: kzalloc failed for lirc_driver", __func__);
758 alloc_status = 2;
759 goto alloc_status_switch;
760 }
761 rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
762 if (!rbuf) {
763 err("%s: kmalloc failed for lirc_buffer", __func__);
764 alloc_status = 3;
765 goto alloc_status_switch;
766 }
767 if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
768 err("%s: lirc_buffer_init failed", __func__);
769 alloc_status = 4;
770 goto alloc_status_switch;
771 }
772 rx_urb = usb_alloc_urb(0, GFP_KERNEL);
773 if (!rx_urb) {
774 err("%s: usb_alloc_urb failed for IR urb", __func__);
775 alloc_status = 5;
776 goto alloc_status_switch;
777 }
778 if (vfd_ep_found) {
779 tx_urb = usb_alloc_urb(0, GFP_KERNEL);
780 if (!tx_urb) {
781 err("%s: usb_alloc_urb failed for VFD urb",
782 __func__);
783 alloc_status = 6;
784 goto alloc_status_switch;
785 }
786 }
787
788 mutex_init(&context->ctx_lock);
789
790 strcpy(driver->name, MOD_NAME);
791 driver->minor = -1;
792 driver->code_length = 64;
793 driver->sample_rate = 0;
794 driver->features = LIRC_CAN_REC_LIRCCODE;
795 driver->data = context;
796 driver->rbuf = rbuf;
797 driver->set_use_inc = ir_open;
798 driver->set_use_dec = ir_close;
799 driver->dev = &interface->dev;
800 driver->owner = THIS_MODULE;
801
802 mutex_lock(&context->ctx_lock);
803
804 lirc_minor = lirc_register_driver(driver);
805 if (lirc_minor < 0) {
806 err("%s: lirc_register_driver failed", __func__);
807 alloc_status = 7;
Jiri Slabyff7d3682010-09-04 14:16:26 +0200808 retval = lirc_minor;
809 goto unlock;
Jarod Wilsonc5ac4572010-07-26 20:31:11 -0300810 } else
811 printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n",
812 __func__, lirc_minor);
813
814alloc_status_switch:
815
816 switch (alloc_status) {
817
818 case 7:
819 if (vfd_ep_found)
820 usb_free_urb(tx_urb);
821 case 6:
822 usb_free_urb(rx_urb);
823 case 5:
824 lirc_buffer_free(rbuf);
825 case 4:
826 kfree(rbuf);
827 case 3:
828 kfree(driver);
829 case 2:
830 kfree(context);
831 context = NULL;
832 case 1:
833 retval = -ENOMEM;
Jiri Slabyff7d3682010-09-04 14:16:26 +0200834 goto unlock;
Jarod Wilsonc5ac4572010-07-26 20:31:11 -0300835 }
836
837 /* Needed while unregistering! */
838 driver->minor = lirc_minor;
839
840 context->dev = dev;
841 context->dev_present = 1;
842 context->rx_endpoint = rx_endpoint;
843 context->rx_urb = rx_urb;
844 if (vfd_ep_found) {
845 context->tx_endpoint = tx_endpoint;
846 context->tx_urb = tx_urb;
847 context->vfd_contrast = 1000; /* range 0 - 1000 */
848 }
849 context->driver = driver;
850
851 usb_set_intfdata(interface, context);
852
853 if (vfd_ep_found) {
854
855 if (debug)
856 printk(KERN_INFO "Registering VFD with sysfs\n");
857 if (usb_register_dev(interface, &sasem_class))
858 /* Not a fatal error, so ignore */
859 printk(KERN_INFO "%s: could not get a minor number "
860 "for VFD\n", __func__);
861 }
862
863 printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n",
864 __func__, dev->bus->busnum, dev->devnum);
Jiri Slabyff7d3682010-09-04 14:16:26 +0200865unlock:
Jarod Wilsonc5ac4572010-07-26 20:31:11 -0300866 mutex_unlock(&context->ctx_lock);
867exit:
868 return retval;
869}
870
871/**
872 * Callback function for USB core API: disonnect
873 */
874static void sasem_disconnect(struct usb_interface *interface)
875{
876 struct sasem_context *context;
877
878 /* prevent races with ir_open()/vfd_open() */
879 mutex_lock(&disconnect_lock);
880
881 context = usb_get_intfdata(interface);
882 mutex_lock(&context->ctx_lock);
883
884 printk(KERN_INFO "%s: Sasem device disconnected\n", __func__);
885
886 usb_set_intfdata(interface, NULL);
887 context->dev_present = 0;
888
889 /* Stop reception */
890 usb_kill_urb(context->rx_urb);
891
892 /* Abort ongoing write */
893 if (atomic_read(&context->tx.busy)) {
894
895 usb_kill_urb(context->tx_urb);
896 wait_for_completion(&context->tx.finished);
897 }
898
899 /* De-register from lirc_dev if IR port is not open */
900 if (!context->ir_isopen)
901 deregister_from_lirc(context);
902
903 usb_deregister_dev(interface, &sasem_class);
904
905 mutex_unlock(&context->ctx_lock);
906
907 if (!context->ir_isopen && !context->vfd_isopen)
908 delete_context(context);
909
910 mutex_unlock(&disconnect_lock);
911}
912
913static int __init sasem_init(void)
914{
915 int rc;
916
917 printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n");
918 printk(KERN_INFO MOD_AUTHOR "\n");
919
920 rc = usb_register(&sasem_driver);
921 if (rc < 0) {
922 err("%s: usb register failed (%d)", __func__, rc);
923 return -ENODEV;
924 }
925 return 0;
926}
927
928static void __exit sasem_exit(void)
929{
930 usb_deregister(&sasem_driver);
931 printk(KERN_INFO "module removed. Goodbye!\n");
932}
933
934
935module_init(sasem_init);
936module_exit(sasem_exit);