[alsa-devel] [PATCH 5/6] compress: add the core file

Nallasellan, Singaravelan singaravelan.nallasellan at intel.com
Fri Dec 2 17:24:02 CET 2011


> +static int snd_compr_open(struct file *f,  struct snd_compr *compr, int
> +type) {
> +	struct snd_compr_file *data;
> +	struct snd_compr_runtime *runtime;
> +	int ret;
> +
> +	mutex_lock(&device_mutex);
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> +	if (!data) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +	data->stream.ops = compr->ops;
NULL check for compr would help here.

> +	data->stream.direction = type;
> +	data->stream.private_data = compr->private_data;
> +	data->stream.device = compr;
> +	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
> +	if (!runtime) {
> +		ret = -ENOMEM;
> +		kfree(data);
> +		goto out;
> +	}
> +	runtime->state = SNDRV_PCM_STATE_OPEN;
> +	init_waitqueue_head(&runtime->sleep);
> +	data->stream.runtime = runtime;
> +	f->private_data = (void *)data;
> +	ret = compr->ops->open(&data->stream);
NULL check for ops would help here.

> +	if (ret) {
> +		kfree(runtime);
> +		kfree(data);
> +		goto out;
> +	}
> +out:
> +	mutex_unlock(&device_mutex);
> +	return ret;
> +}
> +

> +static int snd_compr_free(struct inode *inode, struct file *f) {
> +	struct snd_compr_file *data = f->private_data;
> +	mutex_lock(&device_mutex);
Can we have mutex for each file opened instead of having a single mutex for everything?
I think it is not required to sequence all the streams.

> +	data->stream.ops->free(&data->stream);
> +	kfree(data->stream.runtime->buffer);
> +	kfree(data->stream.runtime);
> +	kfree(data);
> +	mutex_unlock(&device_mutex);
> +	return 0;
> +}
> +
> +static void snd_compr_update_tstamp(struct snd_compr_stream *stream,
> +		struct snd_compr_tstamp *tstamp)
> +{
> +	stream->ops->pointer(stream, tstamp);
How to ensure that the caller always checks for NULL pointer? 

> +	pr_debug("dsp consumed till %d total %d bytes\n",
> +		tstamp->copied_bytes, tstamp->copied_total);
> +	stream->runtime->hw_pointer = tstamp->copied_bytes;
> +	stream->runtime->bytes_copied = tstamp->copied_total; }
> +
> +static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
> +		struct snd_compr_avail *avail)
> +{
> +	long avail_calc; /*this needs to be signed variable */
> +
> +	snd_compr_update_tstamp(stream, &avail->tstamp);
> +
> +	if (stream->runtime->bytes_written == 0 &&
> +			stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
> +		pr_debug("detected init and someone forgot to do a write\n");
> +		return stream->runtime->buffer_size;
> +	}
> +	pr_debug("app wrote %d, DSP consumed %d\n",
> +		stream->runtime->bytes_written, stream->runtime->bytes_copied);
> +	if (stream->runtime->bytes_written == stream->runtime->bytes_copied) {
> +		pr_debug("both pointers are same, returning full avail\n");
> +		return stream->runtime->buffer_size;
> +	}
> +
> +	avail_calc = stream->runtime->buffer_size -
> +		(stream->runtime->app_pointer - stream->runtime->hw_pointer);
> +	pr_debug("calc avail as %ld, app_ptr %d, hw+ptr %d\n", avail_calc,
> +		stream->runtime->app_pointer, stream->runtime->hw_pointer);
> +	if (avail_calc >= stream->runtime->buffer_size)
> +		avail_calc -= stream->runtime->buffer_size;
> +	pr_debug("ret avail as %ld\n", avail_calc);
> +	avail->avail = avail_calc;
> +	return avail_calc;
> +}
> +
> +static size_t snd_compr_get_avail(struct snd_compr_stream *stream) {
> +	struct snd_compr_avail avail;
> +
Can we update the timestamp before calculating the avail? This will allow partial   
Fragment to be written as well.
> +	return snd_compr_calc_avail(stream, &avail); }
> +
> +static int
> +snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long
> +arg) {
> +	struct snd_compr_avail ioctl_avail;
> +
Can we update the timestamp before calculating the avail? This will allow partial   
Fragment to be written as well.

> +	snd_compr_calc_avail(stream, &ioctl_avail);
> +
> +	if (copy_to_user((unsigned long __user *)arg,
> +				&ioctl_avail, sizeof(ioctl_avail)))
> +		return -EFAULT;
> +	return 0;
> +}
> +


> +static ssize_t snd_compr_write(struct file *f, const char __user *buf,
> +		size_t count, loff_t *offset)
> +{
> +
> +	/* while initiating the stream, write should be called before START
> +	 * call, so in setup move state */
> +	if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
> +		stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
Can we implement kind of start threshold similar to PCM path here to initiate the playback?

> +		pr_debug("stream prepared, Houston we are good to go\n");
> +	}
> +
> +	mutex_unlock(&stream->device->lock);
> +	return retval;
> +}


