/* Procfs interface for the PIO series device driver.

   Author: Reed Lai

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */

/* File level history (record changes for this file here.)

  v 0.1.0 3 May 2007 by Golden Wang
    Support Linux kernel 2.6.x.
	
  v 0.0.0 26 Jul 2002 by Reed Lai
    Support Kernel 2.4.
    Steal form IxPCI project.
    Create. */

#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/seq_file.h>

#include "ixcom.h"

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
int ixcom_get_info(char *buf, char **start, off_t offset, int buf_size)
#else
int ixcom_get_info(char *buf, char **start, off_t offset, int buf_size,
				   int unused)
#endif
{
	/* read file /proc/ixpio
	 *
	 * Arguments: as /proc filesystem, read <linux/proc_fs.h>
	 *
	 * Returned:  number of written bytes */

	char *p, *q;
	unsigned int n;
	char my_buf[80];

	if (offset > 0)
		return 0;
	/* here, we assume the buf is always large
	   enough to hold all of our info data at
	   one fell swoop */

	p = buf;
	n = buf_size;

	/* export major number */
	sprintf(my_buf, "maj: %d\n", vxc_major);
	q = my_buf;
	for (; n > 0 && *q != '\0'; --n) {	/* export characters */
		*p++ = *q++;
	}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	sprintf(my_buf, "aux: %d\n", vxc_auxmajor);
	q = my_buf;
	for (; n > 0 && *q != '\0'; --n) {	/* export characters */
		*p++ = *q++;
	}
#endif

	return (p - buf - offset);	/* bye bye */
}

static int ixcom_proc_show(struct seq_file *m, void *v)
{
	seq_printf(m, "maj: %d\n", vxc_major);

	return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
static int ixcom_proc_output(char *buf)
{
        char *p;
        //unsigned int i = 0;
        //unsigned int a, b, c, d;
        //char my_buf[80];

        p = buf;

        /* export major number */

        p += sprintf(p, "maj: %d\n", vxc_major);
        //p += sprintf(p, "\n");

        return  p - buf;
}
#endif

static int ixcom_proc_open(struct inode *inode, struct file *file)
{
        return single_open(file, ixcom_proc_show, NULL);
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
static const struct proc_ops proc_fops = {
        .proc_open     =     ixcom_proc_open,
        .proc_read     =     seq_read,
        .proc_release  =     single_release,
};
#else
static const struct file_operations proc_fops = {
        .open   = ixcom_proc_open,
        .read   = seq_read,
        .release = single_release,
};
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
static int ixcom_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
        int len = ixcom_proc_output (page);
        if (len <= off+count) *eof = 1;
        *start = page + off;
        len -= off;
        if (len>count)
                len = count;
        if (len<0)
                len = 0;

        return len;
}
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
static struct proc_dir_entry *ixcom_proc_dir;
#else
struct proc_dir_entry ixcom_procfile = {
	low_ino:0,			/* inode number, 0, auto-filled */
	namelen:DEVICE_NAME_LEN,	/* length of proc file name */
	name:DEVICE_NAME,		/* proc file name */
	mode:S_IFREG | S_IRUGO,
	nlink:1,			/* number of links (where the file is
					   referenced) */
	uid:0,				/* user id, 0, root */
	gid:0,				/* group id, 0, root */
	size:80,			/* the size of the file reported by
						   command "ls" */
	get_info:ixcom_get_info,	/* the read function for this file the
					  function called when somebody tries to
					   read something from it */
};
#endif			/* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) */

void ixcom_proc_exit(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
	remove_proc_entry(DEVICE_NAME, ixcom_proc_dir);
        remove_proc_entry(FAMILY, NULL);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
	proc_unregister(&proc_root, ixcom_procfile.low_ino);
#endif
}

int ixcom_proc_init(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
        //ixcom_proc_dir = proc_mkdir(ORGANIZATION, 0);
        ixcom_proc_dir = proc_mkdir(FAMILY, 0);

        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)
                proc_create(DEVICE_NAME, 0, ixcom_proc_dir, &proc_fops);
        #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
                create_proc_read_entry(DEVICE_NAME, 0, ixcom_proc_dir, ixcom_read_proc, NULL);
        #else
                create_proc_info_entry(DEVICE_NAME, 0, ixcom_proc_dir, ixcom_get_info);
        #endif
	return 0;

#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
	return proc_register(&proc_root, &ixcom_procfile);
#else
	return proc_register_dynamic(&proc_root, &ixcom_procfile);
#endif
}
