📄 nubus.c
字号:
/* Ditto */ nubus_get_rsrc_mem(&dev->iosize, &ent, 4); printk(KERN_INFO " memory length: 0x%08lx\n", dev->iosize); break; case NUBUS_RESID_FLAGS: dev->flags = ent.data; printk(KERN_INFO " flags: 0x%06x\n", dev->flags); break; case NUBUS_RESID_HWDEVID: dev->hwdevid = ent.data; printk(KERN_INFO " hwdevid: 0x%06x\n", dev->hwdevid); break; default: /* Local/Private resources have their own function */ nubus_show_private_resource(dev, &ent); } } return dev;}/* This is cool. */static int __init nubus_get_vidnames(struct nubus_board* board, const struct nubus_dirent* parent){ struct nubus_dir dir; struct nubus_dirent ent; /* FIXME: obviously we want to put this in a header file soon */ struct vidmode { u32 size; /* Don't know what this is yet */ u16 id; /* Longest one I've seen so far is 26 characters */ char name[32]; }; printk(KERN_INFO " video modes supported:\n"); nubus_get_subdir(parent, &dir); if (console_loglevel >= 10) printk(KERN_DEBUG "nubus_get_vidnames: parent is 0x%p, dir is 0x%p\n", parent->base, dir.base); while(nubus_readdir(&dir, &ent) != -1) { struct vidmode mode; u32 size; /* First get the length */ nubus_get_rsrc_mem(&size, &ent, 4); /* Now clobber the whole thing */ if (size > sizeof(mode) - 1) size = sizeof(mode) - 1; memset(&mode, 0, sizeof(mode)); nubus_get_rsrc_mem(&mode, &ent, size); printk (KERN_INFO " %02X: (%02X) %s\n", ent.type, mode.id, mode.name); } return 0;}/* This is *really* cool. */static int __init nubus_get_icon(struct nubus_board* board, const struct nubus_dirent* ent){ /* Should be 32x32 if my memory serves me correctly */ unsigned char icon[128]; int x, y; nubus_get_rsrc_mem(&icon, ent, 128); printk(KERN_INFO " icon:\n"); /* We should actually plot these somewhere in the framebuffer init. This is just to demonstrate that they do, in fact, exist */ for (y = 0; y < 32; y++) { printk(KERN_INFO " "); for (x = 0; x < 32; x++) { if (icon[y*4 + x/8] & (0x80 >> (x%8))) printk("*"); else printk(" "); } printk("\n"); } return 0;}static int __init nubus_get_vendorinfo(struct nubus_board* board, const struct nubus_dirent* parent){ struct nubus_dir dir; struct nubus_dirent ent; static char* vendor_fields[6] = {"ID", "serial", "revision", "part", "date", "unknown field"}; printk(KERN_INFO " vendor info:\n"); nubus_get_subdir(parent, &dir); if (console_loglevel >= 10) printk(KERN_DEBUG "nubus_get_vendorinfo: parent is 0x%p, dir is 0x%p\n", parent->base, dir.base); while(nubus_readdir(&dir, &ent) != -1) { char name[64]; /* These are all strings, we think */ nubus_get_rsrc_str(name, &ent, 64); if (ent.type > 5) ent.type = 5; printk(KERN_INFO " %s: %s\n", vendor_fields[ent.type-1], name); } return 0;}static int __init nubus_get_board_resource(struct nubus_board* board, int slot, const struct nubus_dirent* parent){ struct nubus_dir dir; struct nubus_dirent ent; nubus_get_subdir(parent, &dir); if (console_loglevel >= 10) printk(KERN_DEBUG "nubus_get_board_resource: parent is 0x%p, dir is 0x%p\n", parent->base, dir.base); while(nubus_readdir(&dir, &ent) != -1) { switch (ent.type) { case NUBUS_RESID_TYPE: { unsigned short nbtdata[4]; /* This type is always the same, and is not useful except insofar as it tells us that we really are looking at a board resource. */ nubus_get_rsrc_mem(nbtdata, &ent, 8); printk(KERN_INFO " type: [cat 0x%x type 0x%x hw 0x%x sw 0x%x]\n", nbtdata[0], nbtdata[1], nbtdata[2], nbtdata[3]); if (nbtdata[0] != 1 || nbtdata[1] != 0 || nbtdata[2] != 0 || nbtdata[3] != 0) printk(KERN_ERR "this sResource is not a board resource!\n"); break; } case NUBUS_RESID_NAME: nubus_get_rsrc_str(board->name, &ent, 64); printk(KERN_INFO " name: %s\n", board->name); break; case NUBUS_RESID_ICON: nubus_get_icon(board, &ent); break; case NUBUS_RESID_BOARDID: printk(KERN_INFO " board id: 0x%x\n", ent.data); break; case NUBUS_RESID_PRIMARYINIT: printk(KERN_INFO " primary init offset: 0x%06x\n", ent.data); break; case NUBUS_RESID_VENDORINFO: nubus_get_vendorinfo(board, &ent); break; case NUBUS_RESID_FLAGS: printk(KERN_INFO " flags: 0x%06x\n", ent.data); break; case NUBUS_RESID_HWDEVID: printk(KERN_INFO " hwdevid: 0x%06x\n", ent.data); break; case NUBUS_RESID_SECONDINIT: printk(KERN_INFO " secondary init offset: 0x%06x\n", ent.data); break; /* WTF isn't this in the functional resources? */ case NUBUS_RESID_VIDNAMES: nubus_get_vidnames(board, &ent); break; /* Same goes for this */ case NUBUS_RESID_VIDMODES: printk(KERN_INFO " video mode parameter directory offset: 0x%06x\n", ent.data); break; default: printk(KERN_INFO " unknown resource %02X, data 0x%06x\n", ent.type, ent.data); } } return 0;}/* Attempt to bypass the somewhat non-obvious arrangement of sResources in the motherboard ROM */static void __init nubus_find_rom_dir(struct nubus_board* board){ unsigned char* rp; unsigned char* romdir; struct nubus_dir dir; struct nubus_dirent ent; /* Check for the extra directory just under the format block */ rp = board->fblock; nubus_rewind(&rp, 4, board->lanes); if (nubus_get_rom(&rp, 4, board->lanes) != NUBUS_TEST_PATTERN) { /* OK, the ROM was telling the truth */ board->directory = board->fblock; nubus_move(&board->directory, nubus_expand32(board->doffset), board->lanes); return; } /* On "slot zero", you have to walk down a few more directories to get to the equivalent of a real card's root directory. We don't know what they were smoking when they came up with this. */ romdir = nubus_rom_addr(board->slot); nubus_rewind(&romdir, ROM_DIR_OFFSET, board->lanes); dir.base = dir.ptr = romdir; dir.done = 0; dir.mask = board->lanes; /* This one points to an "Unknown Macintosh" directory */ if (nubus_readdir(&dir, &ent) == -1) goto badrom; if (console_loglevel >= 10) printk(KERN_INFO "nubus_get_rom_dir: entry %02x %06x\n", ent.type, ent.data); /* This one takes us to where we want to go. */ if (nubus_readdir(&dir, &ent) == -1) goto badrom; if (console_loglevel >= 10) printk(KERN_DEBUG "nubus_get_rom_dir: entry %02x %06x\n", ent.type, ent.data); nubus_get_subdir(&ent, &dir); /* Resource ID 01, also an "Unknown Macintosh" */ if (nubus_readdir(&dir, &ent) == -1) goto badrom; if (console_loglevel >= 10) printk(KERN_DEBUG "nubus_get_rom_dir: entry %02x %06x\n", ent.type, ent.data); /* FIXME: the first one is *not* always the right one. We suspect this has something to do with the ROM revision. "The HORROR ROM" (LC-series) uses 0x7e, while "The HORROR Continues" (Q630) uses 0x7b. The DAFB Macs evidently use something else. Please run "Slots" on your Mac (see include/linux/nubus.h for where to get this program) and tell us where the 'SiDirPtr' for Slot 0 is. If you feel brave, you should also use MacsBug to walk down the ROM directories like this function does and try to find the path to that address... */ if (nubus_readdir(&dir, &ent) == -1) goto badrom; if (console_loglevel >= 10) printk(KERN_DEBUG "nubus_get_rom_dir: entry %02x %06x\n", ent.type, ent.data); /* Bwahahahaha... */ nubus_get_subdir(&ent, &dir); board->directory = dir.base; return; /* Even more evil laughter... */ badrom: board->directory = board->fblock; nubus_move(&board->directory, nubus_expand32(board->doffset), board->lanes); printk(KERN_ERR "nubus_get_rom_dir: ROM weirdness! Notify the developers...\n");}/* Add a board (might be many devices) to the list */static struct nubus_board* __init nubus_add_board(int slot, int bytelanes){ struct nubus_board* board; struct nubus_board** boardp; unsigned char *rp; unsigned long dpat; struct nubus_dir dir; struct nubus_dirent ent; /* Move to the start of the format block */ rp = nubus_rom_addr(slot); nubus_rewind(&rp, FORMAT_BLOCK_SIZE, bytelanes); /* Actually we should probably panic if this fails */ if ((board = kmalloc(sizeof(*board), GFP_ATOMIC)) == NULL) return NULL; memset(board, 0, sizeof(*board)); board->fblock = rp; /* Dump the format block for debugging purposes */ if (console_loglevel >= 10) { int i; printk(KERN_DEBUG "Slot %X, format block at 0x%p\n", slot, rp); printk(KERN_DEBUG "Format block: "); for (i = 0; i < FORMAT_BLOCK_SIZE; i += 4) { unsigned short foo, bar; foo = nubus_get_rom(&rp, 2, bytelanes); bar = nubus_get_rom(&rp, 2, bytelanes); printk("%04x %04x ", foo, bar); } printk("\n"); rp = board->fblock; } board->slot = slot; board->slot_addr = (unsigned long) nubus_slot_addr(slot); board->doffset = nubus_get_rom(&rp, 4, bytelanes); /* rom_length is *supposed* to be the total length of the * ROM. In practice it is the "amount of ROM used to compute * the CRC." So some jokers decide to set it to zero and * set the crc to zero so they don't have to do any math. * See the Performa 460 ROM, for example. Those Apple "engineers". */ board->rom_length = nubus_get_rom(&rp, 4, bytelanes); board->crc = nubus_get_rom(&rp, 4, bytelanes); board->rev = nubus_get_rom(&rp, 1, bytelanes); board->format = nubus_get_rom(&rp,1, bytelanes); board->lanes = bytelanes; /* Directory offset should be small and negative... */ if(!(board->doffset & 0x00FF0000)) printk(KERN_WARNING "Dodgy doffset!\n"); dpat = nubus_get_rom(&rp, 4, bytelanes); if(dpat != NUBUS_TEST_PATTERN) printk(KERN_WARNING "Wrong test pattern %08lx!\n", dpat); /* * I wonder how the CRC is meant to work - * any takers ? * CSA: According to MAC docs, not all cards pass the CRC anyway, * since the initial Macintosh ROM releases skipped the check. */ /* Attempt to work around slot zero weirdness */ nubus_find_rom_dir(board); nubus_get_root_dir(board, &dir); /* We're ready to rock */ printk(KERN_INFO "Slot %X:\n", slot); /* Each slot should have one board resource and any number of functional resources. So we'll fill in some fields in the struct nubus_board from the board resource, then walk down the list of functional resources, spinning out a nubus_dev for each of them. */ if (nubus_readdir(&dir, &ent) == -1) { /* We can't have this! */ printk(KERN_ERR "Board resource not found!\n"); return NULL; } else { printk(KERN_INFO " Board resource:\n"); nubus_get_board_resource(board, slot, &ent); } /* Aaaarrrrgghh! The LC III motherboard has *two* board resources. I have no idea WTF to do about this. */ while (nubus_readdir(&dir, &ent) != -1) { struct nubus_dev* dev; struct nubus_dev** devp; dev = nubus_get_functional_resource(board, slot, &ent); if (dev == NULL) continue; /* We zeroed this out above */ if (board->first_dev == NULL) board->first_dev = dev; /* Put it on the global NuBus device chain. Keep entries in order. */ for (devp=&nubus_devices; *devp!=NULL; devp=&((*devp)->next)) /* spin */; *devp = dev; dev->next = NULL; } /* Put it on the global NuBus board chain. Keep entries in order. */ for (boardp=&nubus_boards; *boardp!=NULL; boardp=&((*boardp)->next)) /* spin */; *boardp = board; board->next = NULL; return board;}void __init nubus_probe_slot(int slot){ unsigned char dp; unsigned char* rp; int i; rp = nubus_rom_addr(slot); for(i = 4; i; i--) { unsigned long flags; int card_present; rp--; save_flags(flags); cli(); card_present = hwreg_present(rp); restore_flags(flags); if (!card_present) continue; printk(KERN_DEBUG "Now probing slot %X at %p\n", slot, rp); dp = *rp; if(dp == 0) continue; /* The last byte of the format block consists of two nybbles which are "mirror images" of each other. These show us the valid bytelanes */ if ((((dp>>4) ^ dp) & 0x0F) != 0x0F) continue; /* Check that this value is actually *on* one of the bytelanes it claims are valid! */ if ((dp & 0x0F) >= (1<<i)) continue; /* Looks promising. Let's put it on the list. */ nubus_add_board(slot, dp); return; }}#if defined(CONFIG_PROC_FS)/* /proc/nubus stuff */static int sprint_nubus_board(struct nubus_board* board, char* ptr, int len){ if(len < 100) return -1; sprintf(ptr, "Slot %X: %s\n", board->slot, board->name); return strlen(ptr);}static int nubus_read_proc(char *buf, char **start, off_t off, int count, int *eof, void *data){ int nprinted, len, begin = 0; int slot,size; struct nubus_board* board; len = sprintf(buf, "Nubus devices found:\n"); /* Walk the list of NuBus boards */ for (board = nubus_boards; board != NULL; board = board->next) { nprinted = sprint_nubus_board(board, buf + len, size - len); if (nprinted < 0) break; len += nprinted; if (len+begin < off) { begin += len; len = 0; } if (len+begin >= off+count) break; } if (slot==16 || len+begin < off) *eof = 1; off -= begin; *start = buf + off; len -= off; if (len>count) len = count; if (len<0) len = 0; return len;}#endifvoid __init nubus_scan_bus(void){ int slot; /* This might not work on your machine */#ifdef I_WANT_TO_PROBE_SLOT_ZERO nubus_probe_slot(0);#endif for(slot = 9; slot < 15; slot++) { nubus_probe_slot(slot); }}void __init nubus_init(void){ if (!MACH_IS_MAC) return; /* Initialize the NuBus interrupts */ if (oss_present) { oss_nubus_init(); } else { via_nubus_init(); }#ifdef TRY_TO_DODGE_WSOD /* Rogue Ethernet interrupts can kill the machine if we don't do this. Obviously this is bogus. Hopefully the local VIA gurus can fix the real cause of the problem. */ mdelay(1000);#endif /* And probe */ printk("NuBus: Scanning NuBus slots.\n"); nubus_devices = NULL; nubus_boards = NULL; nubus_scan_bus();#ifdef CONFIG_PROC_FS create_proc_read_entry("nubus", 0, NULL, nubus_read_proc, NULL); nubus_proc_init();#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -