Re: Bug with IMPS/2 w/ Microsoft Intellimouse 1.3A on Linux

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Ok, I should have commented a while back but I have been very busy with
life.

First off, doing the init via the shut off via reset, set everything,
and reactivate does fix a /lot/ of problems.

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.

Below I will attach bits of the current Debian gpm init stuff, which
handles PS/2, IMPS/2, and EXPS/2 all in the same init function, and also
has a autops2 protocol which will automaticly detect which protocol of
the three the mouse can speak which supports the most options.

In addition to these modes I have fups2 and fuimps2, for mice which
can't handle any sort of complex init sequence, I have yet to see
reports of exps2 mice not being able to support the full set of
commands, however it is only a matter of time before there become
Logitech OEM mice which have more buttens. (OTOH, we could get lucky and
they could be entirely USB.)

I'll also include the parser for the protocols.

(This code has NOT been updated to 1.20 yet, it is high on my todo list,
but lower then getting the bathroom back together and usable.)

This is just grabbing the relevant sections from the mice.c FWIW.

Zephaniah E. Hull.
Debian gpm maintainer.
(Too bloody busy to keep up.)

-- 
	1024D/E65A7801 Zephaniah E. Hull <warp@xxxxxxxxxxxxxxxx>
	   92ED 94E4 B1E6 3624 226D  5727 4453 008B E65A 7801
	    CCs of replies from mailing lists are requested.

[1] Yes, we ARE rather dull people.  We appreciate being dull people.
Exciting is only good when it happens to someone else ... as in "an
exciting wreck", "an exciting plane crash", "an exciting install of
Windows XP", et al.
  -- Ralph Wade Phillips in the Scary Devil Monastery.
/*
 * 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 = GPM_AUX_SEND_ID;
  unsigned char id;

  write(fd, &c, 1);
  read(fd, &c, 1);
  if (c != 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.
 * 
 * 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 = 0, rcnt;
  unsigned char cmd[2], ret[512];

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

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

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

  usleep(sleep);

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

  usleep(sleep);

  if (rcnt <= 0)
	error++;

  for (i = 0; i < rcnt; i++) {
    if (ret[i] != 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=mice; type->fun; type++) {
    if (strcmp(type->name, name) == 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 = 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 += write_ps2 (fd, GPM_AUX_SET_SAMPLE, 200, 2, 2, -1);
    error += write_ps2 (fd, GPM_AUX_SET_SAMPLE, 100, 2, 2, -1);
    error += 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 += write_ps2 (fd, GPM_AUX_SET_SAMPLE, 200, 2, 2, -1);
    error += write_ps2 (fd, GPM_AUX_SET_SAMPLE, 200, 2, 2, -1);
    error += 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 >= 200) rate = 200;
    else if (opt_sample >= 100) rate = 100;
    else if (opt_sample >= 80) rate = 80;
    else if (opt_sample >= 60) rate = 60;
    else if (opt_sample >= 40) rate = 40;
    else if (opt_sample >= 20) rate = 20;
    else if (opt_sample >= 10) rate = 10;
    else rate = 100;
  } else {
    rate = 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 = 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 protocol.\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 = 0;

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

  // Magic to enable the IMPS/2 protocol.
  error += write_ps2 (fd, GPM_AUX_SET_SAMPLE, 200, 2, 2, -1);
  error += write_ps2 (fd, GPM_AUX_SET_SAMPLE, 100, 2, 2, -1);
  error += 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=0; /* there exist glidepoint ps2 mice */

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

  state->buttons =
    ((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 = tap_active = opt_glidepoint_tap;
  else if (tap_active) {
    if (data[0]==8)
      state->buttons = tap_active = 0;
    else state->buttons = tap_active;
  }

  if (data[1])
    state->dx =   (data[0] & 0x10) ? data[1]-256 : data[1];
  if (data[2])
    state->dy = -((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 +=
    ((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 = +1; break;
    case 0x02: state->wdx = -1; break;
    case 0x0f: state->wdy = +1; break;
    case 0x01: state->wdy = -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},

Attachment: pgp00042.pgp
Description: PGP signature


[Index of Archives]     [Kernel Development]     [Red Hat Install]     [Red Hat Watch]     [Red Hat Development]     [Gimp]     [Yosemite News]