Pwmtachtool
Pwmtachtool is a generic tool to configure Fan speed.
It supports set / get fan speed, set / get dutycycle.
Updated LICENSE file to cover all source files and
removed file specific licenses at the top of each
of the files.
Signed-off-by: Hongwei Zhang <hongweiz@ami.com>
Change-Id: I9100c81e59f04f45af4e146e1e547cf7914770e3
Signed-off-by: Kiran Kumar <kirank@ami.com>
diff --git a/hongweiz/pwmtachtool/LICENSE b/hongweiz/pwmtachtool/LICENSE
new file mode 100644
index 0000000..3600ecd
--- /dev/null
+++ b/hongweiz/pwmtachtool/LICENSE
@@ -0,0 +1,12 @@
+Copyright (C) 2019 American Megatrends Internation LLC.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/hongweiz/pwmtachtool/Makefile.am b/hongweiz/pwmtachtool/Makefile.am
new file mode 100644
index 0000000..38bdf12
--- /dev/null
+++ b/hongweiz/pwmtachtool/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = src
+dist_doc_DATA = README
diff --git a/hongweiz/pwmtachtool/README b/hongweiz/pwmtachtool/README
new file mode 100644
index 0000000..374b9fd
--- /dev/null
+++ b/hongweiz/pwmtachtool/README
@@ -0,0 +1,22 @@
+PWMTACHTOOL
+
+This package contains a tool that can be used to get / set PWM values to control
+Fan speeds on a server chassis using the management entity or BMC.
+
+PWMTACH Test Tool (Version 1.0)
+Usage : pwmtachtool <device_id> <command-option> <fannum>
+
+DOCUMENTATION
+-------------
+
+The tool come with simple text documentation.
+Type in the command without any arguments to see the usage and supported commands.
+
+
+QUESTIONS AND BUG REPORTS
+-------------------------
+
+Please post your questions and bug reports to:
+bugs-bmc@ami.com
+with Cc to the current maintainer:
+kirank@ami.com
diff --git a/hongweiz/pwmtachtool/configure.ac b/hongweiz/pwmtachtool/configure.ac
new file mode 100644
index 0000000..2cfbd7a
--- /dev/null
+++ b/hongweiz/pwmtachtool/configure.ac
@@ -0,0 +1,9 @@
+AC_INIT([pwmtachtool], [1.0], [bugs-bmc@ami.com])
+AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AC_PROG_CC
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_FILES([
+ Makefile
+ src/Makefile
+])
+AC_OUTPUT
diff --git a/hongweiz/pwmtachtool/src/EINTR_wrappers.c b/hongweiz/pwmtachtool/src/EINTR_wrappers.c
new file mode 100755
index 0000000..e8f02db
--- /dev/null
+++ b/hongweiz/pwmtachtool/src/EINTR_wrappers.c
@@ -0,0 +1,753 @@
+
+/*
+* File: EINTR_wrappers.c
+*
+* This file implements the wrapper functions for some of the System APIs
+*
+* Copyright (C) <2019> <American Megatrends International LLC>
+*
+*/
+
+#include "EINTR_wrappers.h"
+#if defined(__linux__)
+#include <sys/msg.h>
+#include <sys/file.h>
+#endif
+#include <errno.h>
+#include <unistd.h>
+
+static const int OneSecondasNS = 1000000000;
+
+#ifndef bool
+typedef int bool;
+#endif
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+typedef struct
+{
+ bool OnePoll;
+ struct timespec EndTime, Timeout;
+} SIGWRAP_TIMEOUT;
+
+static void sigwrap_InitTimeout(SIGWRAP_TIMEOUT *pDst, const struct timespec *timeout)
+{
+ pDst->Timeout = *timeout;
+
+ if ((timeout->tv_sec == 0) && (timeout->tv_nsec == 0)) // If both value are zero than only a single poll is requested!
+ {
+ pDst->OnePoll = 1;
+ return;
+ }
+
+ pDst->OnePoll = 0;
+
+ struct timespec Now;
+
+ (void)clock_gettime(CLOCK_MONOTONIC_RAW, &Now); // CLOCK_MONOTONIC_RAW is not affected by NTP etc.
+
+ pDst->EndTime.tv_sec = Now.tv_sec + pDst->Timeout.tv_sec; // Check necessary in 2038 due to signed integer variables
+ pDst->EndTime.tv_nsec = Now.tv_nsec + pDst->Timeout.tv_nsec;
+
+ if (pDst->EndTime.tv_nsec >= OneSecondasNS)
+ {
+ pDst->EndTime.tv_sec += (pDst->EndTime.tv_nsec / OneSecondasNS);
+ pDst->EndTime.tv_nsec = (pDst->EndTime.tv_nsec % OneSecondasNS);
+ }
+}
+
+
+static bool sigwrap_CheckTimeout(SIGWRAP_TIMEOUT *pTo)
+{
+ if (pTo->OnePoll == TRUE) // Make sure, that in the case that a single poll is requested at least one call is not terminated with EINTR
+ return FALSE;
+
+ struct timespec Now;
+
+ (void)clock_gettime(CLOCK_MONOTONIC_RAW, &Now);
+
+ if (Now.tv_sec > pTo->EndTime.tv_sec) // Can become a problem already in 2038 due to signed integer variables
+ return TRUE;
+
+ pTo->Timeout.tv_nsec = pTo->EndTime.tv_nsec - Now.tv_nsec;
+ pTo->Timeout.tv_sec = pTo->EndTime.tv_sec - Now.tv_sec;
+
+ if (pTo->Timeout.tv_sec == 0)
+ {
+ if (pTo->Timeout.tv_nsec <= 0)
+ return TRUE;
+ }
+ else if (pTo->Timeout.tv_nsec < 0)
+ {
+ pTo->Timeout.tv_nsec += OneSecondasNS;
+ pTo->Timeout.tv_sec--;
+ }
+
+ return FALSE;
+}
+
+
+
+int sigwrap_semop(int semid, struct sembuf *sops, size_t nsops)
+{
+ while (1)
+ {
+ if (semop(semid, sops, nsops) == 0)
+ return 0;
+
+ if (errno != EINTR)
+ return -1;
+ }
+}
+
+int sigwrap_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
+{
+ SIGWRAP_TIMEOUT To;
+
+ if (timeout != -1)
+ {
+ struct timespec Timeout;
+
+ Timeout.tv_sec = timeout / 1000;
+ Timeout.tv_nsec = (timeout % 1000) * 1000000; // Convert msec to nsec
+
+ sigwrap_InitTimeout(&To, &Timeout);
+ }
+
+ while (1)
+ {
+ int Result = epoll_wait(epfd, events, maxevents, timeout);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+
+ if (timeout == -1)
+ continue;
+
+ if (sigwrap_CheckTimeout(&To))
+ return 0;
+
+ timeout = To.Timeout.tv_sec * 1000 + To.Timeout.tv_nsec / 1000000;
+ }
+}
+
+
+int sigwrap_epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask)
+{
+ SIGWRAP_TIMEOUT To;
+
+ if (timeout != -1)
+ {
+ struct timespec Timeout;
+
+ Timeout.tv_sec = timeout / 1000;
+ Timeout.tv_nsec = (timeout % 1000) * 1000000; // Convert msec to nsec
+
+ sigwrap_InitTimeout(&To, &Timeout);
+ }
+
+ while (1)
+ {
+ int Result = epoll_pwait(epfd, events, maxevents, timeout, sigmask);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+
+ if (timeout == -1)
+ continue;
+
+ if (sigwrap_CheckTimeout(&To))
+ return 0;
+
+ timeout = To.Timeout.tv_sec * 1000 + To.Timeout.tv_nsec / 1000000;
+ }
+}
+
+
+int sigwrap_sigwaitinfo(const sigset_t *set, siginfo_t *info)
+{
+ while (1)
+ {
+ int Result = sigwaitinfo(set, info);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+ }
+}
+
+
+int sigwrap_sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout)
+{
+ SIGWRAP_TIMEOUT To;
+
+ sigwrap_InitTimeout(&To, timeout);
+
+ while (1)
+ {
+ int Result = sigtimedwait(set, info, &To.Timeout);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+
+ if (sigwrap_CheckTimeout(&To))
+ return 0;
+ }
+}
+
+
+int sigwrap_nanosleep(const struct timespec *req, struct timespec *rem)
+{
+ struct timespec Wait, Remain;
+
+ if (!rem)
+ rem = &Remain;
+
+ Wait = *req;
+
+ while (1)
+ {
+ if (nanosleep(&Wait, rem) == 0)
+ return 0;
+
+ if (errno != EINTR)
+ return -1;
+
+ Wait = *rem;
+ }
+}
+
+
+int sigwrap_clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain)
+{
+ struct timespec Wait, Remain;
+
+ if (!remain)
+ remain = &Remain;
+
+ Wait = *request;
+
+ while (1)
+ {
+ int Result = clock_nanosleep(clock_id, flags, &Wait, remain);
+
+ if (Result == 0)
+ return Result;
+
+ if (Result != EINTR)
+ return Result;
+
+ if (flags != TIMER_ABSTIME)
+ Wait = *remain;
+ }
+}
+
+
+int sigwrap_usleep(useconds_t usec)
+{
+ SIGWRAP_TIMEOUT To;
+
+ struct timespec Timeout;
+
+ Timeout.tv_sec = usec / 1000000;
+ Timeout.tv_nsec = (usec % 1000000) * 1000;
+
+ sigwrap_InitTimeout(&To, &Timeout);
+
+ while (1)
+ {
+ if (usleep(usec) == 0)
+ return 0;
+
+ if (errno != EINTR)
+ return -1;
+
+ if (sigwrap_CheckTimeout(&To))
+ return 0;
+
+ usec = To.Timeout.tv_sec * 1000000 + To.Timeout.tv_nsec / 1000;
+ }
+}
+
+
+int sigwrap_poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+ SIGWRAP_TIMEOUT To;
+
+ if (timeout > 0)
+ {
+ struct timespec Timeout;
+
+ Timeout.tv_sec = timeout / 1000;
+ Timeout.tv_nsec = (timeout % 1000) * 1000000;
+
+ sigwrap_InitTimeout(&To, &Timeout);
+ }
+
+ while (1)
+ {
+ int Result = poll(fds, nfds, timeout);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+
+ if (timeout < 0) // Specifying a negative value in timeout means an infinite/no timeout.
+ continue;
+ else if (timeout == 0)
+ continue; // We want to make sure that at least one check was not aborted with EINTR
+
+ if (sigwrap_CheckTimeout(&To))
+ return 0;
+
+ timeout = To.Timeout.tv_sec * 1000 + To.Timeout.tv_nsec / 1000000;
+ }
+}
+
+int sigwrap_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
+{
+ while (1)
+ {
+ int Result = select(nfds, readfds, writefds, exceptfds, timeout);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+ }
+}
+
+
+int sigwrap_pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout,
+ const sigset_t *sigmask)
+{
+ SIGWRAP_TIMEOUT To;
+
+ if (timeout != NULL)
+ {
+ sigwrap_InitTimeout(&To, timeout);
+ timeout = &To.Timeout;
+ }
+
+ while (1)
+ {
+ int Result = pselect(nfds, readfds, writefds, exceptfds, timeout, sigmask);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+
+ if (timeout == NULL)
+ continue;
+
+ if (sigwrap_CheckTimeout(&To))
+ return 0;
+ }
+}
+
+
+int sigwrap_msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
+{
+ while (1)
+ {
+ int Result = msgsnd(msqid, msgp, msgsz, msgflg);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+ }
+}
+
+
+ssize_t sigwrap_msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
+{
+ while (1)
+ {
+ ssize_t Result = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+ }
+}
+
+
+int sigwrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
+{
+ while (1)
+ {
+ int Result = connect(sockfd, addr, addrlen);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+ }
+}
+
+
+ssize_t sigwrap_send(int sockfd, const void *buf, size_t len, int flags)
+{
+ while (1)
+ {
+ ssize_t Result = send(sockfd, buf, len, flags);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+ }
+}
+
+
+ssize_t sigwrap_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr,
+ socklen_t addrlen)
+{
+ while (1)
+ {
+ ssize_t Result = sendto(sockfd, buf, len, flags, dest_addr, addrlen);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+ }
+}
+
+
+ssize_t sigwrap_sendsendmsg(int sockfd, const struct msghdr *msg, int flags)
+{
+ while (1)
+ {
+ ssize_t Result = sendmsg(sockfd, msg, flags);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+ }
+}
+
+
+int sigwrap_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
+{
+ while (1)
+ {
+ int Result = accept(sockfd, addr, addrlen);
+
+ if (Result != -1)
+ return Result;
+
+ if (errno != EINTR)
+ return Result;
+ }
+}
+
+// EINTR wrapper for the standard read() function. Can be used for sockets that are the to non-blocking mode.
+// The length of the returned data can be shorter than the requested one!
+
+ssize_t sigwrap_read(int fd, void *buf, size_t count)
+{
+ while (1)
+ {
+ ssize_t Result = read(fd, buf, count);
+
+ if (Result != -1)
+ return (Result);
+
+ if (errno != EINTR)
+ return (Result);
+ }
+}
+
+
+// EINTR wrapper for the standard read() function. Waits until ALL requested data is available. Use the non-blocking version (sigwrap_read)
+// for sockets that are set to non-blocking mode or when partial data is okay
+// Although the description for the read() function describes it differently, it seems possible that the original function may already return
+// even though partial data has already been read. This implementation makes sure that all requested data have been read.
+// See the comment in the signal description https://linux.die.net/man/7/signal
+//* read(2), readv(2), write(2), writev(2), and ioctl(2) calls on "slow" devices.
+//* A "slow" device is one where the I/O call may block for an indefinite time, for example, a terminal, pipe, or socket.
+//* (A disk is not a slow device according to this definition.) If an I/O call on a slow device has already transferred
+//* some data by the time it is interrupted by a signal handler, then the call will return a success status (normally, the number of bytes transferred).
+
+ssize_t sigwrap_blocking_read(int hFile, void *pData, size_t RdLen)
+{
+ ssize_t Transfered;
+ ssize_t Len = RdLen;
+
+ while ((Transfered = read(hFile, pData, Len)) != Len)
+ {
+ if (Transfered == 0) // EOF reached?
+ return 0;
+
+ if (Transfered != -1)
+ {
+ pData += Transfered;
+ Len -= Transfered;
+ continue;
+ }
+
+ if (errno != EINTR)
+ return -1;
+ }
+
+ return RdLen;
+}
+
+
+ssize_t sigwrap_readv(int fd, const struct iovec *iov, int iovcnt)
+{
+ while (1)
+ {
+ ssize_t Result = readv(fd, iov, iovcnt);
+
+ if (Result != -1)
+ return (Result);
+
+ if (errno != EINTR)
+ return (Result);
+ }
+}
+
+
+ssize_t sigwrap_recv(int sockfd, void *buf, size_t len, int flags)
+{
+ while (1)
+ {
+ ssize_t Result = recv(sockfd, buf, len, flags);
+
+ if (Result != -1)
+ return (Result);
+
+ if (errno != EINTR)
+ return (Result);
+ }
+}
+
+
+ssize_t sigwrap_recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)
+{
+ while (1)
+ {
+ ssize_t Result = recvfrom(sockfd, buf, len, flags, src_addr, addrlen);
+
+ if (Result != -1)
+ return (Result);
+
+ if (errno != EINTR)
+ return (Result);
+ }
+}
+
+
+ssize_t sigwrap_recvmsg(int sockfd, struct msghdr *msg, int flags)
+{
+ while (1)
+ {
+ ssize_t Result = recvmsg(sockfd, msg, flags);
+
+ if (Result != -1)
+ return (Result);
+
+ if (errno != EINTR)
+ return (Result);
+ }
+}
+
+
+// EINTR wrapper for the standard write() function. Can be used for sockets that are the to non-blocking mode.
+// The length of the effectively written data can be shorter than the length specified at the function call!
+
+ssize_t sigwrap_write(int fd, const void *buf, size_t count)
+{
+ while (1)
+ {
+ ssize_t Result = write(fd, buf, count);
+
+ if (Result != -1)
+ return (Result);
+
+ if (errno != EINTR)
+ return (Result);
+ }
+}
+
+// EINTR wrapper for the standard write() function. Waits until ALL data is written! Use the non-blocking version (sigwrap_write)
+// for sockets that are set to non-blocking mode, or when it is OK to write only partial data.
+// Although the description for the write() function describes it differently, it seems possible that the original function may already return
+// even though partial data has already been written. This implementation makes sure that all requested data have been written.
+// See the comment in the signal description https://linux.die.net/man/7/signal
+//* read(2), readv(2), write(2), writev(2), and ioctl(2) calls on "slow" devices.
+//* A "slow" device is one where the I/O call may block for an indefinite time, for example, a terminal, pipe, or socket.
+//* (A disk is not a slow device according to this definition.) If an I/O call on a slow device has already transferred
+//* some data by the time it is interrupted by a signal handler, then the call will return a success status (normally, the number of bytes transferred).
+
+ssize_t sigwrap_blocking_write(int hFile, const void *pData, ssize_t WrtLen)
+{
+ ssize_t Written;
+ ssize_t Len = WrtLen;
+
+ while ((Written = write(hFile, pData, Len)) != Len)
+ {
+ if (Written != -1)
+ {
+ pData += Written;
+ Len -= Written;
+ continue;
+ }
+
+ if (errno != EINTR)
+ return -1;
+ }
+
+ return WrtLen;
+}
+
+
+ssize_t sigwrap_writev(int fd, const struct iovec *iov, int iovcnt)
+{
+ while (1)
+ {
+ ssize_t Result = writev(fd, iov, iovcnt);
+
+ if (Result != -1)
+ return (Result);
+
+ if (errno != EINTR)
+ return (Result);
+ }
+}
+
+
+int sigwrap_close(int hFile)
+{
+ while (close(hFile) == -1)
+ {
+ if (errno != EINTR)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int sigwrap_open_mode(const char *pathname, int flags, mode_t mode)
+{
+ while (1)
+ {
+ int hFile = open(pathname, flags, mode);
+
+ if(hFile != -1)
+ return hFile;
+
+ if (errno != EINTR)
+ return hFile;
+ }
+}
+
+int sigwrap_open(const char *pathname, int flags)
+{
+ while (1)
+ {
+ int hFile = open(pathname, flags);
+
+ if(hFile != -1)
+ return hFile;
+
+ if (errno != EINTR)
+ return hFile;
+ }
+}
+
+
+pid_t sigwrap_wait(int *status)
+{
+ while(1)
+ {
+ pid_t Result = wait(status);
+
+ if(Result != -1)
+ return Result;
+
+ if(errno != EINTR)
+ return Result;
+ }
+}
+
+
+pid_t sigwrap_waitpid(pid_t pid, int *status, int options)
+{
+ while(1)
+ {
+ pid_t Result = waitpid(pid, status, options);
+
+ if(Result != -1)
+ return Result;
+
+ if(errno != EINTR)
+ return Result;
+ }
+}
+
+
+int sigwrap_waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options)
+{
+ while(1)
+ {
+ int Result = waitid(idtype, id, infop, options);
+
+ if(Result != -1)
+ return Result;
+
+ if(errno != EINTR)
+ return Result;
+ }
+}
+
+
+int sigwrap_flock(int fd, int operation)
+{
+ while(1)
+ {
+ int Result = flock(fd, operation);
+
+ if(Result != -1)
+ return Result;
+
+ if(errno != EINTR)
+ return Result;
+ }
+}
+
+
diff --git a/hongweiz/pwmtachtool/src/EINTR_wrappers.h b/hongweiz/pwmtachtool/src/EINTR_wrappers.h
new file mode 100755
index 0000000..019ef85
--- /dev/null
+++ b/hongweiz/pwmtachtool/src/EINTR_wrappers.h
@@ -0,0 +1,124 @@
+
+/*File:EINTR_wrappers.h
+*
+* Wrapper functions header file.
+*
+* Copyright (C) <2019> <American Megatrends International LLC>
+*
+*/
+
+#ifndef EINTR_WRAPPERS_H__
+#define EINTR_WRAPPERS_H__
+
+#if defined(__linux__)
+#ifndef _SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifndef _SYS_PC_H
+#include <sys/ipc.h>
+#endif
+
+#ifndef _SYS_SEM_H
+#include <sys/sem.h>
+#endif
+
+#ifndef _SYS_EPOLL_H
+#include <sys/epoll.h>
+#endif
+
+#ifndef _SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifndef _SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifndef _TIME_H
+#include <time.h>
+#endif
+
+#ifndef _POLL_H
+#include <poll.h>
+#endif
+
+#ifndef _WAIT_H
+#include <wait.h>
+#endif
+
+#ifndef _UNISTD_H
+#include <unistd.h>
+#endif
+
+int sigwrap_semop(int semid, struct sembuf *sops, size_t nsops);
+int sigwrap_semtimedop(int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout);
+int sigwrap_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
+int sigwrap_epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask);
+int sigwrap_sigwaitinfo(const sigset_t *set, siginfo_t *info);
+int sigwrap_sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout);
+int sigwrap_nanosleep(const struct timespec *req, struct timespec *rem);
+int sigwrap_clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain);
+int sigwrap_usleep(useconds_t usec);
+int sigwrap_poll(struct pollfd *fds, nfds_t nfds, int timeout);
+int sigwrap_ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *tmo_p, const sigset_t *sigmask);
+int sigwrap_select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
+int sigwrap_pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);
+int sigwrap_msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
+int sigwrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
+int sigwrap_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
+int sigwrap_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
+int sigwrap_close(int hFile);
+int sigwrap_open_mode(const char *pathname, int flags, mode_t mode);
+int sigwrap_open(const char *pathname, int flags);
+
+
+pid_t sigwrap_wait(int *status);
+pid_t sigwrap_waitpid(pid_t pid, int *status, int options);
+int sigwrap_waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
+
+
+ssize_t sigwrap_msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
+ssize_t sigwrap_send(int sockfd, const void *buf, size_t len, int flags);
+ssize_t sigwrap_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
+ssize_t sigwrap_sendsendmsg(int sockfd, const struct msghdr *msg, int flags);
+
+ssize_t sigwrap_read(int fd, void *buf, size_t count);
+// EINTR wrapper for the standard read() function. Waits until ALL requested data is available. Use the non-blocking version (sigwrap_read)
+// for sockets that are set to non-blocking mode or when partial data is okay
+// Although the description for the read() function describes it differently, it seems possible that the original function may already return
+// even though partial data has already been read. This implementation makes sure that all requested data have been read.
+// See the comment in the signal description https://linux.die.net/man/7/signal
+//* read(2), readv(2), write(2), writev(2), and ioctl(2) calls on "slow" devices.
+//* A "slow" device is one where the I/O call may block for an indefinite time, for example, a terminal, pipe, or socket.
+//* (A disk is not a slow device according to this definition.) If an I/O call on a slow device has already transferred
+//* some data by the time it is interrupted by a signal handler, then the call will return a success status (normally, the number of bytes transferred).
+ssize_t sigwrap_blocking_read(int hFile, void *pData, size_t RdLen);
+
+ssize_t sigwrap_readv(int fd, const struct iovec *iov, int iovcnt);
+
+ssize_t sigwrap_write(int fd, const void *buf, size_t count);
+// EINTR wrapper for the standard write() function. Waits until ALL data is written! Use the non-blocking version (sigwrap_write)
+// for sockets that are set to non-blocking mode, or when it is OK to write only partial data.
+// Although the description for the write() function describes it differently, it seems possible that the original function may already return
+// even though partial data has already been written. This implementation makes sure that all requested data have been written.
+// See the comment in the signal description https://linux.die.net/man/7/signal
+//* read(2), readv(2), write(2), writev(2), and ioctl(2) calls on "slow" devices.
+//* A "slow" device is one where the I/O call may block for an indefinite time, for example, a terminal, pipe, or socket.
+//* (A disk is not a slow device according to this definition.) If an I/O call on a slow device has already transferred
+//* some data by the time it is interrupted by a signal handler, then the call will return a success status (normally, the number of bytes transferred).
+ssize_t sigwrap_blocking_write(int hFile, const void *pData, ssize_t WrtLen);
+
+ssize_t sigwrap_writev(int fd, const struct iovec *iov, int iovcnt);
+
+ssize_t sigwrap_recv(int sockfd, void *buf, size_t len, int flags);
+
+ssize_t sigwrap_recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
+
+ssize_t sigwrap_recvmsg(int sockfd, struct msghdr *msg, int flags);
+
+int sigwrap_flock(int fd, int operation);
+
+
+#endif
+#endif
diff --git a/hongweiz/pwmtachtool/src/Makefile.am b/hongweiz/pwmtachtool/src/Makefile.am
new file mode 100644
index 0000000..b93db44
--- /dev/null
+++ b/hongweiz/pwmtachtool/src/Makefile.am
@@ -0,0 +1,2 @@
+bin_PROGRAMS = pwmtachtool
+pwmtachtool_SOURCES = pwmtachtool.c pwmtach.c EINTR_wrappers.c EINTR_wrappers.h libpwmtach.h pwmtach_ioctl.h
diff --git a/hongweiz/pwmtachtool/src/libpwmtach.h b/hongweiz/pwmtachtool/src/libpwmtach.h
new file mode 100644
index 0000000..c4af830
--- /dev/null
+++ b/hongweiz/pwmtachtool/src/libpwmtach.h
@@ -0,0 +1,45 @@
+
+/*****************************-*- ********-*-********************************
+ * Filename: libpwmtach.h
+ * Description: Library interface to pwmtach access
+ *
+ * Simple interface library for fan control operations
+ * This file provides interface functions to support pwmtachtool.
+ * Copyright (C) <2019> <American Megatrends International LLC>
+ *
+ *****************************************************************************/
+#ifndef LIBPWMTACH_H
+#define LIBPWMTACH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PWMTACH_DEV_FILE "/dev/pwmtach"
+
+ /** \file libpwmtach.h
+ * \brief Public headers for the PWMTACH interface library
+ *
+ * This library contains friendly function call interfaces for setting
+ * fan control operations.
+ */
+ extern int set_fan_speed ( unsigned int dev_id, unsigned char fan_number, unsigned int rpm_value );
+ extern int get_fan_speed ( unsigned int dev_id, unsigned char fan_number, unsigned int *rpm_value );
+ /************/
+
+
+ /******Pwmtach interface library to set/read pwm/tach by channel basis apart from configure fan table********/
+ /*********Directly control pwm dutycycle (1 to 99) instead of RPM***********/
+ extern int get_tach_speed ( unsigned int dev_id, unsigned char tach_number, unsigned int *rpm_value );
+ //Notice: dutycycle_percentage value should be between 1 to 99.
+ extern int set_pwm_dutycycle ( unsigned int dev_id, unsigned char pwm_number, unsigned char dutycycle_percentage );
+ //Notice: dutycycle_value should be between 0 to 255.
+ extern int set_pwm_dutycycle_value ( unsigned int dev_id, unsigned char pwm_number, unsigned char dutycycle_value );
+ extern int get_pwm_dutycycle ( unsigned int dev_id, unsigned char pwm_number, unsigned char *dutycycle_percentage );
+
+ /************/
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/hongweiz/pwmtachtool/src/pwmtach.c b/hongweiz/pwmtachtool/src/pwmtach.c
new file mode 100644
index 0000000..edb3516
--- /dev/null
+++ b/hongweiz/pwmtachtool/src/pwmtach.c
@@ -0,0 +1,429 @@
+
+/*
+ * Simple interface library for fan control operations
+ * This file provides interface functions to support pwmtachtool.
+ * Copyright (C) <2019> <American Megatrends International LLC>
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include "libpwmtach.h"
+#include "pwmtach_ioctl.h"
+#include "EINTR_wrappers.h"
+#include <stdlib.h>
+
+void select_sleep(time_t sec,suseconds_t usec)
+{
+ struct timeval tv;
+
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+
+ while(sigwrap_select(0, NULL, NULL, NULL, &tv) < 0);
+}
+
+//support acessing driver using sysfs device file
+static char DevNodeFileName[50];
+#define HWMON_DIR "/sys/class/hwmon"
+
+//build the pwm and tach access device node file name, and mapping pwm/tach number starting from 1.
+#define BUILD_PWM_NODE_NAME(buffer,DEV_ID,PWM_NUM) snprintf(buffer, sizeof(buffer), "%s%d%s%d", HWMON_DIR "/hwmon",DEV_ID, "/pwm", PWM_NUM+1)
+#define BUILD_TACH_NODE_NAME(buffer,DEV_ID,TACH_NUM) snprintf(buffer, sizeof(buffer), "%s%d%s%d%s", HWMON_DIR "/hwmon",DEV_ID, "/fan", TACH_NUM+1,"_input")
+#define BUILD_FAN_REG_NAME(buffer,DEV_ID,FAN_NUM) snprintf(buffer, sizeof(buffer), "%s%d%s%d%s", HWMON_DIR "/hwmon",DEV_ID, "/of_node/fan@", FAN_NUM,"/reg")
+
+//predefine FAN RPM range, must defined at some where for configuration.
+#define RPM_MAX 38600
+#define RPM_MIN 7500
+#define COUNTERRES_DEF 100
+
+/* Check hwmon if exist or not */
+static int pwmtach_directory_check(void)
+{
+ int retval = 0;
+ struct stat sb;
+ if (!(stat("/sys/class/hwmon", &sb) == 0 && S_ISDIR(sb.st_mode)))
+ {
+ printf("\"/sys/class/hwmon\" not exist!\n");
+ retval = -1;
+ }
+ return retval;
+}
+//Notice: dutycycle_value is one byte (0-255)
+static int SET_PWM_DUTYCYCLE_VALUE ( pwmtach_ioctl_data *ppwmtach_arg )
+{
+ int retval = 0;
+ unsigned char dutycycle_value;
+ int fd;
+ char duty_num[5];
+
+ retval = pwmtach_directory_check();
+ if(retval != 0)
+ {
+ return retval;
+ }
+
+ dutycycle_value = ppwmtach_arg->dutycycle;
+ BUILD_PWM_NODE_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->pwmnumber);
+ retval = access(DevNodeFileName,F_OK);
+ if(retval != 0)
+ {
+ return retval;
+ }
+
+ fd = sigwrap_open(DevNodeFileName, O_WRONLY);
+ if (fd < 0) {
+ return fd;
+ }
+
+ snprintf(duty_num,5, "%d", dutycycle_value);
+
+ if ( write(fd, duty_num, strlen (duty_num)) != (ssize_t )strlen(duty_num)){
+ printf("%s: Error write dutycycle value %d to pwm %d\n",__FUNCTION__,dutycycle_value,ppwmtach_arg->pwmnumber);
+ retval = -1;
+ }
+ (void)sigwrap_close(fd);
+
+ return retval;
+}
+
+//Notice: dutycycle_percentage value should be between 1 to 99.
+static int SET_PWM_DUTYCYCLE ( pwmtach_ioctl_data *ppwmtach_arg)
+{
+ int retval = 0;
+ unsigned char dutycycle_value;
+
+ if(ppwmtach_arg->dutycycle > 100)
+ {
+ return -1;
+ }
+
+ dutycycle_value = (ppwmtach_arg->dutycycle*255)/100;
+ ppwmtach_arg->dutycycle = dutycycle_value;
+ retval = SET_PWM_DUTYCYCLE_VALUE(ppwmtach_arg);
+ return retval;
+}
+
+static int GET_PWM_DUTYCYCLE ( pwmtach_ioctl_data *ppwmtach_arg )
+{
+ int retval = 0;
+ int fd;
+ char duty_num[5];
+
+ retval = pwmtach_directory_check();
+ if(retval != 0)
+ {//printf("%s,error 0\n",__FUNCTION__);
+ return retval;
+ }
+
+ BUILD_PWM_NODE_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->pwmnumber);
+ retval = access(DevNodeFileName,F_OK);
+ if(retval != 0)
+ {printf("%s,error 2,%s not exist\n",__FUNCTION__,DevNodeFileName);
+ return retval;
+ }
+ fd = sigwrap_open(DevNodeFileName, O_RDONLY);
+ if (fd < 0) {printf("%s,error 3\n",__FUNCTION__);
+ return fd;
+ }
+ read(fd, duty_num, 5);
+ ppwmtach_arg->dutycycle = atoi(duty_num);
+ printf("%s:dutycycle value %d to pwm %d\n",__FUNCTION__,ppwmtach_arg->dutycycle,ppwmtach_arg->pwmnumber);
+ (void)sigwrap_close(fd);
+
+ return retval;
+}
+int GET_TACH_SPEED (pwmtach_ioctl_data *ppwmtach_arg )
+{
+ int retval = 0;
+ int fd;
+ char data[6];
+
+ retval = pwmtach_directory_check();
+ if(retval != 0)
+ {printf("%s,error 0\n",__FUNCTION__);
+ return retval;
+ }
+ BUILD_TACH_NODE_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->tachnumber);
+ retval = access(DevNodeFileName,F_OK);
+ if(retval != 0)
+ {printf("%s,error 2,%s not exist\n",__FUNCTION__,DevNodeFileName);
+ return retval;
+ }
+
+ fd = sigwrap_open(DevNodeFileName, O_RDONLY);
+ if (fd < 0) {printf("%s,error 3\n",__FUNCTION__);
+ return fd;
+ }
+ memset(data, 0, 6);
+ read(fd, data, 6);
+ ppwmtach_arg->rpmvalue = atoi(data);
+ (void)sigwrap_close(fd);
+ printf("%s:rpm value %d\n",__FUNCTION__,ppwmtach_arg->rpmvalue);
+ return retval;
+}
+//mapping function of fan to tach
+//using direct mapping as default
+#define GET_TACH_NUMBER(FAN_NUMBER) FAN_NUMBER
+//mapping fan number to pwm number
+//using information in fan@number reg item, to look up the pwm index
+static int GET_PWM_NUMBER(pwmtach_ioctl_data *ppwmtach_arg)
+{
+ int retval = 0;
+ int fd;
+ int reg_val = 0;
+
+ retval = pwmtach_directory_check();
+ if(retval != 0)
+ {printf("%s,error 0\n",__FUNCTION__);
+ return retval;
+ }
+ BUILD_FAN_REG_NAME(DevNodeFileName,ppwmtach_arg->dev_id,ppwmtach_arg->fannumber);
+ retval = access(DevNodeFileName,F_OK);
+ if(retval != 0)
+ {printf("%s,error 2,%s not exist\n",__FUNCTION__,DevNodeFileName);
+ return retval;
+ }
+
+ fd = sigwrap_open(DevNodeFileName, O_RDONLY);
+ if (fd < 0) {printf("%s,error 3\n",__FUNCTION__);
+ return fd;
+ }
+ read(fd, ®_val, sizeof(int));
+ if(reg_val < 0)
+ {
+ retval = -1;
+ }else
+ {
+ retval = reg_val >> 24; //get the highest byte
+ printf("%s:fan %d, pwm %d, val 0x%X\n",__FUNCTION__,ppwmtach_arg->fannumber,retval,reg_val);
+ printf("%s\n",DevNodeFileName);
+ }
+ (void)sigwrap_close(fd);
+ // printf("%s:rpm value %d\n",__FUNCTION__,ppwmtach_arg->rpmvalue);
+ return retval;
+}
+static int pwmtach_action( pwmtach_ioctl_data* argp, int command )
+{
+ int retval = 0;
+ // printf("%s, Command 0x%X:Dev:%d,Pwm:0x%X,Fan:0x%x,Tach:0x%X\n",__FUNCTION__,command,argp->dev_id,argp->pwmnumber,argp->fannumber,argp->tachnumber);
+ switch(command)
+ {
+ case SET_DUTY_CYCLE_BY_PWM_CHANNEL:
+ retval = SET_PWM_DUTYCYCLE(argp);
+ break;
+ case SET_DUTY_CYCLE_VALUE_BY_PWM_CHANNEL:
+ case SET_DUTY_CYCLE:
+ retval = SET_PWM_DUTYCYCLE_VALUE(argp);
+ break;
+ case GET_TACH_VALUE_BY_TACH_CHANNEL:
+ retval = GET_TACH_SPEED(argp);
+ break;
+ case GET_TACH_VALUE: //used to get fan speed
+ argp->tachnumber = GET_TACH_NUMBER(argp->fannumber);
+ retval = GET_TACH_SPEED(argp);
+ break;
+ case GET_DUTY_CYCLE:
+ retval = GET_PWM_DUTYCYCLE(argp);
+ break;
+ case GET_FAN_RPM_RANGE:
+ argp->max_rpm = RPM_MAX;
+ argp->min_rpm = RPM_MIN;
+ break;
+ case INIT_PWMTACH: //assume that init complete
+ argp->pwmnumber = GET_PWM_NUMBER(argp);; //since we don't have the fan to pwm mapping, just using direct map for workarround.
+ argp->counterresvalue = COUNTERRES_DEF; //since driver don't support COUNTERRES, just using default value for workarround.
+ retval = GET_PWM_DUTYCYCLE(argp);
+ break;
+ case END_OF_FUNC_TABLE:
+ default:
+ printf("%s, Command 0x%X not support!\n",__FUNCTION__,command);
+ retval = -1;
+ }
+ return( retval );
+}
+
+int get_tach_speed ( unsigned int dev_id, unsigned char tach_number, unsigned int *rpm_value )
+{
+ pwmtach_ioctl_data pwmtach_arg;
+ int retval = 0;
+
+ pwmtach_arg.dev_id = dev_id;
+ pwmtach_arg.tachnumber = tach_number;
+ retval = pwmtach_action( &pwmtach_arg, GET_TACH_VALUE_BY_TACH_CHANNEL);
+ if(retval != -1)
+ *rpm_value = pwmtach_arg.rpmvalue;
+ return retval;
+}
+
+//Notice: dutycycle_percentage value should be between 1 to 99.
+int set_pwm_dutycycle ( unsigned int dev_id, unsigned char pwm_number, unsigned char dutycycle_percentage )
+{
+ pwmtach_ioctl_data pwmtach_arg;
+ int retval = 0;
+
+ pwmtach_arg.dev_id = dev_id;
+ pwmtach_arg.pwmnumber = pwm_number;
+ pwmtach_arg.dutycycle= dutycycle_percentage;
+ retval = pwmtach_action( &pwmtach_arg, SET_DUTY_CYCLE_BY_PWM_CHANNEL);
+
+ return retval;
+}
+
+//Notice: dutycycle_value is one byte (0-255)
+int set_pwm_dutycycle_value ( unsigned int dev_id, unsigned char pwm_number, unsigned char dutycycle_value )
+{
+ pwmtach_ioctl_data pwmtach_arg;
+ int retval = 0;
+
+ pwmtach_arg.dev_id = dev_id;
+ pwmtach_arg.pwmnumber = pwm_number;
+ pwmtach_arg.dutycycle= dutycycle_value;
+ retval = pwmtach_action( &pwmtach_arg, SET_DUTY_CYCLE_VALUE_BY_PWM_CHANNEL);
+
+ return retval;
+}
+
+int get_pwm_dutycycle ( unsigned int dev_id, unsigned char pwm_number, unsigned char *dutycycle_percentage )
+{
+ pwmtach_ioctl_data pwmtach_arg;
+ int retval = 0;
+
+ pwmtach_arg.dev_id = dev_id;
+ pwmtach_arg.pwmnumber = pwm_number;
+ retval = pwmtach_action(&pwmtach_arg, GET_DUTY_CYCLE);
+ if(retval != -1)
+ *dutycycle_percentage = pwmtach_arg.dutycycle;
+ return retval;
+
+}
+
+int set_fan_speed ( unsigned int dev_id, unsigned char fan_number, unsigned int rpm_value )
+{
+ int retval = 0;
+ unsigned int retries = 20;
+ unsigned char firsttime = 1;
+ unsigned char duty_cycle_increasing = 0;
+ unsigned char reached90percent = 0;
+ unsigned char reached5percent = 0;
+ unsigned long desiredrpm = rpm_value;
+ pwmtach_ioctl_data pwmtach_arg;
+ pwmtach_data_t* indata = (pwmtach_data_t*) &pwmtach_arg;
+
+ indata->dev_id = dev_id;
+ indata->fannumber = fan_number;
+ indata->rpmvalue = rpm_value;
+ indata->counterresvalue = 0;
+ indata->dutycycle = 0;
+ indata->prevdutycycle = 0;
+
+ retval = pwmtach_action( indata, GET_FAN_RPM_RANGE);
+ if ((rpm_value < indata->min_rpm) || (rpm_value > indata->max_rpm))
+ {
+ printf("Out of range Fan Speed value for fan.\n");
+ return -1;
+ }
+ retval = pwmtach_action ( indata, INIT_PWMTACH );
+
+ while (retries--)
+ {
+ /* Wait for 1 seconds */
+ select_sleep(0,1*1000*1000);
+ if ((retval = pwmtach_action( indata, GET_TACH_VALUE )) != 0)
+ {
+ indata->dutycycle = indata->prevdutycycle;
+ retval = pwmtach_action( indata, SET_DUTY_CYCLE);
+ return -1;
+ }
+ else
+ {
+ indata->prevdutycycle = indata->dutycycle;
+ if (indata->rpmvalue > (desiredrpm + 50))
+ {
+ if (indata->dutycycle <= ((indata->counterresvalue*10)/100))
+ {
+ if (reached5percent == 1)
+ {
+ printf("\nSpeed is set to minimum possible speed of %d RPM.\n",indata->rpmvalue);
+ break;
+ }
+ reached5percent = 1;
+ }
+ else
+ {
+ indata->dutycycle -= ((5 * indata->counterresvalue)/100);
+ }
+ }
+ else if (indata->rpmvalue < (desiredrpm - 50))
+ {
+ if (indata->dutycycle >= indata->counterresvalue)
+ {
+ if (reached90percent == 1)
+ {
+ printf("\nSpeed is set to maximum possible speed of %d RPM.\n",indata->rpmvalue);
+ break;
+ }
+ reached90percent = 1;
+ }
+ else
+ {
+ indata->dutycycle += ((5 * indata->counterresvalue)/100);
+ }
+ }
+ else
+ {
+ break;
+ }
+
+ retval = pwmtach_action (indata, SET_DUTY_CYCLE);
+ printf("After update: dutycycle=%d, rpmvalue=%d\n", indata->dutycycle, indata->rpmvalue);
+
+ if(indata->prevdutycycle < indata->dutycycle)
+ { /* Duty Cycle increasing */
+ if ((firsttime == 0) && (duty_cycle_increasing == 0))
+ {
+ indata->dutycycle = indata->prevdutycycle;
+ retval = pwmtach_action( indata, SET_DUTY_CYCLE);
+ printf("\n");
+ return 0;
+ }
+ duty_cycle_increasing = 1;
+ }
+ else
+ { /* Duty Cycle decreasing */
+ if ((firsttime == 0) && (duty_cycle_increasing == 1))
+ {
+ indata->dutycycle = indata->prevdutycycle;
+ retval = pwmtach_action( indata, SET_DUTY_CYCLE);
+ printf("\n");
+ return 0;
+ }
+ duty_cycle_increasing = 0;
+ }
+ if (firsttime == 1)
+ firsttime = 0;
+ }
+ printf("retry %d : dt=%d, ps=%d, cr=%d\n", retries, indata->dutycycle, indata->prescalervalue, indata->counterresvalue);
+ }
+ return 0;
+
+}
+
+int get_fan_speed ( unsigned int dev_id, unsigned char fan_number, unsigned int *rpm_value )
+{
+ pwmtach_ioctl_data pwmtach_arg;
+ int retval = 0;
+
+ pwmtach_arg.dev_id = dev_id;
+ pwmtach_arg.fannumber = fan_number;
+ retval = pwmtach_action( &pwmtach_arg, GET_TACH_VALUE );
+ if(retval != -1)
+ *rpm_value = pwmtach_arg.rpmvalue;
+ return retval;
+}
diff --git a/hongweiz/pwmtachtool/src/pwmtach_ioctl.h b/hongweiz/pwmtachtool/src/pwmtach_ioctl.h
new file mode 100644
index 0000000..5d2fabb
--- /dev/null
+++ b/hongweiz/pwmtachtool/src/pwmtach_ioctl.h
@@ -0,0 +1,76 @@
+/*
+ * Simple interface library for fan control operations
+ * This file provides interface functions to support pwmtachtool.
+ * Copyright (C) <2019> <American Megatrends International LLC>
+ *
+ */
+
+#ifndef __PWMTACH_IOCTL_H__
+#define __PWMTACH_IOCTL_H__
+
+
+typedef struct
+{
+ unsigned char id;
+ unsigned int value;
+}__attribute__((packed)) pwmtach_property_t;
+
+typedef struct
+{
+ char device_name[16];
+ unsigned int dev_id;
+ unsigned int num_fans;
+ unsigned char fannumber;
+ unsigned int rpmvalue;
+ unsigned int min_rpm;
+ unsigned int max_rpm;
+ unsigned char prevdutycycle;
+ unsigned char dutycycle;
+ unsigned int prescalervalue;
+ unsigned int counterresvalue;
+ unsigned int tachnumber;
+ unsigned int pwmnumber;
+ unsigned char status;
+ pwmtach_property_t property;
+ void* fanproperty_dataptr;
+ void* fanmap_dataptr;
+} __attribute__((packed)) pwmtach_data_t;
+
+#define ENABLE_PWM_CHANNEL _IOW('P', 0, int)
+#define DISABLE_PWM_CHANNEL _IOW('P', 1, int)
+#define ENABLE_TACH_CHANNEL _IOW('P', 2, int)
+#define DISABLE_TACH_CHANNEL _IOW('P', 3, int)
+#define SET_DUTY_CYCLE_BY_PWM_CHANNEL _IOW('P', 4, int)
+#define SET_DUTY_CYCLE_VALUE_BY_PWM_CHANNEL _IOW('P', 5, int)
+#define GET_TACH_VALUE_BY_TACH_CHANNEL _IOR('P', 6, int)
+#define ENABLE_PWM _IOW('P', 7, int)
+#define ENABLE_ALL_PWM _IOW('P', 8, int)
+#define ENABLE_TACH _IOW('P', 9, int)
+#define ENABLE_ALL_TACH _IOW('P', 10, int)
+#define DISABLE_PWM _IOW('P', 11, int)
+#define DISABLE_ALL_PWM _IOW('P', 12, int)
+#define DISABLE_TACH _IOW('P', 13, int)
+#define DISABLE_ALL_TACH _IOW('P', 14, int)
+#define GET_TACH_STATUS _IOR('P', 15, int)
+#define GET_PWM_STATUS _IOR('P', 16, int)
+#define GET_PWM_CHANNEL_STATUS _IOR('P', 17, int)
+#define GET_TACH_VALUE _IOW('P', 18, int)
+#define SET_DUTY_CYCLE _IOW('P', 19, int)
+#define INIT_PWMTACH _IOW('P', 20, int)
+#define CONFIGURE_FANMAP_TABLE _IOW('P', 21, int)
+#define CONFIGURE_FANPROPERTY_TABLE _IOW('P', 22, int)
+#define SHOW_FANMAP_TABLE _IOR('P', 23, int)
+#define SHOW_FANPROPERTY_TABLE _IOR('P', 24, int)
+#define GET_FAN_RPM_RANGE _IOR('P', 25, int)
+#define GET_DUTY_CYCLE _IOR('P', 26, int)
+#define SET_TACH_PROPERTY _IOW('P', 27, int)
+#define GET_TACH_PROPERTY _IOR('P', 28, int)
+#define SET_PWM_PROPERTY _IOW('P', 29, int)
+#define GET_PWM_PROPERTY _IOR('P', 30, int)
+#define CLEAR_TACH_ERROR _IOW('P', 31, int)
+#define CLEAR_PWM_ERRORS _IOW('P', 32, int)
+#define END_OF_FUNC_TABLE _IOW('P', 33, int)
+
+typedef pwmtach_data_t pwmtach_ioctl_data;
+
+#endif // __PWMTACH_IOCTL_H__
diff --git a/hongweiz/pwmtachtool/src/pwmtachtool.c b/hongweiz/pwmtachtool/src/pwmtachtool.c
new file mode 100644
index 0000000..693f498
--- /dev/null
+++ b/hongweiz/pwmtachtool/src/pwmtachtool.c
@@ -0,0 +1,223 @@
+
+/*
+* Pwmtachtool Application
+* This application provides functions to get/set fan speed / PWM dutycycle.
+* Copyright (C) <2019> <American Megatrends International LLC>
+*
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <stdint.h>
+#include <limits.h>
+#include "libpwmtach.h"
+
+#define VERSION_STR "1.0"
+typedef enum {
+ SET_FAN_SPEED,
+ GET_FAN_SPEED,
+ SET_PWM_DUTYCYCLE,
+ SET_PWM_DUTYCYCLE_VALUE,
+ GET_PWM_DUTYCYCLE,
+ END_OF_FUNCLIST
+}ePwmTachactions;
+
+
+ePwmTachactions action = END_OF_FUNCLIST;
+
+static int verbose = 0;
+
+static void ShowUsage ( void )
+ /*@globals fileSystem@*/
+ /*@modifies fileSystem@*/
+{
+ printf ("PWMTACH Test Tool (Version %s)\n",VERSION_STR);
+ printf ("Copyright (c) 2009-2015 American Megatrends Inc.\n");
+ printf( "Usage : pwmtachtool <device_id> <command-option> <fannum>\n" );
+ printf( "\t--set-fan-speed: Set Fan's speed. Takes the RPM value as the last argument\n" );
+ printf("\t\tparameters: <Fan_Number> <Fan_Speed>\n");
+ printf( "\t--set-pwm-dutycycle: Set Fan's dutycycle. dutycycle_percentage value should be between 1 to 100\n" );
+ printf( "\t--set-pwm-dutycycle-value: Set Fan's dutycycle. dutycycle_value should be between 0 to 255\n" );
+ printf("\t\tparameters: <pwm_number> <dutycycle value>\n");
+ printf( "\t--get-pwm-dutycycle: Get Fan's dutycycle\n");
+ printf( "\t--get-fan-speed: Get Fan's speed\n" );
+ printf( "\t--verbose: Enable Debug messages\n" );
+ printf( "\n" );
+}
+
+static void Verbose ( char * msg )
+{
+ if (verbose ) printf ( "%s\n" , msg );
+}
+
+static int process_arguments( int argc, char **argv,
+ unsigned char* fan_num,unsigned int* rpm_value,
+ unsigned int* dev_id )
+{
+ int i = 1;
+
+ if (argc < 3)
+ {
+ printf("need Device Name and Command to process request\n");
+ return -1;
+ }
+
+ *dev_id = (unsigned char)strtol( argv[ i++ ], NULL, 10);
+
+ if( strcmp( argv[ i ], "--set-fan-speed" ) == 0 )
+ {
+ if (argc < 5)
+ {
+ printf("need Fan Number and RPM value to process request\n");
+ return -1;
+ }
+ *fan_num = (unsigned char)strtol( argv[ ++i ], NULL, 10);
+ *rpm_value = (unsigned int)strtol( argv[ ++i ], NULL, 10);
+ action = SET_FAN_SPEED;
+ }
+ else if( strcmp( argv[ i ], "--set-pwm-dutycycle" ) == 0 )
+ {
+ if (argc < 5)
+ {
+ printf("need Fan Number and Dutycycle value to process request\n");
+ return -1;
+ }
+ *fan_num = (unsigned char)strtol( argv[ ++i ], NULL, 10);
+ *rpm_value = (unsigned int)strtol( argv[ ++i ], NULL, 10);
+ action = SET_PWM_DUTYCYCLE;
+ }
+ else if( strcmp( argv[ i ], "--set-pwm-dutycycle-value" ) == 0 )
+ {
+ if (argc < 5)
+ {
+ printf("need Fan Number and Dutycycle value to process request\n");
+ return -1;
+ }
+ *fan_num = (unsigned char)strtol( argv[ ++i ], NULL, 10);
+ *rpm_value = (unsigned int)strtol( argv[ ++i ], NULL, 10);
+ action = SET_PWM_DUTYCYCLE_VALUE;
+ }
+ else if( strcmp( argv[i], "--get-pwm-dutycycle" ) == 0)
+ {
+ if (argc < 4)
+ {
+ printf("need PWM Number to process request\n");
+ return -1;
+ }
+ *fan_num = (unsigned char)strtol( argv[ ++i ], NULL, 10);
+ action = GET_PWM_DUTYCYCLE;
+ }
+
+ else if( strcmp( argv[ i ], "--get-fan-speed" ) == 0 )
+ {
+ if (argc < 4)
+ {
+ printf("need more parameters to process request\n");
+ return -1;
+ }
+ *fan_num = (unsigned char)strtol( argv[ ++i ], NULL, 10);
+ action = GET_FAN_SPEED;
+ }
+
+ else if( strcmp( argv[ i ], "--verbose" ) == 0 )
+ verbose = 1;
+
+ return 0;
+}
+
+int main ( int argc , char* argv [] )
+{
+ unsigned char fannum = 0, property_id = 0;
+ unsigned int rpmvalue = 0;
+ unsigned char dutycycle = 0;
+ int Value = 0;
+ int ret = 0;
+ unsigned int dev_id = 0;
+
+ if (argc < 2)
+ {
+ ShowUsage();
+ return 0;
+ }
+ ret = process_arguments( argc , argv , &fannum, &rpmvalue, &dev_id );
+ if (ret != 0)
+ {
+ return -1;
+ }
+
+ if (END_OF_FUNCLIST == action)
+ {
+ ShowUsage ();
+ return 0;
+ }
+
+ switch ( action )
+ {
+
+ case SET_FAN_SPEED:
+ Verbose ("Inside Set Fan Speed \n");
+ Value = set_fan_speed (dev_id, fannum, rpmvalue);
+ if ( -1 == Value )
+ {
+ printf ( "Set Fan Speed Failed \n");
+ return -1;
+ }
+ printf ( "Fan Speed set Successfully\n");
+ break;
+ case GET_FAN_SPEED:
+ Verbose ("Inside Get Fan Speed \n");
+ Value = get_fan_speed (dev_id, fannum, &rpmvalue);
+ if ( -1 == Value)
+ {
+ printf ( "Get Fan Speed Failed \n");
+ return -1;
+ }
+ printf("Fan %d speed is %d \n", fannum, rpmvalue);
+ break;
+
+ case SET_PWM_DUTYCYCLE:
+ Verbose ("Inside Set PWM Dutycycle \n");
+ Value = set_pwm_dutycycle (dev_id, fannum, rpmvalue);
+ if ( -1 == Value )
+ {
+ printf ( "Set PWM Dutycycle Failed \n");
+ return -1;
+ }
+ printf ( "Fan PWM set dutycycle Successfully\n");
+ break;
+ case SET_PWM_DUTYCYCLE_VALUE:
+ Verbose ("Inside Set PWM Dutycycle Value\n");
+ Value = set_pwm_dutycycle_value (dev_id, fannum, rpmvalue);
+ if ( -1 == Value )
+ {
+ printf ( "Set PWM Dutycycle Value Failed \n");
+ return -1;
+ }
+ printf ( "Fan PWM set dutycycle value Successfully\n");
+ break;
+ case GET_PWM_DUTYCYCLE:
+ Verbose ("Inside Get PWM Dutycycle \n");
+ Value = get_pwm_dutycycle (dev_id, fannum, &dutycycle);
+ if ( -1 == Value )
+ {
+ printf ( "Set PWM Dutycycle Failed \n");
+ return -1;
+ }
+ printf ( "PWM %d Dutycycle is %d\n",fannum, dutycycle);
+ break;
+
+ default:
+ printf("Invalid PWMTACH Function Call\n");
+ break;
+ }
+ return 0;
+}