<p><a class="user-mention" data-hovercard-type="user" data-hovercard-url="/hovercards?user_id=4679640" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/kaspar030">@kaspar030</a> After spending some time in recent weeks changing a lot of code to try your suggested approach, I am at a point where I would be very grateful for your help <g-emoji class="g-emoji" alias="sunglasses" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f60e.png">😎</g-emoji> Maybe you have an idea how to solve the problem or get around it.</p>
<p>What we have are CPU specific or default GPIO definitions that usually look like:</p>
<pre><code>#define GPIO_CPU_PIN(x, y)  (x << 4 | y)
#define GPIO_CPU_UNDEF      ((gpio_cpu_t)(UINT_MAX))

enum {
    PORT_A = 0,
    PORT_B = 1,
};

typedef unsigned gpio_cpu_t;
</code></pre>
<p>In a first step, I renamed all <code>gpio_*</code> and <code>GPIO_*</code> definitions to <code>gpio_cpu_*</code> and <code>GPIO_CPU_*</code>, respectively.  To keep the existing code with a lot of binary operations working, CPU peripheral drivers in <code>cpu/*/periph</code> and peripheral configurations in <code>bords/*/periph_conf*.h</code> use only <code>gpio_cpu_*</code> and <code>GPIO_CPU_*</code>. From my point of view, it makes sense that CPU peripheral drivers and peripheral configurations use only CPU GPIOs.</p>
<p>According to your proposed approach and the work of <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/hovercards?user_id=32377642" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/ZetaR60">@ZetaR60</a> ZetaR60, I then defined the GPIO extension API as follows:</p>
<pre><code>#define GPIO_EXT_PORTS      (0x80)           /**< default definition, can be overriden by CPU */
#define GPIO_EXT_PIN(x, y)  ((gpio_t){ .dev = &gpio_ext[x - GPIO_EXT_PORTS], .pin.ext_pin = y })

#define GPIO_UNDEF          { .dev = 0, .pin.cpu_pin = GPIO_CPU_UNDEF }
#define GPIO_PIN(x, y)      ((x < GPIO_EXT_PORTS) ? (gpio_t){ .dev=0, .pin.cpu_pin=GPIO_CPU_PIN(x, y) } \
                                                  : GPIO_EXT_PIN(x, y))

enum {
    PORT_EXT_A = (GPIO_EXT_PORTS + 0),
    PORT_EXT_B = (GPIO_EXT_PORTS + 1),
};

typedef uint8_t gpio_ext_t;

typedef struct gpio_ext_driver {
    /** extension driver functions */
} gpio_ext_driver_t;

typedef struct gpio_ext_dev {
    const gpio_ext_driver_t *driver;    /**< pointer to extension device driver */
    void *dev;                          /**< pointer to extension device descriptor */
} gpio_ext_dev_t;

typedef struct {
    const gpio_ext_dev_t *dev;  /**< extension device, 0 in case of CPU pin */
    union {
        gpio_cpu_t cpu_pin;     /**< pin number in case of CPU GPIO pin */
        gpio_ext_t ext_pin;     /**< pin number in case of GPIO exension device */
    } pin;
} gpio_t;
</code></pre>
<p>where <code>GPIO_EXT_PORTS</code> is any port number greater than the highest CPU port number and the application defines the extender configuration, for example:</p>
<pre><code>static const gpio_ext_dev_t gpio_ext[] =
{
    {
        .driver = &extend_gpio_driver1,
        .dev = (void *)0xbeef,
    },
    {
        .driver = &extend_gpio_driver2,
        .dev = (void *)0xf00,
    },
};
</code></pre>
<p>The goal was to use GPIOs of CPU and extenders in same way in board configurations, e.g., for LED pins:</p>
<pre><code>#define LED0_PIN = GPIO_PIN(PORT_A, 1);
#define LED1_PIN = GPIO_PIN(PORT_EXT_B, 2);
</code></pre>
<p>or driver configurations.</p>
<p>The problem is the <code>GPIO_PIN</code> macro. While it works perfectly for constant <code>gpio_t</code> variables like</p>
<pre><code>static const gpio_t pin1 = GPIO_PIN(PORT_A, 1);
static const gpio_t pin2 = GPIO_PIN(PORT_EXT_A, 2);
</code></pre>
<p>it doesn't work for constant structures with <code>gpio_t</code> members like</p>
<pre><code>typedef struct {
    gpio_t pin1;
    gpio_t pin2;
} foo_config_t;

static const foo_config_t cfg = {
    .pin1 = GPIO_PIN(PORT_B, 3),
    .pin2 = GPIO_PIN(PORT_EXT_B, 4)
};
</code></pre>
<p>because the compiler complains that the initializer isn't a constant. However, we have a lot of driver configuration structures with <code>gpio_t</code> members.</p>
<p>Changing the code in all <code>cpu/*/periph/*.c</code> to use a structure for <code>gpio_t</code> with <code>port</code> and <code>pin</code> fields instead of scalar types to have only one <code>GPIO_PIN</code> macro, is probably unrealistic and to error-prone.</p>
<p>Do you have any idea?</p>

<p style="font-size:small;-webkit-text-size-adjust:none;color:#666;">—<br />You are receiving this because you are subscribed to this thread.<br />Reply to this email directly, <a href="https://github.com/RIOT-OS/RIOT/issues/9690?email_source=notifications&email_token=ABE7WYHTYPJNKEKE6YFXGE3QLMPYPA5CNFSM4FNUQFZKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7RDBNA#issuecomment-534917300">view it on GitHub</a>, or <a href="https://github.com/notifications/unsubscribe-auth/ABE7WYFLL7ZU4GRPZTLHIELQLMPYPANCNFSM4FNUQFZA">mute the thread</a>.<img src="https://github.com/notifications/beacon/ABE7WYCNSJWZKG7KBIEEDZDQLMPYPA5CNFSM4FNUQFZKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7RDBNA.gif" height="1" width="1" alt="" /></p>
<script type="application/ld+json">[
{
"@context": "http://schema.org",
"@type": "EmailMessage",
"potentialAction": {
"@type": "ViewAction",
"target": "https://github.com/RIOT-OS/RIOT/issues/9690?email_source=notifications\u0026email_token=ABE7WYHTYPJNKEKE6YFXGE3QLMPYPA5CNFSM4FNUQFZKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7RDBNA#issuecomment-534917300",
"url": "https://github.com/RIOT-OS/RIOT/issues/9690?email_source=notifications\u0026email_token=ABE7WYHTYPJNKEKE6YFXGE3QLMPYPA5CNFSM4FNUQFZKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7RDBNA#issuecomment-534917300",
"name": "View Issue"
},
"description": "View this Issue on GitHub",
"publisher": {
"@type": "Organization",
"name": "GitHub",
"url": "https://github.com"
}
}
]</script>