16
16
17
17
#include <linux/kernel.h>
18
18
#include <linux/module.h>
19
+ #include <linux/capability.h>
19
20
#include <linux/dmi.h>
20
21
#include <linux/err.h>
21
22
#include <linux/gfp.h>
22
23
#include <linux/mutex.h>
24
+ #include <linux/platform_device.h>
23
25
#include <linux/slab.h>
24
26
#include <linux/io.h>
25
27
#include "../../firmware/dcdbas.h"
@@ -39,7 +41,11 @@ static DEFINE_MUTEX(buffer_mutex);
39
41
static int da_command_address ;
40
42
static int da_command_code ;
41
43
static int da_num_tokens ;
44
+ static struct platform_device * platform_device ;
42
45
static struct calling_interface_token * da_tokens ;
46
+ static struct device_attribute * token_location_attrs ;
47
+ static struct device_attribute * token_value_attrs ;
48
+ static struct attribute * * token_attrs ;
43
49
44
50
int dell_smbios_error (int value )
45
51
{
@@ -157,6 +163,27 @@ static void __init parse_da_table(const struct dmi_header *dm)
157
163
da_num_tokens += tokens ;
158
164
}
159
165
166
+ static void zero_duplicates (struct device * dev )
167
+ {
168
+ int i , j ;
169
+
170
+ for (i = 0 ; i < da_num_tokens ; i ++ ) {
171
+ if (da_tokens [i ].tokenID == 0 )
172
+ continue ;
173
+ for (j = i + 1 ; j < da_num_tokens ; j ++ ) {
174
+ if (da_tokens [j ].tokenID == 0 )
175
+ continue ;
176
+ if (da_tokens [i ].tokenID == da_tokens [j ].tokenID ) {
177
+ dev_dbg (dev , "Zeroing dup token ID %x(%x/%x)\n" ,
178
+ da_tokens [j ].tokenID ,
179
+ da_tokens [j ].location ,
180
+ da_tokens [j ].value );
181
+ da_tokens [j ].tokenID = 0 ;
182
+ }
183
+ }
184
+ }
185
+ }
186
+
160
187
static void __init find_tokens (const struct dmi_header * dm , void * dummy )
161
188
{
162
189
switch (dm -> type ) {
@@ -170,6 +197,154 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
170
197
}
171
198
}
172
199
200
+ static int match_attribute (struct device * dev ,
201
+ struct device_attribute * attr )
202
+ {
203
+ int i ;
204
+
205
+ for (i = 0 ; i < da_num_tokens * 2 ; i ++ ) {
206
+ if (!token_attrs [i ])
207
+ continue ;
208
+ if (strcmp (token_attrs [i ]-> name , attr -> attr .name ) == 0 )
209
+ return i /2 ;
210
+ }
211
+ dev_dbg (dev , "couldn't match: %s\n" , attr -> attr .name );
212
+ return - EINVAL ;
213
+ }
214
+
215
+ static ssize_t location_show (struct device * dev ,
216
+ struct device_attribute * attr , char * buf )
217
+ {
218
+ int i ;
219
+
220
+ if (!capable (CAP_SYS_ADMIN ))
221
+ return - EPERM ;
222
+
223
+ i = match_attribute (dev , attr );
224
+ if (i > 0 )
225
+ return scnprintf (buf , PAGE_SIZE , "%08x" , da_tokens [i ].location );
226
+ return 0 ;
227
+ }
228
+
229
+ static ssize_t value_show (struct device * dev ,
230
+ struct device_attribute * attr , char * buf )
231
+ {
232
+ int i ;
233
+
234
+ if (!capable (CAP_SYS_ADMIN ))
235
+ return - EPERM ;
236
+
237
+ i = match_attribute (dev , attr );
238
+ if (i > 0 )
239
+ return scnprintf (buf , PAGE_SIZE , "%08x" , da_tokens [i ].value );
240
+ return 0 ;
241
+ }
242
+
243
+ static struct attribute_group smbios_attribute_group = {
244
+ .name = "tokens"
245
+ };
246
+
247
+ static struct platform_driver platform_driver = {
248
+ .driver = {
249
+ .name = "dell-smbios" ,
250
+ },
251
+ };
252
+
253
+ static int build_tokens_sysfs (struct platform_device * dev )
254
+ {
255
+ char buffer_location [13 ];
256
+ char buffer_value [10 ];
257
+ char * location_name ;
258
+ char * value_name ;
259
+ size_t size ;
260
+ int ret ;
261
+ int i , j ;
262
+
263
+ /* (number of tokens + 1 for null terminated */
264
+ size = sizeof (struct device_attribute ) * (da_num_tokens + 1 );
265
+ token_location_attrs = kzalloc (size , GFP_KERNEL );
266
+ if (!token_location_attrs )
267
+ return - ENOMEM ;
268
+ token_value_attrs = kzalloc (size , GFP_KERNEL );
269
+ if (!token_value_attrs )
270
+ goto out_allocate_value ;
271
+
272
+ /* need to store both location and value + terminator*/
273
+ size = sizeof (struct attribute * ) * ((2 * da_num_tokens ) + 1 );
274
+ token_attrs = kzalloc (size , GFP_KERNEL );
275
+ if (!token_attrs )
276
+ goto out_allocate_attrs ;
277
+
278
+ for (i = 0 , j = 0 ; i < da_num_tokens ; i ++ ) {
279
+ /* skip empty */
280
+ if (da_tokens [i ].tokenID == 0 )
281
+ continue ;
282
+ /* add location */
283
+ sprintf (buffer_location , "%04x_location" ,
284
+ da_tokens [i ].tokenID );
285
+ location_name = kstrdup (buffer_location , GFP_KERNEL );
286
+ if (location_name == NULL )
287
+ goto out_unwind_strings ;
288
+ sysfs_attr_init (& token_location_attrs [i ].attr );
289
+ token_location_attrs [i ].attr .name = location_name ;
290
+ token_location_attrs [i ].attr .mode = 0444 ;
291
+ token_location_attrs [i ].show = location_show ;
292
+ token_attrs [j ++ ] = & token_location_attrs [i ].attr ;
293
+
294
+ /* add value */
295
+ sprintf (buffer_value , "%04x_value" ,
296
+ da_tokens [i ].tokenID );
297
+ value_name = kstrdup (buffer_value , GFP_KERNEL );
298
+ if (value_name == NULL )
299
+ goto loop_fail_create_value ;
300
+ sysfs_attr_init (& token_value_attrs [i ].attr );
301
+ token_value_attrs [i ].attr .name = value_name ;
302
+ token_value_attrs [i ].attr .mode = 0444 ;
303
+ token_value_attrs [i ].show = value_show ;
304
+ token_attrs [j ++ ] = & token_value_attrs [i ].attr ;
305
+ continue ;
306
+
307
+ loop_fail_create_value :
308
+ kfree (value_name );
309
+ goto out_unwind_strings ;
310
+ }
311
+ smbios_attribute_group .attrs = token_attrs ;
312
+
313
+ ret = sysfs_create_group (& dev -> dev .kobj , & smbios_attribute_group );
314
+ if (ret )
315
+ goto out_unwind_strings ;
316
+ return 0 ;
317
+
318
+ out_unwind_strings :
319
+ for (i = i - 1 ; i > 0 ; i -- ) {
320
+ kfree (token_location_attrs [i ].attr .name );
321
+ kfree (token_value_attrs [i ].attr .name );
322
+ }
323
+ kfree (token_attrs );
324
+ out_allocate_attrs :
325
+ kfree (token_value_attrs );
326
+ out_allocate_value :
327
+ kfree (token_location_attrs );
328
+
329
+ return - ENOMEM ;
330
+ }
331
+
332
+ static void free_group (struct platform_device * pdev )
333
+ {
334
+ int i ;
335
+
336
+ sysfs_remove_group (& pdev -> dev .kobj ,
337
+ & smbios_attribute_group );
338
+ for (i = 0 ; i < da_num_tokens ; i ++ ) {
339
+ kfree (token_location_attrs [i ].attr .name );
340
+ kfree (token_value_attrs [i ].attr .name );
341
+ }
342
+ kfree (token_attrs );
343
+ kfree (token_value_attrs );
344
+ kfree (token_location_attrs );
345
+ }
346
+
347
+
173
348
static int __init dell_smbios_init (void )
174
349
{
175
350
const struct dmi_device * valid ;
@@ -197,18 +372,54 @@ static int __init dell_smbios_init(void)
197
372
ret = - ENOMEM ;
198
373
goto fail_buffer ;
199
374
}
375
+ ret = platform_driver_register (& platform_driver );
376
+ if (ret )
377
+ goto fail_platform_driver ;
378
+
379
+ platform_device = platform_device_alloc ("dell-smbios" , 0 );
380
+ if (!platform_device ) {
381
+ ret = - ENOMEM ;
382
+ goto fail_platform_device_alloc ;
383
+ }
384
+ ret = platform_device_add (platform_device );
385
+ if (ret )
386
+ goto fail_platform_device_add ;
387
+
388
+ /* duplicate tokens will cause problems building sysfs files */
389
+ zero_duplicates (& platform_device -> dev );
390
+
391
+ ret = build_tokens_sysfs (platform_device );
392
+ if (ret )
393
+ goto fail_create_group ;
200
394
201
395
return 0 ;
202
396
397
+ fail_create_group :
398
+ platform_device_del (platform_device );
399
+
400
+ fail_platform_device_add :
401
+ platform_device_put (platform_device );
402
+
403
+ fail_platform_device_alloc :
404
+ platform_driver_unregister (& platform_driver );
405
+
406
+ fail_platform_driver :
407
+ free_page ((unsigned long )buffer );
408
+
203
409
fail_buffer :
204
410
kfree (da_tokens );
205
411
return ret ;
206
412
}
207
413
208
414
static void __exit dell_smbios_exit (void )
209
415
{
210
- kfree (da_tokens );
416
+ if (platform_device ) {
417
+ free_group (platform_device );
418
+ platform_device_unregister (platform_device );
419
+ platform_driver_unregister (& platform_driver );
420
+ }
211
421
free_page ((unsigned long )buffer );
422
+ kfree (da_tokens );
212
423
}
213
424
214
425
subsys_initcall (dell_smbios_init );
0 commit comments