summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/s390/char/tape_proc.c
blob: 8733b232a116350c96444414cb7b82ae949b382d (plain)
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 *    tape device driver for S/390 and zSeries tapes.
 *
 *  S390 and zSeries version
 *    Copyright IBM Corp. 2001
 *    Author(s): Carsten Otte <cotte@de.ibm.com>
 *		 Michael Holzheu <holzheu@de.ibm.com>
 *		 Tuan Ngo-Anh <ngoanh@de.ibm.com>
 *
 * PROCFS Functions
 */

#define KMSG_COMPONENT "tape"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt

#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>

#define TAPE_DBF_AREA	tape_core_dbf

#include "tape.h"

static const char *tape_med_st_verbose[MS_SIZE] =
{
	[MS_UNKNOWN] = "UNKNOWN ",
	[MS_LOADED] = "LOADED  ",
	[MS_UNLOADED] = "UNLOADED"
};

/* our proc tapedevices entry */
static struct proc_dir_entry *tape_proc_devices;

/*
 * Show function for /proc/tapedevices
 */
static int tape_proc_show(struct seq_file *m, void *v)
{
	struct tape_device *device;
	struct tape_request *request;
	const char *str;
	unsigned long n;

	n = (unsigned long) v - 1;
	if (!n) {
		seq_printf(m, "TapeNo\tBusID      CuType/Model\t"
			"DevType/Model\tBlkSize\tState\tOp\tMedState\n");
	}
	device = tape_find_device(n);
	if (IS_ERR(device))
		return 0;
	spin_lock_irq(get_ccwdev_lock(device->cdev));
	seq_printf(m, "%d\t", (int) n);
	seq_printf(m, "%-10.10s ", dev_name(&device->cdev->dev));
	seq_printf(m, "%04X/", device->cdev->id.cu_type);
	seq_printf(m, "%02X\t", device->cdev->id.cu_model);
	seq_printf(m, "%04X/", device->cdev->id.dev_type);
	seq_printf(m, "%02X\t\t", device->cdev->id.dev_model);
	if (device->char_data.block_size == 0)
		seq_printf(m, "auto\t");
	else
		seq_printf(m, "%i\t", device->char_data.block_size);
	if (device->tape_state >= 0 &&
	    device->tape_state < TS_SIZE)
		str = tape_state_verbose[device->tape_state];
	else
		str = "UNKNOWN";
	seq_printf(m, "%s\t", str);
	if (!list_empty(&device->req_queue)) {
		request = list_entry(device->req_queue.next,
				     struct tape_request, list);
		str = tape_op_verbose[request->op];
	} else
		str = "---";
	seq_printf(m, "%s\t", str);
	seq_printf(m, "%s\n", tape_med_st_verbose[device->medium_state]);
	spin_unlock_irq(get_ccwdev_lock(device->cdev));
	tape_put_device(device);
        return 0;
}

static void *tape_proc_start(struct seq_file *m, loff_t *pos)
{
	if (*pos >= 256 / TAPE_MINORS_PER_DEV)
		return NULL;
	return (void *)((unsigned long) *pos + 1);
}

static void *tape_proc_next(struct seq_file *m, void *v, loff_t *pos)
{
	++*pos;
	return tape_proc_start(m, pos);
}

static void tape_proc_stop(struct seq_file *m, void *v)
{
}

static const struct seq_operations tape_proc_seq = {
	.start		= tape_proc_start,
	.next		= tape_proc_next,
	.stop		= tape_proc_stop,
	.show		= tape_proc_show,
};

static int tape_proc_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &tape_proc_seq);
}

static const struct file_operations tape_proc_ops =
{
	.owner		= THIS_MODULE,
	.open		= tape_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
};

/*
 * Initialize procfs stuff on startup
 */
void
tape_proc_init(void)
{
	tape_proc_devices =
		proc_create("tapedevices", S_IFREG | S_IRUGO | S_IWUSR, NULL,
			    &tape_proc_ops);
	if (tape_proc_devices == NULL) {
		return;
	}
}

