[PATCH] pcmcia: unify detach, REMOVAL_EVENT handlers into one remove callback
[linux-2.6.git] / drivers / scsi / pcmcia / aha152x_stub.c
1 /*======================================================================
2
3     A driver for Adaptec AHA152X-compatible PCMCIA SCSI cards.
4
5     This driver supports the Adaptec AHA-1460, the New Media Bus
6     Toaster, and the New Media Toast & Jam.
7     
8     aha152x_cs.c 1.54 2000/06/12 21:27:25
9
10     The contents of this file are subject to the Mozilla Public
11     License Version 1.1 (the "License"); you may not use this file
12     except in compliance with the License. You may obtain a copy of
13     the License at http://www.mozilla.org/MPL/
14
15     Software distributed under the License is distributed on an "AS
16     IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17     implied. See the License for the specific language governing
18     rights and limitations under the License.
19
20     The initial developer of the original code is David A. Hinds
21     <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
22     are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
23
24     Alternatively, the contents of this file may be used under the
25     terms of the GNU General Public License version 2 (the "GPL"), in which
26     case the provisions of the GPL are applicable instead of the
27     above.  If you wish to allow the use of your version of this file
28     only under the terms of the GPL and not to allow others to use
29     your version of this file under the MPL, indicate your decision
30     by deleting the provisions above and replace them with the notice
31     and other provisions required by the GPL.  If you do not delete
32     the provisions above, a recipient may use your version of this
33     file under either the MPL or the GPL.
34     
35 ======================================================================*/
36
37 #include <linux/module.h>
38 #include <linux/init.h>
39 #include <linux/kernel.h>
40 #include <linux/sched.h>
41 #include <linux/slab.h>
42 #include <linux/string.h>
43 #include <linux/ioport.h>
44 #include <scsi/scsi.h>
45 #include <linux/major.h>
46 #include <linux/blkdev.h>
47 #include <scsi/scsi_ioctl.h>
48
49 #include "scsi.h"
50 #include <scsi/scsi_host.h>
51 #include "aha152x.h"
52
53 #include <pcmcia/cs_types.h>
54 #include <pcmcia/cs.h>
55 #include <pcmcia/cistpl.h>
56 #include <pcmcia/ds.h>
57
58 #ifdef PCMCIA_DEBUG
59 static int pc_debug = PCMCIA_DEBUG;
60 module_param(pc_debug, int, 0644);
61 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
62 static char *version =
63 "aha152x_cs.c 1.54 2000/06/12 21:27:25 (David Hinds)";
64 #else
65 #define DEBUG(n, args...)
66 #endif
67
68 /*====================================================================*/
69
70 /* Parameters that can be set with 'insmod' */
71
72 /* SCSI bus setup options */
73 static int host_id = 7;
74 static int reconnect = 1;
75 static int parity = 1;
76 static int synchronous = 1;
77 static int reset_delay = 100;
78 static int ext_trans = 0;
79
80 module_param(host_id, int, 0);
81 module_param(reconnect, int, 0);
82 module_param(parity, int, 0);
83 module_param(synchronous, int, 0);
84 module_param(reset_delay, int, 0);
85 module_param(ext_trans, int, 0);
86
87 MODULE_LICENSE("Dual MPL/GPL");
88
89 /*====================================================================*/
90
91 typedef struct scsi_info_t {
92     dev_link_t          link;
93     dev_node_t          node;
94     struct Scsi_Host    *host;
95 } scsi_info_t;
96
97 static void aha152x_release_cs(dev_link_t *link);
98 static int aha152x_event(event_t event, int priority,
99                          event_callback_args_t *args);
100
101 static dev_link_t *aha152x_attach(void);
102 static void aha152x_detach(struct pcmcia_device *p_dev);
103
104 static dev_link_t *dev_list;
105 static dev_info_t dev_info = "aha152x_cs";
106
107 static dev_link_t *aha152x_attach(void)
108 {
109     scsi_info_t *info;
110     client_reg_t client_reg;
111     dev_link_t *link;
112     int ret;
113     
114     DEBUG(0, "aha152x_attach()\n");
115
116     /* Create new SCSI device */
117     info = kmalloc(sizeof(*info), GFP_KERNEL);
118     if (!info) return NULL;
119     memset(info, 0, sizeof(*info));
120     link = &info->link; link->priv = info;
121
122     link->io.NumPorts1 = 0x20;
123     link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
124     link->io.IOAddrLines = 10;
125     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
126     link->irq.IRQInfo1 = IRQ_LEVEL_ID;
127     link->conf.Attributes = CONF_ENABLE_IRQ;
128     link->conf.Vcc = 50;
129     link->conf.IntType = INT_MEMORY_AND_IO;
130     link->conf.Present = PRESENT_OPTION;
131
132     /* Register with Card Services */
133     link->next = dev_list;
134     dev_list = link;
135     client_reg.dev_info = &dev_info;
136     client_reg.Version = 0x0210;
137     client_reg.event_callback_args.client_data = link;
138     ret = pcmcia_register_client(&link->handle, &client_reg);
139     if (ret != 0) {
140         cs_error(link->handle, RegisterClient, ret);
141         aha152x_detach(link->handle);
142         return NULL;
143     }
144     
145     return link;
146 } /* aha152x_attach */
147
148 /*====================================================================*/
149
150 static void aha152x_detach(struct pcmcia_device *p_dev)
151 {
152     dev_link_t *link = dev_to_instance(p_dev);
153     dev_link_t **linkp;
154
155     DEBUG(0, "aha152x_detach(0x%p)\n", link);
156     
157     /* Locate device structure */
158     for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
159         if (*linkp == link) break;
160     if (*linkp == NULL)
161         return;
162
163     if (link->state & DEV_CONFIG)
164         aha152x_release_cs(link);
165
166     /* Unlink device structure, free bits */
167     *linkp = link->next;
168     kfree(link->priv);
169     
170 } /* aha152x_detach */
171
172 /*====================================================================*/
173
174 #define CS_CHECK(fn, ret) \
175 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
176
177 static void aha152x_config_cs(dev_link_t *link)
178 {
179     client_handle_t handle = link->handle;
180     scsi_info_t *info = link->priv;
181     struct aha152x_setup s;
182     tuple_t tuple;
183     cisparse_t parse;
184     int i, last_ret, last_fn;
185     u_char tuple_data[64];
186     struct Scsi_Host *host;
187     
188     DEBUG(0, "aha152x_config(0x%p)\n", link);
189
190     tuple.DesiredTuple = CISTPL_CONFIG;
191     tuple.TupleData = tuple_data;
192     tuple.TupleDataMax = 64;
193     tuple.TupleOffset = 0;
194     CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
195     CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
196     CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
197     link->conf.ConfigBase = parse.config.base;
198
199     /* Configure card */
200     link->state |= DEV_CONFIG;
201
202     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
203     CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
204     while (1) {
205         if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
206                 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
207             goto next_entry;
208         /* For New Media T&J, look for a SCSI window */
209         if (parse.cftable_entry.io.win[0].len >= 0x20)
210             link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
211         else if ((parse.cftable_entry.io.nwin > 1) &&
212                  (parse.cftable_entry.io.win[1].len >= 0x20))
213             link->io.BasePort1 = parse.cftable_entry.io.win[1].base;
214         if ((parse.cftable_entry.io.nwin > 0) &&
215             (link->io.BasePort1 < 0xffff)) {
216             link->conf.ConfigIndex = parse.cftable_entry.index;
217             i = pcmcia_request_io(handle, &link->io);
218             if (i == CS_SUCCESS) break;
219         }
220     next_entry:
221         CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
222     }
223     
224     CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
225     CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
226     
227     /* Set configuration options for the aha152x driver */
228     memset(&s, 0, sizeof(s));
229     s.conf        = "PCMCIA setup";
230     s.io_port     = link->io.BasePort1;
231     s.irq         = link->irq.AssignedIRQ;
232     s.scsiid      = host_id;
233     s.reconnect   = reconnect;
234     s.parity      = parity;
235     s.synchronous = synchronous;
236     s.delay       = reset_delay;
237     if (ext_trans)
238         s.ext_trans = ext_trans;
239
240     host = aha152x_probe_one(&s);
241     if (host == NULL) {
242         printk(KERN_INFO "aha152x_cs: no SCSI devices found\n");
243         goto cs_failed;
244     }
245
246     sprintf(info->node.dev_name, "scsi%d", host->host_no);
247     link->dev = &info->node;
248     info->host = host;
249
250     link->state &= ~DEV_CONFIG_PENDING;
251     return;
252     
253 cs_failed:
254     cs_error(link->handle, last_fn, last_ret);
255     aha152x_release_cs(link);
256     return;
257 }
258
259 static void aha152x_release_cs(dev_link_t *link)
260 {
261         scsi_info_t *info = link->priv;
262
263         aha152x_release(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         link->state &= ~DEV_CONFIG;
271 }
272
273 static int aha152x_suspend(struct pcmcia_device *dev)
274 {
275         dev_link_t *link = dev_to_instance(dev);
276
277         link->state |= DEV_SUSPEND;
278         if (link->state & DEV_CONFIG)
279                 pcmcia_release_configuration(link->handle);
280
281         return 0;
282 }
283
284 static int aha152x_resume(struct pcmcia_device *dev)
285 {
286         dev_link_t *link = dev_to_instance(dev);
287         scsi_info_t *info = link->priv;
288
289         link->state &= ~DEV_SUSPEND;
290         if (link->state & DEV_CONFIG) {
291                 Scsi_Cmnd tmp;
292                 pcmcia_request_configuration(link->handle, &link->conf);
293                 tmp.device->host = info->host;
294                 aha152x_host_reset(&tmp);
295         }
296
297         return 0;
298 }
299
300 static int aha152x_event(event_t event, int priority,
301                          event_callback_args_t *args)
302 {
303     dev_link_t *link = args->client_data;
304     
305     DEBUG(0, "aha152x_event(0x%06x)\n", event);
306     
307     switch (event) {
308     case CS_EVENT_CARD_INSERTION:
309         link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
310         aha152x_config_cs(link);
311         break;
312     }
313     return 0;
314 }
315
316 static struct pcmcia_device_id aha152x_ids[] = {
317         PCMCIA_DEVICE_PROD_ID123("New Media", "SCSI", "Bus Toaster", 0xcdf7e4cc, 0x35f26476, 0xa8851d6e),
318         PCMCIA_DEVICE_PROD_ID123("NOTEWORTHY", "SCSI", "Bus Toaster", 0xad89c6e8, 0x35f26476, 0xa8851d6e),
319         PCMCIA_DEVICE_PROD_ID12("Adaptec, Inc.", "APA-1460 SCSI Host Adapter", 0x24ba9738, 0x3a3c3d20),
320         PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "Multimedia Sound/SCSI", 0x085a850b, 0x80a6535c),
321         PCMCIA_DEVICE_PROD_ID12("NOTEWORTHY", "NWCOMB02 SCSI/AUDIO COMBO CARD", 0xad89c6e8, 0x5f9a615b),
322         PCMCIA_DEVICE_NULL,
323 };
324 MODULE_DEVICE_TABLE(pcmcia, aha152x_ids);
325
326 static struct pcmcia_driver aha152x_cs_driver = {
327         .owner          = THIS_MODULE,
328         .drv            = {
329                 .name   = "aha152x_cs",
330         },
331         .attach         = aha152x_attach,
332         .event          = aha152x_event,
333         .remove         = aha152x_detach,
334         .id_table       = aha152x_ids,
335         .suspend        = aha152x_suspend,
336         .resume         = aha152x_resume,
337 };
338
339 static int __init init_aha152x_cs(void)
340 {
341         return pcmcia_register_driver(&aha152x_cs_driver);
342 }
343
344 static void __exit exit_aha152x_cs(void)
345 {
346         pcmcia_unregister_driver(&aha152x_cs_driver);
347         BUG_ON(dev_list != NULL);
348 }
349
350 module_init(init_aha152x_cs);
351 module_exit(exit_aha152x_cs);
352