@@ -80,13 +80,24 @@ static int __power_supply_changed_work(struct power_supply *pst, void *data)
80
80
81
81
static void power_supply_changed_work (struct work_struct * work )
82
82
{
83
+ int ret ;
83
84
unsigned long flags ;
84
85
struct power_supply * psy = container_of (work , struct power_supply ,
85
86
changed_work );
86
87
87
88
dev_dbg (& psy -> dev , "%s\n" , __func__ );
88
89
89
90
spin_lock_irqsave (& psy -> changed_lock , flags );
91
+
92
+ if (unlikely (psy -> update_groups )) {
93
+ psy -> update_groups = false;
94
+ spin_unlock_irqrestore (& psy -> changed_lock , flags );
95
+ ret = sysfs_update_groups (& psy -> dev .kobj , power_supply_dev_type .groups );
96
+ if (ret )
97
+ dev_warn (& psy -> dev , "failed to update sysfs groups: %pe\n" , ERR_PTR (ret ));
98
+ spin_lock_irqsave (& psy -> changed_lock , flags );
99
+ }
100
+
90
101
/*
91
102
* Check 'changed' here to avoid issues due to race between
92
103
* power_supply_changed() and this routine. In worst case
@@ -1208,28 +1219,56 @@ static bool psy_desc_has_property(const struct power_supply_desc *psy_desc,
1208
1219
return found ;
1209
1220
}
1210
1221
1222
+ bool power_supply_ext_has_property (const struct power_supply_ext * psy_ext ,
1223
+ enum power_supply_property psp )
1224
+ {
1225
+ int i ;
1226
+
1227
+ for (i = 0 ; i < psy_ext -> num_properties ; i ++ )
1228
+ if (psy_ext -> properties [i ] == psp )
1229
+ return true;
1230
+
1231
+ return false;
1232
+ }
1233
+
1211
1234
bool power_supply_has_property (struct power_supply * psy ,
1212
1235
enum power_supply_property psp )
1213
1236
{
1237
+ struct power_supply_ext_registration * reg ;
1238
+
1214
1239
if (psy_desc_has_property (psy -> desc , psp ))
1215
1240
return true;
1216
1241
1217
1242
if (power_supply_battery_info_has_prop (psy -> battery_info , psp ))
1218
1243
return true;
1219
1244
1245
+ power_supply_for_each_extension (reg , psy ) {
1246
+ if (power_supply_ext_has_property (reg -> ext , psp ))
1247
+ return true;
1248
+ }
1249
+
1220
1250
return false;
1221
1251
}
1222
1252
1223
1253
int power_supply_get_property (struct power_supply * psy ,
1224
1254
enum power_supply_property psp ,
1225
1255
union power_supply_propval * val )
1226
1256
{
1257
+ struct power_supply_ext_registration * reg ;
1258
+
1227
1259
if (atomic_read (& psy -> use_cnt ) <= 0 ) {
1228
1260
if (!psy -> initialized )
1229
1261
return - EAGAIN ;
1230
1262
return - ENODEV ;
1231
1263
}
1232
1264
1265
+ scoped_guard (rwsem_read , & psy -> extensions_sem ) {
1266
+ power_supply_for_each_extension (reg , psy ) {
1267
+ if (power_supply_ext_has_property (reg -> ext , psp ))
1268
+ return reg -> ext -> get_property (psy , reg -> ext , reg -> data , psp , val );
1269
+ }
1270
+ }
1271
+
1233
1272
if (psy_desc_has_property (psy -> desc , psp ))
1234
1273
return psy -> desc -> get_property (psy , psp , val );
1235
1274
else if (power_supply_battery_info_has_prop (psy -> battery_info , psp ))
@@ -1243,7 +1282,24 @@ int power_supply_set_property(struct power_supply *psy,
1243
1282
enum power_supply_property psp ,
1244
1283
const union power_supply_propval * val )
1245
1284
{
1246
- if (atomic_read (& psy -> use_cnt ) <= 0 || !psy -> desc -> set_property )
1285
+ struct power_supply_ext_registration * reg ;
1286
+
1287
+ if (atomic_read (& psy -> use_cnt ) <= 0 )
1288
+ return - ENODEV ;
1289
+
1290
+ scoped_guard (rwsem_read , & psy -> extensions_sem ) {
1291
+ power_supply_for_each_extension (reg , psy ) {
1292
+ if (power_supply_ext_has_property (reg -> ext , psp )) {
1293
+ if (reg -> ext -> set_property )
1294
+ return reg -> ext -> set_property (psy , reg -> ext , reg -> data ,
1295
+ psp , val );
1296
+ else
1297
+ return - ENODEV ;
1298
+ }
1299
+ }
1300
+ }
1301
+
1302
+ if (!psy -> desc -> set_property )
1247
1303
return - ENODEV ;
1248
1304
1249
1305
return psy -> desc -> set_property (psy , psp , val );
@@ -1253,7 +1309,22 @@ EXPORT_SYMBOL_GPL(power_supply_set_property);
1253
1309
int power_supply_property_is_writeable (struct power_supply * psy ,
1254
1310
enum power_supply_property psp )
1255
1311
{
1256
- return psy -> desc -> property_is_writeable && psy -> desc -> property_is_writeable (psy , psp );
1312
+ struct power_supply_ext_registration * reg ;
1313
+
1314
+ power_supply_for_each_extension (reg , psy ) {
1315
+ if (power_supply_ext_has_property (reg -> ext , psp )) {
1316
+ if (reg -> ext -> property_is_writeable )
1317
+ return reg -> ext -> property_is_writeable (psy , reg -> ext ,
1318
+ reg -> data , psp );
1319
+ else
1320
+ return 0 ;
1321
+ }
1322
+ }
1323
+
1324
+ if (!psy -> desc -> property_is_writeable )
1325
+ return 0 ;
1326
+
1327
+ return psy -> desc -> property_is_writeable (psy , psp );
1257
1328
}
1258
1329
1259
1330
void power_supply_external_power_changed (struct power_supply * psy )
@@ -1272,6 +1343,76 @@ int power_supply_powers(struct power_supply *psy, struct device *dev)
1272
1343
}
1273
1344
EXPORT_SYMBOL_GPL (power_supply_powers );
1274
1345
1346
+ static int power_supply_update_sysfs_and_hwmon (struct power_supply * psy )
1347
+ {
1348
+ unsigned long flags ;
1349
+
1350
+ spin_lock_irqsave (& psy -> changed_lock , flags );
1351
+ psy -> update_groups = true;
1352
+ spin_unlock_irqrestore (& psy -> changed_lock , flags );
1353
+
1354
+ power_supply_changed (psy );
1355
+
1356
+ power_supply_remove_hwmon_sysfs (psy );
1357
+ return power_supply_add_hwmon_sysfs (psy );
1358
+ }
1359
+
1360
+ int power_supply_register_extension (struct power_supply * psy , const struct power_supply_ext * ext ,
1361
+ void * data )
1362
+ {
1363
+ struct power_supply_ext_registration * reg ;
1364
+ size_t i ;
1365
+ int ret ;
1366
+
1367
+ if (!psy || !ext || !ext -> properties || !ext -> num_properties )
1368
+ return - EINVAL ;
1369
+
1370
+ guard (rwsem_write )(& psy -> extensions_sem );
1371
+
1372
+ for (i = 0 ; i < ext -> num_properties ; i ++ )
1373
+ if (power_supply_has_property (psy , ext -> properties [i ]))
1374
+ return - EEXIST ;
1375
+
1376
+ reg = kmalloc (sizeof (* reg ), GFP_KERNEL );
1377
+ if (!reg )
1378
+ return - ENOMEM ;
1379
+
1380
+ reg -> ext = ext ;
1381
+ reg -> data = data ;
1382
+ list_add (& reg -> list_head , & psy -> extensions );
1383
+
1384
+ ret = power_supply_update_sysfs_and_hwmon (psy );
1385
+ if (ret )
1386
+ goto sysfs_hwmon_failed ;
1387
+
1388
+ return 0 ;
1389
+
1390
+ sysfs_hwmon_failed :
1391
+ list_del (& reg -> list_head );
1392
+ kfree (reg );
1393
+ return ret ;
1394
+ }
1395
+ EXPORT_SYMBOL_GPL (power_supply_register_extension );
1396
+
1397
+ void power_supply_unregister_extension (struct power_supply * psy , const struct power_supply_ext * ext )
1398
+ {
1399
+ struct power_supply_ext_registration * reg ;
1400
+
1401
+ guard (rwsem_write )(& psy -> extensions_sem );
1402
+
1403
+ power_supply_for_each_extension (reg , psy ) {
1404
+ if (reg -> ext == ext ) {
1405
+ list_del (& reg -> list_head );
1406
+ kfree (reg );
1407
+ power_supply_update_sysfs_and_hwmon (psy );
1408
+ return ;
1409
+ }
1410
+ }
1411
+
1412
+ dev_warn (& psy -> dev , "Trying to unregister invalid extension" );
1413
+ }
1414
+ EXPORT_SYMBOL_GPL (power_supply_unregister_extension );
1415
+
1275
1416
static void power_supply_dev_release (struct device * dev )
1276
1417
{
1277
1418
struct power_supply * psy = to_power_supply (dev );
@@ -1426,6 +1567,9 @@ __power_supply_register(struct device *parent,
1426
1567
}
1427
1568
1428
1569
spin_lock_init (& psy -> changed_lock );
1570
+ init_rwsem (& psy -> extensions_sem );
1571
+ INIT_LIST_HEAD (& psy -> extensions );
1572
+
1429
1573
rc = device_add (dev );
1430
1574
if (rc )
1431
1575
goto device_add_failed ;
@@ -1438,13 +1582,15 @@ __power_supply_register(struct device *parent,
1438
1582
if (rc )
1439
1583
goto register_thermal_failed ;
1440
1584
1441
- rc = power_supply_create_triggers (psy );
1442
- if (rc )
1443
- goto create_triggers_failed ;
1585
+ scoped_guard (rwsem_read , & psy -> extensions_sem ) {
1586
+ rc = power_supply_create_triggers (psy );
1587
+ if (rc )
1588
+ goto create_triggers_failed ;
1444
1589
1445
- rc = power_supply_add_hwmon_sysfs (psy );
1446
- if (rc )
1447
- goto add_hwmon_sysfs_failed ;
1590
+ rc = power_supply_add_hwmon_sysfs (psy );
1591
+ if (rc )
1592
+ goto add_hwmon_sysfs_failed ;
1593
+ }
1448
1594
1449
1595
/*
1450
1596
* Update use_cnt after any uevents (most notably from device_add()).
0 commit comments