OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [parport/] [parport_cs.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*======================================================================
2
 
3
    A driver for PCMCIA parallel port adapters
4
 
5
    (specifically, for the Quatech SPP-100 EPP card: other cards will
6
    probably require driver tweaks)
7
 
8
    parport_cs.c 1.29 2002/10/11 06:57:41
9
 
10
    The contents of this file are subject to the Mozilla Public
11
    License Version 1.1 (the "License"); you may not use this file
12
    except in compliance with the License. You may obtain a copy of
13
    the License at http://www.mozilla.org/MPL/
14
 
15
    Software distributed under the License is distributed on an "AS
16
    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17
    implied. See the License for the specific language governing
18
    rights and limitations under the License.
19
 
20
    The initial developer of the original code is David A. Hinds
21
    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
22
    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
23
 
24
    Alternatively, the contents of this file may be used under the
25
    terms of the GNU General Public License version 2 (the "GPL"), in
26
    which case the provisions of the GPL are applicable instead of the
27
    above.  If you wish to allow the use of your version of this file
28
    only under the terms of the GPL and not to allow others to use
29
    your version of this file under the MPL, indicate your decision
30
    by deleting the provisions above and replace them with the notice
31
    and other provisions required by the GPL.  If you do not delete
32
    the provisions above, a recipient may use your version of this
33
    file under either the MPL or the GPL.
34
 
35
======================================================================*/
36
 
37
#include <linux/kernel.h>
38
#include <linux/module.h>
39
#include <linux/init.h>
40
#include <linux/sched.h>
41
#include <linux/ptrace.h>
42
#include <linux/slab.h>
43
#include <linux/string.h>
44
#include <linux/timer.h>
45
#include <linux/ioport.h>
46
 
47
#include <linux/parport.h>
48
#include <linux/parport_pc.h>
49
 
50
#include <pcmcia/version.h>
51
#include <pcmcia/cs_types.h>
52
#include <pcmcia/cs.h>
53
#include <pcmcia/cistpl.h>
54
#include <pcmcia/ds.h>
55
#include <pcmcia/cisreg.h>
56
#include <pcmcia/ciscode.h>
57
 
58
/*====================================================================*/
59
 
60
/* Module parameters */
61
 
62
MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
63
MODULE_DESCRIPTION("PCMCIA parallel port card driver");
64
MODULE_LICENSE("Dual MPL/GPL");
65
 
66
#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
67
 
68
/* Bit map of interrupts to choose from */
69
INT_MODULE_PARM(irq_mask, 0xdeb8);
70
static int irq_list[4] = { -1 };
71
MODULE_PARM(irq_list, "1-4i");
72
 
73
INT_MODULE_PARM(epp_mode, 1);
74
 
75
#ifdef PCMCIA_DEBUG
76
INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
77
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
78
static char *version =
79
"parport_cs.c 1.29 2002/10/11 06:57:41 (David Hinds)";
80
#else
81
#define DEBUG(n, args...)
82
#endif
83
 
84
/*====================================================================*/
85
 
86
#define FORCE_EPP_MODE  0x08
87
 
88
typedef struct parport_info_t {
89
    dev_link_t          link;
90
    int                 ndev;
91
    dev_node_t          node;
92
    struct parport      *port;
93
} parport_info_t;
94
 
95
static dev_link_t *parport_attach(void);
96
static void parport_detach(dev_link_t *);
97
static void parport_config(dev_link_t *link);
98
static void parport_cs_release(u_long arg);
99
static int parport_event(event_t event, int priority,
100
                         event_callback_args_t *args);
101
 
102
static dev_info_t dev_info = "parport_cs";
103
static dev_link_t *dev_list = NULL;
104
 
105
/*====================================================================*/
106
 
107
static void cs_error(client_handle_t handle, int func, int ret)
108
{
109
    error_info_t err = { func, ret };
110
    CardServices(ReportError, handle, &err);
111
}
112
 
113
/*======================================================================
114
 
115
    parport_attach() creates an "instance" of the driver, allocating
116
    local data structures for one device.  The device is registered
117
    with Card Services.
118
 
119
======================================================================*/
120
 
121
static dev_link_t *parport_attach(void)
122
{
123
    parport_info_t *info;
124
    dev_link_t *link;
125
    client_reg_t client_reg;
126
    int i, ret;
127
 
128
    DEBUG(0, "parport_attach()\n");
129
 
130
    /* Create new parport device */
131
    info = kmalloc(sizeof(*info), GFP_KERNEL);
132
    if (!info) return NULL;
133
    memset(info, 0, sizeof(*info));
134
    link = &info->link; link->priv = info;
135
 
136
    link->release.function = &parport_cs_release;
137
    link->release.data = (u_long)link;
138
    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
139
    link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
140
    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
141
    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
142
    if (irq_list[0] == -1)
143
        link->irq.IRQInfo2 = irq_mask;
144
    else
145
        for (i = 0; i < 4; i++)
146
            link->irq.IRQInfo2 |= 1 << irq_list[i];
147
    link->conf.Attributes = CONF_ENABLE_IRQ;
148
    link->conf.Vcc = 50;
149
    link->conf.IntType = INT_MEMORY_AND_IO;
150
 
151
    /* Register with Card Services */
152
    link->next = dev_list;
153
    dev_list = link;
154
    client_reg.dev_info = &dev_info;
155
    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
156
    client_reg.EventMask =
157
        CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
158
        CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
159
        CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
160
    client_reg.event_handler = &parport_event;
161
    client_reg.Version = 0x0210;
162
    client_reg.event_callback_args.client_data = link;
163
    ret = CardServices(RegisterClient, &link->handle, &client_reg);
164
    if (ret != CS_SUCCESS) {
165
        cs_error(link->handle, RegisterClient, ret);
166
        parport_detach(link);
167
        return NULL;
168
    }
169
 
170
    return link;
171
} /* parport_attach */
172
 
173
/*======================================================================
174
 
175
    This deletes a driver "instance".  The device is de-registered
176
    with Card Services.  If it has been released, all local data
177
    structures are freed.  Otherwise, the structures will be freed
178
    when the device is released.
179
 
180
======================================================================*/
181
 
182
static void parport_detach(dev_link_t *link)
183
{
184
    dev_link_t **linkp;
185
    int ret;
186
 
187
    DEBUG(0, "parport_detach(0x%p)\n", link);
188
 
189
    /* Locate device structure */
190
    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
191
        if (*linkp == link) break;
192
    if (*linkp == NULL)
193
        return;
194
 
195
    del_timer(&link->release);
196
    if (link->state & DEV_CONFIG)
197
        parport_cs_release((u_long)link);
198
 
199
    if (link->handle) {
200
        ret = CardServices(DeregisterClient, link->handle);
201
        if (ret != CS_SUCCESS)
202
            cs_error(link->handle, DeregisterClient, ret);
203
    }
204
 
205
    /* Unlink, free device structure */
206
    *linkp = link->next;
207
    kfree(link->priv);
208
 
209
} /* parport_detach */
210
 
211
/*======================================================================
212
 
213
    parport_config() is scheduled to run after a CARD_INSERTION event
214
    is received, to configure the PCMCIA socket, and to make the
215
    parport device available to the system.
216
 
217
======================================================================*/
218
 
219
#define CS_CHECK(fn, args...) \
220
while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
221
 
222
#define CFG_CHECK(fn, args...) \
223
if (CardServices(fn, args) != 0) goto next_entry
224
 
225
void parport_config(dev_link_t *link)
226
{
227
    client_handle_t handle = link->handle;
228
    parport_info_t *info = link->priv;
229
    tuple_t tuple;
230
    u_short buf[128];
231
    cisparse_t parse;
232
    config_info_t conf;
233
    cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
234
    cistpl_cftable_entry_t dflt = { 0 };
235
    struct parport *p;
236
    int last_ret, last_fn;
237
 
238
    DEBUG(0, "parport_config(0x%p)\n", link);
239
 
240
    tuple.TupleData = (cisdata_t *)buf;
241
    tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
242
    tuple.Attributes = 0;
243
    tuple.DesiredTuple = CISTPL_CONFIG;
244
    CS_CHECK(GetFirstTuple, handle, &tuple);
245
    CS_CHECK(GetTupleData, handle, &tuple);
246
    CS_CHECK(ParseTuple, handle, &tuple, &parse);
247
    link->conf.ConfigBase = parse.config.base;
248
    link->conf.Present = parse.config.rmask[0];
249
 
250
    /* Configure card */
251
    link->state |= DEV_CONFIG;
252
 
253
    /* Not sure if this is right... look up the current Vcc */
254
    CS_CHECK(GetConfigurationInfo, handle, &conf);
255
 
256
    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
257
    tuple.Attributes = 0;
258
    CS_CHECK(GetFirstTuple, handle, &tuple);
259
    while (1) {
260
        CFG_CHECK(GetTupleData, handle, &tuple);
261
        CFG_CHECK(ParseTuple, handle, &tuple, &parse);
262
 
263
        if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
264
            cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
265
            link->conf.ConfigIndex = cfg->index;
266
            if (epp_mode)
267
                link->conf.ConfigIndex |= FORCE_EPP_MODE;
268
            link->io.BasePort1 = io->win[0].base;
269
            link->io.NumPorts1 = io->win[0].len;
270
            link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
271
            if (io->nwin == 2) {
272
                link->io.BasePort2 = io->win[1].base;
273
                link->io.NumPorts2 = io->win[1].len;
274
            }
275
            CFG_CHECK(RequestIO, link->handle, &link->io);
276
            /* If we've got this far, we're done */
277
            break;
278
        }
279
 
280
    next_entry:
281
        if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
282
        CS_CHECK(GetNextTuple, handle, &tuple);
283
    }
284
 
285
    CS_CHECK(RequestIRQ, handle, &link->irq);
286
    CS_CHECK(RequestConfiguration, handle, &link->conf);
287
 
288
    release_region(link->io.BasePort1, link->io.NumPorts1);
289
    if (link->io.NumPorts2)
290
        release_region(link->io.BasePort2, link->io.NumPorts2);
291
    p = parport_pc_probe_port(link->io.BasePort1, link->io.BasePort2,
292
                              link->irq.AssignedIRQ, PARPORT_DMA_NONE,
293
                              NULL);
294
    if (p == NULL) {
295
        printk(KERN_NOTICE "parport_cs: parport_pc_probe_port() at "
296
               "0x%3x, irq %u failed\n", link->io.BasePort1,
297
               link->irq.AssignedIRQ);
298
        goto failed;
299
    }
300
 
301
    p->modes |= PARPORT_MODE_PCSPP;
302
    if (epp_mode)
303
        p->modes |= PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP;
304
    info->ndev = 1;
305
    info->node.major = LP_MAJOR;
306
    info->node.minor = p->number;
307
    info->port = p;
308
    strcpy(info->node.dev_name, p->name);
309
    link->dev = &info->node;
310
 
311
    link->state &= ~DEV_CONFIG_PENDING;
312
    return;
313
 
314
cs_failed:
315
    cs_error(link->handle, last_fn, last_ret);
316
failed:
317
    parport_cs_release((u_long)link);
318
    link->state &= ~DEV_CONFIG_PENDING;
319
 
320
} /* parport_config */
321
 
