ide-taskfile.c 19.7 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 = WIN_IDENTIFY;
L
Linus Torvalds 已提交
48
	else
49
		args.tf.command = WIN_PIDENTIFY;
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
		if (handler == NULL)
			handler = task_no_data_intr;
102
		/* WIN_{SPECIFY,RESTORE,SETMULT} use custom handlers */
103 104 105 106 107 108 109 110 111
		if (task->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) {
			switch (tf->command) {
			case WIN_SPECIFY: handler = set_geometry_intr;	break;
			case WIN_RESTORE: handler = recal_intr;		break;
			case WIN_SETMULT: handler = set_multmode_intr;	break;
			}
		}
		ide_execute_command(drive, tf->command, handler,
				    WAIT_WORSTCASE, NULL);
L
Linus Torvalds 已提交
112
		return ide_started;
113
	default:
114
		if (drive->using_dma == 0 || dma_ops->dma_setup(drive))
115
			return ide_stopped;
116 117
		dma_ops->dma_exec_cmd(drive, tf->command);
		dma_ops->dma_start(drive);
118
		return ide_started;
L
Linus Torvalds 已提交
119 120
	}
}
121
EXPORT_SYMBOL_GPL(do_rw_taskfile);
L
Linus Torvalds 已提交
122 123 124 125

/*
 * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
 */
126
static ide_startstop_t set_multmode_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
127
{
128
	ide_hwif_t *hwif = drive->hwif;
129
	u8 stat = hwif->tp_ops->read_status(hwif);
L
Linus Torvalds 已提交
130

131
	if (OK_STAT(stat, READY_STAT, BAD_STAT))
L
Linus Torvalds 已提交
132
		drive->mult_count = drive->mult_req;
133
	else {
L
Linus Torvalds 已提交
134 135 136 137 138 139 140 141 142 143
		drive->mult_req = drive->mult_count = 0;
		drive->special.b.recalibrate = 1;
		(void) ide_dump_status(drive, "set_multmode", stat);
	}
	return ide_stopped;
}

/*
 * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
 */
