📄 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 = {
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;
}
#endif
int __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 MODULE
static 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 + -