Hi,

This is a work-in-progress patch for supporting the Si3055 HDAudio softmodem.

It can already play and record sound from the phone line. I could
successfully dial to a phone number using a DTMF generator and talk to
people in the telephone by piping ossrecord to ossplay and vice-versa.

I'm very grateful to Yair (cesium) and François (mmu_man), who gave a
lot of advices by IRC.

When concluded, this code will allow for cross-platform softmodem
support. I already ported slmodem and linmodem for using OSSv4, and
François already ported linmodem for BeOS/Haiku. So slmodem will be
usable in all our x86 (because it contains some closed-source object
code) supported OSs, and linmodem (fully opensource) will be usable in
all supported platforms (although linmodem currently only supports
slowish connection speeds). An "osstelephone" tool can easily be
created too, to allow for easily using the computer as a telephone.

However, this is just a start, and the code is extremely disorganized.
So I'm writing to ask for advice about how to improve this patch.

Some issues remaining:


1) We need a new ioctl for going Off-Hook (connected to the phone
line) and On-Hook (disconnected from the phone line).

We discussed this in IRC and initially we though about going
off-hook/on-hook when opening/closing the device, because we didn't
see any sense in recording sound from an on-hook modem.

However, ALSA uses a mixer control for off-hook, and NetBSD
(http://mail-index.netbsd.org/current-users/2005/04/07/0022.html) uses
a sysctl for it.

Later, we found a reason - when pulse dialing, someone needs to
receive sound from the modem while it is on-hook, to know when the
line tone ceases. Indeed, slmodem does that.

So we need an ioctl for going Off-Hook or On-Hook. In the patch, I
proposed the name SNDCTL_MODEM_OFFHOOK, but better names suggestions
are welcome.

It could be implemented in hdaudio_codec_audio_ioctl(), but then
another code organization issue would arise. How to implement it
without disorganizing the code even more?


2) Perhaps DUPLEX devices for modems would be better.

By the following reasons:

- linmodem and slmodem have code designed so that it's more adequate
to use duplex devices.

- Two different device nodes for a modem looks somewhat cumbersome,
and has strange semantics (for example for off-hook).

- The modem has only one sampling rate register, for both playback and
record. Let's suppose you open the recording device at 16kHz, then
forget the DTMF generator uses 8kHz and call it in the playback
device. When it sets the playback device sampling rate, the modem will
change the recoding sampling rate too, and nobody will know about
that, so messing up the recording stream. This actually happened to me
when I was testing the code.

Using duplex devices would avoid that.

François suggested the device nodes being named "mdm%d". It looks nice.

Perhaps install_outputdevs() would need to be modified for using
duplex for modems, and hdaudio_endpointinfo_t would need to be
modified for being suitable for that.


3) Again, code organization. How to organize this code so it doesn't
look so hackish?


4) The modifications to allow mono devices in install_outputdevs()
were hackish. Maybe they could be cleaned up by solving items 2 and 3.

A modification in hda_audio_set_channels() was needed because it was
forcing at least 2 channels. Is it OK?


5) Solving stream_number issues.

Perhaps OSS treats the stream numbers as something that needs to be
unique in the scope of a codec. For example, in attach_node(), we
have:

endpoint->stream_number = endpoint->default_stream_number =
                codec->num_inendpoints;

So, this way, the OSS lets identical stream_numbers repeat in different codecs.

However, by reading the HDAudio specification, at page 17, the figure
shows that stream_numbers should be treated in the Azalia controller
scope. That is, a stream_number needs to be unique inside an entire
controller, over all of its codecs.

In fact, if I assign stream_number=0 to the modem engine, sound
actually plays in the speaker instead of the phone line.

I temporarily solved this by using the maximum permitted stream_number
(0xf), but this is very hackish.

Perhaps we need a stream counter in some controller-wise struct, like
hda_devc_t or even hdaudio_mixer_t.

Also, can I use the same stream number for the input and output
streams? It's not much clear from the specifications, but appears to
work, and OSS appears to already do this in attach_node().



Some random stuff related to this softmodem work can be found here:
http://basalto.ifsc.usp.br/~paulo_matias/modem/


Please help, I still need a lot of advice on this!


Thanks and best regards,

Paulo Matias
diff -r 7656ac428350 include/soundcard.h
--- a/include/soundcard.h	Wed Oct 29 22:46:59 2008 +0200
+++ b/include/soundcard.h	Tue Nov 04 23:07:48 2008 -0200
@@ -2010,6 +2010,8 @@
 
 #define SNDCTL_MIX_DESCRIPTION	__SIOWR('X',14, oss_mixer_enuminfo)
 
+#define SNDCTL_MODEM_OFFHOOK    __SIOWR('X',15, int)
+
 /* ioctl codes 'X', 200-255 are reserved for internal use */
 
 /*
diff -r 7656ac428350 kernel/drv/oss_hdaudio/hdaudio_codec.c
--- a/kernel/drv/oss_hdaudio/hdaudio_codec.c	Wed Oct 29 22:46:59 2008 +0200
+++ b/kernel/drv/oss_hdaudio/hdaudio_codec.c	Tue Nov 04 23:07:49 2008 -0200
@@ -7,6 +7,26 @@
 #include "hdaudio.h"
 #include "hdaudio_codec.h"
 #include "hdaudio_codecids.h"
+
+
+
+/* Si3055 verbs. */
+#define SI3055_REG_GET_VERB	0x900
+#define SI3055_REG_SET_VERB	0x100
+
+/* Convenience macros for reading from and writing to Si3055 registers. */
+#define SI3055_REG_GET(mixer, cad, reg, a, b) corb_read(mixer, cad, reg, 0, SI3055_REG_GET_VERB, 0, a, b)
+#define SI3055_REG_SET(mixer, cad, reg, val) corb_write(mixer, cad, reg, 0, SI3055_REG_SET_VERB, val)
+/*
+ * Si3055 register IDs.
+ */
+#define SI3055_EXT_MODEM_STATUS	2
+#define SI3055_LINE_RATE	3
+#define SI3055_HDA_STREAMS	4
+#define SI3055_GPIO_PIN_STATUS	10
+#define SI3055_LINE_CONFIG	13
+
+
 
 extern int hdaudio_snoopy;
 extern int hdaudio_jacksense;
@@ -1909,6 +1929,8 @@
     }
 }
 
+extern int hdaudio_si3055_endpoint_init(hdaudio_mixer_t * mixer, int cad);
+
 /* ARGSUSED */
 static int
 attach_codec (hdaudio_mixer_t * mixer, int cad, char *hw_info,
@@ -2128,6 +2150,11 @@
       /* power up the AFG! */
       corb_write (mixer, cad, i, 0, SET_POWER_STATE, 0);
     }
+    
+  /* Initialize and setup manually endpoints for Si3055. */
+  if ((mixer->codecs[cad]->vendor_flags & VF_SI3055_HACK) && (group_type == 2)) {
+	  hdaudio_si3055_endpoint_init(mixer, cad);
+  }
 
   if (has_audio_group)
     {
@@ -2258,6 +2285,16 @@
       }
 
   *setupbits = tmp;
+  
+  if (mixer->codecs[endpoint->cad]->vendor_flags & VF_SI3055_HACK)
+    {
+	unsigned int a, b;
+	cmn_err(CE_CONT, "hdaudio_codec_setup_endpoint for Si3055.\n");
+	cmn_err(CE_CONT, "endpoint dbg: %d, %d, %d, %d.\n", rate, channels, fmt, stream_number);
+	SI3055_REG_SET(mixer, endpoint->cad, SI3055_LINE_RATE, rate);
+	SI3055_REG_GET(mixer, endpoint->cad, SI3055_LINE_RATE, &a, &b);
+	cmn_err(CE_CONT, "new rate: %d\n", a);
+    }
 
   corb_write (mixer, endpoint->cad, endpoint->base_wid, 0,
 	      SET_CONVERTER_FORMAT, tmp);
diff -r 7656ac428350 kernel/drv/oss_hdaudio/hdaudio_codec.h
--- a/kernel/drv/oss_hdaudio/hdaudio_codec.h	Wed Oct 29 22:46:59 2008 +0200
+++ b/kernel/drv/oss_hdaudio/hdaudio_codec.h	Tue Nov 04 23:07:49 2008 -0200
@@ -127,6 +127,7 @@
 #define SET_GPIO_STICKY				0x71a
 #define GET_SUBSYSTEM_ID			0xf20
 #define GET_STRIPE_CONTROL			0xf24
+#define SET_CODEC_RESET				0x7ff
 
 /*
  * Parameters
diff -r 7656ac428350 kernel/drv/oss_hdaudio/hdaudio_codecids.h
--- a/kernel/drv/oss_hdaudio/hdaudio_codecids.h	Wed Oct 29 22:46:59 2008 +0200
+++ b/kernel/drv/oss_hdaudio/hdaudio_codecids.h	Tue Nov 04 23:07:49 2008 -0200
@@ -18,6 +18,8 @@
 S/PDIF */
 #define VF_VAIO_HACK    0x00000002      /* VAIO STAC9872 requires special
 handling for headphone DAC */
+#define VF_SI3055_HACK  0x00000004      /* Si3055 modem requires manual endpoint
+setuping and rate and ioctl hacks. */
   char **remap;
 
   /*
@@ -815,6 +817,8 @@
 	NULL
 };
 
+extern int hdaudio_si3055_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+
 static const codec_desc_t codecs[] = {
   /* Realtek HDA codecs */
   {0x10ec0260, "ALC260", VF_NONE, (char **) &alc260remap},
@@ -893,6 +897,16 @@
   {0x14f15047, "CX20551", VF_NONE, NULL, 0x76543201},
   {0x14f12c06, "Conexant2c06", VF_NONE, (char **) &conexant_modem_remap, 0, NULL_mixer_init}, /* Modem codec (Vaio) */
   {0x14f12bfa, "Conexant2bfa", VF_NONE, (char **) &conexant_modem_remap, 0, NULL_mixer_init}, /* Modem codec (Acer Ferrari 5k) */
+
+  /* Si3055 and compatible modems */
+  {0x163c3055, "Si3055", VF_SI3055_HACK, NULL, 0, hdaudio_si3055_mixer_init },
+  {0x163c3155, "Si3155", VF_SI3055_HACK, NULL, 0, hdaudio_si3055_mixer_init },
+  {0x11c13026, "Agere3026", VF_SI3055_HACK, NULL, 0, hdaudio_si3055_mixer_init },
+  {0x11c13055, "Agere3055", VF_SI3055_HACK, NULL, 0, hdaudio_si3055_mixer_init },
+  {0x11c13155, "Agere3155", VF_SI3055_HACK, NULL, 0, hdaudio_si3055_mixer_init },
+  {0x10573055, "Motorola3055", VF_SI3055_HACK, NULL, 0, hdaudio_si3055_mixer_init },
+  {0x10573057, "Motorola3057", VF_SI3055_HACK, NULL, 0, hdaudio_si3055_mixer_init },
+  {0x10573155, "Motorola3155", VF_SI3055_HACK, NULL, 0, hdaudio_si3055_mixer_init },
 
   /* Unknown */
   {0, "Unknown"}
