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