From patchwork Sat Nov 2 17:46:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Ahmad Khalifa X-Patchwork-Id: 13860217 Received: from doubleyoutf.uk (doubleyoutf.uk [109.228.47.220]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 696DE18BBBA for ; Sat, 2 Nov 2024 18:18:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=109.228.47.220 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730571526; cv=none; b=Ncte6dSc29kspqIXKSE1UQkGv6SWjsRKiytuP+VI4YELf+rJWrbVkMqakQFg6xQKb0gAj+4l5Mo1FNYF30H2EU+7LYGqvSqekxHQjPQv76TRJ+FNmB2AKkN3iX2Hus0RbQLNyPU6nQVYGjWPZGxur6dd+vmW1sG1zY/9lTti9eo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730571526; c=relaxed/simple; bh=rWX0zJSsvDtJXiEHmJwigVLfHQ6uoRDnGgKjlxewLgw=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version:Content-Type; b=dhr3dPWLVLeQijU6i9S+dm3aJoKr158kSfsILUk6xUu6W7rHn3Vhp4sUyMSfQIo/Y4X4KPUByKAELAN3PUvtoQsfaFXvdtQ9qjJFkB/fTwQXDCwETscUjuNxsKAj6vbPKI2imO1eXuUH7OhdJacJHZvdivMDlXq8z0hBMLOSXuQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=khalifa.ws; spf=pass smtp.mailfrom=khalifa.ws; dkim=pass (1024-bit key) header.d=khalifa.ws header.i=@khalifa.ws header.b=fdH8u9AT; arc=none smtp.client-ip=109.228.47.220 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=khalifa.ws Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=khalifa.ws Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=khalifa.ws header.i=@khalifa.ws header.b="fdH8u9AT" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=khalifa.ws; s=rsasel; h=Content-Transfer-Encoding:Content-Type:MIME-Version:Message-Id: Date:Subject:Cc:To:From:Sender:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=Y6ftJjxC3qIRNAGPyeOxTtMbLpkBAGR8BJme047aBYM=; b=fdH8u9ATVKWV9PErYQc/+6RCwJ IM+cp5iSznovJ0Vh/C/KBby6NPrJngMn+eI2Q0KrV38zJzFS4/hqjpmzYWF/YtIAsholTrjP+wikL hnoEfVU24OYmKEDSaRJx6NBoKljJujrTzfc9gygw9N1CVnGb164T6brOYbQag85ntNos=; Received: from [2a00:23cc:d220:b33::b56] (helo=orangina.lan) by doubleyoutf.uk with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1t7IDV-00Bo26-HG; Sat, 02 Nov 2024 17:47:17 +0000 From: Ahmad Khalifa To: Guenter Roeck , linux-hwmon@vger.kernel.org Cc: Ahmad Khalifa Subject: [RFC PATCH] hwmon: Add trivial userspace-configured monitor Date: Sat, 2 Nov 2024 17:46:39 +0000 Message-Id: <20241102174639.102724-1-ahmad@khalifa.ws> X-Mailer: git-send-email 2.30.2 Precedence: bulk X-Mailing-List: linux-hwmon@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add a userspace-configured driver that can receive IOCTRL commands to monitor devices over IO or ACPI access. - registers a miscdevice at /dev/trim-control - awaits attach/detach ioctrl commands with device details - reads sensor values from: 1. IO select registers, 2. IO direct registers or 3. ACPI method calls (single arg) - applies basic conversions: multiply, divide, dividend divisor Useful when there is prior knowledge of the device or when experimenting with newer devices using old device info. Another use is for debugging other drivers when raw reg values need to be compared with what the full driver prints out. Drawbacks: Not aware of any device. It's readonly. Readonly does not make it safe as it still writes for address select and bank select. Needs an ioctrl and cannot be modloaded through config or modalias trimctrl.c (not part of the driver) has details on invocation and sample invocations for it8868, gigabyte acpi and nct6799 devices. To test it, if you know your device registers: $ make $ insmod trivialmon.ko $ ./trimctrl --help In a prior version, this was written with modparams for a single device, which were easier to load, but it was single device. Conversion to ioctrl inspired by loopdevice and similar drivers. For the rfc, it's packaged as a standalone module and it has a lot of pr_info() for debugging and there is no home for trimctrl. Signed-off-by: Ahmad Khalifa --- Makefile | 68 +++++ trimctrl.c | 523 +++++++++++++++++++++++++++++++ trivialmon.c | 844 +++++++++++++++++++++++++++++++++++++++++++++++++++ trivialmon.h | 78 +++++ 4 files changed, 1513 insertions(+) create mode 100644 Makefile create mode 100644 trimctrl.c create mode 100644 trivialmon.c create mode 100644 trivialmon.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..954c794 --- /dev/null +++ b/Makefile @@ -0,0 +1,68 @@ +# Driver details +DRIVER := trivialmon +MOD_SUBDIR := misc + +ifndef TARGET +TARGET := $(shell uname -r) +endif + +KERNEL_MODULES := /lib/modules/$(TARGET) +KERNEL_BUILD := $(KERNEL_MODULES)/build +SYSTEM_MAP := $(KERNEL_BUILD)/System.map +MODDESTDIR := $(KERNEL_MODULES)/$(MOD_SUBDIR) + +ifneq (, $(shell grep "CONFIG_MODULE_SIG=y" $(KERNEL_BUILD)/.config)) + MOD_SIGN := y + MOD_SIGN_HASH := $(shell grep "CONFIG_MODULE_SIG_HASH=" $(KERNEL_BUILD)/.config | sed 's/^[a-zA-Z_ =]*//g' | sed 's/" *//g') + MOD_SIGN_KEY := $(shell grep "CONFIG_MODULE_SIG_KEY=" $(KERNEL_BUILD)/.config | sed 's/^[a-zA-Z_ =]*//g' | sed 's/" *//g') + MOD_SIGN_X509 := $(KERNEL_BUILD)/certs/signing_key.x509 +endif + +obj-m := $(patsubst %,%.o,$(DRIVER)) +obj-ko := $(patsubst %,%.ko,$(DRIVER)) + + +all: modules trimctrl + +modules: + @$(MAKE) -C $(KERNEL_BUILD) M=$(CURDIR) $@ + +trimctrl: trimctrl.o + +clean: + @$(MAKE) -C $(KERNEL_BUILD) M=$(CURDIR) $@ + @rm -fv trimctrl + +install: info modules_install modules_sign modules_depmod + +modules_install: + @mkdir -pv $(MODDESTDIR) + @cp -v $(DRIVER).ko $(MODDESTDIR)/ + +modules_sign: +ifeq ($(MOD_SIGN), y) + @echo "Signing module" $(MODDESTDIR)/$(DRIVER).ko + @$(KERNEL_BUILD)/scripts/sign-file $(MOD_SIGN_HASH) $(MOD_SIGN_KEY) $(MOD_SIGN_X509) $(MODDESTDIR)/$(DRIVER).ko +endif + +modules_depmod: + depmod -a -F $(SYSTEM_MAP) $(TARGET) + +modules_remove: + @rm -fv $(MODDESTDIR)/$(DRIVER).ko + +info: + @echo "Driver:" $(DRIVER) + @echo "Target:" $(TARGET) + @echo "Modules Dir:" $(KERNEL_MODULES) + @echo "Module Dest Dir:" $(MODDESTDIR) + @echo "Build Dir: " $(KERNEL_BUILD) + @echo " ("$(shell readlink -f $(KERNEL_BUILD) )")" + @echo "System Map: " $(SYSTEM_MAP) + @echo " ("$(shell readlink -f $(SYSTEM_MAP) )")" + @echo "Module Sign:" $(MOD_SIGN) + @echo "Module Hash:" $(MOD_SIGN_HASH) + @echo "Module Key: " $(MOD_SIGN_KEY) + @echo "Module X509:" $(MOD_SIGN_X509) + +.PHONY: all install modules modules_install modules_sign clean diff --git a/trimctrl.c b/trimctrl.c new file mode 100644 index 0000000..32bcb8e --- /dev/null +++ b/trimctrl.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Call Trivial Monitor ioctl attach/detach from args or sample devices + * + * $ ./trimctrl -h + * + * # first load the trivialmon module + * $ insmod trivialmon + * + * # Attach a sample device + * $ ./trimctrl -t1 + * + * # Attach an ioselect device with a single sensor + * # device lives at io address 0xa45 and expect chip id 0x90 at register 0x58 + * $ ./trimctrl -m 0x1 -b 0xa45 -c 0x90 -rc 0x58 -st t -sr 0x2b -sl "TCPU" + * $ sensors + * trivialmon.1.auto-isa-0001 + * Adapter: ISA adapter + * TCPU: +30.0°C + * + * # Or the unsafe version which skips IO resource conflicts: + * $ ./trimctrl -m 0x1 -u -b 0xa45 -c 0x90 -rc 0x58 -st t -sr 0x2b -sl "TCPU" + * + * # Attach an ioselect device with several sensors and conversions + * $ ./trimctrl -m 0x1 -b 0xa45 -c 0x90 -rc 0x58 \ + * -st t -sr 0x2b -sl "TCPU" \ + * -st t -sr 0x29 -sl "TSYS" \ + * -st f -sr 0x0d -srh 0x18 -sl "FCPU" -scm 2 -scv 1350000 \ + * -st f -sr 0x0e -srh 0x19 -scm 2 -scv 1350000 \ + * -st i -sr 0x20 -sl "VCore" -scm 12 \ + * -st i -sr 0x21 -sl "3V" -scm 24 + * + * # Attach the same device above through an ACPI device and methods + * $ ./trimctrl -m 0x3 -ah PNP0C14 -au GSADEV0 \ + * -st t -sam SIO0 -sa0 0 -sl TSYS \ + * -st t -sam SIO0 -sa0 2 -sl TCPU \ + * -st f -sam SIO4 -sa0 0 -sl "FCPU" -scm 2 -scv 1350000 \ + * -st f -sam SIO4 -sa0 2 -sl "FPCH" -scm 2 -scv 1350000 + * $ sensors + * trivialmon.2.auto-isa-0002 + * Adapter: ISA adapter + * FCPU: 1398 RPM + * FPCH: 0 RPM + * TSYS: +33.0°C + * TCPU: +30.0°C + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include "trivialmon.h" + +#define APP_VERSION "0.1" + +#define TRIM_DEV_PATH "/dev/"TRIM_CTRL_DEV_NAME + +struct trim_sensor it8868sens[] = { + { .type = trim_type_temp, .label = "TSYS", .reg = 0x29, .conv_mult = 1000, }, + { .type = trim_type_temp, .label = "TCPU", .reg = 0x2b, }, + { .type = trim_type_temp, .label = "TPCH", .reg = 0x2e, }, + { .type = trim_type_fan, .label = "FCPU", .reg = 0x0d, .reg_hi = 0x18, + .conv_mult = 2, .conv_dvdnd = 1350000, }, + { .type = trim_type_fan, .reg = 0x0e, .reg_hi = 0x19, .conv_mult = 2, + .conv_dvdnd = 1350000, }, + { .type = trim_type_fan, .reg = 0x0f, .reg_hi = 0x1a, .conv_mult = 2, + .conv_dvdnd = 1350000, }, + { .type = trim_type_in, .label = "VCore", .reg = 0x20, .conv_mult = 12, }, + { .type = trim_type_in, .label = "3V", .reg = 0x21, .conv_mult = 12 * 2, }, + { .type = trim_type_in, .label = "12V", .reg = 0x22, .conv_mult = 12 * 6, }, +}; + +struct trim_dev_args it8868dev = { + .method = method_ioselect, + .flags = 0, + .chip_id = 0x90, + .reg_chip_id = 0x58, + .base_addr = 0xa45, + .num_sensors = sizeof(it8868sens) / sizeof(struct trim_sensor), + .sensors = it8868sens, +}; + +struct trim_sensor gigabytesens[] = { + { .type = trim_type_temp, .label = "TSYS", .acpi_method = "SIO0", .acpi_arg0 = 0, }, + { .type = trim_type_temp, .label = "TCPU", .acpi_method = "SIO0", .acpi_arg0 = 2, }, + { .type = trim_type_temp, .label = "TPCH", .acpi_method = "SIO0", .acpi_arg0 = 5, }, + { .type = trim_type_fan, .label = "FCPU", .acpi_method = "SIO4", .acpi_arg0 = 0, + .conv_mult = 2, .conv_dvdnd = 1350000, }, + { .type = trim_type_fan, .label = "FPCH", .acpi_method = "SIO4", .acpi_arg0 = 2, + .conv_mult = 2, .conv_dvdnd = 1350000, }, +}; + +struct trim_dev_args gigabytedev = { + .method = method_acpi, + .flags = 0, + .acpi_hid = "PNP0C14", + .acpi_uid = "GSADEV0", + .num_sensors = sizeof(gigabytesens) / sizeof(struct trim_sensor), + .sensors = gigabytesens, +}; + +struct trim_sensor nct6799sens[] = { + { .type = trim_type_temp, .label = "TSYS", .reg = 0x27, }, + { .type = trim_type_temp, .label = "TCPU", .reg = 0x150, }, + { .type = trim_type_fan, .label = "FCPU", .reg = 0x4c3, .reg_hi = 0x4c2, }, + { .type = trim_type_fan, .reg = 0x4c1, .reg_hi = 0x4c0, }, + { .type = trim_type_in, .label = "VCore", .reg = 0x480, .conv_mult = 8, /* 8mV steps */ }, + { .type = trim_type_in, .label = "3VCC", .reg = 0x483, .conv_mult = 16, /* 16mV steps */ }, + { .type = trim_type_in, .label = "VIN0 12V", .reg = 0x484, .conv_mult = 8, }, +}; + +struct trim_dev_args nct6799dev = { + .method = method_ioselect, + .flags = 0, + .chip_id = 0xa3, + .reg_chip_id = 0x4f, + .base_addr = 0x295, + .reg_bank_sel = 0x4e, + .num_sensors = sizeof(nct6799sens) / sizeof(struct trim_sensor), + .sensors = nct6799sens, +}; + +struct trim_sensor nct6799rosens[] = { + { .type = trim_type_temp, .label = "TSYS", .reg = 0xa10, }, + { .type = trim_type_temp, .label = "TCPU", .reg = 0xa11, }, + { .type = trim_type_temp, .reg = 0xa12, }, + { .type = trim_type_in, .label = "VCore", .reg = 0xa00, .conv_mult = 8, }, + { .type = trim_type_in, .label = "3VCC", .reg = 0xa03, .conv_mult = 16, }, + { .type = trim_type_in, .label = "VIN0 12V", .reg = 0xa01, .conv_mult = 8, }, +}; + +struct trim_dev_args nct6799rodev = { + .method = method_iodirect, + .num_sensors = sizeof(nct6799rosens) / sizeof(struct trim_sensor), + .sensors = nct6799rosens, +}; + +void print_dev_args(struct trim_dev_args *devargs) +{ + printf("Device args:\n"); + printf(" method: %i, flags: 0x%x\n", devargs->method, devargs->flags); + + if (devargs->method == method_ioselect) { + printf(" base_addr: 0x%x, chip_id: 0x%x\n", + devargs->base_addr, devargs->chip_id); + printf(" reg_chip_id: 0x%x, reg_bank_sel: 0x%x\n", + devargs->reg_chip_id, devargs->reg_bank_sel); + } + + if (devargs->method == method_acpi) + printf(" acpi_hid: '%s', acpi_uid: '%s'\n", + devargs->acpi_hid, devargs->acpi_uid); + + printf(" num sensors: %i\n", devargs->num_sensors); + for (int i = 0; i < devargs->num_sensors; i++) { + char st = '?'; + + if (devargs->sensors[i].type == trim_type_temp) + st = 't'; + else if (devargs->sensors[i].type == trim_type_fan) + st = 'f'; + else if (devargs->sensors[i].type == trim_type_in) + st = 'i'; + + if (strlen(devargs->sensors[i].label)) + printf(" -- sensor type: '%c', label: '%s', ", + st, devargs->sensors[i].label); + else + printf(" -- sensor type: '%c', ", st); + + if (devargs->method == method_acpi) + printf("acpi_method: %s, acpi_arg0: 0x%x\n", + devargs->sensors[i].acpi_method, devargs->sensors[i].acpi_arg0); + else + printf("reg: 0x%02x, reg_hi: 0x%02x\n", + devargs->sensors[i].reg, devargs->sensors[i].reg_hi); + + /* try to print conversion nicely (DVDND / (R * MULT / DIV) */ + if (devargs->sensors[i].conv_mult || devargs->sensors[i].conv_div || + devargs->sensors[i].conv_dvdnd) { + printf(" Reading conversion: "); + if (devargs->sensors[i].conv_dvdnd) + printf("%i / ( R ", devargs->sensors[i].conv_dvdnd); + else + printf("R "); + + if (devargs->sensors[i].conv_mult) + printf("* %i ", devargs->sensors[i].conv_mult); + if (devargs->sensors[i].conv_div) + printf("/ %i ", devargs->sensors[i].conv_div); + + if (devargs->sensors[i].conv_dvdnd) + printf(")"); + printf("\n"); + } + } + printf("\n"); +} + +int call_list(void) +{ + int ret; + int fd = open(TRIM_DEV_PATH, O_RDWR); + + if (fd < 0) { + error(1, errno, "Cannot open %s for read/write", TRIM_DEV_PATH); + return errno; + } + + printf("List.\n"); + + ret = ioctl(fd, TRIM_IOCTL_LIST, NULL); + if (!ret) { + printf("> OK\n"); + } else { + printf("> Err: %i - %s\n", errno, strerror(errno)); + ret = errno; + } + + return ret; +} + + +int call_attach(struct trim_dev_args *dev, const char *label) +{ + int ret; + int fd = open(TRIM_DEV_PATH, O_RDWR); + + if (fd < 0) { + error(1, errno, "Cannot open %s for read/write", TRIM_DEV_PATH); + return errno; + } + + printf("Attach %s (%s)\n", label, (dev->flags & TRIM_FLAG_UNSAFE_RUN) ? "unsafe" : "safe"); + print_dev_args(dev); + + ret = ioctl(fd, TRIM_IOCTL_ATTACH, dev); + if (!ret) { + printf("> OK. idnum: %i\n", dev->idnum); + } else { + printf("> Err: %i - %s\n", errno, strerror(errno)); + ret = errno; + } + + return ret; +} + +int call_detach(int idnum) +{ + int ret; + int fd = open(TRIM_DEV_PATH, O_WRONLY); + + if (fd < 0) { + error(1, errno, "Cannot open %s for write", TRIM_DEV_PATH); + return errno; + } + + printf("Detaching device %i\n", idnum); + + ret = ioctl(fd, TRIM_IOCTL_DETACH, idnum); + if (!ret) { + printf("> OK\n"); + } else { + printf("> Err: %i - %s\n", errno, strerror(errno)); + ret = errno; + } + + return ret; +} + +long arg_val_as_num(int argc, int *currarg, const char **argval) +{ + const char *val; + char *strend; + long retval; + int nextarg = (*currarg) + 1; + + if (nextarg >= argc) { + printf("Missing argument value for: %s\n", argval[*currarg]); + exit(1); + } + + val = argval[nextarg]; + strend = (char *)val; + errno = 0; + retval = strtol(val, &strend, 0); + if (errno || val == strend) { + printf("Invalid argument number value for: %s\n", argval[*currarg]); + exit(1); + } + (*currarg)++; + + return retval; +} + +void arg_val_strncpy(char *dest, int len, int argc, int *currarg, const char **argval) +{ + int nextarg = (*currarg) + 1; + + if (nextarg >= argc) { + printf("Missing argument value for: %s\n", argval[*currarg]); + exit(1); + } + if (strlen(argval[nextarg]) > len) { + printf("Invalid argument value for: %s\n", argval[*currarg]); + exit(1); + } + strncpy(dest, argval[nextarg], len); + (*currarg)++; +} + +#define CHECK_SENS_ARG(sensor_index) do { if (sensor_index < 0) { \ + printf("Missing sensor type first\n"); exit(1); } \ + } while (0) + +#define CHECK_DEVMETH_ACPI(devargs, arg) do { if (devargs.method != method_acpi) { \ + printf("Bad arg for method: %s\n", arg); exit(1); } \ + } while (0) + +#define CHECK_DEVMETH_IO(devargs, arg) do { if (devargs.method == method_acpi) { \ + printf("Bad arg for method: %s\n", arg); exit(1); } \ + } while (0) + +int process_method_args(int argc, const char **argv) +{ + int i = 1, tmp, sensi = -1, maxsens; + const char *arg; + char sensortype[16]; + struct trim_dev_args devargs; + struct trim_sensor devsensors[TRIM_MAX_CHANNELS * TRIM_MAX_CH_SENSORS]; + + memset(&devargs, 0, sizeof(devargs)); + memset(devsensors, 0, sizeof(devsensors)); + maxsens = sizeof(devsensors) / sizeof(struct trim_sensor); + + while (i < argc) { + arg = argv[i]; + if (!strcmp(arg, "-m")) { + tmp = arg_val_as_num(argc, &i, argv); + if (tmp < method_ioselect || tmp > method_acpi) { + printf("Bad method: %i\n", tmp); + exit(1); + } + devargs.method = tmp; + } else if (!strcmp(arg, "-u")) { + devargs.flags |= TRIM_FLAG_UNSAFE_RUN; + } else if (!strcmp(arg, "-b")) { + CHECK_DEVMETH_IO(devargs, arg); + tmp = arg_val_as_num(argc, &i, argv); + devargs.base_addr = tmp; + } else if (!strcmp(arg, "-c")) { + CHECK_DEVMETH_IO(devargs, arg); + tmp = arg_val_as_num(argc, &i, argv); + devargs.chip_id = tmp; + } else if (!strcmp(arg, "-rc")) { + CHECK_DEVMETH_IO(devargs, arg); + tmp = arg_val_as_num(argc, &i, argv); + devargs.reg_chip_id = tmp; + } else if (!strcmp(arg, "-rs")) { + CHECK_DEVMETH_IO(devargs, arg); + tmp = arg_val_as_num(argc, &i, argv); + devargs.reg_bank_sel = tmp; + } else if (!strcmp(arg, "-ah")) { + CHECK_DEVMETH_ACPI(devargs, arg); + arg_val_strncpy(devargs.acpi_hid, TRIM_MAX_ACPI_HID_LEN, argc, &i, argv); + } else if (!strcmp(arg, "-au")) { + CHECK_DEVMETH_ACPI(devargs, arg); + arg_val_strncpy(devargs.acpi_uid, TRIM_MAX_ACPI_UID_LEN, argc, &i, argv); + } else if (!strcmp(arg, "-st")) { + arg_val_strncpy(sensortype, sizeof(sensortype), argc, &i, argv); + sensi++; + if (sensi >= maxsens) { + printf("Too many sensors\n"); + exit(1); + } + if (!strcmp(sensortype, "t")) { + devsensors[sensi].type = trim_type_temp; + } else if (!strcmp(sensortype, "f")) { + devsensors[sensi].type = trim_type_fan; + } else if (!strcmp(sensortype, "i")) { + devsensors[sensi].type = trim_type_in; + } else { + printf("Bad sensor type: %s\n", sensortype); + exit(1); + } + } else if (!strcmp(arg, "-sr")) { + CHECK_SENS_ARG(sensi); + CHECK_DEVMETH_IO(devargs, arg); + tmp = arg_val_as_num(argc, &i, argv); + devsensors[sensi].reg = tmp; + } else if (!strcmp(arg, "-srh")) { + CHECK_SENS_ARG(sensi); + CHECK_DEVMETH_IO(devargs, arg); + tmp = arg_val_as_num(argc, &i, argv); + devsensors[sensi].reg_hi = tmp; + } else if (!strcmp(arg, "-sam")) { + CHECK_SENS_ARG(sensi); + CHECK_DEVMETH_ACPI(devargs, arg); + arg_val_strncpy(devsensors[sensi].acpi_method, TRIM_MAX_ACPI_METH_LEN, + argc, &i, argv); + } else if (!strcmp(arg, "-sa0")) { + CHECK_SENS_ARG(sensi); + CHECK_DEVMETH_ACPI(devargs, arg); + tmp = arg_val_as_num(argc, &i, argv); + devsensors[sensi].acpi_arg0 = tmp; + } else if (!strcmp(arg, "-scm")) { + CHECK_SENS_ARG(sensi); + tmp = arg_val_as_num(argc, &i, argv); + devsensors[sensi].conv_mult = tmp; + } else if (!strcmp(arg, "-scd")) { + CHECK_SENS_ARG(sensi); + tmp = arg_val_as_num(argc, &i, argv); + devsensors[sensi].conv_div = tmp; + } else if (!strcmp(arg, "-scv")) { + CHECK_SENS_ARG(sensi); + tmp = arg_val_as_num(argc, &i, argv); + devsensors[sensi].conv_dvdnd = tmp; + } else if (!strcmp(arg, "-sl")) { + CHECK_SENS_ARG(sensi); + arg_val_strncpy(devsensors[sensi].label, TRIM_MAX_LABEL_LEN, + argc, &i, argv); + } else { + printf("Invalid argument: %s\n\n", arg); + exit(1); + } + i++; + } + + devargs.num_sensors = sensi - -1; + devargs.sensors = devsensors; + + return call_attach(&devargs, "from args"); +} + +void print_usage(void) +{ + printf(__FILE_NAME__" version: "APP_VERSION"\n" +"\n" +"Usage: trimctrl