18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Linux driver for System z and s390 unit record devices
48c2ecf20Sopenharmony_ci * (z/VM virtual punch, reader, printer)
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2001, 2009
78c2ecf20Sopenharmony_ci * Authors: Malcolm Beattie <beattiem@uk.ibm.com>
88c2ecf20Sopenharmony_ci *	    Michael Holzheu <holzheu@de.ibm.com>
98c2ecf20Sopenharmony_ci *	    Frank Munzert <munzert@de.ibm.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "vmur"
138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/cdev.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
208c2ecf20Sopenharmony_ci#include <asm/cio.h>
218c2ecf20Sopenharmony_ci#include <asm/ccwdev.h>
228c2ecf20Sopenharmony_ci#include <asm/debug.h>
238c2ecf20Sopenharmony_ci#include <asm/diag.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "vmur.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * Driver overview
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Unit record device support is implemented as a character device driver.
318c2ecf20Sopenharmony_ci * We can fit at least 16 bits into a device minor number and use the
328c2ecf20Sopenharmony_ci * simple method of mapping a character device number with minor abcd
338c2ecf20Sopenharmony_ci * to the unit record device with devno abcd.
348c2ecf20Sopenharmony_ci * I/O to virtual unit record devices is handled as follows:
358c2ecf20Sopenharmony_ci * Reads: Diagnose code 0x14 (input spool file manipulation)
368c2ecf20Sopenharmony_ci * is used to read spool data page-wise.
378c2ecf20Sopenharmony_ci * Writes: The CCW used is WRITE_CCW_CMD (0x01). The device's record length
388c2ecf20Sopenharmony_ci * is available by reading sysfs attr reclen. Each write() to the device
398c2ecf20Sopenharmony_ci * must specify an integral multiple (maximal 511) of reclen.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic char ur_banner[] = "z/VM virtual unit record device driver";
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ciMODULE_AUTHOR("IBM Corporation");
458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("s390 z/VM virtual unit record device driver");
468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic dev_t ur_first_dev_maj_min;
498c2ecf20Sopenharmony_cistatic struct class *vmur_class;
508c2ecf20Sopenharmony_cistatic struct debug_info *vmur_dbf;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* We put the device's record length (for writes) in the driver_info field */
538c2ecf20Sopenharmony_cistatic struct ccw_device_id ur_ids[] = {
548c2ecf20Sopenharmony_ci	{ CCWDEV_CU_DI(READER_PUNCH_DEVTYPE, 80) },
558c2ecf20Sopenharmony_ci	{ CCWDEV_CU_DI(PRINTER_DEVTYPE, 132) },
568c2ecf20Sopenharmony_ci	{ /* end of list */ }
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, ur_ids);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int ur_probe(struct ccw_device *cdev);
628c2ecf20Sopenharmony_cistatic void ur_remove(struct ccw_device *cdev);
638c2ecf20Sopenharmony_cistatic int ur_set_online(struct ccw_device *cdev);
648c2ecf20Sopenharmony_cistatic int ur_set_offline(struct ccw_device *cdev);
658c2ecf20Sopenharmony_cistatic int ur_pm_suspend(struct ccw_device *cdev);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic struct ccw_driver ur_driver = {
688c2ecf20Sopenharmony_ci	.driver = {
698c2ecf20Sopenharmony_ci		.name	= "vmur",
708c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
718c2ecf20Sopenharmony_ci	},
728c2ecf20Sopenharmony_ci	.ids		= ur_ids,
738c2ecf20Sopenharmony_ci	.probe		= ur_probe,
748c2ecf20Sopenharmony_ci	.remove		= ur_remove,
758c2ecf20Sopenharmony_ci	.set_online	= ur_set_online,
768c2ecf20Sopenharmony_ci	.set_offline	= ur_set_offline,
778c2ecf20Sopenharmony_ci	.freeze		= ur_pm_suspend,
788c2ecf20Sopenharmony_ci	.int_class	= IRQIO_VMR,
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(vmur_mutex);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/*
848c2ecf20Sopenharmony_ci * Allocation, freeing, getting and putting of urdev structures
858c2ecf20Sopenharmony_ci *
868c2ecf20Sopenharmony_ci * Each ur device (urd) contains a reference to its corresponding ccw device
878c2ecf20Sopenharmony_ci * (cdev) using the urd->cdev pointer. Each ccw device has a reference to the
888c2ecf20Sopenharmony_ci * ur device using dev_get_drvdata(&cdev->dev) pointer.
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci * urd references:
918c2ecf20Sopenharmony_ci * - ur_probe gets a urd reference, ur_remove drops the reference
928c2ecf20Sopenharmony_ci *   dev_get_drvdata(&cdev->dev)
938c2ecf20Sopenharmony_ci * - ur_open gets a urd reference, ur_release drops the reference
948c2ecf20Sopenharmony_ci *   (urf->urd)
958c2ecf20Sopenharmony_ci *
968c2ecf20Sopenharmony_ci * cdev references:
978c2ecf20Sopenharmony_ci * - urdev_alloc get a cdev reference (urd->cdev)
988c2ecf20Sopenharmony_ci * - urdev_free drops the cdev reference (urd->cdev)
998c2ecf20Sopenharmony_ci *
1008c2ecf20Sopenharmony_ci * Setting and clearing of dev_get_drvdata(&cdev->dev) is protected by the ccwdev lock
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic struct urdev *urdev_alloc(struct ccw_device *cdev)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct urdev *urd;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	urd = kzalloc(sizeof(struct urdev), GFP_KERNEL);
1078c2ecf20Sopenharmony_ci	if (!urd)
1088c2ecf20Sopenharmony_ci		return NULL;
1098c2ecf20Sopenharmony_ci	urd->reclen = cdev->id.driver_info;
1108c2ecf20Sopenharmony_ci	ccw_device_get_id(cdev, &urd->dev_id);
1118c2ecf20Sopenharmony_ci	mutex_init(&urd->io_mutex);
1128c2ecf20Sopenharmony_ci	init_waitqueue_head(&urd->wait);
1138c2ecf20Sopenharmony_ci	spin_lock_init(&urd->open_lock);
1148c2ecf20Sopenharmony_ci	refcount_set(&urd->ref_count,  1);
1158c2ecf20Sopenharmony_ci	urd->cdev = cdev;
1168c2ecf20Sopenharmony_ci	get_device(&cdev->dev);
1178c2ecf20Sopenharmony_ci	return urd;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic void urdev_free(struct urdev *urd)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	TRACE("urdev_free: %p\n", urd);
1238c2ecf20Sopenharmony_ci	if (urd->cdev)
1248c2ecf20Sopenharmony_ci		put_device(&urd->cdev->dev);
1258c2ecf20Sopenharmony_ci	kfree(urd);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic void urdev_get(struct urdev *urd)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	refcount_inc(&urd->ref_count);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic struct urdev *urdev_get_from_cdev(struct ccw_device *cdev)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct urdev *urd;
1368c2ecf20Sopenharmony_ci	unsigned long flags;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
1398c2ecf20Sopenharmony_ci	urd = dev_get_drvdata(&cdev->dev);
1408c2ecf20Sopenharmony_ci	if (urd)
1418c2ecf20Sopenharmony_ci		urdev_get(urd);
1428c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
1438c2ecf20Sopenharmony_ci	return urd;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic struct urdev *urdev_get_from_devno(u16 devno)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	char bus_id[16];
1498c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
1508c2ecf20Sopenharmony_ci	struct urdev *urd;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	sprintf(bus_id, "0.0.%04x", devno);
1538c2ecf20Sopenharmony_ci	cdev = get_ccwdev_by_busid(&ur_driver, bus_id);
1548c2ecf20Sopenharmony_ci	if (!cdev)
1558c2ecf20Sopenharmony_ci		return NULL;
1568c2ecf20Sopenharmony_ci	urd = urdev_get_from_cdev(cdev);
1578c2ecf20Sopenharmony_ci	put_device(&cdev->dev);
1588c2ecf20Sopenharmony_ci	return urd;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic void urdev_put(struct urdev *urd)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&urd->ref_count))
1648c2ecf20Sopenharmony_ci		urdev_free(urd);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/*
1688c2ecf20Sopenharmony_ci * State and contents of ur devices can be changed by class D users issuing
1698c2ecf20Sopenharmony_ci * CP commands such as PURGE or TRANSFER, while the Linux guest is suspended.
1708c2ecf20Sopenharmony_ci * Also the Linux guest might be logged off, which causes all active spool
1718c2ecf20Sopenharmony_ci * files to be closed.
1728c2ecf20Sopenharmony_ci * So we cannot guarantee that spool files are still the same when the Linux
1738c2ecf20Sopenharmony_ci * guest is resumed. In order to avoid unpredictable results at resume time
1748c2ecf20Sopenharmony_ci * we simply refuse to suspend if a ur device node is open.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_cistatic int ur_pm_suspend(struct ccw_device *cdev)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct urdev *urd = dev_get_drvdata(&cdev->dev);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	TRACE("ur_pm_suspend: cdev=%p\n", cdev);
1818c2ecf20Sopenharmony_ci	if (urd->open_flag) {
1828c2ecf20Sopenharmony_ci		pr_err("Unit record device %s is busy, %s refusing to "
1838c2ecf20Sopenharmony_ci		       "suspend.\n", dev_name(&cdev->dev), ur_banner);
1848c2ecf20Sopenharmony_ci		return -EBUSY;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	return 0;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/*
1908c2ecf20Sopenharmony_ci * Low-level functions to do I/O to a ur device.
1918c2ecf20Sopenharmony_ci *     alloc_chan_prog
1928c2ecf20Sopenharmony_ci *     free_chan_prog
1938c2ecf20Sopenharmony_ci *     do_ur_io
1948c2ecf20Sopenharmony_ci *     ur_int_handler
1958c2ecf20Sopenharmony_ci *
1968c2ecf20Sopenharmony_ci * alloc_chan_prog allocates and builds the channel program
1978c2ecf20Sopenharmony_ci * free_chan_prog frees memory of the channel program
1988c2ecf20Sopenharmony_ci *
1998c2ecf20Sopenharmony_ci * do_ur_io issues the channel program to the device and blocks waiting
2008c2ecf20Sopenharmony_ci * on a completion event it publishes at urd->io_done. The function
2018c2ecf20Sopenharmony_ci * serialises itself on the device's mutex so that only one I/O
2028c2ecf20Sopenharmony_ci * is issued at a time (and that I/O is synchronous).
2038c2ecf20Sopenharmony_ci *
2048c2ecf20Sopenharmony_ci * ur_int_handler catches the "I/O done" interrupt, writes the
2058c2ecf20Sopenharmony_ci * subchannel status word into the scsw member of the urdev structure
2068c2ecf20Sopenharmony_ci * and complete()s the io_done to wake the waiting do_ur_io.
2078c2ecf20Sopenharmony_ci *
2088c2ecf20Sopenharmony_ci * The caller of do_ur_io is responsible for kfree()ing the channel program
2098c2ecf20Sopenharmony_ci * address pointer that alloc_chan_prog returned.
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic void free_chan_prog(struct ccw1 *cpa)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct ccw1 *ptr = cpa;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	while (ptr->cda) {
2178c2ecf20Sopenharmony_ci		kfree((void *)(addr_t) ptr->cda);
2188c2ecf20Sopenharmony_ci		ptr++;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci	kfree(cpa);
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/*
2248c2ecf20Sopenharmony_ci * alloc_chan_prog
2258c2ecf20Sopenharmony_ci * The channel program we use is write commands chained together
2268c2ecf20Sopenharmony_ci * with a final NOP CCW command-chained on (which ensures that CE and DE
2278c2ecf20Sopenharmony_ci * are presented together in a single interrupt instead of as separate
2288c2ecf20Sopenharmony_ci * interrupts unless an incorrect length indication kicks in first). The
2298c2ecf20Sopenharmony_ci * data length in each CCW is reclen.
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_cistatic struct ccw1 *alloc_chan_prog(const char __user *ubuf, int rec_count,
2328c2ecf20Sopenharmony_ci				    int reclen)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct ccw1 *cpa;
2358c2ecf20Sopenharmony_ci	void *kbuf;
2368c2ecf20Sopenharmony_ci	int i;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	TRACE("alloc_chan_prog(%p, %i, %i)\n", ubuf, rec_count, reclen);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/*
2418c2ecf20Sopenharmony_ci	 * We chain a NOP onto the writes to force CE+DE together.
2428c2ecf20Sopenharmony_ci	 * That means we allocate room for CCWs to cover count/reclen
2438c2ecf20Sopenharmony_ci	 * records plus a NOP.
2448c2ecf20Sopenharmony_ci	 */
2458c2ecf20Sopenharmony_ci	cpa = kcalloc(rec_count + 1, sizeof(struct ccw1),
2468c2ecf20Sopenharmony_ci		      GFP_KERNEL | GFP_DMA);
2478c2ecf20Sopenharmony_ci	if (!cpa)
2488c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	for (i = 0; i < rec_count; i++) {
2518c2ecf20Sopenharmony_ci		cpa[i].cmd_code = WRITE_CCW_CMD;
2528c2ecf20Sopenharmony_ci		cpa[i].flags = CCW_FLAG_CC | CCW_FLAG_SLI;
2538c2ecf20Sopenharmony_ci		cpa[i].count = reclen;
2548c2ecf20Sopenharmony_ci		kbuf = kmalloc(reclen, GFP_KERNEL | GFP_DMA);
2558c2ecf20Sopenharmony_ci		if (!kbuf) {
2568c2ecf20Sopenharmony_ci			free_chan_prog(cpa);
2578c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
2588c2ecf20Sopenharmony_ci		}
2598c2ecf20Sopenharmony_ci		cpa[i].cda = (u32)(addr_t) kbuf;
2608c2ecf20Sopenharmony_ci		if (copy_from_user(kbuf, ubuf, reclen)) {
2618c2ecf20Sopenharmony_ci			free_chan_prog(cpa);
2628c2ecf20Sopenharmony_ci			return ERR_PTR(-EFAULT);
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci		ubuf += reclen;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	/* The following NOP CCW forces CE+DE to be presented together */
2678c2ecf20Sopenharmony_ci	cpa[i].cmd_code = CCW_CMD_NOOP;
2688c2ecf20Sopenharmony_ci	return cpa;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic int do_ur_io(struct urdev *urd, struct ccw1 *cpa)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	int rc;
2748c2ecf20Sopenharmony_ci	struct ccw_device *cdev = urd->cdev;
2758c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(event);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	TRACE("do_ur_io: cpa=%p\n", cpa);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	rc = mutex_lock_interruptible(&urd->io_mutex);
2808c2ecf20Sopenharmony_ci	if (rc)
2818c2ecf20Sopenharmony_ci		return rc;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	urd->io_done = &event;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	spin_lock_irq(get_ccwdev_lock(cdev));
2868c2ecf20Sopenharmony_ci	rc = ccw_device_start(cdev, cpa, 1, 0, 0);
2878c2ecf20Sopenharmony_ci	spin_unlock_irq(get_ccwdev_lock(cdev));
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	TRACE("do_ur_io: ccw_device_start returned %d\n", rc);
2908c2ecf20Sopenharmony_ci	if (rc)
2918c2ecf20Sopenharmony_ci		goto out;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	wait_for_completion(&event);
2948c2ecf20Sopenharmony_ci	TRACE("do_ur_io: I/O complete\n");
2958c2ecf20Sopenharmony_ci	rc = 0;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ciout:
2988c2ecf20Sopenharmony_ci	mutex_unlock(&urd->io_mutex);
2998c2ecf20Sopenharmony_ci	return rc;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/*
3038c2ecf20Sopenharmony_ci * ur interrupt handler, called from the ccw_device layer
3048c2ecf20Sopenharmony_ci */
3058c2ecf20Sopenharmony_cistatic void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
3068c2ecf20Sopenharmony_ci			   struct irb *irb)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct urdev *urd;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (!IS_ERR(irb)) {
3118c2ecf20Sopenharmony_ci		TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n",
3128c2ecf20Sopenharmony_ci		      intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
3138c2ecf20Sopenharmony_ci		      irb->scsw.cmd.count);
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci	if (!intparm) {
3168c2ecf20Sopenharmony_ci		TRACE("ur_int_handler: unsolicited interrupt\n");
3178c2ecf20Sopenharmony_ci		return;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	urd = dev_get_drvdata(&cdev->dev);
3208c2ecf20Sopenharmony_ci	BUG_ON(!urd);
3218c2ecf20Sopenharmony_ci	/* On special conditions irb is an error pointer */
3228c2ecf20Sopenharmony_ci	if (IS_ERR(irb))
3238c2ecf20Sopenharmony_ci		urd->io_request_rc = PTR_ERR(irb);
3248c2ecf20Sopenharmony_ci	else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
3258c2ecf20Sopenharmony_ci		urd->io_request_rc = 0;
3268c2ecf20Sopenharmony_ci	else
3278c2ecf20Sopenharmony_ci		urd->io_request_rc = -EIO;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	complete(urd->io_done);
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci/*
3338c2ecf20Sopenharmony_ci * reclen sysfs attribute - The record length to be used for write CCWs
3348c2ecf20Sopenharmony_ci */
3358c2ecf20Sopenharmony_cistatic ssize_t ur_attr_reclen_show(struct device *dev,
3368c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct urdev *urd;
3398c2ecf20Sopenharmony_ci	int rc;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	urd = urdev_get_from_cdev(to_ccwdev(dev));
3428c2ecf20Sopenharmony_ci	if (!urd)
3438c2ecf20Sopenharmony_ci		return -ENODEV;
3448c2ecf20Sopenharmony_ci	rc = sprintf(buf, "%zu\n", urd->reclen);
3458c2ecf20Sopenharmony_ci	urdev_put(urd);
3468c2ecf20Sopenharmony_ci	return rc;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic DEVICE_ATTR(reclen, 0444, ur_attr_reclen_show, NULL);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic int ur_create_attributes(struct device *dev)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	return device_create_file(dev, &dev_attr_reclen);
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic void ur_remove_attributes(struct device *dev)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	device_remove_file(dev, &dev_attr_reclen);
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci/*
3628c2ecf20Sopenharmony_ci * diagnose code 0x210 - retrieve device information
3638c2ecf20Sopenharmony_ci * cc=0  normal completion, we have a real device
3648c2ecf20Sopenharmony_ci * cc=1  CP paging error
3658c2ecf20Sopenharmony_ci * cc=2  The virtual device exists, but is not associated with a real device
3668c2ecf20Sopenharmony_ci * cc=3  Invalid device address, or the virtual device does not exist
3678c2ecf20Sopenharmony_ci */
3688c2ecf20Sopenharmony_cistatic int get_urd_class(struct urdev *urd)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	static struct diag210 ur_diag210;
3718c2ecf20Sopenharmony_ci	int cc;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	ur_diag210.vrdcdvno = urd->dev_id.devno;
3748c2ecf20Sopenharmony_ci	ur_diag210.vrdclen = sizeof(struct diag210);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	cc = diag210(&ur_diag210);
3778c2ecf20Sopenharmony_ci	switch (cc) {
3788c2ecf20Sopenharmony_ci	case 0:
3798c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3808c2ecf20Sopenharmony_ci	case 2:
3818c2ecf20Sopenharmony_ci		return ur_diag210.vrdcvcla; /* virtual device class */
3828c2ecf20Sopenharmony_ci	case 3:
3838c2ecf20Sopenharmony_ci		return -ENODEV;
3848c2ecf20Sopenharmony_ci	default:
3858c2ecf20Sopenharmony_ci		return -EIO;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci/*
3908c2ecf20Sopenharmony_ci * Allocation and freeing of urfile structures
3918c2ecf20Sopenharmony_ci */
3928c2ecf20Sopenharmony_cistatic struct urfile *urfile_alloc(struct urdev *urd)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct urfile *urf;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	urf = kzalloc(sizeof(struct urfile), GFP_KERNEL);
3978c2ecf20Sopenharmony_ci	if (!urf)
3988c2ecf20Sopenharmony_ci		return NULL;
3998c2ecf20Sopenharmony_ci	urf->urd = urd;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	TRACE("urfile_alloc: urd=%p urf=%p rl=%zu\n", urd, urf,
4028c2ecf20Sopenharmony_ci	      urf->dev_reclen);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return urf;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void urfile_free(struct urfile *urf)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	TRACE("urfile_free: urf=%p urd=%p\n", urf, urf->urd);
4108c2ecf20Sopenharmony_ci	kfree(urf);
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci/*
4148c2ecf20Sopenharmony_ci * The fops implementation of the character device driver
4158c2ecf20Sopenharmony_ci */
4168c2ecf20Sopenharmony_cistatic ssize_t do_write(struct urdev *urd, const char __user *udata,
4178c2ecf20Sopenharmony_ci			size_t count, size_t reclen, loff_t *ppos)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	struct ccw1 *cpa;
4208c2ecf20Sopenharmony_ci	int rc;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	cpa = alloc_chan_prog(udata, count / reclen, reclen);
4238c2ecf20Sopenharmony_ci	if (IS_ERR(cpa))
4248c2ecf20Sopenharmony_ci		return PTR_ERR(cpa);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	rc = do_ur_io(urd, cpa);
4278c2ecf20Sopenharmony_ci	if (rc)
4288c2ecf20Sopenharmony_ci		goto fail_kfree_cpa;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (urd->io_request_rc) {
4318c2ecf20Sopenharmony_ci		rc = urd->io_request_rc;
4328c2ecf20Sopenharmony_ci		goto fail_kfree_cpa;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci	*ppos += count;
4358c2ecf20Sopenharmony_ci	rc = count;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cifail_kfree_cpa:
4388c2ecf20Sopenharmony_ci	free_chan_prog(cpa);
4398c2ecf20Sopenharmony_ci	return rc;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic ssize_t ur_write(struct file *file, const char __user *udata,
4438c2ecf20Sopenharmony_ci			size_t count, loff_t *ppos)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct urfile *urf = file->private_data;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	TRACE("ur_write: count=%zu\n", count);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	if (count == 0)
4508c2ecf20Sopenharmony_ci		return 0;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (count % urf->dev_reclen)
4538c2ecf20Sopenharmony_ci		return -EINVAL;	/* count must be a multiple of reclen */
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (count > urf->dev_reclen * MAX_RECS_PER_IO)
4568c2ecf20Sopenharmony_ci		count = urf->dev_reclen * MAX_RECS_PER_IO;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	return do_write(urf->urd, udata, count, urf->dev_reclen, ppos);
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci/*
4628c2ecf20Sopenharmony_ci * diagnose code 0x14 subcode 0x0028 - position spool file to designated
4638c2ecf20Sopenharmony_ci *				       record
4648c2ecf20Sopenharmony_ci * cc=0  normal completion
4658c2ecf20Sopenharmony_ci * cc=2  no file active on the virtual reader or device not ready
4668c2ecf20Sopenharmony_ci * cc=3  record specified is beyond EOF
4678c2ecf20Sopenharmony_ci */
4688c2ecf20Sopenharmony_cistatic int diag_position_to_record(int devno, int record)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	int cc;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	cc = diag14(record, devno, 0x28);
4738c2ecf20Sopenharmony_ci	switch (cc) {
4748c2ecf20Sopenharmony_ci	case 0:
4758c2ecf20Sopenharmony_ci		return 0;
4768c2ecf20Sopenharmony_ci	case 2:
4778c2ecf20Sopenharmony_ci		return -ENOMEDIUM;
4788c2ecf20Sopenharmony_ci	case 3:
4798c2ecf20Sopenharmony_ci		return -ENODATA; /* position beyond end of file */
4808c2ecf20Sopenharmony_ci	default:
4818c2ecf20Sopenharmony_ci		return -EIO;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci/*
4868c2ecf20Sopenharmony_ci * diagnose code 0x14 subcode 0x0000 - read next spool file buffer
4878c2ecf20Sopenharmony_ci * cc=0  normal completion
4888c2ecf20Sopenharmony_ci * cc=1  EOF reached
4898c2ecf20Sopenharmony_ci * cc=2  no file active on the virtual reader, and no file eligible
4908c2ecf20Sopenharmony_ci * cc=3  file already active on the virtual reader or specified virtual
4918c2ecf20Sopenharmony_ci *	 reader does not exist or is not a reader
4928c2ecf20Sopenharmony_ci */
4938c2ecf20Sopenharmony_cistatic int diag_read_file(int devno, char *buf)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	int cc;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	cc = diag14((unsigned long) buf, devno, 0x00);
4988c2ecf20Sopenharmony_ci	switch (cc) {
4998c2ecf20Sopenharmony_ci	case 0:
5008c2ecf20Sopenharmony_ci		return 0;
5018c2ecf20Sopenharmony_ci	case 1:
5028c2ecf20Sopenharmony_ci		return -ENODATA;
5038c2ecf20Sopenharmony_ci	case 2:
5048c2ecf20Sopenharmony_ci		return -ENOMEDIUM;
5058c2ecf20Sopenharmony_ci	default:
5068c2ecf20Sopenharmony_ci		return -EIO;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic ssize_t diag14_read(struct file *file, char __user *ubuf, size_t count,
5118c2ecf20Sopenharmony_ci			   loff_t *offs)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	size_t len, copied, res;
5148c2ecf20Sopenharmony_ci	char *buf;
5158c2ecf20Sopenharmony_ci	int rc;
5168c2ecf20Sopenharmony_ci	u16 reclen;
5178c2ecf20Sopenharmony_ci	struct urdev *urd;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	urd = ((struct urfile *) file->private_data)->urd;
5208c2ecf20Sopenharmony_ci	reclen = ((struct urfile *) file->private_data)->file_reclen;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	rc = diag_position_to_record(urd->dev_id.devno, *offs / PAGE_SIZE + 1);
5238c2ecf20Sopenharmony_ci	if (rc == -ENODATA)
5248c2ecf20Sopenharmony_ci		return 0;
5258c2ecf20Sopenharmony_ci	if (rc)
5268c2ecf20Sopenharmony_ci		return rc;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	len = min((size_t) PAGE_SIZE, count);
5298c2ecf20Sopenharmony_ci	buf = (char *) __get_free_page(GFP_KERNEL | GFP_DMA);
5308c2ecf20Sopenharmony_ci	if (!buf)
5318c2ecf20Sopenharmony_ci		return -ENOMEM;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	copied = 0;
5348c2ecf20Sopenharmony_ci	res = (size_t) (*offs % PAGE_SIZE);
5358c2ecf20Sopenharmony_ci	do {
5368c2ecf20Sopenharmony_ci		rc = diag_read_file(urd->dev_id.devno, buf);
5378c2ecf20Sopenharmony_ci		if (rc == -ENODATA) {
5388c2ecf20Sopenharmony_ci			break;
5398c2ecf20Sopenharmony_ci		}
5408c2ecf20Sopenharmony_ci		if (rc)
5418c2ecf20Sopenharmony_ci			goto fail;
5428c2ecf20Sopenharmony_ci		if (reclen && (copied == 0) && (*offs < PAGE_SIZE))
5438c2ecf20Sopenharmony_ci			*((u16 *) &buf[FILE_RECLEN_OFFSET]) = reclen;
5448c2ecf20Sopenharmony_ci		len = min(count - copied, PAGE_SIZE - res);
5458c2ecf20Sopenharmony_ci		if (copy_to_user(ubuf + copied, buf + res, len)) {
5468c2ecf20Sopenharmony_ci			rc = -EFAULT;
5478c2ecf20Sopenharmony_ci			goto fail;
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci		res = 0;
5508c2ecf20Sopenharmony_ci		copied += len;
5518c2ecf20Sopenharmony_ci	} while (copied != count);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	*offs += copied;
5548c2ecf20Sopenharmony_ci	rc = copied;
5558c2ecf20Sopenharmony_cifail:
5568c2ecf20Sopenharmony_ci	free_page((unsigned long) buf);
5578c2ecf20Sopenharmony_ci	return rc;
5588c2ecf20Sopenharmony_ci}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_cistatic ssize_t ur_read(struct file *file, char __user *ubuf, size_t count,
5618c2ecf20Sopenharmony_ci		       loff_t *offs)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct urdev *urd;
5648c2ecf20Sopenharmony_ci	int rc;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	TRACE("ur_read: count=%zu ppos=%li\n", count, (unsigned long) *offs);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (count == 0)
5698c2ecf20Sopenharmony_ci		return 0;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	urd = ((struct urfile *) file->private_data)->urd;
5728c2ecf20Sopenharmony_ci	rc = mutex_lock_interruptible(&urd->io_mutex);
5738c2ecf20Sopenharmony_ci	if (rc)
5748c2ecf20Sopenharmony_ci		return rc;
5758c2ecf20Sopenharmony_ci	rc = diag14_read(file, ubuf, count, offs);
5768c2ecf20Sopenharmony_ci	mutex_unlock(&urd->io_mutex);
5778c2ecf20Sopenharmony_ci	return rc;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci/*
5818c2ecf20Sopenharmony_ci * diagnose code 0x14 subcode 0x0fff - retrieve next file descriptor
5828c2ecf20Sopenharmony_ci * cc=0  normal completion
5838c2ecf20Sopenharmony_ci * cc=1  no files on reader queue or no subsequent file
5848c2ecf20Sopenharmony_ci * cc=2  spid specified is invalid
5858c2ecf20Sopenharmony_ci */
5868c2ecf20Sopenharmony_cistatic int diag_read_next_file_info(struct file_control_block *buf, int spid)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	int cc;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	cc = diag14((unsigned long) buf, spid, 0xfff);
5918c2ecf20Sopenharmony_ci	switch (cc) {
5928c2ecf20Sopenharmony_ci	case 0:
5938c2ecf20Sopenharmony_ci		return 0;
5948c2ecf20Sopenharmony_ci	default:
5958c2ecf20Sopenharmony_ci		return -ENODATA;
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic int verify_uri_device(struct urdev *urd)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	struct file_control_block *fcb;
6028c2ecf20Sopenharmony_ci	char *buf;
6038c2ecf20Sopenharmony_ci	int rc;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	fcb = kmalloc(sizeof(*fcb), GFP_KERNEL | GFP_DMA);
6068c2ecf20Sopenharmony_ci	if (!fcb)
6078c2ecf20Sopenharmony_ci		return -ENOMEM;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	/* check for empty reader device (beginning of chain) */
6108c2ecf20Sopenharmony_ci	rc = diag_read_next_file_info(fcb, 0);
6118c2ecf20Sopenharmony_ci	if (rc)
6128c2ecf20Sopenharmony_ci		goto fail_free_fcb;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	/* if file is in hold status, we do not read it */
6158c2ecf20Sopenharmony_ci	if (fcb->file_stat & (FLG_SYSTEM_HOLD | FLG_USER_HOLD)) {
6168c2ecf20Sopenharmony_ci		rc = -EPERM;
6178c2ecf20Sopenharmony_ci		goto fail_free_fcb;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/* open file on virtual reader	*/
6218c2ecf20Sopenharmony_ci	buf = (char *) __get_free_page(GFP_KERNEL | GFP_DMA);
6228c2ecf20Sopenharmony_ci	if (!buf) {
6238c2ecf20Sopenharmony_ci		rc = -ENOMEM;
6248c2ecf20Sopenharmony_ci		goto fail_free_fcb;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci	rc = diag_read_file(urd->dev_id.devno, buf);
6278c2ecf20Sopenharmony_ci	if ((rc != 0) && (rc != -ENODATA)) /* EOF does not hurt */
6288c2ecf20Sopenharmony_ci		goto fail_free_buf;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	/* check if the file on top of the queue is open now */
6318c2ecf20Sopenharmony_ci	rc = diag_read_next_file_info(fcb, 0);
6328c2ecf20Sopenharmony_ci	if (rc)
6338c2ecf20Sopenharmony_ci		goto fail_free_buf;
6348c2ecf20Sopenharmony_ci	if (!(fcb->file_stat & FLG_IN_USE)) {
6358c2ecf20Sopenharmony_ci		rc = -EMFILE;
6368c2ecf20Sopenharmony_ci		goto fail_free_buf;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci	rc = 0;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cifail_free_buf:
6418c2ecf20Sopenharmony_ci	free_page((unsigned long) buf);
6428c2ecf20Sopenharmony_cifail_free_fcb:
6438c2ecf20Sopenharmony_ci	kfree(fcb);
6448c2ecf20Sopenharmony_ci	return rc;
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cistatic int verify_device(struct urdev *urd)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	switch (urd->class) {
6508c2ecf20Sopenharmony_ci	case DEV_CLASS_UR_O:
6518c2ecf20Sopenharmony_ci		return 0; /* no check needed here */
6528c2ecf20Sopenharmony_ci	case DEV_CLASS_UR_I:
6538c2ecf20Sopenharmony_ci		return verify_uri_device(urd);
6548c2ecf20Sopenharmony_ci	default:
6558c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic int get_uri_file_reclen(struct urdev *urd)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct file_control_block *fcb;
6628c2ecf20Sopenharmony_ci	int rc;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	fcb = kmalloc(sizeof(*fcb), GFP_KERNEL | GFP_DMA);
6658c2ecf20Sopenharmony_ci	if (!fcb)
6668c2ecf20Sopenharmony_ci		return -ENOMEM;
6678c2ecf20Sopenharmony_ci	rc = diag_read_next_file_info(fcb, 0);
6688c2ecf20Sopenharmony_ci	if (rc)
6698c2ecf20Sopenharmony_ci		goto fail_free;
6708c2ecf20Sopenharmony_ci	if (fcb->file_stat & FLG_CP_DUMP)
6718c2ecf20Sopenharmony_ci		rc = 0;
6728c2ecf20Sopenharmony_ci	else
6738c2ecf20Sopenharmony_ci		rc = fcb->rec_len;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_cifail_free:
6768c2ecf20Sopenharmony_ci	kfree(fcb);
6778c2ecf20Sopenharmony_ci	return rc;
6788c2ecf20Sopenharmony_ci}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_cistatic int get_file_reclen(struct urdev *urd)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	switch (urd->class) {
6838c2ecf20Sopenharmony_ci	case DEV_CLASS_UR_O:
6848c2ecf20Sopenharmony_ci		return 0;
6858c2ecf20Sopenharmony_ci	case DEV_CLASS_UR_I:
6868c2ecf20Sopenharmony_ci		return get_uri_file_reclen(urd);
6878c2ecf20Sopenharmony_ci	default:
6888c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic int ur_open(struct inode *inode, struct file *file)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	u16 devno;
6958c2ecf20Sopenharmony_ci	struct urdev *urd;
6968c2ecf20Sopenharmony_ci	struct urfile *urf;
6978c2ecf20Sopenharmony_ci	unsigned short accmode;
6988c2ecf20Sopenharmony_ci	int rc;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	accmode = file->f_flags & O_ACCMODE;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (accmode == O_RDWR)
7038c2ecf20Sopenharmony_ci		return -EACCES;
7048c2ecf20Sopenharmony_ci	/*
7058c2ecf20Sopenharmony_ci	 * We treat the minor number as the devno of the ur device
7068c2ecf20Sopenharmony_ci	 * to find in the driver tree.
7078c2ecf20Sopenharmony_ci	 */
7088c2ecf20Sopenharmony_ci	devno = MINOR(file_inode(file)->i_rdev);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	urd = urdev_get_from_devno(devno);
7118c2ecf20Sopenharmony_ci	if (!urd) {
7128c2ecf20Sopenharmony_ci		rc = -ENXIO;
7138c2ecf20Sopenharmony_ci		goto out;
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	spin_lock(&urd->open_lock);
7178c2ecf20Sopenharmony_ci	while (urd->open_flag) {
7188c2ecf20Sopenharmony_ci		spin_unlock(&urd->open_lock);
7198c2ecf20Sopenharmony_ci		if (file->f_flags & O_NONBLOCK) {
7208c2ecf20Sopenharmony_ci			rc = -EBUSY;
7218c2ecf20Sopenharmony_ci			goto fail_put;
7228c2ecf20Sopenharmony_ci		}
7238c2ecf20Sopenharmony_ci		if (wait_event_interruptible(urd->wait, urd->open_flag == 0)) {
7248c2ecf20Sopenharmony_ci			rc = -ERESTARTSYS;
7258c2ecf20Sopenharmony_ci			goto fail_put;
7268c2ecf20Sopenharmony_ci		}
7278c2ecf20Sopenharmony_ci		spin_lock(&urd->open_lock);
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci	urd->open_flag++;
7308c2ecf20Sopenharmony_ci	spin_unlock(&urd->open_lock);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	TRACE("ur_open\n");
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	if (((accmode == O_RDONLY) && (urd->class != DEV_CLASS_UR_I)) ||
7358c2ecf20Sopenharmony_ci	    ((accmode == O_WRONLY) && (urd->class != DEV_CLASS_UR_O))) {
7368c2ecf20Sopenharmony_ci		TRACE("ur_open: unsupported dev class (%d)\n", urd->class);
7378c2ecf20Sopenharmony_ci		rc = -EACCES;
7388c2ecf20Sopenharmony_ci		goto fail_unlock;
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	rc = verify_device(urd);
7428c2ecf20Sopenharmony_ci	if (rc)
7438c2ecf20Sopenharmony_ci		goto fail_unlock;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	urf = urfile_alloc(urd);
7468c2ecf20Sopenharmony_ci	if (!urf) {
7478c2ecf20Sopenharmony_ci		rc = -ENOMEM;
7488c2ecf20Sopenharmony_ci		goto fail_unlock;
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	urf->dev_reclen = urd->reclen;
7528c2ecf20Sopenharmony_ci	rc = get_file_reclen(urd);
7538c2ecf20Sopenharmony_ci	if (rc < 0)
7548c2ecf20Sopenharmony_ci		goto fail_urfile_free;
7558c2ecf20Sopenharmony_ci	urf->file_reclen = rc;
7568c2ecf20Sopenharmony_ci	file->private_data = urf;
7578c2ecf20Sopenharmony_ci	return 0;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_cifail_urfile_free:
7608c2ecf20Sopenharmony_ci	urfile_free(urf);
7618c2ecf20Sopenharmony_cifail_unlock:
7628c2ecf20Sopenharmony_ci	spin_lock(&urd->open_lock);
7638c2ecf20Sopenharmony_ci	urd->open_flag--;
7648c2ecf20Sopenharmony_ci	spin_unlock(&urd->open_lock);
7658c2ecf20Sopenharmony_cifail_put:
7668c2ecf20Sopenharmony_ci	urdev_put(urd);
7678c2ecf20Sopenharmony_ciout:
7688c2ecf20Sopenharmony_ci	return rc;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic int ur_release(struct inode *inode, struct file *file)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	struct urfile *urf = file->private_data;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	TRACE("ur_release\n");
7768c2ecf20Sopenharmony_ci	spin_lock(&urf->urd->open_lock);
7778c2ecf20Sopenharmony_ci	urf->urd->open_flag--;
7788c2ecf20Sopenharmony_ci	spin_unlock(&urf->urd->open_lock);
7798c2ecf20Sopenharmony_ci	wake_up_interruptible(&urf->urd->wait);
7808c2ecf20Sopenharmony_ci	urdev_put(urf->urd);
7818c2ecf20Sopenharmony_ci	urfile_free(urf);
7828c2ecf20Sopenharmony_ci	return 0;
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic loff_t ur_llseek(struct file *file, loff_t offset, int whence)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	if ((file->f_flags & O_ACCMODE) != O_RDONLY)
7888c2ecf20Sopenharmony_ci		return -ESPIPE; /* seek allowed only for reader */
7898c2ecf20Sopenharmony_ci	if (offset % PAGE_SIZE)
7908c2ecf20Sopenharmony_ci		return -ESPIPE; /* only multiples of 4K allowed */
7918c2ecf20Sopenharmony_ci	return no_seek_end_llseek(file, offset, whence);
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic const struct file_operations ur_fops = {
7958c2ecf20Sopenharmony_ci	.owner	 = THIS_MODULE,
7968c2ecf20Sopenharmony_ci	.open	 = ur_open,
7978c2ecf20Sopenharmony_ci	.release = ur_release,
7988c2ecf20Sopenharmony_ci	.read	 = ur_read,
7998c2ecf20Sopenharmony_ci	.write	 = ur_write,
8008c2ecf20Sopenharmony_ci	.llseek  = ur_llseek,
8018c2ecf20Sopenharmony_ci};
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci/*
8048c2ecf20Sopenharmony_ci * ccw_device infrastructure:
8058c2ecf20Sopenharmony_ci *     ur_probe creates the struct urdev (with refcount = 1), the device
8068c2ecf20Sopenharmony_ci *     attributes, sets up the interrupt handler and validates the virtual
8078c2ecf20Sopenharmony_ci *     unit record device.
8088c2ecf20Sopenharmony_ci *     ur_remove removes the device attributes and drops the reference to
8098c2ecf20Sopenharmony_ci *     struct urdev.
8108c2ecf20Sopenharmony_ci *
8118c2ecf20Sopenharmony_ci *     ur_probe, ur_remove, ur_set_online and ur_set_offline are serialized
8128c2ecf20Sopenharmony_ci *     by the vmur_mutex lock.
8138c2ecf20Sopenharmony_ci *
8148c2ecf20Sopenharmony_ci *     urd->char_device is used as indication that the online function has
8158c2ecf20Sopenharmony_ci *     been completed successfully.
8168c2ecf20Sopenharmony_ci */
8178c2ecf20Sopenharmony_cistatic int ur_probe(struct ccw_device *cdev)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	struct urdev *urd;
8208c2ecf20Sopenharmony_ci	int rc;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	TRACE("ur_probe: cdev=%p\n", cdev);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	mutex_lock(&vmur_mutex);
8258c2ecf20Sopenharmony_ci	urd = urdev_alloc(cdev);
8268c2ecf20Sopenharmony_ci	if (!urd) {
8278c2ecf20Sopenharmony_ci		rc = -ENOMEM;
8288c2ecf20Sopenharmony_ci		goto fail_unlock;
8298c2ecf20Sopenharmony_ci	}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	rc = ur_create_attributes(&cdev->dev);
8328c2ecf20Sopenharmony_ci	if (rc) {
8338c2ecf20Sopenharmony_ci		rc = -ENOMEM;
8348c2ecf20Sopenharmony_ci		goto fail_urdev_put;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci	cdev->handler = ur_int_handler;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	/* validate virtual unit record device */
8398c2ecf20Sopenharmony_ci	urd->class = get_urd_class(urd);
8408c2ecf20Sopenharmony_ci	if (urd->class < 0) {
8418c2ecf20Sopenharmony_ci		rc = urd->class;
8428c2ecf20Sopenharmony_ci		goto fail_remove_attr;
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci	if ((urd->class != DEV_CLASS_UR_I) && (urd->class != DEV_CLASS_UR_O)) {
8458c2ecf20Sopenharmony_ci		rc = -EOPNOTSUPP;
8468c2ecf20Sopenharmony_ci		goto fail_remove_attr;
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci	spin_lock_irq(get_ccwdev_lock(cdev));
8498c2ecf20Sopenharmony_ci	dev_set_drvdata(&cdev->dev, urd);
8508c2ecf20Sopenharmony_ci	spin_unlock_irq(get_ccwdev_lock(cdev));
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	mutex_unlock(&vmur_mutex);
8538c2ecf20Sopenharmony_ci	return 0;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_cifail_remove_attr:
8568c2ecf20Sopenharmony_ci	ur_remove_attributes(&cdev->dev);
8578c2ecf20Sopenharmony_cifail_urdev_put:
8588c2ecf20Sopenharmony_ci	urdev_put(urd);
8598c2ecf20Sopenharmony_cifail_unlock:
8608c2ecf20Sopenharmony_ci	mutex_unlock(&vmur_mutex);
8618c2ecf20Sopenharmony_ci	return rc;
8628c2ecf20Sopenharmony_ci}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_cistatic int ur_set_online(struct ccw_device *cdev)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	struct urdev *urd;
8678c2ecf20Sopenharmony_ci	int minor, major, rc;
8688c2ecf20Sopenharmony_ci	char node_id[16];
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	TRACE("ur_set_online: cdev=%p\n", cdev);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	mutex_lock(&vmur_mutex);
8738c2ecf20Sopenharmony_ci	urd = urdev_get_from_cdev(cdev);
8748c2ecf20Sopenharmony_ci	if (!urd) {
8758c2ecf20Sopenharmony_ci		/* ur_remove already deleted our urd */
8768c2ecf20Sopenharmony_ci		rc = -ENODEV;
8778c2ecf20Sopenharmony_ci		goto fail_unlock;
8788c2ecf20Sopenharmony_ci	}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	if (urd->char_device) {
8818c2ecf20Sopenharmony_ci		/* Another ur_set_online was faster */
8828c2ecf20Sopenharmony_ci		rc = -EBUSY;
8838c2ecf20Sopenharmony_ci		goto fail_urdev_put;
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	minor = urd->dev_id.devno;
8878c2ecf20Sopenharmony_ci	major = MAJOR(ur_first_dev_maj_min);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	urd->char_device = cdev_alloc();
8908c2ecf20Sopenharmony_ci	if (!urd->char_device) {
8918c2ecf20Sopenharmony_ci		rc = -ENOMEM;
8928c2ecf20Sopenharmony_ci		goto fail_urdev_put;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	urd->char_device->ops = &ur_fops;
8968c2ecf20Sopenharmony_ci	urd->char_device->owner = ur_fops.owner;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	rc = cdev_add(urd->char_device, MKDEV(major, minor), 1);
8998c2ecf20Sopenharmony_ci	if (rc)
9008c2ecf20Sopenharmony_ci		goto fail_free_cdev;
9018c2ecf20Sopenharmony_ci	if (urd->cdev->id.cu_type == READER_PUNCH_DEVTYPE) {
9028c2ecf20Sopenharmony_ci		if (urd->class == DEV_CLASS_UR_I)
9038c2ecf20Sopenharmony_ci			sprintf(node_id, "vmrdr-%s", dev_name(&cdev->dev));
9048c2ecf20Sopenharmony_ci		if (urd->class == DEV_CLASS_UR_O)
9058c2ecf20Sopenharmony_ci			sprintf(node_id, "vmpun-%s", dev_name(&cdev->dev));
9068c2ecf20Sopenharmony_ci	} else if (urd->cdev->id.cu_type == PRINTER_DEVTYPE) {
9078c2ecf20Sopenharmony_ci		sprintf(node_id, "vmprt-%s", dev_name(&cdev->dev));
9088c2ecf20Sopenharmony_ci	} else {
9098c2ecf20Sopenharmony_ci		rc = -EOPNOTSUPP;
9108c2ecf20Sopenharmony_ci		goto fail_free_cdev;
9118c2ecf20Sopenharmony_ci	}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	urd->device = device_create(vmur_class, &cdev->dev,
9148c2ecf20Sopenharmony_ci				    urd->char_device->dev, NULL, "%s", node_id);
9158c2ecf20Sopenharmony_ci	if (IS_ERR(urd->device)) {
9168c2ecf20Sopenharmony_ci		rc = PTR_ERR(urd->device);
9178c2ecf20Sopenharmony_ci		TRACE("ur_set_online: device_create rc=%d\n", rc);
9188c2ecf20Sopenharmony_ci		goto fail_free_cdev;
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci	urdev_put(urd);
9218c2ecf20Sopenharmony_ci	mutex_unlock(&vmur_mutex);
9228c2ecf20Sopenharmony_ci	return 0;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_cifail_free_cdev:
9258c2ecf20Sopenharmony_ci	cdev_del(urd->char_device);
9268c2ecf20Sopenharmony_ci	urd->char_device = NULL;
9278c2ecf20Sopenharmony_cifail_urdev_put:
9288c2ecf20Sopenharmony_ci	urdev_put(urd);
9298c2ecf20Sopenharmony_cifail_unlock:
9308c2ecf20Sopenharmony_ci	mutex_unlock(&vmur_mutex);
9318c2ecf20Sopenharmony_ci	return rc;
9328c2ecf20Sopenharmony_ci}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_cistatic int ur_set_offline_force(struct ccw_device *cdev, int force)
9358c2ecf20Sopenharmony_ci{
9368c2ecf20Sopenharmony_ci	struct urdev *urd;
9378c2ecf20Sopenharmony_ci	int rc;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	TRACE("ur_set_offline: cdev=%p\n", cdev);
9408c2ecf20Sopenharmony_ci	urd = urdev_get_from_cdev(cdev);
9418c2ecf20Sopenharmony_ci	if (!urd)
9428c2ecf20Sopenharmony_ci		/* ur_remove already deleted our urd */
9438c2ecf20Sopenharmony_ci		return -ENODEV;
9448c2ecf20Sopenharmony_ci	if (!urd->char_device) {
9458c2ecf20Sopenharmony_ci		/* Another ur_set_offline was faster */
9468c2ecf20Sopenharmony_ci		rc = -EBUSY;
9478c2ecf20Sopenharmony_ci		goto fail_urdev_put;
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci	if (!force && (refcount_read(&urd->ref_count) > 2)) {
9508c2ecf20Sopenharmony_ci		/* There is still a user of urd (e.g. ur_open) */
9518c2ecf20Sopenharmony_ci		TRACE("ur_set_offline: BUSY\n");
9528c2ecf20Sopenharmony_ci		rc = -EBUSY;
9538c2ecf20Sopenharmony_ci		goto fail_urdev_put;
9548c2ecf20Sopenharmony_ci	}
9558c2ecf20Sopenharmony_ci	device_destroy(vmur_class, urd->char_device->dev);
9568c2ecf20Sopenharmony_ci	cdev_del(urd->char_device);
9578c2ecf20Sopenharmony_ci	urd->char_device = NULL;
9588c2ecf20Sopenharmony_ci	rc = 0;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cifail_urdev_put:
9618c2ecf20Sopenharmony_ci	urdev_put(urd);
9628c2ecf20Sopenharmony_ci	return rc;
9638c2ecf20Sopenharmony_ci}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_cistatic int ur_set_offline(struct ccw_device *cdev)
9668c2ecf20Sopenharmony_ci{
9678c2ecf20Sopenharmony_ci	int rc;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	mutex_lock(&vmur_mutex);
9708c2ecf20Sopenharmony_ci	rc = ur_set_offline_force(cdev, 0);
9718c2ecf20Sopenharmony_ci	mutex_unlock(&vmur_mutex);
9728c2ecf20Sopenharmony_ci	return rc;
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_cistatic void ur_remove(struct ccw_device *cdev)
9768c2ecf20Sopenharmony_ci{
9778c2ecf20Sopenharmony_ci	unsigned long flags;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	TRACE("ur_remove\n");
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	mutex_lock(&vmur_mutex);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	if (cdev->online)
9848c2ecf20Sopenharmony_ci		ur_set_offline_force(cdev, 1);
9858c2ecf20Sopenharmony_ci	ur_remove_attributes(&cdev->dev);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
9888c2ecf20Sopenharmony_ci	urdev_put(dev_get_drvdata(&cdev->dev));
9898c2ecf20Sopenharmony_ci	dev_set_drvdata(&cdev->dev, NULL);
9908c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	mutex_unlock(&vmur_mutex);
9938c2ecf20Sopenharmony_ci}
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci/*
9968c2ecf20Sopenharmony_ci * Module initialisation and cleanup
9978c2ecf20Sopenharmony_ci */
9988c2ecf20Sopenharmony_cistatic int __init ur_init(void)
9998c2ecf20Sopenharmony_ci{
10008c2ecf20Sopenharmony_ci	int rc;
10018c2ecf20Sopenharmony_ci	dev_t dev;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	if (!MACHINE_IS_VM) {
10048c2ecf20Sopenharmony_ci		pr_err("The %s cannot be loaded without z/VM\n",
10058c2ecf20Sopenharmony_ci		       ur_banner);
10068c2ecf20Sopenharmony_ci		return -ENODEV;
10078c2ecf20Sopenharmony_ci	}
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	vmur_dbf = debug_register("vmur", 4, 1, 4 * sizeof(long));
10108c2ecf20Sopenharmony_ci	if (!vmur_dbf)
10118c2ecf20Sopenharmony_ci		return -ENOMEM;
10128c2ecf20Sopenharmony_ci	rc = debug_register_view(vmur_dbf, &debug_sprintf_view);
10138c2ecf20Sopenharmony_ci	if (rc)
10148c2ecf20Sopenharmony_ci		goto fail_free_dbf;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	debug_set_level(vmur_dbf, 6);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	vmur_class = class_create(THIS_MODULE, "vmur");
10198c2ecf20Sopenharmony_ci	if (IS_ERR(vmur_class)) {
10208c2ecf20Sopenharmony_ci		rc = PTR_ERR(vmur_class);
10218c2ecf20Sopenharmony_ci		goto fail_free_dbf;
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	rc = ccw_driver_register(&ur_driver);
10258c2ecf20Sopenharmony_ci	if (rc)
10268c2ecf20Sopenharmony_ci		goto fail_class_destroy;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	rc = alloc_chrdev_region(&dev, 0, NUM_MINORS, "vmur");
10298c2ecf20Sopenharmony_ci	if (rc) {
10308c2ecf20Sopenharmony_ci		pr_err("Kernel function alloc_chrdev_region failed with "
10318c2ecf20Sopenharmony_ci		       "error code %d\n", rc);
10328c2ecf20Sopenharmony_ci		goto fail_unregister_driver;
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci	ur_first_dev_maj_min = MKDEV(MAJOR(dev), 0);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	pr_info("%s loaded.\n", ur_banner);
10378c2ecf20Sopenharmony_ci	return 0;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_cifail_unregister_driver:
10408c2ecf20Sopenharmony_ci	ccw_driver_unregister(&ur_driver);
10418c2ecf20Sopenharmony_cifail_class_destroy:
10428c2ecf20Sopenharmony_ci	class_destroy(vmur_class);
10438c2ecf20Sopenharmony_cifail_free_dbf:
10448c2ecf20Sopenharmony_ci	debug_unregister(vmur_dbf);
10458c2ecf20Sopenharmony_ci	return rc;
10468c2ecf20Sopenharmony_ci}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_cistatic void __exit ur_exit(void)
10498c2ecf20Sopenharmony_ci{
10508c2ecf20Sopenharmony_ci	unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS);
10518c2ecf20Sopenharmony_ci	ccw_driver_unregister(&ur_driver);
10528c2ecf20Sopenharmony_ci	class_destroy(vmur_class);
10538c2ecf20Sopenharmony_ci	debug_unregister(vmur_dbf);
10548c2ecf20Sopenharmony_ci	pr_info("%s unloaded.\n", ur_banner);
10558c2ecf20Sopenharmony_ci}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_cimodule_init(ur_init);
10588c2ecf20Sopenharmony_cimodule_exit(ur_exit);
1059