ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.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/blkdev.h>
44 #include <scsi/scsi_ioctl.h>
45
46 #include "scsi.h"
47 #include "hosts.h"
48
49 #include <pcmcia/version.h>
50 #include <pcmcia/cs_types.h>
51 #include <pcmcia/cs.h>
52 #include <pcmcia/cistpl.h>
53 #include <pcmcia/ds.h>
54
55 /*====================================================================*/
56
57 /* Module parameters */
58
59 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
60 MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
61 MODULE_LICENSE("Dual MPL/GPL");
62
63 /* Bit map of interrupts to choose from */
64 static int irq_mask = 0xdeb8;
65 MODULE_PARM(irq_mask, "i");
66 static int irq_list[4] = { -1 };
67 MODULE_PARM(irq_list, "1-4i");
68
69 #ifdef PCMCIA_DEBUG
70 static int pc_debug = PCMCIA_DEBUG;
71 MODULE_PARM(pc_debug, "i");
72 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
73 static char *version =
74 "fdomain_cs.c 1.47 2001/10/13 00:08:52 (David Hinds)";
75 #else
76 #define DEBUG(n, args...)
77 #endif
78
79 /*====================================================================*/
80
81 typedef struct scsi_info_t {
82     dev_link_t          link;
83     dev_node_t          node;
84     struct Scsi_Host    *host;
85 } scsi_info_t;
86
87 extern Scsi_Host_Template fdomain_driver_template;
88 extern void fdomain_setup(char *str, int *ints);
89 extern struct Scsi_Host *__fdomain_16x0_detect( Scsi_Host_Template *tpnt );
90 extern int fdomain_16x0_bus_reset(Scsi_Cmnd *SCpnt);
91
92 static void fdomain_release(dev_link_t *link);
93 static int fdomain_event(event_t event, int priority,
94                         event_callback_args_t *args);
95
96 static dev_link_t *fdomain_attach(void);
97 static void fdomain_detach(dev_link_t *);
98
99
100 static dev_link_t *dev_list = NULL;
101
102 static dev_info_t dev_info = "fdomain_cs";
103
104 static dev_link_t *fdomain_attach(void)
105 {
106     scsi_info_t *info;
107     client_reg_t client_reg;
108     dev_link_t *link;
109     int i, ret;
110     
111     DEBUG(0, "fdomain_attach()\n");
112
113     /* Create new SCSI device */
114     info = kmalloc(sizeof(*info), GFP_KERNEL);
115     if (!info) return NULL;
116     memset(info, 0, sizeof(*info));
117     link = &info->link; link->priv = info;
118     link->io.NumPorts1 = 0x10;
119     link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
120     link->io.IOAddrLines = 10;
121     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
122     link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
123     if (irq_list[0] == -1)
124         link->irq.IRQInfo2 = irq_mask;
125     else
126         for (i = 0; i < 4; i++)
127             link->irq.IRQInfo2 |= 1 << irq_list[i];
128     link->conf.Attributes = CONF_ENABLE_IRQ;
129     link->conf.Vcc = 50;
130     link->conf.IntType = INT_MEMORY_AND_IO;
131     link->conf.Present = PRESENT_OPTION;
132
133     /* Register with Card Services */
134     link->next = dev_list;
135     dev_list = link;
136     client_reg.dev_info = &dev_info;
137     client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
138     client_reg.event_handler = &fdomain_event;
139     client_reg.EventMask =
140         CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
141         CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
142         CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
143     client_reg.Version = 0x0210;
144     client_reg.event_callback_args.client_data = link;
145     ret = pcmcia_register_client(&link->handle, &client_reg);
146     if (ret != 0) {
147         cs_error(link->handle, RegisterClient, ret);
148         fdomain_detach(link);
149         return NULL;
150     }
151     
152     return link;
153 } /* fdomain_attach */
154
155 /*====================================================================*/
156
157 static void fdomain_detach(dev_link_t *link)
158 {
159     dev_link_t **linkp;
160
161     DEBUG(0, "fdomain_detach(0x%p)\n", link);
162     
163     /* 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         fdomain_release(link);
171
172     if (link->handle)
173         pcmcia_deregister_client(link->handle);
174     
175     /* Unlink device structure, free bits */
176     *linkp = link->next;
177     kfree(link->priv);
178     
179 } /* fdomain_detach */
180
181 /*====================================================================*/
182
183 #define CS_CHECK(fn, ret) \
184 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
185
186 static void fdomain_config(dev_link_t *link)
187 {
188     client_handle_t handle = link->handle;
189     scsi_info_t *info = link->priv;
190     tuple_t tuple;
191     cisparse_t parse;
192     int i, last_ret, last_fn, ints[3];
193     u_char tuple_data[64];
194     char str[16];
195     struct Scsi_Host *host;
196
197     DEBUG(0, "fdomain_config(0x%p)\n", link);
198
199     tuple.DesiredTuple = CISTPL_CONFIG;
200     tuple.TupleData = tuple_data;
201     tuple.TupleDataMax = 64;
202     tuple.TupleOffset = 0;
203     CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
204     CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
205     CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
206     link->conf.ConfigBase = parse.config.base;
207
208     /* Configure card */
209     link->state |= DEV_CONFIG;
210     
211     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
212     CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
213     while (1) {
214         if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
215                 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
216             goto next_entry;
217         link->conf.ConfigIndex = parse.cftable_entry.index;
218         link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
219         i = pcmcia_request_io(handle, &link->io);
220         if (i == CS_SUCCESS) break;
221     next_entry:
222         CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
223     }
224
225     CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
226     CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
227     
228     /* A bad hack... */
229     release_region(link->io.BasePort1, link->io.NumPorts1);
230
231     /* Set configuration options for the fdomain driver */
232     ints[0] = 2;
233     ints[1] = link->io.BasePort1;
234     ints[2] = link->irq.AssignedIRQ;
235     sprintf(str, "%d,%d", link->io.BasePort1, link->irq.AssignedIRQ);
236     fdomain_setup(str, ints);
237     
238     host = __fdomain_16x0_detect(&fdomain_driver_template);
239     if (!host) {
240         printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
241         goto cs_failed;
242     }
243  
244     scsi_add_host(host, NULL); /* XXX handle failure */
245     scsi_scan_host(host);
246
247     sprintf(info->node.dev_name, "scsi%d", host->host_no);
248     link->dev = &info->node;
249     info->host = host;
250     
251     link->state &= ~DEV_CONFIG_PENDING;
252     return;
253     
254 cs_failed:
255     cs_error(link->handle, last_fn, last_ret);
256     fdomain_release(link);
257     return;
258     
259 } /* fdomain_config */
260
261 /*====================================================================*/
262
263 static void fdomain_release(dev_link_t *link)
264 {
265     scsi_info_t *info = link->priv;
266
267     DEBUG(0, "fdomain_release(0x%p)\n", link);
268
269     scsi_remove_host(info->host);
270     link->dev = NULL;
271     
272     pcmcia_release_configuration(link->handle);
273     pcmcia_release_io(link->handle, &link->io);
274     pcmcia_release_irq(link->handle, &link->irq);
275
276     scsi_unregister(info->host);
277
278     link->state &= ~DEV_CONFIG;
279 }
280
281 /*====================================================================*/
282
283 static int fdomain_event(event_t event, int priority,
284                         event_callback_args_t *args)
285 {
286     dev_link_t *link = args->client_data;
287
288     DEBUG(1, "fdomain_event(0x%06x)\n", event);
289     
290     switch (event) {
291     case CS_EVENT_CARD_REMOVAL:
292         link->state &= ~DEV_PRESENT;
293         if (link->state & DEV_CONFIG)
294             fdomain_release(link);
295         break;
296     case CS_EVENT_CARD_INSERTION:
297         link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
298         fdomain_config(link);
299         break;
300     case CS_EVENT_PM_SUSPEND:
301         link->state |= DEV_SUSPEND;
302         /* Fall through... */
303     case CS_EVENT_RESET_PHYSICAL:
304         if (link->state & DEV_CONFIG)
305             pcmcia_release_configuration(link->handle);
306         break;
307     case CS_EVENT_PM_RESUME:
308         link->state &= ~DEV_SUSPEND;
309         /* Fall through... */
310     case CS_EVENT_CARD_RESET:
311         if (link->state & DEV_CONFIG) {
312             pcmcia_request_configuration(link->handle, &link->conf);
313             fdomain_16x0_bus_reset(NULL);
314         }
315         break;
316     }
317     return 0;
318 } /* fdomain_event */
319
320 static struct pcmcia_driver fdomain_cs_driver = {
321         .owner          = THIS_MODULE,
322         .drv            = {
323                 .name   = "fdomain_cs",
324         },
325         .attach         = fdomain_attach,
326         .detach         = fdomain_detach,
327 };
328
329 static int __init init_fdomain_cs(void)
330 {
331         return pcmcia_register_driver(&fdomain_cs_driver);
332 }
333
334 static void __exit exit_fdomain_cs(void)
335 {
336         pcmcia_unregister_driver(&fdomain_cs_driver);
337
338         /* XXX: this really needs to move into generic code.. */
339         while (dev_list != NULL)
340                 fdomain_detach(dev_list);
341 }
342
343 module_init(init_fdomain_cs);
344 module_exit(exit_fdomain_cs);