123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714 |
- /*
- * Copyright (C) 2013, Samsung Electronics Co. Ltd. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
- #include "ssp_sensorhub.h"
- static void ssp_sensorhub_log(const char *func_name,
- const char *data, int length)
- {
- char buf[6];
- char *log_str;
- int log_size;
- int i;
- if (likely(length <= BIG_DATA_SIZE))
- log_size = length;
- else
- log_size = PRINT_TRUNCATE * 2 + 1;
- log_size = sizeof(buf) * log_size + 1;
- log_str = kzalloc(log_size, GFP_ATOMIC);
- if (unlikely(!log_str)) {
- sensorhub_err("allocate memory for data log err");
- return;
- }
- for (i = 0; i < length; i++) {
- if (length < BIG_DATA_SIZE ||
- i < PRINT_TRUNCATE || i >= length - PRINT_TRUNCATE) {
- snprintf(buf, sizeof(buf), "%d", (signed char)data[i]);
- strlcat(log_str, buf, log_size);
- }
- if (length < BIG_DATA_SIZE ||
- i < PRINT_TRUNCATE || i >= length - PRINT_TRUNCATE) {
- if (i < length - 1)
- strlcat(log_str, ", ", log_size);
- }
- if (length > BIG_DATA_SIZE && i == PRINT_TRUNCATE)
- strlcat(log_str, "..., ", log_size);
- }
- pr_info("[SSP]: %s - %s (%d)", func_name, log_str, length);
- kfree(log_str);
- }
- static int ssp_sensorhub_send_big_data(struct ssp_sensorhub_data *hub_data,
- const char *buf, int count)
- {
- int length = count - 3;
- int ret = 0;
- /* only support voice service for the moment */
- if (buf[1] != TYPE_WAKE_UP_VOICE_SERVICE) {
- sensorhub_err("not voice service(%d)", buf[1]);
- return -EINVAL;
- }
- /* am or grammer data? */
- if (buf[2] != TYPE_WAKE_UP_VOICE_SOUND_SOURCE_AM &&
- buf[2] != TYPE_WAKE_UP_VOICE_SOUND_SOURCE_GRAMMER) {
- sensorhub_err("voice data type err(%d)", buf[2]);
- return -EINVAL;
- }
- hub_data->big_send_events.library_data
- = kzalloc(length * sizeof(char), GFP_KERNEL);
- if (unlikely(!hub_data->big_send_events.library_data)) {
- sensorhub_err("allocate memory for big library err");
- return -ENOMEM;
- }
- memcpy(hub_data->big_send_events.library_data, buf+3, length);
- hub_data->big_send_events.library_length = length;
- /* trigger big data transfer */
- ret = set_big_data_start(hub_data->ssp_data, buf[2]+1, length);
- if (ret != SUCCESS) {
- sensorhub_err("set_big_data_start err(%d)", ret);
- goto exit;
- }
- /* wait until write operation is done */
- ret = wait_for_completion_timeout(&hub_data->big_write_done,
- COMPLETION_TIMEOUT + 1*HZ);
- if (unlikely(!ret)) {
- sensorhub_err("wait timed out");
- ret = -EBUSY;
- } else if (unlikely(ret < 0)) {
- sensorhub_err("wait for completion err(%d)", ret);
- ret = -EIO;
- }
- exit:
- kfree(hub_data->big_send_events.library_data);
- return !ret ? ret + 1 : ret;
- }
- static int ssp_sensorhub_send_cmd(struct ssp_sensorhub_data *hub_data,
- const char *buf, int count)
- {
- int ret = 0;
- if (buf[2] < MSG2SSP_AP_STATUS_WAKEUP ||
- buf[2] >= MSG2SSP_AP_TEMPHUMIDITY_CAL_DONE) {
- sensorhub_err("MSG2SSP_INST_LIB_NOTI err(%d)", buf[2]);
- return -EINVAL;
- }
- ret = ssp_send_cmd(hub_data->ssp_data, buf[2], 0);
- if (buf[2] == MSG2SSP_AP_STATUS_WAKEUP ||
- buf[2] == MSG2SSP_AP_STATUS_SLEEP)
- hub_data->ssp_data->uLastAPState = buf[2];
- if (buf[2] == MSG2SSP_AP_STATUS_SUSPEND ||
- buf[2] == MSG2SSP_AP_STATUS_RESUME)
- hub_data->ssp_data->uLastResumeState = buf[2];
- return ret;
- }
- static int ssp_sensorhub_send_instruction(struct ssp_sensorhub_data *hub_data,
- const char *buf, int count)
- {
- unsigned char instruction = buf[0];
- if (buf[0] == MSG2SSP_INST_LIBRARY_REMOVE)
- instruction = REMOVE_LIBRARY;
- else if (buf[0] == MSG2SSP_INST_LIBRARY_ADD)
- instruction = ADD_LIBRARY;
- return send_instruction(hub_data->ssp_data, instruction,
- (unsigned char)buf[1], (unsigned char *)(buf+2), count-2);
- }
- static ssize_t ssp_sensorhub_write(struct file *file, const char __user *buf,
- size_t count, loff_t *pos)
- {
- struct ssp_sensorhub_data *hub_data
- = container_of(file->private_data,
- struct ssp_sensorhub_data, sensorhub_device);
- int ret = 0;
- if (unlikely(count < 2)) {
- sensorhub_err("library data length err(%d)", count);
- return -EINVAL;
- }
- ssp_sensorhub_log(__func__, buf, count);
- if (unlikely(hub_data->ssp_data->bSspShutdown)) {
- sensorhub_err("stop sending library data(shutdown)");
- return -EBUSY;
- }
- if (buf[0] == MSG2SSP_INST_LIB_DATA && count >= BIG_DATA_SIZE)
- ret = ssp_sensorhub_send_big_data(hub_data, buf, count);
- else if (buf[0] == MSG2SSP_INST_LIB_NOTI)
- ret = ssp_sensorhub_send_cmd(hub_data, buf, count);
- else
- ret = ssp_sensorhub_send_instruction(hub_data, buf, count);
- if (unlikely(ret <= 0)) {
- sensorhub_err("send library data err(%d)", ret);
- /* i2c transfer fail */
- if (ret == ERROR)
- return -EIO;
- /* i2c transfer done but no ack from MCU */
- else if (ret == FAIL)
- return -EAGAIN;
- else
- return ret;
- }
- return count;
- }
- static ssize_t ssp_sensorhub_read(struct file *file, char __user *buf,
- size_t count, loff_t *pos)
- {
- struct ssp_sensorhub_data *hub_data
- = container_of(file->private_data,
- struct ssp_sensorhub_data, sensorhub_device);
- struct sensorhub_event *event;
- int retries = MAX_DATA_COPY_TRY;
- int length = 0;
- int ret = 0;
- spin_lock_bh(&hub_data->sensorhub_lock);
- if (unlikely(kfifo_is_empty(&hub_data->fifo))) {
- sensorhub_info("no library data");
- goto err;
- }
- /* first in first out */
- ret = kfifo_out_peek(&hub_data->fifo, &event, sizeof(void *));
- if (unlikely(!ret)) {
- sensorhub_err("kfifo out peek err(%d)", ret);
- ret = EIO;
- goto err;
- }
- length = event->library_length;
- while (retries--) {
- ret = copy_to_user(buf,
- event->library_data, event->library_length);
- if (likely(!ret))
- break;
- }
- if (unlikely(ret)) {
- sensorhub_err("read library data err(%d)", ret);
- goto err;
- }
- ssp_sensorhub_log(__func__,
- event->library_data, event->library_length);
- /* remove first event from the list */
- ret = kfifo_out(&hub_data->fifo, &event, sizeof(void *));
- if (unlikely(ret != sizeof(void *))) {
- sensorhub_err("kfifo out err(%d)", ret);
- ret = EIO;
- goto err;
- }
- complete(&hub_data->read_done);
- spin_unlock_bh(&hub_data->sensorhub_lock);
- return length;
- err:
- spin_unlock_bh(&hub_data->sensorhub_lock);
- return ret ? -ret : 0;
- }
- static long ssp_sensorhub_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
- {
- struct ssp_sensorhub_data *hub_data
- = container_of(file->private_data,
- struct ssp_sensorhub_data, sensorhub_device);
- void __user *argp = (void __user *)arg;
- int retries = MAX_DATA_COPY_TRY;
- int length = hub_data->big_events.library_length;
- int ret = 0;
- switch (cmd) {
- case IOCTL_READ_BIG_CONTEXT_DATA:
- if (unlikely(!hub_data->big_events.library_data
- || !hub_data->big_events.library_length)) {
- sensorhub_info("no big library data");
- return 0;
- }
- while (retries--) {
- ret = copy_to_user(argp,
- hub_data->big_events.library_data,
- hub_data->big_events.library_length);
- if (likely(!ret))
- break;
- }
- if (unlikely(ret)) {
- sensorhub_err("read big library data err(%d)", ret);
- return -ret;
- }
- ssp_sensorhub_log(__func__,
- hub_data->big_events.library_data,
- hub_data->big_events.library_length);
- hub_data->is_big_event = false;
- kfree(hub_data->big_events.library_data);
- hub_data->big_events.library_length = 0;
- complete(&hub_data->big_read_done);
- break;
- default:
- sensorhub_err("ioctl cmd err(%d)", cmd);
- return -EINVAL;
- }
- return length;
- }
- static struct file_operations ssp_sensorhub_fops = {
- .owner = THIS_MODULE,
- .open = nonseekable_open,
- .write = ssp_sensorhub_write,
- .read = ssp_sensorhub_read,
- .unlocked_ioctl = ssp_sensorhub_ioctl,
- };
- void ssp_sensorhub_report_notice(struct ssp_data *ssp_data, char notice)
- {
- struct ssp_sensorhub_data *hub_data = ssp_data->hub_data;
- input_report_rel(hub_data->sensorhub_input_dev, NOTICE, notice);
- input_sync(hub_data->sensorhub_input_dev);
- if (notice == MSG2SSP_AP_STATUS_WAKEUP)
- sensorhub_info("wake up");
- else if (notice == MSG2SSP_AP_STATUS_SLEEP)
- sensorhub_info("sleep");
- else if (notice == MSG2SSP_AP_STATUS_RESET)
- sensorhub_info("reset");
- else
- sensorhub_err("invalid notice(0x%x)", notice);
- }
- static void ssp_sensorhub_report_library(struct ssp_sensorhub_data *hub_data)
- {
- input_report_rel(hub_data->sensorhub_input_dev, DATA, DATA);
- input_sync(hub_data->sensorhub_input_dev);
- wake_lock_timeout(&hub_data->sensorhub_wake_lock, WAKE_LOCK_TIMEOUT);
- }
- static void ssp_sensorhub_report_big_library(
- struct ssp_sensorhub_data *hub_data)
- {
- input_report_rel(hub_data->sensorhub_input_dev, BIG_DATA, BIG_DATA);
- input_sync(hub_data->sensorhub_input_dev);
- wake_lock_timeout(&hub_data->sensorhub_wake_lock, WAKE_LOCK_TIMEOUT);
- }
- static int ssp_sensorhub_list(struct ssp_sensorhub_data *hub_data,
- char *dataframe, int length)
- {
- struct sensorhub_event *event;
- int ret = 0;
- if (unlikely(length <= 0 || length >= PAGE_SIZE)) {
- sensorhub_err("library length err(%d)", length);
- return -EINVAL;
- }
- ssp_sensorhub_log(__func__, dataframe, length);
- /* overwrite new event if list is full */
- if (unlikely(kfifo_is_full(&hub_data->fifo))) {
- ret = kfifo_out(&hub_data->fifo, &event, sizeof(void *));
- if (unlikely(ret != sizeof(void *))) {
- sensorhub_err("kfifo out err(%d)", ret);
- return -EIO;
- }
- sensorhub_info("overwrite event");
- }
- /* allocate memory for new event */
- kfree(hub_data->events[hub_data->event_number].library_data);
- hub_data->events[hub_data->event_number].library_data
- = kzalloc(length * sizeof(char), GFP_ATOMIC);
- if (unlikely(!hub_data->events[hub_data->event_number].library_data)) {
- sensorhub_err("allocate memory for library err");
- return -ENOMEM;
- }
- /* copy new event into memory */
- memcpy(hub_data->events[hub_data->event_number].library_data,
- dataframe, length);
- hub_data->events[hub_data->event_number].library_length = length;
- /* add new event into the end of list */
- event = &hub_data->events[hub_data->event_number];
- ret = kfifo_in(&hub_data->fifo, &event, sizeof(void *));
- if (unlikely(ret != sizeof(void *))) {
- sensorhub_err("kfifo in err(%d)", ret);
- return -EIO;
- }
- /* not to overflow max list capacity */
- if (hub_data->event_number++ >= LIST_SIZE - 1)
- hub_data->event_number = 0;
- return kfifo_len(&hub_data->fifo) / sizeof(void *);
- }
- int ssp_sensorhub_handle_data(struct ssp_data *ssp_data, char *dataframe,
- int start, int end)
- {
- struct ssp_sensorhub_data *hub_data = ssp_data->hub_data;
- int ret = 0;
- /* add new sensorhub event into list */
- spin_lock_bh(&hub_data->sensorhub_lock);
- ret = ssp_sensorhub_list(hub_data, dataframe+start, end-start);
- spin_unlock_bh(&hub_data->sensorhub_lock);
- if (ret < 0)
- sensorhub_err("sensorhub list err(%d)", ret);
- else
- wake_up(&hub_data->sensorhub_wq);
- return ret;
- }
- static int ssp_sensorhub_thread(void *arg)
- {
- struct ssp_sensorhub_data *hub_data = (struct ssp_sensorhub_data *)arg;
- int ret = 0;
- while (likely(!kthread_should_stop())) {
- /* run thread if list is not empty */
- wait_event_interruptible(hub_data->sensorhub_wq,
- kthread_should_stop() ||
- !kfifo_is_empty(&hub_data->fifo) ||
- hub_data->is_big_event);
- /* exit thread if kthread should stop */
- if (unlikely(kthread_should_stop())) {
- sensorhub_info("kthread_stop()");
- break;
- }
- if (likely(!kfifo_is_empty(&hub_data->fifo))) {
- /* report sensorhub event to user */
- ssp_sensorhub_report_library(hub_data);
- /* wait until transfer finished */
- ret = wait_for_completion_timeout(
- &hub_data->read_done, COMPLETION_TIMEOUT);
- if (unlikely(!ret))
- sensorhub_err("wait for read timed out");
- else if (unlikely(ret < 0))
- sensorhub_err("read completion err(%d)", ret);
- }
- if (unlikely(hub_data->is_big_event)) {
- /* report big sensorhub event to user */
- ssp_sensorhub_report_big_library(hub_data);
- /* wait until transfer finished */
- ret = wait_for_completion_timeout(
- &hub_data->big_read_done, COMPLETION_TIMEOUT);
- if (unlikely(!ret))
- sensorhub_err("wait for big read timed out");
- else if (unlikely(ret < 0))
- sensorhub_err("big read completion err(%d)",
- ret);
- }
- }
- return 0;
- }
- void ssp_read_big_library_task(struct work_struct *work)
- {
- struct ssp_big *big = container_of(work, struct ssp_big, work);
- struct ssp_sensorhub_data *hub_data = big->data->hub_data;
- struct ssp_msg *msg;
- int buf_len, residue = big->length, ret = 0, index = 0, pos = 0;
- hub_data->big_events.library_length = big->length;
- hub_data->big_events.library_data = kzalloc(big->length, GFP_KERNEL);
- while (residue > 0) {
- buf_len = residue > DATA_PACKET_SIZE
- ? DATA_PACKET_SIZE : residue;
- msg = kzalloc(sizeof(*msg), GFP_KERNEL);
- msg->cmd = MSG2SSP_AP_GET_BIG_DATA;
- msg->length = buf_len;
- msg->options = AP2HUB_READ | (index++ << SSP_INDEX);
- msg->data = big->addr;
- msg->buffer = hub_data->big_events.library_data + pos;
- msg->free_buffer = 0;
- ret = ssp_spi_sync(big->data, msg, 1000);
- if (ret != SUCCESS) {
- sensorhub_err("read big data err(%d)", ret);
- break;
- }
- pos += buf_len;
- residue -= buf_len;
- sensorhub_info("read big data (%5d / %5d)", pos, big->length);
- }
- hub_data->is_big_event = true;
- wake_up(&hub_data->sensorhub_wq);
- kfree(big);
- }
- void ssp_send_big_library_task(struct work_struct *work)
- {
- struct ssp_big *big = container_of(work, struct ssp_big, work);
- struct ssp_sensorhub_data *hub_data = big->data->hub_data;
- struct ssp_msg *msg;
- int buf_len, residue = big->length, ret = 0, index = 0, pos = 0;
- while (residue > 0) {
- buf_len = residue > DATA_PACKET_SIZE
- ? DATA_PACKET_SIZE : residue;
- msg = kzalloc(sizeof(*msg), GFP_KERNEL);
- msg->cmd = MSG2SSP_AP_SET_BIG_DATA;
- msg->length = buf_len;
- msg->options = AP2HUB_WRITE | (index++ << SSP_INDEX);
- msg->data = big->addr;
- msg->buffer = hub_data->big_send_events.library_data + pos;
- msg->free_buffer = 0;
- ret = ssp_spi_sync(big->data, msg, 1000);
- if (ret != SUCCESS) {
- sensorhub_err("send big data err(%d)", ret);
- break;
- }
- pos += buf_len;
- residue -= buf_len;
- sensorhub_info("send big data (%5d / %5d)", pos, big->length);
- }
- complete(&hub_data->big_write_done);
- kfree(big);
- }
- void ssp_pcm_dump_task(struct work_struct *work)
- {
- struct ssp_big *big = container_of(work, struct ssp_big, work);
- struct ssp_sensorhub_data *hub_data = big->data->hub_data;
- struct ssp_msg *msg;
- int buf_len, residue = big->length, ret = 0, index = 0;
- mm_segment_t old_fs;
- struct file *voice_filp;
- char pcm_path[BIN_PATH_SIZE+1];
- char *buff;
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- snprintf(pcm_path, BIN_PATH_SIZE,
- "/data/voice%d.pcm", hub_data->pcm_cnt);
- voice_filp = filp_open(pcm_path, O_RDWR | O_CREAT | O_APPEND, 0666);
- if (IS_ERR(voice_filp)) {
- sensorhub_err("open pcm dump file err");
- goto exit;
- }
- buf_len = big->length > DATA_PACKET_SIZE
- ? DATA_PACKET_SIZE : big->length;
- buff = kzalloc(buf_len, GFP_KERNEL);
- while (residue > 0) {
- buf_len = residue > DATA_PACKET_SIZE
- ? DATA_PACKET_SIZE : residue;
- msg = kzalloc(sizeof(*msg), GFP_KERNEL);
- msg->cmd = MSG2SSP_AP_GET_BIG_DATA;
- msg->length = buf_len;
- msg->options = AP2HUB_READ | (index++ << SSP_INDEX);
- msg->data = big->addr;
- msg->buffer = buff;
- msg->free_buffer = 0;
- ret = ssp_spi_sync(big->data, msg, 1000);
- if (ret != SUCCESS) {
- sensorhub_err("receive pcm dump err(%d)", ret);
- break;
- }
- ret = voice_filp->f_op->write(voice_filp, (char __user *)buff,
- buf_len, &voice_filp->f_pos);
- if (ret < 0) {
- sensorhub_err("write pcm dump to file err(%d)", ret);
- break;
- }
- residue -= buf_len;
- sensorhub_info("write pcm dump...");
- }
- filp_close(voice_filp, current->files);
- kfree(buff);
- exit:
- set_fs(old_fs);
- kfree(big);
- }
- int ssp_sensorhub_pcm_dump(struct ssp_sensorhub_data *hub_data)
- {
- hub_data->pcm_cnt++;
- return set_big_data_start(hub_data->ssp_data, BIG_TYPE_VOICE_PCM, 0);
- }
- int ssp_sensorhub_initialize(struct ssp_data *ssp_data)
- {
- struct ssp_sensorhub_data *hub_data;
- int ret;
- /* allocate memory for sensorhub data */
- hub_data = kzalloc(sizeof(*hub_data), GFP_KERNEL);
- if (!hub_data) {
- sensorhub_err("allocate memory for sensorhub data err");
- ret = -ENOMEM;
- goto exit;
- }
- hub_data->ssp_data = ssp_data;
- ssp_data->hub_data = hub_data;
- /* init wakelock, list, waitqueue, completion and spinlock */
- wake_lock_init(&hub_data->sensorhub_wake_lock, WAKE_LOCK_SUSPEND,
- "ssp_sensorhub_wake_lock");
- init_waitqueue_head(&hub_data->sensorhub_wq);
- init_completion(&hub_data->read_done);
- init_completion(&hub_data->big_read_done);
- init_completion(&hub_data->big_write_done);
- spin_lock_init(&hub_data->sensorhub_lock);
- /* allocate sensorhub input device */
- hub_data->sensorhub_input_dev = input_allocate_device();
- if (!hub_data->sensorhub_input_dev) {
- sensorhub_err("allocate sensorhub input device err");
- ret = -ENOMEM;
- goto err_input_allocate_device_sensorhub;
- }
- /* set sensorhub input device */
- input_set_drvdata(hub_data->sensorhub_input_dev, hub_data);
- hub_data->sensorhub_input_dev->name = "ssp_context";
- input_set_capability(hub_data->sensorhub_input_dev, EV_REL, DATA);
- input_set_capability(hub_data->sensorhub_input_dev, EV_REL, BIG_DATA);
- input_set_capability(hub_data->sensorhub_input_dev, EV_REL, NOTICE);
- /* register sensorhub input device */
- ret = input_register_device(hub_data->sensorhub_input_dev);
- if (ret < 0) {
- sensorhub_err("register sensorhub input device err(%d)", ret);
- input_free_device(hub_data->sensorhub_input_dev);
- goto err_input_register_device_sensorhub;
- }
- /* register sensorhub misc device */
- hub_data->sensorhub_device.minor = MISC_DYNAMIC_MINOR;
- hub_data->sensorhub_device.name = "ssp_sensorhub";
- hub_data->sensorhub_device.fops = &ssp_sensorhub_fops;
- ret = misc_register(&hub_data->sensorhub_device);
- if (ret < 0) {
- sensorhub_err("register sensorhub misc device err(%d)", ret);
- goto err_misc_register;
- }
- /* allocate fifo */
- ret = kfifo_alloc(&hub_data->fifo,
- sizeof(void *) * LIST_SIZE, GFP_KERNEL);
- if (ret) {
- sensorhub_err("kfifo allocate err(%d)", ret);
- goto err_kfifo_alloc;
- }
- /* create and run sensorhub thread */
- hub_data->sensorhub_task = kthread_run(ssp_sensorhub_thread,
- (void *)hub_data, "ssp_sensorhub_thread");
- if (IS_ERR(hub_data->sensorhub_task)) {
- ret = PTR_ERR(hub_data->sensorhub_task);
- goto err_kthread_run;
- }
- return 0;
- err_kthread_run:
- kfifo_free(&hub_data->fifo);
- err_kfifo_alloc:
- misc_deregister(&hub_data->sensorhub_device);
- err_misc_register:
- input_unregister_device(hub_data->sensorhub_input_dev);
- err_input_register_device_sensorhub:
- err_input_allocate_device_sensorhub:
- complete_all(&hub_data->big_write_done);
- complete_all(&hub_data->big_read_done);
- complete_all(&hub_data->read_done);
- wake_lock_destroy(&hub_data->sensorhub_wake_lock);
- kfree(hub_data);
- exit:
- return ret;
- }
- void ssp_sensorhub_remove(struct ssp_data *ssp_data)
- {
- struct ssp_sensorhub_data *hub_data = ssp_data->hub_data;
- ssp_sensorhub_fops.write = NULL;
- ssp_sensorhub_fops.read = NULL;
- ssp_sensorhub_fops.unlocked_ioctl = NULL;
- kthread_stop(hub_data->sensorhub_task);
- kfifo_free(&hub_data->fifo);
- misc_deregister(&hub_data->sensorhub_device);
- input_unregister_device(hub_data->sensorhub_input_dev);
- complete_all(&hub_data->big_write_done);
- complete_all(&hub_data->big_read_done);
- complete_all(&hub_data->read_done);
- wake_lock_destroy(&hub_data->sensorhub_wake_lock);
- kfree(hub_data);
- }
- MODULE_DESCRIPTION("Seamless Sensor Platform(SSP) sensorhub driver");
- MODULE_AUTHOR("Samsung Electronics");
- MODULE_LICENSE("GPL");
|