/* Read RDS data */
static ssize_t fm_v4l2_fops_read(struct file *file, char __user * buf,
				 size_t count, loff_t *ppos)
{
	unsigned char rds_mode;
	int ret, noof_bytes_copied;
	FMDRV_API_START();

	if (!radio_disconnected) {
		FM_DRV_ERR("FM device is already disconnected\n");
		FMDRV_API_EXIT(-EIO);
		return -EIO;
	}
	/* Turn on RDS mode , if it is disabled */
	ret = fm_core_rx_get_rds_mode(&rds_mode);
	if (ret) {
		FM_DRV_ERR("Unable to read current rds mode");
		FMDRV_API_EXIT(ret);
		return ret;
	}
	if (rds_mode == FM_RX_RDS_DISABLE) {
		ret = fm_core_set_rds_mode(FM_RX_RDS_ENABLE);
		if (ret < 0) {
			FM_DRV_ERR("Unable to enable rds mode");
			FMDRV_API_EXIT(ret);
			return ret;
		}
	}
	/* Copy RDS data from internal buffer to user buffer */
	noof_bytes_copied =
	    fm_core_transfer_rds_from_internal_buff(file, buf, count);

	FMDRV_API_EXIT(noof_bytes_copied);
	return noof_bytes_copied;
}
Example #2
0
/* Forwards FM Packets to Shared Transport */
int fm_st_send(struct sk_buff *skb)
{
	long len;

	FMDRV_API_START();

	if (skb == NULL) {
		FM_DRV_ERR("Invalid skb, can't send");
		FMDRV_API_EXIT(-ENOMEM);
		return -ENOMEM;
	}

	/* Is anyone called without claiming FM ST? */
	if (is_fm_st_claimed == FM_ST_CLAIMED ) {
		/* Forward FM packet(SKB) to ST for the transmission */
		len = st_write(skb);
		if (len < 0) {
			/* Something went wrong in st write , free skb memory */
			kfree_skb(skb);
			FM_DRV_ERR(" ST write failed (%ld)", len);
			FMDRV_API_EXIT(-EAGAIN);
			return -EAGAIN;
		}
	} else {		/* Nobody calimed FM ST */

		kfree_skb(skb);
		FM_DRV_ERR("FM ST is not claimed, Can't send skb");
		FMDRV_API_EXIT(-EAGAIN);
		return -EAGAIN;
	}

	FMDRV_API_EXIT(0);
	return 0;
}
int fm_v4l2_init_video_device(struct fmdrv_ops *fmdev)
{
	FMDRV_API_START();

	/* Allocate new video device */
	fmdev->v4l2dev = video_device_alloc();
	if (!fmdev->v4l2dev) {
		FM_DRV_ERR("Can't allocate video device");
		FMDRV_API_EXIT(-ENOMEM);
		return -ENOMEM;
	}

	/* Setup FM driver's V4L2 properties */
	memcpy(fmdev->v4l2dev, &fm_viddev_template, sizeof(fm_viddev_template));

	video_set_drvdata(fmdev->v4l2dev, fmdev);

	/* Register with V4L2 subsystem as RADIO device */
	if (video_register_device(fmdev->v4l2dev, VFL_TYPE_RADIO, 0)) {
		video_device_release(fmdev->v4l2dev);
		fmdev->v4l2dev = NULL;

		FM_DRV_ERR("Could not register video device");
		FMDRV_API_EXIT(-ENOMEM);
		return -ENOMEM;
	}
	FMDRV_API_EXIT(0);
	return 0;
}
/* Set the value of a control */
static int fm_v4l2_vidioc_s_ctrl(struct file *file, void *priv,
				 struct v4l2_control *ctrl)
{
	int ret;
	FMDRV_API_START();

	switch (ctrl->id) {

	case V4L2_CID_AUDIO_MUTE:	/* set mute */
		ret = fm_core_set_mute_mode((unsigned char)ctrl->value);
		if (ret < 0) {
			FMDRV_API_EXIT(ret);
			return ret;
		}
		break;

	case V4L2_CID_AUDIO_VOLUME:	/* set volume */
		ret = fm_core_rx_set_volume((unsigned short)ctrl->value);
		if (ret < 0) {
			FMDRV_API_EXIT(ret);
			return ret;
		}
		break;
	}
	FMDRV_API_EXIT(0);
	return 0;
}
/* Set tuner attributes */
static int fm_v4l2_vidioc_s_tuner(struct file *file, void *priv,
				  struct v4l2_tuner *tuner)
{
	unsigned short mode;
	int ret;

	FMDRV_API_START();

	if ((tuner->index != 0) ||
	    (tuner->audmode != V4L2_TUNER_MODE_MONO &&
	     tuner->audmode != V4L2_TUNER_MODE_STEREO)) {
		FMDRV_API_EXIT(-EINVAL);
		return -EINVAL;
	}
	/* Map V4L2 stereo/mono macro to our local stereo/mono macro */
	mode = (tuner->audmode == V4L2_TUNER_MODE_STEREO) ?
	    FM_STEREO_MODE : FM_MONO_MODE;
	ret = fm_core_set_stereo_mono(mode);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}

	FMDRV_API_EXIT(0);
	return 0;
}
/* Get the value of a control */
static int fm_v4l2_vidioc_g_ctrl(struct file *file, void *priv,
				 struct v4l2_control *ctrl)
{
	int ret;
	unsigned short curr_vol;
	unsigned char curr_mute_mode;

	FMDRV_API_START();

	switch (ctrl->id) {

	case V4L2_CID_AUDIO_MUTE:	/* get mute mode */
		ret = fm_core_rx_get_mute_mode(&curr_mute_mode);
		if (ret < 0) {
			FMDRV_API_EXIT(ret);
			return ret;
		}
		ctrl->value = curr_mute_mode;
		break;

	case V4L2_CID_AUDIO_VOLUME:	/* get volume */
		ret = fm_core_rx_get_volume(&curr_vol);
		if (ret < 0) {
			FMDRV_API_EXIT(ret);
			return ret;
		}
		ctrl->value = curr_vol;
		break;
	}
	FMDRV_API_EXIT(0);
	return 0;
}
/* Get tuner attributes */
static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv,
				  struct v4l2_tuner *tuner)
{
	unsigned int bottom_frequency;
	unsigned int top_frequency;
	unsigned short stereo_mono_mode;
	unsigned short rssilvl;
	int ret;

	FMDRV_API_START();

	if (tuner->index != 0) {
		FMDRV_API_EXIT(-EINVAL);
		return -EINVAL;
	}
	ret =
	    fm_core_rx_get_currband_lowhigh_freq(&bottom_frequency,
						 &top_frequency);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}
	ret = fm_core_rx_get_stereo_mono(&stereo_mono_mode);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}
	ret = fm_core_rx_get_rssi_level(&rssilvl);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}
	strcpy(tuner->name, "FM");
	tuner->type = V4L2_TUNER_RADIO;
	/* Store rangelow and rangehigh freq in unit of 62.5 KHz */
	tuner->rangelow = (bottom_frequency * 10000) / 625;
	tuner->rangehigh = (top_frequency * 10000) / 625;
	tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
	tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
	tuner->audmode = (stereo_mono_mode ?
			  V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO);

	/* Actual rssi value lies in between -128 to +127.
	 * Convert this range from 0 to 255 by adding +128
	 */
	rssilvl += 128;

	/* Return signal strength value should be within 0 to 65535.
	 * Find out correct signal radio by multiplying (65535/255) = 257
	 */
	tuner->signal = rssilvl * 257;
	tuner->afc = 0;

	FMDRV_API_EXIT(0);
	return 0;
}
int fm_mixer_deinit(struct fmdrv_ops *fmdev)
{
	FMDRV_API_START();

	/* Unregister FM card from ALSA */
	snd_card_free(fmdev->card);

	FMDRV_API_EXIT(0);
	return 0;
}
int fm_v4l2_deinit_video_device(struct fmdrv_ops *fmdev)
{
	FMDRV_API_START();

	/* Unregister RADIO device from V4L2 subsystem */
	video_unregister_device(fmdev->v4l2dev);

	FMDRV_API_EXIT(0);
	return 0;
}
/* Set audio attributes */
static int fm_v4l2_vidioc_s_audio(struct file *file, void *priv,
				  struct v4l2_audio *audio)
{
	FMDRV_API_START();

	if (audio->index != 0) {
		FMDRV_API_EXIT(-EINVAL);
		return -EINVAL;
	}
	FMDRV_API_EXIT(0);
	return 0;
}
static int fm_mixer_rx_rssi_threshold_info(struct snd_kcontrol *kcontrol,
					   struct snd_ctl_elem_info *uinfo)
{
	FMDRV_API_START();

	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
	uinfo->count = 1;
	uinfo->value.integer.min = -16;
	uinfo->value.integer.max = 15;

	FMDRV_API_EXIT(0);
	return 0;
}
/* Get audio attributes */
static int fm_v4l2_vidioc_g_audio(struct file *file, void *priv,
				  struct v4l2_audio *audio)
{
	FMDRV_API_START();

	memset(audio, 0, sizeof(*audio));
	audio->index = 0;
	strcpy(audio->name, "Radio");
	audio->capability = V4L2_AUDCAP_STEREO;

	FMDRV_API_EXIT(0);
	return 0;
}
/* Set tuner or modulator radio frequency */
static int fm_v4l2_vidioc_s_frequency(struct file *file, void *priv,
				      struct v4l2_frequency *freq)
{
	int ret;
	FMDRV_API_START();

	ret = fm_core_set_frequency(freq->frequency);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}
	FMDRV_API_EXIT(0);
	return 0;
}
/* Poll RDS data */
static unsigned int fm_v4l2_fops_poll(struct file *file,
				      struct poll_table_struct *pts)
{
	int ret;
	FMDRV_API_START();

	ret = fm_core_is_rds_data_available(file, pts);
	if (!ret) {
		FMDRV_API_EXIT(POLLIN | POLLRDNORM);
		return POLLIN | POLLRDNORM;
	}

	FMDRV_API_EXIT(0);
	return 0;
}
/* Query device capabilities */
static int fm_v4l2_vidioc_querycap(struct file *file, void *priv,
				   struct v4l2_capability *capability)
{
	FMDRV_API_START();

	strlcpy(capability->driver, FM_DRV_NAME, sizeof(capability->driver));
	strlcpy(capability->card, FM_DRV_CARD_SHORT_NAME,
		sizeof(capability->card));
	sprintf(capability->bus_info, "UART");
	capability->version = FM_DRV_RADIO_VERSION;
	capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
	    V4L2_CAP_RADIO | V4L2_CAP_READWRITE | V4L2_CAP_AUDIO;
	FMDRV_API_EXIT(0);
	return 0;
}
static int fm_mixer_rx_deemphasis_get(struct snd_kcontrol *kcontrol,
				      struct snd_ctl_elem_value *ucontrol)
{
	unsigned short mode;
	int ret;

	FMDRV_API_START();
	ret = fm_core_rx_get_deemphasis_mode(&mode);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}
	ucontrol->value.enumerated.item[0] = mode & 0x1;
	FMDRV_API_EXIT(0);
	return 0;
}
static int fm_mixer_rssi_level_get(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_value *ucontrol)
{
	unsigned short curr_rssi_lvl;
	int ret;
	FMDRV_API_START();

	ret = fm_core_rx_get_rssi_level(&curr_rssi_lvl);
	if (ret)
		return ret;

	ucontrol->value.integer.value[0] = curr_rssi_lvl;

	FMDRV_API_EXIT(0);
	return 0;
}
/* Set hardware frequency seek */
static int fm_v4l2_vidioc_s_hw_freq_seek(struct file *file, void *priv,
					 struct v4l2_hw_freq_seek *seek)
{
	int ret;

	FMDRV_API_START();

	ret = fm_core_rx_seek(seek->seek_upward, seek->wrap_around);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}

	FMDRV_API_EXIT(0);
	return 0;
}
static int fm_mixer_rx_rds_opmode_get(struct snd_kcontrol *kcontrol,
				      struct snd_ctl_elem_value *ucontrol)
{
	unsigned char rds_mode;
	int ret;

	FMDRV_API_START();
	ret = fm_core_rx_get_rds_system(&rds_mode);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}
	ucontrol->value.enumerated.item[0] = rds_mode & 0x1;
	FMDRV_API_EXIT(0);
	return 0;
}
Example #20
0
/* Called from FM Core and FM Char device interface to claim
 * FM ST. Who ever comes first, ownership of FM ST will be
 * given to them.
 */
int fm_st_claim(void)
{
	FMDRV_API_START();

	/* Give ownership of FM ST to first caller */
	if (is_fm_st_claimed == FM_ST_NOT_CLAIMED) {
		is_fm_st_claimed = FM_ST_CLAIMED;

		FMDRV_API_EXIT(FM_ST_SUCCESS);
		return FM_ST_SUCCESS;
	}

	FM_DRV_DBG("FM ST claimed already");

	FMDRV_API_EXIT(FM_ST_FAILED);
	return FM_ST_FAILED;
}
static int fm_mixer_region_get(struct snd_kcontrol *kcontrol,
			       struct snd_ctl_elem_value *ucontrol)
{
	unsigned char region;
	int ret;

	FMDRV_API_START();
	ret = fm_core_region_get(&region);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}
	ucontrol->value.enumerated.item[0] = region & 0x1;

	FMDRV_API_EXIT(0);
	return 0;
}
static int fm_mixer_rfdepend_mute_get(struct snd_kcontrol *kcontrol,
				      struct snd_ctl_elem_value *ucontrol)
{
	unsigned char en_dis;
	int ret;

	FMDRV_API_START();

	ret = fm_core_rx_get_rfdepend_softmute(&en_dis);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}
	ucontrol->value.enumerated.item[0] = en_dis & 0x1;
	FMDRV_API_EXIT(0);
	return 0;
}
/* Write RDS data */
static ssize_t fm_v4l2_fops_write(struct file *file, const char __user * buf,
				  size_t count, loff_t *ppos)
{
	struct tx_rds rds;
	int ret;
	FMDRV_API_START();

	ret = copy_from_user(&rds, buf, sizeof(rds));
	FM_DRV_DBG("(%d)type: %d, text %s, af %d",
		   ret, rds.text_type, rds.text, rds.af_freq);

	fm_core_tx_set_radio_text(rds.text, rds.text_type);
	fm_core_tx_set_af(rds.af_freq);

	FMDRV_API_EXIT(0);
	return 0;
}
int fm_mixer_init(struct fmdrv_ops *fmdev)
{
	int idx;
	int ret;

	FMDRV_API_START();

	/* Allocate new card for FM driver */
	fmdev->card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
				   THIS_MODULE, 0);
	if (!fmdev->card) {
		FM_DRV_ERR("No memory to allocate new card");
		FMDRV_API_EXIT(-ENOMEM);
		return -ENOMEM;
	}
	fmdev->card->private_data = fmdev;

	/* Add FM mixer controls to the card */
	strcpy(fmdev->card->mixername, FM_DRV_MIXER_NAME);
	for (idx = 0; idx < ARRAY_SIZE(snd_fm_controls); idx++) {
		ret = snd_ctl_add(fmdev->card,
				  snd_ctl_new1(&snd_fm_controls[idx], fmdev));
		if (ret < 0) {
			snd_card_free(fmdev->card);
			FM_DRV_ERR("Failed to add mixer controls");
			FMDRV_API_EXIT(ret);
			return ret;
		}
	}

	/* Register FM card with ALSA */
	ret = snd_card_register(fmdev->card);
	if (ret) {
		snd_card_free(fmdev->card);
		FM_DRV_ERR("Failed to register new card");
		FMDRV_API_EXIT(ret);
		return ret;
	}

	strcpy(fmdev->card->driver, FM_DRV_NAME);
	strcpy(fmdev->card->shortname, FM_DRV_CARD_SHORT_NAME);
	sprintf(fmdev->card->longname, FM_DRV_CARD_LONG_NAME);

	FMDRV_API_EXIT(0);
	return 0;
}
static int fm_mixer_rx_af_switch_get(struct snd_kcontrol *kcontrol,
				     struct snd_ctl_elem_value *ucontrol)
{
	unsigned char af_mode;
	int ret;

