
#ifdef CONFIG_PROC_FS

#define PROC_READ_NAME(name) name##_read_func
#define PROC_WRITE_NAME(name) name##_write_func
#define PROC_TOGGLE_NAME(name) name##_toggle_func

#define MAKE_PROC_READ(name, item) \
	static int name(char* page, char** start, off_t off, int count, int* eof, void* data) \
	{ \
		int len; \
		struct psmouse *psmouse = (struct psmouse *)data; \
		struct trackpoint_data *tp = (struct trackpoint_data*)psmouse->private; \
		len = sprintf(page, "%lu\n", (unsigned long)tp->item); \
		*eof = 1; \
		return len; \
	}

#define MAKE_PROC_WRITE(name, item, command) \
	static int name(struct file *file, const char __user *buffer, unsigned long count, void *data) \
	{ \
		int len = count; \
		unsigned char tmp[5]; \
		struct psmouse *psmouse = (struct psmouse *)data; \
		struct trackpoint_data *tp = (struct trackpoint_data*)psmouse->private; \
		if(count > sizeof(tmp) - 1) \
			len = sizeof(tmp) - 1; \
		if(copy_from_user(tmp, buffer, len)) \
			return -EFAULT; \
		tmp[len] = '\0'; \
		tp->item = simple_strtoul(tmp, 0, 10); \
		trackpoint_write(&psmouse->ps2dev, command, tp->item); \
		return len; \
	}

#define MAKE_PROC_TOGGLE(name, item, command, mask) \
	static int name(struct file *file, const char __user *buffer, unsigned long count, void *data) \
	{ \
		int len = count; \
		unsigned char toggle, tmp[5]; \
		struct psmouse *psmouse = (struct psmouse *)data; \
		struct trackpoint_data *tp = (struct trackpoint_data*)psmouse->private; \
		if(count > sizeof(tmp) - 1) \
			len = sizeof(tmp) - 1; \
		if(copy_from_user(tmp, buffer, len)) \
			return -EFAULT; \
		tmp[len] = '\0'; \
		toggle = (tmp[0] == '1') ? 1 : 0; \
		if( toggle != tp->item) { \
			tp->item = toggle; \
			trackpoint_toggle_bit(&psmouse->ps2dev, command, mask); \
		} \
		return len; \
	}


MAKE_PROC_WRITE(PROC_WRITE_NAME(sensitivity), sens, TP_SENS);
MAKE_PROC_READ(PROC_READ_NAME(sensitivity), sens);

MAKE_PROC_WRITE(PROC_WRITE_NAME(speed), speed, TP_SPEED);
MAKE_PROC_READ(PROC_READ_NAME(speed), speed);

MAKE_PROC_WRITE(PROC_WRITE_NAME(neg_inertia), inertia, TP_INERTIA);
MAKE_PROC_READ(PROC_READ_NAME(neg_inertia), inertia);

MAKE_PROC_WRITE(PROC_WRITE_NAME(backup), reach, TP_REACH);
MAKE_PROC_READ(PROC_READ_NAME(backup), reach);

MAKE_PROC_WRITE(PROC_WRITE_NAME(drag_hyst), draghys, TP_DRAGHYS);
MAKE_PROC_READ(PROC_READ_NAME(drag_hyst), draghys);

MAKE_PROC_WRITE(PROC_WRITE_NAME(min_drag), mindrag, TP_MINDRAG);
MAKE_PROC_READ(PROC_READ_NAME(min_drag), mindrag);

MAKE_PROC_WRITE(PROC_WRITE_NAME(thresh), thresh, TP_THRESH);
MAKE_PROC_READ(PROC_READ_NAME(thresh), thresh);

MAKE_PROC_WRITE(PROC_WRITE_NAME(up_thresh), up_thresh, TP_UP_THRESH);
MAKE_PROC_READ(PROC_READ_NAME(up_thresh), up_thresh);

MAKE_PROC_WRITE(PROC_WRITE_NAME(z_time), z_time, TP_Z_TIME);
MAKE_PROC_READ(PROC_READ_NAME(z_time), z_time);

MAKE_PROC_WRITE(PROC_WRITE_NAME(jenks_curv), jenks_curv, TP_JENKS_CURV);
MAKE_PROC_READ(PROC_READ_NAME(jenks_curv), jenks_curv);

MAKE_PROC_TOGGLE(PROC_TOGGLE_NAME(ptson), ptson, TP_TOGGLE_PTSON, TP_MASK_PTSON);
MAKE_PROC_READ(PROC_READ_NAME(ptson), ptson);

MAKE_PROC_TOGGLE(PROC_TOGGLE_NAME(skip_back), skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK);
MAKE_PROC_READ(PROC_READ_NAME(skip_back), skipback);

MAKE_PROC_TOGGLE(PROC_TOGGLE_NAME(mb), mb, TP_TOGGLE_MB, TP_MASK_MB);
MAKE_PROC_READ(PROC_READ_NAME(mb), mb);

MAKE_PROC_TOGGLE(PROC_TOGGLE_NAME(ext_dev), ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV);
MAKE_PROC_READ(PROC_READ_NAME(ext_dev), ext_dev);

/* MAKE_PROC_WRITE( ) for Soft Transparent mode */
static int transparent_write_func(struct file *file, const char __user *buffer, unsigned long count, void *data)
{
	int len = count;
	unsigned char command;
	unsigned char tmp[5];
	struct psmouse *psmouse = (struct psmouse *)data;
	struct trackpoint_data *tp = (struct trackpoint_data*)psmouse->private;
	if(count > sizeof(tmp) - 1)
		len = sizeof(tmp) - 1;
	if(copy_from_user(tmp, buffer, len))
		return -EFAULT;
	tmp[len] = '\0'; \
	tp->transparent = simple_strtoul(tmp, 0, 10);
	command = (tp->transparent) ? TP_SET_SOFT_TRANS : TP_CANCEL_SOFT_TRANS;

	trackpoint_command(&psmouse->ps2dev, command);
	return len;
}

MAKE_PROC_READ(PROC_READ_NAME(transparent), transparent);

/* MAKE_PROC_WRITE( ) for Middle Button Scroll mode */
static int scroll_write_func(struct file *file, const char __user *buffer, unsigned long count, void *data)
{
	int len = count;
	unsigned char tmp[5];
	struct psmouse *psmouse = (struct psmouse *)data;
	struct trackpoint_data *tp = (struct trackpoint_data*)psmouse->private;
	if(count > sizeof(tmp) - 1)
		len = sizeof(tmp) - 1;
	if(copy_from_user(tmp, buffer, len))
		return -EFAULT;
	tmp[len] = '\0'; \
	tp->mb_scroll = simple_strtoul(tmp, 0, 10);

	return len;
}

MAKE_PROC_READ(PROC_READ_NAME(scroll), mb_scroll);

/* MAKE_PROC_WRITE( ) for delay before scroll */
static int scroll_delay_write_func(struct file *file, const char __user *buffer, unsigned long count, void *data)
{
        int len = count;
        unsigned char tmp[5];
        struct psmouse *psmouse = (struct psmouse *)data;
        struct trackpoint_data *tp = (struct trackpoint_data*)psmouse->private;
        if(count > sizeof(tmp) - 1)
                len = sizeof(tmp) - 1;
        if(copy_from_user(tmp, buffer, len))
                return -EFAULT;
        tmp[len] = '\0'; \
        tp->scroll_delay = simple_strtoul(tmp, 0, 10);

        return len;
}

MAKE_PROC_READ(PROC_READ_NAME(scroll_delay), scroll_delay);



