[PATCH] pcmcia: make config_t independent, add reference counting
Handle config_t structs independent of struct pcmcia_socket, and add reference counting for them. Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
This commit is contained in:
parent
855cdf134d
commit
360b65b95b
|
@ -406,8 +406,6 @@ static void socket_shutdown(struct pcmcia_socket *s)
|
||||||
cb_free(s);
|
cb_free(s);
|
||||||
#endif
|
#endif
|
||||||
s->functions = 0;
|
s->functions = 0;
|
||||||
kfree(s->config);
|
|
||||||
s->config = NULL;
|
|
||||||
|
|
||||||
s->ops->get_status(s, &status);
|
s->ops->get_status(s, &status);
|
||||||
if (status & SS_POWERON) {
|
if (status & SS_POWERON) {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#define _LINUX_CS_INTERNAL_H
|
#define _LINUX_CS_INTERNAL_H
|
||||||
|
|
||||||
#include <linux/config.h>
|
#include <linux/config.h>
|
||||||
|
#include <linux/kref.h>
|
||||||
|
|
||||||
/* Flags in client state */
|
/* Flags in client state */
|
||||||
#define CLIENT_CONFIG_LOCKED 0x0001
|
#define CLIENT_CONFIG_LOCKED 0x0001
|
||||||
|
@ -40,6 +41,7 @@ typedef struct region_t {
|
||||||
|
|
||||||
/* Each card function gets one of these guys */
|
/* Each card function gets one of these guys */
|
||||||
typedef struct config_t {
|
typedef struct config_t {
|
||||||
|
struct kref ref;
|
||||||
u_int state;
|
u_int state;
|
||||||
u_int Attributes;
|
u_int Attributes;
|
||||||
u_int IntType;
|
u_int IntType;
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/crc32.h>
|
#include <linux/crc32.h>
|
||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
|
#include <linux/kref.h>
|
||||||
|
|
||||||
#define IN_CARD_SERVICES
|
#define IN_CARD_SERVICES
|
||||||
#include <pcmcia/cs_types.h>
|
#include <pcmcia/cs_types.h>
|
||||||
|
@ -343,12 +344,19 @@ void pcmcia_put_dev(struct pcmcia_device *p_dev)
|
||||||
put_device(&p_dev->dev);
|
put_device(&p_dev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pcmcia_release_function(struct kref *ref)
|
||||||
|
{
|
||||||
|
struct config_t *c = container_of(ref, struct config_t, ref);
|
||||||
|
kfree(c);
|
||||||
|
}
|
||||||
|
|
||||||
static void pcmcia_release_dev(struct device *dev)
|
static void pcmcia_release_dev(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
|
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
|
||||||
ds_dbg(1, "releasing dev %p\n", p_dev);
|
ds_dbg(1, "releasing dev %p\n", p_dev);
|
||||||
pcmcia_put_socket(p_dev->socket);
|
pcmcia_put_socket(p_dev->socket);
|
||||||
kfree(p_dev->devname);
|
kfree(p_dev->devname);
|
||||||
|
kref_put(&p_dev->function_config->ref, pcmcia_release_function);
|
||||||
kfree(p_dev);
|
kfree(p_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,30 +385,14 @@ static int pcmcia_device_probe(struct device * dev)
|
||||||
p_drv = to_pcmcia_drv(dev->driver);
|
p_drv = to_pcmcia_drv(dev->driver);
|
||||||
s = p_dev->socket;
|
s = p_dev->socket;
|
||||||
|
|
||||||
if ((!p_drv->probe) || (!try_module_get(p_drv->owner))) {
|
if ((!p_drv->probe) || (!p_dev->function_config) ||
|
||||||
|
(!try_module_get(p_drv->owner))) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto put_dev;
|
goto put_dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_dev->state &= ~CLIENT_UNBOUND;
|
p_dev->state &= ~CLIENT_UNBOUND;
|
||||||
|
|
||||||
/* set up the device configuration, if it hasn't been done before */
|
|
||||||
if (!s->functions) {
|
|
||||||
cistpl_longlink_mfc_t mfc;
|
|
||||||
if (pccard_read_tuple(s, p_dev->func, CISTPL_LONGLINK_MFC,
|
|
||||||
&mfc) == CS_SUCCESS)
|
|
||||||
s->functions = mfc.nfn;
|
|
||||||
else
|
|
||||||
s->functions = 1;
|
|
||||||
s->config = kzalloc(sizeof(config_t) * s->functions,
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!s->config) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto put_module;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p_dev->function_config = &s->config[p_dev->func];
|
|
||||||
|
|
||||||
ret = p_drv->probe(p_dev);
|
ret = p_drv->probe(p_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto put_module;
|
goto put_module;
|
||||||
|
@ -576,7 +568,7 @@ static DECLARE_MUTEX(device_add_lock);
|
||||||
|
|
||||||
struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
|
struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
|
||||||
{
|
{
|
||||||
struct pcmcia_device *p_dev;
|
struct pcmcia_device *p_dev, *tmp_dev;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int bus_id_len;
|
int bus_id_len;
|
||||||
|
|
||||||
|
@ -597,6 +589,8 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
|
||||||
p_dev->socket = s;
|
p_dev->socket = s;
|
||||||
p_dev->device_no = (s->device_count++);
|
p_dev->device_no = (s->device_count++);
|
||||||
p_dev->func = function;
|
p_dev->func = function;
|
||||||
|
if (s->functions < function)
|
||||||
|
s->functions = function;
|
||||||
|
|
||||||
p_dev->dev.bus = &pcmcia_bus_type;
|
p_dev->dev.bus = &pcmcia_bus_type;
|
||||||
p_dev->dev.parent = s->dev.dev;
|
p_dev->dev.parent = s->dev.dev;
|
||||||
|
@ -611,28 +605,50 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
|
||||||
/* compat */
|
/* compat */
|
||||||
p_dev->state = CLIENT_UNBOUND;
|
p_dev->state = CLIENT_UNBOUND;
|
||||||
|
|
||||||
/* Add to the list in pcmcia_bus_socket */
|
|
||||||
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
|
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* p_dev->function_config must be the same for all card functions.
|
||||||
|
* Note that this is serialized by the device_add_lock, so that
|
||||||
|
* only one such struct will be created.
|
||||||
|
*/
|
||||||
|
list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list)
|
||||||
|
if (p_dev->func == tmp_dev->func) {
|
||||||
|
p_dev->function_config = tmp_dev->function_config;
|
||||||
|
kref_get(&p_dev->function_config->ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add to the list in pcmcia_bus_socket */
|
||||||
list_add_tail(&p_dev->socket_device_list, &s->devices_list);
|
list_add_tail(&p_dev->socket_device_list, &s->devices_list);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
|
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
|
||||||
|
|
||||||
|
if (!p_dev->function_config) {
|
||||||
|
p_dev->function_config = kzalloc(sizeof(struct config_t),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!p_dev->function_config)
|
||||||
|
goto err_unreg;
|
||||||
|
kref_init(&p_dev->function_config->ref);
|
||||||
|
}
|
||||||
|
|
||||||
printk(KERN_NOTICE "pcmcia: registering new device %s\n",
|
printk(KERN_NOTICE "pcmcia: registering new device %s\n",
|
||||||
p_dev->devname);
|
p_dev->devname);
|
||||||
|
|
||||||
pcmcia_device_query(p_dev);
|
pcmcia_device_query(p_dev);
|
||||||
|
|
||||||
if (device_register(&p_dev->dev)) {
|
if (device_register(&p_dev->dev))
|
||||||
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
|
goto err_unreg;
|
||||||
list_del(&p_dev->socket_device_list);
|
|
||||||
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
|
|
||||||
|
|
||||||
goto err_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
up(&device_add_lock);
|
up(&device_add_lock);
|
||||||
|
|
||||||
return p_dev;
|
return p_dev;
|
||||||
|
|
||||||
|
err_unreg:
|
||||||
|
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
|
||||||
|
list_del(&p_dev->socket_device_list);
|
||||||
|
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
|
||||||
|
|
||||||
err_free:
|
err_free:
|
||||||
kfree(p_dev->devname);
|
kfree(p_dev->devname);
|
||||||
kfree(p_dev);
|
kfree(p_dev);
|
||||||
|
|
|
@ -186,7 +186,6 @@ struct pcmcia_socket {
|
||||||
u_short lock_count;
|
u_short lock_count;
|
||||||
pccard_mem_map cis_mem;
|
pccard_mem_map cis_mem;
|
||||||
void __iomem *cis_virt;
|
void __iomem *cis_virt;
|
||||||
struct config_t *config;
|
|
||||||
struct {
|
struct {
|
||||||
u_int AssignedIRQ;
|
u_int AssignedIRQ;
|
||||||
u_int Config;
|
u_int Config;
|
||||||
|
|
Loading…
Reference in New Issue