[kernel] bug#25: [PATCH] Some ideas


None


X-PA-RISC Linux-PR-Message: report 25
X-PA-RISC Linux-PR-Package: kernel
X-Loop: daniel_frazier@hp.com
Received: via spool by 25-bugs@bugs.parisc-linux.org id=B25.98364333531141
          (code B ref 25); Sat, 03 Mar 2001 18:18:01 GMT
Date: Sat, 3 Mar 2001 11:14:05 -0700
Message-ID: <20010303111405.V27829@tausq.org>
From: "Randolph Chung" <randolph@tausq.org>
To: 25@bugs.parisc-linux.org
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
User-Agent: Mutt/1.2.5i
X-PGP: for PGP key, see http://www.tausq.org/pgp.txt
X-GPG: for GPG key, see http://www.tausq.org/gpg.txt

Been experimenting a bit with various methods...

the first is a /proc/pdc interface:
frodo:~# ls -l /proc/pdc/
total 0
-r--------    1 root     root            0 Jan 31 11:10 model_info
-r--------    1 root     root            0 Jan 31 11:10 model_name
-rw-------    1 root     root            0 Jan 31 11:10 timeofday

frodo:~# cat /proc/pdc/model_info 
hversion: 0x6010
sversion: 0x481
hw_id: 0x0
boot_id: 0x0
sw_id: 0x77F17B38
sw_cap: 0x0
arch_rev: 0x4
pot_key: 0x72
curr_key: 0x72

this is fairly straightforward, but probably only works well for very
simple PDC interfaces. 

The second is a /dev based interface. For example:
frodo:~# ls -l /dev/stable /dev/nvram 
crw-r--r--    1 root     root      10, 160 Mar  3  2001 /dev/nvram
crw-r--r--    1 root     root      10, 162 Mar  1  2001 /dev/stable

On startup, I get:
Initializing /dev/stable and /dev/nvram support
/dev/nvram not available
/dev/stable: 256 bytes available

and i can do, f.i., cat /dev/stable > /tmp/foo
the devices support read/write/lseek and ioctls for init/verify/getsize

sample code for each is attached. still working on the /dev interface. 
i get data back, but it doesn't look quite right... comments,
suggestions and critiques are most welcome.

randolph
-- 
   @..@                                         http://www.TauSq.org/
  (----)
 ( >__< )
 ^^ ~~ ^^

============ arch/parisc/kernel/proc_pdc.c ==============

/* 
 * $Id$
 * /proc/pdc interface
 *
 * Copyright 2001 Randolph Chung <tausq@debian.org>
 * GPLv2
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/proc_fs.h>
#include <asm/pdc.h>
#include <asm/page.h>
#include <asm/uaccess.h>
#define PROCPDCDEBUG

#ifdef PROCPDCDEBUG
#define dbg(fmt...) printk(fmt)
#else
#define dbg(fmt...) /* nothing */
#endif

/*
 * Root /proc/pdc entry
 */
#define PDCROOT				"pdc"
static struct proc_dir_entry *proc_pdc_root = NULL;

static long my_atol(const char *s)
{
	long out = 0;
	const char *p = s;
	for (; *p != 0; p++)
	{
		if (*p < '0' || *p > '9') break;
		out = out * 10 + (*p - '0');
	}
	return out;
}

/*
 * static int procpdc_write(struct file *file, const char *buf, 
 * 	unsigned long size, void *data)
 *
 * static int procpdc_read(char *page, char **start, off_t off, int size, 
 * 	int *eof, void *data)
 */

static int ppdc_modelinfo(char *page, char **start, off_t off, int size, 
 	int *eof, void *data)
{
	struct pdc_model model;

	*eof = 1;
	if (pdc_model_info(&model) == 0)
		return sprintf(page, 
			"hversion: 0x%lX\n"
			"sversion: 0x%lX\n"
			"hw_id: 0x%lX\n"
			"boot_id: 0x%lX\n"
			"sw_id: 0x%lX\n"
			"sw_cap: 0x%lX\n"
			"arch_rev: 0x%lX\n"
			"pot_key: 0x%lX\n"
			"curr_key: 0x%lX\n",
			model.hversion, model.sversion, model.hw_id, 
			model.boot_id, model.sw_id, model.sw_cap, 
			model.arch_rev, model.pot_key, model.curr_key);
	else
		return -EINVAL;
}

static int ppdc_modelname(char *page, char **start, off_t off, int size, 
 	int *eof, void *data)
{
	char modelname[81]; /* PDC spec says name is <= 80 characters */
	*eof = 1;
	if (pdc_model_sysmodel(modelname) == 0)
		return sprintf(page, "%s\n", modelname);
	else
		return -EINVAL;
}

static int ppdc_tod_set(struct file *file, const char *buf, unsigned long size, 
	void *data)
{
	unsigned long sec = 0, usec = 0;
	char todbuf[128] = {0};
	char *p;

	if (size >= sizeof(todbuf)) return -EINVAL;
	copy_from_user(todbuf, buf, size);

	p = todbuf;
	while (*p != 0 && *p != ' ' && *p != '\t') p++;
	if (*p != 0) 
	{
		*p++ = 0;
		while (*p != 0 && (*p == ' ' || *p == '\t')) p++;
	}
	sec = my_atol(todbuf);
	usec = my_atol(p);

	if (sec != 0)
	{
		pdc_tod_set(sec, usec);
		return size;
	}
	else
	{
		return -EINVAL;
	}
}

static int ppdc_tod_get(char *page, char **start, off_t off, int size, 
	int *eof, void *data)
{
	struct pdc_tod tod;
	*eof = 1;

	if (pdc_tod_read(&tod) == 0)
		return sprintf(page, "%ld %ld\n", tod.tod_sec, tod.tod_usec);
	else
		return -EINVAL;
}

/* *********************************************************************** */
static struct pdc_proc_handler_list_t {
	char *name;			/* proc entry to create */
	/* read handler */
	int (*reader)(char *, char **, off_t, int, int *, void *);
	/* write handler */
	int (*writer)(struct file *, const char *, unsigned long, void *);
	void *data;
} pdc_proc_handlers[] = {
	{ "model_info", ppdc_modelinfo, 0, 0 },
	{ "model_name", ppdc_modelname, 0, 0 },
	{ "timeofday", ppdc_tod_get, ppdc_tod_set, 0 }
};

int __init procpdc_init(void)
{
	long i;
	struct proc_dir_entry *ent;

	dbg("Initializing /proc/pdc support: ");
	proc_pdc_root = proc_mkdir(PDCROOT, 0);

	for (i = 0; i < sizeof(pdc_proc_handlers)/sizeof(pdc_proc_handlers[0]); i++)
	{
		dbg("%s ", pdc_proc_handlers[i].name);
		ent = create_proc_entry(pdc_proc_handlers[i].name,
			(pdc_proc_handlers[i].writer ? S_IFREG|S_IRUSR|S_IWUSR :
			 S_IFREG|S_IRUSR), proc_pdc_root);
		if (ent == NULL)
		{
			dbg("create_proc_entry() returned NULL\n");
			return -1;
		}

		ent->nlink = 1;
		ent->data = (void *)&pdc_proc_handlers[i];
		ent->read_proc = pdc_proc_handlers[i].reader;
		ent->write_proc = pdc_proc_handlers[i].writer;
	}

	printk("\n");
	return 0;
}

void __exit procpdc_exit(void)
{
	int i;

	for (i = 0; i < sizeof(pdc_proc_handlers)/sizeof(pdc_proc_handlers[0]); i++)
	{
		remove_proc_entry(pdc_proc_handlers[i].name, 0);
	}
	remove_proc_entry(PDCROOT, 0);
	return 0;
}

module_init(procpdc_init);
module_exit(procpdc_exit);


================= arch/parisc/kernel/stable-nvram.c =======================
/*
 * $Id$
 * /dev/nvram - access to nonvolatile memory via PDC_NVOLATILE
 * /dev/stable - access to stable storage via PDC_STABLE
 *
 * (c) 2001 Randolph Chung <tausq@debian.org>
 * GPLv2
 */

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/pdc.h>

#define DEBUG 1
#ifdef DEBUG
#define dbg(fmt...) printk(fmt)
#else
#define dbg(fmt...)
#endif

static struct file_operations dev_sn_fops;

static struct device_t {
	const char *name;
	int opened;
	unsigned long size;

	int (*init)(void);
	int (*verify)(void);
	int (*getsize)(unsigned long *size);
	int (*read)(void *, void *, unsigned long count);
	int (*write)(void *, void *, unsigned long count);

	struct miscdevice miscdev;
} devices[] = {
	{ "nvram", 0, 0, pdc_nvram_init, pdc_nvram_verify,
	  pdc_nvram_size, pdc_nvram_read, pdc_nvram_write,
	  { PARISC_NVRAM_MINOR, "nvram", &dev_sn_fops } },
	{ "stable", 0, 0, pdc_stable_init, pdc_stable_verify,
	  pdc_stable_size, pdc_stable_read, pdc_stable_write,
	  { PARISC_STABLE_MINOR, "stable", &dev_sn_fops } },
};
#define NVRAM_DEVICE  0
#define STABLE_DEVICE 1

static inline int getdevicenum(int minor)
{
	switch (minor)
	{
	case PARISC_NVRAM_MINOR: return NVRAM_DEVICE;
	case PARISC_STABLE_MINOR: return STABLE_DEVICE;
	default:
		printk(KERN_CRIT "%s: requesting unknown device!\n", __FILE__);	
		return -1;
	}
}

static int dev_sn_open(struct inode *inode, struct file *file)
{
	int n = getdevicenum(MINOR(inode->i_rdev));
	dbg("Opening /dev/%s\n", devices[n].name);
	if (devices[n].opened) return -EBUSY;
	devices[n].opened = 1;
	return 0;
}

static int dev_sn_release(struct inode *inode, struct file *file)
{
	int n = getdevicenum(MINOR(inode->i_rdev));
	dbg("Opening /dev/%s\n", devices[n].name);
	devices[n].opened = 0;
	return 0;
}

static ssize_t dev_sn_read(struct file *file, char *buf, size_t count,
	loff_t *ppos)
{
	char *kbuf;
	unsigned long pos = (long)*ppos;
	int n = getdevicenum(MINOR(file->f_dentry->d_inode->i_rdev));

	dbg("dev_sn_read: pos = %ld, count = %u\n", pos, (unsigned int)count);

	if (pos + count > devices[n].size)
		count = devices[n].size - pos;

	if (pos > devices[n].size || count == 0) 
		return 0;

	if ((kbuf = kmalloc(count, GFP_KERNEL)) == 0)
		return -EFAULT;

	dbg("allocated %u byte buffer\n", (unsigned int)count);

	if (devices[n].read((void *)pos, kbuf, count) < 0 || 
	    copy_to_user(buf, kbuf, count))
		return -EFAULT;

	kfree(kbuf);

	*ppos += count;
	return count;
}

static ssize_t dev_sn_write(struct file *file, const char *buf, 
	size_t count, loff_t *ppos)
{
	char *kbuf;
	unsigned long pos = (long)*ppos;
	int n = getdevicenum(MINOR(file->f_dentry->d_inode->i_rdev));

	dbg("dev_sn_write: pos = %ld, count = %u\n", pos, (unsigned int)count);

	if (pos + count > devices[n].size)
		count = devices[n].size - pos;

	if (pos > devices[n].size || count == 0) 
		return 0;

	if ((kbuf = kmalloc(count, GFP_KERNEL)) == 0)
		return -EFAULT;

	dbg("allocated %u byte buffer\n", (unsigned int)count);

	if (copy_from_user(kbuf, buf, count) ||
	    devices[n].write((void *)pos, kbuf, count) < 0)
		return -EFAULT;

	kfree(kbuf);

	*ppos += count;
	return count;
}

static int dev_sn_ioctl(struct inode *inode, struct file *file,
	unsigned int cmd, unsigned long arg)
{
	int n = getdevicenum(MINOR(inode->i_rdev));
	int r = 1;

	dbg("dev_sn_ioctl: cmd = %d\n", cmd);
	if (cmd == NVRAM_INIT || cmd == STABLE_INIT)
		r = devices[n].init();
	else if (cmd == NVRAM_VERIFY || cmd == STABLE_VERIFY)
		r = devices[n].verify();
	else if (cmd == NVRAM_SIZE || cmd == STABLE_SIZE)
		r = devices[n].getsize((unsigned long *)arg);

	return (r == 0) ? 0 : -EINVAL;
}

static long long dev_sn_llseek(struct file *file, loff_t offset, int origin)
{
	int n = getdevicenum(MINOR(file->f_dentry->d_inode->i_rdev));

	dbg("dev_sn_llseek\n");

	switch (origin)
	{
	case 1:
		offset += file->f_pos;
		break;
	case 2:
		offset += devices[n].size;
		break;
	}
	if (offset < 0 || offset >= devices[n].size) return -EINVAL;

	file->f_pos = offset;
	return file->f_pos;
}

static struct file_operations dev_sn_fops = {
	open: dev_sn_open,
	release: dev_sn_release,
	read: dev_sn_read,
	write: dev_sn_write,
	ioctl: dev_sn_ioctl,
	llseek: dev_sn_llseek,
};

int __init dev_sn_init(void)
{
	int i;
	printk("Initializing /dev/stable and /dev/nvram support\n");

	for (i = 0; i < sizeof(devices)/sizeof(devices[0]); i++)
	{
		if (devices[i].getsize(&devices[i].size) < 0 ||
		    devices[i].size == 0)
		{
			printk("/dev/%s not available\n", 
			       devices[i].name);
		}
		else
		{
			printk("/dev/%s: %lu bytes available\n",
			       devices[i].name, devices[i].size);

			misc_register(&devices[i].miscdev);
		}
	}
	return 0;
}

void __exit dev_sn_exit(void)
{
	int i;
	for (i = 0; i < sizeof(devices)/sizeof(devices[0]); i++)
		misc_register(&devices[i].miscdev);
}

module_init(dev_sn_init);
module_exit(dev_sn_exit);