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