// Return a list of all serial ports wxArrayString Serial::port_list() { wxArrayString list; #if defined(LINUX) // This is ugly guessing, but Linux doesn't seem to provide anything else. // If there really is an API to discover serial devices on Linux, please // email [email protected] with the info. Please? // The really BAD aspect is all ports get DTR raised briefly, because linux // has no way to open the port without raising DTR, and there isn't any way // to tell if the device file really represents hardware without opening it. // maybe sysfs or udev provides a useful API?? DIR *dir; struct dirent *f; struct stat st; unsigned int i, len[NUM_DEVNAMES]; char s[512]; int fd, bits; termios mytios; dir = opendir("/dev/"); if (dir == NULL) return list; for (i=0; i<NUM_DEVNAMES; i++) len[i] = strlen(devnames[i]); // Read all the filenames from the /dev directory... while ((f = readdir(dir)) != NULL) { // ignore everything that doesn't begin with "tty" if (strncmp(f->d_name, "tty", 3)) continue; // ignore anything that's not a known serial device name for (i=0; i<NUM_DEVNAMES; i++) { if (!strncmp(f->d_name + 3, devnames[i], len[i])) break; } if (i >= NUM_DEVNAMES) continue; snprintf(s, sizeof(s), "/dev/%s", f->d_name); // check if it's a character type device (almost certainly is) if (stat(s, &st) != 0 || !(st.st_mode & S_IFCHR)) continue; // now see if we can open the file - if the device file is // populating /dev but doesn't actually represent a loaded // driver, this is where we will detect it. fd = open(s, O_RDONLY | O_NOCTTY | O_NONBLOCK); if (fd < 0) { // if permission denied, give benefit of the doubt // (otherwise the port will be invisible to the user // and we won't have a to alert them to the permssion // problem) if (errno == EACCES) list.Add(wxString(s,wxConvUTF8)); // any other error, assume it's not a real device continue; } // does it respond to termios requests? (probably will since // the name began with tty). Some devices where a single // driver exports multiple names will open but this is where // we can really tell if they work with real hardare. if (tcgetattr(fd, &mytios) != 0) { close(fd); continue; } // does it respond to reading the control signals? If it's // some sort of non-serial terminal (eg, pseudo terminals) // this is where we will detect it's not really a serial port if (ioctl(fd, TIOCMGET, &bits) < 0) { close(fd); continue; } // it passed all the tests, it's a serial port, or some sort // of "terminal" that looks exactly like a real serial port! close(fd); // unfortunately, Linux always raises DTR when open is called. // not nice! Every serial port is going to get DTR raised // and then lowered. I wish there were a way to prevent this, // but it seems impossible. list.Add(wxString(s,wxConvUTF8)); } closedir(dir); #elif defined(MACOSX) // adapted from SerialPortSample.c, by Apple // http://developer.apple.com/samplecode/SerialPortSample/listing2.html // and also testserial.c, by Keyspan // http://www.keyspan.com/downloads-files/developer/macosx/KesypanTestSerial.c // www.rxtx.org, src/SerialImp.c seems to be based on Keyspan's testserial.c // neither keyspan nor rxtx properly release memory allocated. // more documentation at: // http://developer.apple.com/documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/chapter_2_section_6.html mach_port_t masterPort; CFMutableDictionaryRef classesToMatch; io_iterator_t serialPortIterator; if (IOMasterPort(NULL, &masterPort) != KERN_SUCCESS) return list; // a usb-serial adaptor is usually considered a "modem", // especially when it implements the CDC class spec classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); if (!classesToMatch) return list; CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDModemType)); if (IOServiceGetMatchingServices(masterPort, classesToMatch, &serialPortIterator) != KERN_SUCCESS) return list; macos_ports(&serialPortIterator, list); IOObjectRelease(serialPortIterator); // but it might be considered a "rs232 port", so repeat this // search for rs232 ports classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); if (!classesToMatch) return list; CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDRS232Type)); if (IOServiceGetMatchingServices(masterPort, classesToMatch, &serialPortIterator) != KERN_SUCCESS) return list; macos_ports(&serialPortIterator, list); IOObjectRelease(serialPortIterator); #elif defined(WINDOWS) // http://msdn.microsoft.com/en-us/library/aa365461(VS.85).aspx // page with 7 ways - not all of them work! // http://www.naughter.com/enumser.html // may be possible to just query the windows registary // http://it.gps678.com/2/ca9c8631868fdd65.html // search in HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM // Vista has some special new way, vista-only // http://msdn2.microsoft.com/en-us/library/aa814070(VS.85).aspx char *buffer, *p; //DWORD size = QUERYDOSDEVICE_BUFFER_SIZE; DWORD ret; buffer = (char *)malloc(QUERYDOSDEVICE_BUFFER_SIZE); if (buffer == NULL) return list; memset(buffer, 0, QUERYDOSDEVICE_BUFFER_SIZE); ret = QueryDosDeviceA(NULL, buffer, QUERYDOSDEVICE_BUFFER_SIZE); if (ret) { printf("Detect Serial using QueryDosDeviceA: "); for (p = buffer; *p; p += strlen(p) + 1) { printf(": %s", p); if (strncmp(p, "COM", 3)) continue; list.Add(wxString(p) + ":"); } } else { char buf[1024]; win32_err(buf); printf("QueryDosDeviceA failed, error \"%s\"\n", buf); printf("Detect Serial using brute force GetDefaultCommConfig probing: "); for (int i=1; i<=32; i++) { printf("try %s", buf); COMMCONFIG cfg; DWORD len; snprintf(buf, sizeof(buf), "COM%d", i); if (GetDefaultCommConfig(buf, &cfg, &len)) { wxString name; name.Printf("COM%d:", i); list.Add(name); printf(": %s", buf); } } } free(buffer); #endif list.Sort(); return list; }
// Open a port, by name. Return 0 on success, non-zero for error int Serial::Open(const wxString& name) { Close(); #if defined(LINUX) struct serial_struct kernel_serial_settings; int bits; port_fd = open(name.mb_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); if (port_fd < 0) { if (errno == EACCES) { error_msg = _("Unable to access ") + wxString(name,wxConvUTF8) + _(", insufficient permission"); // TODO: we could look at the permission bits and owner // to make a better message here } else if (errno == EISDIR) { error_msg = _("Unable to open ") + wxString(name,wxConvUTF8) + _(", Object is a directory, not a serial port"); } else if (errno == ENODEV || errno == ENXIO) { error_msg = _("Unable to open ") + wxString(name,wxConvUTF8) + _(", Serial port hardware not installed"); } else if (errno == ENOENT) { error_msg = _("Unable to open ") + wxString(name,wxConvUTF8) + _(", Device name does not exist"); } else { error_msg = _("Unable to open ") + wxString(name,wxConvUTF8) + _(", ") + wxString(strerror(errno),wxConvUTF8); } return -1; } if (ioctl(port_fd, TIOCMGET, &bits) < 0) { close(port_fd); error_msg = _("Unable to query serial port signals"); return -1; } bits &= ~(TIOCM_DTR | TIOCM_RTS); if (ioctl(port_fd, TIOCMSET, &bits) < 0) { close(port_fd); error_msg = _("Unable to control serial port signals"); return -1; } if (tcgetattr(port_fd, &settings_orig) != 0) { close(port_fd); error_msg = _("Unable to query serial port settings (perhaps not a serial port)"); return -1; } memset(&settings, 0, sizeof(settings)); settings.c_iflag = IGNBRK | IGNPAR; settings.c_cflag = CS8 | CREAD | HUPCL | CLOCAL; Set_baud(baud_rate); if (ioctl(port_fd, TIOCGSERIAL, &kernel_serial_settings) == 0) { kernel_serial_settings.flags |= ASYNC_LOW_LATENCY; ioctl(port_fd, TIOCSSERIAL, &kernel_serial_settings); } tcflush(port_fd, TCIFLUSH); #elif defined(MACOSX) int bits; port_fd = open(name.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); if (port_fd < 0) { error_msg = _("Unable to open ") + name + _(", ") + strerror(errno); return -1; } if (ioctl(port_fd, TIOCEXCL) == -1) { close(port_fd); error_msg = _("Unable to get exclussive access to port ") + name; return -1; } if (ioctl(port_fd, TIOCMGET, &bits) < 0) { close(port_fd); error_msg = _("Unable to query serial port signals on ") + name; return -1; } bits &= ~(TIOCM_DTR | TIOCM_RTS); if (ioctl(port_fd, TIOCMSET, &bits) < 0) { close(port_fd); error_msg = _("Unable to control serial port signals on ") + name; return -1; } if (tcgetattr(port_fd, &settings_orig) < 0) { close(port_fd); error_msg = _("Unable to access baud rate on port ") + name; return -1; } memset(&settings, 0, sizeof(settings)); settings.c_cflag = CS8 | CLOCAL | CREAD | HUPCL; settings.c_iflag = IGNBRK | IGNPAR; Set_baud(baud_rate); tcflush(port_fd, TCIFLUSH); #elif defined(WINDOWS) COMMCONFIG cfg; COMMTIMEOUTS timeouts; int got_default_cfg=0, port_num; char buf[1024], name_createfile[64], name_commconfig[64], *p; DWORD len; snprintf(buf, sizeof(buf), _("%s"), name.c_str()); p = strstr(buf, _("COM")); if (p && sscanf(p + 3, _("%d"), &port_num) == 1) { printf(_("port_num = %d\n"), port_num); snprintf(name_createfile, sizeof(name_createfile), _("\\\\.\\COM%d"), port_num); snprintf(name_commconfig, sizeof(name_commconfig), _("COM%d"), port_num); } else { snprintf(name_createfile, sizeof(name_createfile), _("%s"), name.c_str()); snprintf(name_commconfig, sizeof(name_commconfig), _("%s"), name.c_str()); } len = sizeof(COMMCONFIG); if (GetDefaultCommConfig(name_commconfig, &cfg, &len)) { // this prevents unintentionally raising DTR when opening // might only work on COM1 to COM9 got_default_cfg = 1; memcpy(&port_cfg_orig, &cfg, sizeof(COMMCONFIG)); cfg.dcb.fDtrControl = DTR_CONTROL_DISABLE; cfg.dcb.fRtsControl = RTS_CONTROL_DISABLE; SetDefaultCommConfig(name_commconfig, &cfg, sizeof(COMMCONFIG)); } else { printf(_("error with GetDefaultCommConfig\n")); } port_handle = CreateFile(name_createfile, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (port_handle == INVALID_HANDLE_VALUE) { win32_err(buf); error_msg = _("Unable to open ") + name + _(", ") + buf; return -1; } len = sizeof(COMMCONFIG); if (!GetCommConfig(port_handle, &port_cfg, &len)) { CloseHandle(port_handle); win32_err(buf); error_msg = _("Unable to read communication config on ") + name + _(", ") + buf; return -1; } if (!got_default_cfg) { memcpy(&port_cfg_orig, &port_cfg, sizeof(COMMCONFIG)); } // http://msdn2.microsoft.com/en-us/library/aa363188(VS.85).aspx port_cfg.dcb.BaudRate = baud_rate; port_cfg.dcb.fBinary = TRUE; port_cfg.dcb.fParity = FALSE; port_cfg.dcb.fOutxCtsFlow = FALSE; port_cfg.dcb.fOutxDsrFlow = FALSE; port_cfg.dcb.fDtrControl = DTR_CONTROL_DISABLE; port_cfg.dcb.fDsrSensitivity = FALSE; port_cfg.dcb.fTXContinueOnXoff = TRUE; // ??? port_cfg.dcb.fOutX = FALSE; port_cfg.dcb.fInX = FALSE; port_cfg.dcb.fErrorChar = FALSE; port_cfg.dcb.fNull = FALSE; port_cfg.dcb.fRtsControl = RTS_CONTROL_DISABLE; port_cfg.dcb.fAbortOnError = FALSE; port_cfg.dcb.ByteSize = 8; port_cfg.dcb.Parity = NOPARITY; port_cfg.dcb.StopBits = ONESTOPBIT; if (!SetCommConfig(port_handle, &port_cfg, sizeof(COMMCONFIG))) { CloseHandle(port_handle); win32_err(buf); error_msg = _("Unable to write communication config to ") + name + _(", ") + buf; return -1; } if (!EscapeCommFunction(port_handle, CLRDTR | CLRRTS)) { CloseHandle(port_handle); win32_err(buf); error_msg = _("Unable to control serial port signals on ") + name + _(", ") + buf; return -1; } // http://msdn2.microsoft.com/en-us/library/aa363190(VS.85).aspx // setting to all zeros means timeouts are not used //timeouts.ReadIntervalTimeout = 0; timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.ReadTotalTimeoutConstant = 0; timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = 0; if (!SetCommTimeouts(port_handle, &timeouts)) { CloseHandle(port_handle); win32_err(buf); error_msg = _("Unable to write timeout settings to ") + name + _(", ") + buf; return -1; } #endif port_name = name; port_is_open = 1; return 0; }
int ArduinoSerial::openPort(const char *portName) { _portName = std::string(portName); _portOpened = false; triedToConfigureAgain = false; closeSerial(); fd = 0; _numberOfChannels = 1; #if defined(__APPLE__) || defined(__linux__) struct termios options; fd = open(portName, O_RDWR | O_NOCTTY | O_NDELAY);//O_SHLOCK sleep(2); int bits; #endif #ifdef __APPLE__ std::stringstream sstm; if (fd < 0) { sstm << "Unable to open " << portName << ", " << strerror(errno); errorString = sstm.str(); std::cout<<"Unable to open "<<portName<<", "<<strerror(errno)<<"\n"; return -1; } if (ioctl(fd, TIOCEXCL) == -1) { close(fd); sstm << "Unable to get exclussive access to port " << portName;; errorString = sstm.str(); std::cout<<"Unable to get exclussive access to port "<<portName<<"\n"; return -1; } if (ioctl(fd, TIOCMGET, &bits) < 0) { close(fd); sstm <<"Unable to query serial port signals on " << portName; errorString = sstm.str(); std::cout<<"Unable to query serial port signals on "<<portName<<"\n"; return -1; } bits &= ~(TIOCM_DTR | TIOCM_RTS); if (ioctl(fd, TIOCMSET, &bits) < 0) { close(fd); sstm <<"Unable to control serial port signals on " << portName; errorString = sstm.str(); std::cout<<"Unable to control serial port signals on "<<portName<<"\n"; return -1; } struct termios settings_orig; if (tcgetattr(fd, &settings_orig) < 0) { close(fd); sstm <<"Unable to access baud rate on port " << portName; errorString = sstm.str(); std::cout<<"Unable to access baud rate on port "<<portName<<"\n"; return -1; } #endif #ifdef __linux__ // struct serial_struct kernel_serial_settings; struct termios settings_orig; //struct termios settings; if (fd < 0) { if (errno == EACCES) { std::cout<<"Unable to access "<< portName<< ", insufficient permission"; // TODO: we could look at the permission bits and owner // to make a better message here } else if (errno == EISDIR) { std::cout<< "Unable to open " << portName << ", Object is a directory, not a serial port"; } else if (errno == ENODEV || errno == ENXIO) { std::cout<< "Unable to open " << portName << ", Serial port hardware not installed"; } else if (errno == ENOENT) { std::cout<< "Unable to open " << portName << ", Device name does not exist"; } else { std::cout<< "Unable to open " << portName; //<< } return -1; } if (ioctl(fd, TIOCMGET, &bits) < 0) { close(fd); std::cout<< "Unable to query serial port signals"; return -1; } bits &= ~(TIOCM_DTR | TIOCM_RTS); if (ioctl(fd, TIOCMSET, &bits) < 0) { close(fd); std::cout<< "Unable to control serial port signals"; return -1; } if (tcgetattr(fd, &settings_orig) != 0) { close(fd); std::cout<< "Unable to query serial port settings (perhaps not a serial port)"; return -1; } /*memset(&settings, 0, sizeof(settings)); settings.c_iflag = IGNBRK | IGNPAR; settings.c_cflag = CS8 | CREAD | HUPCL | CLOCAL; Set_baud(baud_rate); if (ioctl(port_fd, TIOCGSERIAL, &kernel_serial_settings) == 0) { kernel_serial_settings.flags |= ASYNC_LOW_LATENCY; ioctl(port_fd, TIOCSSERIAL, &kernel_serial_settings); } tcflush(port_fd, TCIFLUSH);*/ #endif #if defined(__APPLE__) || defined(__linux__) if (fd == -1) { std::cout<<"Can't open serial port\n"; return -1; } fcntl(fd, F_SETFL, 0); // clear all flags on descriptor, enable direct I/O tcgetattr(fd, &options); // read serial port options // enable receiver, set 8 bit data, ignore control lines options.c_cflag |= (CLOCAL | CREAD | CS8); // disable parity generation and 2 stop bits options.c_cflag &= ~(PARENB | CSTOPB); //cfsetispeed(&options, B9600); //cfsetospeed(&options, B9600); cfsetispeed(&options, B230400); cfsetospeed(&options, B230400); // set the new port options tcsetattr(fd, TCSANOW, &options); #endif #ifdef _WIN32 COMMCONFIG cfg; COMMTIMEOUTS timeouts; int got_default_cfg=0, port_num; char buf[1024], name_createfile[64], name_commconfig[64], *p; DWORD len; snprintf(buf, sizeof(buf), "%s", _portName.c_str()); p = strstr(buf, "COM"); if (p && sscanf(p + 3, "%d", &port_num) == 1) { printf("port_num = %d\n", port_num); snprintf(name_createfile, sizeof(name_createfile), "\\\\.\\COM%d", port_num); snprintf(name_commconfig, sizeof(name_commconfig), "COM%d", port_num); } else { snprintf(name_createfile, sizeof(name_createfile), "%s", _portName.c_str()); snprintf(name_commconfig, sizeof(name_commconfig), "%s", _portName.c_str()); } len = sizeof(COMMCONFIG); if (GetDefaultCommConfig(name_commconfig, &cfg, &len)) { // this prevents unintentionally raising DTR when opening // might only work on COM1 to COM9 got_default_cfg = 1; memcpy(&port_cfg_orig, &cfg, sizeof(COMMCONFIG)); cfg.dcb.fDtrControl = DTR_CONTROL_DISABLE; cfg.dcb.fRtsControl = RTS_CONTROL_DISABLE; SetDefaultCommConfig(name_commconfig, &cfg, sizeof(COMMCONFIG)); } else { printf("error with GetDefaultCommConfig\n"); } port_handle = CreateFile(name_createfile, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (port_handle == INVALID_HANDLE_VALUE) { win32_err(buf); //error_msg = "Unable to open " + _portName + ", " + buf; return -1; } len = sizeof(COMMCONFIG); if (!GetCommConfig(port_handle, &port_cfg, &len)) { CloseHandle(port_handle); win32_err(buf); //error_msg = "Unable to read communication config on " + _portName + ", " + buf; return -1; } if (!got_default_cfg) { memcpy(&port_cfg_orig, &port_cfg, sizeof(COMMCONFIG)); } // http://msdn2.microsoft.com/en-us/library/aa363188(VS.85).aspx port_cfg.dcb.BaudRate = 230400; port_cfg.dcb.fBinary = TRUE; port_cfg.dcb.fParity = FALSE; port_cfg.dcb.fOutxCtsFlow = FALSE; port_cfg.dcb.fOutxDsrFlow = FALSE; port_cfg.dcb.fDtrControl = DTR_CONTROL_DISABLE; port_cfg.dcb.fDsrSensitivity = FALSE; port_cfg.dcb.fTXContinueOnXoff = TRUE; // ??? port_cfg.dcb.fOutX = FALSE; port_cfg.dcb.fInX = FALSE; port_cfg.dcb.fErrorChar = FALSE; port_cfg.dcb.fNull = FALSE; port_cfg.dcb.fRtsControl = RTS_CONTROL_DISABLE; port_cfg.dcb.fAbortOnError = FALSE; port_cfg.dcb.ByteSize = 8; port_cfg.dcb.Parity = NOPARITY; port_cfg.dcb.StopBits = ONESTOPBIT; if (!SetCommConfig(port_handle, &port_cfg, sizeof(COMMCONFIG))) { CloseHandle(port_handle); win32_err(buf); //error_msg = "Unable to write communication config to " + name + ", " + buf; return -1; } if (!EscapeCommFunction(port_handle, CLRDTR | CLRRTS)) { CloseHandle(port_handle); win32_err(buf); //error_msg = "Unable to control serial port signals on " + name + ", " + buf; return -1; } // http://msdn2.microsoft.com/en-us/library/aa363190(VS.85).aspx // setting to all zeros means timeouts are not used //timeouts.ReadIntervalTimeout = 0; timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.ReadTotalTimeoutConstant = 0; timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = 0; if (!SetCommTimeouts(port_handle, &timeouts)) { CloseHandle(port_handle); win32_err(buf); //error_msg = "Unable to write timeout settings to " + name + ", " + buf; return -1; } #endif // _WIN32 circularBuffer[0] = '\n'; cBufHead = 0; cBufTail = 0; serialCounter = 0; _portOpened = true; setNumberOfChannelsAndSamplingRate(1, maxSamplingRate()); return fd; }