ide-taskfile.c 16.9 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
5
 *					IBM Storage Technology Division
6
 *  Copyright (C) 2003-2004, 2007  Bartlomiej Zolnierkiewicz
Linus Torvalds's avatar
Linus Torvalds committed
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>
Linus Torvalds's avatar
Linus Torvalds committed
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>
Jens Axboe's avatar
Jens Axboe committed
21
#include <linux/scatterlist.h>
Linus Torvalds's avatar
Linus Torvalds committed
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
}

Linus Torvalds's avatar
Linus Torvalds committed
40
41
42
int taskfile_lib_get_identify (ide_drive_t *drive, u8 *buf)
{
	ide_task_t args;
43

Linus Torvalds's avatar
Linus Torvalds committed
44
	memset(&args, 0, sizeof(ide_task_t));
45
	args.tf.nsect = 0x01;
Linus Torvalds's avatar
Linus Torvalds committed
46
	if (drive->media == ide_disk)
47
		args.tf.command = ATA_CMD_ID_ATA;
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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

Linus Torvalds's avatar
Linus Torvalds committed
59
60
ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
{
61
	ide_hwif_t *hwif = drive->hwif;
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;
Linus Torvalds's avatar
Linus Torvalds committed
66

67
68
69
70
71
72
73
74
75
	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;
		}
	}

76
77
	if (task->ftf_flags & IDE_FTFLAG_FLAGGED)
		task->ftf_flags |= IDE_FTFLAG_SET_IN_FLAGS;
78

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
	}
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
103
		return ide_started;
104
	default:
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;
Linus Torvalds's avatar
Linus Torvalds committed
111
112
	}
}
113
EXPORT_SYMBOL_GPL(do_rw_taskfile);
Linus Torvalds's avatar
Linus Torvalds committed
114
115

/*
116
 * Handler for commands without a data phase
Linus Torvalds's avatar
Linus Torvalds committed
117
 */
118
static ide_startstop_t task_no_data_intr(ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
133
		udelay(10);
134
	};
Linus Torvalds's avatar
Linus Torvalds committed
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;
			}
		}
Linus Torvalds's avatar
Linus Torvalds committed
149
		return ide_error(drive, "task_no_data_intr", stat);
150
	}
151

152
	if (custom && tf->command == ATA_CMD_IDLEIMMEDIATE) {
153
154
155
156
157
158
159
		hwif->tp_ops->tf_read(drive, task);
		if (tf->lbal != 0xc4) {
			printk(KERN_ERR "%s: head unload failed!\n",
			       drive->name);
			ide_tf_dump(drive->name, tf);
		} else
			drive->dev_flags |= IDE_DFLAG_PARKED;
160
	} else if (custom && tf->command == ATA_CMD_SET_MULTI)
161
		drive->mult_count = drive->mult_req;
Linus Torvalds's avatar
Linus Torvalds committed
162

163
164
165
166
167
168
169
170
171
172
173
174
175
	if (custom == 0 || tf->command == ATA_CMD_IDLEIMMEDIATE) {
		struct request *rq = hwif->rq;
		u8 err = ide_read_error(drive);

		if (blk_pm_request(rq))
			ide_complete_pm_rq(drive, rq);
		else {
			if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE)
				ide_complete_task(drive, task, stat, err);
			ide_complete_rq(drive, err);
		}
	}

Linus Torvalds's avatar
Linus Torvalds committed
176
177
178
	return ide_stopped;
}

179
static u8 wait_drive_not_busy(ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
180
{
181
	ide_hwif_t *hwif = drive->hwif;
182
	int retries;
Linus Torvalds's avatar
Linus Torvalds committed
183
184
185
	u8 stat;

	/*
186
187
	 * 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.
Linus Torvalds's avatar
Linus Torvalds committed
188
	 */
189
	for (retries = 0; retries < 1000; retries++) {
190
		stat = hwif->tp_ops->read_status(hwif);
191

192
		if (stat & ATA_BUSY)
193
194
195
196
			udelay(10);
		else
			break;
	}
Linus Torvalds's avatar
Linus Torvalds committed
197

198
	if (stat & ATA_BUSY)
Linus Torvalds's avatar
Linus Torvalds committed
199
200
201
202
203
		printk(KERN_ERR "%s: drive still BUSY!\n", drive->name);

	return stat;
}

204
205
static void ide_pio_sector(ide_drive_t *drive, struct request *rq,
			   unsigned int write)
Linus Torvalds's avatar
Linus Torvalds committed
206
207
208
{
	ide_hwif_t *hwif = drive->hwif;
	struct scatterlist *sg = hwif->sg_table;
Jens Axboe's avatar
Jens Axboe committed
209
	struct scatterlist *cursg = hwif->cursg;
Linus Torvalds's avatar
Linus Torvalds committed
210
211
212
213
214
215
216
	struct page *page;
#ifdef CONFIG_HIGHMEM
	unsigned long flags;
#endif
	unsigned int offset;
	u8 *buf;

Jens Axboe's avatar
Jens Axboe committed
217
218
219
220
221
222
	cursg = hwif->cursg;
	if (!cursg) {
		cursg = sg;
		hwif->cursg = sg;
	}

223
	page = sg_page(cursg);
Jens Axboe's avatar
Jens Axboe committed
224
	offset = cursg->offset + hwif->cursg_ofs * SECTOR_SIZE;
Linus Torvalds's avatar
Linus Torvalds committed
225
226
227
228
229
230
231
232
233
234
235
236
237

	/* 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++;

Jens Axboe's avatar
Jens Axboe committed
238
239
	if ((hwif->cursg_ofs * SECTOR_SIZE) == cursg->length) {
		hwif->cursg = sg_next(hwif->cursg);
Linus Torvalds's avatar
Linus Torvalds committed
240
241
242
243
244
		hwif->cursg_ofs = 0;
	}

	/* do the actual data transfer */
	if (write)
245
		hwif->tp_ops->output_data(drive, rq, buf, SECTOR_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
246
	else
247
		hwif->tp_ops->input_data(drive, rq, buf, SECTOR_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
248
249
250
251
252
253
254

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

255
256
static void ide_pio_multi(ide_drive_t *drive, struct request *rq,
			  unsigned int write)
Linus Torvalds's avatar
Linus Torvalds committed
257
258
259
260
261
{
	unsigned int nsect;

	nsect = min_t(unsigned int, drive->hwif->nleft, drive->mult_count);
	while (nsect--)
262
		ide_pio_sector(drive, rq, write);
Linus Torvalds's avatar
Linus Torvalds committed
263
264
}

265
static void ide_pio_datablock(ide_drive_t *drive, struct request *rq,
Linus Torvalds's avatar
Linus Torvalds committed
266
267
				     unsigned int write)
{
268
269
	u8 saved_io_32bit = drive->io_32bit;

Linus Torvalds's avatar
Linus Torvalds committed
270
271
272
	if (rq->bio)	/* fs request */
		rq->errors = 0;

273
274
275
276
277
278
279
	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;
	}

280
281
	touch_softlockup_watchdog();

Linus Torvalds's avatar
Linus Torvalds committed
282
283
284
	switch (drive->hwif->data_phase) {
	case TASKFILE_MULTI_IN:
	case TASKFILE_MULTI_OUT:
285
		ide_pio_multi(drive, rq, write);
Linus Torvalds's avatar
Linus Torvalds committed
286
287
		break;
	default:
288
		ide_pio_sector(drive, rq, write);
Linus Torvalds's avatar
Linus Torvalds committed
289
290
		break;
	}
291
292

	drive->io_32bit = saved_io_32bit;
Linus Torvalds's avatar
Linus Torvalds committed
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
}

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) {
321
			struct ide_driver *drv;
Linus Torvalds's avatar
Linus Torvalds committed
322

323
			drv = *(struct ide_driver **)rq->rq_disk->private_data;
Linus Torvalds's avatar
Linus Torvalds committed
324
325
326
327
328
329
			drv->end_request(drive, 1, sectors);
		}
	}
	return ide_error(drive, s, stat);
}

Tejun Heo's avatar
Tejun Heo committed
330
void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat)
Linus Torvalds's avatar
Linus Torvalds committed
331
{
332
	if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
333
		ide_task_t *task = rq->special;
334
		u8 err = ide_read_error(drive);
Linus Torvalds's avatar
Linus Torvalds committed
335

336
337
338
		if (task)
			ide_complete_task(drive, task, stat, err);
		ide_complete_rq(drive, err);
Tejun Heo's avatar
Tejun Heo committed
339
		return;
Linus Torvalds's avatar
Linus Torvalds committed
340
341
	}

342
	if (rq->rq_disk) {
343
		struct ide_driver *drv;
344

345
		drv = *(struct ide_driver **)rq->rq_disk->private_data;;
346
		drv->end_request(drive, 1, rq->nr_sectors);
347
	} else
348
		ide_end_request(drive, 1, rq->nr_sectors);
Linus Torvalds's avatar
Linus Torvalds committed
349
350
}

351
352
353
354
355
356
357
358
359
/*
 * 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? */
