common_vgaarb.c revision 49f872b5
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#include <stdio.h>
29#include <string.h>
30#include <unistd.h>
31#include <fcntl.h>
32#include <errno.h>
33#include <stdlib.h>
34#include <limits.h>
35
36#include "pciaccess.h"
37#include "pciaccess_private.h"
38
39#define BUFSIZE 64
40
41static int
42parse_string_to_decodes_rsrc(char *input, int *vga_count, struct pci_slot_match *match)
43{
44    char *tok;
45    char *input_sp = NULL, *count_sp, *pci_sp;
46    char tmp[32];
47
48    tok = strtok_r(input,",",&input_sp);
49    if (!tok)
50        goto fail;
51
52    strncpy(tmp, input, 15);
53    tmp[15] = 0;
54
55    tok = strtok_r(tmp,":",&count_sp);
56    if (!tok)
57        goto fail;
58    tok = strtok_r(NULL, ":",&count_sp);
59    if (!tok)
60        goto fail;
61
62    *vga_count = strtoul(tok, NULL, 10);
63    if (*vga_count == LONG_MAX)
64        goto fail;
65
66#ifdef DEBUG
67    fprintf(stderr,"vga count is %d\n", *vga_count);
68#endif
69
70    tok = strtok_r(NULL, ",",&input_sp);
71    if (!tok)
72        goto fail;
73
74    if (match) {
75        strncpy(tmp, tok, 32);
76        tmp[31] = 0;
77        tok = strtok_r(tmp, ":", &pci_sp);
78        if (!tok)
79            goto fail;
80        tok = strtok_r(NULL, ":", &pci_sp);
81        if (!tok)
82            goto fail;
83        match->domain = strtoul(tok, NULL, 16);
84
85        tok = strtok_r(NULL, ":", &pci_sp);
86        if (!tok)
87            goto fail;
88        match->bus = strtoul(tok, NULL, 16);
89
90        tok = strtok_r(NULL, ".", &pci_sp);
91        if (!tok)
92            goto fail;
93        match->dev = strtoul(tok, NULL, 16);
94
95        tok = strtok_r(NULL, ".", &pci_sp);
96        if (!tok)
97            goto fail;
98        match->func = strtoul(tok, NULL, 16);
99    }
100
101    tok = strtok_r(NULL, ",",&input_sp);
102    if (!tok)
103        goto fail;
104    tok = strtok_r(tok, "=", &input_sp);
105    if (!tok)
106        goto fail;
107    tok = strtok_r(NULL, "=", &input_sp);
108    if (!tok)
109        goto fail;
110
111    if (!strncmp(tok, "io+mem", 6))
112        return VGA_ARB_RSRC_LEGACY_IO | VGA_ARB_RSRC_LEGACY_MEM;
113    if (!strncmp(tok, "io", 2))
114        return VGA_ARB_RSRC_LEGACY_IO;
115    if (!strncmp(tok, "mem", 3))
116        return VGA_ARB_RSRC_LEGACY_MEM;
117fail:
118    return VGA_ARB_RSRC_NONE;
119}
120
121int
122pci_device_vgaarb_init(void)
123{
124    struct pci_slot_match match;
125    char buf[BUFSIZE];
126    int ret, rsrc;
127
128    if (!pci_sys)
129        return -1;
130
131    if ((pci_sys->vgaarb_fd = open ("/dev/vga_arbiter", O_RDWR)) < 0) {
132        return errno;
133    }
134
135    ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE);
136    if (ret <= 0)
137        return -1;
138
139    memset(&match, 0xff, sizeof(match));
140    /* need to find the device to go back to and what it was decoding */
141    rsrc = parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, &match);
142
143    pci_sys->vga_default_dev = pci_device_find_by_slot(match.domain, match.bus, match.dev, match.func);
144
145    if (pci_sys->vga_default_dev)
146        pci_sys->vga_default_dev->vgaarb_rsrc = rsrc;
147    return 0;
148}
149
150void
151pci_device_vgaarb_fini(void)
152{
153    if (!pci_sys)
154        return;
155
156    close(pci_sys->vgaarb_fd);
157}
158
159/**
160 * Writes message on vga device. The messages are defined by the kernel
161 * implementation.
162 *
163 * \param fd    vga arbiter device.
164 * \param buf   message itself.
165 * \param len   message length.
166 *
167 * \return
168 * Zero on success, 1 if something gets wrong and 2 if fd is busy (only for
169 * 'trylock')
170 */
171static int
172vgaarb_write(int fd, char *buf, int len)
173{
174    int ret;
175
176
177    buf[len] = '\0';
178
179    ret = write(fd, buf, len);
180    if (ret == -1) {
181        /* the user may have called "trylock" and didn't get the lock */
182        if (errno == EBUSY)
183            return 2;
184
185#ifdef DEBUG
186        fprintf(stderr, "write error");
187#endif
188        return 1;
189    }
190    else if (ret != len) {
191        /* it's need to receive the exactly amount required. */
192#ifdef DEBUG
193        fprintf(stderr, "write error: wrote different than expected\n");
194#endif
195        return 1;
196    }
197
198#ifdef DEBUG
199    fprintf(stderr, "%s: successfully wrote: '%s'\n", __FUNCTION__, buf);
200#endif
201
202    return 0;
203}
204
205
206static const char *
207rsrc_to_str(int iostate)
208{
209    switch (iostate) {
210    case VGA_ARB_RSRC_LEGACY_IO | VGA_ARB_RSRC_LEGACY_MEM:
211        return "io+mem";
212    case VGA_ARB_RSRC_LEGACY_IO:
213        return "io";
214    case VGA_ARB_RSRC_LEGACY_MEM:
215        return "mem";
216    }
217
218    return "none";
219}
220
221int
222pci_device_vgaarb_set_target(struct pci_device *dev)
223{
224    int len;
225    char buf[BUFSIZE];
226    int ret;
227
228    if (!dev)
229        dev = pci_sys->vga_default_dev;
230    if (!dev)
231        return -1;
232
233    len = snprintf(buf, BUFSIZE, "target PCI:%04x:%02x:%02x.%x",
234                   dev->domain, dev->bus, dev->dev, dev->func);
235
236    ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len);
237    if (ret)
238        return ret;
239
240    ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE);
241    if (ret <= 0)
242        return -1;
243
244    dev->vgaarb_rsrc = parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, NULL);
245    pci_sys->vga_target = dev;
246    return 0;
247}
248
249int
250pci_device_vgaarb_decodes(int new_vgaarb_rsrc)
251{
252    int len;
253    char buf[BUFSIZE];
254    int ret;
255    struct pci_device *dev = pci_sys->vga_target;
256
257    if (!dev)
258        return -1;
259    if (dev->vgaarb_rsrc == new_vgaarb_rsrc)
260        return 0;
261
262    len = snprintf(buf, BUFSIZE, "decodes %s", rsrc_to_str(dev->vgaarb_rsrc));
263    ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len);
264    if (ret == 0)
265        dev->vgaarb_rsrc = new_vgaarb_rsrc;
266    return ret;
267}
268
269int
270pci_device_vgaarb_lock(void)
271{
272    int len;
273    char buf[BUFSIZE];
274    struct pci_device *dev = pci_sys->vga_target;
275
276    if (!dev)
277	return -1;
278
279    if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
280        return 0;
281
282    len = snprintf(buf, BUFSIZE, "lock %s", rsrc_to_str(dev->vgaarb_rsrc));
283
284    return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
285}
286
287int
288pci_device_vgaarb_trylock(void)
289{
290    int len;
291    char buf[BUFSIZE];
292    struct pci_device *dev = pci_sys->vga_target;
293
294    if (!dev)
295        return -1;
296
297    if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
298        return 0;
299
300    len = snprintf(buf, BUFSIZE, "trylock %s", rsrc_to_str(dev->vgaarb_rsrc));
301
302    return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
303}
304
305int
306pci_device_vgaarb_unlock(void)
307{
308    int len;
309    char buf[BUFSIZE];
310    struct pci_device *dev = pci_sys->vga_target;
311
312    if (!dev)
313        return -1;
314
315    if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
316        return 0;
317
318    len = snprintf(buf, BUFSIZE, "unlock %s", rsrc_to_str(dev->vgaarb_rsrc));
319
320    return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
321}
322
323int pci_device_vgaarb_get_info(struct pci_device *dev, int *vga_count, int *rsrc_decodes)
324{
325    *vga_count = pci_sys->vga_count;
326    if (!dev)
327        return 0;
328
329    *rsrc_decodes = dev->vgaarb_rsrc;
330        return 0;
331}
332