]>
Commit | Line | Data |
---|---|---|
810cb5b3 FM |
1 | /* |
2 | * Linux driver for System z and s390 unit record devices | |
3 | * (z/VM virtual punch, reader, printer) | |
4 | * | |
5 | * Copyright IBM Corp. 2001, 2007 | |
6 | * Authors: Malcolm Beattie <beattiem@uk.ibm.com> | |
7 | * Michael Holzheu <holzheu@de.ibm.com> | |
8 | * Frank Munzert <munzert@de.ibm.com> | |
9 | */ | |
10 | ||
11 | #include <linux/cdev.h> | |
12 | ||
13 | #include <asm/uaccess.h> | |
14 | #include <asm/cio.h> | |
15 | #include <asm/ccwdev.h> | |
16 | #include <asm/debug.h> | |
0a87c5cf | 17 | #include <asm/diag.h> |
810cb5b3 FM |
18 | |
19 | #include "vmur.h" | |
20 | ||
21 | /* | |
22 | * Driver overview | |
23 | * | |
24 | * Unit record device support is implemented as a character device driver. | |
25 | * We can fit at least 16 bits into a device minor number and use the | |
26 | * simple method of mapping a character device number with minor abcd | |
27 | * to the unit record device with devno abcd. | |
28 | * I/O to virtual unit record devices is handled as follows: | |
29 | * Reads: Diagnose code 0x14 (input spool file manipulation) | |
30 | * is used to read spool data page-wise. | |
31 | * Writes: The CCW used is WRITE_CCW_CMD (0x01). The device's record length | |
32 | * is available by reading sysfs attr reclen. Each write() to the device | |
33 | * must specify an integral multiple (maximal 511) of reclen. | |
34 | */ | |
35 | ||
36 | static char ur_banner[] = "z/VM virtual unit record device driver"; | |
37 | ||
38 | MODULE_AUTHOR("IBM Corporation"); | |
39 | MODULE_DESCRIPTION("s390 z/VM virtual unit record device driver"); | |
40 | MODULE_LICENSE("GPL"); | |
41 | ||
42 | #define PRINTK_HEADER "vmur: " | |
43 | ||
44 | static dev_t ur_first_dev_maj_min; | |
45 | static struct class *vmur_class; | |
46 | static struct debug_info *vmur_dbf; | |
47 | ||
48 | /* We put the device's record length (for writes) in the driver_info field */ | |
49 | static struct ccw_device_id ur_ids[] = { | |
50 | { CCWDEV_CU_DI(READER_PUNCH_DEVTYPE, 80) }, | |
51 | { CCWDEV_CU_DI(PRINTER_DEVTYPE, 132) }, | |
52 | { /* end of list */ } | |
53 | }; | |
54 | ||
55 | MODULE_DEVICE_TABLE(ccw, ur_ids); | |
56 | ||
57 | static int ur_probe(struct ccw_device *cdev); | |
58 | static void ur_remove(struct ccw_device *cdev); | |
59 | static int ur_set_online(struct ccw_device *cdev); | |
60 | static int ur_set_offline(struct ccw_device *cdev); | |
61 | ||
62 | static struct ccw_driver ur_driver = { | |
63 | .name = "vmur", | |
64 | .owner = THIS_MODULE, | |
65 | .ids = ur_ids, | |
66 | .probe = ur_probe, | |
67 | .remove = ur_remove, | |
68 | .set_online = ur_set_online, | |
69 | .set_offline = ur_set_offline, | |
70 | }; | |
71 | ||
72 | /* | |
73 | * Allocation, freeing, getting and putting of urdev structures | |
74 | */ | |
75 | static struct urdev *urdev_alloc(struct ccw_device *cdev) | |
76 | { | |
77 | struct urdev *urd; | |
78 | ||
79 | urd = kzalloc(sizeof(struct urdev), GFP_KERNEL); | |
80 | if (!urd) | |
81 | return NULL; | |
82 | urd->cdev = cdev; | |
83 | urd->reclen = cdev->id.driver_info; | |
84 | ccw_device_get_id(cdev, &urd->dev_id); | |
85 | mutex_init(&urd->io_mutex); | |
86 | mutex_init(&urd->open_mutex); | |
87 | return urd; | |
88 | } | |
89 | ||
90 | static void urdev_free(struct urdev *urd) | |
91 | { | |
92 | kfree(urd); | |
93 | } | |
94 | ||
95 | /* | |
96 | * This is how the character device driver gets a reference to a | |
97 | * ur device. When this call returns successfully, a reference has | |
98 | * been taken (by get_device) on the underlying kobject. The recipient | |
99 | * of this urdev pointer must eventually drop it with urdev_put(urd) | |
100 | * which does the corresponding put_device(). | |
101 | */ | |
102 | static struct urdev *urdev_get_from_devno(u16 devno) | |
103 | { | |
104 | char bus_id[16]; | |
105 | struct ccw_device *cdev; | |
106 | ||
107 | sprintf(bus_id, "0.0.%04x", devno); | |
108 | cdev = get_ccwdev_by_busid(&ur_driver, bus_id); | |
109 | if (!cdev) | |
110 | return NULL; | |
111 | ||
112 | return cdev->dev.driver_data; | |
113 | } | |
114 | ||
115 | static void urdev_put(struct urdev *urd) | |
116 | { | |
117 | put_device(&urd->cdev->dev); | |
118 | } | |
119 | ||
120 | /* | |
121 | * Low-level functions to do I/O to a ur device. | |
122 | * alloc_chan_prog | |
1eade380 | 123 | * free_chan_prog |
810cb5b3 FM |
124 | * do_ur_io |
125 | * ur_int_handler | |
126 | * | |
127 | * alloc_chan_prog allocates and builds the channel program | |
1eade380 | 128 | * free_chan_prog frees memory of the channel program |
810cb5b3 FM |
129 | * |
130 | * do_ur_io issues the channel program to the device and blocks waiting | |
131 | * on a completion event it publishes at urd->io_done. The function | |
132 | * serialises itself on the device's mutex so that only one I/O | |
133 | * is issued at a time (and that I/O is synchronous). | |
134 | * | |
135 | * ur_int_handler catches the "I/O done" interrupt, writes the | |
136 | * subchannel status word into the scsw member of the urdev structure | |
137 | * and complete()s the io_done to wake the waiting do_ur_io. | |
138 | * | |
139 | * The caller of do_ur_io is responsible for kfree()ing the channel program | |
140 | * address pointer that alloc_chan_prog returned. | |
141 | */ | |
142 | ||
1eade380 MH |
143 | static void free_chan_prog(struct ccw1 *cpa) |
144 | { | |
145 | struct ccw1 *ptr = cpa; | |
146 | ||
147 | while (ptr->cda) { | |
148 | kfree((void *)(addr_t) ptr->cda); | |
149 | ptr++; | |
150 | } | |
151 | kfree(cpa); | |
152 | } | |
810cb5b3 FM |
153 | |
154 | /* | |
155 | * alloc_chan_prog | |
156 | * The channel program we use is write commands chained together | |
157 | * with a final NOP CCW command-chained on (which ensures that CE and DE | |
158 | * are presented together in a single interrupt instead of as separate | |
159 | * interrupts unless an incorrect length indication kicks in first). The | |
1eade380 | 160 | * data length in each CCW is reclen. |
810cb5b3 | 161 | */ |
1eade380 MH |
162 | static struct ccw1 *alloc_chan_prog(const char __user *ubuf, int rec_count, |
163 | int reclen) | |
810cb5b3 | 164 | { |
810cb5b3 | 165 | struct ccw1 *cpa; |
1eade380 | 166 | void *kbuf; |
810cb5b3 FM |
167 | int i; |
168 | ||
1eade380 | 169 | TRACE("alloc_chan_prog(%p, %i, %i)\n", ubuf, rec_count, reclen); |
810cb5b3 FM |
170 | |
171 | /* | |
172 | * We chain a NOP onto the writes to force CE+DE together. | |
173 | * That means we allocate room for CCWs to cover count/reclen | |
174 | * records plus a NOP. | |
175 | */ | |
1eade380 MH |
176 | cpa = kzalloc((rec_count + 1) * sizeof(struct ccw1), |
177 | GFP_KERNEL | GFP_DMA); | |
810cb5b3 | 178 | if (!cpa) |
1eade380 | 179 | return ERR_PTR(-ENOMEM); |
810cb5b3 | 180 | |
1eade380 | 181 | for (i = 0; i < rec_count; i++) { |
810cb5b3 FM |
182 | cpa[i].cmd_code = WRITE_CCW_CMD; |
183 | cpa[i].flags = CCW_FLAG_CC | CCW_FLAG_SLI; | |
184 | cpa[i].count = reclen; | |
1eade380 MH |
185 | kbuf = kmalloc(reclen, GFP_KERNEL | GFP_DMA); |
186 | if (!kbuf) { | |
187 | free_chan_prog(cpa); | |
188 | return ERR_PTR(-ENOMEM); | |
189 | } | |
190 | cpa[i].cda = (u32)(addr_t) kbuf; | |
191 | if (copy_from_user(kbuf, ubuf, reclen)) { | |
192 | free_chan_prog(cpa); | |
193 | return ERR_PTR(-EFAULT); | |
194 | } | |
195 | ubuf += reclen; | |
810cb5b3 FM |
196 | } |
197 | /* The following NOP CCW forces CE+DE to be presented together */ | |
198 | cpa[i].cmd_code = CCW_CMD_NOOP; | |
810cb5b3 FM |
199 | return cpa; |
200 | } | |
201 | ||
202 | static int do_ur_io(struct urdev *urd, struct ccw1 *cpa) | |
203 | { | |
204 | int rc; | |
205 | struct ccw_device *cdev = urd->cdev; | |
278bc68c | 206 | DECLARE_COMPLETION_ONSTACK(event); |
810cb5b3 FM |
207 | |
208 | TRACE("do_ur_io: cpa=%p\n", cpa); | |
209 | ||
210 | rc = mutex_lock_interruptible(&urd->io_mutex); | |
211 | if (rc) | |
212 | return rc; | |
213 | ||
214 | urd->io_done = &event; | |
215 | ||
216 | spin_lock_irq(get_ccwdev_lock(cdev)); | |
217 | rc = ccw_device_start(cdev, cpa, 1, 0, 0); | |
218 | spin_unlock_irq(get_ccwdev_lock(cdev)); | |
219 | ||
220 | TRACE("do_ur_io: ccw_device_start returned %d\n", rc); | |
221 | if (rc) | |
222 | goto out; | |
223 | ||
224 | wait_for_completion(&event); | |
225 | TRACE("do_ur_io: I/O complete\n"); | |
226 | rc = 0; | |
227 | ||
228 | out: | |
229 | mutex_unlock(&urd->io_mutex); | |
230 | return rc; | |
231 | } | |
232 | ||
233 | /* | |
234 | * ur interrupt handler, called from the ccw_device layer | |
235 | */ | |
236 | static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm, | |
237 | struct irb *irb) | |
238 | { | |
239 | struct urdev *urd; | |
240 | ||
241 | TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n", | |
242 | intparm, irb->scsw.cstat, irb->scsw.dstat, irb->scsw.count); | |
243 | ||
244 | if (!intparm) { | |
245 | TRACE("ur_int_handler: unsolicited interrupt\n"); | |
246 | return; | |
247 | } | |
248 | urd = cdev->dev.driver_data; | |
249 | /* On special conditions irb is an error pointer */ | |
250 | if (IS_ERR(irb)) | |
251 | urd->io_request_rc = PTR_ERR(irb); | |
252 | else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) | |
253 | urd->io_request_rc = 0; | |
254 | else | |
255 | urd->io_request_rc = -EIO; | |
256 | ||
257 | complete(urd->io_done); | |
258 | } | |
259 | ||
260 | /* | |
261 | * reclen sysfs attribute - The record length to be used for write CCWs | |
262 | */ | |
263 | static ssize_t ur_attr_reclen_show(struct device *dev, | |
264 | struct device_attribute *attr, char *buf) | |
265 | { | |
266 | struct urdev *urd = dev->driver_data; | |
267 | ||
268 | return sprintf(buf, "%zu\n", urd->reclen); | |
269 | } | |
270 | ||
271 | static DEVICE_ATTR(reclen, 0444, ur_attr_reclen_show, NULL); | |
272 | ||
273 | static int ur_create_attributes(struct device *dev) | |
274 | { | |
275 | return device_create_file(dev, &dev_attr_reclen); | |
276 | } | |
277 | ||
278 | static void ur_remove_attributes(struct device *dev) | |
279 | { | |
280 | device_remove_file(dev, &dev_attr_reclen); | |
281 | } | |
282 | ||
283 | /* | |
284 | * diagnose code 0x210 - retrieve device information | |
285 | * cc=0 normal completion, we have a real device | |
286 | * cc=1 CP paging error | |
287 | * cc=2 The virtual device exists, but is not associated with a real device | |
288 | * cc=3 Invalid device address, or the virtual device does not exist | |
289 | */ | |
290 | static int get_urd_class(struct urdev *urd) | |
291 | { | |
292 | static struct diag210 ur_diag210; | |
293 | int cc; | |
294 | ||
295 | ur_diag210.vrdcdvno = urd->dev_id.devno; | |
296 | ur_diag210.vrdclen = sizeof(struct diag210); | |
297 | ||
298 | cc = diag210(&ur_diag210); | |
299 | switch (cc) { | |
300 | case 0: | |
301 | return -ENOTSUPP; | |
302 | case 2: | |
303 | return ur_diag210.vrdcvcla; /* virtual device class */ | |
304 | case 3: | |
305 | return -ENODEV; | |
306 | default: | |
307 | return -EIO; | |
308 | } | |
309 | } | |
310 | ||
311 | /* | |
312 | * Allocation and freeing of urfile structures | |
313 | */ | |
314 | static struct urfile *urfile_alloc(struct urdev *urd) | |
315 | { | |
316 | struct urfile *urf; | |
317 | ||
318 | urf = kzalloc(sizeof(struct urfile), GFP_KERNEL); | |
319 | if (!urf) | |
320 | return NULL; | |
321 | urf->urd = urd; | |
322 | ||
323 | TRACE("urfile_alloc: urd=%p urf=%p rl=%zu\n", urd, urf, | |
324 | urf->dev_reclen); | |
325 | ||
326 | return urf; | |
327 | } | |
328 | ||
329 | static void urfile_free(struct urfile *urf) | |
330 | { | |
331 | TRACE("urfile_free: urf=%p urd=%p\n", urf, urf->urd); | |
332 | kfree(urf); | |
333 | } | |
334 | ||
335 | /* | |
336 | * The fops implementation of the character device driver | |
337 | */ | |
338 | static ssize_t do_write(struct urdev *urd, const char __user *udata, | |
339 | size_t count, size_t reclen, loff_t *ppos) | |
340 | { | |
341 | struct ccw1 *cpa; | |
810cb5b3 FM |
342 | int rc; |
343 | ||
1eade380 MH |
344 | cpa = alloc_chan_prog(udata, count / reclen, reclen); |
345 | if (IS_ERR(cpa)) | |
346 | return PTR_ERR(cpa); | |
810cb5b3 FM |
347 | |
348 | rc = do_ur_io(urd, cpa); | |
349 | if (rc) | |
350 | goto fail_kfree_cpa; | |
351 | ||
352 | if (urd->io_request_rc) { | |
353 | rc = urd->io_request_rc; | |
354 | goto fail_kfree_cpa; | |
355 | } | |
356 | *ppos += count; | |
357 | rc = count; | |
1eade380 | 358 | |
810cb5b3 | 359 | fail_kfree_cpa: |
1eade380 | 360 | free_chan_prog(cpa); |
810cb5b3 FM |
361 | return rc; |
362 | } | |
363 | ||
364 | static ssize_t ur_write(struct file *file, const char __user *udata, | |
365 | size_t count, loff_t *ppos) | |
366 | { | |
367 | struct urfile *urf = file->private_data; | |
368 | ||
369 | TRACE("ur_write: count=%zu\n", count); | |
370 | ||
371 | if (count == 0) | |
372 | return 0; | |
373 | ||
374 | if (count % urf->dev_reclen) | |
375 | return -EINVAL; /* count must be a multiple of reclen */ | |
376 | ||
377 | if (count > urf->dev_reclen * MAX_RECS_PER_IO) | |
378 | count = urf->dev_reclen * MAX_RECS_PER_IO; | |
379 | ||
380 | return do_write(urf->urd, udata, count, urf->dev_reclen, ppos); | |
381 | } | |
382 | ||
810cb5b3 FM |
383 | /* |
384 | * diagnose code 0x14 subcode 0x0028 - position spool file to designated | |
385 | * record | |
386 | * cc=0 normal completion | |
387 | * cc=2 no file active on the virtual reader or device not ready | |
388 | * cc=3 record specified is beyond EOF | |
389 | */ | |
390 | static int diag_position_to_record(int devno, int record) | |
391 | { | |
392 | int cc; | |
393 | ||
0a87c5cf | 394 | cc = diag14(record, devno, 0x28); |
810cb5b3 FM |
395 | switch (cc) { |
396 | case 0: | |
397 | return 0; | |
398 | case 2: | |
399 | return -ENOMEDIUM; | |
400 | case 3: | |
401 | return -ENODATA; /* position beyond end of file */ | |
402 | default: | |
403 | return -EIO; | |
404 | } | |
405 | } | |
406 | ||
407 | /* | |
408 | * diagnose code 0x14 subcode 0x0000 - read next spool file buffer | |
409 | * cc=0 normal completion | |
410 | * cc=1 EOF reached | |
411 | * cc=2 no file active on the virtual reader, and no file eligible | |
412 | * cc=3 file already active on the virtual reader or specified virtual | |
413 | * reader does not exist or is not a reader | |
414 | */ | |
415 | static int diag_read_file(int devno, char *buf) | |
416 | { | |
417 | int cc; | |
418 | ||
0a87c5cf | 419 | cc = diag14((unsigned long) buf, devno, 0x00); |
810cb5b3 FM |
420 | switch (cc) { |
421 | case 0: | |
422 | return 0; | |
423 | case 1: | |
424 | return -ENODATA; | |
425 | case 2: | |
426 | return -ENOMEDIUM; | |
427 | default: | |
428 | return -EIO; | |
429 | } | |
430 | } | |
431 | ||
432 | static ssize_t diag14_read(struct file *file, char __user *ubuf, size_t count, | |
433 | loff_t *offs) | |
434 | { | |
435 | size_t len, copied, res; | |
436 | char *buf; | |
437 | int rc; | |
438 | u16 reclen; | |
439 | struct urdev *urd; | |
440 | ||
441 | urd = ((struct urfile *) file->private_data)->urd; | |
442 | reclen = ((struct urfile *) file->private_data)->file_reclen; | |
443 | ||
444 | rc = diag_position_to_record(urd->dev_id.devno, *offs / PAGE_SIZE + 1); | |
445 | if (rc == -ENODATA) | |
446 | return 0; | |
447 | if (rc) | |
448 | return rc; | |
449 | ||
450 | len = min((size_t) PAGE_SIZE, count); | |
3eed13cc | 451 | buf = (char *) __get_free_page(GFP_KERNEL | GFP_DMA); |
810cb5b3 FM |
452 | if (!buf) |
453 | return -ENOMEM; | |
454 | ||
455 | copied = 0; | |
456 | res = (size_t) (*offs % PAGE_SIZE); | |
457 | do { | |
458 | rc = diag_read_file(urd->dev_id.devno, buf); | |
459 | if (rc == -ENODATA) { | |
460 | break; | |
461 | } | |
462 | if (rc) | |
463 | goto fail; | |
2b3d8c9e | 464 | if (reclen && (copied == 0) && (*offs < PAGE_SIZE)) |
810cb5b3 FM |
465 | *((u16 *) &buf[FILE_RECLEN_OFFSET]) = reclen; |
466 | len = min(count - copied, PAGE_SIZE - res); | |
467 | if (copy_to_user(ubuf + copied, buf + res, len)) { | |
468 | rc = -EFAULT; | |
469 | goto fail; | |
470 | } | |
471 | res = 0; | |
472 | copied += len; | |
473 | } while (copied != count); | |
474 | ||
475 | *offs += copied; | |
476 | rc = copied; | |
477 | fail: | |
3eed13cc | 478 | free_page((unsigned long) buf); |
810cb5b3 FM |
479 | return rc; |
480 | } | |
481 | ||
482 | static ssize_t ur_read(struct file *file, char __user *ubuf, size_t count, | |
483 | loff_t *offs) | |
484 | { | |
485 | struct urdev *urd; | |
486 | int rc; | |
487 | ||
488 | TRACE("ur_read: count=%zu ppos=%li\n", count, (unsigned long) *offs); | |
489 | ||
490 | if (count == 0) | |
491 | return 0; | |
492 | ||
493 | urd = ((struct urfile *) file->private_data)->urd; | |
494 | rc = mutex_lock_interruptible(&urd->io_mutex); | |
495 | if (rc) | |
496 | return rc; | |
497 | rc = diag14_read(file, ubuf, count, offs); | |
498 | mutex_unlock(&urd->io_mutex); | |
499 | return rc; | |
500 | } | |
501 | ||
502 | /* | |
503 | * diagnose code 0x14 subcode 0x0fff - retrieve next file descriptor | |
504 | * cc=0 normal completion | |
505 | * cc=1 no files on reader queue or no subsequent file | |
506 | * cc=2 spid specified is invalid | |
507 | */ | |
508 | static int diag_read_next_file_info(struct file_control_block *buf, int spid) | |
509 | { | |
510 | int cc; | |
511 | ||
0a87c5cf | 512 | cc = diag14((unsigned long) buf, spid, 0xfff); |
810cb5b3 FM |
513 | switch (cc) { |
514 | case 0: | |
515 | return 0; | |
516 | default: | |
517 | return -ENODATA; | |
518 | } | |
519 | } | |
520 | ||
3eed13cc | 521 | static int verify_uri_device(struct urdev *urd) |
810cb5b3 | 522 | { |
3eed13cc | 523 | struct file_control_block *fcb; |
810cb5b3 FM |
524 | char *buf; |
525 | int rc; | |
526 | ||
3eed13cc MH |
527 | fcb = kmalloc(sizeof(*fcb), GFP_KERNEL | GFP_DMA); |
528 | if (!fcb) | |
529 | return -ENOMEM; | |
530 | ||
531 | /* check for empty reader device (beginning of chain) */ | |
532 | rc = diag_read_next_file_info(fcb, 0); | |
533 | if (rc) | |
534 | goto fail_free_fcb; | |
535 | ||
536 | /* if file is in hold status, we do not read it */ | |
537 | if (fcb->file_stat & (FLG_SYSTEM_HOLD | FLG_USER_HOLD)) { | |
538 | rc = -EPERM; | |
539 | goto fail_free_fcb; | |
540 | } | |
541 | ||
542 | /* open file on virtual reader */ | |
543 | buf = (char *) __get_free_page(GFP_KERNEL | GFP_DMA); | |
544 | if (!buf) { | |
545 | rc = -ENOMEM; | |
546 | goto fail_free_fcb; | |
547 | } | |
548 | rc = diag_read_file(urd->dev_id.devno, buf); | |
549 | if ((rc != 0) && (rc != -ENODATA)) /* EOF does not hurt */ | |
550 | goto fail_free_buf; | |
551 | ||
552 | /* check if the file on top of the queue is open now */ | |
553 | rc = diag_read_next_file_info(fcb, 0); | |
554 | if (rc) | |
555 | goto fail_free_buf; | |
556 | if (!(fcb->file_stat & FLG_IN_USE)) { | |
557 | rc = -EMFILE; | |
558 | goto fail_free_buf; | |
559 | } | |
560 | rc = 0; | |
561 | ||
562 | fail_free_buf: | |
563 | free_page((unsigned long) buf); | |
564 | fail_free_fcb: | |
565 | kfree(fcb); | |
566 | return rc; | |
567 | } | |
568 | ||
569 | static int verify_device(struct urdev *urd) | |
570 | { | |
810cb5b3 FM |
571 | switch (urd->class) { |
572 | case DEV_CLASS_UR_O: | |
573 | return 0; /* no check needed here */ | |
574 | case DEV_CLASS_UR_I: | |
3eed13cc | 575 | return verify_uri_device(urd); |
810cb5b3 FM |
576 | default: |
577 | return -ENOTSUPP; | |
578 | } | |
579 | } | |
580 | ||
3eed13cc | 581 | static int get_uri_file_reclen(struct urdev *urd) |
810cb5b3 | 582 | { |
3eed13cc | 583 | struct file_control_block *fcb; |
810cb5b3 FM |
584 | int rc; |
585 | ||
3eed13cc MH |
586 | fcb = kmalloc(sizeof(*fcb), GFP_KERNEL | GFP_DMA); |
587 | if (!fcb) | |
588 | return -ENOMEM; | |
589 | rc = diag_read_next_file_info(fcb, 0); | |
590 | if (rc) | |
591 | goto fail_free; | |
592 | if (fcb->file_stat & FLG_CP_DUMP) | |
593 | rc = 0; | |
594 | else | |
595 | rc = fcb->rec_len; | |
596 | ||
597 | fail_free: | |
598 | kfree(fcb); | |
599 | return rc; | |
600 | } | |
601 | ||
602 | static int get_file_reclen(struct urdev *urd) | |
603 | { | |
810cb5b3 FM |
604 | switch (urd->class) { |
605 | case DEV_CLASS_UR_O: | |
606 | return 0; | |
607 | case DEV_CLASS_UR_I: | |
3eed13cc | 608 | return get_uri_file_reclen(urd); |
810cb5b3 FM |
609 | default: |
610 | return -ENOTSUPP; | |
611 | } | |
810cb5b3 FM |
612 | } |
613 | ||
614 | static int ur_open(struct inode *inode, struct file *file) | |
615 | { | |
616 | u16 devno; | |
617 | struct urdev *urd; | |
618 | struct urfile *urf; | |
619 | unsigned short accmode; | |
620 | int rc; | |
621 | ||
622 | accmode = file->f_flags & O_ACCMODE; | |
623 | ||
624 | if (accmode == O_RDWR) | |
625 | return -EACCES; | |
626 | ||
627 | /* | |
628 | * We treat the minor number as the devno of the ur device | |
629 | * to find in the driver tree. | |
630 | */ | |
631 | devno = MINOR(file->f_dentry->d_inode->i_rdev); | |
632 | ||
633 | urd = urdev_get_from_devno(devno); | |
634 | if (!urd) | |
635 | return -ENXIO; | |
636 | ||
637 | if (file->f_flags & O_NONBLOCK) { | |
638 | if (!mutex_trylock(&urd->open_mutex)) { | |
639 | rc = -EBUSY; | |
640 | goto fail_put; | |
641 | } | |
642 | } else { | |
643 | if (mutex_lock_interruptible(&urd->open_mutex)) { | |
644 | rc = -ERESTARTSYS; | |
645 | goto fail_put; | |
646 | } | |
647 | } | |
648 | ||
649 | TRACE("ur_open\n"); | |
650 | ||
651 | if (((accmode == O_RDONLY) && (urd->class != DEV_CLASS_UR_I)) || | |
652 | ((accmode == O_WRONLY) && (urd->class != DEV_CLASS_UR_O))) { | |
653 | TRACE("ur_open: unsupported dev class (%d)\n", urd->class); | |
654 | rc = -EACCES; | |
655 | goto fail_unlock; | |
656 | } | |
657 | ||
658 | rc = verify_device(urd); | |
659 | if (rc) | |
660 | goto fail_unlock; | |
661 | ||
662 | urf = urfile_alloc(urd); | |
663 | if (!urf) { | |
664 | rc = -ENOMEM; | |
665 | goto fail_unlock; | |
666 | } | |
667 | ||
668 | urf->dev_reclen = urd->reclen; | |
669 | rc = get_file_reclen(urd); | |
670 | if (rc < 0) | |
671 | goto fail_urfile_free; | |
672 | urf->file_reclen = rc; | |
673 | file->private_data = urf; | |
674 | return 0; | |
675 | ||
676 | fail_urfile_free: | |
677 | urfile_free(urf); | |
678 | fail_unlock: | |
679 | mutex_unlock(&urd->open_mutex); | |
680 | fail_put: | |
681 | urdev_put(urd); | |
682 | return rc; | |
683 | } | |
684 | ||
685 | static int ur_release(struct inode *inode, struct file *file) | |
686 | { | |
687 | struct urfile *urf = file->private_data; | |
688 | ||
689 | TRACE("ur_release\n"); | |
690 | mutex_unlock(&urf->urd->open_mutex); | |
691 | urdev_put(urf->urd); | |
692 | urfile_free(urf); | |
693 | return 0; | |
694 | } | |
695 | ||
696 | static loff_t ur_llseek(struct file *file, loff_t offset, int whence) | |
697 | { | |
698 | loff_t newpos; | |
699 | ||
700 | if ((file->f_flags & O_ACCMODE) != O_RDONLY) | |
701 | return -ESPIPE; /* seek allowed only for reader */ | |
702 | if (offset % PAGE_SIZE) | |
703 | return -ESPIPE; /* only multiples of 4K allowed */ | |
704 | switch (whence) { | |
705 | case 0: /* SEEK_SET */ | |
706 | newpos = offset; | |
707 | break; | |
708 | case 1: /* SEEK_CUR */ | |
709 | newpos = file->f_pos + offset; | |
710 | break; | |
711 | default: | |
712 | return -EINVAL; | |
713 | } | |
714 | file->f_pos = newpos; | |
715 | return newpos; | |
716 | } | |
717 | ||
718 | static struct file_operations ur_fops = { | |
719 | .owner = THIS_MODULE, | |
720 | .open = ur_open, | |
721 | .release = ur_release, | |
722 | .read = ur_read, | |
723 | .write = ur_write, | |
724 | .llseek = ur_llseek, | |
725 | }; | |
726 | ||
727 | /* | |
728 | * ccw_device infrastructure: | |
729 | * ur_probe gets its own ref to the device (i.e. get_device), | |
730 | * creates the struct urdev, the device attributes, sets up | |
731 | * the interrupt handler and validates the virtual unit record device. | |
732 | * ur_remove removes the device attributes, frees the struct urdev | |
733 | * and drops (put_device) the ref to the device we got in ur_probe. | |
734 | */ | |
735 | static int ur_probe(struct ccw_device *cdev) | |
736 | { | |
737 | struct urdev *urd; | |
738 | int rc; | |
739 | ||
740 | TRACE("ur_probe: cdev=%p state=%d\n", cdev, *(int *) cdev->private); | |
741 | ||
742 | if (!get_device(&cdev->dev)) | |
743 | return -ENODEV; | |
744 | ||
745 | urd = urdev_alloc(cdev); | |
746 | if (!urd) { | |
747 | rc = -ENOMEM; | |
748 | goto fail; | |
749 | } | |
750 | rc = ur_create_attributes(&cdev->dev); | |
751 | if (rc) { | |
752 | rc = -ENOMEM; | |
753 | goto fail; | |
754 | } | |
755 | cdev->dev.driver_data = urd; | |
756 | cdev->handler = ur_int_handler; | |
757 | ||
758 | /* validate virtual unit record device */ | |
759 | urd->class = get_urd_class(urd); | |
760 | if (urd->class < 0) { | |
761 | rc = urd->class; | |
762 | goto fail; | |
763 | } | |
764 | if ((urd->class != DEV_CLASS_UR_I) && (urd->class != DEV_CLASS_UR_O)) { | |
765 | rc = -ENOTSUPP; | |
766 | goto fail; | |
767 | } | |
768 | ||
769 | return 0; | |
770 | ||
771 | fail: | |
772 | urdev_free(urd); | |
773 | put_device(&cdev->dev); | |
774 | return rc; | |
775 | } | |
776 | ||
777 | static void ur_remove(struct ccw_device *cdev) | |
778 | { | |
779 | struct urdev *urd = cdev->dev.driver_data; | |
780 | ||
781 | TRACE("ur_remove\n"); | |
782 | if (cdev->online) | |
783 | ur_set_offline(cdev); | |
784 | ur_remove_attributes(&cdev->dev); | |
785 | urdev_free(urd); | |
786 | put_device(&cdev->dev); | |
787 | } | |
788 | ||
789 | static int ur_set_online(struct ccw_device *cdev) | |
790 | { | |
791 | struct urdev *urd; | |
792 | int minor, major, rc; | |
793 | char node_id[16]; | |
794 | ||
795 | TRACE("ur_set_online: cdev=%p state=%d\n", cdev, | |
796 | *(int *) cdev->private); | |
797 | ||
798 | if (!try_module_get(ur_driver.owner)) | |
799 | return -EINVAL; | |
800 | ||
801 | urd = (struct urdev *) cdev->dev.driver_data; | |
802 | minor = urd->dev_id.devno; | |
803 | major = MAJOR(ur_first_dev_maj_min); | |
804 | ||
805 | urd->char_device = cdev_alloc(); | |
806 | if (!urd->char_device) { | |
807 | rc = -ENOMEM; | |
808 | goto fail_module_put; | |
809 | } | |
810 | ||
811 | cdev_init(urd->char_device, &ur_fops); | |
812 | urd->char_device->dev = MKDEV(major, minor); | |
813 | urd->char_device->owner = ur_fops.owner; | |
814 | ||
815 | rc = cdev_add(urd->char_device, urd->char_device->dev, 1); | |
816 | if (rc) | |
817 | goto fail_free_cdev; | |
818 | if (urd->cdev->id.cu_type == READER_PUNCH_DEVTYPE) { | |
819 | if (urd->class == DEV_CLASS_UR_I) | |
820 | sprintf(node_id, "vmrdr-%s", cdev->dev.bus_id); | |
821 | if (urd->class == DEV_CLASS_UR_O) | |
822 | sprintf(node_id, "vmpun-%s", cdev->dev.bus_id); | |
823 | } else if (urd->cdev->id.cu_type == PRINTER_DEVTYPE) { | |
824 | sprintf(node_id, "vmprt-%s", cdev->dev.bus_id); | |
825 | } else { | |
826 | rc = -ENOTSUPP; | |
827 | goto fail_free_cdev; | |
828 | } | |
829 | ||
830 | urd->device = device_create(vmur_class, NULL, urd->char_device->dev, | |
831 | "%s", node_id); | |
832 | if (IS_ERR(urd->device)) { | |
833 | rc = PTR_ERR(urd->device); | |
834 | TRACE("ur_set_online: device_create rc=%d\n", rc); | |
835 | goto fail_free_cdev; | |
836 | } | |
837 | ||
838 | return 0; | |
839 | ||
840 | fail_free_cdev: | |
841 | cdev_del(urd->char_device); | |
842 | fail_module_put: | |
843 | module_put(ur_driver.owner); | |
844 | ||
845 | return rc; | |
846 | } | |
847 | ||
848 | static int ur_set_offline(struct ccw_device *cdev) | |
849 | { | |
850 | struct urdev *urd; | |
851 | ||
852 | TRACE("ur_set_offline: cdev=%p cdev->private=%p state=%d\n", | |
853 | cdev, cdev->private, *(int *) cdev->private); | |
854 | urd = (struct urdev *) cdev->dev.driver_data; | |
855 | device_destroy(vmur_class, urd->char_device->dev); | |
856 | cdev_del(urd->char_device); | |
857 | module_put(ur_driver.owner); | |
858 | ||
859 | return 0; | |
860 | } | |
861 | ||
862 | /* | |
863 | * Module initialisation and cleanup | |
864 | */ | |
865 | static int __init ur_init(void) | |
866 | { | |
867 | int rc; | |
868 | dev_t dev; | |
869 | ||
870 | if (!MACHINE_IS_VM) { | |
871 | PRINT_ERR("%s is only available under z/VM.\n", ur_banner); | |
872 | return -ENODEV; | |
873 | } | |
874 | ||
875 | vmur_dbf = debug_register("vmur", 4, 1, 4 * sizeof(long)); | |
876 | if (!vmur_dbf) | |
877 | return -ENOMEM; | |
878 | rc = debug_register_view(vmur_dbf, &debug_sprintf_view); | |
879 | if (rc) | |
880 | goto fail_free_dbf; | |
881 | ||
882 | debug_set_level(vmur_dbf, 6); | |
883 | ||
884 | rc = ccw_driver_register(&ur_driver); | |
885 | if (rc) | |
886 | goto fail_free_dbf; | |
887 | ||
888 | rc = alloc_chrdev_region(&dev, 0, NUM_MINORS, "vmur"); | |
889 | if (rc) { | |
890 | PRINT_ERR("alloc_chrdev_region failed: err = %d\n", rc); | |
891 | goto fail_unregister_driver; | |
892 | } | |
893 | ur_first_dev_maj_min = MKDEV(MAJOR(dev), 0); | |
894 | ||
895 | vmur_class = class_create(THIS_MODULE, "vmur"); | |
896 | if (IS_ERR(vmur_class)) { | |
897 | rc = PTR_ERR(vmur_class); | |
898 | goto fail_unregister_region; | |
899 | } | |
900 | PRINT_INFO("%s loaded.\n", ur_banner); | |
901 | return 0; | |
902 | ||
903 | fail_unregister_region: | |
904 | unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS); | |
905 | fail_unregister_driver: | |
906 | ccw_driver_unregister(&ur_driver); | |
907 | fail_free_dbf: | |
908 | debug_unregister(vmur_dbf); | |
909 | return rc; | |
910 | } | |
911 | ||
912 | static void __exit ur_exit(void) | |
913 | { | |
914 | class_destroy(vmur_class); | |
915 | unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS); | |
916 | ccw_driver_unregister(&ur_driver); | |
917 | debug_unregister(vmur_dbf); | |
918 | PRINT_INFO("%s unloaded.\n", ur_banner); | |
919 | } | |
920 | ||
921 | module_init(ur_init); | |
922 | module_exit(ur_exit); |