root/drivers/block/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

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

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