/* * Copyright (C) 2016 BlueKitchen GmbH * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * 4. Any redistribution, use, or modification is done solely for * personal benefit and not for any commercial purpose or for * monetary gain. * * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Please inquire about commercial licensing options at * contact@bluekitchen-gmbh.com * */ #define BTSTACK_FILE__ "btstack_uart_block_windows.c" /* * btstack_uart_block_windows.c * * Common code to access serial port via asynchronous block read/write commands * */ #include "btstack_uart_block.h" #include "btstack_run_loop.h" #include "btstack_debug.h" #include /* File control definitions */ #include #include #include // uart config static const btstack_uart_config_t * uart_config; // data source for integration with BTstack Runloop static btstack_data_source_t transport_data_source_read; static btstack_data_source_t transport_data_source_write; // block write static int write_bytes_len; static const uint8_t * write_bytes_data; // block read static uint16_t read_bytes_len; static uint8_t * read_bytes_data; // callbacks static void (*block_sent)(void); static void (*block_received)(void); // port and async control structure static HANDLE serial_port_handle; static OVERLAPPED overlapped_read; static OVERLAPPED overlapped_write; // -- engine that retries send/receive if not all bytes have been transferred static void btstack_uart_windows_send_engine(void){ // start write DWORD bytes_written; BOOL ok = WriteFile(serial_port_handle, // handle write_bytes_data, // (LPCSTR) 8-bit data write_bytes_len, // length &bytes_written, // amount written &overlapped_write); // overlapped structure if (ok){ // assert all bytes written if (bytes_written != write_bytes_len){ log_error("btstack_uart_windows_send_block: requested write %u but %u were written", (int) write_bytes_len, (int) bytes_written); return; } // // TODO: to defer sending done event by enabling POLL Callback for Write // // notify done if (block_sent){ block_sent(); } return; } DWORD err = GetLastError(); if (err != ERROR_IO_PENDING){ log_error("btstack_uart_windows_send_block: error writing"); return; } // IO_PENDING -> wait for completed btstack_run_loop_enable_data_source_callbacks(&transport_data_source_write, DATA_SOURCE_CALLBACK_WRITE); } static void btstack_uart_windows_receive_engine(void){ DWORD bytes_read; BOOL ok = ReadFile(serial_port_handle, // handle read_bytes_data, // (LPCSTR) 8-bit data read_bytes_len, // length &bytes_read, // amount read &overlapped_read); // overlapped structure if (ok){ // assert all bytes read if (bytes_read != read_bytes_len){ log_error("btstack_uart_windows_receive_block: requested read %u but %u were read", (int) read_bytes_len, (int) bytes_read); return; } // // TODO: to defer sending done event by enabling POLL Callback // // notify done if (block_received){ block_received(); } return; } DWORD err = GetLastError(); if (err != ERROR_IO_PENDING){ log_error("error reading"); return; } // IO_PENDING -> wait for completed btstack_run_loop_enable_data_source_callbacks(&transport_data_source_read, DATA_SOURCE_CALLBACK_READ); } // -- overlapped IO handlers for read & write static void btstack_uart_windows_process_write(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) { btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE); DWORD bytes_written; BOOL ok = GetOverlappedResult(serial_port_handle, &overlapped_write, &bytes_written, FALSE); if(!ok){ DWORD err = GetLastError(); if (err == ERROR_IO_INCOMPLETE){ // IO_INCOMPLETE -> wait for completed btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE); } else { log_error("write: error writing"); } return; } // assert all bytes written if (bytes_written != write_bytes_len){ log_debug("write: requested to write %u but %u were written, try again", (int) write_bytes_len, (int) bytes_written); btstack_uart_windows_send_engine(); write_bytes_data += bytes_written; write_bytes_len -= bytes_written; return; } // notify done if (block_sent){ block_sent(); } } static void btstack_uart_windows_process_read(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) { btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); DWORD bytes_read; BOOL ok = GetOverlappedResult(serial_port_handle, &overlapped_read, &bytes_read, FALSE); if(!ok){ DWORD err = GetLastError(); if (err == ERROR_IO_INCOMPLETE){ // IO_INCOMPLETE -> wait for completed btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); } else { log_error("error reading"); } return; } // assert all bytes read if (bytes_read != read_bytes_len){ log_debug("read: requested read %u but %u were read, try again", (int) read_bytes_len, (int) bytes_read); read_bytes_data += bytes_read; read_bytes_len -= bytes_read; btstack_uart_windows_receive_engine(); return; } // notify done if (block_received){ block_received(); } } // -- API implementation static int btstack_uart_windows_init(const btstack_uart_config_t * config){ uart_config = config; return 0; } static void btstack_uart_windows_send_block(const uint8_t *data, uint16_t size){ // setup async write write_bytes_data = data; write_bytes_len = size; // go btstack_uart_windows_send_engine(); } static void btstack_uart_windows_receive_block(uint8_t *buffer, uint16_t len){ // setup async read read_bytes_data = buffer; read_bytes_len = len; // go btstack_uart_windows_receive_engine(); } static void btstack_uart_windows_set_baudrate_option(DCB * serial_params, uint32_t baudrate){ serial_params->BaudRate = baudrate; } static void btstack_uart_windows_set_parity_option(DCB * serial_params, uint32_t parity){ serial_params->Parity = parity; } static void btstack_uart_windows_set_flowcontrol_option(DCB * serial_params, uint32_t flowcontrol){ // Flowcontrol serial_params->fOutxCtsFlow = flowcontrol; serial_params->fRtsControl = flowcontrol ? RTS_CONTROL_HANDSHAKE : 0; } static int btstack_uart_windows_set_baudrate(uint32_t baudrate){ DCB serial_params; memset(&serial_params, 0, sizeof(DCB)); serial_params.DCBlength = sizeof(DCB); int ok = GetCommState(serial_port_handle, &serial_params); if (!ok){ log_error("windows_set_baudrate: Couldn't get serial parameters"); return -1; } btstack_uart_windows_set_baudrate_option(&serial_params, baudrate); ok = SetCommState(serial_port_handle, &serial_params); if (!ok){ log_error("windows_set_baudrate: Couldn't serial parameters"); return -1; } return 0; } static int btstack_uart_windows_set_parity(int parity){ DCB serial_params; memset(&serial_params, 0, sizeof(DCB)); serial_params.DCBlength = sizeof(DCB); int ok = GetCommState(serial_port_handle, &serial_params); if (!ok){ log_error("windows_set_parity: Couldn't get serial parameters"); return -1; } btstack_uart_windows_set_parity_option(&serial_params, parity); ok = SetCommState(serial_port_handle, &serial_params); if (!ok){ log_error("windows_set_parity: Couldn't serial parameters"); return -1; } return 0; } static int btstack_uart_windows_set_flowcontrol(int flowcontrol){ DCB serial_params; memset(&serial_params, 0, sizeof(DCB)); serial_params.DCBlength = sizeof(DCB); int ok = GetCommState(serial_port_handle, &serial_params); if (!ok){ log_error("windows_set_parity: Couldn't get serial parameters"); return -1; } btstack_uart_windows_set_flowcontrol_option(&serial_params, flowcontrol); ok = SetCommState(serial_port_handle, &serial_params); if (!ok){ log_error("windows_set_flowcontrol: Couldn't serial parameters"); return -1; } return 0; } static int btstack_uart_windows_open(void){ const char * device_name = uart_config->device_name; const uint32_t baudrate = uart_config->baudrate; const int flowcontrol = uart_config->flowcontrol; serial_port_handle = CreateFile( device_name, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if (device_name == INVALID_HANDLE_VALUE){ log_error("windows_open: Unable to open port %s", device_name); return -1; } DCB serial_params; memset(&serial_params, 0, sizeof(DCB)); serial_params.DCBlength = sizeof(DCB); int ok; #if 0 // test - try to set internal buffer ok = SetupComm(serial_port_handle, 64, 64); printf("SetupCommL ok %u\n", ok); #endif #if 0 // test - read internal buffer sizes COMMPROP comm_prop; GetCommProperties(serial_port_handle, &comm_prop); printf("dwMaxTxQueue %ld\n", comm_prop.dwMaxTxQueue); printf("dwMaxRxQueue %ld\n", comm_prop.dwMaxRxQueue); printf("dwCurrentTxQueue %ld\n", comm_prop.dwCurrentTxQueue); printf("dwCurrentRxQueue %ld\n", comm_prop.dwCurrentRxQueue); #endif // Caveat: with the default FTDI driver and a FT232R on Windows 10, the default USB RX/TX buffer sizes are 4096 // this causes a problem when data is received back to back, like with SCO audio data // Workaround: manually set these values in the Device Manager to 64 bytes ok = GetCommState(serial_port_handle, &serial_params); if (!ok){ log_error("windows_open: Couldn't get serial parameters"); return -1; } // 8-N-1 serial_params.ByteSize = 8; serial_params.StopBits = ONESTOPBIT; serial_params.Parity = NOPARITY; // baudrate btstack_uart_windows_set_baudrate_option(&serial_params, baudrate); // flow control btstack_uart_windows_set_flowcontrol_option(&serial_params, flowcontrol); // parity none btstack_uart_windows_set_parity_option(&serial_params, 0); // commit changes ok = SetCommState(serial_port_handle, &serial_params); if (!ok){ log_error("windows_open: Couldn't serial parameters"); return -1; } // setup overlapped structures for async io memset(&overlapped_read, 0, sizeof(overlapped_read)); memset(&overlapped_write, 0, sizeof(overlapped_write)); overlapped_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); overlapped_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // setup read + write data sources transport_data_source_read.source.handle = overlapped_read.hEvent; transport_data_source_write.source.handle = overlapped_write.hEvent; btstack_run_loop_set_data_source_handler(&transport_data_source_read, &btstack_uart_windows_process_read); btstack_run_loop_set_data_source_handler(&transport_data_source_write, &btstack_uart_windows_process_write); btstack_run_loop_add_data_source(&transport_data_source_read); btstack_run_loop_add_data_source(&transport_data_source_write); return 0; } static int btstack_uart_windows_close_new(void){ // first remove run loop handler btstack_run_loop_remove_data_source(&transport_data_source_read); btstack_run_loop_remove_data_source(&transport_data_source_write); // note: an event cannot be freed while a kernel function is waiting. // in our single-threaded environment, this cannot happen. // free events CloseHandle(overlapped_read.hEvent); CloseHandle(overlapped_write.hEvent); CloseHandle(serial_port_handle); // set pointers to zero overlapped_read.hEvent = NULL; overlapped_write.hEvent = NULL; serial_port_handle = NULL; return 0; } static void btstack_uart_windows_set_block_received( void (*block_handler)(void)){ block_received = block_handler; } static void btstack_uart_windows_set_block_sent( void (*block_handler)(void)){ block_sent = block_handler; } // static void btstack_uart_windows_set_sleep(uint8_t sleep){ // } // static void btstack_uart_windows_set_csr_irq_handler( void (*csr_irq_handler)(void)){ // } static const btstack_uart_block_t btstack_uart_windows = { /* int (*init)(hci_transport_config_uart_t * config); */ &btstack_uart_windows_init, /* int (*open)(void); */ &btstack_uart_windows_open, /* int (*close)(void); */ &btstack_uart_windows_close_new, /* void (*set_block_received)(void (*handler)(void)); */ &btstack_uart_windows_set_block_received, /* void (*set_block_sent)(void (*handler)(void)); */ &btstack_uart_windows_set_block_sent, /* int (*set_baudrate)(uint32_t baudrate); */ &btstack_uart_windows_set_baudrate, /* int (*set_parity)(int parity); */ &btstack_uart_windows_set_parity, /* int (*set_flowcontrol)(int flowcontrol); */ &btstack_uart_windows_set_flowcontrol, /* void (*receive_block)(uint8_t *buffer, uint16_t len); */ &btstack_uart_windows_receive_block, /* void (*send_block)(const uint8_t *buffer, uint16_t length); */ &btstack_uart_windows_send_block, /* int (*get_supported_sleep_modes); */ NULL, /* void (*set_sleep)(btstack_uart_sleep_mode_t sleep_mode); */ NULL, /* void (*set_wakeup_handler)(void (*handler)(void)); */ NULL, }; const btstack_uart_block_t * btstack_uart_block_windows_instance(void){ return &btstack_uart_windows; }