Skip to content

Commit 1004250

Browse files
committed
ipmi:ssif: Add support for multi-part transmit messages > 2 parts
The spec was fairly confusing about how multi-part transmit messages worked, so the original implementation only added support for two part messages. But after talking about it with others and finding something I missed, I think it makes more sense. The spec mentions smbus command 8 in a table at the end of the section on SSIF support as the end transaction. If that works, then all is good and as it should be. However, some implementations seem to use a middle transaction <32 bytes tomark the end because of the confusion in the spec, even though that is an SMBus violation if the number of bytes is zero. So this change adds some tests, if command=8 works, it uses that, otherwise if an empty end transaction works, it uses a middle transaction <32 bytes to mark the end. If neither works, then it limits the size to 63 bytes as it is now. Cc: Harri Hakkarainen <[email protected]> Cc: Bazhenov, Dmitry <[email protected]> Cc: Mach, Dat <[email protected]> Signed-off-by: Corey Minyard <[email protected]>
1 parent bb9e2ee commit 1004250

File tree

1 file changed

+159
-50
lines changed

1 file changed

+159
-50
lines changed

drivers/char/ipmi/ipmi_ssif.c

Lines changed: 159 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#define SSIF_IPMI_REQUEST 2
6262
#define SSIF_IPMI_MULTI_PART_REQUEST_START 6
6363
#define SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE 7
64+
#define SSIF_IPMI_MULTI_PART_REQUEST_END 8
6465
#define SSIF_IPMI_RESPONSE 3
6566
#define SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE 9
6667

@@ -272,6 +273,7 @@ struct ssif_info {
272273
/* Info from SSIF cmd */
273274
unsigned char max_xmit_msg_size;
274275
unsigned char max_recv_msg_size;
276+
bool cmd8_works; /* See test_multipart_messages() for details. */
275277
unsigned int multi_support;
276278
int supports_pec;
277279

@@ -885,32 +887,33 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
885887
* in the SSIF_MULTI_n_PART case in the probe function
886888
* for details on the intricacies of this.
887889
*/
888-
int left;
890+
int left, to_write;
889891
unsigned char *data_to_send;
892+
unsigned char cmd;
890893

891894
ssif_inc_stat(ssif_info, sent_messages_parts);
892895

893896
left = ssif_info->multi_len - ssif_info->multi_pos;
894-
if (left > 32)
895-
left = 32;
897+
to_write = left;
898+
if (to_write > 32)
899+
to_write = 32;
896900
/* Length byte. */
897-
ssif_info->multi_data[ssif_info->multi_pos] = left;
901+
ssif_info->multi_data[ssif_info->multi_pos] = to_write;
898902
data_to_send = ssif_info->multi_data + ssif_info->multi_pos;
899-
ssif_info->multi_pos += left;
900-
if (left < 32)
901-
/*
902-
* Write is finished. Note that we must end
903-
* with a write of less than 32 bytes to
904-
* complete the transaction, even if it is
905-
* zero bytes.
906-
*/
903+
ssif_info->multi_pos += to_write;
904+
cmd = SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE;
905+
if (ssif_info->cmd8_works) {
906+
if (left == to_write) {
907+
cmd = SSIF_IPMI_MULTI_PART_REQUEST_END;
908+
ssif_info->multi_data = NULL;
909+
}
910+
} else if (to_write < 32) {
907911
ssif_info->multi_data = NULL;
912+
}
908913

909914
rv = ssif_i2c_send(ssif_info, msg_written_handler,
910-
I2C_SMBUS_WRITE,
911-
SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE,
912-
data_to_send,
913-
I2C_SMBUS_BLOCK_DATA);
915+
I2C_SMBUS_WRITE, cmd,
916+
data_to_send, I2C_SMBUS_BLOCK_DATA);
914917
if (rv < 0) {
915918
/* request failed, just return the error. */
916919
ssif_inc_stat(ssif_info, send_errors);
@@ -1242,6 +1245,24 @@ static int ssif_remove(struct i2c_client *client)
12421245
return 0;
12431246
}
12441247

