1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462 |
- /*
- * cmt_speech.c - HSI CMT speech driver
- *
- * Copyright (C) 2008,2009,2010 Nokia Corporation. All rights reserved.
- *
- * Contact: Kai Vehmanen <kai.vehmanen@nokia.com>
- * Original author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
- #include <linux/errno.h>
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/init.h>
- #include <linux/device.h>
- #include <linux/miscdevice.h>
- #include <linux/mm.h>
- #include <linux/slab.h>
- #include <linux/fs.h>
- #include <linux/poll.h>
- #include <linux/sched.h>
- #include <linux/ioctl.h>
- #include <linux/uaccess.h>
- #include <linux/pm_qos.h>
- #include <linux/hsi/hsi.h>
- #include <linux/hsi/ssi_protocol.h>
- #include <linux/hsi/cs-protocol.h>
- #define CS_MMAP_SIZE PAGE_SIZE
- struct char_queue {
- struct list_head list;
- u32 msg;
- };
- struct cs_char {
- unsigned int opened;
- struct hsi_client *cl;
- struct cs_hsi_iface *hi;
- struct list_head chardev_queue;
- struct list_head dataind_queue;
- int dataind_pending;
- /* mmap things */
- unsigned long mmap_base;
- unsigned long mmap_size;
- spinlock_t lock;
- struct fasync_struct *async_queue;
- wait_queue_head_t wait;
- /* hsi channel ids */
- int channel_id_cmd;
- int channel_id_data;
- };
- #define SSI_CHANNEL_STATE_READING 1
- #define SSI_CHANNEL_STATE_WRITING (1 << 1)
- #define SSI_CHANNEL_STATE_POLL (1 << 2)
- #define SSI_CHANNEL_STATE_ERROR (1 << 3)
- #define TARGET_MASK 0xf000000
- #define TARGET_REMOTE (1 << CS_DOMAIN_SHIFT)
- #define TARGET_LOCAL 0
- /* Number of pre-allocated commands buffers */
- #define CS_MAX_CMDS 4
- /*
- * During data transfers, transactions must be handled
- * within 20ms (fixed value in cmtspeech HSI protocol)
- */
- #define CS_QOS_LATENCY_FOR_DATA_USEC 20000
- /* Timeout to wait for pending HSI transfers to complete */
- #define CS_HSI_TRANSFER_TIMEOUT_MS 500
- #define RX_PTR_BOUNDARY_SHIFT 8
- #define RX_PTR_MAX_SHIFT (RX_PTR_BOUNDARY_SHIFT + \
- CS_MAX_BUFFERS_SHIFT)
- struct cs_hsi_iface {
- struct hsi_client *cl;
- struct hsi_client *master;
- unsigned int iface_state;
- unsigned int wakeline_state;
- unsigned int control_state;
- unsigned int data_state;
- /* state exposed to application */
- struct cs_mmap_config_block *mmap_cfg;
- unsigned long mmap_base;
- unsigned long mmap_size;
- unsigned int rx_slot;
- unsigned int tx_slot;
- /* note: for security reasons, we do not trust the contents of
- * mmap_cfg, but instead duplicate the variables here */
- unsigned int buf_size;
- unsigned int rx_bufs;
- unsigned int tx_bufs;
- unsigned int rx_ptr_boundary;
- unsigned int rx_offsets[CS_MAX_BUFFERS];
- unsigned int tx_offsets[CS_MAX_BUFFERS];
- /* size of aligned memory blocks */
- unsigned int slot_size;
- unsigned int flags;
- struct list_head cmdqueue;
- struct hsi_msg *data_rx_msg;
- struct hsi_msg *data_tx_msg;
- wait_queue_head_t datawait;
- struct pm_qos_request pm_qos_req;
- spinlock_t lock;
- };
- static struct cs_char cs_char_data;
- static void cs_hsi_read_on_control(struct cs_hsi_iface *hi);
- static void cs_hsi_read_on_data(struct cs_hsi_iface *hi);
- static inline void rx_ptr_shift_too_big(void)
- {
- BUILD_BUG_ON((1LLU << RX_PTR_MAX_SHIFT) > UINT_MAX);
- }
- static void cs_notify(u32 message, struct list_head *head)
- {
- struct char_queue *entry;
- spin_lock(&cs_char_data.lock);
- if (!cs_char_data.opened) {
- spin_unlock(&cs_char_data.lock);
- goto out;
- }
- entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
- if (!entry) {
- dev_err(&cs_char_data.cl->device,
- "Can't allocate new entry for the queue.\n");
- spin_unlock(&cs_char_data.lock);
- goto out;
- }
- entry->msg = message;
- list_add_tail(&entry->list, head);
- spin_unlock(&cs_char_data.lock);
- wake_up_interruptible(&cs_char_data.wait);
- kill_fasync(&cs_char_data.async_queue, SIGIO, POLL_IN);
- out:
- return;
- }
- static u32 cs_pop_entry(struct list_head *head)
- {
- struct char_queue *entry;
- u32 data;
- entry = list_entry(head->next, struct char_queue, list);
- data = entry->msg;
- list_del(&entry->list);
- kfree(entry);
- return data;
- }
- static void cs_notify_control(u32 message)
- {
- cs_notify(message, &cs_char_data.chardev_queue);
- }
- static void cs_notify_data(u32 message, int maxlength)
- {
- cs_notify(message, &cs_char_data.dataind_queue);
- spin_lock(&cs_char_data.lock);
- cs_char_data.dataind_pending++;
- while (cs_char_data.dataind_pending > maxlength &&
- !list_empty(&cs_char_data.dataind_queue)) {
- dev_dbg(&cs_char_data.cl->device, "data notification "
- "queue overrun (%u entries)\n", cs_char_data.dataind_pending);
- cs_pop_entry(&cs_char_data.dataind_queue);
- cs_char_data.dataind_pending--;
- }
- spin_unlock(&cs_char_data.lock);
- }
- static inline void cs_set_cmd(struct hsi_msg *msg, u32 cmd)
- {
- u32 *data = sg_virt(msg->sgt.sgl);
- *data = cmd;
- }
- static inline u32 cs_get_cmd(struct hsi_msg *msg)
- {
- u32 *data = sg_virt(msg->sgt.sgl);
- return *data;
- }
- static void cs_release_cmd(struct hsi_msg *msg)
- {
- struct cs_hsi_iface *hi = msg->context;
- list_add_tail(&msg->link, &hi->cmdqueue);
- }
- static void cs_cmd_destructor(struct hsi_msg *msg)
- {
- struct cs_hsi_iface *hi = msg->context;
- spin_lock(&hi->lock);
- dev_dbg(&cs_char_data.cl->device, "control cmd destructor\n");
- if (hi->iface_state != CS_STATE_CLOSED)
- dev_err(&hi->cl->device, "Cmd flushed while driver active\n");
- if (msg->ttype == HSI_MSG_READ)
- hi->control_state &=
- ~(SSI_CHANNEL_STATE_POLL | SSI_CHANNEL_STATE_READING);
- else if (msg->ttype == HSI_MSG_WRITE &&
- hi->control_state & SSI_CHANNEL_STATE_WRITING)
- hi->control_state &= ~SSI_CHANNEL_STATE_WRITING;
- cs_release_cmd(msg);
- spin_unlock(&hi->lock);
- }
- static struct hsi_msg *cs_claim_cmd(struct cs_hsi_iface* ssi)
- {
- struct hsi_msg *msg;
- BUG_ON(list_empty(&ssi->cmdqueue));
- msg = list_first_entry(&ssi->cmdqueue, struct hsi_msg, link);
- list_del(&msg->link);
- msg->destructor = cs_cmd_destructor;
- return msg;
- }
- static void cs_free_cmds(struct cs_hsi_iface *ssi)
- {
- struct hsi_msg *msg, *tmp;
- list_for_each_entry_safe(msg, tmp, &ssi->cmdqueue, link) {
- list_del(&msg->link);
- msg->destructor = NULL;
- kfree(sg_virt(msg->sgt.sgl));
- hsi_free_msg(msg);
- }
- }
- static int cs_alloc_cmds(struct cs_hsi_iface *hi)
- {
- struct hsi_msg *msg;
- u32 *buf;
- unsigned int i;
- INIT_LIST_HEAD(&hi->cmdqueue);
- for (i = 0; i < CS_MAX_CMDS; i++) {
- msg = hsi_alloc_msg(1, GFP_KERNEL);
- if (!msg)
- goto out;
- buf = kmalloc(sizeof(*buf), GFP_KERNEL);
- if (!buf) {
- hsi_free_msg(msg);
- goto out;
- }
- sg_init_one(msg->sgt.sgl, buf, sizeof(*buf));
- msg->channel = cs_char_data.channel_id_cmd;
- msg->context = hi;
- list_add_tail(&msg->link, &hi->cmdqueue);
- }
- return 0;
- out:
- cs_free_cmds(hi);
- return -ENOMEM;
- }
- static void cs_hsi_data_destructor(struct hsi_msg *msg)
- {
- struct cs_hsi_iface *hi = msg->context;
- const char *dir = (msg->ttype == HSI_MSG_READ) ? "TX" : "RX";
- dev_dbg(&cs_char_data.cl->device, "Freeing data %s message\n", dir);
- spin_lock(&hi->lock);
- if (hi->iface_state != CS_STATE_CLOSED)
- dev_err(&cs_char_data.cl->device,
- "Data %s flush while device active\n", dir);
- if (msg->ttype == HSI_MSG_READ)
- hi->data_state &=
- ~(SSI_CHANNEL_STATE_POLL | SSI_CHANNEL_STATE_READING);
- else
- hi->data_state &= ~SSI_CHANNEL_STATE_WRITING;
- msg->status = HSI_STATUS_COMPLETED;
- if (unlikely(waitqueue_active(&hi->datawait)))
- wake_up_interruptible(&hi->datawait);
- spin_unlock(&hi->lock);
- }
- static int cs_hsi_alloc_data(struct cs_hsi_iface *hi)
- {
- struct hsi_msg *txmsg, *rxmsg;
- int res = 0;
- rxmsg = hsi_alloc_msg(1, GFP_KERNEL);
- if (!rxmsg) {
- res = -ENOMEM;
- goto out1;
- }
- rxmsg->channel = cs_char_data.channel_id_data;
- rxmsg->destructor = cs_hsi_data_destructor;
- rxmsg->context = hi;
- txmsg = hsi_alloc_msg(1, GFP_KERNEL);
- if (!txmsg) {
- res = -ENOMEM;
- goto out2;
- }
- txmsg->channel = cs_char_data.channel_id_data;
- txmsg->destructor = cs_hsi_data_destructor;
- txmsg->context = hi;
- hi->data_rx_msg = rxmsg;
- hi->data_tx_msg = txmsg;
- return 0;
- out2:
- hsi_free_msg(rxmsg);
- out1:
- return res;
- }
- static void cs_hsi_free_data_msg(struct hsi_msg *msg)
- {
- WARN_ON(msg->status != HSI_STATUS_COMPLETED &&
- msg->status != HSI_STATUS_ERROR);
- hsi_free_msg(msg);
- }
- static void cs_hsi_free_data(struct cs_hsi_iface *hi)
- {
- cs_hsi_free_data_msg(hi->data_rx_msg);
- cs_hsi_free_data_msg(hi->data_tx_msg);
- }
- static inline void __cs_hsi_error_pre(struct cs_hsi_iface *hi,
- struct hsi_msg *msg, const char *info,
- unsigned int *state)
- {
- spin_lock(&hi->lock);
- dev_err(&hi->cl->device, "HSI %s error, msg %d, state %u\n",
- info, msg->status, *state);
- }
- static inline void __cs_hsi_error_post(struct cs_hsi_iface *hi)
- {
- spin_unlock(&hi->lock);
- }
- static inline void __cs_hsi_error_read_bits(unsigned int *state)
- {
- *state |= SSI_CHANNEL_STATE_ERROR;
- *state &= ~(SSI_CHANNEL_STATE_READING | SSI_CHANNEL_STATE_POLL);
- }
- static inline void __cs_hsi_error_write_bits(unsigned int *state)
- {
- *state |= SSI_CHANNEL_STATE_ERROR;
- *state &= ~SSI_CHANNEL_STATE_WRITING;
- }
- static void cs_hsi_control_read_error(struct cs_hsi_iface *hi,
- struct hsi_msg *msg)
- {
- __cs_hsi_error_pre(hi, msg, "control read", &hi->control_state);
- cs_release_cmd(msg);
- __cs_hsi_error_read_bits(&hi->control_state);
- __cs_hsi_error_post(hi);
- }
- static void cs_hsi_control_write_error(struct cs_hsi_iface *hi,
- struct hsi_msg *msg)
- {
- __cs_hsi_error_pre(hi, msg, "control write", &hi->control_state);
- cs_release_cmd(msg);
- __cs_hsi_error_write_bits(&hi->control_state);
- __cs_hsi_error_post(hi);
- }
- static void cs_hsi_data_read_error(struct cs_hsi_iface *hi, struct hsi_msg *msg)
- {
- __cs_hsi_error_pre(hi, msg, "data read", &hi->data_state);
- __cs_hsi_error_read_bits(&hi->data_state);
- __cs_hsi_error_post(hi);
- }
- static void cs_hsi_data_write_error(struct cs_hsi_iface *hi,
- struct hsi_msg *msg)
- {
- __cs_hsi_error_pre(hi, msg, "data write", &hi->data_state);
- __cs_hsi_error_write_bits(&hi->data_state);
- __cs_hsi_error_post(hi);
- }
- static void cs_hsi_read_on_control_complete(struct hsi_msg *msg)
- {
- u32 cmd = cs_get_cmd(msg);
- struct cs_hsi_iface *hi = msg->context;
- spin_lock(&hi->lock);
- hi->control_state &= ~SSI_CHANNEL_STATE_READING;
- if (msg->status == HSI_STATUS_ERROR) {
- dev_err(&hi->cl->device, "Control RX error detected\n");
- cs_hsi_control_read_error(hi, msg);
- spin_unlock(&hi->lock);
- goto out;
- }
- dev_dbg(&hi->cl->device, "Read on control: %08X\n", cmd);
- cs_release_cmd(msg);
- if (hi->flags & CS_FEAT_TSTAMP_RX_CTRL) {
- struct timespec tspec;
- struct cs_timestamp *tstamp =
- &hi->mmap_cfg->tstamp_rx_ctrl;
- ktime_get_ts(&tspec);
- tstamp->tv_sec = (__u32) tspec.tv_sec;
- tstamp->tv_nsec = (__u32) tspec.tv_nsec;
- }
- spin_unlock(&hi->lock);
- cs_notify_control(cmd);
- out:
- cs_hsi_read_on_control(hi);
- }
- static void cs_hsi_peek_on_control_complete(struct hsi_msg *msg)
- {
- struct cs_hsi_iface *hi = msg->context;
- int ret;
- if (msg->status == HSI_STATUS_ERROR) {
- dev_err(&hi->cl->device, "Control peek RX error detected\n");
- cs_hsi_control_read_error(hi, msg);
- return;
- }
- WARN_ON(!(hi->control_state & SSI_CHANNEL_STATE_READING));
- dev_dbg(&hi->cl->device, "Peek on control complete, reading\n");
- msg->sgt.nents = 1;
- msg->complete = cs_hsi_read_on_control_complete;
- ret = hsi_async_read(hi->cl, msg);
- if (ret)
- cs_hsi_control_read_error(hi, msg);
- }
- static void cs_hsi_read_on_control(struct cs_hsi_iface *hi)
- {
- struct hsi_msg *msg;
- int ret;
- spin_lock(&hi->lock);
- if (hi->control_state & SSI_CHANNEL_STATE_READING) {
- dev_err(&hi->cl->device, "Control read already pending (%d)\n",
- hi->control_state);
- spin_unlock(&hi->lock);
- return;
- }
- if (hi->control_state & SSI_CHANNEL_STATE_ERROR) {
- dev_err(&hi->cl->device, "Control read error (%d)\n",
- hi->control_state);
- spin_unlock(&hi->lock);
- return;
- }
- hi->control_state |= SSI_CHANNEL_STATE_READING;
- dev_dbg(&hi->cl->device, "Issuing RX on control\n");
- msg = cs_claim_cmd(hi);
- spin_unlock(&hi->lock);
- msg->sgt.nents = 0;
- msg->complete = cs_hsi_peek_on_control_complete;
- ret = hsi_async_read(hi->cl, msg);
- if (ret)
- cs_hsi_control_read_error(hi, msg);
- }
- static void cs_hsi_write_on_control_complete(struct hsi_msg *msg)
- {
- struct cs_hsi_iface *hi = msg->context;
- if (msg->status == HSI_STATUS_COMPLETED) {
- spin_lock(&hi->lock);
- hi->control_state &= ~SSI_CHANNEL_STATE_WRITING;
- cs_release_cmd(msg);
- spin_unlock(&hi->lock);
- } else if (msg->status == HSI_STATUS_ERROR) {
- cs_hsi_control_write_error(hi, msg);
- } else {
- dev_err(&hi->cl->device,
- "unexpected status in control write callback %d\n",
- msg->status);
- }
- }
- static int cs_hsi_write_on_control(struct cs_hsi_iface *hi, u32 message)
- {
- struct hsi_msg *msg;
- int ret;
- spin_lock(&hi->lock);
- if (hi->control_state & SSI_CHANNEL_STATE_ERROR) {
- spin_unlock(&hi->lock);
- return -EIO;
- }
- if (hi->control_state & SSI_CHANNEL_STATE_WRITING) {
- dev_err(&hi->cl->device,
- "Write still pending on control channel.\n");
- spin_unlock(&hi->lock);
- return -EBUSY;
- }
- hi->control_state |= SSI_CHANNEL_STATE_WRITING;
- msg = cs_claim_cmd(hi);
- spin_unlock(&hi->lock);
- cs_set_cmd(msg, message);
- msg->sgt.nents = 1;
- msg->complete = cs_hsi_write_on_control_complete;
- dev_dbg(&hi->cl->device,
- "Sending control message %08X\n", message);
- ret = hsi_async_write(hi->cl, msg);
- if (ret) {
- dev_err(&hi->cl->device,
- "async_write failed with %d\n", ret);
- cs_hsi_control_write_error(hi, msg);
- }
- /*
- * Make sure control read is always pending when issuing
- * new control writes. This is needed as the controller
- * may flush our messages if e.g. the peer device reboots
- * unexpectedly (and we cannot directly resubmit a new read from
- * the message destructor; see cs_cmd_destructor()).
- */
- if (!(hi->control_state & SSI_CHANNEL_STATE_READING)) {
- dev_err(&hi->cl->device, "Restarting control reads\n");
- cs_hsi_read_on_control(hi);
- }
- return 0;
- }
- static void cs_hsi_read_on_data_complete(struct hsi_msg *msg)
- {
- struct cs_hsi_iface *hi = msg->context;
- u32 payload;
- if (unlikely(msg->status == HSI_STATUS_ERROR)) {
- cs_hsi_data_read_error(hi, msg);
- return;
- }
- spin_lock(&hi->lock);
- WARN_ON(!(hi->data_state & SSI_CHANNEL_STATE_READING));
- hi->data_state &= ~SSI_CHANNEL_STATE_READING;
- payload = CS_RX_DATA_RECEIVED;
- payload |= hi->rx_slot;
- hi->rx_slot++;
- hi->rx_slot %= hi->rx_ptr_boundary;
- /* expose current rx ptr in mmap area */
- hi->mmap_cfg->rx_ptr = hi->rx_slot;
- if (unlikely(waitqueue_active(&hi->datawait)))
- wake_up_interruptible(&hi->datawait);
- spin_unlock(&hi->lock);
- cs_notify_data(payload, hi->rx_bufs);
- cs_hsi_read_on_data(hi);
- }
- static void cs_hsi_peek_on_data_complete(struct hsi_msg *msg)
- {
- struct cs_hsi_iface *hi = msg->context;
- u32 *address;
- int ret;
- if (unlikely(msg->status == HSI_STATUS_ERROR)) {
- cs_hsi_data_read_error(hi, msg);
- return;
- }
- if (unlikely(hi->iface_state != CS_STATE_CONFIGURED)) {
- dev_err(&hi->cl->device, "Data received in invalid state\n");
- cs_hsi_data_read_error(hi, msg);
- return;
- }
- spin_lock(&hi->lock);
- WARN_ON(!(hi->data_state & SSI_CHANNEL_STATE_POLL));
- hi->data_state &= ~SSI_CHANNEL_STATE_POLL;
- hi->data_state |= SSI_CHANNEL_STATE_READING;
- spin_unlock(&hi->lock);
- address = (u32 *)(hi->mmap_base +
- hi->rx_offsets[hi->rx_slot % hi->rx_bufs]);
- sg_init_one(msg->sgt.sgl, address, hi->buf_size);
- msg->sgt.nents = 1;
- msg->complete = cs_hsi_read_on_data_complete;
- ret = hsi_async_read(hi->cl, msg);
- if (ret)
- cs_hsi_data_read_error(hi, msg);
- }
- /*
- * Read/write transaction is ongoing. Returns false if in
- * SSI_CHANNEL_STATE_POLL state.
- */
- static inline int cs_state_xfer_active(unsigned int state)
- {
- return (state & SSI_CHANNEL_STATE_WRITING) ||
- (state & SSI_CHANNEL_STATE_READING);
- }
- /*
- * No pending read/writes
- */
- static inline int cs_state_idle(unsigned int state)
- {
- return !(state & ~SSI_CHANNEL_STATE_ERROR);
- }
- static void cs_hsi_read_on_data(struct cs_hsi_iface *hi)
- {
- struct hsi_msg *rxmsg;
- int ret;
- spin_lock(&hi->lock);
- if (hi->data_state &
- (SSI_CHANNEL_STATE_READING | SSI_CHANNEL_STATE_POLL)) {
- dev_dbg(&hi->cl->device, "Data read already pending (%u)\n",
- hi->data_state);
- spin_unlock(&hi->lock);
- return;
- }
- hi->data_state |= SSI_CHANNEL_STATE_POLL;
- spin_unlock(&hi->lock);
- rxmsg = hi->data_rx_msg;
- sg_init_one(rxmsg->sgt.sgl, (void *)hi->mmap_base, 0);
- rxmsg->sgt.nents = 0;
- rxmsg->complete = cs_hsi_peek_on_data_complete;
- ret = hsi_async_read(hi->cl, rxmsg);
- if (ret)
- cs_hsi_data_read_error(hi, rxmsg);
- }
- static void cs_hsi_write_on_data_complete(struct hsi_msg *msg)
- {
- struct cs_hsi_iface *hi = msg->context;
- if (msg->status == HSI_STATUS_COMPLETED) {
- spin_lock(&hi->lock);
- hi->data_state &= ~SSI_CHANNEL_STATE_WRITING;
- if (unlikely(waitqueue_active(&hi->datawait)))
- wake_up_interruptible(&hi->datawait);
- spin_unlock(&hi->lock);
- } else {
- cs_hsi_data_write_error(hi, msg);
- }
- }
- static int cs_hsi_write_on_data(struct cs_hsi_iface *hi, unsigned int slot)
- {
- u32 *address;
- struct hsi_msg *txmsg;
- int ret;
- spin_lock(&hi->lock);
- if (hi->iface_state != CS_STATE_CONFIGURED) {
- dev_err(&hi->cl->device, "Not configured, aborting\n");
- ret = -EINVAL;
- goto error;
- }
- if (hi->data_state & SSI_CHANNEL_STATE_ERROR) {
- dev_err(&hi->cl->device, "HSI error, aborting\n");
- ret = -EIO;
- goto error;
- }
- if (hi->data_state & SSI_CHANNEL_STATE_WRITING) {
- dev_err(&hi->cl->device, "Write pending on data channel.\n");
- ret = -EBUSY;
- goto error;
- }
- hi->data_state |= SSI_CHANNEL_STATE_WRITING;
- spin_unlock(&hi->lock);
- hi->tx_slot = slot;
- address = (u32 *)(hi->mmap_base + hi->tx_offsets[hi->tx_slot]);
- txmsg = hi->data_tx_msg;
- sg_init_one(txmsg->sgt.sgl, address, hi->buf_size);
- txmsg->complete = cs_hsi_write_on_data_complete;
- ret = hsi_async_write(hi->cl, txmsg);
- if (ret)
- cs_hsi_data_write_error(hi, txmsg);
- return ret;
- error:
- spin_unlock(&hi->lock);
- if (ret == -EIO)
- cs_hsi_data_write_error(hi, hi->data_tx_msg);
- return ret;
- }
- static unsigned int cs_hsi_get_state(struct cs_hsi_iface *hi)
- {
- return hi->iface_state;
- }
- static int cs_hsi_command(struct cs_hsi_iface *hi, u32 cmd)
- {
- int ret = 0;
- local_bh_disable();
- switch (cmd & TARGET_MASK) {
- case TARGET_REMOTE:
- ret = cs_hsi_write_on_control(hi, cmd);
- break;
- case TARGET_LOCAL:
- if ((cmd & CS_CMD_MASK) == CS_TX_DATA_READY)
- ret = cs_hsi_write_on_data(hi, cmd & CS_PARAM_MASK);
- else
- ret = -EINVAL;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- local_bh_enable();
- return ret;
- }
- static void cs_hsi_set_wakeline(struct cs_hsi_iface *hi, bool new_state)
- {
- int change = 0;
- spin_lock_bh(&hi->lock);
- if (hi->wakeline_state != new_state) {
- hi->wakeline_state = new_state;
- change = 1;
- dev_dbg(&hi->cl->device, "setting wake line to %d (%p)\n",
- new_state, hi->cl);
- }
- spin_unlock_bh(&hi->lock);
- if (change) {
- if (new_state)
- ssip_slave_start_tx(hi->master);
- else
- ssip_slave_stop_tx(hi->master);
- }
- dev_dbg(&hi->cl->device, "wake line set to %d (%p)\n",
- new_state, hi->cl);
- }
- static void set_buffer_sizes(struct cs_hsi_iface *hi, int rx_bufs, int tx_bufs)
- {
- hi->rx_bufs = rx_bufs;
- hi->tx_bufs = tx_bufs;
- hi->mmap_cfg->rx_bufs = rx_bufs;
- hi->mmap_cfg->tx_bufs = tx_bufs;
- if (hi->flags & CS_FEAT_ROLLING_RX_COUNTER) {
- /*
- * For more robust overrun detection, let the rx
- * pointer run in range 0..'boundary-1'. Boundary
- * is a multiple of rx_bufs, and limited in max size
- * by RX_PTR_MAX_SHIFT to allow for fast ptr-diff
- * calculation.
- */
- hi->rx_ptr_boundary = (rx_bufs << RX_PTR_BOUNDARY_SHIFT);
- hi->mmap_cfg->rx_ptr_boundary = hi->rx_ptr_boundary;
- } else {
- hi->rx_ptr_boundary = hi->rx_bufs;
- }
- }
- static int check_buf_params(struct cs_hsi_iface *hi,
- const struct cs_buffer_config *buf_cfg)
- {
- size_t buf_size_aligned = L1_CACHE_ALIGN(buf_cfg->buf_size) *
- (buf_cfg->rx_bufs + buf_cfg->tx_bufs);
- size_t ctrl_size_aligned = L1_CACHE_ALIGN(sizeof(*hi->mmap_cfg));
- int r = 0;
- if (buf_cfg->rx_bufs > CS_MAX_BUFFERS ||
- buf_cfg->tx_bufs > CS_MAX_BUFFERS) {
- r = -EINVAL;
- } else if ((buf_size_aligned + ctrl_size_aligned) >= hi->mmap_size) {
- dev_err(&hi->cl->device, "No space for the requested buffer "
- "configuration\n");
- r = -ENOBUFS;
- }
- return r;
- }
- /**
- * Block until pending data transfers have completed.
- */
- static int cs_hsi_data_sync(struct cs_hsi_iface *hi)
- {
- int r = 0;
- spin_lock_bh(&hi->lock);
- if (!cs_state_xfer_active(hi->data_state)) {
- dev_dbg(&hi->cl->device, "hsi_data_sync break, idle\n");
- goto out;
- }
- for (;;) {
- int s;
- DEFINE_WAIT(wait);
- if (!cs_state_xfer_active(hi->data_state))
- goto out;
- if (signal_pending(current)) {
- r = -ERESTARTSYS;
- goto out;
- }
- /**
- * prepare_to_wait must be called with hi->lock held
- * so that callbacks can check for waitqueue_active()
- */
- prepare_to_wait(&hi->datawait, &wait, TASK_INTERRUPTIBLE);
- spin_unlock_bh(&hi->lock);
- s = schedule_timeout(
- msecs_to_jiffies(CS_HSI_TRANSFER_TIMEOUT_MS));
- spin_lock_bh(&hi->lock);
- finish_wait(&hi->datawait, &wait);
- if (!s) {
- dev_dbg(&hi->cl->device,
- "hsi_data_sync timeout after %d ms\n",
- CS_HSI_TRANSFER_TIMEOUT_MS);
- r = -EIO;
- goto out;
- }
- }
- out:
- spin_unlock_bh(&hi->lock);
- dev_dbg(&hi->cl->device, "hsi_data_sync done with res %d\n", r);
- return r;
- }
- static void cs_hsi_data_enable(struct cs_hsi_iface *hi,
- struct cs_buffer_config *buf_cfg)
- {
- unsigned int data_start, i;
- BUG_ON(hi->buf_size == 0);
- set_buffer_sizes(hi, buf_cfg->rx_bufs, buf_cfg->tx_bufs);
- hi->slot_size = L1_CACHE_ALIGN(hi->buf_size);
- dev_dbg(&hi->cl->device,
- "setting slot size to %u, buf size %u, align %u\n",
- hi->slot_size, hi->buf_size, L1_CACHE_BYTES);
- data_start = L1_CACHE_ALIGN(sizeof(*hi->mmap_cfg));
- dev_dbg(&hi->cl->device,
- "setting data start at %u, cfg block %u, align %u\n",
- data_start, sizeof(*hi->mmap_cfg), L1_CACHE_BYTES);
- for (i = 0; i < hi->mmap_cfg->rx_bufs; i++) {
- hi->rx_offsets[i] = data_start + i * hi->slot_size;
- hi->mmap_cfg->rx_offsets[i] = hi->rx_offsets[i];
- dev_dbg(&hi->cl->device, "DL buf #%u at %u\n",
- i, hi->rx_offsets[i]);
- }
- for (i = 0; i < hi->mmap_cfg->tx_bufs; i++) {
- hi->tx_offsets[i] = data_start +
- (i + hi->mmap_cfg->rx_bufs) * hi->slot_size;
- hi->mmap_cfg->tx_offsets[i] = hi->tx_offsets[i];
- dev_dbg(&hi->cl->device, "UL buf #%u at %u\n",
- i, hi->rx_offsets[i]);
- }
- hi->iface_state = CS_STATE_CONFIGURED;
- }
- static void cs_hsi_data_disable(struct cs_hsi_iface *hi, int old_state)
- {
- if (old_state == CS_STATE_CONFIGURED) {
- dev_dbg(&hi->cl->device,
- "closing data channel with slot size 0\n");
- hi->iface_state = CS_STATE_OPENED;
- }
- }
- static int cs_hsi_buf_config(struct cs_hsi_iface *hi,
- struct cs_buffer_config *buf_cfg)
- {
- int r = 0;
- unsigned int old_state = hi->iface_state;
- spin_lock_bh(&hi->lock);
- /* Prevent new transactions during buffer reconfig */
- if (old_state == CS_STATE_CONFIGURED)
- hi->iface_state = CS_STATE_OPENED;
- spin_unlock_bh(&hi->lock);
- /*
- * make sure that no non-zero data reads are ongoing before
- * proceeding to change the buffer layout
- */
- r = cs_hsi_data_sync(hi);
- if (r < 0)
- return r;
- WARN_ON(cs_state_xfer_active(hi->data_state));
- spin_lock_bh(&hi->lock);
- r = check_buf_params(hi, buf_cfg);
- if (r < 0)
- goto error;
- hi->buf_size = buf_cfg->buf_size;
- hi->mmap_cfg->buf_size = hi->buf_size;
- hi->flags = buf_cfg->flags;
- hi->rx_slot = 0;
- hi->tx_slot = 0;
- hi->slot_size = 0;
- if (hi->buf_size)
- cs_hsi_data_enable(hi, buf_cfg);
- else
- cs_hsi_data_disable(hi, old_state);
- spin_unlock_bh(&hi->lock);
- if (old_state != hi->iface_state) {
- if (hi->iface_state == CS_STATE_CONFIGURED) {
- pm_qos_add_request(&hi->pm_qos_req,
- PM_QOS_CPU_DMA_LATENCY,
- CS_QOS_LATENCY_FOR_DATA_USEC);
- local_bh_disable();
- cs_hsi_read_on_data(hi);
- local_bh_enable();
- } else if (old_state == CS_STATE_CONFIGURED) {
- pm_qos_remove_request(&hi->pm_qos_req);
- }
- }
- return r;
- error:
- spin_unlock_bh(&hi->lock);
- return r;
- }
- static int cs_hsi_start(struct cs_hsi_iface **hi, struct hsi_client *cl,
- unsigned long mmap_base, unsigned long mmap_size)
- {
- int err = 0;
- struct cs_hsi_iface *hsi_if = kzalloc(sizeof(*hsi_if), GFP_KERNEL);
- dev_dbg(&cl->device, "cs_hsi_start\n");
- if (!hsi_if) {
- err = -ENOMEM;
- goto leave0;
- }
- spin_lock_init(&hsi_if->lock);
- hsi_if->cl = cl;
- hsi_if->iface_state = CS_STATE_CLOSED;
- hsi_if->mmap_cfg = (struct cs_mmap_config_block *)mmap_base;
- hsi_if->mmap_base = mmap_base;
- hsi_if->mmap_size = mmap_size;
- memset(hsi_if->mmap_cfg, 0, sizeof(*hsi_if->mmap_cfg));
- init_waitqueue_head(&hsi_if->datawait);
- err = cs_alloc_cmds(hsi_if);
- if (err < 0) {
- dev_err(&cl->device, "Unable to alloc HSI messages\n");
- goto leave1;
- }
- err = cs_hsi_alloc_data(hsi_if);
- if (err < 0) {
- dev_err(&cl->device, "Unable to alloc HSI messages for data\n");
- goto leave2;
- }
- err = hsi_claim_port(cl, 1);
- if (err < 0) {
- dev_err(&cl->device,
- "Could not open, HSI port already claimed\n");
- goto leave3;
- }
- hsi_if->master = ssip_slave_get_master(cl);
- if (IS_ERR(hsi_if->master)) {
- err = PTR_ERR(hsi_if->master);
- dev_err(&cl->device, "Could not get HSI master client\n");
- goto leave4;
- }
- if (!ssip_slave_running(hsi_if->master)) {
- err = -ENODEV;
- dev_err(&cl->device,
- "HSI port not initialized\n");
- goto leave4;
- }
- hsi_if->iface_state = CS_STATE_OPENED;
- local_bh_disable();
- cs_hsi_read_on_control(hsi_if);
- local_bh_enable();
- dev_dbg(&cl->device, "cs_hsi_start...done\n");
- BUG_ON(!hi);
- *hi = hsi_if;
- return 0;
- leave4:
- hsi_release_port(cl);
- leave3:
- cs_hsi_free_data(hsi_if);
- leave2:
- cs_free_cmds(hsi_if);
- leave1:
- kfree(hsi_if);
- leave0:
- dev_dbg(&cl->device, "cs_hsi_start...done/error\n\n");
- return err;
- }
- static void cs_hsi_stop(struct cs_hsi_iface *hi)
- {
- dev_dbg(&hi->cl->device, "cs_hsi_stop\n");
- cs_hsi_set_wakeline(hi, 0);
- ssip_slave_put_master(hi->master);
- /* hsi_release_port() needs to be called with CS_STATE_CLOSED */
- hi->iface_state = CS_STATE_CLOSED;
- hsi_release_port(hi->cl);
- /*
- * hsi_release_port() should flush out all the pending
- * messages, so cs_state_idle() should be true for both
- * control and data channels.
- */
- WARN_ON(!cs_state_idle(hi->control_state));
- WARN_ON(!cs_state_idle(hi->data_state));
- if (pm_qos_request_active(&hi->pm_qos_req))
- pm_qos_remove_request(&hi->pm_qos_req);
- spin_lock_bh(&hi->lock);
- cs_hsi_free_data(hi);
- cs_free_cmds(hi);
- spin_unlock_bh(&hi->lock);
- kfree(hi);
- }
- static int cs_char_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
- {
- struct cs_char *csdata = vma->vm_private_data;
- struct page *page;
- page = virt_to_page(csdata->mmap_base);
- get_page(page);
- vmf->page = page;
- return 0;
- }
- static const struct vm_operations_struct cs_char_vm_ops = {
- .fault = cs_char_vma_fault,
- };
- static int cs_char_fasync(int fd, struct file *file, int on)
- {
- struct cs_char *csdata = file->private_data;
- if (fasync_helper(fd, file, on, &csdata->async_queue) < 0)
- return -EIO;
- return 0;
- }
- static unsigned int cs_char_poll(struct file *file, poll_table *wait)
- {
- struct cs_char *csdata = file->private_data;
- unsigned int ret = 0;
- poll_wait(file, &cs_char_data.wait, wait);
- spin_lock_bh(&csdata->lock);
- if (!list_empty(&csdata->chardev_queue))
- ret = POLLIN | POLLRDNORM;
- else if (!list_empty(&csdata->dataind_queue))
- ret = POLLIN | POLLRDNORM;
- spin_unlock_bh(&csdata->lock);
- return ret;
- }
- static ssize_t cs_char_read(struct file *file, char __user *buf, size_t count,
- loff_t *unused)
- {
- struct cs_char *csdata = file->private_data;
- u32 data;
- ssize_t retval;
- if (count < sizeof(data))
- return -EINVAL;
- for (;;) {
- DEFINE_WAIT(wait);
- spin_lock_bh(&csdata->lock);
- if (!list_empty(&csdata->chardev_queue)) {
- data = cs_pop_entry(&csdata->chardev_queue);
- } else if (!list_empty(&csdata->dataind_queue)) {
- data = cs_pop_entry(&csdata->dataind_queue);
- csdata->dataind_pending--;
- } else {
- data = 0;
- }
- spin_unlock_bh(&csdata->lock);
- if (data)
- break;
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- goto out;
- } else if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- goto out;
- }
- prepare_to_wait_exclusive(&csdata->wait, &wait,
- TASK_INTERRUPTIBLE);
- schedule();
- finish_wait(&csdata->wait, &wait);
- }
- retval = put_user(data, (u32 __user *)buf);
- if (!retval)
- retval = sizeof(data);
- out:
- return retval;
- }
- static ssize_t cs_char_write(struct file *file, const char __user *buf,
- size_t count, loff_t *unused)
- {
- struct cs_char *csdata = file->private_data;
- u32 data;
- int err;
- ssize_t retval;
- if (count < sizeof(data))
- return -EINVAL;
- if (get_user(data, (u32 __user *)buf))
- retval = -EFAULT;
- else
- retval = count;
- err = cs_hsi_command(csdata->hi, data);
- if (err < 0)
- retval = err;
- return retval;
- }
- static long cs_char_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
- {
- struct cs_char *csdata = file->private_data;
- int r = 0;
- switch (cmd) {
- case CS_GET_STATE: {
- unsigned int state;
- state = cs_hsi_get_state(csdata->hi);
- if (copy_to_user((void __user *)arg, &state, sizeof(state)))
- r = -EFAULT;
- break;
- }
- case CS_SET_WAKELINE: {
- unsigned int state;
- if (copy_from_user(&state, (void __user *)arg, sizeof(state))) {
- r = -EFAULT;
- break;
- }
- if (state > 1) {
- r = -EINVAL;
- break;
- }
- cs_hsi_set_wakeline(csdata->hi, !!state);
- break;
- }
- case CS_GET_IF_VERSION: {
- unsigned int ifver = CS_IF_VERSION;
- if (copy_to_user((void __user *)arg, &ifver, sizeof(ifver)))
- r = -EFAULT;
- break;
- }
- case CS_CONFIG_BUFS: {
- struct cs_buffer_config buf_cfg;
- if (copy_from_user(&buf_cfg, (void __user *)arg,
- sizeof(buf_cfg)))
- r = -EFAULT;
- else
- r = cs_hsi_buf_config(csdata->hi, &buf_cfg);
- break;
- }
- default:
- r = -ENOTTY;
- break;
- }
- return r;
- }
- static int cs_char_mmap(struct file *file, struct vm_area_struct *vma)
- {
- if (vma->vm_end < vma->vm_start)
- return -EINVAL;
- if (((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) != 1)
- return -EINVAL;
- vma->vm_flags |= VM_IO | VM_DONTDUMP | VM_DONTEXPAND;
- vma->vm_ops = &cs_char_vm_ops;
- vma->vm_private_data = file->private_data;
- return 0;
- }
- static int cs_char_open(struct inode *unused, struct file *file)
- {
- int ret = 0;
- unsigned long p;
- spin_lock_bh(&cs_char_data.lock);
- if (cs_char_data.opened) {
- ret = -EBUSY;
- spin_unlock_bh(&cs_char_data.lock);
- goto out1;
- }
- cs_char_data.opened = 1;
- cs_char_data.dataind_pending = 0;
- spin_unlock_bh(&cs_char_data.lock);
- p = get_zeroed_page(GFP_KERNEL);
- if (!p) {
- ret = -ENOMEM;
- goto out2;
- }
- ret = cs_hsi_start(&cs_char_data.hi, cs_char_data.cl, p, CS_MMAP_SIZE);
- if (ret) {
- dev_err(&cs_char_data.cl->device, "Unable to initialize HSI\n");
- goto out3;
- }
- /* these are only used in release so lock not needed */
- cs_char_data.mmap_base = p;
- cs_char_data.mmap_size = CS_MMAP_SIZE;
- file->private_data = &cs_char_data;
- return 0;
- out3:
- free_page(p);
- out2:
- spin_lock_bh(&cs_char_data.lock);
- cs_char_data.opened = 0;
- spin_unlock_bh(&cs_char_data.lock);
- out1:
- return ret;
- }
- static void cs_free_char_queue(struct list_head *head)
- {
- struct char_queue *entry;
- struct list_head *cursor, *next;
- if (!list_empty(head)) {
- list_for_each_safe(cursor, next, head) {
- entry = list_entry(cursor, struct char_queue, list);
- list_del(&entry->list);
- kfree(entry);
- }
- }
- }
- static int cs_char_release(struct inode *unused, struct file *file)
- {
- struct cs_char *csdata = file->private_data;
- cs_hsi_stop(csdata->hi);
- spin_lock_bh(&csdata->lock);
- csdata->hi = NULL;
- free_page(csdata->mmap_base);
- cs_free_char_queue(&csdata->chardev_queue);
- cs_free_char_queue(&csdata->dataind_queue);
- csdata->opened = 0;
- spin_unlock_bh(&csdata->lock);
- return 0;
- }
- static const struct file_operations cs_char_fops = {
- .owner = THIS_MODULE,
- .read = cs_char_read,
- .write = cs_char_write,
- .poll = cs_char_poll,
- .unlocked_ioctl = cs_char_ioctl,
- .mmap = cs_char_mmap,
- .open = cs_char_open,
- .release = cs_char_release,
- .fasync = cs_char_fasync,
- };
- static struct miscdevice cs_char_miscdev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "cmt_speech",
- .fops = &cs_char_fops
- };
- static int cs_hsi_client_probe(struct device *dev)
- {
- int err = 0;
- struct hsi_client *cl = to_hsi_client(dev);
- dev_dbg(dev, "hsi_client_probe\n");
- init_waitqueue_head(&cs_char_data.wait);
- spin_lock_init(&cs_char_data.lock);
- cs_char_data.opened = 0;
- cs_char_data.cl = cl;
- cs_char_data.hi = NULL;
- INIT_LIST_HEAD(&cs_char_data.chardev_queue);
- INIT_LIST_HEAD(&cs_char_data.dataind_queue);
- cs_char_data.channel_id_cmd = hsi_get_channel_id_by_name(cl,
- "speech-control");
- if (cs_char_data.channel_id_cmd < 0) {
- err = cs_char_data.channel_id_cmd;
- dev_err(dev, "Could not get cmd channel (%d)\n", err);
- return err;
- }
- cs_char_data.channel_id_data = hsi_get_channel_id_by_name(cl,
- "speech-data");
- if (cs_char_data.channel_id_data < 0) {
- err = cs_char_data.channel_id_data;
- dev_err(dev, "Could not get data channel (%d)\n", err);
- return err;
- }
- err = misc_register(&cs_char_miscdev);
- if (err)
- dev_err(dev, "Failed to register: %d\n", err);
- return err;
- }
- static int cs_hsi_client_remove(struct device *dev)
- {
- struct cs_hsi_iface *hi;
- dev_dbg(dev, "hsi_client_remove\n");
- misc_deregister(&cs_char_miscdev);
- spin_lock_bh(&cs_char_data.lock);
- hi = cs_char_data.hi;
- cs_char_data.hi = NULL;
- spin_unlock_bh(&cs_char_data.lock);
- if (hi)
- cs_hsi_stop(hi);
- return 0;
- }
- static struct hsi_client_driver cs_hsi_driver = {
- .driver = {
- .name = "cmt-speech",
- .owner = THIS_MODULE,
- .probe = cs_hsi_client_probe,
- .remove = cs_hsi_client_remove,
- },
- };
- static int __init cs_char_init(void)
- {
- pr_info("CMT speech driver added\n");
- return hsi_register_client_driver(&cs_hsi_driver);
- }
- module_init(cs_char_init);
- static void __exit cs_char_exit(void)
- {
- hsi_unregister_client_driver(&cs_hsi_driver);
- pr_info("CMT speech driver removed\n");
- }
- module_exit(cs_char_exit);
- MODULE_ALIAS("hsi:cmt-speech");
- MODULE_AUTHOR("Kai Vehmanen <kai.vehmanen@nokia.com>");
- MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
- MODULE_DESCRIPTION("CMT speech driver");
- MODULE_LICENSE("GPL v2");
|