// アドテックシステムサイエンス aPCI P54 デバイスドライバ // By Masaaki KUMAGAI kumagai@emura.mech.tohoku.ac.jp // Mechatronics Design Lab. // Dept. of Mechatronics and Precision Eng. // Tohoku UNIV // // gcc -c apcip54.c -DLINUX -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -m486 // insmod apcip54 #define MODULE #define __KERNEL__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "apcip54.h" #define DPRINTL #undef DPRINTK // Pentium 以降では使用可能 #ifndef USERDTSC // #define USERDTSC #endif // #undef USERDTSC #define ADTEK_VENDOR_ID 0x136c // ============ aPCIP54 管理情報 ============= // デバイスID #define APCIP54_DEVICE_ID 0x9054 // 割り込み認識用定数 #define APCIP54_IRQ_MAGIC 0x90540 // aPCIP54 管理ブロック構造体: BSN で管理: 16個 struct _apci54 { unsigned int base_address; // aPCIP54 のベースアドレス(I/O) unsigned int irq; // 割り込み番号 unsigned int irq_mask; // 個々のボードの割り込み設定 } aPCIP54[16]; #define APCINOTUSE (~0) // ======== デバイス ファイル操作管理情報 ========== #define FILEACCESS 32 typedef struct _fileaccess { unsigned long f_version; // 識別子, 0 で未使用 struct wait_queue *wait; // select の待機で使用 int waiting; // 0:none 1:select 2:selected(int) unsigned long long irq_mask; // 割り込みマスク:4bit*16 struct aPCIP54interrupt istat;// 割り込み時状況 } FileAccess; static FileAccess accesstable[FILEACCESS]; // 空のサーチ static FileAccess *FASearchUnused(void) { int i; for(i=0;i15)||(aPCIP54[bsn].base_address==APCINOTUSE)) return; printk("aPCIP54: bsn %d addr %x: ",bsn,aPCIP54[bsn].base_address); for(i=0;i<16;i++) { printk("%02X ",inb(aPCIP54[bsn].base_address+i)); if(i==7) printk(" "); } printk("\n"); } #endif #ifndef DEBUG inline #endif volatile unsigned long long int GetRDTSC(void) { #ifdef USERDTSC unsigned int h,l; /* read Pentium cycle counter */ __asm__(".byte 0x0f,0x31" :"=a" (l), "=d" (h)); return ((unsigned long long int)h<<32)|l; #else return jiffies; #endif } // ========== 割り込みサービス ============ static void aPCIP54_interrupt(int irq,void *dev_id,struct pt_regs *regs) { int i; int bsn; unsigned long long irq_where,itime; unsigned long int portl; unsigned short int porth; bsn=(int)(dev_id); // なんか、別の割り込みっぽい。 if((bsn&0xffff0)!=APCIP54_IRQ_MAGIC) return; // BSN を摘出 bsn=bsn&0xf; // 状況の保存 portl=inl(aPCIP54[bsn].base_address); porth=inw(aPCIP54[bsn].base_address+4); itime=GetRDTSC(); irq_where=inb(aPCIP54[bsn].base_address+0xf)&0xf; #ifdef DPRINTK printk("aPCIP54: interrupt from bsn:%d pattern:%X\n", bsn,(unsigned int)irq_where); #endif irq_where=irq_where<<(bsn*4); #ifdef DPRINTK printk("aPCIP54: interrupt mask: %16LX\n",irq_where); #endif //dump_apcip54(); for(i=0;i\n ",irqs); #endif for(i=0;i<16;i++,irqs>>=4) { #ifdef DPRINTK dprintk(" %2LX",irqs&0xf); #endif aPCIP54[i].irq_mask=irqs&0xf; // 割り込み有効化 outb(0x10|aPCIP54[i].irq_mask,aPCIP54[i].base_address+0xf); } #ifdef DPRINTK dprintk("\n"); #endif } // ======== デバイスドライバ応答関数 ========== static int aPCIP54_open(struct inode * inode, struct file * file) { FileAccess *n; n=FASearchUnused(); if(n==NULL) { printk("aPCIP54: Device busy\n"); return -EBUSY; } MOD_INC_USE_COUNT; // 参照カウント+ n->f_version=file->f_version; // ファイルの識別 n->wait=NULL; n->waiting=0; // select制御初期化 n->irq_mask=0x0; // 割り込みマスク初期化 n->istat.count=0; file->private_data=n; return 0; } static void aPCIP54_close(struct inode * inode, struct file * file) { FileAccess *f; f=(FileAccess *)(file->private_data); if(f==NULL) { printk("aPCIP54: Illegal access from %ld\n",file->f_version); return ; } f->f_version=0; // 未使用マーク f->waiting=0; f->irq_mask=0; MOD_DEC_USE_COUNT; // 参照カウント− aPCIP54_irq_setup(); return ; } static int aPCIP54_select(struct inode *inode, struct file *file, int flag, select_table *wait) { FileAccess *f; if(flag!=SEL_IN) // IN の select のみ対応 return 0; f=(FileAccess *)(file->private_data); if(f==NULL) { printk("aPCIP54: Illegal access from %ld\n",file->f_version); return -EINVAL; } if(f->waiting==2) { f->waiting=0; return 1; } f->waiting=1; select_wait(&(f->wait), wait); return 0; } // 動作速度チェック #ifdef DEBUG static volatile unsigned long long _rdtscst,_rdtscen; #define SET_TIME_ST() _rdtscst=GetRDTSC(); #define DISP_TIME() _rdtscen=GetRDTSC(); printk("time: %d [ns]\n",(unsigned int)(_rdtscen-_rdtscst)*3); #endif static int aPCIP54_ioctl(struct inode * inode,struct file * file, unsigned int iocmd,unsigned long ioarg) { FileAccess *f; struct aPCIP54port port; struct aPCIP54value val; struct aPCIP54irq irqm; struct aPCIP54portBSN portbsn; int bsn=0,i,mask; // FASearch が不要 switch(iocmd) { case aPCIP54_NONOPERATION: return 0; case aPCIP54_CHECKBSN: // BSN のボードが存在するかどうか bsn=(int)ioarg; if((bsn<0)||(bsn>15)) return -EINVAL; return (aPCIP54[bsn].base_address!=APCINOTUSE)?0:-1; // 値取得系 case aPCIP54_BASEADDRESS: // BSNのボードのベースアドレス取得 memcpy_fromfs(&val,(void *)ioarg,sizeof(val)); if((val.bsn>15)||(val.bsn<0)) return -EINVAL; val.value=aPCIP54[bsn].base_address; memcpy_tofs((void *)ioarg,&val,sizeof(val)); return 0; case aPCIP54_IRQ: memcpy_fromfs(&val,(void *)ioarg,sizeof(val)); if((val.bsn>15)||(val.bsn<0)) return -EINVAL; val.value=aPCIP54[bsn].irq; memcpy_tofs((void *)ioarg,&val,sizeof(val)); return 0; // I/O アクセス系 case aPCIP54_READPORT: memcpy_fromfs(&port,(void *)ioarg,sizeof(port)); if((port.bsn>15)||(port.bsn<0)) return -EINVAL; if((port.port>15)||(port.port<0)) return -EINVAL; port.value=inb(aPCIP54[port.bsn].base_address+port.port); memcpy_tofs((void *)ioarg,&port,sizeof(port)); return 0; case aPCIP54_WRITEPORT: memcpy_fromfs(&port,(void *)ioarg,sizeof(port)); if((port.bsn>15)||(port.bsn<0)) return -EINVAL; if((port.port>15)||(port.port<0)) return -EINVAL; outb(port.value,aPCIP54[port.bsn].base_address+port.port); return 0; case aPCIP54_READPORTBSN: memcpy_fromfs(&portbsn,(void *)ioarg,sizeof(int)); // bsnだけとる if((portbsn.bsn>15)||(portbsn.bsn<0)) return -EINVAL; if(aPCIP54[portbsn.bsn].base_address==APCINOTUSE) return -EINVAL; *((unsigned long *)(portbsn.ports))= inl(aPCIP54[portbsn.bsn].base_address); *((unsigned long *)(portbsn.ports+4))= inl(aPCIP54[portbsn.bsn].base_address+4); portbsn.ports[7]=inb(aPCIP54[portbsn.bsn].base_address+0xf); memcpy_tofs((void *)ioarg,&portbsn,sizeof(portbsn)); return 0; case aPCIP54_WRITEPORTBSN: memcpy_fromfs(&portbsn,(void *)ioarg,sizeof(portbsn)); mask=portbsn.mask; for(i=0;i<7;i++,mask>>=1) if(mask&1) outb(portbsn.ports[i],aPCIP54[portbsn.bsn].base_address+i); return 0; } f=(FileAccess *)(file->private_data); if(f==NULL) { printk("aPCIP54: Illegal access from %ld\n",file->f_version); return -EINVAL; } switch(iocmd) { // 割り込み処理 case aPCIP54_SETIRQMASK: memcpy_fromfs(&irqm,(void *)ioarg,sizeof(irqm)); f->irq_mask=irqm.irq_mask; aPCIP54_irq_setup(); return 0; case aPCIP54_GETIRQMASK: irqm.irq_mask=f->irq_mask; memcpy_tofs((void *)ioarg,&irqm,sizeof(irqm)); return 0; case aPCIP54_GETINTERRUPTSTATUS: memcpy_tofs((void *)ioarg,&(f->istat),sizeof(f->istat)); f->istat.count=0; return 0; default: printk("aPCIP54: illegal ioctl from %ld %u:%lu\n", f->f_version,iocmd,ioarg); return -EINVAL; } return 0; } /* ドライバテーブル */ static struct file_operations aPCIP54_fops = { NULL, /* lseek */ NULL, /* read */ NULL, /* write */ NULL, /* readdir */ aPCIP54_select, /* select */ aPCIP54_ioctl, /* ioctl */ NULL, /* mmap */ aPCIP54_open, /* open */ aPCIP54_close, /* close */ NULL /* fsync */ }; #ifndef APCIP54_MAJOR #define APCIP54_MAJOR 58 #endif #define APCIP54_NAME "aPCIP54" // とりあえず、init_module は情報収集 int init_module(void) { static unsigned char bus=0xff,dev_fn=0xff; static unsigned short pci_command,i; static unsigned int bsn,baseaddr,t; unsigned char irq; // ボード情報初期化 for(i=0;i<16;i++) aPCIP54[i].base_address=APCINOTUSE; // PCIデバイス検索 for(i=0;i<0xff;i++) { if(pcibios_find_device(ADTEK_VENDOR_ID,APCIP54_DEVICE_ID,i,&bus,&dev_fn)) { // もう、ドライバはない。 break; } printk("aPCIP54: found on PCI Bus:%d Device:%d Function:%d\n", bus,dev_fn>>3,dev_fn&0x7); //PrintPCIBaseAddress(bus,dev_fn,0); //PrintPCIBaseAddress(bus,dev_fn,1); //PrintPCIBaseAddress(bus,dev_fn,2); //PrintPCIBaseAddress(bus,dev_fn,3); //PrintPCIBaseAddress(bus,dev_fn,4); //PrintPCIBaseAddress(bus,dev_fn,5); //PrintPCIInterrupt(bus,dev_fn); // 実験 // ベースアドレスの取得 if(pcibios_read_config_dword (bus, dev_fn,PCI_BASE_ADDRESS_1,&baseaddr)) { printk("aPCIP54: cannot get base addr\n"); return -ENODEV; } t=inl(baseaddr+0x28); //outl(0x00540120,baseaddr+0x28); outl(0x00510060,baseaddr+0x28); printk("LAS0BDR: %08X\n",t); // ベースアドレスの取得 if(pcibios_read_config_dword (bus, dev_fn,PCI_BASE_ADDRESS_2,&baseaddr)) { printk("aPCIP54: cannot get base addr\n"); return -ENODEV; } if((baseaddr&PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_IO) { printk("aPCIP54: cannot get base addr(not I/O)\n"); return -ENODEV; } baseaddr=baseaddr&PCI_BASE_ADDRESS_IO_MASK; // BSN の読み取り bsn=(inb(baseaddr+0xf)>>4)&0xf; if(aPCIP54[bsn].base_address!=APCINOTUSE) { printk("aPCIP54: there are confilict boards BSN %d\n",bsn); continue; } // IRQ line reading if(pcibios_read_config_byte(bus, dev_fn,PCI_INTERRUPT_LINE,&irq)) { printk("aPCIP54: cannot get IRQ\n"); continue; } // IOEnable pcibios_read_config_word(bus, dev_fn,PCI_COMMAND,&pci_command); pci_command|=PCI_COMMAND_IO; pcibios_write_config_word(bus, dev_fn,PCI_COMMAND,pci_command); printk("aPCIP54: Base address:%4X, IRQ:%d, BSN:%d\n", baseaddr,irq,bsn); aPCIP54[bsn].base_address=baseaddr; aPCIP54[bsn].irq=irq; aPCIP54[bsn].irq_mask=0x0; } // 現状ダンプ //for(i=0;i<16;i++) //dump_apcip54(i); // character device registration if(register_chrdev(APCIP54_MAJOR,APCIP54_NAME,&aPCIP54_fops)) { printk("device registration error\n"); return -EBUSY; } // ファイル管理テーブル初期化 for(i=0;i