sonic-buildimage/platform/pddf/i2c/modules/led/pddf_led_module.c
fk410167 a3dd3f55f9
Platform Driver Developement Framework (PDDF) (#4756)
This change introduces PDDF which is described here: https://github.com/Azure/SONiC/pull/536

Most of the platform bring up effort goes in developing the platform device drivers, SONiC platform APIs and validating them. Typically each platform vendor writes their own drivers and platform APIs which is very tailor made to that platform. This involves writing code, building, installing it on the target platform devices and testing. Many of the details of the platform are hard coded into these drivers, from the HW spec. They go through this cycle repetitively till everything works fine, and is validated before upstreaming the code.
PDDF aims to make this platform driver and platform APIs development process much simpler by providing a data driven development framework. This is enabled by:

JSON descriptor files for platform data
Generic data-driven drivers for various devices
Generic SONiC platform APIs
Vendor specific extensions for customisation and extensibility

Signed-off-by: Fuzail Khan <fuzail.khan@broadcom.com>
2020-11-12 10:22:38 -08:00

660 lines
24 KiB
C

/*
* 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");