1 /*
2  * Copyright © 2016 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  *
23  */
24 
25 #include <inttypes.h>
26 #include <sys/stat.h>
27 #include <sys/sysmacros.h>
28 #include <sys/mount.h>
29 #include <errno.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <limits.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <i915_drm.h>
38 #include <dirent.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 
42 #include "igt_core.h"
43 #include "igt_sysfs.h"
44 #include "igt_device.h"
45 
46 /**
47  * SECTION:igt_sysfs
48  * @short_description: Support code for sysfs features
49  * @title: sysfs
50  * @include: igt.h
51  *
52  * This library provides helpers to access sysfs features. Right now it only
53  * provides basic support for like igt_sysfs_open().
54  */
55 
readN(int fd,char * buf,int len)56 static int readN(int fd, char *buf, int len)
57 {
58 	int ret, total = 0;
59 	do {
60 		ret = read(fd, buf + total, len - total);
61 		if (ret < 0)
62 			ret = -errno;
63 		if (ret == -EINTR || ret == -EAGAIN)
64 			continue;
65 		if (ret <= 0)
66 			break;
67 		total += ret;
68 	} while (total != len);
69 	return total ?: ret;
70 }
71 
writeN(int fd,const char * buf,int len)72 static int writeN(int fd, const char *buf, int len)
73 {
74 	int ret, total = 0;
75 	do {
76 		ret = write(fd, buf + total, len - total);
77 		if (ret < 0)
78 			ret = -errno;
79 		if (ret == -EINTR || ret == -EAGAIN)
80 			continue;
81 		if (ret <= 0)
82 			break;
83 		total += ret;
84 	} while (total != len);
85 	return total ?: ret;
86 }
87 
88 /**
89  * igt_sysfs_path:
90  * @device: fd of the device
91  * @path: buffer to fill with the sysfs path to the device
92  * @pathlen: length of @path buffer
93  *
94  * This finds the sysfs directory corresponding to @device.
95  *
96  * Returns:
97  * The directory path, or NULL on failure.
98  */
igt_sysfs_path(int device,char * path,int pathlen)99 char *igt_sysfs_path(int device, char *path, int pathlen)
100 {
101 	struct stat st;
102 
103 	if (device < 0)
104 		return NULL;
105 
106 	if (fstat(device, &st) || !S_ISCHR(st.st_mode))
107 		return NULL;
108 
109 	snprintf(path, pathlen, "/sys/dev/char/%d:%d",
110 		 major(st.st_rdev), minor(st.st_rdev));
111 
112 	if (access(path, F_OK))
113 		return NULL;
114 
115 	return path;
116 }
117 
118 /**
119  * igt_sysfs_open:
120  * @device: fd of the device
121  *
122  * This opens the sysfs directory corresponding to device for use
123  * with igt_sysfs_set() and igt_sysfs_get().
124  *
125  * Returns:
126  * The directory fd, or -1 on failure.
127  */
igt_sysfs_open(int device)128 int igt_sysfs_open(int device)
129 {
130 	char path[80];
131 
132 	if (!igt_sysfs_path(device, path, sizeof(path)))
133 		return -1;
134 
135 	return open(path, O_RDONLY);
136 }
137 
138 /**
139  * igt_sysfs_set_parameters:
140  * @device: fd of the device
141  * @parameter: the name of the parameter to set
142  * @fmt: printf-esque format string
143  *
144  * Returns true on success
145  */
igt_sysfs_set_parameter(int device,const char * parameter,const char * fmt,...)146 bool igt_sysfs_set_parameter(int device,
147 			     const char *parameter,
148 			     const char *fmt, ...)
149 {
150 	va_list ap;
151 	int dir;
152 	int ret;
153 
154 	dir = igt_sysfs_open_parameters(device);
155 	if (dir < 0)
156 		return false;
157 
158 	va_start(ap, fmt);
159 	ret = igt_sysfs_vprintf(dir, parameter, fmt, ap);
160 	va_end(ap);
161 
162 	close(dir);
163 
164 	return ret > 0;
165 }
166 
167 /**
168  * igt_sysfs_open_parameters:
169  * @device: fd of the device
170  *
171  * This opens the module parameters directory (under sysfs) corresponding
172  * to the device for use with igt_sysfs_set() and igt_sysfs_get().
173  *
174  * Returns:
175  * The directory fd, or -1 on failure.
176  */
igt_sysfs_open_parameters(int device)177 int igt_sysfs_open_parameters(int device)
178 {
179 	int dir, params = -1;
180 
181 	dir = igt_sysfs_open(device);
182 	if (dir >= 0) {
183 		params = openat(dir,
184 				"device/driver/module/parameters",
185 				O_RDONLY);
186 		close(dir);
187 	}
188 
189 	if (params < 0) { /* builtin? */
190 		drm_version_t version;
191 		char name[32] = "";
192 		char path[PATH_MAX];
193 
194 		memset(&version, 0, sizeof(version));
195 		version.name_len = sizeof(name);
196 		version.name = name;
197 		ioctl(device, DRM_IOCTL_VERSION, &version);
198 
199 		sprintf(path, "/sys/module/%s/parameters", name);
200 		params = open(path, O_RDONLY);
201 	}
202 
203 	return params;
204 }
205 
206 /**
207  * igt_sysfs_write:
208  * @dir: directory for the device from igt_sysfs_open()
209  * @attr: name of the sysfs node to open
210  * @data: the block to write from
211  * @len: the length to write
212  *
213  * This writes @len bytes from @data to the sysfs file.
214  *
215  * Returns:
216  * The number of bytes written, or -errno on error.
217  */
igt_sysfs_write(int dir,const char * attr,const void * data,int len)218 int igt_sysfs_write(int dir, const char *attr, const void *data, int len)
219 {
220 	int fd;
221 
222 	fd = openat(dir, attr, O_WRONLY);
223 	if (fd < 0)
224 		return -errno;
225 
226 	len = writeN(fd, data, len);
227 	close(fd);
228 
229 	return len;
230 }
231 
232 /**
233  * igt_sysfs_read:
234  * @dir: directory for the device from igt_sysfs_open()
235  * @attr: name of the sysfs node to open
236  * @data: the block to read into
237  * @len: the maximum length to read
238  *
239  * This reads @len bytes from the sysfs file to @data
240  *
241  * Returns:
242  * The length read, -errno on failure.
243  */
igt_sysfs_read(int dir,const char * attr,void * data,int len)244 int igt_sysfs_read(int dir, const char *attr, void *data, int len)
245 {
246 	int fd;
247 
248 	fd = openat(dir, attr, O_RDONLY);
249 	if (fd < 0)
250 		return -errno;
251 
252 	len = readN(fd, data, len);
253 	close(fd);
254 
255 	return len;
256 }
257 
258 /**
259  * igt_sysfs_set:
260  * @dir: directory for the device from igt_sysfs_open()
261  * @attr: name of the sysfs node to open
262  * @value: the string to write
263  *
264  * This writes the value to the sysfs file.
265  *
266  * Returns:
267  * True on success, false on failure.
268  */
igt_sysfs_set(int dir,const char * attr,const char * value)269 bool igt_sysfs_set(int dir, const char *attr, const char *value)
270 {
271 	int len = strlen(value);
272 	return igt_sysfs_write(dir, attr, value, len) == len;
273 }
274 
275 /**
276  * igt_sysfs_get:
277  * @dir: directory for the device from igt_sysfs_open()
278  * @attr: name of the sysfs node to open
279  *
280  * This reads the value from the sysfs file.
281  *
282  * Returns:
283  * A nul-terminated string, must be freed by caller after use, or NULL
284  * on failure.
285  */
igt_sysfs_get(int dir,const char * attr)286 char *igt_sysfs_get(int dir, const char *attr)
287 {
288 	char *buf;
289 	int len, offset, rem;
290 	int ret, fd;
291 
292 	fd = openat(dir, attr, O_RDONLY);
293 	if (fd < 0)
294 		return NULL;
295 
296 	offset = 0;
297 	len = 64;
298 	rem = len - offset - 1;
299 	buf = malloc(len);
300 	if (!buf)
301 		goto out;
302 
303 	while ((ret = readN(fd, buf + offset, rem)) == rem) {
304 		char *newbuf;
305 
306 		newbuf = realloc(buf, 2*len);
307 		if (!newbuf)
308 			break;
309 
310 		buf = newbuf;
311 		len *= 2;
312 		offset += ret;
313 		rem = len - offset - 1;
314 	}
315 
316 	if (ret > 0)
317 		offset += ret;
318 	buf[offset] = '\0';
319 	while (offset > 0 && buf[offset-1] == '\n')
320 		buf[--offset] = '\0';
321 
322 out:
323 	close(fd);
324 	return buf;
325 }
326 
327 /**
328  * igt_sysfs_scanf:
329  * @dir: directory for the device from igt_sysfs_open()
330  * @attr: name of the sysfs node to open
331  * @fmt: scanf format string
332  * @...: Additional paramaters to store the scaned input values
333  *
334  * scanf() wrapper for sysfs.
335  *
336  * Returns:
337  * Number of values successfully scanned (which can be 0), EOF on errors or
338  * premature end of file.
339  */
igt_sysfs_scanf(int dir,const char * attr,const char * fmt,...)340 int igt_sysfs_scanf(int dir, const char *attr, const char *fmt, ...)
341 {
342 	FILE *file;
343 	int fd;
344 	int ret = -1;
345 
346 	fd = openat(dir, attr, O_RDONLY);
347 	if (fd < 0)
348 		return -1;
349 
350 	file = fdopen(fd, "r");
351 	if (file) {
352 		va_list ap;
353 
354 		va_start(ap, fmt);
355 		ret = vfscanf(file, fmt, ap);
356 		va_end(ap);
357 
358 		fclose(file);
359 	} else {
360 		close(fd);
361 	}
362 
363 	return ret;
364 }
365 
igt_sysfs_vprintf(int dir,const char * attr,const char * fmt,va_list ap)366 int igt_sysfs_vprintf(int dir, const char *attr, const char *fmt, va_list ap)
367 {
368 	char stack[128], *buf = stack;
369 	va_list tmp;
370 	int ret, fd;
371 
372 	fd = openat(dir, attr, O_WRONLY);
373 	if (fd < 0)
374 		return -errno;
375 
376 	va_copy(tmp, ap);
377 	ret = vsnprintf(buf, sizeof(stack), fmt, tmp);
378 	va_end(tmp);
379 	if (ret < 0)
380 		return -EINVAL;
381 
382 	if (ret > sizeof(stack)) {
383 		unsigned int len = ret + 1;
384 
385 		buf = malloc(len);
386 		if (!buf)
387 			return -ENOMEM;
388 
389 		ret = vsnprintf(buf, ret, fmt, ap);
390 		if (ret > len) {
391 			free(buf);
392 			return -EINVAL;
393 		}
394 	}
395 
396 	ret = writeN(fd, buf, ret);
397 
398 	close(fd);
399 	if (buf != stack)
400 		free(buf);
401 
402 	return ret;
403 }
404 
405 /**
406  * igt_sysfs_printf:
407  * @dir: directory for the device from igt_sysfs_open()
408  * @attr: name of the sysfs node to open
409  * @fmt: printf format string
410  * @...: Additional paramaters to store the scaned input values
411  *
412  * printf() wrapper for sysfs.
413  *
414  * Returns:
415  * Number of characters written, negative value on error.
416  */
igt_sysfs_printf(int dir,const char * attr,const char * fmt,...)417 int igt_sysfs_printf(int dir, const char *attr, const char *fmt, ...)
418 {
419 	va_list ap;
420 	int ret;
421 
422 	va_start(ap, fmt);
423 	ret = igt_sysfs_vprintf(dir, attr, fmt, ap);
424 	va_end(ap);
425 
426 	return ret;
427 }
428 
429 /**
430  * igt_sysfs_get_u32:
431  * @dir: directory for the device from igt_sysfs_open()
432  * @attr: name of the sysfs node to open
433  *
434  * Convenience wrapper to read a unsigned 32bit integer from a sysfs file.
435  *
436  * Returns:
437  * The value read.
438  */
igt_sysfs_get_u32(int dir,const char * attr)439 uint32_t igt_sysfs_get_u32(int dir, const char *attr)
440 {
441 	uint32_t result;
442 
443 	if (igt_sysfs_scanf(dir, attr, "%u", &result) != 1)
444 		return 0;
445 
446 	return result;
447 }
448 
449 /**
450  * igt_sysfs_set_u32:
451  * @dir: directory for the device from igt_sysfs_open()
452  * @attr: name of the sysfs node to open
453  * @value: value to set
454  *
455  * Convenience wrapper to write a unsigned 32bit integer to a sysfs file.
456  *
457  * Returns:
458  * True if successfully written
459  */
igt_sysfs_set_u32(int dir,const char * attr,uint32_t value)460 bool igt_sysfs_set_u32(int dir, const char *attr, uint32_t value)
461 {
462 	return igt_sysfs_printf(dir, attr, "%u", value) > 0;
463 }
464 
465 /**
466  * igt_sysfs_get_boolean:
467  * @dir: directory for the device from igt_sysfs_open()
468  * @attr: name of the sysfs node to open
469  *
470  * Convenience wrapper to read a boolean sysfs file.
471  *
472  * Returns:
473  * The value read.
474  */
igt_sysfs_get_boolean(int dir,const char * attr)475 bool igt_sysfs_get_boolean(int dir, const char *attr)
476 {
477 	int result;
478 
479 	if (igt_sysfs_scanf(dir, attr, "%d", &result) != 1)
480 		return false;
481 
482 	return result;
483 }
484 
485 /**
486  * igt_sysfs_set_boolean:
487  * @dir: directory for the device from igt_sysfs_open()
488  * @attr: name of the sysfs node to open
489  * @value: value to set
490  *
491  * Convenience wrapper to write a boolean sysfs file.
492  *
493  * Returns:
494  * The value read.
495  */
igt_sysfs_set_boolean(int dir,const char * attr,bool value)496 bool igt_sysfs_set_boolean(int dir, const char *attr, bool value)
497 {
498 	return igt_sysfs_printf(dir, attr, "%d", value) == 1;
499 }
500 
bind_con(const char * name,bool enable)501 static void bind_con(const char *name, bool enable)
502 {
503 	const char *path = "/sys/class/vtconsole";
504 	DIR *dir;
505 	struct dirent *de;
506 
507 	dir = opendir(path);
508 	if (!dir)
509 		return;
510 
511 	while ((de = readdir(dir))) {
512 		char buf[PATH_MAX];
513 		int fd, len;
514 
515 		if (strncmp(de->d_name, "vtcon", 5))
516 			continue;
517 
518 		sprintf(buf, "%s/%s/name", path, de->d_name);
519 		fd = open(buf, O_RDONLY);
520 		if (fd < 0)
521 			continue;
522 
523 		buf[sizeof(buf) - 1] = '\0';
524 		len = read(fd, buf, sizeof(buf) - 1);
525 		close(fd);
526 		if (len >= 0)
527 			buf[len] = '\0';
528 
529 		if (!strstr(buf, name))
530 			continue;
531 
532 		sprintf(buf, "%s/%s/bind", path, de->d_name);
533 		fd = open(buf, O_WRONLY);
534 		if (fd != -1) {
535 			igt_ignore_warn(write(fd, enable ? "1\n" : "0\n", 2));
536 			close(fd);
537 		}
538 		break;
539 	}
540 	closedir(dir);
541 }
542 
543 /**
544  * bind_fbcon:
545  * @enable: boolean value
546  *
547  * This functions enables/disables the text console running on top of the
548  * framebuffer device.
549  */
bind_fbcon(bool enable)550 void bind_fbcon(bool enable)
551 {
552 	/*
553 	 * The vtcon bind interface seems somewhat broken. Possibly
554 	 * depending on the order the console drivers have been
555 	 * registered you either have to unbind the old driver,
556 	 * or bind the new driver. Let's do both.
557 	 */
558 	bind_con("dummy device", !enable);
559 	bind_con("frame buffer device", enable);
560 }
561 
562 /**
563  * kick_snd_hda_intel:
564  *
565  * This functions unbinds the snd_hda_intel driver so the module cand be
566  * unloaded.
567  *
568  */
kick_snd_hda_intel(void)569 void kick_snd_hda_intel(void)
570 {
571 	DIR *dir;
572 	struct dirent *snd_hda;
573 	int fd; size_t len;
574 
575 	const char *dpath = "/sys/bus/pci/drivers/snd_hda_intel";
576 	const char *path = "/sys/bus/pci/drivers/snd_hda_intel/unbind";
577 	const char *devid = "0000:";
578 
579 	fd = open(path, O_WRONLY);
580 	if (fd < 0) {
581 		return;
582 	}
583 
584 	dir = opendir(dpath);
585 	if (!dir)
586 		goto out;
587 
588 	len = strlen(devid);
589 	while ((snd_hda = readdir(dir))) {
590 		struct stat st;
591 		char fpath[PATH_MAX];
592 
593 		if (*snd_hda->d_name == '.')
594 			continue;
595 
596 		snprintf(fpath, sizeof(fpath), "%s/%s", dpath, snd_hda->d_name);
597 		if (lstat(fpath, &st))
598 			continue;
599 
600 		if (!S_ISLNK(st.st_mode))
601 			continue;
602 
603 		if (!strncmp(devid, snd_hda->d_name, len)) {
604 			igt_ignore_warn(write(fd, snd_hda->d_name,
605 					strlen(snd_hda->d_name)));
606 		}
607 	}
608 
609 	closedir(dir);
610 out:
611 	close(fd);
612 }
613 
614 static int fbcon_cursor_blink_fd = -1;
615 static char fbcon_cursor_blink_prev_value[2];
616 
fbcon_cursor_blink_restore(int sig)617 static void fbcon_cursor_blink_restore(int sig)
618 {
619 	write(fbcon_cursor_blink_fd, fbcon_cursor_blink_prev_value,
620 	      strlen(fbcon_cursor_blink_prev_value) + 1);
621 	close(fbcon_cursor_blink_fd);
622 }
623 
624 /**
625  * fbcon_blink_enable:
626  * @enable: if true enables the fbcon cursor blinking otherwise disables it
627  *
628  * Enables or disables the cursor blinking in fbcon, it also restores the
629  * previous blinking state when exiting test.
630  *
631  */
fbcon_blink_enable(bool enable)632 void fbcon_blink_enable(bool enable)
633 {
634 	const char *cur_blink_path = "/sys/class/graphics/fbcon/cursor_blink";
635 	int fd, r;
636 	char buffer[2];
637 
638 	fd = open(cur_blink_path, O_RDWR);
639 	igt_require(fd >= 0);
640 
641 	/* Restore original value on exit */
642 	if (fbcon_cursor_blink_fd == -1) {
643 		r = read(fd, fbcon_cursor_blink_prev_value,
644 			 sizeof(fbcon_cursor_blink_prev_value));
645 		if (r > 0) {
646 			fbcon_cursor_blink_fd = dup(fd);
647 			igt_assert(fbcon_cursor_blink_fd >= 0);
648 			igt_install_exit_handler(fbcon_cursor_blink_restore);
649 		}
650 	}
651 
652 	r = snprintf(buffer, sizeof(buffer), enable ? "1" : "0");
653 	write(fd, buffer, r + 1);
654 	close(fd);
655 }
656