2015-02-02 12:26:28 +01:00
/*
* cros_ec_lightbar - expose the Chromebook Pixel lightbar to userspace
*
* Copyright ( C ) 2014 Google , Inc .
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# define pr_fmt(fmt) "cros_ec_lightbar: " fmt
# include <linux/ctype.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/fs.h>
# include <linux/kobject.h>
# include <linux/mfd/cros_ec.h>
# include <linux/mfd/cros_ec_commands.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/sched.h>
# include <linux/types.h>
# include <linux/uaccess.h>
2015-06-09 13:04:42 +02:00
# include <linux/slab.h>
2015-02-02 12:26:28 +01:00
/* Rate-limit the lightbar interface to prevent DoS. */
static unsigned long lb_interval_jiffies = 50 * HZ / 1000 ;
2017-05-16 17:46:48 +02:00
/*
* Whether or not we have given userspace control of the lightbar .
* If this is true , we won ' t do anything during suspend / resume .
*/
static bool userspace_control ;
2017-05-16 17:46:48 +02:00
static struct cros_ec_dev * ec_with_lightbar ;
2017-05-16 17:46:48 +02:00
2015-02-02 12:26:28 +01:00
static ssize_t interval_msec_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
unsigned long msec = lb_interval_jiffies * 1000 / HZ ;
return scnprintf ( buf , PAGE_SIZE , " %lu \n " , msec ) ;
}
static ssize_t interval_msec_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
unsigned long msec ;
if ( kstrtoul ( buf , 0 , & msec ) )
return - EINVAL ;
lb_interval_jiffies = msec * HZ / 1000 ;
return count ;
}
static DEFINE_MUTEX ( lb_mutex ) ;
/* Return 0 if able to throttle correctly, error otherwise */
static int lb_throttle ( void )
{
static unsigned long last_access ;
unsigned long now , next_timeslot ;
long delay ;
int ret = 0 ;
mutex_lock ( & lb_mutex ) ;
now = jiffies ;
next_timeslot = last_access + lb_interval_jiffies ;
if ( time_before ( now , next_timeslot ) ) {
delay = ( long ) ( next_timeslot ) - ( long ) now ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( schedule_timeout ( delay ) > 0 ) {
/* interrupted - just abort */
ret = - EINTR ;
goto out ;
}
now = jiffies ;
}
last_access = now ;
out :
mutex_unlock ( & lb_mutex ) ;
return ret ;
}
2015-06-09 13:04:47 +02:00
static struct cros_ec_command * alloc_lightbar_cmd_msg ( struct cros_ec_dev * ec )
2015-06-09 13:04:42 +02:00
{
struct cros_ec_command * msg ;
int len ;
len = max ( sizeof ( struct ec_params_lightbar ) ,
sizeof ( struct ec_response_lightbar ) ) ;
msg = kmalloc ( sizeof ( * msg ) + len , GFP_KERNEL ) ;
if ( ! msg )
return NULL ;
msg - > version = 0 ;
2015-06-09 13:04:47 +02:00
msg - > command = EC_CMD_LIGHTBAR_CMD + ec - > cmd_offset ;
2015-06-09 13:04:42 +02:00
msg - > outsize = sizeof ( struct ec_params_lightbar ) ;
msg - > insize = sizeof ( struct ec_response_lightbar ) ;
return msg ;
}
2015-02-02 12:26:28 +01:00
2015-06-09 13:04:47 +02:00
static int get_lightbar_version ( struct cros_ec_dev * ec ,
2015-02-02 12:26:28 +01:00
uint32_t * ver_ptr , uint32_t * flg_ptr )
{
struct ec_params_lightbar * param ;
struct ec_response_lightbar * resp ;
2015-06-09 13:04:42 +02:00
struct cros_ec_command * msg ;
2015-02-02 12:26:28 +01:00
int ret ;
2015-06-09 13:04:47 +02:00
msg = alloc_lightbar_cmd_msg ( ec ) ;
2015-06-09 13:04:42 +02:00
if ( ! msg )
2015-02-02 12:26:28 +01:00
return 0 ;
2015-06-09 13:04:42 +02:00
param = ( struct ec_params_lightbar * ) msg - > data ;
param - > cmd = LIGHTBAR_CMD_VERSION ;
2015-06-09 13:04:47 +02:00
ret = cros_ec_cmd_xfer ( ec - > ec_dev , msg ) ;
2015-06-09 13:04:42 +02:00
if ( ret < 0 ) {
ret = 0 ;
goto exit ;
}
switch ( msg - > result ) {
2015-02-02 12:26:28 +01:00
case EC_RES_INVALID_PARAM :
/* Pixel had no version command. */
if ( ver_ptr )
* ver_ptr = 0 ;
if ( flg_ptr )
* flg_ptr = 0 ;
2015-06-09 13:04:42 +02:00
ret = 1 ;
goto exit ;
2015-02-02 12:26:28 +01:00
case EC_RES_SUCCESS :
2015-06-09 13:04:42 +02:00
resp = ( struct ec_response_lightbar * ) msg - > data ;
2015-02-02 12:26:28 +01:00
/* Future devices w/lightbars should implement this command */
if ( ver_ptr )
* ver_ptr = resp - > version . num ;
if ( flg_ptr )
* flg_ptr = resp - > version . flags ;
2015-06-09 13:04:42 +02:00
ret = 1 ;
goto exit ;
2015-02-02 12:26:28 +01:00
}
/* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */
2015-06-09 13:04:42 +02:00
ret = 0 ;
exit :
kfree ( msg ) ;
return ret ;
2015-02-02 12:26:28 +01:00
}
static ssize_t version_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2015-06-09 13:04:42 +02:00
uint32_t version = 0 , flags = 0 ;
2018-05-30 09:04:13 -07:00
struct cros_ec_dev * ec = to_cros_ec_dev ( dev ) ;
2015-02-02 12:26:28 +01:00
int ret ;
ret = lb_throttle ( ) ;
if ( ret )
return ret ;
/* This should always succeed, because we check during init. */
if ( ! get_lightbar_version ( ec , & version , & flags ) )
return - EIO ;
return scnprintf ( buf , PAGE_SIZE , " %d %d \n " , version , flags ) ;
}
static ssize_t brightness_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct ec_params_lightbar * param ;
2015-06-09 13:04:42 +02:00
struct cros_ec_command * msg ;
2015-02-02 12:26:28 +01:00
int ret ;
unsigned int val ;
2018-05-30 09:04:13 -07:00
struct cros_ec_dev * ec = to_cros_ec_dev ( dev ) ;
2015-02-02 12:26:28 +01:00
if ( kstrtouint ( buf , 0 , & val ) )
return - EINVAL ;
2015-06-09 13:04:47 +02:00
msg = alloc_lightbar_cmd_msg ( ec ) ;
2015-06-09 13:04:42 +02:00
if ( ! msg )
return - ENOMEM ;
param = ( struct ec_params_lightbar * ) msg - > data ;
2015-06-09 13:04:43 +02:00
param - > cmd = LIGHTBAR_CMD_SET_BRIGHTNESS ;
param - > set_brightness . num = val ;
2015-02-02 12:26:28 +01:00
ret = lb_throttle ( ) ;
if ( ret )
2015-06-09 13:04:42 +02:00
goto exit ;
2015-02-02 12:26:28 +01:00
2015-06-09 13:04:47 +02:00
ret = cros_ec_cmd_xfer ( ec - > ec_dev , msg ) ;
2015-02-02 12:26:28 +01:00
if ( ret < 0 )
2015-06-09 13:04:42 +02:00
goto exit ;
2015-02-02 12:26:28 +01:00
2015-06-09 13:04:42 +02:00
if ( msg - > result ! = EC_RES_SUCCESS ) {
ret = - EINVAL ;
goto exit ;
}
2015-02-02 12:26:28 +01:00
2015-06-09 13:04:42 +02:00
ret = count ;
exit :
kfree ( msg ) ;
return ret ;
2015-02-02 12:26:28 +01:00
}
/*
* We expect numbers , and we ' ll keep reading until we find them , skipping over
* any whitespace ( sysfs guarantees that the input is null - terminated ) . Every
* four numbers are sent to the lightbar as < LED , R , G , B > . We fail at the first
* parsing error , if we don ' t parse any numbers , or if we have numbers left
* over .
*/
static ssize_t led_rgb_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct ec_params_lightbar * param ;
2015-06-09 13:04:42 +02:00
struct cros_ec_command * msg ;
2018-05-30 09:04:13 -07:00
struct cros_ec_dev * ec = to_cros_ec_dev ( dev ) ;
2015-02-02 12:26:28 +01:00
unsigned int val [ 4 ] ;
int ret , i = 0 , j = 0 , ok = 0 ;
2015-06-09 13:04:47 +02:00
msg = alloc_lightbar_cmd_msg ( ec ) ;
2015-06-09 13:04:42 +02:00
if ( ! msg )
return - ENOMEM ;
2015-02-02 12:26:28 +01:00
do {
/* Skip any whitespace */
while ( * buf & & isspace ( * buf ) )
buf + + ;
if ( ! * buf )
break ;
ret = sscanf ( buf , " %i " , & val [ i + + ] ) ;
if ( ret = = 0 )
2015-07-19 21:43:02 +02:00
goto exit ;
2015-02-02 12:26:28 +01:00
if ( i = = 4 ) {
2015-06-09 13:04:42 +02:00
param = ( struct ec_params_lightbar * ) msg - > data ;
2015-06-09 13:04:43 +02:00
param - > cmd = LIGHTBAR_CMD_SET_RGB ;
param - > set_rgb . led = val [ 0 ] ;
param - > set_rgb . red = val [ 1 ] ;
param - > set_rgb . green = val [ 2 ] ;
param - > set_rgb . blue = val [ 3 ] ;
2015-02-02 12:26:28 +01:00
/*
* Throttle only the first of every four transactions ,
* so that the user can update all four LEDs at once .
*/
if ( ( j + + % 4 ) = = 0 ) {
ret = lb_throttle ( ) ;
if ( ret )
2015-07-19 21:43:02 +02:00
goto exit ;
2015-02-02 12:26:28 +01:00
}
2015-06-09 13:04:47 +02:00
ret = cros_ec_cmd_xfer ( ec - > ec_dev , msg ) ;
2015-02-02 12:26:28 +01:00
if ( ret < 0 )
2015-06-09 13:04:42 +02:00
goto exit ;
2015-02-02 12:26:28 +01:00
2015-07-19 21:43:02 +02:00
if ( msg - > result ! = EC_RES_SUCCESS )
2015-06-09 13:04:42 +02:00
goto exit ;
2015-02-02 12:26:28 +01:00
i = 0 ;
ok = 1 ;
}
/* Skip over the number we just read */
while ( * buf & & ! isspace ( * buf ) )
buf + + ;
} while ( * buf ) ;
2015-06-09 13:04:42 +02:00
exit :
kfree ( msg ) ;
2015-02-02 12:26:28 +01:00
return ( ok & & i = = 0 ) ? count : - EINVAL ;
}
2015-03-03 13:22:33 +01:00
static char const * seqname [ ] = {
2015-02-02 12:26:28 +01:00
" ERROR " , " S5 " , " S3 " , " S0 " , " S5S3 " , " S3S0 " ,
2017-05-16 17:46:48 +02:00
" S0S3 " , " S3S5 " , " STOP " , " RUN " , " KONAMI " ,
" TAP " , " PROGRAM " ,
2015-02-02 12:26:28 +01:00
} ;
static ssize_t sequence_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct ec_params_lightbar * param ;
struct ec_response_lightbar * resp ;
2015-06-09 13:04:42 +02:00
struct cros_ec_command * msg ;
2015-02-02 12:26:28 +01:00
int ret ;
2018-05-30 09:04:13 -07:00
struct cros_ec_dev * ec = to_cros_ec_dev ( dev ) ;
2015-02-02 12:26:28 +01:00
2015-06-09 13:04:47 +02:00
msg = alloc_lightbar_cmd_msg ( ec ) ;
2015-06-09 13:04:42 +02:00
if ( ! msg )
return - ENOMEM ;
param = ( struct ec_params_lightbar * ) msg - > data ;
2015-02-02 12:26:28 +01:00
param - > cmd = LIGHTBAR_CMD_GET_SEQ ;
ret = lb_throttle ( ) ;
if ( ret )
2015-06-09 13:04:42 +02:00
goto exit ;
2015-02-02 12:26:28 +01:00
2015-06-09 13:04:47 +02:00
ret = cros_ec_cmd_xfer ( ec - > ec_dev , msg ) ;
2015-02-02 12:26:28 +01:00
if ( ret < 0 )
2015-06-09 13:04:42 +02:00
goto exit ;
2015-02-02 12:26:28 +01:00
2015-06-09 13:04:42 +02:00
if ( msg - > result ! = EC_RES_SUCCESS ) {
ret = scnprintf ( buf , PAGE_SIZE ,
" ERROR: EC returned %d \n " , msg - > result ) ;
goto exit ;
}
2015-02-02 12:26:28 +01:00
2015-06-09 13:04:42 +02:00
resp = ( struct ec_response_lightbar * ) msg - > data ;
2015-02-02 12:26:28 +01:00
if ( resp - > get_seq . num > = ARRAY_SIZE ( seqname ) )
2015-06-09 13:04:42 +02:00
ret = scnprintf ( buf , PAGE_SIZE , " %d \n " , resp - > get_seq . num ) ;
2015-02-02 12:26:28 +01:00
else
2015-06-09 13:04:42 +02:00
ret = scnprintf ( buf , PAGE_SIZE , " %s \n " ,
seqname [ resp - > get_seq . num ] ) ;
exit :
kfree ( msg ) ;
return ret ;
2015-02-02 12:26:28 +01:00
}
2017-05-16 17:46:48 +02:00
static int lb_send_empty_cmd ( struct cros_ec_dev * ec , uint8_t cmd )
{
struct ec_params_lightbar * param ;
struct cros_ec_command * msg ;
int ret ;
msg = alloc_lightbar_cmd_msg ( ec ) ;
if ( ! msg )
return - ENOMEM ;
param = ( struct ec_params_lightbar * ) msg - > data ;
param - > cmd = cmd ;
ret = lb_throttle ( ) ;
if ( ret )
goto error ;
ret = cros_ec_cmd_xfer ( ec - > ec_dev , msg ) ;
if ( ret < 0 )
goto error ;
if ( msg - > result ! = EC_RES_SUCCESS ) {
ret = - EINVAL ;
goto error ;
}
ret = 0 ;
error :
kfree ( msg ) ;
return ret ;
}
int lb_manual_suspend_ctrl ( struct cros_ec_dev * ec , uint8_t enable )
{
struct ec_params_lightbar * param ;
struct cros_ec_command * msg ;
int ret ;
2017-05-16 17:46:48 +02:00
if ( ec ! = ec_with_lightbar )
return 0 ;
2017-05-16 17:46:48 +02:00
msg = alloc_lightbar_cmd_msg ( ec ) ;
if ( ! msg )
return - ENOMEM ;
param = ( struct ec_params_lightbar * ) msg - > data ;
param - > cmd = LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL ;
param - > manual_suspend_ctrl . enable = enable ;
ret = lb_throttle ( ) ;
if ( ret )
goto error ;
ret = cros_ec_cmd_xfer ( ec - > ec_dev , msg ) ;
if ( ret < 0 )
goto error ;
if ( msg - > result ! = EC_RES_SUCCESS ) {
ret = - EINVAL ;
goto error ;
}
ret = 0 ;
error :
kfree ( msg ) ;
return ret ;
}
2017-11-20 17:15:25 +01:00
EXPORT_SYMBOL ( lb_manual_suspend_ctrl ) ;
2017-05-16 17:46:48 +02:00
int lb_suspend ( struct cros_ec_dev * ec )
{
2017-05-16 17:46:48 +02:00
if ( userspace_control | | ec ! = ec_with_lightbar )
2017-05-16 17:46:48 +02:00
return 0 ;
2017-05-16 17:46:48 +02:00
return lb_send_empty_cmd ( ec , LIGHTBAR_CMD_SUSPEND ) ;
}
2017-11-20 17:15:25 +01:00
EXPORT_SYMBOL ( lb_suspend ) ;
2017-05-16 17:46:48 +02:00
int lb_resume ( struct cros_ec_dev * ec )
{
2017-05-16 17:46:48 +02:00
if ( userspace_control | | ec ! = ec_with_lightbar )
2017-05-16 17:46:48 +02:00
return 0 ;
2017-05-16 17:46:48 +02:00
return lb_send_empty_cmd ( ec , LIGHTBAR_CMD_RESUME ) ;
}
2017-11-20 17:15:25 +01:00
EXPORT_SYMBOL ( lb_resume ) ;
2017-05-16 17:46:48 +02:00
2015-02-02 12:26:28 +01:00
static ssize_t sequence_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct ec_params_lightbar * param ;
2015-06-09 13:04:42 +02:00
struct cros_ec_command * msg ;
2015-02-02 12:26:28 +01:00
unsigned int num ;
int ret , len ;
2018-05-30 09:04:13 -07:00
struct cros_ec_dev * ec = to_cros_ec_dev ( dev ) ;
2015-02-02 12:26:28 +01:00
for ( len = 0 ; len < count ; len + + )
if ( ! isalnum ( buf [ len ] ) )
break ;
for ( num = 0 ; num < ARRAY_SIZE ( seqname ) ; num + + )
if ( ! strncasecmp ( seqname [ num ] , buf , len ) )
break ;
if ( num > = ARRAY_SIZE ( seqname ) ) {
ret = kstrtouint ( buf , 0 , & num ) ;
if ( ret )
return ret ;
}
2015-07-18 19:30:33 +02:00
msg = alloc_lightbar_cmd_msg ( ec ) ;
if ( ! msg )
return - ENOMEM ;
2015-06-09 13:04:42 +02:00
param = ( struct ec_params_lightbar * ) msg - > data ;
2015-02-02 12:26:28 +01:00
param - > cmd = LIGHTBAR_CMD_SEQ ;
param - > seq . num = num ;
ret = lb_throttle ( ) ;
if ( ret )
2015-07-18 19:30:33 +02:00
goto exit ;
2015-02-02 12:26:28 +01:00
2015-06-09 13:04:47 +02:00
ret = cros_ec_cmd_xfer ( ec - > ec_dev , msg ) ;
2015-02-02 12:26:28 +01:00
if ( ret < 0 )
2015-07-18 19:30:33 +02:00
goto exit ;
2015-02-02 12:26:28 +01:00
2015-07-18 19:30:33 +02:00
if ( msg - > result ! = EC_RES_SUCCESS ) {
ret = - EINVAL ;
goto exit ;
}
2015-02-02 12:26:28 +01:00
2015-07-18 19:30:33 +02:00
ret = count ;
exit :
kfree ( msg ) ;
return ret ;
2015-02-02 12:26:28 +01:00
}
2017-05-16 17:46:48 +02:00
static ssize_t program_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
int extra_bytes , max_size , ret ;
struct ec_params_lightbar * param ;
struct cros_ec_command * msg ;
2018-05-30 09:04:13 -07:00
struct cros_ec_dev * ec = to_cros_ec_dev ( dev ) ;
2017-05-16 17:46:48 +02:00
/*
* We might need to reject the program for size reasons . The EC
* enforces a maximum program size , but we also don ' t want to try
* and send a program that is too big for the protocol . In order
* to ensure the latter , we also need to ensure we have extra bytes
* to represent the rest of the packet .
*/
extra_bytes = sizeof ( * param ) - sizeof ( param - > set_program . data ) ;
max_size = min ( EC_LB_PROG_LEN , ec - > ec_dev - > max_request - extra_bytes ) ;
if ( count > max_size ) {
dev_err ( dev , " Program is %u bytes, too long to send (max: %u) " ,
( unsigned int ) count , max_size ) ;
return - EINVAL ;
}
msg = alloc_lightbar_cmd_msg ( ec ) ;
if ( ! msg )
return - ENOMEM ;
ret = lb_throttle ( ) ;
if ( ret )
goto exit ;
dev_info ( dev , " Copying %zu byte program to EC " , count ) ;
param = ( struct ec_params_lightbar * ) msg - > data ;
param - > cmd = LIGHTBAR_CMD_SET_PROGRAM ;
param - > set_program . size = count ;
memcpy ( param - > set_program . data , buf , count ) ;
/*
* We need to set the message size manually or else it will use
* EC_LB_PROG_LEN . This might be too long , and the program
* is unlikely to use all of the space .
*/
msg - > outsize = count + extra_bytes ;
ret = cros_ec_cmd_xfer ( ec - > ec_dev , msg ) ;
if ( ret < 0 )
goto exit ;
if ( msg - > result ! = EC_RES_SUCCESS ) {
ret = - EINVAL ;
goto exit ;
}
ret = count ;
exit :
kfree ( msg ) ;
return ret ;
}
2017-05-16 17:46:48 +02:00
static ssize_t userspace_control_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
return scnprintf ( buf , PAGE_SIZE , " %d \n " , userspace_control ) ;
}
static ssize_t userspace_control_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
bool enable ;
int ret ;
ret = strtobool ( buf , & enable ) ;
if ( ret < 0 )
return ret ;
userspace_control = enable ;
return count ;
}
2015-02-02 12:26:28 +01:00
/* Module initialization */
static DEVICE_ATTR_RW ( interval_msec ) ;
static DEVICE_ATTR_RO ( version ) ;
static DEVICE_ATTR_WO ( brightness ) ;
static DEVICE_ATTR_WO ( led_rgb ) ;
static DEVICE_ATTR_RW ( sequence ) ;
2017-05-16 17:46:48 +02:00
static DEVICE_ATTR_WO ( program ) ;
2017-05-16 17:46:48 +02:00
static DEVICE_ATTR_RW ( userspace_control ) ;
2017-05-16 17:46:48 +02:00
2015-02-02 12:26:28 +01:00
static struct attribute * __lb_cmds_attrs [ ] = {
& dev_attr_interval_msec . attr ,
& dev_attr_version . attr ,
& dev_attr_brightness . attr ,
& dev_attr_led_rgb . attr ,
& dev_attr_sequence . attr ,
2017-05-16 17:46:48 +02:00
& dev_attr_program . attr ,
2017-05-16 17:46:48 +02:00
& dev_attr_userspace_control . attr ,
2015-02-02 12:26:28 +01:00
NULL ,
} ;
2017-05-16 17:46:48 +02:00
bool ec_has_lightbar ( struct cros_ec_dev * ec )
{
return ! ! get_lightbar_version ( ec , NULL , NULL ) ;
}
2015-06-09 13:04:47 +02:00
static umode_t cros_ec_lightbar_attrs_are_visible ( struct kobject * kobj ,
struct attribute * a , int n )
2015-02-02 12:26:28 +01:00
{
2015-06-09 13:04:47 +02:00
struct device * dev = container_of ( kobj , struct device , kobj ) ;
2018-05-30 09:04:13 -07:00
struct cros_ec_dev * ec = to_cros_ec_dev ( dev ) ;
2015-12-27 21:15:45 +08:00
struct platform_device * pdev = to_platform_device ( ec - > dev ) ;
2016-05-11 11:05:35 -07:00
struct cros_ec_platform * pdata = pdev - > dev . platform_data ;
int is_cros_ec ;
is_cros_ec = strcmp ( pdata - > ec_name , CROS_EC_DEV_NAME ) ;
if ( is_cros_ec ! = 0 )
2015-06-09 13:04:47 +02:00
return 0 ;
2015-02-02 12:26:28 +01:00
/* Only instantiate this stuff if the EC has a lightbar */
2017-05-16 17:46:48 +02:00
if ( ec_has_lightbar ( ec ) ) {
ec_with_lightbar = ec ;
2015-06-09 13:04:47 +02:00
return a - > mode ;
2017-05-16 17:46:48 +02:00
}
2017-05-16 17:46:48 +02:00
return 0 ;
2015-02-02 12:26:28 +01:00
}
2015-06-09 13:04:47 +02:00
struct attribute_group cros_ec_lightbar_attr_group = {
. name = " lightbar " ,
. attrs = __lb_cmds_attrs ,
. is_visible = cros_ec_lightbar_attrs_are_visible ,
} ;
2017-11-20 17:15:25 +01:00
EXPORT_SYMBOL ( cros_ec_lightbar_attr_group ) ;