1/*	$NetBSD: mscp_tape.c,v 1.44 2024/01/11 06:19:49 mrg Exp $ */
2/*
3 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed at Ludd, University of
17 *	Lule}, Sweden and its contributors.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33
34/*
35 * MSCP tape device driver
36 */
37
38/*
39 * TODO
40 *	Write status handling code.
41 */
42
43#include <sys/cdefs.h>
44__KERNEL_RCSID(0, "$NetBSD: mscp_tape.c,v 1.44 2024/01/11 06:19:49 mrg Exp $");
45
46#include <sys/param.h>
47#include <sys/device.h>
48#include <sys/kernel.h>
49#include <sys/buf.h>
50#include <sys/bufq.h>
51#include <sys/ioccom.h>
52#include <sys/mtio.h>
53#include <sys/fcntl.h>
54#include <sys/malloc.h>
55#include <sys/systm.h>
56#include <sys/proc.h>
57#include <sys/conf.h>
58
59#include <sys/bus.h>
60#include <sys/cpu.h>
61
62#include <dev/mscp/mscp.h>
63#include <dev/mscp/mscpreg.h>
64#include <dev/mscp/mscpvar.h>
65
66#include "locators.h"
67
68/*
69 * Drive status, per drive
70 */
71struct mt_softc {
72	device_t mt_dev;	/* Autoconf struct */
73	int	mt_state;	/* open/closed state */
74	int	mt_hwunit;	/* Hardware unit number */
75	int	mt_inuse;	/* Locks the tape drive for others */
76	int	mt_waswrite;	/* Last operation was a write op */
77	int	mt_serex;	/* Got serious exception */
78	int	mt_ioctlerr;	/* Error after last ioctl */
79};
80
81#define MT_OFFLINE	0
82#define MT_ONLINE	1
83
84int	mtmatch(device_t, cfdata_t, void *);
85void	mtattach(device_t, device_t, void *);
86void	mtdgram(device_t, struct mscp *, struct mscp_softc *);
87void	mtiodone(device_t, struct buf *);
88int	mtonline(device_t, struct mscp *);
89int	mtgotstatus(device_t, struct mscp *);
90int	mtioerror(device_t, struct mscp *, struct buf *);
91void	mtfillin(struct buf *, struct mscp *);
92int	mtcmd(struct mt_softc *, int, int, int);
93void	mtcmddone(device_t, struct mscp *);
94int	mt_putonline(struct mt_softc *);
95
96struct	mscp_device mt_device = {
97	mtdgram,
98	mtiodone,
99	mtonline,
100	NULL,
101	mtgotstatus,
102	0,
103	mtioerror,
104	0,
105	mtfillin,
106	mtcmddone,
107};
108
109/* This is not good, should allow more than 4 tapes/device type */
110#define mtunit(dev)	(minor(dev) & T_UNIT)
111#define mtnorewind(dev) (dev & T_NOREWIND)
112#define mthdensity(dev) (dev & T_1600BPI)
113
114CFATTACH_DECL_NEW(mt, sizeof(struct mt_softc),
115    mtmatch, mtattach, NULL, NULL);
116
117extern struct cfdriver mt_cd;
118
119dev_type_open(mtopen);
120dev_type_close(mtclose);
121dev_type_read(mtread);
122dev_type_write(mtwrite);
123dev_type_ioctl(mtioctl);
124dev_type_strategy(mtstrategy);
125dev_type_dump(mtdump);
126
127const struct bdevsw mt_bdevsw = {
128	.d_open = mtopen,
129	.d_close = mtclose,
130	.d_strategy = mtstrategy,
131	.d_ioctl = mtioctl,
132	.d_dump = mtdump,
133	.d_psize = nosize,
134	.d_discard = nodiscard,
135	.d_flag = D_TAPE
136};
137
138const struct cdevsw mt_cdevsw = {
139	.d_open = mtopen,
140	.d_close = mtclose,
141	.d_read = mtread,
142	.d_write = mtwrite,
143	.d_ioctl = mtioctl,
144	.d_stop = nostop,
145	.d_tty = notty,
146	.d_poll = nopoll,
147	.d_mmap = nommap,
148	.d_kqfilter = nokqfilter,
149	.d_discard = nodiscard,
150	.d_flag = D_TAPE
151};
152
153/*
154 * More driver definitions, for generic MSCP code.
155 */
156
157int
158mtmatch(device_t parent, cfdata_t cf, void *aux)
159{
160	struct	drive_attach_args *da = aux;
161	struct	mscp *mp = da->da_mp;
162
163	if ((da->da_typ & MSCPBUS_TAPE) == 0)
164		return 0;
165	if (cf->cf_loc[MSCPBUSCF_DRIVE] != MSCPBUSCF_DRIVE_DEFAULT &&
166	    cf->cf_loc[MSCPBUSCF_DRIVE] != mp->mscp_unit)
167		return 0;
168	return 1;
169}
170
171/*
172 * The attach routine only checks and prints drive type.
173 */
174void
175mtattach(device_t parent, device_t self, void *aux)
176{
177	struct	mt_softc *mt = device_private(self);
178	struct	drive_attach_args *da = aux;
179	struct	mscp *mp = da->da_mp;
180	struct	mscp_softc *mi = device_private(parent);
181
182	mt->mt_dev = self;
183	mt->mt_hwunit = mp->mscp_unit;
184	mi->mi_dp[mp->mscp_unit] = self;
185
186	disk_printtype(mp->mscp_unit, mp->mscp_guse.guse_mediaid);
187}
188
189/*
190 * (Try to) put the drive online. This is done the first time the
191 * drive is opened, or if it has fallen offline.
192 */
193int
194mt_putonline(struct mt_softc *mt)
195{
196	struct	mscp *mp;
197	struct	mscp_softc *mi =
198	    device_private(device_parent(mt->mt_dev));
199
200	((volatile struct mt_softc *) mt)->mt_state = MT_OFFLINE;
201	mp = mscp_getcp(mi, MSCP_WAIT);
202	mp->mscp_opcode = M_OP_ONLINE;
203	mp->mscp_unit = mt->mt_hwunit;
204	mp->mscp_cmdref = (long)&mt->mt_state;
205	*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
206
207	/* Poll away */
208	bus_space_read_2(mi->mi_iot, mi->mi_iph, 0);
209	if (tsleep(&mt->mt_state, PRIBIO, "mtonline", 240 * hz))
210		return MSCP_FAILED;
211
212	if ((volatile int)mt->mt_state != MT_ONLINE)
213		return MSCP_FAILED;
214
215	return MSCP_DONE;
216}
217/*
218 * Open a drive.
219 */
220/*ARGSUSED*/
221int
222mtopen(dev_t dev, int flag, int fmt, struct lwp *l)
223{
224	struct mt_softc *mt;
225	int unit;
226
227	/*
228	 * Make sure this is a reasonable open request.
229	 */
230	unit = mtunit(dev);
231	mt = device_lookup_private(&mt_cd, unit);
232	if (!mt)
233		return ENXIO;
234
235	if (mt->mt_inuse)
236			return EBUSY;
237	mt->mt_inuse = 1;
238
239	if (mt_putonline(mt) == MSCP_FAILED) {
240		mt->mt_inuse = 0;
241		return EIO;
242	}
243
244	return 0;
245}
246
247/* ARGSUSED */
248int
249mtclose(dev_t dev, int flags, int fmt, struct lwp *l)
250{
251	int unit = mtunit(dev);
252	struct mt_softc *mt = device_lookup_private(&mt_cd, unit);
253
254	/*
255	 * If we just have finished a writing, write EOT marks.
256	 */
257	if ((flags & FWRITE) && mt->mt_waswrite) {
258		mtcmd(mt, MTWEOF, 0, 0);
259		mtcmd(mt, MTWEOF, 0, 0);
260		mtcmd(mt, MTBSR, 1, 0);
261	}
262	if (mtnorewind(dev) == 0)
263		mtcmd(mt, MTREW, 0, 1);
264	if (mt->mt_serex)
265		mtcmd(mt, -1, 0, 0);
266
267	mt->mt_inuse = 0; /* Release the tape */
268	return 0;
269}
270
271void
272mtstrategy(struct buf *bp)
273{
274	int unit;
275	struct mt_softc *mt;
276
277	/*
278	 * Make sure this is a reasonable drive to use.
279	 */
280	unit = mtunit(bp->b_dev);
281	if ((mt = device_lookup_private(&mt_cd, unit)) == NULL) {
282		bp->b_error = ENXIO;
283		biodone(bp);
284		return;
285	}
286
287	mt->mt_waswrite = bp->b_flags & B_READ ? 0 : 1;
288	mscp_strategy(bp, device_parent(mt->mt_dev));
289	return;
290}
291
292int
293mtread(dev_t dev, struct uio *uio, int flag)
294{
295
296	return (physio(mtstrategy, NULL, dev, B_READ, minphys, uio));
297}
298
299int
300mtwrite(dev_t dev, struct uio *uio, int flag)
301{
302
303	return (physio(mtstrategy, NULL, dev, B_WRITE, minphys, uio));
304}
305
306void
307mtiodone(device_t usc, struct buf *bp)
308{
309
310	biodone(bp);
311}
312
313/*
314 * Fill in drive addresses in a mscp packet waiting for transfer.
315 */
316void
317mtfillin(struct buf *bp, struct mscp *mp)
318{
319	int unit = mtunit(bp->b_dev);
320	struct mt_softc *mt = device_lookup_private(&mt_cd, unit);
321
322	mp->mscp_unit = mt->mt_hwunit;
323	if (mt->mt_serex == 2) {
324		mp->mscp_modifier = M_MD_CLSEX;
325		mt->mt_serex = 0;
326	} else
327		mp->mscp_modifier = 0;
328
329	mp->mscp_seq.seq_bytecount = bp->b_bcount;
330}
331
332/*
333 * Handle an error datagram.
334 */
335void
336mtdgram(device_t usc, struct mscp *mp, struct mscp_softc *mi)
337{
338	if (mscp_decodeerror(usc == NULL?"unconf mt" : device_xname(usc), mp, mi))
339		return;
340}
341
342/*
343 * A drive came on line, make sure it really _is_ on line before
344 * trying to use it.
345 */
346int
347mtonline(device_t usc, struct mscp *mp)
348{
349	struct mt_softc *mt = (void *)usc;
350
351	wakeup((void *)&mt->mt_state);
352	if ((mp->mscp_status & M_ST_MASK) == M_ST_SUCCESS)
353		mt->mt_state = MT_ONLINE;
354
355	return (MSCP_DONE);
356}
357
358/*
359 * We got some (configured) unit's status.  Return DONE.
360 */
361int
362mtgotstatus(device_t usc, struct mscp *mp)
363{
364	return (MSCP_DONE);
365}
366
367static const char *mt_ioerrs[] = {
368	"invalid command",	/* 1 M_ST_INVALCMD */
369	"command aborted",	/* 2 M_ST_ABORTED */
370	"unit offline",		/* 3 M_ST_OFFLINE */
371	"unknown",		/* 4 M_ST_AVAILABLE */
372	"unknown",		/* 5 M_ST_MFMTERR */
373	"unit write protected", /* 6 M_ST_WRPROT */
374	"compare error",	/* 7 M_ST_COMPERR */
375	"data error",		/* 8 M_ST_DATAERR */
376	"host buffer access error",	/* 9 M_ST_HOSTBUFERR */
377	"controller error",	/* 10 M_ST_CTLRERR */
378	"drive error",		/* 11 M_ST_DRIVEERR */
379	"formatter error",	/* 12 M_ST_FORMATTERR */
380	"BOT encountered",	/* 13 M_ST_BOT */
381	"tape mark encountered",/* 14 M_ST_TAPEMARK */
382	"unknown",		/* 15 */
383	"record data truncated",/* 16 M_ST_RDTRUNC */
384};
385
386/*
387 * An I/O error, may be because of a tapemark encountered.
388 * Check that before failing.
389 */
390/*ARGSUSED*/
391int
392mtioerror(device_t usc, struct mscp *mp, struct buf *bp)
393{
394	struct mt_softc *mt = (void *)usc;
395	int st = mp->mscp_status & M_ST_MASK;
396
397	if (mp->mscp_flags & M_EF_SEREX)
398		mt->mt_serex = 1;
399	if (st == M_ST_TAPEMARK)
400		mt->mt_serex = 2;
401	else {
402		if (st && st < 17)
403			printf("%s: error %d (%s)\n", device_xname(mt->mt_dev), st,
404			    mt_ioerrs[st-1]);
405		else
406			printf("%s: error %d\n", device_xname(mt->mt_dev), st);
407		bp->b_error = EROFS;
408	}
409
410	return (MSCP_DONE);
411}
412
413/*
414 * I/O controls.
415 */
416int
417mtioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
418{
419	int unit = mtunit(dev);
420	struct mt_softc *mt = device_lookup_private(&mt_cd, unit);
421	struct mtop *mtop;
422	int error = 0;
423
424	switch (cmd) {
425
426	case MTIOCTOP:
427		mtop = (void *)data;
428		if (mtop->mt_op == MTWEOF) {
429			while (mtop->mt_count-- > 0)
430				if ((error = mtcmd(mt, mtop->mt_op, 0, 0)))
431					break;
432		} else
433			error = mtcmd(mt, mtop->mt_op, mtop->mt_count, 0);
434
435	case MTIOCGET:
436		((struct mtget *)data)->mt_type = MT_ISTMSCP;
437		/* XXX we need to fill in more fields here */
438		break;
439
440	default:
441		error = ENXIO;
442		break;
443	}
444	return (error);
445}
446
447/*
448 * No crash dump support...
449 */
450int
451mtdump(dev_t dev, daddr_t blkno, void *va, size_t size)
452{
453	return -1;
454}
455
456/*
457 * Send a command to the tape drive. Wait until the command is
458 * finished before returning.
459 * This routine must only be called when there are no data transfer
460 * active on this device. Can we be sure of this? Or does the ctlr
461 * queue up all command packets and take them in sequential order?
462 * It sure would be nice if my manual stated this... /ragge
463 */
464int
465mtcmd(struct mt_softc *mt, int cmd, int count, int complete)
466{
467	struct mscp *mp;
468	struct mscp_softc *mi = device_private(device_parent(mt->mt_dev));
469
470	mp = mscp_getcp(mi, MSCP_WAIT);
471
472	mt->mt_ioctlerr = 0;
473	mp->mscp_unit = mt->mt_hwunit;
474	mp->mscp_cmdref = -1;
475	*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
476
477	switch (cmd) {
478	case MTWEOF:
479		mp->mscp_opcode = M_OP_WRITM;
480		break;
481
482	case MTBSF:
483		mp->mscp_modifier = M_MD_REVERSE;
484	case MTFSF:
485		mp->mscp_opcode = M_OP_POS;
486		mp->mscp_seq.seq_buffer = count;
487		break;
488
489	case MTBSR:
490		mp->mscp_modifier = M_MD_REVERSE;
491	case MTFSR:
492		mp->mscp_opcode = M_OP_POS;
493		mp->mscp_modifier |= M_MD_OBJCOUNT;
494		mp->mscp_seq.seq_bytecount = count;
495		break;
496
497	case MTREW:
498		mp->mscp_opcode = M_OP_POS;
499		mp->mscp_modifier = M_MD_REWIND | M_MD_CLSEX;
500		if (complete)
501			mp->mscp_modifier |= M_MD_IMMEDIATE;
502		mt->mt_serex = 0;
503		break;
504
505	case MTOFFL:
506		mp->mscp_opcode = M_OP_AVAILABLE;
507		mp->mscp_modifier = M_MD_UNLOAD | M_MD_CLSEX;
508		mt->mt_serex = 0;
509		break;
510
511	case MTNOP:
512		mp->mscp_opcode = M_OP_GETUNITST;
513		break;
514
515	case -1: /* Clear serious exception only */
516		mp->mscp_opcode = M_OP_POS;
517		mp->mscp_modifier = M_MD_CLSEX;
518		mt->mt_serex = 0;
519		break;
520
521	default:
522		printf("Bad ioctl %x\n", cmd);
523		mp->mscp_opcode = M_OP_POS;
524		break;
525	}
526
527	bus_space_read_2(mi->mi_iot, mi->mi_iph, 0);
528	tsleep(&mt->mt_inuse, PRIBIO, "mtioctl", 0);
529	return mt->mt_ioctlerr;
530}
531
532/*
533 * Called from bus routines whenever a non-data transfer is finished.
534 */
535void
536mtcmddone(device_t usc, struct mscp *mp)
537{
538	struct mt_softc *mt = (void *)usc;
539
540	if (mp->mscp_status) {
541		mt->mt_ioctlerr = EIO;
542		printf("%s: bad status %x\n", device_xname(mt->mt_dev),
543		    mp->mscp_status);
544	}
545	wakeup(&mt->mt_inuse);
546}
547