-#define DRIVER_VERSION "v2.1"
+#define DRIVER_VERSION "v2.2"
#define DRIVER_AUTHOR "Bernd Porr, BerndPorr@f2s.com"
#define DRIVER_DESC "Stirling/ITL USB-DUX -- Bernd.Porr@f2s.com"
/*
Description: University of Stirling USB DAQ & INCITE Technology Limited
Devices: [ITL] USB-DUX (usbdux.o)
Author: Bernd Porr <BerndPorr@f2s.com>
-Updated: 25 Nov 2007
-Status: Testing
+Updated: 8 Dec 2008
+Status: Stable
Configuration options:
You have to upload firmware with the -i option. The
firmware is usually installed under /usr/share/usb or
* 1.2: added PWM suport via EP4
* 2.0: PWM seems to be stable and is not interfering with the other functions
* 2.1: changed PWM API
+ * 2.2: added firmware kernel request to fix an udev problem
*
*/
#include <linux/smp_lock.h>
#include <linux/fcntl.h>
#include <linux/compiler.h>
+#include <linux/firmware.h>
#include "../comedidev.h"
/* pwm-transfer handling */
struct urb *urbPwm;
/* PWM period */
- lsampl_t pwmPeriod;
+ unsigned int pwmPeriod;
/* PWM internal delay for the GPIF in the FX2 */
int8_t pwmDelay;
/* size of the PWM buffer which holds the bit pattern */
/* interface structure in 2.6 */
struct usb_interface *interface;
/* comedi device for the interrupt context */
- comedi_device *comedidev;
+ struct comedi_device *comedidev;
/* is it USB_SPEED_HIGH or not? */
short int high_speed;
/* asynchronous command is running */
* This will cancel a running acquisition operation.
* This is called by comedi but never from inside the driver.
*/
-static int usbdux_ai_cancel(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct usbduxsub *this_usbduxsub;
int res = 0;
{
int i, err, n;
struct usbduxsub *this_usbduxsub;
- comedi_device *this_comedidev;
- comedi_subdevice *s;
+ struct comedi_device *this_comedidev;
+ struct comedi_subdevice *s;
/* the context variable points to the subdevice */
this_comedidev = urb->context;
}
/* force unlink, is called by comedi */
-static int usbdux_ao_cancel(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ao_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct usbduxsub *this_usbduxsub = dev->private;
int res = 0;
int i, ret;
int8_t *datap;
struct usbduxsub *this_usbduxsub;
- comedi_device *this_comedidev;
- comedi_subdevice *s;
+ struct comedi_device *this_comedidev;
+ struct comedi_subdevice *s;
/* the context variable points to the subdevice */
this_comedidev = urb->context;
((uint8_t *) (urb->transfer_buffer))[0] =
s->async->cmd.chanlist_len;
for (i = 0; i < s->async->cmd.chanlist_len; i++) {
- sampl_t temp;
+ short temp;
if (i >= NUMOUTCHANNELS)
break;
int errcode = 0;
uint8_t local_transfer_buffer[16];
- if (usbduxsub->probed) {
- /* 7f92 to zero */
- local_transfer_buffer[0] = 0;
- errcode = usb_control_msg(usbduxsub->usbdev,
- /* create a pipe for a control transfer */
- usb_sndctrlpipe(usbduxsub->usbdev, 0),
- /* bRequest, "Firmware" */
- USBDUXSUB_FIRMWARE,
- /* bmRequestType */
- VENDOR_DIR_OUT,
- /* Value */
- USBDUXSUB_CPUCS,
- /* Index */
- 0x0000,
- /* address of the transfer buffer */
- local_transfer_buffer,
- /* Length */
- 1,
- /* Timeout */
- EZTIMEOUT);
- if (errcode < 0) {
- dev_err(&usbduxsub->interface->dev,
- "comedi_: control msg failed (start)\n");
- return errcode;
- }
+ /* 7f92 to zero */
+ local_transfer_buffer[0] = 0;
+ errcode = usb_control_msg(usbduxsub->usbdev,
+ /* create a pipe for a control transfer */
+ usb_sndctrlpipe(usbduxsub->usbdev, 0),
+ /* bRequest, "Firmware" */
+ USBDUXSUB_FIRMWARE,
+ /* bmRequestType */
+ VENDOR_DIR_OUT,
+ /* Value */
+ USBDUXSUB_CPUCS,
+ /* Index */
+ 0x0000,
+ /* address of the transfer buffer */
+ local_transfer_buffer,
+ /* Length */
+ 1,
+ /* Timeout */
+ EZTIMEOUT);
+ if (errcode < 0) {
+ dev_err(&usbduxsub->interface->dev,
+ "comedi_: control msg failed (start)\n");
+ return errcode;
}
return 0;
}
int errcode = 0;
uint8_t local_transfer_buffer[16];
- if (usbduxsub->probed) {
- /* 7f92 to one */
- local_transfer_buffer[0] = 1;
- errcode = usb_control_msg(usbduxsub->usbdev,
- usb_sndctrlpipe(usbduxsub->usbdev, 0),
- /* bRequest, "Firmware" */
- USBDUXSUB_FIRMWARE,
- /* bmRequestType */
- VENDOR_DIR_OUT,
- /* Value */
- USBDUXSUB_CPUCS,
- /* Index */
- 0x0000, local_transfer_buffer,
- /* Length */
- 1,
- /* Timeout */
- EZTIMEOUT);
- if (errcode < 0) {
- dev_err(&usbduxsub->interface->dev,
- "comedi_: control msg failed (stop)\n");
- return errcode;
- }
+
+ /* 7f92 to one */
+ local_transfer_buffer[0] = 1;
+ errcode = usb_control_msg(usbduxsub->usbdev,
+ usb_sndctrlpipe(usbduxsub->usbdev, 0),
+ /* bRequest, "Firmware" */
+ USBDUXSUB_FIRMWARE,
+ /* bmRequestType */
+ VENDOR_DIR_OUT,
+ /* Value */
+ USBDUXSUB_CPUCS,
+ /* Index */
+ 0x0000, local_transfer_buffer,
+ /* Length */
+ 1,
+ /* Timeout */
+ EZTIMEOUT);
+ if (errcode < 0) {
+ dev_err(&usbduxsub->interface->dev,
+ "comedi_: control msg failed (stop)\n");
+ return errcode;
}
return 0;
}
{
int errcode;
- if (usbduxsub->probed) {
- dev_dbg(&usbduxsub->interface->dev,
- "comedi%d: usbdux: uploading %d bytes"
- " to addr %d, first byte=%d.\n",
- usbduxsub->comedidev->minor, len,
- startAddr, local_transfer_buffer[0]);
- errcode = usb_control_msg(usbduxsub->usbdev,
+ errcode = usb_control_msg(usbduxsub->usbdev,
usb_sndctrlpipe(usbduxsub->usbdev, 0),
/* brequest, firmware */
USBDUXSUB_FIRMWARE,
len,
/* timeout */
EZTIMEOUT);
- dev_dbg(&usbduxsub->interface->dev,
- "comedi_: result=%d\n", errcode);
- if (errcode < 0) {
- dev_err(&usbduxsub->interface->dev,
- "comedi_: upload failed\n");
- return errcode;
- }
- } else {
- /* no device on the bus for this index */
- return -EFAULT;
+ dev_dbg(&usbduxsub->interface->dev,
+ "comedi_: result=%d\n", errcode);
+ if (errcode < 0) {
+ dev_err(&usbduxsub->interface->dev,
+ "comedi_: upload failed\n");
+ return errcode;
}
return 0;
}
return 0;
}
-static int usbdux_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
+static int usbdux_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
comedi_cmd *cmd)
{
int err = 0, tmp, i;
return -EFAULT;
}
-static int usbdux_ai_inttrig(comedi_device *dev, comedi_subdevice *s,
+static int usbdux_ai_inttrig(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int trignum)
{
int ret;
return 1;
}
-static int usbdux_ai_cmd(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
comedi_cmd *cmd = &s->async->cmd;
unsigned int chan, range;
}
/* Mode 0 is used to get a single conversion on demand */
-static int usbdux_ai_insn_read(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ comedi_insn *insn, unsigned int *data)
{
int i;
- lsampl_t one = 0;
+ unsigned int one = 0;
int chan, range;
int err;
struct usbduxsub *this_usbduxsub = dev->private;
/************************************/
/* analog out */
-static int usbdux_ao_insn_read(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ comedi_insn *insn, unsigned int *data)
{
int i;
int chan = CR_CHAN(insn->chanspec);
return i;
}
-static int usbdux_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s,
+ comedi_insn *insn, unsigned int *data)
{
int i, err;
int chan = CR_CHAN(insn->chanspec);
return i;
}
-static int usbdux_ao_inttrig(comedi_device *dev, comedi_subdevice *s,
+static int usbdux_ao_inttrig(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int trignum)
{
int ret;
return 1;
}
-static int usbdux_ao_cmdtest(comedi_device *dev, comedi_subdevice *s,
+static int usbdux_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
comedi_cmd *cmd)
{
int err = 0, tmp;
return 0;
}
-static int usbdux_ao_cmd(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
comedi_cmd *cmd = &s->async->cmd;
unsigned int chan, gain;
return 0;
}
-static int usbdux_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ comedi_insn *insn, unsigned int *data)
{
int chan = CR_CHAN(insn->chanspec);
return insn->n;
}
-static int usbdux_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
+ comedi_insn *insn, unsigned int *data)
{
struct usbduxsub *this_usbduxsub = dev->private;
}
/* reads the 4 counters, only two are used just now */
-static int usbdux_counter_read(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_counter_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ comedi_insn *insn, unsigned int *data)
{
struct usbduxsub *this_usbduxsub = dev->private;
int chan = insn->chanspec;
return 1;
}
-static int usbdux_counter_write(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_counter_write(struct comedi_device *dev, struct comedi_subdevice *s,
+ comedi_insn *insn, unsigned int *data)
{
struct usbduxsub *this_usbduxsub = dev->private;
int err;
return 1;
}
-static int usbdux_counter_config(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_counter_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ comedi_insn *insn, unsigned int *data)
{
/* nothing to do so far */
return 2;
}
/* force unlink - is called by comedi */
-static int usbdux_pwm_cancel(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_pwm_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct usbduxsub *this_usbduxsub = dev->private;
int res = 0;
{
int ret;
struct usbduxsub *this_usbduxsub;
- comedi_device *this_comedidev;
- comedi_subdevice *s;
+ struct comedi_device *this_comedidev;
+ struct comedi_subdevice *s;
/* printk(KERN_DEBUG "PWM: IRQ\n"); */
return 0;
}
-static int usbdux_pwm_period(comedi_device *dev, comedi_subdevice *s,
- lsampl_t period)
+static int usbdux_pwm_period(struct comedi_device *dev, struct comedi_subdevice *s,
+ unsigned int period)
{
struct usbduxsub *this_usbduxsub = dev->private;
int fx2delay = 255;
}
/* is called from insn so there's no need to do all the sanity checks */
-static int usbdux_pwm_start(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_pwm_start(struct comedi_device *dev, struct comedi_subdevice *s)
{
int ret, i;
struct usbduxsub *this_usbduxsub = dev->private;
}
/* generates the bit pattern for PWM with the optional sign bit */
-static int usbdux_pwm_pattern(comedi_device *dev, comedi_subdevice *s,
- int channel, lsampl_t value, lsampl_t sign)
+static int usbdux_pwm_pattern(struct comedi_device *dev, struct comedi_subdevice *s,
+ int channel, unsigned int value, unsigned int sign)
{
struct usbduxsub *this_usbduxsub = dev->private;
int i, szbuf;
return 1;
}
-static int usbdux_pwm_write(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_pwm_write(struct comedi_device *dev, struct comedi_subdevice *s,
+ comedi_insn *insn, unsigned int *data)
{
struct usbduxsub *this_usbduxsub = dev->private;
data[0], 0);
}
-static int usbdux_pwm_read(comedi_device *x1, comedi_subdevice *x2,
- comedi_insn *x3, lsampl_t *x4)
+static int usbdux_pwm_read(struct comedi_device *x1, struct comedi_subdevice *x2,
+ comedi_insn *x3, unsigned int *x4)
{
/* not needed */
return -EINVAL;
};
/* switches on/off PWM */
-static int usbdux_pwm_config(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_pwm_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ comedi_insn *insn, unsigned int *data)
{
struct usbduxsub *this_usbduxsub = dev->private;
switch (data[0]) {
#define FIRMWARE_MAX_LEN 0x2000
/* taken from David Brownell's fxload and adjusted for this driver */
-static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
+static int read_firmware(struct usbduxsub *usbduxsub, const void *firmwarePtr,
long size)
{
struct device *dev = &usbduxsub->interface->dev;
int i = 0;
unsigned char *fp = (char *)firmwarePtr;
- unsigned char *firmwareBinary = NULL;
+ unsigned char *firmwareBinary;
int res = 0;
int maxAddr = 0;
j++;
if (j >= sizeof(buf)) {
dev_err(dev, "comedi_: bogus firmware file!\n");
+ kfree(firmwareBinary);
return -1;
}
}
if (buf[0] != ':') {
dev_err(dev, "comedi_: upload: not an ihex record: %s",
buf);
+ kfree(firmwareBinary);
return -EFAULT;
}
if (maxAddr >= FIRMWARE_MAX_LEN) {
dev_err(dev, "comedi_: firmware upload goes "
"beyond FX2 RAM boundaries.\n");
+ kfree(firmwareBinary);
return -EFAULT;
}
/* dev_dbg(dev, "comedi_: off=%x, len=%x:\n", off, len); */
if (type != 0) {
dev_err(dev, "comedi_: unsupported record type: %u\n",
type);
+ kfree(firmwareBinary);
return -EFAULT;
}
return res;
}
+static void usbdux_firmware_request_complete_handler(const struct firmware *fw,
+ void *context)
+{
+ struct usbduxsub *usbduxsub_tmp = context;
+ struct usb_device *usbdev = usbduxsub_tmp->usbdev;
+ int ret;
+
+ if (fw == NULL) {
+ dev_err(&usbdev->dev,
+ "Firmware complete handler without firmware!\n");
+ return;
+ }
+
+ /*
+ * we need to upload the firmware here because fw will be
+ * freed once we've left this function
+ */
+ ret = read_firmware(usbduxsub_tmp, fw->data, fw->size);
+
+ if (ret) {
+ dev_err(&usbdev->dev,
+ "Could not upload firmware (err=%d)\n",
+ ret);
+ return;
+ }
+ comedi_usb_auto_config(usbdev, BOARDNAME);
+}
+
/* allocate memory for the urbs and initialise them */
static int usbduxsub_probe(struct usb_interface *uinterf,
const struct usb_device_id *id)
struct device *dev = &uinterf->dev;
int i;
int index;
+ int ret;
dev_dbg(dev, "comedi_: usbdux_: "
"finding a free structure for the usb-device\n");
/* we've reached the bottom of the function */
usbduxsub[index].probed = 1;
up(&start_stop_sem);
+
+ ret = request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_HOTPLUG,
+ "usbdux_firmware.hex",
+ &udev->dev,
+ usbduxsub + index,
+ usbdux_firmware_request_complete_handler);
+
+ if (ret) {
+ dev_err(dev, "Could not load firmware (err=%d)\n", ret);
+ return ret;
+ }
+
dev_info(dev, "comedi_: usbdux%d "
"has been successfully initialised.\n", index);
/* success */
"comedi_: BUG! called with wrong ptr!!!\n");
return;
}
+ comedi_usb_auto_unconfig(udev);
down(&start_stop_sem);
down(&usbduxsub_tmp->sem);
tidy_up(usbduxsub_tmp);
}
/* is called when comedi-config is called */
-static int usbdux_attach(comedi_device *dev, comedi_devconfig *it)
+static int usbdux_attach(struct comedi_device *dev, comedi_devconfig *it)
{
int ret;
int index;
int i;
struct usbduxsub *udev;
- comedi_subdevice *s = NULL;
+ struct comedi_subdevice *s = NULL;
dev->private = NULL;
down(&start_stop_sem);
return 0;
}
-static int usbdux_detach(comedi_device *dev)
+static int usbdux_detach(struct comedi_device *dev)
{
struct usbduxsub *usbduxsub_tmp;
.detach = usbdux_detach,
};
-static void init_usb_devices(void)
-{
- int index;
-
- /* 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 < NUMUSBDUX; index++) {
- memset(&(usbduxsub[index]), 0x00, sizeof(usbduxsub[index]));
- init_MUTEX(&(usbduxsub[index].sem));
- }
-}
-
/* Table with the USB-devices: just now only testing IDs */
static struct usb_device_id usbduxsub_table[] = {
{USB_DEVICE(0x13d8, 0x0001) },
/* 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_usbdux(void)
+static int __init init_usbdux(void)
{
printk(KERN_INFO KBUILD_MODNAME ": "
DRIVER_VERSION ":" DRIVER_DESC "\n");
- init_usb_devices();
usb_register(&usbduxsub_driver);
comedi_driver_register(&driver_usbdux);
return 0;
}
/* deregistering the comedi driver and the usb-subsystem */
-static void exit_usbdux(void)
+static void __exit exit_usbdux(void)
{
comedi_driver_unregister(&driver_usbdux);
usb_deregister(&usbduxsub_driver);