Commit d75097d0 authored by Michael Hunold's avatar Michael Hunold Committed by Linus Torvalds

[PATCH] DVB: core update

- [DVB] remove non-linux compatibility stuff from dvb_functions.  rest in
  peace.

- [DVB] remove home-brewn dvb-i2c stuff. rest in peace.

- [DVB] convert MODULE_PARM() to module_param()

- [DVB] convert dvb_delay() to mdelay()

- [DVB] convert C++ comments to C comments

- [DVB] dvb_ca_en50221: fix for matrix CAMs from Sjoerd Simons, use c99
  initializers, Fix for aston CAM read timeout problems, Moved CAM CTRL IF
  reset to a better place, better debugging with multiple cards (Sjoerd
  Simons)

- [DVB] dvb-frontend: patch by Wolfgang Fritz: suppress spurious events
  during tuning, Do not allow write (and related) ioctls when frontend is
  opened RDONLY, Properly lock the frontend module on open/close, patch by
  Christopher Pascoe: remove bogus up(fe->sem) on fe thread exit, patch by
  Christopher Pascoe: remove bogus up(fe->sem) on fe thread exit

- [DVB] dvb-demux: using spin_lock instead of spin_lock_irq caused a race
  condition between irq/tasklet and user space task

- [DVB] dvb-core: add sysfs/udev support using "class_simple", prevent Oops
  when PES filter is set with invalid pes_type, protect feed_list with
  spin_locks
