xref: /libpciaccess/src/common_io.c (revision a798395a)
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 MERCHANTIBILITY,
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 <[email protected]>
24  */
25 
26 #include <stdlib.h>
27 #include <string.h>
28 #include "pciaccess.h"
29 #include "pciaccess_private.h"
30 
31 static struct pci_io_handle *ios;
32 static unsigned int num_ios;
33 
34 static struct pci_io_handle *
new_io_handle(void)35 new_io_handle(void)
36 {
37     struct pci_io_handle *new;
38 
39     new = realloc(ios, sizeof(struct pci_io_handle) * (num_ios + 1));
40     if (!new)
41 	return NULL;
42 
43     ios = new;
44     num_ios++;
45 
46     return ios + num_ios - 1;
47 }
48 
49 static void
delete_io_handle(struct pci_io_handle * handle)50 delete_io_handle(struct pci_io_handle *handle)
51 {
52     struct pci_io_handle *new;
53     int i = 0;
54 
55     if (!handle || !num_ios || (void *)handle < (void *)ios ||
56         (void *)handle > (void *)(ios + num_ios - 1))
57         return;
58 
59     for (i = 0; i < num_ios; i++) {
60         if (ios + i == handle) {
61             memmove(&ios[i], &ios[i+1], sizeof(struct pci_io_handle) *
62                                         (num_ios - i - 1));
63             break;
64         }
65     }
66 
67     num_ios--;
68     if (num_ios) {
69         new = realloc(ios, sizeof(struct pci_io_handle) * num_ios);
70         if (new)
71             ios = new;
72     } else {
73         free(ios);
74         ios = NULL;
75     }
76 }
77 
78 _pci_hidden void
pci_io_cleanup(void)79 pci_io_cleanup(void)
80 {
81     free(ios);
82     ios = NULL;
83     num_ios = 0;
84 }
85 
86 /**
87  * Open a handle to a PCI device I/O range.  The \c base and \c size
88  * requested must fit entirely within a single I/O BAR on the device.
89  * \c size is in bytes.
90  *
91  * \returns
92  * An opaque handle to the I/O BAR, or \c NULL on error.
93  */
94 struct pci_io_handle *
pci_device_open_io(struct pci_device * dev,pciaddr_t base,pciaddr_t size)95 pci_device_open_io(struct pci_device *dev, pciaddr_t base, pciaddr_t size)
96 {
97     struct pci_io_handle *ret;
98     int bar;
99 
100     if (!pci_sys->methods->open_device_io)
101 	return NULL;
102 
103     for (bar = 0; bar < 6; bar++) {
104 	struct pci_mem_region *region = &(dev->regions[bar]);
105 	if (!region->is_IO)
106 	    continue;
107 
108 	if (base < region->base_addr || base > (region->base_addr+region->size))
109 	    continue;
110 
111 	if ((base + size) > (region->base_addr + region->size))
112 	    continue;
113 
114 	ret = new_io_handle();
115 	if (!ret)
116 	    return NULL;
117 
118 	if (!pci_sys->methods->open_device_io(ret, dev, bar, base, size)) {
119 	    delete_io_handle(ret);
120 	    return NULL;
121 	}
122 
123         return ret;
124     }
125 
126     return NULL;
127 }
128 
129 /**
130  * Open a handle to the legacy I/O space for the PCI domain containing
131  * \c dev. \c size is in bytes.
132  *
133  * \returns
134  * An opaque handle to the requested range, or \c NULL on error.
135  */
136 struct pci_io_handle *
pci_legacy_open_io(struct pci_device * dev,pciaddr_t base,pciaddr_t size)137 pci_legacy_open_io(struct pci_device *dev, pciaddr_t base, pciaddr_t size)
138 {
139     struct pci_io_handle *ret;
140 
141     if (!pci_sys->methods->open_legacy_io)
142 	return NULL;
143 
144     ret = new_io_handle();
145     if (!ret)
146 	return NULL;
147 
148     if (!pci_sys->methods->open_legacy_io(ret, dev, base, size)) {
149 	delete_io_handle(ret);
150 	return NULL;
151     }
152 
153     return ret;
154 }
155 
156 /**
157  * Close an I/O handle.
158  */
159 void
pci_device_close_io(struct pci_device * dev,struct pci_io_handle * handle)160 pci_device_close_io(struct pci_device *dev, struct pci_io_handle *handle)
161 {
162     if (dev && handle && pci_sys->methods->close_io)
163 	pci_sys->methods->close_io(dev, handle);
164 
165     delete_io_handle(handle);
166 }
167 
168 /**
169  * Read a 32-bit value from the I/O space.  \c reg is relative to the
170  * \c base specified when the handle was opened.  Some platforms may
171  * require that \c reg be 32-bit-aligned.
172  *
173  * \returns
174  * The value read from the I/O port, or undefined on any error.
175  */
176 uint32_t
pci_io_read32(struct pci_io_handle * handle,uint32_t reg)177 pci_io_read32(struct pci_io_handle *handle, uint32_t reg)
178 {
179     if (reg + 4 > handle->size)
180 	return UINT32_MAX;
181 
182     return pci_sys->methods->read32(handle, reg);
183 }
184 
185 /**
186  * Read a 16-bit value from the I/O space.  \c reg is relative to the
187  * \c base specified when the handle was opened.  Some platforms may
188  * require that \c reg be 16-bit-aligned.
189  *
190  * \returns
191  * The value read from the I/O port, or undefined on any error.
192  */
193 uint16_t
pci_io_read16(struct pci_io_handle * handle,uint32_t reg)194 pci_io_read16(struct pci_io_handle *handle, uint32_t reg)
195 {
196     if (reg + 2 > handle->size)
197 	return UINT16_MAX;
198 
199     return pci_sys->methods->read16(handle, reg);
200 }
201 
202 /**
203  * Read a 8-bit value from the I/O space.  \c reg is relative to the
204  * \c base specified when the handle was opened.
205  *
206  * \returns
207  * The value read from the I/O port, or undefined on any error.
208  */
209 uint8_t
pci_io_read8(struct pci_io_handle * handle,uint32_t reg)210 pci_io_read8(struct pci_io_handle *handle, uint32_t reg)
211 {
212     if (reg + 1 > handle->size)
213 	return UINT8_MAX;
214 
215     return pci_sys->methods->read8(handle, reg);
216 }
217 
218 /**
219  * Write a 32-bit value to the I/O space.  \c reg is relative to the
220  * \c base specified when the handle was opened.  Some platforms may
221  * require that \c reg be 32-bit-aligned.
222  */
223 void
pci_io_write32(struct pci_io_handle * handle,uint32_t reg,uint32_t data)224 pci_io_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data)
225 {
226     if (reg + 4 > handle->size)
227 	return;
228 
229     pci_sys->methods->write32(handle, reg, data);
230 }
231 
232 /**
233  * Write a 16-bit value to the I/O space.  \c reg is relative to the
234  * \c base specified when the handle was opened.  Some platforms may
235  * require that \c reg be 16-bit-aligned.
236  */
237 void
pci_io_write16(struct pci_io_handle * handle,uint32_t reg,uint16_t data)238 pci_io_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data)
239 {
240     if (reg + 2 > handle->size)
241 	return;
242 
243     pci_sys->methods->write16(handle, reg, data);
244 }
245 
246 /**
247  * Write a 8-bit value to the I/O space.  \c reg is relative to the
248  * \c base specified when the handle was opened.
249  */
250 void
pci_io_write8(struct pci_io_handle * handle,uint32_t reg,uint8_t data)251 pci_io_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data)
252 {
253     if (reg + 1 > handle->size)
254 	return;
255 
256     pci_sys->methods->write8(handle, reg, data);
257 }
258