blob: 515382234a05b24fab664157bdd6f4ad1ff82be5 [file] [log] [blame]
Flemmard49e78422013-10-11 15:48:32 +02001/*
2 * drivers/misc/nduid.c
3 *
4 * Copyright (C) 2009 Palm, Inc.
5 * Author: Yvonne Yip <y@palm.com>
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
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 */
17
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/proc_fs.h>
21#include <linux/seq_file.h>
22#include <linux/crypto.h>
23#include <linux/miscdevice.h>
24#include <linux/platform_device.h>
25#include <linux/nduid.h>
26
27static void *nduid_start(struct seq_file *m, loff_t *pos)
28{
29 return *pos < 1 ? (void *)1 : NULL;
30}
31
32static void *nduid_next(struct seq_file *m, void *v, loff_t *pos)
33{
34 ++pos;
35 return NULL;
36}
37
38static void nduid_stop(struct seq_file *m, void *v)
39{
40 /* nothing to do */
41}
42
43static unsigned char nduid[128];
44
45static int nduid_show(struct seq_file *m, void *v)
46{
47 seq_printf(m, "%s\n", nduid);
48 return 0;
49}
50
51static struct seq_operations nduid_ops = {
52 .start = nduid_start,
53 .next = nduid_next,
54 .stop = nduid_stop,
55 .show = nduid_show,
56};
57
58static int nduid_proc_open(struct inode *inode, struct file *file)
59{
60 return seq_open(file, &nduid_ops);
61}
62
63static struct file_operations nduid_proc_ops = {
64 .open = nduid_proc_open,
65 .read = seq_read,
66 .llseek = seq_lseek,
67 .release = seq_release,
68};
69
70static int __init nduid_setup(char *str)
71{
72 strncpy(nduid, str, sizeof(nduid));
73 return 0;
74}
75__setup("nduid=", nduid_setup);
76
77struct nduid_ctxt {
78 struct miscdevice mdev;
79 struct file_operations fops;
80 unsigned char *nduid_binary;
81 size_t nduid_binary_len;
82};
83
84static struct nduid_ctxt *_nduid_ctxt = NULL;
85
86static ssize_t nduid_mdev_read(struct file *file, char __user *buf,
87 size_t count, loff_t *ptr)
88{
89 struct nduid_ctxt *ctxt;
90
91 ctxt = container_of(file->f_op, struct nduid_ctxt, fops);
92
93 if (count < ctxt->nduid_binary_len) {
94 return -EINVAL;
95 }
96
97 if (copy_to_user(buf, ctxt->nduid_binary, ctxt->nduid_binary_len)) {
98 return -EFAULT;
99 }
100 return ctxt->nduid_binary_len;
101}
102
103static struct file_operations nduid_mdev_fops = {
104 .owner = THIS_MODULE,
105 .read = nduid_mdev_read,
106 .open = nonseekable_open,
107};
108
109/*
110 * nduid_hex_to_char - This function gets an input string
111 * of 2 characters, and converts it into an unsigned
112 * character equivalent.
113 * Returns -1 on error
114 */
115static int nduid_hex_to_char (char *source, unsigned char *dest)
116{
117 unsigned char x, c = 0;
118 if ( (NULL == source) || (source+1 == NULL) ) {
119 return -1;
120 }
121 if (NULL == dest) {
122 return -1;
123 }
124
125 x = source[0];
126 // Ignore zeros on the high part, since the nduid
127 // would have been padded with zeros, for single
128 // characters (e.g 0xa would be 0x0a in the nduid string)
129
130 if ('0' != x) {
131 if ( (x >= '0') && (x <= '9') ) {
132 c = x - '0';
133 } else if ( (x >= 'a') && (x <= 'f') ) {
134 c = x - 'a' + 10;
135 } else if ( (x >= 'A') && (x <= 'F') ) {
136 c = x - 'A' + 10;
137 } else {
138 return -1;
139 }
140 c <<= 4;
141 }
142
143 x = source[1];
144
145 if ( (x >= '0') && (x <= '9') ) {
146 c |= (x - '0');
147 } else if ( (x >= 'a') && (x <= 'f') ) {
148 c |= (x - 'a' + 10);
149 } else if ( (x >= 'A') && (x <= 'F') ) {
150 c |= (x - 'A' + 10);
151 } else {
152 return -1;
153 }
154 *dest = c;
155 return 0;
156}
157
158
159static int nduid_probe(struct platform_device *pdev)
160{
161 int r = 0, i;
162 struct nduid_config *pcfg;
163 struct nduid_ctxt *ctxt;
164 unsigned char c;
165 if (_nduid_ctxt) {
166 printk(KERN_ERR "nduid: more than one nduid device\n");
167 return -EINVAL;
168 }
169
170 pcfg = pdev->dev.platform_data;
171 if (!pcfg) {
172 printk(KERN_ERR "nduid: no platform data\n");
173 return -ENODEV;
174 }
175
176 ctxt = kzalloc(sizeof(struct nduid_ctxt), GFP_KERNEL);
177 if (!ctxt) {
178 printk(KERN_ERR "nduid: can't alloc ctxt\n");
179 return -ENOMEM;
180 }
181
182 _nduid_ctxt = ctxt;
183 platform_set_drvdata(pdev, ctxt);
184
185 // Allocate half the length of nduid, as 2 chars
186 // of the string, represent one byte of binary data
187 ctxt->nduid_binary_len = strlen (nduid) / 2;
188
189 ctxt->nduid_binary = kmalloc(ctxt->nduid_binary_len, GFP_KERNEL);
190 if (!ctxt->nduid_binary) {
191 printk(KERN_ERR "nduid: error allocating nduid binary\n");
192 goto nduid_probe_fail;
193 }
194
195 // Construct the binary data, from the nduid string
196 for (i = 0; i < ctxt->nduid_binary_len; i++) {
197 c = 0;
198 if (nduid_hex_to_char (nduid + (i*2), &c) ) {
199 break;
200 }
201 ctxt->nduid_binary [i] = c;
202 }
203
204 // If any errors in constructing the dev id,
205 // zero out the binary
206 if (i != ctxt->nduid_binary_len) {
207 memset(ctxt->nduid_binary, 0, ctxt->nduid_binary_len);
208 }
209
210 /* init misc device */
211 memcpy(&ctxt->fops, &nduid_mdev_fops, sizeof(struct file_operations));
212 ctxt->mdev.minor = MISC_DYNAMIC_MINOR;
213 ctxt->mdev.name = pcfg->dev_name;
214 ctxt->mdev.fops = &ctxt->fops;
215
216 /* register misc device */
217 r = misc_register(&ctxt->mdev);
218 if (r) {
219 printk(KERN_ERR "nduid: failed to register misc device\n");
220 goto nduid_probe_fail;
221 }
222 return 0;
223
224nduid_probe_fail:
225 if (ctxt) {
226 if (ctxt->nduid_binary) {
227 kfree(ctxt->nduid_binary);
228 }
229 kfree(ctxt);
230 }
231 return -1;
232}
233
234static int __devexit nduid_remove(struct platform_device *pdev)
235{
236 struct nduid_ctxt *ctxt;
237
238 ctxt = platform_get_drvdata(pdev);
239 if (!ctxt) {
240 return 0;
241 }
242
243 misc_deregister(&ctxt->mdev);
244
245 kfree(ctxt->nduid_binary);
246 kfree(ctxt);
247 return 0;
248}
249
250
251#ifdef CONFIG_PM
252static int nduid_suspend(struct platform_device *pdev, pm_message_t state)
253{
254 /* nothing to do */
255 return 0;
256}
257
258static int nduid_resume(struct platform_device *pdev)
259{
260 /* nothing to do */
261 return 0;
262}
263#else
264#define nduid_suspend NULL
265#define nduid_resume NULL
266#endif
267
268static struct platform_driver nduid_driver = {
269 .driver = {
270 .name = "nduid",
271 },
272 .probe = nduid_probe,
273 .remove = __devexit_p(nduid_remove),
274 .suspend = nduid_suspend,
275 .resume = nduid_resume,
276};
277
278static int __init nduid_init(void)
279{
280 struct proc_dir_entry *entry;
281
282 entry = create_proc_entry("nduid", S_IRUGO, NULL);
283 if (entry == NULL) {
284 return -1;
285 }
286
287 entry->proc_fops = &nduid_proc_ops;
288
289 printk(KERN_INFO "nduid: %s\n", nduid);
290
291 return platform_driver_register(&nduid_driver);
292}
293
294static void __exit nduid_exit(void)
295{
296 platform_driver_unregister(&nduid_driver);
297}
298
299module_init(nduid_init);
300module_exit(nduid_exit);
301
302MODULE_DESCRIPTION("Nova device UID driver");
303MODULE_LICENSE("GPL");