aboutsummaryrefslogtreecommitdiff
path: root/sys/i386/isa/sound/os.h
blob: 5b233b5f943a033c39eeb215175741472905196e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
#ifndef _OS_H_
#define _OS_H_
/*
 * OS specific settings for FreeBSD
 *
 * This chould be used as an example when porting the driver to a new
 * operating systems.
 *
 * What you should do is to rewrite the soundcard.c and os.h (this file).
 * You should create a new subdirectory and put these two files there.
 * In addition you have to do a makefile.<OS>.
 *
 * If you have to make changes to other than these two files, please contact me
 * before making the changes. It's possible that I have already made the
 * change. 
 */

/*
 * Insert here the includes required by your kernel.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/filio.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <i386/isa/isa_device.h>
#include <machine/cpufunc.h>
#include <sys/signalvar.h>

#if NSND > 0
#define CONFIGURE_SOUNDCARD
#else
#undef CONFIGURED_SOUNDCARD
#endif


/*
 * Rest of the file is compiled only if the driver is really required.
 */
#ifdef CONFIGURE_SOUNDCARD

/* 
 * select() is currently implemented in Linux specific way. Don't enable.
 * I don't remember what the SHORT_BANNERS means so forget it.
 */

/*#undef ALLOW_SELECT*/
#define SHORT_BANNERS

/* The soundcard.h could be in a nonstandard place so inclyde it here. */
#include <machine/soundcard.h>

/*
 * Here is the first portability problem. Every OS has it's own way to
 * pass a pointer to the buffer in read() and write() calls. In Linux it's
 * just a char*. In BSD it's struct uio. This parameter is passed to
 * all functions called from read() or write(). Since nothing can be 
 * assumed about this structure, the driver uses set of macros for
 * accessing the user buffer. 
 *
 * The driver reads/writes bytes in the user buffer sequentially which
 * means that calls like uiomove() can be used.
 *
 * snd_rw_buf is the type which is passed to the device file specific
 * read() and write() calls.
 * 
 * The following macros are used to move date to and from the
 * user buffer. These macros should be used only when the 
 * target or source parameter has snd_rw_buf type.
 * The offs parameter is a offset relative to the beginning of
 * the user buffer. In Linux the offset is required but for example
 * BSD passes the offset info in the uio structure. It could be usefull
 * if these macros verify that the offs parameter and the value in
 * the snd_rw_buf structure are equal.
 */
typedef struct uio snd_rw_buf;

/*
 * Move bytes from the buffer which the application given in a
 * write() call.
 * offs is position relative to the beginning of the buffer in
 * user space. The count is number of bytes to be moved.
 */
#define COPY_FROM_USER(target, source, offs, count) \
	do { if (uiomove((caddr_t ) target, count, (struct uio *)source)) { \
		printf ("sb: Bad copyin()!\n"); \
	} } while(0)

/* Like COPY_FOM_USER but for writes. */

#define COPY_TO_USER(target, offs, source, count) \
	do { if (uiomove(source, count, (struct uio *)target)) { \
		printf ("sb: Bad copyout()!\n"); \
	} } while(0)
/* 
 * The following macros are like COPY_*_USER but work just with one byte (8bit),
 * short (16 bit) or long (32 bit) at a time.
 * The same restrictions apply than for COPY_*_USER
 */
#define GET_BYTE_FROM_USER(target, addr, offs)	{uiomove((char*)&(target), 1, (struct uio *)addr);}
#define GET_SHORT_FROM_USER(target, addr, offs)	{uiomove((char*)&(target), 2, (struct uio *)addr);}
#define GET_WORD_FROM_USER(target, addr, offs)	{uiomove((char*)&(target), 4, (struct uio *)addr);}
#define PUT_WORD_TO_USER(addr, offs, data)	{uiomove((char*)&(data), 4, (struct uio *)addr);}


#define EREMOTEIO -1

/*
 * The way how the ioctl arguments are passed is another nonportable thing.
 * In Linux the argument is just a pointer directly to the user segment. On
 * FreeBSD the data is already moved to the kernel space. The following
 * macros should handle the difference.
 */

/*
 * IOCTL_FROM_USER is used to copy a record pointed by the argument to
 * a buffer in the kernel space. On FreeBSD it can be done just by calling
 * memcpy. With Linux a memcpy_from_fs should be called instead.
 * Parameters of the following macros are like in the COPY_*_USER macros.
 */

/*
 * When the ioctl argument points to a record or array (longer than 32 bits),
 * the macros IOCTL_*_USER are used. It's assumed that the source and target
 * parameters are direct memory addresses.
 */
#define IOCTL_FROM_USER(target, source, offs, count) {memcpy(target, &((source)[offs]), count);}
#define IOCTL_TO_USER(target, offs, source, count) {memcpy(&((target)[offs]), source, count);}
/* The following macros are used if the ioctl argument points to 32 bit int */
#define IOCTL_IN(arg)			(*(int*)arg)
#define IOCTL_OUT(arg, ret)		*(int*)arg = ret

/*
 * When the driver displays something to the console, printk() will be called.
 * The name can be changed here.
 */
#define printk 		printf

/*
 * The following macros define an interface to the process management.
 */

struct snd_wait {
	int mode;
	int aborting;
};

/*
 * DEFINE_WAIT_QUEUE is used where a wait queue is required. It must define
 * a structure which can be passed as a parameter to a sleep(). The second
 * parameter is name of a flag variable (must be defined as int).
 */
#define DEFINE_WAIT_QUEUE(qname, flag) static int *qname = NULL; \
	static volatile struct snd_wait flag = {0}
/* Like the above but defines an array of wait queues and flags */
#define DEFINE_WAIT_QUEUES(qname, flag) static int *qname = {NULL}; \
	static volatile struct snd_wait flag = {{0}}

#define RESET_WAIT_QUEUE(q, f) {f.aborting = 0;f.mode = WK_NONE;}
#define SET_ABORT_FLAG(q, f) f.aborting = 1
#define TIMED_OUT(q, f) (f.mode & WK_TIMEOUT)
#define SOMEONE_WAITING(q, f) (f.mode & WK_SLEEP)
/*
 * This driver handles interrupts little bit nonstandard way. The following
 * macro is used to test if the current process has received a signal which
 * is aborts the process. This macro is called from close() to see if the
 * buffers should be discarded. If this kind info is not available, a constant
 * 1 or 0 could be returned (1 should be better than 0).
 */
#define PROCESS_ABORTING(q, f) (f.aborting || CURSIG(curproc))

/*
 * The following macro calls tsleep. It should be implemented such that
 * the process is resumed if it receives a signal.
 * The q parameter is a wait_queue defined with DEFINE_WAIT_QUEUE(),
 * and the second is a workarea parameter. The third is a timeout 
 * in ticks. Zero means no timeout.
 */
#define DO_SLEEP(q, f, time_limit)	\
	{ \
	  int flag; \
	  f.mode = WK_SLEEP; \
	  flag=tsleep(&q, (PRIBIO-5)|PCATCH, "sndint", time_limit); \
	  f.mode &= ~WK_SLEEP; \
	  if (flag == EWOULDBLOCK) { \
		f.mode |= WK_TIMEOUT; \
		f.aborting = 0; \
	  } else \
		f.aborting = flag; \
	}
/* An the following wakes up a process */
#define WAKE_UP(q, f)   wakeup(&q)

/*
 * Timing macros. This driver assumes that there is a timer running in the
 * kernel. The timer should return a value which is increased once at every
 * timer tick. The macro HZ should return the number of such ticks/sec.
 */

#ifndef HZ
#define HZ	hz
#endif

/* 
 * GET_TIME() returns current value of the counter incremented at timer
 * ticks.  This can overflow, so the timeout might be real big...
 * 
 */

extern unsigned long get_time(void);
#define GET_TIME() get_time()
/*#define GET_TIME()	(lbolt)	*/ /* Returns current time (1/HZ secs since boot) */

/*
 * The following three macros are called before and after atomic
 * code sequences. The flags parameter has always type of unsigned long.
 * The macro DISABLE_INTR() should ensure that all interrupts which
 * may invoke any part of the driver (timer, soundcard interrupts) are
 * disabled.
 * RESTORE_INTR() should return the interrupt status back to the
 * state when DISABLE_INTR() was called. The flags parameter is
 * a variable which can carry 32 bits of state information between
 * DISABLE_INTR() and RESTORE_INTR() calls.
 */
#define DISABLE_INTR(flags)	flags = splhigh()
#define RESTORE_INTR(flags)	splx(flags)

