File: /usr/src/linux/drivers/scsi/pcmcia/fdomain_stub.c
1 /*======================================================================
2
3 A driver for Future Domain-compatible PCMCIA SCSI cards
4
5 fdomain_cs.c 1.43 2000/06/12 21:27:25
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 which
23 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/timer.h>
41 #include <linux/ioport.h>
42 #include <scsi/scsi.h>
43 #include <linux/major.h>
44 #include <linux/blk.h>
45
46 #include <../drivers/scsi/scsi.h>
47 #include <../drivers/scsi/hosts.h>
48 #include <scsi/scsi_ioctl.h>
49 #include <../drivers/scsi/fdomain.h>
50
51 #include <pcmcia/version.h>
52 #include <pcmcia/cs_types.h>
53 #include <pcmcia/cs.h>
54 #include <pcmcia/cistpl.h>
55 #include <pcmcia/ds.h>
56
57 #ifdef PCMCIA_DEBUG
58 static int pc_debug = PCMCIA_DEBUG;
59 MODULE_PARM(pc_debug, "i");
60 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
61 static char *version =
62 "fdomain_cs.c 1.43 2000/06/12 21:27:25 (David Hinds)";
63 #else
64 #define DEBUG(n, args...)
65 #endif
66
67 /*====================================================================*/
68
69 /* Parameters that can be set with 'insmod' */
70
71 /* Bit map of interrupts to choose from */
72 static u_int irq_mask = 0xdeb8;
73 static int irq_list[4] = { -1 };
74
75 MODULE_PARM(irq_mask, "i");
76 MODULE_PARM(irq_list, "1-4i");
77
78 /*====================================================================*/
79
80 typedef struct scsi_info_t {
81 dev_link_t link;
82 int ndev;
83 dev_node_t node[8];
84 } scsi_info_t;
85
86 extern void fdomain_setup(char *str, int *ints);
87
88 static void fdomain_release(u_long arg);
89 static int fdomain_event(event_t event, int priority,
90 event_callback_args_t *args);
91
92 static dev_link_t *fdomain_attach(void);
93 static void fdomain_detach(dev_link_t *);
94
95 static Scsi_Host_Template driver_template = FDOMAIN_16X0;
96
97 static dev_link_t *dev_list = NULL;
98
99 static dev_info_t dev_info = "fdomain_cs";
100
101 /*====================================================================*/
102
103 static void cs_error(client_handle_t handle, int func, int ret)
104 {
105 error_info_t err = { func, ret };
106 CardServices(ReportError, handle, &err);
107 }
108
109 /*====================================================================*/
110
111 static dev_link_t *fdomain_attach(void)
112 {
113 scsi_info_t *info;
114 client_reg_t client_reg;
115 dev_link_t *link;
116 int i, ret;
117
118 DEBUG(0, "fdomain_attach()\n");
119
120 /* Create new SCSI device */
121 info = kmalloc(sizeof(*info), GFP_KERNEL);
122 if (!info) return NULL;
123 memset(info, 0, sizeof(*info));
124 link = &info->link; link->priv = info;
125 link->release.function = &fdomain_release;
126 link->release.data = (u_long)link;
127
128 link->io.NumPorts1 = 0x10;
129 link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
130 link->io.IOAddrLines = 10;
131 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
132 link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
133 if (irq_list[0] == -1)
134 link->irq.IRQInfo2 = irq_mask;
135 else
136 for (i = 0; i < 4; i++)
137 link->irq.IRQInfo2 |= 1 << irq_list[i];
138 link->conf.Attributes = CONF_ENABLE_IRQ;
139 link->conf.Vcc = 50;
140 link->conf.IntType = INT_MEMORY_AND_IO;
141 link->conf.Present = PRESENT_OPTION;
142
143 /* Register with Card Services */
144 link->next = dev_list;
145 dev_list = link;
146 client_reg.dev_info = &dev_info;
147 client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
148 client_reg.event_handler = &fdomain_event;
149 client_reg.EventMask =
150 CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
151 CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
152 CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
153 client_reg.Version = 0x0210;
154 client_reg.event_callback_args.client_data = link;
155 ret = CardServices(RegisterClient, &link->handle, &client_reg);
156 if (ret != 0) {
157 cs_error(link->handle, RegisterClient, ret);
158 fdomain_detach(link);
159 return NULL;
160 }
161
162 return link;
163 } /* fdomain_attach */
164
165 /*====================================================================*/
166
167 static void fdomain_detach(dev_link_t *link)
168 {
169 dev_link_t **linkp;
170
171 DEBUG(0, "fdomain_detach(0x%p)\n", link);
172
173 /* Locate device structure */
174 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
175 if (*linkp == link) break;
176 if (*linkp == NULL)
177 return;
178
179 del_timer(&link->release);
180 if (link->state & DEV_CONFIG) {
181 fdomain_release((u_long)link);
182 if (link->state & DEV_STALE_CONFIG) {
183 link->state |= DEV_STALE_LINK;
184 return;
185 }
186 }
187
188 if (link->handle)
189 CardServices(DeregisterClient, link->handle);
190
191 /* Unlink device structure, free bits */
192 *linkp = link->next;
193 kfree(link->priv);
194
195 } /* fdomain_detach */
196
197 /*====================================================================*/
198
199 #define CS_CHECK(fn, args...) \
200 while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
201
202 #define CFG_CHECK(fn, args...) \
203 if (CardServices(fn, args) != 0) goto next_entry
204
205 static void fdomain_config(dev_link_t *link)
206 {
207 client_handle_t handle = link->handle;
208 scsi_info_t *info = link->priv;
209 tuple_t tuple;
210 cisparse_t parse;
211 int i, last_ret, last_fn, ints[3];
212 u_char tuple_data[64];
213 Scsi_Device *dev;
214 dev_node_t *node, **tail;
215 struct Scsi_Host *host;
216
217 DEBUG(0, "fdomain_config(0x%p)\n", link);
218
219 tuple.DesiredTuple = CISTPL_CONFIG;
220 tuple.TupleData = tuple_data;
221 tuple.TupleDataMax = 64;
222 tuple.TupleOffset = 0;
223 CS_CHECK(GetFirstTuple, handle, &tuple);
224 CS_CHECK(GetTupleData, handle, &tuple);
225 CS_CHECK(ParseTuple, handle, &tuple, &parse);
226 link->conf.ConfigBase = parse.config.base;
227
228 /* Configure card */
229 driver_template.module = &__this_module;
230 link->state |= DEV_CONFIG;
231
232 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
233 CS_CHECK(GetFirstTuple, handle, &tuple);
234 while (1) {
235 CFG_CHECK(GetTupleData, handle, &tuple);
236 CFG_CHECK(ParseTuple, handle, &tuple, &parse);
237 link->conf.ConfigIndex = parse.cftable_entry.index;
238 link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
239 i = CardServices(RequestIO, handle, &link->io);
240 if (i == CS_SUCCESS) break;
241 next_entry:
242 CS_CHECK(GetNextTuple, handle, &tuple);
243 }
244
245 CS_CHECK(RequestIRQ, handle, &link->irq);
246 CS_CHECK(RequestConfiguration, handle, &link->conf);
247
248 /* A bad hack... */
249 release_region(link->io.BasePort1, link->io.NumPorts1);
250
251 /* Set configuration options for the fdomain driver */
252 ints[0] = 2;
253 ints[1] = link->io.BasePort1;
254 ints[2] = link->irq.AssignedIRQ;
255 fdomain_setup("PCMCIA setup", ints);
256
257 scsi_register_module(MODULE_SCSI_HA, &driver_template);
258
259 tail = &link->dev;
260 info->ndev = 0;
261 for (host = scsi_hostlist; host; host = host->next)
262 if (host->hostt == &driver_template)
263 for (dev = host->host_queue; dev; dev = dev->next) {
264 u_long arg[2], id;
265 kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg);
266 id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) +
267 ((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000);
268 node = &info->node[info->ndev];
269 node->minor = 0;
270 switch (dev->type) {
271 case TYPE_TAPE:
272 node->major = SCSI_TAPE_MAJOR;
273 sprintf(node->dev_name, "st#%04lx", id);
274 break;
275 case TYPE_DISK:
276 case TYPE_MOD:
277 node->major = SCSI_DISK0_MAJOR;
278 sprintf(node->dev_name, "sd#%04lx", id);
279 break;
280 case TYPE_ROM:
281 case TYPE_WORM:
282 node->major = SCSI_CDROM_MAJOR;
283 sprintf(node->dev_name, "sr#%04lx", id);
284 break;
285 default:
286 node->major = SCSI_GENERIC_MAJOR;
287 sprintf(node->dev_name, "sg#%04lx", id);
288 break;
289 }
290 *tail = node; tail = &node->next;
291 info->ndev++;
292 }
293 *tail = NULL;
294 if (info->ndev == 0)
295 printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
296
297 link->state &= ~DEV_CONFIG_PENDING;
298 return;
299
300 cs_failed:
301 cs_error(link->handle, last_fn, last_ret);
302 fdomain_release((u_long)link);
303 return;
304
305 } /* fdomain_config */
306
307 /*====================================================================*/
308
309 static void fdomain_release(u_long arg)
310 {
311 dev_link_t *link = (dev_link_t *)arg;
312
313 DEBUG(0, "fdomain_release(0x%p)\n", link);
314
315 if (GET_USE_COUNT(&__this_module) != 0) {
316 DEBUG(1, "fdomain_cs: release postponed, "
317 "device still open\n");
318 link->state |= DEV_STALE_CONFIG;
319 return;
320 }
321
322 scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
323 link->dev = NULL;
324
325 CardServices(ReleaseConfiguration, link->handle);
326 CardServices(ReleaseIO, link->handle, &link->io);
327 CardServices(ReleaseIRQ, link->handle, &link->irq);
328
329 link->state &= ~DEV_CONFIG;
330 if (link->state & DEV_STALE_LINK)
331 fdomain_detach(link);
332
333 } /* fdomain_release */
334
335 /*====================================================================*/
336
337 static int fdomain_event(event_t event, int priority,
338 event_callback_args_t *args)
339 {
340 dev_link_t *link = args->client_data;
341
342 DEBUG(1, "fdomain_event(0x%06x)\n", event);
343
344 switch (event) {
345 case CS_EVENT_CARD_REMOVAL:
346 link->state &= ~DEV_PRESENT;
347 if (link->state & DEV_CONFIG)
348 mod_timer(&link->release, jiffies + HZ/20);
349 break;
350 case CS_EVENT_CARD_INSERTION:
351 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
352 fdomain_config(link);
353 break;
354 case CS_EVENT_PM_SUSPEND:
355 link->state |= DEV_SUSPEND;
356 /* Fall through... */
357 case CS_EVENT_RESET_PHYSICAL:
358 if (link->state & DEV_CONFIG)
359 CardServices(ReleaseConfiguration, link->handle);
360 break;
361 case CS_EVENT_PM_RESUME:
362 link->state &= ~DEV_SUSPEND;
363 /* Fall through... */
364 case CS_EVENT_CARD_RESET:
365 if (link->state & DEV_CONFIG) {
366 CardServices(RequestConfiguration, link->handle, &link->conf);
367 fdomain_16x0_reset(NULL, 0);
368 }
369 break;
370 }
371 return 0;
372 } /* fdomain_event */
373
374 /*====================================================================*/
375
376 static int __init init_fdomain_cs(void) {
377 servinfo_t serv;
378 DEBUG(0, "%s\n", version);
379 CardServices(GetCardServicesInfo, &serv);
380 if (serv.Revision != CS_RELEASE_CODE) {
381 printk(KERN_NOTICE "fdomain_cs: Card Services release "
382 "does not match!\n");
383 return -1;
384 }
385 register_pccard_driver(&dev_info, &fdomain_attach, &fdomain_detach);
386 return 0;
387 }
388
389 static void __exit exit_fdomain_cs(void) {
390 DEBUG(0, "fdomain_cs: unloading\n");
391 unregister_pccard_driver(&dev_info);
392 while (dev_list != NULL)
393 fdomain_detach(dev_list);
394 }
395
396 module_init(init_fdomain_cs);
397 module_exit(exit_fdomain_cs);
398