18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *    character device frontend for tape device driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  S390 and zSeries version
68c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 2001, 2006
78c2ecf20Sopenharmony_ci *    Author(s): Carsten Otte <cotte@de.ibm.com>
88c2ecf20Sopenharmony_ci *		 Michael Holzheu <holzheu@de.ibm.com>
98c2ecf20Sopenharmony_ci *		 Tuan Ngo-Anh <ngoanh@de.ibm.com>
108c2ecf20Sopenharmony_ci *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "tape"
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
198c2ecf20Sopenharmony_ci#include <linux/mtio.h>
208c2ecf20Sopenharmony_ci#include <linux/compat.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define TAPE_DBF_AREA	tape_core_dbf
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "tape.h"
278c2ecf20Sopenharmony_ci#include "tape_std.h"
288c2ecf20Sopenharmony_ci#include "tape_class.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define TAPECHAR_MAJOR		0	/* get dynamic major */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * file operation structure for tape character frontend
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_cistatic ssize_t tapechar_read(struct file *, char __user *, size_t, loff_t *);
368c2ecf20Sopenharmony_cistatic ssize_t tapechar_write(struct file *, const char __user *, size_t, loff_t *);
378c2ecf20Sopenharmony_cistatic int tapechar_open(struct inode *,struct file *);
388c2ecf20Sopenharmony_cistatic int tapechar_release(struct inode *,struct file *);
398c2ecf20Sopenharmony_cistatic long tapechar_ioctl(struct file *, unsigned int, unsigned long);
408c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
418c2ecf20Sopenharmony_cistatic long tapechar_compat_ioctl(struct file *, unsigned int, unsigned long);
428c2ecf20Sopenharmony_ci#endif
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic const struct file_operations tape_fops =
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
478c2ecf20Sopenharmony_ci	.read = tapechar_read,
488c2ecf20Sopenharmony_ci	.write = tapechar_write,
498c2ecf20Sopenharmony_ci	.unlocked_ioctl = tapechar_ioctl,
508c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
518c2ecf20Sopenharmony_ci	.compat_ioctl = tapechar_compat_ioctl,
528c2ecf20Sopenharmony_ci#endif
538c2ecf20Sopenharmony_ci	.open = tapechar_open,
548c2ecf20Sopenharmony_ci	.release = tapechar_release,
558c2ecf20Sopenharmony_ci	.llseek = no_llseek,
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int tapechar_major = TAPECHAR_MAJOR;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/*
618c2ecf20Sopenharmony_ci * This function is called for every new tapedevice
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_ciint
648c2ecf20Sopenharmony_citapechar_setup_device(struct tape_device * device)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	char	device_name[20];
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	sprintf(device_name, "ntibm%i", device->first_minor / 2);
698c2ecf20Sopenharmony_ci	device->nt = register_tape_dev(
708c2ecf20Sopenharmony_ci		&device->cdev->dev,
718c2ecf20Sopenharmony_ci		MKDEV(tapechar_major, device->first_minor),
728c2ecf20Sopenharmony_ci		&tape_fops,
738c2ecf20Sopenharmony_ci		device_name,
748c2ecf20Sopenharmony_ci		"non-rewinding"
758c2ecf20Sopenharmony_ci	);
768c2ecf20Sopenharmony_ci	device_name[0] = 'r';
778c2ecf20Sopenharmony_ci	device->rt = register_tape_dev(
788c2ecf20Sopenharmony_ci		&device->cdev->dev,
798c2ecf20Sopenharmony_ci		MKDEV(tapechar_major, device->first_minor + 1),
808c2ecf20Sopenharmony_ci		&tape_fops,
818c2ecf20Sopenharmony_ci		device_name,
828c2ecf20Sopenharmony_ci		"rewinding"
838c2ecf20Sopenharmony_ci	);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return 0;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_civoid
898c2ecf20Sopenharmony_citapechar_cleanup_device(struct tape_device *device)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	unregister_tape_dev(&device->cdev->dev, device->rt);
928c2ecf20Sopenharmony_ci	device->rt = NULL;
938c2ecf20Sopenharmony_ci	unregister_tape_dev(&device->cdev->dev, device->nt);
948c2ecf20Sopenharmony_ci	device->nt = NULL;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int
988c2ecf20Sopenharmony_citapechar_check_idalbuffer(struct tape_device *device, size_t block_size)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct idal_buffer *new;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (device->char_data.idal_buf != NULL &&
1038c2ecf20Sopenharmony_ci	    device->char_data.idal_buf->size == block_size)
1048c2ecf20Sopenharmony_ci		return 0;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (block_size > MAX_BLOCKSIZE) {
1078c2ecf20Sopenharmony_ci		DBF_EVENT(3, "Invalid blocksize (%zd > %d)\n",
1088c2ecf20Sopenharmony_ci			block_size, MAX_BLOCKSIZE);
1098c2ecf20Sopenharmony_ci		return -EINVAL;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* The current idal buffer is not correct. Allocate a new one. */
1138c2ecf20Sopenharmony_ci	new = idal_buffer_alloc(block_size, 0);
1148c2ecf20Sopenharmony_ci	if (IS_ERR(new))
1158c2ecf20Sopenharmony_ci		return -ENOMEM;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (device->char_data.idal_buf != NULL)
1188c2ecf20Sopenharmony_ci		idal_buffer_free(device->char_data.idal_buf);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	device->char_data.idal_buf = new;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/*
1268c2ecf20Sopenharmony_ci * Tape device read function
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_cistatic ssize_t
1298c2ecf20Sopenharmony_citapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct tape_device *device;
1328c2ecf20Sopenharmony_ci	struct tape_request *request;
1338c2ecf20Sopenharmony_ci	size_t block_size;
1348c2ecf20Sopenharmony_ci	int rc;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	DBF_EVENT(6, "TCHAR:read\n");
1378c2ecf20Sopenharmony_ci	device = (struct tape_device *) filp->private_data;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/*
1408c2ecf20Sopenharmony_ci	 * If the tape isn't terminated yet, do it now. And since we then
1418c2ecf20Sopenharmony_ci	 * are at the end of the tape there wouldn't be anything to read
1428c2ecf20Sopenharmony_ci	 * anyways. So we return immediately.
1438c2ecf20Sopenharmony_ci	 */
1448c2ecf20Sopenharmony_ci	if(device->required_tapemarks) {
1458c2ecf20Sopenharmony_ci		return tape_std_terminate_write(device);
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* Find out block size to use */
1498c2ecf20Sopenharmony_ci	if (device->char_data.block_size != 0) {
1508c2ecf20Sopenharmony_ci		if (count < device->char_data.block_size) {
1518c2ecf20Sopenharmony_ci			DBF_EVENT(3, "TCHAR:read smaller than block "
1528c2ecf20Sopenharmony_ci				  "size was requested\n");
1538c2ecf20Sopenharmony_ci			return -EINVAL;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci		block_size = device->char_data.block_size;
1568c2ecf20Sopenharmony_ci	} else {
1578c2ecf20Sopenharmony_ci		block_size = count;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	rc = tapechar_check_idalbuffer(device, block_size);
1618c2ecf20Sopenharmony_ci	if (rc)
1628c2ecf20Sopenharmony_ci		return rc;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	DBF_EVENT(6, "TCHAR:nbytes: %lx\n", block_size);
1658c2ecf20Sopenharmony_ci	/* Let the discipline build the ccw chain. */
1668c2ecf20Sopenharmony_ci	request = device->discipline->read_block(device, block_size);
1678c2ecf20Sopenharmony_ci	if (IS_ERR(request))
1688c2ecf20Sopenharmony_ci		return PTR_ERR(request);
1698c2ecf20Sopenharmony_ci	/* Execute it. */
1708c2ecf20Sopenharmony_ci	rc = tape_do_io(device, request);
1718c2ecf20Sopenharmony_ci	if (rc == 0) {
1728c2ecf20Sopenharmony_ci		rc = block_size - request->rescnt;
1738c2ecf20Sopenharmony_ci		DBF_EVENT(6, "TCHAR:rbytes:  %x\n", rc);
1748c2ecf20Sopenharmony_ci		/* Copy data from idal buffer to user space. */
1758c2ecf20Sopenharmony_ci		if (idal_buffer_to_user(device->char_data.idal_buf,
1768c2ecf20Sopenharmony_ci					data, rc) != 0)
1778c2ecf20Sopenharmony_ci			rc = -EFAULT;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci	tape_free_request(request);
1808c2ecf20Sopenharmony_ci	return rc;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/*
1848c2ecf20Sopenharmony_ci * Tape device write function
1858c2ecf20Sopenharmony_ci */
1868c2ecf20Sopenharmony_cistatic ssize_t
1878c2ecf20Sopenharmony_citapechar_write(struct file *filp, const char __user *data, size_t count, loff_t *ppos)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct tape_device *device;
1908c2ecf20Sopenharmony_ci	struct tape_request *request;
1918c2ecf20Sopenharmony_ci	size_t block_size;
1928c2ecf20Sopenharmony_ci	size_t written;
1938c2ecf20Sopenharmony_ci	int nblocks;
1948c2ecf20Sopenharmony_ci	int i, rc;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	DBF_EVENT(6, "TCHAR:write\n");
1978c2ecf20Sopenharmony_ci	device = (struct tape_device *) filp->private_data;
1988c2ecf20Sopenharmony_ci	/* Find out block size and number of blocks */
1998c2ecf20Sopenharmony_ci	if (device->char_data.block_size != 0) {
2008c2ecf20Sopenharmony_ci		if (count < device->char_data.block_size) {
2018c2ecf20Sopenharmony_ci			DBF_EVENT(3, "TCHAR:write smaller than block "
2028c2ecf20Sopenharmony_ci				  "size was requested\n");
2038c2ecf20Sopenharmony_ci			return -EINVAL;
2048c2ecf20Sopenharmony_ci		}
2058c2ecf20Sopenharmony_ci		block_size = device->char_data.block_size;
2068c2ecf20Sopenharmony_ci		nblocks = count / block_size;
2078c2ecf20Sopenharmony_ci	} else {
2088c2ecf20Sopenharmony_ci		block_size = count;
2098c2ecf20Sopenharmony_ci		nblocks = 1;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	rc = tapechar_check_idalbuffer(device, block_size);
2138c2ecf20Sopenharmony_ci	if (rc)
2148c2ecf20Sopenharmony_ci		return rc;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	DBF_EVENT(6,"TCHAR:nbytes: %lx\n", block_size);
2178c2ecf20Sopenharmony_ci	DBF_EVENT(6, "TCHAR:nblocks: %x\n", nblocks);
2188c2ecf20Sopenharmony_ci	/* Let the discipline build the ccw chain. */
2198c2ecf20Sopenharmony_ci	request = device->discipline->write_block(device, block_size);
2208c2ecf20Sopenharmony_ci	if (IS_ERR(request))
2218c2ecf20Sopenharmony_ci		return PTR_ERR(request);
2228c2ecf20Sopenharmony_ci	rc = 0;
2238c2ecf20Sopenharmony_ci	written = 0;
2248c2ecf20Sopenharmony_ci	for (i = 0; i < nblocks; i++) {
2258c2ecf20Sopenharmony_ci		/* Copy data from user space to idal buffer. */
2268c2ecf20Sopenharmony_ci		if (idal_buffer_from_user(device->char_data.idal_buf,
2278c2ecf20Sopenharmony_ci					  data, block_size)) {
2288c2ecf20Sopenharmony_ci			rc = -EFAULT;
2298c2ecf20Sopenharmony_ci			break;
2308c2ecf20Sopenharmony_ci		}
2318c2ecf20Sopenharmony_ci		rc = tape_do_io(device, request);
2328c2ecf20Sopenharmony_ci		if (rc)
2338c2ecf20Sopenharmony_ci			break;
2348c2ecf20Sopenharmony_ci		DBF_EVENT(6, "TCHAR:wbytes: %lx\n",
2358c2ecf20Sopenharmony_ci			  block_size - request->rescnt);
2368c2ecf20Sopenharmony_ci		written += block_size - request->rescnt;
2378c2ecf20Sopenharmony_ci		if (request->rescnt != 0)
2388c2ecf20Sopenharmony_ci			break;
2398c2ecf20Sopenharmony_ci		data += block_size;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci	tape_free_request(request);
2428c2ecf20Sopenharmony_ci	if (rc == -ENOSPC) {
2438c2ecf20Sopenharmony_ci		/*
2448c2ecf20Sopenharmony_ci		 * Ok, the device has no more space. It has NOT written
2458c2ecf20Sopenharmony_ci		 * the block.
2468c2ecf20Sopenharmony_ci		 */
2478c2ecf20Sopenharmony_ci		if (device->discipline->process_eov)
2488c2ecf20Sopenharmony_ci			device->discipline->process_eov(device);
2498c2ecf20Sopenharmony_ci		if (written > 0)
2508c2ecf20Sopenharmony_ci			rc = 0;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/*
2558c2ecf20Sopenharmony_ci	 * After doing a write we always need two tapemarks to correctly
2568c2ecf20Sopenharmony_ci	 * terminate the tape (one to terminate the file, the second to
2578c2ecf20Sopenharmony_ci	 * flag the end of recorded data.
2588c2ecf20Sopenharmony_ci	 * Since process_eov positions the tape in front of the written
2598c2ecf20Sopenharmony_ci	 * tapemark it doesn't hurt to write two marks again.
2608c2ecf20Sopenharmony_ci	 */
2618c2ecf20Sopenharmony_ci	if (!rc)
2628c2ecf20Sopenharmony_ci		device->required_tapemarks = 2;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	return rc ? rc : written;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/*
2688c2ecf20Sopenharmony_ci * Character frontend tape device open function.
2698c2ecf20Sopenharmony_ci */
2708c2ecf20Sopenharmony_cistatic int
2718c2ecf20Sopenharmony_citapechar_open (struct inode *inode, struct file *filp)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct tape_device *device;
2748c2ecf20Sopenharmony_ci	int minor, rc;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	DBF_EVENT(6, "TCHAR:open: %i:%i\n",
2778c2ecf20Sopenharmony_ci		imajor(file_inode(filp)),
2788c2ecf20Sopenharmony_ci		iminor(file_inode(filp)));
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (imajor(file_inode(filp)) != tapechar_major)
2818c2ecf20Sopenharmony_ci		return -ENODEV;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	minor = iminor(file_inode(filp));
2848c2ecf20Sopenharmony_ci	device = tape_find_device(minor / TAPE_MINORS_PER_DEV);
2858c2ecf20Sopenharmony_ci	if (IS_ERR(device)) {
2868c2ecf20Sopenharmony_ci		DBF_EVENT(3, "TCHAR:open: tape_find_device() failed\n");
2878c2ecf20Sopenharmony_ci		return PTR_ERR(device);
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	rc = tape_open(device);
2918c2ecf20Sopenharmony_ci	if (rc == 0) {
2928c2ecf20Sopenharmony_ci		filp->private_data = device;
2938c2ecf20Sopenharmony_ci		stream_open(inode, filp);
2948c2ecf20Sopenharmony_ci	} else
2958c2ecf20Sopenharmony_ci		tape_put_device(device);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return rc;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci/*
3018c2ecf20Sopenharmony_ci * Character frontend tape device release function.
3028c2ecf20Sopenharmony_ci */
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic int
3058c2ecf20Sopenharmony_citapechar_release(struct inode *inode, struct file *filp)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct tape_device *device;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	DBF_EVENT(6, "TCHAR:release: %x\n", iminor(inode));
3108c2ecf20Sopenharmony_ci	device = (struct tape_device *) filp->private_data;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/*
3138c2ecf20Sopenharmony_ci	 * If this is the rewinding tape minor then rewind. In that case we
3148c2ecf20Sopenharmony_ci	 * write all required tapemarks. Otherwise only one to terminate the
3158c2ecf20Sopenharmony_ci	 * file.
3168c2ecf20Sopenharmony_ci	 */
3178c2ecf20Sopenharmony_ci	if ((iminor(inode) & 1) != 0) {
3188c2ecf20Sopenharmony_ci		if (device->required_tapemarks)
3198c2ecf20Sopenharmony_ci			tape_std_terminate_write(device);
3208c2ecf20Sopenharmony_ci		tape_mtop(device, MTREW, 1);
3218c2ecf20Sopenharmony_ci	} else {
3228c2ecf20Sopenharmony_ci		if (device->required_tapemarks > 1) {
3238c2ecf20Sopenharmony_ci			if (tape_mtop(device, MTWEOF, 1) == 0)
3248c2ecf20Sopenharmony_ci				device->required_tapemarks--;
3258c2ecf20Sopenharmony_ci		}
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (device->char_data.idal_buf != NULL) {
3298c2ecf20Sopenharmony_ci		idal_buffer_free(device->char_data.idal_buf);
3308c2ecf20Sopenharmony_ci		device->char_data.idal_buf = NULL;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci	tape_release(device);
3338c2ecf20Sopenharmony_ci	filp->private_data = NULL;
3348c2ecf20Sopenharmony_ci	tape_put_device(device);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	return 0;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci/*
3408c2ecf20Sopenharmony_ci * Tape device io controls.
3418c2ecf20Sopenharmony_ci */
3428c2ecf20Sopenharmony_cistatic int
3438c2ecf20Sopenharmony_ci__tapechar_ioctl(struct tape_device *device,
3448c2ecf20Sopenharmony_ci		 unsigned int no, void __user *data)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	int rc;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (no == MTIOCTOP) {
3498c2ecf20Sopenharmony_ci		struct mtop op;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		if (copy_from_user(&op, data, sizeof(op)) != 0)
3528c2ecf20Sopenharmony_ci			return -EFAULT;
3538c2ecf20Sopenharmony_ci		if (op.mt_count < 0)
3548c2ecf20Sopenharmony_ci			return -EINVAL;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		/*
3578c2ecf20Sopenharmony_ci		 * Operations that change tape position should write final
3588c2ecf20Sopenharmony_ci		 * tapemarks.
3598c2ecf20Sopenharmony_ci		 */
3608c2ecf20Sopenharmony_ci		switch (op.mt_op) {
3618c2ecf20Sopenharmony_ci			case MTFSF:
3628c2ecf20Sopenharmony_ci			case MTBSF:
3638c2ecf20Sopenharmony_ci			case MTFSR:
3648c2ecf20Sopenharmony_ci			case MTBSR:
3658c2ecf20Sopenharmony_ci			case MTREW:
3668c2ecf20Sopenharmony_ci			case MTOFFL:
3678c2ecf20Sopenharmony_ci			case MTEOM:
3688c2ecf20Sopenharmony_ci			case MTRETEN:
3698c2ecf20Sopenharmony_ci			case MTBSFM:
3708c2ecf20Sopenharmony_ci			case MTFSFM:
3718c2ecf20Sopenharmony_ci			case MTSEEK:
3728c2ecf20Sopenharmony_ci				if (device->required_tapemarks)
3738c2ecf20Sopenharmony_ci					tape_std_terminate_write(device);
3748c2ecf20Sopenharmony_ci			default:
3758c2ecf20Sopenharmony_ci				;
3768c2ecf20Sopenharmony_ci		}
3778c2ecf20Sopenharmony_ci		rc = tape_mtop(device, op.mt_op, op.mt_count);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		if (op.mt_op == MTWEOF && rc == 0) {
3808c2ecf20Sopenharmony_ci			if (op.mt_count > device->required_tapemarks)
3818c2ecf20Sopenharmony_ci				device->required_tapemarks = 0;
3828c2ecf20Sopenharmony_ci			else
3838c2ecf20Sopenharmony_ci				device->required_tapemarks -= op.mt_count;
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci		return rc;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci	if (no == MTIOCPOS) {
3888c2ecf20Sopenharmony_ci		/* MTIOCPOS: query the tape position. */
3898c2ecf20Sopenharmony_ci		struct mtpos pos;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci		rc = tape_mtop(device, MTTELL, 1);
3928c2ecf20Sopenharmony_ci		if (rc < 0)
3938c2ecf20Sopenharmony_ci			return rc;
3948c2ecf20Sopenharmony_ci		pos.mt_blkno = rc;
3958c2ecf20Sopenharmony_ci		return put_user_mtpos(data, &pos);
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci	if (no == MTIOCGET) {
3988c2ecf20Sopenharmony_ci		/* MTIOCGET: query the tape drive status. */
3998c2ecf20Sopenharmony_ci		struct mtget get;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci		memset(&get, 0, sizeof(get));
4028c2ecf20Sopenharmony_ci		get.mt_type = MT_ISUNKNOWN;
4038c2ecf20Sopenharmony_ci		get.mt_resid = 0 /* device->devstat.rescnt */;
4048c2ecf20Sopenharmony_ci		get.mt_dsreg =
4058c2ecf20Sopenharmony_ci			((device->char_data.block_size << MT_ST_BLKSIZE_SHIFT)
4068c2ecf20Sopenharmony_ci			 & MT_ST_BLKSIZE_MASK);
4078c2ecf20Sopenharmony_ci		/* FIXME: mt_gstat, mt_erreg, mt_fileno */
4088c2ecf20Sopenharmony_ci		get.mt_gstat = 0;
4098c2ecf20Sopenharmony_ci		get.mt_erreg = 0;
4108c2ecf20Sopenharmony_ci		get.mt_fileno = 0;
4118c2ecf20Sopenharmony_ci		get.mt_gstat  = device->tape_generic_status;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		if (device->medium_state == MS_LOADED) {
4148c2ecf20Sopenharmony_ci			rc = tape_mtop(device, MTTELL, 1);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci			if (rc < 0)
4178c2ecf20Sopenharmony_ci				return rc;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci			if (rc == 0)
4208c2ecf20Sopenharmony_ci				get.mt_gstat |= GMT_BOT(~0);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci			get.mt_blkno = rc;
4238c2ecf20Sopenharmony_ci		}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		return put_user_mtget(data, &get);
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci	/* Try the discipline ioctl function. */
4288c2ecf20Sopenharmony_ci	if (device->discipline->ioctl_fn == NULL)
4298c2ecf20Sopenharmony_ci		return -EINVAL;
4308c2ecf20Sopenharmony_ci	return device->discipline->ioctl_fn(device, no, (unsigned long)data);
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic long
4348c2ecf20Sopenharmony_citapechar_ioctl(struct file *filp, unsigned int no, unsigned long data)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	struct tape_device *device;
4378c2ecf20Sopenharmony_ci	long rc;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	DBF_EVENT(6, "TCHAR:ioct\n");
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	device = (struct tape_device *) filp->private_data;
4428c2ecf20Sopenharmony_ci	mutex_lock(&device->mutex);
4438c2ecf20Sopenharmony_ci	rc = __tapechar_ioctl(device, no, (void __user *)data);
4448c2ecf20Sopenharmony_ci	mutex_unlock(&device->mutex);
4458c2ecf20Sopenharmony_ci	return rc;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
4498c2ecf20Sopenharmony_cistatic long
4508c2ecf20Sopenharmony_citapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct tape_device *device = filp->private_data;
4538c2ecf20Sopenharmony_ci	long rc;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (no == MTIOCPOS32)
4568c2ecf20Sopenharmony_ci		no = MTIOCPOS;
4578c2ecf20Sopenharmony_ci	else if (no == MTIOCGET32)
4588c2ecf20Sopenharmony_ci		no = MTIOCGET;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	mutex_lock(&device->mutex);
4618c2ecf20Sopenharmony_ci	rc = __tapechar_ioctl(device, no, compat_ptr(data));
4628c2ecf20Sopenharmony_ci	mutex_unlock(&device->mutex);
4638c2ecf20Sopenharmony_ci	return rc;
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci#endif /* CONFIG_COMPAT */
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci/*
4688c2ecf20Sopenharmony_ci * Initialize character device frontend.
4698c2ecf20Sopenharmony_ci */
4708c2ecf20Sopenharmony_ciint
4718c2ecf20Sopenharmony_citapechar_init (void)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	dev_t	dev;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (alloc_chrdev_region(&dev, 0, 256, "tape") != 0)
4768c2ecf20Sopenharmony_ci		return -1;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	tapechar_major = MAJOR(dev);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	return 0;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci/*
4848c2ecf20Sopenharmony_ci * cleanup
4858c2ecf20Sopenharmony_ci */
4868c2ecf20Sopenharmony_civoid
4878c2ecf20Sopenharmony_citapechar_exit(void)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	unregister_chrdev_region(MKDEV(tapechar_major, 0), 256);
4908c2ecf20Sopenharmony_ci}
491