Elaborating on this, in case anyone wants to know more details (I'd edit the original, but for some reason HN decides you can't edit comments after an annoyingly short time limit.)
Say you have N free pins on your controller, and you want to read K keys. Find the smallest integer number of columns such that log2(k/c) + k/c <= n - so, for example, with 108 keys and 20 pins, you can have 7 columns, with 16 rows (keys/column). This means a 3-bit mux, which is relatively easy to find.
Tie each row via diodes to 1 key in each column. Then tie each row to a pin on the controller.
Tie each column to an output of the mux, and tie the input of the mux to Vcc. You'll also probably want to tie each column to a grounding resister as well. Maybe a small cap. (Or just use the chip that does this directly, I can never remember the name. Output selector? Something along that line. "An IC that outputs Vcc on the selected pin and ground on all others".).
Then connect the selector of the mux to the remaining pins on the controller.
To operate, just loop through all valid outputs on the mux repeatedly - probably with gray code, as that way you don't need to make as many pin transitions. Each time you change the output of the mux, read the values of those keys. You'll probably need a mapping of row/column -> key, as well as the previous values of keys. Any time the value of a key changes, output the appropriate packet. (Note: most of the time most keys don't change. As such it may be faster to do a quick check that "old column values == new column values" before checking bits one by one.)
As long as your scan rate is sufficiently high, you shouldn't miss keypresses. (And if you want to get fancy, you can actually borrow a technique from dRAM. Have each key connected from ground to a cap (per-key) to a diode to Vcc. Potentially with a resistor, although the diode might be enough. Then do the above row/column, but tied to the caps / diodes as opposed to the keys themselves. This way you'll be able to read that there is voltage on the cap even if your scan missed when it was down. It also provides a certain amount of debouncing. The disadvantage is that there's a whole lot more per-key.)