* 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 "config.h"
#include "driver.h"
#include "dvb_interface.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" },
{ }
};
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;
#if defined(DEBUG) && !defined(__HAIKU__)
set_dprintf_enabled(true);
load_driver_symbols("cx23882");
#endif
dprintf(INFO);
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;
}
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 enter\n");
interface_detach(((interface_cookie *)cookie)->cookie);
TRACE("cx23882: driver close leave\n");
return B_OK;
}
static status_t
driver_free(void* cookie)
{
TRACE("cx23882: driver free\n");
atomic_and(&sOpenMask, ~(1 << ((interface_cookie *)cookie)->dev_id));
free(cookie);
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;
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;
return B_ERROR;
}
static status_t
driver_control(void *cookie, uint32 op, void *arg, size_t len)
{
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;
}