144
static ide_startstop_t set_geometry_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
145
{
146
	ide_hwif_t *hwif = drive->hwif;
L
Linus Torvalds 已提交
147 148 149
	int retries = 5;
	u8 stat;

150 151 152 153
	while (1) {
		stat = hwif->tp_ops->read_status(hwif);
		if ((stat & BUSY_STAT) == 0 || retries-- == 0)
			break;
L
Linus Torvalds 已提交
154
		udelay(10);
155
	};
L
Linus Torvalds 已提交
156 157 158 159 160 161 162 163 164 165 166 167 168 169

	if (OK_STAT(stat, READY_STAT, BAD_STAT))
		return ide_stopped;

	if (stat & (ERR_STAT|DRQ_STAT))
		return ide_error(drive, "set_geometry_intr", stat);

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

/*
 * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
 */
170
static ide_startstop_t recal_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
171
{
172
	ide_hwif_t *hwif = drive->hwif;
173
	u8 stat = hwif->tp_ops->read_status(hwif);
L
Linus Torvalds 已提交
174

175
	if (!OK_STAT(stat, READY_STAT, BAD_STAT))
L
Linus Torvalds 已提交
176 177 178 179 180 181 182
		return ide_error(drive, "recal_intr", stat);
	return ide_stopped;
}

/*
 * Handler for commands without a data phase
 */
183
static ide_startstop_t task_no_data_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
184
{
185 186
	ide_hwif_t *hwif = drive->hwif;
	ide_task_t *args = hwif->hwgroup->rq->special;
L
Linus Torvalds 已提交
187 188
	u8 stat;

189
	local_irq_enable_in_hardirq();
190
	stat = hwif->tp_ops->read_status(hwif);
191 192

	if (!OK_STAT(stat, READY_STAT, BAD_STAT))
L
Linus Torvalds 已提交
193 194
		return ide_error(drive, "task_no_data_intr", stat);
		/* calls ide_end_drive_cmd */
195

L
Linus Torvalds 已提交
196
	if (args)
197
		ide_end_drive_cmd(drive, stat, ide_read_error(drive));
L
Linus Torvalds 已提交
198 199 200 201

	return ide_stopped;
}

202
static u8 wait_drive_not_busy(ide_drive_t *drive)
L
Linus Torvalds 已提交
203
{
204
	ide_hwif_t *hwif = drive->hwif;
205
	int retries;
L
Linus Torvalds 已提交
206 207 208
	u8 stat;

	/*
209 210
	 * 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 已提交
211
	 */
212
	for (retries = 0; retries < 1000; retries++) {
213
		stat = hwif->tp_ops->read_status(hwif);
214 215

		if (stat & BUSY_STAT)
216 217 218 219
			udelay(10);
		else
			break;
	}
L
Linus Torvalds 已提交
220

221
	if (stat & BUSY_STAT)
L
Linus Torvalds 已提交
222 223 224 225 226
		printk(KERN_ERR "%s: drive still BUSY!\n", drive->name);

	return stat;
}

227 228
static void ide_pio_sector(ide_drive_t *drive, struct request *rq,
			   unsigned int write)
L
Linus Torvalds 已提交
229 230 231
{
	ide_hwif_t *hwif = drive->hwif;
	struct scatterlist *sg = hwif->sg_table;
J
Jens Axboe 已提交
232
	struct scatterlist *cursg = hwif->cursg;
L
Linus Torvalds 已提交
233 234 235 236 237 238 239
	struct page *page;
#ifdef CONFIG_HIGHMEM
	unsigned long flags;
#endif
	unsigned int offset;
	u8 *buf;

J
Jens Axboe 已提交
240 241 242 243 244 245
	cursg = hwif->cursg;
	if (!cursg) {
		cursg = sg;
		hwif->cursg = sg;
	}

J
Jens Axboe 已提交
246
	page = sg_page(cursg);
J
Jens Axboe 已提交
247
	offset = cursg->offset + hwif->cursg_ofs * SECTOR_SIZE;
L
Linus Torvalds 已提交
248 249 250 251 252 253 254 255 256 257 258 259 260

	/* 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 已提交
261 262
	if ((hwif->cursg_ofs * SECTOR_SIZE) == cursg->length) {
		hwif->cursg = sg_next(hwif->cursg);
L
Linus Torvalds 已提交
263 264 265 266 267
		hwif->cursg_ofs = 0;
	}

	/* do the actual data transfer */
	if (write)
268
		hwif->tp_ops->output_data(drive, rq, buf, SECTOR_SIZE);
L
Linus Torvalds 已提交
269
	else
270
		hwif->tp_ops->input_data(drive, rq, buf, SECTOR_SIZE);
L
Linus Torvalds 已提交
271 272 273 274 275 276 277

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

278 279
static void ide_pio_multi(ide_drive_t *drive, struct request *rq,
			  unsigned int write)
L
Linus Torvalds 已提交
280 281 282 283 284
{
	unsigned int nsect;

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

288
static void ide_pio_datablock(ide_drive_t *drive, struct request *rq,
L
Linus Torvalds 已提交
289 290
				     unsigned int write)
{
291 292
	u8 saved_io_32bit = drive->io_32bit;

L
Linus Torvalds 已提交
293 294 295
	if (rq->bio)	/* fs request */
		rq->errors = 0;

296 297 298 299 300 301 302
	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;
	}

303 304
	touch_softlockup_watchdog();

L
Linus Torvalds 已提交
305 306 307
	switch (drive->hwif->data_phase) {
	case TASKFILE_MULTI_IN:
	case TASKFILE_MULTI_OUT:
308
		ide_pio_multi(drive, rq, write);
L
Linus Torvalds 已提交
309 310
		break;
	default:
311
		ide_pio_sector(drive, rq, write);
L
Linus Torvalds 已提交
312 313
		break;
	}
314 315

	drive->io_32bit = saved_io_32bit;
L
Linus Torvalds 已提交
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
}

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 已提交
353
void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat)
L
Linus Torvalds 已提交
354
{
355
	if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
356
		u8 err = ide_read_error(drive);
L
Linus Torvalds 已提交
357

T
Tejun Heo 已提交
358 359
		ide_end_drive_cmd(drive, stat, err);
		return;
L
Linus Torvalds 已提交
360 361
	}

362 363 364 365
	if (rq->rq_disk) {
		ide_driver_t *drv;

		drv = *(ide_driver_t **)rq->rq_disk->private_data;;
366
		drv->end_request(drive, 1, rq->nr_sectors);
367
	} else
368
		ide_end_request(drive, 1, rq->nr_sectors);
L
Linus Torvalds 已提交
369 370
}

