ide-taskfile.c 17.4 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 58
static ide_startstop_t set_geometry_intr(ide_drive_t *);
static ide_startstop_t recal_intr(ide_drive_t *);
static ide_startstop_t set_multmode_intr(ide_drive_t *);
59 60
static ide_startstop_t pre_task_out_intr(ide_drive_t *, struct request *);
static ide_startstop_t task_in_intr(ide_drive_t *);
61

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

70 71 72 73 74 75 76 77 78 79 80 81
	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;

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

89 90 91
	switch (task->data_phase) {
	case TASKFILE_MULTI_OUT:
	case TASKFILE_OUT:
92
		tp_ops->exec_command(hwif, tf->command);
93 94 95 96
		ndelay(400);	/* FIXME */
		return pre_task_out_intr(drive, task->rq);
	case TASKFILE_MULTI_IN:
	case TASKFILE_IN:
97
		handler = task_in_intr;
98
		/* fall-through */
99
	case TASKFILE_NO_DATA:
100 101 102 103
		if (handler == NULL)
			handler = task_no_data_intr;
		if (task->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) {
			switch (tf->command) {
104 105 106 107 108 109 110 111 112
			case ATA_CMD_INIT_DEV_PARAMS:
				handler = set_geometry_intr;
				break;
			case ATA_CMD_RESTORE:
				handler = recal_intr;
				break;
			case ATA_CMD_SET_MULTI:
				handler = set_multmode_intr;
				break;
113 114 115 116
			}
		}
		ide_execute_command(drive, tf->command, handler,
				    WAIT_WORSTCASE, NULL);
L
Linus Torvalds 已提交
117
		return ide_started;
118
	default:
B
Bartlomiej Zolnierkiewicz 已提交
119 120
		if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0 ||
		    dma_ops->dma_setup(drive))
121
			return ide_stopped;
122 123
		dma_ops->dma_exec_cmd(drive, tf->command);
		dma_ops->dma_start(drive);
124
		return ide_started;
L
Linus Torvalds 已提交
125 126
	}
}
127
EXPORT_SYMBOL_GPL(do_rw_taskfile);
L
Linus Torvalds 已提交
128 129

/*
130
 * set_multmode_intr() is invoked on completion of a ATA_CMD_SET_MULTI cmd.
L
Linus Torvalds 已提交
131
 */
132
static ide_startstop_t set_multmode_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
133
{
134
	ide_hwif_t *hwif = drive->hwif;
135 136 137 138
	u8 stat;

	local_irq_enable_in_hardirq();
	stat = hwif->tp_ops->read_status(hwif);
L
Linus Torvalds 已提交
139

140
	if (OK_STAT(stat, ATA_DRDY, BAD_STAT))
L
Linus Torvalds 已提交
141
		drive->mult_count = drive->mult_req;
142
	else {
L
Linus Torvalds 已提交
143 144 145 146 147 148 149 150
		drive->mult_req = drive->mult_count = 0;
		drive->special.b.recalibrate = 1;
		(void) ide_dump_status(drive, "set_multmode", stat);
	}
	return ide_stopped;
}

/*
151
 * set_geometry_intr() is invoked on completion of a ATA_CMD_INIT_DEV_PARAMS cmd.
L
Linus Torvalds 已提交
152
 */
153
static ide_startstop_t set_geometry_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
154
{
155
	ide_hwif_t *hwif = drive->hwif;
L
Linus Torvalds 已提交
156 157 158
	int retries = 5;
	u8 stat;

159 160
	local_irq_enable_in_hardirq();

161 162
	while (1) {
		stat = hwif->tp_ops->read_status(hwif);
163
		if ((stat & ATA_BUSY) == 0 || retries-- == 0)
164
			break;
L
Linus Torvalds 已提交
165
		udelay(10);
166
	};
L
Linus Torvalds 已提交
167

168
	if (OK_STAT(stat, ATA_DRDY, BAD_STAT))
L
Linus Torvalds 已提交
169 170
		return ide_stopped;

171
	if (stat & (ATA_ERR | ATA_DRQ))
L
Linus Torvalds 已提交
172 173 174 175 176 177 178
		return ide_error(drive, "set_geometry_intr", stat);

	ide_set_handler(drive, &set_geometry_intr, WAIT_WORSTCASE, NULL);
	return ide_started;
}

/*
179
 * recal_intr() is invoked on completion of a ATA_CMD_RESTORE (recalibrate) cmd.
L
Linus Torvalds 已提交
180
 */
