1/*
2 * chnl_sm.c
3 *
4 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5 *
6 * Implements upper edge functions for Bridge driver channel module.
7 *
8 * Copyright (C) 2005-2006 Texas Instruments, Inc.
9 *
10 * This package is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 */
18
19/*
20 *      The lower edge functions must be implemented by the Bridge driver
21 *      writer, and are declared in chnl_sm.h.
22 *
23 *      Care is taken in this code to prevent simulataneous access to channel
24 *      queues from
25 *      1. Threads.
26 *      2. io_dpc(), scheduled from the io_isr() as an event.
27 *
28 *      This is done primarily by:
29 *      - Semaphores.
30 *      - state flags in the channel object; and
31 *      - ensuring the IO_Dispatch() routine, which is called from both
32 *        CHNL_AddIOReq() and the DPC(if implemented), is not re-entered.
33 *
34 *  Channel Invariant:
35 *      There is an important invariant condition which must be maintained per
36 *      channel outside of bridge_chnl_get_ioc() and IO_Dispatch(), violation of
37 *      which may cause timeouts and/or failure offunction sync_wait_on_event.
38 *      This invariant condition is:
39 *
40 *          list_empty(&pchnl->io_completions) ==> pchnl->sync_event is reset
41 *      and
42 *          !list_empty(&pchnl->io_completions) ==> pchnl->sync_event is set.
43 */
44
45#include <linux/types.h>
46
47/*  ----------------------------------- OS */
48#include <dspbridge/host_os.h>
49
50/*  ----------------------------------- DSP/BIOS Bridge */
51#include <dspbridge/dbdefs.h>
52
53/*  ----------------------------------- OS Adaptation Layer */
54#include <dspbridge/sync.h>
55
56/*  ----------------------------------- Bridge Driver */
57#include <dspbridge/dspdefs.h>
58#include <dspbridge/dspchnl.h>
59#include "_tiomap.h"
60
61/*  ----------------------------------- Platform Manager */
62#include <dspbridge/dev.h>
63
64/*  ----------------------------------- Others */
65#include <dspbridge/io_sm.h>
66
67/*  ----------------------------------- Define for This */
68#define USERMODE_ADDR   PAGE_OFFSET
69
70#define MAILBOX_IRQ INT_MAIL_MPU_IRQ
71
72/*  ----------------------------------- Function Prototypes */
73static int create_chirp_list(struct list_head *list, u32 chirps);
74
75static void free_chirp_list(struct list_head *list);
76
77static int search_free_channel(struct chnl_mgr *chnl_mgr_obj,
78				      u32 *chnl);
79
80/*
81 *  ======== bridge_chnl_add_io_req ========
82 *      Enqueue an I/O request for data transfer on a channel to the DSP.
83 *      The direction (mode) is specified in the channel object. Note the DSP
84 *      address is specified for channels opened in direct I/O mode.
85 */
86int bridge_chnl_add_io_req(struct chnl_object *chnl_obj, void *host_buf,
87			       u32 byte_size, u32 buf_size,
88			       u32 dw_dsp_addr, u32 dw_arg)
89{
90	int status = 0;
91	struct chnl_object *pchnl = (struct chnl_object *)chnl_obj;
92	struct chnl_irp *chnl_packet_obj = NULL;
93	struct bridge_dev_context *dev_ctxt;
94	struct dev_object *dev_obj;
95	u8 dw_state;
96	bool is_eos;
97	struct chnl_mgr *chnl_mgr_obj = pchnl->chnl_mgr_obj;
98	u8 *host_sys_buf = NULL;
99	bool sched_dpc = false;
100	u16 mb_val = 0;
101
102	is_eos = (byte_size == 0);
103
104	/* Validate args */
105	if (!host_buf || !pchnl)
106		return -EFAULT;
107
108	if (is_eos && CHNL_IS_INPUT(pchnl->chnl_mode))
109		return -EPERM;
110
111	/*
112	 * Check the channel state: only queue chirp if channel state
113	 * allows it.
114	 */
115	dw_state = pchnl->state;
116	if (dw_state != CHNL_STATEREADY) {
117		if (dw_state & CHNL_STATECANCEL)
118			return -ECANCELED;
119		if ((dw_state & CHNL_STATEEOS) &&
120				CHNL_IS_OUTPUT(pchnl->chnl_mode))
121			return -EPIPE;
122		/* No other possible states left */
123	}
124
125	dev_obj = dev_get_first();
126	dev_get_bridge_context(dev_obj, &dev_ctxt);
127	if (!dev_ctxt)
128		return -EFAULT;
129
130	if (pchnl->chnl_type == CHNL_PCPY && pchnl->chnl_id > 1 && host_buf) {
131		if (!(host_buf < (void *)USERMODE_ADDR)) {
132			host_sys_buf = host_buf;
133			goto func_cont;
134		}
135		/* if addr in user mode, then copy to kernel space */
136		host_sys_buf = kmalloc(buf_size, GFP_KERNEL);
137		if (host_sys_buf == NULL)
138			return -ENOMEM;
139
140		if (CHNL_IS_OUTPUT(pchnl->chnl_mode)) {
141			status = copy_from_user(host_sys_buf, host_buf,
142					buf_size);
143			if (status) {
144				kfree(host_sys_buf);
145				host_sys_buf = NULL;
146				return -EFAULT;
147			}
148		}
149	}
150func_cont:
151	/* Mailbox IRQ is disabled to avoid race condition with DMA/ZCPY
152	 * channels. DPCCS is held to avoid race conditions with PCPY channels.
153	 * If DPC is scheduled in process context (iosm_schedule) and any
154	 * non-mailbox interrupt occurs, that DPC will run and break CS. Hence
155	 * we disable ALL DPCs. We will try to disable ONLY IO DPC later. */
156	spin_lock_bh(&chnl_mgr_obj->chnl_mgr_lock);
157	omap_mbox_disable_irq(dev_ctxt->mbox, IRQ_RX);
158	if (pchnl->chnl_type == CHNL_PCPY) {
159		/* This is a processor-copy channel. */
160		if (CHNL_IS_OUTPUT(pchnl->chnl_mode)) {
161			/* Check buffer size on output channels for fit. */
162			if (byte_size > io_buf_size(
163						pchnl->chnl_mgr_obj->iomgr)) {
164				status = -EINVAL;
165				goto out;
166			}
167		}
168	}
169
170	/* Get a free chirp: */
171	if (list_empty(&pchnl->free_packets_list)) {
172		status = -EIO;
173		goto out;
174	}
175	chnl_packet_obj = list_first_entry(&pchnl->free_packets_list,
176			struct chnl_irp, link);
177	list_del(&chnl_packet_obj->link);
178
179	/* Enqueue the chirp on the chnl's IORequest queue: */
180	chnl_packet_obj->host_user_buf = chnl_packet_obj->host_sys_buf =
181		host_buf;
182	if (pchnl->chnl_type == CHNL_PCPY && pchnl->chnl_id > 1)
183		chnl_packet_obj->host_sys_buf = host_sys_buf;
184
185	/*
186	 * Note: for dma chans dw_dsp_addr contains dsp address
187	 * of SM buffer.
188	 */
189	/* DSP address */
190	chnl_packet_obj->dsp_tx_addr = dw_dsp_addr / chnl_mgr_obj->word_size;
191	chnl_packet_obj->byte_size = byte_size;
192	chnl_packet_obj->buf_size = buf_size;
193	/* Only valid for output channel */
194	chnl_packet_obj->arg = dw_arg;
195	chnl_packet_obj->status = (is_eos ? CHNL_IOCSTATEOS :
196			CHNL_IOCSTATCOMPLETE);
197	list_add_tail(&chnl_packet_obj->link, &pchnl->io_requests);
198	pchnl->cio_reqs++;
199	/*
200	 * If end of stream, update the channel state to prevent
201	 * more IOR's.
202	 */
203	if (is_eos)
204		pchnl->state |= CHNL_STATEEOS;
205
206	/* Request IO from the DSP */
207	io_request_chnl(chnl_mgr_obj->iomgr, pchnl,
208			(CHNL_IS_INPUT(pchnl->chnl_mode) ? IO_INPUT :
209			 IO_OUTPUT), &mb_val);
210	sched_dpc = true;
211out:
212	omap_mbox_enable_irq(dev_ctxt->mbox, IRQ_RX);
213	spin_unlock_bh(&chnl_mgr_obj->chnl_mgr_lock);
214	if (mb_val != 0)
215		sm_interrupt_dsp(dev_ctxt, mb_val);
216
217	/* Schedule a DPC, to do the actual data transfer */
218	if (sched_dpc)
219		iosm_schedule(chnl_mgr_obj->iomgr);
220
221	return status;
222}
223
224/*
225 *  ======== bridge_chnl_cancel_io ========
226 *      Return all I/O requests to the client which have not yet been
227 *      transferred.  The channel's I/O completion object is
228 *      signalled, and all the I/O requests are queued as IOC's, with the
229 *      status field set to CHNL_IOCSTATCANCEL.
230 *      This call is typically used in abort situations, and is a prelude to
231 *      chnl_close();
232 */
233int bridge_chnl_cancel_io(struct chnl_object *chnl_obj)
234{
235	struct chnl_object *pchnl = (struct chnl_object *)chnl_obj;
236	u32 chnl_id = -1;
237	s8 chnl_mode;
238	struct chnl_irp *chirp, *tmp;
239	struct chnl_mgr *chnl_mgr_obj = NULL;
240
241	/* Check args: */
242	if (!pchnl || !pchnl->chnl_mgr_obj)
243		return -EFAULT;
244
245	chnl_id = pchnl->chnl_id;
246	chnl_mode = pchnl->chnl_mode;
247	chnl_mgr_obj = pchnl->chnl_mgr_obj;
248
249	/*  Mark this channel as cancelled, to prevent further IORequests or
250	 *  IORequests or dispatching. */
251	spin_lock_bh(&chnl_mgr_obj->chnl_mgr_lock);
252
253	pchnl->state |= CHNL_STATECANCEL;
254
255	if (list_empty(&pchnl->io_requests)) {
256		spin_unlock_bh(&chnl_mgr_obj->chnl_mgr_lock);
257		return 0;
258	}
259
260	if (pchnl->chnl_type == CHNL_PCPY) {
261		/* Indicate we have no more buffers available for transfer: */
262		if (CHNL_IS_INPUT(pchnl->chnl_mode)) {
263			io_cancel_chnl(chnl_mgr_obj->iomgr, chnl_id);
264		} else {
265			/* Record that we no longer have output buffers
266			 * available: */
267			chnl_mgr_obj->output_mask &= ~(1 << chnl_id);
268		}
269	}
270	/* Move all IOR's to IOC queue: */
271	list_for_each_entry_safe(chirp, tmp, &pchnl->io_requests, link) {
272		list_del(&chirp->link);
273		chirp->byte_size = 0;
274		chirp->status |= CHNL_IOCSTATCANCEL;
275		list_add_tail(&chirp->link, &pchnl->io_completions);
276		pchnl->cio_cs++;
277		pchnl->cio_reqs--;
278	}
279
280	spin_unlock_bh(&chnl_mgr_obj->chnl_mgr_lock);
281
282	return 0;
283}
284
285/*
286 *  ======== bridge_chnl_close ========
287 *  Purpose:
288 *      Ensures all pending I/O on this channel is cancelled, discards all
289 *      queued I/O completion notifications, then frees the resources allocated
290 *      for this channel, and makes the corresponding logical channel id
291 *      available for subsequent use.
292 */
293int bridge_chnl_close(struct chnl_object *chnl_obj)
294{
295	int status;
296	struct chnl_object *pchnl = (struct chnl_object *)chnl_obj;
297
298	/* Check args: */
299	if (!pchnl)
300		return -EFAULT;
301	/* Cancel IO: this ensures no further IO requests or notifications */
302	status = bridge_chnl_cancel_io(chnl_obj);
303	if (status)
304		return status;
305	/* Invalidate channel object: Protects from CHNL_GetIOCompletion() */
306	/* Free the slot in the channel manager: */
307	pchnl->chnl_mgr_obj->channels[pchnl->chnl_id] = NULL;
308	spin_lock_bh(&pchnl->chnl_mgr_obj->chnl_mgr_lock);
309	pchnl->chnl_mgr_obj->open_channels -= 1;
310	spin_unlock_bh(&pchnl->chnl_mgr_obj->chnl_mgr_lock);
311	if (pchnl->ntfy_obj) {
312		ntfy_delete(pchnl->ntfy_obj);
313		kfree(pchnl->ntfy_obj);
314		pchnl->ntfy_obj = NULL;
315	}
316	/* Reset channel event: (NOTE: user_event freed in user context) */
317	if (pchnl->sync_event) {
318		sync_reset_event(pchnl->sync_event);
319		kfree(pchnl->sync_event);
320		pchnl->sync_event = NULL;
321	}
322	/* Free I/O request and I/O completion queues: */
323	free_chirp_list(&pchnl->io_completions);
324	pchnl->cio_cs = 0;
325
326	free_chirp_list(&pchnl->io_requests);
327	pchnl->cio_reqs = 0;
328
329	free_chirp_list(&pchnl->free_packets_list);
330
331	/* Release channel object. */
332	kfree(pchnl);
333
334	return status;
335}
336
337/*
338 *  ======== bridge_chnl_create ========
339 *      Create a channel manager object, responsible for opening new channels
340 *      and closing old ones for a given board.
341 */
342int bridge_chnl_create(struct chnl_mgr **channel_mgr,
343			      struct dev_object *hdev_obj,
344			      const struct chnl_mgrattrs *mgr_attrts)
345{
346	int status = 0;
347	struct chnl_mgr *chnl_mgr_obj = NULL;
348	u8 max_channels;
349
350	/* Allocate channel manager object */
351	chnl_mgr_obj = kzalloc(sizeof(struct chnl_mgr), GFP_KERNEL);
352	if (chnl_mgr_obj) {
353		/*
354		 * The max_channels attr must equal the # of supported chnls for
355		 * each transport(# chnls for PCPY = DDMA = ZCPY): i.e.
356		 *      mgr_attrts->max_channels = CHNL_MAXCHANNELS =
357		 *                       DDMA_MAXDDMACHNLS = DDMA_MAXZCPYCHNLS.
358		 */
359		max_channels = CHNL_MAXCHANNELS + CHNL_MAXCHANNELS * CHNL_PCPY;
360		/* Create array of channels */
361		chnl_mgr_obj->channels = kzalloc(sizeof(struct chnl_object *)
362						* max_channels, GFP_KERNEL);
363		if (chnl_mgr_obj->channels) {
364			/* Initialize chnl_mgr object */
365			chnl_mgr_obj->type = CHNL_TYPESM;
366			chnl_mgr_obj->word_size = mgr_attrts->word_size;
367			/* Total # chnls supported */
368			chnl_mgr_obj->max_channels = max_channels;
369			chnl_mgr_obj->open_channels = 0;
370			chnl_mgr_obj->output_mask = 0;
371			chnl_mgr_obj->last_output = 0;
372			chnl_mgr_obj->dev_obj = hdev_obj;
373			spin_lock_init(&chnl_mgr_obj->chnl_mgr_lock);
374		} else {
375			status = -ENOMEM;
376		}
377	} else {
378		status = -ENOMEM;
379	}
380
381	if (status) {
382		bridge_chnl_destroy(chnl_mgr_obj);
383		*channel_mgr = NULL;
384	} else {
385		/* Return channel manager object to caller... */
386		*channel_mgr = chnl_mgr_obj;
387	}
388	return status;
389}
390
391/*
392 *  ======== bridge_chnl_destroy ========
393 *  Purpose:
394 *      Close all open channels, and destroy the channel manager.
395 */
396int bridge_chnl_destroy(struct chnl_mgr *hchnl_mgr)
397{
398	int status = 0;
399	struct chnl_mgr *chnl_mgr_obj = hchnl_mgr;
400	u32 chnl_id;
401
402	if (hchnl_mgr) {
403		/* Close all open channels: */
404		for (chnl_id = 0; chnl_id < chnl_mgr_obj->max_channels;
405		     chnl_id++) {
406			status =
407			    bridge_chnl_close(chnl_mgr_obj->channels
408					      [chnl_id]);
409			if (status)
410				dev_dbg(bridge, "%s: Error status 0x%x\n",
411					__func__, status);
412		}
413
414		/* Free channel manager object: */
415		kfree(chnl_mgr_obj->channels);
416
417		/* Set hchnl_mgr to NULL in device object. */
418		dev_set_chnl_mgr(chnl_mgr_obj->dev_obj, NULL);
419		/* Free this Chnl Mgr object: */
420		kfree(hchnl_mgr);
421	} else {
422		status = -EFAULT;
423	}
424	return status;
425}
426
427/*
428 *  ======== bridge_chnl_flush_io ========
429 *  purpose:
430 *      Flushes all the outstanding data requests on a channel.
431 */
432int bridge_chnl_flush_io(struct chnl_object *chnl_obj, u32 timeout)
433{
434	int status = 0;
435	struct chnl_object *pchnl = (struct chnl_object *)chnl_obj;
436	s8 chnl_mode = -1;
437	struct chnl_mgr *chnl_mgr_obj;
438	struct chnl_ioc chnl_ioc_obj;
439	/* Check args: */
440	if (pchnl) {
441		if ((timeout == CHNL_IOCNOWAIT)
442		    && CHNL_IS_OUTPUT(pchnl->chnl_mode)) {
443			status = -EINVAL;
444		} else {
445			chnl_mode = pchnl->chnl_mode;
446			chnl_mgr_obj = pchnl->chnl_mgr_obj;
447		}
448	} else {
449		status = -EFAULT;
450	}
451	if (!status) {
452		/* Note: Currently, if another thread continues to add IO
453		 * requests to this channel, this function will continue to
454		 * flush all such queued IO requests. */
455		if (CHNL_IS_OUTPUT(chnl_mode)
456		    && (pchnl->chnl_type == CHNL_PCPY)) {
457			/* Wait for IO completions, up to the specified
458			 * timeout: */
459			while (!list_empty(&pchnl->io_requests) && !status) {
460				status = bridge_chnl_get_ioc(chnl_obj,
461						timeout, &chnl_ioc_obj);
462				if (status)
463					continue;
464
465				if (chnl_ioc_obj.status & CHNL_IOCSTATTIMEOUT)
466					status = -ETIMEDOUT;
467
468			}
469		} else {
470			status = bridge_chnl_cancel_io(chnl_obj);
471			/* Now, leave the channel in the ready state: */
472			pchnl->state &= ~CHNL_STATECANCEL;
473		}
474	}
475	return status;
476}
477
478/*
479 *  ======== bridge_chnl_get_info ========
480 *  Purpose:
481 *      Retrieve information related to a channel.
482 */
483int bridge_chnl_get_info(struct chnl_object *chnl_obj,
484			     struct chnl_info *channel_info)
485{
486	int status = 0;
487	struct chnl_object *pchnl = (struct chnl_object *)chnl_obj;
488	if (channel_info != NULL) {
489		if (pchnl) {
490			/* Return the requested information: */
491			channel_info->chnl_mgr = pchnl->chnl_mgr_obj;
492			channel_info->event_obj = pchnl->user_event;
493			channel_info->cnhl_id = pchnl->chnl_id;
494			channel_info->mode = pchnl->chnl_mode;
495			channel_info->bytes_tx = pchnl->bytes_moved;
496			channel_info->process = pchnl->process;
497			channel_info->sync_event = pchnl->sync_event;
498			channel_info->cio_cs = pchnl->cio_cs;
499			channel_info->cio_reqs = pchnl->cio_reqs;
500			channel_info->state = pchnl->state;
501		} else {
502			status = -EFAULT;
503		}
504	} else {
505		status = -EFAULT;
506	}
507	return status;
508}
509
510/*
511 *  ======== bridge_chnl_get_ioc ========
512 *      Optionally wait for I/O completion on a channel.  Dequeue an I/O
513 *      completion record, which contains information about the completed
514 *      I/O request.
515 *      Note: Ensures Channel Invariant (see notes above).
516 */
517int bridge_chnl_get_ioc(struct chnl_object *chnl_obj, u32 timeout,
518			    struct chnl_ioc *chan_ioc)
519{
520	int status = 0;
521	struct chnl_object *pchnl = (struct chnl_object *)chnl_obj;
522	struct chnl_irp *chnl_packet_obj;
523	int stat_sync;
524	bool dequeue_ioc = true;
525	struct chnl_ioc ioc = { NULL, 0, 0, 0, 0 };
526	u8 *host_sys_buf = NULL;
527	struct bridge_dev_context *dev_ctxt;
528	struct dev_object *dev_obj;
529
530	/* Check args: */
531	if (!chan_ioc || !pchnl) {
532		status = -EFAULT;
533	} else if (timeout == CHNL_IOCNOWAIT) {
534		if (list_empty(&pchnl->io_completions))
535			status = -EREMOTEIO;
536
537	}
538
539	dev_obj = dev_get_first();
540	dev_get_bridge_context(dev_obj, &dev_ctxt);
541	if (!dev_ctxt)
542		status = -EFAULT;
543
544	if (status)
545		goto func_end;
546
547	ioc.status = CHNL_IOCSTATCOMPLETE;
548	if (timeout !=
549	    CHNL_IOCNOWAIT && list_empty(&pchnl->io_completions)) {
550		if (timeout == CHNL_IOCINFINITE)
551			timeout = SYNC_INFINITE;
552
553		stat_sync = sync_wait_on_event(pchnl->sync_event, timeout);
554		if (stat_sync == -ETIME) {
555			/* No response from DSP */
556			ioc.status |= CHNL_IOCSTATTIMEOUT;
557			dequeue_ioc = false;
558		} else if (stat_sync == -EPERM) {
559			/* This can occur when the user mode thread is
560			 * aborted (^C), or when _VWIN32_WaitSingleObject()
561			 * fails due to unknown causes. */
562			/* Even though Wait failed, there may be something in
563			 * the Q: */
564			if (list_empty(&pchnl->io_completions)) {
565				ioc.status |= CHNL_IOCSTATCANCEL;
566				dequeue_ioc = false;
567			}
568		}
569	}
570	/* See comment in AddIOReq */
571	spin_lock_bh(&pchnl->chnl_mgr_obj->chnl_mgr_lock);
572	omap_mbox_disable_irq(dev_ctxt->mbox, IRQ_RX);
573	if (dequeue_ioc) {
574		/* Dequeue IOC and set chan_ioc; */
575		chnl_packet_obj = list_first_entry(&pchnl->io_completions,
576				struct chnl_irp, link);
577		list_del(&chnl_packet_obj->link);
578		/* Update chan_ioc from channel state and chirp: */
579		pchnl->cio_cs--;
580		/*
581		 * If this is a zero-copy channel, then set IOC's pbuf
582		 * to the DSP's address. This DSP address will get
583		 * translated to user's virtual addr later.
584		 */
585		host_sys_buf = chnl_packet_obj->host_sys_buf;
586		ioc.buf = chnl_packet_obj->host_user_buf;
587		ioc.byte_size = chnl_packet_obj->byte_size;
588		ioc.buf_size = chnl_packet_obj->buf_size;
589		ioc.arg = chnl_packet_obj->arg;
590		ioc.status |= chnl_packet_obj->status;
591		/* Place the used chirp on the free list: */
592		list_add_tail(&chnl_packet_obj->link,
593				&pchnl->free_packets_list);
594	} else {
595		ioc.buf = NULL;
596		ioc.byte_size = 0;
597		ioc.arg = 0;
598		ioc.buf_size = 0;
599	}
600	/* Ensure invariant: If any IOC's are queued for this channel... */
601	if (!list_empty(&pchnl->io_completions)) {
602		/*  Since DSPStream_Reclaim() does not take a timeout
603		 *  parameter, we pass the stream's timeout value to
604		 *  bridge_chnl_get_ioc. We cannot determine whether or not
605		 *  we have waited in User mode. Since the stream's timeout
606		 *  value may be non-zero, we still have to set the event.
607		 *  Therefore, this optimization is taken out.
608		 *
609		 *  if (timeout == CHNL_IOCNOWAIT) {
610		 *    ... ensure event is set..
611		 *      sync_set_event(pchnl->sync_event);
612		 *  } */
613		sync_set_event(pchnl->sync_event);
614	} else {
615		/* else, if list is empty, ensure event is reset. */
616		sync_reset_event(pchnl->sync_event);
617	}
618	omap_mbox_enable_irq(dev_ctxt->mbox, IRQ_RX);
619	spin_unlock_bh(&pchnl->chnl_mgr_obj->chnl_mgr_lock);
620	if (dequeue_ioc
621	    && (pchnl->chnl_type == CHNL_PCPY && pchnl->chnl_id > 1)) {
622		if (!(ioc.buf < (void *)USERMODE_ADDR))
623			goto func_cont;
624
625		/* If the addr is in user mode, then copy it */
626		if (!host_sys_buf || !ioc.buf) {
627			status = -EFAULT;
628			goto func_cont;
629		}
630		if (!CHNL_IS_INPUT(pchnl->chnl_mode))
631			goto func_cont1;
632
633		/*host_user_buf */
634		status = copy_to_user(ioc.buf, host_sys_buf, ioc.byte_size);
635		if (status) {
636			if (current->flags & PF_EXITING)
637				status = 0;
638		}
639		if (status)
640			status = -EFAULT;
641func_cont1:
642		kfree(host_sys_buf);
643	}
644func_cont:
645	/* Update User's IOC block: */
646	*chan_ioc = ioc;
647func_end:
648	return status;
649}
650
651/*
652 *  ======== bridge_chnl_get_mgr_info ========
653 *      Retrieve information related to the channel manager.
654 */
655int bridge_chnl_get_mgr_info(struct chnl_mgr *hchnl_mgr, u32 ch_id,
656				 struct chnl_mgrinfo *mgr_info)
657{
658	struct chnl_mgr *chnl_mgr_obj = (struct chnl_mgr *)hchnl_mgr;
659
660	if (!mgr_info || !hchnl_mgr)
661		return -EFAULT;
662
663	if (ch_id > CHNL_MAXCHANNELS)
664		return -ECHRNG;
665
666	/* Return the requested information: */
667	mgr_info->chnl_obj = chnl_mgr_obj->channels[ch_id];
668	mgr_info->open_channels = chnl_mgr_obj->open_channels;
669	mgr_info->type = chnl_mgr_obj->type;
670	/* total # of chnls */
671	mgr_info->max_channels = chnl_mgr_obj->max_channels;
672
673	return 0;
674}
675
676/*
677 *  ======== bridge_chnl_idle ========
678 *      Idles a particular channel.
679 */
680int bridge_chnl_idle(struct chnl_object *chnl_obj, u32 timeout,
681			    bool flush_data)
682{
683	s8 chnl_mode;
684	struct chnl_mgr *chnl_mgr_obj;
685	int status = 0;
686
687	chnl_mode = chnl_obj->chnl_mode;
688	chnl_mgr_obj = chnl_obj->chnl_mgr_obj;
689
690	if (CHNL_IS_OUTPUT(chnl_mode) && !flush_data) {
691		/* Wait for IO completions, up to the specified timeout: */
692		status = bridge_chnl_flush_io(chnl_obj, timeout);
693	} else {
694		status = bridge_chnl_cancel_io(chnl_obj);
695
696		/* Reset the byte count and put channel back in ready state. */
697		chnl_obj->bytes_moved = 0;
698		chnl_obj->state &= ~CHNL_STATECANCEL;
699	}
700
701	return status;
702}
703
704/*
705 *  ======== bridge_chnl_open ========
706 *      Open a new half-duplex channel to the DSP board.
707 */
708int bridge_chnl_open(struct chnl_object **chnl,
709			    struct chnl_mgr *hchnl_mgr, s8 chnl_mode,
710			    u32 ch_id, const struct chnl_attr *pattrs)
711{
712	int status = 0;
713	struct chnl_mgr *chnl_mgr_obj = hchnl_mgr;
714	struct chnl_object *pchnl = NULL;
715	struct sync_object *sync_event = NULL;
716
717	*chnl = NULL;
718
719	/* Validate Args: */
720	if (!pattrs->uio_reqs)
721		return -EINVAL;
722
723	if (!hchnl_mgr)
724		return -EFAULT;
725
726	if (ch_id != CHNL_PICKFREE) {
727		if (ch_id >= chnl_mgr_obj->max_channels)
728			return -ECHRNG;
729		if (chnl_mgr_obj->channels[ch_id] != NULL)
730			return -EALREADY;
731	} else {
732		/* Check for free channel */
733		status = search_free_channel(chnl_mgr_obj, &ch_id);
734		if (status)
735			return status;
736	}
737
738
739	/* Create channel object: */
740	pchnl = kzalloc(sizeof(struct chnl_object), GFP_KERNEL);
741	if (!pchnl)
742		return -ENOMEM;
743
744	/* Protect queues from io_dpc: */
745	pchnl->state = CHNL_STATECANCEL;
746
747	/* Allocate initial IOR and IOC queues: */
748	status = create_chirp_list(&pchnl->free_packets_list,
749			pattrs->uio_reqs);
750	if (status)
751		goto out_err;
752
753	INIT_LIST_HEAD(&pchnl->io_requests);
754	INIT_LIST_HEAD(&pchnl->io_completions);
755
756	pchnl->chnl_packets = pattrs->uio_reqs;
757	pchnl->cio_cs = 0;
758	pchnl->cio_reqs = 0;
759
760	sync_event = kzalloc(sizeof(struct sync_object), GFP_KERNEL);
761	if (!sync_event) {
762		status = -ENOMEM;
763		goto out_err;
764	}
765	sync_init_event(sync_event);
766
767	pchnl->ntfy_obj = kmalloc(sizeof(struct ntfy_object), GFP_KERNEL);
768	if (!pchnl->ntfy_obj) {
769		status = -ENOMEM;
770		goto out_err;
771	}
772	ntfy_init(pchnl->ntfy_obj);
773
774	/* Initialize CHNL object fields: */
775	pchnl->chnl_mgr_obj = chnl_mgr_obj;
776	pchnl->chnl_id = ch_id;
777	pchnl->chnl_mode = chnl_mode;
778	pchnl->user_event = sync_event;
779	pchnl->sync_event = sync_event;
780	/* Get the process handle */
781	pchnl->process = current->tgid;
782	pchnl->cb_arg = 0;
783	pchnl->bytes_moved = 0;
784	/* Default to proc-copy */
785	pchnl->chnl_type = CHNL_PCPY;
786
787	/* Insert channel object in channel manager: */
788	chnl_mgr_obj->channels[pchnl->chnl_id] = pchnl;
789	spin_lock_bh(&chnl_mgr_obj->chnl_mgr_lock);
790	chnl_mgr_obj->open_channels++;
791	spin_unlock_bh(&chnl_mgr_obj->chnl_mgr_lock);
792	/* Return result... */
793	pchnl->state = CHNL_STATEREADY;
794	*chnl = pchnl;
795
796	return status;
797
798out_err:
799	/* Free memory */
800	free_chirp_list(&pchnl->io_completions);
801	free_chirp_list(&pchnl->io_requests);
802	free_chirp_list(&pchnl->free_packets_list);
803
804	kfree(sync_event);
805
806	if (pchnl->ntfy_obj) {
807		ntfy_delete(pchnl->ntfy_obj);
808		kfree(pchnl->ntfy_obj);
809		pchnl->ntfy_obj = NULL;
810	}
811	kfree(pchnl);
812
813	return status;
814}
815
816/*
817 *  ======== bridge_chnl_register_notify ========
818 *      Registers for events on a particular channel.
819 */
820int bridge_chnl_register_notify(struct chnl_object *chnl_obj,
821				    u32 event_mask, u32 notify_type,
822				    struct dsp_notification *hnotification)
823{
824	int status = 0;
825
826
827	if (event_mask)
828		status = ntfy_register(chnl_obj->ntfy_obj, hnotification,
829						event_mask, notify_type);
830	else
831		status = ntfy_unregister(chnl_obj->ntfy_obj, hnotification);
832
833	return status;
834}
835
836/*
837 *  ======== create_chirp_list ========
838 *  Purpose:
839 *      Initialize a queue of channel I/O Request/Completion packets.
840 *  Parameters:
841 *      list:       Pointer to a list_head
842 *      chirps:     Number of Chirps to allocate.
843 *  Returns:
844 *      0 if successful, error code otherwise.
845 *  Requires:
846 *  Ensures:
847 */
848static int create_chirp_list(struct list_head *list, u32 chirps)
849{
850	struct chnl_irp *chirp;
851	u32 i;
852
853	INIT_LIST_HEAD(list);
854
855	/* Make N chirps and place on queue. */
856	for (i = 0; i < chirps; i++) {
857		chirp = kzalloc(sizeof(struct chnl_irp), GFP_KERNEL);
858		if (!chirp)
859			break;
860		list_add_tail(&chirp->link, list);
861	}
862
863	/* If we couldn't allocate all chirps, free those allocated: */
864	if (i != chirps) {
865		free_chirp_list(list);
866		return -ENOMEM;
867	}
868
869	return 0;
870}
871
872/*
873 *  ======== free_chirp_list ========
874 *  Purpose:
875 *      Free the queue of Chirps.
876 */
877static void free_chirp_list(struct list_head *chirp_list)
878{
879	struct chnl_irp *chirp, *tmp;
880
881	list_for_each_entry_safe(chirp, tmp, chirp_list, link) {
882		list_del(&chirp->link);
883		kfree(chirp);
884	}
885}
886
887/*
888 *  ======== search_free_channel ========
889 *      Search for a free channel slot in the array of channel pointers.
890 */
891static int search_free_channel(struct chnl_mgr *chnl_mgr_obj,
892				      u32 *chnl)
893{
894	int status = -ENOSR;
895	u32 i;
896
897	for (i = 0; i < chnl_mgr_obj->max_channels; i++) {
898		if (chnl_mgr_obj->channels[i] == NULL) {
899			status = 0;
900			*chnl = i;
901			break;
902		}
903	}
904
905	return status;
906}
907