Pull bugfix into test branch
[linux-2.6] / arch / alpha / kernel / srm_env.c
1 /*
2  * srm_env.c - Access to SRM environment
3  *             variables through linux' procfs
4  *
5  * (C) 2001,2002,2006 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
6  *
7  * This driver is at all a modified version of Erik Mouw's
8  * Documentation/DocBook/procfs_example.c, so: thank
9  * you, Erik! He can be reached via email at
10  * <J.A.K.Mouw@its.tudelft.nl>. It is based on an idea
11  * provided by DEC^WCompaq^WIntel's "Jumpstart" CD. They
12  * included a patch like this as well. Thanks for idea!
13  *
14  * This program is free software; you can redistribute
15  * it and/or modify it under the terms of the GNU General
16  * Public License version 2 as published by the Free Software
17  * Foundation.
18  *
19  * This program is distributed in the hope that it will be
20  * useful, but WITHOUT ANY WARRANTY; without even the implied
21  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
22  * PURPOSE.  See the GNU General Public License for more
23  * details.
24  *
25  * You should have received a copy of the GNU General Public
26  * License along with this program; if not, write to the
27  * Free Software Foundation, Inc., 59 Temple Place,
28  * Suite 330, Boston, MA  02111-1307  USA
29  *
30  */
31
32 #include <linux/kernel.h>
33 #include <linux/module.h>
34 #include <linux/init.h>
35 #include <linux/proc_fs.h>
36 #include <asm/console.h>
37 #include <asm/uaccess.h>
38 #include <asm/machvec.h>
39
40 #define BASE_DIR        "srm_environment"       /* Subdir in /proc/             */
41 #define NAMED_DIR       "named_variables"       /* Subdir for known variables   */
42 #define NUMBERED_DIR    "numbered_variables"    /* Subdir for all variables     */
43 #define VERSION         "0.0.6"                 /* Module version               */
44 #define NAME            "srm_env"               /* Module name                  */
45
46 MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
47 MODULE_DESCRIPTION("Accessing Alpha SRM environment through procfs interface");
48 MODULE_LICENSE("GPL");
49
50 typedef struct _srm_env {
51         char                    *name;
52         unsigned long           id;
53         struct proc_dir_entry   *proc_entry;
54 } srm_env_t;
55
56 static struct proc_dir_entry    *base_dir;
57 static struct proc_dir_entry    *named_dir;
58 static struct proc_dir_entry    *numbered_dir;
59 static char                     number[256][4];
60
61 static srm_env_t        srm_named_entries[] = {
62         { "auto_action",        ENV_AUTO_ACTION         },
63         { "boot_dev",           ENV_BOOT_DEV            },
64         { "bootdef_dev",        ENV_BOOTDEF_DEV         },
65         { "booted_dev",         ENV_BOOTED_DEV          },
66         { "boot_file",          ENV_BOOT_FILE           },
67         { "booted_file",        ENV_BOOTED_FILE         },
68         { "boot_osflags",       ENV_BOOT_OSFLAGS        },
69         { "booted_osflags",     ENV_BOOTED_OSFLAGS      },
70         { "boot_reset",         ENV_BOOT_RESET          },
71         { "dump_dev",           ENV_DUMP_DEV            },
72         { "enable_audit",       ENV_ENABLE_AUDIT        },
73         { "license",            ENV_LICENSE             },
74         { "char_set",           ENV_CHAR_SET            },
75         { "language",           ENV_LANGUAGE            },
76         { "tty_dev",            ENV_TTY_DEV             },
77         { NULL,                 0                       },
78 };
79 static srm_env_t        srm_numbered_entries[256];
80
81
82 static int
83 srm_env_read(char *page, char **start, off_t off, int count, int *eof,
84                 void *data)
85 {
86         int             nbytes;
87         unsigned long   ret;
88         srm_env_t       *entry;
89
90         if (off != 0) {
91                 *eof = 1;
92                 return 0;
93         }
94
95         entry   = (srm_env_t *) data;
96         ret     = callback_getenv(entry->id, page, count);
97
98         if ((ret >> 61) == 0) {
99                 nbytes = (int) ret;
100                 *eof = 1;
101         } else
102                 nbytes = -EFAULT;
103
104         return nbytes;
105 }
106
107 static int
108 srm_env_write(struct file *file, const char __user *buffer, unsigned long count,
109                 void *data)
110 {
111         int res;
112         srm_env_t       *entry;
113         char            *buf = (char *) __get_free_page(GFP_USER);
114         unsigned long   ret1, ret2;
115
116         entry = (srm_env_t *) data;
117
118         if (!buf)
119                 return -ENOMEM;
120
121         res = -EINVAL;
122         if (count >= PAGE_SIZE)
123                 goto out;
124
125         res = -EFAULT;
126         if (copy_from_user(buf, buffer, count))
127                 goto out;
128         buf[count] = '\0';
129
130         ret1 = callback_setenv(entry->id, buf, count);
131         if ((ret1 >> 61) == 0) {
132                 do
133                         ret2 = callback_save_env();
134                 while((ret2 >> 61) == 1);
135                 res = (int) ret1;
136         }
137
138  out:
139         free_page((unsigned long)buf);
140         return res;
141 }
142
143 static void
144 srm_env_cleanup(void)
145 {
146         srm_env_t       *entry;
147         unsigned long   var_num;
148
149         if (base_dir) {
150                 /*
151                  * Remove named entries
152                  */
153                 if (named_dir) {
154                         entry = srm_named_entries;
155                         while (entry->name != NULL && entry->id != 0) {
156                                 if (entry->proc_entry) {
157                                         remove_proc_entry(entry->name,
158                                                         named_dir);
159                                         entry->proc_entry = NULL;
160                                 }
161                                 entry++;
162                         }
163                         remove_proc_entry(NAMED_DIR, base_dir);
164                 }
165
166                 /*
167                  * Remove numbered entries
168                  */
169                 if (numbered_dir) {
170                         for (var_num = 0; var_num <= 255; var_num++) {
171                                 entry = &srm_numbered_entries[var_num];
172
173                                 if (entry->proc_entry) {
174                                         remove_proc_entry(entry->name,
175                                                         numbered_dir);
176                                         entry->proc_entry       = NULL;
177                                         entry->name             = NULL;
178                                 }
179                         }
180                         remove_proc_entry(NUMBERED_DIR, base_dir);
181                 }
182
183                 remove_proc_entry(BASE_DIR, NULL);
184         }
185
186         return;
187 }
188
189 static int __init
190 srm_env_init(void)
191 {
192         srm_env_t       *entry;
193         unsigned long   var_num;
194
195         /*
196          * Check system
197          */
198         if (!alpha_using_srm) {
199                 printk(KERN_INFO "%s: This Alpha system doesn't "
200                                 "know about SRM (or you've booted "
201                                 "SRM->MILO->Linux, which gets "
202                                 "misdetected)...\n", __FUNCTION__);
203                 return -ENODEV;
204         }
205
206         /*
207          * Init numbers
208          */
209         for (var_num = 0; var_num <= 255; var_num++)
210                 sprintf(number[var_num], "%ld", var_num);
211
212         /*
213          * Create base directory
214          */
215         base_dir = proc_mkdir(BASE_DIR, NULL);
216         if (!base_dir) {
217                 printk(KERN_ERR "Couldn't create base dir /proc/%s\n",
218                                 BASE_DIR);
219                 goto cleanup;
220         }
221         base_dir->owner = THIS_MODULE;
222
223         /*
224          * Create per-name subdirectory
225          */
226         named_dir = proc_mkdir(NAMED_DIR, base_dir);
227         if (!named_dir) {
228                 printk(KERN_ERR "Couldn't create dir /proc/%s/%s\n",
229                                 BASE_DIR, NAMED_DIR);
230                 goto cleanup;
231         }
232         named_dir->owner = THIS_MODULE;
233
234         /*
235          * Create per-number subdirectory
236          */
237         numbered_dir = proc_mkdir(NUMBERED_DIR, base_dir);
238         if (!numbered_dir) {
239                 printk(KERN_ERR "Couldn't create dir /proc/%s/%s\n",
240                                 BASE_DIR, NUMBERED_DIR);
241                 goto cleanup;
242
243         }
244         numbered_dir->owner = THIS_MODULE;
245
246         /*
247          * Create all named nodes
248          */
249         entry = srm_named_entries;
250         while (entry->name && entry->id) {
251                 entry->proc_entry = create_proc_entry(entry->name,
252                                 0644, named_dir);
253                 if (!entry->proc_entry)
254                         goto cleanup;
255
256                 entry->proc_entry->data         = (void *) entry;
257                 entry->proc_entry->owner        = THIS_MODULE;
258                 entry->proc_entry->read_proc    = srm_env_read;
259                 entry->proc_entry->write_proc   = srm_env_write;
260
261                 entry++;
262         }
263
264         /*
265          * Create all numbered nodes
266          */
267         for (var_num = 0; var_num <= 255; var_num++) {
268                 entry = &srm_numbered_entries[var_num];
269                 entry->name = number[var_num];
270
271                 entry->proc_entry = create_proc_entry(entry->name,
272                                 0644, numbered_dir);
273                 if (!entry->proc_entry)
274                         goto cleanup;
275
276                 entry->id                       = var_num;
277                 entry->proc_entry->data         = (void *) entry;
278                 entry->proc_entry->owner        = THIS_MODULE;
279                 entry->proc_entry->read_proc    = srm_env_read;
280                 entry->proc_entry->write_proc   = srm_env_write;
281         }
282
283         printk(KERN_INFO "%s: version %s loaded successfully\n", NAME,
284                         VERSION);
285
286         return 0;
287
288 cleanup:
289         srm_env_cleanup();
290
291         return -ENOMEM;
292 }
293
294 static void __exit
295 srm_env_exit(void)
296 {
297         srm_env_cleanup();
298         printk(KERN_INFO "%s: unloaded successfully\n", NAME);
299
300         return;
301 }
302
303 module_init(srm_env_init);
304 module_exit(srm_env_exit);