Switchtec Userspace PROJECT_NUMBER = 4.4
Loading...
Searching...
No Matches
linux.c
1/*
2 * Microsemi Switchtec(tm) PCIe Management Library
3 * Copyright (c) 2017, Microsemi Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR 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
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25#ifdef __linux__
26
27#define SWITCHTEC_LIB_LINUX
28
29#include "../switchtec_priv.h"
30#include "switchtec/switchtec.h"
31#include "switchtec/pci.h"
32#include "switchtec/utils.h"
33#include "mmap_gas.h"
34#include "gasops.h"
35
36#include <linux/switchtec_ioctl.h>
37
38#include <unistd.h>
39#include <fcntl.h>
40#include <endian.h>
41#include <dirent.h>
42#include <libgen.h>
43#include <sys/stat.h>
44#include <sys/ioctl.h>
45#include <sys/mman.h>
46#include <sys/sysmacros.h>
47#include <glob.h>
48#include <poll.h>
49
50#include <errno.h>
51#include <string.h>
52#include <stddef.h>
53
54static const char *sys_path = "/sys/class/switchtec";
55
56struct switchtec_linux {
57 struct switchtec_dev dev;
58 int fd;
59};
60
61#define to_switchtec_linux(d) \
62 ((struct switchtec_linux *) \
63 ((char *)d - offsetof(struct switchtec_linux, dev)))
64
65const char *platform_strerror(void)
66{
67 return "Success";
68}
69
70static int dev_to_sysfs_path(struct switchtec_linux *ldev, const char *suffix,
71 char *buf, size_t buflen)
72{
73 int ret;
74 struct stat stat;
75
76 ret = fstat(ldev->fd, &stat);
77 if (ret < 0)
78 return ret;
79
80 snprintf(buf, buflen,
81 "/sys/dev/char/%d:%d/%s",
82 major(stat.st_rdev), minor(stat.st_rdev), suffix);
83
84 return 0;
85}
86
87static int sysfs_read_str(const char *path, char *buf, size_t buflen)
88{
89 int ret;
90 int fd;
91
92 fd = open(path, O_RDONLY);
93 if (fd < 0)
94 return -1;
95
96 ret = read(fd, buf, buflen);
97
98 close(fd);
99
100 return ret;
101}
102
103static long long sysfs_read_int(const char *path, int base)
104{
105 int ret;
106 char buf[64];
107
108 ret = sysfs_read_str(path, buf, sizeof(buf));
109 if (ret < 0)
110 return ret;
111
112 return strtoll(buf, NULL, base);
113}
114
115static int check_switchtec_device(struct switchtec_linux *ldev)
116{
117 int ret;
118 char syspath[PATH_MAX];
119
120 ret = dev_to_sysfs_path(ldev, "device/switchtec", syspath,
121 sizeof(syspath));
122 if (ret)
123 return ret;
124
125 ret = access(syspath, F_OK);
126 if (ret)
127 errno = ENOTTY;
128
129 return ret;
130}
131
132static int get_partition(struct switchtec_linux *ldev)
133{
134 int ret;
135 char syspath[PATH_MAX];
136
137 ret = dev_to_sysfs_path(ldev, "partition", syspath,
138 sizeof(syspath));
139 if (ret)
140 return ret;
141
142 ldev->dev.partition = sysfs_read_int(syspath, 10);
143 if (ldev->dev.partition < 0)
144 return ldev->dev.partition;
145
146 ret = dev_to_sysfs_path(ldev, "partition_count", syspath,
147 sizeof(syspath));
148 if (ret)
149 return ret;
150
151 ldev->dev.partition_count = sysfs_read_int(syspath, 10);
152 if (ldev->dev.partition_count < 1)
153 return -1;
154
155 return 0;
156}
157
158static void linux_close(struct switchtec_dev *dev)
159{
160 struct switchtec_linux *ldev = to_switchtec_linux(dev);
161
162 close(ldev->fd);
163 free(ldev);
164}
165
166static int scan_dev_filter(const struct dirent *d)
167{
168 if (d->d_name[0] == '.')
169 return 0;
170
171 return 1;
172}
173
174static void get_device_str(const char *path, const char *file,
175 char *buf, size_t buflen)
176{
177 char sysfs_path[PATH_MAX];
178 int ret;
179
180 snprintf(sysfs_path, sizeof(sysfs_path), "%s/%s",
181 path, file);
182
183 ret = sysfs_read_str(sysfs_path, buf, buflen);
184 if (ret < 0 || buf[0] == -1)
185 snprintf(buf, buflen, "unknown");
186
187 buf[strcspn(buf, "\n")] = 0;
188}
189
190static void get_fw_version(const char *path, char *buf, size_t buflen)
191{
192 char sysfs_path[PATH_MAX];
193 int fw_ver;
194 int ret;
195
196 ret = snprintf(sysfs_path, sizeof(sysfs_path), "%s/fw_version",
197 path);
198 if (ret >= sizeof(sysfs_path))
199 goto unknown_version;
200
201 fw_ver = sysfs_read_int(sysfs_path, 16);
202
203 if (fw_ver < 0)
204 goto unknown_version;
205
206 version_to_string(fw_ver, buf, buflen);
207 return;
208
209unknown_version:
210 snprintf(buf, buflen, "unknown");
211}
212
213int switchtec_list(struct switchtec_device_info **devlist)
214{
215 struct dirent **devices;
216 int i, n;
217 char link_path[PATH_MAX];
218 char pci_path[PATH_MAX] = "";
219 struct switchtec_device_info *dl;
220
221 n = scandir(sys_path, &devices, scan_dev_filter, alphasort);
222 if (n <= 0)
223 return n;
224
225 dl = *devlist = calloc(n, sizeof(struct switchtec_device_info));
226
227 if (!dl) {
228 for (i = 0; i < n; i++)
229 free(devices[i]);
230 free(devices);
231 errno = ENOMEM;
232 return -errno;
233 }
234
235 for (i = 0; i < n; i++) {
236 snprintf(dl[i].name, sizeof(dl[i].name),
237 "%s", devices[i]->d_name);
238 snprintf(dl[i].path, sizeof(dl[i].path),
239 "/dev/%s", devices[i]->d_name);
240
241 snprintf(link_path, sizeof(link_path), "%s/%s/device",
242 sys_path, devices[i]->d_name);
243
244 if (readlink(link_path, pci_path, sizeof(pci_path)) > 0)
245 snprintf(dl[i].pci_dev, sizeof(dl[i].pci_dev),
246 "%s", basename(pci_path));
247 else
248 snprintf(dl[i].pci_dev, sizeof(dl[i].pci_dev),
249 "unknown pci device");
250
251 snprintf(link_path, sizeof(link_path), "%s/%s",
252 sys_path, devices[i]->d_name);
253
254 get_device_str(link_path, "product_id", dl[i].product_id,
255 sizeof(dl[i].product_id));
256 get_device_str(link_path, "product_revision",
257 dl[i].product_rev, sizeof(dl[i].product_rev));
258 get_fw_version(link_path, dl[i].fw_version,
259 sizeof(dl[i].fw_version));
260
261 free(devices[i]);
262 }
263
264 free(devices);
265 return n;
266}
267
268static int linux_get_device_id(struct switchtec_dev *dev)
269{
270 int ret;
271 char link_path[PATH_MAX];
272 struct switchtec_linux *ldev = to_switchtec_linux(dev);
273
274 ret = dev_to_sysfs_path(ldev, "device/device", link_path,
275 sizeof(link_path));
276 if (ret)
277 return ret;
278
279 return sysfs_read_int(link_path, 16);
280}
281
282static int linux_get_fw_version(struct switchtec_dev *dev, char *buf,
283 size_t buflen)
284{
285 int ret;
286 long long version;
287 char syspath[PATH_MAX];
288 struct switchtec_linux *ldev = to_switchtec_linux(dev);
289
290 ret = dev_to_sysfs_path(ldev, "fw_version", syspath, sizeof(syspath));
291 if (ret)
292 return ret;
293
294 version = sysfs_read_int(syspath, 16);
295 if (version < 0)
296 return version;
297
298 version_to_string(version, buf, buflen);
299
300 return 0;
301}
302
303static int linux_get_device_version(struct switchtec_dev *dev, int *version_res)
304{
305 int ret;
306 int version;
307 char syspath[PATH_MAX];
308 struct switchtec_linux *ldev = to_switchtec_linux(dev);
309
310 ret = dev_to_sysfs_path(ldev, "device_version", syspath, sizeof(syspath));
311 if (ret)
312 return ret;
313
314 version = sysfs_read_int(syspath, 16);
315 if (version < 0)
316 return version;
317
318 memcpy(version_res, &version, sizeof(int));
319
320 return 0;
321}
322
323static int submit_cmd(struct switchtec_linux *ldev, uint32_t cmd,
324 const void *payload, size_t payload_len)
325{
326 int ret;
327 size_t bufsize = payload_len + sizeof(cmd);
328 char buf[bufsize];
329
330 cmd = htole32(cmd);
331 memcpy(buf, &cmd, sizeof(cmd));
332 memcpy(&buf[sizeof(cmd)], payload, payload_len);
333
334 ret = write(ldev->fd, buf, bufsize);
335
336 if (ret < 0)
337 return ret;
338
339 if (ret != bufsize) {
340 errno = EIO;
341 return -errno;
342 }
343
344 return 0;
345}
346
347static int read_resp(struct switchtec_linux *ldev, void *resp,
348 size_t resp_len)
349{
350 int32_t ret;
351 size_t bufsize = sizeof(uint32_t) + resp_len;
352 char buf[bufsize];
353
354 ret = read(ldev->fd, buf, bufsize);
355
356 if (ret < 0)
357 return ret;
358
359 if (ret != bufsize) {
360 errno = EIO;
361 return -errno;
362 }
363
364 memcpy(&ret, buf, sizeof(ret));
365
366 if (ret) {
367 errno = ret;
368 return ret;
369 }
370
371 if (!resp)
372 return ret;
373
374 memcpy(resp, &buf[sizeof(ret)], resp_len);
375
376 return ret;
377}
378
379static int linux_cmd(struct switchtec_dev *dev, uint32_t cmd,
380 const void *payload, size_t payload_len, void *resp,
381 size_t resp_len)
382{
383 int ret;
384 struct switchtec_linux *ldev = to_switchtec_linux(dev);
385
386retry:
387 ret = submit_cmd(ldev, cmd, payload, payload_len);
388 if (errno == EBADE) {
389 read_resp(ldev, NULL, 0);
390 errno = 0;
391 goto retry;
392 }
393
394 if (ret < 0)
395 return ret;
396
397 return read_resp(ldev, resp, resp_len);
398}
399
400static int get_class_devices(const char *searchpath,
401 struct switchtec_status *status)
402{
403 int i;
404 ssize_t len;
405 char syspath[PATH_MAX];
406 glob_t paths;
407 int found = 0;
408 const size_t MAX_LEN = 256;
409
410 snprintf(syspath, sizeof(syspath), "%s*/*/device", searchpath);
411 glob(syspath, 0, NULL, &paths);
412
413 for (i = 0; i < paths.gl_pathc; i++) {
414 char *p = paths.gl_pathv[i];
415
416 len = readlink(p, syspath, sizeof(syspath));
417 if (len <= 0)
418 continue;
419
420 p = dirname(p);
421
422 if (!status->class_devices) {
423 status->class_devices = calloc(MAX_LEN, 1);
424 strcpy(status->class_devices, basename(p));
425 } else {
426 len = strlen(status->class_devices);
427 snprintf(&status->class_devices[len], MAX_LEN - len,
428 ", %s", basename(p));
429 }
430
431 found = 1;
432 }
433
434 globfree(&paths);
435 return found;
436}
437
438static void get_port_bdf(const char *searchpath, int port,
439 struct switchtec_status *status)
440{
441 char syspath[PATH_MAX];
442 glob_t paths;
443 int ret;
444
445 ret = snprintf(syspath, sizeof(syspath), "%s/*:*:%02x.?",
446 searchpath, port);
447 if (ret >= sizeof(syspath))
448 return;
449
450 glob(syspath, 0, NULL, &paths);
451
452 if (paths.gl_pathc == 1)
453 status->pci_bdf = strdup(basename(paths.gl_pathv[0]));
454
455 globfree(&paths);
456}
457
458static void get_port_bdf_path(struct switchtec_status *status)
459{
460 char path[PATH_MAX];
461 char rpath[PATH_MAX];
462 int domain, bus, dev, fn;
463 int ptr = 0;
464 char *subpath;
465 int ret;
466
467 if (!status->pci_bdf)
468 return;
469
470 snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s",
471 status->pci_bdf);
472
473 if (!realpath(path, rpath))
474 return;
475
476 subpath = strtok(rpath, "/");
477 while (subpath) {
478 ret = sscanf(subpath, "%x:%x:%x.%x", &domain, &bus, &dev, &fn);
479 if (ret == 4) {
480 if (ptr == 0)
481 ret = snprintf(path + ptr, sizeof(path) - ptr,
482 "%04x:%02x:%02x:%x/",
483 domain, bus, dev, fn);
484 else
485 ret = snprintf(path + ptr, sizeof(path) - ptr,
486 "%02x.%x/", dev, fn);
487
488 if (ret <= 0 || ret >= sizeof(path) - ptr)
489 break;
490
491 ptr += ret;
492 }
493 subpath = strtok(NULL, "/");
494 }
495
496 if (ptr)
497 path[ptr - 1] = 0;
498
499 status->pci_bdf_path = strdup(path);
500}
501
502static void get_port_info(struct switchtec_status *status)
503{
504 int i;
505 char syspath[PATH_MAX];
506 glob_t paths;
507
508 if (!status->pci_bdf)
509 return;
510
511 snprintf(syspath, sizeof(syspath), "/sys/bus/pci/devices/%s/*:*:*/",
512 status->pci_bdf);
513
514 glob(syspath, 0, NULL, &paths);
515
516 for (i = 0; i < paths.gl_pathc; i++) {
517 char *p = paths.gl_pathv[i];
518
519 snprintf(syspath, sizeof(syspath), "%s/vendor", p);
520 status->vendor_id = sysfs_read_int(syspath, 16);
521 if (status->vendor_id < 0)
522 continue;
523
524 snprintf(syspath, sizeof(syspath), "%s/device", p);
525 status->device_id = sysfs_read_int(syspath, 16);
526 if (status->device_id < 0)
527 continue;
528
529 if (get_class_devices(p, status)) {
530 if (status->pci_dev)
531 free(status->pci_dev);
532 status->pci_dev = strdup(basename(p));
533 }
534
535 if (!status->pci_dev)
536 status->pci_dev = strdup(basename(p));
537 }
538
539 globfree(&paths);
540}
541
542static void get_config_info(struct switchtec_status *status)
543{
544 int ret;
545 int fd;
546 char syspath[PATH_MAX];
547 uint32_t extcap;
548 int pos = PCI_EXT_CAP_OFFSET;
549 uint16_t acs;
550
551 snprintf(syspath, sizeof(syspath), "/sys/bus/pci/devices/%s/config",
552 status->pci_bdf);
553
554 fd = open(syspath, O_RDONLY);
555 if (fd < -1)
556 return;
557
558 while (1) {
559 ret = pread(fd, &extcap, sizeof(extcap), pos);
560 if (ret != sizeof(extcap) || !extcap)
561 goto close_and_exit;
562
563 if (PCI_EXT_CAP_ID(extcap) == PCI_EXT_CAP_ID_ACS)
564 break;
565
566 pos = PCI_EXT_CAP_NEXT(extcap);
567 if (pos < PCI_EXT_CAP_OFFSET)
568 goto close_and_exit;
569 }
570
571 ret = pread(fd, &acs, sizeof(acs), pos + PCI_ACS_CTRL);
572 if (ret != sizeof(acs))
573 goto close_and_exit;
574
575 status->acs_ctrl = acs;
576
577close_and_exit:
578 close(fd);
579}
580
581static int linux_get_devices(struct switchtec_dev *dev,
582 struct switchtec_status *status,
583 int ports)
584{
585 int ret;
586 int i;
587 int local_part;
588 char syspath[PATH_MAX];
589 char searchpath[PATH_MAX];
590 struct switchtec_linux *ldev = to_switchtec_linux(dev);
591
592 ret = dev_to_sysfs_path(ldev, "device", syspath,
593 sizeof(syspath));
594 if (ret)
595 return ret;
596
597 if (!realpath(syspath, searchpath)) {
598 errno = ENXIO;
599 return -errno;
600 }
601
602 //Replace eg "0000:03:00.1" into "0000:03:00.0"
603 searchpath[strlen(searchpath) - 1] = '0';
604
605 local_part = switchtec_partition(dev);
606
607 for (i = 0; i < ports; i++) {
608 if (status[i].port.partition != local_part)
609 continue;
610
611 if (status[i].port.upstream) {
612 status[i].pci_bdf = strdup(basename(searchpath));
613 get_port_bdf_path(&status[i]);
614 continue;
615 }
616
617 get_port_bdf(searchpath, status[i].port.log_id - 1, &status[i]);
618 get_port_bdf_path(&status[i]);
619 get_port_info(&status[i]);
620 get_config_info(&status[i]);
621 }
622
623 return 0;
624}
625
626static int linux_pff_to_port(struct switchtec_dev *dev, int pff,
627 int *partition, int *port)
628{
629 int ret;
630 struct switchtec_ioctl_pff_port p;
631 struct switchtec_linux *ldev = to_switchtec_linux(dev);
632
633 p.pff = pff;
634 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_PFF_TO_PORT, &p);
635 if (ret)
636 return ret;
637
638 if (partition)
639 *partition = p.partition;
640 if (port)
641 *port = p.port;
642
643 return 0;
644}
645
646static int linux_port_to_pff(struct switchtec_dev *dev, int partition,
647 int port, int *pff)
648{
649 int ret;
650 struct switchtec_ioctl_pff_port p;
651 struct switchtec_linux *ldev = to_switchtec_linux(dev);
652
653 p.port = port;
654 p.partition = partition;
655
656 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_PORT_TO_PFF, &p);
657 if (ret)
658 return ret;
659
660 if (pff)
661 *pff = p.pff;
662
663 return 0;
664}
665
666#ifdef __CHECKER__
667#define __force __attribute__((force))
668#else
669#define __force
670#endif
671
672static ssize_t resource_size(struct switchtec_linux *ldev, const char *fname)
673{
674 char respath[PATH_MAX];
675 struct stat stat;
676 int fd, ret;
677
678 ret = dev_to_sysfs_path(ldev, fname, respath,
679 sizeof(respath));
680 if (ret) {
681 errno = ret;
682 return -1;
683 }
684
685 fd = open(respath, O_RDONLY);
686 if (fd < 0)
687 return -1;
688
689 ret = fstat(fd, &stat);
690 if (ret < 0) {
691 close(fd);
692 return -1;
693 }
694
695 close(fd);
696 return stat.st_size;
697}
698
699static int mmap_resource(struct switchtec_linux *ldev, const char *fname,
700 void *addr, size_t offset, size_t size, int writeable)
701{
702 char respath[PATH_MAX];
703 void *map;
704 int fd, ret = 0;
705
706 ret = dev_to_sysfs_path(ldev, fname, respath,
707 sizeof(respath));
708 if (ret) {
709 errno = ret;
710 return -1;
711 }
712
713 fd = open(respath, writeable ? O_RDWR : O_RDONLY);
714 if (fd < 0)
715 return -1;
716
717 map = mmap(addr, size, (writeable ? PROT_WRITE : 0) | PROT_READ,
718 MAP_SHARED | MAP_FIXED, fd, offset);
719 if (map == MAP_FAILED)
720 ret = -1;
721
722 close(fd);
723 return ret;
724}
725
726/*
727 * GAS map maps the hardware registers into user memory space.
728 * Needless to say, this can be very dangerous and should only
729 * be done if you know what you are doing. Any register accesses
730 * that use this will remain unsupported by Microsemi unless it's
731 * done within the switchtec user project or otherwise specified.
732 */
733static gasptr_t linux_gas_map(struct switchtec_dev *dev, int writeable,
734 size_t *map_size)
735{
736 int ret;
737 void *map;
738 ssize_t msize;
739 struct switchtec_linux *ldev = to_switchtec_linux(dev);
740
741 msize = resource_size(ldev, "device/resource0");
742 if (msize <= 0)
743 return SWITCHTEC_MAP_FAILED;
744
745 /*
746 * Reserve virtual address space for the entire GAS mapping.
747 */
748 map = mmap(NULL, msize, PROT_NONE,
749 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
750 if (map == MAP_FAILED)
751 return SWITCHTEC_MAP_FAILED;
752
753 ret = mmap_resource(ldev, "device/resource0_wc", map, 0,
754 SWITCHTEC_GAS_TOP_CFG_OFFSET, writeable);
755 if (ret) {
756 ret = mmap_resource(ldev, "device/resource0", map, 0,
757 SWITCHTEC_GAS_TOP_CFG_OFFSET,
758 writeable);
759 if (ret)
760 goto unmap_and_exit;
761 }
762
763 ret = mmap_resource(ldev, "device/resource0",
764 map + SWITCHTEC_GAS_TOP_CFG_OFFSET,
765 SWITCHTEC_GAS_TOP_CFG_OFFSET,
766 msize - SWITCHTEC_GAS_TOP_CFG_OFFSET,
767 writeable);
768 if (ret)
769 goto unmap_and_exit;
770
771 if (map_size)
772 *map_size = msize;
773
774 dev->gas_map = (gasptr_t __force)map;
775 dev->gas_map_size = msize;
776
777 ret = gasop_access_check(dev);
778 if (ret) {
779 errno = ENODEV;
780 goto unmap_and_exit;
781 }
782 return (gasptr_t __force)map;
783
784unmap_and_exit:
785 munmap(map, msize);
786 return SWITCHTEC_MAP_FAILED;
787}
788
789static void linux_gas_unmap(struct switchtec_dev *dev, gasptr_t map)
790{
791 munmap((void __force *)map, dev->gas_map_size);
792}
793
794static int linux_flash_part(struct switchtec_dev *dev,
795 struct switchtec_fw_image_info *info,
796 enum switchtec_fw_image_part_id_gen3 part)
797{
798 struct switchtec_linux *ldev = to_switchtec_linux(dev);
799 struct switchtec_ioctl_flash_part_info ioctl_info = {0};
800 int ret;
801
802 switch (part) {
803 case SWITCHTEC_FW_PART_ID_G3_IMG0:
804 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_IMG0;
805 break;
806 case SWITCHTEC_FW_PART_ID_G3_IMG1:
807 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_IMG1;
808 break;
809 case SWITCHTEC_FW_PART_ID_G3_DAT0:
810 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_CFG0;
811 break;
812 case SWITCHTEC_FW_PART_ID_G3_DAT1:
813 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_CFG1;
814 break;
815 case SWITCHTEC_FW_PART_ID_G3_NVLOG:
816 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_NVLOG;
817 break;
818 default:
819 return -EINVAL;
820 }
821
822 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_FLASH_PART_INFO, &ioctl_info);
823 if (ret)
824 return ret;
825
826 info->part_addr = ioctl_info.address;
827 info->part_len = ioctl_info.length;
828 info->active = false;
829 info->running = false;
830
831 if (ioctl_info.active & SWITCHTEC_IOCTL_PART_ACTIVE)
832 info->active = true;
833
834 if (ioctl_info.active & SWITCHTEC_IOCTL_PART_RUNNING)
835 info->running = true;
836
837 return 0;
838}
839
840static void event_summary_copy(struct switchtec_event_summary *dst,
841 struct switchtec_ioctl_event_summary *src,
842 int size)
843{
844 int i;
845
846 dst->global = src->global;
847 dst->part_bitmap = src->part_bitmap;
848 dst->local_part = src->local_part;
849
850 for (i = 0; i < SWITCHTEC_MAX_PARTS; i++)
851 dst->part[i] = src->part[i];
852
853 for (i = 0; i < SWITCHTEC_MAX_PFF_CSR && i < size; i++)
854 dst->pff[i] = src->pff[i];
855
856 for (; i < SWITCHTEC_MAX_PFF_CSR; i++)
857 dst->pff[i] = 0;
858}
859
860#define EV(t, n)[SWITCHTEC_ ## t ## _EVT_ ## n] = \
861 SWITCHTEC_IOCTL_EVENT_ ## n
862
863static const int event_map[] = {
864 EV(GLOBAL, STACK_ERROR),
865 EV(GLOBAL, PPU_ERROR),
866 EV(GLOBAL, ISP_ERROR),
867 EV(GLOBAL, SYS_RESET),
868 EV(GLOBAL, FW_EXC),
869 EV(GLOBAL, FW_NMI),
870 EV(GLOBAL, FW_NON_FATAL),
871 EV(GLOBAL, FW_FATAL),
872 EV(GLOBAL, TWI_MRPC_COMP),
873 EV(GLOBAL, TWI_MRPC_COMP_ASYNC),
874 EV(GLOBAL, CLI_MRPC_COMP),
875 EV(GLOBAL, CLI_MRPC_COMP_ASYNC),
876 EV(GLOBAL, GPIO_INT),
877 EV(GLOBAL, GFMS),
878 EV(PART, PART_RESET),
879 EV(PART, MRPC_COMP),
880 EV(PART, MRPC_COMP_ASYNC),
881 EV(PART, DYN_PART_BIND_COMP),
882 EV(PFF, AER_IN_P2P),
883 EV(PFF, AER_IN_VEP),
884 EV(PFF, DPC),
885 EV(PFF, CTS),
886 EV(PFF, UEC),
887 EV(PFF, HOTPLUG),
888 EV(PFF, IER),
889 EV(PFF, THRESH),
890 EV(PFF, POWER_MGMT),
891 EV(PFF, TLP_THROTTLING),
892 EV(PFF, FORCE_SPEED),
893 EV(PFF, CREDIT_TIMEOUT),
894 EV(PFF, LINK_STATE),
895 EV(GLOBAL, ASSERT_ERR),
896};
897
898static int linux_event_summary(struct switchtec_dev *dev,
899 struct switchtec_event_summary *sum)
900{
901 int ret;
902 struct switchtec_ioctl_event_summary isum;
903 struct switchtec_ioctl_event_summary_legacy isum_legacy;
904 struct switchtec_linux *ldev = to_switchtec_linux(dev);
905
906 if (!sum)
907 return 0;
908
909 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_SUMMARY, &isum);
910 if (!ret) {
911 event_summary_copy(sum, &isum, ARRAY_SIZE(isum.pff));
912 return ret;
913 }
914
915 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_SUMMARY_LEGACY, &isum);
916 if (ret < 0)
917 return ret;
918
919 event_summary_copy(sum, &isum, ARRAY_SIZE(isum_legacy.pff));
920
921 return 0;
922}
923
924static int linux_event_ctl(struct switchtec_dev *dev,
925 enum switchtec_event_id e,
926 int index, int flags,
927 uint32_t data[5])
928{
929 int ret;
930 struct switchtec_ioctl_event_ctl ctl;
931 struct switchtec_linux *ldev = to_switchtec_linux(dev);
932
933 if (e >= SWITCHTEC_MAX_EVENTS)
934 return -EINVAL;
935
936 ctl.event_id = event_map[e];
937 ctl.flags = 0;
938
939 if (flags & SWITCHTEC_EVT_FLAG_CLEAR)
940 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR;
941 if (flags & SWITCHTEC_EVT_FLAG_EN_POLL)
942 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL;
943 if (flags & SWITCHTEC_EVT_FLAG_EN_LOG)
944 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG;
945 if (flags & SWITCHTEC_EVT_FLAG_EN_CLI)
946 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI;
947 if (flags & SWITCHTEC_EVT_FLAG_EN_FATAL)
948 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL;
949 if (flags & SWITCHTEC_EVT_FLAG_DIS_POLL)
950 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL;
951 if (flags & SWITCHTEC_EVT_FLAG_DIS_LOG)
952 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG;
953 if (flags & SWITCHTEC_EVT_FLAG_DIS_CLI)
954 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI;
955 if (flags & SWITCHTEC_EVT_FLAG_DIS_FATAL)
956 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL;
957
958 ctl.index = index;
959 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_CTL, &ctl);
960
961 if (ret)
962 return ret;
963
964 if (data)
965 memcpy(data, ctl.data, sizeof(ctl.data));
966
967 return ctl.count;
968}
969
970static int linux_event_wait(struct switchtec_dev *dev, int timeout_ms)
971{
972 int ret;
973 struct switchtec_linux *ldev = to_switchtec_linux(dev);
974 struct pollfd fds = {
975 .fd = ldev->fd,
976 .events = POLLPRI,
977 };
978
979 ret = poll(&fds, 1, timeout_ms);
980 if (ret <= 0)
981 return ret;
982
983 if (fds.revents & POLLERR) {
984 errno = ENODEV;
985 return -1;
986 }
987
988 if (fds.revents & POLLPRI)
989 return 1;
990
991 return 0;
992}
993
994static const struct switchtec_ops linux_ops = {
995 .close = linux_close,
996 .get_device_id = linux_get_device_id,
997 .get_fw_version = linux_get_fw_version,
998 .get_device_version = linux_get_device_version,
999 .cmd = linux_cmd,
1000 .get_devices = linux_get_devices,
1001 .pff_to_port = linux_pff_to_port,
1002 .port_to_pff = linux_port_to_pff,
1003 .gas_map = linux_gas_map,
1004 .gas_unmap = linux_gas_unmap,
1005 .flash_part = linux_flash_part,
1006 .event_summary = linux_event_summary,
1007 .event_ctl = linux_event_ctl,
1008 .event_wait = linux_event_wait,
1009
1010 .gas_read8 = mmap_gas_read8,
1011 .gas_read16 = mmap_gas_read16,
1012 .gas_read32 = mmap_gas_read32,
1013 .gas_read64 = mmap_gas_read64,
1014 .gas_write8 = mmap_gas_write8,
1015 .gas_write16 = mmap_gas_write16,
1016 .gas_write32 = mmap_gas_write32,
1017 .gas_write32_no_retry = mmap_gas_write32,
1018 .gas_write64 = mmap_gas_write64,
1019 .memcpy_to_gas = mmap_memcpy_to_gas,
1020 .memcpy_from_gas = mmap_memcpy_from_gas,
1021 .write_from_gas = mmap_write_from_gas,
1022};
1023
1024struct switchtec_dev *switchtec_open_by_path(const char *path)
1025{
1026 struct switchtec_linux *ldev;
1027 int fd;
1028
1029 fd = open(path, O_RDWR | O_CLOEXEC);
1030 if (fd < 0)
1031 return NULL;
1032
1033 if (isatty(fd))
1034 return switchtec_open_uart(fd);
1035 else
1036 errno = 0;
1037
1038 ldev = malloc(sizeof(*ldev));
1039 if (!ldev)
1040 return NULL;
1041
1042 ldev->fd = fd;
1043
1044 if (check_switchtec_device(ldev))
1045 goto err_close_free;
1046
1047 if (get_partition(ldev))
1048 goto err_close_free;
1049
1050 ldev->dev.ops = &linux_ops;
1051
1052 return &ldev->dev;
1053
1054err_close_free:
1055 close(ldev->fd);
1056 free(ldev);
1057 return NULL;
1058}
1059
1060struct switchtec_dev *switchtec_open_by_index(int index)
1061{
1062 char path[PATH_MAX];
1063 struct switchtec_dev *dev;
1064
1065 snprintf(path, sizeof(path), "/dev/switchtec%d", index);
1066
1067 dev = switchtec_open_by_path(path);
1068
1069 if (errno == ENOENT)
1070 errno = ENODEV;
1071
1072 return dev;
1073}
1074
1075struct switchtec_dev *switchtec_open_by_pci_addr(int domain, int bus,
1076 int device, int func)
1077{
1078 char path[PATH_MAX];
1079 struct switchtec_dev *dev;
1080 struct dirent *dirent;
1081 DIR *dir;
1082
1083 snprintf(path, sizeof(path),
1084 "/sys/bus/pci/devices/%04x:%02x:%02x.%x/switchtec",
1085 domain, bus, device, func);
1086
1087 dir = opendir(path);
1088 if (!dir)
1089 goto err_out;
1090
1091 while ((dirent = readdir(dir))) {
1092 if (dirent->d_name[0] != '.')
1093 break;
1094 }
1095
1096 if (!dirent)
1097 goto err_close;
1098
1099 /*
1100 * Should only be one switchtec device, if there are
1101 * more then something is wrong
1102 */
1103 if (readdir(dir))
1104 goto err_close;
1105
1106 snprintf(path, sizeof(path), "/dev/%s", dirent->d_name);
1107 printf("%s\n", path);
1108 dev = switchtec_open(path);
1109
1110 closedir(dir);
1111 return dev;
1112
1113err_close:
1114 closedir(dir);
1115err_out:
1116 errno = ENODEV;
1117 return NULL;
1118}
1119
1120#endif
struct switchtec_dev * switchtec_open(const char *device)
Open a Switchtec device by string.
Definition switchtec.c:279
struct switchtec_dev * switchtec_open_uart(int fd)
Open a switchtec device behind a uart device.
int switchtec_list(struct switchtec_device_info **devlist)
List all the switchtec devices in the system.
struct switchtec_dev * switchtec_open_by_index(int index)
Open a switchtec device by index.
struct switchtec_dev * switchtec_open_by_path(const char *path)
Open a switchtec device by path.
_PURE int switchtec_partition(struct switchtec_dev *dev)
Get the partiton number of the device that was opened.
Definition switchtec.c:422
struct switchtec_dev * switchtec_open_by_pci_addr(int domain, int bus, int device, int func)
Open a switchtec device by PCI address (BDF).
Gas Operations for platforms that the gas is mapped into the address space.
Represents a Switchtec device in the switchtec_list() function.
Definition switchtec.h:152
char fw_version[32]
Firmware version.
Definition switchtec.h:158
char pci_dev[256]
PCI BDF string.
Definition switchtec.h:155
char path[PATH_MAX]
Path to the device.
Definition switchtec.h:159
char name[256]
Device name, eg. switchtec0.
Definition switchtec.h:153
char product_id[32]
Product ID.
Definition switchtec.h:156
char product_rev[8]
Product revision.
Definition switchtec.h:157
Event summary bitmaps.
Definition switchtec.h:343
uint64_t part_bitmap
Bitmap of partitions with active events.
Definition switchtec.h:345
uint64_t global
Bitmap of global events.
Definition switchtec.h:344
unsigned part[SWITCHTEC_MAX_PARTS]
Bitmap of events in each partition.
Definition switchtec.h:349
unsigned local_part
Bitmap of events in the local partition.
Definition switchtec.h:346
unsigned pff[SWITCHTEC_MAX_PFF_CSR]
Bitmap of events in each port function.
Definition switchtec.h:352
Information about a firmware image or partition.
Definition switchtec.h:305
size_t part_addr
Address of the partition.
Definition switchtec.h:310
size_t part_len
Length of the partition.
Definition switchtec.h:311
Port status structure.
Definition switchtec.h:181
unsigned int acs_ctrl
ACS Setting of the Port.
Definition switchtec.h:201
int vendor_id
Vendor ID.
Definition switchtec.h:198
char * pci_bdf_path
PCI BDF path of the port.
Definition switchtec.h:195
char * pci_bdf
PCI BDF of the port.
Definition switchtec.h:194
int device_id
Device ID.
Definition switchtec.h:199
char * class_devices
Comma seperated list of classes.
Definition switchtec.h:200
char * pci_dev
PCI BDF of the device on the port.
Definition switchtec.h:197
Main Switchtec header.
switchtec_event_id
Enumeration of all possible events.
Definition switchtec.h:358
__gas struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.
Definition switchtec.h:88