ide-taskfile.c 16.3 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2 3 4
 *  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
L
Linus Torvalds 已提交
5
 *					IBM Storage Technology Division
6
 *  Copyright (C) 2003-2004, 2007  Bartlomiej Zolnierkiewicz
L
Linus Torvalds 已提交
7 8 9 10 11 12 13
 *
 *  The big the bad and the ugly.
 */

#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
14
#include <linux/sched.h>
L
Linus Torvalds 已提交
15 16 17 18 19 20
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/hdreg.h>
#include <linux/ide.h>
J
Jens Axboe 已提交
21
#include <linux/scatterlist.h>
L
Linus Torvalds 已提交
22 23 24 25

#include <asm/uaccess.h>
#include <asm/io.h>

26
void ide_tf_dump(const char *s, struct ide_taskfile *tf)
27
{
28 29 30
#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",
31
		s, tf->feature, tf->nsect, tf->lbal,
32
		tf->lbam, tf->lbah, tf->device, tf->command);
33 34
	printk("%s: hob: nsect 0x%02x lbal 0x%02x "
		"lbam 0x%02x lbah 0x%02x\n",
35
		s, tf->hob_nsect, tf->hob_lbal,
36
		tf->hob_lbam, tf->hob_lbah);
37
#endif
38 39
}

L
Linus Torvalds 已提交
40 41 42
int taskfile_lib_get_identify (ide_drive_t *drive, u8 *buf)
{
	ide_task_t args;
43

L
Linus Torvalds 已提交
44
	memset(&args, 0, sizeof(ide_task_t));
45
	args.tf.nsect = 0x01;
L
Linus Torvalds 已提交
46
	if (drive->media == ide_disk)
47
		args.tf.command = ATA_CMD_ID_ATA;
L
Linus Torvalds 已提交
48
	else
49
		args.tf.command = ATA_CMD_ID_ATAPI;
50
	args.tf_flags	= IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
51 52
	args.data_phase	= TASKFILE_IN;
	return ide_raw_taskfile(drive, &args, buf, 1);
L
Linus Torvalds 已提交
53 54
}

55
static ide_startstop_t task_no_data_intr(ide_drive_t *);
56 57
static ide_startstop_t pre_task_out_intr(ide_drive_t *, struct request *);
static ide_startstop_t task_in_intr(ide_drive_t *);
58

L
Linus Torvalds 已提交
59 60 61
ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
{
	ide_hwif_t *hwif	= HWIF(drive);
62
	struct ide_taskfile *tf = &task->tf;
63
	ide_handler_t *handler = NULL;
64
	const struct ide_tp_ops *tp_ops = hwif->tp_ops;
65
	const struct ide_dma_ops *dma_ops = hwif->dma_ops;
L
Linus Torvalds 已提交
66

67 68 69 70 71 72 73 74 75 76 77 78
	if (task->data_phase == TASKFILE_MULTI_IN ||
	    task->data_phase == TASKFILE_MULTI_OUT) {
		if (!drive->mult_count) {
			printk(KERN_ERR "%s: multimode not set!\n",
					drive->name);
			return ide_stopped;
		}
	}

	if (task->tf_flags & IDE_TFLAG_FLAGGED)
		task->tf_flags |= IDE_TFLAG_FLAGGED_SET_IN_FLAGS;

79 80
	memcpy(&hwif->task, task, sizeof(*task));

81 82
	if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) {
		ide_tf_dump(drive->name, tf);
83
		tp_ops->set_irq(hwif, 1);
84
		SELECT_MASK(drive, 0);
85
		tp_ops->tf_load(drive, task);
86
	}
L
Linus Torvalds 已提交
87

88 89 90
	switch (task->data_phase) {
	case TASKFILE_MULTI_OUT:
	case TASKFILE_OUT:
91
		tp_ops->exec_command(hwif, tf->command);
92 93 94 95
		ndelay(400);	/* FIXME */
		return pre_task_out_intr(drive, task->rq);
	case TASKFILE_MULTI_IN:
	case TASKFILE_IN:
96
		handler = task_in_intr;
97
		/* fall-through */
98
	case TASKFILE_NO_DATA:
99 100 101 102
		if (handler == NULL)
			handler = task_no_data_intr;
		ide_execute_command(drive, tf->command, handler,
				    WAIT_WORSTCASE, NULL);
L
Linus Torvalds 已提交
103
		return ide_started;
104
	default:
B
Bartlomiej Zolnierkiewicz 已提交
105 106
		if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0 ||
		    dma_ops->dma_setup(drive))
107
			return ide_stopped;
108 109
		dma_ops->dma_exec_cmd(drive, tf->command);
		dma_ops->dma_start(drive);
110
		return ide_started;
L
Linus Torvalds 已提交
111 112
	}
}
113
EXPORT_SYMBOL_GPL(do_rw_taskfile);
L
Linus Torvalds 已提交
114 115

/*
116
 * Handler for commands without a data phase
L
Linus Torvalds 已提交
117
 */
118
static ide_startstop_t task_no_data_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
119
{
120
	ide_hwif_t *hwif = drive->hwif;
121 122 123 124
	ide_task_t *task = &hwif->task;
	struct ide_taskfile *tf = &task->tf;
	int custom = (task->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) ? 1 : 0;
	int retries = (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) ? 5 : 1;
L
Linus Torvalds 已提交
125 126
	u8 stat;

127 128
	local_irq_enable_in_hardirq();

129 130
	while (1) {
		stat = hwif->tp_ops->read_status(hwif);
131
		if ((stat & ATA_BUSY) == 0 || retries-- == 0)
132
			break;
L
Linus Torvalds 已提交
133
		udelay(10);
134
	};
L
Linus Torvalds 已提交
135

136 137 138 139 140 141 142 143 144 145 146 147 148
	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.b.recalibrate = 1;
			(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, NULL);
				return ide_started;
			}
		}
L
Linus Torvalds 已提交
149 150
		return ide_error(drive, "task_no_data_intr", stat);
		/* calls ide_end_drive_cmd */
151
	}
152

153
	if (!custom)
154
		ide_end_drive_cmd(drive, stat, ide_read_error(drive));
155 156
	else if (tf->command == ATA_CMD_SET_MULTI)
		drive->mult_count = drive->mult_req;
L
Linus Torvalds 已提交
157 158 159 160

	return ide_stopped;
}

161
static u8 wait_drive_not_busy(ide_drive_t *drive)
L
Linus Torvalds 已提交
162
{
163
	ide_hwif_t *hwif = drive->hwif;
164
	int retries;
L
Linus Torvalds 已提交
165 166 167
	u8 stat;

	/*
168 169
	 * Last sector was transfered, wait until device is ready.  This can
	 * take up to 6 ms on some ATAPI devices, so we will wait max 10 ms.
L
Linus Torvalds 已提交
170
	 */
171
	for (retries = 0; retries < 1000; retries++) {
172
		stat = hwif->tp_ops->read_status(hwif);
173

174
		if (stat & ATA_BUSY)
175 176 177 178
			udelay(10);
		else
			break;
	}
L
Linus Torvalds 已提交
179

180
	if (stat & ATA_BUSY)
L
Linus Torvalds 已提交
181 182 183 184 185
		printk(KERN_ERR "%s: drive still BUSY!\n", drive->name);

	return stat;
}

186 187
static void ide_pio_sector(ide_drive_t *drive, struct request *rq,
			   unsigned int write)
L
Linus Torvalds 已提交
188 189 190
{
	ide_hwif_t *hwif = drive->hwif;
	struct scatterlist *sg = hwif->sg_table;
J
Jens Axboe 已提交
191
	struct scatterlist *cursg = hwif->cursg;
L
Linus Torvalds 已提交
192 193 194 195 196 197 198
	struct page *page;
#ifdef CONFIG_HIGHMEM
	unsigned long flags;
#endif
	unsigned int offset;
	u8 *buf;

J
Jens Axboe 已提交
199 200 201 202 203 204
	cursg = hwif->cursg;
	if (!cursg) {
		cursg = sg;
		hwif->cursg = sg;
	}

J
Jens Axboe 已提交
205
	page = sg_page(cursg);
J
Jens Axboe 已提交
206
	offset = cursg->offset + hwif->cursg_ofs * SECTOR_SIZE;
L
Linus Torvalds 已提交
207 208 209 210 211 212 213 214 215 216 217 218 219

	/* get the current page and offset */
	page = nth_page(page, (offset >> PAGE_SHIFT));
	offset %= PAGE_SIZE;

#ifdef CONFIG_HIGHMEM
	local_irq_save(flags);
#endif
	buf = kmap_atomic(page, KM_BIO_SRC_IRQ) + offset;

	hwif->nleft--;
	hwif->cursg_ofs++;

J
Jens Axboe 已提交
220 221
	if ((hwif->cursg_ofs * SECTOR_SIZE) == cursg->length) {
		hwif->cursg = sg_next(hwif->cursg);
L
Linus Torvalds 已提交
222 223 224 225 226
		hwif->cursg_ofs = 0;
	}

	/* do the actual data transfer */
	if (write)
227
		hwif->tp_ops->output_data(drive, rq, buf, SECTOR_SIZE);
L
Linus Torvalds 已提交
228
	else
229
		hwif->tp_ops->input_data(drive, rq, buf, SECTOR_SIZE);
L
Linus Torvalds 已提交
230 231 232 233 234 235 236

	kunmap_atomic(buf, KM_BIO_SRC_IRQ);
#ifdef CONFIG_HIGHMEM
	local_irq_restore(flags);
#endif
}

237 238
static void ide_pio_multi(ide_drive_t *drive, struct request *rq,
			  unsigned int write)
L
Linus Torvalds 已提交
239 240 241 242 243
{
	unsigned int nsect;

	nsect = min_t(unsigned int, drive->hwif->nleft, drive->mult_count);
	while (nsect--)
244
		ide_pio_sector(drive, rq, write);
L
Linus Torvalds 已提交
245 246
}

247
static void ide_pio_datablock(ide_drive_t *drive, struct request *rq,
L
Linus Torvalds 已提交
248 249
				     unsigned int write)
{
250 251
	u8 saved_io_32bit = drive->io_32bit;

L
Linus Torvalds 已提交
252 253 254
	if (rq->bio)	/* fs request */
		rq->errors = 0;

255 256 257 258 259 260 261
	if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
		ide_task_t *task = rq->special;

		if (task->tf_flags & IDE_TFLAG_IO_16BIT)
			drive->io_32bit = 0;
	}

262 263
	touch_softlockup_watchdog();

L
Linus Torvalds 已提交
264 265 266
	switch (drive->hwif->data_phase) {
	case TASKFILE_MULTI_IN:
	case TASKFILE_MULTI_OUT:
267
		ide_pio_multi(drive, rq, write);
L
Linus Torvalds 已提交
268 269
		break;
	default:
270
		ide_pio_sector(drive, rq, write);
L
Linus Torvalds 已提交
271 272
		break;
	}
273 274

	drive->io_32bit = saved_io_32bit;
L
Linus Torvalds 已提交
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
}

static ide_startstop_t task_error(ide_drive_t *drive, struct request *rq,
				  const char *s, u8 stat)
{
	if (rq->bio) {
		ide_hwif_t *hwif = drive->hwif;
		int sectors = hwif->nsect - hwif->nleft;

		switch (hwif->data_phase) {
		case TASKFILE_IN:
			if (hwif->nleft)
				break;
			/* fall through */
		case TASKFILE_OUT:
			sectors--;
			break;
		case TASKFILE_MULTI_IN:
			if (hwif->nleft)
				break;
			/* fall through */
		case TASKFILE_MULTI_OUT:
			sectors -= drive->mult_count;
		default:
			break;
		}

		if (sectors > 0) {
			ide_driver_t *drv;

			drv = *(ide_driver_t **)rq->rq_disk->private_data;
			drv->end_request(drive, 1, sectors);
		}
	}
	return ide_error(drive, s, stat);
}