Signed-off-by: default avatarMichael Hunold <hunold@linuxtv.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 67bbef2c
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
# #
dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \ dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \
dvb_ca_en50221.o dvb_functions.o dvb_frontend.o \ dvb_ca_en50221.o dvb_frontend.o \
dvb_i2c.o dvb_net.o dvb_ksyms.o dvb_ringbuffer.o dvb_net.o dvb_ksyms.o dvb_ringbuffer.o
obj-$(CONFIG_DVB_CORE) += dvb-core.o obj-$(CONFIG_DVB_CORE) += dvb-core.o
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/ioctl.h> #include <linux/ioctl.h>
...@@ -33,10 +34,11 @@ ...@@ -33,10 +34,11 @@
#include <asm/system.h> #include <asm/system.h>
#include "dmxdev.h" #include "dmxdev.h"
#include "dvb_functions.h"
MODULE_PARM(debug,"i"); static int debug;
static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
#define dprintk if (debug) printk #define dprintk if (debug) printk
......
...@@ -32,16 +32,19 @@ ...@@ -32,16 +32,19 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <asm/semaphore.h> #include <linux/rwsem.h>
#include <asm/atomic.h>
#include "dvb_ca_en50221.h" #include "dvb_ca_en50221.h"
#include "dvb_functions.h"
#include "dvb_ringbuffer.h" #include "dvb_ringbuffer.h"
static int dvb_ca_en50221_debug = 0; static int dvb_ca_en50221_debug;
module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644);
MODULE_PARM_DESC(cam_debug, "enable verbose debug messages");
#define dprintk if (dvb_ca_en50221_debug) printk #define dprintk if (dvb_ca_en50221_debug) printk
#define INIT_TIMEOUT_SECS 5 #define INIT_TIMEOUT_SECS 5
...@@ -108,7 +111,7 @@ struct dvb_ca_slot { ...@@ -108,7 +111,7 @@ struct dvb_ca_slot {
int link_buf_size; int link_buf_size;
/* semaphore for syncing access to slot structure */ /* semaphore for syncing access to slot structure */
struct semaphore sem; struct rw_semaphore sem;
/* buffer for incoming packets */ /* buffer for incoming packets */
struct dvb_ringbuffer rx_buffer; struct dvb_ringbuffer rx_buffer;
...@@ -199,7 +202,6 @@ static u8* findstr(u8* haystack, int hlen, u8* needle, int nlen) ...@@ -199,7 +202,6 @@ static u8* findstr(u8* haystack, int hlen, u8* needle, int nlen)
static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private* ca, int slot) static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private* ca, int slot)
{ {
int slot_status; int slot_status;
int status;
int cam_present_now; int cam_present_now;
int cam_changed; int cam_changed;
...@@ -209,9 +211,7 @@ static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private* ca, int slot) ...@@ -209,9 +211,7 @@ static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private* ca, int slot)
} }
/* poll mode */ /* poll mode */
if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
slot_status = ca->pub->poll_slot_status(ca->pub, slot); slot_status = ca->pub->poll_slot_status(ca->pub, slot);
up(&ca->slot_info[slot].sem);
cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1: 0; cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1: 0;
cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1: 0; cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1: 0;
...@@ -277,7 +277,7 @@ static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private* ca, int slot, u8 ...@@ -277,7 +277,7 @@ static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private* ca, int slot, u8
} }
/* wait for a bit */ /* wait for a bit */
dvb_delay(1); msleep(1);
} }
dprintk("%s failed timeout:%lu\n", __FUNCTION__, jiffies - start); dprintk("%s failed timeout:%lu\n", __FUNCTION__, jiffies - start);
...@@ -306,10 +306,6 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private* ca, int slot) ...@@ -306,10 +306,6 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private* ca, int slot)
/* we'll be determining these during this function */ /* we'll be determining these during this function */
ca->slot_info[slot].da_irq_supported = 0; ca->slot_info[slot].da_irq_supported = 0;
/* reset the link interface. Note CAM IRQs are disabled */
if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, CMDREG_RS)) != 0) return ret;
if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ/10)) != 0) return ret;
/* set the host link buffer size temporarily. it will be overwritten with the /* set the host link buffer size temporarily. it will be overwritten with the
* real negotiated size later. */ * real negotiated size later. */
ca->slot_info[slot].link_buf_size = 2; ca->slot_info[slot].link_buf_size = 2;
...@@ -360,6 +356,13 @@ static int dvb_ca_en50221_read_tuple(struct dvb_ca_private* ca, int slot, ...@@ -360,6 +356,13 @@ static int dvb_ca_en50221_read_tuple(struct dvb_ca_private* ca, int slot,
/* grab the next tuple length and type */ /* grab the next tuple length and type */
if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0) return _tupleType; if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0) return _tupleType;
if (_tupleType == 0xff) {
dprintk("END OF CHAIN TUPLE type:0x%x\n", _tupleType);
*address += 2;
*tupleType = _tupleType;
*tupleLength = 0;
return 0;
}
if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address+2)) < 0) return _tupleLength; if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address+2)) < 0) return _tupleLength;
_address += 4; _address += 4;
...@@ -452,8 +455,8 @@ static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private* ca, int slot) ...@@ -452,8 +455,8 @@ static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private* ca, int slot)
/* is it a version we support? */ /* is it a version we support? */
if (strncmp(dvb_str + 8, "1.00", 4)) { if (strncmp(dvb_str + 8, "1.00", 4)) {
printk("dvb_ca: Unsupported DVB CAM module version %c%c%c%c\n", printk("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n",
dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]); ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]);
return -EINVAL; return -EINVAL;
} }
...@@ -550,25 +553,22 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu ...@@ -550,25 +553,22 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu
dprintk ("%s\n", __FUNCTION__); dprintk ("%s\n", __FUNCTION__);
/* acquire the slot */
if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
/* check if we have space for a link buf in the rx_buffer */ /* check if we have space for a link buf in the rx_buffer */
if (ebuf == NULL) { if (ebuf == NULL) {
if (dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer) < int buf_free;
(ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
down_read(&ca->slot_info[slot].sem);
buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer);
up_read(&ca->slot_info[slot].sem);
if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
status = -EAGAIN; status = -EAGAIN;
goto exit; goto exit;
} }
} }
/* reset the interface if there's been a tx error */ /* check if there is data available */
if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit; if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
if (status & STATUSREG_TXERR) {
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
status = -EIO;
goto exit;
}
if (!(status & STATUSREG_DA)) { if (!(status & STATUSREG_DA)) {
/* no data */ /* no data */
status = 0; status = 0;
...@@ -584,20 +584,20 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu ...@@ -584,20 +584,20 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu
/* check it will fit */ /* check it will fit */
if (ebuf == NULL) { if (ebuf == NULL) {
if (bytes_read > ca->slot_info[slot].link_buf_size) { if (bytes_read > ca->slot_info[slot].link_buf_size) {
printk("dvb_ca: CAM tried to send a buffer larger than the link buffer size!\n"); printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size!\n", ca->dvbdev->adapter->num);
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
status = -EIO; status = -EIO;
goto exit; goto exit;
} }
if (bytes_read < 2) { if (bytes_read < 2) {
printk("dvb_ca: CAM sent a buffer that was less than 2 bytes!\n"); printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n", ca->dvbdev->adapter->num);
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
status = -EIO; status = -EIO;
goto exit; goto exit;
} }
} else { } else {
if (bytes_read > ecount) { if (bytes_read > ecount) {
printk("dvb_ca: CAM tried to send a buffer larger than the ecount size!\n"); printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n", ca->dvbdev->adapter->num);
status = -EIO; status = -EIO;
goto exit; goto exit;
} }
...@@ -612,20 +612,25 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu ...@@ -612,20 +612,25 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu
buf[i] = status; buf[i] = status;
} }
/* check for read error (RE should now go to 0) */ /* check for read error (RE should now be 0) */
if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit; if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
if (status & STATUSREG_RE) { if (status & STATUSREG_RE) {
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
status = -EIO; status = -EIO;
goto exit; goto exit;
} }
/* OK, add it to the receive buffer, or copy into external buffer if supplied */ /* OK, add it to the receive buffer, or copy into external buffer if supplied */
if (ebuf == NULL) { if (ebuf == NULL) {
down_read(&ca->slot_info[slot].sem);
dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read); dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read);
up_read(&ca->slot_info[slot].sem);
} else { } else {
memcpy(ebuf, buf, bytes_read); memcpy(ebuf, buf, bytes_read);
} }
dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot, buf[0], (buf[1] & 0x80) == 0, bytes_read);
/* wake up readers when a last_fragment is received */ /* wake up readers when a last_fragment is received */
if ((buf[1] & 0x80) == 0x00) { if ((buf[1] & 0x80) == 0x00) {
wake_up_interruptible(&ca->wait_queue); wake_up_interruptible(&ca->wait_queue);
...@@ -634,7 +639,6 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu ...@@ -634,7 +639,6 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu
status = bytes_read; status = bytes_read;
exit: exit:
up(&ca->slot_info[slot].sem);
return status; return status;
} }
...@@ -662,19 +666,9 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu ...@@ -662,19 +666,9 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu
// sanity check // sanity check
if (bytes_write > ca->slot_info[slot].link_buf_size) return -EINVAL; if (bytes_write > ca->slot_info[slot].link_buf_size) return -EINVAL;
/* acquire the slot */ /* check if interface is actually waiting for us to read from it, or if a read is in progress */
if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
/* reset the interface if there's been a tx error */
if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exitnowrite; if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exitnowrite;
if (status & STATUSREG_TXERR) { if (status & (STATUSREG_DA|STATUSREG_RE)) {
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
status = -EIO;
goto exitnowrite;
}
/* check if interface is actually waiting for us to read from it */
if (status & STATUSREG_DA) {
status = -EAGAIN; status = -EAGAIN;
goto exitnowrite; goto exitnowrite;
} }
...@@ -702,16 +696,18 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu ...@@ -702,16 +696,18 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu
/* check for write error (WE should now be 0) */ /* check for write error (WE should now be 0) */
if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit; if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
if (status & STATUSREG_WE) { if (status & STATUSREG_WE) {
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
status = -EIO; status = -EIO;
goto exit; goto exit;
} }
status = bytes_write; status = bytes_write;
dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot, buf[0], (buf[1] & 0x80) == 0, bytes_write);
exit: exit:
ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN); ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
exitnowrite: exitnowrite:
up(&ca->slot_info[slot].sem);
return status; return status;
} }
...@@ -729,16 +725,14 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu ...@@ -729,16 +725,14 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu
*/ */
static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private* ca, int slot) static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private* ca, int slot)
{ {
int status;
dprintk ("%s\n", __FUNCTION__); dprintk ("%s\n", __FUNCTION__);
if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status; down_write(&ca->slot_info[slot].sem);
ca->pub->slot_shutdown(ca->pub, slot); ca->pub->slot_shutdown(ca->pub, slot);
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
if (ca->slot_info[slot].rx_buffer.data) vfree(ca->slot_info[slot].rx_buffer.data); if (ca->slot_info[slot].rx_buffer.data) vfree(ca->slot_info[slot].rx_buffer.data);
ca->slot_info[slot].rx_buffer.data = NULL; ca->slot_info[slot].rx_buffer.data = NULL;
up(&ca->slot_info[slot].sem); up_write(&ca->slot_info[slot].sem);
/* need to wake up all processes to check if they're now /* need to wake up all processes to check if they're now
trying to write to a defunct CAM */ trying to write to a defunct CAM */
...@@ -821,10 +815,7 @@ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* pubca, int slot) ...@@ -821,10 +815,7 @@ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* pubca, int slot)
break; break;
case DVB_CA_SLOTSTATE_RUNNING: case DVB_CA_SLOTSTATE_RUNNING:
flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS); if (ca->open) dvb_ca_en50221_read_data(ca, slot, NULL, 0);
if (flags & STATUSREG_DA) {
dvb_ca_en50221_thread_wakeup(ca);
}
break; break;
} }
} }
...@@ -934,7 +925,11 @@ static int dvb_ca_en50221_thread(void* data) ...@@ -934,7 +925,11 @@ static int dvb_ca_en50221_thread(void* data)
/* setup kernel thread */ /* setup kernel thread */
snprintf(name, sizeof(name), "kdvb-ca-%i:%i", ca->dvbdev->adapter->num, ca->dvbdev->id); snprintf(name, sizeof(name), "kdvb-ca-%i:%i", ca->dvbdev->adapter->num, ca->dvbdev->id);
dvb_kernel_thread_setup(name);
lock_kernel ();
daemonize (name);
sigfillset (&current->blocked);
unlock_kernel ();
/* choose the correct initial delay */ /* choose the correct initial delay */
dvb_ca_en50221_thread_update_delay(ca); dvb_ca_en50221_thread_update_delay(ca);
...@@ -984,7 +979,7 @@ static int dvb_ca_en50221_thread(void* data) ...@@ -984,7 +979,7 @@ static int dvb_ca_en50221_thread(void* data)
case DVB_CA_SLOTSTATE_WAITREADY: case DVB_CA_SLOTSTATE_WAITREADY:
if (time_after(jiffies, ca->slot_info[slot].timeout)) { if (time_after(jiffies, ca->slot_info[slot].timeout)) {
printk("dvb_ca: PC card did not respond :(\n"); printk("dvb_ca adaptor %d: PC card did not respond :(\n", ca->dvbdev->adapter->num);
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
dvb_ca_en50221_thread_update_delay(ca); dvb_ca_en50221_thread_update_delay(ca);
break; break;
...@@ -994,13 +989,19 @@ static int dvb_ca_en50221_thread(void* data) ...@@ -994,13 +989,19 @@ static int dvb_ca_en50221_thread(void* data)
case DVB_CA_SLOTSTATE_VALIDATE: case DVB_CA_SLOTSTATE_VALIDATE:
if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) { if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) {
printk("dvb_ca: Invalid PC card inserted :(\n"); printk("dvb_ca adapter %d: Invalid PC card inserted :(\n", ca->dvbdev->adapter->num);
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
dvb_ca_en50221_thread_update_delay(ca); dvb_ca_en50221_thread_update_delay(ca);
break; break;
} }
if (dvb_ca_en50221_set_configoption(ca, slot) != 0) { if (dvb_ca_en50221_set_configoption(ca, slot) != 0) {
printk("dvb_ca: Unable to initialise CAM :(\n"); printk("dvb_ca adapter %d: Unable to initialise CAM :(\n", ca->dvbdev->adapter->num);
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
dvb_ca_en50221_thread_update_delay(ca);
break;
}
if (ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, CMDREG_RS) != 0) {
printk("dvb_ca adapter %d: Unable to reset CAM IF\n", ca->dvbdev->adapter->num);
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
dvb_ca_en50221_thread_update_delay(ca); dvb_ca_en50221_thread_update_delay(ca);
break; break;
...@@ -1015,7 +1016,7 @@ static int dvb_ca_en50221_thread(void* data) ...@@ -1015,7 +1016,7 @@ static int dvb_ca_en50221_thread(void* data)
case DVB_CA_SLOTSTATE_WAITFR: case DVB_CA_SLOTSTATE_WAITFR:
if (time_after(jiffies, ca->slot_info[slot].timeout)) { if (time_after(jiffies, ca->slot_info[slot].timeout)) {
printk("dvb_ca: DVB CAM did not respond :(\n"); printk("dvb_ca adapter %d: DVB CAM did not respond :(\n", ca->dvbdev->adapter->num);
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
dvb_ca_en50221_thread_update_delay(ca); dvb_ca_en50221_thread_update_delay(ca);
break; break;
...@@ -1030,7 +1031,7 @@ static int dvb_ca_en50221_thread(void* data) ...@@ -1030,7 +1031,7 @@ static int dvb_ca_en50221_thread(void* data)
case DVB_CA_SLOTSTATE_LINKINIT: case DVB_CA_SLOTSTATE_LINKINIT:
if (dvb_ca_en50221_link_init(ca, slot) != 0) { if (dvb_ca_en50221_link_init(ca, slot) != 0) {
printk("dvb_ca: DVB CAM link initialisation failed :(\n"); printk("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n", ca->dvbdev->adapter->num);
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
dvb_ca_en50221_thread_update_delay(ca); dvb_ca_en50221_thread_update_delay(ca);
break; break;
...@@ -1038,7 +1039,7 @@ static int dvb_ca_en50221_thread(void* data) ...@@ -1038,7 +1039,7 @@ static int dvb_ca_en50221_thread(void* data)
rxbuf = vmalloc(RX_BUFFER_SIZE); rxbuf = vmalloc(RX_BUFFER_SIZE);
if (rxbuf == NULL) { if (rxbuf == NULL) {
printk("dvb_ca: Unable to allocate CAM rx buffer :(\n"); printk("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n", ca->dvbdev->adapter->num);
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
dvb_ca_en50221_thread_update_delay(ca); dvb_ca_en50221_thread_update_delay(ca);
break; break;
...@@ -1048,12 +1049,16 @@ static int dvb_ca_en50221_thread(void* data) ...@@ -1048,12 +1049,16 @@ static int dvb_ca_en50221_thread(void* data)
ca->pub->slot_ts_enable(ca->pub, slot); ca->pub->slot_ts_enable(ca->pub, slot);
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_RUNNING; ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_RUNNING;
dvb_ca_en50221_thread_update_delay(ca); dvb_ca_en50221_thread_update_delay(ca);
printk("dvb_ca: DVB CAM detected and initialised successfully\n"); printk("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n", ca->dvbdev->adapter->num);
break; break;
case DVB_CA_SLOTSTATE_RUNNING: case DVB_CA_SLOTSTATE_RUNNING:
if (!ca->open) break; if (!ca->open) break;
// no need to poll if the CAM supports IRQs
if (ca->slot_info[slot].da_irq_supported) break;
// poll mode
pktcount = 0; pktcount = 0;
while(dvb_ca_en50221_read_data(ca, slot, NULL, 0) > 0) { while(dvb_ca_en50221_read_data(ca, slot, NULL, 0) > 0) {
if (!ca->open) break; if (!ca->open) break;
...@@ -1196,7 +1201,7 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file, const char __user *buf ...@@ -1196,7 +1201,7 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file, const char __user *buf
int status; int status;
char fragbuf[HOST_LINK_BUF_SIZE]; char fragbuf[HOST_LINK_BUF_SIZE];
int fragpos = 0; int fragpos = 0;
size_t fraglen; int fraglen;
unsigned long timeout; unsigned long timeout;
int written; int written;
...@@ -1233,7 +1238,7 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file, const char __user *buf ...@@ -1233,7 +1238,7 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file, const char __user *buf
} }
if (status != -EAGAIN) goto exit; if (status != -EAGAIN) goto exit;
dvb_delay(1); msleep(1);
} }
if (!written) { if (!written) {
status = -EIO; status = -EIO;
...@@ -1257,7 +1262,7 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* resu ...@@ -1257,7 +1262,7 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* resu
int slot; int slot;
int slot_count = 0; int slot_count = 0;
int idx; int idx;
size_t fraglen; int fraglen;
int connection_id = -1; int connection_id = -1;
int found = 0; int found = 0;
u8 hdr[2]; u8 hdr[2];
...@@ -1266,7 +1271,7 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* resu ...@@ -1266,7 +1271,7 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* resu
while((slot_count < ca->slot_count) && (!found)) { while((slot_count < ca->slot_count) && (!found)) {
if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) goto nextslot; if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) goto nextslot;
if ((*result = down_interruptible(&ca->slot_info[slot].sem)) != 0) return 1; down_read(&ca->slot_info[slot].sem);
idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen); idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
while(idx != -1) { while(idx != -1) {
...@@ -1281,7 +1286,7 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* resu ...@@ -1281,7 +1286,7 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* resu
idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen); idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
} }
if (!found) up(&ca->slot_info[slot].sem); if (!found) up_read(&ca->slot_info[slot].sem);
nextslot: nextslot:
slot = (slot + 1) % ca->slot_count; slot = (slot + 1) % ca->slot_count;
...@@ -1341,7 +1346,7 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf, size_ ...@@ -1341,7 +1346,7 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf, size_
pktlen = 2; pktlen = 2;
do { do {
if (idx == -1) { if (idx == -1) {
printk("dvb_ca: BUG: read packet ended before last_fragment encountered\n"); printk("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n", ca->dvbdev->adapter->num);
status = -EIO; status = -EIO;
goto exit; goto exit;
} }
...@@ -1378,7 +1383,7 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf, size_ ...@@ -1378,7 +1383,7 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf, size_
status = pktlen; status = pktlen;
exit: exit:
up(&ca->slot_info[slot].sem); up_read(&ca->slot_info[slot].sem);
return status; return status;
} }
...@@ -1406,7 +1411,9 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file) ...@@ -1406,7 +1411,9 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
for(i=0; i< ca->slot_count; i++) { for(i=0; i< ca->slot_count; i++) {
if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) { if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
down_write(&ca->slot_info[i].sem);
dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer); dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer);
up_write(&ca->slot_info[i].sem);
} }
} }
...@@ -1464,7 +1471,7 @@ static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait) ...@@ -1464,7 +1471,7 @@ static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
dprintk ("%s\n", __FUNCTION__); dprintk ("%s\n", __FUNCTION__);
if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) { if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
up(&ca->slot_info[slot].sem); up_read(&ca->slot_info[slot].sem);
mask |= POLLIN; mask |= POLLIN;
} }
...@@ -1475,32 +1482,30 @@ static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait) ...@@ -1475,32 +1482,30 @@ static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
poll_wait(file, &ca->wait_queue, wait); poll_wait(file, &ca->wait_queue, wait);
if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) { if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
up(&ca->slot_info[slot].sem); up_read(&ca->slot_info[slot].sem);
mask |= POLLIN; mask |= POLLIN;
} }
return mask; return mask;
} }
static struct file_operations dvb_ca_fops = { static struct file_operations dvb_ca_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.read = dvb_ca_en50221_io_read, .read = dvb_ca_en50221_io_read,
.write = dvb_ca_en50221_io_write, .write = dvb_ca_en50221_io_write,
.ioctl = dvb_ca_en50221_io_ioctl, .ioctl = dvb_ca_en50221_io_ioctl,
.open = dvb_ca_en50221_io_open, .open = dvb_ca_en50221_io_open,
.release = dvb_ca_en50221_io_release, .release= dvb_ca_en50221_io_release,
.poll = dvb_ca_en50221_io_poll, .poll = dvb_ca_en50221_io_poll,
}; };
static struct dvb_device dvbdev_ca = { static struct dvb_device dvbdev_ca = {
.users = 1, .users = 1,
.readers = 1, .readers= 1,
.writers = 1, .writers= 1,
.fops = &dvb_ca_fops, .fops = &dvb_ca_fops,
}; };
/* ******************************************************************************** */ /* ******************************************************************************** */
/* Initialisation/shutdown functions */ /* Initialisation/shutdown functions */
...@@ -1558,7 +1563,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* ...@@ -1558,7 +1563,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221*
ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE; ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE;
atomic_set(&ca->slot_info[i].camchange_count, 0); atomic_set(&ca->slot_info[i].camchange_count, 0);
ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED; ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
init_MUTEX(&ca->slot_info[i].sem); init_rwsem(&ca->slot_info[i].sem);
} }
if (signal_pending(current)) { if (signal_pending(current)) {
...@@ -1604,7 +1609,7 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221* pubca) ...@@ -1604,7 +1609,7 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221* pubca)
/* shutdown the thread if there was one */ /* shutdown the thread if there was one */
if (ca->thread_pid) { if (ca->thread_pid) {
if (kill_proc(ca->thread_pid, 0, 1) == -ESRCH) { if (kill_proc(ca->thread_pid, 0, 1) == -ESRCH) {
printk("dvb_ca_release: thread PID %d already died\n", ca->thread_pid); printk("dvb_ca_release adapter %d: thread PID %d already died\n", ca->dvbdev->adapter->num, ca->thread_pid);
} else { } else {
ca->exit = 1; ca->exit = 1;
mb(); mb();
...@@ -1622,6 +1627,3 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221* pubca) ...@@ -1622,6 +1627,3 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221* pubca)
pubca->private = NULL; pubca->private = NULL;
} }
MODULE_PARM(dvb_ca_en50221_debug,"i");
MODULE_PARM_DESC(dvb_ca_en50221_debug, "enable verbose debug messages");
...@@ -42,6 +42,9 @@ ...@@ -42,6 +42,9 @@
/* Structure describing a CA interface */ /* Structure describing a CA interface */
struct dvb_ca_en50221 { struct dvb_ca_en50221 {
/* NOTE: the read_*, write_* and poll_slot_status functions must use locks as
* they may be called from several threads at once */
/* functions for accessing attribute memory on the CAM */ /* functions for accessing attribute memory on the CAM */
int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address); int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address);
int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value); int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value);
......
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "dvb_demux.h" #include "dvb_demux.h"
#include "dvb_functions.h"
#define NOBUFS #define NOBUFS
/* /*
...@@ -570,24 +569,30 @@ static int dvb_demux_feed_find(struct dvb_demux_feed *feed) ...@@ -570,24 +569,30 @@ static int dvb_demux_feed_find(struct dvb_demux_feed *feed)
static void dvb_demux_feed_add(struct dvb_demux_feed *feed) static void dvb_demux_feed_add(struct dvb_demux_feed *feed)
{ {
spin_lock_irq(&feed->demux->lock);
if (dvb_demux_feed_find(feed)) { if (dvb_demux_feed_find(feed)) {
printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n", printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n",
__FUNCTION__, feed->type, feed->state, feed->pid); __FUNCTION__, feed->type, feed->state, feed->pid);
return; goto out;
} }
list_add(&feed->list_head, &feed->demux->feed_list); list_add(&feed->list_head, &feed->demux->feed_list);
out:
spin_unlock_irq(&feed->demux->lock);
} }
static void dvb_demux_feed_del(struct dvb_demux_feed *feed) static void dvb_demux_feed_del(struct dvb_demux_feed *feed)
{ {
spin_lock_irq(&feed->demux->lock);
if (!(dvb_demux_feed_find(feed))) { if (!(dvb_demux_feed_find(feed))) {
printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n", printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n",
__FUNCTION__, feed->type, feed->state, feed->pid); __FUNCTION__, feed->type, feed->state, feed->pid);
return; goto out;
} }
list_del(&feed->list_head); list_del(&feed->list_head);
out:
spin_unlock_irq(&feed->demux->lock);
} }
static int dmx_ts_feed_set (struct dmx_ts_feed* ts_feed, u16 pid, int ts_type, static int dmx_ts_feed_set (struct dmx_ts_feed* ts_feed, u16 pid, int ts_type,
...@@ -789,7 +794,7 @@ static int dvbdmx_release_ts_feed(struct dmx_demux *dmx, struct dmx_ts_feed *ts_ ...@@ -789,7 +794,7 @@ static int dvbdmx_release_ts_feed(struct dmx_demux *dmx, struct dmx_ts_feed *ts_
feed->pid = 0xffff; feed->pid = 0xffff;
if (feed->ts_type & TS_DECODER) if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER)
demux->pesfilter[feed->pes_type] = NULL; demux->pesfilter[feed->pes_type] = NULL;
up(&demux->mutex); up(&demux->mutex);
......
/* /*
* dvb-core.c: DVB core driver * dvb_frontend.c: DVB frontend tuning interface/thread
*
* *
* Copyright (C) 1999-2001 Ralph Metzler * Copyright (C) 1999-2001 Ralph Metzler
* Marcus Metzler * Marcus Metzler
...@@ -31,13 +32,33 @@ ...@@ -31,13 +32,33 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/list.h> #include <linux/list.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include "dvb_frontend.h" #include "dvb_frontend.h"
#include "dvbdev.h" #include "dvbdev.h"
#include "dvb_functions.h"
static int dvb_frontend_debug;
static int dvb_shutdown_timeout = 5;
static int dvb_override_frequency_bending;
static int dvb_force_auto_inversion;
static int dvb_override_tune_delay;
static int do_frequency_bending;
module_param_named(frontend_debug, dvb_frontend_debug, int, 0644);
MODULE_PARM_DESC(dvb_frontend_debug, "Turn on/off frontend core debugging (default:off).");
module_param(dvb_shutdown_timeout, int, 0444);
MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware");
module_param(dvb_override_frequency_bending, int, 0444);
MODULE_PARM_DESC(dvb_override_frequency_bending, "0: normal (default), 1: never use frequency bending, 2: always use frequency bending");
module_param(dvb_force_auto_inversion, int, 0444);
MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always");
module_param(dvb_override_tune_delay, int, 0444);
MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt");
#define dprintk if (dvb_frontend_debug) printk
#define FESTATE_IDLE 1 #define FESTATE_IDLE 1
#define FESTATE_RETUNE 2 #define FESTATE_RETUNE 2
...@@ -66,17 +87,6 @@ ...@@ -66,17 +87,6 @@
* FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again. * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again.
*/ */
static int dvb_frontend_debug = 0;
static int dvb_shutdown_timeout = 5;
static int dvb_override_frequency_bending = 0;
static int dvb_force_auto_inversion = 0;
static int dvb_override_tune_delay = 0;
static int do_frequency_bending = 0;
#define dprintk if (dvb_frontend_debug) printk
#define MAX_EVENT 8 #define MAX_EVENT 8
struct dvb_fe_events { struct dvb_fe_events {
...@@ -95,6 +105,7 @@ struct dvb_frontend_data { ...@@ -95,6 +105,7 @@ struct dvb_frontend_data {
struct dvb_device *dvbdev; struct dvb_device *dvbdev;
struct dvb_frontend_parameters parameters; struct dvb_frontend_parameters parameters;
struct dvb_fe_events events; struct dvb_fe_events events;
struct module *module;
struct semaphore sem; struct semaphore sem;
struct list_head list_head; struct list_head list_head;
wait_queue_head_t wait_queue; wait_queue_head_t wait_queue;
...@@ -174,7 +185,7 @@ static void dvb_bend_frequency (struct dvb_frontend_data *this_fe, int recursive ...@@ -174,7 +185,7 @@ static void dvb_bend_frequency (struct dvb_frontend_data *this_fe, int recursive
{ {
struct list_head *entry; struct list_head *entry;
int stepsize = this_fe->info->frequency_stepsize; int stepsize = this_fe->info->frequency_stepsize;
int this_fe_adap_num = this_fe->frontend.i2c->adapter->num; int this_fe_adap_num = this_fe->frontend.dvb_adapter->num;
int frequency; int frequency;
if (!stepsize || recursive > 10) { if (!stepsize || recursive > 10) {
...@@ -198,7 +209,7 @@ static void dvb_bend_frequency (struct dvb_frontend_data *this_fe, int recursive ...@@ -198,7 +209,7 @@ static void dvb_bend_frequency (struct dvb_frontend_data *this_fe, int recursive
fe = list_entry (entry, struct dvb_frontend_data, list_head); fe = list_entry (entry, struct dvb_frontend_data, list_head);
if (fe->frontend.i2c->adapter->num != this_fe_adap_num) if (fe->frontend.dvb_adapter->num != this_fe_adap_num)
continue; continue;
f = fe->parameters.frequency; f = fe->parameters.frequency;
...@@ -233,13 +244,10 @@ static void dvb_call_frontend_notifiers (struct dvb_frontend_data *fe, ...@@ -233,13 +244,10 @@ static void dvb_call_frontend_notifiers (struct dvb_frontend_data *fe,
dprintk ("%s\n", __FUNCTION__); dprintk ("%s\n", __FUNCTION__);
if (((s ^ fe->status) & FE_HAS_LOCK) && (s & FE_HAS_LOCK)) if (((s ^ fe->status) & FE_HAS_LOCK) && (s & FE_HAS_LOCK))
dvb_delay (fe->info->notifier_delay); msleep (fe->info->notifier_delay);
fe->status = s; fe->status = s;
if (!(s & FE_HAS_LOCK) && (fe->info->caps & FE_CAN_MUTE_TS))
return;
/** /**
* now tell the Demux about the TS status changes... * now tell the Demux about the TS status changes...
*/ */
...@@ -333,8 +341,8 @@ static void dvb_frontend_init (struct dvb_frontend_data *fe) ...@@ -333,8 +341,8 @@ static void dvb_frontend_init (struct dvb_frontend_data *fe)
{ {
struct dvb_frontend *frontend = &fe->frontend; struct dvb_frontend *frontend = &fe->frontend;
dprintk ("DVB: initialising frontend %i:%i (%s)...\n", dprintk ("DVB: initialising frontend %i (%s)...\n",
frontend->i2c->adapter->num, frontend->i2c->id, frontend->dvb_adapter->num,
fe->info->name); fe->info->name);
dvb_frontend_internal_ioctl (frontend, FE_INIT, NULL); dvb_frontend_internal_ioctl (frontend, FE_INIT, NULL);
...@@ -371,25 +379,26 @@ static int dvb_frontend_autotune(struct dvb_frontend_data *fe, int check_wrapped ...@@ -371,25 +379,26 @@ static int dvb_frontend_autotune(struct dvb_frontend_data *fe, int check_wrapped
int original_inversion = fe->parameters.inversion; int original_inversion = fe->parameters.inversion;
u32 original_frequency = fe->parameters.frequency; u32 original_frequency = fe->parameters.frequency;
// are we using autoinversion? /* are we using autoinversion? */
autoinversion = ((!(fe->info->caps & FE_CAN_INVERSION_AUTO)) && (fe->parameters.inversion == INVERSION_AUTO)); autoinversion = ((!(fe->info->caps & FE_CAN_INVERSION_AUTO)) &&
(fe->parameters.inversion == INVERSION_AUTO));
// setup parameters correctly /* setup parameters correctly */
while(!ready) { while(!ready) {
// calculate the lnb_drift /* calculate the lnb_drift */
fe->lnb_drift = fe->auto_step * fe->step_size; fe->lnb_drift = fe->auto_step * fe->step_size;
// wrap the auto_step if we've exceeded the maximum drift /* wrap the auto_step if we've exceeded the maximum drift */
if (fe->lnb_drift > fe->max_drift) { if (fe->lnb_drift > fe->max_drift) {
fe->auto_step = 0; fe->auto_step = 0;
fe->auto_sub_step = 0; fe->auto_sub_step = 0;
fe->lnb_drift = 0; fe->lnb_drift = 0;
} }
// perform inversion and +/- zigzag /* perform inversion and +/- zigzag */
switch(fe->auto_sub_step) { switch(fe->auto_sub_step) {
case 0: case 0:
// try with the current inversion and current drift setting /* try with the current inversion and current drift setting */
ready = 1; ready = 1;
break; break;
...@@ -418,35 +427,36 @@ static int dvb_frontend_autotune(struct dvb_frontend_data *fe, int check_wrapped ...@@ -418,35 +427,36 @@ static int dvb_frontend_autotune(struct dvb_frontend_data *fe, int check_wrapped
default: default:
fe->auto_step++; fe->auto_step++;
fe->auto_sub_step = -1; // it'll be incremented to 0 in a moment fe->auto_sub_step = -1; /* it'll be incremented to 0 in a moment */
break; break;
} }
if (!ready) fe->auto_sub_step++; if (!ready) fe->auto_sub_step++;
} }
// if this attempt would hit where we started, indicate a complete iteration has occurred /* if this attempt would hit where we started, indicate a complete
if ((fe->auto_step == fe->started_auto_step) && (fe->auto_sub_step == 0) && check_wrapped) { * iteration has occurred */
if ((fe->auto_step == fe->started_auto_step) &&
(fe->auto_sub_step == 0) && check_wrapped) {
return 1; return 1;
} }
// perform frequency bending if necessary /* perform frequency bending if necessary */
if ((dvb_override_frequency_bending != 1) && do_frequency_bending) if ((dvb_override_frequency_bending != 1) && do_frequency_bending)
dvb_bend_frequency(fe, 0); dvb_bend_frequency(fe, 0);
// instrumentation dprintk("%s: drift:%i bending:%i inversion:%i auto_step:%i "
dprintk("%s: drift:%i bending:%i inversion:%i auto_step:%i auto_sub_step:%i started_auto_step:%i\n", "auto_sub_step:%i started_auto_step:%i\n",
__FUNCTION__, fe->lnb_drift, fe->bending, fe->inversion, fe->auto_step, fe->auto_sub_step, __FUNCTION__, fe->lnb_drift, fe->bending, fe->inversion,
fe->started_auto_step); fe->auto_step, fe->auto_sub_step, fe->started_auto_step);
// set the frontend itself /* set the frontend itself */
fe->parameters.frequency += fe->lnb_drift + fe->bending; fe->parameters.frequency += fe->lnb_drift + fe->bending;
if (autoinversion) fe->parameters.inversion = fe->inversion; if (autoinversion) fe->parameters.inversion = fe->inversion;
dvb_frontend_internal_ioctl (&fe->frontend, FE_SET_FRONTEND, &fe->parameters); dvb_frontend_internal_ioctl (&fe->frontend, FE_SET_FRONTEND, &fe->parameters);
fe->parameters.frequency = original_frequency; fe->parameters.frequency = original_frequency;
fe->parameters.inversion = original_inversion; fe->parameters.inversion = original_inversion;
// normal return
fe->auto_sub_step++; fe->auto_sub_step++;
return 0; return 0;
} }
...@@ -490,10 +500,13 @@ static int dvb_frontend_thread (void *data) ...@@ -490,10 +500,13 @@ static int dvb_frontend_thread (void *data)
dprintk ("%s\n", __FUNCTION__); dprintk ("%s\n", __FUNCTION__);
snprintf (name, sizeof(name), "kdvb-fe-%i:%i", snprintf (name, sizeof(name), "kdvb-fe-%i",
fe->frontend.i2c->adapter->num, fe->frontend.i2c->id); fe->frontend.dvb_adapter->num);
dvb_kernel_thread_setup (name); lock_kernel ();
daemonize (name);
sigfillset (&current->blocked);
unlock_kernel ();
dvb_call_frontend_notifiers (fe, 0); dvb_call_frontend_notifiers (fe, 0);
dvb_frontend_init (fe); dvb_frontend_init (fe);
...@@ -511,65 +524,70 @@ static int dvb_frontend_thread (void *data) ...@@ -511,65 +524,70 @@ static int dvb_frontend_thread (void *data)
if (down_interruptible (&fe->sem)) if (down_interruptible (&fe->sem))
break; break;
// if we've got no parameters, just keep idling /* if we've got no parameters, just keep idling */
if (fe->state & FESTATE_IDLE) { if (fe->state & FESTATE_IDLE) {
delay = 3*HZ; delay = 3*HZ;
quality = 0; quality = 0;
continue; continue;
} }
// get the frontend status /* get the frontend status */
if (fe->state & FESTATE_RETUNE) {
s = 0;
} else {
dvb_frontend_internal_ioctl (&fe->frontend, FE_READ_STATUS, &s); dvb_frontend_internal_ioctl (&fe->frontend, FE_READ_STATUS, &s);
if (s != fe->status) if (s != fe->status) {
dvb_frontend_add_event (fe, s); dvb_frontend_add_event (fe, s);
}
// if we're not tuned, and we have a lock, move to the TUNED state }
/* if we're not tuned, and we have a lock, move to the TUNED state */
if ((fe->state & FESTATE_WAITFORLOCK) && (s & FE_HAS_LOCK)) { if ((fe->state & FESTATE_WAITFORLOCK) && (s & FE_HAS_LOCK)) {
update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK); update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK);
fe->state = FESTATE_TUNED; fe->state = FESTATE_TUNED;
// if we're tuned, then we have determined the correct inversion /* if we're tuned, then we have determined the correct inversion */
if ((!(fe->info->caps & FE_CAN_INVERSION_AUTO)) && (fe->parameters.inversion == INVERSION_AUTO)) { if ((!(fe->info->caps & FE_CAN_INVERSION_AUTO)) &&
(fe->parameters.inversion == INVERSION_AUTO)) {
fe->parameters.inversion = fe->inversion; fe->parameters.inversion = fe->inversion;
} }
continue; continue;
} }
// if we are tuned already, check we're still locked /* if we are tuned already, check we're still locked */
if (fe->state & FESTATE_TUNED) { if (fe->state & FESTATE_TUNED) {
update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK); update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK);
// we're tuned, and the lock is still good... /* we're tuned, and the lock is still good... */
if (s & FE_HAS_LOCK) { if (s & FE_HAS_LOCK)
continue; continue;
} else { else {
// if we _WERE_ tuned, but now don't have a lock, need to zigzag /* if we _WERE_ tuned, but now don't have a lock,
* need to zigzag */
fe->state = FESTATE_ZIGZAG_FAST; fe->state = FESTATE_ZIGZAG_FAST;
fe->started_auto_step = fe->auto_step; fe->started_auto_step = fe->auto_step;
check_wrapped = 0; check_wrapped = 0;
// fallthrough
} }
} }
// don't actually do anything if we're in the LOSTLOCK state, the frontend is set to /* don't actually do anything if we're in the LOSTLOCK state,
// FE_CAN_RECOVER, and the max_drift is 0 * the frontend is set to FE_CAN_RECOVER, and the max_drift is 0 */
if ((fe->state & FESTATE_LOSTLOCK) && if ((fe->state & FESTATE_LOSTLOCK) &&
(fe->info->caps & FE_CAN_RECOVER) && (fe->max_drift == 0)) { (fe->info->caps & FE_CAN_RECOVER) && (fe->max_drift == 0)) {
update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK); update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK);
continue; continue;
} }
// don't do anything if we're in the DISEQC state, since this might be someone /* don't do anything if we're in the DISEQC state, since this
// with a motorized dish controlled by DISEQC. If its actually a re-tune, there will * might be someone with a motorized dish controlled by DISEQC.
// be a SET_FRONTEND soon enough. * If its actually a re-tune, there will be a SET_FRONTEND soon enough. */
if (fe->state & FESTATE_DISEQC) { if (fe->state & FESTATE_DISEQC) {
update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK); update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK);
continue; continue;
} }
// if we're in the RETUNE state, set everything up for a brand new scan, /* if we're in the RETUNE state, set everything up for a brand
// keeping the current inversion setting, as the next tune is _very_ likely * new scan, keeping the current inversion setting, as the next
// to require the same * tune is _very_ likely to require the same */
if (fe->state & FESTATE_RETUNE) { if (fe->state & FESTATE_RETUNE) {
fe->lnb_drift = 0; fe->lnb_drift = 0;
fe->auto_step = 0; fe->auto_step = 0;
...@@ -578,35 +596,36 @@ static int dvb_frontend_thread (void *data) ...@@ -578,35 +596,36 @@ static int dvb_frontend_thread (void *data)
check_wrapped = 0; check_wrapped = 0;
} }
// fast zigzag. /* fast zigzag. */
if ((fe->state & FESTATE_SEARCHING_FAST) || (fe->state & FESTATE_RETUNE)) { if ((fe->state & FESTATE_SEARCHING_FAST) || (fe->state & FESTATE_RETUNE)) {
delay = fe->min_delay; delay = fe->min_delay;
// peform a tune /* peform a tune */
if (dvb_frontend_autotune(fe, check_wrapped)) { if (dvb_frontend_autotune(fe, check_wrapped)) {
// OK, if we've run out of trials at the fast speed. Drop back to /* OK, if we've run out of trials at the fast speed.
// slow for the _next_ attempt * Drop back to slow for the _next_ attempt */
fe->state = FESTATE_SEARCHING_SLOW; fe->state = FESTATE_SEARCHING_SLOW;
fe->started_auto_step = fe->auto_step; fe->started_auto_step = fe->auto_step;
continue; continue;
} }
check_wrapped = 1; check_wrapped = 1;
// if we've just retuned, enter the ZIGZAG_FAST state. This ensures /* if we've just retuned, enter the ZIGZAG_FAST state.
// we cannot return from an FE_SET_FRONTEND ioctl before the first frontend * This ensures we cannot return from an
// tune occurs * FE_SET_FRONTEND ioctl before the first frontend tune
* occurs */
if (fe->state & FESTATE_RETUNE) { if (fe->state & FESTATE_RETUNE) {
fe->state = FESTATE_TUNING_FAST; fe->state = FESTATE_TUNING_FAST;
wake_up_interruptible(&fe->wait_queue); wake_up_interruptible(&fe->wait_queue);
} }
} }
// slow zigzag /* slow zigzag */
if (fe->state & FESTATE_SEARCHING_SLOW) { if (fe->state & FESTATE_SEARCHING_SLOW) {
update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK); update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK);
// Note: don't bother checking for wrapping; we stay in this state /* Note: don't bother checking for wrapping; we stay in this
// until we get a lock * state until we get a lock */
dvb_frontend_autotune(fe, 0); dvb_frontend_autotune(fe, 0);
} }
}; };
...@@ -614,8 +633,6 @@ static int dvb_frontend_thread (void *data) ...@@ -614,8 +633,6 @@ static int dvb_frontend_thread (void *data)
if (dvb_shutdown_timeout) if (dvb_shutdown_timeout)
dvb_frontend_internal_ioctl (&fe->frontend, FE_SLEEP, NULL); dvb_frontend_internal_ioctl (&fe->frontend, FE_SLEEP, NULL);
up (&fe->sem);
fe->thread_pid = 0; fe->thread_pid = 0;
mb(); mb();
...@@ -711,6 +728,11 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file, ...@@ -711,6 +728,11 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file,
if (!fe || !fe->frontend.ioctl || fe->exit) if (!fe || !fe->frontend.ioctl || fe->exit)
return -ENODEV; return -ENODEV;
if ((file->f_flags & O_ACCMODE) == O_RDONLY &&
(_IOC_DIR(cmd) != _IOC_READ || cmd == FE_GET_EVENT ||
cmd == FE_DISEQC_RECV_SLAVE_REPLY))
return -EPERM;
if (down_interruptible (&fe->sem)) if (down_interruptible (&fe->sem))
return -ERESTARTSYS; return -ERESTARTSYS;
...@@ -718,6 +740,7 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file, ...@@ -718,6 +740,7 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file,
case FE_DISEQC_SEND_MASTER_CMD: case FE_DISEQC_SEND_MASTER_CMD:
case FE_DISEQC_SEND_BURST: case FE_DISEQC_SEND_BURST:
case FE_SET_TONE: case FE_SET_TONE:
case FE_SET_VOLTAGE:
if (fe->status) if (fe->status)
dvb_call_frontend_notifiers (fe, 0); dvb_call_frontend_notifiers (fe, 0);
dvb_frontend_internal_ioctl (&fe->frontend, cmd, parg); dvb_frontend_internal_ioctl (&fe->frontend, cmd, parg);
...@@ -734,43 +757,48 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file, ...@@ -734,43 +757,48 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file,
memcpy(&fetunesettings.parameters, parg, memcpy(&fetunesettings.parameters, parg,
sizeof (struct dvb_frontend_parameters)); sizeof (struct dvb_frontend_parameters));
// force auto frequency inversion if requested /* force auto frequency inversion if requested */
if (dvb_force_auto_inversion) { if (dvb_force_auto_inversion) {
fe->parameters.inversion = INVERSION_AUTO; fe->parameters.inversion = INVERSION_AUTO;
fetunesettings.parameters.inversion = INVERSION_AUTO; fetunesettings.parameters.inversion = INVERSION_AUTO;
} }
// get frontend-specific tuning settings /* get frontend-specific tuning settings */
if (dvb_frontend_internal_ioctl(&fe->frontend, FE_GET_TUNE_SETTINGS, &fetunesettings) == 0) { if (dvb_frontend_internal_ioctl(&fe->frontend, FE_GET_TUNE_SETTINGS,
&fetunesettings) == 0) {
fe->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000; fe->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000;
fe->max_drift = fetunesettings.max_drift; fe->max_drift = fetunesettings.max_drift;
fe->step_size = fetunesettings.step_size; fe->step_size = fetunesettings.step_size;
} else { } else {
// default values /* default values */
switch(fe->info->type) { switch(fe->info->type) {
case FE_QPSK: case FE_QPSK:
fe->min_delay = HZ/20; // default mindelay of 50ms fe->min_delay = HZ/20;
fe->step_size = fe->parameters.u.qpsk.symbol_rate / 16000; fe->step_size = fe->parameters.u.qpsk.symbol_rate / 16000;
fe->max_drift = fe->parameters.u.qpsk.symbol_rate / 2000; fe->max_drift = fe->parameters.u.qpsk.symbol_rate / 2000;
break; break;
case FE_QAM: case FE_QAM:
fe->min_delay = HZ/20; // default mindelay of 50ms fe->min_delay = HZ/20;
fe->step_size = 0; fe->step_size = 0; /* no zigzag */
fe->max_drift = 0; // don't want any zigzagging under DVB-C frontends fe->max_drift = 0;
break; break;
case FE_OFDM: case FE_OFDM:
fe->min_delay = HZ/20; // default mindelay of 50ms fe->min_delay = HZ/20;
fe->step_size = fe->info->frequency_stepsize * 2; fe->step_size = fe->info->frequency_stepsize * 2;
fe->max_drift = (fe->info->frequency_stepsize * 2) + 1; fe->max_drift = (fe->info->frequency_stepsize * 2) + 1;
break; break;
case FE_ATSC:
printk("dvb-core: FE_ATSC not handled yet.\n");
break;
} }
} }
if (dvb_override_tune_delay > 0) { if (dvb_override_tune_delay > 0) {
fe->min_delay = (dvb_override_tune_delay * HZ) / 1000; fe->min_delay = (dvb_override_tune_delay * HZ) / 1000;
} }
dvb_frontend_wakeup(fe);
dvb_frontend_add_event (fe, 0); dvb_frontend_add_event (fe, 0);
break; break;
...@@ -789,20 +817,13 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file, ...@@ -789,20 +817,13 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file,
if (err < 0) if (err < 0)
return err; return err;
// Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't do it, it is done for it. /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't
* do it, it is done for it. */
if ((cmd == FE_GET_INFO) && (err == 0)) { if ((cmd == FE_GET_INFO) && (err == 0)) {
struct dvb_frontend_info* tmp = (struct dvb_frontend_info*) parg; struct dvb_frontend_info* tmp = (struct dvb_frontend_info*) parg;
tmp->caps |= FE_CAN_INVERSION_AUTO; tmp->caps |= FE_CAN_INVERSION_AUTO;
} }
// if the frontend has just been set, wait until the first tune has finished.
// This ensures the app doesn't start reading data too quickly, perhaps from the
// previous lock, which is REALLY CONFUSING TO DEBUG!
if ((cmd == FE_SET_FRONTEND) && (err == 0)) {
dvb_frontend_wakeup(fe);
err = wait_event_interruptible(fe->wait_queue, fe->state & ~FESTATE_RETUNE);
}
return err; return err;
} }
...@@ -843,6 +864,11 @@ static int dvb_frontend_open (struct inode *inode, struct file *file) ...@@ -843,6 +864,11 @@ static int dvb_frontend_open (struct inode *inode, struct file *file)
fe->events.eventr = fe->events.eventw = 0; fe->events.eventr = fe->events.eventw = 0;
} }
if (!ret && fe->module) {
if (!try_module_get(fe->module))
return -EINVAL;
}
return ret; return ret;
} }
...@@ -851,13 +877,19 @@ static int dvb_frontend_release (struct inode *inode, struct file *file) ...@@ -851,13 +877,19 @@ static int dvb_frontend_release (struct inode *inode, struct file *file)
{ {
struct dvb_device *dvbdev = file->private_data; struct dvb_device *dvbdev = file->private_data;
struct dvb_frontend_data *fe = dvbdev->priv; struct dvb_frontend_data *fe = dvbdev->priv;
int ret = 0;
dprintk ("%s\n", __FUNCTION__); dprintk ("%s\n", __FUNCTION__);
if ((file->f_flags & O_ACCMODE) != O_RDONLY) if ((file->f_flags & O_ACCMODE) != O_RDONLY)
fe->release_jiffies = jiffies; fe->release_jiffies = jiffies;
return dvb_generic_release (inode, file); ret = dvb_generic_release (inode, file);
if (!ret && fe->module)
module_put(fe->module);
return ret;
} }
...@@ -897,7 +929,7 @@ dvb_add_frontend_ioctls (struct dvb_adapter *adapter, ...@@ -897,7 +929,7 @@ dvb_add_frontend_ioctls (struct dvb_adapter *adapter,
fe = list_entry (entry, struct dvb_frontend_data, list_head); fe = list_entry (entry, struct dvb_frontend_data, list_head);
if (fe->frontend.i2c->adapter == adapter && if (fe->frontend.dvb_adapter == adapter &&
fe->frontend.before_ioctl == NULL && fe->frontend.before_ioctl == NULL &&
fe->frontend.after_ioctl == NULL) fe->frontend.after_ioctl == NULL)
{ {
...@@ -931,7 +963,7 @@ dvb_remove_frontend_ioctls (struct dvb_adapter *adapter, ...@@ -931,7 +963,7 @@ dvb_remove_frontend_ioctls (struct dvb_adapter *adapter,
fe = list_entry (entry, struct dvb_frontend_data, list_head); fe = list_entry (entry, struct dvb_frontend_data, list_head);
if (fe->frontend.i2c->adapter == adapter && if (fe->frontend.dvb_adapter == adapter &&
fe->frontend.before_ioctl == before_ioctl && fe->frontend.before_ioctl == before_ioctl &&
fe->frontend.after_ioctl == after_ioctl) fe->frontend.after_ioctl == after_ioctl)
{ {
...@@ -992,7 +1024,7 @@ dvb_add_frontend_notifier (struct dvb_adapter *adapter, ...@@ -992,7 +1024,7 @@ dvb_add_frontend_notifier (struct dvb_adapter *adapter,
fe = list_entry (entry, struct dvb_frontend_data, list_head); fe = list_entry (entry, struct dvb_frontend_data, list_head);
if (fe->frontend.i2c->adapter == adapter && if (fe->frontend.dvb_adapter == adapter &&
fe->frontend.notifier_callback == NULL) fe->frontend.notifier_callback == NULL)
{ {
fe->frontend.notifier_callback = callback; fe->frontend.notifier_callback = callback;
...@@ -1021,7 +1053,7 @@ dvb_remove_frontend_notifier (struct dvb_adapter *adapter, ...@@ -1021,7 +1053,7 @@ dvb_remove_frontend_notifier (struct dvb_adapter *adapter,
fe = list_entry (entry, struct dvb_frontend_data, list_head); fe = list_entry (entry, struct dvb_frontend_data, list_head);
if (fe->frontend.i2c->adapter == adapter && if (fe->frontend.dvb_adapter == adapter &&
fe->frontend.notifier_callback == callback) fe->frontend.notifier_callback == callback)
{ {
fe->frontend.notifier_callback = NULL; fe->frontend.notifier_callback = NULL;
...@@ -1061,9 +1093,10 @@ static struct file_operations dvb_frontend_fops = { ...@@ -1061,9 +1093,10 @@ static struct file_operations dvb_frontend_fops = {
int int
dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend,
unsigned int cmd, void *arg), unsigned int cmd, void *arg),
struct dvb_i2c_bus *i2c, struct dvb_adapter *dvb_adapter,
void *data, void *data,
struct dvb_frontend_info *info) struct dvb_frontend_info *info,
struct module *module)
{ {
struct list_head *entry; struct list_head *entry;
struct dvb_frontend_data *fe; struct dvb_frontend_data *fe;
...@@ -1093,9 +1126,10 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, ...@@ -1093,9 +1126,10 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend,
init_MUTEX (&fe->events.sem); init_MUTEX (&fe->events.sem);
fe->events.eventw = fe->events.eventr = 0; fe->events.eventw = fe->events.eventr = 0;
fe->events.overflow = 0; fe->events.overflow = 0;
fe->module = module;
fe->frontend.ioctl = ioctl; fe->frontend.ioctl = ioctl;
fe->frontend.i2c = i2c; fe->frontend.dvb_adapter = dvb_adapter;
fe->frontend.data = data; fe->frontend.data = data;
fe->info = info; fe->info = info;
fe->inversion = INVERSION_OFF; fe->inversion = INVERSION_OFF;
...@@ -1107,7 +1141,7 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, ...@@ -1107,7 +1141,7 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend,
struct dvb_frontend_ioctl_data, struct dvb_frontend_ioctl_data,
list_head); list_head);
if (ioctl->adapter == i2c->adapter) { if (ioctl->adapter == dvb_adapter) {
fe->frontend.before_ioctl = ioctl->before_ioctl; fe->frontend.before_ioctl = ioctl->before_ioctl;
fe->frontend.after_ioctl = ioctl->after_ioctl; fe->frontend.after_ioctl = ioctl->after_ioctl;
fe->frontend.before_after_data = ioctl->before_after_data; fe->frontend.before_after_data = ioctl->before_after_data;
...@@ -1122,7 +1156,7 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, ...@@ -1122,7 +1156,7 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend,
struct dvb_frontend_notifier_data, struct dvb_frontend_notifier_data,
list_head); list_head);
if (notifier->adapter == i2c->adapter) { if (notifier->adapter == dvb_adapter) {
fe->frontend.notifier_callback = notifier->callback; fe->frontend.notifier_callback = notifier->callback;
fe->frontend.notifier_data = notifier->data; fe->frontend.notifier_data = notifier->data;
break; break;
...@@ -1131,11 +1165,11 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, ...@@ -1131,11 +1165,11 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend,
list_add_tail (&fe->list_head, &frontend_list); list_add_tail (&fe->list_head, &frontend_list);
printk ("DVB: registering frontend %i:%i (%s)...\n", printk ("DVB: registering frontend %i (%s)...\n",
fe->frontend.i2c->adapter->num, fe->frontend.i2c->id, fe->frontend.dvb_adapter->num,
fe->info->name); fe->info->name);
dvb_register_device (i2c->adapter, &fe->dvbdev, &dvbdev_template, dvb_register_device (dvb_adapter, &fe->dvbdev, &dvbdev_template,
fe, DVB_DEVICE_FRONTEND); fe, DVB_DEVICE_FRONTEND);
if ((info->caps & FE_NEEDS_BENDING) || (dvb_override_frequency_bending == 2)) if ((info->caps & FE_NEEDS_BENDING) || (dvb_override_frequency_bending == 2))
...@@ -1146,10 +1180,9 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, ...@@ -1146,10 +1180,9 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend,
return 0; return 0;
} }
int dvb_unregister_frontend_new (int (*ioctl) (struct dvb_frontend *frontend,
int dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend,
unsigned int cmd, void *arg), unsigned int cmd, void *arg),
struct dvb_i2c_bus *i2c) struct dvb_adapter *dvb_adapter)
{ {
struct list_head *entry, *n; struct list_head *entry, *n;
...@@ -1162,7 +1195,7 @@ int dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend, ...@@ -1162,7 +1195,7 @@ int dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend,
fe = list_entry (entry, struct dvb_frontend_data, list_head); fe = list_entry (entry, struct dvb_frontend_data, list_head);
if (fe->frontend.ioctl == ioctl && fe->frontend.i2c == i2c) { if (fe->frontend.ioctl == ioctl && fe->frontend.dvb_adapter == dvb_adapter) {
dvb_unregister_device (fe->dvbdev); dvb_unregister_device (fe->dvbdev);
list_del (entry); list_del (entry);
up (&frontend_mutex); up (&frontend_mutex);
...@@ -1176,14 +1209,3 @@ int dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend, ...@@ -1176,14 +1209,3 @@ int dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend,
return -EINVAL; return -EINVAL;
} }
MODULE_PARM(dvb_frontend_debug,"i");
MODULE_PARM(dvb_shutdown_timeout,"i");
MODULE_PARM(dvb_override_frequency_bending,"i");
MODULE_PARM(dvb_force_auto_inversion,"i");
MODULE_PARM(dvb_override_tune_delay,"i");
MODULE_PARM_DESC(dvb_frontend_debug, "enable verbose debug messages");
MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware");
MODULE_PARM_DESC(dvb_override_frequency_bending, "0: normal (default), 1: never use frequency bending, 2: always use frequency bending");
MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always");
MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt");
...@@ -31,14 +31,29 @@ ...@@ -31,14 +31,29 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/delay.h>
#include <linux/dvb/frontend.h> #include <linux/dvb/frontend.h>
#include "dvb_i2c.h"
#include "dvbdev.h" #include "dvbdev.h"
/* FIXME: Move to i2c-id.h */
#define I2C_DRIVERID_DVBFE_ALPS_TDLB7 I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_ALPS_TDMB7 I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_AT76C651 I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_CX24110 I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_DST I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_DUMMY I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_L64781 I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_MT312 I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_MT352 I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_NXT6000 I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_SP887X I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_STV0299 I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_TDA1004X I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_TDA8083 I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_VES1820 I2C_DRIVERID_EXP2
#define I2C_DRIVERID_DVBFE_VES1X93 I2C_DRIVERID_EXP2
/** /**
* when before_ioctl is registered and returns value 0, ioctl and after_ioctl * when before_ioctl is registered and returns value 0, ioctl and after_ioctl
...@@ -50,7 +65,7 @@ struct dvb_frontend { ...@@ -50,7 +65,7 @@ struct dvb_frontend {
int (*ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg); int (*ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg);
int (*after_ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg); int (*after_ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg);
void (*notifier_callback) (fe_status_t s, void *data); void (*notifier_callback) (fe_status_t s, void *data);
struct dvb_i2c_bus *i2c; struct dvb_adapter *dvb_adapter;
void *before_after_data; /* can be used by hardware module... */ void *before_after_data; /* can be used by hardware module... */
void *notifier_data; /* can be used by hardware module... */ void *notifier_data; /* can be used by hardware module... */
void *data; /* can be used by hardware module... */ void *data; /* can be used by hardware module... */
...@@ -75,19 +90,21 @@ struct dvb_frontend_tune_settings { ...@@ -75,19 +90,21 @@ struct dvb_frontend_tune_settings {
#define FE_SLEEP _IO('v', 80) #define FE_SLEEP _IO('v', 80)
#define FE_INIT _IO('v', 81) #define FE_INIT _IO('v', 81)
#define FE_GET_TUNE_SETTINGS _IOWR('v', 83, struct dvb_frontend_tune_settings) #define FE_GET_TUNE_SETTINGS _IOWR('v', 83, struct dvb_frontend_tune_settings)
#define FE_REGISTER _IO ('v', 84)
#define FE_UNREGISTER _IO ('v', 85)
extern int extern int
dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend,
unsigned int cmd, void *arg), unsigned int cmd, void *arg),
struct dvb_i2c_bus *i2c, struct dvb_adapter *dvb_adapter,
void *data, void *data,
struct dvb_frontend_info *info); struct dvb_frontend_info *info,
struct module *module);
extern int extern int
dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend, dvb_unregister_frontend_new (int (*ioctl) (struct dvb_frontend *frontend,
unsigned int cmd, void *arg), unsigned int cmd, void *arg),
struct dvb_i2c_bus *i2c); struct dvb_adapter *dvb_adapter);
/** /**
......
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
void dvb_kernel_thread_setup (const char *thread_name)
{
lock_kernel ();
daemonize (thread_name);
sigfillset (&current->blocked);
unlock_kernel ();
}
/* if the miracle happens and "generic_usercopy()" is included into
the kernel, then this can vanish. please don't make the mistake and
define this as video_usercopy(). this will introduce a dependecy
to the v4l "videodev.o" module, which is unnecessary for some
cards (ie. the budget dvb-cards don't need the v4l module...) */
int dvb_usercopy(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg,
int (*func)(struct inode *inode, struct file *file,
unsigned int cmd, void *arg))
{
char sbuf[128];
void *mbuf = NULL;
void *parg = NULL;
int err = -EINVAL;
/* Copy arguments into temp kernel buffer */
switch (_IOC_DIR(cmd)) {
case _IOC_NONE:
/*
* For this command, the pointer is actually an integer
* argument.
*/
parg = (void *) arg;
break;
case _IOC_READ: /* some v4l ioctls are marked wrong ... */
case _IOC_WRITE:
case (_IOC_WRITE | _IOC_READ):
if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
parg = sbuf;
} else {
/* too big to allocate from stack */
mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
if (NULL == mbuf)
return -ENOMEM;
parg = mbuf;
}
err = -EFAULT;
if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
goto out;
break;
}
/* call driver */
if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD)
err = -EINVAL;
if (err < 0)
goto out;
/* Copy results into user buffer */
switch (_IOC_DIR(cmd))
{
case _IOC_READ:
case (_IOC_WRITE | _IOC_READ):
if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
err = -EFAULT;
break;
}
out:
if (mbuf)
kfree(mbuf);
return err;
}
EXPORT_SYMBOL(dvb_usercopy);
EXPORT_SYMBOL(dvb_kernel_thread_setup);
/*
* dvb_functions.h: isolate some Linux specific stuff from the dvb-core
* that can't be expressed as a one-liner
* in order to make porting to other environments easier
*
* Copyright (C) 2003 Convergence GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Lesser Public License
* as published by the Free Software Foundation; either version 2.1
* 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 Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef __DVB_FUNCTIONS_H__
#define __DVB_FUNCTIONS_H__
/**
* a sleeping delay function, waits i ms
*
*/
static inline
void dvb_delay(int i)
{
current->state=TASK_INTERRUPTIBLE;
schedule_timeout((HZ*i)/1000);
}
/* we don't mess with video_usercopy() any more,
we simply define out own dvb_usercopy(), which will hopefull become
generic_usercopy() someday... */
extern int dvb_usercopy(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg,
int (*func)(struct inode *inode, struct file *file,
unsigned int cmd, void *arg));
extern void dvb_kernel_thread_setup (const char *thread_name);
#endif
/*
* dvb_i2c.h: simplified i2c interface for DVB adapters to get rid of i2c-core.c
*
* Copyright (C) 2002 Holger Waechtler for convergence integrated media GmbH
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/module.h>
#include <asm/semaphore.h>
#include "dvb_i2c.h"
#include "dvb_functions.h"
struct dvb_i2c_device {
struct list_head list_head;
struct module *owner;
int (*attach) (struct dvb_i2c_bus *i2c, void **data);
void (*detach) (struct dvb_i2c_bus *i2c, void *data);
void *data;
};
LIST_HEAD(dvb_i2c_buslist);
LIST_HEAD(dvb_i2c_devicelist);
DECLARE_MUTEX(dvb_i2c_mutex);
static int register_i2c_client (struct dvb_i2c_bus *i2c, struct dvb_i2c_device *dev)
{
struct dvb_i2c_device *client;
if (!(client = kmalloc (sizeof (struct dvb_i2c_device), GFP_KERNEL)))
return -ENOMEM;
client->detach = dev->detach;
client->owner = dev->owner;
client->data = dev->data;
INIT_LIST_HEAD(&client->list_head);
list_add_tail (&client->list_head, &i2c->client_list);
return 0;
}
static void try_attach_device (struct dvb_i2c_bus *i2c, struct dvb_i2c_device *dev)
{
if (dev->owner) {
if (!try_module_get(dev->owner))
return;
}
if (dev->attach (i2c, &dev->data) == 0) {
register_i2c_client (i2c, dev);
} else {
if (dev->owner)
module_put (dev->owner);
}
}
static void detach_device (struct dvb_i2c_bus *i2c, struct dvb_i2c_device *dev)
{
dev->detach (i2c, dev->data);
if (dev->owner)
module_put (dev->owner);
}
static void unregister_i2c_client_from_bus (struct dvb_i2c_device *dev,
struct dvb_i2c_bus *i2c)
{
struct list_head *entry, *n;
list_for_each_safe (entry, n, &i2c->client_list) {
struct dvb_i2c_device *client;
client = list_entry (entry, struct dvb_i2c_device, list_head);
if (client->detach == dev->detach) {
list_del (entry);
detach_device (i2c, dev);
}
}
}
static void unregister_i2c_client_from_all_busses (struct dvb_i2c_device *dev)
{
struct list_head *entry, *n;
list_for_each_safe (entry, n, &dvb_i2c_buslist) {
struct dvb_i2c_bus *i2c;
i2c = list_entry (entry, struct dvb_i2c_bus, list_head);
unregister_i2c_client_from_bus (dev, i2c);
}
}
static void unregister_all_clients_from_bus (struct dvb_i2c_bus *i2c)
{
struct list_head *entry, *n;
list_for_each_safe (entry, n, &(i2c->client_list)) {
struct dvb_i2c_device *dev;
dev = list_entry (entry, struct dvb_i2c_device, list_head);
unregister_i2c_client_from_bus (dev, i2c);
}
}
static void probe_device_on_all_busses (struct dvb_i2c_device *dev)
{
struct list_head *entry;
list_for_each (entry, &dvb_i2c_buslist) {
struct dvb_i2c_bus *i2c;
i2c = list_entry (entry, struct dvb_i2c_bus, list_head);
try_attach_device (i2c, dev);
}
}
static void probe_devices_on_bus (struct dvb_i2c_bus *i2c)
{
struct list_head *entry;
list_for_each (entry, &dvb_i2c_devicelist) {
struct dvb_i2c_device *dev;
dev = list_entry (entry, struct dvb_i2c_device, list_head);
try_attach_device (i2c, dev);
}
}
static struct dvb_i2c_bus* dvb_find_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c,
const struct i2c_msg msgs[],
int num),
struct dvb_adapter *adapter,
int id)
{
struct list_head *entry;
list_for_each (entry, &dvb_i2c_buslist) {
struct dvb_i2c_bus *i2c;
i2c = list_entry (entry, struct dvb_i2c_bus, list_head);
if (i2c->xfer == xfer && i2c->adapter == adapter && i2c->id == id)
return i2c;
}
return NULL;
}
struct dvb_i2c_bus*
dvb_register_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c,
const struct i2c_msg *msgs, int num),
void *data, struct dvb_adapter *adapter, int id)
{
struct dvb_i2c_bus *i2c;
if (down_interruptible (&dvb_i2c_mutex))
return NULL;
if (!(i2c = kmalloc (sizeof (struct dvb_i2c_bus), GFP_KERNEL))) {
up (&dvb_i2c_mutex);
return NULL;
}
INIT_LIST_HEAD(&i2c->list_head);
INIT_LIST_HEAD(&i2c->client_list);
i2c->xfer = xfer;
i2c->data = data;
i2c->adapter = adapter;
i2c->id = id;
probe_devices_on_bus (i2c);
list_add_tail (&i2c->list_head, &dvb_i2c_buslist);
up (&dvb_i2c_mutex);
return i2c;
}
void dvb_unregister_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c,
const struct i2c_msg msgs[], int num),
struct dvb_adapter *adapter, int id)
{
struct dvb_i2c_bus *i2c;
down (&dvb_i2c_mutex);
if ((i2c = dvb_find_i2c_bus (xfer, adapter, id))) {
unregister_all_clients_from_bus (i2c);
list_del (&i2c->list_head);
kfree (i2c);
}
up (&dvb_i2c_mutex);
}
int dvb_register_i2c_device (struct module *owner,
int (*attach) (struct dvb_i2c_bus *i2c, void **data),
void (*detach) (struct dvb_i2c_bus *i2c, void *data))
{
struct dvb_i2c_device *entry;
if (down_interruptible (&dvb_i2c_mutex))
return -ERESTARTSYS;
if (!(entry = kmalloc (sizeof (struct dvb_i2c_device), GFP_KERNEL))) {
up(&dvb_i2c_mutex);
return -ENOMEM;
}
entry->owner = owner;
entry->attach = attach;
entry->detach = detach;
INIT_LIST_HEAD(&entry->list_head);
probe_device_on_all_busses (entry);
list_add_tail (&entry->list_head, &dvb_i2c_devicelist);
up (&dvb_i2c_mutex);
return 0;
}
int dvb_unregister_i2c_device (int (*attach) (struct dvb_i2c_bus *i2c, void **data))
{
struct list_head *entry, *n;
down (&dvb_i2c_mutex);
list_for_each_safe (entry, n, &dvb_i2c_devicelist) {
struct dvb_i2c_device *dev;
dev = list_entry (entry, struct dvb_i2c_device, list_head);
if (dev->attach == attach) {
list_del (entry);
unregister_i2c_client_from_all_busses (dev);
kfree (entry);
up (&dvb_i2c_mutex);
return 0;
}
}
up (&dvb_i2c_mutex);
return -EINVAL;
}
/*
* dvb_i2c.h: i2c interface to get rid of i2c-core.c
*
* Copyright (C) 2002 Holger Waechtler for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* 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 Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _DVB_I2C_H_
#define _DVB_I2C_H_
#include <linux/list.h>
#include <linux/i2c.h>
#include "dvbdev.h"
struct dvb_i2c_bus {
struct list_head list_head;
int (*xfer) (struct dvb_i2c_bus *i2c,
const struct i2c_msg msgs[],
int num);
void *data;
struct dvb_adapter *adapter;
int id;
struct list_head client_list;
};
extern struct dvb_i2c_bus*
dvb_register_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c,
const struct i2c_msg *msgs, int num),
void *data,
struct dvb_adapter *adapter,
int id);
extern
void dvb_unregister_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c,
const struct i2c_msg msgs[], int num),
struct dvb_adapter *adapter,
int id);
extern int dvb_register_i2c_device (struct module *owner,
int (*attach) (struct dvb_i2c_bus *i2c, void **data),
void (*detach) (struct dvb_i2c_bus *i2c, void *data));
extern int dvb_unregister_i2c_device (int (*attach) (struct dvb_i2c_bus *i2c, void **data));
#endif
...@@ -24,17 +24,12 @@ EXPORT_SYMBOL(dvbdmx_connect_frontend); ...@@ -24,17 +24,12 @@ EXPORT_SYMBOL(dvbdmx_connect_frontend);
EXPORT_SYMBOL(dvbdmx_disconnect_frontend); EXPORT_SYMBOL(dvbdmx_disconnect_frontend);
EXPORT_SYMBOL(dvb_register_frontend); EXPORT_SYMBOL(dvb_register_frontend);
EXPORT_SYMBOL(dvb_unregister_frontend); EXPORT_SYMBOL(dvb_unregister_frontend_new);
EXPORT_SYMBOL(dvb_add_frontend_ioctls); EXPORT_SYMBOL(dvb_add_frontend_ioctls);
EXPORT_SYMBOL(dvb_remove_frontend_ioctls); EXPORT_SYMBOL(dvb_remove_frontend_ioctls);
EXPORT_SYMBOL(dvb_add_frontend_notifier); EXPORT_SYMBOL(dvb_add_frontend_notifier);
EXPORT_SYMBOL(dvb_remove_frontend_notifier); EXPORT_SYMBOL(dvb_remove_frontend_notifier);
EXPORT_SYMBOL(dvb_register_i2c_bus);
EXPORT_SYMBOL(dvb_unregister_i2c_bus);
EXPORT_SYMBOL(dvb_register_i2c_device);
EXPORT_SYMBOL(dvb_unregister_i2c_device);
EXPORT_SYMBOL(dvb_net_init); EXPORT_SYMBOL(dvb_net_init);
EXPORT_SYMBOL(dvb_net_release); EXPORT_SYMBOL(dvb_net_release);
......
...@@ -40,8 +40,6 @@ ...@@ -40,8 +40,6 @@
#include "dvb_demux.h" #include "dvb_demux.h"
#include "dvb_net.h" #include "dvb_net.h"
#include "dvb_functions.h"
static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt ) static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt )
{ {
......
...@@ -25,22 +25,26 @@ ...@@ -25,22 +25,26 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/device.h>
#include "dvbdev.h" #include "dvbdev.h"
#include "dvb_functions.h"
static int dvbdev_debug = 0; static int dvbdev_debug;
module_param(dvbdev_debug, int, 0644);
MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off).");
#define dprintk if (dvbdev_debug) printk #define dprintk if (dvbdev_debug) printk
static LIST_HEAD(dvb_adapter_list); static LIST_HEAD(dvb_adapter_list);
static DECLARE_MUTEX(dvbdev_register_lock); static DECLARE_MUTEX(dvbdev_register_lock);
static const char * const dnames[] = {
static char *dnames[] = {
"video", "audio", "sec", "frontend", "demux", "dvr", "ca", "video", "audio", "sec", "frontend", "demux", "dvr", "ca",
"net", "osd" "net", "osd"
}; };
...@@ -49,6 +53,9 @@ static char *dnames[] = { ...@@ -49,6 +53,9 @@ static char *dnames[] = {
#define DVB_MAX_IDS 4 #define DVB_MAX_IDS 4
#define nums2minor(num,type,id) ((num << 6) | (id << 4) | type) #define nums2minor(num,type,id) ((num << 6) | (id << 4) | type)
struct class_simple *dvb_class;
EXPORT_SYMBOL(dvb_class);
static struct dvb_device* dvbdev_find_device (int minor) static struct dvb_device* dvbdev_find_device (int minor)
{ {
struct list_head *entry; struct list_head *entry;
...@@ -219,6 +226,9 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, ...@@ -219,6 +226,9 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
S_IFCHR | S_IRUSR | S_IWUSR, S_IFCHR | S_IRUSR | S_IWUSR,
"dvb/adapter%d/%s%d", adap->num, dnames[type], id); "dvb/adapter%d/%s%d", adap->num, dnames[type], id);
class_simple_device_add(dvb_class, MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
NULL, "dvb%d.%s%d", adap->num, dnames[type], id);
dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
adap->num, dnames[type], id, nums2minor(adap->num, type, id), adap->num, dnames[type], id, nums2minor(adap->num, type, id),
nums2minor(adap->num, type, id)); nums2minor(adap->num, type, id));
...@@ -235,6 +245,9 @@ void dvb_unregister_device(struct dvb_device *dvbdev) ...@@ -235,6 +245,9 @@ void dvb_unregister_device(struct dvb_device *dvbdev)
devfs_remove("dvb/adapter%d/%s%d", dvbdev->adapter->num, devfs_remove("dvb/adapter%d/%s%d", dvbdev->adapter->num,
dnames[dvbdev->type], dvbdev->id); dnames[dvbdev->type], dvbdev->id);
class_simple_device_remove(MKDEV(DVB_MAJOR, nums2minor(dvbdev->adapter->num,
dvbdev->type, dvbdev->id)));
list_del(&dvbdev->list_head); list_del(&dvbdev->list_head);
kfree(dvbdev); kfree(dvbdev);
} }
...@@ -300,24 +313,95 @@ int dvb_register_adapter(struct dvb_adapter **padap, const char *name, struct mo ...@@ -300,24 +313,95 @@ int dvb_register_adapter(struct dvb_adapter **padap, const char *name, struct mo
int dvb_unregister_adapter(struct dvb_adapter *adap) int dvb_unregister_adapter(struct dvb_adapter *adap)
{ {
devfs_remove("dvb/adapter%d", adap->num);
if (down_interruptible (&dvbdev_register_lock)) if (down_interruptible (&dvbdev_register_lock))
return -ERESTARTSYS; return -ERESTARTSYS;
devfs_remove("dvb/adapter%d", adap->num);
list_del (&adap->list_head); list_del (&adap->list_head);
up (&dvbdev_register_lock); up (&dvbdev_register_lock);
kfree (adap); kfree (adap);
return 0; return 0;
} }
/* if the miracle happens and "generic_usercopy()" is included into
the kernel, then this can vanish. please don't make the mistake and
define this as video_usercopy(). this will introduce a dependecy
to the v4l "videodev.o" module, which is unnecessary for some
cards (ie. the budget dvb-cards don't need the v4l module...) */
int dvb_usercopy(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg,
int (*func)(struct inode *inode, struct file *file,
unsigned int cmd, void *arg))
{
char sbuf[128];
void *mbuf = NULL;
void *parg = NULL;
int err = -EINVAL;
/* Copy arguments into temp kernel buffer */
switch (_IOC_DIR(cmd)) {
case _IOC_NONE:
/*
* For this command, the pointer is actually an integer
* argument.
*/
parg = (void *) arg;
break;
case _IOC_READ: /* some v4l ioctls are marked wrong ... */
case _IOC_WRITE:
case (_IOC_WRITE | _IOC_READ):
if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
parg = sbuf;
} else {
/* too big to allocate from stack */
mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
if (NULL == mbuf)
return -ENOMEM;
parg = mbuf;
}
err = -EFAULT;
if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
goto out;
break;
}
/* call driver */
if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD)
err = -EINVAL;
if (err < 0)
goto out;
/* Copy results into user buffer */
switch (_IOC_DIR(cmd))
{
case _IOC_READ:
case (_IOC_WRITE | _IOC_READ):
if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
err = -EFAULT;
break;
}
out:
if (mbuf)
kfree(mbuf);
return err;
}
static int __init init_dvbdev(void) static int __init init_dvbdev(void)
{ {
int retval; int retval;
if ((retval = register_chrdev(DVB_MAJOR,"DVB", &dvb_device_fops)))
printk("dvb-core: unable to get major %d\n", DVB_MAJOR);
devfs_mk_dir("dvb"); devfs_mk_dir("dvb");
retval = register_chrdev(DVB_MAJOR,"DVB", &dvb_device_fops); dvb_class = class_simple_create(THIS_MODULE, "dvb");
if (retval) if (IS_ERR(dvb_class))
printk("video_dev: unable to get major %d\n", DVB_MAJOR); return PTR_ERR(dvb_class);
return retval; return retval;
} }
...@@ -327,6 +411,7 @@ static void __exit exit_dvbdev(void) ...@@ -327,6 +411,7 @@ static void __exit exit_dvbdev(void)
{ {
unregister_chrdev(DVB_MAJOR, "DVB"); unregister_chrdev(DVB_MAJOR, "DVB");
devfs_remove("dvb"); devfs_remove("dvb");
class_simple_destroy(dvb_class);
} }
module_init(init_dvbdev); module_init(init_dvbdev);
...@@ -336,6 +421,3 @@ MODULE_DESCRIPTION("DVB Core Driver"); ...@@ -336,6 +421,3 @@ MODULE_DESCRIPTION("DVB Core Driver");
MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler"); MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_PARM(dvbdev_debug,"i");
MODULE_PARM_DESC(dvbdev_debug, "enable verbose debug messages");
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/devfs_fs_kernel.h> #include <linux/devfs_fs_kernel.h>
#include <linux/smp_lock.h>
#define DVB_MAJOR 212 #define DVB_MAJOR 212
...@@ -92,5 +93,15 @@ extern int dvb_generic_open (struct inode *inode, struct file *file); ...@@ -92,5 +93,15 @@ extern int dvb_generic_open (struct inode *inode, struct file *file);
extern int dvb_generic_release (struct inode *inode, struct file *file); extern int dvb_generic_release (struct inode *inode, struct file *file);
extern int dvb_generic_ioctl (struct inode *inode, struct file *file, extern int dvb_generic_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg); unsigned int cmd, unsigned long arg);
/* we don't mess with video_usercopy() any more,
we simply define out own dvb_usercopy(), which will hopefully become
generic_usercopy() someday... */
extern int dvb_usercopy(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg,
int (*func)(struct inode *inode, struct file *file,
unsigned int cmd, void *arg));
#endif /* #ifndef _DVBDEV_H_ */ #endif /* #ifndef _DVBDEV_H_ */
...@@ -32,7 +32,8 @@ ...@@ -32,7 +32,8 @@
typedef enum fe_type { typedef enum fe_type {
FE_QPSK, FE_QPSK,
FE_QAM, FE_QAM,
FE_OFDM FE_OFDM,
FE_ATSC
} fe_type_t; } fe_type_t;
...@@ -59,6 +60,8 @@ typedef enum fe_caps { ...@@ -59,6 +60,8 @@ typedef enum fe_caps {
FE_CAN_BANDWIDTH_AUTO = 0x40000, FE_CAN_BANDWIDTH_AUTO = 0x40000,
FE_CAN_GUARD_INTERVAL_AUTO = 0x80000, FE_CAN_GUARD_INTERVAL_AUTO = 0x80000,
FE_CAN_HIERARCHY_AUTO = 0x100000, FE_CAN_HIERARCHY_AUTO = 0x100000,
FE_CAN_8VSB = 0x200000,
FE_CAN_16VSB = 0x400000,
FE_NEEDS_BENDING = 0x20000000, // frontend requires frequency bending FE_NEEDS_BENDING = 0x20000000, // frontend requires frequency bending
FE_CAN_RECOVER = 0x40000000, // frontend can recover from a cable unplug automatically FE_CAN_RECOVER = 0x40000000, // frontend can recover from a cable unplug automatically
FE_CAN_MUTE_TS = 0x80000000 // frontend can stop spurious TS data output FE_CAN_MUTE_TS = 0x80000000 // frontend can stop spurious TS data output
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment