1/*
2 * Copyright (c) 2007 Paulo R. Zanoni, Tiago Vignatti
3 *               2009 Tiago Vignatti
4 *
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use,
9 * copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following
12 * conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
25 *
26 */
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#include <stdio.h>
33#include <string.h>
34#include <unistd.h>
35#include <fcntl.h>
36#include <errno.h>
37#include <stdlib.h>
38#include <limits.h>
39
40#include "pciaccess.h"
41#include "pciaccess_private.h"
42
43#define BUFSIZE 64
44
45static int
46parse_string_to_decodes_rsrc(char *input, int *vga_count, struct pci_slot_match *match)
47{
48    char *tok;
49    char *input_sp = NULL, *count_sp, *pci_sp;
50    char tmp[32];
51
52    tok = strtok_r(input,",",&input_sp);
53    if (!tok)
54        goto fail;
55
56    strncpy(tmp, input, 15);
57    tmp[15] = 0;
58
59    tok = strtok_r(tmp,":",&count_sp);
60    if (!tok)
61        goto fail;
62    tok = strtok_r(NULL, ":",&count_sp);
63    if (!tok)
64        goto fail;
65
66    *vga_count = strtoul(tok, NULL, 10);
67    if (*vga_count == LONG_MAX)
68        goto fail;
69
70#ifdef DEBUG
71    fprintf(stderr,"vga count is %d\n", *vga_count);
72#endif
73
74    tok = strtok_r(NULL, ",",&input_sp);
75    if (!tok)
76        goto fail;
77
78    if (match) {
79        strncpy(tmp, tok, 32);
80        tmp[31] = 0;
81        tok = strtok_r(tmp, ":", &pci_sp);
82        if (!tok)
83            goto fail;
84        tok = strtok_r(NULL, ":", &pci_sp);
85        if (!tok)
86            goto fail;
87        match->domain = strtoul(tok, NULL, 16);
88
89        tok = strtok_r(NULL, ":", &pci_sp);
90        if (!tok)
91            goto fail;
92        match->bus = strtoul(tok, NULL, 16);
93
94        tok = strtok_r(NULL, ".", &pci_sp);
95        if (!tok)
96            goto fail;
97        match->dev = strtoul(tok, NULL, 16);
98
99        tok = strtok_r(NULL, ".", &pci_sp);
100        if (!tok)
101            goto fail;
102        match->func = strtoul(tok, NULL, 16);
103    }
104
105    tok = strtok_r(NULL, ",",&input_sp);
106    if (!tok)
107        goto fail;
108    tok = strtok_r(tok, "=", &input_sp);
109    if (!tok)
110        goto fail;
111    tok = strtok_r(NULL, "=", &input_sp);
112    if (!tok)
113        goto fail;
114
115    if (!strncmp(tok, "io+mem", 6))
116        return VGA_ARB_RSRC_LEGACY_IO | VGA_ARB_RSRC_LEGACY_MEM;
117    if (!strncmp(tok, "io", 2))
118        return VGA_ARB_RSRC_LEGACY_IO;
119    if (!strncmp(tok, "mem", 3))
120        return VGA_ARB_RSRC_LEGACY_MEM;
121fail:
122    return VGA_ARB_RSRC_NONE;
123}
124
125int
126pci_device_vgaarb_init(void)
127{
128    struct pci_slot_match match;
129    char buf[BUFSIZE + 1]; /* reading BUFSIZE characters, + 1 for NULL */
130    int ret, rsrc;
131
132    if (!pci_sys)
133        return -1;
134
135    if ((pci_sys->vgaarb_fd = open ("/dev/vga_arbiter", O_RDWR | O_CLOEXEC)) < 0) {
136        return errno;
137    }
138
139    ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE);
140    if (ret <= 0)
141        return -1;
142
143    buf[ret] = 0; /* ret will never be greater than BUFSIZE */
144
145    memset(&match, 0xff, sizeof(match));
146    /* need to find the device to go back to and what it was decoding */
147    rsrc = parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, &match);
148
149    pci_sys->vga_default_dev = pci_device_find_by_slot(match.domain, match.bus, match.dev, match.func);
150
151    if (pci_sys->vga_default_dev)
152        pci_sys->vga_default_dev->vgaarb_rsrc = rsrc;
153    return 0;
154}
155
156void
157pci_device_vgaarb_fini(void)
158{
159    if (!pci_sys)
160        return;
161
162    close(pci_sys->vgaarb_fd);
163}
164
165/**
166 * Writes message on vga device. The messages are defined by the kernel
167 * implementation.
168 *
169 * \param fd    vga arbiter device.
170 * \param buf   message itself.
171 * \param len   message length.
172 *
173 * \return
174 * Zero on success, 1 if something gets wrong and 2 if fd is busy (only for
175 * 'trylock')
176 */
177static int
178vgaarb_write(int fd, char *buf, int len)
179{
180    int ret;
181
182
183    buf[len] = '\0';
184
185    ret = write(fd, buf, len);
186    if (ret == -1) {
187        /* the user may have called "trylock" and didn't get the lock */
188        if (errno == EBUSY)
189            return 2;
190
191#ifdef DEBUG
192        fprintf(stderr, "write error");
193#endif
194        return 1;
195    }
196    else if (ret != len) {
197        /* it's need to receive the exactly amount required. */
198#ifdef DEBUG
199        fprintf(stderr, "write error: wrote different than expected\n");
200#endif
201        return 1;
202    }
203
204#ifdef DEBUG
205    fprintf(stderr, "%s: successfully wrote: '%s'\n", __FUNCTION__, buf);
206#endif
207
208    return 0;
209}
210
211
212static const char *
213rsrc_to_str(int iostate)
214{
215    switch (iostate) {
216    case VGA_ARB_RSRC_LEGACY_IO | VGA_ARB_RSRC_LEGACY_MEM:
217        return "io+mem";
218    case VGA_ARB_RSRC_LEGACY_IO:
219        return "io";
220    case VGA_ARB_RSRC_LEGACY_MEM:
221        return "mem";
222    }
223
224    return "none";
225}
226
227int
228pci_device_vgaarb_set_target(struct pci_device *dev)
229{
230    int len;
231    char buf[BUFSIZE + 1]; /* reading BUFSIZE characters, + 1 for NULL */
232    int ret;
233
234    if (!dev)
235        dev = pci_sys->vga_default_dev;
236    if (!dev)
237        return -1;
238
239    len = snprintf(buf, BUFSIZE, "target PCI:%04x:%02x:%02x.%x",
240                   dev->domain, dev->bus, dev->dev, dev->func);
241
242    ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len);
243    if (ret)
244        return ret;
245
246    ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE);
247    if (ret <= 0)
248        return -1;
249
250    buf[ret] = 0; /* ret will never be greater than BUFSIZE */
251
252    dev->vgaarb_rsrc = parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, NULL);
253    pci_sys->vga_target = dev;
254    return 0;
255}
256
257int
258pci_device_vgaarb_decodes(int new_vgaarb_rsrc)
259{
260    int len;
261    char buf[BUFSIZE + 1]; /* reading BUFSIZE characters, + 1 for NULL */
262    int ret;
263    struct pci_device *dev = pci_sys->vga_target;
264
265    if (!dev)
266        return -1;
267    if (dev->vgaarb_rsrc == new_vgaarb_rsrc)
268        return 0;
269
270    len = snprintf(buf, BUFSIZE, "decodes %s", rsrc_to_str(new_vgaarb_rsrc));
271    ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len);
272    if (ret == 0)
273        dev->vgaarb_rsrc = new_vgaarb_rsrc;
274
275    ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE);
276    if (ret <= 0)
277        return -1;
278
279    buf[ret] = 0; /* ret will never be greater than BUFSIZE */
280
281    parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, NULL);
282
283    return ret;
284}
285
286int
287pci_device_vgaarb_lock(void)
288{
289    int len;
290    char buf[BUFSIZE];
291    struct pci_device *dev = pci_sys->vga_target;
292
293    if (!dev)
294	return -1;
295
296    if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
297        return 0;
298
299    len = snprintf(buf, BUFSIZE, "lock %s", rsrc_to_str(dev->vgaarb_rsrc));
300
301    return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
302}
303
304int
305pci_device_vgaarb_trylock(void)
306{
307    int len;
308    char buf[BUFSIZE];
309    struct pci_device *dev = pci_sys->vga_target;
310
311    if (!dev)
312        return -1;
313
314    if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
315        return 0;
316
317    len = snprintf(buf, BUFSIZE, "trylock %s", rsrc_to_str(dev->vgaarb_rsrc));
318
319    return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
320}
321
322int
323pci_device_vgaarb_unlock(void)
324{
325    int len;
326    char buf[BUFSIZE];
327    struct pci_device *dev = pci_sys->vga_target;
328
329    if (!dev)
330        return -1;
331
332    if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
333        return 0;
334
335    len = snprintf(buf, BUFSIZE, "unlock %s", rsrc_to_str(dev->vgaarb_rsrc));
336
337    return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
338}
339
340int pci_device_vgaarb_get_info(struct pci_device *dev, int *vga_count, int *rsrc_decodes)
341{
342    *vga_count = pci_sys->vga_count;
343    if (!dev)
344        return 0;
345
346    *rsrc_decodes = dev->vgaarb_rsrc;
347        return 0;
348}
349