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

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