예제 #1
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;
}
예제 #2
0
/* 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;
}
예제 #3
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;
}
예제 #4
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;
}
예제 #5
0
/* File Open */
static int fm_v4l2_fops_open(struct file *file)
{
	int ret;

	FMDRV_API_START();

	/* Don't allow multiple open */
	if (radio_disconnected) {
		FM_DRV_ERR("FM device is already opened\n");
		FMDRV_API_EXIT(-EBUSY);
		return -EBUSY;
	}

	/* Request FM Core to link with FM ST */
	ret = fm_core_setup_transport();
	if (ret) {
		FM_DRV_ERR("Unable to setup FM Core transport");
		FMDRV_API_EXIT(ret);
		return ret;
	}
	/* Initialize FM Core */
	ret = fm_core_prepare();
	if (ret) {
		FM_DRV_ERR("Unable to prepare FM CORE");
		FMDRV_API_EXIT(ret);
		return ret;
	}

	FM_DRV_DBG("Load FM RX firmware..");
	/* By default load FM RX firmware */
	ret = fm_core_mode_set(FM_MODE_RX);
	if (ret) {
		FM_DRV_ERR("Unable to load FM RX firmware");
		FMDRV_API_EXIT(ret);
		return ret;
	}
	radio_disconnected = 1;
	FM_DRV_DBG("FM CORE is ready");

	FMDRV_API_EXIT(0);
	return 0;
}
예제 #6
0
/* File Release */
static int fm_v4l2_fops_release(struct file *file)
{
	int ret;

	FMDRV_API_START();

	if (!radio_disconnected) {
		FM_DRV_DBG("FM device already closed,close called again?");
		FMDRV_API_EXIT(0);
		return 0;
	}

	FM_DRV_DBG("Turning off..");
	ret = fm_core_mode_set(FM_MODE_OFF);
	if (ret) {
		FM_DRV_ERR("Unable to turn off the chip");
		FMDRV_API_EXIT(ret);
		return ret;
	}
	/* Request FM Core to unlink from ST driver */
	ret = fm_core_release();
	if (ret) {
		FM_DRV_ERR("FM CORE release failed");
		FMDRV_API_EXIT(ret);
		return ret;
	}

	/* Release FM Core transport */
	ret = fm_core_release_transport();
	if (ret) {
		FM_DRV_ERR("Unable to setup FM Core transport");
		FMDRV_API_EXIT(ret);
		return ret;
	}
	radio_disconnected = 0;
	FM_DRV_DBG("FM CORE released successfully");

	FMDRV_API_EXIT(0);
	return 0;
}
예제 #7
0
/* Called by Shared Transport layer when FM packet is
 * available
 */
static long fm_st_receive(struct sk_buff *skb)
{
	FMDRV_API_START();

	if (skb == NULL) {
		FM_DRV_ERR("Invalid SKB received from ST");
		FMDRV_API_EXIT(-EFAULT);
		return -EFAULT;
	}

	/* Is this FM Channel-8 packet? */
	if (skb->cb[0] != FM_PKT_LOGICAL_CHAN_NUMBER) {
		FM_DRV_ERR("Received SKB(%p) is not FM Channel 8 pkt", skb);
		FMDRV_API_EXIT(-EINVAL);
		return -EINVAL;
	}

	/* One byte is already reserved for Channel-8 in skb,
	 * so prepend skb with Channel-8 packet type byte.
	 */
	memcpy(skb_push(skb, 1), &skb->cb[0], 1);

	if (g_rx_q != NULL && g_rx_task != NULL) {
		/* Queue FM packet for FM RX task */
		skb_queue_tail(g_rx_q, skb);
		tasklet_schedule(g_rx_task);
	} else {
		FM_DRV_ERR
		    ("Invalid RX queue and RX tasklet pointer,puring skb");
		kfree_skb(skb);
		FMDRV_API_EXIT(-EFAULT);
		return -EFAULT;
	}

	FMDRV_API_EXIT(0);
	return 0;
}
예제 #8
0
/* Called from FM Core and FM Char device interface
 * to release FM ST.
 */
int fm_st_release(void)
{
	FMDRV_API_START();

	/* Release FM ST if it is already claimed */
	if (is_fm_st_claimed == FM_ST_CLAIMED) {
		is_fm_st_claimed = FM_ST_NOT_CLAIMED;

		FMDRV_API_EXIT(FM_ST_SUCCESS);
		return FM_ST_SUCCESS;

	}

	FM_DRV_ERR("FM ST is not claimed,called again?");

	FMDRV_API_EXIT(FM_ST_FAILED);
	return FM_ST_FAILED;
}
예제 #9
0
/* Unregister FM Driver from Shared Transport */
int fm_st_unregister(void)
{
	int ret;

	FMDRV_API_START();

	/* Unregister FM Driver from ST */
	ret = st_unregister(8);
	if (ret != ST_SUCCESS) {
		FM_DRV_ERR("st_unregister failed %d", ret);
		FMDRV_API_EXIT(-EBUSY);
		return -EBUSY;
	}

	g_rx_task = NULL;
	g_rx_q = NULL;

	FMDRV_API_EXIT(0);
	return 0;
}
예제 #10
0
/* Called from V4L2 RADIO open function (fm_fops_open()) to
 * register FM driver with Shared Transport
 */
int fm_st_register(struct sk_buff_head *rx_q, struct tasklet_struct *rx_task)
{
	static struct st_proto_s fm_st_proto;
	unsigned long timeleft;
	int ret;

	ret = 0;

	FMDRV_API_START();

	/* Populate FM driver info required by ST */
	memset(&fm_st_proto, 0, sizeof(fm_st_proto));

	/* FM driver ID */
	fm_st_proto.channelid = 8;

	/* Receive function which called from ST */
	fm_st_proto.recv = fm_st_receive;

	/* Packet match function may used in future */
	fm_st_proto.match_packet = NULL;

	/* Callback to be called when registration is pending */
	fm_st_proto.reg_complete_cb = fm_st_registration_completion_cb;

	fm_st_proto.max_frame_size = 255;
	fm_st_proto.header_size = 3;
	fm_st_proto.length_offset = 1;
	fm_st_proto.length_size = 2;	
	fm_st_proto.gpio_id = ST_GPIO_FM;


	/* Register with ST layer */
	ret = st_register(&fm_st_proto);
	if (ret == ST_ERR_PENDING) {
		/* Prepare wait-for-completion handler data structures.
		 * Needed to syncronize this and
		 * fm_st_registration_completion_cb() functions.
		 */
		init_completion(&wait_for_fmdrv_reg_completion);

		/* Reset ST registration callback status flag. This value
		 * will be updated in fm_st_registration_completion_cb()
		 * function whenever it is called from ST driver.
		 */
		streg_cbdata = -EINPROGRESS;

		/* ST is busy with other protocol registration (may be busy with
		 * firmware download). So, wait till the registration callback
		 * (passed as a argument to st_register() function) getting
		 * called from ST.
		 */
		FM_DRV_DBG(" %s waiting for reg completion signal from ST",
			   __func__);

		timeleft =
		    wait_for_completion_timeout(&wait_for_fmdrv_reg_completion,
						FM_ST_REGISTER_TIMEOUT);
		if (!timeleft) {
			FM_DRV_ERR("Timeout(%d sec), didn't get reg"
				   "completion signal from ST",
				   jiffies_to_msecs(FM_ST_REGISTER_TIMEOUT) /
				   1000);
			FMDRV_API_EXIT(-ETIMEDOUT);
			return -ETIMEDOUT;
		}

		/* Is ST registration callback called with ERROR value? */
		if (streg_cbdata != 0) {
			FM_DRV_ERR("ST reg completion CB called with invalid"
				   "status %d", streg_cbdata);
			FMDRV_API_EXIT(-EAGAIN);
			return -EAGAIN;
		}
		ret = 0;
	} else if (ret == ST_ERR_FAILURE) {
		FM_DRV_ERR("st_register failed %d", ret);
		FMDRV_API_EXIT(-EAGAIN);
		return -EAGAIN;
	}

	/* Store Rx Q and Rx tasklet pointers. This pointers should
	 * already initialized by caller
	 */
	g_rx_task = rx_task;
	g_rx_q = rx_q;

	FMDRV_API_EXIT(ret);
	return ret;
}