Commit 2f7dd07e authored by Finn Thain's avatar Finn Thain Committed by Geert Uytterhoeven

nubus: Rework /proc/bus/nubus/s/ implementation

The /proc/bus/nubus/s/ directory tree for any slot s is missing a lot
of information. The struct file_operations methods have long been left
unimplemented (hence the familiar compile-time warning, "Need to set
some I/O handlers here").

Slot resources have a complex structure which varies depending on board
function. The logic for interpreting these ROM data structures is found
in nubus.c. Let's not duplicate that logic in proc.c.

Create the /proc/bus/nubus/s/ inodes while scanning slot s. During
descent through slot resource subdirectories, call the new
nubus_proc_add_foo() functions to create the procfs inodes.

Also add a new function, nubus_seq_write_rsrc_mem(), to write the
contents of a particular slot resource to a given seq_file. This is
used by the procfs file_operations methods, to finally give userspace
access to slot ROM information, such as the available video modes.
Tested-by: default avatarStan Johnson <userm57@yahoo.com>
Signed-off-by: default avatarFinn Thain <fthain@telegraphics.com.au>
Signed-off-by: default avatarGeert Uytterhoeven <geert@linux-m68k.org>
parent 883b8cb3
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/setup.h> #include <asm/setup.h>
#include <asm/page.h> #include <asm/page.h>
...@@ -146,7 +147,7 @@ static inline void *nubus_rom_addr(int slot) ...@@ -146,7 +147,7 @@ static inline void *nubus_rom_addr(int slot)
return (void *)(0xF1000000 + (slot << 24)); return (void *)(0xF1000000 + (slot << 24));
} }
static unsigned char *nubus_dirptr(const struct nubus_dirent *nd) unsigned char *nubus_dirptr(const struct nubus_dirent *nd)
{ {
unsigned char *p = nd->base; unsigned char *p = nd->base;
...@@ -173,8 +174,8 @@ void nubus_get_rsrc_mem(void *dest, const struct nubus_dirent *dirent, ...@@ -173,8 +174,8 @@ void nubus_get_rsrc_mem(void *dest, const struct nubus_dirent *dirent,
} }
EXPORT_SYMBOL(nubus_get_rsrc_mem); EXPORT_SYMBOL(nubus_get_rsrc_mem);
void nubus_get_rsrc_str(char *dest, const struct nubus_dirent *dirent, unsigned int nubus_get_rsrc_str(char *dest, const struct nubus_dirent *dirent,
unsigned int len) unsigned int len)
{ {
char *t = dest; char *t = dest;
unsigned char *p = nubus_dirptr(dirent); unsigned char *p = nubus_dirptr(dirent);
...@@ -189,9 +190,33 @@ void nubus_get_rsrc_str(char *dest, const struct nubus_dirent *dirent, ...@@ -189,9 +190,33 @@ void nubus_get_rsrc_str(char *dest, const struct nubus_dirent *dirent,
} }
if (len > 0) if (len > 0)
*t = '\0'; *t = '\0';
return t - dest;
} }
EXPORT_SYMBOL(nubus_get_rsrc_str); EXPORT_SYMBOL(nubus_get_rsrc_str);
void nubus_seq_write_rsrc_mem(struct seq_file *m,
const struct nubus_dirent *dirent,
unsigned int len)
{
unsigned long buf[32];
unsigned int buf_size = sizeof(buf);
unsigned char *p = nubus_dirptr(dirent);
/* If possible, write out full buffers */
while (len >= buf_size) {
unsigned int i;
for (i = 0; i < ARRAY_SIZE(buf); i++)
buf[i] = nubus_get_rom(&p, sizeof(buf[0]),
dirent->mask);
seq_write(m, buf, buf_size);
len -= buf_size;
}
/* If not, write out individual bytes */
while (len--)
seq_putc(m, nubus_get_rom(&p, 1, dirent->mask));
}
int nubus_get_root_dir(const struct nubus_board *board, int nubus_get_root_dir(const struct nubus_board *board,
struct nubus_dir *dir) struct nubus_dir *dir)
{ {
...@@ -326,35 +351,35 @@ EXPORT_SYMBOL(nubus_find_rsrc); ...@@ -326,35 +351,35 @@ EXPORT_SYMBOL(nubus_find_rsrc);
looking at, and print out lots and lots of information from the looking at, and print out lots and lots of information from the
resource blocks. */ resource blocks. */
/* FIXME: A lot of this stuff will eventually be useful after
initialization, for intelligently probing Ethernet and video chips,
among other things. The rest of it should go in the /proc code.
For now, we just use it to give verbose boot logs. */
static int __init nubus_get_block_rsrc_dir(struct nubus_board *board, static int __init nubus_get_block_rsrc_dir(struct nubus_board *board,
struct proc_dir_entry *procdir,
const struct nubus_dirent *parent) const struct nubus_dirent *parent)
{ {
struct nubus_dir dir; struct nubus_dir dir;
struct nubus_dirent ent; struct nubus_dirent ent;
nubus_get_subdir(parent, &dir); nubus_get_subdir(parent, &dir);
dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board);
while (nubus_readdir(&dir, &ent) != -1) { while (nubus_readdir(&dir, &ent) != -1) {
u32 size; u32 size;
nubus_get_rsrc_mem(&size, &ent, 4); nubus_get_rsrc_mem(&size, &ent, 4);
pr_debug(" block (0x%x), size %d\n", ent.type, size); pr_debug(" block (0x%x), size %d\n", ent.type, size);
nubus_proc_add_rsrc_mem(dir.procdir, &ent, size);
} }
return 0; return 0;
} }
static int __init nubus_get_display_vidmode(struct nubus_board *board, static int __init nubus_get_display_vidmode(struct nubus_board *board,
struct proc_dir_entry *procdir,
const struct nubus_dirent *parent) const struct nubus_dirent *parent)
{ {
struct nubus_dir dir; struct nubus_dir dir;
struct nubus_dirent ent; struct nubus_dirent ent;
nubus_get_subdir(parent, &dir); nubus_get_subdir(parent, &dir);
dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board);
while (nubus_readdir(&dir, &ent) != -1) { while (nubus_readdir(&dir, &ent) != -1) {
switch (ent.type) { switch (ent.type) {
...@@ -366,37 +391,42 @@ static int __init nubus_get_display_vidmode(struct nubus_board *board, ...@@ -366,37 +391,42 @@ static int __init nubus_get_display_vidmode(struct nubus_board *board,
nubus_get_rsrc_mem(&size, &ent, 4); nubus_get_rsrc_mem(&size, &ent, 4);
pr_debug(" block (0x%x), size %d\n", ent.type, pr_debug(" block (0x%x), size %d\n", ent.type,
size); size);
nubus_proc_add_rsrc_mem(dir.procdir, &ent, size);
break; break;
} }
default: default:
pr_debug(" unknown resource 0x%02x, data 0x%06x\n", pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
ent.type, ent.data); ent.type, ent.data);
nubus_proc_add_rsrc_mem(dir.procdir, &ent, 0);
} }
} }
return 0; return 0;
} }
static int __init nubus_get_display_resource(struct nubus_dev *dev, static int __init nubus_get_display_resource(struct nubus_dev *dev,
struct proc_dir_entry *procdir,
const struct nubus_dirent *ent) const struct nubus_dirent *ent)
{ {
switch (ent->type) { switch (ent->type) {
case NUBUS_RESID_GAMMADIR: case NUBUS_RESID_GAMMADIR:
pr_debug(" gamma directory offset: 0x%06x\n", ent->data); pr_debug(" gamma directory offset: 0x%06x\n", ent->data);
nubus_get_block_rsrc_dir(dev->board, ent); nubus_get_block_rsrc_dir(dev->board, procdir, ent);
break; break;
case 0x0080 ... 0x0085: case 0x0080 ... 0x0085:
pr_debug(" mode 0x%02x info offset: 0x%06x\n", pr_debug(" mode 0x%02x info offset: 0x%06x\n",
ent->type, ent->data); ent->type, ent->data);
nubus_get_display_vidmode(dev->board, ent); nubus_get_display_vidmode(dev->board, procdir, ent);
break; break;
default: default:
pr_debug(" unknown resource 0x%02x, data 0x%06x\n", pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
ent->type, ent->data); ent->type, ent->data);
nubus_proc_add_rsrc_mem(procdir, ent, 0);
} }
return 0; return 0;
} }
static int __init nubus_get_network_resource(struct nubus_dev *dev, static int __init nubus_get_network_resource(struct nubus_dev *dev,
struct proc_dir_entry *procdir,
const struct nubus_dirent *ent) const struct nubus_dirent *ent)
{ {
switch (ent->type) { switch (ent->type) {
...@@ -406,16 +436,19 @@ static int __init nubus_get_network_resource(struct nubus_dev *dev, ...@@ -406,16 +436,19 @@ static int __init nubus_get_network_resource(struct nubus_dev *dev,
nubus_get_rsrc_mem(addr, ent, 6); nubus_get_rsrc_mem(addr, ent, 6);
pr_debug(" MAC address: %pM\n", addr); pr_debug(" MAC address: %pM\n", addr);
nubus_proc_add_rsrc_mem(procdir, ent, 6);
break; break;
} }
default: default:
pr_debug(" unknown resource 0x%02x, data 0x%06x\n", pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
ent->type, ent->data); ent->type, ent->data);
nubus_proc_add_rsrc_mem(procdir, ent, 0);
} }
return 0; return 0;
} }
static int __init nubus_get_cpu_resource(struct nubus_dev *dev, static int __init nubus_get_cpu_resource(struct nubus_dev *dev,
struct proc_dir_entry *procdir,
const struct nubus_dirent *ent) const struct nubus_dirent *ent)
{ {
switch (ent->type) { switch (ent->type) {
...@@ -426,6 +459,7 @@ static int __init nubus_get_cpu_resource(struct nubus_dev *dev, ...@@ -426,6 +459,7 @@ static int __init nubus_get_cpu_resource(struct nubus_dev *dev,
nubus_get_rsrc_mem(&meminfo, ent, 8); nubus_get_rsrc_mem(&meminfo, ent, 8);
pr_debug(" memory: [ 0x%08lx 0x%08lx ]\n", pr_debug(" memory: [ 0x%08lx 0x%08lx ]\n",
meminfo[0], meminfo[1]); meminfo[0], meminfo[1]);
nubus_proc_add_rsrc_mem(procdir, ent, 8);
break; break;
} }
case NUBUS_RESID_ROMINFO: case NUBUS_RESID_ROMINFO:
...@@ -435,31 +469,35 @@ static int __init nubus_get_cpu_resource(struct nubus_dev *dev, ...@@ -435,31 +469,35 @@ static int __init nubus_get_cpu_resource(struct nubus_dev *dev,
nubus_get_rsrc_mem(&rominfo, ent, 8); nubus_get_rsrc_mem(&rominfo, ent, 8);
pr_debug(" ROM: [ 0x%08lx 0x%08lx ]\n", pr_debug(" ROM: [ 0x%08lx 0x%08lx ]\n",
rominfo[0], rominfo[1]); rominfo[0], rominfo[1]);
nubus_proc_add_rsrc_mem(procdir, ent, 8);
break; break;
} }
default: default:
pr_debug(" unknown resource 0x%02x, data 0x%06x\n", pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
ent->type, ent->data); ent->type, ent->data);
nubus_proc_add_rsrc_mem(procdir, ent, 0);
} }
return 0; return 0;
} }
static int __init nubus_get_private_resource(struct nubus_dev *dev, static int __init nubus_get_private_resource(struct nubus_dev *dev,
struct proc_dir_entry *procdir,
const struct nubus_dirent *ent) const struct nubus_dirent *ent)
{ {
switch (dev->category) { switch (dev->category) {
case NUBUS_CAT_DISPLAY: case NUBUS_CAT_DISPLAY:
nubus_get_display_resource(dev, ent); nubus_get_display_resource(dev, procdir, ent);
break; break;
case NUBUS_CAT_NETWORK: case NUBUS_CAT_NETWORK:
nubus_get_network_resource(dev, ent); nubus_get_network_resource(dev, procdir, ent);
break; break;
case NUBUS_CAT_CPU: case NUBUS_CAT_CPU:
nubus_get_cpu_resource(dev, ent); nubus_get_cpu_resource(dev, procdir, ent);
break; break;
default: default:
pr_debug(" unknown resource 0x%02x, data 0x%06x\n", pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
ent->type, ent->data); ent->type, ent->data);
nubus_proc_add_rsrc_mem(procdir, ent, 0);
} }
return 0; return 0;
} }
...@@ -474,6 +512,7 @@ nubus_get_functional_resource(struct nubus_board *board, int slot, ...@@ -474,6 +512,7 @@ nubus_get_functional_resource(struct nubus_board *board, int slot,
pr_debug(" Functional resource 0x%02x:\n", parent->type); pr_debug(" Functional resource 0x%02x:\n", parent->type);
nubus_get_subdir(parent, &dir); nubus_get_subdir(parent, &dir);
dir.procdir = nubus_proc_add_rsrc_dir(board->procdir, parent, board);
/* Actually we should probably panic if this fails */ /* Actually we should probably panic if this fails */
if ((dev = kzalloc(sizeof(*dev), GFP_ATOMIC)) == NULL) if ((dev = kzalloc(sizeof(*dev), GFP_ATOMIC)) == NULL)
...@@ -495,14 +534,17 @@ nubus_get_functional_resource(struct nubus_board *board, int slot, ...@@ -495,14 +534,17 @@ nubus_get_functional_resource(struct nubus_board *board, int slot,
dev->dr_hw = nbtdata[3]; dev->dr_hw = nbtdata[3];
pr_debug(" type: [cat 0x%x type 0x%x sw 0x%x hw 0x%x]\n", pr_debug(" type: [cat 0x%x type 0x%x sw 0x%x hw 0x%x]\n",
nbtdata[0], nbtdata[1], nbtdata[2], nbtdata[3]); nbtdata[0], nbtdata[1], nbtdata[2], nbtdata[3]);
nubus_proc_add_rsrc_mem(dir.procdir, &ent, 8);
break; break;
} }
case NUBUS_RESID_NAME: case NUBUS_RESID_NAME:
{ {
char name[64]; char name[64];
unsigned int len;
nubus_get_rsrc_str(name, &ent, sizeof(name)); len = nubus_get_rsrc_str(name, &ent, sizeof(name));
pr_debug(" name: %s\n", name); pr_debug(" name: %s\n", name);
nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1);
break; break;
} }
case NUBUS_RESID_DRVRDIR: case NUBUS_RESID_DRVRDIR:
...@@ -511,7 +553,7 @@ nubus_get_functional_resource(struct nubus_board *board, int slot, ...@@ -511,7 +553,7 @@ nubus_get_functional_resource(struct nubus_board *board, int slot,
use this :-) */ use this :-) */
pr_debug(" driver directory offset: 0x%06x\n", pr_debug(" driver directory offset: 0x%06x\n",
ent.data); ent.data);
nubus_get_block_rsrc_dir(board, &ent); nubus_get_block_rsrc_dir(board, dir.procdir, &ent);
break; break;
} }
case NUBUS_RESID_MINOR_BASEOS: case NUBUS_RESID_MINOR_BASEOS:
...@@ -523,6 +565,7 @@ nubus_get_functional_resource(struct nubus_board *board, int slot, ...@@ -523,6 +565,7 @@ nubus_get_functional_resource(struct nubus_board *board, int slot,
nubus_get_rsrc_mem(&base_offset, &ent, 4); nubus_get_rsrc_mem(&base_offset, &ent, 4);
pr_debug(" memory offset: 0x%08x\n", base_offset); pr_debug(" memory offset: 0x%08x\n", base_offset);
nubus_proc_add_rsrc_mem(dir.procdir, &ent, 4);
break; break;
} }
case NUBUS_RESID_MINOR_LENGTH: case NUBUS_RESID_MINOR_LENGTH:
...@@ -532,18 +575,21 @@ nubus_get_functional_resource(struct nubus_board *board, int slot, ...@@ -532,18 +575,21 @@ nubus_get_functional_resource(struct nubus_board *board, int slot,
nubus_get_rsrc_mem(&length, &ent, 4); nubus_get_rsrc_mem(&length, &ent, 4);
pr_debug(" memory length: 0x%08x\n", length); pr_debug(" memory length: 0x%08x\n", length);
nubus_proc_add_rsrc_mem(dir.procdir, &ent, 4);
break; break;
} }
case NUBUS_RESID_FLAGS: case NUBUS_RESID_FLAGS:
pr_debug(" flags: 0x%06x\n", ent.data); pr_debug(" flags: 0x%06x\n", ent.data);
nubus_proc_add_rsrc(dir.procdir, &ent);
break; break;
case NUBUS_RESID_HWDEVID: case NUBUS_RESID_HWDEVID:
pr_debug(" hwdevid: 0x%06x\n", ent.data); pr_debug(" hwdevid: 0x%06x\n", ent.data);
nubus_proc_add_rsrc(dir.procdir, &ent);
break; break;
default: default:
/* Local/Private resources have their own /* Local/Private resources have their own
function */ function */
nubus_get_private_resource(dev, &ent); nubus_get_private_resource(dev, dir.procdir, &ent);
} }
} }
...@@ -552,6 +598,7 @@ nubus_get_functional_resource(struct nubus_board *board, int slot, ...@@ -552,6 +598,7 @@ nubus_get_functional_resource(struct nubus_board *board, int slot,
/* This is *really* cool. */ /* This is *really* cool. */
static int __init nubus_get_icon(struct nubus_board *board, static int __init nubus_get_icon(struct nubus_board *board,
struct proc_dir_entry *procdir,
const struct nubus_dirent *ent) const struct nubus_dirent *ent)
{ {
/* Should be 32x32 if my memory serves me correctly */ /* Should be 32x32 if my memory serves me correctly */
...@@ -564,11 +611,13 @@ static int __init nubus_get_icon(struct nubus_board *board, ...@@ -564,11 +611,13 @@ static int __init nubus_get_icon(struct nubus_board *board,
pr_debug(" %08x %08x %08x %08x\n", pr_debug(" %08x %08x %08x %08x\n",
icon[i * 4 + 0], icon[i * 4 + 1], icon[i * 4 + 0], icon[i * 4 + 1],
icon[i * 4 + 2], icon[i * 4 + 3]); icon[i * 4 + 2], icon[i * 4 + 3]);
nubus_proc_add_rsrc_mem(procdir, ent, 128);
return 0; return 0;
} }
static int __init nubus_get_vendorinfo(struct nubus_board *board, static int __init nubus_get_vendorinfo(struct nubus_board *board,
struct proc_dir_entry *procdir,
const struct nubus_dirent *parent) const struct nubus_dirent *parent)
{ {
struct nubus_dir dir; struct nubus_dir dir;
...@@ -578,15 +627,18 @@ static int __init nubus_get_vendorinfo(struct nubus_board *board, ...@@ -578,15 +627,18 @@ static int __init nubus_get_vendorinfo(struct nubus_board *board,
pr_debug(" vendor info:\n"); pr_debug(" vendor info:\n");
nubus_get_subdir(parent, &dir); nubus_get_subdir(parent, &dir);
dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board);
while (nubus_readdir(&dir, &ent) != -1) { while (nubus_readdir(&dir, &ent) != -1) {
char name[64]; char name[64];
unsigned int len;
/* These are all strings, we think */ /* These are all strings, we think */
nubus_get_rsrc_str(name, &ent, sizeof(name)); len = nubus_get_rsrc_str(name, &ent, sizeof(name));
if (ent.type < 1 || ent.type > 5) if (ent.type < 1 || ent.type > 5)
ent.type = 5; ent.type = 5;
pr_debug(" %s: %s\n", vendor_fields[ent.type - 1], name); pr_debug(" %s: %s\n", vendor_fields[ent.type - 1], name);
nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1);
} }
return 0; return 0;
} }
...@@ -599,6 +651,7 @@ static int __init nubus_get_board_resource(struct nubus_board *board, int slot, ...@@ -599,6 +651,7 @@ static int __init nubus_get_board_resource(struct nubus_board *board, int slot,
pr_debug(" Board resource 0x%02x:\n", parent->type); pr_debug(" Board resource 0x%02x:\n", parent->type);
nubus_get_subdir(parent, &dir); nubus_get_subdir(parent, &dir);
dir.procdir = nubus_proc_add_rsrc_dir(board->procdir, parent, board);
while (nubus_readdir(&dir, &ent) != -1) { while (nubus_readdir(&dir, &ent) != -1) {
switch (ent.type) { switch (ent.type) {
...@@ -615,49 +668,62 @@ static int __init nubus_get_board_resource(struct nubus_board *board, int slot, ...@@ -615,49 +668,62 @@ static int __init nubus_get_board_resource(struct nubus_board *board, int slot,
nbtdata[2] != 0 || nbtdata[3] != 0) nbtdata[2] != 0 || nbtdata[3] != 0)
pr_err("Slot %X: sResource is not a board resource!\n", pr_err("Slot %X: sResource is not a board resource!\n",
slot); slot);
nubus_proc_add_rsrc_mem(dir.procdir, &ent, 8);
break; break;
} }
case NUBUS_RESID_NAME: case NUBUS_RESID_NAME:
nubus_get_rsrc_str(board->name, &ent, {
sizeof(board->name)); unsigned int len;
len = nubus_get_rsrc_str(board->name, &ent,
sizeof(board->name));
pr_debug(" name: %s\n", board->name); pr_debug(" name: %s\n", board->name);
nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1);
break; break;
}
case NUBUS_RESID_ICON: case NUBUS_RESID_ICON:
nubus_get_icon(board, &ent); nubus_get_icon(board, dir.procdir, &ent);
break; break;
case NUBUS_RESID_BOARDID: case NUBUS_RESID_BOARDID:
pr_debug(" board id: 0x%x\n", ent.data); pr_debug(" board id: 0x%x\n", ent.data);
nubus_proc_add_rsrc(dir.procdir, &ent);
break; break;
case NUBUS_RESID_PRIMARYINIT: case NUBUS_RESID_PRIMARYINIT:
pr_debug(" primary init offset: 0x%06x\n", ent.data); pr_debug(" primary init offset: 0x%06x\n", ent.data);
nubus_proc_add_rsrc(dir.procdir, &ent);
break; break;
case NUBUS_RESID_VENDORINFO: case NUBUS_RESID_VENDORINFO:
nubus_get_vendorinfo(board, &ent); nubus_get_vendorinfo(board, dir.procdir, &ent);
break; break;
case NUBUS_RESID_FLAGS: case NUBUS_RESID_FLAGS:
pr_debug(" flags: 0x%06x\n", ent.data); pr_debug(" flags: 0x%06x\n", ent.data);
nubus_proc_add_rsrc(dir.procdir, &ent);
break; break;
case NUBUS_RESID_HWDEVID: case NUBUS_RESID_HWDEVID:
pr_debug(" hwdevid: 0x%06x\n", ent.data); pr_debug(" hwdevid: 0x%06x\n", ent.data);
nubus_proc_add_rsrc(dir.procdir, &ent);
break; break;
case NUBUS_RESID_SECONDINIT: case NUBUS_RESID_SECONDINIT:
pr_debug(" secondary init offset: 0x%06x\n", pr_debug(" secondary init offset: 0x%06x\n",
ent.data); ent.data);
nubus_proc_add_rsrc(dir.procdir, &ent);
break; break;
/* WTF isn't this in the functional resources? */ /* WTF isn't this in the functional resources? */
case NUBUS_RESID_VIDNAMES: case NUBUS_RESID_VIDNAMES:
pr_debug(" vidnames directory offset: 0x%06x\n", pr_debug(" vidnames directory offset: 0x%06x\n",
ent.data); ent.data);
nubus_get_block_rsrc_dir(board, &ent); nubus_get_block_rsrc_dir(board, dir.procdir, &ent);
break; break;
/* Same goes for this */ /* Same goes for this */
case NUBUS_RESID_VIDMODES: case NUBUS_RESID_VIDMODES:
pr_debug(" video mode parameter directory offset: 0x%06x\n", pr_debug(" video mode parameter directory offset: 0x%06x\n",
ent.data); ent.data);
nubus_proc_add_rsrc(dir.procdir, &ent);
break; break;
default: default:
pr_debug(" unknown resource 0x%02x, data 0x%06x\n", pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
ent.type, ent.data); ent.type, ent.data);
nubus_proc_add_rsrc_mem(dir.procdir, &ent, 0);
} }
} }
return 0; return 0;
...@@ -748,6 +814,8 @@ static struct nubus_board * __init nubus_add_board(int slot, int bytelanes) ...@@ -748,6 +814,8 @@ static struct nubus_board * __init nubus_add_board(int slot, int bytelanes)
if (ent.type < 1 || ent.type > 127) if (ent.type < 1 || ent.type > 127)
pr_warn("Slot %X: Board resource ID is invalid!\n", slot); pr_warn("Slot %X: Board resource ID is invalid!\n", slot);
board->procdir = nubus_proc_add_board(board);
nubus_get_board_resource(board, slot, &ent); nubus_get_board_resource(board, slot, &ent);
while (nubus_readdir(&dir, &ent) != -1) { while (nubus_readdir(&dir, &ent) != -1) {
...@@ -835,8 +903,8 @@ static int __init nubus_init(void) ...@@ -835,8 +903,8 @@ static int __init nubus_init(void)
if (!MACH_IS_MAC) if (!MACH_IS_MAC)
return 0; return 0;
nubus_scan_bus();
nubus_proc_init(); nubus_proc_init();
nubus_scan_bus();
return 0; return 0;
} }
......
...@@ -11,24 +11,28 @@ ...@@ -11,24 +11,28 @@
structure in /proc analogous to the structure of the NuBus ROM structure in /proc analogous to the structure of the NuBus ROM
resources. resources.
Therefore each NuBus device is in fact a directory, which may in Therefore each board function gets a directory, which may in turn
turn contain subdirectories. The "files" correspond to NuBus contain subdirectories. Each slot resource is a file. Unrecognized
resource records. For those types of records which we know how to resources are empty files, since every resource ID requires a special
convert to formats that are meaningful to userspace (mostly just case (e.g. if the resource ID implies a directory or block, then its
icons) these files will provide "cooked" data. Otherwise they will value has to be interpreted as a slot ROM pointer etc.).
simply provide raw access (read-only of course) to the ROM. */ */
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/nubus.h> #include <linux/nubus.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
/*
* /proc/bus/nubus/devices stuff
*/
static int static int
nubus_devices_proc_show(struct seq_file *m, void *v) nubus_devices_proc_show(struct seq_file *m, void *v)
{ {
...@@ -61,96 +65,141 @@ static const struct file_operations nubus_devices_proc_fops = { ...@@ -61,96 +65,141 @@ static const struct file_operations nubus_devices_proc_fops = {
static struct proc_dir_entry *proc_bus_nubus_dir; static struct proc_dir_entry *proc_bus_nubus_dir;
static const struct file_operations nubus_proc_subdir_fops = { /*
#warning Need to set some I/O handlers here * /proc/bus/nubus/x/ stuff
*/
struct proc_dir_entry *nubus_proc_add_board(struct nubus_board *board)
{
char name[2];
if (!proc_bus_nubus_dir)
return NULL;
snprintf(name, sizeof(name), "%x", board->slot);
return proc_mkdir(name, proc_bus_nubus_dir);
}
/* The PDE private data for any directory under /proc/bus/nubus/x/
* is the bytelanes value for the board in slot x.
*/
struct proc_dir_entry *nubus_proc_add_rsrc_dir(struct proc_dir_entry *procdir,
const struct nubus_dirent *ent,
struct nubus_board *board)
{
char name[9];
int lanes = board->lanes;
if (!procdir)
return NULL;
snprintf(name, sizeof(name), "%x", ent->type);
return proc_mkdir_data(name, 0555, procdir, (void *)lanes);
}
/* The PDE private data for a file under /proc/bus/nubus/x/ is a pointer to
* an instance of the following structure, which gives the location and size
* of the resource data in the slot ROM. For slot resources which hold only a
* small integer, this integer value is stored directly and size is set to 0.
* A NULL private data pointer indicates an unrecognized resource.
*/
struct nubus_proc_pde_data {
unsigned char *res_ptr;
unsigned int res_size;
}; };
static void nubus_proc_subdir(struct nubus_dev* dev, static struct nubus_proc_pde_data *
struct proc_dir_entry* parent, nubus_proc_alloc_pde_data(unsigned char *ptr, unsigned int size)
struct nubus_dir* dir)
{ {
struct nubus_dirent ent; struct nubus_proc_pde_data *pde_data;
/* Some of these are directories, others aren't */ pde_data = kmalloc(sizeof(*pde_data), GFP_KERNEL);
while (nubus_readdir(dir, &ent) != -1) { if (!pde_data)
char name[9]; return NULL;
struct proc_dir_entry* e;
pde_data->res_ptr = ptr;
snprintf(name, sizeof(name), "%x", ent.type); pde_data->res_size = size;
e = proc_create(name, S_IFREG | S_IRUGO | S_IWUSR, parent, return pde_data;
&nubus_proc_subdir_fops);
if (!e)
return;
}
} }
/* Can't do this recursively since the root directory is structured static int nubus_proc_rsrc_show(struct seq_file *m, void *v)
somewhat differently from the subdirectories */
static void nubus_proc_populate(struct nubus_dev* dev,
struct proc_dir_entry* parent,
struct nubus_dir* root)
{ {
struct nubus_dirent ent; struct inode *inode = m->private;
struct nubus_proc_pde_data *pde_data;
/* We know these are all directories (board resource + one or
more functional resources) */ pde_data = PDE_DATA(inode);
while (nubus_readdir(root, &ent) != -1) { if (!pde_data)
char name[9]; return 0;
struct proc_dir_entry* e;
struct nubus_dir dir; if (pde_data->res_size > m->size)
return -EFBIG;
snprintf(name, sizeof(name), "%x", ent.type);
e = proc_mkdir(name, parent); if (pde_data->res_size) {
if (!e) return; int lanes = (int)proc_get_parent_data(inode);
struct nubus_dirent ent;
/* And descend */
if (nubus_get_subdir(&ent, &dir) == -1) { if (!lanes)
/* This shouldn't happen */ return 0;
printk(KERN_ERR "NuBus root directory node %x:%x has no subdir!\n",
dev->board->slot, ent.type); ent.mask = lanes;
continue; ent.base = pde_data->res_ptr;
} else { ent.data = 0;
nubus_proc_subdir(dev, e, &dir); nubus_seq_write_rsrc_mem(m, &ent, pde_data->res_size);
} } else {
unsigned int data = (unsigned int)pde_data->res_ptr;
seq_putc(m, data >> 16);
seq_putc(m, data >> 8);
seq_putc(m, data >> 0);
} }
return 0;
} }
int nubus_proc_attach_device(struct nubus_dev *dev) static int nubus_proc_rsrc_open(struct inode *inode, struct file *file)
{
return single_open(file, nubus_proc_rsrc_show, inode);
}
static const struct file_operations nubus_proc_rsrc_fops = {
.open = nubus_proc_rsrc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void nubus_proc_add_rsrc_mem(struct proc_dir_entry *procdir,
const struct nubus_dirent *ent,
unsigned int size)
{ {
struct proc_dir_entry *e;
struct nubus_dir root;
char name[9]; char name[9];
struct nubus_proc_pde_data *pde_data;
if (dev == NULL) { if (!procdir)
printk(KERN_ERR return;
"NULL pointer in nubus_proc_attach_device, shoot the programmer!\n");
return -1;
}
if (dev->board == NULL) {
printk(KERN_ERR
"NULL pointer in nubus_proc_attach_device, shoot the programmer!\n");
printk("dev = %p, dev->board = %p\n", dev, dev->board);
return -1;
}
if (dev->board->procdir)
return 0;
/* Create a directory */ snprintf(name, sizeof(name), "%x", ent->type);
snprintf(name, sizeof(name), "%x", dev->board->slot); if (size)
e = proc_mkdir(name, proc_bus_nubus_dir); pde_data = nubus_proc_alloc_pde_data(nubus_dirptr(ent), size);
dev->board->procdir = e; else
if (!e) pde_data = NULL;
return -ENOMEM; proc_create_data(name, S_IFREG | 0444, procdir,
&nubus_proc_rsrc_fops, pde_data);
}
/* Now recursively populate it with files */ void nubus_proc_add_rsrc(struct proc_dir_entry *procdir,
nubus_get_root_dir(dev->board, &root); const struct nubus_dirent *ent)
nubus_proc_populate(dev, e, &root); {
char name[9];
unsigned char *data = (unsigned char *)ent->data;
return 0; if (!procdir)
return;
snprintf(name, sizeof(name), "%x", ent->type);
proc_create_data(name, S_IFREG | 0444, procdir,
&nubus_proc_rsrc_fops,
nubus_proc_alloc_pde_data(data, 0));
} }
EXPORT_SYMBOL(nubus_proc_attach_device);
/* /*
* /proc/nubus stuff * /proc/nubus stuff
...@@ -219,18 +268,11 @@ static const struct file_operations nubus_proc_fops = { ...@@ -219,18 +268,11 @@ static const struct file_operations nubus_proc_fops = {
.release = seq_release, .release = seq_release,
}; };
void __init proc_bus_nubus_add_devices(void)
{
struct nubus_dev *dev;
for(dev = nubus_devices; dev; dev = dev->next)
nubus_proc_attach_device(dev);
}
void __init nubus_proc_init(void) void __init nubus_proc_init(void)
{ {
proc_create("nubus", 0, NULL, &nubus_proc_fops); proc_create("nubus", 0, NULL, &nubus_proc_fops);
proc_bus_nubus_dir = proc_mkdir("bus/nubus", NULL); proc_bus_nubus_dir = proc_mkdir("bus/nubus", NULL);
if (!proc_bus_nubus_dir)
return;
proc_create("devices", 0, proc_bus_nubus_dir, &nubus_devices_proc_fops); proc_create("devices", 0, proc_bus_nubus_dir, &nubus_devices_proc_fops);
proc_bus_nubus_add_devices();
} }
...@@ -13,11 +13,15 @@ ...@@ -13,11 +13,15 @@
#include <asm/nubus.h> #include <asm/nubus.h>
#include <uapi/linux/nubus.h> #include <uapi/linux/nubus.h>
struct proc_dir_entry;
struct seq_file;
struct nubus_dir { struct nubus_dir {
unsigned char *base; unsigned char *base;
unsigned char *ptr; unsigned char *ptr;
int done; int done;
int mask; int mask;
struct proc_dir_entry *procdir;
}; };
struct nubus_dirent { struct nubus_dirent {
...@@ -84,12 +88,33 @@ extern struct nubus_board *nubus_boards; ...@@ -84,12 +88,33 @@ extern struct nubus_board *nubus_boards;
/* Generic NuBus interface functions, modelled after the PCI interface */ /* Generic NuBus interface functions, modelled after the PCI interface */
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
extern void nubus_proc_init(void); void nubus_proc_init(void);
struct proc_dir_entry *nubus_proc_add_board(struct nubus_board *board);
struct proc_dir_entry *nubus_proc_add_rsrc_dir(struct proc_dir_entry *procdir,
const struct nubus_dirent *ent,
struct nubus_board *board);
void nubus_proc_add_rsrc_mem(struct proc_dir_entry *procdir,
const struct nubus_dirent *ent,
unsigned int size);
void nubus_proc_add_rsrc(struct proc_dir_entry *procdir,
const struct nubus_dirent *ent);
#else #else
static inline void nubus_proc_init(void) {} static inline void nubus_proc_init(void) {}
static inline
struct proc_dir_entry *nubus_proc_add_board(struct nubus_board *board)
{ return NULL; }
static inline
struct proc_dir_entry *nubus_proc_add_rsrc_dir(struct proc_dir_entry *procdir,
const struct nubus_dirent *ent,
struct nubus_board *board)
{ return NULL; }
static inline void nubus_proc_add_rsrc_mem(struct proc_dir_entry *procdir,
const struct nubus_dirent *ent,
unsigned int size) {}
static inline void nubus_proc_add_rsrc(struct proc_dir_entry *procdir,
const struct nubus_dirent *ent) {}
#endif #endif
int nubus_proc_attach_device(struct nubus_dev *dev);
/* If we need more precision we can add some more of these */ /* If we need more precision we can add some more of these */
struct nubus_dev *nubus_find_type(unsigned short category, struct nubus_dev *nubus_find_type(unsigned short category,
unsigned short type, unsigned short type,
...@@ -125,8 +150,12 @@ int nubus_get_subdir(const struct nubus_dirent *ent, ...@@ -125,8 +150,12 @@ int nubus_get_subdir(const struct nubus_dirent *ent,
struct nubus_dir *dir); struct nubus_dir *dir);
void nubus_get_rsrc_mem(void *dest, const struct nubus_dirent *dirent, void nubus_get_rsrc_mem(void *dest, const struct nubus_dirent *dirent,
unsigned int len); unsigned int len);
void nubus_get_rsrc_str(char *dest, const struct nubus_dirent *dirent, unsigned int nubus_get_rsrc_str(char *dest, const struct nubus_dirent *dirent,
unsigned int maxlen); unsigned int len);
void nubus_seq_write_rsrc_mem(struct seq_file *m,
const struct nubus_dirent *dirent,
unsigned int len);
unsigned char *nubus_dirptr(const struct nubus_dirent *nd);
/* Returns a pointer to the "standard" slot space. */ /* Returns a pointer to the "standard" slot space. */
static inline void *nubus_slot_addr(int slot) static inline void *nubus_slot_addr(int slot)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment