⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cm206.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 4 页
字号:
{
	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 + -