📄 cm206.c
字号:
cd->dsb = wait_dsb();}int get_current_q(struct cdrom_subchnl *qp){ int i; uch *q = cd->q; if (type_1_command(c_read_current_q, 10, q)) return 0;/* q[0] = bcdbin(q[0]); Don't think so! */ for (i = 2; i < 6; i++) q[i] = bcdbin(q[i]); qp->cdsc_adr = q[0] & 0xf; qp->cdsc_ctrl = q[0] >> 4; /* from mcd.c */ qp->cdsc_trk = q[1]; qp->cdsc_ind = q[2]; if (qp->cdsc_format == CDROM_MSF) { qp->cdsc_reladdr.msf.minute = q[3]; qp->cdsc_reladdr.msf.second = q[4]; qp->cdsc_reladdr.msf.frame = q[5]; qp->cdsc_absaddr.msf.minute = q[7]; qp->cdsc_absaddr.msf.second = q[8]; qp->cdsc_absaddr.msf.frame = q[9]; } else { qp->cdsc_reladdr.lba = f_s_m2lba(q[5], q[4], q[3]); qp->cdsc_absaddr.lba = f_s_m2lba(q[9], q[8], q[7]); } get_drive_status(); if (cd->dsb & dsb_play_in_progress) qp->cdsc_audiostatus = CDROM_AUDIO_PLAY; else if (PAUSED) qp->cdsc_audiostatus = CDROM_AUDIO_PAUSED; else qp->cdsc_audiostatus = CDROM_AUDIO_NO_STATUS; return 0;}void invalidate_toc(void){ memset(cd->toc, 0, sizeof(cd->toc)); memset(cd->disc_status, 0, sizeof(cd->disc_status));}/* cdrom.c guarantees that cdte_format == CDROM_MSF */void get_toc_entry(struct cdrom_tocentry *ep){ uch track = normalize_track(ep->cdte_track); update_toc_entry(track); ep->cdte_addr.msf.frame = cd->toc[track].fsm[0]; ep->cdte_addr.msf.second = cd->toc[track].fsm[1]; ep->cdte_addr.msf.minute = cd->toc[track].fsm[2]; ep->cdte_adr = cd->toc[track].q0 & 0xf; ep->cdte_ctrl = cd->toc[track].q0 >> 4; ep->cdte_datamode = 0;}/* Audio ioctl. Ioctl commands connected to audio are in such an * idiosyncratic i/o format, that we leave these untouched. Return 0 * upon success. Memory checking has been done by cdrom_ioctl(), the * calling function, as well as LBA/MSF sanitization.*/int cm206_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg){ switch (cmd) { case CDROMREADTOCHDR: return read_toc_header((struct cdrom_tochdr *) arg); case CDROMREADTOCENTRY: get_toc_entry((struct cdrom_tocentry *) arg); return 0; case CDROMPLAYMSF: play_from_to_msf((struct cdrom_msf *) arg); return 0; case CDROMPLAYTRKIND: /* admittedly, not particularly beautiful */ play_from_to_track(((struct cdrom_ti *) arg)->cdti_trk0, ((struct cdrom_ti *) arg)->cdti_trk1); return 0; case CDROMSTOP: PAUSED = 0; if (cd->dsb & dsb_play_in_progress) return type_0_command(c_stop, 1); else return 0; case CDROMPAUSE: get_drive_status(); if (cd->dsb & dsb_play_in_progress) { type_0_command(c_stop, 1); type_1_command(c_audio_status, 5, cd->audio_status); PAUSED = 1; /* say we're paused */ } return 0; case CDROMRESUME: if (PAUSED) play_from_to_track(0, 0); PAUSED = 0; return 0; case CDROMSTART: case CDROMVOLCTRL: return 0; case CDROMSUBCHNL: return get_current_q((struct cdrom_subchnl *) arg); default: return -EINVAL; }}/* Ioctl. These ioctls are specific to the cm206 driver. I have made some driver statistics accessible through ioctl calls. */static int cm206_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, unsigned long arg){ switch (cmd) {#ifdef STATISTICS case CM206CTL_GET_STAT: if (arg >= NR_STATS) return -EINVAL; else return cd->stats[arg]; case CM206CTL_GET_LAST_STAT: if (arg >= NR_STATS) return -EINVAL; else return cd->last_stat[arg];#endif default: debug(("Unknown ioctl call 0x%x\n", cmd)); return -EINVAL; }}int cm206_media_changed(struct cdrom_device_info *cdi, int disc_nr){ if (cd != NULL) { int r; get_drive_status(); /* ensure cd->media_changed OK */ r = cd->media_changed; cd->media_changed = 0; /* clear bit */ return r; } else return -EIO;}/* The new generic cdrom support. Routines should be concise, most of the logic should be in cdrom.c *//* returns number of times device is in use */int cm206_open_files(struct cdrom_device_info *cdi){ if (cd) return cd->openfiles; return -1;}/* controls tray movement */int cm206_tray_move(struct cdrom_device_info *cdi, int position){ if (position) { /* 1: eject */ type_0_command(c_open_tray, 1); invalidate_toc(); } else type_0_command(c_close_tray, 1); /* 0: close */ return 0;}/* gives current state of the drive */int cm206_drive_status(struct cdrom_device_info *cdi, int slot_nr){ get_drive_status(); if (cd->dsb & dsb_tray_not_closed) return CDS_TRAY_OPEN; if (!(cd->dsb & dsb_disc_present)) return CDS_NO_DISC; if (cd->dsb & dsb_drive_not_ready) return CDS_DRIVE_NOT_READY; return CDS_DISC_OK;}/* locks or unlocks door lock==1: lock; return 0 upon success */int cm206_lock_door(struct cdrom_device_info *cdi, int lock){ uch command = (lock) ? c_lock_tray : c_unlock_tray; type_0_command(command, 1); /* wait and get dsb */ /* the logic calculates the success, 0 means successful */ return lock ^ ((cd->dsb & dsb_tray_locked) != 0);}/* Although a session start should be in LBA format, we return it in MSF format because it is slightly easier, and the new generic ioctl will take care of the necessary conversion. */int cm206_get_last_session(struct cdrom_device_info *cdi, struct cdrom_multisession *mssp){ if (!FIRST_TRACK) get_disc_status(); if (mssp != NULL) { if (DISC_STATUS & cds_multi_session) { /* multi-session */ mssp->addr.msf.frame = cd->disc_status[3]; mssp->addr.msf.second = cd->disc_status[4]; mssp->addr.msf.minute = cd->disc_status[5]; mssp->addr_format = CDROM_MSF; mssp->xa_flag = 1; } else { mssp->xa_flag = 0; } return 1; } return 0;}int cm206_get_upc(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn){ uch upc[10]; char *ret = mcn->medium_catalog_number; int i; if (type_1_command(c_read_upc, 10, upc)) return -EIO; for (i = 0; i < 13; i++) { int w = i / 2 + 1, r = i % 2; if (r) ret[i] = 0x30 | (upc[w] & 0x0f); else ret[i] = 0x30 | ((upc[w] >> 4) & 0x0f); } ret[13] = '\0'; return 0;}int cm206_reset(struct cdrom_device_info *cdi){ stop_read(); reset_cm260(); outw(dc_normal | dc_break | READ_AHEAD, r_data_control); mdelay(1); /* 750 musec minimum */ outw(dc_normal | READ_AHEAD, r_data_control); cd->sector_last = -1; /* flag no data buffered */ cd->adapter_last = -1; invalidate_toc(); return 0;}int cm206_select_speed(struct cdrom_device_info *cdi, int speed){ int r; switch (speed) { case 0: r = type_0_command(c_auto_mode, 1); break; case 1: r = type_0_command(c_force_1x, 1); break; case 2: r = type_0_command(c_force_2x, 1); break; default: return -1; } if (r < 0) return r; else return 1;}static struct cdrom_device_ops cm206_dops = { open:cm206_open, release:cm206_release, drive_status:cm206_drive_status, media_changed:cm206_media_changed, tray_move:cm206_tray_move, lock_door:cm206_lock_door, select_speed:cm206_select_speed, get_last_session:cm206_get_last_session, get_mcn:cm206_get_upc, reset:cm206_reset, audio_ioctl:cm206_audio_ioctl, dev_ioctl:cm206_ioctl, capability:CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_MULTI_SESSION | CDC_MEDIA_CHANGED | CDC_MCN | CDC_PLAY_AUDIO | CDC_SELECT_SPEED | CDC_IOCTLS | CDC_DRIVE_STATUS, n_minors:1,};static struct cdrom_device_info cm206_info = { ops:&cm206_dops, speed:2, capacity:1, name:"cm206",};/* This routine gets called during initialization if things go wrong, * can be used in cleanup_module as well. */static void cleanup(int level){ switch (level) { case 4: if (unregister_cdrom(&cm206_info)) { printk("Can't unregister cdrom cm206\n"); return; } if (devfs_unregister_blkdev(MAJOR_NR, "cm206")) { printk("Can't unregister major cm206\n"); return; } blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); case 3: free_irq(cm206_irq, NULL); case 2: case 1: kfree(cd); release_region(cm206_base, 16); default:; }}/* This function probes for the adapter card. It returns the base address if it has found the adapter card. One can specify a base port to probe specifically, or 0 which means span all possible bases. Linus says it is too dangerous to use writes for probing, so we stick with pure reads for a while. Hope that 8 possible ranges, check_region, 15 bits of one port and 6 of another make things likely enough to accept the region on the first hit... */int __init probe_base_port(int base){ int b = 0x300, e = 0x370; /* this is the range of start addresses */ volatile int fool, i; if (base) b = e = base; for (base = b; base <= e; base += 0x10) { if (check_region(base, 0x10)) continue; for (i = 0; i < 3; i++) fool = inw(base + 2); /* empty possibly uart_receive_buffer */ if ((inw(base + 6) & 0xffef) != 0x0001 || /* line_status */ (inw(base) & 0xad00) != 0) /* data status */ continue; return (base); } return 0;}#if !defined(MODULE) || defined(AUTO_PROBE_MODULE)/* Probe for irq# nr. If nr==0, probe for all possible irq's. */int __init probe_irq(int nr){ int irqs, irq; outw(dc_normal | READ_AHEAD, r_data_control); /* disable irq-generation */ sti(); irqs = probe_irq_on(); reset_cm260(); /* causes interrupt */ udelay(100); /* wait for it */ irq = probe_irq_off(irqs); outw(dc_normal | READ_AHEAD, r_data_control); /* services interrupt */ if (nr && irq != nr && irq > 0) return 0; /* wrong interrupt happened */ else return irq;}#endifint __init cm206_init(void){ uch e = 0; long int size = sizeof(struct cm206_struct); printk(KERN_INFO "cm206 cdrom driver " REVISION); cm206_base = probe_base_port(auto_probe ? 0 : cm206_base); if (!cm206_base) { printk(" can't find adapter!\n"); return -EIO; } printk(" adapter at 0x%x", cm206_base); request_region(cm206_base, 16, "cm206"); cd = (struct cm206_struct *) kmalloc(size, GFP_KERNEL); if (!cd) return -EIO; /* Now we have found the adaptor card, try to reset it. As we have * found out earlier, this process generates an interrupt as well, * so we might just exploit that fact for irq probing! */#if !defined(MODULE) || defined(AUTO_PROBE_MODULE) cm206_irq = probe_irq(auto_probe ? 0 : cm206_irq); if (cm206_irq <= 0) { printk("can't find IRQ!\n"); cleanup(1); return -EIO; } else printk(" IRQ %d found\n", cm206_irq);#else cli(); reset_cm260(); /* Now, the problem here is that reset_cm260 can generate an interrupt. It seems that this can cause a kernel oops some time later. So we wait a while and `service' this interrupt. */ mdelay(1); outw(dc_normal | READ_AHEAD, r_data_control); sti(); printk(" using IRQ %d\n", cm206_irq);#endif if (send_receive_polled(c_drive_configuration) != c_drive_configuration) { printk(KERN_INFO " drive not there\n"); cleanup(1); return -EIO; } e = send_receive_polled(c_gimme); printk(KERN_INFO "Firmware revision %d", e & dcf_revision_code); if (e & dcf_transfer_rate) printk(" double"); else printk(" single"); printk(" speed drive"); if (e & dcf_motorized_tray) printk(", motorized tray"); if (request_irq(cm206_irq, cm206_interrupt, 0, "cm206", NULL)) { printk("\nUnable to reserve IRQ---aborted\n"); cleanup(2); return -EIO; } printk(".\n"); if (devfs_register_blkdev(MAJOR_NR, "cm206", &cm206_bdops) != 0) { printk(KERN_INFO "Cannot register for major %d!\n", MAJOR_NR); cleanup(3); return -EIO; } cm206_info.dev = MKDEV(MAJOR_NR, 0); if (register_cdrom(&cm206_info) != 0) { printk(KERN_INFO "Cannot register for cdrom %d!\n", MAJOR_NR); cleanup(3); return -EIO; } devfs_plain_cdrom(&cm206_info, &cm206_bdops); blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); blksize_size[MAJOR_NR] = cm206_blocksizes; read_ahead[MAJOR_NR] = 16; /* reads ahead what? */ init_bh(CM206_BH, cm206_bh); memset(cd, 0, sizeof(*cd)); /* give'm some reasonable value */ cd->sector_last = -1; /* flag no data buffered */ cd->adapter_last = -1; cd->timer.function = cm206_timeout; cd->max_sectors = (inw(r_data_status) & ds_ram_size) ? 24 : 97; printk(KERN_INFO "%d kB adapter memory available, " " %ld bytes kernel memory used.\n", cd->max_sectors * 2, size); return 0;}#ifdef MODULEstatic void __init parse_options(void){ int i; for (i = 0; i < 2; i++) { if (0x300 <= cm206[i] && i <= 0x370 && cm206[i] % 0x10 == 0) { cm206_base = cm206[i]; auto_probe = 0; } else if (3 <= cm206[i] && cm206[i] <= 15) { cm206_irq = cm206[i]; auto_probe = 0; } }}int __cm206_init(void){ parse_options();#if !defined(AUTO_PROBE_MODULE) auto_probe = 0;#endif return cm206_init();}void __exit cm206_exit(void){ cleanup(4); printk(KERN_INFO "cm206 removed\n");}module_init(__cm206_init);module_exit(cm206_exit);#else /* !MODULE *//* This setup function accepts either `auto' or numbers in the range * 3--11 (for irq) or 0x300--0x370 (for base port) or both. */static int __init cm206_setup(char *s){ int i, p[4]; (void) get_options(s, ARRAY_SIZE(p), p); if (!strcmp(s, "auto")) auto_probe = 1; for (i = 1; i <= p[0]; i++) { if (0x300 <= p[i] && i <= 0x370 && p[i] % 0x10 == 0) { cm206_base = p[i]; auto_probe = 0; } else if (3 <= p[i] && p[i] <= 15) { cm206_irq = p[i]; auto_probe = 0; } } return 1;}__setup("cm206=", cm206_setup);#endif /* !MODULE *//* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -m486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o cm206.o cm206.c" * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -