diff options
author | Michael Krufky <mkrufky@linuxtv.org> | 2008-05-22 18:29:20 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-07-20 07:20:47 -0300 |
commit | 854470606070d91955f03a7dca3a8024fc2a2540 (patch) | |
tree | 858216ab89c0f23b1f6d23782b4275112550b499 /drivers/media/dvb/siano/smsdvb.c | |
parent | 7d18d2e91b5495ed0b99c83d595a56e25521a111 (diff) | |
download | blackbird-op-linux-854470606070d91955f03a7dca3a8024fc2a2540.tar.gz blackbird-op-linux-854470606070d91955f03a7dca3a8024fc2a2540.zip |
V4L/DVB (8272): sms1xxx: move driver from media/mdtv/ to media/dvb/siano/
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/dvb/siano/smsdvb.c')
-rw-r--r-- | drivers/media/dvb/siano/smsdvb.c | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c new file mode 100644 index 000000000000..e1a14a812c25 --- /dev/null +++ b/drivers/media/dvb/siano/smsdvb.c @@ -0,0 +1,425 @@ +/* + * Driver for the Siano SMS10xx USB dongle + * + * author: Anatoly Greenblat + * + * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> + +#include "smscoreapi.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct list_head g_smsdvb_clients; +kmutex_t g_smsdvb_clientslock; + +int smsdvb_onresponse(void *context, smscore_buffer_t *cb) +{ + smsdvb_client_t *client = (smsdvb_client_t *) context; + SmsMsgHdr_ST *phdr = (SmsMsgHdr_ST *)(((u8*) cb->p) + cb->offset); + + switch(phdr->msgType) + { + case MSG_SMS_DVBT_BDA_DATA: + dvb_dmx_swfilter(&client->demux, (u8*)(phdr + 1), cb->size - sizeof(SmsMsgHdr_ST)); + break; + + case MSG_SMS_RF_TUNE_RES: + complete(&client->tune_done); + break; + + case MSG_SMS_GET_STATISTICS_RES: + { + SmsMsgStatisticsInfo_ST* p = (SmsMsgStatisticsInfo_ST*)(phdr + 1); + + if (p->Stat.IsDemodLocked) + { + client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + client->fe_snr = p->Stat.SNR; + client->fe_ber = p->Stat.BER; + + if (p->Stat.InBandPwr < -95) + client->fe_signal_strength = 0; + else if (p->Stat.InBandPwr > -29) + client->fe_signal_strength = 100; + else + client->fe_signal_strength = (p->Stat.InBandPwr + 95) * 3 / 2; + } + else + { + client->fe_status = 0; + client->fe_snr = + client->fe_ber = + client->fe_signal_strength = 0; + } + + complete(&client->stat_done); + break; + } + } + + smscore_putbuffer(client->coredev, cb); + + return 0; +} + +void smsdvb_unregister_client(smsdvb_client_t* client) +{ + // must be called under clientslock + + list_del(&client->entry); + + smscore_unregister_client(client->smsclient); + dvb_unregister_frontend(&client->frontend); + dvb_dmxdev_release(&client->dmxdev); + dvb_dmx_release(&client->demux); + dvb_unregister_adapter(&client->adapter); + kfree(client); +} + +void smsdvb_onremove(void *context) +{ + kmutex_lock(&g_smsdvb_clientslock); + + smsdvb_unregister_client((smsdvb_client_t*) context); + + kmutex_unlock(&g_smsdvb_clientslock); +} + +static int smsdvb_start_feed(struct dvb_demux_feed *feed) +{ + smsdvb_client_t *client = container_of(feed->demux, smsdvb_client_t, demux); + SmsMsgData_ST PidMsg; + + printk("%s add pid %d(%x)\n", __FUNCTION__, feed->pid, feed->pid); + + PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + PidMsg.xMsgHeader.msgDstId = HIF_TASK; + PidMsg.xMsgHeader.msgFlags = 0; + PidMsg.xMsgHeader.msgType = MSG_SMS_ADD_PID_FILTER_REQ; + PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); + PidMsg.msgData[0] = feed->pid; + + return smsclient_sendrequest(client->smsclient, &PidMsg, sizeof(PidMsg)); +} + +static int smsdvb_stop_feed(struct dvb_demux_feed *feed) +{ + smsdvb_client_t *client = container_of(feed->demux, smsdvb_client_t, demux); + SmsMsgData_ST PidMsg; + + printk("%s remove pid %d(%x)\n", __FUNCTION__, feed->pid, feed->pid); + + PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + PidMsg.xMsgHeader.msgDstId = HIF_TASK; + PidMsg.xMsgHeader.msgFlags = 0; + PidMsg.xMsgHeader.msgType = MSG_SMS_REMOVE_PID_FILTER_REQ; + PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); + PidMsg.msgData[0] = feed->pid; + + return smsclient_sendrequest(client->smsclient, &PidMsg, sizeof(PidMsg)); +} + +static int smsdvb_sendrequest_and_wait(smsdvb_client_t *client, void* buffer, size_t size, struct completion *completion) +{ + int rc = smsclient_sendrequest(client->smsclient, buffer, size); + if (rc < 0) + return rc; + + return wait_for_completion_timeout(completion, msecs_to_jiffies(2000)) ? 0 : -ETIME; +} + +static int smsdvb_send_statistics_request(smsdvb_client_t *client) +{ + SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, DVBT_BDA_CONTROL_MSG_ID, HIF_TASK, sizeof(SmsMsgHdr_ST), 0 }; + return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), &client->stat_done); +} + +static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) +{ + smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend); + int rc = smsdvb_send_statistics_request(client); + + if (!rc) + *stat = client->fe_status; + + return rc; +} + +static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend); + int rc = smsdvb_send_statistics_request(client); + + if (!rc) + *ber = client->fe_ber; + + return rc; +} + +static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend); + int rc = smsdvb_send_statistics_request(client); + + if (!rc) + *strength = client->fe_signal_strength; + + return rc; +} + +static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend); + int rc = smsdvb_send_statistics_request(client); + + if (!rc) + *snr = client->fe_snr; + + return rc; +} + +static int smsdvb_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) +{ + printk("%s\n", __FUNCTION__); + + tune->min_delay_ms = 400; + tune->step_size = 250000; + tune->max_drift = 0; + return 0; +} + +static int smsdvb_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +{ + smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend); + + struct + { + SmsMsgHdr_ST Msg; + u32 Data[3]; + } Msg; + + Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + Msg.Msg.msgDstId = HIF_TASK; + Msg.Msg.msgFlags = 0; + Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ; + Msg.Msg.msgLength = sizeof(Msg); + Msg.Data[0] = fep->frequency; + Msg.Data[2] = 12000000; + + printk("%s freq %d band %d\n", __FUNCTION__, fep->frequency, fep->u.ofdm.bandwidth); + + switch(fep->u.ofdm.bandwidth) + { + case BANDWIDTH_8_MHZ: Msg.Data[1] = BW_8_MHZ; break; + case BANDWIDTH_7_MHZ: Msg.Data[1] = BW_7_MHZ; break; + case BANDWIDTH_6_MHZ: Msg.Data[1] = BW_6_MHZ; break; +// case BANDWIDTH_5_MHZ: Msg.Data[1] = BW_5_MHZ; break; + case BANDWIDTH_AUTO: return -EOPNOTSUPP; + default: return -EINVAL; + } + + return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), &client->tune_done); +} + +static int smsdvb_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +{ + smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend); + + printk("%s\n", __FUNCTION__); + + // todo: + memcpy(fep, &client->fe_params, sizeof(struct dvb_frontend_parameters)); + return 0; +} + +static void smsdvb_release(struct dvb_frontend *fe) +{ + // do nothing +} + +static struct dvb_frontend_ops smsdvb_fe_ops = { + .info = { + .name = "Siano Mobile Digital SMS10xx", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 250000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = smsdvb_release, + + .set_frontend = smsdvb_set_frontend, + .get_frontend = smsdvb_get_frontend, + .get_tune_settings = smsdvb_get_tune_settings, + + .read_status = smsdvb_read_status, + .read_ber = smsdvb_read_ber, + .read_signal_strength = smsdvb_read_signal_strength, + .read_snr = smsdvb_read_snr, +}; + +int smsdvb_hotplug(smscore_device_t *coredev, struct device* device, int arrival) +{ + smsclient_params_t params; + smsdvb_client_t* client; + int rc; + + // device removal handled by onremove callback + if (!arrival) + return 0; + + if (smscore_get_device_mode(coredev) != 4) + { + rc = smscore_set_device_mode(coredev, 4); + if (rc < 0) + return rc; + } + + client = kzalloc(sizeof(smsdvb_client_t), GFP_KERNEL); + if (!client) + { + printk(KERN_INFO "%s kmalloc() failed\n", __FUNCTION__); + return -ENOMEM; + } + + // register dvb adapter + rc = dvb_register_adapter(&client->adapter, "Siano Digital Receiver", THIS_MODULE, device, adapter_nr); + if (rc < 0) + { + printk("%s dvb_register_adapter() failed %d\n", __func__, rc); + goto adapter_error; + } + + // init dvb demux + client->demux.dmx.capabilities = DMX_TS_FILTERING; + client->demux.filternum = 32; // todo: nova ??? + client->demux.feednum = 32; + client->demux.start_feed = smsdvb_start_feed; + client->demux.stop_feed = smsdvb_stop_feed; + + rc = dvb_dmx_init(&client->demux); + if (rc < 0) + { + printk("%s dvb_dmx_init failed %d\n\n", __FUNCTION__, rc); + goto dvbdmx_error; + } + + // init dmxdev + client->dmxdev.filternum = 32; + client->dmxdev.demux = &client->demux.dmx; + client->dmxdev.capabilities = 0; + + rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter); + if (rc < 0) + { + printk("%s dvb_dmxdev_init failed %d\n", __FUNCTION__, rc); + goto dmxdev_error; + } + + // init and register frontend + memcpy(&client->frontend.ops, &smsdvb_fe_ops, sizeof(struct dvb_frontend_ops)); + + rc = dvb_register_frontend(&client->adapter, &client->frontend); + if (rc < 0) + { + printk("%s frontend registration failed %d\n", __FUNCTION__, rc); + goto frontend_error; + } + + params.initial_id = 0; + params.data_type = MSG_SMS_DVBT_BDA_DATA; + params.onresponse_handler = smsdvb_onresponse; + params.onremove_handler = smsdvb_onremove; + params.context = client; + + rc = smscore_register_client(coredev, ¶ms, &client->smsclient); + if (rc < 0) + { + printk(KERN_INFO "%s smscore_register_client() failed %d\n", __FUNCTION__, rc); + goto client_error; + } + + client->coredev = coredev; + + init_completion(&client->tune_done); + init_completion(&client->stat_done); + + kmutex_lock(&g_smsdvb_clientslock); + + list_add(&client->entry, &g_smsdvb_clients); + + kmutex_unlock(&g_smsdvb_clientslock); + + printk(KERN_INFO "%s success\n", __FUNCTION__); + + return 0; + +client_error: + dvb_unregister_frontend(&client->frontend); + +frontend_error: + dvb_dmxdev_release(&client->dmxdev); + +dmxdev_error: + dvb_dmx_release(&client->demux); + +dvbdmx_error: + dvb_unregister_adapter(&client->adapter); + +adapter_error: + kfree(client); + return rc; +} + +int smsdvb_register(void) +{ + int rc; + + INIT_LIST_HEAD(&g_smsdvb_clients); + kmutex_init(&g_smsdvb_clientslock); + + rc = smscore_register_hotplug(smsdvb_hotplug); + + printk(KERN_INFO "%s\n", __FUNCTION__); + + return rc; +} + +void smsdvb_unregister(void) +{ + smscore_unregister_hotplug(smsdvb_hotplug); + + kmutex_lock(&g_smsdvb_clientslock); + + while (!list_empty(&g_smsdvb_clients)) + smsdvb_unregister_client((smsdvb_client_t*) g_smsdvb_clients.next); + + kmutex_unlock(&g_smsdvb_clientslock); + +} + |