|
|
1.1 ! root 1: /* hermes.c ! 2: * ! 3: * Driver core for the "Hermes" wireless MAC controller, as used in ! 4: * the Lucent Orinoco and Cabletron RoamAbout cards. It should also ! 5: * work on the hfa3841 and hfa3842 MAC controller chips used in the ! 6: * Prism II chipsets. ! 7: * ! 8: * This is not a complete driver, just low-level access routines for ! 9: * the MAC controller itself. ! 10: * ! 11: * Based on the prism2 driver from Absolute Value Systems' linux-wlan ! 12: * project, the Linux wvlan_cs driver, Lucent's HCF-Light ! 13: * (wvlan_hcf.c) library, and the NetBSD wireless driver (in no ! 14: * particular order). ! 15: * ! 16: * Copyright (C) 2000, David Gibson, Linuxcare Australia <[email protected]> ! 17: * Copyright (C) 2001, David Gibson, IBM <[email protected]> ! 18: * ! 19: * The contents of this file are subject to the Mozilla Public License ! 20: * Version 1.1 (the "License"); you may not use this file except in ! 21: * compliance with the License. You may obtain a copy of the License ! 22: * at http://www.mozilla.org/MPL/ ! 23: * ! 24: * Software distributed under the License is distributed on an "AS IS" ! 25: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See ! 26: * the License for the specific language governing rights and ! 27: * limitations under the License. ! 28: * ! 29: * Alternatively, the contents of this file may be used under the ! 30: * terms of the GNU General Public License version 2 (the "GPL"), in ! 31: * which case the provisions of the GPL are applicable instead of the ! 32: * above. If you wish to allow the use of your version of this file ! 33: * only under the terms of the GPL and not to allow others to use your ! 34: * version of this file under the MPL, indicate your decision by ! 35: * deleting the provisions above and replace them with the notice and ! 36: * other provisions required by the GPL. If you do not delete the ! 37: * provisions above, a recipient may use your version of this file ! 38: * under either the MPL or the GPL. ! 39: */ ! 40: ! 41: #include <linux/config.h> ! 42: ! 43: #include <linux/module.h> ! 44: #include <linux/types.h> ! 45: #include <linux/threads.h> ! 46: #include <linux/smp.h> ! 47: #include <asm/io.h> ! 48: #include <linux/delay.h> ! 49: #include <linux/init.h> ! 50: #include <linux/kernel.h> ! 51: #include <asm/errno.h> ! 52: ! 53: #include "hermes.h" ! 54: ! 55: MODULE_DESCRIPTION("Low-level driver helper for Lucent Hermes chipset and Prism II HFA384x wireless MAC controller"); ! 56: MODULE_AUTHOR("David Gibson <[email protected]>"); ! 57: #ifdef MODULE_LICENSE ! 58: MODULE_LICENSE("Dual MPL/GPL"); ! 59: #endif ! 60: ! 61: /* These are maximum timeouts. Most often, card wil react much faster */ ! 62: #define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */ ! 63: #define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */ ! 64: #define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */ ! 65: #define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */ ! 66: ! 67: /* ! 68: * Debugging helpers ! 69: */ ! 70: ! 71: #define IO_TYPE(hw) ((hw)->io_space ? "IO " : "MEM ") ! 72: #define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ %s0x%x: " , IO_TYPE(hw), hw->iobase); \ ! 73: printk(stuff);} while (0) ! 74: ! 75: #undef HERMES_DEBUG ! 76: #ifdef HERMES_DEBUG ! 77: #include <stdarg.h> ! 78: ! 79: #define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(stuff) ! 80: ! 81: #else /* ! HERMES_DEBUG */ ! 82: ! 83: #define DEBUG(lvl, stuff...) do { } while (0) ! 84: ! 85: #endif /* ! HERMES_DEBUG */ ! 86: ! 87: ! 88: /* ! 89: * Internal functions ! 90: */ ! 91: ! 92: /* Issue a command to the chip. Waiting for it to complete is the caller's ! 93: problem. ! 94: ! 95: Returns -EBUSY if the command register is busy, 0 on success. ! 96: ! 97: Callable from any context. ! 98: */ ! 99: static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0) ! 100: { ! 101: int k = CMD_BUSY_TIMEOUT; ! 102: u16 reg; ! 103: ! 104: /* First wait for the command register to unbusy */ ! 105: reg = hermes_read_regn(hw, CMD); ! 106: while ( (reg & HERMES_CMD_BUSY) && k ) { ! 107: k--; ! 108: udelay(1); ! 109: reg = hermes_read_regn(hw, CMD); ! 110: } ! 111: if (reg & HERMES_CMD_BUSY) { ! 112: return -EBUSY; ! 113: } ! 114: ! 115: hermes_write_regn(hw, PARAM2, 0); ! 116: hermes_write_regn(hw, PARAM1, 0); ! 117: hermes_write_regn(hw, PARAM0, param0); ! 118: hermes_write_regn(hw, CMD, cmd); ! 119: ! 120: return 0; ! 121: } ! 122: ! 123: /* ! 124: * Function definitions ! 125: */ ! 126: ! 127: void hermes_struct_init(hermes_t *hw, ulong address, ! 128: int io_space, int reg_spacing) ! 129: { ! 130: hw->iobase = address; ! 131: hw->io_space = io_space; ! 132: hw->reg_spacing = reg_spacing; ! 133: hw->inten = 0x0; ! 134: ! 135: #ifdef HERMES_DEBUG_BUFFER ! 136: hw->dbufp = 0; ! 137: memset(&hw->dbuf, 0xff, sizeof(hw->dbuf)); ! 138: memset(&hw->profile, 0, sizeof(hw->profile)); ! 139: #endif ! 140: } ! 141: ! 142: int hermes_init(hermes_t *hw) ! 143: { ! 144: u16 status, reg; ! 145: int err = 0; ! 146: int k; ! 147: ! 148: /* We don't want to be interrupted while resetting the chipset */ ! 149: hw->inten = 0x0; ! 150: hermes_write_regn(hw, INTEN, 0); ! 151: hermes_write_regn(hw, EVACK, 0xffff); ! 152: ! 153: /* Normally it's a "can't happen" for the command register to ! 154: be busy when we go to issue a command because we are ! 155: serializing all commands. However we want to have some ! 156: chance of resetting the card even if it gets into a stupid ! 157: state, so we actually wait to see if the command register ! 158: will unbusy itself here. */ ! 159: k = CMD_BUSY_TIMEOUT; ! 160: reg = hermes_read_regn(hw, CMD); ! 161: while (k && (reg & HERMES_CMD_BUSY)) { ! 162: if (reg == 0xffff) /* Special case - the card has probably been removed, ! 163: so don't wait for the timeout */ ! 164: return -ENODEV; ! 165: ! 166: k--; ! 167: udelay(1); ! 168: reg = hermes_read_regn(hw, CMD); ! 169: } ! 170: ! 171: /* No need to explicitly handle the timeout - if we've timed ! 172: out hermes_issue_cmd() will probably return -EBUSY below */ ! 173: ! 174: /* According to the documentation, EVSTAT may contain ! 175: obsolete event occurrence information. We have to acknowledge ! 176: it by writing EVACK. */ ! 177: reg = hermes_read_regn(hw, EVSTAT); ! 178: hermes_write_regn(hw, EVACK, reg); ! 179: ! 180: /* We don't use hermes_docmd_wait here, because the reset wipes ! 181: the magic constant in SWSUPPORT0 away, and it gets confused */ ! 182: err = hermes_issue_cmd(hw, HERMES_CMD_INIT, 0); ! 183: if (err) ! 184: return err; ! 185: ! 186: reg = hermes_read_regn(hw, EVSTAT); ! 187: k = CMD_INIT_TIMEOUT; ! 188: while ( (! (reg & HERMES_EV_CMD)) && k) { ! 189: k--; ! 190: udelay(10); ! 191: reg = hermes_read_regn(hw, EVSTAT); ! 192: } ! 193: ! 194: hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); ! 195: ! 196: if (! hermes_present(hw)) { ! 197: DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n", ! 198: hw->iobase); ! 199: err = -ENODEV; ! 200: goto out; ! 201: } ! 202: ! 203: if (! (reg & HERMES_EV_CMD)) { ! 204: printk(KERN_ERR "hermes @ %s0x%lx: " ! 205: "Timeout waiting for card to reset (reg=0x%04x)!\n", ! 206: IO_TYPE(hw), hw->iobase, reg); ! 207: err = -ETIMEDOUT; ! 208: goto out; ! 209: } ! 210: ! 211: status = hermes_read_regn(hw, STATUS); ! 212: ! 213: hermes_write_regn(hw, EVACK, HERMES_EV_CMD); ! 214: ! 215: if (status & HERMES_STATUS_RESULT) ! 216: err = -EIO; ! 217: ! 218: out: ! 219: return err; ! 220: } ! 221: ! 222: /* Issue a command to the chip, and (busy!) wait for it to ! 223: * complete. ! 224: * ! 225: * Returns: < 0 on internal error, 0 on success, > 0 on error returned by the firmware ! 226: * ! 227: * Callable from any context, but locking is your problem. */ ! 228: int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, ! 229: hermes_response_t *resp) ! 230: { ! 231: int err; ! 232: int k; ! 233: u16 reg; ! 234: u16 status; ! 235: ! 236: err = hermes_issue_cmd(hw, cmd, parm0); ! 237: if (err) { ! 238: if (! hermes_present(hw)) { ! 239: printk(KERN_WARNING "hermes @ %s0x%lx: " ! 240: "Card removed while issuing command.\n", ! 241: IO_TYPE(hw), hw->iobase); ! 242: err = -ENODEV; ! 243: } else ! 244: printk(KERN_ERR "hermes @ %s0x%lx: Error %d issuing command.\n", ! 245: IO_TYPE(hw), hw->iobase, err); ! 246: goto out; ! 247: } ! 248: ! 249: reg = hermes_read_regn(hw, EVSTAT); ! 250: k = CMD_COMPL_TIMEOUT; ! 251: while ( (! (reg & HERMES_EV_CMD)) && k) { ! 252: k--; ! 253: udelay(10); ! 254: reg = hermes_read_regn(hw, EVSTAT); ! 255: } ! 256: ! 257: if (! hermes_present(hw)) { ! 258: printk(KERN_WARNING "hermes @ %s0x%lx: " ! 259: "Card removed while waiting for command completion.\n", ! 260: IO_TYPE(hw), hw->iobase); ! 261: err = -ENODEV; ! 262: goto out; ! 263: } ! 264: ! 265: if (! (reg & HERMES_EV_CMD)) { ! 266: printk(KERN_ERR "hermes @ %s0x%lx: " ! 267: "Timeout waiting for command completion.\n", ! 268: IO_TYPE(hw), hw->iobase); ! 269: err = -ETIMEDOUT; ! 270: goto out; ! 271: } ! 272: ! 273: status = hermes_read_regn(hw, STATUS); ! 274: if (resp) { ! 275: resp->status = status; ! 276: resp->resp0 = hermes_read_regn(hw, RESP0); ! 277: resp->resp1 = hermes_read_regn(hw, RESP1); ! 278: resp->resp2 = hermes_read_regn(hw, RESP2); ! 279: } ! 280: ! 281: hermes_write_regn(hw, EVACK, HERMES_EV_CMD); ! 282: ! 283: if (status & HERMES_STATUS_RESULT) ! 284: err = -EIO; ! 285: ! 286: out: ! 287: return err; ! 288: } ! 289: ! 290: int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) ! 291: { ! 292: int err = 0; ! 293: int k; ! 294: u16 reg; ! 295: ! 296: if ( (size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX) ) ! 297: return -EINVAL; ! 298: ! 299: err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, NULL); ! 300: if (err) { ! 301: return err; ! 302: } ! 303: ! 304: reg = hermes_read_regn(hw, EVSTAT); ! 305: k = ALLOC_COMPL_TIMEOUT; ! 306: while ( (! (reg & HERMES_EV_ALLOC)) && k) { ! 307: k--; ! 308: udelay(10); ! 309: reg = hermes_read_regn(hw, EVSTAT); ! 310: } ! 311: ! 312: if (! hermes_present(hw)) { ! 313: printk(KERN_WARNING "hermes @ %s0x%lx: " ! 314: "Card removed waiting for frame allocation.\n", ! 315: IO_TYPE(hw), hw->iobase); ! 316: return -ENODEV; ! 317: } ! 318: ! 319: if (! (reg & HERMES_EV_ALLOC)) { ! 320: printk(KERN_ERR "hermes @ %s0x%lx: " ! 321: "Timeout waiting for frame allocation\n", ! 322: IO_TYPE(hw), hw->iobase); ! 323: return -ETIMEDOUT; ! 324: } ! 325: ! 326: *fid = hermes_read_regn(hw, ALLOCFID); ! 327: hermes_write_regn(hw, EVACK, HERMES_EV_ALLOC); ! 328: ! 329: return 0; ! 330: } ! 331: ! 332: ! 333: /* Set up a BAP to read a particular chunk of data from card's internal buffer. ! 334: * ! 335: * Returns: < 0 on internal failure (errno), 0 on success, >0 on error ! 336: * from firmware ! 337: * ! 338: * Callable from any context */ ! 339: static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) ! 340: { ! 341: int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0; ! 342: int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0; ! 343: int k; ! 344: u16 reg; ! 345: ! 346: /* Paranoia.. */ ! 347: if ( (offset > HERMES_BAP_OFFSET_MAX) || (offset % 2) ) ! 348: return -EINVAL; ! 349: ! 350: k = HERMES_BAP_BUSY_TIMEOUT; ! 351: reg = hermes_read_reg(hw, oreg); ! 352: while ((reg & HERMES_OFFSET_BUSY) && k) { ! 353: k--; ! 354: udelay(1); ! 355: reg = hermes_read_reg(hw, oreg); ! 356: } ! 357: ! 358: #ifdef HERMES_DEBUG_BUFFER ! 359: hw->profile[HERMES_BAP_BUSY_TIMEOUT - k]++; ! 360: ! 361: if (k < HERMES_BAP_BUSY_TIMEOUT) { ! 362: struct hermes_debug_entry *e = ! 363: &hw->dbuf[(hw->dbufp++) % HERMES_DEBUG_BUFSIZE]; ! 364: e->bap = bap; ! 365: e->id = id; ! 366: e->offset = offset; ! 367: e->cycles = HERMES_BAP_BUSY_TIMEOUT - k; ! 368: } ! 369: #endif ! 370: ! 371: if (reg & HERMES_OFFSET_BUSY) ! 372: return -ETIMEDOUT; ! 373: ! 374: /* Now we actually set up the transfer */ ! 375: hermes_write_reg(hw, sreg, id); ! 376: hermes_write_reg(hw, oreg, offset); ! 377: ! 378: /* Wait for the BAP to be ready */ ! 379: k = HERMES_BAP_BUSY_TIMEOUT; ! 380: reg = hermes_read_reg(hw, oreg); ! 381: while ( (reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) { ! 382: k--; ! 383: udelay(1); ! 384: reg = hermes_read_reg(hw, oreg); ! 385: } ! 386: ! 387: if (reg & HERMES_OFFSET_BUSY) { ! 388: return -ETIMEDOUT; ! 389: } ! 390: ! 391: if (reg & HERMES_OFFSET_ERR) { ! 392: return -EIO; ! 393: } ! 394: ! 395: ! 396: return 0; ! 397: } ! 398: ! 399: /* Read a block of data from the chip's buffer, via the ! 400: * BAP. Synchronization/serialization is the caller's problem. len ! 401: * must be even. ! 402: * ! 403: * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware ! 404: */ ! 405: int hermes_bap_pread(hermes_t *hw, int bap, void *buf, unsigned len, ! 406: u16 id, u16 offset) ! 407: { ! 408: int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; ! 409: int err = 0; ! 410: ! 411: if ( (len < 0) || (len % 2) ) ! 412: return -EINVAL; ! 413: ! 414: err = hermes_bap_seek(hw, bap, id, offset); ! 415: if (err) ! 416: goto out; ! 417: ! 418: /* Actually do the transfer */ ! 419: hermes_read_words(hw, dreg, buf, len/2); ! 420: ! 421: out: ! 422: return err; ! 423: } ! 424: ! 425: /* Write a block of data to the chip's buffer, via the ! 426: * BAP. Synchronization/serialization is the caller's problem. len ! 427: * must be even. ! 428: * ! 429: * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware ! 430: */ ! 431: int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, unsigned len, ! 432: u16 id, u16 offset) ! 433: { ! 434: int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; ! 435: int err = 0; ! 436: ! 437: if ( (len < 0) || (len % 2) ) ! 438: return -EINVAL; ! 439: ! 440: err = hermes_bap_seek(hw, bap, id, offset); ! 441: if (err) ! 442: goto out; ! 443: ! 444: /* Actually do the transfer */ ! 445: hermes_write_words(hw, dreg, buf, len/2); ! 446: ! 447: out: ! 448: return err; ! 449: } ! 450: ! 451: /* Read a Length-Type-Value record from the card. ! 452: * ! 453: * If length is NULL, we ignore the length read from the card, and ! 454: * read the entire buffer regardless. This is useful because some of ! 455: * the configuration records appear to have incorrect lengths in ! 456: * practice. ! 457: * ! 458: * Callable from user or bh context. */ ! 459: int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, ! 460: u16 *length, void *buf) ! 461: { ! 462: int err = 0; ! 463: int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; ! 464: u16 rlength, rtype; ! 465: unsigned nwords; ! 466: ! 467: if ( (bufsize < 0) || (bufsize % 2) ) ! 468: return -EINVAL; ! 469: ! 470: err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL); ! 471: if (err) ! 472: return err; ! 473: ! 474: err = hermes_bap_seek(hw, bap, rid, 0); ! 475: if (err) ! 476: return err; ! 477: ! 478: rlength = hermes_read_reg(hw, dreg); ! 479: ! 480: if (! rlength) ! 481: return -ENOENT; ! 482: ! 483: rtype = hermes_read_reg(hw, dreg); ! 484: ! 485: if (length) ! 486: *length = rlength; ! 487: ! 488: if (rtype != rid) ! 489: printk(KERN_WARNING "hermes @ %s0x%lx: " ! 490: "hermes_read_ltv(): rid (0x%04x) does not match type (0x%04x)\n", ! 491: IO_TYPE(hw), hw->iobase, rid, rtype); ! 492: if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize) ! 493: printk(KERN_WARNING "hermes @ %s0x%lx: " ! 494: "Truncating LTV record from %d to %d bytes. " ! 495: "(rid=0x%04x, len=0x%04x)\n", ! 496: IO_TYPE(hw), hw->iobase, ! 497: HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength); ! 498: ! 499: nwords = min((unsigned)rlength - 1, bufsize / 2); ! 500: hermes_read_words(hw, dreg, buf, nwords); ! 501: ! 502: return 0; ! 503: } ! 504: ! 505: int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, ! 506: u16 length, const void *value) ! 507: { ! 508: int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; ! 509: int err = 0; ! 510: unsigned count; ! 511: ! 512: if (length == 0) ! 513: return -EINVAL; ! 514: ! 515: err = hermes_bap_seek(hw, bap, rid, 0); ! 516: if (err) ! 517: return err; ! 518: ! 519: hermes_write_reg(hw, dreg, length); ! 520: hermes_write_reg(hw, dreg, rid); ! 521: ! 522: count = length - 1; ! 523: ! 524: hermes_write_words(hw, dreg, value, count); ! 525: ! 526: err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, ! 527: rid, NULL); ! 528: ! 529: return err; ! 530: } ! 531: ! 532: EXPORT_SYMBOL(hermes_struct_init); ! 533: EXPORT_SYMBOL(hermes_init); ! 534: EXPORT_SYMBOL(hermes_docmd_wait); ! 535: EXPORT_SYMBOL(hermes_allocate); ! 536: ! 537: EXPORT_SYMBOL(hermes_bap_pread); ! 538: EXPORT_SYMBOL(hermes_bap_pwrite); ! 539: EXPORT_SYMBOL(hermes_read_ltv); ! 540: EXPORT_SYMBOL(hermes_write_ltv); ! 541: ! 542: static int __init init_hermes(void) ! 543: { ! 544: return 0; ! 545: } ! 546: ! 547: static void __exit exit_hermes(void) ! 548: { ! 549: } ! 550: ! 551: module_init(init_hermes); ! 552: module_exit(exit_hermes);
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.