1/*
2 * Copyright 2009 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software")
6 * to deal in the software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * them Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Author:
23 *	Adam Jackson <ajax@redhat.com>
24 */
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include <stdlib.h>
30#include <string.h>
31#include "pciaccess.h"
32#include "pciaccess_private.h"
33
34static struct pci_io_handle *
35new_io_handle(void)
36{
37    struct pci_io_handle *new;
38
39    new = malloc(sizeof(struct pci_io_handle));
40    if (!new)
41	return NULL;
42
43    return new;
44}
45
46static void
47delete_io_handle(struct pci_io_handle *handle)
48{
49    free(handle);
50    return;
51}
52
53_pci_hidden void
54pci_io_cleanup(void)
55{
56}
57
58/**
59 * Open a handle to a PCI device I/O range.  The \c base and \c size
60 * requested must fit entirely within a single I/O BAR on the device.
61 * \c size is in bytes.
62 *
63 * \returns
64 * An opaque handle to the I/O BAR, or \c NULL on error.
65 */
66struct pci_io_handle *
67pci_device_open_io(struct pci_device *dev, pciaddr_t base, pciaddr_t size)
68{
69    struct pci_io_handle *ret;
70    int bar;
71
72    if (!pci_sys->methods->open_device_io)
73	return NULL;
74
75    for (bar = 0; bar < 6; bar++) {
76	struct pci_mem_region *region = &(dev->regions[bar]);
77	if (!region->is_IO)
78	    continue;
79
80	if (base < region->base_addr || base > (region->base_addr+region->size))
81	    continue;
82
83	if ((base + size) > (region->base_addr + region->size))
84	    continue;
85
86	ret = new_io_handle();
87	if (!ret)
88	    return NULL;
89
90	if (!pci_sys->methods->open_device_io(ret, dev, bar, base, size)) {
91	    delete_io_handle(ret);
92	    return NULL;
93	}
94
95        return ret;
96    }
97
98    return NULL;
99}
100
101/**
102 * Open a handle to the legacy I/O space for the PCI domain containing
103 * \c dev. \c size is in bytes.
104 *
105 * \returns
106 * An opaque handle to the requested range, or \c NULL on error.
107 */
108struct pci_io_handle *
109pci_legacy_open_io(struct pci_device *dev, pciaddr_t base, pciaddr_t size)
110{
111    struct pci_io_handle *ret;
112
113    if (!pci_sys->methods->open_legacy_io)
114	return NULL;
115
116    ret = new_io_handle();
117    if (!ret)
118	return NULL;
119
120    if (!pci_sys->methods->open_legacy_io(ret, dev, base, size)) {
121	delete_io_handle(ret);
122	return NULL;
123    }
124
125    return ret;
126}
127
128/**
129 * Close an I/O handle.
130 */
131void
132pci_device_close_io(struct pci_device *dev, struct pci_io_handle *handle)
133{
134    if (dev && handle && pci_sys->methods->close_io)
135	pci_sys->methods->close_io(dev, handle);
136
137    delete_io_handle(handle);
138}
139
140/**
141 * Read a 32-bit value from the I/O space.  \c reg is relative to the
142 * \c base specified when the handle was opened.  Some platforms may
143 * require that \c reg be 32-bit-aligned.
144 *
145 * \returns
146 * The value read from the I/O port, or undefined on any error.
147 */
148uint32_t
149pci_io_read32(struct pci_io_handle *handle, uint32_t reg)
150{
151    if (reg + 4 > handle->size)
152	return UINT32_MAX;
153
154    return pci_sys->methods->read32(handle, reg);
155}
156
157/**
158 * Read a 16-bit value from the I/O space.  \c reg is relative to the
159 * \c base specified when the handle was opened.  Some platforms may
160 * require that \c reg be 16-bit-aligned.
161 *
162 * \returns
163 * The value read from the I/O port, or undefined on any error.
164 */
165uint16_t
166pci_io_read16(struct pci_io_handle *handle, uint32_t reg)
167{
168    if (reg + 2 > handle->size)
169	return UINT16_MAX;
170
171    return pci_sys->methods->read16(handle, reg);
172}
173
174/**
175 * Read a 8-bit value from the I/O space.  \c reg is relative to the
176 * \c base specified when the handle was opened.
177 *
178 * \returns
179 * The value read from the I/O port, or undefined on any error.
180 */
181uint8_t
182pci_io_read8(struct pci_io_handle *handle, uint32_t reg)
183{
184    if (reg + 1 > handle->size)
185	return UINT8_MAX;
186
187    return pci_sys->methods->read8(handle, reg);
188}
189
190/**
191 * Write a 32-bit value to the I/O space.  \c reg is relative to the
192 * \c base specified when the handle was opened.  Some platforms may
193 * require that \c reg be 32-bit-aligned.
194 */
195void
196pci_io_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data)
197{
198    if (reg + 4 > handle->size)
199	return;
200
201    pci_sys->methods->write32(handle, reg, data);
202}
203
204/**
205 * Write a 16-bit value to the I/O space.  \c reg is relative to the
206 * \c base specified when the handle was opened.  Some platforms may
207 * require that \c reg be 16-bit-aligned.
208 */
209void
210pci_io_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data)
211{
212    if (reg + 2 > handle->size)
213	return;
214
215    pci_sys->methods->write16(handle, reg, data);
216}
217
218/**
219 * Write a 8-bit value to the I/O space.  \c reg is relative to the
220 * \c base specified when the handle was opened.
221 */
222void
223pci_io_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data)
224{
225    if (reg + 1 > handle->size)
226	return;
227
228    pci_sys->methods->write8(handle, reg, data);
229}
230