Loading drivers/misc/wigig_sensing.c +180 −128 Original line number Diff line number Diff line Loading @@ -2,7 +2,6 @@ /* * Copyright (c) 2019, The Linux foundation. All rights reserved. */ #include <linux/cdev.h> #include <linux/circ_buf.h> #include <linux/clk.h> Loading @@ -15,6 +14,7 @@ #include <linux/io.h> #include <linux/ioctl.h> #include <linux/kernel.h> #include <linux/kfifo.h> #include <linux/list.h> #include <linux/module.h> #include <linux/of.h> Loading Loading @@ -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"); Loading @@ -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", 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) Loading @@ -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); Loading @@ -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; rc = -ETIME; goto End; } /* 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"); 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; goto End; } ctx->dropped_bursts = 0; ctx->stm.channel_request = ch; ctx->stm.mode = req.mode; ctx->stm.change_mode_in_progress = false; 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) Loading @@ -629,11 +607,31 @@ 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) { 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) { /* forbid opening more then one instance at a time */ Loading Loading @@ -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) Loading @@ -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) Loading @@ -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", Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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"); Loading Loading @@ -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 = Loading Loading @@ -1131,6 +1158,22 @@ static int wigig_sensing_spi_init(struct wigig_sensing_ctx *ctx) 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; Loading Loading @@ -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"); Loading Loading @@ -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) { Loading @@ -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; Loading @@ -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; Loading @@ -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) Loading Loading @@ -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 */ Loading drivers/misc/wigig_sensing.h +5 −3 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ #define __WIGIG_SENSING_H__ #include <linux/cdev.h> #include <linux/circ_buf.h> #include <linux/kfifo.h> #include <linux/slab.h> #include <uapi/misc/wigig_sensing_uapi.h> Loading Loading @@ -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, Loading @@ -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; Loading @@ -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 { Loading Loading @@ -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; }; Loading include/uapi/misc/wigig_sensing_uapi.h +12 −2 Original line number Diff line number Diff line Loading @@ -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' Loading @@ -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 Loading Loading @@ -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__ */ Loading
drivers/misc/wigig_sensing.c +180 −128 Original line number Diff line number Diff line Loading @@ -2,7 +2,6 @@ /* * Copyright (c) 2019, The Linux foundation. All rights reserved. */ #include <linux/cdev.h> #include <linux/circ_buf.h> #include <linux/clk.h> Loading @@ -15,6 +14,7 @@ #include <linux/io.h> #include <linux/ioctl.h> #include <linux/kernel.h> #include <linux/kfifo.h> #include <linux/list.h> #include <linux/module.h> #include <linux/of.h> Loading Loading @@ -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"); Loading @@ -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", 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) Loading @@ -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); Loading @@ -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; rc = -ETIME; goto End; } /* 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"); 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; goto End; } ctx->dropped_bursts = 0; ctx->stm.channel_request = ch; ctx->stm.mode = req.mode; ctx->stm.change_mode_in_progress = false; 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) Loading @@ -629,11 +607,31 @@ 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) { 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) { /* forbid opening more then one instance at a time */ Loading Loading @@ -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) Loading @@ -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) Loading @@ -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", Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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"); Loading Loading @@ -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 = Loading Loading @@ -1131,6 +1158,22 @@ static int wigig_sensing_spi_init(struct wigig_sensing_ctx *ctx) 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; Loading Loading @@ -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"); Loading Loading @@ -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) { Loading @@ -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; Loading @@ -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; Loading @@ -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) Loading Loading @@ -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 */ Loading
drivers/misc/wigig_sensing.h +5 −3 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ #define __WIGIG_SENSING_H__ #include <linux/cdev.h> #include <linux/circ_buf.h> #include <linux/kfifo.h> #include <linux/slab.h> #include <uapi/misc/wigig_sensing_uapi.h> Loading Loading @@ -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, Loading @@ -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; Loading @@ -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 { Loading Loading @@ -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; }; Loading
include/uapi/misc/wigig_sensing_uapi.h +12 −2 Original line number Diff line number Diff line Loading @@ -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' Loading @@ -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 Loading Loading @@ -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__ */