From 6317b85c57736e6c16e4f3d2047facda9892d8e8 Mon Sep 17 00:00:00 2001 From: Bernd Porr Date: Sun, 31 Oct 2004 23:19:27 +0000 Subject: [PATCH] Initial version of the new usbduxfast driver. Tested with 2.4.27 and 2.6.9. --- comedi/drivers/usbduxfast.c | 1775 +++++++++++++++++++++++++++++++++++ 1 file changed, 1775 insertions(+) create mode 100644 comedi/drivers/usbduxfast.c diff --git a/comedi/drivers/usbduxfast.c b/comedi/drivers/usbduxfast.c new file mode 100644 index 00000000..2104312e --- /dev/null +++ b/comedi/drivers/usbduxfast.c @@ -0,0 +1,1775 @@ +#define DRIVER_VERSION "v0.6" +#define DRIVER_AUTHOR "Bernd Porr, Bernd.Porr@f2s.com" +#define DRIVER_DESC "USB-DUXfast -- Bernd.Porr@f2s.com" +/* + module/usbduxfast.c + Copyright (C) 2004 Bernd Porr, Bernd.Porr@f2s.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ +/* +Driver: usbduxfast.c +Description: ITL USB-DUXfast +Devices: [ITL] USB-DUX (usbduxfast.o) +Author: Bernd Porr +Updated: 31 Oct 2004 +Status: testing + +*/ +/* + * I must give credit here to Chris Baugher who + * wrote the driver for AT-MIO-16d. I used some parts of this + * driver. I also must give credits to David Brownell + * who supported me with the USB development. + * + * Bernd Porr + * + * + * Revision history: + * + */ + + +//#define CONFIG_COMEDI_DEBUG + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "comedi_fc.h" +#include +#include + +#define BOARDNAME "usbduxfast" + +// timeout for the USB-transfer +#define EZTIMEOUT 3 + +// constants for "firmware" upload and download +#define USBDUXFASTSUB_FIRMWARE 0xA0 +#define VENDOR_DIR_IN 0xC0 +#define VENDOR_DIR_OUT 0x40 + +// internal adresses of the 8051 processor +#define USBDUXFASTSUB_CPUCS 0xE600 + +// max lenghth of the transfer-buffer for software upload +#define TB_LEN 0x2000 + +// Input endpoint number: ISO/IRQ +#define ISOINEP 6 + +// Endpoint for the A/D channellist: bulk OUT +#define CHANNELLISTEP 4 + +// Number of channels +#define NUMCHANNELS 32 + +// size of the waveform descriptor +#define WAVESIZE 0x20 + +// Size of one A/D value +#define SIZEADIN ((sizeof(int16_t))) + +// Size of the input-buffer IN BYTES +#define SIZEINBUF 512 + +// 16 bytes. +#define SIZEINSNBUF 512 + +// Size of the buffer for the dux commands +#define SIZEOFDUXBUFFER 256 // bytes + +// Number of in-URBs which receive the data: min=5 +#define NUMOFINBUFFERSHIGH 10 + +// Total number of usbduxfast devices +#define NUMUSBDUXFAST 16 + +// Number of subdevices +#define N_SUBDEVICES 1 + +// Analogue in subdevice +#define SUBDEV_AD 0 + +// minimal time in nanoseconds between two samples +#define MIN_SAMPLING_PERIOD 9 // steps at 30MHz in the FX2 + +///////////////////////////////////////////// +// comedi constants +static comedi_lrange range_usbduxfast_ai_range = { 2, { + BIP_RANGE( 0.75 ), + BIP_RANGE( 0.5 ), +} }; + + +/* + * private structure of one subdevice + */ + +// This is the structure which holds all the data of this driver +// one sub device just now: A/D +typedef struct { + // attached? + int attached; + // is it associated with a subdevice? + int probed; + // pointer to the usb-device + struct usb_device *usbdev; + // actual number of in-buffers + int numOfInBuffers; + // ISO-transfer handling: buffers + struct urb **urbIn; + // input buffer for single insn + int16_t *insnBuffer; + // interface number + int ifnum; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + // interface structure in 2.6 + struct usb_interface * interface; +#endif + // comedi device for the interrupt context + comedi_device *comedidev; + // asynchronous command is running + short int ai_cmd_running; + // continous aquisition + short int ai_continous; + // number of samples to aquire + long int ai_sample_count; + // time between samples in units of the timer + unsigned int ai_timer; + // counter between aquisitions + unsigned int ai_counter; + // commands + uint8_t *dux_commands; + short int ai_insn_running; + struct semaphore sem; +} usbduxfastsub_t; + + + +// The pointer to the private usb-data of the driver +// is also the private data for the comedi-device. +// This has to be global as the usb subsystem needs +// global variables. The other reason is that this +// structure must be there _before_ any comedi +// command is issued. The usb subsystem must be +// initialised before comedi can access it. +static usbduxfastsub_t usbduxfastsub[NUMUSBDUXFAST]; + + + + +static DECLARE_MUTEX (start_stop_sem); + + + + +// bulk transfers to usbduxfast + +#define SENDADCOMMANDS 0 +#define SENDINITEP6 1 + +static int send_dux_commands(usbduxfastsub_t* this_usbduxfastsub,int cmd_type) { + int result,nsent; +#ifdef CONFIG_COMEDI_DEBUG + int i; + printk("comedi%d: usbduxfast: dux_commands: ", + this_usbduxfastsub->comedidev->minor); + for(i=0;idux_commands[i]); + } + printk("\n"); +#endif + this_usbduxfastsub->dux_commands[0]=cmd_type; + result = usb_bulk_msg(this_usbduxfastsub->usbdev, + usb_sndbulkpipe(this_usbduxfastsub->usbdev, + CHANNELLISTEP), + this_usbduxfastsub->dux_commands, + SIZEOFDUXBUFFER, + &nsent, + 10*HZ); + if (result<0) { + printk("comedi%d: could not transmit dux_commands to the usb-device, err=%d\n", + this_usbduxfastsub->comedidev->minor,result); + } + return result; +} + + + + + +// Stops the data acquision +// It should be safe to call this function from any context +static int usbduxfastsub_unlink_InURBs(usbduxfastsub_t* usbduxfastsub_tmp) { + int i,j=0; + int err=0; + + if (usbduxfastsub_tmp && usbduxfastsub_tmp->urbIn) { + usbduxfastsub_tmp->ai_cmd_running=0; + for (i=0; i < usbduxfastsub_tmp->numOfInBuffers; i++) { + if (usbduxfastsub_tmp->urbIn[i]) { + j=usb_unlink_urb(usbduxfastsub_tmp->urbIn[i]); + if (j<0) { + err=j; + } + } +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi: usbduxfast: unlinked InURB %d: res=%d\n", + i, + j); +#endif + } + } + return err; +} + + + +/* This will stop a running acquisition operation */ +// Is called from within this driver from both the +// interrupt context and from comedi +static int usbduxfast_ai_stop(usbduxfastsub_t* this_usbduxfastsub, + int do_unlink) +{ + int ret=0; + + if (!this_usbduxfastsub) { + printk("comedi?: usbduxfast_ai_stop: this_usbduxfastsub=NULL!\n"); + return -EFAULT; + } + +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi: usbduxfast_ai_stop\n"); +#endif + + + this_usbduxfastsub->ai_cmd_running=0; + + if (do_unlink) { + // stop aquistion + ret=usbduxfastsub_unlink_InURBs(this_usbduxfastsub); + } + + return ret; +} + + + +// This will cancel a running acquisition operation. +// This is called by comedi but never from inside the +// driver. +static int usbduxfast_ai_cancel(comedi_device *dev, + comedi_subdevice *s) +{ + usbduxfastsub_t* this_usbduxfastsub; + int res=0; + + // force unlink of all urbs +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi: usbduxfast_ai_cancel\n"); +#endif + this_usbduxfastsub=dev->private; + if (!this_usbduxfastsub) { + printk("comedi: usbduxfast_ai_cancel: this_usbduxfastsub=NULL\n"); + return -EFAULT; + } + down(&this_usbduxfastsub->sem); + if (!(this_usbduxfastsub->probed)) { + up(&this_usbduxfastsub->sem); + return -ENODEV; + } + // unlink + res=usbduxfast_ai_stop(this_usbduxfastsub,1); + up(&this_usbduxfastsub->sem); + + return res; +} + + + + +// analogue IN +// interrupt service routine +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +static void usbduxfastsub_ai_IsocIrq(struct urb *urb) +#else +static void usbduxfastsub_ai_IsocIrq(struct urb *urb, struct pt_regs *regs) +#endif +{ + int n,err; + usbduxfastsub_t* this_usbduxfastsub; + comedi_device *this_comedidev; + comedi_subdevice *s; + uint16_t* p; + + // sanity checks + // is the urb there? + if (!urb) { + printk("comedi_: usbduxfast_: ao int-handler called with urb=NULL!\n"); + return; + } + + // the context variable points to the subdevice + this_comedidev=urb->context; + if (!this_comedidev) { + printk("comedi_: usbduxfast_: urb context is a NULL pointer!\n"); + return; + } + + // the private structure of the subdevice is usbduxfastsub_t + this_usbduxfastsub=this_comedidev->private; + if (!this_usbduxfastsub) { + printk("comedi_: usbduxfast_: private of comedi subdev is a NULL pointer!\n"); + return; + } + + // are we running a command? + if (unlikely(!(this_usbduxfastsub->ai_cmd_running))) { + // not running a command + // do not continue execution if no asynchronous command is running + // in particular not resubmit + return; + } + + if (unlikely(!(this_usbduxfastsub->attached))) { + // no comedi device there + return; + } + + // subdevice which is the AD converter + s=this_comedidev->subdevices + SUBDEV_AD; + + // first we test if something unusual has just happened + switch (urb->status) { + case 0: + break; + + // happens after an unlink command or when the device is plugged out + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -ECONNABORTED: + // tell this comedi + s->async->events |= COMEDI_CB_EOA; + s->async->events |= COMEDI_CB_ERROR; + comedi_event(this_usbduxfastsub->comedidev, + s, + s->async->events); + // stop the transfer w/o unlink + usbduxfast_ai_stop(this_usbduxfastsub,0); + return; + + case -EILSEQ: + // error in the ISOchronous data + // we don't copy the data into the transfer buffer + // and recycle the last data byte +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi%d: usbduxfast: CRC error in ISO IN stream.\n", + this_usbduxfastsub->comedidev->minor); +#endif + break; + + default: + printk("Non-zero urb status received in ai intr context: %d\n", + urb->status); + s->async->events |= COMEDI_CB_EOA; + s->async->events |= COMEDI_CB_ERROR; + comedi_event(this_usbduxfastsub->comedidev, + s, + s->async->events); + usbduxfast_ai_stop(this_usbduxfastsub,0); + return; + } + + + // get the data from the USB bus and hand it over + // to comedi + p=urb->transfer_buffer; + if (!(this_usbduxfastsub->ai_continous)) { + // not continous, fixed number of samples + n=urb->iso_frame_desc[0].actual_length/sizeof(uint16_t); + if (unlikely(this_usbduxfastsub->ai_sample_counttransfer_buffer, + this_usbduxfastsub->ai_sample_count*sizeof(uint16_t)); + usbduxfast_ai_stop(this_usbduxfastsub, + 0); + // say comedi that the acquistion is over + s->async->events |= COMEDI_CB_EOA; + comedi_event(this_usbduxfastsub->comedidev, + s, + s->async->events); + return; + } + this_usbduxfastsub->ai_sample_count-=n; + } + + // write the full buffer to comedi + cfc_write_array_to_buffer(s, + urb->transfer_buffer, + urb->iso_frame_desc[0].actual_length); + + // tell comedi that data is there + comedi_event(this_usbduxfastsub->comedidev, + s, + s->async->events); + + // command is still running + // resubmit urb for ISO transfer + urb->dev = this_usbduxfastsub->usbdev; + urb->status = 0; + if ((err=USB_SUBMIT_URB(urb))<0) { + printk("comedi_: usbduxfast_: urb resubm failed: %d",err); + if (err==-EL2NSYNC) { + printk(" :buggy USB host controller!\n"); + } else { + printk("\n"); + } + s->async->events |= COMEDI_CB_EOA; + s->async->events |= COMEDI_CB_ERROR; + comedi_event(this_usbduxfastsub->comedidev, + s, + s->async->events); + usbduxfast_ai_stop(this_usbduxfastsub,0); + } +} + + + + + +static int usbduxfastsub_start(usbduxfastsub_t* usbduxfastsub) { + int errcode=0; + unsigned char local_transfer_buffer[16]; + + if (usbduxfastsub->probed) { + // 7f92 to zero + local_transfer_buffer[0]=0; + errcode=usb_control_msg + (usbduxfastsub->usbdev, + // create a pipe for a control transfer + usb_sndctrlpipe(usbduxfastsub->usbdev,0), + // bRequest, "Firmware" + USBDUXFASTSUB_FIRMWARE, + // bmRequestType + VENDOR_DIR_OUT, + // Value + USBDUXFASTSUB_CPUCS, + // Index + 0x0000, + // address of the transfer buffer + local_transfer_buffer, + // Length + 1, + // Timeout + EZTIMEOUT + ); + if (errcode<0) { + printk("comedi_: usbduxfast_: control msg failed (start)\n"); + return errcode; + } + } + return 0; +} + + + + +static int usbduxfastsub_stop(usbduxfastsub_t* usbduxfastsub) { + int errcode=0; + + unsigned char local_transfer_buffer[16]; + if (usbduxfastsub->probed) { + // 7f92 to one + local_transfer_buffer[0]=1; + errcode=usb_control_msg + (usbduxfastsub->usbdev, + usb_sndctrlpipe(usbduxfastsub->usbdev,0), + // bRequest, "Firmware" + USBDUXFASTSUB_FIRMWARE, + // bmRequestType + VENDOR_DIR_OUT, + // Value + USBDUXFASTSUB_CPUCS, + // Index + 0x0000, + local_transfer_buffer, + // Length + 1, + // Timeout + EZTIMEOUT + ); + if (errcode<0) { + printk("comedi_: usbduxfast: control msg failed (stop)\n"); + return errcode; + } + } + return 0; +} + + + + + +static int usbduxfastsub_upload(usbduxfastsub_t* usbduxfastsub, + unsigned char* local_transfer_buffer, + unsigned int startAddr, + unsigned int len) { + int errcode; + + if (usbduxfastsub->probed) { +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi%d: usbduxfast: uploading %d bytes", + usbduxfastsub->comedidev->minor,len); + printk(" to addr %d, first byte=%d.\n", + startAddr, + local_transfer_buffer[0]); +#endif + errcode=usb_control_msg + (usbduxfastsub->usbdev, + usb_sndctrlpipe(usbduxfastsub->usbdev, 0), + // brequest, firmware + USBDUXFASTSUB_FIRMWARE, + // bmRequestType + VENDOR_DIR_OUT, + // value + startAddr, + // index + 0x0000, + // our local safe buffer + local_transfer_buffer, + // length + len, + // timeout + EZTIMEOUT + ); +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi_: usbduxfast: result=%d\n",errcode); +#endif + if (errcode<0) { + printk("comedi_: usbduxfast: uppload failed\n"); + return errcode; + } + } else { + // no device on the bus for this index + return -EFAULT; + } +return 0; +} + + + + + +int firmwareUpload(usbduxfastsub_t* usbduxfastsub, + unsigned char* firmwareBinary, + int sizeFirmware) { + int ret; + + if (!firmwareBinary) { + return 0; + } + ret=usbduxfastsub_stop(usbduxfastsub); + if (ret<0) { + printk("comedi_: usbduxfast: can not stop firmware\n" + ); + return ret; + } + ret=usbduxfastsub_upload(usbduxfastsub, + firmwareBinary, + 0, + sizeFirmware); + if (ret<0) { + printk("comedi_: usbduxfast: firmware upload failed\n" + ); + return ret; + } + ret=usbduxfastsub_start(usbduxfastsub); + if (ret<0) { + printk("comedi_: usbduxfast: can not start firmware\n" + ); + return ret; + } + return 0; +} + + + +int usbduxfastsub_submit_InURBs(usbduxfastsub_t* usbduxfastsub) { + int i,errFlag; + + if (!usbduxfastsub) { + return -EFAULT; + } + /* Submit all URBs and start the transfer on the bus */ + for (i=0; i < usbduxfastsub->numOfInBuffers; i++) { + // in case of a resubmission after an unlink... + usbduxfastsub->urbIn[i]->context=usbduxfastsub->comedidev; + usbduxfastsub->urbIn[i]->dev = usbduxfastsub->usbdev; + usbduxfastsub->urbIn[i]->status = 0; + usbduxfastsub->urbIn[i]->transfer_flags = URB_ISO_ASAP; +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi%d: usbduxfast: submitting in-urb[%d]: %x,%x\n", + usbduxfastsub->comedidev->minor, + i, + (int)(usbduxfastsub->urbIn[i]->context), + (int)(usbduxfastsub->urbIn[i]->dev)); +#endif + errFlag = USB_SUBMIT_URB + (usbduxfastsub->urbIn[i]); + if (errFlag) { + printk("comedi_: usbduxfast: ai: "); + printk("USB_SUBMIT_URB(%d)",i); + printk(" error %d\n",errFlag); + return errFlag; + } + } + return 0; +} + + + + +static int usbduxfast_ai_cmdtest(comedi_device *dev, + comedi_subdevice *s, + comedi_cmd *cmd) +{ + int err=0, tmp=0, stop_mask=0, steps; + usbduxfastsub_t* this_usbduxfastsub=dev->private; + if (!(this_usbduxfastsub->probed)) { + return -ENODEV; + } + +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi%d: usbduxfast_ai_cmdtest\n",dev->minor); + printk("comedi%d: usbduxfast: convert_arg=%u scan_begin_arg=%u\n", + dev->minor, + cmd->convert_arg, + cmd->scan_begin_arg); +#endif + /* step 1: make sure trigger sources are trivially valid */ + + tmp = cmd->start_src; + cmd->start_src &= TRIG_NOW | TRIG_EXT; + if(!cmd->start_src || tmp != cmd->start_src) err++; + + tmp = cmd->scan_begin_src; + cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT; + if(!cmd->scan_begin_src || tmp != cmd->scan_begin_src) err++; + + tmp = cmd->convert_src; + cmd->convert_src &= TRIG_TIMER | TRIG_EXT; + if(!cmd->convert_src || tmp != cmd->convert_src) err++; + + tmp = cmd->scan_end_src; + cmd->scan_end_src &= TRIG_COUNT; + if(!cmd->scan_end_src || tmp != cmd->scan_end_src) err++; + + tmp=cmd->stop_src; + stop_mask = TRIG_COUNT | TRIG_NONE; + cmd->stop_src &= stop_mask; + if(!cmd->stop_src || tmp!=cmd->stop_src) err++; + + if(err) return 1; + + /* step 2: make sure trigger sources are unique and mutually compatible */ + + if(cmd->start_src != TRIG_NOW && + cmd->start_src != TRIG_EXT) err++; + if(cmd->scan_begin_src != TRIG_TIMER && + cmd->scan_begin_src != TRIG_FOLLOW && + cmd->scan_begin_src != TRIG_EXT) err++; + if(cmd->convert_src != TRIG_TIMER && + cmd->convert_src != TRIG_EXT) err++; + if(cmd->stop_src != TRIG_COUNT && + cmd->stop_src != TRIG_EXT && + cmd->stop_src != TRIG_NONE) err++; + + // can't have external stop and start triggers at once + if(cmd->start_src == TRIG_EXT && + cmd->stop_src == TRIG_EXT) err++; + + if(err)return 2; + + /* step 3: make sure arguments are trivially compatible */ + + if(cmd->start_arg == TRIG_NOW && cmd->start_arg != 0) + { + cmd->start_arg = 0; + err++; + } + + if(!cmd->chanlist_len) + { + err++; + } + if(cmd->scan_end_arg != cmd->chanlist_len) + { + cmd->scan_end_arg = cmd->chanlist_len; + err++; + } + + if(cmd->convert_src == TRIG_TIMER) + { + steps=(cmd->convert_arg*30)/1000; + if (stepsconvert_arg != tmp) + { + cmd->convert_arg = tmp; + err++; + } + } + + if(cmd->scan_begin_src == TRIG_TIMER) + { + err++; + } + + // stop source + switch(cmd->stop_src) + { + case TRIG_COUNT: + if(!cmd->stop_arg) + { + cmd->stop_arg = 1; + err++; + } + break; + case TRIG_NONE: + if(cmd->stop_arg != 0) + { + cmd->stop_arg = 0; + err++; + } + break; + // TRIG_EXT doesn't care since it doesn't trigger off a numbered channel + default: + break; + } + + if(err)return 3; + + /* step 4: fix up any arguments */ + + return 0; + +} + + + + +static int usbduxfast_ai_inttrig(comedi_device *dev, + comedi_subdevice *s, + unsigned int trignum) +{ + int ret; + usbduxfastsub_t* this_usbduxfastsub=dev->private; + if (!this_usbduxfastsub) { + return -EFAULT; + } + down(&this_usbduxfastsub->sem); + if (!(this_usbduxfastsub->probed)) { + up(&this_usbduxfastsub->sem); + return -ENODEV; + } + +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi%d: usbduxfast_ai_inttrig\n",dev->minor); +#endif + + if (trignum!=0) { + printk("comedi%d: usbduxfast_ai_inttrig: invalid trignum\n",dev->minor); + up(&this_usbduxfastsub->sem); + return -EINVAL; + } + if (!(this_usbduxfastsub->ai_cmd_running)) { + this_usbduxfastsub->ai_cmd_running=1; + ret=usbduxfastsub_submit_InURBs(this_usbduxfastsub); + if (ret<0) { + printk("comedi%d: usbduxfast_ai_inttrig: urbSubmit: err=%d\n", + dev->minor,ret); + this_usbduxfastsub->ai_cmd_running=0; + up(&this_usbduxfastsub->sem); + return ret; + } + s->async->inttrig = NULL; + } else { + printk("comedi%d: ai_inttrig but acqu is already running\n", + dev->minor); + } + up(&this_usbduxfastsub->sem); + return 1; +} + + + + + + + + + +static int usbduxfast_ai_cmd(comedi_device *dev, comedi_subdevice *s) +{ + comedi_cmd *cmd = &s->async->cmd; + unsigned int chan,gain,rngmask=0xff; + int i,j,ret; + usbduxfastsub_t* this_usbduxfastsub=dev->private; + int result; + long steps,steps_tmp; + +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi%d: usbduxfast_ai_cmd\n",dev->minor); +#endif + if (!this_usbduxfastsub) { + return -EFAULT; + } + down(&this_usbduxfastsub->sem); + if (!(this_usbduxfastsub->probed)) { + up(&this_usbduxfastsub->sem); + return -ENODEV; + } + if (this_usbduxfastsub->ai_insn_running) { + printk("comedi%d: ai_cmd not possible. Sync command is running.\n", + dev->minor); + up(&this_usbduxfastsub->sem); + return -EBUSY; + } + if (this_usbduxfastsub->ai_cmd_running) { + printk("comedi%d: ai_cmd not possible. Another ai_cmd is running.\n", + dev->minor); + up(&this_usbduxfastsub->sem); + return -EBUSY; + } + + // set current channel of the running aquisition to zero + s->async->cur_chan = 0; + + if (cmd->chanlist_len>0) { + gain = CR_RANGE(cmd->chanlist[0]); + for(i=0; i < cmd->chanlist_len; ++i ) { + chan = CR_CHAN(cmd->chanlist[i]); + if (chan!=i) { + printk("comedi%d: cmd is accepting only consecutive channels.\n", + dev->minor); + up(&this_usbduxfastsub->sem); + return -EINVAL; + } + if ((gain != CR_RANGE(cmd->chanlist[i]))&&(cmd->chanlist_len>3)) { + printk("comedi%d: the gain must be the same for all channels.\n", + dev->minor); + up(&this_usbduxfastsub->sem); + return -EINVAL; + } + if (i>=NUMCHANNELS) { + printk("comedi%d: channel list too long\n",dev->minor); + break; + } + } + } + steps=0; + if(cmd->scan_begin_src == TRIG_TIMER) { + steps=(cmd->scan_begin_arg*30/cmd->chanlist_len)/1000; + } + if(cmd->convert_src == TRIG_TIMER) { + steps=(cmd->convert_arg*30)/1000; + } + if (steps<8) { + printk("comedi%d: usbduxfast: ai_cmd: steps=%ld, scan_begin_arg=%d. Not properly tested by cmdtest?\n", + dev->minor, + steps, + cmd->scan_begin_arg); + up(&this_usbduxfastsub->sem); + return -EFAULT; + } + + if (steps<500) { + // do the delay in the FX2 + // timer always one. We take all data coming from the FX2 + this_usbduxfastsub->ai_timer = 1; + } else { + steps=500; + this_usbduxfastsub->ai_timer = cmd->convert_arg/16666; + if (this_usbduxfastsub->ai_timer<1) { + printk("comedi%d: usbduxfast: ai_cmd: ai_timer<1. Aborting.\n", + dev->minor); + up(&this_usbduxfastsub->sem); + return -EFAULT; + } + } +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi%d: usbduxfast: steps=%ld, convert_arg=%u, ai_timer=%u\n", + dev->minor, + steps, + cmd->convert_arg, + this_usbduxfastsub->ai_timer); +#endif + + + // the first byte is the command byte +#define LENBASE 1+0x00 +#define OPBASE 1+0x08 +#define OUTBASE 1+0x10 +#define LOGBASE 1+0x18 + + + switch (cmd->chanlist_len) { + // one channel + case 1: + if (CR_RANGE(cmd->chanlist[0])>0) rngmask=0xff-0x04; else rngmask=0xff; + // commit data to the FIFO + this_usbduxfastsub->dux_commands[LENBASE+0]=1; + this_usbduxfastsub->dux_commands[OPBASE+0]=0x02; // data + this_usbduxfastsub->dux_commands[OUTBASE+0]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+0]=0; + + // we have 6 states with duration 1 + steps=steps-6; + + // do the first part of the delay + this_usbduxfastsub->dux_commands[LENBASE+1]=steps/2; + this_usbduxfastsub->dux_commands[OPBASE+1]=0; + this_usbduxfastsub->dux_commands[OUTBASE+1]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+1]=0; + + // and the second part + this_usbduxfastsub->dux_commands[LENBASE+2]=steps-steps/2; + this_usbduxfastsub->dux_commands[OPBASE+2]=0; + this_usbduxfastsub->dux_commands[OUTBASE+2]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+2]=0; + + this_usbduxfastsub->dux_commands[LENBASE+3]=1; + this_usbduxfastsub->dux_commands[OPBASE+3]=0; + this_usbduxfastsub->dux_commands[OUTBASE+3]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+3]=0; + + this_usbduxfastsub->dux_commands[LENBASE+4]=1; + this_usbduxfastsub->dux_commands[OPBASE+4]=0; + this_usbduxfastsub->dux_commands[OUTBASE+4]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+4]=0; + + this_usbduxfastsub->dux_commands[LENBASE+5]=1; + this_usbduxfastsub->dux_commands[OPBASE+5]=0; + this_usbduxfastsub->dux_commands[OUTBASE+5]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+5]=0; + + this_usbduxfastsub->dux_commands[LENBASE+6]=1; + this_usbduxfastsub->dux_commands[OPBASE+6]=0; + this_usbduxfastsub->dux_commands[OUTBASE+6]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+6]=0; + break; + + case 2: + // two channels + // commit data to the FIFO + if (CR_RANGE(cmd->chanlist[0])>0) rngmask=0xff-0x04; else rngmask=0xff; + this_usbduxfastsub->dux_commands[LENBASE+0]=1; + this_usbduxfastsub->dux_commands[OPBASE+0]=0x02; // data + this_usbduxfastsub->dux_commands[OUTBASE+0]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+0]=0; + + // we have 1 state with duration 1: state 0 + steps_tmp=steps-1; + + if (CR_RANGE(cmd->chanlist[1])>0) rngmask=0xff-0x04; else rngmask=0xff; + // do the first part of the delay + this_usbduxfastsub->dux_commands[LENBASE+1]=steps_tmp/2; + this_usbduxfastsub->dux_commands[OPBASE+1]=0; + this_usbduxfastsub->dux_commands[OUTBASE+1]=0xFE & rngmask; //count + this_usbduxfastsub->dux_commands[LOGBASE+1]=0; + + // and the second part + this_usbduxfastsub->dux_commands[LENBASE+2]=steps_tmp-steps_tmp/2; + this_usbduxfastsub->dux_commands[OPBASE+2]=0; + this_usbduxfastsub->dux_commands[OUTBASE+2]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+2]=0; + + this_usbduxfastsub->dux_commands[LENBASE+3]=1; + this_usbduxfastsub->dux_commands[OPBASE+3]=0x02; // data + this_usbduxfastsub->dux_commands[OUTBASE+3]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+3]=0; + + // we have 2 states with duration 1: step 6 and the IDLE state + steps_tmp=steps-2; + + if (CR_RANGE(cmd->chanlist[0])>0) rngmask=0xff-0x04; else rngmask=0xff; + // do the first part of the delay + this_usbduxfastsub->dux_commands[LENBASE+4]=steps_tmp/2; + this_usbduxfastsub->dux_commands[OPBASE+4]=0; + this_usbduxfastsub->dux_commands[OUTBASE+4]=(0xFF-0x02) & rngmask; //reset + this_usbduxfastsub->dux_commands[LOGBASE+4]=0; + + // and the second part + this_usbduxfastsub->dux_commands[LENBASE+5]=steps_tmp-steps_tmp/2; + this_usbduxfastsub->dux_commands[OPBASE+5]=0; + this_usbduxfastsub->dux_commands[OUTBASE+5]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+5]=0; + + this_usbduxfastsub->dux_commands[LENBASE+6]=1; + this_usbduxfastsub->dux_commands[OPBASE+6]=0; + this_usbduxfastsub->dux_commands[OUTBASE+6]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+6]=0; + break; + + case 3: + // three channels + for(j=0;j<1;j++) { + if (CR_RANGE(cmd->chanlist[j])>0) rngmask=0xff-0x04; else rngmask=0xff; + // commit data to the FIFO and do the first part of the delay + this_usbduxfastsub->dux_commands[LENBASE+j*2]=steps/2; + this_usbduxfastsub->dux_commands[OPBASE+j*2]=0x02; // data + this_usbduxfastsub->dux_commands[OUTBASE+j*2]=0xFF & rngmask; // no change + this_usbduxfastsub->dux_commands[LOGBASE+j*2]=0; + + if (CR_RANGE(cmd->chanlist[j+1])>0) rngmask=0xff-0x04; else rngmask=0xff; + // do the second part of the delay + this_usbduxfastsub->dux_commands[LENBASE+j*2+1]=steps-steps/2; + this_usbduxfastsub->dux_commands[OPBASE+j*2+1]=0; // no data + this_usbduxfastsub->dux_commands[OUTBASE+j*2+1]=0xFE & rngmask; //count + this_usbduxfastsub->dux_commands[LOGBASE+j*2+1]=0; + } + + // 2 steps with duration 1: the idele step and step 6: + steps_tmp=steps-2; + // commit data to the FIFO and do the first part of the delay + this_usbduxfastsub->dux_commands[LENBASE+4]=steps_tmp/2; + this_usbduxfastsub->dux_commands[OPBASE+4]=0x02; // data + this_usbduxfastsub->dux_commands[OUTBASE+4]=0xFF & rngmask; // no change + this_usbduxfastsub->dux_commands[LOGBASE+4]=0; + + if (CR_RANGE(cmd->chanlist[0])>0) rngmask=0xff-0x04; else rngmask=0xff; + // do the second part of the delay + this_usbduxfastsub->dux_commands[LENBASE+5]=steps_tmp-steps_tmp/2; + this_usbduxfastsub->dux_commands[OPBASE+5]=0; // no data + this_usbduxfastsub->dux_commands[OUTBASE+5]=(0xFF-0x02) & rngmask; // reset + this_usbduxfastsub->dux_commands[LOGBASE+5]=0; + + this_usbduxfastsub->dux_commands[LENBASE+6]=1; + this_usbduxfastsub->dux_commands[OPBASE+6]=0; + this_usbduxfastsub->dux_commands[OUTBASE+6]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+6]=0; + + case 16: + if (CR_RANGE(cmd->chanlist[0])>0) rngmask=0xff-0x04; else rngmask=0xff; + // commit data to the FIFO + this_usbduxfastsub->dux_commands[LENBASE+0]=1; + this_usbduxfastsub->dux_commands[OPBASE+0]=0x02; // data + this_usbduxfastsub->dux_commands[OUTBASE+0]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+0]=0; + + // we have 6 states with duration 1 + steps=steps-6; + + // do the first part of the delay + this_usbduxfastsub->dux_commands[LENBASE+1]=steps/2; + this_usbduxfastsub->dux_commands[OPBASE+1]=0; + this_usbduxfastsub->dux_commands[OUTBASE+1]=0xFE & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+1]=0; + + // and the second part + this_usbduxfastsub->dux_commands[LENBASE+2]=steps-steps/2; + this_usbduxfastsub->dux_commands[OPBASE+2]=0; + this_usbduxfastsub->dux_commands[OUTBASE+2]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+2]=0; + + this_usbduxfastsub->dux_commands[LENBASE+3]=1; + this_usbduxfastsub->dux_commands[OPBASE+3]=0; + this_usbduxfastsub->dux_commands[OUTBASE+3]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+3]=0; + + this_usbduxfastsub->dux_commands[LENBASE+4]=1; + this_usbduxfastsub->dux_commands[OPBASE+4]=0; + this_usbduxfastsub->dux_commands[OUTBASE+4]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+4]=0; + + this_usbduxfastsub->dux_commands[LENBASE+5]=1; + this_usbduxfastsub->dux_commands[OPBASE+5]=0; + this_usbduxfastsub->dux_commands[OUTBASE+5]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+5]=0; + + this_usbduxfastsub->dux_commands[LENBASE+6]=1; + this_usbduxfastsub->dux_commands[OPBASE+6]=0; + this_usbduxfastsub->dux_commands[OUTBASE+6]=0xFF & rngmask; + this_usbduxfastsub->dux_commands[LOGBASE+0]=0; + break; + + default: + printk("comedi %d: unsupported combination of channels\n", + dev->minor); + up(&this_usbduxfastsub->sem); + return -EFAULT; + } + + +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi %d: sending commands to the usb device\n", + dev->minor); +#endif + // 0 means that the AD commands are sent + result=send_dux_commands(this_usbduxfastsub,SENDADCOMMANDS); + if (result<0) { + printk("comedi%d: adc command could not be submitted. Aborting...\n", + dev->minor); + up(&this_usbduxfastsub->sem); + return result; + } + + this_usbduxfastsub->ai_counter=this_usbduxfastsub->ai_timer; + + if(cmd->stop_src==TRIG_COUNT){ + this_usbduxfastsub->ai_sample_count = (cmd->stop_arg)*(cmd->scan_end_arg); + if (usbduxfastsub->ai_sample_count<1) { + printk("comedi%d: (cmd->stop_arg)*(cmd->scan_end_arg)<1, aborting.\n", + dev->minor); + up(&this_usbduxfastsub->sem); + return -EFAULT; + } + this_usbduxfastsub->ai_continous=0; + } else { + // continous aquisition + this_usbduxfastsub->ai_continous=1; + this_usbduxfastsub->ai_sample_count=0; + } + + if(cmd->start_src == TRIG_NOW){ + // enable this acquisition operation + this_usbduxfastsub->ai_cmd_running=1; + ret=usbduxfastsub_submit_InURBs(this_usbduxfastsub); + if (ret<0) { + this_usbduxfastsub->ai_cmd_running=0; + // fixme: unlink here?? + up(&this_usbduxfastsub->sem); + return ret; + } + s->async->inttrig = NULL; + }else{ + /* TRIG_INT */ + // don't enable the acquision operation + // wait for an internal signal + s->async->inttrig = usbduxfast_ai_inttrig; + } + up(&this_usbduxfastsub->sem); + + return 0; +} + + + +static unsigned hex2unsigned(char *h) { + unsigned hi,lo; + if (h[0]>'9') { + hi=h[0]-'A'+0x0a; + } else { + hi=h[0]-'0'; + } + if (h[1]>'9') { + lo=h[1]-'A'+0x0a; + } else { + lo=h[1]-'0'; + } + return hi*0x10+lo; +} + + +// for FX2 +#define FIRMWARE_MAX_LEN 0x2000 + +// taken from David Brownell's fxload and adjusted for this driver +static int read_firmware(usbduxfastsub_t* usbduxfastsub,int firmwarePtr,long size) { + int i=0; + unsigned char* fp=(char*)firmwarePtr; + unsigned char* firmwareBinary=NULL; + int res=0; + int maxAddr=0; + + firmwareBinary = kmalloc(FIRMWARE_MAX_LEN,GFP_KERNEL); + if(!firmwareBinary){ + printk("comedi_: usbduxfast: mem alloc for firmware failed\n" + ); + return -ENOMEM; + } + + for (;;) { + char buf[256],*cp; + char type; + int len; + int idx, off; + int j=0; + + // get one line + while ((i=sizeof(buf)) { + printk("comedi_: usbduxfast: bogus firmware file!\n"); + return -1; + } + } + // get rid of LF/CR/... + while ((imaxAddr) { + maxAddr=off+len; + } + + if (maxAddr>=FIRMWARE_MAX_LEN) { + printk("comedi_: usbduxfast: firmware upload goes beyond FX2 RAM boundaries."); + return -EFAULT; + } + + //printk("comedi_: usbduxfast: off=%x, len=%x:",off,len); + + /* Read the record type */ + type = hex2unsigned(buf+7); + + /* If this is an EOF record, then make it so. */ + if (type == 1) { + break; + } + + if (type != 0) { + printk("comedi_: usbduxfast: unsupported record type: %u\n",type); + return -EFAULT; + } + + for (idx = 0, cp = buf+9 ; idx < len ; idx += 1, cp += 2) { + firmwareBinary[idx+off] = hex2unsigned(cp); + //printk("%02x ",firmwareBinary[idx+off]); + } + //printk("\n"); + + if (i>=size) { + printk("comedi_: usbduxfast: unexpected end of hex file\n"); + break; + } + + } + res=firmwareUpload(usbduxfastsub,firmwareBinary,maxAddr+1); + kfree(firmwareBinary); + return res; +} + + + +static void tidy_up(usbduxfastsub_t* usbduxfastsub_tmp) { + int i; + +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi_: usbduxfast: tiding up\n"); +#endif + if (!usbduxfastsub_tmp) { + return; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + // shows the usb subsystem that the driver is down + if (usbduxfastsub_tmp->interface) { + usb_set_intfdata(usbduxfastsub_tmp->interface,NULL); + } +#endif + + usbduxfastsub_tmp->probed=0; + + if (usbduxfastsub_tmp->urbIn) { + for (i=0; i < usbduxfastsub_tmp->numOfInBuffers; i++) { + if (usbduxfastsub_tmp->urbIn[i]) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + // waits until a running transfer is over + // thus, under 2.4 hotplugging while a command + // is running is not safe + usb_kill_urb(usbduxfastsub_tmp->urbIn[i]); +#endif + if (usbduxfastsub_tmp->urbIn[i]->transfer_buffer) { + kfree(usbduxfastsub_tmp->urbIn[i]->transfer_buffer); + usbduxfastsub_tmp->urbIn[i]->transfer_buffer=NULL; + } + usb_free_urb(usbduxfastsub_tmp->urbIn[i]); + usbduxfastsub_tmp->urbIn[i]=NULL; + } + } + kfree(usbduxfastsub_tmp->urbIn); + usbduxfastsub_tmp->urbIn=NULL; + } + if (usbduxfastsub_tmp->insnBuffer) { + kfree(usbduxfastsub_tmp->insnBuffer); + usbduxfastsub_tmp->insnBuffer=NULL; + } + if (usbduxfastsub_tmp->dux_commands) { + kfree(usbduxfastsub_tmp->dux_commands); + usbduxfastsub_tmp->dux_commands=NULL; + } + usbduxfastsub_tmp->ai_cmd_running=0; + usbduxfastsub_tmp->ai_insn_running=0; +} + + + + + + + + + + + + + +// allocate memory for the urbs and initialise them +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +static void* usbduxfastsub_probe(struct usb_device *udev, + unsigned int interfnum, + const struct usb_device_id *id) { +#else +static int usbduxfastsub_probe(struct usb_interface *uinterf, + const struct usb_device_id *id) { + struct usb_device *udev = interface_to_usbdev(uinterf); +#endif + int i; + int index; + + + if (udev->speed!=USB_SPEED_HIGH) { + printk("comedi_: usbduxfast_: This driver needs USB 2.0 to operate. Aborting...\n"); + return PROBE_ERR_RETURN(-ENODEV); + } + + +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi_: usbduxfast_: finding a free structure for the usb-device\n"); +#endif + down(&start_stop_sem); + // look for a free place in the usbduxfast array + index=-1; + for(i=0; ialtsetting->desc.bInterfaceNumber; + // hand the private data over to the usb subsystem + // will be needed for disconnect + usb_set_intfdata(uinterf,&(usbduxfastsub[index])); +#endif + +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi_: usbduxfast: ifnum=%d\n",usbduxfastsub[index].ifnum); +#endif + // create space for the commands going to the usb device + usbduxfastsub[index].dux_commands=kmalloc(SIZEOFDUXBUFFER, + GFP_KERNEL); + if (!usbduxfastsub[index].dux_commands) { + printk("comedi_: usbduxfast: error alloc space for dac commands\n"); + tidy_up(&(usbduxfastsub[index])); + up(&start_stop_sem); + return PROBE_ERR_RETURN( -ENOMEM); + } + + // create space of the instruction buffer + usbduxfastsub[index].insnBuffer=kmalloc(SIZEINSNBUF,GFP_KERNEL); + if (!(usbduxfastsub[index].insnBuffer)) { + printk("comedi_: usbduxfast: could not alloc space for insnBuffer\n"); + tidy_up(&(usbduxfastsub[index])); + up(&start_stop_sem); + return PROBE_ERR_RETURN( -ENOMEM); + } + + // setting to alternate setting 3: enabling iso ep and bulk ep. + i=usb_set_interface(usbduxfastsub[index].usbdev, + usbduxfastsub[index].ifnum, + 3); + if (i<0) { + printk("comedi_: usbduxfast%d: could not set alternate setting 3 in high speed.\n",index); + tidy_up(&(usbduxfastsub[index])); + up(&start_stop_sem); + return PROBE_ERR_RETURN( -ENODEV); + } + usbduxfastsub[index].numOfInBuffers=NUMOFINBUFFERSHIGH; + usbduxfastsub[index].urbIn=kmalloc(sizeof(struct urb*)*usbduxfastsub[index].numOfInBuffers, + GFP_KERNEL); + if (!(usbduxfastsub[index].urbIn)) { + printk("comedi_: usbduxfast: Could not alloc. urbIn array\n"); + tidy_up(&(usbduxfastsub[index])); + up(&start_stop_sem); + return PROBE_ERR_RETURN( -ENOMEM); + } + for (i=0; i < usbduxfastsub[index].numOfInBuffers; i++) { + // one frame: 1ms + usbduxfastsub[index].urbIn[i]=USB_ALLOC_URB(1); + if (usbduxfastsub[index].urbIn[i]==NULL) { + printk("comedi_: usbduxfast%d: Could not alloc. urb(%d)\n",index,i); + tidy_up(&(usbduxfastsub[index])); + up(&start_stop_sem); + return PROBE_ERR_RETURN( -ENOMEM); + } + usbduxfastsub[index].urbIn[i]->dev = usbduxfastsub[index].usbdev; + // will be filled later with a pointer to the comedi-device + // and ONLY then the urb should be submitted + usbduxfastsub[index].urbIn[i]->context = NULL; + usbduxfastsub[index].urbIn[i]->pipe = + usb_rcvisocpipe(usbduxfastsub[index].usbdev, ISOINEP); + usbduxfastsub[index].urbIn[i]->transfer_flags = URB_ISO_ASAP; + usbduxfastsub[index].urbIn[i]->transfer_buffer= + kmalloc(SIZEINBUF,GFP_KERNEL); + if (!(usbduxfastsub[index].urbIn[i]->transfer_buffer)) { + printk("comedi_: usbduxfast%d: could not alloc. transb.\n",index); + tidy_up(&(usbduxfastsub[index])); + up(&start_stop_sem); + return PROBE_ERR_RETURN( -ENOMEM); + } + usbduxfastsub[index].urbIn[i]->complete = usbduxfastsub_ai_IsocIrq; + usbduxfastsub[index].urbIn[i]->number_of_packets = 1; + usbduxfastsub[index].urbIn[i]->transfer_buffer_length = SIZEINBUF; + usbduxfastsub[index].urbIn[i]->iso_frame_desc[0].offset = 0; + usbduxfastsub[index].urbIn[i]->iso_frame_desc[0].length = SIZEINBUF; + // 1 microframe + usbduxfastsub[index].urbIn[i]->interval=1; + } + + // we've reached the bottom of the function + usbduxfastsub[index].probed=1; + up(&start_stop_sem); + printk("comedi_: usbduxfast%d has been successfully initialized.\n",index); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + return (void*)(&usbduxfastsub[index]); +#else + // success + return 0; +#endif +} + + + + + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +static void usbduxfastsub_disconnect(struct usb_device *udev, void *ptr) { + usbduxfastsub_t* usbduxfastsub_tmp=(usbduxfastsub_t*)ptr; +#else +static void usbduxfastsub_disconnect(struct usb_interface *intf) { + usbduxfastsub_t* usbduxfastsub_tmp= usb_get_intfdata(intf); + struct usb_device *udev = interface_to_usbdev(intf); +#endif + if (!usbduxfastsub_tmp) { + printk("comedi_: usbduxfast: disconnect called with null pointer.\n"); + return; + } + if (usbduxfastsub_tmp->usbdev!=udev) { + printk("comedi_: usbduxfast: BUG! called with wrong ptr!!!\n"); + return; + } + down(&start_stop_sem); + down(&usbduxfastsub_tmp->sem); + tidy_up(usbduxfastsub_tmp); + up(&usbduxfastsub_tmp->sem); + up(&start_stop_sem); +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi_: usbduxfast: disconnected from the usb\n"); +#endif +} + + + + + +// is called when comedi-config is called +static int usbduxfast_attach(comedi_device * dev, comedi_devconfig * it) +{ + int ret; + int index; + int i; + comedi_subdevice *s=NULL; + dev->private=NULL; + + down(&start_stop_sem); + // find a valid device which has been detected by the probe function of the usb + index=-1; + for(i=0;iminor); + up(&start_stop_sem); + return -ENODEV; + } + + down(&(usbduxfastsub[index].sem)); + // pointer back to the corresponding comedi device + usbduxfastsub[index].comedidev=dev; + + // trying to upload the firmware into the chip + if(it->options[COMEDI_DEVCONF_AUX_DATA] && + it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]){ + read_firmware(usbduxfastsub, + it->options[COMEDI_DEVCONF_AUX_DATA], + it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]); + } + + dev->board_name = BOARDNAME; + + /* set number of subdevices */ + dev->n_subdevices=N_SUBDEVICES; + + // allocate space for the subdevices + if((ret=alloc_subdevices(dev,N_SUBDEVICES))<0) { + printk("comedi%d: usbduxfast: error alloc space for subdev\n", + dev->minor); + up(&start_stop_sem); + return ret; + } + + printk("comedi%d: usbduxfast: usb-device %d is attached to comedi.\n", + dev->minor,index); + // private structure is also simply the usb-structure + dev->private=usbduxfastsub+index; + // the first subdevice is the A/D converter + s = dev->subdevices + SUBDEV_AD; + // the URBs get the comedi subdevice + // which is responsible for reading + // this is the subdevice which reads data + dev->read_subdev = s; + // the subdevice receives as private structure the + // usb-structure + s->private=NULL; + // analog input + s->type=COMEDI_SUBD_AI; + // readable and ref is to ground + s->subdev_flags=SDF_READABLE|SDF_GROUND; + // 8 channels + s->n_chan=16; + // length of the channellist + s->len_chanlist=16; + // callback functions + //s->insn_read = usbduxfast_ai_insn_read; + s->do_cmdtest=usbduxfast_ai_cmdtest; + s->do_cmd=usbduxfast_ai_cmd; + s->cancel=usbduxfast_ai_cancel; + // max value from the A/D converter (12bit) + s->maxdata=0xfff; + // range table to convert to physical units + s->range_table = &range_usbduxfast_ai_range; + + // finally decide that it's attached + usbduxfastsub[index].attached=1; + + up(&(usbduxfastsub[index].sem)); + + up(&start_stop_sem); + + printk("comedi%d: successfully attached to usbduxfast.\n", + dev->minor); + + return 0; +} + + + + + + +static int usbduxfast_detach(comedi_device * dev) { + usbduxfastsub_t* usbduxfastsub_tmp; + +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi%d: usbduxfast: detach usb device\n", + dev->minor); +#endif + + if (!dev) { + printk("comedi?: usbduxfast: detach without dev variable...\n"); + return -EFAULT; + } + + usbduxfastsub_tmp=dev->private; + if (!usbduxfastsub_tmp) { + printk("comedi?: usbduxfast: detach without ptr to usbduxfastsub[]\n"); + return -EFAULT; + } + + down(&usbduxfastsub_tmp->sem); + down(&start_stop_sem); + // Don't allow detach to free the private structure + // It's one entry of of usbduxfastsub[] + dev->private=NULL; + usbduxfastsub_tmp->attached=0; + usbduxfastsub_tmp->comedidev=NULL; +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi%d: usbduxfast: detach: successfully removed\n", + dev->minor); +#endif + up(&start_stop_sem); + up(&usbduxfastsub_tmp->sem); + return 0; +} + + + + + + + + +/* main driver struct */ +static comedi_driver driver_usbduxfast={ + driver_name: "usbduxfast", + module: THIS_MODULE, + attach: usbduxfast_attach, + detach: usbduxfast_detach, +}; + + + + + + + + + + +static void init_usb_devices(void) { + int index; +#ifdef CONFIG_COMEDI_DEBUG + printk("comedi_: usbduxfast: setting all possible devs to invalid\n"); +#endif + // all devices entries are invalid to begin with + // they will become valid by the probe function + // and then finally by the attach-function + for(index=0;index KERNEL_VERSION(2,4,19) + owner: THIS_MODULE, +#endif + name: BOARDNAME, + probe: usbduxfastsub_probe, + disconnect: usbduxfastsub_disconnect, + id_table: usbduxfastsub_table, +}; + + + + + + + +// Can't use the nice macro as I have also to initialise the USB +// subsystem: +// registering the usb-system _and_ the comedi-driver +static int init_usbduxfast(void) { + info(DRIVER_VERSION ":" DRIVER_DESC); + init_usb_devices(); + usb_register(&usbduxfastsub_driver); + comedi_driver_register(&driver_usbduxfast); + return 0; +} + + + +// deregistering the comedi driver and the usb-subsystem +static void exit_usbduxfast(void) { + comedi_driver_unregister(&driver_usbduxfast); + usb_deregister(&usbduxfastsub_driver); +} + + +module_init(init_usbduxfast); +module_exit(exit_usbduxfast); + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); -- 2.26.2