2 * This file is part of the libserialport project.
4 * Copyright (C) 2013 Martin Ling <martin-libserialport@earth.li>
5 * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "libserialport.h"
23 #include "libserialport_internal.h"
26 * The 'e' modifier for O_CLOEXEC is glibc >= 2.7 only, hence not
27 * portable, so provide an own wrapper for this functionality.
29 static FILE *fopen_cloexec_rdonly(const char *pathname
)
32 if ((fd
= open(pathname
, O_RDONLY
| O_CLOEXEC
)) < 0)
34 return fdopen(fd
, "r");
37 SP_PRIV
enum sp_return
get_port_details(struct sp_port
*port
)
40 * Description limited to 127 char, anything longer
41 * would not be user friendly anyway.
43 char description
[128];
45 unsigned int vid
, pid
;
46 char manufacturer
[128], product
[128], serial
[128];
48 const char dir_name
[] = "/sys/class/tty/%s/device/%s%s";
49 char sub_dir
[32] = "", link_name
[PATH_MAX
], file_name
[PATH_MAX
];
50 char *ptr
, *dev
= port
->name
+ 5;
55 if (strncmp(port
->name
, "/dev/", 5))
56 RETURN_ERROR(SP_ERR_ARG
, "Device name not recognized");
58 snprintf(link_name
, sizeof(link_name
), "/sys/class/tty/%s", dev
);
59 if (lstat(link_name
, &statbuf
) == -1)
60 RETURN_ERROR(SP_ERR_ARG
, "Device not found");
61 if (!S_ISLNK(statbuf
.st_mode
))
62 snprintf(link_name
, sizeof(link_name
), "/sys/class/tty/%s/device", dev
);
63 count
= readlink(link_name
, file_name
, sizeof(file_name
));
64 if (count
<= 0 || count
>= (int)(sizeof(file_name
) - 1))
65 RETURN_ERROR(SP_ERR_ARG
, "Device not found");
67 if (strstr(file_name
, "bluetooth"))
68 port
->transport
= SP_TRANSPORT_BLUETOOTH
;
69 else if (strstr(file_name
, "usb"))
70 port
->transport
= SP_TRANSPORT_USB
;
72 if (port
->transport
== SP_TRANSPORT_USB
) {
73 for (i
= 0; i
< 5; i
++) {
74 strcat(sub_dir
, "../");
76 snprintf(file_name
, sizeof(file_name
), dir_name
, dev
, sub_dir
, "busnum");
77 if (!(file
= fopen_cloexec_rdonly(file_name
)))
79 count
= fscanf(file
, "%d", &bus
);
84 snprintf(file_name
, sizeof(file_name
), dir_name
, dev
, sub_dir
, "devnum");
85 if (!(file
= fopen_cloexec_rdonly(file_name
)))
87 count
= fscanf(file
, "%d", &address
);
92 snprintf(file_name
, sizeof(file_name
), dir_name
, dev
, sub_dir
, "idVendor");
93 if (!(file
= fopen_cloexec_rdonly(file_name
)))
95 count
= fscanf(file
, "%4x", &vid
);
100 snprintf(file_name
, sizeof(file_name
), dir_name
, dev
, sub_dir
, "idProduct");
101 if (!(file
= fopen_cloexec_rdonly(file_name
)))
103 count
= fscanf(file
, "%4x", &pid
);
109 port
->usb_address
= address
;
113 snprintf(file_name
, sizeof(file_name
), dir_name
, dev
, sub_dir
, "product");
114 if ((file
= fopen_cloexec_rdonly(file_name
))) {
115 if ((ptr
= fgets(description
, sizeof(description
), file
))) {
116 ptr
= description
+ strlen(description
) - 1;
117 if (ptr
>= description
&& *ptr
== '\n')
119 port
->description
= strdup(description
);
124 port
->description
= strdup(dev
);
126 snprintf(file_name
, sizeof(file_name
), dir_name
, dev
, sub_dir
, "manufacturer");
127 if ((file
= fopen_cloexec_rdonly(file_name
))) {
128 if ((ptr
= fgets(manufacturer
, sizeof(manufacturer
), file
))) {
129 ptr
= manufacturer
+ strlen(manufacturer
) - 1;
130 if (ptr
>= manufacturer
&& *ptr
== '\n')
132 port
->usb_manufacturer
= strdup(manufacturer
);
137 snprintf(file_name
, sizeof(file_name
), dir_name
, dev
, sub_dir
, "product");
138 if ((file
= fopen_cloexec_rdonly(file_name
))) {
139 if ((ptr
= fgets(product
, sizeof(product
), file
))) {
140 ptr
= product
+ strlen(product
) - 1;
141 if (ptr
>= product
&& *ptr
== '\n')
143 port
->usb_product
= strdup(product
);
148 snprintf(file_name
, sizeof(file_name
), dir_name
, dev
, sub_dir
, "serial");
149 if ((file
= fopen_cloexec_rdonly(file_name
))) {
150 if ((ptr
= fgets(serial
, sizeof(serial
), file
))) {
151 ptr
= serial
+ strlen(serial
) - 1;
152 if (ptr
>= serial
&& *ptr
== '\n')
154 port
->usb_serial
= strdup(serial
);
159 /* If present, add serial to description for better identification. */
160 if (port
->usb_serial
&& strlen(port
->usb_serial
)) {
161 snprintf(description
, sizeof(description
),
162 "%s - %s", port
->description
, port
->usb_serial
);
163 if (port
->description
)
164 free(port
->description
);
165 port
->description
= strdup(description
);
171 port
->description
= strdup(dev
);
173 if (port
->transport
== SP_TRANSPORT_BLUETOOTH
) {
174 snprintf(file_name
, sizeof(file_name
), dir_name
, dev
, "", "address");
175 if ((file
= fopen_cloexec_rdonly(file_name
))) {
176 if ((ptr
= fgets(baddr
, sizeof(baddr
), file
))) {
177 ptr
= baddr
+ strlen(baddr
) - 1;
178 if (ptr
>= baddr
&& *ptr
== '\n')
180 port
->bluetooth_address
= strdup(baddr
);
190 SP_PRIV
enum sp_return
list_ports(struct sp_port
***list
)
192 char name
[PATH_MAX
], target
[PATH_MAX
];
193 struct dirent
*entry
;
194 #ifdef HAVE_STRUCT_SERIAL_STRUCT
195 struct serial_struct serial_info
;
198 char buf
[sizeof(entry
->d_name
) + 23];
204 DEBUG("Enumerating tty devices");
205 if (!(dir
= opendir("/sys/class/tty")))
206 RETURN_FAIL("Could not open /sys/class/tty");
208 DEBUG("Iterating over results");
209 while ((entry
= readdir(dir
))) {
210 snprintf(buf
, sizeof(buf
), "/sys/class/tty/%s", entry
->d_name
);
211 if (lstat(buf
, &statbuf
) == -1)
213 if (!S_ISLNK(statbuf
.st_mode
))
214 snprintf(buf
, sizeof(buf
), "/sys/class/tty/%s/device", entry
->d_name
);
215 len
= readlink(buf
, target
, sizeof(target
));
216 if (len
<= 0 || len
>= (int)(sizeof(target
) - 1))
219 if (strstr(target
, "virtual"))
221 snprintf(name
, sizeof(name
), "/dev/%s", entry
->d_name
);
222 DEBUG_FMT("Found device %s", name
);
223 if (strstr(target
, "serial8250")) {
225 * The serial8250 driver has a hardcoded number of ports.
226 * The only way to tell which actually exist on a given system
227 * is to try to open them and make an ioctl call.
229 DEBUG("serial8250 device, attempting to open");
230 if ((fd
= open(name
, O_RDWR
| O_NONBLOCK
| O_NOCTTY
| O_CLOEXEC
)) < 0) {
231 DEBUG("Open failed, skipping");
234 #ifdef HAVE_STRUCT_SERIAL_STRUCT
235 ioctl_result
= ioctl(fd
, TIOCGSERIAL
, &serial_info
);
238 #ifdef HAVE_STRUCT_SERIAL_STRUCT
239 if (ioctl_result
!= 0) {
240 DEBUG("ioctl failed, skipping");
243 if (serial_info
.type
== PORT_UNKNOWN
) {
244 DEBUG("Port type is unknown, skipping");
249 DEBUG_FMT("Found port %s", name
);
250 *list
= list_append(*list
, name
);
252 SET_ERROR(ret
, SP_ERR_MEM
, "List append failed");