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

📄 cdrom.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	cdinfo(CD_OPEN, "entering unregister_cdrom\n"); 	if (major < 0 || major >= MAX_BLKDEV)		return -1;	prev = NULL;	cdi = topCdromPtr;	while (cdi != NULL && cdi->dev != unreg->dev) {		prev = cdi;		cdi = cdi->next;	}	if (cdi == NULL)		return -2;	if (prev)		prev->next = cdi->next;	else		topCdromPtr = cdi->next;	cdi->ops->n_minors--;	devfs_unregister (cdi->de);	cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name);	return 0;}struct cdrom_device_info *cdrom_find_device(kdev_t dev){	struct cdrom_device_info *cdi;	cdi = topCdromPtr;	while (cdi != NULL && cdi->dev != dev)		cdi = cdi->next;	return cdi;}/* We use the open-option O_NONBLOCK to indicate that the * purpose of opening is only for subsequent ioctl() calls; no device * integrity checks are performed. * * We hope that all cd-player programs will adopt this convention. It * is in their own interest: device control becomes a lot easier * this way. */staticint cdrom_open(struct inode *ip, struct file *fp){	struct cdrom_device_info *cdi;	kdev_t dev = ip->i_rdev;	int ret;	cdinfo(CD_OPEN, "entering cdrom_open\n"); 	if ((cdi = cdrom_find_device(dev)) == NULL)		return -ENODEV;	if ((fp->f_mode & FMODE_WRITE) && !CDROM_CAN(CDC_DVD_RAM))		return -EROFS;	/* if this was a O_NONBLOCK open and we should honor the flags,	 * do a quick open without drive/disc integrity checks. */	if ((fp->f_flags & O_NONBLOCK) && (cdi->options & CDO_USE_FFLAGS))		ret = cdi->ops->open(cdi, 1);	else		ret = open_for_data(cdi);	if (!ret) cdi->use_count++;	cdinfo(CD_OPEN, "Use count for \"/dev/%s\" now %d\n", cdi->name, cdi->use_count);	/* Do this on open.  Don't wait for mount, because they might	    not be mounting, but opening with O_NONBLOCK */	check_disk_change(dev);	return ret;}staticint open_for_data(struct cdrom_device_info * cdi){	int ret;	struct cdrom_device_ops *cdo = cdi->ops;	tracktype tracks;	cdinfo(CD_OPEN, "entering open_for_data\n");	/* Check if the driver can report drive status.  If it can, we	   can do clever things.  If it can't, well, we at least tried! */	if (cdo->drive_status != NULL) {		ret = cdo->drive_status(cdi, CDSL_CURRENT);		cdinfo(CD_OPEN, "drive_status=%d\n", ret); 		if (ret == CDS_TRAY_OPEN) {			cdinfo(CD_OPEN, "the tray is open...\n"); 			/* can/may i close it? */			if (CDROM_CAN(CDC_CLOSE_TRAY) &&			    cdi->options & CDO_AUTO_CLOSE) {				cdinfo(CD_OPEN, "trying to close the tray.\n"); 				ret=cdo->tray_move(cdi,0);				if (ret) {					cdinfo(CD_OPEN, "bummer. tried to close the tray but failed.\n"); 					/* Ignore the error from the low					level driver.  We don't care why it					couldn't close the tray.  We only care 					that there is no disc in the drive, 					since that is the _REAL_ problem here.*/					ret=-ENOMEDIUM;					goto clean_up_and_return;				}			} else {				cdinfo(CD_OPEN, "bummer. this drive can't close the tray.\n"); 				ret=-ENOMEDIUM;				goto clean_up_and_return;			}			/* Ok, the door should be closed now.. Check again */			ret = cdo->drive_status(cdi, CDSL_CURRENT);			if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) {				cdinfo(CD_OPEN, "bummer. the tray is still not closed.\n"); 				cdinfo(CD_OPEN, "tray might not contain a medium.\n");				ret=-ENOMEDIUM;				goto clean_up_and_return;			}			cdinfo(CD_OPEN, "the tray is now closed.\n"); 		}		if (ret!=CDS_DISC_OK) {			ret = -ENOMEDIUM;			goto clean_up_and_return;		}	}	cdrom_count_tracks(cdi, &tracks);	if (tracks.error == CDS_NO_DISC) {		cdinfo(CD_OPEN, "bummer. no disc.\n");		ret=-ENOMEDIUM;		goto clean_up_and_return;	}	/* CD-Players which don't use O_NONBLOCK, workman	 * for example, need bit CDO_CHECK_TYPE cleared! */	if (tracks.data==0) {		if (cdi->options & CDO_CHECK_TYPE) {		    /* give people a warning shot, now that CDO_CHECK_TYPE		       is the default case! */		    cdinfo(CD_OPEN, "bummer. wrong media type.\n"); 		    cdinfo(CD_WARNING, "pid %d must open device O_NONBLOCK!\n",					(unsigned int)current->pid); 		    ret=-EMEDIUMTYPE;		    goto clean_up_and_return;		}		else {		    cdinfo(CD_OPEN, "wrong media type, but CDO_CHECK_TYPE not set.\n");		}	}	cdinfo(CD_OPEN, "all seems well, opening the device.\n"); 	/* all seems well, we can open the device */	ret = cdo->open(cdi, 0); /* open for data */	cdinfo(CD_OPEN, "opening the device gave me %d.\n", ret); 	/* After all this careful checking, we shouldn't have problems	   opening the device, but we don't want the device locked if 	   this somehow fails... */	if (ret) {		cdinfo(CD_OPEN, "open device failed.\n"); 		goto clean_up_and_return;	}	if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) {			cdo->lock_door(cdi, 1);			cdinfo(CD_OPEN, "door locked.\n");	}	cdinfo(CD_OPEN, "device opened successfully.\n"); 	return ret;	/* Something failed.  Try to unlock the drive, because some drivers	(notably ide-cd) lock the drive after every command.  This produced	a nasty bug where after mount failed, the drive would remain locked!  	This ensures that the drive gets unlocked after a mount fails.  This 	is a goto to avoid bloating the driver with redundant code. */ clean_up_and_return:	cdinfo(CD_WARNING, "open failed.\n"); 	if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) {			cdo->lock_door(cdi, 0);			cdinfo(CD_OPEN, "door unlocked.\n");	}	return ret;}/* This code is similar to that in open_for_data. The routine is called   whenever an audio play operation is requested.*/int check_for_audio_disc(struct cdrom_device_info * cdi,			 struct cdrom_device_ops * cdo){        int ret;	tracktype tracks;	cdinfo(CD_OPEN, "entering check_for_audio_disc\n");	if (!(cdi->options & CDO_CHECK_TYPE))		return 0;	if (cdo->drive_status != NULL) {		ret = cdo->drive_status(cdi, CDSL_CURRENT);		cdinfo(CD_OPEN, "drive_status=%d\n", ret); 		if (ret == CDS_TRAY_OPEN) {			cdinfo(CD_OPEN, "the tray is open...\n"); 			/* can/may i close it? */			if (CDROM_CAN(CDC_CLOSE_TRAY) &&			    cdi->options & CDO_AUTO_CLOSE) {				cdinfo(CD_OPEN, "trying to close the tray.\n"); 				ret=cdo->tray_move(cdi,0);				if (ret) {					cdinfo(CD_OPEN, "bummer. tried to close tray but failed.\n"); 					/* Ignore the error from the low					level driver.  We don't care why it					couldn't close the tray.  We only care 					that there is no disc in the drive, 					since that is the _REAL_ problem here.*/					return -ENOMEDIUM;				}			} else {				cdinfo(CD_OPEN, "bummer. this driver can't close the tray.\n"); 				return -ENOMEDIUM;			}			/* Ok, the door should be closed now.. Check again */			ret = cdo->drive_status(cdi, CDSL_CURRENT);			if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) {				cdinfo(CD_OPEN, "bummer. the tray is still not closed.\n"); 				return -ENOMEDIUM;			}				if (ret!=CDS_DISC_OK) {				cdinfo(CD_OPEN, "bummer. disc isn't ready.\n"); 				return -EIO;			}				cdinfo(CD_OPEN, "the tray is now closed.\n"); 		}		}	cdrom_count_tracks(cdi, &tracks);	if (tracks.error) 		return(tracks.error);	if (tracks.audio==0)		return -EMEDIUMTYPE;	return 0;}/* Admittedly, the logic below could be performed in a nicer way. */staticint cdrom_release(struct inode *ip, struct file *fp){	kdev_t dev = ip->i_rdev;	struct cdrom_device_info *cdi = cdrom_find_device(dev);	struct cdrom_device_ops *cdo = cdi->ops;	int opened_for_data;	cdinfo(CD_CLOSE, "entering cdrom_release\n"); 	if (cdi->use_count > 0)		cdi->use_count--;	if (cdi->use_count == 0)		cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name);	if (cdi->use_count == 0 &&	    cdo->capability & CDC_LOCK && !keeplocked) {		cdinfo(CD_CLOSE, "Unlocking door!\n");		cdo->lock_door(cdi, 0);	}	opened_for_data = !(cdi->options & CDO_USE_FFLAGS) ||		!(fp && fp->f_flags & O_NONBLOCK);	cdo->release(cdi);	if (cdi->use_count == 0) {      /* last process that closes dev*/		if (opened_for_data &&		    cdi->options & CDO_AUTO_EJECT && CDROM_CAN(CDC_OPEN_TRAY))			cdo->tray_move(cdi, 1);	}	return 0;}static int cdrom_read_mech_status(struct cdrom_device_info *cdi, 				  struct cdrom_changer_info *buf){	struct cdrom_generic_command cgc;	struct cdrom_device_ops *cdo = cdi->ops;	int length;	/*	 * Sanyo changer isn't spec compliant (doesn't use regular change	 * LOAD_UNLOAD command, and it doesn't implement the mech status	 * command below	 */	if (cdi->sanyo_slot) {		buf->hdr.nslots = 3;		buf->hdr.curslot = cdi->sanyo_slot == 3 ? 0 : cdi->sanyo_slot;		for (length = 0; length < 3; length++) {			buf->slots[length].disc_present = 1;			buf->slots[length].change = 0;		}		return 0;	}	length = sizeof(struct cdrom_mechstat_header) +		 cdi->capacity * sizeof(struct cdrom_slot);	init_cdrom_command(&cgc, buf, length, CGC_DATA_READ);	cgc.cmd[0] = GPCMD_MECHANISM_STATUS;	cgc.cmd[8] = (length >> 8) & 0xff;	cgc.cmd[9] = length & 0xff;	return cdo->generic_packet(cdi, &cgc);}static int cdrom_slot_status(struct cdrom_device_info *cdi, int slot){	struct cdrom_changer_info info;	int ret;	cdinfo(CD_CHANGER, "entering cdrom_slot_status()\n"); 	if (cdi->sanyo_slot)		return CDS_NO_INFO;		if ((ret = cdrom_read_mech_status(cdi, &info)))		return ret;	if (info.slots[slot].disc_present)		return CDS_DISC_OK;	else		return CDS_NO_DISC;}/* Return the number of slots for an ATAPI/SCSI cdrom,  * return 1 if not a changer.  */int cdrom_number_of_slots(struct cdrom_device_info *cdi) {	int status;	int nslots = 1;	struct cdrom_changer_info info;	cdinfo(CD_CHANGER, "entering cdrom_number_of_slots()\n"); 	/* cdrom_read_mech_status requires a valid value for capacity: */	cdi->capacity = 0; 	if ((status = cdrom_read_mech_status(cdi, &info)) == 0)		nslots = info.hdr.nslots;	return nslots;}/* If SLOT < 0, unload the current slot.  Otherwise, try to load SLOT. */static int cdrom_load_unload(struct cdrom_device_info *cdi, int slot) {	struct cdrom_generic_command cgc;	cdinfo(CD_CHANGER, "entering cdrom_load_unload()\n"); 	if (cdi->sanyo_slot && slot < 0)		return 0;	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);	cgc.cmd[0] = GPCMD_LOAD_UNLOAD;	cgc.cmd[4] = 2 + (slot >= 0);	cgc.cmd[8] = slot;	/* The Sanyo 3 CD changer uses byte 7 of the 	GPCMD_TEST_UNIT_READY to command to switch CDs instead of	using the GPCMD_LOAD_UNLOAD opcode. */	if (cdi->sanyo_slot && -1 < slot) {		cgc.cmd[0] = GPCMD_TEST_UNIT_READY;		cgc.cmd[7] = slot;		cgc.cmd[4] = cgc.cmd[8] = 0;		cdi->sanyo_slot = slot ? slot : 3;	}	return cdi->ops->generic_packet(cdi, &cgc);}int cdrom_select_disc(struct cdrom_device_info *cdi, int slot){	struct cdrom_changer_info info;	int curslot;	int ret;	cdinfo(CD_CHANGER, "entering cdrom_select_disc()\n"); 	if (!CDROM_CAN(CDC_SELECT_DISC))		return -EDRIVE_CANT_DO_THIS;	(void) cdi->ops->media_changed(cdi, slot);	if (slot == CDSL_NONE) {		/* set media changed bits, on both queues */		cdi->mc_flags = 0x3;		return cdrom_load_unload(cdi, -1);	}	if ((ret = cdrom_read_mech_status(cdi, &info)))		return ret;	curslot = info.hdr.curslot;	if (cdi->use_count > 1 || keeplocked) {		if (slot == CDSL_CURRENT) {	    		return curslot;		} else {			return -EBUSY;		}	}	/* Specifying CDSL_CURRENT will attempt to load the currnet slot,	which is useful if it had been previously unloaded.	Whether it can or not, it returns the current slot. 	Similarly,  if slot happens to be the current one, we still	try and load it. */	if (slot == CDSL_CURRENT)		slot = curslot;	/* set media changed bits on both queues */	cdi->mc_flags = 0x3;	if ((ret = cdrom_load_unload(cdi, slot)))		return ret;	return slot;}/* We want to make media_changed accessible to the user through an * ioctl. The main problem now is that we must double-buffer the * low-level implementation, to assure that the VFS and the user both * see a medium change once. */staticint media_changed(struct cdrom_device_info *cdi, int queue){	unsigned int mask = (1 << (queue & 1));	int ret = !!(cdi->mc_flags & mask);	if (!CDROM_CAN(CDC_MEDIA_CHANGED))	    return ret;	/* changed since last call? */	if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {		cdi->mc_flags = 0x3;    /* set bit on both queues */		ret |= 1;	}	cdi->mc_flags &= ~mask;         /* clear bit */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -