static void __exit mcdx_exit(void) { int i; xinfo("cleanup_module called\n"); for (i = 0; i < MCDX_NDRIVES; i++) { struct s_drive_stuff *stuffp = mcdx_stuffp[i]; if (!stuffp) continue; del_gendisk(stuffp->disk); if (unregister_cdrom(&stuffp->info)) { printk(KERN_WARNING "Can't unregister cdrom mcdx\n"); continue; } put_disk(stuffp->disk); release_region(stuffp->wreg_data, MCDX_IO_SIZE); free_irq(stuffp->irq, NULL); if (stuffp->toc) { xtrace(MALLOC, "cleanup_module() free toc @ %p\n", stuffp->toc); kfree(stuffp->toc); } xtrace(MALLOC, "cleanup_module() free stuffp @ %p\n", stuffp); mcdx_stuffp[i] = NULL; kfree(stuffp); } if (unregister_blkdev(MAJOR_NR, "mcdx") != 0) { xwarn("cleanup() unregister_blkdev() failed\n"); } blk_cleanup_queue(mcdx_queue); #if !MCDX_QUIET else
static void __xpath_expression_eval_print_input(const xpath_enode_t *enode, const xpath_result_t *left, const xpath_result_t *right) { char *leftval = NULL, *rightval = NULL; const char *name; char namebuf[256]; if (enode->ops->print) { name = enode->ops->print(enode); } else if (enode->identifier == NULL) { name = enode->ops->name; } else { snprintf(namebuf, sizeof(namebuf), "%s %s", enode->ops->name, enode->identifier); name = namebuf; } if (left) leftval = __xpath_node_array_print_short(left); if (right) rightval = __xpath_node_array_print_short(right); if (leftval == NULL) xtrace(" EVAL %s []", name); else if (rightval == NULL) xtrace(" EVAL %s %s", name, leftval); else xtrace(" EVAL %s %s %s", name, leftval, rightval); ni_string_free(&leftval); ni_string_free(&rightval); }
void __exit mcdx_exit(void) { int i; xinfo("cleanup_module called\n"); for (i = 0; i < MCDX_NDRIVES; i++) { struct s_drive_stuff *stuffp; if (unregister_cdrom(&mcdx_info)) { printk(KERN_WARNING "Can't unregister cdrom mcdx\n"); return; } stuffp = mcdx_stuffp[i]; if (!stuffp) continue; release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE); free_irq(stuffp->irq, NULL); if (stuffp->toc) { xtrace(MALLOC, "cleanup_module() free toc @ %p\n", stuffp->toc); kfree(stuffp->toc); } xtrace(MALLOC, "cleanup_module() free stuffp @ %p\n", stuffp); mcdx_stuffp[i] = NULL; kfree(stuffp); } if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0) { xwarn("cleanup() unregister_blkdev() failed\n"); } blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); #if !MCDX_QUIET else xinfo("cleanup() succeeded\n");
static void __xpath_expression_eval_print_output(const xpath_enode_t *enode, const xpath_result_t *result) { char *rval = NULL; if (result == NULL) { xtrace(" ERROR"); } else { rval = __xpath_node_array_print_short(result); xtrace(" => %s", rval); ni_string_free(&rval); } }
static int mcdx_setdrivemode(struct s_drive_stuff *stuffp, enum drivemodes mode, int tries) { char cmd[2]; int ans; xtrace(HW, "setdrivemode() %d\n", mode); if (-1 == (ans = mcdx_talk(stuffp, "\xc2", 1, cmd, sizeof(cmd), 5 * HZ, tries))) return -1; switch (mode) { case TOC: cmd[1] |= 0x04; break; case DATA: cmd[1] &= ~0x04; break; case RAW: cmd[1] |= 0x40; break; case COOKED: cmd[1] &= ~0x40; break; default: break; } cmd[0] = 0x50; return mcdx_talk(stuffp, cmd, 2, NULL, 1, 5 * HZ, tries); }
static int mcdx_playmsf(struct s_drive_stuff *stuffp, const struct cdrom_msf *msf) { unsigned char cmd[7] = { 0, 0, 0, 0, 0, 0, 0 }; if (!stuffp->readcmd) { xinfo("Can't play from missing disk.\n"); return -1; } cmd[0] = stuffp->playcmd; cmd[1] = msf->cdmsf_min0; cmd[2] = msf->cdmsf_sec0; cmd[3] = msf->cdmsf_frame0; cmd[4] = msf->cdmsf_min1; cmd[5] = msf->cdmsf_sec1; cmd[6] = msf->cdmsf_frame1; xtrace(PLAYMSF, "ioctl(): play %x " "%02x:%02x:%02x -- %02x:%02x:%02x\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]); outsb(stuffp->wreg_data, cmd, sizeof cmd); if (-1 == mcdx_getval(stuffp, 3 * HZ, 0, NULL)) { xwarn("playmsf() timeout\n"); return -1; } stuffp->audiostatus = CDROM_AUDIO_PLAY; return 0; }
static void mcdx_delay(struct s_drive_stuff *stuff, long jifs) /* This routine is used for sleeping. * A jifs value <0 means NO sleeping, * =0 means minimal sleeping (let the kernel * run for other processes) * >0 means at least sleep for that amount. * May be we could use a simple count loop w/ jumps to itself, but * I wanna make this independent of cpu speed. [1 jiffy is 1/HZ] sec */ { if (jifs < 0) return; xtrace(SLEEP, "*** delay: sleepq\n"); interruptible_sleep_on_timeout(&stuff->sleepq, jifs); xtrace(SLEEP, "delay awoken\n"); if (signal_pending(current)) { xtrace(SLEEP, "got signal\n"); } }
static void mcdx_intr(int irq, void *dev_id, struct pt_regs* regs) { struct s_drive_stuff *stuffp; unsigned char b; stuffp = mcdx_irq_map[irq]; if (stuffp == NULL ) { xwarn("mcdx: no device for intr %d\n", irq); return; } #ifdef AK2 if ( !stuffp->busy && stuffp->pending ) stuffp->int_err = 1; #endif /* AK2 */ /* get the interrupt status */ b = inb((unsigned int) stuffp->rreg_status); stuffp->introk = ~b & MCDX_RBIT_DTEN; /* NOTE: We only should get interrupts if the data we * requested are ready to transfer. * But the drive seems to generate ``asynchronous'' interrupts * on several error conditions too. (Despite the err int enable * setting during initialisation) */ /* if not ok, read the next byte as the drives status */ if (!stuffp->introk) { xtrace(IRQ, "intr() irq %d hw status 0x%02x\n", irq, b); if (~b & MCDX_RBIT_STEN) { xinfo( "intr() irq %d status 0x%02x\n", irq, inb((unsigned int) stuffp->rreg_data)); } else { xinfo( "intr() irq %d ambiguous hw status\n", irq); } } else { xtrace(IRQ, "irq() irq %d ok, status %02x\n", irq, b); } stuffp->busy = 0; wake_up_interruptible(&stuffp->busyq); }
static void mcdx_close(struct cdrom_device_info *cdi) { struct s_drive_stuff *stuffp; xtrace(OPENCLOSE, "close()\n"); stuffp = cdi->handle; --stuffp->users; }
/* * Free a parsed XPATH expression */ static inline void xpath_expr_free(xpath_enode_t *enode, unsigned int depth, const char *info) { if (!enode) return; xtrace("xpath_expression_free(%*.s%s %s %s)", depth, " ", info, enode->ops ? enode->ops->name : NULL, enode->identifier); xpath_expr_free(enode->left, depth + 2, "left "); xpath_expr_free(enode->right, depth + 2, "right"); xpath_enode_free(enode); }
static void mcdx_close(struct cdrom_device_info * cdi) { struct s_drive_stuff *stuffp; xtrace(OPENCLOSE, "close()\n"); stuffp = mcdx_stuffp[MINOR(cdi->dev)]; --stuffp->users; MOD_DEC_USE_COUNT; }
int __mcdx_init(void) { int i; int drives = 0; mcdx_init(); for (i = 0; i < MCDX_NDRIVES; i++) { if (mcdx_stuffp[i]) { xtrace(INIT, "init_module() drive %d stuff @ %p\n", i, mcdx_stuffp[i]); drives++; } } if (!drives) return -EIO; return 0; }
static int mcdx_config(struct s_drive_stuff *stuffp, int tries) { char cmd[4]; xtrace(HW, "config()\n"); cmd[0] = 0x90; cmd[1] = 0x10; /* irq enable */ cmd[2] = 0x05; /* pre, err irq enable */ if (-1 == mcdx_talk(stuffp, cmd, 3, NULL, 1, 1 * HZ, tries)) return -1; cmd[1] = 0x02; /* dma select */ cmd[2] = 0x00; /* no dma */ return mcdx_talk(stuffp, cmd, 3, NULL, 1, 1 * HZ, tries); }
static int mcdx_setdatamode(struct s_drive_stuff *stuffp, enum datamodes mode, int tries) { unsigned char cmd[2] = { 0xa0 }; xtrace(HW, "setdatamode() %d\n", mode); switch (mode) { case MODE0: cmd[1] = 0x00; break; case MODE1: cmd[1] = 0x01; break; case MODE2: cmd[1] = 0x02; break; default: return -EINVAL; } return mcdx_talk(stuffp, cmd, 2, NULL, 1, 5 * HZ, tries); }
static int mcdx_open(struct cdrom_device_info *cdi, int purpose) { struct s_drive_stuff *stuffp; xtrace(OPENCLOSE, "open()\n"); stuffp = cdi->handle; if (!stuffp->present) return -ENXIO; /* Make the modules looking used ... (thanx bjorn). * But we shouldn't forget to decrement the module counter * on error return */ /* this is only done to test if the drive talks with us */ if (-1 == mcdx_getstatus(stuffp, 1)) return -EIO; if (stuffp->xxx) { xtrace(OPENCLOSE, "open() media changed\n"); stuffp->audiostatus = CDROM_AUDIO_INVALID; stuffp->readcmd = 0; xtrace(OPENCLOSE, "open() Request multisession info\n"); if (-1 == mcdx_requestmultidiskinfo(stuffp, &stuffp->multi, 6)) xinfo("No multidiskinfo\n"); } else { /* multisession ? */ if (!stuffp->multi.multi) stuffp->multi.msf_last.second = 2; xtrace(OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n", stuffp->multi.multi, stuffp->multi.msf_last.minute, stuffp->multi.msf_last.second, stuffp->multi.msf_last.frame); {; } /* got multisession information */ /* request the disks table of contents (aka diskinfo) */ if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) { stuffp->lastsector = -1; } else { stuffp->lastsector = (CD_FRAMESIZE / 512) * msf2log(&stuffp->di.msf_leadout) - 1; xtrace(OPENCLOSE, "open() start %d (%02x:%02x.%02x) %d\n", stuffp->di.n_first, stuffp->di.msf_first.minute, stuffp->di.msf_first.second, stuffp->di.msf_first.frame, msf2log(&stuffp->di.msf_first)); xtrace(OPENCLOSE, "open() last %d (%02x:%02x.%02x) %d\n", stuffp->di.n_last, stuffp->di.msf_leadout.minute, stuffp->di.msf_leadout.second, stuffp->di.msf_leadout.frame, msf2log(&stuffp->di.msf_leadout)); } if (stuffp->toc) { xtrace(MALLOC, "open() free old toc @ %p\n", stuffp->toc); kfree(stuffp->toc); stuffp->toc = NULL; } xtrace(OPENCLOSE, "open() init irq generation\n"); if (-1 == mcdx_config(stuffp, 1)) return -EIO; #ifdef FALLBACK /* Set the read speed */ xwarn("AAA %x AAA\n", stuffp->readcmd); if (stuffp->readerrs) stuffp->readcmd = READ1X; else stuffp->readcmd = stuffp->present | SINGLE ? READ1X : READ2X; xwarn("XXX %x XXX\n", stuffp->readcmd); #else stuffp->readcmd = stuffp->present | SINGLE ? READ1X : READ2X; #endif /* try to get the first sector, iff any ... */ if (stuffp->lastsector >= 0) { char buf[512]; int ans; int tries; stuffp->xa = 0; stuffp->audio = 0; for (tries = 6; tries; tries--) { stuffp->introk = 1; xtrace(OPENCLOSE, "open() try as %s\n", stuffp->xa ? "XA" : "normal"); /* set data mode */ if (-1 == (ans = mcdx_setdatamode(stuffp, stuffp-> xa ? MODE2 : MODE1, 1))) { /* return -EIO; */ stuffp->xa = 0; break; } if ((stuffp->audio = e_audio(ans))) break; while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1))); if (ans == 1) break; stuffp->xa = !stuffp->xa; } } /* xa disks will be read in raw mode, others not */ if (-1 == mcdx_setdrivemode(stuffp, stuffp->xa ? RAW : COOKED, 1)) return -EIO; if (stuffp->audio) { xinfo("open() audio disk found\n"); } else if (stuffp->lastsector >= 0) { xinfo("open() %s%s disk found\n", stuffp->xa ? "XA / " : "", stuffp->multi. multi ? "Multi Session" : "Single Session"); } } stuffp->xxx = 0; stuffp->users++; return 0; }
static void do_mcdx_request(request_queue_t * q) { struct s_drive_stuff *stuffp; struct request *req; again: req = elv_next_request(q); if (!req) return; stuffp = req->rq_disk->private_data; if (!stuffp->present) { xwarn("do_request(): bad device: %s\n",req->rq_disk->disk_name); xtrace(REQUEST, "end_request(0): bad device\n"); end_request(req, 0); return; } if (stuffp->audio) { xwarn("do_request() attempt to read from audio cd\n"); xtrace(REQUEST, "end_request(0): read from audio\n"); end_request(req, 0); return; } xtrace(REQUEST, "do_request() (%lu + %lu)\n", req->sector, req->nr_sectors); if (req->cmd != READ) { xwarn("do_request(): non-read command to cd!!\n"); xtrace(REQUEST, "end_request(0): write\n"); end_request(req, 0); return; } else { stuffp->status = 0; while (req->nr_sectors) { int i; i = mcdx_transfer(stuffp, req->buffer, req->sector, req->nr_sectors); if (i == -1) { end_request(req, 0); goto again; } req->sector += i; req->nr_sectors -= i; req->buffer += (i * 512); } end_request(req, 1); goto again; xtrace(REQUEST, "end_request(1)\n"); end_request(req, 1); } goto again; }
static int mcdx_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg) { struct s_drive_stuff *stuffp = cdi->handle; if (!stuffp->present) return -ENXIO; if (stuffp->xxx) { if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) { stuffp->lastsector = -1; } else { stuffp->lastsector = (CD_FRAMESIZE / 512) * msf2log(&stuffp->di.msf_leadout) - 1; } if (stuffp->toc) { kfree(stuffp->toc); stuffp->toc = NULL; if (-1 == mcdx_readtoc(stuffp)) return -1; } stuffp->xxx = 0; } switch (cmd) { case CDROMSTART:{ xtrace(IOCTL, "ioctl() START\n"); /* Spin up the drive. Don't think we can do this. * For now, ignore it. */ return 0; } case CDROMSTOP:{ xtrace(IOCTL, "ioctl() STOP\n"); stuffp->audiostatus = CDROM_AUDIO_INVALID; if (-1 == mcdx_stop(stuffp, 1)) return -EIO; return 0; } case CDROMPLAYTRKIND:{ struct cdrom_ti *ti = (struct cdrom_ti *) arg; xtrace(IOCTL, "ioctl() PLAYTRKIND\n"); if ((ti->cdti_trk0 < stuffp->di.n_first) || (ti->cdti_trk0 > stuffp->di.n_last) || (ti->cdti_trk1 < stuffp->di.n_first)) return -EINVAL; if (ti->cdti_trk1 > stuffp->di.n_last) ti->cdti_trk1 = stuffp->di.n_last; xtrace(PLAYTRK, "ioctl() track %d to %d\n", ti->cdti_trk0, ti->cdti_trk1); return mcdx_playtrk(stuffp, ti); } case CDROMPLAYMSF:{ struct cdrom_msf *msf = (struct cdrom_msf *) arg; xtrace(IOCTL, "ioctl() PLAYMSF\n"); if ((stuffp->audiostatus == CDROM_AUDIO_PLAY) && (-1 == mcdx_hold(stuffp, 1))) return -EIO; msf->cdmsf_min0 = uint2bcd(msf->cdmsf_min0); msf->cdmsf_sec0 = uint2bcd(msf->cdmsf_sec0); msf->cdmsf_frame0 = uint2bcd(msf->cdmsf_frame0); msf->cdmsf_min1 = uint2bcd(msf->cdmsf_min1); msf->cdmsf_sec1 = uint2bcd(msf->cdmsf_sec1); msf->cdmsf_frame1 = uint2bcd(msf->cdmsf_frame1); stuffp->stop.dt.minute = msf->cdmsf_min1; stuffp->stop.dt.second = msf->cdmsf_sec1; stuffp->stop.dt.frame = msf->cdmsf_frame1; return mcdx_playmsf(stuffp, msf); } case CDROMRESUME:{ xtrace(IOCTL, "ioctl() RESUME\n"); return mcdx_playtrk(stuffp, NULL); } case CDROMREADTOCENTRY:{ struct cdrom_tocentry *entry = (struct cdrom_tocentry *) arg; struct s_subqcode *tp = NULL; xtrace(IOCTL, "ioctl() READTOCENTRY\n"); if (-1 == mcdx_readtoc(stuffp)) return -1; if (entry->cdte_track == CDROM_LEADOUT) tp = &stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1]; else if (entry->cdte_track > stuffp->di.n_last || entry->cdte_track < stuffp->di.n_first) return -EINVAL; else tp = &stuffp->toc[entry->cdte_track - stuffp->di.n_first]; if (NULL == tp) return -EIO; entry->cdte_adr = tp->control; entry->cdte_ctrl = tp->control >> 4; /* Always return stuff in MSF, and let the Uniform cdrom driver worry about what the user actually wants */ entry->cdte_addr.msf.minute = bcd2uint(tp->dt.minute); entry->cdte_addr.msf.second = bcd2uint(tp->dt.second); entry->cdte_addr.msf.frame = bcd2uint(tp->dt.frame); return 0; } case CDROMSUBCHNL:{ struct cdrom_subchnl *sub = (struct cdrom_subchnl *) arg; struct s_subqcode q; xtrace(IOCTL, "ioctl() SUBCHNL\n"); if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) return -EIO; xtrace(SUBCHNL, "audiostatus: %x\n", stuffp->audiostatus); sub->cdsc_audiostatus = stuffp->audiostatus; sub->cdsc_adr = q.control; sub->cdsc_ctrl = q.control >> 4; sub->cdsc_trk = bcd2uint(q.tno); sub->cdsc_ind = bcd2uint(q.index); xtrace(SUBCHNL, "trk %d, ind %d\n", sub->cdsc_trk, sub->cdsc_ind); /* Always return stuff in MSF, and let the Uniform cdrom driver worry about what the user actually wants */ sub->cdsc_absaddr.msf.minute = bcd2uint(q.dt.minute); sub->cdsc_absaddr.msf.second = bcd2uint(q.dt.second); sub->cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame); sub->cdsc_reladdr.msf.minute = bcd2uint(q.tt.minute); sub->cdsc_reladdr.msf.second = bcd2uint(q.tt.second); sub->cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame); xtrace(SUBCHNL, "msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n", sub->cdsc_absaddr.msf.minute, sub->cdsc_absaddr.msf.second, sub->cdsc_absaddr.msf.frame, sub->cdsc_reladdr.msf.minute, sub->cdsc_reladdr.msf.second, sub->cdsc_reladdr.msf.frame); return 0; } case CDROMREADTOCHDR:{ struct cdrom_tochdr *toc = (struct cdrom_tochdr *) arg; xtrace(IOCTL, "ioctl() READTOCHDR\n"); toc->cdth_trk0 = stuffp->di.n_first; toc->cdth_trk1 = stuffp->di.n_last; xtrace(TOCHDR, "ioctl() track0 = %d, track1 = %d\n", stuffp->di.n_first, stuffp->di.n_last); return 0; } case CDROMPAUSE:{ xtrace(IOCTL, "ioctl() PAUSE\n"); if (stuffp->audiostatus != CDROM_AUDIO_PLAY) return -EINVAL; if (-1 == mcdx_stop(stuffp, 1)) return -EIO; stuffp->audiostatus = CDROM_AUDIO_PAUSED; if (-1 == mcdx_requestsubqcode(stuffp, &stuffp->start, 1)) return -EIO; return 0; } case CDROMMULTISESSION:{ struct cdrom_multisession *ms = (struct cdrom_multisession *) arg; xtrace(IOCTL, "ioctl() MULTISESSION\n"); /* Always return stuff in LBA, and let the Uniform cdrom driver worry about what the user actually wants */ ms->addr.lba = msf2log(&stuffp->multi.msf_last); ms->xa_flag = !!stuffp->multi.multi; xtrace(MS, "ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n", ms->xa_flag, ms->addr.lba, stuffp->multi.msf_last.minute, stuffp->multi.msf_last.second, stuffp->multi.msf_last.frame); return 0; } case CDROMEJECT:{ xtrace(IOCTL, "ioctl() EJECT\n"); if (stuffp->users > 1) return -EBUSY; return (mcdx_tray_move(cdi, 1)); } case CDROMCLOSETRAY:{ xtrace(IOCTL, "ioctl() CDROMCLOSETRAY\n"); return (mcdx_tray_move(cdi, 0)); } case CDROMVOLCTRL:{ struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg; xtrace(IOCTL, "ioctl() VOLCTRL\n"); #if 0 /* not tested! */ /* adjust for the weirdness of workman (md) */ /* can't test it (hs) */ volctrl.channel2 = volctrl.channel1; volctrl.channel1 = volctrl.channel3 = 0x00; #endif return mcdx_setattentuator(stuffp, volctrl, 2); } default: return -EINVAL; } }
int mcdx_readtoc(struct s_drive_stuff *stuffp) /* Read the toc entries from the CD, * Return: -1 on failure, else 0 */ { if (stuffp->toc) { xtrace(READTOC, "ioctl() toc already read\n"); return 0; } xtrace(READTOC, "ioctl() readtoc for %d tracks\n", stuffp->di.n_last - stuffp->di.n_first + 1); if (-1 == mcdx_hold(stuffp, 1)) return -1; xtrace(READTOC, "ioctl() tocmode\n"); if (-1 == mcdx_setdrivemode(stuffp, TOC, 1)) return -EIO; /* all seems to be ok so far ... malloc */ { int size; size = sizeof(struct s_subqcode) * (stuffp->di.n_last - stuffp->di.n_first + 2); xtrace(MALLOC, "ioctl() malloc %d bytes\n", size); stuffp->toc = kmalloc(size, GFP_KERNEL); if (!stuffp->toc) { xwarn("Cannot malloc %d bytes for toc\n", size); mcdx_setdrivemode(stuffp, DATA, 1); return -EIO; } } /* now read actually the index */ { int trk; int retries; for (trk = 0; trk < (stuffp->di.n_last - stuffp->di.n_first + 1); trk++) stuffp->toc[trk].index = 0; for (retries = 300; retries; retries--) { /* why 300? */ struct s_subqcode q; unsigned int idx; if (-1 == mcdx_requestsubqcode(stuffp, &q, 1)) { mcdx_setdrivemode(stuffp, DATA, 1); return -EIO; } idx = bcd2uint(q.index); if ((idx > 0) && (idx <= stuffp->di.n_last) && (q.tno == 0) && (stuffp->toc[idx - stuffp->di.n_first]. index == 0)) { stuffp->toc[idx - stuffp->di.n_first] = q; xtrace(READTOC, "ioctl() toc idx %d (trk %d)\n", idx, trk); trk--; } if (trk == 0) break; } memset(&stuffp-> toc[stuffp->di.n_last - stuffp->di.n_first + 1], 0, sizeof(stuffp->toc[0])); stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1].dt = stuffp->di.msf_leadout; } /* unset toc mode */ xtrace(READTOC, "ioctl() undo toc mode\n"); if (-1 == mcdx_setdrivemode(stuffp, DATA, 2)) return -EIO; #if MCDX_DEBUG && READTOC { int trk; for (trk = 0; trk < (stuffp->di.n_last - stuffp->di.n_first + 2); trk++) xtrace(READTOC, "ioctl() %d readtoc %02x %02x %02x" " %02x:%02x.%02x %02x:%02x.%02x\n", trk + stuffp->di.n_first, stuffp->toc[trk].control, stuffp->toc[trk].tno, stuffp->toc[trk].index, stuffp->toc[trk].tt.minute, stuffp->toc[trk].tt.second, stuffp->toc[trk].tt.frame, stuffp->toc[trk].dt.minute, stuffp->toc[trk].dt.second, stuffp->toc[trk].dt.frame); } #endif return 0; }
/* * Parse an XPATH expression. * Sorry this is such a spaghetti code implementation :-( */ static xpath_enode_t * __xpath_build_expr(const char **pp, char terminator, int infixprio) { xpath_enode_t *current = NULL; const char *pos = *pp; xtrace("__xpath_build_expr(\"%s\", '%c', %d)", pos, terminator?: '^', infixprio); while (*pos != terminator) { xpath_enode_t *newnode; xpath_operator_t *ops = NULL; const char *ident; const char *token_begin; __xpath_skipws(&pos); xtrace(" current %p - \"%s\"", current, pos); if (*pos == '\0') return NULL; token_begin = pos; if (pos[0] == '/') { char *colons; /* Skip over first slash */ ++pos; if (pos[0] == '/') { /* "//" is shorthand for "descendant::" */ ops = &__xpath_operator_descendant; ++pos; } else if (pos[0] == '@') { handle_atsign: /* "@" is shorthand for "attribute::" */ ops = &__xpath_operator_getattr; ++pos; } else { /* FIXME handle "." and ".." */ ops = NULL; } /* path/Name */ if (!(ident = __xpath_next_identifier(&pos))) { ni_error("XPATH: expected identifier at \"%s\"", pos); goto failed; } handle_name_or_axis: if (!__xpath_enode_assert_element(¤t)) goto failed; if (ops != NULL) { /* Okay, we've been using a shorthand identifier */; } else if ((colons = strstr(ident, "::")) != NULL) { *colons = '\0'; ops = xpath_get_axis_ops(ident); if (!ops) { ni_error("XPATH: unknown operator %s::", ident); goto failed; } ident = NULL; if (colons[2] != '\0') { /* This one has form "axis::Name" */ ident = colons + 2; } else if (*pos == '*') { /* This one has form "axis::*" */ ++pos; } else { ni_error("operator %s:: must be followed by Name or *", ident); goto failed; } } else if (*pos == '(') { goto handle_function; } else { /* "path/Name" really means "path/child::Name" */ ops = &__xpath_operator_child; } newnode = xpath_enode_new(ops); if (ident) ni_string_dup(&newnode->identifier, ident); newnode->left = current; current = newnode; } else if (pos[0] == '@') { goto handle_atsign; } else if (pos[0] == '[') { if (!__xpath_enode_assert_element(¤t)) goto failed; newnode = xpath_enode_new(&__xpath_operator_predicate); newnode->left = current; current = newnode; ++pos; current->right = __xpath_build_expr(&pos, ']', 0); if (!current->right) goto failed; if (*pos != ']') { ni_error("XPATH: Missing closing ]"); goto failed; } ++pos; } else if (pos[0] == '(') { if (current != NULL) goto failed; ++pos; current = __xpath_build_expr(&pos, ')', 0); if (!current || *pos != ')') goto failed; ++pos; } else if ((ops = xpath_get_comparison_ops(&pos)) != NULL) { ident = ops->name; goto handle_infix_operator; } else if (current && (pos[0] == '+' || pos[0] == '-' || pos[0] == '*')) { char infixbuf[2]; infixbuf[0] = *pos++; infixbuf[1] = '\0'; ident = infixbuf; find_infix_operator: ops = xpath_get_infix_ops(ident); if (!ops) { ni_error("operator %s not implemented", ident); goto failed; } handle_infix_operator: if (current == NULL) { ni_error("Operator %s without LHS expression", ident); goto failed; } xtrace(" found infix operator %s - prio %u; current limit %u", ops->name, ops->priority, infixprio); if (ops->priority <= infixprio) { /* stop processing before the infix * operator and return to previous level */ pos = token_begin; break; } newnode = xpath_enode_new(ops); newnode->left = current; current = newnode; current->right = __xpath_build_expr(&pos, terminator, ops->priority); if (!current->right) goto failed; } else if (isalpha(pos[0])) { /* can be * axis::<something> * function(something) * boolean infix operator (and, or) * boolean constant (true, false) */ if (!(ident = __xpath_next_identifier(&pos))) goto failed; if (strstr(ident, "::") != NULL) { goto handle_name_or_axis; } else if (!strcasecmp(ident, "and") || !strcasecmp(ident, "or") || !strcasecmp(ident, "div") || !strcasecmp(ident, "mod")) { goto find_infix_operator; } else if (pos[0] == '(') { handle_function: /* find named function */; ops = xpath_get_function(ident); if (!ops) { ni_error("XPATH: unknown function \"%s\"", ident); goto failed; } newnode = xpath_enode_new(ops); newnode->left = current; current = newnode; ++pos; __xpath_skipws(&pos); if (*pos == ')') { /* This is a function taking no arguments, or * operates on the current node. */ ++pos; } else { if (current->left != NULL) goto failed; current->left = __xpath_build_expr(&pos, ')', 0); if (!current->left) goto failed; ++pos; } } else if (!strcasecmp(ident, "true") || !strcasecmp(ident, "false")) { /* true and false can appear as constants as well as * functions. The function part is handled above; * we just treat the constant appearance here. */ if (current != NULL) goto failed; current = xpath_enode_new(xpath_get_function(ident)); } else { goto handle_name_or_axis; } } else if (pos[0] == '\'') { /* string constant */ const char *begin = ++pos; unsigned int n; if (current != NULL) goto failed; current = xpath_enode_new(&__xpath_operator_stringconst); for (n = 0; pos[n] != '\''; ++n) { if (pos[n] == '\0') goto failed; } current->identifier = malloc(n + 1); memcpy(current->identifier, begin, n); current->identifier[n] = '\0'; pos += n + 1; } else if (isdigit(pos[0])) { if (current != NULL) goto failed; current = xpath_enode_new(&__xpath_operator_intconst); current->integer = strtol(pos, (char **) &pos, 0); } else { goto failed; } } *pp = pos; return current; failed: /* ni_error("xpath: syntax error in expression \"%s\" at position %s", expr, pos); */ if (current) xpath_expression_free(current); return NULL; }
static int mcdx_xfer(struct s_drive_stuff *stuffp, char *p, int sector, int nr_sectors) /* This does actually the transfer from the drive. Return: -1 on timeout or other error else status byte (as in stuff->st) */ { int border; int done = 0; long timeout; if (stuffp->audio) { xwarn("Attempt to read from audio CD.\n"); return -1; } if (!stuffp->readcmd) { xinfo("Can't transfer from missing disk.\n"); return -1; } while (stuffp->lock) { interruptible_sleep_on(&stuffp->lockq); } if (stuffp->valid && (sector >= stuffp->pending) && (sector < stuffp->low_border)) { /* All (or at least a part of the sectors requested) seems * to be already requested, so we don't need to bother the * drive with new requests ... * Wait for the drive become idle, but first * check for possible occurred errors --- the drive * seems to report them asynchronously */ border = stuffp->high_border < (border = sector + nr_sectors) ? stuffp->high_border : border; stuffp->lock = current->pid; do { while (stuffp->busy) { timeout = interruptible_sleep_on_timeout (&stuffp->busyq, 5 * HZ); if (!stuffp->introk) { xtrace(XFER, "error via interrupt\n"); } else if (!timeout) { xtrace(XFER, "timeout\n"); } else if (signal_pending(current)) { xtrace(XFER, "signal\n"); } else continue; stuffp->lock = 0; stuffp->busy = 0; stuffp->valid = 0; wake_up_interruptible(&stuffp->lockq); xtrace(XFER, "transfer() done (-1)\n"); return -1; } /* check if we need to set the busy flag (as we * expect an interrupt */ stuffp->busy = (3 == (stuffp->pending & 3)); /* Test if it's the first sector of a block, * there we have to skip some bytes as we read raw data */ if (stuffp->xa && (0 == (stuffp->pending & 3))) { const int HEAD = CD_FRAMESIZE_RAW - CD_XA_TAIL - CD_FRAMESIZE; insb(stuffp->rreg_data, p, HEAD); } /* now actually read the data */ insb(stuffp->rreg_data, p, 512); /* test if it's the last sector of a block, * if so, we have to handle XA special */ if ((3 == (stuffp->pending & 3)) && stuffp->xa) { char dummy[CD_XA_TAIL]; insb(stuffp->rreg_data, &dummy[0], CD_XA_TAIL); } if (stuffp->pending == sector) { p += 512; done++; sector++; } } while (++(stuffp->pending) < border); stuffp->lock = 0; wake_up_interruptible(&stuffp->lockq); } else { /* The requested sector(s) is/are out of the * already requested range, so we have to bother the drive * with a new request. */ static unsigned char cmd[] = { 0, 0, 0, 0, 0, 0, 0 }; cmd[0] = stuffp->readcmd; /* The numbers held in ->pending, ..., should be valid */ stuffp->valid = 1; stuffp->pending = sector & ~3; /* do some sanity checks */ if (stuffp->pending > stuffp->lastsector) { xwarn ("transfer() sector %d from nirvana requested.\n", stuffp->pending); stuffp->status = MCDX_ST_EOM; stuffp->valid = 0; xtrace(XFER, "transfer() done (-1)\n"); return -1; } if ((stuffp->low_border = stuffp->pending + DIRECT_SIZE) > stuffp->lastsector + 1) { xtrace(XFER, "cut low_border\n"); stuffp->low_border = stuffp->lastsector + 1; } if ((stuffp->high_border = stuffp->pending + REQUEST_SIZE) > stuffp->lastsector + 1) { xtrace(XFER, "cut high_border\n"); stuffp->high_border = stuffp->lastsector + 1; } { /* Convert the sector to be requested to MSF format */ struct cdrom_msf0 pending; log2msf(stuffp->pending / 4, &pending); cmd[1] = pending.minute; cmd[2] = pending.second; cmd[3] = pending.frame; } cmd[6] = (unsigned char) ((stuffp->high_border - stuffp->pending) / 4); xtrace(XFER, "[%2d]\n", cmd[6]); stuffp->busy = 1; /* Now really issue the request command */ outsb(stuffp->wreg_data, cmd, sizeof cmd); } #ifdef AK2 if (stuffp->int_err) { stuffp->valid = 0; stuffp->int_err = 0; return -1; } #endif /* AK2 */ stuffp->low_border = (stuffp->low_border += done) < stuffp->high_border ? stuffp->low_border : stuffp->high_border; return done; }
static int mcdx_talk(struct s_drive_stuff *stuffp, const unsigned char *cmd, size_t cmdlen, void *buffer, size_t size, unsigned int timeout, int tries) /* Send a command to the drive, wait for the result. * returns -1 on timeout, drive status otherwise * If buffer is not zero, the result (length size) is stored there. * If buffer is zero the size should be the number of bytes to read * from the drive. These bytes are discarded. */ { int st; char c; int discard; /* Somebody wants the data read? */ if ((discard = (buffer == NULL))) buffer = &c; while (stuffp->lock) { xtrace(SLEEP, "*** talk: lockq\n"); interruptible_sleep_on(&stuffp->lockq); xtrace(SLEEP, "talk: awoken\n"); } stuffp->lock = 1; /* An operation other then reading data destroys the * data already requested and remembered in stuffp->request, ... */ stuffp->valid = 0; #if MCDX_DEBUG & TALK { unsigned char i; xtrace(TALK, "talk() %d / %d tries, res.size %d, command 0x%02x", tries, timeout, size, (unsigned char) cmd[0]); for (i = 1; i < cmdlen; i++) xtrace(TALK, " 0x%02x", cmd[i]); xtrace(TALK, "\n"); } #endif /* give up if all tries are done (bad) or if the status * st != -1 (good) */ for (st = -1; st == -1 && tries; tries--) { char *bp = (char *) buffer; size_t sz = size; outsb(stuffp->wreg_data, cmd, cmdlen); xtrace(TALK, "talk() command sent\n"); /* get the status byte */ if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) { xinfo("talk() %02x timed out (status), %d tr%s left\n", cmd[0], tries - 1, tries == 2 ? "y" : "ies"); continue; } st = *bp; sz--; if (!discard) bp++; xtrace(TALK, "talk() got status 0x%02x\n", st); /* command error? */ if (e_cmderr(st)) { xwarn("command error cmd = %02x %s \n", cmd[0], cmdlen > 1 ? "..." : ""); st = -1; continue; } /* audio status? */ if (stuffp->audiostatus == CDROM_AUDIO_INVALID) stuffp->audiostatus = e_audiobusy(st) ? CDROM_AUDIO_PLAY : CDROM_AUDIO_NO_STATUS; else if (stuffp->audiostatus == CDROM_AUDIO_PLAY && e_audiobusy(st) == 0) stuffp->audiostatus = CDROM_AUDIO_COMPLETED; /* media change? */ if (e_changed(st)) { xinfo("talk() media changed\n"); stuffp->xxx = stuffp->yyy = 1; } /* now actually get the data */ while (sz--) { if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) { xinfo("talk() %02x timed out (data), %d tr%s left\n", cmd[0], tries - 1, tries == 2 ? "y" : "ies"); st = -1; break; } if (!discard) bp++; xtrace(TALK, "talk() got 0x%02x\n", *(bp - 1)); } } #if !MCDX_QUIET if (!tries && st == -1) xinfo("talk() giving up\n"); #endif stuffp->lock = 0; wake_up_interruptible(&stuffp->lockq); xtrace(TALK, "talk() done with 0x%02x\n", st); return st; }
static int __init mcdx_init_drive(int drive) { struct s_version version; struct gendisk *disk; struct s_drive_stuff *stuffp; int size = sizeof(*stuffp); char msg[80]; xtrace(INIT, "init() try drive %d\n", drive); xtrace(INIT, "kmalloc space for stuffpt's\n"); xtrace(MALLOC, "init() malloc %d bytes\n", size); if (!(stuffp = kzalloc(size, GFP_KERNEL))) { xwarn("init() malloc failed\n"); return 1; } disk = alloc_disk(1); if (!disk) { xwarn("init() malloc failed\n"); kfree(stuffp); return 1; } xtrace(INIT, "init() got %d bytes for drive stuff @ %p\n", sizeof(*stuffp), stuffp); /* set default values */ stuffp->present = 0; /* this should be 0 already */ stuffp->toc = NULL; /* this should be NULL already */ /* setup our irq and i/o addresses */ stuffp->irq = irq(mcdx_drive_map[drive]); stuffp->wreg_data = stuffp->rreg_data = port(mcdx_drive_map[drive]); stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1; stuffp->wreg_hcon = stuffp->wreg_reset + 1; stuffp->wreg_chn = stuffp->wreg_hcon + 1; init_waitqueue_head(&stuffp->busyq); init_waitqueue_head(&stuffp->lockq); init_waitqueue_head(&stuffp->sleepq); /* check if i/o addresses are available */ if (!request_region(stuffp->wreg_data, MCDX_IO_SIZE, "mcdx")) { xwarn("0x%03x,%d: Init failed. " "I/O ports (0x%03x..0x%03x) already in use.\n", stuffp->wreg_data, stuffp->irq, stuffp->wreg_data, stuffp->wreg_data + MCDX_IO_SIZE - 1); xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp); kfree(stuffp); put_disk(disk); xtrace(INIT, "init() continue at next drive\n"); return 0; /* next drive */ } xtrace(INIT, "init() i/o port is available at 0x%03x\n" stuffp->wreg_data); xtrace(INIT, "init() hardware reset\n"); mcdx_reset(stuffp, HARD, 1); xtrace(INIT, "init() get version\n"); if (-1 == mcdx_requestversion(stuffp, &version, 4)) { /* failed, next drive */ release_region(stuffp->wreg_data, MCDX_IO_SIZE); xwarn("%s=0x%03x,%d: Init failed. Can't get version.\n", MCDX, stuffp->wreg_data, stuffp->irq); xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp); kfree(stuffp); put_disk(disk); xtrace(INIT, "init() continue at next drive\n"); return 0; } switch (version.code) { case 'D': stuffp->readcmd = READ2X; stuffp->present = DOUBLE | DOOR | MULTI; break; case 'F': stuffp->readcmd = READ1X; stuffp->present = SINGLE | DOOR | MULTI; break; case 'M': stuffp->readcmd = READ1X; stuffp->present = SINGLE; break; default: stuffp->present = 0; break; } stuffp->playcmd = READ1X; if (!stuffp->present) { release_region(stuffp->wreg_data, MCDX_IO_SIZE); xwarn("%s=0x%03x,%d: Init failed. No Mitsumi CD-ROM?.\n", MCDX, stuffp->wreg_data, stuffp->irq); kfree(stuffp); put_disk(disk); return 0; /* next drive */ } xtrace(INIT, "init() register blkdev\n"); if (register_blkdev(MAJOR_NR, "mcdx")) { release_region(stuffp->wreg_data, MCDX_IO_SIZE); kfree(stuffp); put_disk(disk); return 1; } mcdx_queue = blk_init_queue(do_mcdx_request, &mcdx_lock); if (!mcdx_queue) { unregister_blkdev(MAJOR_NR, "mcdx"); release_region(stuffp->wreg_data, MCDX_IO_SIZE); kfree(stuffp); put_disk(disk); return 1; } xtrace(INIT, "init() subscribe irq and i/o\n"); if (request_irq(stuffp->irq, mcdx_intr, IRQF_DISABLED, "mcdx", stuffp)) { release_region(stuffp->wreg_data, MCDX_IO_SIZE); xwarn("%s=0x%03x,%d: Init failed. Can't get irq (%d).\n", MCDX, stuffp->wreg_data, stuffp->irq, stuffp->irq); stuffp->irq = 0; blk_cleanup_queue(mcdx_queue); kfree(stuffp); put_disk(disk); return 0; } xtrace(INIT, "init() get garbage\n"); { int i; mcdx_delay(stuffp, HZ / 2); for (i = 100; i; i--) (void) inb(stuffp->rreg_status); } #ifdef WE_KNOW_WHY /* irq 11 -> channel register */ outb(0x50, stuffp->wreg_chn); #endif xtrace(INIT, "init() set non dma but irq mode\n"); mcdx_config(stuffp, 1); stuffp->info.ops = &mcdx_dops; stuffp->info.speed = 2; stuffp->info.capacity = 1; stuffp->info.handle = stuffp; sprintf(stuffp->info.name, "mcdx%d", drive); disk->major = MAJOR_NR; disk->first_minor = drive; strcpy(disk->disk_name, stuffp->info.name); disk->fops = &mcdx_bdops; disk->flags = GENHD_FL_CD; stuffp->disk = disk; sprintf(msg, " mcdx: Mitsumi CD-ROM installed at 0x%03x, irq %d." " (Firmware version %c %x)\n", stuffp->wreg_data, stuffp->irq, version.code, version.ver); mcdx_stuffp[drive] = stuffp; xtrace(INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp); if (register_cdrom(&stuffp->info) != 0) { printk("Cannot register Mitsumi CD-ROM!\n"); free_irq(stuffp->irq, NULL); release_region(stuffp->wreg_data, MCDX_IO_SIZE); kfree(stuffp); put_disk(disk); if (unregister_blkdev(MAJOR_NR, "mcdx") != 0) xwarn("cleanup() unregister_blkdev() failed\n"); blk_cleanup_queue(mcdx_queue); return 2; } disk->private_data = stuffp; disk->queue = mcdx_queue; add_disk(disk); printk(msg); return 0; }