T
Tejun Heo 已提交
312
void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat)
L
Linus Torvalds 已提交
313
{
314
	if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
315
		u8 err = ide_read_error(drive);
L
Linus Torvalds 已提交
316

T
Tejun Heo 已提交
317 318
		ide_end_drive_cmd(drive, stat, err);
		return;
L
Linus Torvalds 已提交
319 320
	}

321 322 323 324
	if (rq->rq_disk) {
		ide_driver_t *drv;

		drv = *(ide_driver_t **)rq->rq_disk->private_data;;
325
		drv->end_request(drive, 1, rq->nr_sectors);
326
	} else
327
		ide_end_request(drive, 1, rq->nr_sectors);
L
Linus Torvalds 已提交
328 329
}

330 331 332 333 334 335 336 337 338
/*
 * We got an interrupt on a task_in case, but no errors and no DRQ.
 *
 * It might be a spurious irq (shared irq), but it might be a
 * command that had no output.
 */
static ide_startstop_t task_in_unexpected(ide_drive_t *drive, struct request *rq, u8 stat)
{
	/* Command all done? */
339
	if (OK_STAT(stat, ATA_DRDY, ATA_BUSY)) {
340 341 342 343 344 345 346 347 348
		task_end_request(drive, rq, stat);
		return ide_stopped;
	}

	/* Assume it was a spurious irq */
	ide_set_handler(drive, &task_in_intr, WAIT_WORSTCASE, NULL);
	return ide_started;
}

L
Linus Torvalds 已提交
349 350 351
/*
 * Handler for command with PIO data-in phase (Read/Read Multiple).
 */
352
static ide_startstop_t task_in_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
353 354
{
	ide_hwif_t *hwif = drive->hwif;
355
	struct request *rq = hwif->hwgroup->rq;
356
	u8 stat = hwif->tp_ops->read_status(hwif);
L
Linus Torvalds 已提交
357

358
	/* Error? */
359
	if (stat & ATA_ERR)
360
		return task_error(drive, rq, __func__, stat);
361 362

	/* Didn't want any data? Odd. */
363
	if ((stat & ATA_DRQ) == 0)
364
		return task_in_unexpected(drive, rq, stat);
L
Linus Torvalds 已提交
365 366 367

	ide_pio_datablock(drive, rq, 0);

368
	/* Are we done? Check status and finish transfer. */
L
Linus Torvalds 已提交
369 370
	if (!hwif->nleft) {
		stat = wait_drive_not_busy(drive);
371
		if (!OK_STAT(stat, 0, BAD_STAT))
372
			return task_error(drive, rq, __func__, stat);
L
Linus Torvalds 已提交
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
		task_end_request(drive, rq, stat);
		return ide_stopped;
	}

	/* Still data left to transfer. */
	ide_set_handler(drive, &task_in_intr, WAIT_WORSTCASE, NULL);

	return ide_started;
}

/*
 * Handler for command with PIO data-out phase (Write/Write Multiple).
 */
static ide_startstop_t task_out_intr (ide_drive_t *drive)
{
	ide_hwif_t *hwif = drive->hwif;
	struct request *rq = HWGROUP(drive)->rq;
390
	u8 stat = hwif->tp_ops->read_status(hwif);
L
Linus Torvalds 已提交
391 392

	if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat))
393
		return task_error(drive, rq, __func__, stat);
L
Linus Torvalds 已提交
394 395

	/* Deal with unexpected ATA data phase. */
396
	if (((stat & ATA_DRQ) == 0) ^ !hwif->nleft)
