149f872b5Smrg/*
249f872b5Smrg * Copyright (c) 2007 Paulo R. Zanoni, Tiago Vignatti
349f872b5Smrg *               2009 Tiago Vignatti
449f872b5Smrg *
549f872b5Smrg * Permission is hereby granted, free of charge, to any person
649f872b5Smrg * obtaining a copy of this software and associated documentation
749f872b5Smrg * files (the "Software"), to deal in the Software without
849f872b5Smrg * restriction, including without limitation the rights to use,
949f872b5Smrg * copy, modify, merge, publish, distribute, sublicense, and/or sell
1049f872b5Smrg * copies of the Software, and to permit persons to whom the
1149f872b5Smrg * Software is furnished to do so, subject to the following
1249f872b5Smrg * conditions:
1349f872b5Smrg *
1449f872b5Smrg * The above copyright notice and this permission notice shall be
1549f872b5Smrg * included in all copies or substantial portions of the Software.
1649f872b5Smrg *
1749f872b5Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1849f872b5Smrg * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
1949f872b5Smrg * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2049f872b5Smrg * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
2149f872b5Smrg * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
2249f872b5Smrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2349f872b5Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2449f872b5Smrg * OTHER DEALINGS IN THE SOFTWARE.
2549f872b5Smrg *
2649f872b5Smrg */
2749f872b5Smrg
286a94483fSmrg#ifdef HAVE_CONFIG_H
296a94483fSmrg#include "config.h"
306a94483fSmrg#endif
316a94483fSmrg
3249f872b5Smrg#include <stdio.h>
3349f872b5Smrg#include <string.h>
3449f872b5Smrg#include <unistd.h>
3549f872b5Smrg#include <fcntl.h>
3649f872b5Smrg#include <errno.h>
3749f872b5Smrg#include <stdlib.h>
3849f872b5Smrg#include <limits.h>
3949f872b5Smrg
4049f872b5Smrg#include "pciaccess.h"
4149f872b5Smrg#include "pciaccess_private.h"
4249f872b5Smrg
4349f872b5Smrg#define BUFSIZE 64
4449f872b5Smrg
4549f872b5Smrgstatic int
4649f872b5Smrgparse_string_to_decodes_rsrc(char *input, int *vga_count, struct pci_slot_match *match)
4749f872b5Smrg{
4849f872b5Smrg    char *tok;
4949f872b5Smrg    char *input_sp = NULL, *count_sp, *pci_sp;
5049f872b5Smrg    char tmp[32];
5149f872b5Smrg
5249f872b5Smrg    tok = strtok_r(input,",",&input_sp);
5349f872b5Smrg    if (!tok)
5449f872b5Smrg        goto fail;
5549f872b5Smrg
5649f872b5Smrg    strncpy(tmp, input, 15);
5749f872b5Smrg    tmp[15] = 0;
5849f872b5Smrg
5949f872b5Smrg    tok = strtok_r(tmp,":",&count_sp);
6049f872b5Smrg    if (!tok)
6149f872b5Smrg        goto fail;
6249f872b5Smrg    tok = strtok_r(NULL, ":",&count_sp);
6349f872b5Smrg    if (!tok)
6449f872b5Smrg        goto fail;
6549f872b5Smrg
6649f872b5Smrg    *vga_count = strtoul(tok, NULL, 10);
6749f872b5Smrg    if (*vga_count == LONG_MAX)
6849f872b5Smrg        goto fail;
6949f872b5Smrg
7049f872b5Smrg#ifdef DEBUG
7149f872b5Smrg    fprintf(stderr,"vga count is %d\n", *vga_count);
7249f872b5Smrg#endif
7349f872b5Smrg
7449f872b5Smrg    tok = strtok_r(NULL, ",",&input_sp);
7549f872b5Smrg    if (!tok)
7649f872b5Smrg        goto fail;
7749f872b5Smrg
7849f872b5Smrg    if (match) {
7949f872b5Smrg        strncpy(tmp, tok, 32);
8049f872b5Smrg        tmp[31] = 0;
8149f872b5Smrg        tok = strtok_r(tmp, ":", &pci_sp);
8249f872b5Smrg        if (!tok)
8349f872b5Smrg            goto fail;
8449f872b5Smrg        tok = strtok_r(NULL, ":", &pci_sp);
8549f872b5Smrg        if (!tok)
8649f872b5Smrg            goto fail;
8749f872b5Smrg        match->domain = strtoul(tok, NULL, 16);
8849f872b5Smrg
8949f872b5Smrg        tok = strtok_r(NULL, ":", &pci_sp);
9049f872b5Smrg        if (!tok)
9149f872b5Smrg            goto fail;
9249f872b5Smrg        match->bus = strtoul(tok, NULL, 16);
9349f872b5Smrg
9449f872b5Smrg        tok = strtok_r(NULL, ".", &pci_sp);
9549f872b5Smrg        if (!tok)
9649f872b5Smrg            goto fail;
9749f872b5Smrg        match->dev = strtoul(tok, NULL, 16);
9849f872b5Smrg
9949f872b5Smrg        tok = strtok_r(NULL, ".", &pci_sp);
10049f872b5Smrg        if (!tok)
10149f872b5Smrg            goto fail;
10249f872b5Smrg        match->func = strtoul(tok, NULL, 16);
10349f872b5Smrg    }
10449f872b5Smrg
10549f872b5Smrg    tok = strtok_r(NULL, ",",&input_sp);
10649f872b5Smrg    if (!tok)
10749f872b5Smrg        goto fail;
10849f872b5Smrg    tok = strtok_r(tok, "=", &input_sp);
10949f872b5Smrg    if (!tok)
11049f872b5Smrg        goto fail;
11149f872b5Smrg    tok = strtok_r(NULL, "=", &input_sp);
11249f872b5Smrg    if (!tok)
11349f872b5Smrg        goto fail;
11449f872b5Smrg
11549f872b5Smrg    if (!strncmp(tok, "io+mem", 6))
11649f872b5Smrg        return VGA_ARB_RSRC_LEGACY_IO | VGA_ARB_RSRC_LEGACY_MEM;
11749f872b5Smrg    if (!strncmp(tok, "io", 2))
11849f872b5Smrg        return VGA_ARB_RSRC_LEGACY_IO;
11949f872b5Smrg    if (!strncmp(tok, "mem", 3))
12049f872b5Smrg        return VGA_ARB_RSRC_LEGACY_MEM;
12149f872b5Smrgfail:
12249f872b5Smrg    return VGA_ARB_RSRC_NONE;
12349f872b5Smrg}
12449f872b5Smrg
12549f872b5Smrgint
12649f872b5Smrgpci_device_vgaarb_init(void)
12749f872b5Smrg{
12849f872b5Smrg    struct pci_slot_match match;
12949310723Smrg    char buf[BUFSIZE + 1]; /* reading BUFSIZE characters, + 1 for NULL */
13049f872b5Smrg    int ret, rsrc;
13149f872b5Smrg
13249f872b5Smrg    if (!pci_sys)
13349f872b5Smrg        return -1;
13449f872b5Smrg
135cad31331Smrg    if ((pci_sys->vgaarb_fd = open ("/dev/vga_arbiter", O_RDWR | O_CLOEXEC)) < 0) {
13649f872b5Smrg        return errno;
13749f872b5Smrg    }
13849f872b5Smrg
13949f872b5Smrg    ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE);
14049f872b5Smrg    if (ret <= 0)
14149f872b5Smrg        return -1;
14249f872b5Smrg
14349310723Smrg    buf[ret] = 0; /* ret will never be greater than BUFSIZE */
14449310723Smrg
14549f872b5Smrg    memset(&match, 0xff, sizeof(match));
14649f872b5Smrg    /* need to find the device to go back to and what it was decoding */
14749f872b5Smrg    rsrc = parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, &match);
14849f872b5Smrg
14949f872b5Smrg    pci_sys->vga_default_dev = pci_device_find_by_slot(match.domain, match.bus, match.dev, match.func);
15049f872b5Smrg
15149f872b5Smrg    if (pci_sys->vga_default_dev)
15249f872b5Smrg        pci_sys->vga_default_dev->vgaarb_rsrc = rsrc;
15349f872b5Smrg    return 0;
15449f872b5Smrg}
15549f872b5Smrg
15649f872b5Smrgvoid
15749f872b5Smrgpci_device_vgaarb_fini(void)
15849f872b5Smrg{
15949f872b5Smrg    if (!pci_sys)
16049f872b5Smrg        return;
16149f872b5Smrg
16249f872b5Smrg    close(pci_sys->vgaarb_fd);
16349f872b5Smrg}
16449f872b5Smrg
16549f872b5Smrg/**
16649f872b5Smrg * Writes message on vga device. The messages are defined by the kernel
16749f872b5Smrg * implementation.
16849f872b5Smrg *
16949f872b5Smrg * \param fd    vga arbiter device.
17049f872b5Smrg * \param buf   message itself.
17149f872b5Smrg * \param len   message length.
17249f872b5Smrg *
17349f872b5Smrg * \return
17449f872b5Smrg * Zero on success, 1 if something gets wrong and 2 if fd is busy (only for
17549f872b5Smrg * 'trylock')
17649f872b5Smrg */
17749f872b5Smrgstatic int
17849f872b5Smrgvgaarb_write(int fd, char *buf, int len)
17949f872b5Smrg{
18049f872b5Smrg    int ret;
18149f872b5Smrg
18249f872b5Smrg
18349f872b5Smrg    buf[len] = '\0';
18449f872b5Smrg
18549f872b5Smrg    ret = write(fd, buf, len);
18649f872b5Smrg    if (ret == -1) {
18749f872b5Smrg        /* the user may have called "trylock" and didn't get the lock */
18849f872b5Smrg        if (errno == EBUSY)
18949f872b5Smrg            return 2;
19049f872b5Smrg
19149f872b5Smrg#ifdef DEBUG
19249f872b5Smrg        fprintf(stderr, "write error");
19349f872b5Smrg#endif
19449f872b5Smrg        return 1;
19549f872b5Smrg    }
19649f872b5Smrg    else if (ret != len) {
19749f872b5Smrg        /* it's need to receive the exactly amount required. */
19849f872b5Smrg#ifdef DEBUG
19949f872b5Smrg        fprintf(stderr, "write error: wrote different than expected\n");
20049f872b5Smrg#endif
20149f872b5Smrg        return 1;
20249f872b5Smrg    }
20349f872b5Smrg
20449f872b5Smrg#ifdef DEBUG
20549f872b5Smrg    fprintf(stderr, "%s: successfully wrote: '%s'\n", __FUNCTION__, buf);
20649f872b5Smrg#endif
20749f872b5Smrg
20849f872b5Smrg    return 0;
20949f872b5Smrg}
21049f872b5Smrg
21149f872b5Smrg
21249f872b5Smrgstatic const char *
21349f872b5Smrgrsrc_to_str(int iostate)
21449f872b5Smrg{
21549f872b5Smrg    switch (iostate) {
21649f872b5Smrg    case VGA_ARB_RSRC_LEGACY_IO | VGA_ARB_RSRC_LEGACY_MEM:
21749f872b5Smrg        return "io+mem";
21849f872b5Smrg    case VGA_ARB_RSRC_LEGACY_IO:
21949f872b5Smrg        return "io";
22049f872b5Smrg    case VGA_ARB_RSRC_LEGACY_MEM:
22149f872b5Smrg        return "mem";
22249f872b5Smrg    }
22349f872b5Smrg
22449f872b5Smrg    return "none";
22549f872b5Smrg}
22649f872b5Smrg
22749f872b5Smrgint
22849f872b5Smrgpci_device_vgaarb_set_target(struct pci_device *dev)
22949f872b5Smrg{
23049f872b5Smrg    int len;
23149310723Smrg    char buf[BUFSIZE + 1]; /* reading BUFSIZE characters, + 1 for NULL */
23249f872b5Smrg    int ret;
23349f872b5Smrg
23449f872b5Smrg    if (!dev)
23549f872b5Smrg        dev = pci_sys->vga_default_dev;
23649f872b5Smrg    if (!dev)
23749f872b5Smrg        return -1;
23849f872b5Smrg
23949f872b5Smrg    len = snprintf(buf, BUFSIZE, "target PCI:%04x:%02x:%02x.%x",
24049f872b5Smrg                   dev->domain, dev->bus, dev->dev, dev->func);
24149f872b5Smrg
24249f872b5Smrg    ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len);
24349f872b5Smrg    if (ret)
24449f872b5Smrg        return ret;
24549f872b5Smrg
24649f872b5Smrg    ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE);
24749f872b5Smrg    if (ret <= 0)
24849f872b5Smrg        return -1;
24949f872b5Smrg
25049310723Smrg    buf[ret] = 0; /* ret will never be greater than BUFSIZE */
25149310723Smrg
25249f872b5Smrg    dev->vgaarb_rsrc = parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, NULL);
25349f872b5Smrg    pci_sys->vga_target = dev;
25449f872b5Smrg    return 0;
25549f872b5Smrg}
25649f872b5Smrg
25749f872b5Smrgint
25849f872b5Smrgpci_device_vgaarb_decodes(int new_vgaarb_rsrc)
25949f872b5Smrg{
26049f872b5Smrg    int len;
26149310723Smrg    char buf[BUFSIZE + 1]; /* reading BUFSIZE characters, + 1 for NULL */
26249f872b5Smrg    int ret;
26349f872b5Smrg    struct pci_device *dev = pci_sys->vga_target;
26449f872b5Smrg
26549f872b5Smrg    if (!dev)
26649f872b5Smrg        return -1;
26749f872b5Smrg    if (dev->vgaarb_rsrc == new_vgaarb_rsrc)
26849f872b5Smrg        return 0;
26949f872b5Smrg
27028d65773Smrg    len = snprintf(buf, BUFSIZE, "decodes %s", rsrc_to_str(new_vgaarb_rsrc));
27149f872b5Smrg    ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len);
27249f872b5Smrg    if (ret == 0)
27349f872b5Smrg        dev->vgaarb_rsrc = new_vgaarb_rsrc;
27428d65773Smrg
27528d65773Smrg    ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE);
27628d65773Smrg    if (ret <= 0)
27728d65773Smrg        return -1;
27828d65773Smrg
27949310723Smrg    buf[ret] = 0; /* ret will never be greater than BUFSIZE */
28049310723Smrg
28128d65773Smrg    parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, NULL);
28228d65773Smrg
28349f872b5Smrg    return ret;
28449f872b5Smrg}
28549f872b5Smrg
28649f872b5Smrgint
28749f872b5Smrgpci_device_vgaarb_lock(void)
28849f872b5Smrg{
28949f872b5Smrg    int len;
29049f872b5Smrg    char buf[BUFSIZE];
29149f872b5Smrg    struct pci_device *dev = pci_sys->vga_target;
29249f872b5Smrg
29349f872b5Smrg    if (!dev)
29449f872b5Smrg	return -1;
29549f872b5Smrg
29649f872b5Smrg    if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
29749f872b5Smrg        return 0;
29849f872b5Smrg
29949f872b5Smrg    len = snprintf(buf, BUFSIZE, "lock %s", rsrc_to_str(dev->vgaarb_rsrc));
30049f872b5Smrg
30149f872b5Smrg    return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
30249f872b5Smrg}
30349f872b5Smrg
30449f872b5Smrgint
30549f872b5Smrgpci_device_vgaarb_trylock(void)
30649f872b5Smrg{
30749f872b5Smrg    int len;
30849f872b5Smrg    char buf[BUFSIZE];
30949f872b5Smrg    struct pci_device *dev = pci_sys->vga_target;
31049f872b5Smrg
31149f872b5Smrg    if (!dev)
31249f872b5Smrg        return -1;
31349f872b5Smrg
31449f872b5Smrg    if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
31549f872b5Smrg        return 0;
31649f872b5Smrg
31749f872b5Smrg    len = snprintf(buf, BUFSIZE, "trylock %s", rsrc_to_str(dev->vgaarb_rsrc));
31849f872b5Smrg
31949f872b5Smrg    return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
32049f872b5Smrg}
32149f872b5Smrg
32249f872b5Smrgint
32349f872b5Smrgpci_device_vgaarb_unlock(void)
32449f872b5Smrg{
32549f872b5Smrg    int len;
32649f872b5Smrg    char buf[BUFSIZE];
32749f872b5Smrg    struct pci_device *dev = pci_sys->vga_target;
32849f872b5Smrg
32949f872b5Smrg    if (!dev)
33049f872b5Smrg        return -1;
33149f872b5Smrg
33249f872b5Smrg    if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
33349f872b5Smrg        return 0;
33449f872b5Smrg
33549f872b5Smrg    len = snprintf(buf, BUFSIZE, "unlock %s", rsrc_to_str(dev->vgaarb_rsrc));
33649f872b5Smrg
33749f872b5Smrg    return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
33849f872b5Smrg}
33949f872b5Smrg
34049f872b5Smrgint pci_device_vgaarb_get_info(struct pci_device *dev, int *vga_count, int *rsrc_decodes)
34149f872b5Smrg{
34249f872b5Smrg    *vga_count = pci_sys->vga_count;
34349f872b5Smrg    if (!dev)
34449f872b5Smrg        return 0;
34549f872b5Smrg
34649f872b5Smrg    *rsrc_decodes = dev->vgaarb_rsrc;
34749f872b5Smrg        return 0;
34849f872b5Smrg}
349