⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ibmpex.c

📁 linux 内核源代码
💻 C
字号:
/* * A hwmon driver for the IBM PowerExecutive temperature/power sensors * Copyright (C) 2007 IBM * * Author: Darrick J. Wong <djwong@us.ibm.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; 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */#include <linux/ipmi.h>#include <linux/module.h>#include <linux/hwmon.h>#include <linux/hwmon-sysfs.h>#include <linux/jiffies.h>#include <linux/mutex.h>#define REFRESH_INTERVAL	(2 * HZ)#define DRVNAME			"ibmpex"#define PEX_GET_VERSION		1#define PEX_GET_SENSOR_COUNT	2#define PEX_GET_SENSOR_NAME	3#define PEX_RESET_HIGH_LOW	4#define PEX_GET_SENSOR_DATA	6#define PEX_NET_FUNCTION	0x3A#define PEX_COMMAND		0x3Cstatic inline u16 extract_value(const char *data, int offset){	return be16_to_cpup((u16 *)&data[offset]);}#define TEMP_SENSOR		1#define POWER_SENSOR		2#define PEX_SENSOR_TYPE_LEN	3static u8 const power_sensor_sig[] = {0x70, 0x77, 0x72};static u8 const temp_sensor_sig[]  = {0x74, 0x65, 0x6D};#define PEX_MULT_LEN		2static u8 const watt_sensor_sig[]  = {0x41, 0x43};#define PEX_NUM_SENSOR_FUNCS	3static char const * const power_sensor_name_templates[] = {	"%s%d_average",	"%s%d_average_lowest",	"%s%d_average_highest"};static char const * const temp_sensor_name_templates[] = {	"%s%d_input",	"%s%d_input_lowest",	"%s%d_input_highest"};static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);static void ibmpex_register_bmc(int iface, struct device *dev);static void ibmpex_bmc_gone(int iface);struct ibmpex_sensor_data {	int			in_use;	s16			values[PEX_NUM_SENSOR_FUNCS];	int			multiplier;	struct sensor_device_attribute_2	attr[PEX_NUM_SENSOR_FUNCS];};struct ibmpex_bmc_data {	struct list_head	list;	struct device		*hwmon_dev;	struct device		*bmc_device;	struct mutex		lock;	char			valid;	unsigned long		last_updated;	/* In jiffies */	struct ipmi_addr	address;	struct completion	read_complete;	ipmi_user_t		user;	int			interface;	struct kernel_ipmi_msg	tx_message;	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];	long			tx_msgid;	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];	unsigned long		rx_msg_len;	unsigned char		rx_result;	int			rx_recv_type;	unsigned char		sensor_major;	unsigned char		sensor_minor;	unsigned char		num_sensors;	struct ibmpex_sensor_data	*sensors;};struct ibmpex_driver_data {	struct list_head	bmc_data;	struct ipmi_smi_watcher	bmc_events;	struct ipmi_user_hndl	ipmi_hndlrs;};static struct ibmpex_driver_data driver_data = {	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),	.bmc_events = {		.owner = THIS_MODULE,		.new_smi = ibmpex_register_bmc,		.smi_gone = ibmpex_bmc_gone,	},	.ipmi_hndlrs = {		.ipmi_recv_hndl = ibmpex_msg_handler,	},};static int ibmpex_send_message(struct ibmpex_bmc_data *data){	int err;	err = ipmi_validate_addr(&data->address, sizeof(data->address));	if (err)		goto out;	data->tx_msgid++;	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,				   &data->tx_message, data, 0, 0, 0);	if (err)		goto out1;	return 0;out1:	dev_err(data->bmc_device, "request_settime=%x\n", err);	return err;out:	dev_err(data->bmc_device, "validate_addr=%x\n", err);	return err;}static int ibmpex_ver_check(struct ibmpex_bmc_data *data){	data->tx_msg_data[0] = PEX_GET_VERSION;	data->tx_message.data_len = 1;	ibmpex_send_message(data);	wait_for_completion(&data->read_complete);	if (data->rx_result || data->rx_msg_len != 6)		return -ENOENT;	data->sensor_major = data->rx_msg_data[0];	data->sensor_minor = data->rx_msg_data[1];	dev_info(data->bmc_device, "Found BMC with sensor interface "		 "v%d.%d %d-%02d-%02d on interface %d\n",		 data->sensor_major,		 data->sensor_minor,		 extract_value(data->rx_msg_data, 2),		 data->rx_msg_data[4],		 data->rx_msg_data[5],		 data->interface);	return 0;}static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data){	data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;	data->tx_message.data_len = 1;	ibmpex_send_message(data);	wait_for_completion(&data->read_complete);	if (data->rx_result || data->rx_msg_len != 1)		return -ENOENT;	return data->rx_msg_data[0];}static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor){	data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;	data->tx_msg_data[1] = sensor;	data->tx_message.data_len = 2;	ibmpex_send_message(data);	wait_for_completion(&data->read_complete);	if (data->rx_result || data->rx_msg_len < 1)		return -ENOENT;	return 0;}static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor){	data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;	data->tx_msg_data[1] = sensor;	data->tx_message.data_len = 2;	ibmpex_send_message(data);	wait_for_completion(&data->read_complete);	if (data->rx_result || data->rx_msg_len < 26) {		dev_err(data->bmc_device, "Error reading sensor %d.\n",			sensor);		return -ENOENT;	}	return 0;}static int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data){	data->tx_msg_data[0] = PEX_RESET_HIGH_LOW;	data->tx_message.data_len = 1;	ibmpex_send_message(data);	wait_for_completion(&data->read_complete);	return 0;}static void ibmpex_update_device(struct ibmpex_bmc_data *data){	int i, err;	mutex_lock(&data->lock);	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&	    data->valid)		goto out;	for (i = 0; i < data->num_sensors; i++) {		if (!data->sensors[i].in_use)			continue;		err = ibmpex_query_sensor_data(data, i);		if (err)			continue;		data->sensors[i].values[0] =			extract_value(data->rx_msg_data, 16);		data->sensors[i].values[1] =			extract_value(data->rx_msg_data, 18);		data->sensors[i].values[2] =			extract_value(data->rx_msg_data, 20);	}	data->last_updated = jiffies;	data->valid = 1;out:	mutex_unlock(&data->lock);}static struct ibmpex_bmc_data *get_bmc_data(int iface){	struct ibmpex_bmc_data *p, *next;	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)		if (p->interface == iface)			return p;	return NULL;}static ssize_t show_name(struct device *dev, struct device_attribute *devattr,			 char *buf){	return sprintf(buf, "%s\n", DRVNAME);}static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);static ssize_t ibmpex_show_sensor(struct device *dev,				  struct device_attribute *devattr,				  char *buf){	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);	int mult = data->sensors[attr->index].multiplier;	ibmpex_update_device(data);	return sprintf(buf, "%d\n",		       data->sensors[attr->index].values[attr->nr] * mult);}static ssize_t ibmpex_reset_high_low(struct device *dev,				     struct device_attribute *devattr,				     const char *buf,				     size_t count){	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);	ibmpex_reset_high_low_data(data);	return count;}static SENSOR_DEVICE_ATTR(reset_high_low, S_IWUSR, NULL,			  ibmpex_reset_high_low, 0);static int is_power_sensor(const char *sensor_id, int len){	if (len < PEX_SENSOR_TYPE_LEN)		return 0;	if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))		return 1;	return 0;}static int is_temp_sensor(const char *sensor_id, int len){	if (len < PEX_SENSOR_TYPE_LEN)		return 0;	if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))		return 1;	return 0;}static int power_sensor_multiplier(const char *sensor_id, int len){	int i;	for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)		if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))			return 1000000;	return 100000;}static int create_sensor(struct ibmpex_bmc_data *data, int type,			 int counter, int sensor, int func){	int err;	char *n;	n = kmalloc(32, GFP_KERNEL);	if (!n)		return -ENOMEM;	if (type == TEMP_SENSOR)		sprintf(n, temp_sensor_name_templates[func], "temp", counter);	else if (type == POWER_SENSOR)		sprintf(n, power_sensor_name_templates[func], "power", counter);	data->sensors[sensor].attr[func].dev_attr.attr.name = n;	data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO;	data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;	data->sensors[sensor].attr[func].index = sensor;	data->sensors[sensor].attr[func].nr = func;	err = device_create_file(data->bmc_device,				 &data->sensors[sensor].attr[func].dev_attr);	if (err) {		data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;		kfree(n);		return err;	}	return 0;}static int ibmpex_find_sensors(struct ibmpex_bmc_data *data){	int i, j, err;	int sensor_type;	int sensor_counter;	int num_power = 0;	int num_temp = 0;	err = ibmpex_query_sensor_count(data);	if (err <= 0)		return -ENOENT;	data->num_sensors = err;	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),				GFP_KERNEL);	if (!data->sensors)		return -ENOMEM;	for (i = 0; i < data->num_sensors; i++) {		err = ibmpex_query_sensor_name(data, i);		if (err)			continue;		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {			sensor_type = POWER_SENSOR;			num_power++;			sensor_counter = num_power;			data->sensors[i].multiplier =				power_sensor_multiplier(data->rx_msg_data,						     data->rx_msg_len);		} else if (is_temp_sensor(data->rx_msg_data,					  data->rx_msg_len)) {			sensor_type = TEMP_SENSOR;			num_temp++;			sensor_counter = num_temp;			data->sensors[i].multiplier = 1;		} else			continue;		data->sensors[i].in_use = 1;		/* Create attributes */		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {			err = create_sensor(data, sensor_type, sensor_counter,					    i, j);			if (err)				goto exit_remove;		}	}	err = device_create_file(data->bmc_device,			&sensor_dev_attr_reset_high_low.dev_attr);	if (err)		goto exit_remove;	err = device_create_file(data->bmc_device,			&sensor_dev_attr_name.dev_attr);	if (err)		goto exit_remove;	return 0;exit_remove:	device_remove_file(data->bmc_device,			   &sensor_dev_attr_reset_high_low.dev_attr);	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);	for (i = 0; i < data->num_sensors; i++)		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {			if (!data->sensors[i].attr[j].dev_attr.attr.name)				continue;			device_remove_file(data->bmc_device,				&data->sensors[i].attr[j].dev_attr);			kfree(data->sensors[i].attr[j].dev_attr.attr.name);		}	kfree(data->sensors);	return err;}static void ibmpex_register_bmc(int iface, struct device *dev){	struct ibmpex_bmc_data *data;	int err;	data = kzalloc(sizeof(*data), GFP_KERNEL);	if (!data) {		dev_err(dev, "Insufficient memory for BMC interface.\n");		return;	}	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;	data->address.channel = IPMI_BMC_CHANNEL;	data->address.data[0] = 0;	data->interface = iface;	data->bmc_device = dev;	/* Create IPMI messaging interface user */	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,			       data, &data->user);	if (err < 0) {		dev_err(dev, "Unable to register user with IPMI "			"interface %d\n", data->interface);		goto out;	}	mutex_init(&data->lock);	/* Initialize message */	data->tx_msgid = 0;	init_completion(&data->read_complete);	data->tx_message.netfn = PEX_NET_FUNCTION;	data->tx_message.cmd = PEX_COMMAND;	data->tx_message.data = data->tx_msg_data;	/* Does this BMC support PowerExecutive? */	err = ibmpex_ver_check(data);	if (err)		goto out_user;	/* Register the BMC as a HWMON class device */	data->hwmon_dev = hwmon_device_register(data->bmc_device);	if (IS_ERR(data->hwmon_dev)) {		dev_err(data->bmc_device, "Unable to register hwmon "			"device for IPMI interface %d\n",			data->interface);		goto out_user;	}	/* finally add the new bmc data to the bmc data list */	dev_set_drvdata(dev, data);	list_add_tail(&data->list, &driver_data.bmc_data);	/* Now go find all the sensors */	err = ibmpex_find_sensors(data);	if (err) {		dev_err(data->bmc_device, "Error %d finding sensors\n", err);		goto out_register;	}	return;out_register:	hwmon_device_unregister(data->hwmon_dev);out_user:	ipmi_destroy_user(data->user);out:	kfree(data);}static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data){	int i, j;	device_remove_file(data->bmc_device,			   &sensor_dev_attr_reset_high_low.dev_attr);	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);	for (i = 0; i < data->num_sensors; i++)		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {			if (!data->sensors[i].attr[j].dev_attr.attr.name)				continue;			device_remove_file(data->bmc_device,				&data->sensors[i].attr[j].dev_attr);			kfree(data->sensors[i].attr[j].dev_attr.attr.name);		}	list_del(&data->list);	dev_set_drvdata(data->bmc_device, NULL);	hwmon_device_unregister(data->hwmon_dev);	ipmi_destroy_user(data->user);	kfree(data->sensors);	kfree(data);}static void ibmpex_bmc_gone(int iface){	struct ibmpex_bmc_data *data = get_bmc_data(iface);	if (!data)		return;	ibmpex_bmc_delete(data);}static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data){	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;	if (msg->msgid != data->tx_msgid) {		dev_err(data->bmc_device, "Mismatch between received msgid "			"(%02x) and transmitted msgid (%02x)!\n",			(int)msg->msgid,			(int)data->tx_msgid);		ipmi_free_recv_msg(msg);		return;	}	data->rx_recv_type = msg->recv_type;	if (msg->msg.data_len > 0)		data->rx_result = msg->msg.data[0];	else		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;	if (msg->msg.data_len > 1) {		data->rx_msg_len = msg->msg.data_len - 1;		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);	} else		data->rx_msg_len = 0;	ipmi_free_recv_msg(msg);	complete(&data->read_complete);}static int __init ibmpex_init(void){	return ipmi_smi_watcher_register(&driver_data.bmc_events);}static void __exit ibmpex_exit(void){	struct ibmpex_bmc_data *p, *next;	ipmi_smi_watcher_unregister(&driver_data.bmc_events);	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)		ibmpex_bmc_delete(p);}MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");MODULE_LICENSE("GPL");module_init(ibmpex_init);module_exit(ibmpex_exit);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -