[gpm]Bug with IMPS/2 w/ Microsoft Intellimouse 1.3A on Linux

Peter Berg Larsen pebl@math.ku.dk
Tue, 18 Jun 2002 01:50:17 +0200 (MET DST)


  This message is in MIME format.  The first part should be readable text,
  while the remaining parts are likely unreadable without MIME-aware tools.
  Send mail to mime@docserver.cac.washington.edu for more info.

--dkEUBIird37B8yKS
Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
Content-ID: <Pine.OSF.4.21.0206180144201.30568@artin.math.ku.dk>
Content-Disposition: INLINE



On Mon, 17 Jun 2002, Zephaniah E. Hull wrote:

> However it also breaks a lot of mice which, while they seem to plug into
> the PS/2 port, don't have a damn clue how to speak most of the protocol.
> 
> Failure modes vary, but in general they just Don't Work.

All what is needed of the protocol is the stream off and on. Reset is not
strickly needed but nice. Are there actually mice that don't implement
this? Is there somewhere a list a faulty mice?


Peter
-- 
E-Mail:       pebl@math.ku.dk 
Real name:    Peter Berg Larsen 
Where:        Department of Computer Science, Copenhagen Uni., Denmark

--dkEUBIird37B8yKS
Content-Type: TEXT/X-CSRC; CHARSET=us-ascii
Content-Transfer-Encoding: QUOTED-PRINTABLE
Content-ID: <Pine.OSF.4.21.0206180144202.30568@artin.math.ku.dk>
Content-Description: 
Content-Disposition: ATTACHMENT; FILENAME="deb_mice.c"

/*
 * Sends the SEND_ID command to the ps2-type mouse.
 * Return one of GPM_AUX_ID_...
 */
static int read_mouse_id(int fd)
 {
  unsigned char c =3D GPM_AUX_SEND_ID;
  unsigned char id;

  write(fd, &c, 1);
  read(fd, &c, 1);
  if (c !=3D GPM_AUX_ACK) {
    return(GPM_AUX_ID_ERROR);
  }
  read(fd, &id, 1);

  return(id);
}

/**
 * Writes the given data to the ps2-type mouse.
 * Checks for an ACK from each byte.
 *=20
 * Returns 0 if OK, or >0 if 1 or more errors occurred.
 */
static int write_ps2(int fd, unsigned char cmd0, unsigned char cmd1,
    	size_t num, size_t rnum, unsigned long int sleep)
{
  int i, error =3D 0, rcnt;
  unsigned char cmd[2], ret[512];

  cmd[0] =3D cmd0; cmd[1] =3D cmd1;

  if (sleep =3D=3D -1)
      sleep =3D 50000;

  alarm(5);
  rcnt =3D write(fd, cmd, num);
  alarm(0);
  if (rcnt !=3D num)
    return 1;

  usleep(sleep);

  alarm(5);
  rcnt =3D read(fd, ret, rnum);
  alarm(0);

  usleep(sleep);

  if (rcnt <=3D 0)
	error++;

  for (i =3D 0; i < rcnt; i++) {
    if (ret[i] !=3D GPM_AUX_ACK) {
      gpm_debug_log(LOG_ERR, "write_ps2: %d %d, %x", rcnt, i, ret[i]);
      error++;
    }
  }

  return(error);
}

static Gpm_Type *get_mouse_type (char *name)
{
  Gpm_Type *type;

  for (type=3Dmice; type->fun; type++) {
    if (strcmp(type->name, name) =3D=3D 0) {
      return(type);
    }
  }
  return NULL;
}

/* intellimouse, ps2 version: Ben Pfaff and Colin Plumb */
/* Autodetect: Steve Bennett */
static Gpm_Type *I_ps2(int fd, unsigned short flags_unused,
	struct Gpm_Type *type, int argc, char **argv)
{
  int id, error =3D 0, rate;

  /* Flush any existing input. */
  tcflush (fd, TCIOFLUSH);

  if (write_ps2 (fd, GPM_AUX_DEFAULTS, '\0', 1, 1, -1)) {
    gpm_debug_log(LOG_ERR, "PS/2 mouse failed init");
    return(NULL);
  }

