📄 ftdi.c
字号:
ftdi_error_return(-2, "FTDI purge of TX buffer failed"); return 0;}/** Closes the ftdi device. Call ftdi_deinit() if you're cleaning up. \param ftdi pointer to ftdi_context \retval 0: all fine \retval -1: usb_release failed \retval -2: usb_close failed*/int ftdi_usb_close(struct ftdi_context *ftdi){ int rtn = 0; if (usb_release_interface(ftdi->usb_dev, ftdi->interface) != 0) rtn = -1; if (usb_close (ftdi->usb_dev) != 0) rtn = -2; return rtn;}/* ftdi_convert_baudrate returns nearest supported baud rate to that requested. Function is only used internally \internal*/static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi, unsigned short *value, unsigned short *index){ static const char am_adjust_up[8] = {0, 0, 0, 1, 0, 3, 2, 1}; static const char am_adjust_dn[8] = {0, 0, 0, 1, 0, 1, 2, 3}; static const char frac_code[8] = {0, 3, 2, 4, 1, 5, 6, 7}; int divisor, best_divisor, best_baud, best_baud_diff; unsigned long encoded_divisor; int i; if (baudrate <= 0) { // Return error return -1; } divisor = 24000000 / baudrate; if (ftdi->type == TYPE_AM) { // Round down to supported fraction (AM only) divisor -= am_adjust_dn[divisor & 7]; } // Try this divisor and the one above it (because division rounds down) best_divisor = 0; best_baud = 0; best_baud_diff = 0; for (i = 0; i < 2; i++) { int try_divisor = divisor + i; int baud_estimate; int baud_diff; // Round up to supported divisor value if (try_divisor <= 8) { // Round up to minimum supported divisor try_divisor = 8; } else if (ftdi->type != TYPE_AM && try_divisor < 12) { // BM doesn't support divisors 9 through 11 inclusive try_divisor = 12; } else if (divisor < 16) { // AM doesn't support divisors 9 through 15 inclusive try_divisor = 16; } else { if (ftdi->type == TYPE_AM) { // Round up to supported fraction (AM only) try_divisor += am_adjust_up[try_divisor & 7]; if (try_divisor > 0x1FFF8) { // Round down to maximum supported divisor value (for AM) try_divisor = 0x1FFF8; } } else { if (try_divisor > 0x1FFFF) { // Round down to maximum supported divisor value (for BM) try_divisor = 0x1FFFF; } } } // Get estimated baud rate (to nearest integer) baud_estimate = (24000000 + (try_divisor / 2)) / try_divisor; // Get absolute difference from requested baud rate if (baud_estimate < baudrate) { baud_diff = baudrate - baud_estimate; } else { baud_diff = baud_estimate - baudrate; } if (i == 0 || baud_diff < best_baud_diff) { // Closest to requested baud rate so far best_divisor = try_divisor; best_baud = baud_estimate; best_baud_diff = baud_diff; if (baud_diff == 0) { // Spot on! No point trying break; } } } // Encode the best divisor value encoded_divisor = (best_divisor >> 3) | (frac_code[best_divisor & 7] << 14); // Deal with special cases for encoded value if (encoded_divisor == 1) { encoded_divisor = 0; // 3000000 baud } else if (encoded_divisor == 0x4001) { encoded_divisor = 1; // 2000000 baud (BM only) } // Split into "value" and "index" values *value = (unsigned short)(encoded_divisor & 0xFFFF); if(ftdi->type == TYPE_2232C) { *index = (unsigned short)(encoded_divisor >> 8); *index &= 0xFF00; *index |= ftdi->index; } else *index = (unsigned short)(encoded_divisor >> 16); // Return the nearest baud rate return best_baud;}/** Sets the chip baud rate \param ftdi pointer to ftdi_context \param baudrate baud rate to set \retval 0: all fine \retval -1: invalid baudrate \retval -2: setting baudrate failed*/int ftdi_set_baudrate(struct ftdi_context *ftdi, int baudrate){ unsigned short value, index; int actual_baudrate; if (ftdi->bitbang_enabled) { baudrate = baudrate*4; } actual_baudrate = ftdi_convert_baudrate(baudrate, ftdi, &value, &index); if (actual_baudrate <= 0) ftdi_error_return (-1, "Silly baudrate <= 0."); // Check within tolerance (about 5%) if ((actual_baudrate * 2 < baudrate /* Catch overflows */ ) || ((actual_baudrate < baudrate) ? (actual_baudrate * 21 < baudrate * 20) : (baudrate * 21 < actual_baudrate * 20))) ftdi_error_return (-1, "Unsupported baudrate. Note: bitbang baudrates are automatically multiplied by 4"); if (usb_control_msg(ftdi->usb_dev, 0x40, 3, value, index, NULL, 0, ftdi->usb_write_timeout) != 0) ftdi_error_return (-2, "Setting new baudrate failed"); ftdi->baudrate = baudrate; return 0;}/** Set (RS232) line characteristics by Alain Abbas \param ftdi pointer to ftdi_context \param bits Number of bits \param sbit Number of stop bits \param parity Parity mode \retval 0: all fine \retval -1: Setting line property failed*/int ftdi_set_line_property(struct ftdi_context *ftdi, enum ftdi_bits_type bits, enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity){ unsigned short value = bits; switch(parity) { case NONE: value |= (0x00 << 8); break; case ODD: value |= (0x01 << 8); break; case EVEN: value |= (0x02 << 8); break; case MARK: value |= (0x03 << 8); break; case SPACE: value |= (0x04 << 8); break; } switch(sbit) { case STOP_BIT_1: value |= (0x00 << 11); break; case STOP_BIT_15: value |= (0x01 << 11); break; case STOP_BIT_2: value |= (0x02 << 11); break; } if (usb_control_msg(ftdi->usb_dev, 0x40, 0x04, value, ftdi->index, NULL, 0, ftdi->usb_write_timeout) != 0) ftdi_error_return (-1, "Setting new line property failed"); return 0;}/** Writes data in chunks (see ftdi_write_data_set_chunksize()) to the chip \param ftdi pointer to ftdi_context \param buf Buffer with the data \param size Size of the buffer \retval <0: error code from usb_bulk_write() \retval >0: number of bytes written*/int ftdi_write_data(struct ftdi_context *ftdi, unsigned char *buf, int size){ int ret; int offset = 0; int total_written = 0; while (offset < size) { int write_size = ftdi->writebuffer_chunksize; if (offset+write_size > size) write_size = size-offset; ret = usb_bulk_write(ftdi->usb_dev, ftdi->in_ep, buf+offset, write_size, ftdi->usb_write_timeout); if (ret < 0) ftdi_error_return(ret, "usb bulk write failed"); total_written += ret; offset += write_size; } return total_written;}/** Configure write buffer chunk size. Default is 4096. \param ftdi pointer to ftdi_context \param chunksize Chunk size \retval 0: all fine*/int ftdi_write_data_set_chunksize(struct ftdi_context *ftdi, unsigned int chunksize){ ftdi->writebuffer_chunksize = chunksize; return 0;}/** Get write buffer chunk size. \param ftdi pointer to ftdi_context \param chunksize Pointer to store chunk size in \retval 0: all fine*/int ftdi_write_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize){ *chunksize = ftdi->writebuffer_chunksize; return 0;}/** Reads data in chunks (see ftdi_read_data_set_chunksize()) from the chip. Automatically strips the two modem status bytes transfered during every read. \param ftdi pointer to ftdi_context \param buf Buffer to store data in \param size Size of the buffer \retval <0: error code from usb_bulk_read() \retval 0: no data was available \retval >0: number of bytes read \remark This function is not useful in bitbang mode. Use ftdi_read_pins() to get the current state of the pins.*/int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size){ int offset = 0, ret = 1, i, num_of_chunks, chunk_remains; // everything we want is still in the readbuffer? if (size <= ftdi->readbuffer_remaining) { memcpy (buf, ftdi->readbuffer+ftdi->readbuffer_offset, size); // Fix offsets ftdi->readbuffer_remaining -= size; ftdi->readbuffer_offset += size; /* printf("Returning bytes from buffer: %d - remaining: %d\n", size, ftdi->readbuffer_remaining); */ return size; } // something still in the readbuffer, but not enough to satisfy 'size'? if (ftdi->readbuffer_remaining != 0) { memcpy (buf, ftdi->readbuffer+ftdi->readbuffer_offset, ftdi->readbuffer_remaining); // Fix offset offset += ftdi->readbuffer_remaining; } // do the actual USB read while (offset < size && ret > 0) { ftdi->readbuffer_remaining = 0; ftdi->readbuffer_offset = 0; /* returns how much received */ ret = usb_bulk_read (ftdi->usb_dev, ftdi->out_ep, ftdi->readbuffer, ftdi->readbuffer_chunksize, ftdi->usb_read_timeout); if (ret < 0) ftdi_error_return(ret, "usb bulk read failed"); if (ret > 2) { // skip FTDI status bytes. // Maybe stored in the future to enable modem use num_of_chunks = ret / 64; chunk_remains = ret % 64; //printf("ret = %X, num_of_chunks = %X, chunk_remains = %X, readbuffer_offset = %X\n", ret, num_of_chunks, chunk_remains, ftdi->readbuffer_offset); ftdi->readbuffer_offset += 2; ret -= 2; if (ret > 62) { for (i = 1; i < num_of_chunks; i++) memmove (ftdi->readbuffer+ftdi->readbuffer_offset+62*i, ftdi->readbuffer+ftdi->readbuffer_offset+64*i, 62); if (chunk_remains > 2) { memmove (ftdi->readbuffer+ftdi->readbuffer_offset+62*i, ftdi->readbuffer+ftdi->readbuffer_offset+64*i, chunk_remains-2); ret -= 2*num_of_chunks; } else ret -= 2*(num_of_chunks-1)+chunk_remains; } } else if (ret <= 2) { // no more data to read? return offset; } if (ret > 0) { // data still fits in buf? if (offset+ret <= size) { memcpy (buf+offset, ftdi->readbuffer+ftdi->readbuffer_offset, ret); //printf("buf[0] = %X, buf[1] = %X\n", buf[0], buf[1]); offset += ret; /* Did we read exactly the right amount of bytes? */ if (offset == size) //printf("read_data exact rem %d offset %d\n", //ftdi->readbuffer_remaining, offset); return offset; } else { // only copy part of the data or size <= readbuffer_chunksize int part_size = size-offset; memcpy (buf+offset, ftdi->readbuffer+ftdi->readbuffer_offset, part_size); ftdi->readbuffer_offset += part_size; ftdi->readbuffer_remaining = ret-part_size; offset += part_size; /* printf("Returning part: %d - size: %d - offset: %d - ret: %d - remaining: %d\n", part_size, size, offset, ret, ftdi->readbuffer_remaining); */ return offset; } } } // never reached return -127;}/** Configure read buffer chunk size. Default is 4096. Automatically reallocates the buffer. \param ftdi pointer to ftdi_context \param chunksize Chunk size \retval 0: all fine*/int ftdi_read_data_set_chunksize(struct ftdi_context *ftdi, unsigned int chunksize){ unsigned char *new_buf; // Invalidate all remaining data ftdi->readbuffer_offset = 0; ftdi->readbuffer_remaining = 0; if ((new_buf = (unsigned char *)realloc(ftdi->readbuffer, chunksize)) == NULL) ftdi_error_return(-1, "out of memory for readbuffer"); ftdi->readbuffer = new_buf; ftdi->readbuffer_chunksize = chunksize; return 0;}/** Get read buffer chunk size. \param ftdi pointer to ftdi_context \param chunksize Pointer to store chunk size in \retval 0: all fine*/int ftdi_read_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize){ *chunksize = ftdi->readbuffer_chunksize; return 0;}/** Enable bitbang mode. For advanced bitbang modes of the FT2232C chip use ftdi_set_bitmode(). \param ftdi pointer to ftdi_context \param bitmask Bitmask to configure lines. HIGH/ON value configures a line as output. \retval 0: all fine \retval -1: can't enable bitbang mode*/int ftdi_enable_bitbang(struct ftdi_context *ftdi, unsigned char bitmask){ unsigned short usb_val; usb_val = bitmask; // low byte: bitmask /* FT2232C: Set bitbang_mode to 2 to enable SPI */ usb_val |= (ftdi->bitbang_mode << 8); if (usb_control_msg(ftdi->usb_dev, 0x40, 0x0B, usb_val, ftdi->index, NULL, 0, ftdi->usb_write_timeout) != 0) ftdi_error_return(-1, "unable to enter bitbang mode. Perhaps not a BM type chip?");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -