From 15a6cffc58b4b57904a731e01789f8b67f31942b Mon Sep 17 00:00:00 2001 From: Patrick Grimm Date: Tue, 2 Dec 2025 13:04:11 +0100 Subject: [PATCH] xs2184-tsw202: add package to control PoE for TSW202 device this package is used to configure the Power-over-Ethernet controller xs2184 found on TSW202 switches. The PoE doesn't work without this package. Investigation and Dokuments 'https://git.f2a.space/patrick/openwrt-24.10-tsw202/ -/wikis/POE-XS2184' Signed-off-by: Patrick Grimm --- utils/xs2184-tsw202/Makefile | 53 ++ utils/xs2184-tsw202/README.md | 47 + utils/xs2184-tsw202/files/Makefile | 13 + utils/xs2184-tsw202/files/libxs2184/i2c-dev.h | 330 +++++++ .../xs2184-tsw202/files/libxs2184/i2cbusses.c | 421 +++++++++ .../xs2184-tsw202/files/libxs2184/i2cbusses.h | 44 + utils/xs2184-tsw202/files/libxs2184/xs2184.c | 821 ++++++++++++++++++ utils/xs2184-tsw202/files/libxs2184/xs2184.h | 87 ++ utils/xs2184-tsw202/files/xs2184.config | 39 + utils/xs2184-tsw202/files/xs2184.init | 127 +++ 10 files changed, 1982 insertions(+) create mode 100644 utils/xs2184-tsw202/Makefile create mode 100644 utils/xs2184-tsw202/README.md create mode 100644 utils/xs2184-tsw202/files/Makefile create mode 100644 utils/xs2184-tsw202/files/libxs2184/i2c-dev.h create mode 100644 utils/xs2184-tsw202/files/libxs2184/i2cbusses.c create mode 100644 utils/xs2184-tsw202/files/libxs2184/i2cbusses.h create mode 100644 utils/xs2184-tsw202/files/libxs2184/xs2184.c create mode 100644 utils/xs2184-tsw202/files/libxs2184/xs2184.h create mode 100644 utils/xs2184-tsw202/files/xs2184.config create mode 100755 utils/xs2184-tsw202/files/xs2184.init diff --git a/utils/xs2184-tsw202/Makefile b/utils/xs2184-tsw202/Makefile new file mode 100644 index 0000000000000..9695be17538e3 --- /dev/null +++ b/utils/xs2184-tsw202/Makefile @@ -0,0 +1,53 @@ +include $(TOPDIR)/rules.mk + + +PKG_NAME:=xs2184-tsw202 +PKG_VERSION:=1.0.2 +PKG_RELEASE:=1 + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) +PKG_MAINTAINER:=patrick + +include $(INCLUDE_DIR)/package.mk + +ifeq ($(CONFIG_BIG_ENDIAN),y) +TARGET_CFLAGS+= -DHAVE_BIG_ENDIAN=1 +endif +TARGET_CFLAGS+= -D_GNU_SOURCE + +define Package/xs2184-tsw202 + SECTION:=Applications + CATEGORY:=Network + SECTION:=utils + CATEGORY:=Utilities + TITLE:=XinSheng PoE/PSE i2c driver for Teltonika TSW202 Switch + DEPENDS:=+i2c-tools +libuci +endef + +define Package/xs2184-tsw202/description + ZheJiang XinSheng PoE/PSE SoC devices drivers; +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./files $(PKG_BUILD_DIR)/ +endef + +define Build/Compile + $(MAKE) -C $(PKG_BUILD_DIR)/files \ + $(TARGET_CONFIGURE_OPTS) \ + TARGET=Linux \ + CFLAGS="$(TARGET_CFLAGS)" \ + LDFLAGS="$(TARGET_LDFLAGS)" +endef + +define Package/xs2184-tsw202/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/files/xs2184 $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/xs2184.init $(1)/etc/init.d/xs2184 + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_DATA) ./files/xs2184.config $(1)/etc/config/xs2184 +endef + +$(eval $(call BuildPackage,xs2184-tsw202)) diff --git a/utils/xs2184-tsw202/README.md b/utils/xs2184-tsw202/README.md new file mode 100644 index 0000000000000..a00d7f3e67e83 --- /dev/null +++ b/utils/xs2184-tsw202/README.md @@ -0,0 +1,47 @@ +# XS2184 PSE driver and monitor app + +## 检测原理 +- 正常工作时, PSE 协商使能, 供电给 PD, 并统计15轮 实时功耗; +- 当平均功耗 为0, 判定 PD 出现异常, 关闭 PSE 对应 Port的供电; +- 被关闭的 Port, 在下一轮统计时, 重新开启协商供电使能; +- 实测, MS1800K 在 上电复位异常情况下, 实时功耗 为0 mW, 能被识别出异常, 并断电重启; + +## 测试方法 +1. 正常 PoE - PD 组网; +2. 后台执行: xs2184 -m 1000; +3. xs2184 监听模式, 参数 -m: monitor 模式, 检测间隔 1000ms, 平均功耗计算周期: 默认 15 轮, -s 可修改; +4. 观察 xs2184 日志, 会实时打印功耗和检测状态; +5. 用镊子短路 MS1800K 的 C133 复位电容, 模拟上电无法启动的情况; +6. 重复4), 对比关电和重新上电的行为, 以及MS1800K重新开机和功耗变化; + +## Funktionsprinzip +- Im Normalbetrieb verhandelt die PSE die Freigabe, versorgt das PD mit Strom und zeichnet den Stromverbrauch in Echtzeit über 15 Zyklen auf. +- Wenn der durchschnittliche Stromverbrauch Null erreicht, wird das PD als fehlerhaft eingestuft und die PSE unterbricht die Stromversorgung des entsprechenden Ports. +- Der deaktivierte Port verhandelt während des nächsten Verbrauchszyklus erneut die Freigabe der Stromversorgung. +- Feldtests bestätigen, dass der MS1800K unter abnormalen Power-On-Reset-Bedingungen einen Echtzeit-Stromverbrauch von null mW aufweist, was die Erkennung von Anomalien und den anschließenden Neustart nach dem Ausschalten ermöglicht. + +## Testmethodik +1. Standard-PoE-PD-Netzwerkkonfiguration. +2. Hintergrundausführung: xs2184 -m 1000. +3. xs2184-Überwachungsmodus, Parameter -m: Überwachungsmodus, Erkennungsintervall 1000 ms, durchschnittlicher Stromverbrauchsberechnungszyklus: standardmäßig 15 Runden, änderbar mit -s in ms; +4. Beobachten Sie die xs2184-Protokolle, die den Stromverbrauch und den Erkennungsstatus in Echtzeit anzeigen. +5. Verwenden Sie eine Pinzette, um den C133-Reset-Kondensator des MS1800K kurzzuschließen und so einen Fehler beim Hochfahren nach dem Einschalten zu simulieren. +6. Wiederholen Sie Schritt 4 und vergleichen Sie das Verhalten nach dem Ausschalten und erneuten Einschalten mit dem Neustart des MS1800K und den Änderungen des Stromverbrauchs. + +## UI einfach +``` +uci add luci command +uci set luci.@command[-1].name="poe-status" +uci set luci.@command[-1].command="xs2184 -c" +last=8 +for i in $(seq 1 $last);do +uci add luci command +uci set luci.@command[-1].name="poe-lan$i-on" +uci set luci.@command[-1].command="xs2184 -u $last" +uci add luci command +uci set luci.@command[-1].name="poe-lan$i-off" +uci set luci.@command[-1].command="xs2184 -d $last" +last=$((last-1)) +done +uci commit luci +``` diff --git a/utils/xs2184-tsw202/files/Makefile b/utils/xs2184-tsw202/files/Makefile new file mode 100644 index 0000000000000..84aa0365d0b63 --- /dev/null +++ b/utils/xs2184-tsw202/files/Makefile @@ -0,0 +1,13 @@ +SRCS= \ + libxs2184/xs2184.c \ + libxs2184/i2cbusses.c + +ifeq ($(CC),cc) +CC=${CROSS_COMPILE}gcc +endif + +all: + ${CC} ${CFLAGS} -Wno-format-overflow -Wall -Werror -O1 -s ${SRCS} -o xs2184 ${LDFLAGS} -luci -lpthread -ldl + +clean: + -rm -f *.o xs2184 diff --git a/utils/xs2184-tsw202/files/libxs2184/i2c-dev.h b/utils/xs2184-tsw202/files/libxs2184/i2c-dev.h new file mode 100644 index 0000000000000..7006a7ed3071c --- /dev/null +++ b/utils/xs2184-tsw202/files/libxs2184/i2c-dev.h @@ -0,0 +1,330 @@ +/* + i2c-dev.h - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 Frodo Looijaard + + 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., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#ifndef _LINUX_I2C_DEV_H +#define _LINUX_I2C_DEV_H + +#include +#include +#include + + +/* -- i2c.h -- */ + + +/* + * I2C Message - used for pure i2c transaction, also from /dev interface + */ +struct i2c_msg { + __u16 addr; /* slave address */ + unsigned short flags; +#define I2C_M_TEN 0x10 /* we have a ten bit chip address */ +#define I2C_M_RD 0x01 +#define I2C_M_NOSTART 0x4000 +#define I2C_M_REV_DIR_ADDR 0x2000 +#define I2C_M_IGNORE_NAK 0x1000 +#define I2C_M_NO_RD_ACK 0x0800 + short len; /* msg length */ + char *buf; /* pointer to msg data */ +}; + +/* To determine what functionality is present */ + +#define I2C_FUNC_I2C 0x00000001 +#define I2C_FUNC_10BIT_ADDR 0x00000002 +#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */ +#define I2C_FUNC_SMBUS_PEC 0x00000008 +#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */ +#define I2C_FUNC_SMBUS_QUICK 0x00010000 +#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000 +#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000 +#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000 +#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000 +#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000 +#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000 +#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000 +#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 +#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 +#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */ +#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */ + +#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \ + I2C_FUNC_SMBUS_WRITE_BYTE) +#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \ + I2C_FUNC_SMBUS_WRITE_BYTE_DATA) +#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \ + I2C_FUNC_SMBUS_WRITE_WORD_DATA) +#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) +#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) + +/* Old name, for compatibility */ +#define I2C_FUNC_SMBUS_HWPEC_CALC I2C_FUNC_SMBUS_PEC + +/* + * Data for SMBus Messages + */ +#define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ +#define I2C_SMBUS_I2C_BLOCK_MAX 32 /* Not specified but we use same structure */ +union i2c_smbus_data { + __u8 byte; + __u16 word; + __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */ + /* and one more for PEC */ +}; + +/* smbus_access read or write markers */ +#define I2C_SMBUS_READ 1 +#define I2C_SMBUS_WRITE 0 + +/* SMBus transaction types (size parameter in the above functions) + Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */ +#define I2C_SMBUS_QUICK 0 +#define I2C_SMBUS_BYTE 1 +#define I2C_SMBUS_BYTE_DATA 2 +#define I2C_SMBUS_WORD_DATA 3 +#define I2C_SMBUS_PROC_CALL 4 +#define I2C_SMBUS_BLOCK_DATA 5 +#define I2C_SMBUS_I2C_BLOCK_BROKEN 6 +#define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ +#define I2C_SMBUS_I2C_BLOCK_DATA 8 + + +/* /dev/i2c-X ioctl commands. The ioctl's parameter is always an + * unsigned long, except for: + * - I2C_FUNCS, takes pointer to an unsigned long + * - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data + * - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data + */ +#define I2C_RETRIES 0x0701 /* number of times a device address should + be polled when not acknowledging */ +#define I2C_TIMEOUT 0x0702 /* set timeout in units of 10 ms */ + +/* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses + * are NOT supported! (due to code brokenness) + */ +#define I2C_SLAVE 0x0703 /* Use this slave address */ +#define I2C_SLAVE_FORCE 0x0706 /* Use this slave address, even if it + is already in use by a driver! */ +#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */ + +#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */ + +#define I2C_RDWR 0x0707 /* Combined R/W transfer (one STOP only) */ + +#define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */ +#define I2C_SMBUS 0x0720 /* SMBus transfer */ + + +/* This is the structure as used in the I2C_SMBUS ioctl call */ +struct i2c_smbus_ioctl_data { + __u8 read_write; + __u8 command; + __u32 size; + union i2c_smbus_data *data; +}; + +/* This is the structure as used in the I2C_RDWR ioctl call */ +struct i2c_rdwr_ioctl_data { + struct i2c_msg *msgs; /* pointers to i2c_msgs */ + __u32 nmsgs; /* number of i2c_msgs */ +}; + +#define I2C_RDRW_IOCTL_MAX_MSGS 42 + + +static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_smbus_ioctl_data args; + + args.read_write = read_write; + args.command = command; + args.size = size; + args.data = data; + return ioctl(file,I2C_SMBUS,&args); +} + + +static inline __s32 i2c_smbus_write_quick(int file, __u8 value) +{ + return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL); +} + +static inline __s32 i2c_smbus_read_byte(int file) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +static inline __s32 i2c_smbus_write_byte(int file, __u8 value) +{ + return i2c_smbus_access(file,I2C_SMBUS_WRITE,value, + I2C_SMBUS_BYTE,NULL); +} + +static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command, + I2C_SMBUS_BYTE_DATA,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, + __u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, + I2C_SMBUS_BYTE_DATA, &data); +} + +static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command, + I2C_SMBUS_WORD_DATA,&data)) + return -1; + else + return 0x0FFFF & data.word; +} + +static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, + __u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, + I2C_SMBUS_WORD_DATA, &data); +} + +static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value) +{ + union i2c_smbus_data data; + data.word = value; + if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command, + I2C_SMBUS_PROC_CALL,&data)) + return -1; + else + return 0x0FFFF & data.word; +} + + +/* Returns the number of read bytes */ +static inline __s32 i2c_smbus_read_block_data(int file, __u8 command, + __u8 *values) +{ + union i2c_smbus_data data; + int i; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command, + I2C_SMBUS_BLOCK_DATA,&data)) + return -1; + else { + for (i = 1; i <= data.block[0]; i++) + values[i-1] = data.block[i]; + return data.block[0]; + } +} + +static inline __s32 i2c_smbus_write_block_data(int file, __u8 command, + __u8 length, const __u8 *values) +{ + union i2c_smbus_data data; + int i; + if (length > 32) + length = 32; + for (i = 1; i <= length; i++) + data.block[i] = values[i-1]; + data.block[0] = length; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, + I2C_SMBUS_BLOCK_DATA, &data); +} + +/* Returns the number of read bytes */ +/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you + ask for less than 32 bytes, your code will only work with kernels + 2.6.23 and later. */ +static inline __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, + __u8 length, __u8 *values) +{ + union i2c_smbus_data data; + int i; + + if (length > 32) + length = 32; + data.block[0] = length; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command, + length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN : + I2C_SMBUS_I2C_BLOCK_DATA,&data)) + return -1; + else { + for (i = 1; i <= data.block[0]; i++) + values[i-1] = data.block[i]; + return data.block[0]; + } +} + +static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, + __u8 length, + const __u8 *values) +{ + union i2c_smbus_data data; + int i; + if (length > 32) + length = 32; + for (i = 1; i <= length; i++) + data.block[i] = values[i-1]; + data.block[0] = length; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, + I2C_SMBUS_I2C_BLOCK_BROKEN, &data); +} + +/* Returns the number of read bytes */ +static inline __s32 i2c_smbus_block_process_call(int file, __u8 command, + __u8 length, __u8 *values) +{ + union i2c_smbus_data data; + int i; + if (length > 32) + length = 32; + for (i = 1; i <= length; i++) + data.block[i] = values[i-1]; + data.block[0] = length; + if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command, + I2C_SMBUS_BLOCK_PROC_CALL,&data)) + return -1; + else { + for (i = 1; i <= data.block[0]; i++) + values[i-1] = data.block[i]; + return data.block[0]; + } +} + + +#endif /* _LINUX_I2C_DEV_H */ diff --git a/utils/xs2184-tsw202/files/libxs2184/i2cbusses.c b/utils/xs2184-tsw202/files/libxs2184/i2cbusses.c new file mode 100644 index 0000000000000..c3acc8c17e1b1 --- /dev/null +++ b/utils/xs2184-tsw202/files/libxs2184/i2cbusses.c @@ -0,0 +1,421 @@ +/* + i2cbusses: Print the installed i2c busses for both 2.4 and 2.6 kernels. + Part of user-space programs to access for I2C + devices. + Copyright (c) 1999-2003 Frodo Looijaard and + Mark D. Studebaker + Copyright (C) 2008-2012 Jean Delvare + + 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., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +/* For strdup and snprintf */ +#define _BSD_SOURCE 1 + +#include +#include +#include /* for NAME_MAX */ +#include +#include +#include /* for strcasecmp() */ +#include +#include +#include +#include +#include +#include +#include +#include "i2cbusses.h" +#include "./i2c-dev.h" + +enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown }; + +struct adap_type { + const char *funcs; + const char* algo; +}; + +static struct adap_type adap_types[5] = { + { .funcs = "dummy", + .algo = "Dummy bus", + }, + { .funcs = "isa", + .algo = "ISA bus", + }, + { .funcs = "i2c", + .algo = "I2C adapter", + }, + { .funcs = "smbus", + .algo = "SMBus adapter", + }, + { .funcs = "unknown", + .algo = "N/A", + }, +}; + +static enum adt i2c_get_funcs(int i2cbus) +{ + unsigned long funcs; + int file; + char filename[20]; + enum adt ret; + + file = open_i2c_dev(i2cbus, filename, sizeof(filename), 1); + if (file < 0) + return adt_unknown; + + if (ioctl(file, I2C_FUNCS, &funcs) < 0) + ret = adt_unknown; + else if (funcs & I2C_FUNC_I2C) + ret = adt_i2c; + else if (funcs & (I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + ret = adt_smbus; + else + ret = adt_dummy; + + close(file); + return ret; +} + +/* Remove trailing spaces from a string + Return the new string length including the trailing NUL */ +static int rtrim(char *s) +{ + int i; + + for (i = strlen(s) - 1; i >= 0 && (s[i] == ' ' || s[i] == '\n'); i--) + s[i] = '\0'; + return i + 2; +} + +void free_adapters(struct i2c_adap *adapters) +{ + int i; + + for (i = 0; adapters[i].name; i++) + free(adapters[i].name); + free(adapters); +} + +/* We allocate space for the adapters in bunches. The last item is a + terminator, so here we start with room for 7 adapters, which should + be enough in most cases. If not, we allocate more later as needed. */ +#define BUNCH 8 + +/* n must match the size of adapters at calling time */ +static struct i2c_adap *more_adapters(struct i2c_adap *adapters, int n) +{ + struct i2c_adap *new_adapters; + + new_adapters = realloc(adapters, (n + BUNCH) * sizeof(struct i2c_adap)); + if (!new_adapters) { + free_adapters(adapters); + return NULL; + } + memset(new_adapters + n, 0, BUNCH * sizeof(struct i2c_adap)); + + return new_adapters; +} + +struct i2c_adap *gather_i2c_busses(void) +{ + char s[120]; + struct dirent *de, *dde; + DIR *dir, *ddir; + FILE *f; + char fstype[NAME_MAX], sysfs[NAME_MAX], n[NAME_MAX]; + int foundsysfs = 0; + int count=0; + struct i2c_adap *adapters; + + adapters = calloc(BUNCH, sizeof(struct i2c_adap)); + if (!adapters) + return NULL; + + /* look in /proc/bus/i2c */ + if ((f = fopen("/proc/bus/i2c", "r"))) { + while (fgets(s, 120, f)) { + char *algo, *name, *type, *all; + int len_algo, len_name, len_type; + int i2cbus; + + algo = strrchr(s, '\t'); + *(algo++) = '\0'; + len_algo = rtrim(algo); + + name = strrchr(s, '\t'); + *(name++) = '\0'; + len_name = rtrim(name); + + type = strrchr(s, '\t'); + *(type++) = '\0'; + len_type = rtrim(type); + + sscanf(s, "i2c-%d", &i2cbus); + + if ((count + 1) % BUNCH == 0) { + /* We need more space */ + adapters = more_adapters(adapters, count + 1); + if (!adapters) + return NULL; + } + + all = malloc(len_name + len_type + len_algo); + if (all == NULL) { + free_adapters(adapters); + return NULL; + } + adapters[count].nr = i2cbus; + adapters[count].name = strcpy(all, name); + adapters[count].funcs = strcpy(all + len_name, type); + adapters[count].algo = strcpy(all + len_name + len_type, + algo); + count++; + } + fclose(f); + goto done; + } + + /* look in sysfs */ + /* First figure out where sysfs was mounted */ + if ((f = fopen("/proc/mounts", "r")) == NULL) { + goto done; + } + while (fgets(n, NAME_MAX, f)) { + sscanf(n, "%*[^ ] %[^ ] %[^ ] %*s\n", sysfs, fstype); + if (strcasecmp(fstype, "sysfs") == 0) { + foundsysfs++; + break; + } + } + fclose(f); + if (! foundsysfs) { + goto done; + } + + /* Bus numbers in i2c-adapter don't necessarily match those in + i2c-dev and what we really care about are the i2c-dev numbers. + Unfortunately the names are harder to get in i2c-dev */ + strcat(sysfs, "/class/i2c-dev"); + if(!(dir = opendir(sysfs))) + goto done; + /* go through the busses */ + while ((de = readdir(dir)) != NULL) { + if (!strcmp(de->d_name, ".")) + continue; + if (!strcmp(de->d_name, "..")) + continue; + + /* this should work for kernels 2.6.5 or higher and */ + /* is preferred because is unambiguous */ + sprintf(n, "%s/%s/name", sysfs, de->d_name); + f = fopen(n, "r"); + /* this seems to work for ISA */ + if(f == NULL) { + sprintf(n, "%s/%s/device/name", sysfs, de->d_name); + f = fopen(n, "r"); + } + /* non-ISA is much harder */ + /* and this won't find the correct bus name if a driver + has more than one bus */ + if(f == NULL) { + sprintf(n, "%s/%s/device", sysfs, de->d_name); + if(!(ddir = opendir(n))) + continue; + while ((dde = readdir(ddir)) != NULL) { + if (!strcmp(dde->d_name, ".")) + continue; + if (!strcmp(dde->d_name, "..")) + continue; + if ((!strncmp(dde->d_name, "i2c-", 4))) { + sprintf(n, "%s/%s/device/%s/name", + sysfs, de->d_name, dde->d_name); + if((f = fopen(n, "r"))) + goto found; + } + } + } + +found: + if (f != NULL) { + int i2cbus; + enum adt type; + char *px; + + px = fgets(s, 120, f); + fclose(f); + if (!px) { + fprintf(stderr, "%s: read error\n", n); + continue; + } + if ((px = strchr(s, '\n')) != NULL) + *px = 0; + if (!sscanf(de->d_name, "i2c-%d", &i2cbus)) + continue; + if (!strncmp(s, "ISA ", 4)) { + type = adt_isa; + } else { + /* Attempt to probe for adapter capabilities */ + type = i2c_get_funcs(i2cbus); + } + + if ((count + 1) % BUNCH == 0) { + /* We need more space */ + adapters = more_adapters(adapters, count + 1); + if (!adapters) + return NULL; + } + + adapters[count].nr = i2cbus; + adapters[count].name = strdup(s); + if (adapters[count].name == NULL) { + free_adapters(adapters); + return NULL; + } + adapters[count].funcs = adap_types[type].funcs; + adapters[count].algo = adap_types[type].algo; + count++; + } + } + closedir(dir); + +done: + return adapters; +} + +static int lookup_i2c_bus_by_name(const char *bus_name) +{ + struct i2c_adap *adapters; + int i, i2cbus = -1; + + adapters = gather_i2c_busses(); + if (adapters == NULL) { + fprintf(stderr, "Error: Out of memory!\n"); + return -3; + } + + /* Walk the list of i2c busses, looking for the one with the + right name */ + for (i = 0; adapters[i].name; i++) { + if (strcmp(adapters[i].name, bus_name) == 0) { + if (i2cbus >= 0) { + fprintf(stderr, + "Error: I2C bus name is not unique!\n"); + i2cbus = -4; + goto done; + } + i2cbus = adapters[i].nr; + } + } + + if (i2cbus == -1) + fprintf(stderr, "Error: I2C bus name doesn't match any " + "bus present!\n"); + +done: + free_adapters(adapters); + return i2cbus; +} + +/* + * Parse an I2CBUS command line argument and return the corresponding + * bus number, or a negative value if the bus is invalid. + */ +int lookup_i2c_bus(const char *i2cbus_arg) +{ + unsigned long i2cbus; + char *end; + + i2cbus = strtoul(i2cbus_arg, &end, 0); + if (*end || !*i2cbus_arg) { + /* Not a number, maybe a name? */ + return lookup_i2c_bus_by_name(i2cbus_arg); + } + if (i2cbus > 0xFFFFF) { + fprintf(stderr, "Error: I2C bus out of range!\n"); + return -2; + } + + return i2cbus; +} + +/* + * Parse a CHIP-ADDRESS command line argument and return the corresponding + * chip address, or a negative value if the address is invalid. + */ +int parse_i2c_address(const char *address_arg) +{ + long address; + char *end; + + address = strtol(address_arg, &end, 0); + if (*end || !*address_arg) { + fprintf(stderr, "Error: Chip address is not a number!\n"); + return -1; + } + if (address < 0x03 || address > 0x77) { + fprintf(stderr, "Error: Chip address out of range " + "(0x03-0x77)!\n"); + return -2; + } + + return address; +} + +int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet) +{ + int file; + + snprintf(filename, size, "/dev/i2c/%d", i2cbus); + filename[size - 1] = '\0'; + file = open(filename, O_RDWR); + + if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) { + sprintf(filename, "/dev/i2c-%d", i2cbus); + file = open(filename, O_RDWR); + } + + if (file < 0 && !quiet) { + if (errno == ENOENT) { + fprintf(stderr, "Error: Could not open file " + "`/dev/i2c-%d' or `/dev/i2c/%d': %s\n", + i2cbus, i2cbus, strerror(ENOENT)); + } else { + fprintf(stderr, "Error: Could not open file " + "`%s': %s\n", filename, strerror(errno)); + if (errno == EACCES) + fprintf(stderr, "Run as root?\n"); + } + } + + return file; +} + +int set_slave_addr(int file, int address, int force) +{ + /* With force, let the user read from/write to the registers + even when a driver is also running */ + if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) { + fprintf(stderr, + "Error: Could not set address to 0x%02x: %s\n", + address, strerror(errno)); + return -errno; + } + + return 0; +} diff --git a/utils/xs2184-tsw202/files/libxs2184/i2cbusses.h b/utils/xs2184-tsw202/files/libxs2184/i2cbusses.h new file mode 100644 index 0000000000000..26143a54f9273 --- /dev/null +++ b/utils/xs2184-tsw202/files/libxs2184/i2cbusses.h @@ -0,0 +1,44 @@ +/* + i2cbusses.h - Part of the i2c-tools package + + Copyright (C) 2004-2010 Jean Delvare + + 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., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#ifndef _I2CBUSSES_H +#define _I2CBUSSES_H + +#include + +struct i2c_adap { + int nr; + char *name; + const char *funcs; + const char *algo; +}; + +struct i2c_adap *gather_i2c_busses(void); +void free_adapters(struct i2c_adap *adapters); + +int lookup_i2c_bus(const char *i2cbus_arg); +int parse_i2c_address(const char *address_arg); +int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet); +int set_slave_addr(int file, int address, int force); + +#define MISSING_FUNC_FMT "Error: Adapter does not have %s capability\n" + +#endif diff --git a/utils/xs2184-tsw202/files/libxs2184/xs2184.c b/utils/xs2184-tsw202/files/libxs2184/xs2184.c new file mode 100644 index 0000000000000..771239ec3611b --- /dev/null +++ b/utils/xs2184-tsw202/files/libxs2184/xs2184.c @@ -0,0 +1,821 @@ +/******************************************************************************* + * File name : xs2184.c + * Date : 2021.12.29 Create + * Versions : v1.0 + * Author : ChenMing + * explain : For read the state of each port, + * and realize the switch control of a single port + *******************************************************************************/ +#include "xs2184.h" + +#define MAX_CHIPS 2 +#define MAX_PORTS 8 +#define MAX_AVERAGE 15 +#define MAX_AVERAGE_UB 300 +#define MAX_AVERAGE_LB MAX_AVERAGE + +#define PERME (S_IRWXU | S_IRGRP | S_IROTH) +#define RECORD_FILE "/tmp/xs2184_record" +#define RECORD_BUF_FILE "/tmp/xs2184_tmp" +#define RECORD_DAY_FILE "/tmp/xs2184_record_day" +#define CONFIG_FN "xs2184" + +#define foreach_port(i) for(i=1; i 4) || (data != XS2184_ID_VAL)) + return -1; + else + return 0; +} + +float port_current(char port_num) +{ + float curr; + uint32_t cur_msb = 0, cur_lsb = 0; + + port_reg_read(port_num, CURT_LSB(port_num), (u8*)&cur_lsb); + port_reg_read(port_num, CURT_MSB(port_num), (u8*)&cur_msb); + + curr = ((float)(cur_msb << 8 | cur_lsb) / 1000.0) * ((float)CURRENT_PARA / 1000.0); //mA + + // fprintf(stdout, "port %u, current %u-%u, %.3f\n", port_num, cur_msb, cur_lsb, curr); + return curr; +} + +typedef int (* ps_callback_t)(u8 en, u8 port_num, float volt, float curt); + +float port_voltage(char port_num) +{ + float volt; + uint32_t vol_msb = 0, vol_lsb = 0; + + port_reg_read(port_num, VOLT_LSB(port_num), (u8*)&vol_lsb); + port_reg_read(port_num, VOLT_MSB(port_num), (u8*)&vol_msb); + + volt = ((float)(vol_msb << 8 | vol_lsb) / 1000.0) * ((float)VOLTAGE_PARA / 1000.0); //uV - mV - V + + // fprintf(stdout, "port %u, volt %u-%u, %.3f\n", port_num, vol_lsb, vol_msb, volt); + return volt; +} + +static int save_date_file(uint32_t time, uint32_t line_bound, uint32_t time_bound, data_file *data, char *fn) { + uint32_t i; + u8 port_num; + FILE *fp; + + if(!(fp = fopen(RECORD_BUF_FILE, "w"))) { + fprintf(stderr, "error in writing %s\n", RECORD_BUF_FILE); + return -1; + } + + for(i=0; i= update_file1_time) { + int ret; + uint32_t times = 0; + float total_pwr[MAX_PORTS+1] = {0.0}; + data_file data_file1[max_lines_file1]; + data_file data_file2[max_lines_file2]; + + memset(&data_file1, 0, sizeof(data_file1)); + memset(&data_file2, 0, sizeof(data_file2)); + + foreach_port(port_num) { + total_pwr[port_num] = g_pwatts[port_num].ave_watts_day_r; + g_pwatts[port_num].ave_watts_day_r = 0.0; + } + + ret = read_data_file(max_lines_file1, data_file1, (char *)RECORD_FILE); + if(ret < 0) { + return -1; + } + + ret = save_date_file(now_time, max_lines_file1, update_file1_time, data_file1, (char *)RECORD_FILE); + if(ret < 0) { + return -1; + } + + /* Read previous data in file2 */ + times = 0; + times = read_data_file(max_lines_file2, data_file2, (char *)RECORD_DAY_FILE); + if(times < 0) { + return -1; + } + + data_file2[times].ts = now_time; + foreach_port(port_num) + data_file2[times].pwr[port_num] = total_pwr[port_num]; + + ret = save_date_file(now_time, max_lines_file2, UPDATE_FILE2_TIME, data_file2, (char *)RECORD_DAY_FILE); + if(ret < 0) { + return -1; + } + + last_save_time = now_time; + } + } + return 0; +} + +int enable_port(char port_num) +{ + u8 reg; + + reg = CLASS_EN(port_num) | DET_EN(port_num); + return port_reg_write(port_num, DETECT_CLASS_EN_REG, reg); +} + +int disable_port(char port_num) +{ + u8 reg; + + reg = PWR_OFF(port_num); + return port_reg_write(port_num, POWER_EN_REG, reg); +} + +int port_monitor(u8 en, u8 vp, float volt, float curt) +{ + float mWatt = volt * curt; + port_watt_t* pw = &g_pwatts[vp]; + int reboot_value = rb_watts[vp]; + + pw->watts[pw->watch_counter++] = mWatt; + if (record_en) { + pw->watts_r[pw->watch_counter_r++] = mWatt; + if (pw->watch_counter_r == RECORD_INTERVAL) { + int i; + pw->ave_watts_r = 0.0; + record_time_up = 1; + for (i=0; iwatch_counter_r; i++) + pw->ave_watts_r += pw->watts_r[i]; + if (pw->ave_watts_r) + pw->ave_watts_r /= RECORD_INTERVAL; + pw->watch_counter_r = 0; + } + } + + if(en) { + int i, counter; + float av_mWatt = 0.0; + + counter = pw->rollback ? max_average_watts : pw->watch_counter; + for(i=0; iwatts[i]; + } + + av_mWatt /= counter; + if(av_mWatt < reboot_value && pw->rollback && pw->mark_has_pd) { + pw->mark_has_pd = 0; + pw->rollback = 0; + pw->watch_counter = 0; + fprintf(stdout, "port %u closed with avg %.3f mW, thd %d mW\n", vp, av_mWatt, reboot_value); + disable_port(vp); + sleep(1); + enable_port(vp); + } else if(av_mWatt >= reboot_value) { + pw->mark_has_pd = 1; + fprintf(stdout, "port %u, current %u has %0.2f mW, round avg %.3f mW, thd %d mW\n", + vp, counter, mWatt, av_mWatt, reboot_value); + } else { + /** port en, but no load, wait until one round rollback. */ + fprintf(stderr, "port %u, current %.2f mW, avg %.3f mW, thd %d mW\n", vp, mWatt, av_mWatt, reboot_value); + } + + if(pw->watch_counter == max_average_watts) + pw->rollback = 1; + + if(!port_enable_flag[vp]) { + fprintf(stdout, "shut down port %u \n", vp); + disable_port(vp); + sleep(1); + } + } else { + /* If the configuration sets the port to be open, try to open the port every 3 times. */ + if(port_enable_flag[vp] && !(pw->watch_counter % 3)) { + pw->watch_counter = 0; + pw->rollback = 0; + pw->mark_has_pd = 0; + /* power on & wait PD */ + fprintf(stdout, "port %u enable again PD detection\n", vp); + enable_port(vp); + sleep(1); /* must keep it. */ + } + } + if(pw->watch_counter == max_average_watts) + pw->watch_counter = 0; + + return 0; +} + +static void get_record(char *fn, int bound, data_file *date) { + FILE *fp; + int ret; + + if(access(fn, R_OK|W_OK)) { + if(!(fp = fopen(fn, "w"))) { + fprintf(stderr, "error in writing %s\n", fn); + exit(-1); + } + fclose(fp); + } else { + ret = read_data_file(bound, date, fn); + if(ret<0) { + fprintf(stderr, "error in reading %s\n", fn); + exit(-1); + } + } +} + +static int run_monitor(void) +{ + int i; + + memset(&g_pwatts, 0, sizeof(g_pwatts)); + for(i=0; i last_save_time ? (uint32_t)data_file2[i].ts : last_save_time; + } + + for(i=0; i MAX_AVERAGE_UB) + max_average_watts = MAX_AVERAGE; + + if(statistic_inteval < MONITOR_INTERVAL_LB || statistic_inteval > MONITOR_INTERVAL_UB) + statistic_inteval = MONITOR_INTERVAL_LB; + + if(record_en != 0 && record_en != 1) + record_en = 0; + + if(record_times < RECORD_TIME_LB || record_times > RECORD_TIME_UB) + record_times = RECORD_TIME_UB; +} +static void save_item_uci(struct uci_ptr ptr, struct uci_context *ctx, \ + struct uci_package *p, char *section, char *option, char *value) { + ptr.package = CONFIG_FN; + ptr.o = NULL; + ptr.s = uci_lookup_section(ctx, p, section); + ptr.section = section; + ptr.option = option; + ptr.value = value; + uci_set(ctx, &ptr); +} + +int main(int argc, char *argv[]) +{ + int c, i; + int port = 0; + int monitor_enable = 0; + char buf[10]; + struct uci_package *p = NULL; + struct uci_context *ctx = NULL; + struct uci_element *e; + + for(i=0; isections, e) { + struct uci_section *s = uci_to_section(e); + + if (!strcmp(s->type, "globals")) + config_parse_globals(ctx, s); + + if (!strncmp(s->type, "port", 4)) { + const char *enable = NULL, *pwr_thd = NULL; + int port_uci = 0; + + sscanf(s->e.name, "port%d", &port_uci); + if (port_uci < 1 || port_uci > MAX_PORTS) + port_uci = 1; + + enable = uci_lookup_option_string(ctx, s, "enable"); + + if (!strcmp(enable, "1")) { + port_enable_flag[port_uci] = 1; + if (enable_port(port_uci) < 0) + fprintf(stderr, "Failed to open port %d\n", port_uci); + else + usleep(500*1000); + } else { + port_enable_flag[port_uci] = 0; + if (disable_port(port_uci) < 0) + fprintf(stderr, "Failed to close port %d\n", port_uci); + else + usleep(500*1000); + } + pwr_thd = uci_lookup_option_string(ctx, s, "pwr_thd"); + rb_watts[port_uci] = pwr_thd ? atoi(pwr_thd) : MIN_LOAD_TRIG_mW_DEFAULT; + if (rb_watts[port_uci] < MIN_LOAD_TRIG_mW_LB || rb_watts[port_uci] > MIN_LOAD_TRIG_mW_UB) + rb_watts[port_uci] = MIN_LOAD_TRIG_mW_DEFAULT; + } + } + + struct uci_ptr ptr; + ptr.p = p; + + while ((c = getopt(argc, argv, "d:u:m:cr:s:t:")) != -1) { + switch (c) { + case 'c': + if (port_status(NULL) < 0) + fprintf(stderr, "no PD on port.\n"); + break; + case 'u': + port = atoi(optarg); + if (port < 1 || port > MAX_PORTS) + goto input_err; + + enable_port(port); + + memset(&buf, 0, sizeof(buf)); + sprintf(buf, "port%d", port); + save_item_uci(ptr, ctx, p, buf, "enable", "1"); + break; + case 'd': + port = atoi(optarg); + if (port < 1 || port > MAX_PORTS) + goto input_err; + + disable_port(port); + + memset(&buf, 0, sizeof(buf)); + sprintf(buf, "port%d", port); + save_item_uci(ptr, ctx, p, buf, "enable", "0"); + break; + case 'm': + statistic_inteval = atoi(optarg); + if (statistic_inteval < MONITOR_INTERVAL_LB || statistic_inteval > MONITOR_INTERVAL_UB) + goto input_err; + + monitor_enable = 1; + save_item_uci(ptr, ctx, p, "globals", "interval", optarg); + break; + case 's': + max_average_watts = atoi(optarg); + if (max_average_watts < MAX_AVERAGE_LB || max_average_watts > MAX_AVERAGE_UB) + goto input_err; + save_item_uci(ptr, ctx, p, "globals", "round", optarg); + break; + case 'r': + if (port < 1 || port > MAX_PORTS) + goto input_err; + + rb_watts[port] = atoi(optarg); //units: mV + if (rb_watts[port] < MIN_LOAD_TRIG_mW_LB || rb_watts[port] > MIN_LOAD_TRIG_mW_UB) + goto input_err; + + memset(&buf, 0, sizeof(buf)); + sprintf(buf, "port%d", port); + save_item_uci(ptr, ctx, p, buf, "pwr_thd", optarg); + break; + case 't': + if (!strcmp(optarg, "1")) + record_en = 1; + else if (!strcmp(optarg, "0")) + record_en = 0; + else + goto input_err; + + save_item_uci(ptr, ctx, p, "globals", "record_en", optarg); + break; + case '?': + default: + goto input_err; + } + } + + show_config(); + + uci_save(ctx, ptr.p); + uci_commit(ctx, &ptr.p, false); + + if (monitor_enable) { + return run_monitor(); + } + + return 0; + +input_err: + help(); + return -1; +} diff --git a/utils/xs2184-tsw202/files/libxs2184/xs2184.h b/utils/xs2184-tsw202/files/libxs2184/xs2184.h new file mode 100644 index 0000000000000..c07e66c755fa2 --- /dev/null +++ b/utils/xs2184-tsw202/files/libxs2184/xs2184.h @@ -0,0 +1,87 @@ +#ifndef _XS2184_H_ +#define _XS2184_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "./i2cbusses.h" +#include "./i2c-dev.h" + + +#define BUS_NUM 2 +#define XS2184_1_IIC_ADDR 0x27 +#define XS2184_2_IIC_ADDR 0x2f + +#define POWER_STA_REG 0x10 //供电状态寄存器 R, PGOOD4-1||PWR_EN4-1 +#define PSE_ID_REG 0x1b //芯片ID寄存器 R, 1101-0000 +#define DETECT_CLASS_EN_REG 0x14 //检测分级使能寄存器 R/W,CLASS_EN4-1||DET_EN4 +#define POWER_EN_REG 0x19 //端口供电使能寄存器 W, PWR_OFF4-1||PWR_ON4-1 + +#define XS2184_ID_VAL 0xd0 + +#define PORT_CRT_LSB_COMMON 0x30 +#define PORT_CRT_MSB_COMMON 0x31 +#define PORT_VLT_LSB_COMMON 0x32 +#define PORT_VLT_MSB_COMMON 0x33 + +//Address 0x14 检测/分级使能 +// #define CLASS_EN4 ((u8)0x80) +// #define CLASS_EN3 ((u8)0x40) +// #define CLASS_EN2 ((u8)0x20) +// #define CLASS_EN1 ((u8)0x10) +// #define DET_EN4 ((u8)0x08) +// #define DET_EN3 ((u8)0x04) +// #define DET_EN2 ((u8)0x02) +// #define DET_EN1 ((u8)0x01) +//Address 0x19 电源使能按钮 +// #define PWR_OFF4 ((u8)0x80) +// #define PWR_OFF3 ((u8)0x40) +// #define PWR_OFF2 ((u8)0x20) +// #define PWR_OFF1 ((u8)0x10) +// #define PWR_ON4 ((u8)0x08) +// #define PWR_ON3 ((u8)0x04) +// #define PWR_ON2 ((u8)0x02) +// #define PWR_ON1 ((u8)0x01) + +/** + * p 1 2 3 4 5 6 7 8 + * b 3 2 1 0 3 2 1 0 + */ + +#define PORT_TO_F(pn) ((pn + 3) % 4) +#define CLASS_EN(vp) ((u8)0x1 << (PORT_TO_F(vp) + 4)) +#define DET_EN(vp) ((u8)0x1 << (PORT_TO_F(vp))) +#define PWR_OFF(vp) ((u8)0x1 << (PORT_TO_F(vp) + 4)) +#define PWR_ON(vp) ((u8)0x1 << (PORT_TO_F(vp))) +#define PORT_MASK(pn) ((u8)0x1 << (pn - 1)) + +#define VOLT_MSB(vp) (PORT_VLT_MSB_COMMON + 4 * ((vp - 1) % 4)) +#define VOLT_LSB(vp) (PORT_VLT_LSB_COMMON + 4 * ((vp - 1) % 4)) +#define CURT_MSB(vp) (PORT_CRT_MSB_COMMON + 4 * ((vp - 1) % 4)) +#define CURT_LSB(vp) (PORT_CRT_LSB_COMMON + 4 * ((vp - 1) % 4)) + +#define CURRENT_PARA 122070 //uA +#define VOLTAGE_PARA 5835 //mV + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0])) +#endif + +typedef unsigned char u8; + +int open_chip(u8 file, u8 chip_addr); +int chip_found( u8 chip_addr); +int enable_port(char port_num); +int disable_port(char port_num); + +#endif /* _XS2184_H_ */ diff --git a/utils/xs2184-tsw202/files/xs2184.config b/utils/xs2184-tsw202/files/xs2184.config new file mode 100644 index 0000000000000..1f43e98fe6c41 --- /dev/null +++ b/utils/xs2184-tsw202/files/xs2184.config @@ -0,0 +1,39 @@ + +config globals 'globals' + option record_en '1' + option round '15' + option record_times '1080' + option interval '1000' + +config port 'port1' + option enable '1' + option pwr_thd '500' + +config port 'port2' + option enable '1' + option pwr_thd '500' + +config port 'port3' + option pwr_thd '500' + option enable '1' + +config port 'port4' + option enable '1' + option pwr_thd '500' + +config port 'port5' + option enable '1' + option pwr_thd '500' + +config port 'port6' + option enable '1' + option pwr_thd '500' + +config port 'port7' + option enable '1' + option pwr_thd '500' + +config port 'port8' + option enable '1' + option pwr_thd '500' + diff --git a/utils/xs2184-tsw202/files/xs2184.init b/utils/xs2184-tsw202/files/xs2184.init new file mode 100755 index 0000000000000..54796254b4cd3 --- /dev/null +++ b/utils/xs2184-tsw202/files/xs2184.init @@ -0,0 +1,127 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006-2011 OpenWrt.org + +USE_PROCD=1 +NAME=xs2184 +PROG=/usr/sbin/xs2184 + +START=97 + +# Bus define ... +BUS_NUM=2 + +# chip addr define ... +DEV_1_ADDR=0x27 +DEV_2_ADDR=0x2f + +CHIP_ID_REG=0x1b + +# register define ... +INTERRUPT_MASK_REG=0x01 +GLOPUSH_BUTTON_REG=0x1a +POWER_EN_REG=0x19 +OPER_MODE_REG=0x12 +CLASS5_EN_REG=0x1c +HIGH_POWER_EN_REG=0x44 +PORT1_GPMD_REG=0x46 +PORT2_GPMD_REG=0x4b +PORT3_GPMD_REG=0x50 +PORT4_GPMD_REG=0x55 +PORT12_TLIM_REG=0x1e +PORT34_TLIM_REG=0x1f +DISCON_EN_REG=0x13 +DETECT_CLASS_EN_REG=0x14 +POWER_EN_REG=0x19 +PORT1_ILIM_REG=0x48 +PORT1_ICUT_REG=0x47 +PORT2_ILIM_REG=0x4d +PORT2_ICUT_REG=0x4c +PORT3_ILIM_REG=0x52 +PORT3_ICUT_REG=0x51 +PORT4_ILIM_REG=0x57 +PORT4_ICUT_REG=0x56 + +# cmd templete +i2c_set(){ #1:chip_addr 2:reg_addr 3:vel + i2cset -y ${BUS_NUM} $1 $2 $3 +} + +chip_init(){ #1:chip_addr + echo "xs2184 init on bus ${BUS_NUM}, addr $1" + + i2c_set $1 $INTERRUPT_MASK_REG 0x99 #0x92 0x83 + i2c_set $1 $GLOPUSH_BUTTON_REG 0xc0 + # i2c_set $1 $POWER_EN_REG 0xf0 /* shut down all ports*/ + # i2c_set $1 $OPER_MODE_REG 0xff #工作模式:自动 + i2c_set $1 $OPER_MODE_REG 0xaa + i2c_set $1 $CLASS5_EN_REG 0x0 + i2c_set $1 $HIGH_POWER_EN_REG 0x0f + i2c_set $1 $PORT1_GPMD_REG 0x01 + i2c_set $1 $PORT2_GPMD_REG 0x01 + i2c_set $1 $PORT3_GPMD_REG 0x01 + i2c_set $1 $PORT4_GPMD_REG 0x01 + # i2c_set $1 $PORT12_TLIM_REG 0x77 + # i2c_set $1 $PORT34_TLIM_REG 0x77 + i2c_set $1 $DISCON_EN_REG 0x0f #打开断开检测 + i2c_set $1 $DETECT_CLASS_EN_REG 0xff + i2c_set $1 $PORT1_ILIM_REG 0x80 + i2c_set $1 $PORT1_ICUT_REG 0x14 + i2c_set $1 $PORT2_ILIM_REG 0x80 + i2c_set $1 $PORT2_ICUT_REG 0x14 + i2c_set $1 $PORT3_ILIM_REG 0x80 + i2c_set $1 $PORT3_ICUT_REG 0x14 + i2c_set $1 $PORT4_ILIM_REG 0x80 + i2c_set $1 $PORT4_ICUT_REG 0x14 + i2c_set $1 $POWER_EN_REG 0x0f # switch on all ports +} + +chip_power_off() { + echo "xs2184 power off on bus ${BUS_NUM}, addr $1" + i2c_set $1 $POWER_EN_REG 0xf0 +} + +init_chips() { + i2cget -y ${BUS_NUM} ${DEV_1_ADDR} ${CHIP_ID_REG} 2>/dev/null && { + chip_init ${DEV_1_ADDR} + } + + i2cget -y ${BUS_NUM} ${DEV_2_ADDR} ${CHIP_ID_REG} 2>/dev/null && { + chip_init ${DEV_2_ADDR} + } +} + +close_chips() { + i2cget -y ${BUS_NUM} ${DEV_1_ADDR} ${CHIP_ID_REG} 2>/dev/null && { + chip_power_off ${DEV_1_ADDR} + } + + i2cget -y ${BUS_NUM} ${DEV_2_ADDR} ${CHIP_ID_REG} 2>/dev/null && { + chip_power_off ${DEV_2_ADDR} + } +} + +start_service() { + echo "start xs2184 PSE" + + init_chips + + procd_open_instance + procd_set_param command "$PROG" + procd_append_param command "-m 1000" + # procd_set_param stdout 1 + procd_set_param stderr 1 + procd_set_param respawn + procd_close_instance +} + +stop() { + echo "stop xs2184 PSE" + + service_stop $PROG + init_chips +} + +reload() { + service_reload $PROG + close_chips +}