📄 cm206.c
字号:
{ 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 = { cm206_open, /* open */ cm206_release, /* release */ cm206_drive_status, /* drive status */ cm206_media_changed, /* media changed */ cm206_tray_move, /* tray move */ cm206_lock_door, /* lock door */ cm206_select_speed, /* select speed */ NULL, /* select disc */ cm206_get_last_session, /* get last session */ cm206_get_upc, /* get universal product code */ cm206_reset, /* hard reset */ cm206_audio_ioctl, /* audio ioctl */ cm206_ioctl, /* device-specific ioctl */ 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, /* capability */ 1, /* number of minor devices */};static struct cdrom_device_info cm206_info = { &cm206_dops, /* device operations */ NULL, /* link */ NULL, /* handle (not used by cm206) */ 0, /* dev */ 0, /* mask */ 2, /* maximum speed */ 1, /* number of discs */ 0, /* options, not owned */ 0, /* mc_flags, not owned */ 0, /* use count, not owned */ "cm206" /* name of the device type */};/* 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 (unregister_blkdev(MAJOR_NR, "cm206")) { printk("Can't unregister major cm206\n"); return; } 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... */__initfunc(int 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. */__initfunc(int 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;}#endif__initfunc(int 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 (register_blkdev(MAJOR_NR, "cm206", &cdrom_fops) != 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; } blk_dev[MAJOR_NR].request_fn = 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 int cm206[2] = {0,0}; /* for compatible `insmod' parameter passing */__initfunc(void 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 init_module(void){ parse_options();#if !defined(AUTO_PROBE_MODULE) auto_probe=0;#endif return cm206_init();}void cleanup_module(void){ cleanup(4); printk(KERN_INFO "cm206 removed\n");} #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. */__initfunc(void cm206_setup(char *s, int *p)){ int i; 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; } }}#endif /* MODULE *//* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -D__SMP__ -pipe -fno-strength-reduce -m486 -DCPU=486 -D__SMP__ -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 + -