more changes on original files
[linux-2.4.git] / drivers / scsi / pcmcia / fdomain_stub.c
1 /*======================================================================
2
3     A driver for Future Domain-compatible PCMCIA SCSI cards
4
5     fdomain_cs.c 1.47 2001/10/13 00:08:52
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
23     which 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/ioport.h>
41 #include <scsi/scsi.h>
42 #include <linux/major.h>
43 #include <linux/blk.h>
44
45 #include <../drivers/scsi/scsi.h>
46 #include <../drivers/scsi/hosts.h>
47 #include <scsi/scsi_ioctl.h>
48 #include <../drivers/scsi/fdomain.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
56 /*====================================================================*/
57
58 /* Module parameters */
59
60 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
61 MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
62 MODULE_LICENSE("Dual MPL/GPL");
63
64 #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
65
66 /* Bit map of interrupts to choose from */
67 INT_MODULE_PARM(irq_mask, 0xdeb8);
68 static int irq_list[4] = { -1 };
69 MODULE_PARM(irq_list, "1-4i");
70
71 #ifdef PCMCIA_DEBUG
72 INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
73 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
74 static char *version =
75 "fdomain_cs.c 1.47 2001/10/13 00:08:52 (David Hinds)";
76 #else
77 #define DEBUG(n, args...)
78 #endif
79
80 /*====================================================================*/
81
82 typedef struct scsi_info_t {
83     dev_link_t          link;
84     int                 ndev;
85     dev_node_t          node[8];
86 } scsi_info_t;
87
88 extern void fdomain_setup(char *str, int *ints);
89
90 static void fdomain_release(u_long arg);
91 static int fdomain_event(event_t event, int priority,
92                         event_callback_args_t *args);
93
94 static dev_link_t *fdomain_attach(void);
95 static void fdomain_detach(dev_link_t *);
96
97 static Scsi_Host_Template driver_template = FDOMAIN_16X0;
98
99 static dev_link_t *dev_list = NULL;
100
101 static dev_info_t dev_info = "fdomain_cs";
102
103 /*====================================================================*/
104
105 static void cs_error(client_handle_t handle, int func, int ret)
106 {
107     error_info_t err = { func, ret };
108     CardServices(ReportError, handle, &err);
109 }
110
111 /*====================================================================*/
112
113 static dev_link_t *fdomain_attach(void)
114 {
115     scsi_info_t *info;
116     client_reg_t client_reg;
117     dev_link_t *link;
118     int i, ret;
119     
120     DEBUG(0, "fdomain_attach()\n");
121
122     /* Create new SCSI device */
123     info = kmalloc(sizeof(*info), GFP_KERNEL);
124     if (!info) return NULL;
125     memset(info, 0, sizeof(*info));
126     link = &info->link; link->priv = info;
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     if (link->state & DEV_CONFIG) {
180         fdomain_release((u_long)link);
181         if (link->state & DEV_STALE_CONFIG) {
182             link->state |= DEV_STALE_LINK;
183             return;
184         }
185     }
186
187     if (link->handle)
188         CardServices(DeregisterClient, link->handle);
189     
190     /* Unlink device structure, free bits */
191     *linkp = link->next;
192     kfree(link->priv);
193     
194 } /* fdomain_detach */
195
196 /*====================================================================*/
197
198 #define CS_CHECK(fn, args...) \
199 while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
200
201 #define CFG_CHECK(fn, args...) \
202 if (CardServices(fn, args) != 0) goto next_entry
203
204 static void fdomain_config(dev_link_t *link)
205 {
206     client_handle_t handle = link->handle;
207     scsi_info_t *info = link->priv;
208     tuple_t tuple;
209     cisparse_t parse;
210     int i, last_ret, last_fn, ints[3];
211     u_char tuple_data[64];
212     Scsi_Device *dev;
213     dev_node_t *node, **tail;
214     char str[16];
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     sprintf(str, "%d,%d", link->io.BasePort1, link->irq.AssignedIRQ);
256     fdomain_setup(str, ints);
257     
258     scsi_register_module(MODULE_SCSI_HA, &driver_template);
259
260     tail = &link->dev;
261     info->ndev = 0;
262     for (host = scsi_hostlist; host; host = host->next)
263         if (host->hostt == &driver_template)
264             for (dev = host->host_queue; dev; dev = dev->next) {
265             u_long arg[2], id;
266             kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg);
267             id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) +
268                 ((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000);
269             node = &info->node[info->ndev];
270             node->minor = 0;
271             switch (dev->type) {
272             case TYPE_TAPE:
273                 node->major = SCSI_TAPE_MAJOR;
274                 sprintf(node->dev_name, "st#%04lx", id);
275                 break;
276             case TYPE_DISK:
277             case TYPE_MOD:
278                 node->major = SCSI_DISK0_MAJOR;
279                 sprintf(node->dev_name, "sd#%04lx", id);
280                 break;
281             case TYPE_ROM:
282             case TYPE_WORM:
283                 node->major = SCSI_CDROM_MAJOR;
284                 sprintf(node->dev_name, "sr#%04lx", id);
285                 break;
286             default:
287                 node->major = SCSI_GENERIC_MAJOR;
288                 sprintf(node->dev_name, "sg#%04lx", id);
289                 break;
290             }
291             *tail = node; tail = &node->next;
292             info->ndev++;
293         }
294     *tail = NULL;
295     if (info->ndev == 0)
296         printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
297     
298     link->state &= ~DEV_CONFIG_PENDING;
299     return;
300     
301 cs_failed:
302     cs_error(link->handle, last_fn, last_ret);
303     fdomain_release((u_long)link);
304     return;
305     
306 } /* fdomain_config */
307
308 /*====================================================================*/
309
310 static void fdomain_release(u_long arg)
311 {
312     dev_link_t *link = (dev_link_t *)arg;
313
314     DEBUG(0, "fdomain_release(0x%p)\n", link);
315
316     if (GET_USE_COUNT(&__this_module) != 0) {
317         DEBUG(1, "fdomain_cs: release postponed, "
318               "device still open\n");
319         link->state |= DEV_STALE_CONFIG;
320         return;
321     }
322
323     scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
324     link->dev = NULL;
325     
326     CardServices(ReleaseConfiguration, link->handle);
327     CardServices(ReleaseIO, link->handle, &link->io);
328     CardServices(ReleaseIRQ, link->handle, &link->irq);
329     
330     link->state &= ~DEV_CONFIG;
331     if (link->state & DEV_STALE_LINK)
332         fdomain_detach(link);
333     
334 } /* fdomain_release */
335
336 /*====================================================================*/
337
338 static int fdomain_event(event_t event, int priority,
339                         event_callback_args_t *args)
340 {
341     dev_link_t *link = args->client_data;
342
343     DEBUG(1, "fdomain_event(0x%06x)\n", event);
344     
345     switch (event) {
346     case CS_EVENT_CARD_REMOVAL:
347         link->state &= ~DEV_PRESENT;
348         if (link->state & DEV_CONFIG)
349             fdomain_release((u_long)link);
350         break;
351     case CS_EVENT_CARD_INSERTION:
352         link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
353         fdomain_config(link);
354         break;
355     case CS_EVENT_PM_SUSPEND:
356         link->state |= DEV_SUSPEND;
357         /* Fall through... */
358     case CS_EVENT_RESET_PHYSICAL:
359         if (link->state & DEV_CONFIG)
360             CardServices(ReleaseConfiguration, link->handle);
361         break;
362     case CS_EVENT_PM_RESUME:
363         link->state &= ~DEV_SUSPEND;
364         /* Fall through... */
365     case CS_EVENT_CARD_RESET:
366         if (link->state & DEV_CONFIG) {
367             CardServices(RequestConfiguration, link->handle, &link->conf);
368             fdomain_16x0_reset(NULL, 0);
369         }
370         break;
371     }
372     return 0;
373 } /* fdomain_event */
374
375 /*====================================================================*/
376
377 static int __init init_fdomain_cs(void) {
378     servinfo_t serv;
379     DEBUG(0, "%s\n", version);
380     CardServices(GetCardServicesInfo, &serv);
381     if (serv.Revision != CS_RELEASE_CODE) {
382         printk(KERN_NOTICE "fdomain_cs: Card Services release "
383                "does not match!\n");
384         return -1;
385     }
386     register_pccard_driver(&dev_info, &fdomain_attach, &fdomain_detach);
387     return 0;
388 }
389
390 static void __exit exit_fdomain_cs(void) {
391     DEBUG(0, "fdomain_cs: unloading\n");
392     unregister_pccard_driver(&dev_info);
393     while (dev_list != NULL)
394         fdomain_detach(dev_list);
395 }
396
397 module_init(init_fdomain_cs);
398 module_exit(exit_fdomain_cs);