360
	if (OK_STAT(stat, ATA_DRDY, ATA_BUSY)) {
361
362
363
364
365
366
367
368
369
		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;
}

Linus Torvalds's avatar
Linus Torvalds committed
370
371
372
/*
 * Handler for command with PIO data-in phase (Read/Read Multiple).
 */
373
static ide_startstop_t task_in_intr(ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
374
375
{
	ide_hwif_t *hwif = drive->hwif;
376
	struct request *rq = hwif->rq;
377
	u8 stat = hwif->tp_ops->read_status(hwif);
Linus Torvalds's avatar
Linus Torvalds committed
378

379
	/* Error? */
380
	if (stat & ATA_ERR)
381
		return task_error(drive, rq, __func__, stat);
382
383

	/* Didn't want any data? Odd. */
384
	if ((stat & ATA_DRQ) == 0)
385
		return task_in_unexpected(drive, rq, stat);
Linus Torvalds's avatar
Linus Torvalds committed
386
387
388

	ide_pio_datablock(drive, rq, 0);

389
	/* Are we done? Check status and finish transfer. */
Linus Torvalds's avatar
Linus Torvalds committed
390
391
	if (!hwif->nleft) {
		stat = wait_drive_not_busy(drive);
392
		if (!OK_STAT(stat, 0, BAD_STAT))
393
			return task_error(drive, rq, __func__, stat);
Linus Torvalds's avatar
Linus Torvalds committed
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
		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;
410
	struct request *rq = hwif->rq;
411
	u8 stat = hwif->tp_ops->read_status(hwif);
Linus Torvalds's avatar
Linus Torvalds committed
412
413

	if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat))
414
		return task_error(drive, rq, __func__, stat);
Linus Torvalds's avatar
Linus Torvalds committed
415
416

	/* Deal with unexpected ATA data phase. */
417
	if (((stat & ATA_DRQ) == 0) ^ !hwif->nleft)
418
		return task_error(drive, rq, __func__, stat);
Linus Torvalds's avatar
Linus Torvalds committed
419
420
421
422
423
424
425
426
427
428
429
430
431

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

432
static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq)
Linus Torvalds's avatar
Linus Torvalds committed
433
{
434
	ide_hwif_t *hwif = drive->hwif;
Linus Torvalds's avatar
Linus Torvalds committed
435
436
	ide_startstop_t startstop;

437
	if (ide_wait_stat(&startstop, drive, ATA_DRQ,
Linus Torvalds's avatar
Linus Torvalds committed
438
439
			  drive->bad_wstat, WAIT_DRQ)) {
		printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n",
440
441
			drive->name,
			hwif->data_phase == TASKFILE_MULTI_OUT ? "MULT" : "",
442
			(drive->dev_flags & IDE_DFLAG_LBA48) ? "_EXT" : "");
Linus Torvalds's avatar
Linus Torvalds committed
443
444
445
		return startstop;
	}

446
	if ((drive->dev_flags & IDE_DFLAG_UNMASK) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
447
448
449
450
451
452
453
454
		local_irq_disable();

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

	return ide_started;
}

455
int ide_raw_taskfile(ide_drive_t *drive, ide_task_t *task, u8 *buf, u16 nsect)
Linus Torvalds's avatar
Linus Torvalds committed
456
{
457
458
	struct request *rq;
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
459

460
461
462
	rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
	rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
	rq->buffer = buf;
Linus Torvalds's avatar
Linus Torvalds committed
463
464
465
466
467
468
469

	/*
	 * (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.
	 */
470
471
	rq->hard_nr_sectors = rq->nr_sectors = nsect;
	rq->hard_cur_sectors = rq->current_nr_sectors = nsect;
Linus Torvalds's avatar
Linus Torvalds committed
472

473
	if (task->tf_flags & IDE_TFLAG_WRITE)
474
		rq->cmd_flags |= REQ_RW;
Linus Torvalds's avatar
Linus Torvalds committed
475

476
477
	rq->special = task;
	task->rq = rq;
Linus Torvalds's avatar
Linus Torvalds committed
478

479
480
481
482
	error = blk_execute_rq(drive->queue, NULL, rq, 0);
	blk_put_request(rq);

	return error;
Linus Torvalds's avatar
Linus Torvalds committed
483
484
485
486
}

EXPORT_SYMBOL(ide_raw_taskfile);

487
488
int ide_no_data_taskfile(ide_drive_t *drive, ide_task_t *task)
{
489
	task->data_phase = TASKFILE_NO_DATA;
490

491
	return ide_raw_taskfile(drive, task, NULL, 0);
492
493
494
}
EXPORT_SYMBOL_GPL(ide_no_data_taskfile);

