18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2000-2002 Michael Cornwell <cornwell@acm.org> 48c2ecf20Sopenharmony_ci * Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org> 58c2ecf20Sopenharmony_ci * Copyright (C) 2001-2002 Klaus Smolin 68c2ecf20Sopenharmony_ci * IBM Storage Technology Division 78c2ecf20Sopenharmony_ci * Copyright (C) 2003-2004, 2007 Bartlomiej Zolnierkiewicz 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * The big the bad and the ugly. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/export.h> 168c2ecf20Sopenharmony_ci#include <linux/sched.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/errno.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 228c2ecf20Sopenharmony_ci#include <linux/ide.h> 238c2ecf20Sopenharmony_ci#include <linux/nmi.h> 248c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 258c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <asm/io.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_civoid ide_tf_readback(ide_drive_t *drive, struct ide_cmd *cmd) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 328c2ecf20Sopenharmony_ci const struct ide_tp_ops *tp_ops = hwif->tp_ops; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci /* Be sure we're looking at the low order bytes */ 358c2ecf20Sopenharmony_ci tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci tp_ops->tf_read(drive, &cmd->tf, cmd->valid.in.tf); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (cmd->tf_flags & IDE_TFLAG_LBA48) { 408c2ecf20Sopenharmony_ci tp_ops->write_devctl(hwif, ATA_HOB | ATA_DEVCTL_OBS); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci tp_ops->tf_read(drive, &cmd->hob, cmd->valid.in.hob); 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_civoid ide_tf_dump(const char *s, struct ide_cmd *cmd) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci#ifdef DEBUG 498c2ecf20Sopenharmony_ci printk("%s: tf: feat 0x%02x nsect 0x%02x lbal 0x%02x " 508c2ecf20Sopenharmony_ci "lbam 0x%02x lbah 0x%02x dev 0x%02x cmd 0x%02x\n", 518c2ecf20Sopenharmony_ci s, cmd->tf.feature, cmd->tf.nsect, 528c2ecf20Sopenharmony_ci cmd->tf.lbal, cmd->tf.lbam, cmd->tf.lbah, 538c2ecf20Sopenharmony_ci cmd->tf.device, cmd->tf.command); 548c2ecf20Sopenharmony_ci printk("%s: hob: nsect 0x%02x lbal 0x%02x lbam 0x%02x lbah 0x%02x\n", 558c2ecf20Sopenharmony_ci s, cmd->hob.nsect, cmd->hob.lbal, cmd->hob.lbam, cmd->hob.lbah); 568c2ecf20Sopenharmony_ci#endif 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ciint taskfile_lib_get_identify(ide_drive_t *drive, u8 *buf) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct ide_cmd cmd; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 648c2ecf20Sopenharmony_ci cmd.tf.nsect = 0x01; 658c2ecf20Sopenharmony_ci if (drive->media == ide_disk) 668c2ecf20Sopenharmony_ci cmd.tf.command = ATA_CMD_ID_ATA; 678c2ecf20Sopenharmony_ci else 688c2ecf20Sopenharmony_ci cmd.tf.command = ATA_CMD_ID_ATAPI; 698c2ecf20Sopenharmony_ci cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; 708c2ecf20Sopenharmony_ci cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; 718c2ecf20Sopenharmony_ci cmd.protocol = ATA_PROT_PIO; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return ide_raw_taskfile(drive, &cmd, buf, 1); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic ide_startstop_t task_no_data_intr(ide_drive_t *); 778c2ecf20Sopenharmony_cistatic ide_startstop_t pre_task_out_intr(ide_drive_t *, struct ide_cmd *); 788c2ecf20Sopenharmony_cistatic ide_startstop_t task_pio_intr(ide_drive_t *); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ciide_startstop_t do_rw_taskfile(ide_drive_t *drive, struct ide_cmd *orig_cmd) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 838c2ecf20Sopenharmony_ci struct ide_cmd *cmd = &hwif->cmd; 848c2ecf20Sopenharmony_ci struct ide_taskfile *tf = &cmd->tf; 858c2ecf20Sopenharmony_ci ide_handler_t *handler = NULL; 868c2ecf20Sopenharmony_ci const struct ide_tp_ops *tp_ops = hwif->tp_ops; 878c2ecf20Sopenharmony_ci const struct ide_dma_ops *dma_ops = hwif->dma_ops; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (orig_cmd->protocol == ATA_PROT_PIO && 908c2ecf20Sopenharmony_ci (orig_cmd->tf_flags & IDE_TFLAG_MULTI_PIO) && 918c2ecf20Sopenharmony_ci drive->mult_count == 0) { 928c2ecf20Sopenharmony_ci pr_err("%s: multimode not set!\n", drive->name); 938c2ecf20Sopenharmony_ci return ide_stopped; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (orig_cmd->ftf_flags & IDE_FTFLAG_FLAGGED) 978c2ecf20Sopenharmony_ci orig_cmd->ftf_flags |= IDE_FTFLAG_SET_IN_FLAGS; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci memcpy(cmd, orig_cmd, sizeof(*cmd)); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if ((cmd->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { 1028c2ecf20Sopenharmony_ci ide_tf_dump(drive->name, cmd); 1038c2ecf20Sopenharmony_ci tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { 1068c2ecf20Sopenharmony_ci u8 data[2] = { cmd->tf.data, cmd->hob.data }; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci tp_ops->output_data(drive, cmd, data, 2); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (cmd->valid.out.tf & IDE_VALID_DEVICE) { 1128c2ecf20Sopenharmony_ci u8 HIHI = (cmd->tf_flags & IDE_TFLAG_LBA48) ? 1138c2ecf20Sopenharmony_ci 0xE0 : 0xEF; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (!(cmd->ftf_flags & IDE_FTFLAG_FLAGGED)) 1168c2ecf20Sopenharmony_ci cmd->tf.device &= HIHI; 1178c2ecf20Sopenharmony_ci cmd->tf.device |= drive->select; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci tp_ops->tf_load(drive, &cmd->hob, cmd->valid.out.hob); 1218c2ecf20Sopenharmony_ci tp_ops->tf_load(drive, &cmd->tf, cmd->valid.out.tf); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci switch (cmd->protocol) { 1258c2ecf20Sopenharmony_ci case ATA_PROT_PIO: 1268c2ecf20Sopenharmony_ci if (cmd->tf_flags & IDE_TFLAG_WRITE) { 1278c2ecf20Sopenharmony_ci tp_ops->exec_command(hwif, tf->command); 1288c2ecf20Sopenharmony_ci ndelay(400); /* FIXME */ 1298c2ecf20Sopenharmony_ci return pre_task_out_intr(drive, cmd); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci handler = task_pio_intr; 1328c2ecf20Sopenharmony_ci fallthrough; 1338c2ecf20Sopenharmony_ci case ATA_PROT_NODATA: 1348c2ecf20Sopenharmony_ci if (handler == NULL) 1358c2ecf20Sopenharmony_ci handler = task_no_data_intr; 1368c2ecf20Sopenharmony_ci ide_execute_command(drive, cmd, handler, WAIT_WORSTCASE); 1378c2ecf20Sopenharmony_ci return ide_started; 1388c2ecf20Sopenharmony_ci case ATA_PROT_DMA: 1398c2ecf20Sopenharmony_ci if (ide_dma_prepare(drive, cmd)) 1408c2ecf20Sopenharmony_ci return ide_stopped; 1418c2ecf20Sopenharmony_ci hwif->expiry = dma_ops->dma_timer_expiry; 1428c2ecf20Sopenharmony_ci ide_execute_command(drive, cmd, ide_dma_intr, 2 * WAIT_CMD); 1438c2ecf20Sopenharmony_ci dma_ops->dma_start(drive); 1448c2ecf20Sopenharmony_ci fallthrough; 1458c2ecf20Sopenharmony_ci default: 1468c2ecf20Sopenharmony_ci return ide_started; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(do_rw_taskfile); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic ide_startstop_t task_no_data_intr(ide_drive_t *drive) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 1548c2ecf20Sopenharmony_ci struct ide_cmd *cmd = &hwif->cmd; 1558c2ecf20Sopenharmony_ci struct ide_taskfile *tf = &cmd->tf; 1568c2ecf20Sopenharmony_ci int custom = (cmd->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) ? 1 : 0; 1578c2ecf20Sopenharmony_ci int retries = (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) ? 5 : 1; 1588c2ecf20Sopenharmony_ci u8 stat; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci local_irq_enable_in_hardirq(); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci while (1) { 1638c2ecf20Sopenharmony_ci stat = hwif->tp_ops->read_status(hwif); 1648c2ecf20Sopenharmony_ci if ((stat & ATA_BUSY) == 0 || retries-- == 0) 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci udelay(10); 1678c2ecf20Sopenharmony_ci }; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) { 1708c2ecf20Sopenharmony_ci if (custom && tf->command == ATA_CMD_SET_MULTI) { 1718c2ecf20Sopenharmony_ci drive->mult_req = drive->mult_count = 0; 1728c2ecf20Sopenharmony_ci drive->special_flags |= IDE_SFLAG_RECALIBRATE; 1738c2ecf20Sopenharmony_ci (void)ide_dump_status(drive, __func__, stat); 1748c2ecf20Sopenharmony_ci return ide_stopped; 1758c2ecf20Sopenharmony_ci } else if (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) { 1768c2ecf20Sopenharmony_ci if ((stat & (ATA_ERR | ATA_DRQ)) == 0) { 1778c2ecf20Sopenharmony_ci ide_set_handler(drive, &task_no_data_intr, 1788c2ecf20Sopenharmony_ci WAIT_WORSTCASE); 1798c2ecf20Sopenharmony_ci return ide_started; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci return ide_error(drive, "task_no_data_intr", stat); 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (custom && tf->command == ATA_CMD_SET_MULTI) 1868c2ecf20Sopenharmony_ci drive->mult_count = drive->mult_req; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (custom == 0 || tf->command == ATA_CMD_IDLEIMMEDIATE || 1898c2ecf20Sopenharmony_ci tf->command == ATA_CMD_CHK_POWER) { 1908c2ecf20Sopenharmony_ci struct request *rq = hwif->rq; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (ata_pm_request(rq)) 1938c2ecf20Sopenharmony_ci ide_complete_pm_rq(drive, rq); 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci ide_finish_cmd(drive, cmd, stat); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return ide_stopped; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic u8 wait_drive_not_busy(ide_drive_t *drive) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 2048c2ecf20Sopenharmony_ci int retries; 2058c2ecf20Sopenharmony_ci u8 stat; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* 2088c2ecf20Sopenharmony_ci * Last sector was transferred, wait until device is ready. This can 2098c2ecf20Sopenharmony_ci * take up to 6 ms on some ATAPI devices, so we will wait max 10 ms. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci for (retries = 0; retries < 1000; retries++) { 2128c2ecf20Sopenharmony_ci stat = hwif->tp_ops->read_status(hwif); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (stat & ATA_BUSY) 2158c2ecf20Sopenharmony_ci udelay(10); 2168c2ecf20Sopenharmony_ci else 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (stat & ATA_BUSY) 2218c2ecf20Sopenharmony_ci pr_err("%s: drive still BUSY!\n", drive->name); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return stat; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_civoid ide_pio_bytes(ide_drive_t *drive, struct ide_cmd *cmd, 2278c2ecf20Sopenharmony_ci unsigned int write, unsigned int len) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 2308c2ecf20Sopenharmony_ci struct scatterlist *sg = hwif->sg_table; 2318c2ecf20Sopenharmony_ci struct scatterlist *cursg = cmd->cursg; 2328c2ecf20Sopenharmony_ci struct page *page; 2338c2ecf20Sopenharmony_ci unsigned int offset; 2348c2ecf20Sopenharmony_ci u8 *buf; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (cursg == NULL) 2378c2ecf20Sopenharmony_ci cursg = cmd->cursg = sg; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci while (len) { 2408c2ecf20Sopenharmony_ci unsigned nr_bytes = min(len, cursg->length - cmd->cursg_ofs); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci page = sg_page(cursg); 2438c2ecf20Sopenharmony_ci offset = cursg->offset + cmd->cursg_ofs; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* get the current page and offset */ 2468c2ecf20Sopenharmony_ci page = nth_page(page, (offset >> PAGE_SHIFT)); 2478c2ecf20Sopenharmony_ci offset %= PAGE_SIZE; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci nr_bytes = min_t(unsigned, nr_bytes, (PAGE_SIZE - offset)); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci buf = kmap_atomic(page) + offset; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci cmd->nleft -= nr_bytes; 2548c2ecf20Sopenharmony_ci cmd->cursg_ofs += nr_bytes; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (cmd->cursg_ofs == cursg->length) { 2578c2ecf20Sopenharmony_ci cursg = cmd->cursg = sg_next(cmd->cursg); 2588c2ecf20Sopenharmony_ci cmd->cursg_ofs = 0; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* do the actual data transfer */ 2628c2ecf20Sopenharmony_ci if (write) 2638c2ecf20Sopenharmony_ci hwif->tp_ops->output_data(drive, cmd, buf, nr_bytes); 2648c2ecf20Sopenharmony_ci else 2658c2ecf20Sopenharmony_ci hwif->tp_ops->input_data(drive, cmd, buf, nr_bytes); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci kunmap_atomic(buf); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci len -= nr_bytes; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_pio_bytes); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void ide_pio_datablock(ide_drive_t *drive, struct ide_cmd *cmd, 2758c2ecf20Sopenharmony_ci unsigned int write) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci unsigned int nr_bytes; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci u8 saved_io_32bit = drive->io_32bit; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (cmd->tf_flags & IDE_TFLAG_FS) 2828c2ecf20Sopenharmony_ci scsi_req(cmd->rq)->result = 0; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (cmd->tf_flags & IDE_TFLAG_IO_16BIT) 2858c2ecf20Sopenharmony_ci drive->io_32bit = 0; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci touch_softlockup_watchdog(); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (cmd->tf_flags & IDE_TFLAG_MULTI_PIO) 2908c2ecf20Sopenharmony_ci nr_bytes = min_t(unsigned, cmd->nleft, drive->mult_count << 9); 2918c2ecf20Sopenharmony_ci else 2928c2ecf20Sopenharmony_ci nr_bytes = SECTOR_SIZE; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ide_pio_bytes(drive, cmd, write, nr_bytes); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci drive->io_32bit = saved_io_32bit; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic void ide_error_cmd(ide_drive_t *drive, struct ide_cmd *cmd) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci if (cmd->tf_flags & IDE_TFLAG_FS) { 3028c2ecf20Sopenharmony_ci int nr_bytes = cmd->nbytes - cmd->nleft; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (cmd->protocol == ATA_PROT_PIO && 3058c2ecf20Sopenharmony_ci ((cmd->tf_flags & IDE_TFLAG_WRITE) || cmd->nleft == 0)) { 3068c2ecf20Sopenharmony_ci if (cmd->tf_flags & IDE_TFLAG_MULTI_PIO) 3078c2ecf20Sopenharmony_ci nr_bytes -= drive->mult_count << 9; 3088c2ecf20Sopenharmony_ci else 3098c2ecf20Sopenharmony_ci nr_bytes -= SECTOR_SIZE; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (nr_bytes > 0) 3138c2ecf20Sopenharmony_ci ide_complete_rq(drive, BLK_STS_OK, nr_bytes); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_civoid ide_finish_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 stat) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct request *rq = drive->hwif->rq; 3208c2ecf20Sopenharmony_ci u8 err = ide_read_error(drive), nsect = cmd->tf.nsect; 3218c2ecf20Sopenharmony_ci u8 set_xfer = !!(cmd->tf_flags & IDE_TFLAG_SET_XFER); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ide_complete_cmd(drive, cmd, stat, err); 3248c2ecf20Sopenharmony_ci scsi_req(rq)->result = err; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (err == 0 && set_xfer) { 3278c2ecf20Sopenharmony_ci ide_set_xfer_rate(drive, nsect); 3288c2ecf20Sopenharmony_ci ide_driveid_update(drive); 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci ide_complete_rq(drive, err ? BLK_STS_IOERR : BLK_STS_OK, blk_rq_bytes(rq)); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci/* 3358c2ecf20Sopenharmony_ci * Handler for command with PIO data phase. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_cistatic ide_startstop_t task_pio_intr(ide_drive_t *drive) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 3408c2ecf20Sopenharmony_ci struct ide_cmd *cmd = &drive->hwif->cmd; 3418c2ecf20Sopenharmony_ci u8 stat = hwif->tp_ops->read_status(hwif); 3428c2ecf20Sopenharmony_ci u8 write = !!(cmd->tf_flags & IDE_TFLAG_WRITE); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (write == 0) { 3458c2ecf20Sopenharmony_ci /* Error? */ 3468c2ecf20Sopenharmony_ci if (stat & ATA_ERR) 3478c2ecf20Sopenharmony_ci goto out_err; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Didn't want any data? Odd. */ 3508c2ecf20Sopenharmony_ci if ((stat & ATA_DRQ) == 0) { 3518c2ecf20Sopenharmony_ci /* Command all done? */ 3528c2ecf20Sopenharmony_ci if (OK_STAT(stat, ATA_DRDY, ATA_BUSY)) 3538c2ecf20Sopenharmony_ci goto out_end; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* Assume it was a spurious irq */ 3568c2ecf20Sopenharmony_ci goto out_wait; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci } else { 3598c2ecf20Sopenharmony_ci if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat)) 3608c2ecf20Sopenharmony_ci goto out_err; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* Deal with unexpected ATA data phase. */ 3638c2ecf20Sopenharmony_ci if (((stat & ATA_DRQ) == 0) ^ (cmd->nleft == 0)) 3648c2ecf20Sopenharmony_ci goto out_err; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (write && cmd->nleft == 0) 3688c2ecf20Sopenharmony_ci goto out_end; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* Still data left to transfer. */ 3718c2ecf20Sopenharmony_ci ide_pio_datablock(drive, cmd, write); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Are we done? Check status and finish transfer. */ 3748c2ecf20Sopenharmony_ci if (write == 0 && cmd->nleft == 0) { 3758c2ecf20Sopenharmony_ci stat = wait_drive_not_busy(drive); 3768c2ecf20Sopenharmony_ci if (!OK_STAT(stat, 0, BAD_STAT)) 3778c2ecf20Sopenharmony_ci goto out_err; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci goto out_end; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ciout_wait: 3828c2ecf20Sopenharmony_ci /* Still data left to transfer. */ 3838c2ecf20Sopenharmony_ci ide_set_handler(drive, &task_pio_intr, WAIT_WORSTCASE); 3848c2ecf20Sopenharmony_ci return ide_started; 3858c2ecf20Sopenharmony_ciout_end: 3868c2ecf20Sopenharmony_ci if ((cmd->tf_flags & IDE_TFLAG_FS) == 0) 3878c2ecf20Sopenharmony_ci ide_finish_cmd(drive, cmd, stat); 3888c2ecf20Sopenharmony_ci else 3898c2ecf20Sopenharmony_ci ide_complete_rq(drive, BLK_STS_OK, blk_rq_sectors(cmd->rq) << 9); 3908c2ecf20Sopenharmony_ci return ide_stopped; 3918c2ecf20Sopenharmony_ciout_err: 3928c2ecf20Sopenharmony_ci ide_error_cmd(drive, cmd); 3938c2ecf20Sopenharmony_ci return ide_error(drive, __func__, stat); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic ide_startstop_t pre_task_out_intr(ide_drive_t *drive, 3978c2ecf20Sopenharmony_ci struct ide_cmd *cmd) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci ide_startstop_t startstop; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (ide_wait_stat(&startstop, drive, ATA_DRQ, 4028c2ecf20Sopenharmony_ci drive->bad_wstat, WAIT_DRQ)) { 4038c2ecf20Sopenharmony_ci pr_err("%s: no DRQ after issuing %sWRITE%s\n", drive->name, 4048c2ecf20Sopenharmony_ci (cmd->tf_flags & IDE_TFLAG_MULTI_PIO) ? "MULT" : "", 4058c2ecf20Sopenharmony_ci (drive->dev_flags & IDE_DFLAG_LBA48) ? "_EXT" : ""); 4068c2ecf20Sopenharmony_ci return startstop; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (!force_irqthreads && (drive->dev_flags & IDE_DFLAG_UNMASK) == 0) 4108c2ecf20Sopenharmony_ci local_irq_disable(); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ide_set_handler(drive, &task_pio_intr, WAIT_WORSTCASE); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ide_pio_datablock(drive, cmd, 1); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return ide_started; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ciint ide_raw_taskfile(ide_drive_t *drive, struct ide_cmd *cmd, u8 *buf, 4208c2ecf20Sopenharmony_ci u16 nsect) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct request *rq; 4238c2ecf20Sopenharmony_ci int error; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci rq = blk_get_request(drive->queue, 4268c2ecf20Sopenharmony_ci (cmd->tf_flags & IDE_TFLAG_WRITE) ? 4278c2ecf20Sopenharmony_ci REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); 4288c2ecf20Sopenharmony_ci ide_req(rq)->type = ATA_PRIV_TASKFILE; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * (ks) We transfer currently only whole sectors. 4328c2ecf20Sopenharmony_ci * This is suffient for now. But, it would be great, 4338c2ecf20Sopenharmony_ci * if we would find a solution to transfer any size. 4348c2ecf20Sopenharmony_ci * To support special commands like READ LONG. 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_ci if (nsect) { 4378c2ecf20Sopenharmony_ci error = blk_rq_map_kern(drive->queue, rq, buf, 4388c2ecf20Sopenharmony_ci nsect * SECTOR_SIZE, GFP_NOIO); 4398c2ecf20Sopenharmony_ci if (error) 4408c2ecf20Sopenharmony_ci goto put_req; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci ide_req(rq)->special = cmd; 4448c2ecf20Sopenharmony_ci cmd->rq = rq; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci blk_execute_rq(drive->queue, NULL, rq, 0); 4478c2ecf20Sopenharmony_ci error = scsi_req(rq)->result ? -EIO : 0; 4488c2ecf20Sopenharmony_ciput_req: 4498c2ecf20Sopenharmony_ci blk_put_request(rq); 4508c2ecf20Sopenharmony_ci return error; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ide_raw_taskfile); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ciint ide_no_data_taskfile(ide_drive_t *drive, struct ide_cmd *cmd) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci cmd->protocol = ATA_PROT_NODATA; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return ide_raw_taskfile(drive, cmd, NULL, 0); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_no_data_taskfile); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci#ifdef CONFIG_IDE_TASK_IOCTL 4638c2ecf20Sopenharmony_ciint ide_taskfile_ioctl(ide_drive_t *drive, unsigned long arg) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci ide_task_request_t *req_task; 4668c2ecf20Sopenharmony_ci struct ide_cmd cmd; 4678c2ecf20Sopenharmony_ci u8 *outbuf = NULL; 4688c2ecf20Sopenharmony_ci u8 *inbuf = NULL; 4698c2ecf20Sopenharmony_ci u8 *data_buf = NULL; 4708c2ecf20Sopenharmony_ci int err = 0; 4718c2ecf20Sopenharmony_ci int tasksize = sizeof(struct ide_task_request_s); 4728c2ecf20Sopenharmony_ci unsigned int taskin = 0; 4738c2ecf20Sopenharmony_ci unsigned int taskout = 0; 4748c2ecf20Sopenharmony_ci u16 nsect = 0; 4758c2ecf20Sopenharmony_ci char __user *buf = (char __user *)arg; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci req_task = memdup_user(buf, tasksize); 4788c2ecf20Sopenharmony_ci if (IS_ERR(req_task)) 4798c2ecf20Sopenharmony_ci return PTR_ERR(req_task); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci taskout = req_task->out_size; 4828c2ecf20Sopenharmony_ci taskin = req_task->in_size; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (taskin > 65536 || taskout > 65536) { 4858c2ecf20Sopenharmony_ci err = -EINVAL; 4868c2ecf20Sopenharmony_ci goto abort; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (taskout) { 4908c2ecf20Sopenharmony_ci int outtotal = tasksize; 4918c2ecf20Sopenharmony_ci outbuf = kzalloc(taskout, GFP_KERNEL); 4928c2ecf20Sopenharmony_ci if (outbuf == NULL) { 4938c2ecf20Sopenharmony_ci err = -ENOMEM; 4948c2ecf20Sopenharmony_ci goto abort; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci if (copy_from_user(outbuf, buf + outtotal, taskout)) { 4978c2ecf20Sopenharmony_ci err = -EFAULT; 4988c2ecf20Sopenharmony_ci goto abort; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (taskin) { 5038c2ecf20Sopenharmony_ci int intotal = tasksize + taskout; 5048c2ecf20Sopenharmony_ci inbuf = kzalloc(taskin, GFP_KERNEL); 5058c2ecf20Sopenharmony_ci if (inbuf == NULL) { 5068c2ecf20Sopenharmony_ci err = -ENOMEM; 5078c2ecf20Sopenharmony_ci goto abort; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci if (copy_from_user(inbuf, buf + intotal, taskin)) { 5108c2ecf20Sopenharmony_ci err = -EFAULT; 5118c2ecf20Sopenharmony_ci goto abort; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci memcpy(&cmd.hob, req_task->hob_ports, HDIO_DRIVE_HOB_HDR_SIZE - 2); 5188c2ecf20Sopenharmony_ci memcpy(&cmd.tf, req_task->io_ports, HDIO_DRIVE_TASK_HDR_SIZE); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci cmd.valid.out.tf = IDE_VALID_DEVICE; 5218c2ecf20Sopenharmony_ci cmd.valid.in.tf = IDE_VALID_DEVICE | IDE_VALID_IN_TF; 5228c2ecf20Sopenharmony_ci cmd.tf_flags = IDE_TFLAG_IO_16BIT; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (drive->dev_flags & IDE_DFLAG_LBA48) { 5258c2ecf20Sopenharmony_ci cmd.tf_flags |= IDE_TFLAG_LBA48; 5268c2ecf20Sopenharmony_ci cmd.valid.in.hob = IDE_VALID_IN_HOB; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (req_task->out_flags.all) { 5308c2ecf20Sopenharmony_ci cmd.ftf_flags |= IDE_FTFLAG_FLAGGED; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (req_task->out_flags.b.data) 5338c2ecf20Sopenharmony_ci cmd.ftf_flags |= IDE_FTFLAG_OUT_DATA; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (req_task->out_flags.b.nsector_hob) 5368c2ecf20Sopenharmony_ci cmd.valid.out.hob |= IDE_VALID_NSECT; 5378c2ecf20Sopenharmony_ci if (req_task->out_flags.b.sector_hob) 5388c2ecf20Sopenharmony_ci cmd.valid.out.hob |= IDE_VALID_LBAL; 5398c2ecf20Sopenharmony_ci if (req_task->out_flags.b.lcyl_hob) 5408c2ecf20Sopenharmony_ci cmd.valid.out.hob |= IDE_VALID_LBAM; 5418c2ecf20Sopenharmony_ci if (req_task->out_flags.b.hcyl_hob) 5428c2ecf20Sopenharmony_ci cmd.valid.out.hob |= IDE_VALID_LBAH; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (req_task->out_flags.b.error_feature) 5458c2ecf20Sopenharmony_ci cmd.valid.out.tf |= IDE_VALID_FEATURE; 5468c2ecf20Sopenharmony_ci if (req_task->out_flags.b.nsector) 5478c2ecf20Sopenharmony_ci cmd.valid.out.tf |= IDE_VALID_NSECT; 5488c2ecf20Sopenharmony_ci if (req_task->out_flags.b.sector) 5498c2ecf20Sopenharmony_ci cmd.valid.out.tf |= IDE_VALID_LBAL; 5508c2ecf20Sopenharmony_ci if (req_task->out_flags.b.lcyl) 5518c2ecf20Sopenharmony_ci cmd.valid.out.tf |= IDE_VALID_LBAM; 5528c2ecf20Sopenharmony_ci if (req_task->out_flags.b.hcyl) 5538c2ecf20Sopenharmony_ci cmd.valid.out.tf |= IDE_VALID_LBAH; 5548c2ecf20Sopenharmony_ci } else { 5558c2ecf20Sopenharmony_ci cmd.valid.out.tf |= IDE_VALID_OUT_TF; 5568c2ecf20Sopenharmony_ci if (cmd.tf_flags & IDE_TFLAG_LBA48) 5578c2ecf20Sopenharmony_ci cmd.valid.out.hob |= IDE_VALID_OUT_HOB; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (req_task->in_flags.b.data) 5618c2ecf20Sopenharmony_ci cmd.ftf_flags |= IDE_FTFLAG_IN_DATA; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (req_task->req_cmd == IDE_DRIVE_TASK_RAW_WRITE) { 5648c2ecf20Sopenharmony_ci /* fixup data phase if needed */ 5658c2ecf20Sopenharmony_ci if (req_task->data_phase == TASKFILE_IN_DMAQ || 5668c2ecf20Sopenharmony_ci req_task->data_phase == TASKFILE_IN_DMA) 5678c2ecf20Sopenharmony_ci cmd.tf_flags |= IDE_TFLAG_WRITE; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci cmd.protocol = ATA_PROT_DMA; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci switch (req_task->data_phase) { 5738c2ecf20Sopenharmony_ci case TASKFILE_MULTI_OUT: 5748c2ecf20Sopenharmony_ci if (!drive->mult_count) { 5758c2ecf20Sopenharmony_ci /* (hs): give up if multcount is not set */ 5768c2ecf20Sopenharmony_ci pr_err("%s: %s Multimode Write multcount is not set\n", 5778c2ecf20Sopenharmony_ci drive->name, __func__); 5788c2ecf20Sopenharmony_ci err = -EPERM; 5798c2ecf20Sopenharmony_ci goto abort; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci cmd.tf_flags |= IDE_TFLAG_MULTI_PIO; 5828c2ecf20Sopenharmony_ci fallthrough; 5838c2ecf20Sopenharmony_ci case TASKFILE_OUT: 5848c2ecf20Sopenharmony_ci cmd.protocol = ATA_PROT_PIO; 5858c2ecf20Sopenharmony_ci fallthrough; 5868c2ecf20Sopenharmony_ci case TASKFILE_OUT_DMAQ: 5878c2ecf20Sopenharmony_ci case TASKFILE_OUT_DMA: 5888c2ecf20Sopenharmony_ci cmd.tf_flags |= IDE_TFLAG_WRITE; 5898c2ecf20Sopenharmony_ci nsect = taskout / SECTOR_SIZE; 5908c2ecf20Sopenharmony_ci data_buf = outbuf; 5918c2ecf20Sopenharmony_ci break; 5928c2ecf20Sopenharmony_ci case TASKFILE_MULTI_IN: 5938c2ecf20Sopenharmony_ci if (!drive->mult_count) { 5948c2ecf20Sopenharmony_ci /* (hs): give up if multcount is not set */ 5958c2ecf20Sopenharmony_ci pr_err("%s: %s Multimode Read multcount is not set\n", 5968c2ecf20Sopenharmony_ci drive->name, __func__); 5978c2ecf20Sopenharmony_ci err = -EPERM; 5988c2ecf20Sopenharmony_ci goto abort; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci cmd.tf_flags |= IDE_TFLAG_MULTI_PIO; 6018c2ecf20Sopenharmony_ci fallthrough; 6028c2ecf20Sopenharmony_ci case TASKFILE_IN: 6038c2ecf20Sopenharmony_ci cmd.protocol = ATA_PROT_PIO; 6048c2ecf20Sopenharmony_ci fallthrough; 6058c2ecf20Sopenharmony_ci case TASKFILE_IN_DMAQ: 6068c2ecf20Sopenharmony_ci case TASKFILE_IN_DMA: 6078c2ecf20Sopenharmony_ci nsect = taskin / SECTOR_SIZE; 6088c2ecf20Sopenharmony_ci data_buf = inbuf; 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci case TASKFILE_NO_DATA: 6118c2ecf20Sopenharmony_ci cmd.protocol = ATA_PROT_NODATA; 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci default: 6148c2ecf20Sopenharmony_ci err = -EFAULT; 6158c2ecf20Sopenharmony_ci goto abort; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (req_task->req_cmd == IDE_DRIVE_TASK_NO_DATA) 6198c2ecf20Sopenharmony_ci nsect = 0; 6208c2ecf20Sopenharmony_ci else if (!nsect) { 6218c2ecf20Sopenharmony_ci nsect = (cmd.hob.nsect << 8) | cmd.tf.nsect; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (!nsect) { 6248c2ecf20Sopenharmony_ci pr_err("%s: in/out command without data\n", 6258c2ecf20Sopenharmony_ci drive->name); 6268c2ecf20Sopenharmony_ci err = -EFAULT; 6278c2ecf20Sopenharmony_ci goto abort; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci err = ide_raw_taskfile(drive, &cmd, data_buf, nsect); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci memcpy(req_task->hob_ports, &cmd.hob, HDIO_DRIVE_HOB_HDR_SIZE - 2); 6348c2ecf20Sopenharmony_ci memcpy(req_task->io_ports, &cmd.tf, HDIO_DRIVE_TASK_HDR_SIZE); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if ((cmd.ftf_flags & IDE_FTFLAG_SET_IN_FLAGS) && 6378c2ecf20Sopenharmony_ci req_task->in_flags.all == 0) { 6388c2ecf20Sopenharmony_ci req_task->in_flags.all = IDE_TASKFILE_STD_IN_FLAGS; 6398c2ecf20Sopenharmony_ci if (drive->dev_flags & IDE_DFLAG_LBA48) 6408c2ecf20Sopenharmony_ci req_task->in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8); 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (copy_to_user(buf, req_task, tasksize)) { 6448c2ecf20Sopenharmony_ci err = -EFAULT; 6458c2ecf20Sopenharmony_ci goto abort; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci if (taskout) { 6488c2ecf20Sopenharmony_ci int outtotal = tasksize; 6498c2ecf20Sopenharmony_ci if (copy_to_user(buf + outtotal, outbuf, taskout)) { 6508c2ecf20Sopenharmony_ci err = -EFAULT; 6518c2ecf20Sopenharmony_ci goto abort; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci if (taskin) { 6558c2ecf20Sopenharmony_ci int intotal = tasksize + taskout; 6568c2ecf20Sopenharmony_ci if (copy_to_user(buf + intotal, inbuf, taskin)) { 6578c2ecf20Sopenharmony_ci err = -EFAULT; 6588c2ecf20Sopenharmony_ci goto abort; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ciabort: 6628c2ecf20Sopenharmony_ci kfree(req_task); 6638c2ecf20Sopenharmony_ci kfree(outbuf); 6648c2ecf20Sopenharmony_ci kfree(inbuf); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return err; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci#endif 669