Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Driver for 802.11b cards using RAM-loadable Symbol firmware, such as
0003  * Symbol Wireless Networker LA4137, CompactFlash cards by Socket
0004  * Communications and Intel PRO/Wireless 2011B.
0005  *
0006  * The driver implements Symbol firmware download.  The rest is handled
0007  * in hermes.c and main.c.
0008  *
0009  * Utilities for downloading the Symbol firmware are available at
0010  * http://sourceforge.net/projects/orinoco/
0011  *
0012  * Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
0013  * Portions based on orinoco_cs.c:
0014  *  Copyright (C) David Gibson, Linuxcare Australia
0015  * Portions based on Spectrum24tDnld.c from original spectrum24 driver:
0016  *  Copyright (C) Symbol Technologies.
0017  *
0018  * See copyright notice in file main.c.
0019  */
0020 
0021 #define DRIVER_NAME "spectrum_cs"
0022 #define PFX DRIVER_NAME ": "
0023 
0024 #include <linux/module.h>
0025 #include <linux/kernel.h>
0026 #include <linux/delay.h>
0027 #include <pcmcia/cistpl.h>
0028 #include <pcmcia/cisreg.h>
0029 #include <pcmcia/ds.h>
0030 
0031 #include "orinoco.h"
0032 
0033 /********************************************************************/
0034 /* Module stuff                             */
0035 /********************************************************************/
0036 
0037 MODULE_AUTHOR("Pavel Roskin <proski@gnu.org>");
0038 MODULE_DESCRIPTION("Driver for Symbol Spectrum24 Trilogy cards with firmware downloader");
0039 MODULE_LICENSE("Dual MPL/GPL");
0040 
0041 /* Module parameters */
0042 
0043 /* Some D-Link cards have buggy CIS. They do work at 5v properly, but
0044  * don't have any CIS entry for it. This workaround it... */
0045 static int ignore_cis_vcc; /* = 0 */
0046 module_param(ignore_cis_vcc, int, 0);
0047 MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket");
0048 
0049 /********************************************************************/
0050 /* Data structures                          */
0051 /********************************************************************/
0052 
0053 /* PCMCIA specific device information (goes in the card field of
0054  * struct orinoco_private */
0055 struct orinoco_pccard {
0056     struct pcmcia_device    *p_dev;
0057 };
0058 
0059 /********************************************************************/
0060 /* Function prototypes                          */
0061 /********************************************************************/
0062 
0063 static int spectrum_cs_config(struct pcmcia_device *link);
0064 static void spectrum_cs_release(struct pcmcia_device *link);
0065 
0066 /* Constants for the CISREG_CCSR register */
0067 #define HCR_RUN     0x07    /* run firmware after reset */
0068 #define HCR_IDLE    0x0E    /* don't run firmware after reset */
0069 #define HCR_MEM16   0x10    /* memory width bit, should be preserved */
0070 
0071 
0072 /*
0073  * Reset the card using configuration registers COR and CCSR.
0074  * If IDLE is 1, stop the firmware, so that it can be safely rewritten.
0075  */
0076 static int
0077 spectrum_reset(struct pcmcia_device *link, int idle)
0078 {
0079     int ret;
0080     u8 save_cor;
0081     u8 ccsr;
0082 
0083     /* Doing it if hardware is gone is guaranteed crash */
0084     if (!pcmcia_dev_present(link))
0085         return -ENODEV;
0086 
0087     /* Save original COR value */
0088     ret = pcmcia_read_config_byte(link, CISREG_COR, &save_cor);
0089     if (ret)
0090         goto failed;
0091 
0092     /* Soft-Reset card */
0093     ret = pcmcia_write_config_byte(link, CISREG_COR,
0094                 (save_cor | COR_SOFT_RESET));
0095     if (ret)
0096         goto failed;
0097     udelay(1000);
0098 
0099     /* Read CCSR */
0100     ret = pcmcia_read_config_byte(link, CISREG_CCSR, &ccsr);
0101     if (ret)
0102         goto failed;
0103 
0104     /*
0105      * Start or stop the firmware.  Memory width bit should be
0106      * preserved from the value we've just read.
0107      */
0108     ccsr = (idle ? HCR_IDLE : HCR_RUN) | (ccsr & HCR_MEM16);
0109     ret = pcmcia_write_config_byte(link, CISREG_CCSR, ccsr);
0110     if (ret)
0111         goto failed;
0112     udelay(1000);
0113 
0114     /* Restore original COR configuration index */
0115     ret = pcmcia_write_config_byte(link, CISREG_COR,
0116                 (save_cor & ~COR_SOFT_RESET));
0117     if (ret)
0118         goto failed;
0119     udelay(1000);
0120     return 0;
0121 
0122 failed:
0123     return -ENODEV;
0124 }
0125 
0126 /********************************************************************/
0127 /* Device methods                           */
0128 /********************************************************************/
0129 
0130 static int
0131 spectrum_cs_hard_reset(struct orinoco_private *priv)
0132 {
0133     struct orinoco_pccard *card = priv->card;
0134     struct pcmcia_device *link = card->p_dev;
0135 
0136     /* Soft reset using COR and HCR */
0137     spectrum_reset(link, 0);
0138 
0139     return 0;
0140 }
0141 
0142 static int
0143 spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle)
0144 {
0145     struct orinoco_pccard *card = priv->card;
0146     struct pcmcia_device *link = card->p_dev;
0147 
0148     return spectrum_reset(link, idle);
0149 }
0150 
0151 /********************************************************************/
0152 /* PCMCIA stuff                             */
0153 /********************************************************************/
0154 
0155 static int
0156 spectrum_cs_probe(struct pcmcia_device *link)
0157 {
0158     struct orinoco_private *priv;
0159     struct orinoco_pccard *card;
0160 
0161     priv = alloc_orinocodev(sizeof(*card), &link->dev,
0162                 spectrum_cs_hard_reset,
0163                 spectrum_cs_stop_firmware);
0164     if (!priv)
0165         return -ENOMEM;
0166     card = priv->card;
0167 
0168     /* Link both structures together */
0169     card->p_dev = link;
0170     link->priv = priv;
0171 
0172     return spectrum_cs_config(link);
0173 }               /* spectrum_cs_attach */
0174 
0175 static void spectrum_cs_detach(struct pcmcia_device *link)
0176 {
0177     struct orinoco_private *priv = link->priv;
0178 
0179     orinoco_if_del(priv);
0180 
0181     spectrum_cs_release(link);
0182 
0183     free_orinocodev(priv);
0184 }               /* spectrum_cs_detach */
0185 
0186 static int spectrum_cs_config_check(struct pcmcia_device *p_dev,
0187                     void *priv_data)
0188 {
0189     if (p_dev->config_index == 0)
0190         return -EINVAL;
0191 
0192     return pcmcia_request_io(p_dev);
0193 };
0194 
0195 static int
0196 spectrum_cs_config(struct pcmcia_device *link)
0197 {
0198     struct orinoco_private *priv = link->priv;
0199     struct hermes *hw = &priv->hw;
0200     int ret;
0201     void __iomem *mem;
0202 
0203     link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC |
0204         CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
0205     if (ignore_cis_vcc)
0206         link->config_flags &= ~CONF_AUTO_CHECK_VCC;
0207     ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL);
0208     if (ret) {
0209         if (!ignore_cis_vcc)
0210             printk(KERN_ERR PFX "GetNextTuple(): No matching "
0211                    "CIS configuration.  Maybe you need the "
0212                    "ignore_cis_vcc=1 parameter.\n");
0213         goto failed;
0214     }
0215 
0216     mem = ioport_map(link->resource[0]->start,
0217             resource_size(link->resource[0]));
0218     if (!mem)
0219         goto failed;
0220 
0221     /* We initialize the hermes structure before completing PCMCIA
0222      * configuration just in case the interrupt handler gets
0223      * called. */
0224     hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
0225     hw->eeprom_pda = true;
0226 
0227     ret = pcmcia_request_irq(link, orinoco_interrupt);
0228     if (ret)
0229         goto failed;
0230 
0231     ret = pcmcia_enable_device(link);
0232     if (ret)
0233         goto failed;
0234 
0235     /* Reset card */
0236     if (spectrum_cs_hard_reset(priv) != 0)
0237         goto failed;
0238 
0239     /* Initialise the main driver */
0240     if (orinoco_init(priv) != 0) {
0241         printk(KERN_ERR PFX "orinoco_init() failed\n");
0242         goto failed;
0243     }
0244 
0245     /* Register an interface with the stack */
0246     if (orinoco_if_add(priv, link->resource[0]->start,
0247                link->irq, NULL) != 0) {
0248         printk(KERN_ERR PFX "orinoco_if_add() failed\n");
0249         goto failed;
0250     }
0251 
0252     return 0;
0253 
0254  failed:
0255     spectrum_cs_release(link);
0256     return -ENODEV;
0257 }               /* spectrum_cs_config */
0258 
0259 static void
0260 spectrum_cs_release(struct pcmcia_device *link)
0261 {
0262     struct orinoco_private *priv = link->priv;
0263     unsigned long flags;
0264 
0265     /* We're committed to taking the device away now, so mark the
0266      * hardware as unavailable */
0267     priv->hw.ops->lock_irqsave(&priv->lock, &flags);
0268     priv->hw_unavailable++;
0269     priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
0270 
0271     pcmcia_disable_device(link);
0272     if (priv->hw.iobase)
0273         ioport_unmap(priv->hw.iobase);
0274 }               /* spectrum_cs_release */
0275 
0276 
0277 static int
0278 spectrum_cs_suspend(struct pcmcia_device *link)
0279 {
0280     struct orinoco_private *priv = link->priv;
0281 
0282     /* Mark the device as stopped, to block IO until later */
0283     orinoco_down(priv);
0284 
0285     return 0;
0286 }
0287 
0288 static int
0289 spectrum_cs_resume(struct pcmcia_device *link)
0290 {
0291     struct orinoco_private *priv = link->priv;
0292     int err = orinoco_up(priv);
0293 
0294     return err;
0295 }
0296 
0297 
0298 /********************************************************************/
0299 /* Module initialization                        */
0300 /********************************************************************/
0301 
0302 static const struct pcmcia_device_id spectrum_cs_ids[] = {
0303     PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */
0304     PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */
0305     PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */
0306     PCMCIA_DEVICE_NULL,
0307 };
0308 MODULE_DEVICE_TABLE(pcmcia, spectrum_cs_ids);
0309 
0310 static struct pcmcia_driver orinoco_driver = {
0311     .owner      = THIS_MODULE,
0312     .name       = DRIVER_NAME,
0313     .probe      = spectrum_cs_probe,
0314     .remove     = spectrum_cs_detach,
0315     .suspend    = spectrum_cs_suspend,
0316     .resume     = spectrum_cs_resume,
0317     .id_table       = spectrum_cs_ids,
0318 };
0319 module_pcmcia_driver(orinoco_driver);