Driver for DVB-T cards with cx23882 chipset, like WinTV NOVA-T PCI

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20678 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Marcus Overhagen 2007-04-13 16:21:17 +00:00
parent 36e0e8a8bd
commit 083a11a3f4
18 changed files with 2584 additions and 0 deletions

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __CONFIG_H
#define __CONFIG_H
#define VERSION "1.0"
#define BUILD __DATE__ " "__TIME__
#define INFO1 "cx23882: DVB-T Driver. Version " VERSION ", Revision " REVISION ", Build " BUILD
#define INFO2 "cx23882: Copyright (c) 2004-2006 Marcus Overhagen. All rights reserved.\n"
#define MAX_CARDS 8
#define PCI_LATENCY 0x20
#define I2C_ADDR_DEMOD 0x43
#define I2C_ADDR_EEPROM 0x50
#define I2C_ADDR_PLL 0x61
#endif

View File

@ -0,0 +1,306 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <KernelExport.h>
#include "cx22702.h"
#include "dtt7592.h"
#include "config.h"
#include "dvb.h"
#define TRACE_CX22702
#ifdef TRACE_CX22702
#define TRACE dprintf
#else
#define TRACE(a...)
#endif
static void
cx22702_reg_dump(i2c_bus *bus)
{
int i;
for (i = 0; i < 256; i++) {
uint8 data;
if (cx22702_reg_read(bus, i, &data) != B_OK)
dprintf("cx22702_reg 0x%02x error\n", i);
else
dprintf("cx22702_reg 0x%02x value 0x%02x\n", i, data);
}
}
status_t
cx22702_reg_write(i2c_bus *bus, uint8 reg, uint8 data)
{
status_t res;
uint8 buf[2] = {reg, data};
res = i2c_write(bus, I2C_ADDR_DEMOD, buf, 2);
if (res != B_OK)
TRACE("cx22702_reg_write error, reg 0x%02x, value 0x%02x\n", reg, data);
return res;
}
status_t
cx22702_reg_read(i2c_bus *bus, uint8 reg, uint8 *data)
{
status_t res;
res = i2c_xfer(bus, I2C_ADDR_DEMOD, &reg, 1, data, 1);
if (res != B_OK)
TRACE("cx22702_reg_read error, reg 0x%02x\n", reg);
return res;
}
status_t
cx22702_init(i2c_bus *bus)
{
if (cx22702_reg_write(bus, 0x00, 0x02) != B_OK) return B_ERROR;
if (cx22702_reg_write(bus, 0x00, 0x00) != B_OK) return B_ERROR;
snooze(10000);
if (cx22702_reg_write(bus, 0x00, 0x00) != B_OK) return B_ERROR;
if (cx22702_reg_write(bus, 0x09, 0x01) != B_OK) return B_ERROR;
if (cx22702_reg_write(bus, 0x0B, 0x04) != B_OK) return B_ERROR;
if (cx22702_reg_write(bus, 0x0C, 0x00) != B_OK) return B_ERROR;
if (cx22702_reg_write(bus, 0x0D, 0x80) != B_OK) return B_ERROR;
if (cx22702_reg_write(bus, 0x26, 0x80) != B_OK) return B_ERROR;
if (cx22702_reg_write(bus, 0x2D, 0xff) != B_OK) return B_ERROR;
if (cx22702_reg_write(bus, 0xDC, 0x00) != B_OK) return B_ERROR;
if (cx22702_reg_write(bus, 0xE4, 0x00) != B_OK) return B_ERROR;
if (cx22702_reg_write(bus, 0xF8, 0x02) != B_OK) return B_ERROR;
if (cx22702_reg_write(bus, 0x00, 0x01) != B_OK) return B_ERROR;
return B_OK;
}
status_t
cx22702_get_frequency_info(i2c_bus *bus, dvb_frequency_info_t *info)
{
memset(info, 0, sizeof(*info));
info->frequency_min = 149000000;
info->frequency_max = 860000000;
info->frequency_step = 166667;
return B_OK;
}
status_t
cx22702_set_tuning_parameters(i2c_bus *bus, const dvb_t_tuning_parameters_t *params)
{
uint8 data;
status_t res;
if (cx22702_reg_write(bus, 0x00, 0x00) != B_OK)
return B_ERROR;
res = dtt7592_set_frequency(bus, params->frequency, params->bandwidth);
if (res != B_OK)
return res;
if (cx22702_reg_read(bus, 0x0c, &data) != B_OK)
return B_ERROR;
switch (params->inversion) {
case DVB_INVERSION_ON: data |= 0x01; break;
case DVB_INVERSION_OFF: data &= ~0x01; break;
default: return B_ERROR;
}
switch (params->bandwidth) {
case DVB_BANDWIDTH_6_MHZ: data = (data & ~0x10) | 0x20; break;
case DVB_BANDWIDTH_7_MHZ: data = (data & ~0x20) | 0x10; break;
case DVB_BANDWIDTH_8_MHZ: data &= ~0x30; break;
default: return B_ERROR;
}
if (cx22702_reg_write(bus, 0x0c, data) != B_OK)
return B_ERROR;
switch (params->modulation) {
case DVB_MODULATION_QPSK: data = 0x00; break;
case DVB_MODULATION_16_QAM: data = 0x08; break;
case DVB_MODULATION_64_QAM: data = 0x10; break;
default: return B_ERROR;
}
switch (params->hierarchy) {
case DVB_HIERARCHY_NONE: break;
case DVB_HIERARCHY_1: data |= 0x01; break;
case DVB_HIERARCHY_2: data |= 0x02; break;
case DVB_HIERARCHY_4: data |= 0x03; break;
default: return B_ERROR;
}
if (cx22702_reg_write(bus, 0x06, data) != B_OK)
return B_ERROR;
switch (params->code_rate_hp) {
case DVB_FEC_NONE: data = 0x00; break;
case DVB_FEC_1_2: data = 0x00; break;
case DVB_FEC_2_3: data = 0x08; break;
case DVB_FEC_3_4: data = 0x10; break;
case DVB_FEC_5_6: data = 0x18; break;
case DVB_FEC_6_7: data = 0x20; break;
default: return B_ERROR;
}
switch (params->code_rate_lp) {
case DVB_FEC_NONE: break;
case DVB_FEC_1_2: break;
case DVB_FEC_2_3: data |= 0x01; break;
case DVB_FEC_3_4: data |= 0x02; break;
case DVB_FEC_5_6: data |= 0x03; break;
case DVB_FEC_6_7: data |= 0x04; break;
default: return B_ERROR;
}
if (cx22702_reg_write(bus, 0x07, data) != B_OK)
return B_ERROR;
switch (params->transmission_mode) {
case DVB_TRANSMISSION_MODE_2K: data = 0x00; break;
case DVB_TRANSMISSION_MODE_8K: data = 0x01; break;
default: return B_ERROR;
}
switch (params->guard_interval) {
case DVB_GUARD_INTERVAL_1_4: data |= 0x0c; break;
case DVB_GUARD_INTERVAL_1_8: data |= 0x08; break;
case DVB_GUARD_INTERVAL_1_16: data |= 0x04; break;
case DVB_GUARD_INTERVAL_1_32: break;
default: return B_ERROR;
}
if (cx22702_reg_write(bus, 0x08, data) != B_OK)
return B_ERROR;
if (cx22702_reg_read(bus, 0x0b, &data) != B_OK)
return B_ERROR;
if (cx22702_reg_write(bus, 0x0b, data | 0x02) != B_OK)
return B_ERROR;
if (cx22702_reg_write(bus, 0x00, 0x01) != B_OK)
return B_ERROR;
// cx22702_reg_dump(bus);
return B_OK;
}
status_t
cx22702_get_tuning_parameters(i2c_bus *bus, dvb_t_tuning_parameters_t *params)
{
uint8 reg01, reg02, reg03, reg0A, reg0C;
if (cx22702_reg_read(bus, 0x01, &reg01) != B_OK)
return B_ERROR;
if (cx22702_reg_read(bus, 0x02, &reg02) != B_OK)
return B_ERROR;
if (cx22702_reg_read(bus, 0x03, &reg03) != B_OK)
return B_ERROR;
if (cx22702_reg_read(bus, 0x0a, &reg0A) != B_OK)
return B_ERROR;
if (cx22702_reg_read(bus, 0x0c, &reg0C) != B_OK)
return B_ERROR;
memset(params, 0, sizeof(*params));
params->inversion = (reg0C & 0x01) ? DVB_INVERSION_ON : DVB_INVERSION_OFF;
// XXX TODO...
return B_OK;
}
status_t
cx22702_get_status(i2c_bus *bus, dvb_status_t *status)
{
uint8 reg0A, reg23;
if (cx22702_reg_read(bus, 0x0a, &reg0A) != B_OK)
return B_ERROR;
if (cx22702_reg_read(bus, 0x23, &reg23) != B_OK)
return B_ERROR;
*status = 0;
if (reg0A & 0x10)
*status |= DVB_STATUS_LOCK | DVB_STATUS_VITERBI | DVB_STATUS_SYNC;
if (reg0A & 0x20)
*status |= DVB_STATUS_CARRIER;
if (reg23 < 0xf0)
*status |= DVB_STATUS_SIGNAL;
return B_OK;
}
status_t
cx22702_get_ss(i2c_bus *bus, uint32 *ss)
{
uint8 reg23;
if (cx22702_reg_read(bus, 0x23, &reg23) != B_OK)
return B_ERROR;
*ss = reg23;
return B_OK;
}
status_t
cx22702_get_ber(i2c_bus *bus, uint32 *ber)
{
uint8 regDE_1, regDE_2, regDF;
int trys;
trys = 50;
do {
if (cx22702_reg_read(bus, 0xDE, &regDE_1) != B_OK)
return B_ERROR;
if (cx22702_reg_read(bus, 0xDF, &regDF) != B_OK)
return B_ERROR;
if (cx22702_reg_read(bus, 0xDE, &regDE_2) != B_OK)
return B_ERROR;
} while (regDE_1 != regDE_2 && --trys > 0);
if (trys == 0)
return B_ERROR;
*ber = (regDE_1 & 0x7f) << 7 | (regDF & 0x7f);
return B_OK;
}
status_t
cx22702_get_snr(i2c_bus *bus, uint32 *snr)
{
uint32 ber;
status_t stat = cx22702_get_ber(bus, &ber);
*snr = 16384 - ber;
return stat;
}
status_t
cx22702_get_upc(i2c_bus *bus, uint32 *upc)
{
uint8 regE3;
if (cx22702_reg_read(bus, 0xE3, &regE3) != B_OK)
return B_ERROR;
if (cx22702_reg_write(bus, 0xE3, 0) != B_OK)
return B_ERROR;
*upc = regE3;
return B_OK;
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __CX22702_H
#define __CX22702_H
#include "i2c-core.h"
#include "dvb.h"
status_t cx22702_reg_write(i2c_bus *bus, uint8 reg, uint8 data);
status_t cx22702_reg_read(i2c_bus *bus, uint8 reg, uint8 *data);
status_t cx22702_init(i2c_bus *bus);
status_t cx22702_get_frequency_info(i2c_bus *bus, dvb_frequency_info_t *info);
status_t cx22702_set_tuning_parameters(i2c_bus *bus, const dvb_t_tuning_parameters_t *params);
status_t cx22702_get_tuning_parameters(i2c_bus *bus, dvb_t_tuning_parameters_t *params);
status_t cx22702_get_status(i2c_bus *bus, dvb_status_t *status);
status_t cx22702_get_ss(i2c_bus *bus, uint32 *ss);
status_t cx22702_get_ber(i2c_bus *bus, uint32 *ber);
status_t cx22702_get_snr(i2c_bus *bus, uint32 *snr);
status_t cx22702_get_upc(i2c_bus *bus, uint32 *upc);
#endif

View File

@ -0,0 +1,377 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "cx23882.h"
#include "util.h"
#include <KernelExport.h>
#include <ByteOrder.h>
#include <Drivers.h>
#define TRACE_CX23882
#ifdef TRACE_CX23882
#define TRACE dprintf
#else
#define TRACE(a...)
#endif
// settings for hardware stream sync
#define MPEG2_SYNC_BYTE 0x47
#define MPEG2_PACKET_SIZE 188
#define SYNC_PACKET_COUNT 7 // 0 and 5 don't seem to work
// Line size is also used as FIFO size!
// BYTES_PER_LINE must be a multiple of 8 and <= 4096 bytes
#define PACKETS_PER_LINE 20
#define BYTES_PER_LINE (PACKETS_PER_LINE * MPEG2_PACKET_SIZE)
#define SRAM_START_ADDRESS 0x180000
#define SRAM_BASE_CMDS_TS 0x200
#define SRAM_BASE_RISC_PROG 0x400
#define SRAM_BASE_RISC_QUEUE 0x800
#define SRAM_BASE_CDT 0x900
#define SRAM_BASE_FIFO_0 0x1000
#define SRAM_BASE_FIFO_1 0x2000
// About 64 kByte DMA buffer size
#define LINES_PER_BUFFER 16
#define DMA_BUFFER_SIZE (LINES_PER_BUFFER * BYTES_PER_LINE)
static status_t cx23882_buffers_alloc(cx23882_device *device);
static void cx23882_buffers_free(cx23882_device *device);
static void cx23882_risc_ram_setup(cx23882_device *device);
static void cx23882_sram_setup(cx23882_device *device);
static void cx23882_via_sis_fixup(cx23882_device *device);
void
cx23882_reset(cx23882_device *device)
{
// software reset (XXX Test)
reg_write32(0x38c06c, 1);
snooze(200000);
// disable RISC controller
reg_write32(REG_DEV_CNTRL2, 0);
// disable TS interface DMA
reg_write32(REG_TS_DMA_CNTRL, 0x0);
// disable VIP interface up- & downstram DMA
reg_write32(REG_VIP_STREAM_EN, 0x0);
// disable host interface up- & downstram DMA
reg_write32(REG_HST_STREAM_EN, 0x0);
// stop all interrupts
reg_write32(REG_PCI_INT_MSK, 0x0);
reg_write32(REG_VID_INT_MSK, 0x0);
reg_write32(REG_AUD_INT_MSK, 0x0);
reg_write32(REG_TS_INT_MSK, 0x0);
reg_write32(REG_VIP_INT_MSK, 0x0);
reg_write32(REG_HST_INT_MSK, 0x0);
reg_write32(REG_DMA_RISC_INT_MSK, 0x0);
// clear all pending interrupts
reg_write32(REG_PCI_INT_STAT, 0xffffffff);
reg_write32(REG_VID_INT_STAT, 0xffffffff);
reg_write32(REG_AUD_INT_STAT, 0xffffffff);
reg_write32(REG_TS_INT_STAT, 0xffffffff);
reg_write32(REG_VIP_INT_STAT, 0xffffffff);
reg_write32(REG_HST_INT_STAT, 0xffffffff);
reg_write32(REG_DMA_RISC_INT_MSK, 0xffffffff);
}
status_t
cx23882_init(cx23882_device *device)
{
// assumes that cx23882_reset() has already been called
status_t err;
if ((err = cx23882_buffers_alloc(device)) < B_OK) {
dprintf("cx23882: Error, buffer alloc failed\n");
return err;
}
device->capture_size = DMA_BUFFER_SIZE;
cx23882_via_sis_fixup(device);
// Set FIFO thresholds, should be 0 < x <= 7
reg_write32(REG_PDMA_STHRSH, PDMA_ISBTHRSH_6 | PDMA_PCITHRSH_6);
reg_write32(REG_PDMA_DTHRSH, PDMA_ISBTHRSH_6 | PDMA_PCITHRSH_6);
// init risc programm
cx23882_risc_ram_setup(device);
// init sram
cx23882_sram_setup(device);
// Reset counter to 0
reg_write32(REG_TS_GP_CNT_CNTRL, 0x3);
// Line length for RISC DMA
reg_write32(REG_TS_LNGTH, BYTES_PER_LINE);
// Set serial interface mode
reg_write32(REG_TS_GEN_CONTROL, reg_read32(REG_TS_GEN_CONTROL) | TS_GEN_CONTROL_IPB_SMODE);
// Setup hardware MPEG2 fec interface
reg_write32(REG_HW_SOP_CONTROL, (MPEG2_SYNC_BYTE << 16) | (MPEG2_PACKET_SIZE << 4) | SYNC_PACKET_COUNT);
// Setup TSSTOP status, active low, rising and falling edge, single bit width
reg_write32(REG_TS_SOP_STATUS, reg_read32(REG_TS_SOP_STATUS) | 0x18000);
reg_write32(REG_TS_SOP_STATUS, reg_read32(REG_TS_SOP_STATUS) & ~0x06000);
// Enable interrupts for MPEG TS and all errors
reg_write32(REG_PCI_INT_MSK, reg_read32(REG_PCI_INT_MSK) | PCI_INT_STAT_TS_INT | 0x00fc00);
reg_write32(REG_TS_INT_MSK, reg_read32(REG_TS_INT_MSK) | TS_INT_STAT_TS_RISC1 | TS_INT_STAT_TS_RISC2 | 0x1f1100);
TRACE("cx23882_init done\n");
return B_OK;
}
status_t
cx23882_terminate(cx23882_device *device)
{
cx23882_reset(device);
cx23882_buffers_free(device);
return B_OK;
}
status_t
cx23882_start_capture(cx23882_device *device)
{
TRACE("cx23882_start_capture\n");
// start RISC processor and DMA
reg_write32(REG_DEV_CNTRL2, reg_read32(REG_DEV_CNTRL2) | DEV_CNTRL2_RUN_RISC);
reg_write32(REG_TS_DMA_CNTRL, reg_read32(REG_TS_DMA_CNTRL) | TS_DMA_CNTRL_TS_FIFO_EN | TS_DMA_CNTRL_TS_RISC_EN);
return B_OK;
}
status_t
cx23882_stop_capture(cx23882_device *device)
{
TRACE("cx23882_stop_capture\n");
// stop RISC processor and DMA
reg_write32(REG_TS_DMA_CNTRL, reg_read32(REG_TS_DMA_CNTRL) & ~(TS_DMA_CNTRL_TS_FIFO_EN | TS_DMA_CNTRL_TS_RISC_EN));
reg_write32(REG_DEV_CNTRL2, reg_read32(REG_DEV_CNTRL2) & ~DEV_CNTRL2_RUN_RISC);
return B_OK;
}
static inline void
cx23882_mpegts_int(cx23882_device *device)
{
uint32 mstat = reg_read32(REG_TS_INT_MSTAT);
reg_write32(REG_TS_INT_STAT, mstat);
// dprintf("cx23882_mpegts_int got 0x%08lx\n", mstat);
if (mstat & TS_INT_STAT_OPC_ERR) {
dprintf("cx23882_mpegts_int RISC opcode error\n");
reg_write32(REG_PCI_INT_MSK, 0);
return;
}
if ((mstat & (TS_INT_STAT_TS_RISC1 | TS_INT_STAT_TS_RISC2)) == (TS_INT_STAT_TS_RISC1 | TS_INT_STAT_TS_RISC2)) {
dprintf("cx23882_mpegts_int both buffers ready\n");
mstat = TS_INT_STAT_TS_RISC1;
}
if (mstat & TS_INT_STAT_TS_RISC1) {
int32 count;
// dprintf("cx23882_mpegts_int buffer 1 at %Ld\n", system_time());
device->capture_data = device->dma_buf1_virt;
device->capture_end_time = system_time();
get_sem_count(device->capture_sem, &count);
if (count <= 0)
release_sem_etc(device->capture_sem, 1, B_DO_NOT_RESCHEDULE);
}
if (mstat & TS_INT_STAT_TS_RISC2) {
int32 count;
// dprintf("cx23882_mpegts_int buffer 2 at %Ld\n", system_time());
device->capture_data = device->dma_buf2_virt;
device->capture_end_time = system_time();
get_sem_count(device->capture_sem, &count);
if (count <= 0)
release_sem_etc(device->capture_sem, 1, B_DO_NOT_RESCHEDULE);
}
}
int32
cx23882_int(void *data)
{
cx23882_device *device = data;
uint32 mstat;
uint32 wmstat;
mstat = reg_read32(REG_PCI_INT_MSTAT);
if (!mstat)
return B_UNHANDLED_INTERRUPT;
if (mstat & (PCI_INT_STAT_HST_INT | PCI_INT_STAT_VIP_INT | PCI_INT_STAT_AUD_INT | PCI_INT_STAT_VID_INT)) {
// serious error, these bits should not be set
dprintf("cx23882_int error: msk 0x%08lx, stat 0x%08lx, mstat 0x%08lx\n", reg_read32(REG_PCI_INT_MSK), reg_read32(REG_PCI_INT_STAT), mstat);
reg_write32(REG_PCI_INT_MSK, 0);
return B_HANDLED_INTERRUPT;
}
wmstat = mstat & ~(PCI_INT_STAT_HST_INT | PCI_INT_STAT_VIP_INT | PCI_INT_STAT_TS_INT | PCI_INT_STAT_AUD_INT | PCI_INT_STAT_VID_INT);
if (wmstat)
reg_write32(REG_PCI_INT_STAT, wmstat);
if (wmstat)
dprintf("cx23882_int got 0x%08lx\n", wmstat);
if (mstat & PCI_INT_STAT_TS_INT) {
cx23882_mpegts_int(device);
return B_INVOKE_SCHEDULER;
} else {
return B_HANDLED_INTERRUPT;
}
}
static status_t
cx23882_buffers_alloc(cx23882_device *device)
{
device->dma_buf1_area = alloc_mem(&device->dma_buf1_virt, &device->dma_buf1_phys, DMA_BUFFER_SIZE, B_READ_AREA, "cx23882 dma buf 1");
device->dma_buf2_area = alloc_mem(&device->dma_buf2_virt, &device->dma_buf2_phys, DMA_BUFFER_SIZE, B_READ_AREA, "cx23882 dma buf 2");
if (device->dma_buf1_area < B_OK || device->dma_buf2_area < B_OK) {
cx23882_buffers_free(device);
return B_NO_MEMORY;
}
return B_OK;
}
static void
cx23882_buffers_free(cx23882_device *device)
{
if (device->dma_buf1_area >= 0)
delete_area(device->dma_buf1_area);
if (device->dma_buf2_area >= 0)
delete_area(device->dma_buf2_area);
device->dma_buf1_area = -1;
device->dma_buf2_area = -1;
}
static void
cx23882_sram_setup(cx23882_device *device)
{
dprintf("cx23882_sram_setup enter\n");
// setup CDT entries for both FIFOs
reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CDT, SRAM_START_ADDRESS + SRAM_BASE_FIFO_0);
reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CDT + 16, SRAM_START_ADDRESS + SRAM_BASE_FIFO_1);
// setup CDMS
reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x00, SRAM_START_ADDRESS + SRAM_BASE_RISC_PROG);
reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x04, SRAM_START_ADDRESS + SRAM_BASE_CDT);
reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x08, (2 * 16) / 8); // FIFO count = 2
reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x0c, SRAM_START_ADDRESS + SRAM_BASE_RISC_QUEUE);
reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x10, 0x80000000 | (0x100 / 4));
// setup DMA registers
reg_write32(REG_DMA28_PTR1, SRAM_START_ADDRESS + SRAM_BASE_FIFO_0);
reg_write32(REG_DMA28_PTR2, SRAM_START_ADDRESS + SRAM_BASE_CDT);
reg_write32(REG_DMA28_CNT1, BYTES_PER_LINE / 8);
reg_write32(REG_DMA28_CNT2, (2 * 16) / 8); // FIFO count = 2
dprintf("cx23882_sram_setup leave\n");
}
static void
cx23882_risc_ram_setup(cx23882_device *device)
{
char *start = (char *)(device->regs) + SRAM_START_ADDRESS + SRAM_BASE_RISC_PROG;
volatile uint32 *rp = (volatile uint32 *)start;
int i;
#define set_opcode(a) (*rp++) = B_HOST_TO_LENDIAN_INT32((a))
dprintf("cx23882_risc_ram_setup enter\n");
// sync
set_opcode(RISC_RESYNC | 0);
// copy buffer 1
for (i = 0; i < LINES_PER_BUFFER; i++) {
set_opcode(RISC_WRITE | RISC_SOL | RISC_EOL | BYTES_PER_LINE);
set_opcode((unsigned long)device->dma_buf1_phys + i * BYTES_PER_LINE);
}
// execute IRQ 1
set_opcode(RISC_SKIP | RISC_IRQ1 | RISC_SOL | 0);
// copy buffer 2
for (i = 0; i < LINES_PER_BUFFER; i++) {
set_opcode(RISC_WRITE | RISC_SOL | RISC_EOL | BYTES_PER_LINE);
set_opcode((unsigned long)device->dma_buf2_phys + i * BYTES_PER_LINE);
}
// execute IRQ 2
set_opcode(RISC_SKIP | RISC_IRQ2 | RISC_SOL | 0);
// jmp to start, but skip sync instruction
set_opcode(RISC_JUMP | RISC_SRP);
set_opcode(SRAM_START_ADDRESS + SRAM_BASE_RISC_PROG + 4);
#undef set_opcode
dprintf("cx23882_risc_ram_setup leave\n");
}
static void
cx23882_via_sis_fixup(cx23882_device *device)
{
uint16 host_vendor;
uint32 dev_cntrl1;
host_vendor = gPci->read_pci_config(0, 0, 0, PCI_vendor_id, 2);
dev_cntrl1 = reg_read32(REG_F2_DEV_CNTRL1);
if (host_vendor == PCI_VENDOR_VIA || host_vendor == PCI_VENDOR_SIS) {
dprintf("cx23882: enabling VIA/SIS compatibility mode\n");
reg_write32(REG_F2_DEV_CNTRL1, dev_cntrl1 | F2_DEV_CNTRL1_EN_VSFX);
} else {
dprintf("cx23882: disabling VIA/SIS compatibility mode\n");
reg_write32(REG_F2_DEV_CNTRL1, dev_cntrl1 & ~F2_DEV_CNTRL1_EN_VSFX);
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __CX23882_H
#define __CX23882_H
#include <SupportDefs.h>
#include "driver.h"
#include "cx23882_regs.h"
#include "i2c-core.h"
typedef struct {
const pci_info * pci_info;
int irq;
void * regs;
area_id regs_area;
uint32 i2c_reg;
i2c_bus * i2c_bus;
area_id dma_buf1_area;
void * dma_buf1_virt;
void * dma_buf1_phys;
area_id dma_buf2_area;
void * dma_buf2_virt;
void * dma_buf2_phys;
sem_id capture_sem;
void * capture_data;
size_t capture_size;
bigtime_t capture_end_time;
} cx23882_device;
#define reg_read8(offset) (*(volatile uint8 *) ((char *)(device->regs) + (offset)))
#define reg_read16(offset) (*(volatile uint16 *)((char *)(device->regs) + (offset)))
#define reg_read32(offset) (*(volatile uint32 *)((char *)(device->regs) + (offset)))
#define reg_write8(offset, value) (*(volatile uint8 *) ((char *)(device->regs) + (offset)) = value)
#define reg_write16(offset, value) (*(volatile uint16 *)((char *)(device->regs) + (offset)) = value)
#define reg_write32(offset, value) (*(volatile uint32 *)((char *)(device->regs) + (offset)) = value)
void cx23882_reset(cx23882_device *device);
status_t cx23882_init(cx23882_device *device);
status_t cx23882_terminate(cx23882_device *device);
status_t cx23882_start_capture(cx23882_device *device);
status_t cx23882_stop_capture(cx23882_device *device);
int32 cx23882_int(void *data);
#endif

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "i2c.h"
#include "i2c-core.h"
static void
set_scl(void *cookie, int state)
{
cx23882_device * device = cookie;
if (state)
device->i2c_reg |= I2C_SCL;
else
device->i2c_reg &= ~I2C_SCL;
reg_write32(REG_I2C_CONTROL, device->i2c_reg);
reg_read32(REG_I2C_CONTROL); // PCI bridge flush
}
static void
set_sda(void *cookie, int state)
{
cx23882_device * device = cookie;
if (state)
device->i2c_reg |= I2C_SDA;
else
device->i2c_reg &= ~I2C_SDA;
reg_write32(REG_I2C_CONTROL, device->i2c_reg);
reg_read32(REG_I2C_CONTROL); // PCI bridge flush
}
static int
get_scl(void *cookie)
{
cx23882_device * device = cookie;
return (reg_read32(REG_I2C_CONTROL) & I2C_SCL) >> 1; // I2C_SCL is 0x02
}
static int
get_sda(void *cookie)
{
cx23882_device * device = cookie;
return reg_read32(REG_I2C_CONTROL) & I2C_SDA; // I2C_SDA is 0x01
}
status_t
i2c_init(cx23882_device *device)
{
device->i2c_bus = i2c_create_bus(device, 80000, 2000000, set_scl, set_sda, get_scl, get_sda);
device->i2c_reg = reg_read32(REG_I2C_CONTROL);
device->i2c_reg &= ~I2C_HW_MODE;
device->i2c_reg |= I2C_SCL | I2C_SDA;
reg_write32(REG_I2C_CONTROL, device->i2c_reg);
reg_read32(REG_I2C_CONTROL); // PCI bridge flush
return device->i2c_bus ? B_OK : B_ERROR;
}
void
i2c_terminate(cx23882_device *device)
{
i2c_delete_bus(device->i2c_bus);
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __I2C_H
#define __I2C_H
#include "cx23882.h"
status_t i2c_init(cx23882_device *device);
void i2c_terminate(cx23882_device *device);
#endif

View File

@ -0,0 +1,144 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __CX23882_REGS_H
#define __CX23882_REGS_H
#define PCI_PCICMD_IOS 0x01
#define PCI_PCICMD_MSE 0x02
#define PCI_PCICMD_BME 0x04
#define PCI_VENDOR_SIS 0x1039
#define PCI_VENDOR_VIA 0x1106
#define REG_PDMA_STHRSH 0x200000
#define REG_PDMA_DTHRSH 0x200010
#define PDMA_ISBTHRSH_1 0x0100
#define PDMA_ISBTHRSH_2 0x0200
#define PDMA_ISBTHRSH_3 0x0300
#define PDMA_ISBTHRSH_4 0x0400
#define PDMA_ISBTHRSH_5 0x0500
#define PDMA_ISBTHRSH_6 0x0600
#define PDMA_ISBTHRSH_7 0x0700
#define PDMA_PCITHRSH_1 0x0001
#define PDMA_PCITHRSH_2 0x0002
#define PDMA_PCITHRSH_3 0x0003
#define PDMA_PCITHRSH_4 0x0004
#define PDMA_PCITHRSH_5 0x0005
#define PDMA_PCITHRSH_6 0x0006
#define PDMA_PCITHRSH_7 0x0007
#define REG_DEV_CNTRL2 0x200034
#define DEV_CNTRL2_RUN_RISC 0x20
#define REG_PCI_INT_MSK 0x200040
#define REG_PCI_INT_STAT 0x200044
#define REG_PCI_INT_MSTAT 0x200048
#define PCI_INT_STAT_VID_INT 0x01
#define PCI_INT_STAT_AUD_INT 0x02
#define PCI_INT_STAT_TS_INT 0x04
#define PCI_INT_STAT_VIP_INT 0x08
#define PCI_INT_STAT_HST_INT 0x10
#define REG_VID_INT_MSK 0x200050
#define REG_VID_INT_STAT 0x200054
#define REG_VID_INT_MSTAT 0x200058
#define REG_AUD_INT_MSK 0x200060
#define REG_AUD_INT_STAT 0x200064
#define REG_AUD_INT_MSTAT 0x200068
#define REG_TS_INT_MSK 0x200070
#define REG_TS_INT_STAT 0x200074
#define REG_TS_INT_MSTAT 0x200078
#define TS_INT_STAT_TS_RISC1 0x000001
#define TS_INT_STAT_TS_RISC2 0x000010
#define TS_INT_STAT_OPC_ERR 0x010000
#define REG_VIP_INT_MSK 0x200080
#define REG_VIP_INT_STAT 0x200084
#define REG_VIP_INT_MSTAT 0x200088
#define REG_HST_INT_MSK 0x200090
#define REG_HST_INT_STAT 0x200094
#define REG_HST_INT_MSTAT 0x200098
#define REG_F2_DEV_CNTRL1 0x2f0240
#define F2_DEV_CNTRL1_EN_VSFX 0x8
#define REG_DMA28_PTR1 0x30009c
#define REG_DMA28_PTR2 0x3000dc
#define REG_DMA28_CNT1 0x30011c
#define REG_DMA28_CNT2 0x30015c
#define REG_TS_GP_CNT_CNTRL 0x33c030
#define REG_TS_DMA_CNTRL 0x33c040
#define TS_DMA_CNTRL_TS_FIFO_EN 0x01
#define TS_DMA_CNTRL_TS_RISC_EN 0x10
#define REG_TS_LNGTH 0x33c048
#define REG_HW_SOP_CONTROL 0x33c04c
#define REG_TS_GEN_CONTROL 0x33c050
#define TS_GEN_CONTROL_IPB_SMODE 0x08
#define REG_TS_BD_PKT_STATUS 0x33c054
#define REG_TS_SOP_STATUS 0x33c058
#define REG_VIP_STREAM_EN 0x34c040
// these 3 are not in my spec, taken form Linux
#define REG_DMA_RISC_INT_MSK 0x35C060
#define REG_DMA_RISC_INT_STAT 0x35C064
#define REG_DMA_RISC_INT_MSTAT 0x35C068
#define REG_I2C_CONTROL 0x368000
#define I2C_SDA 0x01
#define I2C_SCL 0x02
#define I2C_HW_MODE 0x80
#define REG_HST_STREAM_EN 0x38c040
// RISC instructions
#define RISC_RESYNC 0x80008000
#define RISC_WRITE 0x10000000
#define RISC_SKIP 0x20000000
#define RISC_JUMP 0x70000000
#define RISC_WRITECR 0xd0000000
#define RISC_IMM 0x00000001
#define RISC_SOL 0x08000000
#define RISC_EOL 0x04000000
#define RISC_IRQ2 0x02000000
#define RISC_IRQ1 0x01000000
#define RISC_SRP 0x00000001
#endif

View File

@ -0,0 +1,301 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <KernelExport.h>
#include <Drivers.h>
#include <Errors.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "version.h"
#include "config.h"
#include "driver.h"
#include "mktime.h"
#define TRACE_DRIVER
#ifdef TRACE_DRIVER
#define TRACE dprintf
#else
#define TRACE(a...)
#endif
typedef struct
{
int vendor;
int device;
int subvendor;
int subdevice;
const char *name;
} card_info;
int32 api_version = B_CUR_DRIVER_API_VERSION;
pci_module_info * gPci;
static char * sDevNameList[MAX_CARDS + 1];
static pci_info * sDevList[MAX_CARDS];
static int32 sOpenMask;
static card_info sCardTable[] =
{
{ 0x14f1, 0x8802, 0x0070, 0x9002, "Hauppauge WinTV-NOVA-T model 928" },
{ /* end */ }
};
typedef struct
{
void * cookie;
int dev_id;
} interface_cookie;
static const char *
identify_device(const card_info *cards, const pci_info *info)
{
for (; cards->name; cards++) {
if (cards->vendor >= 0 && cards->vendor != info->vendor_id)
continue;
if (cards->device >= 0 && cards->device != info->device_id)
continue;
if ((info->header_type & PCI_header_type_mask) != PCI_header_type_generic)
continue;
if (cards->subvendor >= 0 && cards->subvendor != info->u.h0.subsystem_vendor_id)
continue;
if (cards->subdevice >= 0 && cards->subdevice != info->u.h0.subsystem_id)
continue;
return cards->name;
}
return NULL;
}
status_t
init_hardware(void)
{
pci_info info;
status_t res;
int i;
TRACE("cx23882: init_hardware()\n");
if (get_module(B_PCI_MODULE_NAME, (module_info **)&gPci) < B_OK)
return B_ERROR;
for (res = B_ERROR, i = 0; gPci->get_nth_pci_info(i, &info) == B_OK; i++) {
if (identify_device(sCardTable, &info)) {
res = B_OK;
break;
}
}
put_module(B_PCI_MODULE_NAME);
gPci = NULL;
return res;
}
status_t
init_driver(void)
{
struct pci_info *item;
int index;
int cards;
#ifdef DEBUG
set_dprintf_enabled(true);
load_driver_symbols("cx23882");
#endif
dprintf(gBanner);
item = (pci_info *)malloc(sizeof(pci_info));
if (!item)
return B_NO_MEMORY;
if (get_module(B_PCI_MODULE_NAME, (module_info **)&gPci) < B_OK) {
free(item);
return B_ERROR;
}
for (cards = 0, index = 0; gPci->get_nth_pci_info(index++, item) == B_OK; ) {
const char *info = identify_device(sCardTable, item);
if (info) {
char name[64];
sprintf(name, "dvb/cx23882/%d", cards + 1);
dprintf("cx23882: /dev/%s is a %s\n", name, info);
sDevList[cards] = item;
sDevNameList[cards] = strdup(name);
sDevNameList[cards + 1] = NULL;
cards++;
item = (pci_info *)malloc(sizeof(pci_info));
if (!item)
goto err_outofmem;
if (cards == MAX_CARDS)
break;
}
}
free(item);
if (!cards)
goto err_cards;
return B_OK;
err_outofmem:
TRACE("cx23882: err_outofmem\n");
for (index = 0; index < cards; index++) {
free(sDevList[index]);
free(sDevNameList[index]);
}
err_cards:
put_module(B_PCI_MODULE_NAME);
return B_ERROR;
}
void
uninit_driver(void)
{
int32 i;
TRACE("cx23882: uninit_driver\n");
for (i = 0; sDevNameList[i] != NULL; i++) {
free(sDevList[i]);
free(sDevNameList[i]);
}
put_module(B_PCI_MODULE_NAME);
}
static status_t
driver_open(const char *name, uint32 flags, void** _cookie)
{
interface_cookie *cookie;
char *deviceName;
status_t status;
int dev_id;
int mask;
TRACE("cx23882: driver open\n");
for (dev_id = 0; (deviceName = sDevNameList[dev_id]) != NULL; dev_id++) {
if (!strcmp(name, deviceName))
break;
}
if (deviceName == NULL) {
TRACE("cx23882: invalid device name\n");
return B_ERROR;
}
// allow only one concurrent access
mask = 1 << dev_id;
if (atomic_or(&sOpenMask, mask) & mask)
return B_BUSY;
cookie = (interface_cookie *)malloc(sizeof(interface_cookie));
if (!cookie)
return B_NO_MEMORY;
cookie->dev_id = dev_id;
status = interface_attach(&cookie->cookie, sDevList[dev_id]);
if (status != B_OK) {
free(cookie);
atomic_and(&sOpenMask, ~(1 << dev_id));
return status;
}
*_cookie = cookie;
return B_OK;
}
static status_t
driver_close(void* cookie)
{
TRACE("cx23882: driver close\n");
interface_detach(((interface_cookie *)cookie)->cookie);
return B_OK;
}
static status_t
driver_free(void* cookie)
{
TRACE("cx23882: driver free\n");
atomic_and(&sOpenMask, ~(1 << ((interface_cookie *)cookie)->dev_id));
return B_OK;
}
static status_t
driver_read(void* cookie, off_t position, void *buf, size_t* num_bytes)
{
TRACE("cx23882: driver read\n");
*num_bytes = 0; // required by design for read hook!
return B_ERROR;
}
static status_t
driver_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
{
TRACE("cx23882: driver write\n");
*num_bytes = 0; // not sure if required for write hook
return B_ERROR;
}
static status_t
driver_control(void *cookie, uint32 op, void *arg, size_t len)
{
// TRACE("cx23882: driver control\n");
return interface_ioctl(((interface_cookie *)cookie)->cookie, op, arg, len);
}
static device_hooks
sDeviceHooks = {
driver_open,
driver_close,
driver_free,
driver_control,
driver_read,
driver_write,
};
const char**
publish_devices(void)
{
return (const char**)sDevNameList;
}
device_hooks*
find_device(const char* name)
{
return &sDeviceHooks;
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __DRIVER_H
#define __DRIVER_H
#include <PCI.h>
extern pci_module_info *gPci;
#endif

View File

@ -0,0 +1,168 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <KernelExport.h>
#include "dtt7592.h"
#include "config.h"
#define TRACE_DTT7592
#ifdef TRACE_DTT7592
#define TRACE dprintf
#else
#define TRACE(a...)
#endif
status_t
dtt7592_write(i2c_bus *bus, const uint8 data[4])
{
status_t res;
TRACE("dtt7592_write values 0x%02x 0x%02x 0x%02x 0x%02x\n", data[0], data[1], data[2], data[3]);
res = i2c_write(bus, I2C_ADDR_PLL, data, 4);
if (res != B_OK)
TRACE("dtt7592_write error, values 0x%02x 0x%02x 0x%02x 0x%02x\n", data[0], data[1], data[2], data[3]);
return res;
}
status_t
dtt7592_read(i2c_bus *bus, uint8 *data)
{
status_t res;
res = i2c_read(bus, I2C_ADDR_PLL, data, 1);
if (res != B_OK)
TRACE("dtt7592_read error\n");
return res;
}
status_t
dtt7592_set_frequency(i2c_bus *bus, uint32 frequency, dvb_bandwidth_t bandwidth)
{
status_t res;
uint32 divider;
uint8 data[4];
divider = (frequency + 36083333) / 166666;
if (divider > 0x7fff)
divider = 0x7fff;
TRACE("dtt7592_set_frequency frequency %ld, divider 0x%lx (%ld)\n", frequency, divider, divider);
data[0] = divider >> 8;
data[1] = (uint8)divider;
if (frequency > 835000000)
data[2] = 0xfc;
else if (frequency > 735000000)
data[2] = 0xf4;
else if (frequency > 465000000)
data[2] = 0xbc;
else if (frequency > 445000000)
data[2] = 0xfc;
else if (frequency > 405000000)
data[2] = 0xf4;
else if (frequency > 305000000)
data[2] = 0xbc;
else
data[2] = 0xb4;
if (frequency > 429000000)
data[3] = 0x08; // select UHF IV/V
else
data[3] = 0x02; // select VHF III
// only used in Germany right now, where VHF channels
// are 7 MHz wide, while UHF are 8 MHz.
if (bandwidth == DVB_BANDWIDTH_5_MHZ
|| bandwidth == DVB_BANDWIDTH_6_MHZ
|| bandwidth == DVB_BANDWIDTH_7_MHZ) {
data[3] |= 0x10; // activate 7 Mhz filter
}
res = dtt7592_write(bus, data);
if (res != B_OK) {
dprintf("dtt7592_set_frequency step 1 failed\n");
return res;
}
// enable AGC
data[2] = (data[2] & 0x40) | 0x9c;
data[3] = 0xa0;
res = dtt7592_write(bus, data);
if (res != B_OK) {
dprintf("dtt7592_set_frequency step 2 failed\n");
return res;
}
// wait 100 ms
snooze(100000);
// disable AGC
data[3] = 0x20;
res = dtt7592_write(bus, data);
if (res != B_OK) {
dprintf("dtt7592_set_frequency step 3 failed\n");
return res;
}
return B_OK;
}
void
dtt7582_dump(i2c_bus *bus)
{
uint8 data;
if (B_OK != dtt7592_read(bus, &data)) {
TRACE("dtt7582_dump failed\n");
}
TRACE("dtt7582_dump: 0x%02x, PLL %s, AGC %s\n", data, (data & 0x40) ? "locked" : "open", (data & 0x08) ? "active" : "off");
}
void
dtt7582_test(i2c_bus *bus)
{
TRACE("dtt7582_test start\n");
dtt7582_dump(bus);
TRACE("dtt7582_test freq 1\n");
dtt7592_set_frequency(bus, 150000000, DVB_BANDWIDTH_7_MHZ);
dtt7582_dump(bus);
TRACE("dtt7582_test freq 2\n");
dtt7592_set_frequency(bus, 746000000, DVB_BANDWIDTH_8_MHZ); // Kabel 1
dtt7582_dump(bus);
TRACE("dtt7582_test freq 3\n");
dtt7592_set_frequency(bus, 538000000, DVB_BANDWIDTH_7_MHZ); // VOX
dtt7582_dump(bus);
TRACE("dtt7582_test freq 4\n");
dtt7592_set_frequency(bus, 896000000, DVB_BANDWIDTH_8_MHZ);
dtt7582_dump(bus);
TRACE("dtt7582_test freq 5\n");
dtt7592_set_frequency(bus, 333000000, DVB_BANDWIDTH_7_MHZ);
dtt7582_dump(bus);
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __DTT7592_H
#define __DTT7592_H
#include "i2c-core.h"
#include "dvb.h"
status_t dtt7592_write(i2c_bus *bus, const uint8 data[4]);
status_t dtt7592_read(i2c_bus *bus, uint8 *data);
status_t dtt7592_set_frequency(i2c_bus *bus, uint32 frequency, dvb_bandwidth_t bandwidth);
void dtt7582_test(i2c_bus *bus);
#endif

View File

@ -0,0 +1,321 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <KernelExport.h>
#include <stdlib.h>
#include "interface.h"
#include "cx23882.h"
#include "cx22702.h"
#include "dtt7592.h"
#include "driver.h"
#include "config.h"
#include "util.h"
#include "i2c.h"
#define TRACE_INTERFACE
#ifdef TRACE_INTERFACE
#define TRACE dprintf
#else
#define TRACE(a...)
#endif
static inline status_t user_memcpy(void *d, const void *s, size_t z) { memcpy(d, s, z); return B_OK; }
#define B_BAD_ADDRESS B_ERROR
static void
dump_eeprom(cx23882_device *device)
{
uint8 d[256+8];
uint8 adr;
uint8 *p;
int i;
status_t res;
adr = 0;
res = i2c_xfer(device->i2c_bus, I2C_ADDR_EEPROM, &adr, 1, d, sizeof(d));
if (res != B_OK) {
TRACE("i2c_read failed: %08lx\n", res);
return;
}
for (p = d, i = 0; i < ((int)sizeof(d) / 8); i++, p+= 8)
TRACE("EEPROM %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n", i * 8, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
}
status_t
interface_attach(void **cookie, const pci_info *info)
{
cx23882_device *device;
uint32 val;
int i;
TRACE("interface_attach\n");
device = malloc(sizeof(cx23882_device));
if (!device)
return B_NO_MEMORY;
*cookie = device;
// initialize cookie
memset(device, 0, sizeof(*device));
device->regs_area = -1;
device->dma_buf1_area = -1;
device->dma_buf2_area = -1;
device->capture_sem = -1;
device->pci_info = info;
// enable busmaster and memory mapped access, disable io port access
val = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_command, 2);
val = PCI_PCICMD_BME | PCI_PCICMD_MSE | (val & ~PCI_PCICMD_IOS);
gPci->write_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_command, 2, val);
// adjust PCI latency timer
val = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_latency, 1);
TRACE("PCI latency is %02lx, changing to %02x\n", val, PCI_LATENCY);
gPci->write_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_latency, 1, PCI_LATENCY);
// get IRQ
device->irq = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_interrupt_line, 1);
if (device->irq == 0 || device->irq == 0xff) {
dprintf("cx23882: Error, no IRQ assigned\n");
goto err;
}
TRACE("IRQ %d\n", device->irq);
// map registers into memory
val = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, 0x10, 4);
val &= PCI_address_memory_32_mask;
if (val == 0) {
dprintf("cx23882: Error, no memory space assigned\n");
goto err;
}
TRACE("hardware register address %p\n", (void *) val);
device->regs_area = map_mem(&device->regs, (void *)val, 16777216 /* 16 MB */, 0, "cx23882 registers");
if (device->regs_area < B_OK) {
dprintf("cx23882: Error, can't map hardware registers\n");
goto err;
}
TRACE("mapped registers to %p\n", device->regs);
device->capture_sem = create_sem(0, "cx23882 capture");
cx23882_reset(device);
if (i2c_init(device) < B_OK) {
dprintf("cx23882: Error, can't init I2C\n");
}
if (cx23882_init(device) < B_OK) {
dprintf("cx23882: Error, can't init hardware\n");
}
for (i = 0; i < 20; i++)
if (cx22702_init(device->i2c_bus) == B_OK)
break;
if (i == 20) {
TRACE("cx22702_init failed\n");
goto err;
}
// setup interrupt handler
if (install_io_interrupt_handler(device->irq, cx23882_int, device, 0) < B_OK) {
dprintf("cx23882: Error, can't install interrupt handler\n");
goto err;
}
// dump_eeprom(device);
// dtt7582_test(device->i2c_bus);
return B_OK;
err:
free(cookie);
return B_ERROR;
}
void
interface_detach(void *cookie)
{
cx23882_device *device = cookie;
i2c_terminate(device);
if (cx23882_terminate(device) < B_OK) {
}
remove_io_interrupt_handler(device->irq, cx23882_int, device);
delete_area(device->regs_area);
delete_sem(device->capture_sem);
TRACE("interface_detach\n");
}
static void
interface_get_interface_info(dvb_interface_info_t *info)
{
memset(info, 0, sizeof(*info));
info->version = 1;
info->flags = 0;
info->type = DVB_TYPE_DVB_T;
strcpy(info->name, "CX23882");
strcpy(info->info, "Hauppauge WinTV-NOVA-T model 928 driver, Copyright (c) 2005 Marcus Overhagen");
}
status_t
interface_ioctl(void *cookie, uint32 op, void *arg, size_t len)
{
cx23882_device *device = cookie;
status_t res;
switch (op) {
case DVB_GET_INTERFACE_INFO:
{
dvb_interface_info_t info;
interface_get_interface_info(&info);
if (user_memcpy(arg, &info, sizeof(info)) < B_OK)
return B_BAD_ADDRESS;
break;
}
case DVB_GET_FREQUENCY_INFO:
{
dvb_frequency_info_t info;
if ((res = cx22702_get_frequency_info(device->i2c_bus, &info)) < B_OK)
return res;
if (user_memcpy(arg, &info, sizeof(info)) < B_OK)
return B_BAD_ADDRESS;
break;
}
case DVB_START_CAPTURE:
{
return cx23882_start_capture(device);
}
case DVB_STOP_CAPTURE:
{
return cx23882_stop_capture(device);
}
case DVB_SET_TUNING_PARAMETERS:
{
dvb_tuning_parameters_t params;
if (user_memcpy(&params, arg, sizeof(params)) < B_OK)
return B_BAD_ADDRESS;
if ((res = cx22702_set_tuning_parameters(device->i2c_bus, &params.u.dvb_t)) < B_OK)
return res;
break;
}
case DVB_GET_TUNING_PARAMETERS:
{
dvb_tuning_parameters_t params;
if ((res = cx22702_get_tuning_parameters(device->i2c_bus, &params.u.dvb_t)) < B_OK)
return res;
if (user_memcpy(arg, &params, sizeof(params)) < B_OK)
return B_BAD_ADDRESS;
break;
}
case DVB_GET_STATUS:
{
dvb_status_t status;
if ((res = cx22702_get_status(device->i2c_bus, &status)) < B_OK)
return res;
if (user_memcpy(arg, &status, sizeof(status)) < B_OK)
return B_BAD_ADDRESS;
break;
}
case DVB_GET_SS:
{
uint32 value;
if ((res = cx22702_get_ss(device->i2c_bus, &value)) < B_OK)
return res;
if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
return B_BAD_ADDRESS;
break;
}
case DVB_GET_BER:
{
uint32 value;
if ((res = cx22702_get_ber(device->i2c_bus, &value)) < B_OK)
return res;
if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
return B_BAD_ADDRESS;
break;
}
case DVB_GET_SNR:
{
uint32 value;
if ((res = cx22702_get_snr(device->i2c_bus, &value)) < B_OK)
return res;
if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
return B_BAD_ADDRESS;
break;
}
case DVB_GET_UPC:
{
uint32 value;
if ((res = cx22702_get_upc(device->i2c_bus, &value)) < B_OK)
return res;
if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
return B_BAD_ADDRESS;
break;
}
case DVB_CAPTURE:
{
dvb_capture_t cap_data;
// wait for data ready interrupt, with 100 ms timeout (in case tuning failed, bad reception, etc)
if ((res = acquire_sem_etc(device->capture_sem, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, 100000)) < B_OK)
return res;
cap_data.data = device->capture_data;
cap_data.size = device->capture_size;
cap_data.end_time = device->capture_end_time;
if (user_memcpy(arg, &cap_data, sizeof(cap_data)) < B_OK)
return B_BAD_ADDRESS;
break;
}
default:
{
TRACE("interface_ioctl\n");
return B_BAD_VALUE;
}
}
return B_OK;
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __DVB_INTERFACE_H
#define __DVB_INTERFACE_H
status_t interface_attach(void **cookie, const pci_info *info);
void interface_detach(void *cookie);
status_t interface_ioctl(void *cookie, uint32 op, void *arg, size_t len);
#endif

View File

@ -0,0 +1,394 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <KernelExport.h>
#include "i2c-core.h"
#define TRACE_I2C
#ifdef TRACE_I2C
#define TRACE dprintf
#else
#define TRACE(a...)
#endif
static status_t i2c_writebyte(i2c_bus *bus, uint8 byte, int *ack);
static status_t i2c_readbyte(i2c_bus *bus, uint8 *pbyte);
static status_t i2c_read_unlocked(i2c_bus *bus, int address, void *data, int size);
static status_t i2c_write_unlocked(i2c_bus *bus, int address, const void *data, int size);
struct _i2c_bus
{
void *cookie;
bigtime_t delay;
bigtime_t timeout;
i2c_set_scl set_scl;
i2c_set_sda set_sda;
i2c_get_scl get_scl;
i2c_get_sda get_sda;
sem_id sem;
};
i2c_bus *
i2c_create_bus(void *cookie,
int frequency,
bigtime_t timeout,
i2c_set_scl set_scl,
i2c_set_sda set_sda,
i2c_get_scl get_scl,
i2c_get_sda get_sda)
{
i2c_bus *bus = malloc(sizeof(i2c_bus));
if (!bus)
return NULL;
bus->sem = create_sem(1, "i2c bus access");
if (bus->sem < 0) {
free(bus);
return NULL;
}
bus->cookie = cookie;
bus->delay = 1000000 / frequency;
if (bus->delay == 0)
bus->delay = 1;
bus->timeout = timeout;
bus->set_scl = set_scl;
bus->set_sda = set_sda;
bus->get_scl = get_scl;
bus->get_sda = get_sda;
set_scl(cookie, 1);
set_sda(cookie, 1);
return bus;
}
void
i2c_delete_bus(i2c_bus *bus)
{
if (!bus)
return;
delete_sem(bus->sem);
free(bus);
}
static inline void
set_sda_low(i2c_bus *bus)
{
bus->set_sda(bus->cookie, 0);
snooze(bus->delay);
}
static inline void
set_sda_high(i2c_bus *bus)
{
bus->set_sda(bus->cookie, 1);
snooze(bus->delay);
}
static inline void
set_scl_low(i2c_bus *bus)
{
bus->set_scl(bus->cookie, 0);
snooze(bus->delay);
}
static inline status_t
set_scl_high(i2c_bus *bus)
{
bigtime_t end = system_time() + bus->timeout;
bus->set_scl(bus->cookie, 1);
while (0 == bus->get_scl(bus->cookie)) {
if (system_time() > end)
return B_TIMED_OUT;
snooze(5);
}
snooze(bus->delay);
return B_OK;
}
static inline void
i2c_start(i2c_bus *bus)
{
set_sda_low(bus);
set_scl_low(bus);
}
static inline void
i2c_stop(i2c_bus *bus)
{
set_sda_low(bus);
set_scl_high(bus);
set_sda_high(bus);
}
static inline status_t
i2c_start_address(i2c_bus *bus, int address, int read /* 1 = read, 0 = write */)
{
status_t res;
uint8 addr;
int ack;
int i;
// TRACE("i2c_start_address: enter\n");
addr = (address << 1) | (read & 1);
for (i = 0; i < 5; i++) {
i2c_start(bus);
res = i2c_writebyte(bus, addr, &ack);
if (res == B_OK) {
if (ack)
break;
res = B_ERROR;
}
if (res == B_TIMED_OUT)
break;
i2c_stop(bus);
snooze(50);
}
// TRACE("i2c_start_address: %s, ack %d, i %d\n", strerror(res), ack, i);
return res;
}
/* write one byte and the ack/nack
* return values:
* B_OK => byte transmitted
* B_ERROR => arbitration lost
* B_TIMED_OUT => time out
*/
status_t
i2c_writebyte(i2c_bus *bus, uint8 byte, int *ack)
{
int has_arbitration;
int i;
has_arbitration = 1;
for (i = 7; i >= 0; i--) {
int bit = (byte >> i) & 1;
if (has_arbitration) {
if (bit)
set_sda_high(bus);
else
set_sda_low(bus);
}
if (set_scl_high(bus) != B_OK) {
set_sda_high(bus); // avoid blocking the bus
TRACE("i2c_writebyte timeout at bit %d\n", i);
return B_TIMED_OUT;
}
if (has_arbitration) {
if (bit == 1 && 0 == bus->get_sda(bus->cookie)) {
has_arbitration = 0;
// TRACE("i2c_writebyte lost arbitration at bit %d\n", i);
}
}
set_scl_low(bus);
}
set_sda_high(bus);
if (set_scl_high(bus) != B_OK) {
TRACE("i2c_writebyte timeout at ack\n");
return B_TIMED_OUT;
}
*ack = 0 == bus->get_sda(bus->cookie);
set_scl_low(bus);
return has_arbitration ? B_OK : B_ERROR;
}
/* read one byte, don't generate ack
*/
status_t
i2c_readbyte(i2c_bus *bus, uint8 *pbyte)
{
int i;
uint8 byte;
set_sda_high(bus);
byte = 0;
for (i = 7; i >= 0; i--) {
if (set_scl_high(bus) != B_OK) {
TRACE("i2c_readbyte timeout at bit %d\n", i);
return B_TIMED_OUT;
}
byte = (byte << 1) | bus->get_sda(bus->cookie);
set_scl_low(bus);
}
*pbyte = byte;
return B_OK;
}
status_t
i2c_read_unlocked(i2c_bus *bus, int address, void *data, int size)
{
status_t status;
uint8 *bytes;
if (size <= 0)
return B_BAD_VALUE;
status = i2c_start_address(bus, address, 1 /* 1 = read, 0 = write */);
if (status != B_OK) {
TRACE("i2c_read: error on i2c_start_address: %s\n", strerror(status));
return status;
}
bytes = data;
while (size > 0) {
if (i2c_readbyte(bus, bytes) != B_OK) { // timeout
TRACE("i2c_read: timeout error on byte %ld\n", bytes - (uint8 *)data);
return B_TIMED_OUT;
}
if (size > 0)
set_sda_low(bus); // ack
else
set_sda_high(bus); // nack
if (set_scl_high(bus) != B_OK) {
set_sda_high(bus); // avoid blocking the bus
TRACE("i2c_read: timeout at ack\n");
return B_TIMED_OUT;
}
set_scl_low(bus);
set_sda_high(bus);
size--;
bytes++;
}
i2c_stop(bus);
return B_OK;
}
status_t
i2c_write_unlocked(i2c_bus *bus, int address, const void *data, int size)
{
const uint8 *bytes;
status_t status;
int ack;
if (size <= 0)
return B_BAD_VALUE;
status = i2c_start_address(bus, address, 0 /* 1 = read, 0 = write */);
if (status != B_OK) {
TRACE("i2c_write: error on i2c_start_address: %s\n", strerror(status));
return status;
}
bytes = data;
while (size > 0) {
status = i2c_writebyte(bus, *bytes, &ack);
if (status == B_TIMED_OUT) {
TRACE("i2c_write: timeout error on byte %ld\n", bytes - (uint8 *)data);
return B_TIMED_OUT;
}
if (status != B_OK) {
TRACE("i2c_write: arbitration lost on byte %ld\n", bytes - (uint8 *)data);
break;
}
if (!ack) {
TRACE("i2c_write: error, got NACK on byte %ld\n", bytes - (uint8 *)data);
break;
}
bytes++;
size--;
}
i2c_stop(bus);
if (status != B_OK)
return status;
return ack ? B_OK : B_ERROR;
}
status_t
i2c_read(i2c_bus *bus, int address, void *data, int size)
{
status_t res;
acquire_sem(bus->sem);
res = i2c_read_unlocked(bus, address, data, size);
release_sem(bus->sem);
return res;
}
status_t
i2c_write(i2c_bus *bus, int address, const void *data, int size)
{
status_t res;
acquire_sem(bus->sem);
res = i2c_write_unlocked(bus, address, data, size);
release_sem(bus->sem);
return res;
}
status_t
i2c_xfer(i2c_bus *bus, int address,
const void *write_data, int write_size,
void *read_data, int read_size)
{
status_t res;
acquire_sem(bus->sem);
if (write_data != 0 && write_size != 0) {
res = i2c_write_unlocked(bus, address, write_data, write_size);
if (res != B_OK) {
release_sem(bus->sem);
return res;
}
}
if (read_data != 0 && read_size != 0) {
res = i2c_read_unlocked(bus, address, read_data, read_size);
if (res != B_OK) {
release_sem(bus->sem);
return res;
}
}
release_sem(bus->sem);
return B_OK;
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __I2C_CORE_H
#define __I2C_CORE_H
#include <SupportDefs.h>
typedef void (*i2c_set_scl)(void *cookie, int state);
typedef void (*i2c_set_sda)(void *cookie, int state);
typedef int (*i2c_get_scl)(void *cookie);
typedef int (*i2c_get_sda)(void *cookie);
struct _i2c_bus;
typedef struct _i2c_bus i2c_bus;
i2c_bus *i2c_create_bus(void *cookie,
int freqency,
bigtime_t timeout,
i2c_set_scl set_scl,
i2c_set_sda set_sda,
i2c_get_scl get_scl,
i2c_get_sda get_sda);
void i2c_delete_bus(i2c_bus *bus);
status_t i2c_read(i2c_bus *bus, int address, void *data, int size);
status_t i2c_write(i2c_bus *bus, int address, const void *data, int size);
status_t i2c_xfer(i2c_bus *bus, int address,
const void *write_data, int write_size,
void *read_data, int read_size);
#endif

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <KernelExport.h>
#include <Errors.h>
#include <OS.h>
#include <string.h>
#include "util.h"
#define TRACE_UTIL
#ifdef TRACE_UTIL
#define TRACE dprintf
#else
#define TRACE(a...)
#endif
area_id
map_mem(void **virt, void *phy, size_t size, uint32 protection, const char *name)
{
uint32 offset;
void *phyadr;
void *mapadr;
area_id area;
TRACE("mapping physical address %p with %ld bytes for %s\n", phy, size, name);
offset = (uint32)phy & (B_PAGE_SIZE - 1);
phyadr = (char *)phy - offset;
size = ROUNDUP(size + offset, B_PAGE_SIZE);
area = map_physical_memory(name, phyadr, size, B_ANY_KERNEL_BLOCK_ADDRESS, protection, &mapadr);
if (area < B_OK) {
TRACE("mapping '%s' failed, error 0x%lx (%s)\n", name, area, strerror(area));
return area;
}
*virt = (char *)mapadr + offset;
TRACE("physical = %p, virtual = %p, offset = %ld, phyadr = %p, mapadr = %p, size = %ld, area = 0x%08lx\n",
phy, *virt, offset, phyadr, mapadr, size, area);
return area;
}
area_id
alloc_mem(void **virt, void **phy, size_t size, uint32 protection, const char *name)
{
physical_entry pe;
void * virtadr;
area_id areaid;
status_t rv;
TRACE("allocating %ld bytes for %s\n", size, name);
size = ROUNDUP(size, B_PAGE_SIZE);
areaid = create_area(name, &virtadr, B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK | B_CONTIGUOUS, protection);
if (areaid < B_OK) {
TRACE("couldn't allocate area %s\n", name);
return B_ERROR;
}
rv = get_memory_map(virtadr, size, &pe, 1);
if (rv < B_OK) {
delete_area(areaid);
TRACE("couldn't map %s\n", name);
return B_ERROR;
}
if (virt)
*virt = virtadr;
if (phy)
*phy = pe.address;
TRACE("area = %ld, size = %ld, virt = %p, phy = %p\n", areaid, size, virtadr, pe.address);
return areaid;
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __UTIL_H
#define __UTIL_H
#include <OS.h>
area_id map_mem(void **virt, void *phy, size_t size, uint32 protection, const char *name);
area_id alloc_mem(void **virt, void **phy, size_t size, uint32 protection, const char *name);
// generic macro for rounding, can only be used for power of 2 blocksize
#define ROUNDUP(size, blocksize) (((size) + (blocksize) - 1) & ~((blocksize) - 1))
#define atomic_read(a) atomic_or(a, 0)
#endif