181
static ide_startstop_t recal_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
182
{
183
	ide_hwif_t *hwif = drive->hwif;
184 185 186 187
	u8 stat;

	local_irq_enable_in_hardirq();
	stat = hwif->tp_ops->read_status(hwif);
L
Linus Torvalds 已提交
188

189
	if (!OK_STAT(stat, ATA_DRDY, BAD_STAT))
L
Linus Torvalds 已提交
190 191 192 193 194 195 196
		return ide_error(drive, "recal_intr", stat);
	return ide_stopped;
}

/*
 * Handler for commands without a data phase
 */
197
static ide_startstop_t task_no_data_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
198
{
199 200
	ide_hwif_t *hwif = drive->hwif;
	ide_task_t *args = hwif->hwgroup->rq->special;
L
Linus Torvalds 已提交
201 202
	u8 stat;

203
	local_irq_enable_in_hardirq();
204
	stat = hwif->tp_ops->read_status(hwif);
205

206
	if (!OK_STAT(stat, ATA_DRDY, BAD_STAT))
L
Linus Torvalds 已提交
207 208
		return ide_error(drive, "task_no_data_intr", stat);
		/* calls ide_end_drive_cmd */
209

L
Linus Torvalds 已提交
210
	if (args)
211
		ide_end_drive_cmd(drive, stat, ide_read_error(drive));
L
Linus Torvalds 已提交
212 213 214 215

	return ide_stopped;
}

216
static u8 wait_drive_not_busy(ide_drive_t *drive)
L
Linus Torvalds 已提交
217
{
218
	ide_hwif_t *hwif = drive->hwif;
219
	int retries;
L
Linus Torvalds 已提交
220 221 222
	u8 stat;

	/*
223 224
	 * 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 已提交
225
	 */
226
	for (retries = 0; retries < 1000; retries++) {
227
		stat = hwif->tp_ops->read_status(hwif);
228

229
		if (stat & ATA_BUSY)
230 231 232 233
			udelay(10);
		else
			break;
	}
L
Linus Torvalds 已提交
234

235
	if (stat & ATA_BUSY)
L
Linus Torvalds 已提交
236 237 238 239 240
		printk(KERN_ERR "%s: drive still BUSY!\n", drive->name);

	return stat;
}

241 242
static void ide_pio_sector(ide_drive_t *drive, struct request *rq,
			   unsigned int write)
L
Linus Torvalds 已提交
243 244 245
{
	ide_hwif_t *hwif = drive->hwif;
	struct scatterlist *sg = hwif->sg_table;
J
Jens Axboe 已提交
246
	struct scatterlist *cursg = hwif->cursg;
L
Linus Torvalds 已提交
247 248 249 250 251 252 253
	struct page *page;
#ifdef CONFIG_HIGHMEM
	unsigned long flags;
#endif
	unsigned int offset;
	u8 *buf;

J
Jens Axboe 已提交
254 255 256 257 258 259
	cursg = hwif->cursg;
	if (!cursg) {
		cursg = sg;
		hwif->cursg = sg;
	}

J
Jens Axboe 已提交
260
	page = sg_page(cursg);
J
Jens Axboe 已提交
261
	offset = cursg->offset + hwif->cursg_ofs * SECTOR_SIZE;
L
Linus Torvalds 已提交
262 263 264 265 266 267 268 269 270 271 272 273 274

	/* 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 已提交
275 276
	if ((hwif->cursg_ofs * SECTOR_SIZE) == cursg->length) {
		hwif->cursg = sg_next(hwif->cursg);
L
Linus Torvalds 已提交
277 278 279 280 281
		hwif->cursg_ofs = 0;
	}

	/* do the actual data transfer */
	if (write)
282
		hwif->tp_ops->output_data(drive, rq, buf, SECTOR_SIZE);
L
Linus Torvalds 已提交
283
	else
284
		hwif->tp_ops->input_data(drive, rq, buf, SECTOR_SIZE);
L
Linus Torvalds 已提交
285 286 287 288 289 290 291

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

292 293
static void ide_pio_multi(ide_drive_t *drive, struct request *rq,
			  unsigned int write)
L
Linus Torvalds 已提交
294 295 296 297 298
{
	unsigned int nsect;

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

302
static void ide_pio_datablock(ide_drive_t *drive, struct request *rq,
L
Linus Torvalds 已提交
303 304
				     unsigned int write)
{
305 306
	u8 saved_io_32bit = drive->io_32bit;

L
Linus Torvalds 已提交
307 308 309
	if (rq->bio)	/* fs request */
		rq->errors = 0;

310 311 312 313 314 315 316
	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;
	}

317 318
	touch_softlockup_watchdog();

L
Linus Torvalds 已提交
319 320 321
	switch (drive->hwif->data_phase) {
	case TASKFILE_MULTI_IN:
	case TASKFILE_MULTI_OUT:
322
		ide_pio_multi(drive, rq, write);
L
Linus Torvalds 已提交
323 324
		break;
	default:
325
		ide_pio_sector(drive, rq, write);
L
Linus Torvalds 已提交
326 327
		break;
	}
328 329

	drive->io_32bit = saved_io_32bit;
L
Linus Torvalds 已提交
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
}

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 已提交
367
void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat)
L
Linus Torvalds 已提交
368
{
369
	if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
370
		u8 err = ide_read_error(drive);
L
Linus Torvalds 已提交
371

T
Tejun Heo 已提交
372 373
		ide_end_drive_cmd(drive, stat, err);
		return;
L
Linus Torvalds 已提交
374 375
	}

376 377 378 379
	if (rq->rq_disk) {
		ide_driver_t *drv;

		drv = *(ide_driver_t **)rq->rq_disk->private_data;;
380
		drv->end_request(drive, 1, rq->nr_sectors);
381
	} else
382
		ide_end_request(drive, 1, rq->nr_sectors);
L
Linus Torvalds 已提交
383 384
}

385 386 387 388 389 390 391 392 393
/*
 * 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? */
394
	if (OK_STAT(stat, ATA_DRDY, ATA_BUSY)) {
395 396 397 398 399 400 401 402 403
		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 已提交
404 405 406
/*
 * Handler for command with PIO data-in phase (Read/Read Multiple).
 */
407
static ide_startstop_t task_in_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
408 409
{
	ide_hwif_t *hwif = drive->hwif;
410
	struct request *rq = hwif->hwgroup->rq;
411
	u8 stat = hwif->tp_ops->read_status(hwif);
L
Linus Torvalds 已提交
412

413
	/* Error? */
414
	if (stat & ATA_ERR)
415
		return task_error(drive, rq, __func__, stat);
416 417

	/* Didn't want any data? Odd. */
418
	if ((stat & ATA_DRQ) == 0)
419
		return task_in_unexpected(drive, rq, stat);
L
Linus Torvalds 已提交
420 421 422

	ide_pio_datablock(drive, rq, 0);

423
	/* Are we done? Check status and finish transfer. */
L
Linus Torvalds 已提交
424 425
	if (!hwif->nleft) {
		stat = wait_drive_not_busy(drive);
426
		if (!OK_STAT(stat, 0, BAD_STAT))
427
			return task_error(drive, rq, __func__, stat);
L
Linus Torvalds 已提交
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
		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;
445
	u8 stat = hwif->tp_ops->read_status(hwif);
L
Linus Torvalds 已提交
446 447

	if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat))
448
		return task_error(drive, rq, __func__, stat);
L
Linus Torvalds 已提交
449 450

	/* Deal with unexpected ATA data phase. */
451
	if (((stat & ATA_DRQ) == 0) ^ !hwif->nleft)
452
		return task_error(drive, rq, __func__, stat);
L
Linus Torvalds 已提交
453 454 455 456 457 458 459 460 461 462 463 464 465

	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;
}

466
static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq)
L
Linus Torvalds 已提交
467 468 469
{
	ide_startstop_t startstop;

470
	if (ide_wait_stat(&startstop, drive, ATA_DRQ,
L
Linus Torvalds 已提交
471 472
			  drive->bad_wstat, WAIT_DRQ)) {
		printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n",
B
Bartlomiej Zolnierkiewicz 已提交
473 474
			drive->name, drive->hwif->data_phase ? "MULT" : "",
			(drive->dev_flags & IDE_DFLAG_LBA48) ? "_EXT" : "");
L
Linus Torvalds 已提交
475 476 477
		return startstop;
	}

B
Bartlomiej Zolnierkiewicz 已提交
478
	if ((drive->dev_flags & IDE_DFLAG_UNMASK) == 0)
L
Linus Torvalds 已提交
479 480 481 482 483 484 485 486
		local_irq_disable();

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

	return ide_started;
}

487
int ide_raw_taskfile(ide_drive_t *drive, ide_task_t *task, u8 *buf, u16 nsect)
L
Linus Torvalds 已提交
488
{
489 490
	struct request *rq;
	int error;
L
Linus Torvalds 已提交
491

492 493 494
	rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
	rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
	rq->buffer = buf;
L
Linus Torvalds 已提交
495 496 497 498 499 500 501

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

505
	if (task->tf_flags & IDE_TFLAG_WRITE)
506
		rq->cmd_flags |= REQ_RW;
L
Linus Torvalds 已提交
507

508 509
	rq->special = task;
	task->rq = rq;
L
Linus Torvalds 已提交
510

511 512 513 514
	error = blk_execute_rq(drive->queue, NULL, rq, 0);
	blk_put_request(rq);

	return error;
L
Linus Torvalds 已提交
515 516 517 518
}