> +static int snd_compr_resume(struct snd_compr_stream *stream) {
> +	int retval;
> +
> +	if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
If the stream is running, I guess it can return 0.

> +		return -EPERM;
> +	retval = stream->ops->trigger(stream,
> SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
> +	if (!retval)
> +		stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
> +	return retval;
> +}
> +
> +static int snd_compr_start(struct snd_compr_stream *stream) {
> +	int retval;
> +
> +	if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
> +		return -EPERM;
> +	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
> +	stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
Set to running state if start is successful.

> +	return retval;
> +}
> +
> +static int snd_compr_stop(struct snd_compr_stream *stream) {
> +	int retval;
> +
> +	if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
> +		return -EPERM;
> +	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
> +	if (!retval) {
> +		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> +		wake_up(&stream->runtime->sleep);
> +	}
> +	return retval;
> +}
> +
> +static int snd_compr_drain(struct snd_compr_stream *stream) {
> +	int retval;
> +
> +	if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED ||
> +			stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
> +		return -EPERM;
What happens if the stream is in running state? This will return -EPERM. It should send DRAIN in this case.

> +	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
> +	if (!retval) {
> +		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> +		wake_up(&stream->runtime->sleep);
> +	}
> +	return retval;
> +}
> +
> +static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned
> +long arg) {
> +	struct snd_compr_file *data = f->private_data;
> +	struct snd_compr_stream *stream;
> +	int retval = -ENOTTY;
> +
> +	BUG_ON(!data);
> +	stream = &data->stream;
> +	BUG_ON(!stream);
> +	mutex_lock(&stream->device->lock);
Simple structure with ioctl functions will reduce lots of code.
It is not anyway doing any specific processing for each ioctl.
> +	switch (_IOC_NR(cmd)) {
> +	case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
> +		put_user(SNDRV_COMPRESS_VERSION,
> +				(int __user *)arg) ? -EFAULT : 0;
> +		break;
> +	case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
> +		retval = snd_compr_get_caps(stream, arg);
> +		break;
> +	case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
> +		retval = snd_compr_get_codec_caps(stream, arg);
> +		break;
> +	case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
> +		retval = snd_compr_set_params(stream, arg);
> +		break;
> +	case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
> +		retval = snd_compr_get_params(stream, arg);
> +		break;
> +	case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
> +		retval = snd_compr_tstamp(stream, arg);
> +		break;
> +	case _IOC_NR(SNDRV_COMPRESS_AVAIL):
> +		retval = snd_compr_ioctl_avail(stream, arg);
> +		break;
> +	case _IOC_NR(SNDRV_COMPRESS_PAUSE):
> +		retval = snd_compr_pause(stream);
> +		break;
> +	case _IOC_NR(SNDRV_COMPRESS_RESUME):
> +		retval = snd_compr_resume(stream);
> +		break;
> +	case _IOC_NR(SNDRV_COMPRESS_START):
> +		retval = snd_compr_start(stream);
> +		break;
> +	case _IOC_NR(SNDRV_COMPRESS_STOP):
> +		retval = snd_compr_stop(stream);
> +		break;
> +	case _IOC_NR(SNDRV_COMPRESS_DRAIN):
> +		cmd = SND_COMPR_TRIGGER_DRAIN;
> +		retval = snd_compr_drain(stream);
> +		break;
> +	}
> +	mutex_unlock(&stream->device->lock);
> +	return retval;
> +}
> +
> +static const struct file_operations snd_compr_file_ops[2] = {
> +	{
> +		.owner =	THIS_MODULE,
> +		.open =		snd_compr_pb_open,
> +		.release =	snd_compr_free,
> +		.write =	snd_compr_write,
> +		.unlocked_ioctl = snd_compr_ioctl,
> +		.mmap =		snd_compr_mmap,
Suggest you to populate it when implemented.
> +		.poll =		snd_compr_pb_poll,
> +	},
> +	{
> +		.owner =	THIS_MODULE,
> +		.open =		snd_compr_cap_open,
> +		.release =	snd_compr_free,
> +		.read =		snd_compr_read,
> +		.unlocked_ioctl = snd_compr_ioctl,
> +		.mmap =		snd_compr_mmap,
> +		.poll =		snd_compr_cap_poll,
> +	}
> +};
> +
> +static int snd_compress_dev_register(struct snd_device *device) {
> +	int ret;
> +	char str[16];
> +	struct snd_compr *compr;
> +	int devtype, cidx;
> +
> +	if (snd_BUG_ON(!device || !device->device_data))
> +		return -ENXIO;
> +	compr = device->device_data;
> +
> +	for (cidx = 0; cidx < 2; cidx++) {
> +		switch (cidx) {
> +		case 0:
> +			if (!compr->pb)
Can we have the device type or direction in this variable. Suggest you not to use
two variables (cap and pb) here.
> +				continue;
> +			sprintf(str, "comprC%iD%ip", compr->card->number,
> +				compr->device);
> +			pr_debug("reg %s for device %s\n", str, compr->name);
> +			devtype = SNDRV_DEVICE_TYPE_COMPR_PLAYBACK;
> +			break;
> +


More information about the Alsa-devel mailing list