322
/*======================================================================
323
 
324
    After a card is removed, parport_cs_release() will unregister the
325
    device, and release the PCMCIA configuration.  If the device is
326
    still open, this will be postponed until it is closed.
327
 
328
======================================================================*/
329
 
330
void parport_cs_release(u_long arg)
331
{
332
    dev_link_t *link = (dev_link_t *)arg;
333
    parport_info_t *info = link->priv;
334
 
335
    DEBUG(0, "parport_release(0x%p)\n", link);
336
 
337
    if (info->ndev) {
338
        struct parport *p = info->port;
339
        parport_pc_unregister_port(p);
340
        request_region(link->io.BasePort1, link->io.NumPorts1,
341
                       info->node.dev_name);
342
        if (link->io.NumPorts2)
343
            request_region(link->io.BasePort2, link->io.NumPorts2,
344
                           info->node.dev_name);
345
    }
346
    info->ndev = 0;
347
    link->dev = NULL;
348
 
349
    CardServices(ReleaseConfiguration, link->handle);
350
    CardServices(ReleaseIO, link->handle, &link->io);
351
    CardServices(ReleaseIRQ, link->handle, &link->irq);
352
 
353
    link->state &= ~DEV_CONFIG;
354
 
355
} /* parport_cs_release */
356
 
357
/*======================================================================
358
 
359
    The card status event handler.  Mostly, this schedules other
360
    stuff to run after an event is received.
361
 
362
======================================================================*/
363
 
364
int parport_event(event_t event, int priority,
365
                  event_callback_args_t *args)
366
{
367
    dev_link_t *link = args->client_data;
368
 
369
    DEBUG(1, "parport_event(0x%06x)\n", event);
370
 
371
    switch (event) {
372
    case CS_EVENT_CARD_REMOVAL:
373
        link->state &= ~DEV_PRESENT;
374
        if (link->state & DEV_CONFIG)
375
            mod_timer(&link->release, jiffies + HZ/20);
376
        break;
377
    case CS_EVENT_CARD_INSERTION:
378
        link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
379
        parport_config(link);
380
        break;
381
    case CS_EVENT_PM_SUSPEND:
382
        link->state |= DEV_SUSPEND;
383
        /* Fall through... */
384
    case CS_EVENT_RESET_PHYSICAL:
385
        if (link->state & DEV_CONFIG)
386
            CardServices(ReleaseConfiguration, link->handle);
387
        break;
388
    case CS_EVENT_PM_RESUME:
389
        link->state &= ~DEV_SUSPEND;
390
        /* Fall through... */
391
    case CS_EVENT_CARD_RESET:
392
        if (DEV_OK(link))
393
            CardServices(RequestConfiguration, link->handle, &link->conf);
394
        break;
395
    }
396
    return 0;
397
} /* parport_event */
398
 
399
/*====================================================================*/
400
 
401
static int __init init_parport_cs(void)
402
{
403
    servinfo_t serv;
404
    DEBUG(0, "%s\n", version);
405
    CardServices(GetCardServicesInfo, &serv);
406
    if (serv.Revision != CS_RELEASE_CODE) {
407
        printk(KERN_NOTICE "parport_cs: Card Services release "
408
               "does not match!\n");
409
        return -EINVAL;
410
    }
411
    register_pccard_driver(&dev_info, &parport_attach, &parport_detach);
412
    return 0;
413
}
414
 
415
static void __exit exit_parport_cs(void)
416
{
417
    DEBUG(0, "parport_cs: unloading\n");
418
    unregister_pccard_driver(&dev_info);
419
    while (dev_list != NULL)
420
        parport_detach(dev_list);
421
}
422
 
423
module_init(init_parport_cs);
424
module_exit(exit_parport_cs);

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.