diff options
author | Jason Downs <downsj@cvs.openbsd.org> | 1996-06-08 09:12:48 +0000 |
---|---|---|
committer | Jason Downs <downsj@cvs.openbsd.org> | 1996-06-08 09:12:48 +0000 |
commit | fdc68389b9dfe6e5d35bc6cb6b180e9daf9e61e2 (patch) | |
tree | a4460d2ec87ff4da7cc88b4cae0ebcf64034dc98 /sys/dev/atapi/atapiconf.c | |
parent | 9a887c22a316f9b94faed82ec7af6c138ff71262 (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.c | 676 |
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); +} |