sonic-buildimage/platform/pddf/i2c/modules/led/pddf_led_module.c

660 lines
24 KiB
C
Raw Normal View History

/*
* Copyright 2019 Broadcom.
* The term Broadcom refers to Broadcom Inc. and/or its subsidiaries.
*
* 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.
*
*
* A pddf kernel module to manage various LEDs of a switch
*/
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/hwmon-sysfs.h>
#include "pddf_led_defs.h"
#include "pddf_client_defs.h"
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#define DEBUG 0
LED_OPS_DATA sys_led_ops_data[1]={0};
LED_OPS_DATA* psu_led_ops_data=NULL;
LED_OPS_DATA diag_led_ops_data[1]= {0};
LED_OPS_DATA fan_led_ops_data[1]= {0};
LED_OPS_DATA loc_led_ops_data[1]= {0};
LED_OPS_DATA* fantray_led_ops_data=NULL;
LED_OPS_DATA temp_data={0};
LED_OPS_DATA* dev_list[LED_TYPE_MAX] = {
sys_led_ops_data,
NULL,
fan_led_ops_data,
NULL,
diag_led_ops_data,
loc_led_ops_data,
};
int num_psus = 0;
int num_fantrays = 0;
extern int board_i2c_cpld_read(unsigned short cpld_addr, u8 reg);
extern int board_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
extern ssize_t show_pddf_data(struct device *dev, struct device_attribute *da, char *buf);
extern ssize_t store_pddf_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count);
static LED_STATUS find_state_index(const char* state_str) {
int index;
char *ptr = (char *)state_str;
while (*ptr && *ptr!= '\n' && *ptr !='\0') ptr++;
*ptr='\0';
for ( index = 0; index < MAX_LED_STATUS; index++) {
/*int rc = strcmp(state_str, LED_STATUS_STR[index]) ;*/
if (strcmp(state_str, LED_STATUS_STR[index]) == 0 ) {
return index;
}
}
return MAX_LED_STATUS;
}
static LED_TYPE get_dev_type(char* name)
{
LED_TYPE ret = LED_TYPE_MAX;
if(strcasecmp(name, "SYS_LED")==0) {
ret = LED_SYS;
} else if(strcasecmp(name, "FAN_LED")==0) {
ret = LED_FAN;
} else if(strstr(name, "PSU_LED")) {
ret = LED_PSU;
} else if(strcasecmp(name, "DIAG_LED")==0) {
ret = LED_DIAG;
} else if(strcasecmp(name, "LOC_LED")==0) {
ret = LED_LOC;
} else if(strstr(name, "FANTRAY_LED")) {
ret = LED_FANTRAY;
}
#if DEBUG > 1
pddf_dbg(LED, KERN_INFO "LED get_dev_type: %s; %d\n", name, ret);
#endif
return (ret);
}
static int dev_index_check(LED_TYPE type, int index)
{
#if DEBUG
pddf_dbg(LED, "dev_index_check: type:%s index:%d num_psus:%d num_fantrays:%d\n",
LED_TYPE_STR[type], index, num_psus, num_fantrays);
#endif
switch(type)
{
case LED_PSU:
if(index >= num_psus) return (-1);
break;
case LED_FANTRAY:
if(index >= num_fantrays) return (-1);
break;
default:
if(index >= 1) return (-1);
break;
}
return (0);
}
static LED_OPS_DATA* find_led_ops_data(struct device_attribute *da)
{
struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da;
LED_OPS_DATA* ptr=(LED_OPS_DATA*)_ptr->addr;
LED_TYPE led_type;
if(!ptr || strlen(ptr->device_name)==0 ) return(NULL);
if((led_type=get_dev_type(ptr->device_name))==LED_TYPE_MAX) {
printk(KERN_ERR "PDDF_LED ERROR *%s Unsupported Led Type\n", __func__);
return(NULL);
}
if(dev_index_check(led_type, ptr->index)==-1) {
printk(KERN_ERR "PDDF_LED ERROR %s invalid index: %d for type:%s\n", __func__, ptr->index, ptr->device_name);
return(NULL);
}
#if DEBUG > 1
pddf_dbg(LED, "find_led_ops_data: name:%s; index=%d tempAddr:%p actualAddr:%p\n",
ptr->device_name, ptr->index, ptr, dev_list[led_type]+ptr->index);
#endif
return (dev_list[led_type]+ptr->index);
}
static void print_led_data(LED_OPS_DATA *ptr, LED_STATUS state)
{
int i = 0;
if(!ptr) return ;
pddf_dbg(LED, KERN_INFO "Print %s index:%d num_psus:%d num_fantrays:%d ADDR=%p\n",
ptr->device_name, ptr->index, num_psus, num_fantrays, ptr);
pddf_dbg(LED, KERN_INFO "\tindex: %d\n", ptr->index);
pddf_dbg(LED, KERN_INFO "\tcur_state: %d; %s \n", ptr->cur_state.state, ptr->cur_state.color);
for (i = 0; i< MAX_LED_STATUS; i++) {
if(ptr->data[i].swpld_addr && (i == state || state == -1)) {
pddf_dbg(LED, KERN_INFO "\t\t[%s]: addr/offset:0x%x;0x%x color:%s; value:%x; mask_bits: 0x%x; pos:%d\n",
LED_STATUS_STR[i],
ptr->data[i].swpld_addr, ptr->data[i].swpld_addr_offset,
LED_STATUS_STR[i], ptr->data[i].value, ptr->data[i].bits.mask_bits, ptr->data[i].bits.pos);
}
}
}
ssize_t get_status_led(struct device_attribute *da)
{
int ret=0;
struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da;
LED_OPS_DATA* temp_data_ptr=(LED_OPS_DATA*)_ptr->addr;
LED_OPS_DATA* ops_ptr=find_led_ops_data(da);
uint32_t color_val=0, sys_val=0;
int state=0;
if (!ops_ptr) {
pddf_dbg(LED, KERN_ERR "ERROR %s: Cannot find LED Ptr", __func__);
return (-1);
}
if (ops_ptr->swpld_addr == 0x0) {
pddf_dbg(LED, KERN_ERR "ERROR %s: device: %s %d not configured\n", __func__,
temp_data_ptr->device_name, temp_data_ptr->index);
return (-1);
}
sys_val = board_i2c_cpld_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset);
if (sys_val < 0)
return sys_val;
strcpy(temp_data.cur_state.color, "None");
for (state=0; state<MAX_LED_STATUS; state++) {
color_val = (sys_val & ~ops_ptr->data[state].bits.mask_bits);
if ((color_val ^ (ops_ptr->data[state].value<<ops_ptr->data[state].bits.pos))==0) {
strcpy(temp_data.cur_state.color, LED_STATUS_STR[state]);
}
}
#if DEBUG > 1
pddf_dbg(LED, KERN_ERR "Get : %s:%d addr/offset:0x%x; 0x%x value=0x%x [%s]\n",
ops_ptr->device_name, ops_ptr->index,
ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset, sys_val,
temp_data.cur_state.color);
#endif
return(ret);
}
ssize_t set_status_led(struct device_attribute *da)
{
int ret=0;
uint32_t sys_val=0, new_val=0;
LED_STATUS cur_state = MAX_LED_STATUS;
struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da;
LED_OPS_DATA* temp_data_ptr=(LED_OPS_DATA*)_ptr->addr;
LED_OPS_DATA* ops_ptr=find_led_ops_data(da);
char* _buf=temp_data_ptr->cur_state.color;
if (!ops_ptr) {
pddf_dbg(LED, KERN_ERR "PDDF_LED ERROR %s: Cannot find LED Ptr", __func__);
return (-1);
}
if (ops_ptr->swpld_addr == 0x0) {
pddf_dbg(LED, KERN_ERR "PDDF_LED ERROR %s: device: %s %d not configured\n",
__func__, ops_ptr->device_name, ops_ptr->index);
return (-1);
}
pddf_dbg(LED, KERN_ERR "%s: Set [%s;%d] color[%s]\n", __func__,
temp_data_ptr->device_name, temp_data_ptr->index,
temp_data_ptr->cur_state.color);
cur_state = find_state_index(_buf);
if (cur_state == MAX_LED_STATUS) {
pddf_dbg(LED, KERN_ERR "ERROR %s: not supported: %s\n", _buf, __func__);
return (-1);
}
if(ops_ptr->data[cur_state].swpld_addr != 0x0) {
sys_val = board_i2c_cpld_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset);
if (sys_val < 0)
return sys_val;
new_val = (sys_val & ops_ptr->data[cur_state].bits.mask_bits) |
(ops_ptr->data[cur_state].value << ops_ptr->data[cur_state].bits.pos);
} else {
pddf_dbg(LED, KERN_ERR "ERROR %s: %s %d state %d; %s not configured\n",__func__,
ops_ptr->device_name, ops_ptr->index, cur_state, _buf);
return (-1);
}
board_i2c_cpld_write(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset, new_val);
pddf_dbg(LED, KERN_INFO "Set color:%s; 0x%x:0x%x sys_val:0x%x new_val:0x%x read:0x%x\n",
LED_STATUS_STR[cur_state],
ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset,
sys_val, new_val,
ret = board_i2c_cpld_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset));
if (ret < 0)
{
pddf_dbg(LED, KERN_ERR "PDDF_LED ERROR %s: Error %d in reading from cpld(0x%x) offset 0x%x\n", __FUNCTION__, ret, ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset);
return ret;
}
return(ret);
}
ssize_t show_pddf_data(struct device *dev, struct device_attribute *da,
char *buf)
{
int ret = 0;
struct pddf_data_attribute *ptr = (struct pddf_data_attribute *)da;
switch(ptr->type)
{
case PDDF_CHAR:
ret = sprintf(buf, "%s\n", ptr->addr);
break;
case PDDF_INT_DEC:
ret = sprintf(buf, "%d\n", *(int*)(ptr->addr));
break;
case PDDF_INT_HEX:
ret = sprintf(buf, "0x%x\n", *(int*)(ptr->addr));
break;
case PDDF_USHORT:
ret = sprintf(buf, "0x%x\n", *(unsigned short *)(ptr->addr));
break;
case PDDF_UINT32:
ret = sprintf(buf, "0x%x\n", *(uint32_t *)(ptr->addr));
break;
default:
break;
}
#if DEBUG > 1
pddf_dbg(LED, "[ READ ] DATA ATTR PTR [%s] TYPE:%d, Value:[%s]\n",
ptr->dev_attr.attr.name, ptr->type, buf);
#endif
return ret;
}
ssize_t store_pddf_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count)
{
int ret = 0, num = 0;
struct pddf_data_attribute *ptr = (struct pddf_data_attribute *)da;
switch(ptr->type)
{
case PDDF_CHAR:
strncpy(ptr->addr, buf, strlen(buf)-1); // to discard newline char form buf
ptr->addr[strlen(buf)-1] = '\0';
#if DEBUG
pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR [%s] PDDF_CHAR VALUE:%s\n",
ptr->dev_attr.attr.name, ptr->addr);
#endif
break;
case PDDF_INT_DEC:
ret = kstrtoint(buf,10,&num);
if (ret==0)
*(int *)(ptr->addr) = num;
#if DEBUG
pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR [%s] PDDF_DEC VALUE:%d\n",
ptr->dev_attr.attr.name, *(int *)(ptr->addr));
#endif
break;
case PDDF_INT_HEX:
ret = kstrtoint(buf,16,&num);
if (ret==0)
*(int *)(ptr->addr) = num;
#if DEBUG
pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR [%s] PDDF_HEX VALUE:0x%x\n",
ptr->dev_attr.attr.name, *(int *)(ptr->addr));
#endif
break;
case PDDF_USHORT:
ret = kstrtoint(buf,16,&num);
if (ret==0)
*(unsigned short *)(ptr->addr) = (unsigned short)num;
#if DEBUG
pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR [%s] PDDF_USHORT VALUE:%x\n",
ptr->dev_attr.attr.name, *(unsigned short *)(ptr->addr));
#endif
break;
case PDDF_UINT32:
ret = kstrtoint(buf,16,&num);
if (ret==0)
*(uint32_t *)(ptr->addr) = (uint32_t)num;
#if DEBUG
pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR [%s] PDDF_UINT32 VALUE:%d\n",
ptr->dev_attr.attr.name, *(uint32_t *)(ptr->addr));
#endif
break;
default:
break;
}
return count;
}
static int load_led_ops_data(struct device_attribute *da, LED_STATUS state)
{
struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da;
LED_OPS_DATA* ptr=(LED_OPS_DATA*)_ptr->addr;
LED_TYPE led_type;
LED_OPS_DATA* ops_ptr=NULL;
if(!ptr || strlen(ptr->device_name)==0 ) {
pddf_dbg(LED, KERN_INFO "SYSTEM_LED: load_led_ops_data return -1 device_name:%s\n", ptr? ptr->device_name:"NULL");
return(-1);
}
if(ptr->device_name)
{
pddf_dbg(LED, KERN_INFO "[%s]: load_led_ops_data: index=%d addr=0x%x;0x%x valu=0x%x\n",
ptr->device_name, ptr->index, ptr->swpld_addr, ptr->swpld_addr_offset, ptr->data[0].value);
}
if((led_type=get_dev_type(ptr->device_name))==LED_TYPE_MAX) {
pddf_dbg(LED, KERN_ERR "PDDF_LED ERROR *%s Unsupported Led Type\n", __func__);
return(-1);
}
if(dev_index_check(led_type, ptr->index)==-1) {
pddf_dbg(LED, KERN_ERR "PDDF_LED ERROR %s invalid index: %d for type:%d\n", __func__, ptr->index, led_type);
return(-1);
}
ops_ptr = dev_list[led_type]+ptr->index;
memcpy(ops_ptr->device_name, ptr->device_name, sizeof(ops_ptr->device_name));
ops_ptr->index = ptr->index;
memcpy(&ops_ptr->data[state], &ptr->data[0], sizeof(LED_DATA));
ops_ptr->data[state].swpld_addr = ptr->swpld_addr;
ops_ptr->data[state].swpld_addr_offset = ptr->swpld_addr_offset;
ops_ptr->swpld_addr = ptr->swpld_addr;
ops_ptr->swpld_addr_offset = ptr->swpld_addr_offset;
print_led_data(dev_list[led_type]+ptr->index, state);
memset(ptr, 0, sizeof(LED_OPS_DATA));
return (0);
}
static int show_led_ops_data(struct device_attribute *da)
{
LED_OPS_DATA* ops_ptr=find_led_ops_data(da);
print_led_data(ops_ptr, -1);
return(0);
}
static int verify_led_ops_data(struct device_attribute *da)
{
struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da;
LED_OPS_DATA* ptr=(LED_OPS_DATA*)_ptr->addr;
LED_OPS_DATA* ops_ptr=find_led_ops_data(da);
if(ops_ptr)
memcpy(ptr, ops_ptr, sizeof(LED_OPS_DATA));
else
{
pddf_dbg(LED, "SYSTEM_LED: verify_led_ops_data: Failed to find ops_ptr name:%s; index=%d\n", ptr->device_name, ptr->index);
}
return (0);
}
ssize_t dev_operation(struct device *dev, struct device_attribute *da, const char *buf, size_t count)
{
#if DEBUG
pddf_dbg(LED, KERN_INFO "dev_operation [%s]\n", buf);
#endif
if(strstr(buf, "STATUS_LED_COLOR")!= NULL) {
LED_STATUS index = find_state_index(buf);
if (index < MAX_LED_STATUS ) {
load_led_ops_data(da, index);
} else {
printk(KERN_ERR "PDDF_ERROR %s: Invalid state for dev_ops %s", __FUNCTION__, buf);
}
}
else if(strncmp(buf, "show", strlen("show"))==0 ) {
show_led_ops_data(da);
}
else if(strncmp(buf, "verify", strlen("verify"))==0 ) {
verify_led_ops_data(da);
}
else if(strncmp(buf, "get_status", strlen("get_status"))==0 ) {
get_status_led(da);
}
else if(strncmp(buf, "set_status", strlen("set_status"))==0 ) {
set_status_led(da);
}
else {
printk(KERN_ERR "PDDF_ERROR %s: Invalid value for dev_ops %s", __FUNCTION__, buf);
}
return(count);
}
ssize_t store_config_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count)
{
int ret, num;
struct pddf_data_attribute *ptr = (struct pddf_data_attribute *)da;
if(strncmp(ptr->dev_attr.attr.name, "num_psus", strlen("num_psus"))==0 ) {
ret = kstrtoint(buf,10,&num);
if (ret==0)
*(int *)(ptr->addr) = num;
if(psu_led_ops_data == NULL) {
if ((psu_led_ops_data = kzalloc(num * sizeof(LED_OPS_DATA), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "PDDF_LED ERROR failed to allocate memory for PSU LED\n");
return (count);
}
pddf_dbg(LED, "Allocate PSU LED Memory ADDR=%p\n", psu_led_ops_data);
dev_list[LED_PSU]=psu_led_ops_data;
}
#if DEBUG
pddf_dbg(LED, "[ WRITE ] ATTR CONFIG [%s] VALUE:%d; %d\n",
ptr->dev_attr.attr.name, num, num_psus);
#endif
return(count);
}
if(strncmp(ptr->dev_attr.attr.name, "num_fantrays", strlen("num_fantrays"))==0 ) {
ret = kstrtoint(buf,10,&num);
if (ret==0)
*(int *)(ptr->addr) = num;
if (fantray_led_ops_data == NULL) {
if ((fantray_led_ops_data = kzalloc(num * sizeof(LED_OPS_DATA), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "PDDF_LED ERROR failed to allocate memory for FANTRAY LED\n");
return (count);
}
pddf_dbg(LED, "Allocate FanTray LED Memory ADDR=%p\n", fantray_led_ops_data);
dev_list[LED_FANTRAY]=fantray_led_ops_data;
}
#if DEBUG
pddf_dbg(LED, "[ WRITE ] ATTR CONFIG [%s] VALUE:%d; %d\n",
ptr->dev_attr.attr.name, num, num_fantrays);
#endif
return(count);
}
return (count);
}
ssize_t store_bits_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count)
{
int len = 0, num1 = 0, num2 = 0, i=0, rc1=0, rc2=0;
char mask=0xFF;
char *pptr=NULL;
char bits[NAME_SIZE];
struct pddf_data_attribute *ptr = (struct pddf_data_attribute *)da;
MASK_BITS* bits_ptr=(MASK_BITS*)(ptr->addr);
strncpy(bits_ptr->bits, buf, strlen(buf)-1); // to discard newline char form buf
bits_ptr->bits[strlen(buf)-1] = '\0';
if((pptr=strstr(buf,":")) != NULL) {
len=pptr-buf;
sprintf(bits, buf);
bits[len]='\0';
rc1=kstrtoint(bits,16,&num1);
if (rc1==0)
{
sprintf(bits, ++pptr);
rc2=kstrtoint(bits,16,&num2);
if (rc2==0)
{
for (i=num2; i<=num1; i++) {
mask &= ~(1 << i);
}
bits_ptr->mask_bits = mask;
bits_ptr->pos = num2;
}
}
} else {
rc1=kstrtoint(buf,16,&num1);
if (rc1==0)
{
bits_ptr->mask_bits = mask & ~(1 << num1);
bits_ptr->pos = num1;
}
}
#if DEBUG
pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR Bits [%s] VALUE:%s mask:0x%x; pos:0x%x\n",
ptr->dev_attr.attr.name, bits_ptr->bits, bits_ptr->mask_bits, bits_ptr->pos);
#endif
return (count);
}
/**************************************************************************
* platform/ attributes
**************************************************************************/
PDDF_LED_DATA_ATTR(platform, num_psus, S_IWUSR|S_IRUGO, show_pddf_data,
store_config_data, PDDF_INT_DEC, sizeof(int), (void*)&num_psus);
PDDF_LED_DATA_ATTR(platform, num_fantrays, S_IWUSR|S_IRUGO, show_pddf_data,
store_config_data, PDDF_INT_DEC, sizeof(int), (void*)&num_fantrays);
struct attribute* attrs_platform[]={
&pddf_dev_platform_attr_num_psus.dev_attr.attr,
&pddf_dev_platform_attr_num_fantrays.dev_attr.attr,
NULL,
};
struct attribute_group attr_group_platform={
.attrs = attrs_platform,
};
/**************************************************************************
* led/ attributes
**************************************************************************/
PDDF_LED_DATA_ATTR(dev, device_name, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_CHAR, NAME_SIZE, (void*)&temp_data.device_name);
PDDF_LED_DATA_ATTR(dev, index, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_INT_DEC, sizeof(int), (void*)&temp_data.index);
PDDF_LED_DATA_ATTR(dev, swpld_addr, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_INT_HEX, sizeof(int), (void*)&temp_data.swpld_addr);
PDDF_LED_DATA_ATTR(dev, swpld_addr_offset, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_INT_HEX, sizeof(int), (void*)&temp_data.swpld_addr_offset);
PDDF_LED_DATA_ATTR(dev, dev_ops , S_IWUSR, NULL,
dev_operation, PDDF_CHAR, NAME_SIZE, (void*)&temp_data);
struct attribute* attrs_dev[]={
&pddf_dev_dev_attr_device_name.dev_attr.attr,
&pddf_dev_dev_attr_index.dev_attr.attr,
&pddf_dev_dev_attr_swpld_addr.dev_attr.attr,
&pddf_dev_dev_attr_swpld_addr_offset.dev_attr.attr,
&pddf_dev_dev_attr_dev_ops.dev_attr.attr,
NULL,
};
struct attribute_group attr_group_dev={
.attrs = attrs_dev,
};
/**************************************************************************
* state_attr/ attributes
**************************************************************************/
#define LED_DEV_STATE_ATTR_GROUP(name, func) \
PDDF_LED_DATA_ATTR(name, bits, S_IWUSR|S_IRUGO, show_pddf_data, \
store_bits_data, PDDF_CHAR, NAME_SIZE, func.bits.bits); \
PDDF_LED_DATA_ATTR(name, value, S_IWUSR|S_IRUGO, show_pddf_data, \
store_pddf_data, PDDF_USHORT, sizeof(unsigned short), func.value); \
struct attribute* attrs_##name[]={ \
&pddf_dev_##name##_attr_bits.dev_attr.attr, \
&pddf_dev_##name##_attr_value.dev_attr.attr, \
NULL, \
}; \
struct attribute_group attr_group_##name={ \
.attrs = attrs_##name, \
}; \
LED_DEV_STATE_ATTR_GROUP(state_attr, (void*)&temp_data.data[0])
/**************************************************************************
* cur_state/ attributes
**************************************************************************/
PDDF_LED_DATA_ATTR(cur_state, color, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_CHAR, NAME_SIZE, (void*)&temp_data.cur_state.color);
struct attribute* attrs_cur_state[]={
&pddf_dev_cur_state_attr_color.dev_attr.attr,
NULL,
};
struct attribute_group attr_group_cur_state={
.attrs = attrs_cur_state,
};
/*************************************************************************/
#define KOBJ_FREE(obj) \
if(obj) kobject_put(obj); \
void free_kobjs(void)
{
KOBJ_FREE(cur_state_kobj)
KOBJ_FREE(state_attr_kobj)
KOBJ_FREE(led_kobj)
KOBJ_FREE(platform_kobj)
}
int KBOJ_CREATE(char* name, struct kobject* parent, struct kobject** child)
{
if (parent) {
*child = kobject_create_and_add(name, parent);
} else {
printk(KERN_ERR "PDDF_LED ERROR to create %s kobj; null parent\n", name);
free_kobjs();
return (-ENOMEM);
}
return (0);
}
int LED_DEV_ATTR_CREATE(struct kobject *kobj, const struct attribute_group *attr, const char* name)
{
int status = sysfs_create_group(kobj, attr);
if(status) {
pddf_dbg(LED, KERN_ERR "Driver ERROR: sysfs_create %s failed rc=%d\n", name, status);
}
return (status);
}
static int __init led_init(void) {
struct kobject *device_kobj;
pddf_dbg(LED, KERN_INFO "PDDF GENERIC LED MODULE init..\n");
device_kobj = get_device_i2c_kobj();
if(!device_kobj)
return -ENOMEM;
KBOJ_CREATE("platform", device_kobj, &platform_kobj);
KBOJ_CREATE("led", device_kobj, &led_kobj);
KBOJ_CREATE("state_attr", led_kobj, &state_attr_kobj);
KBOJ_CREATE("cur_state", led_kobj, &cur_state_kobj);
LED_DEV_ATTR_CREATE(platform_kobj, &attr_group_platform, "attr_group_platform");
LED_DEV_ATTR_CREATE(led_kobj, &attr_group_dev, "attr_group_dev");
LED_DEV_ATTR_CREATE(state_attr_kobj, &attr_group_state_attr, "attr_group_state_attr");
LED_DEV_ATTR_CREATE(cur_state_kobj, &attr_group_cur_state, "attr_group_cur_state");
return (0);
}
static void __exit led_exit(void) {
pddf_dbg(LED, "PDDF GENERIC LED MODULE exit..\n");
free_kobjs();
if(psu_led_ops_data) kfree(psu_led_ops_data);
if(fantray_led_ops_data) kfree(fantray_led_ops_data);
}
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("Broadcom");
MODULE_DESCRIPTION("led driver");
MODULE_LICENSE("GPL");