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