2011-07-05 15:45:08 +04:00
/*
* HID driver for Nintendo Wiimote devices
* Copyright ( c ) 2011 David Herrmann
*/
/*
* 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 ; either version 2 of the License , or ( at your option )
* any later version .
*/
2011-07-05 15:45:12 +04:00
# include <linux/atomic.h>
2011-07-05 15:45:11 +04:00
# include <linux/device.h>
2011-07-05 15:45:09 +04:00
# include <linux/hid.h>
2011-07-05 15:45:11 +04:00
# include <linux/input.h>
2011-07-05 15:45:08 +04:00
# include <linux/module.h>
2011-07-05 15:45:14 +04:00
# include <linux/spinlock.h>
2011-07-05 15:45:09 +04:00
# include "hid-ids.h"
2011-07-05 15:45:08 +04:00
# define WIIMOTE_VERSION "0.1"
# define WIIMOTE_NAME "Nintendo Wii Remote"
2011-07-05 15:45:14 +04:00
# define WIIMOTE_BUFSIZE 32
struct wiimote_buf {
__u8 data [ HID_MAX_BUFFER_SIZE ] ;
size_t size ;
} ;
2011-07-05 15:45:08 +04:00
2011-07-05 15:45:10 +04:00
struct wiimote_data {
2011-07-05 15:45:12 +04:00
atomic_t ready ;
2011-07-05 15:45:10 +04:00
struct hid_device * hdev ;
2011-07-05 15:45:11 +04:00
struct input_dev * input ;
2011-07-05 15:45:14 +04:00
spinlock_t qlock ;
__u8 head ;
__u8 tail ;
struct wiimote_buf outq [ WIIMOTE_BUFSIZE ] ;
struct work_struct worker ;
2011-07-05 15:45:10 +04:00
} ;
2011-07-05 15:45:13 +04:00
static ssize_t wiimote_hid_send ( struct hid_device * hdev , __u8 * buffer ,
size_t count )
{
__u8 * buf ;
ssize_t ret ;
if ( ! hdev - > hid_output_raw_report )
return - ENODEV ;
buf = kmemdup ( buffer , count , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
ret = hdev - > hid_output_raw_report ( hdev , buf , count , HID_OUTPUT_REPORT ) ;
kfree ( buf ) ;
return ret ;
}
2011-07-05 15:45:14 +04:00
static void wiimote_worker ( struct work_struct * work )
{
struct wiimote_data * wdata = container_of ( work , struct wiimote_data ,
worker ) ;
unsigned long flags ;
spin_lock_irqsave ( & wdata - > qlock , flags ) ;
while ( wdata - > head ! = wdata - > tail ) {
spin_unlock_irqrestore ( & wdata - > qlock , flags ) ;
wiimote_hid_send ( wdata - > hdev , wdata - > outq [ wdata - > tail ] . data ,
wdata - > outq [ wdata - > tail ] . size ) ;
spin_lock_irqsave ( & wdata - > qlock , flags ) ;
wdata - > tail = ( wdata - > tail + 1 ) % WIIMOTE_BUFSIZE ;
}
spin_unlock_irqrestore ( & wdata - > qlock , flags ) ;
}
static void wiimote_queue ( struct wiimote_data * wdata , const __u8 * buffer ,
size_t count )
{
unsigned long flags ;
__u8 newhead ;
if ( count > HID_MAX_BUFFER_SIZE ) {
hid_warn ( wdata - > hdev , " Sending too large output report \n " ) ;
return ;
}
/*
* Copy new request into our output queue and check whether the
* queue is full . If it is full , discard this request .
* If it is empty we need to start a new worker that will
* send out the buffer to the hid device .
* If the queue is not empty , then there must be a worker
* that is currently sending out our buffer and this worker
* will reschedule itself until the queue is empty .
*/
spin_lock_irqsave ( & wdata - > qlock , flags ) ;
memcpy ( wdata - > outq [ wdata - > head ] . data , buffer , count ) ;
wdata - > outq [ wdata - > head ] . size = count ;
newhead = ( wdata - > head + 1 ) % WIIMOTE_BUFSIZE ;
if ( wdata - > head = = wdata - > tail ) {
wdata - > head = newhead ;
schedule_work ( & wdata - > worker ) ;
} else if ( newhead ! = wdata - > tail ) {
wdata - > head = newhead ;
} else {
hid_warn ( wdata - > hdev , " Output queue is full " ) ;
}
spin_unlock_irqrestore ( & wdata - > qlock , flags ) ;
}
2011-07-05 15:45:11 +04:00
static int wiimote_input_event ( struct input_dev * dev , unsigned int type ,
unsigned int code , int value )
{
2011-07-05 15:45:12 +04:00
struct wiimote_data * wdata = input_get_drvdata ( dev ) ;
if ( ! atomic_read ( & wdata - > ready ) )
return - EBUSY ;
/* smp_rmb: Make sure wdata->xy is available when wdata->ready is 1 */
smp_rmb ( ) ;
2011-07-05 15:45:11 +04:00
return 0 ;
}
2011-07-05 15:45:09 +04:00
static int wiimote_hid_event ( struct hid_device * hdev , struct hid_report * report ,
u8 * raw_data , int size )
{
2011-07-05 15:45:12 +04:00
struct wiimote_data * wdata = hid_get_drvdata ( hdev ) ;
if ( ! atomic_read ( & wdata - > ready ) )
return - EBUSY ;
/* smp_rmb: Make sure wdata->xy is available when wdata->ready is 1 */
smp_rmb ( ) ;
2011-07-05 15:45:09 +04:00
if ( size < 1 )
return - EINVAL ;
return 0 ;
}
2011-07-05 15:45:10 +04:00
static struct wiimote_data * wiimote_create ( struct hid_device * hdev )
{
struct wiimote_data * wdata ;
wdata = kzalloc ( sizeof ( * wdata ) , GFP_KERNEL ) ;
if ( ! wdata )
return NULL ;
2011-07-05 15:45:11 +04:00
wdata - > input = input_allocate_device ( ) ;
if ( ! wdata - > input ) {
kfree ( wdata ) ;
return NULL ;
}
2011-07-05 15:45:10 +04:00
wdata - > hdev = hdev ;
hid_set_drvdata ( hdev , wdata ) ;
2011-07-05 15:45:11 +04:00
input_set_drvdata ( wdata - > input , wdata ) ;
wdata - > input - > event = wiimote_input_event ;
wdata - > input - > dev . parent = & wdata - > hdev - > dev ;
wdata - > input - > id . bustype = wdata - > hdev - > bus ;
wdata - > input - > id . vendor = wdata - > hdev - > vendor ;
wdata - > input - > id . product = wdata - > hdev - > product ;
wdata - > input - > id . version = wdata - > hdev - > version ;
wdata - > input - > name = WIIMOTE_NAME ;
2011-07-05 15:45:14 +04:00
spin_lock_init ( & wdata - > qlock ) ;
INIT_WORK ( & wdata - > worker , wiimote_worker ) ;
2011-07-05 15:45:10 +04:00
return wdata ;
}
static void wiimote_destroy ( struct wiimote_data * wdata )
{
kfree ( wdata ) ;
}
2011-07-05 15:45:09 +04:00
static int wiimote_hid_probe ( struct hid_device * hdev ,
const struct hid_device_id * id )
2011-07-05 15:45:08 +04:00
{
2011-07-05 15:45:10 +04:00
struct wiimote_data * wdata ;
2011-07-05 15:45:09 +04:00
int ret ;
2011-07-05 15:45:10 +04:00
wdata = wiimote_create ( hdev ) ;
if ( ! wdata ) {
hid_err ( hdev , " Can't alloc device \n " ) ;
return - ENOMEM ;
}
2011-07-05 15:45:09 +04:00
ret = hid_parse ( hdev ) ;
if ( ret ) {
hid_err ( hdev , " HID parse failed \n " ) ;
2011-07-05 15:45:10 +04:00
goto err ;
2011-07-05 15:45:09 +04:00
}
ret = hid_hw_start ( hdev , HID_CONNECT_HIDRAW ) ;
if ( ret ) {
hid_err ( hdev , " HW start failed \n " ) ;
2011-07-05 15:45:10 +04:00
goto err ;
2011-07-05 15:45:09 +04:00
}
2011-07-05 15:45:11 +04:00
ret = input_register_device ( wdata - > input ) ;
if ( ret ) {
hid_err ( hdev , " Cannot register input device \n " ) ;
goto err_stop ;
}
2011-07-05 15:45:12 +04:00
/* smp_wmb: Write wdata->xy first before wdata->ready is set to 1 */
smp_wmb ( ) ;
atomic_set ( & wdata - > ready , 1 ) ;
2011-07-05 15:45:09 +04:00
hid_info ( hdev , " New device registered \n " ) ;
2011-07-05 15:45:08 +04:00
return 0 ;
2011-07-05 15:45:10 +04:00
2011-07-05 15:45:11 +04:00
err_stop :
hid_hw_stop ( hdev ) ;
2011-07-05 15:45:10 +04:00
err :
2011-07-05 15:45:11 +04:00
input_free_device ( wdata - > input ) ;
2011-07-05 15:45:10 +04:00
wiimote_destroy ( wdata ) ;
return ret ;
2011-07-05 15:45:08 +04:00
}
2011-07-05 15:45:09 +04:00
static void wiimote_hid_remove ( struct hid_device * hdev )
{
2011-07-05 15:45:10 +04:00
struct wiimote_data * wdata = hid_get_drvdata ( hdev ) ;
2011-07-05 15:45:09 +04:00
hid_info ( hdev , " Device removed \n " ) ;
2011-07-05 15:45:14 +04:00
2011-07-05 15:45:09 +04:00
hid_hw_stop ( hdev ) ;
2011-07-05 15:45:11 +04:00
input_unregister_device ( wdata - > input ) ;
2011-07-05 15:45:14 +04:00
cancel_work_sync ( & wdata - > worker ) ;
2011-07-05 15:45:10 +04:00
wiimote_destroy ( wdata ) ;
2011-07-05 15:45:09 +04:00
}
static const struct hid_device_id wiimote_hid_devices [ ] = {
{ HID_BLUETOOTH_DEVICE ( USB_VENDOR_ID_NINTENDO ,
USB_DEVICE_ID_NINTENDO_WIIMOTE ) } ,
{ }
} ;
MODULE_DEVICE_TABLE ( hid , wiimote_hid_devices ) ;
static struct hid_driver wiimote_hid_driver = {
. name = " wiimote " ,
. id_table = wiimote_hid_devices ,
. probe = wiimote_hid_probe ,
. remove = wiimote_hid_remove ,
. raw_event = wiimote_hid_event ,
} ;
static int __init wiimote_init ( void )
{
int ret ;
ret = hid_register_driver ( & wiimote_hid_driver ) ;
if ( ret )
pr_err ( " Can't register wiimote hid driver \n " ) ;
return ret ;
}
2011-07-05 15:45:08 +04:00
static void __exit wiimote_exit ( void )
{
2011-07-05 15:45:09 +04:00
hid_unregister_driver ( & wiimote_hid_driver ) ;
2011-07-05 15:45:08 +04:00
}
module_init ( wiimote_init ) ;
module_exit ( wiimote_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " David Herrmann <dh.herrmann@gmail.com> " ) ;
MODULE_DESCRIPTION ( WIIMOTE_NAME " Device Driver " ) ;
MODULE_VERSION ( WIIMOTE_VERSION ) ;