dspemu.c

Go to the documentation of this file.
00001 /* Gxsm - Gnome X Scanning Microscopy
00002  * universal STM/AFM/SARLS/SPALEED/... controlling and
00003  * data analysis software
00004  * 
00005  * Copyright (C) 1999,2000,2001 Percy Zahl
00006  *
00007  * Authors: Percy Zahl <zahl@users.sf.net>
00008  * additional features: Andreas Klust <klust@users.sf.net>
00009  * WWW Home: http://gxsm.sf.net
00010  *
00011  * This program is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
00024  */
00025 
00026 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 8 c-style: "K&R" -*- */
00027 /*
00028  * DSP Kernel Module
00029  * Master File for all kernel modules / device drivers
00030  * driver type is selected by -DCARD_PCI32, _PC31, _SIM !
00031  *
00032  * Device: /dev/pcdsp 
00033  * look in dsp.h: #define PCDSP_DEVICE "/dev/pcdsp" and ..NAME
00034  *
00035  *#####old making device: mknod -m 666 /dev/pcdsp c xxx 0
00036  *#####for major "xxx" look in log/messages while loading module !
00037  *#####in most cases: xxx = 254
00038  * using devfs now!! => /dev/pcdsps/emu (major=240)
00039  */
00040 
00041 #define PCDSP_VERSION "V0.1 (C) P.Zahl 2000"
00042 
00043 /*
00044  * Compile all versions with make
00045  *
00046  */
00047 
00048 // is defined in Makefile via gcc -D option:
00049 //#define EMULATE_A_SPALEED
00050 //#define EMULATE_DSP_BB_SPALEED
00051 
00052 
00053 #include <linux/config.h>
00054 #include <linux/module.h>
00055 #include <linux/version.h>
00056 #include <linux/kernel.h>
00057 #include <linux/sched.h>
00058 #include <linux/smp_lock.h>
00059 #include <linux/devfs_fs_kernel.h>
00060 
00061 #include <linux/pci.h>
00062 #include <linux/types.h>
00063 #include <linux/major.h>
00064 #include <linux/errno.h>
00065 #include <linux/signal.h>
00066 #include <linux/fcntl.h>
00067 #include <linux/interrupt.h>
00068 #include <linux/devpts_fs.h>
00069 #include <linux/file.h>
00070 #include <linux/console.h>
00071 #include <linux/timer.h>
00072 #include <linux/ctype.h>
00073 #include <linux/kd.h>
00074 #include <linux/mm.h>
00075 #include <linux/string.h>
00076 #include <linux/slab.h>
00077 #include <linux/poll.h>
00078 #include <linux/proc_fs.h>
00079 #include <linux/init.h>
00080 #include <linux/proc_fs.h>
00081 
00082 #include <asm/io.h>
00083 #include <asm/uaccess.h>
00084 #include <asm/system.h>
00085 #include <asm/bitops.h>
00086 
00087 #ifdef __SMP__
00088 #include <asm/pgtable.h>
00089 #include <linux/smp_lock.h>
00090 #endif
00091 
00092 /* kernel_thread */
00093 #define __KERNEL_SYSCALLS__
00094 #include <linux/unistd.h>
00095 
00096 // Dbg Macros
00097 #include "dbgstuff.h"
00098 #include "pcsim.h"
00099 
00100 /*
00101  * Driver Variables 
00102  */
00103 
00104 static devfs_handle_t devfs_handle = NULL;
00105 
00106 static int opened;
00107 
00108 /* Hardware Location */
00109 static char *pcdsp_dprambaseptr;
00110 
00111 /* Process handling... */
00112 static int wakeups = -1;
00113 static DECLARE_WAIT_QUEUE_HEAD(waitq);
00114 
00115 /* Thread Data Structure */
00116 
00117 //static DECLARE_WAIT_QUEUE_HEAD(wq);
00118 //static int khubd_pid = 0;                     /* PID of khubd */
00119 static DECLARE_MUTEX_LOCKED(dspsimd_exited);
00120 
00121 struct dspsim_thread_data {
00122     /* thread data */
00123     int    thread;
00124     wait_queue_head_t wq;
00125     int                  active;
00126 
00127     char *virtual_dpram;
00128     int  SrvReqAck;
00129     int  ReqDAck;
00130 };
00131 
00132 struct dspsim_thread_data dspsim_td;
00133 
00134 #define ACKD_DSP dspsim_td.SrvReqAck
00135 #define REQD_DSP dspsim_td.ReqDAck
00136 
00137 
00138 /*
00139  * Timings in jiffies,  1 jiffie dauert ca. 10ms (HZ = #jiffies fue 1sec)
00140  */
00141 
00142 #define TIMEOUT_TICKS    19      /* 200ms */
00143 #define JIFFIES_SEM      2      /* timeout after 50ms */
00144 #define MAXWAKEUPS_SEM   10
00145 #define MAXWAKEUPS_WMBOX 10
00146 #define MAXWAKEUPS_RMBOX 10
00147 
00148 /* Prototypes */
00149 
00150 static void timeout(unsigned long ignore);
00151 void mysleep(unsigned long myjiffies);
00152 
00153 int  InitEmu(struct dspsim_thread_data *dsp);
00154 void ExitEmu(void);
00155 void ServiceRequest(struct dspsim_thread_data *dsp);
00156 
00157 int BoxFull(int wait);
00158 int BoxEmpty(int wait);
00159 int ChkBoxEmpty(int wait);
00160 int WriteBox(unsigned long data, int wait);
00161 int ChkBoxFull(int wait);
00162 int ReadBox(unsigned long *data, int wait);
00163 
00164 /* Device Service Functions */
00165 static loff_t pcdsp_seek (struct file *, loff_t, int);
00166 static ssize_t pcdsp_read (struct file *, char *, size_t, loff_t *);
00167 static ssize_t pcdsp_write (struct file *, const char *, size_t, loff_t *);
00168 static int pcdsp_release (struct inode *, struct file *);
00169 static int pcdsp_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
00170 
00171 
00172 int pcdsp_initialize(void);
00173 inline void pcdsp_quit(void);
00174 
00175 int init_module(void);
00176 void cleanup_module(void);
00177 
00178 /*
00179  * DSP Simulation -- Emulation of DSP Program
00180  */
00181 
00182 static int dspsim_thread(void *data);
00183 void start_dsp(struct dspsim_thread_data *dsp);
00184 void stop_dsp(struct dspsim_thread_data *dsp);
00185 
00186 #ifdef EMULATE_A_SPALEED
00187 # include "spaleed_emu.c"
00188 #elif EMULATE_DSP_BB_SPALEED
00189 # include "spaleed_bb.c"
00190 #elif EMULATE_DSP_SPM
00191 # include "spm_emu.c"
00192 #else
00193 
00194 // EmuDummy
00195 int  InitEmu(struct dspsim_thread_data *dsp){ return 0; }
00196 void ExitEmu(){}
00197 void ServiceRequest(struct dspsim_thread_data *dsp){
00198     switch(((unsigned char) *dsp->virtual_dpram) & 0xff){
00199     case DSP_CMD_xxxxx: 
00200       ACKD_DSP = TRUE;
00201       break;
00202     default: 
00203       ACKD_DSP = TRUE;
00204       break;
00205     }
00206     REQD_DSP   = FALSE;
00207 }
00208 
00209 #endif
00210 
00211 /*
00212  * A kernel thread for simulation a missing DSP (TMS320) card
00213  * -- we don't want to block the in the ioctl while doing the 
00214  * data aquisitaion
00215  */
00216 
00217 static int dspsim_thread(void *data)
00218 {
00219     struct dspsim_thread_data *dsp = data;
00220     
00221     lock_kernel();
00222     daemonize();
00223 
00224     strcpy(current->comm,"dspemud");
00225 
00226     dsp->active = -1;
00227 
00228     KDEBUG("DSP Simulating daemon: started\n");
00229                 
00230     do {
00231         dsp->active = 0;
00232         interruptible_sleep_on(&dsp->wq);
00233         dsp->active = 1;
00234         KDEBUG_L3("DSP Simulating daemon: wakeup\n");
00235         ServiceRequest(dsp);
00236     } while (!signal_pending(current));
00237 
00238     KDEBUG("DSP Simulating thread: exit\n");
00239     dsp->active = -1;
00240 
00241 /*    up_and_exit(&dspsimd_exited, 0); */
00242     up(&dspsimd_exited);
00243     return 0;
00244 }
00245 
00246 void start_dsp(struct dspsim_thread_data *dsp){
00247   /* startup control thread */
00248   int pid;
00249     init_waitqueue_head(&dsp->wq);
00250     dsp->active = -1;
00251     dsp->thread = -1;
00252     pid = kernel_thread(dspsim_thread, (void *)dsp, 
00253                         CLONE_FS | CLONE_FILES | CLONE_SIGHAND );
00254     if( pid >= 0 ){
00255       dsp->thread = pid;
00256       KDEBUG("DSP thread started\n");
00257       return;
00258     }
00259     KDEBUG("Error starting DSP thread!\n");
00260     // Error...
00261 }
00262 
00263 void stop_dsp(struct dspsim_thread_data *dsp){
00264     /* shutdown control thread */
00265   if (dsp->thread >= 0){ 
00266       KDEBUG("Sending SIGTERM to DSP Thread.\n");
00267       kill_proc( dsp->thread, SIGTERM, 1);
00268 
00269       // wait until thread has finished
00270       KDEBUG("waiting until thread has finished...\n");
00271       while(dsp->active >= 0){
00272         mysleep(5);
00273         if(dsp->active >= 0)
00274           KDEBUG("still waiting\n");
00275       }
00276       return;
00277   }
00278   KDEBUG("Error: no DSP thread to terminate!\n");
00279 }
00280 
00281 
00282 static void timeout(unsigned long ignore){
00283   KDEBUG_L3("tmout wakeups %d\n", wakeups);
00284   wake_up_interruptible(&waitq);
00285 }
00286 
00287 #define MAKE_MY_TIMEOUT_TMR(tmr) \
00288   struct timer_list tmr; \
00289   init_timer(&tmr); \
00290   tmr.function = timeout; \
00291   tmr.data = 0
00292 
00293 
00294 void mysleep(unsigned long myjiffies){
00295   MAKE_MY_TIMEOUT_TMR(timeout_tmr);
00296 
00297   del_timer(&timeout_tmr);
00298   timeout_tmr.expires = jiffies + myjiffies;
00299   add_timer(&timeout_tmr);
00300   interruptible_sleep_on(&waitq);
00301 }
00302 
00303 /*
00304  * DSP Simulation -- Emulation of DSP, DPRAM, MAILBOX
00305  */
00306 
00307 int BoxFull(int wait){
00308   MAKE_MY_TIMEOUT_TMR(timeout_tmr);
00309 
00310   KDEBUG_L2("BoxFull ?\n");
00311   wakeups=0;
00312   while(!(ACKD_DSP) && wakeups < MAXWAKEUPS_RMBOX){
00313       if(!wait)
00314           return FALSE;
00315       del_timer(&timeout_tmr);
00316       timeout_tmr.expires = jiffies + TIMEOUT_TICKS;
00317       add_timer(&timeout_tmr);
00318       interruptible_sleep_on(&waitq);
00319       wakeups++;
00320   }
00321   KDEBUG_L2("BoxFull wakeups %d\n",wakeups);
00322   if(wakeups == MAXWAKEUPS_WMBOX){
00323       KDEBUG_L2("BoxFull timeout\n");
00324       return FALSE;
00325   }
00326   KDEBUG_L2("BoxFull OK\n");
00327   return TRUE;
00328 }
00329 
00330 int BoxEmpty(int wait){
00331   MAKE_MY_TIMEOUT_TMR(timeout_tmr);
00332 
00333   KDEBUG_L2("BoxEmpty ?\n");
00334   wakeups=0;
00335   while(REQD_DSP && wakeups < MAXWAKEUPS_RMBOX){
00336       if(!wait)
00337           return FALSE;
00338       del_timer(&timeout_tmr);
00339       timeout_tmr.expires = jiffies + TIMEOUT_TICKS;
00340       add_timer(&timeout_tmr);
00341       interruptible_sleep_on(&waitq);
00342       wakeups++;
00343   }
00344   KDEBUG_L2("BoxEmpty wakeups %d\n",wakeups);
00345   if(wakeups == MAXWAKEUPS_WMBOX){
00346       KDEBUG_L2("BoxEmpty timeout\n");
00347       return FALSE;
00348   }
00349   KDEBUG_L2("BoxEmpty OK\n");
00350   return TRUE;
00351 }
00352 
00353 int ChkBoxEmpty(int wait){
00354     if(BoxEmpty(wait)){
00355         return TRUE;
00356     }
00357     return FALSE;
00358 }
00359 
00360 int WriteBox(unsigned long data, int wait){
00361     if(BoxEmpty(wait)){
00362         if( dspsim_td.active ){
00363             KDEBUG("Warning: WriteBox called, but dspemu is busy, ignoreing this call!");
00364             return FALSE;
00365         }else{
00366             REQD_DSP   = TRUE;
00367             ACKD_DSP = FALSE;
00368             wake_up_interruptible(&dspsim_td.wq);
00369             return TRUE;
00370         }
00371     }
00372     return FALSE;
00373 }
00374 
00375 int ChkBoxFull(int wait){
00376     if(BoxFull(wait)){
00377         return TRUE;
00378     }
00379     return FALSE;
00380 }
00381 
00382 int ReadBox(unsigned long *data, int wait){
00383     if(BoxFull(wait)){
00384         *data = 0;
00385         // ACK;
00386         return TRUE;
00387     }
00388     return FALSE;
00389 }
00390 
00391 
00392 /*
00393  * Driver FS-Functionality Implementation
00394  */
00395 
00396 /* 
00397   defined in /usr/include/unistd.h, but trouble to include it here..., 
00398   so I define it myself here.
00399   If someone can fix this, I'll be happy!
00400 */
00401 
00402 #define SEEK_SET        0       /* Seek from beginning of file.  */
00403 #define SEEK_CUR        1       /* Seek from current position.  */
00404 #define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
00405 
00406 
00407 static loff_t pcdsp_seek(struct file *f, loff_t offset, int orig)
00408 {
00409   switch(orig){
00410   case SEEK_SET: f->f_pos  = offset; break;
00411   case SEEK_CUR: f->f_pos += offset; break;
00412   case SEEK_END: return EBADF;
00413   }
00414   if(   f->f_pos < 0 
00415      || f->f_pos >= PCDSP_DPRAM_SIZE)
00416     f->f_pos = 0;
00417 
00418   KDEBUG("seek to: %04x\n", (int)(f->f_pos));
00419   return f->f_pos;
00420 }
00421 
00422 /* Read Data from DSP */
00423 static ssize_t pcdsp_read(struct file *f, char *buf, size_t count, loff_t *ppos)
00424 {
00425     if( ((unsigned int)f->f_pos + count) < PCDSP_DPRAM_SIZE){
00426         KDEBUG_L2("%d bytes at %x\n", count, f->f_pos);
00427         copy_to_user(buf, pcdsp_dprambaseptr + (unsigned int)f->f_pos, count);
00428         return 0;
00429     }
00430     return -EINVAL;
00431 }
00432 
00433 /* Write Data to DSP */
00434 static ssize_t pcdsp_write(struct file *f, const char *buf, size_t count, loff_t *ppos)
00435 {
00436     if( ((unsigned int)f->f_pos + count) < PCDSP_DPRAM_SIZE){
00437         KDEBUG_L2("write %d bytes at %x\n", count, f->f_pos);
00438         copy_from_user(pcdsp_dprambaseptr + (unsigned int)f->f_pos, buf, count);
00439         return 0;
00440     }
00441     return -EINVAL;
00442 }
00443 
00444 static int pcdsp_open(struct inode *inode, struct file *f){
00445     MOD_INC_USE_COUNT;
00446     opened++;  
00447     f->f_pos=0;
00448     KDEBUG("Opened %d times\n",opened);
00449     return 0;
00450 }
00451 
00452 static int pcdsp_release (struct inode *inode, struct file *f ){
00453     KDEBUG("release, still open:%d\n", opened-1);
00454     MOD_DEC_USE_COUNT;
00455     opened--;
00456     return 0;
00457 }
00458 
00459 static int pcdsp_ioctl(struct inode *inode, struct file *f, unsigned int cmd, unsigned long arg){
00460     //  KDEBUG("pcdsp_ioctl::%d\n", cmd);
00461   switch(cmd){
00462   case PCDSP_MBOX_FULL:
00463     return ChkBoxFull((int)arg);
00464 
00465   case PCDSP_MBOX_READ_WAIT: 
00466     ReadBox(&arg, TRUE);
00467     return (int)(arg&0x00ffffff);
00468   case PCDSP_MBOX_READ_NOWAIT: 
00469     ReadBox(&arg, FALSE);
00470     return (int)(arg&0x00ffffff);
00471 
00472   case PCDSP_MBOX_EMPTY:
00473     return ChkBoxEmpty((int)arg);
00474   case PCDSP_MBOX_WRITE_WAIT: 
00475     return WriteBox(arg, TRUE);
00476   case PCDSP_MBOX_WRITE_NOWAIT: 
00477     return WriteBox(arg, FALSE);
00478 
00479   case PCDSP_PUT_SPEED: 
00480       return 0;
00481   case PCDSP_RESET:
00482     KDEBUG("reset\n");
00483     return 0;
00484   case PCDSP_HALT:   
00485     KDEBUG("halt\n");
00486     return 0;
00487   case PCDSP_RUN:   
00488     KDEBUG("run\n");
00489     return 0;
00490 
00491   case PCDSP_GETMODID:
00492     return MODID;
00493 
00494   default: 
00495     return -1;
00496   }
00497   return 0;
00498 }
00499 
00500 /*
00501  * init some module variables
00502  */
00503 int pcdsp_initialize(){
00504   opened=0;
00505 
00506   dspsim_td.SrvReqAck=FALSE;
00507   dspsim_td.virtual_dpram=NULL;
00508 
00509   // check Memory availability
00510   dspsim_td.virtual_dpram = (void *)kmalloc (PCDSP_DPRAM_SIZE, GFP_KERNEL);
00511   if (dspsim_td.virtual_dpram == NULL){
00512     KDEBUG("Could not allocate Memory !\n");
00513     return 1;
00514   }
00515 
00516   memset(dspsim_td.virtual_dpram, 0, PCDSP_DPRAM_SIZE);
00517   pcdsp_dprambaseptr = dspsim_td.virtual_dpram;
00518 
00519   KDEBUG_L1("Driver %s\n", PCDSP_VERSION);
00520 
00521   start_dsp(&dspsim_td);
00522   dspsim_td.SrvReqAck=TRUE;
00523   dspsim_td.ReqDAck  =FALSE;
00524 
00525   if( InitEmu(&dspsim_td) )
00526       return 1;
00527 
00528 
00529   return 0;
00530 }
00531 
00532 inline void pcdsp_quit(){
00533 
00534   ExitEmu();
00535 
00536   stop_dsp(&dspsim_td);
00537 
00538   kfree(dspsim_td.virtual_dpram);
00539   dspsim_td.virtual_dpram=NULL;
00540   KDEBUG("Simulation Driver deinstalled\n");
00541 }
00542 
00543 int pcdsp_read_proc(char *page, 
00544                     char **start, 
00545                     off_t off, 
00546                     int count, 
00547                     int *eof,
00548                     void *data)
00549 {
00550   int len;  /* The number of bytes actually used */
00551 
00552   /* This is static so it will still be in memory 
00553    * when we leave this function */
00554   static char pcdsp_proc_buffer[256];  
00555 
00556   /* We give all of our information in one go, so if the 
00557    * user asks us if we have more information the 
00558    * answer should always be no. 
00559    *
00560    * This is important because the standard read 
00561    * function from the library would continue to issue 
00562    * the read system call until the kernel replies
00563    * that it has no more information, or until its 
00564    * buffer is filled.
00565    */
00566   if (off > 0)
00567     return 0;
00568 
00569   /* Fill the buffer and get its length */
00570   len = sprintf(pcdsp_proc_buffer, 
00571                 "PCDSP simulation module status:\n"
00572                 "opened: %d\n"
00573                 "LCD: %16s\n"
00574                 "LCD: %16s\n", 
00575                 opened,
00576                 dspsim_td.virtual_dpram+4*DSP_LCDBUFFER,
00577                 dspsim_td.virtual_dpram+4*DSP_LCDBUFFER+16
00578           );
00579 
00580 
00581   /* Tell the function which called us where the 
00582    * buffer is */
00583   *start = pcdsp_proc_buffer;
00584 
00585   /* Return the length */
00586   return len;
00587 }
00588 
00589 
00590 
00591 
00592 /*
00593  * And now module code and kernel interface.
00594  */
00595 /*
00596 struct file_operations {
00597         loff_t (*llseek) (struct file *, loff_t, int);
00598         ssize_t (*read) (struct file *, char *, size_t, loff_t *);
00599         ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
00600         int (*readdir) (struct file *, void *, filldir_t);
00601         unsigned int (*poll) (struct file *, struct poll_table_struct *);
00602         int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
00603         int (*mmap) (struct file *, struct vm_area_struct *);
00604         int (*open) (struct inode *, struct file *);
00605         int (*flush) (struct file *);
00606         int (*release) (struct inode *, struct file *);
00607         int (*fsync) (struct file *, struct dentry *);
00608         int (*fasync) (int, struct file *, int);
00609         int (*check_media_change) (kdev_t dev);
00610         int (*revalidate) (kdev_t dev);
00611         int (*lock) (struct file *, int, struct file_lock *);
00612 };
00613 */
00614 
00615 struct file_operations pcdsp_fops = {
00616   llseek:    pcdsp_seek,   /* Set DPRAM R/W Position */
00617   read:    pcdsp_read,   /* Read Data to DPRAM */
00618   write:   pcdsp_write,  /* Write Data to DPRAM */
00619   //  NULL,         /* readdir */
00620   //  NULL,         /* poll */
00621   ioctl:   pcdsp_ioctl,  /* ioctl */
00622   //  NULL,         /* mmap */
00623   open:    pcdsp_open,   /* open */
00624   //  NULL,         /* flush */
00625   release: pcdsp_release,/* release */
00626   //  NULL,         /* fsync */
00627   //  NULL,         /* fasync */
00628   //  NULL,         /* check_media_change */
00629   //  NULL,         /* revalidate */
00630   //  NULL          /* lock */
00631 };
00632 
00633 int init_module(void){
00634         struct proc_dir_entry *ent;
00635 
00636         KDEBUG("init_module PCDSP:\n");
00637         //    PCDSP_MAJOR, ...
00638         if ( devfs_register_chrdev (PCDSP_MAJOR, PCDSP_DEVICE_NAME, &pcdsp_fops) ){
00639                 KDEBUG("unable create devfs entry for pcdsp device: %s, major: %d\n", PCDSP_DEVICE_NAME, PCDSP_MAJOR);
00640                 return -EIO;
00641         }
00642         KDEBUG("Major number :%i \n", PCDSP_MAJOR);
00643 
00644         devfs_handle = devfs_mk_dir (NULL, PCDSP_DEVFS_DIR, NULL);
00645 
00646         devfs_register (devfs_handle, PCDSP_DEVICE_NAME,
00647                         DEVFS_FL_DEFAULT, PCDSP_MAJOR, 0,
00648                         S_IFCHR | S_IRUGO | S_IWUGO,
00649                         &pcdsp_fops, NULL);
00650 
00651         if(pcdsp_initialize())
00652                 return -1; /* some error occured */
00653 
00654         if ((ent = create_proc_entry("pcdsp", S_IRUGO | S_IWUSR, NULL)) != NULL) {
00655                 ent->read_proc = pcdsp_read_proc;
00656 //              ent->write_proc = pcdsp_write_proc;
00657         }
00658     
00659         KDEBUG("init_module PCDSP done.\n");
00660 
00661         return 0;
00662 }
00663 
00664 void cleanup_module(void){
00665     KDEBUG("Module PCDSP cleanup:\n");
00666     pcdsp_quit();
00667 
00668     devfs_unregister (devfs_handle);
00669     devfs_unregister_chrdev(PCDSP_MAJOR, PCDSP_DEVICE_NAME);
00670 
00671     remove_proc_entry("pcdsp", NULL);
00672 
00673     KDEBUG("Module PCDSP unregistered.\n");
00674 }

Generated on Sat Apr 1 09:04:16 2006 for GXSM by  doxygen 1.4.6