From 933a838aa1aae8388438bb002fbdaf6fca519a5c Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 29 Dec 2009 18:21:39 +0100 Subject: pcmcia: make use of pcmcia_dev_resume() return value In runtime_resume(), do not throw away the return value of pcmcia_dev_resume(), for we can use it (at least) in pcmcia_store_pm_state(). This also fixes the pointless assignment previosly seen there, as noted by Dan Carpenter. CC: Dan Carpenter Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 1a4a3c49cc1..defa44c27b9 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -970,13 +970,14 @@ static int runtime_suspend(struct device *dev) return rc; } -static void runtime_resume(struct device *dev) +static int runtime_resume(struct device *dev) { int rc; down(&dev->sem); rc = pcmcia_dev_resume(dev); up(&dev->sem); + return rc; } /************************ per-device sysfs output ***************************/ @@ -1027,7 +1028,7 @@ static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute if ((!p_dev->suspended) && !strncmp(buf, "off", 3)) ret = runtime_suspend(dev); else if (p_dev->suspended && !strncmp(buf, "on", 2)) - runtime_resume(dev); + ret = runtime_resume(dev); return ret ? ret : count; } -- cgit v1.2.3 From fa0b3bc504ff813cc05988bb30bbb6c6a0263eb4 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 12:08:22 +0100 Subject: pcmcia: do not meddle with already assigned resources Do not release any iomem resources already in use. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/rsrc_nonstatic.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 9b0dc433a8c..4f93889301b 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -727,13 +727,6 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned break; case REMOVE_MANAGED_RESOURCE: ret = sub_interval(&data->mem_db, start, size); - if (!ret) { - struct pcmcia_socket *socket; - down_read(&pcmcia_socket_list_rwsem); - list_for_each_entry(socket, &pcmcia_socket_list, socket_list) - release_cis_mem(socket); - up_read(&pcmcia_socket_list_rwsem); - } break; default: ret = -EINVAL; -- cgit v1.2.3 From 904e377744bfdcea276c27167fa6a609929f39dc Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 12:28:04 +0100 Subject: pcmcia: validate CIS, not CIS cache. In pccard_validate_cis(), validate the card CIS, not the CIS cache. Also, destroy the CIS cache if pccard_validate_cis fails. Furthermore, do not remove the fake CIS in destroy_cis_cache() but do so explicitely in the code paths where it makes sense. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cistpl.c | 24 ++++++++++++++++-------- drivers/pcmcia/cs.c | 4 ++++ drivers/pcmcia/rsrc_nonstatic.c | 3 +-- 3 files changed, 21 insertions(+), 10 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 25b1cd219e3..41ec7729edd 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -319,22 +319,23 @@ remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len) } } +/** + * destroy_cis_cache() - destroy the CIS cache + * @s: pcmcia_socket for which CIS cache shall be destroyed + * + * This destroys the CIS cache but keeps any fake CIS alive. + */ + void destroy_cis_cache(struct pcmcia_socket *s) { struct list_head *l, *n; + struct cis_cache_entry *cis; list_for_each_safe(l, n, &s->cis_cache) { - struct cis_cache_entry *cis = list_entry(l, struct cis_cache_entry, node); - + cis = list_entry(l, struct cis_cache_entry, node); list_del(&cis->node); kfree(cis); } - - /* - * If there was a fake CIS, destroy that as well. - */ - kfree(s->fake_cis); - s->fake_cis = NULL; } EXPORT_SYMBOL(destroy_cis_cache); @@ -1596,6 +1597,9 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info) if (!s) return -EINVAL; + /* We do not want to validate the CIS cache... */ + destroy_cis_cache(s); + tuple = kmalloc(sizeof(*tuple), GFP_KERNEL); if (tuple == NULL) { dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n"); @@ -1647,6 +1651,10 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info) count = 0; done: + /* invalidate CIS cache on failure */ + if (!dev_ok || !ident_ok || !count) + destroy_cis_cache(s); + if (info) *info = count; kfree(tuple); diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 6d6f82b38a6..96d8d25c209 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -407,6 +407,8 @@ static void socket_shutdown(struct pcmcia_socket *s) s->irq.AssignedIRQ = s->irq.Config = 0; s->lock_count = 0; destroy_cis_cache(s); + kfree(s->fake_cis); + s->fake_cis = NULL; #ifdef CONFIG_CARDBUS cb_free(s); #endif @@ -577,6 +579,8 @@ static int socket_late_resume(struct pcmcia_socket *skt) dev_dbg(&skt->dev, "cis mismatch - different card\n"); socket_remove_drivers(skt); destroy_cis_cache(skt); + kfree(skt->fake_cis); + skt->fake_cis = NULL; /* * Workaround: give DS time to schedule removal. * Remove me once the 100ms delay is eliminated diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 4f93889301b..b886385f12e 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -281,10 +281,9 @@ static int readable(struct pcmcia_socket *s, struct resource *res, s->cis_virt = ioremap(res->start, s->map_size); if (s->cis_virt) { ret = pccard_validate_cis(s, count); - /* invalidate mapping and CIS cache */ + /* invalidate mapping */ iounmap(s->cis_virt); s->cis_virt = NULL; - destroy_cis_cache(s); } s->cis_mem.res = NULL; if ((ret != 0) || (*count == 0)) -- cgit v1.2.3 From f131ddc4bd1713385c70606555d4d63bed5ec3fd Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 12:58:10 +0100 Subject: pcmcia: cleanup pccard_validate_cis() Cleanup pccard_validate_cis() and make it return an error code on all failures, not merely on some failures. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cistpl.c | 155 +++++++++++++++++++++++++----------------------- 1 file changed, 81 insertions(+), 74 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 41ec7729edd..04bf1ba607f 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -1577,88 +1577,95 @@ next_entry: EXPORT_SYMBOL(pccard_loop_tuple); -/*====================================================================== - - This tries to determine if a card has a sensible CIS. It returns - the number of tuples in the CIS, or 0 if the CIS looks bad. The - checks include making sure several critical tuples are present and - valid; seeing if the total number of tuples is reasonable; and - looking for tuples that use reserved codes. - -======================================================================*/ - +/** + * pccard_validate_cis() - check whether card has a sensible CIS + * @s: the struct pcmcia_socket we are to check + * @info: returns the number of tuples in the (valid) CIS, or 0 + * + * This tries to determine if a card has a sensible CIS. In @info, it + * returns the number of tuples in the CIS, or 0 if the CIS looks bad. The + * checks include making sure several critical tuples are present and + * valid; seeing if the total number of tuples is reasonable; and + * looking for tuples that use reserved codes. + * + * The function returns 0 on success. + */ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info) { - tuple_t *tuple; - cisparse_t *p; - unsigned int count = 0; - int ret, reserved, dev_ok = 0, ident_ok = 0; + tuple_t *tuple; + cisparse_t *p; + unsigned int count = 0; + int ret, reserved, dev_ok = 0, ident_ok = 0; - if (!s) - return -EINVAL; + if (!s) + return -EINVAL; - /* We do not want to validate the CIS cache... */ - destroy_cis_cache(s); + /* We do not want to validate the CIS cache... */ + destroy_cis_cache(s); - tuple = kmalloc(sizeof(*tuple), GFP_KERNEL); - if (tuple == NULL) { - dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n"); - return -ENOMEM; - } - p = kmalloc(sizeof(*p), GFP_KERNEL); - if (p == NULL) { - kfree(tuple); - dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n"); - return -ENOMEM; - } + tuple = kmalloc(sizeof(*tuple), GFP_KERNEL); + if (tuple == NULL) { + dev_warn(&s->dev, "no memory to validate CIS\n"); + return -ENOMEM; + } + p = kmalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) { + kfree(tuple); + dev_warn(&s->dev, "no memory to validate CIS\n"); + return -ENOMEM; + } - count = reserved = 0; - tuple->DesiredTuple = RETURN_FIRST_TUPLE; - tuple->Attributes = TUPLE_RETURN_COMMON; - ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple); - if (ret != 0) - goto done; - - /* First tuple should be DEVICE; we should really have either that - or a CFTABLE_ENTRY of some sort */ - if ((tuple->TupleCode == CISTPL_DEVICE) || - (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p) == 0) || - (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p) == 0)) - dev_ok++; - - /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2 - tuple, for card identification. Certain old D-Link and Linksys - cards have only a broken VERS_2 tuple; hence the bogus test. */ - if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) || - (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) || - (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC)) - ident_ok++; - - if (!dev_ok && !ident_ok) - goto done; - - for (count = 1; count < MAX_TUPLES; count++) { - ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple); + count = reserved = 0; + tuple->DesiredTuple = RETURN_FIRST_TUPLE; + tuple->Attributes = TUPLE_RETURN_COMMON; + ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple); if (ret != 0) - break; - if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) || - ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) || - ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff))) - reserved++; - } - if ((count == MAX_TUPLES) || (reserved > 5) || - ((!dev_ok || !ident_ok) && (count > 10))) - count = 0; + goto done; + + /* First tuple should be DEVICE; we should really have either that + or a CFTABLE_ENTRY of some sort */ + if ((tuple->TupleCode == CISTPL_DEVICE) || + (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p)) || + (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p))) + dev_ok++; + + /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2 + tuple, for card identification. Certain old D-Link and Linksys + cards have only a broken VERS_2 tuple; hence the bogus test. */ + if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) || + (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) || + (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC)) + ident_ok++; + + if (!dev_ok && !ident_ok) + goto done; + + for (count = 1; count < MAX_TUPLES; count++) { + ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple); + if (ret != 0) + break; + if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) || + ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) || + ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff))) + reserved++; + } + if ((count == MAX_TUPLES) || (reserved > 5) || + ((!dev_ok || !ident_ok) && (count > 10))) + count = 0; + + ret = 0; done: - /* invalidate CIS cache on failure */ - if (!dev_ok || !ident_ok || !count) - destroy_cis_cache(s); - - if (info) - *info = count; - kfree(tuple); - kfree(p); - return 0; + /* invalidate CIS cache on failure */ + if (!dev_ok || !ident_ok || !count) { + destroy_cis_cache(s); + ret = -EIO; + } + + if (info) + *info = count; + kfree(tuple); + kfree(p); + return ret; } EXPORT_SYMBOL(pccard_validate_cis); -- cgit v1.2.3 From 88b060d6c03fcb9e4d2018b4349954c4242a5c7f Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 14:14:23 +0100 Subject: pcmcia: improve check for same card in slot after resume During a suspend/resume cycle, an user may change the card in the PCMCIA/CardBus slot. The pcmcia_core can at least look at the socket state to check whether it is the same. For PCMCIA devices, move the detection and handling of such a change to ds.c. For CardBus devices, the PCI hotplug interface doesn't offer a "rescan" facility which also _removes_ devices no longer to be found behind a bridge. Therefore, remove and re-add all devices unconditionally. CC: Jesse Barnes CC: Linus Torvalds Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cistpl.c | 1 + drivers/pcmcia/cs.c | 65 +++++++++++++++++++++++-------------------------- drivers/pcmcia/ds.c | 16 +++++++++++- 3 files changed, 46 insertions(+), 36 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 04bf1ba607f..a8323cb2e34 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -377,6 +377,7 @@ int verify_cis_cache(struct pcmcia_socket *s) kfree(buf); return 0; } +EXPORT_SYMBOL(verify_cis_cache); /*====================================================================== diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 96d8d25c209..8c51493d1f9 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -328,7 +328,7 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) { int ret; - if (s->state & SOCKET_CARDBUS) + if ((s->state & SOCKET_CARDBUS) && (event != CS_EVENT_CARD_REMOVAL)) return 0; dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n", @@ -346,13 +346,6 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) return ret; } -static void socket_remove_drivers(struct pcmcia_socket *skt) -{ - dev_dbg(&skt->dev, "remove_drivers\n"); - - send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); -} - static int socket_reset(struct pcmcia_socket *skt) { int status, i; @@ -395,7 +388,7 @@ static void socket_shutdown(struct pcmcia_socket *s) dev_dbg(&s->dev, "shutdown\n"); - socket_remove_drivers(s); + send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); s->state &= SOCKET_INUSE | SOCKET_PRESENT; msleep(shutdown_delay * 10); s->state &= SOCKET_INUSE; @@ -462,7 +455,8 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay) return -EINVAL; } skt->state |= SOCKET_CARDBUS; - } + } else + skt->state &= ~SOCKET_CARDBUS; /* * Decode the card voltage requirements, and apply power to the card. @@ -544,6 +538,8 @@ static int socket_suspend(struct pcmcia_socket *skt) if (skt->state & SOCKET_SUSPEND) return -EBUSY; + skt->suspended_state = skt->state; + send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); skt->socket = dead_socket; skt->ops->set_socket(skt, &skt->socket); @@ -566,38 +562,37 @@ static int socket_early_resume(struct pcmcia_socket *skt) static int socket_late_resume(struct pcmcia_socket *skt) { - if (!(skt->state & SOCKET_PRESENT)) { - skt->state &= ~SOCKET_SUSPEND; + skt->state &= ~SOCKET_SUSPEND; + + if (!(skt->state & SOCKET_PRESENT)) return socket_insert(skt); + + if (skt->resume_status) { + socket_shutdown(skt); + return 0; } - if (skt->resume_status == 0) { - /* - * FIXME: need a better check here for cardbus cards. - */ - if (verify_cis_cache(skt) != 0) { - dev_dbg(&skt->dev, "cis mismatch - different card\n"); - socket_remove_drivers(skt); - destroy_cis_cache(skt); - kfree(skt->fake_cis); - skt->fake_cis = NULL; - /* - * Workaround: give DS time to schedule removal. - * Remove me once the 100ms delay is eliminated - * in ds.c - */ - msleep(200); - send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); - } else { - dev_dbg(&skt->dev, "cis matches cache\n"); - send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); - } - } else { + if (skt->suspended_state != skt->state) { + dev_dbg(&skt->dev, + "suspend state 0x%x != resume state 0x%x\n", + skt->suspended_state, skt->state); + socket_shutdown(skt); + return socket_insert(skt); } - skt->state &= ~SOCKET_SUSPEND; +#ifdef CONFIG_CARDBUS + if (skt->state & SOCKET_CARDBUS) { + /* We can't be sure the CardBus card is the same + * as the one previously inserted. Therefore, remove + * and re-add... */ + cb_free(skt); + cb_alloc(skt); + return 0; + } +#endif + send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); return 0; } diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index defa44c27b9..87e06395c12 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -1252,8 +1252,22 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) case CS_EVENT_EJECTION_REQUEST: break; - case CS_EVENT_PM_SUSPEND: case CS_EVENT_PM_RESUME: + if (verify_cis_cache(skt) != 0) { + dev_dbg(&skt->dev, "cis mismatch - different card\n"); + /* first, remove the card */ + ds_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); + destroy_cis_cache(skt); + kfree(skt->fake_cis); + skt->fake_cis = NULL; + /* now, add the new card */ + ds_event(skt, CS_EVENT_CARD_INSERTION, + CS_EVENT_PRI_LOW); + } + handle_event(skt, event); + break; + + case CS_EVENT_PM_SUSPEND: case CS_EVENT_RESET_PHYSICAL: case CS_EVENT_CARD_RESET: default: -- cgit v1.2.3 From 57197b9b7712eb19f5344c466e8aefbac1adbe55 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 17:27:33 +0100 Subject: pcmcia: CardBus doesn't need CIS access At least no in-kernel CardBus-capable PCI driver makes use of the CIS access functions. Therefore, it seems sensible to remove this unused code, and cleanup cardbus.c a lot. CC: Jesse Barnes CC: Linus Torvalds Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cardbus.c | 175 ++++------------------------------------- drivers/pcmcia/cistpl.c | 200 ++++++++--------------------------------------- 2 files changed, 46 insertions(+), 329 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index d99f846451a..ac0686efbf7 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -20,170 +20,12 @@ */ -#include #include -#include -#include -#include +#include #include -#include -#include -#include -#include #include -#include -#include -#include "cs_internal.h" - -/*====================================================================*/ - -/* Offsets in the Expansion ROM Image Header */ -#define ROM_SIGNATURE 0x0000 /* 2 bytes */ -#define ROM_DATA_PTR 0x0018 /* 2 bytes */ - -/* Offsets in the CardBus PC Card Data Structure */ -#define PCDATA_SIGNATURE 0x0000 /* 4 bytes */ -#define PCDATA_VPD_PTR 0x0008 /* 2 bytes */ -#define PCDATA_LENGTH 0x000a /* 2 bytes */ -#define PCDATA_REVISION 0x000c -#define PCDATA_IMAGE_SZ 0x0010 /* 2 bytes */ -#define PCDATA_ROM_LEVEL 0x0012 /* 2 bytes */ -#define PCDATA_CODE_TYPE 0x0014 -#define PCDATA_INDICATOR 0x0015 - -/*===================================================================== - - Expansion ROM's have a special layout, and pointers specify an - image number and an offset within that image. xlate_rom_addr() - converts an image/offset address to an absolute offset from the - ROM's base address. - -=====================================================================*/ - -static u_int xlate_rom_addr(void __iomem *b, u_int addr) -{ - u_int img = 0, ofs = 0, sz; - u_short data; - while ((readb(b) == 0x55) && (readb(b + 1) == 0xaa)) { - if (img == (addr >> 28)) - return (addr & 0x0fffffff) + ofs; - data = readb(b + ROM_DATA_PTR) + (readb(b + ROM_DATA_PTR + 1) << 8); - sz = 512 * (readb(b + data + PCDATA_IMAGE_SZ) + - (readb(b + data + PCDATA_IMAGE_SZ + 1) << 8)); - if ((sz == 0) || (readb(b + data + PCDATA_INDICATOR) & 0x80)) - break; - b += sz; - ofs += sz; - img++; - } - return 0; -} - -/*===================================================================== - - These are similar to setup_cis_mem and release_cis_mem for 16-bit - cards. The "result" that is used externally is the cb_cis_virt - pointer in the struct pcmcia_socket structure. - -=====================================================================*/ - -static void cb_release_cis_mem(struct pcmcia_socket *s) -{ - if (s->cb_cis_virt) { - dev_dbg(&s->dev, "cb_release_cis_mem()\n"); - iounmap(s->cb_cis_virt); - s->cb_cis_virt = NULL; - s->cb_cis_res = NULL; - } -} - -static int cb_setup_cis_mem(struct pcmcia_socket *s, struct resource *res) -{ - unsigned int start, size; - - if (res == s->cb_cis_res) - return 0; - - if (s->cb_cis_res) - cb_release_cis_mem(s); - - start = res->start; - size = res->end - start + 1; - s->cb_cis_virt = ioremap(start, size); - - if (!s->cb_cis_virt) - return -1; - - s->cb_cis_res = res; - - return 0; -} - -/*===================================================================== - - This is used by the CIS processing code to read CIS information - from a CardBus device. - -=====================================================================*/ - -int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len, - void *ptr) -{ - struct pci_dev *dev; - struct resource *res; - - dev_dbg(&s->dev, "read_cb_mem(%d, %#x, %u)\n", space, addr, len); - dev = pci_get_slot(s->cb_dev->subordinate, 0); - if (!dev) - goto fail; - - /* Config space? */ - if (space == 0) { - if (addr + len > 0x100) - goto failput; - for (; len; addr++, ptr++, len--) - pci_read_config_byte(dev, addr, ptr); - return 0; - } - - res = dev->resource + space - 1; - - pci_dev_put(dev); - - if (!res->flags) - goto fail; - - if (cb_setup_cis_mem(s, res) != 0) - goto fail; - - if (space == 7) { - addr = xlate_rom_addr(s->cb_cis_virt, addr); - if (addr == 0) - goto fail; - } - - if (addr + len > res->end - res->start) - goto fail; - - memcpy_fromio(ptr, s->cb_cis_virt + addr, len); - return 0; - -failput: - pci_dev_put(dev); -fail: - memset(ptr, 0xff, len); - return -1; -} - -/*===================================================================== - - cb_alloc() and cb_free() allocate and free the kernel data - structures for a Cardbus device, and handle the lowest level PCI - device setup issues. - -=====================================================================*/ static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq) { @@ -215,6 +57,13 @@ static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq) } } +/** + * cb_alloc() - add CardBus device + * @s: the pcmcia_socket where the CardBus device is located + * + * cb_alloc() allocates the kernel data structures for a Cardbus device + * and handles the lowest level PCI device setup issues. + */ int __ref cb_alloc(struct pcmcia_socket *s) { struct pci_bus *bus = s->cb_dev->subordinate; @@ -249,12 +98,16 @@ int __ref cb_alloc(struct pcmcia_socket *s) return 0; } +/** + * cb_free() - remove CardBus device + * @s: the pcmcia_socket where the CardBus device was located + * + * cb_free() handles the lowest level PCI device cleanup. + */ void cb_free(struct pcmcia_socket *s) { struct pci_dev *bridge = s->cb_dev; - cb_release_cis_mem(s); - if (bridge) pci_remove_behind_bridge(bridge); } diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index a8323cb2e34..368367ced62 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -268,29 +268,27 @@ EXPORT_SYMBOL(pcmcia_write_cis_mem); static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, size_t len, void *ptr) { - struct cis_cache_entry *cis; - int ret; + struct cis_cache_entry *cis; + int ret; - if (s->fake_cis) { - if (s->fake_cis_len >= addr+len) - memcpy(ptr, s->fake_cis+addr, len); - else - memset(ptr, 0xff, len); - return; - } + if (s->state & SOCKET_CARDBUS) + return; - list_for_each_entry(cis, &s->cis_cache, node) { - if (cis->addr == addr && cis->len == len && cis->attr == attr) { - memcpy(ptr, cis->cache, len); - return; + if (s->fake_cis) { + if (s->fake_cis_len >= addr+len) + memcpy(ptr, s->fake_cis+addr, len); + else + memset(ptr, 0xff, len); + return; + } + + list_for_each_entry(cis, &s->cis_cache, node) { + if (cis->addr == addr && cis->len == len && cis->attr == attr) { + memcpy(ptr, cis->cache, len); + return; + } } - } -#ifdef CONFIG_CARDBUS - if (s->state & SOCKET_CARDBUS) - ret = read_cb_mem(s, attr, addr, len, ptr); - else -#endif ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr); if (ret == 0) { @@ -351,6 +349,9 @@ int verify_cis_cache(struct pcmcia_socket *s) struct cis_cache_entry *cis; char *buf; + if (s->state & SOCKET_CARDBUS) + return -EINVAL; + buf = kmalloc(256, GFP_KERNEL); if (buf == NULL) { dev_printk(KERN_WARNING, &s->dev, @@ -362,12 +363,8 @@ int verify_cis_cache(struct pcmcia_socket *s) if (len > 256) len = 256; -#ifdef CONFIG_CARDBUS - if (s->state & SOCKET_CARDBUS) - read_cb_mem(s, cis->attr, cis->addr, len, buf); - else -#endif - pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf); + + pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf); if (memcmp(buf, cis->cache, len) != 0) { kfree(buf); @@ -427,25 +424,16 @@ int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple { if (!s) return -EINVAL; - if (!(s->state & SOCKET_PRESENT)) + + if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS)) return -ENODEV; tuple->TupleLink = tuple->Flags = 0; -#ifdef CONFIG_CARDBUS - if (s->state & SOCKET_CARDBUS) { - struct pci_dev *dev = s->cb_dev; - u_int ptr; - pci_bus_read_config_dword(dev->subordinate, 0, PCI_CARDBUS_CIS, &ptr); - tuple->CISOffset = ptr & ~7; - SPACE(tuple->Flags) = (ptr & 7); - } else -#endif - { - /* Assume presence of a LONGLINK_C to address 0 */ - tuple->CISOffset = tuple->LinkOffset = 0; - SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1; - } - if (!(s->state & SOCKET_CARDBUS) && (s->functions > 1) && - !(tuple->Attributes & TUPLE_RETURN_COMMON)) { + + /* Assume presence of a LONGLINK_C to address 0 */ + tuple->CISOffset = tuple->LinkOffset = 0; + SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1; + + if ((s->functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) { cisdata_t req = tuple->DesiredTuple; tuple->DesiredTuple = CISTPL_LONGLINK_MFC; if (pccard_get_next_tuple(s, function, tuple) == 0) { @@ -481,7 +469,7 @@ static int follow_link(struct pcmcia_socket *s, tuple_t *tuple) } else { return -1; } - if (!(s->state & SOCKET_CARDBUS) && SPACE(tuple->Flags)) { + if (SPACE(tuple->Flags)) { /* This is ugly, but a common CIS error is to code the long link offset incorrectly, so we check the right spot... */ read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); @@ -507,7 +495,7 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_ if (!s) return -EINVAL; - if (!(s->state & SOCKET_PRESENT)) + if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS)) return -ENODEV; link[1] = tuple->TupleLink; @@ -1192,119 +1180,6 @@ static int parse_cftable_entry(tuple_t *tuple, /*====================================================================*/ -#ifdef CONFIG_CARDBUS - -static int parse_bar(tuple_t *tuple, cistpl_bar_t *bar) -{ - u_char *p; - if (tuple->TupleDataLen < 6) - return -EINVAL; - p = (u_char *)tuple->TupleData; - bar->attr = *p; - p += 2; - bar->size = get_unaligned_le32(p); - return 0; -} - -static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config) -{ - u_char *p; - - p = (u_char *)tuple->TupleData; - if ((*p != 3) || (tuple->TupleDataLen < 6)) - return -EINVAL; - config->last_idx = *(++p); - p++; - config->base = get_unaligned_le32(p); - config->subtuples = tuple->TupleDataLen - 6; - return 0; -} - -static int parse_cftable_entry_cb(tuple_t *tuple, - cistpl_cftable_entry_cb_t *entry) -{ - u_char *p, *q, features; - - p = tuple->TupleData; - q = p + tuple->TupleDataLen; - entry->index = *p & 0x3f; - entry->flags = 0; - if (*p & 0x40) - entry->flags |= CISTPL_CFTABLE_DEFAULT; - - /* Process optional features */ - if (++p == q) - return -EINVAL; - features = *p; p++; - - /* Power options */ - if ((features & 3) > 0) { - p = parse_power(p, q, &entry->vcc); - if (p == NULL) - return -EINVAL; - } else - entry->vcc.present = 0; - if ((features & 3) > 1) { - p = parse_power(p, q, &entry->vpp1); - if (p == NULL) - return -EINVAL; - } else - entry->vpp1.present = 0; - if ((features & 3) > 2) { - p = parse_power(p, q, &entry->vpp2); - if (p == NULL) - return -EINVAL; - } else - entry->vpp2.present = 0; - - /* I/O window options */ - if (features & 0x08) { - if (p == q) - return -EINVAL; - entry->io = *p; p++; - } else - entry->io = 0; - - /* Interrupt options */ - if (features & 0x10) { - p = parse_irq(p, q, &entry->irq); - if (p == NULL) - return -EINVAL; - } else - entry->irq.IRQInfo1 = 0; - - if (features & 0x20) { - if (p == q) - return -EINVAL; - entry->mem = *p; p++; - } else - entry->mem = 0; - - /* Misc features */ - if (features & 0x80) { - if (p == q) - return -EINVAL; - entry->flags |= (*p << 8); - if (*p & 0x80) { - if (++p == q) - return -EINVAL; - entry->flags |= (*p << 16); - } - while (*p & 0x80) - if (++p == q) - return -EINVAL; - p++; - } - - entry->subtuples = q-p; - - return 0; -} - -#endif - -/*====================================================================*/ - static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo) { u_char *p, *q; @@ -1406,17 +1281,6 @@ int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse) case CISTPL_DEVICE_A: ret = parse_device(tuple, &parse->device); break; -#ifdef CONFIG_CARDBUS - case CISTPL_BAR: - ret = parse_bar(tuple, &parse->bar); - break; - case CISTPL_CONFIG_CB: - ret = parse_config_cb(tuple, &parse->config); - break; - case CISTPL_CFTABLE_ENTRY_CB: - ret = parse_cftable_entry_cb(tuple, &parse->cftable_entry_cb); - break; -#endif case CISTPL_CHECKSUM: ret = parse_checksum(tuple, &parse->checksum); break; -- cgit v1.2.3 From 180c33ee409eb3ed605d4ad9884e4a526a7655ff Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 17:34:09 +0100 Subject: pcmcia: call CIS cleanup from ds.c As ds.c is the only real user of CIS access functions, call the cleanup functions from ds.c, too. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 3 --- drivers/pcmcia/ds.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 8c51493d1f9..9d8b9c1f5a6 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -283,8 +283,6 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket) if (socket->thread) kthread_stop(socket->thread); - release_cis_mem(socket); - /* remove from our own list */ down_write(&pcmcia_socket_list_rwsem); list_del(&socket->socket_list); @@ -399,7 +397,6 @@ static void socket_shutdown(struct pcmcia_socket *s) s->ops->set_socket(s, &s->socket); s->irq.AssignedIRQ = s->irq.Config = 0; s->lock_count = 0; - destroy_cis_cache(s); kfree(s->fake_cis); s->fake_cis = NULL; #ifdef CONFIG_CARDBUS diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 87e06395c12..7bb52b003f0 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -1241,10 +1241,12 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) s->pcmcia_state.present = 0; pcmcia_card_remove(skt, NULL); handle_event(skt, event); + destroy_cis_cache(s); break; case CS_EVENT_CARD_INSERTION: s->pcmcia_state.present = 1; + destroy_cis_cache(s); /* to be on the safe side... */ pcmcia_card_add(skt); handle_event(skt, event); break; @@ -1366,6 +1368,7 @@ static void pcmcia_bus_remove_socket(struct device *dev, /* unregister any unbound devices */ mutex_lock(&socket->skt_mutex); pcmcia_card_remove(socket, NULL); + release_cis_mem(socket); mutex_unlock(&socket->skt_mutex); pcmcia_put_socket(socket); -- cgit v1.2.3 From 3f32b3c093eddc03ed477ae0d7a6938db6b94a05 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 22:22:50 +0100 Subject: pcmcia: rsrc_nonstatic io memory probe improvements Add a lot of documentation to the rsrc_nonstatic io memory probe functions. Also, add a first memory probe call -- just checking whether request_resource() succeeds -- upon adding of resources. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/rsrc_nonstatic.c | 174 ++++++++++++++++++++++++++-------------- 1 file changed, 114 insertions(+), 60 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index b886385f12e..120d5ad9929 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -264,18 +264,15 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base, } #endif -/*====================================================================== - - This is tricky... when we set up CIS memory, we try to validate - the memory window space allocations. - -======================================================================*/ +/*======================================================================*/ -/* Validation function for cards with a valid CIS */ +/** + * readable() - iomem validation function for cards with a valid CIS + */ static int readable(struct pcmcia_socket *s, struct resource *res, unsigned int *count) { - int ret = -1; + int ret = -EINVAL; s->cis_mem.res = res; s->cis_virt = ioremap(res->start, s->map_size); @@ -286,13 +283,16 @@ static int readable(struct pcmcia_socket *s, struct resource *res, s->cis_virt = NULL; } s->cis_mem.res = NULL; - if ((ret != 0) || (*count == 0)) - return 0; - return 1; + if ((ret) || (*count == 0)) + return -EINVAL; + return 0; } -/* Validation function for simple memory cards */ -static int checksum(struct pcmcia_socket *s, struct resource *res) +/** + * checksum() - iomem validation function for simple memory cards + */ +static int checksum(struct pcmcia_socket *s, struct resource *res, + unsigned int *value) { pccard_mem_map map; int i, a = 0, b = -1, d; @@ -320,61 +320,83 @@ static int checksum(struct pcmcia_socket *s, struct resource *res) iounmap(virt); } - return (b == -1) ? -1 : (a>>1); -} - -static int -cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size) -{ - struct resource *res1, *res2; - unsigned int info1, info2; - int ret = 0; - - res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe"); - res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, - "PCMCIA memprobe"); - - if (res1 && res2) { - ret = readable(s, res1, &info1); - ret += readable(s, res2, &info2); - } + if (b == -1) + return -EINVAL; - free_region(res2); - free_region(res1); + *value = a; - return (ret == 2) && (info1 == info2); + return 0; } -static int -checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size) +/** + * do_validate_mem() - low level validate a memory region for PCMCIA use + * @s: PCMCIA socket to validate + * @base: start address of resource to check + * @size: size of resource to check + * @validate: validation function to use + * + * do_validate_mem() splits up the memory region which is to be checked + * into two parts. Both are passed to the @validate() function. If + * @validate() returns non-zero, or the value parameter to @validate() + * is zero, or the value parameter is different between both calls, + * the check fails, and -EINVAL is returned. Else, 0 is returned. + */ +static int do_validate_mem(struct pcmcia_socket *s, + unsigned long base, unsigned long size, + int validate (struct pcmcia_socket *s, + struct resource *res, + unsigned int *value)) { struct resource *res1, *res2; - int a = -1, b = -1; + unsigned int info1 = 1, info2 = 1; + int ret = -EINVAL; res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe"); res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "PCMCIA memprobe"); if (res1 && res2) { - a = checksum(s, res1); - b = checksum(s, res2); + ret = 0; + if (validate) { + ret = validate(s, res1, &info1); + ret += validate(s, res2, &info2); + } } free_region(res2); free_region(res1); - return (a == b) && (a >= 0); -} + dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u", + base, base+size-1, res1, res2, ret, info1, info2); -/*====================================================================== + if ((ret) || (info1 != info2) || (info1 == 0)) + return -EINVAL; - The memory probe. If the memory list includes a 64K-aligned block - below 1MB, we probe in 64K chunks, and as soon as we accumulate at - least mem_limit free space, we quit. + return 0; +} -======================================================================*/ -static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) +/** + * do_mem_probe() - validate a memory region for PCMCIA use + * @s: PCMCIA socket to validate + * @base: start address of resource to check + * @num: size of resource to check + * @validate: validation function to use + * @fallback: validation function to use if validate fails + * + * do_mem_probe() checks a memory region for use by the PCMCIA subsystem. + * To do so, the area is split up into sensible parts, and then passed + * into the @validate() function. Only if @validate() and @fallback() fail, + * the area is marked as unavaibale for use by the PCMCIA subsystem. The + * function returns the size of the usable memory area. + */ +static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num, + int validate (struct pcmcia_socket *s, + struct resource *res, + unsigned int *value), + int fallback (struct pcmcia_socket *s, + struct resource *res, + unsigned int *value)) { struct socket_data *s_data = s->resource_data; u_long i, j, bad, fail, step; @@ -392,15 +414,14 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) for (i = j = base; i < base+num; i = j + step) { if (!fail) { for (j = i; j < base+num; j += step) { - if (cis_readable(s, j, step)) + if (!do_validate_mem(s, j, step, validate)) break; } fail = ((i == base) && (j == base+num)); } - if (fail) { - for (j = i; j < base+num; j += 2*step) - if (checksum_match(s, j, step) && - checksum_match(s, j + step, step)) + if ((fail) && (fallback)) { + for (j = i; j < base+num; j += step) + if (!do_validate_mem(s, j, step, fallback)) break; } if (i != j) { @@ -415,8 +436,14 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) return num - bad; } + #ifdef CONFIG_PCMCIA_PROBE +/** + * inv_probe() - top-to-bottom search for one usuable high memory area + * @s: PCMCIA socket to validate + * @m: resource_map to check + */ static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s) { struct socket_data *s_data = s->resource_data; @@ -431,9 +458,18 @@ static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s) } if (m->base < 0x100000) return 0; - return do_mem_probe(m->base, m->num, s); + return do_mem_probe(s, m->base, m->num, readable, checksum); } +/** + * validate_mem() - memory probe function + * @s: PCMCIA socket to validate + * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH + * + * The memory probe. If the memory list includes a 64K-aligned block + * below 1MB, we probe in 64K chunks, and as soon as we accumulate at + * least mem_limit free space, we quit. Returns 0 on usuable ports. + */ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) { struct resource_map *m, mm; @@ -456,7 +492,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) if (mm.base >= 0x100000) continue; if ((mm.base | mm.num) & 0xffff) { - ok += do_mem_probe(mm.base, mm.num, s); + ok += do_mem_probe(s, mm.base, mm.num, readable, + checksum); continue; } /* Special probe for 64K-aligned block */ @@ -466,7 +503,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) if (ok >= mem_limit) sub_interval(&s_data->mem_db, b, 0x10000); else - ok += do_mem_probe(b, 0x10000, s); + ok += do_mem_probe(s, b, 0x10000, + readable, checksum); } } } @@ -479,6 +517,13 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) #else /* CONFIG_PCMCIA_PROBE */ +/** + * validate_mem() - memory probe function + * @s: PCMCIA socket to validate + * @probe_mask: ignored + * + * Returns 0 on usuable ports. + */ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) { struct resource_map *m, mm; @@ -487,7 +532,7 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) { mm = *m; - ok += do_mem_probe(mm.base, mm.num, s); + ok += do_mem_probe(s, mm.base, mm.num, readable, checksum); } if (ok > 0) return 0; @@ -497,7 +542,13 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) #endif /* CONFIG_PCMCIA_PROBE */ -/* +/** + * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use + * @s: PCMCIA socket to validate + * + * This is tricky... when we set up CIS memory, we try to validate + * the memory window space allocations. + * * Locking note: Must be called with skt_mutex held! */ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) @@ -515,10 +566,11 @@ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) probe_mask = MEM_PROBE_HIGH; if (probe_mask & ~s_data->rsrc_mem_probe) { - if (s->state & SOCKET_PRESENT) + if (s->state & SOCKET_PRESENT) { ret = validate_mem(s, probe_mask); - if (!ret) - s_data->rsrc_mem_probe |= probe_mask; + if (!ret) + s_data->rsrc_mem_probe |= probe_mask; + } } mutex_unlock(&rsrc_mutex); @@ -723,6 +775,8 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned switch (action) { case ADD_MANAGED_RESOURCE: ret = add_interval(&data->mem_db, start, size); + if (!ret) + do_mem_probe(s, start, size, NULL, NULL); break; case REMOVE_MANAGED_RESOURCE: ret = sub_interval(&data->mem_db, start, size); -- cgit v1.2.3 From 593f010bc0d8f7fde2ce948cac3f77f6a3d9db2b Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 22:59:15 +0100 Subject: pcmcia: do not lock socket driver module in pcmcia_get_socket() Do not lock the socket driver module in pcmcia_get_socket(), as the PCMCIA core can handle a socket module removal: In pcmcia_unregister_socket(), we explicitely wait for the last put_device() to succeed. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 9d8b9c1f5a6..f0630a61da9 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -140,19 +140,13 @@ struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt) struct device *dev = get_device(&skt->dev); if (!dev) return NULL; - skt = dev_get_drvdata(dev); - if (!try_module_get(skt->owner)) { - put_device(&skt->dev); - return NULL; - } - return skt; + return dev_get_drvdata(dev); } EXPORT_SYMBOL(pcmcia_get_socket); void pcmcia_put_socket(struct pcmcia_socket *skt) { - module_put(skt->owner); put_device(&skt->dev); } EXPORT_SYMBOL(pcmcia_put_socket); -- cgit v1.2.3 From 3970dd8c5169505f0cc5e4c3e2fde7bdd9bbad3e Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 2 Jan 2010 23:19:45 +0100 Subject: pcmcia: do not lock socket driver module on card insert Do not lock the socket driver module on card insert, as the PCMCIA core can handle a socket module removal, at least if we add a call to socket_remove() on pccardd()'s shutdown. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 13 ++++++++++--- drivers/pcmcia/cs_internal.h | 20 -------------------- 2 files changed, 10 insertions(+), 23 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index f0630a61da9..137a5db2eca 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -407,7 +407,7 @@ static void socket_shutdown(struct pcmcia_socket *s) "*** DANGER *** unable to remove socket power\n"); } - cs_socket_put(s); + s->state &= ~SOCKET_INUSE; } static int socket_setup(struct pcmcia_socket *skt, int initial_delay) @@ -496,8 +496,8 @@ static int socket_insert(struct pcmcia_socket *skt) dev_dbg(&skt->dev, "insert\n"); - if (!cs_socket_get(skt)) - return -ENODEV; + WARN_ON(skt->state & SOCKET_INUSE); + skt->state |= SOCKET_INUSE; ret = socket_setup(skt, setup_delay); if (ret == 0) { @@ -697,6 +697,13 @@ static int pccardd(void *__skt) /* make sure we are running before we exit */ set_current_state(TASK_RUNNING); + /* shut down socket, if a device is still present */ + if (skt->state & SOCKET_PRESENT) { + mutex_lock(&skt->skt_mutex); + socket_remove(skt); + mutex_unlock(&skt->skt_mutex); + } + /* remove from the device core */ pccard_sysfs_remove_socket(&skt->dev); device_unregister(&skt->dev); diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 3bc02d53a3a..9a3bbad7761 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -87,26 +87,6 @@ struct pccard_resource_ops { #define SOCKET_CARDBUS 0x8000 #define SOCKET_CARDBUS_CONFIG 0x10000 -static inline int cs_socket_get(struct pcmcia_socket *skt) -{ - int ret; - - WARN_ON(skt->state & SOCKET_INUSE); - - ret = try_module_get(skt->owner); - if (ret) - skt->state |= SOCKET_INUSE; - return ret; -} - -static inline void cs_socket_put(struct pcmcia_socket *skt) -{ - if (skt->state & SOCKET_INUSE) { - skt->state &= ~SOCKET_INUSE; - module_put(skt->owner); - } -} - /* * Stuff internal to module "pcmcia_core": -- cgit v1.2.3 From 385ee871092a524869c71a8180888aadcd6ca36d Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 6 Jan 2010 11:23:58 +0100 Subject: pcmcia: remove useless indirection As release_resoure_db() used to be called only from one place, and it's a two-line function, remove it. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 3 ++- drivers/pcmcia/cs_internal.h | 3 --- drivers/pcmcia/rsrc_mgr.c | 6 ------ 3 files changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 137a5db2eca..43c90f69a7a 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -283,7 +283,8 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket) up_write(&pcmcia_socket_list_rwsem); /* wait for sysfs to drop all references */ - release_resource_db(socket); + if (socket->resource_ops->exit) + socket->resource_ops->exit(socket); wait_for_completion(&socket->socket_released); } /* pcmcia_unregister_socket */ EXPORT_SYMBOL(pcmcia_unregister_socket); diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 9a3bbad7761..7f86d09a583 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -95,9 +95,6 @@ struct pccard_resource_ops { /* cistpl.c */ int verify_cis_cache(struct pcmcia_socket *s); -/* rsrc_mgr.c */ -void release_resource_db(struct pcmcia_socket *s); - /* socket_sysfs.c */ extern int pccard_sysfs_add_socket(struct device *dev); extern void pccard_sysfs_remove_socket(struct device *dev); diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index 52db17263d8..66c780073cd 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -58,12 +58,6 @@ struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, } EXPORT_SYMBOL(pcmcia_find_mem_region); -void release_resource_db(struct pcmcia_socket *s) -{ - if (s->resource_ops->exit) - s->resource_ops->exit(s); -} - static int static_init(struct pcmcia_socket *s) { -- cgit v1.2.3 From f9c316f4a2d32e4d03497ecb24e1d2309361a5b8 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 6 Jan 2010 11:32:22 +0100 Subject: pcmcia: remove some rsrc_mgr indirections Remove rsrc_mgr indirections only used by pcmcia_resource.c Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs_internal.h | 8 -------- drivers/pcmcia/pcmcia_resource.c | 17 +++++++++++++++++ drivers/pcmcia/rsrc_mgr.c | 18 ------------------ 3 files changed, 17 insertions(+), 26 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 7f86d09a583..ad05e3b5947 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -168,14 +168,6 @@ int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple); /* rsrc_mgr.c */ int pcmcia_validate_mem(struct pcmcia_socket *s); -struct resource *pcmcia_find_io_region(unsigned long base, - int num, - unsigned long align, - struct pcmcia_socket *s); -int pcmcia_adjust_io_region(struct resource *res, - unsigned long r_start, - unsigned long r_end, - struct pcmcia_socket *s); struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index d5db95644b6..880b0b63b6a 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -43,6 +43,23 @@ module_param(io_speed, int, 0444); static u8 pcmcia_used_irq[NR_IRQS]; #endif +static int pcmcia_adjust_io_region(struct resource *res, unsigned long start, + unsigned long end, struct pcmcia_socket *s) +{ + if (s->resource_ops->adjust_io_region) + return s->resource_ops->adjust_io_region(res, start, end, s); + return -ENOMEM; +} + +static struct resource *pcmcia_find_io_region(unsigned long base, int num, + unsigned long align, + struct pcmcia_socket *s) +{ + if (s->resource_ops->find_io) + return s->resource_ops->find_io(base, num, align, s); + return NULL; +} + /** alloc_io_space * diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index 66c780073cd..81540c420bb 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -31,24 +31,6 @@ int pcmcia_validate_mem(struct pcmcia_socket *s) } EXPORT_SYMBOL(pcmcia_validate_mem); -int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start, - unsigned long r_end, struct pcmcia_socket *s) -{ - if (s->resource_ops->adjust_io_region) - return s->resource_ops->adjust_io_region(res, r_start, r_end, s); - return -ENOMEM; -} -EXPORT_SYMBOL(pcmcia_adjust_io_region); - -struct resource *pcmcia_find_io_region(unsigned long base, int num, - unsigned long align, struct pcmcia_socket *s) -{ - if (s->resource_ops->find_io) - return s->resource_ops->find_io(base, num, align, s); - return NULL; -} -EXPORT_SYMBOL(pcmcia_find_io_region); - struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, int low, struct pcmcia_socket *s) { -- cgit v1.2.3 From a7eb169dc7292979d78f2d2f1655026ae3a9ff5f Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 6 Jan 2010 12:18:13 +0100 Subject: pcmcia: m32r uses static socket resources m32r_cfc sets the socket capabilities to SS_CAP_STATIC_MAP and also sets io_offset != 0. This means no calls to &pccard_nonstatic_ops went through. Therfore, replace it with &pccard_static_ops which is exactly for this case. CC: Mamoru Sakugawa CC: Hirokazu Takata Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/Kconfig | 2 -- drivers/pcmcia/m32r_cfc.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 9f3adbd9f70..7e9fd38e14f 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -238,14 +238,12 @@ config PCMCIA_PROBE config M32R_PCC bool "M32R PCMCIA I/F" depends on M32R && CHIP_M32700 && PCMCIA - select PCCARD_NONSTATIC help Say Y here to use the M32R PCMCIA controller. config M32R_CFC bool "M32R CF I/F Controller" depends on M32R && (PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_MAPPI3 || PLAT_OPSPUT) - select PCCARD_NONSTATIC help Say Y here to use the M32R CompactFlash controller. diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c index 26a621c9e2f..0ece2cd4a85 100644 --- a/drivers/pcmcia/m32r_cfc.c +++ b/drivers/pcmcia/m32r_cfc.c @@ -764,7 +764,7 @@ static int __init init_m32r_pcc(void) for (i = 0 ; i < pcc_sockets ; i++) { socket[i].socket.dev.parent = &pcc_device.dev; socket[i].socket.ops = &pcc_operations; - socket[i].socket.resource_ops = &pccard_nonstatic_ops; + socket[i].socket.resource_ops = &pccard_static_ops; socket[i].socket.owner = THIS_MODULE; socket[i].number = i; ret = pcmcia_register_socket(&socket[i].socket); -- cgit v1.2.3 From 4e8804ff6dd1a842d9531c819a0acc9eb3bcfa3b Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 6 Jan 2010 11:19:25 +0100 Subject: pcmcia: m8xx_pcmcia.c should use iodyn resource manager The socket driver m8xx_pcmcia.c uses a static memory assignment, but io_offset is set to 0. Therefore, it seems proper to use the iodyn resource manager for this driver, as was previously the case (before commit 80128ff79d282cf71b1819dbca9b8dd47d8ed3e8). CC: Vitaly Bordug CC: Arnd Bergmann CC: Olof Johansson Signed-off-by: Dominik Brodowski --- drivers/pcmcia/Kconfig | 1 - drivers/pcmcia/m8xx_pcmcia.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 7e9fd38e14f..44b324b80e9 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -163,7 +163,6 @@ config PCMCIA_M8XX tristate "MPC8xx PCMCIA support" depends on PCMCIA && PPC && 8xx select PCCARD_IODYN - select PCCARD_NONSTATIC help Say Y here to include support for PowerPC 8xx series PCMCIA controller. diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index 7f79c4e169a..3a1fe3ab2cd 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -1233,7 +1233,7 @@ static int __init m8xx_probe(struct of_device *ofdev, socket[i].socket.io_offset = 0; socket[i].socket.pci_irq = pcmcia_schlvl; socket[i].socket.ops = &m8xx_services; - socket[i].socket.resource_ops = &pccard_nonstatic_ops; + socket[i].socket.resource_ops = &pccard_iodyn_ops; socket[i].socket.cb_dev = NULL; socket[i].socket.dev.parent = &ofdev->dev; socket[i].pcmcia = pcmcia; -- cgit v1.2.3 From 6e7b51a733fde86d3be748543215a69da04d5bb7 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 6 Jan 2010 13:57:43 +0100 Subject: pcmcia: move cistpl.c into pcmcia module As PCMCIA is the only real user of CIS access functions, include cistpl.c in the PCMCIA module, not in the PCMCIA & CardBus core module. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/Makefile | 4 +- drivers/pcmcia/cistpl.c | 158 +++++++++++++++++++++++++++++++++++++--- drivers/pcmcia/cs_internal.h | 44 ++++++----- drivers/pcmcia/ds.c | 10 +++ drivers/pcmcia/rsrc_nonstatic.c | 4 +- drivers/pcmcia/socket_sysfs.c | 150 +------------------------------------- 6 files changed, 183 insertions(+), 187 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 83ff802de54..3c83f68c803 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -2,11 +2,11 @@ # Makefile for the kernel pcmcia subsystem (c/o David Hinds) # -pcmcia_core-y += cs.o cistpl.o rsrc_mgr.o socket_sysfs.o +pcmcia_core-y += cs.o rsrc_mgr.o socket_sysfs.o pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o obj-$(CONFIG_PCCARD) += pcmcia_core.o -pcmcia-y += ds.o pcmcia_resource.o +pcmcia-y += ds.o pcmcia_resource.o cistpl.o pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o obj-$(CONFIG_PCMCIA) += pcmcia.o diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 368367ced62..936417c3e79 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -76,7 +76,6 @@ void release_cis_mem(struct pcmcia_socket *s) s->cis_virt = NULL; } } -EXPORT_SYMBOL(release_cis_mem); /* * Map the card memory at "card_offset" into virtual space. @@ -195,7 +194,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, *(u_char *)(ptr+2), *(u_char *)(ptr+3)); return 0; } -EXPORT_SYMBOL(pcmcia_read_cis_mem); void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, @@ -254,7 +252,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, } } } -EXPORT_SYMBOL(pcmcia_write_cis_mem); /*====================================================================== @@ -335,7 +332,6 @@ void destroy_cis_cache(struct pcmcia_socket *s) kfree(cis); } } -EXPORT_SYMBOL(destroy_cis_cache); /*====================================================================== @@ -374,7 +370,6 @@ int verify_cis_cache(struct pcmcia_socket *s) kfree(buf); return 0; } -EXPORT_SYMBOL(verify_cis_cache); /*====================================================================== @@ -400,7 +395,6 @@ int pcmcia_replace_cis(struct pcmcia_socket *s, memcpy(s->fake_cis, data, len); return 0; } -EXPORT_SYMBOL(pcmcia_replace_cis); /*====================================================================== @@ -446,7 +440,6 @@ int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple } return pccard_get_next_tuple(s, function, tuple); } -EXPORT_SYMBOL(pccard_get_first_tuple); static int follow_link(struct pcmcia_socket *s, tuple_t *tuple) { @@ -582,7 +575,6 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_ tuple->CISOffset = ofs + 2; return 0; } -EXPORT_SYMBOL(pccard_get_next_tuple); /*====================================================================*/ @@ -606,7 +598,6 @@ int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple) _MIN(len, tuple->TupleDataMax), tuple->TupleData); return 0; } -EXPORT_SYMBOL(pccard_get_tuple_data); /*====================================================================== @@ -1379,7 +1370,6 @@ done: kfree(buf); return ret; } -EXPORT_SYMBOL(pccard_read_tuple); /** @@ -1439,7 +1429,6 @@ next_entry: kfree(buf); return ret; } -EXPORT_SYMBOL(pccard_loop_tuple); /** @@ -1533,4 +1522,149 @@ done: kfree(p); return ret; } -EXPORT_SYMBOL(pccard_validate_cis); + + +#define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev) + +static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, + loff_t off, size_t count) +{ + tuple_t tuple; + int status, i; + loff_t pointer = 0; + ssize_t ret = 0; + u_char *tuplebuffer; + u_char *tempbuffer; + + tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL); + if (!tuplebuffer) + return -ENOMEM; + + tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL); + if (!tempbuffer) { + ret = -ENOMEM; + goto free_tuple; + } + + memset(&tuple, 0, sizeof(tuple_t)); + + tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON; + tuple.DesiredTuple = RETURN_FIRST_TUPLE; + tuple.TupleOffset = 0; + + status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple); + while (!status) { + tuple.TupleData = tuplebuffer; + tuple.TupleDataMax = 255; + memset(tuplebuffer, 0, sizeof(u_char) * 255); + + status = pccard_get_tuple_data(s, &tuple); + if (status) + break; + + if (off < (pointer + 2 + tuple.TupleDataLen)) { + tempbuffer[0] = tuple.TupleCode & 0xff; + tempbuffer[1] = tuple.TupleLink & 0xff; + for (i = 0; i < tuple.TupleDataLen; i++) + tempbuffer[i + 2] = tuplebuffer[i] & 0xff; + + for (i = 0; i < (2 + tuple.TupleDataLen); i++) { + if (((i + pointer) >= off) && + (i + pointer) < (off + count)) { + buf[ret] = tempbuffer[i]; + ret++; + } + } + } + + pointer += 2 + tuple.TupleDataLen; + + if (pointer >= (off + count)) + break; + + if (tuple.TupleCode == CISTPL_END) + break; + status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple); + } + + kfree(tempbuffer); + free_tuple: + kfree(tuplebuffer); + + return ret; +} + + +static ssize_t pccard_show_cis(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + unsigned int size = 0x200; + + if (off >= size) + count = 0; + else { + struct pcmcia_socket *s; + unsigned int chains; + + if (off + count > size) + count = size - off; + + s = to_socket(container_of(kobj, struct device, kobj)); + + if (!(s->state & SOCKET_PRESENT)) + return -ENODEV; + if (pccard_validate_cis(s, &chains)) + return -EIO; + if (!chains) + return -ENODATA; + + count = pccard_extract_cis(s, buf, off, count); + } + + return count; +} + + +static ssize_t pccard_store_cis(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct pcmcia_socket *s; + int error; + + s = to_socket(container_of(kobj, struct device, kobj)); + + if (off) + return -EINVAL; + + if (count >= CISTPL_MAX_CIS_SIZE) + return -EINVAL; + + if (!(s->state & SOCKET_PRESENT)) + return -ENODEV; + + error = pcmcia_replace_cis(s, buf, count); + if (error) + return -EIO; + + mutex_lock(&s->skt_mutex); + if ((s->callback) && (s->state & SOCKET_PRESENT) && + !(s->state & SOCKET_CARDBUS)) { + if (try_module_get(s->callback->owner)) { + s->callback->requery(s, 1); + module_put(s->callback->owner); + } + } + mutex_unlock(&s->skt_mutex); + + return count; +} + + +struct bin_attribute pccard_cis_attr = { + .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR }, + .size = 0x200, + .read = pccard_show_cis, + .write = pccard_store_cis, +}; diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index ad05e3b5947..3f438afdcd4 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -92,9 +92,6 @@ struct pccard_resource_ops { * Stuff internal to module "pcmcia_core": */ -/* cistpl.c */ -int verify_cis_cache(struct pcmcia_socket *s); - /* socket_sysfs.c */ extern int pccard_sysfs_add_socket(struct device *dev); extern void pccard_sysfs_remove_socket(struct device *dev); @@ -102,8 +99,6 @@ extern void pccard_sysfs_remove_socket(struct device *dev); /* cardbus.c */ int cb_alloc(struct pcmcia_socket *s); void cb_free(struct pcmcia_socket *s); -int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len, - void *ptr); @@ -116,6 +111,7 @@ struct pcmcia_callback{ int (*event) (struct pcmcia_socket *s, event_t event, int priority); void (*requery) (struct pcmcia_socket *s, int new_cis); + int (*validate) (struct pcmcia_socket *s, unsigned int *i); int (*suspend) (struct pcmcia_socket *s); int (*resume) (struct pcmcia_socket *s); }; @@ -137,7 +133,26 @@ int pcmcia_insert_card(struct pcmcia_socket *skt); struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); void pcmcia_put_socket(struct pcmcia_socket *skt); +/* rsrc_mgr.c */ +int pcmcia_validate_mem(struct pcmcia_socket *s); +struct resource *pcmcia_find_mem_region(u_long base, + u_long num, + u_long align, + int low, + struct pcmcia_socket *s); + +/* + * Stuff internal to module "pcmcia". + */ +/* ds.c */ +extern struct bus_type pcmcia_bus_type; + +/* pcmcia_resource.c */ +extern int pcmcia_release_configuration(struct pcmcia_device *p_dev); + /* cistpl.c */ +extern struct bin_attribute pccard_cis_attr; + int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr); void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, @@ -149,8 +164,8 @@ int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, int pcmcia_replace_cis(struct pcmcia_socket *s, const u8 *data, const size_t len); int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *count); +int verify_cis_cache(struct pcmcia_socket *s); -/* loop over CIS entries */ int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, cisparse_t *parse, void *priv_data, int (*loop_tuple) (tuple_t *tuple, @@ -166,23 +181,6 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple); -/* rsrc_mgr.c */ -int pcmcia_validate_mem(struct pcmcia_socket *s); -struct resource *pcmcia_find_mem_region(u_long base, - u_long num, - u_long align, - int low, - struct pcmcia_socket *s); - -/* - * Stuff internal to module "pcmcia". - */ -/* ds.c */ -extern struct bus_type pcmcia_bus_type; - -/* pcmcia_resource.c */ -extern int pcmcia_release_configuration(struct pcmcia_device *p_dev); - #ifdef CONFIG_PCMCIA_IOCTL /* ds.c */ extern spinlock_t pcmcia_dev_list_lock; diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 7bb52b003f0..0ab4fe04592 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -1313,6 +1313,7 @@ static struct pcmcia_callback pcmcia_bus_callback = { .owner = THIS_MODULE, .event = ds_event, .requery = pcmcia_bus_rescan, + .validate = pccard_validate_cis, .suspend = pcmcia_bus_suspend, .resume = pcmcia_bus_resume, }; @@ -1336,6 +1337,13 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev, */ msleep(250); + ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr); + if (ret) { + dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n"); + pcmcia_put_socket(socket); + return ret; + } + #ifdef CONFIG_PCMCIA_IOCTL init_waitqueue_head(&socket->queue); #endif @@ -1371,6 +1379,8 @@ static void pcmcia_bus_remove_socket(struct device *dev, release_cis_mem(socket); mutex_unlock(&socket->skt_mutex); + sysfs_remove_bin_file(&dev->kobj, &pccard_cis_attr); + pcmcia_put_socket(socket); return; diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 120d5ad9929..91626c17f97 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -277,7 +277,9 @@ static int readable(struct pcmcia_socket *s, struct resource *res, s->cis_mem.res = res; s->cis_virt = ioremap(res->start, s->map_size); if (s->cis_virt) { - ret = pccard_validate_cis(s, count); + /* as we're only called from pcmcia.c, we're safe */ + if (s->callback->validate) + ret = s->callback->validate(s, count); /* invalidate mapping */ iounmap(s->cis_virt); s->cis_virt = NULL; diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index 7a456000332..537d79305e7 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -213,138 +213,6 @@ static ssize_t pccard_store_resource(struct device *dev, } static DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource); - -static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, loff_t off, size_t count) -{ - tuple_t tuple; - int status, i; - loff_t pointer = 0; - ssize_t ret = 0; - u_char *tuplebuffer; - u_char *tempbuffer; - - tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL); - if (!tuplebuffer) - return -ENOMEM; - - tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL); - if (!tempbuffer) { - ret = -ENOMEM; - goto free_tuple; - } - - memset(&tuple, 0, sizeof(tuple_t)); - - tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON; - tuple.DesiredTuple = RETURN_FIRST_TUPLE; - tuple.TupleOffset = 0; - - status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple); - while (!status) { - tuple.TupleData = tuplebuffer; - tuple.TupleDataMax = 255; - memset(tuplebuffer, 0, sizeof(u_char) * 255); - - status = pccard_get_tuple_data(s, &tuple); - if (status) - break; - - if (off < (pointer + 2 + tuple.TupleDataLen)) { - tempbuffer[0] = tuple.TupleCode & 0xff; - tempbuffer[1] = tuple.TupleLink & 0xff; - for (i = 0; i < tuple.TupleDataLen; i++) - tempbuffer[i + 2] = tuplebuffer[i] & 0xff; - - for (i = 0; i < (2 + tuple.TupleDataLen); i++) { - if (((i + pointer) >= off) && - (i + pointer) < (off + count)) { - buf[ret] = tempbuffer[i]; - ret++; - } - } - } - - pointer += 2 + tuple.TupleDataLen; - - if (pointer >= (off + count)) - break; - - if (tuple.TupleCode == CISTPL_END) - break; - status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple); - } - - kfree(tempbuffer); - free_tuple: - kfree(tuplebuffer); - - return ret; -} - -static ssize_t pccard_show_cis(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) -{ - unsigned int size = 0x200; - - if (off >= size) - count = 0; - else { - struct pcmcia_socket *s; - unsigned int chains; - - if (off + count > size) - count = size - off; - - s = to_socket(container_of(kobj, struct device, kobj)); - - if (!(s->state & SOCKET_PRESENT)) - return -ENODEV; - if (pccard_validate_cis(s, &chains)) - return -EIO; - if (!chains) - return -ENODATA; - - count = pccard_extract_cis(s, buf, off, count); - } - - return count; -} - -static ssize_t pccard_store_cis(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) -{ - struct pcmcia_socket *s = to_socket(container_of(kobj, struct device, kobj)); - int error; - - if (off) - return -EINVAL; - - if (count >= CISTPL_MAX_CIS_SIZE) - return -EINVAL; - - if (!(s->state & SOCKET_PRESENT)) - return -ENODEV; - - error = pcmcia_replace_cis(s, buf, count); - if (error) - return -EIO; - - mutex_lock(&s->skt_mutex); - if ((s->callback) && (s->state & SOCKET_PRESENT) && - !(s->state & SOCKET_CARDBUS)) { - if (try_module_get(s->callback->owner)) { - s->callback->requery(s, 1); - module_put(s->callback->owner); - } - } - mutex_unlock(&s->skt_mutex); - - return count; -} - - static struct attribute *pccard_socket_attributes[] = { &dev_attr_card_type.attr, &dev_attr_card_voltage.attr, @@ -362,28 +230,12 @@ static const struct attribute_group socket_attrs = { .attrs = pccard_socket_attributes, }; -static struct bin_attribute pccard_cis_attr = { - .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR }, - .size = 0x200, - .read = pccard_show_cis, - .write = pccard_store_cis, -}; - int pccard_sysfs_add_socket(struct device *dev) { - int ret = 0; - - ret = sysfs_create_group(&dev->kobj, &socket_attrs); - if (!ret) { - ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr); - if (ret) - sysfs_remove_group(&dev->kobj, &socket_attrs); - } - return ret; + return sysfs_create_group(&dev->kobj, &socket_attrs); } void pccard_sysfs_remove_socket(struct device *dev) { - sysfs_remove_bin_file(&dev->kobj, &pccard_cis_attr); sysfs_remove_group(&dev->kobj, &socket_attrs); } -- cgit v1.2.3 From a3ac9af56c14c366a76fb4916995e57392c7b7d5 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 6 Jan 2010 14:03:03 +0100 Subject: pcmcia: remove remaining rsrc_mgr indirections Move rsrc_mgr indirections only used by the pcmcia module to the pcmcia module. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs_internal.h | 15 +++++++-------- drivers/pcmcia/pcmcia_resource.c | 16 ++++++++++++++++ drivers/pcmcia/rsrc_mgr.c | 20 -------------------- 3 files changed, 23 insertions(+), 28 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 3f438afdcd4..76ac4444f0e 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -133,14 +133,6 @@ int pcmcia_insert_card(struct pcmcia_socket *skt); struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); void pcmcia_put_socket(struct pcmcia_socket *skt); -/* rsrc_mgr.c */ -int pcmcia_validate_mem(struct pcmcia_socket *s); -struct resource *pcmcia_find_mem_region(u_long base, - u_long num, - u_long align, - int low, - struct pcmcia_socket *s); - /* * Stuff internal to module "pcmcia". */ @@ -149,6 +141,13 @@ extern struct bus_type pcmcia_bus_type; /* pcmcia_resource.c */ extern int pcmcia_release_configuration(struct pcmcia_device *p_dev); +extern int pcmcia_validate_mem(struct pcmcia_socket *s); +extern struct resource *pcmcia_find_mem_region(u_long base, + u_long num, + u_long align, + int low, + struct pcmcia_socket *s); + /* cistpl.c */ extern struct bin_attribute pccard_cis_attr; diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 880b0b63b6a..8ceb7abc580 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -60,6 +60,22 @@ static struct resource *pcmcia_find_io_region(unsigned long base, int num, return NULL; } +int pcmcia_validate_mem(struct pcmcia_socket *s) +{ + if (s->resource_ops->validate_mem) + return s->resource_ops->validate_mem(s); + /* if there is no callback, we can assume that everything is OK */ + return 0; +} + +struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, + int low, struct pcmcia_socket *s) +{ + if (s->resource_ops->find_mem) + return s->resource_ops->find_mem(base, num, align, low, s); + return NULL; +} + /** alloc_io_space * diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index 81540c420bb..cdd30c18006 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -21,26 +21,6 @@ #include #include "cs_internal.h" - -int pcmcia_validate_mem(struct pcmcia_socket *s) -{ - if (s->resource_ops->validate_mem) - return s->resource_ops->validate_mem(s); - /* if there is no callback, we can assume that everything is OK */ - return 0; -} -EXPORT_SYMBOL(pcmcia_validate_mem); - -struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, - int low, struct pcmcia_socket *s) -{ - if (s->resource_ops->find_mem) - return s->resource_ops->find_mem(base, num, align, low, s); - return NULL; -} -EXPORT_SYMBOL(pcmcia_find_mem_region); - - static int static_init(struct pcmcia_socket *s) { unsigned long flags; -- cgit v1.2.3 From 3a86e1807a53b7164c4ca2aec538d8a5d15416f1 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 6 Jan 2010 14:33:15 +0100 Subject: pcmcia: do not use resource manager on !PCMCIA If only CardBus cards are used, but not PCMCIA cards, we do not need the extensive resource management functions provided for by rsrc_nonstatic.c (~240K). Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 44b324b80e9..efc51b9e811 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -84,7 +84,7 @@ config YENTA tristate "CardBus yenta-compatible bridge support" depends on PCI select CARDBUS if !EMBEDDED - select PCCARD_NONSTATIC + select PCCARD_NONSTATIC if PCMCIA ---help--- This option enables support for CardBus host bridges. Virtually all modern PCMCIA bridges are CardBus compatible. A "bridge" is @@ -162,7 +162,7 @@ config TCIC config PCMCIA_M8XX tristate "MPC8xx PCMCIA support" depends on PCMCIA && PPC && 8xx - select PCCARD_IODYN + select PCCARD_IODYN if PCMCIA help Say Y here to include support for PowerPC 8xx series PCMCIA controller. -- cgit v1.2.3 From 9d9c98e89ee24b7d42f72c4f663c4d644d1c3f81 Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Thu, 7 Jan 2010 21:03:11 +0100 Subject: pcmcia: fix yenta dependency on PCCARD_NONSTATIC With CONFIG_PCMCIA=m and CONFIG_YENTA=y, we get drivers/built-in.o: In function `yenta_probe': yenta_socket.c:(.devinit.text+0x1e582): undefined reference to `pccard_nonstatic_ops' This is because select PCCARD_NONSTATIC if PCMCIA sets PCCARD_NONSTATIC = min(YENTA, PCMCIA). Change it to 'if PCMCIA!=n' to remove the upper limit. [linux@dominikbrodowski.net: propagate change to PCMICA_M8XX] Reported-by: Randy Dunlap Signed-off-by: Michal Marek Acked-by: Randy Dunlap Signed-off-by: Dominik Brodowski --- drivers/pcmcia/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index efc51b9e811..e8f35dac2d5 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -84,7 +84,7 @@ config YENTA tristate "CardBus yenta-compatible bridge support" depends on PCI select CARDBUS if !EMBEDDED - select PCCARD_NONSTATIC if PCMCIA + select PCCARD_NONSTATIC if PCMCIA != n ---help--- This option enables support for CardBus host bridges. Virtually all modern PCMCIA bridges are CardBus compatible. A "bridge" is @@ -161,8 +161,8 @@ config TCIC config PCMCIA_M8XX tristate "MPC8xx PCMCIA support" - depends on PCMCIA && PPC && 8xx - select PCCARD_IODYN if PCMCIA + depends on PCCARD && PPC && 8xx + select PCCARD_IODYN if PCMCIA != n help Say Y here to include support for PowerPC 8xx series PCMCIA controller. -- cgit v1.2.3 From 63c9a8b3023e6e276343da56e0f31a98c28ece9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20N=C3=A9meth?= Date: Tue, 12 Jan 2010 08:56:13 +0100 Subject: pcmcia: make Open Firmware device id constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The match_table field of the struct of_device_id is constant in so it is worth to make the initialization data also constant. The semantic match that finds this kind of pattern is as follows: (http://coccinelle.lip6.fr/) // @r@ disable decl_init,const_decl_init; identifier I1, I2, x; @@ struct I1 { ... const struct I2 *x; ... }; @s@ identifier r.I1, y; identifier r.x, E; @@ struct I1 y = { .x = E, }; @c@ identifier r.I2; identifier s.E; @@ const struct I2 E[] = ... ; @depends on !c@ identifier r.I2; identifier s.E; @@ + const struct I2 E[] = ...; // Signed-off-by: Márton Németh Cc: Julia Lawall Cc: cocci@diku.dk Acked-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/electra_cf.c | 2 +- drivers/pcmcia/m8xx_pcmcia.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c index d187ba4c5e0..89cfddca089 100644 --- a/drivers/pcmcia/electra_cf.c +++ b/drivers/pcmcia/electra_cf.c @@ -347,7 +347,7 @@ static int __devexit electra_cf_remove(struct of_device *ofdev) return 0; } -static struct of_device_id electra_cf_match[] = { +static const struct of_device_id electra_cf_match[] = { { .compatible = "electra-cf", }, diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index 3a1fe3ab2cd..61c21591812 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -1303,7 +1303,7 @@ static int m8xx_resume(struct platform_device *pdev) #define m8xx_resume NULL #endif -static struct of_device_id m8xx_pcmcia_match[] = { +static const struct of_device_id m8xx_pcmcia_match[] = { { .type = "pcmcia", .compatible = "fsl,pq-pcmcia", -- cgit v1.2.3 From 35169529093be3bbef70afd3c4125e35cece7e03 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 10 Jan 2010 09:41:24 +0100 Subject: pcmcia/yenta: add module parameter for O2 speedups O2-bridges can do read prefetch and write burst. However, for some combinations of older bridges and cards, this causes problems, so it is disabled for those bridges. Now, as some users know their setup works with the speedups enabled, a new parameter is introduced to the driver. Now, a user can specifically enable or disable these features, while the default is what we have today: detect the bridge and decide accordingly. Fixes Bugzilla entry 15014. Simplify and unify the printouts, fix a whitespace issue while we are here. Signed-off-by: Wolfram Sang Tested-by: frodone@gmail.com [linux@dominikbrodowski.net: whitespace fixes] Signed-off-by: Dominik Brodowski --- drivers/pcmcia/o2micro.h | 45 ++++++++++++++++++++++++++++--------------- drivers/pcmcia/yenta_socket.c | 5 +++++ 2 files changed, 34 insertions(+), 16 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/o2micro.h b/drivers/pcmcia/o2micro.h index 624442fc0d3..e74bebac269 100644 --- a/drivers/pcmcia/o2micro.h +++ b/drivers/pcmcia/o2micro.h @@ -116,13 +116,12 @@ static int o2micro_override(struct yenta_socket *socket) * from Eric Still, 02Micro. */ u8 a, b; + bool use_speedup; if (PCI_FUNC(socket->dev->devfn) == 0) { a = config_readb(socket, O2_RESERVED1); b = config_readb(socket, O2_RESERVED2); - - dev_printk(KERN_INFO, &socket->dev->dev, - "O2: res at 0x94/0xD4: %02x/%02x\n", a, b); + dev_dbg(&socket->dev->dev, "O2: 0x94/0xD4: %02x/%02x\n", a, b); switch (socket->dev->device) { /* @@ -135,23 +134,37 @@ static int o2micro_override(struct yenta_socket *socket) case PCI_DEVICE_ID_O2_6812: case PCI_DEVICE_ID_O2_6832: case PCI_DEVICE_ID_O2_6836: - case PCI_DEVICE_ID_O2_6933: - dev_printk(KERN_INFO, &socket->dev->dev, - "Yenta O2: old bridge, disabling read " - "prefetch/write burst\n"); - config_writeb(socket, O2_RESERVED1, - a & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST)); - config_writeb(socket, O2_RESERVED2, - b & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST)); + case PCI_DEVICE_ID_O2_6933: + use_speedup = false; break; - default: - dev_printk(KERN_INFO , &socket->dev->dev, - "O2: enabling read prefetch/write burst\n"); + use_speedup = true; + break; + } + + /* the user may override our decision */ + if (strcasecmp(o2_speedup, "on") == 0) + use_speedup = true; + else if (strcasecmp(o2_speedup, "off") == 0) + use_speedup = false; + else if (strcasecmp(o2_speedup, "default") != 0) + dev_warn(&socket->dev->dev, + "O2: Unknown parameter, using 'default'"); + + if (use_speedup) { + dev_info(&socket->dev->dev, + "O2: enabling read prefetch/write burst\n"); + config_writeb(socket, O2_RESERVED1, + a | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST); + config_writeb(socket, O2_RESERVED2, + b | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST); + } else { + dev_info(&socket->dev->dev, + "O2: disabling read prefetch/write burst\n"); config_writeb(socket, O2_RESERVED1, - a | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST); + a & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST)); config_writeb(socket, O2_RESERVED2, - b | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST); + b & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST)); } } diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index e4d12acdd52..041a75a7e55 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -37,6 +37,11 @@ static int pwr_irqs_off; module_param(pwr_irqs_off, bool, 0644); MODULE_PARM_DESC(pwr_irqs_off, "Force IRQs off during power-on of slot. Use only when seeing IRQ storms!"); +static char o2_speedup[] = "default"; +module_param_string(o2_speedup, o2_speedup, sizeof(o2_speedup), 0444); +MODULE_PARM_DESC(o2_speedup, "Use prefetch/burst for O2-bridges: 'on', 'off' " + "or 'default' (uses recommended behaviour for the detected bridge)"); + #define debug(x, s, args...) dev_dbg(&s->dev->dev, x, ##args) /* Don't ask.. */ -- cgit v1.2.3 From 4aa50bc9c2b2d5ab5c63658e0fadad03a08835cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 13 Jan 2010 12:05:45 +0100 Subject: pcmcia/bfin_cf: don't check platform_get_irq's return value against zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit platform_get_irq returns -ENXIO on failure, so !irq was probably always true. Better use irq <= 0. Note that a return value of zero is still handled as error even though this could mean irq0. This is a followup to 305b3228f9ff4d59f49e6d34a7034d44ee8ce2f0 that changed the return value of platform_get_irq from 0 to -ENXIO on error. Signed-off-by: Uwe Kleine-König Cc: David Vrabel Cc: Greg Kroah-Hartman Cc: Rafael J. Wysocki Signed-off-by: Dominik Brodowski --- drivers/pcmcia/bfin_cf_pcmcia.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c index 300b368605c..2482ce7ac6d 100644 --- a/drivers/pcmcia/bfin_cf_pcmcia.c +++ b/drivers/pcmcia/bfin_cf_pcmcia.c @@ -205,7 +205,7 @@ static int __devinit bfin_cf_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n"); irq = platform_get_irq(pdev, 0); - if (!irq) + if (irq <= 0) return -EINVAL; cd_pfx = platform_get_irq(pdev, 1); /*Card Detect GPIO PIN */ -- cgit v1.2.3 From 7432a5b6c7e38f5d89e890c6fcc00990006ab997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 22 Jan 2010 20:13:27 +0100 Subject: pcmcia/at91_cf: don't redefine SZ_2K MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: drivers/pcmcia/at91_cf.c:55:1: warning: "SZ_2K" redefined Since c1191b0 ([ARM] Kirkwood: create a mapping for the Security Accelerator SRAM) SZ_2K is defined in arch/arm/include/asm/sizes.h. Signed-off-by: Uwe Kleine-König Acked-by: Andrew Victor Signed-off-by: Dominik Brodowski --- drivers/pcmcia/at91_cf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index e1dccedc596..5d228071ec6 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -52,8 +52,6 @@ struct at91_cf_socket { unsigned long phys_baseaddr; }; -#define SZ_2K (2 * SZ_1K) - static inline int at91_cf_present(struct at91_cf_socket *cf) { return !gpio_get_value(cf->board->det_pin); -- cgit v1.2.3 From 0d418dfae81987f09a2c13778318d56abee49529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Sat, 23 Jan 2010 21:59:22 +0100 Subject: pcmcia/omap_cf: don't redefine SZ_2K MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: drivers/pcmcia/omap_cf.c:74:1: warning: "SZ_2K" redefined Since c1191b0 ([ARM] Kirkwood: create a mapping for the Security Accelerator SRAM) SZ_2K is defined in arch/arm/include/asm/sizes.h. Signed-off-by: Uwe Kleine-König Signed-off-by: Dominik Brodowski --- drivers/pcmcia/omap_cf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c index 663781d2012..3ef99155239 100644 --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -71,8 +71,6 @@ struct omap_cf_socket { #define POLL_INTERVAL (2 * HZ) -#define SZ_2K (2 * SZ_1K) - /*--------------------------------------------------------------------------*/ static int omap_cf_ss_init(struct pcmcia_socket *s) -- cgit v1.2.3 From c6958fdb041db6ed77f24e871dd4af5f059d1a2b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 6 Feb 2010 22:09:44 +0100 Subject: pcmcia/i82365: fix typos in comments Signed-off-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/i82365.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/i82365.h b/drivers/pcmcia/i82365.h index 622860c689d..849ef1b5d68 100644 --- a/drivers/pcmcia/i82365.h +++ b/drivers/pcmcia/i82365.h @@ -77,8 +77,8 @@ #define I365_VPP2_5V 0x04 /* Vpp2 = 5.0v */ #define I365_VPP2_12V 0x08 /* Vpp2 = 12.0v */ #define I365_VPP1_MASK 0x03 /* Mask for turning off Vpp1 */ -#define I365_VPP1_5V 0x01 /* Vpp2 = 5.0v */ -#define I365_VPP1_12V 0x02 /* Vpp2 = 12.0v */ +#define I365_VPP1_5V 0x01 /* Vpp1 = 5.0v */ +#define I365_VPP1_12V 0x02 /* Vpp1 = 12.0v */ /* Flags for I365_INTCTL */ #define I365_RING_ENA 0x80 -- cgit v1.2.3 From 6b8e087b86c59c3941e125738d30cf38014089e0 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 12 Jan 2010 21:42:51 +0100 Subject: pcmcia: add locking to set_mem_map() Protect the pccard_operations callback "set_mem_map" by a new mutex ops_mutex. This mutex also protects the following values in struct pcmcia_socket: pccard_mem_map win[] pccard_mem_map cis_mem void __iomem *cis_virt Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cistpl.c | 6 ++++++ drivers/pcmcia/cs.c | 1 + drivers/pcmcia/pcmcia_resource.c | 20 +++++++++++++++----- drivers/pcmcia/rsrc_nonstatic.c | 8 ++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 936417c3e79..9ad66c9848e 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -64,6 +64,7 @@ module_param(cis_width, int, 0444); void release_cis_mem(struct pcmcia_socket *s) { + mutex_lock(&s->ops_mutex); if (s->cis_mem.flags & MAP_ACTIVE) { s->cis_mem.flags &= ~MAP_ACTIVE; s->ops->set_mem_map(s, &s->cis_mem); @@ -75,6 +76,7 @@ void release_cis_mem(struct pcmcia_socket *s) iounmap(s->cis_virt); s->cis_virt = NULL; } + mutex_unlock(&s->ops_mutex); } /* @@ -88,11 +90,13 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag pccard_mem_map *mem = &s->cis_mem; int ret; + mutex_lock(&s->ops_mutex); if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) { mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s); if (mem->res == NULL) { dev_printk(KERN_NOTICE, &s->dev, "cs: unable to map card memory!\n"); + mutex_unlock(&s->ops_mutex); return NULL; } s->cis_virt = NULL; @@ -108,6 +112,7 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag if (ret) { iounmap(s->cis_virt); s->cis_virt = NULL; + mutex_unlock(&s->ops_mutex); return NULL; } @@ -117,6 +122,7 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag s->cis_virt = ioremap(mem->static_start, s->map_size); } + mutex_unlock(&s->ops_mutex); return s->cis_virt; } diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 43c90f69a7a..91aa1f28406 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -222,6 +222,7 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) init_completion(&socket->socket_released); init_completion(&socket->thread_done); mutex_init(&socket->skt_mutex); + mutex_init(&socket->ops_mutex); spin_lock_init(&socket->thread_lock); if (socket->resource_ops->init) { diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 8ceb7abc580..f31ba89e40d 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -223,6 +223,7 @@ int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh, memreq_t *req) { struct pcmcia_socket *s = p_dev->socket; + int ret; wh--; if (wh >= MAX_WIN) @@ -231,12 +232,13 @@ int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh, dev_dbg(&s->dev, "failure: requested page is zero\n"); return -EINVAL; } + mutex_lock(&s->ops_mutex); s->win[wh].card_start = req->CardOffset; - if (s->ops->set_mem_map(s, &s->win[wh]) != 0) { - dev_dbg(&s->dev, "failed to set_mem_map\n"); - return -EIO; - } - return 0; + ret = s->ops->set_mem_map(s, &s->win[wh]); + if (ret) + dev_warn(&s->dev, "failed to set_mem_map\n"); + mutex_unlock(&s->ops_mutex); + return ret; } /* pcmcia_map_mem_page */ EXPORT_SYMBOL(pcmcia_map_mem_page); @@ -437,10 +439,12 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh) if (wh >= MAX_WIN) return -EINVAL; + mutex_lock(&s->ops_mutex); win = &s->win[wh]; if (!(p_dev->_win & CLIENT_WIN_REQ(wh))) { dev_dbg(&s->dev, "not releasing unknown window\n"); + mutex_unlock(&s->ops_mutex); return -EINVAL; } @@ -456,6 +460,7 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh) win->res = NULL; } p_dev->_win &= ~CLIENT_WIN_REQ(wh); + mutex_unlock(&s->ops_mutex); return 0; } /* pcmcia_release_window */ @@ -829,6 +834,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha return -EINVAL; } + mutex_lock(&s->ops_mutex); win = &s->win[w]; if (!(s->features & SS_CAP_STATIC_MAP)) { @@ -836,6 +842,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha (req->Attributes & WIN_MAP_BELOW_1MB), s); if (!win->res) { dev_dbg(&s->dev, "allocating mem region failed\n"); + mutex_unlock(&s->ops_mutex); return -EINVAL; } } @@ -854,8 +861,10 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha if (req->Attributes & WIN_USE_WAIT) win->flags |= MAP_USE_WAIT; win->card_start = 0; + if (s->ops->set_mem_map(s, win) != 0) { dev_dbg(&s->dev, "failed to set memory mapping\n"); + mutex_unlock(&s->ops_mutex); return -EIO; } s->state |= SOCKET_WIN_REQ(w); @@ -866,6 +875,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha else req->Base = win->res->start; + mutex_unlock(&s->ops_mutex); *wh = w + 1; return 0; diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 91626c17f97..1de46cf2772 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -274,17 +274,21 @@ static int readable(struct pcmcia_socket *s, struct resource *res, { int ret = -EINVAL; + mutex_lock(&s->ops_mutex); s->cis_mem.res = res; s->cis_virt = ioremap(res->start, s->map_size); if (s->cis_virt) { + mutex_unlock(&s->ops_mutex); /* as we're only called from pcmcia.c, we're safe */ if (s->callback->validate) ret = s->callback->validate(s, count); /* invalidate mapping */ + mutex_lock(&s->ops_mutex); iounmap(s->cis_virt); s->cis_virt = NULL; } s->cis_mem.res = NULL; + mutex_unlock(&s->ops_mutex); if ((ret) || (*count == 0)) return -EINVAL; return 0; @@ -300,6 +304,8 @@ static int checksum(struct pcmcia_socket *s, struct resource *res, int i, a = 0, b = -1, d; void __iomem *virt; + mutex_lock(&s->ops_mutex); + virt = ioremap(res->start, s->map_size); if (virt) { map.map = 0; @@ -322,6 +328,8 @@ static int checksum(struct pcmcia_socket *s, struct resource *res, iounmap(virt); } + mutex_unlock(&s->ops_mutex); + if (b == -1) return -EINVAL; -- cgit v1.2.3 From 8680c4b3faa298dc768c2a78a94a84d89854eca9 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 12 Jan 2010 22:05:36 +0100 Subject: pcmcia: also lock fake and cache CIS by ops_mutex Specifically, struct list_head cis_cache; size_t fake_cis_len; u8 *fake_cis; are protected. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cistpl.c | 18 +++++++++++++++++- drivers/pcmcia/ds.c | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 9ad66c9848e..14de287a8bf 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -277,20 +277,24 @@ static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, if (s->state & SOCKET_CARDBUS) return; + mutex_lock(&s->ops_mutex); if (s->fake_cis) { if (s->fake_cis_len >= addr+len) memcpy(ptr, s->fake_cis+addr, len); else memset(ptr, 0xff, len); + mutex_unlock(&s->ops_mutex); return; } list_for_each_entry(cis, &s->cis_cache, node) { if (cis->addr == addr && cis->len == len && cis->attr == attr) { memcpy(ptr, cis->cache, len); + mutex_unlock(&s->ops_mutex); return; } } + mutex_unlock(&s->ops_mutex); ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr); @@ -302,7 +306,9 @@ static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, cis->len = len; cis->attr = attr; memcpy(cis->cache, ptr, len); + mutex_lock(&s->ops_mutex); list_add(&cis->node, &s->cis_cache); + mutex_unlock(&s->ops_mutex); } } } @@ -312,19 +318,22 @@ remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len) { struct cis_cache_entry *cis; + mutex_lock(&s->ops_mutex); list_for_each_entry(cis, &s->cis_cache, node) if (cis->addr == addr && cis->len == len && cis->attr == attr) { list_del(&cis->node); kfree(cis); break; } + mutex_unlock(&s->ops_mutex); } /** * destroy_cis_cache() - destroy the CIS cache * @s: pcmcia_socket for which CIS cache shall be destroyed * - * This destroys the CIS cache but keeps any fake CIS alive. + * This destroys the CIS cache but keeps any fake CIS alive. Must be + * called with ops_mutex held. */ void destroy_cis_cache(struct pcmcia_socket *s) @@ -391,14 +400,17 @@ int pcmcia_replace_cis(struct pcmcia_socket *s, dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n"); return -EINVAL; } + mutex_lock(&s->ops_mutex); kfree(s->fake_cis); s->fake_cis = kmalloc(len, GFP_KERNEL); if (s->fake_cis == NULL) { dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n"); + mutex_unlock(&s->ops_mutex); return -ENOMEM; } s->fake_cis_len = len; memcpy(s->fake_cis, data, len); + mutex_unlock(&s->ops_mutex); return 0; } @@ -1461,7 +1473,9 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info) return -EINVAL; /* We do not want to validate the CIS cache... */ + mutex_lock(&s->ops_mutex); destroy_cis_cache(s); + mutex_unlock(&s->ops_mutex); tuple = kmalloc(sizeof(*tuple), GFP_KERNEL); if (tuple == NULL) { @@ -1518,7 +1532,9 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info) done: /* invalidate CIS cache on failure */ if (!dev_ok || !ident_ok || !count) { + mutex_lock(&s->ops_mutex); destroy_cis_cache(s); + mutex_unlock(&s->ops_mutex); ret = -EIO; } diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 0ab4fe04592..4f7308df22c 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -1241,12 +1241,16 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) s->pcmcia_state.present = 0; pcmcia_card_remove(skt, NULL); handle_event(skt, event); + mutex_lock(&s->ops_mutex); destroy_cis_cache(s); + mutex_unlock(&s->ops_mutex); break; case CS_EVENT_CARD_INSERTION: s->pcmcia_state.present = 1; + mutex_lock(&s->ops_mutex); destroy_cis_cache(s); /* to be on the safe side... */ + mutex_unlock(&s->ops_mutex); pcmcia_card_add(skt); handle_event(skt, event); break; @@ -1259,9 +1263,11 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) dev_dbg(&skt->dev, "cis mismatch - different card\n"); /* first, remove the card */ ds_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); + mutex_lock(&s->ops_mutex); destroy_cis_cache(skt); kfree(skt->fake_cis); skt->fake_cis = NULL; + mutex_unlock(&s->ops_mutex); /* now, add the new card */ ds_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); -- cgit v1.2.3 From 8533ee31cdc08fc1f2533e5f21f8e4abf6a57dfc Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 12 Jan 2010 23:52:19 +0100 Subject: pcmcia: lock ops->set_io_map() As a side effect, io_window_t io[MAX_IO_WIN]; is explicitely protected now. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/pcmcia_resource.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index f31ba89e40d..4e0aaec5cf9 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -306,6 +306,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, int i; io_on.speed = io_speed; + mutex_lock(&s->ops_mutex); for (i = 0; i < MAX_IO_WIN; i++) { if (!s->io[i].res) continue; @@ -320,6 +321,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, mdelay(40); s->ops->set_io_map(s, &io_on); } + mutex_unlock(&s->ops_mutex); } return 0; @@ -345,6 +347,7 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) } if (c->state & CONFIG_LOCKED) { c->state &= ~CONFIG_LOCKED; + mutex_lock(&s->ops_mutex); if (c->state & CONFIG_IO_REQ) for (i = 0; i < MAX_IO_WIN; i++) { if (!s->io[i].res) @@ -355,6 +358,7 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) io.map = i; s->ops->set_io_map(s, &io); } + mutex_unlock(&s->ops_mutex); } return 0; @@ -562,6 +566,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, /* Configure I/O windows */ if (c->state & CONFIG_IO_REQ) { + mutex_lock(&s->ops_mutex); iomap.speed = io_speed; for (i = 0; i < MAX_IO_WIN; i++) if (s->io[i].res) { @@ -580,6 +585,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, s->ops->set_io_map(s, &iomap); s->io[i].Config++; } + mutex_unlock(&s->ops_mutex); } c->state |= CONFIG_LOCKED; @@ -625,10 +631,12 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req) return -EINVAL; } + mutex_lock(&s->ops_mutex); dev_dbg(&s->dev, "trying to allocate resource 1\n"); if (alloc_io_space(s, req->Attributes1, &req->BasePort1, req->NumPorts1, req->IOAddrLines)) { dev_dbg(&s->dev, "allocation of resource 1 failed\n"); + mutex_unlock(&s->ops_mutex); return -EBUSY; } @@ -638,9 +646,11 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req) req->NumPorts2, req->IOAddrLines)) { dev_dbg(&s->dev, "allocation of resource 2 failed\n"); release_io_space(s, req->BasePort1, req->NumPorts1); + mutex_unlock(&s->ops_mutex); return -EBUSY; } } + mutex_unlock(&s->ops_mutex); c->io = *req; c->state |= CONFIG_IO_REQ; -- cgit v1.2.3 From 9e86749cff70fca505c7c1a9dc760d193f27a059 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 16 Jan 2010 00:26:33 +0100 Subject: pcmcia: lock ops->set_socket As a side effect, socket_state_t socket; u_int state; u_int suspended_state; are properly protected now. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 18 ++++++++++++++++-- drivers/pcmcia/pcmcia_resource.c | 15 +++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 91aa1f28406..cc0ba8aef59 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -383,6 +383,8 @@ static void socket_shutdown(struct pcmcia_socket *s) dev_dbg(&s->dev, "shutdown\n"); send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); + + mutex_lock(&s->ops_mutex); s->state &= SOCKET_INUSE | SOCKET_PRESENT; msleep(shutdown_delay * 10); s->state &= SOCKET_INUSE; @@ -410,6 +412,7 @@ static void socket_shutdown(struct pcmcia_socket *s) } s->state &= ~SOCKET_INUSE; + mutex_unlock(&s->ops_mutex); } static int socket_setup(struct pcmcia_socket *skt, int initial_delay) @@ -498,6 +501,7 @@ static int socket_insert(struct pcmcia_socket *skt) dev_dbg(&skt->dev, "insert\n"); + mutex_lock(&skt->ops_mutex); WARN_ON(skt->state & SOCKET_INUSE); skt->state |= SOCKET_INUSE; @@ -517,9 +521,11 @@ static int socket_insert(struct pcmcia_socket *skt) } #endif dev_dbg(&skt->dev, "insert done\n"); + mutex_unlock(&skt->ops_mutex); send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); } else { + mutex_unlock(&skt->ops_mutex); socket_shutdown(skt); } @@ -531,6 +537,7 @@ static int socket_suspend(struct pcmcia_socket *skt) if (skt->state & SOCKET_SUSPEND) return -EBUSY; + mutex_lock(&skt->ops_mutex); skt->suspended_state = skt->state; send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); @@ -539,23 +546,27 @@ static int socket_suspend(struct pcmcia_socket *skt) if (skt->ops->suspend) skt->ops->suspend(skt); skt->state |= SOCKET_SUSPEND; - + mutex_unlock(&skt->ops_mutex); return 0; } static int socket_early_resume(struct pcmcia_socket *skt) { + mutex_lock(&skt->ops_mutex); skt->socket = dead_socket; skt->ops->init(skt); skt->ops->set_socket(skt, &skt->socket); if (skt->state & SOCKET_PRESENT) skt->resume_status = socket_setup(skt, resume_delay); + mutex_unlock(&skt->ops_mutex); return 0; } static int socket_late_resume(struct pcmcia_socket *skt) { + mutex_lock(&skt->ops_mutex); skt->state &= ~SOCKET_SUSPEND; + mutex_unlock(&skt->ops_mutex); if (!(skt->state & SOCKET_PRESENT)) return socket_insert(skt); @@ -795,7 +806,10 @@ int pcmcia_reset_card(struct pcmcia_socket *skt) send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW); if (skt->callback) skt->callback->suspend(skt); - if (socket_reset(skt) == 0) { + mutex_lock(&skt->ops_mutex); + ret = socket_reset(skt); + mutex_unlock(&skt->ops_mutex); + if (ret == 0) { send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW); if (skt->callback) skt->callback->resume(skt); diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 4e0aaec5cf9..f365ecb9c5c 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -266,6 +266,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, } if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { + mutex_lock(&s->ops_mutex); if (mod->Attributes & CONF_ENABLE_IRQ) { c->Attributes |= CONF_ENABLE_IRQ; s->socket.io_irq = s->irq.AssignedIRQ; @@ -274,6 +275,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, s->socket.io_irq = 0; } s->ops->set_socket(s, &s->socket); + mutex_unlock(&s->ops_mutex); } if (mod->Attributes & CONF_VCC_CHANGE_VALID) { @@ -288,12 +290,15 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, dev_dbg(&s->dev, "Vpp1 and Vpp2 must be the same\n"); return -EINVAL; } + mutex_lock(&s->ops_mutex); s->socket.Vpp = mod->Vpp1; if (s->ops->set_socket(s, &s->socket)) { + mutex_unlock(&s->ops_mutex); dev_printk(KERN_WARNING, &s->dev, "Unable to set VPP\n"); return -EIO; } + mutex_unlock(&s->ops_mutex); } else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) || (mod->Attributes & CONF_VPP2_CHANGE_VALID)) { dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n"); @@ -336,6 +341,7 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) config_t *c = p_dev->function_config; int i; + mutex_lock(&s->ops_mutex); if (p_dev->_locked) { p_dev->_locked = 0; if (--(s->lock_count) == 0) { @@ -347,7 +353,6 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) } if (c->state & CONFIG_LOCKED) { c->state &= ~CONFIG_LOCKED; - mutex_lock(&s->ops_mutex); if (c->state & CONFIG_IO_REQ) for (i = 0; i < MAX_IO_WIN; i++) { if (!s->io[i].res) @@ -358,8 +363,8 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) io.map = i; s->ops->set_io_map(s, &io); } - mutex_unlock(&s->ops_mutex); } + mutex_unlock(&s->ops_mutex); return 0; } /* pcmcia_release_configuration */ @@ -493,9 +498,11 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, return -EACCES; } + mutex_lock(&s->ops_mutex); /* Do power control. We don't allow changes in Vcc. */ s->socket.Vpp = req->Vpp; if (s->ops->set_socket(s, &s->socket)) { + mutex_unlock(&s->ops_mutex); dev_printk(KERN_WARNING, &s->dev, "Unable to set socket state\n"); return -EINVAL; @@ -518,6 +525,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, s->socket.io_irq = 0; s->ops->set_socket(s, &s->socket); s->lock_count++; + mutex_unlock(&s->ops_mutex); /* Set up CIS configuration registers */ base = c->ConfigBase = req->ConfigBase; @@ -698,6 +706,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) return -EBUSY; } + mutex_lock(&s->ops_mutex); /* Decide what type of interrupt we are registering */ type = 0; if (s->functions > 1) /* All of this ought to be handled higher up */ @@ -791,6 +800,8 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) pcmcia_used_irq[irq]++; #endif + mutex_unlock(&s->ops_mutex); + return 0; } /* pcmcia_request_irq */ EXPORT_SYMBOL(pcmcia_request_irq); -- cgit v1.2.3 From 64d8d46f5f501a19aec4db7ff93faf1b831d05ed Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 16 Jan 2010 01:14:38 +0100 Subject: pcmcia: properly lock skt->irq, skt->irq_mask Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/pcmcia_resource.c | 3 +++ drivers/pcmcia/socket_sysfs.c | 2 ++ 2 files changed, 5 insertions(+) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index f365ecb9c5c..f0de7b8b123 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -419,7 +419,9 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req) dev_dbg(&s->dev, "IRQ attributes must match assigned ones\n"); return -EINVAL; } + mutex_lock(&s->ops_mutex); if (s->irq.AssignedIRQ != req->AssignedIRQ) { + mutex_unlock(&s->ops_mutex); dev_dbg(&s->dev, "IRQ must match assigned one\n"); return -EINVAL; } @@ -434,6 +436,7 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req) #ifdef CONFIG_PCMCIA_PROBE pcmcia_used_irq[req->AssignedIRQ]--; #endif + mutex_unlock(&s->ops_mutex); return 0; } /* pcmcia_release_irq */ diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index 537d79305e7..1cba9d38d81 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -167,7 +167,9 @@ static ssize_t pccard_store_irq_mask(struct device *dev, ret = sscanf(buf, "0x%x\n", &mask); if (ret == 1) { + mutex_lock(&s->ops_mutex); s->irq_mask &= mask; + mutex_unlock(&s->ops_mutex); ret = 0; } -- cgit v1.2.3 From e6e4f397e5d0970ee1bb7a5591ac93b37cfa524a Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 16 Jan 2010 01:34:06 +0100 Subject: pcmcia: protect s->device_count Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 4f7308df22c..bcb9ef10342 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -265,6 +265,7 @@ static int pcmcia_device_probe(struct device *dev) struct pcmcia_device_id *did; struct pcmcia_socket *s; cistpl_config_t cis_config; + unsigned long flags; int ret = 0; dev = get_device(dev); @@ -315,9 +316,11 @@ static int pcmcia_device_probe(struct device *dev) goto put_module; } + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && (p_dev->socket->device_count == 1) && (p_dev->device_no == 0)) pcmcia_add_device_later(p_dev->socket, 0); + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); put_module: if (ret) @@ -342,10 +345,12 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le "pcmcia_card_remove(%d) %s\n", s->sock, leftover ? leftover->devname : ""); + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); if (!leftover) s->device_count = 0; else s->device_count = 1; + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); /* unregister all pcmcia_devices registered with this socket, except leftover */ list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) { @@ -382,7 +387,7 @@ static int pcmcia_device_remove(struct device *dev) */ did = dev_get_drvdata(&p_dev->dev); if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && - (p_dev->socket->device_count != 0) && + (p_dev->socket->device_count > 0) && (p_dev->device_no == 0)) pcmcia_card_remove(p_dev->socket, p_dev); @@ -512,16 +517,19 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu pr_debug("adding device to %d, function %d\n", s->sock, function); - /* max of 4 devices per card */ - if (s->device_count == 4) - goto err_put; - p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL); if (!p_dev) goto err_put; - p_dev->socket = s; + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); p_dev->device_no = (s->device_count++); + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + /* max of 4 devices per card */ + if (p_dev->device_no >= 4) + goto err_free; + + p_dev->socket = s; p_dev->func = function; p_dev->dev.bus = &pcmcia_bus_type; @@ -586,9 +594,12 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); err_free: + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + s->device_count--; + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + kfree(p_dev->devname); kfree(p_dev); - s->device_count--; err_put: mutex_unlock(&device_add_lock); pcmcia_put_socket(s); -- cgit v1.2.3 From 3d3de32fad19e37695e6649136e4cb17f9d46329 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 16 Jan 2010 08:32:56 +0100 Subject: pcmcia: add locking to struct pcmcia_socket->pcmcia_state() Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index bcb9ef10342..0eb242cbed1 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -649,10 +649,17 @@ static void pcmcia_delayed_add_device(struct work_struct *work) { struct pcmcia_socket *s = container_of(work, struct pcmcia_socket, device_add); - dev_dbg(&s->dev, "adding additional device to %d\n", s->sock); - pcmcia_device_add(s, s->pcmcia_state.mfc_pfc); + u8 mfc_pfc; + unsigned long flags; + + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mfc_pfc = s->pcmcia_state.mfc_pfc; s->pcmcia_state.device_add_pending = 0; s->pcmcia_state.mfc_pfc = 0; + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + dev_dbg(&s->dev, "adding additional device to %d\n", s->sock); + pcmcia_device_add(s, mfc_pfc); } static int pcmcia_requery(struct device *dev, void * _data) @@ -761,8 +768,13 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) if (old_funcs > no_funcs) pcmcia_card_remove(s, dev); - else if (no_funcs > old_funcs) + else if (no_funcs > old_funcs) { + unsigned long flags; + + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); pcmcia_add_device_later(s, 1); + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + } } release: release_firmware(fw); @@ -1236,6 +1248,7 @@ static int pcmcia_bus_suspend(struct pcmcia_socket *skt) static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) { struct pcmcia_socket *s = pcmcia_get_socket(skt); + unsigned long flags; if (!s) { dev_printk(KERN_ERR, &skt->dev, @@ -1249,7 +1262,9 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) switch (event) { case CS_EVENT_CARD_REMOVAL: + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); s->pcmcia_state.present = 0; + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); pcmcia_card_remove(skt, NULL); handle_event(skt, event); mutex_lock(&s->ops_mutex); @@ -1258,7 +1273,9 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) break; case CS_EVENT_CARD_INSERTION: + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); s->pcmcia_state.present = 1; + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); mutex_lock(&s->ops_mutex); destroy_cis_cache(s); /* to be on the safe side... */ mutex_unlock(&s->ops_mutex); @@ -1304,14 +1321,19 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev) { struct pcmcia_device *p_dev; struct pcmcia_device *ret = NULL; + unsigned long flags; p_dev = pcmcia_get_dev(_p_dev); if (!p_dev) return NULL; + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); if (!p_dev->socket->pcmcia_state.present) goto out; + if (p_dev->socket->pcmcia_state.dead) + goto out; + if (p_dev->_removed) goto out; @@ -1320,6 +1342,7 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev) ret = p_dev; out: + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); pcmcia_put_dev(p_dev); return ret; } @@ -1383,11 +1406,15 @@ static void pcmcia_bus_remove_socket(struct device *dev, struct class_interface *class_intf) { struct pcmcia_socket *socket = dev_get_drvdata(dev); + unsigned long flags; if (!socket) return; + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); socket->pcmcia_state.dead = 1; + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + pccard_register_pcmcia(socket, NULL); /* unregister any unbound devices */ -- cgit v1.2.3 From 00ce99ff506a17882747a7d6874e3f5206a99043 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 16 Jan 2010 09:14:11 +0100 Subject: pcmcia: simplify locking replace pcmcia_socket->lock and pcmcia_dev_list_lock by using the per-socket "ops_mutex", as we do neither need different locks nor a spinlock here. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 2 -- drivers/pcmcia/cs_internal.h | 2 -- drivers/pcmcia/ds.c | 70 +++++++++++++++++-------------------------- drivers/pcmcia/pcmcia_ioctl.c | 36 ++++++++++------------ drivers/pcmcia/rsrc_mgr.c | 6 ++-- drivers/pcmcia/socket_sysfs.c | 5 ++-- 6 files changed, 47 insertions(+), 74 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index cc0ba8aef59..13277eebe46 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -175,8 +175,6 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops); - spin_lock_init(&socket->lock); - /* try to obtain a socket number [yes, it gets ugly if we * register more than 2^sizeof(unsigned int) pcmcia * sockets... but the socket number is deprecated diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 76ac4444f0e..bd386d77845 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -182,8 +182,6 @@ int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple); #ifdef CONFIG_PCMCIA_IOCTL /* ds.c */ -extern spinlock_t pcmcia_dev_list_lock; - extern struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev); extern void pcmcia_put_dev(struct pcmcia_device *p_dev); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 0eb242cbed1..4c40db8889d 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -42,8 +42,6 @@ MODULE_DESCRIPTION("PCMCIA Driver Services"); MODULE_LICENSE("GPL"); -spinlock_t pcmcia_dev_list_lock; - /*====================================================================*/ static void pcmcia_check_driver(struct pcmcia_driver *p_drv) @@ -265,7 +263,6 @@ static int pcmcia_device_probe(struct device *dev) struct pcmcia_device_id *did; struct pcmcia_socket *s; cistpl_config_t cis_config; - unsigned long flags; int ret = 0; dev = get_device(dev); @@ -316,11 +313,11 @@ static int pcmcia_device_probe(struct device *dev) goto put_module; } - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && (p_dev->socket->device_count == 1) && (p_dev->device_no == 0)) pcmcia_add_device_later(p_dev->socket, 0); - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); put_module: if (ret) @@ -339,28 +336,27 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le { struct pcmcia_device *p_dev; struct pcmcia_device *tmp; - unsigned long flags; dev_dbg(leftover ? &leftover->dev : &s->dev, "pcmcia_card_remove(%d) %s\n", s->sock, leftover ? leftover->devname : ""); - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); if (!leftover) s->device_count = 0; else s->device_count = 1; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); /* unregister all pcmcia_devices registered with this socket, except leftover */ list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) { if (p_dev == leftover) continue; - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); list_del(&p_dev->socket_device_list); p_dev->_removed = 1; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); dev_dbg(&p_dev->dev, "unregistering device\n"); device_unregister(&p_dev->dev); @@ -507,7 +503,6 @@ static DEFINE_MUTEX(device_add_lock); struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) { struct pcmcia_device *p_dev, *tmp_dev; - unsigned long flags; s = pcmcia_get_socket(s); if (!s) @@ -521,9 +516,9 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu if (!p_dev) goto err_put; - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); p_dev->device_no = (s->device_count++); - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); /* max of 4 devices per card */ if (p_dev->device_no >= 4) @@ -546,7 +541,7 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu goto err_free; dev_dbg(&p_dev->dev, "devname is %s\n", p_dev->devname); - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); /* * p_dev->function_config must be the same for all card functions. @@ -564,7 +559,7 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu /* Add to the list in pcmcia_bus_socket */ list_add(&p_dev->socket_device_list, &s->devices_list); - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); if (!p_dev->function_config) { dev_dbg(&p_dev->dev, "creating config_t\n"); @@ -589,14 +584,14 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu return p_dev; err_unreg: - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); list_del(&p_dev->socket_device_list); - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); err_free: - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); s->device_count--; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); kfree(p_dev->devname); kfree(p_dev); @@ -650,13 +645,12 @@ static void pcmcia_delayed_add_device(struct work_struct *work) struct pcmcia_socket *s = container_of(work, struct pcmcia_socket, device_add); u8 mfc_pfc; - unsigned long flags; - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); mfc_pfc = s->pcmcia_state.mfc_pfc; s->pcmcia_state.device_add_pending = 0; s->pcmcia_state.mfc_pfc = 0; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); dev_dbg(&s->dev, "adding additional device to %d\n", s->sock); pcmcia_device_add(s, mfc_pfc); @@ -677,15 +671,14 @@ static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis) { int no_devices = 0; int ret = 0; - unsigned long flags; /* must be called with skt_mutex held */ dev_dbg(&skt->dev, "re-scanning socket %d\n", skt->sock); - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&skt->ops_mutex); if (list_empty(&skt->devices_list)) no_devices = 1; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&skt->ops_mutex); /* If this is because of a CIS override, start over */ if (new_cis && !no_devices) @@ -769,11 +762,9 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) if (old_funcs > no_funcs) pcmcia_card_remove(s, dev); else if (no_funcs > old_funcs) { - unsigned long flags; - - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); pcmcia_add_device_later(s, 1); - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); } } release: @@ -1248,7 +1239,6 @@ static int pcmcia_bus_suspend(struct pcmcia_socket *skt) static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) { struct pcmcia_socket *s = pcmcia_get_socket(skt); - unsigned long flags; if (!s) { dev_printk(KERN_ERR, &skt->dev, @@ -1262,9 +1252,9 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) switch (event) { case CS_EVENT_CARD_REMOVAL: - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); s->pcmcia_state.present = 0; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); pcmcia_card_remove(skt, NULL); handle_event(skt, event); mutex_lock(&s->ops_mutex); @@ -1273,10 +1263,8 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) break; case CS_EVENT_CARD_INSERTION: - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - s->pcmcia_state.present = 1; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); mutex_lock(&s->ops_mutex); + s->pcmcia_state.present = 1; destroy_cis_cache(s); /* to be on the safe side... */ mutex_unlock(&s->ops_mutex); pcmcia_card_add(skt); @@ -1321,13 +1309,12 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev) { struct pcmcia_device *p_dev; struct pcmcia_device *ret = NULL; - unsigned long flags; p_dev = pcmcia_get_dev(_p_dev); if (!p_dev) return NULL; - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&p_dev->socket->ops_mutex); if (!p_dev->socket->pcmcia_state.present) goto out; @@ -1342,7 +1329,7 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev) ret = p_dev; out: - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&p_dev->socket->ops_mutex); pcmcia_put_dev(p_dev); return ret; } @@ -1406,14 +1393,13 @@ static void pcmcia_bus_remove_socket(struct device *dev, struct class_interface *class_intf) { struct pcmcia_socket *socket = dev_get_drvdata(dev); - unsigned long flags; if (!socket) return; - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&socket->ops_mutex); socket->pcmcia_state.dead = 1; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&socket->ops_mutex); pccard_register_pcmcia(socket, NULL); @@ -1455,8 +1441,6 @@ static int __init init_pcmcia_bus(void) { int ret; - spin_lock_init(&pcmcia_dev_list_lock); - ret = bus_register(&pcmcia_bus_type); if (ret < 0) { printk(KERN_WARNING "pcmcia: bus_register error: %d\n", ret); diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index f73fd5beaa3..db2e3db8008 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -62,16 +62,15 @@ static struct pcmcia_device *get_pcmcia_device(struct pcmcia_socket *s, unsigned int function) { struct pcmcia_device *p_dev = NULL; - unsigned long flags; - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { if (p_dev->func == function) { - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); return pcmcia_get_dev(p_dev); } } - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); return NULL; } @@ -169,7 +168,6 @@ static int pcmcia_adjust_resource_info(adjust_t *adj) { struct pcmcia_socket *s; int ret = -ENOSYS; - unsigned long flags; down_read(&pcmcia_socket_list_rwsem); list_for_each_entry(s, &pcmcia_socket_list, socket_list) { @@ -182,14 +180,14 @@ static int pcmcia_adjust_resource_info(adjust_t *adj) /* you can't use the old interface if the new * one was used before */ - spin_lock_irqsave(&s->lock, flags); + mutex_lock(&s->ops_mutex); if ((s->resource_setup_new) && !(s->resource_setup_old)) { - spin_unlock_irqrestore(&s->lock, flags); + mutex_unlock(&s->ops_mutex); continue; } else if (!(s->resource_setup_old)) s->resource_setup_old = 1; - spin_unlock_irqrestore(&s->lock, flags); + mutex_unlock(&s->ops_mutex); switch (adj->Resource) { case RES_MEMORY_RANGE: @@ -208,9 +206,9 @@ static int pcmcia_adjust_resource_info(adjust_t *adj) * last call to adjust_resource_info, we * always need to assume this is the latest * one... */ - spin_lock_irqsave(&s->lock, flags); + mutex_lock(&s->ops_mutex); s->resource_setup_done = 1; - spin_unlock_irqrestore(&s->lock, flags); + mutex_unlock(&s->ops_mutex); } } } @@ -470,7 +468,6 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) struct pcmcia_driver *p_drv; struct pcmcia_device *p_dev; int ret = 0; - unsigned long flags; s = pcmcia_get_socket(s); if (!s) @@ -490,7 +487,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) goto err_put_driver; } - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { if (p_dev->func == bind_info->function) { if ((p_dev->dev.driver == &p_drv->drv)) { @@ -499,7 +496,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) * registered, and it was registered * by userspace before, we need to * return the "instance". */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); bind_info->instance = p_dev; ret = -EBUSY; goto err_put_module; @@ -507,7 +504,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) /* the correct driver managed to bind * itself magically to the correct * device. */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); p_dev->cardmgr = p_drv; ret = 0; goto err_put_module; @@ -516,12 +513,12 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) /* there's already a device available where * no device has been bound to yet. So we don't * need to register a device! */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); goto rescan; } } } - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); p_dev = pcmcia_device_add(s, bind_info->function); if (!p_dev) { @@ -578,7 +575,6 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int dev_node_t *node; struct pcmcia_device *p_dev; struct pcmcia_driver *p_drv; - unsigned long flags; int ret = 0; #ifdef CONFIG_CARDBUS @@ -617,7 +613,7 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int } #endif - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { if (p_dev->func == bind_info->function) { p_dev = pcmcia_get_dev(p_dev); @@ -626,11 +622,11 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int goto found; } } - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); return -ENODEV; found: - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); p_drv = to_pcmcia_drv(p_dev->dev.driver); if (p_drv && !p_dev->_locked) { diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index cdd30c18006..b81586622d0 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -23,14 +23,12 @@ static int static_init(struct pcmcia_socket *s) { - unsigned long flags; - /* the good thing about SS_CAP_STATIC_MAP sockets is * that they don't need a resource database */ - spin_lock_irqsave(&s->lock, flags); + mutex_lock(&s->ops_mutex); s->resource_setup_done = 1; - spin_unlock_irqrestore(&s->lock, flags); + mutex_unlock(&s->ops_mutex); return 0; } diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index 1cba9d38d81..e8826df00a3 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -189,16 +189,15 @@ static ssize_t pccard_store_resource(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long flags; struct pcmcia_socket *s = to_socket(dev); if (!count) return -EINVAL; - spin_lock_irqsave(&s->lock, flags); + mutex_lock(&s->ops_mutex); if (!s->resource_setup_done) s->resource_setup_done = 1; - spin_unlock_irqrestore(&s->lock, flags); + mutex_unlock(&s->ops_mutex); mutex_lock(&s->skt_mutex); if ((s->callback) && -- cgit v1.2.3 From 94a819f80297e1f635a7cde4ed5317612e512ba7 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 17 Jan 2010 18:31:34 +0100 Subject: pcmcia: assert locking to struct pcmcia_device Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 38 ++++++++---- drivers/pcmcia/pcmcia_resource.c | 122 ++++++++++++++++++++++++--------------- 2 files changed, 104 insertions(+), 56 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 4c40db8889d..83b51ddd3da 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -835,6 +835,8 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, } if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) { + int ret; + if ((!dev->has_func_id) || (dev->func_id != did->func_id)) return 0; @@ -849,10 +851,15 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, * after it has re-checked that there is no possible module * with a prod_id/manf_id/card_id match. */ - dev_dbg(&dev->dev, - "skipping FUNC_ID match until userspace interaction\n"); - if (!dev->allow_func_id_match) + mutex_lock(&dev->socket->ops_mutex); + ret = dev->allow_func_id_match; + mutex_unlock(&dev->socket->ops_mutex); + + if (!ret) { + dev_dbg(&dev->dev, + "skipping FUNC_ID match until userspace ACK\n"); return 0; + } } if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) { @@ -1079,9 +1086,9 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev, if (!count) return -EINVAL; - mutex_lock(&p_dev->socket->skt_mutex); + mutex_lock(&p_dev->socket->ops_mutex); p_dev->allow_func_id_match = 1; - mutex_unlock(&p_dev->socket->skt_mutex); + mutex_unlock(&p_dev->socket->ops_mutex); ret = bus_rescan_devices(&pcmcia_bus_type); if (ret) @@ -1114,8 +1121,13 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state) struct pcmcia_driver *p_drv = NULL; int ret = 0; - if (p_dev->suspended) + mutex_lock(&p_dev->socket->ops_mutex); + if (p_dev->suspended) { + mutex_unlock(&p_dev->socket->ops_mutex); return 0; + } + p_dev->suspended = 1; + mutex_unlock(&p_dev->socket->ops_mutex); dev_dbg(dev, "suspending\n"); @@ -1132,6 +1144,9 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state) "pcmcia: device %s (driver %s) did " "not want to go to sleep (%d)\n", p_dev->devname, p_drv->drv.name, ret); + mutex_lock(&p_dev->socket->ops_mutex); + p_dev->suspended = 0; + mutex_unlock(&p_dev->socket->ops_mutex); goto out; } } @@ -1142,8 +1157,6 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state) } out: - if (!ret) - p_dev->suspended = 1; return ret; } @@ -1154,8 +1167,13 @@ static int pcmcia_dev_resume(struct device *dev) struct pcmcia_driver *p_drv = NULL; int ret = 0; - if (!p_dev->suspended) + mutex_lock(&p_dev->socket->ops_mutex); + if (!p_dev->suspended) { + mutex_unlock(&p_dev->socket->ops_mutex); return 0; + } + p_dev->suspended = 0; + mutex_unlock(&p_dev->socket->ops_mutex); dev_dbg(dev, "resuming\n"); @@ -1176,8 +1194,6 @@ static int pcmcia_dev_resume(struct device *dev) ret = p_drv->resume(p_dev); out: - if (!ret) - p_dev->suspended = 0; return ret; } diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index f0de7b8b123..b2df04199a2 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -191,14 +191,18 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, return -EINVAL; s = p_dev->socket; + + mutex_lock(&s->ops_mutex); c = p_dev->function_config; if (!(c->state & CONFIG_LOCKED)) { dev_dbg(&s->dev, "Configuration isnt't locked\n"); + mutex_unlock(&s->ops_mutex); return -EACCES; } addr = (c->ConfigBase + reg->Offset) >> 1; + mutex_unlock(&s->ops_mutex); switch (reg->Action) { case CS_READ: @@ -254,19 +258,22 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, config_t *c; s = p_dev->socket; + + mutex_lock(&s->ops_mutex); c = p_dev->function_config; if (!(s->state & SOCKET_PRESENT)) { dev_dbg(&s->dev, "No card present\n"); + mutex_unlock(&s->ops_mutex); return -ENODEV; } if (!(c->state & CONFIG_LOCKED)) { dev_dbg(&s->dev, "Configuration isnt't locked\n"); + mutex_unlock(&s->ops_mutex); return -EACCES; } if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { - mutex_lock(&s->ops_mutex); if (mod->Attributes & CONF_ENABLE_IRQ) { c->Attributes |= CONF_ENABLE_IRQ; s->socket.io_irq = s->irq.AssignedIRQ; @@ -275,7 +282,6 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, s->socket.io_irq = 0; } s->ops->set_socket(s, &s->socket); - mutex_unlock(&s->ops_mutex); } if (mod->Attributes & CONF_VCC_CHANGE_VALID) { @@ -288,9 +294,9 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, (mod->Attributes & CONF_VPP2_CHANGE_VALID)) { if (mod->Vpp1 != mod->Vpp2) { dev_dbg(&s->dev, "Vpp1 and Vpp2 must be the same\n"); + mutex_unlock(&s->ops_mutex); return -EINVAL; } - mutex_lock(&s->ops_mutex); s->socket.Vpp = mod->Vpp1; if (s->ops->set_socket(s, &s->socket)) { mutex_unlock(&s->ops_mutex); @@ -298,10 +304,10 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, "Unable to set VPP\n"); return -EIO; } - mutex_unlock(&s->ops_mutex); } else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) || (mod->Attributes & CONF_VPP2_CHANGE_VALID)) { dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n"); + mutex_unlock(&s->ops_mutex); return -EINVAL; } @@ -311,7 +317,6 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, int i; io_on.speed = io_speed; - mutex_lock(&s->ops_mutex); for (i = 0; i < MAX_IO_WIN; i++) { if (!s->io[i].res) continue; @@ -326,8 +331,8 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, mdelay(40); s->ops->set_io_map(s, &io_on); } - mutex_unlock(&s->ops_mutex); } + mutex_unlock(&s->ops_mutex); return 0; } /* modify_configuration */ @@ -338,10 +343,11 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) { pccard_io_map io = { 0, 0, 0, 0, 1 }; struct pcmcia_socket *s = p_dev->socket; - config_t *c = p_dev->function_config; + config_t *c; int i; mutex_lock(&s->ops_mutex); + c = p_dev->function_config; if (p_dev->_locked) { p_dev->_locked = 0; if (--(s->lock_count) == 0) { @@ -381,10 +387,14 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req) { struct pcmcia_socket *s = p_dev->socket; - config_t *c = p_dev->function_config; + int ret = -EINVAL; + config_t *c; + + mutex_lock(&s->ops_mutex); + c = p_dev->function_config; if (!p_dev->_io) - return -EINVAL; + goto out; p_dev->_io = 0; @@ -392,7 +402,7 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req) (c->io.NumPorts1 != req->NumPorts1) || (c->io.BasePort2 != req->BasePort2) || (c->io.NumPorts2 != req->NumPorts2)) - return -EINVAL; + goto out; c->state &= ~CONFIG_IO_REQ; @@ -400,30 +410,38 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req) if (req->NumPorts2) release_io_space(s, req->BasePort2, req->NumPorts2); - return 0; +out: + mutex_unlock(&s->ops_mutex); + + return ret; } /* pcmcia_release_io */ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req) { struct pcmcia_socket *s = p_dev->socket; - config_t *c = p_dev->function_config; + config_t *c; + int ret = -EINVAL; + + mutex_lock(&s->ops_mutex); + + c = p_dev->function_config; if (!p_dev->_irq) - return -EINVAL; + goto out; + p_dev->_irq = 0; if (c->state & CONFIG_LOCKED) - return -EACCES; + goto out; + if (c->irq.Attributes != req->Attributes) { dev_dbg(&s->dev, "IRQ attributes must match assigned ones\n"); - return -EINVAL; + goto out; } - mutex_lock(&s->ops_mutex); if (s->irq.AssignedIRQ != req->AssignedIRQ) { - mutex_unlock(&s->ops_mutex); dev_dbg(&s->dev, "IRQ must match assigned one\n"); - return -EINVAL; + goto out; } if (--s->irq.Config == 0) { c->state &= ~CONFIG_IRQ_REQ; @@ -436,9 +454,12 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req) #ifdef CONFIG_PCMCIA_PROBE pcmcia_used_irq[req->AssignedIRQ]--; #endif + ret = 0; + +out: mutex_unlock(&s->ops_mutex); - return 0; + return ret; } /* pcmcia_release_irq */ @@ -495,13 +516,15 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, dev_dbg(&s->dev, "IntType may not be INT_CARDBUS\n"); return -EINVAL; } + + mutex_lock(&s->ops_mutex); c = p_dev->function_config; if (c->state & CONFIG_LOCKED) { + mutex_unlock(&s->ops_mutex); dev_dbg(&s->dev, "Configuration is locked\n"); return -EACCES; } - mutex_lock(&s->ops_mutex); /* Do power control. We don't allow changes in Vcc. */ s->socket.Vpp = req->Vpp; if (s->ops->set_socket(s, &s->socket)) { @@ -615,58 +638,65 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req) { struct pcmcia_socket *s = p_dev->socket; config_t *c; + int ret = -EINVAL; + + mutex_lock(&s->ops_mutex); if (!(s->state & SOCKET_PRESENT)) { dev_dbg(&s->dev, "No card present\n"); - return -ENODEV; + goto out; } if (!req) - return -EINVAL; + goto out; + c = p_dev->function_config; if (c->state & CONFIG_LOCKED) { dev_dbg(&s->dev, "Configuration is locked\n"); - return -EACCES; + goto out; } if (c->state & CONFIG_IO_REQ) { dev_dbg(&s->dev, "IO already configured\n"); - return -EBUSY; + goto out; } if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) { dev_dbg(&s->dev, "bad attribute setting for IO region 1\n"); - return -EINVAL; + goto out; } if ((req->NumPorts2 > 0) && (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) { dev_dbg(&s->dev, "bad attribute setting for IO region 2\n"); - return -EINVAL; + goto out; } - mutex_lock(&s->ops_mutex); dev_dbg(&s->dev, "trying to allocate resource 1\n"); - if (alloc_io_space(s, req->Attributes1, &req->BasePort1, - req->NumPorts1, req->IOAddrLines)) { + ret = alloc_io_space(s, req->Attributes1, &req->BasePort1, + req->NumPorts1, req->IOAddrLines); + if (ret) { dev_dbg(&s->dev, "allocation of resource 1 failed\n"); - mutex_unlock(&s->ops_mutex); - return -EBUSY; + goto out; } if (req->NumPorts2) { dev_dbg(&s->dev, "trying to allocate resource 2\n"); - if (alloc_io_space(s, req->Attributes2, &req->BasePort2, - req->NumPorts2, req->IOAddrLines)) { + ret = alloc_io_space(s, req->Attributes2, &req->BasePort2, + req->NumPorts2, req->IOAddrLines); + if (ret) { dev_dbg(&s->dev, "allocation of resource 2 failed\n"); release_io_space(s, req->BasePort1, req->NumPorts1); - mutex_unlock(&s->ops_mutex); - return -EBUSY; + goto out; } } - mutex_unlock(&s->ops_mutex); c->io = *req; c->state |= CONFIG_IO_REQ; p_dev->_io = 1; - return 0; + dev_dbg(&s->dev, "allocating resources succeeded: %d\n", ret); + +out: + mutex_unlock(&s->ops_mutex); + + return ret; } /* pcmcia_request_io */ EXPORT_SYMBOL(pcmcia_request_io); @@ -695,21 +725,22 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) int ret = -EINVAL, irq = 0; int type; + mutex_lock(&s->ops_mutex); + if (!(s->state & SOCKET_PRESENT)) { dev_dbg(&s->dev, "No card present\n"); - return -ENODEV; + goto out; } c = p_dev->function_config; if (c->state & CONFIG_LOCKED) { dev_dbg(&s->dev, "Configuration is locked\n"); - return -EACCES; + goto out; } if (c->state & CONFIG_IRQ_REQ) { dev_dbg(&s->dev, "IRQ already configured\n"); - return -EBUSY; + goto out; } - mutex_lock(&s->ops_mutex); /* Decide what type of interrupt we are registering */ type = 0; if (s->functions > 1) /* All of this ought to be handled higher up */ @@ -768,7 +799,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) if (ret && !s->irq.AssignedIRQ) { if (!s->pci_irq) { dev_printk(KERN_INFO, &s->dev, "no IRQ found\n"); - return ret; + goto out; } type = IRQF_SHARED; irq = s->pci_irq; @@ -780,7 +811,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) if (ret) { dev_printk(KERN_INFO, &s->dev, "request_irq() failed\n"); - return ret; + goto out; } } @@ -803,9 +834,10 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) pcmcia_used_irq[irq]++; #endif + ret = 0; +out: mutex_unlock(&s->ops_mutex); - - return 0; + return ret; } /* pcmcia_request_irq */ EXPORT_SYMBOL(pcmcia_request_irq); -- cgit v1.2.3 From 3f565232c561fbd9d5e03354aac29b90cb2bc78a Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 16 Jan 2010 13:06:40 +0100 Subject: pcmcia: use mutex for dynid lock Even though we weren't calling a blocking function within the dynid spinlock, we do not need a spinlock here but can and should be using a mutex. Reported-by: Jiri Slaby Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 83b51ddd3da..253d9aca5f7 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -124,9 +124,9 @@ pcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count) dynid->id.device_no = device_no; memcpy(dynid->id.prod_id_hash, prod_id_hash, sizeof(__u32) * 4); - spin_lock(&pdrv->dynids.lock); + mutex_lock(&pdrv->dynids.lock); list_add_tail(&dynid->node, &pdrv->dynids.list); - spin_unlock(&pdrv->dynids.lock); + mutex_unlock(&pdrv->dynids.lock); if (get_driver(&pdrv->drv)) { retval = driver_attach(&pdrv->drv); @@ -144,12 +144,12 @@ pcmcia_free_dynids(struct pcmcia_driver *drv) { struct pcmcia_dynid *dynid, *n; - spin_lock(&drv->dynids.lock); + mutex_lock(&drv->dynids.lock); list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { list_del(&dynid->node); kfree(dynid); } - spin_unlock(&drv->dynids.lock); + mutex_unlock(&drv->dynids.lock); } static int @@ -180,7 +180,7 @@ int pcmcia_register_driver(struct pcmcia_driver *driver) /* initialize common fields */ driver->drv.bus = &pcmcia_bus_type; driver->drv.owner = driver->owner; - spin_lock_init(&driver->dynids.lock); + mutex_init(&driver->dynids.lock); INIT_LIST_HEAD(&driver->dynids.list); pr_debug("registering driver %s\n", driver->drv.name); @@ -894,16 +894,16 @@ static int pcmcia_bus_match(struct device *dev, struct device_driver *drv) struct pcmcia_dynid *dynid; /* match dynamic devices first */ - spin_lock(&p_drv->dynids.lock); + mutex_lock(&p_drv->dynids.lock); list_for_each_entry(dynid, &p_drv->dynids.list, node) { dev_dbg(dev, "trying to match to %s\n", drv->name); if (pcmcia_devmatch(p_dev, &dynid->id)) { dev_dbg(dev, "matched to %s\n", drv->name); - spin_unlock(&p_drv->dynids.lock); + mutex_unlock(&p_drv->dynids.lock); return 1; } } - spin_unlock(&p_drv->dynids.lock); + mutex_unlock(&p_drv->dynids.lock); #ifdef CONFIG_PCMCIA_IOCTL /* matching by cardmgr */ -- cgit v1.2.3 From cfe5d809518eda3d5e2da87c5ccbe8647143573a Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 17 Jan 2010 19:31:45 +0100 Subject: pcmcia: use ops_mutex for rsrc_{mgr,nonstatic} locking Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 7 ++++++- drivers/pcmcia/ds.c | 6 +++++- drivers/pcmcia/pcmcia_ioctl.c | 4 +--- drivers/pcmcia/rsrc_mgr.c | 2 -- drivers/pcmcia/rsrc_nonstatic.c | 35 ++++++++--------------------------- 5 files changed, 20 insertions(+), 34 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 13277eebe46..7ba45b0cca6 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -224,7 +224,9 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) spin_lock_init(&socket->thread_lock); if (socket->resource_ops->init) { + mutex_lock(&socket->ops_mutex); ret = socket->resource_ops->init(socket); + mutex_unlock(&socket->ops_mutex); if (ret) goto err; } @@ -282,8 +284,11 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket) up_write(&pcmcia_socket_list_rwsem); /* wait for sysfs to drop all references */ - if (socket->resource_ops->exit) + if (socket->resource_ops->exit) { + mutex_lock(&socket->ops_mutex); socket->resource_ops->exit(socket); + mutex_unlock(&socket->ops_mutex); + } wait_for_completion(&socket->socket_released); } /* pcmcia_unregister_socket */ EXPORT_SYMBOL(pcmcia_unregister_socket); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 253d9aca5f7..76a21638291 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -607,19 +607,23 @@ static int pcmcia_card_add(struct pcmcia_socket *s) { cistpl_longlink_mfc_t mfc; unsigned int no_funcs, i, no_chains; - int ret = 0; + int ret = -EAGAIN; + mutex_lock(&s->ops_mutex); if (!(s->resource_setup_done)) { dev_dbg(&s->dev, "no resources available, delaying card_add\n"); + mutex_unlock(&s->ops_mutex); return -EAGAIN; /* try again, but later... */ } if (pcmcia_validate_mem(s)) { dev_dbg(&s->dev, "validating mem resources failed, " "delaying card_add\n"); + mutex_unlock(&s->ops_mutex); return -EAGAIN; /* try again, but later... */ } + mutex_unlock(&s->ops_mutex); ret = pccard_validate_cis(s, &no_chains); if (ret || !no_chains) { diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index db2e3db8008..96fd236f52a 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -187,7 +187,6 @@ static int pcmcia_adjust_resource_info(adjust_t *adj) continue; } else if (!(s->resource_setup_old)) s->resource_setup_old = 1; - mutex_unlock(&s->ops_mutex); switch (adj->Resource) { case RES_MEMORY_RANGE: @@ -206,10 +205,9 @@ static int pcmcia_adjust_resource_info(adjust_t *adj) * last call to adjust_resource_info, we * always need to assume this is the latest * one... */ - mutex_lock(&s->ops_mutex); s->resource_setup_done = 1; - mutex_unlock(&s->ops_mutex); } + mutex_unlock(&s->ops_mutex); } } up_read(&pcmcia_socket_list_rwsem); diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index b81586622d0..aca2cfd02ca 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -26,9 +26,7 @@ static int static_init(struct pcmcia_socket *s) /* the good thing about SS_CAP_STATIC_MAP sockets is * that they don't need a resource database */ - mutex_lock(&s->ops_mutex); s->resource_setup_done = 1; - mutex_unlock(&s->ops_mutex); return 0; } diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 1de46cf2772..c13424f7b47 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -59,7 +59,6 @@ struct socket_data { unsigned int rsrc_mem_probe; }; -static DEFINE_MUTEX(rsrc_mutex); #define MEM_PROBE_LOW (1 << 0) #define MEM_PROBE_HIGH (1 << 1) @@ -274,7 +273,6 @@ static int readable(struct pcmcia_socket *s, struct resource *res, { int ret = -EINVAL; - mutex_lock(&s->ops_mutex); s->cis_mem.res = res; s->cis_virt = ioremap(res->start, s->map_size); if (s->cis_virt) { @@ -288,7 +286,6 @@ static int readable(struct pcmcia_socket *s, struct resource *res, s->cis_virt = NULL; } s->cis_mem.res = NULL; - mutex_unlock(&s->ops_mutex); if ((ret) || (*count == 0)) return -EINVAL; return 0; @@ -304,8 +301,6 @@ static int checksum(struct pcmcia_socket *s, struct resource *res, int i, a = 0, b = -1, d; void __iomem *virt; - mutex_lock(&s->ops_mutex); - virt = ioremap(res->start, s->map_size); if (virt) { map.map = 0; @@ -328,8 +323,6 @@ static int checksum(struct pcmcia_socket *s, struct resource *res, iounmap(virt); } - mutex_unlock(&s->ops_mutex); - if (b == -1) return -EINVAL; @@ -570,8 +563,6 @@ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) if (!probe_mem) return 0; - mutex_lock(&rsrc_mutex); - if (s->features & SS_CAP_PAGE_REGS) probe_mask = MEM_PROBE_HIGH; @@ -583,8 +574,6 @@ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) } } - mutex_unlock(&rsrc_mutex); - return ret; } @@ -661,7 +650,6 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star struct socket_data *s_data = s->resource_data; int ret = -ENOMEM; - mutex_lock(&rsrc_mutex); for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) { unsigned long start = m->base; unsigned long end = m->base + m->num - 1; @@ -672,7 +660,6 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star ret = adjust_resource(res, r_start, r_end - r_start + 1); break; } - mutex_unlock(&rsrc_mutex); return ret; } @@ -706,7 +693,6 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num, data.offset = base & data.mask; data.map = &s_data->io_db; - mutex_lock(&rsrc_mutex); #ifdef CONFIG_PCI if (s->cb_dev) { ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, @@ -715,7 +701,6 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num, #endif ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, 1, pcmcia_align, &data); - mutex_unlock(&rsrc_mutex); if (ret != 0) { kfree(res); @@ -748,7 +733,6 @@ static struct resource *nonstatic_find_mem_region(u_long base, u_long num, min = 0x100000UL + base; } - mutex_lock(&rsrc_mutex); #ifdef CONFIG_PCI if (s->cb_dev) { ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, @@ -758,7 +742,6 @@ static struct resource *nonstatic_find_mem_region(u_long base, u_long num, #endif ret = allocate_resource(&iomem_resource, res, num, min, max, 1, pcmcia_align, &data); - mutex_unlock(&rsrc_mutex); if (ret == 0 || low) break; low = 1; @@ -781,7 +764,6 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned if (end < start) return -EINVAL; - mutex_lock(&rsrc_mutex); switch (action) { case ADD_MANAGED_RESOURCE: ret = add_interval(&data->mem_db, start, size); @@ -794,7 +776,6 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned default: ret = -EINVAL; } - mutex_unlock(&rsrc_mutex); return ret; } @@ -812,7 +793,6 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long if (end > IO_SPACE_LIMIT) return -EINVAL; - mutex_lock(&rsrc_mutex); switch (action) { case ADD_MANAGED_RESOURCE: if (add_interval(&data->io_db, start, size) != 0) { @@ -831,7 +811,6 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long ret = -EINVAL; break; } - mutex_unlock(&rsrc_mutex); return ret; } @@ -929,7 +908,6 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s) struct socket_data *data = s->resource_data; struct resource_map *p, *q; - mutex_lock(&rsrc_mutex); for (p = data->mem_db.next; p != &data->mem_db; p = q) { q = p->next; kfree(p); @@ -938,7 +916,6 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s) q = p->next; kfree(p); } - mutex_unlock(&rsrc_mutex); } @@ -965,7 +942,7 @@ static ssize_t show_io_db(struct device *dev, struct resource_map *p; ssize_t ret = 0; - mutex_lock(&rsrc_mutex); + mutex_lock(&s->ops_mutex); data = s->resource_data; for (p = data->io_db.next; p != &data->io_db; p = p->next) { @@ -977,7 +954,7 @@ static ssize_t show_io_db(struct device *dev, ((unsigned long) p->base + p->num - 1)); } - mutex_unlock(&rsrc_mutex); + mutex_unlock(&s->ops_mutex); return ret; } @@ -1005,9 +982,11 @@ static ssize_t store_io_db(struct device *dev, if (end_addr < start_addr) return -EINVAL; + mutex_lock(&s->ops_mutex); ret = adjust_io(s, add, start_addr, end_addr); if (!ret) s->resource_setup_new = 1; + mutex_unlock(&s->ops_mutex); return ret ? ret : count; } @@ -1021,7 +1000,7 @@ static ssize_t show_mem_db(struct device *dev, struct resource_map *p; ssize_t ret = 0; - mutex_lock(&rsrc_mutex); + mutex_lock(&s->ops_mutex); data = s->resource_data; for (p = data->mem_db.next; p != &data->mem_db; p = p->next) { @@ -1033,7 +1012,7 @@ static ssize_t show_mem_db(struct device *dev, ((unsigned long) p->base + p->num - 1)); } - mutex_unlock(&rsrc_mutex); + mutex_unlock(&s->ops_mutex); return ret; } @@ -1061,9 +1040,11 @@ static ssize_t store_mem_db(struct device *dev, if (end_addr < start_addr) return -EINVAL; + mutex_lock(&s->ops_mutex); ret = adjust_memory(s, add, start_addr, end_addr); if (!ret) s->resource_setup_new = 1; + mutex_unlock(&s->ops_mutex); return ret ? ret : count; } -- cgit v1.2.3 From f971dbd5da4e2fbf756d07b938a9c65a9c75178b Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 17 Jan 2010 18:13:31 +0100 Subject: pcmcia: use pccardd to handle eject, insert, suspend and resume requests This avoids any sysfs-related deadlock (or lockdep warning), such as reported at http://lkml.org/lkml/2010/1/17/88 . Reported-by: Ming Lei Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 177 ++++++++++++++---------------------------- drivers/pcmcia/cs_internal.h | 10 +-- drivers/pcmcia/pcmcia_ioctl.c | 8 +- drivers/pcmcia/socket_sysfs.c | 26 ++++--- 4 files changed, 81 insertions(+), 140 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 7ba45b0cca6..823ecda3221 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -505,7 +505,10 @@ static int socket_insert(struct pcmcia_socket *skt) dev_dbg(&skt->dev, "insert\n"); mutex_lock(&skt->ops_mutex); - WARN_ON(skt->state & SOCKET_INUSE); + if (skt->state & SOCKET_INUSE) { + mutex_unlock(&skt->ops_mutex); + return -EINVAL; + } skt->state |= SOCKET_INUSE; ret = socket_setup(skt, setup_delay); @@ -682,16 +685,19 @@ static int pccardd(void *__skt) for (;;) { unsigned long flags; unsigned int events; + unsigned int sysfs_events; set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&skt->thread_lock, flags); events = skt->thread_events; skt->thread_events = 0; + sysfs_events = skt->sysfs_events; + skt->sysfs_events = 0; spin_unlock_irqrestore(&skt->thread_lock, flags); + mutex_lock(&skt->skt_mutex); if (events) { - mutex_lock(&skt->skt_mutex); if (events & SS_DETECT) socket_detect_change(skt); if (events & SS_BATDEAD) @@ -700,10 +706,34 @@ static int pccardd(void *__skt) send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW); if (events & SS_READY) send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW); - mutex_unlock(&skt->skt_mutex); - continue; } + if (sysfs_events) { + if (sysfs_events & PCMCIA_UEVENT_EJECT) + socket_remove(skt); + if (sysfs_events & PCMCIA_UEVENT_INSERT) + socket_insert(skt); + if ((sysfs_events & PCMCIA_UEVENT_RESUME) && + !(skt->state & SOCKET_CARDBUS)) { + ret = socket_resume(skt); + if (!ret && skt->callback) + skt->callback->resume(skt); + } + if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) && + !(skt->state & SOCKET_CARDBUS)) { + if (skt->callback) + ret = skt->callback->suspend(skt); + else + ret = 0; + if (!ret) + socket_suspend(skt); + } + } + mutex_unlock(&skt->skt_mutex); + + if (events || sysfs_events) + continue; + if (kthread_should_stop()) break; @@ -745,6 +775,30 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events) } /* pcmcia_parse_events */ EXPORT_SYMBOL(pcmcia_parse_events); +/** + * pcmcia_parse_uevents() - tell pccardd to issue manual commands + * @s: the PCMCIA socket we wan't to command + * @events: events to pass to pccardd + * + * userspace-issued insert, eject, suspend and resume commands must be + * handled by pccardd to avoid any sysfs-related deadlocks. Valid events + * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert), + * PCMCIA_UEVENT_RESUME (for resume) and PCMCIA_UEVENT_SUSPEND (for suspend). + */ +void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events) +{ + unsigned long flags; + dev_dbg(&s->dev, "parse_uevents: events %08x\n", events); + if (s->thread) { + spin_lock_irqsave(&s->thread_lock, flags); + s->sysfs_events |= events; + spin_unlock_irqrestore(&s->thread_lock, flags); + + wake_up_process(s->thread); + } +} +EXPORT_SYMBOL(pcmcia_parse_uevents); + /* register pcmcia_callback */ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) @@ -828,121 +882,6 @@ int pcmcia_reset_card(struct pcmcia_socket *skt) EXPORT_SYMBOL(pcmcia_reset_card); -/* These shut down or wake up a socket. They are sort of user - * initiated versions of the APM suspend and resume actions. - */ -int pcmcia_suspend_card(struct pcmcia_socket *skt) -{ - int ret; - - dev_dbg(&skt->dev, "suspending socket\n"); - - mutex_lock(&skt->skt_mutex); - do { - if (!(skt->state & SOCKET_PRESENT)) { - ret = -ENODEV; - break; - } - if (skt->state & SOCKET_CARDBUS) { - ret = -EPERM; - break; - } - if (skt->callback) { - ret = skt->callback->suspend(skt); - if (ret) - break; - } - ret = socket_suspend(skt); - } while (0); - mutex_unlock(&skt->skt_mutex); - - return ret; -} /* suspend_card */ -EXPORT_SYMBOL(pcmcia_suspend_card); - - -int pcmcia_resume_card(struct pcmcia_socket *skt) -{ - int ret; - - dev_dbg(&skt->dev, "waking up socket\n"); - - mutex_lock(&skt->skt_mutex); - do { - if (!(skt->state & SOCKET_PRESENT)) { - ret = -ENODEV; - break; - } - if (skt->state & SOCKET_CARDBUS) { - ret = -EPERM; - break; - } - ret = socket_resume(skt); - if (!ret && skt->callback) - skt->callback->resume(skt); - } while (0); - mutex_unlock(&skt->skt_mutex); - - return ret; -} /* resume_card */ -EXPORT_SYMBOL(pcmcia_resume_card); - - -/* These handle user requests to eject or insert a card. */ -int pcmcia_eject_card(struct pcmcia_socket *skt) -{ - int ret; - - dev_dbg(&skt->dev, "user eject request\n"); - - mutex_lock(&skt->skt_mutex); - do { - if (!(skt->state & SOCKET_PRESENT)) { - ret = -ENODEV; - break; - } - - ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW); - if (ret != 0) { - ret = -EINVAL; - break; - } - - socket_remove(skt); - ret = 0; - } while (0); - mutex_unlock(&skt->skt_mutex); - - return ret; -} /* eject_card */ -EXPORT_SYMBOL(pcmcia_eject_card); - - -int pcmcia_insert_card(struct pcmcia_socket *skt) -{ - int ret; - - dev_dbg(&skt->dev, "user insert request\n"); - - mutex_lock(&skt->skt_mutex); - do { - if (skt->state & SOCKET_PRESENT) { - ret = -EBUSY; - break; - } - if (socket_insert(skt) == -ENODEV) { - ret = -ENODEV; - break; - } - ret = 0; - } while (0); - mutex_unlock(&skt->skt_mutex); - - return ret; -} /* insert_card */ -EXPORT_SYMBOL(pcmcia_insert_card); - - static int pcmcia_socket_uevent(struct device *dev, struct kobj_uevent_env *env) { diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index bd386d77845..127c97acf84 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -124,11 +124,11 @@ extern struct class pcmcia_socket_class; int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c); struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr); -int pcmcia_suspend_card(struct pcmcia_socket *skt); -int pcmcia_resume_card(struct pcmcia_socket *skt); - -int pcmcia_eject_card(struct pcmcia_socket *skt); -int pcmcia_insert_card(struct pcmcia_socket *skt); +void pcmcia_parse_uevents(struct pcmcia_socket *socket, unsigned int events); +#define PCMCIA_UEVENT_EJECT 0x0001 +#define PCMCIA_UEVENT_INSERT 0x0002 +#define PCMCIA_UEVENT_SUSPEND 0x0004 +#define PCMCIA_UEVENT_RESUME 0x0008 struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); void pcmcia_put_socket(struct pcmcia_socket *skt); diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index 96fd236f52a..13a7132cf68 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -925,16 +925,16 @@ static int ds_ioctl(struct inode *inode, struct file *file, ret = pccard_validate_cis(s, &buf->cisinfo.Chains); break; case DS_SUSPEND_CARD: - ret = pcmcia_suspend_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND); break; case DS_RESUME_CARD: - ret = pcmcia_resume_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME); break; case DS_EJECT_CARD: - err = pcmcia_eject_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT); break; case DS_INSERT_CARD: - err = pcmcia_insert_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT); break; case DS_ACCESS_CONFIGURATION_REGISTER: if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) { diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index e8826df00a3..fba0e30183f 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -88,15 +88,14 @@ static DEVICE_ATTR(card_vcc, 0444, pccard_show_vcc, NULL); static ssize_t pccard_store_insert(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - ssize_t ret; struct pcmcia_socket *s = to_socket(dev); if (!count) return -EINVAL; - ret = pcmcia_insert_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT); - return ret ? ret : count; + return count; } static DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert); @@ -113,18 +112,22 @@ static ssize_t pccard_store_card_pm_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - ssize_t ret = -EINVAL; struct pcmcia_socket *s = to_socket(dev); + ssize_t ret = count; if (!count) return -EINVAL; - if (!(s->state & SOCKET_SUSPEND) && !strncmp(buf, "off", 3)) - ret = pcmcia_suspend_card(s); - else if ((s->state & SOCKET_SUSPEND) && !strncmp(buf, "on", 2)) - ret = pcmcia_resume_card(s); + if (!strncmp(buf, "off", 3)) + pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND); + else { + if (!strncmp(buf, "on", 2)) + pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME); + else + ret = -EINVAL; + } - return ret ? -ENODEV : count; + return ret; } static DEVICE_ATTR(card_pm_state, 0644, pccard_show_card_pm_state, pccard_store_card_pm_state); @@ -132,15 +135,14 @@ static ssize_t pccard_store_eject(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - ssize_t ret; struct pcmcia_socket *s = to_socket(dev); if (!count) return -EINVAL; - ret = pcmcia_eject_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT); - return ret ? ret : count; + return count; } static DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject); -- cgit v1.2.3 From af461fc1875b6ec18e23b5f670af36c4ed35c84e Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 17 Jan 2010 19:30:53 +0100 Subject: pcmcia: delay re-scanning and re-querying of PCMCIA bus After a CIS update -- or the finalization of the resource database --, proceed with the re-scanning or re-querying of PCMCIA cards only in a separate thread to avoid deadlocks. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cistpl.c | 10 +----- drivers/pcmcia/cs.c | 8 ++++- drivers/pcmcia/cs_internal.h | 3 +- drivers/pcmcia/ds.c | 80 ++++++++++++++++++++++++++----------------- drivers/pcmcia/socket_sysfs.c | 11 +----- 5 files changed, 59 insertions(+), 53 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 14de287a8bf..17a5da32cce 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -1670,15 +1670,7 @@ static ssize_t pccard_store_cis(struct kobject *kobj, if (error) return -EIO; - mutex_lock(&s->skt_mutex); - if ((s->callback) && (s->state & SOCKET_PRESENT) && - !(s->state & SOCKET_CARDBUS)) { - if (try_module_get(s->callback->owner)) { - s->callback->requery(s, 1); - module_put(s->callback->owner); - } - } - mutex_unlock(&s->skt_mutex); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); return count; } diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 823ecda3221..d529e02c2d5 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -728,6 +728,11 @@ static int pccardd(void *__skt) if (!ret) socket_suspend(skt); } + if ((sysfs_events & PCMCIA_UEVENT_REQUERY) && + !(skt->state & SOCKET_CARDBUS)) { + if (!ret && skt->callback) + skt->callback->requery(skt); + } } mutex_unlock(&skt->skt_mutex); @@ -783,7 +788,8 @@ EXPORT_SYMBOL(pcmcia_parse_events); * userspace-issued insert, eject, suspend and resume commands must be * handled by pccardd to avoid any sysfs-related deadlocks. Valid events * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert), - * PCMCIA_UEVENT_RESUME (for resume) and PCMCIA_UEVENT_SUSPEND (for suspend). + * PCMCIA_UEVENT_RESUME (for resume), PCMCIA_UEVENT_SUSPEND (for suspend) + * and PCMCIA_UEVENT_REQUERY (for re-querying the PCMCIA card). */ void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events) { diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 127c97acf84..f95864c2191 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -110,7 +110,7 @@ struct pcmcia_callback{ struct module *owner; int (*event) (struct pcmcia_socket *s, event_t event, int priority); - void (*requery) (struct pcmcia_socket *s, int new_cis); + void (*requery) (struct pcmcia_socket *s); int (*validate) (struct pcmcia_socket *s, unsigned int *i); int (*suspend) (struct pcmcia_socket *s); int (*resume) (struct pcmcia_socket *s); @@ -129,6 +129,7 @@ void pcmcia_parse_uevents(struct pcmcia_socket *socket, unsigned int events); #define PCMCIA_UEVENT_INSERT 0x0002 #define PCMCIA_UEVENT_SUSPEND 0x0004 #define PCMCIA_UEVENT_RESUME 0x0008 +#define PCMCIA_UEVENT_REQUERY 0x0010 struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); void pcmcia_put_socket(struct pcmcia_socket *skt); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 76a21638291..5400e20c664 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -432,16 +432,20 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_MANFID, &manf_id)) { + mutex_lock(&p_dev->socket->ops_mutex); p_dev->manf_id = manf_id.manf; p_dev->card_id = manf_id.card; p_dev->has_manf_id = 1; p_dev->has_card_id = 1; + mutex_unlock(&p_dev->socket->ops_mutex); } if (!pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_FUNCID, &func_id)) { + mutex_lock(&p_dev->socket->ops_mutex); p_dev->func_id = func_id.func; p_dev->has_func_id = 1; + mutex_unlock(&p_dev->socket->ops_mutex); } else { /* rule of thumb: cards with no FUNCID, but with * common memory device geometry information, are @@ -458,14 +462,17 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) dev_dbg(&p_dev->dev, "mem device geometry probably means " "FUNCID_MEMORY\n"); + mutex_lock(&p_dev->socket->ops_mutex); p_dev->func_id = CISTPL_FUNCID_MEMORY; p_dev->has_func_id = 1; + mutex_unlock(&p_dev->socket->ops_mutex); } kfree(devgeo); } if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1, vers1)) { + mutex_lock(&p_dev->socket->ops_mutex); for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) { char *tmp; unsigned int length; @@ -484,6 +491,7 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) p_dev->prod_id[i] = strncpy(p_dev->prod_id[i], tmp, length); } + mutex_unlock(&p_dev->socket->ops_mutex); } kfree(vers1); @@ -660,7 +668,7 @@ static void pcmcia_delayed_add_device(struct work_struct *work) pcmcia_device_add(s, mfc_pfc); } -static int pcmcia_requery(struct device *dev, void * _data) +static int pcmcia_requery_callback(struct device *dev, void * _data) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); if (!p_dev->dev.driver) { @@ -671,44 +679,57 @@ static int pcmcia_requery(struct device *dev, void * _data) return 0; } -static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis) +static void pcmcia_requery(struct pcmcia_socket *s) { - int no_devices = 0; - int ret = 0; - - /* must be called with skt_mutex held */ - dev_dbg(&skt->dev, "re-scanning socket %d\n", skt->sock); + int present; - mutex_lock(&skt->ops_mutex); - if (list_empty(&skt->devices_list)) - no_devices = 1; - mutex_unlock(&skt->ops_mutex); + mutex_lock(&s->ops_mutex); + present = s->pcmcia_state.present; + mutex_unlock(&s->ops_mutex); - /* If this is because of a CIS override, start over */ - if (new_cis && !no_devices) - pcmcia_card_remove(skt, NULL); + if (!present) + return; - /* if no devices were added for this socket yet because of - * missing resource information or other trouble, we need to - * do this now. */ - if (no_devices || new_cis) { - ret = pcmcia_card_add(skt); - if (ret) - return; + if (s->functions == 0) { + pcmcia_card_add(s); + return; } /* some device information might have changed because of a CIS * update or because we can finally read it correctly... so * determine it again, overwriting old values if necessary. */ - bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery); + bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery_callback); + + /* if the CIS changed, we need to check whether the number of + * functions changed. */ + if (s->fake_cis) { + int old_funcs, new_funcs; + cistpl_longlink_mfc_t mfc; + + /* does this cis override add or remove functions? */ + old_funcs = s->functions; + + if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, + &mfc)) + new_funcs = mfc.nfn; + else + new_funcs = 1; + if (old_funcs > new_funcs) { + pcmcia_card_remove(s, NULL); + pcmcia_card_add(s); + } else if (new_funcs > old_funcs) { + s->functions = new_funcs; + pcmcia_device_add(s, 1); + } + } /* we re-scan all devices, not just the ones connected to this * socket. This does not matter, though. */ - ret = bus_rescan_devices(&pcmcia_bus_type); - if (ret) - printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n"); + if (bus_rescan_devices(&pcmcia_bus_type)) + dev_warn(&s->dev, "rescanning the bus failed\n"); } + #ifdef CONFIG_PCMCIA_LOAD_CIS /** @@ -1085,7 +1106,6 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); - int ret; if (!count) return -EINVAL; @@ -1093,11 +1113,7 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev, mutex_lock(&p_dev->socket->ops_mutex); p_dev->allow_func_id_match = 1; mutex_unlock(&p_dev->socket->ops_mutex); - - ret = bus_rescan_devices(&pcmcia_bus_type); - if (ret) - printk(KERN_INFO "pcmcia: bus_rescan_devices failed after " - "allowing func_id matches\n"); + pcmcia_parse_uevents(p_dev->socket, PCMCIA_UEVENT_REQUERY); return count; } @@ -1359,7 +1375,7 @@ EXPORT_SYMBOL(pcmcia_dev_present); static struct pcmcia_callback pcmcia_bus_callback = { .owner = THIS_MODULE, .event = ds_event, - .requery = pcmcia_bus_rescan, + .requery = pcmcia_requery, .validate = pccard_validate_cis, .suspend = pcmcia_bus_suspend, .resume = pcmcia_bus_resume, diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index fba0e30183f..08278016e58 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -201,16 +201,7 @@ static ssize_t pccard_store_resource(struct device *dev, s->resource_setup_done = 1; mutex_unlock(&s->ops_mutex); - mutex_lock(&s->skt_mutex); - if ((s->callback) && - (s->state & SOCKET_PRESENT) && - !(s->state & SOCKET_CARDBUS)) { - if (try_module_get(s->callback->owner)) { - s->callback->requery(s, 0); - module_put(s->callback->owner); - } - } - mutex_unlock(&s->skt_mutex); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); return count; } -- cgit v1.2.3 From aa584ca4cdd8db370a524c61fd3ca408303281e9 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 24 Jan 2010 14:36:59 +0100 Subject: pcmcia: use state machine for extended requery The requery callback now also handles the addition of a second pseudo multifunction device. Avoids messing with dev_{g,s}et_drvdata(), and fixes any workqueue <-> skt_mutex deadlock. Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 92 +++++++++++++---------------------------------------- 1 file changed, 22 insertions(+), 70 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 5400e20c664..9968c0d3a5f 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -244,23 +244,11 @@ static void pcmcia_release_dev(struct device *dev) kfree(p_dev); } -static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc) -{ - if (!s->pcmcia_state.device_add_pending) { - dev_dbg(&s->dev, "scheduling to add %s secondary" - " device to %d\n", mfc ? "mfc" : "pfc", s->sock); - s->pcmcia_state.device_add_pending = 1; - s->pcmcia_state.mfc_pfc = mfc; - schedule_work(&s->device_add); - } - return; -} static int pcmcia_device_probe(struct device *dev) { struct pcmcia_device *p_dev; struct pcmcia_driver *p_drv; - struct pcmcia_device_id *did; struct pcmcia_socket *s; cistpl_config_t cis_config; int ret = 0; @@ -273,18 +261,6 @@ static int pcmcia_device_probe(struct device *dev) p_drv = to_pcmcia_drv(dev->driver); s = p_dev->socket; - /* The PCMCIA code passes the match data in via dev_set_drvdata(dev) - * which is an ugly hack. Once the driver probe is called it may - * and often will overwrite the match data so we must save it first - * - * handle pseudo multifunction devices: - * there are at most two pseudo multifunction devices. - * if we're matching against the first, schedule a - * call which will then check whether there are two - * pseudo devices, and if not, add the second one. - */ - did = dev_get_drvdata(&p_dev->dev); - dev_dbg(dev, "trying to bind to %s\n", p_drv->drv.name); if ((!p_drv->probe) || (!p_dev->function_config) || @@ -314,9 +290,9 @@ static int pcmcia_device_probe(struct device *dev) } mutex_lock(&s->ops_mutex); - if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && + if ((s->pcmcia_state.has_pfc) && (p_dev->socket->device_count == 1) && (p_dev->device_no == 0)) - pcmcia_add_device_later(p_dev->socket, 0); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); mutex_unlock(&s->ops_mutex); put_module: @@ -369,7 +345,6 @@ static int pcmcia_device_remove(struct device *dev) { struct pcmcia_device *p_dev; struct pcmcia_driver *p_drv; - struct pcmcia_device_id *did; int i; p_dev = to_pcmcia_dev(dev); @@ -381,8 +356,7 @@ static int pcmcia_device_remove(struct device *dev) * pseudo multi-function card, we need to unbind * all devices */ - did = dev_get_drvdata(&p_dev->dev); - if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && + if ((p_dev->socket->pcmcia_state.has_pfc) && (p_dev->socket->device_count > 0) && (p_dev->device_no == 0)) pcmcia_card_remove(p_dev->socket, p_dev); @@ -528,8 +502,8 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu p_dev->device_no = (s->device_count++); mutex_unlock(&s->ops_mutex); - /* max of 4 devices per card */ - if (p_dev->device_no >= 4) + /* max of 2 devices per card */ + if (p_dev->device_no >= 2) goto err_free; p_dev->socket = s; @@ -652,22 +626,6 @@ static int pcmcia_card_add(struct pcmcia_socket *s) } -static void pcmcia_delayed_add_device(struct work_struct *work) -{ - struct pcmcia_socket *s = - container_of(work, struct pcmcia_socket, device_add); - u8 mfc_pfc; - - mutex_lock(&s->ops_mutex); - mfc_pfc = s->pcmcia_state.mfc_pfc; - s->pcmcia_state.device_add_pending = 0; - s->pcmcia_state.mfc_pfc = 0; - mutex_unlock(&s->ops_mutex); - - dev_dbg(&s->dev, "adding additional device to %d\n", s->sock); - pcmcia_device_add(s, mfc_pfc); -} - static int pcmcia_requery_callback(struct device *dev, void * _data) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); @@ -679,9 +637,10 @@ static int pcmcia_requery_callback(struct device *dev, void * _data) return 0; } + static void pcmcia_requery(struct pcmcia_socket *s) { - int present; + int present, has_pfc; mutex_lock(&s->ops_mutex); present = s->pcmcia_state.present; @@ -723,6 +682,15 @@ static void pcmcia_requery(struct pcmcia_socket *s) } } + /* If the PCMCIA device consists of two pseudo devices, + * call pcmcia_device_add() -- which will fail if both + * devices are already registered. */ + mutex_lock(&s->ops_mutex); + has_pfc = s->pcmcia_state.has_pfc; + mutex_unlock(&s->ops_mutex); + if (has_pfc) + pcmcia_device_add(s, 0); + /* we re-scan all devices, not just the ones connected to this * socket. This does not matter, though. */ if (bus_rescan_devices(&pcmcia_bus_type)) @@ -746,9 +714,6 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) struct pcmcia_socket *s = dev->socket; const struct firmware *fw; int ret = -ENOMEM; - int no_funcs; - int old_funcs; - cistpl_longlink_mfc_t mfc; if (!filename) return -EINVAL; @@ -775,22 +740,8 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) /* update information */ pcmcia_device_query(dev); - /* does this cis override add or remove functions? */ - old_funcs = s->functions; - - if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc)) - no_funcs = mfc.nfn; - else - no_funcs = 1; - s->functions = no_funcs; - - if (old_funcs > no_funcs) - pcmcia_card_remove(s, dev); - else if (no_funcs > old_funcs) { - mutex_lock(&s->ops_mutex); - pcmcia_add_device_later(s, 1); - mutex_unlock(&s->ops_mutex); - } + /* requery (as number of functions might have changed) */ + pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); } release: release_firmware(fw); @@ -857,6 +808,9 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) { if (dev->device_no != did->device_no) return 0; + mutex_lock(&dev->socket->ops_mutex); + dev->socket->pcmcia_state.has_pfc = 1; + mutex_unlock(&dev->socket->ops_mutex); } if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) { @@ -905,8 +859,6 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, return 0; } - dev_set_drvdata(&dev->dev, did); - return 1; } @@ -1300,6 +1252,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) case CS_EVENT_CARD_INSERTION: mutex_lock(&s->ops_mutex); + s->pcmcia_state.has_pfc = 0; s->pcmcia_state.present = 1; destroy_cis_cache(s); /* to be on the safe side... */ mutex_unlock(&s->ops_mutex); @@ -1411,7 +1364,6 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev, init_waitqueue_head(&socket->queue); #endif INIT_LIST_HEAD(&socket->devices_list); - INIT_WORK(&socket->device_add, pcmcia_delayed_add_device); memset(&socket->pcmcia_state, 0, sizeof(u8)); socket->device_count = 0; -- cgit v1.2.3 From c3bfc96ef7366aa996fb8286a36f3333a3b4ff25 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Mon, 18 Jan 2010 08:43:39 +0100 Subject: pcmcia: avoid sysfs-related lockup for cardbus In cb_free(), we remove some sysfs files -- other sysfs files might grab ops_mutex, so we cannot hold it while removing sysfs files. This fixes http://lkml.org/lkml/2010/1/17/88 . Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index d529e02c2d5..9a49c394f04 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -400,10 +400,19 @@ static void socket_shutdown(struct pcmcia_socket *s) s->lock_count = 0; kfree(s->fake_cis); s->fake_cis = NULL; + s->functions = 0; + + /* From here on we can be sure that only we (that is, the + * pccardd thread) accesses this socket, and all (16-bit) + * PCMCIA interactions are gone. Therefore, release + * ops_mutex so that we don't get a sysfs-related lockdep + * warning. + */ + mutex_unlock(&s->ops_mutex); + #ifdef CONFIG_CARDBUS cb_free(s); #endif - s->functions = 0; /* give socket some time to power down */ msleep(100); @@ -415,7 +424,6 @@ static void socket_shutdown(struct pcmcia_socket *s) } s->state &= ~SOCKET_INUSE; - mutex_unlock(&s->ops_mutex); } static int socket_setup(struct pcmcia_socket *skt, int initial_delay) -- cgit v1.2.3 From 44961a03adbf16d872f0b83ec848d0759516d33f Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 24 Jan 2010 12:11:02 +0100 Subject: pcmcia: avoid prod_id memleak Reported-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 9968c0d3a5f..93925f5908b 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -237,8 +237,11 @@ static void pcmcia_release_function(struct kref *ref) static void pcmcia_release_dev(struct device *dev) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); + int i; dev_dbg(dev, "releasing device\n"); pcmcia_put_socket(p_dev->socket); + for (i = 0; i < 4; i++) + kfree(p_dev->prod_id[i]); kfree(p_dev->devname); kref_put(&p_dev->function_config->ref, pcmcia_release_function); kfree(p_dev); @@ -450,6 +453,7 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) { char *tmp; unsigned int length; + char *new; tmp = vers1->str + vers1->ofs[i]; @@ -457,13 +461,15 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) if ((length < 2) || (length > 255)) continue; - p_dev->prod_id[i] = kmalloc(sizeof(char) * length, - GFP_KERNEL); - if (!p_dev->prod_id[i]) + new = kmalloc(sizeof(char) * length, GFP_KERNEL); + if (!new) continue; - p_dev->prod_id[i] = strncpy(p_dev->prod_id[i], - tmp, length); + new = strncpy(new, tmp, length); + + tmp = p_dev->prod_id[i]; + p_dev->prod_id[i] = new; + kfree(tmp); } mutex_unlock(&p_dev->socket->ops_mutex); } @@ -485,6 +491,7 @@ static DEFINE_MUTEX(device_add_lock); struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) { struct pcmcia_device *p_dev, *tmp_dev; + int i; s = pcmcia_get_socket(s); if (!s) @@ -575,6 +582,8 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu s->device_count--; mutex_unlock(&s->ops_mutex); + for (i = 0; i < 4; i++) + kfree(p_dev->prod_id[i]); kfree(p_dev->devname); kfree(p_dev); err_put: -- cgit v1.2.3 From 7ab24855482fbc47712de46c05726d59cd0748e2 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 17 Feb 2010 18:00:07 +0100 Subject: pcmcia: add locking to pcmcia_{read,write}_cis_mem Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cistpl.c | 27 +++++++++++++++++++++------ drivers/pcmcia/rsrc_nonstatic.c | 5 +++++ 2 files changed, 26 insertions(+), 6 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 17a5da32cce..602f574898d 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -83,6 +83,8 @@ void release_cis_mem(struct pcmcia_socket *s) * Map the card memory at "card_offset" into virtual space. * If flags & MAP_ATTRIB, map the attribute space, otherwise * map the memory space. + * + * Must be called with ops_mutex held. */ static void __iomem * set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags) @@ -90,13 +92,11 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag pccard_mem_map *mem = &s->cis_mem; int ret; - mutex_lock(&s->ops_mutex); if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) { mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s); if (mem->res == NULL) { dev_printk(KERN_NOTICE, &s->dev, "cs: unable to map card memory!\n"); - mutex_unlock(&s->ops_mutex); return NULL; } s->cis_virt = NULL; @@ -112,7 +112,6 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag if (ret) { iounmap(s->cis_virt); s->cis_virt = NULL; - mutex_unlock(&s->ops_mutex); return NULL; } @@ -122,7 +121,6 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag s->cis_virt = ioremap(mem->static_start, s->map_size); } - mutex_unlock(&s->ops_mutex); return s->cis_virt; } @@ -145,6 +143,7 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len); + mutex_lock(&s->ops_mutex); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed locations in common memory */ @@ -156,7 +155,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0)); if (!sys) { + dev_dbg(&s->dev, "could not map memory\n"); memset(ptr, 0xff, len); + mutex_unlock(&s->ops_mutex); return -1; } @@ -170,6 +171,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, } else { u_int inc = 1, card_offset, flags; + if (addr > CISTPL_MAX_CIS_SIZE) + dev_dbg(&s->dev, "attempt to read CIS mem at addr %#x", addr); + flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0); if (attr) { flags |= MAP_ATTRIB; @@ -181,7 +185,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, while (len) { sys = set_cis_map(s, card_offset, flags); if (!sys) { + dev_dbg(&s->dev, "could not map memory\n"); memset(ptr, 0xff, len); + mutex_unlock(&s->ops_mutex); return -1; } end = sys + s->map_size; @@ -195,6 +201,7 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, addr = 0; } } + mutex_unlock(&s->ops_mutex); dev_dbg(&s->dev, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n", *(u_char *)(ptr+0), *(u_char *)(ptr+1), *(u_char *)(ptr+2), *(u_char *)(ptr+3)); @@ -210,6 +217,7 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len); + mutex_lock(&s->ops_mutex); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed locations in common memory */ @@ -220,8 +228,11 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, } sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0)); - if (!sys) + if (!sys) { + dev_dbg(&s->dev, "could not map memory\n"); + mutex_unlock(&s->ops_mutex); return; /* FIXME: Error */ + } writeb(flags, sys+CISREG_ICTRL0); writeb(addr & 0xff, sys+CISREG_IADDR0); @@ -243,8 +254,11 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, card_offset = addr & ~(s->map_size-1); while (len) { sys = set_cis_map(s, card_offset, flags); - if (!sys) + if (!sys) { + dev_dbg(&s->dev, "could not map memory\n"); + mutex_unlock(&s->ops_mutex); return; /* FIXME: error */ + } end = sys + s->map_size; sys = sys + (addr & (s->map_size-1)); @@ -257,6 +271,7 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, addr = 0; } } + mutex_unlock(&s->ops_mutex); } diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index c13424f7b47..19cecb53975 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -273,6 +273,11 @@ static int readable(struct pcmcia_socket *s, struct resource *res, { int ret = -EINVAL; + if (s->fake_cis) { + dev_dbg(&s->dev, "fake CIS is being used: can't validate mem\n"); + return 0; + } + s->cis_mem.res = res; s->cis_virt = ioremap(res->start, s->map_size); if (s->cis_virt) { -- cgit v1.2.3 From d700518a0e4d17a47321414e5e487ed53f4ae752 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 17 Feb 2010 18:01:31 +0100 Subject: pcmcia: use read_cis_mem return value Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cistpl.c | 63 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 19 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 602f574898d..2f3622dd4b6 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -283,30 +283,32 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, ======================================================================*/ -static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, - size_t len, void *ptr) +static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, + size_t len, void *ptr) { struct cis_cache_entry *cis; - int ret; + int ret = 0; if (s->state & SOCKET_CARDBUS) - return; + return -EINVAL; mutex_lock(&s->ops_mutex); if (s->fake_cis) { if (s->fake_cis_len >= addr+len) memcpy(ptr, s->fake_cis+addr, len); - else + else { memset(ptr, 0xff, len); + ret = -EINVAL; + } mutex_unlock(&s->ops_mutex); - return; + return ret; } list_for_each_entry(cis, &s->cis_cache, node) { if (cis->addr == addr && cis->len == len && cis->attr == attr) { memcpy(ptr, cis->cache, len); mutex_unlock(&s->ops_mutex); - return; + return 0; } } mutex_unlock(&s->ops_mutex); @@ -326,6 +328,7 @@ static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, mutex_unlock(&s->ops_mutex); } } + return ret; } static void @@ -374,6 +377,7 @@ int verify_cis_cache(struct pcmcia_socket *s) { struct cis_cache_entry *cis; char *buf; + int ret; if (s->state & SOCKET_CARDBUS) return -EINVAL; @@ -390,9 +394,8 @@ int verify_cis_cache(struct pcmcia_socket *s) if (len > 256) len = 256; - pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf); - - if (memcmp(buf, cis->cache, len) != 0) { + ret = pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf); + if (ret || memcmp(buf, cis->cache, len) != 0) { kfree(buf); return -1; } @@ -425,6 +428,7 @@ int pcmcia_replace_cis(struct pcmcia_socket *s, } s->fake_cis_len = len; memcpy(s->fake_cis, data, len); + dev_info(&s->dev, "Using replacement CIS\n"); mutex_unlock(&s->ops_mutex); return 0; } @@ -478,11 +482,14 @@ static int follow_link(struct pcmcia_socket *s, tuple_t *tuple) { u_char link[5]; u_int ofs; + int ret; if (MFC_FN(tuple->Flags)) { /* Get indirect link from the MFC tuple */ - read_cis_cache(s, LINK_SPACE(tuple->Flags), + ret = read_cis_cache(s, LINK_SPACE(tuple->Flags), tuple->LinkOffset, 5, link); + if (ret) + return -1; ofs = get_unaligned_le32(link + 1); SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR); /* Move to the next indirect link */ @@ -498,7 +505,9 @@ static int follow_link(struct pcmcia_socket *s, tuple_t *tuple) if (SPACE(tuple->Flags)) { /* This is ugly, but a common CIS error is to code the long link offset incorrectly, so we check the right spot... */ - read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); + ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); + if (ret) + return -1; if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && (strncmp(link+2, "CIS", 3) == 0)) return ofs; @@ -506,7 +515,9 @@ static int follow_link(struct pcmcia_socket *s, tuple_t *tuple) /* Then, we try the wrong spot... */ ofs = ofs >> 1; } - read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); + ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); + if (ret) + return -1; if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && (strncmp(link+2, "CIS", 3) == 0)) return ofs; @@ -518,6 +529,7 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_ { u_char link[2], tmp; int ofs, i, attr; + int ret; if (!s) return -EINVAL; @@ -532,7 +544,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_ if (link[1] == 0xff) { link[0] = CISTPL_END; } else { - read_cis_cache(s, attr, ofs, 2, link); + ret = read_cis_cache(s, attr, ofs, 2, link); + if (ret) + return -1; if (link[0] == CISTPL_NULL) { ofs++; continue; } @@ -544,7 +558,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_ if (ofs < 0) return -ENOSPC; attr = SPACE(tuple->Flags); - read_cis_cache(s, attr, ofs, 2, link); + ret = read_cis_cache(s, attr, ofs, 2, link); + if (ret) + return -1; } /* Is this a link tuple? Make a note of it */ @@ -558,12 +574,16 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_ case CISTPL_LONGLINK_A: HAS_LINK(tuple->Flags) = 1; LINK_SPACE(tuple->Flags) = attr | IS_ATTR; - read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset); + ret = read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset); + if (ret) + return -1; break; case CISTPL_LONGLINK_C: HAS_LINK(tuple->Flags) = 1; LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR; - read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset); + ret = read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset); + if (ret) + return -1; break; case CISTPL_INDIRECT: HAS_LINK(tuple->Flags) = 1; @@ -575,7 +595,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_ LINK_SPACE(tuple->Flags) = attr; if (function == BIND_FN_ALL) { /* Follow all the MFC links */ - read_cis_cache(s, attr, ofs+2, 1, &tmp); + ret = read_cis_cache(s, attr, ofs+2, 1, &tmp); + if (ret) + return -1; MFC_FN(tuple->Flags) = tmp; } else { /* Follow exactly one of the links */ @@ -616,6 +638,7 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_ int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple) { u_int len; + int ret; if (!s) return -EINVAL; @@ -626,9 +649,11 @@ int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple) tuple->TupleDataLen = tuple->TupleLink; if (len == 0) return 0; - read_cis_cache(s, SPACE(tuple->Flags), + ret = read_cis_cache(s, SPACE(tuple->Flags), tuple->CISOffset + tuple->TupleOffset, _MIN(len, tuple->TupleDataMax), tuple->TupleData); + if (ret) + return -1; return 0; } -- cgit v1.2.3 From d801c1409ef7d45339cbe8ac9de28ade6ed4699a Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 17 Feb 2010 11:02:22 +0100 Subject: pcmcia: remove useless msleep in ds.c As this is the socket thread (pccardd) starting up, we do not have anything to wait for in ds.c. Instead, wait the same amount of time in pccardd to allow userspace to catch up and - possibly - execute pcmcia-socket-startup. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 3 +++ drivers/pcmcia/ds.c | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 9a49c394f04..e679e708db6 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -689,6 +689,9 @@ static int pccardd(void *__skt) complete(&skt->thread_done); + /* wait for userspace to catch up */ + msleep(250); + set_freezable(); for (;;) { unsigned long flags; diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 93925f5908b..0f98be4450b 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -1356,12 +1356,6 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev, return -ENODEV; } - /* - * Ugly. But we want to wait for the socket threads to have started up. - * We really should let the drivers themselves drive some of this.. - */ - msleep(250); - ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr); if (ret) { dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n"); -- cgit v1.2.3 From f309cb3e4fe79a68cb2fdedfb8a8a950a2cfe490 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 17 Feb 2010 14:35:33 +0100 Subject: pcmcia: allow for extension of resource interval If a new interval overlaps or extends an existing interval in add_interval(), do not fail, but extend the existing interval. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/rsrc_nonstatic.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 19cecb53975..a06881c3b96 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -124,8 +124,10 @@ static int add_interval(struct resource_map *map, u_long base, u_long num) struct resource_map *p, *q; for (p = map; ; p = p->next) { - if ((p != map) && (p->base+p->num-1 >= base)) - return -1; + if ((p != map) && (p->base+p->num >= base)) { + p->num = max(num + base - p->base, p->num); + return 0; + } if ((p->next == map) || (p->next->base > base+num-1)) break; } -- cgit v1.2.3 From 7b4884ca8853a638df0eb5d251d80d67777b8b1a Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 17 Feb 2010 16:25:53 +0100 Subject: pcmcia: validate late-added resources Currently, only those mem resources are validated which are already registered at the time the first PCMCIA card is inserted. As we can only validate resources immediately after card insert, store "registered" mem resources in mem_db, and only upon validation move them to mem_db_valid. When allocationg mem resources, mem_db_valid is then preferred to mem_db. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/rsrc_nonstatic.c | 67 +++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 19 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index a06881c3b96..a69eed6c5b9 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -55,8 +55,8 @@ struct resource_map { struct socket_data { struct resource_map mem_db; + struct resource_map mem_db_valid; struct resource_map io_db; - unsigned int rsrc_mem_probe; }; #define MEM_PROBE_LOW (1 << 0) @@ -357,6 +357,7 @@ static int do_validate_mem(struct pcmcia_socket *s, struct resource *res, unsigned int *value)) { + struct socket_data *s_data = s->resource_data; struct resource *res1, *res2; unsigned int info1 = 1, info2 = 1; int ret = -EINVAL; @@ -382,6 +383,12 @@ static int do_validate_mem(struct pcmcia_socket *s, if ((ret) || (info1 != info2) || (info1 == 0)) return -EINVAL; + if (validate && !s->fake_cis) { + /* move it to the validated data set */ + add_interval(&s_data->mem_db_valid, base, size); + sub_interval(&s_data->mem_db, base, size); + } + return 0; } @@ -491,6 +498,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) if (probe_mask & MEM_PROBE_HIGH) { if (inv_probe(s_data->mem_db.next, s) > 0) return 0; + if (s_data->mem_db_valid.next != &s_data->mem_db_valid) + return 0; dev_printk(KERN_NOTICE, &s->dev, "cs: warning: no high memory space available!\n"); return -ENODEV; @@ -565,21 +574,18 @@ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) { struct socket_data *s_data = s->resource_data; unsigned int probe_mask = MEM_PROBE_LOW; - int ret = 0; + int ret; - if (!probe_mem) + if (!probe_mem || !(s->state & SOCKET_PRESENT)) return 0; if (s->features & SS_CAP_PAGE_REGS) probe_mask = MEM_PROBE_HIGH; - if (probe_mask & ~s_data->rsrc_mem_probe) { - if (s->state & SOCKET_PRESENT) { - ret = validate_mem(s, probe_mask); - if (!ret) - s_data->rsrc_mem_probe |= probe_mask; - } - } + ret = validate_mem(s, probe_mask); + + if (s_data->mem_db_valid.next != &s_data->mem_db_valid) + return 0; return ret; } @@ -723,15 +729,15 @@ static struct resource *nonstatic_find_mem_region(u_long base, u_long num, struct socket_data *s_data = s->resource_data; struct pcmcia_align_data data; unsigned long min, max; - int ret, i; + int ret, i, j; low = low || !(s->features & SS_CAP_PAGE_REGS); data.mask = align - 1; data.offset = base & data.mask; - data.map = &s_data->mem_db; for (i = 0; i < 2; i++) { + data.map = &s_data->mem_db_valid; if (low) { max = 0x100000UL; min = base < max ? base : 0; @@ -740,15 +746,23 @@ static struct resource *nonstatic_find_mem_region(u_long base, u_long num, min = 0x100000UL + base; } + for (j = 0; j < 2; j++) { #ifdef CONFIG_PCI - if (s->cb_dev) { - ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, - 1, min, 0, - pcmcia_align, &data); - } else + if (s->cb_dev) { + ret = pci_bus_alloc_resource(s->cb_dev->bus, + res, num, 1, min, 0, + pcmcia_align, &data); + } else #endif - ret = allocate_resource(&iomem_resource, res, num, min, - max, 1, pcmcia_align, &data); + { + ret = allocate_resource(&iomem_resource, + res, num, min, max, 1, + pcmcia_align, &data); + } + if (ret == 0) + break; + data.map = &s_data->mem_db; + } if (ret == 0 || low) break; low = 1; @@ -901,6 +915,7 @@ static int nonstatic_init(struct pcmcia_socket *s) return -ENOMEM; data->mem_db.next = &data->mem_db; + data->mem_db_valid.next = &data->mem_db_valid; data->io_db.next = &data->io_db; s->resource_data = (void *) data; @@ -915,6 +930,10 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s) struct socket_data *data = s->resource_data; struct resource_map *p, *q; + for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) { + q = p->next; + kfree(p); + } for (p = data->mem_db.next; p != &data->mem_db; p = q) { q = p->next; kfree(p); @@ -1010,6 +1029,16 @@ static ssize_t show_mem_db(struct device *dev, mutex_lock(&s->ops_mutex); data = s->resource_data; + for (p = data->mem_db_valid.next; p != &data->mem_db_valid; + p = p->next) { + if (ret > (PAGE_SIZE - 10)) + continue; + ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1), + "0x%08lx - 0x%08lx\n", + ((unsigned long) p->base), + ((unsigned long) p->base + p->num - 1)); + } + for (p = data->mem_db.next; p != &data->mem_db; p = p->next) { if (ret > (PAGE_SIZE - 10)) continue; -- cgit v1.2.3