Re: JR3 force sensor driver

Ian Abbott wrote:
> Hello Anders,
> 
> On 29/06/07 18:19, you wrote:
>> Frank Mori Hess wrote:
>>> On Friday 15 June 2007 12:54, Anders Blomdell wrote:
>>>> Still work to do, but this version seems to be fully working
>>> Hi Anders,
>>>
>>> Thanks for the driver.  The binary firmware shouldn't be compiled into the 
>>> driver though.  The firmware should go in a subdirectory of the 
>>> comedi_nonfree_firmware tarball (we can probably add a directory for it to 
>>> cvs if that would help) and be loaded to the driver from userspace.  You 
>>> can either use the support for loading firmware in comedi_config, or 
>>> figure out how to use the kernel's generic firmware loading support (no 
>>> drivers do this yet).
>> OK,
>>
>> Updated patch with support for firmware upload (place jr3pci.idm in
>> /lib/firmware/comedi).
> 
> I noticed your driver accesses I/O memory without using the proper
> access functions.  Also I don't think your get_s16, get_u16, set_s16 and
> set_u16 functions will work on bigendian machines because, for example,
> the 'as_u16' member of the 'u_val_t' union type would overlay the
> most-significant half of the 'as_u32' member on bigendian machines
> instead of the least-significant half.
> 
> Could you try the attached patch against the current CVS?  Note that the
> 'readl' and 'writel' calls will take care of the endian conversions
> required for bigendian machines.

Slightly modified patch attached (got rid of bitfields).


