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