f0df3fbd84025eff9bf304fd179bed8785e865dc
[linux-2.6.git] / 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/gfp.h>
34 #include <linux/module.h>
35 #include <linux/init.h>
36 #include <linux/proc_fs.h>
37 #include <linux/seq_file.h>
38 #include <asm/console.h>
39 #include <asm/uaccess.h>
40 #include <asm/machvec.h>
41
42 #define BASE_DIR        "srm_environment"       /* Subdir in /proc/             */
43 #define NAMED_DIR       "named_variables"       /* Subdir for known variables   */
44 #define NUMBERED_DIR    "numbered_variables"    /* Subdir for all variables     */
45 #define VERSION         "0.0.6"                 /* Module version               */
46 #define NAME            "srm_env"               /* Module name                  */
47
48 MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
49 MODULE_DESCRIPTION("Accessing Alpha SRM environment through procfs interface");
50 MODULE_LICENSE("GPL");
51
52 typedef struct _srm_env {
53         char                    *name;
54         unsigned long           id;
55         struct proc_dir_entry   *proc_entry;
56 } srm_env_t;
57
58 static struct proc_dir_entry    *base_dir;
59 static struct proc_dir_entry    *named_dir;
60 static struct proc_dir_entry    *numbered_dir;
61 static char                     number[256][4];
62
63 static srm_env_t        srm_named_entries[] = {
64         { "auto_action",        ENV_AUTO_ACTION         },
65         { "boot_dev",           ENV_BOOT_DEV            },
66         { "bootdef_dev",        ENV_BOOTDEF_DEV         },
67         { "booted_dev",         ENV_BOOTED_DEV          },
68         { "boot_file",          ENV_BOOT_FILE           },
69         { "booted_file",        ENV_BOOTED_FILE         },
70         { "boot_osflags",       ENV_BOOT_OSFLAGS        },
71         { "booted_osflags",     ENV_BOOTED_OSFLAGS      },
72         { "boot_reset",         ENV_BOOT_RESET          },
73         { "dump_dev",           ENV_DUMP_DEV            },
74         { "enable_audit",       ENV_ENABLE_AUDIT        },
75         { "license",            ENV_LICENSE             },
76         { "char_set",           ENV_CHAR_SET            },
77         { "language",           ENV_LANGUAGE            },
78         { "tty_dev",            ENV_TTY_DEV             },
79         { NULL,                 0                       },
80 };
81 static srm_env_t        srm_numbered_entries[256];
82
83
84 static int srm_env_proc_show(struct seq_file *m, void *v)
85 {
86         unsigned long   ret;
87         srm_env_t       *entry;
88         char            *page;
89
90         entry = m->private;
91         page = (char *)__get_free_page(GFP_USER);
92         if (!page)
93                 return -ENOMEM;
94
95         ret = callback_getenv(entry->id, page, PAGE_SIZE);
96
97         if ((ret >> 61) == 0) {
98                 seq_write(m, page, ret);
99                 ret = 0;
100         } else
101                 ret = -EFAULT;
102         free_page((unsigned long)page);
103         return ret;
104 }
105
106 static int srm_env_proc_open(struct inode *inode, struct file *file)
107 {
108         return single_open(file, srm_env_proc_show, PDE(inode)->data);
109 }
110
111 static ssize_t srm_env_proc_write(struct file *file, const char __user *buffer,
112                                   size_t count, loff_t *pos)
113 {
114         int res;
115         srm_env_t       *entry = PDE(file->f_path.dentry->d_inode)->data;
116         char            *buf = (char *) __get_free_page(GFP_USER);
117         unsigned long   ret1, ret2;
118
119         if (!buf)
120                 return -ENOMEM;
121
122         res = -EINVAL;
123         if (count >= PAGE_SIZE)
124                 goto out;
125
126         res = -EFAULT;
127         if (copy_from_user(buf, buffer, count))
128                 goto out;
129         buf[count] = '\0';
130
131         ret1 = callback_setenv(entry->id, buf, count);
132         if ((ret1 >> 61) == 0) {
133                 do
134                         ret2 = callback_save_env();
135                 while((ret2 >> 61) == 1);
136                 res = (int) ret1;
137         }
138
139  out:
140         free_page((unsigned long)buf);
141         return res;
142 }
143
144 static const struct file_operations srm_env_proc_fops = {
145         .owner          = THIS_MODULE,
146         .open           = srm_env_proc_open,
147         .read           = seq_read,
148         .llseek         = seq_lseek,
149         .release        = single_release,
150         .write          = srm_env_proc_write,
151 };
152
153 static void
154 srm_env_cleanup(void)
155 {
156         srm_env_t       *entry;
157         unsigned long   var_num;
158
159         if (base_dir) {
160                 /*
161                  * Remove named entries
162                  */
163                 if (named_dir) {
164                         entry = srm_named_entries;
165                         while (entry->name != NULL && entry->id != 0) {
166                                 if (entry->proc_entry) {
167                                         remove_proc_entry(entry->name,
168                                                         named_dir);
169                                         entry->proc_entry = NULL;
170                                 }
171                                 entry++;
172                         }
173                         remove_proc_entry(NAMED_DIR, base_dir);
174                 }
175
176                 /*
177                  * Remove numbered entries
178                  */
179                 if (numbered_dir) {
180                         for (var_num = 0; var_num <= 255; var_num++) {
181                                 entry = &srm_numbered_entries[var_num];
182
183                                 if (entry->proc_entry) {
184                                         remove_proc_entry(entry->name,
185                                                         numbered_dir);
186                                         entry->proc_entry       = NULL;
187                                         entry->name             = NULL;
188                                 }
189                         }
190                         remove_proc_entry(NUMBERED_DIR, base_dir);
191                 }
192
193                 remove_proc_entry(BASE_DIR, NULL);
194         }
195
196         return;
197 }
198
199 static int __init
200 srm_env_init(void)
201 {
202         srm_env_t       *entry;
203         unsigned long   var_num;
204
205         /*
206          * Check system
207          */
208         if (!alpha_using_srm) {
209                 printk(KERN_INFO "%s: This Alpha system doesn't "
210                                 "know about SRM (or you've booted "
211                                 "SRM->MILO->Linux, which gets "
212                                 "misdetected)...\n", __func__);
213                 return -ENODEV;
214         }
215
216         /*
217          * Init numbers
218          */
219         for (var_num = 0; var_num <= 255; var_num++)
220                 sprintf(number[var_num], "%ld", var_num);
221
222         /*
223          * Create base directory
224          */
225         base_dir = proc_mkdir(BASE_DIR, NULL);
226         if (!base_dir) {
227                 printk(KERN_ERR "Couldn't create base dir /proc/%s\n",
228                                 BASE_DIR);
229                 goto cleanup;
230         }
231
232         /*
233          * Create per-name subdirectory
234          */
235         named_dir = proc_mkdir(NAMED_DIR, base_dir);
236         if (!named_dir) {
237                 printk(KERN_ERR "Couldn't create dir /proc/%s/%s\n",
238                                 BASE_DIR, NAMED_DIR);
239                 goto cleanup;
240         }
241
242         /*
243          * Create per-number subdirectory
244          */
245         numbered_dir = proc_mkdir(NUMBERED_DIR, base_dir);
246         if (!numbered_dir) {
247                 printk(KERN_ERR "Couldn't create dir /proc/%s/%s\n",
248                                 BASE_DIR, NUMBERED_DIR);
249                 goto cleanup;
250
251         }
252
253         /*
254          * Create all named nodes
255          */
256         entry = srm_named_entries;
257         while (entry->name && entry->id) {
258                 entry->proc_entry = proc_create_data(entry->name, 0644, named_dir,
259                                                      &srm_env_proc_fops, entry);
260                 if (!entry->proc_entry)
261                         goto cleanup;
262                 entry++;
263         }
264
265         /*
266          * Create all numbered nodes
267          */
268         for (var_num = 0; var_num <= 255; var_num++) {
269                 entry = &srm_numbered_entries[var_num];
270                 entry->name = number[var_num];
271
272                 entry->proc_entry = proc_create_data(entry->name, 0644, numbered_dir,
273                                                      &srm_env_proc_fops, entry);
274                 if (!entry->proc_entry)
275                         goto cleanup;
276
277                 entry->id                       = var_num;
278         }
279
280         printk(KERN_INFO "%s: version %s loaded successfully\n", NAME,
281                         VERSION);
282
283         return 0;
284
285 cleanup:
286         srm_env_cleanup();
287
288         return -ENOMEM;
289 }
290
291 static void __exit
292 srm_env_exit(void)
293 {
294         srm_env_cleanup();
295         printk(KERN_INFO "%s: unloaded successfully\n", NAME);
296
297         return;
298 }
299
300 module_init(srm_env_init);
301 module_exit(srm_env_exit);