in fsi-occ.c [454:565]
int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
void *response, size_t *resp_len)
{
const unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS);
const unsigned long wait_time =
msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
struct occ *occ = dev_get_drvdata(dev);
struct occ_response *resp = response;
size_t user_resp_len = *resp_len;
u8 seq_no;
u16 checksum = 0;
u16 resp_data_length;
const u8 *byte_request = (const u8 *)request;
unsigned long start;
int rc;
size_t i;
*resp_len = 0;
if (!occ)
return -ENODEV;
if (user_resp_len < 7) {
dev_dbg(dev, "Bad resplen %zd\n", user_resp_len);
return -EINVAL;
}
/* Checksum the request, ignoring first byte (sequence number). */
for (i = 1; i < req_len - 2; ++i)
checksum += byte_request[i];
mutex_lock(&occ->occ_lock);
occ->client_buffer = response;
occ->client_buffer_size = user_resp_len;
occ->client_response_size = 0;
/*
* Get a sequence number and update the counter. Avoid a sequence
* number of 0 which would pass the response check below even if the
* OCC response is uninitialized. Any sequence number the user is
* trying to send is overwritten since this function is the only common
* interface to the OCC and therefore the only place we can guarantee
* unique sequence numbers.
*/
seq_no = occ->sequence_number++;
if (!occ->sequence_number)
occ->sequence_number = 1;
checksum += seq_no;
rc = occ_putsram(occ, request, req_len, seq_no, checksum);
if (rc)
goto done;
rc = occ_trigger_attn(occ);
if (rc)
goto done;
/* Read occ response header */
start = jiffies;
do {
rc = occ_getsram(occ, 0, resp, 8);
if (rc)
goto done;
if (resp->return_status == OCC_RESP_CMD_IN_PRG ||
resp->return_status == OCC_RESP_CRIT_INIT ||
resp->seq_no != seq_no) {
rc = -ETIMEDOUT;
if (time_after(jiffies, start + timeout)) {
dev_err(occ->dev, "resp timeout status=%02x "
"resp seq_no=%d our seq_no=%d\n",
resp->return_status, resp->seq_no,
seq_no);
goto done;
}
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(wait_time);
}
} while (rc);
/* Extract size of response data */
resp_data_length = get_unaligned_be16(&resp->data_length);
/* Message size is data length + 5 bytes header + 2 bytes checksum */
if ((resp_data_length + 7) > user_resp_len) {
rc = -EMSGSIZE;
goto done;
}
dev_dbg(dev, "resp_status=%02x resp_data_len=%d\n",
resp->return_status, resp_data_length);
/* Grab the rest */
if (resp_data_length > 1) {
/* already got 3 bytes resp, also need 2 bytes checksum */
rc = occ_getsram(occ, 8, &resp->data[3], resp_data_length - 1);
if (rc)
goto done;
}
occ->client_response_size = resp_data_length + 7;
rc = occ_verify_checksum(occ, resp, resp_data_length);
done:
*resp_len = occ->client_response_size;
mutex_unlock(&occ->occ_lock);
return rc;
}