handler.
*/
-static int avmcs_config(struct pcmcia_device *link);
-static void avmcs_release(struct pcmcia_device *link);
+static void avmcs_config(dev_link_t *link);
+static void avmcs_release(dev_link_t *link);
/*
The attach() and detach() entry points are used to create and destroy
/*
A linked list of "instances" of the skeleton device. Each actual
PCMCIA card corresponds to one device instance, and is described
- by one struct pcmcia_device structure (defined in ds.h).
+ by one dev_link_t structure (defined in ds.h).
You may not want to use a linked list for this -- for example, the
- memory card driver uses an array of struct pcmcia_device pointers, where minor
+ memory card driver uses an array of dev_link_t pointers, where minor
device numbers are used to derive the corresponding array index.
*/
example, ethernet cards, modems). In other cases, there may be
many actual or logical devices (SCSI adapters, memory cards with
multiple partitions). The dev_node_t structures need to be kept
- in a linked list starting at the 'dev' field of a struct pcmcia_device
+ in a linked list starting at the 'dev' field of a dev_link_t
structure. We allocate them in the card's private data structure,
because they generally can't be allocated dynamically.
*/
======================================================================*/
-static int avmcs_probe(struct pcmcia_device *p_dev)
+static int avmcs_attach(struct pcmcia_device *p_dev)
{
+ dev_link_t *link;
local_info_t *local;
+ /* Initialize the dev_link_t structure */
+ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+ if (!link)
+ goto err;
+ memset(link, 0, sizeof(struct dev_link_t));
+
/* The io structure describes IO port mapping */
- p_dev->io.NumPorts1 = 16;
- p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- p_dev->io.NumPorts2 = 0;
+ link->io.NumPorts1 = 16;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.NumPorts2 = 0;
/* Interrupt setup */
- p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
- p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
-
- p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
/* General socket configuration */
- p_dev->conf.Attributes = CONF_ENABLE_IRQ;
- p_dev->conf.IntType = INT_MEMORY_AND_IO;
- p_dev->conf.ConfigIndex = 1;
- p_dev->conf.Present = PRESENT_OPTION;
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ link->conf.ConfigIndex = 1;
+ link->conf.Present = PRESENT_OPTION;
/* Allocate space for private device-specific data */
local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
if (!local)
- goto err;
+ goto err_kfree;
memset(local, 0, sizeof(local_info_t));
- p_dev->priv = local;
+ link->priv = local;
- return avmcs_config(p_dev);
+ link->handle = p_dev;
+ p_dev->instance = link;
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ avmcs_config(link);
+
+ return 0;
+
+ err_kfree:
+ kfree(link);
err:
- return -ENOMEM;
+ return -EINVAL;
} /* avmcs_attach */
/*======================================================================
======================================================================*/
-static void avmcs_detach(struct pcmcia_device *link)
+static void avmcs_detach(struct pcmcia_device *p_dev)
{
+ dev_link_t *link = dev_to_instance(p_dev);
+
+ if (link->state & DEV_CONFIG)
avmcs_release(link);
- kfree(link->priv);
+
+ kfree(link->priv);
+ kfree(link);
} /* avmcs_detach */
/*======================================================================
======================================================================*/
-static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple,
+static int get_tuple(client_handle_t handle, tuple_t *tuple,
cisparse_t *parse)
{
int i = pcmcia_get_tuple_data(handle, tuple);
return pcmcia_parse_tuple(handle, tuple, parse);
}
-static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
+static int first_tuple(client_handle_t handle, tuple_t *tuple,
cisparse_t *parse)
{
int i = pcmcia_get_first_tuple(handle, tuple);
return get_tuple(handle, tuple, parse);
}
-static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
+static int next_tuple(client_handle_t handle, tuple_t *tuple,
cisparse_t *parse)
{
int i = pcmcia_get_next_tuple(handle, tuple);
return get_tuple(handle, tuple, parse);
}
-static int avmcs_config(struct pcmcia_device *link)
+static void avmcs_config(dev_link_t *link)
{
+ client_handle_t handle;
tuple_t tuple;
cisparse_t parse;
cistpl_cftable_entry_t *cf = &parse.cftable_entry;
char devname[128];
int cardtype;
int (*addcard)(unsigned int port, unsigned irq);
-
+
+ handle = link->handle;
dev = link->priv;
/*
*/
do {
tuple.DesiredTuple = CISTPL_CONFIG;
- i = pcmcia_get_first_tuple(link, &tuple);
+ i = pcmcia_get_first_tuple(handle, &tuple);
if (i != CS_SUCCESS) break;
tuple.TupleData = buf;
tuple.TupleDataMax = 64;
tuple.TupleOffset = 0;
- i = pcmcia_get_tuple_data(link, &tuple);
+ i = pcmcia_get_tuple_data(handle, &tuple);
if (i != CS_SUCCESS) break;
- i = pcmcia_parse_tuple(link, &tuple, &parse);
+ i = pcmcia_parse_tuple(handle, &tuple, &parse);
if (i != CS_SUCCESS) break;
link->conf.ConfigBase = parse.config.base;
} while (0);
if (i != CS_SUCCESS) {
- cs_error(link, ParseTuple, i);
- return -ENODEV;
+ cs_error(link->handle, ParseTuple, i);
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
}
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
do {
tuple.DesiredTuple = CISTPL_VERS_1;
devname[0] = 0;
- if( !first_tuple(link, &tuple, &parse) && parse.version_1.ns > 1 ) {
+ if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) {
strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1],
sizeof(devname));
}
tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
tuple.Attributes = 0;
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- i = first_tuple(link, &tuple, &parse);
+ i = first_tuple(handle, &tuple, &parse);
while (i == CS_SUCCESS) {
if (cf->io.nwin > 0) {
link->conf.ConfigIndex = cf->index;
printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
link->io.BasePort1,
link->io.BasePort1+link->io.NumPorts1-1);
- i = pcmcia_request_io(link, &link->io);
+ i = pcmcia_request_io(link->handle, &link->io);
if (i == CS_SUCCESS) goto found_port;
}
- i = next_tuple(link, &tuple, &parse);
+ i = next_tuple(handle, &tuple, &parse);
}
found_port:
if (i != CS_SUCCESS) {
- cs_error(link, RequestIO, i);
+ cs_error(link->handle, RequestIO, i);
break;
}
-
+
/*
* allocate an interrupt line
*/
- i = pcmcia_request_irq(link, &link->irq);
+ i = pcmcia_request_irq(link->handle, &link->irq);
if (i != CS_SUCCESS) {
- cs_error(link, RequestIRQ, i);
- /* undo */
- pcmcia_disable_device(link);
+ cs_error(link->handle, RequestIRQ, i);
+ pcmcia_release_io(link->handle, &link->io);
break;
}
-
+
/*
* configure the PCMCIA socket
*/
- i = pcmcia_request_configuration(link, &link->conf);
+ i = pcmcia_request_configuration(link->handle, &link->conf);
if (i != CS_SUCCESS) {
- cs_error(link, RequestConfiguration, i);
- pcmcia_disable_device(link);
+ cs_error(link->handle, RequestConfiguration, i);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
break;
}
dev->node.major = 64;
dev->node.minor = 0;
- link->dev_node = &dev->node;
-
+ link->dev = &dev->node;
+
+ link->state &= ~DEV_CONFIG_PENDING;
/* If any step failed, release any partially configured state */
if (i != 0) {
avmcs_release(link);
- return -ENODEV;
+ return;
}
printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n",
dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ);
avmcs_release(link);
- return -ENODEV;
+ return;
}
dev->node.minor = i;
- return 0;
} /* avmcs_config */
======================================================================*/
-static void avmcs_release(struct pcmcia_device *link)
+static void avmcs_release(dev_link_t *link)
{
- b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ);
- pcmcia_disable_device(link);
+ b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ);
+
+ /* Unlink the device chain */
+ link->dev = NULL;
+
+ /* Don't bother checking to see if these succeed or not */
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
+ link->state &= ~DEV_CONFIG;
} /* avmcs_release */
+static int avmcs_suspend(struct pcmcia_device *dev)
+{
+ dev_link_t *link = dev_to_instance(dev);
+
+ link->state |= DEV_SUSPEND;
+ if (link->state & DEV_CONFIG)
+ pcmcia_release_configuration(link->handle);
+
+ return 0;
+}
+
+static int avmcs_resume(struct pcmcia_device *dev)
+{
+ dev_link_t *link = dev_to_instance(dev);
+
+ link->state &= ~DEV_SUSPEND;
+ if (link->state & DEV_CONFIG)
+ pcmcia_request_configuration(link->handle, &link->conf);
+
+ return 0;
+}
+
+/*======================================================================
+
+ The card status event handler. Mostly, this schedules other
+ stuff to run after an event is received. A CARD_REMOVAL event
+ also sets some flags to discourage the net drivers from trying
+ to talk to the card any more.
+
+ When a CARD_REMOVAL event is received, we immediately set a flag
+ to block future accesses to this device. All the functions that
+ actually access the device should check this flag to make sure
+ the card is still present.
+
+======================================================================*/
+
static struct pcmcia_device_id avmcs_ids[] = {
PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
.drv = {
.name = "avm_cs",
},
- .probe = avmcs_probe,
+ .probe = avmcs_attach,
.remove = avmcs_detach,
.id_table = avmcs_ids,
+ .suspend= avmcs_suspend,
+ .resume = avmcs_resume,
};
static int __init avmcs_init(void)