2013-01-22 21:01:21 +04:00
/*
* ThingM blink ( 1 ) USB RGB LED driver
*
2014-04-15 00:50:19 +04:00
* Copyright 2013 - 2014 Savoir - faire Linux Inc .
2013-01-22 21:01:21 +04:00
* Vivien Didelot < vivien . didelot @ savoirfairelinux . 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/hid.h>
2014-04-15 00:50:19 +04:00
# include <linux/hidraw.h>
2013-01-22 21:01:21 +04:00
# include <linux/leds.h>
# include <linux/module.h>
2014-04-15 00:50:19 +04:00
# include <linux/mutex.h>
2013-01-22 21:01:21 +04:00
# include "hid-ids.h"
2014-04-15 00:50:19 +04:00
# define REPORT_ID 1
# define REPORT_SIZE 9
2013-01-22 21:01:21 +04:00
2014-04-15 00:50:19 +04:00
/* Firmware major number of supported devices */
# define THINGM_MAJOR_MK1 '1'
2014-04-15 00:50:20 +04:00
# define THINGM_MAJOR_MK2 '2'
2013-01-22 21:01:21 +04:00
2014-04-15 00:50:19 +04:00
struct thingm_fwinfo {
char major ;
unsigned numrgb ;
unsigned first ;
} ;
2014-06-03 15:29:38 +04:00
static const struct thingm_fwinfo thingm_fwinfo [ ] = {
2014-04-15 00:50:19 +04:00
{
. major = THINGM_MAJOR_MK1 ,
. numrgb = 1 ,
. first = 0 ,
2014-04-15 00:50:20 +04:00
} , {
. major = THINGM_MAJOR_MK2 ,
. numrgb = 2 ,
. first = 1 ,
2014-04-15 00:50:19 +04:00
}
} ;
/* A red, green or blue channel, part of an RGB chip */
struct thingm_led {
struct thingm_rgb * rgb ;
struct led_classdev ldev ;
char name [ 32 ] ;
} ;
/* Basically a WS2812 5050 RGB LED chip */
struct thingm_rgb {
struct thingm_device * tdev ;
struct thingm_led red ;
struct thingm_led green ;
struct thingm_led blue ;
u8 num ;
} ;
struct thingm_device {
2013-01-22 21:01:21 +04:00
struct hid_device * hdev ;
2014-04-15 00:50:19 +04:00
struct {
char major ;
char minor ;
} version ;
const struct thingm_fwinfo * fwinfo ;
struct mutex lock ;
struct thingm_rgb * rgb ;
2013-01-22 21:01:21 +04:00
} ;
2014-04-15 00:50:19 +04:00
static int thingm_send ( struct thingm_device * tdev , u8 buf [ REPORT_SIZE ] )
2013-01-22 21:01:21 +04:00
{
int ret ;
2014-04-15 00:50:19 +04:00
hid_dbg ( tdev - > hdev , " -> %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx \n " ,
2013-01-22 21:01:21 +04:00
buf [ 0 ] , buf [ 1 ] , buf [ 2 ] , buf [ 3 ] , buf [ 4 ] ,
buf [ 5 ] , buf [ 6 ] , buf [ 7 ] , buf [ 8 ] ) ;
2016-03-01 22:49:54 +03:00
mutex_lock ( & tdev - > lock ) ;
2014-04-15 00:50:19 +04:00
ret = hid_hw_raw_request ( tdev - > hdev , buf [ 0 ] , buf , REPORT_SIZE ,
HID_FEATURE_REPORT , HID_REQ_SET_REPORT ) ;
2013-01-22 21:01:21 +04:00
2016-03-01 22:49:54 +03:00
mutex_unlock ( & tdev - > lock ) ;
2013-01-22 21:01:21 +04:00
return ret < 0 ? ret : 0 ;
}
2014-04-15 00:50:19 +04:00
static int thingm_recv ( struct thingm_device * tdev , u8 buf [ REPORT_SIZE ] )
2013-01-22 21:01:21 +04:00
{
2014-04-15 00:50:19 +04:00
int ret ;
2013-01-22 21:01:21 +04:00
2016-03-01 22:49:54 +03:00
/*
* A read consists of two operations : sending the read command
* and the actual read from the device . Use the mutex to protect
* the full sequence of both operations .
*/
mutex_lock ( & tdev - > lock ) ;
ret = hid_hw_raw_request ( tdev - > hdev , buf [ 0 ] , buf , REPORT_SIZE ,
HID_FEATURE_REPORT , HID_REQ_SET_REPORT ) ;
if ( ret < 0 )
goto err ;
2014-04-15 00:50:19 +04:00
ret = hid_hw_raw_request ( tdev - > hdev , buf [ 0 ] , buf , REPORT_SIZE ,
HID_FEATURE_REPORT , HID_REQ_GET_REPORT ) ;
if ( ret < 0 )
2016-03-01 22:49:54 +03:00
goto err ;
ret = 0 ;
2013-01-22 21:01:21 +04:00
2014-04-15 00:50:19 +04:00
hid_dbg ( tdev - > hdev , " <- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx \n " ,
buf [ 0 ] , buf [ 1 ] , buf [ 2 ] , buf [ 3 ] , buf [ 4 ] ,
buf [ 5 ] , buf [ 6 ] , buf [ 7 ] , buf [ 8 ] ) ;
2016-03-01 22:49:54 +03:00
err :
mutex_unlock ( & tdev - > lock ) ;
return ret ;
2013-01-22 21:01:21 +04:00
}
2014-04-15 00:50:19 +04:00
static int thingm_version ( struct thingm_device * tdev )
2013-01-22 21:01:21 +04:00
{
2014-04-15 00:50:19 +04:00
u8 buf [ REPORT_SIZE ] = { REPORT_ID , ' v ' , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
int err ;
err = thingm_recv ( tdev , buf ) ;
if ( err )
return err ;
2013-01-22 21:01:21 +04:00
2014-04-15 00:50:19 +04:00
tdev - > version . major = buf [ 3 ] ;
tdev - > version . minor = buf [ 4 ] ;
return 0 ;
2013-01-22 21:01:21 +04:00
}
2014-04-15 00:50:19 +04:00
static int thingm_write_color ( struct thingm_rgb * rgb )
2013-01-22 21:01:21 +04:00
{
2014-04-15 00:50:20 +04:00
u8 buf [ REPORT_SIZE ] = { REPORT_ID , ' c ' , 0 , 0 , 0 , 0 , 0 , rgb - > num , 0 } ;
2014-04-15 00:50:19 +04:00
buf [ 2 ] = rgb - > red . ldev . brightness ;
buf [ 3 ] = rgb - > green . ldev . brightness ;
buf [ 4 ] = rgb - > blue . ldev . brightness ;
2013-01-22 21:01:21 +04:00
2014-04-15 00:50:19 +04:00
return thingm_send ( rgb - > tdev , buf ) ;
2013-01-22 21:01:21 +04:00
}
2016-02-29 23:34:50 +03:00
static int thingm_led_set ( struct led_classdev * ldev ,
enum led_brightness brightness )
2013-01-22 21:01:21 +04:00
{
2016-02-29 23:34:50 +03:00
struct thingm_led * led = container_of ( ldev , struct thingm_led , ldev ) ;
2014-04-15 00:50:19 +04:00
2016-04-20 21:33:05 +03:00
return thingm_write_color ( led - > rgb ) ;
2014-04-15 00:50:19 +04:00
}
2013-01-22 21:01:21 +04:00
2016-04-17 23:46:59 +03:00
static int thingm_init_led ( struct thingm_led * led , const char * color_name ,
struct thingm_rgb * rgb , int minor )
{
snprintf ( led - > name , sizeof ( led - > name ) , " thingm%d:%s:led%d " ,
minor , color_name , rgb - > num ) ;
led - > ldev . name = led - > name ;
led - > ldev . max_brightness = 255 ;
led - > ldev . brightness_set_blocking = thingm_led_set ;
2016-04-17 23:51:46 +03:00
led - > ldev . flags = LED_HW_PLUGGABLE ;
2016-04-17 23:46:59 +03:00
led - > rgb = rgb ;
return devm_led_classdev_register ( & rgb - > tdev - > hdev - > dev , & led - > ldev ) ;
}
2014-04-15 00:50:19 +04:00
static int thingm_init_rgb ( struct thingm_rgb * rgb )
{
const int minor = ( ( struct hidraw * ) rgb - > tdev - > hdev - > hidraw ) - > minor ;
int err ;
/* Register the red diode */
2016-04-17 23:46:59 +03:00
err = thingm_init_led ( & rgb - > red , " red " , rgb , minor ) ;
2014-04-15 00:50:19 +04:00
if ( err )
return err ;
/* Register the green diode */
2016-04-17 23:46:59 +03:00
err = thingm_init_led ( & rgb - > green , " green " , rgb , minor ) ;
2014-04-15 00:50:19 +04:00
if ( err )
2016-02-29 23:38:26 +03:00
return err ;
2014-04-15 00:50:19 +04:00
/* Register the blue diode */
2016-04-17 23:46:59 +03:00
return thingm_init_led ( & rgb - > blue , " blue " , rgb , minor ) ;
2014-04-15 00:50:19 +04:00
}
2013-01-22 21:01:21 +04:00
static int thingm_probe ( struct hid_device * hdev , const struct hid_device_id * id )
{
2014-04-15 00:50:19 +04:00
struct thingm_device * tdev ;
int i , err ;
2013-01-22 21:01:21 +04:00
2014-04-15 00:50:19 +04:00
tdev = devm_kzalloc ( & hdev - > dev , sizeof ( struct thingm_device ) ,
GFP_KERNEL ) ;
if ( ! tdev )
2013-01-22 21:01:21 +04:00
return - ENOMEM ;
2014-04-15 00:50:19 +04:00
tdev - > hdev = hdev ;
hid_set_drvdata ( hdev , tdev ) ;
2013-01-22 21:01:21 +04:00
2014-04-15 00:50:19 +04:00
err = hid_parse ( hdev ) ;
if ( err )
2016-03-10 22:52:21 +03:00
return err ;
2013-01-22 21:01:21 +04:00
2014-04-15 00:50:19 +04:00
mutex_init ( & tdev - > lock ) ;
err = thingm_version ( tdev ) ;
if ( err )
2016-03-10 22:52:21 +03:00
return err ;
2013-01-22 21:01:21 +04:00
2014-04-15 00:50:19 +04:00
hid_dbg ( hdev , " firmware version: %c.%c \n " ,
tdev - > version . major , tdev - > version . minor ) ;
2013-01-22 21:01:21 +04:00
2014-04-15 00:50:19 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( thingm_fwinfo ) & & ! tdev - > fwinfo ; + + i )
if ( thingm_fwinfo [ i ] . major = = tdev - > version . major )
tdev - > fwinfo = & thingm_fwinfo [ i ] ;
if ( ! tdev - > fwinfo ) {
hid_err ( hdev , " unsupported firmware %c \n " , tdev - > version . major ) ;
2016-03-10 22:52:21 +03:00
return - ENODEV ;
2014-04-15 00:50:19 +04:00
}
tdev - > rgb = devm_kzalloc ( & hdev - > dev ,
sizeof ( struct thingm_rgb ) * tdev - > fwinfo - > numrgb ,
GFP_KERNEL ) ;
2016-03-10 22:52:21 +03:00
if ( ! tdev - > rgb )
return - ENOMEM ;
err = hid_hw_start ( hdev , HID_CONNECT_HIDRAW ) ;
if ( err )
return err ;
2014-04-15 00:50:19 +04:00
for ( i = 0 ; i < tdev - > fwinfo - > numrgb ; + + i ) {
struct thingm_rgb * rgb = tdev - > rgb + i ;
rgb - > tdev = tdev ;
rgb - > num = tdev - > fwinfo - > first + i ;
err = thingm_init_rgb ( rgb ) ;
2016-03-10 22:52:21 +03:00
if ( err ) {
hid_hw_stop ( hdev ) ;
return err ;
}
2014-04-15 00:50:19 +04:00
}
2013-01-22 21:01:21 +04:00
2014-04-15 00:50:19 +04:00
return 0 ;
2013-01-22 21:01:21 +04:00
}
static const struct hid_device_id thingm_table [ ] = {
{ HID_USB_DEVICE ( USB_VENDOR_ID_THINGM , USB_DEVICE_ID_BLINK1 ) } ,
{ }
} ;
MODULE_DEVICE_TABLE ( hid , thingm_table ) ;
static struct hid_driver thingm_driver = {
. name = " thingm " ,
. probe = thingm_probe ,
. id_table = thingm_table ,
} ;
module_hid_driver ( thingm_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Vivien Didelot <vivien.didelot@savoirfairelinux.com> " ) ;
MODULE_DESCRIPTION ( " ThingM blink(1) USB RGB LED driver " ) ;