#define NEW_PROC_ENTRY(name) \
        entry = create_proc_entry(#name, 0644, tp_dir); \
        if(!entry) \
                goto no_##name; \
        entry->owner = THIS_MODULE; \
        entry->data = psmouse; \
        entry->read_proc = PROC_READ_NAME(name); \
        entry->write_proc = PROC_WRITE_NAME(name);


#define NEW_PROC_TOGGLE_ENTRY(name) \
        entry = create_proc_entry(#name, 0644, tp_dir); \
        if(!entry) \
                goto no_##name; \
        entry->owner = THIS_MODULE; \
        entry->data = psmouse; \
        entry->read_proc = PROC_READ_NAME(name); \
        entry->write_proc = PROC_TOGGLE_NAME(name);
	


static struct proc_dir_entry *tp_dir = NULL;


static int trackpoint_proc_init(struct psmouse *psmouse) 
{ 
	struct proc_dir_entry *entry = NULL;

	tp_dir = proc_mkdir("trackpoint", NULL);
	if(!tp_dir) 
		return -ENOMEM;

	tp_dir->owner = THIS_MODULE;

	NEW_PROC_ENTRY(sensitivity);
	NEW_PROC_ENTRY(speed);
	NEW_PROC_ENTRY(neg_inertia);
	NEW_PROC_ENTRY(backup);
	NEW_PROC_ENTRY(drag_hyst);
	NEW_PROC_ENTRY(min_drag);
	NEW_PROC_ENTRY(thresh);
	NEW_PROC_ENTRY(up_thresh);
	NEW_PROC_ENTRY(z_time);
	NEW_PROC_ENTRY(jenks_curv);

	NEW_PROC_TOGGLE_ENTRY(ptson);
	NEW_PROC_TOGGLE_ENTRY(skip_back);
	NEW_PROC_TOGGLE_ENTRY(mb);
	NEW_PROC_TOGGLE_ENTRY(ext_dev);

	/* Soft Transparent mode isn't a toggle or a single
	 * command used to read/write the value
	 */
        entry = create_proc_entry("transparent", 0644, tp_dir);
        if(!entry)
                goto no_transparent;
        entry->owner = THIS_MODULE;
        entry->data = psmouse;
        entry->read_proc = transparent_read_func;
        entry->write_proc = transparent_write_func;

        entry = create_proc_entry("scroll", 0644, tp_dir);
        if(!entry)
                goto no_scroll;
        entry->owner = THIS_MODULE;
        entry->data = psmouse;
        entry->read_proc = scroll_read_func;
        entry->write_proc = scroll_write_func;

        entry = create_proc_entry("scroll_delay", 0644, tp_dir);
        if(!entry)
                goto no_scroll_delay;
        entry->owner = THIS_MODULE;
        entry->data = psmouse;
        entry->read_proc = scroll_delay_read_func;
        entry->write_proc = scroll_delay_write_func;

	return 0;

no_scroll_delay:
        remove_proc_entry("scroll_delay", tp_dir);

no_scroll:
	remove_proc_entry("transparent", tp_dir);

no_transparent:
	remove_proc_entry("ext_dev", tp_dir);

no_ext_dev:
	remove_proc_entry("mb", tp_dir);

no_mb:
	remove_proc_entry("skip_back", tp_dir);

no_skip_back:
	remove_proc_entry("ptson", tp_dir);

no_ptson:
	remove_proc_entry("jenks_curv", tp_dir);

no_jenks_curv:
	remove_proc_entry("z_time", tp_dir);

no_z_time:
	remove_proc_entry("up_thresh", tp_dir);

no_up_thresh:
	remove_proc_entry("thresh", tp_dir);

no_thresh:
	remove_proc_entry("min_drag", tp_dir);

no_min_drag:
	remove_proc_entry("drag_hyst", tp_dir);

no_drag_hyst:
	remove_proc_entry("backup", tp_dir);

no_backup:
	remove_proc_entry("neg_inertia", tp_dir);

no_neg_inertia:
	remove_proc_entry("speed", tp_dir);

no_speed:
	remove_proc_entry("sensitivity", tp_dir);

no_sensitivity:
	remove_proc_entry("trackpoint", NULL);

	return -ENOMEM;
}

static void trackpoint_proc_remove(void)
{
        remove_proc_entry("scroll_delay", tp_dir);
        remove_proc_entry("scroll", tp_dir);
        remove_proc_entry("transparent", tp_dir);
        remove_proc_entry("ext_dev", tp_dir);
        remove_proc_entry("mb", tp_dir);
        remove_proc_entry("skip_back", tp_dir);
        remove_proc_entry("ptson", tp_dir);
        remove_proc_entry("jenks_curv", tp_dir);
        remove_proc_entry("z_time", tp_dir);
        remove_proc_entry("up_thresh", tp_dir);
        remove_proc_entry("thresh", tp_dir);
        remove_proc_entry("min_drag", tp_dir);
        remove_proc_entry("drag_hyst", tp_dir);
        remove_proc_entry("backup", tp_dir);
        remove_proc_entry("neg_inertia", tp_dir);
        remove_proc_entry("speed", tp_dir);
        remove_proc_entry("sensitivity", tp_dir);
        remove_proc_entry("trackpoint", NULL);
}

#else
static int trackpoint_proc_init(struct psmouse *psmouse) { return 0; }
static void trackpoint_proc_remove(void) { }
#endif
