xref: /minix/minix/lib/libaudiodriver/audio_fw.c (revision fb9c64b2)
1 /* Best viewed with tabsize 4
2  *
3  * This file contains a standard driver for audio devices.
4  * It supports double dma buffering and can be configured to use
5  * extra buffer space beside the dma buffer.
6  * This driver also support sub devices, which can be independently
7  * opened and closed.
8  *
9  * The file contains one entry point:
10  *
11  *   main:	main entry when driver is brought up
12  *
13  *	October 2007	Updated audio framework to work with mplayer, added
14  *					savecopies (Pieter Hijma)
15  *  February 2006   Updated audio framework,
16  *		changed driver-framework relation (Peter Boonstoppel)
17  *  November 2005   Created generic DMA driver framework (Laurens Bronwasser)
18  *  August 24 2005  Ported audio driver to user space
19  *		(only audio playback) (Peter Boonstoppel)
20  *  May 20 1995	    SB16 Driver: Michel R. Prevenier
21  */
22 
23 
24 #include <minix/audio_fw.h>
25 #include <minix/endpoint.h>
26 #include <minix/ds.h>
27 #include <sys/ioccom.h>
28 
29 
30 static int msg_open(devminor_t minor_dev_nr, int access,
31 	endpoint_t user_endpt);
32 static int msg_close(int minor_dev_nr);
33 static ssize_t msg_read(devminor_t minor, u64_t position, endpoint_t endpt,
34 	cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
35 static ssize_t msg_write(devminor_t minor, u64_t position, endpoint_t endpt,
36 	cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
37 static int msg_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
38 	cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id);
39 static void msg_hardware(unsigned int mask);
40 static int open_sub_dev(int sub_dev_nr, int operation);
41 static int close_sub_dev(int sub_dev_nr);
42 static void handle_int_write(int sub_dev_nr);
43 static void handle_int_read(int sub_dev_nr);
44 static void data_to_user(sub_dev_t *sub_dev_ptr);
45 static void data_from_user(sub_dev_t *sub_dev_ptr);
46 static int init_buffers(sub_dev_t *sub_dev_ptr);
47 static int get_started(sub_dev_t *sub_dev_ptr);
48 static int io_ctl_length(int io_request);
49 static special_file_t* get_special_file(int minor_dev_nr);
50 #if defined(__i386__)
51 static void tell_dev(vir_bytes buf, size_t size, int pci_bus,
52 	int pci_dev, int pci_func);
53 #endif
54 
55 static char io_ctl_buf[IOCPARM_MASK];
56 static int irq_hook_id = 0;	/* id of irq hook at the kernel */
57 static int irq_hook_set = FALSE;
58 
59 /* SEF functions and variables. */
60 static void sef_local_startup(void);
61 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
62 static void sef_cb_signal_handler(int signo);
63 
64 static struct chardriver audio_tab = {
65 	.cdr_open	= msg_open,	/* open the special file */
66 	.cdr_close	= msg_close,	/* close the special file */
67 	.cdr_read	= msg_read,
68 	.cdr_write	= msg_write,
69 	.cdr_ioctl	= msg_ioctl,
70 	.cdr_intr	= msg_hardware
71 };
72 
73 int main(void)
74 {
75 
76 	/* SEF local startup. */
77 	sef_local_startup();
78 
79 	/* Here is the main loop of the dma driver.  It waits for a message,
80 	   carries it out, and sends a reply. */
81 	chardriver_task(&audio_tab);
82 
83 	return 0;
84 }
85 
86 /*===========================================================================*
87  *			       sef_local_startup			     *
88  *===========================================================================*/
89 static void sef_local_startup(void)
90 {
91   /* Register init callbacks. */
92   sef_setcb_init_fresh(sef_cb_init_fresh);
93   sef_setcb_init_restart(sef_cb_init_fresh);
94 
95   /* Register live update callbacks. */
96   sef_setcb_lu_prepare(sef_cb_lu_prepare);
97   sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid);
98   sef_setcb_lu_state_dump(sef_cb_lu_state_dump);
99 
100   /* Register signal callbacks. */
101   sef_setcb_signal_handler(sef_cb_signal_handler);
102 
103   /* Let SEF perform startup. */
104   sef_startup();
105 }
106 
107 /*===========================================================================*
108  *		            sef_cb_init_fresh                                *
109  *===========================================================================*/
110 static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
111 {
112 /* Initialize the audio driver framework. */
113 	int i; char irq;
114 	static int executed = 0;
115 	sub_dev_t* sub_dev_ptr;
116 
117 	/* initialize basic driver variables */
118 	if (drv_init() != OK) {
119 		printf("libaudiodriver: Could not initialize driver\n");
120 		return EIO;
121 	}
122 
123 	/* init variables, get dma buffers */
124 	for (i = 0; i < drv.NrOfSubDevices; i++) {
125 
126 		sub_dev_ptr = &sub_dev[i];
127 
128 		sub_dev_ptr->Opened = FALSE;
129 		sub_dev_ptr->DmaBusy = FALSE;
130 		sub_dev_ptr->DmaMode = NO_DMA;
131 		sub_dev_ptr->DmaReadNext = 0;
132 		sub_dev_ptr->DmaFillNext = 0;
133 		sub_dev_ptr->DmaLength = 0;
134 		sub_dev_ptr->BufReadNext = 0;
135 		sub_dev_ptr->BufFillNext = 0;
136 		sub_dev_ptr->RevivePending = FALSE;
137 		sub_dev_ptr->OutOfData = FALSE;
138 		sub_dev_ptr->Nr = i;
139 	}
140 
141 	/* initialize hardware*/
142 	if (drv_init_hw() != OK) {
143 		printf("%s: Could not initialize hardware\n", drv.DriverName);
144 		return EIO;
145 	}
146 
147 	/* get irq from device driver...*/
148 	if (drv_get_irq(&irq) != OK) {
149 		printf("%s: init driver couldn't get IRQ", drv.DriverName);
150 		return EIO;
151 	}
152 	/* TODO: execute the rest of this function only once
153 	   we don't want to set irq policy twice */
154 	if (executed) return OK;
155 	executed = TRUE;
156 
157 	/* ...and register interrupt vector */
158 	if ((i=sys_irqsetpolicy(irq, 0, &irq_hook_id )) != OK){
159 		printf("%s: init driver couldn't set IRQ policy: %d", drv.DriverName, i);
160 		return EIO;
161 	}
162 	irq_hook_set = TRUE; /* now signal handler knows it must unregister policy*/
163 
164 	/* Announce we are up! */
165 	chardriver_announce();
166 
167 	return OK;
168 }
169 
170 /*===========================================================================*
171  *		           sef_cb_signal_handler                             *
172  *===========================================================================*/
173 static void sef_cb_signal_handler(int signo)
174 {
175 	int i;
176 	char irq;
177 
178 	/* Only check for termination signal, ignore anything else. */
179 	if (signo != SIGTERM) return;
180 
181 	for (i = 0; i < drv.NrOfSubDevices; i++) {
182 		drv_stop(i); /* stop all sub devices */
183 	}
184 	if (irq_hook_set) {
185 		if (sys_irqdisable(&irq_hook_id) != OK) {
186 			printf("Could not disable IRQ\n");
187 		}
188 		/* get irq from device driver*/
189 		if (drv_get_irq(&irq) != OK) {
190 			printf("Msg SIG_STOP Couldn't get IRQ");
191 		}
192 		/* remove the policy */
193 		if (sys_irqrmpolicy(&irq_hook_id) != OK) {
194 			printf("%s: Could not disable IRQ\n",drv.DriverName);
195 		}
196 	}
197 }
198 
199 static int msg_open(devminor_t minor_dev_nr, int UNUSED(access),
200 	endpoint_t UNUSED(user_endpt))
201 {
202 	int r, read_chan, write_chan, io_ctl;
203 	special_file_t* special_file_ptr;
204 
205 	special_file_ptr = get_special_file(minor_dev_nr);
206 	if(special_file_ptr == NULL) {
207 		return EIO;
208 	}
209 
210 	read_chan = special_file_ptr->read_chan;
211 	write_chan = special_file_ptr->write_chan;
212 	io_ctl = special_file_ptr->io_ctl;
213 
214 	if (read_chan==NO_CHANNEL && write_chan==NO_CHANNEL && io_ctl==NO_CHANNEL) {
215 		printf("%s: No channel specified for minor device %d!\n",
216 				drv.DriverName, minor_dev_nr);
217 		return EIO;
218 	}
219 	if (read_chan == write_chan && read_chan != NO_CHANNEL) {
220 		printf("%s: Read and write channels are equal: %d!\n",
221 				drv.DriverName, minor_dev_nr);
222 		return EIO;
223 	}
224 	/* open the sub devices specified in the interface header file */
225 	if (write_chan != NO_CHANNEL) {
226 		/* open sub device for writing */
227 		if (open_sub_dev(write_chan, WRITE_DMA) != OK) return EIO;
228 	}
229 	if (read_chan != NO_CHANNEL) {
230 		if (open_sub_dev(read_chan, READ_DMA) != OK) return EIO;
231 	}
232 	if (read_chan == io_ctl || write_chan == io_ctl) {
233 		/* io_ctl is already opened because it's the same as read or write */
234 		return OK; /* we're done */
235 	}
236 	if (io_ctl != NO_CHANNEL) { /* Ioctl differs from read/write channels, */
237 		r = open_sub_dev(io_ctl, NO_DMA); /* open it explicitly */
238 		if (r != OK) return EIO;
239 	}
240 	return OK;
241 }
242 
243 
244 static int open_sub_dev(int sub_dev_nr, int dma_mode) {
245 	sub_dev_t* sub_dev_ptr;
246 	sub_dev_ptr = &sub_dev[sub_dev_nr];
247 
248 	/* Only one open at a time per sub device */
249 	if (sub_dev_ptr->Opened) {
250 		printf("%s: Sub device %d is already opened\n",
251 				drv.DriverName, sub_dev_nr);
252 		return EBUSY;
253 	}
254 	if (sub_dev_ptr->DmaBusy) {
255 		printf("%s: Sub device %d is still busy\n", drv.DriverName, sub_dev_nr);
256 		return EBUSY;
257 	}
258 	/* Setup variables */
259 	sub_dev_ptr->Opened = TRUE;
260 	sub_dev_ptr->DmaReadNext = 0;
261 	sub_dev_ptr->DmaFillNext = 0;
262 	sub_dev_ptr->DmaLength = 0;
263 	sub_dev_ptr->DmaMode = dma_mode;
264 	sub_dev_ptr->BufReadNext = 0;
265 	sub_dev_ptr->BufFillNext = 0;
266 	sub_dev_ptr->BufLength = 0;
267 	sub_dev_ptr->RevivePending = FALSE;
268 	sub_dev_ptr->OutOfData = TRUE;
269 
270 	/* arrange DMA */
271 	if (dma_mode != NO_DMA) { /* sub device uses DMA */
272 		/* allocate dma buffer and extra buffer space
273 		   and configure sub device for dma */
274 		if (init_buffers(sub_dev_ptr) != OK ) return EIO;
275 	}
276 	return OK;
277 }
278 
279 
280 static int msg_close(devminor_t minor_dev_nr)
281 {
282 
283 	int r, read_chan, write_chan, io_ctl;
284 	special_file_t* special_file_ptr;
285 
286 	special_file_ptr = get_special_file(minor_dev_nr);
287 	if(special_file_ptr == NULL) {
288 		return EIO;
289 	}
290 
291 	read_chan = special_file_ptr->read_chan;
292 	write_chan = special_file_ptr->write_chan;
293 	io_ctl = special_file_ptr->io_ctl;
294 
295 	r= OK;
296 
297 	/* close all sub devices */
298 	if (write_chan != NO_CHANNEL) {
299 		if (close_sub_dev(write_chan) != OK) r = EIO;
300 	}
301 	if (read_chan != NO_CHANNEL) {
302 		if (close_sub_dev(read_chan) != OK) r = EIO;
303 	}
304 	if (read_chan == io_ctl || write_chan == io_ctl) {
305 		/* io_ctl is already closed because it's the same as read or write */
306 		return r; /* we're done */
307 	}
308 	/* ioctl differs from read/write channels... */
309 	if (io_ctl != NO_CHANNEL) {
310 		if (close_sub_dev(io_ctl) != OK) r = EIO; /* ...close it explicitly */
311 	}
312 	return r;
313 }
314 
315 
316 static int close_sub_dev(int sub_dev_nr) {
317 	size_t size;
318 	sub_dev_t *sub_dev_ptr;
319 	sub_dev_ptr = &sub_dev[sub_dev_nr];
320 	if (sub_dev_ptr->DmaMode == WRITE_DMA && !sub_dev_ptr->OutOfData) {
321 		/* do nothing, still data in buffers that has to be transferred */
322 		sub_dev_ptr->Opened = FALSE;  /* keep DMA busy */
323 		return OK;
324 	}
325 	if (sub_dev_ptr->DmaMode == NO_DMA) {
326 		/* do nothing, there is no dma going on */
327 		sub_dev_ptr->Opened = FALSE;
328 		return OK;
329 	}
330 	sub_dev_ptr->Opened = FALSE;
331 	sub_dev_ptr->DmaBusy = FALSE;
332 	/* stop the device */
333 	drv_stop(sub_dev_ptr->Nr);
334 	/* free the buffers */
335 	size= sub_dev_ptr->DmaSize + 64 * 1024;
336 	free_contig(sub_dev_ptr->DmaBuf, size);
337 	free(sub_dev_ptr->ExtraBuf);
338 	return OK;
339 }
340 
341 
342 static int msg_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
343 	cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id)
344 {
345 	int status, len, chan;
346 	sub_dev_t *sub_dev_ptr;
347 	special_file_t* special_file_ptr;
348 
349 	special_file_ptr = get_special_file(minor);
350 	if(special_file_ptr == NULL) {
351 		return EIO;
352 	}
353 
354 	chan = special_file_ptr->io_ctl;
355 
356 	if (chan == NO_CHANNEL) {
357 		printf("%s: No io control channel specified!\n", drv.DriverName);
358 		return EIO;
359 	}
360 	/* get pointer to sub device data */
361 	sub_dev_ptr = &sub_dev[chan];
362 
363 	if(!sub_dev_ptr->Opened) {
364 		printf("%s: io control impossible - not opened!\n", drv.DriverName);
365 		return EIO;
366 	}
367 
368 	if (request & IOC_IN) { /* if there is data for us, copy it */
369 		len = io_ctl_length(request);
370 
371 		if (sys_safecopyfrom(endpt, grant, 0, (vir_bytes)io_ctl_buf,
372 		    len) != OK) {
373 			printf("%s:%d: safecopyfrom failed\n", __FILE__, __LINE__);
374 		}
375 	}
376 
377 	/* all ioctl's are passed to the device specific part of the driver */
378 	status = drv_io_ctl(request, (void *)io_ctl_buf, &len, chan);
379 
380 	/* IOC_OUT bit -> user expects data */
381 	if (status == OK && request & IOC_OUT) {
382 		/* copy result back to user */
383 
384 		if (sys_safecopyto(endpt, grant, 0, (vir_bytes)io_ctl_buf,
385 		    len) != OK) {
386 			printf("%s:%d: safecopyto failed\n", __FILE__, __LINE__);
387 		}
388 
389 	}
390 	return status;
391 }
392 
393 
394 static ssize_t msg_write(devminor_t minor, u64_t UNUSED(position),
395 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int UNUSED(flags),
396 	cdev_id_t id)
397 {
398 	int chan; sub_dev_t *sub_dev_ptr;
399 	special_file_t* special_file_ptr;
400 
401 	special_file_ptr = get_special_file(minor);
402 	chan = special_file_ptr->write_chan;
403 
404 	if (chan == NO_CHANNEL) {
405 		printf("%s: No write channel specified!\n", drv.DriverName);
406 		return EIO;
407 	}
408 	/* get pointer to sub device data */
409 	sub_dev_ptr = &sub_dev[chan];
410 
411 	if (!sub_dev_ptr->DmaBusy) { /* get fragment size on first write */
412 		if (drv_get_frag_size(&(sub_dev_ptr->FragSize), sub_dev_ptr->Nr) != OK){
413 			printf("%s; Failed to get fragment size!\n", drv.DriverName);
414 			return EIO;
415 		}
416 	}
417 	if(size != sub_dev_ptr->FragSize) {
418 		printf("Fragment size does not match user's buffer length\n");
419 		return EINVAL;
420 	}
421 	/* if we are busy with something else than writing, return EBUSY */
422 	if(sub_dev_ptr->DmaBusy && sub_dev_ptr->DmaMode != WRITE_DMA) {
423 		printf("Already busy with something else than writing\n");
424 		return EBUSY;
425 	}
426 
427 	sub_dev_ptr->RevivePending = TRUE;
428 	sub_dev_ptr->ReviveId = id;
429 	sub_dev_ptr->ReviveGrant = grant;
430 	sub_dev_ptr->SourceProcNr = endpt;
431 
432 	data_from_user(sub_dev_ptr);
433 
434 	if(!sub_dev_ptr->DmaBusy) { /* Dma tranfer not yet started */
435 		get_started(sub_dev_ptr);
436 		sub_dev_ptr->DmaMode = WRITE_DMA; /* Dma mode is writing */
437 	}
438 
439 	/* We may already have replied by now. In any case don't reply here. */
440 	return EDONTREPLY;
441 }
442 
443 
444 static ssize_t msg_read(devminor_t minor, u64_t UNUSED(position),
445 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int UNUSED(flags),
446 	cdev_id_t id)
447 {
448 	int chan; sub_dev_t *sub_dev_ptr;
449 	special_file_t* special_file_ptr;
450 
451 	special_file_ptr = get_special_file(minor);
452 	chan = special_file_ptr->read_chan;
453 
454 	if (chan == NO_CHANNEL) {
455 		printf("%s: No read channel specified!\n", drv.DriverName);
456 		return EIO;
457 	}
458 	/* get pointer to sub device data */
459 	sub_dev_ptr = &sub_dev[chan];
460 
461 	if (!sub_dev_ptr->DmaBusy) { /* get fragment size on first read */
462 		if (drv_get_frag_size(&(sub_dev_ptr->FragSize), sub_dev_ptr->Nr) != OK){
463 			printf("%s: Could not retrieve fragment size!\n", drv.DriverName);
464 			return EIO;
465 		}
466 	}
467 	if(size != sub_dev_ptr->FragSize) {
468 		printf("fragment size does not match message size\n");
469 		return EINVAL;
470 	}
471 	/* if we are busy with something else than reading, reply EBUSY */
472 	if(sub_dev_ptr->DmaBusy && sub_dev_ptr->DmaMode != READ_DMA) {
473 		return EBUSY;
474 	}
475 
476 	sub_dev_ptr->RevivePending = TRUE;
477 	sub_dev_ptr->ReviveId = id;
478 	sub_dev_ptr->ReviveGrant = grant;
479 	sub_dev_ptr->SourceProcNr = endpt;
480 
481 	if(!sub_dev_ptr->DmaBusy) { /* Dma tranfer not yet started */
482 		get_started(sub_dev_ptr);
483 		sub_dev_ptr->DmaMode = READ_DMA; /* Dma mode is reading */
484 		/* no need to get data from DMA buffer at this point */
485 		return EDONTREPLY;
486 	}
487 	/* check if data is available and possibly fill user's buffer */
488 	data_to_user(sub_dev_ptr);
489 
490 	/* We may already have replied by now. In any case don't reply here. */
491 	return EDONTREPLY;
492 }
493 
494 
495 static void msg_hardware(unsigned int UNUSED(mask))
496 {
497 	int i;
498 
499 	/* if we have an interrupt */
500 	if (drv_int_sum()) {
501 		/* loop over all sub devices */
502 		for ( i = 0; i < drv.NrOfSubDevices; i++) {
503 			/* if interrupt from sub device and Dma transfer
504 			   was actually busy, take care of business */
505 			if( drv_int(i) && sub_dev[i].DmaBusy ) {
506 				if (sub_dev[i].DmaMode == WRITE_DMA)
507 					handle_int_write(i);
508 				if (sub_dev[i].DmaMode == READ_DMA)
509 					handle_int_read(i);
510 			}
511 		}
512 	}
513 
514 	/* As IRQ_REENABLE is not on in sys_irqsetpolicy, we must
515 	 * re-enable out interrupt after every interrupt.
516 	 */
517 	if ((sys_irqenable(&irq_hook_id)) != OK) {
518 	  printf("%s: msg_hardware: Couldn't enable IRQ\n", drv.DriverName);
519 	}
520 }
521 
522 
523 /* handle interrupt for specified sub device; DmaMode == WRITE_DMA */
524 static void handle_int_write(int sub_dev_nr)
525 {
526 	sub_dev_t *sub_dev_ptr;
527 
528 	sub_dev_ptr = &sub_dev[sub_dev_nr];
529 
530 	sub_dev_ptr->DmaReadNext =
531 		(sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
532 	sub_dev_ptr->DmaLength -= 1;
533 
534 	if (sub_dev_ptr->BufLength != 0) { /* Data in extra buf, copy to Dma buf */
535 
536 		memcpy(sub_dev_ptr->DmaPtr +
537 				sub_dev_ptr->DmaFillNext * sub_dev_ptr->FragSize,
538 				sub_dev_ptr->ExtraBuf +
539 				sub_dev_ptr->BufReadNext * sub_dev_ptr->FragSize,
540 				sub_dev_ptr->FragSize);
541 
542 		sub_dev_ptr->BufReadNext =
543 			(sub_dev_ptr->BufReadNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
544 		sub_dev_ptr->DmaFillNext =
545 			(sub_dev_ptr->DmaFillNext + 1) % sub_dev_ptr->NrOfDmaFragments;
546 
547 		sub_dev_ptr->BufLength -= 1;
548 		sub_dev_ptr->DmaLength += 1;
549 	}
550 
551 	/* space became available, possibly copy new data from user */
552 	data_from_user(sub_dev_ptr);
553 
554 	if(sub_dev_ptr->DmaLength == 0) { /* Dma buffer empty, stop Dma transfer */
555 
556 		sub_dev_ptr->OutOfData = TRUE; /* we're out of data */
557 		if (!sub_dev_ptr->Opened) {
558 			close_sub_dev(sub_dev_ptr->Nr);
559 			return;
560 		}
561 		drv_pause(sub_dev_ptr->Nr);
562 		return;
563 	}
564 
565 	/* confirm and reenable interrupt from this sub dev */
566 	drv_reenable_int(sub_dev_nr);
567 #if 0
568 	/* reenable irq_hook*/
569 	if (sys_irqenable(&irq_hook_id != OK) {
570 		printf("%s Couldn't enable IRQ\n", drv.DriverName);
571 	}
572 #endif
573 }
574 
575 
576 /* handle interrupt for specified sub device; DmaMode == READ_DMA */
577 static void handle_int_read(int sub_dev_nr)
578 {
579 	sub_dev_t *sub_dev_ptr;
580 
581 	sub_dev_ptr = &sub_dev[sub_dev_nr];
582 
583 	sub_dev_ptr->DmaLength += 1;
584 	sub_dev_ptr->DmaFillNext =
585 		(sub_dev_ptr->DmaFillNext + 1) % sub_dev_ptr->NrOfDmaFragments;
586 
587 	/* possibly copy data to user (if it is waiting for us) */
588 	data_to_user(sub_dev_ptr);
589 
590 	if (sub_dev_ptr->DmaLength == sub_dev_ptr->NrOfDmaFragments) {
591 		/* if dma buffer full */
592 
593 		if (sub_dev_ptr->BufLength == sub_dev_ptr->NrOfExtraBuffers) {
594 			printf("All buffers full, we have a problem.\n");
595 			drv_stop(sub_dev_nr);        /* stop the sub device */
596 			sub_dev_ptr->DmaBusy = FALSE;
597 			/* no data for user, this is a sad story */
598 			chardriver_reply_task(sub_dev_ptr->SourceProcNr,
599 				sub_dev_ptr->ReviveId, 0);
600 			return;
601 		}
602 		else { /* dma full, still room in extra buf;
603 				  copy from dma to extra buf */
604 			memcpy(sub_dev_ptr->ExtraBuf +
605 					sub_dev_ptr->BufFillNext * sub_dev_ptr->FragSize,
606 					sub_dev_ptr->DmaPtr +
607 					sub_dev_ptr->DmaReadNext * sub_dev_ptr->FragSize,
608 					sub_dev_ptr->FragSize);
609 			sub_dev_ptr->DmaLength -= 1;
610 			sub_dev_ptr->DmaReadNext =
611 				(sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
612 			sub_dev_ptr->BufLength += 1;
613 			sub_dev_ptr->BufFillNext =
614 				(sub_dev_ptr->BufFillNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
615 		}
616 	}
617 	/* confirm interrupt, and reenable interrupt from this sub dev*/
618 	drv_reenable_int(sub_dev_ptr->Nr);
619 
620 #if 0
621 	/* reenable irq_hook*/
622 	if (sys_irqenable(&irq_hook_id) != OK) {
623 		printf("%s: Couldn't reenable IRQ", drv.DriverName);
624 	}
625 #endif
626 }
627 
628 
629 static int get_started(sub_dev_t *sub_dev_ptr) {
630 	u32_t i;
631 
632 	/* enable interrupt messages from MINIX */
633 	if ((i=sys_irqenable(&irq_hook_id)) != OK) {
634 		printf("%s: Couldn't enable IRQs: error code %u",drv.DriverName, (unsigned int) i);
635 		return EIO;
636 	}
637 	/* let the lower part of the driver start the device */
638 	if (drv_start(sub_dev_ptr->Nr, sub_dev_ptr->DmaMode) != OK) {
639 		printf("%s: Could not start device %d\n",
640 				drv.DriverName, sub_dev_ptr->Nr);
641 	}
642 
643 	sub_dev_ptr->DmaBusy = TRUE;     /* Dma is busy from now on */
644 	sub_dev_ptr->DmaReadNext = 0;
645 	return OK;
646 }
647 
648 
649 static void data_from_user(sub_dev_t *subdev)
650 {
651 	int r;
652 
653 	if (subdev->DmaLength == subdev->NrOfDmaFragments &&
654 			subdev->BufLength == subdev->NrOfExtraBuffers) return;/* no space */
655 
656 	if (!subdev->RevivePending) return; /* no new data waiting to be copied */
657 
658 	if (subdev->DmaLength < subdev->NrOfDmaFragments) { /* room in dma buf */
659 
660 		r = sys_safecopyfrom(subdev->SourceProcNr,
661 				(vir_bytes)subdev->ReviveGrant, 0,
662 				(vir_bytes)subdev->DmaPtr +
663 				subdev->DmaFillNext * subdev->FragSize,
664 				(phys_bytes)subdev->FragSize);
665 		if (r != OK)
666 			printf("%s:%d: safecopy failed\n", __FILE__, __LINE__);
667 
668 
669 		subdev->DmaLength += 1;
670 		subdev->DmaFillNext =
671 			(subdev->DmaFillNext + 1) % subdev->NrOfDmaFragments;
672 
673 	} else { /* room in extra buf */
674 
675 		r = sys_safecopyfrom(subdev->SourceProcNr,
676 				(vir_bytes)subdev->ReviveGrant, 0,
677 				(vir_bytes)subdev->ExtraBuf +
678 				subdev->BufFillNext * subdev->FragSize,
679 				(phys_bytes)subdev->FragSize);
680 		if (r != OK)
681 			printf("%s:%d: safecopy failed\n", __FILE__, __LINE__);
682 
683 		subdev->BufLength += 1;
684 
685 		subdev->BufFillNext =
686 			(subdev->BufFillNext + 1) % subdev->NrOfExtraBuffers;
687 
688 	}
689 	if(subdev->OutOfData) { /* if device paused (because of lack of data) */
690 		subdev->OutOfData = FALSE;
691 		drv_reenable_int(subdev->Nr);
692 		/* reenable irq_hook*/
693 		if ((sys_irqenable(&irq_hook_id)) != OK) {
694 			printf("%s: Couldn't enable IRQ", drv.DriverName);
695 		}
696 		drv_resume(subdev->Nr);  /* resume resume the sub device */
697 	}
698 
699 	chardriver_reply_task(subdev->SourceProcNr, subdev->ReviveId,
700 		subdev->FragSize);
701 
702 	/* reset variables */
703 	subdev->RevivePending = 0;
704 }
705 
706 
707 static void data_to_user(sub_dev_t *sub_dev_ptr)
708 {
709 	int r;
710 
711 	if (!sub_dev_ptr->RevivePending) return; /* nobody is wating for data */
712 	if (sub_dev_ptr->BufLength == 0 && sub_dev_ptr->DmaLength == 0) return;
713 		/* no data for user */
714 
715 	if(sub_dev_ptr->BufLength != 0) { /* data in extra buffer available */
716 
717 		r = sys_safecopyto(sub_dev_ptr->SourceProcNr,
718 				(vir_bytes)sub_dev_ptr->ReviveGrant,
719 				0, (vir_bytes)sub_dev_ptr->ExtraBuf +
720 				sub_dev_ptr->BufReadNext * sub_dev_ptr->FragSize,
721 				(phys_bytes)sub_dev_ptr->FragSize);
722 		if (r != OK)
723 			printf("%s:%d: safecopy failed\n", __FILE__, __LINE__);
724 
725 		/* adjust the buffer status variables */
726 		sub_dev_ptr->BufReadNext =
727 			(sub_dev_ptr->BufReadNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
728 		sub_dev_ptr->BufLength -= 1;
729 
730 	} else { /* extra buf empty, but data in dma buf*/
731 		r = sys_safecopyto(
732 				sub_dev_ptr->SourceProcNr,
733 				(vir_bytes)sub_dev_ptr->ReviveGrant, 0,
734 				(vir_bytes)sub_dev_ptr->DmaPtr +
735 				sub_dev_ptr->DmaReadNext * sub_dev_ptr->FragSize,
736 				(phys_bytes)sub_dev_ptr->FragSize);
737 		if (r != OK)
738 			printf("%s:%d: safecopy failed\n", __FILE__, __LINE__);
739 
740 		/* adjust the buffer status variables */
741 		sub_dev_ptr->DmaReadNext =
742 			(sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
743 		sub_dev_ptr->DmaLength -= 1;
744 	}
745 
746 	chardriver_reply_task(sub_dev_ptr->SourceProcNr, sub_dev_ptr->ReviveId,
747 		sub_dev_ptr->FragSize);
748 
749 	/* reset variables */
750 	sub_dev_ptr->RevivePending = 0;
751 }
752 
753 static int init_buffers(sub_dev_t *sub_dev_ptr)
754 {
755 #if defined(__i386__)
756 	char *base;
757 	size_t size;
758 	unsigned left;
759 	u32_t i;
760 	phys_bytes ph;
761 
762 	/* allocate dma buffer space */
763 	size= sub_dev_ptr->DmaSize + 64 * 1024;
764 	base= alloc_contig(size, AC_ALIGN64K|AC_LOWER16M, &ph);
765 	if (!base) {
766 		printf("%s: failed to allocate dma buffer for a channel\n",
767 				drv.DriverName);
768 		return EIO;
769 	}
770 	sub_dev_ptr->DmaBuf= base;
771 
772 	tell_dev((vir_bytes)base, size, 0, 0, 0);
773 
774 	/* allocate extra buffer space */
775 	if (!(sub_dev_ptr->ExtraBuf = malloc(sub_dev_ptr->NrOfExtraBuffers *
776 					sub_dev_ptr->DmaSize /
777 					sub_dev_ptr->NrOfDmaFragments))) {
778 		printf("%s failed to allocate extra buffer for a channel\n",
779 				drv.DriverName);
780 		return EIO;
781 	}
782 
783 	sub_dev_ptr->DmaPtr = sub_dev_ptr->DmaBuf;
784 	i = sys_umap(SELF, VM_D, (vir_bytes) base, (phys_bytes) size,
785 			&(sub_dev_ptr->DmaPhys));
786 
787 	if (i != OK) {
788 		return EIO;
789 	}
790 
791 	if ((left = dma_bytes_left(sub_dev_ptr->DmaPhys)) <
792 			(unsigned int)sub_dev_ptr->DmaSize) {
793 		/* First half of buffer crosses a 64K boundary,
794 		 * can't DMA into that */
795 		sub_dev_ptr->DmaPtr += left;
796 		sub_dev_ptr->DmaPhys += left;
797 	}
798 	/* write the physical dma address and size to the device */
799 	drv_set_dma(sub_dev_ptr->DmaPhys,
800 			sub_dev_ptr->DmaSize, sub_dev_ptr->Nr);
801 	return OK;
802 
803 #else /* !defined(__i386__) */
804 	printf("%s: init_buffers() failed, CHIP != INTEL", drv.DriverName);
805 	return EIO;
806 #endif /* defined(__i386__) */
807 }
808 
809 
810 static int io_ctl_length(int io_request) {
811 	io_request >>= 16;
812 	return io_request & IOCPARM_MASK;
813 }
814 
815 
816 static special_file_t* get_special_file(int minor_dev_nr) {
817 	int i;
818 
819 	for(i = 0; i < drv.NrOfSpecialFiles; i++) {
820 		if(special_file[i].minor_dev_nr == minor_dev_nr) {
821 			return &special_file[i];
822 		}
823 	}
824 
825 	printf("%s: No subdevice specified for minor device %d!\n",
826 			drv.DriverName, minor_dev_nr);
827 
828 	return NULL;
829 }
830 
831 #if defined(__i386__)
832 static void tell_dev(vir_bytes buf, size_t size, int pci_bus,
833                      int pci_dev, int pci_func)
834 {
835 	int r;
836 	endpoint_t dev_e;
837 	message m;
838 
839 	r= ds_retrieve_label_endpt("amddev", &dev_e);
840 	if (r != OK)
841 	{
842 #if 0
843 		printf("tell_dev: ds_retrieve_label_endpt failed for 'amddev': %d\n",
844 			r);
845 #endif
846 		return;
847 	}
848 
849 	m.m_type= IOMMU_MAP;
850 	m.m2_i1= pci_bus;
851 	m.m2_i2= pci_dev;
852 	m.m2_i3= pci_func;
853 	m.m2_l1= buf;
854 	m.m2_l2= size;
855 
856 	r= ipc_sendrec(dev_e, &m);
857 	if (r != OK)
858 	{
859 		printf("tell_dev: ipc_sendrec to %d failed: %d\n", dev_e, r);
860 		return;
861 	}
862 	if (m.m_type != OK)
863 	{
864 		printf("tell_dev: dma map request failed: %d\n", m.m_type);
865 		return;
866 	}
867 }
868 #endif
869