17332Sjkh/*- 27332Sjkh * Copyright (c) 1995 Mikael Hybsch 37332Sjkh * All rights reserved. 47332Sjkh * 57332Sjkh * Portions of this file are copied from mcd.c 67332Sjkh * which has the following copyrights: 77332Sjkh * 87332Sjkh * Copyright 1993 by Holger Veit (data part) 97332Sjkh * Copyright 1993 by Brian Moore (audio part) 107332Sjkh * Changes Copyright 1993 by Gary Clark II 117332Sjkh * Changes Copyright (C) 1994 by Andrew A. Chernov 127332Sjkh * 137332Sjkh * Rewrote probe routine to work on newer Mitsumi drives. 147332Sjkh * Additional changes (C) 1994 by Jordan K. Hubbard 157332Sjkh * 167332Sjkh * All rights reserved. 177332Sjkh * 187332Sjkh * Redistribution and use in source and binary forms, with or without 197332Sjkh * modification, are permitted provided that the following conditions 207332Sjkh * are met: 217332Sjkh * 1. Redistributions of source code must retain the above copyright 227332Sjkh * notice, this list of conditions and the following disclaimer 237332Sjkh * in this position and unchanged. 247332Sjkh * 2. Redistributions in binary form must reproduce the above copyright 257332Sjkh * notice, this list of conditions and the following disclaimer in the 267332Sjkh * documentation and/or other materials provided with the distribution. 277332Sjkh * 3. The name of the author may not be used to endorse or promote products 2897748Sschweikh * derived from this software without specific prior written permission 297332Sjkh * 307332Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 317332Sjkh * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 327332Sjkh * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 337332Sjkh * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 347332Sjkh * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 357332Sjkh * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 367332Sjkh * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 377332Sjkh * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 387332Sjkh * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 397332Sjkh * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 407332Sjkh * 417332Sjkh */ 427332Sjkh 43119419Sobrien#include <sys/cdefs.h> 44119419Sobrien__FBSDID("$FreeBSD: stable/11/sys/dev/scd/scd.c 349629 2019-07-03 00:12:50Z markj $"); 457332Sjkh 46119419Sobrien 47106451Smdodd#undef SCD_DEBUG 487332Sjkh 497332Sjkh#include <sys/param.h> 507332Sjkh#include <sys/systm.h> 5161011Speter#include <sys/kernel.h> 527332Sjkh#include <sys/conf.h> 53106450Smdodd#include <sys/fcntl.h> 5460041Sphk#include <sys/bio.h> 557332Sjkh#include <sys/cdio.h> 56106450Smdodd#include <sys/disk.h> 5761011Speter#include <sys/bus.h> 587369Sbde 597332Sjkh#include <machine/stdarg.h> 607332Sjkh 61106449Smdodd#include <machine/bus.h> 62106449Smdodd#include <machine/resource.h> 63106449Smdodd#include <sys/rman.h> 647332Sjkh 65106449Smdodd#include <isa/isavar.h> 6612502Sjulian 67106449Smdodd#include <dev/scd/scdreg.h> 68106449Smdodd#include <dev/scd/scdvar.h> 69106449Smdodd 707332Sjkh/* flags */ 717332Sjkh#define SCDOPEN 0x0001 /* device opened */ 727332Sjkh#define SCDVALID 0x0002 /* parameters loaded */ 737332Sjkh#define SCDINIT 0x0004 /* device is init'd */ 747332Sjkh#define SCDPROBING 0x0020 /* probing */ 757332Sjkh#define SCDTOC 0x0100 /* already read toc */ 767332Sjkh#define SCDMBXBSY 0x0200 /* local mbx is busy */ 777332Sjkh#define SCDSPINNING 0x0400 /* drive is spun up */ 787332Sjkh 797332Sjkh#define SCD_S_BEGIN 0 807332Sjkh#define SCD_S_BEGIN1 1 817332Sjkh#define SCD_S_WAITSTAT 2 827332Sjkh#define SCD_S_WAITFIFO 3 837332Sjkh#define SCD_S_WAITSPIN 4 847332Sjkh#define SCD_S_WAITREAD 5 857332Sjkh#define SCD_S_WAITPARAM 6 867332Sjkh 877332Sjkh#define RDELAY_WAIT 300 887332Sjkh#define RDELAY_WAITREAD 300 897332Sjkh 907332Sjkh#define SCDBLKSIZE 2048 917332Sjkh 927332Sjkh#ifdef SCD_DEBUG 9311872Sphk static int scd_debuglevel = SCD_DEBUG; 94106449Smdodd# define XDEBUG(sc, level, fmt, args...) \ 95106449Smdodd do { \ 96106449Smdodd if (scd_debuglevel >= level) \ 97106449Smdodd device_printf(sc->dev, fmt, ## args); \ 98106449Smdodd } while (0) 997332Sjkh#else 100106449Smdodd# define XDEBUG(sc, level, fmt, args...) 1017332Sjkh#endif 1027332Sjkh 103106449Smdodd#define IS_ATTENTION(sc) ((SCD_READ(sc, IREG_STATUS) & SBIT_ATTENTION) != 0) 104106449Smdodd#define IS_BUSY(sc) ((SCD_READ(sc, IREG_STATUS) & SBIT_BUSY) != 0) 105106449Smdodd#define IS_DATA_RDY(sc) ((SCD_READ(sc, IREG_STATUS) & SBIT_DATA_READY) != 0) 106106449Smdodd#define STATUS_BIT(sc, bit) ((SCD_READ(sc, IREG_STATUS) & (bit)) != 0) 107106449Smdodd#define FSTATUS_BIT(sc, bit) ((SCD_READ(sc, IREG_FSTATUS) & (bit)) != 0) 1087332Sjkh 1097332Sjkh/* prototypes */ 1107332Sjkhstatic void hsg2msf(int hsg, bcd_t *msf); 1117332Sjkhstatic int msf2hsg(bcd_t *msf); 1127332Sjkh 113106449Smdoddstatic void process_attention(struct scd_softc *); 114106449Smdoddstatic int waitfor_status_bits(struct scd_softc *, int bits_set, int bits_clear); 115106449Smdoddstatic int send_cmd(struct scd_softc *, u_char cmd, u_int nargs, ...); 116106449Smdoddstatic void init_drive(struct scd_softc *); 117106449Smdoddstatic int spin_up(struct scd_softc *); 118106449Smdoddstatic int read_toc(struct scd_softc *); 119106449Smdoddstatic int get_result(struct scd_softc *, int result_len, u_char *result); 120106449Smdoddstatic void print_error(struct scd_softc *, int errcode); 1217332Sjkh 122106449Smdoddstatic void scd_start(struct scd_softc *); 123274679Sjhbstatic void scd_timeout(void *); 124106449Smdoddstatic void scd_doread(struct scd_softc *, int state, struct scd_mbx *mbxin); 1257332Sjkh 126106449Smdoddstatic int scd_eject(struct scd_softc *); 127106449Smdoddstatic int scd_stop(struct scd_softc *); 128106449Smdoddstatic int scd_pause(struct scd_softc *); 129106449Smdoddstatic int scd_resume(struct scd_softc *); 130106449Smdoddstatic int scd_playtracks(struct scd_softc *, struct ioc_play_track *pt); 131106449Smdoddstatic int scd_playmsf(struct scd_softc *, struct ioc_play_msf *msf); 132106449Smdoddstatic int scd_play(struct scd_softc *, struct ioc_play_msf *msf); 133349629Smarkjstatic int scd_subchan(struct scd_softc *, struct ioc_read_subchannel *sch); 134106449Smdoddstatic int read_subcode(struct scd_softc *, struct sony_subchannel_position_data *sch); 1357332Sjkh 1367332Sjkh/* for xcdplayer */ 137106449Smdoddstatic int scd_toc_header(struct scd_softc *, struct ioc_toc_header *th); 138106449Smdoddstatic int scd_toc_entrys(struct scd_softc *, struct ioc_read_toc_entry *te); 139106449Smdoddstatic int scd_toc_entry(struct scd_softc *, struct ioc_read_toc_single_entry *te); 1407332Sjkh#define SCD_LASTPLUS1 170 /* don't ask, xcdplayer passes this in */ 1417332Sjkh 14212675Sjulianstatic d_open_t scdopen; 14312675Sjulianstatic d_close_t scdclose; 14412675Sjulianstatic d_ioctl_t scdioctl; 14512675Sjulianstatic d_strategy_t scdstrategy; 14612675Sjulian 14774810Sphk 14837389Sjulianstatic struct cdevsw scd_cdevsw = { 149126080Sphk .d_version = D_VERSION, 150111815Sphk .d_open = scdopen, 151111815Sphk .d_close = scdclose, 152111815Sphk .d_read = physread, 153111815Sphk .d_ioctl = scdioctl, 154111815Sphk .d_strategy = scdstrategy, 155111815Sphk .d_name = "scd", 156274679Sjhb .d_flags = D_DISK, 15747625Sphk}; 15812675Sjulian 159106449Smdoddint 160106449Smdoddscd_attach(struct scd_softc *sc) 1617332Sjkh{ 162106449Smdodd int unit; 1638876Srgrimes 164106449Smdodd unit = device_get_unit(sc->dev); 1657332Sjkh 166274679Sjhb SCD_LOCK(sc); 167106449Smdodd init_drive(sc); 1687332Sjkh 169106490Smdodd sc->data.flags = SCDINIT; 170106490Smdodd sc->data.audio_status = CD_AS_AUDIO_INVALID; 171106490Smdodd bioq_init(&sc->data.head); 172274679Sjhb SCD_UNLOCK(sc); 1737332Sjkh 174106449Smdodd sc->scd_dev_t = make_dev(&scd_cdevsw, 8 * unit, 175106449Smdodd UID_ROOT, GID_OPERATOR, 0640, "scd%d", unit); 176106449Smdodd sc->scd_dev_t->si_drv1 = (void *)sc; 177320921Sjhb device_printf(sc->dev, 178320921Sjhb "WARNING: This driver is deprecated and will be removed.\n"); 179106449Smdodd 180106451Smdodd return (0); 1817332Sjkh} 1827332Sjkh 18312675Sjulianstatic int 184130585Sphkscdopen(struct cdev *dev, int flags, int fmt, struct thread *td) 1857332Sjkh{ 186106449Smdodd struct scd_softc *sc; 1877332Sjkh int rc; 1888876Srgrimes 189106449Smdodd sc = (struct scd_softc *)dev->si_drv1; 1907332Sjkh 191274679Sjhb /* mark all open part's invalid */ 192274679Sjhb SCD_LOCK(sc); 193274679Sjhb if (sc->data.openflag) { 194274679Sjhb SCD_UNLOCK(sc); 195106451Smdodd return (ENXIO); 196274679Sjhb } 1977332Sjkh 198106449Smdodd XDEBUG(sc, 1, "DEBUG: status = 0x%x\n", SCD_READ(sc, IREG_STATUS)); 1997332Sjkh 200106449Smdodd if ((rc = spin_up(sc)) != 0) { 201106449Smdodd print_error(sc, rc); 202274679Sjhb SCD_UNLOCK(sc); 203106451Smdodd return (EIO); 2047332Sjkh } 205106490Smdodd if (!(sc->data.flags & SCDTOC)) { 2067332Sjkh int loop_count = 3; 2077332Sjkh 208106449Smdodd while (loop_count-- > 0 && (rc = read_toc(sc)) != 0) { 2097332Sjkh if (rc == ERR_NOT_SPINNING) { 210106449Smdodd rc = spin_up(sc); 2117332Sjkh if (rc) { 212274679Sjhb print_error(sc, rc); 213274679Sjhb SCD_UNLOCK(sc); 214106451Smdodd return (EIO); 2157332Sjkh } 2167332Sjkh continue; 2177332Sjkh } 218106449Smdodd device_printf(sc->dev, "TOC read error 0x%x\n", rc); 219274679Sjhb SCD_UNLOCK(sc); 220106451Smdodd return (EIO); 2217332Sjkh } 2227332Sjkh } 2237332Sjkh 224106490Smdodd sc->data.openflag = 1; 225106490Smdodd sc->data.flags |= SCDVALID; 226274679Sjhb SCD_UNLOCK(sc); 2277332Sjkh 228106451Smdodd return (0); 2297332Sjkh} 2307332Sjkh 23112675Sjulianstatic int 232130585Sphkscdclose(struct cdev *dev, int flags, int fmt, struct thread *td) 2337332Sjkh{ 234106449Smdodd struct scd_softc *sc; 2358876Srgrimes 236106449Smdodd sc = (struct scd_softc *)dev->si_drv1; 2377332Sjkh 238274679Sjhb SCD_LOCK(sc); 239274679Sjhb KASSERT(sc->data.openflag, ("device not open")); 2407332Sjkh 241106490Smdodd if (sc->data.audio_status != CD_AS_PLAY_IN_PROGRESS) { 242106449Smdodd (void)send_cmd(sc, CMD_SPIN_DOWN, 0); 243106490Smdodd sc->data.flags &= ~SCDSPINNING; 2447332Sjkh } 2457332Sjkh 2467332Sjkh /* close channel */ 247106490Smdodd sc->data.openflag = 0; 248274679Sjhb SCD_UNLOCK(sc); 2497332Sjkh 250106451Smdodd return (0); 2517332Sjkh} 2527332Sjkh 25312675Sjulianstatic void 25459249Sphkscdstrategy(struct bio *bp) 2557332Sjkh{ 256106449Smdodd struct scd_softc *sc; 2577332Sjkh 258106449Smdodd sc = (struct scd_softc *)bp->bio_dev->si_drv1; 2597332Sjkh 2607332Sjkh /* if device invalidated (e.g. media change, door open), error */ 261274679Sjhb SCD_LOCK(sc); 262106490Smdodd if (!(sc->data.flags & SCDVALID)) { 263106449Smdodd device_printf(sc->dev, "media changed\n"); 26459249Sphk bp->bio_error = EIO; 2657332Sjkh goto bad; 2667332Sjkh } 2677332Sjkh 2687332Sjkh /* read only */ 26959249Sphk if (!(bp->bio_cmd == BIO_READ)) { 27059249Sphk bp->bio_error = EROFS; 2717332Sjkh goto bad; 2727332Sjkh } 2738876Srgrimes 2747332Sjkh /* no data to read */ 27559249Sphk if (bp->bio_bcount == 0) 2767332Sjkh goto done; 2778876Srgrimes 278106490Smdodd if (!(sc->data.flags & SCDTOC)) { 27959249Sphk bp->bio_error = EIO; 2807332Sjkh goto bad; 2817332Sjkh } 2827332Sjkh 28359249Sphk bp->bio_resid = 0; 2848876Srgrimes 2857332Sjkh /* queue it */ 286112946Sphk bioq_disksort(&sc->data.head, bp); 2878876Srgrimes 2887332Sjkh /* now check whether we can perform processing */ 289106449Smdodd scd_start(sc); 290274679Sjhb SCD_UNLOCK(sc); 2917332Sjkh return; 2927332Sjkh 2937332Sjkhbad: 29459249Sphk bp->bio_flags |= BIO_ERROR; 2957332Sjkhdone: 296274679Sjhb SCD_UNLOCK(sc); 29759249Sphk bp->bio_resid = bp->bio_bcount; 2987332Sjkh biodone(bp); 2997332Sjkh return; 3007332Sjkh} 3017332Sjkh 3027332Sjkhstatic void 303106449Smdoddscd_start(struct scd_softc *sc) 3047332Sjkh{ 30559249Sphk struct bio *bp; 3068876Srgrimes 307274679Sjhb SCD_ASSERT_LOCKED(sc); 308274679Sjhb if (sc->data.flags & SCDMBXBSY) 3097332Sjkh return; 3107332Sjkh 311137046Sphk bp = bioq_takefirst(&sc->data.head); 31215574Sphk if (bp != 0) { 3137332Sjkh /* block found to process, dequeue */ 314106490Smdodd sc->data.flags |= SCDMBXBSY; 3157332Sjkh } else { 3167332Sjkh /* nothing to do */ 3177332Sjkh return; 3187332Sjkh } 3197332Sjkh 320106490Smdodd sc->data.mbx.retry = 3; 321106490Smdodd sc->data.mbx.bp = bp; 3227332Sjkh 323106490Smdodd scd_doread(sc, SCD_S_BEGIN, &(sc->data.mbx)); 3247332Sjkh return; 3257332Sjkh} 3267332Sjkh 32712675Sjulianstatic int 328130585Sphkscdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) 3297332Sjkh{ 330106449Smdodd struct scd_softc *sc; 331274679Sjhb int error; 3328876Srgrimes 333106449Smdodd sc = (struct scd_softc *)dev->si_drv1; 3347332Sjkh 335106449Smdodd XDEBUG(sc, 1, "ioctl: cmd=0x%lx\n", cmd); 3367332Sjkh 337274679Sjhb SCD_LOCK(sc); 338274679Sjhb if (!(sc->data.flags & SCDVALID)) { 339274679Sjhb SCD_UNLOCK(sc); 340106451Smdodd return (EIO); 341274679Sjhb } 3427332Sjkh 343274679Sjhb error = 0; 3447332Sjkh switch (cmd) { 345106450Smdodd case DIOCGMEDIASIZE: 346106490Smdodd *(off_t *)addr = (off_t)sc->data.disksize * sc->data.blksize; 347106450Smdodd break; 348106450Smdodd case DIOCGSECTORSIZE: 349106490Smdodd *(u_int *)addr = sc->data.blksize; 350106450Smdodd break; 3517332Sjkh case CDIOCPLAYTRACKS: 352274679Sjhb error = scd_playtracks(sc, (struct ioc_play_track *) addr); 353274679Sjhb break; 3547332Sjkh case CDIOCPLAYBLOCKS: 355274679Sjhb error = EINVAL; 356274679Sjhb break; 3577332Sjkh case CDIOCPLAYMSF: 358274679Sjhb error = scd_playmsf(sc, (struct ioc_play_msf *) addr); 359274679Sjhb break; 3607332Sjkh case CDIOCREADSUBCHANNEL: 361349629Smarkj return scd_subchan(sc, (struct ioc_read_subchannel *) addr); 3627332Sjkh case CDIOREADTOCHEADER: 363274679Sjhb error = scd_toc_header (sc, (struct ioc_toc_header *) addr); 364274679Sjhb break; 3657332Sjkh case CDIOREADTOCENTRYS: 366106449Smdodd return scd_toc_entrys (sc, (struct ioc_read_toc_entry*) addr); 36725460Sjoerg case CDIOREADTOCENTRY: 368274679Sjhb error = scd_toc_entry (sc, (struct ioc_read_toc_single_entry*) addr); 369274679Sjhb break; 3707332Sjkh case CDIOCSETPATCH: 3717332Sjkh case CDIOCGETVOL: 3727332Sjkh case CDIOCSETVOL: 3737332Sjkh case CDIOCSETMONO: 3747332Sjkh case CDIOCSETSTERIO: 3757332Sjkh case CDIOCSETMUTE: 3767332Sjkh case CDIOCSETLEFT: 3777332Sjkh case CDIOCSETRIGHT: 378274679Sjhb error = EINVAL; 379274679Sjhb break; 3807332Sjkh case CDIOCRESUME: 381274679Sjhb error = scd_resume(sc); 382274679Sjhb break; 3837332Sjkh case CDIOCPAUSE: 384274679Sjhb error = scd_pause(sc); 385274679Sjhb break; 3867332Sjkh case CDIOCSTART: 387274679Sjhb error = EINVAL; 388274679Sjhb break; 3897332Sjkh case CDIOCSTOP: 390274679Sjhb error = scd_stop(sc); 391274679Sjhb break; 3927332Sjkh case CDIOCEJECT: 393274679Sjhb error = scd_eject(sc); 394274679Sjhb break; 3957332Sjkh case CDIOCALLOW: 396274679Sjhb break; 3977332Sjkh case CDIOCSETDEBUG: 3987332Sjkh#ifdef SCD_DEBUG 3997332Sjkh scd_debuglevel++; 4007332Sjkh#endif 401274679Sjhb break; 4027332Sjkh case CDIOCCLRDEBUG: 4037332Sjkh#ifdef SCD_DEBUG 4047332Sjkh scd_debuglevel = 0; 4057332Sjkh 4067332Sjkh#endif 407274679Sjhb break; 4087332Sjkh default: 409106449Smdodd device_printf(sc->dev, "unsupported ioctl (cmd=0x%lx)\n", cmd); 410274679Sjhb error = ENOTTY; 411274679Sjhb break; 4127332Sjkh } 413274679Sjhb SCD_UNLOCK(sc); 414274679Sjhb return (error); 4157332Sjkh} 4167332Sjkh 4177332Sjkh/*************************************************************** 4187332Sjkh * lower level of driver starts here 4197332Sjkh **************************************************************/ 4207332Sjkh 4217332Sjkhstatic int 422106449Smdoddscd_playtracks(struct scd_softc *sc, struct ioc_play_track *pt) 4237332Sjkh{ 4247332Sjkh struct ioc_play_msf msf; 4257332Sjkh int a = pt->start_track; 4267332Sjkh int z = pt->end_track; 42711872Sphk int rc; 4287332Sjkh 429106490Smdodd if (!(sc->data.flags & SCDTOC) && (rc = read_toc(sc)) != 0) { 4307332Sjkh if (rc == -ERR_NOT_SPINNING) { 431106449Smdodd if (spin_up(sc) != 0) 432106451Smdodd return (EIO); 433106449Smdodd rc = read_toc(sc); 4347332Sjkh } 4357332Sjkh if (rc != 0) { 436106449Smdodd print_error(sc, rc); 437106451Smdodd return (EIO); 4387332Sjkh } 4397332Sjkh } 4407332Sjkh 441106449Smdodd XDEBUG(sc, 1, "playtracks from %d:%d to %d:%d\n", 442106449Smdodd a, pt->start_index, z, pt->end_index); 4437332Sjkh 444106490Smdodd if ( a < sc->data.first_track 445106490Smdodd || a > sc->data.last_track 4467332Sjkh || a > z 447106490Smdodd || z > sc->data.last_track) 448106451Smdodd return (EINVAL); 4497332Sjkh 450106490Smdodd bcopy(sc->data.toc[a].start_msf, &msf.start_m, 3); 451106490Smdodd hsg2msf(msf2hsg(sc->data.toc[z+1].start_msf)-1, &msf.end_m); 4527332Sjkh 453106449Smdodd return scd_play(sc, &msf); 4547332Sjkh} 4557332Sjkh 4567332Sjkh/* The start/end msf is expected to be in bin format */ 4577332Sjkhstatic int 458106449Smdoddscd_playmsf(struct scd_softc *sc, struct ioc_play_msf *msfin) 4597332Sjkh{ 4607332Sjkh struct ioc_play_msf msf; 4617332Sjkh 4627332Sjkh msf.start_m = bin2bcd(msfin->start_m); 4637332Sjkh msf.start_s = bin2bcd(msfin->start_s); 4647332Sjkh msf.start_f = bin2bcd(msfin->start_f); 4657332Sjkh msf.end_m = bin2bcd(msfin->end_m); 4667332Sjkh msf.end_s = bin2bcd(msfin->end_s); 4677332Sjkh msf.end_f = bin2bcd(msfin->end_f); 4687332Sjkh 469106449Smdodd return scd_play(sc, &msf); 4707332Sjkh} 4717332Sjkh 4727332Sjkh/* The start/end msf is expected to be in bcd format */ 4737332Sjkhstatic int 474106449Smdoddscd_play(struct scd_softc *sc, struct ioc_play_msf *msf) 4757332Sjkh{ 4767332Sjkh int i, rc; 4777332Sjkh 478106449Smdodd XDEBUG(sc, 1, "playing: %02x:%02x:%02x -> %02x:%02x:%02x\n", 4797332Sjkh msf->start_m, msf->start_s, msf->start_f, 480106449Smdodd msf->end_m, msf->end_s, msf->end_f); 4817332Sjkh 4827332Sjkh for (i = 0; i < 2; i++) { 483106449Smdodd rc = send_cmd(sc, CMD_PLAY_AUDIO, 7, 4847332Sjkh 0x03, 4857332Sjkh msf->start_m, msf->start_s, msf->start_f, 4867332Sjkh msf->end_m, msf->end_s, msf->end_f); 4877332Sjkh if (rc == -ERR_NOT_SPINNING) { 488106490Smdodd sc->data.flags &= ~SCDSPINNING; 489106449Smdodd if (spin_up(sc) != 0) 490106451Smdodd return (EIO); 4917332Sjkh } else if (rc < 0) { 492106449Smdodd print_error(sc, rc); 493106451Smdodd return (EIO); 4947332Sjkh } else { 4957332Sjkh break; 4967332Sjkh } 4977332Sjkh } 498106490Smdodd sc->data.audio_status = CD_AS_PLAY_IN_PROGRESS; 499106490Smdodd bcopy((char *)msf, (char *)&sc->data.last_play, sizeof(struct ioc_play_msf)); 500106451Smdodd return (0); 5017332Sjkh} 5027332Sjkh 5037332Sjkhstatic int 504106449Smdoddscd_stop(struct scd_softc *sc) 5057332Sjkh{ 5067332Sjkh 507106449Smdodd (void)send_cmd(sc, CMD_STOP_AUDIO, 0); 508106490Smdodd sc->data.audio_status = CD_AS_PLAY_COMPLETED; 509106451Smdodd return (0); 5107332Sjkh} 5117332Sjkh 5127332Sjkhstatic int 513106449Smdoddscd_pause(struct scd_softc *sc) 5147332Sjkh{ 5157332Sjkh struct sony_subchannel_position_data subpos; 5167332Sjkh 517106490Smdodd if (sc->data.audio_status != CD_AS_PLAY_IN_PROGRESS) 518106451Smdodd return (EINVAL); 5197332Sjkh 520106449Smdodd if (read_subcode(sc, &subpos) != 0) 521106451Smdodd return (EIO); 5227332Sjkh 523106449Smdodd if (send_cmd(sc, CMD_STOP_AUDIO, 0) != 0) 524106451Smdodd return (EIO); 5258876Srgrimes 526106490Smdodd sc->data.last_play.start_m = subpos.abs_msf[0]; 527106490Smdodd sc->data.last_play.start_s = subpos.abs_msf[1]; 528106490Smdodd sc->data.last_play.start_f = subpos.abs_msf[2]; 529106490Smdodd sc->data.audio_status = CD_AS_PLAY_PAUSED; 5307332Sjkh 531106449Smdodd XDEBUG(sc, 1, "pause @ %02x:%02x:%02x\n", 532106490Smdodd sc->data.last_play.start_m, 533106490Smdodd sc->data.last_play.start_s, 534106490Smdodd sc->data.last_play.start_f); 5357332Sjkh 536106451Smdodd return (0); 5377332Sjkh} 5387332Sjkh 5397332Sjkhstatic int 540106449Smdoddscd_resume(struct scd_softc *sc) 5417332Sjkh{ 542106449Smdodd 543106449Smdodd if (sc->data.audio_status != CD_AS_PLAY_PAUSED) 544106451Smdodd return (EINVAL); 545106449Smdodd return scd_play(sc, &sc->data.last_play); 5467332Sjkh} 5477332Sjkh 5487332Sjkhstatic int 549106449Smdoddscd_eject(struct scd_softc *sc) 5507332Sjkh{ 5517332Sjkh 552106490Smdodd sc->data.audio_status = CD_AS_AUDIO_INVALID; 553106490Smdodd sc->data.flags &= ~(SCDSPINNING|SCDTOC); 5547332Sjkh 555106449Smdodd if (send_cmd(sc, CMD_STOP_AUDIO, 0) != 0 || 556106449Smdodd send_cmd(sc, CMD_SPIN_DOWN, 0) != 0 || 557106449Smdodd send_cmd(sc, CMD_EJECT, 0) != 0) 5587332Sjkh { 559106451Smdodd return (EIO); 5607332Sjkh } 561106451Smdodd return (0); 5627332Sjkh} 5637332Sjkh 5647332Sjkhstatic int 565349629Smarkjscd_subchan(struct scd_softc *sc, struct ioc_read_subchannel *sch) 5667332Sjkh{ 5677332Sjkh struct sony_subchannel_position_data q; 5687332Sjkh struct cd_sub_channel_info data; 5697332Sjkh 570106449Smdodd XDEBUG(sc, 1, "subchan af=%d, df=%d\n", 571106449Smdodd sch->address_format, sch->data_format); 5727332Sjkh 573106449Smdodd if (sch->address_format != CD_MSF_FORMAT) 574106451Smdodd return (EINVAL); 5757332Sjkh 576106449Smdodd if (sch->data_format != CD_CURRENT_POSITION) 577106451Smdodd return (EINVAL); 5787332Sjkh 579106449Smdodd if (read_subcode(sc, &q) != 0) 580106451Smdodd return (EIO); 5817332Sjkh 582106490Smdodd data.header.audio_status = sc->data.audio_status; 5837332Sjkh data.what.position.data_format = CD_MSF_FORMAT; 5847332Sjkh data.what.position.track_number = bcd2bin(q.track_number); 5857332Sjkh data.what.position.reladdr.msf.unused = 0; 5867332Sjkh data.what.position.reladdr.msf.minute = bcd2bin(q.rel_msf[0]); 5877332Sjkh data.what.position.reladdr.msf.second = bcd2bin(q.rel_msf[1]); 5887332Sjkh data.what.position.reladdr.msf.frame = bcd2bin(q.rel_msf[2]); 5897332Sjkh data.what.position.absaddr.msf.unused = 0; 5907332Sjkh data.what.position.absaddr.msf.minute = bcd2bin(q.abs_msf[0]); 5917332Sjkh data.what.position.absaddr.msf.second = bcd2bin(q.abs_msf[1]); 5927332Sjkh data.what.position.absaddr.msf.frame = bcd2bin(q.abs_msf[2]); 593274679Sjhb SCD_UNLOCK(sc); 5947332Sjkh 595349629Smarkj if (copyout(&data, sch->data, min(sizeof(struct cd_sub_channel_info), sch->data_len))!=0) 596349629Smarkj return (EFAULT); 597106451Smdodd return (0); 5987332Sjkh} 5997332Sjkh 600106449Smdoddint 601106449Smdoddscd_probe(struct scd_softc *sc) 60242552Seivind{ 6037332Sjkh struct sony_drive_configuration drive_config; 6047332Sjkh int rc; 6057332Sjkh static char namebuf[8+16+8+3]; 6067332Sjkh char *s = namebuf; 6077332Sjkh int loop_count = 0; 6087332Sjkh 609106490Smdodd sc->data.flags = SCDPROBING; 6107332Sjkh 6117332Sjkh bzero(&drive_config, sizeof(drive_config)); 6127332Sjkh 6137332Sjkhagain: 6147332Sjkh /* Reset drive */ 615106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_RESET_DRIVE); 6167332Sjkh 6177332Sjkh /* Calm down */ 6187332Sjkh DELAY(300000); 6197332Sjkh 6207332Sjkh /* Only the ATTENTION bit may be set */ 621106449Smdodd if ((SCD_READ(sc, IREG_STATUS) & ~1) != 0) { 622106449Smdodd XDEBUG(sc, 1, "too many bits set. probe failed.\n"); 623106449Smdodd return (ENXIO); 6247332Sjkh } 625106449Smdodd rc = send_cmd(sc, CMD_GET_DRIVE_CONFIG, 0); 6267332Sjkh if (rc != sizeof(drive_config)) { 6277332Sjkh /* Sometimes if the drive is playing audio I get */ 6287332Sjkh /* the bad result 82. Fix by repeating the reset */ 6297332Sjkh if (rc > 0 && loop_count++ == 0) 6307332Sjkh goto again; 631106449Smdodd return (ENXIO); 6327332Sjkh } 633106449Smdodd if (get_result(sc, rc, (u_char *)&drive_config) != 0) 634106449Smdodd return (ENXIO); 6357332Sjkh 6367332Sjkh bcopy(drive_config.vendor, namebuf, 8); 6377332Sjkh s = namebuf+8; 6387332Sjkh while (*(s-1) == ' ') /* Strip trailing spaces */ 6397332Sjkh s--; 6407332Sjkh *s++ = ' '; 6417332Sjkh bcopy(drive_config.product, s, 16); 6427332Sjkh s += 16; 6437332Sjkh while (*(s-1) == ' ') 6447332Sjkh s--; 6457332Sjkh *s++ = ' '; 6467332Sjkh bcopy(drive_config.revision, s, 8); 6477332Sjkh s += 8; 6487332Sjkh while (*(s-1) == ' ') 6497332Sjkh s--; 6507332Sjkh *s = 0; 6517332Sjkh 652106490Smdodd sc->data.name = namebuf; 6537332Sjkh 6547332Sjkh if (drive_config.config & 0x10) 655106490Smdodd sc->data.double_speed = 1; 6567332Sjkh else 657106490Smdodd sc->data.double_speed = 0; 6587332Sjkh 659106449Smdodd return (0); 6607332Sjkh} 6617332Sjkh 6627332Sjkhstatic int 663106449Smdoddread_subcode(struct scd_softc *sc, struct sony_subchannel_position_data *scp) 6647332Sjkh{ 6657332Sjkh int rc; 6667332Sjkh 667106449Smdodd rc = send_cmd(sc, CMD_GET_SUBCHANNEL_DATA, 0); 668106449Smdodd if (rc < 0 || rc < sizeof(*scp)) 669106451Smdodd return (EIO); 670106449Smdodd if (get_result(sc, rc, (u_char *)scp) != 0) 671106451Smdodd return (EIO); 672106451Smdodd return (0); 6737332Sjkh} 6747332Sjkh 6757332Sjkh/* State machine copied from mcd.c */ 6767332Sjkh 6777332Sjkh/* This (and the code in mcd.c) will not work with more than one drive */ 678106449Smdodd/* because there is only one sc->ch_mbxsave below. Should fix that some day. */ 679106449Smdodd/* (sc->ch_mbxsave & state should probably be included in the scd_data struct and */ 6807332Sjkh/* the unit number used as first argument to scd_doread().) /Micke */ 6817332Sjkh 6827332Sjkh/* state machine to process read requests 6837332Sjkh * initialize with SCD_S_BEGIN: reset state machine 6847332Sjkh * SCD_S_WAITSTAT: wait for ready (!busy) 6857332Sjkh * SCD_S_WAITSPIN: wait for drive to spin up (if not spinning) 6867332Sjkh * SCD_S_WAITFIFO: wait for param fifo to get ready, them exec. command. 6877332Sjkh * SCD_S_WAITREAD: wait for data ready, read data 6887332Sjkh * SCD_S_WAITPARAM: wait for command result params, read them, error if bad data read. 6897332Sjkh */ 6907332Sjkh 6917332Sjkhstatic void 69225056Sbdescd_timeout(void *arg) 69325056Sbde{ 694106449Smdodd struct scd_softc *sc; 695106449Smdodd sc = (struct scd_softc *)arg; 696106449Smdodd 697274679Sjhb SCD_ASSERT_LOCKED(sc); 698106449Smdodd scd_doread(sc, sc->ch_state, sc->ch_mbxsave); 69925056Sbde} 70025056Sbde 70125056Sbdestatic void 702106449Smdoddscd_doread(struct scd_softc *sc, int state, struct scd_mbx *mbxin) 7037332Sjkh{ 704106449Smdodd struct scd_mbx *mbx = (state!=SCD_S_BEGIN) ? sc->ch_mbxsave : mbxin; 70559249Sphk struct bio *bp = mbx->bp; 706106449Smdodd int i; 7077332Sjkh int blknum; 7087332Sjkh caddr_t addr; 7097332Sjkh static char sdata[3]; /* Must be preserved between calls to this function */ 7107332Sjkh 711274679Sjhb SCD_ASSERT_LOCKED(sc); 7127332Sjkhloop: 7137332Sjkh switch (state) { 7147332Sjkh case SCD_S_BEGIN: 715106449Smdodd mbx = sc->ch_mbxsave = mbxin; 7167332Sjkh 7177332Sjkh case SCD_S_BEGIN1: 7187332Sjkh /* get status */ 7197332Sjkh mbx->count = RDELAY_WAIT; 7207332Sjkh 721106449Smdodd process_attention(sc); 7227332Sjkh goto trystat; 7237332Sjkh 7247332Sjkh case SCD_S_WAITSTAT: 725106449Smdodd sc->ch_state = SCD_S_WAITSTAT; 726274679Sjhb callout_stop(&sc->timer); 7277332Sjkh if (mbx->count-- <= 0) { 728106449Smdodd device_printf(sc->dev, "timeout. drive busy.\n"); 7297332Sjkh goto harderr; 7307332Sjkh } 7317332Sjkh 7327332Sjkhtrystat: 733106449Smdodd if (IS_BUSY(sc)) { 734106449Smdodd sc->ch_state = SCD_S_WAITSTAT; 735274679Sjhb callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ 7367332Sjkh return; 7377332Sjkh } 7388876Srgrimes 739106449Smdodd process_attention(sc); 7407332Sjkh 7417332Sjkh /* reject, if audio active */ 742106490Smdodd if (sc->data.audio_status & CD_AS_PLAY_IN_PROGRESS) { 743106449Smdodd device_printf(sc->dev, "audio is active\n"); 7447332Sjkh goto harderr; 7457332Sjkh } 7467332Sjkh 747106490Smdodd mbx->sz = sc->data.blksize; 7487332Sjkh 7497332Sjkh /* for first block */ 750298646Spfg mbx->nblk = howmany(bp->bio_bcount, mbx->sz); 7517332Sjkh mbx->skip = 0; 7527332Sjkh 7537332Sjkhnextblock: 754106490Smdodd if (!(sc->data.flags & SCDVALID)) 7557332Sjkh goto changed; 7567332Sjkh 757121212Sphk blknum = bp->bio_offset / mbx->sz + mbx->skip/mbx->sz; 7587332Sjkh 759106449Smdodd XDEBUG(sc, 2, "scd_doread: read blknum=%d\n", blknum); 7607332Sjkh 7617332Sjkh /* build parameter block */ 7627332Sjkh hsg2msf(blknum, sdata); 7637332Sjkh 764106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); 765106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_RPARAM_CLEAR); 766106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_DATA_READY_CLEAR); 7677332Sjkh 768106449Smdodd if (FSTATUS_BIT(sc, FBIT_WPARAM_READY)) 7697332Sjkh goto writeparam; 7707332Sjkh 7717332Sjkh mbx->count = 100; 772106449Smdodd sc->ch_state = SCD_S_WAITFIFO; 773274679Sjhb callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ 7747332Sjkh return; 7757332Sjkh 7767332Sjkh case SCD_S_WAITSPIN: 777106449Smdodd sc->ch_state = SCD_S_WAITSPIN; 778274679Sjhb callout_stop(&sc->timer); 7797332Sjkh if (mbx->count-- <= 0) { 780106449Smdodd device_printf(sc->dev, "timeout waiting for drive to spin up.\n"); 7817332Sjkh goto harderr; 7827332Sjkh } 783106449Smdodd if (!STATUS_BIT(sc, SBIT_RESULT_READY)) { 784106449Smdodd sc->ch_state = SCD_S_WAITSPIN; 785274679Sjhb callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ 7867332Sjkh return; 7877332Sjkh } 788106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); 789106449Smdodd switch ((i = SCD_READ(sc, IREG_RESULT)) & 0xf0) { 7907332Sjkh case 0x20: 791106449Smdodd i = SCD_READ(sc, IREG_RESULT); 792106449Smdodd print_error(sc, i); 7937332Sjkh goto harderr; 7947332Sjkh case 0x00: 795106449Smdodd (void)SCD_READ(sc, IREG_RESULT); 796106490Smdodd sc->data.flags |= SCDSPINNING; 7977332Sjkh break; 7987332Sjkh } 799106449Smdodd XDEBUG(sc, 1, "DEBUG: spin up complete\n"); 8007332Sjkh 8017332Sjkh state = SCD_S_BEGIN1; 8027332Sjkh goto loop; 8037332Sjkh 8047332Sjkh case SCD_S_WAITFIFO: 805106449Smdodd sc->ch_state = SCD_S_WAITFIFO; 806274679Sjhb callout_stop(&sc->timer); 8077332Sjkh if (mbx->count-- <= 0) { 808106449Smdodd device_printf(sc->dev, "timeout. write param not ready.\n"); 8097332Sjkh goto harderr; 8107332Sjkh } 811106449Smdodd if (!FSTATUS_BIT(sc, FBIT_WPARAM_READY)) { 812106449Smdodd sc->ch_state = SCD_S_WAITFIFO; 813274679Sjhb callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ 8147332Sjkh return; 8157332Sjkh } 816106449Smdodd XDEBUG(sc, 1, "mbx->count (writeparamwait) = %d(%d)\n", mbx->count, 100); 8177332Sjkh 8187332Sjkhwriteparam: 8197332Sjkh /* The reason this test isn't done 'till now is to make sure */ 8207332Sjkh /* that it is ok to send the SPIN_UP cmd below. */ 821106490Smdodd if (!(sc->data.flags & SCDSPINNING)) { 822106449Smdodd XDEBUG(sc, 1, "spinning up drive ...\n"); 823106449Smdodd SCD_WRITE(sc, OREG_COMMAND, CMD_SPIN_UP); 8247332Sjkh mbx->count = 300; 825106449Smdodd sc->ch_state = SCD_S_WAITSPIN; 826274679Sjhb callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ 8277332Sjkh return; 8287332Sjkh } 8297332Sjkh 8307332Sjkh /* send the read command */ 831106449Smdodd SCD_WRITE(sc, OREG_WPARAMS, sdata[0]); 832106449Smdodd SCD_WRITE(sc, OREG_WPARAMS, sdata[1]); 833106449Smdodd SCD_WRITE(sc, OREG_WPARAMS, sdata[2]); 834106449Smdodd SCD_WRITE(sc, OREG_WPARAMS, 0); 835106449Smdodd SCD_WRITE(sc, OREG_WPARAMS, 0); 836106449Smdodd SCD_WRITE(sc, OREG_WPARAMS, 1); 837106449Smdodd SCD_WRITE(sc, OREG_COMMAND, CMD_READ); 8387332Sjkh 8397332Sjkh mbx->count = RDELAY_WAITREAD; 8407332Sjkh for (i = 0; i < 50; i++) { 841106449Smdodd if (STATUS_BIT(sc, SBIT_DATA_READY)) 8427332Sjkh goto got_data; 8437332Sjkh DELAY(100); 8447332Sjkh } 8457332Sjkh 846106449Smdodd sc->ch_state = SCD_S_WAITREAD; 847274679Sjhb callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ 8487332Sjkh return; 8497332Sjkh 8507332Sjkh case SCD_S_WAITREAD: 851106449Smdodd sc->ch_state = SCD_S_WAITREAD; 852274679Sjhb callout_stop(&sc->timer); 8537332Sjkh if (mbx->count-- <= 0) { 854106449Smdodd if (STATUS_BIT(sc, SBIT_RESULT_READY)) 8557332Sjkh goto got_param; 856106449Smdodd device_printf(sc->dev, "timeout while reading data\n"); 8577332Sjkh goto readerr; 8587332Sjkh } 859106449Smdodd if (!STATUS_BIT(sc, SBIT_DATA_READY)) { 860106449Smdodd process_attention(sc); 861106490Smdodd if (!(sc->data.flags & SCDVALID)) 8627332Sjkh goto changed; 863106449Smdodd sc->ch_state = SCD_S_WAITREAD; 864274679Sjhb callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ 8657332Sjkh return; 8667332Sjkh } 867106449Smdodd XDEBUG(sc, 2, "mbx->count (after RDY_BIT) = %d(%d)\n", mbx->count, RDELAY_WAITREAD); 8687332Sjkh 8697332Sjkhgot_data: 8707332Sjkh /* data is ready */ 87159249Sphk addr = bp->bio_data + mbx->skip; 872106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_DATA_READY_CLEAR); 873106449Smdodd SCD_READ_MULTI(sc, IREG_DATA, addr, mbx->sz); 8747332Sjkh 8757332Sjkh mbx->count = 100; 8767332Sjkh for (i = 0; i < 20; i++) { 877106449Smdodd if (STATUS_BIT(sc, SBIT_RESULT_READY)) 8787332Sjkh goto waitfor_param; 8797332Sjkh DELAY(100); 8807332Sjkh } 8817332Sjkh goto waitfor_param; 8827332Sjkh 8837332Sjkh case SCD_S_WAITPARAM: 884106449Smdodd sc->ch_state = SCD_S_WAITPARAM; 885274679Sjhb callout_stop(&sc->timer); 8867332Sjkh if (mbx->count-- <= 0) { 887106449Smdodd device_printf(sc->dev, "timeout waiting for params\n"); 8887332Sjkh goto readerr; 8897332Sjkh } 8907332Sjkh 8917332Sjkhwaitfor_param: 892106449Smdodd if (!STATUS_BIT(sc, SBIT_RESULT_READY)) { 893106449Smdodd sc->ch_state = SCD_S_WAITPARAM; 894274679Sjhb callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ 8957332Sjkh return; 8967332Sjkh } 897106451Smdodd#ifdef SCD_DEBUG 8987332Sjkh if (mbx->count < 100 && scd_debuglevel > 0) 899106449Smdodd device_printf(sc->dev, "mbx->count (paramwait) = %d(%d)\n", mbx->count, 100); 9007332Sjkh#endif 9017332Sjkh 9027332Sjkhgot_param: 903106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); 904106449Smdodd switch ((i = SCD_READ(sc, IREG_RESULT)) & 0xf0) { 9057332Sjkh case 0x50: 9067332Sjkh switch (i) { 9077332Sjkh case ERR_FATAL_READ_ERROR1: 9087332Sjkh case ERR_FATAL_READ_ERROR2: 909106449Smdodd device_printf(sc->dev, "unrecoverable read error 0x%x\n", i); 9107332Sjkh goto harderr; 9117332Sjkh } 9127332Sjkh break; 9137332Sjkh case 0x20: 914106449Smdodd i = SCD_READ(sc, IREG_RESULT); 9157332Sjkh switch (i) { 9167332Sjkh case ERR_NOT_SPINNING: 917106449Smdodd XDEBUG(sc, 1, "read error: drive not spinning\n"); 9187332Sjkh if (mbx->retry-- > 0) { 9197332Sjkh state = SCD_S_BEGIN1; 920106490Smdodd sc->data.flags &= ~SCDSPINNING; 9217332Sjkh goto loop; 9227332Sjkh } 9237332Sjkh goto harderr; 9247332Sjkh default: 925106449Smdodd print_error(sc, i); 9267332Sjkh goto readerr; 9277332Sjkh } 9287332Sjkh case 0x00: 929106449Smdodd i = SCD_READ(sc, IREG_RESULT); 9307332Sjkh break; 9317332Sjkh } 9327332Sjkh 9337332Sjkh if (--mbx->nblk > 0) { 9347332Sjkh mbx->skip += mbx->sz; 9357332Sjkh goto nextblock; 9367332Sjkh } 9377332Sjkh 9387332Sjkh /* return buffer */ 93959249Sphk bp->bio_resid = 0; 9407332Sjkh biodone(bp); 9417332Sjkh 942106490Smdodd sc->data.flags &= ~SCDMBXBSY; 943106449Smdodd scd_start(sc); 9447332Sjkh return; 9457332Sjkh } 9467332Sjkh 9477332Sjkhreaderr: 9487332Sjkh if (mbx->retry-- > 0) { 949106449Smdodd device_printf(sc->dev, "retrying ...\n"); 9507332Sjkh state = SCD_S_BEGIN1; 9517332Sjkh goto loop; 9527332Sjkh } 9537332Sjkhharderr: 9547332Sjkh /* invalidate the buffer */ 95559249Sphk bp->bio_error = EIO; 95659249Sphk bp->bio_flags |= BIO_ERROR; 95759249Sphk bp->bio_resid = bp->bio_bcount; 9587332Sjkh biodone(bp); 9597332Sjkh 960106490Smdodd sc->data.flags &= ~SCDMBXBSY; 961106449Smdodd scd_start(sc); 9627332Sjkh return; 9637332Sjkh 9647332Sjkhchanged: 965106449Smdodd device_printf(sc->dev, "media changed\n"); 9667332Sjkh goto harderr; 9677332Sjkh} 9687332Sjkh 9697332Sjkhstatic void 9707332Sjkhhsg2msf(int hsg, bcd_t *msf) 9717332Sjkh{ 972106490Smdodd 9737332Sjkh hsg += 150; 9747332Sjkh M_msf(msf) = bin2bcd(hsg / 4500); 9757332Sjkh hsg %= 4500; 9767332Sjkh S_msf(msf) = bin2bcd(hsg / 75); 9777332Sjkh F_msf(msf) = bin2bcd(hsg % 75); 9787332Sjkh} 9797332Sjkh 9807332Sjkhstatic int 9817332Sjkhmsf2hsg(bcd_t *msf) 9827332Sjkh{ 983106490Smdodd 9847332Sjkh return (bcd2bin(M_msf(msf)) * 60 + 9857332Sjkh bcd2bin(S_msf(msf))) * 75 + 9867332Sjkh bcd2bin(F_msf(msf)) - 150; 9877332Sjkh} 9887332Sjkh 9897332Sjkhstatic void 990106449Smdoddprocess_attention(struct scd_softc *sc) 9917332Sjkh{ 9927332Sjkh unsigned char code; 9937332Sjkh int count = 0; 9947332Sjkh 995106449Smdodd while (IS_ATTENTION(sc) && count++ < 30) { 996106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_ATTENTION_CLEAR); 997106449Smdodd code = SCD_READ(sc, IREG_RESULT); 9987332Sjkh 999106451Smdodd#ifdef SCD_DEBUG 10007332Sjkh if (scd_debuglevel > 0) { 10017332Sjkh if (count == 1) 1002106449Smdodd device_printf(sc->dev, "DEBUG: ATTENTIONS = 0x%x", code); 10037332Sjkh else 10047332Sjkh printf(",0x%x", code); 10057332Sjkh } 10067332Sjkh#endif 10077332Sjkh 10087332Sjkh switch (code) { 10097332Sjkh case ATTEN_SPIN_DOWN: 1010106449Smdodd sc->data.flags &= ~SCDSPINNING; 10117332Sjkh break; 10127332Sjkh 10137332Sjkh case ATTEN_SPIN_UP_DONE: 1014106449Smdodd sc->data.flags |= SCDSPINNING; 10157332Sjkh break; 10167332Sjkh 10177332Sjkh case ATTEN_AUDIO_DONE: 1018106449Smdodd sc->data.audio_status = CD_AS_PLAY_COMPLETED; 10197332Sjkh break; 10207332Sjkh 10217332Sjkh case ATTEN_DRIVE_LOADED: 1022106449Smdodd sc->data.flags &= ~(SCDTOC|SCDSPINNING|SCDVALID); 1023106449Smdodd sc->data.audio_status = CD_AS_AUDIO_INVALID; 10247332Sjkh break; 10257332Sjkh 10267332Sjkh case ATTEN_EJECT_PUSHED: 1027106449Smdodd sc->data.flags &= ~SCDVALID; 10287332Sjkh break; 10297332Sjkh } 10307332Sjkh DELAY(100); 10317332Sjkh } 1032106451Smdodd#ifdef SCD_DEBUG 10337332Sjkh if (scd_debuglevel > 0 && count > 0) 10347332Sjkh printf("\n"); 10357332Sjkh#endif 10367332Sjkh} 10377332Sjkh 10387332Sjkh/* Returns 0 OR sony error code */ 10397332Sjkhstatic int 1040106449Smdoddspin_up(struct scd_softc *sc) 10417332Sjkh{ 10427332Sjkh unsigned char res_reg[12]; 10437332Sjkh unsigned int res_size; 10447332Sjkh int rc; 10457332Sjkh int loop_count = 0; 10467332Sjkh 10477332Sjkhagain: 1048106449Smdodd rc = send_cmd(sc, CMD_SPIN_UP, 0, 0, res_reg, &res_size); 10497332Sjkh if (rc != 0) { 1050106449Smdodd XDEBUG(sc, 2, "CMD_SPIN_UP error 0x%x\n", rc); 1051106451Smdodd return (rc); 10527332Sjkh } 10537332Sjkh 1054106449Smdodd if (!(sc->data.flags & SCDTOC)) { 1055106449Smdodd rc = send_cmd(sc, CMD_READ_TOC, 0); 10567332Sjkh if (rc == ERR_NOT_SPINNING) { 10577332Sjkh if (loop_count++ < 3) 10587332Sjkh goto again; 1059106451Smdodd return (rc); 10607332Sjkh } 10617332Sjkh if (rc != 0) 1062106451Smdodd return (rc); 10637332Sjkh } 10647332Sjkh 1065106449Smdodd sc->data.flags |= SCDSPINNING; 10667332Sjkh 1067106451Smdodd return (0); 10687332Sjkh} 10697332Sjkh 10707332Sjkhstatic struct sony_tracklist * 10717332Sjkhget_tl(struct sony_toc *toc, int size) 10727332Sjkh{ 10737332Sjkh struct sony_tracklist *tl = &toc->tracks[0]; 10747332Sjkh 10757332Sjkh if (tl->track != 0xb0) 1076106451Smdodd return (tl); 10778876Srgrimes if (tl->track != 0xb1) 1078106451Smdodd return (tl); 1079132771Skan tl = (struct sony_tracklist *)((char *)tl + 9); 10808876Srgrimes if (tl->track != 0xb2) 1081106451Smdodd return (tl); 1082132771Skan tl = (struct sony_tracklist *)((char *)tl + 9); 10838876Srgrimes if (tl->track != 0xb3) 1084106451Smdodd return (tl); 1085132771Skan tl = (struct sony_tracklist *)((char *)tl + 9); 10868876Srgrimes if (tl->track != 0xb4) 1087106451Smdodd return (tl); 1088132771Skan tl = (struct sony_tracklist *)((char *)tl + 9); 10898876Srgrimes if (tl->track != 0xc0) 1090106451Smdodd return (tl); 1091132771Skan tl = (struct sony_tracklist *)((char *)tl + 9); 1092106451Smdodd return (tl); 10937332Sjkh} 10947332Sjkh 10957332Sjkhstatic int 1096106449Smdoddread_toc(struct scd_softc *sc) 10977332Sjkh{ 10987332Sjkh struct sony_toc toc; 10997332Sjkh struct sony_tracklist *tl; 11007332Sjkh int rc, i, j; 11017332Sjkh u_long first, last; 11027332Sjkh 1103106453Smdodd rc = send_cmd(sc, CMD_GET_TOC, 1, 1); 11047332Sjkh if (rc < 0) 1105106451Smdodd return (rc); 11067332Sjkh if (rc > sizeof(toc)) { 1107106449Smdodd device_printf(sc->dev, "program error: toc too large (%d)\n", rc); 1108106451Smdodd return (EIO); 11097332Sjkh } 1110106449Smdodd if (get_result(sc, rc, (u_char *)&toc) != 0) 1111106451Smdodd return (EIO); 11127332Sjkh 1113106449Smdodd XDEBUG(sc, 1, "toc read. len = %d, sizeof(toc) = %d\n", rc, sizeof(toc)); 11147332Sjkh 11157332Sjkh tl = get_tl(&toc, rc); 11167332Sjkh first = msf2hsg(tl->start_msf); 11177332Sjkh last = msf2hsg(toc.lead_out_start_msf); 1118106490Smdodd sc->data.blksize = SCDBLKSIZE; 1119106490Smdodd sc->data.disksize = last*sc->data.blksize/DEV_BSIZE; 11207332Sjkh 1121106449Smdodd XDEBUG(sc, 1, "firstsector = %ld, lastsector = %ld", first, last); 11227332Sjkh 1123106490Smdodd sc->data.first_track = bcd2bin(toc.first_track); 1124106490Smdodd sc->data.last_track = bcd2bin(toc.last_track); 1125106490Smdodd if (sc->data.last_track > (MAX_TRACKS-2)) 1126106490Smdodd sc->data.last_track = MAX_TRACKS-2; 1127106490Smdodd for (j = 0, i = sc->data.first_track; i <= sc->data.last_track; i++, j++) { 1128106490Smdodd sc->data.toc[i].adr = tl[j].adr; 1129106490Smdodd sc->data.toc[i].ctl = tl[j].ctl; /* for xcdplayer */ 1130106490Smdodd bcopy(tl[j].start_msf, sc->data.toc[i].start_msf, 3); 11317332Sjkh#ifdef SCD_DEBUG 11327332Sjkh if (scd_debuglevel > 0) { 1133106449Smdodd if ((j % 3) == 0) { 1134106449Smdodd printf("\n"); 1135106449Smdodd device_printf(sc->dev, "tracks "); 1136106449Smdodd } 11377332Sjkh printf("[%03d: %2d %2d %2d] ", i, 1138106490Smdodd bcd2bin(sc->data.toc[i].start_msf[0]), 1139106490Smdodd bcd2bin(sc->data.toc[i].start_msf[1]), 1140106490Smdodd bcd2bin(sc->data.toc[i].start_msf[2])); 11417332Sjkh } 11427332Sjkh#endif 11437332Sjkh } 1144106490Smdodd bcopy(toc.lead_out_start_msf, sc->data.toc[sc->data.last_track+1].start_msf, 3); 11457332Sjkh#ifdef SCD_DEBUG 11467332Sjkh if (scd_debuglevel > 0) { 1147106490Smdodd i = sc->data.last_track+1; 11487332Sjkh printf("[END: %2d %2d %2d]\n", 1149106490Smdodd bcd2bin(sc->data.toc[i].start_msf[0]), 1150106490Smdodd bcd2bin(sc->data.toc[i].start_msf[1]), 1151106490Smdodd bcd2bin(sc->data.toc[i].start_msf[2])); 11527332Sjkh } 11537332Sjkh#endif 11547332Sjkh 1155106490Smdodd sc->data.flags |= SCDTOC; 11567332Sjkh 1157106451Smdodd return (0); 11587332Sjkh} 11597332Sjkh 11607332Sjkhstatic void 1161106449Smdoddinit_drive(struct scd_softc *sc) 11627332Sjkh{ 11637332Sjkh int rc; 11647332Sjkh 1165106449Smdodd rc = send_cmd(sc, CMD_SET_DRIVE_PARAM, 2, 1166106449Smdodd 0x05, 0x03 | ((sc->data.double_speed) ? 0x04: 0)); 11677332Sjkh if (rc != 0) 1168106449Smdodd device_printf(sc->dev, "Unable to set parameters. Errcode = 0x%x\n", rc); 11697332Sjkh} 11707332Sjkh 11717332Sjkh/* Returns 0 or errno */ 11727332Sjkhstatic int 1173106449Smdoddget_result(struct scd_softc *sc, int result_len, u_char *result) 11747332Sjkh{ 11757332Sjkh int loop_index = 2; /* send_cmd() reads two bytes ... */ 11767332Sjkh 1177106449Smdodd XDEBUG(sc, 1, "DEBUG: get_result: bytes=%d\n", result_len); 11787332Sjkh 11797332Sjkh while (result_len-- > 0) { 11807332Sjkh if (loop_index++ >= 10) { 11817332Sjkh loop_index = 1; 1182106449Smdodd if (waitfor_status_bits(sc, SBIT_RESULT_READY, 0)) 1183106451Smdodd return (EIO); 1184106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); 11857332Sjkh } 11867332Sjkh if (result) 1187106449Smdodd *result++ = SCD_READ(sc, IREG_RESULT); 11887332Sjkh else 1189106449Smdodd (void)SCD_READ(sc, IREG_RESULT); 11907332Sjkh } 1191106451Smdodd return (0); 11927332Sjkh} 11937332Sjkh 11947332Sjkh/* Returns -0x100 for timeout, -(drive error code) OR number of result bytes */ 11957332Sjkhstatic int 1196106449Smdoddsend_cmd(struct scd_softc *sc, u_char cmd, u_int nargs, ...) 11977332Sjkh{ 11987332Sjkh va_list ap; 11997332Sjkh u_char c; 12007332Sjkh int rc; 12017332Sjkh int i; 12027332Sjkh 1203106449Smdodd if (waitfor_status_bits(sc, 0, SBIT_BUSY)) { 1204106449Smdodd device_printf(sc->dev, "drive busy\n"); 1205106451Smdodd return (-0x100); 12067332Sjkh } 12077332Sjkh 1208106449Smdodd XDEBUG(sc, 1, "DEBUG: send_cmd: cmd=0x%x nargs=%d", cmd, nargs); 12097332Sjkh 1210106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); 1211106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_RPARAM_CLEAR); 12127332Sjkh 12137332Sjkh for (i = 0; i < 100; i++) 1214106449Smdodd if (FSTATUS_BIT(sc, FBIT_WPARAM_READY)) 12157332Sjkh break; 1216106449Smdodd if (!FSTATUS_BIT(sc, FBIT_WPARAM_READY)) { 1217106449Smdodd XDEBUG(sc, 1, "\nwparam timeout\n"); 1218106451Smdodd return (-EIO); 12197332Sjkh } 12208876Srgrimes 12217332Sjkh va_start(ap, nargs); 12227332Sjkh for (i = 0; i < nargs; i++) { 12237332Sjkh c = (u_char)va_arg(ap, int); 1224106449Smdodd SCD_WRITE(sc, OREG_WPARAMS, c); 1225106449Smdodd XDEBUG(sc, 1, ",{0x%x}", c); 12267332Sjkh } 12277332Sjkh va_end(ap); 1228106449Smdodd XDEBUG(sc, 1, "\n"); 12297332Sjkh 1230106449Smdodd SCD_WRITE(sc, OREG_COMMAND, cmd); 12317332Sjkh 1232106449Smdodd rc = waitfor_status_bits(sc, SBIT_RESULT_READY, SBIT_BUSY); 123311872Sphk if (rc) 1234106451Smdodd return (-0x100); 12357332Sjkh 1236106449Smdodd SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); 1237106449Smdodd switch ((rc = SCD_READ(sc, IREG_RESULT)) & 0xf0) { 12387332Sjkh case 0x20: 1239106449Smdodd rc = SCD_READ(sc, IREG_RESULT); 1240102412Scharnier /* FALLTHROUGH */ 12417332Sjkh case 0x50: 1242106449Smdodd XDEBUG(sc, 1, "DEBUG: send_cmd: drive_error=0x%x\n", rc); 1243106451Smdodd return (-rc); 12447332Sjkh case 0x00: 12457332Sjkh default: 1246106449Smdodd rc = SCD_READ(sc, IREG_RESULT); 1247106449Smdodd XDEBUG(sc, 1, "DEBUG: send_cmd: result_len=%d\n", rc); 1248106451Smdodd return (rc); 12497332Sjkh } 12507332Sjkh} 12517332Sjkh 12527332Sjkhstatic void 1253106449Smdoddprint_error(struct scd_softc *sc, int errcode) 12547332Sjkh{ 1255106490Smdodd 12567332Sjkh switch (errcode) { 12577332Sjkh case -ERR_CD_NOT_LOADED: 1258106449Smdodd device_printf(sc->dev, "door is open\n"); 12597332Sjkh break; 12607332Sjkh case -ERR_NO_CD_INSIDE: 1261106449Smdodd device_printf(sc->dev, "no cd inside\n"); 12627332Sjkh break; 12637332Sjkh default: 12647332Sjkh if (errcode == -0x100 || errcode > 0) 1265106449Smdodd device_printf(sc->dev, "device timeout\n"); 12667332Sjkh else 1267106449Smdodd device_printf(sc->dev, "unexpected error 0x%x\n", -errcode); 12687332Sjkh break; 12697332Sjkh } 12707332Sjkh} 12717332Sjkh 12727332Sjkh/* Returns 0 or errno value */ 12737332Sjkhstatic int 1274106449Smdoddwaitfor_status_bits(struct scd_softc *sc, int bits_set, int bits_clear) 12757332Sjkh{ 1276106449Smdodd u_int flags = sc->data.flags; 12777332Sjkh u_int max_loop; 12787332Sjkh u_char c = 0; 12797332Sjkh 12807332Sjkh if (flags & SCDPROBING) { 12817332Sjkh max_loop = 0; 12827332Sjkh while (max_loop++ < 1000) { 1283106449Smdodd c = SCD_READ(sc, IREG_STATUS); 12847332Sjkh if (c == 0xff) 1285106451Smdodd return (EIO); 12867332Sjkh if (c & SBIT_ATTENTION) { 1287106449Smdodd process_attention(sc); 12887332Sjkh continue; 12897332Sjkh } 12907332Sjkh if ((c & bits_set) == bits_set && 12917332Sjkh (c & bits_clear) == 0) 12927332Sjkh { 12937332Sjkh break; 12947332Sjkh } 12957332Sjkh DELAY(10000); 12967332Sjkh } 12977332Sjkh } else { 12987332Sjkh max_loop = 100; 12997332Sjkh while (max_loop-- > 0) { 1300106449Smdodd c = SCD_READ(sc, IREG_STATUS); 13017332Sjkh if (c & SBIT_ATTENTION) { 1302106449Smdodd process_attention(sc); 13037332Sjkh continue; 13047332Sjkh } 13057332Sjkh if ((c & bits_set) == bits_set && 13067332Sjkh (c & bits_clear) == 0) 13077332Sjkh { 13087332Sjkh break; 13097332Sjkh } 1310274679Sjhb SCD_UNLOCK(sc); 1311167086Sjhb pause("waitfor", hz/10); 1312274679Sjhb SCD_LOCK(sc); 13137332Sjkh } 13147332Sjkh } 13157332Sjkh if ((c & bits_set) == bits_set && 13167332Sjkh (c & bits_clear) == 0) 13177332Sjkh { 1318106451Smdodd return (0); 13197332Sjkh } 13207332Sjkh#ifdef SCD_DEBUG 13217332Sjkh if (scd_debuglevel > 0) 1322106449Smdodd device_printf(sc->dev, "DEBUG: waitfor: TIMEOUT (0x%x,(0x%x,0x%x))\n", c, bits_set, bits_clear); 13237332Sjkh else 13247332Sjkh#endif 1325106449Smdodd device_printf(sc->dev, "timeout.\n"); 1326106451Smdodd return (EIO); 13277332Sjkh} 13287332Sjkh 13297332Sjkh/* these two routines for xcdplayer - "borrowed" from mcd.c */ 13307332Sjkhstatic int 1331106449Smdoddscd_toc_header (struct scd_softc *sc, struct ioc_toc_header* th) 13327332Sjkh{ 13337332Sjkh int rc; 13347332Sjkh 1335106490Smdodd if (!(sc->data.flags & SCDTOC) && (rc = read_toc(sc)) != 0) { 1336106449Smdodd print_error(sc, rc); 1337106451Smdodd return (EIO); 13387332Sjkh } 13397332Sjkh 1340106490Smdodd th->starting_track = sc->data.first_track; 1341106490Smdodd th->ending_track = sc->data.last_track; 13427332Sjkh th->len = 0; /* not used */ 13437332Sjkh 1344106451Smdodd return (0); 13457332Sjkh} 13467332Sjkh 13477332Sjkhstatic int 1348106449Smdoddscd_toc_entrys (struct scd_softc *sc, struct ioc_read_toc_entry *te) 13497332Sjkh{ 13507332Sjkh struct cd_toc_entry toc_entry; 13517332Sjkh int rc, i, len = te->data_len; 13527332Sjkh 1353106490Smdodd if (!(sc->data.flags & SCDTOC) && (rc = read_toc(sc)) != 0) { 1354106449Smdodd print_error(sc, rc); 1355106451Smdodd return (EIO); 13567332Sjkh } 13577332Sjkh 13587332Sjkh /* find the toc to copy*/ 13597332Sjkh i = te->starting_track; 13607332Sjkh if (i == SCD_LASTPLUS1) 1361106490Smdodd i = sc->data.last_track + 1; 13628876Srgrimes 13637332Sjkh /* verify starting track */ 1364106490Smdodd if (i < sc->data.first_track || i > sc->data.last_track+1) 1365106451Smdodd return (EINVAL); 13667332Sjkh 13677332Sjkh /* valid length ? */ 13687332Sjkh if (len < sizeof(struct cd_toc_entry) 13697332Sjkh || (len % sizeof(struct cd_toc_entry)) != 0) 1370106451Smdodd return (EINVAL); 13717332Sjkh 13727332Sjkh /* copy the toc data */ 1373106490Smdodd toc_entry.control = sc->data.toc[i].ctl; 13747332Sjkh toc_entry.addr_type = te->address_format; 13757332Sjkh toc_entry.track = i; 13767332Sjkh if (te->address_format == CD_MSF_FORMAT) { 13777332Sjkh toc_entry.addr.msf.unused = 0; 1378106490Smdodd toc_entry.addr.msf.minute = bcd2bin(sc->data.toc[i].start_msf[0]); 1379106490Smdodd toc_entry.addr.msf.second = bcd2bin(sc->data.toc[i].start_msf[1]); 1380106490Smdodd toc_entry.addr.msf.frame = bcd2bin(sc->data.toc[i].start_msf[2]); 13817332Sjkh } 1382274679Sjhb SCD_UNLOCK(sc); 13837332Sjkh 13847332Sjkh /* copy the data back */ 13857332Sjkh if (copyout(&toc_entry, te->data, sizeof(struct cd_toc_entry)) != 0) 1386106451Smdodd return (EFAULT); 13877332Sjkh 1388106451Smdodd return (0); 13897332Sjkh} 13907332Sjkh 139112517Sjulian 139225460Sjoergstatic int 1393106449Smdoddscd_toc_entry (struct scd_softc *sc, struct ioc_read_toc_single_entry *te) 139425460Sjoerg{ 139525460Sjoerg struct cd_toc_entry toc_entry; 139625460Sjoerg int rc, i; 139725460Sjoerg 1398106490Smdodd if (!(sc->data.flags & SCDTOC) && (rc = read_toc(sc)) != 0) { 1399106449Smdodd print_error(sc, rc); 1400106451Smdodd return (EIO); 140125460Sjoerg } 140225460Sjoerg 140325460Sjoerg /* find the toc to copy*/ 140425460Sjoerg i = te->track; 140525460Sjoerg if (i == SCD_LASTPLUS1) 1406106490Smdodd i = sc->data.last_track + 1; 140725460Sjoerg 140825460Sjoerg /* verify starting track */ 1409106490Smdodd if (i < sc->data.first_track || i > sc->data.last_track+1) 1410106451Smdodd return (EINVAL); 141125460Sjoerg 141225460Sjoerg /* copy the toc data */ 1413106490Smdodd toc_entry.control = sc->data.toc[i].ctl; 141425460Sjoerg toc_entry.addr_type = te->address_format; 141525460Sjoerg toc_entry.track = i; 141625460Sjoerg if (te->address_format == CD_MSF_FORMAT) { 141725460Sjoerg toc_entry.addr.msf.unused = 0; 1418106490Smdodd toc_entry.addr.msf.minute = bcd2bin(sc->data.toc[i].start_msf[0]); 1419106490Smdodd toc_entry.addr.msf.second = bcd2bin(sc->data.toc[i].start_msf[1]); 1420106490Smdodd toc_entry.addr.msf.frame = bcd2bin(sc->data.toc[i].start_msf[2]); 142125460Sjoerg } 142225460Sjoerg 142325460Sjoerg /* copy the data back */ 142425460Sjoerg bcopy(&toc_entry, &te->entry, sizeof(struct cd_toc_entry)); 142525460Sjoerg 1426106451Smdodd return (0); 142725460Sjoerg} 1428