diff -r 7656ac428350 kernel/drv/oss_hdaudio/hdaudio_si3055.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/drv/oss_hdaudio/hdaudio_si3055.c	Tue Nov 04 23:07:49 2008 -0200
@@ -0,0 +1,192 @@
+/*
+ * Purpose: Driver for Si3055 and compatible modems.
+ */
+#define COPYING Copyright (C) 2008 Paulo Matias. Licensed to 4Front Technologies
+
+
+/*
+ * Documentation Note
+ * 
+ * There is no publicly available documentation for Si3055. However,
+ * there is a very similar modem (Si3038) for which a datasheet is
+ * publicly available:
+ * 
+ * https://www.silabs.com/Support%20Documents/TechnicalDocs/si3038.pdf
+ * 
+ * This driver was written by reading the ALSA code, looking for a
+ * similar modem (Si3038), and figuring out the corresponding Si3055
+ * register IDs for Si3038 registers, again by reading the ALSA code,
+ * and by checking the default after-reset values.
+ * 
+ */
+
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_dedicated.h"
+#include "hdaudio_mixers.h"
+
+/*
+ * Si3055 register IDs.
+ */
+#define SI3055_EXT_MODEM_STATUS	2
+#define SI3055_LINE_RATE	3
+#define SI3055_HDA_STREAMS	4
+#define SI3055_GPIO_CONFIG      5
+#define SI3055_GPIO_PIN_STATUS	10
+#define SI3055_LINE_CONFIG	13
+
+
+/* Corresponding registers in Si3038 (for reference):
+ * 
+ * SI3055_EXT_MODEM_STATUS	3Eh (Extended Modem Status & Control)
+ * SI3055_LINE_RATE		40h (Line 1 DAC/ADC Rate)
+ * SI3055_GPIO_PIN_STATUS	54h (GPIO Pin Status)
+ * SI3055_LINE_CONFIG		56h (Line Side Configuration 1)
+ */
+
+/*
+ * The SI3055_HDA_STREAMS register has no corresponding in Si3038.
+ * It contains the playback and recording stream descriptors in the
+ * following format:
+ * 
+ *    ((playback_stream_num << 4) << 8) | (recording_stream_num << 4)
+ */
+
+/* Si3055 verbs. */
+#define SI3055_REG_GET_VERB	0x900
+#define SI3055_REG_SET_VERB	0x100
+
+/* Convenience macros for reading from and writing to Si3055 registers. */
+#define SI3055_REG_GET(mixer, cad, reg, a, b) corb_read(mixer, cad, reg, 0, SI3055_REG_GET_VERB, 0, a, b)
+#define SI3055_REG_SET(mixer, cad, reg, val) corb_write(mixer, cad, reg, 0, SI3055_REG_SET_VERB, val)
+
+
+/* TODO: DDB() around debugging messages. */
+
+int hdaudio_si3055_endpoint_init(hdaudio_mixer_t * mixer, int cad)
+{
+	codec_t *codec = mixer->codecs[cad];  /* Modem codec */
+	widget_t *widget;		      /* MFG widget */
+	hdaudio_endpointinfo_t *endpoint;
+	unsigned int a, b;	   /* Used for reading data. */
+	int tmout;		   /* Timeout counter. */
+	
+	cmn_err(CE_CONT, "hdaudio_si3055_endpoint_init got called.\n");
+
+	/* Reset the modem codec. */
+	corb_write(mixer, cad, 0x00, 0, SET_CODEC_RESET, 0);
+	corb_write(mixer, cad, codec->afg, 0, SET_CONVERTER, IDDLE_STREAM << 4);
+	corb_write(mixer, cad, codec->afg, 0, SET_CONVERTER_FORMAT, 0);
+	
+	/* Set 9600Hz as the initial line sampling rate.
+	 * It can be changed later when desired.
+	 */
+	SI3055_REG_SET(mixer, cad, SI3055_LINE_RATE, 9600);
+	
+	/* Assign the "unused" value to the playback and recording
+	 * stream descriptors (ref. HDAudio_03.pdf, page 40).
+	 */
+	SI3055_REG_SET(mixer, cad, SI3055_HDA_STREAMS, 0x0000);
+	
+	/* Write 0x0000 to the Extended Modem Status & Control register
+	 * to power up the modem (ref. si3038.pdf, page 22).
+	 */
+	SI3055_REG_SET(mixer, cad, SI3055_EXT_MODEM_STATUS, 0x0000);
+	
+	/* Wait for the modem to complete power up. The lower 8 bits
+	 * indicate that it is ready (ref. si3038.pdf, page 22).
+	 */
+	tmout = 10;
+	do {
+		SI3055_REG_GET(mixer, cad, SI3055_EXT_MODEM_STATUS, &a, &b);
+		cmn_err(CE_CONT, "si3055: ext modem status: %04x.\n", a);
+		oss_udelay(1000);
+	} while(((a & 0xf) == 0) && --tmout);
+	
+	if((a & 0xf) == 0) {
+		cmn_err(CE_WARN, "Si3055 power up timeout (status: %04x).\n", a);
+	}
+	
+	/* This register contains 0x1fff after reset. We need to set it
+	 * to zero to get the modem working. No corresponding register
+	 * could be found in the Si3038 datasheet.
+	 */
+	SI3055_REG_SET(mixer, cad, SI3055_GPIO_CONFIG, 0x0000);
+
+	/* Program line interface parameters. The register value after
+	 * a reset is 0xF010. Set it to 0x0010 to unmute the analog
+	 * receive and transmit paths.
+	 */
+	SI3055_REG_SET(mixer, cad, SI3055_LINE_CONFIG, 0x0010);
+	
+	/* Setup the stream numbers. */
+	SI3055_REG_SET(mixer, cad, SI3055_HDA_STREAMS, 0xf0f0);
+	
+	/* TODO: We are always enabling Off-Hook, only for testing.
+	 * This code needs to be moved to the ioctl later.
+	 */
+	SI3055_REG_GET(mixer, cad, SI3055_GPIO_PIN_STATUS, &a, &b);
+	cmn_err(CE_CONT, "si3055: gpio: %04x\n", a);
+	a |= 0x1; /* Set Off-Hook bit */
+	SI3055_REG_SET(mixer, cad, SI3055_GPIO_PIN_STATUS, a);
+	SI3055_REG_GET(mixer, cad, SI3055_GPIO_PIN_STATUS, &a, &b);
+	cmn_err(CE_CONT, "si3055: gpio: %04x\n", a);
+		
+	/* Setup the widget info. */
+	widget = &codec->widgets[codec->afg];
+	widget->endpoint = &codec->inendpoints[0];
+	widget->sizes = 0x20000; /* 16 bits */
+	strcpy(widget->name, "modem");
+	
+	/* Setup the output endpoint. */
+	codec->num_outendpoints = 1;
+	endpoint = &codec->outendpoints[0];
+	endpoint->stream_number = endpoint->default_stream_number = 0xf;
+	endpoint->ix = endpoint->stream_number - 1;
+	endpoint->iddle_stream = 0;
+	endpoint->cad = cad;
+	endpoint->base_wid = codec->afg;
+	endpoint->recsrc_wid = endpoint->volume_wid = -1;
+	endpoint->nrates = 3;
+	endpoint->rates[0] = 8000;
+	endpoint->rates[1] = 9600;
+	endpoint->rates[2] = 16000;
+	endpoint->name = widget->name;
+	endpoint->channels = 1;
+	endpoint->is_digital = 0;
+	endpoint->sizemask = widget->sizes;
+	endpoint->fmt_mask = AFMT_S16_LE;
+	endpoint->afg = codec->afg;
+	endpoint->min_rate = 8000;
+	endpoint->max_rate = 16000;
+	
+	/* Setup the input endpoint. */
+	codec->num_inendpoints = 1;
+	endpoint = &codec->inendpoints[0];
+	endpoint->stream_number = endpoint->default_stream_number = 0xf;
+	endpoint->ix = endpoint->stream_number - 1;
+	endpoint->iddle_stream = 0;
+	endpoint->cad = cad;
+	endpoint->base_wid = codec->afg;
+	endpoint->recsrc_wid = endpoint->volume_wid = -1;
+	endpoint->nrates = 3;
+	endpoint->rates[0] = 8000;
+	endpoint->rates[1] = 9600;
+	endpoint->rates[2] = 16000;
+	endpoint->name = widget->name;
+	endpoint->channels = 1;
+	endpoint->is_digital = 0;
+	endpoint->sizemask = widget->sizes;
+	endpoint->fmt_mask = AFMT_S16_LE;
+	endpoint->afg = codec->afg;
+	endpoint->min_rate = 8000;
+	endpoint->max_rate = 16000;
+  
+	return 0;
+}
+
+int hdaudio_si3055_mixer_init(int dev, hdaudio_mixer_t * mixer, int cad, int top_group)
+{
+	return hdaudio_generic_mixer_init(dev, mixer, cad, top_group);
+}
diff -r 7656ac428350 kernel/drv/oss_hdaudio/oss_hdaudio.c
--- a/kernel/drv/oss_hdaudio/oss_hdaudio.c	Wed Oct 29 22:46:59 2008 +0200
+++ b/kernel/drv/oss_hdaudio/oss_hdaudio.c	Tue Nov 04 23:07:49 2008 -0200
@@ -451,8 +451,8 @@
       return portc->channels;
     }
 
