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