Some details about Syllable audio drivers ----------------------------------------- An audio driver for syllable needs to fulfill the following requirements: - It has to be placed in /system/drivers/dev/audio. - It has to create node(s) in /dev/audio/. - It has to implement the IOCTL_GET_USERSPACE_DRIVER ioctl for every node and return the name of the userspace driver. - Like every driver for the media system, the userspace driver has to implement the init_media_addon() call and return a os::MediaAddon object (which can then export multiple os::MediaOutput and os::MediaInput objects). The parameter passed to init_media_addon() is the device node path. The rest of the implementation is private to the driver but there is some code that can help you: Generic streams implementation ------------------------------ The files audio.c/audio.h contain a generic streams implementation. To use it add a GenericStream_s structure to your own stream structure. Then you need to fill the structure: *pDriverData - Set this to your private stream structure. It will be passed to all called driver functions. *pfGetFragSize() - Returns the fragment size of the stream. *pfGetFragNumber() - Returns the number of fragments of the stream. *pfGetBuffer() - Returns a pointer to the soundcard buffer. *pfGetCurrentPosition() - Returns the byte position in the current fragment(!) of the stream. *pfStart - Start the engine. *pfStop - Stop the engine. Call generic_stream_init() in your open() function and generic_stream_free() in your close() function. Now you can use the generic_stream_write()/generic_stream_read() function from your own write()/read() function. You need to call generic_stream_fragment_processed() for every played buffer fragment, probably from the interrupt handler. ac97 kernel driver ------------------ If the card uses an ac97 codec you can use the ac97 kernel driver code. Add a AC97AudioDriver_s structure to your private driver structure and fill the following parts: *pDriverData - Set this to your private driver structure. It will be passed to all called driver functions. *pfWait - Wait for the codec to become ready. *pfWrite - Write data to one register of the codec. *pfRead - Read data from one register of the codec. Then call ac97_initialize() with a unique id string and the number of codecs of the card. There are various other ac97 functions that you can use (see ac97audio.h), the most important one is probably ac97_set_rate() to set one samplerate register. ac97 userspace driver --------------------- To use the ac97 userspace driver your driver needs to implement the followinging ioctls: - AC97_GET_CARD_INFO: Fill the AC97CardInfo_s structure and return it - AC97_SET_FORMAT: Set the format specified by the AC97Format_s structure - AC97_GET_DELAY: Return the not-played number of bytes in the buffer. You can use the value returned by generic_stream_get_delay() here. - AC97_GET_BUFFER_SIZE: Returns the total soundcard buffer size. - AC97_CLEAR: Stop the engine and reset the buffer pointer to the start of the buffer. Call generic_stream_clear() from your implementation if you use the generic streams code. - Pass all other ioctls to the ac97_ioctl() function. The ac97 userspace driver expects that you create a device node for every hardware stream. Use another driver as an example -------------------------------- The easiest way to create a new driver is to take a present one like the i8xx driver and then replace the hardware specific code, especially if your card uses an ac97 codec. A few words about porting alsa drivers -------------------------------------- Here is some information about where you can find the necessary information in the alsa drivers. The function names are based on the i8xx/vt82xx/hda syllable drivers and it is assumed that you use the generic streams code. hw_check_format(): The snd_pcm_hardware and snd_pcm_hw_constraint_list structures should give you some ideas about the number of supported channels and samplerates. The xxx_playback_open() or xxx_pcm_open() calls might supply additional information. hw_set_format(): Search for xxx_prepare() functions and the functions they call. Do not forget to call the ac97 functions if you use the ac97 code. Also load the dma buffer or descriptor table pointer to the hardware. hw_create_buffers(): Most of this code is not hw specific. Check the maximum buffer and fragment size of the card. If your card uses a descriptor table just adjust the entry format (hw_sgd_table) and fill it correctly. hw_get_frag_size()/hw_get_frag_number()/hw_get_buffer(): No hardware specific code required. hw_get_current_positition(): See xxx_pcm_pointer(). Make sure you return the position in the current fragment. The i8xx/vt82xx cards are examples for having different registers for the current fragment and the current position inside the fragment, hd-audio cards are examples for having a register that returns the position in the whole buffer. hw_start()/hw_stop(): See xxx_trigger(). hw_clear(): This function is a bit difficult to implement. You need to stop the engine and reset the pointer to the start of the buffer without resetting the format. This can be difficult if your hardware does not have a direct way to set the buffer position. hw_open()/hw_close()/hw_read()/hw_write(): See the i8xx/vt82xx/hda driver for this. No hardware specific code required. hw_ioctl(): If your card uses ac97 then you can take the code from the i8xx/vt82xx drivers. Otherwise you have to create your own interface to the userspace driver. hw_init_stream(): Not hardware specific. hw_interrupt()/hw_stream_interrupt(): See xxx_interrupt() for this. The current audio drivers call hw_stream_interrupt() from hw_interrupt() for every stream that needs attention. Call generic_stream_fragment_processed() for every processed fragment. Use the current fragment register if your hardware has it (see i8xx) or calculate the current fragment if the hardware can return the current position in the whole buffer (see hda). hw_init(): See xxx_probe() and the called functions. Check the i8xx driver as an example about how to initialize the parts that are not hardware specific. hw_uninit(): Nothing should be required here. hw_suspend()/hw_resume(): See xxx_suspend()/xxx_resume() in the alsa driver. hw_release(): Check the i8xx driver.