660 lines
24 KiB
C
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");
|