]> sigrok.org Git - libserialport.git/blame - linux.c
Build: Use GNUMAKEFLAGS and $@
[libserialport.git] / linux.c
CommitLineData
e33dcf90
ML
1/*
2 * This file is part of the libserialport project.
3 *
f77bb46d 4 * Copyright (C) 2013 Martin Ling <martin-libserialport@earth.li>
e33dcf90
ML
5 * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
6 *
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.
11 *
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.
16 *
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/>.
19 */
20
21#include "libserialport.h"
22#include "libserialport_internal.h"
23
970f279a 24SP_PRIV enum sp_return get_port_details(struct sp_port *port)
e33dcf90 25{
dc422c04
UH
26 /*
27 * Description limited to 127 char, anything longer
28 * would not be user friendly anyway.
29 */
e33dcf90 30 char description[128];
0666ccc7
ML
31 int bus, address;
32 unsigned int vid, pid;
e33dcf90
ML
33 char manufacturer[128], product[128], serial[128];
34 char baddr[32];
35 const char dir_name[] = "/sys/class/tty/%s/device/%s%s";
36 char sub_dir[32] = "", file_name[PATH_MAX];
37 char *ptr, *dev = port->name + 5;
38 FILE *file;
39 int i, count;
46d8b0a0 40 struct stat statbuf;
e33dcf90
ML
41
42 if (strncmp(port->name, "/dev/", 5))
dc422c04 43 RETURN_ERROR(SP_ERR_ARG, "Device name not recognized");
e33dcf90
ML
44
45 snprintf(file_name, sizeof(file_name), "/sys/class/tty/%s", dev);
46d8b0a0 46 if (lstat(file_name, &statbuf) == -1)
47 RETURN_ERROR(SP_ERR_ARG, "Device not found");
48 if (!S_ISLNK(statbuf.st_mode))
49 snprintf(file_name, sizeof(file_name), "/sys/class/tty/%s/device", dev);
e33dcf90 50 count = readlink(file_name, file_name, sizeof(file_name));
dc422c04
UH
51 if (count <= 0 || count >= (int)(sizeof(file_name) - 1))
52 RETURN_ERROR(SP_ERR_ARG, "Device not found");
e33dcf90
ML
53 file_name[count] = 0;
54 if (strstr(file_name, "bluetooth"))
55 port->transport = SP_TRANSPORT_BLUETOOTH;
56 else if (strstr(file_name, "usb"))
57 port->transport = SP_TRANSPORT_USB;
58
59 if (port->transport == SP_TRANSPORT_USB) {
dc422c04 60 for (i = 0; i < 5; i++) {
e33dcf90
ML
61 strcat(sub_dir, "../");
62
63 snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "busnum");
64 if (!(file = fopen(file_name, "r")))
65 continue;
66 count = fscanf(file, "%d", &bus);
67 fclose(file);
68 if (count != 1)
69 continue;
70
71 snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "devnum");
72 if (!(file = fopen(file_name, "r")))
73 continue;
74 count = fscanf(file, "%d", &address);
75 fclose(file);
76 if (count != 1)
77 continue;
78
79 snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "idVendor");
80 if (!(file = fopen(file_name, "r")))
81 continue;
82 count = fscanf(file, "%4x", &vid);
83 fclose(file);
84 if (count != 1)
85 continue;
86
87 snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "idProduct");
88 if (!(file = fopen(file_name, "r")))
89 continue;
90 count = fscanf(file, "%4x", &pid);
91 fclose(file);
92 if (count != 1)
93 continue;
94
95 port->usb_bus = bus;
96 port->usb_address = address;
97 port->usb_vid = vid;
98 port->usb_pid = pid;
99
100 snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "product");
101 if ((file = fopen(file_name, "r"))) {
102 if ((ptr = fgets(description, sizeof(description), file))) {
103 ptr = description + strlen(description) - 1;
104 if (ptr >= description && *ptr == '\n')
105 *ptr = 0;
106 port->description = strdup(description);
107 }
108 fclose(file);
109 }
110 if (!file || !ptr)
111 port->description = strdup(dev);
112
113 snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "manufacturer");
114 if ((file = fopen(file_name, "r"))) {
115 if ((ptr = fgets(manufacturer, sizeof(manufacturer), file))) {
116 ptr = manufacturer + strlen(manufacturer) - 1;
117 if (ptr >= manufacturer && *ptr == '\n')
118 *ptr = 0;
119 port->usb_manufacturer = strdup(manufacturer);
120 }
121 fclose(file);
122 }
123
124 snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "product");
125 if ((file = fopen(file_name, "r"))) {
126 if ((ptr = fgets(product, sizeof(product), file))) {
127 ptr = product + strlen(product) - 1;
128 if (ptr >= product && *ptr == '\n')
129 *ptr = 0;
130 port->usb_product = strdup(product);
131 }
132 fclose(file);
133 }
134
135 snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "serial");
136 if ((file = fopen(file_name, "r"))) {
137 if ((ptr = fgets(serial, sizeof(serial), file))) {
138 ptr = serial + strlen(serial) - 1;
139 if (ptr >= serial && *ptr == '\n')
140 *ptr = 0;
141 port->usb_serial = strdup(serial);
142 }
143 fclose(file);
144 }
145
ea17bfca
UJ
146 /* If present, add serial to description for better identification. */
147 if (port->usb_serial && strlen(port->usb_serial)) {
7c1101dc 148 snprintf(description, sizeof(description),
ea17bfca
UJ
149 "%s - %s", port->description, port->usb_serial);
150 if (port->description)
151 free(port->description);
152 port->description = strdup(description);
153 }
154
e33dcf90
ML
155 break;
156 }
157 } else {
158 port->description = strdup(dev);
159
160 if (port->transport == SP_TRANSPORT_BLUETOOTH) {
161 snprintf(file_name, sizeof(file_name), dir_name, dev, "", "address");
162 if ((file = fopen(file_name, "r"))) {
163 if ((ptr = fgets(baddr, sizeof(baddr), file))) {
164 ptr = baddr + strlen(baddr) - 1;
165 if (ptr >= baddr && *ptr == '\n')
166 *ptr = 0;
167 port->bluetooth_address = strdup(baddr);
168 }
169 fclose(file);
170 }
171 }
172 }
173
174 RETURN_OK();
175}
48a4076f 176
970f279a 177SP_PRIV enum sp_return list_ports(struct sp_port ***list)
48a4076f
AJ
178{
179 char name[PATH_MAX], target[PATH_MAX];
180 struct dirent entry, *result;
f1c916ed 181#ifdef HAVE_STRUCT_SERIAL_STRUCT
48a4076f 182 struct serial_struct serial_info;
12056e2f
MC
183 int ioctl_result;
184#endif
46d8b0a0 185 char buf[sizeof(entry.d_name) + 23];
12056e2f 186 int len, fd;
48a4076f
AJ
187 DIR *dir;
188 int ret = SP_OK;
46d8b0a0 189 struct stat statbuf;
48a4076f
AJ
190
191 DEBUG("Enumerating tty devices");
192 if (!(dir = opendir("/sys/class/tty")))
dc422c04 193 RETURN_FAIL("Could not open /sys/class/tty");
48a4076f
AJ
194
195 DEBUG("Iterating over results");
196 while (!readdir_r(dir, &entry, &result) && result) {
5bd33b7c 197 snprintf(buf, sizeof(buf), "/sys/class/tty/%s", entry.d_name);
46d8b0a0 198 if (lstat(buf, &statbuf) == -1)
199 continue;
200 if (!S_ISLNK(statbuf.st_mode))
201 snprintf(buf, sizeof(buf), "/sys/class/tty/%s/device", entry.d_name);
5bd33b7c 202 len = readlink(buf, target, sizeof(target));
dc422c04 203 if (len <= 0 || len >= (int)(sizeof(target) - 1))
48a4076f
AJ
204 continue;
205 target[len] = 0;
206 if (strstr(target, "virtual"))
207 continue;
208 snprintf(name, sizeof(name), "/dev/%s", entry.d_name);
7890cef6 209 DEBUG_FMT("Found device %s", name);
48a4076f 210 if (strstr(target, "serial8250")) {
dc422c04
UH
211 /*
212 * The serial8250 driver has a hardcoded number of ports.
48a4076f 213 * The only way to tell which actually exist on a given system
dc422c04
UH
214 * is to try to open them and make an ioctl call.
215 */
48a4076f
AJ
216 DEBUG("serial8250 device, attempting to open");
217 if ((fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) {
dc422c04 218 DEBUG("Open failed, skipping");
48a4076f
AJ
219 continue;
220 }
f1c916ed 221#ifdef HAVE_STRUCT_SERIAL_STRUCT
48a4076f 222 ioctl_result = ioctl(fd, TIOCGSERIAL, &serial_info);
12056e2f 223#endif
48a4076f 224 close(fd);
f1c916ed 225#ifdef HAVE_STRUCT_SERIAL_STRUCT
48a4076f
AJ
226 if (ioctl_result != 0) {
227 DEBUG("ioctl failed, skipping");
228 continue;
229 }
230 if (serial_info.type == PORT_UNKNOWN) {
dc422c04 231 DEBUG("Port type is unknown, skipping");
48a4076f
AJ
232 continue;
233 }
12056e2f 234#endif
48a4076f 235 }
7890cef6 236 DEBUG_FMT("Found port %s", name);
48a4076f
AJ
237 *list = list_append(*list, name);
238 if (!list) {
dc422c04 239 SET_ERROR(ret, SP_ERR_MEM, "List append failed");
48a4076f
AJ
240 break;
241 }
242 }
243 closedir(dir);
244
245 return ret;
246}