root/drivers/s390/char/tape_char.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tapechar_setup_device
  2. tapechar_cleanup_device
  3. tapechar_check_idalbuffer
  4. tapechar_read
  5. tapechar_write
  6. tapechar_open
  7. tapechar_release
  8. __tapechar_ioctl
  9. tapechar_ioctl
  10. tapechar_compat_ioctl
  11. tapechar_init
  12. tapechar_exit

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

/* [<][>][^][v][top][bottom][index][help] */