diff --git a/drivers/misc/wigig_sensing.c b/drivers/misc/wigig_sensing.c index ec64b6531dc3..c6f7e0cee7c2 100644 --- a/drivers/misc/wigig_sensing.c +++ b/drivers/misc/wigig_sensing.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2019, The Linux foundation. All rights reserved. */ - #include #include #include @@ -15,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -433,7 +433,8 @@ static int wigig_sensing_change_state(struct wigig_sensing_ctx *ctx, enum wigig_sensing_stm_e new_state) { enum wigig_sensing_stm_e curr_state; - bool transition_allowed = false; + bool transition_allowed = true; + int rc = 0; if (!state) { pr_err("state is NULL\n"); @@ -441,86 +442,60 @@ static int wigig_sensing_change_state(struct wigig_sensing_ctx *ctx, } if (new_state <= WIGIG_SENSING_STATE_MIN || new_state >= WIGIG_SENSING_STATE_MAX) { - pr_err("new_state is invalid\n"); + pr_err("new_state (%d) is invalid\n", new_state); return -EINVAL; } curr_state = state->state; - if (new_state == curr_state) { - pr_debug("Already in the requested state, bailing out\n"); - return 0; - } - - if ((new_state == WIGIG_SENSING_STATE_SYS_ASSERT && - !state->fw_is_ready) || - (new_state == WIGIG_SENSING_STATE_SPI_READY)) { - transition_allowed = true; - } else { - switch (curr_state) { - case WIGIG_SENSING_STATE_INITIALIZED: - if (new_state == WIGIG_SENSING_STATE_SPI_READY && - state->fw_is_ready) - transition_allowed = true; - break; - case WIGIG_SENSING_STATE_SPI_READY: - if (new_state == WIGIG_SENSING_STATE_READY_STOPPED && - state->enabled) - transition_allowed = true; - break; - case WIGIG_SENSING_STATE_READY_STOPPED: - if (new_state == WIGIG_SENSING_STATE_SEARCH || - new_state == WIGIG_SENSING_STATE_FACIAL || - new_state == WIGIG_SENSING_STATE_GESTURE || - new_state == WIGIG_SENSING_STATE_CUSTOM || - new_state == WIGIG_SENSING_STATE_GET_PARAMS) - transition_allowed = true; - break; - case WIGIG_SENSING_STATE_SEARCH: - if (new_state == WIGIG_SENSING_STATE_READY_STOPPED || - new_state == WIGIG_SENSING_STATE_SEARCH || - new_state == WIGIG_SENSING_STATE_FACIAL || - new_state == WIGIG_SENSING_STATE_GESTURE) - transition_allowed = true; - break; - case WIGIG_SENSING_STATE_FACIAL: - if (new_state == WIGIG_SENSING_STATE_READY_STOPPED || - new_state == WIGIG_SENSING_STATE_SEARCH) - transition_allowed = true; - break; - case WIGIG_SENSING_STATE_GESTURE: - if (new_state == WIGIG_SENSING_STATE_READY_STOPPED || - new_state == WIGIG_SENSING_STATE_SEARCH) - transition_allowed = true; - break; - case WIGIG_SENSING_STATE_CUSTOM: - if (new_state == WIGIG_SENSING_STATE_READY_STOPPED) - transition_allowed = true; - break; - case WIGIG_SENSING_STATE_GET_PARAMS: - if (new_state == WIGIG_SENSING_STATE_READY_STOPPED) - transition_allowed = true; - break; - case WIGIG_SENSING_STATE_SYS_ASSERT: - if (new_state == WIGIG_SENSING_STATE_READY_STOPPED && - state->fw_is_ready) - transition_allowed = true; - break; - default: - pr_err("new_state is invalid\n"); - return -EINVAL; - } + + /* Moving to SYS_ASSEERT state is always allowed */ + if (new_state == WIGIG_SENSING_STATE_SYS_ASSERT) + goto skip; + + /* + * Moving from INITIALIZED state is allowed only to READY_STOPPED state + */ + else if (curr_state == WIGIG_SENSING_STATE_INITIALIZED && + new_state != WIGIG_SENSING_STATE_READY_STOPPED) { + transition_allowed = false; + rc = -EFAULT; + } + /* + * Moving to GET_PARAMS state is allowed only from READY_STOPPED state + */ + else if (curr_state != WIGIG_SENSING_STATE_READY_STOPPED && + new_state == WIGIG_SENSING_STATE_GET_PARAMS) { + transition_allowed = false; + rc = -EFAULT; + } + /* + * Moving from GET_PARAMS state is allowed only to READY_STOPPED state + */ + else if (curr_state == WIGIG_SENSING_STATE_GET_PARAMS && + new_state != WIGIG_SENSING_STATE_READY_STOPPED) { + transition_allowed = false; + rc = -EFAULT; + } + /* + * Moving from SYS_ASSERT state is allowed only to READY_STOPPED state + */ + else if (curr_state == WIGIG_SENSING_STATE_SYS_ASSERT && + new_state != WIGIG_SENSING_STATE_READY_STOPPED) { + transition_allowed = false; + rc = -ENODEV; } +skip: if (transition_allowed) { pr_info("state transition (%d) --> (%d)\n", curr_state, new_state); state->state = new_state; } else { - pr_info("state transition rejected (%d) xx> (%d)\n", - curr_state, new_state); + pr_err("state transition rejected (%d) xx> (%d)\n", + curr_state, new_state); } - return 0; + return rc; } static int wigig_sensing_ioc_set_auto_recovery(struct wigig_sensing_ctx *ctx) @@ -542,27 +517,31 @@ static int wigig_sensing_ioc_change_mode(struct wigig_sensing_ctx *ctx, struct wigig_sensing_change_mode req) { struct wigig_sensing_stm sim_state; - enum wigig_sensing_stm_e new_state; int rc; u32 ch; - pr_info("mode = %d, channel = %d\n", req.mode, req.channel); + pr_info("mode = %d, channel = %d, has_channel = %d\n", + req.mode, req.channel, req.has_channel); if (!ctx) return -EINVAL; + /* Save the request for later use */ + ctx->stm.mode_request = req.mode; + /* Simulate a state change */ - new_state = convert_mode_to_state(req.mode); + ctx->stm.state_request = convert_mode_to_state(req.mode); sim_state = ctx->stm; - rc = wigig_sensing_change_state(ctx, &sim_state, new_state); - if (rc || sim_state.state != new_state) { + rc = wigig_sensing_change_state(ctx, &sim_state, + ctx->stm.state_request); + if (rc) { pr_err("State change not allowed\n"); - rc = -EFAULT; goto End; } /* Send command to FW */ ctx->stm.change_mode_in_progress = true; ch = req.has_channel ? req.channel : 0; + ctx->stm.channel_request = ch; ctx->stm.burst_size_ready = false; /* Change mode command must not be called during DRI processing */ mutex_lock(&ctx->dri_lock); @@ -583,29 +562,28 @@ static int wigig_sensing_ioc_change_mode(struct wigig_sensing_ctx *ctx, /* Interrupted by a signal */ pr_err("wait_event_interruptible_timeout() interrupted by a signal (%d)\n", rc); - return rc; + goto End; } if (rc == 0) { /* Timeout, FW did not respond in time */ pr_err("wait_event_interruptible_timeout() timed out\n"); - return -ETIME; - } - - /* Change internal state */ - rc = wigig_sensing_change_state(ctx, &ctx->stm, new_state); - if (rc || ctx->stm.state != new_state) { - pr_err("wigig_sensing_change_state() failed\n"); - rc = -EFAULT; + rc = -ETIME; goto End; } - ctx->dropped_bursts = 0; - ctx->stm.channel_request = ch; - ctx->stm.mode = req.mode; - ctx->stm.change_mode_in_progress = false; + if (ctx->stm.state != ctx->stm.state_request) { + pr_err("%s() failed\n", __func__); + if (ctx->stm.state == WIGIG_SENSING_STATE_SYS_ASSERT) + rc = -ENODEV; + else + rc = -EFAULT; + } End: - return ctx->stm.burst_size; + ctx->stm.state_request = WIGIG_SENSING_STATE_MIN; + ctx->stm.channel_request = 0; + ctx->stm.mode_request = WIGIG_SENSING_MODE_STOP; + return (rc == 0) ? ctx->stm.burst_size : rc; } static int wigig_sensing_ioc_clear_data(struct wigig_sensing_ctx *ctx) @@ -629,9 +607,29 @@ static int wigig_sensing_ioc_get_num_dropped_bursts( return ctx->dropped_bursts; } -static int wigig_sensing_ioc_get_event(struct wigig_sensing_ctx *ctx) +static int wigig_sensing_ioc_get_num_avail_bursts( + struct wigig_sensing_ctx *ctx) { - return 0; + if (ctx->stm.burst_size) + return circ_cnt(&ctx->cir_data.b, ctx->cir_data.size_bytes) / + ctx->stm.burst_size; + else + return 0; +} + +static int wigig_sensing_ioc_get_event(struct wigig_sensing_ctx *ctx, + enum wigig_sensing_event *event) +{ + u32 copied; + + if (!ctx->event_pending) + return -EINVAL; + + if (kfifo_len(&ctx->events_fifo) == 1) + ctx->event_pending = false; + + return kfifo_to_user(&ctx->events_fifo, event, + sizeof(enum wigig_sensing_event), &copied); } static int wigig_sensing_open(struct inode *inode, struct file *filp) @@ -662,7 +660,8 @@ static unsigned int wigig_sensing_poll(struct file *filp, poll_table *wait) poll_wait(filp, &ctx->data_wait_q, wait); - if (circ_cnt(&ctx->cir_data.b, ctx->cir_data.size_bytes)) + if (!ctx->stm.change_mode_in_progress && + circ_cnt(&ctx->cir_data.b, ctx->cir_data.size_bytes)) mask |= (POLLIN | POLLRDNORM); if (ctx->event_pending) @@ -681,11 +680,19 @@ static ssize_t wigig_sensing_read(struct file *filp, char __user *buf, struct cir_data *d = &ctx->cir_data; /* Driver not ready to send data */ - if ((!ctx) || - (!ctx->spi_dev) || - (!d->b.buf)) + if (!ctx || !ctx->spi_dev || !d->b.buf || + ctx->stm.state == WIGIG_SENSING_STATE_SYS_ASSERT) return -ENODEV; + if (ctx->stm.change_mode_in_progress) + return -EINVAL; + + /* Read buffer too small */ + if (count < ctx->stm.burst_size) { + pr_err("Read buffer must be larger than burst size\n"); + return -EINVAL; + } + /* No data in the buffer */ while (circ_cnt(&d->b, d->size_bytes) == 0) { if (filp->f_flags & O_NONBLOCK) @@ -695,11 +702,11 @@ static ssize_t wigig_sensing_read(struct file *filp, char __user *buf, circ_cnt(&d->b, d->size_bytes) != 0)) return -ERESTARTSYS; } - if (mutex_lock_interruptible(&d->lock)) return -ERESTARTSYS; copy_size = min_t(u32, circ_cnt(&d->b, d->size_bytes), count); + copy_size -= copy_size % ctx->stm.burst_size; size_to_end = circ_cnt_to_end(&d->b, d->size_bytes); tail = d->b.tail; pr_debug("copy_size=%u, size_to_end=%u, head=%u, tail=%u\n", @@ -759,7 +766,7 @@ static int wigig_sensing_release(struct inode *inode, struct file *filp) } static long wigig_sensing_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) + __user unsigned long arg) { int rc; struct wigig_sensing_ctx *ctx = file->private_data; @@ -812,7 +819,12 @@ static long wigig_sensing_ioctl(struct file *file, unsigned int cmd, break; case WIGIG_SENSING_IOCTL_GET_EVENT: pr_info("Received WIGIG_SENSING_IOCTL_GET_EVENT command\n"); - rc = wigig_sensing_ioc_get_event(ctx); + rc = wigig_sensing_ioc_get_event(ctx, + (enum wigig_sensing_event *)arg); + break; + case WIGIG_SENSING_IOCTL_GET_NUM_AVAIL_BURSTS: + pr_info("Received WIGIG_SENSING_IOCTL_GET_NUM_AVAIL_BURSTS command\n"); + rc = wigig_sensing_ioc_get_num_avail_bursts(ctx); break; default: rc = -EINVAL; @@ -908,17 +920,11 @@ static int wigig_sensing_handle_fifo_ready_dri(struct wigig_sensing_ctx *ctx) goto End; } - if (!ctx->stm.enabled && burst_size != 0) { - pr_info("Invalid burst size while disabled %d\n", burst_size); - rc = -EFAULT; - goto End; - } - ctx->stm.burst_size = burst_size; - if (!ctx->stm.enabled || - ctx->stm.state >= WIGIG_SENSING_STATE_SYS_ASSERT || - ctx->stm.state < WIGIG_SENSING_STATE_SPI_READY) { - pr_err("Received burst_size in an unexpected state\n"); + if (ctx->stm.state >= WIGIG_SENSING_STATE_SYS_ASSERT || + ctx->stm.state < WIGIG_SENSING_STATE_READY_STOPPED) { + pr_err("Received burst_size in an unexpected state (%d)\n", + ctx->stm.state); rc = -EFAULT; goto End; } @@ -951,9 +957,24 @@ static int wigig_sensing_handle_fifo_ready_dri(struct wigig_sensing_ctx *ctx) ctx->temp_buffer = 0; } - wake_up_interruptible(&ctx->cmd_wait_q); + /* Change internal state */ + rc = wigig_sensing_change_state(ctx, &ctx->stm, ctx->stm.state_request); + if (rc) { + pr_err("wigig_sensing_change_state() failed\n"); + goto End; + } + + /* Initialize head and tail pointers to 0 */ + wigig_sensing_ioc_clear_data(ctx); + + ctx->dropped_bursts = 0; + ctx->stm.channel = ctx->stm.channel_request; + ctx->stm.mode = ctx->stm.mode_request; + End: + ctx->stm.change_mode_in_progress = false; mutex_unlock(&ctx->spi_lock); + wake_up_interruptible(&ctx->cmd_wait_q); return rc; } @@ -969,9 +990,9 @@ static int wigig_sensing_chip_data_ready(struct wigig_sensing_ctx *ctx, u32 idx = 0; u32 spi_transaction_size; u32 available_space_to_end; + u32 orig_head; if (stm_state == WIGIG_SENSING_STATE_INITIALIZED || - stm_state == WIGIG_SENSING_STATE_SPI_READY || stm_state == WIGIG_SENSING_STATE_READY_STOPPED || stm_state == WIGIG_SENSING_STATE_SYS_ASSERT) { pr_err("Received data ready interrupt in an unexpected stm_state, disregarding\n"); @@ -1017,8 +1038,14 @@ static int wigig_sensing_chip_data_ready(struct wigig_sensing_ctx *ctx, spi_transaction_size = calc_spi_transaction_size(fill_level, SPI_MAX_TRANSACTION_SIZE); local = d->b; + orig_head = local.head; mutex_lock(&ctx->spi_lock); while (fill_level > 0) { + if (ctx->stm.change_mode_in_progress) { + local.head = orig_head; + break; + } + bytes_to_read = (fill_level < spi_transaction_size) ? fill_level : spi_transaction_size; available_space_to_end = @@ -1131,6 +1158,22 @@ cmd_reply_buf_alloc_failed: return rc; } +static int wigig_sensing_send_event(struct wigig_sensing_ctx *ctx, + enum wigig_sensing_event event) +{ + if (kfifo_is_full(&ctx->events_fifo)) { + pr_err("events fifo is full, unable to send event\n"); + return -EFAULT; + } + + kfifo_in(&ctx->events_fifo, &event, 1); + ctx->event_pending = true; + + wake_up_interruptible(&ctx->cmd_wait_q); + + return 0; +} + static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie) { struct wigig_sensing_ctx *ctx = cookie; @@ -1170,7 +1213,7 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie) ctx->stm.spi_malfunction = false; if (ctx->stm.state == WIGIG_SENSING_STATE_INITIALIZED) wigig_sensing_change_state(ctx, &ctx->stm, - WIGIG_SENSING_STATE_SPI_READY); + WIGIG_SENSING_STATE_READY_STOPPED); } pr_debug("Reading SANITY register\n"); @@ -1207,17 +1250,37 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie) goto bail_out; } + if (spi_status.b.int_sysassert) { + enum wigig_sensing_stm_e old_state = ctx->stm.state; + + pr_info_ratelimited("SYSASSERT INTERRUPT\n"); + ctx->stm.fw_is_ready = false; + + wigig_sensing_change_state(ctx, &ctx->stm, + WIGIG_SENSING_STATE_SYS_ASSERT); + + /* Send asynchronous RESET event to application */ + if (old_state != WIGIG_SENSING_STATE_READY_STOPPED) + wigig_sensing_send_event(ctx, + WIGIG_SENSING_EVENT_RESET); + + ctx->stm.spi_malfunction = true; + memset(&ctx->inb_cmd, 0, sizeof(ctx->inb_cmd)); + spi_status.v &= ~INT_SYSASSERT; + goto deassert_and_bail_out; + } if (spi_status.b.int_fw_ready) { - pr_debug("FW READY INTERRUPT\n"); + pr_info_ratelimited("FW READY INTERRUPT\n"); ctx->stm.fw_is_ready = true; ctx->stm.channel_request = 0; ctx->stm.burst_size = 0; ctx->stm.mode = WIGIG_SENSING_MODE_STOP; - ctx->stm.enabled = true; - wigig_sensing_change_state(ctx, &ctx->stm, WIGIG_SENSING_STATE_READY_STOPPED); + /* Send asynchronous FW_READY event to application */ + wigig_sensing_send_event(ctx, WIGIG_SENSING_EVENT_FW_READY); + spi_status.v &= ~INT_FW_READY; } if (spi_status.b.int_data_ready) { @@ -1230,24 +1293,11 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie) pr_debug("Change mode in progress, aborting data processing\n"); spi_status.v &= ~INT_DATA_READY; } - if (spi_status.b.int_sysassert) { - pr_debug("SYSASSERT INTERRUPT\n"); - ctx->stm.fw_is_ready = false; - - rc = wigig_sensing_change_state(ctx, &ctx->stm, - WIGIG_SENSING_STATE_SYS_ASSERT); - if (rc != 0 || - ctx->stm.state != WIGIG_SENSING_STATE_SYS_ASSERT) - pr_err("State change to WIGIG_SENSING_SYS_ASSERT failed\n"); - - ctx->stm.spi_malfunction = true; - spi_status.v &= ~INT_SYSASSERT; - } if (spi_status.b.int_deep_sleep_exit || (ctx->stm.waiting_for_deep_sleep_exit && ctx->stm.waiting_for_deep_sleep_exit_first_pass)) { if (spi_status.b.int_deep_sleep_exit) - pr_debug("DEEP SLEEP EXIT INTERRUPT\n"); + pr_info_ratelimited("DEEP SLEEP EXIT INTERRUPT\n"); if (ctx->stm.waiting_for_deep_sleep_exit) { additional_inb_command = ctx->inb_cmd; @@ -1260,7 +1310,7 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie) spi_status.v &= ~INT_DEEP_SLEEP_EXIT; } if (spi_status.b.int_fifo_ready) { - pr_debug("FIFO READY INTERRUPT\n"); + pr_info_ratelimited("FIFO READY INTERRUPT\n"); wigig_sensing_handle_fifo_ready_dri(ctx); spi_status.v &= ~INT_FIFO_READY; @@ -1274,6 +1324,7 @@ static irqreturn_t wigig_sensing_dri_isr_thread(int irq, void *cookie) pr_err("Unexpected interrupt received, spi_status=0x%X\n", spi_status.v & CLEAR_LOW_23_BITS); +deassert_and_bail_out: /* Notify FW we are done with interrupt handling */ rc = wigig_sensing_deassert_dri(ctx, additional_inb_command); if (rc) @@ -1312,6 +1363,7 @@ static int wigig_sensing_probe(struct spi_device *spi) init_waitqueue_head(&ctx->cmd_wait_q); init_waitqueue_head(&ctx->data_wait_q); ctx->stm.state = WIGIG_SENSING_STATE_INITIALIZED; + INIT_KFIFO(ctx->events_fifo); /* Allocate memory for the CIRs */ /* Allocate a 2MB == 2^21 buffer for CIR data */ diff --git a/drivers/misc/wigig_sensing.h b/drivers/misc/wigig_sensing.h index c4b291062454..1389c3f77aac 100644 --- a/drivers/misc/wigig_sensing.h +++ b/drivers/misc/wigig_sensing.h @@ -7,6 +7,7 @@ #define __WIGIG_SENSING_H__ #include #include +#include #include #include @@ -130,7 +131,6 @@ struct spi_fifo { enum wigig_sensing_stm_e { WIGIG_SENSING_STATE_MIN = 0, WIGIG_SENSING_STATE_INITIALIZED, - WIGIG_SENSING_STATE_SPI_READY, WIGIG_SENSING_STATE_READY_STOPPED, WIGIG_SENSING_STATE_SEARCH, WIGIG_SENSING_STATE_FACIAL, @@ -143,10 +143,8 @@ enum wigig_sensing_stm_e { struct wigig_sensing_stm { bool auto_recovery; - bool enabled; bool fw_is_ready; bool spi_malfunction; - bool sys_assert; bool waiting_for_deep_sleep_exit; bool waiting_for_deep_sleep_exit_first_pass; bool burst_size_ready; @@ -154,7 +152,10 @@ struct wigig_sensing_stm { enum wigig_sensing_stm_e state; enum wigig_sensing_mode mode; u32 burst_size; + u32 channel; u32 channel_request; + enum wigig_sensing_stm_e state_request; + enum wigig_sensing_mode mode_request; }; struct wigig_sensing_ctx { @@ -193,6 +194,7 @@ struct wigig_sensing_ctx { struct cir_data cir_data; u8 *temp_buffer; bool event_pending; + DECLARE_KFIFO(events_fifo, enum wigig_sensing_event, 8); u32 dropped_bursts; }; diff --git a/include/uapi/misc/wigig_sensing_uapi.h b/include/uapi/misc/wigig_sensing_uapi.h index 6ab94f3ef260..d1c69645c024 100644 --- a/include/uapi/misc/wigig_sensing_uapi.h +++ b/include/uapi/misc/wigig_sensing_uapi.h @@ -27,8 +27,10 @@ struct wigig_sensing_change_mode { }; enum wigig_sensing_event { + WIGIG_SENSING_EVENT_MIN, WIGIG_SENSING_EVENT_FW_READY, WIGIG_SENSING_EVENT_RESET, + WIGIG_SENSING_EVENT_MAX, }; #define WIGIG_SENSING_IOC_MAGIC 'r' @@ -39,6 +41,7 @@ enum wigig_sensing_event { #define WIGIG_SENSING_IOCTL_CLEAR_DATA (3) #define WIGIG_SENSING_IOCTL_GET_NUM_DROPPED_BURSTS (4) #define WIGIG_SENSING_IOCTL_GET_EVENT (5) +#define WIGIG_SENSING_IOCTL_GET_NUM_AVAIL_BURSTS (6) /** * Set auto recovery, which means that the system will go back to search mode @@ -83,10 +86,17 @@ enum wigig_sensing_event { WIGIG_SENSING_IOCTL_GET_NUM_DROPPED_BURSTS, uint32_t) /** - * Get number of bursts that where dropped due to data buffer overflow + * Get asynchronous event (FW_READY, RESET) */ #define WIGIG_SENSING_IOC_GET_EVENT \ _IOR(WIGIG_SENSING_IOC_MAGIC, WIGIG_SENSING_IOCTL_GET_EVENT, \ sizeof(enum wigig_sensing_event)) -#endif /* ____WIGIG_SENSING_UAPI_H__ */ +/** + * Get number of available bursts in the data buffer + */ +#define WIGIG_SENSING_IOC_GET_NUM_AVAIL_BURSTS \ + _IOR(WIGIG_SENSING_IOC_MAGIC, WIGIG_SENSING_IOCTL_GET_NUM_AVAIL_BURSTS,\ + sizeof(uint32_t)) + +#endif /* __WIGIG_SENSING_UAPI_H__ */