Commit | Line | Data |
---|---|---|
7938f421 LDM |
1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
2 | /* | |
3 | * Pointer abstraction for IO/system memory | |
4 | */ | |
5 | ||
6 | #ifndef __IOSYS_MAP_H__ | |
7 | #define __IOSYS_MAP_H__ | |
8 | ||
5f278dbd | 9 | #include <linux/compiler_types.h> |
7938f421 LDM |
10 | #include <linux/io.h> |
11 | #include <linux/string.h> | |
12 | ||
13 | /** | |
14 | * DOC: overview | |
15 | * | |
16 | * When accessing a memory region, depending on its location, users may have to | |
17 | * access it with I/O operations or memory load/store operations. For example, | |
18 | * copying to system memory could be done with memcpy(), copying to I/O memory | |
19 | * would be done with memcpy_toio(). | |
20 | * | |
21 | * .. code-block:: c | |
22 | * | |
23 | * void *vaddr = ...; // pointer to system memory | |
24 | * memcpy(vaddr, src, len); | |
25 | * | |
26 | * void *vaddr_iomem = ...; // pointer to I/O memory | |
e4a8864f | 27 | * memcpy_toio(vaddr_iomem, src, len); |
7938f421 LDM |
28 | * |
29 | * The user of such pointer may not have information about the mapping of that | |
30 | * region or may want to have a single code path to handle operations on that | |
31 | * buffer, regardless if it's located in system or IO memory. The type | |
32 | * :c:type:`struct iosys_map <iosys_map>` and its helpers abstract that so the | |
33 | * buffer can be passed around to other drivers or have separate duties inside | |
34 | * the same driver for allocation, read and write operations. | |
35 | * | |
36 | * Open-coding access to :c:type:`struct iosys_map <iosys_map>` is considered | |
37 | * bad style. Rather then accessing its fields directly, use one of the provided | |
38 | * helper functions, or implement your own. For example, instances of | |
39 | * :c:type:`struct iosys_map <iosys_map>` can be initialized statically with | |
40 | * IOSYS_MAP_INIT_VADDR(), or at runtime with iosys_map_set_vaddr(). These | |
41 | * helpers will set an address in system memory. | |
42 | * | |
43 | * .. code-block:: c | |
44 | * | |
45 | * struct iosys_map map = IOSYS_MAP_INIT_VADDR(0xdeadbeaf); | |
46 | * | |
47 | * iosys_map_set_vaddr(&map, 0xdeadbeaf); | |
48 | * | |
116d902f TZ |
49 | * To set an address in I/O memory, use IOSYS_MAP_INIT_VADDR_IOMEM() or |
50 | * iosys_map_set_vaddr_iomem(). | |
7938f421 LDM |
51 | * |
52 | * .. code-block:: c | |
53 | * | |
116d902f TZ |
54 | * struct iosys_map map = IOSYS_MAP_INIT_VADDR_IOMEM(0xdeadbeaf); |
55 | * | |
7938f421 LDM |
56 | * iosys_map_set_vaddr_iomem(&map, 0xdeadbeaf); |
57 | * | |
58 | * Instances of struct iosys_map do not have to be cleaned up, but | |
59 | * can be cleared to NULL with iosys_map_clear(). Cleared mappings | |
60 | * always refer to system memory. | |
61 | * | |
62 | * .. code-block:: c | |
63 | * | |
64 | * iosys_map_clear(&map); | |
65 | * | |
66 | * Test if a mapping is valid with either iosys_map_is_set() or | |
67 | * iosys_map_is_null(). | |
68 | * | |
69 | * .. code-block:: c | |
70 | * | |
71 | * if (iosys_map_is_set(&map) != iosys_map_is_null(&map)) | |
72 | * // always true | |
73 | * | |
74 | * Instances of :c:type:`struct iosys_map <iosys_map>` can be compared for | |
75 | * equality with iosys_map_is_equal(). Mappings that point to different memory | |
76 | * spaces, system or I/O, are never equal. That's even true if both spaces are | |
77 | * located in the same address space, both mappings contain the same address | |
78 | * value, or both mappings refer to NULL. | |
79 | * | |
80 | * .. code-block:: c | |
81 | * | |
82 | * struct iosys_map sys_map; // refers to system memory | |
83 | * struct iosys_map io_map; // refers to I/O memory | |
84 | * | |
85 | * if (iosys_map_is_equal(&sys_map, &io_map)) | |
86 | * // always false | |
87 | * | |
88 | * A set up instance of struct iosys_map can be used to access or manipulate the | |
89 | * buffer memory. Depending on the location of the memory, the provided helpers | |
90 | * will pick the correct operations. Data can be copied into the memory with | |
91 | * iosys_map_memcpy_to(). The address can be manipulated with iosys_map_incr(). | |
92 | * | |
93 | * .. code-block:: c | |
94 | * | |
95 | * const void *src = ...; // source buffer | |
96 | * size_t len = ...; // length of src | |
97 | * | |
98 | * iosys_map_memcpy_to(&map, src, len); | |
99 | * iosys_map_incr(&map, len); // go to first byte after the memcpy | |
100 | */ | |
101 | ||
102 | /** | |
103 | * struct iosys_map - Pointer to IO/system memory | |
104 | * @vaddr_iomem: The buffer's address if in I/O memory | |
105 | * @vaddr: The buffer's address if in system memory | |
106 | * @is_iomem: True if the buffer is located in I/O memory, or false | |
107 | * otherwise. | |
108 | */ | |
109 | struct iosys_map { | |
110 | union { | |
111 | void __iomem *vaddr_iomem; | |
112 | void *vaddr; | |
113 | }; | |
114 | bool is_iomem; | |
115 | }; | |
116 | ||
117 | /** | |
118 | * IOSYS_MAP_INIT_VADDR - Initializes struct iosys_map to an address in system memory | |
119 | * @vaddr_: A system-memory address | |
120 | */ | |
121 | #define IOSYS_MAP_INIT_VADDR(vaddr_) \ | |
122 | { \ | |
123 | .vaddr = (vaddr_), \ | |
124 | .is_iomem = false, \ | |
125 | } | |
126 | ||
116d902f TZ |
127 | /** |
128 | * IOSYS_MAP_INIT_VADDR_IOMEM - Initializes struct iosys_map to an address in I/O memory | |
129 | * @vaddr_iomem_: An I/O-memory address | |
130 | */ | |
131 | #define IOSYS_MAP_INIT_VADDR_IOMEM(vaddr_iomem_) \ | |
132 | { \ | |
133 | .vaddr_iomem = (vaddr_iomem_), \ | |
134 | .is_iomem = true, \ | |
135 | } | |
136 | ||
e62f25e8 LDM |
137 | /** |
138 | * IOSYS_MAP_INIT_OFFSET - Initializes struct iosys_map from another iosys_map | |
139 | * @map_: The dma-buf mapping structure to copy from | |
140 | * @offset_: Offset to add to the other mapping | |
141 | * | |
142 | * Initializes a new iosys_map struct based on another passed as argument. It | |
143 | * does a shallow copy of the struct so it's possible to update the back storage | |
144 | * without changing where the original map points to. It is the equivalent of | |
145 | * doing: | |
146 | * | |
147 | * .. code-block:: c | |
148 | * | |
149 | * iosys_map map = other_map; | |
150 | * iosys_map_incr(&map, &offset); | |
151 | * | |
152 | * Example usage: | |
153 | * | |
154 | * .. code-block:: c | |
155 | * | |
156 | * void foo(struct device *dev, struct iosys_map *base_map) | |
157 | * { | |
158 | * ... | |
159 | * struct iosys_map map = IOSYS_MAP_INIT_OFFSET(base_map, FIELD_OFFSET); | |
160 | * ... | |
161 | * } | |
162 | * | |
163 | * The advantage of using the initializer over just increasing the offset with | |
164 | * iosys_map_incr() like above is that the new map will always point to the | |
165 | * right place of the buffer during its scope. It reduces the risk of updating | |
166 | * the wrong part of the buffer and having no compiler warning about that. If | |
167 | * the assignment to IOSYS_MAP_INIT_OFFSET() is forgotten, the compiler can warn | |
168 | * about the use of uninitialized variable. | |
169 | */ | |
170 | #define IOSYS_MAP_INIT_OFFSET(map_, offset_) ({ \ | |
171 | struct iosys_map copy = *map_; \ | |
172 | iosys_map_incr(©, offset_); \ | |
173 | copy; \ | |
174 | }) | |
175 | ||
7938f421 LDM |
176 | /** |
177 | * iosys_map_set_vaddr - Sets a iosys mapping structure to an address in system memory | |
178 | * @map: The iosys_map structure | |
179 | * @vaddr: A system-memory address | |
180 | * | |
181 | * Sets the address and clears the I/O-memory flag. | |
182 | */ | |
183 | static inline void iosys_map_set_vaddr(struct iosys_map *map, void *vaddr) | |
184 | { | |
185 | map->vaddr = vaddr; | |
186 | map->is_iomem = false; | |
187 | } | |
188 | ||
189 | /** | |
190 | * iosys_map_set_vaddr_iomem - Sets a iosys mapping structure to an address in I/O memory | |
191 | * @map: The iosys_map structure | |
192 | * @vaddr_iomem: An I/O-memory address | |
193 | * | |
194 | * Sets the address and the I/O-memory flag. | |
195 | */ | |
196 | static inline void iosys_map_set_vaddr_iomem(struct iosys_map *map, | |
197 | void __iomem *vaddr_iomem) | |
198 | { | |
199 | map->vaddr_iomem = vaddr_iomem; | |
200 | map->is_iomem = true; | |
201 | } | |
202 | ||
203 | /** | |
204 | * iosys_map_is_equal - Compares two iosys mapping structures for equality | |
205 | * @lhs: The iosys_map structure | |
206 | * @rhs: A iosys_map structure to compare with | |
207 | * | |
208 | * Two iosys mapping structures are equal if they both refer to the same type of memory | |
209 | * and to the same address within that memory. | |
210 | * | |
211 | * Returns: | |
212 | * True is both structures are equal, or false otherwise. | |
213 | */ | |
214 | static inline bool iosys_map_is_equal(const struct iosys_map *lhs, | |
215 | const struct iosys_map *rhs) | |
216 | { | |
217 | if (lhs->is_iomem != rhs->is_iomem) | |
218 | return false; | |
219 | else if (lhs->is_iomem) | |
220 | return lhs->vaddr_iomem == rhs->vaddr_iomem; | |
221 | else | |
222 | return lhs->vaddr == rhs->vaddr; | |
223 | } | |
224 | ||
225 | /** | |
226 | * iosys_map_is_null - Tests for a iosys mapping to be NULL | |
227 | * @map: The iosys_map structure | |
228 | * | |
229 | * Depending on the state of struct iosys_map.is_iomem, tests if the | |
230 | * mapping is NULL. | |
231 | * | |
232 | * Returns: | |
233 | * True if the mapping is NULL, or false otherwise. | |
234 | */ | |
235 | static inline bool iosys_map_is_null(const struct iosys_map *map) | |
236 | { | |
237 | if (map->is_iomem) | |
238 | return !map->vaddr_iomem; | |
239 | return !map->vaddr; | |
240 | } | |
241 | ||
242 | /** | |
243 | * iosys_map_is_set - Tests if the iosys mapping has been set | |
244 | * @map: The iosys_map structure | |
245 | * | |
246 | * Depending on the state of struct iosys_map.is_iomem, tests if the | |
247 | * mapping has been set. | |
248 | * | |
249 | * Returns: | |
250 | * True if the mapping is been set, or false otherwise. | |
251 | */ | |
252 | static inline bool iosys_map_is_set(const struct iosys_map *map) | |
253 | { | |
254 | return !iosys_map_is_null(map); | |
255 | } | |
256 | ||
257 | /** | |
258 | * iosys_map_clear - Clears a iosys mapping structure | |
259 | * @map: The iosys_map structure | |
260 | * | |
261 | * Clears all fields to zero, including struct iosys_map.is_iomem, so | |
262 | * mapping structures that were set to point to I/O memory are reset for | |
263 | * system memory. Pointers are cleared to NULL. This is the default. | |
264 | */ | |
265 | static inline void iosys_map_clear(struct iosys_map *map) | |
266 | { | |
267 | if (map->is_iomem) { | |
268 | map->vaddr_iomem = NULL; | |
269 | map->is_iomem = false; | |
270 | } else { | |
271 | map->vaddr = NULL; | |
272 | } | |
273 | } | |
274 | ||
275 | /** | |
cccd73d6 | 276 | * iosys_map_memcpy_to - Memcpy into offset of iosys_map |
7938f421 | 277 | * @dst: The iosys_map structure |
cccd73d6 | 278 | * @dst_offset: The offset from which to copy |
7938f421 LDM |
279 | * @src: The source buffer |
280 | * @len: The number of byte in src | |
281 | * | |
cccd73d6 LDM |
282 | * Copies data into a iosys_map with an offset. The source buffer is in |
283 | * system memory. Depending on the buffer's location, the helper picks the | |
284 | * correct method of accessing the memory. | |
7938f421 | 285 | */ |
cccd73d6 LDM |
286 | static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset, |
287 | const void *src, size_t len) | |
7938f421 LDM |
288 | { |
289 | if (dst->is_iomem) | |
cccd73d6 | 290 | memcpy_toio(dst->vaddr_iomem + dst_offset, src, len); |
7938f421 | 291 | else |
cccd73d6 | 292 | memcpy(dst->vaddr + dst_offset, src, len); |
7938f421 LDM |
293 | } |
294 | ||
e62f25e8 LDM |
295 | /** |
296 | * iosys_map_memcpy_from - Memcpy from iosys_map into system memory | |
297 | * @dst: Destination in system memory | |
298 | * @src: The iosys_map structure | |
299 | * @src_offset: The offset from which to copy | |
300 | * @len: The number of byte in src | |
301 | * | |
302 | * Copies data from a iosys_map with an offset. The dest buffer is in | |
303 | * system memory. Depending on the mapping location, the helper picks the | |
304 | * correct method of accessing the memory. | |
305 | */ | |
306 | static inline void iosys_map_memcpy_from(void *dst, const struct iosys_map *src, | |
307 | size_t src_offset, size_t len) | |
308 | { | |
309 | if (src->is_iomem) | |
310 | memcpy_fromio(dst, src->vaddr_iomem + src_offset, len); | |
311 | else | |
312 | memcpy(dst, src->vaddr + src_offset, len); | |
313 | } | |
314 | ||
7938f421 LDM |
315 | /** |
316 | * iosys_map_incr - Increments the address stored in a iosys mapping | |
317 | * @map: The iosys_map structure | |
318 | * @incr: The number of bytes to increment | |
319 | * | |
320 | * Increments the address stored in a iosys mapping. Depending on the | |
321 | * buffer's location, the correct value will be updated. | |
322 | */ | |
323 | static inline void iosys_map_incr(struct iosys_map *map, size_t incr) | |
324 | { | |
325 | if (map->is_iomem) | |
326 | map->vaddr_iomem += incr; | |
327 | else | |
328 | map->vaddr += incr; | |
329 | } | |
330 | ||
e62f25e8 LDM |
331 | /** |
332 | * iosys_map_memset - Memset iosys_map | |
333 | * @dst: The iosys_map structure | |
334 | * @offset: Offset from dst where to start setting value | |
335 | * @value: The value to set | |
336 | * @len: The number of bytes to set in dst | |
337 | * | |
338 | * Set value in iosys_map. Depending on the buffer's location, the helper | |
339 | * picks the correct method of accessing the memory. | |
340 | */ | |
341 | static inline void iosys_map_memset(struct iosys_map *dst, size_t offset, | |
342 | int value, size_t len) | |
343 | { | |
344 | if (dst->is_iomem) | |
345 | memset_io(dst->vaddr_iomem + offset, value, len); | |
346 | else | |
347 | memset(dst->vaddr + offset, value, len); | |
348 | } | |
349 | ||
5f278dbd LDM |
350 | #ifdef CONFIG_64BIT |
351 | #define __iosys_map_rd_io_u64_case(val_, vaddr_iomem_) \ | |
352 | u64: val_ = readq(vaddr_iomem_) | |
6fb5ee7c LDM |
353 | #define __iosys_map_wr_io_u64_case(val_, vaddr_iomem_) \ |
354 | u64: writeq(val_, vaddr_iomem_) | |
5f278dbd LDM |
355 | #else |
356 | #define __iosys_map_rd_io_u64_case(val_, vaddr_iomem_) \ | |
357 | u64: memcpy_fromio(&(val_), vaddr_iomem_, sizeof(u64)) | |
6fb5ee7c LDM |
358 | #define __iosys_map_wr_io_u64_case(val_, vaddr_iomem_) \ |
359 | u64: memcpy_toio(vaddr_iomem_, &(val_), sizeof(u64)) | |
5f278dbd LDM |
360 | #endif |
361 | ||
362 | #define __iosys_map_rd_io(val__, vaddr_iomem__, type__) _Generic(val__, \ | |
363 | u8: val__ = readb(vaddr_iomem__), \ | |
364 | u16: val__ = readw(vaddr_iomem__), \ | |
365 | u32: val__ = readl(vaddr_iomem__), \ | |
366 | __iosys_map_rd_io_u64_case(val__, vaddr_iomem__)) | |
367 | ||
368 | #define __iosys_map_rd_sys(val__, vaddr__, type__) \ | |
369 | val__ = READ_ONCE(*(type__ *)(vaddr__)) | |
370 | ||
6fb5ee7c LDM |
371 | #define __iosys_map_wr_io(val__, vaddr_iomem__, type__) _Generic(val__, \ |
372 | u8: writeb(val__, vaddr_iomem__), \ | |
373 | u16: writew(val__, vaddr_iomem__), \ | |
374 | u32: writel(val__, vaddr_iomem__), \ | |
375 | __iosys_map_wr_io_u64_case(val__, vaddr_iomem__)) | |
376 | ||
377 | #define __iosys_map_wr_sys(val__, vaddr__, type__) \ | |
378 | WRITE_ONCE(*(type__ *)(vaddr__), val__) | |
379 | ||
e62f25e8 LDM |
380 | /** |
381 | * iosys_map_rd - Read a C-type value from the iosys_map | |
382 | * | |
383 | * @map__: The iosys_map structure | |
384 | * @offset__: The offset from which to read | |
385 | * @type__: Type of the value being read | |
386 | * | |
5f278dbd LDM |
387 | * Read a C type value (u8, u16, u32 and u64) from iosys_map. For other types or |
388 | * if pointer may be unaligned (and problematic for the architecture supported), | |
389 | * use iosys_map_memcpy_from(). | |
e62f25e8 LDM |
390 | * |
391 | * Returns: | |
392 | * The value read from the mapping. | |
393 | */ | |
5f278dbd LDM |
394 | #define iosys_map_rd(map__, offset__, type__) ({ \ |
395 | type__ val; \ | |
396 | if ((map__)->is_iomem) { \ | |
397 | __iosys_map_rd_io(val, (map__)->vaddr_iomem + (offset__), type__);\ | |
398 | } else { \ | |
399 | __iosys_map_rd_sys(val, (map__)->vaddr + (offset__), type__); \ | |
400 | } \ | |
401 | val; \ | |
e62f25e8 LDM |
402 | }) |
403 | ||
404 | /** | |
405 | * iosys_map_wr - Write a C-type value to the iosys_map | |
406 | * | |
407 | * @map__: The iosys_map structure | |
408 | * @offset__: The offset from the mapping to write to | |
409 | * @type__: Type of the value being written | |
410 | * @val__: Value to write | |
411 | * | |
6fb5ee7c LDM |
412 | * Write a C type value (u8, u16, u32 and u64) to the iosys_map. For other types |
413 | * or if pointer may be unaligned (and problematic for the architecture | |
414 | * supported), use iosys_map_memcpy_to() | |
e62f25e8 | 415 | */ |
6fb5ee7c LDM |
416 | #define iosys_map_wr(map__, offset__, type__, val__) ({ \ |
417 | type__ val = (val__); \ | |
418 | if ((map__)->is_iomem) { \ | |
419 | __iosys_map_wr_io(val, (map__)->vaddr_iomem + (offset__), type__);\ | |
420 | } else { \ | |
421 | __iosys_map_wr_sys(val, (map__)->vaddr + (offset__), type__); \ | |
422 | } \ | |
e62f25e8 LDM |
423 | }) |
424 | ||
425 | /** | |
426 | * iosys_map_rd_field - Read a member from a struct in the iosys_map | |
427 | * | |
428 | * @map__: The iosys_map structure | |
429 | * @struct_offset__: Offset from the beggining of the map, where the struct | |
430 | * is located | |
431 | * @struct_type__: The struct describing the layout of the mapping | |
432 | * @field__: Member of the struct to read | |
433 | * | |
434 | * Read a value from iosys_map considering its layout is described by a C struct | |
435 | * starting at @struct_offset__. The field offset and size is calculated and its | |
5f278dbd LDM |
436 | * value read. If the field access would incur in un-aligned access, then either |
437 | * iosys_map_memcpy_from() needs to be used or the architecture must support it. | |
438 | * For example: suppose there is a @struct foo defined as below and the value | |
439 | * ``foo.field2.inner2`` needs to be read from the iosys_map: | |
e62f25e8 LDM |
440 | * |
441 | * .. code-block:: c | |
442 | * | |
443 | * struct foo { | |
444 | * int field1; | |
445 | * struct { | |
446 | * int inner1; | |
447 | * int inner2; | |
448 | * } field2; | |
449 | * int field3; | |
450 | * } __packed; | |
451 | * | |
452 | * This is the expected memory layout of a buffer using iosys_map_rd_field(): | |
453 | * | |
454 | * +------------------------------+--------------------------+ | |
455 | * | Address | Content | | |
456 | * +==============================+==========================+ | |
457 | * | buffer + 0000 | start of mmapped buffer | | |
458 | * | | pointed by iosys_map | | |
459 | * +------------------------------+--------------------------+ | |
460 | * | ... | ... | | |
461 | * +------------------------------+--------------------------+ | |
462 | * | buffer + ``struct_offset__`` | start of ``struct foo`` | | |
463 | * +------------------------------+--------------------------+ | |
464 | * | ... | ... | | |
465 | * +------------------------------+--------------------------+ | |
466 | * | buffer + wwww | ``foo.field2.inner2`` | | |
467 | * +------------------------------+--------------------------+ | |
468 | * | ... | ... | | |
469 | * +------------------------------+--------------------------+ | |
470 | * | buffer + yyyy | end of ``struct foo`` | | |
471 | * +------------------------------+--------------------------+ | |
472 | * | ... | ... | | |
473 | * +------------------------------+--------------------------+ | |
474 | * | buffer + zzzz | end of mmaped buffer | | |
475 | * +------------------------------+--------------------------+ | |
476 | * | |
477 | * Values automatically calculated by this macro or not needed are denoted by | |
478 | * wwww, yyyy and zzzz. This is the code to read that value: | |
479 | * | |
480 | * .. code-block:: c | |
481 | * | |
482 | * x = iosys_map_rd_field(&map, offset, struct foo, field2.inner2); | |
483 | * | |
484 | * Returns: | |
485 | * The value read from the mapping. | |
486 | */ | |
487 | #define iosys_map_rd_field(map__, struct_offset__, struct_type__, field__) ({ \ | |
488 | struct_type__ *s; \ | |
489 | iosys_map_rd(map__, struct_offset__ + offsetof(struct_type__, field__), \ | |
490 | typeof(s->field__)); \ | |
491 | }) | |
492 | ||
493 | /** | |
494 | * iosys_map_wr_field - Write to a member of a struct in the iosys_map | |
495 | * | |
496 | * @map__: The iosys_map structure | |
497 | * @struct_offset__: Offset from the beggining of the map, where the struct | |
498 | * is located | |
499 | * @struct_type__: The struct describing the layout of the mapping | |
500 | * @field__: Member of the struct to read | |
501 | * @val__: Value to write | |
502 | * | |
6fb5ee7c LDM |
503 | * Write a value to the iosys_map considering its layout is described by a C |
504 | * struct starting at @struct_offset__. The field offset and size is calculated | |
505 | * and the @val__ is written. If the field access would incur in un-aligned | |
506 | * access, then either iosys_map_memcpy_to() needs to be used or the | |
507 | * architecture must support it. Refer to iosys_map_rd_field() for expected | |
508 | * usage and memory layout. | |
e62f25e8 LDM |
509 | */ |
510 | #define iosys_map_wr_field(map__, struct_offset__, struct_type__, field__, val__) ({ \ | |
511 | struct_type__ *s; \ | |
512 | iosys_map_wr(map__, struct_offset__ + offsetof(struct_type__, field__), \ | |
513 | typeof(s->field__), val__); \ | |
514 | }) | |
515 | ||
7938f421 | 516 | #endif /* __IOSYS_MAP_H__ */ |