Added lirc.
[irreco.git] / lirc-0.8.4a / drivers / lirc_sasem / lirc_sasem.c
blob45ce784817e2783f5f8282aa3fb75e40a54057dc
1 /* $Id: lirc_sasem.c,v 1.22 2008/05/16 22:02:13 uzuul Exp $ */
3 /* lirc_sasem.c - USB remote support for LIRC
4 * Version 0.5
6 * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de>
7 * Tim Davies <tim@opensystems.net.au>
9 * This driver was derived from:
10 * Venky Raju <dev@venky.ws>
11 * "lirc_imon - "LIRC plugin/VFD driver for Ahanix/Soundgraph IMON IR/VFD"
12 * Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
13 * "lirc_atiusb - USB remote support for LIRC"
14 * Culver Consulting Services <henry@culcon.com>'s 2003
15 * "Sasem OnAir VFD/IR USB driver"
18 * 2004/06/13 - 0.1
19 * initial version
21 * 2004/06/28 - 0.2
22 * added file system support to write data to VFD device (used
23 * in conjunction with LCDProc)
25 * 2004/11/22 - 0.3
26 * Ported to 2.6 kernel
27 * - Tim Davies <tim@opensystems.net.au>
29 * 2005/03/29 - 0.4
30 * A few tidyups and keypress timings
31 * - Tim Davies <tim@opensystems.net.au>
33 * 2005/06/23 - 0.5
34 * A complete rewrite (shamelessly) based on lirc_imon.c
35 * Tim Davies <tim@opensystems.net.au>
37 * NOTE - The LCDproc iMon driver should work with this module. More info at
38 * http://www.frogstorm.info/sasem
42 * This program is free software; you can redistribute it and/or modify
43 * it under the terms of the GNU General Public License as published by
44 * the Free Software Foundation; either version 2 of the License, or
45 * (at your option) any later version.
47 * This program is distributed in the hope that it will be useful,
48 * but WITHOUT ANY WARRANTY; without even the implied warranty of
49 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50 * GNU General Public License for more details.
52 * You should have received a copy of the GNU General Public License
53 * along with this program; if not, write to the Free Software
54 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
57 #include <linux/version.h>
59 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 22)
60 #error "*** Sorry, this driver requires kernel version 2.4.22 or higher"
61 #endif
63 #include <linux/autoconf.h>
65 #include <linux/errno.h>
66 #include <linux/init.h>
67 #include <linux/kernel.h>
68 #include <linux/module.h>
69 #include <linux/slab.h>
70 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
71 #include <asm/uaccess.h>
72 #else
73 #include <linux/uaccess.h>
74 #endif
75 #include <linux/usb.h>
77 #include "drivers/kcompat.h"
78 #include "drivers/lirc.h"
79 #include "drivers/lirc_dev/lirc_dev.h"
82 #define MOD_AUTHOR "Oliver Stabel <oliver.stabel@gmx.de>, " \
83 "Tim Davies <tim@opensystems.net.au>"
84 #define MOD_DESC "USB Driver for Sasem Remote Controller V1.1"
85 #define MOD_NAME "lirc_sasem"
86 #define MOD_VERSION "0.5"
88 #define VFD_MINOR_BASE 144 /* Same as LCD */
89 #define DEVFS_MODE S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
90 #define DEVFS_NAME LIRC_DEVFS_PREFIX "lcd%d"
92 #define BUF_CHUNK_SIZE 8
93 #define BUF_SIZE 128
95 #define SUCCESS 0
96 #define TRUE 1
97 #define FALSE 0
99 #define IOCTL_LCD_CONTRAST 1
101 /* ------------------------------------------------------------
102 * P R O T O T Y P E S
103 * ------------------------------------------------------------
106 /* USB Callback prototypes */
107 #ifdef KERNEL_2_5
108 static int sasem_probe(struct usb_interface *interface,
109 const struct usb_device_id *id);
110 static void sasem_disconnect(struct usb_interface *interface);
111 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
112 static void usb_rx_callback(struct urb *urb, struct pt_regs *regs);
113 static void usb_tx_callback(struct urb *urb, struct pt_regs *regs);
114 #else
115 static void usb_rx_callback(struct urb *urb);
116 static void usb_tx_callback(struct urb *urb);
117 #endif
118 #else
119 static void *sasem_probe(struct usb_device *dev, unsigned int intf,
120 const struct usb_device_id *id);
121 static void sasem_disconnect(struct usb_device *dev, void *data);
122 static void usb_rx_callback(struct urb *urb);
123 static void usb_tx_callback(struct urb *urb);
124 #endif
126 /* VFD file_operations function prototypes */
127 static int vfd_open(struct inode *inode, struct file *file);
128 static int vfd_ioctl(struct inode *inode, struct file *file,
129 unsigned cmd, unsigned long arg);
130 static int vfd_close(struct inode *inode, struct file *file);
131 static ssize_t vfd_write(struct file *file, const char *buf,
132 size_t n_bytes, loff_t *pos);
134 /* LIRC plugin function prototypes */
135 static int ir_open(void *data);
136 static void ir_close(void *data);
138 /* Driver init/exit prototypes */
139 static int __init sasem_init(void);
140 static void __exit sasem_exit(void);
142 /* ------------------------------------------------------------
143 * G L O B A L S
144 * ------------------------------------------------------------
147 struct sasem_context {
149 struct usb_device *dev;
150 int vfd_isopen; /* VFD port has been opened */
151 unsigned int vfd_contrast; /* VFD contrast */
152 #if !defined(KERNEL_2_5)
153 int subminor; /* index into minor_table */
154 devfs_handle_t devfs;
155 #endif
156 int ir_isopen; /* IR port has been opened */
157 int dev_present; /* USB device presence */
158 struct semaphore sem; /* to lock this object */
159 wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
161 struct lirc_plugin *plugin;
162 struct usb_endpoint_descriptor *rx_endpoint;
163 struct usb_endpoint_descriptor *tx_endpoint;
164 struct urb *rx_urb;
165 struct urb *tx_urb;
166 unsigned char usb_rx_buf [8];
167 unsigned char usb_tx_buf [8];
169 struct tx_t {
170 unsigned char data_buf [32]; /* user data buffer */
171 struct completion finished; /* wait for write to finish */
172 atomic_t busy; /* write in progress */
173 int status; /* status of tx completion */
174 } tx;
176 /* for dealing with repeat codes (wish there was a toggle bit!) */
177 struct timeval presstime;
178 char lastcode[8];
179 int codesaved;
182 #define LOCK_CONTEXT down(&context->sem)
183 #define UNLOCK_CONTEXT up(&context->sem)
185 /* VFD file operations */
186 static struct file_operations vfd_fops = {
188 .owner = THIS_MODULE,
189 .open = &vfd_open,
190 .write = &vfd_write,
191 .ioctl = &vfd_ioctl,
192 .release = &vfd_close
195 /* USB Device ID for Sasem USB Control Board */
196 static struct usb_device_id sasem_usb_id_table [] = {
197 /* Sasem USB Control Board */
198 { USB_DEVICE(0x11ba, 0x0101) },
199 /* Terminating entry */
203 /* USB Device data */
204 static struct usb_driver sasem_driver = {
205 LIRC_THIS_MODULE(.owner = THIS_MODULE)
206 .name = MOD_NAME,
207 .probe = sasem_probe,
208 .disconnect = sasem_disconnect,
209 .id_table = sasem_usb_id_table,
210 #if !defined(KERNEL_2_5)
211 .fops = &vfd_fops,
212 .minor = VFD_MINOR_BASE,
213 #endif
216 #ifdef KERNEL_2_5
217 static struct usb_class_driver sasem_class = {
218 .name = DEVFS_NAME,
219 .fops = &vfd_fops,
220 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15)
221 .mode = DEVFS_MODE,
222 #endif
223 .minor_base = VFD_MINOR_BASE,
225 #endif
227 /* to prevent races between open() and disconnect() */
228 static DECLARE_MUTEX(disconnect_sem);
230 static int debug;
232 #if !defined(KERNEL_2_5)
234 #define MAX_DEVICES 4 /* In case there's more than one Sasem device */
235 static struct sasem_context *minor_table [MAX_DEVICES];
237 /* the global usb devfs handle */
238 extern devfs_handle_t usb_devfs_handle;
240 #endif
242 /* ------------------------------------------------------------
243 * M O D U L E C O D E
244 * ------------------------------------------------------------
247 MODULE_AUTHOR(MOD_AUTHOR);
248 MODULE_DESCRIPTION(MOD_DESC);
249 MODULE_LICENSE("GPL");
250 module_param(debug, int, 0);
251 MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
253 static inline void delete_context(struct sasem_context *context)
255 usb_free_urb(context->tx_urb); /* VFD */
256 usb_free_urb(context->rx_urb); /* IR */
257 lirc_buffer_free(context->plugin->rbuf);
258 kfree(context->plugin->rbuf);
259 kfree(context->plugin);
260 kfree(context);
262 if (debug)
263 info("%s: context deleted", __FUNCTION__);
266 static inline void deregister_from_lirc(struct sasem_context *context)
268 int retval;
269 int minor = context->plugin->minor;
271 retval = lirc_unregister_plugin(minor);
272 if (retval)
273 err("%s: unable to deregister from lirc (%d)",
274 __FUNCTION__, retval);
275 else
276 info("Deregistered Sasem plugin (minor:%d)", minor);
281 * Called when the VFD device (e.g. /dev/usb/lcd)
282 * is opened by the application.
284 static int vfd_open(struct inode *inode, struct file *file)
286 #ifdef KERNEL_2_5
287 struct usb_interface *interface;
288 #endif
289 struct sasem_context *context = NULL;
290 int subminor;
291 int retval = SUCCESS;
293 /* prevent races with disconnect */
294 down(&disconnect_sem);
296 #ifdef KERNEL_2_5
297 subminor = iminor(inode);
298 interface = usb_find_interface(&sasem_driver, subminor);
299 if (!interface) {
300 err("%s: could not find interface for minor %d",
301 __FUNCTION__, subminor);
302 retval = -ENODEV;
303 goto exit;
305 context = usb_get_intfdata(interface);
306 #else
307 subminor = MINOR(inode->i_rdev) - VFD_MINOR_BASE;
308 if (subminor < 0 || subminor >= MAX_DEVICES) {
309 err("%s: no record of minor %d", __FUNCTION__, subminor);
310 retval = -ENODEV;
311 goto exit;
313 context = minor_table [subminor];
314 #endif
316 if (!context) {
317 err("%s: no context found for minor %d",
318 __FUNCTION__, subminor);
319 retval = -ENODEV;
320 goto exit;
323 LOCK_CONTEXT;
325 if (context->vfd_isopen) {
326 err("%s: VFD port is already open", __FUNCTION__);
327 retval = -EBUSY;
328 } else {
329 MOD_INC_USE_COUNT;
330 context->vfd_isopen = TRUE;
331 file->private_data = context;
332 info("VFD port opened");
335 UNLOCK_CONTEXT;
337 exit:
338 up(&disconnect_sem);
339 return retval;
343 * Called when the VFD device (e.g. /dev/usb/lcd)
344 * is closed by the application.
346 static int vfd_ioctl(struct inode *inode, struct file *file,
347 unsigned cmd, unsigned long arg)
349 struct sasem_context *context = NULL;
351 context = (struct sasem_context *) file->private_data;
353 if (!context) {
354 err("%s: no context for device", __FUNCTION__);
355 return -ENODEV;
358 LOCK_CONTEXT;
360 switch (cmd) {
361 case IOCTL_LCD_CONTRAST:
362 if (arg > 1000)
363 arg = 1000;
364 if (arg < 0)
365 arg = 0;
366 context->vfd_contrast = (unsigned int)arg;
367 break;
368 default:
369 info("Unknown IOCTL command");
370 UNLOCK_CONTEXT;
371 return -ENOIOCTLCMD; /* not supported */
374 UNLOCK_CONTEXT;
375 return 0;
379 * Called when the VFD device (e.g. /dev/usb/lcd)
380 * is closed by the application.
382 static int vfd_close(struct inode *inode, struct file *file)
384 struct sasem_context *context = NULL;
385 int retval = SUCCESS;
387 context = (struct sasem_context *) file->private_data;
389 if (!context) {
390 err("%s: no context for device", __FUNCTION__);
391 return -ENODEV;
394 LOCK_CONTEXT;
396 if (!context->vfd_isopen) {
397 err("%s: VFD is not open", __FUNCTION__);
398 retval = -EIO;
399 } else {
400 context->vfd_isopen = FALSE;
401 MOD_DEC_USE_COUNT;
402 info("VFD port closed");
403 if (!context->dev_present && !context->ir_isopen) {
405 /* Device disconnected before close and IR port is
406 * not open. If IR port is open, context will be
407 * deleted by ir_close. */
408 UNLOCK_CONTEXT;
409 delete_context(context);
410 return retval;
414 UNLOCK_CONTEXT;
415 return retval;
419 * Sends a packet to the VFD.
421 static inline int send_packet(struct sasem_context *context)
423 unsigned int pipe;
424 int interval = 0;
425 int retval = SUCCESS;
427 pipe = usb_sndintpipe(context->dev,
428 context->tx_endpoint->bEndpointAddress);
429 #ifdef KERNEL_2_5
430 interval = context->tx_endpoint->bInterval;
431 #endif /* Use 0 for 2.4 kernels */
433 usb_fill_int_urb(context->tx_urb, context->dev, pipe,
434 context->usb_tx_buf, sizeof(context->usb_tx_buf),
435 usb_tx_callback, context, interval);
437 context->tx_urb->actual_length = 0;
439 init_completion(&context->tx.finished);
440 atomic_set(&(context->tx.busy), 1);
442 #ifdef KERNEL_2_5
443 retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
444 #else
445 retval = usb_submit_urb(context->tx_urb);
446 #endif
447 if (retval != SUCCESS) {
448 atomic_set(&(context->tx.busy), 0);
449 err("%s: error submitting urb (%d)", __FUNCTION__, retval);
450 } else {
451 /* Wait for tranmission to complete (or abort) */
452 UNLOCK_CONTEXT;
453 wait_for_completion(&context->tx.finished);
454 LOCK_CONTEXT;
456 retval = context->tx.status;
457 if (retval != SUCCESS)
458 err("%s: packet tx failed (%d)", __FUNCTION__, retval);
461 return retval;
465 * Writes data to the VFD. The Sasem VFD is 2x16 characters
466 * and requires data in 9 consecutive USB interrupt packets,
467 * each packet carrying 8 bytes.
469 static ssize_t vfd_write(struct file *file, const char *buf,
470 size_t n_bytes, loff_t *pos)
472 int i;
473 int retval = SUCCESS;
474 struct sasem_context *context;
476 context = (struct sasem_context *) file->private_data;
477 if (!context) {
478 err("%s: no context for device", __FUNCTION__);
479 return -ENODEV;
482 LOCK_CONTEXT;
484 if (!context->dev_present) {
485 err("%s: no Sasem device present", __FUNCTION__);
486 retval = -ENODEV;
487 goto exit;
490 if (n_bytes <= 0 || n_bytes > 32) {
491 err("%s: invalid payload size", __FUNCTION__);
492 retval = -EINVAL;
493 goto exit;
496 copy_from_user(context->tx.data_buf, buf, n_bytes);
498 /* Pad with spaces */
499 for (i = n_bytes; i < 32; ++i)
500 context->tx.data_buf [i] = ' ';
502 /* Nine 8 byte packets to be sent */
503 /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
504 * will clear the VFD */
505 for (i = 0; i < 9; i++) {
506 switch (i) {
507 case 0:
508 memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
509 context->usb_tx_buf[1] = (context->vfd_contrast) ?
510 (0x2B - (context->vfd_contrast - 1) / 250):0x2B;
511 break;
512 case 1:
513 memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
514 break;
515 case 2:
516 memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
517 break;
518 case 3:
519 memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
520 break;
521 case 4:
522 memcpy(context->usb_tx_buf,
523 context->tx.data_buf + 8, 8);
524 break;
525 case 5:
526 memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
527 break;
528 case 6:
529 memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
530 break;
531 case 7:
532 memcpy(context->usb_tx_buf,
533 context->tx.data_buf + 16, 8);
534 break;
535 case 8:
536 memcpy(context->usb_tx_buf,
537 context->tx.data_buf + 24, 8);
538 break;
540 retval = send_packet(context);
541 if (retval != SUCCESS) {
543 err("%s: send packet failed for packet #%d",
544 __FUNCTION__, i);
545 goto exit;
548 exit:
550 UNLOCK_CONTEXT;
552 return (retval == SUCCESS) ? n_bytes : retval;
556 * Callback function for USB core API: transmit data
558 #if defined(KERNEL_2_5) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
559 static void usb_tx_callback(struct urb *urb, struct pt_regs *regs)
560 #else
561 static void usb_tx_callback(struct urb *urb)
562 #endif
564 struct sasem_context *context;
566 if (!urb)
567 return;
568 context = (struct sasem_context *) urb->context;
569 if (!context)
570 return;
572 context->tx.status = urb->status;
574 /* notify waiters that write has finished */
575 atomic_set(&context->tx.busy, 0);
576 complete(&context->tx.finished);
578 return;
582 * Called by lirc_dev when the application opens /dev/lirc
584 static int ir_open(void *data)
586 int retval = SUCCESS;
587 struct sasem_context *context;
589 /* prevent races with disconnect */
590 down(&disconnect_sem);
592 context = (struct sasem_context *) data;
594 LOCK_CONTEXT;
596 if (context->ir_isopen) {
597 err("%s: IR port is already open", __FUNCTION__);
598 retval = -EBUSY;
599 goto exit;
602 usb_fill_int_urb(context->rx_urb, context->dev,
603 usb_rcvintpipe(context->dev,
604 context->rx_endpoint->bEndpointAddress),
605 context->usb_rx_buf, sizeof(context->usb_rx_buf),
606 usb_rx_callback, context, context->rx_endpoint->bInterval);
608 #ifdef KERNEL_2_5
609 retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
610 #else
611 retval = usb_submit_urb(context->rx_urb);
612 #endif
614 if (retval)
615 err("%s: usb_submit_urb failed for ir_open (%d)",
616 __FUNCTION__, retval);
617 else {
618 MOD_INC_USE_COUNT;
619 context->ir_isopen = TRUE;
620 info("IR port opened");
623 exit:
624 UNLOCK_CONTEXT;
626 up(&disconnect_sem);
627 return SUCCESS;
631 * Called by lirc_dev when the application closes /dev/lirc
633 static void ir_close(void *data)
635 struct sasem_context *context;
637 context = (struct sasem_context *)data;
638 if (!context) {
639 err("%s: no context for device", __FUNCTION__);
640 return;
643 LOCK_CONTEXT;
645 usb_kill_urb(context->rx_urb);
646 context->ir_isopen = FALSE;
647 MOD_DEC_USE_COUNT;
648 info("IR port closed");
650 if (!context->dev_present) {
653 * Device disconnected while IR port was
654 * still open. Plugin was not deregistered
655 * at disconnect time, so do it now.
657 deregister_from_lirc(context);
659 if (!context->vfd_isopen) {
661 UNLOCK_CONTEXT;
662 delete_context(context);
663 return;
665 /* If VFD port is open, context will be deleted by vfd_close */
668 UNLOCK_CONTEXT;
669 return;
673 * Process the incoming packet
675 static inline void incoming_packet(struct sasem_context *context,
676 struct urb *urb)
678 int len = urb->actual_length;
679 unsigned char *buf = urb->transfer_buffer;
680 long ms;
681 struct timeval tv;
683 if (len != 8) {
684 warn("%s: invalid incoming packet size (%d)",
685 __FUNCTION__, len);
686 return;
689 #ifdef DEBUG
690 int i;
691 for (i = 0; i < 8; ++i)
692 printk(KERN_INFO "%02x ", buf [i]);
693 printk(KERN_INFO "\n");
694 #endif
696 /* Lirc could deal with the repeat code, but we really need to block it
697 * if it arrives too late. Otherwise we could repeat the wrong code. */
699 /* get the time since the last button press */
700 do_gettimeofday(&tv);
701 ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
702 (tv.tv_usec - context->presstime.tv_usec) / 1000;
704 if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
705 /* the repeat code is being sent, so we copy
706 * the old code to LIRC */
708 /* NOTE: Only if the last code was less than 250ms ago
709 * - no one should be able to push another (undetected) button
710 * in that time and then get a false repeat of the previous
711 * press but it is long enough for a genuine repeat */
712 if ((ms < 250) && (context->codesaved != 0)) {
713 memcpy(buf, &context->lastcode, 8);
714 context->presstime.tv_sec = tv.tv_sec;
715 context->presstime.tv_usec = tv.tv_usec;
717 } else {
718 /* save the current valid code for repeats */
719 memcpy(&context->lastcode, buf, 8);
720 /* set flag to signal a valid code was save;
721 * just for safety reasons */
722 context->codesaved = 1;
723 context->presstime.tv_sec = tv.tv_sec;
724 context->presstime.tv_usec = tv.tv_usec;
727 lirc_buffer_write_1(context->plugin->rbuf, buf);
728 wake_up(&context->plugin->rbuf->wait_poll);
732 * Callback function for USB core API: receive data
734 #if defined(KERNEL_2_5) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
735 static void usb_rx_callback(struct urb *urb, struct pt_regs *regs)
736 #else
737 static void usb_rx_callback(struct urb *urb)
738 #endif
740 struct sasem_context *context;
742 if (!urb)
743 return;
744 context = (struct sasem_context *) urb->context;
745 if (!context)
746 return;
748 switch (urb->status) {
750 case -ENOENT: /* usbcore unlink successful! */
751 return;
753 case SUCCESS:
754 if (context->ir_isopen)
755 incoming_packet(context, urb);
756 break;
758 default:
759 warn("%s: status (%d): ignored",
760 __FUNCTION__, urb->status);
761 break;
764 #ifdef KERNEL_2_5
765 usb_submit_urb(context->rx_urb, GFP_ATOMIC);
766 #endif
767 return;
773 * Callback function for USB core API: Probe
775 #ifdef KERNEL_2_5
776 static int sasem_probe(struct usb_interface *interface,
777 const struct usb_device_id *id)
778 #else
779 static void *sasem_probe(struct usb_device *dev, unsigned int intf,
780 const struct usb_device_id *id)
781 #endif
783 #ifdef KERNEL_2_5
784 struct usb_device *dev = NULL;
785 struct usb_host_interface *iface_desc = NULL;
786 #else
787 struct usb_interface *interface = NULL;
788 struct usb_interface_descriptor *iface_desc = NULL;
789 char name [10];
790 int subminor = 0;
791 #endif
792 struct usb_endpoint_descriptor *rx_endpoint = NULL;
793 struct usb_endpoint_descriptor *tx_endpoint = NULL;
794 struct urb *rx_urb = NULL;
795 struct urb *tx_urb = NULL;
796 struct lirc_plugin *plugin = NULL;
797 struct lirc_buffer *rbuf = NULL;
798 int lirc_minor = 0;
799 int num_endpoints;
800 int retval = SUCCESS;
801 int vfd_ep_found;
802 int ir_ep_found;
803 int alloc_status;
804 struct sasem_context *context = NULL;
805 int i;
807 info("%s: found Sasem device", __FUNCTION__);
809 #if !defined(KERNEL_2_5)
810 for (subminor = 0; subminor < MAX_DEVICES; ++subminor) {
811 if (minor_table [subminor] == NULL)
812 break;
814 if (subminor == MAX_DEVICES) {
815 err("%s: allowed number of devices already present",
816 __FUNCTION__);
817 retval = -ENOMEM;
818 goto exit;
820 #endif
822 #ifdef KERNEL_2_5
823 dev = usb_get_dev(interface_to_usbdev(interface));
824 iface_desc = interface->cur_altsetting;
825 num_endpoints = iface_desc->desc.bNumEndpoints;
826 #else
827 interface = &dev->actconfig->interface [intf];
828 iface_desc = &interface->altsetting [interface->act_altsetting];
829 num_endpoints = iface_desc->bNumEndpoints;
830 #endif
833 * Scan the endpoint list and set:
834 * first input endpoint = IR endpoint
835 * first output endpoint = VFD endpoint
838 ir_ep_found = FALSE;
839 vfd_ep_found = FALSE;
841 for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
843 struct usb_endpoint_descriptor *ep;
844 int ep_dir;
845 int ep_type;
846 #ifdef KERNEL_2_5
847 ep = &iface_desc->endpoint [i].desc;
848 #else
849 ep = &iface_desc->endpoint [i];
850 #endif
851 ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
852 ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
854 if (!ir_ep_found &&
855 ep_dir == USB_DIR_IN &&
856 ep_type == USB_ENDPOINT_XFER_INT) {
858 rx_endpoint = ep;
859 ir_ep_found = TRUE;
860 if (debug)
861 info("%s: found IR endpoint", __FUNCTION__);
863 } else if (!vfd_ep_found &&
864 ep_dir == USB_DIR_OUT &&
865 ep_type == USB_ENDPOINT_XFER_INT) {
867 tx_endpoint = ep;
868 vfd_ep_found = TRUE;
869 if (debug)
870 info("%s: found VFD endpoint", __FUNCTION__);
874 /* Input endpoint is mandatory */
875 if (!ir_ep_found) {
877 err("%s: no valid input (IR) endpoint found.", __FUNCTION__);
878 retval = -ENODEV;
879 goto exit;
882 /* Warning if no VFD endpoint */
883 if (!vfd_ep_found)
884 info("%s: no valid output (VFD) endpoint found.", __FUNCTION__);
887 /* Allocate memory */
888 alloc_status = SUCCESS;
890 context = kmalloc(sizeof(struct sasem_context), GFP_KERNEL);
891 if (!context) {
892 err("%s: kmalloc failed for context", __FUNCTION__);
893 alloc_status = 1;
894 goto alloc_status_switch;
896 plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
897 if (!plugin) {
898 err("%s: kmalloc failed for lirc_plugin", __FUNCTION__);
899 alloc_status = 2;
900 goto alloc_status_switch;
902 rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
903 if (!rbuf) {
904 err("%s: kmalloc failed for lirc_buffer", __FUNCTION__);
905 alloc_status = 3;
906 goto alloc_status_switch;
908 if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
909 err("%s: lirc_buffer_init failed", __FUNCTION__);
910 alloc_status = 4;
911 goto alloc_status_switch;
913 #ifdef KERNEL_2_5
914 rx_urb = usb_alloc_urb(0, GFP_KERNEL);
915 #else
916 rx_urb = usb_alloc_urb(0);
917 #endif
918 if (!rx_urb) {
919 err("%s: usb_alloc_urb failed for IR urb", __FUNCTION__);
920 alloc_status = 5;
921 goto alloc_status_switch;
923 if (vfd_ep_found) {
924 #ifdef KERNEL_2_5
925 tx_urb = usb_alloc_urb(0, GFP_KERNEL);
926 #else
927 tx_urb = usb_alloc_urb(0);
928 #endif
929 if (!tx_urb) {
930 err("%s: usb_alloc_urb failed for VFD urb",
931 __FUNCTION__);
932 alloc_status = 6;
933 goto alloc_status_switch;
937 /* clear all members of sasem_context and lirc_plugin */
938 memset(context, 0, sizeof(struct sasem_context));
939 init_MUTEX(&context->sem);
941 memset(plugin, 0, sizeof(struct lirc_plugin));
943 strcpy(plugin->name, MOD_NAME);
944 plugin->minor = -1;
945 plugin->code_length = 64;
946 plugin->sample_rate = 0;
947 plugin->features = LIRC_CAN_REC_LIRCCODE;
948 plugin->data = context;
949 plugin->rbuf = rbuf;
950 plugin->set_use_inc = ir_open;
951 plugin->set_use_dec = ir_close;
952 #ifdef LIRC_HAVE_SYSFS
953 plugin->dev = &dev->dev;
954 #endif
955 plugin->owner = THIS_MODULE;
957 LOCK_CONTEXT;
959 lirc_minor = lirc_register_plugin(plugin);
960 if (lirc_minor < 0) {
961 err("%s: lirc_register_plugin failed", __FUNCTION__);
962 alloc_status = 7;
963 UNLOCK_CONTEXT;
964 } else
965 info("%s: Registered Sasem plugin (minor:%d)",
966 __FUNCTION__, lirc_minor);
968 alloc_status_switch:
970 switch (alloc_status) {
972 case 7:
973 if (vfd_ep_found)
974 usb_free_urb(tx_urb);
975 case 6:
976 usb_free_urb(rx_urb);
977 case 5:
978 lirc_buffer_free(rbuf);
979 case 4:
980 kfree(rbuf);
981 case 3:
982 kfree(plugin);
983 case 2:
984 kfree(context);
985 context = NULL;
986 case 1:
987 retval = -ENOMEM;
988 goto exit;
991 /* Needed while unregistering! */
992 plugin->minor = lirc_minor;
994 context->dev = dev;
995 context->dev_present = TRUE;
996 context->rx_endpoint = rx_endpoint;
997 context->rx_urb = rx_urb;
998 if (vfd_ep_found) {
999 context->tx_endpoint = tx_endpoint;
1000 context->tx_urb = tx_urb;
1001 context->vfd_contrast = 1000; /* range 0 - 1000 */
1003 context->plugin = plugin;
1005 #ifdef KERNEL_2_5
1006 usb_set_intfdata(interface, context);
1007 #else
1008 minor_table [subminor] = context;
1009 context->subminor = subminor;
1010 #endif
1012 if (vfd_ep_found) {
1014 if (debug)
1015 info("Registering VFD with devfs");
1016 #ifdef KERNEL_2_5
1017 if (usb_register_dev(interface, &sasem_class))
1018 /* Not a fatal error, so ignore */
1019 info("%s: could not get a minor number for VFD",
1020 __FUNCTION__);
1021 #else
1022 sprintf(name, DEVFS_NAME, subminor);
1023 context->devfs = devfs_register(usb_devfs_handle, name,
1024 DEVFS_FL_DEFAULT,
1025 USB_MAJOR, VFD_MINOR_BASE + subminor,
1026 DEVFS_MODE, &vfd_fops, NULL);
1027 if (!context->devfs)
1028 /* not a fatal error so ignore */
1029 info("%s: devfs register failed for VFD",
1030 __FUNCTION__);
1031 #endif
1034 info("%s: Sasem device on usb<%d:%d> initialized",
1035 __FUNCTION__, dev->bus->busnum, dev->devnum);
1037 UNLOCK_CONTEXT;
1038 exit:
1039 #ifdef KERNEL_2_5
1040 return retval;
1041 #else
1042 return (retval == SUCCESS) ? context : NULL;
1043 #endif
1047 * Callback function for USB core API: disonnect
1049 #ifdef KERNEL_2_5
1050 static void sasem_disconnect(struct usb_interface *interface)
1051 #else
1052 static void sasem_disconnect(struct usb_device *dev, void *data)
1053 #endif
1055 struct sasem_context *context;
1057 /* prevent races with ir_open()/vfd_open() */
1058 down(&disconnect_sem);
1060 #ifdef KERNEL_2_5
1061 context = usb_get_intfdata(interface);
1062 #else
1063 context = (struct sasem_context *)data;
1064 #endif
1065 LOCK_CONTEXT;
1067 info("%s: Sasem device disconnected", __FUNCTION__);
1069 #ifdef KERNEL_2_5
1070 usb_set_intfdata(interface, NULL);
1071 #else
1072 minor_table [context->subminor] = NULL;
1073 #endif
1074 context->dev_present = FALSE;
1076 /* Stop reception */
1077 usb_kill_urb(context->rx_urb);
1079 /* Abort ongoing write */
1080 if (atomic_read(&context->tx.busy)) {
1082 usb_kill_urb(context->tx_urb);
1083 wait_for_completion(&context->tx.finished);
1086 /* De-register from lirc_dev if IR port is not open */
1087 if (!context->ir_isopen)
1088 deregister_from_lirc(context);
1090 #ifdef KERNEL_2_5
1091 usb_deregister_dev(interface, &sasem_class);
1092 #else
1093 if (context->devfs)
1094 devfs_unregister(context->devfs);
1095 #endif
1097 UNLOCK_CONTEXT;
1099 if (!context->ir_isopen && !context->vfd_isopen)
1100 delete_context(context);
1102 up(&disconnect_sem);
1105 static int __init sasem_init(void)
1107 int rc;
1109 info(MOD_DESC ", v" MOD_VERSION);
1110 info(MOD_AUTHOR);
1112 rc = usb_register(&sasem_driver);
1113 if (rc < 0) {
1114 err("%s: usb register failed (%d)", __FUNCTION__, rc);
1115 return -ENODEV;
1117 return SUCCESS;
1120 static void __exit sasem_exit(void)
1122 usb_deregister(&sasem_driver);
1123 info("module removed. Goodbye!");
1127 module_init(sasem_init);
1128 module_exit(sasem_exit);
1130 #if !defined(KERNEL_2_5)
1131 EXPORT_NO_SYMBOLS;
1132 #endif