Remove the racy try_irq/check_irq/undo_irq interface, and try to register the correct handler directly in pcmcia_request_irq(). Also, simplify the IRQ usage database, but avoid using the same IRQ twice. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/cs.c | 131 +++++++++++++++++++++++++++---------------- drivers/pcmcia/cs_internal.h | 2 drivers/pcmcia/rsrc_mgr.c | 118 -------------------------------------- 3 files changed, 84 insertions(+), 167 deletions(-) Index: 2.6.11-rc1/drivers/pcmcia/cs.c =================================================================== --- 2.6.11-rc1.orig/drivers/pcmcia/cs.c 2005-01-12 11:36:37.000000000 +0100 +++ 2.6.11-rc1/drivers/pcmcia/cs.c 2005-01-12 11:56:32.000000000 +0100 @@ -118,6 +118,11 @@ EXPORT_SYMBOL(pcmcia_socket_list_rwsem); +#ifdef CONFIG_PCMCIA_PROBE +/* mask ofIRQs already reserved by other cards, we should avoid using them */ +static u8 pcmcia_used_irq[NR_IRQS]; +#endif + /*==================================================================== Low-level PC Card interface drivers need to register with Card @@ -1297,10 +1302,9 @@ } #ifdef CONFIG_PCMCIA_PROBE - if (req->AssignedIRQ != s->pci_irq) - undo_irq(req->Attributes, req->AssignedIRQ); + pcmcia_used_irq[req->AssignedIRQ]--; #endif - + return CS_SUCCESS; } /* cs_release_irq */ @@ -1532,65 +1536,96 @@ ======================================================================*/ +#ifdef CONFIG_PCMCIA_PROBE +static irqreturn_t test_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + return IRQ_NONE; +} +#endif + int pcmcia_request_irq(client_handle_t handle, irq_req_t *req) { - struct pcmcia_socket *s; - config_t *c; - int ret = CS_IN_USE, irq = 0; - struct pcmcia_device *p_dev = handle_to_pdev(handle); - - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - c = CONFIG(handle); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - if (c->state & CONFIG_IRQ_REQ) - return CS_IN_USE; + struct pcmcia_socket *s; + config_t *c; + int ret = CS_IN_USE, irq = 0; + struct pcmcia_device *p_dev = handle_to_pdev(handle); + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + if (c->state & CONFIG_IRQ_REQ) + return CS_IN_USE; #ifdef CONFIG_PCMCIA_PROBE if (s->irq.AssignedIRQ != 0) { /* If the interrupt is already assigned, it must be the same */ irq = s->irq.AssignedIRQ; } else { - u_int try, mask = s->irq_mask; - for (try = 0; try < 2; try++) { - for (irq = 0; irq < 32; irq++) { - if ((mask >> irq) & 1) { - ret = try_irq(req->Attributes, irq, try); - if (!ret) - break; - } - } - if (!ret) + int try; + u32 mask = s->irq_mask; + void *data = NULL; + + for (try = 0; try < 64; try++) { + irq = try % 32; + + /* marked as available by driver, and not blocked by userspace? */ + if (!((mask >> irq) & 1)) + continue; + + /* avoid an IRQ which is already used by a PCMCIA card */ + if ((try < 32) && pcmcia_used_irq[irq]) + continue; + + /* register the correct driver, if possible, of check whether + * registering a dummy handle works, i.e. if the IRQ isn't + * marked as used by the kernel resource management core */ + ret = request_irq(irq, + (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Handler : test_action, + ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || + (s->functions > 1) || + (irq == s->pci_irq)) ? SA_SHIRQ : 0, + p_dev->dev.bus_id, + (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Instance : data); + if (!ret) { + if (!(req->Attributes & IRQ_HANDLE_PRESENT)) + free_irq(irq, data); break; + } } } #endif - if (ret != 0) { - if (!s->pci_irq) - return ret; - irq = s->pci_irq; - } + if (ret) { + if (!s->pci_irq) + return ret; + irq = s->pci_irq; + } + + if (ret && req->Attributes & IRQ_HANDLE_PRESENT) { + if (request_irq(irq, req->Handler, + ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || + (s->functions > 1) || + (irq == s->pci_irq)) ? SA_SHIRQ : 0, + p_dev->dev.bus_id, req->Instance)) + return CS_IN_USE; + } + + c->irq.Attributes = req->Attributes; + s->irq.AssignedIRQ = req->AssignedIRQ = irq; + s->irq.Config++; - if (req->Attributes & IRQ_HANDLE_PRESENT) { - if (request_irq(irq, req->Handler, - ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || - (s->functions > 1) || - (irq == s->pci_irq)) ? SA_SHIRQ : 0, - p_dev->dev.bus_id, req->Instance)) - return CS_IN_USE; - } + c->state |= CONFIG_IRQ_REQ; + handle->state |= CLIENT_IRQ_REQ; - c->irq.Attributes = req->Attributes; - s->irq.AssignedIRQ = req->AssignedIRQ = irq; - s->irq.Config++; - - c->state |= CONFIG_IRQ_REQ; - handle->state |= CLIENT_IRQ_REQ; - return CS_SUCCESS; +#ifdef CONFIG_PCMCIA_PROBE + pcmcia_used_irq[irq]++; +#endif + + return CS_SUCCESS; } /* pcmcia_request_irq */ /*====================================================================== Index: 2.6.11-rc1/drivers/pcmcia/cs_internal.h =================================================================== --- 2.6.11-rc1.orig/drivers/pcmcia/cs_internal.h 2005-01-12 10:49:03.000000000 +0100 +++ 2.6.11-rc1/drivers/pcmcia/cs_internal.h 2005-01-12 11:38:04.000000000 +0100 @@ -140,8 +140,6 @@ unsigned long r_end, struct pcmcia_socket *s); struct resource *find_mem_region(u_long base, u_long num, u_long align, int low, struct pcmcia_socket *s); -int try_irq(u_int Attributes, int irq, int specific); -void undo_irq(u_int Attributes, int irq); int adjust_resource_info(client_handle_t handle, adjust_t *adj); void release_resource_db(struct pcmcia_socket *s); Index: 2.6.11-rc1/drivers/pcmcia/rsrc_mgr.c =================================================================== --- 2.6.11-rc1.orig/drivers/pcmcia/rsrc_mgr.c 2005-01-12 11:37:04.000000000 +0100 +++ 2.6.11-rc1/drivers/pcmcia/rsrc_mgr.c 2005-01-12 11:38:04.000000000 +0100 @@ -34,122 +34,6 @@ #include #include "cs_internal.h" -static DECLARE_MUTEX(rsrc_sem); - -#ifdef CONFIG_PCMCIA_PROBE - -typedef struct irq_info_t { - u_int Attributes; - int time_share, dyn_share; - struct pcmcia_socket *Socket; -} irq_info_t; - -/* Table of IRQ assignments */ -static irq_info_t irq_table[NR_IRQS]; - -#endif - - -/*====================================================================== - - This checks to see if an interrupt is available, with support - for interrupt sharing. We don't support reserving interrupts - yet. If the interrupt is available, we allocate it. - -======================================================================*/ - -#ifdef CONFIG_PCMCIA_PROBE - -static irqreturn_t fake_irq(int i, void *d, struct pt_regs *r) { return IRQ_NONE; } -static inline int check_irq(int irq) -{ - if (request_irq(irq, fake_irq, 0, "bogus", NULL) != 0) - return -1; - free_irq(irq, NULL); - return 0; -} - -int try_irq(u_int Attributes, int irq, int specific) -{ - irq_info_t *info = &irq_table[irq]; - int ret = 0; - - down(&rsrc_sem); - if (info->Attributes & RES_ALLOCATED) { - switch (Attributes & IRQ_TYPE) { - case IRQ_TYPE_EXCLUSIVE: - ret = CS_IN_USE; - break; - case IRQ_TYPE_DYNAMIC_SHARING: - if ((info->Attributes & RES_IRQ_TYPE) - != RES_IRQ_TYPE_DYNAMIC) { - ret = CS_IN_USE; - break; - } - if (Attributes & IRQ_FIRST_SHARED) { - ret = CS_BAD_ATTRIBUTE; - break; - } - info->Attributes |= RES_IRQ_TYPE_DYNAMIC | RES_ALLOCATED; - info->dyn_share++; - break; - } - } else { - if ((info->Attributes & RES_RESERVED) && !specific) { - ret = CS_IN_USE; - goto out; - } - if (check_irq(irq) != 0) { - ret = CS_IN_USE; - goto out; - } - switch (Attributes & IRQ_TYPE) { - case IRQ_TYPE_EXCLUSIVE: - info->Attributes |= RES_ALLOCATED; - break; - case IRQ_TYPE_DYNAMIC_SHARING: - if (!(Attributes & IRQ_FIRST_SHARED)) { - ret = CS_BAD_ATTRIBUTE; - break; - } - info->Attributes |= RES_IRQ_TYPE_DYNAMIC | RES_ALLOCATED; - info->dyn_share = 1; - break; - } - } - out: - up(&rsrc_sem); - return ret; -} - -#endif - -/*====================================================================*/ - -#ifdef CONFIG_PCMCIA_PROBE - -void undo_irq(u_int Attributes, int irq) -{ - irq_info_t *info; - - info = &irq_table[irq]; - down(&rsrc_sem); - switch (Attributes & IRQ_TYPE) { - case IRQ_TYPE_EXCLUSIVE: - info->Attributes &= RES_RESERVED; - break; - case IRQ_TYPE_DYNAMIC_SHARING: - info->dyn_share--; - if (info->dyn_share == 0) - info->Attributes &= RES_RESERVED; - break; - } - up(&rsrc_sem); -} - -#endif - -/*====================================================================*/ #ifdef CONFIG_PCMCIA_PROBE @@ -167,7 +51,7 @@ mask = 1 << irq; - if !(s->irq_mask & mask) + if (!(s->irq_mask & mask)) return 0; s->irq_mask &= ~mask;