diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/s390/block/dasd_diag.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/s390/block/dasd_diag.c')
-rw-r--r-- | drivers/s390/block/dasd_diag.c | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c new file mode 100644 index 000000000000..127699830fa1 --- /dev/null +++ b/drivers/s390/block/dasd_diag.c | |||
@@ -0,0 +1,541 @@ | |||
1 | /* | ||
2 | * File...........: linux/drivers/s390/block/dasd_diag.c | ||
3 | * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> | ||
4 | * Based on.......: linux/drivers/s390/block/mdisk.c | ||
5 | * ...............: by Hartmunt Penner <hpenner@de.ibm.com> | ||
6 | * Bugreports.to..: <Linux390@de.ibm.com> | ||
7 | * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 | ||
8 | * | ||
9 | * $Revision: 1.42 $ | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/stddef.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/hdreg.h> /* HDIO_GETGEO */ | ||
17 | #include <linux/bio.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/init.h> | ||
20 | |||
21 | #include <asm/dasd.h> | ||
22 | #include <asm/debug.h> | ||
23 | #include <asm/ebcdic.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/s390_ext.h> | ||
26 | #include <asm/todclk.h> | ||
27 | |||
28 | #include "dasd_int.h" | ||
29 | #include "dasd_diag.h" | ||
30 | |||
31 | #ifdef PRINTK_HEADER | ||
32 | #undef PRINTK_HEADER | ||
33 | #endif /* PRINTK_HEADER */ | ||
34 | #define PRINTK_HEADER "dasd(diag):" | ||
35 | |||
36 | MODULE_LICENSE("GPL"); | ||
37 | |||
38 | struct dasd_discipline dasd_diag_discipline; | ||
39 | |||
40 | struct dasd_diag_private { | ||
41 | struct dasd_diag_characteristics rdc_data; | ||
42 | struct dasd_diag_rw_io iob; | ||
43 | struct dasd_diag_init_io iib; | ||
44 | unsigned int pt_block; | ||
45 | }; | ||
46 | |||
47 | struct dasd_diag_req { | ||
48 | int block_count; | ||
49 | struct dasd_diag_bio bio[0]; | ||
50 | }; | ||
51 | |||
52 | static __inline__ int | ||
53 | dia250(void *iob, int cmd) | ||
54 | { | ||
55 | int rc; | ||
56 | |||
57 | __asm__ __volatile__(" lhi %0,3\n" | ||
58 | " lr 0,%2\n" | ||
59 | " diag 0,%1,0x250\n" | ||
60 | "0: ipm %0\n" | ||
61 | " srl %0,28\n" | ||
62 | " or %0,1\n" | ||
63 | "1:\n" | ||
64 | #ifndef CONFIG_ARCH_S390X | ||
65 | ".section __ex_table,\"a\"\n" | ||
66 | " .align 4\n" | ||
67 | " .long 0b,1b\n" | ||
68 | ".previous\n" | ||
69 | #else | ||
70 | ".section __ex_table,\"a\"\n" | ||
71 | " .align 8\n" | ||
72 | " .quad 0b,1b\n" | ||
73 | ".previous\n" | ||
74 | #endif | ||
75 | : "=&d" (rc) | ||
76 | : "d" (cmd), "d" ((void *) __pa(iob)) | ||
77 | : "0", "1", "cc"); | ||
78 | return rc; | ||
79 | } | ||
80 | |||
81 | static __inline__ int | ||
82 | mdsk_init_io(struct dasd_device * device, int blocksize, int offset, int size) | ||
83 | { | ||
84 | struct dasd_diag_private *private; | ||
85 | struct dasd_diag_init_io *iib; | ||
86 | int rc; | ||
87 | |||
88 | private = (struct dasd_diag_private *) device->private; | ||
89 | iib = &private->iib; | ||
90 | memset(iib, 0, sizeof (struct dasd_diag_init_io)); | ||
91 | |||
92 | iib->dev_nr = _ccw_device_get_device_number(device->cdev); | ||
93 | iib->block_size = blocksize; | ||
94 | iib->offset = offset; | ||
95 | iib->start_block = 0; | ||
96 | iib->end_block = size; | ||
97 | |||
98 | rc = dia250(iib, INIT_BIO); | ||
99 | |||
100 | return rc & 3; | ||
101 | } | ||
102 | |||
103 | static __inline__ int | ||
104 | mdsk_term_io(struct dasd_device * device) | ||
105 | { | ||
106 | struct dasd_diag_private *private; | ||
107 | struct dasd_diag_init_io *iib; | ||
108 | int rc; | ||
109 | |||
110 | private = (struct dasd_diag_private *) device->private; | ||
111 | iib = &private->iib; | ||
112 | memset(iib, 0, sizeof (struct dasd_diag_init_io)); | ||
113 | iib->dev_nr = _ccw_device_get_device_number(device->cdev); | ||
114 | rc = dia250(iib, TERM_BIO); | ||
115 | return rc & 3; | ||
116 | } | ||
117 | |||
118 | static int | ||
119 | dasd_start_diag(struct dasd_ccw_req * cqr) | ||
120 | { | ||
121 | struct dasd_device *device; | ||
122 | struct dasd_diag_private *private; | ||
123 | struct dasd_diag_req *dreq; | ||
124 | int rc; | ||
125 | |||
126 | device = cqr->device; | ||
127 | private = (struct dasd_diag_private *) device->private; | ||
128 | dreq = (struct dasd_diag_req *) cqr->data; | ||
129 | |||
130 | private->iob.dev_nr = _ccw_device_get_device_number(device->cdev); | ||
131 | private->iob.key = 0; | ||
132 | private->iob.flags = 2; /* do asynchronous io */ | ||
133 | private->iob.block_count = dreq->block_count; | ||
134 | private->iob.interrupt_params = (u32)(addr_t) cqr; | ||
135 | private->iob.bio_list = __pa(dreq->bio); | ||
136 | |||
137 | cqr->startclk = get_clock(); | ||
138 | |||
139 | rc = dia250(&private->iob, RW_BIO); | ||
140 | if (rc > 8) { | ||
141 | DEV_MESSAGE(KERN_WARNING, device, "dia250 returned CC %d", rc); | ||
142 | cqr->status = DASD_CQR_ERROR; | ||
143 | } else if (rc == 0) { | ||
144 | cqr->status = DASD_CQR_DONE; | ||
145 | dasd_schedule_bh(device); | ||
146 | } else { | ||
147 | cqr->status = DASD_CQR_IN_IO; | ||
148 | rc = 0; | ||
149 | } | ||
150 | return rc; | ||
151 | } | ||
152 | |||
153 | static void | ||
154 | dasd_ext_handler(struct pt_regs *regs, __u16 code) | ||
155 | { | ||
156 | struct dasd_ccw_req *cqr, *next; | ||
157 | struct dasd_device *device; | ||
158 | unsigned long long expires; | ||
159 | unsigned long flags; | ||
160 | char status; | ||
161 | int ip; | ||
162 | |||
163 | /* | ||
164 | * Get the external interruption subcode. VM stores | ||
165 | * this in the 'cpu address' field associated with | ||
166 | * the external interrupt. For diag 250 the subcode | ||
167 | * needs to be 3. | ||
168 | */ | ||
169 | if ((S390_lowcore.cpu_addr & 0xff00) != 0x0300) | ||
170 | return; | ||
171 | status = *((char *) &S390_lowcore.ext_params + 5); | ||
172 | ip = S390_lowcore.ext_params; | ||
173 | |||
174 | if (!ip) { /* no intparm: unsolicited interrupt */ | ||
175 | MESSAGE(KERN_DEBUG, "%s", "caught unsolicited interrupt"); | ||
176 | return; | ||
177 | } | ||
178 | cqr = (struct dasd_ccw_req *)(addr_t) ip; | ||
179 | device = (struct dasd_device *) cqr->device; | ||
180 | if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { | ||
181 | DEV_MESSAGE(KERN_WARNING, device, | ||
182 | " magic number of dasd_ccw_req 0x%08X doesn't" | ||
183 | " match discipline 0x%08X", | ||
184 | cqr->magic, *(int *) (&device->discipline->name)); | ||
185 | return; | ||
186 | } | ||
187 | |||
188 | /* get irq lock to modify request queue */ | ||
189 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | ||
190 | |||
191 | cqr->stopclk = get_clock(); | ||
192 | |||
193 | expires = 0; | ||
194 | if (status == 0) { | ||
195 | cqr->status = DASD_CQR_DONE; | ||
196 | /* Start first request on queue if possible -> fast_io. */ | ||
197 | if (!list_empty(&device->ccw_queue)) { | ||
198 | next = list_entry(device->ccw_queue.next, | ||
199 | struct dasd_ccw_req, list); | ||
200 | if (next->status == DASD_CQR_QUEUED) { | ||
201 | if (dasd_start_diag(next) == 0) | ||
202 | expires = next->expires; | ||
203 | else | ||
204 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
205 | "Interrupt fastpath " | ||
206 | "failed!"); | ||
207 | } | ||
208 | } | ||
209 | } else | ||
210 | cqr->status = DASD_CQR_FAILED; | ||
211 | |||
212 | if (expires != 0) | ||
213 | dasd_set_timer(device, expires); | ||
214 | else | ||
215 | dasd_clear_timer(device); | ||
216 | dasd_schedule_bh(device); | ||
217 | |||
218 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | ||
219 | } | ||
220 | |||
221 | static int | ||
222 | dasd_diag_check_device(struct dasd_device *device) | ||
223 | { | ||
224 | struct dasd_diag_private *private; | ||
225 | struct dasd_diag_characteristics *rdc_data; | ||
226 | struct dasd_diag_bio bio; | ||
227 | long *label; | ||
228 | int sb, bsize; | ||
229 | int rc; | ||
230 | |||
231 | private = (struct dasd_diag_private *) device->private; | ||
232 | if (private == NULL) { | ||
233 | private = kmalloc(sizeof(struct dasd_diag_private),GFP_KERNEL); | ||
234 | if (private == NULL) { | ||
235 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
236 | "memory allocation failed for private data"); | ||
237 | return -ENOMEM; | ||
238 | } | ||
239 | device->private = (void *) private; | ||
240 | } | ||
241 | /* Read Device Characteristics */ | ||
242 | rdc_data = (void *) &(private->rdc_data); | ||
243 | rdc_data->dev_nr = _ccw_device_get_device_number(device->cdev); | ||
244 | rdc_data->rdc_len = sizeof (struct dasd_diag_characteristics); | ||
245 | |||
246 | rc = diag210((struct diag210 *) rdc_data); | ||
247 | if (rc) | ||
248 | return -ENOTSUPP; | ||
249 | |||
250 | /* Figure out position of label block */ | ||
251 | switch (private->rdc_data.vdev_class) { | ||
252 | case DEV_CLASS_FBA: | ||
253 | private->pt_block = 1; | ||
254 | break; | ||
255 | case DEV_CLASS_ECKD: | ||
256 | private->pt_block = 2; | ||
257 | break; | ||
258 | default: | ||
259 | return -ENOTSUPP; | ||
260 | } | ||
261 | |||
262 | DBF_DEV_EVENT(DBF_INFO, device, | ||
263 | "%04X: %04X on real %04X/%02X", | ||
264 | rdc_data->dev_nr, | ||
265 | rdc_data->vdev_type, | ||
266 | rdc_data->rdev_type, rdc_data->rdev_model); | ||
267 | |||
268 | /* terminate all outstanding operations */ | ||
269 | mdsk_term_io(device); | ||
270 | |||
271 | /* figure out blocksize of device */ | ||
272 | label = (long *) get_zeroed_page(GFP_KERNEL); | ||
273 | if (label == NULL) { | ||
274 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
275 | "No memory to allocate initialization request"); | ||
276 | return -ENOMEM; | ||
277 | } | ||
278 | /* try all sizes - needed for ECKD devices */ | ||
279 | for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) { | ||
280 | mdsk_init_io(device, bsize, 0, 64); | ||
281 | memset(&bio, 0, sizeof (struct dasd_diag_bio)); | ||
282 | bio.type = MDSK_READ_REQ; | ||
283 | bio.block_number = private->pt_block + 1; | ||
284 | bio.buffer = __pa(label); | ||
285 | memset(&private->iob, 0, sizeof (struct dasd_diag_rw_io)); | ||
286 | private->iob.dev_nr = rdc_data->dev_nr; | ||
287 | private->iob.key = 0; | ||
288 | private->iob.flags = 0; /* do synchronous io */ | ||
289 | private->iob.block_count = 1; | ||
290 | private->iob.interrupt_params = 0; | ||
291 | private->iob.bio_list = __pa(&bio); | ||
292 | if (dia250(&private->iob, RW_BIO) == 0) | ||
293 | break; | ||
294 | mdsk_term_io(device); | ||
295 | } | ||
296 | if (bsize <= PAGE_SIZE && label[0] == 0xc3d4e2f1) { | ||
297 | /* get formatted blocksize from label block */ | ||
298 | bsize = (int) label[3]; | ||
299 | device->blocks = label[7]; | ||
300 | device->bp_block = bsize; | ||
301 | device->s2b_shift = 0; /* bits to shift 512 to get a block */ | ||
302 | for (sb = 512; sb < bsize; sb = sb << 1) | ||
303 | device->s2b_shift++; | ||
304 | |||
305 | DEV_MESSAGE(KERN_INFO, device, | ||
306 | "capacity (%dkB blks): %ldkB", | ||
307 | (device->bp_block >> 10), | ||
308 | (device->blocks << device->s2b_shift) >> 1); | ||
309 | rc = 0; | ||
310 | } else { | ||
311 | if (bsize > PAGE_SIZE) | ||
312 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
313 | "DIAG access failed"); | ||
314 | else | ||
315 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
316 | "volume is not CMS formatted"); | ||
317 | rc = -EMEDIUMTYPE; | ||
318 | } | ||
319 | free_page((long) label); | ||
320 | return rc; | ||
321 | } | ||
322 | |||
323 | static int | ||
324 | dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) | ||
325 | { | ||
326 | if (dasd_check_blocksize(device->bp_block) != 0) | ||
327 | return -EINVAL; | ||
328 | geo->cylinders = (device->blocks << device->s2b_shift) >> 10; | ||
329 | geo->heads = 16; | ||
330 | geo->sectors = 128 >> device->s2b_shift; | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static dasd_era_t | ||
335 | dasd_diag_examine_error(struct dasd_ccw_req * cqr, struct irb * stat) | ||
336 | { | ||
337 | return dasd_era_fatal; | ||
338 | } | ||
339 | |||
340 | static dasd_erp_fn_t | ||
341 | dasd_diag_erp_action(struct dasd_ccw_req * cqr) | ||
342 | { | ||
343 | return dasd_default_erp_action; | ||
344 | } | ||
345 | |||
346 | static dasd_erp_fn_t | ||
347 | dasd_diag_erp_postaction(struct dasd_ccw_req * cqr) | ||
348 | { | ||
349 | return dasd_default_erp_postaction; | ||
350 | } | ||
351 | |||
352 | static struct dasd_ccw_req * | ||
353 | dasd_diag_build_cp(struct dasd_device * device, struct request *req) | ||
354 | { | ||
355 | struct dasd_ccw_req *cqr; | ||
356 | struct dasd_diag_req *dreq; | ||
357 | struct dasd_diag_bio *dbio; | ||
358 | struct bio *bio; | ||
359 | struct bio_vec *bv; | ||
360 | char *dst; | ||
361 | int count, datasize; | ||
362 | sector_t recid, first_rec, last_rec; | ||
363 | unsigned blksize, off; | ||
364 | unsigned char rw_cmd; | ||
365 | int i; | ||
366 | |||
367 | if (rq_data_dir(req) == READ) | ||
368 | rw_cmd = MDSK_READ_REQ; | ||
369 | else if (rq_data_dir(req) == WRITE) | ||
370 | rw_cmd = MDSK_WRITE_REQ; | ||
371 | else | ||
372 | return ERR_PTR(-EINVAL); | ||
373 | blksize = device->bp_block; | ||
374 | /* Calculate record id of first and last block. */ | ||
375 | first_rec = req->sector >> device->s2b_shift; | ||
376 | last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift; | ||
377 | /* Check struct bio and count the number of blocks for the request. */ | ||
378 | count = 0; | ||
379 | rq_for_each_bio(bio, req) { | ||
380 | bio_for_each_segment(bv, bio, i) { | ||
381 | if (bv->bv_len & (blksize - 1)) | ||
382 | /* Fba can only do full blocks. */ | ||
383 | return ERR_PTR(-EINVAL); | ||
384 | count += bv->bv_len >> (device->s2b_shift + 9); | ||
385 | } | ||
386 | } | ||
387 | /* Paranoia. */ | ||
388 | if (count != last_rec - first_rec + 1) | ||
389 | return ERR_PTR(-EINVAL); | ||
390 | /* Build the request */ | ||
391 | datasize = sizeof(struct dasd_diag_req) + | ||
392 | count*sizeof(struct dasd_diag_bio); | ||
393 | cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0, | ||
394 | datasize, device); | ||
395 | if (IS_ERR(cqr)) | ||
396 | return cqr; | ||
397 | |||
398 | dreq = (struct dasd_diag_req *) cqr->data; | ||
399 | dreq->block_count = count; | ||
400 | dbio = dreq->bio; | ||
401 | recid = first_rec; | ||
402 | rq_for_each_bio(bio, req) { | ||
403 | bio_for_each_segment(bv, bio, i) { | ||
404 | dst = page_address(bv->bv_page) + bv->bv_offset; | ||
405 | for (off = 0; off < bv->bv_len; off += blksize) { | ||
406 | memset(dbio, 0, sizeof (struct dasd_diag_bio)); | ||
407 | dbio->type = rw_cmd; | ||
408 | dbio->block_number = recid + 1; | ||
409 | dbio->buffer = __pa(dst); | ||
410 | dbio++; | ||
411 | dst += blksize; | ||
412 | recid++; | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | cqr->buildclk = get_clock(); | ||
417 | cqr->device = device; | ||
418 | cqr->expires = 50 * HZ; /* 50 seconds */ | ||
419 | cqr->status = DASD_CQR_FILLED; | ||
420 | return cqr; | ||
421 | } | ||
422 | |||
423 | static int | ||
424 | dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) | ||
425 | { | ||
426 | int status; | ||
427 | |||
428 | status = cqr->status == DASD_CQR_DONE; | ||
429 | dasd_sfree_request(cqr, cqr->device); | ||
430 | return status; | ||
431 | } | ||
432 | |||
433 | static int | ||
434 | dasd_diag_fill_info(struct dasd_device * device, | ||
435 | struct dasd_information2_t * info) | ||
436 | { | ||
437 | struct dasd_diag_private *private; | ||
438 | |||
439 | private = (struct dasd_diag_private *) device->private; | ||
440 | info->label_block = private->pt_block; | ||
441 | info->FBA_layout = 1; | ||
442 | info->format = DASD_FORMAT_LDL; | ||
443 | info->characteristics_size = sizeof (struct dasd_diag_characteristics); | ||
444 | memcpy(info->characteristics, | ||
445 | &((struct dasd_diag_private *) device->private)->rdc_data, | ||
446 | sizeof (struct dasd_diag_characteristics)); | ||
447 | info->confdata_size = 0; | ||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | static void | ||
452 | dasd_diag_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, | ||
453 | struct irb *stat) | ||
454 | { | ||
455 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
456 | "dump sense not available for DIAG data"); | ||
457 | } | ||
458 | |||
459 | /* | ||
460 | * max_blocks is dependent on the amount of storage that is available | ||
461 | * in the static io buffer for each device. Currently each device has | ||
462 | * 8192 bytes (=2 pages). dasd diag is only relevant for 31 bit. | ||
463 | * The struct dasd_ccw_req has 96 bytes, the struct dasd_diag_req has | ||
464 | * 8 bytes and the struct dasd_diag_bio for each block has 16 bytes. | ||
465 | * That makes: | ||
466 | * (8192 - 96 - 8) / 16 = 505.5 blocks at maximum. | ||
467 | * We want to fit two into the available memory so that we can immediately | ||
468 | * start the next request if one finishes off. That makes 252.75 blocks | ||
469 | * for one request. Give a little safety and the result is 240. | ||
470 | */ | ||
471 | struct dasd_discipline dasd_diag_discipline = { | ||
472 | .owner = THIS_MODULE, | ||
473 | .name = "DIAG", | ||
474 | .ebcname = "DIAG", | ||
475 | .max_blocks = 240, | ||
476 | .check_device = dasd_diag_check_device, | ||
477 | .fill_geometry = dasd_diag_fill_geometry, | ||
478 | .start_IO = dasd_start_diag, | ||
479 | .examine_error = dasd_diag_examine_error, | ||
480 | .erp_action = dasd_diag_erp_action, | ||
481 | .erp_postaction = dasd_diag_erp_postaction, | ||
482 | .build_cp = dasd_diag_build_cp, | ||
483 | .free_cp = dasd_diag_free_cp, | ||
484 | .dump_sense = dasd_diag_dump_sense, | ||
485 | .fill_info = dasd_diag_fill_info, | ||
486 | }; | ||
487 | |||
488 | static int __init | ||
489 | dasd_diag_init(void) | ||
490 | { | ||
491 | if (!MACHINE_IS_VM) { | ||
492 | MESSAGE_LOG(KERN_INFO, | ||
493 | "Machine is not VM: %s " | ||
494 | "discipline not initializing", | ||
495 | dasd_diag_discipline.name); | ||
496 | return -EINVAL; | ||
497 | } | ||
498 | ASCEBC(dasd_diag_discipline.ebcname, 4); | ||
499 | |||
500 | ctl_set_bit(0, 9); | ||
501 | register_external_interrupt(0x2603, dasd_ext_handler); | ||
502 | dasd_diag_discipline_pointer = &dasd_diag_discipline; | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static void __exit | ||
507 | dasd_diag_cleanup(void) | ||
508 | { | ||
509 | if (!MACHINE_IS_VM) { | ||
510 | MESSAGE_LOG(KERN_INFO, | ||
511 | "Machine is not VM: %s " | ||
512 | "discipline not cleaned", | ||
513 | dasd_diag_discipline.name); | ||
514 | return; | ||
515 | } | ||
516 | unregister_external_interrupt(0x2603, dasd_ext_handler); | ||
517 | ctl_clear_bit(0, 9); | ||
518 | dasd_diag_discipline_pointer = NULL; | ||
519 | } | ||
520 | |||
521 | module_init(dasd_diag_init); | ||
522 | module_exit(dasd_diag_cleanup); | ||
523 | |||
524 | /* | ||
525 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
526 | * Emacs will notice this stuff at the end of the file and automatically | ||
527 | * adjust the settings for this buffer only. This must remain at the end | ||
528 | * of the file. | ||
529 | * --------------------------------------------------------------------------- | ||
530 | * Local variables: | ||
531 | * c-indent-level: 4 | ||
532 | * c-brace-imaginary-offset: 0 | ||
533 | * c-brace-offset: -4 | ||
534 | * c-argdecl-indent: 4 | ||
535 | * c-label-offset: -4 | ||
536 | * c-continued-statement-offset: 4 | ||
537 | * c-continued-brace-offset: 0 | ||
538 | * indent-tabs-mode: 1 | ||
539 | * tab-width: 8 | ||
540 | * End: | ||
541 | */ | ||