1/*
2 * Copyright (C) 2007 JiSheng Zhang <jszhang3@gmail.com>. All rights reserved
3 * Distributed under the terms of the MIT license.
4 *
5 * Kernel driver for firewire
6 */
7
8#include <OS.h>
9#include <KernelExport.h>
10#include <SupportDefs.h>
11#include <PCI.h>
12
13#include <stdlib.h>
14#include <stdio.h>
15#include <string.h>
16#include <malloc.h>
17#include <dpc.h>
18
19#include "fwdebug.h"
20#include "queue.h"
21#include "fwglue.h"
22#include "firewire.h"
23#include "iec13213.h"
24#include "firewirereg.h"
25#include "fwdma.h"
26#include "fwohcireg.h"
27#include "fwohcivar.h"
28#include "firewire_module.h"
29
30status_t fwohci_pci_attach(int index);
31status_t fwohci_pci_detach(int index);
32pci_info *pciInfo[MAX_CARDS];
33fwohci_softc_t *gFwohci_softc[MAX_CARDS];
34struct firewire_softc *gFirewire_softc[MAX_CARDS];
35pci_module_info	*gPci;
36dpc_module_info *gDpc;
37
38struct supported_device{
39	uint16 vendor_id;
40	uint32 device_id;
41	const char *name;
42};
43
44struct supported_device supported_devices[] = {
45	{FW_VENDORID_NATSEMI, FW_DEVICE_CS4210, "National Semiconductor CS4210"},
46	{FW_VENDORID_NEC, FW_DEVICE_UPD861, "NEC uPD72861"},
47	{FW_VENDORID_NEC, FW_DEVICE_UPD871, "NEC uPD72871/2"},
48	{FW_VENDORID_NEC, FW_DEVICE_UPD72870, "NEC uPD72870"},
49	{FW_VENDORID_NEC, FW_DEVICE_UPD72873, "NEC uPD72873"},
50	{FW_VENDORID_NEC, FW_DEVICE_UPD72874, "NEC uPD72874"},
51	{FW_VENDORID_SIS, FW_DEVICE_7007, "SiS 7007"},
52	{FW_VENDORID_TI, FW_DEVICE_TITSB22, "Texas Instruments TSB12LV22"},
53	{FW_VENDORID_TI, FW_DEVICE_TITSB23, "Texas Instruments TSB12LV23"},
54	{FW_VENDORID_TI, FW_DEVICE_TITSB26, "Texas Instruments TSB12LV26"},
55	{FW_VENDORID_TI, FW_DEVICE_TITSB43, "Texas Instruments TSB43AA22"},
56	{FW_VENDORID_TI, FW_DEVICE_TITSB43A, "Texas Instruments TSB43AB22/A"},
57	{FW_VENDORID_TI, FW_DEVICE_TITSB43AB21, "Texas Instruments TSB43AB21/A/AI/A-EP"},
58	{FW_VENDORID_TI, FW_DEVICE_TITSB43AB23, "Texas Instruments TSB43AB23"},
59	{FW_VENDORID_TI, FW_DEVICE_TITSB82AA2, "Texas Instruments TSB82AA2"},
60	{FW_VENDORID_TI, FW_DEVICE_TIPCI4450, "Texas Instruments PCI4450"},
61	{FW_VENDORID_TI, FW_DEVICE_TIPCI4410A, "Texas Instruments PCI4410A"},
62	{FW_VENDORID_TI, FW_DEVICE_TIPCI4451, "Texas Instruments PCI4451"},
63	{FW_VENDORID_VIA, FW_DEVICE_VT6306, "VIA Fire II (VT6306)"},
64	{FW_VENDORID_RICOH, FW_DEVICE_R5C551, "Ricoh R5C551"},
65	{FW_VENDORID_RICOH, FW_DEVICE_R5C552, "Ricoh R5C552"},
66	{FW_VENDORID_APPLE, FW_DEVICE_PANGEA, "Apple Pangea"},
67	{FW_VENDORID_APPLE, FW_DEVICE_UNINORTH, "Apple UniNorth"},
68	{FW_VENDORID_LUCENT, FW_DEVICE_FW322, "Lucent FW322/323"},
69	{FW_VENDORID_INTEL, FW_DEVICE_82372FB, "Intel 82372FB"},
70	{FW_VENDORID_ADAPTEC, FW_DEVICE_AIC5800, "Adaptec AHA-894x/AIC-5800"},
71	{FW_VENDORID_SUN, FW_DEVICE_PCIO2FW, "Sun PCIO-2"},
72	{FW_VENDORID_SONY, FW_DEVICE_CXD3222, "Sony i.LINK (CXD3222)"},
73	{0, 0, NULL}
74};
75
76
77static int
78find_device_name(pci_info *info)
79{
80	struct supported_device *device;
81	for (device = supported_devices; device->name; device++) {
82		if (info->vendor_id == device->vendor_id
83			&& info->device_id == device->device_id >> 16) {
84			dprintf("%s\n", device->name);
85			return 1;
86		}
87	}
88	return 0;
89}
90
91
92#if 0
93static status_t
94fw_add_child(const char *childname,
95		const struct firewire_notify_hooks *hooks)
96{
97	status_t status;
98	int i;
99	TRACE("add child %s\n", childname);
100	for (i = 0; gFirewire_softc[i] != NULL; i++) {
101		status = firewire_add_child(gFirewire_softc[i], childname, hooks);
102		if (status != B_OK)
103			return status;
104	}
105
106	return B_OK;
107}
108
109
110static status_t
111fw_remove_child(const char *childname)
112{
113	status_t status;
114	int i;
115	TRACE("remove child %s\n", childname);
116	for (i = 0; gFirewire_softc[i] != NULL; i++) {
117		status = firewire_remove_child(gFirewire_softc[i], childname);
118		if (status != B_OK)
119			return status;
120	}
121
122	return B_OK;
123}
124#endif
125
126
127static int
128fw_get_handle(int socket, struct firewire_softc **handle)
129{
130	if (handle == NULL)
131		return B_BAD_VALUE;
132	if (socket >= 0 && socket < MAX_CARDS && gFirewire_softc[socket]) {
133		*handle = gFirewire_softc[socket];
134		return B_OK;
135	}
136	*handle = NULL;
137	return ENODEV;
138}
139
140
141static status_t
142fw_module_init(void)
143{
144	status_t status;
145	int i, found;
146	fwohci_softc_t *fwohci_sc;
147	struct firewire_softc *fw_sc;
148
149	pci_info *info = (pci_info*)malloc(sizeof(pci_info));
150	if (!info)
151		return B_NO_MEMORY;
152
153	if ((status = get_module(B_PCI_MODULE_NAME,(module_info **)&gPci)) != B_OK) {
154		TRACE("pci module unavailable\n");
155		free(info);
156		return status;
157	}
158
159	if ((status = get_module(B_DPC_MODULE_NAME,(module_info **)&gDpc)) != B_OK) {
160		TRACE("pci module unavailable\n");
161		free(info);
162		put_module(B_PCI_MODULE_NAME);
163		return status;
164	}
165
166	memset(gFwohci_softc, 0, sizeof(gFwohci_softc));
167
168	// find devices
169	for (i = 0, found = 0; (status = gPci->get_nth_pci_info(i, info)) == B_OK; i++) {
170		if (find_device_name(info)
171				|| ((info->class_base == PCI_serial_bus)
172					&& (info->class_sub == PCI_firewire)
173					&& (info->class_api == PCI_INTERFACE_OHCI))) {
174			dprintf( "vendor=%x, device=%x, revision = %x\n", info->vendor_id, info->device_id, info->revision);
175			pciInfo[found] = info;
176
177			fwohci_sc = (fwohci_softc_t*)malloc(sizeof(fwohci_softc_t));
178			if (!fwohci_sc) {
179				free(info);
180				goto err_outofmem;
181			}
182			memset(fwohci_sc, 0, sizeof(fwohci_softc_t));
183			gFwohci_softc[found] = fwohci_sc;
184
185			fw_sc = (firewire_softc*)malloc(sizeof(struct firewire_softc));
186			if (!fw_sc) {
187				free(info);
188				free(fwohci_sc);
189				goto err_outofmem;
190			}
191			memset(fw_sc, 0, sizeof(struct firewire_softc));
192			gFirewire_softc[found] = fw_sc;
193			if (found < MAX_CARDS - 1)
194				gFirewire_softc[found + 1] = NULL;
195
196			found++;
197			info = (pci_info*)malloc(sizeof(pci_info));
198			if (!info)
199				goto err_outofmem;
200
201			if (found == MAX_CARDS)
202				break;
203
204		}
205	}
206	TRACE("found %d cards\n", found);
207	free(info);
208
209	if ((status = initialize_timer()) != B_OK) {
210		ERROR("timer init failed\n");
211		goto err_timer;
212	}
213
214	for (i = 0; i < found; i++) {
215		if (fwohci_pci_attach(i) != B_OK) {
216			ERROR("fwohci_pci_attach failed\n");
217			goto err_pci;
218		}
219	}
220	return B_OK;
221
222err_pci:
223	terminate_timer();
224err_timer:
225err_outofmem:
226	for (i = 0; i < found; i++) {
227		free(gFirewire_softc[i]);
228		free(gFwohci_softc[i]);
229		free(pciInfo[i]);
230	}
231	put_module(B_PCI_MODULE_NAME);
232	put_module(B_DPC_MODULE_NAME);
233	return B_ERROR;
234
235}
236
237static status_t
238fw_module_uninit(void)
239{
240	int i;
241
242	terminate_timer();
243
244	for (i = 0; i < MAX_CARDS && gFirewire_softc[i] != NULL; i++) {
245		fwohci_pci_detach(i);
246		free(gFirewire_softc[i]);
247		free(gFwohci_softc[i]);
248		free(pciInfo[i]);
249	}
250
251	put_module(B_PCI_MODULE_NAME);
252	put_module(B_DPC_MODULE_NAME);
253
254	return B_OK;
255}
256
257
258static status_t
259fw_module_std_ops(int32 op, ...)
260{
261	switch (op) {
262		case B_MODULE_INIT:
263			TRACE("fw_module_init\n");
264			return fw_module_init();
265
266		case B_MODULE_UNINIT:
267			TRACE("fw_module_uninit\n");
268			return fw_module_uninit();
269	}
270	return B_BAD_VALUE;
271}
272
273static struct fw_module_info gModuleInfo = {
274	{
275		{
276			FIREWIRE_MODULE_NAME,
277			0,
278			fw_module_std_ops
279		},
280		NULL
281	},
282	fw_noderesolve_nodeid,
283	fw_noderesolve_eui64,
284	fw_asyreq,
285	fw_xferwake,
286	fw_xferwait,
287	fw_bindlookup,
288	fw_bindadd,
289	fw_bindremove,
290	fw_xferlist_add,
291	fw_xferlist_remove,
292	fw_xfer_alloc,
293	fw_xfer_alloc_buf,
294	fw_xfer_done,
295	fw_xfer_unload,
296	fw_xfer_free_buf,
297	fw_xfer_free,
298	fw_asy_callback_free,
299	fw_open_isodma,
300	fw_get_handle,
301	fwdma_malloc_multiseg,
302	fwdma_free_multiseg
303};
304
305module_info *modules[] = {
306	(module_info *)&gModuleInfo,
307	NULL
308};
309