📄 vmur.c
字号:
} res = 0; copied += len; } while (copied != count); *offs += copied; rc = copied;fail: free_page((unsigned long) buf); return rc;}static ssize_t ur_read(struct file *file, char __user *ubuf, size_t count, loff_t *offs){ struct urdev *urd; int rc; TRACE("ur_read: count=%zu ppos=%li\n", count, (unsigned long) *offs); if (count == 0) return 0; urd = ((struct urfile *) file->private_data)->urd; rc = mutex_lock_interruptible(&urd->io_mutex); if (rc) return rc; rc = diag14_read(file, ubuf, count, offs); mutex_unlock(&urd->io_mutex); return rc;}/* * diagnose code 0x14 subcode 0x0fff - retrieve next file descriptor * cc=0 normal completion * cc=1 no files on reader queue or no subsequent file * cc=2 spid specified is invalid */static int diag_read_next_file_info(struct file_control_block *buf, int spid){ int cc; cc = diag14((unsigned long) buf, spid, 0xfff); switch (cc) { case 0: return 0; default: return -ENODATA; }}static int verify_uri_device(struct urdev *urd){ struct file_control_block *fcb; char *buf; int rc; fcb = kmalloc(sizeof(*fcb), GFP_KERNEL | GFP_DMA); if (!fcb) return -ENOMEM; /* check for empty reader device (beginning of chain) */ rc = diag_read_next_file_info(fcb, 0); if (rc) goto fail_free_fcb; /* if file is in hold status, we do not read it */ if (fcb->file_stat & (FLG_SYSTEM_HOLD | FLG_USER_HOLD)) { rc = -EPERM; goto fail_free_fcb; } /* open file on virtual reader */ buf = (char *) __get_free_page(GFP_KERNEL | GFP_DMA); if (!buf) { rc = -ENOMEM; goto fail_free_fcb; } rc = diag_read_file(urd->dev_id.devno, buf); if ((rc != 0) && (rc != -ENODATA)) /* EOF does not hurt */ goto fail_free_buf; /* check if the file on top of the queue is open now */ rc = diag_read_next_file_info(fcb, 0); if (rc) goto fail_free_buf; if (!(fcb->file_stat & FLG_IN_USE)) { rc = -EMFILE; goto fail_free_buf; } rc = 0;fail_free_buf: free_page((unsigned long) buf);fail_free_fcb: kfree(fcb); return rc;}static int verify_device(struct urdev *urd){ switch (urd->class) { case DEV_CLASS_UR_O: return 0; /* no check needed here */ case DEV_CLASS_UR_I: return verify_uri_device(urd); default: return -ENOTSUPP; }}static int get_uri_file_reclen(struct urdev *urd){ struct file_control_block *fcb; int rc; fcb = kmalloc(sizeof(*fcb), GFP_KERNEL | GFP_DMA); if (!fcb) return -ENOMEM; rc = diag_read_next_file_info(fcb, 0); if (rc) goto fail_free; if (fcb->file_stat & FLG_CP_DUMP) rc = 0; else rc = fcb->rec_len;fail_free: kfree(fcb); return rc;}static int get_file_reclen(struct urdev *urd){ switch (urd->class) { case DEV_CLASS_UR_O: return 0; case DEV_CLASS_UR_I: return get_uri_file_reclen(urd); default: return -ENOTSUPP; }}static int ur_open(struct inode *inode, struct file *file){ u16 devno; struct urdev *urd; struct urfile *urf; unsigned short accmode; int rc; accmode = file->f_flags & O_ACCMODE; if (accmode == O_RDWR) return -EACCES; /* * We treat the minor number as the devno of the ur device * to find in the driver tree. */ devno = MINOR(file->f_dentry->d_inode->i_rdev); urd = urdev_get_from_devno(devno); if (!urd) return -ENXIO; if (file->f_flags & O_NONBLOCK) { if (!mutex_trylock(&urd->open_mutex)) { rc = -EBUSY; goto fail_put; } } else { if (mutex_lock_interruptible(&urd->open_mutex)) { rc = -ERESTARTSYS; goto fail_put; } } TRACE("ur_open\n"); if (((accmode == O_RDONLY) && (urd->class != DEV_CLASS_UR_I)) || ((accmode == O_WRONLY) && (urd->class != DEV_CLASS_UR_O))) { TRACE("ur_open: unsupported dev class (%d)\n", urd->class); rc = -EACCES; goto fail_unlock; } rc = verify_device(urd); if (rc) goto fail_unlock; urf = urfile_alloc(urd); if (!urf) { rc = -ENOMEM; goto fail_unlock; } urf->dev_reclen = urd->reclen; rc = get_file_reclen(urd); if (rc < 0) goto fail_urfile_free; urf->file_reclen = rc; file->private_data = urf; return 0;fail_urfile_free: urfile_free(urf);fail_unlock: mutex_unlock(&urd->open_mutex);fail_put: urdev_put(urd); return rc;}static int ur_release(struct inode *inode, struct file *file){ struct urfile *urf = file->private_data; TRACE("ur_release\n"); mutex_unlock(&urf->urd->open_mutex); urdev_put(urf->urd); urfile_free(urf); return 0;}static loff_t ur_llseek(struct file *file, loff_t offset, int whence){ loff_t newpos; if ((file->f_flags & O_ACCMODE) != O_RDONLY) return -ESPIPE; /* seek allowed only for reader */ if (offset % PAGE_SIZE) return -ESPIPE; /* only multiples of 4K allowed */ switch (whence) { case 0: /* SEEK_SET */ newpos = offset; break; case 1: /* SEEK_CUR */ newpos = file->f_pos + offset; break; default: return -EINVAL; } file->f_pos = newpos; return newpos;}static struct file_operations ur_fops = { .owner = THIS_MODULE, .open = ur_open, .release = ur_release, .read = ur_read, .write = ur_write, .llseek = ur_llseek,};/* * ccw_device infrastructure: * ur_probe creates the struct urdev (with refcount = 1), the device * attributes, sets up the interrupt handler and validates the virtual * unit record device. * ur_remove removes the device attributes and drops the reference to * struct urdev. * * ur_probe, ur_remove, ur_set_online and ur_set_offline are serialized * by the vmur_mutex lock. * * urd->char_device is used as indication that the online function has * been completed successfully. */static int ur_probe(struct ccw_device *cdev){ struct urdev *urd; int rc; TRACE("ur_probe: cdev=%p\n", cdev); mutex_lock(&vmur_mutex); urd = urdev_alloc(cdev); if (!urd) { rc = -ENOMEM; goto fail_unlock; } rc = ur_create_attributes(&cdev->dev); if (rc) { rc = -ENOMEM; goto fail_urdev_put; } cdev->handler = ur_int_handler; /* validate virtual unit record device */ urd->class = get_urd_class(urd); if (urd->class < 0) { rc = urd->class; goto fail_remove_attr; } if ((urd->class != DEV_CLASS_UR_I) && (urd->class != DEV_CLASS_UR_O)) { rc = -ENOTSUPP; goto fail_remove_attr; } spin_lock_irq(get_ccwdev_lock(cdev)); cdev->dev.driver_data = urd; spin_unlock_irq(get_ccwdev_lock(cdev)); mutex_unlock(&vmur_mutex); return 0;fail_remove_attr: ur_remove_attributes(&cdev->dev);fail_urdev_put: urdev_put(urd);fail_unlock: mutex_unlock(&vmur_mutex); return rc;}static int ur_set_online(struct ccw_device *cdev){ struct urdev *urd; int minor, major, rc; char node_id[16]; TRACE("ur_set_online: cdev=%p\n", cdev); mutex_lock(&vmur_mutex); urd = urdev_get_from_cdev(cdev); if (!urd) { /* ur_remove already deleted our urd */ rc = -ENODEV; goto fail_unlock; } if (urd->char_device) { /* Another ur_set_online was faster */ rc = -EBUSY; goto fail_urdev_put; } minor = urd->dev_id.devno; major = MAJOR(ur_first_dev_maj_min); urd->char_device = cdev_alloc(); if (!urd->char_device) { rc = -ENOMEM; goto fail_urdev_put; } cdev_init(urd->char_device, &ur_fops); urd->char_device->dev = MKDEV(major, minor); urd->char_device->owner = ur_fops.owner; rc = cdev_add(urd->char_device, urd->char_device->dev, 1); if (rc) goto fail_free_cdev; if (urd->cdev->id.cu_type == READER_PUNCH_DEVTYPE) { if (urd->class == DEV_CLASS_UR_I) sprintf(node_id, "vmrdr-%s", cdev->dev.bus_id); if (urd->class == DEV_CLASS_UR_O) sprintf(node_id, "vmpun-%s", cdev->dev.bus_id); } else if (urd->cdev->id.cu_type == PRINTER_DEVTYPE) { sprintf(node_id, "vmprt-%s", cdev->dev.bus_id); } else { rc = -ENOTSUPP; goto fail_free_cdev; } urd->device = device_create(vmur_class, NULL, urd->char_device->dev, "%s", node_id); if (IS_ERR(urd->device)) { rc = PTR_ERR(urd->device); TRACE("ur_set_online: device_create rc=%d\n", rc); goto fail_free_cdev; } urdev_put(urd); mutex_unlock(&vmur_mutex); return 0;fail_free_cdev: cdev_del(urd->char_device); urd->char_device = NULL;fail_urdev_put: urdev_put(urd);fail_unlock: mutex_unlock(&vmur_mutex); return rc;}static int ur_set_offline_force(struct ccw_device *cdev, int force){ struct urdev *urd; int rc; TRACE("ur_set_offline: cdev=%p\n", cdev); urd = urdev_get_from_cdev(cdev); if (!urd) /* ur_remove already deleted our urd */ return -ENODEV; if (!urd->char_device) { /* Another ur_set_offline was faster */ rc = -EBUSY; goto fail_urdev_put; } if (!force && (atomic_read(&urd->ref_count) > 2)) { /* There is still a user of urd (e.g. ur_open) */ TRACE("ur_set_offline: BUSY\n"); rc = -EBUSY; goto fail_urdev_put; } device_destroy(vmur_class, urd->char_device->dev); cdev_del(urd->char_device); urd->char_device = NULL; rc = 0;fail_urdev_put: urdev_put(urd); return rc;}static int ur_set_offline(struct ccw_device *cdev){ int rc; mutex_lock(&vmur_mutex); rc = ur_set_offline_force(cdev, 0); mutex_unlock(&vmur_mutex); return rc;}static void ur_remove(struct ccw_device *cdev){ unsigned long flags; TRACE("ur_remove\n"); mutex_lock(&vmur_mutex); if (cdev->online) ur_set_offline_force(cdev, 1); ur_remove_attributes(&cdev->dev); spin_lock_irqsave(get_ccwdev_lock(cdev), flags); urdev_put(cdev->dev.driver_data); cdev->dev.driver_data = NULL; spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); mutex_unlock(&vmur_mutex);}/* * Module initialisation and cleanup */static int __init ur_init(void){ int rc; dev_t dev; if (!MACHINE_IS_VM) { PRINT_ERR("%s is only available under z/VM.\n", ur_banner); return -ENODEV; } vmur_dbf = debug_register("vmur", 4, 1, 4 * sizeof(long)); if (!vmur_dbf) return -ENOMEM; rc = debug_register_view(vmur_dbf, &debug_sprintf_view); if (rc) goto fail_free_dbf; debug_set_level(vmur_dbf, 6); rc = ccw_driver_register(&ur_driver); if (rc) goto fail_free_dbf; rc = alloc_chrdev_region(&dev, 0, NUM_MINORS, "vmur"); if (rc) { PRINT_ERR("alloc_chrdev_region failed: err = %d\n", rc); goto fail_unregister_driver; } ur_first_dev_maj_min = MKDEV(MAJOR(dev), 0); vmur_class = class_create(THIS_MODULE, "vmur"); if (IS_ERR(vmur_class)) { rc = PTR_ERR(vmur_class); goto fail_unregister_region; } PRINT_INFO("%s loaded.\n", ur_banner); return 0;fail_unregister_region: unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS);fail_unregister_driver: ccw_driver_unregister(&ur_driver);fail_free_dbf: debug_unregister(vmur_dbf); return rc;}static void __exit ur_exit(void){ class_destroy(vmur_class); unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS); ccw_driver_unregister(&ur_driver); debug_unregister(vmur_dbf); PRINT_INFO("%s unloaded.\n", ur_banner);}module_init(ur_init);module_exit(ur_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -