+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;