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

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