1 /*======================================================================
3 A driver for PCMCIA parallel port adapters
5 (specifically, for the Quatech SPP-100 EPP card: other cards will
6 probably require driver tweaks)
8 parport_cs.c 1.29 2002/10/11 06:57:41
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/
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.
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.
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.
35 ======================================================================*/
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>
47 #include <linux/parport.h>
48 #include <linux/parport_pc.h>
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>
58 /*====================================================================*/
60 /* Module parameters */
62 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
63 MODULE_DESCRIPTION("PCMCIA parallel port card driver");
64 MODULE_LICENSE("Dual MPL/GPL");
66 #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
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");
73 INT_MODULE_PARM(epp_mode, 1);
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)";
81 #define DEBUG(n, args...)
84 /*====================================================================*/
86 #define FORCE_EPP_MODE 0x08
88 typedef struct parport_info_t {
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);
102 static dev_info_t dev_info = "parport_cs";
103 static dev_link_t *dev_list = NULL;
105 /*====================================================================*/
107 static void cs_error(client_handle_t handle, int func, int ret)
109 error_info_t err = { func, ret };
110 CardServices(ReportError, handle, &err);
113 /*======================================================================
115 parport_attach() creates an "instance" of the driver, allocating
116 local data structures for one device. The device is registered
119 ======================================================================*/
121 static dev_link_t *parport_attach(void)
123 parport_info_t *info;
125 client_reg_t client_reg;
128 DEBUG(0, "parport_attach()\n");
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;
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;
145 for (i = 0; i < 4; i++)
146 link->irq.IRQInfo2 |= 1 << irq_list[i];
147 link->conf.Attributes = CONF_ENABLE_IRQ;
149 link->conf.IntType = INT_MEMORY_AND_IO;
151 /* Register with Card Services */
152 link->next = dev_list;
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);
171 } /* parport_attach */
173 /*======================================================================
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.
180 ======================================================================*/
182 static void parport_detach(dev_link_t *link)
187 DEBUG(0, "parport_detach(0x%p)\n", link);
189 /* Locate device structure */
190 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
191 if (*linkp == link) break;
195 del_timer(&link->release);
196 if (link->state & DEV_CONFIG)
197 parport_cs_release((u_long)link);
200 ret = CardServices(DeregisterClient, link->handle);
201 if (ret != CS_SUCCESS)
202 cs_error(link->handle, DeregisterClient, ret);
205 /* Unlink, free device structure */
209 } /* parport_detach */
211 /*======================================================================
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.
217 ======================================================================*/
219 #define CS_CHECK(fn, args...) \
220 while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
222 #define CFG_CHECK(fn, args...) \
223 if (CardServices(fn, args) != 0) goto next_entry
225 void parport_config(dev_link_t *link)
227 client_handle_t handle = link->handle;
228 parport_info_t *info = link->priv;
233 cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
234 cistpl_cftable_entry_t dflt = { 0 };
236 int last_ret, last_fn;
238 DEBUG(0, "parport_config(0x%p)\n", link);
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];
251 link->state |= DEV_CONFIG;
253 /* Not sure if this is right... look up the current Vcc */
254 CS_CHECK(GetConfigurationInfo, handle, &conf);
256 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
257 tuple.Attributes = 0;
258 CS_CHECK(GetFirstTuple, handle, &tuple);
260 CFG_CHECK(GetTupleData, handle, &tuple);
261 CFG_CHECK(ParseTuple, handle, &tuple, &parse);
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;
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;
272 link->io.BasePort2 = io->win[1].base;
273 link->io.NumPorts2 = io->win[1].len;
275 CFG_CHECK(RequestIO, link->handle, &link->io);
276 /* If we've got this far, we're done */
281 if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
282 CS_CHECK(GetNextTuple, handle, &tuple);
285 CS_CHECK(RequestIRQ, handle, &link->irq);
286 CS_CHECK(RequestConfiguration, handle, &link->conf);
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,
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);
301 p->modes |= PARPORT_MODE_PCSPP;
303 p->modes |= PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP;
305 info->node.major = LP_MAJOR;
306 info->node.minor = p->number;
308 strcpy(info->node.dev_name, p->name);
309 link->dev = &info->node;
311 link->state &= ~DEV_CONFIG_PENDING;
315 cs_error(link->handle, last_fn, last_ret);
317 parport_cs_release((u_long)link);
318 link->state &= ~DEV_CONFIG_PENDING;
320 } /* parport_config */
322 /*======================================================================
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.
328 ======================================================================*/
330 void parport_cs_release(u_long arg)
332 dev_link_t *link = (dev_link_t *)arg;
333 parport_info_t *info = link->priv;
335 DEBUG(0, "parport_release(0x%p)\n", link);
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);
349 CardServices(ReleaseConfiguration, link->handle);
350 CardServices(ReleaseIO, link->handle, &link->io);
351 CardServices(ReleaseIRQ, link->handle, &link->irq);
353 link->state &= ~DEV_CONFIG;
355 } /* parport_cs_release */
357 /*======================================================================
359 The card status event handler. Mostly, this schedules other
360 stuff to run after an event is received.
362 ======================================================================*/
364 int parport_event(event_t event, int priority,
365 event_callback_args_t *args)
367 dev_link_t *link = args->client_data;
369 DEBUG(1, "parport_event(0x%06x)\n", 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);
377 case CS_EVENT_CARD_INSERTION:
378 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
379 parport_config(link);
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);
388 case CS_EVENT_PM_RESUME:
389 link->state &= ~DEV_SUSPEND;
390 /* Fall through... */
391 case CS_EVENT_CARD_RESET:
393 CardServices(RequestConfiguration, link->handle, &link->conf);
397 } /* parport_event */
399 /*====================================================================*/
401 static int __init init_parport_cs(void)
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");
411 register_pccard_driver(&dev_info, &parport_attach, &parport_detach);
415 static void __exit exit_parport_cs(void)
417 DEBUG(0, "parport_cs: unloading\n");
418 unregister_pccard_driver(&dev_info);
419 while (dev_list != NULL)
420 parport_detach(dev_list);
423 module_init(init_parport_cs);
424 module_exit(exit_parport_cs);