ide.c

来自「2410的硬盘块设备源码」· C语言 代码 · 共 2,013 行 · 第 1/4 页

C
2,013
字号
 */ int ide_read_setting (ide_drive_t *drive, ide_settings_t *setting){	int		val = -EINVAL;	unsigned long	flags;	if ((setting->rw & SETTING_READ)) {		spin_lock_irqsave(&ide_lock, flags);		switch(setting->data_type) {			case TYPE_BYTE:				val = *((u8 *) setting->data);				break;			case TYPE_SHORT:				val = *((u16 *) setting->data);				break;			case TYPE_INT:			case TYPE_INTA:				val = *((u32 *) setting->data);				break;		}		spin_unlock_irqrestore(&ide_lock, flags);	}	return val;}/** *	ide_spin_wait_hwgroup	-	wait for group *	@drive: drive in the group * *	Wait for an IDE device group to go non busy and then return *	holding the ide_lock which guards the hwgroup->busy status *	and right to use it. */int ide_spin_wait_hwgroup (ide_drive_t *drive){	ide_hwgroup_t *hwgroup = HWGROUP(drive);	unsigned long timeout = jiffies + (3 * HZ);	spin_lock_irq(&ide_lock);	while (hwgroup->busy) {		unsigned long lflags;		spin_unlock_irq(&ide_lock);		local_irq_set(lflags);		if (time_after(jiffies, timeout)) {			local_irq_restore(lflags);			printk(KERN_ERR "%s: channel busy\n", drive->name);			return -EBUSY;		}		local_irq_restore(lflags);		spin_lock_irq(&ide_lock);	}	return 0;}EXPORT_SYMBOL(ide_spin_wait_hwgroup);/** *	ide_write_setting	-	read an IDE setting *	@drive: drive to read from *	@setting: drive setting *	@val: value * *	Write a drive setting if it is possible. The caller *	must hold the ide_setting_sem when making this call. * *	BUGS: the data return and error are the same return value *	so an error -EINVAL and true return of the same value cannot *	be told apart * *	FIXME:  This should be changed to enqueue a special request *	to the driver to change settings, and then wait on a sema for completion. *	The current scheme of polling is kludgy, though safe enough. */int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val){	int i;	u32 *p;	if (!capable(CAP_SYS_ADMIN))		return -EACCES;	if (!(setting->rw & SETTING_WRITE))		return -EPERM;	if (val < setting->min || val > setting->max)		return -EINVAL;	if (setting->set)		return setting->set(drive, val);	if (ide_spin_wait_hwgroup(drive))		return -EBUSY;	switch (setting->data_type) {		case TYPE_BYTE:			*((u8 *) setting->data) = val;			break;		case TYPE_SHORT:			*((u16 *) setting->data) = val;			break;		case TYPE_INT:			*((u32 *) setting->data) = val;			break;		case TYPE_INTA:			p = (u32 *) setting->data;			for (i = 0; i < 1 << PARTN_BITS; i++, p++)				*p = val;			break;	}	spin_unlock_irq(&ide_lock);	return 0;}static int set_io_32bit(ide_drive_t *drive, int arg){	drive->io_32bit = arg;#ifdef CONFIG_BLK_DEV_DTC2278	if (HWIF(drive)->chipset == ide_dtc2278)		HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;#endif /* CONFIG_BLK_DEV_DTC2278 */	return 0;}static int set_using_dma (ide_drive_t *drive, int arg){#ifdef CONFIG_BLK_DEV_IDEDMA	if (!drive->id || !(drive->id->capability & 1))		return -EPERM;	if (HWIF(drive)->ide_dma_check == NULL)		return -EPERM;	if (arg) {		if (HWIF(drive)->ide_dma_check(drive)) return -EIO;		if (HWIF(drive)->ide_dma_on(drive)) return -EIO;	} else {		if (__ide_dma_off(drive))			return -EIO;	}	return 0;#else	return -EPERM;#endif}static int set_pio_mode (ide_drive_t *drive, int arg){	struct request rq;	if (!HWIF(drive)->tuneproc)		return -ENOSYS;	if (drive->special.b.set_tune)		return -EBUSY;	ide_init_drive_cmd(&rq);	drive->tune_req = (u8) arg;	drive->special.b.set_tune = 1;	(void) ide_do_drive_cmd(drive, &rq, ide_wait);	return 0;}static int set_xfer_rate (ide_drive_t *drive, int arg){	int err = ide_wait_cmd(drive,			WIN_SETFEATURES, (u8) arg,			SETFEATURES_XFER, 0, NULL);	if (!err && arg) {		ide_set_xfer_rate(drive, (u8) arg);		ide_driveid_update(drive);	}	return err;}/** *	ide_add_generic_settings	-	generic ide settings *	@drive: drive being configured * *	Add the generic parts of the system settings to the /proc files and *	ioctls for this IDE device. The caller must not be holding the *	ide_setting_sem. */void ide_add_generic_settings (ide_drive_t *drive){/* *			  drive		setting name		read/write access				read ioctl		write ioctl		data type	min	max				mul_factor	div_factor	data pointer			set function */	__ide_add_setting(drive,	"io_32bit",		drive->no_io_32bit ? SETTING_READ : SETTING_RW,	HDIO_GET_32BIT,		HDIO_SET_32BIT,		TYPE_BYTE,	0,	1 + (SUPPORT_VLB_SYNC << 1),	1,		1,		&drive->io_32bit,		set_io_32bit,	0);	__ide_add_setting(drive,	"keepsettings",		SETTING_RW,					HDIO_GET_KEEPSETTINGS,	HDIO_SET_KEEPSETTINGS,	TYPE_BYTE,	0,	1,				1,		1,		&drive->keep_settings,		NULL,		0);	__ide_add_setting(drive,	"nice1",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->nice1,			NULL,		0);	__ide_add_setting(drive,	"pio_mode",		SETTING_WRITE,					-1,			HDIO_SET_PIO_MODE,	TYPE_BYTE,	0,	255,				1,		1,		NULL,				set_pio_mode,	0);	__ide_add_setting(drive,	"unmaskirq",		drive->no_unmask ? SETTING_READ : SETTING_RW,	HDIO_GET_UNMASKINTR,	HDIO_SET_UNMASKINTR,	TYPE_BYTE,	0,	1,				1,		1,		&drive->unmask,			NULL,		0);	__ide_add_setting(drive,	"using_dma",		SETTING_RW,					HDIO_GET_DMA,		HDIO_SET_DMA,		TYPE_BYTE,	0,	1,				1,		1,		&drive->using_dma,		set_using_dma,	0);	__ide_add_setting(drive,	"init_speed",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	70,				1,		1,		&drive->init_speed,		NULL,		0);	__ide_add_setting(drive,	"current_speed",	SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	70,				1,		1,		&drive->current_speed,		set_xfer_rate,	0);	__ide_add_setting(drive,	"number",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	3,				1,		1,		&drive->dn,			NULL,		0);}/** *	system_bus_clock	-	clock guess * *	External version of the bus clock guess used by very old IDE drivers *	for things like VLB timings. Should not be used. */int system_bus_clock (void){	return((int) ((!system_bus_speed) ? ide_system_bus_speed() : system_bus_speed ));}EXPORT_SYMBOL(system_bus_clock);static int generic_ide_suspend(struct device *dev, pm_message_t state){	ide_drive_t *drive = dev->driver_data;	struct request rq;	struct request_pm_state rqpm;	ide_task_t args;	memset(&rq, 0, sizeof(rq));	memset(&rqpm, 0, sizeof(rqpm));	memset(&args, 0, sizeof(args));	rq.flags = REQ_PM_SUSPEND;	rq.special = &args;	rq.pm = &rqpm;	rqpm.pm_step = ide_pm_state_start_suspend;	rqpm.pm_state = state.event;	return ide_do_drive_cmd(drive, &rq, ide_wait);}static int generic_ide_resume(struct device *dev){	ide_drive_t *drive = dev->driver_data;	struct request rq;	struct request_pm_state rqpm;	ide_task_t args;	memset(&rq, 0, sizeof(rq));	memset(&rqpm, 0, sizeof(rqpm));	memset(&args, 0, sizeof(args));	rq.flags = REQ_PM_RESUME;	rq.special = &args;	rq.pm = &rqpm;	rqpm.pm_step = ide_pm_state_start_resume;	rqpm.pm_state = PM_EVENT_ON;	return ide_do_drive_cmd(drive, &rq, ide_head_wait);}int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device *bdev,			unsigned int cmd, unsigned long arg){	ide_settings_t *setting;	ide_driver_t *drv;	int err = 0;	void __user *p = (void __user *)arg;	down(&ide_setting_sem);	if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) {		if (cmd == setting->read_ioctl) {			err = ide_read_setting(drive, setting);			up(&ide_setting_sem);			return err >= 0 ? put_user(err, (long __user *)arg) : err;		} else {			if (bdev != bdev->bd_contains)				err = -EINVAL;			else				err = ide_write_setting(drive, setting, arg);			up(&ide_setting_sem);			return err;		}	}	up(&ide_setting_sem);	switch (cmd) {		case HDIO_GETGEO:		{			struct hd_geometry geom;			if (!p || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;			geom.heads = drive->bios_head;			geom.sectors = drive->bios_sect;			geom.cylinders = (u16)drive->bios_cyl; /* truncate */			geom.start = get_start_sect(bdev);			if (copy_to_user(p, &geom, sizeof(struct hd_geometry)))				return -EFAULT;			return 0;		}		case HDIO_OBSOLETE_IDENTITY:		case HDIO_GET_IDENTITY:			if (bdev != bdev->bd_contains)				return -EINVAL;			if (drive->id_read == 0)				return -ENOMSG;			if (copy_to_user(p, drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))				return -EFAULT;			return 0;		case HDIO_GET_NICE:			return put_user(drive->dsc_overlap	<<	IDE_NICE_DSC_OVERLAP	|					drive->atapi_overlap	<<	IDE_NICE_ATAPI_OVERLAP	|					drive->nice0		<< 	IDE_NICE_0		|					drive->nice1		<<	IDE_NICE_1		|					drive->nice2		<<	IDE_NICE_2,					(long __user *) arg);#ifdef CONFIG_IDE_TASK_IOCTL		case HDIO_DRIVE_TASKFILE:		        if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))				return -EACCES;			switch(drive->media) {				case ide_disk:					return ide_taskfile_ioctl(drive, cmd, arg);				default:					return -ENOMSG;			}#endif /* CONFIG_IDE_TASK_IOCTL */		case HDIO_DRIVE_CMD:			if (!capable(CAP_SYS_RAWIO))				return -EACCES;			return ide_cmd_ioctl(drive, cmd, arg);		case HDIO_DRIVE_TASK:			if (!capable(CAP_SYS_RAWIO))				return -EACCES;			return ide_task_ioctl(drive, cmd, arg);		case HDIO_SCAN_HWIF:		{			hw_regs_t hw;			int args[3];			if (!capable(CAP_SYS_RAWIO)) return -EACCES;			if (copy_from_user(args, p, 3 * sizeof(int)))				return -EFAULT;			memset(&hw, 0, sizeof(hw));			ide_init_hwif_ports(&hw, (unsigned long) args[0],					    (unsigned long) args[1], NULL);			hw.irq = args[2];			if (ide_register_hw(&hw, NULL) == -1)				return -EIO;			return 0;		}	        case HDIO_UNREGISTER_HWIF:			if (!capable(CAP_SYS_RAWIO)) return -EACCES;			/* (arg > MAX_HWIFS) checked in function */			ide_unregister(arg);			return 0;		case HDIO_SET_NICE:			if (!capable(CAP_SYS_ADMIN)) return -EACCES;			if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1))))				return -EPERM;			drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1;			drv = *(ide_driver_t **)bdev->bd_disk->private_data;			if (drive->dsc_overlap && !drv->supports_dsc_overlap) {				drive->dsc_overlap = 0;				return -EPERM;			}			drive->nice1 = (arg >> IDE_NICE_1) & 1;			return 0;		case HDIO_DRIVE_RESET:		{			unsigned long flags;			if (!capable(CAP_SYS_ADMIN)) return -EACCES;						/*			 *	Abort the current command on the			 *	group if there is one, taking			 *	care not to allow anything else			 *	to be queued and to die on the			 *	spot if we miss one somehow			 */			spin_lock_irqsave(&ide_lock, flags);			ide_abort(drive, "drive reset");			if(HWGROUP(drive)->handler)				BUG();							/* Ensure nothing gets queued after we			   drop the lock. Reset will clear the busy */		   			HWGROUP(drive)->busy = 1;			spin_unlock_irqrestore(&ide_lock, flags);			(void) ide_do_reset(drive);			return 0;		}		case CDROMEJECT:		case CDROMCLOSETRAY:			return scsi_cmd_ioctl(file, bdev->bd_disk, cmd, p);		case HDIO_GET_BUSSTATE:			if (!capable(CAP_SYS_ADMIN))				return -EACCES;			if (put_user(HWIF(drive)->bus_state, (long __user *)arg))				return -EFAULT;			return 0;		case HDIO_SET_BUSSTATE:			if (!capable(CAP_SYS_ADMIN))				return -EACCES;			if (HWIF(drive)->busproc)				return HWIF(drive)->busproc(drive, (int)arg);			return -EOPNOTSUPP;		default:			return -EINVAL;	}}EXPORT_SYMBOL(generic_ide_ioctl);/* * stridx() returns the offset of c within s, * or -1 if c is '\0' or not found within s. */static int __init stridx (const char *s, char c){	char *i = strchr(s, c);	return (i && c) ? i - s : -1;}/* * match_parm() does parsing for ide_setup(): * * 1. the first char of s must be '='. * 2. if the remainder matches one of the supplied keywords, *     the index (1 based) of the keyword is negated and returned. * 3. if the remainder is a series of no more than max_vals numbers *     separated by commas, the numbers are saved in vals[] and a *     count of how many were saved is returned.  Base10 is assumed, *     and base16 is allowed when prefixed with "0x". * 4. otherwise, zero is returned. */static int __init match_parm (char *s, const char *keywords[], int vals[], int max_vals){	static const char *decimal = "0123456789";	static const char *hex = "0123456789abcdef";	int i, n;	if (*s++ == '=') {		/*		 * Try matching against the supplied keywords,		 * and return -(index+1) if we match one		 */		if (keywords != NULL) {			for (i = 0; *keywords != NULL; ++i) {				if (!strcmp(s, *keywords++))					return -(i+1);			}		}		/*		 * Look for a series of no more than "max_vals"		 * numeric values separated by commas, in base10,		 * or base16 when prefixed with "0x".		 * Return a count of how many were found.		 */		for (n = 0; (i = stridx(decimal, *s)) >= 0;) {			vals[n] = i;			while ((i = stridx(decimal, *++s)) >= 0)				vals[n] = (vals[n] * 10) + i;			if (*s == 'x' && !vals[n]) {				while ((i = stridx(hex, *++s)) >= 0)					vals[n] = (vals[n] * 0x10) + i;			}			if (++n == max_vals)				break;			if (*s == ',' || *s == ';')				++s;		}		if (!*s)			return n;	}	return 0;	/* zero = nothing matched */}#ifdef CONFIG_BLK_DEV_ALI14XXstatic int __initdata probe_ali14xx;extern int ali14xx_init(void);#endif#ifdef CONFIG_BLK_DEV_UMC8672static int __initdata probe_umc8672;extern int umc8672_init(void);#endif#ifdef CONFIG_BLK_DEV_DTC2278static int __initdata probe_dtc2278;extern int dtc2278_init(void);#endif#ifdef CONFIG_BLK_DEV_HT6560Bstatic int __initdata probe_ht6560b;extern int ht6560b_init(void);#endif#ifdef CONFIG_BLK_DEV_QD65XXstatic int __initdata probe_qd65xx;extern int qd65xx_init(void);#endifstatic int __initdata is_chipset_set[MAX_HWIFS];/* * ide_setup() gets called VERY EARLY during initialization, * to handle kernel "command line" strings beginning with "hdx=" or "ide". * * Remember to update Documentation/ide.txt if you change something here.

⌨️ 快捷键说明

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