/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include "transport.h" #include "mmc.h" #include "util.h" #include "main.h" int test_unit_ready(int fd) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; /* give length of cdb structure */ scmd->uscsi_cdblen = 6; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int inquiry(int fd, uchar_t *inq) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[0] = INQUIRY_CMD; scmd->uscsi_cdb[4] = INQUIRY_DATA_LENGTH; scmd->uscsi_cdblen = 6; scmd->uscsi_bufaddr = (char *)inq; scmd->uscsi_buflen = INQUIRY_DATA_LENGTH; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int read_capacity(int fd, uchar_t *capbuf) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[0] = READ_CAP_CMD; scmd->uscsi_cdblen = 10; scmd->uscsi_bufaddr = (char *)capbuf; scmd->uscsi_buflen = 8; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int mode_sense(int fd, uchar_t pc, int dbd, int page_len, uchar_t *buffer) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_buflen = page_len; scmd->uscsi_bufaddr = (char *)buffer; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdblen = 0xa; scmd->uscsi_cdb[0] = MODE_SENSE_10_CMD; if (dbd) { /* don't return any block descriptors */ scmd->uscsi_cdb[1] = 0x8; } /* the page code we want */ scmd->uscsi_cdb[2] = pc; /* allocation length */ scmd->uscsi_cdb[7] = (page_len >> 8) & 0xff; scmd->uscsi_cdb[8] = page_len & 0xff; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int mode_select(int fd, int page_len, uchar_t *buffer) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT; scmd->uscsi_buflen = page_len; scmd->uscsi_bufaddr = (char *)buffer; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdblen = 0xa; /* mode select (10) command */ scmd->uscsi_cdb[0] = MODE_SELECT_10_CMD; scmd->uscsi_cdb[1] = 0x10; /* parameter list length */ scmd->uscsi_cdb[7] = (page_len >> 8) & 0xff; scmd->uscsi_cdb[8] = page_len & 0xff; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int read_track_info(int fd, int trackno, uchar_t *ti) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[0] = READ_TRACK_CMD; /* tell device we are giving it a track number */ scmd->uscsi_cdb[1] = 1; /* track number to read */ if (trackno == -1) if (device_type == CD_RW) { ((uchar_t *)scmd->uscsi_cdb)[5] = 0xff; } else { /* only 1 track is allowed on DVD media */ scmd->uscsi_cdb[1] = 0; ((uchar_t *)scmd->uscsi_cdb)[5] = 0; } else scmd->uscsi_cdb[5] = (uchar_t)trackno; scmd->uscsi_cdb[8] = TRACK_INFO_SIZE; scmd->uscsi_cdblen = 10; scmd->uscsi_bufaddr = (char *)ti; scmd->uscsi_buflen = TRACK_INFO_SIZE; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int read_toc(int fd, int format, int trackno, int buflen, uchar_t *buf) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[0] = READ_TOC_CMD; scmd->uscsi_cdb[2] = format & 0xf; scmd->uscsi_cdb[6] = trackno; scmd->uscsi_cdb[8] = buflen & 0xff; scmd->uscsi_cdb[7] = (buflen >> 8) & 0xff; scmd->uscsi_cdblen = 10; scmd->uscsi_bufaddr = (char *)buf; scmd->uscsi_buflen = buflen; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); /* Fix for old SONY drives */ if ((format == 0) && (buflen == 4) && (buf[0] == 0) && (buf[1] == 2)) { uint16_t toc_size; toc_size = (((uint16_t)(buf[3] + 1)) * 8) + 2; load_scsi16(buf, toc_size); } return (1); } int read_header(int fd, uint32_t lba, uchar_t *buf) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[0] = READ_HDR_CMD; /* Logical block address */ load_scsi32(&scmd->uscsi_cdb[2], lba); /* allocation length */ scmd->uscsi_cdb[8] = 8; scmd->uscsi_cdblen = 10; scmd->uscsi_bufaddr = (char *)buf; scmd->uscsi_buflen = 8; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int read_disc_info(int fd, uchar_t *di) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[0] = READ_INFO_CMD; scmd->uscsi_cdb[8] = DISC_INFO_BLOCK_SIZE; scmd->uscsi_cdblen = 10; scmd->uscsi_bufaddr = (char *)di; scmd->uscsi_buflen = DISC_INFO_BLOCK_SIZE; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } /* Get information about the Logical Unit's capabilities */ int get_configuration(int fd, uint16_t feature, int bufsize, uchar_t *buf) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; /* Set OPERATION CODE in CDB */ scmd->uscsi_cdb[0] = GET_CONFIG_CMD; /* * Set RT field in CDB, currently need at most one * Feature Descriptor */ scmd->uscsi_cdb[1] = 0x2; /* Set Starting Feature Number in CDB */ scmd->uscsi_cdb[2] = (feature >> 8) & 0xff; scmd->uscsi_cdb[3] = feature & 0xff; /* Set Allocation Length in CDB */ scmd->uscsi_cdb[7] = (bufsize >> 8) & 0xff; scmd->uscsi_cdb[8] = bufsize & 0xff; scmd->uscsi_cdblen = 10; scmd->uscsi_bufaddr = (char *)buf; scmd->uscsi_buflen = bufsize; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int read10(int fd, uint32_t start_blk, uint16_t nblk, uchar_t *buf, uint32_t bufsize) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[0] = READ_10_CMD; load_scsi32(&scmd->uscsi_cdb[2], start_blk); scmd->uscsi_cdb[8] = nblk & 0xff; scmd->uscsi_cdb[7] = (nblk >> 8) & 0xff; scmd->uscsi_cdblen = 10; scmd->uscsi_bufaddr = (char *)buf; scmd->uscsi_buflen = bufsize; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int write10(int fd, uint32_t start_blk, uint16_t nblk, uchar_t *buf, uint32_t bufsize) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT; /* * Some DVD drives take longer to write than * the standard time, since they tend to generate * the media TOC on the fly when the cache is full */ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT * 3; scmd->uscsi_cdb[0] = WRITE_10_CMD; load_scsi32(&scmd->uscsi_cdb[2], start_blk); scmd->uscsi_cdb[8] = nblk & 0xff; scmd->uscsi_cdb[7] = (nblk >> 8) & 0xff; scmd->uscsi_cdblen = 10; scmd->uscsi_bufaddr = (char *)buf; scmd->uscsi_buflen = bufsize; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int close_track(int fd, int trackno, int close_session, int immediate) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_SILENT; scmd->uscsi_cdb[0] = CLOSE_TRACK_CMD; if (immediate) { scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[1] = 1; } else { scmd->uscsi_timeout = 240; } if ((close_session) || (device_type == DVD_PLUS) || (device_type == DVD_PLUS_W)) { /* close the session */ scmd->uscsi_cdb[2] = 2; } else { /* Close the track but leave session open */ scmd->uscsi_cdb[2] = 1; scmd->uscsi_cdb[5] = trackno & 0xff; } /* * DVD+R media are already formatted, we are using * a special case to notify that drive to close * track/session and null-fill the remaining space. */ if (device_type == DVD_PLUS) { scmd->uscsi_cdb[5] = 1; /* only 1 track */ if (close_session) { scmd->uscsi_cdb[2] = 6; /* session */ } else { scmd->uscsi_cdb[2] = 1; /* track */ } } scmd->uscsi_cdblen = 10; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int blank_disc(int fd, int type, int immediate) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_SILENT; if (immediate) { scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[1] = 0x10; } else { scmd->uscsi_timeout = 0x12c0; } ((uchar_t *)scmd->uscsi_cdb)[0] = BLANK_CMD; /* tell it to blank the last session or all of the disk */ scmd->uscsi_cdb[1] |= type & 0x07; scmd->uscsi_cdblen = 12; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int read_cd(int fd, uint32_t start_blk, uint16_t nblk, uchar_t sector_type, uchar_t *buf, uint32_t bufsize) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; ((uchar_t *)scmd->uscsi_cdb)[0] = READ_CD_CMD; scmd->uscsi_cdb[1] = (sector_type & 0x7) << 2; scmd->uscsi_cdb[5] = start_blk & 0xff; scmd->uscsi_cdb[4] = (start_blk >> 8) & 0xff; scmd->uscsi_cdb[3] = (start_blk >> 16) & 0xff; scmd->uscsi_cdb[2] = (start_blk >> 24) & 0xff; scmd->uscsi_cdb[8] = nblk & 0xff; scmd->uscsi_cdb[7] = (nblk >> 8) & 0xff; scmd->uscsi_cdb[9] = 0x10; scmd->uscsi_cdblen = 12; scmd->uscsi_bufaddr = (char *)buf; scmd->uscsi_buflen = bufsize; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int load_unload(int fd, int load) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[0] = START_STOP_CMD; if (load == 0) { /* unload medium */ scmd->uscsi_cdb[4] = 2; } else { /* load medium */ scmd->uscsi_cdb[4] = 3; } scmd->uscsi_cdblen = 6; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int prevent_allow_mr(int fd, int op) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[0] = PREVENT_ALLOW_CMD; if (!op) { /* prevent */ scmd->uscsi_cdb[4] = 1; } scmd->uscsi_cdblen = 6; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int set_cd_speed(int fd, uint16_t read_speed, uint16_t write_speed) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdblen = 0xc; ((uchar_t *)scmd->uscsi_cdb)[0] = SET_CD_SPEED; scmd->uscsi_cdb[2] = (read_speed >> 8) & 0xff; scmd->uscsi_cdb[3] = read_speed & 0xff; scmd->uscsi_cdb[4] = (write_speed >> 8) & 0xff; scmd->uscsi_cdb[5] = write_speed & 0xff; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int get_performance(int fd, int get_write_performance, uchar_t *perf) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_buflen = GET_PERF_DATA_LEN; scmd->uscsi_bufaddr = (char *)perf; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdblen = 0xc; ((uchar_t *)scmd->uscsi_cdb)[0] = GET_PERFORMANCE_CMD; scmd->uscsi_cdb[1] = 0x10; if (get_write_performance) scmd->uscsi_cdb[1] |= 4; scmd->uscsi_cdb[9] = 2; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int set_streaming(int fd, uchar_t *buf) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT; scmd->uscsi_buflen = SET_STREAM_DATA_LEN; scmd->uscsi_bufaddr = (char *)buf; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdblen = 0xc; ((uchar_t *)scmd->uscsi_cdb)[0] = STREAM_CMD; scmd->uscsi_cdb[10] = SET_STREAM_DATA_LEN; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int rezero_unit(int fd) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdblen = 0x6; scmd->uscsi_cdb[0] = REZERO_UNIT_CMD; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int start_stop(int fd, int start) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdblen = 0x6; scmd->uscsi_cdb[0] = START_STOP_CMD; if (start) { scmd->uscsi_cdb[4] = 1; } if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } int flush_cache(int fd) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdblen = 10; scmd->uscsi_cdb[0] = SYNC_CACHE_CMD; if (device_type != CD_RW) { scmd->uscsi_cdb[1] = 0x2; /* Immediate */ } if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } /* * used for DVD- to reserve the size we want to write. * This is used by the drive to generate a TOC. */ int set_reservation(int fd, ulong_t size) { struct uscsi_cmd *scmd; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdb[0] = SET_RESERVATION_CMD; scmd->uscsi_cdblen = 10; scmd->uscsi_cdb[5] = (uchar_t)(size >> 24); scmd->uscsi_cdb[6] = (uchar_t)(size >> 16); scmd->uscsi_cdb[7] = (uchar_t)(size >> 8); scmd->uscsi_cdb[8] = (uchar_t)size; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } /* * Used for DVD+RW media to prepare the disk to write. * It will also be used for packet mode writing when * it becomes supported. */ int format_media(int fd) { struct uscsi_cmd *scmd; uchar_t buf[20]; (void) memset(buf, 0, 20); scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdblen = 12; scmd->uscsi_cdb[0] = READ_FORMAT_CAP_CMD; scmd->uscsi_cdb[8] = 0x14; /* buffer length */ scmd->uscsi_buflen = 20; scmd->uscsi_bufaddr = (char *)buf; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); /* RE-use cap structure */ scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdblen = 6; scmd->uscsi_cdb[0] = FORMAT_UNIT_CMD; /* full format */ scmd->uscsi_cdb[1] = 0x11; scmd->uscsi_buflen = 12; buf[1] = 0x82; /* immediate and FOV */ buf[3] = 8; /* descriptor length */ buf[8] = 0x98; /* type = 26 DVD+RW format */ buf[10] = 0; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); return (1); } /* * Prefered method of reading the media size. This is * the only supported method on several newer drives. */ uint32_t read_format_capacity(int fd, uint_t *bsize) { struct uscsi_cmd *scmd; uint32_t filesize; char buf[20]; scmd = get_uscsi_cmd(); scmd->uscsi_flags = USCSI_READ|USCSI_SILENT; scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT; scmd->uscsi_cdblen = 12; scmd->uscsi_cdb[0] = READ_FORMAT_CAP_CMD; scmd->uscsi_cdb[8] = 0x14; scmd->uscsi_buflen = 20; scmd->uscsi_bufaddr = buf; if ((uscsi_error = uscsi(fd, scmd)) < 0) return (0); filesize = (uint32_t)(((uchar_t)buf[4] << 24) + ((uchar_t)buf[5] << 16) + ((uchar_t)buf[6] << 8) + (uchar_t)buf[7]); *bsize = (uint16_t)(((uchar_t)buf[10] << 8) + (uchar_t)buf[11]); return (filesize); }