You need to deconstruct the sortstring and check all its parts against a whitelist of allowed terms.  
See the following pseudo code. I haven't fully tested it, but lets say it's the idea that counts.  
DELIMITER $$
CREATE PROCEDURE `get_users`(IN sortstring TEXT)
BEGIN
  //check sortstring against a whitelist of allowed sortstrings
  DECLARE sortpart VARCHAR(255);
  DECLARE done BOOLEAN DEFAULT false;
  DECLARE allok BOOLEAN DEFAULT true; 
  DECLARE i INTEGER DEFAULT 1;
  WHILE ((NOT done) AND allOK) DO
    SET sortpart = SUBSTRING_INDEX(sortstring,',',i);
    SET i = i + 1;
    SET done = (sortpart IS NULL);
    IF NOT DONE THEN
      SELECT 1 INTO allok WHERE EXISTS 
        (SELECT 1 FROM whitelist 
         WHERE allowed_sort_claused = sortpart AND tablename = 'users');
    END IF
  END WHILE;
  IF allOK THEN     
    PREPARE statement FROM 
      CONCAT('SELECT username, passhashwithsalt FROM users ',sortstring);
    EXECUTE statement;
    DEALLOCATE PREPARE statement;
  ELSE SELECT 'error' as username, 'error' as passhashwithsalt;
  END IF; 
END$$
See: How to prevent SQL injection with dynamic tablenames?
The error in your code
You cannot use columnnames or SQL-keywords as parameters. You can only use values as parameters.  For that reason your query will never pass prepare.
The ? in SELECT x FROM t1 ? wil just be replaced by SELECT x FROM t1 'ORDER BY field1, field2' Which makes no sense.