diff options
Diffstat (limited to 'drivers/s390/char/tape_core.c')
-rw-r--r-- | drivers/s390/char/tape_core.c | 1242 |
1 files changed, 1242 insertions, 0 deletions
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c new file mode 100644 index 00000000000..e51046ab8ad --- /dev/null +++ b/drivers/s390/char/tape_core.c | |||
@@ -0,0 +1,1242 @@ | |||
1 | /* | ||
2 | * drivers/s390/char/tape_core.c | ||
3 | * basic function of the tape device driver | ||
4 | * | ||
5 | * S390 and zSeries version | ||
6 | * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
7 | * Author(s): Carsten Otte <cotte@de.ibm.com> | ||
8 | * Michael Holzheu <holzheu@de.ibm.com> | ||
9 | * Tuan Ngo-Anh <ngoanh@de.ibm.com> | ||
10 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/init.h> // for kernel parameters | ||
16 | #include <linux/kmod.h> // for requesting modules | ||
17 | #include <linux/spinlock.h> // for locks | ||
18 | #include <linux/vmalloc.h> | ||
19 | #include <linux/list.h> | ||
20 | |||
21 | #include <asm/types.h> // for variable types | ||
22 | |||
23 | #define TAPE_DBF_AREA tape_core_dbf | ||
24 | |||
25 | #include "tape.h" | ||
26 | #include "tape_std.h" | ||
27 | |||
28 | #define PRINTK_HEADER "TAPE_CORE: " | ||
29 | |||
30 | static void __tape_do_irq (struct ccw_device *, unsigned long, struct irb *); | ||
31 | static void __tape_remove_request(struct tape_device *, struct tape_request *); | ||
32 | |||
33 | /* | ||
34 | * One list to contain all tape devices of all disciplines, so | ||
35 | * we can assign the devices to minor numbers of the same major | ||
36 | * The list is protected by the rwlock | ||
37 | */ | ||
38 | static struct list_head tape_device_list = LIST_HEAD_INIT(tape_device_list); | ||
39 | static DEFINE_RWLOCK(tape_device_lock); | ||
40 | |||
41 | /* | ||
42 | * Pointer to debug area. | ||
43 | */ | ||
44 | debug_info_t *TAPE_DBF_AREA = NULL; | ||
45 | EXPORT_SYMBOL(TAPE_DBF_AREA); | ||
46 | |||
47 | /* | ||
48 | * Printable strings for tape enumerations. | ||
49 | */ | ||
50 | const char *tape_state_verbose[TS_SIZE] = | ||
51 | { | ||
52 | [TS_UNUSED] = "UNUSED", | ||
53 | [TS_IN_USE] = "IN_USE", | ||
54 | [TS_BLKUSE] = "BLKUSE", | ||
55 | [TS_INIT] = "INIT ", | ||
56 | [TS_NOT_OPER] = "NOT_OP" | ||
57 | }; | ||
58 | |||
59 | const char *tape_op_verbose[TO_SIZE] = | ||
60 | { | ||
61 | [TO_BLOCK] = "BLK", [TO_BSB] = "BSB", | ||
62 | [TO_BSF] = "BSF", [TO_DSE] = "DSE", | ||
63 | [TO_FSB] = "FSB", [TO_FSF] = "FSF", | ||
64 | [TO_LBL] = "LBL", [TO_NOP] = "NOP", | ||
65 | [TO_RBA] = "RBA", [TO_RBI] = "RBI", | ||
66 | [TO_RFO] = "RFO", [TO_REW] = "REW", | ||
67 | [TO_RUN] = "RUN", [TO_WRI] = "WRI", | ||
68 | [TO_WTM] = "WTM", [TO_MSEN] = "MSN", | ||
69 | [TO_LOAD] = "LOA", [TO_READ_CONFIG] = "RCF", | ||
70 | [TO_READ_ATTMSG] = "RAT", | ||
71 | [TO_DIS] = "DIS", [TO_ASSIGN] = "ASS", | ||
72 | [TO_UNASSIGN] = "UAS" | ||
73 | }; | ||
74 | |||
75 | static inline int | ||
76 | busid_to_int(char *bus_id) | ||
77 | { | ||
78 | int dec; | ||
79 | int d; | ||
80 | char * s; | ||
81 | |||
82 | for(s = bus_id, d = 0; *s != '\0' && *s != '.'; s++) | ||
83 | d = (d * 10) + (*s - '0'); | ||
84 | dec = d; | ||
85 | for(s++, d = 0; *s != '\0' && *s != '.'; s++) | ||
86 | d = (d * 10) + (*s - '0'); | ||
87 | dec = (dec << 8) + d; | ||
88 | |||
89 | for(s++; *s != '\0'; s++) { | ||
90 | if (*s >= '0' && *s <= '9') { | ||
91 | d = *s - '0'; | ||
92 | } else if (*s >= 'a' && *s <= 'f') { | ||
93 | d = *s - 'a' + 10; | ||
94 | } else { | ||
95 | d = *s - 'A' + 10; | ||
96 | } | ||
97 | dec = (dec << 4) + d; | ||
98 | } | ||
99 | |||
100 | return dec; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * Some channel attached tape specific attributes. | ||
105 | * | ||
106 | * FIXME: In the future the first_minor and blocksize attribute should be | ||
107 | * replaced by a link to the cdev tree. | ||
108 | */ | ||
109 | static ssize_t | ||
110 | tape_medium_state_show(struct device *dev, char *buf) | ||
111 | { | ||
112 | struct tape_device *tdev; | ||
113 | |||
114 | tdev = (struct tape_device *) dev->driver_data; | ||
115 | return scnprintf(buf, PAGE_SIZE, "%i\n", tdev->medium_state); | ||
116 | } | ||
117 | |||
118 | static | ||
119 | DEVICE_ATTR(medium_state, 0444, tape_medium_state_show, NULL); | ||
120 | |||
121 | static ssize_t | ||
122 | tape_first_minor_show(struct device *dev, char *buf) | ||
123 | { | ||
124 | struct tape_device *tdev; | ||
125 | |||
126 | tdev = (struct tape_device *) dev->driver_data; | ||
127 | return scnprintf(buf, PAGE_SIZE, "%i\n", tdev->first_minor); | ||
128 | } | ||
129 | |||
130 | static | ||
131 | DEVICE_ATTR(first_minor, 0444, tape_first_minor_show, NULL); | ||
132 | |||
133 | static ssize_t | ||
134 | tape_state_show(struct device *dev, char *buf) | ||
135 | { | ||
136 | struct tape_device *tdev; | ||
137 | |||
138 | tdev = (struct tape_device *) dev->driver_data; | ||
139 | return scnprintf(buf, PAGE_SIZE, "%s\n", (tdev->first_minor < 0) ? | ||
140 | "OFFLINE" : tape_state_verbose[tdev->tape_state]); | ||
141 | } | ||
142 | |||
143 | static | ||
144 | DEVICE_ATTR(state, 0444, tape_state_show, NULL); | ||
145 | |||
146 | static ssize_t | ||
147 | tape_operation_show(struct device *dev, char *buf) | ||
148 | { | ||
149 | struct tape_device *tdev; | ||
150 | ssize_t rc; | ||
151 | |||
152 | tdev = (struct tape_device *) dev->driver_data; | ||
153 | if (tdev->first_minor < 0) | ||
154 | return scnprintf(buf, PAGE_SIZE, "N/A\n"); | ||
155 | |||
156 | spin_lock_irq(get_ccwdev_lock(tdev->cdev)); | ||
157 | if (list_empty(&tdev->req_queue)) | ||
158 | rc = scnprintf(buf, PAGE_SIZE, "---\n"); | ||
159 | else { | ||
160 | struct tape_request *req; | ||
161 | |||
162 | req = list_entry(tdev->req_queue.next, struct tape_request, | ||
163 | list); | ||
164 | rc = scnprintf(buf,PAGE_SIZE, "%s\n", tape_op_verbose[req->op]); | ||
165 | } | ||
166 | spin_unlock_irq(get_ccwdev_lock(tdev->cdev)); | ||
167 | return rc; | ||
168 | } | ||
169 | |||
170 | static | ||
171 | DEVICE_ATTR(operation, 0444, tape_operation_show, NULL); | ||
172 | |||
173 | static ssize_t | ||
174 | tape_blocksize_show(struct device *dev, char *buf) | ||
175 | { | ||
176 | struct tape_device *tdev; | ||
177 | |||
178 | tdev = (struct tape_device *) dev->driver_data; | ||
179 | |||
180 | return scnprintf(buf, PAGE_SIZE, "%i\n", tdev->char_data.block_size); | ||
181 | } | ||
182 | |||
183 | static | ||
184 | DEVICE_ATTR(blocksize, 0444, tape_blocksize_show, NULL); | ||
185 | |||
186 | static struct attribute *tape_attrs[] = { | ||
187 | &dev_attr_medium_state.attr, | ||
188 | &dev_attr_first_minor.attr, | ||
189 | &dev_attr_state.attr, | ||
190 | &dev_attr_operation.attr, | ||
191 | &dev_attr_blocksize.attr, | ||
192 | NULL | ||
193 | }; | ||
194 | |||
195 | static struct attribute_group tape_attr_group = { | ||
196 | .attrs = tape_attrs, | ||
197 | }; | ||
198 | |||
199 | /* | ||
200 | * Tape state functions | ||
201 | */ | ||
202 | void | ||
203 | tape_state_set(struct tape_device *device, enum tape_state newstate) | ||
204 | { | ||
205 | const char *str; | ||
206 | |||
207 | if (device->tape_state == TS_NOT_OPER) { | ||
208 | DBF_EVENT(3, "ts_set err: not oper\n"); | ||
209 | return; | ||
210 | } | ||
211 | DBF_EVENT(4, "ts. dev: %x\n", device->first_minor); | ||
212 | if (device->tape_state < TO_SIZE && device->tape_state >= 0) | ||
213 | str = tape_state_verbose[device->tape_state]; | ||
214 | else | ||
215 | str = "UNKNOWN TS"; | ||
216 | DBF_EVENT(4, "old ts: %s\n", str); | ||
217 | if (device->tape_state < TO_SIZE && device->tape_state >=0 ) | ||
218 | str = tape_state_verbose[device->tape_state]; | ||
219 | else | ||
220 | str = "UNKNOWN TS"; | ||
221 | DBF_EVENT(4, "%s\n", str); | ||
222 | DBF_EVENT(4, "new ts:\t\n"); | ||
223 | if (newstate < TO_SIZE && newstate >= 0) | ||
224 | str = tape_state_verbose[newstate]; | ||
225 | else | ||
226 | str = "UNKNOWN TS"; | ||
227 | DBF_EVENT(4, "%s\n", str); | ||
228 | device->tape_state = newstate; | ||
229 | wake_up(&device->state_change_wq); | ||
230 | } | ||
231 | |||
232 | void | ||
233 | tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate) | ||
234 | { | ||
235 | if (device->medium_state == newstate) | ||
236 | return; | ||
237 | switch(newstate){ | ||
238 | case MS_UNLOADED: | ||
239 | device->tape_generic_status |= GMT_DR_OPEN(~0); | ||
240 | PRINT_INFO("(%s): Tape is unloaded\n", | ||
241 | device->cdev->dev.bus_id); | ||
242 | break; | ||
243 | case MS_LOADED: | ||
244 | device->tape_generic_status &= ~GMT_DR_OPEN(~0); | ||
245 | PRINT_INFO("(%s): Tape has been mounted\n", | ||
246 | device->cdev->dev.bus_id); | ||
247 | break; | ||
248 | default: | ||
249 | // print nothing | ||
250 | break; | ||
251 | } | ||
252 | device->medium_state = newstate; | ||
253 | wake_up(&device->state_change_wq); | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * Stop running ccw. Has to be called with the device lock held. | ||
258 | */ | ||
259 | static inline int | ||
260 | __tape_halt_io(struct tape_device *device, struct tape_request *request) | ||
261 | { | ||
262 | int retries; | ||
263 | int rc; | ||
264 | |||
265 | /* Check if interrupt has already been processed */ | ||
266 | if (request->callback == NULL) | ||
267 | return 0; | ||
268 | |||
269 | rc = 0; | ||
270 | for (retries = 0; retries < 5; retries++) { | ||
271 | rc = ccw_device_clear(device->cdev, (long) request); | ||
272 | |||
273 | if (rc == 0) { /* Termination successful */ | ||
274 | request->rc = -EIO; | ||
275 | request->status = TAPE_REQUEST_DONE; | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | if (rc == -ENODEV) | ||
280 | DBF_EXCEPTION(2, "device gone, retry\n"); | ||
281 | else if (rc == -EIO) | ||
282 | DBF_EXCEPTION(2, "I/O error, retry\n"); | ||
283 | else if (rc == -EBUSY) | ||
284 | DBF_EXCEPTION(2, "device busy, retry late\n"); | ||
285 | else | ||
286 | BUG(); | ||
287 | } | ||
288 | |||
289 | return rc; | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * Add device into the sorted list, giving it the first | ||
294 | * available minor number. | ||
295 | */ | ||
296 | static int | ||
297 | tape_assign_minor(struct tape_device *device) | ||
298 | { | ||
299 | struct tape_device *tmp; | ||
300 | int minor; | ||
301 | |||
302 | minor = 0; | ||
303 | write_lock(&tape_device_lock); | ||
304 | list_for_each_entry(tmp, &tape_device_list, node) { | ||
305 | if (minor < tmp->first_minor) | ||
306 | break; | ||
307 | minor += TAPE_MINORS_PER_DEV; | ||
308 | } | ||
309 | if (minor >= 256) { | ||
310 | write_unlock(&tape_device_lock); | ||
311 | return -ENODEV; | ||
312 | } | ||
313 | device->first_minor = minor; | ||
314 | list_add_tail(&device->node, &tmp->node); | ||
315 | write_unlock(&tape_device_lock); | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | /* remove device from the list */ | ||
320 | static void | ||
321 | tape_remove_minor(struct tape_device *device) | ||
322 | { | ||
323 | write_lock(&tape_device_lock); | ||
324 | list_del_init(&device->node); | ||
325 | device->first_minor = -1; | ||
326 | write_unlock(&tape_device_lock); | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * Set a device online. | ||
331 | * | ||
332 | * This function is called by the common I/O layer to move a device from the | ||
333 | * detected but offline into the online state. | ||
334 | * If we return an error (RC < 0) the device remains in the offline state. This | ||
335 | * can happen if the device is assigned somewhere else, for example. | ||
336 | */ | ||
337 | int | ||
338 | tape_generic_online(struct tape_device *device, | ||
339 | struct tape_discipline *discipline) | ||
340 | { | ||
341 | int rc; | ||
342 | |||
343 | DBF_LH(6, "tape_enable_device(%p, %p)\n", device, discipline); | ||
344 | |||
345 | if (device->tape_state != TS_INIT) { | ||
346 | DBF_LH(3, "Tapestate not INIT (%d)\n", device->tape_state); | ||
347 | return -EINVAL; | ||
348 | } | ||
349 | |||
350 | /* Let the discipline have a go at the device. */ | ||
351 | device->discipline = discipline; | ||
352 | if (!try_module_get(discipline->owner)) { | ||
353 | PRINT_ERR("Cannot get module. Module gone.\n"); | ||
354 | return -EINVAL; | ||
355 | } | ||
356 | |||
357 | rc = discipline->setup_device(device); | ||
358 | if (rc) | ||
359 | goto out; | ||
360 | rc = tape_assign_minor(device); | ||
361 | if (rc) | ||
362 | goto out_discipline; | ||
363 | |||
364 | rc = tapechar_setup_device(device); | ||
365 | if (rc) | ||
366 | goto out_minor; | ||
367 | rc = tapeblock_setup_device(device); | ||
368 | if (rc) | ||
369 | goto out_char; | ||
370 | |||
371 | tape_state_set(device, TS_UNUSED); | ||
372 | |||
373 | DBF_LH(3, "(%08x): Drive set online\n", device->cdev_id); | ||
374 | |||
375 | return 0; | ||
376 | |||
377 | out_char: | ||
378 | tapechar_cleanup_device(device); | ||
379 | out_discipline: | ||
380 | device->discipline->cleanup_device(device); | ||
381 | device->discipline = NULL; | ||
382 | out_minor: | ||
383 | tape_remove_minor(device); | ||
384 | out: | ||
385 | module_put(discipline->owner); | ||
386 | return rc; | ||
387 | } | ||
388 | |||
389 | static inline void | ||
390 | tape_cleanup_device(struct tape_device *device) | ||
391 | { | ||
392 | tapeblock_cleanup_device(device); | ||
393 | tapechar_cleanup_device(device); | ||
394 | device->discipline->cleanup_device(device); | ||
395 | module_put(device->discipline->owner); | ||
396 | tape_remove_minor(device); | ||
397 | tape_med_state_set(device, MS_UNKNOWN); | ||
398 | } | ||
399 | |||
400 | /* | ||
401 | * Set device offline. | ||
402 | * | ||
403 | * Called by the common I/O layer if the drive should set offline on user | ||
404 | * request. We may prevent this by returning an error. | ||
405 | * Manual offline is only allowed while the drive is not in use. | ||
406 | */ | ||
407 | int | ||
408 | tape_generic_offline(struct tape_device *device) | ||
409 | { | ||
410 | if (!device) { | ||
411 | PRINT_ERR("tape_generic_offline: no such device\n"); | ||
412 | return -ENODEV; | ||
413 | } | ||
414 | |||
415 | DBF_LH(3, "(%08x): tape_generic_offline(%p)\n", | ||
416 | device->cdev_id, device); | ||
417 | |||
418 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | ||
419 | switch (device->tape_state) { | ||
420 | case TS_INIT: | ||
421 | case TS_NOT_OPER: | ||
422 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
423 | break; | ||
424 | case TS_UNUSED: | ||
425 | tape_state_set(device, TS_INIT); | ||
426 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
427 | tape_cleanup_device(device); | ||
428 | break; | ||
429 | default: | ||
430 | DBF_EVENT(3, "(%08x): Set offline failed " | ||
431 | "- drive in use.\n", | ||
432 | device->cdev_id); | ||
433 | PRINT_WARN("(%s): Set offline failed " | ||
434 | "- drive in use.\n", | ||
435 | device->cdev->dev.bus_id); | ||
436 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
437 | return -EBUSY; | ||
438 | } | ||
439 | |||
440 | DBF_LH(3, "(%08x): Drive set offline.\n", device->cdev_id); | ||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | /* | ||
445 | * Allocate memory for a new device structure. | ||
446 | */ | ||
447 | static struct tape_device * | ||
448 | tape_alloc_device(void) | ||
449 | { | ||
450 | struct tape_device *device; | ||
451 | |||
452 | device = (struct tape_device *) | ||
453 | kmalloc(sizeof(struct tape_device), GFP_KERNEL); | ||
454 | if (device == NULL) { | ||
455 | DBF_EXCEPTION(2, "ti:no mem\n"); | ||
456 | PRINT_INFO ("can't allocate memory for " | ||
457 | "tape info structure\n"); | ||
458 | return ERR_PTR(-ENOMEM); | ||
459 | } | ||
460 | memset(device, 0, sizeof(struct tape_device)); | ||
461 | device->modeset_byte = (char *) kmalloc(1, GFP_KERNEL | GFP_DMA); | ||
462 | if (device->modeset_byte == NULL) { | ||
463 | DBF_EXCEPTION(2, "ti:no mem\n"); | ||
464 | PRINT_INFO("can't allocate memory for modeset byte\n"); | ||
465 | kfree(device); | ||
466 | return ERR_PTR(-ENOMEM); | ||
467 | } | ||
468 | INIT_LIST_HEAD(&device->req_queue); | ||
469 | INIT_LIST_HEAD(&device->node); | ||
470 | init_waitqueue_head(&device->state_change_wq); | ||
471 | device->tape_state = TS_INIT; | ||
472 | device->medium_state = MS_UNKNOWN; | ||
473 | *device->modeset_byte = 0; | ||
474 | device->first_minor = -1; | ||
475 | atomic_set(&device->ref_count, 1); | ||
476 | |||
477 | return device; | ||
478 | } | ||
479 | |||
480 | /* | ||
481 | * Get a reference to an existing device structure. This will automatically | ||
482 | * increment the reference count. | ||
483 | */ | ||
484 | struct tape_device * | ||
485 | tape_get_device_reference(struct tape_device *device) | ||
486 | { | ||
487 | DBF_EVENT(4, "tape_get_device_reference(%p) = %i\n", device, | ||
488 | atomic_inc_return(&device->ref_count)); | ||
489 | |||
490 | return device; | ||
491 | } | ||
492 | |||
493 | /* | ||
494 | * Decrease the reference counter of a devices structure. If the | ||
495 | * reference counter reaches zero free the device structure. | ||
496 | * The function returns a NULL pointer to be used by the caller | ||
497 | * for clearing reference pointers. | ||
498 | */ | ||
499 | struct tape_device * | ||
500 | tape_put_device(struct tape_device *device) | ||
501 | { | ||
502 | int remain; | ||
503 | |||
504 | remain = atomic_dec_return(&device->ref_count); | ||
505 | if (remain > 0) { | ||
506 | DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, remain); | ||
507 | } else { | ||
508 | if (remain < 0) { | ||
509 | DBF_EVENT(4, "put device without reference\n"); | ||
510 | PRINT_ERR("put device without reference\n"); | ||
511 | } else { | ||
512 | DBF_EVENT(4, "tape_free_device(%p)\n", device); | ||
513 | kfree(device->modeset_byte); | ||
514 | kfree(device); | ||
515 | } | ||
516 | } | ||
517 | |||
518 | return NULL; | ||
519 | } | ||
520 | |||
521 | /* | ||
522 | * Find tape device by a device index. | ||
523 | */ | ||
524 | struct tape_device * | ||
525 | tape_get_device(int devindex) | ||
526 | { | ||
527 | struct tape_device *device, *tmp; | ||
528 | |||
529 | device = ERR_PTR(-ENODEV); | ||
530 | read_lock(&tape_device_lock); | ||
531 | list_for_each_entry(tmp, &tape_device_list, node) { | ||
532 | if (tmp->first_minor / TAPE_MINORS_PER_DEV == devindex) { | ||
533 | device = tape_get_device_reference(tmp); | ||
534 | break; | ||
535 | } | ||
536 | } | ||
537 | read_unlock(&tape_device_lock); | ||
538 | return device; | ||
539 | } | ||
540 | |||
541 | /* | ||
542 | * Driverfs tape probe function. | ||
543 | */ | ||
544 | int | ||
545 | tape_generic_probe(struct ccw_device *cdev) | ||
546 | { | ||
547 | struct tape_device *device; | ||
548 | |||
549 | device = tape_alloc_device(); | ||
550 | if (IS_ERR(device)) | ||
551 | return -ENODEV; | ||
552 | PRINT_INFO("tape device %s found\n", cdev->dev.bus_id); | ||
553 | cdev->dev.driver_data = device; | ||
554 | device->cdev = cdev; | ||
555 | device->cdev_id = busid_to_int(cdev->dev.bus_id); | ||
556 | cdev->handler = __tape_do_irq; | ||
557 | |||
558 | ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); | ||
559 | sysfs_create_group(&cdev->dev.kobj, &tape_attr_group); | ||
560 | |||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | static inline void | ||
565 | __tape_discard_requests(struct tape_device *device) | ||
566 | { | ||
567 | struct tape_request * request; | ||
568 | struct list_head * l, *n; | ||
569 | |||
570 | list_for_each_safe(l, n, &device->req_queue) { | ||
571 | request = list_entry(l, struct tape_request, list); | ||
572 | if (request->status == TAPE_REQUEST_IN_IO) | ||
573 | request->status = TAPE_REQUEST_DONE; | ||
574 | list_del(&request->list); | ||
575 | |||
576 | /* Decrease ref_count for removed request. */ | ||
577 | request->device = tape_put_device(device); | ||
578 | request->rc = -EIO; | ||
579 | if (request->callback != NULL) | ||
580 | request->callback(request, request->callback_data); | ||
581 | } | ||
582 | } | ||
583 | |||
584 | /* | ||
585 | * Driverfs tape remove function. | ||
586 | * | ||
587 | * This function is called whenever the common I/O layer detects the device | ||
588 | * gone. This can happen at any time and we cannot refuse. | ||
589 | */ | ||
590 | void | ||
591 | tape_generic_remove(struct ccw_device *cdev) | ||
592 | { | ||
593 | struct tape_device * device; | ||
594 | |||
595 | device = cdev->dev.driver_data; | ||
596 | if (!device) { | ||
597 | PRINT_ERR("No device pointer in tape_generic_remove!\n"); | ||
598 | return; | ||
599 | } | ||
600 | DBF_LH(3, "(%08x): tape_generic_remove(%p)\n", device->cdev_id, cdev); | ||
601 | |||
602 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | ||
603 | switch (device->tape_state) { | ||
604 | case TS_INIT: | ||
605 | tape_state_set(device, TS_NOT_OPER); | ||
606 | case TS_NOT_OPER: | ||
607 | /* | ||
608 | * Nothing to do. | ||
609 | */ | ||
610 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
611 | break; | ||
612 | case TS_UNUSED: | ||
613 | /* | ||
614 | * Need only to release the device. | ||
615 | */ | ||
616 | tape_state_set(device, TS_NOT_OPER); | ||
617 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
618 | tape_cleanup_device(device); | ||
619 | break; | ||
620 | default: | ||
621 | /* | ||
622 | * There may be requests on the queue. We will not get | ||
623 | * an interrupt for a request that was running. So we | ||
624 | * just post them all as I/O errors. | ||
625 | */ | ||
626 | DBF_EVENT(3, "(%08x): Drive in use vanished!\n", | ||
627 | device->cdev_id); | ||
628 | PRINT_WARN("(%s): Drive in use vanished - " | ||
629 | "expect trouble!\n", | ||
630 | device->cdev->dev.bus_id); | ||
631 | PRINT_WARN("State was %i\n", device->tape_state); | ||
632 | tape_state_set(device, TS_NOT_OPER); | ||
633 | __tape_discard_requests(device); | ||
634 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
635 | tape_cleanup_device(device); | ||
636 | } | ||
637 | |||
638 | if (cdev->dev.driver_data != NULL) { | ||
639 | sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group); | ||
640 | cdev->dev.driver_data = tape_put_device(cdev->dev.driver_data); | ||
641 | } | ||
642 | } | ||
643 | |||
644 | /* | ||
645 | * Allocate a new tape ccw request | ||
646 | */ | ||
647 | struct tape_request * | ||
648 | tape_alloc_request(int cplength, int datasize) | ||
649 | { | ||
650 | struct tape_request *request; | ||
651 | |||
652 | if (datasize > PAGE_SIZE || (cplength*sizeof(struct ccw1)) > PAGE_SIZE) | ||
653 | BUG(); | ||
654 | |||
655 | DBF_LH(6, "tape_alloc_request(%d, %d)\n", cplength, datasize); | ||
656 | |||
657 | request = (struct tape_request *) kmalloc(sizeof(struct tape_request), | ||
658 | GFP_KERNEL); | ||
659 | if (request == NULL) { | ||
660 | DBF_EXCEPTION(1, "cqra nomem\n"); | ||
661 | return ERR_PTR(-ENOMEM); | ||
662 | } | ||
663 | memset(request, 0, sizeof(struct tape_request)); | ||
664 | /* allocate channel program */ | ||
665 | if (cplength > 0) { | ||
666 | request->cpaddr = kmalloc(cplength*sizeof(struct ccw1), | ||
667 | GFP_ATOMIC | GFP_DMA); | ||
668 | if (request->cpaddr == NULL) { | ||
669 | DBF_EXCEPTION(1, "cqra nomem\n"); | ||
670 | kfree(request); | ||
671 | return ERR_PTR(-ENOMEM); | ||
672 | } | ||
673 | memset(request->cpaddr, 0, cplength*sizeof(struct ccw1)); | ||
674 | } | ||
675 | /* alloc small kernel buffer */ | ||
676 | if (datasize > 0) { | ||
677 | request->cpdata = kmalloc(datasize, GFP_KERNEL | GFP_DMA); | ||
678 | if (request->cpdata == NULL) { | ||
679 | DBF_EXCEPTION(1, "cqra nomem\n"); | ||
680 | if (request->cpaddr != NULL) | ||
681 | kfree(request->cpaddr); | ||
682 | kfree(request); | ||
683 | return ERR_PTR(-ENOMEM); | ||
684 | } | ||
685 | memset(request->cpdata, 0, datasize); | ||
686 | } | ||
687 | DBF_LH(6, "New request %p(%p/%p)\n", request, request->cpaddr, | ||
688 | request->cpdata); | ||
689 | |||
690 | return request; | ||
691 | } | ||
692 | |||
693 | /* | ||
694 | * Free tape ccw request | ||
695 | */ | ||
696 | void | ||
697 | tape_free_request (struct tape_request * request) | ||
698 | { | ||
699 | DBF_LH(6, "Free request %p\n", request); | ||
700 | |||
701 | if (request->device != NULL) { | ||
702 | request->device = tape_put_device(request->device); | ||
703 | } | ||
704 | if (request->cpdata != NULL) | ||
705 | kfree(request->cpdata); | ||
706 | if (request->cpaddr != NULL) | ||
707 | kfree(request->cpaddr); | ||
708 | kfree(request); | ||
709 | } | ||
710 | |||
711 | static inline void | ||
712 | __tape_do_io_list(struct tape_device *device) | ||
713 | { | ||
714 | struct list_head *l, *n; | ||
715 | struct tape_request *request; | ||
716 | int rc; | ||
717 | |||
718 | DBF_LH(6, "__tape_do_io_list(%p)\n", device); | ||
719 | /* | ||
720 | * Try to start each request on request queue until one is | ||
721 | * started successful. | ||
722 | */ | ||
723 | list_for_each_safe(l, n, &device->req_queue) { | ||
724 | request = list_entry(l, struct tape_request, list); | ||
725 | #ifdef CONFIG_S390_TAPE_BLOCK | ||
726 | if (request->op == TO_BLOCK) | ||
727 | device->discipline->check_locate(device, request); | ||
728 | #endif | ||
729 | rc = ccw_device_start(device->cdev, request->cpaddr, | ||
730 | (unsigned long) request, 0x00, | ||
731 | request->options); | ||
732 | if (rc == 0) { | ||
733 | request->status = TAPE_REQUEST_IN_IO; | ||
734 | break; | ||
735 | } | ||
736 | /* Start failed. Remove request and indicate failure. */ | ||
737 | DBF_EVENT(1, "tape: DOIO failed with er = %i\n", rc); | ||
738 | |||
739 | /* Set ending status and do callback. */ | ||
740 | request->rc = rc; | ||
741 | request->status = TAPE_REQUEST_DONE; | ||
742 | __tape_remove_request(device, request); | ||
743 | } | ||
744 | } | ||
745 | |||
746 | static void | ||
747 | __tape_remove_request(struct tape_device *device, struct tape_request *request) | ||
748 | { | ||
749 | /* Remove from request queue. */ | ||
750 | list_del(&request->list); | ||
751 | |||
752 | /* Do callback. */ | ||
753 | if (request->callback != NULL) | ||
754 | request->callback(request, request->callback_data); | ||
755 | |||
756 | /* Start next request. */ | ||
757 | if (!list_empty(&device->req_queue)) | ||
758 | __tape_do_io_list(device); | ||
759 | } | ||
760 | |||
761 | /* | ||
762 | * Write sense data to console/dbf | ||
763 | */ | ||
764 | void | ||
765 | tape_dump_sense(struct tape_device* device, struct tape_request *request, | ||
766 | struct irb *irb) | ||
767 | { | ||
768 | unsigned int *sptr; | ||
769 | |||
770 | PRINT_INFO("-------------------------------------------------\n"); | ||
771 | PRINT_INFO("DSTAT : %02x CSTAT: %02x CPA: %04x\n", | ||
772 | irb->scsw.dstat, irb->scsw.cstat, irb->scsw.cpa); | ||
773 | PRINT_INFO("DEVICE: %s\n", device->cdev->dev.bus_id); | ||
774 | if (request != NULL) | ||
775 | PRINT_INFO("OP : %s\n", tape_op_verbose[request->op]); | ||
776 | |||
777 | sptr = (unsigned int *) irb->ecw; | ||
778 | PRINT_INFO("Sense data: %08X %08X %08X %08X \n", | ||
779 | sptr[0], sptr[1], sptr[2], sptr[3]); | ||
780 | PRINT_INFO("Sense data: %08X %08X %08X %08X \n", | ||
781 | sptr[4], sptr[5], sptr[6], sptr[7]); | ||
782 | PRINT_INFO("--------------------------------------------------\n"); | ||
783 | } | ||
784 | |||
785 | /* | ||
786 | * Write sense data to dbf | ||
787 | */ | ||
788 | void | ||
789 | tape_dump_sense_dbf(struct tape_device *device, struct tape_request *request, | ||
790 | struct irb *irb) | ||
791 | { | ||
792 | unsigned int *sptr; | ||
793 | const char* op; | ||
794 | |||
795 | if (request != NULL) | ||
796 | op = tape_op_verbose[request->op]; | ||
797 | else | ||
798 | op = "---"; | ||
799 | DBF_EVENT(3, "DSTAT : %02x CSTAT: %02x\n", | ||
800 | irb->scsw.dstat,irb->scsw.cstat); | ||
801 | DBF_EVENT(3, "DEVICE: %08x OP\t: %s\n", device->cdev_id, op); | ||
802 | sptr = (unsigned int *) irb->ecw; | ||
803 | DBF_EVENT(3, "%08x %08x\n", sptr[0], sptr[1]); | ||
804 | DBF_EVENT(3, "%08x %08x\n", sptr[2], sptr[3]); | ||
805 | DBF_EVENT(3, "%08x %08x\n", sptr[4], sptr[5]); | ||
806 | DBF_EVENT(3, "%08x %08x\n", sptr[6], sptr[7]); | ||
807 | } | ||
808 | |||
809 | /* | ||
810 | * I/O helper function. Adds the request to the request queue | ||
811 | * and starts it if the tape is idle. Has to be called with | ||
812 | * the device lock held. | ||
813 | */ | ||
814 | static inline int | ||
815 | __tape_do_io(struct tape_device *device, struct tape_request *request) | ||
816 | { | ||
817 | int rc; | ||
818 | |||
819 | switch (request->op) { | ||
820 | case TO_MSEN: | ||
821 | case TO_ASSIGN: | ||
822 | case TO_UNASSIGN: | ||
823 | case TO_READ_ATTMSG: | ||
824 | if (device->tape_state == TS_INIT) | ||
825 | break; | ||
826 | if (device->tape_state == TS_UNUSED) | ||
827 | break; | ||
828 | default: | ||
829 | if (device->tape_state == TS_BLKUSE) | ||
830 | break; | ||
831 | if (device->tape_state != TS_IN_USE) | ||
832 | return -ENODEV; | ||
833 | } | ||
834 | |||
835 | /* Increase use count of device for the added request. */ | ||
836 | request->device = tape_get_device_reference(device); | ||
837 | |||
838 | if (list_empty(&device->req_queue)) { | ||
839 | /* No other requests are on the queue. Start this one. */ | ||
840 | #ifdef CONFIG_S390_TAPE_BLOCK | ||
841 | if (request->op == TO_BLOCK) | ||
842 | device->discipline->check_locate(device, request); | ||
843 | #endif | ||
844 | rc = ccw_device_start(device->cdev, request->cpaddr, | ||
845 | (unsigned long) request, 0x00, | ||
846 | request->options); | ||
847 | if (rc) { | ||
848 | DBF_EVENT(1, "tape: DOIO failed with rc = %i\n", rc); | ||
849 | return rc; | ||
850 | } | ||
851 | DBF_LH(5, "Request %p added for execution.\n", request); | ||
852 | list_add(&request->list, &device->req_queue); | ||
853 | request->status = TAPE_REQUEST_IN_IO; | ||
854 | } else { | ||
855 | DBF_LH(5, "Request %p add to queue.\n", request); | ||
856 | list_add_tail(&request->list, &device->req_queue); | ||
857 | request->status = TAPE_REQUEST_QUEUED; | ||
858 | } | ||
859 | return 0; | ||
860 | } | ||
861 | |||
862 | /* | ||
863 | * Add the request to the request queue, try to start it if the | ||
864 | * tape is idle. Return without waiting for end of i/o. | ||
865 | */ | ||
866 | int | ||
867 | tape_do_io_async(struct tape_device *device, struct tape_request *request) | ||
868 | { | ||
869 | int rc; | ||
870 | |||
871 | DBF_LH(6, "tape_do_io_async(%p, %p)\n", device, request); | ||
872 | |||
873 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | ||
874 | /* Add request to request queue and try to start it. */ | ||
875 | rc = __tape_do_io(device, request); | ||
876 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
877 | return rc; | ||
878 | } | ||
879 | |||
880 | /* | ||
881 | * tape_do_io/__tape_wake_up | ||
882 | * Add the request to the request queue, try to start it if the | ||
883 | * tape is idle and wait uninterruptible for its completion. | ||
884 | */ | ||
885 | static void | ||
886 | __tape_wake_up(struct tape_request *request, void *data) | ||
887 | { | ||
888 | request->callback = NULL; | ||
889 | wake_up((wait_queue_head_t *) data); | ||
890 | } | ||
891 | |||
892 | int | ||
893 | tape_do_io(struct tape_device *device, struct tape_request *request) | ||
894 | { | ||
895 | wait_queue_head_t wq; | ||
896 | int rc; | ||
897 | |||
898 | init_waitqueue_head(&wq); | ||
899 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | ||
900 | /* Setup callback */ | ||
901 | request->callback = __tape_wake_up; | ||
902 | request->callback_data = &wq; | ||
903 | /* Add request to request queue and try to start it. */ | ||
904 | rc = __tape_do_io(device, request); | ||
905 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
906 | if (rc) | ||
907 | return rc; | ||
908 | /* Request added to the queue. Wait for its completion. */ | ||
909 | wait_event(wq, (request->callback == NULL)); | ||
910 | /* Get rc from request */ | ||
911 | return request->rc; | ||
912 | } | ||
913 | |||
914 | /* | ||
915 | * tape_do_io_interruptible/__tape_wake_up_interruptible | ||
916 | * Add the request to the request queue, try to start it if the | ||
917 | * tape is idle and wait uninterruptible for its completion. | ||
918 | */ | ||
919 | static void | ||
920 | __tape_wake_up_interruptible(struct tape_request *request, void *data) | ||
921 | { | ||
922 | request->callback = NULL; | ||
923 | wake_up_interruptible((wait_queue_head_t *) data); | ||
924 | } | ||
925 | |||
926 | int | ||
927 | tape_do_io_interruptible(struct tape_device *device, | ||
928 | struct tape_request *request) | ||
929 | { | ||
930 | wait_queue_head_t wq; | ||
931 | int rc; | ||
932 | |||
933 | init_waitqueue_head(&wq); | ||
934 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | ||
935 | /* Setup callback */ | ||
936 | request->callback = __tape_wake_up_interruptible; | ||
937 | request->callback_data = &wq; | ||
938 | rc = __tape_do_io(device, request); | ||
939 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
940 | if (rc) | ||
941 | return rc; | ||
942 | /* Request added to the queue. Wait for its completion. */ | ||
943 | rc = wait_event_interruptible(wq, (request->callback == NULL)); | ||
944 | if (rc != -ERESTARTSYS) | ||
945 | /* Request finished normally. */ | ||
946 | return request->rc; | ||
947 | /* Interrupted by a signal. We have to stop the current request. */ | ||
948 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | ||
949 | rc = __tape_halt_io(device, request); | ||
950 | if (rc == 0) { | ||
951 | DBF_EVENT(3, "IO stopped on %08x\n", device->cdev_id); | ||
952 | rc = -ERESTARTSYS; | ||
953 | } | ||
954 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
955 | return rc; | ||
956 | } | ||
957 | |||
958 | /* | ||
959 | * Handle requests that return an i/o error in the irb. | ||
960 | */ | ||
961 | static inline void | ||
962 | tape_handle_killed_request( | ||
963 | struct tape_device *device, | ||
964 | struct tape_request *request) | ||
965 | { | ||
966 | if(request != NULL) { | ||
967 | /* Set ending status. FIXME: Should the request be retried? */ | ||
968 | request->rc = -EIO; | ||
969 | request->status = TAPE_REQUEST_DONE; | ||
970 | __tape_remove_request(device, request); | ||
971 | } else { | ||
972 | __tape_do_io_list(device); | ||
973 | } | ||
974 | } | ||
975 | |||
976 | /* | ||
977 | * Tape interrupt routine, called from the ccw_device layer | ||
978 | */ | ||
979 | static void | ||
980 | __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) | ||
981 | { | ||
982 | struct tape_device *device; | ||
983 | struct tape_request *request; | ||
984 | int final; | ||
985 | int rc; | ||
986 | |||
987 | device = (struct tape_device *) cdev->dev.driver_data; | ||
988 | if (device == NULL) { | ||
989 | PRINT_ERR("could not get device structure for %s " | ||
990 | "in interrupt\n", cdev->dev.bus_id); | ||
991 | return; | ||
992 | } | ||
993 | request = (struct tape_request *) intparm; | ||
994 | |||
995 | DBF_LH(6, "__tape_do_irq(device=%p, request=%p)\n", device, request); | ||
996 | |||
997 | /* On special conditions irb is an error pointer */ | ||
998 | if (IS_ERR(irb)) { | ||
999 | switch (PTR_ERR(irb)) { | ||
1000 | case -ETIMEDOUT: | ||
1001 | PRINT_WARN("(%s): Request timed out\n", | ||
1002 | cdev->dev.bus_id); | ||
1003 | case -EIO: | ||
1004 | tape_handle_killed_request(device, request); | ||
1005 | break; | ||
1006 | default: | ||
1007 | PRINT_ERR("(%s): Unexpected i/o error %li\n", | ||
1008 | cdev->dev.bus_id, | ||
1009 | PTR_ERR(irb)); | ||
1010 | } | ||
1011 | return; | ||
1012 | } | ||
1013 | |||
1014 | /* May be an unsolicited irq */ | ||
1015 | if(request != NULL) | ||
1016 | request->rescnt = irb->scsw.count; | ||
1017 | |||
1018 | if (irb->scsw.dstat != 0x0c) { | ||
1019 | /* Set the 'ONLINE' flag depending on sense byte 1 */ | ||
1020 | if(*(((__u8 *) irb->ecw) + 1) & SENSE_DRIVE_ONLINE) | ||
1021 | device->tape_generic_status |= GMT_ONLINE(~0); | ||
1022 | else | ||
1023 | device->tape_generic_status &= ~GMT_ONLINE(~0); | ||
1024 | |||
1025 | /* | ||
1026 | * Any request that does not come back with channel end | ||
1027 | * and device end is unusual. Log the sense data. | ||
1028 | */ | ||
1029 | DBF_EVENT(3,"-- Tape Interrupthandler --\n"); | ||
1030 | tape_dump_sense_dbf(device, request, irb); | ||
1031 | } else { | ||
1032 | /* Upon normal completion the device _is_ online */ | ||
1033 | device->tape_generic_status |= GMT_ONLINE(~0); | ||
1034 | } | ||
1035 | if (device->tape_state == TS_NOT_OPER) { | ||
1036 | DBF_EVENT(6, "tape:device is not operational\n"); | ||
1037 | return; | ||
1038 | } | ||
1039 | |||
1040 | /* | ||
1041 | * Request that were canceled still come back with an interrupt. | ||
1042 | * To detect these request the state will be set to TAPE_REQUEST_DONE. | ||
1043 | */ | ||
1044 | if(request != NULL && request->status == TAPE_REQUEST_DONE) { | ||
1045 | __tape_remove_request(device, request); | ||
1046 | return; | ||
1047 | } | ||
1048 | |||
1049 | rc = device->discipline->irq(device, request, irb); | ||
1050 | /* | ||
1051 | * rc < 0 : request finished unsuccessfully. | ||
1052 | * rc == TAPE_IO_SUCCESS: request finished successfully. | ||
1053 | * rc == TAPE_IO_PENDING: request is still running. Ignore rc. | ||
1054 | * rc == TAPE_IO_RETRY: request finished but needs another go. | ||
1055 | * rc == TAPE_IO_STOP: request needs to get terminated. | ||
1056 | */ | ||
1057 | final = 0; | ||
1058 | switch (rc) { | ||
1059 | case TAPE_IO_SUCCESS: | ||
1060 | /* Upon normal completion the device _is_ online */ | ||
1061 | device->tape_generic_status |= GMT_ONLINE(~0); | ||
1062 | final = 1; | ||
1063 | break; | ||
1064 | case TAPE_IO_PENDING: | ||
1065 | break; | ||
1066 | case TAPE_IO_RETRY: | ||
1067 | #ifdef CONFIG_S390_TAPE_BLOCK | ||
1068 | if (request->op == TO_BLOCK) | ||
1069 | device->discipline->check_locate(device, request); | ||
1070 | #endif | ||
1071 | rc = ccw_device_start(cdev, request->cpaddr, | ||
1072 | (unsigned long) request, 0x00, | ||
1073 | request->options); | ||
1074 | if (rc) { | ||
1075 | DBF_EVENT(1, "tape: DOIO failed with er = %i\n", rc); | ||
1076 | final = 1; | ||
1077 | } | ||
1078 | break; | ||
1079 | case TAPE_IO_STOP: | ||
1080 | __tape_halt_io(device, request); | ||
1081 | break; | ||
1082 | default: | ||
1083 | if (rc > 0) { | ||
1084 | DBF_EVENT(6, "xunknownrc\n"); | ||
1085 | PRINT_ERR("Invalid return code from discipline " | ||
1086 | "interrupt function.\n"); | ||
1087 | rc = -EIO; | ||
1088 | } | ||
1089 | final = 1; | ||
1090 | break; | ||
1091 | } | ||
1092 | if (final) { | ||
1093 | /* May be an unsolicited irq */ | ||
1094 | if(request != NULL) { | ||
1095 | /* Set ending status. */ | ||
1096 | request->rc = rc; | ||
1097 | request->status = TAPE_REQUEST_DONE; | ||
1098 | __tape_remove_request(device, request); | ||
1099 | } else { | ||
1100 | __tape_do_io_list(device); | ||
1101 | } | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | /* | ||
1106 | * Tape device open function used by tape_char & tape_block frontends. | ||
1107 | */ | ||
1108 | int | ||
1109 | tape_open(struct tape_device *device) | ||
1110 | { | ||
1111 | int rc; | ||
1112 | |||
1113 | spin_lock(get_ccwdev_lock(device->cdev)); | ||
1114 | if (device->tape_state == TS_NOT_OPER) { | ||
1115 | DBF_EVENT(6, "TAPE:nodev\n"); | ||
1116 | rc = -ENODEV; | ||
1117 | } else if (device->tape_state == TS_IN_USE) { | ||
1118 | DBF_EVENT(6, "TAPE:dbusy\n"); | ||
1119 | rc = -EBUSY; | ||
1120 | } else if (device->tape_state == TS_BLKUSE) { | ||
1121 | DBF_EVENT(6, "TAPE:dbusy\n"); | ||
1122 | rc = -EBUSY; | ||
1123 | } else if (device->discipline != NULL && | ||
1124 | !try_module_get(device->discipline->owner)) { | ||
1125 | DBF_EVENT(6, "TAPE:nodisc\n"); | ||
1126 | rc = -ENODEV; | ||
1127 | } else { | ||
1128 | tape_state_set(device, TS_IN_USE); | ||
1129 | rc = 0; | ||
1130 | } | ||
1131 | spin_unlock(get_ccwdev_lock(device->cdev)); | ||
1132 | return rc; | ||
1133 | } | ||
1134 | |||
1135 | /* | ||
1136 | * Tape device release function used by tape_char & tape_block frontends. | ||
1137 | */ | ||
1138 | int | ||
1139 | tape_release(struct tape_device *device) | ||
1140 | { | ||
1141 | spin_lock(get_ccwdev_lock(device->cdev)); | ||
1142 | if (device->tape_state == TS_IN_USE) | ||
1143 | tape_state_set(device, TS_UNUSED); | ||
1144 | module_put(device->discipline->owner); | ||
1145 | spin_unlock(get_ccwdev_lock(device->cdev)); | ||
1146 | return 0; | ||
1147 | } | ||
1148 | |||
1149 | /* | ||
1150 | * Execute a magnetic tape command a number of times. | ||
1151 | */ | ||
1152 | int | ||
1153 | tape_mtop(struct tape_device *device, int mt_op, int mt_count) | ||
1154 | { | ||
1155 | tape_mtop_fn fn; | ||
1156 | int rc; | ||
1157 | |||
1158 | DBF_EVENT(6, "TAPE:mtio\n"); | ||
1159 | DBF_EVENT(6, "TAPE:ioop: %x\n", mt_op); | ||
1160 | DBF_EVENT(6, "TAPE:arg: %x\n", mt_count); | ||
1161 | |||
1162 | if (mt_op < 0 || mt_op >= TAPE_NR_MTOPS) | ||
1163 | return -EINVAL; | ||
1164 | fn = device->discipline->mtop_array[mt_op]; | ||
1165 | if (fn == NULL) | ||
1166 | return -EINVAL; | ||
1167 | |||
1168 | /* We assume that the backends can handle count up to 500. */ | ||
1169 | if (mt_op == MTBSR || mt_op == MTFSR || mt_op == MTFSF || | ||
1170 | mt_op == MTBSF || mt_op == MTFSFM || mt_op == MTBSFM) { | ||
1171 | rc = 0; | ||
1172 | for (; mt_count > 500; mt_count -= 500) | ||
1173 | if ((rc = fn(device, 500)) != 0) | ||
1174 | break; | ||
1175 | if (rc == 0) | ||
1176 | rc = fn(device, mt_count); | ||
1177 | } else | ||
1178 | rc = fn(device, mt_count); | ||
1179 | return rc; | ||
1180 | |||
1181 | } | ||
1182 | |||
1183 | /* | ||
1184 | * Tape init function. | ||
1185 | */ | ||
1186 | static int | ||
1187 | tape_init (void) | ||
1188 | { | ||
1189 | TAPE_DBF_AREA = debug_register ( "tape", 1, 2, 4*sizeof(long)); | ||
1190 | debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view); | ||
1191 | #ifdef DBF_LIKE_HELL | ||
1192 | debug_set_level(TAPE_DBF_AREA, 6); | ||
1193 | #endif | ||
1194 | DBF_EVENT(3, "tape init: ($Revision: 1.51 $)\n"); | ||
1195 | tape_proc_init(); | ||
1196 | tapechar_init (); | ||
1197 | tapeblock_init (); | ||
1198 | return 0; | ||
1199 | } | ||
1200 | |||
1201 | /* | ||
1202 | * Tape exit function. | ||
1203 | */ | ||
1204 | static void | ||
1205 | tape_exit(void) | ||
1206 | { | ||
1207 | DBF_EVENT(6, "tape exit\n"); | ||
1208 | |||
1209 | /* Get rid of the frontends */ | ||
1210 | tapechar_exit(); | ||
1211 | tapeblock_exit(); | ||
1212 | tape_proc_cleanup(); | ||
1213 | debug_unregister (TAPE_DBF_AREA); | ||
1214 | } | ||
1215 | |||
1216 | MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte and " | ||
1217 | "Michael Holzheu (cotte@de.ibm.com,holzheu@de.ibm.com)"); | ||
1218 | MODULE_DESCRIPTION("Linux on zSeries channel attached " | ||
1219 | "tape device driver ($Revision: 1.51 $)"); | ||
1220 | MODULE_LICENSE("GPL"); | ||
1221 | |||
1222 | module_init(tape_init); | ||
1223 | module_exit(tape_exit); | ||
1224 | |||
1225 | EXPORT_SYMBOL(tape_generic_remove); | ||
1226 | EXPORT_SYMBOL(tape_generic_probe); | ||
1227 | EXPORT_SYMBOL(tape_generic_online); | ||
1228 | EXPORT_SYMBOL(tape_generic_offline); | ||
1229 | EXPORT_SYMBOL(tape_put_device); | ||
1230 | EXPORT_SYMBOL(tape_get_device_reference); | ||
1231 | EXPORT_SYMBOL(tape_state_verbose); | ||
1232 | EXPORT_SYMBOL(tape_op_verbose); | ||
1233 | EXPORT_SYMBOL(tape_state_set); | ||
1234 | EXPORT_SYMBOL(tape_med_state_set); | ||
1235 | EXPORT_SYMBOL(tape_alloc_request); | ||
1236 | EXPORT_SYMBOL(tape_free_request); | ||
1237 | EXPORT_SYMBOL(tape_dump_sense); | ||
1238 | EXPORT_SYMBOL(tape_dump_sense_dbf); | ||
1239 | EXPORT_SYMBOL(tape_do_io); | ||
1240 | EXPORT_SYMBOL(tape_do_io_async); | ||
1241 | EXPORT_SYMBOL(tape_do_io_interruptible); | ||
1242 | EXPORT_SYMBOL(tape_mtop); | ||