495
#ifdef CONFIG_IDE_TASK_IOCTL
Linus Torvalds's avatar
Linus Torvalds committed
496
497
498
499
500
501
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;
502
	u8 *data_buf		= NULL;
Linus Torvalds's avatar
Linus Torvalds committed
503
504
	int err			= 0;
	int tasksize		= sizeof(struct ide_task_request_s);
505
506
	unsigned int taskin	= 0;
	unsigned int taskout	= 0;
507
	u16 nsect		= 0;
Linus Torvalds's avatar
Linus Torvalds committed
508
509
510
511
	char __user *buf = (char __user *)arg;

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

512
	req_task = kzalloc(tasksize, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
513
514
515
516
517
518
	if (req_task == NULL) return -ENOMEM;
	if (copy_from_user(req_task, buf, tasksize)) {
		kfree(req_task);
		return -EFAULT;
	}

519
520
521
522
523
524
525
	taskout = req_task->out_size;
	taskin  = req_task->in_size;
	
	if (taskin > 65536 || taskout > 65536) {
		err = -EINVAL;
		goto abort;
	}
Linus Torvalds's avatar
Linus Torvalds committed
526
527
528

	if (taskout) {
		int outtotal = tasksize;
529
		outbuf = kzalloc(taskout, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
530
531
532
533
534
535
536
537
538
539
540
541
		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;
542
		inbuf = kzalloc(taskin, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
543
544
545
546
547
548
549
550
551
552
553
554
		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));

555
556
	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);
557
558

	args.data_phase = req_task->data_phase;
Linus Torvalds's avatar
Linus Torvalds committed
559

560
561
	args.tf_flags = IDE_TFLAG_IO_16BIT | IDE_TFLAG_DEVICE |
			IDE_TFLAG_IN_TF;
562
	if (drive->dev_flags & IDE_DFLAG_LBA48)
563
		args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_IN_HOB);
564

565
	if (req_task->out_flags.all) {
566
		args.ftf_flags |= IDE_FTFLAG_FLAGGED;
567
568

		if (req_task->out_flags.b.data)
569
			args.ftf_flags |= IDE_FTFLAG_OUT_DATA;
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589

		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;
590
591
592
593
	} else {
		args.tf_flags |= IDE_TFLAG_OUT_TF;
		if (args.tf_flags & IDE_TFLAG_LBA48)
			args.tf_flags |= IDE_TFLAG_OUT_HOB;
594
595
	}

596
	if (req_task->in_flags.b.data)
597
		args.ftf_flags |= IDE_FTFLAG_IN_DATA;
598

Linus Torvalds's avatar
Linus Torvalds committed
599
600
601
602
603
604
	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",
605
					drive->name, __func__);
Linus Torvalds's avatar
Linus Torvalds committed
606
607
608
609
610
				err = -EPERM;
				goto abort;
			}
			/* fall through */
		case TASKFILE_OUT:
611
612
613
614
615
			/* fall through */
		case TASKFILE_OUT_DMAQ:
		case TASKFILE_OUT_DMA:
			nsect = taskout / SECTOR_SIZE;
			data_buf = outbuf;
Linus Torvalds's avatar
Linus Torvalds committed
616
617
618
619
620
621
			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",
622
					drive->name, __func__);
Linus Torvalds's avatar
Linus Torvalds committed
623
624
625
626
627
				err = -EPERM;
				goto abort;
			}
			/* fall through */
		case TASKFILE_IN:
628
629
630
631
632
			/* fall through */
		case TASKFILE_IN_DMAQ:
		case TASKFILE_IN_DMA:
			nsect = taskin / SECTOR_SIZE;
			data_buf = inbuf;
Linus Torvalds's avatar
Linus Torvalds committed
633
634
635
636
637
638
639
640
			break;
		case TASKFILE_NO_DATA:
			break;
		default:
			err = -EFAULT;
			goto abort;
	}

641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
	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);

659
660
	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);
661

662
	if ((args.ftf_flags & IDE_FTFLAG_SET_IN_FLAGS) &&
663
664
	    req_task->in_flags.all == 0) {
		req_task->in_flags.all = IDE_TASKFILE_STD_IN_FLAGS;
665
		if (drive->dev_flags & IDE_DFLAG_LBA48)
666
667
			req_task->in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8);
	}
Linus Torvalds's avatar
Linus Torvalds committed
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688

	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);
689
690
	kfree(outbuf);
	kfree(inbuf);
Linus Torvalds's avatar
Linus Torvalds committed
691
692
693
694
695

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

	return err;
}
696
#endif