2014-06-18 20:05:02 +04:00
/*
* MSI GT683R led driver
*
* Copyright ( c ) 2014 Janne Kanniainen < janne . kanniainen @ 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 ; either version 2 of
* the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
*/
# include <linux/device.h>
# include <linux/hid.h>
# include <linux/kernel.h>
# include <linux/leds.h>
# include <linux/module.h>
# include "hid-ids.h"
# define GT683R_BUFFER_SIZE 8
/*
* GT683R_LED_OFF : all LEDs are off
* GT683R_LED_AUDIO : LEDs brightness depends on sound level
* GT683R_LED_BREATHING : LEDs brightness varies at human breathing rate
* GT683R_LED_NORMAL : LEDs are fully on when enabled
*/
enum gt683r_led_mode {
GT683R_LED_OFF = 0 ,
GT683R_LED_AUDIO = 2 ,
GT683R_LED_BREATHING = 3 ,
GT683R_LED_NORMAL = 5
} ;
enum gt683r_panels {
GT683R_LED_BACK = 0 ,
GT683R_LED_SIDE = 1 ,
GT683R_LED_FRONT = 2 ,
GT683R_LED_COUNT ,
} ;
static const char * const gt683r_panel_names [ ] = {
" back " ,
" side " ,
" front " ,
} ;
struct gt683r_led {
struct hid_device * hdev ;
struct led_classdev led_devs [ GT683R_LED_COUNT ] ;
struct mutex lock ;
struct work_struct work ;
enum led_brightness brightnesses [ GT683R_LED_COUNT ] ;
enum gt683r_led_mode mode ;
} ;
static const struct hid_device_id gt683r_led_id [ ] = {
{ HID_USB_DEVICE ( USB_VENDOR_ID_MSI , USB_DEVICE_ID_MSI_GT683R_LED_PANEL ) } ,
{ }
} ;
static void gt683r_brightness_set ( struct led_classdev * led_cdev ,
enum led_brightness brightness )
{
int i ;
struct device * dev = led_cdev - > dev - > parent ;
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2014-06-18 20:05:02 +04:00
struct gt683r_led * led = hid_get_drvdata ( hdev ) ;
for ( i = 0 ; i < GT683R_LED_COUNT ; i + + ) {
if ( led_cdev = = & led - > led_devs [ i ] )
break ;
}
if ( i < GT683R_LED_COUNT ) {
led - > brightnesses [ i ] = brightness ;
schedule_work ( & led - > work ) ;
}
}
2014-07-03 21:17:09 +04:00
static ssize_t mode_show ( struct device * dev ,
2014-06-18 20:05:02 +04:00
struct device_attribute * attr ,
char * buf )
{
u8 sysfs_mode ;
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev - > parent ) ;
2014-06-18 20:05:02 +04:00
struct gt683r_led * led = hid_get_drvdata ( hdev ) ;
if ( led - > mode = = GT683R_LED_NORMAL )
sysfs_mode = 0 ;
else if ( led - > mode = = GT683R_LED_AUDIO )
sysfs_mode = 1 ;
else
sysfs_mode = 2 ;
return scnprintf ( buf , PAGE_SIZE , " %u \n " , sysfs_mode ) ;
}
2014-07-03 21:17:09 +04:00
static ssize_t mode_store ( struct device * dev ,
2014-06-18 20:05:02 +04:00
struct device_attribute * attr ,
const char * buf , size_t count )
{
u8 sysfs_mode ;
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev - > parent ) ;
2014-06-18 20:05:02 +04:00
struct gt683r_led * led = hid_get_drvdata ( hdev ) ;
if ( kstrtou8 ( buf , 10 , & sysfs_mode ) | | sysfs_mode > 2 )
return - EINVAL ;
mutex_lock ( & led - > lock ) ;
if ( sysfs_mode = = 0 )
led - > mode = GT683R_LED_NORMAL ;
else if ( sysfs_mode = = 1 )
led - > mode = GT683R_LED_AUDIO ;
else
led - > mode = GT683R_LED_BREATHING ;
mutex_unlock ( & led - > lock ) ;
schedule_work ( & led - > work ) ;
return count ;
}
static int gt683r_led_snd_msg ( struct gt683r_led * led , u8 * msg )
{
int ret ;
ret = hid_hw_raw_request ( led - > hdev , msg [ 0 ] , msg , GT683R_BUFFER_SIZE ,
HID_FEATURE_REPORT , HID_REQ_SET_REPORT ) ;
if ( ret ! = GT683R_BUFFER_SIZE ) {
hid_err ( led - > hdev ,
" failed to send set report request: %i \n " , ret ) ;
if ( ret < 0 )
return ret ;
return - EIO ;
}
return 0 ;
}
static int gt683r_leds_set ( struct gt683r_led * led , u8 leds )
{
int ret ;
u8 * buffer ;
buffer = kzalloc ( GT683R_BUFFER_SIZE , GFP_KERNEL ) ;
if ( ! buffer )
return - ENOMEM ;
buffer [ 0 ] = 0x01 ;
buffer [ 1 ] = 0x02 ;
buffer [ 2 ] = 0x30 ;
buffer [ 3 ] = leds ;
ret = gt683r_led_snd_msg ( led , buffer ) ;
kfree ( buffer ) ;
return ret ;
}
static int gt683r_mode_set ( struct gt683r_led * led , u8 mode )
{
int ret ;
u8 * buffer ;
buffer = kzalloc ( GT683R_BUFFER_SIZE , GFP_KERNEL ) ;
if ( ! buffer )
return - ENOMEM ;
buffer [ 0 ] = 0x01 ;
buffer [ 1 ] = 0x02 ;
buffer [ 2 ] = 0x20 ;
buffer [ 3 ] = mode ;
buffer [ 4 ] = 0x01 ;
ret = gt683r_led_snd_msg ( led , buffer ) ;
kfree ( buffer ) ;
return ret ;
}
static void gt683r_led_work ( struct work_struct * work )
{
int i ;
u8 leds = 0 ;
u8 mode ;
struct gt683r_led * led = container_of ( work , struct gt683r_led , work ) ;
mutex_lock ( & led - > lock ) ;
for ( i = 0 ; i < GT683R_LED_COUNT ; i + + ) {
if ( led - > brightnesses [ i ] )
leds | = BIT ( i ) ;
}
if ( gt683r_leds_set ( led , leds ) )
goto fail ;
if ( leds )
mode = led - > mode ;
else
mode = GT683R_LED_OFF ;
gt683r_mode_set ( led , mode ) ;
fail :
mutex_unlock ( & led - > lock ) ;
}
2014-07-03 21:17:09 +04:00
static DEVICE_ATTR_RW ( mode ) ;
static struct attribute * gt683r_led_attrs [ ] = {
& dev_attr_mode . attr ,
NULL
} ;
static const struct attribute_group gt683r_led_group = {
. name = " gt683r " ,
. attrs = gt683r_led_attrs ,
} ;
static const struct attribute_group * gt683r_led_groups [ ] = {
& gt683r_led_group ,
NULL
} ;
2014-06-18 20:05:02 +04:00
static int gt683r_led_probe ( struct hid_device * hdev ,
const struct hid_device_id * id )
{
int i ;
int ret ;
int name_sz ;
char * name ;
struct gt683r_led * led ;
led = devm_kzalloc ( & hdev - > dev , sizeof ( * led ) , GFP_KERNEL ) ;
if ( ! led )
return - ENOMEM ;
2014-07-03 21:17:08 +04:00
mutex_init ( & led - > lock ) ;
INIT_WORK ( & led - > work , gt683r_led_work ) ;
2014-06-18 20:05:02 +04:00
led - > mode = GT683R_LED_NORMAL ;
led - > hdev = hdev ;
hid_set_drvdata ( hdev , led ) ;
ret = hid_parse ( hdev ) ;
if ( ret ) {
hid_err ( hdev , " hid parsing failed \n " ) ;
return ret ;
}
ret = hid_hw_start ( hdev , HID_CONNECT_HIDRAW ) ;
if ( ret ) {
hid_err ( hdev , " hw start failed \n " ) ;
return ret ;
}
for ( i = 0 ; i < GT683R_LED_COUNT ; i + + ) {
name_sz = strlen ( dev_name ( & hdev - > dev ) ) +
strlen ( gt683r_panel_names [ i ] ) + 3 ;
name = devm_kzalloc ( & hdev - > dev , name_sz , GFP_KERNEL ) ;
if ( ! name ) {
ret = - ENOMEM ;
goto fail ;
}
snprintf ( name , name_sz , " %s::%s " ,
dev_name ( & hdev - > dev ) , gt683r_panel_names [ i ] ) ;
led - > led_devs [ i ] . name = name ;
led - > led_devs [ i ] . max_brightness = 1 ;
led - > led_devs [ i ] . brightness_set = gt683r_brightness_set ;
2014-07-03 21:17:09 +04:00
led - > led_devs [ i ] . groups = gt683r_led_groups ;
2014-06-18 20:05:02 +04:00
ret = led_classdev_register ( & hdev - > dev , & led - > led_devs [ i ] ) ;
if ( ret ) {
hid_err ( hdev , " could not register led device \n " ) ;
goto fail ;
}
}
return 0 ;
fail :
for ( i = i - 1 ; i > = 0 ; i - - )
led_classdev_unregister ( & led - > led_devs [ i ] ) ;
hid_hw_stop ( hdev ) ;
return ret ;
}
static void gt683r_led_remove ( struct hid_device * hdev )
{
int i ;
struct gt683r_led * led = hid_get_drvdata ( hdev ) ;
for ( i = 0 ; i < GT683R_LED_COUNT ; i + + )
led_classdev_unregister ( & led - > led_devs [ i ] ) ;
flush_work ( & led - > work ) ;
hid_hw_stop ( hdev ) ;
}
static struct hid_driver gt683r_led_driver = {
. probe = gt683r_led_probe ,
. remove = gt683r_led_remove ,
. name = " gt683r_led " ,
. id_table = gt683r_led_id ,
} ;
module_hid_driver ( gt683r_led_driver ) ;
MODULE_AUTHOR ( " Janne Kanniainen " ) ;
MODULE_DESCRIPTION ( " MSI GT683R led driver " ) ;
MODULE_LICENSE ( " GPL " ) ;