?? ads8344_driver.c
字號:
ts_busy_flag = inl(S3C44B0X_SR_SIOBUSY); if (0xA5A5A5A5 == ts_busy_flag) return;/* End of add */ /* if unwanted interrupts come, trash them */ if ( (ad_conv_state == AD_CONV_IDLE) || (ad_conv_state == AD_CONV_END) ) return; save_flags(flags); /* disable interrupts */ cli(); CLEAR_SIO_IRQ; /* clear source of interrupt */ switch(ad_conv_state) { case AD_CONV_ST0: /* start SIO receiving */ set_ad_ctrl(SIODATA_NULL); /* start SIO receiving */ ad_conv_state_shift(AD_CONV_ST1); break; case AD_CONV_ST1: read_conv_dat(BIT15_BIT9); set_ad_ctrl(SIODATA_NULL); ad_conv_state_shift(AD_CONV_ST2); break; case AD_CONV_ST3: read_conv_dat(BIT0_BIT0); ad_conv_state_shift(AD_CONV_END); release_SIO_transfer(); break; default: ad_conv_state_shift(AD_CONV_IDLE); release_SIO_transfer(); break; } restore_flags(flags); /* Enable interrupts */}static void handle_digit_sample_data(int channel){ if (AD_DIGIT_BEGIN == ad_digit_st[channel]) { /* to clear digital sampling data * * since it has been read into the queue */ adc_digit_sample[channel].ad_count_dur = 0; adc_digit_sample[channel].first_int.tv_sec = 0; adc_digit_sample[channel].first_int.tv_usec = 0; adc_digit_sample[channel].second_int.tv_sec = 0; adc_digit_sample[channel].second_int.tv_usec = 0; } if (SNR_MODE_COUNT == current_params.cha[channel].snr_mode) { adc_digit_sample[channel].ad_count_dur++; spin_lock_irq(&digit_lock); ad_digit_st[channel] = AD_DIGIT_END; spin_unlock_irq(&digit_lock); } else if (SNR_MODE_DUR == current_params.cha[channel].snr_mode) { if ((0 == adc_digit_sample[channel].first_int.tv_sec) && (0 == adc_digit_sample[channel].first_int.tv_usec) ) { do_gettimeofday(&adc_digit_sample[channel].first_int); adc_digit_sample[channel].second_int.tv_sec = 0; adc_digit_sample[channel].second_int.tv_usec = 0; spin_lock_irq(&digit_lock); ad_digit_st[channel] = AD_DIGIT_RUNNING; spin_unlock_irq(&digit_lock); } else if ((0 == adc_digit_sample[channel].second_int.tv_sec) && (0 == adc_digit_sample[channel].second_int.tv_usec) ) { do_gettimeofday(&adc_digit_sample[channel].second_int); adc_digit_sample[channel].ad_count_dur = 100000 * (adc_digit_sample[channel].second_int.tv_sec - adc_digit_sample[channel].first_int.tv_sec ) + (adc_digit_sample[channel].second_int.tv_usec - adc_digit_sample[channel].first_int.tv_usec ); spin_lock_irq(&digit_lock); ad_digit_st[channel] = AD_DIGIT_END; spin_unlock_irq(&digit_lock); } else { ; /* do nothing until the duration is read out */ } }}/* * digital interrupt service: channel 0. */static void handle_cha0_irq(int irq, void *dev_id, struct pt_regs *regs){ CLEAR_CHA0_IRQ; if (SNR_TYPE_DIGITAL != current_params.cha[0].snr_type) return; handle_digit_sample_data(0); }static void handle_cha1_irq(int irq, void *dev_id, struct pt_regs *regs){ CLEAR_CHA1_IRQ; if (SNR_TYPE_DIGITAL != current_params.cha[1].snr_type) return; handle_digit_sample_data(1);}static int get_extint2345_src(void){ int val; val = inl(S3C44B0X_EXTINPND); if (val & 0x00000001) return 2; else if (val & 0x00000002) return 3; else if (val & 0x00000004) return 4; else if (val & 0x00000008) return 5; else return -1;}static void handle_cha2345_irq(int irq, void *dev_id, struct pt_regs *regs){ int int_src; int_src = get_extint2345_src(); /* to get interrupt source: one of * ExtInt4 or ExtInt5 or ExtInt6 or * ExtInt7, and then convert to * channel no: 2, 3, 4, 5 */ if (-1 == int_src) return; /* no interrupt is triggered */ CLEAR_CHA2345_IRQ; if (SNR_TYPE_DIGITAL != current_params.cha[int_src].snr_type) return; handle_digit_sample_data(int_src);}/* ----------------- file_operations functions ---------------------------*//* * Called whenever a process attempts to read the device it has already open. */static struct file_operations adc_fops = { owner: THIS_MODULE, read: adc_read, poll: adc_poll, ioctl: adc_ioctl, open: adc_open, release: adc_release, fasync: adc_fasync,};/* * miscdevice structure for misc driver registration. */static struct miscdevice s3c44b0_adc = { NSY_ADC_MINOR,"AD Converter",&adc_fops};static ssize_t adc_read (struct file *file, char *buff, /* the buffer to fill with data */ size_t len, /* the length of the buffer. */ loff_t *offset) /* Our offset in the file */{ char *p_buffer; char c; int i; int err; while(!have_ad_data) { /* noting to read */ if(file->f_flags & O_NONBLOCK) return -EAGAIN; interruptible_sleep_on(&queue->proc_list); if(signal_pending(current)) return -ERESTARTSYS; /* tell the fs layer to handle it */ } /* Force length to the one available */ len = DATA_LENGTH; /* verify that the user space address to write is valid */ err = verify_area(VERIFY_WRITE,buff,len); if(err) return err; /* * The events are anyway put in the queue. * But the read could choose to get the last event or the next in queue. * This decision is controlled through the ioctl function. * When the read doesn't flush the queue, this last is always full. */ if(current_params.event_queue_on) { i = len; while((i>0) && !queue_empty()) { c = get_char_from_queue(); put_user(c,buff++); i--; } } else { p_buffer = (char *)&adc_queue_node; for (i = len-1; i >= 0; i--) put_user(p_buffer[i],buff+i); } if(queue_empty() || !current_params.event_queue_on) { spin_lock_irq(&have_dat_lock); have_ad_data = 0; /* queue events fully consumned */ spin_unlock_irq(&have_dat_lock); } else { spin_lock_irq(&have_dat_lock); have_ad_data = 1; /* queue not empty */ spin_unlock_irq(&have_dat_lock); } if (len-i) { file->f_dentry->d_inode->i_atime = CURRENT_TIME; /* nfi */ return len-i; } return 0;}/* * select file operation seems to become poll since version 2.2 of the kernel */static unsigned int adc_poll(struct file *file, poll_table *table) { poll_wait(file,&queue->proc_list,table); if (have_ad_data) return POLLIN | POLLRDNORM; return 0;}/* * Called whenever a process attempts to do an ioctl to the device it has * already open. */static int adc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, /* the command to the ioctl */ unsigned long arg) /* the parameter to it */{ struct adc_drv_params new_params; int err; int i; char *p_in; char *p_out; switch(cmd) { case ADC_PARAMS_GET: /* Get internal params. First check if user space area * to write is valid. */ err = verify_area(VERIFY_WRITE,(char *)arg,sizeof(current_params)); if(err) return err; p_in = (char *)¤t_params; p_out = (char *)arg; for(i=0;i<sizeof(current_params);i++) put_user(p_in[i],p_out+i); return 0; case ADC_PARAMS_SET: /* Set internal params. First check if user space area * to read is valid.*/ err = verify_area(VERIFY_READ,(char *)arg,sizeof(new_params)); if(err) return err; p_in = (char *)arg; p_out = (char *)&new_params; for(i=0;i<sizeof(new_params);i++) get_user(p_in[i],p_out+i); /* All the params are set with minimal test. */ memcpy((void*)¤t_params, (void*)&new_params, sizeof(current_params)); /* check version */ if(new_params.version_req != -1) { if(new_params.version_req != ADC_VERSION) { printk("%s: error: driver version is %d, requested version is %d\n", __file__, ADC_VERSION, new_params.version_req); return -EBADRQC; } } return 0; default: /* cmd is not an ioctl command */ return -ENOIOCTLCMD; }}static int init_adc_interrupt(void){ int err; /* Interrupt for digital A/D converter */ err = 0; err |= request_irq(CHA_IRQ_0, handle_cha0_irq, SA_INTERRUPT, "Digital channel 0", NULL); err |= request_irq(CHA_IRQ_1, handle_cha1_irq, SA_INTERRUPT, "Digital channel 0", NULL); err |= request_irq(CHA_IRQ_2345, handle_cha2345_irq, SA_INTERRUPT, "Digital channel 0", NULL); if(err) { printk("%s: Error. Cannot attach IRQ %d~%d to ADC driver\n", __file__, CHA_IRQ_0, CHA_IRQ_2345); return err; } printk("%s: IRQ %d~%d attached to ADC driver\n", __file__, CHA_IRQ_0, CHA_IRQ_2345); /* IRQ for SIO */ //err = request_irq(S3C44B0X_INTERRUPT_SIO, handle_sio_irq,SA_INTERRUPT,"sio_irq",NULL); err = request_irq(S3C44B0X_INTERRUPT_SIO, handle_sio_irq, SA_SHIRQ, "adc_sio_irq", &s3c44b0_adc); if(err) { printk("%s: Error. Cannot attach IRQ %d to SIO device\n", __file__, S3C44B0X_INTERRUPT_SIO); return err; } printk("%s: IRQ %d attached to SIO\n", __file__, S3C44B0X_INTERRUPT_SIO); return err;} /* * Called whenever a process attempts to open the device file. */static int adc_open(struct inode *inode, struct file *file) { int err; /* To talk to only one device at a time */ if(device_open) return -EBUSY; /* IRQ registration have to be done before the hardware is instructed to * generate interruptions. */ ad_conv_state = AD_CONV_IDLE; /*request interrupt */ if( 0 != (err = init_adc_interrupt())) return err; /* Init the queue */ queue = (struct adc_queue *) kmalloc(sizeof(*queue),GFP_KERNEL); memset(queue,0,sizeof(*queue)); queue->head = queue->tail = 0; queue->count = 0; queue->lock = SPIN_LOCK_UNLOCKED; init_waitqueue_head(&queue->proc_list); /* Init hardware */ if (0 != (err = init_adc_drv())) return err; printk("adc: Init hardware done..\n"); /* Increment the usage count (number of opened references to the module) * to be sure the module couldn't be removed while the file is open. */ MOD_INC_USE_COUNT; /* Add device count */ device_open++; return 0; /* open succeed */}static int adc_fasync(int inode, struct file *file, int mode) { int retval; retval = fasync_helper(inode,file,mode,&queue->fasync); if(retval < 0) return retval; return 0;}/* * Called whenever a process attempts to close the device file. */static int adc_release(struct inode *inode, struct file *file) { /* Remove this file from the asynchronously notified file's */ adc_fasync(inode,file,0); /* Release hardware */ release_adc_hardware(); /* Free the allocated memory for the queue */ kfree(queue); /* IRQ have to be freed after the hardware is instructed not to interrupt * the processor any more. */ free_irq(S3C44B0X_INTERRUPT_SIO, NULL); free_irq(CHA_IRQ_0, NULL); free_irq(CHA_IRQ_1, NULL); free_irq(CHA_IRQ_2345, NULL); /* Decrement the usage count, otherwise once the file is open we'll never * get rid of the module. */ MOD_DEC_USE_COUNT; /* And my own counter too */ device_open--; return 0;}/* * Initialize the driver. */static int __init adc_init(void) { int err; /* Register the misc device driver */ err = misc_register(&s3c44b0_adc); if(err<0) printk("%s: Error registering the device\n", __file__); else printk("%s: Device register with name: %s and number: %d %d\n", __file__, s3c44b0_adc.name, NSY_ADC_MAJOR, s3c44b0_adc.minor); /* Init prameters settings at boot time */ init_adc_settings(); return err; /* A non zero value means that init_module failed */}/* * Cleanup - undid whatever adc_init did. */void adc_cleanup(void) { /* Unregister device driver */ misc_deregister(&s3c44b0_adc);} module_init(adc_init);module_exit(adc_cleanup);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -