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