Deleted Added
full compact
channel.c (74763) channel.c (74797)
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3 * Portions Copyright by Luigi Rizzo - 1997-99
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3 * Portions Copyright by Luigi Rizzo - 1997-99
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/dev/sound/pcm/channel.c 74763 2001-03-24 23:10:29Z cg $
27 * $FreeBSD: head/sys/dev/sound/pcm/channel.c 74797 2001-03-25 21:43:24Z cg $
28 */
29
30#include <dev/sound/pcm/sound.h>
31
32#include "feeder_if.h"
33
34#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
35#define DMA_ALIGN_THRESHOLD 4
36#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1))
37
38#define MIN(x, y) (((x) < (y))? (x) : (y))
39#define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
40
41/*
42#define DEB(x) x
43*/
44
45static int chn_buildfeeder(struct pcm_channel *c);
46
47static void
48chn_lockinit(struct pcm_channel *c)
49{
50 c->lock = snd_mtxcreate(c->name);
51}
52
53static void
54chn_lockdestroy(struct pcm_channel *c)
55{
56 snd_mtxfree(c->lock);
57}
58
59static int
60chn_polltrigger(struct pcm_channel *c)
61{
62 struct snd_dbuf *bs = c->bufsoft;
63 unsigned amt, lim;
64
65 CHN_LOCKASSERT(c);
66 if (c->flags & CHN_F_MAPPED) {
67 if (sndbuf_getprevblocks(bs) == 0)
68 return 1;
28 */
29
30#include <dev/sound/pcm/sound.h>
31
32#include "feeder_if.h"
33
34#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
35#define DMA_ALIGN_THRESHOLD 4
36#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1))
37
38#define MIN(x, y) (((x) < (y))? (x) : (y))
39#define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
40
41/*
42#define DEB(x) x
43*/
44
45static int chn_buildfeeder(struct pcm_channel *c);
46
47static void
48chn_lockinit(struct pcm_channel *c)
49{
50 c->lock = snd_mtxcreate(c->name);
51}
52
53static void
54chn_lockdestroy(struct pcm_channel *c)
55{
56 snd_mtxfree(c->lock);
57}
58
59static int
60chn_polltrigger(struct pcm_channel *c)
61{
62 struct snd_dbuf *bs = c->bufsoft;
63 unsigned amt, lim;
64
65 CHN_LOCKASSERT(c);
66 if (c->flags & CHN_F_MAPPED) {
67 if (sndbuf_getprevblocks(bs) == 0)
68 return 1;
69 else
69 else
70 return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0;
71 } else {
72 amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
73 lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
74 lim = 1;
75 return (amt >= lim)? 1 : 0;
76 }
77 return 0;
78}
79
80static int
81chn_pollreset(struct pcm_channel *c)
82{
83 struct snd_dbuf *bs = c->bufsoft;
84
85 CHN_LOCKASSERT(c);
86 sndbuf_updateprevtotal(bs);
87 return 1;
88}
89
90static void
91chn_wakeup(struct pcm_channel *c)
92{
93 struct snd_dbuf *bs = c->bufsoft;
94
95 CHN_LOCKASSERT(c);
96 if (sndbuf_getsel(bs)->si_pid && chn_polltrigger(c))
97 selwakeup(sndbuf_getsel(bs));
98 wakeup(bs);
99}
100
101static int
102chn_sleep(struct pcm_channel *c, char *str, int timeout)
103{
104 struct snd_dbuf *bs = c->bufsoft;
105 int ret;
106
107 CHN_LOCKASSERT(c);
108 ret = msleep(bs, c->lock, PRIBIO | PCATCH, str, timeout);
109
110 return ret;
111}
112
113/*
114 * chn_dmaupdate() tracks the status of a dma transfer,
115 * updating pointers. It must be called at spltty().
116 */
117
118static unsigned int
119chn_dmaupdate(struct pcm_channel *c)
120{
121 struct snd_dbuf *b = c->bufhard;
122 unsigned int delta, old, hwptr, amt;
123
124 CHN_LOCKASSERT(c);
125 old = sndbuf_gethwptr(b);
126 hwptr = chn_getptr(c);
127 delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
128 sndbuf_sethwptr(b, hwptr);
129
130 DEB(
131 if (delta >= ((sndbuf_getsize(b) * 15) / 16)) {
132 if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING)))
133 device_printf(c->parent->dev, "hwptr went backwards %d -> %d\n", old, hwptr);
134 }
135 );
136
137 if (c->direction == PCMDIR_PLAY) {
138 amt = MIN(delta, sndbuf_getready(b));
139 if (amt > 0)
140 sndbuf_dispose(b, NULL, amt);
141 } else {
142 amt = MIN(delta, sndbuf_getfree(b));
143 if (amt > 0)
144 sndbuf_acquire(b, NULL, amt);
145 }
146
147 return delta;
148}
149
150void
151chn_wrupdate(struct pcm_channel *c)
152{
153 int ret;
154
155 CHN_LOCKASSERT(c);
156 KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
157
158 if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED))
159 return;
160 chn_dmaupdate(c);
161 ret = chn_wrfeed(c);
162 /* tell the driver we've updated the primary buffer */
163 chn_trigger(c, PCMTRIG_EMLDMAWR);
164 DEB(if (ret)
165 printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);)
166
167}
168
169static int irqc = 0;
170
171int
172chn_wrfeed(struct pcm_channel *c)
173{
174 struct snd_dbuf *b = c->bufhard;
175 struct snd_dbuf *bs = c->bufsoft;
176 unsigned int ret, amt;
177
178 CHN_LOCKASSERT(c);
179 DEB(
180 if (c->flags & CHN_F_CLOSING) {
181 sndbuf_dump(b, "b", 0x02);
182 sndbuf_dump(bs, "bs", 0x02);
183 })
184
185 amt = sndbuf_getfree(b);
186 ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
187 if (ret == 0)
188 chn_wakeup(c);
189/*
190 if (!(irqc & 63) || (ret != 0))
191 sndbuf_dump(b, "b:wrfeed", 0x03);
192*/
193 return ret;
194}
195
196static void
197chn_wrintr(struct pcm_channel *c)
198{
199 int ret;
200
201 CHN_LOCKASSERT(c);
202 irqc++;
203 /* update pointers in primary buffer */
70 return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0;
71 } else {
72 amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
73 lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
74 lim = 1;
75 return (amt >= lim)? 1 : 0;
76 }
77 return 0;
78}
79
80static int
81chn_pollreset(struct pcm_channel *c)
82{
83 struct snd_dbuf *bs = c->bufsoft;
84
85 CHN_LOCKASSERT(c);
86 sndbuf_updateprevtotal(bs);
87 return 1;
88}
89
90static void
91chn_wakeup(struct pcm_channel *c)
92{
93 struct snd_dbuf *bs = c->bufsoft;
94
95 CHN_LOCKASSERT(c);
96 if (sndbuf_getsel(bs)->si_pid && chn_polltrigger(c))
97 selwakeup(sndbuf_getsel(bs));
98 wakeup(bs);
99}
100
101static int
102chn_sleep(struct pcm_channel *c, char *str, int timeout)
103{
104 struct snd_dbuf *bs = c->bufsoft;
105 int ret;
106
107 CHN_LOCKASSERT(c);
108 ret = msleep(bs, c->lock, PRIBIO | PCATCH, str, timeout);
109
110 return ret;
111}
112
113/*
114 * chn_dmaupdate() tracks the status of a dma transfer,
115 * updating pointers. It must be called at spltty().
116 */
117
118static unsigned int
119chn_dmaupdate(struct pcm_channel *c)
120{
121 struct snd_dbuf *b = c->bufhard;
122 unsigned int delta, old, hwptr, amt;
123
124 CHN_LOCKASSERT(c);
125 old = sndbuf_gethwptr(b);
126 hwptr = chn_getptr(c);
127 delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
128 sndbuf_sethwptr(b, hwptr);
129
130 DEB(
131 if (delta >= ((sndbuf_getsize(b) * 15) / 16)) {
132 if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING)))
133 device_printf(c->parent->dev, "hwptr went backwards %d -> %d\n", old, hwptr);
134 }
135 );
136
137 if (c->direction == PCMDIR_PLAY) {
138 amt = MIN(delta, sndbuf_getready(b));
139 if (amt > 0)
140 sndbuf_dispose(b, NULL, amt);
141 } else {
142 amt = MIN(delta, sndbuf_getfree(b));
143 if (amt > 0)
144 sndbuf_acquire(b, NULL, amt);
145 }
146
147 return delta;
148}
149
150void
151chn_wrupdate(struct pcm_channel *c)
152{
153 int ret;
154
155 CHN_LOCKASSERT(c);
156 KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
157
158 if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED))
159 return;
160 chn_dmaupdate(c);
161 ret = chn_wrfeed(c);
162 /* tell the driver we've updated the primary buffer */
163 chn_trigger(c, PCMTRIG_EMLDMAWR);
164 DEB(if (ret)
165 printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);)
166
167}
168
169static int irqc = 0;
170
171int
172chn_wrfeed(struct pcm_channel *c)
173{
174 struct snd_dbuf *b = c->bufhard;
175 struct snd_dbuf *bs = c->bufsoft;
176 unsigned int ret, amt;
177
178 CHN_LOCKASSERT(c);
179 DEB(
180 if (c->flags & CHN_F_CLOSING) {
181 sndbuf_dump(b, "b", 0x02);
182 sndbuf_dump(bs, "bs", 0x02);
183 })
184
185 amt = sndbuf_getfree(b);
186 ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
187 if (ret == 0)
188 chn_wakeup(c);
189/*
190 if (!(irqc & 63) || (ret != 0))
191 sndbuf_dump(b, "b:wrfeed", 0x03);
192*/
193 return ret;
194}
195
196static void
197chn_wrintr(struct pcm_channel *c)
198{
199 int ret;
200
201 CHN_LOCKASSERT(c);
202 irqc++;
203 /* update pointers in primary buffer */
204 chn_dmaupdate(c);
204 chn_dmaupdate(c);
205 /* ...and feed from secondary to primary */
206 ret = chn_wrfeed(c);
207 /* tell the driver we've updated the primary buffer */
205 /* ...and feed from secondary to primary */
206 ret = chn_wrfeed(c);
207 /* tell the driver we've updated the primary buffer */
208 chn_trigger(c, PCMTRIG_EMLDMAWR);
208 chn_trigger(c, PCMTRIG_EMLDMAWR);
209 DEB(if (ret)
210 printf("chn_wrintr: chn_wrfeed returned %d\n", ret);)
211}
212
213/*
214 * user write routine - uiomove data into secondary bufhard, trigger if necessary
215 * if blocking, sleep, rinse and repeat.
216 *
217 * called externally, so must handle locking
218 */
219
220int
221chn_write(struct pcm_channel *c, struct uio *buf)
222{
223 int ret, timeout, newsize, count, sz;
224 struct snd_dbuf *bs = c->bufsoft;
225
226 CHN_LOCKASSERT(c);
227 /*
228 * XXX Certain applications attempt to write larger size
229 * of pcm data than c->blocksize2nd without blocking,
230 * resulting partial write. Expand the block size so that
231 * the write operation avoids blocking.
232 */
233 if ((c->flags & CHN_F_NBIO) && buf->uio_resid > sndbuf_getblksz(bs)) {
234 DEB(device_printf(c->parent->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n",
235 buf->uio_resid, sndbuf_getblksz(bs)));
236 newsize = 16;
237 while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2))
238 newsize <<= 1;
239 chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize);
240 DEB(device_printf(c->parent->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs)));
241 }
242
243 ret = 0;
209 DEB(if (ret)
210 printf("chn_wrintr: chn_wrfeed returned %d\n", ret);)
211}
212
213/*
214 * user write routine - uiomove data into secondary bufhard, trigger if necessary
215 * if blocking, sleep, rinse and repeat.
216 *
217 * called externally, so must handle locking
218 */
219
220int
221chn_write(struct pcm_channel *c, struct uio *buf)
222{
223 int ret, timeout, newsize, count, sz;
224 struct snd_dbuf *bs = c->bufsoft;
225
226 CHN_LOCKASSERT(c);
227 /*
228 * XXX Certain applications attempt to write larger size
229 * of pcm data than c->blocksize2nd without blocking,
230 * resulting partial write. Expand the block size so that
231 * the write operation avoids blocking.
232 */
233 if ((c->flags & CHN_F_NBIO) && buf->uio_resid > sndbuf_getblksz(bs)) {
234 DEB(device_printf(c->parent->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n",
235 buf->uio_resid, sndbuf_getblksz(bs)));
236 newsize = 16;
237 while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2))
238 newsize <<= 1;
239 chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize);
240 DEB(device_printf(c->parent->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs)));
241 }
242
243 ret = 0;
244 count = hz;
244 count = hz;
245 while (!ret && (buf->uio_resid > 0) && (count > 0)) {
246 sz = sndbuf_getfree(bs);
247 if (sz == 0) {
248 if (c->flags & CHN_F_NBIO)
249 ret = EWOULDBLOCK;
250 else {
251 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
252 if (timeout < 1)
253 timeout = 1;
254 ret = chn_sleep(c, "pcmwr", timeout);
255 if (ret == EWOULDBLOCK) {
256 count -= timeout;
257 ret = 0;
258 } else if (ret == 0)
245 while (!ret && (buf->uio_resid > 0) && (count > 0)) {
246 sz = sndbuf_getfree(bs);
247 if (sz == 0) {
248 if (c->flags & CHN_F_NBIO)
249 ret = EWOULDBLOCK;
250 else {
251 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
252 if (timeout < 1)
253 timeout = 1;
254 ret = chn_sleep(c, "pcmwr", timeout);
255 if (ret == EWOULDBLOCK) {
256 count -= timeout;
257 ret = 0;
258 } else if (ret == 0)
259 count = hz;
260 }
259 count = hz;
260 }
261 } else {
262 sz = MIN(sz, buf->uio_resid);
263 KASSERT(sz > 0, ("confusion in chn_write"));
264 /* printf("sz: %d\n", sz); */
265 ret = sndbuf_uiomove(bs, buf, sz);
266 if (ret == 0 && !(c->flags & CHN_F_TRIGGERED))
267 chn_start(c, 0);
268 }
269 }
270 /* printf("ret: %d left: %d\n", ret, buf->uio_resid); */
271
272 if (count <= 0) {
261 } else {
262 sz = MIN(sz, buf->uio_resid);
263 KASSERT(sz > 0, ("confusion in chn_write"));
264 /* printf("sz: %d\n", sz); */
265 ret = sndbuf_uiomove(bs, buf, sz);
266 if (ret == 0 && !(c->flags & CHN_F_TRIGGERED))
267 chn_start(c, 0);
268 }
269 }
270 /* printf("ret: %d left: %d\n", ret, buf->uio_resid); */
271
272 if (count <= 0) {
273 c->flags |= CHN_F_DEAD;
274 device_printf(c->parent->dev, "play interrupt timeout, channel dead\n");
275 }
273 c->flags |= CHN_F_DEAD;
274 device_printf(c->parent->dev, "play interrupt timeout, channel dead\n");
275 }
276
277 return ret;
278}
279
280static int
281chn_rddump(struct pcm_channel *c, unsigned int cnt)
282{
283 struct snd_dbuf *b = c->bufhard;
284
285 CHN_LOCKASSERT(c);
286 sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
287 return sndbuf_dispose(b, NULL, cnt);
288}
289
290/*
291 * Feed new data from the read bufhard. Can be called in the bottom half.
292 * Hence must be called at spltty.
293 */
294int
295chn_rdfeed(struct pcm_channel *c)
296{
297 struct snd_dbuf *b = c->bufhard;
298 struct snd_dbuf *bs = c->bufsoft;
299 int ret;
300
301 CHN_LOCKASSERT(c);
302 DEB(
303 if (c->flags & CHN_F_CLOSING) {
304 sndbuf_dump(b, "b", 0x02);
305 sndbuf_dump(bs, "bs", 0x02);
306 })
307
308 ret = sndbuf_feed(b, bs, c, c->feeder, sndbuf_getblksz(b));
309
310 if (ret == 0)
311 chn_wakeup(c);
312
313 return ret;
314}
315
316void
317chn_rdupdate(struct pcm_channel *c)
318{
319 int ret;
320
321 CHN_LOCKASSERT(c);
322 KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
323
324 if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED))
325 return;
326 chn_trigger(c, PCMTRIG_EMLDMARD);
327 chn_dmaupdate(c);
328 ret = chn_rdfeed(c);
329 if (ret)
330 printf("chn_rdfeed: %d\n", ret);
331
332}
333
334/* read interrupt routine. Must be called with interrupts blocked. */
335static void
336chn_rdintr(struct pcm_channel *c)
337{
338 struct snd_dbuf *b = c->bufhard;
339 int ret;
340
341 CHN_LOCKASSERT(c);
342 /* tell the driver to update the primary bufhard if non-dma */
276
277 return ret;
278}
279
280static int
281chn_rddump(struct pcm_channel *c, unsigned int cnt)
282{
283 struct snd_dbuf *b = c->bufhard;
284
285 CHN_LOCKASSERT(c);
286 sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
287 return sndbuf_dispose(b, NULL, cnt);
288}
289
290/*
291 * Feed new data from the read bufhard. Can be called in the bottom half.
292 * Hence must be called at spltty.
293 */
294int
295chn_rdfeed(struct pcm_channel *c)
296{
297 struct snd_dbuf *b = c->bufhard;
298 struct snd_dbuf *bs = c->bufsoft;
299 int ret;
300
301 CHN_LOCKASSERT(c);
302 DEB(
303 if (c->flags & CHN_F_CLOSING) {
304 sndbuf_dump(b, "b", 0x02);
305 sndbuf_dump(bs, "bs", 0x02);
306 })
307
308 ret = sndbuf_feed(b, bs, c, c->feeder, sndbuf_getblksz(b));
309
310 if (ret == 0)
311 chn_wakeup(c);
312
313 return ret;
314}
315
316void
317chn_rdupdate(struct pcm_channel *c)
318{
319 int ret;
320
321 CHN_LOCKASSERT(c);
322 KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
323
324 if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED))
325 return;
326 chn_trigger(c, PCMTRIG_EMLDMARD);
327 chn_dmaupdate(c);
328 ret = chn_rdfeed(c);
329 if (ret)
330 printf("chn_rdfeed: %d\n", ret);
331
332}
333
334/* read interrupt routine. Must be called with interrupts blocked. */
335static void
336chn_rdintr(struct pcm_channel *c)
337{
338 struct snd_dbuf *b = c->bufhard;
339 int ret;
340
341 CHN_LOCKASSERT(c);
342 /* tell the driver to update the primary bufhard if non-dma */
343 chn_trigger(c, PCMTRIG_EMLDMARD);
343 chn_trigger(c, PCMTRIG_EMLDMARD);
344 /* update pointers in primary bufhard */
344 /* update pointers in primary bufhard */
345 chn_dmaupdate(c);
345 chn_dmaupdate(c);
346 /* ...and feed from primary to secondary */
347 ret = chn_rdfeed(c);
348 if (ret)
349 chn_rddump(c, sndbuf_getblksz(b));
350}
351
352/*
353 * user read routine - trigger if necessary, uiomove data from secondary bufhard
354 * if blocking, sleep, rinse and repeat.
355 *
356 * called externally, so must handle locking
357 */
358
359int
360chn_read(struct pcm_channel *c, struct uio *buf)
361{
362 int ret, timeout, sz, count;
363 struct snd_dbuf *bs = c->bufsoft;
364
365 CHN_LOCKASSERT(c);
366 if (!(c->flags & CHN_F_TRIGGERED))
367 chn_start(c, 0);
368
369 ret = 0;
346 /* ...and feed from primary to secondary */
347 ret = chn_rdfeed(c);
348 if (ret)
349 chn_rddump(c, sndbuf_getblksz(b));
350}
351
352/*
353 * user read routine - trigger if necessary, uiomove data from secondary bufhard
354 * if blocking, sleep, rinse and repeat.
355 *
356 * called externally, so must handle locking
357 */
358
359int
360chn_read(struct pcm_channel *c, struct uio *buf)
361{
362 int ret, timeout, sz, count;
363 struct snd_dbuf *bs = c->bufsoft;
364
365 CHN_LOCKASSERT(c);
366 if (!(c->flags & CHN_F_TRIGGERED))
367 chn_start(c, 0);
368
369 ret = 0;
370 count = hz;
370 count = hz;
371 while (!ret && (buf->uio_resid > 0) && (count > 0)) {
372 sz = MIN(buf->uio_resid, sndbuf_getblksz(bs));
373
374 if (sz <= sndbuf_getready(bs)) {
375 ret = sndbuf_uiomove(bs, buf, sz);
376 } else {
377 if (c->flags & CHN_F_NBIO)
378 ret = EWOULDBLOCK;
379 else {
380 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
381 if (timeout < 1)
382 timeout = 1;
383 CHN_UNLOCK(c);
384 ret = chn_sleep(c, "pcmrd", timeout);
385 CHN_LOCK(c);
386 if (ret == EWOULDBLOCK) {
387 count -= timeout;
388 ret = 0;
389 }
390 }
391 }
392 }
393
394 if (count <= 0) {
371 while (!ret && (buf->uio_resid > 0) && (count > 0)) {
372 sz = MIN(buf->uio_resid, sndbuf_getblksz(bs));
373
374 if (sz <= sndbuf_getready(bs)) {
375 ret = sndbuf_uiomove(bs, buf, sz);
376 } else {
377 if (c->flags & CHN_F_NBIO)
378 ret = EWOULDBLOCK;
379 else {
380 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
381 if (timeout < 1)
382 timeout = 1;
383 CHN_UNLOCK(c);
384 ret = chn_sleep(c, "pcmrd", timeout);
385 CHN_LOCK(c);
386 if (ret == EWOULDBLOCK) {
387 count -= timeout;
388 ret = 0;
389 }
390 }
391 }
392 }
393
394 if (count <= 0) {
395 c->flags |= CHN_F_DEAD;
396 device_printf(c->parent->dev, "record interrupt timeout, channel dead\n");
397 }
395 c->flags |= CHN_F_DEAD;
396 device_printf(c->parent->dev, "record interrupt timeout, channel dead\n");
397 }
398
399 return ret;
400}
401
402void
403chn_intr(struct pcm_channel *c)
404{
405 CHN_LOCK(c);
406 if (c->direction == PCMDIR_PLAY)
407 chn_wrintr(c);
408 else
409 chn_rdintr(c);
410 CHN_UNLOCK(c);
411}
412
413u_int32_t
414chn_start(struct pcm_channel *c, int force)
415{
416 u_int32_t i;
417 struct snd_dbuf *b = c->bufhard;
418 struct snd_dbuf *bs = c->bufsoft;
419
420 CHN_LOCKASSERT(c);
421 /* if we're running, or if we're prevented from triggering, bail */
422 if ((c->flags & CHN_F_TRIGGERED) || (c->flags & CHN_F_NOTRIGGER))
423 return EINVAL;
424
425 i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs);
426 if (force || (i >= sndbuf_getblksz(b))) {
427 c->flags |= CHN_F_TRIGGERED;
428 if (c->direction == PCMDIR_PLAY)
429 chn_wrfeed(c);
430 sndbuf_setrun(b, 1);
431 chn_trigger(c, PCMTRIG_START);
432 return 0;
433 }
434
435 return 0;
436}
437
438void
439chn_resetbuf(struct pcm_channel *c)
440{
441 struct snd_dbuf *b = c->bufhard;
442 struct snd_dbuf *bs = c->bufsoft;
443
444 c->blocks = 0;
445 sndbuf_reset(b);
446 sndbuf_reset(bs);
447}
448
449/*
450 * chn_sync waits until the space in the given channel goes above
451 * a threshold. The threshold is checked against fl or rl respectively.
452 * Assume that the condition can become true, do not check here...
453 */
454int
455chn_sync(struct pcm_channel *c, int threshold)
456{
457 u_long rdy;
458 int ret;
459 struct snd_dbuf *bs = c->bufsoft;
460
461 CHN_LOCKASSERT(c);
462 for (;;) {
463 chn_wrupdate(c);
464 rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
465 if (rdy <= threshold) {
466 ret = chn_sleep(c, "pcmsyn", 1);
467 if (ret == ERESTART || ret == EINTR) {
468 DEB(printf("chn_sync: tsleep returns %d\n", ret));
469 return -1;
470 }
471 } else
472 break;
473 }
474 return 0;
475}
476
477/* called externally, handle locking */
478int
479chn_poll(struct pcm_channel *c, int ev, struct proc *p)
480{
481 struct snd_dbuf *bs = c->bufsoft;
482 int ret;
483
484 CHN_LOCK(c);
485 if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED))
398
399 return ret;
400}
401
402void
403chn_intr(struct pcm_channel *c)
404{
405 CHN_LOCK(c);
406 if (c->direction == PCMDIR_PLAY)
407 chn_wrintr(c);
408 else
409 chn_rdintr(c);
410 CHN_UNLOCK(c);
411}
412
413u_int32_t
414chn_start(struct pcm_channel *c, int force)
415{
416 u_int32_t i;
417 struct snd_dbuf *b = c->bufhard;
418 struct snd_dbuf *bs = c->bufsoft;
419
420 CHN_LOCKASSERT(c);
421 /* if we're running, or if we're prevented from triggering, bail */
422 if ((c->flags & CHN_F_TRIGGERED) || (c->flags & CHN_F_NOTRIGGER))
423 return EINVAL;
424
425 i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs);
426 if (force || (i >= sndbuf_getblksz(b))) {
427 c->flags |= CHN_F_TRIGGERED;
428 if (c->direction == PCMDIR_PLAY)
429 chn_wrfeed(c);
430 sndbuf_setrun(b, 1);
431 chn_trigger(c, PCMTRIG_START);
432 return 0;
433 }
434
435 return 0;
436}
437
438void
439chn_resetbuf(struct pcm_channel *c)
440{
441 struct snd_dbuf *b = c->bufhard;
442 struct snd_dbuf *bs = c->bufsoft;
443
444 c->blocks = 0;
445 sndbuf_reset(b);
446 sndbuf_reset(bs);
447}
448
449/*
450 * chn_sync waits until the space in the given channel goes above
451 * a threshold. The threshold is checked against fl or rl respectively.
452 * Assume that the condition can become true, do not check here...
453 */
454int
455chn_sync(struct pcm_channel *c, int threshold)
456{
457 u_long rdy;
458 int ret;
459 struct snd_dbuf *bs = c->bufsoft;
460
461 CHN_LOCKASSERT(c);
462 for (;;) {
463 chn_wrupdate(c);
464 rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
465 if (rdy <= threshold) {
466 ret = chn_sleep(c, "pcmsyn", 1);
467 if (ret == ERESTART || ret == EINTR) {
468 DEB(printf("chn_sync: tsleep returns %d\n", ret));
469 return -1;
470 }
471 } else
472 break;
473 }
474 return 0;
475}
476
477/* called externally, handle locking */
478int
479chn_poll(struct pcm_channel *c, int ev, struct proc *p)
480{
481 struct snd_dbuf *bs = c->bufsoft;
482 int ret;
483
484 CHN_LOCK(c);
485 if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED))
486 chn_start(c, 1);
486 chn_start(c, 1);
487 ret = 0;
488 if (chn_polltrigger(c) && chn_pollreset(c))
489 ret = ev;
490 else
491 selrecord(p, sndbuf_getsel(bs));
492 CHN_UNLOCK(c);
493 return ret;
494}
495
496/*
497 * chn_abort terminates a running dma transfer. it may sleep up to 200ms.
498 * it returns the number of bytes that have not been transferred.
499 *
500 * called from: dsp_close, dsp_ioctl, with both bufhards locked
501 */
502int
503chn_abort(struct pcm_channel *c)
504{
505 int missing = 0, cnt = 0;
506 struct snd_dbuf *b = c->bufhard;
507 struct snd_dbuf *bs = c->bufsoft;
508
509 CHN_LOCKASSERT(c);
510 if (!(c->flags & CHN_F_TRIGGERED))
511 return 0;
512 c->flags |= CHN_F_ABORTING;
513
514 /* wait up to 200ms for the secondary bufhard to empty */
515 cnt = 10;
516 while ((sndbuf_getready(bs) > 0) && (cnt-- > 0)) {
517 chn_sleep(c, "pcmabr", hz / 50);
518 }
519
520 c->flags &= ~CHN_F_TRIGGERED;
521 /* kill the channel */
522 chn_trigger(c, PCMTRIG_ABORT);
523 sndbuf_setrun(b, 0);
524 chn_dmaupdate(c);
525 missing = sndbuf_getready(bs) + sndbuf_getready(b);
526
527 c->flags &= ~CHN_F_ABORTING;
487 ret = 0;
488 if (chn_polltrigger(c) && chn_pollreset(c))
489 ret = ev;
490 else
491 selrecord(p, sndbuf_getsel(bs));
492 CHN_UNLOCK(c);
493 return ret;
494}
495
496/*
497 * chn_abort terminates a running dma transfer. it may sleep up to 200ms.
498 * it returns the number of bytes that have not been transferred.
499 *
500 * called from: dsp_close, dsp_ioctl, with both bufhards locked
501 */
502int
503chn_abort(struct pcm_channel *c)
504{
505 int missing = 0, cnt = 0;
506 struct snd_dbuf *b = c->bufhard;
507 struct snd_dbuf *bs = c->bufsoft;
508
509 CHN_LOCKASSERT(c);
510 if (!(c->flags & CHN_F_TRIGGERED))
511 return 0;
512 c->flags |= CHN_F_ABORTING;
513
514 /* wait up to 200ms for the secondary bufhard to empty */
515 cnt = 10;
516 while ((sndbuf_getready(bs) > 0) && (cnt-- > 0)) {
517 chn_sleep(c, "pcmabr", hz / 50);
518 }
519
520 c->flags &= ~CHN_F_TRIGGERED;
521 /* kill the channel */
522 chn_trigger(c, PCMTRIG_ABORT);
523 sndbuf_setrun(b, 0);
524 chn_dmaupdate(c);
525 missing = sndbuf_getready(bs) + sndbuf_getready(b);
526
527 c->flags &= ~CHN_F_ABORTING;
528 return missing;
528 return missing;
529}
530
531/*
532 * this routine tries to flush the dma transfer. It is called
533 * on a close. We immediately abort any read DMA
534 * operation, and then wait for the play bufhard to drain.
535 *
536 * called from: dsp_close
537 */
538
539int
540chn_flush(struct pcm_channel *c)
541{
542 int ret, count, resid, resid_p;
543 struct snd_dbuf *b = c->bufhard;
544 struct snd_dbuf *bs = c->bufsoft;
545
546 CHN_LOCKASSERT(c);
547 KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
548 DEB(printf("chn_flush c->flags 0x%08x\n", c->flags));
549 if (!(c->flags & CHN_F_TRIGGERED))
550 return 0;
551
529}
530
531/*
532 * this routine tries to flush the dma transfer. It is called
533 * on a close. We immediately abort any read DMA
534 * operation, and then wait for the play bufhard to drain.
535 *
536 * called from: dsp_close
537 */
538
539int
540chn_flush(struct pcm_channel *c)
541{
542 int ret, count, resid, resid_p;
543 struct snd_dbuf *b = c->bufhard;
544 struct snd_dbuf *bs = c->bufsoft;
545
546 CHN_LOCKASSERT(c);
547 KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
548 DEB(printf("chn_flush c->flags 0x%08x\n", c->flags));
549 if (!(c->flags & CHN_F_TRIGGERED))
550 return 0;
551
552 c->flags |= CHN_F_CLOSING;
552 c->flags |= CHN_F_CLOSING;
553 resid = sndbuf_getready(bs) + sndbuf_getready(b);
553 resid = sndbuf_getready(bs) + sndbuf_getready(b);
554 resid_p = resid;
555 count = 10;
554 resid_p = resid;
555 count = 10;
556 ret = 0;
557 while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
556 ret = 0;
557 while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
558 /* still pending output data. */
558 /* still pending output data. */
559 ret = chn_sleep(c, "pcmflu", hz / 10);
560 if (ret == EWOULDBLOCK)
561 ret = 0;
562 if (ret == 0) {
563 resid = sndbuf_getready(bs) + sndbuf_getready(b);
564 if (resid >= resid_p)
565 count--;
566 resid_p = resid;
559 ret = chn_sleep(c, "pcmflu", hz / 10);
560 if (ret == EWOULDBLOCK)
561 ret = 0;
562 if (ret == 0) {
563 resid = sndbuf_getready(bs) + sndbuf_getready(b);
564 if (resid >= resid_p)
565 count--;
566 resid_p = resid;
567 }
568 }
567 }
568 }
569 if (count == 0)
570 DEB(printf("chn_flush: timeout\n"));
571
572 c->flags &= ~CHN_F_TRIGGERED;
573 /* kill the channel */
574 chn_trigger(c, PCMTRIG_ABORT);
575 sndbuf_setrun(b, 0);
576
577 c->flags &= ~CHN_F_CLOSING;
578 return 0;
579}
580
581int
582fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
583{
584 int i;
585
586 for (i = 0; fmtlist[i]; i++)
587 if (fmt == fmtlist[i])
588 return 1;
589 return 0;
590}
591
592int
593chn_reset(struct pcm_channel *c, u_int32_t fmt)
594{
595 int hwspd, r = 0;
596
597 CHN_LOCKASSERT(c);
598 c->flags &= CHN_F_RESET;
599 CHANNEL_RESET(c->methods, c->devinfo);
600 if (fmt) {
601 hwspd = DSP_DEFAULT_SPEED;
602 RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
603 c->speed = hwspd;
604
605 r = chn_setformat(c, fmt);
606 if (r == 0)
607 r = chn_setspeed(c, hwspd);
608 if (r == 0)
609 r = chn_setvolume(c, 100, 100);
610 }
611 r = chn_setblocksize(c, 0, 0);
612 if (r == 0) {
569 if (count == 0)
570 DEB(printf("chn_flush: timeout\n"));
571
572 c->flags &= ~CHN_F_TRIGGERED;
573 /* kill the channel */
574 chn_trigger(c, PCMTRIG_ABORT);
575 sndbuf_setrun(b, 0);
576
577 c->flags &= ~CHN_F_CLOSING;
578 return 0;
579}
580
581int
582fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
583{
584 int i;
585
586 for (i = 0; fmtlist[i]; i++)
587 if (fmt == fmtlist[i])
588 return 1;
589 return 0;
590}
591
592int
593chn_reset(struct pcm_channel *c, u_int32_t fmt)
594{
595 int hwspd, r = 0;
596
597 CHN_LOCKASSERT(c);
598 c->flags &= CHN_F_RESET;
599 CHANNEL_RESET(c->methods, c->devinfo);
600 if (fmt) {
601 hwspd = DSP_DEFAULT_SPEED;
602 RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
603 c->speed = hwspd;
604
605 r = chn_setformat(c, fmt);
606 if (r == 0)
607 r = chn_setspeed(c, hwspd);
608 if (r == 0)
609 r = chn_setvolume(c, 100, 100);
610 }
611 r = chn_setblocksize(c, 0, 0);
612 if (r == 0) {
613 chn_resetbuf(c);
614 CHANNEL_RESETDONE(c->methods, c->devinfo);
613 chn_resetbuf(c);
614 CHANNEL_RESETDONE(c->methods, c->devinfo);
615 }
616 return r;
617}
618
619int
620chn_init(struct pcm_channel *c, void *devinfo, int dir)
621{
622 struct feeder_class *fc;
623 struct snd_dbuf *b, *bs;
624
625 chn_lockinit(c);
626 CHN_LOCK(c);
627 /* Initialize the hardware and DMA bufhard first. */
628 c->feeder = NULL;
629 fc = feeder_getclass(NULL);
630 if (fc == NULL)
631 return EINVAL;
632 if (chn_addfeeder(c, fc, NULL))
633 return EINVAL;
634
635 b = sndbuf_create(c->name, "primary");
636 if (b == NULL)
637 return ENOMEM;
638 bs = sndbuf_create(c->name, "secondary");
639 if (bs == NULL) {
640 sndbuf_destroy(b);
641 return ENOMEM;
642 }
643 sndbuf_setup(bs, NULL, 0);
644 c->bufhard = b;
645 c->bufsoft = bs;
646 c->flags = 0;
647 c->feederflags = 0;
648 c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir);
649 if (c->devinfo == NULL) {
650 sndbuf_destroy(bs);
651 sndbuf_destroy(b);
652 return ENODEV;
653 }
654 if (sndbuf_getsize(b) == 0) {
655 sndbuf_destroy(bs);
656 sndbuf_destroy(b);
657 return ENOMEM;
658 }
659 chn_setdir(c, dir);
660
661 /* And the secondary bufhard. */
662 sndbuf_setfmt(b, AFMT_U8);
663 sndbuf_setfmt(bs, AFMT_U8);
664 CHN_UNLOCK(c);
665 return 0;
666}
667
668int
669chn_kill(struct pcm_channel *c)
670{
671 struct snd_dbuf *b = c->bufhard;
672 struct snd_dbuf *bs = c->bufsoft;
673
674 CHN_LOCK(c);
675 if (c->flags & CHN_F_TRIGGERED)
676 chn_trigger(c, PCMTRIG_ABORT);
677 while (chn_removefeeder(c) == 0);
678 if (CHANNEL_FREE(c->methods, c->devinfo))
679 sndbuf_free(c->bufhard);
680 c->flags |= CHN_F_DEAD;
681 sndbuf_destroy(bs);
682 sndbuf_destroy(b);
683 chn_lockdestroy(c);
684 return 0;
685}
686
687int
688chn_setdir(struct pcm_channel *c, int dir)
689{
690 struct snd_dbuf *b = c->bufhard;
691 int r;
692
693 CHN_LOCKASSERT(c);
694 c->direction = dir;
695 r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction);
696 if (!r && ISA_DMA(b))
697 sndbuf_isadmasetdir(b, c->direction);
698 return r;
699}
700
701int
702chn_setvolume(struct pcm_channel *c, int left, int right)
703{
704 CHN_LOCKASSERT(c);
705 /* could add a feeder for volume changing if channel returns -1 */
615 }
616 return r;
617}
618
619int
620chn_init(struct pcm_channel *c, void *devinfo, int dir)
621{
622 struct feeder_class *fc;
623 struct snd_dbuf *b, *bs;
624
625 chn_lockinit(c);
626 CHN_LOCK(c);
627 /* Initialize the hardware and DMA bufhard first. */
628 c->feeder = NULL;
629 fc = feeder_getclass(NULL);
630 if (fc == NULL)
631 return EINVAL;
632 if (chn_addfeeder(c, fc, NULL))
633 return EINVAL;
634
635 b = sndbuf_create(c->name, "primary");
636 if (b == NULL)
637 return ENOMEM;
638 bs = sndbuf_create(c->name, "secondary");
639 if (bs == NULL) {
640 sndbuf_destroy(b);
641 return ENOMEM;
642 }
643 sndbuf_setup(bs, NULL, 0);
644 c->bufhard = b;
645 c->bufsoft = bs;
646 c->flags = 0;
647 c->feederflags = 0;
648 c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir);
649 if (c->devinfo == NULL) {
650 sndbuf_destroy(bs);
651 sndbuf_destroy(b);
652 return ENODEV;
653 }
654 if (sndbuf_getsize(b) == 0) {
655 sndbuf_destroy(bs);
656 sndbuf_destroy(b);
657 return ENOMEM;
658 }
659 chn_setdir(c, dir);
660
661 /* And the secondary bufhard. */
662 sndbuf_setfmt(b, AFMT_U8);
663 sndbuf_setfmt(bs, AFMT_U8);
664 CHN_UNLOCK(c);
665 return 0;
666}
667
668int
669chn_kill(struct pcm_channel *c)
670{
671 struct snd_dbuf *b = c->bufhard;
672 struct snd_dbuf *bs = c->bufsoft;
673
674 CHN_LOCK(c);
675 if (c->flags & CHN_F_TRIGGERED)
676 chn_trigger(c, PCMTRIG_ABORT);
677 while (chn_removefeeder(c) == 0);
678 if (CHANNEL_FREE(c->methods, c->devinfo))
679 sndbuf_free(c->bufhard);
680 c->flags |= CHN_F_DEAD;
681 sndbuf_destroy(bs);
682 sndbuf_destroy(b);
683 chn_lockdestroy(c);
684 return 0;
685}
686
687int
688chn_setdir(struct pcm_channel *c, int dir)
689{
690 struct snd_dbuf *b = c->bufhard;
691 int r;
692
693 CHN_LOCKASSERT(c);
694 c->direction = dir;
695 r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction);
696 if (!r && ISA_DMA(b))
697 sndbuf_isadmasetdir(b, c->direction);
698 return r;
699}
700
701int
702chn_setvolume(struct pcm_channel *c, int left, int right)
703{
704 CHN_LOCKASSERT(c);
705 /* could add a feeder for volume changing if channel returns -1 */
706 c->volume = (left << 8) | right;
707 return 0;
706 c->volume = (left << 8) | right;
707 return 0;
708}
709
710static int
711chn_tryspeed(struct pcm_channel *c, int speed)
712{
713 struct pcm_feeder *f;
714 struct snd_dbuf *b = c->bufhard;
715 struct snd_dbuf *bs = c->bufsoft;
716 int r, delta;
717
718 CHN_LOCKASSERT(c);
719 DEB(printf("want speed %d, ", speed));
720 if (speed <= 0)
721 return EINVAL;
722 if (CANCHANGE(c)) {
723 r = 0;
724 c->speed = speed;
725 sndbuf_setspd(bs, speed);
726 RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
727 sndbuf_setspd(b, speed);
728 DEB(printf("try speed %d, ", sndbuf_getspd(b)));
729 sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, sndbuf_getspd(b)));
730 DEB(printf("got speed %d, ", sndbuf_getspd(b)));
731
732 delta = sndbuf_getspd(b) - sndbuf_getspd(bs);
733 if (delta < 0)
734 delta = -delta;
735
736 c->feederflags &= ~(1 << FEEDER_RATE);
737 if (delta > 500)
738 c->feederflags |= 1 << FEEDER_RATE;
739 else
740 sndbuf_setspd(bs, sndbuf_getspd(b));
741
742 r = chn_buildfeeder(c);
743 DEB(printf("r = %d\n", r));
744 if (r)
745 goto out;
746
747 r = chn_setblocksize(c, 0, 0);
748 if (r)
749 goto out;
750
751 if (!(c->feederflags & (1 << FEEDER_RATE)))
752 goto out;
753
754 r = EINVAL;
755 f = chn_findfeeder(c, FEEDER_RATE);
756 DEB(printf("feedrate = %p\n", f));
757 if (f == NULL)
758 goto out;
759
760 r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(bs));
761 DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(bs), r));
762 if (r)
763 goto out;
764
765 r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(b));
766 DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(b), r));
767out:
708}
709
710static int
711chn_tryspeed(struct pcm_channel *c, int speed)
712{
713 struct pcm_feeder *f;
714 struct snd_dbuf *b = c->bufhard;
715 struct snd_dbuf *bs = c->bufsoft;
716 int r, delta;
717
718 CHN_LOCKASSERT(c);
719 DEB(printf("want speed %d, ", speed));
720 if (speed <= 0)
721 return EINVAL;
722 if (CANCHANGE(c)) {
723 r = 0;
724 c->speed = speed;
725 sndbuf_setspd(bs, speed);
726 RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
727 sndbuf_setspd(b, speed);
728 DEB(printf("try speed %d, ", sndbuf_getspd(b)));
729 sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, sndbuf_getspd(b)));
730 DEB(printf("got speed %d, ", sndbuf_getspd(b)));
731
732 delta = sndbuf_getspd(b) - sndbuf_getspd(bs);
733 if (delta < 0)
734 delta = -delta;
735
736 c->feederflags &= ~(1 << FEEDER_RATE);
737 if (delta > 500)
738 c->feederflags |= 1 << FEEDER_RATE;
739 else
740 sndbuf_setspd(bs, sndbuf_getspd(b));
741
742 r = chn_buildfeeder(c);
743 DEB(printf("r = %d\n", r));
744 if (r)
745 goto out;
746
747 r = chn_setblocksize(c, 0, 0);
748 if (r)
749 goto out;
750
751 if (!(c->feederflags & (1 << FEEDER_RATE)))
752 goto out;
753
754 r = EINVAL;
755 f = chn_findfeeder(c, FEEDER_RATE);
756 DEB(printf("feedrate = %p\n", f));
757 if (f == NULL)
758 goto out;
759
760 r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(bs));
761 DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(bs), r));
762 if (r)
763 goto out;
764
765 r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(b));
766 DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(b), r));
767out:
768 return r;
768 return r;
769 } else
770 return EINVAL;
771}
772
773int
774chn_setspeed(struct pcm_channel *c, int speed)
775{
776 int r, oldspeed = c->speed;
777
778 r = chn_tryspeed(c, speed);
779 if (r) {
780 DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
781 chn_tryspeed(c, oldspeed);
782 }
783 return r;
784}
785
786static int
787chn_tryformat(struct pcm_channel *c, u_int32_t fmt)
788{
789 struct snd_dbuf *b = c->bufhard;
790 struct snd_dbuf *bs = c->bufsoft;
791 int r;
792 u_int32_t hwfmt;
793
794 CHN_LOCKASSERT(c);
795 if (CANCHANGE(c)) {
796 DEB(printf("want format %d\n", fmt));
797 c->format = fmt;
798 hwfmt = c->format;
799 c->feederflags &= ~(1 << FEEDER_FMT);
800 if (!fmtvalid(hwfmt, chn_getcaps(c)->fmtlist))
801 c->feederflags |= 1 << FEEDER_FMT;
802 r = chn_buildfeeder(c);
803 if (r == 0) {
769 } else
770 return EINVAL;
771}
772
773int
774chn_setspeed(struct pcm_channel *c, int speed)
775{
776 int r, oldspeed = c->speed;
777
778 r = chn_tryspeed(c, speed);
779 if (r) {
780 DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
781 chn_tryspeed(c, oldspeed);
782 }
783 return r;
784}
785
786static int
787chn_tryformat(struct pcm_channel *c, u_int32_t fmt)
788{
789 struct snd_dbuf *b = c->bufhard;
790 struct snd_dbuf *bs = c->bufsoft;
791 int r;
792 u_int32_t hwfmt;
793
794 CHN_LOCKASSERT(c);
795 if (CANCHANGE(c)) {
796 DEB(printf("want format %d\n", fmt));
797 c->format = fmt;
798 hwfmt = c->format;
799 c->feederflags &= ~(1 << FEEDER_FMT);
800 if (!fmtvalid(hwfmt, chn_getcaps(c)->fmtlist))
801 c->feederflags |= 1 << FEEDER_FMT;
802 r = chn_buildfeeder(c);
803 if (r == 0) {
804 hwfmt = c->feeder->desc->out;
805 sndbuf_setfmt(b, hwfmt);
804 hwfmt = c->feeder->desc->out;
805 sndbuf_setfmt(b, hwfmt);
806 sndbuf_setfmt(bs, fmt);
806 sndbuf_setfmt(bs, fmt);
807 chn_resetbuf(c);
808 CHANNEL_SETFORMAT(c->methods, c->devinfo, hwfmt);
807 chn_resetbuf(c);
808 CHANNEL_SETFORMAT(c->methods, c->devinfo, hwfmt);
809 r = chn_tryspeed(c, c->speed);
809 r = chn_tryspeed(c, c->speed);
810 }
810 }
811 return r;
812 } else
813 return EINVAL;
814}
815
816int
817chn_setformat(struct pcm_channel *c, u_int32_t fmt)
818{
819 u_int32_t oldfmt = c->format;
820 int r;
821
822 r = chn_tryformat(c, fmt);
823 if (r) {
824 DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
825 chn_tryformat(c, oldfmt);
826 }
827 return r;
828}
829
830int
831chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
832{
833 struct snd_dbuf *b = c->bufhard;
834 struct snd_dbuf *bs = c->bufsoft;
835 int bufsz, irqhz, tmp, ret;
836
837 CHN_LOCKASSERT(c);
838 if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED))
839 return EINVAL;
840
841 ret = 0;
842 DEB(printf("%s(%d, %d)\n", __FUNCTION__, blkcnt, blksz));
843 if (blksz == 0 || blksz == -1) {
844 if (blksz == -1)
845 c->flags &= ~CHN_F_HAS_SIZE;
846 if (!(c->flags & CHN_F_HAS_SIZE)) {
847 blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / CHN_DEFAULT_HZ;
848 tmp = 32;
849 while (tmp <= blksz)
850 tmp <<= 1;
851 tmp >>= 1;
852 blksz = tmp;
853 blkcnt = CHN_2NDBUFMAXSIZE / blksz;
854
855 RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
856 RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
857 DEB(printf("%s: defaulting to (%d, %d)\n", __FUNCTION__, blkcnt, blksz));
858 } else {
859 blkcnt = sndbuf_getblkcnt(bs);
860 blksz = sndbuf_getblksz(bs);
861 DEB(printf("%s: updating (%d, %d)\n", __FUNCTION__, blkcnt, blksz));
862 }
863 } else {
864 ret = EINVAL;
865 if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
866 goto out;
867 ret = 0;
868 c->flags |= CHN_F_HAS_SIZE;
869 }
870
871 bufsz = blkcnt * blksz;
872
873 ret = ENOMEM;
874 if (sndbuf_remalloc(bs, blkcnt, blksz))
875 goto out;
876 ret = 0;
877
878 /* adjust for different hw format/speed */
879 irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs);
880 DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __FUNCTION__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz));
881 RANGE(irqhz, 16, 512);
882
883 sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz);
884
885 /* round down to 2^x */
886 blksz = 32;
887 while (blksz <= sndbuf_getblksz(b))
888 blksz <<= 1;
889 blksz >>= 1;
890
891 /* round down to fit hw bufhard size */
892 RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
893 DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __FUNCTION__, blksz, sndbuf_getmaxsize(b)));
894
895 sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
896
897 irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b);
898 DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz));
899
900 chn_resetbuf(c);
901out:
902 return ret;
903}
904
905int
906chn_trigger(struct pcm_channel *c, int go)
907{
908 struct snd_dbuf *b = c->bufhard;
909 int ret;
910
911 CHN_LOCKASSERT(c);
912 if (ISA_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
913 sndbuf_isadmabounce(b);
914 ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
915
916 return ret;
917}
918
919int
920chn_getptr(struct pcm_channel *c)
921{
922 int hwptr;
923 int a = (1 << c->align) - 1;
924
925 CHN_LOCKASSERT(c);
926 hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
927 /* don't allow unaligned values in the hwa ptr */
928#if 1
929 hwptr &= ~a ; /* Apply channel align mask */
930#endif
931 hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
932 return hwptr;
933}
934
935struct pcmchan_caps *
936chn_getcaps(struct pcm_channel *c)
937{
938 CHN_LOCKASSERT(c);
939 return CHANNEL_GETCAPS(c->methods, c->devinfo);
940}
941
942u_int32_t
943chn_getformats(struct pcm_channel *c)
944{
945 u_int32_t *fmtlist, fmts;
946 int i;
947
948 fmtlist = chn_getcaps(c)->fmtlist;
949 fmts = 0;
950 for (i = 0; fmtlist[i]; i++)
951 fmts |= fmtlist[i];
952
953 return fmts;
954}
955
956static int
957chn_buildfeeder(struct pcm_channel *c)
958{
959 struct feeder_class *fc;
960 struct pcm_feederdesc desc;
961 u_int32_t tmp[2], src, dst, type, flags;
962
963 CHN_LOCKASSERT(c);
964 while (chn_removefeeder(c) == 0);
965 KASSERT((c->feeder == NULL), ("feeder chain not empty"));
966 c->align = sndbuf_getalign(c->bufsoft);
967 fc = feeder_getclass(NULL);
968 if (fc == NULL)
969 return EINVAL;
970 if (chn_addfeeder(c, fc, NULL))
971 return EINVAL;
972 c->feeder->desc->out = c->format;
973
974 flags = c->feederflags;
975 src = c->feeder->desc->out;
976 if ((c->flags & CHN_F_MAPPED) && (flags != 0))
977 return EINVAL;
978 DEB(printf("not mapped, flags %x, ", flags));
979 for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) {
980 if (flags & (1 << type)) {
981 desc.type = type;
982 desc.in = 0;
983 desc.out = 0;
984 desc.flags = 0;
985 DEB(printf("find feeder type %d, ", type));
986 fc = feeder_getclass(&desc);
987 DEB(printf("got %p\n", fc));
988 if (fc == NULL)
989 return EINVAL;
990 dst = fc->desc->in;
991 if (src != dst) {
992 DEB(printf("build fmtchain from %x to %x: ", src, dst));
993 tmp[0] = dst;
994 tmp[1] = 0;
995 if (chn_fmtchain(c, tmp) == 0)
996 return EINVAL;
997 DEB(printf("ok\n"));
998 }
999 if (chn_addfeeder(c, fc, fc->desc))
1000 return EINVAL;
1001 src = fc->desc->out;
1002 DEB(printf("added feeder %p, output %x\n", fc, src));
1003 dst = 0;
1004 flags &= ~(1 << type);
1005 }
1006 }
1007 if (!fmtvalid(src, chn_getcaps(c)->fmtlist)) {
1008 if (chn_fmtchain(c, chn_getcaps(c)->fmtlist) == 0)
1009 return EINVAL;
1010 DEB(printf("built fmtchain from %x to %x\n", src, c->feeder->desc->out));
1011 flags &= ~(1 << FEEDER_FMT);
1012 }
1013 return 0;
1014}
1015
1016
1017
811 return r;
812 } else
813 return EINVAL;
814}
815
816int
817chn_setformat(struct pcm_channel *c, u_int32_t fmt)
818{
819 u_int32_t oldfmt = c->format;
820 int r;
821
822 r = chn_tryformat(c, fmt);
823 if (r) {
824 DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
825 chn_tryformat(c, oldfmt);
826 }
827 return r;
828}
829
830int
831chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
832{
833 struct snd_dbuf *b = c->bufhard;
834 struct snd_dbuf *bs = c->bufsoft;
835 int bufsz, irqhz, tmp, ret;
836
837 CHN_LOCKASSERT(c);
838 if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED))
839 return EINVAL;
840
841 ret = 0;
842 DEB(printf("%s(%d, %d)\n", __FUNCTION__, blkcnt, blksz));
843 if (blksz == 0 || blksz == -1) {
844 if (blksz == -1)
845 c->flags &= ~CHN_F_HAS_SIZE;
846 if (!(c->flags & CHN_F_HAS_SIZE)) {
847 blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / CHN_DEFAULT_HZ;
848 tmp = 32;
849 while (tmp <= blksz)
850 tmp <<= 1;
851 tmp >>= 1;
852 blksz = tmp;
853 blkcnt = CHN_2NDBUFMAXSIZE / blksz;
854
855 RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
856 RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
857 DEB(printf("%s: defaulting to (%d, %d)\n", __FUNCTION__, blkcnt, blksz));
858 } else {
859 blkcnt = sndbuf_getblkcnt(bs);
860 blksz = sndbuf_getblksz(bs);
861 DEB(printf("%s: updating (%d, %d)\n", __FUNCTION__, blkcnt, blksz));
862 }
863 } else {
864 ret = EINVAL;
865 if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
866 goto out;
867 ret = 0;
868 c->flags |= CHN_F_HAS_SIZE;
869 }
870
871 bufsz = blkcnt * blksz;
872
873 ret = ENOMEM;
874 if (sndbuf_remalloc(bs, blkcnt, blksz))
875 goto out;
876 ret = 0;
877
878 /* adjust for different hw format/speed */
879 irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs);
880 DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __FUNCTION__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz));
881 RANGE(irqhz, 16, 512);
882
883 sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz);
884
885 /* round down to 2^x */
886 blksz = 32;
887 while (blksz <= sndbuf_getblksz(b))
888 blksz <<= 1;
889 blksz >>= 1;
890
891 /* round down to fit hw bufhard size */
892 RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
893 DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __FUNCTION__, blksz, sndbuf_getmaxsize(b)));
894
895 sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
896
897 irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b);
898 DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz));
899
900 chn_resetbuf(c);
901out:
902 return ret;
903}
904
905int
906chn_trigger(struct pcm_channel *c, int go)
907{
908 struct snd_dbuf *b = c->bufhard;
909 int ret;
910
911 CHN_LOCKASSERT(c);
912 if (ISA_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
913 sndbuf_isadmabounce(b);
914 ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
915
916 return ret;
917}
918
919int
920chn_getptr(struct pcm_channel *c)
921{
922 int hwptr;
923 int a = (1 << c->align) - 1;
924
925 CHN_LOCKASSERT(c);
926 hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
927 /* don't allow unaligned values in the hwa ptr */
928#if 1
929 hwptr &= ~a ; /* Apply channel align mask */
930#endif
931 hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
932 return hwptr;
933}
934
935struct pcmchan_caps *
936chn_getcaps(struct pcm_channel *c)
937{
938 CHN_LOCKASSERT(c);
939 return CHANNEL_GETCAPS(c->methods, c->devinfo);
940}
941
942u_int32_t
943chn_getformats(struct pcm_channel *c)
944{
945 u_int32_t *fmtlist, fmts;
946 int i;
947
948 fmtlist = chn_getcaps(c)->fmtlist;
949 fmts = 0;
950 for (i = 0; fmtlist[i]; i++)
951 fmts |= fmtlist[i];
952
953 return fmts;
954}
955
956static int
957chn_buildfeeder(struct pcm_channel *c)
958{
959 struct feeder_class *fc;
960 struct pcm_feederdesc desc;
961 u_int32_t tmp[2], src, dst, type, flags;
962
963 CHN_LOCKASSERT(c);
964 while (chn_removefeeder(c) == 0);
965 KASSERT((c->feeder == NULL), ("feeder chain not empty"));
966 c->align = sndbuf_getalign(c->bufsoft);
967 fc = feeder_getclass(NULL);
968 if (fc == NULL)
969 return EINVAL;
970 if (chn_addfeeder(c, fc, NULL))
971 return EINVAL;
972 c->feeder->desc->out = c->format;
973
974 flags = c->feederflags;
975 src = c->feeder->desc->out;
976 if ((c->flags & CHN_F_MAPPED) && (flags != 0))
977 return EINVAL;
978 DEB(printf("not mapped, flags %x, ", flags));
979 for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) {
980 if (flags & (1 << type)) {
981 desc.type = type;
982 desc.in = 0;
983 desc.out = 0;
984 desc.flags = 0;
985 DEB(printf("find feeder type %d, ", type));
986 fc = feeder_getclass(&desc);
987 DEB(printf("got %p\n", fc));
988 if (fc == NULL)
989 return EINVAL;
990 dst = fc->desc->in;
991 if (src != dst) {
992 DEB(printf("build fmtchain from %x to %x: ", src, dst));
993 tmp[0] = dst;
994 tmp[1] = 0;
995 if (chn_fmtchain(c, tmp) == 0)
996 return EINVAL;
997 DEB(printf("ok\n"));
998 }
999 if (chn_addfeeder(c, fc, fc->desc))
1000 return EINVAL;
1001 src = fc->desc->out;
1002 DEB(printf("added feeder %p, output %x\n", fc, src));
1003 dst = 0;
1004 flags &= ~(1 << type);
1005 }
1006 }
1007 if (!fmtvalid(src, chn_getcaps(c)->fmtlist)) {
1008 if (chn_fmtchain(c, chn_getcaps(c)->fmtlist) == 0)
1009 return EINVAL;
1010 DEB(printf("built fmtchain from %x to %x\n", src, c->feeder->desc->out));
1011 flags &= ~(1 << FEEDER_FMT);
1012 }
1013 return 0;
1014}
1015
1016
1017