9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
From: andrey mirtchovski <andrey@lanl.gov>
To: 9fans@cse.psu.edu
Subject: [9fans] devpcidev.c
Date: Wed, 27 Feb 2002 17:25:32 -0700	[thread overview]
Message-ID: <20020228002424.49B1219AE0@mail.cse.psu.edu> (raw)

[-- Attachment #1: Type: text/plain, Size: 3280 bytes --]

Hi, 

This little program allows me to read/write to random memory addresses
from userland.  Russ Cox indicated that he has something similar, but
since i wanted to learn how to write /dev/ stuff i decided to give it
a try myself...  I find it helpful when debugging vga (for example)
from serial console.  Maybe some of you will find it helpful for
other things :)

Something to note: it is unsafe to use peek and poke at the hardware
from userland.  There are so many ways to render your machine
inoperable that I'm not going to bother listing them here.  (Normally
a reboot fixes things, but...)  Don't take it out on me or on LANL if
your hardware chokes as a consequence of pcidev.  Do not attempt to
remove bread stuck in a plugged toaster with a knife either.
*shrug*...  You know all this, bot nevertheless you have been warned.

Pcidev (sorry, couldn't think of a better name) sits between you and
the hardware devices on your computer.  It allows the user to
read/write (standard inb, inw, inl, outb, outw, outl) using simple
shell scripts.

Pcidev could be found as a drive named #Z.  When bound to a directory
it presents itself as a sigle file named 'base'.  This file takes as
input a memory address, to which the user wants to write or read.  
Having been given an address, pcidev creates a directory named after 
the it and populates it with 6 files used for reading/writing, namely:

	inb -> read a byte
	inw -> read a word (16 bits)  
	inl -> read a long   

	outb -> write to the port (byte)
	outw -> short
	outl 	-> long

That's pretty much it.. It's really simple.

Here's a sample session with it:

----

% bind -a '#Z' /tmp/z
% cd /tmp/z
% ls
base
% echo 0x3cc > base  	# address should be a valid atol()-understandable string, 0xF, 0F, 15 -> all accepted
% ls
0x3cc				# 0x3cc is VGA's Miscellaneous Output Register
base
% cd 0x3cc
% ls
inb
inl
inw
outb
outl
outw
% cat inb
0xe3%				# no \n appended...
% cat inl
0xff0800e3%
% cd ..
% echo 0x80 > base
% rm 0x3cc
% ls
0x80				# a pci post-card. write-only
base
% cd 0x80
% echo 0xf > outb		# 0F shows on the device, 
% echo 14 > outb		# 0D shows on the device

----

That's about it.  The maximum addresses opened at a single time is 128.
Just rm some stuff to open up space (rm base will not work, of
course).

To compile a kernel with pcidev:

cd /sys/src/9/pc 
cp devpcidev.c .  
edit pcdisk and add 'pcidev' at the bottom of the 'dev' list 
mk 'CONF=9pcdisk' (or your favorite way ofdoing it) 
9fat:; cp 9pcdisk /n/9fat 
reboot...

Sorry, there is no man page for it, but there are no plans to put it
into the official distribution either :)

While discussing the 'design' with Ron Minnich another way of doing it
came up -- have a single file for input, another one for output and
manage the addresses with seek().  That way a 5-line C program could
read and report the entire status of a device.  I'm posting this
implementation because it's a bit more complex (dealing with
subdirectories, adding/deleting addresses) and not because it's
necessarily the better way to present files for inb and outb.

regards: andrey

ps: as usual, flame me privately if you don't like it or if it doesn't work..


