USB hub inside modular keyboard for comunication
There is a simple solution to your problem: don't use type-C connectors. 

Since your keyboard is going to be, at best a high speed usb device, you can use Type-A and Type-B (or, better, micro-B) connectors.

Your upstream (hub like) ports will have the type-A connectors on them, and your downstream (device like) ports will have type-B connectors on.

Since neither A to A, nor B to B cables should exist according to the USB standards (and the ones that exist are rare/specialized), you can rest easy knowing the only way the modules would likely be connected is the correct way.

If you think about it, a (non-type C) USB hub has exactly the same problem, which it solves exactly the same way. 

Since your devices are likely to only have one upstream port, you also can't have the user make a loop.

-- edit, since you say you want to use USB C --

If you're determined to use USB C, you can use captive leads to distinguish your upstream ports (captive cable) from your downstream ports (socket). 

If that doesn't appeal to you will need to determine if the port is an upstream port or a downstream port and route it appropriately you'll also need to do at least a minimal amount of PD negotiation. The only devices which I've seen which are able to do this are USB-c docking stations. 

I would recommend looking a development kit for a usb type-c hub chip or chipset which is able to automatically switch ports from upstream or downstream, then you can leave all the gory details of usb-c to them and get on with building your usb peripheral.

If you don't mind about not being fully USB-C compatible the minimum you could get away with is a usb 2.0 hub, some sort of usb 2:1 multiplexer (an analog switch, or a dedicated IC, plenty exist to handle the fact usb-c can be turned over) used as a multiplexer between your usb 2.0's upstream port, and one of it's downstream ports, and the a microcontroller which monitors the CC1 and CC2 lines of all the usb-c ports and sets your multiplexers appropriately.

It's a fairly hard problem, but doable.

-- edit after failing to find any suitable development kits --

I had a look at cypress, via, and on semiconductor, none of them have a usb hub offering that does both upstream and downstream USB (plenty do dual role for power) on a single port, so I think that leaves you with the usb 2.0 switch solution. Which would look something like this:







simulate this circuit &ndash; Schematic created using CircuitLab

Note there's a lot missing from this schematic, but with a uP that has switchable direction, pull up, pull down capabilities on its GPIO (yes the pull ups won't be correct for the USB-c spec, you can do better with some fet switches and the correct value resistors) you will be able to determine which ports are upstream and which are downstream, and signal appropriately. 

Note when you implement this that you add a little randomness to your probing algorithm so you don't end up with ports hunting whilst they try and figure out if they're the upstream port. You'll need to do something to make all the ports identify as upstream ports when there is no power, as a USB-C downstream port won't power up unless it spots an upstream port. (The most budget way of doing that would with a couple of diodes from CC1 and CC2 to a rail which is clamped to zero until the system has power)

You keyboard controller and any other things you want to show up on the bus would hang off one of the (un-shown) other downstream ports on the hub.

-- edit more details --

Note - this is simplified - there are more sophisticated mechanisms used for communication on the CC1/CC2 pins and to arrange power delivery:

At start of day a usb-c DFP (downstream facing port, for example in your laptop) does not supply VBUS.
It instead pulls the CC1 and CC2 pins high with Rp (36k to 3.3V, or 56k to 5.0V).

The UFP (upstream facing port to which it connects) pulls CC1 and CC2 low with Rd (5.1k). The cable has one wire for CC1/CC2, and can be inserted in one of four ways to connect CC1->CC1, CC2->CC2, or CC1->CC2, or CC2->CC1 ).

When the DFP port sees one of CC1 or CC2 pulled low by Rd it applies power.

The point of this design is so that if you connect UFP ports together the don't both try to supply power to each other.

You deisgn your board so that, unpowered, it pulls CC1 of each USB connector low with 5.1k, and leaves the inputs to the mosfets floating.

When an cable from an Downstream facing port (like your computer) is connected, the DFP will detect your port signalling that it an upstream facing port and apply the power. 

The power will flow through the diode, and power your circuit, but it won't flow out through any ports as your mosfet is disabled. Your arduino will boot and will look at all the CC pins to work out which port is the upstream port that has been connected. Once it's done that it'll switch the multiplexer to connect the D+/D- for that port to the USB hub's upstream port.

Next it'll pull up all the CC pins on the remaining ports and look for exactly one of CC1 or CC2 being pulled low. When it sees that it can enable the mosfet on that port and begin supplying power.

You might want a circuits like the following to interface the each set of CC pins to your uP.







simulate this circuit

(again lots omitted in that schematic, if your uP can cope with 0.1mA without going into SCR latchup you can omit D3)

With your uP unpowered, the downstream facing port (in your computer) applies 
5V through 56k to CC1 and CC2. The cable only connects one of CC1 or CC2.

C1 charges, and turns on M1 (you need to make sure M1 is fully on with just 0.8V on the gate). This pulls down CC1 with 5.6k. The DFP applies 5V to Vbus and your uP powers up.

Your uP enables weak pull ups on GPIOS A-C for every port.

The for each CC line (2 per port) it looks at GPIO B. When it finds a port with one GPIO B &lt; 1V, and the other GPIO B ~ 5V, it knows it from the upstream port, and it flips the multiplexer to connect D+/D- to the hub's upstream port.

It then for all the other ports CC lines it sets GPIO A be an output driven high (to start looking for devices), and GPIO C to be an output driven low (to stop indicating that it's a downstream port). It then scans GPIO B and
looks to see if exactly one of GPIO Bs is &lt; 1V for that port, when that is true, it switches on the Vbus mosfet to provide power.

If on the original upstream port, GPIO B goes high the device should disable all the mosfets and, set all the GPIOs to high impedance and start looking for a new upstream port (or more likely lose power)

(The complexity of this circuit is to enable it to do the right thing even when your uP is powered off and its protection diodes are pulling all of its inputs low.)