/*
 * ts_sbcinfo.c
 * Liberty Young	Technologic Systems
 * spam_filter(email): liberty(at)embeddedx86.com/dot/com
 * These functions add proc entries to display information
 * about the SingleBoardComputer
*/

#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/modsetver.h>
#include <linux/ioport.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/ts_sbc.h>
#include <asm/io.h>
	
struct sbc_info ts_sbc;

struct sbc_info *get_sbcinfo ( void ) {
	struct sbc_info *info;
	info = NULL;

	info = kmalloc (sizeof(struct sbc_info), GFP_KERNEL);
	if (!info)
		return info;

	memset (info, 0, sizeof(struct sbc_info));
	*info = ts_sbc;
	return info;
}
void remove_sbcinfo ( struct sbc_info *info)
{
	kfree (info);
}

static int sbc_setinfo ( struct sbc_info *sbc){
	unsigned short temp;

	memset( sbc, 0, sizeof(struct sbc_info));

	temp = inw(TS7XXX_MODEL) & 0x07;

	sbc->pld_ver = temp & 0x0007;
	//set the model number
	switch (temp) {
		case TS7200:
			sbc->board_id = 7200;
			break;
		case TS7250:
			sbc->board_id = 7250;
			break;
		case TS7260:
			sbc->board_id = 7260;
			break;
		case TS7300:
			sbc->board_id = 7300;
			break;
		default:
			sbc->board_id = 0;
			break;
	}

	temp = inb(0x02400000 + TS7XXX_IO16_BASE );

	//check for AtoD
	switch (sbc->board_id) {
		case 7200:
			if (temp & 0x01)
				sbc->AtoD = 1;
			break;
			if ( temp & 0x01)
				sbc->AtoD = 1;
		default:
			break;
	}

	//check for 485
	switch (sbc->board_id) {
		case 7200:
		case 7250:
		case 7260:
		case 7300:
			if (temp & 0x02) {
				sbc->RS485 = 1;
				sbc->auto485 = 1;
			}
			break;
		default:
			break;
	}

	return 0;
}

static int sbc_read_proc(char *page, char **start, off_t off,
		int count, int *eof, void *data)
{
	int len = 0;
	unsigned int temp;

	len += sprintf(page + len, "Board ID:		TS-%d\n",
			ts_sbc.board_id );
	len += sprintf(page + len, "RS485:   		%s\n",
			ts_sbc.RS485 ? "yes" : "no");
	len += sprintf(page + len, "AnalogToDigital: 	%s\n",
			ts_sbc.AtoD ? "yes" : "no");
	len += sprintf(page + len, "Auto485:  		%s\n",
			ts_sbc.auto485 ? "yes" : "no");
/*	len += sprintf(page + len, "SRAM:   		%s\n",
			ts_sbc.sram ? "yes" : "no");
	len += sprintf(page + len, "External Reset:	%s\n",
			ts_sbc.extrst ? "yes" : "no");
*/
	if (len <= off+count)
		*eof = 1;
	*start = page + off;
	len -= off;
	if (len > count)
		len =count;
	
	return len;
}

static void sbc_remove_proc (struct proc_dir_entry *entry) {
	remove_proc_entry (entry->name,
			entry->parent);
	entry = NULL;
}

static int __init sbc_proc_init (void)
{
	struct proc_dir_entry *entry, *parent_dir ;
	int err; 

	parent_dir = proc_mkdir("SBC", NULL);
	if (parent_dir == NULL)
		return -ENOMEM;

	entry = create_proc_read_entry ("info", S_IRUGO, parent_dir, sbc_read_proc, 0);

	if (entry == NULL) {
		sbc_remove_proc (entry);
		printk("ts_sbcinfo.c: unable to create proc entry SBC/info");
		return -ENOMEM;
	}

	err = sbc_setinfo (&ts_sbc);
	if (err < 0 ) {
		sbc_remove_proc( entry);
		sbc_remove_proc (parent_dir);
		printk("ts_sbcinfo.c: Problems determing SBC information..Error %d\n",err);
		return err;
	}

	return 0;
}

module_init (sbc_proc_init);

EXPORT_SYMBOL(remove_sbcinfo);
EXPORT_SYMBOL(get_sbcinfo);

