/* $OpenBSD: openbsd.c,v 1.3 2008/05/01 09:05:12 fgsch Exp $ */ /* $NetBSD: _cdio_netbsd.c,v 1.4 2005/05/31 17:05:36 drochner Exp $ */ /* * Copyright (c) 2008 Federico G. Schwindt * Copyright (c) 2003 Matthias Drochner * 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. * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "cdio_assert.h" #include "cdio_private.h" #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_CDIO_DEVICE "/dev/rcd0c" typedef struct { generic_img_private_t gen; struct ioc_toc_header tochdr; struct cd_toc_entry tocent[CDIO_CD_MAX_TRACKS + 1]; } _img_private_t; static driver_return_code_t run_mmc_cmd_openbsd(void *p_user_data, unsigned int i_timeout_ms, unsigned int i_cdb, const mmc_cdb_t *p_cdb, cdio_mmc_direction_t e_direction, unsigned int i_buf, void *p_buf) { const _img_private_t *p_env = p_user_data; scsireq_t req; memset(&req, 0, sizeof(req)); memcpy(&req.cmd[0], p_cdb, i_cdb); req.cmdlen = i_cdb; req.datalen = i_buf; req.databuf = p_buf; req.timeout = i_timeout_ms; req.flags = e_direction == SCSI_MMC_DATA_READ ? SCCMD_READ : SCCMD_WRITE; if (ioctl(p_env->gen.fd, SCIOCCOMMAND, &req) < 0) { cdio_info("ioctl SCIOCCOMMAND failed: %s\n", strerror(errno)); return (DRIVER_OP_ERROR); } if (req.retsts != SCCMD_OK) { cdio_info("unexpected status for SCIOCCOMMAND %#x: %#x\n", req.cmd[0], req.retsts); return (DRIVER_OP_ERROR); } return (DRIVER_OP_SUCCESS); } static driver_return_code_t audio_get_volume_openbsd(void *p_user_data, cdio_audio_volume_t *p_volume) { const _img_private_t *p_env = p_user_data; return (ioctl(p_env->gen.fd, CDIOCGETVOL, p_volume)); } static driver_return_code_t audio_pause_openbsd(void *p_user_data) { const _img_private_t *p_env = p_user_data; return (ioctl(p_env->gen.fd, CDIOCPAUSE)); } static driver_return_code_t audio_play_msf_openbsd(void *p_user_data, msf_t *p_start_msf, msf_t *p_end_msf) { const _img_private_t *p_env = p_user_data; struct ioc_play_msf a; a.start_m = cdio_from_bcd8(p_start_msf->m); a.start_s = cdio_from_bcd8(p_start_msf->s); a.start_f = cdio_from_bcd8(p_start_msf->f); a.end_m = cdio_from_bcd8(p_end_msf->m); a.end_s = cdio_from_bcd8(p_end_msf->s); a.end_f = cdio_from_bcd8(p_end_msf->f); return (ioctl(p_env->gen.fd, CDIOCPLAYMSF, (char *)&a)); } #if !USE_MMC_SUBCHANNEL static driver_return_code_t audio_read_subchannel_openbsd(void *p_user_data, cdio_subchannel_t *subchannel) { const _img_private_t *p_env = p_user_data; struct ioc_read_subchannel s; struct cd_sub_channel_info data; bzero(&s, sizeof(s)); s.data = &data; s.data_len = sizeof(data); s.address_format = CD_MSF_FORMAT; s.data_format = CD_CURRENT_POSITION; if (ioctl(p_env->gen.fd, CDIOCREADSUBCHANNEL, &s) != -1) { subchannel->control = s.data->what.position.control; subchannel->track = s.data->what.position.track_number; subchannel->index = s.data->what.position.index_number; subchannel->abs_addr.m = cdio_to_bcd8(s.data->what.position.absaddr.msf.minute); subchannel->abs_addr.s = cdio_to_bcd8(s.data->what.position.absaddr.msf.second); subchannel->abs_addr.f = cdio_to_bcd8(s.data->what.position.absaddr.msf.frame); subchannel->rel_addr.m = cdio_to_bcd8(s.data->what.position.reladdr.msf.minute); subchannel->rel_addr.s = cdio_to_bcd8(s.data->what.position.reladdr.msf.second); subchannel->rel_addr.f = cdio_to_bcd8(s.data->what.position.reladdr.msf.frame); subchannel->audio_status = s.data->header.audio_status; return (DRIVER_OP_SUCCESS); } else { cdio_info("ioctl CDIOCREADSUBCHANNEL failed: %s\n", strerror(errno)); return (DRIVER_OP_ERROR); } } #endif static driver_return_code_t audio_resume_openbsd(void *p_user_data) { const _img_private_t *p_env = p_user_data; return (ioctl(p_env->gen.fd, CDIOCRESUME)); } static driver_return_code_t audio_set_volume_openbsd(void *p_user_data, cdio_audio_volume_t *p_volume) { const _img_private_t *p_env = p_user_data; return (ioctl(p_env->gen.fd, CDIOCSETVOL, p_volume)); } static driver_return_code_t audio_stop_openbsd(void *p_user_data) { const _img_private_t *p_env = p_user_data; return (ioctl(p_env->gen.fd, CDIOCSTOP)); } static driver_return_code_t eject_media_openbsd(void *p_user_data) { _img_private_t *p_env = p_user_data; driver_return_code_t ret = DRIVER_OP_SUCCESS; bool was_open = false; if (p_env->gen.fd == -1) p_env->gen.fd = open(p_env->gen.source_name, O_RDONLY|O_NONBLOCK, 0); else was_open = true; if (p_env->gen.fd == -1) return (DRIVER_OP_ERROR); if (ioctl(p_env->gen.fd, CDIOCALLOW) == -1) cdio_info("ioctl CDIOCALLOW failed: %s\n", strerror(errno)); if (ioctl(p_env->gen.fd, CDIOCEJECT) == -1) { cdio_info("ioctl CDIOCEJECT failed: %s\n", strerror(errno)); ret = DRIVER_OP_ERROR; } if (!was_open) { close(p_env->gen.fd); p_env->gen.fd = -1; } return (ret); } static const char * get_arg_openbsd(void *p_user_data, const char key[]) { _img_private_t *p_env = p_user_data; if (!strcmp(key, "source")) { return (p_env->gen.source_name); } else if (!strcmp(key, "access-mode")) { return ("READ_CD"); } return (NULL); } static bool read_toc_openbsd(void *p_user_data) { _img_private_t *p_env = p_user_data; struct ioc_read_toc_entry req; if (ioctl(p_env->gen.fd, CDIOREADTOCHEADER, &p_env->tochdr) == -1) { cdio_info("error in ioctl CDIOREADTOCHEADER: %s\n", strerror(errno)); return (false); } p_env->gen.i_first_track = p_env->tochdr.starting_track; p_env->gen.i_tracks = (p_env->tochdr.ending_track - p_env->tochdr.starting_track) + 1; req.address_format = CD_LBA_FORMAT; req.starting_track = p_env->gen.i_first_track; req.data_len = sizeof(p_env->tocent); req.data = p_env->tocent; if (ioctl(p_env->gen.fd, CDIOREADTOCENTRIES, &req) == -1) { cdio_info("error in ioctl CDIOREADTOCENTRIES: %s\n", strerror(errno)); return (false); } p_env->gen.toc_init = true; return (true); } static lba_t get_track_lba_openbsd(void *p_user_data, track_t i_track) { _img_private_t *p_env = p_user_data; if (!p_env->gen.toc_init) read_toc_openbsd(p_env); if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->gen.i_first_track + p_env->gen.i_tracks; if (!p_env->gen.toc_init || i_track > (p_env->gen.i_first_track + p_env->gen.i_tracks) || i_track < p_env->gen.i_first_track) return (CDIO_INVALID_LBA); return (p_env->tocent[i_track - p_env->gen.i_first_track].addr.lba + CDIO_PREGAP_SECTORS); } static lsn_t get_disc_last_lsn_openbsd(void *user_data) { return (get_track_lba_openbsd(user_data, CDIO_CDROM_LEADOUT_TRACK)); } static driver_return_code_t get_last_session_openbsd(void *p_user_data, lsn_t *i_last_session) { const _img_private_t *p_env = p_user_data; int addr; if (ioctl(p_env->gen.fd, CDIOREADMSADDR, &addr) == 0) { *i_last_session = addr; return (DRIVER_OP_SUCCESS); } else { cdio_info("ioctl CDIOREADMSADDR failed: %s\n", strerror(errno)); return (DRIVER_OP_ERROR); } } static track_format_t get_track_format_openbsd(void *p_user_data, track_t i_track) { _img_private_t *p_env = p_user_data; if (!p_env) return (TRACK_FORMAT_ERROR); if (!p_env->gen.toc_init) read_toc_openbsd(p_env); if (!p_env->gen.toc_init || i_track > (p_env->gen.i_first_track + p_env->gen.i_tracks) || i_track < p_env->gen.i_first_track) return (TRACK_FORMAT_ERROR); if (p_env->tocent[i_track - 1].control & CDIO_CDROM_DATA_TRACK) return (TRACK_FORMAT_DATA); else return (TRACK_FORMAT_AUDIO); } static bool get_track_green_openbsd(void *user_data, track_t i_track) { return (get_track_format_openbsd(user_data, i_track) == TRACK_FORMAT_XA); } static int read_audio_sectors_openbsd(void *p_user_data, void *p_buf, lsn_t i_lsn, unsigned int i_blocks) { _img_private_t *p_env = p_user_data; return (mmc_read_sectors(p_env->gen.cdio, p_buf, i_lsn, CDIO_MMC_READ_TYPE_CDDA, i_blocks)); } static int read_mode2_sector_openbsd(void *p_user_data, void *p_buf, lsn_t i_lsn, bool b_mode2_form2) { scsireq_t req; _img_private_t *p_env = p_user_data; char buf[M2RAW_SECTOR_SIZE] = { 0, }; memset(&req, 0, sizeof(req)); req.cmd[0] = CDIO_MMC_GPCMD_READ_CD; req.cmd[2] = (i_lsn >> 24) & 0xff; req.cmd[3] = (i_lsn >> 16) & 0xff; req.cmd[4] = (i_lsn >> 8) & 0xff; req.cmd[5] = (i_lsn >> 0) & 0xff; req.cmd[8] = 1; req.cmd[9] = 0x58; /* subheader + userdata + ECC */ req.cmdlen = 10; req.datalen = M2RAW_SECTOR_SIZE; req.databuf = buf; req.timeout = 10000; req.flags = SCCMD_READ; if (ioctl(p_env->gen.fd, SCIOCCOMMAND, &req) < 0) { cdio_info("ioctl SCIOCCOMMAND failed: %s\n", strerror(errno)); return (DRIVER_OP_ERROR); } if (req.retsts != SCCMD_OK) { cdio_info("unexpected status for SCIOCCOMMAND %#x: %#x\n", req.cmd[0], req.retsts); return (DRIVER_OP_ERROR); } if (b_mode2_form2) memcpy(p_buf, buf, M2RAW_SECTOR_SIZE); else memcpy(p_buf, buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE); return (DRIVER_OP_SUCCESS); } static int read_mode2_sectors_openbsd(void *p_user_data, void *p_buf, lsn_t i_lsn, bool b_mode2_form2, unsigned int i_blocks) { uint16_t i_blocksize = b_mode2_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; unsigned int i; int retval; for (i = 0; i < i_blocks; i++) { if ((retval = read_mode2_sector_openbsd(p_user_data, ((char *)p_buf) + (i_blocksize * i), i_lsn + i, b_mode2_form2))) return (retval); } return (DRIVER_OP_SUCCESS); } static int set_arg_openbsd(void *p_user_data, const char key[], const char value[]) { _img_private_t *p_env = p_user_data; if (!strcmp(key, "source")) { if (!value) return (DRIVER_OP_ERROR); free(p_env->gen.source_name); p_env->gen.source_name = strdup(value); } else if (!strcmp(key, "access-mode")) { if (strcmp(value, "READ_CD")) cdio_info("unknown access type: %s ignored.", value); } else return (DRIVER_OP_ERROR); return (DRIVER_OP_SUCCESS); } static cdio_funcs_t _funcs = { .audio_get_volume = audio_get_volume_openbsd, .audio_pause = audio_pause_openbsd, .audio_play_msf = audio_play_msf_openbsd, .audio_play_track_index = NULL, #if USE_MMC_SUBCHANNEL .audio_read_subchannel = audio_read_subchannel_mmc, #else .audio_read_subchannel = audio_read_subchannel_openbsd, #endif .audio_resume = audio_resume_openbsd, .audio_set_volume = audio_set_volume_openbsd, .audio_stop = audio_stop_openbsd, .eject_media = eject_media_openbsd, .free = cdio_generic_free, .get_arg = get_arg_openbsd, .get_blocksize = NULL, .get_cdtext = get_cdtext_generic, .get_default_device = cdio_get_default_device_openbsd, .get_devices = cdio_get_devices_openbsd, .get_disc_last_lsn = get_disc_last_lsn_openbsd, .get_discmode = get_discmode_generic, .get_drive_cap = get_drive_cap_mmc, .get_first_track_num = get_first_track_num_generic, .get_hwinfo = NULL, .get_last_session = get_last_session_openbsd, .get_media_changed = get_media_changed_mmc, .get_mcn = get_mcn_mmc, .get_num_tracks = get_num_tracks_generic, .get_track_channels = get_track_channels_generic, .get_track_copy_permit = get_track_copy_permit_generic, .get_track_lba = get_track_lba_openbsd, .get_track_format = get_track_format_openbsd, .get_track_green = get_track_green_openbsd, .get_track_msf = NULL, .get_track_preemphasis = get_track_preemphasis_generic, .lseek = cdio_generic_lseek, .read = cdio_generic_read, .read_audio_sectors = read_audio_sectors_openbsd, .read_data_sectors = read_data_sectors_generic, .read_mode2_sector = read_mode2_sector_openbsd, .read_mode2_sectors = read_mode2_sectors_openbsd, .read_mode1_sector = NULL, .read_mode1_sectors = NULL, .read_toc = read_toc_openbsd, .run_mmc_cmd = run_mmc_cmd_openbsd, .set_arg = set_arg_openbsd, .set_blocksize = NULL, .set_speed = NULL, }; bool cdio_have_openbsd(void) { return (true); } CdIo * cdio_open_openbsd(const char *source_name) { CdIo *ret; _img_private_t *_data; _data = calloc(1, sizeof(_img_private_t)); _data->gen.init = false; _data->gen.toc_init = false; _data->gen.fd = -1; set_arg_openbsd(_data, "source", (source_name ? source_name : DEFAULT_CDIO_DEVICE)); if (source_name && !cdio_is_device_generic(source_name)) goto error; ret = cdio_new(_data, &_funcs); if (!ret) goto error; if (!cdio_generic_init(_data, O_RDONLY)) goto error; return (ret); error: cdio_generic_free(_data); return (NULL); } CdIo * cdio_open_am_openbsd(const char *source_name, const char *am) { return (cdio_open_openbsd(source_name)); } char * cdio_get_default_device_openbsd() { return (strdup(DEFAULT_CDIO_DEVICE)); } char ** cdio_get_devices_openbsd(void) { char drive[40]; char **drives = NULL; unsigned int num_drives = 0; int cdfd; int n; for (n = 0; n <= 9; n++) { snprintf(drive, sizeof(drive), "/dev/rcd%dc", n); if (!cdio_is_device_quiet_generic(drive)) continue; if ((cdfd = open(drive, O_RDONLY|O_NONBLOCK, 0)) == -1) continue; close(cdfd); cdio_add_device_list(&drives, drive, &num_drives); } cdio_add_device_list(&drives, NULL, &num_drives); return (drives); } driver_return_code_t close_tray_openbsd(const char *psz_device) { return (DRIVER_OP_UNSUPPORTED); }