File Index
Files and directories
Sign in
FILE

tape_char.c

File preview with syntax highlighting.

linux-6.19.14/drivers/s390/char/tape_char.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 *    character device frontend for tape device driver
  4 *
  5 *  S390 and zSeries version
  6 *    Copyright IBM Corp. 2001, 2006
  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#define pr_fmt(fmt) "tape: " fmt
 14
 15#include <linux/module.h>
 16#include <linux/types.h>
 17#include <linux/proc_fs.h>
 18#include <linux/mtio.h>
 19
 20#include <linux/uaccess.h>
 21
 22#define TAPE_DBF_AREA	tape_core_dbf
 23
 24#include "tape.h"
 25#include "tape_std.h"
 26#include "tape_class.h"
 27
 28#define TAPECHAR_MAJOR		0	/* get dynamic major */
 29
 30/*
 31 * file operation structure for tape character frontend
 32 */
 33static ssize_t tapechar_read(struct file *, char __user *, size_t, loff_t *);
 34static ssize_t tapechar_write(struct file *, const char __user *, size_t, loff_t *);
 35static int tapechar_open(struct inode *,struct file *);
 36static int tapechar_release(struct inode *,struct file *);
 37static long tapechar_ioctl(struct file *, unsigned int, unsigned long);
 38
 39static const struct file_operations tape_fops =
 40{
 41	.owner = THIS_MODULE,
 42	.read = tapechar_read,
 43	.write = tapechar_write,
 44	.unlocked_ioctl = tapechar_ioctl,
 45	.open = tapechar_open,
 46	.release = tapechar_release,
 47};
 48
 49static int tapechar_major = TAPECHAR_MAJOR;
 50
 51/*
 52 * This function is called for every new tapedevice
 53 */
 54int
 55tapechar_setup_device(struct tape_device * device)
 56{
 57	char	device_name[20];
 58
 59	scnprintf(device_name, sizeof(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
 79void
 80tapechar_cleanup_device(struct tape_device *device)
 81{
 82	unregister_tape_dev(&device->cdev->dev, device->rt);
 83	device->rt = NULL;
 84	unregister_tape_dev(&device->cdev->dev, device->nt);
 85	device->nt = NULL;
 86}
 87
 88
 89/*
 90 * Tape device read function
 91 */
 92static ssize_t
 93tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
 94{
 95	struct tape_request *request;
 96	struct ccw1 *ccw, *last_ccw;
 97	struct tape_device *device;
 98	struct idal_buffer **ibs;
 99	size_t block_size;
100	size_t read = 0;
101	int rc;
102
103	DBF_EVENT(6, "TCHAR:read\n");
104	device = (struct tape_device *) filp->private_data;
105
106	/*
107	 * If the tape isn't terminated yet, do it now. And since we then
108	 * are at the end of the tape there wouldn't be anything to read
109	 * anyways. So we return immediately.
110	 */
111	if(device->required_tapemarks) {
112		return tape_std_terminate_write(device);
113	}
114
115	/* Find out block size to use */
116	if (device->char_data.block_size != 0) {
117		if (count < device->char_data.block_size) {
118			DBF_EVENT(3, "TCHAR:read smaller than block "
119				  "size was requested\n");
120			return -EINVAL;
121		}
122		block_size = device->char_data.block_size;
123	} else {
124		block_size = count;
125	}
126
127	rc = tape_check_idalbuffer(device, block_size);
128	if (rc)
129		return rc;
130
131	DBF_EVENT(6, "TCHAR:nbytes: %lx\n", block_size);
132	/* Let the discipline build the ccw chain. */
133	request = device->discipline->read_block(device);
134	if (IS_ERR(request))
135		return PTR_ERR(request);
136	/* Execute it. */
137	rc = tape_do_io(device, request);
138	if (rc == 0) {
139		DBF_EVENT(6, "TCHAR:rbytes:  %x\n", rc);
140		/* Channel Program Address (cpa) points to last CCW + 8 */
141		last_ccw = dma32_to_virt(request->irb.scsw.cmd.cpa);
142		ccw = request->cpaddr;
143		ibs = device->char_data.ibs;
144		while (++ccw < last_ccw) {
145			/* Copy data from idal buffer to user space. */
146			if (idal_buffer_to_user(*ibs++, data, ccw->count) != 0) {
147				rc = -EFAULT;
148				break;
149			}
150			read += ccw->count;
151			data += ccw->count;
152		}
153		if (&last_ccw[-1] == &request->cpaddr[1] &&
154		    request->rescnt == last_ccw[-1].count)
155			rc = 0;
156		else
157			rc = read - request->rescnt;
158	}
159	tape_free_request(request);
160	return rc;
161}
162
163/*
164 * Tape device write function
165 */
166static ssize_t
167tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t *ppos)
168{
169	struct tape_request *request;
170	struct ccw1 *ccw, *last_ccw;
171	struct tape_device *device;
172	struct idal_buffer **ibs;
173	size_t written = 0;
174	size_t block_size;
175	int nblocks;
176	int i, rc;
177
178	DBF_EVENT(6, "TCHAR:write\n");
179	device = (struct tape_device *) filp->private_data;
180	/* Find out block size and number of blocks */
181	if (device->char_data.block_size != 0) {
182		if (count < device->char_data.block_size) {
183			DBF_EVENT(3, "TCHAR:write smaller than block "
184				  "size was requested\n");
185			return -EINVAL;
186		}
187		block_size = device->char_data.block_size;
188		nblocks = count / block_size;
189	} else {
190		block_size = count;
191		nblocks = 1;
192	}
193
194	rc = tape_check_idalbuffer(device, block_size);
195	if (rc)
196		return rc;
197
198	DBF_EVENT(6, "TCHAR:nbytes: %lx\n", block_size);
199	DBF_EVENT(6, "TCHAR:nblocks: %x\n", nblocks);
200	/* Let the discipline build the ccw chain. */
201	request = device->discipline->write_block(device);
202	if (IS_ERR(request))
203		return PTR_ERR(request);
204
205	for (i = 0; i < nblocks; i++) {
206		size_t wbytes = 0; /* Used to trace written data in dbf */
207
208		ibs = device->char_data.ibs;
209		while (ibs && *ibs) {
210			if (idal_buffer_from_user(*ibs, data, (*ibs)->size)) {
211				rc = -EFAULT;
212				goto out;
213			}
214			data += (*ibs)->size;
215			ibs++;
216		}
217		rc = tape_do_io(device, request);
218		if (rc)
219			goto out;
220
221		/* Channel Program Address (cpa) points to last CCW + 8 */
222		last_ccw = dma32_to_virt(request->irb.scsw.cmd.cpa);
223		ccw = request->cpaddr;
224		while (++ccw < last_ccw)
225			wbytes += ccw->count;
226		DBF_EVENT(6, "TCHAR:wbytes: %lx\n", wbytes - request->rescnt);
227		written += wbytes - request->rescnt;
228		if (request->rescnt != 0)
229			break;
230	}
231
232out:
233	tape_free_request(request);
234	if (rc == -ENOSPC) {
235		/*
236		 * Ok, the device has no more space. It has NOT written
237		 * the block.
238		 */
239		if (device->discipline->process_eov)
240			device->discipline->process_eov(device);
241		if (written > 0)
242			rc = 0;
243
244	}
245
246	/*
247	 * After doing a write we always need two tapemarks to correctly
248	 * terminate the tape (one to terminate the file, the second to
249	 * flag the end of recorded data.
250	 * Since process_eov positions the tape in front of the written
251	 * tapemark it doesn't hurt to write two marks again.
252	 */
253	if (!rc)
254		device->required_tapemarks = 2;
255
256	return rc ? rc : written;
257}
258
259/*
260 * Character frontend tape device open function.
261 */
262static int
263tapechar_open (struct inode *inode, struct file *filp)
264{
265	struct tape_device *device;
266	int minor, rc;
267
268	DBF_EVENT(6, "TCHAR:open: %i:%i\n",
269		imajor(file_inode(filp)),
270		iminor(file_inode(filp)));
271
272	if (imajor(file_inode(filp)) != tapechar_major)
273		return -ENODEV;
274
275	minor = iminor(file_inode(filp));
276	device = tape_find_device(minor / TAPE_MINORS_PER_DEV);
277	if (IS_ERR(device)) {
278		DBF_EVENT(3, "TCHAR:open: tape_find_device() failed\n");
279		return PTR_ERR(device);
280	}
281
282	rc = tape_open(device);
283	if (rc == 0) {
284		filp->private_data = device;
285		stream_open(inode, filp);
286	} else
287		tape_put_device(device);
288
289	return rc;
290}
291
292/*
293 * Character frontend tape device release function.
294 */
295
296static int
297tapechar_release(struct inode *inode, struct file *filp)
298{
299	struct tape_device *device;
300
301	DBF_EVENT(6, "TCHAR:release: %x\n", iminor(inode));
302	device = (struct tape_device *) filp->private_data;
303
304	/*
305	 * If this is the rewinding tape minor then rewind. In that case we
306	 * write all required tapemarks. Otherwise only one to terminate the
307	 * file.
308	 */
309	if ((iminor(inode) & 1) != 0) {
310		if (device->required_tapemarks)
311			tape_std_terminate_write(device);
312		tape_mtop(device, MTREW, 1);
313	} else {
314		if (device->required_tapemarks > 1) {
315			if (tape_mtop(device, MTWEOF, 1) == 0)
316				device->required_tapemarks--;
317		}
318	}
319
320	if (device->char_data.ibs)
321		idal_buffer_array_free(&device->char_data.ibs);
322	tape_release(device);
323	filp->private_data = NULL;
324	tape_put_device(device);
325
326	return 0;
327}
328
329/*
330 * Tape device io controls.
331 */
332static int
333__tapechar_ioctl(struct tape_device *device,
334		 unsigned int no, void __user *data)
335{
336	int rc;
337
338	if (no == MTIOCTOP) {
339		struct mtop op;
340
341		if (copy_from_user(&op, data, sizeof(op)) != 0)
342			return -EFAULT;
343		if (op.mt_count < 0)
344			return -EINVAL;
345
346		/*
347		 * Operations that change tape position should write final
348		 * tapemarks.
349		 */
350		switch (op.mt_op) {
351			case MTFSF:
352			case MTBSF:
353			case MTFSR:
354			case MTBSR:
355			case MTREW:
356			case MTOFFL:
357			case MTEOM:
358			case MTRETEN:
359			case MTBSFM:
360			case MTFSFM:
361			case MTSEEK:
362				if (device->required_tapemarks)
363					tape_std_terminate_write(device);
364		}
365		rc = tape_mtop(device, op.mt_op, op.mt_count);
366
367		if (op.mt_op == MTWEOF && rc == 0) {
368			if (op.mt_count > device->required_tapemarks)
369				device->required_tapemarks = 0;
370			else
371				device->required_tapemarks -= op.mt_count;
372		}
373		return rc;
374	}
375	if (no == MTIOCPOS) {
376		/* MTIOCPOS: query the tape position. */
377		struct mtpos pos;
378
379		rc = tape_mtop(device, MTTELL, 1);
380		if (rc < 0)
381			return rc;
382		pos.mt_blkno = rc;
383		return put_user_mtpos(data, &pos);
384	}
385	if (no == MTIOCGET) {
386		/* MTIOCGET: query the tape drive status. */
387		struct mtget get;
388
389		memset(&get, 0, sizeof(get));
390		get.mt_type = MT_ISUNKNOWN;
391		get.mt_resid = 0 /* device->devstat.rescnt */;
392		get.mt_dsreg =
393			((device->char_data.block_size << MT_ST_BLKSIZE_SHIFT)
394			 & MT_ST_BLKSIZE_MASK);
395		/* FIXME: mt_gstat, mt_erreg, mt_fileno */
396		get.mt_gstat = 0;
397		get.mt_erreg = 0;
398		get.mt_fileno = 0;
399		get.mt_gstat  = device->tape_generic_status;
400
401		if (device->medium_state == MS_LOADED) {
402			rc = tape_mtop(device, MTTELL, 1);
403
404			if (rc < 0)
405				return rc;
406
407			if (rc == 0)
408				get.mt_gstat |= GMT_BOT(~0);
409
410			get.mt_blkno = rc;
411		}
412
413		return put_user_mtget(data, &get);
414	}
415	/* Try the discipline ioctl function. */
416	if (device->discipline->ioctl_fn == NULL)
417		return -EINVAL;
418	return device->discipline->ioctl_fn(device, no, (unsigned long)data);
419}
420
421static long
422tapechar_ioctl(struct file *filp, unsigned int no, unsigned long data)
423{
424	struct tape_device *device;
425	long rc;
426
427	DBF_EVENT(6, "TCHAR:ioct\n");
428
429	device = (struct tape_device *) filp->private_data;
430	mutex_lock(&device->mutex);
431	rc = __tapechar_ioctl(device, no, (void __user *)data);
432	mutex_unlock(&device->mutex);
433	return rc;
434}
435
436/*
437 * Initialize character device frontend.
438 */
439int
440tapechar_init (void)
441{
442	dev_t	dev;
443
444	if (alloc_chrdev_region(&dev, 0, 256, "tape") != 0)
445		return -1;
446
447	tapechar_major = MAJOR(dev);
448
449	return 0;
450}
451
452/*
453 * cleanup
454 */
455void
456tapechar_exit(void)
457{
458	unregister_chrdev_region(MKDEV(tapechar_major, 0), 256);
459}
~/ Read-only file preview.