1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Support for the Matrox "lspci" channel
4 Copyright (C) 2005 Matrox Graphics Inc.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "rdesktop.h"
22 #include <sys/types.h>
23 #include <unistd.h>
24
25 static VCHANNEL *lspci_channel;
26
27 typedef struct _pci_device
28 {
29 uint16 klass;
30 uint16 vendor;
31 uint16 device;
32 uint16 subvendor;
33 uint16 subdevice;
34 uint8 revision;
35 uint8 progif;
36 } pci_device;
37
38 static pci_device current_device;
39
40 static void lspci_send(RDPCLIENT * This, const char *output);
41
42
43 /* Handle one line of output from the lspci subprocess */
44 static BOOL
handle_child_line(RDPCLIENT * This,const char * line,void * data)45 handle_child_line(RDPCLIENT * This, const char *line, void *data)
46 {
47 const char *val;
48 char buf[1024];
49
50 if (str_startswith(line, "Class:"))
51 {
52 val = line + sizeof("Class:");
53 /* Skip whitespace and second Class: occurance */
54 val += strspn(val, " \t") + sizeof("Class");
55 current_device.klass = strtol(val, NULL, 16);
56 }
57 else if (str_startswith(line, "Vendor:"))
58 {
59 val = line + sizeof("Vendor:");
60 current_device.vendor = strtol(val, NULL, 16);
61 }
62 else if (str_startswith(line, "Device:"))
63 {
64 val = line + sizeof("Device:");
65 /* Sigh, there are *two* lines tagged as Device:. We
66 are not interested in the domain/bus/slot/func */
67 if (!strchr(val, ':'))
68 current_device.device = strtol(val, NULL, 16);
69 }
70 else if (str_startswith(line, "SVendor:"))
71 {
72 val = line + sizeof("SVendor:");
73 current_device.subvendor = strtol(val, NULL, 16);
74 }
75 else if (str_startswith(line, "SDevice:"))
76 {
77 val = line + sizeof("SDevice:");
78 current_device.subdevice = strtol(val, NULL, 16);
79 }
80 else if (str_startswith(line, "Rev:"))
81 {
82 val = line + sizeof("Rev:");
83 current_device.revision = strtol(val, NULL, 16);
84 }
85 else if (str_startswith(line, "ProgIf:"))
86 {
87 val = line + sizeof("ProgIf:");
88 current_device.progif = strtol(val, NULL, 16);
89 }
90 else if (strspn(line, " \t") == strlen(line))
91 {
92 /* Blank line. Send collected information over channel */
93 snprintf(buf, sizeof(buf), "%04x,%04x,%04x,%04x,%04x,%02x,%02x\n",
94 current_device.klass, current_device.vendor,
95 current_device.device, current_device.subvendor,
96 current_device.subdevice, current_device.revision, current_device.progif);
97 lspci_send(This, buf);
98 memset(¤t_device, 0, sizeof(current_device));
99 }
100 else
101 {
102 warning("lspci: Unrecoqnized line '%s'\n", line);
103 }
104 return True;
105 }
106
107
108 /* Process one line of input from virtual channel */
109 static BOOL
lspci_process_line(RDPCLIENT * This,const char * line,void * data)110 lspci_process_line(RDPCLIENT * This, const char *line, void *data)
111 {
112 char *lspci_command[5] = { "lspci", "-m", "-n", "-v", NULL };
113
114 if (!strcmp(line, "LSPCI"))
115 {
116 memset(¤t_device, 0, sizeof(current_device));
117 subprocess(This, lspci_command, handle_child_line, NULL);
118 /* Send single dot to indicate end of enumeration */
119 lspci_send(This, ".\n");
120 }
121 else
122 {
123 error("lspci protocol error: Invalid line '%s'\n", line);
124 }
125 return True;
126 }
127
128
129 /* Process new data from the virtual channel */
130 static void
lspci_process(RDPCLIENT * This,STREAM s)131 lspci_process(RDPCLIENT * This, STREAM s)
132 {
133 unsigned int pkglen;
134 static char *rest = NULL;
135 char *buf;
136
137 pkglen = s->end - s->p;
138 /* str_handle_lines requires null terminated strings */
139 buf = xmalloc(pkglen + 1);
140 STRNCPY(buf, (char *) s->p, pkglen + 1);
141 #if 0
142 printf("lspci recv:\n");
143 hexdump(s->p, pkglen);
144 #endif
145
146 str_handle_lines(This, buf, &rest, lspci_process_line, NULL);
147 xfree(buf);
148 }
149
150 /* Initialize this module: Register the lspci channel */
151 BOOL
lspci_init(RDPCLIENT * This)152 lspci_init(RDPCLIENT * This)
153 {
154 lspci_channel =
155 channel_register(This, "lspci", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
156 lspci_process);
157 return (lspci_channel != NULL);
158 }
159
160 /* Send data to channel */
161 static void
lspci_send(RDPCLIENT * This,const char * output)162 lspci_send(RDPCLIENT * This, const char *output)
163 {
164 STREAM s;
165 size_t len;
166
167 len = strlen(output);
168 s = channel_init(This, lspci_channel, len);
169 out_uint8p(s, output, len) s_mark_end(s);
170
171 #if 0
172 printf("lspci send:\n");
173 hexdump(s->channel_hdr + 8, s->end - s->channel_hdr - 8);
174 #endif
175
176 channel_send(This, s, lspci_channel);
177 }
178