2010-09-29 07:16:50 +04:00
/*
* Driver for Meywa - Denki & KAYAC YUREX
*
* Copyright ( C ) 2010 Tomoki Sekiyama ( tomoki . sekiyama @ gmail . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation , version 2.
*
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/kref.h>
# include <linux/mutex.h>
# include <linux/uaccess.h>
# include <linux/usb.h>
# include <linux/hid.h>
# define DRIVER_AUTHOR "Tomoki Sekiyama"
# define DRIVER_DESC "Driver for Meywa-Denki & KAYAC YUREX"
# define YUREX_VENDOR_ID 0x0c45
# define YUREX_PRODUCT_ID 0x1010
# define CMD_ACK '!'
# define CMD_ANIMATE 'A'
# define CMD_COUNT 'C'
# define CMD_LED 'L'
# define CMD_READ 'R'
# define CMD_SET 'S'
# define CMD_VERSION 'V'
# define CMD_EOF 0x0d
# define CMD_PADDING 0xff
# define YUREX_BUF_SIZE 8
2010-10-03 01:59:06 +04:00
# define YUREX_WRITE_TIMEOUT (HZ*2)
2010-09-29 07:16:50 +04:00
/* table of devices that work with this driver */
static struct usb_device_id yurex_table [ ] = {
{ USB_DEVICE ( YUREX_VENDOR_ID , YUREX_PRODUCT_ID ) } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , yurex_table ) ;
# ifdef CONFIG_USB_DYNAMIC_MINORS
2010-09-30 16:01:22 +04:00
# define YUREX_MINOR_BASE 0
2010-09-29 07:16:50 +04:00
# else
2010-09-30 16:01:22 +04:00
# define YUREX_MINOR_BASE 192
2010-09-29 07:16:50 +04:00
# endif
/* Structure to hold all of our device specific stuff */
struct usb_yurex {
struct usb_device * udev ;
struct usb_interface * interface ;
__u8 int_in_endpointAddr ;
struct urb * urb ; /* URB for interrupt in */
unsigned char * int_buffer ; /* buffer for intterupt in */
struct urb * cntl_urb ; /* URB for control msg */
struct usb_ctrlrequest * cntl_req ; /* req for control msg */
unsigned char * cntl_buffer ; /* buffer for control msg */
struct kref kref ;
struct mutex io_mutex ;
struct fasync_struct * async_queue ;
wait_queue_head_t waitq ;
spinlock_t lock ;
__s64 bbu ; /* BBU from device */
} ;
# define to_yurex_dev(d) container_of(d, struct usb_yurex, kref)
static struct usb_driver yurex_driver ;
static const struct file_operations yurex_fops ;
static void yurex_control_callback ( struct urb * urb )
{
struct usb_yurex * dev = urb - > context ;
int status = urb - > status ;
if ( status ) {
2012-04-21 03:53:56 +04:00
dev_err ( & urb - > dev - > dev , " %s - control failed: %d \n " ,
__func__ , status ) ;
2010-09-29 07:16:50 +04:00
wake_up_interruptible ( & dev - > waitq ) ;
return ;
}
/* on success, sender woken up by CMD_ACK int in, or timeout */
}
static void yurex_delete ( struct kref * kref )
{
struct usb_yurex * dev = to_yurex_dev ( kref ) ;
2012-05-02 08:34:11 +04:00
dev_dbg ( & dev - > interface - > dev , " %s \n " , __func__ ) ;
2010-09-29 07:16:50 +04:00
usb_put_dev ( dev - > udev ) ;
2010-10-03 01:59:06 +04:00
if ( dev - > cntl_urb ) {
usb_kill_urb ( dev - > cntl_urb ) ;
2012-03-30 03:51:28 +04:00
kfree ( dev - > cntl_req ) ;
2010-10-03 01:59:06 +04:00
if ( dev - > cntl_buffer )
usb_free_coherent ( dev - > udev , YUREX_BUF_SIZE ,
dev - > cntl_buffer , dev - > cntl_urb - > transfer_dma ) ;
usb_free_urb ( dev - > cntl_urb ) ;
}
2010-09-29 07:16:50 +04:00
if ( dev - > urb ) {
usb_kill_urb ( dev - > urb ) ;
if ( dev - > int_buffer )
usb_free_coherent ( dev - > udev , YUREX_BUF_SIZE ,
dev - > int_buffer , dev - > urb - > transfer_dma ) ;
usb_free_urb ( dev - > urb ) ;
}
kfree ( dev ) ;
}
/*
* usb class driver info in order to get a minor number from the usb core ,
* and to have the device registered with the driver core
*/
static struct usb_class_driver yurex_class = {
. name = " yurex%d " ,
. fops = & yurex_fops ,
. minor_base = YUREX_MINOR_BASE ,
} ;
static void yurex_interrupt ( struct urb * urb )
{
struct usb_yurex * dev = urb - > context ;
unsigned char * buf = dev - > int_buffer ;
int status = urb - > status ;
unsigned long flags ;
int retval , i ;
switch ( status ) {
case 0 : /*success*/
break ;
case - EOVERFLOW :
2012-04-21 03:53:56 +04:00
dev_err ( & dev - > interface - > dev ,
" %s - overflow with length %d, actual length is %d \n " ,
__func__ , YUREX_BUF_SIZE , dev - > urb - > actual_length ) ;
2010-09-29 07:16:50 +04:00
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
case - EILSEQ :
/* The device is terminated, clean up */
return ;
default :
2012-04-21 03:53:56 +04:00
dev_err ( & dev - > interface - > dev ,
" %s - unknown status received: %d \n " , __func__ , status ) ;
2010-09-29 07:16:50 +04:00
goto exit ;
}
/* handle received message */
switch ( buf [ 0 ] ) {
case CMD_COUNT :
case CMD_READ :
if ( buf [ 6 ] = = CMD_EOF ) {
spin_lock_irqsave ( & dev - > lock , flags ) ;
dev - > bbu = 0 ;
for ( i = 1 ; i < 6 ; i + + ) {
dev - > bbu + = buf [ i ] ;
if ( i ! = 5 )
dev - > bbu < < = 8 ;
}
2012-05-02 08:34:11 +04:00
dev_dbg ( & dev - > interface - > dev , " %s count: %lld \n " ,
__func__ , dev - > bbu ) ;
2010-09-29 07:16:50 +04:00
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
kill_fasync ( & dev - > async_queue , SIGIO , POLL_IN ) ;
}
else
2012-05-02 08:34:11 +04:00
dev_dbg ( & dev - > interface - > dev ,
" data format error - no EOF \n " ) ;
2010-09-29 07:16:50 +04:00
break ;
case CMD_ACK :
2012-05-02 08:34:11 +04:00
dev_dbg ( & dev - > interface - > dev , " %s ack: %c \n " ,
__func__ , buf [ 1 ] ) ;
2010-09-29 07:16:50 +04:00
wake_up_interruptible ( & dev - > waitq ) ;
break ;
}
exit :
retval = usb_submit_urb ( dev - > urb , GFP_ATOMIC ) ;
if ( retval ) {
2012-04-21 03:53:56 +04:00
dev_err ( & dev - > interface - > dev , " %s - usb_submit_urb failed: %d \n " ,
2010-09-29 07:16:50 +04:00
__func__ , retval ) ;
}
}
static int yurex_probe ( struct usb_interface * interface , const struct usb_device_id * id )
{
struct usb_yurex * dev ;
struct usb_host_interface * iface_desc ;
struct usb_endpoint_descriptor * endpoint ;
int retval = - ENOMEM ;
int i ;
DEFINE_WAIT ( wait ) ;
/* allocate memory for our device state and initialize it */
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev ) {
2012-04-21 03:53:56 +04:00
dev_err ( & interface - > dev , " Out of memory \n " ) ;
2010-09-29 07:16:50 +04:00
goto error ;
}
kref_init ( & dev - > kref ) ;
mutex_init ( & dev - > io_mutex ) ;
spin_lock_init ( & dev - > lock ) ;
init_waitqueue_head ( & dev - > waitq ) ;
dev - > udev = usb_get_dev ( interface_to_usbdev ( interface ) ) ;
dev - > interface = interface ;
/* set up the endpoint information */
iface_desc = interface - > cur_altsetting ;
for ( i = 0 ; i < iface_desc - > desc . bNumEndpoints ; i + + ) {
endpoint = & iface_desc - > endpoint [ i ] . desc ;
if ( usb_endpoint_is_int_in ( endpoint ) ) {
dev - > int_in_endpointAddr = endpoint - > bEndpointAddress ;
break ;
}
}
if ( ! dev - > int_in_endpointAddr ) {
retval = - ENODEV ;
2012-04-21 03:53:56 +04:00
dev_err ( & interface - > dev , " Could not find endpoints \n " ) ;
2010-09-29 07:16:50 +04:00
goto error ;
}
/* allocate control URB */
dev - > cntl_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! dev - > cntl_urb ) {
2012-04-21 03:53:56 +04:00
dev_err ( & interface - > dev , " Could not allocate control URB \n " ) ;
2010-09-29 07:16:50 +04:00
goto error ;
}
/* allocate buffer for control req */
2012-03-30 03:51:28 +04:00
dev - > cntl_req = kmalloc ( YUREX_BUF_SIZE , GFP_KERNEL ) ;
2010-09-29 07:16:50 +04:00
if ( ! dev - > cntl_req ) {
2012-04-21 03:53:56 +04:00
dev_err ( & interface - > dev , " Could not allocate cntl_req \n " ) ;
2010-09-29 07:16:50 +04:00
goto error ;
}
/* allocate buffer for control msg */
dev - > cntl_buffer = usb_alloc_coherent ( dev - > udev , YUREX_BUF_SIZE ,
GFP_KERNEL ,
& dev - > cntl_urb - > transfer_dma ) ;
if ( ! dev - > cntl_buffer ) {
2012-04-21 03:53:56 +04:00
dev_err ( & interface - > dev , " Could not allocate cntl_buffer \n " ) ;
2010-09-29 07:16:50 +04:00
goto error ;
}
/* configure control URB */
dev - > cntl_req - > bRequestType = USB_DIR_OUT | USB_TYPE_CLASS |
USB_RECIP_INTERFACE ;
dev - > cntl_req - > bRequest = HID_REQ_SET_REPORT ;
dev - > cntl_req - > wValue = cpu_to_le16 ( ( HID_OUTPUT_REPORT + 1 ) < < 8 ) ;
dev - > cntl_req - > wIndex = cpu_to_le16 ( iface_desc - > desc . bInterfaceNumber ) ;
dev - > cntl_req - > wLength = cpu_to_le16 ( YUREX_BUF_SIZE ) ;
usb_fill_control_urb ( dev - > cntl_urb , dev - > udev ,
usb_sndctrlpipe ( dev - > udev , 0 ) ,
( void * ) dev - > cntl_req , dev - > cntl_buffer ,
YUREX_BUF_SIZE , yurex_control_callback , dev ) ;
2010-10-03 01:59:06 +04:00
dev - > cntl_urb - > transfer_flags | = URB_NO_TRANSFER_DMA_MAP ;
2010-09-29 07:16:50 +04:00
/* allocate interrupt URB */
dev - > urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! dev - > urb ) {
2012-04-21 03:53:56 +04:00
dev_err ( & interface - > dev , " Could not allocate URB \n " ) ;
2010-09-29 07:16:50 +04:00
goto error ;
}
/* allocate buffer for interrupt in */
dev - > int_buffer = usb_alloc_coherent ( dev - > udev , YUREX_BUF_SIZE ,
GFP_KERNEL , & dev - > urb - > transfer_dma ) ;
if ( ! dev - > int_buffer ) {
2012-04-21 03:53:56 +04:00
dev_err ( & interface - > dev , " Could not allocate int_buffer \n " ) ;
2010-09-29 07:16:50 +04:00
goto error ;
}
/* configure interrupt URB */
usb_fill_int_urb ( dev - > urb , dev - > udev ,
usb_rcvintpipe ( dev - > udev , dev - > int_in_endpointAddr ) ,
dev - > int_buffer , YUREX_BUF_SIZE , yurex_interrupt ,
dev , 1 ) ;
2012-03-30 03:51:36 +04:00
dev - > urb - > transfer_flags | = URB_NO_TRANSFER_DMA_MAP ;
2010-09-29 07:16:50 +04:00
if ( usb_submit_urb ( dev - > urb , GFP_KERNEL ) ) {
retval = - EIO ;
2012-04-21 03:53:56 +04:00
dev_err ( & interface - > dev , " Could not submitting URB \n " ) ;
2010-09-29 07:16:50 +04:00
goto error ;
}
/* save our data pointer in this interface device */
usb_set_intfdata ( interface , dev ) ;
2014-05-19 15:52:20 +04:00
dev - > bbu = - 1 ;
2010-09-29 07:16:50 +04:00
/* we can register the device now, as it is ready */
retval = usb_register_dev ( interface , & yurex_class ) ;
if ( retval ) {
2012-04-21 03:53:56 +04:00
dev_err ( & interface - > dev ,
" Not able to get a minor for this device. \n " ) ;
2010-09-29 07:16:50 +04:00
usb_set_intfdata ( interface , NULL ) ;
goto error ;
}
dev_info ( & interface - > dev ,
2010-10-03 01:59:06 +04:00
" USB YUREX device now attached to Yurex #%d \n " ,
2010-09-29 07:16:50 +04:00
interface - > minor ) ;
return 0 ;
error :
if ( dev )
/* this frees allocated memory */
kref_put ( & dev - > kref , yurex_delete ) ;
return retval ;
}
static void yurex_disconnect ( struct usb_interface * interface )
{
struct usb_yurex * dev ;
int minor = interface - > minor ;
dev = usb_get_intfdata ( interface ) ;
usb_set_intfdata ( interface , NULL ) ;
/* give back our minor */
usb_deregister_dev ( interface , & yurex_class ) ;
/* prevent more I/O from starting */
mutex_lock ( & dev - > io_mutex ) ;
dev - > interface = NULL ;
mutex_unlock ( & dev - > io_mutex ) ;
/* wakeup waiters */
kill_fasync ( & dev - > async_queue , SIGIO , POLL_IN ) ;
wake_up_interruptible ( & dev - > waitq ) ;
/* decrement our usage count */
kref_put ( & dev - > kref , yurex_delete ) ;
2010-10-03 01:59:06 +04:00
dev_info ( & interface - > dev , " USB YUREX #%d now disconnected \n " , minor ) ;
2010-09-29 07:16:50 +04:00
}
static struct usb_driver yurex_driver = {
. name = " yurex " ,
. probe = yurex_probe ,
. disconnect = yurex_disconnect ,
. id_table = yurex_table ,
} ;
static int yurex_fasync ( int fd , struct file * file , int on )
{
struct usb_yurex * dev ;
2014-08-19 02:53:34 +04:00
dev = file - > private_data ;
2010-09-29 07:16:50 +04:00
return fasync_helper ( fd , file , on , & dev - > async_queue ) ;
}
static int yurex_open ( struct inode * inode , struct file * file )
{
struct usb_yurex * dev ;
struct usb_interface * interface ;
int subminor ;
int retval = 0 ;
subminor = iminor ( inode ) ;
interface = usb_find_interface ( & yurex_driver , subminor ) ;
if ( ! interface ) {
2012-04-21 03:53:56 +04:00
printk ( KERN_ERR " %s - error, can't find device for minor %d " ,
__func__ , subminor ) ;
2010-09-29 07:16:50 +04:00
retval = - ENODEV ;
goto exit ;
}
dev = usb_get_intfdata ( interface ) ;
if ( ! dev ) {
retval = - ENODEV ;
goto exit ;
}
/* increment our usage count for the device */
kref_get ( & dev - > kref ) ;
/* save our object in the file's private structure */
mutex_lock ( & dev - > io_mutex ) ;
file - > private_data = dev ;
mutex_unlock ( & dev - > io_mutex ) ;
exit :
return retval ;
}
static int yurex_release ( struct inode * inode , struct file * file )
{
struct usb_yurex * dev ;
2014-08-19 02:53:34 +04:00
dev = file - > private_data ;
2010-09-29 07:16:50 +04:00
if ( dev = = NULL )
return - ENODEV ;
/* decrement the count on our device */
kref_put ( & dev - > kref , yurex_delete ) ;
return 0 ;
}
2014-10-10 16:49:45 +04:00
static ssize_t yurex_read ( struct file * file , char __user * buffer , size_t count ,
loff_t * ppos )
2010-09-29 07:16:50 +04:00
{
struct usb_yurex * dev ;
int retval = 0 ;
int bytes_read = 0 ;
char in_buffer [ 20 ] ;
unsigned long flags ;
2014-08-19 02:53:34 +04:00
dev = file - > private_data ;
2010-09-29 07:16:50 +04:00
mutex_lock ( & dev - > io_mutex ) ;
if ( ! dev - > interface ) { /* already disconnected */
retval = - ENODEV ;
goto exit ;
}
spin_lock_irqsave ( & dev - > lock , flags ) ;
2010-10-03 01:59:06 +04:00
bytes_read = snprintf ( in_buffer , 20 , " %lld \n " , dev - > bbu ) ;
2010-09-29 07:16:50 +04:00
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
if ( * ppos < bytes_read ) {
if ( copy_to_user ( buffer , in_buffer + * ppos , bytes_read - * ppos ) )
retval = - EFAULT ;
else {
retval = bytes_read - * ppos ;
* ppos + = bytes_read ;
}
}
exit :
mutex_unlock ( & dev - > io_mutex ) ;
return retval ;
}
2014-10-10 16:49:45 +04:00
static ssize_t yurex_write ( struct file * file , const char __user * user_buffer ,
size_t count , loff_t * ppos )
2010-09-29 07:16:50 +04:00
{
struct usb_yurex * dev ;
int i , set = 0 , retval = 0 ;
char buffer [ 16 ] ;
char * data = buffer ;
unsigned long long c , c2 = 0 ;
signed long timeout = 0 ;
DEFINE_WAIT ( wait ) ;
count = min ( sizeof ( buffer ) , count ) ;
2014-08-19 02:53:34 +04:00
dev = file - > private_data ;
2010-09-29 07:16:50 +04:00
/* verify that we actually have some data to write */
if ( count = = 0 )
goto error ;
mutex_lock ( & dev - > io_mutex ) ;
2013-12-30 19:19:19 +04:00
if ( ! dev - > interface ) { /* already disconnected */
2010-09-29 07:16:50 +04:00
mutex_unlock ( & dev - > io_mutex ) ;
retval = - ENODEV ;
goto error ;
}
if ( copy_from_user ( buffer , user_buffer , count ) ) {
mutex_unlock ( & dev - > io_mutex ) ;
retval = - EFAULT ;
goto error ;
}
memset ( dev - > cntl_buffer , CMD_PADDING , YUREX_BUF_SIZE ) ;
switch ( buffer [ 0 ] ) {
case CMD_ANIMATE :
case CMD_LED :
dev - > cntl_buffer [ 0 ] = buffer [ 0 ] ;
dev - > cntl_buffer [ 1 ] = buffer [ 1 ] ;
dev - > cntl_buffer [ 2 ] = CMD_EOF ;
break ;
case CMD_READ :
case CMD_VERSION :
dev - > cntl_buffer [ 0 ] = buffer [ 0 ] ;
dev - > cntl_buffer [ 1 ] = 0x00 ;
dev - > cntl_buffer [ 2 ] = CMD_EOF ;
break ;
case CMD_SET :
data + + ;
/* FALL THROUGH */
case ' 0 ' . . . ' 9 ' :
set = 1 ;
c = c2 = simple_strtoull ( data , NULL , 0 ) ;
dev - > cntl_buffer [ 0 ] = CMD_SET ;
for ( i = 1 ; i < 6 ; i + + ) {
dev - > cntl_buffer [ i ] = ( c > > 32 ) & 0xff ;
c < < = 8 ;
}
buffer [ 6 ] = CMD_EOF ;
break ;
default :
mutex_unlock ( & dev - > io_mutex ) ;
return - EINVAL ;
}
/* send the data as the control msg */
prepare_to_wait ( & dev - > waitq , & wait , TASK_INTERRUPTIBLE ) ;
2012-05-02 08:34:11 +04:00
dev_dbg ( & dev - > interface - > dev , " %s - submit %c \n " , __func__ ,
dev - > cntl_buffer [ 0 ] ) ;
2010-09-29 07:16:50 +04:00
retval = usb_submit_urb ( dev - > cntl_urb , GFP_KERNEL ) ;
if ( retval > = 0 )
timeout = schedule_timeout ( YUREX_WRITE_TIMEOUT ) ;
finish_wait ( & dev - > waitq , & wait ) ;
mutex_unlock ( & dev - > io_mutex ) ;
if ( retval < 0 ) {
2012-04-21 03:53:56 +04:00
dev_err ( & dev - > interface - > dev ,
" %s - failed to send bulk msg, error %d \n " ,
__func__ , retval ) ;
2010-09-29 07:16:50 +04:00
goto error ;
}
if ( set & & timeout )
dev - > bbu = c2 ;
return timeout ? count : - EIO ;
error :
return retval ;
}
static const struct file_operations yurex_fops = {
. owner = THIS_MODULE ,
. read = yurex_read ,
. write = yurex_write ,
. open = yurex_open ,
. release = yurex_release ,
. fasync = yurex_fasync ,
2010-11-22 13:29:23 +03:00
. llseek = default_llseek ,
2010-09-29 07:16:50 +04:00
} ;
2011-11-18 21:34:02 +04:00
module_usb_driver ( yurex_driver ) ;
2010-09-29 07:16:50 +04:00
MODULE_LICENSE ( " GPL " ) ;