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; }
/* 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; }
/* 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_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; }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
/* 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; }