add opendir alias
[minix.git] / lib / libaudiodriver / audio_fw.c
blob5b9ea47e33b8ac24785368c962e31435f45a65d0
1 /* Best viewed with tabsize 4
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.
9 * The driver supports the following operations:
11 * m_type DEVICE USER_ENDPT COUNT POSITION ADRRESS
12 * -----------------------------------------------------------------
13 * | DEV_OPEN | device | proc nr | | | |
14 * |-------------+---------+---------+---------+---------+---------|
15 * | DEV_CLOSE | device | proc nr | | | |
16 * |-------------+---------+---------+---------+---------+---------|
17 * | DEV_READ_S | device | proc nr | bytes | | buf ptr |
18 * |-------------+---------+---------+---------+---------+---------|
19 * | DEV_WRITE_S | device | proc nr | bytes | | buf ptr |
20 * |-------------+---------+---------+---------+---------+---------|
21 * | DEV_IOCTL_S | device | proc nr |func code| | buf ptr |
22 * |-------------+---------+---------+---------+---------+---------|
23 * | DEV_STATUS | | | | | |
24 * |-------------+---------+---------+---------+---------+---------|
25 * | HARD_INT | | | | | |
26 * |-------------+---------+---------+---------+---------+---------|
27 * | SIG_STOP | | | | | |
28 * -----------------------------------------------------------------
30 * The file contains one entry point:
32 * main: main entry when driver is brought up
34 * October 2007 Updated audio framework to work with mplayer, added
35 * savecopies (Pieter Hijma)
36 * February 2006 Updated audio framework,
37 * changed driver-framework relation (Peter Boonstoppel)
38 * November 2005 Created generic DMA driver framework (Laurens Bronwasser)
39 * August 24 2005 Ported audio driver to user space
40 * (only audio playback) (Peter Boonstoppel)
41 * May 20 1995 SB16 Driver: Michel R. Prevenier
45 #include <minix/audio_fw.h>
46 #include <minix/endpoint.h>
47 #include <minix/ds.h>
50 static int msg_open(int minor_dev_nr);
51 static int msg_close(int minor_dev_nr);
52 static int msg_ioctl(const message *m_ptr);
53 static void msg_write(const message *m_ptr);
54 static void msg_read(message *m_ptr);
55 static void msg_hardware(void);
56 static void msg_status(message *m_ptr);
57 static int init_driver(void);
58 static int open_sub_dev(int sub_dev_nr, int operation);
59 static int close_sub_dev(int sub_dev_nr);
60 static void handle_int_write(int sub_dev_nr);
61 static void handle_int_read(int sub_dev_nr);
62 static void data_to_user(sub_dev_t *sub_dev_ptr);
63 static void data_from_user(sub_dev_t *sub_dev_ptr);
64 static int init_buffers(sub_dev_t *sub_dev_ptr);
65 static int get_started(sub_dev_t *sub_dev_ptr);
66 static void reply(int code, int replyee, int process,int status);
67 static int io_ctl_length(int io_request);
68 static special_file_t* get_special_file(int minor_dev_nr);
69 static void tell_dev(vir_bytes buf, size_t size, int pci_bus, int
70 pci_dev, int pci_func);
72 static char io_ctl_buf[_IOCPARM_MASK];
73 static int irq_hook_id = 0; /* id of irq hook at the kernel */
74 static int irq_hook_set = FALSE;
75 static int device_available = 0;/*todo*/
77 /* SEF functions and variables. */
78 static void sef_local_startup(void);
79 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
80 static void sef_cb_signal_handler(int signo);
81 EXTERN int sef_cb_lu_prepare(int state);
82 EXTERN int sef_cb_lu_state_isvalid(int state);
83 EXTERN void sef_cb_lu_state_dump(int state);
84 int is_status_msg_expected = FALSE;
86 int main(void)
88 int r, caller;
89 message mess, repl_mess;
90 int ipc_status;
92 /* SEF local startup. */
93 sef_local_startup();
95 /* Here is the main loop of the dma driver. It waits for a message,
96 carries it out, and sends a reply. */
98 while(1) {
99 if(driver_receive(ANY, &mess, &ipc_status) != OK) {
100 panic("driver_receive failed");
102 caller = mess.m_source;
104 /* Now carry out the work. First check for notifications. */
105 if (is_ipc_notify(ipc_status)) {
106 switch (_ENDPOINT_P(mess.m_source)) {
107 case HARDWARE:
108 msg_hardware();
109 break;
110 default:
111 printf("%s: %d uncaught notify!\n",
112 drv.DriverName, mess.m_type);
115 /* get next message */
116 continue;
119 /* Normal messages. */
120 switch(mess.m_type) {
121 case DEV_OPEN:
122 /* open the special file ( = parameter) */
123 r = msg_open(mess.DEVICE);
124 repl_mess.m_type = DEV_REVIVE;
125 repl_mess.REP_ENDPT = mess.USER_ENDPT;
126 repl_mess.REP_STATUS = r;
127 send(caller, &repl_mess);
129 continue;
131 case DEV_CLOSE:
132 /* close the special file ( = parameter) */
133 r = msg_close(mess.DEVICE);
134 repl_mess.m_type = DEV_CLOSE_REPL;
135 repl_mess.REP_ENDPT = mess.USER_ENDPT;
136 repl_mess.REP_STATUS = r;
137 send(caller, &repl_mess);
139 continue;
141 case DEV_IOCTL_S:
142 r = msg_ioctl(&mess);
144 if (r != SUSPEND)
146 repl_mess.m_type = DEV_REVIVE;
147 repl_mess.REP_ENDPT = mess.USER_ENDPT;
148 repl_mess.REP_IO_GRANT =
149 (unsigned)mess.IO_GRANT;
150 repl_mess.REP_STATUS = r;
151 send(caller, &repl_mess);
153 continue;
155 case DEV_READ_S:
156 msg_read(&mess); continue; /* don't reply */
157 case DEV_WRITE_S:
158 msg_write(&mess); continue; /* don't reply */
159 case DEV_STATUS:
160 msg_status(&mess);continue; /* don't reply */
161 case DEV_REOPEN:
162 /* reopen the special file ( = parameter) */
163 r = msg_open(mess.DEVICE);
164 repl_mess.m_type = DEV_REOPEN_REPL;
165 repl_mess.REP_ENDPT = mess.USER_ENDPT;
166 repl_mess.REP_STATUS = r;
167 send(caller, &repl_mess);
168 continue;
169 default:
170 printf("%s: %d uncaught msg!\n",
171 drv.DriverName, mess.m_type);
172 continue;
175 /* Should not be here. Just continue. */
177 return 1;
180 /*===========================================================================*
181 * sef_local_startup *
182 *===========================================================================*/
183 static void sef_local_startup()
185 /* Register init callbacks. */
186 sef_setcb_init_fresh(sef_cb_init_fresh);
187 sef_setcb_init_lu(sef_cb_init_fresh);
188 sef_setcb_init_restart(sef_cb_init_fresh);
190 /* Register live update callbacks. */
191 sef_setcb_lu_prepare(sef_cb_lu_prepare);
192 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid);
193 sef_setcb_lu_state_dump(sef_cb_lu_state_dump);
195 /* Register signal callbacks. */
196 sef_setcb_signal_handler(sef_cb_signal_handler);
198 /* Let SEF perform startup. */
199 sef_startup();
202 /*===========================================================================*
203 * sef_cb_init_fresh *
204 *===========================================================================*/
205 static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
207 /* Initialize the audio driver framework. */
208 return init_driver();
211 static int init_driver(void) {
212 u32_t i; char irq;
213 static int executed = 0;
214 sub_dev_t* sub_dev_ptr;
216 /* initialize basic driver variables */
217 if (drv_init() != OK) {
218 printf("libaudiodriver: Could not initialize driver\n");
219 return EIO;
222 /* init variables, get dma buffers */
223 for (i = 0; i < drv.NrOfSubDevices; i++) {
225 sub_dev_ptr = &sub_dev[i];
227 sub_dev_ptr->Opened = FALSE;
228 sub_dev_ptr->DmaBusy = FALSE;
229 sub_dev_ptr->DmaMode = NO_DMA;
230 sub_dev_ptr->DmaReadNext = 0;
231 sub_dev_ptr->DmaFillNext = 0;
232 sub_dev_ptr->DmaLength = 0;
233 sub_dev_ptr->BufReadNext = 0;
234 sub_dev_ptr->BufFillNext = 0;
235 sub_dev_ptr->RevivePending = FALSE;
236 sub_dev_ptr->OutOfData = FALSE;
237 sub_dev_ptr->Nr = i;
240 /* initialize hardware*/
241 if (drv_init_hw() != OK) {
242 printf("%s: Could not initialize hardware\n", drv.DriverName);
243 return EIO;
246 /* get irq from device driver...*/
247 if (drv_get_irq(&irq) != OK) {
248 printf("%s: init driver couldn't get IRQ", drv.DriverName);
249 return EIO;
251 /* TODO: execute the rest of this function only once
252 we don't want to set irq policy twice */
253 if (executed) return OK;
254 executed = TRUE;
256 /* ...and register interrupt vector */
257 if ((i=sys_irqsetpolicy(irq, 0, &irq_hook_id )) != OK){
258 printf("%s: init driver couldn't set IRQ policy: %d", drv.DriverName, i);
259 return EIO;
261 irq_hook_set = TRUE; /* now signal handler knows it must unregister policy*/
263 /* Announce we are up! */
264 chardriver_announce();
266 return OK;
269 /*===========================================================================*
270 * sef_cb_signal_handler *
271 *===========================================================================*/
272 static void sef_cb_signal_handler(int signo)
274 int i;
275 char irq;
277 /* Only check for termination signal, ignore anything else. */
278 if (signo != SIGTERM) return;
280 for (i = 0; i < drv.NrOfSubDevices; i++) {
281 drv_stop(i); /* stop all sub devices */
283 if (irq_hook_set) {
284 if (sys_irqdisable(&irq_hook_id) != OK) {
285 printf("Could not disable IRQ\n");
287 /* get irq from device driver*/
288 if (drv_get_irq(&irq) != OK) {
289 printf("Msg SIG_STOP Couldn't get IRQ");
291 /* remove the policy */
292 if (sys_irqrmpolicy(&irq_hook_id) != OK) {
293 printf("%s: Could not disable IRQ\n",drv.DriverName);
298 static int msg_open (int minor_dev_nr) {
299 int r, read_chan, write_chan, io_ctl;
300 special_file_t* special_file_ptr;
302 special_file_ptr = get_special_file(minor_dev_nr);
303 if(special_file_ptr == NULL) {
304 return EIO;
307 read_chan = special_file_ptr->read_chan;
308 write_chan = special_file_ptr->write_chan;
309 io_ctl = special_file_ptr->io_ctl;
311 if (read_chan==NO_CHANNEL && write_chan==NO_CHANNEL && io_ctl==NO_CHANNEL) {
312 printf("%s: No channel specified for minor device %d!\n",
313 drv.DriverName, minor_dev_nr);
314 return EIO;
316 if (read_chan == write_chan && read_chan != NO_CHANNEL) {
317 printf("%s: Read and write channels are equal: %d!\n",
318 drv.DriverName, minor_dev_nr);
319 return EIO;
321 /* init driver */
322 if (!device_available) {
323 if (init_driver() != OK) {
324 printf("%s: Couldn't init driver!\n", drv.DriverName);
325 return EIO;
326 } else {
327 device_available = TRUE;
330 /* open the sub devices specified in the interface header file */
331 if (write_chan != NO_CHANNEL) {
332 /* open sub device for writing */
333 if (open_sub_dev(write_chan, DEV_WRITE_S) != OK) return EIO;
335 if (read_chan != NO_CHANNEL) {
336 if (open_sub_dev(read_chan, DEV_READ_S) != OK) return EIO;
338 if (read_chan == io_ctl || write_chan == io_ctl) {
339 /* io_ctl is already opened because it's the same as read or write */
340 return OK; /* we're done */
342 if (io_ctl != NO_CHANNEL) { /* Ioctl differs from read/write channels, */
343 r = open_sub_dev(io_ctl, NO_DMA); /* open it explicitly */
344 if (r != OK) return EIO;
346 return OK;
350 static int open_sub_dev(int sub_dev_nr, int dma_mode) {
351 sub_dev_t* sub_dev_ptr;
352 sub_dev_ptr = &sub_dev[sub_dev_nr];
354 /* Only one open at a time per sub device */
355 if (sub_dev_ptr->Opened) {
356 printf("%s: Sub device %d is already opened\n",
357 drv.DriverName, sub_dev_nr);
358 return EBUSY;
360 if (sub_dev_ptr->DmaBusy) {
361 printf("%s: Sub device %d is still busy\n", drv.DriverName, sub_dev_nr);
362 return EBUSY;
364 /* Setup variables */
365 sub_dev_ptr->Opened = TRUE;
366 sub_dev_ptr->DmaReadNext = 0;
367 sub_dev_ptr->DmaFillNext = 0;
368 sub_dev_ptr->DmaLength = 0;
369 sub_dev_ptr->DmaMode = dma_mode;
370 sub_dev_ptr->BufReadNext = 0;
371 sub_dev_ptr->BufFillNext = 0;
372 sub_dev_ptr->BufLength = 0;
373 sub_dev_ptr->RevivePending = FALSE;
374 sub_dev_ptr->OutOfData = TRUE;
376 /* arrange DMA */
377 if (dma_mode != NO_DMA) { /* sub device uses DMA */
378 /* allocate dma buffer and extra buffer space
379 and configure sub device for dma */
380 if (init_buffers(sub_dev_ptr) != OK ) return EIO;
382 return OK;
386 static int msg_close(int minor_dev_nr) {
388 int r, read_chan, write_chan, io_ctl;
389 special_file_t* special_file_ptr;
391 special_file_ptr = get_special_file(minor_dev_nr);
392 if(special_file_ptr == NULL) {
393 return EIO;
396 read_chan = special_file_ptr->read_chan;
397 write_chan = special_file_ptr->write_chan;
398 io_ctl = special_file_ptr->io_ctl;
400 r= OK;
402 /* close all sub devices */
403 if (write_chan != NO_CHANNEL) {
404 if (close_sub_dev(write_chan) != OK) r = EIO;
406 if (read_chan != NO_CHANNEL) {
407 if (close_sub_dev(read_chan) != OK) r = EIO;
409 if (read_chan == io_ctl || write_chan == io_ctl) {
410 /* io_ctl is already closed because it's the same as read or write */
411 return r; /* we're done */
413 /* ioctl differs from read/write channels... */
414 if (io_ctl != NO_CHANNEL) {
415 if (close_sub_dev(io_ctl) != OK) r = EIO; /* ...close it explicitly */
417 return r;
421 static int close_sub_dev(int sub_dev_nr) {
422 size_t size;
423 sub_dev_t *sub_dev_ptr;
424 sub_dev_ptr = &sub_dev[sub_dev_nr];
425 if (sub_dev_ptr->DmaMode == DEV_WRITE_S && !sub_dev_ptr->OutOfData) {
426 /* do nothing, still data in buffers that has to be transferred */
427 sub_dev_ptr->Opened = FALSE; /* keep DMA busy */
428 return OK;
430 if (sub_dev_ptr->DmaMode == NO_DMA) {
431 /* do nothing, there is no dma going on */
432 sub_dev_ptr->Opened = FALSE;
433 return OK;
435 sub_dev_ptr->Opened = FALSE;
436 sub_dev_ptr->DmaBusy = FALSE;
437 /* stop the device */
438 drv_stop(sub_dev_ptr->Nr);
439 /* free the buffers */
440 size= sub_dev_ptr->DmaSize + 64 * 1024;
441 free_contig(sub_dev_ptr->DmaBuf, size);
442 free(sub_dev_ptr->ExtraBuf);
443 return OK;
447 static int msg_ioctl(const message *m_ptr)
449 int status, len, chan;
450 sub_dev_t *sub_dev_ptr;
451 special_file_t* special_file_ptr;
453 special_file_ptr = get_special_file(m_ptr->DEVICE);
454 if(special_file_ptr == NULL) {
455 return EIO;
458 chan = special_file_ptr->io_ctl;
460 if (chan == NO_CHANNEL) {
461 printf("%s: No io control channel specified!\n", drv.DriverName);
462 return EIO;
464 /* get pointer to sub device data */
465 sub_dev_ptr = &sub_dev[chan];
467 if(!sub_dev_ptr->Opened) {
468 printf("%s: io control impossible - not opened!\n", drv.DriverName);
469 return EIO;
473 /* this is a hack...todo: may we intercept reset calls? */
475 if(m_ptr->REQUEST == DSPIORESET) {
476 device_available = FALSE;
481 if (m_ptr->REQUEST & _IOC_IN) { /* if there is data for us, copy it */
482 len = io_ctl_length(m_ptr->REQUEST);
484 if(sys_safecopyfrom(m_ptr->m_source,
485 (vir_bytes)m_ptr->ADDRESS, 0,
486 (vir_bytes)io_ctl_buf, len) != OK) {
487 printf("%s:%d: safecopyfrom failed\n", __FILE__, __LINE__);
491 /* all ioctl's are passed to the device specific part of the driver */
492 status = drv_io_ctl(m_ptr->REQUEST, (void *)io_ctl_buf, &len, chan);
494 /* _IOC_OUT bit -> user expects data */
495 if (status == OK && m_ptr->REQUEST & _IOC_OUT) {
496 /* copy result back to user */
498 if(sys_safecopyto(m_ptr->m_source, (vir_bytes)m_ptr->ADDRESS, 0,
499 (vir_bytes)io_ctl_buf, len) != OK) {
500 printf("%s:%d: safecopyto failed\n", __FILE__, __LINE__);
504 return status;
508 static void msg_write(const message *m_ptr)
510 int chan; sub_dev_t *sub_dev_ptr;
511 special_file_t* special_file_ptr;
513 special_file_ptr = get_special_file(m_ptr->DEVICE);
514 chan = special_file_ptr->write_chan;
516 if (chan == NO_CHANNEL) {
517 printf("%s: No write channel specified!\n", drv.DriverName);
518 reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EIO);
519 return;
521 /* get pointer to sub device data */
522 sub_dev_ptr = &sub_dev[chan];
524 if (!sub_dev_ptr->DmaBusy) { /* get fragment size on first write */
525 if (drv_get_frag_size(&(sub_dev_ptr->FragSize), sub_dev_ptr->Nr) != OK){
526 printf("%s; Failed to get fragment size!\n", drv.DriverName);
527 return;
530 if(m_ptr->COUNT != sub_dev_ptr->FragSize) {
531 printf("Fragment size does not match user's buffer length\n");
532 reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EINVAL);
533 return;
535 /* if we are busy with something else than writing, return EBUSY */
536 if(sub_dev_ptr->DmaBusy && sub_dev_ptr->DmaMode != DEV_WRITE_S) {
537 printf("Already busy with something else then writing\n");
538 reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EBUSY);
539 return;
542 sub_dev_ptr->RevivePending = TRUE;
543 sub_dev_ptr->ReviveProcNr = m_ptr->USER_ENDPT;
544 sub_dev_ptr->ReviveGrant = (cp_grant_id_t) m_ptr->ADDRESS;
545 sub_dev_ptr->SourceProcNr = m_ptr->m_source;
547 data_from_user(sub_dev_ptr);
549 if(!sub_dev_ptr->DmaBusy) { /* Dma tranfer not yet started */
550 get_started(sub_dev_ptr);
551 sub_dev_ptr->DmaMode = DEV_WRITE_S; /* Dma mode is writing */
556 static void msg_read(message *m_ptr)
558 int chan; sub_dev_t *sub_dev_ptr;
559 special_file_t* special_file_ptr;
561 special_file_ptr = get_special_file(m_ptr->DEVICE);
562 chan = special_file_ptr->read_chan;
564 if (chan == NO_CHANNEL) {
565 printf("%s: No read channel specified!\n", drv.DriverName);
566 reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EIO);
567 return;
569 /* get pointer to sub device data */
570 sub_dev_ptr = &sub_dev[chan];
572 if (!sub_dev_ptr->DmaBusy) { /* get fragment size on first read */
573 if (drv_get_frag_size(&(sub_dev_ptr->FragSize), sub_dev_ptr->Nr) != OK){
574 printf("%s: Could not retrieve fragment size!\n", drv.DriverName);
575 reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT,
576 EIO);
577 return;
580 if(m_ptr->COUNT != sub_dev_ptr->FragSize) {
581 reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EINVAL);
582 printf("fragment size does not match message size\n");
583 return;
585 /* if we are busy with something else than reading, reply EBUSY */
586 if(sub_dev_ptr->DmaBusy && sub_dev_ptr->DmaMode != DEV_READ_S) {
587 reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EBUSY);
588 return;
591 sub_dev_ptr->RevivePending = TRUE;
592 sub_dev_ptr->ReviveProcNr = m_ptr->USER_ENDPT;
593 sub_dev_ptr->ReviveGrant = (cp_grant_id_t) m_ptr->ADDRESS;
594 sub_dev_ptr->SourceProcNr = m_ptr->m_source;
596 if(!sub_dev_ptr->DmaBusy) { /* Dma tranfer not yet started */
597 get_started(sub_dev_ptr);
598 sub_dev_ptr->DmaMode = DEV_READ_S; /* Dma mode is reading */
599 return; /* no need to get data from DMA buffer at this point */
601 /* check if data is available and possibly fill user's buffer */
602 data_to_user(sub_dev_ptr);
606 static void msg_hardware(void) {
608 u32_t i;
610 /* while we have an interrupt */
611 while ( drv_int_sum()) {
612 /* loop over all sub devices */
613 for ( i = 0; i < drv.NrOfSubDevices; i++) {
614 /* if interrupt from sub device and Dma transfer
615 was actually busy, take care of business */
616 if( drv_int(i) && sub_dev[i].DmaBusy ) {
617 if (sub_dev[i].DmaMode == DEV_WRITE_S) handle_int_write(i);
618 if (sub_dev[i].DmaMode == DEV_READ_S) handle_int_read(i);
623 /* As IRQ_REENABLE is not on in sys_irqsetpolicy, we must
624 * re-enable out interrupt after every interrupt.
626 if ((sys_irqenable(&irq_hook_id)) != OK) {
627 printf("%s: msg_hardware: Couldn't enable IRQ\n", drv.DriverName);
632 static void msg_status(message *m_ptr)
634 int i;
636 for (i = 0; i < drv.NrOfSubDevices; i++) {
638 if(sub_dev[i].ReadyToRevive)
640 m_ptr->m_type = DEV_REVIVE; /* build message */
641 m_ptr->REP_ENDPT = sub_dev[i].ReviveProcNr;
642 m_ptr->REP_IO_GRANT = sub_dev[i].ReviveGrant;
643 m_ptr->REP_STATUS = sub_dev[i].ReviveStatus;
644 send(m_ptr->m_source, m_ptr); /* send the message */
646 /* reset variables */
647 sub_dev[i].ReadyToRevive = FALSE;
648 sub_dev[i].RevivePending = 0;
650 is_status_msg_expected = TRUE;
651 return; /* stop after one mess,
652 file system will get back for other processes */
655 m_ptr->m_type = DEV_NO_STATUS;
656 m_ptr->REP_STATUS = 0;
657 send(m_ptr->m_source, m_ptr); /* send DEV_NO_STATUS message */
658 is_status_msg_expected = FALSE;
661 /* handle interrupt for specified sub device; DmaMode == DEV_WRITE_S*/
662 static void handle_int_write(int sub_dev_nr)
664 sub_dev_t *sub_dev_ptr;
666 sub_dev_ptr = &sub_dev[sub_dev_nr];
668 sub_dev_ptr->DmaReadNext =
669 (sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
670 sub_dev_ptr->DmaLength -= 1;
672 if (sub_dev_ptr->BufLength != 0) { /* Data in extra buf, copy to Dma buf */
674 memcpy(sub_dev_ptr->DmaPtr +
675 sub_dev_ptr->DmaFillNext * sub_dev_ptr->FragSize,
676 sub_dev_ptr->ExtraBuf +
677 sub_dev_ptr->BufReadNext * sub_dev_ptr->FragSize,
678 sub_dev_ptr->FragSize);
680 sub_dev_ptr->BufReadNext =
681 (sub_dev_ptr->BufReadNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
682 sub_dev_ptr->DmaFillNext =
683 (sub_dev_ptr->DmaFillNext + 1) % sub_dev_ptr->NrOfDmaFragments;
685 sub_dev_ptr->BufLength -= 1;
686 sub_dev_ptr->DmaLength += 1;
689 /* space became available, possibly copy new data from user */
690 data_from_user(sub_dev_ptr);
692 if(sub_dev_ptr->DmaLength == 0) { /* Dma buffer empty, stop Dma transfer */
694 sub_dev_ptr->OutOfData = TRUE; /* we're out of data */
695 if (!sub_dev_ptr->Opened) {
696 close_sub_dev(sub_dev_ptr->Nr);
697 return;
699 drv_pause(sub_dev_ptr->Nr);
700 return;
703 /* confirm and reenable interrupt from this sub dev */
704 drv_reenable_int(sub_dev_nr);
705 #if 0
706 /* reenable irq_hook*/
707 if (sys_irqenable(&irq_hook_id != OK) {
708 printf("%s Couldn't enable IRQ\n", drv.DriverName);
710 #endif
714 /* handle interrupt for specified sub device; DmaMode == DEV_READ_S */
715 static void handle_int_read(int sub_dev_nr)
717 sub_dev_t *sub_dev_ptr;
719 sub_dev_ptr = &sub_dev[sub_dev_nr];
721 sub_dev_ptr->DmaLength += 1;
722 sub_dev_ptr->DmaFillNext =
723 (sub_dev_ptr->DmaFillNext + 1) % sub_dev_ptr->NrOfDmaFragments;
725 /* possibly copy data to user (if it is waiting for us) */
726 data_to_user(sub_dev_ptr);
728 if (sub_dev_ptr->DmaLength == sub_dev_ptr->NrOfDmaFragments) {
729 /* if dma buffer full */
731 if (sub_dev_ptr->BufLength == sub_dev_ptr->NrOfExtraBuffers) {
732 printf("All buffers full, we have a problem.\n");
733 drv_stop(sub_dev_nr); /* stop the sub device */
734 sub_dev_ptr->DmaBusy = FALSE;
735 sub_dev_ptr->ReviveStatus = 0; /* no data for user,
736 this is a sad story */
737 sub_dev_ptr->ReadyToRevive = TRUE; /* wake user up */
738 return;
740 else { /* dma full, still room in extra buf;
741 copy from dma to extra buf */
742 memcpy(sub_dev_ptr->ExtraBuf +
743 sub_dev_ptr->BufFillNext * sub_dev_ptr->FragSize,
744 sub_dev_ptr->DmaPtr +
745 sub_dev_ptr->DmaReadNext * sub_dev_ptr->FragSize,
746 sub_dev_ptr->FragSize);
747 sub_dev_ptr->DmaLength -= 1;
748 sub_dev_ptr->DmaReadNext =
749 (sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
750 sub_dev_ptr->BufFillNext =
751 (sub_dev_ptr->BufFillNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
754 /* confirm interrupt, and reenable interrupt from this sub dev*/
755 drv_reenable_int(sub_dev_ptr->Nr);
757 #if 0
758 /* reenable irq_hook*/
759 if (sys_irqenable(&irq_hook_id) != OK) {
760 printf("%s: Couldn't reenable IRQ", drv.DriverName);
762 #endif
766 static int get_started(sub_dev_t *sub_dev_ptr) {
767 u32_t i;
769 /* enable interrupt messages from MINIX */
770 if ((i=sys_irqenable(&irq_hook_id)) != OK) {
771 printf("%s: Couldn't enable IRQs: error code %u",drv.DriverName, (unsigned int) i);
772 return EIO;
774 /* let the lower part of the driver start the device */
775 if (drv_start(sub_dev_ptr->Nr, sub_dev_ptr->DmaMode) != OK) {
776 printf("%s: Could not start device %d\n",
777 drv.DriverName, sub_dev_ptr->Nr);
780 sub_dev_ptr->DmaBusy = TRUE; /* Dma is busy from now on */
781 sub_dev_ptr->DmaReadNext = 0;
782 return OK;
786 static void data_from_user(sub_dev_t *subdev)
788 int r;
789 message m;
791 if (subdev->DmaLength == subdev->NrOfDmaFragments &&
792 subdev->BufLength == subdev->NrOfExtraBuffers) return;/* no space */
794 if (!subdev->RevivePending) return; /* no new data waiting to be copied */
796 if (subdev->RevivePending &&
797 subdev->ReadyToRevive) return; /* we already got this data */
799 if (subdev->DmaLength < subdev->NrOfDmaFragments) { /* room in dma buf */
801 r = sys_safecopyfrom(subdev->SourceProcNr,
802 (vir_bytes)subdev->ReviveGrant, 0,
803 (vir_bytes)subdev->DmaPtr +
804 subdev->DmaFillNext * subdev->FragSize,
805 (phys_bytes)subdev->FragSize);
806 if (r != OK)
807 printf("%s:%d: safecopy failed\n", __FILE__, __LINE__);
810 subdev->DmaLength += 1;
811 subdev->DmaFillNext =
812 (subdev->DmaFillNext + 1) % subdev->NrOfDmaFragments;
814 } else { /* room in extra buf */
816 r = sys_safecopyfrom(subdev->SourceProcNr,
817 (vir_bytes)subdev->ReviveGrant, 0,
818 (vir_bytes)subdev->ExtraBuf +
819 subdev->BufFillNext * subdev->FragSize,
820 (phys_bytes)subdev->FragSize);
821 if (r != OK)
822 printf("%s:%d: safecopy failed\n", __FILE__, __LINE__);
824 subdev->BufLength += 1;
826 subdev->BufFillNext =
827 (subdev->BufFillNext + 1) % subdev->NrOfExtraBuffers;
830 if(subdev->OutOfData) { /* if device paused (because of lack of data) */
831 subdev->OutOfData = FALSE;
832 drv_reenable_int(subdev->Nr);
833 /* reenable irq_hook*/
834 if ((sys_irqenable(&irq_hook_id)) != OK) {
835 printf("%s: Couldn't enable IRQ", drv.DriverName);
837 drv_resume(subdev->Nr); /* resume resume the sub device */
840 subdev->ReviveStatus = subdev->FragSize;
841 subdev->ReadyToRevive = TRUE;
843 m.m_type = DEV_REVIVE; /* build message */
844 m.REP_ENDPT = subdev->ReviveProcNr;
845 m.REP_IO_GRANT = subdev->ReviveGrant;
846 m.REP_STATUS = subdev->ReviveStatus;
847 r= send(subdev->SourceProcNr, &m); /* send the message */
848 if (r != OK)
850 printf("audio_fw: send to %d failed: %d\n",
851 subdev->SourceProcNr, r);
854 /* reset variables */
855 subdev->ReadyToRevive = FALSE;
856 subdev->RevivePending = 0;
860 static void data_to_user(sub_dev_t *sub_dev_ptr)
862 int r;
863 message m;
865 if (!sub_dev_ptr->RevivePending) return; /* nobody is wating for data */
866 if (sub_dev_ptr->ReadyToRevive) return;/* we already filled user's buffer */
867 if (sub_dev_ptr->BufLength == 0 && sub_dev_ptr->DmaLength == 0) return;
868 /* no data for user */
870 if(sub_dev_ptr->BufLength != 0) { /* data in extra buffer available */
872 r = sys_safecopyto(sub_dev_ptr->SourceProcNr,
873 (vir_bytes)sub_dev_ptr->ReviveGrant,
874 0, (vir_bytes)sub_dev_ptr->ExtraBuf +
875 sub_dev_ptr->BufReadNext * sub_dev_ptr->FragSize,
876 (phys_bytes)sub_dev_ptr->FragSize);
877 if (r != OK)
878 printf("%s:%d: safecopy failed\n", __FILE__, __LINE__);
880 /* adjust the buffer status variables */
881 sub_dev_ptr->BufReadNext =
882 (sub_dev_ptr->BufReadNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
883 sub_dev_ptr->BufLength -= 1;
885 } else { /* extra buf empty, but data in dma buf*/
886 r = sys_safecopyto(
887 sub_dev_ptr->SourceProcNr,
888 (vir_bytes)sub_dev_ptr->ReviveGrant, 0,
889 (vir_bytes)sub_dev_ptr->DmaPtr +
890 sub_dev_ptr->DmaReadNext * sub_dev_ptr->FragSize,
891 (phys_bytes)sub_dev_ptr->FragSize);
892 if (r != OK)
893 printf("%s:%d: safecopy failed\n", __FILE__, __LINE__);
895 /* adjust the buffer status variables */
896 sub_dev_ptr->DmaReadNext =
897 (sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
898 sub_dev_ptr->DmaLength -= 1;
901 sub_dev_ptr->ReviveStatus = sub_dev_ptr->FragSize;
902 sub_dev_ptr->ReadyToRevive = TRUE;
903 /* drv_status will send REVIVE mess to FS*/
905 m.m_type = DEV_REVIVE; /* build message */
906 m.REP_ENDPT = sub_dev_ptr->ReviveProcNr;
907 m.REP_IO_GRANT = sub_dev_ptr->ReviveGrant;
908 m.REP_STATUS = sub_dev_ptr->ReviveStatus;
909 r= send(sub_dev_ptr->SourceProcNr, &m); /* send the message */
910 if (r != OK)
912 printf("audio_fw: send to %d failed: %d\n",
913 sub_dev_ptr->SourceProcNr, r);
916 /* reset variables */
917 sub_dev_ptr->ReadyToRevive = FALSE;
918 sub_dev_ptr->RevivePending = 0;
921 static int init_buffers(sub_dev_t *sub_dev_ptr)
923 #if defined(__i386__)
924 char *base;
925 size_t size;
926 unsigned left;
927 u32_t i;
928 phys_bytes ph;
930 /* allocate dma buffer space */
931 size= sub_dev_ptr->DmaSize + 64 * 1024;
932 base= alloc_contig(size, AC_ALIGN4K, &ph);
933 if (!base) {
934 printf("%s: failed to allocate dma buffer for a channel\n",
935 drv.DriverName);
936 return EIO;
938 sub_dev_ptr->DmaBuf= base;
940 tell_dev((vir_bytes)base, size, 0, 0, 0);
942 /* allocate extra buffer space */
943 if (!(sub_dev_ptr->ExtraBuf = malloc(sub_dev_ptr->NrOfExtraBuffers *
944 sub_dev_ptr->DmaSize /
945 sub_dev_ptr->NrOfDmaFragments))) {
946 printf("%s failed to allocate extra buffer for a channel\n",
947 drv.DriverName);
948 return EIO;
951 sub_dev_ptr->DmaPtr = sub_dev_ptr->DmaBuf;
952 i = sys_umap(SELF, VM_D, (vir_bytes) base, (phys_bytes) size,
953 &(sub_dev_ptr->DmaPhys));
955 if (i != OK) {
956 return EIO;
959 if ((left = dma_bytes_left(sub_dev_ptr->DmaPhys)) <
960 sub_dev_ptr->DmaSize) {
961 /* First half of buffer crosses a 64K boundary,
962 * can't DMA into that */
963 sub_dev_ptr->DmaPtr += left;
964 sub_dev_ptr->DmaPhys += left;
966 /* write the physical dma address and size to the device */
967 drv_set_dma(sub_dev_ptr->DmaPhys,
968 sub_dev_ptr->DmaSize, sub_dev_ptr->Nr);
969 return OK;
971 #else /* !defined(__i386__) */
972 printf("%s: init_buffers() failed, CHIP != INTEL", drv.DriverName);
973 return EIO;
974 #endif /* defined(__i386__) */
978 static void reply(int code, int replyee, int process, int status) {
979 message m;
981 m.m_type = code; /* DEV_REVIVE */
982 m.REP_STATUS = status; /* result of device operation */
983 m.REP_ENDPT = process; /* which user made the request */
984 send(replyee, &m);
988 static int io_ctl_length(int io_request) {
989 io_request >>= 16;
990 return io_request & _IOCPARM_MASK;
994 static special_file_t* get_special_file(int minor_dev_nr) {
995 int i;
997 for(i = 0; i < drv.NrOfSpecialFiles; i++) {
998 if(special_file[i].minor_dev_nr == minor_dev_nr) {
999 return &special_file[i];
1003 printf("%s: No subdevice specified for minor device %d!\n",
1004 drv.DriverName, minor_dev_nr);
1006 return NULL;
1009 static void tell_dev(buf, size, pci_bus, pci_dev, pci_func)
1010 vir_bytes buf;
1011 size_t size;
1012 int pci_bus;
1013 int pci_dev;
1014 int pci_func;
1016 int r;
1017 endpoint_t dev_e;
1018 message m;
1020 r= ds_retrieve_label_endpt("amddev", &dev_e);
1021 if (r != OK)
1023 #if 0
1024 printf("tell_dev: ds_retrieve_label_endpt failed for 'amddev': %d\n",
1026 #endif
1027 return;
1030 m.m_type= IOMMU_MAP;
1031 m.m2_i1= pci_bus;
1032 m.m2_i2= pci_dev;
1033 m.m2_i3= pci_func;
1034 m.m2_l1= buf;
1035 m.m2_l2= size;
1037 r= sendrec(dev_e, &m);
1038 if (r != OK)
1040 printf("tell_dev: sendrec to %d failed: %d\n", dev_e, r);
1041 return;
1043 if (m.m_type != OK)
1045 printf("tell_dev: dma map request failed: %d\n", m.m_type);
1046 return;