676 lines · 16.7 KB · C
RawDownload
/* *  Copyright (C) 2000-2002	   Michael Cornwell <cornwell@acm.org> *  Copyright (C) 2000-2002	   Andre Hedrick <andre@linux-ide.org> *  Copyright (C) 2001-2002	   Klaus Smolin *					IBM Storage Technology Division *  Copyright (C) 2003-2004, 2007  Bartlomiej Zolnierkiewicz * *  The big the bad and the ugly. */ #include <linux/types.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/export.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/hdreg.h>#include <linux/ide.h>#include <linux/nmi.h>#include <linux/scatterlist.h>#include <linux/uaccess.h> #include <asm/io.h> void ide_tf_readback(ide_drive_t *drive, struct ide_cmd *cmd){	ide_hwif_t *hwif = drive->hwif;	const struct ide_tp_ops *tp_ops = hwif->tp_ops; 	/* Be sure we're looking at the low order bytes */	tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); 	tp_ops->tf_read(drive, &cmd->tf, cmd->valid.in.tf); 	if (cmd->tf_flags & IDE_TFLAG_LBA48) {		tp_ops->write_devctl(hwif, ATA_HOB | ATA_DEVCTL_OBS); 		tp_ops->tf_read(drive, &cmd->hob, cmd->valid.in.hob);	}} void ide_tf_dump(const char *s, struct ide_cmd *cmd){#ifdef DEBUG	printk("%s: tf: feat 0x%02x nsect 0x%02x lbal 0x%02x "		"lbam 0x%02x lbah 0x%02x dev 0x%02x cmd 0x%02x\n",	       s, cmd->tf.feature, cmd->tf.nsect,	       cmd->tf.lbal, cmd->tf.lbam, cmd->tf.lbah,	       cmd->tf.device, cmd->tf.command);	printk("%s: hob: nsect 0x%02x lbal 0x%02x lbam 0x%02x lbah 0x%02x\n",	       s, cmd->hob.nsect, cmd->hob.lbal, cmd->hob.lbam, cmd->hob.lbah);#endif} int taskfile_lib_get_identify(ide_drive_t *drive, u8 *buf){	struct ide_cmd cmd; 	memset(&cmd, 0, sizeof(cmd));	cmd.tf.nsect = 0x01;	if (drive->media == ide_disk)		cmd.tf.command = ATA_CMD_ID_ATA;	else		cmd.tf.command = ATA_CMD_ID_ATAPI;	cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE;	cmd.valid.in.tf  = IDE_VALID_IN_TF  | IDE_VALID_DEVICE;	cmd.protocol = ATA_PROT_PIO; 	return ide_raw_taskfile(drive, &cmd, buf, 1);} static ide_startstop_t task_no_data_intr(ide_drive_t *);static ide_startstop_t pre_task_out_intr(ide_drive_t *, struct ide_cmd *);static ide_startstop_t task_pio_intr(ide_drive_t *); ide_startstop_t do_rw_taskfile(ide_drive_t *drive, struct ide_cmd *orig_cmd){	ide_hwif_t *hwif = drive->hwif;	struct ide_cmd *cmd = &hwif->cmd;	struct ide_taskfile *tf = &cmd->tf;	ide_handler_t *handler = NULL;	const struct ide_tp_ops *tp_ops = hwif->tp_ops;	const struct ide_dma_ops *dma_ops = hwif->dma_ops; 	if (orig_cmd->protocol == ATA_PROT_PIO &&	    (orig_cmd->tf_flags & IDE_TFLAG_MULTI_PIO) &&	    drive->mult_count == 0) {		pr_err("%s: multimode not set!\n", drive->name);		return ide_stopped;	} 	if (orig_cmd->ftf_flags & IDE_FTFLAG_FLAGGED)		orig_cmd->ftf_flags |= IDE_FTFLAG_SET_IN_FLAGS; 	memcpy(cmd, orig_cmd, sizeof(*cmd)); 	if ((cmd->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) {		ide_tf_dump(drive->name, cmd);		tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); 		if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) {			u8 data[2] = { cmd->tf.data, cmd->hob.data }; 			tp_ops->output_data(drive, cmd, data, 2);		} 		if (cmd->valid.out.tf & IDE_VALID_DEVICE) {			u8 HIHI = (cmd->tf_flags & IDE_TFLAG_LBA48) ?				  0xE0 : 0xEF; 			if (!(cmd->ftf_flags & IDE_FTFLAG_FLAGGED))				cmd->tf.device &= HIHI;			cmd->tf.device |= drive->select;		} 		tp_ops->tf_load(drive, &cmd->hob, cmd->valid.out.hob);		tp_ops->tf_load(drive, &cmd->tf,  cmd->valid.out.tf);	} 	switch (cmd->protocol) {	case ATA_PROT_PIO:		if (cmd->tf_flags & IDE_TFLAG_WRITE) {			tp_ops->exec_command(hwif, tf->command);			ndelay(400);	/* FIXME */			return pre_task_out_intr(drive, cmd);		}		handler = task_pio_intr;		/* fall-through */	case ATA_PROT_NODATA:		if (handler == NULL)			handler = task_no_data_intr;		ide_execute_command(drive, cmd, handler, WAIT_WORSTCASE);		return ide_started;	case ATA_PROT_DMA:		if (ide_dma_prepare(drive, cmd))			return ide_stopped;		hwif->expiry = dma_ops->dma_timer_expiry;		ide_execute_command(drive, cmd, ide_dma_intr, 2 * WAIT_CMD);		dma_ops->dma_start(drive);	default:		return ide_started;	}}EXPORT_SYMBOL_GPL(do_rw_taskfile); static ide_startstop_t task_no_data_intr(ide_drive_t *drive){	ide_hwif_t *hwif = drive->hwif;	struct ide_cmd *cmd = &hwif->cmd;	struct ide_taskfile *tf = &cmd->tf;	int custom = (cmd->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) ? 1 : 0;	int retries = (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) ? 5 : 1;	u8 stat; 	local_irq_enable_in_hardirq(); 	while (1) {		stat = hwif->tp_ops->read_status(hwif);		if ((stat & ATA_BUSY) == 0 || retries-- == 0)			break;		udelay(10);	}; 	if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) {		if (custom && tf->command == ATA_CMD_SET_MULTI) {			drive->mult_req = drive->mult_count = 0;			drive->special_flags |= IDE_SFLAG_RECALIBRATE;			(void)ide_dump_status(drive, __func__, stat);			return ide_stopped;		} else if (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) {			if ((stat & (ATA_ERR | ATA_DRQ)) == 0) {				ide_set_handler(drive, &task_no_data_intr,						WAIT_WORSTCASE);				return ide_started;			}		}		return ide_error(drive, "task_no_data_intr", stat);	} 	if (custom && tf->command == ATA_CMD_SET_MULTI)		drive->mult_count = drive->mult_req; 	if (custom == 0 || tf->command == ATA_CMD_IDLEIMMEDIATE ||	    tf->command == ATA_CMD_CHK_POWER) {		struct request *rq = hwif->rq; 		if (ata_pm_request(rq))			ide_complete_pm_rq(drive, rq);		else			ide_finish_cmd(drive, cmd, stat);	} 	return ide_stopped;} static u8 wait_drive_not_busy(ide_drive_t *drive){	ide_hwif_t *hwif = drive->hwif;	int retries;	u8 stat; 	/*	 * Last sector was transferred, wait until device is ready.  This can	 * take up to 6 ms on some ATAPI devices, so we will wait max 10 ms.	 */	for (retries = 0; retries < 1000; retries++) {		stat = hwif->tp_ops->read_status(hwif); 		if (stat & ATA_BUSY)			udelay(10);		else			break;	} 	if (stat & ATA_BUSY)		pr_err("%s: drive still BUSY!\n", drive->name); 	return stat;} void ide_pio_bytes(ide_drive_t *drive, struct ide_cmd *cmd,		   unsigned int write, unsigned int len){	ide_hwif_t *hwif = drive->hwif;	struct scatterlist *sg = hwif->sg_table;	struct scatterlist *cursg = cmd->cursg;	unsigned long uninitialized_var(flags);	struct page *page;	unsigned int offset;	u8 *buf; 	cursg = cmd->cursg;	if (cursg == NULL)		cursg = cmd->cursg = sg; 	while (len) {		unsigned nr_bytes = min(len, cursg->length - cmd->cursg_ofs);		int page_is_high; 		page = sg_page(cursg);		offset = cursg->offset + cmd->cursg_ofs; 		/* get the current page and offset */		page = nth_page(page, (offset >> PAGE_SHIFT));		offset %= PAGE_SIZE; 		nr_bytes = min_t(unsigned, nr_bytes, (PAGE_SIZE - offset)); 		page_is_high = PageHighMem(page);		if (page_is_high)			local_irq_save(flags); 		buf = kmap_atomic(page) + offset; 		cmd->nleft -= nr_bytes;		cmd->cursg_ofs += nr_bytes; 		if (cmd->cursg_ofs == cursg->length) {			cursg = cmd->cursg = sg_next(cmd->cursg);			cmd->cursg_ofs = 0;		} 		/* do the actual data transfer */		if (write)			hwif->tp_ops->output_data(drive, cmd, buf, nr_bytes);		else			hwif->tp_ops->input_data(drive, cmd, buf, nr_bytes); 		kunmap_atomic(buf); 		if (page_is_high)			local_irq_restore(flags); 		len -= nr_bytes;	}}EXPORT_SYMBOL_GPL(ide_pio_bytes); static void ide_pio_datablock(ide_drive_t *drive, struct ide_cmd *cmd,			      unsigned int write){	unsigned int nr_bytes; 	u8 saved_io_32bit = drive->io_32bit; 	if (cmd->tf_flags & IDE_TFLAG_FS)		scsi_req(cmd->rq)->result = 0; 	if (cmd->tf_flags & IDE_TFLAG_IO_16BIT)		drive->io_32bit = 0; 	touch_softlockup_watchdog(); 	if (cmd->tf_flags & IDE_TFLAG_MULTI_PIO)		nr_bytes = min_t(unsigned, cmd->nleft, drive->mult_count << 9);	else		nr_bytes = SECTOR_SIZE; 	ide_pio_bytes(drive, cmd, write, nr_bytes); 	drive->io_32bit = saved_io_32bit;} static void ide_error_cmd(ide_drive_t *drive, struct ide_cmd *cmd){	if (cmd->tf_flags & IDE_TFLAG_FS) {		int nr_bytes = cmd->nbytes - cmd->nleft; 		if (cmd->protocol == ATA_PROT_PIO &&		    ((cmd->tf_flags & IDE_TFLAG_WRITE) || cmd->nleft == 0)) {			if (cmd->tf_flags & IDE_TFLAG_MULTI_PIO)				nr_bytes -= drive->mult_count << 9;			else				nr_bytes -= SECTOR_SIZE;		} 		if (nr_bytes > 0)			ide_complete_rq(drive, BLK_STS_OK, nr_bytes);	}} void ide_finish_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 stat){	struct request *rq = drive->hwif->rq;	u8 err = ide_read_error(drive), nsect = cmd->tf.nsect;	u8 set_xfer = !!(cmd->tf_flags & IDE_TFLAG_SET_XFER); 	ide_complete_cmd(drive, cmd, stat, err);	scsi_req(rq)->result = err; 	if (err == 0 && set_xfer) {		ide_set_xfer_rate(drive, nsect);		ide_driveid_update(drive);	} 	ide_complete_rq(drive, err ? BLK_STS_IOERR : BLK_STS_OK, blk_rq_bytes(rq));} /* * Handler for command with PIO data phase. */static ide_startstop_t task_pio_intr(ide_drive_t *drive){	ide_hwif_t *hwif = drive->hwif;	struct ide_cmd *cmd = &drive->hwif->cmd;	u8 stat = hwif->tp_ops->read_status(hwif);	u8 write = !!(cmd->tf_flags & IDE_TFLAG_WRITE); 	if (write == 0) {		/* Error? */		if (stat & ATA_ERR)			goto out_err; 		/* Didn't want any data? Odd. */		if ((stat & ATA_DRQ) == 0) {			/* Command all done? */			if (OK_STAT(stat, ATA_DRDY, ATA_BUSY))				goto out_end; 			/* Assume it was a spurious irq */			goto out_wait;		}	} else {		if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat))			goto out_err; 		/* Deal with unexpected ATA data phase. */		if (((stat & ATA_DRQ) == 0) ^ (cmd->nleft == 0))			goto out_err;	} 	if (write && cmd->nleft == 0)		goto out_end; 	/* Still data left to transfer. */	ide_pio_datablock(drive, cmd, write); 	/* Are we done? Check status and finish transfer. */	if (write == 0 && cmd->nleft == 0) {		stat = wait_drive_not_busy(drive);		if (!OK_STAT(stat, 0, BAD_STAT))			goto out_err; 		goto out_end;	}out_wait:	/* Still data left to transfer. */	ide_set_handler(drive, &task_pio_intr, WAIT_WORSTCASE);	return ide_started;out_end:	if ((cmd->tf_flags & IDE_TFLAG_FS) == 0)		ide_finish_cmd(drive, cmd, stat);	else		ide_complete_rq(drive, BLK_STS_OK, blk_rq_sectors(cmd->rq) << 9);	return ide_stopped;out_err:	ide_error_cmd(drive, cmd);	return ide_error(drive, __func__, stat);} static ide_startstop_t pre_task_out_intr(ide_drive_t *drive,					 struct ide_cmd *cmd){	ide_startstop_t startstop; 	if (ide_wait_stat(&startstop, drive, ATA_DRQ,			  drive->bad_wstat, WAIT_DRQ)) {		pr_err("%s: no DRQ after issuing %sWRITE%s\n", drive->name,			(cmd->tf_flags & IDE_TFLAG_MULTI_PIO) ? "MULT" : "",			(drive->dev_flags & IDE_DFLAG_LBA48) ? "_EXT" : "");		return startstop;	} 	if ((drive->dev_flags & IDE_DFLAG_UNMASK) == 0)		local_irq_disable(); 	ide_set_handler(drive, &task_pio_intr, WAIT_WORSTCASE); 	ide_pio_datablock(drive, cmd, 1); 	return ide_started;} int ide_raw_taskfile(ide_drive_t *drive, struct ide_cmd *cmd, u8 *buf,		     u16 nsect){	struct request *rq;	int error; 	rq = blk_get_request(drive->queue,		(cmd->tf_flags & IDE_TFLAG_WRITE) ?			REQ_OP_DRV_OUT : REQ_OP_DRV_IN, __GFP_RECLAIM);	ide_req(rq)->type = ATA_PRIV_TASKFILE; 	/*	 * (ks) We transfer currently only whole sectors.	 * This is suffient for now.  But, it would be great,	 * if we would find a solution to transfer any size.	 * To support special commands like READ LONG.	 */	if (nsect) {		error = blk_rq_map_kern(drive->queue, rq, buf,					nsect * SECTOR_SIZE, __GFP_RECLAIM);		if (error)			goto put_req;	} 	rq->special = cmd;	cmd->rq = rq; 	blk_execute_rq(drive->queue, NULL, rq, 0);	error = scsi_req(rq)->result ? -EIO : 0;put_req:	blk_put_request(rq);	return error;}EXPORT_SYMBOL(ide_raw_taskfile); int ide_no_data_taskfile(ide_drive_t *drive, struct ide_cmd *cmd){	cmd->protocol = ATA_PROT_NODATA; 	return ide_raw_taskfile(drive, cmd, NULL, 0);}EXPORT_SYMBOL_GPL(ide_no_data_taskfile); #ifdef CONFIG_IDE_TASK_IOCTLint ide_taskfile_ioctl(ide_drive_t *drive, unsigned long arg){	ide_task_request_t	*req_task;	struct ide_cmd		cmd;	u8 *outbuf		= NULL;	u8 *inbuf		= NULL;	u8 *data_buf		= NULL;	int err			= 0;	int tasksize		= sizeof(struct ide_task_request_s);	unsigned int taskin	= 0;	unsigned int taskout	= 0;	u16 nsect		= 0;	char __user *buf = (char __user *)arg; 	req_task = memdup_user(buf, tasksize);	if (IS_ERR(req_task))		return PTR_ERR(req_task); 	taskout = req_task->out_size;	taskin  = req_task->in_size; 	if (taskin > 65536 || taskout > 65536) {		err = -EINVAL;		goto abort;	} 	if (taskout) {		int outtotal = tasksize;		outbuf = kzalloc(taskout, GFP_KERNEL);		if (outbuf == NULL) {

Lines 1–500 of 676.