Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b78750c1 authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman
Browse files

staging: comedi: dt9812: convert to use comedi (*auto_attach)



Converting this driver to use the comedi (*auto_attach) mechanism
allows pushing the usb (*probe) into the comedi (*auto_attach) and
the usb (disconnect) into the comedi (*detach). This removes the
disconnect between the usb driver and the comedi driver. Now when
the comedi driver is attached it will always have a usb device
associated with it.

This removes the 16 usb device limitation and allows bringing all
the private data into a single struct that can be kzalloc'ed when
the comedi driver is (*auto_attached). It also allows removing the
the sanity checks that make sure a usb device is connected to the
comedi device in the helper functions.

For aesthetic reasons, add some whitespace to the subdevice init.

Also, fix the analog out subdevice. There are 2 analog output
channels available on the usb device.

Signed-off-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 8db1eba1
Loading
Loading
Loading
Loading
+173 −285
Original line number Diff line number Diff line
@@ -45,7 +45,6 @@ for my needs.
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kref.h>
#include <linux/uaccess.h>
#include <linux/usb.h>

@@ -258,55 +257,26 @@ struct dt9812_usb_cmd {
#endif
};

#define DT9812_NUM_SLOTS	16

static DEFINE_SEMAPHORE(dt9812_mutex);

struct usb_dt9812 {
	struct slot_dt9812 *slot;
	struct usb_device *udev;
	u16 vendor;
	u16 product;
	u16 device;
	u32 serial;
struct dt9812_private {
	struct semaphore sem;
	struct {
		__u8 addr;
		size_t size;
	} cmd_wr, cmd_rd;
	struct kref kref;
};

struct dt9812_private {
	struct semaphore sem;
	struct slot_dt9812 *slot;
	u32 serial;
	u16 vendor;
	u16 product;
	u16 device;
	u16 ao_shadow[2];
	u8 do_shadow;
};

struct slot_dt9812 {
	struct usb_dt9812 *usb;
	struct dt9812_private *devpriv;
};

static struct slot_dt9812 dt9812[DT9812_NUM_SLOTS];

static inline struct usb_dt9812 *to_dt9812_dev(struct kref *d)
static int dt9812_read_info(struct comedi_device *dev,
			    int offset, void *buf, size_t buf_size)
{
	return container_of(d, struct usb_dt9812, kref);
}

static void dt9812_delete(struct kref *kref)
{
	struct usb_dt9812 *dev = to_dt9812_dev(kref);

	usb_put_dev(dev->udev);
	kfree(dev);
}

static int dt9812_read_info(struct usb_dt9812 *dev, int offset, void *buf,
			    size_t buf_size)
{
	struct usb_device *usb = dev->udev;
	struct usb_interface *intf = comedi_to_usb_interface(dev);
	struct usb_device *usb = interface_to_usbdev(intf);
	struct dt9812_private *devpriv = dev->private;
	struct dt9812_usb_cmd cmd;
	int count, ret;

@@ -316,19 +286,22 @@ static int dt9812_read_info(struct usb_dt9812 *dev, int offset, void *buf,
	cmd.u.flash_data_info.numbytes = cpu_to_le16(buf_size);

	/* DT9812 only responds to 32 byte writes!! */
	ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
	ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
			   &cmd, 32, &count, HZ * 1);
	if (ret)
		return ret;

	return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, dev->cmd_rd.addr),
	return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
			    buf, buf_size, &count, HZ * 1);
}

static int dt9812_read_multiple_registers(struct usb_dt9812 *dev, int reg_count,
					  u8 *address, u8 *value)
static int dt9812_read_multiple_registers(struct comedi_device *dev,
					  int reg_count, u8 *address,
					  u8 *value)
{
	struct usb_device *usb = dev->udev;
	struct usb_interface *intf = comedi_to_usb_interface(dev);
	struct usb_device *usb = interface_to_usbdev(intf);
	struct dt9812_private *devpriv = dev->private;
	struct dt9812_usb_cmd cmd;
	int i, count, ret;

@@ -338,20 +311,22 @@ static int dt9812_read_multiple_registers(struct usb_dt9812 *dev, int reg_count,
		cmd.u.read_multi_info.address[i] = address[i];

	/* DT9812 only responds to 32 byte writes!! */
	ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
	ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
			   &cmd, 32, &count, HZ * 1);
	if (ret)
		return ret;

	return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, dev->cmd_rd.addr),
	return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
			    value, reg_count, &count, HZ * 1);
}

static int dt9812_write_multiple_registers(struct usb_dt9812 *dev,
static int dt9812_write_multiple_registers(struct comedi_device *dev,
					   int reg_count, u8 *address,
					   u8 *value)
{
	struct usb_device *usb = dev->udev;
	struct usb_interface *intf = comedi_to_usb_interface(dev);
	struct usb_device *usb = interface_to_usbdev(intf);
	struct dt9812_private *devpriv = dev->private;
	struct dt9812_usb_cmd cmd;
	int i, count;

@@ -363,14 +338,17 @@ static int dt9812_write_multiple_registers(struct usb_dt9812 *dev,
	}

	/* DT9812 only responds to 32 byte writes!! */
	return usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
	return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
			    &cmd, 32, &count, HZ * 1);
}

static int dt9812_rmw_multiple_registers(struct usb_dt9812 *dev, int reg_count,
static int dt9812_rmw_multiple_registers(struct comedi_device *dev,
					 int reg_count,
					 struct dt9812_rmw_byte *rmw)
{
	struct usb_device *usb = dev->udev;
	struct usb_interface *intf = comedi_to_usb_interface(dev);
	struct usb_device *usb = interface_to_usbdev(intf);
	struct dt9812_private *devpriv = dev->private;
	struct dt9812_usb_cmd cmd;
	int i, count;

@@ -380,22 +358,19 @@ static int dt9812_rmw_multiple_registers(struct usb_dt9812 *dev, int reg_count,
		cmd.u.rmw_multi_info.rmw[i] = rmw[i];

	/* DT9812 only responds to 32 byte writes!! */
	return usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
	return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
			    &cmd, 32, &count, HZ * 1);
}

static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
{
	struct dt9812_private *devpriv = dev->private;
	struct slot_dt9812 *slot = devpriv->slot;
	int ret = -ENODEV;

	down(&devpriv->sem);
	if (slot->usb) {
	u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 };
	u8 value[2];
	int ret;

		ret = dt9812_read_multiple_registers(slot->usb, 2, reg, value);
	down(&devpriv->sem);
	ret = dt9812_read_multiple_registers(dev, 2, reg, value);
	if (ret == 0) {
		/*
		 * bits 0-6 in F020_SFR_P3 are bits 0-6 in the digital
@@ -404,7 +379,6 @@ static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
		 */
		*bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4);
	}
	}
	up(&devpriv->sem);

	return ret;
