/* Procfs interface for the PCI series device driver.

   Author: Golden Wang

   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.0.0  19 Dec 2016 by Golden Wang
    Create. */

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

#include "ixecat.h"

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
#define HAVE_PROC_OPS
#endif

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

	char *p, *q, *l;
	unsigned int i, n, a, b, c, d;
	ixecat_devinfo_t *r;
	char my_buf[128];

	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", ixecat_major);
	q = my_buf;
	for (; n > 0 && *q != '\0'; --n) {	/* export characters */
		*p++ = *q++;
	}

	/* export module names */
	i = 0;
	l = "mod:";
	for (; n > 0 && *l != '\0'; --n) {
		*p++ = *l++;
	}
	while (ixecat_card[i].id) {	/* scan card list */
		if (ixecat_card[i].present) {	/* find present card */
			if (n > 0) {
				*p++ = ' ';
				--n;
			}
			q = ixecat_card[i].module;
			for (; n > 0 && *q != '\0'; --n) {	/* export card's module name */
				*p++ = *q++;
			}
		}
		++i;
	}
	if (n > 0) {				/* separator */
		*p++ = '\n';
		--n;
	}

	/* export device characters */
	r = ixecat_dev;
	while (r) {
		l = "dev: ";
		for (; n > 0 && *l != '\0'; --n) {	/* export card's module name */
			*p++ = *l++;
		}
		a = (r->id >> 48) & 0xffff;
		b = (r->id >> 32) & 0xffff;
		c = (r->id >> 16) & 0xffff;
		d = r->id & 0xffff;
		sprintf(my_buf,
				"ixecat%d %d 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%04x%04x%04x%04x %s\n",
				r->no, r->irq, r->base[0], r->base[1], r->base[2],
				r->base[3], r->base[4], r->base[5], a, b, c, d,
				(char *) _pci_cardname(r->id));
		q = my_buf;
		for (; n > 0 && *q != '\0'; --n) {	/* export characters */
			*p++ = *q++;
		}
		r = r->next;
	}

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

#if 1
static int ixecat_proc_show(struct seq_file *m, void *v)
{
	unsigned int i = 0;
	unsigned int a, b, c, d;
	ixecat_devinfo_t *r;

	seq_printf(m, "maj: %d\n", ixecat_major);
	seq_printf(m, "%s", "mod:");

	while (ixecat_card[i].id) {      /* scan card list */
		if (ixecat_card[i].present) {    /* find present card */
			seq_printf(m, " ");
			seq_printf(m, "%s", ixecat_card[i].module);
		}
		++i;
	}

	seq_printf(m, "\n");

	/* export device characters */
	r = ixecat_dev;
	while (r) {

		seq_printf(m, "%s", "dev: ");

		a = (r->id >> 48) & 0xffff;
		b = (r->id >> 32) & 0xffff;
		c = (r->id >> 16) & 0xffff;
		d = r->id & 0xffff;

		seq_printf(m, "ixecat%d %d 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%04x%04x%04x%04x %s\n", r->no, r->irq, r->base[0], r->base[1], r->base[2], r->base[3], r->base[4], r->base[5], a, b, c, d, (char *) _pci_cardname(r->id));

		r = r->next;
	}

	return  0;
}

static int ixecat_proc_open(struct inode *inode, struct file *file)
{
        return single_open(file, ixecat_proc_show, NULL);
}
#ifdef HAVE_PROC_OPS
static const struct proc_ops proc_fops = {
        .proc_open   = ixecat_proc_open,
        .proc_read   = seq_read,
        .proc_release = single_release,
};
#else
static const struct file_operations proc_fops = {
        .open   = ixecat_proc_open,
        .read   = seq_read,
        .release = single_release,
};

#endif
#else
static int ixecat_proc_output(char *buf)
{
        char *p;
        unsigned int i = 0;
	unsigned int a, b, c, d;
        ixecat_devinfo_t *r;
        //char my_buf[80];

        p = buf;

        /* export major number */

        p += sprintf(p, "maj: %d\n", ixecat_major);
        p += sprintf(p, "%s", "mod:");

	 while (ixecat_card[i].id) {      /* scan card list */
                if (ixecat_card[i].present) {    /* find present card */
                        p += sprintf(p, " ");
                        p += sprintf(p, "%s", ixecat_card[i].module);
                }
                ++i;
        }
        p += sprintf(p, "\n");

         /* export device characters */
        r = ixecat_dev;
        while (r) {
		
                p += sprintf(p, "%s", "dev: ");

		a = (r->id >> 48) & 0xffff;
                b = (r->id >> 32) & 0xffff;
                c = (r->id >> 16) & 0xffff;
                d = r->id & 0xffff;
               	p += sprintf(p, "ixecat%d %d 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%04x%04x%04x%04x %s\n", r->no, r->irq, r->base[0], r->base[1], r->base[2], r->base[3], r->base[4], r->base[5], a, b, c, d, (char *) _pci_cardname(r->id));

                r = r->next;
        }

        return  p - buf;
}

static int ixecat_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
        int len = ixecat_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 *ixecat_proc_dir;
#else
struct proc_dir_entry ixecat_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:ixecat_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 ixecat_proc_exit(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
	remove_proc_entry(DEVICE_NAME, ixecat_proc_dir);
	remove_proc_entry(FAMILY, NULL);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
	proc_unregister(&proc_root, ixecat_procfile.low_ino);
#endif
}

int ixecat_proc_init(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
	//ixecat_proc_dir = proc_mkdir(ORGANIZATION, 0);
	ixecat_proc_dir = proc_mkdir(FAMILY, 0);
#if 1
        proc_create(DEVICE_NAME, 0, ixecat_proc_dir, &proc_fops);
#else
	create_proc_read_entry(DEVICE_NAME, 0, ixecat_proc_dir, ixecat_read_proc, NULL);
#endif
	//create_proc_info_entry(DEVICE_NAME, 0, ixecat_proc_dir, ixecat_get_info);
	return 0;
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
	return proc_register(&proc_root, &ixecat_procfile);
#else
	return proc_register_dynamic(&proc_root, &ixecat_procfile);
#endif
}