  // Magic to enable the IMPS/2 protocol.
  if ((!strcmp(type->name, "imps2")) || (!strcmp(type->name, "autops2"))) {
    error +=3D write_ps2 (fd, GPM_AUX_SET_SAMPLE, 200, 2, 2, -1);
    error +=3D write_ps2 (fd, GPM_AUX_SET_SAMPLE, 100, 2, 2, -1);
    error +=3D write_ps2 (fd, GPM_AUX_SET_SAMPLE, 80, 2, 2, -1);
    if (error) {
      gpm_debug_log(LOG_ERR, "imps2: PS/2 mouse failed (3 button) init");
      return(NULL);
    }
  }
  if ((!strcmp(type->name, "exps2")) || (!strcmp(type->name, "autops2"))) {
    error +=3D write_ps2 (fd, GPM_AUX_SET_SAMPLE, 200, 2, 2, -1);
    error +=3D write_ps2 (fd, GPM_AUX_SET_SAMPLE, 200, 2, 2, -1);
    error +=3D write_ps2 (fd, GPM_AUX_SET_SAMPLE, 80, 2, 2, -1);
    if (error) {
      gpm_debug_log (LOG_ERR, "exps2: PS/2 mouse failed (3 button) init");
      return (NULL);
    }
  }

  if (write_ps2 (fd, GPM_AUX_SET_SCALE11, '\0', 1, 1, -1)) {
    gpm_debug_log(LOG_ERR, "PS/2 mouse failed init: Unable to set 1:1 scale=
.");
    return (NULL);
  }

  if (opt_sample > 0) {
    if (opt_sample >=3D 200) rate =3D 200;
    else if (opt_sample >=3D 100) rate =3D 100;
    else if (opt_sample >=3D 80) rate =3D 80;
    else if (opt_sample >=3D 60) rate =3D 60;
    else if (opt_sample >=3D 40) rate =3D 40;
    else if (opt_sample >=3D 20) rate =3D 20;
    else if (opt_sample >=3D 10) rate =3D 10;
    else rate =3D 100;
  } else {
    rate =3D 100;
  }

  if (write_ps2 (fd, GPM_AUX_SET_SAMPLE, rate, 2, 1, -1)) {
    gpm_debug_log(LOG_ERR, "PS/2 mouse failed init: Unable to set rate.");
    return (NULL);
  }

  if (!strcmp(type->name, "autops2")) {
    /* Read the mouse id */
    id =3D read_mouse_id(fd);

    switch (id) {
      case GPM_AUX_ID_ERROR:
	gpm_debug_log (LOG_ERR, "Unable to read PS/2 mouse ID: Using base PS/2 pro=
tocol.\n");
	write_ps2 (fd, GPM_AUX_SET_STREAM, '\0', 1, 1, 1);
	write_ps2 (fd, GPM_AUX_ENABLE_DEV, '\0', 1, 1, 1);
	return get_mouse_type("ps2");
      case GPM_AUX_ID_PS2:
	gpm_debug_log(LOG_NOTICE, "Detected base PS/2 protocol mouse.");
	write_ps2 (fd, GPM_AUX_SET_STREAM, '\0', 1, 1, 1);
	write_ps2 (fd, GPM_AUX_ENABLE_DEV, '\0', 1, 1, 1);
	return get_mouse_type("ps2");
      case GPM_AUX_ID_IMPS2:
	gpm_debug_log(LOG_NOTICE, "Detected IMPS/2 protocol mouse.");
	write_ps2 (fd, GPM_AUX_SET_STREAM, '\0', 1, 1, 1);
	write_ps2 (fd, GPM_AUX_ENABLE_DEV, '\0', 1, 1, 1);
	return get_mouse_type("imps2");
      case GPM_AUX_ID_EXPS2:
	gpm_debug_log(LOG_NOTICE, "Detected EXPS/2 protocol mouse.");
	write_ps2 (fd, GPM_AUX_SET_STREAM, '\0', 1, 1, 1);
	write_ps2 (fd, GPM_AUX_ENABLE_DEV, '\0', 1, 1, 1);
	return get_mouse_type("exps2");
      default:
	gpm_debug_log (LOG_ERR, "Unknown mouse ID, using base PS/2 protocol.");
	write_ps2 (fd, GPM_AUX_SET_STREAM, '\0', 1, 1, 1);
	write_ps2 (fd, GPM_AUX_ENABLE_DEV, '\0', 1, 1, 1);
	return get_mouse_type("ps2");
    }
  }

  write_ps2 (fd, GPM_AUX_SET_STREAM, '\0', 1, 1, 1);
  write_ps2 (fd, GPM_AUX_ENABLE_DEV, '\0', 1, 1, 1);
  return type;
}

/* PS/2 Init */
static Gpm_Type *I_fuimps2(int fd, unsigned short flags,
                               struct Gpm_Type *type, int argc, char **argv)
{
  int error =3D 0;

  if (check_no_argv(argc, argv)) return NULL;

  // Magic to enable the IMPS/2 protocol.
  error +=3D write_ps2 (fd, GPM_AUX_SET_SAMPLE, 200, 2, 2, -1);
  error +=3D write_ps2 (fd, GPM_AUX_SET_SAMPLE, 100, 2, 2, -1);
  error +=3D write_ps2 (fd, GPM_AUX_SET_SAMPLE, 80, 2, 2, -1);
  if (error)
    gpm_debug_log(LOG_ERR, "fuimps2: %d errors in init, ignoring.", error);

  return type;
}

/*
 * PS/2 mouse parser.
 * Also called by the 'children' of the PS/2 mouse, for insanity sake.
 *  -- Zephaniah E. Hull.
 */
/* Some PS/2 mice send reports with negative bit set in data[0]
 * and zero for movement.  I think this is a bug in the mouse, but
 * working around it only causes artifacts when the actual report is -256;
 * they'll be treated as zero. This should be rare if the mouse sampling
 * rate is set to a reasonable value; the default of 100 Hz is plenty.
 * (Stephen Tell)
 */

static int M_ps2(Gpm_Event *state,  unsigned char *data)
{
  static int tap_active=3D0; /* there exist glidepoint ps2 mice */

  state->dx =3D state->dy =3D state->wdx =3D state->wdy =3D 0;

  state->buttons =3D
    ((data[0] & 0x02) ? (1 << 0) : 0) |		// Right.
    ((data[0] & 0x04) ? (1 << 1) : 0) |		// Middle.
    ((data[0] & 0x01) ? (1 << 2) : 0);		// Left.

  if (!data[0] && opt_glidepoint_tap) /* by default this is false */
    state->buttons =3D tap_active =3D opt_glidepoint_tap;
  else if (tap_active) {
    if (data[0]=3D=3D8)
      state->buttons =3D tap_active =3D 0;
    else state->buttons =3D tap_active;
  }

  if (data[1])
    state->dx =3D   (data[0] & 0x10) ? data[1]-256 : data[1];
  if (data[2])
    state->dy =3D -((data[0] & 0x20) ? data[2]-256 : data[2]);

  return 0;
}

/*
 * I cheat, because the two protocols are almost identical except for
 * the 4th and 5th buttons, I just handle both.
 *
 * Note, the only thing that I've seen describe the 4th and 5th buttons
 * for the IMPS/2 protocol is the X4 source, someone let me know if they
 * actually see this used?
 *  -- Zephaniah E. Hull.
 */
static int M_imps2(Gpm_Event *state,  unsigned char *data)
{
  M_ps2(state, data);

  state->buttons +=3D
    ((data[0] & 0x40) ? (1 << 3) : 0) | // IMPS/2 Button 4
    ((data[0] & 0x80) ? (1 << 4) : 0) | // IMPS/2 Button 5
    ((data[3] & 0x10) ? (1 << 3) : 0) | // EXPS/2 Button 4
    ((data[3] & 0x20) ? (1 << 4) : 0);  // EXPS/2 Button 5

  // The wheels..
  switch (data[3] & 0x0f) {
    case 0x0e: state->wdx =3D +1; break;
    case 0x02: state->wdx =3D -1; break;
    case 0x0f: state->wdy =3D +1; break;
    case 0x01: state->wdy =3D -1; break;
  }

  return 0;
}


  {"ps2",  "For PS/2 mice (round with 6 metal pins).\n",
           "PS/2", M_ps2, I_ps2, STD_FLG,
                                {0xc0, 0x00, 0x00, 0x00}, 3, 1, 0, 0, 0},
  {"fups2",  "For /BROKEN/ PS/2 mice (round with 6 metal pins).\n",
           "PS/2", M_ps2, I_empty, STD_FLG,
                                {0xc0, 0x00, 0x00, 0x00}, 3, 1, 0, 0, 0},
  {"imps2","For the Microsoft IntelliMouse on a PS/2 port\n"
           "(round connector with 6 pins), 3 buttons (wheel is repeated).",
           "", M_imps2, I_ps2, STD_FLG,
                                {0xc0, 0x00, 0x00, 0x00}, 4, 1, 0, 0, 0},
  {"fuimps2","For BROKEN wheel mice on a PS/2 port\n"
           "(round connector with 6 pins), 3 buttons (wheel is repeated).",
           "", M_imps2, I_fuimps2, STD_FLG,
                                {0xc0, 0x00, 0x00, 0x00}, 4, 1, 0, 0, 0},
  {"exps2",  "IntelliMouse Explorer (ps2) - 3 buttons (wheel is repeated).",
           "ExplorerPS/2", M_imps2, I_ps2, STD_FLG,
                                {0xc0, 0x00, 0x00, 0x00}, 4, 1, 0, 0, 0},
  {"autops2","For PS/2 type mouse, specific protocol will be auto detected",
           "", M_ps2, I_ps2, STD_FLG,
                                {0xc0, 0x00, 0x00, 0x00}, 3, 1, 0, 0, 0},

--dkEUBIird37B8yKS--