teles_cs.c 8.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
/* $Id: teles_cs.c,v 1.1.2.2 2004/01/25 15:07:06 keil Exp $ */
/*======================================================================

    A teles S0 PCMCIA client driver

    Based on skeleton by David Hinds, dhinds@allegro.stanford.edu
    Written by Christof Petig, christof.petig@wtal.de
    
    Also inspired by ELSA PCMCIA driver 
    by Klaus Lichtenwalder <Lichtenwalder@ACM.org>
    
    Extentions to new hisax_pcmcia by Karsten Keil

    minor changes to be compatible with kernel 2.4.x
    by Jan.Schubert@GMX.li

======================================================================*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/system.h>

#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include "hisax_cfg.h"

MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards");
MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de");
MODULE_LICENSE("GPL");


/*====================================================================*/

/* Parameters that can be set with 'insmod' */

static int protocol = 2;        /* EURO-ISDN Default */
module_param(protocol, int, 0);

/*====================================================================*/

/*
   The event() function is this driver's Card Services event handler.
   It will be called by Card Services when an appropriate card status
   event is received.  The config() and release() entry points are
   used to configure or release a socket, in response to card insertion
   and ejection events.  They are invoked from the teles_cs event
   handler.
*/

59
static int teles_cs_config(struct pcmcia_device *link) __devinit ;
60
static void teles_cs_release(struct pcmcia_device *link);
L
Linus Torvalds 已提交
61 62 63 64 65 66 67

/*
   The attach() and detach() entry points are used to create and destroy
   "instances" of the driver, where each instance represents everything
   needed to manage one actual PCMCIA card.
*/

68
static void teles_detach(struct pcmcia_device *p_dev) __devexit ;
L
Linus Torvalds 已提交
69 70

typedef struct local_info_t {
71
	struct pcmcia_device	*p_dev;
L
Linus Torvalds 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
    int                 busy;
    int			cardnr;
} local_info_t;

/*======================================================================

    teles_attach() creates an "instance" of the driver, allocatingx
    local data structures for one device.  The device is registered
    with Card Services.

    The dev_link structure is initialized, but we don't actually
    configure the card at this point -- we wait until we receive a
    card insertion event.

======================================================================*/

88
static int __devinit teles_probe(struct pcmcia_device *link)
L
Linus Torvalds 已提交
89 90 91
{
    local_info_t *local;

92
    dev_dbg(&link->dev, "teles_attach()\n");
L
Linus Torvalds 已提交
93 94

    /* Allocate space for private device-specific data */
95
    local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
96
    if (!local) return -ENOMEM;
L
Linus Torvalds 已提交
97
    local->cardnr = -1;
98

99
    local->p_dev = link;
100
    link->priv = local;
L
Linus Torvalds 已提交
101 102 103 104 105 106 107 108

    /*
      General socket configuration defaults can go here.  In this
      client, we assume very little, and rely on the CIS for almost
      everything.  In most clients, many details (i.e., number, sizes,
      and attributes of IO windows) are fixed by the nature of the
      device, and can be hard-wired here.
    */
109 110
    link->resource[0]->end = 96;
    link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
L
Linus Torvalds 已提交
111 112 113

    link->conf.Attributes = CONF_ENABLE_IRQ;

114
    return teles_cs_config(link);
L
Linus Torvalds 已提交
115 116 117 118 119 120 121 122 123 124 125
} /* teles_attach */

/*======================================================================

    This deletes a driver "instance".  The device is de-registered
    with Card Services.  If it has been released, all local data
    structures are freed.  Otherwise, the structures will be freed
    when the device is released.

======================================================================*/

126
static void __devexit teles_detach(struct pcmcia_device *link)
L
Linus Torvalds 已提交
127
{
128
	local_info_t *info = link->priv;
L
Linus Torvalds 已提交
129

130
	dev_dbg(&link->dev, "teles_detach(0x%p)\n", link);
L
Linus Torvalds 已提交
131

132 133
	info->busy = 1;
	teles_cs_release(link);
L
Linus Torvalds 已提交
134

135
	kfree(info);
L
Linus Torvalds 已提交
136 137 138 139 140 141 142 143 144 145
} /* teles_detach */

/*======================================================================

    teles_cs_config() is scheduled to run after a CARD_INSERTION event
    is received, to configure the PCMCIA socket, and to make the
    device available to the system.

======================================================================*/

146 147
static int teles_cs_configcheck(struct pcmcia_device *p_dev,
				cistpl_cftable_entry_t *cf,
148
				cistpl_cftable_entry_t *dflt,
149
				unsigned int vcc,
150
				void *priv_data)
