[PATCH] pcmcia: move event handler
[linux-2.6] / 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/version.h>
54 #include <pcmcia/cs_types.h>
55 #include <pcmcia/cs.h>
56 #include <pcmcia/cistpl.h>
57 #include <pcmcia/ds.h>
58
59 #ifdef PCMCIA_DEBUG
60 static int pc_debug = PCMCIA_DEBUG;
61 module_param(pc_debug, int, 0644);
62 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
63 static char *version =
64 "aha152x_cs.c 1.54 2000/06/12 21:27:25 (David Hinds)";
65 #else
66 #define DEBUG(n, args...)
67 #endif
68
69 /*====================================================================*/
70
71 /* Parameters that can be set with 'insmod' */
72
73 /* SCSI bus setup options */
74 static int host_id = 7;
75 static int reconnect = 1;
76 static int parity = 1;
77 static int synchronous = 1;
78 static int reset_delay = 100;
79 static int ext_trans = 0;
80
81 module_param(host_id, int, 0);
82 module_param(reconnect, int, 0);
83 module_param(parity, int, 0);
84 module_param(synchronous, int, 0);
85 module_param(reset_delay, int, 0);
86 module_param(ext_trans, int, 0);
87
88 MODULE_LICENSE("Dual MPL/GPL");
89
90 /*====================================================================*/
91
92 typedef struct scsi_info_t {
93     dev_link_t          link;
94     dev_node_t          node;
95     struct Scsi_Host    *host;
96 } scsi_info_t;
97
98 static void aha152x_release_cs(dev_link_t *link);
99 static int aha152x_event(event_t event, int priority,
100                          event_callback_args_t *args);
101
102 static dev_link_t *aha152x_attach(void);
103 static void aha152x_detach(dev_link_t *);
104
105 static dev_link_t *dev_list;
106 static dev_info_t dev_info = "aha152x_cs";
107
108 static dev_link_t *aha152x_attach(void)
109 {
110     scsi_info_t *info;
111     client_reg_t client_reg;
112     dev_link_t *link;
113     int ret;
114     
115     DEBUG(0, "aha152x_attach()\n");
116
117     /* Create new SCSI device */
118     info = kmalloc(sizeof(*info), GFP_KERNEL);
119     if (!info) return NULL;
120     memset(info, 0, sizeof(*info));
121     link = &info->link; link->priv = info;
122
123     link->io.NumPorts1 = 0x20;
124     link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
125     link->io.IOAddrLines = 10;
126     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
127     link->irq.IRQInfo1 = IRQ_LEVEL_ID;
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.Version = 0x0210;
138     client_reg.event_callback_args.client_data = link;
139     ret = pcmcia_register_client(&link->handle, &client_reg);
140     if (ret != 0) {
141         cs_error(link->handle, RegisterClient, ret);
142         aha152x_detach(link);
143         return NULL;
144     }
145     
146     return link;
147 } /* aha152x_attach */
148
149 /*====================================================================*/
150
151 static void aha152x_detach(dev_link_t *link)
152 {
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     if (link->handle)
167         pcmcia_deregister_client(link->handle);
168     
169     /* Unlink device structure, free bits */
170     *linkp = link->next;
171     kfree(link->priv);
172     
173 } /* aha152x_detach */
174
175 /*====================================================================*/
176
177 #define CS_CHECK(fn, ret) \
178 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
179
180 static void aha152x_config_cs(dev_link_t *link)
181 {
182     client_handle_t handle = link->handle;
183     scsi_info_t *info = link->priv;
184     struct aha152x_setup s;
185     tuple_t tuple;
186     cisparse_t parse;
187     int i, last_ret, last_fn;
188     u_char tuple_data[64];
189     struct Scsi_Host *host;
190     
191     DEBUG(0, "aha152x_config(0x%p)\n", link);
192
193     tuple.DesiredTuple = CISTPL_CONFIG;
194     tuple.TupleData = tuple_data;
195     tuple.TupleDataMax = 64;
196     tuple.TupleOffset = 0;
197     CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
198     CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
199     CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
200     link->conf.ConfigBase = parse.config.base;
201
202     /* Configure card */
203     link->state |= DEV_CONFIG;
204
205     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
206     CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
207     while (1) {
208         if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
209                 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
210             goto next_entry;
211         /* For New Media T&J, look for a SCSI window */
212         if (parse.cftable_entry.io.win[0].len >= 0x20)
213             link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
214         else if ((parse.cftable_entry.io.nwin > 1) &&
215                  (parse.cftable_entry.io.win[1].len >= 0x20))
216             link->io.BasePort1 = parse.cftable_entry.io.win[1].base;
217         if ((parse.cftable_entry.io.nwin > 0) &&
218             (link->io.BasePort1 < 0xffff)) {
219             link->conf.ConfigIndex = parse.cftable_entry.index;
220             i = pcmcia_request_io(handle, &link->io);
221             if (i == CS_SUCCESS) break;
222         }
223     next_entry:
224         CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
225     }
226     
227     CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
228     CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
229     
230     /* Set configuration options for the aha152x driver */
231     memset(&s, 0, sizeof(s));
232     s.conf        = "PCMCIA setup";
233     s.io_port     = link->io.BasePort1;
234     s.irq         = link->irq.AssignedIRQ;
235     s.scsiid      = host_id;
236     s.reconnect   = reconnect;
237     s.parity      = parity;
238     s.synchronous = synchronous;
239     s.delay       = reset_delay;
240     if (ext_trans)
241         s.ext_trans = ext_trans;
242
243     host = aha152x_probe_one(&s);
244     if (host == NULL) {
245         printk(KERN_INFO "aha152x_cs: no SCSI devices found\n");
246         goto cs_failed;
247     }
248
249     sprintf(info->node.dev_name, "scsi%d", host->host_no);
250     link->dev = &info->node;
251     info->host = host;
252
253     link->state &= ~DEV_CONFIG_PENDING;
254     return;
255     
256 cs_failed:
257     cs_error(link->handle, last_fn, last_ret);
258     aha152x_release_cs(link);
259     return;
260 }
261
262 static void aha152x_release_cs(dev_link_t *link)
263 {
264         scsi_info_t *info = link->priv;
265
266         aha152x_release(info->host);
267         link->dev = NULL;
268     
269         pcmcia_release_configuration(link->handle);
270         pcmcia_release_io(link->handle, &link->io);
271         pcmcia_release_irq(link->handle, &link->irq);
272     
273         link->state &= ~DEV_CONFIG;
274 }
275
276 static int aha152x_event(event_t event, int priority,
277                          event_callback_args_t *args)
278 {
279     dev_link_t *link = args->client_data;
280     scsi_info_t *info = link->priv;
281     
282     DEBUG(0, "aha152x_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             aha152x_release_cs(link);
289         break;
290     case CS_EVENT_CARD_INSERTION:
291         link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
292         aha152x_config_cs(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             Scsi_Cmnd tmp;
307             pcmcia_request_configuration(link->handle, &link->conf);
308             tmp.device->host = info->host;
309             aha152x_host_reset(&tmp);
310         }
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         .detach         = aha152x_detach,
334         .id_table       = aha152x_ids,
335 };
336
337 static int __init init_aha152x_cs(void)
338 {
339         return pcmcia_register_driver(&aha152x_cs_driver);
340 }
341
342 static void __exit exit_aha152x_cs(void)
343 {
344         pcmcia_unregister_driver(&aha152x_cs_driver);
345         BUG_ON(dev_list != NULL);
346 }
347
348 module_init(init_aha152x_cs);
349 module_exit(exit_aha152x_cs);
350