X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=serialport.c;h=ce306fdffa08a29e2d4026da54c7a4bbb6658f97;hb=24c1a4bb053e517a55eb0f6355b8f362659d4caf;hp=c53a639008fa19e0543d9ea77ced7c19c8c18da4;hpb=64eec30d22f1b821a021bfd4a8d54e169e84731b;p=libserialport.git diff --git a/serialport.c b/serialport.c index c53a639..ce306fd 100644 --- a/serialport.c +++ b/serialport.c @@ -24,17 +24,214 @@ #include #include #include +#include +#include #ifdef _WIN32 #include +#include #else #include #include #endif -#include -#include +#ifdef __APPLE__ +#include +#include +#include +#endif +#ifdef __linux__ +#include "libudev.h" +#include "linux/serial.h" +#endif #include "serialport.h" +static char **sp_list_append(char **list, void *data, size_t len) +{ + void *tmp; + unsigned int count; + for (count = 0; list[count]; count++); + if (!(tmp = realloc(list, sizeof(char *) * (count + 2)))) + goto fail; + list = tmp; + if (!(list[count] = malloc(len))) + goto fail; + memcpy(list[count], data, len); + list[count + 1] = NULL; + return list; +fail: + sp_free_port_list(list); + return NULL; +} + +/** + * List the serial ports available on the system. + * + * @return A null-terminated array of port name strings. + */ +char **sp_list_ports(void) +{ + char **list; + + if (!(list = malloc(sizeof(char *)))) + return NULL; + + list[0] = NULL; + +#ifdef _WIN32 + HKEY key; + TCHAR *value, *data; + DWORD max_value_len, max_data_size, max_data_len; + DWORD value_len, data_size, data_len; + DWORD type, index = 0; + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"), + 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) + return NULL; + if (RegQueryInfoKey(key, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &max_value_len, &max_data_size, NULL, NULL) != ERROR_SUCCESS) + goto out_close; + max_data_len = max_data_size / sizeof(TCHAR); + if (!(value = malloc((max_value_len + 1) * sizeof(TCHAR)))) + goto out_close; + if (!(data = malloc((max_data_len + 1) * sizeof(TCHAR)))) + goto out_free_value; + while ( + value_len = max_value_len, + data_size = max_data_size, + RegEnumValue(key, index, value, &value_len, + NULL, &type, (LPBYTE)data, &data_size) == ERROR_SUCCESS) + { + data_len = data_size / sizeof(TCHAR); + data[data_len] = '\0'; + if (type == REG_SZ) + if (!(list = sp_list_append(list, + data, (data_len + 1) * sizeof(TCHAR)))) + goto out; + index++; + } +out: + free(data); +out_free_value: + free(value); +out_close: + RegCloseKey(key); + return list; +#endif +#ifdef __APPLE__ + mach_port_t master; + CFMutableDictionaryRef classes; + io_iterator_t iter; + char *path; + io_object_t port; + CFTypeRef cf_path; + Boolean result; + + if (IOMasterPort(MACH_PORT_NULL, &master) != KERN_SUCCESS) + return NULL; + + if (!(classes = IOServiceMatching(kIOSerialBSDServiceValue))) + return NULL; + + CFDictionarySetValue(classes, + CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); + + if (!(IOServiceGetMatchingServices(master, classes, &iter))) + return NULL; + + if (!(path = malloc(PATH_MAX))) + goto out_release; + + while ((port = IOIteratorNext(iter))) { + cf_path = IORegistryEntryCreateCFProperty(port, + CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); + if (cf_path) { + result = CFStringGetCString(cf_path, + path, PATH_MAX, kCFStringEncodingASCII); + CFRelease(cf_path); + if (result) + if (!(list = sp_list_append(list, path, strlen(path) + 1))) + { + IOObjectRelease(port); + goto out; + } + } + IOObjectRelease(port); + } + +out: + free(path); +out_release: + IOObjectRelease(iter); + return list; +#endif +#ifdef __linux__ + struct udev *ud; + struct udev_enumerate *ud_enumerate; + struct udev_list_entry *ud_list; + struct udev_list_entry *ud_entry; + const char *path; + struct udev_device *ud_dev, *ud_parent; + const char *name; + const char *driver; + int fd, ioctl_result; + struct serial_struct serial_info; + + ud = udev_new(); + ud_enumerate = udev_enumerate_new(ud); + udev_enumerate_add_match_subsystem(ud_enumerate, "tty"); + udev_enumerate_scan_devices(ud_enumerate); + ud_list = udev_enumerate_get_list_entry(ud_enumerate); + udev_list_entry_foreach(ud_entry, ud_list) + { + path = udev_list_entry_get_name(ud_entry); + ud_dev = udev_device_new_from_syspath(ud, path); + /* If there is no parent device, this is a virtual tty. */ + ud_parent = udev_device_get_parent(ud_dev); + if (ud_parent == NULL) + { + udev_device_unref(ud_dev); + continue; + } + name = udev_device_get_devnode(ud_dev); + /* The serial8250 driver has a hardcoded number of ports. + * The only way to tell which actually exist on a given system + * is to try to open them and make an ioctl call. */ + driver = udev_device_get_driver(ud_parent); + if (driver && !strcmp(driver, "serial8250")) + { + if ((fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) + goto skip; + ioctl_result = ioctl(fd, TIOCGSERIAL, &serial_info); + close(fd); + if (ioctl_result != 0) + goto skip; + if (serial_info.type == PORT_UNKNOWN) + goto skip; + } + list = sp_list_append(list, (void *)name, strlen(name) + 1); +skip: + udev_device_unref(ud_dev); + if (!list) + goto out; + } +out: + udev_enumerate_unref(ud_enumerate); + udev_unref(ud); + return list; +#endif +} + +/** + * Free a port list returned by sp_list_ports. + */ +void sp_free_port_list(char **list) +{ + unsigned int i; + for (i = 0; list[i]; i++) + free(list[i]); + free(list); +} + static int sp_validate_port(struct sp_port *port) { if (port == NULL) @@ -439,7 +636,7 @@ int sp_set_params(struct sp_port *port, int baudrate, return SP_ERR_ARG; } - term.c_iflag &= ~(IXON | IXOFF); + term.c_iflag &= ~(IXON | IXOFF | IXANY); term.c_cflag &= ~CRTSCTS; switch (flowcontrol) { case 0: @@ -449,14 +646,14 @@ int sp_set_params(struct sp_port *port, int baudrate, term.c_cflag |= CRTSCTS; break; case 2: - term.c_iflag |= IXON | IXOFF; + term.c_iflag |= IXON | IXOFF | IXANY; break; default: return SP_ERR_ARG; } term.c_iflag &= ~IGNPAR; - term.c_cflag &= ~(PARODD | PARENB); + term.c_cflag &= ~(PARENB | PARODD); switch (parity) { case SP_PARITY_NONE: term.c_iflag |= IGNPAR; @@ -481,6 +678,9 @@ int sp_set_params(struct sp_port *port, int baudrate, /* Disable canonical mode, and don't echo input characters. */ term.c_lflag &= ~(ICANON | ECHO); + /* Ignore modem status lines; enable receiver */ + term.c_cflag |= (CLOCAL | CREAD); + /* Write the configured settings. */ if (tcsetattr(port->fd, TCSADRAIN, &term) < 0) return SP_ERR_FAIL;