X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=serialport.c;h=558322ecfff4e3d529d46998fc7e365b5135a147;hb=235269990bd62a906af871c34839ebb922eecb45;hp=3d15106d585f5ec843a249fc289a9e2c0dcf321a;hpb=073c86bd070a4a5d55c9971ad779a0da32835b17;p=libserialport.git diff --git a/serialport.c b/serialport.c index 3d15106..558322e 100644 --- a/serialport.c +++ b/serialport.c @@ -5,6 +5,7 @@ * Copyright (C) 2010-2012 Uwe Hermann * Copyright (C) 2013 Martin Ling * Copyright (C) 2013 Matthias Heidbrink + * Copyright (C) 2014 Aurelien Jacobs * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as @@ -20,123 +21,8 @@ * along with this program. If not, see . */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef _WIN32 -#include -#include -#else -#include -#include -#include -#include -#include -#include -#endif -#ifdef __APPLE__ -#include -#include -#include -#include -#endif -#ifdef __linux__ -#ifdef HAVE_LIBUDEV -#include "libudev.h" -#endif -#ifndef __ANDROID__ -#include "linux/serial.h" -#endif -#include "linux_termios.h" - -/* TCGETX/TCSETX is not available everywhere. */ -#if defined(TCGETX) && defined(TCSETX) && defined(HAVE_TERMIOX) -#define USE_TERMIOX -#endif -#endif - -/* TIOCINQ/TIOCOUTQ is not available everywhere. */ -#if !defined(TIOCINQ) && defined(FIONREAD) -#define TIOCINQ FIONREAD -#endif -#if !defined(TIOCOUTQ) && defined(FIONWRITE) -#define TIOCOUTQ FIONWRITE -#endif - -/* Non-standard baudrates are not available everywhere. */ -#if defined(HAVE_TERMIOS_SPEED) || defined(HAVE_TERMIOS2_SPEED) -#define USE_TERMIOS_SPEED -#endif - #include "libserialport.h" - -struct sp_port { - char *name; -#ifdef _WIN32 - HANDLE hdl; - COMMTIMEOUTS timeouts; - OVERLAPPED write_ovl; - OVERLAPPED read_ovl; - OVERLAPPED wait_ovl; - DWORD events; - BYTE pending_byte; - BOOL writing; -#else - int fd; -#endif -}; - -struct sp_port_config { - int baudrate; - int bits; - enum sp_parity parity; - int stopbits; - enum sp_rts rts; - enum sp_cts cts; - enum sp_dtr dtr; - enum sp_dsr dsr; - enum sp_xonxoff xon_xoff; -}; - -struct port_data { -#ifdef _WIN32 - DCB dcb; -#else - struct termios term; - int controlbits; - int termiox_supported; - int rts_flow; - int cts_flow; - int dtr_flow; - int dsr_flow; -#endif -}; - -#ifdef _WIN32 -typedef HANDLE event_handle; -#else -typedef int event_handle; -#endif - -/* Standard baud rates. */ -#ifdef _WIN32 -#define BAUD_TYPE DWORD -#define BAUD(n) {CBR_##n, n} -#else -#define BAUD_TYPE speed_t -#define BAUD(n) {B##n, n} -#endif - -struct std_baudrate { - BAUD_TYPE index; - int value; -}; +#include "libserialport_internal.h" const struct std_baudrate std_baudrates[] = { #ifdef _WIN32 @@ -160,51 +46,16 @@ const struct std_baudrate std_baudrates[] = { void (*sp_debug_handler)(const char *format, ...) = sp_default_debug_handler; -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) -#define NUM_STD_BAUDRATES ARRAY_SIZE(std_baudrates) - -/* Debug output macros. */ -#define DEBUG(fmt, ...) do { if (sp_debug_handler) sp_debug_handler(fmt ".\n", ##__VA_ARGS__); } while (0) -#define DEBUG_ERROR(err, fmt, ...) DEBUG("%s returning " #err ": " fmt, __func__, ##__VA_ARGS__) -#define DEBUG_FAIL(fmt, ...) do { \ - char *errmsg = sp_last_error_message(); \ - DEBUG("%s returning SP_ERR_FAIL: "fmt": %s", __func__,##__VA_ARGS__,errmsg); \ - sp_free_error_message(errmsg); \ -} while (0); -#define RETURN() do { DEBUG("%s returning", __func__); return; } while(0) -#define RETURN_CODE(x) do { DEBUG("%s returning " #x, __func__); return x; } while (0) -#define RETURN_CODEVAL(x) do { \ - switch (x) { \ - case SP_OK: RETURN_CODE(SP_OK); \ - case SP_ERR_ARG: RETURN_CODE(SP_ERR_ARG); \ - case SP_ERR_FAIL: RETURN_CODE(SP_ERR_FAIL); \ - case SP_ERR_MEM: RETURN_CODE(SP_ERR_MEM); \ - case SP_ERR_SUPP: RETURN_CODE(SP_ERR_SUPP); \ - } \ -} while (0) -#define RETURN_OK() RETURN_CODE(SP_OK); -#define RETURN_ERROR(err, ...) do { DEBUG_ERROR(err, __VA_ARGS__); return err; } while (0) -#define RETURN_FAIL(...) do { DEBUG_FAIL(__VA_ARGS__); return SP_ERR_FAIL; } while (0) -#define RETURN_VALUE(fmt, x) do { \ - typeof(x) _x = x; \ - DEBUG("%s returning " fmt, __func__, _x); \ - return _x; \ -} while (0) -#define SET_ERROR(val, err, msg) do { DEBUG_ERROR(err, msg); val = err; } while (0) -#define SET_FAIL(val, msg) do { DEBUG_FAIL(msg); val = SP_ERR_FAIL; } while (0) -#define TRACE(fmt, ...) DEBUG("%s(" fmt ") called", __func__, ##__VA_ARGS__) - -#define TRY(x) do { int ret = x; if (ret != SP_OK) RETURN_CODEVAL(ret); } while (0) - -/* Helper functions. */ static enum sp_return get_config(struct sp_port *port, struct port_data *data, struct sp_port_config *config); + static enum sp_return set_config(struct sp_port *port, struct port_data *data, const struct sp_port_config *config); enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr) { struct sp_port *port; + enum sp_return ret; int len; TRACE("%s, %p", portname, port_ptr); @@ -237,6 +88,22 @@ enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_p port->fd = -1; #endif + port->description = NULL; + port->transport = SP_TRANSPORT_NATIVE; + port->usb_bus = -1; + port->usb_address = -1; + port->usb_vid = -1; + port->usb_pid = -1; + port->usb_manufacturer = NULL; + port->usb_product = NULL; + port->usb_serial = NULL; + port->bluetooth_address = NULL; + + if ((ret = get_port_details(port)) != SP_OK) { + sp_free_port(port); + return ret; + } + *port_ptr = port; RETURN_OK(); @@ -252,6 +119,103 @@ char *sp_get_port_name(const struct sp_port *port) RETURN_VALUE("%s", port->name); } +char *sp_get_port_description(struct sp_port *port) +{ + TRACE("%p", port); + + if (!port || !port->description) + return NULL; + + RETURN_VALUE("%s", port->description); +} + +enum sp_transport sp_get_port_transport(struct sp_port *port) +{ + TRACE("%p", port); + + if (!port) + RETURN_ERROR(SP_ERR_ARG, "Null port"); + + RETURN_VALUE("%d", port->transport); +} + +enum sp_return sp_get_port_usb_bus_address(const struct sp_port *port, + int *usb_bus, int *usb_address) +{ + TRACE("%p", port); + + if (!port) + RETURN_ERROR(SP_ERR_ARG, "Null port"); + if (port->transport != SP_TRANSPORT_USB) + RETURN_ERROR(SP_ERR_ARG, "Port does not use USB transport"); + if (port->usb_bus < 0 || port->usb_address < 0) + RETURN_ERROR(SP_ERR_SUPP, "Bus and address values are not available"); + + if (usb_bus) *usb_bus = port->usb_bus; + if (usb_address) *usb_address = port->usb_address; + + RETURN_OK(); +} + +enum sp_return sp_get_port_usb_vid_pid(const struct sp_port *port, + int *usb_vid, int *usb_pid) +{ + TRACE("%p", port); + + if (!port) + RETURN_ERROR(SP_ERR_ARG, "Null port"); + if (port->transport != SP_TRANSPORT_USB) + RETURN_ERROR(SP_ERR_ARG, "Port does not use USB transport"); + if (port->usb_vid < 0 || port->usb_pid < 0) + RETURN_ERROR(SP_ERR_SUPP, "VID:PID values are not available"); + + if (usb_vid) *usb_vid = port->usb_vid; + if (usb_pid) *usb_pid = port->usb_pid; + + RETURN_OK(); +} + +char *sp_get_port_usb_manufacturer(const struct sp_port *port) +{ + TRACE("%p", port); + + if (!port || port->transport != SP_TRANSPORT_USB || !port->usb_manufacturer) + return NULL; + + RETURN_VALUE("%s", port->usb_manufacturer); +} + +char *sp_get_port_usb_product(const struct sp_port *port) +{ + TRACE("%p", port); + + if (!port || port->transport != SP_TRANSPORT_USB || !port->usb_product) + return NULL; + + RETURN_VALUE("%s", port->usb_product); +} + +char *sp_get_port_usb_serial(const struct sp_port *port) +{ + TRACE("%p", port); + + if (!port || port->transport != SP_TRANSPORT_USB || !port->usb_serial) + return NULL; + + RETURN_VALUE("%s", port->usb_serial); +} + +char *sp_get_port_bluetooth_address(const struct sp_port *port) +{ + TRACE("%p", port); + + if (!port || port->transport != SP_TRANSPORT_BLUETOOTH + || !port->bluetooth_address) + return NULL; + + RETURN_VALUE("%s", port->bluetooth_address); +} + enum sp_return sp_get_port_handle(const struct sp_port *port, void *result_ptr) { TRACE("%p, %p", port, result_ptr); @@ -303,6 +267,20 @@ void sp_free_port(struct sp_port *port) if (port->name) free(port->name); + if (port->description) + free(port->description); + if (port->usb_manufacturer) + free(port->usb_manufacturer); + if (port->usb_product) + free(port->usb_product); + if (port->usb_serial) + free(port->usb_serial); + if (port->bluetooth_address) + free(port->bluetooth_address); +#ifdef _WIN32 + if (port->usb_path) + free(port->usb_path); +#endif free(port); @@ -463,72 +441,57 @@ out: IOObjectRelease(iter); out_done: #endif -#if defined(__linux__) && defined(HAVE_LIBUDEV) - 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; +#ifdef __linux__ + char name[PATH_MAX], target[PATH_MAX]; + struct dirent entry, *result; struct serial_struct serial_info; + int len, fd, ioctl_result; + DIR *dir; ret = SP_OK; DEBUG("Enumerating tty devices"); - 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); + if (!(dir = opendir("/sys/class/tty"))) + RETURN_FAIL("could not open /sys/class/tty"); + DEBUG("Iterating over results"); - udev_list_entry_foreach(ud_entry, ud_list) { - path = udev_list_entry_get_name(ud_entry); - DEBUG("Found device %s", path); - 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) { - DEBUG("No parent device, assuming virtual tty"); - udev_device_unref(ud_dev); + while (!readdir_r(dir, &entry, &result) && result) { + len = readlinkat(dirfd(dir), entry.d_name, target, sizeof(target)); + if (len <= 0 || len >= (int) sizeof(target)-1) 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")) { + target[len] = 0; + if (strstr(target, "virtual")) + continue; + snprintf(name, sizeof(name), "/dev/%s", entry.d_name); + DEBUG("Found device %s", name); + if (strstr(target, "serial8250")) { + /* 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. */ DEBUG("serial8250 device, attempting to open"); if ((fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) { DEBUG("open failed, skipping"); - goto skip; + continue; } ioctl_result = ioctl(fd, TIOCGSERIAL, &serial_info); close(fd); if (ioctl_result != 0) { DEBUG("ioctl failed, skipping"); - goto skip; + continue; } if (serial_info.type == PORT_UNKNOWN) { DEBUG("port type is unknown, skipping"); - goto skip; + continue; } } DEBUG("Found port %s", name); list = list_append(list, name); -skip: - udev_device_unref(ud_dev); if (!list) { SET_ERROR(ret, SP_ERR_MEM, "list append failed"); - goto out; + break; } } -out: - udev_enumerate_unref(ud_enumerate); - udev_unref(ud); + closedir(dir); #endif switch (ret) {