leonid@255: /* leonid@255: This file is part of CanFestival, a library implementing CanOpen Stack. leonid@255: leonid@255: CanFestival Copyright (C): Edouard TISSERANT and Francis DUPIN leonid@255: CanFestival Win32 port Copyright (C) 2007 Leonid Tochinski, ChattenAssociates, Inc. leonid@255: leonid@255: See COPYING file for copyrights details. leonid@255: leonid@255: This library is free software; you can redistribute it and/or leonid@255: modify it under the terms of the GNU Lesser General Public leonid@255: License as published by the Free Software Foundation; either leonid@255: version 2.1 of the License, or (at your option) any later version. leonid@255: leonid@255: This library is distributed in the hope that it will be useful, leonid@255: but WITHOUT ANY WARRANTY; without even the implied warranty of leonid@255: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU leonid@255: Lesser General Public License for more details. leonid@255: leonid@255: You should have received a copy of the GNU Lesser General Public leonid@255: License along with this library; if not, write to the Free Software leonid@255: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA leonid@255: */ leonid@255: etisserant@145: // can_uvccm_win32 adapter (http://www.gridconnect.com) etisserant@145: // driver for CanFestival-3 Win32 port etisserant@145: etisserant@145: #include etisserant@145: #include etisserant@145: #if 0 // change to 1 if you use boost etisserant@145: #include etisserant@145: #else etisserant@145: #include etisserant@145: #endif etisserant@145: etisserant@145: extern "C" { etisserant@145: #include "can_driver.h" etisserant@145: } etisserant@145: class can_uvccm_win32 etisserant@145: { etisserant@145: public: etisserant@145: class error etisserant@145: { etisserant@145: }; etisserant@145: can_uvccm_win32(s_BOARD *board); etisserant@145: ~can_uvccm_win32(); etisserant@145: bool send(const Message *m); etisserant@145: bool receive(Message *m); etisserant@145: private: etisserant@145: bool open_rs232(int port = 1, int baud_rate = 57600); etisserant@145: bool close_rs232(); etisserant@145: bool get_can_data(const char* can_cmd_buf, long& bufsize, Message* m); etisserant@145: bool set_can_data(const Message& m, std::string& can_cmd); etisserant@145: private: etisserant@145: HANDLE m_port; etisserant@145: HANDLE m_read_event; etisserant@145: HANDLE m_write_event; etisserant@145: std::string m_residual_buffer; etisserant@145: }; etisserant@145: etisserant@145: can_uvccm_win32::can_uvccm_win32(s_BOARD *board) : m_port(INVALID_HANDLE_VALUE), etisserant@145: m_read_event(0), etisserant@145: m_write_event(0) etisserant@145: { etisserant@145: if (strcmp( board->baudrate, "125K") || !open_rs232(1)) etisserant@145: throw error(); etisserant@145: } etisserant@145: etisserant@145: can_uvccm_win32::~can_uvccm_win32() etisserant@145: { etisserant@145: close_rs232(); etisserant@145: } etisserant@145: etisserant@145: bool can_uvccm_win32::send(const Message *m) etisserant@145: { etisserant@145: if (m_port == INVALID_HANDLE_VALUE) greg@267: return true; etisserant@145: etisserant@145: // build can_uvccm_win32 command string etisserant@145: std::string can_cmd; etisserant@145: set_can_data(*m, can_cmd); etisserant@145: etisserant@145: OVERLAPPED overlapped; etisserant@145: ::memset(&overlapped, 0, sizeof overlapped); etisserant@145: overlapped.hEvent = m_write_event; etisserant@145: ::ResetEvent(overlapped.hEvent); etisserant@145: etisserant@145: unsigned long bytes_written = 0; etisserant@145: ::WriteFile(m_port, can_cmd.c_str(), (unsigned long)can_cmd.size(), &bytes_written, &overlapped); etisserant@145: // wait for write operation completion etisserant@145: enum { WRITE_TIMEOUT = 1000 }; etisserant@145: ::WaitForSingleObject(overlapped.hEvent, WRITE_TIMEOUT); etisserant@145: // get number of bytes written etisserant@145: ::GetOverlappedResult(m_port, &overlapped, &bytes_written, FALSE); etisserant@145: etisserant@145: bool result = (bytes_written == can_cmd.size()); greg@267: greg@267: return false; etisserant@145: } etisserant@145: etisserant@145: etisserant@145: bool can_uvccm_win32::receive(Message *m) etisserant@145: { etisserant@145: if (m_port == INVALID_HANDLE_VALUE) etisserant@145: return false; etisserant@145: etisserant@145: long res_buffer_size = (long)m_residual_buffer.size(); etisserant@145: bool result = get_can_data(m_residual_buffer.c_str(), res_buffer_size, m); etisserant@145: if (result) etisserant@145: { etisserant@145: m_residual_buffer.erase(0, res_buffer_size); etisserant@145: return true; etisserant@145: } etisserant@145: etisserant@145: enum { READ_TIMEOUT = 500 }; etisserant@145: etisserant@145: OVERLAPPED overlapped; etisserant@145: ::memset(&overlapped, 0, sizeof overlapped); etisserant@145: overlapped.hEvent = m_read_event; etisserant@145: ::ResetEvent(overlapped.hEvent); etisserant@145: unsigned long event_mask = 0; etisserant@145: etisserant@145: if (FALSE == ::WaitCommEvent(m_port, &event_mask, &overlapped) && ERROR_IO_PENDING == ::GetLastError()) etisserant@145: { etisserant@145: if (WAIT_TIMEOUT == ::WaitForSingleObject(overlapped.hEvent, READ_TIMEOUT)) etisserant@145: return false; etisserant@145: } etisserant@145: etisserant@145: // get number of bytes in the input que etisserant@145: COMSTAT stat; etisserant@145: ::memset(&stat, 0, sizeof stat); etisserant@145: unsigned long errors = 0; etisserant@145: ::ClearCommError(m_port, &errors, &stat); etisserant@145: if (stat.cbInQue == 0) etisserant@145: return false; etisserant@145: char buffer[3000]; etisserant@145: etisserant@145: unsigned long bytes_to_read = min(stat.cbInQue, sizeof (buffer)); etisserant@145: etisserant@145: unsigned long bytes_read = 0; etisserant@145: ::ReadFile(m_port, buffer, bytes_to_read, &bytes_read, &overlapped); etisserant@145: // wait for read operation completion etisserant@145: ::WaitForSingleObject(overlapped.hEvent, READ_TIMEOUT); etisserant@145: // get number of bytes read etisserant@145: ::GetOverlappedResult(m_port, &overlapped, &bytes_read, FALSE); etisserant@145: result = false; etisserant@145: if (bytes_read > 0) etisserant@145: { etisserant@145: m_residual_buffer.append(buffer, bytes_read); etisserant@145: res_buffer_size = (long)m_residual_buffer.size(); etisserant@145: result = get_can_data(m_residual_buffer.c_str(), res_buffer_size, m); etisserant@145: if (result) etisserant@145: m_residual_buffer.erase(0, res_buffer_size); etisserant@145: } etisserant@145: return result; etisserant@145: } etisserant@145: etisserant@145: bool can_uvccm_win32::open_rs232(int port, int baud_rate) etisserant@145: { etisserant@145: if (m_port != INVALID_HANDLE_VALUE) etisserant@145: return true; etisserant@145: etisserant@145: std::ostringstream device_name; etisserant@145: device_name << "COM" << port; etisserant@145: etisserant@145: m_port = ::CreateFile(device_name.str().c_str(), etisserant@145: GENERIC_READ | GENERIC_WRITE, etisserant@145: 0, // exclusive access etisserant@145: NULL, // no security etisserant@145: OPEN_EXISTING, etisserant@145: FILE_FLAG_OVERLAPPED, // overlapped I/O etisserant@145: NULL); // null template etisserant@145: etisserant@145: // Check the returned handle for INVALID_HANDLE_VALUE and then set the buffer sizes. etisserant@145: if (m_port == INVALID_HANDLE_VALUE) etisserant@145: return false; etisserant@145: etisserant@145: // SetCommMask(m_hCom,EV_RXCHAR|EV_TXEMPTY|EV_CTS|EV_DSR|EV_RLSD|EV_BREAK|EV_ERR|EV_RING); // etisserant@145: ::SetCommMask(m_port, EV_RXFLAG); etisserant@145: etisserant@145: COMMTIMEOUTS timeouts; etisserant@145: ::memset(&timeouts, 0, sizeof (timeouts)); etisserant@145: timeouts.ReadIntervalTimeout = -1; etisserant@145: timeouts.ReadTotalTimeoutConstant = 0; etisserant@145: timeouts.ReadTotalTimeoutMultiplier = 0; etisserant@145: timeouts.WriteTotalTimeoutConstant = 5000; etisserant@145: timeouts.WriteTotalTimeoutMultiplier = 0; etisserant@145: SetCommTimeouts(m_port, &timeouts); // etisserant@145: etisserant@145: ::SetupComm(m_port, 1024, 512); // set buffer sizes etisserant@145: etisserant@145: // Port settings are specified in a Data Communication Block (DCB). The easiest way to initialize a DCB is to call GetCommState to fill in its default values, override the values that you want to change and then call SetCommState to set the values. etisserant@145: DCB dcb; etisserant@145: ::memset(&dcb, 0, sizeof (dcb)); etisserant@145: ::GetCommState(m_port, &dcb); etisserant@145: dcb.BaudRate = baud_rate; etisserant@145: dcb.ByteSize = 8; etisserant@145: dcb.Parity = NOPARITY; etisserant@145: dcb.StopBits = ONESTOPBIT; etisserant@145: dcb.fAbortOnError = TRUE; etisserant@145: dcb.EvtChar = 0x0A; // '\n' character etisserant@145: ::SetCommState(m_port, &dcb); etisserant@145: etisserant@145: ::PurgeComm(m_port, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR); etisserant@145: etisserant@145: m_read_event = ::CreateEvent(NULL, TRUE, FALSE, NULL); etisserant@145: m_write_event = ::CreateEvent(NULL, TRUE, FALSE, NULL); etisserant@145: etisserant@145: return true; etisserant@145: } etisserant@145: etisserant@145: bool can_uvccm_win32::close_rs232() etisserant@145: { etisserant@145: if (m_port != INVALID_HANDLE_VALUE) etisserant@145: { etisserant@145: ::PurgeComm(m_port, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR); etisserant@145: ::CloseHandle(m_port); etisserant@145: m_port = INVALID_HANDLE_VALUE; etisserant@145: ::CloseHandle(m_read_event); etisserant@145: m_read_event = 0; etisserant@145: ::CloseHandle(m_write_event); etisserant@145: m_write_event = 0; etisserant@145: m_residual_buffer.clear(); etisserant@145: } etisserant@145: return true; etisserant@145: } etisserant@145: etisserant@145: bool can_uvccm_win32::get_can_data(const char* can_cmd_buf, long& bufsize, Message* m) etisserant@145: { etisserant@145: if (bufsize < 5) etisserant@145: { etisserant@145: bufsize = 0; etisserant@145: return false; etisserant@145: } etisserant@145: etisserant@145: Message msg; etisserant@145: ::memset(&msg, 0 , sizeof (msg)); etisserant@145: char colon = 0, type = 0, request = 0; etisserant@145: std::istringstream buf(std::string(can_cmd_buf, bufsize)); etisserant@365: buf >> colon >> type >> std::hex >> msg.cob_id >> request; etisserant@145: if (colon != ':' || (type != 'S' && type != 'X')) etisserant@145: { etisserant@145: bufsize = 0; etisserant@145: return false; etisserant@145: } etisserant@145: if (request == 'N') etisserant@145: { etisserant@145: msg.rtr = 0; etisserant@145: for (msg.len = 0; msg.len < 8; ++msg.len) etisserant@145: { etisserant@145: std::string data_byte_str; etisserant@145: buf >> std::setw(2) >> data_byte_str; etisserant@145: if (data_byte_str[0] == ';') etisserant@145: break; etisserant@145: long byte_val = -1; etisserant@145: std::istringstream(data_byte_str) >> std::hex >> byte_val; etisserant@145: if (byte_val == -1) etisserant@145: { etisserant@145: bufsize = 0; etisserant@145: return false; etisserant@145: } etisserant@145: msg.data[msg.len] = (UNS8)byte_val; etisserant@145: } etisserant@145: if (msg.len == 8) etisserant@145: { etisserant@145: char semicolon = 0; etisserant@145: buf >> semicolon; etisserant@145: if (semicolon != ';') etisserant@145: { etisserant@145: bufsize = 0; etisserant@145: return false; etisserant@145: } etisserant@145: } etisserant@145: etisserant@145: } etisserant@145: else if (request == 'R') etisserant@145: { etisserant@145: msg.rtr = 1; etisserant@145: buf >> msg.len; etisserant@145: } etisserant@145: else etisserant@145: { etisserant@145: bufsize = 0; etisserant@145: return false; etisserant@145: } etisserant@145: etisserant@145: bufsize = buf.tellg(); etisserant@145: etisserant@145: *m = msg; etisserant@145: return true; etisserant@145: } etisserant@145: etisserant@145: bool can_uvccm_win32::set_can_data(const Message& m, std::string& can_cmd) etisserant@145: { etisserant@145: // build can_uvccm_win32 command string etisserant@145: std::ostringstream can_cmd_str; etisserant@365: can_cmd_str << ":S" << std::hex << m.cob_id; etisserant@145: if (m.rtr == 1) etisserant@145: { etisserant@145: can_cmd_str << 'R' << (long)m.len; etisserant@145: } etisserant@145: else etisserant@145: { etisserant@145: can_cmd_str << 'N'; etisserant@145: for (int i = 0; i < m.len; ++i) etisserant@145: can_cmd_str << std::hex << std::setfill('0') << std::setw(2) << (long)m.data[i]; etisserant@145: } etisserant@145: can_cmd_str << ';'; etisserant@145: can_cmd = can_cmd_str.str(); etisserant@145: #ifdef BOOST_VERSION etisserant@145: boost::to_upper(can_cmd); etisserant@145: #else etisserant@145: std::transform(can_cmd.begin(),can_cmd.end(),can_cmd.begin(),::toupper); etisserant@145: #endif etisserant@145: return true; etisserant@145: } etisserant@145: etisserant@145: etisserant@145: //------------------------------------------------------------------------ etisserant@145: extern "C" hacking@706: UNS8 __stdcall canReceive_driver(CAN_HANDLE fd0, Message *m) etisserant@145: { etisserant@145: return (UNS8)(!(reinterpret_cast(fd0)->receive(m))); etisserant@145: } etisserant@145: etisserant@145: extern "C" hacking@706: UNS8 __stdcall canSend_driver(CAN_HANDLE fd0, Message const *m) etisserant@145: { etisserant@145: return (UNS8)reinterpret_cast(fd0)->send(m); etisserant@145: } etisserant@145: etisserant@145: extern "C" hacking@706: CAN_HANDLE __stdcall canOpen_driver(s_BOARD *board) etisserant@145: { etisserant@145: try etisserant@145: { etisserant@145: return (CAN_HANDLE) new can_uvccm_win32(board); etisserant@145: } etisserant@145: catch (can_uvccm_win32::error&) etisserant@145: { etisserant@145: return NULL; etisserant@145: } etisserant@145: } etisserant@145: etisserant@145: extern "C" hacking@706: int __stdcall canClose_driver(CAN_HANDLE inst) etisserant@145: { etisserant@145: delete reinterpret_cast(inst); etisserant@145: return 1; etisserant@145: } etisserant@145: groke6@384: extern "C" hacking@706: UNS8 __stdcall canChangeBaudRate_driver( CAN_HANDLE fd, char* baud) groke6@384: { groke6@384: return 0; edouard@631: }