/* * h8-adc.c */ #if !defined(__KERNEL__) #define __KERNEL__ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef union { unsigned short WORD; struct { unsigned char H:8; unsigned char L:8; } BYTE; } _ADDR; #define ADC_DEVNAME "h8_adc" #define ADC_MAJOR 191 #define ADC_IRQ 23 MODULE_AUTHOR( "sushi-k@quake3.jp" ); MODULE_DESCRIPTION( "H8/3069F ADC driver." ); MODULE_LICENSE( "GPL" ); static unsigned int adc_major = ADC_MAJOR; static char *adc_devname = ADC_DEVNAME; static unsigned char *msg = "h8_adc.o"; static unsigned int adc_ch = 0; static int f_adc = 0; static _ADDR ad_data; // アクセス数 static int f_access = 0; // 読み出しポインタ static int read_p = 0; // 割り込み処理 void adc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { volatile unsigned char *addr; volatile unsigned char *adcsr = (volatile unsigned char *)ADCSR; unsigned char d; // いちど空読み d = *adcsr; // ADF & ADIE & ADST クリア *adcsr &= 0x1F; addr = (volatile unsigned char *)ADDRAH + (adc_ch * 2); // データ読みだし、上位、下位の順で読み取る ad_data.BYTE.H = *addr; addr++; ad_data.BYTE.L = *addr; // 6ビットシフト ad_data.WORD = ad_data.WORD >> 6; // 終了フラグセット f_adc = 1; } static int adc_open(struct inode *inode, struct file *filp) { // 1プロセスしかオープンできないようにする if( f_access ) { printk( "%s: ADC is already used.", msg ); return -EFAULT; } adc_ch = MINOR(inode->i_rdev); if( adc_ch > 3 ) { printk( "%s: ch > 3 is not supported.", msg ); return -EFAULT; } read_p = 0; f_access++; MOD_INC_USE_COUNT; return 0; } // release static int adc_release(struct inode *inode, struct file *filp) { f_access--; MOD_DEC_USE_COUNT; return 0; } static ssize_t adc_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { volatile unsigned char *adcsr = (volatile unsigned char *)ADCSR; if( read_p == 0 ) { // 単一モードADCスタート f_adc = 0; *adcsr = 0x60 | adc_ch; // 割り込み待ち while( ! f_adc ) schedule(); } if( count >= 2 ) { buf[0] = ad_data.BYTE.H; buf[1] = ad_data.BYTE.L; read_p = 2; } else if( count != 0 ) { if( read_p == 0 ) { buf[0] = ad_data.BYTE.H; read_p++; } else { buf[0] = ad_data.BYTE.L; read_p++; } } if( read_p > 1 ){ read_p = 0; } return read_p; } struct file_operations adc_fops = { owner: THIS_MODULE, read: adc_read, write: NULL, open: adc_open, release: adc_release, ioctl: NULL, }; // init static int __init adc_init(void) { int retval; // デバイスドライバ登録 retval = register_chrdev(adc_major, adc_devname , &adc_fops); if (retval < 0) { printk(KERN_ERR "Cannnot register: %s(%d)\n", adc_devname, adc_major); return retval; } else { printk(KERN_INFO "%s(%d): registered.\n", adc_devname, adc_major ); } retval = request_irq(ADC_IRQ, adc_interrupt, SA_INTERRUPT, adc_devname, NULL); if( retval < 0 ) { printk(KERN_ERR "%s(%d): Cannot get irq %d.\n", adc_devname, adc_major,ADC_IRQ ); return retval; } f_access = 0; adc_ch = 0; read_p = 0; return 0; } // exit static void __exit adc_cleanup(void) { // 割り込み解放 free_irq( ADC_IRQ, NULL ); printk( KERN_INFO "%s : removed.\n", msg ); } // モジュールの初期化、クリーンアップ関数登録 module_init(adc_init); module_exit(adc_cleanup);