EXPORT_SYMBOL(ide_raw_taskfile);

519 520
int ide_no_data_taskfile(ide_drive_t *drive, ide_task_t *task)
{
521
	task->data_phase = TASKFILE_NO_DATA;
522

523
	return ide_raw_taskfile(drive, task, NULL, 0);
524 525 526
}
EXPORT_SYMBOL_GPL(ide_no_data_taskfile);

527
#ifdef CONFIG_IDE_TASK_IOCTL
L
Linus Torvalds 已提交
528 529 530 531 532 533
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;
534
	u8 *data_buf		= NULL;
L
Linus Torvalds 已提交
535 536
	int err			= 0;
	int tasksize		= sizeof(struct ide_task_request_s);
537 538
	unsigned int taskin	= 0;
	unsigned int taskout	= 0;
539
	u16 nsect		= 0;
L
Linus Torvalds 已提交
540 541 542 543
	char __user *buf = (char __user *)arg;

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

544
	req_task = kzalloc(tasksize, GFP_KERNEL);
L
Linus Torvalds 已提交
545 546 547 548 549 550
	if (req_task == NULL) return -ENOMEM;
	if (copy_from_user(req_task, buf, tasksize)) {
		kfree(req_task);
		return -EFAULT;
	}

551 552 553 554 555 556 557
	taskout = req_task->out_size;
	taskin  = req_task->in_size;
	
	if (taskin > 65536 || taskout > 65536) {
		err = -EINVAL;
		goto abort;
	}
L
Linus Torvalds 已提交
558 559 560

	if (taskout) {
		int outtotal = tasksize;
561
		outbuf = kzalloc(taskout, GFP_KERNEL);
L
Linus Torvalds 已提交
562 563 564 565 566 567 568 569 570 571 572 573
		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;
574
		inbuf = kzalloc(taskin, GFP_KERNEL);
L
Linus Torvalds 已提交
575 576 577 578 579 580 581 582 583 584 585 586
		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));

587 588
	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);
589 590

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

592 593
	args.tf_flags = IDE_TFLAG_IO_16BIT | IDE_TFLAG_DEVICE |
			IDE_TFLAG_IN_TF;
B
Bartlomiej Zolnierkiewicz 已提交
594
	if (drive->dev_flags & IDE_DFLAG_LBA48)
595
		args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_IN_HOB);
596

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
	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;
622 623 624 625
	} else {
		args.tf_flags |= IDE_TFLAG_OUT_TF;
		if (args.tf_flags & IDE_TFLAG_LBA48)
			args.tf_flags |= IDE_TFLAG_OUT_HOB;
626 627
	}

628 629 630
	if (req_task->in_flags.b.data)
		args.tf_flags |= IDE_TFLAG_IN_DATA;

L
Linus Torvalds 已提交
631 632 633 634 635 636
	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",
637
					drive->name, __func__);
L
Linus Torvalds 已提交
638 639 640 641 642
				err = -EPERM;
				goto abort;
			}
			/* fall through */
		case TASKFILE_OUT:
643 644 645 646 647
			/* fall through */
		case TASKFILE_OUT_DMAQ:
		case TASKFILE_OUT_DMA:
			nsect = taskout / SECTOR_SIZE;
			data_buf = outbuf;
L
Linus Torvalds 已提交
648 649 650 651 652 653
			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",
654
					drive->name, __func__);
L
Linus Torvalds 已提交
655 656 657 658 659
				err = -EPERM;
				goto abort;
			}
			/* fall through */
		case TASKFILE_IN:
660 661 662 663 664
			/* fall through */
		case TASKFILE_IN_DMAQ:
		case TASKFILE_IN_DMA:
			nsect = taskin / SECTOR_SIZE;
			data_buf = inbuf;
L
Linus Torvalds 已提交
665 666 667 668 669 670 671 672
			break;
		case TASKFILE_NO_DATA:
			break;
		default:
			err = -EFAULT;
			goto abort;
	}

673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
	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);

691 692
	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);
693 694 695 696

	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 已提交
697
		if (drive->dev_flags & IDE_DFLAG_LBA48)
698 699
			req_task->in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8);
	}
L
Linus Torvalds 已提交
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720

	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);
721 722
	kfree(outbuf);
	kfree(inbuf);
L
Linus Torvalds 已提交
723 724 725 726 727

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

	return err;
}
728
#endif