xref: /openbmc/linux/drivers/s390/char/vmur.c (revision 0a87c5cfc0bb0c1bdcc1cc9fd82e4a1711fac512)
1810cb5b3SFrank Munzert /*
2810cb5b3SFrank Munzert  * Linux driver for System z and s390 unit record devices
3810cb5b3SFrank Munzert  * (z/VM virtual punch, reader, printer)
4810cb5b3SFrank Munzert  *
5810cb5b3SFrank Munzert  * Copyright IBM Corp. 2001, 2007
6810cb5b3SFrank Munzert  * Authors: Malcolm Beattie <beattiem@uk.ibm.com>
7810cb5b3SFrank Munzert  *	    Michael Holzheu <holzheu@de.ibm.com>
8810cb5b3SFrank Munzert  *	    Frank Munzert <munzert@de.ibm.com>
9810cb5b3SFrank Munzert  */
10810cb5b3SFrank Munzert 
11810cb5b3SFrank Munzert #include <linux/cdev.h>
12810cb5b3SFrank Munzert 
13810cb5b3SFrank Munzert #include <asm/uaccess.h>
14810cb5b3SFrank Munzert #include <asm/cio.h>
15810cb5b3SFrank Munzert #include <asm/ccwdev.h>
16810cb5b3SFrank Munzert #include <asm/debug.h>
17*0a87c5cfSMichael Holzheu #include <asm/diag.h>
18810cb5b3SFrank Munzert 
19810cb5b3SFrank Munzert #include "vmur.h"
20810cb5b3SFrank Munzert 
21810cb5b3SFrank Munzert /*
22810cb5b3SFrank Munzert  * Driver overview
23810cb5b3SFrank Munzert  *
24810cb5b3SFrank Munzert  * Unit record device support is implemented as a character device driver.
25810cb5b3SFrank Munzert  * We can fit at least 16 bits into a device minor number and use the
26810cb5b3SFrank Munzert  * simple method of mapping a character device number with minor abcd
27810cb5b3SFrank Munzert  * to the unit record device with devno abcd.
28810cb5b3SFrank Munzert  * I/O to virtual unit record devices is handled as follows:
29810cb5b3SFrank Munzert  * Reads: Diagnose code 0x14 (input spool file manipulation)
30810cb5b3SFrank Munzert  * is used to read spool data page-wise.
31810cb5b3SFrank Munzert  * Writes: The CCW used is WRITE_CCW_CMD (0x01). The device's record length
32810cb5b3SFrank Munzert  * is available by reading sysfs attr reclen. Each write() to the device
33810cb5b3SFrank Munzert  * must specify an integral multiple (maximal 511) of reclen.
34810cb5b3SFrank Munzert  */
35810cb5b3SFrank Munzert 
36810cb5b3SFrank Munzert static char ur_banner[] = "z/VM virtual unit record device driver";
37810cb5b3SFrank Munzert 
38810cb5b3SFrank Munzert MODULE_AUTHOR("IBM Corporation");
39810cb5b3SFrank Munzert MODULE_DESCRIPTION("s390 z/VM virtual unit record device driver");
40810cb5b3SFrank Munzert MODULE_LICENSE("GPL");
41810cb5b3SFrank Munzert 
42810cb5b3SFrank Munzert #define PRINTK_HEADER "vmur: "
43810cb5b3SFrank Munzert 
44810cb5b3SFrank Munzert static dev_t ur_first_dev_maj_min;
45810cb5b3SFrank Munzert static struct class *vmur_class;
46810cb5b3SFrank Munzert static struct debug_info *vmur_dbf;
47810cb5b3SFrank Munzert 
48810cb5b3SFrank Munzert /* We put the device's record length (for writes) in the driver_info field */
49810cb5b3SFrank Munzert static struct ccw_device_id ur_ids[] = {
50810cb5b3SFrank Munzert 	{ CCWDEV_CU_DI(READER_PUNCH_DEVTYPE, 80) },
51810cb5b3SFrank Munzert 	{ CCWDEV_CU_DI(PRINTER_DEVTYPE, 132) },
52810cb5b3SFrank Munzert 	{ /* end of list */ }
53810cb5b3SFrank Munzert };
54810cb5b3SFrank Munzert 
55810cb5b3SFrank Munzert MODULE_DEVICE_TABLE(ccw, ur_ids);
56810cb5b3SFrank Munzert 
57810cb5b3SFrank Munzert static int ur_probe(struct ccw_device *cdev);
58810cb5b3SFrank Munzert static void ur_remove(struct ccw_device *cdev);
59810cb5b3SFrank Munzert static int ur_set_online(struct ccw_device *cdev);
60810cb5b3SFrank Munzert static int ur_set_offline(struct ccw_device *cdev);
61810cb5b3SFrank Munzert 
62810cb5b3SFrank Munzert static struct ccw_driver ur_driver = {
63810cb5b3SFrank Munzert 	.name		= "vmur",
64810cb5b3SFrank Munzert 	.owner		= THIS_MODULE,
65810cb5b3SFrank Munzert 	.ids		= ur_ids,
66810cb5b3SFrank Munzert 	.probe		= ur_probe,
67810cb5b3SFrank Munzert 	.remove		= ur_remove,
68810cb5b3SFrank Munzert 	.set_online	= ur_set_online,
69810cb5b3SFrank Munzert 	.set_offline	= ur_set_offline,
70810cb5b3SFrank Munzert };
71810cb5b3SFrank Munzert 
72810cb5b3SFrank Munzert /*
73810cb5b3SFrank Munzert  * Allocation, freeing, getting and putting of urdev structures
74810cb5b3SFrank Munzert  */
75810cb5b3SFrank Munzert static struct urdev *urdev_alloc(struct ccw_device *cdev)
76810cb5b3SFrank Munzert {
77810cb5b3SFrank Munzert 	struct urdev *urd;
78810cb5b3SFrank Munzert 
79810cb5b3SFrank Munzert 	urd = kzalloc(sizeof(struct urdev), GFP_KERNEL);
80810cb5b3SFrank Munzert 	if (!urd)
81810cb5b3SFrank Munzert 		return NULL;
82810cb5b3SFrank Munzert 	urd->cdev = cdev;
83810cb5b3SFrank Munzert 	urd->reclen = cdev->id.driver_info;
84810cb5b3SFrank Munzert 	ccw_device_get_id(cdev, &urd->dev_id);
85810cb5b3SFrank Munzert 	mutex_init(&urd->io_mutex);
86810cb5b3SFrank Munzert 	mutex_init(&urd->open_mutex);
87810cb5b3SFrank Munzert 	return urd;
88810cb5b3SFrank Munzert }
89810cb5b3SFrank Munzert 
90810cb5b3SFrank Munzert static void urdev_free(struct urdev *urd)
91810cb5b3SFrank Munzert {
92810cb5b3SFrank Munzert 	kfree(urd);
93810cb5b3SFrank Munzert }
94810cb5b3SFrank Munzert 
95810cb5b3SFrank Munzert /*
96810cb5b3SFrank Munzert  * This is how the character device driver gets a reference to a
97810cb5b3SFrank Munzert  * ur device. When this call returns successfully, a reference has
98810cb5b3SFrank Munzert  * been taken (by get_device) on the underlying kobject. The recipient
99810cb5b3SFrank Munzert  * of this urdev pointer must eventually drop it with urdev_put(urd)
100810cb5b3SFrank Munzert  * which does the corresponding put_device().
101810cb5b3SFrank Munzert  */
102810cb5b3SFrank Munzert static struct urdev *urdev_get_from_devno(u16 devno)
103810cb5b3SFrank Munzert {
104810cb5b3SFrank Munzert 	char bus_id[16];
105810cb5b3SFrank Munzert 	struct ccw_device *cdev;
106810cb5b3SFrank Munzert 
107810cb5b3SFrank Munzert 	sprintf(bus_id, "0.0.%04x", devno);
108810cb5b3SFrank Munzert 	cdev = get_ccwdev_by_busid(&ur_driver, bus_id);
109810cb5b3SFrank Munzert 	if (!cdev)
110810cb5b3SFrank Munzert 		return NULL;
111810cb5b3SFrank Munzert 
112810cb5b3SFrank Munzert 	return cdev->dev.driver_data;
113810cb5b3SFrank Munzert }
114810cb5b3SFrank Munzert 
115810cb5b3SFrank Munzert static void urdev_put(struct urdev *urd)
116810cb5b3SFrank Munzert {
117810cb5b3SFrank Munzert 	put_device(&urd->cdev->dev);
118810cb5b3SFrank Munzert }
119810cb5b3SFrank Munzert 
120810cb5b3SFrank Munzert /*
121810cb5b3SFrank Munzert  * Low-level functions to do I/O to a ur device.
122810cb5b3SFrank Munzert  *     alloc_chan_prog
1231eade380SMichael Holzheu  *     free_chan_prog
124810cb5b3SFrank Munzert  *     do_ur_io
125810cb5b3SFrank Munzert  *     ur_int_handler
126810cb5b3SFrank Munzert  *
127810cb5b3SFrank Munzert  * alloc_chan_prog allocates and builds the channel program
1281eade380SMichael Holzheu  * free_chan_prog frees memory of the channel program
129810cb5b3SFrank Munzert  *
130810cb5b3SFrank Munzert  * do_ur_io issues the channel program to the device and blocks waiting
131810cb5b3SFrank Munzert  * on a completion event it publishes at urd->io_done. The function
132810cb5b3SFrank Munzert  * serialises itself on the device's mutex so that only one I/O
133810cb5b3SFrank Munzert  * is issued at a time (and that I/O is synchronous).
134810cb5b3SFrank Munzert  *
135810cb5b3SFrank Munzert  * ur_int_handler catches the "I/O done" interrupt, writes the
136810cb5b3SFrank Munzert  * subchannel status word into the scsw member of the urdev structure
137810cb5b3SFrank Munzert  * and complete()s the io_done to wake the waiting do_ur_io.
138810cb5b3SFrank Munzert  *
139810cb5b3SFrank Munzert  * The caller of do_ur_io is responsible for kfree()ing the channel program
140810cb5b3SFrank Munzert  * address pointer that alloc_chan_prog returned.
141810cb5b3SFrank Munzert  */
142810cb5b3SFrank Munzert 
1431eade380SMichael Holzheu static void free_chan_prog(struct ccw1 *cpa)
1441eade380SMichael Holzheu {
1451eade380SMichael Holzheu 	struct ccw1 *ptr = cpa;
1461eade380SMichael Holzheu 
1471eade380SMichael Holzheu 	while (ptr->cda) {
1481eade380SMichael Holzheu 		kfree((void *)(addr_t) ptr->cda);
1491eade380SMichael Holzheu 		ptr++;
1501eade380SMichael Holzheu 	}
1511eade380SMichael Holzheu 	kfree(cpa);
1521eade380SMichael Holzheu }
153810cb5b3SFrank Munzert 
154810cb5b3SFrank Munzert /*
155810cb5b3SFrank Munzert  * alloc_chan_prog
156810cb5b3SFrank Munzert  * The channel program we use is write commands chained together
157810cb5b3SFrank Munzert  * with a final NOP CCW command-chained on (which ensures that CE and DE
158810cb5b3SFrank Munzert  * are presented together in a single interrupt instead of as separate
159810cb5b3SFrank Munzert  * interrupts unless an incorrect length indication kicks in first). The
1601eade380SMichael Holzheu  * data length in each CCW is reclen.
161810cb5b3SFrank Munzert  */
1621eade380SMichael Holzheu static struct ccw1 *alloc_chan_prog(const char __user *ubuf, int rec_count,
1631eade380SMichael Holzheu 				    int reclen)
164810cb5b3SFrank Munzert {
165810cb5b3SFrank Munzert 	struct ccw1 *cpa;
1661eade380SMichael Holzheu 	void *kbuf;
167810cb5b3SFrank Munzert 	int i;
168810cb5b3SFrank Munzert 
1691eade380SMichael Holzheu 	TRACE("alloc_chan_prog(%p, %i, %i)\n", ubuf, rec_count, reclen);
170810cb5b3SFrank Munzert 
171810cb5b3SFrank Munzert 	/*
172810cb5b3SFrank Munzert 	 * We chain a NOP onto the writes to force CE+DE together.
173810cb5b3SFrank Munzert 	 * That means we allocate room for CCWs to cover count/reclen
174810cb5b3SFrank Munzert 	 * records plus a NOP.
175810cb5b3SFrank Munzert 	 */
1761eade380SMichael Holzheu 	cpa = kzalloc((rec_count + 1) * sizeof(struct ccw1),
1771eade380SMichael Holzheu 		      GFP_KERNEL | GFP_DMA);
178810cb5b3SFrank Munzert 	if (!cpa)
1791eade380SMichael Holzheu 		return ERR_PTR(-ENOMEM);
180810cb5b3SFrank Munzert 
1811eade380SMichael Holzheu 	for (i = 0; i < rec_count; i++) {
182810cb5b3SFrank Munzert 		cpa[i].cmd_code = WRITE_CCW_CMD;
183810cb5b3SFrank Munzert 		cpa[i].flags = CCW_FLAG_CC | CCW_FLAG_SLI;
184810cb5b3SFrank Munzert 		cpa[i].count = reclen;
1851eade380SMichael Holzheu 		kbuf = kmalloc(reclen, GFP_KERNEL | GFP_DMA);
1861eade380SMichael Holzheu 		if (!kbuf) {
1871eade380SMichael Holzheu 			free_chan_prog(cpa);
1881eade380SMichael Holzheu 			return ERR_PTR(-ENOMEM);
1891eade380SMichael Holzheu 		}
1901eade380SMichael Holzheu 		cpa[i].cda = (u32)(addr_t) kbuf;
1911eade380SMichael Holzheu 		if (copy_from_user(kbuf, ubuf, reclen)) {
1921eade380SMichael Holzheu 			free_chan_prog(cpa);
1931eade380SMichael Holzheu 			return ERR_PTR(-EFAULT);
1941eade380SMichael Holzheu 		}
1951eade380SMichael Holzheu 		ubuf += reclen;
196810cb5b3SFrank Munzert 	}
197810cb5b3SFrank Munzert 	/* The following NOP CCW forces CE+DE to be presented together */
198810cb5b3SFrank Munzert 	cpa[i].cmd_code = CCW_CMD_NOOP;
199810cb5b3SFrank Munzert 	return cpa;
200810cb5b3SFrank Munzert }
201810cb5b3SFrank Munzert 
202810cb5b3SFrank Munzert static int do_ur_io(struct urdev *urd, struct ccw1 *cpa)
203810cb5b3SFrank Munzert {
204810cb5b3SFrank Munzert 	int rc;
205810cb5b3SFrank Munzert 	struct ccw_device *cdev = urd->cdev;
206278bc68cSHeiko Carstens 	DECLARE_COMPLETION_ONSTACK(event);
207810cb5b3SFrank Munzert 
208810cb5b3SFrank Munzert 	TRACE("do_ur_io: cpa=%p\n", cpa);
209810cb5b3SFrank Munzert 
210810cb5b3SFrank Munzert 	rc = mutex_lock_interruptible(&urd->io_mutex);
211810cb5b3SFrank Munzert 	if (rc)
212810cb5b3SFrank Munzert 		return rc;
213810cb5b3SFrank Munzert 
214810cb5b3SFrank Munzert 	urd->io_done = &event;
215810cb5b3SFrank Munzert 
216810cb5b3SFrank Munzert 	spin_lock_irq(get_ccwdev_lock(cdev));
217810cb5b3SFrank Munzert 	rc = ccw_device_start(cdev, cpa, 1, 0, 0);
218810cb5b3SFrank Munzert 	spin_unlock_irq(get_ccwdev_lock(cdev));
219810cb5b3SFrank Munzert 
220810cb5b3SFrank Munzert 	TRACE("do_ur_io: ccw_device_start returned %d\n", rc);
221810cb5b3SFrank Munzert 	if (rc)
222810cb5b3SFrank Munzert 		goto out;
223810cb5b3SFrank Munzert 
224810cb5b3SFrank Munzert 	wait_for_completion(&event);
225810cb5b3SFrank Munzert 	TRACE("do_ur_io: I/O complete\n");
226810cb5b3SFrank Munzert 	rc = 0;
227810cb5b3SFrank Munzert 
228810cb5b3SFrank Munzert out:
229810cb5b3SFrank Munzert 	mutex_unlock(&urd->io_mutex);
230810cb5b3SFrank Munzert 	return rc;
231810cb5b3SFrank Munzert }
232810cb5b3SFrank Munzert 
233810cb5b3SFrank Munzert /*
234810cb5b3SFrank Munzert  * ur interrupt handler, called from the ccw_device layer
235810cb5b3SFrank Munzert  */
236810cb5b3SFrank Munzert static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
237810cb5b3SFrank Munzert 			   struct irb *irb)
238810cb5b3SFrank Munzert {
239810cb5b3SFrank Munzert 	struct urdev *urd;
240810cb5b3SFrank Munzert 
241810cb5b3SFrank Munzert 	TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n",
242810cb5b3SFrank Munzert 	      intparm, irb->scsw.cstat, irb->scsw.dstat, irb->scsw.count);
243810cb5b3SFrank Munzert 
244810cb5b3SFrank Munzert 	if (!intparm) {
245810cb5b3SFrank Munzert 		TRACE("ur_int_handler: unsolicited interrupt\n");
246810cb5b3SFrank Munzert 		return;
247810cb5b3SFrank Munzert 	}
248810cb5b3SFrank Munzert 	urd = cdev->dev.driver_data;
249810cb5b3SFrank Munzert 	/* On special conditions irb is an error pointer */
250810cb5b3SFrank Munzert 	if (IS_ERR(irb))
251810cb5b3SFrank Munzert 		urd->io_request_rc = PTR_ERR(irb);
252810cb5b3SFrank Munzert 	else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
253810cb5b3SFrank Munzert 		urd->io_request_rc = 0;
254810cb5b3SFrank Munzert 	else
255810cb5b3SFrank Munzert 		urd->io_request_rc = -EIO;
256810cb5b3SFrank Munzert 
257810cb5b3SFrank Munzert 	complete(urd->io_done);
258810cb5b3SFrank Munzert }
259810cb5b3SFrank Munzert 
260810cb5b3SFrank Munzert /*
261810cb5b3SFrank Munzert  * reclen sysfs attribute - The record length to be used for write CCWs
262810cb5b3SFrank Munzert  */
263810cb5b3SFrank Munzert static ssize_t ur_attr_reclen_show(struct device *dev,
264810cb5b3SFrank Munzert 				   struct device_attribute *attr, char *buf)
265810cb5b3SFrank Munzert {
266810cb5b3SFrank Munzert 	struct urdev *urd = dev->driver_data;
267810cb5b3SFrank Munzert 
268810cb5b3SFrank Munzert 	return sprintf(buf, "%zu\n", urd->reclen);
269810cb5b3SFrank Munzert }
270810cb5b3SFrank Munzert 
271810cb5b3SFrank Munzert static DEVICE_ATTR(reclen, 0444, ur_attr_reclen_show, NULL);
272810cb5b3SFrank Munzert 
273810cb5b3SFrank Munzert static int ur_create_attributes(struct device *dev)
274810cb5b3SFrank Munzert {
275810cb5b3SFrank Munzert 	return device_create_file(dev, &dev_attr_reclen);
276810cb5b3SFrank Munzert }
277810cb5b3SFrank Munzert 
278810cb5b3SFrank Munzert static void ur_remove_attributes(struct device *dev)
279810cb5b3SFrank Munzert {
280810cb5b3SFrank Munzert 	device_remove_file(dev, &dev_attr_reclen);
281810cb5b3SFrank Munzert }
282810cb5b3SFrank Munzert 
283810cb5b3SFrank Munzert /*
284810cb5b3SFrank Munzert  * diagnose code 0x210 - retrieve device information
285810cb5b3SFrank Munzert  * cc=0  normal completion, we have a real device
286810cb5b3SFrank Munzert  * cc=1  CP paging error
287810cb5b3SFrank Munzert  * cc=2  The virtual device exists, but is not associated with a real device
288810cb5b3SFrank Munzert  * cc=3  Invalid device address, or the virtual device does not exist
289810cb5b3SFrank Munzert  */
290810cb5b3SFrank Munzert static int get_urd_class(struct urdev *urd)
291810cb5b3SFrank Munzert {
292810cb5b3SFrank Munzert 	static struct diag210 ur_diag210;
293810cb5b3SFrank Munzert 	int cc;
294810cb5b3SFrank Munzert 
295810cb5b3SFrank Munzert 	ur_diag210.vrdcdvno = urd->dev_id.devno;
296810cb5b3SFrank Munzert 	ur_diag210.vrdclen = sizeof(struct diag210);
297810cb5b3SFrank Munzert 
298810cb5b3SFrank Munzert 	cc = diag210(&ur_diag210);
299810cb5b3SFrank Munzert 	switch (cc) {
300810cb5b3SFrank Munzert 	case 0:
301810cb5b3SFrank Munzert 		return -ENOTSUPP;
302810cb5b3SFrank Munzert 	case 2:
303810cb5b3SFrank Munzert 		return ur_diag210.vrdcvcla; /* virtual device class */
304810cb5b3SFrank Munzert 	case 3:
305810cb5b3SFrank Munzert 		return -ENODEV;
306810cb5b3SFrank Munzert 	default:
307810cb5b3SFrank Munzert 		return -EIO;
308810cb5b3SFrank Munzert 	}
309810cb5b3SFrank Munzert }
310810cb5b3SFrank Munzert 
311810cb5b3SFrank Munzert /*
312810cb5b3SFrank Munzert  * Allocation and freeing of urfile structures
313810cb5b3SFrank Munzert  */
314810cb5b3SFrank Munzert static struct urfile *urfile_alloc(struct urdev *urd)
315810cb5b3SFrank Munzert {
316810cb5b3SFrank Munzert 	struct urfile *urf;
317810cb5b3SFrank Munzert 
318810cb5b3SFrank Munzert 	urf = kzalloc(sizeof(struct urfile), GFP_KERNEL);
319810cb5b3SFrank Munzert 	if (!urf)
320810cb5b3SFrank Munzert 		return NULL;
321810cb5b3SFrank Munzert 	urf->urd = urd;
322810cb5b3SFrank Munzert 
323810cb5b3SFrank Munzert 	TRACE("urfile_alloc: urd=%p urf=%p rl=%zu\n", urd, urf,
324810cb5b3SFrank Munzert 	      urf->dev_reclen);
325810cb5b3SFrank Munzert 
326810cb5b3SFrank Munzert 	return urf;
327810cb5b3SFrank Munzert }
328810cb5b3SFrank Munzert 
329810cb5b3SFrank Munzert static void urfile_free(struct urfile *urf)
330810cb5b3SFrank Munzert {
331810cb5b3SFrank Munzert 	TRACE("urfile_free: urf=%p urd=%p\n", urf, urf->urd);
332810cb5b3SFrank Munzert 	kfree(urf);
333810cb5b3SFrank Munzert }
334810cb5b3SFrank Munzert 
335810cb5b3SFrank Munzert /*
336810cb5b3SFrank Munzert  * The fops implementation of the character device driver
337810cb5b3SFrank Munzert  */
338810cb5b3SFrank Munzert static ssize_t do_write(struct urdev *urd, const char __user *udata,
339810cb5b3SFrank Munzert 			size_t count, size_t reclen, loff_t *ppos)
340810cb5b3SFrank Munzert {
341810cb5b3SFrank Munzert 	struct ccw1 *cpa;
342810cb5b3SFrank Munzert 	int rc;
343810cb5b3SFrank Munzert 
3441eade380SMichael Holzheu 	cpa = alloc_chan_prog(udata, count / reclen, reclen);
3451eade380SMichael Holzheu 	if (IS_ERR(cpa))
3461eade380SMichael Holzheu 		return PTR_ERR(cpa);
347810cb5b3SFrank Munzert 
348810cb5b3SFrank Munzert 	rc = do_ur_io(urd, cpa);
349810cb5b3SFrank Munzert 	if (rc)
350810cb5b3SFrank Munzert 		goto fail_kfree_cpa;
351810cb5b3SFrank Munzert 
352810cb5b3SFrank Munzert 	if (urd->io_request_rc) {
353810cb5b3SFrank Munzert 		rc = urd->io_request_rc;
354810cb5b3SFrank Munzert 		goto fail_kfree_cpa;
355810cb5b3SFrank Munzert 	}
356810cb5b3SFrank Munzert 	*ppos += count;
357810cb5b3SFrank Munzert 	rc = count;
3581eade380SMichael Holzheu 
359810cb5b3SFrank Munzert fail_kfree_cpa:
3601eade380SMichael Holzheu 	free_chan_prog(cpa);
361810cb5b3SFrank Munzert 	return rc;
362810cb5b3SFrank Munzert }
363810cb5b3SFrank Munzert 
364810cb5b3SFrank Munzert static ssize_t ur_write(struct file *file, const char __user *udata,
365810cb5b3SFrank Munzert 			size_t count, loff_t *ppos)
366810cb5b3SFrank Munzert {
367810cb5b3SFrank Munzert 	struct urfile *urf = file->private_data;
368810cb5b3SFrank Munzert 
369810cb5b3SFrank Munzert 	TRACE("ur_write: count=%zu\n", count);
370810cb5b3SFrank Munzert 
371810cb5b3SFrank Munzert 	if (count == 0)
372810cb5b3SFrank Munzert 		return 0;
373810cb5b3SFrank Munzert 
374810cb5b3SFrank Munzert 	if (count % urf->dev_reclen)
375810cb5b3SFrank Munzert 		return -EINVAL;	/* count must be a multiple of reclen */
376810cb5b3SFrank Munzert 
377810cb5b3SFrank Munzert 	if (count > urf->dev_reclen * MAX_RECS_PER_IO)
378810cb5b3SFrank Munzert 		count = urf->dev_reclen * MAX_RECS_PER_IO;
379810cb5b3SFrank Munzert 
380810cb5b3SFrank Munzert 	return do_write(urf->urd, udata, count, urf->dev_reclen, ppos);
381810cb5b3SFrank Munzert }
382810cb5b3SFrank Munzert 
383810cb5b3SFrank Munzert /*
384810cb5b3SFrank Munzert  * diagnose code 0x14 subcode 0x0028 - position spool file to designated
385810cb5b3SFrank Munzert  *				       record
386810cb5b3SFrank Munzert  * cc=0  normal completion
387810cb5b3SFrank Munzert  * cc=2  no file active on the virtual reader or device not ready
388810cb5b3SFrank Munzert  * cc=3  record specified is beyond EOF
389810cb5b3SFrank Munzert  */
390810cb5b3SFrank Munzert static int diag_position_to_record(int devno, int record)
391810cb5b3SFrank Munzert {
392810cb5b3SFrank Munzert 	int cc;
393810cb5b3SFrank Munzert 
394*0a87c5cfSMichael Holzheu 	cc = diag14(record, devno, 0x28);
395810cb5b3SFrank Munzert 	switch (cc) {
396810cb5b3SFrank Munzert 	case 0:
397810cb5b3SFrank Munzert 		return 0;
398810cb5b3SFrank Munzert 	case 2:
399810cb5b3SFrank Munzert 		return -ENOMEDIUM;
400810cb5b3SFrank Munzert 	case 3:
401810cb5b3SFrank Munzert 		return -ENODATA; /* position beyond end of file */
402810cb5b3SFrank Munzert 	default:
403810cb5b3SFrank Munzert 		return -EIO;
404810cb5b3SFrank Munzert 	}
405810cb5b3SFrank Munzert }
406810cb5b3SFrank Munzert 
407810cb5b3SFrank Munzert /*
408810cb5b3SFrank Munzert  * diagnose code 0x14 subcode 0x0000 - read next spool file buffer
409810cb5b3SFrank Munzert  * cc=0  normal completion
410810cb5b3SFrank Munzert  * cc=1  EOF reached
411810cb5b3SFrank Munzert  * cc=2  no file active on the virtual reader, and no file eligible
412810cb5b3SFrank Munzert  * cc=3  file already active on the virtual reader or specified virtual
413810cb5b3SFrank Munzert  *	 reader does not exist or is not a reader
414810cb5b3SFrank Munzert  */
415810cb5b3SFrank Munzert static int diag_read_file(int devno, char *buf)
416810cb5b3SFrank Munzert {
417810cb5b3SFrank Munzert 	int cc;
418810cb5b3SFrank Munzert 
419*0a87c5cfSMichael Holzheu 	cc = diag14((unsigned long) buf, devno, 0x00);
420810cb5b3SFrank Munzert 	switch (cc) {
421810cb5b3SFrank Munzert 	case 0:
422810cb5b3SFrank Munzert 		return 0;
423810cb5b3SFrank Munzert 	case 1:
424810cb5b3SFrank Munzert 		return -ENODATA;
425810cb5b3SFrank Munzert 	case 2:
426810cb5b3SFrank Munzert 		return -ENOMEDIUM;
427810cb5b3SFrank Munzert 	default:
428810cb5b3SFrank Munzert 		return -EIO;
429810cb5b3SFrank Munzert 	}
430810cb5b3SFrank Munzert }
431810cb5b3SFrank Munzert 
432810cb5b3SFrank Munzert static ssize_t diag14_read(struct file *file, char __user *ubuf, size_t count,
433810cb5b3SFrank Munzert 			   loff_t *offs)
434810cb5b3SFrank Munzert {
435810cb5b3SFrank Munzert 	size_t len, copied, res;
436810cb5b3SFrank Munzert 	char *buf;
437810cb5b3SFrank Munzert 	int rc;
438810cb5b3SFrank Munzert 	u16 reclen;
439810cb5b3SFrank Munzert 	struct urdev *urd;
440810cb5b3SFrank Munzert 
441810cb5b3SFrank Munzert 	urd = ((struct urfile *) file->private_data)->urd;
442810cb5b3SFrank Munzert 	reclen = ((struct urfile *) file->private_data)->file_reclen;
443810cb5b3SFrank Munzert 
444810cb5b3SFrank Munzert 	rc = diag_position_to_record(urd->dev_id.devno, *offs / PAGE_SIZE + 1);
445810cb5b3SFrank Munzert 	if (rc == -ENODATA)
446810cb5b3SFrank Munzert 		return 0;
447810cb5b3SFrank Munzert 	if (rc)
448810cb5b3SFrank Munzert 		return rc;
449810cb5b3SFrank Munzert 
450810cb5b3SFrank Munzert 	len = min((size_t) PAGE_SIZE, count);
4513eed13ccSMichael Holzheu 	buf = (char *) __get_free_page(GFP_KERNEL | GFP_DMA);
452810cb5b3SFrank Munzert 	if (!buf)
453810cb5b3SFrank Munzert 		return -ENOMEM;
454810cb5b3SFrank Munzert 
455810cb5b3SFrank Munzert 	copied = 0;
456810cb5b3SFrank Munzert 	res = (size_t) (*offs % PAGE_SIZE);
457810cb5b3SFrank Munzert 	do {
458810cb5b3SFrank Munzert 		rc = diag_read_file(urd->dev_id.devno, buf);
459810cb5b3SFrank Munzert 		if (rc == -ENODATA) {
460810cb5b3SFrank Munzert 			break;
461810cb5b3SFrank Munzert 		}
462810cb5b3SFrank Munzert 		if (rc)
463810cb5b3SFrank Munzert 			goto fail;
4642b3d8c9eSFrank Munzert 		if (reclen && (copied == 0) && (*offs < PAGE_SIZE))
465810cb5b3SFrank Munzert 			*((u16 *) &buf[FILE_RECLEN_OFFSET]) = reclen;
466810cb5b3SFrank Munzert 		len = min(count - copied, PAGE_SIZE - res);
467810cb5b3SFrank Munzert 		if (copy_to_user(ubuf + copied, buf + res, len)) {
468810cb5b3SFrank Munzert 			rc = -EFAULT;
469810cb5b3SFrank Munzert 			goto fail;
470810cb5b3SFrank Munzert 		}
471810cb5b3SFrank Munzert 		res = 0;
472810cb5b3SFrank Munzert 		copied += len;
473810cb5b3SFrank Munzert 	} while (copied != count);
474810cb5b3SFrank Munzert 
475810cb5b3SFrank Munzert 	*offs += copied;
476810cb5b3SFrank Munzert 	rc = copied;
477810cb5b3SFrank Munzert fail:
4783eed13ccSMichael Holzheu 	free_page((unsigned long) buf);
479810cb5b3SFrank Munzert 	return rc;
480810cb5b3SFrank Munzert }
481810cb5b3SFrank Munzert 
482810cb5b3SFrank Munzert static ssize_t ur_read(struct file *file, char __user *ubuf, size_t count,
483810cb5b3SFrank Munzert 		       loff_t *offs)
484810cb5b3SFrank Munzert {
485810cb5b3SFrank Munzert 	struct urdev *urd;
486810cb5b3SFrank Munzert 	int rc;
487810cb5b3SFrank Munzert 
488810cb5b3SFrank Munzert 	TRACE("ur_read: count=%zu ppos=%li\n", count, (unsigned long) *offs);
489810cb5b3SFrank Munzert 
490810cb5b3SFrank Munzert 	if (count == 0)
491810cb5b3SFrank Munzert 		return 0;
492810cb5b3SFrank Munzert 
493810cb5b3SFrank Munzert 	urd = ((struct urfile *) file->private_data)->urd;
494810cb5b3SFrank Munzert 	rc = mutex_lock_interruptible(&urd->io_mutex);
495810cb5b3SFrank Munzert 	if (rc)
496810cb5b3SFrank Munzert 		return rc;
497810cb5b3SFrank Munzert 	rc = diag14_read(file, ubuf, count, offs);
498810cb5b3SFrank Munzert 	mutex_unlock(&urd->io_mutex);
499810cb5b3SFrank Munzert 	return rc;
500810cb5b3SFrank Munzert }
501810cb5b3SFrank Munzert 
502810cb5b3SFrank Munzert /*
503810cb5b3SFrank Munzert  * diagnose code 0x14 subcode 0x0fff - retrieve next file descriptor
504810cb5b3SFrank Munzert  * cc=0  normal completion
505810cb5b3SFrank Munzert  * cc=1  no files on reader queue or no subsequent file
506810cb5b3SFrank Munzert  * cc=2  spid specified is invalid
507810cb5b3SFrank Munzert  */
508810cb5b3SFrank Munzert static int diag_read_next_file_info(struct file_control_block *buf, int spid)
509810cb5b3SFrank Munzert {
510810cb5b3SFrank Munzert 	int cc;
511810cb5b3SFrank Munzert 
512*0a87c5cfSMichael Holzheu 	cc = diag14((unsigned long) buf, spid, 0xfff);
513810cb5b3SFrank Munzert 	switch (cc) {
514810cb5b3SFrank Munzert 	case 0:
515810cb5b3SFrank Munzert 		return 0;
516810cb5b3SFrank Munzert 	default:
517810cb5b3SFrank Munzert 		return -ENODATA;
518810cb5b3SFrank Munzert 	}
519810cb5b3SFrank Munzert }
520810cb5b3SFrank Munzert 
5213eed13ccSMichael Holzheu static int verify_uri_device(struct urdev *urd)
522810cb5b3SFrank Munzert {
5233eed13ccSMichael Holzheu 	struct file_control_block *fcb;
524810cb5b3SFrank Munzert 	char *buf;
525810cb5b3SFrank Munzert 	int rc;
526810cb5b3SFrank Munzert 
5273eed13ccSMichael Holzheu 	fcb = kmalloc(sizeof(*fcb), GFP_KERNEL | GFP_DMA);
5283eed13ccSMichael Holzheu 	if (!fcb)
5293eed13ccSMichael Holzheu 		return -ENOMEM;
5303eed13ccSMichael Holzheu 
5313eed13ccSMichael Holzheu 	/* check for empty reader device (beginning of chain) */
5323eed13ccSMichael Holzheu 	rc = diag_read_next_file_info(fcb, 0);
5333eed13ccSMichael Holzheu 	if (rc)
5343eed13ccSMichael Holzheu 		goto fail_free_fcb;
5353eed13ccSMichael Holzheu 
5363eed13ccSMichael Holzheu 	/* if file is in hold status, we do not read it */
5373eed13ccSMichael Holzheu 	if (fcb->file_stat & (FLG_SYSTEM_HOLD | FLG_USER_HOLD)) {
5383eed13ccSMichael Holzheu 		rc = -EPERM;
5393eed13ccSMichael Holzheu 		goto fail_free_fcb;
5403eed13ccSMichael Holzheu 	}
5413eed13ccSMichael Holzheu 
5423eed13ccSMichael Holzheu 	/* open file on virtual reader	*/
5433eed13ccSMichael Holzheu 	buf = (char *) __get_free_page(GFP_KERNEL | GFP_DMA);
5443eed13ccSMichael Holzheu 	if (!buf) {
5453eed13ccSMichael Holzheu 		rc = -ENOMEM;
5463eed13ccSMichael Holzheu 		goto fail_free_fcb;
5473eed13ccSMichael Holzheu 	}
5483eed13ccSMichael Holzheu 	rc = diag_read_file(urd->dev_id.devno, buf);
5493eed13ccSMichael Holzheu 	if ((rc != 0) && (rc != -ENODATA)) /* EOF does not hurt */
5503eed13ccSMichael Holzheu 		goto fail_free_buf;
5513eed13ccSMichael Holzheu 
5523eed13ccSMichael Holzheu 	/* check if the file on top of the queue is open now */
5533eed13ccSMichael Holzheu 	rc = diag_read_next_file_info(fcb, 0);
5543eed13ccSMichael Holzheu 	if (rc)
5553eed13ccSMichael Holzheu 		goto fail_free_buf;
5563eed13ccSMichael Holzheu 	if (!(fcb->file_stat & FLG_IN_USE)) {
5573eed13ccSMichael Holzheu 		rc = -EMFILE;
5583eed13ccSMichael Holzheu 		goto fail_free_buf;
5593eed13ccSMichael Holzheu 	}
5603eed13ccSMichael Holzheu 	rc = 0;
5613eed13ccSMichael Holzheu 
5623eed13ccSMichael Holzheu fail_free_buf:
5633eed13ccSMichael Holzheu 	free_page((unsigned long) buf);
5643eed13ccSMichael Holzheu fail_free_fcb:
5653eed13ccSMichael Holzheu 	kfree(fcb);
5663eed13ccSMichael Holzheu 	return rc;
5673eed13ccSMichael Holzheu }
5683eed13ccSMichael Holzheu 
5693eed13ccSMichael Holzheu static int verify_device(struct urdev *urd)
5703eed13ccSMichael Holzheu {
571810cb5b3SFrank Munzert 	switch (urd->class) {
572810cb5b3SFrank Munzert 	case DEV_CLASS_UR_O:
573810cb5b3SFrank Munzert 		return 0; /* no check needed here */
574810cb5b3SFrank Munzert 	case DEV_CLASS_UR_I:
5753eed13ccSMichael Holzheu 		return verify_uri_device(urd);
576810cb5b3SFrank Munzert 	default:
577810cb5b3SFrank Munzert 		return -ENOTSUPP;
578810cb5b3SFrank Munzert 	}
579810cb5b3SFrank Munzert }
580810cb5b3SFrank Munzert 
5813eed13ccSMichael Holzheu static int get_uri_file_reclen(struct urdev *urd)
5823eed13ccSMichael Holzheu {
5833eed13ccSMichael Holzheu 	struct file_control_block *fcb;
5843eed13ccSMichael Holzheu 	int rc;
5853eed13ccSMichael Holzheu 
5863eed13ccSMichael Holzheu 	fcb = kmalloc(sizeof(*fcb), GFP_KERNEL | GFP_DMA);
5873eed13ccSMichael Holzheu 	if (!fcb)
5883eed13ccSMichael Holzheu 		return -ENOMEM;
5893eed13ccSMichael Holzheu 	rc = diag_read_next_file_info(fcb, 0);
5903eed13ccSMichael Holzheu 	if (rc)
5913eed13ccSMichael Holzheu 		goto fail_free;
5923eed13ccSMichael Holzheu 	if (fcb->file_stat & FLG_CP_DUMP)
5933eed13ccSMichael Holzheu 		rc = 0;
5943eed13ccSMichael Holzheu 	else
5953eed13ccSMichael Holzheu 		rc = fcb->rec_len;
5963eed13ccSMichael Holzheu 
5973eed13ccSMichael Holzheu fail_free:
5983eed13ccSMichael Holzheu 	kfree(fcb);
5993eed13ccSMichael Holzheu 	return rc;
6003eed13ccSMichael Holzheu }
6013eed13ccSMichael Holzheu 
602810cb5b3SFrank Munzert static int get_file_reclen(struct urdev *urd)
603810cb5b3SFrank Munzert {
604810cb5b3SFrank Munzert 	switch (urd->class) {
605810cb5b3SFrank Munzert 	case DEV_CLASS_UR_O:
606810cb5b3SFrank Munzert 		return 0;
607810cb5b3SFrank Munzert 	case DEV_CLASS_UR_I:
6083eed13ccSMichael Holzheu 		return get_uri_file_reclen(urd);
609810cb5b3SFrank Munzert 	default:
610810cb5b3SFrank Munzert 		return -ENOTSUPP;
611810cb5b3SFrank Munzert 	}
612810cb5b3SFrank Munzert }
613810cb5b3SFrank Munzert 
614810cb5b3SFrank Munzert static int ur_open(struct inode *inode, struct file *file)
615810cb5b3SFrank Munzert {
616810cb5b3SFrank Munzert 	u16 devno;
617810cb5b3SFrank Munzert 	struct urdev *urd;
618810cb5b3SFrank Munzert 	struct urfile *urf;
619810cb5b3SFrank Munzert 	unsigned short accmode;
620810cb5b3SFrank Munzert 	int rc;
621810cb5b3SFrank Munzert 
622810cb5b3SFrank Munzert 	accmode = file->f_flags & O_ACCMODE;
623810cb5b3SFrank Munzert 
624810cb5b3SFrank Munzert 	if (accmode == O_RDWR)
625810cb5b3SFrank Munzert 		return -EACCES;
626810cb5b3SFrank Munzert 
627810cb5b3SFrank Munzert 	/*
628810cb5b3SFrank Munzert 	 * We treat the minor number as the devno of the ur device
629810cb5b3SFrank Munzert 	 * to find in the driver tree.
630810cb5b3SFrank Munzert 	 */
631810cb5b3SFrank Munzert 	devno = MINOR(file->f_dentry->d_inode->i_rdev);
632810cb5b3SFrank Munzert 
633810cb5b3SFrank Munzert 	urd = urdev_get_from_devno(devno);
634810cb5b3SFrank Munzert 	if (!urd)
635810cb5b3SFrank Munzert 		return -ENXIO;
636810cb5b3SFrank Munzert 
637810cb5b3SFrank Munzert 	if (file->f_flags & O_NONBLOCK) {
638810cb5b3SFrank Munzert 		if (!mutex_trylock(&urd->open_mutex)) {
639810cb5b3SFrank Munzert 			rc = -EBUSY;
640810cb5b3SFrank Munzert 			goto fail_put;
641810cb5b3SFrank Munzert 		}
642810cb5b3SFrank Munzert 	} else {
643810cb5b3SFrank Munzert 		if (mutex_lock_interruptible(&urd->open_mutex)) {
644810cb5b3SFrank Munzert 			rc = -ERESTARTSYS;
645810cb5b3SFrank Munzert 			goto fail_put;
646810cb5b3SFrank Munzert 		}
647810cb5b3SFrank Munzert 	}
648810cb5b3SFrank Munzert 
649810cb5b3SFrank Munzert 	TRACE("ur_open\n");
650810cb5b3SFrank Munzert 
651810cb5b3SFrank Munzert 	if (((accmode == O_RDONLY) && (urd->class != DEV_CLASS_UR_I)) ||
652810cb5b3SFrank Munzert 	    ((accmode == O_WRONLY) && (urd->class != DEV_CLASS_UR_O))) {
653810cb5b3SFrank Munzert 		TRACE("ur_open: unsupported dev class (%d)\n", urd->class);
654810cb5b3SFrank Munzert 		rc = -EACCES;
655810cb5b3SFrank Munzert 		goto fail_unlock;
656810cb5b3SFrank Munzert 	}
657810cb5b3SFrank Munzert 
658810cb5b3SFrank Munzert 	rc = verify_device(urd);
659810cb5b3SFrank Munzert 	if (rc)
660810cb5b3SFrank Munzert 		goto fail_unlock;
661810cb5b3SFrank Munzert 
662810cb5b3SFrank Munzert 	urf = urfile_alloc(urd);
663810cb5b3SFrank Munzert 	if (!urf) {
664810cb5b3SFrank Munzert 		rc = -ENOMEM;
665810cb5b3SFrank Munzert 		goto fail_unlock;
666810cb5b3SFrank Munzert 	}
667810cb5b3SFrank Munzert 
668810cb5b3SFrank Munzert 	urf->dev_reclen = urd->reclen;
669810cb5b3SFrank Munzert 	rc = get_file_reclen(urd);
670810cb5b3SFrank Munzert 	if (rc < 0)
671810cb5b3SFrank Munzert 		goto fail_urfile_free;
672810cb5b3SFrank Munzert 	urf->file_reclen = rc;
673810cb5b3SFrank Munzert 	file->private_data = urf;
674810cb5b3SFrank Munzert 	return 0;
675810cb5b3SFrank Munzert 
676810cb5b3SFrank Munzert fail_urfile_free:
677810cb5b3SFrank Munzert 	urfile_free(urf);
678810cb5b3SFrank Munzert fail_unlock:
679810cb5b3SFrank Munzert 	mutex_unlock(&urd->open_mutex);
680810cb5b3SFrank Munzert fail_put:
681810cb5b3SFrank Munzert 	urdev_put(urd);
682810cb5b3SFrank Munzert 	return rc;
683810cb5b3SFrank Munzert }
684810cb5b3SFrank Munzert 
685810cb5b3SFrank Munzert static int ur_release(struct inode *inode, struct file *file)
686810cb5b3SFrank Munzert {
687810cb5b3SFrank Munzert 	struct urfile *urf = file->private_data;
688810cb5b3SFrank Munzert 
689810cb5b3SFrank Munzert 	TRACE("ur_release\n");
690810cb5b3SFrank Munzert 	mutex_unlock(&urf->urd->open_mutex);
691810cb5b3SFrank Munzert 	urdev_put(urf->urd);
692810cb5b3SFrank Munzert 	urfile_free(urf);
693810cb5b3SFrank Munzert 	return 0;
694810cb5b3SFrank Munzert }
695810cb5b3SFrank Munzert 
696810cb5b3SFrank Munzert static loff_t ur_llseek(struct file *file, loff_t offset, int whence)
697810cb5b3SFrank Munzert {
698810cb5b3SFrank Munzert 	loff_t newpos;
699810cb5b3SFrank Munzert 
700810cb5b3SFrank Munzert 	if ((file->f_flags & O_ACCMODE) != O_RDONLY)
701810cb5b3SFrank Munzert 		return -ESPIPE; /* seek allowed only for reader */
702810cb5b3SFrank Munzert 	if (offset % PAGE_SIZE)
703810cb5b3SFrank Munzert 		return -ESPIPE; /* only multiples of 4K allowed */
704810cb5b3SFrank Munzert 	switch (whence) {
705810cb5b3SFrank Munzert 	case 0: /* SEEK_SET */
706810cb5b3SFrank Munzert 		newpos = offset;
707810cb5b3SFrank Munzert 		break;
708810cb5b3SFrank Munzert 	case 1: /* SEEK_CUR */
709810cb5b3SFrank Munzert 		newpos = file->f_pos + offset;
710810cb5b3SFrank Munzert 		break;
711810cb5b3SFrank Munzert 	default:
712810cb5b3SFrank Munzert 		return -EINVAL;
713810cb5b3SFrank Munzert 	}
714810cb5b3SFrank Munzert 	file->f_pos = newpos;
715810cb5b3SFrank Munzert 	return newpos;
716810cb5b3SFrank Munzert }
717810cb5b3SFrank Munzert 
718810cb5b3SFrank Munzert static struct file_operations ur_fops = {
719810cb5b3SFrank Munzert 	.owner	 = THIS_MODULE,
720810cb5b3SFrank Munzert 	.open	 = ur_open,
721810cb5b3SFrank Munzert 	.release = ur_release,
722810cb5b3SFrank Munzert 	.read	 = ur_read,
723810cb5b3SFrank Munzert 	.write	 = ur_write,
724810cb5b3SFrank Munzert 	.llseek  = ur_llseek,
725810cb5b3SFrank Munzert };
726810cb5b3SFrank Munzert 
727810cb5b3SFrank Munzert /*
728810cb5b3SFrank Munzert  * ccw_device infrastructure:
729810cb5b3SFrank Munzert  *     ur_probe gets its own ref to the device (i.e. get_device),
730810cb5b3SFrank Munzert  *     creates the struct urdev, the device attributes, sets up
731810cb5b3SFrank Munzert  *     the interrupt handler and validates the virtual unit record device.
732810cb5b3SFrank Munzert  *     ur_remove removes the device attributes, frees the struct urdev
733810cb5b3SFrank Munzert  *     and drops (put_device) the ref to the device we got in ur_probe.
734810cb5b3SFrank Munzert  */
735810cb5b3SFrank Munzert static int ur_probe(struct ccw_device *cdev)
736810cb5b3SFrank Munzert {
737810cb5b3SFrank Munzert 	struct urdev *urd;
738810cb5b3SFrank Munzert 	int rc;
739810cb5b3SFrank Munzert 
740810cb5b3SFrank Munzert 	TRACE("ur_probe: cdev=%p state=%d\n", cdev, *(int *) cdev->private);
741810cb5b3SFrank Munzert 
742810cb5b3SFrank Munzert 	if (!get_device(&cdev->dev))
743810cb5b3SFrank Munzert 		return -ENODEV;
744810cb5b3SFrank Munzert 
745810cb5b3SFrank Munzert 	urd = urdev_alloc(cdev);
746810cb5b3SFrank Munzert 	if (!urd) {
747810cb5b3SFrank Munzert 		rc = -ENOMEM;
748810cb5b3SFrank Munzert 		goto fail;
749810cb5b3SFrank Munzert 	}
750810cb5b3SFrank Munzert 	rc = ur_create_attributes(&cdev->dev);
751810cb5b3SFrank Munzert 	if (rc) {
752810cb5b3SFrank Munzert 		rc = -ENOMEM;
753810cb5b3SFrank Munzert 		goto fail;
754810cb5b3SFrank Munzert 	}
755810cb5b3SFrank Munzert 	cdev->dev.driver_data = urd;
756810cb5b3SFrank Munzert 	cdev->handler = ur_int_handler;
757810cb5b3SFrank Munzert 
758810cb5b3SFrank Munzert 	/* validate virtual unit record device */
759810cb5b3SFrank Munzert 	urd->class = get_urd_class(urd);
760810cb5b3SFrank Munzert 	if (urd->class < 0) {
761810cb5b3SFrank Munzert 		rc = urd->class;
762810cb5b3SFrank Munzert 		goto fail;
763810cb5b3SFrank Munzert 	}
764810cb5b3SFrank Munzert 	if ((urd->class != DEV_CLASS_UR_I) && (urd->class != DEV_CLASS_UR_O)) {
765810cb5b3SFrank Munzert 		rc = -ENOTSUPP;
766810cb5b3SFrank Munzert 		goto fail;
767810cb5b3SFrank Munzert 	}
768810cb5b3SFrank Munzert 
769810cb5b3SFrank Munzert 	return 0;
770810cb5b3SFrank Munzert 
771810cb5b3SFrank Munzert fail:
772810cb5b3SFrank Munzert 	urdev_free(urd);
773810cb5b3SFrank Munzert 	put_device(&cdev->dev);
774810cb5b3SFrank Munzert 	return rc;
775810cb5b3SFrank Munzert }
776810cb5b3SFrank Munzert 
777810cb5b3SFrank Munzert static void ur_remove(struct ccw_device *cdev)
778810cb5b3SFrank Munzert {
779810cb5b3SFrank Munzert 	struct urdev *urd = cdev->dev.driver_data;
780810cb5b3SFrank Munzert 
781810cb5b3SFrank Munzert 	TRACE("ur_remove\n");
782810cb5b3SFrank Munzert 	if (cdev->online)
783810cb5b3SFrank Munzert 		ur_set_offline(cdev);
784810cb5b3SFrank Munzert 	ur_remove_attributes(&cdev->dev);
785810cb5b3SFrank Munzert 	urdev_free(urd);
786810cb5b3SFrank Munzert 	put_device(&cdev->dev);
787810cb5b3SFrank Munzert }
788810cb5b3SFrank Munzert 
789810cb5b3SFrank Munzert static int ur_set_online(struct ccw_device *cdev)
790810cb5b3SFrank Munzert {
791810cb5b3SFrank Munzert 	struct urdev *urd;
792810cb5b3SFrank Munzert 	int minor, major, rc;
793810cb5b3SFrank Munzert 	char node_id[16];
794810cb5b3SFrank Munzert 
795810cb5b3SFrank Munzert 	TRACE("ur_set_online: cdev=%p state=%d\n", cdev,
796810cb5b3SFrank Munzert 	      *(int *) cdev->private);
797810cb5b3SFrank Munzert 
798810cb5b3SFrank Munzert 	if (!try_module_get(ur_driver.owner))
799810cb5b3SFrank Munzert 		return -EINVAL;
800810cb5b3SFrank Munzert 
801810cb5b3SFrank Munzert 	urd = (struct urdev *) cdev->dev.driver_data;
802810cb5b3SFrank Munzert 	minor = urd->dev_id.devno;
803810cb5b3SFrank Munzert 	major = MAJOR(ur_first_dev_maj_min);
804810cb5b3SFrank Munzert 
805810cb5b3SFrank Munzert 	urd->char_device = cdev_alloc();
806810cb5b3SFrank Munzert 	if (!urd->char_device) {
807810cb5b3SFrank Munzert 		rc = -ENOMEM;
808810cb5b3SFrank Munzert 		goto fail_module_put;
809810cb5b3SFrank Munzert 	}
810810cb5b3SFrank Munzert 
811810cb5b3SFrank Munzert 	cdev_init(urd->char_device, &ur_fops);
812810cb5b3SFrank Munzert 	urd->char_device->dev = MKDEV(major, minor);
813810cb5b3SFrank Munzert 	urd->char_device->owner = ur_fops.owner;
814810cb5b3SFrank Munzert 
815810cb5b3SFrank Munzert 	rc = cdev_add(urd->char_device, urd->char_device->dev, 1);
816810cb5b3SFrank Munzert 	if (rc)
817810cb5b3SFrank Munzert 		goto fail_free_cdev;
818810cb5b3SFrank Munzert 	if (urd->cdev->id.cu_type == READER_PUNCH_DEVTYPE) {
819810cb5b3SFrank Munzert 		if (urd->class == DEV_CLASS_UR_I)
820810cb5b3SFrank Munzert 			sprintf(node_id, "vmrdr-%s", cdev->dev.bus_id);
821810cb5b3SFrank Munzert 		if (urd->class == DEV_CLASS_UR_O)
822810cb5b3SFrank Munzert 			sprintf(node_id, "vmpun-%s", cdev->dev.bus_id);
823810cb5b3SFrank Munzert 	} else if (urd->cdev->id.cu_type == PRINTER_DEVTYPE) {
824810cb5b3SFrank Munzert 		sprintf(node_id, "vmprt-%s", cdev->dev.bus_id);
825810cb5b3SFrank Munzert 	} else {
826810cb5b3SFrank Munzert 		rc = -ENOTSUPP;
827810cb5b3SFrank Munzert 		goto fail_free_cdev;
828810cb5b3SFrank Munzert 	}
829810cb5b3SFrank Munzert 
830810cb5b3SFrank Munzert 	urd->device = device_create(vmur_class, NULL, urd->char_device->dev,
831810cb5b3SFrank Munzert 					"%s", node_id);
832810cb5b3SFrank Munzert 	if (IS_ERR(urd->device)) {
833810cb5b3SFrank Munzert 		rc = PTR_ERR(urd->device);
834810cb5b3SFrank Munzert 		TRACE("ur_set_online: device_create rc=%d\n", rc);
835810cb5b3SFrank Munzert 		goto fail_free_cdev;
836810cb5b3SFrank Munzert 	}
837810cb5b3SFrank Munzert 
838810cb5b3SFrank Munzert 	return 0;
839810cb5b3SFrank Munzert 
840810cb5b3SFrank Munzert fail_free_cdev:
841810cb5b3SFrank Munzert 	cdev_del(urd->char_device);
842810cb5b3SFrank Munzert fail_module_put:
843810cb5b3SFrank Munzert 	module_put(ur_driver.owner);
844810cb5b3SFrank Munzert 
845810cb5b3SFrank Munzert 	return rc;
846810cb5b3SFrank Munzert }
847810cb5b3SFrank Munzert 
848810cb5b3SFrank Munzert static int ur_set_offline(struct ccw_device *cdev)
849810cb5b3SFrank Munzert {
850810cb5b3SFrank Munzert 	struct urdev *urd;
851810cb5b3SFrank Munzert 
852810cb5b3SFrank Munzert 	TRACE("ur_set_offline: cdev=%p cdev->private=%p state=%d\n",
853810cb5b3SFrank Munzert 		cdev, cdev->private, *(int *) cdev->private);
854810cb5b3SFrank Munzert 	urd = (struct urdev *) cdev->dev.driver_data;
855810cb5b3SFrank Munzert 	device_destroy(vmur_class, urd->char_device->dev);
856810cb5b3SFrank Munzert 	cdev_del(urd->char_device);
857810cb5b3SFrank Munzert 	module_put(ur_driver.owner);
858810cb5b3SFrank Munzert 
859810cb5b3SFrank Munzert 	return 0;
860810cb5b3SFrank Munzert }
861810cb5b3SFrank Munzert 
862810cb5b3SFrank Munzert /*
863810cb5b3SFrank Munzert  * Module initialisation and cleanup
864810cb5b3SFrank Munzert  */
865810cb5b3SFrank Munzert static int __init ur_init(void)
866810cb5b3SFrank Munzert {
867810cb5b3SFrank Munzert 	int rc;
868810cb5b3SFrank Munzert 	dev_t dev;
869810cb5b3SFrank Munzert 
870810cb5b3SFrank Munzert 	if (!MACHINE_IS_VM) {
871810cb5b3SFrank Munzert 		PRINT_ERR("%s is only available under z/VM.\n", ur_banner);
872810cb5b3SFrank Munzert 		return -ENODEV;
873810cb5b3SFrank Munzert 	}
874810cb5b3SFrank Munzert 
875810cb5b3SFrank Munzert 	vmur_dbf = debug_register("vmur", 4, 1, 4 * sizeof(long));
876810cb5b3SFrank Munzert 	if (!vmur_dbf)
877810cb5b3SFrank Munzert 		return -ENOMEM;
878810cb5b3SFrank Munzert 	rc = debug_register_view(vmur_dbf, &debug_sprintf_view);
879810cb5b3SFrank Munzert 	if (rc)
880810cb5b3SFrank Munzert 		goto fail_free_dbf;
881810cb5b3SFrank Munzert 
882810cb5b3SFrank Munzert 	debug_set_level(vmur_dbf, 6);
883810cb5b3SFrank Munzert 
884810cb5b3SFrank Munzert 	rc = ccw_driver_register(&ur_driver);
885810cb5b3SFrank Munzert 	if (rc)
886810cb5b3SFrank Munzert 		goto fail_free_dbf;
887810cb5b3SFrank Munzert 
888810cb5b3SFrank Munzert 	rc = alloc_chrdev_region(&dev, 0, NUM_MINORS, "vmur");
889810cb5b3SFrank Munzert 	if (rc) {
890810cb5b3SFrank Munzert 		PRINT_ERR("alloc_chrdev_region failed: err = %d\n", rc);
891810cb5b3SFrank Munzert 		goto fail_unregister_driver;
892810cb5b3SFrank Munzert 	}
893810cb5b3SFrank Munzert 	ur_first_dev_maj_min = MKDEV(MAJOR(dev), 0);
894810cb5b3SFrank Munzert 
895810cb5b3SFrank Munzert 	vmur_class = class_create(THIS_MODULE, "vmur");
896810cb5b3SFrank Munzert 	if (IS_ERR(vmur_class)) {
897810cb5b3SFrank Munzert 		rc = PTR_ERR(vmur_class);
898810cb5b3SFrank Munzert 		goto fail_unregister_region;
899810cb5b3SFrank Munzert 	}
900810cb5b3SFrank Munzert 	PRINT_INFO("%s loaded.\n", ur_banner);
901810cb5b3SFrank Munzert 	return 0;
902810cb5b3SFrank Munzert 
903810cb5b3SFrank Munzert fail_unregister_region:
904810cb5b3SFrank Munzert 	unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS);
905810cb5b3SFrank Munzert fail_unregister_driver:
906810cb5b3SFrank Munzert 	ccw_driver_unregister(&ur_driver);
907810cb5b3SFrank Munzert fail_free_dbf:
908810cb5b3SFrank Munzert 	debug_unregister(vmur_dbf);
909810cb5b3SFrank Munzert 	return rc;
910810cb5b3SFrank Munzert }
911810cb5b3SFrank Munzert 
912810cb5b3SFrank Munzert static void __exit ur_exit(void)
913810cb5b3SFrank Munzert {
914810cb5b3SFrank Munzert 	class_destroy(vmur_class);
915810cb5b3SFrank Munzert 	unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS);
916810cb5b3SFrank Munzert 	ccw_driver_unregister(&ur_driver);
917810cb5b3SFrank Munzert 	debug_unregister(vmur_dbf);
918810cb5b3SFrank Munzert 	PRINT_INFO("%s unloaded.\n", ur_banner);
919810cb5b3SFrank Munzert }
920810cb5b3SFrank Munzert 
921810cb5b3SFrank Munzert module_init(ur_init);
922810cb5b3SFrank Munzert module_exit(ur_exit);
923