blob: 063d22de97437f2c1a75d2ff17da67d602a15f17 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*======================================================================
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#include <linux/major.h>
47
48#include <linux/parport.h>
49#include <linux/parport_pc.h>
50
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#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
62MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
63MODULE_DESCRIPTION("PCMCIA parallel port card driver");
64MODULE_LICENSE("Dual MPL/GPL");
65
66#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
67
68INT_MODULE_PARM(epp_mode, 1);
69
70#ifdef PCMCIA_DEBUG
71INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
72#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
73static char *version =
74"parport_cs.c 1.29 2002/10/11 06:57:41 (David Hinds)";
75#else
76#define DEBUG(n, args...)
77#endif
78
79/*====================================================================*/
80
81#define FORCE_EPP_MODE 0x08
82
83typedef struct parport_info_t {
84 dev_link_t link;
85 int ndev;
86 dev_node_t node;
87 struct parport *port;
88} parport_info_t;
89
90static dev_link_t *parport_attach(void);
Dominik Brodowskicc3b4862005-11-14 21:23:14 +010091static void parport_detach(struct pcmcia_device *p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070092static void parport_config(dev_link_t *link);
93static void parport_cs_release(dev_link_t *);
94static int parport_event(event_t event, int priority,
95 event_callback_args_t *args);
96
97static dev_info_t dev_info = "parport_cs";
98static dev_link_t *dev_list = NULL;
99
100/*======================================================================
101
102 parport_attach() creates an "instance" of the driver, allocating
103 local data structures for one device. The device is registered
104 with Card Services.
105
106======================================================================*/
107
108static dev_link_t *parport_attach(void)
109{
110 parport_info_t *info;
111 dev_link_t *link;
112 client_reg_t client_reg;
113 int ret;
114
115 DEBUG(0, "parport_attach()\n");
116
117 /* Create new parport device */
118 info = kmalloc(sizeof(*info), GFP_KERNEL);
119 if (!info) return NULL;
120 memset(info, 0, sizeof(*info));
121 link = &info->link; link->priv = info;
122
123 link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
124 link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
125 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
126 link->irq.IRQInfo1 = IRQ_LEVEL_ID;
127 link->conf.Attributes = CONF_ENABLE_IRQ;
128 link->conf.Vcc = 50;
129 link->conf.IntType = INT_MEMORY_AND_IO;
130
131 /* Register with Card Services */
132 link->next = dev_list;
133 dev_list = link;
134 client_reg.dev_info = &dev_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 client_reg.Version = 0x0210;
136 client_reg.event_callback_args.client_data = link;
137 ret = pcmcia_register_client(&link->handle, &client_reg);
138 if (ret != CS_SUCCESS) {
139 cs_error(link->handle, RegisterClient, ret);
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100140 parport_detach(link->handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 return NULL;
142 }
143
144 return link;
145} /* parport_attach */
146
147/*======================================================================
148
149 This deletes a driver "instance". The device is de-registered
150 with Card Services. If it has been released, all local data
151 structures are freed. Otherwise, the structures will be freed
152 when the device is released.
153
154======================================================================*/
155
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100156static void parport_detach(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157{
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100158 dev_link_t *link = dev_to_instance(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 dev_link_t **linkp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
161 DEBUG(0, "parport_detach(0x%p)\n", link);
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 /* Locate device structure */
164 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
165 if (*linkp == link) break;
166 if (*linkp == NULL)
167 return;
168
169 if (link->state & DEV_CONFIG)
170 parport_cs_release(link);
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100171
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 /* Unlink, free device structure */
173 *linkp = link->next;
174 kfree(link->priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175} /* parport_detach */
176
177/*======================================================================
178
179 parport_config() is scheduled to run after a CARD_INSERTION event
180 is received, to configure the PCMCIA socket, and to make the
181 parport device available to the system.
182
183======================================================================*/
184
185#define CS_CHECK(fn, ret) \
186do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
187
188void parport_config(dev_link_t *link)
189{
190 client_handle_t handle = link->handle;
191 parport_info_t *info = link->priv;
192 tuple_t tuple;
193 u_short buf[128];
194 cisparse_t parse;
195 config_info_t conf;
196 cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
197 cistpl_cftable_entry_t dflt = { 0 };
198 struct parport *p;
199 int last_ret, last_fn;
200
201 DEBUG(0, "parport_config(0x%p)\n", link);
202
203 tuple.TupleData = (cisdata_t *)buf;
204 tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
205 tuple.Attributes = 0;
206 tuple.DesiredTuple = CISTPL_CONFIG;
207 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
208 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
209 CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
210 link->conf.ConfigBase = parse.config.base;
211 link->conf.Present = parse.config.rmask[0];
212
213 /* Configure card */
214 link->state |= DEV_CONFIG;
215
216 /* Not sure if this is right... look up the current Vcc */
217 CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
218
219 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
220 tuple.Attributes = 0;
221 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
222 while (1) {
223 if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
224 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
225 goto next_entry;
226
227 if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
228 cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
229 link->conf.ConfigIndex = cfg->index;
230 if (epp_mode)
231 link->conf.ConfigIndex |= FORCE_EPP_MODE;
232 link->io.BasePort1 = io->win[0].base;
233 link->io.NumPorts1 = io->win[0].len;
234 link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
235 if (io->nwin == 2) {
236 link->io.BasePort2 = io->win[1].base;
237 link->io.NumPorts2 = io->win[1].len;
238 }
239 if (pcmcia_request_io(link->handle, &link->io) != 0)
240 goto next_entry;
241 /* If we've got this far, we're done */
242 break;
243 }
244
245 next_entry:
246 if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
247 CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
248 }
249
250 CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
251 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
252
253 release_region(link->io.BasePort1, link->io.NumPorts1);
254 if (link->io.NumPorts2)
255 release_region(link->io.BasePort2, link->io.NumPorts2);
256 p = parport_pc_probe_port(link->io.BasePort1, link->io.BasePort2,
257 link->irq.AssignedIRQ, PARPORT_DMA_NONE,
258 NULL);
259 if (p == NULL) {
260 printk(KERN_NOTICE "parport_cs: parport_pc_probe_port() at "
261 "0x%3x, irq %u failed\n", link->io.BasePort1,
262 link->irq.AssignedIRQ);
263 goto failed;
264 }
265
266 p->modes |= PARPORT_MODE_PCSPP;
267 if (epp_mode)
268 p->modes |= PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP;
269 info->ndev = 1;
270 info->node.major = LP_MAJOR;
271 info->node.minor = p->number;
272 info->port = p;
273 strcpy(info->node.dev_name, p->name);
274 link->dev = &info->node;
275
276 link->state &= ~DEV_CONFIG_PENDING;
277 return;
278
279cs_failed:
280 cs_error(link->handle, last_fn, last_ret);
281failed:
282 parport_cs_release(link);
283 link->state &= ~DEV_CONFIG_PENDING;
284
285} /* parport_config */
286
287/*======================================================================
288
289 After a card is removed, parport_cs_release() will unregister the
290 device, and release the PCMCIA configuration. If the device is
291 still open, this will be postponed until it is closed.
292
293======================================================================*/
294
295void parport_cs_release(dev_link_t *link)
296{
297 parport_info_t *info = link->priv;
298
299 DEBUG(0, "parport_release(0x%p)\n", link);
300
301 if (info->ndev) {
302 struct parport *p = info->port;
303 parport_pc_unregister_port(p);
304 request_region(link->io.BasePort1, link->io.NumPorts1,
305 info->node.dev_name);
306 if (link->io.NumPorts2)
307 request_region(link->io.BasePort2, link->io.NumPorts2,
308 info->node.dev_name);
309 }
310 info->ndev = 0;
311 link->dev = NULL;
312
313 pcmcia_release_configuration(link->handle);
314 pcmcia_release_io(link->handle, &link->io);
315 pcmcia_release_irq(link->handle, &link->irq);
316
317 link->state &= ~DEV_CONFIG;
318
319} /* parport_cs_release */
320
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100321static int parport_suspend(struct pcmcia_device *dev)
322{
323 dev_link_t *link = dev_to_instance(dev);
324
325 link->state |= DEV_SUSPEND;
326 if (link->state & DEV_CONFIG)
327 pcmcia_release_configuration(link->handle);
328
329 return 0;
330}
331
332static int parport_resume(struct pcmcia_device *dev)
333{
334 dev_link_t *link = dev_to_instance(dev);
335
336 link->state &= ~DEV_SUSPEND;
337 if (DEV_OK(link))
338 pcmcia_request_configuration(link->handle, &link->conf);
339
340 return 0;
341}
342
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343/*======================================================================
344
345 The card status event handler. Mostly, this schedules other
346 stuff to run after an event is received.
347
348======================================================================*/
349
350int parport_event(event_t event, int priority,
351 event_callback_args_t *args)
352{
353 dev_link_t *link = args->client_data;
354
355 DEBUG(1, "parport_event(0x%06x)\n", event);
356
357 switch (event) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 case CS_EVENT_CARD_INSERTION:
359 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
360 parport_config(link);
361 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 }
363 return 0;
364} /* parport_event */
365
Dominik Brodowski476835a2005-06-27 16:28:29 -0700366static struct pcmcia_device_id parport_ids[] = {
367 PCMCIA_DEVICE_FUNC_ID(3),
368 PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0003),
369 PCMCIA_DEVICE_NULL
370};
371MODULE_DEVICE_TABLE(pcmcia, parport_ids);
372
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373static struct pcmcia_driver parport_cs_driver = {
374 .owner = THIS_MODULE,
375 .drv = {
376 .name = "parport_cs",
377 },
378 .attach = parport_attach,
Dominik Brodowski1e212f32005-07-07 17:59:00 -0700379 .event = parport_event,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100380 .remove = parport_detach,
Dominik Brodowski476835a2005-06-27 16:28:29 -0700381 .id_table = parport_ids,
Dominik Brodowski98e4c282005-11-14 21:21:18 +0100382 .suspend = parport_suspend,
383 .resume = parport_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384};
385
386static int __init init_parport_cs(void)
387{
388 return pcmcia_register_driver(&parport_cs_driver);
389}
390
391static void __exit exit_parport_cs(void)
392{
393 pcmcia_unregister_driver(&parport_cs_driver);
394 BUG_ON(dev_list != NULL);
395}
396
397module_init(init_parport_cs);
398module_exit(exit_parport_cs);