-- 
Anders Blomdell                  Email: anders.blomdell_at_control.lth.se
Department of Automatic Control
Lund University                  Phone:    +46 46 222 4625
P.O. Box 118                     Fax:      +46 46 138118
SE-221 00 Lund, Sweden
Index: comedi/drivers/jr3_pci.c
===================================================================
RCS file: /cvs/comedi/comedi/comedi/drivers/jr3_pci.c,v
retrieving revision 1.1
diff -u -r1.1 jr3_pci.c
--- comedi/drivers/jr3_pci.c	3 Jul 2007 02:44:22 -0000	1.1
+++ comedi/drivers/jr3_pci.c	5 Jul 2007 14:37:56 -0000
_at__at_ -175,7 +175,7 _at__at_
 
 static int is_complete(volatile jr3_channel_t *channel)
 {
-  return get_s16(channel->command_word0) == 0;
+  return get_s16(&channel->command_word0) == 0;
 }
  
 typedef struct {
_at__at_ -253,24 +253,24 _at__at_
 static six_axis_t get_min_full_scales(volatile jr3_channel_t *channel)
 {
   six_axis_t result;
-  result.fx = get_s16(channel->min_full_scale.fx);
-  result.fy = get_s16(channel->min_full_scale.fy);
-  result.fz = get_s16(channel->min_full_scale.fz);
-  result.mx = get_s16(channel->min_full_scale.mx);
-  result.my = get_s16(channel->min_full_scale.my);
-  result.mz = get_s16(channel->min_full_scale.mz);
+  result.fx = get_s16(&channel->min_full_scale.fx);
+  result.fy = get_s16(&channel->min_full_scale.fy);
+  result.fz = get_s16(&channel->min_full_scale.fz);
+  result.mx = get_s16(&channel->min_full_scale.mx);
+  result.my = get_s16(&channel->min_full_scale.my);
+  result.mz = get_s16(&channel->min_full_scale.mz);
   return result;
 }
  
 static six_axis_t get_max_full_scales(volatile jr3_channel_t *channel)
 {
   six_axis_t result;
-  result.fx = get_s16(channel->max_full_scale.fx);
-  result.fy = get_s16(channel->max_full_scale.fy);
-  result.fz = get_s16(channel->max_full_scale.fz);
-  result.mx = get_s16(channel->max_full_scale.mx);
-  result.my = get_s16(channel->max_full_scale.my);
-  result.mz = get_s16(channel->max_full_scale.mz);
+  result.fx = get_s16(&channel->max_full_scale.fx);
+  result.fy = get_s16(&channel->max_full_scale.fy);
+  result.fz = get_s16(&channel->max_full_scale.fz);
+  result.mx = get_s16(&channel->max_full_scale.mx);
+  result.my = get_s16(&channel->max_full_scale.my);
+  result.mz = get_s16(&channel->max_full_scale.mz);
   return result;
 }
  
_at__at_ -290,7 +290,8 _at__at_
 
     result = insn->n;
     if (p->state != state_jr3_done || 
-	(get_u16(p->channel->errors) & 0xe800) != 0) {
+	(get_u16(&p->channel->errors) & (watch_dog | watch_dog2 | 
+					 sensor_change))) {
       /* No sensor or sensor changed */
       if (p->state == state_jr3_done) {
 	/* Restart polling */
_at__at_ -310,28 +311,28 _at__at_
 	  int F = 0;
 	  switch (axis) {
 	    case 0: {
-	      F = get_s16(p->channel->filter[filter].fx);
+	      F = get_s16(&p->channel->filter[filter].fx);
 	    } break;
 	    case 1: {
-	      F = get_s16(p->channel->filter[filter].fy);
+	      F = get_s16(&p->channel->filter[filter].fy);
 	    } break;
 	    case 2: {
-	      F = get_s16(p->channel->filter[filter].fz);
+	      F = get_s16(&p->channel->filter[filter].fz);
 	    } break;
 	    case 3: {
-	      F = get_s16(p->channel->filter[filter].mx);
+	      F = get_s16(&p->channel->filter[filter].mx);
 	    } break;
 	    case 4: {
-	      F = get_s16(p->channel->filter[filter].my);
+	      F = get_s16(&p->channel->filter[filter].my);
 	    } break;
 	    case 5: {
-	      F = get_s16(p->channel->filter[filter].mz);
+	      F = get_s16(&p->channel->filter[filter].mz);
 	    } break;
 	    case 6: {
-	      F = get_s16(p->channel->filter[filter].v1);
+	      F = get_s16(&p->channel->filter[filter].v1);
 	    } break;
 	    case 7: {
-	      F = get_s16(p->channel->filter[filter].v2);
+	      F = get_s16(&p->channel->filter[filter].v2);
 	    } break;
 	  }
 	  data[i] = F + 0x4000;
_at__at_ -340,13 +341,13 _at__at_
 	if (p->state != state_jr3_done) {
 	  data[i] = 0;
 	} else {
-	  data[i] = get_u16(p->channel->model_no);
+	  data[i] = get_u16(&p->channel->model_no);
 	}
       } else if (channel == 57) {
 	if (p->state != state_jr3_done) {
 	  data[i] = 0;
 	} else {
-	  data[i] = get_u16(p->channel->serial_no);
+	  data[i] = get_u16(&p->channel->serial_no);
 	}
       }
     }
_at__at_ -480,22 +481,23 _at__at_
 
   if (p) {
     volatile jr3_channel_t *channel = p->channel;
-    int errors = get_u16(channel->errors);
+    int errors = get_u16(&channel->errors);
     
     if (errors != p->errors) {
       printk("Errors: %x -> %x\n", p->errors, errors);
       p->errors = errors;
     }
-    if ((errors & 0xe800) != 0) {
+    if (errors & (watch_dog | watch_dog2 | sensor_change)) {
       // Sensor communication lost, force poll mode
       p->state = state_jr3_poll;
       
     }
     switch (p->state) {
       case state_jr3_poll: {
-	u16 model_no = get_u16(channel->model_no);
-	u16 serial_no = get_u16(channel->serial_no);
-	if ((errors & 0xe000) != 0 || model_no == 0 || serial_no == 0) {
+	u16 model_no = get_u16(&channel->model_no);
+	u16 serial_no = get_u16(&channel->serial_no);
+	if ((errors & (watch_dog | watch_dog2)) || 
+	    model_no == 0 || serial_no == 0) {
 	  // Still no sensor, keep on polling. Since it takes up to 
 	  // 10 seconds for offsets to stabilize, polling each
 	  // second should suffice.
_at__at_ -514,8 +516,8 _at__at_
 	} else {
 	  transform_t transf;
   
-	  p->model_no = get_u16(channel->model_no);
-	  p->serial_no = get_u16(channel->serial_no);
+	  p->model_no = get_u16(&channel->model_no);
+	  p->serial_no = get_u16(&channel->serial_no);
 
 	  printk("Setting transform for channel %d\n", p->channel_no);
 	  printk("Sensor Model     = %i\n", p->model_no);
_at__at_ -584,22 +586,22 _at__at_
 
 	  // Use ranges in kN or we will overflow arount 2000N!
 	  full_scale = &channel->full_scale;
-	  p->range[0].range.min = -get_s16(full_scale->fx) * 1000;
-	  p->range[0].range.max =  get_s16(full_scale->fx) * 1000;
-	  p->range[1].range.min = -get_s16(full_scale->fy) * 1000;
-	  p->range[1].range.max =  get_s16(full_scale->fy) * 1000;
-	  p->range[2].range.min = -get_s16(full_scale->fz) * 1000;
-	  p->range[2].range.max =  get_s16(full_scale->fz) * 1000;
-	  p->range[3].range.min = -get_s16(full_scale->mx) * 100;
-	  p->range[3].range.max =  get_s16(full_scale->mx) * 100;
-	  p->range[4].range.min = -get_s16(full_scale->my) * 100;
-	  p->range[4].range.max =  get_s16(full_scale->my) * 100;
-	  p->range[5].range.min = -get_s16(full_scale->mz) * 100;
-	  p->range[5].range.max =  get_s16(full_scale->mz) * 100;
-	  p->range[6].range.min = -get_s16(full_scale->v1) * 100; // ??
-	  p->range[6].range.max =  get_s16(full_scale->v1) * 100; // ??
-	  p->range[7].range.min = -get_s16(full_scale->v2) * 100; // ??
-	  p->range[7].range.max =  get_s16(full_scale->v2) * 100; // ??
+	  p->range[0].range.min = -get_s16(&full_scale->fx) * 1000;
+	  p->range[0].range.max =  get_s16(&full_scale->fx) * 1000;
+	  p->range[1].range.min = -get_s16(&full_scale->fy) * 1000;
+	  p->range[1].range.max =  get_s16(&full_scale->fy) * 1000;
+	  p->range[2].range.min = -get_s16(&full_scale->fz) * 1000;
+	  p->range[2].range.max =  get_s16(&full_scale->fz) * 1000;
+	  p->range[3].range.min = -get_s16(&full_scale->mx) * 100;
+	  p->range[3].range.max =  get_s16(&full_scale->mx) * 100;
+	  p->range[4].range.min = -get_s16(&full_scale->my) * 100;
+	  p->range[4].range.max =  get_s16(&full_scale->my) * 100;
+	  p->range[5].range.min = -get_s16(&full_scale->mz) * 100;
+	  p->range[5].range.max =  get_s16(&full_scale->mz) * 100;
+	  p->range[6].range.min = -get_s16(&full_scale->v1) * 100; // ??
+	  p->range[6].range.max =  get_s16(&full_scale->v1) * 100; // ??
+	  p->range[7].range.min = -get_s16(&full_scale->v2) * 100; // ??
+	  p->range[7].range.max =  get_s16(&full_scale->v2) * 100; // ??
 	  p->range[8].range.min = 0;
 	  p->range[8].range.max = 65535;
 
_at__at_ -623,12 +625,12 _at__at_
 	  result = poll_delay_min_max(20, 100);
 	} else {
 	  printk("Default offsets %d %d %d %d %d %d\n",
-		 get_s16(channel->offsets.fx),
-		 get_s16(channel->offsets.fy),
-		 get_s16(channel->offsets.fz),
-		 get_s16(channel->offsets.mx),
-		 get_s16(channel->offsets.my),
-		 get_s16(channel->offsets.mz));
+		 get_s16(&channel->offsets.fx),
+		 get_s16(&channel->offsets.fy),
+		 get_s16(&channel->offsets.fz),
+		 get_s16(&channel->offsets.mx),
+		 get_s16(&channel->offsets.my),
+		 get_s16(&channel->offsets.mz));
 
 	  set_s16(&channel->offsets.fx, 0);
 	  set_s16(&channel->offsets.fy, 0);
_at__at_ -829,7 +831,7 _at__at_
   // as much as we can read firmware version
   msleep_interruptible(25);
   for (i=0 ; i < 0x18 ; i++){
-    printk("%c", get_u16(devpriv->iobase->channel[0].data.copyright[i])>>8);
+    printk("%c", get_u16(&devpriv->iobase->channel[0].data.copyright[i])>>8);
   }
 
   // Start card timer
Index: comedi/drivers/jr3_pci.h
===================================================================
RCS file: /cvs/comedi/comedi/comedi/drivers/jr3_pci.h,v
retrieving revision 1.1
diff -u -r1.1 jr3_pci.h
--- comedi/drivers/jr3_pci.h	3 Jul 2007 02:44:22 -0000	1.1
+++ comedi/drivers/jr3_pci.h	5 Jul 2007 14:37:56 -0000
_at__at_ -1,43 +1,27 _at__at_
 // Helper types to take care of the fact that the DSP card memory
 //   is 16 bits, but aligned on a 32 bit PCI boundary
-typedef union {
-  u8 as_u8;
-  u16 as_u16;
-  u32 as_u32;
-} u_val_t;
-
-typedef union {
-  s8 as_s8;
-  s16 as_s16;
-  s32 as_s32;
-} s_val_t;
+typedef u32 u_val_t;
 
-static inline u16 get_u16(volatile u_val_t val) 
+typedef s32 s_val_t;
+
+static inline u16 get_u16(volatile const u_val_t *p) 
 {
-  u_val_t tmp;
-  tmp.as_u32 = le32_to_cpu(val.as_u32);
-  return tmp.as_u16;
+  return (u16)readl(p);
 } 
 
 static inline void set_u16(volatile u_val_t *p, u16 val) 
 {
-  u_val_t tmp;
-  tmp.as_u16 = val;
-  p->as_u32 = cpu_to_le32(tmp.as_u32);
+  writel(val, p);
 } 
 
-static inline s16 get_s16(volatile s_val_t val) 
+static inline s16 get_s16(volatile const s_val_t *p) 
 {
-  s_val_t tmp;
-  tmp.as_s32 = le32_to_cpu(val.as_s32);
-  return tmp.as_s16;
+  return (s16)readl(p);
 } 
 
 static inline void set_s16(volatile s_val_t *p, s16 val) 
 {
-  s_val_t tmp;
-  tmp.as_s16 = val;
-  p->as_s32 = cpu_to_le32(tmp.as_s32);
+  writel(val, p);
 } 
 
 // The raw data is stored in a format which facilitates rapid
_at__at_ -90,75 +74,116 _at__at_
   s_val_t mz;
 } six_axis_array_t;
 
-// VECT_BITS
-// Indicates which axis are to be used when computing the vectors. A vector
-// is composed by 3 components and its "magnitude" is placed in V1 and V2.
-// V1 defaults to a force vector and V2 defaults to a moment vector.
-// Setting changeV1 or changeV2 will change that vector to be the opposite of
-// its default.
-
-// *** Check this norby ***
-// This is badly defined at JR3 Manual. Correct definition follows:
-typedef struct vect_bits
-{
- unsigned fx : 1;
- unsigned fy : 1;
- unsigned fz : 1;
- unsigned mx : 1;
- unsigned my : 1;
- unsigned mz : 1;
- unsigned changeV1 : 1;
- unsigned changeV2 : 1;
- unsigned reserved : 8;
-} vect_bits;
-
-// WARNINGS
-// Bit pattern for the warning word: xx_near_sat means that a near saturation
-// has been reached or exceeded.
-typedef struct warning_bits
-{
- unsigned fx_near_sat : 1;
- unsigned fy_near_sat : 1;
- unsigned fz_near_sat : 1;
- unsigned mx_near_sat : 1;
- unsigned my_near_sat : 1;
- unsigned mz_near_sat : 1;
- unsigned reserved : 10;
-} warning_bits;
+// VECT_BITS 
+// The vect_bits structure shows the layout for indicating
+// which axes to use in computing the vectors. Each bit signifies
+// selection of a single axis. The V1x axis bit corresponds to a hex
+// value of 0x0001 and the V2z bit corresponds to a hex value of
+// 0x0020. Example: to specify the axes V1x, V1y, V2x, and V2z the
+// pattern would be 0x002b. Vector 1 defaults to a force vector and
+// vector 2 defaults to a moment vector. It is possible to change one
+// or the other so that two force vectors or two moment vectors are
+// calculated. Setting the changeV1 bit or the changeV2 bit will
+// change that vector to be the opposite of its default. Therefore to
+// have two force vectors, set changeV1 to 1.
+
+typedef enum {
+  fx = 0x0001,
+  fy = 0x0002,
+  fz = 0x0004,
+  mx = 0x0008,
+  my = 0x0010,
+  mz = 0x0020,
+  changeV2 = 0x0040,
+  changeV1 = 0x0080
+} vect_bits_t;
+
+// WARNING_BITS
+// The warning_bits structure shows the bit pattern for the warning
+// word. The bit fields are shown from bit 0 (lsb) to bit 15 (msb).
+//
+// XX_NEAR_SET
+// The xx_near_sat bits signify that the indicated axis has reached or
+// exceeded the near saturation value.
+
+typedef enum {
+  fx_near_sat = 0x0001,
+  fy_near_sat = 0x0002,
+  fz_near_sat = 0x0004,
+  mx_near_sat = 0x0008,
+  my_near_sat = 0x0010,
+  mz_near_sat = 0x0020
+} warnig_bits_t;
 
 // ERROR_BITS
-// Bit pattern for the error word:
-// 1. xx_sat means that a near saturation has been reached or exceeded.
-// 2. memory_error indicates RAM memory error during power up.
-// 3. sensor_change indicates that the sensor plugged in (different from the
-//    original one) has passed CRC check. The user must reset this bit.
-// 4. system_busyindicates system busy: transf. change, new full scale or new
-//    sennsor plugged in.
-// 5. cal_crc_bad means that it was a problem transmiting the calibration data
-//    stored inside the sensor. If this bit does not come to zero 2s after the
-//    sensor has been plugged in, there is a problem with the sensor's calibra-
-//    tion data.
-// 6. watch_dog2 indicates that sensor data and clock are being received.
-// 7. watch_dog indicates that data line seems to be acting correctly.
-// If either watch dog barks, the sensor data is not beig receive correctly.
-#if 0
-typedef struct error_bits
-{
- unsigned fx_sat : 1;
- unsigned fy_sat : 1;
- unsigned fz_sat : 1;
- unsigned mx_sat : 1;
- unsigned my_sat : 1;
- unsigned mz_sat : 1;
- unsigned reserved : 4;
- unsigned memory_error : 1;
- unsigned sensor_change : 1;
- unsigned system_busy : 1;
- unsigned cal_crc_bad : 1;
- unsigned watch_dog2 : 1;
- unsigned watch_dog : 1;
-} error_bits;
-#endif
+// XX_SAT
+// MEMORY_ERROR
+// SENSOR_CHANGE
+// 
+// The error_bits structure shows the bit pattern for the error word.
+// The bit fields are shown from bit 0 (lsb) to bit 15 (msb). The
+// xx_sat bits signify that the indicated axis has reached or exceeded
+// the saturation value. The memory_error bit indicates that a problem
+// was detected in the on-board RAM during the power-up
+// initialization. The sensor_change bit indicates that a sensor other
+// than the one originally plugged in has passed its CRC check. This
+// bit latches, and must be reset by the user.
+//
+// SYSTEM_BUSY
+//
+// The system_busy bit indicates that the JR3 DSP is currently busy
+// and is not calculating force data. This occurs when a new
+// coordinate transformation, or new sensor full scale is set by the
+// user. A very fast system using the force data for feedback might
+// become unstable during the approximately 4 ms needed to accomplish
+// these calculations. This bit will also become active when a new
+// sensor is plugged in and the system needs to recalculate the
+// calibration CRC.
+//
+// CAL_CRC_BAD
+//
+// The cal_crc_bad bit indicates that the calibration CRC has not
+// calculated to zero. CRC is short for cyclic redundancy code. It is
+// a method for determining the integrity of messages in data
+// communication. The calibration data stored inside the sensor is
+// transmitted to the JR3 DSP along with the sensor data. The
+// calibration data has a CRC attached to the end of it, to assist in
+// determining the completeness and integrity of the calibration data
+// received from the sensor. There are two reasons the CRC may not
+// have calculated to zero. The first is that all the calibration data
+// has not yet been received, the second is that the calibration data
+// has been corrupted. A typical sensor transmits the entire contents
+// of its calibration matrix over 30 times a second. Therefore, if
+// this bit is not zero within a couple of seconds after the sensor
+// has been plugged in, there is a problem with the sensor's
+// calibration data.
+//
+// WATCH_DOG
+// WATCH_DOG2
+//
+// The watch_dog and watch_dog2 bits are sensor, not processor, watch
+// dog bits. Watch_dog indicates that the sensor data line seems to be
+// acting correctly, while watch_dog2 indicates that sensor data and
+// clock are being received. It is possible for watch_dog2 to go off
+// while watch_dog does not. This would indicate an improper clock
+// signal, while data is acting correctly. If either watch dog barks,
+// the sensor data is not being received correctly.
+
+typedef enum {
+  fx_sat = 0x0001,
+  fy_sat = 0x0002,
+  fz_sat = 0x0004,
+  mx_sat = 0x0008,
+  my_sat = 0x0010,
+  mz_sat = 0x0020,
+  memory_error = 0x0400,
+  sensor_change = 0x0800,
+  system_busy = 0x1000,
+  cal_crc_bad = 0x2000,
+  watch_dog2 = 0x4000,
+  watch_dog = 0x8000
+} error_bits_t;
+
 
 // THRESH_STRUCT
 // This structure shows the layout for a single threshold packet inside of a

Received on 2007-07-05Z13:38:49