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