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, &reg_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;
+}