/* * Driver for Skympeg dummy frontend * Written by Mark Watson and very heavily based on the * dummy frontend written by Emard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 #include #include #include #include "dvb_frontend.h" #include "dvb_skympeg_fe.h" static DECLARE_MUTEX (sem); static int locked = 1; static int lastFreq = -1; static int sameFreqCount = 0; struct dvb_skympeg_fe_state { struct dvb_frontend_ops ops; struct dvb_frontend frontend; }; static void writeFrontend(struct dvb_frontend_parameters * arg) { int length_write; char buffer[250]; struct file * f = NULL; mm_segment_t orig_fs; locked = 0; /* We are calling filp_open() again to obtain a file object pointer to the file to be written. */ f = filp_open("/frontend", O_WRONLY | O_CREAT, 0600); if (!f || !f->f_op || !f->f_op->write) { printk("WARNING: File (write) object is a null pointer!!!\n"); return; } f->f_pos = 0; /* We repeat the same process of temporarily modifying the addr_limit field of the current task_struct. This will allow us to use a buffer whose address is in kernel space. */ orig_fs = get_fs(); set_fs(KERNEL_DS); int len = 0; len+=sprintf(buffer, "%d", arg->frequency); buffer[len] = 0; length_write = f->f_op->write(f, buffer, len, &f->f_pos); set_fs(orig_fs); /* Release the file object pointer. */ fput(f); } static void readFrontend(void) { struct file * f = NULL; mm_segment_t orig_fs; char buffer[256]; int length_read =0; f = filp_open("/frontend_reply", O_RDONLY, 00); if (!f || !f->f_op || !f->f_op->read) { printk("WARNING: File (read) object is a null pointer!!!\n"); } f->f_pos = 0; /* Use get_fs() and set_fs() to temporarily modify the addr_limit field of the current task_struct. This will allow the read to use a buffer whose address is not in use space. */ orig_fs = get_fs(); set_fs(KERNEL_DS); length_read = f->f_op->read(f, buffer, sizeof(buffer), &f->f_pos); buffer[length_read] = 0; printk("Buffer:%s\n", buffer); if (strncmp(buffer, "LOCK", 4) == 0) { locked = 1; printk("LOCKED!\n"); } set_fs(orig_fs); /* Release the file object pointer. */ fput(f); } static int dvb_skympeg_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) { *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK; if (locked) { *status|= FE_HAS_SYNC; } else { *status|= FE_TIMEDOUT; } return 0; } static int dvb_skympeg_fe_read_ber(struct dvb_frontend* fe, u32* ber) { *ber = 0; return 0; } static int dvb_skympeg_fe_read_signal_strength(struct dvb_frontend* fe, u16* strength) { *strength = 0; return 0; } static int dvb_skympeg_fe_read_snr(struct dvb_frontend* fe, u16* snr) { *snr = 0; return 0; } static int dvb_skympeg_fe_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { *ucblocks = 0; return 0; } static int dvb_skympeg_fe_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { return 0; } static int dvb_skympeg_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { down(&sem); printk("FE_SET_FRONTEND\n"); if (params->frequency == lastFreq) { if (sameFreqCount>= 3) { up(&sem); return -EINVAL; } sameFreqCount++; } else { sameFreqCount = 0; lastFreq = params->frequency; } printk("writeFrontend\n"); writeFrontend(params); // send request to userspace printk("readFrontend\n"); readFrontend(); // get result back... printk("result\n"); up(&sem); if (!locked) // failed? { printk("RETURNING FAILED\n"); return -EINVAL; } return 0; } static int dvb_skympeg_fe_sleep(struct dvb_frontend* fe) { return 0; } static int dvb_skympeg_fe_init(struct dvb_frontend* fe) { return 0; } static int dvb_skympeg_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { return 0; } static int dvb_skympeg_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) { return 0; } static void dvb_skympeg_fe_release(struct dvb_frontend* fe) { struct dvb_skympeg_fe_state* state = (struct dvb_skympeg_fe_state*) fe->demodulator_priv; kfree(state); } static struct dvb_frontend_ops dvb_skympeg_fe_qpsk_ops; struct dvb_frontend* dvb_skympeg_fe_attach() { struct dvb_skympeg_fe_state* state = NULL; /* allocate memory for the internal state */ state = (struct dvb_skympeg_fe_state*) kmalloc(sizeof(struct dvb_skympeg_fe_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ memcpy(&state->ops, &dvb_skympeg_fe_qpsk_ops, sizeof(struct dvb_frontend_ops)); /* create dvb_frontend */ state->frontend.ops = &state->ops; state->frontend.demodulator_priv = state; return &state->frontend; error: if (state) kfree(state); return NULL; } static struct dvb_frontend_ops dvb_skympeg_fe_qpsk_ops = { /*FIXME - take a lot of this from current network state!*/ .info = { .name = "Sky MPEG DVB-S", .type = FE_QPSK, .frequency_min = 950000, .frequency_max = 2150000, .frequency_stepsize = 250, /* kHz for QPSK frontends */ .frequency_tolerance = 29500, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .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 }, .release = dvb_skympeg_fe_release, .init = dvb_skympeg_fe_init, .sleep = dvb_skympeg_fe_sleep, .set_frontend = dvb_skympeg_fe_set_frontend, .get_frontend = dvb_skympeg_fe_get_frontend, .read_status = dvb_skympeg_fe_read_status, .read_ber = dvb_skympeg_fe_read_ber, .read_signal_strength = dvb_skympeg_fe_read_signal_strength, .read_snr = dvb_skympeg_fe_read_snr, .read_ucblocks = dvb_skympeg_fe_read_ucblocks, .set_voltage = dvb_skympeg_fe_set_voltage, .set_tone = dvb_skympeg_fe_set_tone, }; MODULE_DESCRIPTION("DVB Sky MPEG Frontend"); MODULE_AUTHOR("Mark"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(dvb_skympeg_fe_attach);