[-- Attachment #2: devpcidev.c --]
[-- Type: text/plain, Size: 5893 bytes --]

#include "u.h"
#include "../port/lib.h" 
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

static	char Etoomany[] = 	"too many ports opened";
static	char Enoclient[] =	"no such pci device...";

static void *pcidevbase = 0;
enum {
	Qtopdir = 0,
	Qbase,

	Qpcidir,
	Qinb,	/* reading */
	Qinw,	
	Qinl,	
	Qoutb,	/* writing */
	Qoutw,
	Qoutl,	
	
};

typedef struct Mypci Mypci;
typedef struct Devpci Devpci;

struct Mypci
{
	QLock;
	ulong base;
};

enum {
	Maxpcidev = 128,		/* looks like a good number */
};

struct Devpci 
{
	Mypci *pci[Maxpcidev];
	uint npci;
};

static Devpci devpci;

static Dirtab pcidevdir[] = {
	"inb", {Qinb}, 0, 0440,
	"inw", {Qinw}, 0, 0440,
	"inl", {Qinl}, 0, 0440,
	"outb", {Qoutb}, 0, 0220,
	"outw", {Qoutw}, 0, 0220,
	"outl", {Qoutl}, 0, 0220,
};

static Dirtab pcitopdir[] = {
	"base", {Qbase}, 0, 0220,
};

#define	QSHIFT	4	/* location in qid of client # */

#define	QID(q)		(((q).path&0x0000000F)>>0)
#define	CLIENTPATH(q)	((q&0x07FFFFFF0)>>QSHIFT)
#define	CLIENT(q)	CLIENTPATH((q).path)


Mypci*
pcislotpath(ulong path)
{
	Mypci *cl;
	int slot;

	slot = CLIENTPATH(path);
	if(slot == 0)
		return nil;
	cl = devpci.pci[slot-1];
	if(cl==0 || cl->base==0)
		return nil;
	return cl;
}


Mypci*
pcislot(Chan *c)
{
	Mypci *client;

	client = pcislotpath(c->qid.path);
	if(client == nil)
		error(Enoclient);
	return client;
}


static int
pcidevgen(Chan *c, Dirtab *tab, int x, int s, Dir *dp)
{
	int t;
	Qid q;
	ulong path;
	Mypci *cl;
	char buf[NAMELEN];
	

	USED(tab, x);
	q.vers = 0;

	if(s == DEVDOTDOT){
		switch(QID(c->qid)){
		case Qpcidir:
			cl = pcislot(c);
			sprint(buf, "0x%lux", cl->base);
			devdir(c, (Qid){CHDIR|Qtopdir, 0}, buf, 0, eve, 0500, dp);
			break;
		default:
			panic("pcidevwalk %lux", c->qid.path);
		}
		return 1;
	}


	t = QID(c->qid);
	if(t == Qtopdir){
		if(s == 0){
			q = (Qid){Qbase, 0};
			devdir(c, q, "base", 0, eve, 0600, dp);
		}
		else if(s <= devpci.npci){
			cl = devpci.pci[s-1];
			if(cl == 0)
				return 0;
			sprint(buf, "0x%lux", cl->base);
			q = (Qid){CHDIR|(s<<QSHIFT)|Qpcidir, 0};
			devdir(c, q, buf, 0, eve, 0555, dp);
			return 1;
		}
		else
			return -1;
		return 1;
	}


	path = c->qid.path&~(CHDIR|((1<<QSHIFT)-1));	/* slot component */
	q.vers = c->qid.vers;
	switch(s){
	case 0:
		q = (Qid){path|Qinb, c->qid.vers};
		devdir(c, q, "inb", 0, eve, 0200, dp);
		break;
	case 1:
		q = (Qid){path|Qinw, c->qid.vers};
		devdir(c, q, "inw", 0, eve, 0200, dp);
		break;
	case 2:
		q = (Qid){path|Qinl, c->qid.vers};
		devdir(c, q, "inl", 0, eve, 0200, dp);
		break;
	case 3:
		q = (Qid){path|Qoutb, c->qid.vers};
		devdir(c, q, "outb", 0, eve, 0400, dp);
		break;
	case 4:
		q = (Qid){path|Qoutw, c->qid.vers};
		devdir(c, q, "outw", 0, eve, 0400, dp);
		break;
	case 5:
		q = (Qid){path|Qoutl, c->qid.vers};
		devdir(c, q, "outl", 0, eve, 0400, dp);
		break;
	default:
		return -1;
	}
	return 1;

}
	
static void
pcidevreset(void)
{
}

void
pcidevinit(void)
{

	devinit();

}

static Chan*
pcidevattach(char* spec)
{
	return devattach('Z', spec);
}

int
pcidevwalk(Chan* c, char* name)
{
	return devwalk(c, name, 0,0 , pcidevgen);
}

static void
pcidevstat(Chan* c, char* dp)
{
	devstat(c, dp, pcitopdir, nelem(pcitopdir), devgen);
}

static Chan*
pcidevopen(Chan* c, int omode)
{
	return devopen(c, omode, pcitopdir, nelem(pcitopdir), devgen);
}




static long
pcidevread(Chan* c, void* a, long n, vlong)
{
	char str[16];
	int size = 0;
	ulong o;
	Mypci *cl;




	if(c->qid.path & CHDIR)
		return devdirread(c, a, n, 0, 0, pcidevgen);
	cl = pcislot(c);

	qlock(cl);
	if(waserror()){
		qunlock(cl);
		nexterror();
	}

	/* assume some things about 'a' that we probably shouldn't */
	switch(QID(c->qid)){
	case Qinb:
		size = sprint(str, "0x%2.2ux", inb(cl->base) & 0xFF);
		break;
	case Qinw:
		size = sprint(str, "0x%4.4ux", ins(cl->base) & 0xFFFF);
		break;
	case Qinl:
		size = sprint(str, "0x%8.8lux", inl(cl->base) & 0xFFFFFFFF);
		break;
	}

	qunlock(cl);
	poperror();

	o = c->offset;
	if(o >= size)
		return 0;
	if(o+n > size)
		n = size-c->offset;
	memmove(a, str+o, n);

	return n;
}


static long
pcidevwrite(Chan* c, void* a, long n, vlong off)
{
	Mypci *cl;
	ulong offset = off;


	USED(offset, n);
	if(c->qid.path & CHDIR)
		error(Eisdir);

	if(QID(c->qid) == Qbase) {
		if(devpci.npci < Maxpcidev) {
			devpci.pci[devpci.npci] = (Mypci *)malloc(sizeof(Mypci));
			devpci.pci[devpci.npci]->base = strtol(a, nil, 0);
			devpci.npci++;
			return 1;
		} else {
			error(Etoomany);
			return 0;
		}			
	}

	cl = pcislot(c);
	qlock(cl);
	switch(QID(c->qid)){
	case Qoutb:
		outb(PADDR(cl->base), atol(a) & 0xff);
		break;
	case Qoutw:
		outs(PADDR(cl->base), atol(a) & 0xffff);
		break;
	case Qoutl:
		outl(PADDR(cl->base), atol(a));
		break;
	}
	qunlock(cl);
	return 1;

}

static void
pcidevcreate(Chan *, char*, int, ulong)
{
}

static Chan *
pcidevclone(Chan *c1, Chan *c2)
{
	return devclone(c1, c2);
}

static void
pcidevremove(Chan *c)
{
	int slot;
	Mypci *cl;
	

	slot = CLIENTPATH(c->qid.path);

	if(slot == 0)
		return;	

	slot--;	/* align with pci[] */

	cl = devpci.pci[slot];

	free(cl);
	devpci.npci--;

	while (slot < devpci.npci) {
		devpci.pci[slot] = devpci.pci[slot+1];
		slot++;
	}
}

static void
pcidevclose(Chan *c) 
{
	USED(c);
}

Dev pcidevdevtab = {
	'Z',
	"pcidev",
	pcidevreset,
	devinit,
	pcidevattach,
	pcidevclone,
	pcidevwalk,
	pcidevstat,
	pcidevopen,
	pcidevcreate,
	pcidevclose,
	pcidevread,
	devbread,
	pcidevwrite,
	devbwrite,
	pcidevremove,
	devwstat,

};

             reply	other threads:[~2002-02-28  0:25 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2002-02-28  0:25 andrey mirtchovski [this message]
2002-02-28  0:28 David Gordon Hogan
2002-02-28  0:35 andrey mirtchovski
2002-02-28  0:49 David Gordon Hogan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20020228002424.49B1219AE0@mail.cse.psu.edu \
    --to=andrey@lanl.gov \
    --cc=9fans@cse.psu.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).