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. port
  17. irq
  18. bcd2uint
  19. uint2bcd
  20. log2msf
  21. msf2log
  22. mcdx_readtoc
  23. mcdx_playmsf
  24. mcdx_playtrk
  25. mcdx_closedoor
  26. mcdx_stop
  27. mcdx_hold
  28. mcdx_eject
  29. mcdx_requestsubqcode
  30. mcdx_requestmultidiskinfo
  31. mcdx_requesttocdata
  32. mcdx_setdrivemode
  33. mcdx_setdatamode
  34. mcdx_config
  35. mcdx_requestversion
  36. mcdx_reset
  37. mcdx_lockdoor
  38. mcdx_getstatus
  39. mcdx_getval
  40. mcdx_setattentuator

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

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