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