📄 das16.c
字号:
if(devpriv->timer_running) mod_timer(&devpriv->timer, jiffies + timer_period);}static void das16_interrupt( comedi_device *dev ){ unsigned long dma_flags, spin_flags; comedi_subdevice *s = dev->read_subdev; comedi_async *async; comedi_cmd *cmd; int num_bytes, residue; int buffer_index; if(dev->attached == 0) { comedi_error(dev, "premature interrupt"); return; } // initialize async here to make sure it is not NULL async = s->async; cmd = &async->cmd; if( devpriv->dma_chan == 0 ) { comedi_error(dev, "interrupt with no dma channel?"); return; } comedi_spin_lock_irqsave( &dev->spinlock, spin_flags ); if( ( devpriv->control_state & DMA_ENABLE ) == 0 ) { comedi_spin_unlock_irqrestore( &dev->spinlock, spin_flags ); DEBUG_PRINT( "interrupt while dma disabled?\n" ); return; } dma_flags = claim_dma_lock(); disable_dma(devpriv->dma_chan); /* clear flip-flop to make sure 2-byte registers for * count and address get set correctly */ clear_dma_ff(devpriv->dma_chan); // figure out how many points to read /* residue is the number of bytes left to be done on the dma * transfer. */ residue = get_dma_residue(devpriv->dma_chan); if(residue > devpriv->dma_transfer_size) { comedi_error(dev, "residue > transfer size!\n"); async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; num_bytes = 0; }else num_bytes = devpriv->dma_transfer_size - residue; if(cmd->stop_src == TRIG_COUNT && num_bytes > devpriv->adc_byte_count) { num_bytes = devpriv->adc_byte_count; async->events |= COMEDI_CB_EOA; } buffer_index = devpriv->current_buffer; devpriv->current_buffer = ( devpriv->current_buffer + 1 ) % 2; devpriv->adc_byte_count -= num_bytes; // figure out how many bytes for next transfer if(cmd->stop_src == TRIG_COUNT && devpriv->timer_mode == 0 && devpriv->dma_transfer_size > devpriv->adc_byte_count ) devpriv->dma_transfer_size = devpriv->adc_byte_count; // re-enable dma if(( async->events & COMEDI_CB_EOA ) == 0) { set_dma_addr( devpriv->dma_chan, devpriv->dma_buffer_addr[ devpriv->current_buffer ] ); set_dma_count( devpriv->dma_chan, devpriv->dma_transfer_size ); enable_dma(devpriv->dma_chan); } release_dma_lock(dma_flags); comedi_spin_unlock_irqrestore( &dev->spinlock, spin_flags ); das16_write_array_to_buffer( dev, devpriv->dma_buffer[ buffer_index ], num_bytes ); cfc_handle_events( dev, s );}static unsigned int das16_set_pacer(comedi_device *dev, unsigned int ns, int rounding_flags){ i8253_cascade_ns_to_timer_2div(devpriv->clockbase, &(devpriv->divisor1), &(devpriv->divisor2), &ns, rounding_flags & TRIG_ROUND_MASK); /* Write the values of ctr1 and ctr2 into counters 1 and 2 */ i8254_load(dev->iobase + DAS16_CNTR0_DATA, 1, devpriv->divisor1, 2); i8254_load(dev->iobase + DAS16_CNTR0_DATA, 2, devpriv->divisor2, 2); return ns;}static void reg_dump(comedi_device *dev){ DEBUG_PRINT("********DAS1600 REGISTER DUMP********\n"); DEBUG_PRINT("DAS16_MUX: %x\n", inb(dev->iobase+DAS16_MUX) ); DEBUG_PRINT("DAS16_DIO: %x\n", inb(dev->iobase+DAS16_DIO) ); DEBUG_PRINT("DAS16_STATUS: %x\n", inb(dev->iobase+DAS16_STATUS) ); DEBUG_PRINT("DAS16_CONTROL: %x\n", inb(dev->iobase+DAS16_CONTROL) ); DEBUG_PRINT("DAS16_PACER: %x\n", inb(dev->iobase+DAS16_PACER) ); DEBUG_PRINT("DAS16_GAIN: %x\n", inb(dev->iobase+DAS16_GAIN) ); DEBUG_PRINT("DAS16_CNTR_CONTROL: %x\n", inb(dev->iobase+DAS16_CNTR_CONTROL) ); DEBUG_PRINT("DAS1600_CONV: %x\n", inb(dev->iobase+DAS1600_CONV) ); DEBUG_PRINT("DAS1600_BURST: %x\n", inb(dev->iobase+DAS1600_BURST) ); DEBUG_PRINT("DAS1600_ENABLE: %x\n", inb(dev->iobase+DAS1600_ENABLE) ); DEBUG_PRINT("DAS1600_STATUS_B: %x\n", inb(dev->iobase+DAS1600_STATUS_B) );}static int das16_probe(comedi_device *dev, comedi_devconfig *it){ int status; int diobits; /* status is available on all boards */ status = inb(dev->iobase + DAS16_STATUS); if((status & UNIPOLAR)){ devpriv->ai_unipolar = 1; }else{ devpriv->ai_unipolar = 0; } if((status & DAS16_MUXBIT)){ devpriv->ai_singleended = 1; }else{ devpriv->ai_singleended = 0; } /* diobits indicates boards */ diobits = inb(dev->iobase + DAS16_DIO) & 0xf0; printk(" id bits are 0x%02x\n",diobits); if(thisboard->id != diobits) { printk(" requested board's id bits are 0x%x\n", thisboard->id); return -1; } return 0;}static int das1600_mode_detect(comedi_device *dev){ int status=0; status = inb(dev->iobase + DAS1600_STATUS_B); if(status & DAS1600_CLK_10MHZ) { devpriv->clockbase = 100; printk(" 10MHz pacer clock\n"); } else { devpriv->clockbase = 1000; printk(" 1MHz pacer clock\n"); } reg_dump(dev); return 0;}/* * * Options list: * 0 I/O base * 1 IRQ * 2 DMA * 3 Clock speed (in MHz) */static int das16_attach(comedi_device *dev, comedi_devconfig *it){ comedi_subdevice *s; int ret, irq; int iobase; int dma_chan; int timer_mode; unsigned long flags; comedi_krange *user_ai_range, *user_ao_range; iobase = it->options[0]; irq = it->options[1]; timer_mode = it->options[8]; if( timer_mode ) irq = 0; printk("comedi%d: das16:",dev->minor); // check that clock setting is valid if(it->options[3]) { if(it->options[3] != 0 && it->options[3] != 1 && it->options[3] != 10) { printk("\n Invalid option. Master clock must be set to 1 or 10 (MHz)\n"); return -EINVAL; } } if((ret=alloc_private(dev,sizeof(struct das16_private_struct)))<0) return ret; if(thisboard->size<0x400){ printk(" 0x%04x-0x%04x\n", iobase, iobase+thisboard->size); if(check_region(iobase,thisboard->size)<0){ printk(" I/O port conflict\n"); return -EIO; } }else{ printk(" 0x%04x-0x%04x 0x%04x-0x%04x\n", iobase,iobase+0x0f, iobase+0x400,iobase+0x400+(thisboard->size&0x3ff)); if(check_region(iobase,0x10) < 0) { printk(" I/O port conflict: 0x%04x-0x%04x\n", iobase,iobase+0x0f); return -EIO; } if(check_region(iobase+0x400,thisboard->size&0x3ff)<0){ printk(" I/O port conflict: 0x%04x-0x%04x\n", iobase+0x400, iobase+0x400+(thisboard->size&0x3ff)); return -EIO; } } if(thisboard->size < 0x400){ request_region(iobase,thisboard->size,"das16"); }else{ request_region(iobase,0x10,"das16"); request_region(iobase+0x400,thisboard->size&0x3ff,"das16"); } dev->iobase = iobase; // probe id bits to make sure they are consistent if(das16_probe(dev, it)) { printk(" id bits do not match selected board, aborting\n"); return -EINVAL; } dev->board_name = thisboard->name; // get master clock speed if(thisboard->size < 0x400) { if(it->options[3]) devpriv->clockbase = 1000 / it->options[3]; else devpriv->clockbase = 1000; // 1 MHz default }else { das1600_mode_detect(dev); } /* now for the irq */ if( irq > 1 && irq < 8 ) { if((ret=comedi_request_irq(irq, das16_dma_interrupt, 0, "das16",dev)) < 0) return ret; dev->irq = irq; printk(" ( irq = %d )",irq); }else if(irq == 0){ printk(" ( no irq )"); }else { printk(" invalid irq\n"); return -EINVAL; } // initialize dma dma_chan = it->options[2]; if(dma_chan == 1 || dma_chan == 3) { // allocate dma buffers int i; for( i = 0; i < 2; i++) { devpriv->dma_buffer[i] = pci_alloc_consistent( NULL, DAS16_DMA_SIZE, &devpriv->dma_buffer_addr[i] ); if( devpriv->dma_buffer[i] == NULL ) return -ENOMEM; } if(request_dma(dma_chan, "das16")) { printk(" failed to allocate dma channel %i\n", dma_chan); return -EINVAL; } devpriv->dma_chan = dma_chan; flags = claim_dma_lock(); disable_dma(devpriv->dma_chan); set_dma_mode(devpriv->dma_chan, DMA_MODE_READ); release_dma_lock(flags); printk(" ( dma = %d)\n", dma_chan); }else if(dma_chan == 0){ printk(" ( no dma )\n"); }else { printk(" invalid dma channel\n"); return -EINVAL; } // get any user-defined input range if(thisboard->ai_pg == das16_pg_none && (it->options[4] || it->options[5])) { // allocate single-range range table devpriv->user_ai_range_table = kmalloc(sizeof(comedi_lrange) + sizeof(comedi_krange), GFP_KERNEL); // initialize ai range devpriv->user_ai_range_table->length = 1; user_ai_range = devpriv->user_ai_range_table->range; user_ai_range->min = it->options[4]; user_ai_range->max = it->options[5]; user_ai_range->flags = UNIT_volt; } // get any user-defined output range if(it->options[6] || it->options[7]) { // allocate single-range range table devpriv->user_ao_range_table = kmalloc(sizeof(comedi_lrange) + sizeof(comedi_krange), GFP_KERNEL); // initialize ao range devpriv->user_ao_range_table->length = 1; user_ao_range = devpriv->user_ao_range_table->range; user_ao_range->min = it->options[6]; user_ao_range->max = it->options[7]; user_ao_range->flags = UNIT_volt; } if(timer_mode) { init_timer(&(devpriv->timer)); devpriv->timer.function = das16_timer_interrupt; devpriv->timer.data = (unsigned long) dev; devpriv->timer_mode = timer_mode ? 1 : 0; } if((ret=alloc_subdevices(dev, 5))<0) return ret; s=dev->subdevices+0; dev->read_subdev=s; /* ai */ if(thisboard->ai){ s->type = COMEDI_SUBD_AI; s->subdev_flags = SDF_READABLE; if(devpriv->ai_singleended){ s->n_chan = 16; s->len_chanlist = 16; s->subdev_flags |= SDF_GROUND; }else{ s->n_chan = 8; s->len_chanlist = 8; s->subdev_flags |= SDF_DIFF; } s->maxdata = (1 << thisboard->ai_nbits) - 1; if(devpriv->user_ai_range_table) { // user defined ai range s->range_table = devpriv->user_ai_range_table; }else if(devpriv->ai_unipolar){ s->range_table = das16_ai_uni_lranges[thisboard->ai_pg]; }else{ s->range_table = das16_ai_bip_lranges[thisboard->ai_pg]; } s->insn_read = thisboard->ai; s->do_cmdtest = das16_cmd_test; s->do_cmd = das16_cmd_exec; s->cancel = das16_cancel; }else{ s->type=COMEDI_SUBD_UNUSED; } s = dev->subdevices + 1; /* ao */ if(thisboard->ao){ s->type = COMEDI_SUBD_AO; s->subdev_flags = SDF_WRITABLE; s->n_chan = 2; s->maxdata = (1 << thisboard->ao_nbits) - 1; if(devpriv->user_ao_range_table) { // user defined ao range s->range_table = devpriv->user_ao_range_table; }else { s->range_table = &range_unknown; } s->insn_write = thisboard->ao; }else{ s->type = COMEDI_SUBD_UNUSED; } s = dev->subdevices + 2; /* di */ if(thisboard->di){ s->type = COMEDI_SUBD_DI; s->subdev_flags = SDF_READABLE; s->n_chan = 4; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = thisboard->di; }else{ s->type = COMEDI_SUBD_UNUSED; } s = dev->subdevices + 3; /* do */ if(thisboard->do_){ s->type = COMEDI_SUBD_DO; s->subdev_flags = SDF_WRITABLE | SDF_READABLE; s->n_chan = 4; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = thisboard->do_; // initialize digital output lines outb(s->state, dev->iobase + DAS16_DIO); }else{ s->type = COMEDI_SUBD_UNUSED; } s = dev->subdevices + 4; /* 8255 */ if(thisboard->i8255_offset!=0){ subdev_8255_init(dev,s,NULL,(unsigned long)(dev->iobase+ thisboard->i8255_offset)); }else{ s->type = COMEDI_SUBD_UNUSED; } das16_reset(dev); /* set the interrupt level */ devpriv->control_state = DAS16_IRQ(dev->irq); outb(devpriv->control_state, dev->iobase + DAS16_CONTROL); // turn on das1600 mode if available if(thisboard->size > 0x400) { outb(DAS1600_ENABLE_VAL, dev->iobase + DAS1600_ENABLE); outb(0, dev->iobase + DAS1600_CONV); outb(0, dev->iobase + DAS1600_BURST); } return 0;}static int das16_detach(comedi_device *dev){ printk("comedi%d: das16: remove\n", dev->minor); das16_reset(dev); if(dev->subdevices) subdev_8255_cleanup(dev,dev->subdevices+4); if(devpriv) { int i; for( i = 0; i < 2; i++ ) { if( devpriv->dma_buffer[i] ) pci_free_consistent( NULL, DAS16_DMA_SIZE, devpriv->dma_buffer[i], devpriv->dma_buffer_addr[i] ); } if(devpriv->dma_chan) free_dma(devpriv->dma_chan); if(devpriv->user_ai_range_table) kfree(devpriv->user_ai_range_table); if(devpriv->user_ao_range_table) kfree(devpriv->user_ao_range_table); } if(dev->irq) comedi_free_irq(dev->irq, dev); if( dev->iobase ) { if(thisboard->size < 0x400){ release_region(dev->iobase, thisboard->size); }else{ release_region(dev->iobase, 0x10); release_region(dev->iobase + 0x400, thisboard->size&0x3ff); } } return 0;}COMEDI_INITCLEANUP(driver_das16);// utility function that suggests a dma transfer size in bytesstatic unsigned int das16_suggest_transfer_size(comedi_device *dev, comedi_cmd cmd){ unsigned int size; unsigned int freq; /* if we are using timer interrupt, we don't care how long it * will take to complete transfer since it will be interrupted * by timer interrupt */ if(devpriv->timer_mode) return DAS16_DMA_SIZE; /* otherwise, we are relying on dma terminal count interrupt, * so pick a reasonable size */ if(cmd.convert_src == TRIG_TIMER) freq = 1000000000 / cmd.convert_arg; else if(cmd.scan_begin_src == TRIG_TIMER) freq = (1000000000 / cmd.scan_begin_arg) * cmd.chanlist_len; // return some default value else freq = 0xffffffff; if(cmd.flags & TRIG_WAKE_EOS) { size = sample_size * cmd.chanlist_len; }else { // make buffer fill in no more than 1/3 second size = (freq / 3) * sample_size; } // set a minimum and maximum size allowed if(size > DAS16_DMA_SIZE) size = DAS16_DMA_SIZE - DAS16_DMA_SIZE % sample_size; else if(size < sample_size) size = sample_size; if( cmd.stop_src == TRIG_COUNT && size > devpriv->adc_byte_count ) size = devpriv->adc_byte_count; return size;}static void init_munge_info( struct munge_info *info ){ info->have_byte = 0;}/* we want to be able to write one byte at a time to buffer to deal with * possibility that 8-bit dma transfer will be interrupted inbetween * least significant and most significant byte of a sample */static void write_byte_to_buffer( comedi_device *dev, comedi_subdevice *subd, uint8_t raw_byte ){ sampl_t data; struct munge_info *info = &devpriv->ai_munge_info; if( info->have_byte == 0 ) { info->byte = raw_byte; info->have_byte = 1; }else { info->have_byte = 0; if( thisboard->ai_nbits == 12 ) { data = ( raw_byte << 4 ) & 0xff0; data |= ( info->byte >> 4 ) & 0xf; }else { data = ( raw_byte << 8 ) & 0xff00; data |= info->byte & 0xff; } cfc_write_to_buffer( subd, data ); }}static void das16_write_array_to_buffer( comedi_device *dev, void *data, unsigned int num_bytes ){ unsigned int i; uint8_t *array = data; for( i = 0; i < num_bytes; i++ ) write_byte_to_buffer( dev, dev->read_subdev, array[i] );};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -