1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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/blk.h>
44
45#include <../drivers/scsi/scsi.h>
46#include <../drivers/scsi/hosts.h>
47#include <scsi/scsi_ioctl.h>
48#include <../drivers/scsi/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
59
60MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
61MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
62MODULE_LICENSE("Dual MPL/GPL");
63
64#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
65
66
67INT_MODULE_PARM(irq_mask, 0xdeb8);
68static int irq_list[4] = { -1 };
69MODULE_PARM(irq_list, "1-4i");
70
71#ifdef PCMCIA_DEBUG
72INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
73#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
74static 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
82typedef struct scsi_info_t {
83 dev_link_t link;
84 int ndev;
85 dev_node_t node[8];
86} scsi_info_t;
87
88extern void fdomain_setup(char *str, int *ints);
89
90static void fdomain_release(u_long arg);
91static int fdomain_event(event_t event, int priority,
92 event_callback_args_t *args);
93
94static dev_link_t *fdomain_attach(void);
95static void fdomain_detach(dev_link_t *);
96
97static Scsi_Host_Template driver_template = FDOMAIN_16X0;
98
99static dev_link_t *dev_list = NULL;
100
101static dev_info_t dev_info = "fdomain_cs";
102
103
104
105static void cs_error(client_handle_t handle, int func, int ret)
106{
107 error_info_t err = { func, ret };
108 CardServices(ReportError, handle, &err);
109}
110
111
112
113static dev_link_t *fdomain_attach(void)
114{
115 scsi_info_t *info;
116 client_reg_t client_reg;
117 dev_link_t *link;
118 int i, ret;
119
120 DEBUG(0, "fdomain_attach()\n");
121
122
123 info = kmalloc(sizeof(*info), GFP_KERNEL);
124 if (!info) return NULL;
125 memset(info, 0, sizeof(*info));
126 link = &info->link; link->priv = info;
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
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}
164
165
166
167static 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
174 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
175 if (*linkp == link) break;
176 if (*linkp == NULL)
177 return;
178
179 if (link->state & DEV_CONFIG) {
180 fdomain_release((u_long)link);
181 if (link->state & DEV_STALE_CONFIG) {
182 link->state |= DEV_STALE_LINK;
183 return;
184 }
185 }
186
187 if (link->handle)
188 CardServices(DeregisterClient, link->handle);
189
190
191 *linkp = link->next;
192 kfree(link->priv);
193
194}
195
196
197
198#define CS_CHECK(fn, args...) \
199while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
200
201#define CFG_CHECK(fn, args...) \
202if (CardServices(fn, args) != 0) goto next_entry
203
204static void fdomain_config(dev_link_t *link)
205{
206 client_handle_t handle = link->handle;
207 scsi_info_t *info = link->priv;
208 tuple_t tuple;
209 cisparse_t parse;
210 int i, last_ret, last_fn, ints[3];
211 u_char tuple_data[64];
212 Scsi_Device *dev;
213 dev_node_t *node, **tail;
214 char str[16];
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
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
249 release_region(link->io.BasePort1, link->io.NumPorts1);
250
251
252 ints[0] = 2;
253 ints[1] = link->io.BasePort1;
254 ints[2] = link->irq.AssignedIRQ;
255 sprintf(str, "%d,%d", link->io.BasePort1, link->irq.AssignedIRQ);
256 fdomain_setup(str, ints);
257
258 scsi_register_module(MODULE_SCSI_HA, &driver_template);
259
260 tail = &link->dev;
261 info->ndev = 0;
262 for (host = scsi_hostlist; host; host = host->next)
263 if (host->hostt == &driver_template)
264 for (dev = host->host_queue; dev; dev = dev->next) {
265 u_long arg[2], id;
266 kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg);
267 id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) +
268 ((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000);
269 node = &info->node[info->ndev];
270 node->minor = 0;
271 switch (dev->type) {
272 case TYPE_TAPE:
273 node->major = SCSI_TAPE_MAJOR;
274 sprintf(node->dev_name, "st#%04lx", id);
275 break;
276 case TYPE_DISK:
277 case TYPE_MOD:
278 node->major = SCSI_DISK0_MAJOR;
279 sprintf(node->dev_name, "sd#%04lx", id);
280 break;
281 case TYPE_ROM:
282 case TYPE_WORM:
283 node->major = SCSI_CDROM_MAJOR;
284 sprintf(node->dev_name, "sr#%04lx", id);
285 break;
286 default:
287 node->major = SCSI_GENERIC_MAJOR;
288 sprintf(node->dev_name, "sg#%04lx", id);
289 break;
290 }
291 *tail = node; tail = &node->next;
292 info->ndev++;
293 }
294 *tail = NULL;
295 if (info->ndev == 0)
296 printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
297
298 link->state &= ~DEV_CONFIG_PENDING;
299 return;
300
301cs_failed:
302 cs_error(link->handle, last_fn, last_ret);
303 fdomain_release((u_long)link);
304 return;
305
306}
307
308
309
310static void fdomain_release(u_long arg)
311{
312 dev_link_t *link = (dev_link_t *)arg;
313
314 DEBUG(0, "fdomain_release(0x%p)\n", link);
315
316 if (GET_USE_COUNT(&__this_module) != 0) {
317 DEBUG(1, "fdomain_cs: release postponed, "
318 "device still open\n");
319 link->state |= DEV_STALE_CONFIG;
320 return;
321 }
322
323 scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
324 link->dev = NULL;
325
326 CardServices(ReleaseConfiguration, link->handle);
327 CardServices(ReleaseIO, link->handle, &link->io);
328 CardServices(ReleaseIRQ, link->handle, &link->irq);
329
330 link->state &= ~DEV_CONFIG;
331 if (link->state & DEV_STALE_LINK)
332 fdomain_detach(link);
333
334}
335
336
337
338static int fdomain_event(event_t event, int priority,
339 event_callback_args_t *args)
340{
341 dev_link_t *link = args->client_data;
342
343 DEBUG(1, "fdomain_event(0x%06x)\n", event);
344
345 switch (event) {
346 case CS_EVENT_CARD_REMOVAL:
347 link->state &= ~DEV_PRESENT;
348 if (link->state & DEV_CONFIG)
349 fdomain_release((u_long)link);
350 break;
351 case CS_EVENT_CARD_INSERTION:
352 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
353 fdomain_config(link);
354 break;
355 case CS_EVENT_PM_SUSPEND:
356 link->state |= DEV_SUSPEND;
357
358 case CS_EVENT_RESET_PHYSICAL:
359 if (link->state & DEV_CONFIG)
360 CardServices(ReleaseConfiguration, link->handle);
361 break;
362 case CS_EVENT_PM_RESUME:
363 link->state &= ~DEV_SUSPEND;
364
365 case CS_EVENT_CARD_RESET:
366 if (link->state & DEV_CONFIG) {
367 CardServices(RequestConfiguration, link->handle, &link->conf);
368 fdomain_16x0_reset(NULL, 0);
369 }
370 break;
371 }
372 return 0;
373}
374
375
376
377static int __init init_fdomain_cs(void) {
378 servinfo_t serv;
379 DEBUG(0, "%s\n", version);
380 CardServices(GetCardServicesInfo, &serv);
381 if (serv.Revision != CS_RELEASE_CODE) {
382 printk(KERN_NOTICE "fdomain_cs: Card Services release "
383 "does not match!\n");
384 return -1;
385 }
386 register_pccard_driver(&dev_info, &fdomain_attach, &fdomain_detach);
387 return 0;
388}
389
390static void __exit exit_fdomain_cs(void) {
391 DEBUG(0, "fdomain_cs: unloading\n");
392 unregister_pccard_driver(&dev_info);
393 while (dev_list != NULL)
394 fdomain_detach(dev_list);
395}
396
397module_init(init_fdomain_cs);
398module_exit(exit_fdomain_cs);
399