xref: /openbmc/linux/drivers/s390/char/tape_char.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1*1da177e4SLinus Torvalds /*
2*1da177e4SLinus Torvalds  *  drivers/s390/char/tape_char.c
3*1da177e4SLinus Torvalds  *    character device frontend for tape device driver
4*1da177e4SLinus Torvalds  *
5*1da177e4SLinus Torvalds  *  S390 and zSeries version
6*1da177e4SLinus Torvalds  *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
7*1da177e4SLinus Torvalds  *    Author(s): Carsten Otte <cotte@de.ibm.com>
8*1da177e4SLinus Torvalds  *		 Michael Holzheu <holzheu@de.ibm.com>
9*1da177e4SLinus Torvalds  *		 Tuan Ngo-Anh <ngoanh@de.ibm.com>
10*1da177e4SLinus Torvalds  *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
11*1da177e4SLinus Torvalds  */
12*1da177e4SLinus Torvalds 
13*1da177e4SLinus Torvalds #include <linux/config.h>
14*1da177e4SLinus Torvalds #include <linux/module.h>
15*1da177e4SLinus Torvalds #include <linux/types.h>
16*1da177e4SLinus Torvalds #include <linux/proc_fs.h>
17*1da177e4SLinus Torvalds #include <linux/mtio.h>
18*1da177e4SLinus Torvalds 
19*1da177e4SLinus Torvalds #include <asm/uaccess.h>
20*1da177e4SLinus Torvalds 
21*1da177e4SLinus Torvalds #define TAPE_DBF_AREA	tape_core_dbf
22*1da177e4SLinus Torvalds 
23*1da177e4SLinus Torvalds #include "tape.h"
24*1da177e4SLinus Torvalds #include "tape_std.h"
25*1da177e4SLinus Torvalds #include "tape_class.h"
26*1da177e4SLinus Torvalds 
27*1da177e4SLinus Torvalds #define PRINTK_HEADER "TAPE_CHAR: "
28*1da177e4SLinus Torvalds 
29*1da177e4SLinus Torvalds #define TAPECHAR_MAJOR		0	/* get dynamic major */
30*1da177e4SLinus Torvalds 
31*1da177e4SLinus Torvalds /*
32*1da177e4SLinus Torvalds  * file operation structure for tape character frontend
33*1da177e4SLinus Torvalds  */
34*1da177e4SLinus Torvalds static ssize_t tapechar_read(struct file *, char __user *, size_t, loff_t *);
35*1da177e4SLinus Torvalds static ssize_t tapechar_write(struct file *, const char __user *, size_t, loff_t *);
36*1da177e4SLinus Torvalds static int tapechar_open(struct inode *,struct file *);
37*1da177e4SLinus Torvalds static int tapechar_release(struct inode *,struct file *);
38*1da177e4SLinus Torvalds static int tapechar_ioctl(struct inode *, struct file *, unsigned int,
39*1da177e4SLinus Torvalds 			  unsigned long);
40*1da177e4SLinus Torvalds 
41*1da177e4SLinus Torvalds static struct file_operations tape_fops =
42*1da177e4SLinus Torvalds {
43*1da177e4SLinus Torvalds 	.owner = THIS_MODULE,
44*1da177e4SLinus Torvalds 	.read = tapechar_read,
45*1da177e4SLinus Torvalds 	.write = tapechar_write,
46*1da177e4SLinus Torvalds 	.ioctl = tapechar_ioctl,
47*1da177e4SLinus Torvalds 	.open = tapechar_open,
48*1da177e4SLinus Torvalds 	.release = tapechar_release,
49*1da177e4SLinus Torvalds };
50*1da177e4SLinus Torvalds 
51*1da177e4SLinus Torvalds static int tapechar_major = TAPECHAR_MAJOR;
52*1da177e4SLinus Torvalds 
53*1da177e4SLinus Torvalds /*
54*1da177e4SLinus Torvalds  * This function is called for every new tapedevice
55*1da177e4SLinus Torvalds  */
56*1da177e4SLinus Torvalds int
57*1da177e4SLinus Torvalds tapechar_setup_device(struct tape_device * device)
58*1da177e4SLinus Torvalds {
59*1da177e4SLinus Torvalds 	char	device_name[20];
60*1da177e4SLinus Torvalds 
61*1da177e4SLinus Torvalds 	sprintf(device_name, "ntibm%i", device->first_minor / 2);
62*1da177e4SLinus Torvalds 	device->nt = register_tape_dev(
63*1da177e4SLinus Torvalds 		&device->cdev->dev,
64*1da177e4SLinus Torvalds 		MKDEV(tapechar_major, device->first_minor),
65*1da177e4SLinus Torvalds 		&tape_fops,
66*1da177e4SLinus Torvalds 		device_name,
67*1da177e4SLinus Torvalds 		"non-rewinding"
68*1da177e4SLinus Torvalds 	);
69*1da177e4SLinus Torvalds 	device_name[0] = 'r';
70*1da177e4SLinus Torvalds 	device->rt = register_tape_dev(
71*1da177e4SLinus Torvalds 		&device->cdev->dev,
72*1da177e4SLinus Torvalds 		MKDEV(tapechar_major, device->first_minor + 1),
73*1da177e4SLinus Torvalds 		&tape_fops,
74*1da177e4SLinus Torvalds 		device_name,
75*1da177e4SLinus Torvalds 		"rewinding"
76*1da177e4SLinus Torvalds 	);
77*1da177e4SLinus Torvalds 
78*1da177e4SLinus Torvalds 	return 0;
79*1da177e4SLinus Torvalds }
80*1da177e4SLinus Torvalds 
81*1da177e4SLinus Torvalds void
82*1da177e4SLinus Torvalds tapechar_cleanup_device(struct tape_device *device)
83*1da177e4SLinus Torvalds {
84*1da177e4SLinus Torvalds 	unregister_tape_dev(device->rt);
85*1da177e4SLinus Torvalds 	device->rt = NULL;
86*1da177e4SLinus Torvalds 	unregister_tape_dev(device->nt);
87*1da177e4SLinus Torvalds 	device->nt = NULL;
88*1da177e4SLinus Torvalds }
89*1da177e4SLinus Torvalds 
90*1da177e4SLinus Torvalds /*
91*1da177e4SLinus Torvalds  * Terminate write command (we write two TMs and skip backward over last)
92*1da177e4SLinus Torvalds  * This ensures that the tape is always correctly terminated.
93*1da177e4SLinus Torvalds  * When the user writes afterwards a new file, he will overwrite the
94*1da177e4SLinus Torvalds  * second TM and therefore one TM will remain to separate the
95*1da177e4SLinus Torvalds  * two files on the tape...
96*1da177e4SLinus Torvalds  */
97*1da177e4SLinus Torvalds static inline void
98*1da177e4SLinus Torvalds tapechar_terminate_write(struct tape_device *device)
99*1da177e4SLinus Torvalds {
100*1da177e4SLinus Torvalds 	if (tape_mtop(device, MTWEOF, 1) == 0 &&
101*1da177e4SLinus Torvalds 	    tape_mtop(device, MTWEOF, 1) == 0)
102*1da177e4SLinus Torvalds 		tape_mtop(device, MTBSR, 1);
103*1da177e4SLinus Torvalds }
104*1da177e4SLinus Torvalds 
105*1da177e4SLinus Torvalds static inline int
106*1da177e4SLinus Torvalds tapechar_check_idalbuffer(struct tape_device *device, size_t block_size)
107*1da177e4SLinus Torvalds {
108*1da177e4SLinus Torvalds 	struct idal_buffer *new;
109*1da177e4SLinus Torvalds 
110*1da177e4SLinus Torvalds 	if (device->char_data.idal_buf != NULL &&
111*1da177e4SLinus Torvalds 	    device->char_data.idal_buf->size == block_size)
112*1da177e4SLinus Torvalds 		return 0;
113*1da177e4SLinus Torvalds 
114*1da177e4SLinus Torvalds 	if (block_size > MAX_BLOCKSIZE) {
115*1da177e4SLinus Torvalds 		DBF_EVENT(3, "Invalid blocksize (%zd > %d)\n",
116*1da177e4SLinus Torvalds 			block_size, MAX_BLOCKSIZE);
117*1da177e4SLinus Torvalds 		PRINT_ERR("Invalid blocksize (%zd> %d)\n",
118*1da177e4SLinus Torvalds 			block_size, MAX_BLOCKSIZE);
119*1da177e4SLinus Torvalds 		return -EINVAL;
120*1da177e4SLinus Torvalds 	}
121*1da177e4SLinus Torvalds 
122*1da177e4SLinus Torvalds 	/* The current idal buffer is not correct. Allocate a new one. */
123*1da177e4SLinus Torvalds 	new = idal_buffer_alloc(block_size, 0);
124*1da177e4SLinus Torvalds 	if (new == NULL)
125*1da177e4SLinus Torvalds 		return -ENOMEM;
126*1da177e4SLinus Torvalds 
127*1da177e4SLinus Torvalds 	if (device->char_data.idal_buf != NULL)
128*1da177e4SLinus Torvalds 		idal_buffer_free(device->char_data.idal_buf);
129*1da177e4SLinus Torvalds 
130*1da177e4SLinus Torvalds 	device->char_data.idal_buf = new;
131*1da177e4SLinus Torvalds 
132*1da177e4SLinus Torvalds 	return 0;
133*1da177e4SLinus Torvalds }
134*1da177e4SLinus Torvalds 
135*1da177e4SLinus Torvalds /*
136*1da177e4SLinus Torvalds  * Tape device read function
137*1da177e4SLinus Torvalds  */
138*1da177e4SLinus Torvalds ssize_t
139*1da177e4SLinus Torvalds tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
140*1da177e4SLinus Torvalds {
141*1da177e4SLinus Torvalds 	struct tape_device *device;
142*1da177e4SLinus Torvalds 	struct tape_request *request;
143*1da177e4SLinus Torvalds 	size_t block_size;
144*1da177e4SLinus Torvalds 	int rc;
145*1da177e4SLinus Torvalds 
146*1da177e4SLinus Torvalds 	DBF_EVENT(6, "TCHAR:read\n");
147*1da177e4SLinus Torvalds 	device = (struct tape_device *) filp->private_data;
148*1da177e4SLinus Torvalds 
149*1da177e4SLinus Torvalds 	/*
150*1da177e4SLinus Torvalds 	 * If the tape isn't terminated yet, do it now. And since we then
151*1da177e4SLinus Torvalds 	 * are at the end of the tape there wouldn't be anything to read
152*1da177e4SLinus Torvalds 	 * anyways. So we return immediatly.
153*1da177e4SLinus Torvalds 	 */
154*1da177e4SLinus Torvalds 	if(device->required_tapemarks) {
155*1da177e4SLinus Torvalds 		return tape_std_terminate_write(device);
156*1da177e4SLinus Torvalds 	}
157*1da177e4SLinus Torvalds 
158*1da177e4SLinus Torvalds 	/* Find out block size to use */
159*1da177e4SLinus Torvalds 	if (device->char_data.block_size != 0) {
160*1da177e4SLinus Torvalds 		if (count < device->char_data.block_size) {
161*1da177e4SLinus Torvalds 			DBF_EVENT(3, "TCHAR:read smaller than block "
162*1da177e4SLinus Torvalds 				  "size was requested\n");
163*1da177e4SLinus Torvalds 			return -EINVAL;
164*1da177e4SLinus Torvalds 		}
165*1da177e4SLinus Torvalds 		block_size = device->char_data.block_size;
166*1da177e4SLinus Torvalds 	} else {
167*1da177e4SLinus Torvalds 		block_size = count;
168*1da177e4SLinus Torvalds 	}
169*1da177e4SLinus Torvalds 
170*1da177e4SLinus Torvalds 	rc = tapechar_check_idalbuffer(device, block_size);
171*1da177e4SLinus Torvalds 	if (rc)
172*1da177e4SLinus Torvalds 		return rc;
173*1da177e4SLinus Torvalds 
174*1da177e4SLinus Torvalds #ifdef CONFIG_S390_TAPE_BLOCK
175*1da177e4SLinus Torvalds 	/* Changes position. */
176*1da177e4SLinus Torvalds 	device->blk_data.medium_changed = 1;
177*1da177e4SLinus Torvalds #endif
178*1da177e4SLinus Torvalds 
179*1da177e4SLinus Torvalds 	DBF_EVENT(6, "TCHAR:nbytes: %lx\n", block_size);
180*1da177e4SLinus Torvalds 	/* Let the discipline build the ccw chain. */
181*1da177e4SLinus Torvalds 	request = device->discipline->read_block(device, block_size);
182*1da177e4SLinus Torvalds 	if (IS_ERR(request))
183*1da177e4SLinus Torvalds 		return PTR_ERR(request);
184*1da177e4SLinus Torvalds 	/* Execute it. */
185*1da177e4SLinus Torvalds 	rc = tape_do_io(device, request);
186*1da177e4SLinus Torvalds 	if (rc == 0) {
187*1da177e4SLinus Torvalds 		rc = block_size - request->rescnt;
188*1da177e4SLinus Torvalds 		DBF_EVENT(6, "TCHAR:rbytes:  %x\n", rc);
189*1da177e4SLinus Torvalds 		filp->f_pos += rc;
190*1da177e4SLinus Torvalds 		/* Copy data from idal buffer to user space. */
191*1da177e4SLinus Torvalds 		if (idal_buffer_to_user(device->char_data.idal_buf,
192*1da177e4SLinus Torvalds 					data, rc) != 0)
193*1da177e4SLinus Torvalds 			rc = -EFAULT;
194*1da177e4SLinus Torvalds 	}
195*1da177e4SLinus Torvalds 	tape_free_request(request);
196*1da177e4SLinus Torvalds 	return rc;
197*1da177e4SLinus Torvalds }
198*1da177e4SLinus Torvalds 
199*1da177e4SLinus Torvalds /*
200*1da177e4SLinus Torvalds  * Tape device write function
201*1da177e4SLinus Torvalds  */
202*1da177e4SLinus Torvalds ssize_t
203*1da177e4SLinus Torvalds tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t *ppos)
204*1da177e4SLinus Torvalds {
205*1da177e4SLinus Torvalds 	struct tape_device *device;
206*1da177e4SLinus Torvalds 	struct tape_request *request;
207*1da177e4SLinus Torvalds 	size_t block_size;
208*1da177e4SLinus Torvalds 	size_t written;
209*1da177e4SLinus Torvalds 	int nblocks;
210*1da177e4SLinus Torvalds 	int i, rc;
211*1da177e4SLinus Torvalds 
212*1da177e4SLinus Torvalds 	DBF_EVENT(6, "TCHAR:write\n");
213*1da177e4SLinus Torvalds 	device = (struct tape_device *) filp->private_data;
214*1da177e4SLinus Torvalds 	/* Find out block size and number of blocks */
215*1da177e4SLinus Torvalds 	if (device->char_data.block_size != 0) {
216*1da177e4SLinus Torvalds 		if (count < device->char_data.block_size) {
217*1da177e4SLinus Torvalds 			DBF_EVENT(3, "TCHAR:write smaller than block "
218*1da177e4SLinus Torvalds 				  "size was requested\n");
219*1da177e4SLinus Torvalds 			return -EINVAL;
220*1da177e4SLinus Torvalds 		}
221*1da177e4SLinus Torvalds 		block_size = device->char_data.block_size;
222*1da177e4SLinus Torvalds 		nblocks = count / block_size;
223*1da177e4SLinus Torvalds 	} else {
224*1da177e4SLinus Torvalds 		block_size = count;
225*1da177e4SLinus Torvalds 		nblocks = 1;
226*1da177e4SLinus Torvalds 	}
227*1da177e4SLinus Torvalds 
228*1da177e4SLinus Torvalds 	rc = tapechar_check_idalbuffer(device, block_size);
229*1da177e4SLinus Torvalds 	if (rc)
230*1da177e4SLinus Torvalds 		return rc;
231*1da177e4SLinus Torvalds 
232*1da177e4SLinus Torvalds #ifdef CONFIG_S390_TAPE_BLOCK
233*1da177e4SLinus Torvalds 	/* Changes position. */
234*1da177e4SLinus Torvalds 	device->blk_data.medium_changed = 1;
235*1da177e4SLinus Torvalds #endif
236*1da177e4SLinus Torvalds 
237*1da177e4SLinus Torvalds 	DBF_EVENT(6,"TCHAR:nbytes: %lx\n", block_size);
238*1da177e4SLinus Torvalds 	DBF_EVENT(6, "TCHAR:nblocks: %x\n", nblocks);
239*1da177e4SLinus Torvalds 	/* Let the discipline build the ccw chain. */
240*1da177e4SLinus Torvalds 	request = device->discipline->write_block(device, block_size);
241*1da177e4SLinus Torvalds 	if (IS_ERR(request))
242*1da177e4SLinus Torvalds 		return PTR_ERR(request);
243*1da177e4SLinus Torvalds 	rc = 0;
244*1da177e4SLinus Torvalds 	written = 0;
245*1da177e4SLinus Torvalds 	for (i = 0; i < nblocks; i++) {
246*1da177e4SLinus Torvalds 		/* Copy data from user space to idal buffer. */
247*1da177e4SLinus Torvalds 		if (idal_buffer_from_user(device->char_data.idal_buf,
248*1da177e4SLinus Torvalds 					  data, block_size)) {
249*1da177e4SLinus Torvalds 			rc = -EFAULT;
250*1da177e4SLinus Torvalds 			break;
251*1da177e4SLinus Torvalds 		}
252*1da177e4SLinus Torvalds 		rc = tape_do_io(device, request);
253*1da177e4SLinus Torvalds 		if (rc)
254*1da177e4SLinus Torvalds 			break;
255*1da177e4SLinus Torvalds 		DBF_EVENT(6, "TCHAR:wbytes: %lx\n",
256*1da177e4SLinus Torvalds 			  block_size - request->rescnt);
257*1da177e4SLinus Torvalds 		filp->f_pos += block_size - request->rescnt;
258*1da177e4SLinus Torvalds 		written += block_size - request->rescnt;
259*1da177e4SLinus Torvalds 		if (request->rescnt != 0)
260*1da177e4SLinus Torvalds 			break;
261*1da177e4SLinus Torvalds 		data += block_size;
262*1da177e4SLinus Torvalds 	}
263*1da177e4SLinus Torvalds 	tape_free_request(request);
264*1da177e4SLinus Torvalds 	if (rc == -ENOSPC) {
265*1da177e4SLinus Torvalds 		/*
266*1da177e4SLinus Torvalds 		 * Ok, the device has no more space. It has NOT written
267*1da177e4SLinus Torvalds 		 * the block.
268*1da177e4SLinus Torvalds 		 */
269*1da177e4SLinus Torvalds 		if (device->discipline->process_eov)
270*1da177e4SLinus Torvalds 			device->discipline->process_eov(device);
271*1da177e4SLinus Torvalds 		if (written > 0)
272*1da177e4SLinus Torvalds 			rc = 0;
273*1da177e4SLinus Torvalds 
274*1da177e4SLinus Torvalds 	}
275*1da177e4SLinus Torvalds 
276*1da177e4SLinus Torvalds 	/*
277*1da177e4SLinus Torvalds 	 * After doing a write we always need two tapemarks to correctly
278*1da177e4SLinus Torvalds 	 * terminate the tape (one to terminate the file, the second to
279*1da177e4SLinus Torvalds 	 * flag the end of recorded data.
280*1da177e4SLinus Torvalds 	 * Since process_eov positions the tape in front of the written
281*1da177e4SLinus Torvalds 	 * tapemark it doesn't hurt to write two marks again.
282*1da177e4SLinus Torvalds 	 */
283*1da177e4SLinus Torvalds 	if (!rc)
284*1da177e4SLinus Torvalds 		device->required_tapemarks = 2;
285*1da177e4SLinus Torvalds 
286*1da177e4SLinus Torvalds 	return rc ? rc : written;
287*1da177e4SLinus Torvalds }
288*1da177e4SLinus Torvalds 
289*1da177e4SLinus Torvalds /*
290*1da177e4SLinus Torvalds  * Character frontend tape device open function.
291*1da177e4SLinus Torvalds  */
292*1da177e4SLinus Torvalds int
293*1da177e4SLinus Torvalds tapechar_open (struct inode *inode, struct file *filp)
294*1da177e4SLinus Torvalds {
295*1da177e4SLinus Torvalds 	struct tape_device *device;
296*1da177e4SLinus Torvalds 	int minor, rc;
297*1da177e4SLinus Torvalds 
298*1da177e4SLinus Torvalds 	DBF_EVENT(6, "TCHAR:open: %i:%i\n",
299*1da177e4SLinus Torvalds 		imajor(filp->f_dentry->d_inode),
300*1da177e4SLinus Torvalds 		iminor(filp->f_dentry->d_inode));
301*1da177e4SLinus Torvalds 
302*1da177e4SLinus Torvalds 	if (imajor(filp->f_dentry->d_inode) != tapechar_major)
303*1da177e4SLinus Torvalds 		return -ENODEV;
304*1da177e4SLinus Torvalds 
305*1da177e4SLinus Torvalds 	minor = iminor(filp->f_dentry->d_inode);
306*1da177e4SLinus Torvalds 	device = tape_get_device(minor / TAPE_MINORS_PER_DEV);
307*1da177e4SLinus Torvalds 	if (IS_ERR(device)) {
308*1da177e4SLinus Torvalds 		DBF_EVENT(3, "TCHAR:open: tape_get_device() failed\n");
309*1da177e4SLinus Torvalds 		return PTR_ERR(device);
310*1da177e4SLinus Torvalds 	}
311*1da177e4SLinus Torvalds 
312*1da177e4SLinus Torvalds 
313*1da177e4SLinus Torvalds 	rc = tape_open(device);
314*1da177e4SLinus Torvalds 	if (rc == 0) {
315*1da177e4SLinus Torvalds 		filp->private_data = device;
316*1da177e4SLinus Torvalds 		return nonseekable_open(inode, filp);
317*1da177e4SLinus Torvalds 	}
318*1da177e4SLinus Torvalds 	tape_put_device(device);
319*1da177e4SLinus Torvalds 
320*1da177e4SLinus Torvalds 	return rc;
321*1da177e4SLinus Torvalds }
322*1da177e4SLinus Torvalds 
323*1da177e4SLinus Torvalds /*
324*1da177e4SLinus Torvalds  * Character frontend tape device release function.
325*1da177e4SLinus Torvalds  */
326*1da177e4SLinus Torvalds 
327*1da177e4SLinus Torvalds int
328*1da177e4SLinus Torvalds tapechar_release(struct inode *inode, struct file *filp)
329*1da177e4SLinus Torvalds {
330*1da177e4SLinus Torvalds 	struct tape_device *device;
331*1da177e4SLinus Torvalds 
332*1da177e4SLinus Torvalds 	DBF_EVENT(6, "TCHAR:release: %x\n", iminor(inode));
333*1da177e4SLinus Torvalds 	device = (struct tape_device *) filp->private_data;
334*1da177e4SLinus Torvalds 
335*1da177e4SLinus Torvalds 	/*
336*1da177e4SLinus Torvalds 	 * If this is the rewinding tape minor then rewind. In that case we
337*1da177e4SLinus Torvalds 	 * write all required tapemarks. Otherwise only one to terminate the
338*1da177e4SLinus Torvalds 	 * file.
339*1da177e4SLinus Torvalds 	 */
340*1da177e4SLinus Torvalds 	if ((iminor(inode) & 1) != 0) {
341*1da177e4SLinus Torvalds 		if (device->required_tapemarks)
342*1da177e4SLinus Torvalds 			tape_std_terminate_write(device);
343*1da177e4SLinus Torvalds 		tape_mtop(device, MTREW, 1);
344*1da177e4SLinus Torvalds 	} else {
345*1da177e4SLinus Torvalds 		if (device->required_tapemarks > 1) {
346*1da177e4SLinus Torvalds 			if (tape_mtop(device, MTWEOF, 1) == 0)
347*1da177e4SLinus Torvalds 				device->required_tapemarks--;
348*1da177e4SLinus Torvalds 		}
349*1da177e4SLinus Torvalds 	}
350*1da177e4SLinus Torvalds 
351*1da177e4SLinus Torvalds 	if (device->char_data.idal_buf != NULL) {
352*1da177e4SLinus Torvalds 		idal_buffer_free(device->char_data.idal_buf);
353*1da177e4SLinus Torvalds 		device->char_data.idal_buf = NULL;
354*1da177e4SLinus Torvalds 	}
355*1da177e4SLinus Torvalds 	tape_release(device);
356*1da177e4SLinus Torvalds 	filp->private_data = tape_put_device(device);
357*1da177e4SLinus Torvalds 
358*1da177e4SLinus Torvalds 	return 0;
359*1da177e4SLinus Torvalds }
360*1da177e4SLinus Torvalds 
361*1da177e4SLinus Torvalds /*
362*1da177e4SLinus Torvalds  * Tape device io controls.
363*1da177e4SLinus Torvalds  */
364*1da177e4SLinus Torvalds static int
365*1da177e4SLinus Torvalds tapechar_ioctl(struct inode *inp, struct file *filp,
366*1da177e4SLinus Torvalds 	       unsigned int no, unsigned long data)
367*1da177e4SLinus Torvalds {
368*1da177e4SLinus Torvalds 	struct tape_device *device;
369*1da177e4SLinus Torvalds 	int rc;
370*1da177e4SLinus Torvalds 
371*1da177e4SLinus Torvalds 	DBF_EVENT(6, "TCHAR:ioct\n");
372*1da177e4SLinus Torvalds 
373*1da177e4SLinus Torvalds 	device = (struct tape_device *) filp->private_data;
374*1da177e4SLinus Torvalds 
375*1da177e4SLinus Torvalds 	if (no == MTIOCTOP) {
376*1da177e4SLinus Torvalds 		struct mtop op;
377*1da177e4SLinus Torvalds 
378*1da177e4SLinus Torvalds 		if (copy_from_user(&op, (char __user *) data, sizeof(op)) != 0)
379*1da177e4SLinus Torvalds 			return -EFAULT;
380*1da177e4SLinus Torvalds 		if (op.mt_count < 0)
381*1da177e4SLinus Torvalds 			return -EINVAL;
382*1da177e4SLinus Torvalds 
383*1da177e4SLinus Torvalds 		/*
384*1da177e4SLinus Torvalds 		 * Operations that change tape position should write final
385*1da177e4SLinus Torvalds 		 * tapemarks.
386*1da177e4SLinus Torvalds 		 */
387*1da177e4SLinus Torvalds 		switch (op.mt_op) {
388*1da177e4SLinus Torvalds 			case MTFSF:
389*1da177e4SLinus Torvalds 			case MTBSF:
390*1da177e4SLinus Torvalds 			case MTFSR:
391*1da177e4SLinus Torvalds 			case MTBSR:
392*1da177e4SLinus Torvalds 			case MTREW:
393*1da177e4SLinus Torvalds 			case MTOFFL:
394*1da177e4SLinus Torvalds 			case MTEOM:
395*1da177e4SLinus Torvalds 			case MTRETEN:
396*1da177e4SLinus Torvalds 			case MTBSFM:
397*1da177e4SLinus Torvalds 			case MTFSFM:
398*1da177e4SLinus Torvalds 			case MTSEEK:
399*1da177e4SLinus Torvalds #ifdef CONFIG_S390_TAPE_BLOCK
400*1da177e4SLinus Torvalds 				device->blk_data.medium_changed = 1;
401*1da177e4SLinus Torvalds #endif
402*1da177e4SLinus Torvalds 				if (device->required_tapemarks)
403*1da177e4SLinus Torvalds 					tape_std_terminate_write(device);
404*1da177e4SLinus Torvalds 			default:
405*1da177e4SLinus Torvalds 				;
406*1da177e4SLinus Torvalds 		}
407*1da177e4SLinus Torvalds 		rc = tape_mtop(device, op.mt_op, op.mt_count);
408*1da177e4SLinus Torvalds 
409*1da177e4SLinus Torvalds 		if (op.mt_op == MTWEOF && rc == 0) {
410*1da177e4SLinus Torvalds 			if (op.mt_count > device->required_tapemarks)
411*1da177e4SLinus Torvalds 				device->required_tapemarks = 0;
412*1da177e4SLinus Torvalds 			else
413*1da177e4SLinus Torvalds 				device->required_tapemarks -= op.mt_count;
414*1da177e4SLinus Torvalds 		}
415*1da177e4SLinus Torvalds 		return rc;
416*1da177e4SLinus Torvalds 	}
417*1da177e4SLinus Torvalds 	if (no == MTIOCPOS) {
418*1da177e4SLinus Torvalds 		/* MTIOCPOS: query the tape position. */
419*1da177e4SLinus Torvalds 		struct mtpos pos;
420*1da177e4SLinus Torvalds 
421*1da177e4SLinus Torvalds 		rc = tape_mtop(device, MTTELL, 1);
422*1da177e4SLinus Torvalds 		if (rc < 0)
423*1da177e4SLinus Torvalds 			return rc;
424*1da177e4SLinus Torvalds 		pos.mt_blkno = rc;
425*1da177e4SLinus Torvalds 		if (copy_to_user((char __user *) data, &pos, sizeof(pos)) != 0)
426*1da177e4SLinus Torvalds 			return -EFAULT;
427*1da177e4SLinus Torvalds 		return 0;
428*1da177e4SLinus Torvalds 	}
429*1da177e4SLinus Torvalds 	if (no == MTIOCGET) {
430*1da177e4SLinus Torvalds 		/* MTIOCGET: query the tape drive status. */
431*1da177e4SLinus Torvalds 		struct mtget get;
432*1da177e4SLinus Torvalds 
433*1da177e4SLinus Torvalds 		memset(&get, 0, sizeof(get));
434*1da177e4SLinus Torvalds 		get.mt_type = MT_ISUNKNOWN;
435*1da177e4SLinus Torvalds 		get.mt_resid = 0 /* device->devstat.rescnt */;
436*1da177e4SLinus Torvalds 		get.mt_dsreg = device->tape_state;
437*1da177e4SLinus Torvalds 		/* FIXME: mt_gstat, mt_erreg, mt_fileno */
438*1da177e4SLinus Torvalds 		get.mt_gstat = 0;
439*1da177e4SLinus Torvalds 		get.mt_erreg = 0;
440*1da177e4SLinus Torvalds 		get.mt_fileno = 0;
441*1da177e4SLinus Torvalds 		get.mt_gstat  = device->tape_generic_status;
442*1da177e4SLinus Torvalds 
443*1da177e4SLinus Torvalds 		if (device->medium_state == MS_LOADED) {
444*1da177e4SLinus Torvalds 			rc = tape_mtop(device, MTTELL, 1);
445*1da177e4SLinus Torvalds 
446*1da177e4SLinus Torvalds 			if (rc < 0)
447*1da177e4SLinus Torvalds 				return rc;
448*1da177e4SLinus Torvalds 
449*1da177e4SLinus Torvalds 			if (rc == 0)
450*1da177e4SLinus Torvalds 				get.mt_gstat |= GMT_BOT(~0);
451*1da177e4SLinus Torvalds 
452*1da177e4SLinus Torvalds 			get.mt_blkno = rc;
453*1da177e4SLinus Torvalds 		}
454*1da177e4SLinus Torvalds 
455*1da177e4SLinus Torvalds 		if (copy_to_user((char __user *) data, &get, sizeof(get)) != 0)
456*1da177e4SLinus Torvalds 			return -EFAULT;
457*1da177e4SLinus Torvalds 
458*1da177e4SLinus Torvalds 		return 0;
459*1da177e4SLinus Torvalds 	}
460*1da177e4SLinus Torvalds 	/* Try the discipline ioctl function. */
461*1da177e4SLinus Torvalds 	if (device->discipline->ioctl_fn == NULL)
462*1da177e4SLinus Torvalds 		return -EINVAL;
463*1da177e4SLinus Torvalds 	return device->discipline->ioctl_fn(device, no, data);
464*1da177e4SLinus Torvalds }
465*1da177e4SLinus Torvalds 
466*1da177e4SLinus Torvalds /*
467*1da177e4SLinus Torvalds  * Initialize character device frontend.
468*1da177e4SLinus Torvalds  */
469*1da177e4SLinus Torvalds int
470*1da177e4SLinus Torvalds tapechar_init (void)
471*1da177e4SLinus Torvalds {
472*1da177e4SLinus Torvalds 	dev_t	dev;
473*1da177e4SLinus Torvalds 
474*1da177e4SLinus Torvalds 	if (alloc_chrdev_region(&dev, 0, 256, "tape") != 0)
475*1da177e4SLinus Torvalds 		return -1;
476*1da177e4SLinus Torvalds 
477*1da177e4SLinus Torvalds 	tapechar_major = MAJOR(dev);
478*1da177e4SLinus Torvalds 	PRINT_INFO("tape gets major %d for character devices\n", MAJOR(dev));
479*1da177e4SLinus Torvalds 
480*1da177e4SLinus Torvalds 	return 0;
481*1da177e4SLinus Torvalds }
482*1da177e4SLinus Torvalds 
483*1da177e4SLinus Torvalds /*
484*1da177e4SLinus Torvalds  * cleanup
485*1da177e4SLinus Torvalds  */
486*1da177e4SLinus Torvalds void
487*1da177e4SLinus Torvalds tapechar_exit(void)
488*1da177e4SLinus Torvalds {
489*1da177e4SLinus Torvalds 	PRINT_INFO("tape releases major %d for character devices\n",
490*1da177e4SLinus Torvalds 		tapechar_major);
491*1da177e4SLinus Torvalds 	unregister_chrdev_region(MKDEV(tapechar_major, 0), 256);
492*1da177e4SLinus Torvalds }
493