	FMDRV_API_START();

	ret = fm_core_rx_get_af_switch(&af_mode);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}
	ucontrol->value.enumerated.item[0] = af_mode & 0x1;
	FMDRV_API_EXIT(0);
	return 0;
}
static int fm_mixer_mode_get(struct snd_kcontrol *kcontrol,
			     struct snd_ctl_elem_value *ucontrol)
{
	int ret;
	unsigned char current_fmmode;

	FMDRV_API_START();
	ret = fm_core_mode_get(&current_fmmode);
	if (ret) {
		FMDRV_API_EXIT(ret);
		return ret;
	}
	ucontrol->value.enumerated.item[0] = current_fmmode & 3;

	FMDRV_API_EXIT(0);
	return 0;
}
static int fm_mixer_region_info(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_info *uinfo)
{
	static char *region[] = { "Europe/US", "Japan" };

	FMDRV_API_START();

	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
	uinfo->count = 1;
	uinfo->value.enumerated.items = 2;
	if (uinfo->value.enumerated.item > 1)
		uinfo->value.enumerated.item = 1;
	strcpy(uinfo->value.enumerated.name,
	       region[uinfo->value.enumerated.item]);

	FMDRV_API_EXIT(0);
	return 0;
}
static int fm_mixer_mode_info(struct snd_kcontrol *kcontrol,
			      struct snd_ctl_elem_info *uinfo)
{
	static char *fm_modes[] = { "Off", "Tx", "Rx" };

	FMDRV_API_START();

	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
	uinfo->count = 1;
	uinfo->value.enumerated.items = 3;
	if (uinfo->value.enumerated.item > 2)
		uinfo->value.enumerated.item = 2;
	strcpy(uinfo->value.enumerated.name,
	       fm_modes[uinfo->value.enumerated.item]);

	FMDRV_API_EXIT(0);
	return 0;
}
static int fm_mixer_rx_af_switch_info(struct snd_kcontrol *kcontrol,
				      struct snd_ctl_elem_info *uinfo)
{
	static char *af_mode[] = { "Off", "On" };

	FMDRV_API_START();

	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
	uinfo->count = 1;
	uinfo->value.enumerated.items = 2;
	if (uinfo->value.enumerated.item > 1)
		uinfo->value.enumerated.item = 1;
	strcpy(uinfo->value.enumerated.name,
	       af_mode[uinfo->value.enumerated.item]);

	FMDRV_API_EXIT(0);
	return 0;
}
static int fm_mixer_rx_deemphasis_info(struct snd_kcontrol *kcontrol,
				       struct snd_ctl_elem_info *uinfo)
{
	static char *filter_mode[] = { "50 us", "75 us" };

	FMDRV_API_START();

	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
	uinfo->count = 1;
	uinfo->value.enumerated.items = 2;
	if (uinfo->value.enumerated.item > 1)
		uinfo->value.enumerated.item = 1;
	strcpy(uinfo->value.enumerated.name,
	       filter_mode[uinfo->value.enumerated.item]);

	FMDRV_API_EXIT(0);
	return 0;
}