root/sys/dev/scsipi/st_atapi.c
/*      $NetBSD: st_atapi.c,v 1.31 2016/07/14 04:00:46 msaitoh Exp $ */

/*
 * Copyright (c) 2001 Manuel Bouyer.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: st_atapi.c,v 1.31 2016/07/14 04:00:46 msaitoh Exp $");

#ifdef _KERNEL_OPT
#include "opt_scsi.h"
#endif

#include <sys/param.h>
#include <sys/device.h>
#include <sys/buf.h>
#include <sys/bufq.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/systm.h>

#include <dev/scsipi/stvar.h>
#include <dev/scsipi/atapi_tape.h>

static int      st_atapibus_match(device_t, cfdata_t, void *);
static void     st_atapibus_attach(device_t, device_t, void *);
static int      st_atapibus_ops(struct st_softc *, int, int);
static int      st_atapibus_mode_sense(struct st_softc *, int);

CFATTACH_DECL_NEW(
        st_atapibus,
        sizeof(struct st_softc),
        st_atapibus_match,
        st_atapibus_attach,
        stdetach,
        NULL
);

static const struct scsipi_inquiry_pattern st_atapibus_patterns[] = {
        {T_SEQUENTIAL, T_REMOV,
         "",     "",             ""},
};

static int
st_atapibus_match(device_t parent, cfdata_t match,  void *aux)
{
        struct scsipibus_attach_args *sa = aux;
        int priority;

        if (SCSIPI_BUSTYPE_TYPE(scsipi_periph_bustype(sa->sa_periph)) !=
            SCSIPI_BUSTYPE_ATAPI)
                return 0;

        (void)scsipi_inqmatch(&sa->sa_inqbuf,
            st_atapibus_patterns,
            sizeof(st_atapibus_patterns)/sizeof(st_atapibus_patterns[0]),
            sizeof(st_atapibus_patterns[0]), &priority);
        return priority;
}

static void
st_atapibus_attach(device_t parent, device_t self, void *aux)
{
        struct st_softc *st = device_private(self);
        struct scsipibus_attach_args *sa = aux;
        struct scsipi_periph *periph = sa->sa_periph;

        if (strcmp(sa->sa_inqbuf.vendor, "OnStream DI-30") == 0) {
                struct ast_identifypage identify;
                int error;

                error = scsipi_mode_sense(periph, SMS_DBD,
                    ATAPI_TAPE_IDENTIFY_PAGE, &identify.header,
                    sizeof(identify), XS_CTL_DISCOVERY,
                    ST_RETRIES, ST_CTL_TIME);
                if (error) {
                        aprint_error_dev(self,
                            "onstream get identify: error %d\n", error);
                        return;
                }
                strncpy(identify.ident, "NBSD", 4);
                error = scsipi_mode_select(periph, SMS_PF,
                    &identify.header, sizeof(identify),
                    XS_CTL_DISCOVERY, ST_RETRIES, ST_CTL_TIME);
                if (error) {
                        aprint_error_dev(self,
                            "onstream set identify: error %d\n", error);
                        return;
                }
        }

        st->ops = st_atapibus_ops;
        stattach(parent, self, aux);
}

static int
st_atapibus_ops(struct st_softc *st, int op, int flags)
{
        switch(op) {
        case ST_OPS_RBL:
                /* done in mode_sense */
                return 0;
        case ST_OPS_MODESENSE:
                return st_atapibus_mode_sense(st, flags);
        case ST_OPS_MODESELECT:
                return st_mode_select(st, flags);
        case ST_OPS_CMPRSS_ON:
        case ST_OPS_CMPRSS_OFF:
                return ENODEV;
        default:
                panic("st_scsibus_ops: invalid op");
                /* NOTREACHED */
        }
}

static int
st_atapibus_mode_sense(struct st_softc *st, int flags)
{
        int count, error;
        struct atapi_cappage cappage;
        struct scsipi_periph *periph = st->sc_periph;

        /* get drive capabilities, some drives needs this repeated */
        for (count = 0 ; count < 5 ; count++) {
                error = scsipi_mode_sense(periph, SMS_DBD,
                    ATAPI_TAPE_CAP_PAGE, &cappage.header, sizeof(cappage),
                    flags, ST_RETRIES, ST_CTL_TIME);
                if (error == 0) {
                        st->numblks = 0; /* unused anyway */
                        if (cappage.cap4 & ATAPI_TAPE_CAP_PAGE_BLK32K)
                                st->media_blksize = 32768;
                        else if (cappage.cap4 & ATAPI_TAPE_CAP_PAGE_BLK1K)
                                st->media_blksize = 1024;
                        else if (cappage.cap4 & ATAPI_TAPE_CAP_PAGE_BLK512)
                                st->media_blksize = 512;
                        else {
                                error = ENODEV;
                                continue;
                        }
                        st->blkmin = st->blkmax = st->media_blksize;
                        st->media_density = 0;
                        if (cappage.header.dev_spec & SMH_DSP_WRITE_PROT)
                                st->flags |= ST_READONLY;
                        else
                                st->flags &= ~ST_READONLY;
                        SC_DEBUG(periph, SCSIPI_DB3,
                            ("density code %d, %d-byte blocks, write-%s, ",
                            st->media_density, st->media_blksize,
                            st->flags & ST_READONLY ? "protected" : "enabled"));
                        SC_DEBUG(periph, SCSIPI_DB3,
                            ("%sbuffered\n",
                            cappage.header.dev_spec & SMH_DSP_BUFF_MODE ?
                            "" : "un"));
                        periph->periph_flags |= PERIPH_MEDIA_LOADED;
                        return 0;
                }
        }
        return error;
}