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