/* pk2serial (c) 2009 Tom Schouten I hereby grant the rights for any use of this software, given that this copyright notice is preserved. Connect to PK2 and start a serial console on stdio. Use this in conjunction with socat for pty/socket/... emulation. */ #include #include #include #include #include #include #include #include #include /* Program options */ int verbose = 0; int usec = 100000; int output_fd = 1; int input_fd = 0; int baud_rate = 9600; int vendor = 0x04d8; int product = 0x0033; int exit_on_eof = 0; int latency_bytes = 32; int power_target = 0; // (1) int reset_target = 0; /* (1) Manual says not to power target in UART mode. I found this to work but somehow be less reliable.. Note that VDD does need to be connected to target power for proper signal clamping. (You need -p with loopback testing!) */ #define ENDPOINT_IN 0x81 #define ENDPOINT_OUT 0x01 #define BUFFER_SIZE 64 #define TIMEOUT 5000 // PK2 commands #define GET_STATUS 0xA2 #define EXECUTE_SCRIPT 0xA6 #define CLR_DOWNLOAD_BFR 0xA7 #define CLR_UPLOAD_BFR 0xA9 #define DOWNLOAD_DATA 0xA8 #define UPLOAD_DATA 0xAA #define ENTER_UART_MODE 0xB3 #define EXIT_UART_MODE 0xB4 #define SET_VDD 0xA0 #define RESET 0xAE #define END_OF_BUFFER 0xAD // PK2 script instructions #define VDD_GND_ON 0xFD #define VDD_ON 0xFF #define VDD_GND_OFF 0xFC #define VDD_OFF 0xFE #define MCLR_GND_ON 0xF7 #define MCLR_GND_OFF 0xF6 #define SET_ISP_PINS 0xF3 usb_dev_handle *handle = NULL; // only one device per program instance. #define LOGL(level, ...) { if (verbose >= level) fprintf(stderr, __VA_ARGS__); } #define LOG(...) { LOGL(1, __VA_ARGS__); } #define LOG_ERROR(...) { fprintf(stderr, __VA_ARGS__); } #define RAISE_ERROR(...) { LOG_ERROR(__VA_ARGS__); exit(1); } void log_buf(char *kind, unsigned char *data, int len) { int i; if (len) { LOGL(2, "%s: ", kind); for(i=0; i> 8) & 0xFF, vfi); } void reset_pk2(void) { COMMAND(RESET); } void reset_hold(int rst) { COMMAND(EXECUTE_SCRIPT, 1, rst ? MCLR_GND_ON : MCLR_GND_OFF); } void reset_release(void) { COMMAND(EXECUTE_SCRIPT, 1, MCLR_GND_OFF); } void target_off(int pwr) { COMMAND(EXECUTE_SCRIPT, 2, VDD_OFF, pwr ? VDD_GND_ON : VDD_GND_OFF); } void target_on(int pwr) { COMMAND(EXECUTE_SCRIPT, 2, VDD_GND_OFF, pwr ? VDD_ON : VDD_OFF); usleep(50000); } void uart_off(void) { COMMAND(EXIT_UART_MODE); } void uart_on(float rate) { int irate = (65536.0f - (((1.0f / rate) - .000003f) / .000000167f)); COMMAND(CLR_UPLOAD_BFR); COMMAND(CLR_DOWNLOAD_BFR); COMMAND(ENTER_UART_MODE, irate & 0xFF, (irate >> 8) & 0xFF); } void dots(int n) { while (n--) LOG("."); } /* Transfer data between fds and the PK2. These use the lower level transfer functions. */ int transfer_input(int fd, int max_tx) { char buf[BUFFER_SIZE]; memset(buf, END_OF_BUFFER, BUFFER_SIZE); buf[0] = DOWNLOAD_DATA; buf[1] = read(fd, buf+2, max_tx); if (-1 == buf[1]) return buf[1]; LOG("I %2d\n", buf[1]); usb_send_buf(buf); return buf[1]; } void transfer_output(int fd) { // from PK2 -> host char buf[BUFFER_SIZE]; char cmd[] = {UPLOAD_DATA}; int written; usb_send(cmd, 1); usb_receive_buf(buf); LOG(" O %2d\n", buf[0]); if (buf[0]) { written = write(fd, buf + 1, buf[0]); if (written != buf[0]) { RAISE_ERROR("output buffer overrun!\n"); } } } /* Signal handler */ int stop = 0; void handler(int signal) { LOG("got signal %d\n", signal); stop = 1; } /* Event loop */ void start_serial(void) { int eof = 0; /* Compute optimal buffer size depending on desired latency. */ int usec_per_byte = (10 * (1000000 / baud_rate)); LOG("latency = %d bytes\n", latency_bytes); /* Setup PK2 with target off. FIXME: connect to running target! */ reset_hold(reset_target); target_off(power_target); if (power_target) set_vdd(5.0); /* Start target. */ target_on(power_target); reset_release(); /* Check if votage is ok. */ LOG("status %04X\n", get_status()); /* Go */ if (-1 == fcntl(input_fd, F_SETFL, O_NONBLOCK)) { RAISE_ERROR("Can't set input_fd O_NONBLOCK.\n"); } uart_on(baud_rate); while(!stop) { if (eof) { /* If input is closed we cn just poll the output. */ LOG("EOF\n"); usleep(usec_per_byte); transfer_output(output_fd); } else { /* When there's room in the buffer, always respond immediately to input data. */ struct timeval timeout; fd_set inset; timeout.tv_sec = 0; timeout.tv_usec = usec_per_byte * latency_bytes; FD_ZERO(&inset); FD_SET(input_fd, &inset); if (-1 == (select(1+input_fd, &inset, NULL, NULL, &timeout))) { if (errno != EINTR) { RAISE_ERROR("select() error\n"); } } /* If there's data, read as much as possible. */ if (FD_ISSET(input_fd, &inset)) { int rv; rv = transfer_input(input_fd, latency_bytes); if (rv == -1) { if (errno == EAGAIN) break; RAISE_ERROR("read(): %s\n",strerror(errno)); } if (rv == 0) { eof = 1; if (exit_on_eof) break; } /* Simple rate limiting mechanism: per transfer, wait for as long as it takes to transfer the bytes. Note that this can be made more efficient when we track an estimate of the number of elements in the upload buffer, keeping the buffer full instead of empty. */ while(rv--) { if (stop) break; usleep(usec_per_byte); } } } transfer_output(output_fd); } /* Cleanup */ uart_off(); reset_pk2(); } void usage(void){ fprintf(stderr, "usage: pk2serial [