@@ -1247,6 +1247,100 @@ static void vdm_state_machine_work(struct work_struct *work)
1247
1247
mutex_unlock (& port -> lock );
1248
1248
}
1249
1249
1250
+ enum pdo_err {
1251
+ PDO_NO_ERR ,
1252
+ PDO_ERR_NO_VSAFE5V ,
1253
+ PDO_ERR_VSAFE5V_NOT_FIRST ,
1254
+ PDO_ERR_PDO_TYPE_NOT_IN_ORDER ,
1255
+ PDO_ERR_FIXED_NOT_SORTED ,
1256
+ PDO_ERR_VARIABLE_BATT_NOT_SORTED ,
1257
+ PDO_ERR_DUPE_PDO ,
1258
+ };
1259
+
1260
+ static const char * const pdo_err_msg [] = {
1261
+ [PDO_ERR_NO_VSAFE5V ] =
1262
+ " err: source/sink caps should atleast have vSafe5V" ,
1263
+ [PDO_ERR_VSAFE5V_NOT_FIRST ] =
1264
+ " err: vSafe5V Fixed Supply Object Shall always be the first object" ,
1265
+ [PDO_ERR_PDO_TYPE_NOT_IN_ORDER ] =
1266
+ " err: PDOs should be in the following order: Fixed; Battery; Variable" ,
1267
+ [PDO_ERR_FIXED_NOT_SORTED ] =
1268
+ " err: Fixed supply pdos should be in increasing order of their fixed voltage" ,
1269
+ [PDO_ERR_VARIABLE_BATT_NOT_SORTED ] =
1270
+ " err: Variable/Battery supply pdos should be in increasing order of their minimum voltage" ,
1271
+ [PDO_ERR_DUPE_PDO ] =
1272
+ " err: Variable/Batt supply pdos cannot have same min/max voltage" ,
1273
+ };
1274
+
1275
+ static enum pdo_err tcpm_caps_err (struct tcpm_port * port , const u32 * pdo ,
1276
+ unsigned int nr_pdo )
1277
+ {
1278
+ unsigned int i ;
1279
+
1280
+ /* Should at least contain vSafe5v */
1281
+ if (nr_pdo < 1 )
1282
+ return PDO_ERR_NO_VSAFE5V ;
1283
+
1284
+ /* The vSafe5V Fixed Supply Object Shall always be the first object */
1285
+ if (pdo_type (pdo [0 ]) != PDO_TYPE_FIXED ||
1286
+ pdo_fixed_voltage (pdo [0 ]) != VSAFE5V )
1287
+ return PDO_ERR_VSAFE5V_NOT_FIRST ;
1288
+
1289
+ for (i = 1 ; i < nr_pdo ; i ++ ) {
1290
+ if (pdo_type (pdo [i ]) < pdo_type (pdo [i - 1 ])) {
1291
+ return PDO_ERR_PDO_TYPE_NOT_IN_ORDER ;
1292
+ } else if (pdo_type (pdo [i ]) == pdo_type (pdo [i - 1 ])) {
1293
+ enum pd_pdo_type type = pdo_type (pdo [i ]);
1294
+
1295
+ switch (type ) {
1296
+ /*
1297
+ * The remaining Fixed Supply Objects, if
1298
+ * present, shall be sent in voltage order;
1299
+ * lowest to highest.
1300
+ */
1301
+ case PDO_TYPE_FIXED :
1302
+ if (pdo_fixed_voltage (pdo [i ]) <=
1303
+ pdo_fixed_voltage (pdo [i - 1 ]))
1304
+ return PDO_ERR_FIXED_NOT_SORTED ;
1305
+ break ;
1306
+ /*
1307
+ * The Battery Supply Objects and Variable
1308
+ * supply, if present shall be sent in Minimum
1309
+ * Voltage order; lowest to highest.
1310
+ */
1311
+ case PDO_TYPE_VAR :
1312
+ case PDO_TYPE_BATT :
1313
+ if (pdo_min_voltage (pdo [i ]) <
1314
+ pdo_min_voltage (pdo [i - 1 ]))
1315
+ return PDO_ERR_VARIABLE_BATT_NOT_SORTED ;
1316
+ else if ((pdo_min_voltage (pdo [i ]) ==
1317
+ pdo_min_voltage (pdo [i - 1 ])) &&
1318
+ (pdo_max_voltage (pdo [i ]) ==
1319
+ pdo_min_voltage (pdo [i - 1 ])))
1320
+ return PDO_ERR_DUPE_PDO ;
1321
+ break ;
1322
+ default :
1323
+ tcpm_log_force (port , " Unknown pdo type" );
1324
+ }
1325
+ }
1326
+ }
1327
+
1328
+ return PDO_NO_ERR ;
1329
+ }
1330
+
1331
+ static int tcpm_validate_caps (struct tcpm_port * port , const u32 * pdo ,
1332
+ unsigned int nr_pdo )
1333
+ {
1334
+ enum pdo_err err_index = tcpm_caps_err (port , pdo , nr_pdo );
1335
+
1336
+ if (err_index != PDO_NO_ERR ) {
1337
+ tcpm_log_force (port , " %s" , pdo_err_msg [err_index ]);
1338
+ return - EINVAL ;
1339
+ }
1340
+
1341
+ return 0 ;
1342
+ }
1343
+
1250
1344
/*
1251
1345
* PD (data, control) command handling functions
1252
1346
*/
@@ -1269,6 +1363,9 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
1269
1363
1270
1364
tcpm_log_source_caps (port );
1271
1365
1366
+ tcpm_validate_caps (port , port -> source_caps ,
1367
+ port -> nr_source_caps );
1368
+
1272
1369
/*
1273
1370
* This message may be received even if VBUS is not
1274
1371
* present. This is quite unexpected; see USB PD
@@ -3435,9 +3532,12 @@ static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo,
3435
3532
return nr_vdo ;
3436
3533
}
3437
3534
3438
- void tcpm_update_source_capabilities (struct tcpm_port * port , const u32 * pdo ,
3439
- unsigned int nr_pdo )
3535
+ int tcpm_update_source_capabilities (struct tcpm_port * port , const u32 * pdo ,
3536
+ unsigned int nr_pdo )
3440
3537
{
3538
+ if (tcpm_validate_caps (port , pdo , nr_pdo ))
3539
+ return - EINVAL ;
3540
+
3441
3541
mutex_lock (& port -> lock );
3442
3542
port -> nr_src_pdo = tcpm_copy_pdos (port -> src_pdo , pdo , nr_pdo );
3443
3543
switch (port -> state ) {
@@ -3457,16 +3557,20 @@ void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo,
3457
3557
break ;
3458
3558
}
3459
3559
mutex_unlock (& port -> lock );
3560
+ return 0 ;
3460
3561
}
3461
3562
EXPORT_SYMBOL_GPL (tcpm_update_source_capabilities );
3462
3563
3463
- void tcpm_update_sink_capabilities (struct tcpm_port * port , const u32 * pdo ,
3464
- unsigned int nr_pdo ,
3465
- unsigned int max_snk_mv ,
3466
- unsigned int max_snk_ma ,
3467
- unsigned int max_snk_mw ,
3468
- unsigned int operating_snk_mw )
3564
+ int tcpm_update_sink_capabilities (struct tcpm_port * port , const u32 * pdo ,
3565
+ unsigned int nr_pdo ,
3566
+ unsigned int max_snk_mv ,
3567
+ unsigned int max_snk_ma ,
3568
+ unsigned int max_snk_mw ,
3569
+ unsigned int operating_snk_mw )
3469
3570
{
3571
+ if (tcpm_validate_caps (port , pdo , nr_pdo ))
3572
+ return - EINVAL ;
3573
+
3470
3574
mutex_lock (& port -> lock );
3471
3575
port -> nr_snk_pdo = tcpm_copy_pdos (port -> snk_pdo , pdo , nr_pdo );
3472
3576
port -> max_snk_mv = max_snk_mv ;
@@ -3485,6 +3589,7 @@ void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
3485
3589
break ;
3486
3590
}
3487
3591
mutex_unlock (& port -> lock );
3592
+ return 0 ;
3488
3593
}
3489
3594
EXPORT_SYMBOL_GPL (tcpm_update_sink_capabilities );
3490
3595
@@ -3520,7 +3625,15 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
3520
3625
3521
3626
init_completion (& port -> tx_complete );
3522
3627
init_completion (& port -> swap_complete );
3628
+ tcpm_debugfs_init (port );
3523
3629
3630
+ if (tcpm_validate_caps (port , tcpc -> config -> src_pdo ,
3631
+ tcpc -> config -> nr_src_pdo ) ||
3632
+ tcpm_validate_caps (port , tcpc -> config -> snk_pdo ,
3633
+ tcpc -> config -> nr_snk_pdo )) {
3634
+ err = - EINVAL ;
3635
+ goto out_destroy_wq ;
3636
+ }
3524
3637
port -> nr_src_pdo = tcpm_copy_pdos (port -> src_pdo , tcpc -> config -> src_pdo ,
3525
3638
tcpc -> config -> nr_src_pdo );
3526
3639
port -> nr_snk_pdo = tcpm_copy_pdos (port -> snk_pdo , tcpc -> config -> snk_pdo ,
@@ -3575,7 +3688,6 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
3575
3688
}
3576
3689
}
3577
3690
3578
- tcpm_debugfs_init (port );
3579
3691
mutex_lock (& port -> lock );
3580
3692
tcpm_init (port );
3581
3693
mutex_unlock (& port -> lock );
0 commit comments