X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Fwl3501_cs.c;fp=drivers%2Fnet%2Fwireless%2Fwl3501_cs.c;h=48e10b0c7e747ede33fe13c5dbd6582511aee870;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=c03e400faceefe26714d11a92fce08e940899c61;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index c03e400fa..48e10b0c7 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -28,6 +28,7 @@ */ #undef REALLY_SLOW_IO /* most systems can safely undef this */ +#include #include #include #include @@ -102,8 +103,8 @@ module_param(pc_debug, int, 0); * release a socket, in response to card insertion and ejection events. They * are invoked from the wl24 event handler. */ -static int wl3501_config(struct pcmcia_device *link); -static void wl3501_release(struct pcmcia_device *link); +static void wl3501_config(dev_link_t *link); +static void wl3501_release(dev_link_t *link); /* * The dev_info variable is the "key" that is used to match up this @@ -225,6 +226,17 @@ static void iw_copy_mgmt_info_element(struct iw_mgmt_info_element *to, iw_set_mgmt_info_element(from->id, to, from->data, from->len); } +/* + * A linked list of "instances" of the wl24 device. Each actual PCMCIA card + * corresponds to one device instance, and is described by one dev_link_t + * structure (defined in ds.h). + * + * You may not want to use a linked list for this -- for example, the memory + * card driver uses an array of dev_link_t pointers, where minor device numbers + * are used to derive the corresponding array index. + */ +static dev_link_t *wl3501_dev_list; + static inline void wl3501_switch_page(struct wl3501_card *this, u8 page) { wl3501_outb(page, this->base_addr + WL3501_NIC_BSS); @@ -1269,10 +1281,15 @@ static int wl3501_close(struct net_device *dev) struct wl3501_card *this = dev->priv; int rc = -ENODEV; unsigned long flags; - struct pcmcia_device *link; - link = this->p_dev; + dev_link_t *link; spin_lock_irqsave(&this->lock, flags); + /* Check if the device is in wl3501_dev_list */ + for (link = wl3501_dev_list; link; link = link->next) + if (link->priv == dev) + break; + if (!link) + goto out; link->open--; /* Stop wl3501_hard_start_xmit() from now on */ @@ -1284,6 +1301,7 @@ static int wl3501_close(struct net_device *dev) rc = 0; printk(KERN_INFO "%s: WL3501 closed\n", dev->name); +out: spin_unlock_irqrestore(&this->lock, flags); return rc; } @@ -1382,11 +1400,14 @@ static int wl3501_open(struct net_device *dev) int rc = -ENODEV; struct wl3501_card *this = dev->priv; unsigned long flags; - struct pcmcia_device *link; - link = this->p_dev; + dev_link_t *link; spin_lock_irqsave(&this->lock, flags); - if (!pcmcia_dev_present(link)) + /* Check if the device is in wl3501_dev_list */ + for (link = wl3501_dev_list; link; link = link->next) + if (link->priv == dev) + break; + if (!DEV_OK(link)) goto out; netif_device_attach(dev); link->open++; @@ -1476,23 +1497,38 @@ static struct ethtool_ops ops = { * Services. If it has been released, all local data structures are freed. * Otherwise, the structures will be freed when the device is released. */ -static void wl3501_detach(struct pcmcia_device *link) +static void wl3501_detach(struct pcmcia_device *p_dev) { + dev_link_t *link = dev_to_instance(p_dev); + dev_link_t **linkp; struct net_device *dev = link->priv; + /* Locate device structure */ + for (linkp = &wl3501_dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) + break; + if (!*linkp) + goto out; + /* If the device is currently configured and active, we won't actually * delete it yet. Instead, it is marked so that when the release() * function is called, that will trigger a proper detach(). */ - while (link->open > 0) - wl3501_close(dev); + if (link->state & DEV_CONFIG) { + while (link->open > 0) + wl3501_close(dev); - netif_device_detach(dev); - wl3501_release(link); + netif_device_detach(dev); + wl3501_release(link); + } + + /* Unlink device structure, free pieces */ + *linkp = link->next; if (link->priv) free_netdev(link->priv); - + kfree(link); +out: return; } @@ -1917,26 +1953,33 @@ static const struct iw_handler_def wl3501_handler_def = { * The dev_link structure is initialized, but we don't actually configure the * card at this point -- we wait until we receive a card insertion event. */ -static int wl3501_probe(struct pcmcia_device *p_dev) +static int wl3501_attach(struct pcmcia_device *p_dev) { + dev_link_t *link; struct net_device *dev; struct wl3501_card *this; + /* Initialize the dev_link_t structure */ + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (!link) + return -ENOMEM; + /* The io structure describes IO port mapping */ - p_dev->io.NumPorts1 = 16; - p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - p_dev->io.IOAddrLines = 5; + link->io.NumPorts1 = 16; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.IOAddrLines = 5; /* Interrupt setup */ - p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; - p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID; - p_dev->irq.Handler = wl3501_interrupt; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + link->irq.Handler = wl3501_interrupt; /* General socket configuration */ - p_dev->conf.Attributes = CONF_ENABLE_IRQ; - p_dev->conf.IntType = INT_MEMORY_AND_IO; - p_dev->conf.ConfigIndex = 1; - p_dev->conf.Present = PRESENT_OPTION; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; dev = alloc_etherdev(sizeof(struct wl3501_card)); if (!dev) @@ -1949,15 +1992,22 @@ static int wl3501_probe(struct pcmcia_device *p_dev) dev->get_stats = wl3501_get_stats; this = dev->priv; this->wireless_data.spy_data = &this->spy_data; - this->p_dev = p_dev; dev->wireless_data = &this->wireless_data; dev->wireless_handlers = (struct iw_handler_def *)&wl3501_handler_def; SET_ETHTOOL_OPS(dev, &ops); netif_stop_queue(dev); - p_dev->priv = p_dev->irq.Instance = dev; + link->priv = link->irq.Instance = dev; + + link->handle = p_dev; + p_dev->instance = link; + + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + wl3501_config(link); - return wl3501_config(p_dev); + return 0; out_link: + kfree(link); + link = NULL; return -ENOMEM; } @@ -1972,10 +2022,11 @@ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) * received, to configure the PCMCIA socket, and to make the ethernet device * available to the system. */ -static int wl3501_config(struct pcmcia_device *link) +static void wl3501_config(dev_link_t *link) { tuple_t tuple; cisparse_t parse; + client_handle_t handle = link->handle; struct net_device *dev = link->priv; int i = 0, j, last_fn, last_ret; unsigned char bf[64]; @@ -1984,15 +2035,18 @@ static int wl3501_config(struct pcmcia_device *link) /* This reads the card's CONFIG tuple to find its config registers. */ tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); tuple.TupleData = bf; tuple.TupleDataMax = sizeof(bf); tuple.TupleOffset = 0; - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; + /* Configure card */ + link->state |= DEV_CONFIG; + /* Try allocating IO ports. This tries a few fixed addresses. If you * want, you can also read the card's config table to pick addresses -- * see the serial driver for an example. */ @@ -2002,28 +2056,28 @@ static int wl3501_config(struct pcmcia_device *link) * 0x200-0x2ff, and so on, because this seems safer */ link->io.BasePort1 = j; link->io.BasePort2 = link->io.BasePort1 + 0x10; - i = pcmcia_request_io(link, &link->io); + i = pcmcia_request_io(link->handle, &link->io); if (i == CS_SUCCESS) break; } if (i != CS_SUCCESS) { - cs_error(link, RequestIO, i); + cs_error(link->handle, RequestIO, i); goto failed; } /* Now allocate an interrupt line. Note that this does not actually * assign a handler to the interrupt. */ - CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); /* This actually configures the PCMCIA socket -- setting up the I/O * windows and the interrupt mapping. */ - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; - SET_NETDEV_DEV(dev, &handle_to_dev(link)); + SET_NETDEV_DEV(dev, &handle_to_dev(handle)); if (register_netdev(dev)) { printk(KERN_NOTICE "wl3501_cs: register_netdev() failed\n"); goto failed; @@ -2034,9 +2088,10 @@ static int wl3501_config(struct pcmcia_device *link) this = dev->priv; /* * At this point, the dev_node_t structure(s) should be initialized and - * arranged in a linked list at link->dev_node. + * arranged in a linked list at link->dev. */ - link->dev_node = &this->node; + link->dev = &this->node; + link->state &= ~DEV_CONFIG_PENDING; this->base_addr = dev->base_addr; @@ -2072,13 +2127,13 @@ static int wl3501_config(struct pcmcia_device *link) spin_lock_init(&this->lock); init_waitqueue_head(&this->wait); netif_start_queue(dev); - return 0; - + goto out; cs_failed: - cs_error(link, last_fn, last_ret); + cs_error(link->handle, last_fn, last_ret); failed: wl3501_release(link); - return -ENODEV; +out: + return; } /** @@ -2089,36 +2144,52 @@ failed: * and release the PCMCIA configuration. If the device is still open, this * will be postponed until it is closed. */ -static void wl3501_release(struct pcmcia_device *link) +static void wl3501_release(dev_link_t *link) { struct net_device *dev = link->priv; /* Unlink the device chain */ - if (link->dev_node) + if (link->dev) { unregister_netdev(dev); + link->dev = NULL; + } - pcmcia_disable_device(link); + /* Don't bother checking to see if these succeed or not */ + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); + link->state &= ~DEV_CONFIG; } -static int wl3501_suspend(struct pcmcia_device *link) +static int wl3501_suspend(struct pcmcia_device *p_dev) { + dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; + link->state |= DEV_SUSPEND; + wl3501_pwr_mgmt(dev->priv, WL3501_SUSPEND); - if (link->open) - netif_device_detach(dev); + if (link->state & DEV_CONFIG) { + if (link->open) + netif_device_detach(dev); + pcmcia_release_configuration(link->handle); + } return 0; } -static int wl3501_resume(struct pcmcia_device *link) +static int wl3501_resume(struct pcmcia_device *p_dev) { + dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; wl3501_pwr_mgmt(dev->priv, WL3501_RESUME); - if (link->open) { - wl3501_reset(dev); - netif_device_attach(dev); + if (link->state & DEV_CONFIG) { + pcmcia_request_configuration(link->handle, &link->conf); + if (link->open) { + wl3501_reset(dev); + netif_device_attach(dev); + } } return 0; @@ -2136,7 +2207,7 @@ static struct pcmcia_driver wl3501_driver = { .drv = { .name = "wl3501_cs", }, - .probe = wl3501_probe, + .probe = wl3501_attach, .remove = wl3501_detach, .id_table = wl3501_ids, .suspend = wl3501_suspend, @@ -2150,7 +2221,9 @@ static int __init wl3501_init_module(void) static void __exit wl3501_exit_module(void) { + dprintk(0, ": unloading"); pcmcia_unregister_driver(&wl3501_driver); + BUG_ON(wl3501_dev_list != NULL); } module_init(wl3501_init_module);