397
		return task_error(drive, rq, __func__, stat);
L
Linus Torvalds 已提交
398 399 400 401 402 403 404 405 406 407 408 409 410

	if (!hwif->nleft) {
		task_end_request(drive, rq, stat);
		return ide_stopped;
	}

	/* Still data left to transfer. */
	ide_pio_datablock(drive, rq, 1);
	ide_set_handler(drive, &task_out_intr, WAIT_WORSTCASE, NULL);

	return ide_started;
}

411
static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq)
L
Linus Torvalds 已提交
412 413 414
{
	ide_startstop_t startstop;

415
	if (ide_wait_stat(&startstop, drive, ATA_DRQ,
L
Linus Torvalds 已提交
416 417
			  drive->bad_wstat, WAIT_DRQ)) {
		printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n",
B
Bartlomiej Zolnierkiewicz 已提交
418 419
			drive->name, drive->hwif->data_phase ? "MULT" : "",
			(drive->dev_flags & IDE_DFLAG_LBA48) ? "_EXT" : "");
L
Linus Torvalds 已提交
420 421 422
		return startstop;
	}

B
Bartlomiej Zolnierkiewicz 已提交
423
	if ((drive->dev_flags & IDE_DFLAG_UNMASK) == 0)
L
Linus Torvalds 已提交
424 425 426 427 428 429 430 431
		local_irq_disable();

	ide_set_handler(drive, &task_out_intr, WAIT_WORSTCASE, NULL);
	ide_pio_datablock(drive, rq, 1);

	return ide_started;
}

432
int ide_raw_taskfile(ide_drive_t *drive, ide_task_t *task, u8 *buf, u16 nsect)
L
Linus Torvalds 已提交
433
{
434 435
	struct request *rq;
	int error;
L
Linus Torvalds 已提交
436

437 438 439
	rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
	rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
	rq->buffer = buf;
L
Linus Torvalds 已提交
440 441 442 443 444 445 446

	/*
	 * (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.
	 */
447 448
	rq->hard_nr_sectors = rq->nr_sectors = nsect;
	rq->hard_cur_sectors = rq->current_nr_sectors = nsect;
L
Linus Torvalds 已提交
449

450
	if (task->tf_flags & IDE_TFLAG_WRITE)
451
		rq->cmd_flags |= REQ_RW;
L
Linus Torvalds 已提交
452

453 454
	rq->special = task;
	task->rq = rq;
L
Linus Torvalds 已提交
455

456 457 458 459
	error = blk_execute_rq(drive->queue, NULL, rq, 0);
	blk_put_request(rq);

	return error;
L
Linus Torvalds 已提交
460 461 462 463
}

EXPORT_SYMBOL(ide_raw_taskfile);

464 465
int ide_no_data_taskfile(ide_drive_t *drive, ide_task_t *task)
{
466
	task->data_phase = TASKFILE_NO_DATA;
467

468
	return ide_raw_taskfile(drive, task, NULL, 0);
469 470 471
}
EXPORT_SYMBOL_GPL(ide_no_data_taskfile);

