Add PnP IDs from Giuseppe Bilotta:
[pnputils] / setpnp.c
1 /*======================================================================
2
3     A utility for reconfiguring PnP BIOS devices
4
5     setpnp.c 1.11 2006/07/06 15:27:55 MDT
6
7     The initial developer of the original code is David A. Hinds
8     <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
9     are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
10
11     (c) Copyright 2006 Hewlett-Packard Development Company, L.P.
12         Bjorn Helgaas <bjorn.helgaas@hp.com>
13
14     This program is free software; you can redistribute it and/or modify
15     it under the terms of the GNU General Public License version 2 as
16     published by the Free Software Foundation.
17
18     Usage:
19
20     setpnp [-b] [device #] [resource list]
21     setpnp [-b] [device #] {on|off}
22
23     The device number is a two-digit hex string.  The resource list
24     consists of a series of resource names and values.  Four resource
25     names are available: "io", "mem", "irq", and "dma".  Values can
26     either be single numbers or dash-delimited ranges.  More than one
27     value can be listed in a single argument, separated by commas.
28     
29     For example:
30
31     setpnp 0d irq 3 io 0x02f8-0x02ff
32
33 ======================================================================*/
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <string.h>
40 #include <endian.h>
41 #include <ctype.h>
42 #include <asm/types.h>
43
44 #include "pnp_resource.h"
45
46 static int verbose = 0, boot = 0;
47
48 #define swap16(n) ((((n)&0x00ff)<<8) | (((n)&0xff00)>>8))
49 #define swap32(n) \
50     ((((n)&0xff000000)>>24) | (((n)&0x00ff0000)>>8) | \
51      (((n)&0x0000ff00)<<8)  | (((n)&0x000000ff)<<24))
52
53 #if (__BYTE_ORDER == _BIG_ENDIAN)
54 #define flip16(n)       swap16(n)
55 #define flip32(n)       swap32(n)
56 #else
57 #define flip16(n)       (n)
58 #define flip32(n)       (n)
59 #endif
60
61 #define NRSRC   4
62 #define NBASE   8
63 #define R_IO    0
64 #define R_MEM   1
65 #define R_IRQ   2
66 #define R_DMA   3
67
68 struct rsrc_list {
69     int         nr[NRSRC];
70     u_long      base[NRSRC][NBASE];
71     u_long      len[NRSRC][NBASE];
72 };
73
74 static const char *rsrc_type[] = { "io", "mem", "irq", "dma" };
75
76 /*====================================================================*/
77
78 static u_char *update_chain(u_char *buf, int nr, struct rsrc_list *res)
79 {
80     union pnp_resource *p = (union pnp_resource *)buf;
81     int tag = 0, sz, nu[4];
82     u_long base, len;
83     
84     nu[0] = nu[1] = nu[2] = nu[3] = 0;
85     while (((u_char *)p < buf+nr) && (tag != PNP_RES_SMTAG_END)) {
86         if (p->lg.tag & PNP_RES_LARGE_ITEM) {
87             union pnp_large_resource *r = &p->lg.d;
88             tag = p->lg.tag & ~PNP_RES_LARGE_ITEM;
89             sz = flip16(p->lg.sz) + 2;
90             switch (tag) {
91             case PNP_RES_LGTAG_MEM:
92                 if (res->nr[R_MEM] > nu[R_MEM]) {
93                     base = res->base[R_MEM][nu[R_MEM]++];
94                     len = res->len[R_MEM][nu[R_MEM]++];
95                     r->mem.min = r->mem.max = flip16(base >> 8);
96                     r->mem.len = flip16(len);
97                 }
98                 break;
99             case PNP_RES_LGTAG_MEM32:
100                 if (res->nr[R_MEM] > nu[R_MEM]) {
101                     base = res->base[R_MEM][nu[R_MEM]++];
102                     len = res->len[R_MEM][nu[R_MEM]++];
103                     r->mem32.min = r->mem32.max = flip32(base);
104                     r->mem32.len = flip32(len);
105                 }
106                 break;
107             case PNP_RES_LGTAG_MEM32_FIXED:
108                 if (res->nr[R_MEM] > nu[R_MEM]) {
109                     base = res->base[R_MEM][nu[R_MEM]];
110                     len = res->len[R_MEM][nu[R_MEM]++];
111                     r->mem32_fixed.base = flip32(base);
112                     r->mem32_fixed.len = flip32(len);
113                 }
114                 break;
115             }
116         } else {
117             union pnp_small_resource *r = &p->sm.d;
118             tag = (p->sm.tag >> 3); sz = (p->sm.tag & 7);
119             switch (tag) {
120             case PNP_RES_SMTAG_IRQ:
121                 if (res->nr[R_IRQ] > nu[R_IRQ]) {
122                     base = res->base[R_IRQ][nu[R_IRQ]];
123                     len = res->len[R_IRQ][nu[R_IRQ]++];
124                     r->irq.mask = len ? flip16(1<<base) : 0;
125                 }
126                 break;
127             case PNP_RES_SMTAG_DMA:
128                 if (res->nr[R_DMA] > nu[R_DMA]) {
129                     base = res->base[R_DMA][nu[R_DMA]];
130                     len = res->len[R_DMA][nu[R_DMA]++];
131                     r->dma.mask = len ? flip16(1<<base) : 0;
132                 }
133                 break;
134             case PNP_RES_SMTAG_IO:
135                 if (res->nr[R_IO] > nu[R_IO]) {
136                     base = res->base[R_IO][nu[R_IO]];
137                     len = res->len[R_IO][nu[R_IO]++];
138                     r->io.min = r->io.max = flip16(base);
139                     r->io.len = len;
140                 }
141                 break;
142             case PNP_RES_SMTAG_IO_FIXED:
143                 if (res->nr[R_IO] > nu[R_IO]) {
144                     base = res->base[R_IO][nu[R_IO]++];
145                     len = res->len[R_IO][nu[R_IO]++];
146                     r->io_fixed.base = flip16(base);
147                     r->io_fixed.len = len;
148                 }
149                 break;
150             }
151         }
152         p = (union pnp_resource *) ((u_char *)p + sz + 1);
153     }
154     return (u_char *)p;
155 }
156
157 static int update_resources(int num, struct rsrc_list *res)
158 {
159     char fn[40];
160     u_char buf[4096];
161     int fd, nr, nw;
162     
163     if (access("/proc/bus/pnp", F_OK) != 0) {
164         fprintf(stderr, "lspnp: /proc/bus/pnp not available\n");
165         return EXIT_FAILURE;
166     }
167     
168     sprintf(fn, "/proc/bus/pnp/%s%02x", (boot ? "boot/" : ""), num);
169     fd = open(fn, O_RDWR);
170     nr = read(fd, buf, sizeof(buf));
171     if (nr <= 0) {
172         perror("read failed");
173         return EXIT_FAILURE;
174     }
175     
176     update_chain(buf, nr, res);
177     nw = write(fd, buf, nr);
178     close(fd);
179     if (nr != nw) {
180         perror("write failed");
181         return EXIT_FAILURE;
182     }
183     return EXIT_SUCCESS;
184 }
185
186 static int reset_resources(int num)
187 {
188     char fn[40];
189     u_char buf[4096];
190     int fd, nr, nw;
191     
192     if (access("/proc/bus/pnp", F_OK) != 0) {
193         fprintf(stderr, "lspnp: /proc/bus/pnp not available\n");
194         return EXIT_FAILURE;
195     }
196     sprintf(fn, "/proc/bus/pnp/boot/%02x", num);
197     fd = open(fn, O_RDONLY);
198     nr = read(fd, buf, sizeof(buf));
199     close(fd);
200     if (nr <= 0) {
201         perror("read failed");
202         return EXIT_FAILURE;
203     }
204     sprintf(fn, "/proc/bus/pnp/%02x", num);
205     fd = open(fn, O_WRONLY);
206     nw = write(fd, buf, nr);
207     close(fd);
208     if (nr != nw) {
209         perror("write failed");
210         return EXIT_FAILURE;
211     }
212     return EXIT_SUCCESS;
213 }
214
215 /*====================================================================*/
216
217 static int parse_resources(char *argv[], int argc,
218                            struct rsrc_list *res)
219 {
220     int i, j;
221     u_long base, len;
222     char *s, *t;
223     
224     for (i = 0; i < argc; i += 2) {
225         for (j = 0; j < NRSRC; j++)
226             if (strcmp(rsrc_type[j], argv[i]) == 0) break;
227         if (j == NRSRC) {
228             fprintf(stderr, "bad resource type: '%s'\n", argv[i]);
229             return EXIT_FAILURE;
230         }
231         s = strtok(argv[i+1], ", \t");
232         while (s) {
233             if (strcmp(s, "off") == 0) {
234                 base = len = 0;
235                 t = s + strlen(s);
236             } else {
237                 base = strtoul(s, &t, 0);
238                 len = ((*t == '-') ? strtoul(t+1, &t, 0)-base+1 : 1);
239             }
240             if ((*s == '\0') || (*t != '\0')) {
241                 fprintf(stderr, "bad resource argument: '%s'\n", t);
242                 return EXIT_FAILURE;
243             }
244             res->base[j][res->nr[j]] = base;
245             res->len[j][res->nr[j]++] = len;
246             s = strtok(NULL, ", \t");
247         }
248     }
249     return EXIT_SUCCESS;
250 }
251
252 /*====================================================================*/
253
254 void usage(char *name)
255 {
256     fprintf(stderr, "usage: %s [-b] [device #] [resources ...]\n"
257             "    or %s [-b] [device #] {on|off}\n", name, name);
258     exit(EXIT_FAILURE);
259 }
260     
261 int main(int argc, char *argv[])
262 {
263     int i, optch, errflg = 0;
264     static struct rsrc_list res;
265     char *s;
266     
267     while ((optch = getopt(argc, argv, "bv")) != -1) {
268         switch (optch) {
269         case 'b':
270             boot++; break;
271         case 'v':
272             verbose++; break;
273         default:
274             errflg = 1; break;
275         }
276     }
277     if (errflg || (optind == argc))
278         usage(argv[0]);
279     
280     i = strtoul(argv[optind], &s, 16);
281     if ((argv[optind] == '\0') || (*s != '\0'))
282         usage(argv[0]);
283     optind++;
284
285     /* Special commands */
286     if (argc == optind+1) {
287         if (strcmp(argv[optind], "off") == 0) {
288             res.nr[0] = res.nr[1] = res.nr[2] = res.nr[3] = 7;
289             optind++;
290         } else if (strcmp(argv[optind], "on") == 0) {
291             return reset_resources(i);
292         } else {
293             usage(argv[0]);
294         }
295     } else if (argc == optind)
296         usage(argv[0]);
297     
298     if ((argc - optind) % 2)
299         usage(argv[0]);
300     if (parse_resources(argv+optind, argc-optind, &res) == 0)
301         return update_resources(i, &res);
302     return EXIT_FAILURE;
303 }