1/*
2 * Copyright (c) 2011 ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <stdlib.h>
11#include <errors/errno.h>
12#include <ahci/sata_fis.h>
13
14errval_t sata_alloc_h2d_register_fis(void **fis_p, size_t *fis_size_p)
15{
16    struct sata_fis_reg_h2d *fis;
17
18    fis = calloc(1, sizeof(*fis));
19    if (!fis) {
20        return LIB_ERR_MALLOC_FAIL;
21    }
22    fis->type = SATA_FIS_TYPE_H2D;
23
24    /* Device Shadow Register layout (see: [1])
25     * [  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  ]
26     * [  -  |  L  |  -  | DEV | HS3 | HS2 | HS1 | HS0 ]
27     *
28     * L is the address mode, cleared implies CHS addressing, set, LBA addressing
29     * DEV device select, cleared and set imply Device 0 and 1 resp.
30     *   for SATA this should always be cleared (see: [2])
31     * HS3-HS0 are bits 28-25 of the LBA 28 (not used for LBA 48, see [3])
32     *
33     * [1] Serial ATA NSSD Rev. 1.0 (Sept 2008), section 6.3.1
34     * [2] Serial ATA Rev. 2.6 (15-February-2007), section 13.1, paragraph 2
35     * [3] ATA8-ACS Rev. 3f (December 11, 2006), section 7.1.5.2
36     */
37    fis->device |= (1 << 6);
38
39    *fis_p = fis;
40    *fis_size_p = sizeof(*fis);
41
42    return SYS_ERR_OK;
43}
44
45errval_t sata_set_command(void *fis, uint8_t command)
46{
47    uint8_t fis_type = *(uint8_t*)fis;
48
49    if (fis_type == SATA_FIS_TYPE_H2D) {
50        struct sata_fis_reg_h2d *fis_reg_h2d = fis;
51        fis_reg_h2d->command = command;
52
53        /* set bit to indicate update of command register (see [1])
54         *
55         * [1]: SATA Rev. 2.6 (15-February-2007), section 10.3.4
56         */
57        fis_reg_h2d->specialstuff |= (1 << 7);
58
59        return SYS_ERR_OK;
60    }
61    else {
62        return SATA_ERR_INVALID_TYPE;
63    }
64}
65
66errval_t sata_set_feature(void *fis, uint8_t feature)
67{
68    uint8_t fis_type = *(uint8_t*)fis;
69
70    if (fis_type == SATA_FIS_TYPE_H2D) {
71        struct sata_fis_reg_h2d *fis_reg_h2d = fis;
72        fis_reg_h2d->feature = feature;
73
74        return SYS_ERR_OK;
75    }
76    else {
77        return SATA_ERR_INVALID_TYPE;
78    }
79}
80
81errval_t sata_set_lba28(void *fis, uint32_t lba)
82{
83    uint8_t fis_type = *(uint8_t*)fis;
84
85    if (fis_type == SATA_FIS_TYPE_H2D) {
86        struct sata_fis_reg_h2d *fis_reg_h2d = fis;
87        fis_reg_h2d->lba0 = lba & 0xFF;
88        fis_reg_h2d->lba1 = (lba >> 8) & 0xFF;
89        fis_reg_h2d->lba2 = (lba >> 16) & 0xFF;
90        fis_reg_h2d->device = (fis_reg_h2d->device & ~0x0F) | ((lba >> 24) & 0x0F);
91
92        return SYS_ERR_OK;
93    }
94    else {
95        return SATA_ERR_INVALID_TYPE;
96    }
97}
98
99errval_t sata_set_lba48(void *fis, uint64_t lba)
100{
101    uint8_t fis_type = *(uint8_t*)fis;
102
103    if (fis_type == SATA_FIS_TYPE_H2D) {
104        struct sata_fis_reg_h2d *fis_reg_h2d = fis;
105        fis_reg_h2d->lba0 = lba & 0xFF;
106        fis_reg_h2d->lba1 = (lba >> 8) & 0xFF;
107        fis_reg_h2d->lba2 = (lba >> 16) & 0xFF;
108        fis_reg_h2d->device &= 0xF0; // clear bits otherwise used by lba28
109
110        fis_reg_h2d->lba3 = (lba >> 24) & 0xFF;
111        fis_reg_h2d->lba4 = (lba >> 32) & 0xFF;
112        fis_reg_h2d->lba5 = (lba >> 40) & 0xFF;
113
114        return SYS_ERR_OK;
115    }
116    else {
117        return SATA_ERR_INVALID_TYPE;
118    }
119}
120
121errval_t sata_set_count(void *fis, uint16_t count)
122{
123    uint8_t fis_type = *(uint8_t*)fis;
124
125    if (fis_type == SATA_FIS_TYPE_H2D) {
126        struct sata_fis_reg_h2d *fis_reg_h2d = fis;
127        fis_reg_h2d->countl = count & 0xFF;
128        fis_reg_h2d->counth = (count >> 8) & 0xFF;
129
130        return SYS_ERR_OK;
131    }
132    else {
133        return SATA_ERR_INVALID_TYPE;
134    }
135}
136