1 /*======================================================================
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 
cs_error(client_handle_t handle,int func,int ret)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 
parport_attach(void)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 
parport_detach(dev_link_t * link)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 
parport_config(dev_link_t * link)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 
parport_cs_release(u_long arg)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 
parport_event(event_t event,int priority,event_callback_args_t * args)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 
init_parport_cs(void)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 
exit_parport_cs(void)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);
425