📄 sdladrv.c
字号:
int sdla_intde (sdlahw_t* hw){ unsigned port = hw->port; int tmp, i; switch (hw->type) { case SDLA_S502E: /* Notes: * 1) interrupt control operations are allowed only if CPU is * enabled (bit 0 of status register is set). * 2) disabling interrupts using bit 1 of control register * causes IRQ line go high, therefore we are going to use * 0x04 instead: lower it to inhibit interrupts to PC. */ if (_INB(port) & 0x01) { _OUTB(port, hw->regs[0] & ~0x04); hw->regs[0] &= ~0x04; } else return -EIO; break; case SDLA_S503: tmp = hw->regs[0] & ~0x04; _OUTB(port, tmp); hw->regs[0] = tmp; /* update mirror */ for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (_INB(port) & 0x02) /* verify */ return -EIO ; break; case SDLA_S508: tmp = hw->regs[0] & ~0x10; _OUTB(port, tmp); hw->regs[0] = tmp; /* update mirror */ for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (_INB(port) & 0x10) /* verify */ return -EIO ; break; case SDLA_S502A: case SDLA_S507: break; default: return -EINVAL; } return 0;}/*============================================================================ * Acknowledge SDLA hardware interrupt. */EXPORT_SYMBOL(sdla_intack);int sdla_intack (sdlahw_t* hw){ unsigned port = hw->port; int tmp; switch (hw->type) { case SDLA_S502E: /* To acknoledge hardware interrupt we have to toggle bit 3 of * control register: \_/ * Note that interrupt control operations on S502E are allowed * only if CPU is enabled (bit 1 of status register is set). */ if (_INB(port) & 0x01) { tmp = hw->regs[0] & ~0x04; _OUTB(port, tmp); tmp |= 0x04; _OUTB(port, tmp); hw->regs[0] = tmp; } else return -EIO; break; case SDLA_S503: if (_INB(port) & 0x04) { tmp = hw->regs[0] & ~0x08; _OUTB(port, tmp); tmp |= 0x08; _OUTB(port, tmp); hw->regs[0] = tmp; } break; case SDLA_S502A: case SDLA_S507: case SDLA_S508: break; default: return -EINVAL; } return 0;}/*============================================================================ * Generate an interrupt to adapter's CPU. */EXPORT_SYMBOL(sdla_intr);int sdla_intr (sdlahw_t* hw){ unsigned port = hw->port; switch (hw->type) { case SDLA_S502A: if (!(_INB(port) & 0x40)) { _OUTB(port, 0x10); /* issue NMI to CPU */ hw->regs[0] = 0x10; } else return -EIO; break; case SDLA_S507: if ((_INB(port) & 0x06) == 0x06) { _OUTB(port + 3, 0); } else return -EIO; break; case SDLA_S508: if (_INB(port + 1) & 0x02) { _OUTB(port, 0x08); } else return -EIO; break; case SDLA_S502E: case SDLA_S503: default: return -EINVAL; } return 0;}/*============================================================================ * Execute Adapter Command. * o Set exec flag. * o Busy-wait until flag is reset. * o Return number of loops made, or 0 if command timed out. */EXPORT_SYMBOL(sdla_exec);int sdla_exec (void* opflag){ volatile unsigned char* flag = opflag; unsigned long tstop; int nloops; if (*flag) return 0; /* ???? */ *flag = 1; tstop = SYSTEM_TICK + EXEC_TIMEOUT; for (nloops = 1; *flag; ++nloops) { unsigned delay = exec_idle; while (--delay); /* delay */ if (SYSTEM_TICK > tstop) return 0; /* time is up! */ } return nloops;}/*============================================================================ * Read absolute adapter memory. * Transfer data from adapter's memory to data buffer. * * Note: * Care should be taken when crossing dual-port memory window boundary. * This function is not atomic, so caller must disable interrupt if * interrupt routines are accessing adapter shared memory. */ EXPORT_SYMBOL(sdla_peek);int sdla_peek (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len){ unsigned long oldvec = hw->vector; unsigned winsize = hw->dpmsize; unsigned curpos, curlen; /* current offset and block size */ unsigned long curvec; /* current DPM window vector */ int err = 0; if (addr + len > hw->memory) /* verify arguments */ return -EINVAL ; while (len && !err) { curpos = addr % winsize; /* current window offset */ curvec = addr - curpos; /* current window vector */ curlen = (len > (winsize - curpos)) ? (winsize - curpos) : len; /* Relocate window and copy block of data */ err = sdla_mapmem(hw, curvec); memcpy(buf, (void *)((u8 *)hw->dpmbase + curpos), curlen); addr += curlen; (char*)buf += curlen; len -= curlen; } /* Restore DPM window position */ sdla_mapmem(hw, oldvec); return err;}/*============================================================================ * Write Absolute Adapter Memory. * Transfer data from data buffer to adapter's memory. * * Note: * Care should be taken when crossing dual-port memory window boundary. * This function is not atomic, so caller must disable interrupt if * interrupt routines are accessing adapter shared memory. */ EXPORT_SYMBOL(sdla_poke); int sdla_poke (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len){ unsigned long oldvec = hw->vector; unsigned winsize = hw->dpmsize; unsigned curpos, curlen; /* current offset and block size */ unsigned long curvec; /* current DPM window vector */ int err = 0; if (addr + len > hw->memory) /* verify arguments */ return -EINVAL ; while (len && !err) { curpos = addr % winsize; /* current window offset */ curvec = addr - curpos; /* current window vector */ curlen = (len > (winsize - curpos)) ? (winsize - curpos) : len; /* Relocate window and copy block of data */ sdla_mapmem(hw, curvec); memcpy((void*)((u8 *)hw->dpmbase + curpos), buf, curlen); addr += curlen; (char*)buf += curlen; len -= curlen; } /* Restore DPM window position */ sdla_mapmem(hw, oldvec); return err;}#ifdef DONT_COMPIPLE_THIS#endif /* DONT_COMPIPLE_THIS *//****** Hardware-Specific Functions *****************************************//*============================================================================ * Detect adapter type. * o if adapter type is specified then call detection routine for that adapter * type. Otherwise call detection routines for every adapter types until * adapter is detected. * * Notes: * 1) Detection tests are destructive! Adapter will be left in shutdown state * after the test. */static int sdla_detect (sdlahw_t* hw){ unsigned port = hw->port; int err = 0; if (!port) return -EFAULT ; switch (hw->type) { case SDLA_S502A: if (!detect_s502a(port)) err = -ENODEV; break; case SDLA_S502E: if (!detect_s502e(port)) err = -ENODEV; break; case SDLA_S503: if (!detect_s503(port)) err = -ENODEV; break; case SDLA_S507: if (!detect_s507(port)) err = -ENODEV; break; case SDLA_S508: if (!detect_s508(port)) err = -ENODEV; break; default: if (detect_s502a(port)) hw->type = SDLA_S502A ; else if (detect_s502e(port)) hw->type = SDLA_S502E ; else if (detect_s503(port)) hw->type = SDLA_S503 ; else if (detect_s507(port)) hw->type = SDLA_S507 ; else if (detect_s508(port)) hw->type = SDLA_S508 ; else err = -ENODEV; } return err;}/*============================================================================ * Autoselect memory region. * o try all available DMP address options from the top down until success. */static int sdla_autodpm (sdlahw_t* hw){ int i, err = -EINVAL; unsigned* opt; switch (hw->type) { case SDLA_S502A: opt = s502a_dpmbase_options; break; case SDLA_S502E: case SDLA_S503: case SDLA_S508: opt = s508_dpmbase_options; break; case SDLA_S507: opt = s507_dpmbase_options; break; default: return -EINVAL; } for (i = opt[0]; i && err; --i) { hw->dpmbase = phys_to_virt(opt[i]); err = sdla_setdpm(hw); } return err;}/*============================================================================ * Set up adapter dual-port memory window. * o shut down adapter * o make sure that no physical memory exists in this region, i.e entire * region reads 0xFF and is not writable when adapter is shut down. * o initialize adapter hardware * o make sure that region is usable with SDLA card, i.e. we can write to it * when adapter is configured. */static int sdla_setdpm (sdlahw_t* hw){ int err; /* Shut down card and verify memory region */ sdla_down(hw); if (check_memregion(hw->dpmbase, hw->dpmsize)) return -EINVAL ; /* Initialize adapter and test on-board memory segment by segment. * If memory size appears to be less than shared memory window size, * assume that memory region is unusable. */ err = sdla_init(hw); if (err) return err; if (sdla_memtest(hw) < hw->dpmsize) /* less than window size */ { sdla_down(hw); return -EIO; } sdla_mapmem(hw, 0L); /* set window vector at bottom */ return 0;}/*============================================================================ * Load adapter from the memory image of the SDLA firmware module. * o verify firmware integrity and compatibility * o start adapter up */static int sdla_load (sdlahw_t* hw, sfm_t* sfm, unsigned len){ int i; /* Verify firmware signature */ if (strcmp(sfm->signature, SFM_SIGNATURE)) { printk(KERN_ERR "%s: not SDLA firmware!\n", modname) ; return -EINVAL; } /* Verify firmware module format version */ if (sfm->version != SFM_VERSION) { printk(KERN_ERR "%s: firmware format %u rejected! Expecting %u.\n", modname, sfm->version, SFM_VERSION) ; return -EINVAL; } /* Verify firmware module length and checksum */ if ((len - offsetof(sfm_t, image) != sfm->info.codesize) || (checksum((void*)&sfm->info, sizeof(sfm_info_t) + sfm->info.codesize) != sfm->checksum)) { printk(KERN_ERR "%s: firmware corrupted!\n", modname); return -EINVAL; } /* Announce */ printk(KERN_INFO "%s: loading %s (ID=%u)...\n", modname, (sfm->descr[0] != '\0') ? sfm->descr : "unknown firmware", sfm->info.codeid) ; /* Scan through the list of compatible adapters and make sure our * adapter type is listed. */ for (i = 0; (i < SFM_MAX_SDLA) && (sfm->info.adapter[i] != hw->type); ++i) ; if (i == SFM_MAX_SDLA) { printk(KERN_ERR "%s: firmware is not compatible with S%u!\n", modname, hw->type) ; return -EINVAL; } /* Make sure there is enough on-board memory */ if (hw->memory < sfm->info.memsize) { printk(KERN_ERR "%s: firmware needs %lu bytes of on-board memory!\n", modname, sfm->info.memsize) ; return -EINVAL; } /* Move code onto adapter */ if (sdla_poke(hw, sfm->info.codeoffs, sfm->image, sfm->info.codesize)) { printk(KERN_ERR "%s: failed to load code segment!\n", modname) ; return -EIO; } /* Prepare boot-time configuration data and kick-off CPU */ sdla_bootcfg(hw, &sfm->info); if (sdla_start(hw, sfm->info.startoffs)) { printk(KERN_ERR "%s: Damn... Adapter won't start!\n", modname) ; return -EIO; } /* position DPM window over the mailbox and enable interrupts */ if (sdla_mapmem(hw, sfm->info.winoffs) || sdla_inten(hw)) { printk(KERN_ERR "%s: adapter hardware failure!\n", modname) ; return -EIO; } hw->fwid = sfm->info.codeid; /* set firmware ID */ return 0;}/*============================================================================ * Initialize SDLA hardware: setup memory window, IRQ, etc. */static int sdla_init (sdlahw_t* hw){ int i; for (i = 0; i < SDLA_MAXIORANGE; ++i) hw->regs[i] = 0 ; switch (hw->type) { case SDLA_S502A: return init_s502a(hw); case SDLA_S502E: return init_s502e(hw); case SDLA_S503: return init_s503(hw); case SDLA_S507: return init_s507(hw); case SDLA_S508: return init_s508(hw); } return -EINVAL;}/*============================================================================ * Test adapter on-board memory. * o slide DPM window from the bottom up and test adapter memory segment by * segment. * Return adapter memory size. */static unsigned long sdla_memtest (sdlahw_t* hw){ unsigned long memsize; unsigned winsize; for (memsize = 0, winsize = hw->dpmsize; !sdla_mapmem(hw, memsize) && (test_memregion(hw->dpmbase, winsize) == winsize) ; memsize += winsize) ; hw->memory = memsize; return memsize;}/*============================================================================ * Prepare boot-time firmware configuration data. * o position DPM window * o initialize configuration data area */static int sdla_bootcfg (sdlahw_t* hw, sfm_info_t* sfminfo){ unsigned char* data; if (!sfminfo->datasize) return 0; /* nothing to do */ if (sdla_mapmem(hw, sfminfo->dataoffs) != 0) return -EIO ; data = (void*)((u8 *)hw->dpmbase + (sfminfo->dataoffs - hw->vector)); memset(data, 0, sfminfo->datasize); data[0x00] = make_config_byte(hw); switch (sfminfo->codeid) { case SFID_X25_502: case SFID_X25_508: data[0x01] = 3; /* T1 timer */ data[0x03] = 10; /* N2 */ data[0x06] = 7; /* HDLC window size */ data[0x0B] = 1; /* DTE */ data[0x0C] = 2; /* X.25 packet window size */ *(short*)&data[0x0D] = 128; /* default X.25 data size */ *(short*)&data[0x0F] = 128; /* maximum X.25 data size */ break; } return 0;}/*============================================================================ * Prepare configuration byte identifying adapter type and CPU clock rate. */static unsigned char make_config_byte (sdlahw_t* hw){ unsigned char byte = 0; switch (hw->pclk) { case 5000: byte = 0x01; break; case 7200: byte = 0x02; break; case 8000: byte = 0x03; break; case 10000: byte = 0x04; break; case 16000: byte = 0x05; break; } switch (hw->type) { case SDLA_S502E: byte |= 0x80; break; case SDLA_S503: byte |= 0x40; break; } return byte;}/*============================================================================ * Start adapter's CPU. * o calculate a pointer to adapter's cold boot entry point * o position DPM window * o place boot instruction (jp addr) at cold boot entry point * o start CPU */static int sdla_start (sdlahw_t* hw, unsigned addr){ unsigned port = hw->port; unsigned char *bootp; int err, tmp, i; if (!port) return -EFAULT; switch (hw->type) { case SDLA_S502A: bootp = hw->dpmbase; bootp += 0x66; break; case SDLA_S502E:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -