patch-2_6_7-vs1_9_1_12
[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         /* Check position. */
149         if (ppos != &filp->f_pos) {
150                 /*
151                  * "A request was outside the capabilities of the device."
152                  * This check uses internal knowledge about how pread and
153                  * read work...
154                  */
155                 DBF_EVENT(6, "TCHAR:ppos wrong\n");
156                 return -EOVERFLOW;
157         }
158
159         /*
160          * If the tape isn't terminated yet, do it now. And since we then
161          * are at the end of the tape there wouldn't be anything to read
162          * anyways. So we return immediatly.
163          */
164         if(device->required_tapemarks) {
165                 return tape_std_terminate_write(device);
166         }
167
168         /* Find out block size to use */
169         if (device->char_data.block_size != 0) {
170                 if (count < device->char_data.block_size) {
171                         DBF_EVENT(3, "TCHAR:read smaller than block "
172                                   "size was requested\n");
173                         return -EINVAL;
174                 }
175                 block_size = device->char_data.block_size;
176         } else {
177                 block_size = count;
178         }
179
180         rc = tapechar_check_idalbuffer(device, block_size);
181         if (rc)
182                 return rc;
183
184 #ifdef CONFIG_S390_TAPE_BLOCK
185         /* Changes position. */
186         device->blk_data.medium_changed = 1;
187 #endif
188
189         DBF_EVENT(6, "TCHAR:nbytes: %lx\n", block_size);
190         /* Let the discipline build the ccw chain. */
191         request = device->discipline->read_block(device, block_size);
192         if (IS_ERR(request))
193                 return PTR_ERR(request);
194         /* Execute it. */
195         rc = tape_do_io(device, request);
196         if (rc == 0) {
197                 rc = block_size - request->rescnt;
198                 DBF_EVENT(6, "TCHAR:rbytes:  %x\n", rc);
199                 filp->f_pos += rc;
200                 /* Copy data from idal buffer to user space. */
201                 if (idal_buffer_to_user(device->char_data.idal_buf,
202                                         data, rc) != 0)
203                         rc = -EFAULT;
204         }
205         tape_free_request(request);
206         return rc;
207 }
208
209 /*
210  * Tape device write function
211  */
212 ssize_t
213 tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t *ppos)
214 {
215         struct tape_device *device;
216         struct tape_request *request;
217         size_t block_size;
218         size_t written;
219         int nblocks;
220         int i, rc;
221
222         DBF_EVENT(6, "TCHAR:write\n");
223         device = (struct tape_device *) filp->private_data;
224         /* Check position */
225         if (ppos != &filp->f_pos) {
226                 /* "A request was outside the capabilities of the device." */
227                 DBF_EVENT(6, "TCHAR:ppos wrong\n");
228                 return -EOVERFLOW;
229         }
230         /* Find out block size and number of blocks */
231         if (device->char_data.block_size != 0) {
232                 if (count < device->char_data.block_size) {
233                         DBF_EVENT(3, "TCHAR:write smaller than block "
234                                   "size was requested\n");
235                         return -EINVAL;
236                 }
237                 block_size = device->char_data.block_size;
238                 nblocks = count / block_size;
239         } else {
240                 block_size = count;
241                 nblocks = 1;
242         }
243
244         rc = tapechar_check_idalbuffer(device, block_size);
245         if (rc)
246                 return rc;
247
248 #ifdef CONFIG_S390_TAPE_BLOCK
249         /* Changes position. */
250         device->blk_data.medium_changed = 1;
251 #endif
252
253         DBF_EVENT(6,"TCHAR:nbytes: %lx\n", block_size);
254         DBF_EVENT(6, "TCHAR:nblocks: %x\n", nblocks);
255         /* Let the discipline build the ccw chain. */
256         request = device->discipline->write_block(device, block_size);
257         if (IS_ERR(request))
258                 return PTR_ERR(request);
259         rc = 0;
260         written = 0;
261         for (i = 0; i < nblocks; i++) {
262                 /* Copy data from user space to idal buffer. */
263                 if (idal_buffer_from_user(device->char_data.idal_buf,
264                                           data, block_size)) {
265                         rc = -EFAULT;
266                         break;
267                 }
268                 rc = tape_do_io(device, request);
269                 if (rc)
270                         break;
271                 DBF_EVENT(6, "TCHAR:wbytes: %lx\n",
272                           block_size - request->rescnt);
273                 filp->f_pos += block_size - request->rescnt;
274                 written += block_size - request->rescnt;
275                 if (request->rescnt != 0)
276                         break;
277                 data += block_size;
278         }
279         tape_free_request(request);
280         if (rc == -ENOSPC) {
281                 /*
282                  * Ok, the device has no more space. It has NOT written
283                  * the block.
284                  */
285                 if (device->discipline->process_eov)
286                         device->discipline->process_eov(device);
287                 if (written > 0)
288                         rc = 0;
289
290         }
291
292         /*
293          * After doing a write we always need two tapemarks to correctly
294          * terminate the tape (one to terminate the file, the second to
295          * flag the end of recorded data.
296          * Since process_eov positions the tape in front of the written
297          * tapemark it doesn't hurt to write two marks again.
298          */
299         if (!rc)
300                 device->required_tapemarks = 2;
301
302         return rc ? rc : written;
303 }
304
305 /*
306  * Character frontend tape device open function.
307  */
308 int
309 tapechar_open (struct inode *inode, struct file *filp)
310 {
311         struct tape_device *device;
312         int minor, rc;
313
314         DBF_EVENT(6, "TCHAR:open: %i:%i\n",
315                 imajor(filp->f_dentry->d_inode),
316                 iminor(filp->f_dentry->d_inode));
317
318         if (imajor(filp->f_dentry->d_inode) != tapechar_major)
319                 return -ENODEV;
320
321         minor = iminor(filp->f_dentry->d_inode);
322         device = tape_get_device(minor / TAPE_MINORS_PER_DEV);
323         if (IS_ERR(device)) {
324                 DBF_EVENT(3, "TCHAR:open: tape_get_device() failed\n");
325                 return PTR_ERR(device);
326         }
327
328
329         rc = tape_open(device);
330         if (rc == 0) {
331                 filp->private_data = device;
332                 return 0;
333         }
334         tape_put_device(device);
335
336         return rc;
337 }
338
339 /*
340  * Character frontend tape device release function.
341  */
342
343 int
344 tapechar_release(struct inode *inode, struct file *filp)
345 {
346         struct tape_device *device;
347
348         DBF_EVENT(6, "TCHAR:release: %x\n", iminor(inode));
349         device = (struct tape_device *) filp->private_data;
350
351         /*
352          * If this is the rewinding tape minor then rewind. In that case we
353          * write all required tapemarks. Otherwise only one to terminate the
354          * file.
355          */
356         if ((iminor(inode) & 1) != 0) {
357                 if (device->required_tapemarks)
358                         tape_std_terminate_write(device);
359                 tape_mtop(device, MTREW, 1);
360         } else {
361                 if (device->required_tapemarks > 1) {
362                         if (tape_mtop(device, MTWEOF, 1) == 0)
363                                 device->required_tapemarks--;
364                 }
365         }
366
367         if (device->char_data.idal_buf != NULL) {
368                 idal_buffer_free(device->char_data.idal_buf);
369                 device->char_data.idal_buf = NULL;
370         }
371         tape_release(device);
372         filp->private_data = tape_put_device(device);
373
374         return 0;
375 }
376
377 /*
378  * Tape device io controls.
379  */
380 static int
381 tapechar_ioctl(struct inode *inp, struct file *filp,
382                unsigned int no, unsigned long data)
383 {
384         struct tape_device *device;
385         int rc;
386
387         DBF_EVENT(6, "TCHAR:ioct\n");
388
389         device = (struct tape_device *) filp->private_data;
390
391         if (no == MTIOCTOP) {
392                 struct mtop op;
393
394                 if (copy_from_user(&op, (char __user *) data, sizeof(op)) != 0)
395                         return -EFAULT;
396                 if (op.mt_count < 0)
397                         return -EINVAL;
398
399                 /*
400                  * Operations that change tape position should write final
401                  * tapemarks.
402                  */
403                 switch (op.mt_op) {
404                         case MTFSF:
405                         case MTBSF:
406                         case MTFSR:
407                         case MTBSR:
408                         case MTREW:
409                         case MTOFFL:
410                         case MTEOM:
411                         case MTRETEN:
412                         case MTBSFM:
413                         case MTFSFM:
414                         case MTSEEK:
415 #ifdef CONFIG_S390_TAPE_BLOCK
416                                 device->blk_data.medium_changed = 1;
417 #endif
418                                 if (device->required_tapemarks)
419                                         tape_std_terminate_write(device);
420                         default:
421                                 ;
422                 }
423                 rc = tape_mtop(device, op.mt_op, op.mt_count);
424
425                 if (op.mt_op == MTWEOF && rc == 0) {
426                         if (op.mt_count > device->required_tapemarks)
427                                 device->required_tapemarks = 0;
428                         else
429                                 device->required_tapemarks -= op.mt_count;
430                 }
431                 return rc;
432         }
433         if (no == MTIOCPOS) {
434                 /* MTIOCPOS: query the tape position. */
435                 struct mtpos pos;
436
437                 rc = tape_mtop(device, MTTELL, 1);
438                 if (rc < 0)
439                         return rc;
440                 pos.mt_blkno = rc;
441                 if (copy_to_user((char __user *) data, &pos, sizeof(pos)) != 0)
442                         return -EFAULT;
443                 return 0;
444         }
445         if (no == MTIOCGET) {
446                 /* MTIOCGET: query the tape drive status. */
447                 struct mtget get;
448
449                 memset(&get, 0, sizeof(get));
450                 get.mt_type = MT_ISUNKNOWN;
451                 get.mt_resid = 0 /* device->devstat.rescnt */;
452                 get.mt_dsreg = device->tape_state;
453                 /* FIXME: mt_gstat, mt_erreg, mt_fileno */
454                 get.mt_gstat = 0;
455                 get.mt_erreg = 0;
456                 get.mt_fileno = 0;
457                 get.mt_gstat  = device->tape_generic_status;
458
459                 if (device->medium_state == MS_LOADED) {
460                         rc = tape_mtop(device, MTTELL, 1);
461
462                         if (rc < 0)
463                                 return rc;
464
465                         if (rc == 0)
466                                 get.mt_gstat |= GMT_BOT(~0);
467
468                         get.mt_blkno = rc;
469                 }
470
471                 if (copy_to_user((char __user *) data, &get, sizeof(get)) != 0)
472                         return -EFAULT;
473
474                 return 0;
475         }
476         /* Try the discipline ioctl function. */
477         if (device->discipline->ioctl_fn == NULL)
478                 return -EINVAL;
479         return device->discipline->ioctl_fn(device, no, data);
480 }
481
482 /*
483  * Initialize character device frontend.
484  */
485 int
486 tapechar_init (void)
487 {
488         dev_t   dev;
489
490         if (alloc_chrdev_region(&dev, 0, 256, "tape") != 0)
491                 return -1;
492
493         tapechar_major = MAJOR(dev);
494         PRINT_INFO("tape gets major %d for character devices\n", MAJOR(dev));
495
496         return 0;
497 }
498
499 /*
500  * cleanup
501  */
502 void
503 tapechar_exit(void)
504 {
505         PRINT_INFO("tape releases major %d for character devices\n",
506                 tapechar_major);
507         unregister_chrdev_region(MKDEV(tapechar_major, 0), 256);
508 }