371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
/*
 * 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? */
	if (OK_STAT(stat, READY_STAT, BUSY_STAT)) {
		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 已提交
390 391 392
/*
 * Handler for command with PIO data-in phase (Read/Read Multiple).
 */
393
static ide_startstop_t task_in_intr(ide_drive_t *drive)
L
Linus Torvalds 已提交
394 395
{
	ide_hwif_t *hwif = drive->hwif;
396
	struct request *rq = hwif->hwgroup->rq;
397
	u8 stat = hwif->tp_ops->read_status(hwif);
L
Linus Torvalds 已提交
398

399 400
	/* Error? */
	if (stat & ERR_STAT)
401
		return task_error(drive, rq, __func__, stat);
402 403 404 405

	/* Didn't want any data? Odd. */
	if (!(stat & DRQ_STAT))
		return task_in_unexpected(drive, rq, stat);
L
Linus Torvalds 已提交
406 407 408

	ide_pio_datablock(drive, rq, 0);

409
	/* Are we done? Check status and finish transfer. */
L
Linus Torvalds 已提交
410 411
	if (!hwif->nleft) {
		stat = wait_drive_not_busy(drive);
412
		if (!OK_STAT(stat, 0, BAD_STAT))
413
			return task_error(drive, rq, __func__, stat);
L
Linus Torvalds 已提交
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
		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;
431
	u8 stat = hwif->tp_ops->read_status(hwif);
L
Linus Torvalds 已提交
432 433

	if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat))
434
		return task_error(drive, rq, __func__, stat);
L
Linus Torvalds 已提交
435 436 437

	/* Deal with unexpected ATA data phase. */
	if (((stat & DRQ_STAT) == 0) ^ !hwif->nleft)
438
		return task_error(drive, rq, __func__, stat);
L
Linus Torvalds 已提交
439 440 441 442 443 444 445 446 447 448 449 450 451

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

452
static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq)
L
Linus Torvalds 已提交
453 454 455
{
	ide_startstop_t startstop;

456
	if (ide_wait_stat(&startstop, drive, DRQ_STAT,
L
Linus Torvalds 已提交
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
			  drive->bad_wstat, WAIT_DRQ)) {
		printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n",
				drive->name,
				drive->hwif->data_phase ? "MULT" : "",
				drive->addressing ? "_EXT" : "");
		return startstop;
	}

	if (!drive->unmask)
		local_irq_disable();

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

	return ide_started;
}

474
int ide_raw_taskfile(ide_drive_t *drive, ide_task_t *task, u8 *buf, u16 nsect)
L
Linus Torvalds 已提交
475
{
476 477
	struct request *rq;
	int error;
L
Linus Torvalds 已提交
478

479 480 481
	rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
	rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
	rq->buffer = buf;
L
Linus Torvalds 已提交
482 483 484 485 486 487 488

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

492
	if (task->tf_flags & IDE_TFLAG_WRITE)
493
		rq->cmd_flags |= REQ_RW;
L
Linus Torvalds 已提交
494

495 496
	rq->special = task;
	task->rq = rq;
L
Linus Torvalds 已提交
497

498 499 500 501
	error = blk_execute_rq(drive->queue, NULL, rq, 0);
	blk_put_request(rq);

	return error;
L
Linus Torvalds 已提交
502 503 504 505
}

EXPORT_SYMBOL(ide_raw_taskfile);

506 507
int ide_no_data_taskfile(ide_drive_t *drive, ide_task_t *task)
{
508
	task->data_phase = TASKFILE_NO_DATA;
509

510
	return ide_raw_taskfile(drive, task, NULL, 0);
511 512 513
}
EXPORT_SYMBOL_GPL(ide_no_data_taskfile);

514
#ifdef CONFIG_IDE_TASK_IOCTL
L
Linus Torvalds 已提交
515 516 517 518 519 520
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;
521
	u8 *data_buf		= NULL;
L
Linus Torvalds 已提交
522 523
	int err			= 0;
	int tasksize		= sizeof(struct ide_task_request_s);
524 525
	unsigned int taskin	= 0;
	unsigned int taskout	= 0;
526
	u16 nsect		= 0;
L
Linus Torvalds 已提交
527 528 529 530
	char __user *buf = (char __user *)arg;

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

531
	req_task = kzalloc(tasksize, GFP_KERNEL);
L
Linus Torvalds 已提交
532 533 534 535 536 537
	if (req_task == NULL) return -ENOMEM;
	if (copy_from_user(req_task, buf, tasksize)) {
		kfree(req_task);
		return -EFAULT;
	}

538 539 540 541 542 543 544
	taskout = req_task->out_size;
	taskin  = req_task->in_size;
	
	if (taskin > 65536 || taskout > 65536) {
		err = -EINVAL;
		goto abort;
	}
L
Linus Torvalds 已提交
545 546 547

	if (taskout) {
		int outtotal = tasksize;
548
		outbuf = kzalloc(taskout, GFP_KERNEL);
L
Linus Torvalds 已提交
549 550 551 552 553 554 555 556 557 558 559 560
		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;
561
		inbuf = kzalloc(taskin, GFP_KERNEL);
L
Linus Torvalds 已提交
562 563 564 565 566 567 568 569 570 571 572 573
		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));

574 575
	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);
576 577

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

579 580
	args.tf_flags = IDE_TFLAG_IO_16BIT | IDE_TFLAG_DEVICE |
			IDE_TFLAG_IN_TF;
581
	if (drive->addressing == 1)
582
		args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_IN_HOB);
583

584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
	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;
609 610 611 612
	} else {
		args.tf_flags |= IDE_TFLAG_OUT_TF;
		if (args.tf_flags & IDE_TFLAG_LBA48)
			args.tf_flags |= IDE_TFLAG_OUT_HOB;
613 614
	}

615 616 617
	if (req_task->in_flags.b.data)
		args.tf_flags |= IDE_TFLAG_IN_DATA;

L
Linus Torvalds 已提交
618 619 620 621 622 623
	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",
624
					drive->name, __func__);
L
Linus Torvalds 已提交
625 626 627 628 629
				err = -EPERM;
				goto abort;
			}
			/* fall through */
		case TASKFILE_OUT:
630 631 632 633 634
			/* fall through */
		case TASKFILE_OUT_DMAQ:
		case TASKFILE_OUT_DMA:
			nsect = taskout / SECTOR_SIZE;
			data_buf = outbuf;
L
Linus Torvalds 已提交
635 636 637 638 639 640
			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",
641
					drive->name, __func__);
L
Linus Torvalds 已提交
642 643 644 645 646
				err = -EPERM;
				goto abort;
			}
			/* fall through */
		case TASKFILE_IN:
647 648 649 650 651
			/* fall through */
		case TASKFILE_IN_DMAQ:
		case TASKFILE_IN_DMA:
			nsect = taskin / SECTOR_SIZE;
			data_buf = inbuf;
L
Linus Torvalds 已提交
652 653 654 655 656 657 658 659
			break;
		case TASKFILE_NO_DATA:
			break;
		default:
			err = -EFAULT;
			goto abort;
	}

660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
	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);

678 679
	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);
680 681 682 683 684 685 686

	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;
		if (drive->addressing == 1)
			req_task->in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8);
	}
L
Linus Torvalds 已提交
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707

	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);
708 709
	kfree(outbuf);
	kfree(inbuf);
L
Linus Torvalds 已提交
710 711 712 713 714

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

	return err;
}
715
#endif
L
Linus Torvalds 已提交
716 717 718

