1/*
2	Copyright 1999, Be Incorporated.   All Rights Reserved.
3	This file may be used under the terms of the Be Sample Code License.
4*/
5
6#include "cm_private.h"
7#include <string.h>
8
9#if !defined(_KERNEL_EXPORT_H)
10#include <KernelExport.h>
11#endif /* _KERNEL_EXPORT_H */
12
13
14static status_t mux_open(const char *name, uint32 flags, void **cookie);
15static status_t mux_close(void *cookie);
16static status_t mux_free(void *cookie);
17static status_t mux_control(void *cookie, uint32 op, void *data, size_t len);
18static status_t mux_read(void *cookie, off_t pos, void *data, size_t *len);
19static status_t mux_write(void *cookie, off_t pos, const void *data, size_t *len);
20
21device_hooks mux_hooks = {
22    &mux_open,
23    &mux_close,
24    &mux_free,
25    &mux_control,
26    &mux_read,
27    &mux_write,
28    NULL,		/* select */
29    NULL,		/* deselect */
30    NULL,		/* readv */
31    NULL		/* writev */
32};
33
34
35typedef struct {
36	int port_l;
37	int port_r;
38	int minval;
39	int maxval;
40	int leftshift;
41	int mask;
42	char name[B_OS_NAME_LENGTH];
43} mux_info;
44
45const mux_info the_muxes[] = {
46	{ 0, 1, 1, 7, 5, 0xe0, "Sampling input" },
47	{ 0, -1, 0, 1, 4, 0x10, "Mic +20dB selection" },
48	{ 0x2a, -1, 0, 1, 0, 0x01, "MIDI output to synth" },
49	{ 0x2a, -1, 0, 1, 1, 0x02, "MIDI input to synth" },
50	{ 0x2a, -1, 0, 1, 2, 0x04, "MIDI output to port" },
51};
52
53static const uchar unmap_input[] = {
54  0,
55  CMEDIA_PCI_INPUT_CD,
56  CMEDIA_PCI_INPUT_DAC,
57  CMEDIA_PCI_INPUT_AUX2,
58  CMEDIA_PCI_INPUT_LINE,
59  CMEDIA_PCI_INPUT_AUX1,
60  CMEDIA_PCI_INPUT_MIC,
61  CMEDIA_PCI_INPUT_MIX_OUT
62};
63
64static uchar
65map_input(uchar val) {
66  int i;
67  for (i = 0; i < 8; i++)
68	if (unmap_input[i] == val)
69	  return i;
70  return 0;
71}
72
73
74static status_t
75mux_open(
76	const char * name,
77	uint32 flags,
78	void ** cookie)
79{
80	int ix;
81	/* mux_dev * plex = NULL; */
82
83	ddprintf(("cmedia_pci: mux_open()\n"));
84
85	*cookie = NULL;
86	for (ix=0; ix<num_cards; ix++) {
87		if (!strcmp(name, cards[ix].mux.name)) {
88			break;
89		}
90	}
91	if (ix == num_cards) {
92		return ENODEV;
93	}
94
95	atomic_add(&cards[ix].mux.open_count, 1);
96	cards[ix].mux.card = &cards[ix];
97	*cookie = &cards[ix].mux;
98
99	return B_OK;
100}
101
102
103static status_t
104mux_close(
105	void * cookie)
106{
107	mux_dev * plex = (mux_dev *)cookie;
108
109	atomic_add(&plex->open_count, -1);
110
111	return B_OK;
112}
113
114
115static status_t
116mux_free(
117	void * cookie)
118{
119	ddprintf(("cmedia_pci: mux_free()\n"));
120
121	if (((mux_dev *)cookie)->open_count != 0) {
122		dprintf("cmedia_pci: mux open_count is bad in mux_free()!\n");
123	}
124	return B_OK;	/* already done in close */
125}
126
127
128static int
129get_mux_value(
130	cmedia_pci_dev * card,
131	int ix)
132{
133	uchar val;
134	if (ix < 0) {
135		return -1;
136	}
137	if (ix > 4) {
138		return -1;
139	}
140	val = get_indirect(card, the_muxes[ix].port_l);
141	val &= the_muxes[ix].mask;
142	val >>= the_muxes[ix].leftshift;
143	if (ix == CMEDIA_PCI_INPUT_MUX)
144	  return unmap_input[val];
145	return val;
146}
147
148
149static int
150gather_info(
151	mux_dev * mux,
152	cmedia_pci_routing * data,
153	int count)
154{
155	int ix;
156	cpu_status cp;
157
158	cp = disable_interrupts();
159	acquire_spinlock(&mux->card->hardware);
160
161	for (ix=0; ix<count; ix++) {
162		data[ix].value = get_mux_value(mux->card, data[ix].selector);
163		if (data[ix].value < 0) {
164			break;
165		}
166	}
167
168	release_spinlock(&mux->card->hardware);
169	restore_interrupts(cp);
170
171	return ix;
172}
173
174
175static status_t
176set_mux_value(
177	cmedia_pci_dev * card,
178	int selector,
179	int value)
180{
181	ddprintf(("set_mux_value(%d,%d)\n", selector, value));
182	if (selector < 0 || selector > 4) {
183		ddprintf(("selector EINVAL\n"));
184		return EINVAL;
185	}
186	if (selector == CMEDIA_PCI_INPUT_MUX)
187	    value = map_input(value);
188	if (value < the_muxes[selector].minval ||
189		value > the_muxes[selector].maxval) {
190		ddprintf(("value EINVAL\n"));
191		return EINVAL;
192	}
193	set_indirect(card, the_muxes[selector].port_l,
194		(value << the_muxes[selector].leftshift),
195		the_muxes[selector].mask);
196	if (the_muxes[selector].port_r > -1) {
197		set_indirect(card, the_muxes[selector].port_r,
198			(value << the_muxes[selector].leftshift),
199			the_muxes[selector].mask);
200	}
201	return B_OK;
202}
203
204
205static int
206disperse_info(
207	mux_dev * mux,
208	cmedia_pci_routing * data,
209	int count)
210{
211	int ix;
212	cpu_status cp;
213
214	cp = disable_interrupts();
215	acquire_spinlock(&mux->card->hardware);
216
217	for (ix=0; ix<count; ix++) {
218		if (set_mux_value(mux->card, data[ix].selector, data[ix].value) < B_OK) {
219			break;
220		}
221	}
222
223	release_spinlock(&mux->card->hardware);
224	restore_interrupts(cp);
225
226	return ix;
227}
228
229
230static status_t
231mux_control(
232	void * cookie,
233	uint32 iop,
234	void * data,
235	size_t len)
236{
237	mux_dev * plex = (mux_dev *)cookie;
238	status_t err = B_OK;
239
240	ddprintf(("cmedia_pci: mux_control()\n")); /* slow printing */
241
242	switch (iop) {
243	case B_ROUTING_GET_VALUES:
244		((cmedia_pci_routing_cmd *)data)->count =
245			gather_info(plex, ((cmedia_pci_routing_cmd *)data)->data,
246				((cmedia_pci_routing_cmd *)data)->count);
247		break;
248	case B_ROUTING_SET_VALUES:
249		((cmedia_pci_routing_cmd *)data)->count =
250			disperse_info(plex, ((cmedia_pci_routing_cmd *)data)->data,
251				((cmedia_pci_routing_cmd *)data)->count);
252		break;
253	default:
254		err = B_BAD_VALUE;
255		break;
256	}
257	return err;
258}
259
260
261static status_t
262mux_read(
263	void * cookie,
264	off_t pos,
265	void * data,
266	size_t * nread)
267{
268	return EPERM;
269}
270
271
272static status_t
273mux_write(
274	void * cookie,
275	off_t pos,
276	const void * data,
277	size_t * nwritten)
278{
279	return EPERM;
280}
281
282