⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hermes.c

📁 pcmcia source code
💻 C
字号:
/* hermes.c * * Driver core for the "Hermes" wireless MAC controller, as used in * the Lucent Orinoco and Cabletron RoamAbout cards. It should also * work on the hfa3841 and hfa3842 MAC controller chips used in the * Prism II chipsets. * * This is not a complete driver, just low-level access routines for * the MAC controller itself. * * Based on the prism2 driver from Absolute Value Systems' linux-wlan * project, the Linux wvlan_cs driver, Lucent's HCF-Light * (wvlan_hcf.c) library, and the NetBSD wireless driver (in no * particular order). * * Copyright (C) 2000, David Gibson, Linuxcare Australia <hermes@gibson.dropbear.id.au> * Copyright (C) 2001, David Gibson, IBM <hermes@gibson.dropbear.id.au> *  * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License version 2 (the "GPL"), in * which case the provisions of the GPL are applicable instead of the * above.  If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the GPL.  If you do not delete the * provisions above, a recipient may use your version of this file * under either the MPL or the GPL. */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/threads.h>#include <linux/smp.h>#include <asm/io.h>#include <linux/ptrace.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/kernel.h>#include <asm/errno.h>#include "hermes.h"static char version[] __initdata = "hermes.c: 5 Apr 2002 David Gibson <hermes@gibson.dropbear.id.au>";MODULE_DESCRIPTION("Low-level driver helper for Lucent Hermes chipset and Prism II HFA384x wireless MAC controller");MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");#ifdef MODULE_LICENSEMODULE_LICENSE("Dual MPL/GPL");#endif/* These are maximum timeouts. Most often, card wil react much faster */#define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */#define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */#define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */#define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us *//* * Debugging helpers */#undef HERMES_DEBUG#ifdef HERMES_DEBUG#include <stdarg.h>#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ 0x%x: " , hw->iobase); \			printk(stuff);} while (0)#define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(stuff)#else /* ! HERMES_DEBUG */#define DEBUG(lvl, stuff...) do { } while (0)#endif /* ! HERMES_DEBUG */#define IO_TYPE(hw)	((hw)->io_space ? "IO " : "MEM ")/* * Internal functions *//* Issue a command to the chip. Waiting for it to complete is the caller's   problem.   Returns -EBUSY if the command register is busy, 0 on success.   Callable from any context.*/static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0){	int k = CMD_BUSY_TIMEOUT;	u16 reg;	/* First wait for the command register to unbusy */	reg = hermes_read_regn(hw, CMD);	while ( (reg & HERMES_CMD_BUSY) && k ) {		k--;		udelay(1);		reg = hermes_read_regn(hw, CMD);	}	DEBUG(3, "hermes_issue_cmd: did %d retries.\n", CMD_BUSY_TIMEOUT-k);	if (reg & HERMES_CMD_BUSY) {		return -EBUSY;	}	hermes_write_regn(hw, PARAM2, 0);	hermes_write_regn(hw, PARAM1, 0);	hermes_write_regn(hw, PARAM0, param0);	hermes_write_regn(hw, CMD, cmd);		return 0;}/* * Function definitions */void hermes_struct_init(hermes_t *hw, ulong address,			int io_space, int reg_spacing){	hw->iobase = address;	hw->io_space = io_space;	hw->reg_spacing = reg_spacing;	hw->inten = 0x0;#ifdef HERMES_DEBUG_BUFFER	hw->dbufp = 0;	memset(&hw->dbuf, 0xff, sizeof(hw->dbuf));	memset(&hw->profile, 0, sizeof(hw->profile));#endif}int hermes_reset(hermes_t *hw){	u16 status, reg;	int err = 0;	int k;	/* We don't want to be interrupted while resetting the chipset */	hw->inten = 0x0;	hermes_write_regn(hw, INTEN, 0);	hermes_write_regn(hw, EVACK, 0xffff);	/* Normally it's a "can't happen" for the command register to           be busy when we go to issue a command because we are           serializing all commands.  However we want to have some           chance of resetting the card even if it gets into a stupid           state, so we actually wait to see if the command register           will unbusy itself here. */	k = CMD_BUSY_TIMEOUT;	reg = hermes_read_regn(hw, CMD);	while (k && (reg & HERMES_CMD_BUSY)) {		if (reg == 0xffff) /* Special case - the card has probably been removed,				      so don't wait for the timeout */			return -ENODEV;		k--;		udelay(1);		reg = hermes_read_regn(hw, CMD);	}		/* No need to explicitly handle the timeout - if we've timed	   out hermes_issue_cmd() will probably return -EBUSY below */	/* According to the documentation, EVSTAT may contain	   obsolete event occurrence information.  We have to acknowledge	   it by writing EVACK. */	reg = hermes_read_regn(hw, EVSTAT);	hermes_write_regn(hw, EVACK, reg);	/* We don't use hermes_docmd_wait here, because the reset wipes	   the magic constant in SWSUPPORT0 away, and it gets confused */	err = hermes_issue_cmd(hw, HERMES_CMD_INIT, 0);	if (err)		return err;	reg = hermes_read_regn(hw, EVSTAT);	k = CMD_INIT_TIMEOUT;	while ( (! (reg & HERMES_EV_CMD)) && k) {		k--;		udelay(10);		reg = hermes_read_regn(hw, EVSTAT);	}	DEBUG(0, "Reset completed in %d iterations\n", CMD_INIT_TIMEOUT - k);	hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC);	if (! hermes_present(hw)) {		DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n",		       hw->iobase);		err = -ENODEV;		goto out;	}			if (! (reg & HERMES_EV_CMD)) {		printk(KERN_ERR "hermes @ %s0x%lx: " 		       "Timeout waiting for card to reset (reg=0x%04x)!\n",		       IO_TYPE(hw), hw->iobase, reg);		err = -ETIMEDOUT;		goto out;	}	status = hermes_read_regn(hw, STATUS);	hermes_write_regn(hw, EVACK, HERMES_EV_CMD);	if (status & HERMES_STATUS_RESULT)		err = -EIO; out:	return err;}/* Issue a command to the chip, and (busy!) wait for it to * complete. * * Returns: < 0 on internal error, 0 on success, > 0 on error returned by the firmware * * Callable from any context, but locking is your problem. */int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp){	int err;	int k;	u16 reg;	u16 status;	err = hermes_issue_cmd(hw, cmd, parm0);	if (err) {		if (! hermes_present(hw)) {			printk(KERN_WARNING "hermes @ %s0x%lx: "			       "Card removed while issuing command.\n",			       IO_TYPE(hw), hw->iobase);			err = -ENODEV;		} else 			printk(KERN_ERR "hermes @ %s0x%lx: Error %d issuing command.\n",			       IO_TYPE(hw), hw->iobase, err);		goto out;	}	reg = hermes_read_regn(hw, EVSTAT);	k = CMD_COMPL_TIMEOUT;	while ( (! (reg & HERMES_EV_CMD)) && k) {		k--;		udelay(10);		reg = hermes_read_regn(hw, EVSTAT);	}	if (! hermes_present(hw)) {		printk(KERN_WARNING "hermes @ %s0x%lx: "		       "Card removed while waiting for command completion.\n",		       IO_TYPE(hw), hw->iobase);		err = -ENODEV;		goto out;	}			if (! (reg & HERMES_EV_CMD)) {		printk(KERN_ERR "hermes @ %s0x%lx: "		       "Timeout waiting for command completion.\n",		       IO_TYPE(hw), hw->iobase);		err = -ETIMEDOUT;		goto out;	}	status = hermes_read_regn(hw, STATUS);	if (resp) {		resp->status = status;		resp->resp0 = hermes_read_regn(hw, RESP0);		resp->resp1 = hermes_read_regn(hw, RESP1);		resp->resp2 = hermes_read_regn(hw, RESP2);	}	hermes_write_regn(hw, EVACK, HERMES_EV_CMD);	if (status & HERMES_STATUS_RESULT)		err = -EIO; out:	return err;}int hermes_allocate(hermes_t *hw, u16 size, u16 *fid){	int err = 0;	int k;	u16 reg;		if ( (size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX) )		return -EINVAL;	err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, NULL);	if (err) {		printk(KERN_WARNING "hermes @ %s0x%lx: "		       "Frame allocation command failed (0x%X).\n",		       IO_TYPE(hw), hw->iobase, err);		return err;	}	reg = hermes_read_regn(hw, EVSTAT);	k = ALLOC_COMPL_TIMEOUT;	while ( (! (reg & HERMES_EV_ALLOC)) && k) {		k--;		udelay(10);		reg = hermes_read_regn(hw, EVSTAT);	}		if (! hermes_present(hw)) {		printk(KERN_WARNING "hermes @ %s0x%lx: "		       "Card removed waiting for frame allocation.\n",		       IO_TYPE(hw), hw->iobase);		return -ENODEV;	}			if (! (reg & HERMES_EV_ALLOC)) {		printk(KERN_ERR "hermes @ %s0x%lx: "		       "Timeout waiting for frame allocation\n",		       IO_TYPE(hw), hw->iobase);		return -ETIMEDOUT;	}	*fid = hermes_read_regn(hw, ALLOCFID);	hermes_write_regn(hw, EVACK, HERMES_EV_ALLOC);		return 0;}/* Set up a BAP to read a particular chunk of data from card's internal buffer. * * Returns: < 0 on internal failure (errno), 0 on success, >0 on error * from firmware * * Callable from any context */static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset){	int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0;	int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0;	int k;	u16 reg;	/* Paranoia.. */	if ( (offset > HERMES_BAP_OFFSET_MAX) || (offset % 2) )		return -EINVAL;	k = HERMES_BAP_BUSY_TIMEOUT;	reg = hermes_read_reg(hw, oreg);	while ((reg & HERMES_OFFSET_BUSY) && k) {		k--;		udelay(1);		reg = hermes_read_reg(hw, oreg);	}#ifdef HERMES_DEBUG_BUFFER	hw->profile[HERMES_BAP_BUSY_TIMEOUT - k]++;	if (k < HERMES_BAP_BUSY_TIMEOUT) {		struct hermes_debug_entry *e = 			&hw->dbuf[(hw->dbufp++) % HERMES_DEBUG_BUFSIZE];		e->bap = bap;		e->id = id;		e->offset = offset;		e->cycles = HERMES_BAP_BUSY_TIMEOUT - k;	}#endif	if (reg & HERMES_OFFSET_BUSY)		return -ETIMEDOUT;	/* Now we actually set up the transfer */	hermes_write_reg(hw, sreg, id);	hermes_write_reg(hw, oreg, offset);	/* Wait for the BAP to be ready */	k = HERMES_BAP_BUSY_TIMEOUT;	reg = hermes_read_reg(hw, oreg);	while ( (reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) {		k--;		udelay(1);		reg = hermes_read_reg(hw, oreg);	}	if (reg & HERMES_OFFSET_BUSY) {		DEBUG(1,"hermes_bap_seek: timeout\n");		return -ETIMEDOUT;	}	if (reg & HERMES_OFFSET_ERR) {		DEBUG(1,"hermes_bap_seek: BAP error\n");		return -EIO;	}	return 0;}/* Read a block of data from the chip's buffer, via the * BAP. Synchronization/serialization is the caller's problem.  len * must be even. * * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware */int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,		     u16 id, u16 offset){	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;	int err = 0;	if ( (len < 0) || (len % 2) )		return -EINVAL;	err = hermes_bap_seek(hw, bap, id, offset);	if (err)		goto out;	/* Actually do the transfer */	hermes_read_words(hw, dreg, buf, len/2); out:	return err;}/* Write a block of data to the chip's buffer, via the * BAP. Synchronization/serialization is the caller's problem. len * must be even. * * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware */int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,		      u16 id, u16 offset){	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;	int err = 0;	if ( (len < 0) || (len % 2) )		return -EINVAL;	err = hermes_bap_seek(hw, bap, id, offset);	if (err)		goto out;		/* Actually do the transfer */	hermes_write_words(hw, dreg, buf, len/2); out:		return err;}/* Read a Length-Type-Value record from the card. * * If length is NULL, we ignore the length read from the card, and * read the entire buffer regardless. This is useful because some of * the configuration records appear to have incorrect lengths in * practice. * * Callable from user or bh context.  */int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize,		    u16 *length, void *buf){	int err = 0;	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;	u16 rlength, rtype;	if ( (bufsize < 0) || (bufsize % 2) )		return -EINVAL;	err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL);	if (err)		goto out;	err = hermes_bap_seek(hw, bap, rid, 0);	if (err)		goto out;	rlength = hermes_read_reg(hw, dreg);	rtype = hermes_read_reg(hw, dreg);	if (length)		*length = rlength;	if (rtype != rid)		printk(KERN_WARNING "hermes @ %s0x%lx: "		       "hermes_read_ltv(): rid  (0x%04x) does not match type (0x%04x)\n",		       IO_TYPE(hw), hw->iobase, rid, rtype);	if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize)		printk(KERN_WARNING "hermes @ %s0x%lx: "		       "Truncating LTV record from %d to %d bytes. "		       "(rid=0x%04x, len=0x%04x)\n",		       IO_TYPE(hw), hw->iobase,		       HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength);		/* FIXME: we should read the min of the requested length and           the actual record length */	hermes_read_words(hw, dreg, buf, bufsize / 2); out:	return err;}int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, 		     u16 length, const void *value){	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;	int err = 0;	int count;		DEBUG(3, "write_ltv(): bap=%d rid=0x%04x length=%d (value=0x%04x)\n",	      bap, rid, length, * ((u16 *)value));	err = hermes_bap_seek(hw, bap, rid, 0);	if (err)		goto out;	hermes_write_reg(hw, dreg, length);	hermes_write_reg(hw, dreg, rid);	count = length - 1;	hermes_write_words(hw, dreg, value, count);	err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, 				rid, NULL); out:	return err;}EXPORT_SYMBOL(hermes_struct_init);EXPORT_SYMBOL(hermes_reset);EXPORT_SYMBOL(hermes_docmd_wait);EXPORT_SYMBOL(hermes_allocate);EXPORT_SYMBOL(hermes_bap_pread);EXPORT_SYMBOL(hermes_bap_pwrite);EXPORT_SYMBOL(hermes_read_ltv);EXPORT_SYMBOL(hermes_write_ltv);static int __init init_hermes(void){	printk(KERN_DEBUG "%s\n", version);	return 0;}module_init(init_hermes);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -