summaryrefslogtreecommitdiff
path: root/sys/dev/atapi/atapiconf.c
diff options
context:
space:
mode:
authorJason Downs <downsj@cvs.openbsd.org>1996-06-08 09:12:48 +0000
committerJason Downs <downsj@cvs.openbsd.org>1996-06-08 09:12:48 +0000
commitfdc68389b9dfe6e5d35bc6cb6b180e9daf9e61e2 (patch)
treea4460d2ec87ff4da7cc88b4cae0ebcf64034dc98 /sys/dev/atapi/atapiconf.c
parent9a887c22a316f9b94faed82ec7af6c138ff71262 (diff)
Initial support ATAPI CD-ROMs.
Based on Manuel Bouyer's drivers.
Diffstat (limited to 'sys/dev/atapi/atapiconf.c')
-rw-r--r--sys/dev/atapi/atapiconf.c676
1 files changed, 676 insertions, 0 deletions
diff --git a/sys/dev/atapi/atapiconf.c b/sys/dev/atapi/atapiconf.c
new file mode 100644
index 00000000000..85095a4c0a4
--- /dev/null
+++ b/sys/dev/atapi/atapiconf.c
@@ -0,0 +1,676 @@
+/* $NetBSD: $ */
+
+/*
+ * Copyright (c) 1996 Manuel Bouyer. All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+
+#include <dev/atapi/atapilink.h>
+#include <dev/atapi/atapi.h>
+
+#define SILENT_PRINTF(flags,string) if (!(flags & A_SILENT)) printf string
+
+#ifdef ATAPI_DEBUG_CMD
+#define ATAPI_DEBUG_CMD_PRINT(args) printf args
+#else
+#define ATAPI_DEBUG_CMD_PRINT(args)
+#endif
+
+#ifdef ATAPI_DEBUG_FCTN
+#define ATAPI_DEBUG_FCTN_PRINT(args) printf args
+#else
+#define ATAPI_DEBUG_FCTN_PRINT(args)
+#endif
+
+struct atapibus_softc {
+ struct device sc_dev;
+ struct bus_link *b_link;
+};
+
+LIST_HEAD(pkt_free_list, atapi_command_packet) pkt_free_list;
+
+static void bswap __P((char *, int));
+static void btrim __P((char *, int));
+
+int atapi_error __P((struct atapi_command_packet *));
+void atapi_sense __P((struct atapi_command_packet *, u_int8_t, u_int8_t));
+void at_print_addr __P((struct at_dev_link *, u_int8_t));
+
+int atapibusmatch __P((struct device *, void *, void *));
+void atapibusattach __P((struct device *, struct device *, void *));
+
+struct cfattach atapibus_ca = {
+ sizeof(struct atapibus_softc), atapibusmatch, atapibusattach
+};
+
+struct cfdriver atapibus_cd = {
+ NULL, "atapibus", DV_DULL
+};
+
+
+int
+atapibusmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct cfdata *cf = match;
+ struct bus_link *ab_link = aux;
+
+ if (ab_link == NULL)
+ return 0;
+ if (ab_link->type != BUS)
+ return 0;
+ return 1;
+}
+
+int
+atapiprint(aux, bus)
+ void *aux;
+ char *bus;
+{
+ struct at_dev_link *ad_link = aux;
+ struct atapi_identify *id = &ad_link->id;
+ char *dtype, *fixrem;
+
+ /*
+ * Figure out basic device type.
+ */
+ switch (id->config.device_type & ATAPI_DEVICE_TYPE_MASK) {
+ case ATAPI_DEVICE_TYPE_DAD:
+ dtype = "direct";
+ break;
+
+ case ATAPI_DEVICE_TYPE_CD:
+ dtype = "cdrom";
+ break;
+
+ case ATAPI_DEVICE_TYPE_OMD:
+ dtype = "optical";
+ break;
+
+ default:
+ dtype = "unknown";
+ break;
+ }
+
+ fixrem = (id->config.cmd_drq_rem & ATAPI_REMOVABLE) ?
+ "removable" : "fixed";
+
+ /*
+ * Shuffle string byte order.
+ * Mitsumi and NEC drives don't need this.
+ */
+ if (((id->model[0] == 'N' && id->model[1] == 'E') ||
+ (id->model[0] == 'F' && id->model[1] == 'X')) == 0)
+ bswap(id->model, sizeof(id->model));
+
+ /*
+ * XXX Poorly named... These appear to actually be in
+ * XXX network byte order, so bswap() is a no-op on
+ * XXX big-endian hosts. Clean me up, please.
+ */
+ bswap(id->serial_number, sizeof(id->serial_number));
+ bswap(id->firmware_revision, sizeof(id->firmware_revision));
+
+ /*
+ * Clean up the model name, serial and
+ * revision numbers.
+ */
+ btrim(id->model, sizeof(id->model));
+ btrim(id->serial_number, sizeof(id->serial_number));
+ btrim(id->firmware_revision, sizeof(id->firmware_revision));
+
+ if (bus != NULL)
+ printf("%s", bus);
+
+ printf(" drive %d: <%s, %s, %s> type %d/%s %s",
+ ad_link->drive, id->model, id->serial_number,
+ id->firmware_revision,
+ id->config.device_type & ATAPI_DEVICE_TYPE_MASK, dtype, fixrem);
+
+ return UNCONF;
+}
+
+
+void
+atapibusattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct atapibus_softc *ab = (struct atapibus_softc *)self;
+ struct bus_link *ab_link_proto = aux;
+ struct atapi_identify ids;
+ struct atapi_identify *id = &ids;
+ struct at_dev_link *ad_link;
+ int drive;
+
+ printf("\n");
+
+ ab_link_proto->atapibus_softc = (caddr_t)ab;
+ ab->b_link = ab_link_proto;
+
+ for (drive = 0; drive < 2 ; drive++) {
+ if (wdc_atapi_get_params(ab_link_proto, drive, id)) {
+#ifdef ATAPI_DEBUG_PROBE
+ printf("%s drive %d: cmdsz 0x%x drqtype 0x%x\n",
+ self->dv_xname, drive,
+ id->config.cmd_drq_rem & ATAPI_PACKET_SIZE_MASK,
+ id->config.cmd_drq_rem & ATAPI_DRQ_MASK);
+#endif
+
+ /*
+ * Allocate a device link and try and attach
+ * a driver to this device. If we fail, free
+ * the link.
+ */
+ ad_link = malloc(sizeof(*ad_link), M_DEVBUF, M_NOWAIT);
+ if (ad_link == NULL) {
+ printf("%s: can't allocate link for drive %d\n",
+ self->dv_xname, drive);
+ continue;
+ }
+
+ /* Fill in link. */
+ ad_link->drive = drive;
+ if (id->config.cmd_drq_rem & ATAPI_PACKET_SIZE_16)
+ ad_link->flags |= ACAP_LEN;
+ ad_link->flags |=
+ (id->config.cmd_drq_rem & ATAPI_DRQ_MASK) << 3;
+ ad_link->bus = ab_link_proto;
+ bcopy(id, &ad_link->id, sizeof(*id));
+
+ /* Try to find a match. */
+ if (config_found(self, ad_link, atapiprint) == NULL)
+ free(ad_link, M_DEVBUF);
+ }
+ }
+}
+
+static void
+bswap (buf, len)
+ char *buf;
+ int len;
+{
+ u_int16_t *p = (u_int16_t *)(buf + len);
+
+ while (--p >= (u_int16_t *)buf)
+ *p = ntohs(*p);
+}
+
+static void
+btrim (buf, len)
+ char *buf;
+ int len;
+{
+ char *p;
+
+ /* Remove the trailing spaces. */
+ for (p = buf; p < buf + len; ++p)
+ if (*p == '\0')
+ *p = ' ';
+
+ for (p = buf + len - 1; p >= buf && *p == ' '; --p)
+ *p = '\0';
+}
+
+int
+atapi_exec_cmd(ad_link, cmd, cmd_size, databuf, datalen, rw, flags)
+ struct at_dev_link *ad_link;
+ void *cmd;
+ int cmd_size;
+ void *databuf;
+ int datalen;
+ long rw;
+ int flags;
+{
+ struct atapi_command_packet *pkt;
+ struct bus_link *b_link = ad_link->bus;
+ int status, s;
+
+ /* Allocate packet. */
+ pkt = atapi_get_pkt(ad_link, flags);
+ if (pkt == NULL)
+ return -1;
+
+ /* Fill it out. */
+ bcopy(cmd, &pkt->cmd_store, cmd_size);
+ pkt->command = &pkt->cmd_store;
+ pkt->command_size = (ad_link->flags & ACAP_LEN) ? 16 : 12;
+ pkt->databuf = databuf;
+ pkt->data_size = datalen;
+ pkt->flags = rw | (flags & 0xff) | (ad_link->flags & 0x0300);
+ pkt->drive = ad_link->drive;
+
+ /* Send it to drive. */
+ wdc_atapi_send_command_packet(b_link, pkt);
+ if ((flags & (A_POLLED | A_NOSLEEP)) == 0) {
+ ATAPI_DEBUG_CMD_PRINT(("atapi_exec_cmd: sleeping\n"));
+
+ s = splbio();
+ while ((pkt->status & ITSDONE) == 0)
+ tsleep(pkt, PRIBIO + 1,"atapicmd", 0);
+ splx(s);
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_exec_cmd: done sleeping\n"));
+
+ status = pkt->status & STATUS_MASK;
+ atapi_free_pkt(pkt);
+ } else {
+ if ((flags & A_POLLED) != 0) {
+ if ((pkt->status & ERROR) && (pkt->error)) {
+ atapi_error(pkt);
+ SILENT_PRINTF(flags,("\n"));
+ }
+ }
+ status = pkt->status & STATUS_MASK;
+ if ((flags & A_POLLED) != 0)
+ atapi_free_pkt(pkt);
+ }
+
+ return status;
+}
+
+int
+atapi_exec_io(ad_link, cmd, cmd_size, bp, flags)
+ struct at_dev_link *ad_link;
+ void *cmd;
+ int cmd_size;
+ struct buf *bp;
+ int flags;
+{
+ struct atapi_command_packet *pkt;
+ struct bus_link *b_link = ad_link->bus;
+
+ /* Allocate a packet. */
+ pkt = atapi_get_pkt(ad_link, flags);
+ if (pkt == NULL) {
+ printf("atapi_exec_io: no pkt\n");
+ return ERROR;
+ }
+
+ /* Fill it in. */
+ bcopy(cmd, &pkt->cmd_store, cmd_size);
+ pkt->command = &pkt->cmd_store;
+ pkt->command_size = (ad_link->flags & ACAP_LEN) ? 16 : 12;
+ pkt->bp = bp;
+ pkt->databuf = bp->b_data;
+ pkt->data_size = bp->b_bcount;
+ pkt->flags = bp->b_flags & (B_READ|B_WRITE) | (flags & 0xff) |
+ (ad_link->flags & 0x0300);
+ pkt->drive = ad_link->drive;
+
+ wdc_atapi_send_command_packet(b_link, pkt);
+ return (pkt->status & STATUS_MASK);
+}
+
+void
+atapi_done(acp)
+ struct atapi_command_packet *acp;
+{
+ struct at_dev_link *ad_link = acp->ad_link;
+ struct buf *bp = acp->bp;
+ int error = 0;
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_done\n"));
+
+ if ((acp->status & ERROR) && (acp->error)) {
+ atapi_error(acp);
+ if (acp->status & RETRY) {
+ if (acp->retries <ATAPI_NRETRIES) {
+ acp->retries++;
+ acp->status = 0;
+ acp->error = 0;
+ SILENT_PRINTF(acp->flags & 0xff,
+ (", retry #%d\n", acp->retries));
+ wdc_atapi_send_command_packet(ad_link->bus,
+ acp);
+ return;
+ } else
+ acp->status = ERROR;
+ }
+ SILENT_PRINTF(acp->flags & 0xff,("\n"));
+ }
+ acp->status |= ITSDONE;
+
+ if (ad_link->done) {
+ ATAPI_DEBUG_CMD_PRINT(("calling private done\n"));
+ error = (*ad_link->done)(acp);
+ if (error == EJUSTRETURN)
+ return;
+ }
+ if (acp->bp == NULL) {
+ ATAPI_DEBUG_CMD_PRINT(("atapidone: wakeup acp\n"));
+ wakeup(acp);
+ return;
+ }
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_done: status %d\n", acp->status));
+
+ switch (acp->status & 0x0f) {
+ case MEDIA_CHANGE:
+ if (ad_link->flags & ADEV_REMOVABLE)
+ ad_link->flags &= ~ADEV_MEDIA_LOADED;
+
+ error = EIO;
+ break;
+
+ case NO_ERROR:
+ error = 0;
+ break;
+
+ case ERROR:
+ case END_OF_MEDIA:
+ default:
+ error = EIO;
+ break;
+ }
+
+ switch (acp->status & 0xf0) {
+ case NOT_READY:
+ case UNIT_ATTENTION:
+ if (ad_link->flags & ADEV_REMOVABLE)
+ ad_link->flags &= ~ADEV_MEDIA_LOADED;
+
+ error = EIO;
+ break;
+ }
+
+ if (error) {
+ bp->b_error = error;
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ } else {
+ bp->b_error = 0;
+ bp->b_resid = acp->data_size;
+ }
+ biodone(bp);
+ atapi_free_pkt(acp);
+}
+
+struct atapi_command_packet *
+atapi_get_pkt(ad_link, flags)
+ struct at_dev_link *ad_link;
+ int flags;
+{
+ struct atapi_command_packet *pkt;
+ int s;
+
+ s = splbio();
+ while (ad_link->openings <= 0) {
+ if (flags & A_NOSLEEP) {
+ splx(s);
+ return 0;
+ }
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_get_pkt: sleeping\n"));
+
+ ad_link->flags |= ADEV_WAITING;
+ (void)tsleep(ad_link, PRIBIO, "getpkt", 0);
+ }
+
+ ad_link->openings--;
+
+ if ((pkt = pkt_free_list.lh_first) != 0) {
+ LIST_REMOVE(pkt, free_list);
+ splx(s);
+ } else {
+ splx(s);
+ pkt = malloc(sizeof(struct atapi_command_packet), M_DEVBUF,
+ ((flags & A_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK));
+ if (pkt == NULL) {
+ printf("atapi_get_pkt: cannot allocate pkt\n");
+ ad_link->openings++;
+ return 0;
+ }
+ }
+
+ bzero(pkt, sizeof(struct atapi_command_packet));
+ pkt->ad_link = ad_link;
+ return pkt;
+}
+
+void
+atapi_free_pkt(pkt)
+ struct atapi_command_packet *pkt;
+{
+ struct at_dev_link *ad_link = pkt->ad_link;
+ int s;
+
+ s = splbio();
+ LIST_INSERT_HEAD(&pkt_free_list, pkt, free_list);
+
+ ad_link->openings++;
+
+ if ((ad_link->flags & ADEV_WAITING) != 0) {
+ ad_link->flags &= ~ADEV_WAITING;
+ wakeup(ad_link);
+ } else {
+ if (ad_link->start) {
+ ATAPI_DEBUG_CMD_PRINT(("atapi_free_pkt: calling private start\n"));
+ (*ad_link->start)((void *)ad_link->device_softc);
+ }
+ }
+ splx(s);
+}
+
+int
+atapi_test_unit_ready(ad_link, flags)
+ struct at_dev_link *ad_link;
+ int flags;
+{
+ int ret;
+ struct test_unit_ready cmd;
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_test_unit_ready: "));
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_TEST_UNIT_READY;
+
+ ret = atapi_exec_cmd(ad_link, &cmd, sizeof(cmd), 0, 0, 0, flags);
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_test_unit_ready: ret %d\n", ret));
+
+ return ret;
+}
+
+int
+atapi_start_stop(ad_link, how, flags)
+ struct at_dev_link *ad_link;
+ int how;
+ int flags;
+{
+ struct start_stop_unit cmd;
+ int ret;
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_start_stop: "));
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_START_STOP_UNIT;
+ cmd.how = how;
+
+ ret = atapi_exec_cmd(ad_link, &cmd, sizeof(cmd), 0,0,0,flags);
+
+ ATAPI_DEBUG_FCTN_PRINT(("ret %d\n", ret));
+
+ return ret;
+}
+
+int
+atapi_prevent(ad_link, how)
+ struct at_dev_link *ad_link;
+ int how;
+{
+ struct prevent_allow_medium_removal cmd;
+ int ret;
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_prevent: "));
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_PREVENT_ALLOW_MEDIUM_REMOVAL;
+ cmd.how = how & 0xff;
+
+ ret = atapi_exec_cmd(ad_link, &cmd, sizeof(cmd), 0,0,0,0);
+
+ ATAPI_DEBUG_FCTN_PRINT(("ret %d\n", ret));
+
+ return ret;
+}
+
+int
+atapi_error(acp)
+ struct atapi_command_packet* acp;
+{
+ int flags, error, ret = -1;
+ struct at_dev_link *ad_link = acp->ad_link;
+
+ flags = acp->flags & 0xff;
+ error = acp->error;
+
+ at_print_addr(ad_link, acp->flags & 0xff);
+
+ if (error & ATAPI_MCR) {
+ SILENT_PRINTF(flags,("media change requested"));
+ acp->status = MEDIA_CHANGE;
+ }
+
+ if (error & ATAPI_ABRT) {
+ SILENT_PRINTF(flags,("command aborted"));
+ acp->status = ERROR;
+ }
+
+ if (error & ATAPI_EOM) {
+ SILENT_PRINTF(flags,("end of media"));
+ acp->status = END_OF_MEDIA;
+ }
+
+ if (error & ATAPI_ILI) {
+ SILENT_PRINTF(flags,("illegal length indication"));
+ acp->status = ERROR;
+ }
+
+ if ((error & 0x0f) == 0)
+ ret = 0;
+
+ atapi_sense(acp, error >> 4, flags);
+
+ if (((flags & A_SILENT) == 0) && (acp->status != NO_ERROR)) {
+ int i;
+ printf(", command:");
+ for (i = 0; i < acp->command_size; i++)
+ printf(" %2x", ((u_int8_t *)acp->command)[i]);
+ }
+
+ return ret;
+}
+
+void
+atapi_sense(acp, sense_key, flags)
+ struct atapi_command_packet *acp;
+ u_int8_t sense_key;
+ u_int8_t flags;
+{
+ struct at_dev_link *ad_link = acp->ad_link;
+
+ switch (sense_key) {
+ case ATAPI_SK_NO_SENSE:
+ break;
+
+ case ATAPI_SK_REC_ERROR:
+ SILENT_PRINTF(flags,("recovered error"));
+ acp->status = 0;
+ break;
+
+ case ATAPI_SK_NOT_READY:
+ SILENT_PRINTF(flags,("not ready"));
+ acp->status = NOT_READY;
+ break;
+
+ case ATAPI_SK_MEDIUM_ERROR:
+ SILENT_PRINTF(flags,("medium error"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_HARDWARE_ERROR:
+ SILENT_PRINTF(flags,("hardware error"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_ILLEGAL_REQUEST:
+ SILENT_PRINTF(flags,("illegal request"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_UNIT_ATTENTION:
+ SILENT_PRINTF(flags,("unit attention"));
+ acp->status = UNIT_ATTENTION;
+ if (ad_link->flags & ADEV_REMOVABLE)
+ ad_link->flags &= ~ADEV_MEDIA_LOADED;
+ break;
+
+ case ATAPI_SK_DATA_PROTECT:
+ SILENT_PRINTF(flags,("data protect"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_ABORTED_COMMAND:
+ SILENT_PRINTF(flags,("aborted command"));
+ acp->status = RETRY;
+ break;
+
+ case ATAPI_SK_MISCOMPARE:
+ SILENT_PRINTF(flags,("miscompare"));
+ acp->status = ERROR;
+ break;
+
+ default:
+ SILENT_PRINTF(flags,("unexpected sense key %02x", sense_key));
+ acp->status = ERROR;
+ }
+}
+
+void
+at_print_addr(ad_link, flags)
+ struct at_dev_link *ad_link;
+ u_int8_t flags;
+{
+
+ if (flags & A_SILENT)
+ return;
+
+ printf("%s(%s:%d): ", ad_link->device_softc ?
+ ((struct device *)ad_link->device_softc)->dv_xname : "probe",
+ ((struct device *)ad_link->bus->wdc_softc)->dv_xname,
+ ad_link->drive);
+}