L
Linus Torvalds 已提交
151
{
152 153
	int j;

154 155
	p_dev->io_lines = 5;

156 157
	if ((cf->io.nwin > 0) && cf->io.win[0].base) {
		printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
158 159
		p_dev->resource[0]->start = cf->io.win[0].base;
		if (!pcmcia_request_io(p_dev))
160 161 162 163
			return 0;
	} else {
		printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
		for (j = 0x2f0; j > 0x100; j -= 0x10) {
164 165
			p_dev->resource[0]->start = j;
			if (!pcmcia_request_io(p_dev))
166 167 168 169
				return 0;
		}
	}
	return -ENODEV;
L
Linus Torvalds 已提交
170 171
}

172
static int __devinit teles_cs_config(struct pcmcia_device *link)
L
Linus Torvalds 已提交
173 174
{
    local_info_t *dev;
175
    int i;
L
Linus Torvalds 已提交
176 177
    IsdnCard_t icard;

178
    dev_dbg(&link->dev, "teles_config(0x%p)\n", link);
L
Linus Torvalds 已提交
179 180
    dev = link->priv;

181
    i = pcmcia_loop_config(link, teles_cs_configcheck, NULL);
182
    if (i != 0)
L
Linus Torvalds 已提交
183 184
	goto cs_failed;

185
    if (!link->irq)
L
Linus Torvalds 已提交
186 187
        goto cs_failed;

188
    i = pcmcia_request_configuration(link, &link->conf);
189
    if (i != 0)
L
Linus Torvalds 已提交
190 191 192
      goto cs_failed;

    /* Finally, report what we've done */
193
    dev_info(&link->dev, "index 0x%02x:",
194
	    link->config_index);
L
Linus Torvalds 已提交
195
    if (link->conf.Attributes & CONF_ENABLE_IRQ)
196
	    printk(", irq %d", link->irq);
197 198 199 200
    if (link->resource[0])
	printk(" & %pR", link->resource[0]);
    if (link->resource[1])
	printk(" & %pR", link->resource[1]);
L
Linus Torvalds 已提交
201 202
    printk("\n");

203
    icard.para[0] = link->irq;
204
    icard.para[1] = link->resource[0]->start;
L
Linus Torvalds 已提交
205 206 207 208 209 210
    icard.protocol = protocol;
    icard.typ = ISDN_CTYPE_TELESPCMCIA;
    
    i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
    if (i < 0) {
    	printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
211
			i, (unsigned int) link->resource[0]->start);
L
Linus Torvalds 已提交
212
    	teles_cs_release(link);
213 214 215 216 217
	return -ENODEV;
    }

    ((local_info_t*)link->priv)->cardnr = i;
    return 0;
L
Linus Torvalds 已提交
218 219 220

cs_failed:
    teles_cs_release(link);
221
    return -ENODEV;
L
Linus Torvalds 已提交
222 223 224 225 226 227 228 229 230 231
} /* teles_cs_config */

/*======================================================================

    After a card is removed, teles_cs_release() will unregister the net
    device, and release the PCMCIA configuration.  If the device is
    still open, this will be postponed until it is closed.

======================================================================*/

232
static void teles_cs_release(struct pcmcia_device *link)
L
Linus Torvalds 已提交
233 234 235
{
    local_info_t *local = link->priv;

236
    dev_dbg(&link->dev, "teles_cs_release(0x%p)\n", link);
L
Linus Torvalds 已提交
237 238 239 240 241 242 243

    if (local) {
    	if (local->cardnr >= 0) {
    	    /* no unregister function with hisax */
	    HiSax_closecard(local->cardnr);
	}
    }
244

245
    pcmcia_disable_device(link);
L
Linus Torvalds 已提交
246 247
} /* teles_cs_release */

248
static int teles_suspend(struct pcmcia_device *link)
249 250 251 252 253 254 255 256
{
	local_info_t *dev = link->priv;

        dev->busy = 1;

	return 0;
}

257
static int teles_resume(struct pcmcia_device *link)
258 259 260 261 262 263 264 265
{
	local_info_t *dev = link->priv;

        dev->busy = 0;

	return 0;
}

L
Linus Torvalds 已提交
266

267 268 269 270 271 272
static struct pcmcia_device_id teles_ids[] = {
	PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119),
	PCMCIA_DEVICE_NULL,
};
MODULE_DEVICE_TABLE(pcmcia, teles_ids);

L
Linus Torvalds 已提交
273 274 275 276 277
static struct pcmcia_driver teles_cs_driver = {
	.owner		= THIS_MODULE,
	.drv		= {
		.name	= "teles_cs",
	},
278
	.probe		= teles_probe,
279
	.remove		= __devexit_p(teles_detach),
280
	.id_table       = teles_ids,
281 282
	.suspend	= teles_suspend,
	.resume		= teles_resume,
L
Linus Torvalds 已提交
283 284 285 286 287 288 289 290 291 292 293 294 295 296
};

static int __init init_teles_cs(void)
{
	return pcmcia_register_driver(&teles_cs_driver);
}

static void __exit exit_teles_cs(void)
{
	pcmcia_unregister_driver(&teles_cs_driver);
}

module_init(init_teles_cs);
module_exit(exit_teles_cs);