root/drivers/cdrom/mcdx.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mcdx_ioctl
  2. do_mcdx_request
  3. mcdx_open
  4. mcdx_close
  5. check_mcdx_media_change
  6. mcdx_setup
  7. mcdx_delay
  8. mcdx_intr
  9. mcdx_talk
  10. init_module
  11. cleanup_module
  12. trace
  13. warn
  14. mcdx_init
  15. mcdx_transfer
  16. mcdx_xfer
  17. port
  18. irq
  19. bcd2uint
  20. uint2bcd
  21. log2msf
  22. msf2log
  23. mcdx_readtoc
  24. mcdx_playmsf
  25. mcdx_playtrk
  26. mcdx_closedoor
  27. mcdx_stop
  28. mcdx_hold
  29. mcdx_eject
  30. mcdx_requestsubqcode
  31. mcdx_requestmultidiskinfo
  32. mcdx_requesttocdata
  33. mcdx_setdrivemode
  34. mcdx_setdatamode
  35. mcdx_config
  36. mcdx_requestversion
  37. mcdx_reset
  38. mcdx_lockdoor
  39. mcdx_getstatus
  40. mcdx_getval
  41. mcdx_setattentuator

   1 /*
   2  * The Mitsumi CDROM interface
   3  * Copyright (C) 1995 Heiko Schlittermann <heiko@lotte.sax.de>
   4  * VERSION: 1.7
   5  * 
   6  * This program is free software; you can redistribute it and/or modify
   7  * it under the terms of the GNU General Public License as published by
   8  * the Free Software Foundation; either version 2, or (at your option)
   9  * any later version.
  10  * 
  11  * This program is distributed in the hope that it will be useful,
  12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14  * GNU General Public License for more details.
  15  * 
  16  * You should have received a copy of the GNU General Public License
  17  * along with this program; see the file COPYING.  If not, write to
  18  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19  *
  20  * Thanks to
  21  *  The Linux Community at all and ...
  22  *  Martin Harriss (he wrote the first Mitsumi Driver)
  23  *  Eberhard Moenkeberg (he gave me much support and the initial kick)
  24  *  Bernd Huebner, Ruediger Helsch (Unifix-Software GmbH, they
  25  *      improved the original driver)
  26  *  Jon Tombs, Bjorn Ekwall (module support)
  27  *  Daniel v. Mosnenck (he sent me the Technical and Programming Reference)
  28  *  Gerd Knorr (he lent me his PhotoCD)
  29  *  Nils Faerber and Roger E. Wolff (extensivly tested the LU portion)
  30  *  Andreas Kies (testing the mysterious hang up's)
  31  *  ... somebody forgotten?
  32  *  
  33  */
  34 
  35 
  36 #if RCS
  37 static const char *mcdx_c_version
  38                 = "mcdx.c,v 1.31 1996/03/02 00:21:18 heiko Exp";
  39 #endif
  40 
  41 #include <linux/version.h>
  42 #include <linux/module.h>
  43 
  44 #include <linux/errno.h>
  45 #include <linux/sched.h>
  46 #include <linux/timer.h>
  47 #include <linux/fs.h>
  48 #include <linux/kernel.h>
  49 #include <linux/cdrom.h>
  50 #include <linux/ioport.h>
  51 #include <linux/mm.h>
  52 #include <linux/malloc.h>
  53 #include <asm/system.h>
  54 #include <asm/io.h>
  55 #include <asm/segment.h>
  56 
  57 
  58 #include <linux/major.h>
  59 #define MAJOR_NR MITSUMI_X_CDROM_MAJOR
  60 #include <linux/blk.h>
  61 
  62 /* for compatible parameter passing with "insmod" */ 
  63 #define mcdx_drive_map mcdx    
  64 #include <linux/mcdx.h>
  65 
  66 #ifndef HZ 
  67 #error HZ not defined
  68 #endif
  69 
  70 /* CONSTANTS *******************************************************/
  71 
  72 const int REQUEST_SIZE = 200;
  73 const int DIRECT_SIZE = 200;
  74 const unsigned long ACLOSE_INHIBIT = 800;  /* 1/100 s of autoclose inhibit */
  75 
  76 enum drivemodes { TOC, DATA, RAW, COOKED };
  77 enum datamodes { MODE0, MODE1, MODE2 };
  78 enum resetmodes { SOFT, HARD };
  79 
  80 const int SINGLE = 0x01;                /* single speed drive (FX001S, LU) */
  81 const int DOUBLE = 0x02;                /* double speed drive (FX001D, ..? */
  82 const int DOOR   = 0x04;                /* door locking capability */
  83 const int MULTI  = 0x08;                /* multi session capability */
  84 
  85 const unsigned char READ1X = 0xc0;
  86 const unsigned char READ2X = 0xc1;
  87 
  88 
  89 /* DECLARATIONS ****************************************************/ 
  90 struct s_msf {
  91         unsigned char minute;
  92         unsigned char second;
  93         unsigned char frame;
  94 };
  95 
  96 struct s_subqcode {
  97         unsigned char control;
  98         unsigned char tno;
  99         unsigned char index;
 100         struct s_msf tt;
 101         struct s_msf dt;
 102 };
 103 
 104 struct s_diskinfo {
 105         unsigned int n_first;
 106         unsigned int n_last;
 107         struct s_msf msf_leadout;
 108         struct s_msf msf_first;
 109 };
 110         
 111 struct s_multi {
 112         unsigned char multi;
 113         struct s_msf msf_last;
 114 };
 115 
 116 struct s_version {
 117         unsigned char code;
 118         unsigned char ver;
 119 };
 120 
 121 /* Per drive/controller stuff **************************************/
 122 
 123 struct s_drive_stuff {
 124         /* waitquenes */
 125     struct wait_queue *busyq;
 126     struct wait_queue *lockq;
 127     struct wait_queue *sleepq;
 128 
 129         /* flags */
 130     volatile int introk;        /* status of last irq operation */
 131     volatile int busy;          /* drive performs an operation */
 132     volatile int lock;          /* exclusive usage */
 133     int eject_sw;           /* 1 - eject on last close (default 0) */
 134     int autoclose;          /* 1 - close the door on open (default 1) */
 135     
 136         /* cd infos */
 137         struct s_diskinfo di;
 138         struct s_multi multi;
 139         struct s_subqcode* toc; /* first enty of the toc array */
 140         struct s_subqcode start;
 141     struct s_subqcode stop;
 142         int xa;                                 /* 1 if xa disk */
 143         int audio;                              /* 1 if audio disk */
 144         int audiostatus;                        
 145 
 146         /* `buffer' control */
 147     volatile int valid;
 148     volatile int pending;
 149     volatile int off_direct;
 150     volatile int off_requested;
 151 
 152         /* adds and odds */
 153         void* wreg_data;        /* w data */
 154         void* wreg_reset;       /* w hardware reset */
 155         void* wreg_hcon;        /* w hardware conf */
 156         void* wreg_chn;         /* w channel */
 157         void* rreg_data;        /* r data */
 158         void* rreg_status;      /* r status */
 159 
 160     int irq;                    /* irq used by this drive */
 161     int minor;                  /* minor number of this drive */
 162     int present;            /* drive present and its capabilities */
 163     unsigned char readcmd;      /* read cmd depends on single/double speed */
 164     unsigned char playcmd;  /* play should always be single speed */
 165     unsigned int xxx;      /* set if changed, reset while open */
 166     unsigned int yyy;      /* set if changed, reset by media_changed */
 167     unsigned long ejected;  /* time we called the eject function */
 168     int users;                          /* keeps track of open/close */
 169     int lastsector;                     /* last block accessible */
 170     int status;                         /* last operation's error / status */
 171         int readerrs;                   /* # of blocks read w/o error */
 172 };
 173 
 174 
 175 /* Prototypes ******************************************************/ 
 176 
 177 /*      The following prototypes are already declared elsewhere.  They are
 178         repeated here to show what's going on.  And to sense, if they're
 179         changed elsewhere. */
 180 
 181 /* declared in blk.h */
 182 int mcdx_init(void);
 183 void do_mcdx_request(void);
 184 int check_mcdx_media_change(kdev_t);
 185 
 186 /* already declared in init/main */
 187 void mcdx_setup(char *, int *);
 188 
 189 /*      Indirect exported functions. These functions are exported by their
 190         addresses, such as mcdx_open and mcdx_close in the 
 191         structure fops. */
 192 
 193 /* ???  exported by the mcdx_sigaction struct */
 194 static void mcdx_intr(int, void *, struct pt_regs*);
 195 
 196 /* exported by file_ops */
 197 static int mcdx_open(struct inode*, struct file*);
 198 static void mcdx_close(struct inode*, struct file*);
 199 static int mcdx_ioctl(struct inode*, struct file*, unsigned int, unsigned long);
 200 
 201 /* misc internal support functions */
 202 static void log2msf(unsigned int, struct s_msf*);
 203 static unsigned int msf2log(const struct s_msf*);
 204 static unsigned int uint2bcd(unsigned int);
 205 static unsigned int bcd2uint(unsigned char);
 206 #if MCDX_DEBUG
 207 static void TRACE((int level, const char* fmt, ...));
 208 #endif
 209 static void warn(const char* fmt, ...);
 210 static char *port(int*);
 211 static int irq(int*);
 212 static void mcdx_delay(struct s_drive_stuff*, long jifs);
 213 static int mcdx_transfer(struct s_drive_stuff*, char* buf, int sector, int nr_sectors);
 214 static int mcdx_xfer(struct s_drive_stuff*, char* buf, int sector, int nr_sectors);
 215 
 216 static int mcdx_config(struct s_drive_stuff*, int);
 217 static int mcdx_closedoor(struct s_drive_stuff*, int);
 218 static int mcdx_requestversion(struct s_drive_stuff*, struct s_version*, int);
 219 static int mcdx_lockdoor(struct s_drive_stuff*, int, int);
 220 static int mcdx_stop(struct s_drive_stuff*, int);
 221 static int mcdx_hold(struct s_drive_stuff*, int);
 222 static int mcdx_reset(struct s_drive_stuff*, enum resetmodes, int);
 223 static int mcdx_eject(struct s_drive_stuff*, int);
 224 static int mcdx_setdrivemode(struct s_drive_stuff*, enum drivemodes, int);
 225 static int mcdx_setdatamode(struct s_drive_stuff*, enum datamodes, int);
 226 static int mcdx_requestsubqcode(struct s_drive_stuff*, struct s_subqcode*, int);
 227 static int mcdx_requestmultidiskinfo(struct s_drive_stuff*, struct s_multi*, int);
 228 static int mcdx_requesttocdata(struct s_drive_stuff*, struct s_diskinfo*, int);
 229 static int mcdx_getstatus(struct s_drive_stuff*, int);
 230 static int mcdx_getval(struct s_drive_stuff*, int to, int delay, char*);
 231 static int mcdx_talk(struct s_drive_stuff*, 
 232                 const unsigned char* cmd, size_t, 
 233         void *buffer, size_t size, 
 234         unsigned int timeout, int);
 235 static int mcdx_readtoc(struct s_drive_stuff*);
 236 static int mcdx_playtrk(struct s_drive_stuff*, const struct cdrom_ti*);
 237 static int mcdx_playmsf(struct s_drive_stuff*, const struct cdrom_msf*);
 238 static int mcdx_setattentuator(struct s_drive_stuff*, struct cdrom_volctrl*, int);
 239 
 240 /* static variables ************************************************/
 241 
 242 static int mcdx_blocksizes[MCDX_NDRIVES];
 243 static int mcdx_drive_map[][2] = MCDX_DRIVEMAP;
 244 static struct s_drive_stuff* mcdx_stuffp[MCDX_NDRIVES];
 245 static struct s_drive_stuff* mcdx_irq_map[16] =
 246                 {0, 0, 0, 0, 0, 0, 0, 0,
 247                 0, 0, 0, 0, 0, 0, 0, 0};
 248 
 249 static struct file_operations mcdx_fops = {
 250         NULL,                   /* lseek - use kernel default */
 251         block_read,             /* read - general block-dev read */
 252         block_write,    /* write - general block-dev write */
 253         NULL,                   /* no readdir */
 254         NULL,                   /* no select */
 255         mcdx_ioctl,             /* ioctl() */
 256         NULL,                   /* no mmap */
 257         mcdx_open,              /* open() */
 258         mcdx_close,             /* close() */
 259         NULL,                   /* fsync */
 260         NULL,                   /* fasync */
 261         check_mcdx_media_change, /* media_change */
 262         NULL                    /* revalidate */
 263 };
 264 
 265 /* KERNEL INTERFACE FUNCTIONS **************************************/ 
 266 
 267 static int 
 268 mcdx_ioctl(
     /* [previous][next][first][last][top][bottom][index][help] */
 269         struct inode* ip, struct file* fp, 
 270         unsigned int cmd, unsigned long arg)
 271 { 
 272         struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
 273 
 274         if (!stuffp->present) return -ENXIO;
 275         if (!ip) return -EINVAL;
 276 
 277         switch (cmd) {
 278                 case CDROMSTART: {
 279                         TRACE((IOCTL, "ioctl() START\n"));
 280                         return 0;
 281                 }
 282 
 283                 case CDROMSTOP: {
 284                         TRACE((IOCTL, "ioctl() STOP\n"));
 285             stuffp->audiostatus = CDROM_AUDIO_INVALID;
 286                         if (-1 == mcdx_stop(stuffp, 1))
 287                                 return -EIO;
 288                         return 0;
 289                 }
 290 
 291                 case CDROMPLAYTRKIND: {
 292                         int ans;
 293                         struct cdrom_ti ti;
 294 
 295                         TRACE((IOCTL, "ioctl() PLAYTRKIND\n"));
 296                         if ((ans = verify_area(VERIFY_READ, (void*) arg, sizeof(ti))))
 297                                 return ans;
 298                         memcpy_fromfs(&ti, (void*) arg, sizeof(ti));
 299                         if ((ti.cdti_trk0 < stuffp->di.n_first)
 300                                         || (ti.cdti_trk0 > stuffp->di.n_last)
 301                                         || (ti.cdti_trk1 < stuffp->di.n_first))
 302                                 return -EINVAL;
 303                         if (ti.cdti_trk1 > stuffp->di.n_last) ti.cdti_trk1 = stuffp->di.n_last;
 304             TRACE((PLAYTRK, "ioctl() track %d to %d\n", ti.cdti_trk0, ti.cdti_trk1));
 305 
 306             return mcdx_playtrk(stuffp, &ti);
 307         }
 308 
 309         case CDROMPLAYMSF: {
 310             int ans;
 311             struct cdrom_msf msf;
 312 
 313             TRACE((IOCTL, "ioctl() PLAYMSF\n"));
 314 
 315             if ((stuffp->audiostatus == CDROM_AUDIO_PLAY)
 316                 && (-1 == mcdx_hold(stuffp, 1))) return -EIO;
 317 
 318             if ((ans = verify_area(
 319                     VERIFY_READ, (void*) arg, sizeof(struct cdrom_msf)))) 
 320                 return ans;
 321 
 322             memcpy_fromfs(&msf, (void*) arg, sizeof msf);
 323 
 324             msf.cdmsf_min0 = uint2bcd(msf.cdmsf_min0);
 325             msf.cdmsf_sec0 = uint2bcd(msf.cdmsf_sec0);
 326             msf.cdmsf_frame0 = uint2bcd(msf.cdmsf_frame0);
 327 
 328             msf.cdmsf_min1 = uint2bcd(msf.cdmsf_min1);
 329             msf.cdmsf_sec1 = uint2bcd(msf.cdmsf_sec1);
 330             msf.cdmsf_frame1 = uint2bcd(msf.cdmsf_frame1);
 331 
 332             return mcdx_playmsf(stuffp, &msf);
 333         }
 334 
 335         case CDROMRESUME: {
 336             TRACE((IOCTL, "ioctl() RESUME\n"));
 337             return mcdx_playtrk(stuffp, NULL);
 338         }
 339 
 340                 case CDROMREADTOCENTRY: {
 341                         struct cdrom_tocentry entry;
 342                         struct s_subqcode *tp = NULL;
 343                         int ans;
 344 
 345                         TRACE((IOCTL, "ioctl() READTOCENTRY\n"));
 346 
 347             if (-1 == mcdx_readtoc(stuffp)) return -1;
 348 
 349                         if ((ans = verify_area(VERIFY_READ, (void *) arg, sizeof(entry)))) return ans;
 350                         memcpy_fromfs(&entry, (void *) arg, sizeof(entry));
 351 
 352                         if (entry.cdte_track == CDROM_LEADOUT) 
 353                                 tp = &stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1];
 354                         else if (entry.cdte_track > stuffp->di.n_last 
 355                                         || entry.cdte_track < stuffp->di.n_first) return -EINVAL;
 356                         else tp = &stuffp->toc[entry.cdte_track - stuffp->di.n_first];
 357 
 358                         if (NULL == tp) WARN(("FATAL.\n"));
 359 
 360                         entry.cdte_adr = tp->control;
 361                         entry.cdte_ctrl = tp->control >> 4;
 362 
 363                         if (entry.cdte_format == CDROM_MSF) {
 364                                 entry.cdte_addr.msf.minute = bcd2uint(tp->dt.minute);
 365                                 entry.cdte_addr.msf.second = bcd2uint(tp->dt.second);
 366                                 entry.cdte_addr.msf.frame = bcd2uint(tp->dt.frame);
 367                         } else if (entry.cdte_format == CDROM_LBA)
 368                                 entry.cdte_addr.lba = msf2log(&tp->dt);
 369                         else return -EINVAL;
 370 
 371                         if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(entry)))) return ans;
 372                         memcpy_tofs((void*) arg, &entry, sizeof(entry));
 373 
 374                         return 0;
 375                 }
 376 
 377                 case CDROMSUBCHNL: {
 378                         int ans;
 379                         struct cdrom_subchnl sub;
 380                         struct s_subqcode q;
 381 
 382                         TRACE((IOCTL, "ioctl() SUBCHNL\n"));
 383 
 384                         if ((ans = verify_area(VERIFY_READ, 
 385                     (void*) arg, sizeof(sub)))) return ans;
 386 
 387                         memcpy_fromfs(&sub, (void*) arg, sizeof(sub));
 388 
 389                         if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) return -EIO;
 390 
 391             TRACE((SUBCHNL, "audiostatus: %x\n", stuffp->audiostatus));
 392                         sub.cdsc_audiostatus = stuffp->audiostatus;
 393                         sub.cdsc_adr = q.control;
 394                         sub.cdsc_ctrl = q.control >> 4;
 395                         sub.cdsc_trk = bcd2uint(q.tno);
 396                         sub.cdsc_ind = bcd2uint(q.index);
 397 
 398             TRACE((SUBCHNL, "trk %d, ind %d\n", 
 399                     sub.cdsc_trk, sub.cdsc_ind));
 400 
 401                         if (sub.cdsc_format == CDROM_LBA) {
 402                                 sub.cdsc_absaddr.lba = msf2log(&q.dt);
 403                                 sub.cdsc_reladdr.lba = msf2log(&q.tt);
 404                 TRACE((SUBCHNL, "lba: abs %d, rel %d\n",
 405                     sub.cdsc_absaddr.lba,
 406                     sub.cdsc_reladdr.lba));
 407                         } else if (sub.cdsc_format == CDROM_MSF) {
 408                                 sub.cdsc_absaddr.msf.minute = bcd2uint(q.dt.minute);
 409                                 sub.cdsc_absaddr.msf.second = bcd2uint(q.dt.second);
 410                                 sub.cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame);
 411                                 sub.cdsc_reladdr.msf.minute = bcd2uint(q.tt.minute);
 412                                 sub.cdsc_reladdr.msf.second = bcd2uint(q.tt.second);
 413                                 sub.cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame);
 414                 TRACE((SUBCHNL,
 415                         "msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n",
 416                         sub.cdsc_absaddr.msf.minute,
 417                         sub.cdsc_absaddr.msf.second,
 418                         sub.cdsc_absaddr.msf.frame,
 419                         sub.cdsc_reladdr.msf.minute,
 420                         sub.cdsc_reladdr.msf.second,
 421                         sub.cdsc_reladdr.msf.frame));
 422                         } else return -EINVAL;
 423 
 424                         if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(sub))))
 425                                 return ans;
 426                         memcpy_tofs((void*) arg, &sub, sizeof(sub));
 427 
 428                         return 0;
 429                 }
 430 
 431                 case CDROMREADTOCHDR: {
 432                         struct cdrom_tochdr toc;
 433                         int ans;
 434 
 435                         TRACE((IOCTL, "ioctl() READTOCHDR\n"));
 436                         if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof toc)))
 437                                 return ans;
 438                         toc.cdth_trk0 = stuffp->di.n_first;
 439                         toc.cdth_trk1 = stuffp->di.n_last;
 440                         memcpy_tofs((void*) arg, &toc, sizeof toc);
 441                         TRACE((TOCHDR, "ioctl() track0 = %d, track1 = %d\n",
 442                                         stuffp->di.n_first, stuffp->di.n_last));
 443                         return 0;
 444                 }
 445 
 446                 case CDROMPAUSE: {
 447                         TRACE((IOCTL, "ioctl() PAUSE\n"));
 448                         if (stuffp->audiostatus != CDROM_AUDIO_PLAY) return -EINVAL;
 449                         if (-1 == mcdx_stop(stuffp, 1)) return -EIO;
 450             stuffp->audiostatus = CDROM_AUDIO_PAUSED;
 451                         if (-1 == mcdx_requestsubqcode(stuffp, &stuffp->start, 1))
 452                                 return -EIO;
 453                         return 0;
 454                 }
 455 
 456                 case CDROMMULTISESSION: {
 457                         int ans;
 458                         struct cdrom_multisession ms;
 459                         TRACE((IOCTL, "ioctl() MULTISESSION\n"));
 460                         if (0 != (ans = verify_area(VERIFY_READ, (void*) arg, 
 461                                         sizeof(struct cdrom_multisession))))
 462                                 return ans;
 463                                 
 464                         memcpy_fromfs(&ms, (void*) arg, sizeof(struct cdrom_multisession));
 465                         if (ms.addr_format == CDROM_MSF) {
 466                                 ms.addr.msf.minute = bcd2uint(stuffp->multi.msf_last.minute);
 467                                 ms.addr.msf.second = bcd2uint(stuffp->multi.msf_last.second);
 468                                 ms.addr.msf.frame = bcd2uint(stuffp->multi.msf_last.frame);
 469                         } else if (ms.addr_format == CDROM_LBA)
 470                                 ms.addr.lba = msf2log(&stuffp->multi.msf_last);
 471                         else
 472                                 return -EINVAL;
 473                         ms.xa_flag = !!stuffp->multi.multi;
 474 
 475                         if (0 != (ans = verify_area(VERIFY_WRITE, (void*) arg,
 476                                         sizeof(struct cdrom_multisession))))
 477                                 return ans;
 478 
 479                         memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession));
 480                         if (ms.addr_format == CDROM_MSF) 
 481                                 TRACE((MS, 
 482                                                 "ioctl() (%d, %02x:%02x.%02x [%02x:%02x.%02x])\n",
 483                                                 ms.xa_flag, 
 484                                                 ms.addr.msf.minute,
 485                                                 ms.addr.msf.second,
 486                                                 ms.addr.msf.frame,
 487                                                 stuffp->multi.msf_last.minute,
 488                                                 stuffp->multi.msf_last.second,
 489                                                 stuffp->multi.msf_last.frame));
 490                         else
 491                           {
 492                             TRACE((MS, 
 493                                         "ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n",
 494                                         ms.xa_flag,
 495                                         ms.addr.lba,
 496                                         stuffp->multi.msf_last.minute,
 497                                         stuffp->multi.msf_last.second,
 498                                         stuffp->multi.msf_last.frame));
 499                           }
 500                         return 0;
 501                 }
 502 
 503                 case CDROMEJECT: {
 504                         TRACE((IOCTL, "ioctl() EJECT\n"));
 505                         if (stuffp->users > 1) return -EBUSY;
 506                         if (-1 == mcdx_eject(stuffp, 1)) return -EIO;
 507                         return 0;
 508                 }
 509 
 510         case CDROMEJECT_SW: {
 511             stuffp->eject_sw = arg;
 512             return 0;
 513         }
 514 
 515         case CDROMVOLCTRL: {
 516             int ans;
 517             struct cdrom_volctrl volctrl;
 518 
 519             TRACE((IOCTL, "ioctl() VOLCTRL\n"));
 520             if ((ans = verify_area(
 521                     VERIFY_READ, 
 522                     (void*) arg,
 523                     sizeof(volctrl))))
 524                 return ans;
 525 
 526             memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
 527             return mcdx_setattentuator(stuffp, &volctrl, 1);
 528         }
 529 
 530                 default:
 531                         WARN(("ioctl(): unknown request 0x%04x\n", cmd));
 532                 return -EINVAL;
 533         }
 534 }
 535 
 536 void do_mcdx_request()
     /* [previous][next][first][last][top][bottom][index][help] */
 537 {
 538     int dev;
 539     struct s_drive_stuff *stuffp;
 540 
 541   again:
 542 
 543         TRACE((REQUEST, "do_request()\n"));
 544 
 545         if ((CURRENT == NULL) || (CURRENT->rq_status == RQ_INACTIVE))  {
 546                 TRACE((REQUEST, "do_request() done\n"));
 547                 return;
 548         }
 549 
 550     stuffp = mcdx_stuffp[MINOR(CURRENT->rq_dev)];
 551         TRACE((REQUEST, "do_request() stuffp = %p\n", stuffp));
 552 
 553     INIT_REQUEST;
 554     dev = MINOR(CURRENT->rq_dev);
 555 
 556         if ((dev < 0) || (dev >= MCDX_NDRIVES) || (!stuffp->present)) {
 557                 WARN(("do_request(): bad device: %s\n", 
 558             kdevname(CURRENT->rq_dev)));
 559                 end_request(0);
 560                 goto again;
 561     }
 562 
 563         if (stuffp->audio) {
 564                 WARN(("do_request() attempt to read from audio cd\n"));
 565                 end_request(0);
 566                 goto again;
 567         }
 568 
 569     switch (CURRENT->cmd) {
 570       case WRITE:
 571           WARN(("do_request(): attempt to write to cd!!\n"));
 572           end_request(0);
 573           break;
 574 
 575       case READ:
 576           stuffp->status = 0;
 577           while (CURRENT->nr_sectors) {
 578               int i;
 579 
 580               if (-1 == (i = mcdx_transfer(
 581                                       stuffp,
 582                                       CURRENT->buffer,
 583                                       CURRENT->sector,
 584                                       CURRENT->nr_sectors))) {
 585               INFO(("do_request() read error\n"));
 586               if (stuffp->status & MCDX_ST_EOM) {
 587                   CURRENT->sector += CURRENT->nr_sectors;
 588                   CURRENT->nr_sectors = 0;
 589               } 
 590               end_request(0);
 591                           /* return; */
 592               goto again;
 593               }
 594               CURRENT->sector += i;
 595               CURRENT->nr_sectors -= i;
 596               CURRENT->buffer += (i * 512);
 597 
 598           }
 599 
 600           end_request(1);
 601           break;
 602 
 603       default:
 604           panic(MCDX "do_request: unknown command.\n");
 605           break;
 606     }
 607 
 608     goto again;
 609 }
 610 
 611 static int 
 612 mcdx_open(struct inode *ip, struct file *fp)
     /* [previous][next][first][last][top][bottom][index][help] */
 613 /*  actions done on open:
 614  *  1)  get the drives status */
 615 {
 616     struct s_drive_stuff *stuffp;
 617 
 618         TRACE((OPENCLOSE, "open()\n"));
 619     stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
 620     if (!stuffp->present) return -ENXIO;
 621 
 622     /* this is only done to test if the drive talks with us */
 623     if (-1 == mcdx_getstatus(stuffp, 1)) return -EIO;
 624 
 625         /* close the door, if necessary (get the door information
 626     from the hardware status register). 
 627     If the last eject is too recent, an autoclose wouldn't probably
 628     be what we want ..., if we can't read the CD after an autoclose
 629     no further autclose's will be tried */
 630         if (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_DOOR) {
 631         if (jiffies - stuffp->ejected < ACLOSE_INHIBIT) return -EIO;
 632         if (stuffp->autoclose) mcdx_closedoor(stuffp, 1);
 633                 else return -EIO;
 634     }
 635 
 636         /* if the media changed we will have to do a little more */
 637         if (stuffp->xxx) {
 638 
 639                 TRACE((OPENCLOSE, "open() media changed\n"));
 640         /* but wait - the time of media change will be set at the 
 641            very last of this block - it seems, some of the following
 642            talk() will detect a media change ... (I think, config()
 643            is the reason. */
 644 
 645         stuffp->audiostatus = CDROM_AUDIO_INVALID;
 646 
 647                 /* get the multisession information */
 648                 TRACE((OPENCLOSE, "open() Request multisession info\n"));
 649                 if (-1 == mcdx_requestmultidiskinfo(
 650                                 stuffp, &stuffp->multi, 6)) {
 651                         INFO(("No multidiskinfo\n"));
 652                         stuffp->autoclose = 0;
 653                 } else {
 654                         /* we succeeded, so on next open(2) we could try auto close
 655                         again */
 656                         stuffp->autoclose = 1;
 657                 
 658 #if !MCDX_QUIET
 659                         if (stuffp->multi.multi > 2)
 660                                 INFO(("open() unknown multisession value (%d)\n", 
 661                                                 stuffp->multi.multi));
 662 #endif
 663 
 664                         /* multisession ? */
 665                         if (!stuffp->multi.multi)
 666                                 stuffp->multi.msf_last.second = 2;
 667 
 668                         TRACE((OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n",
 669                                         stuffp->multi.multi,
 670                                         stuffp->multi.msf_last.minute,
 671                                         stuffp->multi.msf_last.second,
 672                                         stuffp->multi.msf_last.frame));
 673 
 674                 } /* got multisession information */
 675 
 676                 /* request the disks table of contents (aka diskinfo) */
 677                 if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) {
 678 
 679                         stuffp->lastsector = -1;
 680 
 681                 } else {
 682 
 683                         stuffp->lastsector = (CD_FRAMESIZE / 512) 
 684                                         * msf2log(&stuffp->di.msf_leadout) - 1;
 685 
 686                         TRACE((OPENCLOSE, "open() start %d (%02x:%02x.%02x) %d\n",
 687                                         stuffp->di.n_first,
 688                                         stuffp->di.msf_first.minute,
 689                                         stuffp->di.msf_first.second,
 690                                         stuffp->di.msf_first.frame,
 691                                         msf2log(&stuffp->di.msf_first)));
 692                         TRACE((OPENCLOSE, "open() last %d (%02x:%02x.%02x) %d\n",
 693                                         stuffp->di.n_last,
 694                                         stuffp->di.msf_leadout.minute,
 695                                         stuffp->di.msf_leadout.second,
 696                                         stuffp->di.msf_leadout.frame,
 697                                         msf2log(&stuffp->di.msf_leadout)));
 698                 }
 699 
 700                 if (stuffp->toc) {
 701                         TRACE((MALLOC, "open() free old toc @ %p\n", stuffp->toc));
 702                         kfree(stuffp->toc);
 703                 
 704                         stuffp->toc = NULL;
 705                 }
 706 
 707                 TRACE((OPENCLOSE, "open() init irq generation\n"));
 708                 if (-1 == mcdx_config(stuffp, 1)) return -EIO;
 709 
 710 #if FALLBACK
 711                 /* Set the read speed */
 712                 WARN(("AAA %x AAA\n", stuffp->readcmd));
 713                 if (stuffp->readerrs) stuffp->readcmd = READ1X;
 714                 else stuffp->readcmd = 
 715                                 stuffp->present | SINGLE ? READ1X : READ2X;
 716                 WARN(("XXX %x XXX\n", stuffp->readcmd));
 717 #endif
 718 
 719                 /* try to get the first sector, iff any ... */
 720                 if (stuffp->lastsector >= 0) {
 721                         char buf[512];
 722                         int ans;
 723                         int tries;
 724 
 725                         stuffp->xa = 0;
 726                         stuffp->audio = 0;
 727 
 728                         for (tries = 6; tries; tries--) {
 729 
 730                                 stuffp->introk = 1;
 731 
 732                                 TRACE((OPENCLOSE, "open() try as %s\n",
 733                                         stuffp->xa ? "XA" : "normal"));
 734 
 735                                 /* set data mode */
 736                                 if (-1 == (ans = mcdx_setdatamode(stuffp, 
 737                                                 stuffp->xa ? MODE2 : MODE1, 1))) {
 738                                         /* return -EIO; */
 739                                         stuffp->xa = 0;
 740                                         break;
 741                                 }
 742 
 743                                 if ((stuffp->audio = e_audio(ans))) break; 
 744 
 745                                 while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1))) 
 746                                         ;
 747 
 748                                 if (ans == 1) break;
 749                                 stuffp->xa = !stuffp->xa; 
 750                         }
 751                         /* if (!tries) return -EIO; */
 752                 }
 753 
 754                 /* xa disks will be read in raw mode, others not */
 755                 if (-1 == mcdx_setdrivemode(stuffp, 
 756                                 stuffp->xa ? RAW : COOKED, 1))
 757                         return -EIO;
 758 
 759                 if (stuffp->audio) {
 760                         INFO(("open() audio disk found\n"));
 761                 } else if (stuffp->lastsector >= 0) {
 762                         INFO(("open() %s%s disk found\n",
 763                                         stuffp->xa ? "XA / " : "",
 764                                         stuffp->multi.multi ? "Multi Session" : "Single Session"));
 765                 } 
 766 
 767         stuffp->xxx = 0;
 768         }
 769 
 770     /* lock the door if not already done */
 771     if (0 == stuffp->users && (-1 == mcdx_lockdoor(stuffp, 1, 1))) 
 772         return -EIO;
 773 
 774     stuffp->users++;
 775     MOD_INC_USE_COUNT;
 776     return 0;
 777 }
 778 
 779 static void 
 780 mcdx_close(struct inode *ip, struct file *fp)
     /* [previous][next][first][last][top][bottom][index][help] */
 781 {
 782     struct s_drive_stuff *stuffp;
 783 
 784     TRACE((OPENCLOSE, "close()\n"));
 785 
 786     stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
 787 
 788     if (0 == --stuffp->users) {
 789                 sync_dev(ip->i_rdev);   /* needed for r/o device? */
 790 
 791                 /* invalidate_inodes(ip->i_rdev); */
 792                 invalidate_buffers(ip->i_rdev);
 793 
 794 
 795 #if !MCDX_QUIET
 796                 if (-1 == mcdx_lockdoor(stuffp, 0, 3))
 797                                 INFO(("close() Cannot unlock the door\n"));
 798 #else
 799                 mcdx_lockdoor(stuffp, 0, 3);
 800 #endif
 801 
 802         /* eject if wished */
 803         if (stuffp->eject_sw) mcdx_eject(stuffp, 1);
 804 
 805     }
 806     MOD_DEC_USE_COUNT;
 807 
 808     return;
 809 }
 810 
 811 int check_mcdx_media_change(kdev_t full_dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 812 /*      Return: 1 if media changed since last call to this function
 813                         0 otherwise */
 814 {
 815     struct s_drive_stuff *stuffp; 
 816 
 817     INFO(("check_mcdx_media_change called for device %s\n",
 818           kdevname(full_dev)));
 819 
 820         stuffp = mcdx_stuffp[MINOR(full_dev)];
 821         mcdx_getstatus(stuffp, 1);
 822 
 823         if (stuffp->yyy == 0) {
 824                 INFO((" ... not changed\n"));
 825                 return 0;
 826         }
 827 
 828         INFO((" ... changed\n"));
 829         stuffp->yyy = 0;
 830         return 1;
 831 }
 832 
 833 void mcdx_setup(char *str, int *pi)
     /* [previous][next][first][last][top][bottom][index][help] */
 834 {
 835         if (pi[0] > 0) mcdx_drive_map[0][0] = pi[1];
 836         if (pi[0] > 1) mcdx_drive_map[0][1] = pi[2];
 837 }
 838 
 839 /* DIRTY PART ******************************************************/ 
 840 
 841 static void mcdx_delay(struct s_drive_stuff *stuff, long jifs)
     /* [previous][next][first][last][top][bottom][index][help] */
 842 /*      This routine is used for sleeping.
 843         May be we could use a simple count loop w/ jumps to itself, but 
 844         I wanna make this independend of cpu speed. [1 jiffie is 1/HZ] sec */
 845 {
 846     unsigned long tout = jiffies + jifs;
 847 
 848     /* TRACE((INIT, "mcdx_delay %d\n", jifs)); */
 849     if (jifs <= 0) return;
 850 
 851     if (current->pid == 0) {        /* no sleep allowed */
 852                 while (jiffies < tout) {
 853             current->timeout = jiffies;
 854             schedule();
 855         }
 856     } else {                        /* sleeping is allowed */
 857         current->timeout = tout;
 858         /* current->state = TASK_INTERRUPTIBLE; */
 859                 /* And perhaps we should remove the while() { ... } */
 860         while (current->timeout) {
 861             interruptible_sleep_on(&stuff->sleepq);
 862                         TRACE((SLEEP, "delay: to is %d\n", current->timeout));
 863         }
 864     }
 865 }
 866 
 867 static void 
 868 mcdx_intr(int irq, void *dev_id, struct pt_regs* regs)
     /* [previous][next][first][last][top][bottom][index][help] */
 869 {
 870     struct s_drive_stuff *stuffp;
 871         unsigned char b;
 872 
 873     stuffp = mcdx_irq_map[irq];
 874 
 875     if (stuffp == NULL ) {
 876                 WARN(("mcdx: no device for intr %d\n", irq));
 877                 return;
 878     }
 879 
 880         /* get the interrupt status */
 881         b = inb((unsigned int) stuffp->rreg_status);
 882         stuffp->introk = ~b & MCDX_RBIT_DTEN;
 883 
 884         /* NOTE: We only should get interrupts if data were requested.
 885            But the drive seems to generate ``asynchronous'' interrupts
 886            on several error conditions too.  (Despite the err int enable
 887            setting during initialisation) */
 888 
 889         /* if not ok, read the next byte as the drives status */
 890         if (!stuffp->introk) {
 891                 TRACE((IRQ, "intr() irq %d hw status 0x%02x\n", irq, b));
 892                 if (~b & MCDX_RBIT_STEN) {
 893                         INFO((  "intr() irq %d    status 0x%02x\n", 
 894                                         irq, inb((unsigned int) stuffp->rreg_data)));
 895                 } else {
 896                         INFO((  "intr() irq %d ambigous hw status\n", irq));
 897                 }
 898         } else {
 899                 TRACE((IRQ, "irq() irq %d ok, status %02x\n", irq, b));
 900     }
 901 
 902     stuffp->busy = 0;
 903     wake_up_interruptible(&stuffp->busyq);
 904 }
 905 
 906 
 907 static int 
 908 mcdx_talk (
     /* [previous][next][first][last][top][bottom][index][help] */
 909                 struct s_drive_stuff *stuffp, 
 910                 const unsigned char *cmd, size_t cmdlen,
 911                 void *buffer, size_t size, 
 912                 unsigned int timeout, int tries)
 913 /* Send a command to the drive, wait for the result.
 914  * returns -1 on timeout, drive status otherwise
 915  * If buffer is not zero, the result (length size) is stored there.
 916  * If buffer is zero the size should be the number of bytes to read
 917  * from the drive.  These bytes are discarded.
 918  */
 919 {
 920         int st;
 921     char c;
 922     int disgard;
 923 
 924     if ((disgard = (buffer == NULL))) buffer = &c;
 925 
 926     while (stuffp->lock) {
 927                 interruptible_sleep_on(&stuffp->lockq); 
 928                 TRACE((SLEEP, "talk: lock = %d\n",
 929                                 stuffp->lock));
 930         }
 931 
 932     stuffp->lock = 1;
 933     stuffp->valid = 0;  
 934 
 935 #if MCDX_DEBUG & TALK
 936         { 
 937                 unsigned char i;
 938                 TRACE((TALK, "talk() %d / %d tries, res.size %d, command 0x%02x", 
 939                                 tries, timeout, size, (unsigned char) cmd[0]));
 940                 for (i = 1; i < cmdlen; i++) printk(" 0x%02x", cmd[i]);
 941                 printk("\n");
 942         }
 943 #endif
 944 
 945     /*  give up if all tries are done (bad) or if the status
 946      *  st != -1 (good) */
 947         for (st = -1; st == -1 && tries; tries--) {
 948 
 949                 char *bp = (char*) buffer;
 950                 size_t sz = size;
 951 
 952                 outsb((unsigned int) stuffp->wreg_data, cmd, cmdlen);
 953         TRACE((TALK, "talk() command sent\n"));
 954 
 955         /* get the status byte */
 956         if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) {
 957             INFO(("talk() %02x timed out (status), %d tr%s left\n", 
 958                     cmd[0], tries - 1, tries == 2 ? "y" : "ies"));
 959                 continue; 
 960         }
 961         st = *bp;
 962         sz--;
 963         if (!disgard) bp++;
 964 
 965         TRACE((TALK, "talk() got status 0x%02x\n", st));
 966 
 967         /* command error? */
 968         if (e_cmderr(st)) {
 969             WARN(("command error cmd = %02x %s \n", 
 970                     cmd[0], cmdlen > 1 ? "..." : ""));
 971             st = -1;
 972             continue;
 973                 }
 974 
 975         /* audio status? */
 976         if (stuffp->audiostatus == CDROM_AUDIO_INVALID)
 977             stuffp->audiostatus = 
 978                     e_audiobusy(st) ? CDROM_AUDIO_PLAY : CDROM_AUDIO_NO_STATUS;
 979         else if (stuffp->audiostatus == CDROM_AUDIO_PLAY 
 980                 && e_audiobusy(st) == 0)
 981             stuffp->audiostatus = CDROM_AUDIO_COMPLETED;
 982 
 983         /* media change? */
 984         if (e_changed(st)) {
 985             INFO(("talk() media changed\n"));
 986             stuffp->xxx = stuffp->yyy = 1;
 987         }
 988 
 989         /* now actually get the data */
 990         while (sz--) {
 991             if (-1 == mcdx_getval(stuffp, timeout, -1, bp)) {
 992                 INFO(("talk() %02x timed out (data), %d tr%s left\n", 
 993                         cmd[0], tries - 1, tries == 2 ? "y" : "ies"));
 994                 st = -1; break;
 995             }
 996             if (!disgard) bp++;
 997             TRACE((TALK, "talk() got 0x%02x\n", *(bp - 1)));
 998         }
 999     }
1000 
1001 #if !MCDX_QUIET
1002     if (!tries && st == -1) INFO(("talk() giving up\n"));
1003 #endif
1004 
1005     stuffp->lock = 0;
1006     wake_up_interruptible(&stuffp->lockq);
1007 
1008         TRACE((TALK, "talk() done with 0x%02x\n", st));
1009     return st;
1010 }
1011 
1012 /* MODULE STUFF ***********************************************************/
1013 #ifdef MODULE
1014 
1015 int init_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1016 {
1017         int i;
1018         int drives = 0;
1019 
1020         mcdx_init();
1021         for (i = 0; i < MCDX_NDRIVES; i++)  {
1022                 if (mcdx_stuffp[i]) {
1023                 TRACE((INIT, "init_module() drive %d stuff @ %p\n",
1024                                 i, mcdx_stuffp[i]));
1025                         drives++;
1026                 }
1027         }
1028 
1029     if (!drives) 
1030                 return -EIO;
1031 
1032     register_symtab(0);
1033     return 0;
1034 }
1035 
1036 void cleanup_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1037 {
1038     int i;
1039 
1040         INFO(("cleanup_module called\n"));
1041         
1042     for (i = 0; i < MCDX_NDRIVES; i++) {
1043                 struct s_drive_stuff *stuffp;
1044                 stuffp = mcdx_stuffp[i];
1045                 if (!stuffp) continue;
1046                 release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE);
1047                 free_irq(stuffp->irq, NULL);
1048                 if (stuffp->toc) {
1049                         TRACE((MALLOC, "cleanup_module() free toc @ %p\n", stuffp->toc));
1050                         kfree(stuffp->toc);
1051                 }
1052                 TRACE((MALLOC, "cleanup_module() free stuffp @ %p\n", stuffp));
1053                 mcdx_stuffp[i] = NULL;
1054                 kfree(stuffp);
1055     }
1056 
1057     if (unregister_blkdev(MAJOR_NR, DEVICE_NAME) != 0) {
1058         WARN(("cleanup() unregister_blkdev() failed\n"));
1059     } 
1060 #if !MCDX_QUIET
1061         else INFO(("cleanup() succeeded\n"));
1062 #endif
1063 }
1064 
1065 #endif MODULE
1066 
1067 /* Support functions ************************************************/
1068 
1069 #if MCDX_DEBUG
1070 void trace(int level, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
1071 {
1072         char s[255];
1073         va_list args;
1074         if (level < 1) return;
1075         va_start(args, fmt);
1076         if (sizeof(s) < vsprintf(s, fmt, args))
1077                 printk(MCDX ":: dprintf exeeds limit!!\n");
1078         else printk(MCDX ":: %s", s);
1079         va_end(args);
1080 }
1081 #endif
1082 
1083 void warn(const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
1084 {
1085         char s[255];
1086         va_list args;
1087         va_start(args, fmt);
1088         if (sizeof(s) < vsprintf(s, fmt, args))
1089                 printk(MCDX ":: dprintf exeeds limit!!\n");
1090         else printk(MCDX ": %s", s);
1091         va_end(args);
1092 }
1093 
1094 int mcdx_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1095 {
1096         int drive;
1097 
1098         WARN(("Version 1.7 for %s\n", kernel_version));
1099         WARN(("mcdx.c,v 1.31 1996/03/02 00:21:18 heiko Exp\n"));
1100 
1101         /* zero the pointer array */
1102         for (drive = 0; drive < MCDX_NDRIVES; drive++)
1103                 mcdx_stuffp[drive] = NULL;
1104 
1105         /* do the initialisation */
1106         for (drive = 0; drive < MCDX_NDRIVES; drive++) { 
1107                 struct s_version version;
1108                 struct s_drive_stuff* stuffp;
1109         int size;
1110 
1111                 mcdx_blocksizes[drive] = 0;
1112 
1113         size = sizeof(*stuffp);
1114                 
1115                 TRACE((INIT, "init() try drive %d\n", drive));
1116 
1117         TRACE((INIT, "kmalloc space for stuffpt's\n"));
1118                 TRACE((MALLOC, "init() malloc %d bytes\n", size));
1119                 if (!(stuffp = kmalloc(size, GFP_KERNEL))) {
1120                         WARN(("init() malloc failed\n"));
1121                         break; 
1122                 }
1123 
1124                 TRACE((INIT, "init() got %d bytes for drive stuff @ %p\n", sizeof(*stuffp), stuffp));
1125 
1126                 /* set default values */
1127                 memset(stuffp, 0, sizeof(*stuffp));
1128         stuffp->autoclose = 1;      /* close the door on open(2) */
1129 
1130                 stuffp->present = 0;            /* this should be 0 already */
1131                 stuffp->toc = NULL;                     /* this should be NULL already */
1132 
1133                 /* setup our irq and i/o addresses */
1134                 stuffp->irq = irq(mcdx_drive_map[drive]);
1135                 stuffp->wreg_data = stuffp->rreg_data = port(mcdx_drive_map[drive]);
1136                 stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1;
1137                 stuffp->wreg_hcon = stuffp->wreg_reset + 1;
1138                 stuffp->wreg_chn = stuffp->wreg_hcon + 1;
1139 
1140                 /* check if i/o addresses are available */
1141                 if (0 != check_region((unsigned int) stuffp->wreg_data, MCDX_IO_SIZE)) {
1142             WARN(("0x%3p,%d: "
1143                     "Init failed. I/O ports (0x%3p..0x%3p) already in use.\n",
1144                     stuffp->wreg_data, stuffp->irq,
1145                     stuffp->wreg_data, 
1146                     stuffp->wreg_data + MCDX_IO_SIZE - 1));
1147                         TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp));
1148             kfree(stuffp);
1149                         TRACE((INIT, "init() continue at next drive\n"));
1150                         continue; /* next drive */
1151                 }
1152 
1153                 TRACE((INIT, "init() i/o port is available at 0x%3p\n", stuffp->wreg_data));
1154 
1155                 TRACE((INIT, "init() hardware reset\n"));
1156                 mcdx_reset(stuffp, HARD, 1);
1157 
1158                 TRACE((INIT, "init() get version\n"));
1159                 if (-1 == mcdx_requestversion(stuffp, &version, 4)) {
1160                         /* failed, next drive */
1161             WARN(("%s=0x%3p,%d: Init failed. Can't get version.\n",
1162                     MCDX,
1163                     stuffp->wreg_data, stuffp->irq));
1164                         TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp));
1165             kfree(stuffp);
1166                         TRACE((INIT, "init() continue at next drive\n"));
1167                         continue;
1168                 }
1169 
1170                 switch (version.code) {
1171                 case 'D': 
1172                 stuffp->readcmd = READ2X; 
1173                 stuffp->present = DOUBLE | DOOR | MULTI; 
1174                 break;
1175                 case 'F': 
1176                 stuffp->readcmd = READ1X; 
1177                 stuffp->present = SINGLE | DOOR | MULTI;
1178                 break;
1179                 case 'M': 
1180                 stuffp->readcmd = READ1X;
1181                 stuffp->present = SINGLE;
1182                 break;
1183                 default: 
1184                 stuffp->present = 0; break;
1185                 }
1186 
1187         stuffp->playcmd = READ1X;
1188 
1189 
1190                 if (!stuffp->present) {
1191             WARN(("%s=0x%3p,%d: Init failed. No Mitsumi CD-ROM?.\n",
1192                     MCDX, stuffp->wreg_data, stuffp->irq));
1193                         kfree(stuffp);
1194                         continue; /* next drive */
1195                 }
1196 
1197                 TRACE((INIT, "init() register blkdev\n"));
1198                 if (register_blkdev(MAJOR_NR, DEVICE_NAME, &mcdx_fops) != 0) {
1199             WARN(("%s=0x%3p,%d: Init failed. Can't get major %d.\n",
1200                     MCDX,
1201                     stuffp->wreg_data, stuffp->irq, MAJOR_NR));
1202                         kfree(stuffp);
1203                         continue; /* next drive */
1204                 }
1205 
1206                 blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
1207                 read_ahead[MAJOR_NR] = READ_AHEAD;
1208 
1209                 blksize_size[MAJOR_NR] = mcdx_blocksizes;
1210 
1211                 TRACE((INIT, "init() subscribe irq and i/o\n"));
1212                 mcdx_irq_map[stuffp->irq] = stuffp;
1213                 if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, DEVICE_NAME, NULL)) {
1214             WARN(("%s=0x%3p,%d: Init failed. Can't get irq (%d).\n",
1215                     MCDX,
1216                     stuffp->wreg_data, stuffp->irq, stuffp->irq));
1217                         stuffp->irq = 0;
1218                         kfree(stuffp);
1219                         continue;
1220                 }
1221                 request_region((unsigned int) stuffp->wreg_data, 
1222                 MCDX_IO_SIZE, 
1223                 DEVICE_NAME); 
1224 
1225                 TRACE((INIT, "init() get garbage\n"));
1226                 {
1227                         int i;
1228                         mcdx_delay(stuffp, HZ/2);
1229                         for (i = 100; i; i--) (void) inb((unsigned int) stuffp->rreg_status);
1230                 }
1231 
1232 
1233 #if WE_KNOW_WHY
1234                         outb(0x50, (unsigned int) stuffp->wreg_chn);    /* irq 11 -> channel register */
1235 #endif
1236 
1237                 TRACE((INIT, "init() set non dma but irq mode\n"));
1238                 mcdx_config(stuffp, 1);
1239 
1240                 stuffp->minor = drive;
1241 
1242                 WARN((DEVICE_NAME " installed at 0x%3p, irq %d."
1243                            " (Firmware version %c %x)\n",
1244                            stuffp->wreg_data, stuffp->irq, version.code,
1245                version.ver));
1246                 mcdx_stuffp[drive] = stuffp;
1247                 TRACE((INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp));
1248         }
1249 
1250         return 0;
1251 }
1252 
1253 static int 
1254 mcdx_transfer(struct s_drive_stuff *stuffp,
     /* [previous][next][first][last][top][bottom][index][help] */
1255                 char *p, int sector, int nr_sectors)
1256 /*      This seems to do the actually transfer.  But it does more.  It
1257         keeps track of errors ocurred and will (if possible) fall back
1258         to single speed on error. 
1259         Return: -1 on timeout or other error
1260                         else status byte (as in stuff->st) */
1261 {
1262         int ans;
1263 
1264         ans = mcdx_xfer(stuffp, p, sector, nr_sectors);
1265         return ans;
1266 #if FALLBACK
1267         if (-1 == ans) stuffp->readerrs++;
1268         else return ans;
1269 
1270         if (stuffp->readerrs && stuffp->readcmd == READ1X) {
1271                 WARN(("XXX Alrady reading 1x -- no chance\n"));
1272                 return -1;
1273         }
1274 
1275         WARN(("XXX Fallback to 1x\n"));
1276 
1277         stuffp->readcmd = READ1X;
1278         return mcdx_transfer(stuffp, p, sector, nr_sectors);
1279 #endif
1280         
1281 }
1282 
1283 
1284 static int mcdx_xfer(struct s_drive_stuff *stuffp,
     /* [previous][next][first][last][top][bottom][index][help] */
1285                 char *p, int sector, int nr_sectors)
1286 /*      This does actually the transfer from the drive.
1287         Return: -1 on timeout or other error
1288                         else status byte (as in stuff->st) */
1289 {
1290     int off;
1291     int done = 0;
1292 
1293         TRACE((TRANSFER, "transfer() %d sectors at sector %d\n",
1294                         nr_sectors, sector));
1295 
1296         if (stuffp->audio) {
1297                 WARN(("attempt to read from audio cd\n"));
1298                 return -1;
1299         }
1300 
1301     while (stuffp->lock) {
1302                 interruptible_sleep_on(&stuffp->lockq);
1303                 TRACE((SLEEP, "xfer: lock = %d\n",
1304                         stuffp->lock));
1305         }
1306 
1307     if (stuffp->valid
1308                         && (sector >= stuffp->pending)
1309                         && (sector < stuffp->off_direct)) {
1310 
1311 
1312         off = stuffp->off_requested < (off = sector + nr_sectors)
1313                         ? stuffp->off_requested : off;
1314 
1315         stuffp->lock = current->pid;
1316 
1317         do {
1318                 /* wait for the drive become idle, but first
1319                    check for possible occured errors --- the drive
1320                    seems to report them asynchronously */
1321 
1322             current->timeout = jiffies + 5 * HZ;
1323             while (stuffp->introk && stuffp->busy && current->timeout) {
1324                         interruptible_sleep_on(&stuffp->busyq);
1325                         TRACE((SLEEP, "request: busy = %d, timeout = %d\n",
1326                                 stuffp->busy, current->timeout));
1327                 }
1328 
1329                 /* test for possible errors */
1330                 if (current->timeout == 0 || !stuffp->introk) {
1331                         if (current->timeout == 0)  {
1332                                 WARN(("mcdx_transfer() timeout\n"));
1333                         } else if (!stuffp->introk) { 
1334                                 WARN(("mcdx_transfer() error via irq reported\n"));
1335                         } else {
1336                                 WARN(("mcdx_transfer() unknown failure in data request\n"));
1337                         }
1338 
1339                         stuffp->lock = 0;
1340                         stuffp->busy = 0;
1341                         stuffp->valid = 0;
1342 
1343                         wake_up_interruptible(&stuffp->lockq);
1344                         TRACE((TRANSFER, "transfer() done (-1)\n"));
1345                         return -1;
1346             }
1347 
1348                 /* test if it's the first sector of a block,
1349                  * there we have to skip some bytes as we read raw data */
1350                 if (stuffp->xa && (0 == (stuffp->pending & 3))) {
1351                         const int HEAD = CD_FRAMESIZE_RAW - CD_XA_TAIL - CD_FRAMESIZE;
1352                         TRACE((TRANSFER, "transfer() sector %d, skip %d header bytes\n",
1353                                 stuffp->pending, HEAD));
1354                         insb((unsigned int) stuffp->rreg_data, p, HEAD);
1355                 }
1356 
1357                 /* now actually read the data */
1358 
1359                 TRACE((TRANSFER, "transfer() read sector %d\n", stuffp->pending));
1360             insb((unsigned int) stuffp->rreg_data, p, 512); 
1361 
1362                 /* test if it's the last sector of a block,
1363                  * if so, we have to expect an interrupt and to skip some
1364                  * data too */
1365                 if ((stuffp->busy = (3 == (stuffp->pending & 3))) && stuffp->xa) {
1366                         char dummy[CD_XA_TAIL];
1367                         TRACE((TRANSFER, "transfer() sector %d, skip %d trailer bytes\n",
1368                                         stuffp->pending, CD_XA_TAIL));
1369                         insb((unsigned int) stuffp->rreg_data, &dummy[0], CD_XA_TAIL);
1370                 }
1371 
1372             if (stuffp->pending == sector) {
1373                         p += 512;
1374                         done++;
1375                         sector++;
1376             }
1377         } while (++(stuffp->pending) < off);
1378 
1379         stuffp->lock = 0;
1380         wake_up_interruptible(&stuffp->lockq);
1381 
1382     } else {
1383 
1384                 static unsigned char cmd[] = {
1385                         0,
1386                         0, 0, 0,
1387                         0, 0, 0
1388                 };
1389 
1390                 cmd[0] = stuffp->readcmd;
1391 
1392                 stuffp->valid = 1;
1393                 stuffp->pending = sector & ~3;
1394 
1395                 /* do some sanity checks */
1396                 TRACE((TRANSFER, "transfer() request sector %d\n", stuffp->pending));
1397                 if (stuffp->pending > stuffp->lastsector) {
1398                         WARN(("transfer() sector %d from nirvana requested.\n",
1399                                 stuffp->pending));
1400                         stuffp->status = MCDX_ST_EOM;
1401                         stuffp->valid = 0;
1402                         TRACE((TRANSFER, "transfer() done (-1)\n"));
1403                         return -1;
1404                 }
1405 
1406                 if ((stuffp->off_direct = stuffp->pending + DIRECT_SIZE)
1407                         > stuffp->lastsector + 1)
1408                         stuffp->off_direct = stuffp->lastsector + 1;
1409                 if ((stuffp->off_requested = stuffp->pending + REQUEST_SIZE)
1410                         > stuffp->lastsector + 1)
1411                         stuffp->off_requested = stuffp->lastsector + 1;
1412 
1413                 TRACE((TRANSFER, "transfer() pending %d\n", stuffp->pending));
1414                 TRACE((TRANSFER, "transfer() off_dir %d\n", stuffp->off_direct));
1415                 TRACE((TRANSFER, "transfer() off_req %d\n", stuffp->off_requested));
1416 
1417                 {
1418                         struct s_msf pending;
1419                         log2msf(stuffp->pending / 4, &pending);
1420                         cmd[1] = pending.minute;
1421                         cmd[2] = pending.second;
1422                         cmd[3] = pending.frame;
1423                 }
1424 
1425                 stuffp->busy = 1;
1426                 cmd[6] = (unsigned char) (stuffp->off_requested - stuffp->pending) / 4;
1427 
1428                 outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd);
1429 
1430     }
1431 
1432     stuffp->off_direct = (stuffp->off_direct += done) < stuffp->off_requested
1433             ? stuffp->off_direct : stuffp->off_requested;
1434 
1435         TRACE((TRANSFER, "transfer() done (%d)\n", done));
1436     return done;
1437 }
1438 
1439 
1440 /*      Access to elements of the mcdx_drive_map members */
1441 
1442 static char* port(int *ip) { return (char*) ip[0]; }
     /* [previous][next][first][last][top][bottom][index][help] */
1443 static int irq(int *ip) { return ip[1]; }
     /* [previous][next][first][last][top][bottom][index][help] */
1444 
1445 /*      Misc number converters */
1446 
1447 static unsigned int bcd2uint(unsigned char c)
     /* [previous][next][first][last][top][bottom][index][help] */
1448 { return (c >> 4) * 10 + (c & 0x0f); }
1449 
1450 static unsigned int uint2bcd(unsigned int ival)
     /* [previous][next][first][last][top][bottom][index][help] */
1451 { return ((ival / 10) << 4) | (ival % 10); }
1452 
1453 static void log2msf(unsigned int l, struct s_msf* pmsf)
     /* [previous][next][first][last][top][bottom][index][help] */
1454 {
1455     l += CD_BLOCK_OFFSET;
1456     pmsf->minute = uint2bcd(l / 4500), l %= 4500;
1457     pmsf->second = uint2bcd(l / 75);
1458     pmsf->frame = uint2bcd(l % 75);
1459 }
1460 
1461 static unsigned int msf2log(const struct s_msf* pmsf)
     /* [previous][next][first][last][top][bottom][index][help] */
1462 {
1463     return bcd2uint(pmsf->frame)
1464     + bcd2uint(pmsf->second) * 75
1465     + bcd2uint(pmsf->minute) * 4500
1466     - CD_BLOCK_OFFSET;
1467 }
1468         
1469 int mcdx_readtoc(struct s_drive_stuff* stuffp)
     /* [previous][next][first][last][top][bottom][index][help] */
1470 /*  Read the toc entries from the CD,
1471  *  Return: -1 on failure, else 0 */
1472 {
1473 
1474         if (stuffp->toc) {
1475                 TRACE((READTOC, "ioctl() toc already read\n"));
1476                 return 0;
1477         }
1478 
1479         TRACE((READTOC, "ioctl() readtoc for %d tracks\n",
1480                         stuffp->di.n_last - stuffp->di.n_first + 1));
1481 
1482     if (-1 == mcdx_hold(stuffp, 1)) return -1;
1483 
1484         TRACE((READTOC, "ioctl() tocmode\n"));
1485         if (-1 == mcdx_setdrivemode(stuffp, TOC, 1)) return -EIO;
1486 
1487         /* all seems to be ok so far ... malloc */
1488         {
1489                 int size;
1490                 size = sizeof(struct s_subqcode) * (stuffp->di.n_last - stuffp->di.n_first + 2);
1491 
1492                 TRACE((MALLOC, "ioctl() malloc %d bytes\n", size));
1493                 stuffp->toc = kmalloc(size, GFP_KERNEL);
1494                 if (!stuffp->toc) {
1495                         WARN(("Cannot malloc %s bytes for toc\n", size));
1496                         mcdx_setdrivemode(stuffp, DATA, 1);
1497                         return -EIO;
1498                 }
1499         }
1500 
1501         /* now read actually the index */
1502         {
1503                 int trk;
1504                 int retries;
1505 
1506                 for (trk = 0; 
1507                                 trk < (stuffp->di.n_last - stuffp->di.n_first + 1); 
1508                                 trk++)
1509                         stuffp->toc[trk].index = 0;
1510 
1511                 for (retries = 300; retries; retries--) { /* why 300? */
1512                         struct s_subqcode q;
1513                         unsigned int idx;
1514                 
1515                         if (-1 == mcdx_requestsubqcode(stuffp, &q, 1)) {
1516                                 mcdx_setdrivemode(stuffp, DATA, 1);
1517                                 return -EIO;
1518                         }
1519 
1520                         idx = bcd2uint(q.index);
1521 
1522                         if ((idx > 0) 
1523                                         && (idx <= stuffp->di.n_last) 
1524                                         && (q.tno == 0)
1525                                         && (stuffp->toc[idx - stuffp->di.n_first].index == 0)) {
1526                                 stuffp->toc[idx - stuffp->di.n_first] = q;
1527                                 TRACE((READTOC, "ioctl() toc idx %d (trk %d)\n", idx, trk));
1528                                 trk--;
1529                         }
1530                         if (trk == 0) break;
1531                 }
1532                 memset(&stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1], 
1533                                 0, sizeof(stuffp->toc[0]));
1534                 stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1].dt
1535                                 = stuffp->di.msf_leadout;
1536         }
1537 
1538         /* unset toc mode */
1539         TRACE((READTOC, "ioctl() undo toc mode\n"));
1540         if (-1 == mcdx_setdrivemode(stuffp, DATA, 2))
1541                 return -EIO;
1542 
1543 #if MCDX_DEBUG && READTOC
1544         { int trk;
1545         for (trk = 0; 
1546                         trk < (stuffp->di.n_last - stuffp->di.n_first + 2); 
1547                         trk++)
1548                 TRACE((READTOC, "ioctl() %d readtoc %02x %02x %02x"
1549                                 "  %02x:%02x.%02x  %02x:%02x.%02x\n",
1550                                 trk + stuffp->di.n_first,
1551                                 stuffp->toc[trk].control, stuffp->toc[trk].tno, stuffp->toc[trk].index,
1552                                 stuffp->toc[trk].tt.minute, stuffp->toc[trk].tt.second, stuffp->toc[trk].tt.frame,
1553                                 stuffp->toc[trk].dt.minute, stuffp->toc[trk].dt.second, stuffp->toc[trk].dt.frame));
1554         }
1555 #endif
1556 
1557         return 0;
1558 }
1559 
1560 static int
1561 mcdx_playmsf(struct s_drive_stuff* stuffp, const struct cdrom_msf* msf)
     /* [previous][next][first][last][top][bottom][index][help] */
1562 {
1563     unsigned char cmd[7] = {
1564         0, 0, 0, 0, 0, 0, 0
1565     };
1566 
1567     cmd[0] = stuffp->playcmd;
1568     
1569     cmd[1] = msf->cdmsf_min0;
1570     cmd[2] = msf->cdmsf_sec0;
1571     cmd[3] = msf->cdmsf_frame0;
1572     cmd[4] = msf->cdmsf_min1;
1573     cmd[5] = msf->cdmsf_sec1;
1574     cmd[6] = msf->cdmsf_frame1;
1575 
1576     TRACE((PLAYMSF, "ioctl(): play %x "
1577             "%02x:%02x:%02x -- %02x:%02x:%02x\n",
1578             cmd[0], cmd[1], cmd[2], cmd[3],
1579             cmd[4], cmd[5], cmd[6])); 
1580 
1581     outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd);
1582 
1583     if (-1 == mcdx_getval(stuffp, 3 * HZ, 0, NULL)) {
1584         WARN(("playmsf() timeout\n")); 
1585         return -1;
1586     }
1587 
1588     stuffp->audiostatus = CDROM_AUDIO_PLAY;
1589     return 0;
1590 }
1591 
1592 static int 
1593 mcdx_playtrk(struct s_drive_stuff* stuffp, const struct cdrom_ti* ti)
     /* [previous][next][first][last][top][bottom][index][help] */
