xref: /dflybsd-src/sys/dev/disk/nata/atapi-tape.c (revision 2458a87a718a37261418254ad47f5f9adc3366d4)
1c1b3d7c5SThomas E. Spanjaard /*-
2*2458a87aSzrj  * Copyright (c) 1998 - 2008 Søren Schmidt <sos@FreeBSD.org>
3c1b3d7c5SThomas E. Spanjaard  * All rights reserved.
4c1b3d7c5SThomas E. Spanjaard  *
5c1b3d7c5SThomas E. Spanjaard  * Redistribution and use in source and binary forms, with or without
6c1b3d7c5SThomas E. Spanjaard  * modification, are permitted provided that the following conditions
7c1b3d7c5SThomas E. Spanjaard  * are met:
8c1b3d7c5SThomas E. Spanjaard  * 1. Redistributions of source code must retain the above copyright
9c1b3d7c5SThomas E. Spanjaard  *    notice, this list of conditions and the following disclaimer,
10c1b3d7c5SThomas E. Spanjaard  *    without modification, immediately at the beginning of the file.
11c1b3d7c5SThomas E. Spanjaard  * 2. Redistributions in binary form must reproduce the above copyright
12c1b3d7c5SThomas E. Spanjaard  *    notice, this list of conditions and the following disclaimer in the
13c1b3d7c5SThomas E. Spanjaard  *    documentation and/or other materials provided with the distribution.
14c1b3d7c5SThomas E. Spanjaard  *
15c1b3d7c5SThomas E. Spanjaard  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16c1b3d7c5SThomas E. Spanjaard  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17c1b3d7c5SThomas E. Spanjaard  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18c1b3d7c5SThomas E. Spanjaard  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19c1b3d7c5SThomas E. Spanjaard  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20c1b3d7c5SThomas E. Spanjaard  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21c1b3d7c5SThomas E. Spanjaard  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22c1b3d7c5SThomas E. Spanjaard  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23c1b3d7c5SThomas E. Spanjaard  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24c1b3d7c5SThomas E. Spanjaard  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25c1b3d7c5SThomas E. Spanjaard  *
26c1b3d7c5SThomas E. Spanjaard  * $FreeBSD: src/sys/dev/ata/atapi-tape.c,v 1.101 2006/01/05 21:27:19 sos Exp $
27c1b3d7c5SThomas E. Spanjaard  */
28c1b3d7c5SThomas E. Spanjaard 
29c1b3d7c5SThomas E. Spanjaard #include "opt_ata.h"
30c1b3d7c5SThomas E. Spanjaard 
31c1b3d7c5SThomas E. Spanjaard #include <sys/param.h>
32c1b3d7c5SThomas E. Spanjaard #include <sys/bio.h>
33c1b3d7c5SThomas E. Spanjaard #include <sys/buf.h>
34c1b3d7c5SThomas E. Spanjaard #include <sys/bus.h>
35c1b3d7c5SThomas E. Spanjaard #include <sys/conf.h>
36c1b3d7c5SThomas E. Spanjaard #include <sys/device.h>
37c1b3d7c5SThomas E. Spanjaard #include <sys/devicestat.h>
38c1b3d7c5SThomas E. Spanjaard #include <sys/disk.h>
39c1b3d7c5SThomas E. Spanjaard #include <sys/kernel.h>
40c1b3d7c5SThomas E. Spanjaard #include <sys/libkern.h>
41c1b3d7c5SThomas E. Spanjaard #include <sys/malloc.h>
42c1b3d7c5SThomas E. Spanjaard #include <sys/module.h>
43c1b3d7c5SThomas E. Spanjaard #include <sys/mtio.h>
44c1b3d7c5SThomas E. Spanjaard #include <sys/nata.h>
45c1b3d7c5SThomas E. Spanjaard #include <sys/systm.h>
46c1b3d7c5SThomas E. Spanjaard 
47c1b3d7c5SThomas E. Spanjaard #include "ata-all.h"
48c1b3d7c5SThomas E. Spanjaard #include "atapi-tape.h"
49c1b3d7c5SThomas E. Spanjaard #include "ata_if.h"
50c1b3d7c5SThomas E. Spanjaard 
51bb15467aSzrj /* local implementation, to trigger a warning */
52bb15467aSzrj static inline void
biofinish(struct bio * bp,struct bio * x __unused,int error)53bb15467aSzrj biofinish(struct bio *bp, struct bio *x __unused, int error)
54bb15467aSzrj {
55bb15467aSzrj 	struct buf *bbp = bp->bio_buf;
56bb15467aSzrj 
57bb15467aSzrj 	bbp->b_flags |= B_ERROR;
58bb15467aSzrj 	bbp->b_error = error;
59bb15467aSzrj 	biodone(bp);
60bb15467aSzrj }
61bb15467aSzrj 
62c1b3d7c5SThomas E. Spanjaard /* device structure */
63c1b3d7c5SThomas E. Spanjaard static  d_open_t        ast_open;
64c1b3d7c5SThomas E. Spanjaard static  d_close_t       ast_close;
65c1b3d7c5SThomas E. Spanjaard static  d_ioctl_t       ast_ioctl;
66c1b3d7c5SThomas E. Spanjaard static  d_strategy_t    ast_strategy;
67c1b3d7c5SThomas E. Spanjaard static struct dev_ops ast_ops = {
68c1b3d7c5SThomas E. Spanjaard 	{ "ast", 119, D_TAPE | D_TRACKCLOSE },
69c1b3d7c5SThomas E. Spanjaard 	.d_open =       ast_open,
70c1b3d7c5SThomas E. Spanjaard 	.d_close =      ast_close,
71c1b3d7c5SThomas E. Spanjaard 	.d_read =       physread,
72c1b3d7c5SThomas E. Spanjaard 	.d_write =      physwrite,
73c1b3d7c5SThomas E. Spanjaard 	.d_ioctl =      ast_ioctl,
74c1b3d7c5SThomas E. Spanjaard 	.d_strategy =   ast_strategy
75c1b3d7c5SThomas E. Spanjaard };
76c1b3d7c5SThomas E. Spanjaard 
77c1b3d7c5SThomas E. Spanjaard /* prototypes */
78c1b3d7c5SThomas E. Spanjaard static int ast_sense(device_t);
79c1b3d7c5SThomas E. Spanjaard static void ast_describe(device_t);
80c1b3d7c5SThomas E. Spanjaard static void ast_done(struct ata_request *);
81c1b3d7c5SThomas E. Spanjaard static int ast_mode_sense(device_t, int, void *, int);
82c1b3d7c5SThomas E. Spanjaard static int ast_mode_select(device_t, void *, int);
83c1b3d7c5SThomas E. Spanjaard static int ast_write_filemark(device_t, u_int8_t);
84c1b3d7c5SThomas E. Spanjaard static int ast_read_position(device_t, int, struct ast_readposition *);
85c1b3d7c5SThomas E. Spanjaard static int ast_space(device_t, u_int8_t, int32_t);
86c1b3d7c5SThomas E. Spanjaard static int ast_locate(device_t, int, u_int32_t);
87c1b3d7c5SThomas E. Spanjaard static int ast_prevent_allow(device_t, int);
88c1b3d7c5SThomas E. Spanjaard static int ast_load_unload(device_t, u_int8_t);
89c1b3d7c5SThomas E. Spanjaard static int ast_rewind(device_t);
90c1b3d7c5SThomas E. Spanjaard static int ast_erase(device_t);
91c1b3d7c5SThomas E. Spanjaard static int ast_test_ready(device_t);
92c1b3d7c5SThomas E. Spanjaard static int ast_wait_dsc(device_t, int);
93c1b3d7c5SThomas E. Spanjaard 
94c1b3d7c5SThomas E. Spanjaard /* internal vars */
95c1b3d7c5SThomas E. Spanjaard static u_int64_t ast_total = 0;
96c1b3d7c5SThomas E. Spanjaard static MALLOC_DEFINE(M_AST, "ast_driver", "ATAPI tape driver buffers");
97c1b3d7c5SThomas E. Spanjaard 
98c1b3d7c5SThomas E. Spanjaard static int
ast_probe(device_t dev)99c1b3d7c5SThomas E. Spanjaard ast_probe(device_t dev)
100c1b3d7c5SThomas E. Spanjaard {
101c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
102c1b3d7c5SThomas E. Spanjaard 
103c1b3d7c5SThomas E. Spanjaard     if ((atadev->param.config & ATA_PROTO_ATAPI) &&
104c1b3d7c5SThomas E. Spanjaard 	(atadev->param.config & ATA_ATAPI_TYPE_MASK) == ATA_ATAPI_TYPE_TAPE)
105c1b3d7c5SThomas E. Spanjaard 	return 0;
106c1b3d7c5SThomas E. Spanjaard     else
107c1b3d7c5SThomas E. Spanjaard 	return ENXIO;
108c1b3d7c5SThomas E. Spanjaard }
109c1b3d7c5SThomas E. Spanjaard 
110c1b3d7c5SThomas E. Spanjaard static int
ast_attach(device_t dev)111c1b3d7c5SThomas E. Spanjaard ast_attach(device_t dev)
112c1b3d7c5SThomas E. Spanjaard {
113c1b3d7c5SThomas E. Spanjaard     struct ata_channel *ch = device_get_softc(device_get_parent(dev));
114c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
115c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp;
116c1b3d7c5SThomas E. Spanjaard     struct ast_readposition position;
117c1b3d7c5SThomas E. Spanjaard     cdev_t cdev;
118c1b3d7c5SThomas E. Spanjaard 
119a01741bbSMatthew Dillon     stp = kmalloc(sizeof(struct ast_softc), M_AST, M_WAITOK | M_ZERO);
120c1b3d7c5SThomas E. Spanjaard     device_set_ivars(dev, stp);
121c1b3d7c5SThomas E. Spanjaard     ATA_SETMODE(device_get_parent(dev), dev);
122c1b3d7c5SThomas E. Spanjaard 
123c1b3d7c5SThomas E. Spanjaard     if (ast_sense(dev)) {
124c1b3d7c5SThomas E. Spanjaard 	device_set_ivars(dev, NULL);
125c1b3d7c5SThomas E. Spanjaard 	kfree(stp, M_AST);
126c1b3d7c5SThomas E. Spanjaard 	return ENXIO;
127c1b3d7c5SThomas E. Spanjaard     }
128c1b3d7c5SThomas E. Spanjaard     if (!strcmp(atadev->param.model, "OnStream DI-30")) {
129c1b3d7c5SThomas E. Spanjaard 	struct ast_transferpage transfer;
130c1b3d7c5SThomas E. Spanjaard 	struct ast_identifypage identify;
131c1b3d7c5SThomas E. Spanjaard 
132c1b3d7c5SThomas E. Spanjaard 	stp->flags |= F_ONSTREAM;
133c1b3d7c5SThomas E. Spanjaard 	bzero(&transfer, sizeof(struct ast_transferpage));
134c1b3d7c5SThomas E. Spanjaard 	ast_mode_sense(dev, ATAPI_TAPE_TRANSFER_PAGE,
135c1b3d7c5SThomas E. Spanjaard 		       &transfer, sizeof(transfer));
136c1b3d7c5SThomas E. Spanjaard 	bzero(&identify, sizeof(struct ast_identifypage));
137c1b3d7c5SThomas E. Spanjaard 	ast_mode_sense(dev, ATAPI_TAPE_IDENTIFY_PAGE,
138c1b3d7c5SThomas E. Spanjaard 		       &identify, sizeof(identify));
139c1b3d7c5SThomas E. Spanjaard 	strncpy(identify.ident, "FBSD", 4);
140c1b3d7c5SThomas E. Spanjaard 	ast_mode_select(dev, &identify, sizeof(identify));
141c1b3d7c5SThomas E. Spanjaard 	ast_read_position(dev, 0, &position);
142c1b3d7c5SThomas E. Spanjaard     }
143c1b3d7c5SThomas E. Spanjaard 
144c1b3d7c5SThomas E. Spanjaard     devstat_add_entry(&stp->stats, "ast", device_get_unit(dev), DEV_BSIZE,
145c1b3d7c5SThomas E. Spanjaard 		      DEVSTAT_NO_ORDERED_TAGS,
146c1b3d7c5SThomas E. Spanjaard 		      DEVSTAT_TYPE_SEQUENTIAL | DEVSTAT_TYPE_IF_IDE,
147c1b3d7c5SThomas E. Spanjaard 		      DEVSTAT_PRIORITY_TAPE);
148c1b3d7c5SThomas E. Spanjaard     cdev = make_dev(&ast_ops, 2 * device_get_unit(dev), UID_ROOT, GID_OPERATOR,
149c1b3d7c5SThomas E. Spanjaard 		    0640, "ast%d", device_get_unit(dev));
150c1b3d7c5SThomas E. Spanjaard     reference_dev(cdev);
151c1b3d7c5SThomas E. Spanjaard     cdev->si_drv1 = dev;
152c1b3d7c5SThomas E. Spanjaard     if (ch->dma)
153c1b3d7c5SThomas E. Spanjaard 	cdev->si_iosize_max = ch->dma->max_iosize;
154c1b3d7c5SThomas E. Spanjaard     else
155d83666e0SFrançois Tigeot 	cdev->si_iosize_max = min(MAXPHYS,64*1024);
156c1b3d7c5SThomas E. Spanjaard     stp->cdev1 = cdev;
157c1b3d7c5SThomas E. Spanjaard     cdev = make_dev(&ast_ops, 2 * device_get_unit(dev) + 1, UID_ROOT,
158c1b3d7c5SThomas E. Spanjaard 		    GID_OPERATOR, 0640, "nast%d", device_get_unit(dev));
159c1b3d7c5SThomas E. Spanjaard     reference_dev(cdev);
160c1b3d7c5SThomas E. Spanjaard     cdev->si_drv1 = dev;
161c1b3d7c5SThomas E. Spanjaard     if (ch->dma)
162c1b3d7c5SThomas E. Spanjaard 	cdev->si_iosize_max = ch->dma->max_iosize;
163c1b3d7c5SThomas E. Spanjaard     else
164d83666e0SFrançois Tigeot 	cdev->si_iosize_max = min(MAXPHYS,64*1024);
165c1b3d7c5SThomas E. Spanjaard     stp->cdev2 = cdev;
166c1b3d7c5SThomas E. Spanjaard 
167c1b3d7c5SThomas E. Spanjaard     /* announce we are here and ready */
168c1b3d7c5SThomas E. Spanjaard     ast_describe(dev);
169c1b3d7c5SThomas E. Spanjaard     return 0;
170c1b3d7c5SThomas E. Spanjaard }
171c1b3d7c5SThomas E. Spanjaard 
172c1b3d7c5SThomas E. Spanjaard static int
ast_detach(device_t dev)173c1b3d7c5SThomas E. Spanjaard ast_detach(device_t dev)
174c1b3d7c5SThomas E. Spanjaard {
175c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp = device_get_ivars(dev);
176c1b3d7c5SThomas E. Spanjaard 
177c1b3d7c5SThomas E. Spanjaard     /* detroy devices from the system so we dont get any further requests */
178c1b3d7c5SThomas E. Spanjaard     destroy_dev(stp->cdev1);
179c1b3d7c5SThomas E. Spanjaard     destroy_dev(stp->cdev2);
180c1b3d7c5SThomas E. Spanjaard 
181c1b3d7c5SThomas E. Spanjaard     /* fail requests on the queue and any thats "in flight" for this device */
182c1b3d7c5SThomas E. Spanjaard     ata_fail_requests(dev);
183c1b3d7c5SThomas E. Spanjaard 
184c1b3d7c5SThomas E. Spanjaard     /* dont leave anything behind */
185cd29885aSMatthew Dillon     dev_ops_remove_minor(&ast_ops, /*dkunitmask(), */dkmakeunit(device_get_unit(dev)));
186c1b3d7c5SThomas E. Spanjaard     devstat_remove_entry(&stp->stats);
187c1b3d7c5SThomas E. Spanjaard     device_set_ivars(dev, NULL);
188c1b3d7c5SThomas E. Spanjaard     kfree(stp, M_AST);
189c1b3d7c5SThomas E. Spanjaard     return 0;
190c1b3d7c5SThomas E. Spanjaard }
191c1b3d7c5SThomas E. Spanjaard 
192c1b3d7c5SThomas E. Spanjaard static void
ast_shutdown(device_t dev)193c1b3d7c5SThomas E. Spanjaard ast_shutdown(device_t dev)
194c1b3d7c5SThomas E. Spanjaard {
195c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
196c1b3d7c5SThomas E. Spanjaard 
197c1b3d7c5SThomas E. Spanjaard     if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE)
198c1b3d7c5SThomas E. Spanjaard 	ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0);
199c1b3d7c5SThomas E. Spanjaard }
200c1b3d7c5SThomas E. Spanjaard 
201c1b3d7c5SThomas E. Spanjaard static int
ast_reinit(device_t dev)202c1b3d7c5SThomas E. Spanjaard ast_reinit(device_t dev)
203c1b3d7c5SThomas E. Spanjaard {
204c1b3d7c5SThomas E. Spanjaard     struct ata_channel *ch = device_get_softc(device_get_parent(dev));
205c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
206c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp = device_get_ivars(dev);
207c1b3d7c5SThomas E. Spanjaard 
208c1b3d7c5SThomas E. Spanjaard     if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATAPI_MASTER)) ||
209c1b3d7c5SThomas E. Spanjaard 	((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATAPI_SLAVE))) {
210c1b3d7c5SThomas E. Spanjaard 	device_set_ivars(dev, NULL);
211c1b3d7c5SThomas E. Spanjaard 	kfree(stp, M_AST);
212c1b3d7c5SThomas E. Spanjaard 	return 1;
213c1b3d7c5SThomas E. Spanjaard     }
214c1b3d7c5SThomas E. Spanjaard     ATA_SETMODE(device_get_parent(dev), dev);
215c1b3d7c5SThomas E. Spanjaard     return 0;
216c1b3d7c5SThomas E. Spanjaard }
217c1b3d7c5SThomas E. Spanjaard 
218c1b3d7c5SThomas E. Spanjaard static int
ast_open(struct dev_open_args * ap)219c1b3d7c5SThomas E. Spanjaard ast_open(struct dev_open_args *ap)
220c1b3d7c5SThomas E. Spanjaard {
221c1b3d7c5SThomas E. Spanjaard     device_t dev = ap->a_head.a_dev->si_drv1;
222c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
223c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp = device_get_ivars(dev);
224c1b3d7c5SThomas E. Spanjaard 
225c1b3d7c5SThomas E. Spanjaard     if (!stp)
226c1b3d7c5SThomas E. Spanjaard 	return ENXIO;
227c1b3d7c5SThomas E. Spanjaard     if (!device_is_attached(dev))
228c1b3d7c5SThomas E. Spanjaard 	return EBUSY;
229c1b3d7c5SThomas E. Spanjaard 
230c1b3d7c5SThomas E. Spanjaard     ast_test_ready(dev);
231c1b3d7c5SThomas E. Spanjaard     if (stp->cap.lock)
232c1b3d7c5SThomas E. Spanjaard 	ast_prevent_allow(dev, 1);
233c1b3d7c5SThomas E. Spanjaard     if (ast_sense(dev))
234c1b3d7c5SThomas E. Spanjaard 	device_printf(dev, "sense media type failed\n");
235c1b3d7c5SThomas E. Spanjaard 
236c1b3d7c5SThomas E. Spanjaard     atadev->flags &= ~ATA_D_MEDIA_CHANGED;
237c1b3d7c5SThomas E. Spanjaard     stp->flags &= ~(F_DATA_WRITTEN | F_FM_WRITTEN);
238c1b3d7c5SThomas E. Spanjaard     ast_total = 0;
239c1b3d7c5SThomas E. Spanjaard     return 0;
240c1b3d7c5SThomas E. Spanjaard }
241c1b3d7c5SThomas E. Spanjaard 
242c1b3d7c5SThomas E. Spanjaard static int
ast_close(struct dev_close_args * ap)243c1b3d7c5SThomas E. Spanjaard ast_close(struct dev_close_args *ap)
244c1b3d7c5SThomas E. Spanjaard {
245c1b3d7c5SThomas E. Spanjaard     device_t dev = ap->a_head.a_dev->si_drv1;
246c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp = device_get_ivars(dev);
247c1b3d7c5SThomas E. Spanjaard     cdev_t cdev = ap->a_head.a_dev;
248c1b3d7c5SThomas E. Spanjaard 
249c1b3d7c5SThomas E. Spanjaard     /* flush buffers, some drives fail here, they should report ctl = 0 */
250c1b3d7c5SThomas E. Spanjaard     if (stp->cap.ctl && (stp->flags & F_DATA_WRITTEN))
251c1b3d7c5SThomas E. Spanjaard 	ast_write_filemark(dev, 0);
252c1b3d7c5SThomas E. Spanjaard 
253c1b3d7c5SThomas E. Spanjaard     /* write filemark if data written to tape */
254c1b3d7c5SThomas E. Spanjaard     if (!(stp->flags & F_ONSTREAM) &&
255c1b3d7c5SThomas E. Spanjaard 	(stp->flags & (F_DATA_WRITTEN | F_FM_WRITTEN)) == F_DATA_WRITTEN)
256c1b3d7c5SThomas E. Spanjaard 	ast_write_filemark(dev, ATAPI_WF_WRITE);
257c1b3d7c5SThomas E. Spanjaard 
258c1b3d7c5SThomas E. Spanjaard     /* if minor is even rewind on close */
259c1b3d7c5SThomas E. Spanjaard     if (!(minor(cdev) & 0x01))
260c1b3d7c5SThomas E. Spanjaard 	ast_rewind(dev);
261c1b3d7c5SThomas E. Spanjaard 
262c1b3d7c5SThomas E. Spanjaard     if (stp->cap.lock && count_dev(cdev) == 1)
263c1b3d7c5SThomas E. Spanjaard 	ast_prevent_allow(dev, 0);
264c1b3d7c5SThomas E. Spanjaard 
265c1b3d7c5SThomas E. Spanjaard     stp->flags &= ~F_CTL_WARN;
266c1b3d7c5SThomas E. Spanjaard #ifdef AST_DEBUG
267bb15467aSzrj     device_printf(dev, "%ju total bytes transferred\n", (uintmax_t)ast_total);
268c1b3d7c5SThomas E. Spanjaard #endif
269c1b3d7c5SThomas E. Spanjaard     return 0;
270c1b3d7c5SThomas E. Spanjaard }
271c1b3d7c5SThomas E. Spanjaard 
272c1b3d7c5SThomas E. Spanjaard static int
ast_ioctl(struct dev_ioctl_args * ap)273c1b3d7c5SThomas E. Spanjaard ast_ioctl(struct dev_ioctl_args *ap)
274c1b3d7c5SThomas E. Spanjaard {
275c1b3d7c5SThomas E. Spanjaard     device_t dev = ap->a_head.a_dev->si_drv1;
276c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp = device_get_ivars(dev);
277c1b3d7c5SThomas E. Spanjaard     int error = 0;
278c1b3d7c5SThomas E. Spanjaard 
279c1b3d7c5SThomas E. Spanjaard     switch (ap->a_cmd) {
280c1b3d7c5SThomas E. Spanjaard     case MTIOCGET:
281c1b3d7c5SThomas E. Spanjaard 	{
282c1b3d7c5SThomas E. Spanjaard 	    struct mtget *g = (struct mtget *)ap->a_data;
283c1b3d7c5SThomas E. Spanjaard 
284c1b3d7c5SThomas E. Spanjaard 	    bzero(g, sizeof(struct mtget));
285c1b3d7c5SThomas E. Spanjaard 	    g->mt_type = 7;
286c1b3d7c5SThomas E. Spanjaard 	    g->mt_density = 1;
287c1b3d7c5SThomas E. Spanjaard 	    g->mt_blksiz = stp->blksize;
288c1b3d7c5SThomas E. Spanjaard 	    g->mt_comp = stp->cap.compress;
289c1b3d7c5SThomas E. Spanjaard 	    g->mt_density0 = 0; g->mt_density1 = 0;
290c1b3d7c5SThomas E. Spanjaard 	    g->mt_density2 = 0; g->mt_density3 = 0;
291c1b3d7c5SThomas E. Spanjaard 	    g->mt_blksiz0 = 0; g->mt_blksiz1 = 0;
292c1b3d7c5SThomas E. Spanjaard 	    g->mt_blksiz2 = 0; g->mt_blksiz3 = 0;
293c1b3d7c5SThomas E. Spanjaard 	    g->mt_comp0 = 0; g->mt_comp1 = 0;
294c1b3d7c5SThomas E. Spanjaard 	    g->mt_comp2 = 0; g->mt_comp3 = 0;
295c1b3d7c5SThomas E. Spanjaard 	}
296c1b3d7c5SThomas E. Spanjaard 	break;
297c1b3d7c5SThomas E. Spanjaard 
298c1b3d7c5SThomas E. Spanjaard     case MTIOCTOP:
299c1b3d7c5SThomas E. Spanjaard 	{
300c1b3d7c5SThomas E. Spanjaard 	    int i;
301c1b3d7c5SThomas E. Spanjaard 	    struct mtop *mt = (struct mtop *)ap->a_data;
302c1b3d7c5SThomas E. Spanjaard 
303c1b3d7c5SThomas E. Spanjaard 	    switch ((int16_t) (mt->mt_op)) {
304c1b3d7c5SThomas E. Spanjaard 
305c1b3d7c5SThomas E. Spanjaard 	    case MTWEOF:
306c1b3d7c5SThomas E. Spanjaard 		for (i=0; i < mt->mt_count && !error; i++)
307c1b3d7c5SThomas E. Spanjaard 		    error = ast_write_filemark(dev, ATAPI_WF_WRITE);
308c1b3d7c5SThomas E. Spanjaard 		break;
309c1b3d7c5SThomas E. Spanjaard 
310c1b3d7c5SThomas E. Spanjaard 	    case MTFSF:
311c1b3d7c5SThomas E. Spanjaard 		if (mt->mt_count)
312c1b3d7c5SThomas E. Spanjaard 		    error = ast_space(dev, ATAPI_SP_FM, mt->mt_count);
313c1b3d7c5SThomas E. Spanjaard 		break;
314c1b3d7c5SThomas E. Spanjaard 
315c1b3d7c5SThomas E. Spanjaard 	    case MTBSF:
316c1b3d7c5SThomas E. Spanjaard 		if (mt->mt_count)
317c1b3d7c5SThomas E. Spanjaard 		    error = ast_space(dev, ATAPI_SP_FM, -(mt->mt_count));
318c1b3d7c5SThomas E. Spanjaard 		break;
319c1b3d7c5SThomas E. Spanjaard 
320c1b3d7c5SThomas E. Spanjaard 	    case MTREW:
321c1b3d7c5SThomas E. Spanjaard 		error = ast_rewind(dev);
322c1b3d7c5SThomas E. Spanjaard 		break;
323c1b3d7c5SThomas E. Spanjaard 
324c1b3d7c5SThomas E. Spanjaard 	    case MTOFFL:
325c1b3d7c5SThomas E. Spanjaard 		error = ast_load_unload(dev, ATAPI_SS_EJECT);
326c1b3d7c5SThomas E. Spanjaard 		break;
327c1b3d7c5SThomas E. Spanjaard 
328c1b3d7c5SThomas E. Spanjaard 	    case MTNOP:
329c1b3d7c5SThomas E. Spanjaard 		error = ast_write_filemark(dev, 0);
330c1b3d7c5SThomas E. Spanjaard 		break;
331c1b3d7c5SThomas E. Spanjaard 
332c1b3d7c5SThomas E. Spanjaard 	    case MTERASE:
333c1b3d7c5SThomas E. Spanjaard 		error = ast_erase(dev);
334c1b3d7c5SThomas E. Spanjaard 		break;
335c1b3d7c5SThomas E. Spanjaard 
336c1b3d7c5SThomas E. Spanjaard 	    case MTEOD:
337c1b3d7c5SThomas E. Spanjaard 		error = ast_space(dev, ATAPI_SP_EOD, 0);
338c1b3d7c5SThomas E. Spanjaard 		break;
339c1b3d7c5SThomas E. Spanjaard 
340c1b3d7c5SThomas E. Spanjaard 	    case MTRETENS:
341c1b3d7c5SThomas E. Spanjaard 		error = ast_load_unload(dev, ATAPI_SS_RETENSION|ATAPI_SS_LOAD);
342c1b3d7c5SThomas E. Spanjaard 		break;
343c1b3d7c5SThomas E. Spanjaard 
344c1b3d7c5SThomas E. Spanjaard 	    case MTFSR:
345c1b3d7c5SThomas E. Spanjaard 	    case MTBSR:
346c1b3d7c5SThomas E. Spanjaard 	    case MTCACHE:
347c1b3d7c5SThomas E. Spanjaard 	    case MTNOCACHE:
348c1b3d7c5SThomas E. Spanjaard 	    case MTSETBSIZ:
349c1b3d7c5SThomas E. Spanjaard 	    case MTSETDNSTY:
350c1b3d7c5SThomas E. Spanjaard 	    case MTCOMP:
351c1b3d7c5SThomas E. Spanjaard 	    default:
352c1b3d7c5SThomas E. Spanjaard 		error = EINVAL;
353c1b3d7c5SThomas E. Spanjaard 	    }
354c1b3d7c5SThomas E. Spanjaard 	}
355c1b3d7c5SThomas E. Spanjaard 	break;
356c1b3d7c5SThomas E. Spanjaard 
357c1b3d7c5SThomas E. Spanjaard     case MTIOCRDSPOS:
358c1b3d7c5SThomas E. Spanjaard 	{
359c1b3d7c5SThomas E. Spanjaard 	    struct ast_readposition position;
360c1b3d7c5SThomas E. Spanjaard 
361c1b3d7c5SThomas E. Spanjaard 	    if ((error = ast_read_position(dev, 0, &position)))
362c1b3d7c5SThomas E. Spanjaard 		break;
363c1b3d7c5SThomas E. Spanjaard 	    *(u_int32_t *)ap->a_data = position.tape;
364c1b3d7c5SThomas E. Spanjaard 	}
365c1b3d7c5SThomas E. Spanjaard 	break;
366c1b3d7c5SThomas E. Spanjaard 
367c1b3d7c5SThomas E. Spanjaard     case MTIOCRDHPOS:
368c1b3d7c5SThomas E. Spanjaard 	{
369c1b3d7c5SThomas E. Spanjaard 	    struct ast_readposition position;
370c1b3d7c5SThomas E. Spanjaard 
371c1b3d7c5SThomas E. Spanjaard 	    if ((error = ast_read_position(dev, 1, &position)))
372c1b3d7c5SThomas E. Spanjaard 		break;
373c1b3d7c5SThomas E. Spanjaard 	    *(u_int32_t *)ap->a_data = position.tape;
374c1b3d7c5SThomas E. Spanjaard 	}
375c1b3d7c5SThomas E. Spanjaard 	break;
376c1b3d7c5SThomas E. Spanjaard 
377c1b3d7c5SThomas E. Spanjaard     case MTIOCSLOCATE:
378c1b3d7c5SThomas E. Spanjaard 	error = ast_locate(dev, 0, *(u_int32_t *)ap->a_data);
379c1b3d7c5SThomas E. Spanjaard 	break;
380c1b3d7c5SThomas E. Spanjaard 
381c1b3d7c5SThomas E. Spanjaard     case MTIOCHLOCATE:
382c1b3d7c5SThomas E. Spanjaard 	error = ast_locate(dev, 1, *(u_int32_t *)ap->a_data);
383c1b3d7c5SThomas E. Spanjaard 	break;
384c1b3d7c5SThomas E. Spanjaard 
385c1b3d7c5SThomas E. Spanjaard     default:
386c1b3d7c5SThomas E. Spanjaard 	error = ata_device_ioctl(dev, ap->a_cmd, ap->a_data);
387c1b3d7c5SThomas E. Spanjaard     }
388c1b3d7c5SThomas E. Spanjaard     return error;
389c1b3d7c5SThomas E. Spanjaard }
390c1b3d7c5SThomas E. Spanjaard 
391c1b3d7c5SThomas E. Spanjaard static int
ast_strategy(struct dev_strategy_args * ap)392c1b3d7c5SThomas E. Spanjaard ast_strategy(struct dev_strategy_args *ap)
393c1b3d7c5SThomas E. Spanjaard {
394c1b3d7c5SThomas E. Spanjaard     device_t dev = ap->a_head.a_dev->si_drv1;
395c1b3d7c5SThomas E. Spanjaard     struct bio *bp = ap->a_bio;
396c1b3d7c5SThomas E. Spanjaard     struct buf *bbp = bp->bio_buf;
397c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
398c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp = device_get_ivars(dev);
399c1b3d7c5SThomas E. Spanjaard     struct ata_request *request;
400c1b3d7c5SThomas E. Spanjaard     u_int32_t blkcount;
401c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16];
402c1b3d7c5SThomas E. Spanjaard 
403c1b3d7c5SThomas E. Spanjaard     /* if it's a null transfer, return immediatly. */
404c1b3d7c5SThomas E. Spanjaard     if (bbp->b_bcount == 0) {
405c1b3d7c5SThomas E. Spanjaard 	bbp->b_resid = 0;
406c1b3d7c5SThomas E. Spanjaard 	biodone(bp);
407c1b3d7c5SThomas E. Spanjaard 	return 0;
408c1b3d7c5SThomas E. Spanjaard     }
409b106cb48SMatthew Dillon     if (!(bbp->b_cmd == BUF_CMD_READ) && (stp->flags & F_WRITEPROTECT)) {
410bb15467aSzrj 	biofinish(bp, NULL, EPERM);
411c1b3d7c5SThomas E. Spanjaard 	return 0;
412c1b3d7c5SThomas E. Spanjaard     }
413b106cb48SMatthew Dillon     if (bbp->b_cmd != BUF_CMD_READ && bbp->b_cmd != BUF_CMD_WRITE) {
414bb15467aSzrj 	biofinish(bp, NULL, EIO);
415b106cb48SMatthew Dillon 	return 0;
416b106cb48SMatthew Dillon     }
417c1b3d7c5SThomas E. Spanjaard 
418c1b3d7c5SThomas E. Spanjaard     /* check for != blocksize requests */
419c1b3d7c5SThomas E. Spanjaard     if (bbp->b_bcount % stp->blksize) {
420c1b3d7c5SThomas E. Spanjaard 	device_printf(dev, "transfers must be multiple of %d\n", stp->blksize);
421bb15467aSzrj 	biofinish(bp, NULL, EIO);
422c1b3d7c5SThomas E. Spanjaard 	return 0;
423c1b3d7c5SThomas E. Spanjaard     }
424c1b3d7c5SThomas E. Spanjaard 
425c1b3d7c5SThomas E. Spanjaard     /* warn about transfers bigger than the device suggests */
426c1b3d7c5SThomas E. Spanjaard     if (bbp->b_bcount > stp->blksize * stp->cap.ctl) {
427c1b3d7c5SThomas E. Spanjaard 	if ((stp->flags & F_CTL_WARN) == 0) {
428c1b3d7c5SThomas E. Spanjaard 	    device_printf(dev, "WARNING: CTL exceeded %d>%d\n",
429c1b3d7c5SThomas E. Spanjaard 			  bbp->b_bcount, stp->blksize * stp->cap.ctl);
430c1b3d7c5SThomas E. Spanjaard 	    stp->flags |= F_CTL_WARN;
431c1b3d7c5SThomas E. Spanjaard 	}
432c1b3d7c5SThomas E. Spanjaard     }
433c1b3d7c5SThomas E. Spanjaard 
434c1b3d7c5SThomas E. Spanjaard     bzero(ccb, sizeof(ccb));
435c1b3d7c5SThomas E. Spanjaard 
436c1b3d7c5SThomas E. Spanjaard     if (bbp->b_cmd == BUF_CMD_READ)
437c1b3d7c5SThomas E. Spanjaard 	ccb[0] = ATAPI_READ;
438c1b3d7c5SThomas E. Spanjaard     else
439c1b3d7c5SThomas E. Spanjaard 	ccb[0] = ATAPI_WRITE;
440c1b3d7c5SThomas E. Spanjaard 
441c1b3d7c5SThomas E. Spanjaard     blkcount = bbp->b_bcount / stp->blksize;
442c1b3d7c5SThomas E. Spanjaard 
443c1b3d7c5SThomas E. Spanjaard     ccb[1] = 1;
444c1b3d7c5SThomas E. Spanjaard     ccb[2] = blkcount >> 16;
445c1b3d7c5SThomas E. Spanjaard     ccb[3] = blkcount >> 8;
446c1b3d7c5SThomas E. Spanjaard     ccb[4] = blkcount;
447c1b3d7c5SThomas E. Spanjaard 
448c1b3d7c5SThomas E. Spanjaard     if (!(request = ata_alloc_request())) {
449bb15467aSzrj 	biofinish(bp, NULL, ENOMEM);
450c1b3d7c5SThomas E. Spanjaard 	return 0;
451c1b3d7c5SThomas E. Spanjaard     }
452c1b3d7c5SThomas E. Spanjaard     request->dev = dev;
453c1b3d7c5SThomas E. Spanjaard     request->bio = bp;
454c1b3d7c5SThomas E. Spanjaard     bcopy(ccb, request->u.atapi.ccb,
455c1b3d7c5SThomas E. Spanjaard 	  (atadev->param.config & ATA_PROTO_MASK) ==
456c1b3d7c5SThomas E. Spanjaard 	  ATA_PROTO_ATAPI_12 ? 16 : 12);
457c1b3d7c5SThomas E. Spanjaard     request->data = bbp->b_data;
458c1b3d7c5SThomas E. Spanjaard     request->bytecount = blkcount * stp->blksize;
459c1b3d7c5SThomas E. Spanjaard     request->transfersize = min(request->bytecount, 65534);
460c1b3d7c5SThomas E. Spanjaard     request->timeout = (ccb[0] == ATAPI_WRITE_BIG) ? 180 : 120;
461c1b3d7c5SThomas E. Spanjaard     request->retries = 2;
462c1b3d7c5SThomas E. Spanjaard     request->callback = ast_done;
463b106cb48SMatthew Dillon 
464c1b3d7c5SThomas E. Spanjaard     switch (bbp->b_cmd) {
465c1b3d7c5SThomas E. Spanjaard     case BUF_CMD_READ:
466c1b3d7c5SThomas E. Spanjaard 	request->flags |= (ATA_R_ATAPI | ATA_R_READ);
467c1b3d7c5SThomas E. Spanjaard 	break;
468c1b3d7c5SThomas E. Spanjaard     case BUF_CMD_WRITE:
469c1b3d7c5SThomas E. Spanjaard 	request->flags |= (ATA_R_ATAPI | ATA_R_WRITE);
470c1b3d7c5SThomas E. Spanjaard 	break;
471c1b3d7c5SThomas E. Spanjaard     default:
472b106cb48SMatthew Dillon 	panic("bbp->b_cmd");
473c1b3d7c5SThomas E. Spanjaard     }
474c1b3d7c5SThomas E. Spanjaard     devstat_start_transaction(&stp->stats);
475c1b3d7c5SThomas E. Spanjaard     ata_queue_request(request);
476c1b3d7c5SThomas E. Spanjaard     return 0;
477c1b3d7c5SThomas E. Spanjaard }
478c1b3d7c5SThomas E. Spanjaard 
479c1b3d7c5SThomas E. Spanjaard static void
ast_done(struct ata_request * request)480c1b3d7c5SThomas E. Spanjaard ast_done(struct ata_request *request)
481c1b3d7c5SThomas E. Spanjaard {
482c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp = device_get_ivars(request->dev);
483c1b3d7c5SThomas E. Spanjaard     struct bio *bp = request->bio;
484c1b3d7c5SThomas E. Spanjaard     struct buf *bbp = bp->bio_buf;
485c1b3d7c5SThomas E. Spanjaard 
486c1b3d7c5SThomas E. Spanjaard     /* finish up transfer */
487c1b3d7c5SThomas E. Spanjaard     if ((bbp->b_error = request->result))
488c1b3d7c5SThomas E. Spanjaard 	bbp->b_flags |= B_ERROR;
489c1b3d7c5SThomas E. Spanjaard     if (bbp->b_cmd == BUF_CMD_WRITE)
490c1b3d7c5SThomas E. Spanjaard 	stp->flags |= F_DATA_WRITTEN;
491c1b3d7c5SThomas E. Spanjaard     bbp->b_resid = bbp->b_bcount - request->donecount;
492c1b3d7c5SThomas E. Spanjaard     ast_total += (bbp->b_bcount - bbp->b_resid);
493c1b3d7c5SThomas E. Spanjaard     devstat_end_transaction_buf(&stp->stats, bbp);
494c1b3d7c5SThomas E. Spanjaard     biodone(bp);
495c1b3d7c5SThomas E. Spanjaard     ata_free_request(request);
496c1b3d7c5SThomas E. Spanjaard }
497c1b3d7c5SThomas E. Spanjaard 
498c1b3d7c5SThomas E. Spanjaard static int
ast_sense(device_t dev)499c1b3d7c5SThomas E. Spanjaard ast_sense(device_t dev)
500c1b3d7c5SThomas E. Spanjaard {
501c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp = device_get_ivars(dev);
502c1b3d7c5SThomas E. Spanjaard     int count;
503c1b3d7c5SThomas E. Spanjaard 
504c1b3d7c5SThomas E. Spanjaard     /* get drive capabilities, some bugridden drives needs this repeated */
505c1b3d7c5SThomas E. Spanjaard     for (count = 0 ; count < 5 ; count++) {
506c1b3d7c5SThomas E. Spanjaard 	if (!ast_mode_sense(dev, ATAPI_TAPE_CAP_PAGE,
507c1b3d7c5SThomas E. Spanjaard 			    &stp->cap, sizeof(stp->cap)) &&
508c1b3d7c5SThomas E. Spanjaard 	    stp->cap.page_code == ATAPI_TAPE_CAP_PAGE) {
509c1b3d7c5SThomas E. Spanjaard 	    if (stp->cap.blk32k)
510c1b3d7c5SThomas E. Spanjaard 		stp->blksize = 32768;
511c1b3d7c5SThomas E. Spanjaard 	    if (stp->cap.blk1024)
512c1b3d7c5SThomas E. Spanjaard 		stp->blksize = 1024;
513c1b3d7c5SThomas E. Spanjaard 	    if (stp->cap.blk512)
514c1b3d7c5SThomas E. Spanjaard 		stp->blksize = 512;
515c1b3d7c5SThomas E. Spanjaard 	    if (!stp->blksize)
516c1b3d7c5SThomas E. Spanjaard 		continue;
517c1b3d7c5SThomas E. Spanjaard 	    stp->cap.max_speed = ntohs(stp->cap.max_speed);
518c1b3d7c5SThomas E. Spanjaard 	    stp->cap.max_defects = ntohs(stp->cap.max_defects);
519c1b3d7c5SThomas E. Spanjaard 	    stp->cap.ctl = ntohs(stp->cap.ctl);
520c1b3d7c5SThomas E. Spanjaard 	    stp->cap.speed = ntohs(stp->cap.speed);
521c1b3d7c5SThomas E. Spanjaard 	    stp->cap.buffer_size = ntohs(stp->cap.buffer_size);
522c1b3d7c5SThomas E. Spanjaard 	    return 0;
523c1b3d7c5SThomas E. Spanjaard 	}
524c1b3d7c5SThomas E. Spanjaard     }
525c1b3d7c5SThomas E. Spanjaard     return 1;
526c1b3d7c5SThomas E. Spanjaard }
527c1b3d7c5SThomas E. Spanjaard 
528c1b3d7c5SThomas E. Spanjaard static int
ast_mode_sense(device_t dev,int page,void * pagebuf,int pagesize)529c1b3d7c5SThomas E. Spanjaard ast_mode_sense(device_t dev, int page, void *pagebuf, int pagesize)
530c1b3d7c5SThomas E. Spanjaard {
531c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_MODE_SENSE, 0x08, page, pagesize>>8, pagesize,
532c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
533c1b3d7c5SThomas E. Spanjaard     int error;
534c1b3d7c5SThomas E. Spanjaard 
535c1b3d7c5SThomas E. Spanjaard     error = ata_atapicmd(dev, ccb, pagebuf, pagesize, ATA_R_READ, 10);
536c1b3d7c5SThomas E. Spanjaard     return error;
537c1b3d7c5SThomas E. Spanjaard }
538c1b3d7c5SThomas E. Spanjaard 
539c1b3d7c5SThomas E. Spanjaard static int
ast_mode_select(device_t dev,void * pagebuf,int pagesize)540c1b3d7c5SThomas E. Spanjaard ast_mode_select(device_t dev, void *pagebuf, int pagesize)
541c1b3d7c5SThomas E. Spanjaard {
542c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_MODE_SELECT, 0x10, 0, pagesize>>8, pagesize,
543c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
544c1b3d7c5SThomas E. Spanjaard 
545c1b3d7c5SThomas E. Spanjaard     return ata_atapicmd(dev, ccb, pagebuf, pagesize, 0, 10);
546c1b3d7c5SThomas E. Spanjaard }
547c1b3d7c5SThomas E. Spanjaard 
548c1b3d7c5SThomas E. Spanjaard static int
ast_write_filemark(device_t dev,u_int8_t function)549c1b3d7c5SThomas E. Spanjaard ast_write_filemark(device_t dev, u_int8_t function)
550c1b3d7c5SThomas E. Spanjaard {
551c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp = device_get_ivars(dev);
552c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_WEOF, 0x01, 0, 0, function, 0, 0, 0,
553c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0 };
554c1b3d7c5SThomas E. Spanjaard     int error;
555c1b3d7c5SThomas E. Spanjaard 
556c1b3d7c5SThomas E. Spanjaard     if (stp->flags & F_ONSTREAM)
557c1b3d7c5SThomas E. Spanjaard 	ccb[4] = 0x00;          /* only flush buffers supported */
558c1b3d7c5SThomas E. Spanjaard     else {
559c1b3d7c5SThomas E. Spanjaard 	if (function) {
560c1b3d7c5SThomas E. Spanjaard 	    if (stp->flags & F_FM_WRITTEN)
561c1b3d7c5SThomas E. Spanjaard 		stp->flags &= ~F_DATA_WRITTEN;
562c1b3d7c5SThomas E. Spanjaard 	    else
563c1b3d7c5SThomas E. Spanjaard 		stp->flags |= F_FM_WRITTEN;
564c1b3d7c5SThomas E. Spanjaard 	}
565c1b3d7c5SThomas E. Spanjaard     }
566c1b3d7c5SThomas E. Spanjaard     error = ata_atapicmd(dev, ccb, NULL, 0, 0, 10);
567c1b3d7c5SThomas E. Spanjaard     if (error)
568c1b3d7c5SThomas E. Spanjaard 	return error;
569c1b3d7c5SThomas E. Spanjaard     return ast_wait_dsc(dev, 10*60);
570c1b3d7c5SThomas E. Spanjaard }
571c1b3d7c5SThomas E. Spanjaard 
572c1b3d7c5SThomas E. Spanjaard static int
ast_read_position(device_t dev,int hard,struct ast_readposition * position)573c1b3d7c5SThomas E. Spanjaard ast_read_position(device_t dev, int hard, struct ast_readposition *position)
574c1b3d7c5SThomas E. Spanjaard {
575c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_READ_POSITION, (hard ? 0x01 : 0), 0, 0, 0, 0, 0, 0,
576c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0 };
577c1b3d7c5SThomas E. Spanjaard     int error;
578c1b3d7c5SThomas E. Spanjaard 
579c1b3d7c5SThomas E. Spanjaard     error = ata_atapicmd(dev, ccb, (caddr_t)position,
580c1b3d7c5SThomas E. Spanjaard 			 sizeof(struct ast_readposition), ATA_R_READ, 10);
581c1b3d7c5SThomas E. Spanjaard     position->tape = ntohl(position->tape);
582c1b3d7c5SThomas E. Spanjaard     position->host = ntohl(position->host);
583c1b3d7c5SThomas E. Spanjaard     return error;
584c1b3d7c5SThomas E. Spanjaard }
585c1b3d7c5SThomas E. Spanjaard 
586c1b3d7c5SThomas E. Spanjaard static int
ast_space(device_t dev,u_int8_t function,int32_t count)587c1b3d7c5SThomas E. Spanjaard ast_space(device_t dev, u_int8_t function, int32_t count)
588c1b3d7c5SThomas E. Spanjaard {
589c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_SPACE, function, count>>16, count>>8, count,
590c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
591c1b3d7c5SThomas E. Spanjaard 
592c1b3d7c5SThomas E. Spanjaard     return ata_atapicmd(dev, ccb, NULL, 0, 0, 60*60);
593c1b3d7c5SThomas E. Spanjaard }
594c1b3d7c5SThomas E. Spanjaard 
595c1b3d7c5SThomas E. Spanjaard static int
ast_locate(device_t dev,int hard,u_int32_t pos)596c1b3d7c5SThomas E. Spanjaard ast_locate(device_t dev, int hard, u_int32_t pos)
597c1b3d7c5SThomas E. Spanjaard {
598c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_LOCATE, 0x01 | (hard ? 0x4 : 0), 0,
599c1b3d7c5SThomas E. Spanjaard 		       pos>>24, pos>>16, pos>>8, pos,
600c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0, 0 };
601c1b3d7c5SThomas E. Spanjaard     int error;
602c1b3d7c5SThomas E. Spanjaard 
603c1b3d7c5SThomas E. Spanjaard     error = ata_atapicmd(dev, ccb, NULL, 0, 0, 10);
604c1b3d7c5SThomas E. Spanjaard     if (error)
605c1b3d7c5SThomas E. Spanjaard 	return error;
606c1b3d7c5SThomas E. Spanjaard     return ast_wait_dsc(dev, 60*60);
607c1b3d7c5SThomas E. Spanjaard }
608c1b3d7c5SThomas E. Spanjaard 
609c1b3d7c5SThomas E. Spanjaard static int
ast_prevent_allow(device_t dev,int lock)610c1b3d7c5SThomas E. Spanjaard ast_prevent_allow(device_t dev, int lock)
611c1b3d7c5SThomas E. Spanjaard {
612c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock,
613c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
614c1b3d7c5SThomas E. Spanjaard 
615c1b3d7c5SThomas E. Spanjaard     return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
616c1b3d7c5SThomas E. Spanjaard }
617c1b3d7c5SThomas E. Spanjaard 
618c1b3d7c5SThomas E. Spanjaard static int
ast_load_unload(device_t dev,u_int8_t function)619c1b3d7c5SThomas E. Spanjaard ast_load_unload(device_t dev, u_int8_t function)
620c1b3d7c5SThomas E. Spanjaard {
621c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp = device_get_ivars(dev);
622c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_START_STOP, 0x01, 0, 0, function, 0, 0, 0,
623c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0 };
624c1b3d7c5SThomas E. Spanjaard     int error;
625c1b3d7c5SThomas E. Spanjaard 
626c1b3d7c5SThomas E. Spanjaard     if ((function & ATAPI_SS_EJECT) && !stp->cap.eject)
627c1b3d7c5SThomas E. Spanjaard 	return 0;
628c1b3d7c5SThomas E. Spanjaard     error = ata_atapicmd(dev, ccb, NULL, 0, 0, 10);
629c1b3d7c5SThomas E. Spanjaard     if (error)
630c1b3d7c5SThomas E. Spanjaard 	return error;
631c1b3d7c5SThomas E. Spanjaard     tsleep((caddr_t)&error, 0, "astlu", 1 * hz);
632c1b3d7c5SThomas E. Spanjaard     if (function == ATAPI_SS_EJECT)
633c1b3d7c5SThomas E. Spanjaard 	return 0;
634c1b3d7c5SThomas E. Spanjaard     return ast_wait_dsc(dev, 60*60);
635c1b3d7c5SThomas E. Spanjaard }
636c1b3d7c5SThomas E. Spanjaard 
637c1b3d7c5SThomas E. Spanjaard static int
ast_rewind(device_t dev)638c1b3d7c5SThomas E. Spanjaard ast_rewind(device_t dev)
639c1b3d7c5SThomas E. Spanjaard {
640c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_REZERO, 0x01, 0, 0, 0, 0, 0, 0,
641c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0 };
642c1b3d7c5SThomas E. Spanjaard     int error;
643c1b3d7c5SThomas E. Spanjaard 
644c1b3d7c5SThomas E. Spanjaard     error = ata_atapicmd(dev, ccb, NULL, 0, 0, 10);
645c1b3d7c5SThomas E. Spanjaard     if (error)
646c1b3d7c5SThomas E. Spanjaard 	return error;
647c1b3d7c5SThomas E. Spanjaard     return ast_wait_dsc(dev, 60*60);
648c1b3d7c5SThomas E. Spanjaard }
649c1b3d7c5SThomas E. Spanjaard 
650c1b3d7c5SThomas E. Spanjaard static int
ast_erase(device_t dev)651c1b3d7c5SThomas E. Spanjaard ast_erase(device_t dev)
652c1b3d7c5SThomas E. Spanjaard {
653c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_ERASE, 3, 0, 0, 0, 0, 0, 0,
654c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0 };
655c1b3d7c5SThomas E. Spanjaard     int error;
656c1b3d7c5SThomas E. Spanjaard 
657c1b3d7c5SThomas E. Spanjaard     if ((error = ast_rewind(dev)))
658c1b3d7c5SThomas E. Spanjaard 	return error;
659c1b3d7c5SThomas E. Spanjaard 
660c1b3d7c5SThomas E. Spanjaard     return ata_atapicmd(dev, ccb, NULL, 0, 0, 60*60);
661c1b3d7c5SThomas E. Spanjaard }
662c1b3d7c5SThomas E. Spanjaard 
663c1b3d7c5SThomas E. Spanjaard static int
ast_test_ready(device_t dev)664c1b3d7c5SThomas E. Spanjaard ast_test_ready(device_t dev)
665c1b3d7c5SThomas E. Spanjaard {
666c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0,
667c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
668c1b3d7c5SThomas E. Spanjaard 
669c1b3d7c5SThomas E. Spanjaard     return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
670c1b3d7c5SThomas E. Spanjaard }
671c1b3d7c5SThomas E. Spanjaard 
672c1b3d7c5SThomas E. Spanjaard static int
ast_wait_dsc(device_t dev,int timeout)673c1b3d7c5SThomas E. Spanjaard ast_wait_dsc(device_t dev, int timeout)
674c1b3d7c5SThomas E. Spanjaard {
675c1b3d7c5SThomas E. Spanjaard     int error = 0;
676c1b3d7c5SThomas E. Spanjaard     int8_t ccb[16] = { ATAPI_POLL_DSC, 0, 0, 0, 0,
677c1b3d7c5SThomas E. Spanjaard 		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
678c1b3d7c5SThomas E. Spanjaard 
679c1b3d7c5SThomas E. Spanjaard     timeout *= hz;
680c1b3d7c5SThomas E. Spanjaard     while (timeout > 0) {
681c1b3d7c5SThomas E. Spanjaard 	error = ata_atapicmd(dev, ccb, NULL, 0, 0, 0);
682c1b3d7c5SThomas E. Spanjaard 	if (error != EBUSY)
683c1b3d7c5SThomas E. Spanjaard 	    break;
684c1b3d7c5SThomas E. Spanjaard 	tsleep(&error, 0, "atpwt", hz / 2);
685c1b3d7c5SThomas E. Spanjaard 	timeout -= (hz / 2);
686c1b3d7c5SThomas E. Spanjaard     }
687c1b3d7c5SThomas E. Spanjaard     return error;
688c1b3d7c5SThomas E. Spanjaard }
689c1b3d7c5SThomas E. Spanjaard 
690c1b3d7c5SThomas E. Spanjaard static void
ast_describe(device_t dev)691c1b3d7c5SThomas E. Spanjaard ast_describe(device_t dev)
692c1b3d7c5SThomas E. Spanjaard {
693c1b3d7c5SThomas E. Spanjaard     struct ata_channel *ch = device_get_softc(device_get_parent(dev));
694c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
695c1b3d7c5SThomas E. Spanjaard     struct ast_softc *stp = device_get_ivars(dev);
696c1b3d7c5SThomas E. Spanjaard 
697c1b3d7c5SThomas E. Spanjaard     if (bootverbose) {
698c1b3d7c5SThomas E. Spanjaard 	device_printf(dev, "<%.40s/%.8s> tape drive at ata%d as %s\n",
699c1b3d7c5SThomas E. Spanjaard 		      atadev->param.model, atadev->param.revision,
700cbf684e5Szrj 		      device_get_unit(ch->dev), ata_unit2str(atadev));
701c1b3d7c5SThomas E. Spanjaard 	device_printf(dev, "%dKB/s, ", stp->cap.max_speed);
702e3869ec7SSascha Wildner 	kprintf("transfer limit %d blk%s, ",
703c1b3d7c5SThomas E. Spanjaard 	       stp->cap.ctl, (stp->cap.ctl > 1) ? "s" : "");
704e3869ec7SSascha Wildner 	kprintf("%dKB buffer, ", (stp->cap.buffer_size * DEV_BSIZE) / 1024);
705e3869ec7SSascha Wildner 	kprintf("%s\n", ata_mode2str(atadev->mode));
706c1b3d7c5SThomas E. Spanjaard 	device_printf(dev, "Medium: ");
707c1b3d7c5SThomas E. Spanjaard 	switch (stp->cap.medium_type) {
708c1b3d7c5SThomas E. Spanjaard 	    case 0x00:
709e3869ec7SSascha Wildner 		kprintf("none"); break;
710c1b3d7c5SThomas E. Spanjaard 	    case 0x17:
711e3869ec7SSascha Wildner 		kprintf("Travan 1 (400 Mbyte)"); break;
712c1b3d7c5SThomas E. Spanjaard 	    case 0xb6:
713e3869ec7SSascha Wildner 		kprintf("Travan 4 (4 Gbyte)"); break;
714c1b3d7c5SThomas E. Spanjaard 	    case 0xda:
715e3869ec7SSascha Wildner 		kprintf("OnStream ADR (15Gyte)"); break;
716c1b3d7c5SThomas E. Spanjaard 	    default:
717e3869ec7SSascha Wildner 		kprintf("unknown (0x%x)", stp->cap.medium_type);
718c1b3d7c5SThomas E. Spanjaard 	}
719e3869ec7SSascha Wildner 	if (stp->cap.readonly) kprintf(", readonly");
720e3869ec7SSascha Wildner 	if (stp->cap.reverse) kprintf(", reverse");
721e3869ec7SSascha Wildner 	if (stp->cap.eformat) kprintf(", eformat");
722e3869ec7SSascha Wildner 	if (stp->cap.qfa) kprintf(", qfa");
723e3869ec7SSascha Wildner 	if (stp->cap.lock) kprintf(", lock");
724e3869ec7SSascha Wildner 	if (stp->cap.locked) kprintf(", locked");
725e3869ec7SSascha Wildner 	if (stp->cap.prevent) kprintf(", prevent");
726e3869ec7SSascha Wildner 	if (stp->cap.eject) kprintf(", eject");
727e3869ec7SSascha Wildner 	if (stp->cap.disconnect) kprintf(", disconnect");
728e3869ec7SSascha Wildner 	if (stp->cap.ecc) kprintf(", ecc");
729e3869ec7SSascha Wildner 	if (stp->cap.compress) kprintf(", compress");
730e3869ec7SSascha Wildner 	if (stp->cap.blk512) kprintf(", 512b");
731e3869ec7SSascha Wildner 	if (stp->cap.blk1024) kprintf(", 1024b");
732e3869ec7SSascha Wildner 	if (stp->cap.blk32k) kprintf(", 32kb");
733e3869ec7SSascha Wildner 	kprintf("\n");
734c1b3d7c5SThomas E. Spanjaard     }
735c1b3d7c5SThomas E. Spanjaard     else {
736c1b3d7c5SThomas E. Spanjaard 	device_printf(dev, "TAPE <%.40s/%.8s> at ata%d-%s %s\n",
737c1b3d7c5SThomas E. Spanjaard 		      atadev->param.model, atadev->param.revision,
738cbf684e5Szrj 		      device_get_unit(ch->dev), ata_unit2str(atadev),
739c1b3d7c5SThomas E. Spanjaard 		      ata_mode2str(atadev->mode));
740c1b3d7c5SThomas E. Spanjaard     }
741c1b3d7c5SThomas E. Spanjaard }
742c1b3d7c5SThomas E. Spanjaard 
743c1b3d7c5SThomas E. Spanjaard static device_method_t ast_methods[] = {
744c1b3d7c5SThomas E. Spanjaard     /* device interface */
745c1b3d7c5SThomas E. Spanjaard     DEVMETHOD(device_probe,     ast_probe),
746c1b3d7c5SThomas E. Spanjaard     DEVMETHOD(device_attach,    ast_attach),
747c1b3d7c5SThomas E. Spanjaard     DEVMETHOD(device_detach,    ast_detach),
748c1b3d7c5SThomas E. Spanjaard     DEVMETHOD(device_shutdown,  ast_shutdown),
749c1b3d7c5SThomas E. Spanjaard 
750c1b3d7c5SThomas E. Spanjaard     /* ATA methods */
751c1b3d7c5SThomas E. Spanjaard     DEVMETHOD(ata_reinit,       ast_reinit),
752c1b3d7c5SThomas E. Spanjaard 
753d3c9c58eSSascha Wildner     DEVMETHOD_END
754c1b3d7c5SThomas E. Spanjaard };
755c1b3d7c5SThomas E. Spanjaard 
756c1b3d7c5SThomas E. Spanjaard static driver_t ast_driver = {
757c1b3d7c5SThomas E. Spanjaard     "ast",
758c1b3d7c5SThomas E. Spanjaard     ast_methods,
759c1b3d7c5SThomas E. Spanjaard     0,
760c1b3d7c5SThomas E. Spanjaard };
761c1b3d7c5SThomas E. Spanjaard 
762c1b3d7c5SThomas E. Spanjaard static devclass_t ast_devclass;
763c1b3d7c5SThomas E. Spanjaard 
764c1b3d7c5SThomas E. Spanjaard DRIVER_MODULE(ast, ata, ast_driver, ast_devclass, NULL, NULL);
765c1b3d7c5SThomas E. Spanjaard MODULE_VERSION(ast, 1);
766c1b3d7c5SThomas E. Spanjaard MODULE_DEPEND(ast, ata, 1, 1, 1);
767