1248+
static int read_response(struct i2c_client *client, unsigned char *resp)
1249+
{
1250+
int ret = -ENODEV, retry_cnt = SSIF_RECV_RETRIES;
1251+
1252+
while (retry_cnt > 0) {
1253+
ret = i2c_smbus_read_block_data(client, SSIF_IPMI_RESPONSE,
1254+
resp);
1255+
if (ret > 0)
1256+
break;
1257+
msleep(SSIF_MSG_MSEC);
1258+
retry_cnt--;
1259+
if (retry_cnt <= 0)
1260+
break;
1261+
}
1262+
1263+
return ret;
1264+
}
1265+
12451266
static int do_cmd(struct i2c_client *client, int len, unsigned char *msg,
12461267
int *resp_len, unsigned char *resp)
12471268
{
@@ -1258,26 +1279,16 @@ static int do_cmd(struct i2c_client *client, int len, unsigned char *msg,
12581279
return -ENODEV;
12591280
}
12601281

1261-
ret = -ENODEV;
1262-
retry_cnt = SSIF_RECV_RETRIES;
1263-
while (retry_cnt > 0) {
1264-
ret = i2c_smbus_read_block_data(client, SSIF_IPMI_RESPONSE,
1265-
resp);
1266-
if (ret > 0)
1267-
break;
1268-
msleep(SSIF_MSG_MSEC);
1269-
retry_cnt--;
1270-
if (retry_cnt <= 0)
1271-
break;
1272-
}
1273-
1282+
ret = read_response(client, resp);
12741283
if (ret > 0) {
12751284
/* Validate that the response is correct. */
12761285
if (ret < 3 ||
12771286
(resp[0] != (msg[0] | (1 << 2))) ||
12781287
(resp[1] != msg[1]))
12791288
ret = -EINVAL;
1280-
else {
1289+
else if (ret > IPMI_MAX_MSG_LENGTH) {
1290+
ret = -E2BIG;
1291+
} else {
12811292
*resp_len = ret;
12821293
ret = 0;
12831294
}
@@ -1389,6 +1400,121 @@ static int find_slave_address(struct i2c_client *client, int slave_addr)
13891400
return slave_addr;
13901401
}
13911402

1403+
static int start_multipart_test(struct i2c_client *client,
1404+
unsigned char *msg, bool do_middle)
1405+
{
1406+
int retry_cnt = SSIF_SEND_RETRIES, ret;
1407+
1408+
retry_write:
1409+
ret = i2c_smbus_write_block_data(client,
1410+
SSIF_IPMI_MULTI_PART_REQUEST_START,
1411+
32, msg);
1412+
if (ret) {
1413+
retry_cnt--;
1414+
if (retry_cnt > 0)
1415+
goto retry_write;
1416+
dev_err(&client->dev, "Could not write multi-part start, though the BMC said it could handle it. Just limit sends to one part.\n");
1417+
return ret;
1418+
}
1419+
1420+
if (!do_middle)
1421+
return 0;
1422+
1423+
ret = i2c_smbus_write_block_data(client,
1424+
SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE,
1425+
32, msg + 32);
1426+
if (ret) {
1427+
dev_err(&client->dev, "Could not write multi-part middle, though the BMC said it could handle it. Just limit sends to one part.\n");
1428+
return ret;
1429+
}
1430+
1431+
return 0;
1432+
}
1433+
1434+
static void test_multipart_messages(struct i2c_client *client,
1435+
struct ssif_info *ssif_info,
1436+
unsigned char *resp)
1437+
{
1438+
unsigned char msg[65];
1439+
int ret;
1440+
bool do_middle;
1441+
1442+
if (ssif_info->max_xmit_msg_size <= 32)
1443+
return;
1444+
1445+
do_middle = ssif_info->max_xmit_msg_size > 63;
1446+
1447+
memset(msg, 0, sizeof(msg));
1448+
msg[0] = IPMI_NETFN_APP_REQUEST << 2;
1449+
msg[1] = IPMI_GET_DEVICE_ID_CMD;
1450+
1451+
/*
1452+
* The specification is all messed up dealing with sending
1453+
* multi-part messages. Per what the specification says, it
1454+
* is impossible to send a message that is a multiple of 32
1455+
* bytes, except for 32 itself. It talks about a "start"
1456+
* transaction (cmd=6) that must be 32 bytes, "middle"
1457+
* transaction (cmd=7) that must be 32 bytes, and an "end"
1458+
* transaction. The "end" transaction is shown as cmd=7 in
1459+
* the text, but if that's the case there is no way to
1460+
* differentiate between a middle and end part except the
1461+
* length being less than 32. But there is a table at the far
1462+
* end of the section (that I had never noticed until someone
1463+
* pointed it out to me) that mentions it as cmd=8.
1464+
*
1465+
* After some thought, I think the example is wrong and the
1466+
* end transaction should be cmd=8. But some systems don't
1467+
* implement cmd=8, they use a zero-length end transaction,
1468+
* even though that violates the SMBus specification.
1469+
*
1470+
* So, to work around this, this code tests if cmd=8 works.
1471+
* If it does, then we use that. If not, it tests zero-
1472+
* byte end transactions. If that works, good. If not,
1473+
* we only allow 63-byte transactions max.
1474+
*/
1475+
1476+
ret = start_multipart_test(client, msg, do_middle);
1477+
if (ret)
1478+
goto out_no_multi_part;
1479+
1480+
ret = i2c_smbus_write_block_data(client,
1481+
SSIF_IPMI_MULTI_PART_REQUEST_END,
1482+
1, msg + 64);
1483+
1484+
if (!ret)
1485+
ret = read_response(client, resp);
1486+
1487+
if (ret > 0) {
1488+
/* End transactions work, we are good. */
1489+
ssif_info->cmd8_works = true;
1490+
return;
1491+
}
1492+
1493+
ret = start_multipart_test(client, msg, do_middle);
1494+
if (ret) {
1495+
dev_err(&client->dev, "Second multipart test failed.\n");
1496+
goto out_no_multi_part;
1497+
}
1498+
1499+
ret = i2c_smbus_write_block_data(client,
1500+
SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE,
1501+
0, msg + 64);
1502+
if (!ret)
1503+
ret = read_response(client, resp);
1504+
if (ret > 0)
1505+
/* Zero-size end parts work, use those. */
1506+
return;
1507+
1508+
/* Limit to 63 bytes and use a short middle command to mark the end. */
1509+
if (ssif_info->max_xmit_msg_size > 63)
1510+
ssif_info->max_xmit_msg_size = 63;
1511+
return;
1512+
1513+
out_no_multi_part:
1514+
ssif_info->max_xmit_msg_size = 32;
1515+
return;
1516+
}
1517+
13921518
/*
13931519
* Global enables we care about.
13941520
*/
@@ -1475,26 +1601,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
14751601
break;
14761602

14771603
case SSIF_MULTI_n_PART:
1478-
/*
1479-
* The specification is rather confusing at
1480-
* this point, but I think I understand what
1481-
* is meant. At least I have a workable
1482-
* solution. With multi-part messages, you
1483-
* cannot send a message that is a multiple of
1484-
* 32-bytes in length, because the start and
1485-
* middle messages are 32-bytes and the end
1486-
* message must be at least one byte. You
1487-
* can't fudge on an extra byte, that would
1488-
* screw up things like fru data writes. So
1489-
* we limit the length to 63 bytes. That way
1490-
* a 32-byte message gets sent as a single
1491-
* part. A larger message will be a 32-byte
1492-
* start and the next message is always going
1493-
* to be 1-31 bytes in length. Not ideal, but
1494-
* it should work.
1495-
*/
1496-
if (ssif_info->max_xmit_msg_size > 63)
1497-
ssif_info->max_xmit_msg_size = 63;
1604+
/* We take whatever size given, but do some testing. */
14981605
break;
14991606

15001607
default:
@@ -1513,6 +1620,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
15131620
ssif_info->supports_pec = 0;
15141621
}
15151622

1623+
test_multipart_messages(client, ssif_info, resp);
1624+
15161625
/* Make sure the NMI timeout is cleared. */
15171626
msg[0] = IPMI_NETFN_APP_REQUEST << 2;
15181627
msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD;

0 commit comments

Comments
 (0)