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

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