File: /usr/src/linux/drivers/scsi/pcmcia/fdomain_stub.c

1     /*======================================================================
2     
3         A driver for Future Domain-compatible PCMCIA SCSI cards
4     
5         fdomain_cs.c 1.43 2000/06/12 21:27:25
6     
7         The contents of this file are subject to the Mozilla Public
8         License Version 1.1 (the "License"); you may not use this file
9         except in compliance with the License. You may obtain a copy of
10         the License at http://www.mozilla.org/MPL/
11     
12         Software distributed under the License is distributed on an "AS
13         IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14         implied. See the License for the specific language governing
15         rights and limitations under the License.
16     
17         The initial developer of the original code is David A. Hinds
18         <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
19         are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
20     
21         Alternatively, the contents of this file may be used under the
22         terms of the GNU General Public License version 2 (the "GPL"), in which
23         case the provisions of the GPL are applicable instead of the
24         above.  If you wish to allow the use of your version of this file
25         only under the terms of the GPL and not to allow others to use
26         your version of this file under the MPL, indicate your decision
27         by deleting the provisions above and replace them with the notice
28         and other provisions required by the GPL.  If you do not delete
29         the provisions above, a recipient may use your version of this
30         file under either the MPL or the GPL.
31         
32     ======================================================================*/
33     
34     #include <linux/module.h>
35     #include <linux/init.h>
36     #include <linux/kernel.h>
37     #include <linux/sched.h>
38     #include <linux/slab.h>
39     #include <linux/string.h>
40     #include <linux/timer.h>
41     #include <linux/ioport.h>
42     #include <scsi/scsi.h>
43     #include <linux/major.h>
44     #include <linux/blk.h>
45     
46     #include <../drivers/scsi/scsi.h>
47     #include <../drivers/scsi/hosts.h>
48     #include <scsi/scsi_ioctl.h>
49     #include <../drivers/scsi/fdomain.h>
50     
51     #include <pcmcia/version.h>
52     #include <pcmcia/cs_types.h>
53     #include <pcmcia/cs.h>
54     #include <pcmcia/cistpl.h>
55     #include <pcmcia/ds.h>
56     
57     #ifdef PCMCIA_DEBUG
58     static int pc_debug = PCMCIA_DEBUG;
59     MODULE_PARM(pc_debug, "i");
60     #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
61     static char *version =
62     "fdomain_cs.c 1.43 2000/06/12 21:27:25 (David Hinds)";
63     #else
64     #define DEBUG(n, args...)
65     #endif
66     
67     /*====================================================================*/
68     
69     /* Parameters that can be set with 'insmod' */
70     
71     /* Bit map of interrupts to choose from */
72     static u_int irq_mask = 0xdeb8;
73     static int irq_list[4] = { -1 };
74     
75     MODULE_PARM(irq_mask, "i");
76     MODULE_PARM(irq_list, "1-4i");
77     
78     /*====================================================================*/
79     
80     typedef struct scsi_info_t {
81         dev_link_t		link;
82         int			ndev;
83         dev_node_t		node[8];
84     } scsi_info_t;
85     
86     extern void fdomain_setup(char *str, int *ints);
87     
88     static void fdomain_release(u_long arg);
89     static int fdomain_event(event_t event, int priority,
90     			event_callback_args_t *args);
91     
92     static dev_link_t *fdomain_attach(void);
93     static void fdomain_detach(dev_link_t *);
94     
95     static Scsi_Host_Template driver_template = FDOMAIN_16X0;
96     
97     static dev_link_t *dev_list = NULL;
98     
99     static dev_info_t dev_info = "fdomain_cs";
100     
101     /*====================================================================*/
102     
103     static void cs_error(client_handle_t handle, int func, int ret)
104     {
105         error_info_t err = { func, ret };
106         CardServices(ReportError, handle, &err);
107     }
108     
109     /*====================================================================*/
110     
111     static dev_link_t *fdomain_attach(void)
112     {
113         scsi_info_t *info;
114         client_reg_t client_reg;
115         dev_link_t *link;
116         int i, ret;
117         
118         DEBUG(0, "fdomain_attach()\n");
119     
120         /* Create new SCSI device */
121         info = kmalloc(sizeof(*info), GFP_KERNEL);
122         if (!info) return NULL;
123         memset(info, 0, sizeof(*info));
124         link = &info->link; link->priv = info;
125         link->release.function = &fdomain_release;
126         link->release.data = (u_long)link;
127     
128         link->io.NumPorts1 = 0x10;
129         link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
130         link->io.IOAddrLines = 10;
131         link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
132         link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
133         if (irq_list[0] == -1)
134     	link->irq.IRQInfo2 = irq_mask;
135         else
136     	for (i = 0; i < 4; i++)
137     	    link->irq.IRQInfo2 |= 1 << irq_list[i];
138         link->conf.Attributes = CONF_ENABLE_IRQ;
139         link->conf.Vcc = 50;
140         link->conf.IntType = INT_MEMORY_AND_IO;
141         link->conf.Present = PRESENT_OPTION;
142     
143         /* Register with Card Services */
144         link->next = dev_list;
145         dev_list = link;
146         client_reg.dev_info = &dev_info;
147         client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
148         client_reg.event_handler = &fdomain_event;
149         client_reg.EventMask =
150     	CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
151     	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
152     	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
153         client_reg.Version = 0x0210;
154         client_reg.event_callback_args.client_data = link;
155         ret = CardServices(RegisterClient, &link->handle, &client_reg);
156         if (ret != 0) {
157     	cs_error(link->handle, RegisterClient, ret);
158     	fdomain_detach(link);
159     	return NULL;
160         }
161         
162         return link;
163     } /* fdomain_attach */
164     
165     /*====================================================================*/
166     
167     static void fdomain_detach(dev_link_t *link)
168     {
169         dev_link_t **linkp;
170     
171         DEBUG(0, "fdomain_detach(0x%p)\n", link);
172         
173         /* Locate device structure */
174         for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
175     	if (*linkp == link) break;
176         if (*linkp == NULL)
177     	return;
178     
179         del_timer(&link->release);
180         if (link->state & DEV_CONFIG) {
181     	fdomain_release((u_long)link);
182     	if (link->state & DEV_STALE_CONFIG) {
183     	    link->state |= DEV_STALE_LINK;
184     	    return;
185     	}
186         }
187     
188         if (link->handle)
189     	CardServices(DeregisterClient, link->handle);
190         
191         /* Unlink device structure, free bits */
192         *linkp = link->next;
193         kfree(link->priv);
194         
195     } /* fdomain_detach */
196     
197     /*====================================================================*/
198     
199     #define CS_CHECK(fn, args...) \
200     while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
201     
202     #define CFG_CHECK(fn, args...) \
203     if (CardServices(fn, args) != 0) goto next_entry
204     
205     static void fdomain_config(dev_link_t *link)
206     {
207         client_handle_t handle = link->handle;
208         scsi_info_t *info = link->priv;
209         tuple_t tuple;
210         cisparse_t parse;
211         int i, last_ret, last_fn, ints[3];
212         u_char tuple_data[64];
213         Scsi_Device *dev;
214         dev_node_t *node, **tail;
215         struct Scsi_Host *host;
216     
217         DEBUG(0, "fdomain_config(0x%p)\n", link);
218     
219         tuple.DesiredTuple = CISTPL_CONFIG;
220         tuple.TupleData = tuple_data;
221         tuple.TupleDataMax = 64;
222         tuple.TupleOffset = 0;
223         CS_CHECK(GetFirstTuple, handle, &tuple);
224         CS_CHECK(GetTupleData, handle, &tuple);
225         CS_CHECK(ParseTuple, handle, &tuple, &parse);
226         link->conf.ConfigBase = parse.config.base;
227     
228         /* Configure card */
229         driver_template.module = &__this_module;
230         link->state |= DEV_CONFIG;
231         
232         tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
233         CS_CHECK(GetFirstTuple, handle, &tuple);
234         while (1) {
235     	CFG_CHECK(GetTupleData, handle, &tuple);
236     	CFG_CHECK(ParseTuple, handle, &tuple, &parse);
237     	link->conf.ConfigIndex = parse.cftable_entry.index;
238     	link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
239     	i = CardServices(RequestIO, handle, &link->io);
240     	if (i == CS_SUCCESS) break;
241         next_entry:
242     	CS_CHECK(GetNextTuple, handle, &tuple);
243         }
244     
245         CS_CHECK(RequestIRQ, handle, &link->irq);
246         CS_CHECK(RequestConfiguration, handle, &link->conf);
247         
248         /* A bad hack... */
249         release_region(link->io.BasePort1, link->io.NumPorts1);
250     
251         /* Set configuration options for the fdomain driver */
252         ints[0] = 2;
253         ints[1] = link->io.BasePort1;
254         ints[2] = link->irq.AssignedIRQ;
255         fdomain_setup("PCMCIA setup", ints);
256         
257         scsi_register_module(MODULE_SCSI_HA, &driver_template);
258     
259         tail = &link->dev;
260         info->ndev = 0;
261         for (host = scsi_hostlist; host; host = host->next)
262     	if (host->hostt == &driver_template)
263     	    for (dev = host->host_queue; dev; dev = dev->next) {
264     	    u_long arg[2], id;
265     	    kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg);
266     	    id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) +
267     		((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000);
268     	    node = &info->node[info->ndev];
269     	    node->minor = 0;
270     	    switch (dev->type) {
271     	    case TYPE_TAPE:
272     		node->major = SCSI_TAPE_MAJOR;
273     		sprintf(node->dev_name, "st#%04lx", id);
274     		break;
275     	    case TYPE_DISK:
276     	    case TYPE_MOD:
277     		node->major = SCSI_DISK0_MAJOR;
278     		sprintf(node->dev_name, "sd#%04lx", id);
279     		break;
280     	    case TYPE_ROM:
281     	    case TYPE_WORM:
282     		node->major = SCSI_CDROM_MAJOR;
283     		sprintf(node->dev_name, "sr#%04lx", id);
284     		break;
285     	    default:
286     		node->major = SCSI_GENERIC_MAJOR;
287     		sprintf(node->dev_name, "sg#%04lx", id);
288     		break;
289     	    }
290     	    *tail = node; tail = &node->next;
291     	    info->ndev++;
292     	}
293         *tail = NULL;
294         if (info->ndev == 0)
295     	printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
296         
297         link->state &= ~DEV_CONFIG_PENDING;
298         return;
299         
300     cs_failed:
301         cs_error(link->handle, last_fn, last_ret);
302         fdomain_release((u_long)link);
303         return;
304         
305     } /* fdomain_config */
306     
307     /*====================================================================*/
308     
309     static void fdomain_release(u_long arg)
310     {
311         dev_link_t *link = (dev_link_t *)arg;
312     
313         DEBUG(0, "fdomain_release(0x%p)\n", link);
314     
315         if (GET_USE_COUNT(&__this_module) != 0) {
316     	DEBUG(1, "fdomain_cs: release postponed, "
317     	      "device still open\n");
318     	link->state |= DEV_STALE_CONFIG;
319     	return;
320         }
321     
322         scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
323         link->dev = NULL;
324         
325         CardServices(ReleaseConfiguration, link->handle);
326         CardServices(ReleaseIO, link->handle, &link->io);
327         CardServices(ReleaseIRQ, link->handle, &link->irq);
328         
329         link->state &= ~DEV_CONFIG;
330         if (link->state & DEV_STALE_LINK)
331     	fdomain_detach(link);
332         
333     } /* fdomain_release */
334     
335     /*====================================================================*/
336     
337     static int fdomain_event(event_t event, int priority,
338     			event_callback_args_t *args)
339     {
340         dev_link_t *link = args->client_data;
341     
342         DEBUG(1, "fdomain_event(0x%06x)\n", event);
343         
344         switch (event) {
345         case CS_EVENT_CARD_REMOVAL:
346     	link->state &= ~DEV_PRESENT;
347     	if (link->state & DEV_CONFIG)
348     	    mod_timer(&link->release, jiffies + HZ/20);
349     	break;
350         case CS_EVENT_CARD_INSERTION:
351     	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
352     	fdomain_config(link);
353     	break;
354         case CS_EVENT_PM_SUSPEND:
355     	link->state |= DEV_SUSPEND;
356     	/* Fall through... */
357         case CS_EVENT_RESET_PHYSICAL:
358     	if (link->state & DEV_CONFIG)
359     	    CardServices(ReleaseConfiguration, link->handle);
360     	break;
361         case CS_EVENT_PM_RESUME:
362     	link->state &= ~DEV_SUSPEND;
363     	/* Fall through... */
364         case CS_EVENT_CARD_RESET:
365     	if (link->state & DEV_CONFIG) {
366     	    CardServices(RequestConfiguration, link->handle, &link->conf);
367     	    fdomain_16x0_reset(NULL, 0);
368     	}
369     	break;
370         }
371         return 0;
372     } /* fdomain_event */
373     
374     /*====================================================================*/
375     
376     static int __init init_fdomain_cs(void) {
377         servinfo_t serv;
378         DEBUG(0, "%s\n", version);
379         CardServices(GetCardServicesInfo, &serv);
380         if (serv.Revision != CS_RELEASE_CODE) {
381     	printk(KERN_NOTICE "fdomain_cs: Card Services release "
382     	       "does not match!\n");
383     	return -1;
384         }
385         register_pccard_driver(&dev_info, &fdomain_attach, &fdomain_detach);
386         return 0;
387     }
388     
389     static void __exit exit_fdomain_cs(void) {
390         DEBUG(0, "fdomain_cs: unloading\n");
391         unregister_pccard_driver(&dev_info);
392         while (dev_list != NULL)
393     	fdomain_detach(dev_list);
394     }
395     
396     module_init(init_fdomain_cs);
397     module_exit(exit_fdomain_cs);
398