blob: 2d8a5e5b19aafaa4e2fed2f3a8589c585032a6f7 [file] [log] [blame]
/*
* Copyright (c) 2015 Hauke Mehrtens <hauke@hauke-m.de>
* Copyright (c) 2015 - 2016 Intel Deutschland GmbH
*
* Backport functionality introduced in Linux 4.3.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/version.h>
#include <linux/seq_file.h>
#include <linux/export.h>
#include <linux/printk.h>
#include <linux/thermal.h>
#include <linux/slab.h>
#if LINUX_VERSION_IS_GEQ(3,8,0)
struct backport_thermal_ops_wrapper {
old_thermal_zone_device_ops_t ops;
struct thermal_zone_device_ops *driver_ops;
};
static int backport_thermal_get_temp(struct thermal_zone_device *dev,
unsigned long *temp)
{
struct backport_thermal_ops_wrapper *wrapper =
container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
int _temp, ret;
ret = wrapper->driver_ops->get_temp(dev, &_temp);
if (!ret)
*temp = (unsigned long)_temp;
return ret;
}
static int backport_thermal_get_trip_temp(struct thermal_zone_device *dev,
int i, unsigned long *temp)
{
struct backport_thermal_ops_wrapper *wrapper =
container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
int _temp, ret;
ret = wrapper->driver_ops->get_trip_temp(dev, i, &_temp);
if (!ret)
*temp = (unsigned long)_temp;
return ret;
}
static int backport_thermal_set_trip_temp(struct thermal_zone_device *dev,
int i, unsigned long temp)
{
struct backport_thermal_ops_wrapper *wrapper =
container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
return wrapper->driver_ops->set_trip_temp(dev, i, (int)temp);
}
static int backport_thermal_get_trip_hyst(struct thermal_zone_device *dev,
int i, unsigned long *temp)
{
struct backport_thermal_ops_wrapper *wrapper =
container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
int _temp, ret;
ret = wrapper->driver_ops->get_trip_hyst(dev, i, &_temp);
if (!ret)
*temp = (unsigned long)_temp;
return ret;
}
static int backport_thermal_set_trip_hyst(struct thermal_zone_device *dev,
int i, unsigned long temp)
{
struct backport_thermal_ops_wrapper *wrapper =
container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
return wrapper->driver_ops->set_trip_hyst(dev, i, (int)temp);
}
static int backport_thermal_get_crit_temp(struct thermal_zone_device *dev,
unsigned long *temp)
{
struct backport_thermal_ops_wrapper *wrapper =
container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
int _temp, ret;
ret = wrapper->driver_ops->get_crit_temp(dev, &_temp);
if (!ret)
*temp = (unsigned long)_temp;
return ret;
}
#if LINUX_VERSION_IS_GEQ(3, 19, 0)
static int backport_thermal_set_emul_temp(struct thermal_zone_device *dev,
unsigned long temp)
{
struct backport_thermal_ops_wrapper *wrapper =
container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
return wrapper->driver_ops->set_emul_temp(dev, (int)temp);
}
#endif /* LINUX_VERSION_IS_GEQ(3, 19, 0) */
struct thermal_zone_device *backport_thermal_zone_device_register(
const char *type, int trips, int mask, void *devdata,
struct thermal_zone_device_ops *ops,
const struct thermal_zone_params *tzp,
int passive_delay, int polling_delay)
{
struct backport_thermal_ops_wrapper *wrapper = kzalloc(sizeof(*wrapper), GFP_KERNEL);
struct thermal_zone_device *ret;
if (!wrapper)
return NULL;
wrapper->driver_ops = ops;
#define copy(_op) \
wrapper->ops._op = ops->_op
copy(bind);
copy(unbind);
copy(get_mode);
copy(set_mode);
copy(get_trip_type);
copy(get_trend);
copy(notify);
/* Assign the backport ops to the old struct to get the
* correct types. But only assign if the registrant defined
* the ops.
*/
#define assign_ops(_op) \
if (ops->_op) \
wrapper->ops._op = backport_thermal_##_op
assign_ops(get_temp);
assign_ops(get_trip_temp);
assign_ops(set_trip_temp);
assign_ops(get_trip_hyst);
assign_ops(set_trip_hyst);
assign_ops(get_crit_temp);
#if LINUX_VERSION_IS_GEQ(3, 19, 0)
assign_ops(set_emul_temp);
#endif /* LINUX_VERSION_IS_GEQ(3, 19, 0) */
#undef assign_ops
ret = old_thermal_zone_device_register(type, trips, mask, devdata,
&wrapper->ops, tzp, passive_delay,
polling_delay);
if (!ret)
kfree(wrapper);
return ret;
}
EXPORT_SYMBOL_GPL(backport_thermal_zone_device_register);
void backport_thermal_zone_device_unregister(struct thermal_zone_device *dev)
{
struct backport_thermal_ops_wrapper *wrapper =
container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
old_thermal_zone_device_unregister(dev);
kfree(wrapper);
}
EXPORT_SYMBOL_GPL(backport_thermal_zone_device_unregister);
#endif /* >= 3.8.0 */
static void seq_set_overflow(struct seq_file *m)
{
m->count = m->size;
}
/* A complete analogue of print_hex_dump() */
void seq_hex_dump(struct seq_file *m, const char *prefix_str, int prefix_type,
int rowsize, int groupsize, const void *buf, size_t len,
bool ascii)
{
const u8 *ptr = buf;
int i, linelen, remaining = len;
int ret;
if (rowsize != 16 && rowsize != 32)
rowsize = 16;
for (i = 0; i < len && !seq_has_overflowed(m); i += rowsize) {
linelen = min(remaining, rowsize);
remaining -= rowsize;
switch (prefix_type) {
case DUMP_PREFIX_ADDRESS:
seq_printf(m, "%s%p: ", prefix_str, ptr + i);
break;
case DUMP_PREFIX_OFFSET:
seq_printf(m, "%s%.8x: ", prefix_str, i);
break;
default:
seq_printf(m, "%s", prefix_str);
break;
}
ret = hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
m->buf + m->count, m->size - m->count,
ascii);
if (ret >= m->size - m->count) {
seq_set_overflow(m);
} else {
m->count += ret;
seq_putc(m, '\n');
}
}
}
EXPORT_SYMBOL_GPL(seq_hex_dump);
ssize_t strscpy(char *dest, const char *src, size_t count)
{
long res = 0;
if (count == 0)
return -E2BIG;
while (count) {
char c;
c = src[res];
dest[res] = c;
if (!c)
return res;
res++;
count--;
}
/* Hit buffer length without finding a NUL; force NUL-termination. */
if (res)
dest[res-1] = '\0';
return -E2BIG;
}
EXPORT_SYMBOL_GPL(strscpy);