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_closedoor
  24. mcdx_stop
  25. mcdx_eject
  26. mcdx_requestsubqcode
  27. mcdx_requestmultidiskinfo
  28. mcdx_requesttocdata
  29. mcdx_setdrivemode
  30. mcdx_setdatamode
  31. mcdx_config
  32. mcdx_requestversion
  33. mcdx_reset
  34. mcdx_lockdoor
  35. mcdx_getstatus

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

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