472
#ifdef CONFIG_IDE_TASK_IOCTL
L
Linus Torvalds 已提交
473 474 475 476 477 478
int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
{
	ide_task_request_t	*req_task;
	ide_task_t		args;
	u8 *outbuf		= NULL;
	u8 *inbuf		= NULL;
479
	u8 *data_buf		= NULL;
L
Linus Torvalds 已提交
480 481
	int err			= 0;
	int tasksize		= sizeof(struct ide_task_request_s);
482 483
	unsigned int taskin	= 0;
	unsigned int taskout	= 0;
484
	u16 nsect		= 0;
L
Linus Torvalds 已提交
485 486 487 488
	char __user *buf = (char __user *)arg;

//	printk("IDE Taskfile ...\n");

489
	req_task = kzalloc(tasksize, GFP_KERNEL);
L
Linus Torvalds 已提交
490 491 492 493 494 495
	if (req_task == NULL) return -ENOMEM;
	if (copy_from_user(req_task, buf, tasksize)) {
		kfree(req_task);
		return -EFAULT;
	}

496 497 498 499 500 501 502
	taskout = req_task->out_size;
	taskin  = req_task->in_size;
	
	if (taskin > 65536 || taskout > 65536) {
		err = -EINVAL;
		goto abort;
	}
L
Linus Torvalds 已提交
503 504 505

	if (taskout) {
		int outtotal = tasksize;
506
		outbuf = kzalloc(taskout, GFP_KERNEL);
L
Linus Torvalds 已提交
507 508 509 510 511 512 513 514 515 516 517 518
		if (outbuf == NULL) {
			err = -ENOMEM;
			goto abort;
		}
		if (copy_from_user(outbuf, buf + outtotal, taskout)) {
			err = -EFAULT;
			goto abort;
		}
	}

	if (taskin) {
		int intotal = tasksize + taskout;
519
		inbuf = kzalloc(taskin, GFP_KERNEL);
L
Linus Torvalds 已提交
520 521 522 523 524 525 526 527 528 529 530 531
		if (inbuf == NULL) {
			err = -ENOMEM;
			goto abort;
		}
		if (copy_from_user(inbuf, buf + intotal, taskin)) {
			err = -EFAULT;
			goto abort;
		}
	}

	memset(&args, 0, sizeof(ide_task_t));

532 533
	memcpy(&args.tf_array[0], req_task->hob_ports, HDIO_DRIVE_HOB_HDR_SIZE - 2);
	memcpy(&args.tf_array[6], req_task->io_ports, HDIO_DRIVE_TASK_HDR_SIZE);
534 535

	args.data_phase = req_task->data_phase;
L
Linus Torvalds 已提交
536

537 538
	args.tf_flags = IDE_TFLAG_IO_16BIT | IDE_TFLAG_DEVICE |
			IDE_TFLAG_IN_TF;
B
Bartlomiej Zolnierkiewicz 已提交
539
	if (drive->dev_flags & IDE_DFLAG_LBA48)
540
		args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_IN_HOB);
541

542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
	if (req_task->out_flags.all) {
		args.tf_flags |= IDE_TFLAG_FLAGGED;

		if (req_task->out_flags.b.data)
			args.tf_flags |= IDE_TFLAG_OUT_DATA;

		if (req_task->out_flags.b.nsector_hob)
			args.tf_flags |= IDE_TFLAG_OUT_HOB_NSECT;
		if (req_task->out_flags.b.sector_hob)
			args.tf_flags |= IDE_TFLAG_OUT_HOB_LBAL;
		if (req_task->out_flags.b.lcyl_hob)
			args.tf_flags |= IDE_TFLAG_OUT_HOB_LBAM;
		if (req_task->out_flags.b.hcyl_hob)
			args.tf_flags |= IDE_TFLAG_OUT_HOB_LBAH;

		if (req_task->out_flags.b.error_feature)
			args.tf_flags |= IDE_TFLAG_OUT_FEATURE;
		if (req_task->out_flags.b.nsector)
			args.tf_flags |= IDE_TFLAG_OUT_NSECT;
		if (req_task->out_flags.b.sector)
			args.tf_flags |= IDE_TFLAG_OUT_LBAL;
		if (req_task->out_flags.b.lcyl)
			args.tf_flags |= IDE_TFLAG_OUT_LBAM;
		if (req_task->out_flags.b.hcyl)
			args.tf_flags |= IDE_TFLAG_OUT_LBAH;
567 568 569 570
	} else {
		args.tf_flags |= IDE_TFLAG_OUT_TF;
		if (args.tf_flags & IDE_TFLAG_LBA48)
			args.tf_flags |= IDE_TFLAG_OUT_HOB;
571 572
	}