int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
{
719 720 721
	u8 *buf = NULL;
	int bufsize = 0, err = 0;
	u8 args[4], xfer_rate = 0;
L
Linus Torvalds 已提交
722
	ide_task_t tfargs;
723
	struct ide_taskfile *tf = &tfargs.tf;
724
	struct hd_driveid *id = drive->id;
L
Linus Torvalds 已提交
725 726

	if (NULL == (void *) arg) {
727
		struct request *rq;
728

729 730 731 732
		rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
		rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
		err = blk_execute_rq(drive->queue, NULL, rq, 0);
		blk_put_request(rq);
733

734
		return err;
L
Linus Torvalds 已提交
735 736 737 738 739 740
	}

	if (copy_from_user(args, (void __user *)arg, 4))
		return -EFAULT;

	memset(&tfargs, 0, sizeof(ide_task_t));
741
	tf->feature = args[2];
742 743 744 745 746 747 748 749 750 751 752
	if (args[0] == WIN_SMART) {
		tf->nsect = args[3];
		tf->lbal  = args[1];
		tf->lbam  = 0x4f;
		tf->lbah  = 0xc2;
		tfargs.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_IN_NSECT;
	} else {
		tf->nsect = args[1];
		tfargs.tf_flags = IDE_TFLAG_OUT_FEATURE |
				  IDE_TFLAG_OUT_NSECT | IDE_TFLAG_IN_NSECT;
	}
753
	tf->command = args[0];
754
	tfargs.data_phase = args[3] ? TASKFILE_IN : TASKFILE_NO_DATA;
L
Linus Torvalds 已提交
755 756

	if (args[3]) {
757 758 759 760
		tfargs.tf_flags |= IDE_TFLAG_IO_16BIT;
		bufsize = SECTOR_WORDS * 4 * args[3];
		buf = kzalloc(bufsize, GFP_KERNEL);
		if (buf == NULL)
L
Linus Torvalds 已提交
761 762
			return -ENOMEM;
	}
763

764 765 766 767
	if (tf->command == WIN_SETFEATURES &&
	    tf->feature == SETFEATURES_XFER &&
	    tf->nsect >= XFER_SW_DMA_0 &&
	    (id->dma_ultra || id->dma_mword || id->dma_1word)) {
L
Linus Torvalds 已提交
768
		xfer_rate = args[1];
769 770 771
		if (tf->nsect > XFER_UDMA_2 && !eighty_ninty_three(drive)) {
			printk(KERN_WARNING "%s: UDMA speeds >UDMA33 cannot "
					    "be set\n", drive->name);
L
Linus Torvalds 已提交
772
			goto abort;
773
		}
L
Linus Torvalds 已提交
774 775
	}

776 777 778 779 780
	err = ide_raw_taskfile(drive, &tfargs, buf, args[3]);

	args[0] = tf->status;
	args[1] = tf->error;
	args[2] = tf->nsect;
L
Linus Torvalds 已提交
781 782 783 784 785 786 787

	if (!err && xfer_rate) {
		/* active-retuning-calls future */
		ide_set_xfer_rate(drive, xfer_rate);
		ide_driveid_update(drive);
	}
abort:
788
	if (copy_to_user((void __user *)arg, &args, 4))
L
Linus Torvalds 已提交
789
		err = -EFAULT;
790 791 792 793 794
	if (buf) {
		if (copy_to_user((void __user *)(arg + 4), buf, bufsize))
			err = -EFAULT;
		kfree(buf);
	}
L
Linus Torvalds 已提交
795 796 797 798 799 800 801
	return err;
}

int ide_task_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
{
	void __user *p = (void __user *)arg;
	int err = 0;
802 803
	u8 args[7];
	ide_task_t task;
L
Linus Torvalds 已提交
804 805 806

	if (copy_from_user(args, p, 7))
		return -EFAULT;
807 808 809 810

	memset(&task, 0, sizeof(task));
	memcpy(&task.tf_array[7], &args[1], 6);
	task.tf.command = args[0];
811
	task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
812 813 814 815 816 817 818

	err = ide_no_data_taskfile(drive, &task);

	args[0] = task.tf.command;
	memcpy(&args[1], &task.tf_array[7], 6);

	if (copy_to_user(p, args, 7))
L
Linus Torvalds 已提交
819
		err = -EFAULT;
820

L
Linus Torvalds 已提交
821 822
	return err;
}