minix/lib/libaudiodriver/audio_fw.c
Ben Gras 50e2064049 No more intel/minix segments.
This commit removes all traces of Minix segments (the text/data/stack
memory map abstraction in the kernel) and significance of Intel segments
(hardware segments like CS, DS that add offsets to all addressing before
page table translation). This ultimately simplifies the memory layout
and addressing and makes the same layout possible on non-Intel
architectures.

There are only two types of addresses in the world now: virtual
and physical; even the kernel and processes have the same virtual
address space. Kernel and user processes can be distinguished at a
glance as processes won't use 0xF0000000 and above.

No static pre-allocated memory sizes exist any more.

Changes to booting:
        . The pre_init.c leaves the kernel and modules exactly as
          they were left by the bootloader in physical memory
        . The kernel starts running using physical addressing,
          loaded at a fixed location given in its linker script by the
          bootloader.  All code and data in this phase are linked to
          this fixed low location.
        . It makes a bootstrap pagetable to map itself to a
          fixed high location (also in linker script) and jumps to
          the high address. All code and data then use this high addressing.
        . All code/data symbols linked at the low addresses is prefixed by
          an objcopy step with __k_unpaged_*, so that that code cannot
          reference highly-linked symbols (which aren't valid yet) or vice
          versa (symbols that aren't valid any more).
        . The two addressing modes are separated in the linker script by
          collecting the unpaged_*.o objects and linking them with low
          addresses, and linking the rest high. Some objects are linked
          twice, once low and once high.
        . The bootstrap phase passes a lot of information (e.g. free memory
          list, physical location of the modules, etc.) using the kinfo
          struct.
        . After this bootstrap the low-linked part is freed.
        . The kernel maps in VM into the bootstrap page table so that VM can
          begin executing. Its first job is to make page tables for all other
          boot processes. So VM runs before RS, and RS gets a fully dynamic,
          VM-managed address space. VM gets its privilege info from RS as usual
          but that happens after RS starts running.
        . Both the kernel loading VM and VM organizing boot processes happen
	  using the libexec logic. This removes the last reason for VM to
	  still know much about exec() and vm/exec.c is gone.

Further Implementation:
        . All segments are based at 0 and have a 4 GB limit.
        . The kernel is mapped in at the top of the virtual address
          space so as not to constrain the user processes.
        . Processes do not use segments from the LDT at all; there are
          no segments in the LDT any more, so no LLDT is needed.
        . The Minix segments T/D/S are gone and so none of the
          user-space or in-kernel copy functions use them. The copy
          functions use a process endpoint of NONE to realize it's
          a physical address, virtual otherwise.
        . The umap call only makes sense to translate a virtual address
          to a physical address now.
        . Segments-related calls like newmap and alloc_segments are gone.
        . All segments-related translation in VM is gone (vir2map etc).
        . Initialization in VM is simpler as no moving around is necessary.
        . VM and all other boot processes can be linked wherever they wish
          and will be mapped in at the right location by the kernel and VM
          respectively.

Other changes:
        . The multiboot code is less special: it does not use mb_print
          for its diagnostics any more but uses printf() as normal, saving
          the output into the diagnostics buffer, only printing to the
          screen using the direct print functions if a panic() occurs.
        . The multiboot code uses the flexible 'free memory map list'
          style to receive the list of free memory if available.
        . The kernel determines the memory layout of the processes to
          a degree: it tells VM where the kernel starts and ends and
          where the kernel wants the top of the process to be. VM then
          uses this entire range, i.e. the stack is right at the top,
          and mmap()ped bits of memory are placed below that downwards,
          and the break grows upwards.

Other Consequences:
        . Every process gets its own page table as address spaces
          can't be separated any more by segments.
        . As all segments are 0-based, there is no distinction between
          virtual and linear addresses, nor between userspace and
          kernel addresses.
        . Less work is done when context switching, leading to a net
          performance increase. (8% faster on my machine for 'make servers'.)
	. The layout and configuration of the GDT makes sysenter and syscall
	  possible.
2012-07-15 22:30:15 +02:00

1035 lines
29 KiB
C

/* Best viewed with tabsize 4
*
* This file contains a standard driver for audio devices.
* It supports double dma buffering and can be configured to use
* extra buffer space beside the dma buffer.
* This driver also support sub devices, which can be independently
* opened and closed.
*
* The driver supports the following operations:
*
* m_type DEVICE USER_ENDPT COUNT POSITION ADRRESS
* -----------------------------------------------------------------
* | DEV_OPEN | device | proc nr | | | |
* |-------------+---------+---------+---------+---------+---------|
* | DEV_CLOSE | device | proc nr | | | |
* |-------------+---------+---------+---------+---------+---------|
* | DEV_READ_S | device | proc nr | bytes | | buf ptr |
* |-------------+---------+---------+---------+---------+---------|
* | DEV_WRITE_S | device | proc nr | bytes | | buf ptr |
* |-------------+---------+---------+---------+---------+---------|
* | DEV_IOCTL_S | device | proc nr |func code| | buf ptr |
* |-------------+---------+---------+---------+---------+---------|
* | DEV_STATUS | | | | | |
* |-------------+---------+---------+---------+---------+---------|
* | HARD_INT | | | | | |
* |-------------+---------+---------+---------+---------+---------|
* | SIG_STOP | | | | | |
* -----------------------------------------------------------------
*
* The file contains one entry point:
*
* main: main entry when driver is brought up
*
* October 2007 Updated audio framework to work with mplayer, added
* savecopies (Pieter Hijma)
* February 2006 Updated audio framework,
* changed driver-framework relation (Peter Boonstoppel)
* November 2005 Created generic DMA driver framework (Laurens Bronwasser)
* August 24 2005 Ported audio driver to user space
* (only audio playback) (Peter Boonstoppel)
* May 20 1995 SB16 Driver: Michel R. Prevenier
*/
#include <minix/audio_fw.h>
#include <minix/endpoint.h>
#include <minix/ds.h>
static int msg_open(int minor_dev_nr);
static int msg_close(int minor_dev_nr);
static int msg_ioctl(const message *m_ptr);
static void msg_write(const message *m_ptr);
static void msg_read(message *m_ptr);
static void msg_hardware(void);
static void msg_status(message *m_ptr);
static int init_driver(void);
static int open_sub_dev(int sub_dev_nr, int operation);
static int close_sub_dev(int sub_dev_nr);
static void handle_int_write(int sub_dev_nr);
static void handle_int_read(int sub_dev_nr);
static void data_to_user(sub_dev_t *sub_dev_ptr);
static void data_from_user(sub_dev_t *sub_dev_ptr);
static int init_buffers(sub_dev_t *sub_dev_ptr);
static int get_started(sub_dev_t *sub_dev_ptr);
static void reply(int code, int replyee, int process,int status);
static int io_ctl_length(int io_request);
static special_file_t* get_special_file(int minor_dev_nr);
static void tell_dev(vir_bytes buf, size_t size, int pci_bus, int
pci_dev, int pci_func);
static char io_ctl_buf[_IOCPARM_MASK];
static int irq_hook_id = 0; /* id of irq hook at the kernel */
static int irq_hook_set = FALSE;
static int device_available = 0;/*todo*/
/* SEF functions and variables. */
static void sef_local_startup(void);
static int sef_cb_init_fresh(int type, sef_init_info_t *info);
static void sef_cb_signal_handler(int signo);
EXTERN int sef_cb_lu_prepare(int state);
EXTERN int sef_cb_lu_state_isvalid(int state);
EXTERN void sef_cb_lu_state_dump(int state);
int is_status_msg_expected = FALSE;
int main(void)
{
int r, caller;
message mess, repl_mess;
int ipc_status;
/* SEF local startup. */
sef_local_startup();
/* Here is the main loop of the dma driver. It waits for a message,
carries it out, and sends a reply. */
while(1) {
if(driver_receive(ANY, &mess, &ipc_status) != OK) {
panic("driver_receive failed");
}
caller = mess.m_source;
/* Now carry out the work. First check for notifications. */
if (is_ipc_notify(ipc_status)) {
switch (_ENDPOINT_P(mess.m_source)) {
case HARDWARE:
msg_hardware();
break;
default:
printf("%s: %d uncaught notify!\n",
drv.DriverName, mess.m_type);
}
/* get next message */
continue;
}
/* Normal messages. */
switch(mess.m_type) {
case DEV_OPEN:
/* open the special file ( = parameter) */
r = msg_open(mess.DEVICE);
repl_mess.m_type = DEV_REVIVE;
repl_mess.REP_ENDPT = mess.USER_ENDPT;
repl_mess.REP_STATUS = r;
send(caller, &repl_mess);
continue;
case DEV_CLOSE:
/* close the special file ( = parameter) */
r = msg_close(mess.DEVICE);
repl_mess.m_type = DEV_CLOSE_REPL;
repl_mess.REP_ENDPT = mess.USER_ENDPT;
repl_mess.REP_STATUS = r;
send(caller, &repl_mess);
continue;
case DEV_IOCTL_S:
r = msg_ioctl(&mess);
if (r != SUSPEND)
{
repl_mess.m_type = DEV_REVIVE;
repl_mess.REP_ENDPT = mess.USER_ENDPT;
repl_mess.REP_IO_GRANT =
(unsigned)mess.IO_GRANT;
repl_mess.REP_STATUS = r;
send(caller, &repl_mess);
}
continue;
case DEV_READ_S:
msg_read(&mess); continue; /* don't reply */
case DEV_WRITE_S:
msg_write(&mess); continue; /* don't reply */
case DEV_STATUS:
msg_status(&mess);continue; /* don't reply */
case DEV_REOPEN:
/* reopen the special file ( = parameter) */
r = msg_open(mess.DEVICE);
repl_mess.m_type = DEV_REOPEN_REPL;
repl_mess.REP_ENDPT = mess.USER_ENDPT;
repl_mess.REP_STATUS = r;
send(caller, &repl_mess);
continue;
default:
printf("%s: %d uncaught msg!\n",
drv.DriverName, mess.m_type);
continue;
}
/* Should not be here. Just continue. */
}
return 1;
}
/*===========================================================================*
* sef_local_startup *
*===========================================================================*/
static void sef_local_startup()
{
/* Register init callbacks. */
sef_setcb_init_fresh(sef_cb_init_fresh);
sef_setcb_init_lu(sef_cb_init_fresh);
sef_setcb_init_restart(sef_cb_init_fresh);
/* Register live update callbacks. */
sef_setcb_lu_prepare(sef_cb_lu_prepare);
sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid);
sef_setcb_lu_state_dump(sef_cb_lu_state_dump);
/* Register signal callbacks. */
sef_setcb_signal_handler(sef_cb_signal_handler);
/* Let SEF perform startup. */
sef_startup();
}
/*===========================================================================*
* sef_cb_init_fresh *
*===========================================================================*/
static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
{
/* Initialize the audio driver framework. */
return init_driver();
}
static int init_driver(void) {
u32_t i; char irq;
static int executed = 0;
sub_dev_t* sub_dev_ptr;
/* init variables, get dma buffers */
for (i = 0; i < drv.NrOfSubDevices; i++) {
sub_dev_ptr = &sub_dev[i];
sub_dev_ptr->Opened = FALSE;
sub_dev_ptr->DmaBusy = FALSE;
sub_dev_ptr->DmaMode = NO_DMA;
sub_dev_ptr->DmaReadNext = 0;
sub_dev_ptr->DmaFillNext = 0;
sub_dev_ptr->DmaLength = 0;
sub_dev_ptr->BufReadNext = 0;
sub_dev_ptr->BufFillNext = 0;
sub_dev_ptr->RevivePending = FALSE;
sub_dev_ptr->OutOfData = FALSE;
sub_dev_ptr->Nr = i;
}
/* initialize hardware*/
if (drv_init_hw() != OK) {
printf("%s: Could not initialize hardware\n", drv.DriverName);
return EIO;
}
/* get irq from device driver...*/
if (drv_get_irq(&irq) != OK) {
printf("%s: init driver couldn't get IRQ", drv.DriverName);
return EIO;
}
/* TODO: execute the rest of this function only once
we don't want to set irq policy twice */
if (executed) return OK;
executed = TRUE;
/* ...and register interrupt vector */
if ((i=sys_irqsetpolicy(irq, 0, &irq_hook_id )) != OK){
printf("%s: init driver couldn't set IRQ policy: %d", drv.DriverName, i);
return EIO;
}
irq_hook_set = TRUE; /* now signal handler knows it must unregister policy*/
/* Announce we are up! */
chardriver_announce();
return OK;
}
/*===========================================================================*
* sef_cb_signal_handler *
*===========================================================================*/
static void sef_cb_signal_handler(int signo)
{
int i;
char irq;
/* Only check for termination signal, ignore anything else. */
if (signo != SIGTERM) return;
for (i = 0; i < drv.NrOfSubDevices; i++) {
drv_stop(i); /* stop all sub devices */
}
if (irq_hook_set) {
if (sys_irqdisable(&irq_hook_id) != OK) {
printf("Could not disable IRQ\n");
}
/* get irq from device driver*/
if (drv_get_irq(&irq) != OK) {
printf("Msg SIG_STOP Couldn't get IRQ");
}
/* remove the policy */
if (sys_irqrmpolicy(&irq_hook_id) != OK) {
printf("%s: Could not disable IRQ\n",drv.DriverName);
}
}
}
static int msg_open (int minor_dev_nr) {
int r, read_chan, write_chan, io_ctl;
special_file_t* special_file_ptr;
special_file_ptr = get_special_file(minor_dev_nr);
if(special_file_ptr == NULL) {
return EIO;
}
read_chan = special_file_ptr->read_chan;
write_chan = special_file_ptr->write_chan;
io_ctl = special_file_ptr->io_ctl;
if (read_chan==NO_CHANNEL && write_chan==NO_CHANNEL && io_ctl==NO_CHANNEL) {
printf("%s: No channel specified for minor device %d!\n",
drv.DriverName, minor_dev_nr);
return EIO;
}
if (read_chan == write_chan && read_chan != NO_CHANNEL) {
printf("%s: Read and write channels are equal: %d!\n",
drv.DriverName, minor_dev_nr);
return EIO;
}
/* init driver */
if (!device_available) {
if (init_driver() != OK) {
printf("%s: Couldn't init driver!\n", drv.DriverName);
return EIO;
} else {
device_available = TRUE;
}
}
/* open the sub devices specified in the interface header file */
if (write_chan != NO_CHANNEL) {
/* open sub device for writing */
if (open_sub_dev(write_chan, DEV_WRITE_S) != OK) return EIO;
}
if (read_chan != NO_CHANNEL) {
if (open_sub_dev(read_chan, DEV_READ_S) != OK) return EIO;
}
if (read_chan == io_ctl || write_chan == io_ctl) {
/* io_ctl is already opened because it's the same as read or write */
return OK; /* we're done */
}
if (io_ctl != NO_CHANNEL) { /* Ioctl differs from read/write channels, */
r = open_sub_dev(io_ctl, NO_DMA); /* open it explicitly */
if (r != OK) return EIO;
}
return OK;
}
static int open_sub_dev(int sub_dev_nr, int dma_mode) {
sub_dev_t* sub_dev_ptr;
sub_dev_ptr = &sub_dev[sub_dev_nr];
/* Only one open at a time per sub device */
if (sub_dev_ptr->Opened) {
printf("%s: Sub device %d is already opened\n",
drv.DriverName, sub_dev_nr);
return EBUSY;
}
if (sub_dev_ptr->DmaBusy) {
printf("%s: Sub device %d is still busy\n", drv.DriverName, sub_dev_nr);
return EBUSY;
}
/* Setup variables */
sub_dev_ptr->Opened = TRUE;
sub_dev_ptr->DmaReadNext = 0;
sub_dev_ptr->DmaFillNext = 0;
sub_dev_ptr->DmaLength = 0;
sub_dev_ptr->DmaMode = dma_mode;
sub_dev_ptr->BufReadNext = 0;
sub_dev_ptr->BufFillNext = 0;
sub_dev_ptr->BufLength = 0;
sub_dev_ptr->RevivePending = FALSE;
sub_dev_ptr->OutOfData = TRUE;
/* arrange DMA */
if (dma_mode != NO_DMA) { /* sub device uses DMA */
/* allocate dma buffer and extra buffer space
and configure sub device for dma */
if (init_buffers(sub_dev_ptr) != OK ) return EIO;
}
return OK;
}
static int msg_close(int minor_dev_nr) {
int r, read_chan, write_chan, io_ctl;
special_file_t* special_file_ptr;
special_file_ptr = get_special_file(minor_dev_nr);
if(special_file_ptr == NULL) {
return EIO;
}
read_chan = special_file_ptr->read_chan;
write_chan = special_file_ptr->write_chan;
io_ctl = special_file_ptr->io_ctl;
r= OK;
/* close all sub devices */
if (write_chan != NO_CHANNEL) {
if (close_sub_dev(write_chan) != OK) r = EIO;
}
if (read_chan != NO_CHANNEL) {
if (close_sub_dev(read_chan) != OK) r = EIO;
}
if (read_chan == io_ctl || write_chan == io_ctl) {
/* io_ctl is already closed because it's the same as read or write */
return r; /* we're done */
}
/* ioctl differs from read/write channels... */
if (io_ctl != NO_CHANNEL) {
if (close_sub_dev(io_ctl) != OK) r = EIO; /* ...close it explicitly */
}
return r;
}
static int close_sub_dev(int sub_dev_nr) {
sub_dev_t *sub_dev_ptr;
sub_dev_ptr = &sub_dev[sub_dev_nr];
if (sub_dev_ptr->DmaMode == DEV_WRITE_S && !sub_dev_ptr->OutOfData) {
/* do nothing, still data in buffers that has to be transferred */
sub_dev_ptr->Opened = FALSE; /* keep DMA busy */
return OK;
}
if (sub_dev_ptr->DmaMode == NO_DMA) {
/* do nothing, there is no dma going on */
sub_dev_ptr->Opened = FALSE;
return OK;
}
sub_dev_ptr->Opened = FALSE;
sub_dev_ptr->DmaBusy = FALSE;
/* stop the device */
drv_stop(sub_dev_ptr->Nr);
/* free the buffers */
free(sub_dev_ptr->DmaBuf);
free(sub_dev_ptr->ExtraBuf);
return OK;
}
static int msg_ioctl(const message *m_ptr)
{
int status, len, chan;
sub_dev_t *sub_dev_ptr;
special_file_t* special_file_ptr;
special_file_ptr = get_special_file(m_ptr->DEVICE);
if(special_file_ptr == NULL) {
return EIO;
}
chan = special_file_ptr->io_ctl;
if (chan == NO_CHANNEL) {
printf("%s: No io control channel specified!\n", drv.DriverName);
return EIO;
}
/* get pointer to sub device data */
sub_dev_ptr = &sub_dev[chan];
if(!sub_dev_ptr->Opened) {
printf("%s: io control impossible - not opened!\n", drv.DriverName);
return EIO;
}
/* this is a hack...todo: may we intercept reset calls? */
/*
if(m_ptr->REQUEST == DSPIORESET) {
device_available = FALSE;
}
*/
if (m_ptr->REQUEST & _IOC_IN) { /* if there is data for us, copy it */
len = io_ctl_length(m_ptr->REQUEST);
if(sys_safecopyfrom(m_ptr->m_source,
(vir_bytes)m_ptr->ADDRESS, 0,
(vir_bytes)io_ctl_buf, len) != OK) {
printf("%s:%d: safecopyfrom failed\n", __FILE__, __LINE__);
}
}
/* all ioctl's are passed to the device specific part of the driver */
status = drv_io_ctl(m_ptr->REQUEST, (void *)io_ctl_buf, &len, chan);
/* _IOC_OUT bit -> user expects data */
if (status == OK && m_ptr->REQUEST & _IOC_OUT) {
/* copy result back to user */
if(sys_safecopyto(m_ptr->m_source, (vir_bytes)m_ptr->ADDRESS, 0,
(vir_bytes)io_ctl_buf, len) != OK) {
printf("%s:%d: safecopyto failed\n", __FILE__, __LINE__);
}
}
return status;
}
static void msg_write(const message *m_ptr)
{
int chan; sub_dev_t *sub_dev_ptr;
special_file_t* special_file_ptr;
special_file_ptr = get_special_file(m_ptr->DEVICE);
chan = special_file_ptr->write_chan;
if (chan == NO_CHANNEL) {
printf("%s: No write channel specified!\n", drv.DriverName);
reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EIO);
return;
}
/* get pointer to sub device data */
sub_dev_ptr = &sub_dev[chan];
if (!sub_dev_ptr->DmaBusy) { /* get fragment size on first write */
if (drv_get_frag_size(&(sub_dev_ptr->FragSize), sub_dev_ptr->Nr) != OK){
printf("%s; Failed to get fragment size!\n", drv.DriverName);
return;
}
}
if(m_ptr->COUNT != sub_dev_ptr->FragSize) {
printf("Fragment size does not match user's buffer length\n");
reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EINVAL);
return;
}
/* if we are busy with something else than writing, return EBUSY */
if(sub_dev_ptr->DmaBusy && sub_dev_ptr->DmaMode != DEV_WRITE_S) {
printf("Already busy with something else then writing\n");
reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EBUSY);
return;
}
sub_dev_ptr->RevivePending = TRUE;
sub_dev_ptr->ReviveProcNr = m_ptr->USER_ENDPT;
sub_dev_ptr->ReviveGrant = (cp_grant_id_t) m_ptr->ADDRESS;
sub_dev_ptr->SourceProcNr = m_ptr->m_source;
data_from_user(sub_dev_ptr);
if(!sub_dev_ptr->DmaBusy) { /* Dma tranfer not yet started */
get_started(sub_dev_ptr);
sub_dev_ptr->DmaMode = DEV_WRITE_S; /* Dma mode is writing */
}
}
static void msg_read(message *m_ptr)
{
int chan; sub_dev_t *sub_dev_ptr;
special_file_t* special_file_ptr;
special_file_ptr = get_special_file(m_ptr->DEVICE);
chan = special_file_ptr->read_chan;
if (chan == NO_CHANNEL) {
printf("%s: No read channel specified!\n", drv.DriverName);
reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EIO);
return;
}
/* get pointer to sub device data */
sub_dev_ptr = &sub_dev[chan];
if (!sub_dev_ptr->DmaBusy) { /* get fragment size on first read */
if (drv_get_frag_size(&(sub_dev_ptr->FragSize), sub_dev_ptr->Nr) != OK){
printf("%s: Could not retrieve fragment size!\n", drv.DriverName);
reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT,
EIO);
return;
}
}
if(m_ptr->COUNT != sub_dev_ptr->FragSize) {
reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EINVAL);
printf("fragment size does not match message size\n");
return;
}
/* if we are busy with something else than reading, reply EBUSY */
if(sub_dev_ptr->DmaBusy && sub_dev_ptr->DmaMode != DEV_READ_S) {
reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT, EBUSY);
return;
}
sub_dev_ptr->RevivePending = TRUE;
sub_dev_ptr->ReviveProcNr = m_ptr->USER_ENDPT;
sub_dev_ptr->ReviveGrant = (cp_grant_id_t) m_ptr->ADDRESS;
sub_dev_ptr->SourceProcNr = m_ptr->m_source;
if(!sub_dev_ptr->DmaBusy) { /* Dma tranfer not yet started */
get_started(sub_dev_ptr);
sub_dev_ptr->DmaMode = DEV_READ_S; /* Dma mode is reading */
return; /* no need to get data from DMA buffer at this point */
}
/* check if data is available and possibly fill user's buffer */
data_to_user(sub_dev_ptr);
}
static void msg_hardware(void) {
u32_t i;
/* while we have an interrupt */
while ( drv_int_sum()) {
/* loop over all sub devices */
for ( i = 0; i < drv.NrOfSubDevices; i++) {
/* if interrupt from sub device and Dma transfer
was actually busy, take care of business */
if( drv_int(i) && sub_dev[i].DmaBusy ) {
if (sub_dev[i].DmaMode == DEV_WRITE_S) handle_int_write(i);
if (sub_dev[i].DmaMode == DEV_READ_S) handle_int_read(i);
}
}
}
/* As IRQ_REENABLE is not on in sys_irqsetpolicy, we must
* re-enable out interrupt after every interrupt.
*/
if ((sys_irqenable(&irq_hook_id)) != OK) {
printf("%s: msg_hardware: Couldn't enable IRQ\n", drv.DriverName);
}
}
static void msg_status(message *m_ptr)
{
int i;
for (i = 0; i < drv.NrOfSubDevices; i++) {
if(sub_dev[i].ReadyToRevive)
{
m_ptr->m_type = DEV_REVIVE; /* build message */
m_ptr->REP_ENDPT = sub_dev[i].ReviveProcNr;
m_ptr->REP_IO_GRANT = sub_dev[i].ReviveGrant;
m_ptr->REP_STATUS = sub_dev[i].ReviveStatus;
send(m_ptr->m_source, m_ptr); /* send the message */
/* reset variables */
sub_dev[i].ReadyToRevive = FALSE;
sub_dev[i].RevivePending = 0;
is_status_msg_expected = TRUE;
return; /* stop after one mess,
file system will get back for other processes */
}
}
m_ptr->m_type = DEV_NO_STATUS;
m_ptr->REP_STATUS = 0;
send(m_ptr->m_source, m_ptr); /* send DEV_NO_STATUS message */
is_status_msg_expected = FALSE;
}
/* handle interrupt for specified sub device; DmaMode == DEV_WRITE_S*/
static void handle_int_write(int sub_dev_nr)
{
sub_dev_t *sub_dev_ptr;
sub_dev_ptr = &sub_dev[sub_dev_nr];
sub_dev_ptr->DmaReadNext =
(sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
sub_dev_ptr->DmaLength -= 1;
if (sub_dev_ptr->BufLength != 0) { /* Data in extra buf, copy to Dma buf */
memcpy(sub_dev_ptr->DmaPtr +
sub_dev_ptr->DmaFillNext * sub_dev_ptr->FragSize,
sub_dev_ptr->ExtraBuf +
sub_dev_ptr->BufReadNext * sub_dev_ptr->FragSize,
sub_dev_ptr->FragSize);
sub_dev_ptr->BufReadNext =
(sub_dev_ptr->BufReadNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
sub_dev_ptr->DmaFillNext =
(sub_dev_ptr->DmaFillNext + 1) % sub_dev_ptr->NrOfDmaFragments;
sub_dev_ptr->BufLength -= 1;
sub_dev_ptr->DmaLength += 1;
}
/* space became available, possibly copy new data from user */
data_from_user(sub_dev_ptr);
if(sub_dev_ptr->DmaLength == 0) { /* Dma buffer empty, stop Dma transfer */
sub_dev_ptr->OutOfData = TRUE; /* we're out of data */
if (!sub_dev_ptr->Opened) {
close_sub_dev(sub_dev_ptr->Nr);
return;
}
drv_pause(sub_dev_ptr->Nr);
return;
}
/* confirm and reenable interrupt from this sub dev */
drv_reenable_int(sub_dev_nr);
#if 0
/* reenable irq_hook*/
if (sys_irqenable(&irq_hook_id != OK) {
printf("%s Couldn't enable IRQ\n", drv.DriverName);
}
#endif
}
/* handle interrupt for specified sub device; DmaMode == DEV_READ_S */
static void handle_int_read(int sub_dev_nr)
{
sub_dev_t *sub_dev_ptr;
sub_dev_ptr = &sub_dev[sub_dev_nr];
sub_dev_ptr->DmaLength += 1;
sub_dev_ptr->DmaFillNext =
(sub_dev_ptr->DmaFillNext + 1) % sub_dev_ptr->NrOfDmaFragments;
/* possibly copy data to user (if it is waiting for us) */
data_to_user(sub_dev_ptr);
if (sub_dev_ptr->DmaLength == sub_dev_ptr->NrOfDmaFragments) {
/* if dma buffer full */
if (sub_dev_ptr->BufLength == sub_dev_ptr->NrOfExtraBuffers) {
printf("All buffers full, we have a problem.\n");
drv_stop(sub_dev_nr); /* stop the sub device */
sub_dev_ptr->DmaBusy = FALSE;
sub_dev_ptr->ReviveStatus = 0; /* no data for user,
this is a sad story */
sub_dev_ptr->ReadyToRevive = TRUE; /* wake user up */
return;
}
else { /* dma full, still room in extra buf;
copy from dma to extra buf */
memcpy(sub_dev_ptr->ExtraBuf +
sub_dev_ptr->BufFillNext * sub_dev_ptr->FragSize,
sub_dev_ptr->DmaPtr +
sub_dev_ptr->DmaReadNext * sub_dev_ptr->FragSize,
sub_dev_ptr->FragSize);
sub_dev_ptr->DmaLength -= 1;
sub_dev_ptr->DmaReadNext =
(sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
sub_dev_ptr->BufFillNext =
(sub_dev_ptr->BufFillNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
}
}
/* confirm interrupt, and reenable interrupt from this sub dev*/
drv_reenable_int(sub_dev_ptr->Nr);
#if 0
/* reenable irq_hook*/
if (sys_irqenable(&irq_hook_id) != OK) {
printf("%s: Couldn't reenable IRQ", drv.DriverName);
}
#endif
}
static int get_started(sub_dev_t *sub_dev_ptr) {
u32_t i;
/* enable interrupt messages from MINIX */
if ((i=sys_irqenable(&irq_hook_id)) != OK) {
printf("%s: Couldn't enable IRQs: error code %u",drv.DriverName, (unsigned int) i);
return EIO;
}
/* let the lower part of the driver start the device */
if (drv_start(sub_dev_ptr->Nr, sub_dev_ptr->DmaMode) != OK) {
printf("%s: Could not start device %d\n",
drv.DriverName, sub_dev_ptr->Nr);
}
sub_dev_ptr->DmaBusy = TRUE; /* Dma is busy from now on */
sub_dev_ptr->DmaReadNext = 0;
return OK;
}
static void data_from_user(sub_dev_t *subdev)
{
int r;
message m;
if (subdev->DmaLength == subdev->NrOfDmaFragments &&
subdev->BufLength == subdev->NrOfExtraBuffers) return;/* no space */
if (!subdev->RevivePending) return; /* no new data waiting to be copied */
if (subdev->RevivePending &&
subdev->ReadyToRevive) return; /* we already got this data */
if (subdev->DmaLength < subdev->NrOfDmaFragments) { /* room in dma buf */
sys_safecopyfrom(subdev->SourceProcNr,
(vir_bytes)subdev->ReviveGrant, 0,
(vir_bytes)subdev->DmaPtr +
subdev->DmaFillNext * subdev->FragSize,
(phys_bytes)subdev->FragSize);
subdev->DmaLength += 1;
subdev->DmaFillNext =
(subdev->DmaFillNext + 1) % subdev->NrOfDmaFragments;
} else { /* room in extra buf */
sys_safecopyfrom(subdev->SourceProcNr,
(vir_bytes)subdev->ReviveGrant, 0,
(vir_bytes)subdev->ExtraBuf +
subdev->BufFillNext * subdev->FragSize,
(phys_bytes)subdev->FragSize);
subdev->BufLength += 1;
subdev->BufFillNext =
(subdev->BufFillNext + 1) % subdev->NrOfExtraBuffers;
}
if(subdev->OutOfData) { /* if device paused (because of lack of data) */
subdev->OutOfData = FALSE;
drv_reenable_int(subdev->Nr);
/* reenable irq_hook*/
if ((sys_irqenable(&irq_hook_id)) != OK) {
printf("%s: Couldn't enable IRQ", drv.DriverName);
}
drv_resume(subdev->Nr); /* resume resume the sub device */
}
subdev->ReviveStatus = subdev->FragSize;
subdev->ReadyToRevive = TRUE;
m.m_type = DEV_REVIVE; /* build message */
m.REP_ENDPT = subdev->ReviveProcNr;
m.REP_IO_GRANT = subdev->ReviveGrant;
m.REP_STATUS = subdev->ReviveStatus;
r= send(subdev->SourceProcNr, &m); /* send the message */
if (r != OK)
{
printf("audio_fw: send to %d failed: %d\n",
subdev->SourceProcNr, r);
}
/* reset variables */
subdev->ReadyToRevive = FALSE;
subdev->RevivePending = 0;
}
static void data_to_user(sub_dev_t *sub_dev_ptr)
{
int r;
message m;
if (!sub_dev_ptr->RevivePending) return; /* nobody is wating for data */
if (sub_dev_ptr->ReadyToRevive) return;/* we already filled user's buffer */
if (sub_dev_ptr->BufLength == 0 && sub_dev_ptr->DmaLength == 0) return;
/* no data for user */
if(sub_dev_ptr->BufLength != 0) { /* data in extra buffer available */
sys_safecopyto(sub_dev_ptr->SourceProcNr,
(vir_bytes)sub_dev_ptr->ReviveGrant,
0, (vir_bytes)sub_dev_ptr->ExtraBuf +
sub_dev_ptr->BufReadNext * sub_dev_ptr->FragSize,
(phys_bytes)sub_dev_ptr->FragSize);
/* adjust the buffer status variables */
sub_dev_ptr->BufReadNext =
(sub_dev_ptr->BufReadNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
sub_dev_ptr->BufLength -= 1;
} else { /* extra buf empty, but data in dma buf*/
sys_safecopyto(
sub_dev_ptr->SourceProcNr,
(vir_bytes)sub_dev_ptr->ReviveGrant, 0,
(vir_bytes)sub_dev_ptr->DmaPtr +
sub_dev_ptr->DmaReadNext * sub_dev_ptr->FragSize,
(phys_bytes)sub_dev_ptr->FragSize);
/* adjust the buffer status variables */
sub_dev_ptr->DmaReadNext =
(sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
sub_dev_ptr->DmaLength -= 1;
}
sub_dev_ptr->ReviveStatus = sub_dev_ptr->FragSize;
sub_dev_ptr->ReadyToRevive = TRUE;
/* drv_status will send REVIVE mess to FS*/
m.m_type = DEV_REVIVE; /* build message */
m.REP_ENDPT = sub_dev_ptr->ReviveProcNr;
m.REP_IO_GRANT = sub_dev_ptr->ReviveGrant;
m.REP_STATUS = sub_dev_ptr->ReviveStatus;
r= send(sub_dev_ptr->SourceProcNr, &m); /* send the message */
if (r != OK)
{
printf("audio_fw: send to %d failed: %d\n",
sub_dev_ptr->SourceProcNr, r);
}
/* reset variables */
sub_dev_ptr->ReadyToRevive = FALSE;
sub_dev_ptr->RevivePending = 0;
}
static int init_buffers(sub_dev_t *sub_dev_ptr)
{
#if (CHIP == INTEL)
char *base;
size_t size;
unsigned left;
u32_t i;
phys_bytes ph;
/* allocate dma buffer space */
size= sub_dev_ptr->DmaSize + 64 * 1024;
base= alloc_contig(size, AC_ALIGN4K, &ph);
if (!base) {
printf("%s: failed to allocate dma buffer for a channel\n",
drv.DriverName);
return EIO;
}
sub_dev_ptr->DmaBuf= base;
tell_dev((vir_bytes)base, size, 0, 0, 0);
/* allocate extra buffer space */
if (!(sub_dev_ptr->ExtraBuf = malloc(sub_dev_ptr->NrOfExtraBuffers *
sub_dev_ptr->DmaSize /
sub_dev_ptr->NrOfDmaFragments))) {
printf("%s failed to allocate extra buffer for a channel\n",
drv.DriverName);
return EIO;
}
sub_dev_ptr->DmaPtr = sub_dev_ptr->DmaBuf;
i = sys_umap(SELF, VM_D,
(vir_bytes) sub_dev_ptr->DmaBuf,
(phys_bytes) sizeof(sub_dev_ptr->DmaBuf),
&(sub_dev_ptr->DmaPhys));
if (i != OK) {
return EIO;
}
if ((left = dma_bytes_left(sub_dev_ptr->DmaPhys)) <
sub_dev_ptr->DmaSize) {
/* First half of buffer crosses a 64K boundary,
* can't DMA into that */
sub_dev_ptr->DmaPtr += left;
sub_dev_ptr->DmaPhys += left;
}
/* write the physical dma address and size to the device */
drv_set_dma(sub_dev_ptr->DmaPhys,
sub_dev_ptr->DmaSize, sub_dev_ptr->Nr);
return OK;
#else /* CHIP != INTEL */
printf("%s: init_buffers() failed, CHIP != INTEL", drv.DriverName);
return EIO;
#endif /* CHIP == INTEL */
}
static void reply(int code, int replyee, int process, int status) {
message m;
m.m_type = code; /* DEV_REVIVE */
m.REP_STATUS = status; /* result of device operation */
m.REP_ENDPT = process; /* which user made the request */
send(replyee, &m);
}
static int io_ctl_length(int io_request) {
io_request >>= 16;
return io_request & _IOCPARM_MASK;
}
static special_file_t* get_special_file(int minor_dev_nr) {
int i;
for(i = 0; i < drv.NrOfSpecialFiles; i++) {
if(special_file[i].minor_dev_nr == minor_dev_nr) {
return &special_file[i];
}
}
printf("%s: No subdevice specified for minor device %d!\n",
drv.DriverName, minor_dev_nr);
return NULL;
}
static void tell_dev(buf, size, pci_bus, pci_dev, pci_func)
vir_bytes buf;
size_t size;
int pci_bus;
int pci_dev;
int pci_func;
{
int r;
endpoint_t dev_e;
message m;
r= ds_retrieve_label_endpt("amddev", &dev_e);
if (r != OK)
{
#if 0
printf("tell_dev: ds_retrieve_label_endpt failed for 'amddev': %d\n",
r);
#endif
return;
}
m.m_type= IOMMU_MAP;
m.m2_i1= pci_bus;
m.m2_i2= pci_dev;
m.m2_i3= pci_func;
m.m2_l1= buf;
m.m2_l2= size;
r= sendrec(dev_e, &m);
if (r != OK)
{
printf("tell_dev: sendrec to %d failed: %d\n", dev_e, r);
return;
}
if (m.m_type != OK)
{
printf("tell_dev: dma map request failed: %d\n", m.m_type);
return;
}
}