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