1594 {
1595     struct s_subqcode* p;
1596     struct cdrom_msf msf;
1597 
1598     if (-1 == mcdx_readtoc(stuffp)) return -1;
1599 
1600     if (ti) p = &stuffp->toc[ti->cdti_trk0 - stuffp->di.n_first];
1601     else p = &stuffp->start;
1602 
1603     msf.cdmsf_min0 = p->dt.minute;
1604     msf.cdmsf_sec0 = p->dt.second;
1605     msf.cdmsf_frame0 = p->dt.frame;
1606 
1607     if (ti) {
1608         p = &stuffp->toc[ti->cdti_trk1 - stuffp->di.n_first + 1];
1609         stuffp->stop = *p;
1610     } else p = &stuffp->stop;
1611 
1612     msf.cdmsf_min1 = p->dt.minute;
1613     msf.cdmsf_sec1 = p->dt.second;
1614     msf.cdmsf_frame1 = p->dt.frame;
1615 
1616     return mcdx_playmsf(stuffp, &msf);
1617 }
1618 
1619 
1620 /* Drive functions ************************************************/
1621 
1622 static int 
1623 mcdx_closedoor(struct s_drive_stuff *stuffp, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1624 {
1625         if (stuffp->present & DOOR)
1626                 return mcdx_talk(stuffp, "\xf8", 1, NULL, 1, 5 * HZ, tries);
1627         else
1628                 return 0;
1629 }
1630 
1631 static int 
1632 mcdx_stop(struct s_drive_stuff *stuffp, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1633 { return mcdx_talk(stuffp, "\xf0", 1, NULL, 1, 2 * HZ, tries); }
1634 
1635 static int
1636 mcdx_hold(struct s_drive_stuff *stuffp, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1637 { return mcdx_talk(stuffp, "\x70", 1, NULL, 1, 2 * HZ, tries); }
1638 
1639 static int
1640 mcdx_eject(struct s_drive_stuff *stuffp, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1641 {
1642         if (stuffp->present & DOOR) {
1643         stuffp->ejected = jiffies;
1644                 return mcdx_talk(stuffp, "\xf6", 1, NULL, 1, 5 * HZ, tries);
1645     } else return 0;
1646 }
1647 
1648 static int
1649 mcdx_requestsubqcode(struct s_drive_stuff *stuffp, 
     /* [previous][next][first][last][top][bottom][index][help] */
1650         struct s_subqcode *sub, 
1651         int tries)
1652 {
1653         char buf[11];
1654         int ans;
1655 
1656         if (-1 == (ans = mcdx_talk(
1657             stuffp, "\x20", 1, buf, sizeof(buf),
1658             2 * HZ, tries))) 
1659         return -1;
1660         sub->control = buf[1];
1661         sub->tno = buf[2];
1662         sub->index = buf[3];
1663         sub->tt.minute = buf[4];
1664         sub->tt.second = buf[5];
1665         sub->tt.frame = buf[6];
1666         sub->dt.minute = buf[8];
1667         sub->dt.second = buf[9];
1668         sub->dt.frame = buf[10];
1669 
1670         return ans;
1671 }
1672 
1673 static int
1674 mcdx_requestmultidiskinfo(struct s_drive_stuff *stuffp, struct s_multi *multi, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1675 {
1676         char buf[5];
1677         int ans;
1678 
1679     if (stuffp->present & MULTI) {
1680                 ans = mcdx_talk(stuffp, "\x11", 1, buf, sizeof(buf), 1 * HZ, tries);
1681                 multi->multi = buf[1];
1682         multi->msf_last.minute = buf[2];
1683         multi->msf_last.second = buf[3];
1684         multi->msf_last.frame = buf[4];
1685         return ans;
1686     } else {
1687         multi->multi = 0;
1688         return 0;
1689     }
1690 }
1691 
1692 static int 
1693 mcdx_requesttocdata(struct s_drive_stuff *stuffp, struct s_diskinfo *info, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1694 {
1695         char buf[9];
1696         int ans;
1697         ans = mcdx_talk(stuffp, "\x10", 1, buf, sizeof(buf), 1 * HZ, tries);
1698         if (ans == -1) {
1699                 info->n_first = 0;
1700                 info->n_last = 0;
1701         } else {
1702                 info->n_first = bcd2uint(buf[1]);
1703                 info->n_last = bcd2uint(buf[2]);
1704                 info->msf_leadout.minute = buf[3];
1705                 info->msf_leadout.second = buf[4];
1706                 info->msf_leadout.frame = buf[5];
1707                 info->msf_first.minute = buf[6];
1708                 info->msf_first.second = buf[7];
1709                 info->msf_first.frame = buf[8];
1710         }
1711         return ans;
1712 }
1713 
1714 static int
1715 mcdx_setdrivemode(struct s_drive_stuff *stuffp, enum drivemodes mode, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1716 {
1717         char cmd[2];
1718         int ans;
1719 
1720         TRACE((HW, "setdrivemode() %d\n", mode));
1721 
1722         if (-1 == (ans = mcdx_talk(stuffp, "\xc2", 1, cmd, sizeof(cmd), 5 * HZ, tries)))
1723                 return -1;
1724 
1725         switch (mode) {
1726           case TOC: cmd[1] |= 0x04; break;
1727           case DATA: cmd[1] &= ~0x04; break;
1728           case RAW: cmd[1] |= 0x40; break;
1729           case COOKED: cmd[1] &= ~0x40; break;
1730           default: break;
1731         }
1732         cmd[0] = 0x50;
1733         return mcdx_talk(stuffp, cmd, 2, NULL, 1, 5 * HZ, tries);
1734 }
1735 
1736 static int
1737 mcdx_setdatamode(struct s_drive_stuff *stuffp, enum datamodes mode, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1738 {
1739         unsigned char cmd[2] = { 0xa0 };
1740         TRACE((HW, "setdatamode() %d\n", mode));
1741         switch (mode) {
1742           case MODE0: cmd[1] = 0x00; break;
1743           case MODE1: cmd[1] = 0x01; break;
1744           case MODE2: cmd[1] = 0x02; break;
1745           default: return -EINVAL;
1746         }
1747         return mcdx_talk(stuffp, cmd, 2, NULL, 1, 5 * HZ, tries);
1748 }
1749 
1750 static int
1751 mcdx_config(struct s_drive_stuff *stuffp, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1752 {
1753         char cmd[4];
1754 
1755         TRACE((HW, "config()\n"));
1756 
1757         cmd[0] = 0x90;
1758 
1759         cmd[1] = 0x10;          /* irq enable */
1760         cmd[2] = 0x05;          /* pre, err irq enable */
1761 
1762         if (-1 == mcdx_talk(stuffp, cmd, 3, NULL, 1, 1 * HZ, tries))
1763                 return -1;
1764 
1765         cmd[1] = 0x02;          /* dma select */
1766         cmd[2] = 0x00;          /* no dma */
1767 
1768         return mcdx_talk(stuffp, cmd, 3, NULL, 1, 1 * HZ, tries);
1769 }
1770 
1771 static int
1772 mcdx_requestversion(struct s_drive_stuff *stuffp, struct s_version *ver, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1773 {
1774         char buf[3];
1775         int ans;
1776 
1777         if (-1 == (ans = mcdx_talk(stuffp, "\xdc", 
1778                         1, buf, sizeof(buf), 1 * HZ, tries)))
1779                 return ans;
1780 
1781         ver->code = buf[1];
1782         ver->ver = buf[2];
1783 
1784         return ans;
1785 }
1786 
1787 static int
1788 mcdx_reset(struct s_drive_stuff *stuffp, enum resetmodes mode, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1789 { 
1790         if (mode == HARD) {
1791                 outb(0, (unsigned int) stuffp->wreg_chn);               /* no dma, no irq -> hardware */
1792                 outb(0, (unsigned int) stuffp->wreg_reset);             /* hw reset */
1793                 return 0;
1794         } else return mcdx_talk(stuffp, "\x60", 1, NULL, 1, 5 * HZ, tries);
1795 }
1796 
1797 static int
1798 mcdx_lockdoor(struct s_drive_stuff *stuffp, int lock, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1799 {
1800         char cmd[2] = { 0xfe };
1801     if (stuffp->present & DOOR) {
1802         cmd[1] = lock ? 0x01 : 0x00;
1803         return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 1, 5 * HZ, tries);
1804     } else return 0;
1805 }
1806 
1807 static int
1808 mcdx_getstatus(struct s_drive_stuff *stuffp, int tries)
     /* [previous][next][first][last][top][bottom][index][help] */
1809 { return mcdx_talk(stuffp, "\x40", 1, NULL, 1, 5 * HZ, tries); }
1810 
1811 static int
1812 mcdx_getval(struct s_drive_stuff *stuffp, int to, int delay, char* buf)
     /* [previous][next][first][last][top][bottom][index][help] */
1813 {
1814     unsigned long timeout = to + jiffies;
1815     char c;
1816 
1817     if (!buf) buf = &c;
1818 
1819     while (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_STEN) {
1820         if (jiffies > timeout) return -1;
1821         mcdx_delay(stuffp, delay);
1822     }
1823 
1824     *buf = (unsigned char) inb((unsigned int) stuffp->rreg_data) & 0xff;
1825 
1826     return 0;
1827 }
1828 
1829 static int
1830 mcdx_setattentuator(
     /* [previous][next][first][last][top][bottom][index][help] */
1831         struct s_drive_stuff* stuffp, 
1832         struct cdrom_volctrl* vol, 
1833         int tries)
1834 {
1835     char cmd[5];
1836     cmd[0] = 0xae;
1837     cmd[1] = vol->channel0;
1838     cmd[2] = 0;
1839     cmd[3] = vol->channel1;
1840     cmd[4] = 0;
1841 
1842     return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 5, 200, tries);
1843 }
1844 
1845 /* ex:set ts=4 sw=4: */

/* [previous][next][first][last][top][bottom][index][help] */