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