@@ -413,17 +387,13 @@ static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
static int dt9812_digital_out(struct comedi_device *dev, u8 bits)
{
	struct dt9812_private *devpriv = dev->private;
	struct slot_dt9812 *slot = devpriv->slot;
	int ret = -ENODEV;

	down(&devpriv->sem);
	if (slot->usb) {
	u8 reg[1] = { F020_SFR_P2 };
	u8 value[1] = { bits };
	int ret;

		ret = dt9812_write_multiple_registers(slot->usb, 1, reg, value);
	down(&devpriv->sem);
	ret = dt9812_write_multiple_registers(dev, 1, reg, value);
	devpriv->do_shadow = bits;
	}
	up(&devpriv->sem);

	return ret;
@@ -444,10 +414,8 @@ static void dt9812_configure_mux(struct comedi_device *dev,
				 struct dt9812_rmw_byte *rmw, int channel)
{
	struct dt9812_private *devpriv = dev->private;
	struct slot_dt9812 *slot = devpriv->slot;
	struct usb_dt9812 *usb = slot->usb;

	if (usb->device == DT9812_DEVID_DT9812_10) {
	if (devpriv->device == DT9812_DEVID_DT9812_10) {
		/* In the DT9812/10V MUX is selected by P1.5-7 */
		rmw->address = F020_SFR_P1;
		rmw->and_mask = 0xe0;
@@ -465,11 +433,9 @@ static void dt9812_configure_gain(struct comedi_device *dev,
				  enum dt9812_gain gain)
{
	struct dt9812_private *devpriv = dev->private;
	struct slot_dt9812 *slot = devpriv->slot;
	struct usb_dt9812 *usb = slot->usb;

	/* In the DT9812/10V, there is an external gain of 0.5 */
	if (usb->device == DT9812_DEVID_DT9812_10)
	if (devpriv->device == DT9812_DEVID_DT9812_10)
		gain <<= 1;

	rmw->address = F020_SFR_ADC0CF;
@@ -516,7 +482,6 @@ static int dt9812_analog_in(struct comedi_device *dev,
			    int channel, u16 *value, enum dt9812_gain gain)
{
	struct dt9812_private *devpriv = dev->private;
	struct slot_dt9812 *slot = devpriv->slot;
	struct dt9812_rmw_byte rmw[3];
	u8 reg[3] = {
		F020_SFR_ADC0CN,
@@ -524,11 +489,9 @@ static int dt9812_analog_in(struct comedi_device *dev,
		F020_SFR_ADC0L
	};
	u8 val[3];
	int ret = -ENODEV;
	int ret;

	down(&devpriv->sem);
	if (!slot->usb)
		goto exit;

	/* 1 select the gain */
	dt9812_configure_gain(dev, &rmw[0], gain);
@@ -541,12 +504,12 @@ static int dt9812_analog_in(struct comedi_device *dev,
	rmw[2].and_mask = 0xff;
	rmw[2].or_value = F020_MASK_ADC0CN_AD0EN | F020_MASK_ADC0CN_AD0BUSY;

	ret = dt9812_rmw_multiple_registers(slot->usb, 3, rmw);
	ret = dt9812_rmw_multiple_registers(dev, 3, rmw);
	if (ret)
		goto exit;

	/* read the status and ADC */
	ret = dt9812_read_multiple_registers(slot->usb, 3, reg, val);
	ret = dt9812_read_multiple_registers(dev, 3, reg, val);
	if (ret)
		goto exit;

@@ -561,7 +524,7 @@ static int dt9812_analog_in(struct comedi_device *dev,
	 */
	if ((val[0] & (F020_MASK_ADC0CN_AD0INT | F020_MASK_ADC0CN_AD0BUSY)) ==
	    F020_MASK_ADC0CN_AD0INT) {
		switch (slot->usb->device) {
		switch (devpriv->device) {
		case DT9812_DEVID_DT9812_10:
			/*
			 * For DT9812-10V the personality module set the
@@ -597,12 +560,10 @@ static int dt9812_analog_out_shadow(struct comedi_device *dev,
static int dt9812_analog_out(struct comedi_device *dev, int channel, u16 value)
{
	struct dt9812_private *devpriv = dev->private;
	struct slot_dt9812 *slot = devpriv->slot;
	int ret = -ENODEV;
	struct dt9812_rmw_byte rmw[3];
	int ret;

	down(&devpriv->sem);
	if (slot->usb) {
		struct dt9812_rmw_byte rmw[3];

	switch (channel) {
	case 0:
@@ -641,9 +602,9 @@ static int dt9812_analog_out(struct comedi_device *dev, int channel, u16 value)
		rmw[2].or_value = (value >> 8) & 0xf;
		break;
	}
		ret = dt9812_rmw_multiple_registers(slot->usb, 3, rmw);
	ret = dt9812_rmw_multiple_registers(dev, 3, rmw);
	devpriv->ao_shadow[channel] = value;
	}

	up(&devpriv->sem);

	return ret;
@@ -727,10 +688,11 @@ static int dt9812_ao_winsn(struct comedi_device *dev,
	return n;
}

static int dt9812_find_endpoints(struct usb_interface *intf,
				 struct usb_dt9812 *devpriv)
static int dt9812_find_endpoints(struct comedi_device *dev)
{
	struct usb_interface *intf = comedi_to_usb_interface(dev);
	struct usb_host_interface *host = intf->cur_altsetting;
	struct dt9812_private *devpriv = dev->private;
	struct usb_endpoint_descriptor *ep;
	int i;

@@ -774,24 +736,26 @@ static int dt9812_find_endpoints(struct usb_interface *intf,
	return 0;
}

static int dt9812_reset_device(struct usb_interface *intf,
			       struct usb_dt9812 *devpriv)
static int dt9812_reset_device(struct comedi_device *dev)
{
	struct usb_interface *intf = comedi_to_usb_interface(dev);
	struct usb_device *usb = interface_to_usbdev(intf);
	struct dt9812_private *devpriv = dev->private;
	int ret;
	int i;
	u32 tmp32;
	u16 tmp16;
	u8 tmp8;

	ret = dt9812_read_info(devpriv, 0, &tmp8, sizeof(tmp8));
	ret = dt9812_read_info(dev, 0, &tmp8, sizeof(tmp8));
	if (ret) {
		/*
		 * Seems like a configuration reset is necessary if driver is
		 * reloaded while device is attached
		 */
		usb_reset_configuration(devpriv->udev);
		usb_reset_configuration(usb);
		for (i = 0; i < 10; i++) {
			ret = dt9812_read_info(devpriv, 1, &tmp8, sizeof(tmp8));
			ret = dt9812_read_info(dev, 1, &tmp8, sizeof(tmp8));
			if (ret == 0)
				break;
		}
@@ -801,28 +765,28 @@ static int dt9812_reset_device(struct usb_interface *intf,
		}
	}

	ret = dt9812_read_info(devpriv, 1, &tmp16, sizeof(tmp16));
	ret = dt9812_read_info(dev, 1, &tmp16, sizeof(tmp16));
	if (ret) {
		dev_err(&intf->dev, "failed to read vendor id\n");
		return ret;
	}
	devpriv->vendor = le16_to_cpu(tmp16);

	ret = dt9812_read_info(devpriv, 3, &tmp16, sizeof(tmp16));
	ret = dt9812_read_info(dev, 3, &tmp16, sizeof(tmp16));
	if (ret) {
		dev_err(&intf->dev, "failed to read product id\n");
		return ret;
	}
	devpriv->product = le16_to_cpu(tmp16);

	ret = dt9812_read_info(devpriv, 5, &tmp16, sizeof(tmp16));
	ret = dt9812_read_info(dev, 5, &tmp16, sizeof(tmp16));
	if (ret) {
		dev_err(&intf->dev, "failed to read device id\n");
		return ret;
	}
	devpriv->device = le16_to_cpu(tmp16);

	ret = dt9812_read_info(devpriv, 7, &tmp32, sizeof(tmp32));
	ret = dt9812_read_info(dev, 7, &tmp32, sizeof(tmp32));
	if (ret) {
		dev_err(&intf->dev, "failed to read serial number\n");
		return ret;
@@ -837,11 +801,11 @@ static int dt9812_reset_device(struct usb_interface *intf,
	return 0;
}

static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
static int dt9812_auto_attach(struct comedi_device *dev,
			      unsigned long context)
{
	struct slot_dt9812 *slot = NULL;
	struct usb_interface *intf = comedi_to_usb_interface(dev);
	struct dt9812_private *devpriv;
	int i;
	struct comedi_subdevice *s;
	bool is_unipolar;
	int ret;
@@ -850,186 +814,110 @@ static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
	if (!devpriv)
		return -ENOMEM;
	dev->private = devpriv;
	sema_init(&devpriv->sem, 1);

	down(&dt9812_mutex);
	sema_init(&devpriv->sem, 1);
	usb_set_intfdata(intf, devpriv);

	/*
	 * Find the first unused slot for the comedi device
	 * that has a usb device connected.
	 */
	for (i = 0; i < DT9812_NUM_SLOTS; i++) {
		if (dt9812[i].usb && !dt9812[i].devpriv) {
			slot = &dt9812[i];
			break;
		}
	}
	if (!slot) {
		up(&dt9812_mutex);
		return -ENODEV;
	}
	ret = dt9812_find_endpoints(dev);
	if (ret)
		return ret;

	slot->devpriv = devpriv;
	devpriv->slot = slot;
	is_unipolar = (slot->usb->device == DT9812_DEVID_DT9812_2PT5);
	ret = dt9812_reset_device(dev);
	if (ret)
		return ret;

	up(&dt9812_mutex);
	is_unipolar = (devpriv->device == DT9812_DEVID_DT9812_2PT5);

	ret = comedi_alloc_subdevices(dev, 4);
	if (ret)
		return ret;

	/* digital input subdevice */
	/* Digital Input subdevice */
	s = &dev->subdevices[0];
	s->type		= COMEDI_SUBD_DI;
	s->subdev_flags	= SDF_READABLE;
	s->n_chan	= 8;
	s->maxdata	= 1;
	s->range_table	= &range_digital;
	s->insn_read = &dt9812_di_rinsn;
	s->insn_read	= dt9812_di_rinsn;

	/* digital output subdevice */
	/* Digital Output subdevice */
	s = &dev->subdevices[1];
	s->type		= COMEDI_SUBD_DO;
	s->subdev_flags	= SDF_WRITEABLE;
	s->n_chan	= 8;
	s->maxdata	= 1;
	s->range_table	= &range_digital;
	s->insn_write = &dt9812_do_winsn;
	s->insn_write	= dt9812_do_winsn;

	devpriv->do_shadow = 0;

	/* analog input subdevice */
	/* Analog Input subdevice */
	s = &dev->subdevices[2];
	s->type		= COMEDI_SUBD_AI;
	s->subdev_flags	= SDF_READABLE | SDF_GROUND;
	s->n_chan	= 8;
	s->maxdata = 4095;
	s->maxdata	= 0x0fff;
	s->range_table	= is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
	s->insn_read = &dt9812_ai_rinsn;
	s->insn_read	= dt9812_ai_rinsn;

	/* analog output subdevice */
	/* Analog Output subdevice */
	s = &dev->subdevices[3];
	s->type		= COMEDI_SUBD_AO;
	s->subdev_flags	= SDF_WRITEABLE;
	s->n_chan = 0;
	s->maxdata = 4095;
	s->n_chan	= 2;
	s->maxdata	= 0x0fff;
	s->range_table	= is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
	s->insn_write = &dt9812_ao_winsn;
	s->insn_read = &dt9812_ao_rinsn;
	s->insn_write	= dt9812_ao_winsn;
	s->insn_read	= dt9812_ao_rinsn;

	devpriv->ao_shadow[0] = is_unipolar ? 0x0000 : 0x0800;
	devpriv->ao_shadow[1] = is_unipolar ? 0x0000 : 0x0800;

	dev_info(dev->class_dev, "successfully attached to dt9812.\n");

	return 0;
}

static void dt9812_detach(struct comedi_device *dev)
{
	struct usb_interface *intf = comedi_to_usb_interface(dev);
	struct dt9812_private *devpriv = dev->private;

	if (devpriv && devpriv->slot)
		devpriv->slot = NULL;
	if (!devpriv)
		return;

	down(&devpriv->sem);

	usb_set_intfdata(intf, NULL);

	up(&devpriv->sem);
}

static struct comedi_driver dt9812_comedi_driver = {
	.module = THIS_MODULE,
static struct comedi_driver dt9812_driver = {
	.driver_name	= "dt9812",
	.attach = dt9812_attach,
	.module		= THIS_MODULE,
	.auto_attach	= dt9812_auto_attach,
	.detach		= dt9812_detach,
};

static int dt9812_probe(struct usb_interface *intf,
static int dt9812_usb_probe(struct usb_interface *intf,
			    const struct usb_device_id *id)
{
	struct slot_dt9812 *slot = NULL;
	struct usb_dt9812 *dev = NULL;
	int retval = -ENOMEM;
	int i;

	/* allocate memory for our device state and initialize it */
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (dev == NULL)
		goto error;

	kref_init(&dev->kref);

	down(&dt9812_mutex);

	/* Find an empty slot for the usb device */
	for (i = 0; i < DT9812_NUM_SLOTS; i++) {
		if (!dt9812[i].usb) {
			slot = &dt9812[i];
			break;
		}
	}
	if (!slot) {
		up(&dt9812_mutex);
		retval = -ENODEV;
		goto error;
	}

	slot->usb = dev;
	dev->slot = slot;

	up(&dt9812_mutex);

	dev->udev = usb_get_dev(interface_to_usbdev(intf));

	retval = dt9812_find_endpoints(intf, dev);
	if (retval)
		goto error;

	retval = dt9812_reset_device(intf, dev);
	if (retval)
		goto error;

	/* save our data pointer in this interface device */
	usb_set_intfdata(intf, dev);

	return 0;

error:
	if (dev)
		kref_put(&dev->kref, dt9812_delete);
	return retval;
}

static void dt9812_disconnect(struct usb_interface *intf)
{
	struct usb_dt9812 *dev;
	int minor = intf->minor;

	down(&dt9812_mutex);
	dev = usb_get_intfdata(intf);
	if (dev->slot) {
		dev->slot->usb = NULL;
		dev->slot = NULL;
	}
	usb_set_intfdata(intf, NULL);
	up(&dt9812_mutex);

	/* queue final destruction */
	kref_put(&dev->kref, dt9812_delete);

	dev_info(&intf->dev, "USB Dt9812 #%d now disconnected\n", minor);
	return comedi_usb_auto_config(intf, &dt9812_driver, id->driver_info);
}

static const struct usb_device_id dt9812_table[] = {
static const struct usb_device_id dt9812_usb_table[] = {
	{ USB_DEVICE(0x0867, 0x9812) },
	{ }
};
MODULE_DEVICE_TABLE(usb, dt9812_table);
MODULE_DEVICE_TABLE(usb, dt9812_usb_table);

static struct usb_driver dt9812_usb_driver = {
	.name		= "dt9812",
	.id_table	= dt9812_table,
	.probe		= dt9812_probe,
	.disconnect	= dt9812_disconnect,
	.id_table	= dt9812_usb_table,
	.probe		= dt9812_usb_probe,
	.disconnect	= comedi_usb_auto_unconfig,
};
module_comedi_usb_driver(dt9812_comedi_driver, dt9812_usb_driver);
module_comedi_usb_driver(dt9812_driver, dt9812_usb_driver);

MODULE_AUTHOR("Anders Blomdell <anders.blomdell@control.lth.se>");
MODULE_DESCRIPTION("Comedi DT9812 driver");