#ifndef KCT_H_
#  define KCT_H_

#  include <linux/netlink.h>

/*
 * warning: structures and constants in this header must match the
 * ones in libc/kernel/common/linux/kct.h, so that information can
 * be exchange between kernel and userspace throught netlink socket.
 */
/* flags to optionally filter events on android property activation */
#define	EV_FLAGS_PRIORITY_LOW	(1<<0)

#  ifndef MAX_SB_N
#    define MAX_SB_N 32
#  endif

#  ifndef MAX_EV_N
#    define MAX_EV_N 32
#  endif

#  define NETLINK_CRASHTOOL 27
#  define ATTCHMT_ALIGN 4U

/* Type of events supported by crashtool */
enum ct_ev_type {
	CT_EV_STAT,
	CT_EV_INFO,
	CT_EV_ERROR,
	CT_EV_CRASH,
	CT_EV_LAST
};

enum ct_attchmt_type {
	CT_ATTCHMT_DATA0,
	CT_ATTCHMT_DATA1,
	CT_ATTCHMT_DATA2,
	CT_ATTCHMT_DATA3,
	CT_ATTCHMT_DATA4,
	CT_ATTCHMT_DATA5,
	/* Always add new types after DATA5 */
	CT_ATTCHMT_BINARY,
	CT_ATTCHMT_FILELIST
};

struct ct_attchmt {
	__u32 size; /* sizeof(data) */
	enum ct_attchmt_type type;
	char data[];
} __aligned(4);

struct ct_event {
	__u64 timestamp;
	char submitter_name[MAX_SB_N];
	char ev_name[MAX_EV_N];
	enum ct_ev_type type;
	__u32 attchmt_size; /* sizeof(all_attachments inc. padding) */
	__u32 flags;
	struct ct_attchmt attachments[];
} __aligned(4);

enum kct_nlmsg_type {
	/* kernel -> userland */
	KCT_EVENT,
	/* userland -> kernel */
	KCT_SET_PID = 4200,
};

struct kct_packet {
	struct nlmsghdr nlh;
	struct ct_event event;
};

#  define ATTCHMT_ALIGNMENT	4

#  ifndef KCT_ALIGN
#    define __KCT_ALIGN_MASK(x, mask)    (((x) + (mask)) & ~(mask))
#    define __KCT_ALIGN(x, a)            __KCT_ALIGN_MASK(x, (typeof(x))(a) - 1)
#    define KCT_ALIGN(x, a)		     __KCT_ALIGN((x), (a))
#  endif /* !KCT_ALIGN */

#  define foreach_attchmt(Event, Attchmt)				\
	if ((Event)->attchmt_size)					\
		for ((Attchmt) = (Event)->attachments;			\
		     (Attchmt) < (typeof(Attchmt))(((char *)		\
				  (Event)->attachments) +               \
			(Event)->attchmt_size);                         \
	(Attchmt) = (typeof(Attchmt))KCT_ALIGN(((size_t)(Attchmt)) \
						     + sizeof(*(Attchmt)) + \
			      (Attchmt)->size, ATTCHMT_ALIGNMENT))

/*
 * User should use the macros below rather than those extern functions
 * directly. Laters' declaration are only to set them __weak so
 * that the macros works fine.
 */
/* Raw API (deprecated) */
extern struct ct_event *kct_alloc_event(const char *submitter_name,
					const char *ev_name,
					enum ct_ev_type ev_type,
					gfp_t flags, uint eflags) __weak;
extern int kct_add_attchmt(struct ct_event **ev,
			   enum ct_attchmt_type at_type,
			   unsigned int size,
			   char *data, gfp_t flags)  __weak;
extern void kct_free_event(struct ct_event *ev) __weak;
extern int kct_log_event(struct ct_event *ev, gfp_t flags) __weak;

/* API */
#define MKFN(fn, ...) MKFN_N(fn, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)(__VA_ARGS__)
#define MKFN_N(fn, n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n, ...) fn##n
#define kct_log(...) MKFN(__kct_log_, ##__VA_ARGS__)

#define __kct_log_4(Type, Submitter_name, Ev_name, flags) \
	do {  if (kct_alloc_event) {	\
		struct ct_event *__ev =	\
			kct_alloc_event(Submitter_name, Ev_name, Type, \
				GFP_ATOMIC, flags); \
		if (__ev) { \
			kct_log_event(__ev, GFP_ATOMIC); \
		} \
	} } while (0)

#define __kct_log_5(Type, Submitter_name, Ev_name, flags, Data0) \
	do {  if (kct_alloc_event) {	\
		struct ct_event *__ev =	\
			kct_alloc_event(Submitter_name, Ev_name, Type, \
				GFP_ATOMIC, flags); \
		if (__ev) { \
			if (Data0) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA0, \
					strlen(Data0) + 1, Data0, GFP_ATOMIC); \
			kct_log_event(__ev, GFP_ATOMIC); \
		} \
	} } while (0)

#define __kct_log_6(Type, Submitter_name, Ev_name, flags, Data0, Data1) \
	do {  if (kct_alloc_event) {	\
		struct ct_event *__ev =	\
			kct_alloc_event(Submitter_name, Ev_name, Type, \
				GFP_ATOMIC, flags); \
		if (__ev) { \
			if (Data0) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA0, \
					strlen(Data0) + 1, Data0, GFP_ATOMIC); \
			if (Data1) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA1, \
					strlen(Data1) + 1, Data1, GFP_ATOMIC); \
			kct_log_event(__ev, GFP_ATOMIC); \
		} \
	} } while (0)

