1616
1717#include <linux/kernel.h>
1818#include <linux/module.h>
19+ #include <linux/capability.h>
1920#include <linux/dmi.h>
2021#include <linux/err.h>
2122#include <linux/gfp.h>
2223#include <linux/mutex.h>
24+ #include <linux/platform_device.h>
2325#include <linux/slab.h>
2426#include <linux/io.h>
2527#include "../../firmware/dcdbas.h"
@@ -39,7 +41,11 @@ static DEFINE_MUTEX(buffer_mutex);
3941static int da_command_address ;
4042static int da_command_code ;
4143static int da_num_tokens ;
44+ static struct platform_device * platform_device ;
4245static 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 ;
4349
4450int dell_smbios_error (int value )
4551{
@@ -157,6 +163,27 @@ static void __init parse_da_table(const struct dmi_header *dm)
157163 da_num_tokens += tokens ;
158164}
159165
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+
160187static void __init find_tokens (const struct dmi_header * dm , void * dummy )
161188{
162189 switch (dm -> type ) {
@@ -170,6 +197,154 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
170197 }
171198}
172199
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+
173348static int __init dell_smbios_init (void )
174349{
175350 const struct dmi_device * valid ;
@@ -197,18 +372,54 @@ static int __init dell_smbios_init(void)
197372 ret = - ENOMEM ;
198373 goto fail_buffer ;
199374 }
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 ;
200394
201395 return 0 ;
202396
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+
203409fail_buffer :
204410 kfree (da_tokens );
205411 return ret ;
206412}
207413
208414static void __exit dell_smbios_exit (void )
209415{
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+ }
211421 free_page ((unsigned long )buffer );
422+ kfree (da_tokens );
212423}
213424
214425subsys_initcall (dell_smbios_init );
0 commit comments