1/*
2 * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify,
8 * merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include <KernelExport.h>
26#include <Drivers.h>
27#include <Errors.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <string.h>
31
32#include "config.h"
33#include "driver.h"
34#include "dvb_interface.h"
35
36#define TRACE_DRIVER
37#ifdef TRACE_DRIVER
38  #define TRACE dprintf
39#else
40  #define TRACE(a...)
41#endif
42
43typedef struct
44{
45	int			vendor;
46	int			device;
47	int			subvendor;
48	int			subdevice;
49	const char *name;
50} card_info;
51
52int32 				api_version = B_CUR_DRIVER_API_VERSION;
53pci_module_info *	gPci;
54static char *		sDevNameList[MAX_CARDS + 1];
55static pci_info *	sDevList[MAX_CARDS];
56static int32		sOpenMask;
57
58static card_info sCardTable[] =
59{
60	{ 0x14f1, 0x8802, 0x0070, 0x9002, "Hauppauge WinTV-NOVA-T model 928" },
61	{ /* end */ }
62};
63
64
65typedef struct
66{
67	void *	cookie;
68	int		dev_id;
69} interface_cookie;
70
71
72static const char *
73identify_device(const card_info *cards, const pci_info *info)
74{
75	for (; cards->name; cards++) {
76		if (cards->vendor >= 0 && cards->vendor != info->vendor_id)
77			continue;
78		if (cards->device >= 0 && cards->device != info->device_id)
79			continue;
80		if ((info->header_type & PCI_header_type_mask) != PCI_header_type_generic)
81			continue;
82		if (cards->subvendor >= 0 && cards->subvendor != info->u.h0.subsystem_vendor_id)
83			continue;
84		if (cards->subdevice >= 0 && cards->subdevice != info->u.h0.subsystem_id)
85			continue;
86		return cards->name;
87	}
88	return NULL;
89}
90
91
92status_t
93init_hardware(void)
94{
95	pci_info info;
96	status_t res;
97	int i;
98
99	TRACE("cx23882: init_hardware()\n");
100
101	if (get_module(B_PCI_MODULE_NAME, (module_info **)&gPci) < B_OK)
102		return B_ERROR;
103	for (res = B_ERROR, i = 0; gPci->get_nth_pci_info(i, &info) == B_OK; i++) {
104		if (identify_device(sCardTable, &info)) {
105			res = B_OK;
106			break;
107		}
108	}
109	put_module(B_PCI_MODULE_NAME);
110	gPci = NULL;
111
112	return res;
113}
114
115
116status_t
117init_driver(void)
118{
119	struct pci_info *item;
120	int index;
121	int cards;
122
123#if defined(DEBUG) && !defined(__HAIKU__)
124	set_dprintf_enabled(true);
125	load_driver_symbols("cx23882");
126#endif
127
128	dprintf(INFO);
129
130	item = (pci_info *)malloc(sizeof(pci_info));
131	if (!item)
132		return B_NO_MEMORY;
133
134	if (get_module(B_PCI_MODULE_NAME, (module_info **)&gPci) < B_OK) {
135		free(item);
136		return B_ERROR;
137	}
138
139	for (cards = 0, index = 0; gPci->get_nth_pci_info(index++, item) == B_OK; ) {
140		const char *info = identify_device(sCardTable, item);
141		if (info) {
142			char name[64];
143			sprintf(name, "dvb/cx23882/%d", cards + 1);
144			dprintf("cx23882: /dev/%s is a %s\n", name, info);
145			sDevList[cards] = item;
146			sDevNameList[cards] = strdup(name);
147			sDevNameList[cards + 1] = NULL;
148			cards++;
149			item = (pci_info *)malloc(sizeof(pci_info));
150			if (!item)
151				goto err_outofmem;
152			if (cards == MAX_CARDS)
153				break;
154		}
155	}
156
157	free(item);
158
159	if (!cards)
160		goto err_cards;
161
162	return B_OK;
163
164err_outofmem:
165	TRACE("cx23882: err_outofmem\n");
166	for (index = 0; index < cards; index++) {
167		free(sDevList[index]);
168		free(sDevNameList[index]);
169	}
170err_cards:
171	put_module(B_PCI_MODULE_NAME);
172	return B_ERROR;
173}
174
175
176void
177uninit_driver(void)
178{
179	int32 i;
180
181	TRACE("cx23882: uninit_driver\n");
182
183	for (i = 0; sDevNameList[i] != NULL; i++) {
184		free(sDevList[i]);
185		free(sDevNameList[i]);
186	}
187
188	put_module(B_PCI_MODULE_NAME);
189}
190
191
192static status_t
193driver_open(const char *name, uint32 flags, void** _cookie)
194{
195	interface_cookie *cookie;
196	char *deviceName;
197	status_t status;
198	int dev_id;
199	int mask;
200
201	TRACE("cx23882: driver open\n");
202
203	for (dev_id = 0; (deviceName = sDevNameList[dev_id]) != NULL; dev_id++) {
204		if (!strcmp(name, deviceName))
205			break;
206	}
207	if (deviceName == NULL) {
208		TRACE("cx23882: invalid device name\n");
209		return B_ERROR;
210	}
211
212	// allow only one concurrent access
213	mask = 1 << dev_id;
214	if (atomic_or(&sOpenMask, mask) & mask)
215		return B_BUSY;
216
217	cookie = (interface_cookie *)malloc(sizeof(interface_cookie));
218	if (!cookie)
219		return B_NO_MEMORY;
220
221	cookie->dev_id = dev_id;
222	status = interface_attach(&cookie->cookie, sDevList[dev_id]);
223	if (status != B_OK) {
224		free(cookie);
225		atomic_and(&sOpenMask, ~(1 << dev_id));
226		return status;
227	}
228
229	*_cookie = cookie;
230	return B_OK;
231}
232
233
234static status_t
235driver_close(void* cookie)
236{
237	TRACE("cx23882: driver close enter\n");
238	interface_detach(((interface_cookie *)cookie)->cookie);
239	TRACE("cx23882: driver close leave\n");
240	return B_OK;
241}
242
243
244static status_t
245driver_free(void* cookie)
246{
247	TRACE("cx23882: driver free\n");
248	atomic_and(&sOpenMask, ~(1 << ((interface_cookie *)cookie)->dev_id));
249	free(cookie);
250	return B_OK;
251}
252
253
254static status_t
255driver_read(void* cookie, off_t position, void *buf, size_t* num_bytes)
256{
257	TRACE("cx23882: driver read\n");
258	*num_bytes = 0; // required by design for read hook!
259	return B_ERROR;
260}
261
262
263static status_t
264driver_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
265{
266	TRACE("cx23882: driver write\n");
267	*num_bytes = 0; // not sure if required for write hook
268	return B_ERROR;
269}
270
271
272static status_t
273driver_control(void *cookie, uint32 op, void *arg, size_t len)
274{
275//	TRACE("cx23882: driver control\n");
276	return interface_ioctl(((interface_cookie *)cookie)->cookie, op, arg, len);
277}
278
279
280static device_hooks
281sDeviceHooks = {
282	driver_open,
283	driver_close,
284	driver_free,
285	driver_control,
286	driver_read,
287	driver_write,
288};
289
290
291const char**
292publish_devices(void)
293{
294	return (const char**)sDevNameList;
295}
296
297
298device_hooks*
299find_device(const char* name)
300{
301	return &sDeviceHooks;
302}
303