Changeset 110 for trunk/src/os2ahci/ata.c
- Timestamp:
- Jun 21, 2011, 2:39:30 PM (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/os2ahci/ata.c
r101 r110 33 33 34 34 /* -------------------------- function prototypes -------------------------- */ 35 36 static int ata_cmd_read (IORBH _far *iorb, AD_INFO *ai, int p, int d, int slot, 37 ULONG sector, ULONG count, SCATGATENTRY _far *sg_list, 38 ULONG sg_cnt); 39 40 static int ata_cmd_write(IORBH _far *iorb, AD_INFO *ai, int p, int d, int slot, 41 ULONG sector, ULONG count, SCATGATENTRY _far *sg_list, 42 ULONG sg_cnt, int write_through); 35 43 36 44 /* ------------------------ global/static variables ------------------------ */ … … 124 132 if (ata_cmd.lba_l & 0xf0000000UL) { 125 133 dprintf("error: LBA-28 address %ld has more than 28 bits\n", ata_cmd.lba_l); 126 return( -1);134 return(ATA_CMD_INVALID_PARM); 127 135 } 128 136 /* add upper 4 bits to device field */ … … 152 160 case AP_VADDR: 153 161 /* virtual buffer address in addr/len format (up to 4K) */ 154 DevHelp_VirtToPhys(va_arg(va, void _far *), &sg_single.ppXferBuf);162 sg_single.ppXferBuf = virt_to_phys(va_arg(va, void _far *)); 155 163 sg_single.XferBufLen = va_arg(va, u16); 156 164 sg_list = &sg_single; … … 175 183 default: 176 184 dprintf("error: v_ata_cmd() called with invalid parameter type (%d)\n", (int) ap); 177 return( -1);185 return(ATA_CMD_INVALID_PARM); 178 186 } 179 187 … … 254 262 return(i - 1); 255 263 } 264 if ((sg_addr & 1) || (chunk & 1)) { 265 ddprintf("error: ata_cmd() called with unaligned S/G element(s)\n"); 266 return(ATA_CMD_UNALIGNED_ADDR); 267 } 256 268 cmd_tbl->sg_list[n].addr = sg_addr; 257 269 cmd_tbl->sg_list[n].size = chunk - 1; … … 277 289 } 278 290 279 return( 0);291 return(ATA_CMD_SUCCESS); 280 292 } 281 293 … … 567 579 int rc; 568 580 581 if (io->BlockCount == 0) { 582 /* NOP; return -1 without error in IORB to indicate success */ 583 return(-1); 584 } 585 586 if (add_workspace(iorb)->unaligned) { 587 /* unaligned S/G addresses present; need to use double buffers */ 588 return(ata_read_unaligned(iorb, slot)); 589 } 590 569 591 /* Kludge: some I/O commands during boot use excessive S/G buffer lengths 570 592 * which cause NCQ commands to lock up. If there's only one S/G element … … 573 595 */ 574 596 if (io->BlocksXferred == 0 && io->cSGList == 1 && 575 io->pSGList[0].XferBufLen > io->BlockCount * io->BlockSize) {576 io->pSGList[0].XferBufLen = io->BlockCount * io->BlockSize;597 io->pSGList[0].XferBufLen > (ULONG) io->BlockCount * io->BlockSize) { 598 io->pSGList[0].XferBufLen = (ULONG) io->BlockCount * io->BlockSize; 577 599 } 578 600 … … 581 603 sg_indx = ata_get_sg_indx(io); 582 604 sg_cnt = io->cSGList - sg_indx; 583 584 if (sector >= (1UL << 28) || count > 256 || add_workspace(iorb)->is_ncq) { 585 /* need LBA48 for this command */ 586 if (!ai->ports[p].devs[d].lba48) { 587 iorb_seterr(iorb, IOERR_RBA_LIMIT); 588 return(-1); 589 } 590 if (add_workspace(iorb)->is_ncq) { 591 /* use NCQ read; count goes into feature register, tag into count! */ 592 rc = ata_cmd(ai, p, d, slot, ATA_CMD_FPDMA_READ, 593 AP_SECTOR_48, (u32) sector, (u16) 0, 594 AP_FEATURES, (u16) count, 595 AP_COUNT, (u16) (slot << 3), /* tag = slot */ 596 AP_SGLIST, io->pSGList + sg_indx, (u16) sg_cnt, 597 AP_DEVICE, 0x40, 598 AP_END); 599 } else { 600 rc = ata_cmd(ai, p, d, slot, ATA_CMD_READ_EXT, 601 AP_SECTOR_48, (u32) sector, (u16) 0, 602 AP_COUNT, (u16) count, 603 AP_SGLIST, io->pSGList + sg_indx, (u16) sg_cnt, 604 AP_DEVICE, 0x40, 605 AP_END); 606 } 607 608 } else { 609 rc = ata_cmd(ai, p, d, slot, ATA_CMD_READ, 610 AP_SECTOR_28, (u32) sector, 611 AP_COUNT, (u16) count & 0xffU, 612 AP_SGLIST, io->pSGList + sg_indx, (u16) sg_cnt, 613 AP_DEVICE, 0x40, 614 AP_END); 615 } 616 617 if (rc > 0) { 605 if ((rc = ata_cmd_read(iorb, ai, p, d, slot, sector, count, 606 io->pSGList + sg_indx, sg_cnt)) > 0) { 618 607 /* couldn't map all S/G elements */ 619 608 ata_max_sg_cnt(io, sg_indx, (USHORT) rc, &sg_cnt, &count); … … 628 617 iorb_seterr(iorb, IOERR_CMD_SGLIST_BAD); 629 618 619 } else if (rc == ATA_CMD_UNALIGNED_ADDR) { 620 /* unaligned S/G addresses detected; need to use double buffers */ 621 add_workspace(iorb)->unaligned = 1; 622 return(ata_read_unaligned(iorb, slot)); 623 624 } else { 625 iorb_seterr(iorb, IOERR_CMD_ADD_SOFTWARE_FAILURE); 626 } 627 628 return(rc); 629 } 630 631 /****************************************************************************** 632 * Read sectors from AHCI device with unaligned S/G element addresses. AHCI 633 * only allows aligned S/G addresses while OS/2 doesn't have these kind of 634 * restrictions. This doesn't happen very often but when it does, we need to 635 * use a transfer buffer and copy the data manually. 636 */ 637 int ata_read_unaligned(IORBH _far *iorb, int slot) 638 { 639 IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb; 640 ADD_WORKSPACE _far *aws = add_workspace(iorb); 641 AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb); 642 ULONG sector = io->RBA + io->BlocksXferred; 643 SCATGATENTRY sg_single; 644 int p = iorb_unit_port(iorb); 645 int d = iorb_unit_device(iorb); 646 int rc; 647 648 ddprintf("ata_read_unaligned(%d.%d.%d, %ld)\n", ad_no(ai), p, d, sector); 649 650 /* allocate transfer buffer */ 651 if ((aws->buf = malloc(io->BlockSize)) == NULL) { 652 iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE); 653 return(-1); 654 } 655 656 /* prepare read command using transfer buffer */ 657 sg_single.ppXferBuf = virt_to_phys(aws->buf); 658 sg_single.XferBufLen = io->BlockSize; 659 rc = ata_cmd_read(iorb, ai, p, d, slot, sector, 1, &sg_single, 1); 660 661 if (rc == 0) { 662 add_workspace(iorb)->blocks = 1; 663 add_workspace(iorb)->ppfunc = ata_read_pp; 664 665 } else if (rc > 0) { 666 iorb_seterr(iorb, IOERR_CMD_SGLIST_BAD); 667 630 668 } else { 631 669 iorb_seterr(iorb, IOERR_CMD_ADD_SOFTWARE_FAILURE); … … 638 676 * Post processing function for ata_read(); this function updates the 639 677 * BlocksXferred counter in the IORB and, if not all blocks have been 640 * transferred, requeues the IORB to process the remaining sectors. 678 * transferred, requeues the IORB to process the remaining sectors. It also 679 * takes care of copying data from the transfer buffer for unaligned reads. 641 680 */ 642 681 void ata_read_pp(IORBH _far *iorb) 643 682 { 644 683 IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb; 684 ADD_WORKSPACE _far *aws = add_workspace(iorb); 685 686 if (aws->unaligned) { 687 /* copy transfer buffer to corresponding physical address in S/G list */ 688 sg_memcpy(io->pSGList, io->cSGList, 689 (ULONG) io->BlocksXferred * (ULONG) io->BlockSize, 690 aws->buf, io->BlockSize, BUF_TO_SG); 691 } 645 692 646 693 io->BlocksXferred += add_workspace(iorb)->blocks; … … 666 713 int d = iorb_unit_device(iorb); 667 714 int rc; 715 716 if (io->BlockCount == 0) { 717 /* NOP; return -1 without error in IORB to indicate success */ 718 return(-1); 719 } 668 720 669 721 /* prepare verify command */ … … 704 756 int rc; 705 757 758 if (io->BlockCount == 0) { 759 /* NOP; return -1 without error in IORB to indicate success */ 760 return(-1); 761 } 762 763 if (add_workspace(iorb)->unaligned) { 764 /* unaligned S/G addresses present; need to use double buffers */ 765 return(ata_write_unaligned(iorb, slot)); 766 } 767 706 768 /* prepare write command while keeping an eye on S/G count limitations */ 707 769 do { 708 770 sg_indx = ata_get_sg_indx(io); 709 771 sg_cnt = io->cSGList - sg_indx; 710 711 if (sector >= (1UL << 28) || count > 256 || add_workspace(iorb)->is_ncq) { 712 /* need LBA48 for this command */ 713 if (!ai->ports[p].devs[d].lba48) { 714 iorb_seterr(iorb, IOERR_RBA_LIMIT); 715 return(-1); 716 } 717 if (add_workspace(iorb)->is_ncq) { 718 /* use NCQ write; count goes into feature register, tag into count! */ 719 rc = ata_cmd(ai, p, d, slot, ATA_CMD_FPDMA_WRITE, 720 AP_SECTOR_48, (u32) sector, (u16) 0, 721 AP_FEATURES, (u16) count, 722 AP_COUNT, (u16) (slot << 3), /* tag = slot */ 723 AP_SGLIST, io->pSGList + sg_indx, (u16) sg_cnt, 724 AP_DEVICE, 0x40, 725 AP_DEVICE, (io->Flags & XIO_DISABLE_HW_WRITE_CACHE) ? 726 0x80 : 0, /* force unit access */ 727 AP_WRITE, 1, 728 AP_END); 729 } else { 730 rc = ata_cmd(ai, p, d, slot, ATA_CMD_WRITE_EXT, 731 AP_SECTOR_48, (u32) sector, (u16) 0, 732 AP_COUNT, (u16) count, 733 AP_SGLIST, io->pSGList + sg_indx, (u16) sg_cnt, 734 AP_DEVICE, 0x40, 735 AP_WRITE, 1, 736 AP_END); 737 } 738 739 } else { 740 rc = ata_cmd(ai, p, d, slot, ATA_CMD_WRITE, 741 AP_SECTOR_28, (u32) sector, 742 AP_COUNT, (u16) count & 0xffU, 743 AP_SGLIST, io->pSGList + sg_indx, (u16) sg_cnt, 744 AP_DEVICE, 0x40, 745 AP_WRITE, 1, 746 AP_END); 747 } 748 749 if (rc > 0) { 772 if ((rc = ata_cmd_write(iorb, ai, p, d, slot, sector, count, 773 io->pSGList + sg_indx, sg_cnt, 774 io->Flags & XIO_DISABLE_HW_WRITE_CACHE)) > 0) { 750 775 /* couldn't map all S/G elements */ 751 776 ata_max_sg_cnt(io, sg_indx, (USHORT) rc, &sg_cnt, &count); … … 760 785 iorb_seterr(iorb, IOERR_CMD_SGLIST_BAD); 761 786 787 } else if (rc == ATA_CMD_UNALIGNED_ADDR) { 788 /* unaligned S/G addresses detected; need to use double buffers */ 789 add_workspace(iorb)->unaligned = 1; 790 return(ata_write_unaligned(iorb, slot)); 791 762 792 } else { 763 793 iorb_seterr(iorb, IOERR_CMD_ADD_SOFTWARE_FAILURE); … … 766 796 return(rc); 767 797 } 798 799 /****************************************************************************** 800 * Write sectors from AHCI device with unaligned S/G element addresses. AHCI 801 * only allows aligned S/G addresses while OS/2 doesn't have these kind of 802 * restrictions. This doesn't happen very often but when it does, we need to 803 * use a transfer buffer and copy the data manually. 804 */ 805 int ata_write_unaligned(IORBH _far *iorb, int slot) 806 { 807 IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb; 808 ADD_WORKSPACE _far *aws = add_workspace(iorb); 809 AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb); 810 ULONG sector = io->RBA + io->BlocksXferred; 811 SCATGATENTRY sg_single; 812 int p = iorb_unit_port(iorb); 813 int d = iorb_unit_device(iorb); 814 int rc; 815 816 ddprintf("ata_write_unaligned(%d.%d.%d, %ld)\n", ad_no(ai), p, d, sector); 817 818 /* allocate transfer buffer */ 819 if ((aws->buf = malloc(io->BlockSize)) == NULL) { 820 iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE); 821 return(-1); 822 } 823 824 /* copy next sector from S/G list to transfer buffer */ 825 sg_memcpy(io->pSGList, io->cSGList, 826 (ULONG) io->BlocksXferred * (ULONG) io->BlockSize, 827 aws->buf, io->BlockSize, SG_TO_BUF); 828 829 /* prepare write command using transfer buffer */ 830 sg_single.ppXferBuf = virt_to_phys(aws->buf); 831 sg_single.XferBufLen = io->BlockSize; 832 rc = ata_cmd_write(iorb, ai, p, d, slot, sector, 1, &sg_single, 1, 833 io->Flags & XIO_DISABLE_HW_WRITE_CACHE); 834 835 if (rc == 0) { 836 add_workspace(iorb)->blocks = 1; 837 add_workspace(iorb)->ppfunc = ata_write_pp; 838 839 } else if (rc > 0) { 840 iorb_seterr(iorb, IOERR_CMD_SGLIST_BAD); 841 842 } else { 843 iorb_seterr(iorb, IOERR_CMD_ADD_SOFTWARE_FAILURE); 844 } 845 846 return(rc); 847 } 848 768 849 769 850 /****************************************************************************** … … 888 969 } 889 970 971 /****************************************************************************** 972 * Fabricate ATA READ command based on the capabilities of the corresponding 973 * device and the paramters set from above (NCQ, etc). 974 */ 975 static int ata_cmd_read(IORBH _far *iorb, AD_INFO *ai, int p, int d, int slot, 976 ULONG sector, ULONG count, SCATGATENTRY _far *sg_list, 977 ULONG sg_cnt) 978 { 979 int rc; 980 981 if (sector >= (1UL << 28) || count > 256 || add_workspace(iorb)->is_ncq) { 982 /* need LBA48 for this command */ 983 if (!ai->ports[p].devs[d].lba48) { 984 iorb_seterr(iorb, IOERR_RBA_LIMIT); 985 return(-1); 986 } 987 if (add_workspace(iorb)->is_ncq) { 988 /* use NCQ read; count goes into feature register, tag into count! */ 989 rc = ata_cmd(ai, p, d, slot, ATA_CMD_FPDMA_READ, 990 AP_SECTOR_48, (u32) sector, (u16) 0, 991 AP_FEATURES, (u16) count, 992 AP_COUNT, (u16) (slot << 3), /* tag == slot */ 993 AP_SGLIST, sg_list, (u16) sg_cnt, 994 AP_DEVICE, 0x40, 995 AP_END); 996 } else { 997 rc = ata_cmd(ai, p, d, slot, ATA_CMD_READ_EXT, 998 AP_SECTOR_48, (u32) sector, (u16) 0, 999 AP_COUNT, (u16) count, 1000 AP_SGLIST, sg_list, (u16) sg_cnt, 1001 AP_DEVICE, 0x40, 1002 AP_END); 1003 } 1004 1005 } else { 1006 rc = ata_cmd(ai, p, d, slot, ATA_CMD_READ, 1007 AP_SECTOR_28, (u32) sector, 1008 AP_COUNT, (u16) count & 0xffU, 1009 AP_SGLIST, sg_list, (u16) sg_cnt, 1010 AP_DEVICE, 0x40, 1011 AP_END); 1012 } 1013 1014 return(rc); 1015 } 1016 1017 /****************************************************************************** 1018 * Fabricate ATA WRITE command based on the capabilities of the corresponding 1019 * device and the paramters set from above (NCQ, etc) 1020 */ 1021 static int ata_cmd_write(IORBH _far *iorb, AD_INFO *ai, int p, int d, int slot, 1022 ULONG sector, ULONG count, SCATGATENTRY _far *sg_list, 1023 ULONG sg_cnt, int write_through) 1024 { 1025 int rc; 1026 1027 if (sector >= (1UL << 28) || count > 256 || add_workspace(iorb)->is_ncq) { 1028 /* need LBA48 for this command */ 1029 if (!ai->ports[p].devs[d].lba48) { 1030 iorb_seterr(iorb, IOERR_RBA_LIMIT); 1031 return(-1); 1032 } 1033 if (add_workspace(iorb)->is_ncq) { 1034 /* use NCQ write; count goes into feature register, tag into count! */ 1035 rc = ata_cmd(ai, p, d, slot, ATA_CMD_FPDMA_WRITE, 1036 AP_SECTOR_48, (u32) sector, (u16) 0, 1037 AP_FEATURES, (u16) count, 1038 AP_COUNT, (u16) (slot << 3), /* tag = slot */ 1039 AP_SGLIST, sg_list, (u16) sg_cnt, 1040 AP_DEVICE, 0x40, 1041 AP_DEVICE, (write_through) ? 0x80 : 0, /* force unit access */ 1042 AP_WRITE, 1, 1043 AP_END); 1044 } else { 1045 rc = ata_cmd(ai, p, d, slot, ATA_CMD_WRITE_EXT, 1046 AP_SECTOR_48, (u32) sector, (u16) 0, 1047 AP_COUNT, (u16) count, 1048 AP_SGLIST, sg_list, (u16) sg_cnt, 1049 AP_DEVICE, 0x40, 1050 AP_WRITE, 1, 1051 AP_END); 1052 } 1053 1054 } else { 1055 rc = ata_cmd(ai, p, d, slot, ATA_CMD_WRITE, 1056 AP_SECTOR_28, (u32) sector, 1057 AP_COUNT, (u16) count & 0xffU, 1058 AP_SGLIST, sg_list, (u16) sg_cnt, 1059 AP_DEVICE, 0x40, 1060 AP_WRITE, 1, 1061 AP_END); 1062 } 1063 1064 return(rc); 1065 }
Note:
See TracChangeset
for help on using the changeset viewer.