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