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
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"
22 * added file system support to write data to VFD device (used
23 * in conjunction with LCDProc)
26 * Ported to 2.6 kernel
27 * - Tim Davies <tim@opensystems.net.au>
30 * A few tidyups and keypress timings
31 * - Tim Davies <tim@opensystems.net.au>
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"
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>
73 #include <linux/uaccess.h>
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
99 #define IOCTL_LCD_CONTRAST 1
101 /* ------------------------------------------------------------
102 * P R O T O T Y P E S
103 * ------------------------------------------------------------
106 /* USB Callback prototypes */
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
);
115 static void usb_rx_callback(struct urb
*urb
);
116 static void usb_tx_callback(struct urb
*urb
);
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
);
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 /* ------------------------------------------------------------
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
;
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
;
166 unsigned char usb_rx_buf
[8];
167 unsigned char usb_tx_buf
[8];
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 */
176 /* for dealing with repeat codes (wish there was a toggle bit!) */
177 struct timeval presstime
;
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
,
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
)
207 .probe
= sasem_probe
,
208 .disconnect
= sasem_disconnect
,
209 .id_table
= sasem_usb_id_table
,
210 #if !defined(KERNEL_2_5)
212 .minor
= VFD_MINOR_BASE
,
217 static struct usb_class_driver sasem_class
= {
220 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15)
223 .minor_base
= VFD_MINOR_BASE
,
227 /* to prevent races between open() and disconnect() */
228 static DECLARE_MUTEX(disconnect_sem
);
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
;
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
);
263 info("%s: context deleted", __FUNCTION__
);
266 static inline void deregister_from_lirc(struct sasem_context
*context
)
269 int minor
= context
->plugin
->minor
;
271 retval
= lirc_unregister_plugin(minor
);
273 err("%s: unable to deregister from lirc (%d)",
274 __FUNCTION__
, retval
);
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
)
287 struct usb_interface
*interface
;
289 struct sasem_context
*context
= NULL
;
291 int retval
= SUCCESS
;
293 /* prevent races with disconnect */
294 down(&disconnect_sem
);
297 subminor
= iminor(inode
);
298 interface
= usb_find_interface(&sasem_driver
, subminor
);
300 err("%s: could not find interface for minor %d",
301 __FUNCTION__
, subminor
);
305 context
= usb_get_intfdata(interface
);
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
);
313 context
= minor_table
[subminor
];
317 err("%s: no context found for minor %d",
318 __FUNCTION__
, subminor
);
325 if (context
->vfd_isopen
) {
326 err("%s: VFD port is already open", __FUNCTION__
);
330 context
->vfd_isopen
= TRUE
;
331 file
->private_data
= context
;
332 info("VFD port opened");
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
;
354 err("%s: no context for device", __FUNCTION__
);
361 case IOCTL_LCD_CONTRAST
:
366 context
->vfd_contrast
= (unsigned int)arg
;
369 info("Unknown IOCTL command");
371 return -ENOIOCTLCMD
; /* not supported */
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
;
390 err("%s: no context for device", __FUNCTION__
);
396 if (!context
->vfd_isopen
) {
397 err("%s: VFD is not open", __FUNCTION__
);
400 context
->vfd_isopen
= FALSE
;
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. */
409 delete_context(context
);
419 * Sends a packet to the VFD.
421 static inline int send_packet(struct sasem_context
*context
)
425 int retval
= SUCCESS
;
427 pipe
= usb_sndintpipe(context
->dev
,
428 context
->tx_endpoint
->bEndpointAddress
);
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);
443 retval
= usb_submit_urb(context
->tx_urb
, GFP_KERNEL
);
445 retval
= usb_submit_urb(context
->tx_urb
);
447 if (retval
!= SUCCESS
) {
448 atomic_set(&(context
->tx
.busy
), 0);
449 err("%s: error submitting urb (%d)", __FUNCTION__
, retval
);
451 /* Wait for tranmission to complete (or abort) */
453 wait_for_completion(&context
->tx
.finished
);
456 retval
= context
->tx
.status
;
457 if (retval
!= SUCCESS
)
458 err("%s: packet tx failed (%d)", __FUNCTION__
, 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
)
473 int retval
= SUCCESS
;
474 struct sasem_context
*context
;
476 context
= (struct sasem_context
*) file
->private_data
;
478 err("%s: no context for device", __FUNCTION__
);
484 if (!context
->dev_present
) {
485 err("%s: no Sasem device present", __FUNCTION__
);
490 if (n_bytes
<= 0 || n_bytes
> 32) {
491 err("%s: invalid payload size", __FUNCTION__
);
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
++) {
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;
513 memcpy(context
->usb_tx_buf
, "\x09\x01\0\0\0\0\0\0", 8);
516 memcpy(context
->usb_tx_buf
, "\x0b\x01\0\0\0\0\0\0", 8);
519 memcpy(context
->usb_tx_buf
, context
->tx
.data_buf
, 8);
522 memcpy(context
->usb_tx_buf
,
523 context
->tx
.data_buf
+ 8, 8);
526 memcpy(context
->usb_tx_buf
, "\x09\x01\0\0\0\0\0\0", 8);
529 memcpy(context
->usb_tx_buf
, "\x0b\x02\0\0\0\0\0\0", 8);
532 memcpy(context
->usb_tx_buf
,
533 context
->tx
.data_buf
+ 16, 8);
536 memcpy(context
->usb_tx_buf
,
537 context
->tx
.data_buf
+ 24, 8);
540 retval
= send_packet(context
);
541 if (retval
!= SUCCESS
) {
543 err("%s: send packet failed for packet #%d",
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
)
561 static void usb_tx_callback(struct urb
*urb
)
564 struct sasem_context
*context
;
568 context
= (struct sasem_context
*) urb
->context
;
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
);
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
;
596 if (context
->ir_isopen
) {
597 err("%s: IR port is already open", __FUNCTION__
);
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
);
609 retval
= usb_submit_urb(context
->rx_urb
, GFP_KERNEL
);
611 retval
= usb_submit_urb(context
->rx_urb
);
615 err("%s: usb_submit_urb failed for ir_open (%d)",
616 __FUNCTION__
, retval
);
619 context
->ir_isopen
= TRUE
;
620 info("IR port opened");
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
;
639 err("%s: no context for device", __FUNCTION__
);
645 usb_kill_urb(context
->rx_urb
);
646 context
->ir_isopen
= FALSE
;
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
) {
662 delete_context(context
);
665 /* If VFD port is open, context will be deleted by vfd_close */
673 * Process the incoming packet
675 static inline void incoming_packet(struct sasem_context
*context
,
678 int len
= urb
->actual_length
;
679 unsigned char *buf
= urb
->transfer_buffer
;
684 warn("%s: invalid incoming packet size (%d)",
691 for (i
= 0; i
< 8; ++i
)
692 printk(KERN_INFO
"%02x ", buf
[i
]);
693 printk(KERN_INFO
"\n");
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
;
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
)
737 static void usb_rx_callback(struct urb
*urb
)
740 struct sasem_context
*context
;
744 context
= (struct sasem_context
*) urb
->context
;
748 switch (urb
->status
) {
750 case -ENOENT
: /* usbcore unlink successful! */
754 if (context
->ir_isopen
)
755 incoming_packet(context
, urb
);
759 warn("%s: status (%d): ignored",
760 __FUNCTION__
, urb
->status
);
765 usb_submit_urb(context
->rx_urb
, GFP_ATOMIC
);
773 * Callback function for USB core API: Probe
776 static int sasem_probe(struct usb_interface
*interface
,
777 const struct usb_device_id
*id
)
779 static void *sasem_probe(struct usb_device
*dev
, unsigned int intf
,
780 const struct usb_device_id
*id
)
784 struct usb_device
*dev
= NULL
;
785 struct usb_host_interface
*iface_desc
= NULL
;
787 struct usb_interface
*interface
= NULL
;
788 struct usb_interface_descriptor
*iface_desc
= NULL
;
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
;
800 int retval
= SUCCESS
;
804 struct sasem_context
*context
= NULL
;
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
)
814 if (subminor
== MAX_DEVICES
) {
815 err("%s: allowed number of devices already present",
823 dev
= usb_get_dev(interface_to_usbdev(interface
));
824 iface_desc
= interface
->cur_altsetting
;
825 num_endpoints
= iface_desc
->desc
.bNumEndpoints
;
827 interface
= &dev
->actconfig
->interface
[intf
];
828 iface_desc
= &interface
->altsetting
[interface
->act_altsetting
];
829 num_endpoints
= iface_desc
->bNumEndpoints
;
833 * Scan the endpoint list and set:
834 * first input endpoint = IR endpoint
835 * first output endpoint = VFD endpoint
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
;
847 ep
= &iface_desc
->endpoint
[i
].desc
;
849 ep
= &iface_desc
->endpoint
[i
];
851 ep_dir
= ep
->bEndpointAddress
& USB_ENDPOINT_DIR_MASK
;
852 ep_type
= ep
->bmAttributes
& USB_ENDPOINT_XFERTYPE_MASK
;
855 ep_dir
== USB_DIR_IN
&&
856 ep_type
== USB_ENDPOINT_XFER_INT
) {
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
) {
870 info("%s: found VFD endpoint", __FUNCTION__
);
874 /* Input endpoint is mandatory */
877 err("%s: no valid input (IR) endpoint found.", __FUNCTION__
);
882 /* Warning if no VFD endpoint */
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
);
892 err("%s: kmalloc failed for context", __FUNCTION__
);
894 goto alloc_status_switch
;
896 plugin
= kmalloc(sizeof(struct lirc_plugin
), GFP_KERNEL
);
898 err("%s: kmalloc failed for lirc_plugin", __FUNCTION__
);
900 goto alloc_status_switch
;
902 rbuf
= kmalloc(sizeof(struct lirc_buffer
), GFP_KERNEL
);
904 err("%s: kmalloc failed for lirc_buffer", __FUNCTION__
);
906 goto alloc_status_switch
;
908 if (lirc_buffer_init(rbuf
, BUF_CHUNK_SIZE
, BUF_SIZE
)) {
909 err("%s: lirc_buffer_init failed", __FUNCTION__
);
911 goto alloc_status_switch
;
914 rx_urb
= usb_alloc_urb(0, GFP_KERNEL
);
916 rx_urb
= usb_alloc_urb(0);
919 err("%s: usb_alloc_urb failed for IR urb", __FUNCTION__
);
921 goto alloc_status_switch
;
925 tx_urb
= usb_alloc_urb(0, GFP_KERNEL
);
927 tx_urb
= usb_alloc_urb(0);
930 err("%s: usb_alloc_urb failed for VFD urb",
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
);
945 plugin
->code_length
= 64;
946 plugin
->sample_rate
= 0;
947 plugin
->features
= LIRC_CAN_REC_LIRCCODE
;
948 plugin
->data
= context
;
950 plugin
->set_use_inc
= ir_open
;
951 plugin
->set_use_dec
= ir_close
;
952 #ifdef LIRC_HAVE_SYSFS
953 plugin
->dev
= &dev
->dev
;
955 plugin
->owner
= THIS_MODULE
;
959 lirc_minor
= lirc_register_plugin(plugin
);
960 if (lirc_minor
< 0) {
961 err("%s: lirc_register_plugin failed", __FUNCTION__
);
965 info("%s: Registered Sasem plugin (minor:%d)",
966 __FUNCTION__
, lirc_minor
);
970 switch (alloc_status
) {
974 usb_free_urb(tx_urb
);
976 usb_free_urb(rx_urb
);
978 lirc_buffer_free(rbuf
);
991 /* Needed while unregistering! */
992 plugin
->minor
= lirc_minor
;
995 context
->dev_present
= TRUE
;
996 context
->rx_endpoint
= rx_endpoint
;
997 context
->rx_urb
= rx_urb
;
999 context
->tx_endpoint
= tx_endpoint
;
1000 context
->tx_urb
= tx_urb
;
1001 context
->vfd_contrast
= 1000; /* range 0 - 1000 */
1003 context
->plugin
= plugin
;
1006 usb_set_intfdata(interface
, context
);
1008 minor_table
[subminor
] = context
;
1009 context
->subminor
= subminor
;
1015 info("Registering VFD with devfs");
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",
1022 sprintf(name
, DEVFS_NAME
, subminor
);
1023 context
->devfs
= devfs_register(usb_devfs_handle
, name
,
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",
1034 info("%s: Sasem device on usb<%d:%d> initialized",
1035 __FUNCTION__
, dev
->bus
->busnum
, dev
->devnum
);
1042 return (retval
== SUCCESS
) ? context
: NULL
;
1047 * Callback function for USB core API: disonnect
1050 static void sasem_disconnect(struct usb_interface
*interface
)
1052 static void sasem_disconnect(struct usb_device
*dev
, void *data
)
1055 struct sasem_context
*context
;
1057 /* prevent races with ir_open()/vfd_open() */
1058 down(&disconnect_sem
);
1061 context
= usb_get_intfdata(interface
);
1063 context
= (struct sasem_context
*)data
;
1067 info("%s: Sasem device disconnected", __FUNCTION__
);
1070 usb_set_intfdata(interface
, NULL
);
1072 minor_table
[context
->subminor
] = NULL
;
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
);
1091 usb_deregister_dev(interface
, &sasem_class
);
1094 devfs_unregister(context
->devfs
);
1099 if (!context
->ir_isopen
&& !context
->vfd_isopen
)
1100 delete_context(context
);
1102 up(&disconnect_sem
);
1105 static int __init
sasem_init(void)
1109 info(MOD_DESC
", v" MOD_VERSION
);
1112 rc
= usb_register(&sasem_driver
);
1114 err("%s: usb register failed (%d)", __FUNCTION__
, rc
);
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)