/* * SSP -> AP Instruction * They tell what packet type can be expected. In the future there will * be less of them. BYPASS means common sensor packets with accel, gyro, * hrm etc. data. LIBRARY and META are mock-up's for now.
*/ #define SSP_MSG2AP_INST_BYPASS_DATA 0x37 #define SSP_MSG2AP_INST_LIBRARY_DATA 0x01 #define SSP_MSG2AP_INST_DEBUG_DATA 0x03 #define SSP_MSG2AP_INST_BIG_DATA 0x04 #define SSP_MSG2AP_INST_META_DATA 0x05 #define SSP_MSG2AP_INST_TIME_SYNC 0x06 #define SSP_MSG2AP_INST_RESET 0x07
/* * It is a bit heavy to do it this way but often the function is used to compose * the message from smaller chunks which are placed on the stack. Often the * chunks are small so memcpy should be optimized.
*/ staticinlinevoid ssp_fill_buffer(struct ssp_msg *m, unsignedint offset, constvoid *src, unsignedint len)
{
memcpy(&m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], src, len);
}
ssp_dbg("[SSP]: MSG From MCU - %s\n", &data_frame[*data_index]);
*data_index += length;
return 0;
}
/* * It was designed that way - additional lines to some kind of handshake, * please do not ask why - only the firmware guy can know it.
*/ staticint ssp_check_lines(struct ssp_data *data, bool state)
{ int delay_cnt = 0;
if (!state)
gpiod_set_value_cansleep(data->ap_mcu_gpiod, 1);
return -ETIMEDOUT;
}
}
return 0;
}
staticint ssp_do_transfer(struct ssp_data *data, struct ssp_msg *msg, struct completion *done, int timeout)
{ int status; /* * check if this is a short one way message or the whole transfer has * second part after an interrupt
*/ constbool use_no_irq = msg->length == 0;
if (data->shut_down) return -EPERM;
msg->done = done;
mutex_lock(&data->comm_lock);
status = ssp_check_lines(data, false); if (status < 0) goto _error_locked;
status = spi_write(data->spi, msg->buffer, SSP_HEADER_SIZE); if (status < 0) {
gpiod_set_value_cansleep(data->ap_mcu_gpiod, 1);
dev_err(SSP_DEV, "%s spi_write fail\n", __func__); goto _error_locked;
}
if (!use_no_irq) {
mutex_lock(&data->pending_lock);
list_add_tail(&msg->list, &data->pending_list);
mutex_unlock(&data->pending_lock);
}
status = ssp_check_lines(data, true); if (status < 0) { if (!use_no_irq) {
mutex_lock(&data->pending_lock);
list_del(&msg->list);
mutex_unlock(&data->pending_lock);
} goto _error_locked;
}
mutex_unlock(&data->comm_lock);
if (!use_no_irq && done) if (wait_for_completion_timeout(done,
msecs_to_jiffies(timeout)) ==
0) {
mutex_lock(&data->pending_lock);
list_del(&msg->list);
mutex_unlock(&data->pending_lock);
staticint ssp_handle_big_data(struct ssp_data *data, char *dataframe, int *idx)
{ /* mock-up, it will be changed with adding another sensor types */
*idx += 8; return 0;
}
staticint ssp_parse_dataframe(struct ssp_data *data, char *dataframe, int len)
{ int idx, sd; struct ssp_sensor_data *spd; struct iio_dev **indio_devs = data->sensor_devs;
for (idx = 0; idx < len;) { switch (dataframe[idx++]) { case SSP_MSG2AP_INST_BYPASS_DATA: if (idx >= len) return -EPROTO;
sd = dataframe[idx++]; if (sd < 0 || sd >= SSP_SENSOR_MAX) {
dev_err(SSP_DEV, "Mcu data frame1 error %d\n", sd); return -EPROTO;
}
if (indio_devs[sd]) {
spd = iio_priv(indio_devs[sd]); if (spd->process_data) { if (idx >= len) return -EPROTO;
spd->process_data(indio_devs[sd],
&dataframe[idx],
data->timestamp);
}
} else {
dev_err(SSP_DEV, "no client for frame\n");
}
idx += ssp_offset_map[sd]; break; case SSP_MSG2AP_INST_DEBUG_DATA: if (idx >= len) return -EPROTO;
sd = ssp_print_mcu_debug(dataframe, &idx, len); if (sd) {
dev_err(SSP_DEV, "Mcu data frame3 error %d\n", sd); return sd;
} break; case SSP_MSG2AP_INST_LIBRARY_DATA:
idx += len; break; case SSP_MSG2AP_INST_BIG_DATA:
ssp_handle_big_data(data, dataframe, &idx); break; case SSP_MSG2AP_INST_TIME_SYNC:
data->time_syncing = true; break; case SSP_MSG2AP_INST_RESET:
ssp_queue_ssp_refresh_task(data, 0); break;
}
}
if (data->time_syncing)
data->timestamp = ktime_get_real_ns();
if (length == 0) {
dev_err(SSP_DEV, "length received from mcu is 0\n"); return -EINVAL;
}
msg_type = SSP_GET_MESSAGE_TYPE(msg_options);
switch (msg_type) { case SSP_AP2HUB_READ: case SSP_AP2HUB_WRITE: /* * this is a small list, a few elements - the packets can be * received with no order
*/
mutex_lock(&data->pending_lock);
list_for_each_entry_safe(iter, n, &data->pending_list, list) { if (iter->options == msg_options) {
list_del(&iter->list);
msg = iter; break;
}
}
if (!msg) { /* * here can be implemented dead messages handling * but the slave should not send such ones - it is to * check but let's handle this
*/
buffer = kmalloc(length, GFP_KERNEL | GFP_DMA); if (!buffer) {
ret = -ENOMEM; goto _unlock;
}
/* got dead packet so it is always an error */
ret = spi_read(data->spi, buffer, length); if (ret >= 0)
ret = -EPROTO;
kfree(buffer);
dev_err(SSP_DEV, "No match error %x\n",
msg_options);
goto _unlock;
}
if (msg_type == SSP_AP2HUB_READ)
ret = spi_read(data->spi,
&msg->buffer[SSP_HEADER_SIZE_ALIGNED],
msg->length);
if (msg_type == SSP_AP2HUB_WRITE) {
ret = spi_write(data->spi,
&msg->buffer[SSP_HEADER_SIZE_ALIGNED],
msg->length); if (msg_options & SSP_AP2HUB_RETURN) {
msg->options =
SSP_AP2HUB_READ | SSP_AP2HUB_RETURN;
msg->length = 1;
if (msg->done) if (!completion_done(msg->done))
complete(msg->done);
_unlock:
mutex_unlock(&data->pending_lock); break; case SSP_HUB2AP_WRITE:
buffer = kzalloc(length, GFP_KERNEL | GFP_DMA); if (!buffer) return -ENOMEM;
ret = spi_read(data->spi, buffer, length); if (ret < 0) {
dev_err(SSP_DEV, "spi read fail\n");
kfree(buffer); break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.