#define __kct_log_7(Type, Submitter_name, Ev_name, flags, Data0, Data1, Data2) \
	do {  if (kct_alloc_event) {	\
		struct ct_event *__ev =	\
			kct_alloc_event(Submitter_name, Ev_name, Type, \
				GFP_ATOMIC, flags); \
		if (__ev) { \
			if (Data0) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA0, \
					strlen(Data0) + 1, Data0, GFP_ATOMIC); \
			if (Data1) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA1, \
					strlen(Data1) + 1, Data1, GFP_ATOMIC); \
			if (Data2) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA2, \
					strlen(Data2) + 1, Data2, GFP_ATOMIC); \
			kct_log_event(__ev, GFP_ATOMIC); \
		} \
	} } while (0)

#define __kct_log_8(Type, Submitter_name, Ev_name, flags, Data0, Data1, Data2, \
					Data3) \
	do {  if (kct_alloc_event) {	\
		struct ct_event *__ev =	\
			kct_alloc_event(Submitter_name, Ev_name, Type, \
				GFP_ATOMIC, flags); \
		if (__ev) { \
			if (Data0) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA0, \
					strlen(Data0) + 1, Data0, GFP_ATOMIC); \
			if (Data1) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA1, \
					strlen(Data1) + 1, Data1, GFP_ATOMIC); \
			if (Data2) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA2, \
					strlen(Data2) + 1, Data2, GFP_ATOMIC); \
			if (Data3) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA3, \
					strlen(Data3) + 1, Data3, GFP_ATOMIC); \
			kct_log_event(__ev, GFP_ATOMIC); \
		} \
	} } while (0)

	#define __kct_log_9(Type, Submitter_name, Ev_name, flags, Data0, Data1, Data2, \
					 Data3, Data4) \
	do {  if (kct_alloc_event) {	\
		struct ct_event *__ev =	\
			kct_alloc_event(Submitter_name, Ev_name, Type, \
				GFP_ATOMIC, flags); \
		if (__ev) { \
			if (Data0) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA0, \
					strlen(Data0) + 1, Data0, GFP_ATOMIC); \
			if (Data1) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA1, \
					strlen(Data1) + 1, Data1, GFP_ATOMIC); \
			if (Data2) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA2, \
					strlen(Data2) + 1, Data2, GFP_ATOMIC); \
			if (Data3) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA3, \
					strlen(Data3) + 1, Data3, GFP_ATOMIC); \
			if (Data4) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA4, \
					strlen(Data4) + 1, Data4, GFP_ATOMIC); \
			kct_log_event(__ev, GFP_ATOMIC); \
		} \
	} } while (0)

	#define __kct_log_10(Type, Submitter_name, Ev_name, flags, Data0, Data1, Data2, \
					 Data3, Data4, Data5) \
	do {  if (kct_alloc_event) {	\
		struct ct_event *__ev =	\
			kct_alloc_event(Submitter_name, Ev_name, Type, \
				GFP_ATOMIC, flags); \
		if (__ev) { \
			if (Data0) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA0, \
					strlen(Data0) + 1, Data0, GFP_ATOMIC); \
			if (Data1) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA1, \
					strlen(Data1) + 1, Data1, GFP_ATOMIC); \
			if (Data2) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA2, \
					strlen(Data2) + 1, Data2, GFP_ATOMIC); \
			if (Data3) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA3, \
					strlen(Data3) + 1, Data3, GFP_ATOMIC); \
			if (Data4) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA4, \
					strlen(Data4) + 1, Data4, GFP_ATOMIC); \
			if (Data5) \
				kct_add_attchmt(&__ev, CT_ATTCHMT_DATA5, \
					strlen(Data5) + 1, Data5, GFP_ATOMIC); \
			kct_log_event(__ev, GFP_ATOMIC); \
		} \
	} } while (0)

	#define __kct_log_11(Type, Submitter_name, Ev_name, flags, Data0, Data1, Data2, \
					 Data3, Data4, Data5, filelist) \
	do {  if (kct_alloc_event) {	\
		struct ct_event *__ev =	\
			kct_alloc_event(Submitter_name, Ev_name, Type, \
				GFP_ATOMIC, flags); \
		if (__ev) { \
			if (Data0) \
			kct_add_attchmt(&__ev, CT_ATTCHMT_DATA0, \
					strlen(Data0) + 1, Data0, GFP_ATOMIC); \
			if (Data1) \
			kct_add_attchmt(&__ev, CT_ATTCHMT_DATA1, \
					strlen(Data1) + 1, Data1, GFP_ATOMIC); \
			if (Data2) \
			kct_add_attchmt(&__ev, CT_ATTCHMT_DATA2, \
					strlen(Data2) + 1, Data2, GFP_ATOMIC); \
			if (Data3) \
			kct_add_attchmt(&__ev, CT_ATTCHMT_DATA3, \
					strlen(Data3) + 1, Data3, GFP_ATOMIC); \
			if (Data4) \
			kct_add_attchmt(&__ev, CT_ATTCHMT_DATA4, \
					strlen(Data4) + 1, Data4, GFP_ATOMIC); \
			if (Data5) \
			kct_add_attchmt(&__ev, CT_ATTCHMT_DATA5, \
					strlen(Data5) + 1, Data5, GFP_ATOMIC); \
			if (filelist) \
			kct_add_attchmt(&__ev, CT_ATTCHMT_FILELIST, \
					strlen(filelist) + 1, filelist, GFP_ATOMIC); \
			kct_log_event(__ev, GFP_ATOMIC); \
		} \
	} } while (0)

#endif /* !KCT_H_ */