ожидающая функция getchar() возвращает символ из потока (из буфера UART-а) или ошибку, если поток отвалился и т.п., при этом пустой буфер не считается ошибкой и производится ожидание. Неожидающая же возвращает соответствующий код ошибки при пустом буфере. Так как в МК обычно поток (UART) назначается и практически не используется его освобождение, то коды ошибок бывают типа "переполнение буфера UART-а", превышен таймаут, ну и коды ошибок из UART-а (FE/PE/BREAK). Все эти коды удобно передавать в знаковом int, а не char.
При неожидающей функции getchar() в getline() можно делать выход с соответствующим кодом возврата, типа
ch = getchar();
if(ch < 0) return -2;
но тогда len придётся делать статической и придумывать как её обнулять.
Например, с помощью передачи в ту же getline указателя не на строку, а NULL
int getline(char* buf){
int ch;
static int len = 0;
if(!buf){ // buf == NULL
len = 0;
return -3;
}
...
}