diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/code/Makefile | 15 | ||||
| -rw-r--r-- | kernel/code/gpio.c | 49 | ||||
| -rw-r--r-- | kernel/code/libradiator.c | 463 | ||||
| -rw-r--r-- | kernel/code/program_radiator.c | 23 | ||||
| -rw-r--r-- | kernel/code/radiator.c | 34 | ||||
| -rw-r--r-- | kernel/code/radiator.h | 19 | ||||
| -rw-r--r-- | kernel/code/set.c | 63 | ||||
| -rw-r--r-- | kernel/code/thing.c | 207 | ||||
| -rw-r--r-- | kernel/cp2103-3.11.10.patch | 491 | 
9 files changed, 1364 insertions, 0 deletions
diff --git a/kernel/code/Makefile b/kernel/code/Makefile new file mode 100644 index 0000000..a478c0c --- /dev/null +++ b/kernel/code/Makefile @@ -0,0 +1,15 @@ + +PROGS=radiator program_radiator +CSRCS=libradiator.c  + +OBJS=${CSRCS:%.c=%.o} + +default:${PROGS} + + +${PROGS}: %:%.o ${OBJS} +	${CC} -o $@ ${OBJS} $@.o + + +clean: +	/bin/rm -f core ${OBJS} ${PROGS} ${PROGS:%=%.o} diff --git a/kernel/code/gpio.c b/kernel/code/gpio.c new file mode 100644 index 0000000..549eb81 --- /dev/null +++ b/kernel/code/gpio.c @@ -0,0 +1,49 @@ +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> + +#include <sys/ioctl.h> +#include <linux/cp210x.h> + + +#define  log_ioctl(fd,op,arg) do_ioctl(fd,#op,op,arg) + +int +do_ioctl (int fd, char *sop, int op, void *arg) +{ +  int ret; + +  errno = 0; + +  ret = ioctl (fd, op, arg); + +  printf ("ioctl(%d,%s(0x%x),%p)=%d  (errno=%d(%s))\n", fd, sop, op, arg, ret, +          errno, strerror (errno)); + +  return ret; +} + +int +main (int argc, char *argv[]) +{ +  struct cp210x_port_config config; + +  int fd = open (argv[1], O_RDWR); + +  memset (&config, 0, sizeof (config)); + +  log_ioctl (fd, CPIOC_PORTCONFGET, &config); + +  printf ("reset=(%x,%x,%x), suspend=(%x,%x,%x), enhanced_fxn=%x\n", +          config.reset.mode, +          config.reset.low_power, +          config.reset.latch, +          config.suspend.mode, +          config.suspend.low_power, +          config.suspend.latch, config.enhanced_fxn); + + +} diff --git a/kernel/code/libradiator.c b/kernel/code/libradiator.c new file mode 100644 index 0000000..7baed2e --- /dev/null +++ b/kernel/code/libradiator.c @@ -0,0 +1,463 @@ +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <malloc.h> +#include <time.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <linux/cp210x.h> +#include <sys/termios.h> + +#include "radiator.h" + + + + + +/* + * bit 0: 1	left  leg NPN base + * bit 1: 2	left  leg PNP base + * bit 2: 4	right leg PNP base + * bit 3: 8	right leg NPN base + */ + +#define LEFT_NPN_OFF	0 +#define LEFT_NPN_ON	1 +#define LEFT_PNP_OFF	2 +#define LEFT_PNP_ON	0 + +#define LEFT_LOW	(LEFT_NPN_ON | LEFT_PNP_OFF) +#define LEFT_HI		(LEFT_NPN_OFF | LEFT_PNP_ON) +#define LEFT_OFF	(LEFT_NPN_OFF | LEFT_PNP_OFF) + +#define RIGHT_NPN_OFF	0 +#define RIGHT_NPN_ON	8 +#define RIGHT_PNP_OFF	4 +#define RIGHT_PNP_ON	0 + + +#define RIGHT_LOW	(RIGHT_NPN_ON | RIGHT_PNP_OFF) +#define RIGHT_HI	(RIGHT_NPN_OFF | RIGHT_PNP_ON) +#define RIGHT_OFF	(RIGHT_NPN_OFF | RIGHT_PNP_OFF) + +#define BRAKE		(RIGHT_LOW | LEFT_LOW) +#define FORWARDS	(RIGHT_LOW | LEFT_HI) +#define BACKWARDS	(RIGHT_HI | LEFT_LOW) +#define OFF		(RIGHT_OFF| LEFT_OFF) + +#define TIMEOUT 	750000         /*75ms */ +#define RUN_IN 		50 +#define RELEASE_TENSION	10 + +static int brake_motor(int fd) +{ +  int i=BRAKE; +  printf (" Motor braking\n"); +  return ioctl (fd, CPIOC_GPIOSET, &i); +} + +static int +drive_motor (int fd, int dir) +{ +  int i; + +  if (dir > 0) +    { +      printf (" Motor forwards\n"); +      i = FORWARDS; +    } +  else if (dir < 0) +    { +      printf (" Motor backwards\n"); +      i = BACKWARDS; +    } +  else +    { +      printf (" Motor off\n"); +      i = OFF; +    } + + +  return ioctl (fd, CPIOC_GPIOSET, &i); +} + +static int +sensor_led_power (int fd, int on) +{ +  int i = TIOCM_DTR; + +  printf (" Rotation sensor power %s\n", on ? "on" : "off"); + +  return ioctl (fd, on ? TIOCMBIS : TIOCMBIC, &i); +} + + +#if 0 +static int +count_steps (int fd, int count) +{ +  struct timeval tv, tv_changed, tv_diff; +  int cts, old_cts = -1; +  int counted = 0; + +  printf (" Counting %d steps", count); + +  gettimeofday (&tv_changed, NULL); + +  while (count) +    { +      ioctl (fd, TIOCMGET, &cts); +      cts &= TIOCM_CTS; + +      if (cts != old_cts) +        { +          if (cts) +            { +              counted++; +              count--; +              printf ("."); +              fflush (stdout); +            } +          gettimeofday (&tv_changed, NULL); +          old_cts = cts; +        } +      gettimeofday (&tv, NULL); +      timersub (&tv, &tv_changed, &tv_diff); + +      if (tv_diff.tv_sec || (tv_diff.tv_usec > TIMEOUT)) +        break; + +    } + +  printf ("%d steps\n", counted); +  return counted; +} +#else +count_steps (int fd, int count) +{ +  int counted = 0; +  struct timeval tv; +  char buf[128]; +  int i; +  fd_set rfds; + +  printf (" Counting %d steps", count); + +FD_ZERO(&rfds); + +tcflush(fd,TCIFLUSH); + + +  while (count) +    { +	tv.tv_sec=0; +	tv.tv_usec=TIMEOUT; +	FD_SET(fd,&rfds); + +	if (!select(fd+1,&rfds,NULL,NULL,&tv)) break; + +	i=read(fd,buf,sizeof(buf)); +	if (i<0) continue; + +        counted+=i; +	if (count>0) { +		count-=i; +		if (count<0) count=0; +	} + +         printf ("."); +         fflush (stdout); +    } + +  printf ("%d steps\n", counted); +  return counted; +} +#endif + + + + +static int +drive_motor_count (Radiator * r, int dir, int steps) +{ +  int s1, s2, ret; + +  sensor_led_power (r->fd, 1); +  drive_motor (r->fd, dir); +  s1 = count_steps (r->fd, steps); +  if (s1 != steps) { +    printf (" Hit end stop\n"); +    drive_motor (r->fd, 0); +  } else  { +    brake_motor(r->fd); +  } +  s2 = count_steps (r->fd, -1); +  sensor_led_power (r->fd, 0); +  drive_motor (r->fd, 0); + +  /*did we hit an end stop? */ +  if (s1 != steps) +    ret = s1 - s2; +  else +    ret = s1 + s2; + +  printf (" %d steps under power (of %d), %d steps coasting -> %d steps\n", +          s1, steps, s2, ret); + +  r->pos += ret * dir; + +  /*Recalibrate our notion of ends */ + +  if (s1 != steps) +    { +      if (dir == -1) +        { +          r->pos = 0; +        } +      else +        { +          r->max = r->pos; +        } +    } + +  return ret; +} + + + + +int +radiator_set_pos (Radiator * r, int wanted) +{ +  int guard, delta; + +  delta = wanted - r->pos; +  printf ("Now at %d want %d, delta %d\n", r->pos, wanted, delta); + +  if (wanted == 0) +    { +      drive_motor_count (r, -1, -1); +      return 0; +    } +  else if (r->max && (wanted >= r->max)) +    { +      drive_motor_count (r, 1, -1); +      return 0; +    } + + +  guard = r->overshoot * 3; + +  if ((delta > -guard) && (delta < guard)) +    { +      printf ("Too close\n"); + +      drive_motor_count (r, (r->pos > r->half) ? -1 : 1, RUN_IN); + +      delta = wanted - r->pos; +      printf ("Now at %d want %d, delta %d\n", r->pos, wanted, delta); +    } + + +  if (delta < 0) +    { +      drive_motor_count (r, -1, (-delta) - r->overshoot); +    } +  else if (delta > 0) +    { +      drive_motor_count (r, 1, delta - r->overshoot); +    } + +  printf ("Wanted %d, got %d\n", wanted, r->pos); + +  return 0; +} + +int +radiator_calibrate (Radiator * r) +{ +  radiator_set_pos (r, 0); + +  drive_motor_count (r, 1, RUN_IN); + +  r->overshoot = r->pos - RUN_IN; +} + + +static int utf16(uint8_t *dst,uint8_t *src) +{ +int len=0; + + +while (*src) { +	*(dst++)=*(src++); +	len+=2; +	*(dst++)=0; +} + +return len; +} + + +static void make_usb_descriptor(uint8_t *dst,uint8_t *src) +{ +int len; +len=utf16(dst+2,src); +dst[0]=len+2; +dst[1]=0x3; +} + + + + +int +radiator_program (Radiator * r) +{ +  struct cp210x_port_config config; +  uint8_t buf[256]; + + +#if 0 +  memset (&config, 0, sizeof (config)); +  ioctl (r->fd, CPIOC_PORTCONFGET, &config); + + +  printf +    ("config was reset=(%x,%x,%x), suspend=(%x,%x,%x), enhanced_fxn=%x\n", +     config.reset.mode, config.reset.low_power, config.reset.latch, +     config.suspend.mode, config.suspend.low_power, config.suspend.latch, +     config.enhanced_fxn); + + +  memset (&config, 0, sizeof (config)); + +  config.reset.mode = ~CP_INPUT_PINS; +  config.reset.low_power = 0; +  config.reset.latch = CP_INPUT_PINS | CP_PIN_GPIO_1 | CP_PIN_GPIO_2; +  config.suspend.mode = ~CP_INPUT_PINS; +  config.suspend.low_power = 0; +  config.suspend.latch = CP_INPUT_PINS | CP_PIN_GPIO_1 | CP_PIN_GPIO_2; +  config.enhanced_fxn = CP_EFXN_ENABLE_WPU; + +  ioctl (r->fd, CPIOC_PORTCONFSET, &config); + +  memset (&config, 0, sizeof (config)); +  ioctl (r->fd, CPIOC_PORTCONFGET, &config); + + +  printf +    ("config is  reset=(%x,%x,%x), suspend=(%x,%x,%x), enhanced_fxn=%x\n", +     config.reset.mode, config.reset.low_power, config.reset.latch, +     config.suspend.mode, config.suspend.low_power, config.suspend.latch, +     config.enhanced_fxn); + +#endif +  memset(buf,0,sizeof(buf)); + +{ +int i=0x413c; +  ioctl (r->fd, CPIOC_SETVID, &i); +} + +{ +int i=0x9500; +  ioctl (r->fd, CPIOC_SETPID, &i); +} + +  make_usb_descriptor(buf,"USB Radiator Valve"); +  ioctl (r->fd, CPIOC_SETPRODUCT, buf); + +  make_usb_descriptor(buf,"000001"); +  ioctl (r->fd, CPIOC_SETSERIAL, buf); +   +#if 0 +  make_usb_descriptor(buf,"Global Panaceas"); +  ioctl (r->fd, CPIOC_SETMFG, buf); +#endif + + +  ioctl (r->fd, CPIOC_DEVICERESET, 0); + + + + +  return 0; +} + + +static void tty_setup(int fd) +{ +struct termios tios; + +tcgetattr(fd, &tios); + +cfmakeraw( &tios); + +  tios.c_iflag = PARMRK | INPCK; +  tios.c_oflag = NL0 | CR0 | TAB0 | BS0 | VT0 | FF0; +  tios.c_lflag = 0; +  tios.c_cflag = CS8 | CREAD | CLOCAL; +  tios.c_cc[VMIN]=1; +  tios.c_cc[VTIME]=0; + +  cfsetispeed (&tios, B115200); +  cfsetospeed (&tios, B115200); + +  tcsetattr (fd, TCSANOW, &tios); +} + + + +static void +set_nonblocking (int fd) +{ +  long arg = 0; +  arg = fcntl (fd, F_GETFL, arg); +  arg |= O_NONBLOCK; +  fcntl (fd, F_SETFL, arg); +} + +static void +set_blocking (int fd) +{ +  long arg = 0; +  arg = fcntl (fd, F_GETFL, arg); +  arg &= ~O_NONBLOCK; +  fcntl (fd, F_SETFL, arg); +} + + +Radiator * +radiator_open (char *s, int quiet) +{ +  Radiator *r = malloc (sizeof (Radiator)); + +  r->fd = open (s, O_RDWR |O_NOCTTY); + +  if (r->fd < 0) +    { +      free (r); +      return NULL; +    } + +  tty_setup(r->fd); +  set_nonblocking(r->fd); + +  r->half = 5 * RUN_IN; +  r->overshoot = 0; +  r->max = 0; +  r->pos = 0; + +  if (!quiet) +  	radiator_calibrate (r); +} + +void +radiator_close (Radiator * r) +{ +  close (r->fd); +  free (r); +} diff --git a/kernel/code/program_radiator.c b/kernel/code/program_radiator.c new file mode 100644 index 0000000..75077da --- /dev/null +++ b/kernel/code/program_radiator.c @@ -0,0 +1,23 @@ +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdint.h> +#include "radiator.h" + +int +main (int argc, char *argv[]) +{ +  Radiator *r; +  int pos; +  char buf[1024]; + + +  r = radiator_open (argv[1],1); + +  if (!r) +    return -1; + +  radiator_program(r); + +  return 0; +} diff --git a/kernel/code/radiator.c b/kernel/code/radiator.c new file mode 100644 index 0000000..2d594da --- /dev/null +++ b/kernel/code/radiator.c @@ -0,0 +1,34 @@ +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdint.h> +#include "radiator.h" + +int +main (int argc, char *argv[]) +{ +  Radiator *r; +  int pos; +  char buf[1024]; + + +  r = radiator_open (argv[1],0); + +  if (!r) +    return -1; + + +  for (;;) +    { +      printf ("Current position %d (end stop at %d, overshoots by %d)\n", +              r->pos, r->max, r->overshoot); +      printf ("Enter new>"); +      fflush (stdout); +      buf[sizeof (buf) - 1] = 0; +      fgets (buf, sizeof (buf) - 1, stdin); +      radiator_set_pos (r, atoi (buf)); +    } + + +  return 0; +} diff --git a/kernel/code/radiator.h b/kernel/code/radiator.h new file mode 100644 index 0000000..a16edb8 --- /dev/null +++ b/kernel/code/radiator.h @@ -0,0 +1,19 @@ +#ifndef _RADIATOR_H_ +#define _RADIATOR_H_ + +typedef struct +{ +  int fd; +  int pos; +  int half; +  int overshoot; +  int max; +} Radiator; + +int radiator_set_pos (Radiator * r, int wanted); +int radiator_calibrate (Radiator * r); +int radiator_program (Radiator * r); +Radiator *radiator_open (char *s, int quiet); +void radiator_close (Radiator * r); + +#endif /* _RADIATOR_H_ */ diff --git a/kernel/code/set.c b/kernel/code/set.c new file mode 100644 index 0000000..4c8de0e --- /dev/null +++ b/kernel/code/set.c @@ -0,0 +1,63 @@ +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> + +#include <sys/ioctl.h> +#include <linux/cp210x.h> + + +#define  log_ioctl(fd,op,arg) do_ioctl(fd,#op,op,arg) + +int +do_ioctl (int fd, char *sop, int op, void *arg) +{ +  int ret; + +  errno = 0; + +  ret = ioctl (fd, op, arg); + +  printf ("ioctl(%d,%s(0x%x),%p)=%d  (errno=%d(%s))\n", fd, sop, op, arg, ret, +          errno, strerror (errno)); + +  return ret; +} + +int +main (int argc, char *argv[]) +{ +  struct cp210x_port_config config; + +  int fd = open (argv[1], O_RDWR); + +  memset (&config, 0, sizeof (config)); + +// config.reset.mode=0xf05f; +  config.reset.mode = ~CP_INPUT_PINS; +  config.reset.low_power = 0; +  config.reset.latch = CP_INPUT_PINS | CP_PIN_GPIO_1 | CP_PIN_GPIO_2; +// config.suspend.mode=0xf05f; +  config.suspend.mode = ~CP_INPUT_PINS; +  config.suspend.low_power = 0; +  config.suspend.latch = CP_INPUT_PINS | CP_PIN_GPIO_1 | CP_PIN_GPIO_2; +// config.enhanced_fxn=0x30; +  config.enhanced_fxn = CP_EFXN_ENABLE_WPU; + + +  log_ioctl (fd, CPIOC_PORTCONFSET, &config); +  log_ioctl (fd, CPIOC_PORTCONFGET, &config); + +  printf ("reset=(%x,%x,%x), suspend=(%x,%x,%x), enhanced_fxn=%x\n", +          config.reset.mode, +          config.reset.low_power, +          config.reset.latch, +          config.suspend.mode, +          config.suspend.low_power, +          config.suspend.latch, config.enhanced_fxn); + +  log_ioctl (fd, CPIOC_DEVICERESET, 0); + +} diff --git a/kernel/code/thing.c b/kernel/code/thing.c new file mode 100644 index 0000000..d9b5eac --- /dev/null +++ b/kernel/code/thing.c @@ -0,0 +1,207 @@ +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <linux/cp210x.h> + +/* + * bit 0: 1	left  leg NPN base + * bit 1: 2	left  leg PNP base + * bit 2: 4	right leg PNP base + * bit 3: 8	right leg NPN base + */ + +#define LEFT_NPN_OFF	0 +#define LEFT_NPN_ON	1 +#define LEFT_PNP_OFF	2 +#define LEFT_PNP_ON	0 + +#define LEFT_LOW	(LEFT_NPN_ON | LEFT_PNP_OFF) +#define LEFT_HI		(LEFT_NPN_OFF | LEFT_PNP_ON) +#define LEFT_OFF	(LEFT_NPN_OFF | LEFT_PNP_OFF) + +#define RIGHT_NPN_OFF	0 +#define RIGHT_NPN_ON	8 +#define RIGHT_PNP_OFF	4 +#define RIGHT_PNP_ON	0 + + +#define RIGHT_LOW	(RIGHT_NPN_ON | RIGHT_PNP_OFF) +#define RIGHT_HI	(RIGHT_NPN_OFF | RIGHT_PNP_ON) +#define RIGHT_OFF	(RIGHT_NPN_OFF | RIGHT_PNP_OFF) + +#define FORWARDS	(RIGHT_LOW | LEFT_HI) +#define BACKWARDS	(RIGHT_HI | LEFT_LOW) +#define OFF		(RIGHT_OFF| LEFT_OFF) + +#define TIMEOUT 	750000         /*75ms */ +#define RUN_IN 		50 +#define RELEASE_TENSION	10 + +int +drive_motor (int fd, int dir) +{ +  int i; + +  if (dir > 0) +    { +      printf (" Motor forwards\n"); +      i = FORWARDS; +    } +  else if (dir < 0) +    { +      printf (" Motor backwards\n"); +      i = BACKWARDS; +    } +  else +    { +      printf (" Motor off\n"); +      i = OFF; +    } + + +  return ioctl (fd, CPIOC_GPIOSET, &i); +} + +int +sensor_led_power (int fd, int on) +{ +  int i = TIOCM_DTR; + +  printf (" Rotation sensor power %s\n", on ? "on" : "off"); + +  return ioctl (fd, on ? TIOCMBIS : TIOCMBIC, &i); +} + + +static int +count_steps (int fd, int count) +{ +  struct timeval tv, tv_changed, tv_diff; +  int cts, old_cts = -1; +  int counted = 0; + +  printf (" Counting %d steps", count); + +  gettimeofday (&tv_changed, NULL); + +  while (count) +    { +      ioctl (fd, TIOCMGET, &cts); +      cts &= TIOCM_CTS; + +      if (cts != old_cts) +        { +          if (cts) +            { +              counted++; +              count--; +              printf ("."); +              fflush (stdout); +            } +          gettimeofday (&tv_changed, NULL); +          old_cts = cts; +        } +      gettimeofday (&tv, NULL); +      timersub (&tv, &tv_changed, &tv_diff); + +      if (tv_diff.tv_sec || (tv_diff.tv_usec > TIMEOUT)) +        break; + +    } + +  printf ("%d steps\n", counted); +  return counted; +} + + + + +static int +drive_motor_home (int fd) +{ +  int ret; +  sensor_led_power (fd, 1); +  drive_motor (fd, -1); +  ret = count_steps (fd, -1); +  drive_motor (fd, 0); +  ret -= count_steps (fd, -1); +  sensor_led_power (fd, 0); +  return ret; +} + +static int +drive_motor_count (int fd, int dir, int steps) +{ +  int ret; +  sensor_led_power (fd, 1); +  drive_motor (fd, dir); +  ret = count_steps (fd, steps); +  drive_motor (fd, 0); +  ret += count_steps (fd, -1); +  sensor_led_power (fd, 0); +  return ret; +} + +int +main (int argc, char *argv[]) +{ +  int overshoot, guard, pos, wanted, delta; + +  int fd = open (argv[1], O_RDWR); + +  if (fd < 0) +    return -1; + + +  pos = drive_motor_home (fd); + +  printf ("Motor was %d steps from home\n", pos); + + +  if (argc == 2) +    { +      pos = drive_motor_count (fd, 1, RELEASE_TENSION); + +      printf ("Motor now %d steps from home\n", pos); +      return 0; +    } + +  pos = drive_motor_count (fd, 1, RUN_IN); + +  overshoot = pos - RUN_IN;     /*overshot */ + +  wanted = atoi (argv[2]); + +  delta = wanted - pos; +  printf ("Now at %d want %d, delta %d\n", pos, wanted, delta); + +  guard = overshoot * 3; + +  if ((delta > -guard) && (delta < guard)) +    { +      printf ("Too close\n"); +      pos += drive_motor_count (fd, 1, RUN_IN); +      delta = wanted - pos; +      printf ("Now at %d want %d, delta %d\n", pos, wanted, delta); +    } + + +  if (delta < 0) +    { +      pos -= drive_motor_count (fd, -1, (-delta) - overshoot); +    } +  else if (delta > 0) +    { +      pos += drive_motor_count (fd, 1, delta - overshoot); +    } + +  printf ("Wanted %d steps, got %d steps\n", wanted, pos); + +  return 0; +} diff --git a/kernel/cp2103-3.11.10.patch b/kernel/cp2103-3.11.10.patch new file mode 100644 index 0000000..949d962 --- /dev/null +++ b/kernel/cp2103-3.11.10.patch @@ -0,0 +1,491 @@ +diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c +index 0eae4ba..3e0b558 100644 +--- a/drivers/usb/serial/cp210x.c ++++ b/drivers/usb/serial/cp210x.c +@@ -23,14 +23,22 @@ + #include <linux/usb.h> + #include <linux/uaccess.h> + #include <linux/usb/serial.h> ++#include <linux/cp210x.h> +  + #define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver" +  ++#define	CP210x_MAX_DEVICE_STRLEN	256 ++#define	CP210x_MAX_PRODUCT_STRLEN	126 ++#define	CP210x_MAX_SERIAL_STRLEN	63 ++#define	CP210x_MAX_MAXPOWER		250 ++ ++ + /* +  * Function Prototypes +  */ + static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *); + static void cp210x_close(struct usb_serial_port *); ++static int cp210x_ioctl(struct tty_struct *, struct file *, unsigned long); + static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *); + static void cp210x_get_termios_port(struct usb_serial_port *port, + 	unsigned int *cflagp, unsigned int *baudp); +@@ -198,6 +206,7 @@ static struct usb_serial_driver cp210x_device = { + 	.bulk_out_size		= 256, + 	.open			= cp210x_open, + 	.close			= cp210x_close, ++	.ioctl			= cp210x_ioctl, + 	.break_ctl		= cp210x_break_ctl, + 	.set_termios		= cp210x_set_termios, + 	.tiocmget		= cp210x_tiocmget, +@@ -286,6 +295,185 @@ static struct usb_serial_driver * const serial_drivers[] = { + #define CONTROL_WRITE_DTR	0x0100 + #define CONTROL_WRITE_RTS	0x0200 +  ++ ++ ++/* ++ * cp210x_ctlmsg ++ * A generic usb control message interface. ++ * Returns the actual size of the data read or written within the message, 0 ++ * if no data were read or written, or a negative value to indicate an error. ++ */ ++static int cp210x_ctlmsg(struct usb_serial_port *port, u8 request, ++		u8 requestype, u16 value, u16 index, void *data, u16 size) ++{ ++	struct usb_serial *serial = port->serial; ++	u8 *tbuf; ++	int ret; ++ ++	if (!(tbuf = kmalloc(size, GFP_KERNEL))) ++		return -ENOMEM; ++	if (requestype & 0x80) { ++		ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), request, ++				requestype, value, index, tbuf, size, 300); ++		if (ret > 0 && size) ++			memcpy(data, tbuf, size); ++	} else { ++		if (size) ++			memcpy(tbuf, data, size); ++		ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), request, ++				requestype, value, index, tbuf, size, 300); ++	} ++	kfree(tbuf); ++	if (ret < 0 && ret != -EPIPE) { ++		dev_err(&port->dev, "cp210x: control failed cmd rqt %u " ++				"rq %u len %u ret %d\n", requestype, request, size, ret); ++	} ++	return ret; ++} ++ ++ ++static int cp210x_reset(struct usb_serial_port *port) ++{ ++	/* Is this better than usb_device_reset?  It may be.  Once a client issues ++	 * the reset ioctl, it must disconnect and reconnect, since the USB ++	 * connections are torn down.  We also ignore the error return, since ++	 * the part resets and doesn't send one... ++	 */ ++	cp210x_ctlmsg(port, 0xff, 0x40, 0x0008, 0x00, 0, 0); ++ ++	return 0; ++} ++ ++// // cp210x_get_partnum ++ ++static inline int cp210x_setu16(struct usb_serial_port *port, int cmd, ++		unsigned int value) ++{ ++	return cp210x_ctlmsg(port, 0xff, 0x40, 0x3700 | (cmd & 0xff), ++			value, 0, 0); ++} ++ ++ ++// make_usb_string ++/* ++ * cp210x_setstr ++ * ++ * Set a USB string descriptor using proprietary cp210x control messages. ++ * Return the number of characters actually written. ++ */ ++static int cp210x_setstr(struct usb_serial_port *port, int cmd, uint8_t *usbstr,size_t len) ++{ ++	int ret = cp210x_ctlmsg(port, 0xff, 0x40, 0x3700 | (cmd & 0xff), 0, ++			usbstr, len); ++	dbg("%s - cmd 0x%02x len %d ret %d", __FUNCTION__, cmd, len, ret); ++	printk(KERN_ERR "%s - cmd 0x%02x len %d ret %d %x %x %x %x %x %x", __FUNCTION__, cmd, len, ret, ++		usbstr[0],usbstr[1],usbstr[2],usbstr[3],usbstr[4],usbstr[5]); ++	return ret; ++} ++  ++ ++ ++/* Set all gpio simultaneously */ ++static int cp210x_gpioset(struct usb_serial_port *port, int gpio) ++{ ++	dbg("%s - port %d, gpio = 0x%.2x", __FUNCTION__, port->number, gpio); ++ ++	return cp210x_ctlmsg(port, 0xff, 0x40, 0x37e1, ++			((uint16_t)gpio << 8) | CP_GPIO_MASK, 0, 0); ++} ++ ++/* Set select gpio bits */ ++static int cp210x_gpiosetb(struct usb_serial_port *port, int set, int clear) ++{ ++	u16 gpio = 0; ++ ++	gpio |= set | clear; ++	gpio |= set << 8; ++ ++	dbg("%s - port %d, gpiob = 0x%.4x", __FUNCTION__, port->number, gpio); ++ ++	/* FIXME: how about REQTYPE_HOST_TO_DEVICE instead of 0x40? */ ++	return cp210x_ctlmsg(port, 0xff, 0x40, 0x37e1, gpio, 0, 0); ++} ++ ++static int cp210x_gpioget(struct usb_serial_port *port, int *gpio_ret) ++{ ++	int ret; ++        u8 gpio; ++ ++	dbg("%s - port %d", __FUNCTION__, port->number); ++ ++	/* FIXME: how about REQTYPE_DEVICE_TO_HOST instead of 0xc0? */ ++	ret = cp210x_ctlmsg(port, 0xff, 0xc0, 0x00c2, 0, &gpio, 1); ++ ++	dbg("%s - gpio = 0x%.2x (%d)", __FUNCTION__, gpio, ret); ++ ++       *gpio_ret=gpio; ++ ++	return (ret == 1) ? 0 : -EPROTO; ++} ++ ++static int cp210x_portconfset(struct usb_serial_port *port, ++		struct cp210x_port_config *config) ++{ ++	struct cp210x_port_config be_config; ++	int ret; ++ ++	dbg("%s", __FUNCTION__); ++ ++	memset(&be_config, 0, sizeof(be_config)); ++ ++	be_config.reset.mode 		= cpu_to_be16(config->reset.mode); ++	be_config.reset.low_power 	= 0; // cpu_to_be16(config->reset.low_power); ++	be_config.reset.latch		= cpu_to_be16(config->reset.latch); ++ ++	be_config.suspend.mode 		= cpu_to_be16(config->suspend.mode); ++	be_config.suspend.low_power 	= 0; // cpu_to_be16(config->suspend.low_power); ++	be_config.suspend.latch		= cpu_to_be16(config->suspend.latch); ++ ++	be_config.enhanced_fxn		= config->enhanced_fxn; ++ ++ ++	ret = cp210x_ctlmsg(port, 0xff, 0x40, 0x370c, 0, &be_config, sizeof(be_config)); ++	if (ret == sizeof(be_config)) ++		return 0; ++	else if (ret >= 0) ++		return -EPROTO; ++	else ++		return ret; ++} ++ ++static int cp210x_portconfget(struct usb_serial_port *port, ++		struct cp210x_port_config *config) ++{ ++	int ret; ++ ++	dbg("%s", __FUNCTION__); ++ ++	ret = cp210x_ctlmsg(port, 0xff, 0xc0, 0x370c, 0, config, sizeof(*config)); ++ ++	if (ret != sizeof(*config))  ++		return (ret >= 0) ? -EPROTO : ret; ++ ++	/* Words from cp2103 are MSB */ ++ ++	config->reset.mode = be16_to_cpu(config->reset.mode); ++	config->reset.low_power = be16_to_cpu(config->reset.low_power); ++	config->reset.latch = be16_to_cpu(config->reset.latch); ++ ++	config->suspend.mode = be16_to_cpu(config->suspend.mode); ++	config->suspend.low_power = be16_to_cpu(config->suspend.low_power); ++	config->suspend.latch = be16_to_cpu(config->suspend.latch); ++ ++	/* apparently not implemented yet */ ++	config->reset.low_power = 0; ++	config->suspend.low_power = 0; ++ ++	return 0; ++} ++ ++ ++ + /* +  * cp210x_get_config +  * Reads from the CP210x configuration registers +@@ -469,6 +657,163 @@ static void cp210x_close(struct usb_serial_port *port) + 	cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE); + } +  ++static int cp210x_string_ioctl(struct usb_serial_port *port, unsigned long arg, int cmd,size_t max_len) ++{ ++	uint8_t buf[CP210x_MAX_STRLEN]; ++	size_t len; ++ ++	if (copy_from_user(buf,(uint8_t *)arg,1)) ++		return -EFAULT; ++ ++	len=buf[0]; ++ ++	if (len>max_len) ++		return -EMSGSIZE; ++ ++	if (copy_from_user(buf,(uint8_t *)arg,len)) ++		return -EFAULT; ++ ++	if (len && cp210x_setstr(port, cmd, buf, len) != len) ++		return -EPROTO; ++ ++	return 0; ++} ++ ++  ++static int cp210x_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) ++{ ++	struct usb_serial_port *port = tty->driver_data; ++	struct cp210x_port_config config; ++  	int iarg; ++	int ret = -ENOIOCTLCMD; ++ ++	dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd); ++ ++	switch (cmd) { ++		case CPIOC_GPIOGET: ++			/*if (cp210x_get_partnum(port) != CP210x_CP2103_VERSION) break; */ ++			ret = cp210x_gpioget(port, &iarg); ++ ++			if (ret) ++				break; ++			 ++			if (copy_to_user((int *)arg, &iarg, sizeof(iarg))) ++				ret = -EFAULT; ++ ++			break; ++ ++		case CPIOC_GPIOSET: ++			/*if (cp210x_get_partnum(port) != CP210x_CP2103_VERSION) break; */ ++ ++                        if (copy_from_user(&iarg,(int *)arg, sizeof(iarg))) { ++                                ret = -EFAULT; ++				break; ++			} ++ ++			ret = cp210x_gpioset(port, iarg); ++ ++			break; ++ ++		case CPIOC_GPIOBIS: ++			/*if (cp210x_get_partnum(port) != CP210x_CP2103_VERSION) break; */ ++ ++                        if (copy_from_user(&iarg, (int *)arg, sizeof(iarg))) { ++                                ret = -EFAULT; ++				break; ++			} ++ ++			ret = cp210x_gpiosetb(port, iarg, 0); ++ ++			break; ++ ++		case CPIOC_GPIOBIC: ++			/*if (cp210x_get_partnum(port) != CP210x_CP2103_VERSION) break; */ ++ ++                        if (copy_from_user(&iarg, (int *)arg, sizeof(iarg))) { ++                                ret = -EFAULT; ++				break; ++			} ++ ++			ret = cp210x_gpiosetb(port, 0, iarg); ++ ++			break; ++ ++		case CPIOC_DEVICERESET: ++			ret = cp210x_reset(port); ++			break; ++ ++		case CPIOC_PORTCONFGET: ++			ret = cp210x_portconfget(port, &config); ++ ++			if (ret) break; ++ ++                        if (copy_to_user((struct cp210x_port_state *)arg, &config, sizeof(config)))  ++                                ret = -EFAULT; ++ ++			break; ++ ++		case CPIOC_PORTCONFSET: ++		        if (copy_from_user(&config, (struct cp210x_port_state *)arg, sizeof(config))) { ++                                ret = -EFAULT; ++				break; ++			} ++ ++			ret = cp210x_portconfset(port, &config); ++			break; ++ ++		case CPIOC_SETVID: ++			if (copy_from_user(&iarg, (int *)arg,sizeof(iarg))) { ++                                ret = -EFAULT; ++				break; ++			} ++ ++			ret = cp210x_setu16(port, 0x01, iarg); ++			 ++			break; ++ ++		case CPIOC_SETPID: ++			if (copy_from_user(&iarg, (int *)arg,sizeof(iarg))) { ++                                ret = -EFAULT; ++				break; ++			} ++ ++			ret = cp210x_setu16(port, 0x02, iarg); ++			 ++			break; ++ ++		case CPIOC_SETMFG: ++			ret = cp210x_string_ioctl(port, arg, 0x0, CP210x_MAX_MFG_STRLEN); ++			break; ++ ++		case CPIOC_SETPRODUCT: ++			ret = cp210x_string_ioctl(port, arg, 0x3, CP210x_MAX_PRODUCT_STRLEN); ++ ++			break; ++ ++		case CPIOC_SETSERIAL: ++			ret = cp210x_string_ioctl(port, arg, 0x3, CP210x_MAX_SERIAL_STRLEN); ++			break; ++ ++		case CPIOC_SETDEVVER: ++			if (copy_from_user(&iarg, (int *)arg,sizeof(iarg))) { ++                                ret = -EFAULT; ++				break; ++			} ++ ++			ret = cp210x_setu16(port, 0x07, iarg); ++			 ++			break; ++		default: ++			dbg("%s not supported = 0x%04x", __FUNCTION__, cmd); ++		break; ++	} ++ ++	return ret; ++} ++ ++ ++ ++ + /* +  * cp210x_get_termios +  * Reads the baud rate, data bits, parity, stop bits and flow control mode +diff --git a/include/linux/cp210x.h b/include/linux/cp210x.h +new file mode 100644 +index 0000000..39faf96 +--- /dev/null ++++ b/include/linux/cp210x.h +@@ -0,0 +1,100 @@ ++/* ++ * ++ */ ++#ifndef	_CPIO210X_H ++#define	_CPIO210X_H ++ ++#define CP210X_IOCTL_BASE     'C' ++ ++//struct watchdog_info { ++//        __u32 options;          /* Options the card/driver supports */ ++//        __u32 firmware_version; /* Firmware version of the card */ ++//        __u8  identity[32];     /* Identity of the board */ ++//}; ++ ++struct cp210x_port_state { ++	uint16_t mode;		/* push-pull = 1, open-drain = 0 */ ++	uint16_t low_power;     /* 1 = ground the pin, and disable */ ++	uint16_t latch;  ++} __attribute__((packed)); ++ ++/* LOPWR MODE LATCH */ ++/*     0    0     0     Pin drivers are totem pole, and inital config sinks current to GND */ ++/*     0    0     1    Pin drivers are totem pole, and inital config sources current from VIO */ ++/*     0    1     0    Pin drivers are open drain, and inital config sinks current to GND */ ++/*     0    1     1    Pin drivers are open drain, and inital config leaves pin HI-Z (you want this for inputs) */ ++/*     1    X     X    Pin drivers are disabled, and pin sinks current to GND */ ++ ++ ++struct cp210x_port_config { ++	struct cp210x_port_state reset; ++	struct cp210x_port_state suspend; ++	uint8_t enhanced_fxn; ++} __attribute__((packed)); ++ ++  ++#define CP_PIN_RI		(1 <<  0) ++#define CP_PIN_DCD		(1 <<  1) ++#define CP_PIN_DTR		(1 <<  2) ++#define CP_PIN_DSR		(1 <<  3) ++#define CP_PIN_TXD		(1 <<  4) ++#define CP_PIN_RXD		(1 <<  5) ++#define CP_PIN_RTS		(1 <<  6) ++#define CP_PIN_CTS		(1 <<  7) ++#define CP_PIN_GPIO_0		(1 <<  8) ++#define CP_PIN_GPIO_1		(1 <<  9) ++#define CP_PIN_GPIO_2		(1 << 10) ++#define CP_PIN_GPIO_3		(1 << 11) ++#define CP_PIN_UNUSED_1		(1 << 12) ++#define CP_PIN_UNUSED_2		(1 << 13) ++#define CP_PIN_GPIO_SUSPEND	(1 << 14) ++#define CP_PIN_GPIO_NSUSPEND	(1 << 15) ++ ++#define CP_INPUT_PINS		(CP_PIN_RXD|CP_PIN_CTS|CP_PIN_DSR|CP_PIN_RI|CP_PIN_DCD) ++ ++ ++#define CP_EFXN_GPIO_0_IS_TXLED		(1 << 0) ++#define CP_EFXN_GPIO_1_IS_RXLED		(1 << 1) ++#define CP_EFXN_GPIO_2_IS_RS485_TX	(1 << 2) ++#define CP_EFXN_UNUSED_1		(1 << 3)	/* Set to zero */ ++#define CP_EFXN_ENABLE_WPU		(1 << 4)	 ++#define CP_EFXN_UNUSED_2		(1 << 5)	/* Set to zero */ ++#define CP_EFXN_SERIAL_AUTOOFF		(1 << 6)	 ++#define CP_EFXN_GPIOL_AUTOOFF		(1 << 7)	 ++ ++  ++  ++ ++ ++ ++#define CPIOC_GPIOGET		_IOR(CP210X_IOCTL_BASE, 0, int) ++#define CPIOC_GPIOSET		_IOW(CP210X_IOCTL_BASE, 1, int) ++#define CPIOC_GPIOBIC		_IOW(CP210X_IOCTL_BASE, 2, int) ++#define CPIOC_GPIOBIS		_IOW(CP210X_IOCTL_BASE, 3, int) ++ ++#define CPIOC_DEVICERESET	_IO(CP210X_IOCTL_BASE,4) ++#define CPIOC_PORTCONFGET	_IOR(CP210X_IOCTL_BASE, 5, struct cp210x_port_config) ++#define CPIOC_PORTCONFSET	_IOW(CP210X_IOCTL_BASE, 6, struct cp210x_port_config) ++ ++#define CPIOC_SETVID		_IOW(CP210X_IOCTL_BASE,7, int) ++#define CPIOC_SETPID		_IOW(CP210X_IOCTL_BASE,8, int) ++#define CPIOC_SETMFG		_IOW(CP210X_IOCTL_BASE,9, char *) ++#define CPIOC_SETPRODUCT	_IOW(CP210X_IOCTL_BASE,10, char *) ++#define CPIOC_SETSERIAL		_IOW(CP210X_IOCTL_BASE,11, char *) ++#define CPIOC_SETDEVVER		_IOW(CP210X_IOCTL_BASE,12, int) ++ ++/* CP2103 GPIO */ ++#define CP_GPIO_0		0x01 ++#define CP_GPIO_1		0x02 ++#define CP_GPIO_2		0x04 ++#define CP_GPIO_3		0x08 ++#define CP_GPIO_MASK		(CP_GPIO_0|CP_GPIO_1|CP_GPIO_2|CP_GPIO_3) ++ ++ ++#define CP210x_MAX_MFG_STRLEN           128 ++#define	CP210x_MAX_PRODUCT_STRLEN	126 ++#define	CP210x_MAX_SERIAL_STRLEN	63 ++#define	CP210x_MAX_MAXPOWER		250 ++ ++#endif /* _CPIO210X_H */ ++  | 
