Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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