/*
 * Cleanup all stuff registered to the procfs
 */
void
tape_proc_cleanup(void)
{
	if (tape_proc_devices != NULL)
		remove_proc_entry ("tapedevices", NULL);
}
* struct example { * struct raw3270_view view; * ... * }; */ struct raw3270_view { struct list_head list; spinlock_t lock; atomic_t ref_count; struct raw3270 *dev; struct raw3270_fn *fn; unsigned int model; unsigned int rows, cols; /* # of rows & colums of the view */ unsigned char *ascebc; /* ascii -> ebcdic table */ }; int raw3270_add_view(struct raw3270_view *, struct raw3270_fn *, int); int raw3270_activate_view(struct raw3270_view *); void raw3270_del_view(struct raw3270_view *); void raw3270_deactivate_view(struct raw3270_view *); struct raw3270_view *raw3270_find_view(struct raw3270_fn *, int); int raw3270_start(struct raw3270_view *, struct raw3270_request *); int raw3270_start_locked(struct raw3270_view *, struct raw3270_request *); int raw3270_start_irq(struct raw3270_view *, struct raw3270_request *); int raw3270_reset(struct raw3270_view *); struct raw3270_view *raw3270_view(struct raw3270_view *); int raw3270_view_active(struct raw3270_view *); /* Reference count inliner for view structures. */ static inline void raw3270_get_view(struct raw3270_view *view) { atomic_inc(&view->ref_count); } extern wait_queue_head_t raw3270_wait_queue; static inline void raw3270_put_view(struct raw3270_view *view) { if (atomic_dec_return(&view->ref_count) == 0) wake_up(&raw3270_wait_queue); } struct raw3270 *raw3270_setup_console(void); void raw3270_wait_cons_dev(struct raw3270 *); /* Notifier for device addition/removal */ struct raw3270_notifier { struct list_head list; void (*create)(int minor); void (*destroy)(int minor); }; int raw3270_register_notifier(struct raw3270_notifier *); void raw3270_unregister_notifier(struct raw3270_notifier *); void raw3270_pm_unfreeze(struct raw3270_view *); /* * Little memory allocator for string objects. */ struct string { struct list_head list; struct list_head update; unsigned long size; unsigned long len; char string[0]; } __attribute__ ((aligned(8))); static inline struct string * alloc_string(struct list_head *free_list, unsigned long len) { struct string *cs, *tmp; unsigned long size; size = (len + 7L) & -8L; list_for_each_entry(cs, free_list, list) { if (cs->size < size) continue; if (cs->size > size + sizeof(struct string)) { char *endaddr = (char *) (cs + 1) + cs->size; tmp = (struct string *) (endaddr - size) - 1; tmp->size = size; cs->size -= size + sizeof(struct string); cs = tmp; } else list_del(&cs->list); cs->len = len; INIT_LIST_HEAD(&cs->list); INIT_LIST_HEAD(&cs->update); return cs; } return NULL; } static inline unsigned long free_string(struct list_head *free_list, struct string *cs) { struct string *tmp; struct list_head *p, *left; /* Find out the left neighbour in free memory list. */ left = free_list; list_for_each(p, free_list) { if (list_entry(p, struct string, list) > cs) break; left = p; } /* Try to merge with right neighbour = next element from left. */ if (left->next != free_list) { tmp = list_entry(left->next, struct string, list); if ((char *) (cs + 1) + cs->size == (char *) tmp) { list_del(&tmp->list); cs->size += tmp->size + sizeof(struct string); } } /* Try to merge with left neighbour. */ if (left != free_list) { tmp = list_entry(left, struct string, list); if ((char *) (tmp + 1) + tmp->size == (char *) cs) { tmp->size += cs->size + sizeof(struct string); return tmp->size; } } __list_add(&cs->list, left, left->next); return cs->size; } static inline void add_string_memory(struct list_head *free_list, void *mem, unsigned long size) { struct string *cs; cs = (struct string *) mem; cs->size = size - sizeof(struct string); free_string(free_list, cs); }