-  if (arg < 2)
-    arg = 2;
+  if (arg < 1)
+    arg = 1;
 
   if (arg > adev->max_channels)
   {
@@ -1460,7 +1460,7 @@
 	adev->mixer_dev = devc->mixer_dev;
 	adev->min_rate = 8000;
 	adev->max_rate = 192000;
-	adev->min_channels = 2;
+	adev->min_channels = 1;
 
 	if (output_num == 0)
 	  adev->max_channels = 8;
@@ -1582,7 +1582,7 @@
       adev->mixer_dev = devc->mixer_dev;
       adev->min_rate = 8000;
       adev->max_rate = 192000;
-      adev->min_channels = 2;
+      adev->min_channels = 1;
       adev->max_channels = 2;
       adev->min_block = 128;	/* Hardware limitation */
       adev->min_fragments = 4;	/* Vmix doesn't work without this */
diff -r 7656ac428350 setup/Linux/oss/build/install.sh
--- a/setup/Linux/oss/build/install.sh	Wed Oct 29 22:46:59 2008 +0200
+++ b/setup/Linux/oss/build/install.sh	Tue Nov 04 23:07:49 2008 -0200
@@ -56,12 +56,6 @@
 if ! test -f $OSSLIBDIR/objects/osscore.o
 then
 	echo Error: OSS core module for $REGPARM kernel is not available in $OSSLIBDIR/objects
-	exit 1
-fi
-
-if ! test -f $OSSLIBDIR/modules/oss_ich.o
-then
-	echo Error: OSS driver modules for $REGPARM kernel are not available
 	exit 1
 fi
 
_______________________________________________
oss-devel mailing list
oss-devel@mailman.opensound.com
http://mailman.opensound.com/mailman/listinfo/oss-devel

Reply via email to