Date: Sun, 21 Mar 1999 18:50:02 +0100 (CET) From: Andrea Arcangeli <andrea@e-mind.com> To: Alan Cox <alan@lxorguk.ukuu.org.uk>, "Daniel J. Rodriksson" <djr@dit.upm.es>, linux-sound@vger.rutgers.edu Subject: Re: [patch] SBPRO 16bit 44.1khz stereo (and some improvement to sb16 On Thu, 18 Mar 1999, Andrea Arcangeli wrote: >I did some little improvement to the previous code. This patch is >incremental and it's called sbpro-16bit-2.2.3-AB (AB because it's >incremental to A ;). Oh well I have a final patch that is been tested also on the sb16 side. My -A and -B patches had a grave bug in the sb16 side. So this new final patch (sbpro-16bit-2.2.3-C) is just been reported to works fine also with sb16 (and the sbpro side is tested by me ;), in both half-duplex and fullduplex mode by Edward S. Marshall <emarshal@logic.net>. This new patch make the sb16_copy_to_user operation thread safe (if you have two sb16 both working in fullduplex you _need_ it) and it avoids us to do the signed<->unsigned conversion in software because now it's the sb16 that do that in hardware. The second part of the patch is sbpro related. If you (like me) have an old sbpro you'll magically find yourself able to reproduce at 16bit 44.1khz stereo mode as you would using an sb16 :). Obviously the sound quality is bad, but at least everything works again ;)). Last but not the least, the patch change the copy_user interface to make it able to report a segfault and it removes a field of overhead. Index: linux/drivers/sound/audio.c diff -u linux/drivers/sound/audio.c:1.1.1.1 linux/drivers/sound/audio.c:1.1.2.2 --- linux/drivers/sound/audio.c:1.1.1.1 Mon Jan 18 02:28:36 1999 +++ linux/drivers/sound/audio.c Wed Mar 17 16:46:59 1999 @@ -249,12 +249,12 @@ if(copy_from_user(dma_buf, &(buf)[p], l)) return -EFAULT; } - else audio_devs[dev]->d->copy_user (dev, - dma_buf, 0, - buf, p, - c, buf_size, - &used, &returned, - l); + else if (audio_devs[dev]->d->copy_user (dev, + dma_buf, 0, + buf, p, + c, buf_size, + &used, &returned)) + return -EFAULT; l = returned; if (audio_devs[dev]->local_conversion & CNV_MU_LAW) Index: linux/drivers/sound/dev_table.h diff -u linux/drivers/sound/dev_table.h:1.1.1.1 linux/drivers/sound/dev_table.h:1.1.2.2 --- linux/drivers/sound/dev_table.h:1.1.1.1 Mon Jan 18 02:28:38 1999 +++ linux/drivers/sound/dev_table.h Wed Mar 17 16:46:59 1999 @@ -190,12 +190,11 @@ int (*prepare_for_output) (int dev, int bufsize, int nbufs); void (*halt_io) (int dev); int (*local_qlen)(int dev); - void (*copy_user) (int dev, - char *localbuf, int localoffs, - const char *userbuf, int useroffs, - int max_in, int max_out, - int *used, int *returned, - int len); + int (*copy_user) (int dev, + char *localbuf, int localoffs, + const char *userbuf, int useroffs, + int max_in, int max_out, + int *used, int *returned); void (*halt_input) (int dev); void (*halt_output) (int dev); void (*trigger) (int dev, int bits); Index: linux/drivers/sound/sb.h diff -u linux/drivers/sound/sb.h:1.1.1.4 linux/drivers/sound/sb.h:1.1.2.5 --- linux/drivers/sound/sb.h:1.1.1.4 Tue Mar 9 01:53:33 1999 +++ linux/drivers/sound/sb.h Thu Mar 18 17:55:41 1999 @@ -95,7 +95,8 @@ /* State variables */ int opened; /* new audio fields for full duplex support */ - int fullduplex; + int fullduplex:1; + int double_speed:1; int duplex; int speed, bits, channels; volatile int irq_ok; Index: linux/drivers/sound/sb_audio.c diff -u linux/drivers/sound/sb_audio.c:1.1.1.2 linux/drivers/sound/sb_audio.c:1.1.2.5 --- linux/drivers/sound/sb_audio.c:1.1.1.2 Mon Jan 18 14:38:39 1999 +++ linux/drivers/sound/sb_audio.c Sun Mar 21 15:37:08 1999 @@ -19,6 +19,9 @@ * Daniel J. Rodriksson: Changes to make sb16 work full duplex. * Maybe other 16 bit cards in this code could behave * the same. + * Andrea Arcangeli: Yes Daniel, the sbpro now is behaving the same, + * now that I developed now a virtual 2 channel + * 44.1khz 16 bit mode for the SBPRO ;). */ #include <linux/config.h> @@ -68,6 +71,7 @@ devc->irq_mode_16 = IMODE_NONE; devc->fullduplex = devc->duplex && ((mode & OPEN_READ) && (mode & OPEN_WRITE)); + devc->double_speed = 0; sb_dsp_reset(devc); /* At first glance this check isn't enough, some ESS chips might not @@ -325,6 +329,20 @@ return devc->bits = 8; } +static unsigned int sbpro_audio_set_bits(int dev, unsigned int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + if (bits != 0) + { + if (bits == AFMT_S16_LE && !(devc->opened & OPEN_READ)) + devc->bits = AFMT_S16_LE; + else + devc->bits = AFMT_U8; + } + + return devc->bits; +} + static void sb1_audio_halt_xfer(int dev) { unsigned long flags; @@ -347,6 +365,7 @@ int count = nr_bytes; sb_devc *devc = audio_devs[dev]->devc; unsigned char cmd; + int speed; /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ @@ -356,6 +375,11 @@ devc->irq_mode = IMODE_OUTPUT; + if (!devc->double_speed) + speed = devc->speed; + else + speed = 22050; + save_flags(flags); cli(); if (sb_dsp_command(devc, 0x48)) /* DSP Block size */ @@ -363,7 +387,7 @@ sb_dsp_command(devc, (unsigned char) (count & 0xff)); sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); - if (devc->speed * devc->channels <= 23000) + if (speed * devc->channels <= 23000) cmd = 0x1c; /* 8 bit PCM output */ else cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */ @@ -555,7 +579,9 @@ if (speed > 44100) speed = 44100; if (devc->channels > 1 && speed > 22050) - speed = 22050; + devc->double_speed = 1; + else + devc->double_speed = 0; sb201_audio_set_speed(dev, speed); } return devc->speed; @@ -733,7 +759,7 @@ sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6)); sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) + - (devc->bits == AFMT_S16_LE ? 0x10 : 0))); + (bits == AFMT_S16_LE ? 0x10 : 0))); sb_dsp_command(devc, (unsigned char) (cnt & 0xff)); sb_dsp_command(devc, (unsigned char) (cnt >> 8)); @@ -835,25 +861,22 @@ devc->trigger_bits = bits | bits_16; } -static unsigned char lbuf8[2048]; -static signed short *lbuf16 = (signed short *)lbuf8; -#define LBUFCOPYSIZE 1024 -static void +static int sb16_copy_from_user(int dev, char *localbuf, int localoffs, const char *userbuf, int useroffs, int max_in, int max_out, - int *used, int *returned, - int len) + int *used, int *returned) { sb_devc *devc = audio_devs[dev]->devc; - int i, c, p, locallen; + int i, len; unsigned char *buf8; signed short *buf16; /* if not duplex no conversion */ if (!devc->fullduplex) { + len = max_in > max_out ? max_out : max_in; copy_from_user (localbuf + localoffs, userbuf + useroffs, len); *used = len; *returned = len; @@ -867,21 +890,14 @@ /* c, count of samples remaining in buffer ( 16 bits )*/ /* p, count of samples already processed ( 16 bits )*/ len = ( (max_in >> 1) > max_out) ? max_out : (max_in >> 1); - c = len; - p = 0; - buf8 = (unsigned char *)(localbuf + localoffs); - while (c) + buf8 = (unsigned char *) (localbuf + localoffs); + buf16 = (signed short *) (userbuf + useroffs); + for (i=0; i<len; i++) { - locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c); - /* << 1 in order to get 16 bit samples */ - copy_from_user (lbuf16, - userbuf+useroffs + (p << 1), - locallen << 1); - for (i = 0; i < locallen; i++) - { - buf8[p+i] = ~((lbuf16[i] >> 8) & 0xff) ^ 0x80; - } - c -= locallen; p += locallen; + signed short sample; + if (get_user(sample, buf16+i)) + return -EFAULT; + buf8[i] = sample >> 8; } /* used = ( samples * 16 bits size ) */ *used = len << 1; @@ -897,26 +913,102 @@ /* c, count of samples remaining in buffer ( 8 bits )*/ /* p, count of samples already processed ( 8 bits )*/ len = max_in > (max_out >> 1) ? (max_out >> 1) : max_in; - c = len; - p = 0; - buf16 = (signed short *)(localbuf + localoffs); - while (c) + buf16 = (signed short *) (localbuf + localoffs); + buf8 = (unsigned char *) (userbuf + useroffs); + for (i=0; i<len; i++) { - locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c); - copy_from_user (lbuf8, - userbuf+useroffs + p, - locallen); - for (i = 0; i < locallen; i++) - { - buf16[p+i] = (~lbuf8[i] ^ 0x80) << 8; - } - c -= locallen; p += locallen; + unsigned char sample; + if (get_user(sample, buf8+i)) + return -EFAULT; + buf16[i] = sample << 8; } /* used = ( samples * 8 bits size ) */ *used = len; /* returned = ( samples * 16 bits size ) */ *returned = len << 1; } + return 0; +} + +static int sbpro_copy_from_user(int dev, char *localbuf, int localoffs, + const char *userbuf, int useroffs, + int max_in, int max_out, + int *used, int *returned) +{ + sb_devc *devc = audio_devs[dev]->devc; + int len; + + if (devc->bits == AFMT_U8) + { + if (!devc->double_speed) + { + len = max_in; + copy_from_user(localbuf + localoffs, userbuf + useroffs, len); + *used = len; + *returned = len; + } else { + unsigned char *buf22k; + unsigned char *buf44k; + int i; + + len = ( (max_in >> 1) > max_out) ? max_out : (max_in >> 1); + buf22k = (unsigned char *) (localbuf + localoffs); + buf44k = (unsigned char *) (userbuf + useroffs); + for (i=0; i<len; i++) + { + unsigned char sample; + if (get_user(sample, buf44k+i*2)) + return -EFAULT; + buf22k[i] = sample; + } + /* used = ( samples * 16 bits size ) */ + *used = len << 1; + /* returned = ( samples * 8 bits size ) */ + *returned = len; + } + } else { + if (!devc->double_speed) + { + unsigned char *buf8; + signed short *buf16; + int i; + + len = ( (max_in >> 1) > max_out) ? max_out : (max_in >> 1); + buf8 = (unsigned char *) (localbuf + localoffs); + buf16 = (signed short *) (userbuf + useroffs); + for (i=0; i<len; i++) + { + signed short sample; + if (get_user(sample, buf16+i)) + return -EFAULT; + buf8[i] = ~(sample >> 8) ^ 0x80; + } + /* used = ( samples * 16 bits size ) */ + *used = len << 1; + /* returned = ( samples * 8 bits size ) */ + *returned = len; + } else { + unsigned char *buf8_22k; + signed short *buf16_44k; + int i; + + len = ( (max_in >> 2) > max_out) ? max_out : (max_in >> 2); + buf8_22k = (unsigned char *) (localbuf + localoffs); + buf16_44k = (signed short *) (userbuf + useroffs); + for (i=0; i<len; i++) + { + signed short sample; + if (get_user(sample, buf16_44k+i*2)) + return -EFAULT; + buf8_22k[i] = ~(sample >> 8) ^ 0x80; + } + /* used = ( samples * 16 bits size ) */ + *used = len << 2; + /* returned = ( samples * 8 bits size ) */ + *returned = len; + } + } + return 0; } static void @@ -997,12 +1089,12 @@ sbpro_audio_prepare_for_output, sb1_audio_halt_xfer, NULL, /* local_qlen */ - NULL, /* copy_from_user */ + sbpro_copy_from_user, /* copy_from_user */ NULL, NULL, sb20_audio_trigger, sbpro_audio_set_speed, - sb1_audio_set_bits, + sbpro_audio_set_bits, sbpro_audio_set_channels }; @@ -1104,6 +1196,7 @@ DDB(printk("Will use SB Pro driver\n")); audio_flags = DMA_AUTOMODE; driver = &sbpro_audio_driver; + format_mask |= AFMT_S16_LE; } if ((devc->dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, Andrea Arcangeli - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/