573 574 575
	if (req_task->in_flags.b.data)
		args.tf_flags |= IDE_TFLAG_IN_DATA;

L
Linus Torvalds 已提交
576 577 578 579 580 581
	switch(req_task->data_phase) {
		case TASKFILE_MULTI_OUT:
			if (!drive->mult_count) {
				/* (hs): give up if multcount is not set */
				printk(KERN_ERR "%s: %s Multimode Write " \
					"multcount is not set\n",
582
					drive->name, __func__);
L
Linus Torvalds 已提交
583 584 585 586 587
				err = -EPERM;
				goto abort;
			}
			/* fall through */
		case TASKFILE_OUT:
588 589 590 591 592
			/* fall through */
		case TASKFILE_OUT_DMAQ:
		case TASKFILE_OUT_DMA:
			nsect = taskout / SECTOR_SIZE;
			data_buf = outbuf;
L
Linus Torvalds 已提交
593 594 595 596 597 598
			break;
		case TASKFILE_MULTI_IN:
			if (!drive->mult_count) {
				/* (hs): give up if multcount is not set */
				printk(KERN_ERR "%s: %s Multimode Read failure " \
					"multcount is not set\n",
599
					drive->name, __func__);
L
Linus Torvalds 已提交
600 601 602 603 604
				err = -EPERM;
				goto abort;
			}
			/* fall through */
		case TASKFILE_IN:
605 606 607 608 609
			/* fall through */
		case TASKFILE_IN_DMAQ:
		case TASKFILE_IN_DMA:
			nsect = taskin / SECTOR_SIZE;
			data_buf = inbuf;
L
Linus Torvalds 已提交
610 611 612 613 614 615 616 617
			break;
		case TASKFILE_NO_DATA:
			break;
		default:
			err = -EFAULT;
			goto abort;
	}

618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
	if (req_task->req_cmd == IDE_DRIVE_TASK_NO_DATA)
		nsect = 0;
	else if (!nsect) {
		nsect = (args.tf.hob_nsect << 8) | args.tf.nsect;

		if (!nsect) {
			printk(KERN_ERR "%s: in/out command without data\n",
					drive->name);
			err = -EFAULT;
			goto abort;
		}
	}

	if (req_task->req_cmd == IDE_DRIVE_TASK_RAW_WRITE)
		args.tf_flags |= IDE_TFLAG_WRITE;

	err = ide_raw_taskfile(drive, &args, data_buf, nsect);

636 637
	memcpy(req_task->hob_ports, &args.tf_array[0], HDIO_DRIVE_HOB_HDR_SIZE - 2);
	memcpy(req_task->io_ports, &args.tf_array[6], HDIO_DRIVE_TASK_HDR_SIZE);
638 639 640 641

	if ((args.tf_flags & IDE_TFLAG_FLAGGED_SET_IN_FLAGS) &&
	    req_task->in_flags.all == 0) {
		req_task->in_flags.all = IDE_TASKFILE_STD_IN_FLAGS;
B
Bartlomiej Zolnierkiewicz 已提交
642
		if (drive->dev_flags & IDE_DFLAG_LBA48)
643 644
			req_task->in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8);
	}
L
Linus Torvalds 已提交
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665

	if (copy_to_user(buf, req_task, tasksize)) {
		err = -EFAULT;
		goto abort;
	}
	if (taskout) {
		int outtotal = tasksize;
		if (copy_to_user(buf + outtotal, outbuf, taskout)) {
			err = -EFAULT;
			goto abort;
		}
	}
	if (taskin) {
		int intotal = tasksize + taskout;
		if (copy_to_user(buf + intotal, inbuf, taskin)) {
			err = -EFAULT;
			goto abort;
		}
	}
abort:
	kfree(req_task);
666 667
	kfree(outbuf);
	kfree(inbuf);
L
Linus Torvalds 已提交
668 669 670 671 672

//	printk("IDE Taskfile ioctl ended. rc = %i\n", err);

	return err;
}
673
#endif