xref: /illumos-gate/usr/src/uts/common/io/1394/targets/dcam1394/dcam_ring_buff.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * dcam_ring_buff.c
31  *
32  * dcam1394 driver.  Video frame ring buffer support.
33  */
34 
35 #include <sys/types.h>
36 #include <sys/kmem.h>
37 #include <sys/ddidmareq.h>
38 #include <sys/types.h>
39 #include <sys/inttypes.h>
40 #include <sys/tnf_probe.h>
41 #include <sys/cmn_err.h>
42 
43 #include <sys/1394/targets/dcam1394/dcam.h>
44 
45 /*
46  * ring_buff_create
47  *
48  *  - alloc ring_buff_t structure
49  *  - init ring_buff's num_buffs, buff_num_bytes, num_read_ptrs,
50  *        read_ptr_pos
51  *  - alloc (num buffs) entries in ring_buff's buff_info_array_p
52  *
53  *  - for each buff
54  *     - alloc DMA handle; store DMA handle in buff's buff_info_array_p
55  *     - alloc mem for DMA transfer; store base addr, data access handle
56  *           in buff's  buff_info_array_p entry
57  *     - bind alloc'ed mem to DMA handle; store assoc info in buff's
58  *           buff_info_array_p entry
59  */
60 ring_buff_t *
61 ring_buff_create(dcam_state_t *softc_p, size_t num_buffs,
62     size_t buff_num_bytes)
63 {
64 	buff_info_t *buff_info_p;
65 	size_t buff;
66 	int i, rc;
67 	ring_buff_t *ring_buff_p;
68 	size_t num_bytes;
69 
70 	num_bytes = sizeof (ring_buff_t);
71 
72 	ring_buff_p = (ring_buff_t *)kmem_alloc(num_bytes, KM_SLEEP);
73 
74 	ring_buff_p->num_buffs		= num_buffs;
75 	ring_buff_p->buff_num_bytes	= buff_num_bytes;
76 	ring_buff_p->write_ptr_pos	= 0;
77 	ring_buff_p->num_read_ptrs	= 0;
78 	ring_buff_p->read_ptr_incr_val	= 1;
79 
80 	for (i = 0; i < MAX_NUM_READ_PTRS; i++) {
81 		ring_buff_p->read_ptr_pos[i] = (size_t)-1;
82 	}
83 
84 	num_bytes = num_buffs * sizeof (buff_info_t);
85 
86 	ring_buff_p->buff_info_array_p =
87 	    (buff_info_t *)kmem_alloc(num_bytes, KM_SLEEP);
88 
89 	for (buff = 0; buff < num_buffs; buff++) {
90 
91 		buff_info_p = &(ring_buff_p->buff_info_array_p[buff]);
92 
93 		if ((ddi_dma_alloc_handle(
94 		    softc_p->dip,
95 		    &softc_p->attachinfo.dma_attr,
96 		    DDI_DMA_DONTWAIT,
97 		    NULL,
98 		    &(buff_info_p->dma_handle))) != DDI_SUCCESS) {
99 			ring_buff_free(softc_p, ring_buff_p);
100 			return (NULL);
101 		}
102 
103 		if (ddi_dma_mem_alloc(
104 		    buff_info_p->dma_handle,
105 		    buff_num_bytes,
106 		    &softc_p->attachinfo.acc_attr,
107 		    DDI_DMA_STREAMING,
108 		    DDI_DMA_DONTWAIT,
109 		    (caddr_t)NULL,
110 		    &(buff_info_p->kaddr_p),
111 		    &(buff_info_p->real_len),
112 		    &(buff_info_p->data_acc_handle)) != DDI_SUCCESS) {
113 			ring_buff_free(softc_p, ring_buff_p);
114 
115 			/*
116 			 *  Print a warning, this triggered the bug
117 			 *  report #4423667.  This call can fail if
118 			 *  the memory tests are being run in sunvts.
119 			 *  The fact is, this code is doing the right
120 			 *  thing.  I added an error message, so that
121 			 *  future occurrences can be dealt with directly.
122 			 *  This is not a bug... The vmem test in sunvts
123 			 *  can eat up all swap/virtual memory.
124 			 */
125 			cmn_err(CE_WARN,
126 			    "ddi_dma_mem_alloc() failed in ring_buff_create(),"\
127 			    " insufficient memory resources.\n");
128 			return (NULL);
129 		}
130 
131 		rc = ddi_dma_addr_bind_handle(
132 		    buff_info_p->dma_handle,
133 		    (struct as *)NULL,
134 		    (caddr_t)buff_info_p->kaddr_p,
135 		    buff_info_p->real_len,
136 		    DDI_DMA_RDWR | DDI_DMA_STREAMING,
137 		    DDI_DMA_DONTWAIT,
138 		    NULL,
139 		    &buff_info_p->dma_cookie,
140 		    &buff_info_p->dma_cookie_count);
141 
142 		if (rc != DDI_DMA_MAPPED) {
143 			ring_buff_free(softc_p, ring_buff_p);
144 			return (NULL);
145 		}
146 	}
147 
148 	return (ring_buff_p);
149 }
150 
151 
152 /*
153  * ring_buff_free
154  */
155 void
156 ring_buff_free(dcam_state_t *softc_p, ring_buff_t  *ring_buff_p)
157 {
158 	buff_info_t *buff_info_p;
159 	int i;
160 
161 	if (ring_buff_p == NULL) {
162 		softc_p->ring_buff_p = NULL;
163 		return;
164 	}
165 
166 	if (ring_buff_p->buff_info_array_p != NULL) {
167 		for (i = 0; i < ring_buff_p->num_buffs; i++) {
168 
169 			buff_info_p = &(ring_buff_p->buff_info_array_p[i]);
170 
171 			(void) ddi_dma_unbind_handle(buff_info_p->dma_handle);
172 			ddi_dma_mem_free(&buff_info_p->data_acc_handle);
173 			ddi_dma_free_handle(&buff_info_p->dma_handle);
174 		}
175 
176 		kmem_free(ring_buff_p->buff_info_array_p,
177 		    ring_buff_p->num_buffs * sizeof (buff_info_t));
178 	}
179 
180 	kmem_free(ring_buff_p, sizeof (ring_buff_t));
181 
182 	softc_p->ring_buff_p = NULL;
183 }
184 
185 
186 /*
187  * ring_buff_read_ptr_add
188  */
189 int
190 ring_buff_read_ptr_add(ring_buff_t *ring_buff_p)
191 {
192 	int i;
193 	int read_ptr_id;
194 
195 	read_ptr_id = -1;
196 
197 	for (i = 0; i < MAX_NUM_READ_PTRS; i++) {
198 
199 		if (ring_buff_p->read_ptr_pos[i] == -1) {
200 			ring_buff_p->read_ptr_pos[i] = 0;
201 			read_ptr_id = i;
202 			break;
203 		}
204 	}
205 
206 	return (read_ptr_id);
207 }
208 
209 
210 /*
211  * ring_buff_read_ptr_remove
212  */
213 int
214 ring_buff_read_ptr_remove(ring_buff_t *ring_buff_p, int read_ptr_id)
215 {
216 	ring_buff_p->read_ptr_pos[read_ptr_id] = (size_t)-1;
217 
218 	return (0);
219 }
220 
221 
222 /*
223  * ring_buff_read_ptr_buff_get
224  *
225  * Return pointer to buffer that a read pointer associated with the
226  * ring buffer is pointing to.
227  */
228 buff_info_t *
229 ring_buff_read_ptr_buff_get(ring_buff_t *ring_buff_p, int read_ptr_id)
230 {
231 	size_t		read_ptr_pos;
232 	buff_info_t	*buff_info_p;
233 
234 	read_ptr_pos = ring_buff_p->read_ptr_pos[read_ptr_id];
235 	buff_info_p  = &(ring_buff_p->buff_info_array_p[read_ptr_pos]);
236 
237 	return (buff_info_p);
238 }
239 
240 
241 /*
242  * ring_buff_read_ptr_pos_get
243  */
244 size_t
245 ring_buff_read_ptr_pos_get(ring_buff_t *ring_buff_p, int read_ptr_id)
246 {
247 	return (ring_buff_p->read_ptr_pos[read_ptr_id]);
248 }
249 
250 
251 /*
252  * ring_buff_read_ptr_incr
253  */
254 void
255 ring_buff_read_ptr_incr(ring_buff_t *ring_buff_p, int read_ptr_id)
256 {
257 	size_t read_ptr_pos;
258 #if defined(_ADDL_RING_BUFF_CHECK)
259 	size_t lrp, lwp; /* linear read, write positions */
260 #endif	/* _ADDL_RING_BUFFER_CHECK */
261 
262 	/*
263 	 * increment the read pointer based on read_ptr_incr_val
264 	 * which can vary from 1 to 10
265 	 */
266 
267 	/* get current read pointer pos */
268 	read_ptr_pos = ring_buff_p->read_ptr_pos[read_ptr_id];
269 
270 	ring_buff_p->read_ptr_pos[read_ptr_id] =
271 	    (read_ptr_pos + 1) % ring_buff_p->num_buffs;
272 
273 #if defined(_ADDL_RING_BUFF_CHECK)
274 	if ((read_ptr_pos == 0) && (ring_buff_p->write_ptr_pos == 0)) {
275 		return;
276 	}
277 
278 	if (read_ptr_pos < ring_buff_p->write_ptr_pos) {
279 
280 		/* calculate new read pointer position */
281 		if ((read_ptr_pos + ring_buff_p->read_ptr_incr_val) <
282 		    ring_buff_p->write_ptr_pos) {
283 
284 			/* there is still some valid frame data */
285 			ring_buff_p->read_ptr_pos[read_ptr_id] =
286 			    (read_ptr_pos +
287 			    ring_buff_p->read_ptr_incr_val) %
288 			    ring_buff_p->num_buffs;
289 		} else {
290 			/*
291 			 * we have skipped beyond available frame
292 			 * data, so the buffer is empty
293 			 */
294 			ring_buff_p->read_ptr_pos[read_ptr_id] =
295 			    ring_buff_p->write_ptr_pos;
296 		}
297 	} else {
298 		/*
299 		 * since read pointer is ahead of write pointer,
300 		 * it becomes easier to check for new read
301 		 * pointer position if we pretend that our data
302 		 * buffer is linear instead of circular
303 		 */
304 
305 		lrp = read_ptr_pos + ring_buff_p->read_ptr_incr_val;
306 		lwp = ring_buff_p->num_buffs +
307 		    ring_buff_p->write_ptr_pos;
308 
309 		if (lrp < lwp) {
310 			/* there is still some valid frame data */
311 			ring_buff_p->read_ptr_pos[read_ptr_id] =
312 			    (read_ptr_pos +
313 			    ring_buff_p->read_ptr_incr_val) %
314 			    ring_buff_p->num_buffs;
315 		} else {
316 			/*
317 			 * we have skipped beyond available
318 			 * frame  data, so the buffer is empty
319 			 */
320 			ring_buff_p->read_ptr_pos[read_ptr_id] =
321 			    ring_buff_p->write_ptr_pos;
322 		}
323 	}
324 #endif	/* _ADDL_RING_BUFF_CHECK */
325 }
326 
327 
328 /*
329  * ring_buff_write_ptr_pos_get
330  */
331 size_t
332 ring_buff_write_ptr_pos_get(ring_buff_t *ring_buff_p)
333 {
334 	return (ring_buff_p->write_ptr_pos);
335 }
336 
337 
338 /*
339  * ring_buff_write_ptr_incr
340  */
341 void
342 ring_buff_write_ptr_incr(ring_buff_t *ring_buff_p)
343 {
344 	size_t write_ptr_pos;
345 
346 	write_ptr_pos = ring_buff_p->write_ptr_pos;
347 
348 	ring_buff_p->write_ptr_pos =
349 	    ((write_ptr_pos + 1) % ring_buff_p->num_buffs);
350 }
351