Эх сурвалжийг харах

Merge tag 'staging-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging tree update from Greg Kroah-Hartman:
 "Here is the big staging tree update for the 3.7-rc1 merge window.

  There are a few patches in here that are outside of the staging area,
  namely HID and IIO patches, but all of them have been acked by the
  relevant subsystem maintainers.  The IIO stuff is still coming in
  through this tree as it hasn't entirely moved out of the staging tree,
  but is almost there.

  Other than that, there wa a ton of work on the comedi drivers to make
  them more readable and the correct style.  Doing that removed a lot of
  code, but we added a new driver to the staging tree, so we didn't end
  up with a net reduction this time around:

   662 files changed, 51649 insertions(+), 26582 deletions(-)

  All of these patches have been in the linux-next tree already.

  Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"

* tag 'staging-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1094 commits)
  staging: comedi: jr3_pci: fix iomem dereference
  staging: comedi: drivers: use comedi_fc.h cmdtest helpers
  Staging: winbond: usb_free_urb(NULL) is safe
  Staging: winbond: checkpatch cleanup
  Staging: winbond: Removed undesired spaces, lines and tabs
  Staging: winbond: Typo corrections in comments
  Staging: winbond: Changed c99 comments to c89 comments
  staging: r8712u: Do not queue cloned skb
  staging: comedi: ni_mio_common: always lock in ni_ai_poll()
  staging: comedi: s626: add FIXME comment
  staging: comedi: s626: don't dereference insn->data
  staging: comedi: s526: fix if() check in s526_gpct_winsn()
  staging: comedi: s626: cleanup comments in s626_initialize()
  staging: comedi: s626: remove clear of kzalloc'ed data
  staging: comedi: s626: remove 'WDInterval' from private data
  staging: comedi: s626: remove 'ChargeEnabled' from private data
  staging: comedi: s626: remove 'IsBoardRevA' comment
  staging: comedi: s626: #if 0 out the "SAA7146 BUG WORKAROUND"
  staging: comedi: s626: remove 'allocatedBuf' from private data
  staging: comedi: s626: add final attach message
  ...
Linus Torvalds 12 жил өмнө
parent
commit
def7cb8cd4
100 өөрчлөгдсөн 15388 нэмэгдсэн , 2025 устгасан
  1. 15 0
      Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
  2. 41 0
      Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt
  3. 140 0
      Documentation/hid/hid-sensor.txt
  4. 14 0
      drivers/hid/Kconfig
  5. 1 0
      drivers/hid/Makefile
  6. 5 0
      drivers/hid/hid-core.c
  7. 6 0
      drivers/hid/hid-ids.h
  8. 680 0
      drivers/hid/hid-sensor-hub.c
  9. 6 1
      drivers/iio/Kconfig
  10. 5 0
      drivers/iio/Makefile
  11. 16 0
      drivers/iio/accel/Kconfig
  12. 5 0
      drivers/iio/accel/Makefile
  13. 418 0
      drivers/iio/accel/hid-sensor-accel-3d.c
  14. 38 0
      drivers/iio/adc/Kconfig
  15. 4 0
      drivers/iio/adc/Makefile
  16. 1 1
      drivers/iio/adc/ad7266.c
  17. 149 61
      drivers/iio/adc/ad7476.c
  18. 460 0
      drivers/iio/adc/ad7791.c
  19. 558 0
      drivers/iio/adc/ad_sigma_delta.c
  20. 18 59
      drivers/iio/adc/at91_adc.c
  21. 264 0
      drivers/iio/adc/lp8788_adc.c
  22. 5 0
      drivers/iio/common/Kconfig
  23. 9 0
      drivers/iio/common/Makefile
  24. 26 0
      drivers/iio/common/hid-sensors/Kconfig
  25. 6 0
      drivers/iio/common/hid-sensors/Makefile
  26. 250 0
      drivers/iio/common/hid-sensors/hid-sensor-attributes.c
  27. 57 0
      drivers/iio/common/hid-sensors/hid-sensor-attributes.h
  28. 103 0
      drivers/iio/common/hid-sensors/hid-sensor-trigger.c
  29. 26 0
      drivers/iio/common/hid-sensors/hid-sensor-trigger.h
  30. 16 4
      drivers/iio/dac/Kconfig
  31. 1 0
      drivers/iio/dac/Makefile
  32. 338 112
      drivers/iio/dac/ad5446.c
  33. 0 91
      drivers/iio/dac/ad5446.h
  34. 650 0
      drivers/iio/dac/ad5755.c
  35. 16 0
      drivers/iio/gyro/Kconfig
  36. 5 0
      drivers/iio/gyro/Makefile
  37. 418 0
      drivers/iio/gyro/hid-sensor-gyro-3d.c
  38. 7 8
      drivers/iio/industrialio-buffer.c
  39. 10 3
      drivers/iio/industrialio-core.c
  40. 128 11
      drivers/iio/inkern.c
  41. 23 8
      drivers/iio/kfifo_buf.c
  42. 10 0
      drivers/iio/light/Kconfig
  43. 1 0
      drivers/iio/light/Makefile
  44. 1 1
      drivers/iio/light/adjd_s311.c
  45. 385 0
      drivers/iio/light/hid-sensor-als.c
  46. 16 0
      drivers/iio/magnetometer/Kconfig
  47. 5 0
      drivers/iio/magnetometer/Makefile
  48. 419 0
      drivers/iio/magnetometer/hid-sensor-magn-3d.c
  49. 7 0
      drivers/power/Kconfig
  50. 1 0
      drivers/power/Makefile
  51. 422 0
      drivers/power/generic-adc-battery.c
  52. 8 2
      drivers/staging/Kconfig
  53. 4 1
      drivers/staging/Makefile
  54. 7 10
      drivers/staging/android/alarm-dev.c
  55. 16 16
      drivers/staging/android/ashmem.c
  56. 3 3
      drivers/staging/android/binder.c
  57. 26 14
      drivers/staging/android/logger.c
  58. 17 7
      drivers/staging/android/logger.h
  59. 1 12
      drivers/staging/android/timed_gpio.c
  60. 45 11
      drivers/staging/asus_oled/asus_oled.c
  61. 2 0
      drivers/staging/bcm/Bcmchar.c
  62. 68 82
      drivers/staging/bcm/CmHost.c
  63. 19 35
      drivers/staging/bcm/CmHost.h
  64. 13 4
      drivers/staging/bcm/InterfaceInit.c
  65. 16 17
      drivers/staging/bcm/InterfaceInit.h
  66. 1 1
      drivers/staging/bcm/Kconfig
  67. 4 1
      drivers/staging/bcm/Misc.c
  68. 2 2
      drivers/staging/bcm/PHSModule.c
  69. 1 1
      drivers/staging/bcm/Prototypes.h
  70. 106 128
      drivers/staging/bcm/Transmit.c
  71. 296 408
      drivers/staging/bcm/cntrl_SignalingInterface.h
  72. 1 1
      drivers/staging/bcm/hostmibs.c
  73. 2 2
      drivers/staging/bcm/nvm.c
  74. 1 1
      drivers/staging/bcm/target_params.h
  75. 1 1
      drivers/staging/ccg/Kconfig
  76. 5 9
      drivers/staging/ccg/ccg.c
  77. 6 0
      drivers/staging/ced1401/Kconfig
  78. 3 0
      drivers/staging/ced1401/Makefile
  79. 10 0
      drivers/staging/ced1401/TODO
  80. 1515 0
      drivers/staging/ced1401/ced_ioc.c
  81. 345 0
      drivers/staging/ced1401/ced_ioctl.h
  82. 127 0
      drivers/staging/ced1401/machine.h
  83. 1637 0
      drivers/staging/ced1401/usb1401.c
  84. 249 0
      drivers/staging/ced1401/usb1401.h
  85. 287 0
      drivers/staging/ced1401/use1401.h
  86. 301 0
      drivers/staging/ced1401/use14_ioc.h
  87. 3035 0
      drivers/staging/ced1401/userspace/use1401.c
  88. 28 38
      drivers/staging/comedi/Kconfig
  89. 470 470
      drivers/staging/comedi/comedi.h
  90. 73 79
      drivers/staging/comedi/comedi_fops.c
  91. 1 1
      drivers/staging/comedi/comedidev.h
  92. 45 37
      drivers/staging/comedi/drivers.c
  93. 9 6
      drivers/staging/comedi/drivers/8253.h
  94. 12 29
      drivers/staging/comedi/drivers/8255.c
  95. 353 0
      drivers/staging/comedi/drivers/8255_pci.c
  96. 2 4
      drivers/staging/comedi/drivers/Makefile
  97. 3 3
      drivers/staging/comedi/drivers/acl7225b.c
  98. 0 195
      drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.c
  99. 0 27
      drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.h
  100. 7 7
      drivers/staging/comedi/drivers/addi-data/addi_common.c

+ 15 - 0
Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt

@@ -0,0 +1,15 @@
+* Freescale i.MX28 LRADC device driver
+
+Required properties:
+- compatible: Should be "fsl,imx28-lradc"
+- reg: Address and length of the register set for the device
+- interrupts: Should contain the LRADC interrupts
+
+Examples:
+
+	lradc@80050000 {
+		compatible = "fsl,imx28-lradc";
+		reg = <0x80050000 0x2000>;
+		interrupts = <10 14 15 16 17 18 19
+				20 21 22 23 24 25>;
+	};

+ 41 - 0
Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt

@@ -0,0 +1,41 @@
+Freescale i.MX IPUv3
+====================
+
+Required properties:
+- compatible: Should be "fsl,<chip>-ipu"
+- reg: should be register base and length as documented in the
+  datasheet
+- interrupts: Should contain sync interrupt and error interrupt,
+  in this order.
+- #crtc-cells: 1, See below
+
+example:
+
+ipu: ipu@18000000 {
+	#crtc-cells = <1>;
+	compatible = "fsl,imx53-ipu";
+	reg = <0x18000000 0x080000000>;
+	interrupts = <11 10>;
+};
+
+Parallel display support
+========================
+
+Required properties:
+- compatible: Should be "fsl,imx-parallel-display"
+- crtc: the crtc this display is connected to, see below
+Optional properties:
+- interface_pix_fmt: How this display is connected to the
+  crtc. Currently supported types: "rgb24", "rgb565"
+- edid: verbatim EDID data block describing attached display.
+- ddc: phandle describing the i2c bus handling the display data
+  channel
+
+example:
+
+display@di0 {
+	compatible = "fsl,imx-parallel-display";
+	edid = [edid-data];
+	crtc = <&ipu 0>;
+	interface-pix-fmt = "rgb24";
+};

+ 140 - 0
Documentation/hid/hid-sensor.txt

@@ -0,0 +1,140 @@
+
+HID Sensors Framework
+======================
+HID sensor framework provides necessary interfaces to implement sensor drivers,
+which are connected to a sensor hub. The sensor hub is a HID device and it provides
+a report descriptor conforming to HID 1.12 sensor usage tables.
+
+Description from the HID 1.12 "HID Sensor Usages" specification:
+"Standardization of HID usages for sensors would allow (but not require) sensor
+hardware vendors to provide a consistent Plug And Play interface at the USB boundary,
+thereby enabling some operating systems to incorporate common device drivers that
+could be reused between vendors, alleviating any need for the vendors to provide
+the drivers themselves."
+
+This specification describes many usage IDs, which describe the type of sensor
+and also the individual data fields. Each sensor can have variable number of
+data fields. The length and order is specified in the report descriptor. For
+example a part of report descriptor can look like:
+
+   INPUT(1)[INPUT]
+ ..
+    Field(2)
+      Physical(0020.0073)
+      Usage(1)
+        0020.045f
+      Logical Minimum(-32767)
+      Logical Maximum(32767)
+      Report Size(8)
+      Report Count(1)
+      Report Offset(16)
+      Flags(Variable Absolute)
+..
+..
+
+The report is indicating "sensor page (0x20)" contains an accelerometer-3D (0x73).
+This accelerometer-3D has some fields. Here for example field 2 is motion intensity
+(0x045f) with a logical minimum value of -32767 and logical maximum of 32767. The
+order of fields and length of each field is important as the input event raw
+data will use this format.
+
+
+Implementation
+=================
+
+This specification defines many different types of sensors with different sets of
+data fields. It is difficult to have a common input event to user space applications,
+for different sensors. For example an accelerometer can send X,Y and Z data, whereas
+an ambient light sensor can send illumination data.
+So the implementation has two parts:
+- Core hid driver
+- Individual sensor processing part (sensor drivers)
+
+Core driver
+-----------
+The core driver registers (hid-sensor-hub) registers as a HID driver. It parses
+report descriptors and identifies all the sensors present. It adds an MFD device
+with name HID-SENSOR-xxxx (where xxxx is usage id from the specification).
+For example
+HID-SENSOR-200073 is registered for an Accelerometer 3D driver.
+So if any driver with this name is inserted, then the probe routine for that
+function will be called. So an accelerometer processing driver can register
+with this name and will be probed if there is an accelerometer-3D detected.
+
+The core driver provides a set of APIs which can be used by the processing
+drivers to register and get events for that usage id. Also it provides parsing
+functions, which get and set each input/feature/output report.
+
+Individual sensor processing part (sensor drivers)
+-----------
+The processing driver will use an interface provided by the core driver to parse
+the report and get the indexes of the fields and also can get events. This driver
+can use IIO interface to use the standard ABI defined for a type of sensor.
+
+
+Core driver Interface
+=====================
+
+Callback structure:
+Each processing driver can use this structure to set some callbacks.
+	int (*suspend)(..): Callback when HID suspend is received
+	int (*resume)(..): Callback when HID resume is received
+	int (*capture_sample)(..): Capture a sample for one of its data fields
+	int (*send_event)(..): One complete event is received which can have
+                               multiple data fields.
+
+Registration functions:
+int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
+			u32 usage_id,
+			struct hid_sensor_hub_callbacks *usage_callback):
+
+Registers callbacks for an usage id. The callback functions are not allowed
+to sleep.
+
+
+int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
+			u32 usage_id):
+
+Removes callbacks for an usage id.
+
+
+Parsing function:
+int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
+			u8 type,
+			u32 usage_id, u32 attr_usage_id,
+			struct hid_sensor_hub_attribute_info *info);
+
+A processing driver can look for some field of interest and check if it exists
+in a report descriptor. If it exists it will store necessary information
+so that fields can be set or get individually.
+These indexes avoid searching every time and getting field index to get or set.
+
+
+Set Feature report
+int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+			u32 field_index, s32 value);
+
+This interface is used to set a value for a field in feature report. For example
+if there is a field report_interval, which is parsed by a call to
+sensor_hub_input_get_attribute_info before, then it can directly set that individual
+field.
+
+
+int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+			u32 field_index, s32 *value);
+
+This interface is used to get a value for a field in input report. For example
+if there is a field report_interval, which is parsed by a call to
+sensor_hub_input_get_attribute_info before, then it can directly get that individual
+field value.
+
+
+int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
+			u32 usage_id,
+			u32 attr_usage_id, u32 report_id);
+
+This is used to get a particular field value through input reports. For example
+accelerometer wants to poll X axis value, then it can call this function with
+the usage id of X axis. HID sensors can provide events, so this is not necessary
+to poll for any field. If there is some new sample, the core driver will call
+registered callback function to process the sample.

+ 14 - 0
drivers/hid/Kconfig

@@ -708,6 +708,20 @@ config HID_ZYDACRON
 	---help---
 	Support for Zydacron remote control.
 
+config HID_SENSOR_HUB
+	tristate "HID Sensors framework support"
+	depends on USB_HID
+	select MFD_CORE
+	default n
+	-- help---
+	  Support for HID Sensor framework. This creates a MFD instance
+	  for a sensor hub and identifies all the sensors connected to it.
+	  Each sensor is registered as a MFD cell, so that sensor specific
+	  processing can be done in a separate driver. Each sensor
+	  drivers can use the service provided by this driver to register
+	  for events and handle data streams. Each sensor driver can format
+	  data and present to user mode using input or IIO interface.
+
 endmenu
 
 endif # HID

+ 1 - 0
drivers/hid/Makefile

@@ -112,6 +112,7 @@ obj-$(CONFIG_HID_ZYDACRON)	+= hid-zydacron.o
 obj-$(CONFIG_HID_WACOM)		+= hid-wacom.o
 obj-$(CONFIG_HID_WALTOP)	+= hid-waltop.o
 obj-$(CONFIG_HID_WIIMOTE)	+= hid-wiimote.o
+obj-$(CONFIG_HID_SENSOR_HUB)	+= hid-sensor-hub.o
 
 obj-$(CONFIG_USB_HID)		+= usbhid/
 obj-$(CONFIG_USB_MOUSE)		+= usbhid/

+ 5 - 0
drivers/hid/hid-core.c

@@ -1568,6 +1568,10 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_1020) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_09FA) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_1020) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_09FA) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
@@ -1665,6 +1669,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_SENSOR_HUB_7014) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) },

+ 6 - 0
drivers/hid/hid-ids.h

@@ -429,6 +429,11 @@
 #define USB_VENDOR_ID_IMATION		0x0718
 #define USB_DEVICE_ID_DISC_STAKKA	0xd000
 
+#define USB_VENDOR_ID_INTEL_8086	0x8086
+#define USB_VENDOR_ID_INTEL_8087	0x8087
+#define USB_DEVICE_ID_SENSOR_HUB_1020	0x1020
+#define USB_DEVICE_ID_SENSOR_HUB_09FA	0x09FA
+
 #define USB_VENDOR_ID_IRTOUCHSYSTEMS	0x6615
 #define USB_DEVICE_ID_IRTOUCH_INFRARED_USB	0x0070
 
@@ -706,6 +711,7 @@
 
 #define USB_VENDOR_ID_STANTUM_STM		0x0483
 #define USB_DEVICE_ID_MTP_STM		0x3261
+#define USB_DEVICE_ID_SENSOR_HUB_7014	0x7014
 
 #define USB_VENDOR_ID_STANTUM_SITRONIX		0x1403
 #define USB_DEVICE_ID_MTP_SITRONIX		0x5001

+ 680 - 0
drivers/hid/hid-sensor-hub.c

@@ -0,0 +1,680 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/usb.h>
+#include "usbhid/usbhid.h"
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+#include <linux/list.h>
+#include <linux/hid-sensor-ids.h>
+#include <linux/hid-sensor-hub.h>
+#include "hid-ids.h"
+
+/**
+ * struct sensor_hub_pending - Synchronous read pending information
+ * @status:		Pending status true/false.
+ * @ready:		Completion synchronization data.
+ * @usage_id:		Usage id for physical device, E.g. Gyro usage id.
+ * @attr_usage_id:	Usage Id of a field, E.g. X-AXIS for a gyro.
+ * @raw_size:		Response size for a read request.
+ * @raw_data:		Place holder for received response.
+ */
+struct sensor_hub_pending {
+	bool status;
+	struct completion ready;
+	u32 usage_id;
+	u32 attr_usage_id;
+	int raw_size;
+	u8  *raw_data;
+};
+
+/**
+ * struct sensor_hub_data - Hold a instance data for a HID hub device
+ * @hsdev:		Stored hid instance for current hub device.
+ * @mutex:		Mutex to serialize synchronous request.
+ * @lock:		Spin lock to protect pending request structure.
+ * @pending:		Holds information of pending sync read request.
+ * @dyn_callback_list:	Holds callback function
+ * @dyn_callback_lock:	spin lock to protect callback list
+ * @hid_sensor_hub_client_devs:	Stores all MFD cells for a hub instance.
+ * @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached).
+ */
+struct sensor_hub_data {
+	struct hid_sensor_hub_device *hsdev;
+	struct mutex mutex;
+	spinlock_t lock;
+	struct sensor_hub_pending pending;
+	struct list_head dyn_callback_list;
+	spinlock_t dyn_callback_lock;
+	struct mfd_cell *hid_sensor_hub_client_devs;
+	int hid_sensor_client_cnt;
+};
+
+/**
+ * struct hid_sensor_hub_callbacks_list - Stores callback list
+ * @list:		list head.
+ * @usage_id:		usage id for a physical device.
+ * @usage_callback:	Stores registered callback functions.
+ * @priv:		Private data for a physical device.
+ */
+struct hid_sensor_hub_callbacks_list {
+	struct list_head list;
+	u32 usage_id;
+	struct hid_sensor_hub_callbacks *usage_callback;
+	void *priv;
+};
+
+static int sensor_hub_check_for_sensor_page(struct hid_device *hdev)
+{
+	int i;
+	int ret = -EINVAL;
+
+	for (i = 0; i < hdev->maxcollection; i++) {
+		struct hid_collection *col = &hdev->collection[i];
+		if (col->type == HID_COLLECTION_PHYSICAL &&
+		   (col->usage & HID_USAGE_PAGE) == HID_UP_SENSOR) {
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev,
+						int dir)
+{
+	struct hid_report *report;
+
+	list_for_each_entry(report, &hdev->report_enum[dir].report_list, list) {
+		if (report->id == id)
+			return report;
+	}
+	hid_warn(hdev, "No report with id 0x%x found\n", id);
+
+	return NULL;
+}
+
+static int sensor_hub_get_physical_device_count(
+				struct hid_report_enum *report_enum)
+{
+	struct hid_report *report;
+	struct hid_field *field;
+	int cnt = 0;
+
+	list_for_each_entry(report, &report_enum->report_list, list) {
+		field = report->field[0];
+		if (report->maxfield && field &&
+					field->physical)
+			cnt++;
+	}
+
+	return cnt;
+}
+
+static void sensor_hub_fill_attr_info(
+		struct hid_sensor_hub_attribute_info *info,
+		s32 index, s32 report_id, s32 units, s32 unit_expo, s32 size)
+{
+	info->index = index;
+	info->report_id = report_id;
+	info->units = units;
+	info->unit_expo = unit_expo;
+	info->size = size/8;
+}
+
+static struct hid_sensor_hub_callbacks *sensor_hub_get_callback(
+					struct hid_device *hdev,
+					u32 usage_id, void **priv)
+{
+	struct hid_sensor_hub_callbacks_list *callback;
+	struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
+
+	spin_lock(&pdata->dyn_callback_lock);
+	list_for_each_entry(callback, &pdata->dyn_callback_list, list)
+		if (callback->usage_id == usage_id) {
+			*priv = callback->priv;
+			spin_unlock(&pdata->dyn_callback_lock);
+			return callback->usage_callback;
+		}
+	spin_unlock(&pdata->dyn_callback_lock);
+
+	return NULL;
+}
+
+int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
+			u32 usage_id,
+			struct hid_sensor_hub_callbacks *usage_callback)
+{
+	struct hid_sensor_hub_callbacks_list *callback;
+	struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev);
+
+	spin_lock(&pdata->dyn_callback_lock);
+	list_for_each_entry(callback, &pdata->dyn_callback_list, list)
+		if (callback->usage_id == usage_id) {
+			spin_unlock(&pdata->dyn_callback_lock);
+			return -EINVAL;
+		}
+	callback = kzalloc(sizeof(*callback), GFP_ATOMIC);
+	if (!callback) {
+		spin_unlock(&pdata->dyn_callback_lock);
+		return -ENOMEM;
+	}
+	callback->usage_callback = usage_callback;
+	callback->usage_id = usage_id;
+	callback->priv = NULL;
+	list_add_tail(&callback->list, &pdata->dyn_callback_list);
+	spin_unlock(&pdata->dyn_callback_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_register_callback);
+
+int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
+				u32 usage_id)
+{
+	struct hid_sensor_hub_callbacks_list *callback;
+	struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev);
+
+	spin_lock(&pdata->dyn_callback_lock);
+	list_for_each_entry(callback, &pdata->dyn_callback_list, list)
+		if (callback->usage_id == usage_id) {
+			list_del(&callback->list);
+			kfree(callback);
+			break;
+		}
+	spin_unlock(&pdata->dyn_callback_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_remove_callback);
+
+int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+				u32 field_index, s32 value)
+{
+	struct hid_report *report;
+	struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev);
+	int ret = 0;
+
+	mutex_lock(&data->mutex);
+	report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT);
+	if (!report || (field_index >=  report->maxfield)) {
+		ret = -EINVAL;
+		goto done_proc;
+	}
+	hid_set_field(report->field[field_index], 0, value);
+	usbhid_submit_report(hsdev->hdev, report, USB_DIR_OUT);
+	usbhid_wait_io(hsdev->hdev);
+
+done_proc:
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_set_feature);
+
+int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+				u32 field_index, s32 *value)
+{
+	struct hid_report *report;
+	struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev);
+	int ret = 0;
+
+	mutex_lock(&data->mutex);
+	report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT);
+	if (!report || (field_index >=  report->maxfield)) {
+		ret = -EINVAL;
+		goto done_proc;
+	}
+	usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN);
+	usbhid_wait_io(hsdev->hdev);
+	*value = report->field[field_index]->value[0];
+
+done_proc:
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_get_feature);
+
+
+int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
+					u32 usage_id,
+					u32 attr_usage_id, u32 report_id)
+{
+	struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev);
+	unsigned long flags;
+	struct hid_report *report;
+	int ret_val = 0;
+
+	mutex_lock(&data->mutex);
+	memset(&data->pending, 0, sizeof(data->pending));
+	init_completion(&data->pending.ready);
+	data->pending.usage_id = usage_id;
+	data->pending.attr_usage_id = attr_usage_id;
+	data->pending.raw_size = 0;
+
+	spin_lock_irqsave(&data->lock, flags);
+	data->pending.status = true;
+	report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT);
+	if (!report) {
+		spin_unlock_irqrestore(&data->lock, flags);
+		goto err_free;
+	}
+	usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN);
+	spin_unlock_irqrestore(&data->lock, flags);
+	wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5);
+	switch (data->pending.raw_size) {
+	case 1:
+		ret_val = *(u8 *)data->pending.raw_data;
+		break;
+	case 2:
+		ret_val = *(u16 *)data->pending.raw_data;
+		break;
+	case 4:
+		ret_val = *(u32 *)data->pending.raw_data;
+		break;
+	default:
+		ret_val = 0;
+	}
+	kfree(data->pending.raw_data);
+
+err_free:
+	data->pending.status = false;
+	mutex_unlock(&data->mutex);
+
+	return ret_val;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_input_attr_get_raw_value);
+
+int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
+				u8 type,
+				u32 usage_id,
+				u32 attr_usage_id,
+				struct hid_sensor_hub_attribute_info *info)
+{
+	int ret = -1;
+	int i, j;
+	int collection_index = -1;
+	struct hid_report *report;
+	struct hid_field *field;
+	struct hid_report_enum *report_enum;
+	struct hid_device *hdev = hsdev->hdev;
+
+	/* Initialize with defaults */
+	info->usage_id = usage_id;
+	info->attrib_id =  attr_usage_id;
+	info->report_id = -1;
+	info->index = -1;
+	info->units = -1;
+	info->unit_expo = -1;
+
+	for (i = 0; i < hdev->maxcollection; ++i) {
+		struct hid_collection *collection = &hdev->collection[i];
+		if (usage_id == collection->usage) {
+			collection_index = i;
+			break;
+		}
+	}
+	if (collection_index == -1)
+		goto err_ret;
+
+	report_enum = &hdev->report_enum[type];
+	list_for_each_entry(report, &report_enum->report_list, list) {
+		for (i = 0; i < report->maxfield; ++i) {
+			field = report->field[i];
+			if (field->physical == usage_id &&
+				field->logical == attr_usage_id) {
+				sensor_hub_fill_attr_info(info, i, report->id,
+					field->unit, field->unit_exponent,
+					field->report_size);
+				ret = 0;
+			} else {
+				for (j = 0; j < field->maxusage; ++j) {
+					if (field->usage[j].hid ==
+					attr_usage_id &&
+					field->usage[j].collection_index ==
+					collection_index)  {
+						sensor_hub_fill_attr_info(info,
+							i, report->id,
+							field->unit,
+							field->unit_exponent,
+							field->report_size);
+						ret = 0;
+						break;
+					}
+				}
+			}
+			if (ret == 0)
+				break;
+		}
+	}
+
+err_ret:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info);
+
+#ifdef CONFIG_PM
+static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message)
+{
+	struct sensor_hub_data *pdata =  hid_get_drvdata(hdev);
+	struct hid_sensor_hub_callbacks_list *callback;
+
+	hid_dbg(hdev, " sensor_hub_suspend\n");
+	spin_lock(&pdata->dyn_callback_lock);
+	list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
+		if (callback->usage_callback->suspend)
+			callback->usage_callback->suspend(
+					pdata->hsdev, callback->priv);
+	}
+	spin_unlock(&pdata->dyn_callback_lock);
+
+	return 0;
+}
+
+static int sensor_hub_resume(struct hid_device *hdev)
+{
+	struct sensor_hub_data *pdata =  hid_get_drvdata(hdev);
+	struct hid_sensor_hub_callbacks_list *callback;
+
+	hid_dbg(hdev, " sensor_hub_resume\n");
+	spin_lock(&pdata->dyn_callback_lock);
+	list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
+		if (callback->usage_callback->resume)
+			callback->usage_callback->resume(
+					pdata->hsdev, callback->priv);
+	}
+	spin_unlock(&pdata->dyn_callback_lock);
+
+	return 0;
+}
+
+static int sensor_hub_reset_resume(struct hid_device *hdev)
+{
+	return 0;
+}
+#endif
+/*
+ * Handle raw report as sent by device
+ */
+static int sensor_hub_raw_event(struct hid_device *hdev,
+		struct hid_report *report, u8 *raw_data, int size)
+{
+	int i;
+	u8 *ptr;
+	int sz;
+	struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
+	unsigned long flags;
+	struct hid_sensor_hub_callbacks *callback = NULL;
+	struct hid_collection *collection = NULL;
+	void *priv = NULL;
+
+	hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n",
+			 report->id, size, report->type);
+	hid_dbg(hdev, "maxfield:%d\n", report->maxfield);
+	if (report->type != HID_INPUT_REPORT)
+		return 1;
+
+	ptr = raw_data;
+	ptr++; /*Skip report id*/
+
+	if (!report)
+		goto err_report;
+
+	spin_lock_irqsave(&pdata->lock, flags);
+
+	for (i = 0; i < report->maxfield; ++i) {
+
+		hid_dbg(hdev, "%d collection_index:%x hid:%x sz:%x\n",
+				i, report->field[i]->usage->collection_index,
+				report->field[i]->usage->hid,
+				report->field[i]->report_size/8);
+
+		sz = report->field[i]->report_size/8;
+		if (pdata->pending.status && pdata->pending.attr_usage_id ==
+				report->field[i]->usage->hid) {
+			hid_dbg(hdev, "data was pending ...\n");
+			pdata->pending.raw_data = kmalloc(sz, GFP_ATOMIC);
+			if (pdata->pending.raw_data) {
+				memcpy(pdata->pending.raw_data, ptr, sz);
+				pdata->pending.raw_size  = sz;
+			} else
+				pdata->pending.raw_size = 0;
+			complete(&pdata->pending.ready);
+		}
+		collection = &hdev->collection[
+				report->field[i]->usage->collection_index];
+		hid_dbg(hdev, "collection->usage %x\n",
+					collection->usage);
+		callback = sensor_hub_get_callback(pdata->hsdev->hdev,
+						report->field[i]->physical,
+							&priv);
+		if (callback && callback->capture_sample) {
+			if (report->field[i]->logical)
+				callback->capture_sample(pdata->hsdev,
+					report->field[i]->logical, sz, ptr,
+					callback->pdev);
+			else
+				callback->capture_sample(pdata->hsdev,
+					report->field[i]->usage->hid, sz, ptr,
+					callback->pdev);
+		}
+		ptr += sz;
+	}
+	if (callback && collection && callback->send_event)
+		callback->send_event(pdata->hsdev, collection->usage,
+				callback->pdev);
+	spin_unlock_irqrestore(&pdata->lock, flags);
+
+err_report:
+	return 1;
+}
+
+static int sensor_hub_probe(struct hid_device *hdev,
+				const struct hid_device_id *id)
+{
+	int ret;
+	struct sensor_hub_data *sd;
+	int i;
+	char *name;
+	struct hid_report *report;
+	struct hid_report_enum *report_enum;
+	struct hid_field *field;
+	int dev_cnt;
+
+	sd = kzalloc(sizeof(struct sensor_hub_data), GFP_KERNEL);
+	if (!sd) {
+		hid_err(hdev, "cannot allocate Sensor data\n");
+		return -ENOMEM;
+	}
+	sd->hsdev = kzalloc(sizeof(struct hid_sensor_hub_device), GFP_KERNEL);
+	if (!sd->hsdev) {
+		hid_err(hdev, "cannot allocate hid_sensor_hub_device\n");
+		ret = -ENOMEM;
+		goto err_free_hub;
+	}
+	hid_set_drvdata(hdev, sd);
+	sd->hsdev->hdev = hdev;
+	sd->hsdev->vendor_id = hdev->vendor;
+	sd->hsdev->product_id = hdev->product;
+	spin_lock_init(&sd->lock);
+	spin_lock_init(&sd->dyn_callback_lock);
+	mutex_init(&sd->mutex);
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "parse failed\n");
+		goto err_free;
+	}
+	if (sensor_hub_check_for_sensor_page(hdev) < 0) {
+		hid_err(hdev, "sensor page not found\n");
+		goto err_free;
+	}
+	INIT_LIST_HEAD(&hdev->inputs);
+
+	ret = hid_hw_start(hdev, 0);
+	if (ret) {
+		hid_err(hdev, "hw start failed\n");
+		goto err_free;
+	}
+	ret = hid_hw_open(hdev);
+	if (ret) {
+		hid_err(hdev, "failed to open input interrupt pipe\n");
+		goto err_stop_hw;
+	}
+
+	INIT_LIST_HEAD(&sd->dyn_callback_list);
+	sd->hid_sensor_client_cnt = 0;
+	report_enum = &hdev->report_enum[HID_INPUT_REPORT];
+
+	dev_cnt = sensor_hub_get_physical_device_count(report_enum);
+	if (dev_cnt > HID_MAX_PHY_DEVICES) {
+		hid_err(hdev, "Invalid Physical device count\n");
+		ret = -EINVAL;
+		goto err_close;
+	}
+	sd->hid_sensor_hub_client_devs = kzalloc(dev_cnt *
+						sizeof(struct mfd_cell),
+						GFP_KERNEL);
+	if (sd->hid_sensor_hub_client_devs == NULL) {
+		hid_err(hdev, "Failed to allocate memory for mfd cells\n");
+			ret = -ENOMEM;
+			goto err_close;
+	}
+	list_for_each_entry(report, &report_enum->report_list, list) {
+		hid_dbg(hdev, "Report id:%x\n", report->id);
+		field = report->field[0];
+		if (report->maxfield && field &&
+					field->physical) {
+			name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x",
+						field->physical);
+			if (name  == NULL) {
+				hid_err(hdev, "Failed MFD device name\n");
+					ret = -ENOMEM;
+					goto err_free_names;
+			}
+			sd->hid_sensor_hub_client_devs[
+				sd->hid_sensor_client_cnt].name = name;
+			sd->hid_sensor_hub_client_devs[
+				sd->hid_sensor_client_cnt].platform_data =
+						sd->hsdev;
+			sd->hid_sensor_hub_client_devs[
+				sd->hid_sensor_client_cnt].pdata_size =
+						sizeof(*sd->hsdev);
+			hid_dbg(hdev, "Adding %s:%p\n", name, sd);
+			sd->hid_sensor_client_cnt++;
+		}
+	}
+	ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs,
+		sd->hid_sensor_client_cnt, NULL, 0, NULL);
+	if (ret < 0)
+		goto err_free_names;
+
+	return ret;
+
+err_free_names:
+	for (i = 0; i < sd->hid_sensor_client_cnt ; ++i)
+		kfree(sd->hid_sensor_hub_client_devs[i].name);
+	kfree(sd->hid_sensor_hub_client_devs);
+err_close:
+	hid_hw_close(hdev);
+err_stop_hw:
+	hid_hw_stop(hdev);
+err_free:
+	kfree(sd->hsdev);
+err_free_hub:
+	kfree(sd);
+
+	return ret;
+}
+
+static void sensor_hub_remove(struct hid_device *hdev)
+{
+	struct sensor_hub_data *data = hid_get_drvdata(hdev);
+	unsigned long flags;
+	int i;
+
+	hid_dbg(hdev, " hardware removed\n");
+	hid_hw_close(hdev);
+	hid_hw_stop(hdev);
+	spin_lock_irqsave(&data->lock, flags);
+	if (data->pending.status)
+		complete(&data->pending.ready);
+	spin_unlock_irqrestore(&data->lock, flags);
+	mfd_remove_devices(&hdev->dev);
+	for (i = 0; i < data->hid_sensor_client_cnt ; ++i)
+		kfree(data->hid_sensor_hub_client_devs[i].name);
+	kfree(data->hid_sensor_hub_client_devs);
+	hid_set_drvdata(hdev, NULL);
+	mutex_destroy(&data->mutex);
+	kfree(data->hsdev);
+	kfree(data);
+}
+
+static const struct hid_device_id sensor_hub_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086,
+			USB_DEVICE_ID_SENSOR_HUB_1020) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087,
+			USB_DEVICE_ID_SENSOR_HUB_1020) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086,
+			USB_DEVICE_ID_SENSOR_HUB_09FA) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087,
+			USB_DEVICE_ID_SENSOR_HUB_09FA) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM,
+			USB_DEVICE_ID_SENSOR_HUB_7014) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, sensor_hub_devices);
+
+static const struct hid_usage_id sensor_hub_grabbed_usages[] = {
+	{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
+	{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 }
+};
+
+static struct hid_driver sensor_hub_driver = {
+	.name = "hid-sensor-hub",
+	.id_table = sensor_hub_devices,
+	.probe = sensor_hub_probe,
+	.remove = sensor_hub_remove,
+	.raw_event = sensor_hub_raw_event,
+#ifdef CONFIG_PM
+	.suspend = sensor_hub_suspend,
+	.resume =  sensor_hub_resume,
+	.reset_resume =  sensor_hub_reset_resume,
+#endif
+};
+
+static int __init sensor_hub_init(void)
+{
+	return hid_register_driver(&sensor_hub_driver);
+}
+
+static void __exit sensor_hub_exit(void)
+{
+	hid_unregister_driver(&sensor_hub_driver);
+}
+
+module_init(sensor_hub_init);
+module_exit(sensor_hub_exit);
+
+MODULE_DESCRIPTION("HID Sensor Hub driver");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");

+ 6 - 1
drivers/iio/Kconfig

@@ -1,5 +1,5 @@
 #
-# Industrial I/O subsytem configuration
+# Industrial I/O subsystem configuration
 #
 
 menuconfig IIO
@@ -54,10 +54,15 @@ config IIO_CONSUMERS_PER_TRIGGER
 	This value controls the maximum number of consumers that a
 	given trigger may handle. Default is 2.
 
+source "drivers/iio/accel/Kconfig"
 source "drivers/iio/adc/Kconfig"
 source "drivers/iio/amplifiers/Kconfig"
 source "drivers/iio/light/Kconfig"
 source "drivers/iio/frequency/Kconfig"
 source "drivers/iio/dac/Kconfig"
+source "drivers/iio/common/Kconfig"
+source "drivers/iio/gyro/Kconfig"
+source "drivers/iio/light/Kconfig"
+source "drivers/iio/magnetometer/Kconfig"
 
 endif # IIO

+ 5 - 0
drivers/iio/Makefile

@@ -10,8 +10,13 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
 obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
 obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
 
+obj-y += accel/
 obj-y += adc/
 obj-y += amplifiers/
 obj-y += light/
 obj-y += frequency/
 obj-y += dac/
+obj-y += common/
+obj-y += gyro/
+obj-y += light/
+obj-y += magnetometer/

+ 16 - 0
drivers/iio/accel/Kconfig

@@ -0,0 +1,16 @@
+#
+# Accelerometer drivers
+#
+menu "Accelerometers"
+
+config HID_SENSOR_ACCEL_3D
+	depends on HID_SENSOR_HUB
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	select HID_SENSOR_IIO_COMMON
+	tristate "HID Acelerometers 3D"
+	help
+	  Say yes here to build support for the HID SENSOR
+	  accelerometers 3D.
+
+endmenu

+ 5 - 0
drivers/iio/accel/Makefile

@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O accelerometer drivers
+#
+
+obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o

+ 418 - 0
drivers/iio/accel/hid-sensor-accel-3d.c

@@ -0,0 +1,418 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-attributes.h"
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Accelerometer-3D: 0x200073*/
+#define DRIVER_NAME "HID-SENSOR-200073"
+
+enum accel_3d_channel {
+	CHANNEL_SCAN_INDEX_X,
+	CHANNEL_SCAN_INDEX_Y,
+	CHANNEL_SCAN_INDEX_Z,
+	ACCEL_3D_CHANNEL_MAX,
+};
+
+struct accel_3d_state {
+	struct hid_sensor_hub_callbacks callbacks;
+	struct hid_sensor_iio_common common_attributes;
+	struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
+	u32 accel_val[ACCEL_3D_CHANNEL_MAX];
+};
+
+static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
+	HID_USAGE_SENSOR_ACCEL_X_AXIS,
+	HID_USAGE_SENSOR_ACCEL_Y_AXIS,
+	HID_USAGE_SENSOR_ACCEL_Z_AXIS
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec accel_3d_channels[] = {
+	{
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT |
+		IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+		IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+		.scan_index = CHANNEL_SCAN_INDEX_X,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT |
+		IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+		IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+		.scan_index = CHANNEL_SCAN_INDEX_Y,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT |
+		IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+		IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+		.scan_index = CHANNEL_SCAN_INDEX_Z,
+	}
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+						int channel, int size)
+{
+	channels[channel].scan_type.sign = 's';
+	/* Real storage bits will change based on the report desc. */
+	channels[channel].scan_type.realbits = size * 8;
+	/* Maximum size of a sample to capture is u32 */
+	channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int accel_3d_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val, int *val2,
+			      long mask)
+{
+	struct accel_3d_state *accel_state = iio_priv(indio_dev);
+	int report_id = -1;
+	u32 address;
+	int ret;
+	int ret_type;
+
+	*val = 0;
+	*val2 = 0;
+	switch (mask) {
+	case 0:
+		report_id = accel_state->accel[chan->scan_index].report_id;
+		address = accel_3d_addresses[chan->scan_index];
+		if (report_id >= 0)
+			*val = sensor_hub_input_attr_get_raw_value(
+				accel_state->common_attributes.hsdev,
+				HID_USAGE_SENSOR_ACCEL_3D, address,
+				report_id);
+		else {
+			*val = 0;
+			return -EINVAL;
+		}
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		*val = accel_state->accel[CHANNEL_SCAN_INDEX_X].units;
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = hid_sensor_convert_exponent(
+			accel_state->accel[CHANNEL_SCAN_INDEX_X].unit_expo);
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = hid_sensor_read_samp_freq_value(
+			&accel_state->common_attributes, val, val2);
+		ret_type = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		ret = hid_sensor_read_raw_hyst_value(
+			&accel_state->common_attributes, val, val2);
+		ret_type = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	default:
+		ret_type = -EINVAL;
+		break;
+	}
+
+	return ret_type;
+}
+
+/* Channel write_raw handler */
+static int accel_3d_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask)
+{
+	struct accel_3d_state *accel_state = iio_priv(indio_dev);
+	int ret = 0;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = hid_sensor_write_samp_freq_value(
+				&accel_state->common_attributes, val, val2);
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		ret = hid_sensor_write_raw_hyst_value(
+				&accel_state->common_attributes, val, val2);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int accel_3d_write_raw_get_fmt(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       long mask)
+{
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static const struct iio_info accel_3d_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &accel_3d_read_raw,
+	.write_raw = &accel_3d_write_raw,
+	.write_raw_get_fmt = &accel_3d_write_raw_get_fmt,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
+{
+	struct iio_buffer *buffer = indio_dev->buffer;
+	int datum_sz;
+
+	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+	if (!buffer) {
+		dev_err(&indio_dev->dev, "Buffer == NULL\n");
+		return;
+	}
+	datum_sz = buffer->access->get_bytes_per_datum(buffer);
+	if (len > datum_sz) {
+		dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+				datum_sz);
+		return;
+	}
+	iio_push_to_buffer(buffer, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
+				unsigned usage_id,
+				void *priv)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(priv);
+	struct accel_3d_state *accel_state = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "accel_3d_proc_event [%d]\n",
+				accel_state->common_attributes.data_ready);
+	if (accel_state->common_attributes.data_ready)
+		hid_sensor_push_data(indio_dev,
+				(u8 *)accel_state->accel_val,
+				sizeof(accel_state->accel_val));
+
+	return 0;
+}
+
+/* Capture samples in local storage */
+static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
+				unsigned usage_id,
+				size_t raw_len, char *raw_data,
+				void *priv)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(priv);
+	struct accel_3d_state *accel_state = iio_priv(indio_dev);
+	int offset;
+	int ret = -EINVAL;
+
+	switch (usage_id) {
+	case HID_USAGE_SENSOR_ACCEL_X_AXIS:
+	case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
+	case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
+		offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
+		accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] =
+						*(u32 *)raw_data;
+		ret = 0;
+	break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int accel_3d_parse_report(struct platform_device *pdev,
+				struct hid_sensor_hub_device *hsdev,
+				struct iio_chan_spec *channels,
+				unsigned usage_id,
+				struct accel_3d_state *st)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
+		ret = sensor_hub_input_get_attribute_info(hsdev,
+				HID_INPUT_REPORT,
+				usage_id,
+				HID_USAGE_SENSOR_ACCEL_X_AXIS + i,
+				&st->accel[CHANNEL_SCAN_INDEX_X + i]);
+		if (ret < 0)
+			break;
+		accel_3d_adjust_channel_bit_mask(channels,
+				CHANNEL_SCAN_INDEX_X + i,
+				st->accel[CHANNEL_SCAN_INDEX_X + i].size);
+	}
+	dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
+			st->accel[0].index,
+			st->accel[0].report_id,
+			st->accel[1].index, st->accel[1].report_id,
+			st->accel[2].index, st->accel[2].report_id);
+
+	return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int __devinit hid_accel_3d_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	static const char *name = "accel_3d";
+	struct iio_dev *indio_dev;
+	struct accel_3d_state *accel_state;
+	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+	struct iio_chan_spec *channels;
+
+	indio_dev = iio_device_alloc(sizeof(struct accel_3d_state));
+	if (indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, indio_dev);
+
+	accel_state = iio_priv(indio_dev);
+	accel_state->common_attributes.hsdev = hsdev;
+	accel_state->common_attributes.pdev = pdev;
+
+	ret = hid_sensor_parse_common_attributes(hsdev,
+					HID_USAGE_SENSOR_ACCEL_3D,
+					&accel_state->common_attributes);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to setup common attributes\n");
+		goto error_free_dev;
+	}
+
+	channels = kmemdup(accel_3d_channels,
+					sizeof(accel_3d_channels),
+					GFP_KERNEL);
+	if (!channels) {
+		dev_err(&pdev->dev, "failed to duplicate channels\n");
+		goto error_free_dev;
+	}
+
+	ret = accel_3d_parse_report(pdev, hsdev, channels,
+					HID_USAGE_SENSOR_ACCEL_3D, accel_state);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to setup attributes\n");
+		goto error_free_dev_mem;
+	}
+
+	indio_dev->channels = channels;
+	indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->info = &accel_3d_info;
+	indio_dev->name = name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+		NULL, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+		goto error_free_dev_mem;
+	}
+	accel_state->common_attributes.data_ready = false;
+	ret = hid_sensor_setup_trigger(indio_dev, name,
+					&accel_state->common_attributes);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "trigger setup failed\n");
+		goto error_unreg_buffer_funcs;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_trigger;
+	}
+
+	accel_state->callbacks.send_event = accel_3d_proc_event;
+	accel_state->callbacks.capture_sample = accel_3d_capture_sample;
+	accel_state->callbacks.pdev = pdev;
+	ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D,
+					&accel_state->callbacks);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "callback reg failed\n");
+		goto error_iio_unreg;
+	}
+
+	return ret;
+
+error_iio_unreg:
+	iio_device_unregister(indio_dev);
+error_remove_trigger:
+	hid_sensor_remove_trigger(indio_dev);
+error_unreg_buffer_funcs:
+	iio_triggered_buffer_cleanup(indio_dev);
+error_free_dev_mem:
+	kfree(indio_dev->channels);
+error_free_dev:
+	iio_device_free(indio_dev);
+error_ret:
+	return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int __devinit hid_accel_3d_remove(struct platform_device *pdev)
+{
+	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D);
+	iio_device_unregister(indio_dev);
+	hid_sensor_remove_trigger(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	kfree(indio_dev->channels);
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static struct platform_driver hid_accel_3d_platform_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= hid_accel_3d_probe,
+	.remove		= hid_accel_3d_remove,
+};
+module_platform_driver(hid_accel_3d_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Accel 3D");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");

+ 38 - 0
drivers/iio/adc/Kconfig

@@ -3,6 +3,11 @@
 #
 menu "Analog to digital converters"
 
+config AD_SIGMA_DELTA
+	tristate
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+
 config AD7266
 	tristate "Analog Devices AD7265/AD7266 ADC driver"
 	depends on SPI_MASTER
@@ -13,6 +18,33 @@ config AD7266
 	  Say yes here to build support for Analog Devices AD7265 and AD7266
 	  ADCs.
 
+config AD7791
+	tristate "Analog Devices AD7791 ADC driver"
+	depends on SPI
+	select AD_SIGMA_DELTA
+	help
+	  Say yes here to build support for Analog Devices AD7787, AD7788, AD7789,
+	  AD7790 and AD7791 SPI analog to digital converters (ADC). If unsure, say
+	  N (but it is safe to say "Y").
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ad7791.
+
+config AD7476
+	tristate "Analog Devices AD7476 and similar 1-channel ADCs driver"
+	depends on SPI
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build support for Analog Devices AD7273, AD7274, AD7276,
+	  AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
+	  AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC).
+
+	  If unsure, say N (but it's safe to say "Y").
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad7476.
+
 config AT91_ADC
 	tristate "Atmel AT91 ADC"
 	depends on ARCH_AT91
@@ -22,4 +54,10 @@ config AT91_ADC
 	help
 	  Say yes here to build support for Atmel AT91 ADC.
 
+config LP8788_ADC
+	bool "LP8788 ADC driver"
+	depends on MFD_LP8788
+	help
+	  Say yes here to build support for TI LP8788 ADC.
+
 endmenu

+ 4 - 0
drivers/iio/adc/Makefile

@@ -2,5 +2,9 @@
 # Makefile for IIO ADC drivers
 #
 
+obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
 obj-$(CONFIG_AD7266) += ad7266.o
+obj-$(CONFIG_AD7476) += ad7476.o
+obj-$(CONFIG_AD7791) += ad7791.o
 obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o

+ 1 - 1
drivers/iio/adc/ad7266.c

@@ -99,7 +99,7 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p)
 	if (ret == 0) {
 		if (indio_dev->scan_timestamp)
 			((s64 *)st->data)[1] = pf->timestamp;
-		iio_push_to_buffer(buffer, (u8 *)st->data, pf->timestamp);
+		iio_push_to_buffer(buffer, (u8 *)st->data);
 	}
 
 	iio_trigger_notify_done(indio_dev->trig);

+ 149 - 61
drivers/staging/iio/adc/ad7476_core.c → drivers/iio/adc/ad7476.c

@@ -18,8 +18,76 @@
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
 #include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
 
-#include "ad7476.h"
+#define RES_MASK(bits)	((1 << (bits)) - 1)
+
+struct ad7476_state;
+
+struct ad7476_chip_info {
+	unsigned int			int_vref_uv;
+	struct iio_chan_spec		channel[2];
+	void (*reset)(struct ad7476_state *);
+};
+
+struct ad7476_state {
+	struct spi_device		*spi;
+	const struct ad7476_chip_info	*chip_info;
+	struct regulator		*reg;
+	struct spi_transfer		xfer;
+	struct spi_message		msg;
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 * Make the buffer large enough for one 16 bit sample and one 64 bit
+	 * aligned 64 bit timestamp.
+	 */
+	unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)]
+			____cacheline_aligned;
+};
+
+enum ad7476_supported_device_ids {
+	ID_AD7091R,
+	ID_AD7276,
+	ID_AD7277,
+	ID_AD7278,
+	ID_AD7466,
+	ID_AD7467,
+	ID_AD7468,
+	ID_AD7495,
+	ID_AD7940,
+};
+
+static irqreturn_t ad7476_trigger_handler(int irq, void  *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct ad7476_state *st = iio_priv(indio_dev);
+	s64 time_ns;
+	int b_sent;
+
+	b_sent = spi_sync(st->spi, &st->msg);
+	if (b_sent < 0)
+		goto done;
+
+	time_ns = iio_get_time_ns();
+
+	if (indio_dev->scan_timestamp)
+		((s64 *)st->data)[1] = time_ns;
+
+	iio_push_to_buffer(indio_dev->buffer, st->data);
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static void ad7091_reset(struct ad7476_state *st)
+{
+	/* Any transfers with 8 scl cycles will reset the device */
+	spi_read(st->spi, st->data, 1);
+}
 
 static int ad7476_scan_direct(struct ad7476_state *st)
 {
@@ -29,7 +97,7 @@ static int ad7476_scan_direct(struct ad7476_state *st)
 	if (ret)
 		return ret;
 
-	return (st->data[0] << 8) | st->data[1];
+	return be16_to_cpup((__be16 *)st->data);
 }
 
 static int ad7476_read_raw(struct iio_dev *indio_dev,
@@ -40,7 +108,7 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
 {
 	int ret;
 	struct ad7476_state *st = iio_priv(indio_dev);
-	unsigned int scale_uv;
+	int scale_uv;
 
 	switch (m) {
 	case IIO_CHAN_INFO_RAW:
@@ -57,62 +125,80 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
 			RES_MASK(st->chip_info->channel[0].scan_type.realbits);
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
-		scale_uv = (st->int_vref_mv * 1000)
-			>> st->chip_info->channel[0].scan_type.realbits;
-		*val =  scale_uv/1000;
-		*val2 = (scale_uv%1000)*1000;
+		if (!st->chip_info->int_vref_uv) {
+			scale_uv = regulator_get_voltage(st->reg);
+			if (scale_uv < 0)
+				return scale_uv;
+		} else {
+			scale_uv = st->chip_info->int_vref_uv;
+		}
+		scale_uv >>= chan->scan_type.realbits;
+		*val =  scale_uv / 1000;
+		*val2 = (scale_uv % 1000) * 1000;
 		return IIO_VAL_INT_PLUS_MICRO;
 	}
 	return -EINVAL;
 }
 
-#define AD7476_CHAN(bits)					\
+#define _AD7476_CHAN(bits, _shift, _info_mask)			\
 	{							\
 	.type = IIO_VOLTAGE,					\
 	.indexed = 1,						\
-	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |		\
+	.info_mask = _info_mask |				\
 	IIO_CHAN_INFO_SCALE_SHARED_BIT,				\
 	.scan_type = {						\
 		.sign = 'u',					\
-		.realbits = bits,				\
+		.realbits = (bits),				\
 		.storagebits = 16,				\
-		.shift = 12 - bits,				\
+		.shift = (_shift),				\
+		.endianness = IIO_BE,				\
 	},							\
 }
 
+#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
+		IIO_CHAN_INFO_RAW_SEPARATE_BIT)
+#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
+		IIO_CHAN_INFO_RAW_SEPARATE_BIT)
+#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
+
 static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
-	[ID_AD7466] = {
-		.channel[0] = AD7476_CHAN(12),
+	[ID_AD7091R] = {
+		.channel[0] = AD7091R_CHAN(12),
 		.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+		.reset = ad7091_reset,
 	},
-	[ID_AD7467] = {
-		.channel[0] = AD7476_CHAN(10),
+	[ID_AD7276] = {
+		.channel[0] = AD7940_CHAN(12),
 		.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
 	},
-	[ID_AD7468] = {
-		.channel[0] = AD7476_CHAN(8),
+	[ID_AD7277] = {
+		.channel[0] = AD7940_CHAN(10),
 		.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
 	},
-	[ID_AD7475] = {
-		.channel[0] = AD7476_CHAN(12),
+	[ID_AD7278] = {
+		.channel[0] = AD7940_CHAN(8),
 		.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
 	},
-	[ID_AD7476] = {
+	[ID_AD7466] = {
 		.channel[0] = AD7476_CHAN(12),
 		.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
 	},
-	[ID_AD7477] = {
+	[ID_AD7467] = {
 		.channel[0] = AD7476_CHAN(10),
 		.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
 	},
-	[ID_AD7478] = {
+	[ID_AD7468] = {
 		.channel[0] = AD7476_CHAN(8),
 		.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
 	},
 	[ID_AD7495] = {
 		.channel[0] = AD7476_CHAN(12),
 		.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
-		.int_vref_mv = 2500,
+		.int_vref_uv = 2500000,
+	},
+	[ID_AD7940] = {
+		.channel[0] = AD7940_CHAN(14),
+		.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
 	},
 };
 
@@ -123,10 +209,9 @@ static const struct iio_info ad7476_info = {
 
 static int __devinit ad7476_probe(struct spi_device *spi)
 {
-	struct ad7476_platform_data *pdata = spi->dev.platform_data;
 	struct ad7476_state *st;
 	struct iio_dev *indio_dev;
-	int ret, voltage_uv = 0;
+	int ret;
 
 	indio_dev = iio_device_alloc(sizeof(*st));
 	if (indio_dev == NULL) {
@@ -134,25 +219,18 @@ static int __devinit ad7476_probe(struct spi_device *spi)
 		goto error_ret;
 	}
 	st = iio_priv(indio_dev);
-	st->reg = regulator_get(&spi->dev, "vcc");
-	if (!IS_ERR(st->reg)) {
-		ret = regulator_enable(st->reg);
-		if (ret)
-			goto error_put_reg;
-
-		voltage_uv = regulator_get_voltage(st->reg);
-	}
 	st->chip_info =
 		&ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
 
-	if (st->chip_info->int_vref_mv)
-		st->int_vref_mv = st->chip_info->int_vref_mv;
-	else if (pdata && pdata->vref_mv)
-		st->int_vref_mv = pdata->vref_mv;
-	else if (voltage_uv)
-		st->int_vref_mv = voltage_uv / 1000;
-	else
-		dev_warn(&spi->dev, "reference voltage unspecified\n");
+	st->reg = regulator_get(&spi->dev, "vcc");
+	if (IS_ERR(st->reg)) {
+		ret = PTR_ERR(st->reg);
+		goto error_free_dev;
+	}
+
+	ret = regulator_enable(st->reg);
+	if (ret)
+		goto error_put_reg;
 
 	spi_set_drvdata(spi, indio_dev);
 
@@ -173,57 +251,67 @@ static int __devinit ad7476_probe(struct spi_device *spi)
 	spi_message_init(&st->msg);
 	spi_message_add_tail(&st->xfer, &st->msg);
 
-	ret = ad7476_register_ring_funcs_and_init(indio_dev);
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+			&ad7476_trigger_handler, NULL);
 	if (ret)
 		goto error_disable_reg;
 
+	if (st->chip_info->reset)
+		st->chip_info->reset(st);
+
 	ret = iio_device_register(indio_dev);
 	if (ret)
 		goto error_ring_unregister;
 	return 0;
 
 error_ring_unregister:
-	ad7476_ring_cleanup(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
 error_disable_reg:
-	if (!IS_ERR(st->reg))
-		regulator_disable(st->reg);
+	regulator_disable(st->reg);
 error_put_reg:
-	if (!IS_ERR(st->reg))
-		regulator_put(st->reg);
+	regulator_put(st->reg);
+error_free_dev:
 	iio_device_free(indio_dev);
 
 error_ret:
 	return ret;
 }
 
-static int ad7476_remove(struct spi_device *spi)
+static int __devexit ad7476_remove(struct spi_device *spi)
 {
 	struct iio_dev *indio_dev = spi_get_drvdata(spi);
 	struct ad7476_state *st = iio_priv(indio_dev);
 
 	iio_device_unregister(indio_dev);
-	ad7476_ring_cleanup(indio_dev);
-	if (!IS_ERR(st->reg)) {
-		regulator_disable(st->reg);
-		regulator_put(st->reg);
-	}
+	iio_triggered_buffer_cleanup(indio_dev);
+	regulator_disable(st->reg);
+	regulator_put(st->reg);
 	iio_device_free(indio_dev);
 
 	return 0;
 }
 
 static const struct spi_device_id ad7476_id[] = {
+	{"ad7091r", ID_AD7091R},
+	{"ad7273", ID_AD7277},
+	{"ad7274", ID_AD7276},
+	{"ad7276", ID_AD7276},
+	{"ad7277", ID_AD7277},
+	{"ad7278", ID_AD7278},
 	{"ad7466", ID_AD7466},
 	{"ad7467", ID_AD7467},
 	{"ad7468", ID_AD7468},
-	{"ad7475", ID_AD7475},
-	{"ad7476", ID_AD7476},
-	{"ad7476a", ID_AD7476},
-	{"ad7477", ID_AD7477},
-	{"ad7477a", ID_AD7477},
-	{"ad7478", ID_AD7478},
-	{"ad7478a", ID_AD7478},
+	{"ad7475", ID_AD7466},
+	{"ad7476", ID_AD7466},
+	{"ad7476a", ID_AD7466},
+	{"ad7477", ID_AD7467},
+	{"ad7477a", ID_AD7467},
+	{"ad7478", ID_AD7468},
+	{"ad7478a", ID_AD7468},
 	{"ad7495", ID_AD7495},
+	{"ad7910", ID_AD7467},
+	{"ad7920", ID_AD7466},
+	{"ad7940", ID_AD7940},
 	{}
 };
 MODULE_DEVICE_TABLE(spi, ad7476_id);
@@ -240,5 +328,5 @@ static struct spi_driver ad7476_driver = {
 module_spi_driver(ad7476_driver);
 
 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("Analog Devices AD7475/6/7/8(A) AD7466/7/8 ADC");
+MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
 MODULE_LICENSE("GPL v2");

+ 460 - 0
drivers/iio/adc/ad7791.c

@@ -0,0 +1,460 @@
+/*
+ * AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#include <linux/platform_data/ad7791.h>
+
+#define AD7791_REG_COMM			0x0 /* For writes */
+#define AD7791_REG_STATUS		0x0 /* For reads */
+#define AD7791_REG_MODE			0x1
+#define AD7791_REG_FILTER		0x2
+#define AD7791_REG_DATA			0x3
+
+#define AD7791_MODE_CONTINUOUS		0x00
+#define AD7791_MODE_SINGLE		0x02
+#define AD7791_MODE_POWERDOWN		0x03
+
+#define AD7791_CH_AIN1P_AIN1N		0x00
+#define AD7791_CH_AIN2			0x01
+#define AD7791_CH_AIN1N_AIN1N		0x02
+#define AD7791_CH_AVDD_MONITOR		0x03
+
+#define AD7791_FILTER_CLK_DIV_1		(0x0 << 4)
+#define AD7791_FILTER_CLK_DIV_2		(0x1 << 4)
+#define AD7791_FILTER_CLK_DIV_4		(0x2 << 4)
+#define AD7791_FILTER_CLK_DIV_8		(0x3 << 4)
+#define AD7791_FILTER_CLK_MASK		(0x3 << 4)
+#define AD7791_FILTER_RATE_120		0x0
+#define AD7791_FILTER_RATE_100		0x1
+#define AD7791_FILTER_RATE_33_3		0x2
+#define AD7791_FILTER_RATE_20		0x3
+#define AD7791_FILTER_RATE_16_6		0x4
+#define AD7791_FILTER_RATE_16_7		0x5
+#define AD7791_FILTER_RATE_13_3		0x6
+#define AD7791_FILTER_RATE_9_5		0x7
+#define AD7791_FILTER_RATE_MASK		0x7
+
+#define AD7791_MODE_BUFFER		BIT(1)
+#define AD7791_MODE_UNIPOLAR		BIT(2)
+#define AD7791_MODE_BURNOUT		BIT(3)
+#define AD7791_MODE_SEL_MASK		(0x3 << 6)
+#define AD7791_MODE_SEL(x)		((x) << 6)
+
+#define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \
+const struct iio_chan_spec name[] = { \
+	AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
+		(bits), (storagebits), 0), \
+	AD_SD_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \
+	AD_SD_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \
+		(bits), (storagebits), 0), \
+	AD_SD_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR,  \
+		(bits), (storagebits), 0), \
+	IIO_CHAN_SOFT_TIMESTAMP(4), \
+}
+
+#define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \
+const struct iio_chan_spec name[] = { \
+	AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
+		(bits), (storagebits), 0), \
+	AD_SD_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \
+		(bits), (storagebits), 0), \
+	AD_SD_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \
+		(bits), (storagebits), 0), \
+	IIO_CHAN_SOFT_TIMESTAMP(3), \
+}
+
+static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32);
+static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16);
+static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32);
+
+enum {
+	AD7787,
+	AD7788,
+	AD7789,
+	AD7790,
+	AD7791,
+};
+
+enum ad7791_chip_info_flags {
+	AD7791_FLAG_HAS_FILTER		= (1 << 0),
+	AD7791_FLAG_HAS_BUFFER		= (1 << 1),
+	AD7791_FLAG_HAS_UNIPOLAR	= (1 << 2),
+	AD7791_FLAG_HAS_BURNOUT		= (1 << 3),
+};
+
+struct ad7791_chip_info {
+	const struct iio_chan_spec *channels;
+	unsigned int num_channels;
+	enum ad7791_chip_info_flags flags;
+};
+
+static const struct ad7791_chip_info ad7791_chip_infos[] = {
+	[AD7787] = {
+		.channels = ad7787_channels,
+		.num_channels = ARRAY_SIZE(ad7787_channels),
+		.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+			AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
+	},
+	[AD7788] = {
+		.channels = ad7790_channels,
+		.num_channels = ARRAY_SIZE(ad7790_channels),
+		.flags = AD7791_FLAG_HAS_UNIPOLAR,
+	},
+	[AD7789] = {
+		.channels = ad7791_channels,
+		.num_channels = ARRAY_SIZE(ad7791_channels),
+		.flags = AD7791_FLAG_HAS_UNIPOLAR,
+	},
+	[AD7790] = {
+		.channels = ad7790_channels,
+		.num_channels = ARRAY_SIZE(ad7790_channels),
+		.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+			AD7791_FLAG_HAS_BURNOUT,
+	},
+	[AD7791] = {
+		.channels = ad7791_channels,
+		.num_channels = ARRAY_SIZE(ad7791_channels),
+		.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+			AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
+	},
+};
+
+struct ad7791_state {
+	struct ad_sigma_delta sd;
+	uint8_t mode;
+	uint8_t filter;
+
+	struct regulator *reg;
+	const struct ad7791_chip_info *info;
+};
+
+static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd)
+{
+	return container_of(sd, struct ad7791_state, sd);
+}
+
+static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+	ad_sd_set_comm(sd, channel);
+
+	return 0;
+}
+
+static int ad7791_set_mode(struct ad_sigma_delta *sd,
+	enum ad_sigma_delta_mode mode)
+{
+	struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd);
+
+	switch (mode) {
+	case AD_SD_MODE_CONTINUOUS:
+		mode = AD7791_MODE_CONTINUOUS;
+		break;
+	case AD_SD_MODE_SINGLE:
+		mode = AD7791_MODE_SINGLE;
+		break;
+	case AD_SD_MODE_IDLE:
+	case AD_SD_MODE_POWERDOWN:
+		mode = AD7791_MODE_POWERDOWN;
+		break;
+	}
+
+	st->mode &= ~AD7791_MODE_SEL_MASK;
+	st->mode |= AD7791_MODE_SEL(mode);
+
+	return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode);
+}
+
+static const struct ad_sigma_delta_info ad7791_sigma_delta_info = {
+	.set_channel = ad7791_set_channel,
+	.set_mode = ad7791_set_mode,
+	.has_registers = true,
+	.addr_shift = 4,
+	.read_mask = BIT(3),
+};
+
+static int ad7791_read_raw(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+	struct ad7791_state *st = iio_priv(indio_dev);
+	bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR);
+	unsigned long long scale_pv;
+
+	switch (info) {
+	case IIO_CHAN_INFO_RAW:
+		return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+	case IIO_CHAN_INFO_OFFSET:
+		/**
+		 * Unipolar: 0 to VREF
+		 * Bipolar -VREF to VREF
+		 **/
+		if (unipolar)
+			*val = 0;
+		else
+			*val = -(1 << (chan->scan_type.realbits - 1));
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		/* The monitor channel uses an internal reference. */
+		if (chan->address == AD7791_CH_AVDD_MONITOR) {
+			scale_pv = 5850000000000ULL;
+		} else {
+			int voltage_uv;
+
+			voltage_uv = regulator_get_voltage(st->reg);
+			if (voltage_uv < 0)
+				return voltage_uv;
+			scale_pv = (unsigned long long)voltage_uv * 1000000;
+		}
+		if (unipolar)
+			scale_pv >>= chan->scan_type.realbits;
+		else
+			scale_pv >>= chan->scan_type.realbits - 1;
+		*val2 = do_div(scale_pv, 1000000000);
+		*val = scale_pv;
+
+		return IIO_VAL_INT_PLUS_NANO;
+	}
+
+	return -EINVAL;
+}
+
+static const char * const ad7791_sample_freq_avail[] = {
+	[AD7791_FILTER_RATE_120] = "120",
+	[AD7791_FILTER_RATE_100] = "100",
+	[AD7791_FILTER_RATE_33_3] = "33.3",
+	[AD7791_FILTER_RATE_20] = "20",
+	[AD7791_FILTER_RATE_16_6] = "16.6",
+	[AD7791_FILTER_RATE_16_7] = "16.7",
+	[AD7791_FILTER_RATE_13_3] = "13.3",
+	[AD7791_FILTER_RATE_9_5] = "9.5",
+};
+
+static ssize_t ad7791_read_frequency(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct ad7791_state *st = iio_priv(indio_dev);
+	unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK;
+
+	return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]);
+}
+
+static ssize_t ad7791_write_frequency(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct ad7791_state *st = iio_priv(indio_dev);
+	int i, ret;
+
+	mutex_lock(&indio_dev->mlock);
+	if (iio_buffer_enabled(indio_dev)) {
+		mutex_unlock(&indio_dev->mlock);
+		return -EBUSY;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	ret = -EINVAL;
+
+	for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) {
+		if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) {
+
+			mutex_lock(&indio_dev->mlock);
+			st->filter &= ~AD7791_FILTER_RATE_MASK;
+			st->filter |= i;
+			ad_sd_write_reg(&st->sd, AD7791_REG_FILTER,
+					 sizeof(st->filter), st->filter);
+			mutex_unlock(&indio_dev->mlock);
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+		ad7791_read_frequency,
+		ad7791_write_frequency);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5");
+
+static struct attribute *ad7791_attributes[] = {
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group ad7791_attribute_group = {
+	.attrs = ad7791_attributes,
+};
+
+static const struct iio_info ad7791_info = {
+	.read_raw = &ad7791_read_raw,
+	.attrs = &ad7791_attribute_group,
+	.validate_trigger = ad_sd_validate_trigger,
+	.driver_module = THIS_MODULE,
+};
+
+static const struct iio_info ad7791_no_filter_info = {
+	.read_raw = &ad7791_read_raw,
+	.validate_trigger = ad_sd_validate_trigger,
+	.driver_module = THIS_MODULE,
+};
+
+static int __devinit ad7791_setup(struct ad7791_state *st,
+	struct ad7791_platform_data *pdata)
+{
+	/* Set to poweron-reset default values */
+	st->mode = AD7791_MODE_BUFFER;
+	st->filter = AD7791_FILTER_RATE_16_6;
+
+	if (!pdata)
+		return 0;
+
+	if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered)
+		st->mode &= ~AD7791_MODE_BUFFER;
+
+	if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) &&
+		pdata->burnout_current)
+		st->mode |= AD7791_MODE_BURNOUT;
+
+	if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar)
+		st->mode |= AD7791_MODE_UNIPOLAR;
+
+	return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode),
+		st->mode);
+}
+
+static int __devinit ad7791_probe(struct spi_device *spi)
+{
+	struct ad7791_platform_data *pdata = spi->dev.platform_data;
+	struct iio_dev *indio_dev;
+	struct ad7791_state *st;
+	int ret;
+
+	if (!spi->irq) {
+		dev_err(&spi->dev, "Missing IRQ.\n");
+		return -ENXIO;
+	}
+
+	indio_dev = iio_device_alloc(sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+
+	st->reg = regulator_get(&spi->dev, "refin");
+	if (IS_ERR(st->reg)) {
+		ret = PTR_ERR(st->reg);
+		goto err_iio_free;
+	}
+
+	ret = regulator_enable(st->reg);
+	if (ret)
+		goto error_put_reg;
+
+	st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data];
+	ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info);
+
+	spi_set_drvdata(spi, indio_dev);
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = st->info->channels;
+	indio_dev->num_channels = st->info->num_channels;
+	if (st->info->flags & AD7791_FLAG_HAS_FILTER)
+		indio_dev->info = &ad7791_info;
+	else
+		indio_dev->info = &ad7791_no_filter_info;
+
+	ret = ad_sd_setup_buffer_and_trigger(indio_dev);
+	if (ret)
+		goto error_disable_reg;
+
+	ret = ad7791_setup(st, pdata);
+	if (ret)
+		goto error_remove_trigger;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_remove_trigger;
+
+	return 0;
+
+error_remove_trigger:
+	ad_sd_cleanup_buffer_and_trigger(indio_dev);
+error_disable_reg:
+	regulator_disable(st->reg);
+error_put_reg:
+	regulator_put(st->reg);
+err_iio_free:
+	iio_device_free(indio_dev);
+
+	return ret;
+}
+
+static int __devexit ad7791_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct ad7791_state *st = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	ad_sd_cleanup_buffer_and_trigger(indio_dev);
+
+	regulator_disable(st->reg);
+	regulator_put(st->reg);
+
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id ad7791_spi_ids[] = {
+	{ "ad7787", AD7787 },
+	{ "ad7788", AD7788 },
+	{ "ad7789", AD7789 },
+	{ "ad7790", AD7790 },
+	{ "ad7791", AD7791 },
+	{}
+};
+MODULE_DEVICE_TABLE(spi, ad7791_spi_ids);
+
+static struct spi_driver ad7791_driver = {
+	.driver = {
+		.name	= "ad7791",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ad7791_probe,
+	.remove		= __devexit_p(ad7791_remove),
+	.id_table	= ad7791_spi_ids,
+};
+module_spi_driver(ad7791_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Device AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver");
+MODULE_LICENSE("GPL v2");

+ 558 - 0
drivers/iio/adc/ad_sigma_delta.c

@@ -0,0 +1,558 @@
+/*
+ * Support code for Analog Devices Sigma-Delta ADCs
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#include <asm/unaligned.h>
+
+
+#define AD_SD_COMM_CHAN_MASK	0x3
+
+#define AD_SD_REG_COMM		0x00
+#define AD_SD_REG_DATA		0x03
+
+/**
+ * ad_sd_set_comm() - Set communications register
+ *
+ * @sigma_delta: The sigma delta device
+ * @comm: New value for the communications register
+ */
+void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm)
+{
+	/* Some variants use the lower two bits of the communications register
+	 * to select the channel */
+	sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK;
+}
+EXPORT_SYMBOL_GPL(ad_sd_set_comm);
+
+/**
+ * ad_sd_write_reg() - Write a register
+ *
+ * @sigma_delta: The sigma delta device
+ * @reg: Address of the register
+ * @size: Size of the register (0-3)
+ * @val: Value to write to the register
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
+	unsigned int size, unsigned int val)
+{
+	uint8_t *data = sigma_delta->data;
+	struct spi_transfer t = {
+		.tx_buf		= data,
+		.len		= size + 1,
+		.cs_change	= sigma_delta->bus_locked,
+	};
+	struct spi_message m;
+	int ret;
+
+	data[0] = (reg << sigma_delta->info->addr_shift) | sigma_delta->comm;
+
+	switch (size) {
+	case 3:
+		data[1] = val >> 16;
+		data[2] = val >> 8;
+		data[3] = val;
+		break;
+	case 2:
+		put_unaligned_be16(val, &data[1]);
+		break;
+	case 1:
+		data[1] = val;
+		break;
+	case 0:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	if (sigma_delta->bus_locked)
+		ret = spi_sync_locked(sigma_delta->spi, &m);
+	else
+		ret = spi_sync(sigma_delta->spi, &m);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ad_sd_write_reg);
+
+static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
+	unsigned int reg, unsigned int size, uint8_t *val)
+{
+	uint8_t *data = sigma_delta->data;
+	int ret;
+	struct spi_transfer t[] = {
+		{
+			.tx_buf = data,
+			.len = 1,
+		}, {
+			.rx_buf = val,
+			.len = size,
+			.cs_change = sigma_delta->bus_locked,
+		},
+	};
+	struct spi_message m;
+
+	spi_message_init(&m);
+
+	if (sigma_delta->info->has_registers) {
+		data[0] = reg << sigma_delta->info->addr_shift;
+		data[0] |= sigma_delta->info->read_mask;
+		spi_message_add_tail(&t[0], &m);
+	}
+	spi_message_add_tail(&t[1], &m);
+
+	if (sigma_delta->bus_locked)
+		ret = spi_sync_locked(sigma_delta->spi, &m);
+	else
+		ret = spi_sync(sigma_delta->spi, &m);
+
+	return ret;
+}
+
+/**
+ * ad_sd_read_reg() - Read a register
+ *
+ * @sigma_delta: The sigma delta device
+ * @reg: Address of the register
+ * @size: Size of the register (1-4)
+ * @val: Read value
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta,
+	unsigned int reg, unsigned int size, unsigned int *val)
+{
+	int ret;
+
+	ret = ad_sd_read_reg_raw(sigma_delta, reg, size, sigma_delta->data);
+	if (ret < 0)
+		goto out;
+
+	switch (size) {
+	case 4:
+		*val = get_unaligned_be32(sigma_delta->data);
+		break;
+	case 3:
+		*val = (sigma_delta->data[0] << 16) |
+			(sigma_delta->data[1] << 8) |
+			sigma_delta->data[2];
+		break;
+	case 2:
+		*val = get_unaligned_be16(sigma_delta->data);
+		break;
+	case 1:
+		*val = sigma_delta->data[0];
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ad_sd_read_reg);
+
+static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
+	unsigned int mode, unsigned int channel)
+{
+	int ret;
+
+	ret = ad_sigma_delta_set_channel(sigma_delta, channel);
+	if (ret)
+		return ret;
+
+	spi_bus_lock(sigma_delta->spi->master);
+	sigma_delta->bus_locked = true;
+	INIT_COMPLETION(sigma_delta->completion);
+
+	ret = ad_sigma_delta_set_mode(sigma_delta, mode);
+	if (ret < 0)
+		goto out;
+
+	sigma_delta->irq_dis = false;
+	enable_irq(sigma_delta->spi->irq);
+	ret = wait_for_completion_timeout(&sigma_delta->completion, 2*HZ);
+	if (ret == 0) {
+		sigma_delta->irq_dis = true;
+		disable_irq_nosync(sigma_delta->spi->irq);
+		ret = -EIO;
+	} else {
+		ret = 0;
+	}
+out:
+	sigma_delta->bus_locked = false;
+	spi_bus_unlock(sigma_delta->spi->master);
+	ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+
+	return ret;
+}
+
+/**
+ * ad_sd_calibrate_all() - Performs channel calibration
+ * @sigma_delta: The sigma delta device
+ * @cb: Array of channels and calibration type to perform
+ * @n: Number of items in cb
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta,
+	const struct ad_sd_calib_data *cb, unsigned int n)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < n; i++) {
+		ret = ad_sd_calibrate(sigma_delta, cb[i].mode, cb[i].channel);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_calibrate_all);
+
+/**
+ * ad_sigma_delta_single_conversion() - Performs a single data conversion
+ * @indio_dev: The IIO device
+ * @chan: The conversion is done for this channel
+ * @val: Pointer to the location where to store the read value
+ *
+ * Returns: 0 on success, an error value otherwise.
+ */
+int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *val)
+{
+	struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+	unsigned int sample, raw_sample;
+	int ret = 0;
+
+	if (iio_buffer_enabled(indio_dev))
+		return -EBUSY;
+
+	mutex_lock(&indio_dev->mlock);
+	ad_sigma_delta_set_channel(sigma_delta, chan->address);
+
+	spi_bus_lock(sigma_delta->spi->master);
+	sigma_delta->bus_locked = true;
+	INIT_COMPLETION(sigma_delta->completion);
+
+	ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
+
+	sigma_delta->irq_dis = false;
+	enable_irq(sigma_delta->spi->irq);
+	ret = wait_for_completion_interruptible_timeout(
+			&sigma_delta->completion, HZ);
+
+	sigma_delta->bus_locked = false;
+	spi_bus_unlock(sigma_delta->spi->master);
+
+	if (ret == 0)
+		ret = -EIO;
+	if (ret < 0)
+		goto out;
+
+	ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_DATA,
+		DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8),
+		&raw_sample);
+
+out:
+	if (!sigma_delta->irq_dis) {
+		disable_irq_nosync(sigma_delta->spi->irq);
+		sigma_delta->irq_dis = true;
+	}
+
+	ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+	mutex_unlock(&indio_dev->mlock);
+
+	if (ret)
+		return ret;
+
+	sample = raw_sample >> chan->scan_type.shift;
+	sample &= (1 << chan->scan_type.realbits) - 1;
+	*val = sample;
+
+	ret = ad_sigma_delta_postprocess_sample(sigma_delta, raw_sample);
+	if (ret)
+		return ret;
+
+	return IIO_VAL_INT;
+}
+EXPORT_SYMBOL_GPL(ad_sigma_delta_single_conversion);
+
+static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
+{
+	struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+	unsigned int channel;
+	int ret;
+
+	ret = iio_triggered_buffer_postenable(indio_dev);
+	if (ret < 0)
+		return ret;
+
+	channel = find_first_bit(indio_dev->active_scan_mask,
+				 indio_dev->masklength);
+	ret = ad_sigma_delta_set_channel(sigma_delta,
+		indio_dev->channels[channel].address);
+	if (ret)
+		goto err_predisable;
+
+	spi_bus_lock(sigma_delta->spi->master);
+	sigma_delta->bus_locked = true;
+	ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS);
+	if (ret)
+		goto err_unlock;
+
+	sigma_delta->irq_dis = false;
+	enable_irq(sigma_delta->spi->irq);
+
+	return 0;
+
+err_unlock:
+	spi_bus_unlock(sigma_delta->spi->master);
+err_predisable:
+
+	return ret;
+}
+
+static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+
+	INIT_COMPLETION(sigma_delta->completion);
+	wait_for_completion_timeout(&sigma_delta->completion, HZ);
+
+	if (!sigma_delta->irq_dis) {
+		disable_irq_nosync(sigma_delta->spi->irq);
+		sigma_delta->irq_dis = true;
+	}
+
+	ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+
+	sigma_delta->bus_locked = false;
+	return spi_bus_unlock(sigma_delta->spi->master);
+}
+
+static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+	unsigned int reg_size;
+	uint8_t data[16];
+	int ret;
+
+	memset(data, 0x00, 16);
+
+	/* Guaranteed to be aligned with 8 byte boundary */
+	if (indio_dev->scan_timestamp)
+		((s64 *)data)[1] = pf->timestamp;
+
+	reg_size = indio_dev->channels[0].scan_type.realbits +
+			indio_dev->channels[0].scan_type.shift;
+	reg_size = DIV_ROUND_UP(reg_size, 8);
+
+	switch (reg_size) {
+	case 4:
+	case 2:
+	case 1:
+		ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
+			reg_size, &data[0]);
+		break;
+	case 3:
+		/* We store 24 bit samples in a 32 bit word. Keep the upper
+		 * byte set to zero. */
+		ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
+			reg_size, &data[1]);
+		break;
+	}
+
+	iio_push_to_buffer(indio_dev->buffer, (uint8_t *)data);
+
+	iio_trigger_notify_done(indio_dev->trig);
+	sigma_delta->irq_dis = false;
+	enable_irq(sigma_delta->spi->irq);
+
+	return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = {
+	.preenable = &iio_sw_buffer_preenable,
+	.postenable = &ad_sd_buffer_postenable,
+	.predisable = &iio_triggered_buffer_predisable,
+	.postdisable = &ad_sd_buffer_postdisable,
+	.validate_scan_mask = &iio_validate_scan_mask_onehot,
+};
+
+static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private)
+{
+	struct ad_sigma_delta *sigma_delta = private;
+
+	complete(&sigma_delta->completion);
+	disable_irq_nosync(irq);
+	sigma_delta->irq_dis = true;
+	iio_trigger_poll(sigma_delta->trig, iio_get_time_ns());
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * ad_sd_validate_trigger() - validate_trigger callback for ad_sigma_delta devices
+ * @indio_dev: The IIO device
+ * @trig: The new trigger
+ *
+ * Returns: 0 if the 'trig' matches the trigger registered by the ad_sigma_delta
+ * device, -EINVAL otherwise.
+ */
+int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig)
+{
+	struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+
+	if (sigma_delta->trig != trig)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_validate_trigger);
+
+static const struct iio_trigger_ops ad_sd_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static int ad_sd_probe_trigger(struct iio_dev *indio_dev)
+{
+	struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+	int ret;
+
+	sigma_delta->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
+						indio_dev->id);
+	if (sigma_delta->trig == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	sigma_delta->trig->ops = &ad_sd_trigger_ops;
+	init_completion(&sigma_delta->completion);
+
+	ret = request_irq(sigma_delta->spi->irq,
+			  ad_sd_data_rdy_trig_poll,
+			  IRQF_TRIGGER_LOW,
+			  indio_dev->name,
+			  sigma_delta);
+	if (ret)
+		goto error_free_trig;
+
+	if (!sigma_delta->irq_dis) {
+		sigma_delta->irq_dis = true;
+		disable_irq_nosync(sigma_delta->spi->irq);
+	}
+	sigma_delta->trig->dev.parent = &sigma_delta->spi->dev;
+	sigma_delta->trig->private_data = sigma_delta;
+
+	ret = iio_trigger_register(sigma_delta->trig);
+	if (ret)
+		goto error_free_irq;
+
+	/* select default trigger */
+	indio_dev->trig = sigma_delta->trig;
+
+	return 0;
+
+error_free_irq:
+	free_irq(sigma_delta->spi->irq, sigma_delta);
+error_free_trig:
+	iio_trigger_free(sigma_delta->trig);
+error_ret:
+	return ret;
+}
+
+static void ad_sd_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+
+	iio_trigger_unregister(sigma_delta->trig);
+	free_irq(sigma_delta->spi->irq, sigma_delta);
+	iio_trigger_free(sigma_delta->trig);
+}
+
+/**
+ * ad_sd_setup_buffer_and_trigger() -
+ * @indio_dev: The IIO device
+ */
+int ad_sd_setup_buffer_and_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+
+	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+			&ad_sd_trigger_handler, &ad_sd_buffer_setup_ops);
+	if (ret)
+		return ret;
+
+	ret = ad_sd_probe_trigger(indio_dev);
+	if (ret) {
+		iio_triggered_buffer_cleanup(indio_dev);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_setup_buffer_and_trigger);
+
+/**
+ * ad_sd_cleanup_buffer_and_trigger() -
+ * @indio_dev: The IIO device
+ */
+void ad_sd_cleanup_buffer_and_trigger(struct iio_dev *indio_dev)
+{
+	ad_sd_remove_trigger(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL_GPL(ad_sd_cleanup_buffer_and_trigger);
+
+/**
+ * ad_sd_init() - Initializes a ad_sigma_delta struct
+ * @sigma_delta: The ad_sigma_delta device
+ * @indio_dev: The IIO device which the Sigma Delta device is used for
+ * @spi: The SPI device for the ad_sigma_delta device
+ * @info: Device specific callbacks and options
+ *
+ * This function needs to be called before any other operations are performed on
+ * the ad_sigma_delta struct.
+ */
+int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
+	struct spi_device *spi, const struct ad_sigma_delta_info *info)
+{
+	sigma_delta->spi = spi;
+	sigma_delta->info = info;
+	iio_device_set_drvdata(indio_dev, sigma_delta);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_init);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs");
+MODULE_LICENSE("GPL v2");

+ 18 - 59
drivers/iio/adc/at91_adc.c

@@ -82,7 +82,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
 		*timestamp = pf->timestamp;
 	}
 
-	buffer->access->store_to(buffer, (u8 *)st->buffer, pf->timestamp);
+	buffer->access->store_to(buffer, (u8 *)st->buffer);
 
 	iio_trigger_notify_done(idev->trig);
 	st->irq_enabled = true;
@@ -545,13 +545,6 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 		goto error_free_device;
 	}
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res) {
-		dev_err(&pdev->dev, "No resource defined\n");
-		ret = -ENXIO;
-		goto error_ret;
-	}
-
 	platform_set_drvdata(pdev, idev);
 
 	idev->dev.parent = &pdev->dev;
@@ -566,18 +559,12 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 		goto error_free_device;
 	}
 
-	if (!request_mem_region(res->start, resource_size(res),
-				"AT91 adc registers")) {
-		dev_err(&pdev->dev, "Resources are unavailable.\n");
-		ret = -EBUSY;
-		goto error_free_device;
-	}
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
-	st->reg_base = ioremap(res->start, resource_size(res));
+	st->reg_base = devm_request_and_ioremap(&pdev->dev, res);
 	if (!st->reg_base) {
-		dev_err(&pdev->dev, "Failed to map registers.\n");
 		ret = -ENOMEM;
-		goto error_release_mem;
+		goto error_free_device;
 	}
 
 	/*
@@ -592,45 +579,35 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 			  idev);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
-		goto error_unmap_reg;
+		goto error_free_device;
 	}
 
-	st->clk = clk_get(&pdev->dev, "adc_clk");
+	st->clk = devm_clk_get(&pdev->dev, "adc_clk");
 	if (IS_ERR(st->clk)) {
 		dev_err(&pdev->dev, "Failed to get the clock.\n");
 		ret = PTR_ERR(st->clk);
 		goto error_free_irq;
 	}
 
-	ret = clk_prepare(st->clk);
+	ret = clk_prepare_enable(st->clk);
 	if (ret) {
-		dev_err(&pdev->dev, "Could not prepare the clock.\n");
-		goto error_free_clk;
-	}
-
-	ret = clk_enable(st->clk);
-	if (ret) {
-		dev_err(&pdev->dev, "Could not enable the clock.\n");
-		goto error_unprepare_clk;
+		dev_err(&pdev->dev,
+			"Could not prepare or enable the clock.\n");
+		goto error_free_irq;
 	}
 
-	st->adc_clk = clk_get(&pdev->dev, "adc_op_clk");
+	st->adc_clk = devm_clk_get(&pdev->dev, "adc_op_clk");
 	if (IS_ERR(st->adc_clk)) {
 		dev_err(&pdev->dev, "Failed to get the ADC clock.\n");
 		ret = PTR_ERR(st->adc_clk);
 		goto error_disable_clk;
 	}
 
-	ret = clk_prepare(st->adc_clk);
+	ret = clk_prepare_enable(st->adc_clk);
 	if (ret) {
-		dev_err(&pdev->dev, "Could not prepare the ADC clock.\n");
-		goto error_free_adc_clk;
-	}
-
-	ret = clk_enable(st->adc_clk);
-	if (ret) {
-		dev_err(&pdev->dev, "Could not enable the ADC clock.\n");
-		goto error_unprepare_adc_clk;
+		dev_err(&pdev->dev,
+			"Could not prepare or enable the ADC clock.\n");
+		goto error_disable_clk;
 	}
 
 	/*
@@ -694,23 +671,11 @@ error_remove_triggers:
 error_unregister_buffer:
 	at91_adc_buffer_remove(idev);
 error_disable_adc_clk:
-	clk_disable(st->adc_clk);
-error_unprepare_adc_clk:
-	clk_unprepare(st->adc_clk);
-error_free_adc_clk:
-	clk_put(st->adc_clk);
+	clk_disable_unprepare(st->adc_clk);
 error_disable_clk:
-	clk_disable(st->clk);
-error_unprepare_clk:
-	clk_unprepare(st->clk);
-error_free_clk:
-	clk_put(st->clk);
+	clk_disable_unprepare(st->clk);
 error_free_irq:
 	free_irq(st->irq, idev);
-error_unmap_reg:
-	iounmap(st->reg_base);
-error_release_mem:
-	release_mem_region(res->start, resource_size(res));
 error_free_device:
 	iio_device_free(idev);
 error_ret:
@@ -720,20 +685,14 @@ error_ret:
 static int __devexit at91_adc_remove(struct platform_device *pdev)
 {
 	struct iio_dev *idev = platform_get_drvdata(pdev);
-	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	struct at91_adc_state *st = iio_priv(idev);
 
 	iio_device_unregister(idev);
 	at91_adc_trigger_remove(idev);
 	at91_adc_buffer_remove(idev);
 	clk_disable_unprepare(st->adc_clk);
-	clk_put(st->adc_clk);
-	clk_disable(st->clk);
-	clk_unprepare(st->clk);
-	clk_put(st->clk);
+	clk_disable_unprepare(st->clk);
 	free_irq(st->irq, idev);
-	iounmap(st->reg_base);
-	release_mem_region(res->start, resource_size(res));
 	iio_device_free(idev);
 
 	return 0;

+ 264 - 0
drivers/iio/adc/lp8788_adc.c

@@ -0,0 +1,264 @@
+/*
+ * TI LP8788 MFD - ADC driver
+ *
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/machine.h>
+#include <linux/mfd/lp8788.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* register address */
+#define LP8788_ADC_CONF			0x60
+#define LP8788_ADC_RAW			0x61
+#define LP8788_ADC_DONE			0x63
+
+#define ADC_CONV_START			1
+
+struct lp8788_adc {
+	struct lp8788 *lp;
+	struct iio_map *map;
+	struct mutex lock;
+};
+
+static const int lp8788_scale[LPADC_MAX] = {
+	[LPADC_VBATT_5P5] = 1343101,
+	[LPADC_VIN_CHG]   = 3052503,
+	[LPADC_IBATT]     = 610500,
+	[LPADC_IC_TEMP]   = 61050,
+	[LPADC_VBATT_6P0] = 1465201,
+	[LPADC_VBATT_5P0] = 1221001,
+	[LPADC_ADC1]      = 610500,
+	[LPADC_ADC2]      = 610500,
+	[LPADC_VDD]       = 1025641,
+	[LPADC_VCOIN]     = 757020,
+	[LPADC_ADC3]      = 610500,
+	[LPADC_ADC4]      = 610500,
+};
+
+static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id,
+				int *val)
+{
+	unsigned int msb;
+	unsigned int lsb;
+	unsigned int result;
+	u8 data;
+	u8 rawdata[2];
+	int size = ARRAY_SIZE(rawdata);
+	int retry = 5;
+	int ret;
+
+	data = (id << 1) | ADC_CONV_START;
+	ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data);
+	if (ret)
+		goto err_io;
+
+	/* retry until adc conversion is done */
+	data = 0;
+	while (retry--) {
+		usleep_range(100, 200);
+
+		ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data);
+		if (ret)
+			goto err_io;
+
+		/* conversion done */
+		if (data)
+			break;
+	}
+
+	ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size);
+	if (ret)
+		goto err_io;
+
+	msb = (rawdata[0] << 4) & 0x00000ff0;
+	lsb = (rawdata[1] >> 4) & 0x0000000f;
+	result = msb | lsb;
+	*val = result;
+
+	return 0;
+
+err_io:
+	return ret;
+}
+
+static int lp8788_adc_read_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *chan,
+			int *val, int *val2, long mask)
+{
+	struct lp8788_adc *adc = iio_priv(indio_dev);
+	enum lp8788_adc_id id = chan->channel;
+	int ret;
+
+	mutex_lock(&adc->lock);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		*val = lp8788_scale[id] / 1000000;
+		*val2 = lp8788_scale[id] % 1000000;
+		ret = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&adc->lock);
+
+	return ret;
+}
+
+static const struct iio_info lp8788_adc_info = {
+	.read_raw = &lp8788_adc_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+#define LP8788_CHAN(_id, _type) {				\
+		.type = _type,					\
+		.indexed = 1,					\
+		.channel = LPADC_##_id,				\
+		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |	\
+			IIO_CHAN_INFO_SCALE_SEPARATE_BIT,	\
+		.datasheet_name = #_id,				\
+}
+
+static const struct iio_chan_spec lp8788_adc_channels[] = {
+	[LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE),
+	[LPADC_VIN_CHG]   = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE),
+	[LPADC_IBATT]     = LP8788_CHAN(IBATT, IIO_CURRENT),
+	[LPADC_IC_TEMP]   = LP8788_CHAN(IC_TEMP, IIO_TEMP),
+	[LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE),
+	[LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE),
+	[LPADC_ADC1]      = LP8788_CHAN(ADC1, IIO_VOLTAGE),
+	[LPADC_ADC2]      = LP8788_CHAN(ADC2, IIO_VOLTAGE),
+	[LPADC_VDD]       = LP8788_CHAN(VDD, IIO_VOLTAGE),
+	[LPADC_VCOIN]     = LP8788_CHAN(VCOIN, IIO_VOLTAGE),
+	[LPADC_ADC3]      = LP8788_CHAN(ADC3, IIO_VOLTAGE),
+	[LPADC_ADC4]      = LP8788_CHAN(ADC4, IIO_VOLTAGE),
+};
+
+/* default maps used by iio consumer (lp8788-charger driver) */
+static struct iio_map lp8788_default_iio_maps[] = {
+	{
+		.consumer_dev_name = "lp8788-charger",
+		.consumer_channel = "lp8788_vbatt_5p0",
+		.adc_channel_label = "VBATT_5P0",
+	},
+	{
+		.consumer_dev_name = "lp8788-charger",
+		.consumer_channel = "lp8788_adc1",
+		.adc_channel_label = "ADC1",
+	},
+	{ }
+};
+
+static int lp8788_iio_map_register(struct iio_dev *indio_dev,
+				struct lp8788_platform_data *pdata,
+				struct lp8788_adc *adc)
+{
+	struct iio_map *map;
+	int ret;
+
+	map = (!pdata || !pdata->adc_pdata) ?
+		lp8788_default_iio_maps : pdata->adc_pdata;
+
+	ret = iio_map_array_register(indio_dev, map);
+	if (ret) {
+		dev_err(adc->lp->dev, "iio map err: %d\n", ret);
+		return ret;
+	}
+
+	adc->map = map;
+	return 0;
+}
+
+static inline void lp8788_iio_map_unregister(struct iio_dev *indio_dev,
+				struct lp8788_adc *adc)
+{
+	iio_map_array_unregister(indio_dev, adc->map);
+}
+
+static int __devinit lp8788_adc_probe(struct platform_device *pdev)
+{
+	struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
+	struct iio_dev *indio_dev;
+	struct lp8788_adc *adc;
+	int ret;
+
+	indio_dev = iio_device_alloc(sizeof(*adc));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	adc = iio_priv(indio_dev);
+	adc->lp = lp;
+	platform_set_drvdata(pdev, indio_dev);
+
+	ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc);
+	if (ret)
+		goto err_iio_map;
+
+	mutex_init(&adc->lock);
+
+	indio_dev->dev.parent = lp->dev;
+	indio_dev->name = pdev->name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &lp8788_adc_info;
+	indio_dev->channels = lp8788_adc_channels;
+	indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels);
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(lp->dev, "iio dev register err: %d\n", ret);
+		goto err_iio_device;
+	}
+
+	return 0;
+
+err_iio_device:
+	lp8788_iio_map_unregister(indio_dev, adc);
+err_iio_map:
+	iio_device_free(indio_dev);
+	return ret;
+}
+
+static int __devexit lp8788_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct lp8788_adc *adc = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	lp8788_iio_map_unregister(indio_dev, adc);
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static struct platform_driver lp8788_adc_driver = {
+	.probe = lp8788_adc_probe,
+	.remove = __devexit_p(lp8788_adc_remove),
+	.driver = {
+		.name = LP8788_DEV_ADC,
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(lp8788_adc_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lp8788-adc");

+ 5 - 0
drivers/iio/common/Kconfig

@@ -0,0 +1,5 @@
+#
+# IIO common modules
+#
+
+source "drivers/iio/common/hid-sensors/Kconfig"

+ 9 - 0
drivers/iio/common/Makefile

@@ -0,0 +1,9 @@
+#
+# Makefile for the IIO common modules.
+# Common modules contains modules, which can be shared among multiple
+# IIO modules. For example if the trigger processing is common for
+# multiple IIO modules then this can be moved to a common module
+# instead of duplicating in each module.
+#
+
+obj-y += hid-sensors/

+ 26 - 0
drivers/iio/common/hid-sensors/Kconfig

@@ -0,0 +1,26 @@
+#
+# Hid Sensor common modules
+#
+menu "Hid Sensor IIO Common"
+
+config HID_SENSOR_IIO_COMMON
+	tristate "Common modules for all HID Sensor IIO drivers"
+	depends on HID_SENSOR_HUB
+	select IIO_TRIGGER if IIO_BUFFER
+	help
+	  Say yes here to build support for HID sensor to use
+	  HID sensor common processing for attributes and IIO triggers.
+	  There are many attributes which can be shared among multiple
+	  HID sensor drivers, this module contains processing for those
+	  attributes.
+
+config HID_SENSOR_ENUM_BASE_QUIRKS
+	tristate "ENUM base quirks for HID Sensor IIO drivers"
+	depends on HID_SENSOR_IIO_COMMON
+	help
+	  Say yes here to build support for sensor hub FW using
+	  enumeration, which is using 1 as base instead of 0.
+	  Since logical minimum is still set 0 instead of 1,
+	  there is no easy way to differentiate.
+
+endmenu

+ 6 - 0
drivers/iio/common/hid-sensors/Makefile

@@ -0,0 +1,6 @@
+#
+# Makefile for the Hid sensor common modules.
+#
+
+obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o
+hid-sensor-iio-common-y := hid-sensor-attributes.o hid-sensor-trigger.o

+ 250 - 0
drivers/iio/common/hid-sensors/hid-sensor-attributes.c

@@ -0,0 +1,250 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include "hid-sensor-attributes.h"
+
+static int pow_10(unsigned power)
+{
+	int i;
+	int ret = 1;
+	for (i = 0; i < power; ++i)
+		ret = ret * 10;
+
+	return ret;
+}
+
+static void simple_div(int dividend, int divisor, int *whole,
+				int *micro_frac)
+{
+	int rem;
+	int exp = 0;
+
+	*micro_frac = 0;
+	if (divisor == 0) {
+		*whole = 0;
+		return;
+	}
+	*whole = dividend/divisor;
+	rem = dividend % divisor;
+	if (rem) {
+		while (rem <= divisor) {
+			rem *= 10;
+			exp++;
+		}
+		*micro_frac = (rem / divisor) * pow_10(6-exp);
+	}
+}
+
+static void split_micro_fraction(unsigned int no, int exp, int *val1, int *val2)
+{
+	*val1 = no/pow_10(exp);
+	*val2 = no%pow_10(exp) * pow_10(6-exp);
+}
+
+/*
+VTF format uses exponent and variable size format.
+For example if the size is 2 bytes
+0x0067 with VTF16E14 format -> +1.03
+To convert just change to 0x67 to decimal and use two decimal as E14 stands
+for 10^-2.
+Negative numbers are 2's complement
+*/
+static void convert_from_vtf_format(u32 value, int size, int exp,
+					int *val1, int *val2)
+{
+	int sign = 1;
+
+	if (value & BIT(size*8 - 1)) {
+		value =  ((1LL << (size * 8)) - value);
+		sign = -1;
+	}
+	exp = hid_sensor_convert_exponent(exp);
+	if (exp >= 0) {
+		*val1 = sign * value * pow_10(exp);
+		*val2 = 0;
+	} else {
+		split_micro_fraction(value, -exp, val1, val2);
+		if (*val1)
+			*val1 = sign * (*val1);
+		else
+			*val2 = sign * (*val2);
+	}
+}
+
+static u32 convert_to_vtf_format(int size, int exp, int val1, int val2)
+{
+	u32 value;
+	int sign = 1;
+
+	if (val1 < 0 || val2 < 0)
+		sign = -1;
+	exp = hid_sensor_convert_exponent(exp);
+	if (exp < 0) {
+		value = abs(val1) * pow_10(-exp);
+		value += abs(val2) / pow_10(6+exp);
+	} else
+		value = abs(val1) / pow_10(exp);
+	if (sign < 0)
+		value =  ((1LL << (size * 8)) - value);
+
+	return value;
+}
+
+int hid_sensor_read_samp_freq_value(struct hid_sensor_iio_common *st,
+				int *val1, int *val2)
+{
+	s32 value;
+	int ret;
+
+	ret = sensor_hub_get_feature(st->hsdev,
+		st->poll.report_id,
+		st->poll.index, &value);
+	if (ret < 0 || value < 0) {
+		*val1 = *val2 = 0;
+		return -EINVAL;
+	} else {
+		if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
+			simple_div(1000, value, val1, val2);
+		else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
+			simple_div(1, value, val1, val2);
+		else {
+			*val1 = *val2 = 0;
+			return -EINVAL;
+		}
+	}
+
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+EXPORT_SYMBOL(hid_sensor_read_samp_freq_value);
+
+int hid_sensor_write_samp_freq_value(struct hid_sensor_iio_common *st,
+				int val1, int val2)
+{
+	s32 value;
+	int ret;
+
+	if (val1 < 0 || val2 < 0)
+		ret = -EINVAL;
+
+	value = val1 * pow_10(6) + val2;
+	if (value) {
+		if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
+			value = pow_10(9)/value;
+		else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
+			value = pow_10(6)/value;
+		else
+			value = 0;
+	}
+	ret = sensor_hub_set_feature(st->hsdev,
+		st->poll.report_id,
+		st->poll.index, value);
+	if (ret < 0 || value < 0)
+		ret = -EINVAL;
+
+	return ret;
+}
+EXPORT_SYMBOL(hid_sensor_write_samp_freq_value);
+
+int hid_sensor_read_raw_hyst_value(struct hid_sensor_iio_common *st,
+				int *val1, int *val2)
+{
+	s32 value;
+	int ret;
+
+	ret = sensor_hub_get_feature(st->hsdev,
+		st->sensitivity.report_id,
+		st->sensitivity.index, &value);
+	if (ret < 0 || value < 0) {
+		*val1 = *val2 = 0;
+		return -EINVAL;
+	} else {
+		convert_from_vtf_format(value, st->sensitivity.size,
+					st->sensitivity.unit_expo,
+					val1, val2);
+	}
+
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value);
+
+int hid_sensor_write_raw_hyst_value(struct hid_sensor_iio_common *st,
+					int val1, int val2)
+{
+	s32 value;
+	int ret;
+
+	value = convert_to_vtf_format(st->sensitivity.size,
+				st->sensitivity.unit_expo,
+				val1, val2);
+	ret = sensor_hub_set_feature(st->hsdev,
+		st->sensitivity.report_id,
+		st->sensitivity.index, value);
+	if (ret < 0 || value < 0)
+		ret = -EINVAL;
+
+	return ret;
+}
+EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value);
+
+int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
+					u32 usage_id,
+					struct hid_sensor_iio_common *st)
+{
+
+	sensor_hub_input_get_attribute_info(hsdev,
+					HID_FEATURE_REPORT, usage_id,
+					HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
+					&st->poll);
+
+	sensor_hub_input_get_attribute_info(hsdev,
+					HID_FEATURE_REPORT, usage_id,
+					HID_USAGE_SENSOR_PROP_REPORT_STATE,
+					&st->report_state);
+
+	sensor_hub_input_get_attribute_info(hsdev,
+					HID_FEATURE_REPORT, usage_id,
+					HID_USAGE_SENSOR_PROY_POWER_STATE,
+					&st->power_state);
+
+	sensor_hub_input_get_attribute_info(hsdev,
+			HID_FEATURE_REPORT, usage_id,
+			HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,
+			 &st->sensitivity);
+
+	hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x\n",
+			st->poll.index, st->poll.report_id,
+			st->report_state.index, st->report_state.report_id,
+			st->power_state.index, st->power_state.report_id,
+			st->sensitivity.index, st->sensitivity.report_id);
+
+	return 0;
+}
+EXPORT_SYMBOL(hid_sensor_parse_common_attributes);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_DESCRIPTION("HID Sensor common attribute processing");
+MODULE_LICENSE("GPL");

+ 57 - 0
drivers/iio/common/hid-sensors/hid-sensor-attributes.h

@@ -0,0 +1,57 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef _HID_SENSORS_ATTRIBUTES_H
+#define _HID_SENSORS_ATTRIBUTES_H
+
+/* Common hid sensor iio structure */
+struct hid_sensor_iio_common {
+	struct hid_sensor_hub_device *hsdev;
+	struct platform_device *pdev;
+	unsigned usage_id;
+	bool data_ready;
+	struct hid_sensor_hub_attribute_info poll;
+	struct hid_sensor_hub_attribute_info report_state;
+	struct hid_sensor_hub_attribute_info power_state;
+	struct hid_sensor_hub_attribute_info sensitivity;
+};
+
+/*Convert from hid unit expo to regular exponent*/
+static inline int hid_sensor_convert_exponent(int unit_expo)
+{
+	if (unit_expo < 0x08)
+		return unit_expo;
+	else if (unit_expo <= 0x0f)
+		return -(0x0f-unit_expo+1);
+	else
+		return 0;
+}
+
+int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
+					u32 usage_id,
+					struct hid_sensor_iio_common *st);
+int hid_sensor_write_raw_hyst_value(struct hid_sensor_iio_common *st,
+					int val1, int val2);
+int hid_sensor_read_raw_hyst_value(struct hid_sensor_iio_common *st,
+					int *val1, int *val2);
+int hid_sensor_write_samp_freq_value(struct hid_sensor_iio_common *st,
+					int val1, int val2);
+int hid_sensor_read_samp_freq_value(struct hid_sensor_iio_common *st,
+					int *val1, int *val2);
+
+#endif

+ 103 - 0
drivers/iio/common/hid-sensors/hid-sensor-trigger.c

@@ -0,0 +1,103 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/sysfs.h>
+#include "hid-sensor-attributes.h"
+#include "hid-sensor-trigger.h"
+
+static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct hid_sensor_iio_common *st = trig->private_data;
+	int state_val;
+
+	state_val = state ? 1 : 0;
+#if (defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS) || \
+	(defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS_MODULE)
+	++state_val;
+#endif
+	st->data_ready = state;
+	sensor_hub_set_feature(st->hsdev, st->power_state.report_id,
+					st->power_state.index,
+					(s32)state_val);
+
+	sensor_hub_set_feature(st->hsdev, st->report_state.report_id,
+					st->report_state.index,
+					(s32)state_val);
+
+	return 0;
+}
+
+void hid_sensor_remove_trigger(struct iio_dev *indio_dev)
+{
+	iio_trigger_unregister(indio_dev->trig);
+	iio_trigger_free(indio_dev->trig);
+	indio_dev->trig = NULL;
+}
+EXPORT_SYMBOL(hid_sensor_remove_trigger);
+
+static const struct iio_trigger_ops hid_sensor_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = &hid_sensor_data_rdy_trigger_set_state,
+};
+
+int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
+				struct hid_sensor_iio_common *attrb)
+{
+	int ret;
+	struct iio_trigger *trig;
+
+	trig = iio_trigger_alloc("%s-dev%d", name, indio_dev->id);
+	if (trig == NULL) {
+		dev_err(&indio_dev->dev, "Trigger Allocate Failed\n");
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	trig->dev.parent = indio_dev->dev.parent;
+	trig->private_data = attrb;
+	trig->ops = &hid_sensor_trigger_ops;
+	ret = iio_trigger_register(trig);
+
+	if (ret) {
+		dev_err(&indio_dev->dev, "Trigger Register Failed\n");
+		goto error_free_trig;
+	}
+	indio_dev->trig = trig;
+
+	return ret;
+
+error_free_trig:
+	iio_trigger_free(trig);
+error_ret:
+	return ret;
+}
+EXPORT_SYMBOL(hid_sensor_setup_trigger);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_DESCRIPTION("HID Sensor trigger processing");
+MODULE_LICENSE("GPL");

+ 26 - 0
drivers/iio/common/hid-sensors/hid-sensor-trigger.h

@@ -0,0 +1,26 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef _HID_SENSOR_TRIGGER_H
+#define _HID_SENSOR_TRIGGER_H
+
+int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
+				struct hid_sensor_iio_common *attrb);
+void hid_sensor_remove_trigger(struct iio_dev *indio_dev);
+
+#endif

+ 16 - 4
drivers/iio/dac/Kconfig

@@ -57,11 +57,12 @@ config AD5624R_SPI
 
 config AD5446
 	tristate "Analog Devices AD5446 and similar single channel DACs driver"
-	depends on SPI
+	depends on (SPI_MASTER || I2C)
 	help
-	  Say yes here to build support for Analog Devices AD5444, AD5446, AD5450,
-	  AD5451, AD5452, AD5453, AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601,
-	  AD5611, AD5620, AD5621, AD5640, AD5660, AD5662 DACs.
+	  Say yes here to build support for Analog Devices AD5300, AD5301, AD5310,
+	  AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453,
+	  AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5602, AD5611, AD5612,
+	  AD5620, AD5621, AD5622, AD5640, AD5660, AD5662 DACs.
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad5446.
@@ -76,6 +77,17 @@ config AD5504
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad5504.
 
+config AD5755
+	tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver"
+	depends on SPI_MASTER
+	help
+	  Say yes here to build support for Analog Devices AD5755, AD5755-1,
+	  AD5757, AD5735, AD5737 quad channel Digital to
+	  Analog Converter.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad5755.
+
 config AD5764
 	tristate "Analog Devices AD5764/64R/44/44R DAC driver"
 	depends on SPI_MASTER

+ 1 - 0
drivers/iio/dac/Makefile

@@ -9,6 +9,7 @@ obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
 obj-$(CONFIG_AD5064) += ad5064.o
 obj-$(CONFIG_AD5504) += ad5504.o
 obj-$(CONFIG_AD5446) += ad5446.o
+obj-$(CONFIG_AD5755) += ad5755.o
 obj-$(CONFIG_AD5764) += ad5764.o
 obj-$(CONFIG_AD5791) += ad5791.o
 obj-$(CONFIG_AD5686) += ad5686.o

+ 338 - 112
drivers/iio/dac/ad5446.c

@@ -14,6 +14,7 @@
 #include <linux/sysfs.h>
 #include <linux/list.h>
 #include <linux/spi/spi.h>
+#include <linux/i2c.h>
 #include <linux/regulator/consumer.h>
 #include <linux/err.h>
 #include <linux/module.h>
@@ -21,24 +22,40 @@
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
 
-#include "ad5446.h"
+#define MODE_PWRDWN_1k		0x1
+#define MODE_PWRDWN_100k	0x2
+#define MODE_PWRDWN_TRISTATE	0x3
 
-static int ad5446_write(struct ad5446_state *st, unsigned val)
-{
-	__be16 data = cpu_to_be16(val);
-	return spi_write(st->spi, &data, sizeof(data));
-}
+/**
+ * struct ad5446_state - driver instance specific data
+ * @spi:		spi_device
+ * @chip_info:		chip model specific constants, available modes etc
+ * @reg:		supply regulator
+ * @vref_mv:		actual reference voltage used
+ */
 
-static int ad5660_write(struct ad5446_state *st, unsigned val)
-{
-	uint8_t data[3];
+struct ad5446_state {
+	struct device		*dev;
+	const struct ad5446_chip_info	*chip_info;
+	struct regulator		*reg;
+	unsigned short			vref_mv;
+	unsigned			cached_val;
+	unsigned			pwr_down_mode;
+	unsigned			pwr_down;
+};
 
-	data[0] = (val >> 16) & 0xFF;
-	data[1] = (val >> 8) & 0xFF;
-	data[2] = val & 0xFF;
+/**
+ * struct ad5446_chip_info - chip specific information
+ * @channel:		channel spec for the DAC
+ * @int_vref_mv:	AD5620/40/60: the internal reference voltage
+ * @write:		chip specific helper function to write to the register
+ */
 
-	return spi_write(st->spi, data, sizeof(data));
-}
+struct ad5446_chip_info {
+	struct iio_chan_spec	channel;
+	u16			int_vref_mv;
+	int			(*write)(struct ad5446_state *st, unsigned val);
+};
 
 static const char * const ad5446_powerdown_modes[] = {
 	"1kohm_to_gnd", "100kohm_to_gnd", "three_state"
@@ -110,7 +127,7 @@ static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev,
 	return ret ? ret : len;
 }
 
-static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = {
+static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = {
 	{
 		.name = "powerdown",
 		.read = ad5446_read_dac_powerdown,
@@ -136,84 +153,7 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = {
 	_AD5446_CHANNEL(bits, storage, shift, NULL)
 
 #define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \
-	_AD5446_CHANNEL(bits, storage, shift, ad5064_ext_info_powerdown)
-
-static const struct ad5446_chip_info ad5446_chip_info_tbl[] = {
-	[ID_AD5444] = {
-		.channel = AD5446_CHANNEL(12, 16, 2),
-		.write = ad5446_write,
-	},
-	[ID_AD5446] = {
-		.channel = AD5446_CHANNEL(14, 16, 0),
-		.write = ad5446_write,
-	},
-	[ID_AD5450] = {
-		.channel = AD5446_CHANNEL(8, 16, 6),
-		.write = ad5446_write,
-	},
-	[ID_AD5451] = {
-		.channel = AD5446_CHANNEL(10, 16, 4),
-		.write = ad5446_write,
-	},
-	[ID_AD5541A] = {
-		.channel = AD5446_CHANNEL(16, 16, 0),
-		.write = ad5446_write,
-	},
-	[ID_AD5512A] = {
-		.channel = AD5446_CHANNEL(12, 16, 4),
-		.write = ad5446_write,
-	},
-	[ID_AD5553] = {
-		.channel = AD5446_CHANNEL(14, 16, 0),
-		.write = ad5446_write,
-	},
-	[ID_AD5601] = {
-		.channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6),
-		.write = ad5446_write,
-	},
-	[ID_AD5611] = {
-		.channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4),
-		.write = ad5446_write,
-	},
-	[ID_AD5621] = {
-		.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
-		.write = ad5446_write,
-	},
-	[ID_AD5620_2500] = {
-		.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
-		.int_vref_mv = 2500,
-		.write = ad5446_write,
-	},
-	[ID_AD5620_1250] = {
-		.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
-		.int_vref_mv = 1250,
-		.write = ad5446_write,
-	},
-	[ID_AD5640_2500] = {
-		.channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
-		.int_vref_mv = 2500,
-		.write = ad5446_write,
-	},
-	[ID_AD5640_1250] = {
-		.channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
-		.int_vref_mv = 1250,
-		.write = ad5446_write,
-	},
-	[ID_AD5660_2500] = {
-		.channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
-		.int_vref_mv = 2500,
-		.write = ad5660_write,
-	},
-	[ID_AD5660_1250] = {
-		.channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
-		.int_vref_mv = 1250,
-		.write = ad5660_write,
-	},
-	[ID_AD5662] = {
-		.channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
-		.write = ad5660_write,
-	},
-};
+	_AD5446_CHANNEL(bits, storage, shift, ad5446_ext_info_powerdown)
 
 static int ad5446_read_raw(struct iio_dev *indio_dev,
 			   struct iio_chan_spec const *chan,
@@ -272,14 +212,15 @@ static const struct iio_info ad5446_info = {
 	.driver_module = THIS_MODULE,
 };
 
-static int __devinit ad5446_probe(struct spi_device *spi)
+static int __devinit ad5446_probe(struct device *dev, const char *name,
+	const struct ad5446_chip_info *chip_info)
 {
 	struct ad5446_state *st;
 	struct iio_dev *indio_dev;
 	struct regulator *reg;
 	int ret, voltage_uv = 0;
 
-	reg = regulator_get(&spi->dev, "vcc");
+	reg = regulator_get(dev, "vcc");
 	if (!IS_ERR(reg)) {
 		ret = regulator_enable(reg);
 		if (ret)
@@ -294,16 +235,15 @@ static int __devinit ad5446_probe(struct spi_device *spi)
 		goto error_disable_reg;
 	}
 	st = iio_priv(indio_dev);
-	st->chip_info =
-		&ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+	st->chip_info = chip_info;
 
-	spi_set_drvdata(spi, indio_dev);
+	dev_set_drvdata(dev, indio_dev);
 	st->reg = reg;
-	st->spi = spi;
+	st->dev = dev;
 
-	/* Establish that the iio_dev is a child of the spi device */
-	indio_dev->dev.parent = &spi->dev;
-	indio_dev->name = spi_get_device_id(spi)->name;
+	/* Establish that the iio_dev is a child of the device */
+	indio_dev->dev.parent = dev;
+	indio_dev->name = name;
 	indio_dev->info = &ad5446_info;
 	indio_dev->modes = INDIO_DIRECT_MODE;
 	indio_dev->channels = &st->chip_info->channel;
@@ -316,7 +256,7 @@ static int __devinit ad5446_probe(struct spi_device *spi)
 	else if (voltage_uv)
 		st->vref_mv = voltage_uv / 1000;
 	else
-		dev_warn(&spi->dev, "reference voltage unspecified\n");
+		dev_warn(dev, "reference voltage unspecified\n");
 
 	ret = iio_device_register(indio_dev);
 	if (ret)
@@ -336,9 +276,9 @@ error_put_reg:
 	return ret;
 }
 
-static int ad5446_remove(struct spi_device *spi)
+static int ad5446_remove(struct device *dev)
 {
-	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
 	struct ad5446_state *st = iio_priv(indio_dev);
 
 	iio_device_unregister(indio_dev);
@@ -351,7 +291,151 @@ static int ad5446_remove(struct spi_device *spi)
 	return 0;
 }
 
-static const struct spi_device_id ad5446_id[] = {
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static int ad5446_write(struct ad5446_state *st, unsigned val)
+{
+	struct spi_device *spi = to_spi_device(st->dev);
+	__be16 data = cpu_to_be16(val);
+
+	return spi_write(spi, &data, sizeof(data));
+}
+
+static int ad5660_write(struct ad5446_state *st, unsigned val)
+{
+	struct spi_device *spi = to_spi_device(st->dev);
+	uint8_t data[3];
+
+	data[0] = (val >> 16) & 0xFF;
+	data[1] = (val >> 8) & 0xFF;
+	data[2] = val & 0xFF;
+
+	return spi_write(spi, data, sizeof(data));
+}
+
+/**
+ * ad5446_supported_spi_device_ids:
+ * The AD5620/40/60 parts are available in different fixed internal reference
+ * voltage options. The actual part numbers may look differently
+ * (and a bit cryptic), however this style is used to make clear which
+ * parts are supported here.
+ */
+enum ad5446_supported_spi_device_ids {
+	ID_AD5300,
+	ID_AD5310,
+	ID_AD5320,
+	ID_AD5444,
+	ID_AD5446,
+	ID_AD5450,
+	ID_AD5451,
+	ID_AD5541A,
+	ID_AD5512A,
+	ID_AD5553,
+	ID_AD5601,
+	ID_AD5611,
+	ID_AD5621,
+	ID_AD5620_2500,
+	ID_AD5620_1250,
+	ID_AD5640_2500,
+	ID_AD5640_1250,
+	ID_AD5660_2500,
+	ID_AD5660_1250,
+	ID_AD5662,
+};
+
+static const struct ad5446_chip_info ad5446_spi_chip_info[] = {
+	[ID_AD5300] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4),
+		.write = ad5446_write,
+	},
+	[ID_AD5310] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2),
+		.write = ad5446_write,
+	},
+	[ID_AD5320] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0),
+		.write = ad5446_write,
+	},
+	[ID_AD5444] = {
+		.channel = AD5446_CHANNEL(12, 16, 2),
+		.write = ad5446_write,
+	},
+	[ID_AD5446] = {
+		.channel = AD5446_CHANNEL(14, 16, 0),
+		.write = ad5446_write,
+	},
+	[ID_AD5450] = {
+		.channel = AD5446_CHANNEL(8, 16, 6),
+		.write = ad5446_write,
+	},
+	[ID_AD5451] = {
+		.channel = AD5446_CHANNEL(10, 16, 4),
+		.write = ad5446_write,
+	},
+	[ID_AD5541A] = {
+		.channel = AD5446_CHANNEL(16, 16, 0),
+		.write = ad5446_write,
+	},
+	[ID_AD5512A] = {
+		.channel = AD5446_CHANNEL(12, 16, 4),
+		.write = ad5446_write,
+	},
+	[ID_AD5553] = {
+		.channel = AD5446_CHANNEL(14, 16, 0),
+		.write = ad5446_write,
+	},
+	[ID_AD5601] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6),
+		.write = ad5446_write,
+	},
+	[ID_AD5611] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4),
+		.write = ad5446_write,
+	},
+	[ID_AD5621] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+		.write = ad5446_write,
+	},
+	[ID_AD5620_2500] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+		.int_vref_mv = 2500,
+		.write = ad5446_write,
+	},
+	[ID_AD5620_1250] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+		.int_vref_mv = 1250,
+		.write = ad5446_write,
+	},
+	[ID_AD5640_2500] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
+		.int_vref_mv = 2500,
+		.write = ad5446_write,
+	},
+	[ID_AD5640_1250] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
+		.int_vref_mv = 1250,
+		.write = ad5446_write,
+	},
+	[ID_AD5660_2500] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+		.int_vref_mv = 2500,
+		.write = ad5660_write,
+	},
+	[ID_AD5660_1250] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+		.int_vref_mv = 1250,
+		.write = ad5660_write,
+	},
+	[ID_AD5662] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+		.write = ad5660_write,
+	},
+};
+
+static const struct spi_device_id ad5446_spi_ids[] = {
+	{"ad5300", ID_AD5300},
+	{"ad5310", ID_AD5310},
+	{"ad5320", ID_AD5320},
 	{"ad5444", ID_AD5444},
 	{"ad5446", ID_AD5446},
 	{"ad5450", ID_AD5450},
@@ -375,18 +459,160 @@ static const struct spi_device_id ad5446_id[] = {
 	{"ad5662", ID_AD5662},
 	{}
 };
-MODULE_DEVICE_TABLE(spi, ad5446_id);
+MODULE_DEVICE_TABLE(spi, ad5446_spi_ids);
+
+static int __devinit ad5446_spi_probe(struct spi_device *spi)
+{
+	const struct spi_device_id *id = spi_get_device_id(spi);
 
-static struct spi_driver ad5446_driver = {
+	return ad5446_probe(&spi->dev, id->name,
+		&ad5446_spi_chip_info[id->driver_data]);
+}
+
+static int __devexit ad5446_spi_remove(struct spi_device *spi)
+{
+	return ad5446_remove(&spi->dev);
+}
+
+static struct spi_driver ad5446_spi_driver = {
 	.driver = {
 		.name	= "ad5446",
 		.owner	= THIS_MODULE,
 	},
-	.probe		= ad5446_probe,
-	.remove		= __devexit_p(ad5446_remove),
-	.id_table	= ad5446_id,
+	.probe		= ad5446_spi_probe,
+	.remove		= __devexit_p(ad5446_spi_remove),
+	.id_table	= ad5446_spi_ids,
 };
-module_spi_driver(ad5446_driver);
+
+static int __init ad5446_spi_register_driver(void)
+{
+	return spi_register_driver(&ad5446_spi_driver);
+}
+
+static void ad5446_spi_unregister_driver(void)
+{
+	spi_unregister_driver(&ad5446_spi_driver);
+}
+
+#else
+
+static inline int ad5446_spi_register_driver(void) { return 0; }
+static inline void ad5446_spi_unregister_driver(void) { }
+
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static int ad5622_write(struct ad5446_state *st, unsigned val)
+{
+	struct i2c_client *client = to_i2c_client(st->dev);
+	__be16 data = cpu_to_be16(val);
+
+	return i2c_master_send(client, (char *)&data, sizeof(data));
+}
+
+/**
+ * ad5446_supported_i2c_device_ids:
+ * The AD5620/40/60 parts are available in different fixed internal reference
+ * voltage options. The actual part numbers may look differently
+ * (and a bit cryptic), however this style is used to make clear which
+ * parts are supported here.
+ */
+enum ad5446_supported_i2c_device_ids {
+	ID_AD5602,
+	ID_AD5612,
+	ID_AD5622,
+};
+
+static const struct ad5446_chip_info ad5446_i2c_chip_info[] = {
+	[ID_AD5602] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4),
+		.write = ad5622_write,
+	},
+	[ID_AD5612] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2),
+		.write = ad5622_write,
+	},
+	[ID_AD5622] = {
+		.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0),
+		.write = ad5622_write,
+	},
+};
+
+static int __devinit ad5446_i2c_probe(struct i2c_client *i2c,
+	const struct i2c_device_id *id)
+{
+	return ad5446_probe(&i2c->dev, id->name,
+		&ad5446_i2c_chip_info[id->driver_data]);
+}
+
+static int __devexit ad5446_i2c_remove(struct i2c_client *i2c)
+{
+	return ad5446_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id ad5446_i2c_ids[] = {
+	{"ad5301", ID_AD5602},
+	{"ad5311", ID_AD5612},
+	{"ad5321", ID_AD5622},
+	{"ad5602", ID_AD5602},
+	{"ad5612", ID_AD5612},
+	{"ad5622", ID_AD5622},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, ad5446_i2c_ids);
+
+static struct i2c_driver ad5446_i2c_driver = {
+	.driver = {
+		   .name = "ad5446",
+		   .owner = THIS_MODULE,
+	},
+	.probe = ad5446_i2c_probe,
+	.remove = __devexit_p(ad5446_i2c_remove),
+	.id_table = ad5446_i2c_ids,
+};
+
+static int __init ad5446_i2c_register_driver(void)
+{
+	return i2c_add_driver(&ad5446_i2c_driver);
+}
+
+static void __exit ad5446_i2c_unregister_driver(void)
+{
+	i2c_del_driver(&ad5446_i2c_driver);
+}
+
+#else
+
+static inline int ad5446_i2c_register_driver(void) { return 0; }
+static inline void ad5446_i2c_unregister_driver(void) { }
+
+#endif
+
+static int __init ad5446_init(void)
+{
+	int ret;
+
+	ret = ad5446_spi_register_driver();
+	if (ret)
+		return ret;
+
+	ret = ad5446_i2c_register_driver();
+	if (ret) {
+		ad5446_spi_unregister_driver();
+		return ret;
+	}
+
+	return 0;
+}
+module_init(ad5446_init);
+
+static void __exit ad5446_exit(void)
+{
+	ad5446_i2c_unregister_driver();
+	ad5446_spi_unregister_driver();
+}
+module_exit(ad5446_exit);
 
 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
 MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC");

+ 0 - 91
drivers/iio/dac/ad5446.h

@@ -1,91 +0,0 @@
-/*
- * AD5446 SPI DAC driver
- *
- * Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-#ifndef IIO_DAC_AD5446_H_
-#define IIO_DAC_AD5446_H_
-
-/* DAC Control Bits */
-
-#define AD5446_LOAD		(0x0 << 14) /* Load and update */
-#define AD5446_SDO_DIS		(0x1 << 14) /* Disable SDO */
-#define AD5446_NOP		(0x2 << 14) /* No operation */
-#define AD5446_CLK_RISING	(0x3 << 14) /* Clock data on rising edge */
-
-#define AD5620_LOAD		(0x0 << 14) /* Load and update Norm Operation*/
-#define AD5620_PWRDWN_1k	(0x1 << 14) /* Power-down: 1kOhm to GND */
-#define AD5620_PWRDWN_100k	(0x2 << 14) /* Power-down: 100kOhm to GND */
-#define AD5620_PWRDWN_TRISTATE	(0x3 << 14) /* Power-down: Three-state */
-
-#define AD5660_LOAD		(0x0 << 16) /* Load and update Norm Operation*/
-#define AD5660_PWRDWN_1k	(0x1 << 16) /* Power-down: 1kOhm to GND */
-#define AD5660_PWRDWN_100k	(0x2 << 16) /* Power-down: 100kOhm to GND */
-#define AD5660_PWRDWN_TRISTATE	(0x3 << 16) /* Power-down: Three-state */
-
-#define MODE_PWRDWN_1k		0x1
-#define MODE_PWRDWN_100k	0x2
-#define MODE_PWRDWN_TRISTATE	0x3
-
-/**
- * struct ad5446_state - driver instance specific data
- * @spi:		spi_device
- * @chip_info:		chip model specific constants, available modes etc
- * @reg:		supply regulator
- * @vref_mv:		actual reference voltage used
- */
-
-struct ad5446_state {
-	struct spi_device		*spi;
-	const struct ad5446_chip_info	*chip_info;
-	struct regulator		*reg;
-	unsigned short			vref_mv;
-	unsigned			cached_val;
-	unsigned			pwr_down_mode;
-	unsigned			pwr_down;
-};
-
-/**
- * struct ad5446_chip_info - chip specific information
- * @channel:		channel spec for the DAC
- * @int_vref_mv:	AD5620/40/60: the internal reference voltage
- * @write:		chip specific helper function to write to the register
- */
-
-struct ad5446_chip_info {
-	struct iio_chan_spec	channel;
-	u16			int_vref_mv;
-	int			(*write)(struct ad5446_state *st, unsigned val);
-};
-
-/**
- * ad5446_supported_device_ids:
- * The AD5620/40/60 parts are available in different fixed internal reference
- * voltage options. The actual part numbers may look differently
- * (and a bit cryptic), however this style is used to make clear which
- * parts are supported here.
- */
-
-enum ad5446_supported_device_ids {
-	ID_AD5444,
-	ID_AD5446,
-	ID_AD5450,
-	ID_AD5451,
-	ID_AD5541A,
-	ID_AD5512A,
-	ID_AD5553,
-	ID_AD5601,
-	ID_AD5611,
-	ID_AD5621,
-	ID_AD5620_2500,
-	ID_AD5620_1250,
-	ID_AD5640_2500,
-	ID_AD5640_1250,
-	ID_AD5660_2500,
-	ID_AD5660_1250,
-	ID_AD5662,
-};
-
-#endif /* IIO_DAC_AD5446_H_ */

+ 650 - 0
drivers/iio/dac/ad5755.c

@@ -0,0 +1,650 @@
+/*
+ * AD5755, AD5755-1, AD5757, AD5735, AD5737 Digital to analog converters driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/platform_data/ad5755.h>
+
+#define AD5755_NUM_CHANNELS 4
+
+#define AD5755_ADDR(x)			((x) << 16)
+
+#define AD5755_WRITE_REG_DATA(chan)	(chan)
+#define AD5755_WRITE_REG_GAIN(chan)	(0x08 | (chan))
+#define AD5755_WRITE_REG_OFFSET(chan)	(0x10 | (chan))
+#define AD5755_WRITE_REG_CTRL(chan)	(0x1c | (chan))
+
+#define AD5755_READ_REG_DATA(chan)	(chan)
+#define AD5755_READ_REG_CTRL(chan)	(0x4 | (chan))
+#define AD5755_READ_REG_GAIN(chan)	(0x8 | (chan))
+#define AD5755_READ_REG_OFFSET(chan)	(0xc | (chan))
+#define AD5755_READ_REG_CLEAR(chan)	(0x10 | (chan))
+#define AD5755_READ_REG_SLEW(chan)	(0x14 | (chan))
+#define AD5755_READ_REG_STATUS		0x18
+#define AD5755_READ_REG_MAIN		0x19
+#define AD5755_READ_REG_DC_DC		0x1a
+
+#define AD5755_CTRL_REG_SLEW	0x0
+#define AD5755_CTRL_REG_MAIN	0x1
+#define AD5755_CTRL_REG_DAC	0x2
+#define AD5755_CTRL_REG_DC_DC	0x3
+#define AD5755_CTRL_REG_SW	0x4
+
+#define AD5755_READ_FLAG 0x800000
+
+#define AD5755_NOOP 0x1CE000
+
+#define AD5755_DAC_INT_EN			BIT(8)
+#define AD5755_DAC_CLR_EN			BIT(7)
+#define AD5755_DAC_OUT_EN			BIT(6)
+#define AD5755_DAC_INT_CURRENT_SENSE_RESISTOR	BIT(5)
+#define AD5755_DAC_DC_DC_EN			BIT(4)
+#define AD5755_DAC_VOLTAGE_OVERRANGE_EN		BIT(3)
+
+#define AD5755_DC_DC_MAXV			0
+#define AD5755_DC_DC_FREQ_SHIFT			2
+#define AD5755_DC_DC_PHASE_SHIFT		4
+#define AD5755_EXT_DC_DC_COMP_RES		BIT(6)
+
+#define AD5755_SLEW_STEP_SIZE_SHIFT		0
+#define AD5755_SLEW_RATE_SHIFT			3
+#define AD5755_SLEW_ENABLE			BIT(12)
+
+/**
+ * struct ad5755_chip_info - chip specific information
+ * @channel_template:	channel specification
+ * @calib_shift:	shift for the calibration data registers
+ * @has_voltage_out:	whether the chip has voltage outputs
+ */
+struct ad5755_chip_info {
+	const struct iio_chan_spec channel_template;
+	unsigned int calib_shift;
+	bool has_voltage_out;
+};
+
+/**
+ * struct ad5755_state - driver instance specific data
+ * @spi:	spi device the driver is attached to
+ * @chip_info:	chip model specific constants, available modes etc
+ * @pwr_down:	bitmask which contains  hether a channel is powered down or not
+ * @ctrl:	software shadow of the channel ctrl registers
+ * @channels:	iio channel spec for the device
+ * @data:	spi transfer buffers
+ */
+struct ad5755_state {
+	struct spi_device		*spi;
+	const struct ad5755_chip_info	*chip_info;
+	unsigned int			pwr_down;
+	unsigned int			ctrl[AD5755_NUM_CHANNELS];
+	struct iio_chan_spec		channels[AD5755_NUM_CHANNELS];
+
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 */
+
+	union {
+		u32 d32;
+		u8 d8[4];
+	} data[2] ____cacheline_aligned;
+};
+
+enum ad5755_type {
+	ID_AD5755,
+	ID_AD5757,
+	ID_AD5735,
+	ID_AD5737,
+};
+
+static int ad5755_write_unlocked(struct iio_dev *indio_dev,
+	unsigned int reg, unsigned int val)
+{
+	struct ad5755_state *st = iio_priv(indio_dev);
+
+	st->data[0].d32 = cpu_to_be32((reg << 16) | val);
+
+	return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5755_write_ctrl_unlocked(struct iio_dev *indio_dev,
+	unsigned int channel, unsigned int reg, unsigned int val)
+{
+	return ad5755_write_unlocked(indio_dev,
+		AD5755_WRITE_REG_CTRL(channel), (reg << 13) | val);
+}
+
+static int ad5755_write(struct iio_dev *indio_dev, unsigned int reg,
+	unsigned int val)
+{
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+	ret = ad5755_write_unlocked(indio_dev, reg, val);
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel,
+	unsigned int reg, unsigned int val)
+{
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+	ret = ad5755_write_ctrl_unlocked(indio_dev, channel, reg, val);
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr)
+{
+	struct ad5755_state *st = iio_priv(indio_dev);
+	struct spi_message m;
+	int ret;
+	struct spi_transfer t[] = {
+		{
+			.tx_buf = &st->data[0].d8[1],
+			.len = 3,
+			.cs_change = 1,
+		}, {
+			.tx_buf = &st->data[1].d8[1],
+			.rx_buf = &st->data[1].d8[1],
+			.len = 3,
+		},
+	};
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t[0], &m);
+	spi_message_add_tail(&t[1], &m);
+
+	mutex_lock(&indio_dev->mlock);
+
+	st->data[0].d32 = cpu_to_be32(AD5755_READ_FLAG | (addr << 16));
+	st->data[1].d32 = cpu_to_be32(AD5755_NOOP);
+
+	ret = spi_sync(st->spi, &m);
+	if (ret >= 0)
+		ret = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static int ad5755_update_dac_ctrl(struct iio_dev *indio_dev,
+	unsigned int channel, unsigned int set, unsigned int clr)
+{
+	struct ad5755_state *st = iio_priv(indio_dev);
+	int ret;
+
+	st->ctrl[channel] |= set;
+	st->ctrl[channel] &= ~clr;
+
+	ret = ad5755_write_ctrl_unlocked(indio_dev, channel,
+		AD5755_CTRL_REG_DAC, st->ctrl[channel]);
+
+	return ret;
+}
+
+static int ad5755_set_channel_pwr_down(struct iio_dev *indio_dev,
+	unsigned int channel, bool pwr_down)
+{
+	struct ad5755_state *st = iio_priv(indio_dev);
+	unsigned int mask = BIT(channel);
+
+	mutex_lock(&indio_dev->mlock);
+
+	if ((bool)(st->pwr_down & mask) == pwr_down)
+		goto out_unlock;
+
+	if (!pwr_down) {
+		st->pwr_down &= ~mask;
+		ad5755_update_dac_ctrl(indio_dev, channel,
+			AD5755_DAC_INT_EN | AD5755_DAC_DC_DC_EN, 0);
+		udelay(200);
+		ad5755_update_dac_ctrl(indio_dev, channel,
+			AD5755_DAC_OUT_EN, 0);
+	} else {
+		st->pwr_down |= mask;
+		ad5755_update_dac_ctrl(indio_dev, channel,
+			0, AD5755_DAC_INT_EN | AD5755_DAC_OUT_EN |
+				AD5755_DAC_DC_DC_EN);
+	}
+
+out_unlock:
+	mutex_unlock(&indio_dev->mlock);
+
+	return 0;
+}
+
+static const int ad5755_min_max_table[][2] = {
+	[AD5755_MODE_VOLTAGE_0V_5V] = { 0, 5000 },
+	[AD5755_MODE_VOLTAGE_0V_10V] = { 0, 10000 },
+	[AD5755_MODE_VOLTAGE_PLUSMINUS_5V] = { -5000, 5000 },
+	[AD5755_MODE_VOLTAGE_PLUSMINUS_10V] = { -10000, 10000 },
+	[AD5755_MODE_CURRENT_4mA_20mA] = { 4, 20 },
+	[AD5755_MODE_CURRENT_0mA_20mA] = { 0, 20 },
+	[AD5755_MODE_CURRENT_0mA_24mA] = { 0, 24 },
+};
+
+static void ad5755_get_min_max(struct ad5755_state *st,
+	struct iio_chan_spec const *chan, int *min, int *max)
+{
+	enum ad5755_mode mode = st->ctrl[chan->channel] & 7;
+	*min = ad5755_min_max_table[mode][0];
+	*max = ad5755_min_max_table[mode][1];
+}
+
+static inline int ad5755_get_offset(struct ad5755_state *st,
+	struct iio_chan_spec const *chan)
+{
+	int min, max;
+
+	ad5755_get_min_max(st, chan, &min, &max);
+	return (min * (1 << chan->scan_type.realbits)) / (max - min);
+}
+
+static inline int ad5755_get_scale(struct ad5755_state *st,
+	struct iio_chan_spec const *chan)
+{
+	int min, max;
+
+	ad5755_get_min_max(st, chan, &min, &max);
+	return ((max - min) * 1000000000ULL) >> chan->scan_type.realbits;
+}
+
+static int ad5755_chan_reg_info(struct ad5755_state *st,
+	struct iio_chan_spec const *chan, long info, bool write,
+	unsigned int *reg, unsigned int *shift, unsigned int *offset)
+{
+	switch (info) {
+	case IIO_CHAN_INFO_RAW:
+		if (write)
+			*reg = AD5755_WRITE_REG_DATA(chan->address);
+		else
+			*reg = AD5755_READ_REG_DATA(chan->address);
+		*shift = chan->scan_type.shift;
+		*offset = 0;
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		if (write)
+			*reg = AD5755_WRITE_REG_OFFSET(chan->address);
+		else
+			*reg = AD5755_READ_REG_OFFSET(chan->address);
+		*shift = st->chip_info->calib_shift;
+		*offset = 32768;
+		break;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		if (write)
+			*reg =  AD5755_WRITE_REG_GAIN(chan->address);
+		else
+			*reg =  AD5755_READ_REG_GAIN(chan->address);
+		*shift = st->chip_info->calib_shift;
+		*offset = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ad5755_read_raw(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+	struct ad5755_state *st = iio_priv(indio_dev);
+	unsigned int reg, shift, offset;
+	int ret;
+
+	switch (info) {
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = ad5755_get_scale(st, chan);
+		return IIO_VAL_INT_PLUS_NANO;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = ad5755_get_offset(st, chan);
+		return IIO_VAL_INT;
+	default:
+		ret = ad5755_chan_reg_info(st, chan, info, false,
+						&reg, &shift, &offset);
+		if (ret)
+			return ret;
+
+		ret = ad5755_read(indio_dev, reg);
+		if (ret < 0)
+			return ret;
+
+		*val = (ret - offset) >> shift;
+
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static int ad5755_write_raw(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int val, int val2, long info)
+{
+	struct ad5755_state *st = iio_priv(indio_dev);
+	unsigned int shift, reg, offset;
+	int ret;
+
+	ret = ad5755_chan_reg_info(st, chan, info, true,
+					&reg, &shift, &offset);
+	if (ret)
+		return ret;
+
+	val <<= shift;
+	val += offset;
+
+	if (val < 0 || val > 0xffff)
+		return -EINVAL;
+
+	return ad5755_write(indio_dev, reg, val);
+}
+
+static ssize_t ad5755_read_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
+	const struct iio_chan_spec *chan, char *buf)
+{
+	struct ad5755_state *st = iio_priv(indio_dev);
+
+	return sprintf(buf, "%d\n",
+		       (bool)(st->pwr_down & (1 << chan->channel)));
+}
+
+static ssize_t ad5755_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
+	struct iio_chan_spec const *chan, const char *buf, size_t len)
+{
+	bool pwr_down;
+	int ret;
+
+	ret = strtobool(buf, &pwr_down);
+	if (ret)
+		return ret;
+
+	ret = ad5755_set_channel_pwr_down(indio_dev, chan->channel, pwr_down);
+	return ret ? ret : len;
+}
+
+static const struct iio_info ad5755_info = {
+	.read_raw = ad5755_read_raw,
+	.write_raw = ad5755_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec_ext_info ad5755_ext_info[] = {
+	{
+		.name = "powerdown",
+		.read = ad5755_read_powerdown,
+		.write = ad5755_write_powerdown,
+	},
+	{ },
+};
+
+#define AD5755_CHANNEL(_bits) {					\
+	.indexed = 1,						\
+	.output = 1,						\
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |		\
+		IIO_CHAN_INFO_SCALE_SEPARATE_BIT |		\
+		IIO_CHAN_INFO_OFFSET_SEPARATE_BIT |		\
+		IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |		\
+		IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,		\
+	.scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)),	\
+	.ext_info = ad5755_ext_info,				\
+}
+
+static const struct ad5755_chip_info ad5755_chip_info_tbl[] = {
+	[ID_AD5735] = {
+		.channel_template = AD5755_CHANNEL(14),
+		.has_voltage_out = true,
+		.calib_shift = 4,
+	},
+	[ID_AD5737] = {
+		.channel_template = AD5755_CHANNEL(14),
+		.has_voltage_out = false,
+		.calib_shift = 4,
+	},
+	[ID_AD5755] = {
+		.channel_template = AD5755_CHANNEL(16),
+		.has_voltage_out = true,
+		.calib_shift = 0,
+	},
+	[ID_AD5757] = {
+		.channel_template = AD5755_CHANNEL(16),
+		.has_voltage_out = false,
+		.calib_shift = 0,
+	},
+};
+
+static bool ad5755_is_valid_mode(struct ad5755_state *st, enum ad5755_mode mode)
+{
+	switch (mode) {
+	case AD5755_MODE_VOLTAGE_0V_5V:
+	case AD5755_MODE_VOLTAGE_0V_10V:
+	case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
+	case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
+		return st->chip_info->has_voltage_out;
+	case AD5755_MODE_CURRENT_4mA_20mA:
+	case AD5755_MODE_CURRENT_0mA_20mA:
+	case AD5755_MODE_CURRENT_0mA_24mA:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int __devinit ad5755_setup_pdata(struct iio_dev *indio_dev,
+	const struct ad5755_platform_data *pdata)
+{
+	struct ad5755_state *st = iio_priv(indio_dev);
+	unsigned int val;
+	unsigned int i;
+	int ret;
+
+	if (pdata->dc_dc_phase > AD5755_DC_DC_PHASE_90_DEGREE ||
+		pdata->dc_dc_freq > AD5755_DC_DC_FREQ_650kHZ ||
+		pdata->dc_dc_maxv > AD5755_DC_DC_MAXV_29V5)
+		return -EINVAL;
+
+	val = pdata->dc_dc_maxv << AD5755_DC_DC_MAXV;
+	val |= pdata->dc_dc_freq << AD5755_DC_DC_FREQ_SHIFT;
+	val |= pdata->dc_dc_phase << AD5755_DC_DC_PHASE_SHIFT;
+	if (pdata->ext_dc_dc_compenstation_resistor)
+		val |= AD5755_EXT_DC_DC_COMP_RES;
+
+	ret = ad5755_write_ctrl(indio_dev, 0, AD5755_CTRL_REG_DC_DC, val);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) {
+		val = pdata->dac[i].slew.step_size <<
+			AD5755_SLEW_STEP_SIZE_SHIFT;
+		val |= pdata->dac[i].slew.rate <<
+			AD5755_SLEW_RATE_SHIFT;
+		if (pdata->dac[i].slew.enable)
+			val |= AD5755_SLEW_ENABLE;
+
+		ret = ad5755_write_ctrl(indio_dev, i,
+					AD5755_CTRL_REG_SLEW, val);
+		if (ret < 0)
+			return ret;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) {
+		if (!ad5755_is_valid_mode(st, pdata->dac[i].mode))
+			return -EINVAL;
+
+		val = 0;
+		if (!pdata->dac[i].ext_current_sense_resistor)
+			val |= AD5755_DAC_INT_CURRENT_SENSE_RESISTOR;
+		if (pdata->dac[i].enable_voltage_overrange)
+			val |= AD5755_DAC_VOLTAGE_OVERRANGE_EN;
+		val |= pdata->dac[i].mode;
+
+		ret = ad5755_update_dac_ctrl(indio_dev, i, val, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static bool __devinit ad5755_is_voltage_mode(enum ad5755_mode mode)
+{
+	switch (mode) {
+	case AD5755_MODE_VOLTAGE_0V_5V:
+	case AD5755_MODE_VOLTAGE_0V_10V:
+	case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
+	case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int __devinit ad5755_init_channels(struct iio_dev *indio_dev,
+	const struct ad5755_platform_data *pdata)
+{
+	struct ad5755_state *st = iio_priv(indio_dev);
+	struct iio_chan_spec *channels = st->channels;
+	unsigned int i;
+
+	for (i = 0; i < AD5755_NUM_CHANNELS; ++i) {
+		channels[i] = st->chip_info->channel_template;
+		channels[i].channel = i;
+		channels[i].address = i;
+		if (pdata && ad5755_is_voltage_mode(pdata->dac[i].mode))
+			channels[i].type = IIO_VOLTAGE;
+		else
+			channels[i].type = IIO_CURRENT;
+	}
+
+	indio_dev->channels = channels;
+
+	return 0;
+}
+
+#define AD5755_DEFAULT_DAC_PDATA { \
+		.mode = AD5755_MODE_CURRENT_4mA_20mA, \
+		.ext_current_sense_resistor = true, \
+		.enable_voltage_overrange = false, \
+		.slew = { \
+			.enable = false, \
+			.rate = AD5755_SLEW_RATE_64k, \
+			.step_size = AD5755_SLEW_STEP_SIZE_1, \
+		}, \
+	}
+
+static const struct ad5755_platform_data ad5755_default_pdata = {
+	.ext_dc_dc_compenstation_resistor = false,
+	.dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE,
+	.dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ,
+	.dc_dc_maxv = AD5755_DC_DC_MAXV_23V,
+	.dac = {
+		[0] = AD5755_DEFAULT_DAC_PDATA,
+		[1] = AD5755_DEFAULT_DAC_PDATA,
+		[2] = AD5755_DEFAULT_DAC_PDATA,
+		[3] = AD5755_DEFAULT_DAC_PDATA,
+	},
+};
+
+static int __devinit ad5755_probe(struct spi_device *spi)
+{
+	enum ad5755_type type = spi_get_device_id(spi)->driver_data;
+	const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev);
+	struct iio_dev *indio_dev;
+	struct ad5755_state *st;
+	int ret;
+
+	indio_dev = iio_device_alloc(sizeof(*st));
+	if (indio_dev == NULL) {
+		dev_err(&spi->dev, "Failed to allocate iio device\n");
+		return  -ENOMEM;
+	}
+
+	st = iio_priv(indio_dev);
+	spi_set_drvdata(spi, indio_dev);
+
+	st->chip_info = &ad5755_chip_info_tbl[type];
+	st->spi = spi;
+	st->pwr_down = 0xf;
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->info = &ad5755_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->num_channels = AD5755_NUM_CHANNELS;
+
+	if (!pdata)
+		pdata = &ad5755_default_pdata;
+
+	ret = ad5755_init_channels(indio_dev, pdata);
+	if (ret)
+		goto error_free;
+
+	ret = ad5755_setup_pdata(indio_dev, pdata);
+	if (ret)
+		goto error_free;
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
+		goto error_free;
+	}
+
+	return 0;
+
+error_free:
+	iio_device_free(indio_dev);
+
+	return ret;
+}
+
+static int __devexit ad5755_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+	iio_device_unregister(indio_dev);
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id ad5755_id[] = {
+	{ "ad5755", ID_AD5755 },
+	{ "ad5755-1", ID_AD5755 },
+	{ "ad5757", ID_AD5757 },
+	{ "ad5735", ID_AD5735 },
+	{ "ad5737", ID_AD5737 },
+	{}
+};
+MODULE_DEVICE_TABLE(spi, ad5755_id);
+
+static struct spi_driver ad5755_driver = {
+	.driver = {
+		.name = "ad5755",
+		.owner = THIS_MODULE,
+	},
+	.probe = ad5755_probe,
+	.remove = __devexit_p(ad5755_remove),
+	.id_table = ad5755_id,
+};
+module_spi_driver(ad5755_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5755/55-1/57/35/37 DAC");
+MODULE_LICENSE("GPL v2");

+ 16 - 0
drivers/iio/gyro/Kconfig

@@ -0,0 +1,16 @@
+#
+# IIO Digital Gyroscope Sensor drivers configuration
+#
+menu "Digital gyroscope sensors"
+
+config HID_SENSOR_GYRO_3D
+	depends on HID_SENSOR_HUB
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	select HID_SENSOR_IIO_COMMON
+	tristate "HID Gyroscope 3D"
+	help
+	  Say yes here to build support for the HID SENSOR
+	  Gyroscope 3D.
+
+endmenu

+ 5 - 0
drivers/iio/gyro/Makefile

@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O gyroscope sensor drivers
+#
+
+obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o

+ 418 - 0
drivers/iio/gyro/hid-sensor-gyro-3d.c

@@ -0,0 +1,418 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-attributes.h"
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Gyro-3D: 0x200076*/
+#define DRIVER_NAME "HID-SENSOR-200076"
+
+enum gyro_3d_channel {
+	CHANNEL_SCAN_INDEX_X,
+	CHANNEL_SCAN_INDEX_Y,
+	CHANNEL_SCAN_INDEX_Z,
+	GYRO_3D_CHANNEL_MAX,
+};
+
+struct gyro_3d_state {
+	struct hid_sensor_hub_callbacks callbacks;
+	struct hid_sensor_iio_common common_attributes;
+	struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX];
+	u32 gyro_val[GYRO_3D_CHANNEL_MAX];
+};
+
+static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = {
+	HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS,
+	HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS,
+	HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec gyro_3d_channels[] = {
+	{
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT |
+		IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+		IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+		.scan_index = CHANNEL_SCAN_INDEX_X,
+	}, {
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT |
+		IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+		IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+		.scan_index = CHANNEL_SCAN_INDEX_Y,
+	}, {
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT |
+		IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+		IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+		.scan_index = CHANNEL_SCAN_INDEX_Z,
+	}
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void gyro_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+						int channel, int size)
+{
+	channels[channel].scan_type.sign = 's';
+	/* Real storage bits will change based on the report desc. */
+	channels[channel].scan_type.realbits = size * 8;
+	/* Maximum size of a sample to capture is u32 */
+	channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int gyro_3d_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val, int *val2,
+			      long mask)
+{
+	struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+	int report_id = -1;
+	u32 address;
+	int ret;
+	int ret_type;
+
+	*val = 0;
+	*val2 = 0;
+	switch (mask) {
+	case 0:
+		report_id = gyro_state->gyro[chan->scan_index].report_id;
+		address = gyro_3d_addresses[chan->scan_index];
+		if (report_id >= 0)
+			*val = sensor_hub_input_attr_get_raw_value(
+				gyro_state->common_attributes.hsdev,
+				HID_USAGE_SENSOR_GYRO_3D, address,
+				report_id);
+		else {
+			*val = 0;
+			return -EINVAL;
+		}
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		*val = gyro_state->gyro[CHANNEL_SCAN_INDEX_X].units;
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = hid_sensor_convert_exponent(
+			gyro_state->gyro[CHANNEL_SCAN_INDEX_X].unit_expo);
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = hid_sensor_read_samp_freq_value(
+			&gyro_state->common_attributes, val, val2);
+			ret_type = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		ret = hid_sensor_read_raw_hyst_value(
+			&gyro_state->common_attributes, val, val2);
+		ret_type = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	default:
+		ret_type = -EINVAL;
+		break;
+	}
+
+	return ret_type;
+}
+
+/* Channel write_raw handler */
+static int gyro_3d_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask)
+{
+	struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+	int ret = 0;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = hid_sensor_write_samp_freq_value(
+				&gyro_state->common_attributes, val, val2);
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		ret = hid_sensor_write_raw_hyst_value(
+				&gyro_state->common_attributes, val, val2);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int gyro_3d_write_raw_get_fmt(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       long mask)
+{
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static const struct iio_info gyro_3d_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &gyro_3d_read_raw,
+	.write_raw = &gyro_3d_write_raw,
+	.write_raw_get_fmt = &gyro_3d_write_raw_get_fmt,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
+{
+	struct iio_buffer *buffer = indio_dev->buffer;
+	int datum_sz;
+
+	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+	if (!buffer) {
+		dev_err(&indio_dev->dev, "Buffer == NULL\n");
+		return;
+	}
+	datum_sz = buffer->access->get_bytes_per_datum(buffer);
+	if (len > datum_sz) {
+		dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+				datum_sz);
+		return;
+	}
+	iio_push_to_buffer(buffer, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
+				unsigned usage_id,
+				void *priv)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(priv);
+	struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "gyro_3d_proc_event [%d]\n",
+				gyro_state->common_attributes.data_ready);
+	if (gyro_state->common_attributes.data_ready)
+		hid_sensor_push_data(indio_dev,
+				(u8 *)gyro_state->gyro_val,
+				sizeof(gyro_state->gyro_val));
+
+	return 0;
+}
+
+/* Capture samples in local storage */
+static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
+				unsigned usage_id,
+				size_t raw_len, char *raw_data,
+				void *priv)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(priv);
+	struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+	int offset;
+	int ret = -EINVAL;
+
+	switch (usage_id) {
+	case HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS:
+	case HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS:
+	case HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS:
+		offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS;
+		gyro_state->gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
+						*(u32 *)raw_data;
+		ret = 0;
+	break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int gyro_3d_parse_report(struct platform_device *pdev,
+				struct hid_sensor_hub_device *hsdev,
+				struct iio_chan_spec *channels,
+				unsigned usage_id,
+				struct gyro_3d_state *st)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
+		ret = sensor_hub_input_get_attribute_info(hsdev,
+				HID_INPUT_REPORT,
+				usage_id,
+				HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS + i,
+				&st->gyro[CHANNEL_SCAN_INDEX_X + i]);
+		if (ret < 0)
+			break;
+		gyro_3d_adjust_channel_bit_mask(channels,
+				CHANNEL_SCAN_INDEX_X + i,
+				st->gyro[CHANNEL_SCAN_INDEX_X + i].size);
+	}
+	dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n",
+			st->gyro[0].index,
+			st->gyro[0].report_id,
+			st->gyro[1].index, st->gyro[1].report_id,
+			st->gyro[2].index, st->gyro[2].report_id);
+
+	return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int __devinit hid_gyro_3d_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	static const char *name = "gyro_3d";
+	struct iio_dev *indio_dev;
+	struct gyro_3d_state *gyro_state;
+	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+	struct iio_chan_spec *channels;
+
+	indio_dev = iio_device_alloc(sizeof(struct gyro_3d_state));
+	if (indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, indio_dev);
+
+	gyro_state = iio_priv(indio_dev);
+	gyro_state->common_attributes.hsdev = hsdev;
+	gyro_state->common_attributes.pdev = pdev;
+
+	ret = hid_sensor_parse_common_attributes(hsdev,
+						HID_USAGE_SENSOR_GYRO_3D,
+						&gyro_state->common_attributes);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to setup common attributes\n");
+		goto error_free_dev;
+	}
+
+	channels = kmemdup(gyro_3d_channels,
+					sizeof(gyro_3d_channels),
+					GFP_KERNEL);
+	if (!channels) {
+		dev_err(&pdev->dev, "failed to duplicate channels\n");
+		goto error_free_dev;
+	}
+
+	ret = gyro_3d_parse_report(pdev, hsdev, channels,
+					HID_USAGE_SENSOR_GYRO_3D, gyro_state);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to setup attributes\n");
+		goto error_free_dev_mem;
+	}
+
+	indio_dev->channels = channels;
+	indio_dev->num_channels = ARRAY_SIZE(gyro_3d_channels);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->info = &gyro_3d_info;
+	indio_dev->name = name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+		NULL, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+		goto error_free_dev_mem;
+	}
+	gyro_state->common_attributes.data_ready = false;
+	ret = hid_sensor_setup_trigger(indio_dev, name,
+					&gyro_state->common_attributes);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "trigger setup failed\n");
+		goto error_unreg_buffer_funcs;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_trigger;
+	}
+
+	gyro_state->callbacks.send_event = gyro_3d_proc_event;
+	gyro_state->callbacks.capture_sample = gyro_3d_capture_sample;
+	gyro_state->callbacks.pdev = pdev;
+	ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D,
+					&gyro_state->callbacks);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "callback reg failed\n");
+		goto error_iio_unreg;
+	}
+
+	return ret;
+
+error_iio_unreg:
+	iio_device_unregister(indio_dev);
+error_remove_trigger:
+	hid_sensor_remove_trigger(indio_dev);
+error_unreg_buffer_funcs:
+	iio_triggered_buffer_cleanup(indio_dev);
+error_free_dev_mem:
+	kfree(indio_dev->channels);
+error_free_dev:
+	iio_device_free(indio_dev);
+error_ret:
+	return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int __devinit hid_gyro_3d_remove(struct platform_device *pdev)
+{
+	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
+	iio_device_unregister(indio_dev);
+	hid_sensor_remove_trigger(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	kfree(indio_dev->channels);
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static struct platform_driver hid_gyro_3d_platform_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= hid_gyro_3d_probe,
+	.remove		= hid_gyro_3d_remove,
+};
+module_platform_driver(hid_gyro_3d_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Gyroscope 3D");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");

+ 7 - 8
drivers/iio/industrialio-buffer.c

@@ -422,7 +422,7 @@ ssize_t iio_buffer_store_enable(struct device *dev,
 			ret = indio_dev->setup_ops->preenable(indio_dev);
 			if (ret) {
 				printk(KERN_ERR
-				       "Buffer not started:"
+				       "Buffer not started: "
 				       "buffer preenable failed\n");
 				goto error_ret;
 			}
@@ -431,12 +431,12 @@ ssize_t iio_buffer_store_enable(struct device *dev,
 			ret = buffer->access->request_update(buffer);
 			if (ret) {
 				printk(KERN_INFO
-				       "Buffer not started:"
+				       "Buffer not started: "
 				       "buffer parameter update failed\n");
 				goto error_ret;
 			}
 		}
-		/* Definitely possible for devices to support both of these.*/
+		/* Definitely possible for devices to support both of these. */
 		if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) {
 			if (!indio_dev->trig) {
 				printk(KERN_INFO
@@ -456,7 +456,7 @@ ssize_t iio_buffer_store_enable(struct device *dev,
 			ret = indio_dev->setup_ops->postenable(indio_dev);
 			if (ret) {
 				printk(KERN_INFO
-				       "Buffer not started:"
+				       "Buffer not started: "
 				       "postenable failed\n");
 				indio_dev->currentmode = previous_mode;
 				if (indio_dev->setup_ops->postdisable)
@@ -657,7 +657,7 @@ EXPORT_SYMBOL_GPL(iio_scan_mask_query);
 /**
  * struct iio_demux_table() - table describing demux memcpy ops
  * @from:	index to copy from
- * @to:	index to copy to
+ * @to:		index to copy to
  * @length:	how many bytes to copy
  * @l:		list head used for management
  */
@@ -682,12 +682,11 @@ static unsigned char *iio_demux(struct iio_buffer *buffer,
 	return buffer->demux_bounce;
 }
 
-int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data,
-		       s64 timestamp)
+int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data)
 {
 	unsigned char *dataout = iio_demux(buffer, data);
 
-	return buffer->access->store_to(buffer, dataout, timestamp);
+	return buffer->access->store_to(buffer, dataout);
 }
 EXPORT_SYMBOL_GPL(iio_push_to_buffer);
 

+ 10 - 3
drivers/iio/industrialio-core.c

@@ -29,7 +29,7 @@
 #include <linux/iio/sysfs.h>
 #include <linux/iio/events.h>
 
-/* IDA to assign each registered device a unique id*/
+/* IDA to assign each registered device a unique id */
 static DEFINE_IDA(iio_ida);
 
 static dev_t iio_devt;
@@ -99,6 +99,7 @@ static const char * const iio_chan_info_postfix[] = {
 	[IIO_CHAN_INFO_FREQUENCY] = "frequency",
 	[IIO_CHAN_INFO_PHASE] = "phase",
 	[IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain",
+	[IIO_CHAN_INFO_HYSTERESIS] = "hysteresis",
 };
 
 const struct iio_chan_spec
@@ -365,6 +366,7 @@ static ssize_t iio_read_channel_info(struct device *dev,
 {
 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	unsigned long long tmp;
 	int val, val2;
 	bool scale_db = false;
 	int ret = indio_dev->info->read_raw(indio_dev, this_attr->c,
@@ -390,6 +392,11 @@ static ssize_t iio_read_channel_info(struct device *dev,
 			return sprintf(buf, "-%d.%09u\n", val, -val2);
 		else
 			return sprintf(buf, "%d.%09u\n", val, val2);
+	case IIO_VAL_FRACTIONAL:
+		tmp = div_s64((s64)val * 1000000000LL, val2);
+		val2 = do_div(tmp, 1000000000LL);
+		val = tmp;
+		return sprintf(buf, "%d.%09u\n", val, val2);
 	default:
 		return 0;
 	}
@@ -729,7 +736,7 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
 	attrcount = attrcount_orig;
 	/*
 	 * New channel registration method - relies on the fact a group does
-	 * not need to be initialized if it is name is NULL.
+	 * not need to be initialized if its name is NULL.
 	 */
 	if (indio_dev->channels)
 		for (i = 0; i < indio_dev->num_channels; i++) {
@@ -980,6 +987,6 @@ EXPORT_SYMBOL(iio_device_unregister);
 subsys_initcall(iio_init);
 module_exit(iio_exit);
 
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
 MODULE_DESCRIPTION("Industrial I/O core");
 MODULE_LICENSE("GPL");

+ 128 - 11
drivers/iio/inkern.c

@@ -111,6 +111,7 @@ struct iio_channel *iio_channel_get(const char *name, const char *channel_name)
 {
 	struct iio_map_internal *c_i = NULL, *c = NULL;
 	struct iio_channel *channel;
+	int err;
 
 	if (name == NULL && channel_name == NULL)
 		return ERR_PTR(-ENODEV);
@@ -130,18 +131,32 @@ struct iio_channel *iio_channel_get(const char *name, const char *channel_name)
 	if (c == NULL)
 		return ERR_PTR(-ENODEV);
 
-	channel = kmalloc(sizeof(*channel), GFP_KERNEL);
-	if (channel == NULL)
-		return ERR_PTR(-ENOMEM);
+	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+	if (channel == NULL) {
+		err = -ENOMEM;
+		goto error_no_mem;
+	}
 
 	channel->indio_dev = c->indio_dev;
 
-	if (c->map->adc_channel_label)
+	if (c->map->adc_channel_label) {
 		channel->channel =
 			iio_chan_spec_from_name(channel->indio_dev,
 						c->map->adc_channel_label);
 
+		if (channel->channel == NULL) {
+			err = -EINVAL;
+			goto error_no_chan;
+		}
+	}
+
 	return channel;
+
+error_no_chan:
+	kfree(channel);
+error_no_mem:
+	iio_device_put(c->indio_dev);
+	return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(iio_channel_get);
 
@@ -229,9 +244,21 @@ void iio_channel_release_all(struct iio_channel *channels)
 }
 EXPORT_SYMBOL_GPL(iio_channel_release_all);
 
+static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
+	enum iio_chan_info_enum info)
+{
+	int unused;
+
+	if (val2 == NULL)
+		val2 = &unused;
+
+	return chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel,
+						val, val2, info);
+}
+
 int iio_read_channel_raw(struct iio_channel *chan, int *val)
 {
-	int val2, ret;
+	int ret;
 
 	mutex_lock(&chan->indio_dev->info_exist_lock);
 	if (chan->indio_dev->info == NULL) {
@@ -239,8 +266,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val)
 		goto err_unlock;
 	}
 
-	ret = chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel,
-					      val, &val2, 0);
+	ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
 err_unlock:
 	mutex_unlock(&chan->indio_dev->info_exist_lock);
 
@@ -248,6 +274,100 @@ err_unlock:
 }
 EXPORT_SYMBOL_GPL(iio_read_channel_raw);
 
+static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
+	int raw, int *processed, unsigned int scale)
+{
+	int scale_type, scale_val, scale_val2, offset;
+	s64 raw64 = raw;
+	int ret;
+
+	ret = iio_channel_read(chan, &offset, NULL, IIO_CHAN_INFO_SCALE);
+	if (ret == 0)
+		raw64 += offset;
+
+	scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
+					IIO_CHAN_INFO_SCALE);
+	if (scale_type < 0)
+		return scale_type;
+
+	switch (scale_type) {
+	case IIO_VAL_INT:
+		*processed = raw64 * scale_val;
+		break;
+	case IIO_VAL_INT_PLUS_MICRO:
+		if (scale_val2 < 0)
+			*processed = -raw64 * scale_val;
+		else
+			*processed = raw64 * scale_val;
+		*processed += div_s64(raw64 * (s64)scale_val2 * scale,
+				      1000000LL);
+		break;
+	case IIO_VAL_INT_PLUS_NANO:
+		if (scale_val2 < 0)
+			*processed = -raw64 * scale_val;
+		else
+			*processed = raw64 * scale_val;
+		*processed += div_s64(raw64 * (s64)scale_val2 * scale,
+				      1000000000LL);
+		break;
+	case IIO_VAL_FRACTIONAL:
+		*processed = div_s64(raw64 * (s64)scale_val * scale,
+				     scale_val2);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
+	int *processed, unsigned int scale)
+{
+	int ret;
+
+	mutex_lock(&chan->indio_dev->info_exist_lock);
+	if (chan->indio_dev->info == NULL) {
+		ret = -ENODEV;
+		goto err_unlock;
+	}
+
+	ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed,
+							scale);
+err_unlock:
+	mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
+
+int iio_read_channel_processed(struct iio_channel *chan, int *val)
+{
+	int ret;
+
+	mutex_lock(&chan->indio_dev->info_exist_lock);
+	if (chan->indio_dev->info == NULL) {
+		ret = -ENODEV;
+		goto err_unlock;
+	}
+
+	if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) {
+		ret = iio_channel_read(chan, val, NULL,
+				       IIO_CHAN_INFO_PROCESSED);
+	} else {
+		ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
+		if (ret < 0)
+			goto err_unlock;
+		ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, 1);
+	}
+
+err_unlock:
+	mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_processed);
+
 int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
 {
 	int ret;
@@ -258,10 +378,7 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
 		goto err_unlock;
 	}
 
-	ret = chan->indio_dev->info->read_raw(chan->indio_dev,
-					      chan->channel,
-					      val, val2,
-					      IIO_CHAN_INFO_SCALE);
+	ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE);
 err_unlock:
 	mutex_unlock(&chan->indio_dev->info_exist_lock);
 

+ 23 - 8
drivers/iio/kfifo_buf.c

@@ -6,6 +6,7 @@
 #include <linux/kfifo.h>
 #include <linux/mutex.h>
 #include <linux/iio/kfifo_buf.h>
+#include <linux/sched.h>
 
 struct iio_kfifo {
 	struct iio_buffer buffer;
@@ -22,7 +23,8 @@ static inline int __iio_allocate_kfifo(struct iio_kfifo *buf,
 		return -EINVAL;
 
 	__iio_update_buffer(&buf->buffer, bytes_per_datum, length);
-	return kfifo_alloc(&buf->kf, bytes_per_datum*length, GFP_KERNEL);
+	return __kfifo_alloc((struct __kfifo *)&buf->kf, length,
+			     bytes_per_datum, GFP_KERNEL);
 }
 
 static int iio_request_update_kfifo(struct iio_buffer *r)
@@ -35,6 +37,7 @@ static int iio_request_update_kfifo(struct iio_buffer *r)
 	kfifo_free(&buf->kf);
 	ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,
 				   buf->buffer.length);
+	r->stufftoread = false;
 error_ret:
 	return ret;
 }
@@ -81,6 +84,9 @@ static int iio_set_bytes_per_datum_kfifo(struct iio_buffer *r, size_t bpd)
 
 static int iio_set_length_kfifo(struct iio_buffer *r, int length)
 {
+	/* Avoid an invalid state */
+	if (length < 2)
+		length = 2;
 	if (r->length != length) {
 		r->length = length;
 		iio_mark_update_needed_kfifo(r);
@@ -89,14 +95,16 @@ static int iio_set_length_kfifo(struct iio_buffer *r, int length)
 }
 
 static int iio_store_to_kfifo(struct iio_buffer *r,
-			      u8 *data,
-			      s64 timestamp)
+			      u8 *data)
 {
 	int ret;
 	struct iio_kfifo *kf = iio_to_kfifo(r);
-	ret = kfifo_in(&kf->kf, data, r->bytes_per_datum);
-	if (ret != r->bytes_per_datum)
+	ret = kfifo_in(&kf->kf, data, 1);
+	if (ret != 1)
 		return -EBUSY;
+	r->stufftoread = true;
+	wake_up_interruptible(&r->pollq);
+
 	return 0;
 }
 
@@ -106,11 +114,18 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r,
 	int ret, copied;
 	struct iio_kfifo *kf = iio_to_kfifo(r);
 
-	if (n < r->bytes_per_datum)
+	if (n < r->bytes_per_datum || r->bytes_per_datum == 0)
 		return -EINVAL;
 
-	n = rounddown(n, r->bytes_per_datum);
 	ret = kfifo_to_user(&kf->kf, buf, n, &copied);
+	if (ret < 0)
+		return ret;
+
+	if (kfifo_is_empty(&kf->kf))
+		r->stufftoread = false;
+	/* verify it is still empty to avoid race */
+	if (!kfifo_is_empty(&kf->kf))
+		r->stufftoread = true;
 
 	return copied;
 }
@@ -136,7 +151,7 @@ struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev)
 	iio_buffer_init(&kf->buffer);
 	kf->buffer.attrs = &iio_kfifo_attribute_group;
 	kf->buffer.access = &kfifo_access_funcs;
-
+	kf->buffer.length = 2;
 	return &kf->buffer;
 }
 EXPORT_SYMBOL(iio_kfifo_allocate);

+ 10 - 0
drivers/iio/light/Kconfig

@@ -42,4 +42,14 @@ config VCNL4000
 	 To compile this driver as a module, choose M here: the
 	 module will be called vcnl4000.
 
+config HID_SENSOR_ALS
+	depends on HID_SENSOR_HUB
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	select HID_SENSOR_IIO_COMMON
+	tristate "HID ALS"
+	help
+	  Say yes here to build support for the HID SENSOR
+	  Ambient light sensor.
+
 endmenu

+ 1 - 0
drivers/iio/light/Makefile

@@ -5,3 +5,4 @@
 obj-$(CONFIG_ADJD_S311)		+= adjd_s311.o
 obj-$(CONFIG_SENSORS_LM3533)	+= lm3533-als.o
 obj-$(CONFIG_VCNL4000)		+= vcnl4000.o
+obj-$(CONFIG_HID_SENSOR_ALS)	+= hid-sensor-als.o

+ 1 - 1
drivers/iio/light/adjd_s311.c

@@ -187,7 +187,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)
 	if (indio_dev->scan_timestamp)
 		*(s64 *)((u8 *)data->buffer + ALIGN(len, sizeof(s64)))
 			= time_ns;
-	iio_push_to_buffer(buffer, (u8 *)data->buffer, time_ns);
+	iio_push_to_buffer(buffer, (u8 *)data->buffer);
 
 done:
 	iio_trigger_notify_done(indio_dev->trig);

+ 385 - 0
drivers/iio/light/hid-sensor-als.c

@@ -0,0 +1,385 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-attributes.h"
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Accelerometer-3D: 0x200041*/
+#define DRIVER_NAME "HID-SENSOR-200041"
+
+#define CHANNEL_SCAN_INDEX_ILLUM 0
+
+struct als_state {
+	struct hid_sensor_hub_callbacks callbacks;
+	struct hid_sensor_iio_common common_attributes;
+	struct hid_sensor_hub_attribute_info als_illum;
+	u32 illum;
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec als_channels[] = {
+	{
+		.type = IIO_INTENSITY,
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_BOTH,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT |
+		IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+		IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+		.scan_index = CHANNEL_SCAN_INDEX_ILLUM,
+	}
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void als_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+					int channel, int size)
+{
+	channels[channel].scan_type.sign = 's';
+	/* Real storage bits will change based on the report desc. */
+	channels[channel].scan_type.realbits = size * 8;
+	/* Maximum size of a sample to capture is u32 */
+	channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int als_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val, int *val2,
+			      long mask)
+{
+	struct als_state *als_state = iio_priv(indio_dev);
+	int report_id = -1;
+	u32 address;
+	int ret;
+	int ret_type;
+
+	*val = 0;
+	*val2 = 0;
+	switch (mask) {
+	case 0:
+		switch (chan->scan_index) {
+		case  CHANNEL_SCAN_INDEX_ILLUM:
+			report_id = als_state->als_illum.report_id;
+			address =
+			HID_USAGE_SENSOR_LIGHT_ILLUM;
+			break;
+		default:
+			report_id = -1;
+			break;
+		}
+		if (report_id >= 0)
+			*val = sensor_hub_input_attr_get_raw_value(
+				als_state->common_attributes.hsdev,
+				HID_USAGE_SENSOR_ALS, address,
+				report_id);
+		else {
+			*val = 0;
+			return -EINVAL;
+		}
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		*val = als_state->als_illum.units;
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = hid_sensor_convert_exponent(
+				als_state->als_illum.unit_expo);
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = hid_sensor_read_samp_freq_value(
+				&als_state->common_attributes, val, val2);
+		ret_type = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		ret = hid_sensor_read_raw_hyst_value(
+				&als_state->common_attributes, val, val2);
+		ret_type = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	default:
+		ret_type = -EINVAL;
+		break;
+	}
+
+	return ret_type;
+}
+
+/* Channel write_raw handler */
+static int als_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask)
+{
+	struct als_state *als_state = iio_priv(indio_dev);
+	int ret = 0;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = hid_sensor_write_samp_freq_value(
+				&als_state->common_attributes, val, val2);
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		ret = hid_sensor_write_raw_hyst_value(
+				&als_state->common_attributes, val, val2);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int als_write_raw_get_fmt(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       long mask)
+{
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static const struct iio_info als_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &als_read_raw,
+	.write_raw = &als_write_raw,
+	.write_raw_get_fmt = &als_write_raw_get_fmt,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
+{
+	struct iio_buffer *buffer = indio_dev->buffer;
+	int datum_sz;
+
+	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+	if (!buffer) {
+		dev_err(&indio_dev->dev, "Buffer == NULL\n");
+		return;
+	}
+	datum_sz = buffer->access->get_bytes_per_datum(buffer);
+	if (len > datum_sz) {
+		dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+				datum_sz);
+		return;
+	}
+	iio_push_to_buffer(buffer, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int als_proc_event(struct hid_sensor_hub_device *hsdev,
+				unsigned usage_id,
+				void *priv)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(priv);
+	struct als_state *als_state = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "als_proc_event [%d]\n",
+				als_state->common_attributes.data_ready);
+	if (als_state->common_attributes.data_ready)
+		hid_sensor_push_data(indio_dev,
+				(u8 *)&als_state->illum,
+				sizeof(als_state->illum));
+
+	return 0;
+}
+
+/* Capture samples in local storage */
+static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
+				unsigned usage_id,
+				size_t raw_len, char *raw_data,
+				void *priv)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(priv);
+	struct als_state *als_state = iio_priv(indio_dev);
+	int ret = -EINVAL;
+
+	switch (usage_id) {
+	case HID_USAGE_SENSOR_LIGHT_ILLUM:
+		als_state->illum = *(u32 *)raw_data;
+		ret = 0;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int als_parse_report(struct platform_device *pdev,
+				struct hid_sensor_hub_device *hsdev,
+				struct iio_chan_spec *channels,
+				unsigned usage_id,
+				struct als_state *st)
+{
+	int ret;
+
+	ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
+			usage_id,
+			HID_USAGE_SENSOR_LIGHT_ILLUM,
+			&st->als_illum);
+	if (ret < 0)
+		return ret;
+	als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_ILLUM,
+					st->als_illum.size);
+
+	dev_dbg(&pdev->dev, "als %x:%x\n", st->als_illum.index,
+			st->als_illum.report_id);
+
+	return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int __devinit hid_als_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	static const char *name = "als";
+	struct iio_dev *indio_dev;
+	struct als_state *als_state;
+	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+	struct iio_chan_spec *channels;
+
+	indio_dev = iio_device_alloc(sizeof(struct als_state));
+	if (indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, indio_dev);
+
+	als_state = iio_priv(indio_dev);
+	als_state->common_attributes.hsdev = hsdev;
+	als_state->common_attributes.pdev = pdev;
+
+	ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_ALS,
+					&als_state->common_attributes);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to setup common attributes\n");
+		goto error_free_dev;
+	}
+
+	channels = kmemdup(als_channels,
+					sizeof(als_channels),
+					GFP_KERNEL);
+	if (!channels) {
+		dev_err(&pdev->dev, "failed to duplicate channels\n");
+		goto error_free_dev;
+	}
+
+	ret = als_parse_report(pdev, hsdev, channels,
+				HID_USAGE_SENSOR_ALS, als_state);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to setup attributes\n");
+		goto error_free_dev_mem;
+	}
+
+	indio_dev->channels = channels;
+	indio_dev->num_channels =
+				ARRAY_SIZE(als_channels);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->info = &als_info;
+	indio_dev->name = name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+		NULL, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+		goto error_free_dev_mem;
+	}
+	als_state->common_attributes.data_ready = false;
+	ret = hid_sensor_setup_trigger(indio_dev, name,
+				&als_state->common_attributes);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "trigger setup failed\n");
+		goto error_unreg_buffer_funcs;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_trigger;
+	}
+
+	als_state->callbacks.send_event = als_proc_event;
+	als_state->callbacks.capture_sample = als_capture_sample;
+	als_state->callbacks.pdev = pdev;
+	ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ALS,
+					&als_state->callbacks);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "callback reg failed\n");
+		goto error_iio_unreg;
+	}
+
+	return ret;
+
+error_iio_unreg:
+	iio_device_unregister(indio_dev);
+error_remove_trigger:
+	hid_sensor_remove_trigger(indio_dev);
+error_unreg_buffer_funcs:
+	iio_triggered_buffer_cleanup(indio_dev);
+error_free_dev_mem:
+	kfree(indio_dev->channels);
+error_free_dev:
+	iio_device_free(indio_dev);
+error_ret:
+	return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int __devinit hid_als_remove(struct platform_device *pdev)
+{
+	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ALS);
+	iio_device_unregister(indio_dev);
+	hid_sensor_remove_trigger(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	kfree(indio_dev->channels);
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static struct platform_driver hid_als_platform_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= hid_als_probe,
+	.remove		= hid_als_remove,
+};
+module_platform_driver(hid_als_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor ALS");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");

+ 16 - 0
drivers/iio/magnetometer/Kconfig

@@ -0,0 +1,16 @@
+#
+# Magnetometer sensors
+#
+menu "Magnetometer sensors"
+
+config HID_SENSOR_MAGNETOMETER_3D
+	depends on HID_SENSOR_HUB
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	select HID_SENSOR_IIO_COMMON
+	tristate "HID Magenetometer 3D"
+	help
+	  Say yes here to build support for the HID SENSOR
+	  Magnetometer 3D.
+
+endmenu

+ 5 - 0
drivers/iio/magnetometer/Makefile

@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O Magnetometer sensor drivers
+#
+
+obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o

+ 419 - 0
drivers/iio/magnetometer/hid-sensor-magn-3d.c

@@ -0,0 +1,419 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-attributes.h"
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Magnetometer-3D: 0x200083*/
+#define DRIVER_NAME "HID-SENSOR-200083"
+
+enum magn_3d_channel {
+	CHANNEL_SCAN_INDEX_X,
+	CHANNEL_SCAN_INDEX_Y,
+	CHANNEL_SCAN_INDEX_Z,
+	MAGN_3D_CHANNEL_MAX,
+};
+
+struct magn_3d_state {
+	struct hid_sensor_hub_callbacks callbacks;
+	struct hid_sensor_iio_common common_attributes;
+	struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX];
+	u32 magn_val[MAGN_3D_CHANNEL_MAX];
+};
+
+static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
+	HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS,
+	HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS,
+	HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec magn_3d_channels[] = {
+	{
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT |
+		IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+		IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+		.scan_index = CHANNEL_SCAN_INDEX_X,
+	}, {
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT |
+		IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+		IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+		.scan_index = CHANNEL_SCAN_INDEX_Y,
+	}, {
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT |
+		IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+		IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+		.scan_index = CHANNEL_SCAN_INDEX_Z,
+	}
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void magn_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+						int channel, int size)
+{
+	channels[channel].scan_type.sign = 's';
+	/* Real storage bits will change based on the report desc. */
+	channels[channel].scan_type.realbits = size * 8;
+	/* Maximum size of a sample to capture is u32 */
+	channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int magn_3d_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val, int *val2,
+			      long mask)
+{
+	struct magn_3d_state *magn_state = iio_priv(indio_dev);
+	int report_id = -1;
+	u32 address;
+	int ret;
+	int ret_type;
+
+	*val = 0;
+	*val2 = 0;
+	switch (mask) {
+	case 0:
+		report_id =
+			magn_state->magn[chan->scan_index].report_id;
+		address = magn_3d_addresses[chan->scan_index];
+		if (report_id >= 0)
+			*val = sensor_hub_input_attr_get_raw_value(
+				magn_state->common_attributes.hsdev,
+				HID_USAGE_SENSOR_COMPASS_3D, address,
+				report_id);
+		else {
+			*val = 0;
+			return -EINVAL;
+		}
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		*val = magn_state->magn[CHANNEL_SCAN_INDEX_X].units;
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = hid_sensor_convert_exponent(
+			magn_state->magn[CHANNEL_SCAN_INDEX_X].unit_expo);
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = hid_sensor_read_samp_freq_value(
+			&magn_state->common_attributes, val, val2);
+		ret_type = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		ret = hid_sensor_read_raw_hyst_value(
+			&magn_state->common_attributes, val, val2);
+		ret_type = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	default:
+		ret_type = -EINVAL;
+		break;
+	}
+
+	return ret_type;
+}
+
+/* Channel write_raw handler */
+static int magn_3d_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask)
+{
+	struct magn_3d_state *magn_state = iio_priv(indio_dev);
+	int ret = 0;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = hid_sensor_write_samp_freq_value(
+				&magn_state->common_attributes, val, val2);
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		ret = hid_sensor_write_raw_hyst_value(
+				&magn_state->common_attributes, val, val2);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int magn_3d_write_raw_get_fmt(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       long mask)
+{
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static const struct iio_info magn_3d_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &magn_3d_read_raw,
+	.write_raw = &magn_3d_write_raw,
+	.write_raw_get_fmt = &magn_3d_write_raw_get_fmt,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
+{
+	struct iio_buffer *buffer = indio_dev->buffer;
+	int datum_sz;
+
+	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+	if (!buffer) {
+		dev_err(&indio_dev->dev, "Buffer == NULL\n");
+		return;
+	}
+	datum_sz = buffer->access->get_bytes_per_datum(buffer);
+	if (len > datum_sz) {
+		dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+				datum_sz);
+		return;
+	}
+	iio_push_to_buffer(buffer, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
+				unsigned usage_id,
+				void *priv)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(priv);
+	struct magn_3d_state *magn_state = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "magn_3d_proc_event [%d]\n",
+				magn_state->common_attributes.data_ready);
+	if (magn_state->common_attributes.data_ready)
+		hid_sensor_push_data(indio_dev,
+				(u8 *)magn_state->magn_val,
+				sizeof(magn_state->magn_val));
+
+	return 0;
+}
+
+/* Capture samples in local storage */
+static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
+				unsigned usage_id,
+				size_t raw_len, char *raw_data,
+				void *priv)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(priv);
+	struct magn_3d_state *magn_state = iio_priv(indio_dev);
+	int offset;
+	int ret = -EINVAL;
+
+	switch (usage_id) {
+	case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS:
+	case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS:
+	case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS:
+		offset = usage_id - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS;
+		magn_state->magn_val[CHANNEL_SCAN_INDEX_X + offset] =
+						*(u32 *)raw_data;
+		ret = 0;
+	break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int magn_3d_parse_report(struct platform_device *pdev,
+				struct hid_sensor_hub_device *hsdev,
+				struct iio_chan_spec *channels,
+				unsigned usage_id,
+				struct magn_3d_state *st)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
+		ret = sensor_hub_input_get_attribute_info(hsdev,
+				HID_INPUT_REPORT,
+				usage_id,
+				HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS + i,
+				&st->magn[CHANNEL_SCAN_INDEX_X + i]);
+		if (ret < 0)
+			break;
+		magn_3d_adjust_channel_bit_mask(channels,
+				CHANNEL_SCAN_INDEX_X + i,
+				st->magn[CHANNEL_SCAN_INDEX_X + i].size);
+	}
+	dev_dbg(&pdev->dev, "magn_3d %x:%x, %x:%x, %x:%x\n",
+			st->magn[0].index,
+			st->magn[0].report_id,
+			st->magn[1].index, st->magn[1].report_id,
+			st->magn[2].index, st->magn[2].report_id);
+
+	return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int __devinit hid_magn_3d_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	static char *name = "magn_3d";
+	struct iio_dev *indio_dev;
+	struct magn_3d_state *magn_state;
+	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+	struct iio_chan_spec *channels;
+
+	indio_dev = iio_device_alloc(sizeof(struct magn_3d_state));
+	if (indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, indio_dev);
+
+	magn_state = iio_priv(indio_dev);
+	magn_state->common_attributes.hsdev = hsdev;
+	magn_state->common_attributes.pdev = pdev;
+
+	ret = hid_sensor_parse_common_attributes(hsdev,
+				HID_USAGE_SENSOR_COMPASS_3D,
+				&magn_state->common_attributes);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to setup common attributes\n");
+		goto error_free_dev;
+	}
+
+	channels = kmemdup(magn_3d_channels,
+					sizeof(magn_3d_channels),
+					GFP_KERNEL);
+	if (!channels) {
+		dev_err(&pdev->dev, "failed to duplicate channels\n");
+		goto error_free_dev;
+	}
+
+	ret = magn_3d_parse_report(pdev, hsdev, channels,
+				HID_USAGE_SENSOR_COMPASS_3D, magn_state);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to setup attributes\n");
+		goto error_free_dev_mem;
+	}
+
+	indio_dev->channels = channels;
+	indio_dev->num_channels = ARRAY_SIZE(magn_3d_channels);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->info = &magn_3d_info;
+	indio_dev->name = name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+		NULL, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+		goto error_free_dev_mem;
+	}
+	magn_state->common_attributes.data_ready = false;
+	ret = hid_sensor_setup_trigger(indio_dev, name,
+					&magn_state->common_attributes);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "trigger setup failed\n");
+		goto error_unreg_buffer_funcs;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_trigger;
+	}
+
+	magn_state->callbacks.send_event = magn_3d_proc_event;
+	magn_state->callbacks.capture_sample = magn_3d_capture_sample;
+	magn_state->callbacks.pdev = pdev;
+	ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D,
+					&magn_state->callbacks);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "callback reg failed\n");
+		goto error_iio_unreg;
+	}
+
+	return ret;
+
+error_iio_unreg:
+	iio_device_unregister(indio_dev);
+error_remove_trigger:
+	hid_sensor_remove_trigger(indio_dev);
+error_unreg_buffer_funcs:
+	iio_triggered_buffer_cleanup(indio_dev);
+error_free_dev_mem:
+	kfree(indio_dev->channels);
+error_free_dev:
+	iio_device_free(indio_dev);
+error_ret:
+	return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int __devinit hid_magn_3d_remove(struct platform_device *pdev)
+{
+	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
+	iio_device_unregister(indio_dev);
+	hid_sensor_remove_trigger(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	kfree(indio_dev->channels);
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static struct platform_driver hid_magn_3d_platform_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= hid_magn_3d_probe,
+	.remove		= hid_magn_3d_remove,
+};
+module_platform_driver(hid_magn_3d_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Magnetometer 3D");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");

+ 7 - 0
drivers/power/Kconfig

@@ -29,6 +29,13 @@ config APM_POWER
 	  Say Y here to enable support APM status emulation using
 	  battery class devices.
 
+config GENERIC_ADC_BATTERY
+	tristate "Generic battery support using IIO"
+	depends on IIO
+	help
+	  Say Y here to enable support for the generic battery driver
+	  which uses IIO framework to read adc.
+
 config MAX8925_POWER
 	tristate "MAX8925 battery charger support"
 	depends on MFD_MAX8925

+ 1 - 0
drivers/power/Makefile

@@ -5,6 +5,7 @@ power_supply-$(CONFIG_SYSFS)		+= power_supply_sysfs.o
 power_supply-$(CONFIG_LEDS_TRIGGERS)	+= power_supply_leds.o
 
 obj-$(CONFIG_POWER_SUPPLY)	+= power_supply.o
+obj-$(CONFIG_GENERIC_ADC_BATTERY)	+= generic-adc-battery.o
 
 obj-$(CONFIG_PDA_POWER)		+= pda_power.o
 obj-$(CONFIG_APM_POWER)		+= apm_power.o

+ 422 - 0
drivers/power/generic-adc-battery.c

@@ -0,0 +1,422 @@
+/*
+ * Generic battery driver code using IIO
+ * Copyright (C) 2012, Anish Kumar <anish198519851985@gmail.com>
+ * based on jz4740-battery.c
+ * based on s3c_adc_battery.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/power/generic-adc-battery.h>
+
+#define JITTER_DEFAULT 10 /* hope 10ms is enough */
+
+enum gab_chan_type {
+	GAB_VOLTAGE = 0,
+	GAB_CURRENT,
+	GAB_POWER,
+	GAB_MAX_CHAN_TYPE
+};
+
+/*
+ * gab_chan_name suggests the standard channel names for commonly used
+ * channel types.
+ */
+static const char *const gab_chan_name[] = {
+	[GAB_VOLTAGE]	= "voltage",
+	[GAB_CURRENT]	= "current",
+	[GAB_POWER]		= "power",
+};
+
+struct gab {
+	struct power_supply	psy;
+	struct iio_channel	*channel[GAB_MAX_CHAN_TYPE];
+	struct gab_platform_data	*pdata;
+	struct delayed_work bat_work;
+	int	level;
+	int	status;
+	bool cable_plugged;
+};
+
+static struct gab *to_generic_bat(struct power_supply *psy)
+{
+	return container_of(psy, struct gab, psy);
+}
+
+static void gab_ext_power_changed(struct power_supply *psy)
+{
+	struct gab *adc_bat = to_generic_bat(psy);
+
+	schedule_delayed_work(&adc_bat->bat_work, msecs_to_jiffies(0));
+}
+
+static const enum power_supply_property gab_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+/*
+ * This properties are set based on the received platform data and this
+ * should correspond one-to-one with enum chan_type.
+ */
+static const enum power_supply_property gab_dyn_props[] = {
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_POWER_NOW,
+};
+
+static bool gab_charge_finished(struct gab *adc_bat)
+{
+	struct gab_platform_data *pdata = adc_bat->pdata;
+	bool ret = gpio_get_value(pdata->gpio_charge_finished);
+	bool inv = pdata->gpio_inverted;
+
+	if (!gpio_is_valid(pdata->gpio_charge_finished))
+		return false;
+	return ret ^ inv;
+}
+
+static int gab_get_status(struct gab *adc_bat)
+{
+	struct gab_platform_data *pdata = adc_bat->pdata;
+	struct power_supply_info *bat_info;
+
+	bat_info = &pdata->battery_info;
+	if (adc_bat->level == bat_info->charge_full_design)
+		return POWER_SUPPLY_STATUS_FULL;
+	return adc_bat->status;
+}
+
+static enum gab_chan_type gab_prop_to_chan(enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_POWER_NOW:
+		return GAB_POWER;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		return GAB_VOLTAGE;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		return GAB_CURRENT;
+	default:
+		WARN_ON(1);
+		break;
+	}
+	return GAB_POWER;
+}
+
+static int read_channel(struct gab *adc_bat, enum power_supply_property psp,
+		int *result)
+{
+	int ret;
+	int chan_index;
+
+	chan_index = gab_prop_to_chan(psp);
+	ret = iio_read_channel_processed(adc_bat->channel[chan_index],
+			result);
+	if (ret < 0)
+		pr_err("read channel error\n");
+	return ret;
+}
+
+static int gab_get_property(struct power_supply *psy,
+		enum power_supply_property psp, union power_supply_propval *val)
+{
+	struct gab *adc_bat;
+	struct gab_platform_data *pdata;
+	struct power_supply_info *bat_info;
+	int result = 0;
+	int ret = 0;
+
+	adc_bat = to_generic_bat(psy);
+	if (!adc_bat) {
+		dev_err(psy->dev, "no battery infos ?!\n");
+		return -EINVAL;
+	}
+	pdata = adc_bat->pdata;
+	bat_info = &pdata->battery_info;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		gab_get_status(adc_bat);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
+		val->intval = 0;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_NOW:
+		val->intval = pdata->cal_charge(result);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+	case POWER_SUPPLY_PROP_POWER_NOW:
+		ret = read_channel(adc_bat, psp, &result);
+		if (ret < 0)
+			goto err;
+		val->intval = result;
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = bat_info->technology;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = bat_info->voltage_min_design;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = bat_info->voltage_max_design;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+		val->intval = bat_info->charge_full_design;
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = bat_info->name;
+		break;
+	default:
+		return -EINVAL;
+	}
+err:
+	return ret;
+}
+
+static void gab_work(struct work_struct *work)
+{
+	struct gab *adc_bat;
+	struct gab_platform_data *pdata;
+	struct delayed_work *delayed_work;
+	bool is_plugged;
+	int status;
+
+	delayed_work = container_of(work, struct delayed_work, work);
+	adc_bat = container_of(delayed_work, struct gab, bat_work);
+	pdata = adc_bat->pdata;
+	status = adc_bat->status;
+
+	is_plugged = power_supply_am_i_supplied(&adc_bat->psy);
+	adc_bat->cable_plugged = is_plugged;
+
+	if (!is_plugged)
+		adc_bat->status =  POWER_SUPPLY_STATUS_DISCHARGING;
+	else if (gab_charge_finished(adc_bat))
+		adc_bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	else
+		adc_bat->status = POWER_SUPPLY_STATUS_CHARGING;
+
+	if (status != adc_bat->status)
+		power_supply_changed(&adc_bat->psy);
+}
+
+static irqreturn_t gab_charged(int irq, void *dev_id)
+{
+	struct gab *adc_bat = dev_id;
+	struct gab_platform_data *pdata = adc_bat->pdata;
+	int delay;
+
+	delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT;
+	schedule_delayed_work(&adc_bat->bat_work,
+			msecs_to_jiffies(delay));
+	return IRQ_HANDLED;
+}
+
+static int __devinit gab_probe(struct platform_device *pdev)
+{
+	struct gab *adc_bat;
+	struct power_supply *psy;
+	struct gab_platform_data *pdata = pdev->dev.platform_data;
+	enum power_supply_property *properties;
+	int ret = 0;
+	int chan;
+	int index = 0;
+
+	adc_bat = devm_kzalloc(&pdev->dev, sizeof(*adc_bat), GFP_KERNEL);
+	if (!adc_bat) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	psy = &adc_bat->psy;
+	psy->name = pdata->battery_info.name;
+
+	/* bootup default values for the battery */
+	adc_bat->cable_plugged = false;
+	adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+	psy->type = POWER_SUPPLY_TYPE_BATTERY;
+	psy->get_property = gab_get_property;
+	psy->external_power_changed = gab_ext_power_changed;
+	adc_bat->pdata = pdata;
+
+	/* calculate the total number of channels */
+	chan = ARRAY_SIZE(gab_chan_name);
+
+	/*
+	 * copying the static properties and allocating extra memory for holding
+	 * the extra configurable properties received from platform data.
+	 */
+	psy->properties = kcalloc(ARRAY_SIZE(gab_props) +
+					ARRAY_SIZE(gab_chan_name),
+					sizeof(*psy->properties), GFP_KERNEL);
+	if (!psy->properties) {
+		ret = -ENOMEM;
+		goto first_mem_fail;
+	}
+
+	memcpy(psy->properties, gab_props, sizeof(gab_props));
+	properties = psy->properties + sizeof(gab_props);
+
+	/*
+	 * getting channel from iio and copying the battery properties
+	 * based on the channel supported by consumer device.
+	 */
+	for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
+		adc_bat->channel[chan] = iio_channel_get(dev_name(&pdev->dev),
+						gab_chan_name[chan]);
+		if (IS_ERR(adc_bat->channel[chan])) {
+			ret = PTR_ERR(adc_bat->channel[chan]);
+		} else {
+			/* copying properties for supported channels only */
+			memcpy(properties + sizeof(*(psy->properties)) * index,
+					&gab_dyn_props[chan],
+					sizeof(gab_dyn_props[chan]));
+			index++;
+		}
+	}
+
+	/* none of the channels are supported so let's bail out */
+	if (index == ARRAY_SIZE(gab_chan_name))
+		goto second_mem_fail;
+
+	/*
+	 * Total number of properties is equal to static properties
+	 * plus the dynamic properties.Some properties may not be set
+	 * as come channels may be not be supported by the device.So
+	 * we need to take care of that.
+	 */
+	psy->num_properties = ARRAY_SIZE(gab_props) + index;
+
+	ret = power_supply_register(&pdev->dev, psy);
+	if (ret)
+		goto err_reg_fail;
+
+	INIT_DELAYED_WORK(&adc_bat->bat_work, gab_work);
+
+	if (gpio_is_valid(pdata->gpio_charge_finished)) {
+		int irq;
+		ret = gpio_request(pdata->gpio_charge_finished, "charged");
+		if (ret)
+			goto gpio_req_fail;
+
+		irq = gpio_to_irq(pdata->gpio_charge_finished);
+		ret = request_any_context_irq(irq, gab_charged,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				"battery charged", adc_bat);
+		if (ret)
+			goto err_gpio;
+	}
+
+	platform_set_drvdata(pdev, adc_bat);
+
+	/* Schedule timer to check current status */
+	schedule_delayed_work(&adc_bat->bat_work,
+			msecs_to_jiffies(0));
+	return 0;
+
+err_gpio:
+	gpio_free(pdata->gpio_charge_finished);
+gpio_req_fail:
+	power_supply_unregister(psy);
+err_reg_fail:
+	for (chan = 0; ARRAY_SIZE(gab_chan_name); chan++)
+		iio_channel_release(adc_bat->channel[chan]);
+second_mem_fail:
+	kfree(psy->properties);
+first_mem_fail:
+	return ret;
+}
+
+static int __devexit gab_remove(struct platform_device *pdev)
+{
+	int chan;
+	struct gab *adc_bat = platform_get_drvdata(pdev);
+	struct gab_platform_data *pdata = adc_bat->pdata;
+
+	power_supply_unregister(&adc_bat->psy);
+
+	if (gpio_is_valid(pdata->gpio_charge_finished)) {
+		free_irq(gpio_to_irq(pdata->gpio_charge_finished), adc_bat);
+		gpio_free(pdata->gpio_charge_finished);
+	}
+
+	for (chan = 0; ARRAY_SIZE(gab_chan_name); chan++)
+		iio_channel_release(adc_bat->channel[chan]);
+
+	kfree(adc_bat->psy.properties);
+	cancel_delayed_work(&adc_bat->bat_work);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int gab_suspend(struct device *dev)
+{
+	struct gab *adc_bat = dev_get_drvdata(dev);
+
+	cancel_delayed_work_sync(&adc_bat->bat_work);
+	adc_bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
+	return 0;
+}
+
+static int gab_resume(struct device *dev)
+{
+	struct gab *adc_bat = dev_get_drvdata(dev);
+	struct gab_platform_data *pdata = adc_bat->pdata;
+	int delay;
+
+	delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT;
+
+	/* Schedule timer to check current status */
+	schedule_delayed_work(&adc_bat->bat_work,
+			msecs_to_jiffies(delay));
+	return 0;
+}
+
+static const struct dev_pm_ops gab_pm_ops = {
+	.suspend        = gab_suspend,
+	.resume         = gab_resume,
+};
+
+#define GAB_PM_OPS       (&gab_pm_ops)
+#else
+#define GAB_PM_OPS       (NULL)
+#endif
+
+static struct platform_driver gab_driver = {
+	.driver		= {
+		.name	= "generic-adc-battery",
+		.owner	= THIS_MODULE,
+		.pm	= GAB_PM_OPS
+	},
+	.probe		= gab_probe,
+	.remove		= __devexit_p(gab_remove),
+};
+module_platform_driver(gab_driver);
+
+MODULE_AUTHOR("anish kumar <anish198519851985@gmail.com>");
+MODULE_DESCRIPTION("generic battery driver using IIO");
+MODULE_LICENSE("GPL");

+ 8 - 2
drivers/staging/Kconfig

@@ -122,8 +122,6 @@ source "drivers/staging/android/Kconfig"
 
 source "drivers/staging/telephony/Kconfig"
 
-source "drivers/staging/ramster/Kconfig"
-
 source "drivers/staging/ozwpan/Kconfig"
 
 source "drivers/staging/ccg/Kconfig"
@@ -136,4 +134,12 @@ source "drivers/staging/csr/Kconfig"
 
 source "drivers/staging/omap-thermal/Kconfig"
 
+source "drivers/staging/ramster/Kconfig"
+
+source "drivers/staging/silicom/Kconfig"
+
+source "drivers/staging/ced1401/Kconfig"
+
+source "drivers/staging/imx-drm/Kconfig"
+
 endif # STAGING

+ 4 - 1
drivers/staging/Makefile

@@ -54,9 +54,12 @@ obj-$(CONFIG_MFD_NVEC)		+= nvec/
 obj-$(CONFIG_DRM_OMAP)		+= omapdrm/
 obj-$(CONFIG_ANDROID)		+= android/
 obj-$(CONFIG_PHONE)		+= telephony/
-obj-$(CONFIG_RAMSTER)		+= ramster/
 obj-$(CONFIG_USB_WPAN_HCD)	+= ozwpan/
 obj-$(CONFIG_USB_G_CCG)		+= ccg/
 obj-$(CONFIG_WIMAX_GDM72XX)	+= gdm72xx/
 obj-$(CONFIG_CSR_WIFI)		+= csr/
 obj-$(CONFIG_OMAP_BANDGAP)	+= omap-thermal/
+obj-$(CONFIG_ZCACHE2)		+= ramster/
+obj-$(CONFIG_NET_VENDOR_SILICOM)	+= silicom/
+obj-$(CONFIG_CED1401)		+= ced1401/
+obj-$(CONFIG_DRM_IMX)		+= imx-drm/

+ 7 - 10
drivers/staging/android/alarm-dev.c

@@ -67,10 +67,8 @@ static struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT];
 
 static int is_wakeup(enum android_alarm_type type)
 {
-	if (type == ANDROID_ALARM_RTC_WAKEUP ||
-			type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP)
-		return 1;
-	return 0;
+	return (type == ANDROID_ALARM_RTC_WAKEUP ||
+		type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP);
 }
 
 
@@ -85,12 +83,9 @@ static void devalarm_start(struct devalarm *alrm, ktime_t exp)
 
 static int devalarm_try_to_cancel(struct devalarm *alrm)
 {
-	int ret;
 	if (is_wakeup(alrm->type))
-		ret = alarm_try_to_cancel(&alrm->u.alrm);
-	else
-		ret = hrtimer_try_to_cancel(&alrm->u.hrt);
-	return ret;
+		return alarm_try_to_cancel(&alrm->u.alrm);
+	return hrtimer_try_to_cancel(&alrm->u.hrt);
 }
 
 static void devalarm_cancel(struct devalarm *alrm)
@@ -223,10 +218,12 @@ from_old_alarm_set:
 		case ANDROID_ALARM_ELAPSED_REALTIME:
 			get_monotonic_boottime(&tmp_time);
 			break;
-		case ANDROID_ALARM_TYPE_COUNT:
 		case ANDROID_ALARM_SYSTEMTIME:
 			ktime_get_ts(&tmp_time);
 			break;
+		default:
+			rv = -EINVAL;
+			goto err1;
 		}
 		if (copy_to_user((void __user *)arg, &tmp_time,
 		    sizeof(tmp_time))) {

+ 16 - 16
drivers/staging/android/ashmem.c

@@ -1,20 +1,20 @@
 /* mm/ashmem.c
-**
-** Anonymous Shared Memory Subsystem, ashmem
-**
-** Copyright (C) 2008 Google, Inc.
-**
-** Robert Love <rlove@google.com>
-**
-** This software is licensed under the terms of the GNU General Public
-** License version 2, as published by the Free Software Foundation, and
-** may be copied, distributed, and modified under those terms.
-**
-** 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.
-*/
+ *
+ * Anonymous Shared Memory Subsystem, ashmem
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * Robert Love <rlove@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
 
 #define pr_fmt(fmt) "ashmem: " fmt
 

+ 3 - 3
drivers/staging/android/binder.c

@@ -365,7 +365,7 @@ binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer);
 /*
  * copied from get_unused_fd_flags
  */
-int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
+static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
 {
 	struct files_struct *files = proc->files;
 	int fd, error;
@@ -415,13 +415,13 @@ repeat:
 	else
 		__clear_close_on_exec(fd, fdt);
 	files->next_fd = fd + 1;
-#if 1
+
 	/* Sanity check */
 	if (fdt->fd[fd] != NULL) {
 		pr_warn("get_unused_fd: slot %d not NULL!\n", fd);
 		fdt->fd[fd] = NULL;
 	}
-#endif
+
 	error = fd;
 
 out:

+ 26 - 14
drivers/staging/android/logger.c

@@ -32,38 +32,50 @@
 
 #include <asm/ioctls.h>
 
-/*
+/**
  * struct logger_log - represents a specific log, such as 'main' or 'radio'
+ * @buffer:	The actual ring buffer
+ * @misc:	The "misc" device representing the log
+ * @wq:		The wait queue for @readers
+ * @readers:	This log's readers
+ * @mutex:	The mutex that protects the @buffer
+ * @w_off:	The current write head offset
+ * @head:	The head, or location that readers start reading at.
+ * @size:	The size of the log
+ * @logs:	The list of log channels
  *
  * This structure lives from module insertion until module removal, so it does
  * not need additional reference counting. The structure is protected by the
  * mutex 'mutex'.
  */
 struct logger_log {
-	unsigned char		*buffer;/* the ring buffer itself */
-	struct miscdevice	misc;	/* misc device representing the log */
-	wait_queue_head_t	wq;	/* wait queue for readers */
-	struct list_head	readers; /* this log's readers */
-	struct mutex		mutex;	/* mutex protecting buffer */
-	size_t			w_off;	/* current write head offset */
-	size_t			head;	/* new readers start here */
-	size_t			size;	/* size of the log */
-	struct list_head	logs;	/* list of log channels (myself)*/
+	unsigned char		*buffer;
+	struct miscdevice	misc;
+	wait_queue_head_t	wq;
+	struct list_head	readers;
+	struct mutex		mutex;
+	size_t			w_off;
+	size_t			head;
+	size_t			size;
+	struct list_head	logs;
 };
 
 static LIST_HEAD(log_list);
 
 
-/*
+/**
  * struct logger_reader - a logging device open for reading
+ * @log:	The associated log
+ * @list:	The associated entry in @logger_log's list
+ * @r_off:	The current read head offset.
  *
  * This object lives from open to release, so we don't need additional
  * reference counting. The structure is protected by log->mutex.
  */
 struct logger_reader {
-	struct logger_log	*log;	/* associated log */
-	struct list_head	list;	/* entry in logger_log's list */
-	size_t			r_off;	/* current read head offset */
+	struct logger_log	*log;
+	struct list_head	list;
+	size_t			r_off;
 };
 
 /* logger_offset - returns index 'n' into the log via (optimized) modulus */

+ 17 - 7
drivers/staging/android/logger.h

@@ -20,14 +20,24 @@
 #include <linux/types.h>
 #include <linux/ioctl.h>
 
+/**
+ * struct logger_entry - defines a single entry that is given to a logger
+ * @len:	The length of the payload
+ * @__pad:	Two bytes of padding that appear to be required
+ * @pid:	The generating process' process ID
+ * @tid:	The generating process' thread ID
+ * @sec:	The number of seconds that have elapsed since the Epoch
+ * @nsec:	The number of nanoseconds that have elapsed since @sec
+ * @msg:	The message that is to be logged
+ */
 struct logger_entry {
-	__u16		len;	/* length of the payload */
-	__u16		__pad;	/* no matter what, we get 2 bytes of padding */
-	__s32		pid;	/* generating process's pid */
-	__s32		tid;	/* generating process's tid */
-	__s32		sec;	/* seconds since Epoch */
-	__s32		nsec;	/* nanoseconds */
-	char		msg[0];	/* the entry's payload */
+	__u16		len;
+	__u16		__pad;
+	__s32		pid;
+	__s32		tid;
+	__s32		sec;
+	__s32		nsec;
+	char		msg[0];
 };
 
 #define LOGGER_LOG_RADIO	"log_radio"	/* radio-related messages */

+ 1 - 12
drivers/staging/android/timed_gpio.c

@@ -161,18 +161,7 @@ static struct platform_driver timed_gpio_driver = {
 	},
 };
 
-static int __init timed_gpio_init(void)
-{
-	return platform_driver_register(&timed_gpio_driver);
-}
-
-static void __exit timed_gpio_exit(void)
-{
-	platform_driver_unregister(&timed_gpio_driver);
-}
-
-module_init(timed_gpio_init);
-module_exit(timed_gpio_exit);
+module_platform_driver(timed_gpio_driver);
 
 MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
 MODULE_DESCRIPTION("timed gpio driver");

+ 45 - 11
drivers/staging/asus_oled/asus_oled.c

@@ -42,8 +42,6 @@
 #define ASUS_OLED_NAME			"asus-oled"
 #define ASUS_OLED_UNDERSCORE_NAME	"asus_oled"
 
-#define ASUS_OLED_ERROR			"Asus OLED Display Error: "
-
 #define ASUS_OLED_STATIC		's'
 #define ASUS_OLED_ROLL			'r'
 #define ASUS_OLED_FLASH			'f'
@@ -57,8 +55,9 @@
 #define USB_DEVICE_ID_ASUS_LCM2     0x175b
 
 MODULE_AUTHOR("Jakub Schmidtke, sjakub@gmail.com");
-MODULE_DESCRIPTION("Asus OLED Driver v" ASUS_OLED_VERSION);
+MODULE_DESCRIPTION("Asus OLED Driver");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(ASUS_OLED_VERSION);
 
 static struct class *oled_class;
 static int oled_num;
@@ -138,6 +137,7 @@ struct asus_oled_dev {
 	size_t			buf_size;
 	char			*buf;
 	uint8_t			enabled;
+	uint8_t			enabled_post_resume;
 	struct device		*dev;
 };
 
@@ -383,13 +383,13 @@ static int append_values(struct asus_oled_dev *odev, uint8_t val, size_t count)
 
 		default:
 			i = 0;
-			printk(ASUS_OLED_ERROR "Unknown OLED Pack Mode: %d!\n",
+			dev_err(odev->dev, "Unknown OLED Pack Mode: %d!\n",
 			       odev->pack_mode);
 			break;
 		}
 
 		if (i >= odev->buf_size) {
-			printk(ASUS_OLED_ERROR "Buffer overflow! Report a bug:"
+			dev_err(odev->dev, "Buffer overflow! Report a bug:"
 			       "offs: %d >= %d i: %d (x: %d y: %d)\n",
 			       (int) odev->buf_offs, (int) odev->buf_size,
 			       (int) i, (int) x, (int) y);
@@ -435,7 +435,7 @@ static ssize_t odev_set_picture(struct asus_oled_dev *odev,
 		odev->buf = kmalloc(odev->buf_size, GFP_KERNEL);
 		if (odev->buf == NULL) {
 			odev->buf_size = 0;
-			printk(ASUS_OLED_ERROR "Out of memory!\n");
+			dev_err(odev->dev, "Out of memory!\n");
 			return -ENOMEM;
 		}
 
@@ -473,7 +473,7 @@ static ssize_t odev_set_picture(struct asus_oled_dev *odev,
 			odev->pic_mode = buf[1];
 			break;
 		default:
-			printk(ASUS_OLED_ERROR "Wrong picture mode: '%c'.\n",
+			dev_err(odev->dev, "Wrong picture mode: '%c'.\n",
 			       buf[1]);
 			return -EIO;
 			break;
@@ -533,7 +533,7 @@ static ssize_t odev_set_picture(struct asus_oled_dev *odev,
 
 		if (odev->buf == NULL) {
 			odev->buf_size = 0;
-			printk(ASUS_OLED_ERROR "Out of memory!\n");
+			dev_err(odev->dev, "Out of memory!\n");
 			return -ENOMEM;
 		}
 
@@ -593,15 +593,15 @@ static ssize_t odev_set_picture(struct asus_oled_dev *odev,
 	return count;
 
 error_width:
-	printk(ASUS_OLED_ERROR "Wrong picture width specified.\n");
+	dev_err(odev->dev, "Wrong picture width specified.\n");
 	return -EIO;
 
 error_height:
-	printk(ASUS_OLED_ERROR "Wrong picture height specified.\n");
+	dev_err(odev->dev, "Wrong picture height specified.\n");
 	return -EIO;
 
 error_header:
-	printk(ASUS_OLED_ERROR "Wrong picture header.\n");
+	dev_err(odev->dev, "Wrong picture header.\n");
 	return -EIO;
 }
 
@@ -766,11 +766,45 @@ static void asus_oled_disconnect(struct usb_interface *interface)
 	dev_info(&interface->dev, "Disconnected Asus OLED device\n");
 }
 
+#ifdef CONFIG_PM
+static int asus_oled_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct asus_oled_dev *odev;
+
+	odev = usb_get_intfdata(intf);
+	if (!odev)
+		return -ENODEV;
+
+	odev->enabled_post_resume = odev->enabled;
+	enable_oled(odev, 0);
+
+	return 0;
+}
+
+static int asus_oled_resume(struct usb_interface *intf)
+{
+	struct asus_oled_dev *odev;
+
+	odev = usb_get_intfdata(intf);
+	if (!odev)
+		return -ENODEV;
+
+	enable_oled(odev, odev->enabled_post_resume);
+
+	return 0;
+}
+#else
+#define asus_oled_suspend NULL
+#define asus_oled_resume NULL
+#endif
+
 static struct usb_driver oled_driver = {
 	.name =		ASUS_OLED_NAME,
 	.probe =	asus_oled_probe,
 	.disconnect =	asus_oled_disconnect,
 	.id_table =	id_table,
+	.suspend =	asus_oled_suspend,
+	.resume =	asus_oled_resume,
 };
 
 static CLASS_ATTR_STRING(version, S_IRUGO,

+ 2 - 0
drivers/staging/bcm/Bcmchar.c

@@ -820,6 +820,7 @@ cntrlEnd:
 
 		if (copy_from_user(psFwInfo, IoBuffer.InputBuffer, IoBuffer.InputLength)) {
 			up(&Adapter->fw_download_sema);
+			kfree(psFwInfo);
 			return -EFAULT;
 		}
 
@@ -829,6 +830,7 @@ cntrlEnd:
 			BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0, "Something else is wrong %lu\n",
 					psFwInfo->u32FirmwareLength);
 			up(&Adapter->fw_download_sema);
+			kfree(psFwInfo);
 			Status = -EINVAL;
 			return Status;
 		}

+ 68 - 82
drivers/staging/bcm/CmHost.c

@@ -235,7 +235,7 @@ void ClearTargetDSXBuffer(struct bcm_mini_adapter *Adapter, B_UINT16 TID, BOOLEA
  * @ingroup ctrl_pkt_functions
  * copy classifier rule into the specified SF index
  */
-static inline VOID CopyClassifierRuleToSF(struct bcm_mini_adapter *Adapter, stConvergenceSLTypes  *psfCSType, UINT uiSearchRuleIndex, UINT nClassifierIndex)
+static inline VOID CopyClassifierRuleToSF(struct bcm_mini_adapter *Adapter, struct bcm_convergence_types *psfCSType, UINT uiSearchRuleIndex, UINT nClassifierIndex)
 {
 	struct bcm_classifier_rule *pstClassifierEntry = NULL;
 	/* VOID *pvPhsContext = NULL; */
@@ -428,7 +428,7 @@ VOID DeleteAllClassifiersForSF(struct bcm_mini_adapter *Adapter, UINT uiSearchRu
  * @ingroup ctrl_pkt_functions
  */
 static VOID CopyToAdapter(register struct bcm_mini_adapter *Adapter, /* <Pointer to the Adapter structure */
-			register pstServiceFlowParamSI psfLocalSet, /* <Pointer to the ServiceFlowParamSI structure */
+			register struct bcm_connect_mgr_params *psfLocalSet, /* Pointer to the connection manager parameters structure */
 			register UINT uiSearchRuleIndex, /* <Index of Queue, to which this data belongs */
 			register UCHAR ucDsxType,
 			stLocalSFAddIndicationAlt *pstAddIndication) {
@@ -439,7 +439,7 @@ static VOID CopyToAdapter(register struct bcm_mini_adapter *Adapter, /* <Pointer
 	enum E_CLASSIFIER_ACTION eClassifierAction = eInvalidClassifierAction;
 	B_UINT16 u16PacketClassificationRuleIndex = 0;
 	int i;
-	stConvergenceSLTypes *psfCSType = NULL;
+	struct bcm_convergence_types *psfCSType = NULL;
 	S_PHS_RULE sPhsRule;
 	USHORT uVCID = Adapter->PackInfo[uiSearchRuleIndex].usVCID_Value;
 	UINT UGIValue = 0;
@@ -915,7 +915,7 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
 	if (!pstAddIndication->sfAuthorizedSet.bValid)
 		pstAddIndication->sfAuthorizedSet.bValid = 1;
 	for (nIndex = 0; nIndex < nCurClassifierCnt; nIndex++) {
-		stConvergenceSLTypes *psfCSType = NULL;
+		struct bcm_convergence_types *psfCSType = NULL;
 		psfCSType =  &pstAddIndication->sfAuthorizedSet.cConvergenceSLTypes[nIndex];
 
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "psfCSType = %p", psfCSType);
@@ -999,13 +999,10 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
 #ifdef VERSION_D5
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPv6FlowLableLength: 0x%X ",
 				psfCSType->cCPacketClassificationRule.u8IPv6FlowLableLength);
-		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPv6FlowLable[6]: 0x %02X %02X %02X %02X %02X %02X ",
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[0],
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[1],
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[2],
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[3],
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[4],
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[5]);
+		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+				DBG_LVL_ALL, "u8IPv6FlowLable[6]: 0x%*ph ",
+				6, psfCSType->cCPacketClassificationRule.
+					      u8IPv6FlowLable);
 #endif
 	}
 
@@ -1015,13 +1012,9 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u16CID: 0x%X", pstAddIndication->sfAdmittedSet.u16CID);
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ServiceClassNameLength: 0x%X",
 			pstAddIndication->sfAdmittedSet.u8ServiceClassNameLength);
-	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ServiceClassName: 0x %02X %02X %02X %02X %02X %02X",
-			pstAddIndication->sfAdmittedSet.u8ServiceClassName[0],
-			pstAddIndication->sfAdmittedSet.u8ServiceClassName[1],
-			pstAddIndication->sfAdmittedSet.u8ServiceClassName[2],
-			pstAddIndication->sfAdmittedSet.u8ServiceClassName[3],
-			pstAddIndication->sfAdmittedSet.u8ServiceClassName[4],
-			pstAddIndication->sfAdmittedSet.u8ServiceClassName[5]);
+	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL,
+			"u8ServiceClassName: 0x%*ph",
+			6, pstAddIndication->sfAdmittedSet.u8ServiceClassName);
 
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8MBSService: 0x%02X", pstAddIndication->sfAdmittedSet.u8MBSService);
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8QosParamSet: 0x%02X", pstAddIndication->sfAdmittedSet.u8QosParamSet);
@@ -1066,7 +1059,7 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
 		nCurClassifierCnt = MAX_CLASSIFIERS_IN_SF;
 
 	for (nIndex = 0; nIndex < nCurClassifierCnt; nIndex++) {
-		stConvergenceSLTypes *psfCSType = NULL;
+		struct bcm_convergence_types *psfCSType = NULL;
 
 		psfCSType =  &pstAddIndication->sfAdmittedSet.cConvergenceSLTypes[nIndex];
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, " CCPacketClassificationRuleSI====>");
@@ -1074,10 +1067,10 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
 				psfCSType->cCPacketClassificationRule.u8ClassifierRulePriority);
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPTypeOfServiceLength: 0x%02X",
 				psfCSType->cCPacketClassificationRule.u8IPTypeOfServiceLength);
-		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPTypeOfService[3]: 0x%02X %02X %02X",
-				psfCSType->cCPacketClassificationRule.u8IPTypeOfService[0],
-				psfCSType->cCPacketClassificationRule.u8IPTypeOfService[1],
-				psfCSType->cCPacketClassificationRule.u8IPTypeOfService[2]);
+		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+				DBG_LVL_ALL, "u8IPTypeOfService[3]: 0x%*ph",
+				3, psfCSType->cCPacketClassificationRule.
+					      u8IPTypeOfService);
 		for (uiLoopIndex = 0; uiLoopIndex < 1; uiLoopIndex++)
 			BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8Protocol: 0x%02X ", psfCSType->cCPacketClassificationRule.u8Protocol);
 
@@ -1098,20 +1091,20 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ProtocolSourcePortRangeLength: 0x%02X ",
 				psfCSType->cCPacketClassificationRule.u8ProtocolSourcePortRangeLength);
 
-		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ProtocolSourcePortRange[4]: 0x %02X %02X %02X %02X ",
-				psfCSType->cCPacketClassificationRule.u8ProtocolSourcePortRange[0],
-				psfCSType->cCPacketClassificationRule.u8ProtocolSourcePortRange[1],
-				psfCSType->cCPacketClassificationRule.u8ProtocolSourcePortRange[2],
-				psfCSType->cCPacketClassificationRule.u8ProtocolSourcePortRange[3]);
+		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+				DBG_LVL_ALL, "u8ProtocolSourcePortRange[4]: "
+				"0x%*ph ", 4, psfCSType->
+						cCPacketClassificationRule.
+						u8ProtocolSourcePortRange);
 
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ProtocolDestPortRangeLength: 0x%02X ",
 				psfCSType->cCPacketClassificationRule.u8ProtocolDestPortRangeLength);
 
-		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ProtocolDestPortRange[4]: 0x %02X %02X %02X %02X ",
-				psfCSType->cCPacketClassificationRule.u8ProtocolDestPortRange[0],
-				psfCSType->cCPacketClassificationRule.u8ProtocolDestPortRange[1],
-				psfCSType->cCPacketClassificationRule.u8ProtocolDestPortRange[2],
-				psfCSType->cCPacketClassificationRule.u8ProtocolDestPortRange[3]);
+		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+				DBG_LVL_ALL, "u8ProtocolDestPortRange[4]: "
+				"0x%*ph ", 4, psfCSType->
+						cCPacketClassificationRule.
+						u8ProtocolDestPortRange);
 
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8EthernetDestMacAddressLength: 0x%02X ",
 				psfCSType->cCPacketClassificationRule.u8EthernetDestMacAddressLength);
@@ -1130,10 +1123,10 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
 						u8EthernetSourceMACAddress);
 
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8EthertypeLength: 0x%02X ", psfCSType->cCPacketClassificationRule.u8EthertypeLength);
-		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8Ethertype[3]: 0x%02X %02X %02X",
-				psfCSType->cCPacketClassificationRule.u8Ethertype[0],
-				psfCSType->cCPacketClassificationRule.u8Ethertype[1],
-				psfCSType->cCPacketClassificationRule.u8Ethertype[2]);
+		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+				DBG_LVL_ALL, "u8Ethertype[3]: 0x%*ph",
+				3, psfCSType->cCPacketClassificationRule.
+					      u8Ethertype);
 
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u16UserPriority: 0x%X ", psfCSType->cCPacketClassificationRule.u16UserPriority);
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u16VLANID: 0x%X ", psfCSType->cCPacketClassificationRule.u16VLANID);
@@ -1147,13 +1140,10 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
 #ifdef VERSION_D5
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPv6FlowLableLength: 0x%X ",
 				psfCSType->cCPacketClassificationRule.u8IPv6FlowLableLength);
-		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPv6FlowLable[6]: 0x %02X %02X %02X %02X %02X %02X ",
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[0],
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[1],
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[2],
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[3],
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[4],
-				psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[5]);
+		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+				DBG_LVL_ALL, "u8IPv6FlowLable[6]: 0x%*ph ",
+				6, psfCSType->cCPacketClassificationRule.
+					      u8IPv6FlowLable);
 #endif
 	}
 
@@ -1162,13 +1152,9 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u32SFID: 0x%X", pstAddIndication->sfActiveSet.u32SFID);
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u16CID: 0x%X", pstAddIndication->sfActiveSet.u16CID);
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ServiceClassNameLength: 0x%X", pstAddIndication->sfActiveSet.u8ServiceClassNameLength);
-	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ServiceClassName: 0x %02X %02X %02X %02X %02X %02X",
-			pstAddIndication->sfActiveSet.u8ServiceClassName[0],
-			pstAddIndication->sfActiveSet.u8ServiceClassName[1],
-			pstAddIndication->sfActiveSet.u8ServiceClassName[2],
-			pstAddIndication->sfActiveSet.u8ServiceClassName[3],
-			pstAddIndication->sfActiveSet.u8ServiceClassName[4],
-			pstAddIndication->sfActiveSet.u8ServiceClassName[5]);
+	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL,
+			"u8ServiceClassName: 0x%*ph",
+			6, pstAddIndication->sfActiveSet.u8ServiceClassName);
 
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8MBSService: 0x%02X", pstAddIndication->sfActiveSet.u8MBSService);
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8QosParamSet: 0x%02X", pstAddIndication->sfActiveSet.u8QosParamSet);
@@ -1212,7 +1198,7 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
 		nCurClassifierCnt = MAX_CLASSIFIERS_IN_SF;
 
 	for (nIndex = 0; nIndex < nCurClassifierCnt; nIndex++)	{
-		stConvergenceSLTypes *psfCSType = NULL;
+		struct bcm_convergence_types *psfCSType = NULL;
 
 		psfCSType =  &pstAddIndication->sfActiveSet.cConvergenceSLTypes[nIndex];
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, " CCPacketClassificationRuleSI====>");
@@ -1314,7 +1300,7 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
 
 static inline ULONG RestoreSFParam(struct bcm_mini_adapter *Adapter, ULONG ulAddrSFParamSet, PUCHAR pucDestBuffer)
 {
-	UINT  nBytesToRead = sizeof(stServiceFlowParamSI);
+	UINT  nBytesToRead = sizeof(struct bcm_connect_mgr_params);
 
 	if (ulAddrSFParamSet == 0 || NULL == pucDestBuffer) {
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "Got Param address as 0!!");
@@ -1331,7 +1317,7 @@ static inline ULONG RestoreSFParam(struct bcm_mini_adapter *Adapter, ULONG ulAdd
 
 static ULONG StoreSFParam(struct bcm_mini_adapter *Adapter, PUCHAR pucSrcBuffer, ULONG ulAddrSFParamSet)
 {
-	UINT nBytesToWrite = sizeof(stServiceFlowParamSI);
+	UINT nBytesToWrite = sizeof(struct bcm_connect_mgr_params);
 	int ret = 0;
 
 	if (ulAddrSFParamSet == 0 || NULL == pucSrcBuffer)
@@ -1348,8 +1334,8 @@ static ULONG StoreSFParam(struct bcm_mini_adapter *Adapter, PUCHAR pucSrcBuffer,
 ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBuffer, UINT *puBufferLength)
 {
 	stLocalSFAddIndicationAlt *pstAddIndicationAlt = NULL;
-	stLocalSFAddIndication *pstAddIndication = NULL;
-	stLocalSFDeleteRequest *pstDeletionRequest;
+	struct bcm_add_indication *pstAddIndication = NULL;
+	struct bcm_del_request *pstDeletionRequest;
 	UINT uiSearchRuleIndex;
 	ULONG ulSFID;
 
@@ -1360,7 +1346,7 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
 	 * we can stop the further classifying the pkt for this SF.
 	 */
 	if (pstAddIndicationAlt->u8Type == DSD_REQ) {
-		pstDeletionRequest = (stLocalSFDeleteRequest *)pvBuffer;
+		pstDeletionRequest = (struct bcm_del_request *)pvBuffer;
 
 		ulSFID = ntohl(pstDeletionRequest->u32SFID);
 		uiSearchRuleIndex = SearchSfid(Adapter, ulSFID);
@@ -1379,12 +1365,12 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
 	}
 	/* For DSA_REQ, only up to "psfAuthorizedSet" parameter should be accessed by driver! */
 
-	pstAddIndication = kmalloc(sizeof(*pstAddIndication), GFP_KERNEL);
+	pstAddIndication = kmalloc(sizeof(struct bcm_add_indication), GFP_KERNEL);
 	if (pstAddIndication == NULL)
 		return 0;
 
 	/* AUTHORIZED SET */
-	pstAddIndication->psfAuthorizedSet = (stServiceFlowParamSI *)
+	pstAddIndication->psfAuthorizedSet = (struct bcm_connect_mgr_params *)
 			GetNextTargetBufferLocation(Adapter, pstAddIndicationAlt->u16TID);
 	if (!pstAddIndication->psfAuthorizedSet) {
 		kfree(pstAddIndication);
@@ -1398,10 +1384,10 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
 	}
 
 	/* this can't possibly be right */
-	pstAddIndication->psfAuthorizedSet = (stServiceFlowParamSI *)ntohl((ULONG)pstAddIndication->psfAuthorizedSet);
+	pstAddIndication->psfAuthorizedSet = (struct bcm_connect_mgr_params *)ntohl((ULONG)pstAddIndication->psfAuthorizedSet);
 
 	if (pstAddIndicationAlt->u8Type == DSA_REQ) {
-		stLocalSFAddRequest AddRequest;
+		struct bcm_add_request AddRequest;
 
 		AddRequest.u8Type = pstAddIndicationAlt->u8Type;
 		AddRequest.eConnectionDir = pstAddIndicationAlt->u8Direction;
@@ -1409,8 +1395,8 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
 		AddRequest.u16CID = pstAddIndicationAlt->u16CID;
 		AddRequest.u16VCID = pstAddIndicationAlt->u16VCID;
 		AddRequest.psfParameterSet = pstAddIndication->psfAuthorizedSet;
-		(*puBufferLength) = sizeof(stLocalSFAddRequest);
-		memcpy(pvBuffer, &AddRequest, sizeof(stLocalSFAddRequest));
+		(*puBufferLength) = sizeof(struct bcm_add_request);
+		memcpy(pvBuffer, &AddRequest, sizeof(struct bcm_add_request));
 		kfree(pstAddIndication);
 		return 1;
 	}
@@ -1426,7 +1412,7 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
 	pstAddIndication->u8CC = pstAddIndicationAlt->u8CC;
 
 	/* ADMITTED SET */
-	pstAddIndication->psfAdmittedSet = (stServiceFlowParamSI *)
+	pstAddIndication->psfAdmittedSet = (struct bcm_connect_mgr_params *)
 		GetNextTargetBufferLocation(Adapter, pstAddIndicationAlt->u16TID);
 	if (!pstAddIndication->psfAdmittedSet) {
 		kfree(pstAddIndication);
@@ -1437,10 +1423,10 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
 		return 0;
 	}
 
-	pstAddIndication->psfAdmittedSet = (stServiceFlowParamSI *)ntohl((ULONG)pstAddIndication->psfAdmittedSet);
+	pstAddIndication->psfAdmittedSet = (struct bcm_connect_mgr_params *)ntohl((ULONG)pstAddIndication->psfAdmittedSet);
 
 	/* ACTIVE SET */
-	pstAddIndication->psfActiveSet = (stServiceFlowParamSI *)
+	pstAddIndication->psfActiveSet = (struct bcm_connect_mgr_params *)
 		GetNextTargetBufferLocation(Adapter, pstAddIndicationAlt->u16TID);
 	if (!pstAddIndication->psfActiveSet) {
 		kfree(pstAddIndication);
@@ -1451,10 +1437,10 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
 		return 0;
 	}
 
-	pstAddIndication->psfActiveSet = (stServiceFlowParamSI *)ntohl((ULONG)pstAddIndication->psfActiveSet);
+	pstAddIndication->psfActiveSet = (struct bcm_connect_mgr_params *)ntohl((ULONG)pstAddIndication->psfActiveSet);
 
-	(*puBufferLength) = sizeof(stLocalSFAddIndication);
-	*(stLocalSFAddIndication *)pvBuffer = *pstAddIndication;
+	(*puBufferLength) = sizeof(struct bcm_add_indication);
+	*(struct bcm_add_indication *)pvBuffer = *pstAddIndication;
 	kfree(pstAddIndication);
 	return 1;
 }
@@ -1463,10 +1449,10 @@ static inline stLocalSFAddIndicationAlt
 *RestoreCmControlResponseMessage(register struct bcm_mini_adapter *Adapter, register PVOID pvBuffer)
 {
 	ULONG ulStatus = 0;
-	stLocalSFAddIndication *pstAddIndication = NULL;
+	struct bcm_add_indication *pstAddIndication = NULL;
 	stLocalSFAddIndicationAlt *pstAddIndicationDest = NULL;
 
-	pstAddIndication = (stLocalSFAddIndication *)(pvBuffer);
+	pstAddIndication = (struct bcm_add_indication *)(pvBuffer);
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "=====>");
 	if ((pstAddIndication->u8Type == DSD_REQ) ||
 		(pstAddIndication->u8Type == DSD_RSP) ||
@@ -1553,7 +1539,7 @@ ULONG SetUpTargetDsxBuffers(struct bcm_mini_adapter *Adapter)
 	if (Adapter->astTargetDsxBuffer[0].ulTargetDsxBuffer)
 		return 1;
 
-	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "Size of Each DSX Buffer(Also size of ServiceFlowParamSI): %zx ", sizeof(stServiceFlowParamSI));
+	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "Size of Each DSX Buffer(Also size of connection manager parameters): %zx ", sizeof(struct bcm_connect_mgr_params));
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "Reading DSX buffer From Target location %x ", DSX_MESSAGE_EXCHANGE_BUFFER);
 
 	Status = rdmalt(Adapter, DSX_MESSAGE_EXCHANGE_BUFFER, (PUINT)&ulTargetDsxBuffersBase, sizeof(UINT));
@@ -1564,7 +1550,7 @@ ULONG SetUpTargetDsxBuffers(struct bcm_mini_adapter *Adapter)
 
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "Base Address Of DSX  Target Buffer : 0x%lx", ulTargetDsxBuffersBase);
 	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL,  "Tgt Buffer is Now %lx :", ulTargetDsxBuffersBase);
-	ulCntTargetBuffers = DSX_MESSAGE_EXCHANGE_BUFFER_SIZE / sizeof(stServiceFlowParamSI);
+	ulCntTargetBuffers = DSX_MESSAGE_EXCHANGE_BUFFER_SIZE / sizeof(struct bcm_connect_mgr_params);
 
 	Adapter->ulTotalTargetBuffersAvailable =
 		ulCntTargetBuffers > MAX_TARGET_DSX_BUFFERS ?
@@ -1576,7 +1562,7 @@ ULONG SetUpTargetDsxBuffers(struct bcm_mini_adapter *Adapter)
 		Adapter->astTargetDsxBuffer[i].ulTargetDsxBuffer = ulTargetDsxBuffersBase;
 		Adapter->astTargetDsxBuffer[i].valid = 1;
 		Adapter->astTargetDsxBuffer[i].tid = 0;
-		ulTargetDsxBuffersBase += sizeof(stServiceFlowParamSI);
+		ulTargetDsxBuffersBase += sizeof(struct bcm_connect_mgr_params);
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "  Target DSX Buffer %lx setup at 0x%lx",
 				i, Adapter->astTargetDsxBuffer[i].ulTargetDsxBuffer);
 	}
@@ -1647,7 +1633,7 @@ int FreeAdapterDsxBuffer(struct bcm_mini_adapter *Adapter)
 BOOLEAN CmControlResponseMessage(struct bcm_mini_adapter *Adapter,  /* <Pointer to the Adapter structure */
 				PVOID pvBuffer /* Starting Address of the Buffer, that contains the AddIndication Data */)
 {
-	stServiceFlowParamSI *psfLocalSet = NULL;
+	struct bcm_connect_mgr_params *psfLocalSet = NULL;
 	stLocalSFAddIndicationAlt *pstAddIndication = NULL;
 	stLocalSFChangeIndicationAlt *pstChangeIndication = NULL;
 	struct bcm_leader *pLeader = NULL;
@@ -1658,7 +1644,7 @@ BOOLEAN CmControlResponseMessage(struct bcm_mini_adapter *Adapter,  /* <Pointer
 	 */
 	pstAddIndication = RestoreCmControlResponseMessage(Adapter, pvBuffer);
 	if (pstAddIndication == NULL) {
-		ClearTargetDSXBuffer(Adapter, ((stLocalSFAddIndication *)pvBuffer)->u16TID, FALSE);
+		ClearTargetDSXBuffer(Adapter, ((struct bcm_add_indication *)pvBuffer)->u16TID, FALSE);
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0, "Error in restoring Service Flow param structure from DSx message");
 		return FALSE;
 	}
@@ -1870,10 +1856,10 @@ BOOLEAN CmControlResponseMessage(struct bcm_mini_adapter *Adapter,  /* <Pointer
 		UINT uiSearchRuleIndex;
 		ULONG ulSFID;
 
-		pLeader->PLength = sizeof(stLocalSFDeleteIndication);
-		*((stLocalSFDeleteIndication *)&(Adapter->caDsxReqResp[LEADER_SIZE])) = *((stLocalSFDeleteIndication *)pstAddIndication);
+		pLeader->PLength = sizeof(struct bcm_del_indication);
+		*((struct bcm_del_indication *)&(Adapter->caDsxReqResp[LEADER_SIZE])) = *((struct bcm_del_indication *)pstAddIndication);
 
-		ulSFID = ntohl(((stLocalSFDeleteIndication *)pstAddIndication)->u32SFID);
+		ulSFID = ntohl(((struct bcm_del_indication *)pstAddIndication)->u32SFID);
 		uiSearchRuleIndex = SearchSfid(Adapter, ulSFID);
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "DSD - Removing connection %x", uiSearchRuleIndex);
 
@@ -1884,7 +1870,7 @@ BOOLEAN CmControlResponseMessage(struct bcm_mini_adapter *Adapter,  /* <Pointer
 		}
 
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "SENDING DSD RESPONSE TO MAC");
-		((stLocalSFDeleteIndication *)&(Adapter->caDsxReqResp[LEADER_SIZE]))->u8Type = DSD_RSP;
+		((struct bcm_del_indication *)&(Adapter->caDsxReqResp[LEADER_SIZE]))->u8Type = DSD_RSP;
 		CopyBufferToControlPacket(Adapter, (PVOID)Adapter->caDsxReqResp);
 	}
 	case DSD_RSP:
@@ -1927,7 +1913,7 @@ int get_dsx_sf_data_to_application(struct bcm_mini_adapter *Adapter, UINT uiSFId
 VOID OverrideServiceFlowParams(struct bcm_mini_adapter *Adapter, PUINT puiBuffer)
 {
 	B_UINT32 u32NumofSFsinMsg = ntohl(*(puiBuffer + 1));
-	stIM_SFHostNotify *pHostInfo = NULL;
+	struct bcm_stim_sfhostnotify *pHostInfo = NULL;
 	UINT uiSearchRuleIndex = 0;
 	ULONG ulSFID = 0;
 
@@ -1936,7 +1922,7 @@ VOID OverrideServiceFlowParams(struct bcm_mini_adapter *Adapter, PUINT puiBuffer
 
 	while (u32NumofSFsinMsg != 0 && u32NumofSFsinMsg < NO_OF_QUEUES) {
 		u32NumofSFsinMsg--;
-		pHostInfo = (stIM_SFHostNotify *)puiBuffer;
+		pHostInfo = (struct bcm_stim_sfhostnotify *)puiBuffer;
 		puiBuffer = (PUINT)(pHostInfo + 1);
 
 		ulSFID = ntohl(pHostInfo->SFID);

+ 19 - 35
drivers/staging/bcm/CmHost.h

@@ -35,8 +35,7 @@ typedef struct stLocalSFAddRequestAlt{
     B_UINT16                        u16VCID;
 
 
-	/// \brief structure ParameterSet
-    stServiceFlowParamSI              sfParameterSet;
+	struct bcm_connect_mgr_params sfParameterSet;
 
     //USE_MEMORY_MANAGER();
 }stLocalSFAddRequestAlt;
@@ -50,12 +49,9 @@ typedef struct stLocalSFAddIndicationAlt{
     B_UINT16                        u16CID;
     /// \brief 16bitVCID
     B_UINT16                        u16VCID;
-	/// \brief structure AuthorizedSet
-    stServiceFlowParamSI              sfAuthorizedSet;
-    /// \brief structure AdmittedSet
-    stServiceFlowParamSI              sfAdmittedSet;
-	/// \brief structure ActiveSet
-    stServiceFlowParamSI              sfActiveSet;
+	struct bcm_connect_mgr_params sfAuthorizedSet;
+	struct bcm_connect_mgr_params sfAdmittedSet;
+	struct bcm_connect_mgr_params sfActiveSet;
 
 	B_UINT8 						u8CC;	/**<  Confirmation Code*/
 	B_UINT8 						u8Padd; 	/**<  8-bit Padding */
@@ -72,12 +68,9 @@ typedef struct stLocalSFAddConfirmationAlt{
     B_UINT16                        u16CID;
     /// \brief 16bitVCID
     B_UINT16                        u16VCID;
-    /// \brief structure AuthorizedSet
-    stServiceFlowParamSI              sfAuthorizedSet;
-    /// \brief structure AdmittedSet
-    stServiceFlowParamSI              sfAdmittedSet;
-    /// \brief structure ActiveSet
-    stServiceFlowParamSI              sfActiveSet;
+	struct bcm_connect_mgr_params sfAuthorizedSet;
+	struct bcm_connect_mgr_params sfAdmittedSet;
+	struct bcm_connect_mgr_params sfActiveSet;
 }stLocalSFAddConfirmationAlt;
 
 
@@ -91,16 +84,13 @@ typedef struct stLocalSFChangeRequestAlt{
     /// \brief 16bitVCID
     B_UINT16                        u16VCID;
 	/*
-	//Pointer location at which following Service Flow param Structure can be read
-	//from the target. We get only the address location and we need to read out the
-	//entire SF param structure at the given location on target
+	//Pointer location at which following connection manager param Structure can be read
+	//from the target. We only get the address location and we need to read out the
+	//entire connection manager param structure at the given location on target
 	*/
-    /// \brief structure AuthorizedSet
-    stServiceFlowParamSI              sfAuthorizedSet;
-    /// \brief structure AdmittedSet
-    stServiceFlowParamSI              sfAdmittedSet;
-    /// \brief structure ParameterSet
-    stServiceFlowParamSI              sfActiveSet;
+	struct bcm_connect_mgr_params sfAuthorizedSet;
+	struct bcm_connect_mgr_params sfAdmittedSet;
+	struct bcm_connect_mgr_params sfActiveSet;
 
 	B_UINT8 						u8CC;	/**<  Confirmation Code*/
 	B_UINT8 						u8Padd; 	/**<  8-bit Padding */
@@ -117,12 +107,9 @@ typedef struct stLocalSFChangeConfirmationAlt{
     B_UINT16                        u16CID;
     /// \brief 16bitVCID
     B_UINT16                        u16VCID;
-    /// \brief structure AuthorizedSet
-    stServiceFlowParamSI              sfAuthorizedSet;
-    /// \brief structure AdmittedSet
-    stServiceFlowParamSI              sfAdmittedSet;
-    /// \brief structure ActiveSet
-    stServiceFlowParamSI              sfActiveSet;
+	struct bcm_connect_mgr_params sfAuthorizedSet;
+	struct bcm_connect_mgr_params sfAdmittedSet;
+	struct bcm_connect_mgr_params sfActiveSet;
 
 }stLocalSFChangeConfirmationAlt;
 
@@ -135,12 +122,9 @@ typedef struct stLocalSFChangeIndicationAlt{
     B_UINT16                        u16CID;
     /// \brief 16bitVCID
     B_UINT16                        u16VCID;
-    /// \brief structure AuthorizedSet
-    stServiceFlowParamSI              sfAuthorizedSet;
-    /// \brief structure AdmittedSet
-    stServiceFlowParamSI              sfAdmittedSet;
-    /// \brief structure ActiveSet
-    stServiceFlowParamSI              sfActiveSet;
+	struct bcm_connect_mgr_params sfAuthorizedSet;
+	struct bcm_connect_mgr_params sfAdmittedSet;
+	struct bcm_connect_mgr_params sfActiveSet;
 
 	B_UINT8 						u8CC;	/**<  Confirmation Code*/
 	B_UINT8 						u8Padd; 	/**<  8-bit Padding */

+ 13 - 4
drivers/staging/bcm/InterfaceInit.c

@@ -8,6 +8,7 @@ static struct usb_device_id InterfaceUsbtable[] = {
 	{ USB_DEVICE(BCM_USB_VENDOR_ID_ZTE, BCM_USB_PRODUCT_ID_226) },
 	{ USB_DEVICE(BCM_USB_VENDOR_ID_FOXCONN, BCM_USB_PRODUCT_ID_1901) },
 	{ USB_DEVICE(BCM_USB_VENDOR_ID_ZTE, BCM_USB_PRODUCT_ID_ZTE_TU25) },
+	{ USB_DEVICE(BCM_USB_VENDOR_ID_ZTE, BCM_USB_PRODUCT_ID_ZTE_226) },
 	{ }
 };
 MODULE_DEVICE_TABLE(usb, InterfaceUsbtable);
@@ -669,16 +670,24 @@ struct class *bcm_class;
 
 static __init int bcm_init(void)
 {
-	printk(KERN_INFO "%s: %s, %s\n", DRV_NAME, DRV_DESCRIPTION, DRV_VERSION);
-	printk(KERN_INFO "%s\n", DRV_COPYRIGHT);
+	int retval;
+
+	pr_info("%s: %s, %s\n", DRV_NAME, DRV_DESCRIPTION, DRV_VERSION);
+	pr_info("%s\n", DRV_COPYRIGHT);
 
 	bcm_class = class_create(THIS_MODULE, DRV_NAME);
 	if (IS_ERR(bcm_class)) {
-		printk(KERN_ERR DRV_NAME ": could not create class\n");
+		pr_err(DRV_NAME ": could not create class\n");
 		return PTR_ERR(bcm_class);
 	}
 
-	return usb_register(&usbbcm_driver);
+	retval = usb_register(&usbbcm_driver);
+	if (retval < 0) {
+		pr_err(DRV_NAME ": could not register usb driver\n");
+		class_destroy(bcm_class);
+		return retval;
+	}
+	return 0;
 }
 
 static __exit void bcm_exit(void)

+ 16 - 17
drivers/staging/bcm/InterfaceInit.h

@@ -1,27 +1,26 @@
 #ifndef _INTERFACE_INIT_H
 #define _INTERFACE_INIT_H
 
-#define BCM_USB_VENDOR_ID_T3 	0x198f
-#define BCM_USB_VENDOR_ID_FOXCONN       0x0489
-#define BCM_USB_VENDOR_ID_ZTE   0x19d2
+#define BCM_USB_VENDOR_ID_T3	0x198f
+#define BCM_USB_VENDOR_ID_FOXCONN	0x0489
+#define BCM_USB_VENDOR_ID_ZTE	0x19d2
 
-#define BCM_USB_PRODUCT_ID_T3 	0x0300
-#define BCM_USB_PRODUCT_ID_T3B 	0x0210
-#define BCM_USB_PRODUCT_ID_T3L 	0x0220
-#define BCM_USB_PRODUCT_ID_SM250 	0xbccd
-#define BCM_USB_PRODUCT_ID_SYM  0x15E
-#define BCM_USB_PRODUCT_ID_1901 0xe017
-#define BCM_USB_PRODUCT_ID_226  0x0132
-#define BCM_USB_PRODUCT_ID_ZTE_TU25 0x0007
+#define BCM_USB_PRODUCT_ID_T3	0x0300
+#define BCM_USB_PRODUCT_ID_T3B	0x0210
+#define BCM_USB_PRODUCT_ID_T3L	0x0220
+#define BCM_USB_PRODUCT_ID_SM250	0xbccd
+#define BCM_USB_PRODUCT_ID_SYM	0x15E
+#define BCM_USB_PRODUCT_ID_1901	0xe017
+#define BCM_USB_PRODUCT_ID_226	0x0132 /* not sure if this is valid */
+#define BCM_USB_PRODUCT_ID_ZTE_226 0x172
+#define BCM_USB_PRODUCT_ID_ZTE_TU25	0x0007
 
-#define BCM_USB_MINOR_BASE 		192
+#define BCM_USB_MINOR_BASE	192
 
+int InterfaceInitialize(void);
 
-INT InterfaceInitialize(void);
+int InterfaceExit(void);
 
-INT InterfaceExit(void);
-
-INT usbbcm_worker_thread(PS_INTERFACE_ADAPTER psIntfAdapter);
+int usbbcm_worker_thread(PS_INTERFACE_ADAPTER psIntfAdapter);
 
 #endif
-

+ 1 - 1
drivers/staging/bcm/Kconfig

@@ -1,6 +1,6 @@
 config BCM_WIMAX
        tristate "Beceem BCS200/BCS220-3 and BCSM250 wimax support"
-       depends on USB && NET && EXPERIMENTAL
+       depends on USB && NET
        default N
        help
          This is an experimental driver for the Beceem WIMAX chipset used

+ 4 - 1
drivers/staging/bcm/Misc.c

@@ -752,7 +752,10 @@ VOID DumpPackInfo(struct bcm_mini_adapter *Adapter)
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "AuthzSet: %x\n", Adapter->PackInfo[uiLoopIndex].bAuthorizedSet);
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "ClassifyPrority: %x\n", Adapter->PackInfo[uiLoopIndex].bClassifierPriority);
 		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "uiMaxLatency: %x\n", Adapter->PackInfo[uiLoopIndex].uiMaxLatency);
-		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "ServiceClassName: %x %x %x %x\n", Adapter->PackInfo[uiLoopIndex].ucServiceClassName[0], Adapter->PackInfo[uiLoopIndex].ucServiceClassName[1], Adapter->PackInfo[uiLoopIndex].ucServiceClassName[2], Adapter->PackInfo[uiLoopIndex].ucServiceClassName[3]);
+		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO,
+				DBG_LVL_ALL, "ServiceClassName: %*ph\n",
+				4, Adapter->PackInfo[uiLoopIndex].
+					    ucServiceClassName);
 /* BCM_DEBUG_PRINT (Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "bHeaderSuppressionEnabled :%X\n", Adapter->PackInfo[uiLoopIndex].bHeaderSuppressionEnabled);
  * BCM_DEBUG_PRINT (Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "uiTotalTxBytes:%X\n", Adapter->PackInfo[uiLoopIndex].uiTotalTxBytes);
  * BCM_DEBUG_PRINT (Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "uiTotalRxBytes:%X\n", Adapter->PackInfo[uiLoopIndex].uiTotalRxBytes);

+ 2 - 2
drivers/staging/bcm/PHSModule.c

@@ -66,7 +66,7 @@ Input parameters:		IN struct bcm_mini_adapter *Adapter         - Miniport Adapte
 						BOOLEAN bHeaderSuppressionEnabled - indicates if header suprression is enabled for SF.
 
 Return:					STATUS_SUCCESS - If the send was successful.
-						Other          - If an error occured.
+						Other  - If an error occurred.
 */
 
 int PHSTransmit(struct bcm_mini_adapter *Adapter,
@@ -346,7 +346,7 @@ int phs_init(PPHS_DEVICE_EXTENSION pPhsdeviceExtension, struct bcm_mini_adapter
 
 
 
-	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\n phs_init Successfull");
+	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\n phs_init Successful");
 	return STATUS_SUCCESS;
 }
 

+ 1 - 1
drivers/staging/bcm/Prototypes.h

@@ -95,7 +95,7 @@ void beceem_parse_target_struct(struct bcm_mini_adapter *Adapter);
 int bcm_ioctl_fw_download(struct bcm_mini_adapter *Adapter, struct bcm_firmware_info *psFwInfo);
 
 void CopyMIBSExtendedSFParameters(struct bcm_mini_adapter *Adapter,
-		CServiceFlowParamSI *psfLocalSet, UINT uiSearchRuleIndex);
+		struct bcm_connect_mgr_params *psfLocalSet, UINT uiSearchRuleIndex);
 
 VOID ResetCounters(struct bcm_mini_adapter *Adapter);
 

+ 106 - 128
drivers/staging/bcm/Transmit.c

@@ -1,162 +1,149 @@
 /**
-@file Transmit.c
-@defgroup tx_functions Transmission
-@section Queueing
-@dot
-digraph transmit1 {
-node[shape=box]
-edge[weight=5;color=red]
-
-bcm_transmit->GetPacketQueueIndex[label="IP Packet"]
-GetPacketQueueIndex->IpVersion4[label="IPV4"]
-GetPacketQueueIndex->IpVersion6[label="IPV6"]
-}
-
-@enddot
-
-@section De-Queueing
-@dot
-digraph transmit2 {
-node[shape=box]
-edge[weight=5;color=red]
-interrupt_service_thread->transmit_packets
-tx_pkt_hdler->transmit_packets
-transmit_packets->CheckAndSendPacketFromIndex
-transmit_packets->UpdateTokenCount
-CheckAndSendPacketFromIndex->PruneQueue
-CheckAndSendPacketFromIndex->IsPacketAllowedForFlow
-CheckAndSendPacketFromIndex->SendControlPacket[label="control pkt"]
-SendControlPacket->bcm_cmd53
-CheckAndSendPacketFromIndex->SendPacketFromQueue[label="data pkt"]
-SendPacketFromQueue->SetupNextSend->bcm_cmd53
-}
-@enddot
-*/
+ * @file Transmit.c
+ * @defgroup tx_functions Transmission
+ * @section Queueing
+ * @dot
+ * digraph transmit1 {
+ * node[shape=box]
+ * edge[weight=5;color=red]
+ *
+ * bcm_transmit->GetPacketQueueIndex[label="IP Packet"]
+ * GetPacketQueueIndex->IpVersion4[label="IPV4"]
+ * GetPacketQueueIndex->IpVersion6[label="IPV6"]
+ * }
+ *
+ * @enddot
+ *
+ * @section De-Queueing
+ * @dot
+ * digraph transmit2 {
+ * node[shape=box]
+ * edge[weight=5;color=red]
+ * interrupt_service_thread->transmit_packets
+ * tx_pkt_hdler->transmit_packets
+ * transmit_packets->CheckAndSendPacketFromIndex
+ * transmit_packets->UpdateTokenCount
+ * CheckAndSendPacketFromIndex->PruneQueue
+ * CheckAndSendPacketFromIndex->IsPacketAllowedForFlow
+ * CheckAndSendPacketFromIndex->SendControlPacket[label="control pkt"]
+ * SendControlPacket->bcm_cmd53
+ * CheckAndSendPacketFromIndex->SendPacketFromQueue[label="data pkt"]
+ * SendPacketFromQueue->SetupNextSend->bcm_cmd53
+ * }
+ * @enddot
+ */
 
 #include "headers.h"
 
-
 /**
-@ingroup ctrl_pkt_functions
-This function dispatches control packet to the h/w interface
-@return zero(success) or -ve value(failure)
-*/
-INT SendControlPacket(struct bcm_mini_adapter *Adapter, char *pControlPacket)
+ * @ingroup ctrl_pkt_functions
+ * This function dispatches control packet to the h/w interface
+ * @return zero(success) or -ve value(failure)
+ */
+int SendControlPacket(struct bcm_mini_adapter *Adapter, char *pControlPacket)
 {
 	struct bcm_leader *PLeader = (struct bcm_leader *)pControlPacket;
 
-	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Tx");
-	if(!pControlPacket || !Adapter)
-	{
-		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Got NULL Control Packet or Adapter");
+	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Tx");
+	if (!pControlPacket || !Adapter) {
+		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Got NULL Control Packet or Adapter");
+		return STATUS_FAILURE;
+	}
+	if ((atomic_read(&Adapter->CurrNumFreeTxDesc) <
+			((PLeader->PLength-1)/MAX_DEVICE_DESC_SIZE)+1))	{
+		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "NO FREE DESCRIPTORS TO SEND CONTROL PACKET");
 		return STATUS_FAILURE;
 	}
-	if((atomic_read( &Adapter->CurrNumFreeTxDesc ) <
-		((PLeader->PLength-1)/MAX_DEVICE_DESC_SIZE)+1))
-    {
-    	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "NO FREE DESCRIPTORS TO SEND CONTROL PACKET");
-        return STATUS_FAILURE;
-    }
 
 	/* Update the netdevice statistics */
 	/* Dump Packet  */
-	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader Status: %x", PLeader->Status);
-	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader VCID: %x",PLeader->Vcid);
-	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader Length: %x",PLeader->PLength);
-	if(Adapter->device_removed)
+	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader Status: %x", PLeader->Status);
+	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader VCID: %x", PLeader->Vcid);
+	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader Length: %x", PLeader->PLength);
+	if (Adapter->device_removed)
 		return 0;
 
 	if (netif_msg_pktdata(Adapter))
 		print_hex_dump(KERN_DEBUG, PFX "tx control: ", DUMP_PREFIX_NONE,
-			       16, 1, pControlPacket, PLeader->PLength + LEADER_SIZE, 0);
+			16, 1, pControlPacket, PLeader->PLength + LEADER_SIZE, 0);
 
 	Adapter->interface_transmit(Adapter->pvInterfaceAdapter,
-					pControlPacket, (PLeader->PLength + LEADER_SIZE));
+				pControlPacket, (PLeader->PLength + LEADER_SIZE));
 
 	atomic_dec(&Adapter->CurrNumFreeTxDesc);
-	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "<=========");
+	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "<=========");
 	return STATUS_SUCCESS;
 }
 
 /**
-@ingroup tx_functions
-This function despatches the IP packets with the given vcid
-to the target via the host h/w interface.
-@return  zero(success) or -ve value(failure)
-*/
-INT SetupNextSend(struct bcm_mini_adapter *Adapter,  struct sk_buff *Packet, USHORT Vcid)
+ * @ingroup tx_functions
+ * This function despatches the IP packets with the given vcid
+ * to the target via the host h/w interface.
+ * @return  zero(success) or -ve value(failure)
+ */
+int SetupNextSend(struct bcm_mini_adapter *Adapter,  struct sk_buff *Packet, USHORT Vcid)
 {
-	int		status=0;
-	BOOLEAN bHeaderSupressionEnabled = FALSE;
-	B_UINT16            uiClassifierRuleID;
+	int	status = 0;
+	BOOLEAN	bHeaderSupressionEnabled = FALSE;
+	B_UINT16 uiClassifierRuleID;
 	u16	QueueIndex = skb_get_queue_mapping(Packet);
-	struct bcm_leader Leader={0};
+	struct bcm_leader Leader = {0};
 
-	if(Packet->len > MAX_DEVICE_DESC_SIZE)
-	{
+	if (Packet->len > MAX_DEVICE_DESC_SIZE) {
 		status = STATUS_FAILURE;
 		goto errExit;
 	}
 
 	/* Get the Classifier Rule ID */
-	uiClassifierRuleID = *((UINT32*) (Packet->cb)+SKB_CB_CLASSIFICATION_OFFSET);
+	uiClassifierRuleID = *((UINT32 *) (Packet->cb) + SKB_CB_CLASSIFICATION_OFFSET);
 
 	bHeaderSupressionEnabled = Adapter->PackInfo[QueueIndex].bHeaderSuppressionEnabled
 		& Adapter->bPHSEnabled;
 
-	if(Adapter->device_removed)
-		{
+	if (Adapter->device_removed) {
 		status = STATUS_FAILURE;
 		goto errExit;
-		}
+	}
 
 	status = PHSTransmit(Adapter, &Packet, Vcid, uiClassifierRuleID, bHeaderSupressionEnabled,
-							(UINT *)&Packet->len, Adapter->PackInfo[QueueIndex].bEthCSSupport);
+			(UINT *)&Packet->len, Adapter->PackInfo[QueueIndex].bEthCSSupport);
 
-	if(status != STATUS_SUCCESS)
-	{
-		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "PHS Transmit failed..\n");
+	if (status != STATUS_SUCCESS) {
+		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "PHS Transmit failed..\n");
 		goto errExit;
 	}
 
-	Leader.Vcid	= Vcid;
+	Leader.Vcid = Vcid;
 
-	if(TCP_ACK == *((UINT32*) (Packet->cb) + SKB_CB_TCPACK_OFFSET ))
+	if (TCP_ACK == *((UINT32 *) (Packet->cb) + SKB_CB_TCPACK_OFFSET))
 		Leader.Status = LEADER_STATUS_TCP_ACK;
 	else
 		Leader.Status = LEADER_STATUS;
 
-	if(Adapter->PackInfo[QueueIndex].bEthCSSupport)
-	{
+	if (Adapter->PackInfo[QueueIndex].bEthCSSupport) {
 		Leader.PLength = Packet->len;
-		if(skb_headroom(Packet) < LEADER_SIZE)
-        {
-			if((status = skb_cow(Packet,LEADER_SIZE)))
-			{
-				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL,"bcm_transmit : Failed To Increase headRoom\n");
+		if (skb_headroom(Packet) < LEADER_SIZE) {
+			status = skb_cow(Packet, LEADER_SIZE);
+			if (status) {
+				BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "bcm_transmit : Failed To Increase headRoom\n");
 				goto errExit;
 			}
 		}
 		skb_push(Packet, LEADER_SIZE);
 		memcpy(Packet->data, &Leader, LEADER_SIZE);
-	}
-	else
-	{
+	} else {
 		Leader.PLength = Packet->len - ETH_HLEN;
 		memcpy((struct bcm_leader *)skb_pull(Packet, (ETH_HLEN - LEADER_SIZE)), &Leader, LEADER_SIZE);
 	}
 
 	status = Adapter->interface_transmit(Adapter->pvInterfaceAdapter,
-			Packet->data, (Leader.PLength + LEADER_SIZE));
-	if(status)
-	{
+					Packet->data, (Leader.PLength + LEADER_SIZE));
+	if (status) {
 		++Adapter->dev->stats.tx_errors;
 		if (netif_msg_tx_err(Adapter))
 			pr_info(PFX "%s: transmit error %d\n", Adapter->dev->name,
 				status);
-	}
-	else
-	{
+	} else {
 		struct net_device_stats *netstats = &Adapter->dev->stats;
 		Adapter->PackInfo[QueueIndex].uiTotalTxBytes += Leader.PLength;
 
@@ -175,7 +162,6 @@ INT SetupNextSend(struct bcm_mini_adapter *Adapter,  struct sk_buff *Packet, USH
 	atomic_dec(&Adapter->CurrNumFreeTxDesc);
 
 errExit:
-
 	dev_kfree_skb(Packet);
 	return status;
 }
@@ -188,73 +174,65 @@ static int tx_pending(struct bcm_mini_adapter *Adapter)
 }
 
 /**
-@ingroup tx_functions
-Transmit thread
-*/
-int tx_pkt_handler(struct bcm_mini_adapter *Adapter  /**< pointer to adapter object*/
-				)
+ * @ingroup tx_functions
+ * Transmit thread
+ */
+int tx_pkt_handler(struct bcm_mini_adapter *Adapter /**< pointer to adapter object*/)
 {
 	int status = 0;
 
-	while(! kthread_should_stop()) {
+	while (!kthread_should_stop()) {
 		/* FIXME - the timeout looks like workaround for racey usage of TxPktAvail */
-		if(Adapter->LinkUpStatus)
+		if (Adapter->LinkUpStatus)
 			wait_event_timeout(Adapter->tx_packet_wait_queue,
-					   tx_pending(Adapter), msecs_to_jiffies(10));
+					tx_pending(Adapter), msecs_to_jiffies(10));
 		else
 			wait_event_interruptible(Adapter->tx_packet_wait_queue,
-						 tx_pending(Adapter));
+						tx_pending(Adapter));
 
 		if (Adapter->device_removed)
 			break;
 
-		if(Adapter->downloadDDR == 1)
-		{
-			Adapter->downloadDDR +=1;
+		if (Adapter->downloadDDR == 1) {
+			Adapter->downloadDDR += 1;
 			status = download_ddr_settings(Adapter);
-			if(status)
+			if (status)
 				pr_err(PFX "DDR DOWNLOAD FAILED! %d\n", status);
 			continue;
 		}
 
-		//Check end point for halt/stall.
-		if(Adapter->bEndPointHalted == TRUE)
-		{
+		/* Check end point for halt/stall. */
+		if (Adapter->bEndPointHalted == TRUE) {
 			Bcm_clear_halt_of_endpoints(Adapter);
 			Adapter->bEndPointHalted = FALSE;
 			StartInterruptUrb((PS_INTERFACE_ADAPTER)(Adapter->pvInterfaceAdapter));
 		}
 
-		if(Adapter->LinkUpStatus && !Adapter->IdleMode)
-		{
-			if(atomic_read(&Adapter->TotalPacketCount))
-			{
+		if (Adapter->LinkUpStatus && !Adapter->IdleMode) {
+			if (atomic_read(&Adapter->TotalPacketCount))
 				update_per_sf_desc_cnts(Adapter);
-			}
 		}
 
-		if( atomic_read(&Adapter->CurrNumFreeTxDesc) &&
+		if (atomic_read(&Adapter->CurrNumFreeTxDesc) &&
 			Adapter->LinkStatus == SYNC_UP_REQUEST &&
-			!Adapter->bSyncUpRequestSent)
-		{
-			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Calling LinkMessage");
+			!Adapter->bSyncUpRequestSent) {
+
+			BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Calling LinkMessage");
 			LinkMessage(Adapter);
 		}
 
-		if((Adapter->IdleMode || Adapter->bShutStatus) && atomic_read(&Adapter->TotalPacketCount))
-		{
-				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Device in Low Power mode...waking up");
-    			Adapter->usIdleModePattern = ABORT_IDLE_MODE;
-				Adapter->bWakeUpDevice = TRUE;
-				wake_up(&Adapter->process_rx_cntrlpkt);
+		if ((Adapter->IdleMode || Adapter->bShutStatus) && atomic_read(&Adapter->TotalPacketCount)) {
+			BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Device in Low Power mode...waking up");
+			Adapter->usIdleModePattern = ABORT_IDLE_MODE;
+			Adapter->bWakeUpDevice = TRUE;
+			wake_up(&Adapter->process_rx_cntrlpkt);
 		}
 
 		transmit_packets(Adapter);
-
 		atomic_set(&Adapter->TxPktAvail, 0);
 	}
 
-	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Exiting the tx thread..\n");
+	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Exiting the tx thread..\n");
 	Adapter->transmit_packet_thread = NULL;
 	return 0;
 }

+ 296 - 408
drivers/staging/bcm/cntrl_SignalingInterface.h

@@ -1,423 +1,311 @@
 #ifndef CNTRL_SIGNALING_INTERFACE_
 #define CNTRL_SIGNALING_INTERFACE_
 
-
-
-
-#define DSA_REQ 11
-#define DSA_RSP 12
-#define DSA_ACK 13
-#define DSC_REQ 14
-#define DSC_RSP 15
-#define DSC_ACK 16
-#define DSD_REQ 17
-#define DSD_RSP 18
-#define DSD_ACK 19
-#define MAX_CLASSIFIERS_IN_SF  4
-
-
-#define MAX_STRING_LEN 20
-#define MAX_PHS_LENGTHS 255
-#define VENDOR_PHS_PARAM_LENGTH 10
-#define MAX_NUM_ACTIVE_BS 10
-#define AUTH_TOKEN_LENGTH	10
-#define NUM_HARQ_CHANNELS	16	//Changed from 10 to 16 to accommodate all HARQ channels
-#define VENDOR_CLASSIFIER_PARAM_LENGTH 1 //Changed the size to 1 byte since we dnt use it
-#define  VENDOR_SPECIF_QOS_PARAM 1
-#define VENDOR_PHS_PARAM_LENGTH	10
-#define MBS_CONTENTS_ID_LENGTH	10
-#define GLOBAL_SF_CLASSNAME_LENGTH 6
-
-#define TYPE_OF_SERVICE_LENGTH				3
-#define IP_MASKED_SRC_ADDRESS_LENGTH			32
-#define IP_MASKED_DEST_ADDRESS_LENGTH		32
-#define PROTOCOL_SRC_PORT_RANGE_LENGTH		4
-#define PROTOCOL_DEST_PORT_RANGE_LENGTH		4
-#define ETHERNET_DEST_MAC_ADDR_LENGTH		12
-#define ETHERNET_SRC_MAC_ADDR_LENGTH		12
-#define NUM_ETHERTYPE_BYTES  3
-#define NUM_IPV6_FLOWLABLE_BYTES 3
-
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////structure Definitions///////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-/// \brief class cCPacketClassificationRule
-struct _stCPacketClassificationRuleSI{
-
-	/**  16bit UserPriority Of The Service Flow*/
-    B_UINT16                        u16UserPriority;
-	/**  16bit VLANID Of The Service Flow*/
-    B_UINT16                        u16VLANID;
-	/**  16bit Packet Classification RuleIndex Of The Service Flow*/
-    B_UINT16                        u16PacketClassificationRuleIndex;
-	/**  8bit Classifier Rule Priority Of The Service Flow*/
-    B_UINT8                         u8ClassifierRulePriority;
-	/**  Length of IP TypeOfService field*/
-	B_UINT8                         u8IPTypeOfServiceLength;
-	/**  3bytes IP TypeOfService */
-    B_UINT8                         u8IPTypeOfService[TYPE_OF_SERVICE_LENGTH];
-	/** Protocol used in classification of Service Flow*/
-    B_UINT8                         u8Protocol;
-	/**  Length of IP Masked Source Address */
-    B_UINT8                         u8IPMaskedSourceAddressLength;
-	/**  IP Masked Source Address used in classification for the Service Flow*/
-    B_UINT8                         u8IPMaskedSourceAddress[IP_MASKED_SRC_ADDRESS_LENGTH];
-	/**  Length of IP Destination Address */
-    B_UINT8                         u8IPDestinationAddressLength;
-	/**  IP Destination Address used in classification for the Service Flow*/
-    B_UINT8                         u8IPDestinationAddress[IP_MASKED_DEST_ADDRESS_LENGTH];
-	/** Length of Protocol Source Port Range */
-    B_UINT8                         u8ProtocolSourcePortRangeLength;
-	/**  Protocol Source Port Range used in the Service Flow*/
-    B_UINT8                         u8ProtocolSourcePortRange[PROTOCOL_SRC_PORT_RANGE_LENGTH];
-	/** Length of Protocol Dest Port Range */
-    B_UINT8                         u8ProtocolDestPortRangeLength;
-	/**  Protocol Dest Port Range used in the Service Flow*/
-    B_UINT8                         u8ProtocolDestPortRange[PROTOCOL_DEST_PORT_RANGE_LENGTH];
-	/** Length of Ethernet Destination MAC Address  */
-    B_UINT8                         u8EthernetDestMacAddressLength;
-	/**  Ethernet Destination MAC Address  used in classification of the Service Flow*/
-    B_UINT8                         u8EthernetDestMacAddress[ETHERNET_DEST_MAC_ADDR_LENGTH];
-	/** Length of Ethernet Source MAC Address  */
-    B_UINT8                         u8EthernetSourceMACAddressLength;
-	/**  Ethernet Source MAC Address  used in classification of the Service Flow*/
-    B_UINT8                         u8EthernetSourceMACAddress[ETHERNET_SRC_MAC_ADDR_LENGTH];
-	/**  Length of Ethertype */
-	B_UINT8                         u8EthertypeLength;
-	/**  3bytes Ethertype Of The Service Flow*/
-    B_UINT8                         u8Ethertype[NUM_ETHERTYPE_BYTES];
-	/**  8bit Associated PHSI Of The Service Flow*/
-    B_UINT8                         u8AssociatedPHSI;
-	/** Length of Vendor Specific Classifier Param length Of The Service Flow*/
-    B_UINT8                         u8VendorSpecificClassifierParamLength;
-	/**  Vendor Specific Classifier Param Of The Service Flow*/
-    B_UINT8                         u8VendorSpecificClassifierParam[VENDOR_CLASSIFIER_PARAM_LENGTH];
-    /** Length Of IPv6 Flow Lable of the Service Flow*/
-    B_UINT8                         u8IPv6FlowLableLength;
-	/**  IPv6 Flow Lable Of The Service Flow*/
-    B_UINT8                         u8IPv6FlowLable[NUM_IPV6_FLOWLABLE_BYTES];
-	/**  Action associated with the classifier rule*/
-    B_UINT8							u8ClassifierActionRule;
-    B_UINT16							u16ValidityBitMap;
+#define DSA_REQ			11
+#define DSA_RSP			12
+#define DSA_ACK			13
+#define DSC_REQ			14
+#define DSC_RSP			15
+#define DSC_ACK			16
+#define DSD_REQ			17
+#define DSD_RSP			18
+#define DSD_ACK			19
+#define MAX_CLASSIFIERS_IN_SF	4
+
+#define MAX_STRING_LEN			20
+#define MAX_PHS_LENGTHS			255
+#define VENDOR_PHS_PARAM_LENGTH		10
+#define MAX_NUM_ACTIVE_BS		10
+#define AUTH_TOKEN_LENGTH		10
+#define NUM_HARQ_CHANNELS		16 /* Changed from 10 to 16 to accommodate all HARQ channels */
+#define VENDOR_CLASSIFIER_PARAM_LENGTH	1  /* Changed the size to 1 byte since we dnt use it */
+#define  VENDOR_SPECIF_QOS_PARAM	1
+#define VENDOR_PHS_PARAM_LENGTH		10
+#define MBS_CONTENTS_ID_LENGTH		10
+#define GLOBAL_SF_CLASSNAME_LENGTH	6
+
+#define TYPE_OF_SERVICE_LENGTH		3
+#define IP_MASKED_SRC_ADDRESS_LENGTH	32
+#define IP_MASKED_DEST_ADDRESS_LENGTH	32
+#define PROTOCOL_SRC_PORT_RANGE_LENGTH	4
+#define PROTOCOL_DEST_PORT_RANGE_LENGTH	4
+#define ETHERNET_DEST_MAC_ADDR_LENGTH	12
+#define ETHERNET_SRC_MAC_ADDR_LENGTH	12
+#define NUM_ETHERTYPE_BYTES		3
+#define NUM_IPV6_FLOWLABLE_BYTES	3
+
+struct bcm_packet_class_rules {
+	/* 16bit UserPriority Of The Service Flow */
+	B_UINT16 u16UserPriority;
+	/* 16bit VLANID Of The Service Flow */
+	B_UINT16 u16VLANID;
+	/* 16bit Packet Classification RuleIndex Of The Service Flow */
+	B_UINT16 u16PacketClassificationRuleIndex;
+	/* 8bit Classifier Rule Priority Of The Service Flow */
+	B_UINT8 u8ClassifierRulePriority;
+	/* Length of IP TypeOfService field */
+	B_UINT8 u8IPTypeOfServiceLength;
+	/* 3bytes IP TypeOfService */
+	B_UINT8 u8IPTypeOfService[TYPE_OF_SERVICE_LENGTH];
+	/* Protocol used in classification of Service Flow */
+	B_UINT8 u8Protocol;
+	/* Length of IP Masked Source Address */
+	B_UINT8 u8IPMaskedSourceAddressLength;
+	/* IP Masked Source Address used in classification for the Service Flow */
+	B_UINT8 u8IPMaskedSourceAddress[IP_MASKED_SRC_ADDRESS_LENGTH];
+	/* Length of IP Destination Address */
+	B_UINT8 u8IPDestinationAddressLength;
+	/* IP Destination Address used in classification for the Service Flow */
+	B_UINT8 u8IPDestinationAddress[IP_MASKED_DEST_ADDRESS_LENGTH];
+	/* Length of Protocol Source Port Range */
+	B_UINT8 u8ProtocolSourcePortRangeLength;
+	/* Protocol Source Port Range used in the Service Flow */
+	B_UINT8 u8ProtocolSourcePortRange[PROTOCOL_SRC_PORT_RANGE_LENGTH];
+	/* Length of Protocol Dest Port Range */
+	B_UINT8 u8ProtocolDestPortRangeLength;
+	/* Protocol Dest Port Range used in the Service Flow */
+	B_UINT8 u8ProtocolDestPortRange[PROTOCOL_DEST_PORT_RANGE_LENGTH];
+	/* Length of Ethernet Destination MAC Address */
+	B_UINT8 u8EthernetDestMacAddressLength;
+	/* Ethernet Destination MAC Address  used in classification of the Service Flow */
+	B_UINT8 u8EthernetDestMacAddress[ETHERNET_DEST_MAC_ADDR_LENGTH];
+	/* Length of Ethernet Source MAC Address */
+	B_UINT8 u8EthernetSourceMACAddressLength;
+	/* Ethernet Source MAC Address  used in classification of the Service Flow */
+	B_UINT8 u8EthernetSourceMACAddress[ETHERNET_SRC_MAC_ADDR_LENGTH];
+	/* Length of Ethertype */
+	B_UINT8 u8EthertypeLength;
+	/* 3bytes Ethertype Of The Service Flow */
+	B_UINT8 u8Ethertype[NUM_ETHERTYPE_BYTES];
+	/* 8bit Associated PHSI Of The Service Flow */
+	B_UINT8 u8AssociatedPHSI;
+	/* Length of Vendor Specific Classifier Param length Of The Service Flow */
+	B_UINT8 u8VendorSpecificClassifierParamLength;
+	/* Vendor Specific Classifier Param Of The Service Flow */
+	B_UINT8 u8VendorSpecificClassifierParam[VENDOR_CLASSIFIER_PARAM_LENGTH];
+	/* Length Of IPv6 Flow Lable of the Service Flow */
+	B_UINT8 u8IPv6FlowLableLength;
+	/* IPv6 Flow Lable Of The Service Flow */
+	B_UINT8 u8IPv6FlowLable[NUM_IPV6_FLOWLABLE_BYTES];
+	/* Action associated with the classifier rule */
+	B_UINT8 u8ClassifierActionRule;
+	B_UINT16 u16ValidityBitMap;
 };
-typedef struct _stCPacketClassificationRuleSI CCPacketClassificationRuleSI,stCPacketClassificationRuleSI, *pstCPacketClassificationRuleSI;
-
-/// \brief class CPhsRuleSI
-typedef struct _stPhsRuleSI {
-	/**  8bit PHS Index Of The Service Flow*/
-    B_UINT8                         u8PHSI;
-	/**  PHSF Length Of The Service Flow*/
-    B_UINT8                         u8PHSFLength;
-    /** String of bytes containing header information to be suppressed by the sending CS and reconstructed by the receiving CS*/
-    B_UINT8                         u8PHSF[MAX_PHS_LENGTHS];
-	/**  PHSM Length Of The Service Flow*/
-    B_UINT8                         u8PHSMLength;
-	/**  PHS Mask for the SF*/
-    B_UINT8                         u8PHSM[MAX_PHS_LENGTHS];
-	/**  8bit Total number of bytes to be suppressed for the Service Flow*/
-    B_UINT8                         u8PHSS;
-	/**  8bit Indicates whether or not Packet Header contents need to be verified prior to suppression */
-    B_UINT8                         u8PHSV;
-	/**  Vendor Specific PHS param Length Of The Service Flow*/
-    B_UINT8                         u8VendorSpecificPHSParamsLength;
-	/**  Vendor Specific PHS param Of The Service Flow*/
-    B_UINT8                         u8VendorSpecificPHSParams[VENDOR_PHS_PARAM_LENGTH];
-
-	B_UINT8                         u8Padding[2];
-}stPhsRuleSI,*pstPhsRuleSI;
-typedef stPhsRuleSI CPhsRuleSI;
 
-/// \brief structure cConvergenceSLTypes
-struct _stConvergenceSLTypes{
-	/**  8bit Phs Classfier Action Of The Service Flow*/
-    B_UINT8                         u8ClassfierDSCAction;
-	/**  8bit Phs DSC Action Of The Service Flow*/
-    B_UINT8                         u8PhsDSCAction;
-	/**   16bit Padding */
-    B_UINT8                         u8Padding[2];
-    /// \brief class cCPacketClassificationRule
-    stCPacketClassificationRuleSI     cCPacketClassificationRule;
-    /// \brief class CPhsRuleSI
-     struct _stPhsRuleSI		cPhsRule;
+struct bcm_phs_rules {
+	/* 8bit PHS Index Of The Service Flow */
+	B_UINT8 u8PHSI;
+	/* PHSF Length Of The Service Flow */
+	B_UINT8 u8PHSFLength;
+	/* String of bytes containing header information to be suppressed by the sending CS and reconstructed by the receiving CS */
+	B_UINT8 u8PHSF[MAX_PHS_LENGTHS];
+	/* PHSM Length Of The Service Flow */
+	B_UINT8 u8PHSMLength;
+	/* PHS Mask for the SF */
+	B_UINT8 u8PHSM[MAX_PHS_LENGTHS];
+	/* 8bit Total number of bytes to be suppressed for the Service Flow */
+	B_UINT8 u8PHSS;
+	/* 8bit Indicates whether or not Packet Header contents need to be verified prior to suppression */
+	B_UINT8 u8PHSV;
+	/* Vendor Specific PHS param Length Of The Service Flow */
+	B_UINT8 u8VendorSpecificPHSParamsLength;
+	/* Vendor Specific PHS param Of The Service Flow */
+	B_UINT8 u8VendorSpecificPHSParams[VENDOR_PHS_PARAM_LENGTH];
+	B_UINT8 u8Padding[2];
 };
-typedef struct _stConvergenceSLTypes stConvergenceSLTypes,CConvergenceSLTypes, *pstConvergenceSLTypes;
-
-
-/// \brief structure CServiceFlowParamSI
-typedef struct _stServiceFlowParamSI{
-
-     /**  32bitSFID Of The Service Flow*/
-    B_UINT32                        u32SFID;
-
-     /**  32bit Maximum Sustained Traffic Rate of the Service Flow*/
-    B_UINT32                        u32MaxSustainedTrafficRate;
-
-     /**  32bit Maximum Traffic Burst allowed for the Service Flow*/
-    B_UINT32                        u32MaxTrafficBurst;
-
-    /**  32bit Minimum Reserved Traffic Rate of the Service Flow*/
-    B_UINT32                        u32MinReservedTrafficRate;
-
-	/**  32bit Tolerated Jitter of the Service Flow*/
-    	B_UINT32                        u32ToleratedJitter;
-
-   /**  32bit Maximum Latency of the Service Flow*/
-    B_UINT32                        u32MaximumLatency;
-
-	/**  16bitCID Of The Service Flow*/
-    B_UINT16                        u16CID;
-
-     /**  16bit SAID on which the service flow being set up shall be mapped*/
-    B_UINT16                        u16TargetSAID;
-
-	/** 16bit  ARQ window size negotiated*/
-    B_UINT16                        u16ARQWindowSize;
-
-     /**  16bit Total Tx delay incl sending, receiving & processing delays 	*/
-    B_UINT16                        u16ARQRetryTxTimeOut;
-
-	/**  16bit Total Rx delay incl sending, receiving & processing delays 	*/
-    B_UINT16                        u16ARQRetryRxTimeOut;
-
-	/**  16bit ARQ block lifetime	*/
-    B_UINT16                        u16ARQBlockLifeTime;
-
-	/**  16bit ARQ Sync loss timeout*/
-    B_UINT16                        u16ARQSyncLossTimeOut;
-
-	 /**  16bit ARQ Purge timeout */
-    B_UINT16                        u16ARQRxPurgeTimeOut;
-//TODO::Remove this once we move to a new CORR2 driver
-    /// \brief Size of an ARQ block
-    B_UINT16                        u16ARQBlockSize;
-
-//#endif
-	/**  16bit Nominal interval b/w consecutive SDU arrivals at MAC SAP*/
-	B_UINT16                        u16SDUInterArrivalTime;
-
-	/**  16bit Specifies the time base for rate measurement 	*/
-	B_UINT16                        u16TimeBase;
-
-	 /** 16bit Interval b/w Successive Grant oppurtunities*/
-	B_UINT16                        u16UnsolicitedGrantInterval;
-
-	/** 16bit Interval b/w Successive Polling grant oppurtunities*/
-	B_UINT16						u16UnsolicitedPollingInterval;
-
-	 /**   internal var to get the overhead */
-	B_UINT16						u16MacOverhead;
-
-	 /**  MBS contents Identifier*/
-	B_UINT16						u16MBSContentsID[MBS_CONTENTS_ID_LENGTH];
-
-	/**  MBS contents Identifier length*/
-	B_UINT8							u8MBSContentsIDLength;
-
-	/**	 ServiceClassName Length Of The Service Flow*/
-    B_UINT8                         u8ServiceClassNameLength;
-
-	/**  32bytes ServiceClassName Of The Service Flow*/
-    B_UINT8                         u8ServiceClassName[32];
-
-	/**  8bit Indicates whether or not MBS service is requested for this Serivce Flow*/
-	B_UINT8							u8MBSService;
-
-    /**  8bit QOS Parameter Set specifies proper application of QoS paramters to Provisioned, Admitted and Active sets*/
-    B_UINT8                         u8QosParamSet;
-
-   /**  8bit Traffic Priority Of the Service Flow */
-    B_UINT8                         u8TrafficPriority;
-
-   /**  8bit Uplink Grant Scheduling Type of The Service Flow */
-    B_UINT8                         u8ServiceFlowSchedulingType;
-
-  /**  8bit Request transmission Policy of the Service Flow*/
-    B_UINT8							u8RequesttransmissionPolicy;
-
-	/**  8bit Specifies whether SDUs for this Service flow are of FixedLength or Variable length */
-    B_UINT8                         u8FixedLengthVSVariableLengthSDUIndicator;
-
-	/**  8bit Length of the SDU for a fixed length SDU service flow*/
-	B_UINT8                         u8SDUSize;
 
-	 /** 8bit Indicates whether or not ARQ is requested for this connection*/
-       B_UINT8                         u8ARQEnable;
-
-	/**<  8bit Indicates whether or not data has tobe delivered in order to higher layer*/
-       B_UINT8                         u8ARQDeliverInOrder;
-
-	/**  8bit Receiver ARQ ACK processing time */
-	B_UINT8                         u8RxARQAckProcessingTime;
-
-	/**  8bit Convergence Sublayer Specification Of The Service Flow*/
-       B_UINT8                         u8CSSpecification;
-
-	 /**  8 bit Type of data delivery service*/
-	B_UINT8                         u8TypeOfDataDeliveryService;
-
-	/** 8bit Specifies whether a service flow may generate Paging	*/
-	B_UINT8                         u8PagingPreference;
-
-	/**  8bit Indicates the MBS Zone through which the connection or virtual connection is valid	*/
-       B_UINT8                         u8MBSZoneIdentifierassignment;
-
-       /**  8bit Specifies whether traffic on SF should generate MOB_TRF_IND to MS in sleep mode*/
-	B_UINT8                         u8TrafficIndicationPreference;
-
-	/** 8bit Speciifes the length of predefined Global QoS parameter set encoding for this SF	*/
-	B_UINT8                         u8GlobalServicesClassNameLength;
-
-	 /**  6 byte Speciifes the predefined Global QoS parameter set encoding for this SF	*/
-	B_UINT8                         u8GlobalServicesClassName[GLOBAL_SF_CLASSNAME_LENGTH];
-
-	 /**  8bit Indicates whether or not SN feedback is enabled for the conn	*/
-	B_UINT8                         u8SNFeedbackEnabled;
-
-	 /**  Indicates the size of the Fragment Sequence Number for the connection	*/
-	B_UINT8                         u8FSNSize;
-
-	/** 8bit Number of CIDs in active BS list 	*/
-	B_UINT8							u8CIDAllocation4activeBSsLength;
-
-	/**  CIDs of BS in the active list	*/
-	B_UINT8							u8CIDAllocation4activeBSs[MAX_NUM_ACTIVE_BS];
-
-	 /**  Specifies if PDU extended subheader should be applied on every PDU on this conn*/
-	B_UINT8                         u8PDUSNExtendedSubheader4HarqReordering;
-
-	 /**  8bit Specifies whether the connection uses HARQ or not	*/
-	B_UINT8                         u8HARQServiceFlows;
-
-	/**  Specifies the length of Authorization token*/
-	B_UINT8							u8AuthTokenLength;
-
-	/**  Specifies the Authorization token*/
-	B_UINT8							u8AuthToken[AUTH_TOKEN_LENGTH];
-
-	/**  specifes Number of HARQ channels used to carry data length*/
-	B_UINT8							u8HarqChannelMappingLength;
-
-	 /**  specifes HARQ channels used to carry data*/
-	B_UINT8							u8HARQChannelMapping[NUM_HARQ_CHANNELS];
-
-	/**  8bit Length of Vendor Specific QoS Params */
-    B_UINT8                         u8VendorSpecificQoSParamLength;
-
-	/** 1byte  Vendor Specific QoS Param Of The Service Flow*/
-    B_UINT8                          u8VendorSpecificQoSParam[VENDOR_SPECIF_QOS_PARAM];
-
-	// indicates total classifiers in the SF
-	B_UINT8                         u8TotalClassifiers;  /**< Total number of valid classifiers*/
-	B_UINT8							bValid;	/**<  Validity flag */
-	B_UINT8				u8Padding;	 /**<  Padding byte*/
-
-/**
-Structure for Convergence SubLayer Types with a maximum of 4 classifiers
-*/
-	stConvergenceSLTypes		cConvergenceSLTypes[MAX_CLASSIFIERS_IN_SF];
-
-} stServiceFlowParamSI, *pstServiceFlowParamSI;
-typedef stServiceFlowParamSI CServiceFlowParamSI;
-
-/**
-structure stLocalSFAddRequest
-*/
-typedef struct _stLocalSFAddRequest{
-
-	B_UINT8                         u8Type;	/**<  Type*/
-	B_UINT8      eConnectionDir;		/**<  Connection direction*/
-	/// \brief 16 bit TID
-	B_UINT16                        u16TID;	/**<  16bit TID*/
-	/// \brief 16bitCID
-    	B_UINT16                        u16CID;	/**<  16bit CID*/
-	/// \brief 16bitVCID
-	B_UINT16                        u16VCID;	/**<  16bit VCID*/
-    /// \brief structure ParameterSet
-
-	stServiceFlowParamSI	*psfParameterSet;	/**<  structure ParameterSet*/
-
-}stLocalSFAddRequest, *pstLocalSFAddRequest;
-
-
-/**
-structure stLocalSFAddIndication
-*/
-typedef struct _stLocalSFAddIndication{
-
-	B_UINT8                         u8Type;	/**<  Type*/
-	B_UINT8      eConnectionDir;	/**<  Connection Direction*/
-	/// \brief 16 bit TID
-	B_UINT16                         u16TID;	/**<  TID*/
-    /// \brief 16bitCID
-    B_UINT16                        u16CID;		/**<  16bitCID*/
-    /// \brief 16bitVCID
-    B_UINT16                        u16VCID;	 /**<  16bitVCID*/
-
-
-    /// \brief structure AuthorizedSet
-    /// \brief structure AuthorizedSet
-    stServiceFlowParamSI              *psfAuthorizedSet;	/**<  AuthorizedSet of type stServiceFlowParamSI*/
-    /// \brief structure AdmittedSet
-    stServiceFlowParamSI              *psfAdmittedSet;	/**<  AdmittedSet of type stServiceFlowParamSI*/
-    /// \brief structure ActiveSet
-    stServiceFlowParamSI              *psfActiveSet;	/**<  sfActiveSet of type stServiceFlowParamSI*/
-	B_UINT8				   u8CC;	/**<  Confirmation Code*/
-	B_UINT8				   u8Padd;		/**<  8-bit Padding */
-
-    B_UINT16               u16Padd;	/**< 16 bit Padding */
-
-}stLocalSFAddIndication;
+struct bcm_convergence_types {
+	/* 8bit Phs Classfier Action Of The Service Flow */
+	B_UINT8 u8ClassfierDSCAction;
+	/* 8bit Phs DSC Action Of The Service Flow */
+	B_UINT8 u8PhsDSCAction;
+	/* 16bit Padding */
+	B_UINT8 u8Padding[2];
+	/* Packet classification rules structure */
+	struct bcm_packet_class_rules cCPacketClassificationRule;
+	/* Payload header suppression rules structure */
+	struct bcm_phs_rules cPhsRule;
+};
 
+struct bcm_connect_mgr_params {
+	/* 32bitSFID Of The Service Flow */
+	B_UINT32 u32SFID;
+	/* 32bit Maximum Sustained Traffic Rate of the Service Flow */
+	B_UINT32 u32MaxSustainedTrafficRate;
+	/* 32bit Maximum Traffic Burst allowed for the Service Flow */
+	B_UINT32 u32MaxTrafficBurst;
+	/* 32bit Minimum Reserved Traffic Rate of the Service Flow */
+	B_UINT32 u32MinReservedTrafficRate;
+	/* 32bit Tolerated Jitter of the Service Flow */
+	B_UINT32 u32ToleratedJitter;
+	/* 32bit Maximum Latency of the Service Flow */
+	B_UINT32 u32MaximumLatency;
+	/* 16bitCID Of The Service Flow */
+	B_UINT16 u16CID;
+	/* 16bit SAID on which the service flow being set up shall be mapped */
+	B_UINT16 u16TargetSAID;
+	/* 16bit  ARQ window size negotiated */
+	B_UINT16 u16ARQWindowSize;
+	/* 16bit Total Tx delay incl sending, receiving & processing delays */
+	B_UINT16 u16ARQRetryTxTimeOut;
+	/* 16bit Total Rx delay incl sending, receiving & processing delays */
+	B_UINT16 u16ARQRetryRxTimeOut;
+	/* 16bit ARQ block lifetime */
+	B_UINT16 u16ARQBlockLifeTime;
+	/* 16bit ARQ Sync loss timeout */
+	B_UINT16 u16ARQSyncLossTimeOut;
+	/* 16bit ARQ Purge timeout */
+	B_UINT16 u16ARQRxPurgeTimeOut;
+	/* TODO::Remove this once we move to a new CORR2 driver
+	 * brief Size of an ARQ block
+	 */
+	B_UINT16 u16ARQBlockSize;
+	/* #endif */
+	/* 16bit Nominal interval b/w consecutive SDU arrivals at MAC SAP */
+	B_UINT16 u16SDUInterArrivalTime;
+	/* 16bit Specifies the time base for rate measurement */
+	B_UINT16 u16TimeBase;
+	/* 16bit Interval b/w Successive Grant oppurtunities */
+	B_UINT16 u16UnsolicitedGrantInterval;
+	/* 16bit Interval b/w Successive Polling grant oppurtunities */
+	B_UINT16 u16UnsolicitedPollingInterval;
+	/* internal var to get the overhead */
+	B_UINT16 u16MacOverhead;
+	/* MBS contents Identifier */
+	B_UINT16 u16MBSContentsID[MBS_CONTENTS_ID_LENGTH];
+	/* MBS contents Identifier length */
+	B_UINT8 u8MBSContentsIDLength;
+	/* ServiceClassName Length Of The Service Flow */
+	B_UINT8 u8ServiceClassNameLength;
+	/* 32bytes ServiceClassName Of The Service Flow */
+	B_UINT8 u8ServiceClassName[32];
+	/* 8bit Indicates whether or not MBS service is requested for this Serivce Flow */
+	B_UINT8 u8MBSService;
+	/* 8bit QOS Parameter Set specifies proper application of QoS parameters to Provisioned, Admitted and Active sets */
+	B_UINT8 u8QosParamSet;
+	/* 8bit Traffic Priority Of the Service Flow */
+	B_UINT8 u8TrafficPriority;
+	/* 8bit Uplink Grant Scheduling Type of The Service Flow */
+	B_UINT8 u8ServiceFlowSchedulingType;
+	/* 8bit Request transmission Policy of the Service Flow */
+	B_UINT8 u8RequesttransmissionPolicy;
+	/* 8bit Specifies whether SDUs for this Service flow are of FixedLength or Variable length */
+	B_UINT8 u8FixedLengthVSVariableLengthSDUIndicator;
+	/* 8bit Length of the SDU for a fixed length SDU service flow */
+	B_UINT8 u8SDUSize;
+	/* 8bit Indicates whether or not ARQ is requested for this connection */
+	B_UINT8 u8ARQEnable;
+	/* < 8bit Indicates whether or not data has tobe delivered in order to higher layer */
+	B_UINT8 u8ARQDeliverInOrder;
+	/* 8bit Receiver ARQ ACK processing time */
+	B_UINT8 u8RxARQAckProcessingTime;
+	/* 8bit Convergence Sublayer Specification Of The Service Flow */
+	B_UINT8 u8CSSpecification;
+	/* 8 bit Type of data delivery service */
+	B_UINT8 u8TypeOfDataDeliveryService;
+	/* 8bit Specifies whether a service flow may generate Paging */
+	B_UINT8 u8PagingPreference;
+	/* 8bit Indicates the MBS Zone through which the connection or virtual connection is valid */
+	B_UINT8 u8MBSZoneIdentifierassignment;
+	/* 8bit Specifies whether traffic on SF should generate MOB_TRF_IND to MS in sleep mode */
+	B_UINT8 u8TrafficIndicationPreference;
+	/* 8bit Speciifes the length of predefined Global QoS parameter set encoding for this SF */
+	B_UINT8 u8GlobalServicesClassNameLength;
+	/* 6 byte Speciifes the predefined Global QoS parameter set encoding for this SF */
+	B_UINT8 u8GlobalServicesClassName[GLOBAL_SF_CLASSNAME_LENGTH];
+	/* 8bit Indicates whether or not SN feedback is enabled for the conn */
+	B_UINT8 u8SNFeedbackEnabled;
+	/* Indicates the size of the Fragment Sequence Number for the connection */
+	B_UINT8 u8FSNSize;
+	/* 8bit Number of CIDs in active BS list */
+	B_UINT8 u8CIDAllocation4activeBSsLength;
+	/* CIDs of BS in the active list */
+	B_UINT8 u8CIDAllocation4activeBSs[MAX_NUM_ACTIVE_BS];
+	/* Specifies if PDU extended subheader should be applied on every PDU on this conn */
+	B_UINT8 u8PDUSNExtendedSubheader4HarqReordering;
+	/* 8bit Specifies whether the connection uses HARQ or not */
+	B_UINT8 u8HARQServiceFlows;
+	/* Specifies the length of Authorization token */
+	B_UINT8 u8AuthTokenLength;
+	/* Specifies the Authorization token */
+	B_UINT8 u8AuthToken[AUTH_TOKEN_LENGTH];
+	/* specifes Number of HARQ channels used to carry data length */
+	B_UINT8 u8HarqChannelMappingLength;
+	/* specifes HARQ channels used to carry data */
+	B_UINT8 u8HARQChannelMapping[NUM_HARQ_CHANNELS];
+	/* 8bit Length of Vendor Specific QoS Params */
+	B_UINT8 u8VendorSpecificQoSParamLength;
+	/* 1byte  Vendor Specific QoS Param Of The Service Flow */
+	B_UINT8 u8VendorSpecificQoSParam[VENDOR_SPECIF_QOS_PARAM];
+	/* indicates total classifiers in the SF */
+	B_UINT8 u8TotalClassifiers;  /* < Total number of valid classifiers */
+	B_UINT8 bValid;	/* < Validity flag */
+	B_UINT8 u8Padding;	 /* < Padding byte */
+	/*
+	 * Structure for Convergence SubLayer Types with a maximum of 4 classifiers
+	 */
+	struct bcm_convergence_types cConvergenceSLTypes[MAX_CLASSIFIERS_IN_SF];
+};
 
-typedef struct _stLocalSFAddIndication *pstLocalSFAddIndication;
-/**
-structure stLocalSFChangeRequest is same as structure stLocalSFAddIndication
-*/
-typedef struct _stLocalSFAddIndication stLocalSFChangeRequest, *pstLocalSFChangeRequest;
-/**
-structure stLocalSFChangeIndication is same as structure stLocalSFAddIndication
-*/
-typedef struct _stLocalSFAddIndication stLocalSFChangeIndication, *pstLocalSFChangeIndication;
+struct bcm_add_request {
+	B_UINT8 u8Type;	/* < Type */
+	B_UINT8 eConnectionDir; /* < Connection direction */
+	/* brief 16 bit TID */
+	B_UINT16 u16TID; /* < 16bit TID */
+	/* brief 16bitCID */
+	B_UINT16 u16CID; /* < 16bit CID */
+	/* brief 16bitVCID */
+	B_UINT16 u16VCID; /* < 16bit VCID */
+	struct bcm_connect_mgr_params *psfParameterSet; /* < connection manager parameters */
+};
 
-/**
-structure stLocalSFDeleteRequest
-*/
-typedef struct _stLocalSFDeleteRequest{
-	B_UINT8                         u8Type;	 /**< Type*/
-	B_UINT8                         u8Padding;	 /**<  Padding byte*/
-	B_UINT16			u16TID;		 /**<  TID*/
-    /// \brief 32bitSFID
-    B_UINT32                        u32SFID;	 /**<  SFID*/
-}stLocalSFDeleteRequest, *pstLocalSFDeleteRequest;
+struct bcm_add_indication {
+	B_UINT8 u8Type;	/* < Type */
+	B_UINT8 eConnectionDir;	/* < Connection Direction */
+	/* brief 16 bit TID */
+	B_UINT16 u16TID; /* < TID */
+	/* brief 16bitCID */
+	B_UINT16 u16CID; /* < 16bitCID */
+	/* brief 16bitVCID */
+	B_UINT16 u16VCID; /* < 16bitVCID */
+	struct bcm_connect_mgr_params *psfAuthorizedSet; /* Authorized set of connection manager parameters */
+	struct bcm_connect_mgr_params *psfAdmittedSet; /* Admitted set of connection manager parameters */
+	struct bcm_connect_mgr_params *psfActiveSet; /* Activeset of connection manager parameters */
+	B_UINT8 u8CC; /* <Confirmation Code */
+	B_UINT8 u8Padd; /* < 8-bit Padding */
+	B_UINT16 u16Padd; /* < 16 bit Padding */
+};
 
-/**
-structure stLocalSFDeleteIndication
-*/
-typedef struct stLocalSFDeleteIndication{
-	B_UINT8                         u8Type;	/**< Type */
-	B_UINT8                         u8Padding;	/**< Padding  */
-	B_UINT16			u16TID;			/**< TID */
-       /// \brief 16bitCID
-    B_UINT16                        u16CID;			/**< CID */
-    /// \brief 16bitVCID
-    B_UINT16                        u16VCID;		/**< VCID */
-    /// \brief 32bitSFID
-    B_UINT32                        u32SFID; 		/**< SFID */
-	/// \brief 8bit Confirmation code
-	B_UINT8                         u8ConfirmationCode;	/**< Confirmation code */
-	B_UINT8                         u8Padding1[3];		/**< 3 byte Padding  */
-}stLocalSFDeleteIndication;
+struct bcm_del_request {
+	B_UINT8 u8Type; /* < Type */
+	B_UINT8 u8Padding; /* < Padding byte */
+	B_UINT16 u16TID; /* < TID */
+	/* brief 32bitSFID */
+	B_UINT32 u32SFID; /* < SFID */
+};
 
-typedef struct _stIM_SFHostNotify
-{
-	B_UINT32 	SFID;      //SFID of the service flow
-	B_UINT16 	newCID;   //the new/changed CID
-	B_UINT16 	VCID;             //Get new Vcid if the flow has been made active in CID update TLV, but was inactive earlier or the orig vcid
-	B_UINT8  	RetainSF;        //Indication to Host if the SF is to be retained or deleted; if TRUE-retain else delete
-	B_UINT8 	QoSParamSet; //QoS paramset of the retained SF
-	B_UINT16 	u16reserved;  //For byte alignment
+struct bcm_del_indication {
+	B_UINT8 u8Type;	/* < Type */
+	B_UINT8 u8Padding; /* < Padding */
+	B_UINT16 u16TID; /* < TID */
+	/* brief 16bitCID */
+	B_UINT16 u16CID; /* < CID */
+	/* brief 16bitVCID */
+	B_UINT16 u16VCID; /* < VCID */
+	/* brief 32bitSFID */
+	B_UINT32 u32SFID; /* < SFID */
+	/* brief 8bit Confirmation code */
+	B_UINT8 u8ConfirmationCode; /* < Confirmation code */
+	B_UINT8 u8Padding1[3]; /* < 3 byte Padding */
+};
 
-} stIM_SFHostNotify;
+struct bcm_stim_sfhostnotify {
+	B_UINT32 SFID; /* SFID of the service flow */
+	B_UINT16 newCID; /* the new/changed CID */
+	B_UINT16 VCID; /* Get new Vcid if the flow has been made active in CID update TLV, but was inactive earlier or the orig vcid */
+	B_UINT8 RetainSF; /* Indication to Host if the SF is to be retained or deleted; if TRUE-retain else delete */
+	B_UINT8 QoSParamSet; /* QoS paramset of the retained SF */
+	B_UINT16 u16reserved; /* For byte alignment */
+};
 
 #endif

+ 1 - 1
drivers/staging/bcm/hostmibs.c

@@ -101,7 +101,7 @@ VOID GetDroppedAppCntrlPktMibs(S_MIBS_HOST_STATS_MIBS *pstHostMibs, struct bcm_t
 	       sizeof(S_MIBS_DROPPED_APP_CNTRL_MESSAGES));
 }
 
-VOID CopyMIBSExtendedSFParameters(struct bcm_mini_adapter *Adapter, CServiceFlowParamSI *psfLocalSet, UINT uiSearchRuleIndex)
+VOID CopyMIBSExtendedSFParameters(struct bcm_mini_adapter *Adapter, struct bcm_connect_mgr_params *psfLocalSet, UINT uiSearchRuleIndex)
 {
 	S_MIBS_EXTSERVICEFLOW_PARAMETERS *t = &Adapter->PackInfo[uiSearchRuleIndex].stMibsExtServiceFlowTable;
 

+ 2 - 2
drivers/staging/bcm/nvm.c

@@ -577,7 +577,7 @@ static int FlashSectorErase(struct bcm_mini_adapter *Adapter,
 			 * the sector erase cycle is 500 ms to 40000 msec. hence sleeping 10 ms
 			 * won't hamper performance in any case.
 			 */
-			udelay(10000);
+			mdelay(10);
 		} while ((uiStatus & 0x1) && (iRetries < 400));
 
 		if (uiStatus & 0x1) {
@@ -3932,7 +3932,7 @@ int validateFlash2xReadWrite(struct bcm_mini_adapter *Adapter, PFLASH2X_READWRIT
 				BcmGetSectionValStartOffset(Adapter, ISO_IMAGE2_PART3);
 		}
 
-		/* since this uiSectEndoffset is the size of iso Image. hence for calculating the vitual endoffset
+		/* since this uiSectEndoffset is the size of iso Image. hence for calculating the virtual endoffset
 		 * it should be added in startoffset. so that check done in last of this function can be valued.
 		 */
 		uiSectEndOffset = uiSectStartOffset + uiSectEndOffset;

+ 1 - 1
drivers/staging/bcm/target_params.h

@@ -32,7 +32,7 @@ typedef struct _TARGET_PARAMS
       B_UINT32 m_u32PowerSavingModesEnable; //bit 1: 1 Idlemode enable; bit2: 1 Sleepmode Enable
 	  /* PowerSaving Mode Options:
 	     bit 0 = 1: CPE mode - to keep pcmcia if alive;
-	     bit 1 = 1: CINR reporing in Idlemode Msg
+	     bit 1 = 1: CINR reporting in Idlemode Msg
 	     bit 2 = 1: Default PSC Enable in sleepmode*/
       B_UINT32 m_u32PowerSavingModeOptions;
 

+ 1 - 1
drivers/staging/ccg/Kconfig

@@ -2,7 +2,7 @@ if USB_GADGET
 
 config USB_G_CCG
 	tristate "Configurable Composite Gadget (STAGING)"
-	depends on STAGING && BLOCK && !USB_ZERO && !USB_ZERO_HNPTEST && !USB_AUDIO && !GADGET_UAC1 && !USB_ETH && !USB_ETH_RNDIS && !USB_ETH_EEM && !USB_G_NCM && !USB_GADGETFS && !USB_FUNCTIONFS && !USB_FUNCTIONFS_ETH && !USB_FUNCTIONFS_RNDIS && !USB_FUNCTIONFS_GENERIC && !USB_FILE_STORAGE && !USB_FILE_STORAGE_TEST && !USB_MASS_STORAGE && !USB_G_SERIAL && !USB_MIDI_GADGET && !USB_G_PRINTER && !USB_CDC_COMPOSITE && !USB_G_NOKIA && !USB_G_ACM_MS && !USB_G_MULTI && !USB_G_MULTI_RNDIS && !USB_G_MULTI_CDC && !USB_G_HID && !USB_G_DBGP && !USB_G_WEBCAM
+	depends on STAGING && BLOCK && NET && !USB_ZERO && !USB_ZERO_HNPTEST && !USB_AUDIO && !GADGET_UAC1 && !USB_ETH && !USB_ETH_RNDIS && !USB_ETH_EEM && !USB_G_NCM && !USB_GADGETFS && !USB_FUNCTIONFS && !USB_FUNCTIONFS_ETH && !USB_FUNCTIONFS_RNDIS && !USB_FUNCTIONFS_GENERIC && !USB_FILE_STORAGE && !USB_FILE_STORAGE_TEST && !USB_MASS_STORAGE && !USB_G_SERIAL && !USB_MIDI_GADGET && !USB_G_PRINTER && !USB_CDC_COMPOSITE && !USB_G_NOKIA && !USB_G_ACM_MS && !USB_G_MULTI && !USB_G_MULTI_RNDIS && !USB_G_MULTI_CDC && !USB_G_HID && !USB_G_DBGP && !USB_G_WEBCAM
 	help
 	  The Configurable Composite Gadget supports multiple USB
 	  functions: acm, mass storage, rndis and FunctionFS.

+ 5 - 9
drivers/staging/ccg/ccg.c

@@ -728,7 +728,7 @@ static int mass_storage_function_init(struct ccg_usb_function *f,
 	struct fsg_common *common;
 	int err;
 
-	memset(&fsg, 0, sizeof fsg);
+	memset(&fsg, 0, sizeof(fsg));
 	fsg.nluns = 1;
 	fsg.luns[0].removable = 1;
 	fsg.vendor_name = iManufacturer;
@@ -1101,13 +1101,7 @@ static struct device_attribute *ccg_usb_attributes[] = {
 static int ccg_bind_config(struct usb_configuration *c)
 {
 	struct ccg_dev *dev = _ccg_dev;
-	int ret = 0;
-
-	ret = ccg_bind_enabled_functions(dev, c);
-	if (ret)
-		return ret;
-
-	return 0;
+	return ccg_bind_enabled_functions(dev, c);
 }
 
 static void ccg_unbind_config(struct usb_configuration *c)
@@ -1254,8 +1248,10 @@ static int __init init(void)
 		return PTR_ERR(ccg_class);
 
 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-	if (!dev)
+	if (!dev) {
+		class_destroy(ccg_class);
 		return -ENOMEM;
+	}
 
 	dev->functions = supported_functions;
 	INIT_LIST_HEAD(&dev->enabled_functions);

+ 6 - 0
drivers/staging/ced1401/Kconfig

@@ -0,0 +1,6 @@
+config CED1401
+	tristate "Cambridge Electronic Design 1401 USB support"
+	depends on USB
+	help
+	  This driver supports the Cambridge Electronic Design 1401 USB device
+	  (whatever that is.)

+ 3 - 0
drivers/staging/ced1401/Makefile

@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_CED1401)	:= cedusb.o
+cedusb-objs	:= usb1401.o ced_ioc.o

+ 10 - 0
drivers/staging/ced1401/TODO

@@ -0,0 +1,10 @@
+TODO:
+	- coding syle fixes
+	- build warning fixups
+	- ioctl auditing
+	- usb api auditing
+	- proper USB minor number (it's stomping on an existing one right now.)
+
+Please send patches to Greg Kroah-Hartman <gregkh@linuxfoundation.org> and Cc:
+Alois Schlögl <alois.schloegl@ist.ac.at>
+

+ 1515 - 0
drivers/staging/ced1401/ced_ioc.c

@@ -0,0 +1,1515 @@
+/* ced_ioc.c
+ ioctl part of the 1401 usb device driver for linux.
+ Copyright (C) 2010 Cambridge Electronic Design Ltd
+ Author Greg P Smith (greg@ced.co.uk)
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/page-flags.h>
+#include <linux/pagemap.h>
+#include <linux/jiffies.h>
+
+#include "usb1401.h"
+
+/****************************************************************************
+** FlushOutBuff
+**
+** Empties the Output buffer and sets int lines. Used from user level only
+****************************************************************************/
+void FlushOutBuff(DEVICE_EXTENSION * pdx)
+{
+	dev_dbg(&pdx->interface->dev, "%s currentState=%d", __func__,
+		pdx->sCurrentState);
+	if (pdx->sCurrentState == U14ERR_TIME)	/* Do nothing if hardware in trouble */
+		return;
+//    CharSend_Cancel(pdx);                   /* Kill off any pending I/O */
+	spin_lock_irq(&pdx->charOutLock);
+	pdx->dwNumOutput = 0;
+	pdx->dwOutBuffGet = 0;
+	pdx->dwOutBuffPut = 0;
+	spin_unlock_irq(&pdx->charOutLock);
+}
+
+/****************************************************************************
+**
+** FlushInBuff
+**
+** Empties the input buffer and sets int lines
+****************************************************************************/
+void FlushInBuff(DEVICE_EXTENSION * pdx)
+{
+	dev_dbg(&pdx->interface->dev, "%s currentState=%d", __func__,
+		pdx->sCurrentState);
+	if (pdx->sCurrentState == U14ERR_TIME)	/* Do nothing if hardware in trouble */
+		return;
+//    CharRead_Cancel(pDevObject);            /* Kill off any pending I/O */
+	spin_lock_irq(&pdx->charInLock);
+	pdx->dwNumInput = 0;
+	pdx->dwInBuffGet = 0;
+	pdx->dwInBuffPut = 0;
+	spin_unlock_irq(&pdx->charInLock);
+}
+
+/****************************************************************************
+** PutChars
+**
+** Utility routine to copy chars into the output buffer and fire them off.
+** called from user mode, holds charOutLock.
+****************************************************************************/
+static int PutChars(DEVICE_EXTENSION * pdx, const char *pCh,
+		    unsigned int uCount)
+{
+	int iReturn;
+	spin_lock_irq(&pdx->charOutLock);	// get the output spin lock
+	if ((OUTBUF_SZ - pdx->dwNumOutput) >= uCount) {
+		unsigned int u;
+		for (u = 0; u < uCount; u++) {
+			pdx->outputBuffer[pdx->dwOutBuffPut++] = pCh[u];
+			if (pdx->dwOutBuffPut >= OUTBUF_SZ)
+				pdx->dwOutBuffPut = 0;
+		}
+		pdx->dwNumOutput += uCount;
+		spin_unlock_irq(&pdx->charOutLock);
+		iReturn = SendChars(pdx);	// ...give a chance to transmit data
+	} else {
+		iReturn = U14ERR_NOOUT;	// no room at the out (ha-ha)
+		spin_unlock_irq(&pdx->charOutLock);
+	}
+	return iReturn;
+}
+
+/*****************************************************************************
+** Add the data in pData (local pointer) of length n to the output buffer, and
+** trigger an output transfer if this is appropriate. User mode.
+** Holds the io_mutex
+*****************************************************************************/
+int SendString(DEVICE_EXTENSION * pdx, const char __user * pData,
+	       unsigned int n)
+{
+	int iReturn = U14ERR_NOERROR;	// assume all will be well
+	char buffer[OUTBUF_SZ + 1];	// space in our address space for characters
+	if (n > OUTBUF_SZ)	// check space in local buffer...
+		return U14ERR_NOOUT;	// ...too many characters
+	if (copy_from_user(buffer, pData, n))
+		return -EFAULT;
+	buffer[n] = 0;		// terminate for debug purposes
+
+	mutex_lock(&pdx->io_mutex);	// Protect disconnect from new i/o
+	if (n > 0)		// do nothing if nowt to do!
+	{
+		dev_dbg(&pdx->interface->dev, "%s n=%d>%s<", __func__, n,
+			buffer);
+		iReturn = PutChars(pdx, buffer, n);
+	}
+
+	Allowi(pdx, false);	// make sure we have input int
+	mutex_unlock(&pdx->io_mutex);
+
+	return iReturn;
+}
+
+/****************************************************************************
+** SendChar
+**
+** Sends a single character to the 1401. User mode, holds io_mutex.
+****************************************************************************/
+int SendChar(DEVICE_EXTENSION * pdx, char c)
+{
+	int iReturn;
+	mutex_lock(&pdx->io_mutex);	// Protect disconnect from new i/o
+	iReturn = PutChars(pdx, &c, 1);
+	dev_dbg(&pdx->interface->dev, "SendChar >%c< (0x%02x)", c, c);
+	Allowi(pdx, false);	// Make sure char reads are running
+	mutex_unlock(&pdx->io_mutex);
+	return iReturn;
+}
+
+/***************************************************************************
+**
+** Get1401State
+**
+**  Retrieves state information from the 1401, adjusts the 1401 state held
+**  in the device extension to indicate the current 1401 type.
+**
+**  *state is updated with information about the 1401 state as returned by the
+**         1401. The low byte is a code for what 1401 is doing:
+**
+**  0       normal 1401 operation
+**  1       sending chars to host
+**  2       sending block data to host
+**  3       reading block data from host
+**  4       sending an escape sequence to the host
+**  0x80    1401 is executing self-test, in which case the upper word
+**          is the last error code seen (or zero for no new error).
+**
+** *error is updated with error information if a self-test error code
+**          is returned in the upper word of state.
+**
+**  both state and error are set to -1 if there are comms problems, and
+**  to zero if there is a simple failure.
+**
+** return error code (U14ERR_NOERROR for OK)
+*/
+int Get1401State(DEVICE_EXTENSION * pdx, __u32 * state, __u32 * error)
+{
+	int nGot;
+	dev_dbg(&pdx->interface->dev, "Get1401State() entry");
+
+	*state = 0xFFFFFFFF;	// Start off with invalid state
+	nGot = usb_control_msg(pdx->udev, usb_rcvctrlpipe(pdx->udev, 0),
+			       GET_STATUS, (D_TO_H | VENDOR | DEVREQ), 0, 0,
+			       pdx->statBuf, sizeof(pdx->statBuf), HZ);
+	if (nGot != sizeof(pdx->statBuf)) {
+		dev_err(&pdx->interface->dev,
+			"Get1401State() FAILED, return code %d", nGot);
+		pdx->sCurrentState = U14ERR_TIME;	// Indicate that things are very wrong indeed
+		*state = 0;	// Force status values to a known state
+		*error = 0;
+	} else {
+		int nDevice;
+		dev_dbg(&pdx->interface->dev,
+			"Get1401State() Success, state: 0x%x, 0x%x",
+			pdx->statBuf[0], pdx->statBuf[1]);
+
+		*state = pdx->statBuf[0];	// Return the state values to the calling code
+		*error = pdx->statBuf[1];
+
+		nDevice = pdx->udev->descriptor.bcdDevice >> 8;	// 1401 type code value
+		switch (nDevice)	// so we can clean up current state
+		{
+		case 0:
+			pdx->sCurrentState = U14ERR_U1401;
+			break;
+
+		default:	// allow lots of device codes for future 1401s
+			if ((nDevice >= 1) && (nDevice <= 23))
+				pdx->sCurrentState = (short)(nDevice + 6);
+			else
+				pdx->sCurrentState = U14ERR_ILL;
+			break;
+		}
+	}
+
+	return pdx->sCurrentState >= 0 ? U14ERR_NOERROR : pdx->sCurrentState;
+}
+
+/****************************************************************************
+** ReadWrite_Cancel
+**
+** Kills off staged read\write request from the USB if one is pending.
+****************************************************************************/
+int ReadWrite_Cancel(DEVICE_EXTENSION * pdx)
+{
+	dev_dbg(&pdx->interface->dev, "ReadWrite_Cancel entry %d",
+		pdx->bStagedUrbPending);
+#ifdef NOT_WRITTEN_YET
+	int ntStatus = STATUS_SUCCESS;
+	bool bResult = false;
+	unsigned int i;
+	// We can fill this in when we know how we will implement the staged transfer stuff
+	spin_lock_irq(&pdx->stagedLock);
+
+	if (pdx->bStagedUrbPending)	// anything to be cancelled? May need more...
+	{
+		dev_info(&pdx->interface - dev,
+			 "ReadWrite_Cancel about to cancel Urb");
+
+		//       KeClearEvent(&pdx->StagingDoneEvent);   // Clear the staging done flag
+		USB_ASSERT(pdx->pStagedIrp != NULL);
+
+		// Release the spinlock first otherwise the completion routine may hang
+		//  on the spinlock while this function hands waiting for the event.
+		spin_unlock_irq(&pdx->stagedLock);
+		bResult = IoCancelIrp(pdx->pStagedIrp);	// Actually do the cancel
+		if (bResult) {
+			LARGE_INTEGER timeout;
+			timeout.QuadPart = -10000000;	// Use a timeout of 1 second
+			dev_info(&pdx->interface - dev,
+				 "ReadWrite_Cancel about to wait till done");
+			ntStatus =
+			    KeWaitForSingleObject(&pdx->StagingDoneEvent,
+						  Executive, KernelMode, FALSE,
+						  &timeout);
+		} else {
+			dev_info(&pdx->interface - dev,
+				 "ReadWrite_Cancel, cancellation failed");
+			ntStatus = U14ERR_FAIL;
+		}
+		USB_KdPrint(DBGLVL_DEFAULT,
+			    ("ReadWrite_Cancel ntStatus = 0x%x decimal %d\n",
+			     ntStatus, ntStatus));
+	} else
+		spin_unlock_irq(&pdx->stagedLock);
+
+	dev_info(&pdx->interface - dev, "ReadWrite_Cancel  done");
+	return ntStatus;
+#else
+	return U14ERR_NOERROR;
+#endif
+
+}
+
+/***************************************************************************
+** InSelfTest - utility to check in self test. Return 1 for ST, 0 for not or
+** a -ve error code if we failed for some reason.
+***************************************************************************/
+static int InSelfTest(DEVICE_EXTENSION * pdx, unsigned int *pState)
+{
+	unsigned int state, error;
+	int iReturn = Get1401State(pdx, &state, &error);	// see if in self-test
+	if (iReturn == U14ERR_NOERROR)	// if all still OK
+		iReturn = (state == (unsigned int)-1) ||	// TX problem or...
+		    ((state & 0xff) == 0x80);	// ...self test
+	*pState = state;	// return actual state
+	return iReturn;
+}
+
+/***************************************************************************
+** Is1401 - ALWAYS CALLED HOLDING THE io_mutex
+**
+** Tests for the current state of the 1401. Sets sCurrentState:
+**
+**  U14ERR_NOIF  1401  i/f card not installed (not done here)
+**  U14ERR_OFF   1401  apparently not switched on
+**  U14ERR_NC    1401  appears to be not connected
+**  U14ERR_ILL   1401  if it is there its not very well at all
+**  U14ERR_TIME  1401  appears OK, but doesn't communicate - very bad
+**  U14ERR_STD   1401  OK and ready for use
+**  U14ERR_PLUS  1401+ OK and ready for use
+**  U14ERR_U1401 Micro1401 OK and ready for use
+**  U14ERR_POWER Power1401 OK and ready for use
+**  U14ERR_U14012 Micro1401 mkII OK and ready for use
+**
+**  Returns TRUE if a 1401 detected and OK, else FALSE
+****************************************************************************/
+bool Is1401(DEVICE_EXTENSION * pdx)
+{
+	int iReturn;
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+	ced_draw_down(pdx);	// wait for, then kill outstanding Urbs
+	FlushInBuff(pdx);	// Clear out input buffer & pipe
+	FlushOutBuff(pdx);	// Clear output buffer & pipe
+
+	// The next call returns 0 if OK, but has returned 1 in the past, meaning that
+	// usb_unlock_device() is needed... now it always is
+	iReturn = usb_lock_device_for_reset(pdx->udev, pdx->interface);
+
+	// release the io_mutex because if we don't, we will deadlock due to system
+	// calls back into the driver.
+	mutex_unlock(&pdx->io_mutex);	// locked, so we will not get system calls
+	if (iReturn >= 0)	// if we failed
+	{
+		iReturn = usb_reset_device(pdx->udev);	// try to do the reset
+		usb_unlock_device(pdx->udev);	// undo the lock
+	}
+
+	mutex_lock(&pdx->io_mutex);	// hold stuff off while we wait
+	pdx->dwDMAFlag = MODE_CHAR;	// Clear DMA mode flag regardless!
+	if (iReturn == 0)	// if all is OK still
+	{
+		unsigned int state;
+		iReturn = InSelfTest(pdx, &state);	// see if likely in self test
+		if (iReturn > 0)	// do we need to wait for self-test?
+		{
+			unsigned long ulTimeOut = jiffies + 30 * HZ;	// when to give up
+			while ((iReturn > 0) && time_before(jiffies, ulTimeOut)) {
+				schedule();	// let other stuff run
+				iReturn = InSelfTest(pdx, &state);	// see if done yet
+			}
+		}
+
+		if (iReturn == 0)	// if all is OK...
+			iReturn = state == 0;	// then sucess is that the state is 0
+	} else
+		iReturn = 0;	// we failed
+	pdx->bForceReset = false;	// Clear forced reset flag now
+
+	return iReturn > 0;
+}
+
+/****************************************************************************
+** QuickCheck  - ALWAYS CALLED HOLDING THE io_mutex
+** This is used to test for a 1401. It will try to do a quick check if all is
+**  OK, that is the 1401 was OK the last time it was asked, and there is no DMA
+**  in progress, and if the bTestBuff flag is set, the character buffers must be
+**  empty too. If the quick check shows that the state is still the same, then
+**  all is OK.
+**
+** If any of the above conditions are not met, or if the state or type of the
+**  1401 has changed since the previous test, the full Is1401 test is done, but
+**  only if bCanReset is also TRUE.
+**
+** The return value is TRUE if a useable 1401 is found, FALSE if not
+*/
+bool QuickCheck(DEVICE_EXTENSION * pdx, bool bTestBuff, bool bCanReset)
+{
+	bool bRet = false;	// assume it will fail and we will reset
+	bool bShortTest;
+
+	bShortTest = ((pdx->dwDMAFlag == MODE_CHAR) &&	// no DMA running
+		      (!pdx->bForceReset) &&	// Not had a real reset forced
+		      (pdx->sCurrentState >= U14ERR_STD));	// No 1401 errors stored
+
+	dev_dbg(&pdx->interface->dev,
+		"%s DMAFlag:%d, state:%d, force:%d, testBuff:%d, short:%d",
+		__func__, pdx->dwDMAFlag, pdx->sCurrentState, pdx->bForceReset,
+		bTestBuff, bShortTest);
+
+	if ((bTestBuff) &&	// Buffer check requested, and...
+	    (pdx->dwNumInput || pdx->dwNumOutput))	// ...characters were in the buffer?
+	{
+		bShortTest = false;	// Then do the full test
+		dev_dbg(&pdx->interface->dev,
+			"%s will reset as buffers not empty", __func__);
+	}
+
+	if (bShortTest || !bCanReset)	// Still OK to try the short test?
+	{			// Always test if no reset - we want state update
+		unsigned int state, error;
+		dev_dbg(&pdx->interface->dev, "%s->Get1401State", __func__);
+		if (Get1401State(pdx, &state, &error) == U14ERR_NOERROR)	// Check on the 1401 state
+		{
+			if ((state & 0xFF) == 0)	// If call worked, check the status value
+				bRet = true;	// If that was zero, all is OK, no reset needed
+		}
+	}
+
+	if (!bRet && bCanReset)	// If all not OK, then
+	{
+		dev_info(&pdx->interface->dev, "%s->Is1401 %d %d %d %d",
+			 __func__, bShortTest, pdx->sCurrentState, bTestBuff,
+			 pdx->bForceReset);
+		bRet = Is1401(pdx);	//  do full test
+	}
+
+	return bRet;
+}
+
+/****************************************************************************
+** Reset1401
+**
+** Resets the 1401 and empties the i/o buffers
+*****************************************************************************/
+int Reset1401(DEVICE_EXTENSION * pdx)
+{
+	mutex_lock(&pdx->io_mutex);	// Protect disconnect from new i/o
+	dev_dbg(&pdx->interface->dev, "ABout to call QuickCheck");
+	QuickCheck(pdx, true, true);	// Check 1401, reset if not OK
+	mutex_unlock(&pdx->io_mutex);
+	return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+** GetChar
+**
+** Gets a single character from the 1401
+****************************************************************************/
+int GetChar(DEVICE_EXTENSION * pdx)
+{
+	int iReturn = U14ERR_NOIN;	// assume we will get  nothing
+	mutex_lock(&pdx->io_mutex);	// Protect disconnect from new i/o
+
+	dev_dbg(&pdx->interface->dev, "GetChar");
+
+	Allowi(pdx, false);	// Make sure char reads are running
+	SendChars(pdx);		// and send any buffered chars
+
+	spin_lock_irq(&pdx->charInLock);
+	if (pdx->dwNumInput > 0)	// worth looking
+	{
+		iReturn = pdx->inputBuffer[pdx->dwInBuffGet++];
+		if (pdx->dwInBuffGet >= INBUF_SZ)
+			pdx->dwInBuffGet = 0;
+		pdx->dwNumInput--;
+	} else
+		iReturn = U14ERR_NOIN;	// no input data to read
+	spin_unlock_irq(&pdx->charInLock);
+
+	Allowi(pdx, false);	// Make sure char reads are running
+
+	mutex_unlock(&pdx->io_mutex);	// Protect disconnect from new i/o
+	return iReturn;
+}
+
+/****************************************************************************
+** GetString
+**
+** Gets a string from the 1401. Returns chars up to the next CR or when
+** there are no more to read or nowhere to put them. CR is translated to
+** 0 and counted as a character. If the string does not end in a 0, we will
+** add one, if there is room, but it is not counted as a character.
+**
+** returns the count of characters (including the terminator, or 0 if none
+** or a negative error code.
+****************************************************************************/
+int GetString(DEVICE_EXTENSION * pdx, char __user * pUser, int n)
+{
+	int nAvailable;		// character in the buffer
+	int iReturn = U14ERR_NOIN;
+	if (n <= 0)
+		return -ENOMEM;
+
+	mutex_lock(&pdx->io_mutex);	// Protect disconnect from new i/o
+	Allowi(pdx, false);	// Make sure char reads are running
+	SendChars(pdx);		// and send any buffered chars
+
+	spin_lock_irq(&pdx->charInLock);
+	nAvailable = pdx->dwNumInput;	// characters available now
+	if (nAvailable > n)	// read max of space in pUser...
+		nAvailable = n;	// ...or input characters
+
+	if (nAvailable > 0)	// worth looking?
+	{
+		char buffer[INBUF_SZ + 1];	// space for a linear copy of data
+		int nGot = 0;
+		int nCopyToUser;	// number to copy to user
+		char cData;
+		do {
+			cData = pdx->inputBuffer[pdx->dwInBuffGet++];
+			if (cData == CR_CHAR)	// replace CR with zero
+				cData = (char)0;
+
+			if (pdx->dwInBuffGet >= INBUF_SZ)
+				pdx->dwInBuffGet = 0;	// wrap buffer pointer
+
+			buffer[nGot++] = cData;	// save the output
+		}
+		while ((nGot < nAvailable) && cData);
+
+		nCopyToUser = nGot;	// what to copy...
+		if (cData)	// do we need null
+		{
+			buffer[nGot] = (char)0;	// make it tidy
+			if (nGot < n)	// if space in user buffer...
+				++nCopyToUser;	// ...copy the 0 as well.
+		}
+
+		pdx->dwNumInput -= nGot;
+		spin_unlock_irq(&pdx->charInLock);
+
+		dev_dbg(&pdx->interface->dev,
+			"GetString read %d characters >%s<", nGot, buffer);
+		if (copy_to_user(pUser, buffer, nCopyToUser))
+			iReturn = -EFAULT;
+		else
+			iReturn = nGot;		// report characters read
+	} else
+		spin_unlock_irq(&pdx->charInLock);
+
+	Allowi(pdx, false);	// Make sure char reads are running
+	mutex_unlock(&pdx->io_mutex);	// Protect disconnect from new i/o
+
+	return iReturn;
+}
+
+/*******************************************************************************
+** Get count of characters in the inout buffer.
+*******************************************************************************/
+int Stat1401(DEVICE_EXTENSION * pdx)
+{
+	int iReturn;
+	mutex_lock(&pdx->io_mutex);	// Protect disconnect from new i/o
+	Allowi(pdx, false);	// make sure we allow pending chars
+	SendChars(pdx);		// in both directions
+	iReturn = pdx->dwNumInput;	// no lock as single read
+	mutex_unlock(&pdx->io_mutex);	// Protect disconnect from new i/o
+	return iReturn;
+}
+
+/****************************************************************************
+** LineCount
+**
+** Returns the number of newline chars in the buffer. There is no need for
+** any fancy interlocks as we only read the interrupt routine data, and the
+** system is arranged so nothing can be destroyed.
+****************************************************************************/
+int LineCount(DEVICE_EXTENSION * pdx)
+{
+	int iReturn = 0;	// will be count of line ends
+
+	mutex_lock(&pdx->io_mutex);	// Protect disconnect from new i/o
+	Allowi(pdx, false);	// Make sure char reads are running
+	SendChars(pdx);		// and send any buffered chars
+	spin_lock_irq(&pdx->charInLock);	// Get protection
+
+	if (pdx->dwNumInput > 0)	// worth looking?
+	{
+		unsigned int dwIndex = pdx->dwInBuffGet;	// start at first available
+		unsigned int dwEnd = pdx->dwInBuffPut;	// Position for search end
+		do {
+			if (pdx->inputBuffer[dwIndex++] == CR_CHAR)
+				++iReturn;	// inc count if CR
+
+			if (dwIndex >= INBUF_SZ)	// see if we fall off buff
+				dwIndex = 0;
+		}
+		while (dwIndex != dwEnd);	// go to last avaliable
+	}
+
+	spin_unlock_irq(&pdx->charInLock);
+	dev_dbg(&pdx->interface->dev, "LineCount returned %d", iReturn);
+	mutex_unlock(&pdx->io_mutex);	// Protect disconnect from new i/o
+	return iReturn;
+}
+
+/****************************************************************************
+** GetOutBufSpace
+**
+** Gets the space in the output buffer. Called from user code.
+*****************************************************************************/
+int GetOutBufSpace(DEVICE_EXTENSION * pdx)
+{
+	int iReturn;
+	mutex_lock(&pdx->io_mutex);	// Protect disconnect from new i/o
+	SendChars(pdx);		// send any buffered chars
+	iReturn = (int)(OUTBUF_SZ - pdx->dwNumOutput);	// no lock needed for single read
+	dev_dbg(&pdx->interface->dev, "OutBufSpace %d", iReturn);
+	mutex_unlock(&pdx->io_mutex);	// Protect disconnect from new i/o
+	return iReturn;
+}
+
+/****************************************************************************
+**
+** ClearArea
+**
+** Clears up a transfer area. This is always called in the context of a user
+** request, never from a call-back.
+****************************************************************************/
+int ClearArea(DEVICE_EXTENSION * pdx, int nArea)
+{
+	int iReturn = U14ERR_NOERROR;
+
+	if ((nArea < 0) || (nArea >= MAX_TRANSAREAS)) {
+		iReturn = U14ERR_BADAREA;
+		dev_err(&pdx->interface->dev, "%s Attempt to clear area %d",
+			__func__, nArea);
+	} else {
+		TRANSAREA *pTA = &pdx->rTransDef[nArea];	// to save typing
+		if (!pTA->bUsed)	// if not used...
+			iReturn = U14ERR_NOTSET;	// ...nothing to be done
+		else {
+			// We must save the memory we return as we shouldn't mess with memory while
+			// holding a spin lock.
+			struct page **pPages = 0;	// save page address list
+			int nPages = 0;	// and number of pages
+			int np;
+
+			dev_dbg(&pdx->interface->dev, "%s area %d", __func__,
+				nArea);
+			spin_lock_irq(&pdx->stagedLock);
+			if ((pdx->StagedId == nArea)
+			    && (pdx->dwDMAFlag > MODE_CHAR)) {
+				iReturn = U14ERR_UNLOCKFAIL;	// cannot delete as in use
+				dev_err(&pdx->interface->dev,
+					"%s call on area %d while active",
+					__func__, nArea);
+			} else {
+				pPages = pTA->pPages;	// save page address list
+				nPages = pTA->nPages;	// and page count
+				if (pTA->dwEventSz)	// if events flagging in use
+					wake_up_interruptible(&pTA->wqEvent);	// release anything that was waiting
+
+				if (pdx->bXFerWaiting
+				    && (pdx->rDMAInfo.wIdent == nArea))
+					pdx->bXFerWaiting = false;	// Cannot have pending xfer if area cleared
+
+				// Clean out the TRANSAREA except for the wait queue, which is at the end
+				// This sets bUsed to false and dwEventSz to 0 to say area not used and no events.
+				memset(pTA, 0,
+				       sizeof(TRANSAREA) -
+				       sizeof(wait_queue_head_t));
+			}
+			spin_unlock_irq(&pdx->stagedLock);
+
+			if (pPages)	// if we decided to release the memory
+			{
+				// Now we must undo the pinning down of the pages. We will assume the worst and mark
+				// all the pages as dirty. Don't be tempted to move this up above as you must not be
+				// holding a spin lock to do this stuff as it is not atomic.
+				dev_dbg(&pdx->interface->dev, "%s nPages=%d",
+					__func__, nPages);
+
+				for (np = 0; np < nPages; ++np) {
+					if (pPages[np]) {
+						SetPageDirty(pPages[np]);
+						page_cache_release(pPages[np]);
+					}
+				}
+
+				kfree(pPages);
+				dev_dbg(&pdx->interface->dev,
+					"%s kfree(pPages) done", __func__);
+			}
+		}
+	}
+
+	return iReturn;
+}
+
+/****************************************************************************
+** SetArea
+**
+** Sets up a transfer area - the functional part. Called by both
+** SetTransfer and SetCircular.
+****************************************************************************/
+static int SetArea(DEVICE_EXTENSION * pdx, int nArea, char __user * puBuf,
+		   unsigned int dwLength, bool bCircular, bool bCircToHost)
+{
+	// Start by working out the page aligned start of the area and the size
+	// of the area in pages, allowing for the start not being aligned and the
+	// end needing to be rounded up to a page boundary.
+	unsigned long ulStart = ((unsigned long)puBuf) & PAGE_MASK;
+	unsigned int ulOffset = ((unsigned long)puBuf) & (PAGE_SIZE - 1);
+	int len = (dwLength + ulOffset + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+	TRANSAREA *pTA = &pdx->rTransDef[nArea];	// to save typing
+	struct page **pPages = 0;	// space for page tables
+	int nPages = 0;		// and number of pages
+
+	int iReturn = ClearArea(pdx, nArea);	// see if OK to use this area
+	if ((iReturn != U14ERR_NOTSET) &&	// if not area unused and...
+	    (iReturn != U14ERR_NOERROR))	// ...not all OK, then...
+		return iReturn;	// ...we cannot use this area
+
+	if (!access_ok(VERIFY_WRITE, puBuf, dwLength))	// if we cannot access the memory...
+		return -EFAULT;	// ...then we are done
+
+	// Now allocate space to hold the page pointer and virtual address pointer tables
+	pPages =
+	    (struct page **)kmalloc(len * sizeof(struct page *), GFP_KERNEL);
+	if (!pPages) {
+		iReturn = U14ERR_NOMEMORY;
+		goto error;
+	}
+	dev_dbg(&pdx->interface->dev, "%s %p, length=%06x, circular %d",
+		__func__, puBuf, dwLength, bCircular);
+
+	// To pin down user pages we must first acquire the mapping semaphore.
+	down_read(&current->mm->mmap_sem);	// get memory map semaphore
+	nPages =
+	    get_user_pages(current, current->mm, ulStart, len, 1, 0, pPages, 0);
+	up_read(&current->mm->mmap_sem);	// release the semaphore
+	dev_dbg(&pdx->interface->dev, "%s nPages = %d", __func__, nPages);
+
+	if (nPages > 0)		// if we succeeded
+	{
+		// If you are tempted to use page_address (form LDD3), forget it. You MUST use
+		// kmap() or kmap_atomic() to get a virtual address. page_address will give you
+		// (null) or at least it does in this context with an x86 machine.
+		spin_lock_irq(&pdx->stagedLock);
+		pTA->lpvBuff = puBuf;	// keep start of region (user address)
+		pTA->dwBaseOffset = ulOffset;	// save offset in first page to start of xfer
+		pTA->dwLength = dwLength;	// Size if the region in bytes
+		pTA->pPages = pPages;	// list of pages that are used by buffer
+		pTA->nPages = nPages;	// number of pages
+
+		pTA->bCircular = bCircular;
+		pTA->bCircToHost = bCircToHost;
+
+		pTA->aBlocks[0].dwOffset = 0;
+		pTA->aBlocks[0].dwSize = 0;
+		pTA->aBlocks[1].dwOffset = 0;
+		pTA->aBlocks[1].dwSize = 0;
+		pTA->bUsed = true;	// This is now a used block
+
+		spin_unlock_irq(&pdx->stagedLock);
+		iReturn = U14ERR_NOERROR;	// say all was well
+	} else {
+		iReturn = U14ERR_LOCKFAIL;
+		goto error;
+	}
+
+	return iReturn;
+
+error:
+	kfree(pPages);
+	return iReturn;
+}
+
+/****************************************************************************
+** SetTransfer
+**
+** Sets up a transfer area record. If the area is already set, we attempt to
+** unset it. Unsetting will fail if the area is booked, and a transfer to that
+** area is in progress. Otherwise, we will release the area and re-assign it.
+****************************************************************************/
+int SetTransfer(DEVICE_EXTENSION * pdx, TRANSFERDESC __user * pTD)
+{
+	int iReturn;
+	TRANSFERDESC td;
+
+	if (copy_from_user(&td, pTD, sizeof(td)))
+		return -EFAULT;
+
+	mutex_lock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s area:%d, size:%08x", __func__,
+		td.wAreaNum, td.dwLength);
+	// The strange cast is done so that we don't get warnings in 32-bit linux about the size of the
+	// pointer. The pointer is always passed as a 64-bit object so that we don't have problems using
+	// a 32-bit program on a 64-bit system. unsigned long is 64-bits on a 64-bit system.
+	iReturn =
+	    SetArea(pdx, td.wAreaNum,
+		    (char __user *)((unsigned long)td.lpvBuff), td.dwLength,
+		    false, false);
+	mutex_unlock(&pdx->io_mutex);
+	return iReturn;
+}
+
+/****************************************************************************
+** UnSetTransfer
+** Erases a transfer area record
+****************************************************************************/
+int UnsetTransfer(DEVICE_EXTENSION * pdx, int nArea)
+{
+	int iReturn;
+	mutex_lock(&pdx->io_mutex);
+	iReturn = ClearArea(pdx, nArea);
+	mutex_unlock(&pdx->io_mutex);
+	return iReturn;
+}
+
+/****************************************************************************
+** SetEvent
+** Creates an event that we can test for based on a transfer to/from an area.
+** The area must be setup for a transfer. We attempt to simulate the Windows
+** driver behavior for events (as we don't actually use them), which is to
+** pretend that whatever the user asked for was achieved, so we return 1 if
+** try to create one, and 0 if they ask to remove (assuming all else was OK).
+****************************************************************************/
+int SetEvent(DEVICE_EXTENSION * pdx, TRANSFEREVENT __user * pTE)
+{
+	int iReturn = U14ERR_NOERROR;
+	TRANSFEREVENT te;
+
+	// get a local copy of the data
+	if (copy_from_user(&te, pTE, sizeof(te)))
+		return -EFAULT;
+
+	if (te.wAreaNum >= MAX_TRANSAREAS)	// the area must exist
+		return U14ERR_BADAREA;
+	else {
+		TRANSAREA *pTA = &pdx->rTransDef[te.wAreaNum];
+		mutex_lock(&pdx->io_mutex);	// make sure we have no competitor
+		spin_lock_irq(&pdx->stagedLock);
+		if (pTA->bUsed)	// area must be in use
+		{
+			pTA->dwEventSt = te.dwStart;	// set area regions
+			pTA->dwEventSz = te.dwLength;	// set size (0 cancels it)
+			pTA->bEventToHost = te.wFlags & 1;	// set the direction
+			pTA->iWakeUp = 0;	// zero the wake up count
+		} else
+			iReturn = U14ERR_NOTSET;
+		spin_unlock_irq(&pdx->stagedLock);
+		mutex_unlock(&pdx->io_mutex);
+	}
+	return iReturn ==
+	    U14ERR_NOERROR ? (te.iSetEvent ? 1 : U14ERR_NOERROR) : iReturn;
+}
+
+/****************************************************************************
+** WaitEvent
+** Sleep the process with a timeout waiting for an event. Returns the number
+** of times that a block met the event condition since we last cleared it or
+** 0 if timed out, or -ve error (bad area or not set, or signal).
+****************************************************************************/
+int WaitEvent(DEVICE_EXTENSION * pdx, int nArea, int msTimeOut)
+{
+	int iReturn;
+	if ((unsigned)nArea >= MAX_TRANSAREAS)
+		return U14ERR_BADAREA;
+	else {
+		int iWait;
+		TRANSAREA *pTA = &pdx->rTransDef[nArea];
+		msTimeOut = (msTimeOut * HZ + 999) / 1000;	// convert timeout to jiffies
+
+		// We cannot wait holding the mutex, but we check the flags while holding
+		// it. This may well be pointless as another thread could get in between
+		// releasing it and the wait call. However, this would have to clear the
+		// iWakeUp flag. However, the !pTA-bUsed may help us in this case.
+		mutex_lock(&pdx->io_mutex);	// make sure we have no competitor
+		if (!pTA->bUsed || !pTA->dwEventSz)	// check something to wait for...
+			return U14ERR_NOTSET;	// ...else we do nothing
+		mutex_unlock(&pdx->io_mutex);
+
+		if (msTimeOut)
+			iWait =
+			    wait_event_interruptible_timeout(pTA->wqEvent,
+							     pTA->iWakeUp
+							     || !pTA->bUsed,
+							     msTimeOut);
+		else
+			iWait =
+			    wait_event_interruptible(pTA->wqEvent, pTA->iWakeUp
+						     || !pTA->bUsed);
+		if (iWait)
+			iReturn = -ERESTARTSYS;	// oops - we have had a SIGNAL
+		else
+			iReturn = pTA->iWakeUp;	// else the wakeup count
+
+		spin_lock_irq(&pdx->stagedLock);
+		pTA->iWakeUp = 0;	// clear the flag
+		spin_unlock_irq(&pdx->stagedLock);
+	}
+	return iReturn;
+}
+
+/****************************************************************************
+** TestEvent
+** Test the event to see if a WaitEvent would return immediately. Returns the
+** number of times a block completed since the last call, or 0 if none or a
+** negative error.
+****************************************************************************/
+int TestEvent(DEVICE_EXTENSION * pdx, int nArea)
+{
+	int iReturn;
+	if ((unsigned)nArea >= MAX_TRANSAREAS)
+		iReturn = U14ERR_BADAREA;
+	else {
+		TRANSAREA *pTA = &pdx->rTransDef[nArea];
+		mutex_lock(&pdx->io_mutex);	// make sure we have no competitor
+		spin_lock_irq(&pdx->stagedLock);
+		iReturn = pTA->iWakeUp;	// get wakeup count since last call
+		pTA->iWakeUp = 0;	// clear the count
+		spin_unlock_irq(&pdx->stagedLock);
+		mutex_unlock(&pdx->io_mutex);
+	}
+	return iReturn;
+}
+
+/****************************************************************************
+** GetTransferInfo
+** Puts the current state of the 1401 in a TGET_TX_BLOCK.
+*****************************************************************************/
+int GetTransfer(DEVICE_EXTENSION * pdx, TGET_TX_BLOCK __user * pTX)
+{
+	int iReturn = U14ERR_NOERROR;
+	unsigned int dwIdent;
+
+	mutex_lock(&pdx->io_mutex);
+	dwIdent = pdx->StagedId;	// area ident for last xfer
+	if (dwIdent >= MAX_TRANSAREAS)
+		iReturn = U14ERR_BADAREA;
+	else {
+		// Return the best information we have - we don't have physical addresses
+		TGET_TX_BLOCK tx;
+		memset(&tx, 0, sizeof(tx));	// clean out local work structure
+		tx.size = pdx->rTransDef[dwIdent].dwLength;
+		tx.linear = (long long)((long)pdx->rTransDef[dwIdent].lpvBuff);
+		tx.avail = GET_TX_MAXENTRIES;	// how many blocks we could return
+		tx.used = 1;	// number we actually return
+		tx.entries[0].physical =
+		    (long long)(tx.linear + pdx->StagedOffset);
+		tx.entries[0].size = tx.size;
+
+		if (copy_to_user(pTX, &tx, sizeof(tx)))
+			iReturn = -EFAULT;
+	}
+	mutex_unlock(&pdx->io_mutex);
+	return iReturn;
+}
+
+/****************************************************************************
+** KillIO1401
+**
+** Empties the host i/o buffers
+****************************************************************************/
+int KillIO1401(DEVICE_EXTENSION * pdx)
+{
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+	mutex_lock(&pdx->io_mutex);
+	FlushOutBuff(pdx);
+	FlushInBuff(pdx);
+	mutex_unlock(&pdx->io_mutex);
+	return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+** BlkTransState
+** Returns a 0 or a 1 for whether DMA is happening. No point holding a mutex
+** for this as it only does one read.
+*****************************************************************************/
+int BlkTransState(DEVICE_EXTENSION * pdx)
+{
+	int iReturn = pdx->dwDMAFlag != MODE_CHAR;
+	dev_dbg(&pdx->interface->dev, "%s = %d", __func__, iReturn);
+	return iReturn;
+}
+
+/****************************************************************************
+** StateOf1401
+**
+** Puts the current state of the 1401 in the Irp return buffer.
+*****************************************************************************/
+int StateOf1401(DEVICE_EXTENSION * pdx)
+{
+	int iReturn;
+	mutex_lock(&pdx->io_mutex);
+
+	QuickCheck(pdx, false, false);	// get state up to date, no reset
+	iReturn = pdx->sCurrentState;
+
+	mutex_unlock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s = %d", __func__, iReturn);
+
+	return iReturn;
+}
+
+/****************************************************************************
+** StartSelfTest
+**
+** Initiates a self-test cycle. The assumption is that we have no interrupts
+** active, so we should make sure that this is the case.
+*****************************************************************************/
+int StartSelfTest(DEVICE_EXTENSION * pdx)
+{
+	int nGot;
+	mutex_lock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+	ced_draw_down(pdx);	// wait for, then kill outstanding Urbs
+	FlushInBuff(pdx);	// Clear out input buffer & pipe
+	FlushOutBuff(pdx);	// Clear output buffer & pipe
+//    ReadWrite_Cancel(pDeviceObject);        /* so things stay tidy */
+	pdx->dwDMAFlag = MODE_CHAR;	/* Clear DMA mode flags here */
+
+	nGot = usb_control_msg(pdx->udev, usb_rcvctrlpipe(pdx->udev, 0), DB_SELFTEST, (H_TO_D | VENDOR | DEVREQ), 0, 0, 0, 0, HZ);	// allow 1 second timeout
+	pdx->ulSelfTestTime = jiffies + HZ * 30;	// 30 seconds into the future
+
+	mutex_unlock(&pdx->io_mutex);
+	if (nGot < 0)
+		dev_err(&pdx->interface->dev, "%s err=%d", __func__, nGot);
+	return nGot < 0 ? U14ERR_FAIL : U14ERR_NOERROR;
+}
+
+/****************************************************************************
+** CheckSelfTest
+**
+** Check progress of a self-test cycle
+****************************************************************************/
+int CheckSelfTest(DEVICE_EXTENSION * pdx, TGET_SELFTEST __user * pGST)
+{
+	unsigned int state, error;
+	int iReturn;
+	TGET_SELFTEST gst;	// local work space
+	memset(&gst, 0, sizeof(gst));	// clear out the space (sets code 0)
+
+	mutex_lock(&pdx->io_mutex);
+
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+	iReturn = Get1401State(pdx, &state, &error);
+	if (iReturn == U14ERR_NOERROR)	// Only accept zero if it happens twice
+		iReturn = Get1401State(pdx, &state, &error);
+
+	if (iReturn != U14ERR_NOERROR)	// Self-test can cause comms errors
+	{			// so we assume still testing
+		dev_err(&pdx->interface->dev,
+			"%s Get1401State=%d, assuming still testing", __func__,
+			iReturn);
+		state = 0x80;	// Force still-testing, no error
+		error = 0;
+		iReturn = U14ERR_NOERROR;
+	}
+
+	if ((state == -1) && (error == -1))	// If Get1401State had problems
+	{
+		dev_err(&pdx->interface->dev,
+			"%s Get1401State failed, assuming still testing",
+			__func__);
+		state = 0x80;	// Force still-testing, no error
+		error = 0;
+	}
+
+	if ((state & 0xFF) == 0x80)	// If we are still in self-test
+	{
+		if (state & 0x00FF0000)	// Have we got an error?
+		{
+			gst.code = (state & 0x00FF0000) >> 16;	// read the error code
+			gst.x = error & 0x0000FFFF;	// Error data X
+			gst.y = (error & 0xFFFF0000) >> 16;	// and data Y
+			dev_dbg(&pdx->interface->dev, "Self-test error code %d",
+				gst.code);
+		} else		// No error, check for timeout
+		{
+			unsigned long ulNow = jiffies;	// get current time
+			if (time_after(ulNow, pdx->ulSelfTestTime)) {
+				gst.code = -2;	// Flag the timeout
+				dev_dbg(&pdx->interface->dev,
+					"Self-test timed-out");
+			} else
+				dev_dbg(&pdx->interface->dev,
+					"Self-test on-going");
+		}
+	} else {
+		gst.code = -1;	// Flag the test is done
+		dev_dbg(&pdx->interface->dev, "Self-test done");
+	}
+
+	if (gst.code < 0)	// If we have a problem or finished
+	{			// If using the 2890 we should reset properly
+		if ((pdx->nPipes == 4) && (pdx->s1401Type <= TYPEPOWER))
+			Is1401(pdx);	// Get 1401 reset and OK
+		else
+			QuickCheck(pdx, true, true);	// Otherwise check without reset unless problems
+	}
+	mutex_unlock(&pdx->io_mutex);
+
+	if (copy_to_user(pGST, &gst, sizeof(gst)))
+		return -EFAULT;
+
+	return iReturn;
+}
+
+/****************************************************************************
+** TypeOf1401
+**
+** Returns code for standard, plus, micro1401, power1401 or none
+****************************************************************************/
+int TypeOf1401(DEVICE_EXTENSION * pdx)
+{
+	int iReturn = TYPEUNKNOWN;
+	mutex_lock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+	switch (pdx->s1401Type) {
+	case TYPE1401:
+		iReturn = U14ERR_STD;
+		break;		// Handle these types directly
+	case TYPEPLUS:
+		iReturn = U14ERR_PLUS;
+		break;
+	case TYPEU1401:
+		iReturn = U14ERR_U1401;
+		break;
+	default:
+		if ((pdx->s1401Type >= TYPEPOWER) && (pdx->s1401Type <= 25))
+			iReturn = pdx->s1401Type + 4;	// We can calculate types
+		else		//  for up-coming 1401 designs
+			iReturn = TYPEUNKNOWN;	// Don't know or not there
+	}
+	dev_dbg(&pdx->interface->dev, "%s %d", __func__, iReturn);
+	mutex_unlock(&pdx->io_mutex);
+
+	return iReturn;
+}
+
+/****************************************************************************
+** TransferFlags
+**
+** Returns flags on block transfer abilities
+****************************************************************************/
+int TransferFlags(DEVICE_EXTENSION * pdx)
+{
+	int iReturn = U14TF_MULTIA | U14TF_DIAG |	// we always have multiple DMA area
+	    U14TF_NOTIFY | U14TF_CIRCTH;	// diagnostics, notify and circular
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+	mutex_lock(&pdx->io_mutex);
+	if (pdx->bIsUSB2)	// Set flag for USB2 if appropriate
+		iReturn |= U14TF_USB2;
+	mutex_unlock(&pdx->io_mutex);
+
+	return iReturn;
+}
+
+/***************************************************************************
+** DbgCmd1401
+** Issues a debug\diagnostic command to the 1401 along with a 32-bit datum
+** This is a utility command used for dbg operations.
+*/
+static int DbgCmd1401(DEVICE_EXTENSION * pdx, unsigned char cmd,
+		      unsigned int data)
+{
+	int iReturn;
+	dev_dbg(&pdx->interface->dev, "%s entry", __func__);
+	iReturn = usb_control_msg(pdx->udev, usb_sndctrlpipe(pdx->udev, 0), cmd, (H_TO_D | VENDOR | DEVREQ), (unsigned short)data, (unsigned short)(data >> 16), 0, 0, HZ);	// allow 1 second timeout
+	if (iReturn < 0)
+		dev_err(&pdx->interface->dev, "%s fail code=%d", __func__,
+			iReturn);
+
+	return iReturn;
+}
+
+/****************************************************************************
+** DbgPeek
+**
+** Execute the diagnostic peek operation. Uses address, width and repeats.
+****************************************************************************/
+int DbgPeek(DEVICE_EXTENSION * pdx, TDBGBLOCK __user * pDB)
+{
+	int iReturn;
+	TDBGBLOCK db;
+
+	if (copy_from_user(&db, pDB, sizeof(db)))
+		return -EFAULT;
+
+	mutex_lock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s @ %08x", __func__, db.iAddr);
+
+	iReturn = DbgCmd1401(pdx, DB_SETADD, db.iAddr);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_PEEK, 0);
+	mutex_unlock(&pdx->io_mutex);
+
+	return iReturn;
+}
+
+/****************************************************************************
+** DbgPoke
+**
+** Execute the diagnostic poke operation. Parameters are in the CSBLOCK struct
+** in order address, size, repeats and value to poke.
+****************************************************************************/
+int DbgPoke(DEVICE_EXTENSION * pdx, TDBGBLOCK __user * pDB)
+{
+	int iReturn;
+	TDBGBLOCK db;
+
+	if (copy_from_user(&db, pDB, sizeof(db)))
+		return -EFAULT;
+
+	mutex_lock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s @ %08x", __func__, db.iAddr);
+
+	iReturn = DbgCmd1401(pdx, DB_SETADD, db.iAddr);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_POKE, db.iData);
+	mutex_unlock(&pdx->io_mutex);
+
+	return iReturn;
+}
+
+/****************************************************************************
+** DbgRampData
+**
+** Execute the diagnostic ramp data operation. Parameters are in the CSBLOCK struct
+** in order address, default, enable mask, size and repeats.
+****************************************************************************/
+int DbgRampData(DEVICE_EXTENSION * pdx, TDBGBLOCK __user * pDB)
+{
+	int iReturn;
+	TDBGBLOCK db;
+
+	if (copy_from_user(&db, pDB, sizeof(db)))
+		return -EFAULT;
+
+	mutex_lock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s @ %08x", __func__, db.iAddr);
+
+	iReturn = DbgCmd1401(pdx, DB_SETADD, db.iAddr);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_SETDEF, db.iDefault);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_SETMASK, db.iMask);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_RAMPD, 0);
+	mutex_unlock(&pdx->io_mutex);
+
+	return iReturn;
+}
+
+/****************************************************************************
+** DbgRampAddr
+**
+** Execute the diagnostic ramp address operation
+****************************************************************************/
+int DbgRampAddr(DEVICE_EXTENSION * pdx, TDBGBLOCK __user * pDB)
+{
+	int iReturn;
+	TDBGBLOCK db;
+
+	if (copy_from_user(&db, pDB, sizeof(db)))
+		return -EFAULT;
+
+	mutex_lock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+	iReturn = DbgCmd1401(pdx, DB_SETDEF, db.iDefault);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_SETMASK, db.iMask);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+	if (iReturn == U14ERR_NOERROR)
+		iReturn = DbgCmd1401(pdx, DB_RAMPA, 0);
+	mutex_unlock(&pdx->io_mutex);
+
+	return iReturn;
+}
+
+/****************************************************************************
+** DbgGetData
+**
+** Retrieve the data resulting from the last debug Peek operation
+****************************************************************************/
+int DbgGetData(DEVICE_EXTENSION * pdx, TDBGBLOCK __user * pDB)
+{
+	int iReturn;
+	TDBGBLOCK db;
+	memset(&db, 0, sizeof(db));	// fill returned block with 0s
+
+	mutex_lock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+	// Read back the last peeked value from the 1401.
+	iReturn = usb_control_msg(pdx->udev, usb_rcvctrlpipe(pdx->udev, 0),
+				  DB_DATA, (D_TO_H | VENDOR | DEVREQ), 0, 0,
+				  &db.iData, sizeof(db.iData), HZ);
+	if (iReturn == sizeof(db.iData)) {
+		if (copy_to_user(pDB, &db, sizeof(db)))
+			iReturn = -EFAULT;
+		else
+			iReturn = U14ERR_NOERROR;
+	} else
+		dev_err(&pdx->interface->dev, "%s failed, code %d", __func__,
+			iReturn);
+
+	mutex_unlock(&pdx->io_mutex);
+
+	return iReturn;
+}
+
+/****************************************************************************
+** DbgStopLoop
+**
+** Stop any never-ending debug loop, we just call Get1401State for USB
+**
+****************************************************************************/
+int DbgStopLoop(DEVICE_EXTENSION * pdx)
+{
+	int iReturn;
+	unsigned int uState, uErr;
+
+	mutex_lock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+	iReturn = Get1401State(pdx, &uState, &uErr);
+	mutex_unlock(&pdx->io_mutex);
+
+	return iReturn;
+}
+
+/****************************************************************************
+** SetCircular
+**
+** Sets up a transfer area record for circular transfers. If the area is
+** already set, we attempt to unset it. Unsetting will fail if the area is
+** booked and a transfer to that area is in progress. Otherwise, we will
+** release the area and re-assign it.
+****************************************************************************/
+int SetCircular(DEVICE_EXTENSION * pdx, TRANSFERDESC __user * pTD)
+{
+	int iReturn;
+	bool bToHost;
+	TRANSFERDESC td;
+
+	if (copy_from_user(&td, pTD, sizeof(td)))
+		return -EFAULT;
+
+	mutex_lock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s area:%d, size:%08x", __func__,
+		td.wAreaNum, td.dwLength);
+	bToHost = td.eSize != 0;	// this is used as the tohost flag
+
+	// The strange cast is done so that we don't get warnings in 32-bit linux about the size of the
+	// pointer. The pointer is always passed as a 64-bit object so that we don't have problems using
+	// a 32-bit program on a 64-bit system. unsigned long is 64-bits on a 64-bit system.
+	iReturn =
+	    SetArea(pdx, td.wAreaNum,
+		    (char __user *)((unsigned long)td.lpvBuff), td.dwLength,
+		    true, bToHost);
+	mutex_unlock(&pdx->io_mutex);
+	return iReturn;
+}
+
+/****************************************************************************
+** GetCircBlock
+**
+** Return the next available block of circularly-transferred data.
+****************************************************************************/
+int GetCircBlock(DEVICE_EXTENSION * pdx, TCIRCBLOCK __user * pCB)
+{
+	int iReturn = U14ERR_NOERROR;
+	unsigned int nArea;
+	TCIRCBLOCK cb;
+
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+	if (copy_from_user(&cb, pCB, sizeof(cb)))
+		return -EFAULT;
+
+	mutex_lock(&pdx->io_mutex);
+
+	nArea = cb.nArea;	// Retrieve parameters first
+	cb.dwOffset = 0;	// set default result (nothing)
+	cb.dwSize = 0;
+
+	if (nArea < MAX_TRANSAREAS)	// The area number must be OK
+	{
+		TRANSAREA *pArea = &pdx->rTransDef[nArea];	// Pointer to relevant info
+		spin_lock_irq(&pdx->stagedLock);	// Lock others out
+
+		if ((pArea->bUsed) && (pArea->bCircular) &&	// Must be circular area
+		    (pArea->bCircToHost))	// For now at least must be to host
+		{
+			if (pArea->aBlocks[0].dwSize > 0)	// Got anything?
+			{
+				cb.dwOffset = pArea->aBlocks[0].dwOffset;
+				cb.dwSize = pArea->aBlocks[0].dwSize;
+				dev_dbg(&pdx->interface->dev,
+					"%s return block 0: %d bytes at %d",
+					__func__, cb.dwSize, cb.dwOffset);
+			}
+		} else
+			iReturn = U14ERR_NOTSET;
+
+		spin_unlock_irq(&pdx->stagedLock);
+	} else
+		iReturn = U14ERR_BADAREA;
+
+	if (copy_to_user(pCB, &cb, sizeof(cb)))
+		iReturn = -EFAULT;
+
+	mutex_unlock(&pdx->io_mutex);
+	return iReturn;
+}
+
+/****************************************************************************
+** FreeCircBlock
+**
+** Frees a block of circularly-transferred data and returns the next one.
+****************************************************************************/
+int FreeCircBlock(DEVICE_EXTENSION * pdx, TCIRCBLOCK __user * pCB)
+{
+	int iReturn = U14ERR_NOERROR;
+	unsigned int nArea, uStart, uSize;
+	TCIRCBLOCK cb;
+
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+	if (copy_from_user(&cb, pCB, sizeof(cb)))
+		return -EFAULT;
+
+	mutex_lock(&pdx->io_mutex);
+
+	nArea = cb.nArea;	// Retrieve parameters first
+	uStart = cb.dwOffset;
+	uSize = cb.dwSize;
+	cb.dwOffset = 0;	// then set default result (nothing)
+	cb.dwSize = 0;
+
+	if (nArea < MAX_TRANSAREAS)	// The area number must be OK
+	{
+		TRANSAREA *pArea = &pdx->rTransDef[nArea];	// Pointer to relevant info
+		spin_lock_irq(&pdx->stagedLock);	// Lock others out
+
+		if ((pArea->bUsed) && (pArea->bCircular) &&	// Must be circular area
+		    (pArea->bCircToHost))	// For now at least must be to host
+		{
+			bool bWaiting = false;
+
+			if ((pArea->aBlocks[0].dwSize >= uSize) &&	// Got anything?
+			    (pArea->aBlocks[0].dwOffset == uStart))	// Must be legal data
+			{
+				pArea->aBlocks[0].dwSize -= uSize;
+				pArea->aBlocks[0].dwOffset += uSize;
+				if (pArea->aBlocks[0].dwSize == 0)	// Have we emptied this block?
+				{
+					if (pArea->aBlocks[1].dwSize)	// Is there a second block?
+					{
+						pArea->aBlocks[0] = pArea->aBlocks[1];	// Copy down block 2 data
+						pArea->aBlocks[1].dwSize = 0;	// and mark the second block as unused
+						pArea->aBlocks[1].dwOffset = 0;
+					} else
+						pArea->aBlocks[0].dwOffset = 0;
+				}
+
+				dev_dbg(&pdx->interface->dev,
+					"%s free %d bytes at %d, return %d bytes at %d, wait=%d",
+					__func__, uSize, uStart,
+					pArea->aBlocks[0].dwSize,
+					pArea->aBlocks[0].dwOffset,
+					pdx->bXFerWaiting);
+
+				// Return the next available block of memory as well
+				if (pArea->aBlocks[0].dwSize > 0)	// Got anything?
+				{
+					cb.dwOffset =
+					    pArea->aBlocks[0].dwOffset;
+					cb.dwSize = pArea->aBlocks[0].dwSize;
+				}
+
+				bWaiting = pdx->bXFerWaiting;
+				if (bWaiting && pdx->bStagedUrbPending) {
+					dev_err(&pdx->interface->dev,
+						"%s ERROR: waiting xfer and staged Urb pending!",
+						__func__);
+					bWaiting = false;
+				}
+			} else {
+				dev_err(&pdx->interface->dev,
+					"%s ERROR: freeing %d bytes at %d, block 0 is %d bytes at %d",
+					__func__, uSize, uStart,
+					pArea->aBlocks[0].dwSize,
+					pArea->aBlocks[0].dwOffset);
+				iReturn = U14ERR_NOMEMORY;
+			}
+
+			// If we have one, kick off pending transfer
+			if (bWaiting)	// Got a block xfer waiting?
+			{
+				int RWMStat =
+				    ReadWriteMem(pdx, !pdx->rDMAInfo.bOutWard,
+						 pdx->rDMAInfo.wIdent,
+						 pdx->rDMAInfo.dwOffset,
+						 pdx->rDMAInfo.dwSize);
+				if (RWMStat != U14ERR_NOERROR)
+					dev_err(&pdx->interface->dev,
+						"%s rw setup failed %d",
+						__func__, RWMStat);
+			}
+		} else
+			iReturn = U14ERR_NOTSET;
+
+		spin_unlock_irq(&pdx->stagedLock);
+	} else
+		iReturn = U14ERR_BADAREA;
+
+	if (copy_to_user(pCB, &cb, sizeof(cb)))
+		return -EFAULT;
+
+	mutex_unlock(&pdx->io_mutex);
+	return iReturn;
+}

+ 345 - 0
drivers/staging/ced1401/ced_ioctl.h

@@ -0,0 +1,345 @@
+/*
+ * IOCTL calls for the CED1401 driver
+ * Copyright (C) 2010 Cambridge Electronic Design Ltd
+ * Author Greg P Smith (greg@ced.co.uk)
+ *
+ * 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.
+ */
+#ifndef __CED_IOCTL_H__
+#define __CED_IOCTL_H__
+
+#include <linux/ioctl.h>
+
+/* dma modes, only MODE_CHAR and MODE_LINEAR are used in this driver */
+#define MODE_CHAR		0
+#define MODE_LINEAR		1
+
+/****************************************************************************
+** TypeDefs
+*****************************************************************************/
+
+typedef unsigned short TBLOCKENTRY;	/* index the blk transfer table 0-7 */
+
+typedef struct TransferDesc {
+	long long lpvBuff;	/* address of transfer area (for 64 or 32 bit) */
+	unsigned int dwLength;	/* length of the area */
+	TBLOCKENTRY wAreaNum;	/* number of transfer area to set up */
+	short eSize;		/* element size - is tohost flag for circular */
+} TRANSFERDESC;
+
+typedef TRANSFERDESC * LPTRANSFERDESC;
+
+typedef struct TransferEvent {
+	unsigned int dwStart;		/* offset into the area */
+	unsigned int dwLength;		/* length of the region */
+	unsigned short wAreaNum;	/* the area number */
+	unsigned short wFlags;		/* bit 0 set for toHost */
+	int iSetEvent;			/* could be dummy in LINUX */
+} TRANSFEREVENT;
+
+#define MAX_TRANSFER_SIZE	0x4000		/* Maximum data bytes per IRP */
+#define MAX_AREA_LENGTH		0x100000	/* Maximum size of transfer area */
+#define MAX_TRANSAREAS		8		/* definitions for dma set up  */
+
+typedef struct TGetSelfTest {
+	int code;			/* self-test error code */
+	int x, y;			/* additional information */
+} TGET_SELFTEST;
+
+/* Debug block used for several commands. Not all fields are used for all commands. */
+typedef struct TDbgBlock {
+	int iAddr;			/* the address in the 1401 */
+	int iRepeats;			/* number of repeats */
+	int iWidth;			/* width in bytes 1, 2, 4 */
+	int iDefault;			/* default value */
+	int iMask;			/* mask to apply */
+	int iData;			/* data for poke, result for peek */
+} TDBGBLOCK;
+
+/* Used to collect information about a circular block from the device driver */
+typedef struct TCircBlock {
+	unsigned int nArea;		/* the area to collect information from */
+	unsigned int dwOffset;		/* offset into the area to the available block */
+	unsigned int dwSize;		/* size of the area */
+} TCIRCBLOCK;
+
+/* Used to clollect the 1401 status */
+typedef struct TCSBlock {
+	unsigned int uiState;
+	unsigned int uiError;
+} TCSBLOCK;
+
+/*
+ * As seen by the user, an ioctl call looks like: int ioctl(int fd, unsigned
+ * long cmd, char* argp); We will then have all sorts of variants on this that
+ * can be used to pass stuff to our driver. We will generate macros for each
+ * type of call so as to provide some sort of type safety in the calling:
+ */
+#define CED_MAGIC_IOC 0xce
+
+/* NBNB: READ and WRITE are from the point of view of the device, not user. */
+typedef struct ced_ioc_string {
+	int nChars;
+	char buffer[256];
+} CED_IOC_STRING;
+
+#define IOCTL_CED_SENDSTRING(n)		_IOC(_IOC_WRITE, CED_MAGIC_IOC, 2, n)
+
+#define IOCTL_CED_RESET1401		_IO(CED_MAGIC_IOC, 3)
+#define IOCTL_CED_GETCHAR		_IO(CED_MAGIC_IOC, 4)
+#define IOCTL_CED_SENDCHAR		_IO(CED_MAGIC_IOC, 5)
+#define IOCTL_CED_STAT1401		_IO(CED_MAGIC_IOC, 6)
+#define IOCTL_CED_LINECOUNT		_IO(CED_MAGIC_IOC, 7)
+#define IOCTL_CED_GETSTRING(nMax)	_IOC(_IOC_READ, CED_MAGIC_IOC, 8, nMax)
+
+#define IOCTL_CED_SETTRANSFER		_IOW(CED_MAGIC_IOC, 11, TRANSFERDESC)
+#define IOCTL_CED_UNSETTRANSFER		_IO(CED_MAGIC_IOC, 12)
+#define IOCTL_CED_SETEVENT		_IOW(CED_MAGIC_IOC, 13, TRANSFEREVENT)
+#define IOCTL_CED_GETOUTBUFSPACE	_IO(CED_MAGIC_IOC, 14)
+#define IOCTL_CED_GETBASEADDRESS	_IO(CED_MAGIC_IOC, 15)
+#define IOCTL_CED_GETDRIVERREVISION	_IO(CED_MAGIC_IOC, 16)
+
+#define IOCTL_CED_GETTRANSFER		_IOR(CED_MAGIC_IOC, 17, TGET_TX_BLOCK)
+#define IOCTL_CED_KILLIO1401		_IO(CED_MAGIC_IOC, 18)
+#define IOCTL_CED_BLKTRANSSTATE		_IO(CED_MAGIC_IOC, 19)
+
+#define IOCTL_CED_STATEOF1401		_IO(CED_MAGIC_IOC, 23)
+#define IOCTL_CED_GRAB1401		_IO(CED_MAGIC_IOC, 25)
+#define IOCTL_CED_FREE1401		_IO(CED_MAGIC_IOC, 26)
+#define IOCTL_CED_STARTSELFTEST		_IO(CED_MAGIC_IOC, 31)
+#define IOCTL_CED_CHECKSELFTEST		_IOR(CED_MAGIC_IOC, 32, TGET_SELFTEST)
+#define IOCTL_CED_TYPEOF1401		_IO(CED_MAGIC_IOC, 33)
+#define IOCTL_CED_TRANSFERFLAGS		_IO(CED_MAGIC_IOC, 34)
+
+#define IOCTL_CED_DBGPEEK		_IOW(CED_MAGIC_IOC, 35, TDBGBLOCK)
+#define IOCTL_CED_DBGPOKE		_IOW(CED_MAGIC_IOC, 36, TDBGBLOCK)
+#define IOCTL_CED_DBGRAMPDATA		_IOW(CED_MAGIC_IOC, 37, TDBGBLOCK)
+#define IOCTL_CED_DBGRAMPADDR		_IOW(CED_MAGIC_IOC, 38, TDBGBLOCK)
+#define IOCTL_CED_DBGGETDATA		_IOR(CED_MAGIC_IOC, 39, TDBGBLOCK)
+#define IOCTL_CED_DBGSTOPLOOP		_IO(CED_MAGIC_IOC, 40)
+#define IOCTL_CED_FULLRESET		_IO(CED_MAGIC_IOC, 41)
+#define IOCTL_CED_SETCIRCULAR		_IOW(CED_MAGIC_IOC, 42, TRANSFERDESC)
+#define IOCTL_CED_GETCIRCBLOCK		_IOWR(CED_MAGIC_IOC, 43, TCIRCBLOCK)
+#define IOCTL_CED_FREECIRCBLOCK		_IOWR(CED_MAGIC_IOC, 44, TCIRCBLOCK)
+#define IOCTL_CED_WAITEVENT		_IO(CED_MAGIC_IOC, 45)
+#define IOCTL_CED_TESTEVENT		_IO(CED_MAGIC_IOC, 46)
+
+#ifndef __KERNEL__
+/*
+ * If nothing said about return value, it is a U14ERR_... error code
+ * (U14ERR_NOERROR for none)
+ */
+inline int CED_SendString(int fh, const char *szText, int n)
+{
+	return ioctl(fh, IOCTL_CED_SENDSTRING(n), szText);
+}
+
+inline int CED_Reset1401(int fh)
+{
+	return ioctl(fh, IOCTL_CED_RESET1401);
+}
+
+/* Return the singe character or a -ve error code. */
+inline int CED_GetChar(int fh)
+{
+	return ioctl(fh, IOCTL_CED_GETCHAR);
+}
+
+/* Return character count in input buffer */
+inline int CED_Stat1401(int fh)
+{
+	return ioctl(fh, IOCTL_CED_STAT1401);
+}
+
+inline int CED_SendChar(int fh, char c)
+{
+	return ioctl(fh, IOCTL_CED_SENDCHAR, c);
+}
+
+inline int CED_LineCount(int fh)
+{
+	return ioctl(fh, IOCTL_CED_LINECOUNT);
+}
+
+/*
+ * return the count of characters returned. If the string was terminated by CR
+ * or 0, then the 0 is part of the count. Otherwise, we will add a zero if
+ * there is room, but it is not included in the count.  The return value is 0
+ * if there was nothing to read.
+ */
+inline int CED_GetString(int fh, char *szText, int nMax)
+{
+	return ioctl(fh, IOCTL_CED_GETSTRING(nMax), szText);
+}
+
+/* returns space in the output buffer. */
+inline int CED_GetOutBufSpace(int fh)
+{
+	return ioctl(fh, IOCTL_CED_GETOUTBUFSPACE);
+}
+
+/* This always returns -1 as not implemented. */
+inline int CED_GetBaseAddress(int fh)
+{
+	return ioctl(fh, IOCTL_CED_GETBASEADDRESS);
+}
+
+/* returns the major revision <<16 | minor revision. */
+inline int CED_GetDriverRevision(int fh)
+{
+	return ioctl(fh, IOCTL_CED_GETDRIVERREVISION);
+}
+
+inline int CED_SetTransfer(int fh, TRANSFERDESC *pTD)
+{
+	return ioctl(fh, IOCTL_CED_SETTRANSFER, pTD);
+}
+
+inline int CED_UnsetTransfer(int fh, int nArea)
+{
+	return ioctl(fh, IOCTL_CED_UNSETTRANSFER, nArea);
+}
+
+inline int CED_SetEvent(int fh, TRANSFEREVENT *pTE)
+{
+	return ioctl(fh, IOCTL_CED_SETEVENT, pTE);
+}
+
+inline int CED_GetTransfer(int fh, TGET_TX_BLOCK *pTX)
+{
+	return ioctl(fh, IOCTL_CED_GETTRANSFER, pTX);
+}
+
+inline int CED_KillIO1401(int fh)
+{
+	return ioctl(fh, IOCTL_CED_KILLIO1401);
+}
+
+/* returns 0 if no active DMA, 1 if active */
+inline int CED_BlkTransState(int fh)
+{
+	return ioctl(fh, IOCTL_CED_BLKTRANSSTATE);
+}
+
+inline int CED_StateOf1401(int fh)
+{
+	return ioctl(fh, IOCTL_CED_STATEOF1401);
+}
+
+inline int CED_Grab1401(int fh)
+{
+	return ioctl(fh, IOCTL_CED_GRAB1401);
+}
+
+inline int CED_Free1401(int fh)
+{
+	return ioctl(fh, IOCTL_CED_FREE1401);
+}
+
+inline int CED_StartSelfTest(int fh)
+{
+	return ioctl(fh, IOCTL_CED_STARTSELFTEST);
+}
+
+inline int CED_CheckSelfTest(int fh, TGET_SELFTEST *pGST)
+{
+	return ioctl(fh, IOCTL_CED_CHECKSELFTEST, pGST);
+}
+
+inline int CED_TypeOf1401(int fh)
+{
+	return ioctl(fh, IOCTL_CED_TYPEOF1401);
+}
+
+inline int CED_TransferFlags(int fh)
+{
+	return ioctl(fh, IOCTL_CED_TRANSFERFLAGS);
+}
+
+inline int CED_DbgPeek(int fh, TDBGBLOCK *pDB)
+{
+	return ioctl(fh, IOCTL_CED_DBGPEEK, pDB);
+}
+
+inline int CED_DbgPoke(int fh, TDBGBLOCK *pDB)
+{
+	return ioctl(fh, IOCTL_CED_DBGPOKE, pDB);
+}
+
+inline int CED_DbgRampData(int fh, TDBGBLOCK *pDB)
+{
+	return ioctl(fh, IOCTL_CED_DBGRAMPDATA, pDB);
+}
+
+inline int CED_DbgRampAddr(int fh, TDBGBLOCK *pDB)
+{
+	return ioctl(fh, IOCTL_CED_DBGRAMPADDR, pDB);
+}
+
+inline int CED_DbgGetData(int fh, TDBGBLOCK *pDB)
+{
+	return ioctl(fh, IOCTL_CED_DBGGETDATA, pDB);
+}
+
+inline int CED_DbgStopLoop(int fh)
+{
+	return ioctl(fh, IOCTL_CED_DBGSTOPLOOP);
+}
+
+inline int CED_FullReset(int fh)
+{
+	return ioctl(fh, IOCTL_CED_FULLRESET);
+}
+
+inline int CED_SetCircular(int fh, TRANSFERDESC *pTD)
+{
+	return ioctl(fh, IOCTL_CED_SETCIRCULAR, pTD);
+}
+
+inline int CED_GetCircBlock(int fh, TCIRCBLOCK *pCB)
+{
+	return ioctl(fh, IOCTL_CED_GETCIRCBLOCK, pCB);
+}
+
+inline int CED_FreeCircBlock(int fh, TCIRCBLOCK *pCB)
+{
+	return ioctl(fh, IOCTL_CED_FREECIRCBLOCK, pCB);
+}
+
+inline int CED_WaitEvent(int fh, int nArea, int msTimeOut)
+{
+	return ioctl(fh, IOCTL_CED_WAITEVENT, (nArea & 0xff)|(msTimeOut << 8));
+}
+
+inline int CED_TestEvent(int fh, int nArea)
+{
+	return ioctl(fh, IOCTL_CED_TESTEVENT, nArea);
+}
+#endif
+
+#ifdef NOTWANTEDYET
+#define IOCTL_CED_REGCALLBACK		_IO(CED_MAGIC_IOC, 9)	/* Not used */
+#define IOCTL_CED_GETMONITORBUF		_IO(CED_MAGIC_IOC, 10)	/* Not used */
+
+#define IOCTL_CED_BYTECOUNT		_IO(CED_MAGIC_IOC, 20)	/* Not used */
+#define IOCTL_CED_ZEROBLOCKCOUNT	_IO(CED_MAGIC_IOC, 21)	/* Not used */
+#define IOCTL_CED_STOPCIRCULAR		_IO(CED_MAGIC_IOC, 22)	/* Not used */
+
+#define IOCTL_CED_REGISTERS1401		_IO(CED_MAGIC_IOC, 24)	/* Not used */
+#define IOCTL_CED_STEP1401		_IO(CED_MAGIC_IOC, 27)	/* Not used */
+#define IOCTL_CED_SET1401REGISTERS	_IO(CED_MAGIC_IOC, 28)	/* Not used */
+#define IOCTL_CED_STEPTILL1401		_IO(CED_MAGIC_IOC, 29)	/* Not used */
+#define IOCTL_CED_SETORIN		_IO(CED_MAGIC_IOC, 30)	/* Not used */
+
+#endif
+
+/* __CED_IOCTL_H__ */
+#endif

+ 127 - 0
drivers/staging/ced1401/machine.h

@@ -0,0 +1,127 @@
+/*****************************************************************************
+**
+** machine.h
+**
+** Copyright (c) Cambridge Electronic Design Limited 1991,1992,2010
+**
+** 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+**
+** Contact CED: Cambridge Electronic Design Limited, Science Park, Milton Road
+**              Cambridge, CB6 0FE.
+**              www.ced.co.uk
+**              greg@ced.co.uk
+**
+** This file is included at the start of 'C' or 'C++' source file to define
+** things for cross-platform/compiler interoperability. This used to deal with
+** MSDOS/16-bit stuff, but this was all removed in Decemeber 2010. There are
+** three things to consider: Windows, LINUX, mac OSX (BSD Unix) and 32 vs 64
+** bit. At the time of writing (DEC 2010) there is a consensus on the following
+** and their unsigned equivalents:
+**
+** type       bits
+** char         8
+** short       16
+** int         32
+** long long   64
+**
+** long is a problem as it is always 64 bits on linux/unix and is always 32 bits
+** on windows.
+** On windows, we define _IS_WINDOWS_ and one of WIN32 or WIN64.
+** On linux we define LINUX
+** On Max OSX we define MACOSX
+**
+*/
+
+#ifndef __MACHINE_H__
+#define __MACHINE_H__
+#ifndef __KERNEL__
+#include <float.h>
+#include <limits.h>
+#endif
+
+/*
+** The initial section is to identify the operating system
+*/
+#if (defined(__linux__) || defined(_linux) || defined(__linux)) && !defined(LINUX)
+#define LINUX 1
+#endif
+
+#if (defined(__WIN32__) || defined(_WIN32)) && !defined(WIN32)
+#define WIN32 1
+#endif
+
+#if defined(__APPLE__)
+#define MACOSX
+#endif
+
+#if defined(_WIN64)
+#undef WIN32
+#undef WIN64
+#define WIN64 1
+#endif
+
+#if defined(WIN32) || defined(WIN64)
+#define _IS_WINDOWS_ 1
+#endif
+
+#if defined(LINUX) || defined(MAXOSX)
+    #define FAR
+
+    typedef int BOOL;       // To match Windows
+    typedef char * LPSTR;
+    typedef const char * LPCSTR;
+    typedef unsigned short WORD;
+    typedef unsigned int  DWORD;
+    typedef unsigned char  BYTE;
+    typedef BYTE  BOOLEAN;
+    typedef unsigned char UCHAR;
+    #define __packed __attribute__((packed))
+    typedef BYTE * LPBYTE;
+    #define HIWORD(x) (WORD)(((x)>>16) & 0xffff)
+    #define LOWORD(x) (WORD)((x) & 0xffff)
+#endif
+
+#ifdef _IS_WINDOWS_
+#include <windows.h>
+#define __packed
+#endif
+
+/*
+** Sort out the DllExport and DllImport macros. The GCC compiler has its own
+** syntax for this, though it also supports the MS specific __declspec() as
+** a synonym.
+*/
+#ifdef GNUC
+    #define DllExport __attribute__((dllexport))
+    #define DllImport __attribute__((dllimport))
+#endif
+
+#ifndef DllExport
+#ifdef _IS_WINDOWS_
+    #define DllExport __declspec(dllexport)
+    #define DllImport __declspec(dllimport)
+#else
+    #define DllExport
+    #define DllImport
+#endif
+#endif /* _IS_WINDOWS_ */
+
+    
+#ifndef TRUE
+   #define TRUE 1
+   #define FALSE 0
+#endif
+
+#endif

+ 1637 - 0
drivers/staging/ced1401/usb1401.c

@@ -0,0 +1,1637 @@
+/***********************************************************************************
+ CED1401 usb driver. This basic loading is based on the usb-skeleton.c code that is:
+ Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ Copyright (C) 2012 Alois Schloegl <alois.schloegl@ist.ac.at>
+ There is not a great deal of the skeleton left.
+
+ All the remainder dealing specifically with the CED1401 is based on drivers written
+ by CED for other systems (mainly Windows) and is:
+ Copyright (C) 2010 Cambridge Electronic Design Ltd
+ Author Greg P Smith (greg@ced.co.uk)
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+Endpoints
+*********
+There are 4 endpoints plus the control endpoint in the standard interface
+provided by most 1401s. The control endpoint is used for standard USB requests,
+plus various CED-specific transactions such as start self test, debug and get
+the 1401 status. The other endpoints are:
+
+ 1 Characters to the 1401
+ 2 Characters from the 1401
+ 3 Block data to the 1401
+ 4 Block data to the host.
+
+inside the driver these are indexed as an array from 0 to 3, transactions
+over the control endpoint are carried out using a separate mechanism. The
+use of the endpoints is mostly straightforward, with the driver issuing
+IO request packets (IRPs) as required to transfer data to and from the 1401.
+The handling of endpoint 2 is different because it is used for characters
+from the 1401, which can appear spontaneously and without any other driver
+activity - for example to repeatedly request DMA transfers in Spike2. The
+desired effect is achieved by using an interrupt endpoint which can be
+polled to see if it has data available, and writing the driver so that it
+always maintains a pending read IRP from that endpoint which will read the
+character data and terminate as soon as the 1401 makes data available. This
+works very well, some care is taken with when you kick off this character
+read IRP to avoid it being active when it is not wanted but generally it
+is running all the time.
+
+In the 2270, there are only three endpoints plus the control endpoint. In
+addition to the transactions mentioned above, the control endpoint is used
+to transfer character data to the 1401. The other endpoints are used as:
+
+ 1 Characters from the 1401
+ 2 Block data to the 1401
+ 3 Block data to the host.
+
+The type of interface available is specified by the interface subclass field
+in the interface descriptor provided by the 1401. See the USB_INT_ constants
+for the values that this field can hold.
+
+****************************************************************************
+Linux implementation
+
+Although Linux Device Drivers (3rd Edition) was a major source of information,
+it is very out of date. A lot of information was gleaned from the latest
+usb_skeleton.c code (you need to download the kernel sources to get this).
+
+To match the Windows version, everything is done using ioctl calls. All the
+device state is held in the DEVICE_EXTENSION (named to match Windows use).
+Block transfers are done by using get_user_pages() to pin down a list of
+pages that we hold a pointer to in the device driver. We also allocate a
+coherent transfer buffer of size STAGED_SZ (this must be a multiple of the
+bulk endpoint size so that the 1401 does not realise that we break large
+transfers down into smaller pieces). We use kmap_atomic() to get a kernel
+va for each page, as it is required, for copying; see CopyUserSpace().
+
+All character and data transfers are done using asynchronous IO. All Urbs are
+tracked by anchoring them. Status and debug ioctls are implemented with the
+synchronous non-Urb based transfers.
+*/
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/version.h>
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) )
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/uaccess.h>
+#endif
+
+#include "usb1401.h"
+
+/* Define these values to match your devices */
+#define USB_CED_VENDOR_ID	0x0525
+#define USB_CED_PRODUCT_ID	0xa0f0
+
+/* table of devices that work with this driver */
+static const struct usb_device_id ced_table[] = {
+	{USB_DEVICE(USB_CED_VENDOR_ID, USB_CED_PRODUCT_ID)},
+	{}			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ced_table);
+
+/* Get a minor range for your devices from the usb maintainer */
+#define USB_CED_MINOR_BASE	192
+
+/* our private defines. if this grows any larger, use your own .h file */
+#define MAX_TRANSFER		(PAGE_SIZE - 512)
+/* MAX_TRANSFER is chosen so that the VM is not stressed by
+   allocations > PAGE_SIZE and the number of packets in a page
+   is an integer 512 is the largest possible packet on EHCI */
+#define WRITES_IN_FLIGHT	8
+/* arbitrarily chosen */
+
+/* 
+The cause for these errors is that the driver makes use of the functions usb_buffer_alloc() and usb_buffer_free() which got renamed in kernel 2.6.35. This is stated in the Changelog:   USB: rename usb_buffer_alloc() and usb_buffer_free() users
+    For more clearance what the functions actually do,
+      usb_buffer_alloc() is renamed to usb_alloc_coherent()
+      usb_buffer_free()  is renamed to usb_free_coherent()
+   This is needed on Debian 2.6.32-5-amd64
+*/
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) )
+#define usb_alloc_coherent usb_buffer_alloc
+#define usb_free_coherent  usb_buffer_free
+#define noop_llseek NULL
+#endif
+
+static struct usb_driver ced_driver;
+
+static void ced_delete(struct kref *kref)
+{
+	DEVICE_EXTENSION *pdx = to_DEVICE_EXTENSION(kref);
+
+	// Free up the output buffer, then free the output urb. Note that the interface member
+	// of pdx will probably be NULL, so cannot be used to get to dev.
+	usb_free_coherent(pdx->udev, OUTBUF_SZ, pdx->pCoherCharOut,
+			  pdx->pUrbCharOut->transfer_dma);
+	usb_free_urb(pdx->pUrbCharOut);
+
+	// Do the same for chan input
+	usb_free_coherent(pdx->udev, INBUF_SZ, pdx->pCoherCharIn,
+			  pdx->pUrbCharIn->transfer_dma);
+	usb_free_urb(pdx->pUrbCharIn);
+
+	// Do the same for the block transfers
+	usb_free_coherent(pdx->udev, STAGED_SZ, pdx->pCoherStagedIO,
+			  pdx->pStagedUrb->transfer_dma);
+	usb_free_urb(pdx->pStagedUrb);
+
+	usb_put_dev(pdx->udev);
+	kfree(pdx);
+}
+
+// This is the driver end of the open() call from user space.
+static int ced_open(struct inode *inode, struct file *file)
+{
+	DEVICE_EXTENSION *pdx;
+	int retval = 0;
+	int subminor = iminor(inode);
+	struct usb_interface *interface =
+	    usb_find_interface(&ced_driver, subminor);
+	if (!interface) {
+		pr_err("%s - error, can't find device for minor %d", __func__,
+		       subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	pdx = usb_get_intfdata(interface);
+	if (!pdx) {
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	dev_dbg(&interface->dev, "%s got pdx", __func__);
+
+	/* increment our usage count for the device */
+	kref_get(&pdx->kref);
+
+	/* lock the device to allow correctly handling errors
+	 * in resumption */
+	mutex_lock(&pdx->io_mutex);
+
+	if (!pdx->open_count++) {
+		retval = usb_autopm_get_interface(interface);
+		if (retval) {
+			pdx->open_count--;
+			mutex_unlock(&pdx->io_mutex);
+			kref_put(&pdx->kref, ced_delete);
+			goto exit;
+		}
+	} else {		//uncomment this block if you want exclusive open
+		dev_err(&interface->dev, "%s fail: already open", __func__);
+		retval = -EBUSY;
+		pdx->open_count--;
+		mutex_unlock(&pdx->io_mutex);
+		kref_put(&pdx->kref, ced_delete);
+		goto exit;
+	}
+	/* prevent the device from being autosuspended */
+
+	/* save our object in the file's private structure */
+	file->private_data = pdx;
+	mutex_unlock(&pdx->io_mutex);
+
+exit:
+	return retval;
+}
+
+static int ced_release(struct inode *inode, struct file *file)
+{
+	DEVICE_EXTENSION *pdx = file->private_data;
+	if (pdx == NULL)
+		return -ENODEV;
+
+	dev_dbg(&pdx->interface->dev, "%s called", __func__);
+	mutex_lock(&pdx->io_mutex);
+	if (!--pdx->open_count && pdx->interface)	// Allow autosuspend
+		usb_autopm_put_interface(pdx->interface);
+	mutex_unlock(&pdx->io_mutex);
+
+	kref_put(&pdx->kref, ced_delete);	// decrement the count on our device
+	return 0;
+}
+
+static int ced_flush(struct file *file, fl_owner_t id)
+{
+	int res;
+	DEVICE_EXTENSION *pdx = file->private_data;
+	if (pdx == NULL)
+		return -ENODEV;
+
+	dev_dbg(&pdx->interface->dev, "%s char in pend=%d", __func__,
+		pdx->bReadCharsPending);
+
+	/* wait for io to stop */
+	mutex_lock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s got io_mutex", __func__);
+	ced_draw_down(pdx);
+
+	/* read out errors, leave subsequent opens a clean slate */
+	spin_lock_irq(&pdx->err_lock);
+	res = pdx->errors ? (pdx->errors == -EPIPE ? -EPIPE : -EIO) : 0;
+	pdx->errors = 0;
+	spin_unlock_irq(&pdx->err_lock);
+
+	mutex_unlock(&pdx->io_mutex);
+	dev_dbg(&pdx->interface->dev, "%s exit reached", __func__);
+
+	return res;
+}
+
+/***************************************************************************
+** CanAcceptIoRequests
+** If the device is removed, interface is set NULL. We also clear our pointer
+** from the interface, so we should make sure that pdx is not NULL. This will
+** not help with a device extension held by a file.
+** return true if can accept new io requests, else false
+*/
+static bool CanAcceptIoRequests(DEVICE_EXTENSION * pdx)
+{
+	return pdx && pdx->interface;	// Can we accept IO requests
+}
+
+/****************************************************************************
+** Callback routine to complete writes. This may need to fire off another
+** urb to complete the transfer.
+****************************************************************************/
+static void ced_writechar_callback(struct urb *pUrb)
+{
+	DEVICE_EXTENSION *pdx = pUrb->context;
+	int nGot = pUrb->actual_length;	// what we transferred
+
+	if (pUrb->status) {	// sync/async unlink faults aren't errors
+		if (!
+		    (pUrb->status == -ENOENT || pUrb->status == -ECONNRESET
+		     || pUrb->status == -ESHUTDOWN)) {
+			dev_err(&pdx->interface->dev,
+				"%s - nonzero write bulk status received: %d",
+				__func__, pUrb->status);
+		}
+
+		spin_lock(&pdx->err_lock);
+		pdx->errors = pUrb->status;
+		spin_unlock(&pdx->err_lock);
+		nGot = 0;	//  and tidy up again if so
+
+		spin_lock(&pdx->charOutLock);	// already at irq level
+		pdx->dwOutBuffGet = 0;	// Reset the output buffer
+		pdx->dwOutBuffPut = 0;
+		pdx->dwNumOutput = 0;	// Clear the char count
+		pdx->bPipeError[0] = 1;	// Flag an error for later
+		pdx->bSendCharsPending = false;	// Allow other threads again
+		spin_unlock(&pdx->charOutLock);	// already at irq level
+		dev_dbg(&pdx->interface->dev,
+			"%s - char out done, 0 chars sent", __func__);
+	} else {
+		dev_dbg(&pdx->interface->dev,
+			"%s - char out done, %d chars sent", __func__, nGot);
+		spin_lock(&pdx->charOutLock);	// already at irq level
+		pdx->dwNumOutput -= nGot;	// Now adjust the char send buffer
+		pdx->dwOutBuffGet += nGot;	// to match what we did
+		if (pdx->dwOutBuffGet >= OUTBUF_SZ)	// Can't do this any earlier as data could be overwritten
+			pdx->dwOutBuffGet = 0;
+
+		if (pdx->dwNumOutput > 0)	// if more to be done...
+		{
+			int nPipe = 0;	// The pipe number to use
+			int iReturn;
+			char *pDat = &pdx->outputBuffer[pdx->dwOutBuffGet];
+			unsigned int dwCount = pdx->dwNumOutput;	// maximum to send
+			if ((pdx->dwOutBuffGet + dwCount) > OUTBUF_SZ)	// does it cross buffer end?
+				dwCount = OUTBUF_SZ - pdx->dwOutBuffGet;
+			spin_unlock(&pdx->charOutLock);	// we are done with stuff that changes
+			memcpy(pdx->pCoherCharOut, pDat, dwCount);	// copy output data to the buffer
+			usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev,
+					  usb_sndbulkpipe(pdx->udev,
+							  pdx->epAddr[0]),
+					  pdx->pCoherCharOut, dwCount,
+					  ced_writechar_callback, pdx);
+			pdx->pUrbCharOut->transfer_flags |=
+			    URB_NO_TRANSFER_DMA_MAP;
+			usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted);	// in case we need to kill it
+			iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_ATOMIC);
+			dev_dbg(&pdx->interface->dev, "%s n=%d>%s<", __func__,
+				dwCount, pDat);
+			spin_lock(&pdx->charOutLock);	// grab lock for errors
+			if (iReturn) {
+				pdx->bPipeError[nPipe] = 1;	// Flag an error to be handled later
+				pdx->bSendCharsPending = false;	// Allow other threads again
+				usb_unanchor_urb(pdx->pUrbCharOut);
+				dev_err(&pdx->interface->dev,
+					"%s usb_submit_urb() returned %d",
+					__func__, iReturn);
+			}
+		} else
+			pdx->bSendCharsPending = false;	// Allow other threads again
+		spin_unlock(&pdx->charOutLock);	// already at irq level
+	}
+}
+
+/****************************************************************************
+** SendChars
+** Transmit the characters in the output buffer to the 1401. This may need
+** breaking down into multiple transfers.
+****************************************************************************/
+int SendChars(DEVICE_EXTENSION * pdx)
+{
+	int iReturn = U14ERR_NOERROR;
+
+	spin_lock_irq(&pdx->charOutLock);	// Protect ourselves
+
+	if ((!pdx->bSendCharsPending) &&	// Not currently sending
+	    (pdx->dwNumOutput > 0) &&	//  has characters to output
+	    (CanAcceptIoRequests(pdx)))	//  and current activity is OK
+	{
+		unsigned int dwCount = pdx->dwNumOutput;	// Get a copy of the character count
+		pdx->bSendCharsPending = true;	// Set flag to lock out other threads
+
+		dev_dbg(&pdx->interface->dev,
+			"Send %d chars to 1401, EP0 flag %d\n", dwCount,
+			pdx->nPipes == 3);
+		// If we have only 3 end points we must send the characters to the 1401 using EP0.
+		if (pdx->nPipes == 3) {
+			// For EP0 character transmissions to the 1401, we have to hang about until they
+			// are gone, as otherwise without more character IO activity they will never go.
+			unsigned int count = dwCount;	// Local char counter
+			unsigned int index = 0;	// The index into the char buffer
+
+			spin_unlock_irq(&pdx->charOutLock);	// Free spinlock as we call USBD
+
+			while ((count > 0) && (iReturn == U14ERR_NOERROR)) {
+				// We have to break the transfer up into 64-byte chunks because of a 2270 problem
+				int n = count > 64 ? 64 : count;	// Chars for this xfer, max of 64
+				int nSent = usb_control_msg(pdx->udev,
+							    usb_sndctrlpipe(pdx->udev, 0),	// use end point 0
+							    DB_CHARS,	// bRequest
+							    (H_TO_D | VENDOR | DEVREQ),	// to the device, vendor request to the device
+							    0, 0,	// value and index are both 0
+							    &pdx->outputBuffer[index],	// where to send from
+							    n,	// how much to send
+							    1000);	// timeout in jiffies
+				if (nSent <= 0) {
+					iReturn = nSent ? nSent : -ETIMEDOUT;	// if 0 chars says we timed out
+					dev_err(&pdx->interface->dev,
+						"Send %d chars by EP0 failed: %d",
+						n, iReturn);
+				} else {
+					dev_dbg(&pdx->interface->dev,
+						"Sent %d chars by EP0", n);
+					count -= nSent;
+					index += nSent;
+				}
+			}
+
+			spin_lock_irq(&pdx->charOutLock);	// Protect pdx changes, released by general code
+			pdx->dwOutBuffGet = 0;	// so reset the output buffer
+			pdx->dwOutBuffPut = 0;
+			pdx->dwNumOutput = 0;	// and clear the buffer count
+			pdx->bSendCharsPending = false;	// Allow other threads again
+		} else {	// Here for sending chars normally - we hold the spin lock
+			int nPipe = 0;	// The pipe number to use
+			char *pDat = &pdx->outputBuffer[pdx->dwOutBuffGet];
+
+			if ((pdx->dwOutBuffGet + dwCount) > OUTBUF_SZ)	// does it cross buffer end?
+				dwCount = OUTBUF_SZ - pdx->dwOutBuffGet;
+			spin_unlock_irq(&pdx->charOutLock);	// we are done with stuff that changes
+			memcpy(pdx->pCoherCharOut, pDat, dwCount);	// copy output data to the buffer
+			usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev,
+					  usb_sndbulkpipe(pdx->udev,
+							  pdx->epAddr[0]),
+					  pdx->pCoherCharOut, dwCount,
+					  ced_writechar_callback, pdx);
+			pdx->pUrbCharOut->transfer_flags |=
+			    URB_NO_TRANSFER_DMA_MAP;
+			usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted);
+			iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_KERNEL);
+			spin_lock_irq(&pdx->charOutLock);	// grab lock for errors
+			if (iReturn) {
+				pdx->bPipeError[nPipe] = 1;	// Flag an error to be handled later
+				pdx->bSendCharsPending = false;	// Allow other threads again
+				usb_unanchor_urb(pdx->pUrbCharOut);	// remove from list of active urbs
+			}
+		}
+	} else if (pdx->bSendCharsPending && (pdx->dwNumOutput > 0))
+		dev_dbg(&pdx->interface->dev,
+			"SendChars bSendCharsPending:true");
+
+	dev_dbg(&pdx->interface->dev, "SendChars exit code: %d", iReturn);
+	spin_unlock_irq(&pdx->charOutLock);	// Now let go of the spinlock
+	return iReturn;
+}
+
+/***************************************************************************
+** CopyUserSpace
+** This moves memory between pinned down user space and the pCoherStagedIO
+** memory buffer we use for transfers. Copy n bytes in the directions that
+** is defined by pdx->StagedRead. The user space is determined by the area
+** in pdx->StagedId and the offset in pdx->StagedDone. The user
+** area may well not start on a page boundary, so allow for that.
+**
+** We have a table of physical pages that describe the area, so we can use
+** this to get a virtual address that the kernel can use.
+**
+** pdx  Is our device extension which holds all we know about the transfer.
+** n    The number of bytes to move one way or the other.
+***************************************************************************/
+static void CopyUserSpace(DEVICE_EXTENSION * pdx, int n)
+{
+	unsigned int nArea = pdx->StagedId;
+	if (nArea < MAX_TRANSAREAS) {
+		TRANSAREA *pArea = &pdx->rTransDef[nArea];	// area to be used
+		unsigned int dwOffset =
+		    pdx->StagedDone + pdx->StagedOffset + pArea->dwBaseOffset;
+		char *pCoherBuf = pdx->pCoherStagedIO;	// coherent buffer
+		if (!pArea->bUsed) {
+			dev_err(&pdx->interface->dev, "%s area %d unused",
+				__func__, nArea);
+			return;
+		}
+
+		while (n) {
+			int nPage = dwOffset >> PAGE_SHIFT;	// page number in table
+			if (nPage < pArea->nPages) {
+				char *pvAddress =
+				    (char *)kmap_atomic(pArea->pPages[nPage]);
+				if (pvAddress) {
+					unsigned int uiPageOff = dwOffset & (PAGE_SIZE - 1);	// offset into the page
+					size_t uiXfer = PAGE_SIZE - uiPageOff;	// max to transfer on this page
+					if (uiXfer > n)	// limit byte count if too much
+						uiXfer = n;	// for the page
+					if (pdx->StagedRead)
+						memcpy(pvAddress + uiPageOff,
+						       pCoherBuf, uiXfer);
+					else
+						memcpy(pCoherBuf,
+						       pvAddress + uiPageOff,
+						       uiXfer);
+					kunmap_atomic(pvAddress);
+					dwOffset += uiXfer;
+					pCoherBuf += uiXfer;
+					n -= uiXfer;
+				} else {
+					dev_err(&pdx->interface->dev,
+						"%s did not map page %d",
+						__func__, nPage);
+					return;
+				}
+
+			} else {
+				dev_err(&pdx->interface->dev,
+					"%s exceeded pages %d", __func__,
+					nPage);
+				return;
+			}
+		}
+	} else
+		dev_err(&pdx->interface->dev, "%s bad area %d", __func__,
+			nArea);
+}
+
+// Forward declarations for stuff used circularly
+static int StageChunk(DEVICE_EXTENSION * pdx);
+/***************************************************************************
+** ReadWrite_Complete
+**
+**  Completion routine for our staged read/write Irps
+*/
+static void staged_callback(struct urb *pUrb)
+{
+	DEVICE_EXTENSION *pdx = pUrb->context;
+	unsigned int nGot = pUrb->actual_length;	// what we transferred
+	bool bCancel = false;
+	bool bRestartCharInput;	// used at the end
+
+	spin_lock(&pdx->stagedLock);	// stop ReadWriteMem() action while this routine is running
+	pdx->bStagedUrbPending = false;	// clear the flag for staged IRP pending
+
+	if (pUrb->status) {	// sync/async unlink faults aren't errors
+		if (!
+		    (pUrb->status == -ENOENT || pUrb->status == -ECONNRESET
+		     || pUrb->status == -ESHUTDOWN)) {
+			dev_err(&pdx->interface->dev,
+				"%s - nonzero write bulk status received: %d",
+				__func__, pUrb->status);
+		} else
+			dev_info(&pdx->interface->dev,
+				 "%s - staged xfer cancelled", __func__);
+
+		spin_lock(&pdx->err_lock);
+		pdx->errors = pUrb->status;
+		spin_unlock(&pdx->err_lock);
+		nGot = 0;	//  and tidy up again if so
+		bCancel = true;
+	} else {
+		dev_dbg(&pdx->interface->dev, "%s %d chars xferred", __func__,
+			nGot);
+		if (pdx->StagedRead)	// if reading, save to user space
+			CopyUserSpace(pdx, nGot);	// copy from buffer to user
+		if (nGot == 0)
+			dev_dbg(&pdx->interface->dev, "%s ZLP", __func__);
+	}
+
+	// Update the transfer length based on the TransferBufferLength value in the URB
+	pdx->StagedDone += nGot;
+
+	dev_dbg(&pdx->interface->dev, "%s, done %d bytes of %d", __func__,
+		pdx->StagedDone, pdx->StagedLength);
+
+	if ((pdx->StagedDone == pdx->StagedLength) ||	// If no more to do
+	    (bCancel))		// or this IRP was cancelled
+	{
+		TRANSAREA *pArea = &pdx->rTransDef[pdx->StagedId];	// Transfer area info
+		dev_dbg(&pdx->interface->dev,
+			"%s transfer done, bytes %d, cancel %d", __func__,
+			pdx->StagedDone, bCancel);
+
+		// Here is where we sort out what to do with this transfer if using a circular buffer. We have
+		//  a completed transfer that can be assumed to fit into the transfer area. We should be able to
+		//  add this to the end of a growing block or to use it to start a new block unless the code
+		//  that calculates the offset to use (in ReadWriteMem) is totally duff.
+		if ((pArea->bCircular) && (pArea->bCircToHost) && (!bCancel) &&	// Time to sort out circular buffer info?
+		    (pdx->StagedRead))	// Only for tohost transfers for now
+		{
+			if (pArea->aBlocks[1].dwSize > 0)	// If block 1 is in use we must append to it
+			{
+				if (pdx->StagedOffset ==
+				    (pArea->aBlocks[1].dwOffset +
+				     pArea->aBlocks[1].dwSize)) {
+					pArea->aBlocks[1].dwSize +=
+					    pdx->StagedLength;
+					dev_dbg(&pdx->interface->dev,
+						"RWM_Complete, circ block 1 now %d bytes at %d",
+						pArea->aBlocks[1].dwSize,
+						pArea->aBlocks[1].dwOffset);
+				} else {
+					// Here things have gone very, very, wrong, but I cannot see how this can actually be achieved
+					pArea->aBlocks[1].dwOffset =
+					    pdx->StagedOffset;
+					pArea->aBlocks[1].dwSize =
+					    pdx->StagedLength;
+					dev_err(&pdx->interface->dev,
+						"%s ERROR, circ block 1 re-started %d bytes at %d",
+						__func__,
+						pArea->aBlocks[1].dwSize,
+						pArea->aBlocks[1].dwOffset);
+				}
+			} else	// If block 1 is not used, we try to add to block 0
+			{
+				if (pArea->aBlocks[0].dwSize > 0)	// Got stored block 0 information?
+				{	// Must append onto the existing block 0
+					if (pdx->StagedOffset ==
+					    (pArea->aBlocks[0].dwOffset +
+					     pArea->aBlocks[0].dwSize)) {
+						pArea->aBlocks[0].dwSize += pdx->StagedLength;	// Just add this transfer in
+						dev_dbg(&pdx->interface->dev,
+							"RWM_Complete, circ block 0 now %d bytes at %d",
+							pArea->aBlocks[0].
+							dwSize,
+							pArea->aBlocks[0].
+							dwOffset);
+					} else	// If it doesn't append, put into new block 1
+					{
+						pArea->aBlocks[1].dwOffset =
+						    pdx->StagedOffset;
+						pArea->aBlocks[1].dwSize =
+						    pdx->StagedLength;
+						dev_dbg(&pdx->interface->dev,
+							"RWM_Complete, circ block 1 started %d bytes at %d",
+							pArea->aBlocks[1].
+							dwSize,
+							pArea->aBlocks[1].
+							dwOffset);
+					}
+				} else	// No info stored yet, just save in block 0
+				{
+					pArea->aBlocks[0].dwOffset =
+					    pdx->StagedOffset;
+					pArea->aBlocks[0].dwSize =
+					    pdx->StagedLength;
+					dev_dbg(&pdx->interface->dev,
+						"RWM_Complete, circ block 0 started %d bytes at %d",
+						pArea->aBlocks[0].dwSize,
+						pArea->aBlocks[0].dwOffset);
+				}
+			}
+		}
+
+		if (!bCancel)	// Don't generate an event if cancelled
+		{
+			dev_dbg(&pdx->interface->dev,
+				"RWM_Complete,  bCircular %d, bToHost %d, eStart %d, eSize %d",
+				pArea->bCircular, pArea->bEventToHost,
+				pArea->dwEventSt, pArea->dwEventSz);
+			if ((pArea->dwEventSz) &&	// Set a user-mode event...
+			    (pdx->StagedRead == pArea->bEventToHost))	// ...on transfers in this direction?
+			{
+				int iWakeUp = 0;	// assume
+				// If we have completed the right sort of DMA transfer then set the event to notify
+				//   the user code to wake up anyone that is waiting.
+				if ((pArea->bCircular) &&	// Circular areas use a simpler test
+				    (pArea->bCircToHost))	// only in supported direction
+				{	// Is total data waiting up to size limit?
+					unsigned int dwTotal =
+					    pArea->aBlocks[0].dwSize +
+					    pArea->aBlocks[1].dwSize;
+					iWakeUp = (dwTotal >= pArea->dwEventSz);
+				} else {
+					unsigned int transEnd =
+					    pdx->StagedOffset +
+					    pdx->StagedLength;
+					unsigned int eventEnd =
+					    pArea->dwEventSt + pArea->dwEventSz;
+					iWakeUp = (pdx->StagedOffset < eventEnd)
+					    && (transEnd > pArea->dwEventSt);
+				}
+
+				if (iWakeUp) {
+					dev_dbg(&pdx->interface->dev,
+						"About to set event to notify app");
+					wake_up_interruptible(&pArea->wqEvent);	// wake up waiting processes
+					++pArea->iWakeUp;	// increment wakeup count
+				}
+			}
+		}
+
+		pdx->dwDMAFlag = MODE_CHAR;	// Switch back to char mode before ReadWriteMem call
+
+		if (!bCancel)	// Don't look for waiting transfer if cancelled
+		{
+			// If we have a transfer waiting, kick it off
+			if (pdx->bXFerWaiting)	// Got a block xfer waiting?
+			{
+				int iReturn;
+				dev_info(&pdx->interface->dev,
+					 "*** RWM_Complete *** pending transfer will now be set up!!!");
+				iReturn =
+				    ReadWriteMem(pdx, !pdx->rDMAInfo.bOutWard,
+						 pdx->rDMAInfo.wIdent,
+						 pdx->rDMAInfo.dwOffset,
+						 pdx->rDMAInfo.dwSize);
+
+				if (iReturn)
+					dev_err(&pdx->interface->dev,
+						"RWM_Complete rw setup failed %d",
+						iReturn);
+			}
+		}
+
+	} else			// Here for more to do
+		StageChunk(pdx);	// fire off the next bit
+
+	// While we hold the stagedLock, see if we should reallow character input ints
+	// Don't allow if cancelled, or if a new block has started or if there is a waiting block.
+	// This feels wrong as we should ask which spin lock protects dwDMAFlag.
+	bRestartCharInput = !bCancel && (pdx->dwDMAFlag == MODE_CHAR)
+	    && !pdx->bXFerWaiting;
+
+	spin_unlock(&pdx->stagedLock);	// Finally release the lock again
+
+	// This is not correct as dwDMAFlag is protected by the staged lock, but it is treated
+	// in Allowi as if it were protected by the char lock. In any case, most systems will
+	// not be upset by char input during DMA... sigh. Needs sorting out.
+	if (bRestartCharInput)	// may be out of date, but...
+		Allowi(pdx, true);	// ...Allowi tests a lock too.
+	dev_dbg(&pdx->interface->dev, "%s done", __func__);
+}
+
+/****************************************************************************
+** StageChunk
+**
+** Generates the next chunk of data making up a staged transfer.
+**
+** The calling code must have acquired the staging spinlock before calling
+**  this function, and is responsible for releasing it. We are at callback level.
+****************************************************************************/
+static int StageChunk(DEVICE_EXTENSION * pdx)
+{
+	int iReturn = U14ERR_NOERROR;
+	unsigned int ChunkSize;
+	int nPipe = pdx->StagedRead ? 3 : 2;	// The pipe number to use for reads or writes
+	if (pdx->nPipes == 3)
+		nPipe--;	// Adjust for the 3-pipe case
+	if (nPipe < 0)		// and trap case that should never happen
+		return U14ERR_FAIL;
+
+	if (!CanAcceptIoRequests(pdx))	// got sudden remove?
+	{
+		dev_info(&pdx->interface->dev, "%s sudden remove, giving up",
+			 __func__);
+		return U14ERR_FAIL;	// could do with a better error
+	}
+
+	ChunkSize = (pdx->StagedLength - pdx->StagedDone);	// transfer length remaining
+	if (ChunkSize > STAGED_SZ)	// make sure to keep legal
+		ChunkSize = STAGED_SZ;	//  limit to max allowed
+
+	if (!pdx->StagedRead)	// if writing...
+		CopyUserSpace(pdx, ChunkSize);	// ...copy data into the buffer
+
+	usb_fill_bulk_urb(pdx->pStagedUrb, pdx->udev,
+			  pdx->StagedRead ? usb_rcvbulkpipe(pdx->udev,
+							    pdx->
+							    epAddr[nPipe]) :
+			  usb_sndbulkpipe(pdx->udev, pdx->epAddr[nPipe]),
+			  pdx->pCoherStagedIO, ChunkSize, staged_callback, pdx);
+	pdx->pStagedUrb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	usb_anchor_urb(pdx->pStagedUrb, &pdx->submitted);	// in case we need to kill it
+	iReturn = usb_submit_urb(pdx->pStagedUrb, GFP_ATOMIC);
+	if (iReturn) {
+		usb_unanchor_urb(pdx->pStagedUrb);	// kill it
+		pdx->bPipeError[nPipe] = 1;	// Flag an error to be handled later
+		dev_err(&pdx->interface->dev, "%s submit urb failed, code %d",
+			__func__, iReturn);
+	} else
+		pdx->bStagedUrbPending = true;	// Set the flag for staged URB pending
+	dev_dbg(&pdx->interface->dev, "%s done so far:%d, this size:%d",
+		__func__, pdx->StagedDone, ChunkSize);
+
+	return iReturn;
+}
+
+/***************************************************************************
+** ReadWriteMem
+**
+** This routine is used generally for block read and write operations.
+** Breaks up a read or write in to specified sized chunks, as specified by pipe
+** information on maximum transfer size.
+**
+** Any code that calls this must be holding the stagedLock
+**
+** Arguments:
+**    DeviceObject - pointer to our FDO (Functional Device Object)
+**    Read - TRUE for read, FALSE for write. This is from POV of the driver
+**    wIdent - the transfer area number - defines memory area and more.
+**    dwOffs - the start offset within the transfer area of the start of this
+**             transfer.
+**    dwLen - the number of bytes to transfer.
+*/
+int ReadWriteMem(DEVICE_EXTENSION * pdx, bool Read, unsigned short wIdent,
+		 unsigned int dwOffs, unsigned int dwLen)
+{
+	TRANSAREA *pArea = &pdx->rTransDef[wIdent];	// Transfer area info
+
+	if (!CanAcceptIoRequests(pdx))	// Are we in a state to accept new requests?
+	{
+		dev_err(&pdx->interface->dev, "%s can't accept requests",
+			__func__);
+		return U14ERR_FAIL;
+	}
+
+	dev_dbg(&pdx->interface->dev,
+		"%s xfer %d bytes to %s, offset %d, area %d", __func__, dwLen,
+		Read ? "host" : "1401", dwOffs, wIdent);
+
+	// Amazingly, we can get an escape sequence back before the current staged Urb is done, so we
+	//  have to check for this situation and, if so, wait until all is OK.
+	if (pdx->bStagedUrbPending) {
+		pdx->bXFerWaiting = true;	// Flag we are waiting
+		dev_info(&pdx->interface->dev,
+			 "%s xfer is waiting, as previous staged pending",
+			 __func__);
+		return U14ERR_NOERROR;
+	}
+
+	if (dwLen == 0)		// allow 0-len read or write; just return success
+	{
+		dev_dbg(&pdx->interface->dev,
+			"%s OK; zero-len read/write request", __func__);
+		return U14ERR_NOERROR;
+	}
+
+	if ((pArea->bCircular) &&	// Circular transfer?
+	    (pArea->bCircToHost) && (Read))	// In a supported direction
+	{			// If so, we sort out offset ourself
+		bool bWait = false;	// Flag for transfer having to wait
+
+		dev_dbg(&pdx->interface->dev,
+			"Circular buffers are %d at %d and %d at %d",
+			pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset,
+			pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset);
+		if (pArea->aBlocks[1].dwSize > 0)	// Using the second block already?
+		{
+			dwOffs = pArea->aBlocks[1].dwOffset + pArea->aBlocks[1].dwSize;	// take offset from that
+			bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset;	// Wait if will overwrite block 0?
+			bWait |= (dwOffs + dwLen) > pArea->dwLength;	// or if it overflows the buffer
+		} else		// Area 1 not in use, try to use area 0
+		{
+			if (pArea->aBlocks[0].dwSize == 0)	// Reset block 0 if not in use
+				pArea->aBlocks[0].dwOffset = 0;
+			dwOffs =
+			    pArea->aBlocks[0].dwOffset +
+			    pArea->aBlocks[0].dwSize;
+			if ((dwOffs + dwLen) > pArea->dwLength)	// Off the end of the buffer?
+			{
+				pArea->aBlocks[1].dwOffset = 0;	// Set up to use second block
+				dwOffs = 0;
+				bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset;	// Wait if will overwrite block 0?
+				bWait |= (dwOffs + dwLen) > pArea->dwLength;	// or if it overflows the buffer
+			}
+		}
+
+		if (bWait)	// This transfer will have to wait?
+		{
+			pdx->bXFerWaiting = true;	// Flag we are waiting
+			dev_dbg(&pdx->interface->dev,
+				"%s xfer waiting for circular buffer space",
+				__func__);
+			return U14ERR_NOERROR;
+		}
+
+		dev_dbg(&pdx->interface->dev,
+			"%s circular xfer, %d bytes starting at %d", __func__,
+			dwLen, dwOffs);
+	}
+	// Save the parameters for the read\write transfer
+	pdx->StagedRead = Read;	// Save the parameters for this read
+	pdx->StagedId = wIdent;	// ID allows us to get transfer area info
+	pdx->StagedOffset = dwOffs;	// The area within the transfer area
+	pdx->StagedLength = dwLen;
+	pdx->StagedDone = 0;	// Initialise the byte count
+	pdx->dwDMAFlag = MODE_LINEAR;	// Set DMA mode flag at this point
+	pdx->bXFerWaiting = false;	// Clearly not a transfer waiting now
+
+//    KeClearEvent(&pdx->StagingDoneEvent);           // Clear the transfer done event
+	StageChunk(pdx);	// fire off the first chunk
+
+	return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+**
+** ReadChar
+**
+** Reads a character a buffer. If there is no more
+**  data we return FALSE. Used as part of decoding a DMA request.
+**
+****************************************************************************/
+static bool ReadChar(unsigned char *pChar, char *pBuf, unsigned int *pdDone,
+		     unsigned int dGot)
+{
+	bool bRead = false;
+	unsigned int dDone = *pdDone;
+
+	if (dDone < dGot)	// If there is more data
+	{
+		*pChar = (unsigned char)pBuf[dDone];	// Extract the next char
+		dDone++;	// Increment the done count
+		*pdDone = dDone;
+		bRead = true;	// and flag success
+	}
+
+	return bRead;
+}
+
+#ifdef NOTUSED
+/****************************************************************************
+**
+** ReadWord
+**
+** Reads a word from the 1401, just uses ReadChar twice; passes on any error
+**
+*****************************************************************************/
+static bool ReadWord(unsigned short *pWord, char *pBuf, unsigned int *pdDone,
+		     unsigned int dGot)
+{
+	if (ReadChar((unsigned char *)pWord, pBuf, pdDone, dGot))
+		return ReadChar(((unsigned char *)pWord) + 1, pBuf, pdDone,
+				dGot);
+	else
+		return false;
+}
+#endif
+
+/****************************************************************************
+** ReadHuff
+**
+** Reads a coded number in and returns it, Code is:
+** If data is in range 0..127 we recieve 1 byte. If data in range 128-16383
+** we recieve two bytes, top bit of first indicates another on its way. If
+** data in range 16383-4194303 we get three bytes, top two bits of first set
+** to indicate three byte total.
+**
+*****************************************************************************/
+static bool ReadHuff(volatile unsigned int *pDWord, char *pBuf,
+		     unsigned int *pdDone, unsigned int dGot)
+{
+	unsigned char ucData;	/* for each read to ReadChar */
+	bool bReturn = true;	/* assume we will succeed */
+	unsigned int dwData = 0;	/* Accumulator for the data */
+
+	if (ReadChar(&ucData, pBuf, pdDone, dGot)) {
+		dwData = ucData;	/* copy the data */
+		if ((dwData & 0x00000080) != 0) {	/* Bit set for more data ? */
+			dwData &= 0x0000007F;	/* Clear the relevant bit */
+			if (ReadChar(&ucData, pBuf, pdDone, dGot)) {
+				dwData = (dwData << 8) | ucData;
+				if ((dwData & 0x00004000) != 0) {	/* three byte sequence ? */
+					dwData &= 0x00003FFF;	/* Clear the relevant bit */
+					if (ReadChar
+					    (&ucData, pBuf, pdDone, dGot))
+						dwData = (dwData << 8) | ucData;
+					else
+						bReturn = false;
+				}
+			} else
+				bReturn = false;	/* couldn't read data */
+		}
+	} else
+		bReturn = false;
+
+	*pDWord = dwData;	/* return the data */
+	return bReturn;
+}
+
+/***************************************************************************
+**
+** ReadDMAInfo
+**
+** Tries to read info about the dma request from the 1401 and decode it into
+** the dma descriptor block. We have at this point had the escape character
+** from the 1401 and now we must read in the rest of the information about
+** the transfer request. Returns FALSE if 1401 fails to respond or obselete
+** code from 1401 or bad parameters.
+**
+** The pBuf char pointer does not include the initial escape character, so
+**  we start handling the data at offset zero.
+**
+*****************************************************************************/
+static bool ReadDMAInfo(volatile DMADESC * pDmaDesc, DEVICE_EXTENSION * pdx,
+			char *pBuf, unsigned int dwCount)
+{
+	bool bResult = false;	// assume we won't succeed
+	unsigned char ucData;
+	unsigned int dDone = 0;	// We haven't parsed anything so far
+
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+	if (ReadChar(&ucData, pBuf, &dDone, dwCount)) {
+		unsigned char ucTransCode = (ucData & 0x0F);	// get code for transfer type
+		unsigned short wIdent = ((ucData >> 4) & 0x07);	// and area identifier
+
+		// fill in the structure we were given
+		pDmaDesc->wTransType = ucTransCode;	// type of transfer
+		pDmaDesc->wIdent = wIdent;	// area to use
+		pDmaDesc->dwSize = 0;	// initialise other bits
+		pDmaDesc->dwOffset = 0;
+
+		dev_dbg(&pdx->interface->dev, "%s type: %d ident: %d", __func__,
+			pDmaDesc->wTransType, pDmaDesc->wIdent);
+
+		pDmaDesc->bOutWard = (ucTransCode != TM_EXTTOHOST);	// set transfer direction
+
+		switch (ucTransCode) {
+		case TM_EXTTOHOST:	// Extended linear transfer modes (the only ones!)
+		case TM_EXTTO1401:
+			{
+				bResult =
+				    ReadHuff(&(pDmaDesc->dwOffset), pBuf,
+					     &dDone, dwCount)
+				    && ReadHuff(&(pDmaDesc->dwSize), pBuf,
+						&dDone, dwCount);
+				if (bResult) {
+					dev_dbg(&pdx->interface->dev,
+						"%s xfer offset & size %d %d",
+						__func__, pDmaDesc->dwOffset,
+						pDmaDesc->dwSize);
+
+					if ((wIdent >= MAX_TRANSAREAS) ||	// Illegal area number, or...
+					    (!pdx->rTransDef[wIdent].bUsed) ||	// area not set up, or...
+					    (pDmaDesc->dwOffset > pdx->rTransDef[wIdent].dwLength) ||	// range/size
+					    ((pDmaDesc->dwOffset +
+					      pDmaDesc->dwSize) >
+					     (pdx->rTransDef[wIdent].
+					      dwLength))) {
+						bResult = false;	// bad parameter(s)
+						dev_dbg(&pdx->interface->dev,
+							"%s bad param - id %d, bUsed %d, offset %d, size %d, area length %d",
+							__func__, wIdent,
+							pdx->rTransDef[wIdent].
+							bUsed,
+							pDmaDesc->dwOffset,
+							pDmaDesc->dwSize,
+							pdx->rTransDef[wIdent].
+							dwLength);
+					}
+				}
+				break;
+			}
+		default:
+			break;
+		}
+	} else
+		bResult = false;
+
+	if (!bResult)		// now check parameters for validity
+		dev_err(&pdx->interface->dev, "%s error reading Esc sequence",
+			__func__);
+
+	return bResult;
+}
+
+/****************************************************************************
+**
+** Handle1401Esc
+**
+** Deals with an escape sequence coming from the 1401. This can either be
+**  a DMA transfer request of various types or a response to an escape sequence
+**  sent to the 1401. This is called from a callback.
+**
+** Parameters are
+**
+** dwCount - the number of characters in the device extension char in buffer,
+**           this is known to be at least 2 or we will not be called.
+**
+****************************************************************************/
+static int Handle1401Esc(DEVICE_EXTENSION * pdx, char *pCh,
+			 unsigned int dwCount)
+{
+	int iReturn = U14ERR_FAIL;
+
+	// I have no idea what this next test is about. '?' is 0x3f, which is area 3, code
+	// 15. At the moment, this is not used, so it does no harm, but unless someone can
+	// tell me what this is for, it should be removed from this and the Windows driver.
+	if (pCh[0] == '?')	// Is this an information response
+	{			// Parse and save the information
+	} else {
+		spin_lock(&pdx->stagedLock);	// Lock others out
+
+		if (ReadDMAInfo(&pdx->rDMAInfo, pdx, pCh, dwCount))	// Get DMA parameters
+		{
+			unsigned short wTransType = pdx->rDMAInfo.wTransType;	// check transfer type
+
+			dev_dbg(&pdx->interface->dev,
+				"%s xfer to %s, offset %d, length %d", __func__,
+				pdx->rDMAInfo.bOutWard ? "1401" : "host",
+				pdx->rDMAInfo.dwOffset, pdx->rDMAInfo.dwSize);
+
+			if (pdx->bXFerWaiting)	// Check here for badly out of kilter...
+			{	// This can never happen, really
+				dev_err(&pdx->interface->dev,
+					"ERROR: DMA setup while transfer still waiting");
+				spin_unlock(&pdx->stagedLock);
+			} else {
+				if ((wTransType == TM_EXTTOHOST)
+				    || (wTransType == TM_EXTTO1401)) {
+					iReturn =
+					    ReadWriteMem(pdx,
+							 !pdx->rDMAInfo.
+							 bOutWard,
+							 pdx->rDMAInfo.wIdent,
+							 pdx->rDMAInfo.dwOffset,
+							 pdx->rDMAInfo.dwSize);
+					if (iReturn != U14ERR_NOERROR)
+						dev_err(&pdx->interface->dev,
+							"%s ReadWriteMem() failed %d",
+							__func__, iReturn);
+				} else	// This covers non-linear transfer setup
+					dev_err(&pdx->interface->dev,
+						"%s Unknown block xfer type %d",
+						__func__, wTransType);
+			}
+		} else		// Failed to read parameters
+			dev_err(&pdx->interface->dev, "%s ReadDMAInfo() fail",
+				__func__);
+
+		spin_unlock(&pdx->stagedLock);	// OK here
+	}
+
+	dev_dbg(&pdx->interface->dev, "%s returns %d", __func__, iReturn);
+
+	return iReturn;
+}
+
+/****************************************************************************
+** Callback for the character read complete or error
+****************************************************************************/
+static void ced_readchar_callback(struct urb *pUrb)
+{
+	DEVICE_EXTENSION *pdx = pUrb->context;
+	int nGot = pUrb->actual_length;	// what we transferred
+
+	if (pUrb->status)	// Do we have a problem to handle?
+	{
+		int nPipe = pdx->nPipes == 4 ? 1 : 0;	// The pipe number to use for error
+		// sync/async unlink faults aren't errors... just saying device removed or stopped
+		if (!
+		    (pUrb->status == -ENOENT || pUrb->status == -ECONNRESET
+		     || pUrb->status == -ESHUTDOWN)) {
+			dev_err(&pdx->interface->dev,
+				"%s - nonzero write bulk status received: %d",
+				__func__, pUrb->status);
+		} else
+			dev_dbg(&pdx->interface->dev,
+				"%s - 0 chars pUrb->status=%d (shutdown?)",
+				__func__, pUrb->status);
+
+		spin_lock(&pdx->err_lock);
+		pdx->errors = pUrb->status;
+		spin_unlock(&pdx->err_lock);
+		nGot = 0;	//  and tidy up again if so
+
+		spin_lock(&pdx->charInLock);	// already at irq level
+		pdx->bPipeError[nPipe] = 1;	// Flag an error for later
+	} else {
+		if ((nGot > 1) && ((pdx->pCoherCharIn[0] & 0x7f) == 0x1b))	// Esc sequence?
+		{
+			Handle1401Esc(pdx, &pdx->pCoherCharIn[1], nGot - 1);	// handle it
+			spin_lock(&pdx->charInLock);	// already at irq level
+		} else {
+			spin_lock(&pdx->charInLock);	// already at irq level
+			if (nGot > 0) {
+				unsigned int i;
+				if (nGot < INBUF_SZ) {
+					pdx->pCoherCharIn[nGot] = 0;	// tidy the string
+					dev_dbg(&pdx->interface->dev,
+						"%s got %d chars >%s<",
+						__func__, nGot,
+						pdx->pCoherCharIn);
+				}
+				// We know that whatever we read must fit in the input buffer
+				for (i = 0; i < nGot; i++) {
+					pdx->inputBuffer[pdx->dwInBuffPut++] =
+					    pdx->pCoherCharIn[i] & 0x7F;
+					if (pdx->dwInBuffPut >= INBUF_SZ)
+						pdx->dwInBuffPut = 0;
+				}
+
+				if ((pdx->dwNumInput + nGot) <= INBUF_SZ)
+					pdx->dwNumInput += nGot;	// Adjust the buffer count accordingly
+			} else
+				dev_dbg(&pdx->interface->dev, "%s read ZLP",
+					__func__);
+		}
+	}
+
+	pdx->bReadCharsPending = false;	// No longer have a pending read
+	spin_unlock(&pdx->charInLock);	// already at irq level
+
+	Allowi(pdx, true);	// see if we can do the next one
+}
+
+/****************************************************************************
+** Allowi
+**
+** This is used to make sure that there is always a pending input transfer so
+** we can pick up any inward transfers. This can be called in multiple contexts
+** so we use the irqsave version of the spinlock.
+****************************************************************************/
+int Allowi(DEVICE_EXTENSION * pdx, bool bInCallback)
+{
+	int iReturn = U14ERR_NOERROR;
+	unsigned long flags;
+	spin_lock_irqsave(&pdx->charInLock, flags);	// can be called in multiple contexts
+
+	// We don't want char input running while DMA is in progress as we know that this
+	//  can cause sequencing problems for the 2270. So don't. It will also allow the
+	//  ERR response to get back to the host code too early on some PCs, even if there
+	//  is no actual driver failure, so we don't allow this at all.
+	if (!pdx->bInDrawDown &&	// stop input if
+	    !pdx->bReadCharsPending &&	// If no read request outstanding
+	    (pdx->dwNumInput < (INBUF_SZ / 2)) &&	//  and there is some space
+	    (pdx->dwDMAFlag == MODE_CHAR) &&	//  not doing any DMA
+	    (!pdx->bXFerWaiting) &&	//  no xfer waiting to start
+	    (CanAcceptIoRequests(pdx)))	//  and activity is generally OK
+	{			//  then off we go
+		unsigned int nMax = INBUF_SZ - pdx->dwNumInput;	// max we could read
+		int nPipe = pdx->nPipes == 4 ? 1 : 0;	// The pipe number to use
+
+		dev_dbg(&pdx->interface->dev, "%s %d chars in input buffer",
+			__func__, pdx->dwNumInput);
+
+		usb_fill_int_urb(pdx->pUrbCharIn, pdx->udev,
+				 usb_rcvintpipe(pdx->udev, pdx->epAddr[nPipe]),
+				 pdx->pCoherCharIn, nMax, ced_readchar_callback,
+				 pdx, pdx->bInterval);
+		pdx->pUrbCharIn->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;	// short xfers are OK by default
+		usb_anchor_urb(pdx->pUrbCharIn, &pdx->submitted);	// in case we need to kill it
+		iReturn =
+		    usb_submit_urb(pdx->pUrbCharIn,
+				   bInCallback ? GFP_ATOMIC : GFP_KERNEL);
+		if (iReturn) {
+			usb_unanchor_urb(pdx->pUrbCharIn);	// remove from list of active Urbs
+			pdx->bPipeError[nPipe] = 1;	// Flag an error to be handled later
+			dev_err(&pdx->interface->dev,
+				"%s submit urb failed: %d", __func__, iReturn);
+		} else
+			pdx->bReadCharsPending = true;	// Flag that we are active here
+	}
+
+	spin_unlock_irqrestore(&pdx->charInLock, flags);
+
+	return iReturn;
+
+}
+
+/*****************************************************************************
+** The ioctl entry point to the driver that is used by us to talk to it.
+** inode    The device node (no longer in 3.0.0 kernels)
+** file     The file that is open, which holds our pdx pointer
+** ulArg    The argument passed in. Note that long is 64-bits in 64-bit system, i.e. it is big
+**          enough for a 64-bit pointer.
+*****************************************************************************/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+static long ced_ioctl(struct file *file, unsigned int cmd, unsigned long ulArg)
+#else
+static int ced_ioctl(struct inode *node, struct file *file, unsigned int cmd,
+		     unsigned long ulArg)
+#endif
+{
+	int err = 0;
+	DEVICE_EXTENSION *pdx = file->private_data;
+	if (!CanAcceptIoRequests(pdx))	// check we still exist
+		return -ENODEV;
+
+	// Check that access is allowed, where is is needed. Anything that would have an indeterminate
+	// size will be checked by the specific command.
+	if (_IOC_DIR(cmd) & _IOC_READ)	// read from point of view of user...
+		err = !access_ok(VERIFY_WRITE, (void __user *)ulArg, _IOC_SIZE(cmd));	// is kernel write
+	else if (_IOC_DIR(cmd) & _IOC_WRITE)	// and write from point of view of user...
+		err = !access_ok(VERIFY_READ, (void __user *)ulArg, _IOC_SIZE(cmd));	// is kernel read
+	if (err)
+		return -EFAULT;
+
+	switch (_IOC_NR(cmd)) {
+	case _IOC_NR(IOCTL_CED_SENDSTRING(0)):
+		return SendString(pdx, (const char __user *)ulArg,
+				  _IOC_SIZE(cmd));
+
+	case _IOC_NR(IOCTL_CED_RESET1401):
+		return Reset1401(pdx);
+
+	case _IOC_NR(IOCTL_CED_GETCHAR):
+		return GetChar(pdx);
+
+	case _IOC_NR(IOCTL_CED_SENDCHAR):
+		return SendChar(pdx, (char)ulArg);
+
+	case _IOC_NR(IOCTL_CED_STAT1401):
+		return Stat1401(pdx);
+
+	case _IOC_NR(IOCTL_CED_LINECOUNT):
+		return LineCount(pdx);
+
+	case _IOC_NR(IOCTL_CED_GETSTRING(0)):
+		return GetString(pdx, (char __user *)ulArg, _IOC_SIZE(cmd));
+
+	case _IOC_NR(IOCTL_CED_SETTRANSFER):
+		return SetTransfer(pdx, (TRANSFERDESC __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_UNSETTRANSFER):
+		return UnsetTransfer(pdx, (int)ulArg);
+
+	case _IOC_NR(IOCTL_CED_SETEVENT):
+		return SetEvent(pdx, (TRANSFEREVENT __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_GETOUTBUFSPACE):
+		return GetOutBufSpace(pdx);
+
+	case _IOC_NR(IOCTL_CED_GETBASEADDRESS):
+		return -1;
+
+	case _IOC_NR(IOCTL_CED_GETDRIVERREVISION):
+		return (2 << 24) | (DRIVERMAJREV << 16) | DRIVERMINREV;	// USB | MAJOR | MINOR
+
+	case _IOC_NR(IOCTL_CED_GETTRANSFER):
+		return GetTransfer(pdx, (TGET_TX_BLOCK __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_KILLIO1401):
+		return KillIO1401(pdx);
+
+	case _IOC_NR(IOCTL_CED_STATEOF1401):
+		return StateOf1401(pdx);
+
+	case _IOC_NR(IOCTL_CED_GRAB1401):
+	case _IOC_NR(IOCTL_CED_FREE1401):
+		return U14ERR_NOERROR;
+
+	case _IOC_NR(IOCTL_CED_STARTSELFTEST):
+		return StartSelfTest(pdx);
+
+	case _IOC_NR(IOCTL_CED_CHECKSELFTEST):
+		return CheckSelfTest(pdx, (TGET_SELFTEST __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_TYPEOF1401):
+		return TypeOf1401(pdx);
+
+	case _IOC_NR(IOCTL_CED_TRANSFERFLAGS):
+		return TransferFlags(pdx);
+
+	case _IOC_NR(IOCTL_CED_DBGPEEK):
+		return DbgPeek(pdx, (TDBGBLOCK __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_DBGPOKE):
+		return DbgPoke(pdx, (TDBGBLOCK __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_DBGRAMPDATA):
+		return DbgRampData(pdx, (TDBGBLOCK __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_DBGRAMPADDR):
+		return DbgRampAddr(pdx, (TDBGBLOCK __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_DBGGETDATA):
+		return DbgGetData(pdx, (TDBGBLOCK __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_DBGSTOPLOOP):
+		return DbgStopLoop(pdx);
+
+	case _IOC_NR(IOCTL_CED_FULLRESET):
+		pdx->bForceReset = true;	// Set a flag for a full reset
+		break;
+
+	case _IOC_NR(IOCTL_CED_SETCIRCULAR):
+		return SetCircular(pdx, (TRANSFERDESC __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_GETCIRCBLOCK):
+		return GetCircBlock(pdx, (TCIRCBLOCK __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_FREECIRCBLOCK):
+		return FreeCircBlock(pdx, (TCIRCBLOCK __user *) ulArg);
+
+	case _IOC_NR(IOCTL_CED_WAITEVENT):
+		return WaitEvent(pdx, (int)(ulArg & 0xff), (int)(ulArg >> 8));
+
+	case _IOC_NR(IOCTL_CED_TESTEVENT):
+		return TestEvent(pdx, (int)ulArg);
+
+	default:
+		return U14ERR_NO_SUCH_FN;
+	}
+	return U14ERR_NOERROR;
+}
+
+static const struct file_operations ced_fops = {
+	.owner = THIS_MODULE,
+	.open = ced_open,
+	.release = ced_release,
+	.flush = ced_flush,
+	.llseek = noop_llseek,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+	.unlocked_ioctl = ced_ioctl,
+#else
+	.ioctl = ced_ioctl,
+#endif
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with the driver core
+ */
+static struct usb_class_driver ced_class = {
+	.name = "cedusb%d",
+	.fops = &ced_fops,
+	.minor_base = USB_CED_MINOR_BASE,
+};
+
+// Check that the device that matches a 1401 vendor and product ID is OK to use and
+// initialise our DEVICE_EXTENSION.
+static int ced_probe(struct usb_interface *interface,
+		     const struct usb_device_id *id)
+{
+	DEVICE_EXTENSION *pdx;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int i, bcdDevice;
+	int retval = -ENOMEM;
+
+	// allocate memory for our device extension and initialize it
+	pdx = kzalloc(sizeof(*pdx), GFP_KERNEL);
+	if (!pdx) {
+		dev_err(&interface->dev, "Out of memory\n");
+		goto error;
+	}
+
+	for (i = 0; i < MAX_TRANSAREAS; ++i)	// Initialise the wait queues
+	{
+		init_waitqueue_head(&pdx->rTransDef[i].wqEvent);
+	}
+
+	// Put initialises for our stuff here. Note that all of *pdx is zero, so
+	// no need to explicitly zero it.
+	spin_lock_init(&pdx->charOutLock);
+	spin_lock_init(&pdx->charInLock);
+	spin_lock_init(&pdx->stagedLock);
+
+	// Initialises from the skeleton stuff
+	kref_init(&pdx->kref);
+	mutex_init(&pdx->io_mutex);
+	spin_lock_init(&pdx->err_lock);
+	init_usb_anchor(&pdx->submitted);
+
+	pdx->udev = usb_get_dev(interface_to_usbdev(interface));
+	pdx->interface = interface;
+
+	// Attempt to identify the device
+	bcdDevice = pdx->udev->descriptor.bcdDevice;
+	i = (bcdDevice >> 8);
+	if (i == 0)
+		pdx->s1401Type = TYPEU1401;
+	else if ((i >= 1) && (i <= 23))
+		pdx->s1401Type = i + 2;
+	else {
+		dev_err(&interface->dev, "%s Unknown device. bcdDevice = %d",
+			__func__, bcdDevice);
+		goto error;
+	}
+	// set up the endpoint information. We only care about the number of EP as
+	// we know that we are dealing with a 1401 device.
+	iface_desc = interface->cur_altsetting;
+	pdx->nPipes = iface_desc->desc.bNumEndpoints;
+	dev_info(&interface->dev, "1401Type=%d with %d End Points",
+		 pdx->s1401Type, pdx->nPipes);
+	if ((pdx->nPipes < 3) || (pdx->nPipes > 4))
+		goto error;
+
+	// Allocate the URBs we hold for performing transfers
+	pdx->pUrbCharOut = usb_alloc_urb(0, GFP_KERNEL);	// character output URB
+	pdx->pUrbCharIn = usb_alloc_urb(0, GFP_KERNEL);	// character input URB
+	pdx->pStagedUrb = usb_alloc_urb(0, GFP_KERNEL);	// block transfer URB
+	if (!pdx->pUrbCharOut || !pdx->pUrbCharIn || !pdx->pStagedUrb) {
+		dev_err(&interface->dev, "%s URB alloc failed", __func__);
+		goto error;
+	}
+
+	pdx->pCoherStagedIO =
+	    usb_alloc_coherent(pdx->udev, STAGED_SZ, GFP_KERNEL,
+			       &pdx->pStagedUrb->transfer_dma);
+	pdx->pCoherCharOut =
+	    usb_alloc_coherent(pdx->udev, OUTBUF_SZ, GFP_KERNEL,
+			       &pdx->pUrbCharOut->transfer_dma);
+	pdx->pCoherCharIn =
+	    usb_alloc_coherent(pdx->udev, INBUF_SZ, GFP_KERNEL,
+			       &pdx->pUrbCharIn->transfer_dma);
+	if (!pdx->pCoherCharOut || !pdx->pCoherCharIn || !pdx->pCoherStagedIO) {
+		dev_err(&interface->dev, "%s Coherent buffer alloc failed",
+			__func__);
+		goto error;
+	}
+
+	for (i = 0; i < pdx->nPipes; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+		pdx->epAddr[i] = endpoint->bEndpointAddress;
+		dev_info(&interface->dev, "Pipe %d, ep address %02x", i,
+			 pdx->epAddr[i]);
+		if (((pdx->nPipes == 3) && (i == 0)) ||	// if char input end point
+		    ((pdx->nPipes == 4) && (i == 1))) {
+			pdx->bInterval = endpoint->bInterval;	// save the endpoint interrupt interval
+			dev_info(&interface->dev, "Pipe %d, bInterval = %d", i,
+				 pdx->bInterval);
+		}
+		// Detect USB2 by checking last ep size (64 if USB1)
+		if (i == pdx->nPipes - 1)	// if this is the last ep (bulk)
+		{
+			pdx->bIsUSB2 =
+			    le16_to_cpu(endpoint->wMaxPacketSize) > 64;
+			dev_info(&pdx->interface->dev, "USB%d",
+				 pdx->bIsUSB2 + 1);
+		}
+	}
+
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(interface, pdx);
+
+	/* we can register the device now, as it is ready */
+	retval = usb_register_dev(interface, &ced_class);
+	if (retval) {
+		/* something prevented us from registering this driver */
+		dev_err(&interface->dev,
+			"Not able to get a minor for this device.\n");
+		usb_set_intfdata(interface, NULL);
+		goto error;
+	}
+
+	/* let the user know what node this device is now attached to */
+	dev_info(&interface->dev,
+		 "USB CEDUSB device now attached to cedusb #%d",
+		 interface->minor);
+	return 0;
+
+error:
+	if (pdx)
+		kref_put(&pdx->kref, ced_delete);	// frees allocated memory
+	return retval;
+}
+
+static void ced_disconnect(struct usb_interface *interface)
+{
+	DEVICE_EXTENSION *pdx = usb_get_intfdata(interface);
+	int minor = interface->minor;	// save for message at the end
+	int i;
+
+	usb_set_intfdata(interface, NULL);	// remove the pdx from the interface
+	usb_deregister_dev(interface, &ced_class);	// give back our minor device number
+
+	mutex_lock(&pdx->io_mutex);	// stop more I/O starting while...
+	ced_draw_down(pdx);	// ...wait for then kill any io
+	for (i = 0; i < MAX_TRANSAREAS; ++i) {
+		int iErr = ClearArea(pdx, i);	// ...release any used memory
+		if (iErr == U14ERR_UNLOCKFAIL)
+			dev_err(&pdx->interface->dev, "%s Area %d was in used",
+				__func__, i);
+	}
+	pdx->interface = NULL;	// ...we kill off link to interface
+	mutex_unlock(&pdx->io_mutex);
+
+	usb_kill_anchored_urbs(&pdx->submitted);
+
+	kref_put(&pdx->kref, ced_delete);	// decrement our usage count
+
+	dev_info(&interface->dev, "USB cedusb #%d now disconnected", minor);
+}
+
+// Wait for all the urbs we know of to be done with, then kill off any that
+// are left. NBNB we will need to have a mechanism to stop circular xfers
+// from trying to fire off more urbs. We will wait up to 3 seconds for Urbs
+// to be done.
+void ced_draw_down(DEVICE_EXTENSION * pdx)
+{
+	int time;
+	dev_dbg(&pdx->interface->dev, "%s called", __func__);
+
+	pdx->bInDrawDown = true;
+	time = usb_wait_anchor_empty_timeout(&pdx->submitted, 3000);
+	if (!time)		// if we timed out we kill the urbs
+	{
+		usb_kill_anchored_urbs(&pdx->submitted);
+		dev_err(&pdx->interface->dev, "%s timed out", __func__);
+	}
+	pdx->bInDrawDown = false;
+}
+
+static int ced_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+	if (!pdx)
+		return 0;
+	ced_draw_down(pdx);
+
+	dev_dbg(&pdx->interface->dev, "%s called", __func__);
+	return 0;
+}
+
+static int ced_resume(struct usb_interface *intf)
+{
+	DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+	if (!pdx)
+		return 0;
+	dev_dbg(&pdx->interface->dev, "%s called", __func__);
+	return 0;
+}
+
+static int ced_pre_reset(struct usb_interface *intf)
+{
+	DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+	mutex_lock(&pdx->io_mutex);
+	ced_draw_down(pdx);
+	return 0;
+}
+
+static int ced_post_reset(struct usb_interface *intf)
+{
+	DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+	dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+	/* we are sure no URBs are active - no locking needed */
+	pdx->errors = -EPIPE;
+	mutex_unlock(&pdx->io_mutex);
+
+	return 0;
+}
+
+static struct usb_driver ced_driver = {
+	.name = "cedusb",
+	.probe = ced_probe,
+	.disconnect = ced_disconnect,
+	.suspend = ced_suspend,
+	.resume = ced_resume,
+	.pre_reset = ced_pre_reset,
+	.post_reset = ced_post_reset,
+	.id_table = ced_table,
+	.supports_autosuspend = 1,
+};
+
+module_usb_driver(ced_driver);
+MODULE_LICENSE("GPL");

+ 249 - 0
drivers/staging/ced1401/usb1401.h

@@ -0,0 +1,249 @@
+/* usb1401.h
+ Header file for the CED 1401 USB device driver for Linux
+ Copyright (C) 2010 Cambridge Electronic Design Ltd
+ Author Greg P Smith (greg@ced.co.uk)
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+#ifndef __USB1401_H__
+#define __USB1401_H__
+#include "use1401.h"
+#include "ced_ioctl.h"
+
+#ifndef UINT
+#define UINT unsigned int
+#endif
+
+/// Device type codes, but these don't need to be extended - a succession is assumed
+/// These are set for usb from the bcdDevice field (suitably mangled). Future devices
+/// will be added in order of device creation to the list, so the names here are just
+/// to help use remember which device is which. The U14ERR_... values follow the same
+/// pattern for modern devices.
+#define TYPEUNKNOWN        -1             // dont know
+#define TYPE1401           0              // standard 1401
+#define TYPEPLUS           1              // 1401 plus
+#define TYPEU1401          2              // u1401
+#define TYPEPOWER          3              // Power1401
+#define TYPEU14012         4              // u1401 mkII
+#define TYPEPOWER2         5              // Power1401 mk II
+#define TYPEMICRO3         6              // Micro1401-3
+#define TYPEPOWER3         7              // Power1401-3
+
+/// Some useful defines of constants. DONT FORGET to change the version in the
+/// resources whenever you change it here!.
+#define DRIVERMAJREV      2             // driver revision level major (match windows)
+#define DRIVERMINREV      0             // driver revision level minor
+
+/// Definitions of the various block transfer command codes
+#define TM_EXTTOHOST    8               // extended tohost
+#define TM_EXTTO1401    9               // extended to1401
+
+/// Definitions of values in usbReqtype. Used in sorting out setup actions
+#define H_TO_D 0x00
+#define D_TO_H 0x80
+#define VENDOR 0x40
+#define DEVREQ 0x00
+#define INTREQ 0x01
+#define ENDREQ 0x02
+
+/// Definition of values in usbRequest, again used to sort out setup
+#define GET_STATUS      0x00
+#define CLEAR_FEATURE   0x01
+#define SET_FEATURE     0x03
+#define SET_ADDRESS     0x05
+#define GET_DESC        0x06
+#define SET_DESC        0x07
+#define GET_CONF        0x08
+#define SET_CONF        0x09
+#define GET_INTERFACE   0x0a
+#define SET_INTERFACE   0x0b
+#define SYNCH_FRAME     0x0c
+
+/// Definitions of the various debug command codes understood by the 1401. These
+/// are used in various vendor-specific commands to achieve the desired effect
+#define DB_GRAB         0x50            /* Grab is a NOP for USB */
+#define DB_FREE         0x51            /* Free is a NOP for the USB */
+#define DB_SETADD       0x52            /* Set debug address (double) */
+#define DB_SELFTEST     0x53            /* Start self test */
+#define DB_SETMASK      0x54            /* Set enable mask (double) */
+#define DB_SETDEF       0x55            /* Set default mask (double) */
+#define DB_PEEK         0x56            /* Peek address, save result */
+#define DB_POKE         0x57            /* Poke address with data (double) */
+#define DB_RAMPD        0x58            /* Ramp data at debug address */
+#define DB_RAMPA        0x59            /* Ramp address bus */
+#define DB_REPEATS      0x5A            /* Set repeats for operations (double) */
+#define DB_WIDTH        0x5B            /* Set width for operations (byte) */
+#define DB_DATA         0x5C            /* Get 4-byte data read by PEEK */
+#define DB_CHARS        0x5D            /* Send chars via EP0 control write */
+
+#define CR_CHAR          0x0D           /* The carriage return character */
+#define CR_CHAR_80       0x8d           /*  and with bit 7 set */
+
+/// A structure holding information about a block of memory for use in circular transfers
+typedef struct circBlk
+{
+    volatile UINT dwOffset;             /* Offset within area of block start */
+    volatile UINT dwSize;               /* Size of the block, in bytes (0 = unused) */
+} CIRCBLK;
+
+/// A structure holding all of the information about a transfer area - an area of
+///  memory set up for use either as a source or destination in DMA transfers.
+typedef struct transarea
+{
+    void*       lpvBuff;                // User address of xfer area saved for completeness
+    UINT        dwBaseOffset;           // offset to start of xfer area in first page
+    UINT        dwLength;               // Length of xfer area, in bytes
+    struct page **pPages;               // Points at array of locked down pages
+    int         nPages;                 // number of pages that are locked down
+    bool        bUsed;                  // Is this structure in use?
+    bool        bCircular;              // Is this area for circular transfers?
+    bool        bCircToHost;            // Flag for direction of circular transfer
+    bool        bEventToHost;           // Set event on transfer to host?
+    int         iWakeUp;                // Set 1 on event, cleared by TestEvent()
+    UINT        dwEventSt;              // Defines section within xfer area for...
+    UINT        dwEventSz;              // ...notification by the event SZ is 0 if unset
+    CIRCBLK     aBlocks[2];             // Info on a pair of circular blocks
+    wait_queue_head_t wqEvent;          // The wait queue for events in this area MUST BE LAST
+} TRANSAREA;
+
+/// The DMADESC structure is used to hold information on the transfer in progress. It
+/// is set up by ReadDMAInfo, using information sent by the 1401 in an escape sequence.
+typedef struct dmadesc
+{
+    unsigned short wTransType;          /* transfer type as TM_xxx above        */
+    unsigned short wIdent;              /* identifier word                      */
+    unsigned int   dwSize;              /* bytes to transfer                    */
+    unsigned int   dwOffset;            /* offset into transfer area for trans  */
+    bool           bOutWard;            /* true when data is going TO 1401      */
+} DMADESC;
+
+#define INBUF_SZ         256            /* input buffer size */
+#define OUTBUF_SZ        256            /* output buffer size */
+#define STAGED_SZ 0x10000               // size of coherent buffer for staged transfers
+
+/// Structure to hold all of our device specific stuff. We are making this as similar as we
+/// can to the Windows driver to help in our understanding of what is going on.
+typedef struct _DEVICE_EXTENSION
+{
+    char inputBuffer[INBUF_SZ];         /* The two buffers */
+    char outputBuffer[OUTBUF_SZ];       /* accessed by the host functions */
+    volatile unsigned int dwNumInput;   /* num of chars in input buffer   */
+    volatile unsigned int dwInBuffGet;  /* where to get from input buffer */
+    volatile unsigned int dwInBuffPut;  /* where to put into input buffer */
+    volatile unsigned int dwNumOutput;  /* num of chars in output buffer  */
+    volatile unsigned int dwOutBuffGet; /* where to get from output buffer*/
+    volatile unsigned int dwOutBuffPut; /* where to put into output buffer*/
+
+    volatile bool bSendCharsPending;    /* Flag to indicate sendchar active */
+    volatile bool bReadCharsPending;    /* Flag to indicate a read is primed */
+    char* pCoherCharOut;                /* special aligned buffer for chars to 1401 */
+    struct urb* pUrbCharOut;            /* urb used for chars to 1401 */
+    char* pCoherCharIn;                 /* special aligned buffer for chars to host */
+    struct urb* pUrbCharIn;             /* urb used for chars to host */
+
+    spinlock_t charOutLock;             /* to protect the outputBuffer and outputting */
+    spinlock_t charInLock;              /* to protect the inputBuffer and char reads */
+    __u8 bInterval;                     /* Interrupt end point interval */
+
+    volatile unsigned int dwDMAFlag;    /* state of DMA */
+    TRANSAREA rTransDef[MAX_TRANSAREAS];/* transfer area info */
+    volatile DMADESC rDMAInfo;          // info on current DMA transfer
+    volatile bool bXFerWaiting;         // Flag set if DMA transfer stalled
+    volatile bool bInDrawDown;          // Flag that we want to halt transfers
+
+    // Parameters relating to a block read\write that is in progress. Some of these values
+    //  are equivalent to values in rDMAInfo. The values here are those in use, while those
+    //  in rDMAInfo are those recieved from the 1401 via an escape sequence. If another
+    //  escape sequence arrives before the previous xfer ends, rDMAInfo values are updated while these
+    //  are used to finish off the current transfer.
+    volatile short StagedId;            // The transfer area id for this transfer
+    volatile bool StagedRead;           // Flag TRUE for read from 1401, FALSE for write
+    volatile unsigned int StagedLength; // Total length of this transfer
+    volatile unsigned int StagedOffset; // Offset within memory area for transfer start
+    volatile unsigned int StagedDone;   // Bytes transferred so far
+    volatile bool bStagedUrbPending;    // Flag to indicate active
+    char* pCoherStagedIO;               // buffer used for block transfers
+    struct urb* pStagedUrb;             // The URB to use
+    spinlock_t stagedLock;              // protects ReadWriteMem() and circular buffer stuff
+
+    short s1401Type;                    // type of 1401 attached
+    short sCurrentState;                // current error state
+    bool bIsUSB2;                       // type of the interface we connect to
+    bool bForceReset;                   // Flag to make sure we get a real reset
+    __u32 statBuf[2];                   // buffer for 1401 state info
+
+    unsigned long ulSelfTestTime;       // used to timeout self test
+
+    int nPipes;                         // Should be 3 or 4 depending on 1401 usb chip
+    int bPipeError[4];                  // set non-zero if an error on one of the pipe
+    __u8 epAddr[4];                     // addresses of the 3/4 end points
+
+    struct usb_device *udev;            // the usb device for this device
+    struct usb_interface *interface;    // the interface for this device, NULL if removed
+    struct usb_anchor submitted;        // in case we need to retract our submissions
+    struct mutex io_mutex;              // synchronize I/O with disconnect, one user-mode caller at a time
+
+    int    errors;                      // the last request tanked
+    int    open_count;                  // count the number of openers
+    spinlock_t err_lock;                // lock for errors
+    struct kref kref;
+}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
+#define to_DEVICE_EXTENSION(d) container_of(d, DEVICE_EXTENSION, kref)
+
+/// Definitions of routimes used between compilation object files
+// in usb1401.c
+extern int Allowi(DEVICE_EXTENSION* pdx, bool bInCallback);
+extern int SendChars(DEVICE_EXTENSION* pdx);
+extern void ced_draw_down(DEVICE_EXTENSION *pdx);
+extern int ReadWriteMem(DEVICE_EXTENSION *pdx, bool Read, unsigned short wIdent,
+                      unsigned int dwOffs, unsigned int dwLen);
+
+// in ced_ioc.c
+extern int ClearArea(DEVICE_EXTENSION *pdx, int nArea);
+extern int SendString(DEVICE_EXTENSION* pdx, const char __user* pData, unsigned int n);
+extern int SendChar(DEVICE_EXTENSION *pdx, char c);
+extern int Get1401State(DEVICE_EXTENSION* pdx, __u32* state, __u32* error);
+extern int ReadWrite_Cancel(DEVICE_EXTENSION *pdx);
+extern bool Is1401(DEVICE_EXTENSION* pdx);
+extern bool QuickCheck(DEVICE_EXTENSION* pdx, bool bTestBuff, bool bCanReset);
+extern int Reset1401(DEVICE_EXTENSION *pdx);
+extern int GetChar(DEVICE_EXTENSION *pdx);
+extern int GetString(DEVICE_EXTENSION *pdx, char __user* pUser, int n);
+extern int SetTransfer(DEVICE_EXTENSION *pdx, TRANSFERDESC __user *pTD);
+extern int UnsetTransfer(DEVICE_EXTENSION *pdx, int nArea);
+extern int SetEvent(DEVICE_EXTENSION *pdx, TRANSFEREVENT __user*pTE);
+extern int Stat1401(DEVICE_EXTENSION *pdx);
+extern int LineCount(DEVICE_EXTENSION *pdx);
+extern int GetOutBufSpace(DEVICE_EXTENSION *pdx);
+extern int GetTransfer(DEVICE_EXTENSION *pdx, TGET_TX_BLOCK __user *pGTB);
+extern int KillIO1401(DEVICE_EXTENSION *pdx);
+extern int BlkTransState(DEVICE_EXTENSION *pdx);
+extern int StateOf1401(DEVICE_EXTENSION *pdx);
+extern int StartSelfTest(DEVICE_EXTENSION *pdx);
+extern int CheckSelfTest(DEVICE_EXTENSION *pdx, TGET_SELFTEST __user *pGST);
+extern int TypeOf1401(DEVICE_EXTENSION *pdx);
+extern int TransferFlags(DEVICE_EXTENSION *pdx);
+extern int DbgPeek(DEVICE_EXTENSION *pdx, TDBGBLOCK __user* pDB);
+extern int DbgPoke(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgRampData(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgRampAddr(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgGetData(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgStopLoop(DEVICE_EXTENSION *pdx);
+extern int SetCircular(DEVICE_EXTENSION *pdx, TRANSFERDESC __user *pTD);
+extern int GetCircBlock(DEVICE_EXTENSION *pdx, TCIRCBLOCK __user* pCB);
+extern int FreeCircBlock(DEVICE_EXTENSION *pdx, TCIRCBLOCK __user* pCB);
+extern int WaitEvent(DEVICE_EXTENSION *pdx, int nArea, int msTimeOut);
+extern int TestEvent(DEVICE_EXTENSION *pdx, int nArea);
+#endif

+ 287 - 0
drivers/staging/ced1401/use1401.h

@@ -0,0 +1,287 @@
+/****************************************************************************
+** use1401.h
+** Copyright (C) Cambridge Electronic Design Ltd, 1992-2010
+** Authors: Paul Cox, Tim Bergel, Greg Smith
+** See CVS for revisions.
+**
+** Because the size of a long is different between 32-bit and 64-bit on some
+** systems, we avoid this in this interface.
+****************************************************************************/
+#ifndef __USE1401_H__
+#define __USE1401_H__
+#include "machine.h"
+
+// Some definitions to make things compatible. If you want to use Use1401 directly
+//  from a Windows program you should define U14_NOT_DLL, in which case you also
+//  MUST make sure that your application startup code calls U14InitLib().
+// DLL_USE1401 is defined when you are building the Use1401 dll, not otherwise.
+#ifdef _IS_WINDOWS_
+#ifndef U14_NOT_DLL
+#ifdef DLL_USE1401
+#define U14API(retType) retType DllExport __stdcall
+#else
+#define U14API(retType) retType DllImport __stdcall
+#endif
+#endif
+
+#define U14ERRBASE -500
+#define U14LONG long
+#endif
+
+#ifdef LINUX
+#define U14ERRBASE -1000
+#define U14LONG int
+#endif
+
+#ifdef _QT
+#ifndef U14_NOT_DLL
+#undef U14API
+#define U14API(retType) retType __declspec(dllimport) __stdcall
+#endif
+#undef U14LONG
+#define U14LONG int
+#endif
+
+#ifndef U14API
+#define U14API(retType) retType
+#endif
+
+#ifndef U14LONG
+#define U14LONG long
+#endif
+
+/// Error codes: We need them here as user space can see them.
+#define U14ERR_NOERROR        0             // no problems
+
+/// Device error codes, but these don't need to be extended - a succession is assumed
+#define U14ERR_STD            4              // standard 1401 connected
+#define U14ERR_U1401          5              // u1401 connected
+#define U14ERR_PLUS           6              // 1401 plus connected
+#define U14ERR_POWER          7              // Power1401 connected
+#define U14ERR_U14012         8              // u1401 mkII connected
+#define U14ERR_POWER2         9
+#define U14ERR_U14013        10
+#define U14ERR_POWER3        11
+
+/// NBNB Error numbers need shifting as some linux error codes start at 512
+#define U14ERR(n)             (n+U14ERRBASE)
+#define U14ERR_OFF            U14ERR(0)      /* 1401 there but switched off    */
+#define U14ERR_NC             U14ERR(-1)     /* 1401 not connected             */
+#define U14ERR_ILL            U14ERR(-2)     /* if present it is ill           */
+#define U14ERR_NOIF           U14ERR(-3)     /* I/F card missing               */
+#define U14ERR_TIME           U14ERR(-4)     /* 1401 failed to come ready      */
+#define U14ERR_BADSW          U14ERR(-5)     /* I/F card bad switches          */
+#define U14ERR_PTIME          U14ERR(-6)     /* 1401plus failed to come ready  */
+#define U14ERR_NOINT          U14ERR(-7)     /* couldn't grab the int vector   */
+#define U14ERR_INUSE          U14ERR(-8)     /* 1401 is already in use         */
+#define U14ERR_NODMA          U14ERR(-9)     /* couldn't get DMA channel       */
+#define U14ERR_BADHAND        U14ERR(-10)    /* handle provided was bad        */
+#define U14ERR_BAD1401NUM     U14ERR(-11)    /* 1401 number provided was bad   */
+
+#define U14ERR_NO_SUCH_FN     U14ERR(-20)    /* no such function               */
+#define U14ERR_NO_SUCH_SUBFN  U14ERR(-21)    /* no such sub function           */
+#define U14ERR_NOOUT          U14ERR(-22)    /* no room in output buffer       */
+#define U14ERR_NOIN           U14ERR(-23)    /* no input in buffer             */
+#define U14ERR_STRLEN         U14ERR(-24)    /* string longer than buffer      */
+#define U14ERR_ERR_STRLEN     U14ERR(-24)    /* string longer than buffer      */
+#define U14ERR_LOCKFAIL       U14ERR(-25)    /* failed to lock memory          */
+#define U14ERR_UNLOCKFAIL     U14ERR(-26)    /* failed to unlock memory        */
+#define U14ERR_ALREADYSET     U14ERR(-27)    /* area already set up            */
+#define U14ERR_NOTSET         U14ERR(-28)    /* area not set up                */
+#define U14ERR_BADAREA        U14ERR(-29)    /* illegal area number            */
+#define U14ERR_FAIL           U14ERR(-30)    /* we failed for some other reason*/
+
+#define U14ERR_NOFILE         U14ERR(-40)    /* command file not found         */
+#define U14ERR_READERR        U14ERR(-41)    /* error reading command file     */
+#define U14ERR_UNKNOWN        U14ERR(-42)    /* unknown command                */
+#define U14ERR_HOSTSPACE      U14ERR(-43)    /* not enough host space to load  */
+#define U14ERR_LOCKERR        U14ERR(-44)    /* could not lock resource/command*/
+#define U14ERR_CLOADERR       U14ERR(-45)    /* CLOAD command failed           */
+
+#define U14ERR_TOXXXERR       U14ERR(-60)    /* tohost/1401 failed             */
+#define U14ERR_NO386ENH       U14ERR(-80)    /* not 386 enhanced mode          */
+#define U14ERR_NO1401DRIV     U14ERR(-81)    /* no device driver               */
+#define U14ERR_DRIVTOOOLD     U14ERR(-82)    /* device driver too old          */
+
+#define U14ERR_TIMEOUT        U14ERR(-90)    /* timeout occurred               */
+
+#define U14ERR_BUFF_SMALL     U14ERR(-100)   /* buffer for getstring too small */
+#define U14ERR_CBALREADY      U14ERR(-101)   /* there is already a callback    */
+#define U14ERR_BADDEREG       U14ERR(-102)   /* bad parameter to deregcallback */
+#define U14ERR_NOMEMORY       U14ERR(-103)   /* no memory for allocation       */
+
+#define U14ERR_DRIVCOMMS      U14ERR(-110)   /* failed talking to driver       */
+#define U14ERR_OUTOFMEMORY    U14ERR(-111)   /* needed memory and couldnt get it*/
+
+/// 1401 type codes.
+#define U14TYPE1401           0           /* standard 1401                  */
+#define U14TYPEPLUS           1           /* 1401 plus                      */
+#define U14TYPEU1401          2           /* u1401                          */
+#define U14TYPEPOWER          3           /* power1401                      */
+#define U14TYPEU14012         4           /* u1401 mk II                    */
+#define U14TYPEPOWER2         5           /* power1401 mk II                */
+#define U14TYPEU14013         6           /* u1401-3                        */
+#define U14TYPEPOWER3         7           /* power1401-3                    */
+#define U14TYPEUNKNOWN        -1          /* dont know                      */
+
+/// Transfer flags to allow driver capabilities to be interrogated
+
+/// Constants for transfer flags
+#define U14TF_USEDMA          1           /* Transfer flag for use DMA      */
+#define U14TF_MULTIA          2           /* Transfer flag for multi areas  */
+#define U14TF_FIFO            4           /* for FIFO interface card        */
+#define U14TF_USB2            8           /* for USB2 interface and 1401    */
+#define U14TF_NOTIFY          16          /* for event notifications        */
+#define U14TF_SHORT           32          /* for PCI can short cycle        */
+#define U14TF_PCI2            64          /* for new PCI card 1401-70       */
+#define U14TF_CIRCTH          128         /* Circular-mode to host          */
+#define U14TF_DIAG            256         /* Diagnostics/debug functions    */
+#define U14TF_CIRC14          512         /* Circular-mode to 1401          */
+
+/// Definitions of element sizes for DMA transfers - to allow byte-swapping
+#define ESZBYTES              0           /* BYTE element size value        */
+#define ESZWORDS              1           /* WORD element size value        */
+#define ESZLONGS              2           /* long element size value        */
+#define ESZUNKNOWN            0           /* unknown element size value     */
+
+/// These define required access types for the debug/diagnostics function
+#define BYTE_SIZE             1           /* 8-bit access                   */
+#define WORD_SIZE             2           /* 16-bit access                  */
+#define LONG_SIZE             3           /* 32-bit access                  */
+
+/// Stuff used by U14_GetTransfer
+#define GET_TX_MAXENTRIES  257          /* (max length / page size + 1) */
+
+#ifdef _IS_WINDOWS_
+#pragma pack(1)
+
+typedef struct                          /* used for U14_GetTransfer results */
+{                                          /* Info on a single mapped block */
+   U14LONG physical;
+   U14LONG size;
+} TXENTRY;
+
+typedef struct TGetTxBlock              /* used for U14_GetTransfer results */
+{                                               /* matches structure in VXD */
+   U14LONG size;
+   U14LONG linear;
+   short   seg;
+   short   reserved;
+   short   avail;                      /* number of available entries */
+   short   used;                       /* number of used entries */
+   TXENTRY entries[GET_TX_MAXENTRIES];       /* Array of mapped block info */
+} TGET_TX_BLOCK;
+
+typedef TGET_TX_BLOCK *LPGET_TX_BLOCK;
+
+#pragma pack()
+#endif
+
+#ifdef LINUX
+typedef struct                          /* used for U14_GetTransfer results */
+{                                       /* Info on a single mapped block */
+   long long physical;
+   long     size;
+} TXENTRY;
+
+typedef struct TGetTxBlock              /* used for U14_GetTransfer results */
+{                                       /* matches structure in VXD */
+   long long linear;                    /* linear address */
+   long     size;                       /* total size of the mapped area, holds id when called */
+   short    seg;                        /* segment of the address for Win16 */
+   short    reserved;
+   short    avail;                      /* number of available entries */
+   short    used;                       /* number of used entries */
+   TXENTRY  entries[GET_TX_MAXENTRIES]; /* Array of mapped block info */
+} TGET_TX_BLOCK;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+U14API(int)   U14WhenToTimeOut(short hand);         // when to timeout in ms
+U14API(short) U14PassedTime(int iTime);             // non-zero if iTime passed
+
+U14API(short) U14LastErrCode(short hand);
+
+U14API(short) U14Open1401(short n1401);
+U14API(short) U14Close1401(short hand);
+U14API(short) U14Reset1401(short hand);
+U14API(short) U14ForceReset(short hand);
+U14API(short) U14TypeOf1401(short hand);
+U14API(short) U14NameOf1401(short hand, char* pBuf, WORD wMax);
+
+U14API(short) U14Stat1401(short hand);
+U14API(short) U14CharCount(short hand);
+U14API(short) U14LineCount(short hand);
+
+U14API(short) U14SendString(short hand, const char* pString);
+U14API(short) U14GetString(short hand, char* pBuffer, WORD wMaxLen);
+U14API(short) U14SendChar(short hand, char cChar);
+U14API(short) U14GetChar(short hand, char* pcChar);
+
+U14API(short) U14LdCmd(short hand, const char* command);
+U14API(DWORD) U14Ld(short hand, const char* vl, const char* str);
+
+U14API(short) U14SetTransArea(short hand, WORD wArea, void *pvBuff,
+                                            DWORD dwLength, short eSz);
+U14API(short) U14UnSetTransfer(short hand, WORD wArea);
+U14API(short) U14SetTransferEvent(short hand, WORD wArea, BOOL bEvent,
+                                  BOOL bToHost, DWORD dwStart, DWORD dwLength);
+U14API(int)   U14TestTransferEvent(short hand, WORD wArea);
+U14API(int)   U14WaitTransferEvent(short hand, WORD wArea, int msTimeOut);
+U14API(short) U14GetTransfer(short hand, TGET_TX_BLOCK *pTransBlock);
+
+U14API(short) U14ToHost(short hand, char* pAddrHost,DWORD dwSize,DWORD dw1401,
+                                                            short eSz);
+U14API(short) U14To1401(short hand, const char* pAddrHost,DWORD dwSize,DWORD dw1401,
+                                                            short eSz);
+
+U14API(short) U14SetCircular(short hand, WORD wArea, BOOL bToHost, void *pvBuff,
+                                         DWORD dwLength);
+
+U14API(int)   U14GetCircBlk(short hand, WORD wArea, DWORD *pdwOffs);
+U14API(int)   U14FreeCircBlk(short hand, WORD wArea, DWORD dwOffs, DWORD dwSize,
+                                         DWORD *pdwOffs);
+
+U14API(short) U14StrToLongs(const char* pszBuff, U14LONG *palNums, short sMaxLongs);
+U14API(short) U14LongsFrom1401(short hand, U14LONG *palBuff, short sMaxLongs);
+
+U14API(void)  U14SetTimeout(short hand, int lTimeout);
+U14API(int)   U14GetTimeout(short hand);
+U14API(short) U14OutBufSpace(short hand);
+U14API(int)   U14BaseAddr1401(short hand);
+U14API(int)   U14DriverVersion(short hand);
+U14API(int)   U14DriverType(short hand);
+U14API(short) U14DriverName(short hand, char* pBuf, WORD wMax);
+U14API(short) U14GetUserMemorySize(short hand, DWORD *pMemorySize);
+U14API(short) U14KillIO1401(short hand);
+
+U14API(short) U14BlkTransState(short hand);
+U14API(short) U14StateOf1401(short hand);
+
+U14API(short) U14Grab1401(short hand);
+U14API(short) U14Free1401(short hand);
+U14API(short) U14Peek1401(short hand, DWORD dwAddr, int nSize, int nRepeats);
+U14API(short) U14Poke1401(short hand, DWORD dwAddr, DWORD dwValue, int nSize, int nRepeats);
+U14API(short) U14Ramp1401(short hand, DWORD dwAddr, DWORD dwDef, DWORD dwEnable, int nSize, int nRepeats);
+U14API(short) U14RampAddr(short hand, DWORD dwDef, DWORD dwEnable, int nSize, int nRepeats);
+U14API(short) U14StopDebugLoop(short hand);
+U14API(short) U14GetDebugData(short hand, U14LONG *plValue);
+
+U14API(short) U14StartSelfTest(short hand);
+U14API(short) U14CheckSelfTest(short hand, U14LONG *pData);
+U14API(short) U14TransferFlags(short hand);
+U14API(void)  U14GetErrorString(short nErr, char* pStr, WORD wMax);
+U14API(int)   U14MonitorRev(short hand);
+U14API(void)  U14CloseAll(void);
+
+U14API(short) U14WorkingSet(DWORD dwMinKb, DWORD dwMaxKb);
+U14API(int)   U14InitLib(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* End of ifndef __USE1401_H__ */

+ 301 - 0
drivers/staging/ced1401/use14_ioc.h

@@ -0,0 +1,301 @@
+/* use14_ioc.h
+** definitions of use1401 module stuff that is shared between use1401 and the driver.
+** Copyright (C) Cambridge Electronic Design Limited 2010
+** Author Greg P Smith
+************************************************************************************/
+#ifndef __USE14_IOC_H__
+#define __USE14_IOC_H__
+
+#define  MAX_TRANSAREAS   8   /* The number of transfer areas supported by driver */
+
+#define i386
+#include "winioctl.h"                   /* needed so we can access driver   */
+
+/*
+** Defines for IOCTL functions to ask driver to perform. These must be matched
+** in both use1401 and in the driver. The IOCTL code contains a command
+** identifier, plus other information about the device, the type of access
+** with which the file must have been opened, and the type of buffering.
+** The IOCTL function codes from 0x80 to 0xFF are for developer use.
+*/
+#define  FILE_DEVICE_CED1401    0x8001
+#define  FNNUMBASE              0x800
+
+#define  U14_OPEN1401            CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE,               \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_CLOSE1401           CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+1,             \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_SENDSTRING          CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+2,             \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_RESET1401           CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+3,             \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_GETCHAR             CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+4,             \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_SENDCHAR            CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+5,             \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_STAT1401            CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+6,             \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_LINECOUNT           CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+7,             \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_GETSTRING           CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+8,             \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_REGCALLBACK         CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+9,             \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_GETMONITORBUF       CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+10,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_SETTRANSFER         CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+11,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_UNSETTRANSFER       CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+12,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_SETTRANSEVENT       CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+13,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_GETOUTBUFSPACE      CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+14,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_GETBASEADDRESS      CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+15,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_GETDRIVERREVISION   CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+16,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_GETTRANSFER         CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+17,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_KILLIO1401          CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+18,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_BLKTRANSSTATE       CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+19,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_BYTECOUNT           CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+20,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_ZEROBLOCKCOUNT      CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+21,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_STOPCIRCULAR        CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+22,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_STATEOF1401         CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+23,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_REGISTERS1401       CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+24,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_GRAB1401            CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+25,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_FREE1401            CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+26,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_STEP1401            CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+27,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_SET1401REGISTERS    CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+28,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_STEPTILL1401        CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+29,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_SETORIN             CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+30,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_STARTSELFTEST       CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+31,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_CHECKSELFTEST       CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+32,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_TYPEOF1401          CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+33,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_TRANSFERFLAGS       CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+34,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_DBGPEEK             CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+35,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_DBGPOKE             CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+36,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_DBGRAMPDATA         CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+37,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_DBGRAMPADDR         CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+38,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_DBGGETDATA          CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+39,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_DBGSTOPLOOP         CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+40,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_FULLRESET           CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+41,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_SETCIRCULAR         CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+42,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_GETCIRCBLK          CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+43,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+#define  U14_FREECIRCBLK         CTL_CODE( FILE_DEVICE_CED1401,     \
+                                           FNNUMBASE+44,            \
+                                           METHOD_BUFFERED,         \
+                                           FILE_ANY_ACCESS)
+
+//--------------- Structures that are shared with the driver -------------
+#pragma pack(1)
+
+typedef struct                  /* used for get/set standard 1401 registers */
+{
+   short   sPC;
+   char    A;
+   char    X;
+   char    Y;
+   char    stat;
+   char    rubbish;
+} T1401REGISTERS;
+
+typedef union     /* to communicate with 1401 driver status & control funcs */
+{
+   char           chrs[22];
+   short          ints[11];
+   long           longs[5];
+   T1401REGISTERS registers;
+} TCSBLOCK;
+
+typedef TCSBLOCK*  LPTCSBLOCK;
+
+typedef struct paramBlk
+{
+    short       sState;
+    TCSBLOCK    csBlock;
+} PARAMBLK;
+
+typedef PARAMBLK*   PPARAMBLK;
+
+typedef struct TransferDesc          /* Structure and type for SetTransArea */
+{
+   WORD        wArea;            /* number of transfer area to set up       */
+   void FAR *  lpvBuff;          /* address of transfer area                */
+   DWORD       dwLength;         /* length of area to set up                */
+   short       eSize;            /* size to move (for swapping on MAC)      */
+} TRANSFERDESC;
+
+typedef TRANSFERDESC FAR *    LPTRANSFERDESC;
+
+/* This is the structure used to set up a transfer area */
+typedef struct VXTransferDesc    /* use1401.c and use1432x.x use only       */
+{
+   WORD        wArea;            /* number of transfer area to set up       */
+   WORD        wAddrSel;         /* 16 bit selector for area                */
+   DWORD       dwAddrOfs;        /* 32 bit offset for area start            */
+   DWORD       dwLength;         /* length of area to set up                */
+} VXTRANSFERDESC;
+
+#pragma pack()
+
+#endif

+ 3035 - 0
drivers/staging/ced1401/userspace/use1401.c

@@ -0,0 +1,3035 @@
+/****************************************************************************
+** use1401.c
+** Copyright (C) Cambridge Electronic Design Ltd, 1992-2010
+**
+** 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+**
+** Contact CED: Cambridge Electronic Design Limited, Science Park, Milton Road
+**              Cambridge, CB6 0FE.
+**              www.ced.co.uk
+**              greg@ced.co.uk
+**
+**  Title:      USE1401.C
+**  Version:    4.00
+**  Author:     Paul Cox, Tim Bergel, Greg Smith
+**
+** The code was vigorously pruned in DEC 2010 to remove the macintosh options
+** and to get rid of the 16-bit support. It has also been aligned with the
+** Linux version. See CVS for revisions. This will work for Win 9x onwards.
+****************************************************************************
+**
+** Notes on Windows interface to driver
+** ************************************
+**
+** Under Windows 9x and NT, Use1401 uses DeviceIoControl to get access to
+** the 1401 driver. This has parameters for the device handle, the function
+** code, an input pointer and byte count, an output pointer and byte count
+** and a pointer to a DWORD to hold the output byte count. Note that input
+** and output are from the point-of-view of the driver, so the output stuff
+** is used to read values from the 1401, not send to the 1401. The use of
+** these parameters varies with the function in use and the operating
+** system; there are five separate DIOC calls SendString, GetString and
+** SetTransferArea all have their own specialised calls, the rest use the
+** Status1401 or Control1401 functions.
+**
+** There are two basic styles of DIOC call used, one for Win9x VxD drivers
+** and one for NT Kernel-mode and WDM drivers (see below for tables showing
+** the different parameters used. The array bUseNTDIOC[] selects between
+** these two calling styles.
+**
+** Function codes
+** In Win3.x, simple function codes from 0 to 40 were used, shifted left 8
+** bits with a sub-function code in the lower 8 bits. These were also used
+** in the Windows 95 driver, though we had to add 1 to the code value to
+** avoid problems (Open from CreateFile is zero), and the sub-function code
+** is now unused. We found that this gave some problems with Windows 98
+** as the function code values are reserved by microsoft, so we switched to
+** using the NT function codes instead. The NT codes are generated using the
+** CTL_CODE macro, essentially this gives 0x80012000 | (func << 2), where
+** func is the original 0 to 34 value. The driver will handle both types of
+** code and Use1432 only uses the NT codes if it knows the driver is new
+** enough. The array bUseNTCodes[] holds flags on the type of codes required.
+** GPS/TDB Dec 2010: we removed the bUseNTCodes array as this is always true
+** as we no longer support ancient versions.
+**
+** The CreateFile and CloseFile function calls are also handled
+** by DIOC, using the special function codes 0 and -1 respectively.
+**
+** Input pointer and buffer size
+** These are intended for data sent to the device driver. In nearly all cases
+** they are unused in calls to the Win95 driver, the NT driver uses them
+** for all information sent to the driver. The table below shows the pointer
+** and byte count used for the various calls:
+**
+**                      Win 95                  Win NT
+** SendString           NULL, 0                 pStr, nStr
+** GetString            NULL, 0                 NULL, 0
+** SetTransferArea      pBuf, nBuf (unused?)    pDesc, nDesc
+** GetTransfer          NULL, 0                 NULL, 0
+** Status1401           NULL, 0                 NULL, 0
+** Control1401          NULL, 0                 pBlk, nBlk
+**
+** pStr and nStr are pointers to a char buffer and the buffer length for
+** string I/O, note that these are temporary buffers owned by the DLL, not
+** application memory, pBuf and nBuf are the transfer area buffer (I think
+** these are unused), pDesc and nDesc are the TRANSFERDESC structure, pBlk
+** and nBlk are the TCSBLOCK structure.
+**
+**
+** Output pointer and buffer size
+** These are intended for data read from the device driver. These are used
+** for almost all information sent to the Win95 driver, the NT driver uses
+** them for information read from the driver, chiefly the error code. The
+** table below shows the pointer and byte count used for the various calls:
+**
+**                      Win 95                  Win NT
+** SendString           pStr, nStr              pPar, nPar
+** GetString            pStr, nStr+2            pStr, nStr+2
+** SetTransferArea      pDesc, nDesc            pPar, nPar
+** GetTransfer          pGet, nGet              pGet, nGet
+** Status1401           pBlk, nBlk              pPar, nPar
+** Control1401          pBlk, nBlk              pPar, nPar
+**
+** pStr and nStr are pointers to a char buffer and the buffer length for
+** string I/O, the +2 for GetString refers to two spare bytes at the start
+** used to hold the string length and returning an error code for NT. Note
+** again that these are (and must be) DLL-owned temporary buffers. pPar
+** and nPar are a PARAM structure used in NT (it holds an error code and a 
+** TCSBLOCK structure). pDesc and nDesc are the VXTRANSFERDESC structure,
+** pBlk and nBlk are the TCSBLOCK structure. pGet and nGet indicate the
+** TGET_TX_BLOCK structure used for GetTransfer.
+**
+**
+** The output byte count
+** Both drivers return the output buffer size here, regardless of the actual
+** bytes output. This is used to check that we did get through to the driver.
+**
+** Multiple 1401s
+** **************
+**
+** We have code that tries to support the use of multiple 1401s, but there
+** are problems: The lDriverVersion and lDriverType variables are global, not
+** per-1401 (a particular problem as the U14 functions that use them don't
+** have a hand parameter). In addition, the mechansim for finding a free
+** 1401 depends upon the 1401 device driver open operation failing if it's
+** already in use, which doesn't always happen, particularly with the VxDs.
+** The code in TryToOpen tries to fix this by relying on TYPEOF1401 to detect
+** the 1401-in-use state - the VxDs contain special code to help this. This is
+** working OK but multiple 1401 support works better with the Win2000 drivers.
+**
+** USB driver
+** **********
+**
+** The USB driver, which runs on both Win98 and NT2000, uses the NT-style
+** calling convention, both for the DIOC codes and the DIOC parameters. The
+** TryToOpen function has been altered to look for an NT driver first in
+** the appropriate circumstances, and to set the driver DIOC flags up in
+** the correct state.
+**
+** Adding a new 1401 type - now almost nothing to do
+** *************************************************
+**
+** The 1401 types are defined by a set of U14TYPExxxx codes in USE1401.H.
+** You should add a new one of these to keep things tidy for applications.
+**
+** DRIVERET_MAX (below) specifies the maximum allowed type code from the
+** 1401 driver; I have set this high to accomodate as yet undesigned 1401
+** types. Similarly, as long as the command file names follow the ARM,
+** ARN, ARO sequence, these are calculated by the ExtForType function, so
+** you don't need to do anything here either.
+**
+** Version number
+** **************
+** The new U14InitLib() function returns 0 if the OS is incapable of use,
+** otherwise is returns the version of the USE1401 library. This is done
+** in three parts: Major(31-24).Minor(23-16).Revision.(15-0) (brackets are
+** the bits used). The Major number starts at 2 for the first revision with
+** the U14InitLib() function. Changes to the Major version means that we
+** have broken backwards compatibility. Minor number changes mean that we
+** have added new functionality that does not break backwards compatibility.
+** we starts at 0. Revision changes mean we have fixed something. Each index
+** returns to 0 when a higer one changes.
+*/
+#define U14LIB_MAJOR 4
+#define U14LIB_MINOR 0
+#define U14LIB_REVISION 0
+#define U14LIB_VERSION ((U14LIB_MAJOR<<24) | (U14LIB_MINOR<<16) | U14LIB_REVISION)
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "USE1401.H"
+
+#ifdef _IS_WINDOWS_
+#include <io.h>
+#include <windows.h>
+#pragma warning(disable: 4100) /* Disable "Unused formal parameter" warning */
+#include <assert.h>
+#include "process.h"
+
+
+#define sprintf wsprintf
+#define PATHSEP '\\'
+#define PATHSEPSTR "\\"
+#define DEFCMDPATH "\\1401\\"   // default command path if all else fails
+#define MINDRIVERMAJREV 1       // minimum driver revision level we need
+#define __packed                // does nothing in Windows
+
+#include "use14_ioc.h"          // links to device driver stuff
+#endif
+
+#ifdef LINUX
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sched.h>
+#include <libgen.h>
+#define PATHSEP '/'
+#define PATHSEPSTR "/"
+#define DEFCMDPATH "/var/1401/" // default command path if all else fails
+#define MINDRIVERMAJREV 2       // minimum driver revision level we need
+
+#include "ced_ioctl.h"          // links to device driver stuff
+#endif
+
+#define MAX1401         8       // The number of 1401s that can be supported
+
+/*
+** These are the 1401 type codes returned by the driver, they are a slightly
+** odd sequence & start for reasons of compatability with the DOS driver.
+** The maximum code value is the upper limit of 1401 device types.
+*/
+#define DRIVRET_STD     4       // Codes for 1401 types matching driver values
+#define DRIVRET_U1401   5       // This table does not need extending, as
+#define DRIVRET_PLUS    6       // we can calculate values now.
+#define DRIVRET_POWER   7       // but we need all of these values still
+#define DRIVRET_MAX     26      // Maximum tolerated code - future designs
+
+/*
+** These variables store data that will be used to generate the last
+** error string. For now, a string will hold the 1401 command file name.
+*/
+static char szLastName[20];     // additional text information
+
+/*
+** Information stored per handle. NBNB, driverType and DriverVersion used to be
+** only stored once for all handles... i.e. nonsensical. This change means that
+** three U14...() calls now include handles that were previously void. We have
+** set a constructor and a destructor call for the library (see the end) to
+** initialise important structures, or call use1401_load().
+*/
+static short asDriverType[MAX1401] = {0};
+static int lLastDriverVersion = U14ERR_NO1401DRIV;
+static int lLastDriverType = U14TYPEUNKNOWN;
+static int alDriverVersion[MAX1401];            // version/type of each driver
+static int alTimeOutPeriod[MAX1401];            // timeout time in milliseconds
+static short asLastRetCode[MAX1401];            // last code from a fn call
+static short asType1401[MAX1401] = {0};         // The type of the 1401
+static BOOL abGrabbed[MAX1401] = {0};           // Flag for grabbed, set true by grab1401
+static int iAttached = 0;                       // counts process attaches so can let go
+
+#ifdef _IS_WINDOWS_
+/****************************************************************************
+** Windows NT Specific Variables and internal types
+****************************************************************************/
+static HANDLE aHand1401[MAX1401] = {0};         // handles for 1401s
+static HANDLE aXferEvent[MAX1401] = {0};        // transfer events for the 1401s
+static LPVOID apAreas[MAX1401][MAX_TRANSAREAS]; // Locked areas
+static DWORD  auAreas[MAX1401][MAX_TRANSAREAS]; // Size of locked areas
+static BOOL   bWindows9x = FALSE;               // if we are Windows 95 or better
+#ifdef _WIN64
+#define USE_NT_DIOC(ind) TRUE
+#else
+static BOOL   abUseNTDIOC[MAX1401];             // Use NT-style DIOC parameters */
+#define USE_NT_DIOC(ind) abUseNTDIOC[ind]
+#endif
+
+#endif
+
+#ifdef LINUX
+static int aHand1401[MAX1401] = {0};    // handles for 1401s
+#define INVALID_HANDLE_VALUE 0          // to avoid code differences
+#endif
+
+
+/*
+** The CmdHead relates to backwards compatibility with ancient Microsoft (and Sperry!)
+** versions of BASIC, where this header was needed so we could load a command into
+** memory.
+*/
+#pragma pack(1)                 // pack our structure
+typedef struct CmdHead          // defines header block on command
+{                               // for PC commands
+   char   acBasic[5];           // BASIC information - needed to align things
+   WORD   wBasicSz;             // size as seen by BASIC
+   WORD   wCmdSize;             // size of the following info
+} __packed CMDHEAD;
+#pragma pack()                  // back to normal
+
+/*
+** The rest of the header looks like this...
+**  int    iRelPnt;             relocation pointer... actual start
+**  char   acName[8];           string holding the command name
+**  BYTE   bMonRev;             monitor revision level
+**  BYTE   bCmdRev;             command revision level
+*/
+
+typedef CMDHEAD *LPCMDHEAD;     // pointer to a command header
+
+#define  MAXSTRLEN   255        // maximum string length we use
+#define  TOHOST      FALSE
+#define  TO1401      TRUE
+
+static short CheckHandle(short h)
+{
+    if ((h < 0) || (h >= MAX1401))  // must be legal range...
+        return U14ERR_BADHAND;
+    if (aHand1401[h] <= 0)          // must be open
+        return U14ERR_BADHAND;
+    return U14ERR_NOERROR;
+}
+
+#ifdef _IS_WINDOWS_
+/****************************************************************************
+** U14Status1401    Used for functions which do not pass any data in but
+**                  get data back
+****************************************************************************/
+static short U14Status1401(short sHand, LONG lCode, TCSBLOCK* pBlk)
+{
+    DWORD dwBytes = 0;
+
+    if ((sHand < 0) || (sHand >= MAX1401))  /* Check parameters */
+        return U14ERR_BADHAND;
+#ifndef _WIN64
+    if (!USE_NT_DIOC(sHand)) 
+    {   /* Windows 9x DIOC methods? */
+        if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, pBlk,sizeof(TCSBLOCK),&dwBytes,NULL))
+            return (short)((dwBytes>=sizeof(TCSBLOCK)) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS);
+        else
+            return (short)GetLastError();
+    }
+    else
+#endif
+    {                                       /* Windows NT or USB driver */
+        PARAMBLK rWork;
+        rWork.sState = U14ERR_DRIVCOMMS;
+        if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, &rWork,sizeof(PARAMBLK),&dwBytes,NULL) &&
+            (dwBytes >= sizeof(PARAMBLK)))
+        {
+            *pBlk = rWork.csBlock;
+            return rWork.sState;
+        }
+    }
+
+    return U14ERR_DRIVCOMMS;
+}
+
+/****************************************************************************
+** U14Control1401   Used for functions which pass data in and only expect
+**                  an error code back
+****************************************************************************/
+static short U14Control1401(short sHand, LONG lCode, TCSBLOCK* pBlk)
+{
+    DWORD dwBytes = 0;
+
+    if ((sHand < 0) || (sHand >= MAX1401))              /* Check parameters */
+        return U14ERR_BADHAND;
+
+#ifndef _WIN64
+    if (!USE_NT_DIOC(sHand))                    
+    {                            /* Windows 9x DIOC methods */
+        if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, pBlk, sizeof(TCSBLOCK), &dwBytes, NULL))
+            return (short)(dwBytes >= sizeof(TCSBLOCK) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS);
+        else
+            return (short)GetLastError();
+    }
+    else
+#endif
+    {                            /* Windows NT or later */
+        PARAMBLK rWork;
+        rWork.sState = U14ERR_DRIVCOMMS;
+        if (DeviceIoControl(aHand1401[sHand], lCode, pBlk, sizeof(TCSBLOCK), &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
+            (dwBytes >= sizeof(PARAMBLK)))
+            return rWork.sState;
+    }
+
+    return U14ERR_DRIVCOMMS;
+}
+#endif
+
+/****************************************************************************
+** SafeTickCount
+** Gets time in approximately units of a millisecond.
+*****************************************************************************/
+static long SafeTickCount()
+{
+#ifdef _IS_WINDOWS_
+    return GetTickCount();
+#endif
+#ifdef LINUX
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return (tv.tv_sec*1000 + tv.tv_usec/1000);
+#endif
+}
+
+/****************************************************************************
+** A utility routine to get the command file extension for a given type
+** of 1401. We assume the type code is vaguely legal.
+****************************************************************************/
+static int ExtForType(short sType, char* szExt)
+{
+    szExt[0] = 0;                       /* Default return is a blank string */
+    switch (sType)
+    {
+    case U14TYPE1401: strcpy(szExt, ".CMD");  break;    // Standard 1401
+    case U14TYPEPLUS: strcpy(szExt, ".GXC");  break;    // 1401 plus
+    default:               // All others are in a predictable sequence
+        strcpy(szExt, ".ARM");
+            szExt[3] = (char)('M' + sType - U14TYPEU1401);
+        if (szExt[3] > 'Z')             // Wrap round to ARA after ARZ
+                szExt[3] = (char)(szExt[3] - 26);
+    }
+    return 0;
+}
+
+/****************************************************************************
+**   U14WhenToTimeOut
+**       Returns the time to time out in time units suitable for the machine
+** we are running on  ie millsecs for pc/linux, or Mac/
+****************************************************************************/
+U14API(int) U14WhenToTimeOut(short hand)
+{
+    int iNow = SafeTickCount();
+    if ((hand >= 0) && (hand < MAX1401))
+        iNow += alTimeOutPeriod[hand];
+    return iNow;
+}
+
+/****************************************************************************
+** U14PassedTime
+** Returns non zero if the timed passed in has been passed 0 if not
+****************************************************************************/
+U14API(short) U14PassedTime(int lCheckTime)
+{
+    return (short)((SafeTickCount()-lCheckTime) > 0);
+}
+
+/****************************************************************************
+** TranslateString
+** Tidies up string that U14GetString returns. Converts all the commas in a
+** string to spaces. Removes terminating CR character. May do more in future.
+****************************************************************************/
+static void TranslateString(char* pStr)
+{
+    int i = 0;
+    while (pStr[i])
+    {
+        if (pStr[i] == ',')
+            pStr[i] = ' ';              /* convert comma to space */
+        ++i;
+    }
+
+    if ((i > 0) && (pStr[i-1] == '\n'))  /* kill terminating LF */
+        pStr[i-1] = (char)0;
+}
+
+/****************************************************************************
+** U14StrToLongs
+** Converts a string to an array of longs and returns the number of values
+****************************************************************************/
+U14API(short) U14StrToLongs(const char* pszBuff, U14LONG *palNums, short sMaxLongs)
+{
+    WORD wChInd = 0;                // index into source
+    short sLgInd = 0;               // index into result longs
+
+    while (pszBuff[wChInd] &&       // until we get to end of string...
+           (sLgInd < sMaxLongs))    // ...or filled the buffer
+    {
+        // Why not use a C Library converter?
+        switch (pszBuff[wChInd])
+        {
+        case '-':
+        case '0': case '1':   case '2': case '3':   case '4':
+        case '5': case '6':   case '7': case '8':   case '9':
+            {
+                BOOL bDone = FALSE; // true at end of number
+                int iSign = 1;      // sign of number
+                long lValue = 0;
+
+                while ((!bDone) && pszBuff[wChInd])
+                {
+                    switch (pszBuff[wChInd])
+                    {
+                    case '-':
+                        iSign = -1; // swap sign
+                        break;
+
+                    case '0': case '1':   case '2': case '3':   case '4':
+                    case '5': case '6':   case '7': case '8':   case '9':
+                        lValue *= 10;   // move to next digit base 10
+                        lValue += ((int)pszBuff[wChInd]-(int)'0');
+                        break;
+
+                    default:        // end of number
+                        bDone = TRUE;
+                        break;
+                    }
+                    wChInd++;       // move onto next character
+                }
+                palNums[sLgInd] = lValue * iSign;
+                sLgInd++;
+            }
+            break;
+
+        default:
+            wChInd++;               // look at next char
+            break;
+        }
+    }
+    return (sLgInd);
+}
+
+
+/****************************************************************************
+** U14LongsFrom1401
+** Gets the next waiting line from the 1401 and converts it longs
+** Returns the number of numbers read or an error.
+****************************************************************************/
+U14API(short) U14LongsFrom1401(short hand, U14LONG *palBuff, short sMaxLongs)
+{
+    char szWork[MAXSTRLEN];
+    short sResult = U14GetString(hand, szWork, MAXSTRLEN);/* get reply from 1401   */
+    if (sResult == U14ERR_NOERROR)                  /* if no error convert   */
+        sResult = U14StrToLongs(szWork, palBuff, sMaxLongs);
+    return sResult;
+}
+
+/****************************************************************************
+**   U14CheckErr
+**   Sends the ERR command to the 1401 and gets the result. Returns 0, a
+**   negative error code, or the first error value.
+****************************************************************************/
+U14API(short) U14CheckErr(short hand)
+{
+    short sResult = U14SendString(hand, ";ERR;");
+    if (sResult == U14ERR_NOERROR)
+    {
+        U14LONG er[3];
+        sResult = U14LongsFrom1401(hand, er, 3);
+        if (sResult > 0)
+        {
+            sResult = (short)er[0];        /* Either zero or an error value */
+#ifdef _DEBUG
+            if (er[0] != 0)
+            {
+                char szMsg[50];
+                sprintf(szMsg, "U14CheckErr returned %d,%d\n", er[0], er[1]);
+                OutputDebugString(szMsg);
+            }
+#endif
+        }
+        else
+        {
+            if (sResult == 0)
+                sResult = U14ERR_TIMEOUT;      /* No numbers equals timeout */
+        }
+    }
+
+    return sResult;
+}
+
+/****************************************************************************
+** U14LastErrCode
+** Returns the last code from the driver. This is for Windows where all calls
+** go through the Control and Status routines, so we can save any error.
+****************************************************************************/
+U14API(short) U14LastErrCode(short hand)
+{
+    if ((hand < 0) || (hand >= MAX1401))
+        return U14ERR_BADHAND;
+    return asLastRetCode[hand];
+}
+
+/****************************************************************************
+** U14SetTimeout
+** Set the timeout period for 1401 comms in milliseconds
+****************************************************************************/
+U14API(void) U14SetTimeout(short hand, int lTimeOut)
+{
+    if ((hand < 0) || (hand >= MAX1401))
+        return;
+    alTimeOutPeriod[hand] = lTimeOut;
+}
+
+/****************************************************************************
+** U14GetTimeout
+** Get the timeout period for 1401 comms in milliseconds
+****************************************************************************/
+U14API(int) U14GetTimeout(short hand)
+{
+    if ((hand < 0) || (hand >= MAX1401))
+        return U14ERR_BADHAND;
+    return alTimeOutPeriod[hand];
+}
+
+/****************************************************************************
+** U14OutBufSpace
+** Return the space in the output buffer, or an error.
+****************************************************************************/
+U14API(short) U14OutBufSpace(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    short sErr = U14Status1401(hand, U14_GETOUTBUFSPACE,&csBlock);
+    if (sErr == U14ERR_NOERROR)
+        sErr = csBlock.ints[0];
+    return sErr;
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_GetOutBufSpace(aHand1401[hand]) : sErr;
+#endif
+}
+
+
+/****************************************************************************
+** U14BaseAddr1401
+** Returns the 1401 base address or an error code. Meaningless nowadays
+****************************************************************************/
+U14API(int) U14BaseAddr1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    int iError = U14Status1401(hand, U14_GETBASEADDRESS,&csBlock);
+    if (iError == U14ERR_NOERROR)
+        iError = csBlock.longs[0];
+    return iError;
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_GetBaseAddress(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14StateOf1401
+** Return error state, either NOERROR or a negative code.
+****************************************************************************/
+U14API(short) U14StateOf1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    short sErr = U14Status1401(hand, U14_STATEOF1401, &csBlock);
+    if (sErr == U14ERR_NOERROR)
+    {
+        sErr = csBlock.ints[0];      // returned 1401 state
+        if ((sErr >= DRIVRET_STD) && (sErr <= DRIVRET_MAX))
+            sErr = U14ERR_NOERROR;
+    }
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    if (sErr == U14ERR_NOERROR)
+    {
+        sErr = (short)CED_StateOf1401(aHand1401[hand]);
+        if ((sErr >= DRIVRET_STD) && (sErr <= DRIVRET_MAX))
+            sErr = U14ERR_NOERROR;
+    }
+#endif
+    return sErr;
+}
+
+/****************************************************************************
+** U14DriverVersion
+** Returns the driver version. Hi word is major revision, low word is minor.
+** If you pass in a silly handle (like -1), we return the version of the last
+** driver we know of (to cope with PCI and no 1401 attached).
+****************************************************************************/
+U14API(int) U14DriverVersion(short hand)
+{
+    return CheckHandle(hand) != U14ERR_NOERROR ? lLastDriverVersion : alDriverVersion[hand];
+}
+
+/****************************************************************************
+** U14DriverType
+** Returns the driver type. The type, 0=ISA/NU-Bus, 1=PCI, 2=USB, 3=HSS
+** If you pass in a silly handle (like -1), we return the type of the last
+** driver we know of (to cope with PCI and no 1401 attached).
+****************************************************************************/
+U14API(int) U14DriverType(short hand)
+{
+    return CheckHandle(hand) != U14ERR_NOERROR ? lLastDriverType : asDriverType[hand];
+}
+
+/****************************************************************************
+** U14DriverName
+** Returns the driver type as 3 character (ISA, PCI, USB or HSS))
+****************************************************************************/
+U14API(short) U14DriverName(short hand, char* pBuf, WORD wMax)
+{
+    char* pName;
+    *pBuf = 0;                             // Start off with a blank string
+    switch (U14DriverType(hand))           // Results according to type
+    {
+    case 0:  pName = "ISA"; break;
+    case 1:  pName = "PCI"; break;
+    case 2:  pName = "USB"; break;
+    case 3:  pName = "HSS"; break;
+    default: pName = "???"; break;
+    }
+    strncpy(pBuf, pName, wMax);            // Copy the correct name to return
+
+    return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+** U14BlkTransState
+** Returns 0 no transfer in progress, 1 transfer in progress or an error code
+****************************************************************************/
+U14API(short) U14BlkTransState(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    short sErr = U14Status1401(hand, U14_BLKTRANSSTATE, &csBlock);
+    if (sErr == U14ERR_NOERROR)
+        sErr = csBlock.ints[0];
+    return sErr;
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_BlkTransState(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14Grab1401
+** Take control of the 1401 for diagnostics purposes. USB does nothing.
+****************************************************************************/
+U14API(short) U14Grab1401(short hand)
+{
+    short sErr = CheckHandle(hand);
+    if (sErr == U14ERR_NOERROR)
+    {
+#ifdef _IS_WINDOWS_
+        if (abGrabbed[hand])            // 1401 should not have been grabbed
+            sErr = U14ERR_ALREADYSET;   // Error code defined for this
+        else
+        {
+            TCSBLOCK csBlock;
+            sErr = U14Control1401(hand, U14_GRAB1401, &csBlock);
+        }
+#endif
+#ifdef LINUX
+        // 1401 should not have been grabbed
+        sErr = abGrabbed[hand] ? U14ERR_ALREADYSET : CED_Grab1401(aHand1401[hand]);
+#endif
+        if (sErr == U14ERR_NOERROR)
+            abGrabbed[hand] = TRUE;
+    }
+    return sErr;
+}
+
+/****************************************************************************
+** U14Free1401
+****************************************************************************/
+U14API(short)  U14Free1401(short hand)
+{
+    short sErr = CheckHandle(hand);
+    if (sErr == U14ERR_NOERROR)
+    {
+#ifdef _IS_WINDOWS_
+        if (abGrabbed[hand])    // 1401 should have been grabbed
+        {
+            TCSBLOCK csBlock;
+            sErr = U14Control1401(hand, U14_FREE1401, &csBlock);
+        }
+        else
+            sErr = U14ERR_NOTSET;
+#endif
+#ifdef LINUX
+        // 1401 should not have been grabbed
+        sErr = abGrabbed[hand] ? CED_Free1401(aHand1401[hand]) : U14ERR_NOTSET;
+#endif
+        if (sErr == U14ERR_NOERROR)
+            abGrabbed[hand] = FALSE;
+    }
+    return sErr;
+}
+
+/****************************************************************************
+** U14Peek1401
+** DESCRIPTION  Cause the 1401 to do one or more peek operations.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop
+** is called. After the peek is done, use U14GetDebugData to retrieve
+** the results of the peek.
+****************************************************************************/
+U14API(short) U14Peek1401(short hand, DWORD dwAddr, int nSize, int nRepeats)
+{
+    short sErr = CheckHandle(hand);
+    if (sErr == U14ERR_NOERROR)
+    {
+        if (abGrabbed[hand])    // 1401 should have been grabbed
+        {
+#ifdef _IS_WINDOWS_
+            TCSBLOCK csBlock;
+            csBlock.longs[0] = (long)dwAddr;
+            csBlock.longs[1] = nSize;
+            csBlock.longs[2] = nRepeats;
+            sErr = U14Control1401(hand, U14_DBGPEEK, &csBlock);
+#endif
+#ifdef LINUX
+            TDBGBLOCK dbb;
+            dbb.iAddr = (int)dwAddr;
+            dbb.iWidth = nSize;
+            dbb.iRepeats = nRepeats;
+            sErr = CED_DbgPeek(aHand1401[hand], &dbb);
+#endif
+        }
+        else
+            sErr = U14ERR_NOTSET;
+    }
+    return sErr;
+}
+
+/****************************************************************************
+** U14Poke1401
+** DESCRIPTION  Cause the 1401 to do one or more poke operations.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop
+** is called.
+****************************************************************************/
+U14API(short) U14Poke1401(short hand, DWORD dwAddr, DWORD dwValue,
+                                      int nSize, int nRepeats)
+{
+    short sErr = CheckHandle(hand);
+    if (sErr == U14ERR_NOERROR)
+    {
+        if (abGrabbed[hand])    // 1401 should have been grabbed
+        {
+#ifdef _IS_WINDOWS_
+            TCSBLOCK csBlock;
+            csBlock.longs[0] = (long)dwAddr;
+            csBlock.longs[1] = nSize;
+            csBlock.longs[2] = nRepeats;
+            csBlock.longs[3] = (long)dwValue;
+            sErr = U14Control1401(hand, U14_DBGPOKE, &csBlock);
+#endif
+#ifdef LINUX
+            TDBGBLOCK dbb;
+            dbb.iAddr = (int)dwAddr;
+            dbb.iWidth = nSize;
+            dbb.iRepeats= nRepeats;
+            dbb.iData = (int)dwValue;
+            sErr = CED_DbgPoke(aHand1401[hand], &dbb);
+#endif
+        }
+        else
+            sErr = U14ERR_NOTSET;
+    }
+    return sErr;
+}
+
+/****************************************************************************
+** U14Ramp1401
+** DESCRIPTION  Cause the 1401 to loop, writing a ramp to a location.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop.
+****************************************************************************/
+U14API(short) U14Ramp1401(short hand, DWORD dwAddr, DWORD dwDef, DWORD dwEnable,
+                                      int nSize, int nRepeats)
+{
+    short sErr = CheckHandle(hand);
+    if (sErr == U14ERR_NOERROR)
+    {
+        if (abGrabbed[hand])    // 1401 should have been grabbed
+        {
+#ifdef _IS_WINDOWS_
+            TCSBLOCK csBlock;
+            csBlock.longs[0] = (long)dwAddr;
+            csBlock.longs[1] = (long)dwDef;
+            csBlock.longs[2] = (long)dwEnable;
+            csBlock.longs[3] = nSize;
+            csBlock.longs[4] = nRepeats;
+            sErr = U14Control1401(hand, U14_DBGRAMPDATA, &csBlock);
+#endif
+#ifdef LINUX
+            TDBGBLOCK dbb;
+            dbb.iAddr = (int)dwAddr;
+            dbb.iDefault = (int)dwDef;
+            dbb.iMask = (int)dwEnable;
+            dbb.iWidth = nSize;
+            dbb.iRepeats = nRepeats;
+            sErr = CED_DbgRampAddr(aHand1401[hand], &dbb);
+#endif
+        }
+        else
+            sErr = U14ERR_NOTSET;
+    }
+    return sErr;
+}
+
+/****************************************************************************
+** U14RampAddr
+** DESCRIPTION  Cause the 1401 to loop, reading from a ramping location.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop
+****************************************************************************/
+U14API(short) U14RampAddr(short hand, DWORD dwDef, DWORD dwEnable,
+                                      int nSize, int nRepeats)
+{
+    short sErr = CheckHandle(hand);
+    if (sErr == U14ERR_NOERROR)
+    {
+        if (abGrabbed[hand])    // 1401 should have been grabbed
+        {
+#ifdef _IS_WINDOWS_
+            TCSBLOCK csBlock;
+            csBlock.longs[0] = (long)dwDef;
+            csBlock.longs[1] = (long)dwEnable;
+            csBlock.longs[2] = nSize;
+            csBlock.longs[3] = nRepeats;
+            sErr = U14Control1401(hand, U14_DBGRAMPADDR, &csBlock);
+#endif
+#ifdef LINUX
+            TDBGBLOCK dbb;
+            dbb.iDefault = (int)dwDef;
+            dbb.iMask = (int)dwEnable;
+            dbb.iWidth = nSize;
+            dbb.iRepeats = nRepeats;
+            sErr = CED_DbgRampAddr(aHand1401[hand], &dbb);
+#endif
+        }
+        else
+            sErr = U14ERR_NOTSET;
+    }
+    return sErr;
+}
+
+/****************************************************************************
+**    U14StopDebugLoop
+**    DESCRIPTION Stops a peek\poke\ramp that, with repeats set to zero,
+**    will otherwise continue forever.
+****************************************************************************/
+U14API(short) U14StopDebugLoop(short hand)
+{
+    short sErr = CheckHandle(hand);
+    if (sErr == U14ERR_NOERROR)
+#ifdef _IS_WINDOWS_
+    {
+        if (abGrabbed[hand])    // 1401 should have been grabbed
+        {
+            TCSBLOCK csBlock;
+            sErr = U14Control1401(hand, U14_DBGSTOPLOOP, &csBlock);
+        }
+        else
+            sErr = U14ERR_NOTSET;
+    }
+#endif
+#ifdef LINUX
+        sErr = abGrabbed[hand] ? CED_DbgStopLoop(aHand1401[hand]) : U14ERR_NOTSET;
+#endif
+    return sErr;
+}
+
+/****************************************************************************
+** U14GetDebugData
+** DESCRIPTION Returns the result from a previous peek operation.
+****************************************************************************/
+U14API(short) U14GetDebugData(short hand, U14LONG* plValue)
+{
+    short sErr = CheckHandle(hand);
+    if (sErr == U14ERR_NOERROR)
+    {
+        if (abGrabbed[hand])    // 1401 should have been grabbed
+        {
+#ifdef _IS_WINDOWS_
+            TCSBLOCK csBlock;
+            sErr = U14Status1401(hand, U14_DBGGETDATA, &csBlock);
+            if (sErr == U14ERR_NOERROR)
+                *plValue = csBlock.longs[0];    // Return the data
+#endif
+#ifdef LINUX
+            TDBGBLOCK dbb;
+            sErr = CED_DbgGetData(aHand1401[hand], &dbb);
+            if (sErr == U14ERR_NOERROR)
+                *plValue = dbb.iData;                     /* Return the data */
+#endif
+        }
+        else
+            sErr = U14ERR_NOTSET;
+    }
+    return sErr;
+}
+
+/****************************************************************************
+** U14StartSelfTest
+****************************************************************************/
+U14API(short) U14StartSelfTest(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    return U14Control1401(hand, U14_STARTSELFTEST, &csBlock);
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_StartSelfTest(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14CheckSelfTest
+****************************************************************************/
+U14API(short) U14CheckSelfTest(short hand, U14LONG *pData)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    short sErr = U14Status1401(hand, U14_CHECKSELFTEST, &csBlock);
+    if (sErr == U14ERR_NOERROR)
+    {
+        pData[0] = csBlock.longs[0];        /* Return the results to user */
+        pData[1] = csBlock.longs[1];
+        pData[2] = csBlock.longs[2];
+    }
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    if (sErr == U14ERR_NOERROR)                /* Check parameters */
+    {
+        TGET_SELFTEST gst;
+        sErr = CED_CheckSelfTest(aHand1401[hand], &gst);
+        if (sErr == U14ERR_NOERROR)
+        {
+            pData[0] = gst.code;        /* Return the results to user */
+            pData[1] = gst.x;
+            pData[2] = gst.y;
+        }
+    }
+#endif
+    return sErr;
+}
+
+/****************************************************************************
+** U14GetUserMemorySize
+****************************************************************************/
+U14API(short) U14GetUserMemorySize(short hand, DWORD *pMemorySize)
+{
+    // The original 1401 used a different command for getting the size
+    short sErr = U14SendString(hand, (asType1401[hand] == U14TYPE1401) ? "MEMTOP;" : "MEMTOP,?;");
+    *pMemorySize = 0;         /* if we get error then leave size set at 0  */
+    if (sErr == U14ERR_NOERROR)
+    {
+        U14LONG alLimits[4];
+        sErr = U14LongsFrom1401(hand, alLimits, 4);
+        if (sErr > 0)              /* +ve sErr is the number of values read */
+        {
+            sErr = U14ERR_NOERROR;                  /* All OK, flag success */
+            if (asType1401[hand] == U14TYPE1401)    /* result for standard  */
+                *pMemorySize = alLimits[0] - alLimits[1]; /* memtop-membot */
+            else
+                *pMemorySize = alLimits[0];   /* result for plus or u1401  */
+        }
+    }
+    return sErr;
+}
+
+/****************************************************************************
+** U14TypeOf1401
+** Returns the type of the 1401, maybe unknown
+****************************************************************************/
+U14API(short) U14TypeOf1401(short hand)
+{
+    if ((hand < 0) || (hand >= MAX1401))                /* Check parameters */
+        return U14ERR_BADHAND;
+    else
+        return asType1401[hand];
+}
+
+/****************************************************************************
+** U14NameOf1401
+** Returns the type of the 1401 as a string, blank if unknown
+****************************************************************************/
+U14API(short) U14NameOf1401(short hand, char* pBuf, WORD wMax)
+{
+    short sErr = CheckHandle(hand);
+    if (sErr == U14ERR_NOERROR)
+    {
+    char* pName;
+    switch (asType1401[hand])               // Results according to type
+    {
+    case U14TYPE1401:  pName = "Std 1401"; break;
+    case U14TYPEPLUS:  pName = "1401plus"; break;
+    case U14TYPEU1401: pName = "micro1401"; break;
+    case U14TYPEPOWER: pName = "Power1401"; break;
+    case U14TYPEU14012:pName = "Micro1401 mk II"; break;
+    case U14TYPEPOWER2:pName = "Power1401 mk II"; break;
+    case U14TYPEU14013:pName = "Micro1401-3"; break;
+    case U14TYPEPOWER3:pName = "Power1401-3"; break;
+    default:           pName = "Unknown";
+    }
+        strncpy(pBuf, pName, wMax);
+    }
+    return sErr;
+}
+
+/****************************************************************************
+** U14TransferFlags
+**  Returns the driver block transfer flags.
+**  Bits can be set - see U14TF_ constants in use1401.h
+*****************************************************************************/
+U14API(short) U14TransferFlags(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    short sErr = U14Status1401(hand, U14_TRANSFERFLAGS, &csBlock);
+    return (sErr == U14ERR_NOERROR) ? (short)csBlock.ints[0] : sErr;
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_TransferFlags(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** GetDriverVersion
+** Actually reads driver version from the device driver.
+** Hi word is major revision, low word is minor revision.
+** Assumes that hand has been checked. Also codes driver type in bits 24 up.
+*****************************************************************************/
+static int GetDriverVersion(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    int iErr = U14Status1401(hand, U14_GETDRIVERREVISION, &csBlock);
+    if (iErr == U14ERR_NOERROR)
+        iErr = csBlock.longs[0];
+    return iErr;
+#endif
+#ifdef LINUX
+    return CED_GetDriverRevision(aHand1401[hand]);
+#endif
+}
+
+/****************************************************************************
+** U14MonitorRev
+** Returns the 1401 monitor revision number.
+** The number returned is the minor revision - the part after the
+** decimal point - plus the major revision times 1000.
+*****************************************************************************/
+U14API(int) U14MonitorRev(short hand)
+{
+    int iRev = 0;
+    int iErr = CheckHandle(hand);
+    if (iErr != U14ERR_NOERROR)                 // Check open and in use
+        return iErr;
+
+    if (asType1401[hand] >= U14TYPEPOWER2)      // The Power2 onwards can give us the monitor
+    {                                           //  revision directly for all versions
+        iErr = U14SendString(hand, "INFO,S,28;");
+        if (iErr == U14ERR_NOERROR)
+        {
+            U14LONG lVals[2];                   // Read a single number being the revision
+            iErr = U14LongsFrom1401(hand, lVals, 1);
+            if (iErr > 0)
+            {
+                iErr = U14ERR_NOERROR;
+                iRev = lVals[0];                // This is the minor part of the revision
+                iRev += asType1401[hand] * 10000;
+            }
+        }
+    }
+    else
+    {                                           /* Do it the hard way for older hardware */
+        iErr = U14SendString(hand, ";CLIST;");     /* ask for command levels */
+        if (iErr == U14ERR_NOERROR)
+        {     
+            while (iErr == U14ERR_NOERROR)
+            {
+                char wstr[50];
+                iErr = U14GetString(hand, wstr, 45);
+                if (iErr == U14ERR_NOERROR)
+                {
+                    char *pstr = strstr(wstr,"RESET");  /* Is this the RESET command? */
+                    if ((pstr == wstr) && (wstr[5] == ' '))
+                    {
+                        char *pstr2;
+                        size_t l;
+                        pstr += 6;       /* Move past RESET and followinmg char */
+                        l = strlen(pstr);       /* The length of text remaining */
+                        while (((pstr[l-1] == ' ') || (pstr[l-1] == 13)) && (l > 0))
+                        {
+                            pstr[l-1] = 0;         /* Tidy up string at the end */
+                            l--;                  /* by removing spaces and CRs */
+                        }
+                        pstr2 = strchr(pstr, '.');    /* Find the decimal point */
+                        if (pstr2 != NULL)                /* If we found the DP */
+                        {
+                            *pstr2 = 0;                /* End pstr string at DP */
+                            pstr2++;              /* Now past the decimal point */
+                            iRev = atoi(pstr2);   /* Get the number after point */
+                        }
+                        iRev += (atoi(pstr) * 1000);    /* Add first bit * 1000 */
+                    }
+                    if ((strlen(wstr) < 3) && (wstr[0] == ' '))
+                        break;              /* Spot the last line of results */
+                }
+            }
+        }
+    }
+    if (iErr == U14ERR_NOERROR)            /* Return revision if no error */
+        iErr = iRev;
+
+    return iErr;
+}
+
+/****************************************************************************
+** U14TryToOpen     Tries to open the 1401 number passed
+**  Note : This will succeed with NT driver even if no I/F card or
+**         1401 switched off, so we check state and close the driver
+**         if the state is unsatisfactory in U14Open1401.
+****************************************************************************/
+#ifdef _IS_WINDOWS_
+#define U14NAMEOLD "\\\\.\\CED_140%d"
+#define U14NAMENEW "\\\\.\\CED%d"
+static short U14TryToOpen(int n1401, long* plRetVal, short* psHandle)
+{
+    short sErr = U14ERR_NOERROR;
+    HANDLE hDevice = INVALID_HANDLE_VALUE;
+    DWORD dwErr = 0;
+    int nFirst, nLast, nDev = 0;        /* Used for the search for a 1401 */
+    BOOL bOldName = FALSE;               /* start by looking for a modern driver */
+
+    if (n1401 == 0)                             /* If we need to look for a 1401 */
+    {
+        nFirst = 1;                             /* Set the search range */
+        nLast = MAX1401;                        /* through all the possible 1401s */
+    }
+    else
+        nFirst = nLast = n1401;                 /* Otherwise just one 1401 */
+
+    while (hDevice == INVALID_HANDLE_VALUE)     /* Loop to try for a 1401 */
+    {
+        for (nDev = nFirst; nDev <= nLast; nDev++)
+        {
+            char szDevName[40];                 /* name of the device to open */
+            sprintf(szDevName, bOldName ? U14NAMEOLD : U14NAMENEW, nDev);
+            hDevice = CreateFile(szDevName, GENERIC_WRITE | GENERIC_READ,
+                                 0, 0,          /* Unshared mode does nothing as this is a device */
+                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+            if (hDevice != INVALID_HANDLE_VALUE)/* Check 1401 if opened */
+            {
+                TCSBLOCK csBlock;
+                assert(aHand1401[nDev-1] == INVALID_HANDLE_VALUE);  // assert if already open
+                aHand1401[nDev-1] = hDevice;    /* Save handle for now */
+
+#ifndef _WIN64
+                // Use DIOC method if not windows 9x or if using new device name
+                abUseNTDIOC[nDev-1] = (BOOL)(!bWindows9x || !bOldName);
+#endif
+                sErr = U14Status1401((short)(nDev-1), U14_TYPEOF1401, &csBlock);
+                if (sErr == U14ERR_NOERROR)
+                {
+                    *plRetVal = csBlock.ints[0];
+                    if (csBlock.ints[0] == U14ERR_INUSE)/* Prevent multi opens */
+                    {
+                        CloseHandle(hDevice);   /* treat as open failure */
+                        hDevice = INVALID_HANDLE_VALUE;
+                        aHand1401[nDev-1] = INVALID_HANDLE_VALUE;
+                        sErr = U14ERR_INUSE;
+                    }
+                    else
+                        break;                  /* Exit from for loop on success */
+                }
+                else
+                {
+                    CloseHandle(hDevice);       /* Give up if func fails */
+                    hDevice = INVALID_HANDLE_VALUE;
+                    aHand1401[nDev-1] = INVALID_HANDLE_VALUE;
+                }
+            }
+            else
+            {
+                DWORD dwe = GetLastError();     /* Get error code otherwise */
+                if ((dwe != ERROR_FILE_NOT_FOUND) || (dwErr == 0))
+                    dwErr = dwe;                /* Ignore repeats of 'not found' */
+            }
+        }
+
+        if ((hDevice == INVALID_HANDLE_VALUE) &&/* No device found, and... */
+            (bWindows9x) &&                     /* ...old names are allowed, and... */
+            (bOldName == FALSE))                /* ...not tried old names yet */
+            bOldName = TRUE;                    /* Set flag and go round again */
+        else
+            break;                              /* otherwise that's all folks */
+    }
+
+    if (hDevice != INVALID_HANDLE_VALUE)        /* If we got our device open */
+        *psHandle = (short)(nDev-1);            /* return 1401 number opened */
+    else
+    {
+        if (dwErr == ERROR_FILE_NOT_FOUND)      /* Sort out the error codes */
+            sErr = U14ERR_NO1401DRIV;           /* if file not found */
+        else if (dwErr == ERROR_NOT_SUPPORTED)
+            sErr = U14ERR_DRIVTOOOLD;           /* if DIOC not supported */
+        else if (dwErr == ERROR_ACCESS_DENIED)
+            sErr = U14ERR_INUSE;
+        else
+            sErr = U14ERR_DRIVCOMMS;            /* otherwise assume comms problem */
+    }
+    return sErr;
+}
+#endif
+#ifdef LINUX
+static short U14TryToOpen(int n1401, long* plRetVal, short* psHandle)
+{
+    short sErr = U14ERR_NOERROR;
+    int fh = 0;                             // will be 1401 handle
+    int iErr = 0;
+    int nFirst, nLast, nDev = 0;            // Used for the search for a 1401
+
+    if (n1401 == 0)                         // If we need to look for a 1401
+    {
+        nFirst = 1;                             /* Set the search range */
+        nLast = MAX1401;                        /* through all the possible 1401s */
+    }
+    else
+        nFirst = nLast = n1401;                 /* Otherwise just one 1401 */
+
+    for (nDev = nFirst; nDev <= nLast; nDev++)
+    {
+        char szDevName[40];                 // name of the device to open
+        sprintf(szDevName,"/dev/cedusb/%d", nDev-1);
+        fh = open(szDevName, O_RDWR);       // can only be opened once at a time
+        if (fh > 0)                         // Check 1401 if opened
+        {
+            int iType1401 = CED_TypeOf1401(fh); // get 1401 type
+            aHand1401[nDev-1] = fh;         // Save handle for now
+            if (iType1401 >= 0)
+            {
+                *plRetVal = iType1401;
+                 break;                     // Exit from for loop on success
+            }
+            else
+            {
+                close(fh);                  // Give up if func fails
+                fh = 0;
+                aHand1401[nDev-1] = 0;
+            }
+        }
+        else
+        {
+            if (((errno != ENODEV) && (errno != ENOENT)) || (iErr == 0))
+                iErr = errno;                // Ignore repeats of 'not found'
+        }
+    }
+
+
+    if (fh)                                 // If we got our device open
+        *psHandle = (short)(nDev-1);        // return 1401 number opened
+    else
+    {
+        if ((iErr == ENODEV) || (iErr == ENOENT)) // Sort out the error codes
+            sErr = U14ERR_NO1401DRIV;       // if file not found
+        else if (iErr == EBUSY)
+            sErr = U14ERR_INUSE;
+        else
+            sErr = U14ERR_DRIVCOMMS;        // otherwise assume comms problem
+    }
+
+    return sErr;
+}
+#endif
+/****************************************************************************
+** U14Open1401
+** Tries to get the 1401 for use by this application
+*****************************************************************************/
+U14API(short) U14Open1401(short n1401)
+{
+    long     lRetVal = -1;
+    short    sErr;
+    short    hand = 0;
+    
+    if ((n1401 < 0) || (n1401 > MAX1401))       // must check the 1401 number
+        return U14ERR_BAD1401NUM;
+
+    szLastName[0] = 0;          /* initialise the error info string */
+
+    sErr = U14TryToOpen(n1401, &lRetVal, &hand);
+    if (sErr == U14ERR_NOERROR)
+    {
+        long lDriverVersion = GetDriverVersion(hand);   /* get driver revision */
+        long lDriverRev = -1;
+		if (lDriverVersion >= 0)                    /* can use it if all OK */
+        {
+            lLastDriverType = (lDriverVersion >> 24) & 0x000000FF;
+            asDriverType[hand] = (short)lLastDriverType;    /* Drv type */
+            lLastDriverVersion = lDriverVersion & 0x00FFFFFF;
+            alDriverVersion[hand] = lLastDriverVersion;     /* Actual version */
+            lDriverRev = ((lDriverVersion>>16) & 0x00FF);    /* use hi word */
+        }
+        else
+        {
+            U14Close1401(hand);    /* If there is a problem we should close */
+            return (short)lDriverVersion;      /* and return the error code */
+        }
+    
+        if (lDriverRev < MINDRIVERMAJREV)       /* late enough version?     */
+        {
+            U14Close1401(hand);    /* If there is a problem we should close */
+            return U14ERR_DRIVTOOOLD;           /* too old                  */
+        }
+    
+        asLastRetCode[hand] = U14ERR_NOERROR; /* Initialise this 1401s info */
+        abGrabbed[hand] = FALSE;          /* we are not in single step mode */
+        U14SetTimeout(hand, 3000);      /* set 3 seconds as default timeout */
+
+        switch (lRetVal)
+        {
+        case DRIVRET_STD:  asType1401[hand] = U14TYPE1401; break;      /* Some we do by hand */
+        case DRIVRET_U1401:asType1401[hand] = U14TYPEU1401; break;
+        case DRIVRET_PLUS: asType1401[hand] = U14TYPEPLUS; break;
+        default:  // For the power upwards, we can calculate the codes
+                if ((lRetVal >= DRIVRET_POWER) && (lRetVal <= DRIVRET_MAX))
+                    asType1401[hand] = (short)(lRetVal - (DRIVRET_POWER - U14TYPEPOWER));
+                else
+                    asType1401[hand] = U14TYPEUNKNOWN;
+                break;
+            }
+        U14KillIO1401(hand);                     /* resets the 1401 buffers */
+
+        if (asType1401[hand] != U14TYPEUNKNOWN)   /* If all seems OK so far */
+        {
+            sErr = U14CheckErr(hand);        /* we can check 1401 comms now */
+            if (sErr != 0)                       /* If this failed to go OK */
+                U14Reset1401(hand); /* Reset the 1401 to try to sort it out */
+        }
+
+        sErr = U14StateOf1401(hand);/* Get the state of the 1401 for return */
+        if (sErr == U14ERR_NOERROR)
+            sErr = hand;                 /* return the handle if no problem */
+        else
+            U14Close1401(hand);    /* If there is a problem we should close */
+    }
+
+    return sErr;
+}
+
+
+/****************************************************************************
+** U14Close1401
+** Closes the 1401 so someone else can use it.
+****************************************************************************/
+U14API(short) U14Close1401(short hand)
+{
+    int j;
+    int iAreaMask = 0;                          // Mask for active areas
+    short sErr = CheckHandle(hand);
+    if (sErr != U14ERR_NOERROR)                 // Check open and in use
+        return sErr;
+
+    for (j = 0; j<MAX_TRANSAREAS; ++j)
+    {
+        TGET_TX_BLOCK gtb;
+        int iReturn = U14GetTransfer(hand, &gtb);   // get area information
+        if (iReturn == U14ERR_NOERROR)          // ignore if any problem
+            if (gtb.used)
+                iAreaMask |= (1 << j);          // set a bit for each used area
+    }
+
+    if (iAreaMask)                              // if any areas are in use
+    {
+        U14Reset1401(hand);                     // in case an active transfer running
+        for (j = 0; j < MAX_TRANSAREAS; ++j)    // Locate locked areas
+            if (iAreaMask & (1 << j))           // And kill off any transfers
+                U14UnSetTransfer(hand, (WORD)j);
+    }
+
+#ifdef _IS_WINDOWS_
+    if (aXferEvent[hand])                       // if this 1401 has an open event handle
+    {
+        CloseHandle(aXferEvent[hand]);          // close down the handle
+        aXferEvent[hand] = NULL;                // and mark it as gone
+    }
+
+    if (CloseHandle(aHand1401[hand]))
+#endif
+#ifdef LINUX
+    if (close(aHand1401[hand]) == 0)            // make sure that close works
+#endif
+    {
+        aHand1401[hand] = INVALID_HANDLE_VALUE;
+        asType1401[hand] = U14TYPEUNKNOWN;
+        return U14ERR_NOERROR;
+    }
+    else
+        return U14ERR_BADHAND;     /* BUGBUG GetLastError() ? */
+}
+
+/**************************************************************************
+**
+** Look for open 1401s and attempt to close them down. 32-bit windows only.
+**************************************************************************/
+U14API(void) U14CloseAll(void)
+{
+    int i;
+    for (i = 0; i < MAX1401; i++)       // Tidy up and make safe
+        if (aHand1401[i] != INVALID_HANDLE_VALUE)
+            U14Close1401((short)i);     // Last ditch close 1401
+}
+
+/****************************************************************************
+** U14Reset1401
+** Resets the 1401
+****************************************************************************/
+U14API(short) U14Reset1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    return U14Control1401(hand, U14_RESET1401, &csBlock);
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_Reset1401(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14ForceReset
+**    Sets the 1401 full reset flag, so that next call to Reset1401 will
+**     always cause a genuine reset.
+*****************************************************************************/
+U14API(short) U14ForceReset(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    return U14Control1401(hand, U14_FULLRESET, &csBlock);
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_FullReset(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14KillIO1401
+**    Removes any pending IO from the buffers.
+*****************************************************************************/
+U14API(short) U14KillIO1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    return U14Control1401(hand, U14_KILLIO1401, &csBlock);
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_KillIO1401(aHand1401[hand]) : sErr;
+#endif
+}
+
+
+/****************************************************************************
+** U14SendString
+** Send characters to the 1401
+*****************************************************************************/
+U14API(short) U14SendString(short hand, const char* pString)
+{
+    int nChars;                     // length we are sending
+    long lTimeOutTicks;             // when to time out
+    BOOL bSpaceToSend;              // space to send yet
+    short sErr = CheckHandle(hand);
+    if (sErr != U14ERR_NOERROR)
+        return sErr;
+
+    nChars = (int)strlen(pString);  // get string length we want to send
+    if (nChars > MAXSTRLEN)
+        return U14ERR_STRLEN;       // String too long
+
+#ifdef _IS_WINDOWS_
+    // To get here we must wait for the buffer to have some space
+    lTimeOutTicks = U14WhenToTimeOut(hand);
+    do
+    {
+        bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
+    }
+    while (!bSpaceToSend && !U14PassedTime(lTimeOutTicks));
+
+    if (!bSpaceToSend)             /* Last-ditch attempt to avoid timeout */
+    {           /* This can happen with anti-virus or network activity! */
+        int i;
+        for (i = 0; (i < 4) && (!bSpaceToSend); ++i)
+        {
+            Sleep(25);       /* Give other threads a chance for a while */
+            bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
+        }
+    }
+
+    if (asLastRetCode[hand] == U14ERR_NOERROR)      /* no errors? */
+    {
+        if (bSpaceToSend)
+        {
+            PARAMBLK    rData;
+            DWORD       dwBytes;
+            char        tstr[MAXSTRLEN+5];          /* Buffer for chars */
+
+            if ((hand < 0) || (hand >= MAX1401))
+                sErr = U14ERR_BADHAND;
+            else
+            {
+                strcpy(tstr, pString);              /* Into local buf */
+#ifndef _WIN64
+                if (!USE_NT_DIOC(hand))             /* Using WIN 95 driver access? */
+                {
+                    int iOK = DeviceIoControl(aHand1401[hand], (DWORD)U14_SENDSTRING,
+                                    NULL, 0, tstr, nChars,
+                                    &dwBytes, NULL);
+                    if (iOK)
+                        sErr = (dwBytes >= (DWORD)nChars) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS;
+                    else
+                        sErr = (short)GetLastError();
+                }
+                else
+#endif
+                {
+                    int iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_SENDSTRING,
+                                    tstr, nChars,
+                                    &rData,sizeof(PARAMBLK),&dwBytes,NULL);
+                    if (iOK && (dwBytes >= sizeof(PARAMBLK)))
+                        sErr = rData.sState;
+                    else
+                        sErr = U14ERR_DRIVCOMMS;
+                }
+
+                if (sErr != U14ERR_NOERROR) // If we have had a comms error
+                    U14ForceReset(hand);    //  make sure we get real reset
+            }
+
+            return sErr;
+
+        }
+        else
+        {
+            U14ForceReset(hand);                //  make sure we get real reset
+            return U14ERR_TIMEOUT;
+        }
+    }
+    else
+        return asLastRetCode[hand];
+#endif
+#ifdef LINUX
+    // Just try to send it and see what happens!
+    sErr = CED_SendString(aHand1401[hand], pString, nChars);
+    if (sErr != U14ERR_NOOUT)       // if any result except "no room in output"...
+    {
+        if (sErr != U14ERR_NOERROR) // if a problem...
+             U14ForceReset(hand);   // ...make sure we get real reset next time
+        return sErr;                // ... we are done as nothing we can do
+    }
+
+    // To get here we must wait for the buffer to have some space
+    lTimeOutTicks = U14WhenToTimeOut(hand);
+    do
+    {
+        bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
+        if (!bSpaceToSend)
+            sched_yield();          // let others have fun while we wait
+    }
+    while (!bSpaceToSend && !U14PassedTime(lTimeOutTicks));
+
+    if (asLastRetCode[hand] == U14ERR_NOERROR)                /* no errors? */
+    {
+        if (bSpaceToSend)
+        {
+            sErr = CED_SendString(aHand1401[hand], pString, nChars);
+            if (sErr != U14ERR_NOERROR) // If we have had a comms error
+                U14ForceReset(hand);    //  make sure we get real reset
+            return sErr;
+        }
+        else
+        {
+            U14ForceReset(hand);                //  make sure we get real reset
+            return U14ERR_TIMEOUT;
+        }
+    }
+    else
+        return asLastRetCode[hand];
+#endif
+}
+
+/****************************************************************************
+** U14SendChar
+** Send character to the 1401
+*****************************************************************************/
+U14API(short) U14SendChar(short hand, char cChar)
+{
+#ifdef _IS_WINDOWS_
+    char sz[2]=" ";                         // convert to a string and send
+    sz[0] = cChar;
+    sz[1] = 0;
+    return(U14SendString(hand, sz));        // String routines are better
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_SendChar(aHand1401[hand], cChar) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14GetString
+** Get a string from the 1401. Returns a null terminated string.
+** The string is all the characters up to the next CR in the buffer
+** or the end of the buffer if that comes first. This only returns text
+** if there is a CR in the buffer. The terminating CR character is removed.
+** wMaxLen  Is the size of the buffer and must be at least 2 or an error.
+** Returns  U14ERR_NOERR if OK with the result in the string or a negative
+**          error code. Any error from the device causes us to set up for
+**          a full reset.
+****************************************************************************/
+U14API(short) U14GetString(short hand, char* pBuffer, WORD wMaxLen)
+{
+    short sErr = CheckHandle(hand);
+    if (sErr != U14ERR_NOERROR)             // If an error...
+        return sErr;                        // ...bail out!
+
+#ifdef _IS_WINDOWS_
+    if (wMaxLen>1)                          // we need space for terminating 0
+    {
+        BOOL bLineToGet;                    // true when a line to get
+        long lTimeOutTicks = U14WhenToTimeOut(hand);
+        do
+            bLineToGet = (BOOL)(U14LineCount(hand) != 0);
+        while (!bLineToGet && !U14PassedTime(lTimeOutTicks));
+
+        if (!bLineToGet)             /* Last-ditch attempt to avoid timeout */
+        {           /* This can happen with anti-virus or network activity! */
+            int i;
+            for (i = 0; (i < 4) && (!bLineToGet); ++i)
+            {
+                Sleep(25);       /* Give other threads a chance for a while */
+                bLineToGet = (BOOL)(U14LineCount(hand) != 0);
+            }
+        }
+
+        if (bLineToGet)
+        {
+            if (asLastRetCode[hand] == U14ERR_NOERROR)     /* all ok so far */
+            {
+                DWORD       dwBytes = 0;
+                *((WORD *)pBuffer) = wMaxLen;       /* set up length */
+#ifndef _WIN64
+                if (!USE_NT_DIOC(hand))             /* Win 95 DIOC here ? */
+                {
+                    char tstr[MAXSTRLEN+5];         /* Buffer for Win95 chars */
+                    int iOK;
+
+                    if (wMaxLen > MAXSTRLEN)        /* Truncate length */
+                        wMaxLen = MAXSTRLEN;    
+
+                    *((WORD *)tstr) = wMaxLen;      /* set len */
+
+                    iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_GETSTRING,
+                                    NULL, 0, tstr, wMaxLen+sizeof(short),
+                                    &dwBytes, NULL);
+                    if (iOK)                        /* Device IO control OK ? */
+                    {
+                        if (dwBytes >= 0)           /* If driver OK */
+                        {
+                            strcpy(pBuffer, tstr);
+                            sErr = U14ERR_NOERROR;
+                        }
+                        else
+                            sErr = U14ERR_DRIVCOMMS;
+                    }
+                    else
+                    {
+                        sErr = (short)GetLastError();
+                        if (sErr > 0)               /* Errors are -ve */
+                            sErr = (short)-sErr;
+                    }
+                }
+                else
+#endif
+                {       /* Here for NT, the DLL must own the buffer */
+                    HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE,wMaxLen+sizeof(short));
+                    if (hMem)
+                    {
+                        char* pMem = (char*)GlobalLock(hMem);
+                        if (pMem)
+                        {
+                            int iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_GETSTRING,
+                                            NULL, 0, pMem, wMaxLen+sizeof(short),
+                                            &dwBytes, NULL);
+                            if (iOK)                /* Device IO control OK ? */
+                            {
+                                if (dwBytes >= wMaxLen)
+                                {
+                                    strcpy(pBuffer, pMem+sizeof(short));
+                                    sErr = *((SHORT*)pMem);
+                                }
+                                else
+                                    sErr = U14ERR_DRIVCOMMS;
+                            }
+                            else
+                                sErr = U14ERR_DRIVCOMMS;
+
+                            GlobalUnlock(hMem);
+                        }
+                        else
+                            sErr = U14ERR_OUTOFMEMORY;
+
+                        GlobalFree(hMem);
+                    }
+                    else
+                        sErr = U14ERR_OUTOFMEMORY;
+                }
+
+                if (sErr == U14ERR_NOERROR)     // If all OK...
+                    TranslateString(pBuffer);   // ...convert any commas to spaces
+                else                            // If we have had a comms error...
+                    U14ForceReset(hand);        // ...make sure we get real reset
+
+            }
+            else
+                sErr = asLastRetCode[hand];
+        }
+        else
+        {
+            sErr = U14ERR_TIMEOUT;
+            U14ForceReset(hand);            //  make sure we get real reset
+        }
+    }
+    else
+        sErr = U14ERR_BUFF_SMALL;
+    return sErr;
+#endif
+#ifdef LINUX
+    if (wMaxLen>1)                          // we need space for terminating 0
+    {
+        BOOL bLineToGet;                    // true when a line to get
+        long lTimeOutTicks = U14WhenToTimeOut(hand);
+        do
+        {
+            bLineToGet = (BOOL)(U14LineCount(hand) != 0);
+            if (!bLineToGet)
+                sched_yield();
+
+        }
+        while (!bLineToGet && !U14PassedTime(lTimeOutTicks));
+
+        if (bLineToGet)
+        {
+            sErr = CED_GetString(aHand1401[hand], pBuffer, wMaxLen-1);   // space for terminator
+            if (sErr >=0)                    // if we were OK...
+            {
+                if (sErr >= wMaxLen)         // this should NOT happen unless
+                    sErr = U14ERR_DRIVCOMMS; // ...driver Comms are very bad
+                else
+                {
+                    pBuffer[sErr] = 0;      // OK, so terminate the string...
+                    TranslateString(pBuffer);  // ...and convert commas to spaces.
+                }
+            }
+
+            if (sErr < U14ERR_NOERROR)       // If we have had a comms error
+                U14ForceReset(hand);            //  make sure we get real reset
+        }
+        else
+        {
+            sErr = U14ERR_TIMEOUT;
+            U14ForceReset(hand);            //  make sure we get real reset
+        }
+    }
+    else
+        sErr = U14ERR_BUFF_SMALL;
+
+    return sErr >= U14ERR_NOERROR ? U14ERR_NOERROR : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14GetChar
+** Get a character from the 1401. CR returned as CR.
+*****************************************************************************/
+U14API(short) U14GetChar(short hand, char* pcChar)
+{
+#ifdef _IS_WINDOWS_
+    char sz[2];                             // read a very short string
+    short sErr = U14GetString(hand, sz, 2); // read one char and nul terminate it
+    *pcChar = sz[0];    // copy to result, NB char translate done by GetString
+    if (sErr == U14ERR_NOERROR)
+    {                                       // undo translate of CR to zero
+        if (*pcChar == '\0')                // by converting back
+            *pcChar = '\n';                 // What a nasty thing to have to do
+    }
+    return sErr;
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    if (sErr != U14ERR_NOERROR)             // Check parameters
+        return sErr;
+    sErr = CED_GetChar(aHand1401[hand]);    // get one char, if available
+    if (sErr >= 0)
+    {
+        *pcChar = (char)sErr;              // return if it we have one
+        return U14ERR_NOERROR;              // say all OK
+    }
+    else
+        return sErr;
+#endif
+}
+
+/****************************************************************************
+** U14Stat1401
+** Returns 0 for no lines or error or non zero for something waiting
+****************************************************************************/
+U14API(short) U14Stat1401(short hand)
+{
+    return ((short)(U14LineCount(hand) > 0));
+}
+
+/****************************************************************************
+** U14CharCount
+** Returns the number of characters in the input buffer
+*****************************************************************************/
+U14API(short) U14CharCount(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    short sErr = U14Status1401(hand, U14_STAT1401, &csBlock);
+    if (sErr == U14ERR_NOERROR)
+        sErr = csBlock.ints[0];
+    return sErr;
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_Stat1401(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14LineCount
+** Returns the number of CR characters in the input buffer
+*****************************************************************************/
+U14API(short) U14LineCount(short hand)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    short sErr = U14Status1401(hand, U14_LINECOUNT, &csBlock);
+    if (sErr == U14ERR_NOERROR)
+        sErr = csBlock.ints[0];
+    return sErr;
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_LineCount(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14GetErrorString
+** Converts error code supplied to a decent descriptive string.
+** NOTE: This function may use some extra information stored
+**       internally in the DLL. This information is stored on a
+**       per-process basis, but it might be altered if you call
+**       other functions after getting an error and before using
+**       this function.
+****************************************************************************/
+U14API(void)  U14GetErrorString(short nErr, char* pStr, WORD wMax)
+{
+    char    wstr[150];
+
+    switch (nErr)              /* Basically, we do this with a switch block */
+    {
+    case U14ERR_OFF:
+        sprintf(wstr, "The 1401 is apparently switched off (code %d)", nErr);
+        break;
+
+    case U14ERR_NC:
+        sprintf(wstr, "The 1401 is not connected to the interface card (code %d)", nErr);
+        break;
+
+    case U14ERR_ILL:
+        sprintf(wstr, "The 1401 is not working correctly (code %d)", nErr);
+        break;
+
+    case U14ERR_NOIF:
+        sprintf(wstr, "The 1401 interface card was not detected (code %d)", nErr);
+        break;
+
+    case U14ERR_TIME:
+        sprintf(wstr, "The 1401 fails to become ready for use (code %d)", nErr);
+        break;
+
+    case U14ERR_BADSW:
+        sprintf(wstr, "The 1401 interface card jumpers are incorrect (code %d)", nErr);
+        break;
+
+    case U14ERR_NOINT:
+        sprintf(wstr, "The 1401 interrupt is not available for use (code %d)", nErr);
+        break;
+
+    case U14ERR_INUSE:
+        sprintf(wstr, "The 1401 is already in use by another program (code %d)", nErr);
+        break;
+
+    case U14ERR_NODMA:
+        sprintf(wstr, "The 1401 DMA channel is not available for use (code %d)", nErr);
+        break;
+
+    case U14ERR_BADHAND:
+        sprintf(wstr, "The application supplied an incorrect 1401 handle (code %d)", nErr);
+        break;
+
+    case U14ERR_BAD1401NUM:
+        sprintf(wstr, "The application used an incorrect 1401 number (code %d)", nErr);
+        break;
+
+    case U14ERR_NO_SUCH_FN:
+        sprintf(wstr, "The code passed to the 1401 driver is invalid (code %d)", nErr);
+        break;
+
+    case U14ERR_NO_SUCH_SUBFN:
+        sprintf(wstr, "The sub-code passed to the 1401 driver is invalid (code %d)", nErr);
+        break;
+
+    case U14ERR_NOOUT:
+        sprintf(wstr, "No room in buffer for characters for the 1401 (code %d)", nErr);
+        break;
+
+    case U14ERR_NOIN:
+        sprintf(wstr, "No characters from the 1401 are available (code %d)", nErr);
+        break;
+
+    case U14ERR_STRLEN:
+        sprintf(wstr, "A string sent to or read from the 1401 was too long (code %d)", nErr);
+        break;
+
+    case U14ERR_LOCKFAIL:
+        sprintf(wstr, "Failed to lock host memory for data transfer (code %d)", nErr);
+        break;
+
+    case U14ERR_UNLOCKFAIL:
+        sprintf(wstr, "Failed to unlock host memory after data transfer (code %d)", nErr);
+        break;
+
+    case U14ERR_ALREADYSET:
+        sprintf(wstr, "The transfer area used is already set up (code %d)", nErr);
+        break;
+
+    case U14ERR_NOTSET:
+        sprintf(wstr, "The transfer area used has not been set up (code %d)", nErr);
+        break;
+
+    case U14ERR_BADAREA:
+        sprintf(wstr, "The transfer area number is incorrect (code %d)", nErr);
+        break;
+
+    case U14ERR_NOFILE:
+        sprintf(wstr, "The command file %s could not be opened (code %d)", szLastName, nErr);
+        break;
+
+    case U14ERR_READERR:
+        sprintf(wstr, "The command file %s could not be read (code %d)", szLastName, nErr);
+        break;
+
+    case U14ERR_UNKNOWN:
+        sprintf(wstr, "The %s command resource could not be found (code %d)", szLastName, nErr);
+        break;
+
+    case U14ERR_HOSTSPACE:
+        sprintf(wstr, "Unable to allocate memory for loading command %s (code %d)", szLastName, nErr);
+        break;
+
+    case U14ERR_LOCKERR:
+        sprintf(wstr, "Unable to lock memory for loading command %s (code %d)", szLastName, nErr);
+        break;
+
+    case U14ERR_CLOADERR:
+        sprintf(wstr, "Error in loading command %s, bad command format (code %d)", szLastName, nErr);
+        break;
+
+    case U14ERR_TOXXXERR:
+        sprintf(wstr, "Error detected after data transfer to or from the 1401 (code %d)", nErr);
+        break;
+
+    case U14ERR_NO386ENH:
+        sprintf(wstr, "Windows 3.1 is not running in 386 enhanced mode (code %d)", nErr);
+        break;
+
+    case U14ERR_NO1401DRIV:
+        sprintf(wstr, "The 1401 device driver cannot be found (code %d)\nUSB:   check plugged in and powered\nOther: not installed?", nErr);
+        break;
+
+    case U14ERR_DRIVTOOOLD:
+        sprintf(wstr, "The 1401 device driver is too old for use (code %d)", nErr);
+        break;
+
+    case U14ERR_TIMEOUT:
+        sprintf(wstr, "Character transmissions to the 1401 timed-out (code %d)", nErr);
+        break;
+
+    case U14ERR_BUFF_SMALL:
+        sprintf(wstr, "Buffer for text from the 1401 was too small (code %d)", nErr);
+        break;
+
+    case U14ERR_CBALREADY:
+        sprintf(wstr, "1401 monitor callback already set up (code %d)", nErr);
+        break;
+
+    case U14ERR_BADDEREG:
+        sprintf(wstr, "1401 monitor callback deregister invalid (code %d)", nErr);
+        break;
+
+    case U14ERR_DRIVCOMMS:
+        sprintf(wstr, "1401 device driver communications failed (code %d)", nErr);
+        break;
+
+    case U14ERR_OUTOFMEMORY:
+        sprintf(wstr, "Failed to allocate or lock memory for text from the 1401 (code %d)", nErr);
+        break;
+
+    default:
+        sprintf(wstr, "1401 error code %d returned; this code is unknown", nErr);
+        break;
+
+    }
+    if ((WORD)strlen(wstr) >= wMax-1)  /* Check for string being too long */
+        wstr[wMax-1] = 0;                          /* and truncate it if so */
+    strcpy(pStr, wstr);                       /* Return the error string */
+}
+
+/***************************************************************************
+** U14GetTransfer
+** Get a TGET_TX_BLOCK describing a transfer area (held in the block)
+***************************************************************************/
+U14API(short) U14GetTransfer(short hand, TGET_TX_BLOCK *pTransBlock)
+{
+    short sErr = CheckHandle(hand);
+#ifdef _IS_WINDOWS_
+    if (sErr == U14ERR_NOERROR)
+    { 
+        DWORD dwBytes = 0;
+        BOOL bOK = DeviceIoControl(aHand1401[hand], (DWORD)U14_GETTRANSFER, NULL, 0, pTransBlock,
+                              sizeof(TGET_TX_BLOCK), &dwBytes, NULL);
+    
+        if (bOK && (dwBytes >= sizeof(TGET_TX_BLOCK)))
+            sErr = U14ERR_NOERROR;
+        else
+            sErr = U14ERR_DRIVCOMMS;
+    }
+    return sErr;
+#endif
+#ifdef LINUX
+    return (sErr == U14ERR_NOERROR) ? CED_GetTransfer(aHand1401[hand], pTransBlock) : sErr;
+#endif
+}
+/////////////////////////////////////////////////////////////////////////////
+// U14WorkingSet
+// For Win32 only, adjusts process working set so that minimum is at least
+//  dwMinKb and maximum is at least dwMaxKb.
+// Return value is zero if all went OK, or a code from 1 to 3 indicating the
+//  cause of the failure:
+//
+//     1 unable to access process (insufficient rights?)
+//     2 unable to read process working set
+//     3 unable to set process working set - bad parameters?
+U14API(short) U14WorkingSet(DWORD dwMinKb, DWORD dwMaxKb)
+{
+#ifdef _IS_WINDOWS_
+    short sRetVal = 0;                      // 0 means all is OK
+    HANDLE hProcess;
+    DWORD dwVer = GetVersion();
+	if (dwVer & 0x80000000)                 // is this not NT?
+        return 0;                           // then give up right now
+
+    // Now attempt to get information on working set size
+    hProcess = OpenProcess(STANDARD_RIGHTS_REQUIRED |
+                                  PROCESS_QUERY_INFORMATION |
+                                  PROCESS_SET_QUOTA,
+                                  FALSE, _getpid());
+    if (hProcess)
+    {
+        SIZE_T dwMinSize,dwMaxSize;
+        if (GetProcessWorkingSetSize(hProcess, &dwMinSize, &dwMaxSize))
+        {
+            DWORD dwMin = dwMinKb << 10;    // convert from kb to bytes
+            DWORD dwMax = dwMaxKb << 10;
+
+            // if we get here, we have managed to read the current size
+            if (dwMin > dwMinSize)          // need to change sizes?
+                dwMinSize = dwMin;
+
+            if (dwMax > dwMaxSize)
+                dwMaxSize = dwMax;
+
+            if (!SetProcessWorkingSetSize(hProcess, dwMinSize, dwMaxSize))
+                sRetVal = 3;                // failed to change size
+        }
+        else
+            sRetVal = 2;                    // failed to read original size
+
+        CloseHandle(hProcess);
+    }
+    else
+        sRetVal = 1;            // failed to get handle
+
+    return sRetVal;
+#endif
+#ifdef LINUX
+    if (dwMinKb | dwMaxKb)
+    {
+        // to stop compiler moaning
+    }
+    return U14ERR_NOERROR;
+#endif
+}
+
+/****************************************************************************
+** U14UnSetTransfer  Cancels a transfer area
+** wArea    The index of a block previously used in by SetTransfer
+*****************************************************************************/
+U14API(short) U14UnSetTransfer(short hand, WORD wArea)
+{
+    short sErr = CheckHandle(hand);
+#ifdef _IS_WINDOWS_
+    if (sErr == U14ERR_NOERROR)
+    {
+       TCSBLOCK csBlock;
+       csBlock.ints[0] = (short)wArea;       /* Area number into control block */
+       sErr = U14Control1401(hand, U14_UNSETTRANSFER, &csBlock);  /* Free area */
+   
+       VirtualUnlock(apAreas[hand][wArea], auAreas[hand][wArea]);/* Unlock */
+       apAreas[hand][wArea] = NULL;                         /* Clear locations */
+       auAreas[hand][wArea] = 0;
+    }
+    return sErr;
+#endif
+#ifdef LINUX
+    return (sErr == U14ERR_NOERROR) ? CED_UnsetTransfer(aHand1401[hand], wArea) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14SetTransArea      Sets an area up to be used for transfers
+** WORD  wArea     The area number to set up
+** void *pvBuff    The address of the buffer for the data.
+** DWORD dwLength  The length of the buffer for the data
+** short eSz       The element size (used for byte swapping on the Mac)
+****************************************************************************/
+U14API(short) U14SetTransArea(short hand, WORD wArea, void *pvBuff,
+                                          DWORD dwLength, short eSz)
+{
+    TRANSFERDESC td;
+    short sErr = CheckHandle(hand);
+    if (sErr != U14ERR_NOERROR)
+        return sErr;
+    if (wArea >= MAX_TRANSAREAS)                    // Is this a valid area number
+        return U14ERR_BADAREA;
+
+#ifdef _IS_WINDOWS_
+    assert(apAreas[hand][wArea] == NULL);
+    assert(auAreas[hand][wArea] == 0);
+
+    apAreas[hand][wArea] = pvBuff;                  /* Save data for later */
+    auAreas[hand][wArea] = dwLength;
+
+    if (!VirtualLock(pvBuff, dwLength))             /* Lock using WIN32 calls */
+    {
+        apAreas[hand][wArea] = NULL;                /* Clear locations */
+        auAreas[hand][wArea] = 0;
+        return U14ERR_LOCKERR;                      /* VirtualLock failed */
+    }
+#ifndef _WIN64
+    if (!USE_NT_DIOC(hand))                         /* Use Win 9x DIOC? */
+    {
+        DWORD dwBytes;
+        VXTRANSFERDESC vxDesc;                      /* Structure to pass to VXD */
+        vxDesc.wArea = wArea;                       /* Copy across simple params */
+        vxDesc.dwLength = dwLength;
+
+        // Check we are not asking an old driver for more than area 0
+        if ((wArea != 0) && (U14DriverVersion(hand) < 0x00010002L))
+            sErr = U14ERR_DRIVTOOOLD;
+        else
+        {
+            vxDesc.dwAddrOfs = (DWORD)pvBuff;       /* 32 bit offset */
+            vxDesc.wAddrSel  = 0;
+
+            if (DeviceIoControl(aHand1401[hand], (DWORD)U14_SETTRANSFER,
+                                pvBuff,dwLength,    /* Will translate pointer */
+                                &vxDesc,sizeof(VXTRANSFERDESC),
+                                &dwBytes,NULL))
+            {
+                if (dwBytes >= sizeof(VXTRANSFERDESC)) /* Driver OK ? */
+                    sErr = U14ERR_NOERROR;
+                else
+                    sErr = U14ERR_DRIVCOMMS;        /* Else never got there */
+            }
+            else
+                sErr = (short)GetLastError();
+        }
+    }
+    else
+#endif
+    {
+        PARAMBLK rWork;
+        DWORD dwBytes;
+        td.wArea = wArea;     /* Pure NT - put data into struct */
+        td.lpvBuff = pvBuff;
+        td.dwLength = dwLength;
+        td.eSize = 0;                // Dummy element size
+
+        if (DeviceIoControl(aHand1401[hand],(DWORD)U14_SETTRANSFER,
+                            &td,sizeof(TRANSFERDESC),
+                            &rWork,sizeof(PARAMBLK),&dwBytes,NULL))
+        {
+            if (dwBytes >= sizeof(PARAMBLK))    // maybe error from driver?
+                sErr = rWork.sState;            // will report any error
+            else
+                sErr = U14ERR_DRIVCOMMS;        // Else never got there
+        }
+        else
+            sErr = U14ERR_DRIVCOMMS;
+    }
+
+    if (sErr != U14ERR_NOERROR)
+    {
+        if (sErr != U14ERR_LOCKERR)             // unless lock failed...
+            VirtualUnlock(pvBuff, dwLength);    // ...release the lock
+        apAreas[hand][wArea] = NULL;            // Clear locations
+        auAreas[hand][wArea] = 0;
+    }
+
+    return sErr;
+#endif
+#ifdef LINUX
+    // The strange cast is so that it works in 64 and 32-bit linux as long is 64-bits
+    // in the 64 bit version.
+    td.lpvBuff = (long long)((unsigned long)pvBuff);
+    td.wAreaNum = wArea;
+    td.dwLength = dwLength;
+    td.eSize = eSz;                // Dummy element size
+    return CED_SetTransfer(aHand1401[hand], &td);
+#endif
+}
+
+/****************************************************************************
+** U14SetTransferEvent  Sets an event for notification of application
+** wArea       The tranfer area index, from 0 to MAXAREAS-1
+**    bEvent      True to create an event, false to remove it
+**    bToHost     Set 0 for notification on to1401 tranfers, 1 for
+**                notification of transfers to the host PC
+**    dwStart     The offset of the sub-area of interest
+**    dwLength    The size of the sub-area of interest
+**
+** The device driver will set the event supplied to the signalled state
+** whenever a DMA transfer to/from the specified area is completed. The
+** transfer has to be in the direction specified by bToHost, and overlap
+** that part of the whole transfer area specified by dwStart and dwLength.
+** It is important that this function is called with bEvent false to release
+** the event once 1401 activity is finished.
+**
+** Returns 1 if an event handle exists, 0 if all OK and no event handle or
+** a negative code for an error.
+****************************************************************************/
+U14API(short) U14SetTransferEvent(short hand, WORD wArea, BOOL bEvent,
+                                  BOOL bToHost, DWORD dwStart, DWORD dwLength)
+{
+#ifdef _IS_WINDOWS_
+    TCSBLOCK csBlock;
+    short sErr = U14TransferFlags(hand);        // see if we can handle events
+    if (sErr >= U14ERR_NOERROR)                 // check handle is OK
+    {
+        bEvent = bEvent && ((sErr & U14TF_NOTIFY) != 0); // remove request if we cannot do events
+        if (wArea >= MAX_TRANSAREAS)            // Check a valid area...
+            return U14ERR_BADAREA;              // ...and bail of not
+
+        // We can hold an event for each area, so see if we need to change the
+        // state of the event.
+        if ((bEvent != 0) != (aXferEvent[hand] != 0))    // change of event state?
+        {
+            if (bEvent)                         // want one and none present
+                aXferEvent[hand] = CreateEvent(NULL, FALSE, FALSE, NULL);
+            else
+            {
+                CloseHandle(aXferEvent[hand]);  // clear the existing event
+                aXferEvent[hand] = NULL;        // and clear handle
+            }
+        }
+
+        // We have to store the parameters differently for 64-bit operations
+        //  because a handle is 64 bits long. The drivers know of this and
+        //  handle the information appropriately.
+#ifdef _WIN64
+        csBlock.longs[0] = wArea;               // Pass paramaters into the driver...
+        if (bToHost != 0)                       // The direction flag is held in the
+            csBlock.longs[0] |= 0x10000;        //  upper word of the transfer area value
+        *((HANDLE*)&csBlock.longs[1]) = aXferEvent[hand];  // The event handle is 64-bits
+        csBlock.longs[3] = dwStart;             // Thankfully these two remain
+        csBlock.longs[4] = dwLength;            //  as unsigned 32-bit values
+#else
+        csBlock.longs[0] = wArea;               // pass paramaters into the driver...
+        csBlock.longs[1] = (long)aXferEvent[hand];    // ...especially the event handle
+        csBlock.longs[2] = bToHost;
+        csBlock.longs[3] = dwStart;
+        csBlock.longs[4] = dwLength;
+#endif
+        sErr = U14Control1401(hand, U14_SETTRANSEVENT, &csBlock);
+        if (sErr == U14ERR_NOERROR)
+            sErr = (short)(aXferEvent[hand] != NULL);    // report if we have a flag
+    }
+
+    return sErr;
+#endif
+#ifdef LINUX
+    TRANSFEREVENT te;
+    short sErr = CheckHandle(hand);
+    if (sErr != U14ERR_NOERROR)
+        return sErr;
+
+    if (wArea >= MAX_TRANSAREAS)            // Is this a valid area number
+        return U14ERR_BADAREA;
+
+    te.wAreaNum = wArea;                    // copy parameters to the control block
+    te.wFlags = bToHost ? 1 : 0;            // bit 0 sets the direction
+    te.dwStart = dwStart;                   // start offset of the event area
+    te.dwLength = dwLength;                 // size of the event area
+    te.iSetEvent = bEvent;                  // in Windows, this creates/destroys the event
+    return CED_SetEvent(aHand1401[hand], &te);
+#endif
+}
+
+/****************************************************************************
+** U14TestTransferEvent
+** Would a U14WaitTransferEvent() call return immediately? return 1 if so,
+** 0 if not or a negative code if a problem.
+****************************************************************************/
+U14API(int) U14TestTransferEvent(short hand, WORD wArea)
+{
+#ifdef _IS_WINDOWS_
+    int iErr = CheckHandle(hand);
+    if (iErr == U14ERR_NOERROR)
+    {
+        if (aXferEvent[hand])           // if a handle is set...
+            iErr = WaitForSingleObject(aXferEvent[hand], 0) == WAIT_OBJECT_0;
+    }
+    return iErr;
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_TestEvent(aHand1401[hand], wArea) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14WaitTransferEvent
+** Wait for a transfer event with a timeout.
+** msTimeOut is 0 for an infinite wait, else it is the maximum time to wait
+**           in milliseconds in range 0-0x00ffffff.
+** Returns   If no event handle then return immediately. Else return 1 if
+**           timed out or 0=event, and a negative code if a problem.
+****************************************************************************/
+U14API(int) U14WaitTransferEvent(short hand, WORD wArea, int msTimeOut)
+{
+#ifdef _IS_WINDOWS_
+    int iErr = CheckHandle(hand);
+    if (iErr == U14ERR_NOERROR)
+    {
+        if (aXferEvent[hand])
+        {
+            if (msTimeOut == 0)
+                msTimeOut = INFINITE;
+            iErr = WaitForSingleObject(aXferEvent[hand], msTimeOut) != WAIT_OBJECT_0;
+        }
+        else
+            iErr = TRUE;                // say we timed out if no event
+    }
+    return iErr;
+#endif
+#ifdef LINUX
+    short sErr = CheckHandle(hand);
+    return (sErr == U14ERR_NOERROR) ? CED_WaitEvent(aHand1401[hand], wArea, msTimeOut) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14SetCircular    Sets an area up for circular DMA transfers
+** WORD  wArea          The area number to set up
+** BOOL  bToHost        Sets the direction of data transfer
+** void *pvBuff        The address of the buffer for the data
+** DWORD dwLength       The length of the buffer for the data
+****************************************************************************/
+U14API(short) U14SetCircular(short hand, WORD wArea, BOOL bToHost,
+									void *pvBuff, DWORD dwLength)
+{
+    short sErr = CheckHandle(hand);
+    if (sErr != U14ERR_NOERROR)
+        return sErr;
+
+    if (wArea >= MAX_TRANSAREAS)         /* Is this a valid area number */
+        return U14ERR_BADAREA;
+
+	if (!bToHost)             /* For now, support tohost transfers only */
+        return U14ERR_BADAREA;            /* best error code I can find */
+#ifdef _IS_WINDOWS_
+    assert(apAreas[hand][wArea] == NULL);
+    assert(auAreas[hand][wArea] == 0);
+
+    apAreas[hand][wArea] = pvBuff;              /* Save data for later */
+    auAreas[hand][wArea] = dwLength;
+
+    if (!VirtualLock(pvBuff, dwLength))      /* Lock using WIN32 calls */
+        sErr = U14ERR_LOCKERR;                    /* VirtualLock failed */
+    else
+    {
+        PARAMBLK rWork;
+        DWORD dwBytes;
+        TRANSFERDESC txDesc;
+        txDesc.wArea = wArea;             /* Pure NT - put data into struct */
+        txDesc.lpvBuff = pvBuff;
+        txDesc.dwLength = dwLength;
+        txDesc.eSize = (short)bToHost;       /* Use this for direction flag */
+   
+        if (DeviceIoControl(aHand1401[hand],(DWORD)U14_SETCIRCULAR,
+                           &txDesc, sizeof(TRANSFERDESC),
+                           &rWork, sizeof(PARAMBLK),&dwBytes,NULL))
+        {
+           if (dwBytes >= sizeof(PARAMBLK))          /* error from driver? */
+               sErr = rWork.sState;         /* No, just return driver data */
+           else
+               sErr = U14ERR_DRIVCOMMS;            /* Else never got there */
+        }
+        else
+            sErr = U14ERR_DRIVCOMMS;
+    }
+
+    if (sErr != U14ERR_NOERROR)
+    {
+        if (sErr != U14ERR_LOCKERR)
+            VirtualUnlock(pvBuff, dwLength);         /* Release NT lock */
+        apAreas[hand][wArea] = NULL;                 /* Clear locations */
+        auAreas[hand][wArea] = 0;
+    }
+
+    return sErr;
+#endif
+#ifdef LINUX
+    else
+    {
+        TRANSFERDESC td;
+        td.lpvBuff = (long long)((unsigned long)pvBuff);
+        td.wAreaNum = wArea;
+        td.dwLength = dwLength;
+        td.eSize = (short)bToHost;       /* Use this for direction flag */
+        return CED_SetCircular(aHand1401[hand], &td);
+    }
+#endif
+}
+
+/****************************************************************************
+** Function  GetCircBlk returns the size (& start offset) of the next
+**           available block of circular data.
+****************************************************************************/
+U14API(int) U14GetCircBlk(short hand, WORD wArea, DWORD *pdwOffs)
+{
+    int lErr = CheckHandle(hand);
+    if (lErr != U14ERR_NOERROR)
+        return lErr;
+
+    if (wArea >= MAX_TRANSAREAS)            // Is this a valid area number?
+        return U14ERR_BADAREA;
+    else
+    {
+#ifdef _IS_WINDOWS_
+        PARAMBLK rWork;
+        TCSBLOCK csBlock;
+        DWORD dwBytes;
+        csBlock.longs[0] = wArea;               // Area number into control block
+        rWork.sState = U14ERR_DRIVCOMMS;
+        if (DeviceIoControl(aHand1401[hand], (DWORD)U14_GETCIRCBLK, &csBlock, sizeof(TCSBLOCK), &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
+           (dwBytes >= sizeof(PARAMBLK)))
+            lErr = rWork.sState;
+        else
+            lErr = U14ERR_DRIVCOMMS;
+   
+        if (lErr == U14ERR_NOERROR)             // Did everything go OK?
+        {                                       // Yes, we can pass the results back
+            lErr = rWork.csBlock.longs[1];      // Return the block information
+            *pdwOffs = rWork.csBlock.longs[0];  // Offset is first in array
+        }
+#endif
+#ifdef LINUX
+        TCIRCBLOCK cb;
+        cb.nArea = wArea;                       // Area number into control block
+        cb.dwOffset = 0;
+        cb.dwSize = 0;
+        lErr = CED_GetCircBlock(aHand1401[hand], &cb);
+        if (lErr == U14ERR_NOERROR)             // Did everything go OK?
+        {                                       // Yes, we can pass the results back
+            lErr = cb.dwSize;                   // return the size
+            *pdwOffs = cb.dwOffset;             // and the offset
+        }
+#endif
+    }
+    return lErr;
+}
+
+/****************************************************************************
+** Function  FreeCircBlk marks the specified area of memory as free for
+**           resuse for circular transfers and returns the size (& start
+**           offset) of the next available block of circular data.
+****************************************************************************/
+U14API(int) U14FreeCircBlk(short hand, WORD wArea, DWORD dwOffs, DWORD dwSize,
+                                        DWORD *pdwOffs)
+{
+    int lErr = CheckHandle(hand);
+    if (lErr != U14ERR_NOERROR)
+        return lErr;
+
+    if (wArea < MAX_TRANSAREAS)                 // Is this a valid area number
+    {
+#ifdef _IS_WINDOWS_
+        PARAMBLK rWork;
+        TCSBLOCK csBlock;
+        DWORD dwBytes;
+        csBlock.longs[0] = wArea;               // Area number into control block
+        csBlock.longs[1] = dwOffs;
+        csBlock.longs[2] = dwSize;
+        rWork.sState = U14ERR_DRIVCOMMS;
+        if (DeviceIoControl(aHand1401[hand], (DWORD)U14_FREECIRCBLK, &csBlock, sizeof(TCSBLOCK),
+                           &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
+           (dwBytes >= sizeof(PARAMBLK)))
+           lErr = rWork.sState;
+        else
+           lErr = U14ERR_DRIVCOMMS;
+       if (lErr == U14ERR_NOERROR)             // Did everything work OK?
+       {                                       // Yes, we can pass the results back
+           lErr = rWork.csBlock.longs[1];      // Return the block information
+           *pdwOffs = rWork.csBlock.longs[0];  // Offset is first in array
+       }
+#endif
+#ifdef LINUX
+        TCIRCBLOCK cb;
+        cb.nArea = wArea;                       // Area number into control block
+        cb.dwOffset = dwOffs;
+        cb.dwSize = dwSize;
+    
+        lErr = CED_FreeCircBlock(aHand1401[hand], &cb);
+        if (lErr == U14ERR_NOERROR)             // Did everything work OK?
+        {                                       // Yes, we can pass the results back
+            lErr = cb.dwSize;                   // Return the block information
+            *pdwOffs = cb.dwOffset;             // Offset is first in array
+        }
+#endif
+    }
+    else
+        lErr = U14ERR_BADAREA;
+
+    return lErr;
+}
+
+/****************************************************************************
+** Transfer
+** Transfer moves data to 1401 or to host
+** Assumes memory is allocated and locked,
+** which it should be to get a pointer
+*****************************************************************************/
+static short Transfer(short hand, BOOL bTo1401, char* pData,
+                       DWORD dwSize, DWORD dw1401, short eSz)
+{
+    char strcopy[MAXSTRLEN+1];          // to hold copy of work string
+    short sResult = U14SetTransArea(hand, 0, (void *)pData, dwSize, eSz);
+    if (sResult == U14ERR_NOERROR)      // no error
+    {
+        sprintf(strcopy,                // data offset is always 0
+                "TO%s,$%X,$%X,0;", bTo1401 ? "1401" : "HOST", dw1401, dwSize);
+
+        U14SendString(hand, strcopy);   // send transfer string
+
+        sResult = U14CheckErr(hand);    // Use ERR command to check for done
+        if (sResult > 0)
+            sResult = U14ERR_TOXXXERR;  // If a 1401 error, use this code
+
+        U14UnSetTransfer(hand, 0);
+    }
+    return sResult;
+}
+
+/****************************************************************************
+** Function  ToHost transfers data into the host from the 1401
+****************************************************************************/
+U14API(short) U14ToHost(short hand, char* pAddrHost, DWORD dwSize,
+                                            DWORD dw1401, short eSz)
+{
+    short sErr = CheckHandle(hand);
+    if ((sErr == U14ERR_NOERROR) && dwSize) // TOHOST is a constant
+        sErr = Transfer(hand, TOHOST, pAddrHost, dwSize, dw1401, eSz);
+    return sErr;
+}
+
+/****************************************************************************
+** Function  To1401 transfers data into the 1401 from the host
+****************************************************************************/
+U14API(short) U14To1401(short hand, const char* pAddrHost,DWORD dwSize,
+                                    DWORD dw1401, short eSz)
+{
+    short sErr = CheckHandle(hand);
+    if ((sErr == U14ERR_NOERROR) && dwSize) // TO1401 is a constant
+        sErr = Transfer(hand, TO1401, (char*)pAddrHost, dwSize, dw1401, eSz);
+    return sErr;
+}
+
+/****************************************************************************
+** Function  LdCmd    Loads a command from a full path or just a file
+*****************************************************************************/
+#ifdef _IS_WINDOWS_
+#define file_exist(name) (_access(name, 0) != -1)
+#define file_open(name) _lopen(name, OF_READ)
+#define file_close(h)   _lclose(h)
+#define file_seek(h, pos) _llseek(h, pos, FILE_BEGIN) 
+#define file_read(h, buffer, size) (_lread(h, buffer, size) == size)
+#endif
+#ifdef LINUX
+#define file_exist(name) (access(name, F_OK) != -1)
+#define file_open(name) open(name, O_RDONLY)
+#define file_close(h)   close(h)
+#define file_seek(h, pos) lseek(h, pos, SEEK_SET) 
+#define file_read(h, buffer, size) (read(h, buffer, size) == (ssize_t)size)
+static DWORD GetModuleFileName(void* dummy, char* buffer, int max)
+{
+    // The following works for Linux systems with a /proc file system.
+    char szProcPath[32];
+    sprintf(szProcPath, "/proc/%d/exe", getpid());  // attempt to read link
+    if (readlink(szProcPath, buffer, max) != -1)
+    {
+        dirname (buffer);
+        strcat  (buffer, "/");
+        return strlen(buffer);
+    }
+    return 0;
+}
+#endif
+
+U14API(short) U14LdCmd(short hand, const char* command)
+{
+    char strcopy[MAXSTRLEN+1];      // to hold copy of work string
+    BOOL bGotIt = FALSE;            // have we found the command file?
+    int iFHandle;                   // file handle of command
+#define FNSZ 260
+    char filnam[FNSZ];              // space to build name in
+    char szCmd[25];                 // just the command name with extension
+
+    short sErr = CheckHandle(hand);
+    if (sErr != U14ERR_NOERROR)
+        return sErr;
+
+    if (strchr(command, '.') != NULL)       // see if we have full name
+    {
+        if (file_exist(command))            // If the file exists
+        {
+            strcpy(filnam, command);        // use name as is
+            bGotIt = TRUE;                  // Flag no more searching
+        }
+        else                                // not found, get file name for search
+        {
+            char* pStr = strrchr(command, PATHSEP);  // Point to last separator
+            if (pStr != NULL)               // Check we got it
+            {
+                pStr++;                     // move past the backslash
+                strcpy(szCmd, pStr);        // copy file name as is
+            }
+            else
+                strcpy(szCmd, command);     // use as is
+        }
+    }
+    else    // File extension not supplied, so build the command file name
+    {
+        char szExt[8];
+        strcpy(szCmd, command);             // Build command file name
+        ExtForType(asType1401[hand], szExt);// File extension string
+        strcat(szCmd, szExt);               // add it to the end
+    }
+
+    // Next place to look is in the 1401 folder in the same place as the
+    // application was run from.
+    if (!bGotIt)                            // Still not got it?
+    {
+        DWORD dwLen = GetModuleFileName(NULL, filnam, FNSZ); // Get app path
+        if (dwLen > 0)                      // and use it as path if found
+        {
+            char* pStr = strrchr(filnam, PATHSEP);    // Point to last separator
+            if (pStr != NULL)
+            {
+                *(++pStr) = 0;                  // Terminate string there
+                if (strlen(filnam) < FNSZ-6)    // make sure we have space
+                {
+                    strcat(filnam, "1401" PATHSEPSTR);  // add in 1401 subdir
+                    strcat(filnam,szCmd);
+                    bGotIt = (BOOL)file_exist(filnam);  // See if file exists
+                }
+            }
+        }
+    }
+
+    // Next place to look is in whatever path is set by the 1401DIR environment
+    // variable, if it exists.
+    if (!bGotIt)                            // Need to do more searches?/
+    {
+        char* pStr = getenv("1401DIR");     // Try to find environment var
+        if (pStr != NULL)                   // and use it as path if found
+        {
+            strcpy(filnam, pStr);                   // Use path in environment
+            if (filnam[strlen(filnam)-1] != PATHSEP)// We need separator
+                strcat(filnam, PATHSEPSTR);
+            strcat(filnam, szCmd);
+            bGotIt = (BOOL)file_exist(filnam); // Got this one?
+        }
+    }
+
+    // Last place to look is the default location.
+    if (!bGotIt)                        // Need to do more searches?
+    {
+        strcpy(filnam, DEFCMDPATH);     // Use default path
+        strcat(filnam, szCmd);
+        bGotIt = file_exist(filnam);    // Got this one?
+    }
+
+    iFHandle = file_open(filnam);
+    if (iFHandle == -1)
+        sErr = U14ERR_NOFILE;
+    else
+    {                                   // first read in the header block
+        CMDHEAD rCmdHead;               // to hold the command header
+        if (file_read(iFHandle, &rCmdHead, sizeof(CMDHEAD)))
+        {
+            size_t nComSize = rCmdHead.wCmdSize;
+            char* pMem = malloc(nComSize);
+            if (pMem != NULL)
+            {
+                file_seek(iFHandle, sizeof(CMDHEAD));
+                if (file_read(iFHandle, pMem, (UINT)nComSize))
+                {
+                    sErr = U14SetTransArea(hand, 0, (void *)pMem, (DWORD)nComSize, ESZBYTES);
+                    if (sErr == U14ERR_NOERROR)
+                    {
+                        sprintf(strcopy, "CLOAD,0,$%X;", (int)nComSize);
+                        sErr = U14SendString(hand, strcopy);
+                        if (sErr == U14ERR_NOERROR)
+                        {
+                            sErr = U14CheckErr(hand);     // Use ERR to check for done
+                            if (sErr > 0)
+                                sErr = U14ERR_CLOADERR;   // If an error, this code
+                        }
+                        U14UnSetTransfer(hand, 0);  // release transfer area
+                    }
+                }
+                else
+                    sErr = U14ERR_READERR;
+                free(pMem);
+            }
+            else
+                sErr = U14ERR_HOSTSPACE;    // memory allocate failed
+        }
+        else
+            sErr = U14ERR_READERR;
+
+        file_close(iFHandle);               // close the file
+    }
+
+    return sErr;
+}
+
+
+/****************************************************************************
+** Ld
+** Loads a command into the 1401
+** Returns NOERROR code or a long with error in lo word and index of
+** command that failed in high word
+****************************************************************************/
+U14API(DWORD) U14Ld(short hand, const char* vl, const char* str)
+{
+    DWORD dwIndex = 0;              // index to current command
+    long lErr = U14ERR_NOERROR;     // what the error was that went wrong
+    char strcopy[MAXSTRLEN+1];      // stores unmodified str parameter
+    char szFExt[8];                 // The command file extension
+    short sErr = CheckHandle(hand);
+    if (sErr != U14ERR_NOERROR)
+        return sErr;
+
+    ExtForType(asType1401[hand], szFExt);   // File extension string
+    strcpy(strcopy, str);               // to avoid changing original
+
+    // now break out one command at a time and see if loaded
+    if (*str)                           // if anything there
+    {
+        BOOL bDone = FALSE;             // true when finished all commands
+        int iLoop1 = 0;                 // Point at start of string for command name
+        int iLoop2 = 0;                 // and at start of str parameter
+        do                              // repeat until end of str
+        {
+            char filnam[MAXSTRLEN+1];   // filename to use
+            char szFName[MAXSTRLEN+1];  // filename work string
+
+            if (!strcopy[iLoop1])       // at the end of the string?
+                bDone = TRUE;           // set the finish flag
+
+            if (bDone || (strcopy[iLoop1] == ','))  // end of cmd?
+            {
+                U14LONG er[5];                  // Used to read back error results
+                ++dwIndex;                      // Keep count of command number, first is 1
+                szFName[iLoop2]=(char)0;        // null terminate name of command
+
+                strncpy(szLastName, szFName, sizeof(szLastName));    // Save for error info
+                szLastName[sizeof(szLastName)-1] = 0;
+                strncat(szLastName, szFExt, sizeof(szLastName));     // with extension included
+                szLastName[sizeof(szLastName)-1] = 0;
+
+                U14SendString(hand, szFName);   // ask if loaded
+                U14SendString(hand, ";ERR;");   // add err return
+
+                lErr = U14LongsFrom1401(hand, er, 5);
+                if (lErr > 0)
+                {
+                    lErr = U14ERR_NOERROR;
+                    if (er[0] == 255)           // if command not loaded at all
+                    {
+                        if (vl && *vl)          // if we have a path name
+                        {
+                            strcpy(filnam, vl);
+                            if (strchr("\\/:", filnam[strlen(filnam)-1]) == NULL)
+                                strcat(filnam, PATHSEPSTR); // add separator if none found
+                            strcat(filnam, szFName);    // add the file name
+                            strcat(filnam, szFExt);     // and extension
+                        }
+                        else
+                            strcpy(filnam, szFName);    // simple name
+
+                        lErr = U14LdCmd(hand, filnam);  // load cmd
+                        if (lErr != U14ERR_NOERROR)     // spot any errors
+                            bDone = TRUE;               // give up if an error
+                    }
+                }
+                else
+                    bDone = TRUE;       // give up if an error
+
+                iLoop2 = 0;             // Reset pointer to command name string
+                ++iLoop1;               // and move on through str parameter
+            }
+            else
+                szFName[iLoop2++] = strcopy[iLoop1++];  // no command end, so copy 1 char
+        }
+        while (!bDone);
+    }
+
+    if (lErr == U14ERR_NOERROR)
+    {
+        szLastName[0] = 0;      // No error, so clean out command name here
+        return lErr;
+    }
+    else
+        return ((dwIndex<<16) | ((DWORD)lErr & 0x0000FFFF));
+}
+
+// Initialise the library (if not initialised) and return the library version
+U14API(int) U14InitLib(void)
+{
+    int iRetVal = U14LIB_VERSION;
+    if (iAttached == 0)         // only do this the first time please
+    {
+        int i;
+#ifdef _IS_WINDOWS_
+        int j;
+        DWORD   dwVersion = GetVersion();
+        bWindows9x = FALSE;                  // Assume not Win9x
+
+        if (dwVersion & 0x80000000)                 // if not windows NT
+        {
+            if ((LOBYTE(LOWORD(dwVersion)) < 4) &&  // if Win32s or...
+                 (HIBYTE(LOWORD(dwVersion)) < 95))  // ...below Windows 95
+            iRetVal = 0;                            // We do not support this
+        else
+            bWindows9x = TRUE;                      // Flag we have Win9x
+        }
+#endif
+        
+        for (i = 0; i < MAX1401; i++)               // initialise the device area
+        {
+            aHand1401[i] = INVALID_HANDLE_VALUE;    // Clear handle values
+            asType1401[i] = U14TYPEUNKNOWN;         // and 1401 type codes
+            alTimeOutPeriod[i] = 3000;              // 3 second timeouts
+#ifdef _IS_WINDOWS_
+#ifndef _WIN64
+            abUseNTDIOC[i] = (BOOL)!bWindows9x;
+#endif
+            aXferEvent[i] = NULL;                   // there are no Xfer events
+            for (j = 0; j < MAX_TRANSAREAS; j++)    // Clear out locked area info
+            {
+                apAreas[i][j] = NULL;
+                auAreas[i][j] = 0;
+            }
+#endif
+        }
+    }
+    return iRetVal;
+}
+
+///--------------------------------------------------------------------------------
+/// Functions called when the library is loaded and unloaded to give us a chance to
+/// setup the library.
+
+
+#ifdef _IS_WINDOWS_
+#ifndef U14_NOT_DLL
+/****************************************************************************
+** FUNCTION: DllMain(HANDLE, DWORD, LPVOID)
+** LibMain is called by Windows when the DLL is initialized, Thread Attached,
+** and other times. Refer to SDK documentation, as to the different ways this
+** may be called.
+****************************************************************************/
+INT APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved)
+{
+    int iRetVal = 1;
+
+    switch (ul_reason_being_called)
+    {
+    case DLL_PROCESS_ATTACH:
+        iRetVal = U14InitLib() > 0;         // does nothing if iAttached != 0
+        ++iAttached;                        // count times attached
+        break;
+
+    case DLL_PROCESS_DETACH:
+        if (--iAttached == 0)               // last man out?
+            U14CloseAll();                  // release all open handles
+        break;
+    }
+    return iRetVal;
+
+    UNREFERENCED_PARAMETER(lpReserved);
+}
+#endif
+#endif
+#ifdef LINUX
+void __attribute__((constructor)) use1401_load(void)
+{
+    U14InitLib();
+    ++iAttached;
+}
+
+void __attribute__((destructor)) use1401_unload(void)
+{
+        if (--iAttached == 0)               // last man out?
+            U14CloseAll();                  // release all open handles
+}
+#endif

+ 28 - 38
drivers/staging/comedi/Kconfig

@@ -549,6 +549,23 @@ menuconfig COMEDI_PCI_DRIVERS
 
 if COMEDI_PCI_DRIVERS
 
+config COMEDI_8255_PCI
+	tristate "Generic PCI based 8255 digital i/o board support"
+	select COMEDI_8255
+	---help---
+	  Enable support for PCI based 8255 digital i/o boards. This driver
+	  provides a PCI wrapper around the generic 8255 driver.
+
+	  Supported boards:
+	    ADlink - PCI-7224, PCI-7248, and PCI-7296
+	    Measurement Computing - PCI-DIO24, PCI-DIO24H, PCI-DIO48H and
+	                            PCI-DIO96H
+	    National Instruments - PCI-DIO-96, PCI-DIO-96B, PXI-6508, PCI-6503,
+	                           PCI-6503B, PCI-6503X, and PXI-6503
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called 8255_pci.
+
 config COMEDI_ADDI_APCI_035
 	tristate "ADDI-DATA APCI_035 support"
 	depends on VIRT_TO_BUS
@@ -676,30 +693,16 @@ config COMEDI_ADL_PCI6208
 	  To compile this driver as a module, choose M here: the module will be
 	  called adl_pci6208.
 
-config COMEDI_ADL_PCI7230
-	tristate "ADLink PCI-7230 digital io board support"
-	---help---
-	  Enable support for ADlink PCI-7230 digital io board support
-
-	  To compile this driver as a module, choose M here: the module will be
-	  called adl_pci7230.
-
-config COMEDI_ADL_PCI7296
-	tristate "ADLink PCI-7296 96 ch. digital io board support"
-	select COMEDI_8255
+config COMEDI_ADL_PCI7X3X
+	tristate "ADLink PCI-723X/743X isolated digital i/o board support"
 	---help---
-	  Enable support for ADlink PCI-7296 96 ch. digital io board support
+	  Enable support for ADlink PCI-723X/743X isolated digital i/o boards.
+	  Supported boards include the 32-channel PCI-7230 (16 in/16 out),
+	  PCI-7233 (32 in), and PCI-7234 (32 out) as well as the 64-channel
+	  PCI-7432 (32 in/32 out), PCI-7433 (64 in), and PCI-7434 (64 out).
 
 	  To compile this driver as a module, choose M here: the module will be
-	  called adl_pci7296.
-
-config COMEDI_ADL_PCI7432
-	tristate "ADLink PCI-7432 64 ch. isolated digital io board support"
-	---help---
-	  Enable support for ADlink PCI-7432 64 ch. isolated digital io board
-
-	  To compile this driver as a module, choose M here: the module will be
-	  called adl_pci7432.
+	  called adl_pci7x3x.
 
 config COMEDI_ADL_PCI8164
 	tristate "ADLink PCI-8164 4 Axes Motion Control board support"
@@ -935,16 +938,6 @@ config COMEDI_CB_PCIDDA
 	  To compile this driver as a module, choose M here: the module will be
 	  called cb_pcidda.
 
-config COMEDI_CB_PCIDIO
-	tristate "MeasurementComputing PCI-DIO series support"
-	select COMEDI_8255
-	---help---
-	  Enable support for ComputerBoards/MeasurementComputing PCI-DIO series
-	  PCI-DIO24, PCI-DIO24H and PCI-DIO48H
-
-	  To compile this driver as a module, choose M here: the module will be
-	  called cb_pcidio.
-
 config COMEDI_CB_PCIMDAS
 	tristate "MeasurementComputing PCIM-DAS1602/16 support"
 	select COMEDI_8255
@@ -1039,15 +1032,12 @@ config COMEDI_NI_LABPC
 	  called ni_labpc.
 
 config COMEDI_NI_PCIDIO
-	tristate "NI PCI-DIO32HS, PCI-DIO96, PCI-6533, PCI-6503 support"
+	tristate "NI PCI-DIO32HS, PCI-6533, PCI-6534 support"
 	select COMEDI_MITE
 	select COMEDI_8255
 	---help---
 	  Enable support for National Instruments PCI-DIO-32HS, PXI-6533,
-	  PCI-DIO-96, PCI-DIO-96B, PXI-6508, PCI-6503, PCI-6503B, PCI-6503X,
-	  PXI-6503, PCI-6533 and PCI-6534
-	  The DIO-96 appears as four 8255 subdevices. See the 8255
-	  driver notes for details.
+	  PCI-6533 and PCI-6534
 
 	  To compile this driver as a module, choose M here: the module will be
 	  called ni_pcidio.
@@ -1262,8 +1252,8 @@ config COMEDI_8255
 	  that has an 8255 chip. For multifunction boards, the main driver will
 	  configure the 8255 subdevice automatically.
 
-	  Note that most PCI 8255 boards do NOT work with this driver, and
-	  need a separate driver as a wrapper.
+	  Note that most PCI based 8255 boards use the 8255_pci driver as a
+	  wrapper around this driver.
 
 	  To compile this driver as a module, choose M here: the module will be
 	  called 8255.

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 470 - 470
drivers/staging/comedi/comedi.h


+ 73 - 79
drivers/staging/comedi/comedi_fops.c

@@ -370,7 +370,8 @@ static int do_devconfig_ioctl(struct comedi_device *dev,
 			return -ENOMEM;
 
 		if (copy_from_user(aux_data,
-				   comedi_aux_data(it.options, 0), aux_len)) {
+				   (unsigned char __user *
+				    )comedi_aux_data(it.options, 0), aux_len)) {
 			vfree(aux_data);
 			return -EFAULT;
 		}
@@ -426,7 +427,7 @@ static int do_bufconfig_ioctl(struct comedi_device *dev,
 	if (bc.subdevice >= dev->n_subdevices || bc.subdevice < 0)
 		return -EINVAL;
 
-	s = dev->subdevices + bc.subdevice;
+	s = &dev->subdevices[bc.subdevice];
 	async = s->async;
 
 	if (!async) {
@@ -539,7 +540,7 @@ static int do_subdinfo_ioctl(struct comedi_device *dev,
 
 	/* fill subdinfo structs */
 	for (i = 0; i < dev->n_subdevices; i++) {
-		s = dev->subdevices + i;
+		s = &dev->subdevices[i];
 		us = tmp + i;
 
 		us->type = s->type;
@@ -617,7 +618,7 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
 
 	if (it.subdev >= dev->n_subdevices)
 		return -EINVAL;
-	s = dev->subdevices + it.subdev;
+	s = &dev->subdevices[it.subdev];
 
 	if (it.maxdata_list) {
 		if (s->maxdata || !s->maxdata_list)
@@ -685,7 +686,7 @@ static int do_bufinfo_ioctl(struct comedi_device *dev,
 	if (bi.subdevice >= dev->n_subdevices || bi.subdevice < 0)
 		return -EINVAL;
 
-	s = dev->subdevices + bi.subdevice;
+	s = &dev->subdevices[bi.subdevice];
 
 	if (s->lock && s->lock != file)
 		return -EACCES;
@@ -882,14 +883,12 @@ static int check_insn_config_length(struct comedi_insn *insn,
 		/* by default we allow the insn since we don't have checks for
 		 * all possible cases yet */
 	default:
-		printk(KERN_WARNING
-		       "comedi: no check for data length of config insn id "
-		       "%i is implemented.\n"
-		       " Add a check to %s in %s.\n"
-		       " Assuming n=%i is correct.\n", data[0], __func__,
-		       __FILE__, insn->n);
+		pr_warn("comedi: No check for data length of config insn id %i is implemented.\n",
+			data[0]);
+		pr_warn("comedi: Add a check to %s in %s.\n",
+			__func__, __FILE__);
+		pr_warn("comedi: Assuming n=%i is correct.\n", insn->n);
 		return 0;
-		break;
 	}
 	return -EINVAL;
 }
@@ -940,7 +939,7 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
 				ret = -EINVAL;
 				break;
 			}
-			s = dev->subdevices + insn->subdev;
+			s = &dev->subdevices[insn->subdev];
 			if (!s->async) {
 				DPRINTK("no async\n");
 				ret = -EINVAL;
@@ -951,7 +950,7 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
 				ret = -EAGAIN;
 				break;
 			}
-			ret = s->async->inttrig(dev, s, insn->data[0]);
+			ret = s->async->inttrig(dev, s, data[0]);
 			if (ret >= 0)
 				ret = 1;
 			break;
@@ -969,7 +968,7 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
 			ret = -EINVAL;
 			goto out;
 		}
-		s = dev->subdevices + insn->subdev;
+		s = &dev->subdevices[insn->subdev];
 
 		if (s->type == COMEDI_SUBD_UNUSED) {
 			DPRINTK("%d not usable subdevice\n", insn->subdev);
@@ -1133,37 +1132,37 @@ static void comedi_set_subdevice_runflags(struct comedi_subdevice *s,
 }
 
 static int do_cmd_ioctl(struct comedi_device *dev,
-			struct comedi_cmd __user *cmd, void *file)
+			struct comedi_cmd __user *arg, void *file)
 {
-	struct comedi_cmd user_cmd;
+	struct comedi_cmd cmd;
 	struct comedi_subdevice *s;
 	struct comedi_async *async;
 	int ret = 0;
-	unsigned int __user *chanlist_saver = NULL;
+	unsigned int __user *user_chanlist;
 
-	if (copy_from_user(&user_cmd, cmd, sizeof(struct comedi_cmd))) {
+	if (copy_from_user(&cmd, arg, sizeof(struct comedi_cmd))) {
 		DPRINTK("bad cmd address\n");
 		return -EFAULT;
 	}
 	/* save user's chanlist pointer so it can be restored later */
-	chanlist_saver = user_cmd.chanlist;
+	user_chanlist = (unsigned int __user *)cmd.chanlist;
 
-	if (user_cmd.subdev >= dev->n_subdevices) {
-		DPRINTK("%d no such subdevice\n", user_cmd.subdev);
+	if (cmd.subdev >= dev->n_subdevices) {
+		DPRINTK("%d no such subdevice\n", cmd.subdev);
 		return -ENODEV;
 	}
 
-	s = dev->subdevices + user_cmd.subdev;
+	s = &dev->subdevices[cmd.subdev];
 	async = s->async;
 
 	if (s->type == COMEDI_SUBD_UNUSED) {
-		DPRINTK("%d not valid subdevice\n", user_cmd.subdev);
+		DPRINTK("%d not valid subdevice\n", cmd.subdev);
 		return -EIO;
 	}
 
 	if (!s->do_cmd || !s->do_cmdtest || !s->async) {
 		DPRINTK("subdevice %i does not support commands\n",
-			user_cmd.subdev);
+			cmd.subdev);
 		return -EIO;
 	}
 
@@ -1181,23 +1180,22 @@ static int do_cmd_ioctl(struct comedi_device *dev,
 	s->busy = file;
 
 	/* make sure channel/gain list isn't too long */
-	if (user_cmd.chanlist_len > s->len_chanlist) {
+	if (cmd.chanlist_len > s->len_chanlist) {
 		DPRINTK("channel/gain list too long %u > %d\n",
-			user_cmd.chanlist_len, s->len_chanlist);
+			cmd.chanlist_len, s->len_chanlist);
 		ret = -EINVAL;
 		goto cleanup;
 	}
 
 	/* make sure channel/gain list isn't too short */
-	if (user_cmd.chanlist_len < 1) {
+	if (cmd.chanlist_len < 1) {
 		DPRINTK("channel/gain list too short %u < 1\n",
-			user_cmd.chanlist_len);
+			cmd.chanlist_len);
 		ret = -EINVAL;
 		goto cleanup;
 	}
 
-	kfree(async->cmd.chanlist);
-	async->cmd = user_cmd;
+	async->cmd = cmd;
 	async->cmd.data = NULL;
 	/* load channel/gain list */
 	async->cmd.chanlist =
@@ -1208,7 +1206,7 @@ static int do_cmd_ioctl(struct comedi_device *dev,
 		goto cleanup;
 	}
 
-	if (copy_from_user(async->cmd.chanlist, user_cmd.chanlist,
+	if (copy_from_user(async->cmd.chanlist, user_chanlist,
 			   async->cmd.chanlist_len * sizeof(int))) {
 		DPRINTK("fault reading chanlist\n");
 		ret = -EFAULT;
@@ -1228,11 +1226,11 @@ static int do_cmd_ioctl(struct comedi_device *dev,
 
 	if (async->cmd.flags & TRIG_BOGUS || ret) {
 		DPRINTK("test returned %d\n", ret);
-		user_cmd = async->cmd;
+		cmd = async->cmd;
 		/* restore chanlist pointer before copying back */
-		user_cmd.chanlist = chanlist_saver;
-		user_cmd.data = NULL;
-		if (copy_to_user(cmd, &user_cmd, sizeof(struct comedi_cmd))) {
+		cmd.chanlist = (unsigned int __force *)user_chanlist;
+		cmd.data = NULL;
+		if (copy_to_user(arg, &cmd, sizeof(struct comedi_cmd))) {
 			DPRINTK("fault writing cmd\n");
 			ret = -EFAULT;
 			goto cleanup;
@@ -1285,77 +1283,77 @@ cleanup:
 static int do_cmdtest_ioctl(struct comedi_device *dev,
 			    struct comedi_cmd __user *arg, void *file)
 {
-	struct comedi_cmd user_cmd;
+	struct comedi_cmd cmd;
 	struct comedi_subdevice *s;
 	int ret = 0;
 	unsigned int *chanlist = NULL;
-	unsigned int __user *chanlist_saver = NULL;
+	unsigned int __user *user_chanlist;
 
-	if (copy_from_user(&user_cmd, arg, sizeof(struct comedi_cmd))) {
+	if (copy_from_user(&cmd, arg, sizeof(struct comedi_cmd))) {
 		DPRINTK("bad cmd address\n");
 		return -EFAULT;
 	}
 	/* save user's chanlist pointer so it can be restored later */
-	chanlist_saver = user_cmd.chanlist;
+	user_chanlist = (unsigned int __user *)cmd.chanlist;
 
-	if (user_cmd.subdev >= dev->n_subdevices) {
-		DPRINTK("%d no such subdevice\n", user_cmd.subdev);
+	if (cmd.subdev >= dev->n_subdevices) {
+		DPRINTK("%d no such subdevice\n", cmd.subdev);
 		return -ENODEV;
 	}
 
-	s = dev->subdevices + user_cmd.subdev;
+	s = &dev->subdevices[cmd.subdev];
 	if (s->type == COMEDI_SUBD_UNUSED) {
-		DPRINTK("%d not valid subdevice\n", user_cmd.subdev);
+		DPRINTK("%d not valid subdevice\n", cmd.subdev);
 		return -EIO;
 	}
 
 	if (!s->do_cmd || !s->do_cmdtest) {
 		DPRINTK("subdevice %i does not support commands\n",
-			user_cmd.subdev);
+			cmd.subdev);
 		return -EIO;
 	}
 
 	/* make sure channel/gain list isn't too long */
-	if (user_cmd.chanlist_len > s->len_chanlist) {
+	if (cmd.chanlist_len > s->len_chanlist) {
 		DPRINTK("channel/gain list too long %d > %d\n",
-			user_cmd.chanlist_len, s->len_chanlist);
+			cmd.chanlist_len, s->len_chanlist);
 		ret = -EINVAL;
 		goto cleanup;
 	}
 
 	/* load channel/gain list */
-	if (user_cmd.chanlist) {
+	if (cmd.chanlist) {
 		chanlist =
-		    kmalloc(user_cmd.chanlist_len * sizeof(int), GFP_KERNEL);
+		    kmalloc(cmd.chanlist_len * sizeof(int), GFP_KERNEL);
 		if (!chanlist) {
 			DPRINTK("allocation failed\n");
 			ret = -ENOMEM;
 			goto cleanup;
 		}
 
-		if (copy_from_user(chanlist, user_cmd.chanlist,
-				   user_cmd.chanlist_len * sizeof(int))) {
+		if (copy_from_user(chanlist, user_chanlist,
+				   cmd.chanlist_len * sizeof(int))) {
 			DPRINTK("fault reading chanlist\n");
 			ret = -EFAULT;
 			goto cleanup;
 		}
 
 		/* make sure each element in channel/gain list is valid */
-		ret = comedi_check_chanlist(s, user_cmd.chanlist_len, chanlist);
+		ret = comedi_check_chanlist(s, cmd.chanlist_len, chanlist);
 		if (ret < 0) {
 			DPRINTK("bad chanlist\n");
 			goto cleanup;
 		}
 
-		user_cmd.chanlist = chanlist;
+		cmd.chanlist = chanlist;
 	}
 
-	ret = s->do_cmdtest(dev, s, &user_cmd);
+	ret = s->do_cmdtest(dev, s, &cmd);
 
 	/* restore chanlist pointer before copying back */
-	user_cmd.chanlist = chanlist_saver;
+	cmd.chanlist = (unsigned int __force *)user_chanlist;
 
-	if (copy_to_user(arg, &user_cmd, sizeof(struct comedi_cmd))) {
+	if (copy_to_user(arg, &cmd, sizeof(struct comedi_cmd))) {
 		DPRINTK("bad cmd address\n");
 		ret = -EFAULT;
 		goto cleanup;
@@ -1390,7 +1388,7 @@ static int do_lock_ioctl(struct comedi_device *dev, unsigned int arg,
 
 	if (arg >= dev->n_subdevices)
 		return -EINVAL;
-	s = dev->subdevices + arg;
+	s = &dev->subdevices[arg];
 
 	spin_lock_irqsave(&s->spin_lock, flags);
 	if (s->busy || s->lock)
@@ -1433,7 +1431,7 @@ static int do_unlock_ioctl(struct comedi_device *dev, unsigned int arg,
 
 	if (arg >= dev->n_subdevices)
 		return -EINVAL;
-	s = dev->subdevices + arg;
+	s = &dev->subdevices[arg];
 
 	if (s->busy)
 		return -EBUSY;
@@ -1474,7 +1472,7 @@ static int do_cancel_ioctl(struct comedi_device *dev, unsigned int arg,
 
 	if (arg >= dev->n_subdevices)
 		return -EINVAL;
-	s = dev->subdevices + arg;
+	s = &dev->subdevices[arg];
 	if (s->async == NULL)
 		return -EINVAL;
 
@@ -1511,7 +1509,7 @@ static int do_poll_ioctl(struct comedi_device *dev, unsigned int arg,
 
 	if (arg >= dev->n_subdevices)
 		return -EINVAL;
-	s = dev->subdevices + arg;
+	s = &dev->subdevices[arg];
 
 	if (s->lock && s->lock != file)
 		return -EACCES;
@@ -2025,7 +2023,8 @@ done:
 /*
    This function restores a subdevice to an idle state.
  */
-void do_become_nonbusy(struct comedi_device *dev, struct comedi_subdevice *s)
+static void do_become_nonbusy(struct comedi_device *dev,
+			      struct comedi_subdevice *s)
 {
 	struct comedi_async *async = s->async;
 
@@ -2033,9 +2032,11 @@ void do_become_nonbusy(struct comedi_device *dev, struct comedi_subdevice *s)
 	if (async) {
 		comedi_reset_async_buf(async);
 		async->inttrig = NULL;
+		kfree(async->cmd.chanlist);
+		async->cmd.chanlist = NULL;
 	} else {
-		printk(KERN_ERR
-		       "BUG: (?) do_become_nonbusy called with async=0\n");
+		dev_err(dev->class_dev,
+			"BUG: (?) do_become_nonbusy called with async=NULL\n");
 	}
 
 	s->busy = NULL;
@@ -2140,7 +2141,7 @@ static int comedi_close(struct inode *inode, struct file *file)
 
 	if (dev->subdevices) {
 		for (i = 0; i < dev->n_subdevices; i++) {
-			s = dev->subdevices + i;
+			s = &dev->subdevices[i];
 
 			if (s->busy == file)
 				do_cancel(dev, s);
@@ -2211,14 +2212,12 @@ static int __init comedi_init(void)
 	int i;
 	int retval;
 
-	printk(KERN_INFO "comedi: version " COMEDI_RELEASE
-	       " - http://www.comedi.org\n");
+	pr_info("comedi: version " COMEDI_RELEASE " - http://www.comedi.org\n");
 
 	if (comedi_num_legacy_minors < 0 ||
 	    comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
-		printk(KERN_ERR "comedi: error: invalid value for module "
-		       "parameter \"comedi_num_legacy_minors\".  Valid values "
-		       "are 0 through %i.\n", COMEDI_NUM_BOARD_MINORS);
+		pr_err("comedi: error: invalid value for module parameter \"comedi_num_legacy_minors\".  Valid values are 0 through %i.\n",
+		       COMEDI_NUM_BOARD_MINORS);
 		return -EINVAL;
 	}
 
@@ -2247,7 +2246,7 @@ static int __init comedi_init(void)
 	}
 	comedi_class = class_create(THIS_MODULE, "comedi");
 	if (IS_ERR(comedi_class)) {
-		printk(KERN_ERR "comedi: failed to create class");
+		pr_err("comedi: failed to create class\n");
 		cdev_del(&comedi_cdev);
 		unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
 					 COMEDI_NUM_MINORS);
@@ -2295,8 +2294,7 @@ module_exit(comedi_cleanup);
 
 void comedi_error(const struct comedi_device *dev, const char *s)
 {
-	printk(KERN_ERR "comedi%d: %s: %s\n", dev->minor,
-	       dev->driver->driver_name, s);
+	dev_err(dev->class_dev, "%s: %s\n", dev->driver->driver_name, s);
 }
 EXPORT_SYMBOL(comedi_error);
 
@@ -2364,7 +2362,7 @@ static int is_device_busy(struct comedi_device *dev)
 		return 0;
 
 	for (i = 0; i < dev->n_subdevices; i++) {
-		s = dev->subdevices + i;
+		s = &dev->subdevices[i];
 		if (s->busy)
 			return 1;
 		if (s->async && s->async->mmap_count)
@@ -2420,9 +2418,7 @@ int comedi_alloc_board_minor(struct device *hardware_device)
 		comedi_device_cleanup(info->device);
 		kfree(info->device);
 		kfree(info);
-		printk(KERN_ERR
-		       "comedi: error: "
-		       "ran out of minor numbers for board device files.\n");
+		pr_err("comedi: error: ran out of minor numbers for board device files.\n");
 		return -EBUSY;
 	}
 	info->device->minor = i;
@@ -2499,9 +2495,7 @@ int comedi_alloc_subdevice_minor(struct comedi_device *dev,
 	spin_unlock(&comedi_file_info_table_lock);
 	if (i == COMEDI_NUM_MINORS) {
 		kfree(info);
-		printk(KERN_ERR
-		       "comedi: error: "
-		       "ran out of minor numbers for board device files.\n");
+		pr_err("comedi: error: ran out of minor numbers for board device files.\n");
 		return -EBUSY;
 	}
 	s->minor = i;

+ 1 - 1
drivers/staging/comedi/comedidev.h

@@ -46,7 +46,7 @@
 
 #define DPRINTK(format, args...)	do {		\
 	if (comedi_debug)				\
-		printk(KERN_DEBUG "comedi: " format , ## args);	\
+		pr_debug("comedi: " format, ## args);	\
 } while (0)
 
 #define COMEDI_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))

+ 45 - 37
drivers/staging/comedi/drivers.c

@@ -71,7 +71,7 @@ int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices)
 	dev->n_subdevices = num_subdevices;
 
 	for (i = 0; i < num_subdevices; ++i) {
-		s = dev->subdevices + i;
+		s = &dev->subdevices[i];
 		s->device = dev;
 		s->async_dma_dir = DMA_NONE;
 		spin_lock_init(&s->spin_lock);
@@ -88,7 +88,7 @@ static void cleanup_device(struct comedi_device *dev)
 
 	if (dev->subdevices) {
 		for (i = 0; i < dev->n_subdevices; i++) {
-			s = dev->subdevices + i;
+			s = &dev->subdevices[i];
 			comedi_free_subdevice_minor(s);
 			if (s->async) {
 				comedi_buf_alloc(dev, s, 0);
@@ -119,8 +119,8 @@ static void __comedi_device_detach(struct comedi_device *dev)
 	if (dev->driver)
 		dev->driver->detach(dev);
 	else
-		printk(KERN_WARNING
-		       "BUG: dev->driver=NULL in comedi_device_detach()\n");
+		dev_warn(dev->class_dev,
+			 "BUG: dev->driver=NULL in comedi_device_detach()\n");
 	cleanup_device(dev);
 }
 
@@ -142,8 +142,7 @@ static int comedi_device_postconfig(struct comedi_device *dev)
 		return ret;
 	}
 	if (!dev->board_name) {
-		printk(KERN_WARNING "BUG: dev->board_name=<%p>\n",
-		       dev->board_name);
+		dev_warn(dev->class_dev, "BUG: dev->board_name=NULL\n");
 		dev->board_name = "BUG";
 	}
 	smp_wmb();
@@ -160,10 +159,8 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 		return -EBUSY;
 
 	for (driv = comedi_drivers; driv; driv = driv->next) {
-		if (!try_module_get(driv->module)) {
-			printk(KERN_INFO "comedi: failed to increment module count, skipping\n");
+		if (!try_module_get(driv->module))
 			continue;
-		}
 		if (driv->num_names) {
 			dev->board_ptr = comedi_recognize(driv, it->board_name);
 			if (dev->board_ptr)
@@ -176,16 +173,21 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 		/*  recognize has failed if we get here */
 		/*  report valid board names before returning error */
 		for (driv = comedi_drivers; driv; driv = driv->next) {
-			if (!try_module_get(driv->module)) {
-				printk(KERN_INFO
-				       "comedi: failed to increment module count\n");
+			if (!try_module_get(driv->module))
 				continue;
-			}
 			comedi_report_boards(driv);
 			module_put(driv->module);
 		}
 		return -EIO;
 	}
+	if (driv->attach == NULL) {
+		/* driver does not support manual configuration */
+		dev_warn(dev->class_dev,
+			 "driver '%s' does not support attach using comedi_config\n",
+			 driv->driver_name);
+		module_put(driv->module);
+		return -ENOSYS;
+	}
 	/* initialize dev->driver here so
 	 * comedi_error() can be called from attach */
 	dev->driver = driv;
@@ -225,8 +227,9 @@ int comedi_driver_unregister(struct comedi_driver *driver)
 		mutex_lock(&dev->mutex);
 		if (dev->attached && dev->driver == driver) {
 			if (dev->use_count)
-				printk(KERN_WARNING "BUG! detaching device with use_count=%d\n",
-						dev->use_count);
+				dev_warn(dev->class_dev,
+					 "BUG! detaching device with use_count=%d\n",
+					 dev->use_count);
 			comedi_device_detach(dev);
 		}
 		mutex_unlock(&dev->mutex);
@@ -255,7 +258,7 @@ static int postconfig(struct comedi_device *dev)
 	int ret;
 
 	for (i = 0; i < dev->n_subdevices; i++) {
-		s = dev->subdevices + i;
+		s = &dev->subdevices[i];
 
 		if (s->type == COMEDI_SUBD_UNUSED)
 			continue;
@@ -273,8 +276,8 @@ static int postconfig(struct comedi_device *dev)
 			async =
 			    kzalloc(sizeof(struct comedi_async), GFP_KERNEL);
 			if (async == NULL) {
-				printk(KERN_INFO
-				       "failed to allocate async struct\n");
+				dev_warn(dev->class_dev,
+					 "failed to allocate async struct\n");
 				return -ENOMEM;
 			}
 			init_waitqueue_head(&async->wait_head);
@@ -290,7 +293,8 @@ static int postconfig(struct comedi_device *dev)
 			async->prealloc_buf = NULL;
 			async->prealloc_bufsz = 0;
 			if (comedi_buf_alloc(dev, s, buf_size) < 0) {
-				printk(KERN_INFO "Buffer allocation failed\n");
+				dev_warn(dev->class_dev,
+					 "Buffer allocation failed\n");
 				return -ENOMEM;
 			}
 			if (s->buf_change) {
@@ -370,17 +374,17 @@ static void comedi_report_boards(struct comedi_driver *driv)
 	unsigned int i;
 	const char *const *name_ptr;
 
-	printk(KERN_INFO "comedi: valid board names for %s driver are:\n",
-	       driv->driver_name);
+	pr_info("comedi: valid board names for %s driver are:\n",
+		driv->driver_name);
 
 	name_ptr = driv->board_name;
 	for (i = 0; i < driv->num_names; i++) {
-		printk(KERN_INFO " %s\n", *name_ptr);
+		pr_info(" %s\n", *name_ptr);
 		name_ptr = (const char **)((char *)name_ptr + driv->offset);
 	}
 
 	if (driv->num_names == 0)
-		printk(KERN_INFO " %s\n", driv->driver_name);
+		pr_info(" %s\n", driv->driver_name);
 }
 
 static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s)
@@ -411,7 +415,6 @@ static int insn_rw_emulate_bits(struct comedi_device *dev,
 	new_insn.insn = INSN_BITS;
 	new_insn.chanspec = base_bitfield_channel;
 	new_insn.n = 2;
-	new_insn.data = new_data;
 	new_insn.subdev = insn->subdev;
 
 	if (insn->insn == INSN_WRITE) {
@@ -584,9 +587,9 @@ static unsigned int comedi_buf_munge(struct comedi_async *async,
 
 		block_size = num_bytes - count;
 		if (block_size < 0) {
-			printk(KERN_WARNING
-			       "%s: %s: bug! block_size is negative\n",
-			       __FILE__, __func__);
+			dev_warn(s->device->class_dev,
+				 "%s: %s: bug! block_size is negative\n",
+				 __FILE__, __func__);
 			break;
 		}
 		if ((int)(async->munge_ptr + block_size -
@@ -667,7 +670,8 @@ unsigned comedi_buf_write_free(struct comedi_async *async, unsigned int nbytes)
 {
 	if ((int)(async->buf_write_count + nbytes -
 		  async->buf_write_alloc_count) > 0) {
-		printk(KERN_INFO "comedi: attempted to write-free more bytes than have been write-allocated.\n");
+		dev_info(async->subdevice->device->class_dev,
+			 "attempted to write-free more bytes than have been write-allocated.\n");
 		nbytes = async->buf_write_alloc_count - async->buf_write_count;
 	}
 	async->buf_write_count += nbytes;
@@ -703,8 +707,8 @@ unsigned comedi_buf_read_free(struct comedi_async *async, unsigned int nbytes)
 	smp_mb();
 	if ((int)(async->buf_read_count + nbytes -
 		  async->buf_read_alloc_count) > 0) {
-		printk(KERN_INFO
-		       "comedi: attempted to read-free more bytes than have been read-allocated.\n");
+		dev_info(async->subdevice->device->class_dev,
+			 "attempted to read-free more bytes than have been read-allocated.\n");
 		nbytes = async->buf_read_alloc_count - async->buf_read_count;
 	}
 	async->buf_read_count += nbytes;
@@ -853,10 +857,9 @@ comedi_auto_config_helper(struct device *hardware_device,
 	mutex_lock(&comedi_dev->mutex);
 	if (comedi_dev->attached)
 		ret = -EBUSY;
-	else if (!try_module_get(driver->module)) {
-		printk(KERN_INFO "comedi: failed to increment module count\n");
+	else if (!try_module_get(driver->module))
 		ret = -EIO;
-	} else {
+	else {
 		/* set comedi_dev->driver here for attach wrapper */
 		comedi_dev->driver = driver;
 		ret = (*attach_wrapper)(comedi_dev, context);
@@ -884,14 +887,19 @@ static int comedi_auto_config_wrapper(struct comedi_device *dev, void *context)
 		 * has already been copied to it->board_name */
 		dev->board_ptr = comedi_recognize(driv, it->board_name);
 		if (dev->board_ptr == NULL) {
-			printk(KERN_WARNING
-			       "comedi: auto config failed to find board entry"
-			       " '%s' for driver '%s'\n", it->board_name,
-			       driv->driver_name);
+			dev_warn(dev->class_dev,
+				 "auto config failed to find board entry '%s' for driver '%s'\n",
+				 it->board_name, driv->driver_name);
 			comedi_report_boards(driv);
 			return -EINVAL;
 		}
 	}
+	if (!driv->attach) {
+		dev_warn(dev->class_dev,
+			 "BUG! driver '%s' using old-style auto config but has no attach handler\n",
+			 driv->driver_name);
+		return -EINVAL;
+	}
 	return driv->attach(dev, it);
 }
 

+ 9 - 6
drivers/staging/comedi/drivers/8253.h

@@ -262,8 +262,10 @@ static inline int i8254_load(unsigned long base_address, unsigned int regshift,
 	return 0;
 }
 
-static inline int i8254_mm_load(void *base_address, unsigned int regshift,
-				unsigned int counter_number, unsigned int count,
+static inline int i8254_mm_load(void __iomem *base_address,
+				unsigned int regshift,
+				unsigned int counter_number,
+				unsigned int count,
 				unsigned int mode)
 {
 	unsigned int byte;
@@ -311,7 +313,8 @@ static inline int i8254_read(unsigned long base_address, unsigned int regshift,
 	return ret;
 }
 
-static inline int i8254_mm_read(void *base_address, unsigned int regshift,
+static inline int i8254_mm_read(void __iomem *base_address,
+				unsigned int regshift,
 				unsigned int counter_number)
 {
 	unsigned int byte;
@@ -348,7 +351,7 @@ static inline void i8254_write(unsigned long base_address,
 	outb(byte, base_address + (counter_number << regshift));
 }
 
-static inline void i8254_mm_write(void *base_address,
+static inline void i8254_mm_write(void __iomem *base_address,
 				  unsigned int regshift,
 				  unsigned int counter_number,
 				  unsigned int count)
@@ -390,7 +393,7 @@ static inline int i8254_set_mode(unsigned long base_address,
 	return 0;
 }
 
-static inline int i8254_mm_set_mode(void *base_address,
+static inline int i8254_mm_set_mode(void __iomem *base_address,
 				    unsigned int regshift,
 				    unsigned int counter_number,
 				    unsigned int mode)
@@ -419,7 +422,7 @@ static inline int i8254_status(unsigned long base_address,
 	return inb(base_address + (counter_number << regshift));
 }
 
-static inline int i8254_mm_status(void *base_address,
+static inline int i8254_mm_status(void __iomem *base_address,
 				  unsigned int regshift,
 				  unsigned int counter_number)
 {

+ 12 - 29
drivers/staging/comedi/drivers/8255.c

@@ -82,6 +82,8 @@ I/O port base address can be found in the output of 'lspci -v'.
 
 #include <linux/ioport.h>
 #include <linux/slab.h>
+
+#include "comedi_fc.h"
 #include "8255.h"
 
 #define _8255_SIZE	4
@@ -229,39 +231,20 @@ static int subdev_8255_cmdtest(struct comedi_device *dev,
 			       struct comedi_cmd *cmd)
 {
 	int err = 0;
-	unsigned int tmp;
-
-	/* step 1 */
-
-	tmp = cmd->start_src;
-	cmd->start_src &= TRIG_NOW;
-	if (!cmd->start_src || tmp != cmd->start_src)
-		err++;
-
-	tmp = cmd->scan_begin_src;
-	cmd->scan_begin_src &= TRIG_EXT;
-	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
-		err++;
 
-	tmp = cmd->convert_src;
-	cmd->convert_src &= TRIG_FOLLOW;
-	if (!cmd->convert_src || tmp != cmd->convert_src)
-		err++;
+	/* Step 1 : check if triggers are trivially valid */
 
-	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;
-	cmd->stop_src &= TRIG_NONE;
-	if (!cmd->stop_src || tmp != cmd->stop_src)
-		err++;
+	err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+	err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+	err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+	err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+	err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
 
 	if (err)
 		return 1;
 
-	/* step 2 */
+	/* Step 2a : make sure trigger sources are unique */
+	/* Step 2b : and mutually compatible */
 
 	if (err)
 		return 2;
@@ -403,7 +386,7 @@ static int dev_8255_attach(struct comedi_device *dev,
 		return ret;
 
 	for (i = 0; i < dev->n_subdevices; i++) {
-		s = dev->subdevices + i;
+		s = &dev->subdevices[i];
 		iobase = it->options[i];
 
 		if (!request_region(iobase, _8255_SIZE, "8255")) {
@@ -429,7 +412,7 @@ static void dev_8255_detach(struct comedi_device *dev)
 	int i;
 
 	for (i = 0; i < dev->n_subdevices; i++) {
-		s = dev->subdevices + i;
+		s = &dev->subdevices[i];
 		if (s->type != COMEDI_SUBD_UNUSED) {
 			spriv = s->private;
 			release_region(spriv->iobase, _8255_SIZE);

+ 353 - 0
drivers/staging/comedi/drivers/8255_pci.c

@@ -0,0 +1,353 @@
+/*
+ * COMEDI driver for generic PCI based 8255 digital i/o boards
+ * Copyright (C) 2012 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on the tested adl_pci7296 driver written by:
+ *	Jon Grierson <jd@renko.co.uk>
+ * and the experimental cb_pcidio driver written by:
+ *	Yoshiya Matsuzaka
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * 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: 8255_pci
+Description: Generic PCI based 8255 Digital I/O boards
+Devices: (ADLink) PCI-7224 [adl_pci-7224] - 24 channels
+	 (ADLink) PCI-7248 [adl_pci-7248] - 48 channels
+	 (ADLink) PCI-7296 [adl_pci-7296] - 96 channels
+	 (Measurement Computing) PCI-DIO24 [cb_pci-dio24] - 24 channels
+	 (Measurement Computing) PCI-DIO24H [cb_pci-dio24h] - 24 channels
+	 (Measurement Computing) PCI-DIO48H [cb_pci-dio48h] - 48 channels
+	 (Measurement Computing) PCI-DIO96H [cb_pci-dio96h] - 96 channels
+	 (National Instruments) PCI-DIO-96 [ni_pci-dio-96] - 96 channels
+	 (National Instruments) PCI-DIO-96B [ni_pci-dio-96b] - 96 channels
+	 (National Instruments) PXI-6508 [ni_pxi-6508] - 96 channels
+	 (National Instruments) PCI-6503 [ni_pci-6503] - 24 channels
+	 (National Instruments) PCI-6503B [ni_pci-6503b] - 24 channels
+	 (National Instruments) PCI-6503X [ni_pci-6503x] - 24 channels
+	 (National Instruments) PXI-6503 [ni_pxi-6503] - 24 channels
+Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+Updated: Wed, 12 Sep 2012 11:52:01 -0700
+Status: untested
+
+Some of these boards also have an 8254 programmable timer/counter
+chip. This chip is not currently supported by this driver.
+
+Interrupt support for these boards is also not currently supported.
+
+Configuration Options: not applicable, uses PCI auto config
+*/
+
+#include "../comedidev.h"
+
+#include "8255.h"
+
+/*
+ * PCI Device ID's supported by this driver
+ */
+#define PCI_DEVICE_ID_ADLINK_PCI7224	0x7224
+#define PCI_DEVICE_ID_ADLINK_PCI7248	0x7248
+#define PCI_DEVICE_ID_ADLINK_PCI7296	0x7296
+
+/* ComputerBoards is now known as Measurement Computing */
+#define PCI_VENDOR_ID_CB		0x1307
+
+#define PCI_DEVICE_ID_CB_PCIDIO48H	0x000b
+#define PCI_DEVICE_ID_CB_PCIDIO24H	0x0014
+#define PCI_DEVICE_ID_CB_PCIDIO96H	0x0017
+#define PCI_DEVICE_ID_CB_PCIDIO24	0x0028
+
+#define PCI_DEVICE_ID_NI_PCIDIO96	0x0160
+#define PCI_DEVICE_ID_NI_PCI6503	0x0400
+#define PCI_DEVICE_ID_NI_PCI6503B	0x1250
+#define PCI_DEVICE_ID_NI_PXI6508	0x13c0
+#define PCI_DEVICE_ID_NI_PCIDIO96B	0x1630
+#define PCI_DEVICE_ID_NI_PCI6503X	0x17d0
+#define PCI_DEVICE_ID_NI_PXI_6503	0x1800
+
+struct pci_8255_boardinfo {
+	const char *name;
+	unsigned short vendor;
+	unsigned short device;
+	int dio_badr;
+	int is_mmio;
+	int n_8255;
+};
+
+static const struct pci_8255_boardinfo pci_8255_boards[] = {
+	{
+		.name		= "adl_pci-7224",
+		.vendor		= PCI_VENDOR_ID_ADLINK,
+		.device		= PCI_DEVICE_ID_ADLINK_PCI7224,
+		.dio_badr	= 2,
+		.n_8255		= 1,
+	}, {
+		.name		= "adl_pci-7248",
+		.vendor		= PCI_VENDOR_ID_ADLINK,
+		.device		= PCI_DEVICE_ID_ADLINK_PCI7248,
+		.dio_badr	= 2,
+		.n_8255		= 2,
+	}, {
+		.name		= "adl_pci-7296",
+		.vendor		= PCI_VENDOR_ID_ADLINK,
+		.device		= PCI_DEVICE_ID_ADLINK_PCI7296,
+		.dio_badr	= 2,
+		.n_8255		= 4,
+	}, {
+		.name		= "cb_pci-dio24",
+		.vendor		= PCI_VENDOR_ID_CB,
+		.device		= PCI_DEVICE_ID_CB_PCIDIO24,
+		.dio_badr	= 2,
+		.n_8255		= 1,
+	}, {
+		.name		= "cb_pci-dio24h",
+		.vendor		= PCI_VENDOR_ID_CB,
+		.device		= PCI_DEVICE_ID_CB_PCIDIO24H,
+		.dio_badr	= 2,
+		.n_8255		= 1,
+	}, {
+		.name		= "cb_pci-dio48h",
+		.vendor		= PCI_VENDOR_ID_CB,
+		.device		= PCI_DEVICE_ID_CB_PCIDIO48H,
+		.dio_badr	= 1,
+		.n_8255		= 2,
+	}, {
+		.name		= "cb_pci-dio96h",
+		.vendor		= PCI_VENDOR_ID_CB,
+		.device		= PCI_DEVICE_ID_CB_PCIDIO96H,
+		.dio_badr	= 2,
+		.n_8255		= 4,
+	}, {
+		.name		= "ni_pci-dio-96",
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PCIDIO96,
+		.dio_badr	= 1,
+		.is_mmio	= 1,
+		.n_8255		= 4,
+	}, {
+		.name		= "ni_pci-dio-96b",
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PCIDIO96B,
+		.dio_badr	= 1,
+		.is_mmio	= 1,
+		.n_8255		= 4,
+	}, {
+		.name		= "ni_pxi-6508",
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PXI6508,
+		.dio_badr	= 1,
+		.is_mmio	= 1,
+		.n_8255		= 4,
+	}, {
+		.name		= "ni_pci-6503",
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PCI6503,
+		.dio_badr	= 1,
+		.is_mmio	= 1,
+		.n_8255		= 1,
+	}, {
+		.name		= "ni_pci-6503b",
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PCI6503B,
+		.dio_badr	= 1,
+		.is_mmio	= 1,
+		.n_8255		= 1,
+	}, {
+		.name		= "ni_pci-6503x",
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PCI6503X,
+		.dio_badr	= 1,
+		.is_mmio	= 1,
+		.n_8255		= 1,
+	}, {
+		.name		= "ni_pxi-6503",
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PXI_6503,
+		.dio_badr	= 1,
+		.is_mmio	= 1,
+		.n_8255		= 1,
+	},
+};
+
+struct pci_8255_private {
+	void __iomem *mmio_base;
+};
+
+static int pci_8255_mmio(int dir, int port, int data, unsigned long iobase)
+{
+	void __iomem *mmio_base = (void __iomem *)iobase;
+
+	if (dir) {
+		writeb(data, mmio_base + port);
+		return 0;
+	} else {
+		return readb(mmio_base  + port);
+	}
+}
+
+static const void *pci_8255_find_boardinfo(struct comedi_device *dev,
+					      struct pci_dev *pcidev)
+{
+	const struct pci_8255_boardinfo *board;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pci_8255_boards); i++) {
+		board = &pci_8255_boards[i];
+		if (pcidev->vendor == board->vendor &&
+		    pcidev->device == board->device)
+			return board;
+	}
+	return NULL;
+}
+
+static int pci_8255_attach_pci(struct comedi_device *dev,
+			       struct pci_dev *pcidev)
+{
+	const struct pci_8255_boardinfo *board;
+	struct pci_8255_private *devpriv;
+	struct comedi_subdevice *s;
+	resource_size_t iobase;
+	unsigned long len;
+	int ret;
+	int i;
+
+	comedi_set_hw_dev(dev, &pcidev->dev);
+
+	board = pci_8255_find_boardinfo(dev, pcidev);
+	if (!board)
+		return -ENODEV;
+	dev->board_ptr = board;
+	dev->board_name = board->name;
+
+	ret = alloc_private(dev, sizeof(*devpriv));
+	if (ret < 0)
+		return ret;
+	devpriv = dev->private;
+
+	ret = comedi_pci_enable(pcidev, dev->board_name);
+	if (ret)
+		return ret;
+	iobase = pci_resource_start(pcidev, board->dio_badr);
+	len = pci_resource_len(pcidev, board->dio_badr);
+
+	if (board->is_mmio) {
+		devpriv->mmio_base = ioremap(iobase, len);
+		if (!devpriv->mmio_base)
+			return -ENOMEM;
+	}
+	dev->iobase = iobase;
+
+	/*
+	 * One, two, or four subdevices are setup by this driver depending
+	 * on the number of channels provided by the board. Each subdevice
+	 * has 24 channels supported by the 8255 module.
+	 */
+	ret = comedi_alloc_subdevices(dev, board->n_8255);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < board->n_8255; i++) {
+		s = &dev->subdevices[i];
+		if (board->is_mmio) {
+			iobase = (unsigned long)(devpriv->mmio_base + (i * 4));
+			ret = subdev_8255_init(dev, s, pci_8255_mmio, iobase);
+		} else {
+			iobase = dev->iobase + (i * 4);
+			ret = subdev_8255_init(dev, s, NULL, iobase);
+		}
+		if (ret)
+			return ret;
+	}
+
+	dev_info(dev->class_dev, "%s attached (%d digital i/o channels)\n",
+		dev->board_name, board->n_8255 * 24);
+
+	return 0;
+}
+
+static void pci_8255_detach(struct comedi_device *dev)
+{
+	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+	const struct pci_8255_boardinfo *board = comedi_board(dev);
+	struct pci_8255_private *devpriv = dev->private;
+	struct comedi_subdevice *s;
+	int i;
+
+	if (dev->subdevices) {
+		for (i = 0; i < board->n_8255; i++) {
+			s = &dev->subdevices[i];
+			subdev_8255_cleanup(dev, s);
+		}
+	}
+	if (pcidev) {
+		if (devpriv->mmio_base)
+			iounmap(devpriv->mmio_base);
+		if (dev->iobase)
+			comedi_pci_disable(pcidev);
+	}
+}
+
+static struct comedi_driver pci_8255_driver = {
+	.driver_name	= "8255_pci",
+	.module		= THIS_MODULE,
+	.attach_pci	= pci_8255_attach_pci,
+	.detach		= pci_8255_detach,
+};
+
+static int __devinit pci_8255_pci_probe(struct pci_dev *dev,
+					const struct pci_device_id *ent)
+{
+	return comedi_pci_auto_config(dev, &pci_8255_driver);
+}
+
+static void __devexit pci_8255_pci_remove(struct pci_dev *dev)
+{
+	comedi_pci_auto_unconfig(dev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(pci_8255_pci_table) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_ADLINK_PCI7224) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_ADLINK_PCI7248) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_ADLINK_PCI7296) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_CB_PCIDIO24) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_CB_PCIDIO24H) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_CB_PCIDIO48H) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_CB_PCIDIO96H) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCIDIO96) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCIDIO96B) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI6508) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI6503) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI6503B) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI6503X) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI_6503) },
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(pci, pci_8255_pci_table);
+
+static struct pci_driver pci_8255_pci_driver = {
+	.name		= "8255_pci",
+	.id_table	= pci_8255_pci_table,
+	.probe		= pci_8255_pci_probe,
+	.remove		= __devexit_p(pci_8255_pci_remove),
+};
+module_comedi_pci_driver(pci_8255_driver, pci_8255_pci_driver);
+
+MODULE_DESCRIPTION("COMEDI - Generic PCI based 8255 Digital I/O boards");
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_LICENSE("GPL");

+ 2 - 4
drivers/staging/comedi/drivers/Makefile

@@ -55,6 +55,7 @@ obj-$(CONFIG_COMEDI_MULTIQ3)		+= multiq3.o
 obj-$(CONFIG_COMEDI_POC)		+= poc.o
 
 # Comedi PCI drivers
+obj-$(CONFIG_COMEDI_8255_PCI)		+= 8255_pci.o
 obj-$(CONFIG_COMEDI_ADDI_APCI_035)	+= addi_apci_035.o
 obj-$(CONFIG_COMEDI_ADDI_APCI_1032)	+= addi_apci_1032.o
 obj-$(CONFIG_COMEDI_ADDI_APCI_1500)	+= addi_apci_1500.o
@@ -69,9 +70,7 @@ obj-$(CONFIG_COMEDI_ADDI_APCI_3120)	+= addi_apci_3120.o
 obj-$(CONFIG_COMEDI_ADDI_APCI_3501)	+= addi_apci_3501.o
 obj-$(CONFIG_COMEDI_ADDI_APCI_3XXX)	+= addi_apci_3xxx.o
 obj-$(CONFIG_COMEDI_ADL_PCI6208)	+= adl_pci6208.o
-obj-$(CONFIG_COMEDI_ADL_PCI7230)	+= adl_pci7230.o
-obj-$(CONFIG_COMEDI_ADL_PCI7296)	+= adl_pci7296.o
-obj-$(CONFIG_COMEDI_ADL_PCI7432)	+= adl_pci7432.o
+obj-$(CONFIG_COMEDI_ADL_PCI7X3X)	+= adl_pci7x3x.o
 obj-$(CONFIG_COMEDI_ADL_PCI8164)	+= adl_pci8164.o
 obj-$(CONFIG_COMEDI_ADL_PCI9111)	+= adl_pci9111.o
 obj-$(CONFIG_COMEDI_ADL_PCI9118)	+= adl_pci9118.o
@@ -96,7 +95,6 @@ obj-$(CONFIG_COMEDI_KE_COUNTER)		+= ke_counter.o
 obj-$(CONFIG_COMEDI_CB_PCIDAS64)	+= cb_pcidas64.o
 obj-$(CONFIG_COMEDI_CB_PCIDAS)		+= cb_pcidas.o
 obj-$(CONFIG_COMEDI_CB_PCIDDA)		+= cb_pcidda.o
-obj-$(CONFIG_COMEDI_CB_PCIDIO)		+= cb_pcidio.o
 obj-$(CONFIG_COMEDI_CB_PCIMDAS)		+= cb_pcimdas.o
 obj-$(CONFIG_COMEDI_CB_PCIMDDA)		+= cb_pcimdda.o
 obj-$(CONFIG_COMEDI_ME4000)		+= me4000.o

+ 3 - 3
drivers/staging/comedi/drivers/acl7225b.c

@@ -81,7 +81,7 @@ static int acl7225b_attach(struct comedi_device *dev,
 	if (ret)
 		return ret;
 
-	s = dev->subdevices + 0;
+	s = &dev->subdevices[0];
 	/* Relays outputs */
 	s->type = COMEDI_SUBD_DO;
 	s->subdev_flags = SDF_WRITABLE;
@@ -91,7 +91,7 @@ static int acl7225b_attach(struct comedi_device *dev,
 	s->range_table = &range_digital;
 	s->private = (void *)ACL7225_RIO_LO;
 
-	s = dev->subdevices + 1;
+	s = &dev->subdevices[1];
 	/* Relays status */
 	s->type = COMEDI_SUBD_DI;
 	s->subdev_flags = SDF_READABLE;
@@ -101,7 +101,7 @@ static int acl7225b_attach(struct comedi_device *dev,
 	s->range_table = &range_digital;
 	s->private = (void *)ACL7225_RIO_LO;
 
-	s = dev->subdevices + 2;
+	s = &dev->subdevices[2];
 	/* Isolated digital inputs */
 	s->type = COMEDI_SUBD_DI;
 	s->subdev_flags = SDF_READABLE;

+ 0 - 195
drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.c

@@ -1,195 +0,0 @@
-/**
-@verbatim
-
-Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
-
-	ADDI-DATA GmbH
-	Dieselstrasse 3
-	D-77833 Ottersweier
-	Tel: +19(0)7223/9493-0
-	Fax: +49(0)7223/9493-92
-	http://www.addi-data.com
-	info@addi-data.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-You should also find the complete GPL in the COPYING file accompanying this source code.
-
-@endverbatim
-*/
-/*
-  +-----------------------------------------------------------------------+
-  | (C) ADDI-DATA GmbH          Dieselstraße 3       D-77833 Ottersweier  |
-  +-----------------------------------------------------------------------+
-  | Tel : +49 (0) 7223/9493-0     | email    : info@addi-data.com         |
-  | Fax : +49 (0) 7223/9493-92    | Internet : http://www.addi-data.com   |
-  +-------------------------------+---------------------------------------+
-  | Project : ADDI HEADER READ WRITER |     Compiler   : Visual C++       |
-  | Module name : S5920.cpp           |     Version    : 6.0              |
-  +-------------------------------+---------------------------------------+
-  | Author : E. LIBS                      Date : 02/05/2002               |
-  +-----------------------------------------------------------------------+
-  | Description   : DLL with the S5920 PCI Controller functions           |
-  +-----------------------------------------------------------------------+
-  |                             UPDATE'S                                  |
-  +-----------------------------------------------------------------------+
-  |   Date   |   Author  |          Description of updates                |
-  +----------+-----------+------------------------------------------------+
-  | 28/08/02 | LIBS Eric | Add return codes each time a function of the   |
-  |          |           | Addi Library is called                         |
-  +-----------------------------------------------------------------------+
-  | 31/07/03 | KRAUTH J. | Changes for the MSX-Box                        |
-  +-----------------------------------------------------------------------+
-*/
-
-#include "addi_amcc_S5920.h"
-
-/*+----------------------------------------------------------------------------+*/
-/*| Function   Name   : int i_AddiHeaderRW_ReadEeprom                          |*/
-/*|                               (int    i_NbOfWordsToRead,                   |*/
-/*|                                unsigned int dw_PCIBoardEepromAddress,             |*/
-/*|                                unsigned short   w_EepromStartAddress,                |*/
-/*|                                unsigned short * pw_DataRead)                          |*/
-/*+----------------------------------------------------------------------------+*/
-/*| Task              : Read word from the 5920 eeprom.                        |*/
-/*+----------------------------------------------------------------------------+*/
-/*| Input Parameters  : int    i_NbOfWordsToRead : Nbr. of word to read        |*/
-/*|                     unsigned int dw_PCIBoardEepromAddress : Address of the eeprom |*/
-/*|                     unsigned short   w_EepromStartAddress : Eeprom start address     |*/
-/*+----------------------------------------------------------------------------+*/
-/*| Output Parameters : unsigned short * pw_DataRead : Read data                          |*/
-/*+----------------------------------------------------------------------------+*/
-/*| Return Value      : -                                                      |*/
-/*+----------------------------------------------------------------------------+*/
-
-int i_AddiHeaderRW_ReadEeprom(int i_NbOfWordsToRead,
-	unsigned int dw_PCIBoardEepromAddress,
-	unsigned short w_EepromStartAddress, unsigned short *pw_DataRead)
-{
-	unsigned int dw_eeprom_busy = 0;
-	int i_Counter = 0;
-	int i_WordCounter;
-	int i;
-	unsigned char pb_ReadByte[1];
-	unsigned char b_ReadLowByte = 0;
-	unsigned char b_ReadHighByte = 0;
-	unsigned char b_SelectedAddressLow = 0;
-	unsigned char b_SelectedAddressHigh = 0;
-	unsigned short w_ReadWord = 0;
-
-	for (i_WordCounter = 0; i_WordCounter < i_NbOfWordsToRead;
-		i_WordCounter++) {
-		do {
-			dw_eeprom_busy =
-				inl(dw_PCIBoardEepromAddress +
-				AMCC_OP_REG_MCSR);
-			dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
-		} while (dw_eeprom_busy == EEPROM_BUSY);
-
-		for (i_Counter = 0; i_Counter < 2; i_Counter++) {
-			b_SelectedAddressLow = (w_EepromStartAddress + i_Counter) % 256;	/* Read the low 8 bit part */
-			b_SelectedAddressHigh = (w_EepromStartAddress + i_Counter) / 256;	/* Read the high 8 bit part */
-
-			/* Select the load low address mode */
-			outb(NVCMD_LOAD_LOW,
-				dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
-				3);
-
-			/* Wait on busy */
-			do {
-				dw_eeprom_busy =
-					inl(dw_PCIBoardEepromAddress +
-					AMCC_OP_REG_MCSR);
-				dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
-			} while (dw_eeprom_busy == EEPROM_BUSY);
-
-			/* Load the low address */
-			outb(b_SelectedAddressLow,
-				dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
-				2);
-
-			/* Wait on busy */
-			do {
-				dw_eeprom_busy =
-					inl(dw_PCIBoardEepromAddress +
-					AMCC_OP_REG_MCSR);
-				dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
-			} while (dw_eeprom_busy == EEPROM_BUSY);
-
-			/* Select the load high address mode */
-			outb(NVCMD_LOAD_HIGH,
-				dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
-				3);
-
-			/* Wait on busy */
-			do {
-				dw_eeprom_busy =
-					inl(dw_PCIBoardEepromAddress +
-					AMCC_OP_REG_MCSR);
-				dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
-			} while (dw_eeprom_busy == EEPROM_BUSY);
-
-			/* Load the high address */
-			outb(b_SelectedAddressHigh,
-				dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
-				2);
-
-			/* Wait on busy */
-			do {
-				dw_eeprom_busy =
-					inl(dw_PCIBoardEepromAddress +
-					AMCC_OP_REG_MCSR);
-				dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
-			} while (dw_eeprom_busy == EEPROM_BUSY);
-
-			/* Select the READ mode */
-			outb(NVCMD_BEGIN_READ,
-				dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
-				3);
-
-			/* Wait on busy */
-			do {
-				dw_eeprom_busy =
-					inl(dw_PCIBoardEepromAddress +
-					AMCC_OP_REG_MCSR);
-				dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
-			} while (dw_eeprom_busy == EEPROM_BUSY);
-
-			/* Read data into the EEPROM */
-			*pb_ReadByte =
-				inb(dw_PCIBoardEepromAddress +
-				AMCC_OP_REG_MCSR + 2);
-
-			/* Wait on busy */
-			do {
-				dw_eeprom_busy =
-					inl(dw_PCIBoardEepromAddress +
-					AMCC_OP_REG_MCSR);
-				dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
-			} while (dw_eeprom_busy == EEPROM_BUSY);
-
-			/* Select the upper address part */
-			if (i_Counter == 0)
-				b_ReadLowByte = pb_ReadByte[0];
-			else
-				b_ReadHighByte = pb_ReadByte[0];
-
-			/* Sleep */
-			msleep(1);
-
-		}
-		w_ReadWord =
-			(b_ReadLowByte | (((unsigned short)b_ReadHighByte) *
-				256));
-
-		pw_DataRead[i_WordCounter] = w_ReadWord;
-
-		w_EepromStartAddress += 2;	/*  to read the next word */
-
-	}			/*  for (...) i_NbOfWordsToRead */
-	return 0;
-}

+ 0 - 27
drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.h

@@ -1,27 +0,0 @@
-/*
- *  Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
- *
- *	ADDI-DATA GmbH
- *	Dieselstrasse 3
- *	D-77833 Ottersweier
- *	Tel: +19(0)7223/9493-0
- *	Fax: +49(0)7223/9493-92
- *	http://www.addi-data.com
- *	info@addi-data.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.
- */
-
-#define AMCC_OP_REG_MCSR	0x3c
-#define EEPROM_BUSY		0x80000000
-#define NVCMD_LOAD_LOW		(0x4 << 5)	/* nvRam load low command */
-#define NVCMD_LOAD_HIGH		(0x5 << 5)	/* nvRam load high command */
-#define NVCMD_BEGIN_READ	(0x7 << 5)	/* nvRam begin read command */
-#define NVCMD_BEGIN_WRITE	(0x6 << 5)	/* EEPROM begin write command */
-
-int i_AddiHeaderRW_ReadEeprom(int i_NbOfWordsToRead,
-			      unsigned int dw_PCIBoardEepromAddress,
-			      unsigned short w_EepromStartAddress, unsigned short *pw_DataRead);

+ 7 - 7
drivers/staging/comedi/drivers/addi-data/addi_common.c

@@ -1669,7 +1669,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
 			return ret;
 
 		/*  Allocate and Initialise AI Subdevice Structures */
-		s = dev->subdevices + 0;
+		s = &dev->subdevices[0];
 		if ((devpriv->s_EeParameters.i_NbrAiChannel)
 			|| (this_board->i_NbrAiChannelDiff)) {
 			dev->read_subdev = s;
@@ -1705,7 +1705,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
 		}
 
 		/*  Allocate and Initialise AO Subdevice Structures */
-		s = dev->subdevices + 1;
+		s = &dev->subdevices[1];
 		if (devpriv->s_EeParameters.i_NbrAoChannel) {
 			s->type = COMEDI_SUBD_AO;
 			s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
@@ -1720,7 +1720,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
 			s->type = COMEDI_SUBD_UNUSED;
 		}
 		/*  Allocate and Initialise DI Subdevice Structures */
-		s = dev->subdevices + 2;
+		s = &dev->subdevices[2];
 		if (devpriv->s_EeParameters.i_NbrDiChannel) {
 			s->type = COMEDI_SUBD_DI;
 			s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
@@ -1738,7 +1738,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
 			s->type = COMEDI_SUBD_UNUSED;
 		}
 		/*  Allocate and Initialise DO Subdevice Structures */
-		s = dev->subdevices + 3;
+		s = &dev->subdevices[3];
 		if (devpriv->s_EeParameters.i_NbrDoChannel) {
 			s->type = COMEDI_SUBD_DO;
 			s->subdev_flags =
@@ -1760,7 +1760,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
 		}
 
 		/*  Allocate and Initialise Timer Subdevice Structures */
-		s = dev->subdevices + 4;
+		s = &dev->subdevices[4];
 		if (devpriv->s_EeParameters.i_Timer) {
 			s->type = COMEDI_SUBD_TIMER;
 			s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
@@ -1778,7 +1778,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
 		}
 
 		/*  Allocate and Initialise TTL */
-		s = dev->subdevices + 5;
+		s = &dev->subdevices[5];
 		if (this_board->i_NbrTTLChannel) {
 			s->type = COMEDI_SUBD_TTLIO;
 			s->subdev_flags =
@@ -1797,7 +1797,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
 		}
 
 		/* EEPROM */
-		s = dev->subdevices + 6;
+		s = &dev->subdevices[6];
 		if (this_board->i_PCIEeprom) {
 			s->type = COMEDI_SUBD_MEMORY;
 			s->subdev_flags = SDF_READABLE | SDF_INTERNAL;

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно