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