Commit b1e3e2f0 authored by Dominik Brodowski's avatar Dominik Brodowski Committed by Linus Torvalds

[PATCH] pcmcia: remove racy try_irq()

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: default avatarDominik Brodowski <linux@brodo.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent b6c2f23b
......@@ -117,6 +117,11 @@ EXPORT_SYMBOL(pcmcia_socket_list);
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
......@@ -1295,10 +1300,9 @@ int pcmcia_release_irq(client_handle_t handle, irq_req_t *req)
}
#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 */
......@@ -1530,65 +1534,96 @@ int pcmcia_request_io(client_handle_t handle, io_req_t *req)
======================================================================*/
#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 (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;
}
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++;
c->state |= CONFIG_IRQ_REQ;
handle->state |= CLIENT_IRQ_REQ;
return CS_SUCCESS;
c->irq.Attributes = req->Attributes;
s->irq.AssignedIRQ = req->AssignedIRQ = irq;
s->irq.Config++;
c->state |= CONFIG_IRQ_REQ;
handle->state |= CLIENT_IRQ_REQ;
#ifdef CONFIG_PCMCIA_PROBE
pcmcia_used_irq[irq]++;
#endif
return CS_SUCCESS;
} /* pcmcia_request_irq */
/*======================================================================
......
......@@ -140,8 +140,6 @@ int adjust_io_region(struct resource *res, unsigned long r_start,
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);
......
......@@ -34,122 +34,6 @@
#include <pcmcia/cistpl.h>
#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 @@ static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
mask = 1 << irq;
if !(s->irq_mask & mask)
if (!(s->irq_mask & mask))
return 0;
s->irq_mask &= ~mask;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment