| #include "lynq-irq.h" | 
 | #include "mbtk_type.h" | 
 | #include "mbtk_info_api.h" | 
 | #include "mbtk_log.h" | 
 |  | 
 |  | 
 | #include <ctype.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <pthread.h> | 
 |  | 
 | #define _GNU_SOURCE | 
 |  | 
 | #include <errno.h> | 
 | #include <fcntl.h> | 
 | #include <limits.h> | 
 | #include <poll.h> | 
 |  | 
 | #include <sys/ioctl.h> | 
 | #include <sys/stat.h> | 
 | #include <sys/sysmacros.h> | 
 | #include <sys/types.h> | 
 | #include <unistd.h> | 
 |  | 
 |  | 
 | struct libirq_info { | 
 | 	unsigned int 	line; | 
 | 	unsigned int 	type; | 
 | 	int 			wake; | 
 | 	int				fd; | 
 | 	irq_handler 	handler; | 
 | 	unsigned int 	used; | 
 | }; | 
 |  | 
 | struct libirq_context { | 
 | 	unsigned int		inited; | 
 | 	int					fd; | 
 | 	pthread_t 			th; | 
 | 	struct libirq_info	info[SC_LIBIRQ_MAX]; | 
 | }; | 
 | static struct libirq_context irq_ctx = {0}; | 
 |  | 
 | #define irq_init()			irq_ctx.inited | 
 | #define line_used(l)		irq_ctx.info[l].used | 
 | #define irq_fd(l)			irq_ctx.info[l].fd | 
 | #define libirq_fd()			irq_ctx.fd | 
 |  | 
 | static void *libirq_loop(void *arg) | 
 | { | 
 | 	unsigned int int_status = 0; | 
 | 	int fd = libirq_fd(); | 
 | 	int ret = 0; | 
 | 	int i; | 
 |  | 
 | 	while(1) { | 
 | 	    ret = ioctl(fd, SC_IRQ_GET_STATUS, &int_status); | 
 | 		if (ret < 0) { | 
 | 			LOGE("libirq_loop get status failed:%d", ret); | 
 | 		} else { | 
 | /*			printf("libirq_loop get status :0x%x\n", int_status); */ | 
 | 		} | 
 |  | 
 | 		for (i=0; i<SC_LIBIRQ_MAX; i++) { | 
 | 			if ((int_status & (1<<i)) && line_used(i) && irq_ctx.info[i].handler) { | 
 | 				irq_ctx.info[i].handler(); | 
 |  | 
 | 			    ret = ioctl(fd, SC_IRQ_CLEAR_STATUS, 0); | 
 | 				if (ret < 0) { | 
 | 					LOGE("libirq_loop clear status failed:%d", ret); | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static int libirq_init_thread(void) | 
 | { | 
 | 	int ret = 0; | 
 | 	pthread_attr_t attribute; | 
 |  | 
 |     pthread_attr_init(&attribute); | 
 |     pthread_attr_setstacksize(&attribute, 32*1024); | 
 |  | 
 | 	ret = pthread_create(&irq_ctx.th, &attribute, libirq_loop, NULL); | 
 | 	if(ret) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  Add a handler for an interrupt line. | 
 |  * | 
 |  *  line      		:  The interrupt line | 
 |  *  handler         :  Function to be called when the IRQ occurs. | 
 |  *  trig_type		:  rising edge or fallling edge | 
 |  * | 
 |  *  return 0 if succeed, others failed | 
 |  */ | 
 | int sc_irq_install(unsigned int line, irq_handler handler, int trig_type) | 
 | { | 
 | 	int fd; | 
 | 	struct libirq_info *info; | 
 | 	char *usr_name; | 
 | 	int ret = 0; | 
 |  | 
 | 	if ((line >= SC_LIBIRQ_MAX) || (handler == NULL) || (trig_type >= SC_LIBIRQ_TYPE_MAX)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (line_used(line)) | 
 | 		return -EEXIST; | 
 |  | 
 | 	ret = asprintf(&usr_name, "%s%d", SC_IRQ_DEV, line); | 
 | 	if (ret < 0) { | 
 | 		return -ENOMEM; | 
 | 	} | 
 |  | 
 | 	fd = open(usr_name, O_RDWR); | 
 | 	if(fd < 0) { | 
 | 		free(usr_name); | 
 | 		return -ENODEV; | 
 | 	} | 
 | 	irq_fd(line) = fd; | 
 | 	free(usr_name); | 
 | 	info = &irq_ctx.info[line]; | 
 | 	info->line = line; | 
 | 	info->type = trig_type; | 
 | 	info->handler = handler; | 
 |  | 
 |     if (ioctl(fd, SC_IRQ_INSTALL, trig_type) < 0) { | 
 |         return -EPERM; | 
 |     } | 
 |  | 
 | 	line_used(line) = 1; | 
 |  | 
 | 	if (!irq_init()) { | 
 | 		ret = libirq_init_thread(); | 
 | 		if (ret) { | 
 | 			LOGE("libirq_init_thread, err:%d", ret); | 
 | 			return ret; | 
 | 		} | 
 |  | 
 | 		libirq_fd() = fd; | 
 | 		irq_init() = 1; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  free an interrupt allocated with sc_irq_install. | 
 |  * | 
 |  *  line      		:  The interrupt line | 
 |  * | 
 |  *  return 0 if succeed, others failed | 
 |  */ | 
 | int sc_irq_uninstall(unsigned int line) | 
 | { | 
 | 	int fd; | 
 |  | 
 | 	if (line >= SC_LIBIRQ_MAX) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (!line_used(line)) | 
 | 		return -ENODEV; | 
 |  | 
 |     if (ioctl(irq_fd(line), SC_IRQ_UNINSTALL, 0) < 0) { | 
 |         return -EPERM; | 
 |     } | 
 |  | 
 | 	fd = libirq_fd(); | 
 | 	if (fd) | 
 | 		close(fd); | 
 |  | 
 | 	line_used(line) = 0; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  set the irq trigger type for an irq. | 
 |  * | 
 |  *  line      		:  The interrupt line | 
 |  *  trig_type		:  edge or level type | 
 |  * | 
 |  *  return 0 if succeed, others failed | 
 |  */ | 
 | int sc_irq_set_type(unsigned int line, int trig_type) | 
 | { | 
 | 	struct libirq_info *info; | 
 |  | 
 | 	if ((line >= SC_LIBIRQ_MAX) || (trig_type >= SC_LIBIRQ_TYPE_MAX)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (!line_used(line)) | 
 | 		return -EEXIST; | 
 |  | 
 | 	info = &irq_ctx.info[line]; | 
 | 	if (info->type != trig_type) { | 
 | 	    if (ioctl(irq_fd(line), SC_IRQ_SET_TYPE, trig_type) < 0) { | 
 | 			return -EPERM; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	info->type = trig_type; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  get the irq trigger type for an irq. | 
 |  * | 
 |  *  line      		:  The interrupt line | 
 |  *  trig_type		:  edge or level type | 
 |  * | 
 |  *  return 0 if succeed, others failed | 
 |  */ | 
 | int sc_irq_get_type(unsigned int line, int *trig_type) | 
 | { | 
 | 	struct libirq_info *info; | 
 |  | 
 | 	if ((line >= SC_LIBIRQ_MAX) || !trig_type) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (!line_used(line)) | 
 | 		return -EEXIST; | 
 |  | 
 | 	info = &irq_ctx.info[line]; | 
 | 	*trig_type = info->type; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  control irq power management wakeup. | 
 |  * | 
 |  *  line      		:  The interrupt line | 
 |  *  en				:  enable/disable power management wakeup | 
 |  * | 
 |  *  return 0 if succeed, others failed | 
 |  */ | 
 | int sc_irq_set_wake(unsigned int line, int en) | 
 | { | 
 | 	struct libirq_info *info; | 
 |  | 
 | 	if (line >= SC_LIBIRQ_MAX) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (!line_used(line)) | 
 | 		return -EEXIST; | 
 |  | 
 | 	info = &irq_ctx.info[line]; | 
 | 	if (info->wake != en) { | 
 | 	    if (ioctl(irq_fd(line), SC_IRQ_SET_WAKE, en) < 0) { | 
 | 			return -EPERM; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	info->wake = en; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  get the irq awake status for an irq. | 
 |  * | 
 |  *  line      		:  The interrupt line | 
 |  *  en				:  enable/disable power management wakeup | 
 |  * | 
 |  *  return 0 if succeed, others failed | 
 |  */ | 
 | int sc_irq_get_wake(unsigned int line, int *en) | 
 | { | 
 | 	struct libirq_info *info; | 
 | 	unsigned int wake; | 
 |  | 
 | 	if (line >= SC_LIBIRQ_MAX) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (!line_used(line)) | 
 | 		return -EEXIST; | 
 | 	 | 
 | 	if (ioctl(irq_fd(line), SC_IRQ_GET_WAKE, &wake) < 0) { | 
 | 		return -EPERM; | 
 | 	} | 
 |  | 
 | 	info = &irq_ctx.info[line]; | 
 | 	info->wake = wake; | 
 | 	*en = wake; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 |  | 
 |  | 
 | /***************************************** | 
 | * @brief:lynq_irq_install | 
 | * @param count [IN]:2 | 
 | * @param sum [OUT]:NA | 
 | * @return :success 0, failed other | 
 | * @todo:NA | 
 | * @see:NA | 
 | * @warning:NA | 
 | ******************************************/ | 
 |  | 
 | int lynq_irq_install(int line, irq_handler irq_test_handler, trig_type_e trig_type) | 
 | { | 
 |     int ret; | 
 |     if(trig_type < 0) | 
 |     { | 
 |         return -1; | 
 |     } | 
 | #if defined(MBTK_PROJECT_T108) | 
 |  | 
 |     if (trig_type != 0 && trig_type != 1) | 
 |     { | 
 |         LOGE("lynq_irq_install error trig_type:%d", trig_type); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     line = line-117; | 
 |  | 
 |     if (line < 0) | 
 |     { | 
 |         LOGE("lynq_irq_install error line:%d", line); | 
 |         return -1; | 
 |     } | 
 |  | 
 | #endif | 
 |  | 
 |     ret = sc_irq_install(line, irq_test_handler, trig_type); | 
 |     if (ret != 0) | 
 |     { | 
 |         LOGE("do_install_irq failed, ret:%d", ret); | 
 |         return ret; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 | /***************************************** | 
 | * @brief:lynq_irq_uninstall | 
 | * @param count [IN]:2 | 
 | * @param sum [OUT]:NA | 
 | * @return :success 0, failed other | 
 | * @todo:NA | 
 | * @see:NA | 
 | * @warning:NA | 
 | ******************************************/ | 
 |  | 
 | int lynq_irq_uninstall(int line) | 
 | { | 
 | #if defined(MBTK_PROJECT_T108) | 
 |         line = line-117; | 
 | #endif | 
 |  | 
 |     int ret; | 
 |     ret = sc_irq_uninstall(line); | 
 |     if (ret != 0) | 
 |     { | 
 |         LOGE("unistall failed, ret:%d", ret); | 
 |         return ret; | 
 |     } | 
 |     LOGI("uninstall irq(%d) ok", line); | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 | /***************************************** | 
 | * @brief:lynq_irq_set_type | 
 | * @param count [IN]:2 | 
 | * @param sum [OUT]:NA | 
 | * @return :success 0, failed other | 
 | * @todo:NA | 
 | * @see:NA | 
 | * @warning:NA | 
 | ******************************************/ | 
 | int lynq_irq_set_type(int line, int trig_type) | 
 | { | 
 | #if defined(MBTK_PROJECT_T108) | 
 |         line = line-117; | 
 |  | 
 |     if (trig_type != 0 && trig_type != 1) | 
 |     { | 
 |         LOGE("lynq_irq_set_type error trig_type:%d", trig_type); | 
 |         return -1; | 
 |     } | 
 | #endif | 
 |  | 
 |     int ret; | 
 |     if(trig_type < 0) | 
 |     { | 
 |         return -1; | 
 |     } | 
 |     ret = sc_irq_set_type(line, trig_type); | 
 |     if (ret != 0) | 
 |     { | 
 |         LOGE("set_type failed, ret:%d", ret); | 
 |         return ret; | 
 |     } | 
 |     return 0; | 
 |  | 
 | } | 
 |  | 
 | /***************************************** | 
 | * @brief:lynq_irq_get_type | 
 | * @param count [IN]:1 | 
 | * @param sum [OUT]:NA | 
 | * @return :success >= 0, failed other | 
 | * @todo:NA | 
 | * @see:NA | 
 | * @warning:NA | 
 | ******************************************/ | 
 | int lynq_irq_get_type(int line) | 
 | { | 
 | #if defined(MBTK_PROJECT_T108) | 
 |         line = line-117; | 
 | #endif | 
 |  | 
 |     int ret; | 
 |     int trig_type; | 
 |     ret = sc_irq_get_type(line, &trig_type); | 
 |     if (ret != 0) | 
 |     { | 
 |         LOGE("get_type failed, ret:%d", ret); | 
 |         return ret; | 
 |     } | 
 |     LOGI("get_type readback(%d)", trig_type); | 
 |     return trig_type; | 
 | } | 
 |  | 
 |  | 
 | /***************************************** | 
 | * @brief:lynq_irq_set_wake | 
 | * @param count [IN]:2 | 
 | * @param sum [OUT]:NA | 
 | * @return :success 0, failed other | 
 | * @todo:NA | 
 | * @see:NA | 
 | * @warning:NA | 
 | ******************************************/ | 
 | int lynq_irq_set_wake(int line, int en) | 
 | { | 
 | #if defined(MBTK_PROJECT_T108) | 
 |         line = line-117; | 
 | #endif | 
 |  | 
 |     int ret; | 
 |     if((en != 0) && (en != 1)) | 
 |     { | 
 |         LOGE("wake_state is not 0 or 1"); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     ret = sc_irq_set_wake(line, en); | 
 |     if (ret != 0) | 
 |     { | 
 |         LOGE("set_wake failed, ret:%d", ret); | 
 |         return ret; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | /***************************************** | 
 | * @brief:lynq_irq_get_wake | 
 | * @param count [IN]:1 | 
 | * @param sum [OUT]:NA | 
 | * @return :success >= 0, failed other | 
 | * @todo:NA | 
 | * @see:NA | 
 | * @warning:NA | 
 | ******************************************/ | 
 | int lynq_irq_get_wake(int line) | 
 | { | 
 | #if defined(MBTK_PROJECT_T108) | 
 |         line = line-117; | 
 | #endif | 
 |  | 
 |     int ret; | 
 |     int en; | 
 |     ret = sc_irq_get_wake(line, &en); | 
 |     if (ret != 0) | 
 |     { | 
 |         LOGE("get_wake failed, ret:%d", ret); | 
 |         return ret; | 
 |     } | 
 |  | 
 |     LOGI("get_wake readback(%d)", en); | 
 |     return en; | 
 | } | 
 |  | 
 |  | 
 |  |