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 /*
00029  * DSP Kernel Module
00030  * Master File for all kernel modules / device drivers
00031  * driver type is selected by -DCARD_PCI32, _PC31, _SIM !
00032  *
00033  * Device: /dev/pcdsp 
00034  * look in dsp.h: #define PCDSP_DEVICE "/dev/pcdsp" and ..NAME
00035  *
00036  * making device: mknod -m 666 /dev/pcdsp c xxx 0
00037  * for major "xxx" look in log/messages while loading module !
00038  * in most cases: xxx = 254
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 
00059 #include <linux/pci.h>
00060 #include <linux/types.h>
00061 #include <linux/major.h>
00062 #include <linux/errno.h>
00063 #include <linux/signal.h>
00064 #include <linux/fcntl.h>
00065 #include <linux/interrupt.h>
00066 #include <linux/devpts_fs.h>
00067 #include <linux/file.h>
00068 #include <linux/console.h>
00069 #include <linux/timer.h>
00070 #include <linux/ctype.h>
00071 #include <linux/kd.h>
00072 #include <linux/mm.h>
00073 #include <linux/string.h>
00074 #include <linux/malloc.h>
00075 #include <linux/poll.h>
00076 #include <linux/proc_fs.h>
00077 #include <linux/init.h>
00078 
00079 #include <asm/io.h>
00080 #include <asm/uaccess.h>
00081 #include <asm/system.h>
00082 #include <asm/bitops.h>
00083 
00084 #ifdef __SMP__
00085 #include <asm/pgtable.h>
00086 #include <linux/smp_lock.h>
00087 #endif
00088 
00089 /* kernel_thread */
00090 #define __KERNEL_SYSCALLS__
00091 #include <linux/unistd.h>
00092 
00093 // Dbg Macros
00094 #include "dbgstuff.h"
00095 #include "pcsim.h"
00096 
00097 /*
00098  * Driver Variables 
00099  */
00100 
00101 static int pcdsp_major;
00102 
00103 static int opened;
00104 
00105 /* Hardware Location */
00106 static char *pcdsp_dprambaseptr;
00107 
00108 /* Process handling... */
00109 static int wakeups = -1;
00110 static struct wait_queue *waitq = NULL;
00111 
00112 /* Thread Data Structure */
00113 struct dspsim_thread_data {
00114     /* thread */
00115     struct task_struct  *thread;
00116     struct wait_queue   *wq;
00117     int                  active, rmmod;
00118 
00119     char *virtual_dpram;
00120     int  SrvReqAck;
00121     int  ReqDAck;
00122 };
00123 
00124 struct dspsim_thread_data dspsim_td;
00125 
00126 #define ACKD_DSP dspsim_td.SrvReqAck
00127 #define REQD_DSP dspsim_td.ReqDAck
00128 
00129 
00130 /*
00131  * Timings in jiffies,  1 jiffie dauert ca. 10ms (HZ = #jiffies fue 1sec)
00132  */
00133 
00134 #define TIMEOUT_TICKS    19      /* 200ms */
00135 #define JIFFIES_SEM      2      /* timeout after 50ms */
00136 #define MAXWAKEUPS_SEM   10
00137 #define MAXWAKEUPS_WMBOX 10
00138 #define MAXWAKEUPS_RMBOX 10
00139 
00140 /* Prototypes */
00141 
00142 static void timeout(unsigned long ignore);
00143 void mysleep(unsigned long myjiffies);
00144 
00145 int  InitEmu(struct dspsim_thread_data *dsp);
00146 void ExitEmu(void);
00147 void ServiceRequest(struct dspsim_thread_data *dsp);
00148 
00149 int BoxFull(int wait);
00150 int BoxEmpty(int wait);
00151 int ChkBoxEmpty(int wait);
00152 int WriteBox(unsigned long data, int wait);
00153 int ChkBoxFull(int wait);
00154 int ReadBox(unsigned long *data, int wait);
00155 
00156 /* Device Service Functions */
00157 static loff_t pcdsp_seek (struct file *, loff_t, int);
00158 static ssize_t pcdsp_read (struct file *, char *, size_t, loff_t *);
00159 static ssize_t pcdsp_write (struct file *, const char *, size_t, loff_t *);
00160 static int pcdsp_release (struct inode *, struct file *);
00161 static int pcdsp_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
00162 
00163 
00164 int pcdsp_initialize(void);
00165 inline void pcdsp_quit(void);
00166 
00167 int init_module(void);
00168 void cleanup_module(void);
00169 
00170 /*
00171  * DSP Simulation -- Emulation of DSP Program
00172  */
00173 
00174 static int dspsim_thread(void *data);
00175 void start_dsp(struct dspsim_thread_data *dsp);
00176 void stop_dsp(struct dspsim_thread_data *dsp);
00177 
00178 #ifdef EMULATE_A_SPALEED
00179 # include "spaleed_emu.c"
00180 #else
00181 # ifdef EMULATE_DSP_BB_SPALEED
00182 #  include "spaleed_bb.c"
00183 # else
00184 
00185 // EmuDummy
00186 int  InitEmu(struct dspsim_thread_data *dsp){ return 0; }
00187 void ExitEmu(){}
00188 void ServiceRequest(struct dspsim_thread_data *dsp){
00189     switch(((unsigned char) *dsp->virtual_dpram) & 0xff){
00190     case DSP_CMD_xxxxx: 
00191       ACKD_DSP = TRUE;
00192       break;
00193     default: 
00194       ACKD_DSP = TRUE;
00195       break;
00196     }
00197     REQD_DSP   = FALSE;
00198 }
00199 
00200 # endif
00201 #endif
00202 
00203 /*
00204  * A kernel thread for simulation a missing DSP (TMS320) card
00205  * -- we don't want to block the in the ioctl while doing the 
00206  * data aquisitaion
00207  */
00208 
00209 static int dspsim_thread(void *data)
00210 {
00211     struct dspsim_thread_data *dsp = data;
00212     
00213 #ifdef __SMP__
00214     lock_kernel();
00215 #endif
00216 
00217     exit_mm(current);
00218     current->session = 1;
00219     current->pgrp = 1;
00220     sigfillset(&current->blocked);
00221     current->fs->umask = 0;
00222     strcpy(current->comm,"dspemu");
00223 
00224     dsp->thread = current;
00225     dsp->active = -1;
00226 
00227 #ifdef __SMP__
00228     unlock_kernel();
00229 #endif
00230 
00231     KDEBUG("DSP Simulating daemon: started\n");
00232                 
00233     for (;;) {
00234         dsp->active = 0;
00235         interruptible_sleep_on(&dsp->wq);
00236         dsp->active = 1;
00237         KDEBUG_L3("DSP Simulating daemon: wakeup\n");
00238         if (dsp->rmmod)
00239             goto done;
00240 
00241         ServiceRequest(dsp);
00242     }
00243 
00244 done:
00245     KDEBUG("DSP Simulating thread: exit\n");
00246     dsp->active = -1;
00247     dsp->thread = NULL;
00248 
00249     return 0;
00250 }
00251 
00252 void start_dsp(struct dspsim_thread_data *dsp){
00253     /* startup control thread */
00254     dsp->wq     = NULL;
00255     dsp->thread = NULL;
00256     dsp->rmmod  = 0;
00257     kernel_thread(dspsim_thread, (void *)dsp, 0);
00258     //    wake_up_interruptible(&dsp->wq);
00259 }
00260 
00261 void stop_dsp(struct dspsim_thread_data *dsp){
00262     /* shutdown control thread */
00263     if (dsp->thread) 
00264         {
00265             // wait while thread is busy
00266             while( dsp->active )
00267                 mysleep(100);
00268             dsp->rmmod = 1;
00269             wake_up_interruptible(&dsp->wq);
00270         }
00271     // wait until thread has finished
00272     KDEBUG("waiting until thread has finished...\n");
00273     while(dsp->thread){
00274         mysleep(5);
00275         if(dsp->thread)
00276             KDEBUG("still waiting\n");
00277     }
00278 }
00279 
00280 
00281 
00282 
00283 
00284 static void timeout(unsigned long ignore){
00285   KDEBUG_L3("tmout wakeups %d\n", wakeups);
00286   wake_up_interruptible(&waitq);
00287 }
00288 
00289 void mysleep(unsigned long myjiffies){
00290   static struct timer_list timeout_tmr = { NULL, NULL, 0, 0, timeout };
00291   del_timer(&timeout_tmr);
00292   timeout_tmr.expires = jiffies + myjiffies;
00293   add_timer(&timeout_tmr);
00294   interruptible_sleep_on(&waitq);
00295 }
00296 
00297 /*
00298  * DSP Simulation -- Emulation of DSP, DPRAM, MAILBOX
00299  */
00300 
00301 int BoxFull(int wait){
00302   static struct timer_list timeout_tmr = { NULL, NULL, 0, 0, timeout };
00303   KDEBUG_L2("BoxFull ?\n");
00304   wakeups=0;
00305   while(!(ACKD_DSP) && wakeups < MAXWAKEUPS_RMBOX){
00306       if(!wait)
00307           return FALSE;
00308       del_timer(&timeout_tmr);
00309       timeout_tmr.expires = jiffies + TIMEOUT_TICKS;
00310       add_timer(&timeout_tmr);
00311       interruptible_sleep_on(&waitq);
00312       wakeups++;
00313   }
00314   KDEBUG_L2("BoxFull wakeups %d\n",wakeups);
00315   if(wakeups == MAXWAKEUPS_WMBOX){
00316       KDEBUG_L2("BoxFull timeout\n");
00317       return FALSE;
00318   }
00319   KDEBUG_L2("BoxFull OK\n");
00320   return TRUE;
00321 }
00322 
00323 int BoxEmpty(int wait){
00324   static struct timer_list timeout_tmr = { NULL, NULL, 0, 0, timeout };
00325   KDEBUG_L2("BoxEmpty ?\n");
00326   wakeups=0;
00327   while(REQD_DSP && wakeups < MAXWAKEUPS_RMBOX){
00328       if(!wait)
00329           return FALSE;
00330       del_timer(&timeout_tmr);
00331       timeout_tmr.expires = jiffies + TIMEOUT_TICKS;
00332       add_timer(&timeout_tmr);
00333       interruptible_sleep_on(&waitq);
00334       wakeups++;
00335   }
00336   KDEBUG_L2("BoxEmpty wakeups %d\n",wakeups);
00337   if(wakeups == MAXWAKEUPS_WMBOX){
00338       KDEBUG_L2("BoxEmpty timeout\n");
00339       return FALSE;
00340   }
00341   KDEBUG_L2("BoxEmpty OK\n");
00342   return TRUE;
00343 }
00344 
00345 int ChkBoxEmpty(int wait){
00346     if(BoxEmpty(wait)){
00347         return TRUE;
00348     }
00349     return FALSE;
00350 }
00351 
00352 int WriteBox(unsigned long data, int wait){
00353     if(BoxEmpty(wait)){
00354         if( dspsim_td.active ){
00355             KDEBUG("Warning: WriteBox called, but dspemu is busy, ignoreing this call!");
00356             return FALSE;
00357         }else{
00358             REQD_DSP   = TRUE;
00359             ACKD_DSP = FALSE;
00360             wake_up_interruptible(&dspsim_td.wq);
00361             return TRUE;
00362         }
00363     }
00364     return FALSE;
00365 }
00366 
00367 int ChkBoxFull(int wait){
00368     if(BoxFull(wait)){
00369         return TRUE;
00370     }
00371     return FALSE;
00372 }
00373 
00374 int ReadBox(unsigned long *data, int wait){
00375     if(BoxFull(wait)){
00376         *data = 0;
00377         // ACK;
00378         return TRUE;
00379     }
00380     return FALSE;
00381 }
00382 
00383 
00384 /*
00385  * Driver FS-Functionality Implementation
00386  */
00387 
00388 /* 
00389   defined in /usr/include/unistd.h, but trouble to include it here..., 
00390   so I define it myself here.
00391   If someone can fix this, I'll be happy!
00392 */
00393 
00394 #define SEEK_SET        0       /* Seek from beginning of file.  */
00395 #define SEEK_CUR        1       /* Seek from current position.  */
00396 #define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
00397 
00398 
00399 static loff_t pcdsp_seek(struct file *f, loff_t offset, int orig)
00400 {
00401   switch(orig){
00402   case SEEK_SET: f->f_pos  = offset; break;
00403   case SEEK_CUR: f->f_pos += offset; break;
00404   case SEEK_END: return EBADF;
00405   }
00406   if(   f->f_pos < 0 
00407      || f->f_pos >= PCDSP_DPRAM_SIZE)
00408     f->f_pos = 0;
00409 
00410   KDEBUG("seek to: %04x\n", (int)(f->f_pos));
00411   return f->f_pos;
00412 }
00413 
00414 /* Read Data from DSP */
00415 static ssize_t pcdsp_read(struct file *f, char *buf, size_t count, loff_t *ppos)
00416 {
00417     if( ((unsigned int)f->f_pos + count) < PCDSP_DPRAM_SIZE){
00418         KDEBUG_L2("%d bytes at %x\n", count, f->f_pos);
00419         copy_to_user(buf, pcdsp_dprambaseptr + (unsigned int)f->f_pos, count);
00420         return 0;
00421     }
00422     return -EINVAL;
00423 }
00424 
00425 /* Write Data to DSP */
00426 static ssize_t pcdsp_write(struct file *f, const char *buf, size_t count, loff_t *ppos)
00427 {
00428     if( ((unsigned int)f->f_pos + count) < PCDSP_DPRAM_SIZE){
00429         KDEBUG_L2("write %d bytes at %x\n", count, f->f_pos);
00430         copy_from_user(pcdsp_dprambaseptr + (unsigned int)f->f_pos, buf, count);
00431         return 0;
00432     }
00433     return -EINVAL;
00434 }
00435 
00436 static int pcdsp_open(struct inode *inode, struct file *f){
00437     MOD_INC_USE_COUNT;
00438     opened++;  
00439     f->f_pos=0;
00440     KDEBUG("Opened %d times\n",opened);
00441     return 0;
00442 }
00443 
00444 static int pcdsp_release (struct inode *inode, struct file *f ){
00445     KDEBUG("release, still open:%d\n", opened-1);
00446     MOD_DEC_USE_COUNT;
00447     opened--;
00448     return 0;
00449 }
00450 
00451 static int pcdsp_ioctl(struct inode *inode, struct file *f, unsigned int cmd, unsigned long arg){
00452     //  KDEBUG("pcdsp_ioctl::%d\n", cmd);
00453   switch(cmd){
00454   case PCDSP_MBOX_FULL:
00455     return ChkBoxFull((int)arg);
00456 
00457   case PCDSP_MBOX_READ_WAIT: 
00458     ReadBox(&arg, TRUE);
00459     return (int)(arg&0x00ffffff);
00460   case PCDSP_MBOX_READ_NOWAIT: 
00461     ReadBox(&arg, FALSE);
00462     return (int)(arg&0x00ffffff);
00463 
00464   case PCDSP_MBOX_EMPTY:
00465     return ChkBoxEmpty((int)arg);
00466   case PCDSP_MBOX_WRITE_WAIT: 
00467     return WriteBox(arg, TRUE);
00468   case PCDSP_MBOX_WRITE_NOWAIT: 
00469     return WriteBox(arg, FALSE);
00470 
00471   case PCDSP_PUT_SPEED: 
00472       return 0;
00473   case PCDSP_RESET:
00474     KDEBUG("reset\n");
00475     return 0;
00476   case PCDSP_HALT:   
00477     KDEBUG("halt\n");
00478     return 0;
00479   case PCDSP_RUN:   
00480     KDEBUG("run\n");
00481     return 0;
00482 
00483   case PCDSP_GETMODID:
00484     return MODID;
00485 
00486   default: 
00487     return -1;
00488   }
00489   return 0;
00490 }
00491 
00492 /*
00493  * init some module variables
00494  */
00495 int pcdsp_initialize(){
00496   opened=0;
00497 
00498   dspsim_td.SrvReqAck=FALSE;
00499   dspsim_td.virtual_dpram=NULL;
00500 
00501   // check Memory availability
00502   dspsim_td.virtual_dpram = (void *)kmalloc (PCDSP_DPRAM_SIZE, GFP_KERNEL);
00503   if (dspsim_td.virtual_dpram == NULL){
00504     KDEBUG("Could not allocate Memory !\n");
00505     return 1;
00506   }
00507 
00508   memset(dspsim_td.virtual_dpram, 0, PCDSP_DPRAM_SIZE);
00509   pcdsp_dprambaseptr = dspsim_td.virtual_dpram;
00510 
00511   KDEBUG_L1("Driver %s\n", PCDSP_VERSION);
00512 
00513   start_dsp(&dspsim_td);
00514   dspsim_td.SrvReqAck=TRUE;
00515   dspsim_td.ReqDAck  =FALSE;
00516 
00517   if( InitEmu(&dspsim_td) )
00518       return 1;
00519 
00520 
00521   return 0;
00522 }
00523 
00524 inline void pcdsp_quit(){
00525 
00526   ExitEmu();
00527 
00528   stop_dsp(&dspsim_td);
00529 
00530   kfree(dspsim_td.virtual_dpram);
00531   dspsim_td.virtual_dpram=NULL;
00532   KDEBUG("Simulation Driver deinstalled\n");
00533 }
00534 
00535 /*
00536  * And now module code and kernel interface.
00537  */
00538 /*
00539 struct file_operations {
00540         loff_t (*llseek) (struct file *, loff_t, int);
00541         ssize_t (*read) (struct file *, char *, size_t, loff_t *);
00542         ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
00543         int (*readdir) (struct file *, void *, filldir_t);
00544         unsigned int (*poll) (struct file *, struct poll_table_struct *);
00545         int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
00546         int (*mmap) (struct file *, struct vm_area_struct *);
00547         int (*open) (struct inode *, struct file *);
00548         int (*flush) (struct file *);
00549         int (*release) (struct inode *, struct file *);
00550         int (*fsync) (struct file *, struct dentry *);
00551         int (*fasync) (int, struct file *, int);
00552         int (*check_media_change) (kdev_t dev);
00553         int (*revalidate) (kdev_t dev);
00554         int (*lock) (struct file *, int, struct file_lock *);
00555 };
00556 */
00557 
00558 struct file_operations pcdsp_fops = {
00559   pcdsp_seek,   /* Set DPRAM R/W Position */
00560   pcdsp_read,   /* Read Data to DPRAM */
00561   pcdsp_write,  /* Write Data to DPRAM */
00562   NULL,         /* readdir */
00563   NULL,         /* poll */
00564   pcdsp_ioctl,  /* ioctl */
00565   NULL,         /* mmap */
00566   pcdsp_open,   /* open */
00567   NULL,         /* flush */
00568   pcdsp_release,/* release */
00569   NULL,         /* fsync */
00570   NULL,         /* fasync */
00571   NULL,         /* check_media_change */
00572   NULL,         /* revalidate */
00573   NULL          /* lock */
00574 };
00575 
00576 int init_module(void){
00577     KDEBUG("init_module PCDSP:\n");
00578     //    PCDSP_MAJOR, ...
00579     if ((pcdsp_major = register_chrdev (0, PCDSP_DEVICE_NAME, &pcdsp_fops)) == -EBUSY){
00580         KDEBUG("unable to get major for pcdsp device: %s\n", PCDSP_DEVICE_NAME);
00581         return -EIO;
00582     }
00583     KDEBUG("Major number :%i \n", pcdsp_major);
00584 
00585     if(pcdsp_initialize())
00586         return -1; /* some error occured */
00587     
00588     KDEBUG("init_module PCDSP done.\n");
00589     return 0; /* success */
00590 }
00591 
00592 void cleanup_module(void){
00593     KDEBUG("Module PCDSP cleanup:\n");
00594     pcdsp_quit();
00595     unregister_chrdev(pcdsp_major, PCDSP_DEVICE_NAME);
00596     KDEBUG("Module PCDSP unregistered.\n");
00597 }

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