/*
 * INB() and OUTB() should be obvious. NOTE! The order of
 * paratemeters of OUTB() is different than on some other
 * operating systems.
 */

#define INB			inb
#define INW			inb

#if 0
/*  
 * The outb(0, 0x80) is just for slowdown. It's bit unsafe since
 * this address could be used for something usefull.
 */
#ifdef PC98
#define OUTB(addr, data)	{outb(data, addr);outb(0x5f, 0);}
#define OUTW(addr, data)	{outw(data, addr);outb(0x5f, 0);}
#else /* IBM-PC */
#define OUTB(addr, data)	{outb(data, addr);outb(0x80, 0);}
#define OUTW(addr, data)	{outw(data, addr);outb(0x80, 0);}
#endif /* PC98 */
#else
#define OUTB(addr, data)	outb(data, addr)
#define OUTW(addr, data)	outw(data, addr)
#endif

/* memcpy() was not defined on FreeBSD. Lets define it here */
#define memcpy(d, s, c)		bcopy(s, d, c)

/*
 * When a error (such as EINVAL) is returned by a function,
 * the following macro is used. The driver assumes that a
 * error is signalled by returning a negative value.
 */

#define RET_ERROR(err)		-(err)

/* 
   KERNEL_MALLOC() allocates requested number of memory  and 
   KERNEL_FREE is used to free it. 
   These macros are never called from interrupt, in addition the
   nbytes will never be more than 4096 bytes. Generally the driver
   will allocate memory in blocks of 4k. If the kernel has just a
   page level memory allocation, 4K can be safely used as the size
   (the nbytes parameter can be ignored).
*/
#define	KERNEL_MALLOC(nbytes)	malloc(nbytes, M_TEMP, M_WAITOK)
#define	KERNEL_FREE(addr)	free(addr, M_TEMP)

/*
 * The macro PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr)
 * returns size bytes of
 * (kernel virtual) memory which will never get freed by the driver.
 * This macro is called only during boot. The linux_ptr is a linux specific
 * parameter which should be ignored in other operating systems.
 * The mem_ptr is a pointer variable where the macro assigns pointer to the
 * memory area. The type is the type of the mem_ptr.
 */
#define PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr) \
  {mem_ptr = (typecast)malloc(size, M_DEVBUF, M_NOWAIT); \
   if (!mem_ptr)panic("SOUND: Cannot allocate memory\n");}

/*
 * The macro DEFINE_TIMER defines variables for the ACTIVATE_TIMER if
 * required. The name is the variable/name to be used and the proc is
 * the procedure to be called when the timer expires.
 */

#define DEFINE_TIMER(name, proc)

/*
 * The ACTIVATE_TIMER requests system to call 'proc' after 'time' ticks.
 */

#define ACTIVATE_TIMER(name, proc, time) \
	timeout((timeout_func_t)proc, 0, time);
/*
 * The rest of this file is not complete yet. The functions using these
 * macros will not work
 */
#define ALLOC_DMA_CHN(chn,deviceID) (isa_dma_acquire(chn))
#define RELEASE_DMA_CHN(chn) (isa_dma_release(chn))
#define DMA_MODE_READ		0
#define DMA_MODE_WRITE		1
#define RELEASE_IRQ(irq_no)

/*
 * The macro DECLARE_FILE() adds an entry to struct fileinfo referencing the
 * connected filestructure.
 * This entry must be initialized in sound_open() in soundcard.c
 *
 * ISSET_FILE_FLAG() allows checking of flags like O_NONBLOCK on files
 *
 */

#define DECLARE_FILE()                    struct file *filp 
#ifdef notdef
#define ISSET_FILE_FLAG(fileinfo, flag)   (fileinfo->filp->f_flag & (flag) ? \
					  1 : 0) 
#else
#define ISSET_FILE_FLAG(fileinfo, flag)   0
#endif
#define INT_HANDLER_PROTO() void(*hndlr)(int)
#define INT_HANDLER_PARMS(irq, parms) int irq
#define INT_HANDLER_CALL(irq) irq

/*
 * For select call...
 */
#ifdef ALLOW_SELECT
typedef struct proc select_table;
#define	SEL_IN FREAD
#define SEL_OUT FWRITE
#define SEL_EX 0
extern struct selinfo selinfo[];
#endif 
#endif
#endif