Here is a driver for the usb servo controllers from Phidgets
<http://www.phidgets.com/>, using sysfs.
Note that the devices claim to be hid devices, so I've added them to the
hid_blacklist (HID_QUIRK_IGNORE). A servo controller isn't really an hid
device (or is it?).
diff against 2.6.6-rc2.
Sean
diff -Nur linux-2.6.0/drivers/usb/Makefile /usr/src/linux-2.6.0/drivers/usb/Makefile
--- linux-2.6.0/drivers/usb/Makefile 2004-04-22 11:25:19.000000000 +0200
+++ /usr/src/linux-2.6.0/drivers/usb/Makefile 2004-04-21 18:02:00.000000000 +0200
@@ -66,3 +66,4 @@
obj-$(CONFIG_USB_TEST) += misc/
obj-$(CONFIG_USB_TIGL) += misc/
obj-$(CONFIG_USB_USS720) += misc/
+obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
diff -Nur linux-2.6.0/drivers/usb/input/hid-core.c /usr/src/linux-2.6.0/drivers/usb/input/hid-core.c
--- linux-2.6.0/drivers/usb/input/hid-core.c 2004-04-12 19:36:21.000000000 +0200
+++ /usr/src/linux-2.6.0/drivers/usb/input/hid-core.c 2004-04-22 11:54:25.000000000 +0200
@@ -1412,6 +1412,14 @@
#define USB_VENDOR_ID_CHIC 0x05fe
#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014
+#define USB_VENDOR_ID_GLAB 0x06c2
+#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
+#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
+
+#define USB_VENDOR_ID_WISEGROUP 0x0925
+#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
+#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
+
struct hid_blacklist {
__u16 idVendor;
__u16 idProduct;
@@ -1459,6 +1467,10 @@
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 7, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_VOLITO, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PTU, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
diff -Nur linux-2.6.0/drivers/usb/misc/Kconfig /usr/src/linux-2.6.0/drivers/usb/misc/Kconfig
--- linux-2.6.0/drivers/usb/misc/Kconfig 2004-04-22 11:25:20.000000000 +0200
+++ /usr/src/linux-2.6.0/drivers/usb/misc/Kconfig 2004-04-21 18:02:00.000000000 +0200
@@ -133,6 +133,18 @@
To compile this driver as a module, choose M here: the
module will be called speedtch.
+config USB_PHIDGETSERVO
+ tristate "USB PhidgetServo support"
+ depends on USB
+ help
+ Say Y here if you want to connect an 1 or 4 Motor PhidgetServo
+ servo controller version 2.0 or 3.0.
+
+ Phidgets Inc. has a web page at <http://www.phidgets.com/>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called phidgetservo.
+
config USB_TEST
tristate "USB testing driver (DEVELOPMENT)"
depends on USB && USB_DEVICEFS && EXPERIMENTAL
diff -Nur linux-2.6.0/drivers/usb/misc/Makefile /usr/src/linux-2.6.0/drivers/usb/misc/Makefile
--- linux-2.6.0/drivers/usb/misc/Makefile 2004-04-22 11:25:20.000000000 +0200
+++ /usr/src/linux-2.6.0/drivers/usb/misc/Makefile 2004-04-21 18:02:00.000000000 +0200
@@ -15,3 +15,4 @@
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_TIGL) += tiglusb.o
obj-$(CONFIG_USB_USS720) += uss720.o
+obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o
diff -Nur linux-2.6.0/drivers/usb/misc/phidgetservo.c /usr/src/linux-2.6.0/drivers/usb/misc/phidgetservo.c
--- linux-2.6.0/drivers/usb/misc/phidgetservo.c 1970-01-01 01:00:00.000000000 +0100
+++ /usr/src/linux-2.6.0/drivers/usb/misc/phidgetservo.c 2004-04-27 13:03:07.000000000 +0200
@@ -0,0 +1,330 @@
+/*
+ * USB PhidgetServo driver 1.0
+ *
+ * Copyright (C) 2004 Sean Young <[email protected]>
+ *
+ * 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 is a driver for the USB PhidgetServo version 2.0 and 3.0 servo
+ * controllers available at: http://www.phidgets.com/
+ *
+ * Note that the driver takes input as: degrees.minutes
+ * -23 < degrees < 203
+ * 0 < minutes < 59
+ *
+ * CAUTION: Generally you should use 0 < degrees < 180 as anything else
+ * is probably beyond the range of your servo and may damage it.
+ */
+
+#include <linux/config.h>
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG 1
+#endif
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#define DRIVER_AUTHOR "Sean Young <[email protected]>"
+#define DRIVER_DESC "USB PhidgetServo Driver"
+
+#define VENDOR_ID_GLAB 0x06c2
+#define DEVICE_ID_4MOTOR_SERVO_30 0x0038
+#define DEVICE_ID_1MOTOR_SERVO_30 0x0039
+
+#define VENDOR_ID_WISEGROUP 0x0925
+#define DEVICE_ID_1MOTOR_SERVO_20 0x8101
+#define DEVICE_ID_4MOTOR_SERVO_20 0x8104
+
+static struct usb_device_id id_table[] = {
+ {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_4MOTOR_SERVO_30)},
+ {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_1MOTOR_SERVO_30)},
+ {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_4MOTOR_SERVO_20)},
+ {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_1MOTOR_SERVO_20)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct phidget_servo {
+ struct usb_device *udev;
+ int version;
+ int quad_servo;
+ int pulse[4];
+ int degrees[4];
+ int minutes[4];
+};
+
+static void
+change_position_v30(struct phidget_servo *servo, int servo_no, int degrees,
+ int minutes)
+{
+ int retval;
+ unsigned char *buffer;
+
+ /*
+ * pulse = 0 - 4095
+ * angle = 0 - 180 degrees
+ *
+ * pulse = angle * 10.6 + 243.8
+ */
+ servo->pulse[servo_no] = ((degrees*60 + minutes)*106 + 2438*60)/600;
+ servo->degrees[servo_no]= degrees;
+ servo->minutes[servo_no]= minutes;
+
+ buffer = kmalloc(6, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&servo->udev->dev, "%s - out of memory\n",
+ __FUNCTION__);
+ return;
+ }
+
+ /*
+ * The PhidgetServo v3.0 is controlled by sending 6 bytes,
+ * 4 * 12 bits for each servo.
+ *
+ * low = lower 8 bits pulse
+ * high = higher 4 bits pulse
+ *
+ * offset bits
+ * +---+-----------------+
+ * | 0 | low 0 |
+ * +---+--------+--------+
+ * | 1 | high 1 | high 0 |
+ * +---+--------+--------+
+ * | 2 | low 1 |
+ * +---+-----------------+
+ * | 3 | low 2 |
+ * +---+--------+--------+
+ * | 4 | high 3 | high 2 |
+ * +---+--------+--------+
+ * | 5 | low 3 |
+ * +---+-----------------+
+ */
+
+ buffer[0] = servo->pulse[0] & 0xff;
+ buffer[1] = (servo->pulse[0] >> 8 & 0x0f)
+ | (servo->pulse[1] >> 4 & 0xf0);
+ buffer[2] = servo->pulse[1] & 0xff;
+ buffer[3] = servo->pulse[2] & 0xff;
+ buffer[4] = (servo->pulse[2] >> 8 & 0x0f)
+ | (servo->pulse[3] >> 4 & 0xf0);
+ buffer[5] = servo->pulse[3] & 0xff;
+
+ dev_dbg(&servo->udev->dev,
+ "data: %02x %02x %02x %02x %02x %02x\n",
+ buffer[0], buffer[1], buffer[2],
+ buffer[3], buffer[4], buffer[5]);
+
+ retval = usb_control_msg(servo->udev,
+ usb_sndctrlpipe(servo->udev, 0),
+ 0x09, 0x21, 0x0200, 0x0000, buffer, 6, 2 * HZ);
+ if (retval != 6)
+ dev_err(&servo->udev->dev, "retval = %d\n", retval);
+ kfree(buffer);
+}
+
+static void
+change_position_v20(struct phidget_servo *servo, int servo_no, int degrees,
+ int minutes)
+{
+ int retval;
+ unsigned char *buffer;
+
+ /*
+ * angle = 0 - 180 degrees
+ * pulse = angle + 23
+ */
+ servo->pulse[servo_no]= degrees + 23;
+ servo->degrees[servo_no]= degrees;
+ servo->minutes[servo_no]= 0;
+
+ buffer = kmalloc(2, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&servo->udev->dev, "%s - out of memory\n",
+ __FUNCTION__);
+ return;
+ }
+
+ /*
+ * The PhidgetServo v2.0 is controlled by sending two bytes. The
+ * first byte is the servo number xor'ed with 2:
+ *
+ * servo 0 = 2
+ * servo 1 = 3
+ * servo 2 = 0
+ * servo 3 = 1
+ *
+ * The second byte is the position.
+ */
+
+ buffer[0] = servo_no ^ 2;
+ buffer[1] = servo->pulse[servo_no];
+
+ dev_dbg(&servo->udev->dev, "data: %02x %02x\n", buffer[0], buffer[1]);
+
+ retval = usb_control_msg(servo->udev,
+ usb_sndctrlpipe(servo->udev, 0),
+ 0x09, 0x21, 0x0200, 0x0000, buffer, 2, 2 * HZ);
+ if (retval != 2)
+ dev_err(&servo->udev->dev, "retval = %d\n", retval);
+ kfree(buffer);
+}
+
+#define show_set(value) \
+static ssize_t set_servo##value (struct device *dev, \
+ const char *buf, size_t count) \
+{ \
+ int degrees, minutes; \
+ struct usb_interface *intf = to_usb_interface (dev); \
+ struct phidget_servo *servo = usb_get_intfdata (intf); \
+ \
+ minutes = 0; \
+ /* must at least convert degrees */ \
+ if (sscanf (buf, "%d.%d", °rees, &minutes) < 1) { \
+ return -EINVAL; \
+ } \
+ \
+ if (degrees < -23 || degrees > (180 + 23) || \
+ minutes < 0 || minutes > 59) { \
+ return -EINVAL; \
+ } \
+ \
+ if (servo->version >= 3) \
+ change_position_v30 (servo, value, degrees, minutes); \
+ else \
+ change_position_v20 (servo, value, degrees, minutes); \
+ \
+ return count; \
+} \
+ \
+static ssize_t show_servo##value (struct device *dev, char *buf) \
+{ \
+ struct usb_interface *intf = to_usb_interface (dev); \
+ struct phidget_servo *servo = usb_get_intfdata (intf); \
+ \
+ return sprintf (buf, "%d.%02d\n", servo->degrees[value], \
+ servo->minutes[value]); \
+} \
+static DEVICE_ATTR(servo##value, S_IWUGO | S_IRUGO, \
+ show_servo##value, set_servo##value);
+
+show_set(0);
+show_set(1);
+show_set(2);
+show_set(3);
+
+static int
+servo_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct phidget_servo *dev = NULL;
+
+ dev = kmalloc(sizeof (struct phidget_servo), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+ memset(dev, 0x00, sizeof (*dev));
+
+ dev->udev = usb_get_dev(udev);
+ switch (udev->descriptor.idVendor) {
+ case VENDOR_ID_WISEGROUP:
+ dev->version = 2;
+ break;
+ case VENDOR_ID_GLAB:
+ dev->version = 3;
+ break;
+ }
+ switch (udev->descriptor.idProduct) {
+ case DEVICE_ID_4MOTOR_SERVO_20:
+ case DEVICE_ID_4MOTOR_SERVO_30:
+ dev->quad_servo = 1;
+ break;
+ case DEVICE_ID_1MOTOR_SERVO_20:
+ case DEVICE_ID_1MOTOR_SERVO_30:
+ dev->quad_servo = 0;
+ break;
+ }
+
+ usb_set_intfdata(interface, dev);
+
+ device_create_file(&interface->dev, &dev_attr_servo0);
+ if (dev->quad_servo) {
+ device_create_file(&interface->dev, &dev_attr_servo1);
+ device_create_file(&interface->dev, &dev_attr_servo2);
+ device_create_file(&interface->dev, &dev_attr_servo3);
+ }
+
+ dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n",
+ dev->quad_servo ? 4 : 1, dev->version);
+ if (dev->version == 2)
+ dev_info(&interface->dev,
+ "WARNING: v2.0 not tested! Please report if it works.\n");
+
+ return 0;
+}
+
+static void
+servo_disconnect(struct usb_interface *interface)
+{
+ struct phidget_servo *dev;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ device_remove_file(&interface->dev, &dev_attr_servo0);
+ if (dev->quad_servo) {
+ device_remove_file(&interface->dev, &dev_attr_servo1);
+ device_remove_file(&interface->dev, &dev_attr_servo2);
+ device_remove_file(&interface->dev, &dev_attr_servo3);
+ }
+
+ usb_put_dev(dev->udev);
+
+ kfree(dev);
+
+ dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n",
+ dev->quad_servo ? 4 : 1, dev->version);
+
+ dev_info(&interface->dev,
+ "WARNING: version 2.0 not tested. Please report if this works.\n");
+}
+
+static struct usb_driver servo_driver = {
+ .owner = THIS_MODULE,
+ .name = "phidgetservo",
+ .probe = servo_probe,
+ .disconnect = servo_disconnect,
+ .id_table = id_table
+};
+
+static int __init
+phidget_servo_init(void)
+{
+ int retval = 0;
+
+ retval = usb_register(&servo_driver);
+ if (retval)
+ err("usb_register failed. Error number %d", retval);
+
+ return retval;
+}
+
+static void __exit
+phidget_servo_exit(void)
+{
+ usb_deregister(&servo_driver);
+}
+
+module_init(phidget_servo_init);
+module_exit(phidget_servo_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
On Wed, Apr 28, 2004 at 08:18:06PM +0200, Sean Young wrote:
> Here is a driver for the usb servo controllers from Phidgets
> <http://www.phidgets.com/>, using sysfs.
>
> Note that the devices claim to be hid devices, so I've added them to the
> hid_blacklist (HID_QUIRK_IGNORE). A servo controller isn't really an hid
> device (or is it?).
>
> diff against 2.6.6-rc2.
Nice, I like tiny clean drivers like this :)
I've applied it to my trees, and it will make it into the next -mm tree,
and show up in the 2.6.7 release whenever it happens.
thanks,
greg k-h
On Wed, Apr 28, 2004 at 11:41:38AM -0700, Greg KH wrote:
> On Wed, Apr 28, 2004 at 08:18:06PM +0200, Sean Young wrote:
> > Here is a driver for the usb servo controllers from Phidgets
> > <http://www.phidgets.com/>, using sysfs.
> >
> > Note that the devices claim to be hid devices, so I've added them to the
> > hid_blacklist (HID_QUIRK_IGNORE). A servo controller isn't really an hid
> > device (or is it?).
> >
> > diff against 2.6.6-rc2.
>
> Nice, I like tiny clean drivers like this :)
>
> I've applied it to my trees, and it will make it into the next -mm tree,
> and show up in the 2.6.7 release whenever it happens.
Great! Thanks.
Somehow I managed to send the wrong version. Here is a patch which fixes
that. (Remove a dev_info() which wasn't supposed to be there, and make sure
that everything is still consistent in the unlikely event that kmalloc()
fails). Just minor cleanups.
Sean
diff -Nur linux-2.6.0/drivers/usb/misc/phidgetservo.c /usr/src/linux-2.6.0/drivers/usb/misc/phidgetservo.c
--- linux-2.6.0/drivers/usb/misc/phidgetservo.c 2004-04-29 02:35:19.000000000 +0200
+++ /usr/src/linux-2.6.0/drivers/usb/misc/phidgetservo.c 2004-04-29 02:35:09.000000000 +0200
@@ -67,6 +67,13 @@
int retval;
unsigned char *buffer;
+ buffer = kmalloc(6, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&servo->udev->dev, "%s - out of memory\n",
+ __FUNCTION__);
+ return;
+ }
+
/*
* pulse = 0 - 4095
* angle = 0 - 180 degrees
@@ -77,13 +84,6 @@
servo->degrees[servo_no]= degrees;
servo->minutes[servo_no]= minutes;
- buffer = kmalloc(6, GFP_KERNEL);
- if (!buffer) {
- dev_err(&servo->udev->dev, "%s - out of memory\n",
- __FUNCTION__);
- return;
- }
-
/*
* The PhidgetServo v3.0 is controlled by sending 6 bytes,
* 4 * 12 bits for each servo.
@@ -136,6 +136,13 @@
int retval;
unsigned char *buffer;
+ buffer = kmalloc(2, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&servo->udev->dev, "%s - out of memory\n",
+ __FUNCTION__);
+ return;
+ }
+
/*
* angle = 0 - 180 degrees
* pulse = angle + 23
@@ -144,13 +151,6 @@
servo->degrees[servo_no]= degrees;
servo->minutes[servo_no]= 0;
- buffer = kmalloc(2, GFP_KERNEL);
- if (!buffer) {
- dev_err(&servo->udev->dev, "%s - out of memory\n",
- __FUNCTION__);
- return;
- }
-
/*
* The PhidgetServo v2.0 is controlled by sending two bytes. The
* first byte is the servo number xor'ed with 2:
@@ -291,9 +291,6 @@
dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n",
dev->quad_servo ? 4 : 1, dev->version);
-
- dev_info(&interface->dev,
- "WARNING: version 2.0 not tested. Please report if this works.\n");
}
static struct usb_driver servo_driver = {
Really cool Sean. I am currently working on the IfKit for both the
8/8/8 and the 0/8/8 on the textlcds. Also working on the textlcds
output.
If I get these done soon was thinking of moving to RFID, so that we can
also possibly throw in Chesters PAM auth module idea in.
Bryan
On Apr 28, 2004, at 2:18 PM, Sean Young wrote:
> Here is a driver for the usb servo controllers from Phidgets
> <http://www.phidgets.com/>, using sysfs.
>
> Note that the devices claim to be hid devices, so I've added them to
> the
> hid_blacklist (HID_QUIRK_IGNORE). A servo controller isn't really an
> hid
> device (or is it?).
>
> diff against 2.6.6-rc2.
>
>
> Sean
>
>
> diff -Nur linux-2.6.0/drivers/usb/Makefile
> /usr/src/linux-2.6.0/drivers/usb/Makefile
> --- linux-2.6.0/drivers/usb/Makefile 2004-04-22 11:25:19.000000000
> +0200
> +++ /usr/src/linux-2.6.0/drivers/usb/Makefile 2004-04-21
> 18:02:00.000000000 +0200
> @@ -66,3 +66,4 @@
> obj-$(CONFIG_USB_TEST) += misc/
> obj-$(CONFIG_USB_TIGL) += misc/
> obj-$(CONFIG_USB_USS720) += misc/
> +obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
> diff -Nur linux-2.6.0/drivers/usb/input/hid-core.c
> /usr/src/linux-2.6.0/drivers/usb/input/hid-core.c
> --- linux-2.6.0/drivers/usb/input/hid-core.c 2004-04-12
> 19:36:21.000000000 +0200
> +++ /usr/src/linux-2.6.0/drivers/usb/input/hid-core.c 2004-04-22
> 11:54:25.000000000 +0200
> @@ -1412,6 +1412,14 @@
> #define USB_VENDOR_ID_CHIC 0x05fe
> #define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014
>
> +#define USB_VENDOR_ID_GLAB 0x06c2
> +#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
> +#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
> +
> +#define USB_VENDOR_ID_WISEGROUP 0x0925
> +#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
> +#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
> +
> struct hid_blacklist {
> __u16 idVendor;
> __u16 idProduct;
> @@ -1459,6 +1467,10 @@
> { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 7,
> HID_QUIRK_IGNORE },
> { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_VOLITO, HID_QUIRK_IGNORE
> },
> { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PTU, HID_QUIRK_IGNORE },
> + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30,
> HID_QUIRK_IGNORE },
> + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30,
> HID_QUIRK_IGNORE },
> + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20,
> HID_QUIRK_IGNORE },
> + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20,
> HID_QUIRK_IGNORE },
>
> { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
> { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
> diff -Nur linux-2.6.0/drivers/usb/misc/Kconfig
> /usr/src/linux-2.6.0/drivers/usb/misc/Kconfig
> --- linux-2.6.0/drivers/usb/misc/Kconfig 2004-04-22 11:25:20.000000000
> +0200
> +++ /usr/src/linux-2.6.0/drivers/usb/misc/Kconfig 2004-04-21
> 18:02:00.000000000 +0200
> @@ -133,6 +133,18 @@
> To compile this driver as a module, choose M here: the
> module will be called speedtch.
>
> +config USB_PHIDGETSERVO
> + tristate "USB PhidgetServo support"
> + depends on USB
> + help
> + Say Y here if you want to connect an 1 or 4 Motor PhidgetServo
> + servo controller version 2.0 or 3.0.
> +
> + Phidgets Inc. has a web page at <http://www.phidgets.com/>.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called phidgetservo.
> +
> config USB_TEST
> tristate "USB testing driver (DEVELOPMENT)"
> depends on USB && USB_DEVICEFS && EXPERIMENTAL
> diff -Nur linux-2.6.0/drivers/usb/misc/Makefile
> /usr/src/linux-2.6.0/drivers/usb/misc/Makefile
> --- linux-2.6.0/drivers/usb/misc/Makefile 2004-04-22
> 11:25:20.000000000 +0200
> +++ /usr/src/linux-2.6.0/drivers/usb/misc/Makefile 2004-04-21
> 18:02:00.000000000 +0200
> @@ -15,3 +15,4 @@
> obj-$(CONFIG_USB_TEST) += usbtest.o
> obj-$(CONFIG_USB_TIGL) += tiglusb.o
> obj-$(CONFIG_USB_USS720) += uss720.o
> +obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o
> diff -Nur linux-2.6.0/drivers/usb/misc/phidgetservo.c
> /usr/src/linux-2.6.0/drivers/usb/misc/phidgetservo.c
> --- linux-2.6.0/drivers/usb/misc/phidgetservo.c 1970-01-01
> 01:00:00.000000000 +0100
> +++ /usr/src/linux-2.6.0/drivers/usb/misc/phidgetservo.c 2004-04-27
> 13:03:07.000000000 +0200
> @@ -0,0 +1,330 @@
> +/*
> + * USB PhidgetServo driver 1.0
> + *
> + * Copyright (C) 2004 Sean Young <[email protected]>
> + *
> + * 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 is a driver for the USB PhidgetServo version 2.0 and 3.0 servo
> + * controllers available at: http://www.phidgets.com/
> + *
> + * Note that the driver takes input as: degrees.minutes
> + * -23 < degrees < 203
> + * 0 < minutes < 59
> + *
> + * CAUTION: Generally you should use 0 < degrees < 180 as anything
> else
> + * is probably beyond the range of your servo and may damage it.
> + */
> +
> +#include <linux/config.h>
> +#ifdef CONFIG_USB_DEBUG
> +#define DEBUG 1
> +#endif
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +
> +#define DRIVER_AUTHOR "Sean Young <[email protected]>"
> +#define DRIVER_DESC "USB PhidgetServo Driver"
> +
> +#define VENDOR_ID_GLAB 0x06c2
> +#define DEVICE_ID_4MOTOR_SERVO_30 0x0038
> +#define DEVICE_ID_1MOTOR_SERVO_30 0x0039
> +
> +#define VENDOR_ID_WISEGROUP 0x0925
> +#define DEVICE_ID_1MOTOR_SERVO_20 0x8101
> +#define DEVICE_ID_4MOTOR_SERVO_20 0x8104
> +
> +static struct usb_device_id id_table[] = {
> + {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_4MOTOR_SERVO_30)},
> + {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_1MOTOR_SERVO_30)},
> + {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_4MOTOR_SERVO_20)},
> + {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_1MOTOR_SERVO_20)},
> + {}
> +};
> +
> +MODULE_DEVICE_TABLE(usb, id_table);
> +
> +struct phidget_servo {
> + struct usb_device *udev;
> + int version;
> + int quad_servo;
> + int pulse[4];
> + int degrees[4];
> + int minutes[4];
> +};
> +
> +static void
> +change_position_v30(struct phidget_servo *servo, int servo_no, int
> degrees,
> + int minutes)
> +{
> + int retval;
> + unsigned char *buffer;
> +
> + /*
> + * pulse = 0 - 4095
> + * angle = 0 - 180 degrees
> + *
> + * pulse = angle * 10.6 + 243.8
> + */
> + servo->pulse[servo_no] = ((degrees*60 + minutes)*106 + 2438*60)/600;
> + servo->degrees[servo_no]= degrees;
> + servo->minutes[servo_no]= minutes;
> +
> + buffer = kmalloc(6, GFP_KERNEL);
> + if (!buffer) {
> + dev_err(&servo->udev->dev, "%s - out of memory\n",
> + __FUNCTION__);
> + return;
> + }
> +
> + /*
> + * The PhidgetServo v3.0 is controlled by sending 6 bytes,
> + * 4 * 12 bits for each servo.
> + *
> + * low = lower 8 bits pulse
> + * high = higher 4 bits pulse
> + *
> + * offset bits
> + * +---+-----------------+
> + * | 0 | low 0 |
> + * +---+--------+--------+
> + * | 1 | high 1 | high 0 |
> + * +---+--------+--------+
> + * | 2 | low 1 |
> + * +---+-----------------+
> + * | 3 | low 2 |
> + * +---+--------+--------+
> + * | 4 | high 3 | high 2 |
> + * +---+--------+--------+
> + * | 5 | low 3 |
> + * +---+-----------------+
> + */
> +
> + buffer[0] = servo->pulse[0] & 0xff;
> + buffer[1] = (servo->pulse[0] >> 8 & 0x0f)
> + | (servo->pulse[1] >> 4 & 0xf0);
> + buffer[2] = servo->pulse[1] & 0xff;
> + buffer[3] = servo->pulse[2] & 0xff;
> + buffer[4] = (servo->pulse[2] >> 8 & 0x0f)
> + | (servo->pulse[3] >> 4 & 0xf0);
> + buffer[5] = servo->pulse[3] & 0xff;
> +
> + dev_dbg(&servo->udev->dev,
> + "data: %02x %02x %02x %02x %02x %02x\n",
> + buffer[0], buffer[1], buffer[2],
> + buffer[3], buffer[4], buffer[5]);
> +
> + retval = usb_control_msg(servo->udev,
> + usb_sndctrlpipe(servo->udev, 0),
> + 0x09, 0x21, 0x0200, 0x0000, buffer, 6, 2 * HZ);
> + if (retval != 6)
> + dev_err(&servo->udev->dev, "retval = %d\n", retval);
> + kfree(buffer);
> +}
> +
> +static void
> +change_position_v20(struct phidget_servo *servo, int servo_no, int
> degrees,
> + int minutes)
> +{
> + int retval;
> + unsigned char *buffer;
> +
> + /*
> + * angle = 0 - 180 degrees
> + * pulse = angle + 23
> + */
> + servo->pulse[servo_no]= degrees + 23;
> + servo->degrees[servo_no]= degrees;
> + servo->minutes[servo_no]= 0;
> +
> + buffer = kmalloc(2, GFP_KERNEL);
> + if (!buffer) {
> + dev_err(&servo->udev->dev, "%s - out of memory\n",
> + __FUNCTION__);
> + return;
> + }
> +
> + /*
> + * The PhidgetServo v2.0 is controlled by sending two bytes. The
> + * first byte is the servo number xor'ed with 2:
> + *
> + * servo 0 = 2
> + * servo 1 = 3
> + * servo 2 = 0
> + * servo 3 = 1
> + *
> + * The second byte is the position.
> + */
> +
> + buffer[0] = servo_no ^ 2;
> + buffer[1] = servo->pulse[servo_no];
> +
> + dev_dbg(&servo->udev->dev, "data: %02x %02x\n", buffer[0],
> buffer[1]);
> +
> + retval = usb_control_msg(servo->udev,
> + usb_sndctrlpipe(servo->udev, 0),
> + 0x09, 0x21, 0x0200, 0x0000, buffer, 2, 2 * HZ);
> + if (retval != 2)
> + dev_err(&servo->udev->dev, "retval = %d\n", retval);
> + kfree(buffer);
> +}
> +
> +#define show_set(value) \
> +static ssize_t set_servo##value (struct device *dev, \
> + const char *buf, size_t count) \
> +{ \
> + int degrees, minutes; \
> + struct usb_interface *intf = to_usb_interface (dev); \
> + struct phidget_servo *servo = usb_get_intfdata (intf); \
> + \
> + minutes = 0; \
> + /* must at least convert degrees */ \
> + if (sscanf (buf, "%d.%d", °rees, &minutes) < 1) { \
> + return -EINVAL; \
> + } \
> + \
> + if (degrees < -23 || degrees > (180 + 23) || \
> + minutes < 0 || minutes > 59) { \
> + return -EINVAL; \
> + } \
> + \
> + if (servo->version >= 3) \
> + change_position_v30 (servo, value, degrees, minutes); \
> + else \
> + change_position_v20 (servo, value, degrees, minutes); \
> + \
> + return count; \
> +} \
> + \
> +static ssize_t show_servo##value (struct device *dev, char *buf) \
> +{ \
> + struct usb_interface *intf = to_usb_interface (dev); \
> + struct phidget_servo *servo = usb_get_intfdata (intf); \
> + \
> + return sprintf (buf, "%d.%02d\n", servo->degrees[value], \
> + servo->minutes[value]); \
> +} \
> +static DEVICE_ATTR(servo##value, S_IWUGO | S_IRUGO, \
> + show_servo##value, set_servo##value);
> +
> +show_set(0);
> +show_set(1);
> +show_set(2);
> +show_set(3);
> +
> +static int
> +servo_probe(struct usb_interface *interface, const struct
> usb_device_id *id)
> +{
> + struct usb_device *udev = interface_to_usbdev(interface);
> + struct phidget_servo *dev = NULL;
> +
> + dev = kmalloc(sizeof (struct phidget_servo), GFP_KERNEL);
> + if (dev == NULL) {
> + dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);
> + return -ENOMEM;
> + }
> + memset(dev, 0x00, sizeof (*dev));
> +
> + dev->udev = usb_get_dev(udev);
> + switch (udev->descriptor.idVendor) {
> + case VENDOR_ID_WISEGROUP:
> + dev->version = 2;
> + break;
> + case VENDOR_ID_GLAB:
> + dev->version = 3;
> + break;
> + }
> + switch (udev->descriptor.idProduct) {
> + case DEVICE_ID_4MOTOR_SERVO_20:
> + case DEVICE_ID_4MOTOR_SERVO_30:
> + dev->quad_servo = 1;
> + break;
> + case DEVICE_ID_1MOTOR_SERVO_20:
> + case DEVICE_ID_1MOTOR_SERVO_30:
> + dev->quad_servo = 0;
> + break;
> + }
> +
> + usb_set_intfdata(interface, dev);
> +
> + device_create_file(&interface->dev, &dev_attr_servo0);
> + if (dev->quad_servo) {
> + device_create_file(&interface->dev, &dev_attr_servo1);
> + device_create_file(&interface->dev, &dev_attr_servo2);
> + device_create_file(&interface->dev, &dev_attr_servo3);
> + }
> +
> + dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0
> attached\n",
> + dev->quad_servo ? 4 : 1, dev->version);
> + if (dev->version == 2)
> + dev_info(&interface->dev,
> + "WARNING: v2.0 not tested! Please report if it works.\n");
> +
> + return 0;
> +}
> +
> +static void
> +servo_disconnect(struct usb_interface *interface)
> +{
> + struct phidget_servo *dev;
> +
> + dev = usb_get_intfdata(interface);
> + usb_set_intfdata(interface, NULL);
> +
> + device_remove_file(&interface->dev, &dev_attr_servo0);
> + if (dev->quad_servo) {
> + device_remove_file(&interface->dev, &dev_attr_servo1);
> + device_remove_file(&interface->dev, &dev_attr_servo2);
> + device_remove_file(&interface->dev, &dev_attr_servo3);
> + }
> +
> + usb_put_dev(dev->udev);
> +
> + kfree(dev);
> +
> + dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0
> detached\n",
> + dev->quad_servo ? 4 : 1, dev->version);
> +
> + dev_info(&interface->dev,
> + "WARNING: version 2.0 not tested. Please report if this works.\n");
> +}
> +
> +static struct usb_driver servo_driver = {
> + .owner = THIS_MODULE,
> + .name = "phidgetservo",
> + .probe = servo_probe,
> + .disconnect = servo_disconnect,
> + .id_table = id_table
> +};
> +
> +static int __init
> +phidget_servo_init(void)
> +{
> + int retval = 0;
> +
> + retval = usb_register(&servo_driver);
> + if (retval)
> + err("usb_register failed. Error number %d", retval);
> +
> + return retval;
> +}
> +
> +static void __exit
> +phidget_servo_exit(void)
> +{
> + usb_deregister(&servo_driver);
> +}
> +
> +module_init(phidget_servo_init);
> +module_exit(phidget_servo_exit);
> +
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_LICENSE("GPL");
>
On Thu, Apr 29, 2004 at 03:59:51AM +0200, Sean Young wrote:
> On Wed, Apr 28, 2004 at 11:41:38AM -0700, Greg KH wrote:
> > On Wed, Apr 28, 2004 at 08:18:06PM +0200, Sean Young wrote:
> > > Here is a driver for the usb servo controllers from Phidgets
> > > <http://www.phidgets.com/>, using sysfs.
> > >
> > > Note that the devices claim to be hid devices, so I've added them to the
> > > hid_blacklist (HID_QUIRK_IGNORE). A servo controller isn't really an hid
> > > device (or is it?).
> > >
> > > diff against 2.6.6-rc2.
> >
> > Nice, I like tiny clean drivers like this :)
> >
> > I've applied it to my trees, and it will make it into the next -mm tree,
> > and show up in the 2.6.7 release whenever it happens.
>
> Great! Thanks.
>
> Somehow I managed to send the wrong version. Here is a patch which fixes
> that. (Remove a dev_info() which wasn't supposed to be there, and make sure
> that everything is still consistent in the unlikely event that kmalloc()
> fails). Just minor cleanups.
Heh, that happens :)
Applied, thanks.
greg k-h
On Wed, Apr 28, 2004 at 10:18:28PM -0400, Bryan Small wrote:
> Really cool Sean. I am currently working on the IfKit for both the
> 8/8/8 and the 0/8/8 on the textlcds. Also working on the textlcds
> output.
What kernel/userspace interface were you going to use? Or are you using
a pure libusb/usbfs type interface?
> If I get these done soon was thinking of moving to RFID, so that we can
> also possibly throw in Chesters PAM auth module idea in.
That is a cool project, again, what kind of kernel interaction would you
want for it?
thanks,
greg k-h
The IFkit ( both 8/8/8 and 0/8/8) and the TextLCD will work nearly the
same as Sean's servo control. They will use sysfs also. They will be
blacklisted from HID. Sean, what phidget did you want to work on next.
I just wanted to make sure, I was not stepping on others toes by
jumping on the ones I picked. If you want Chester can just make us a
big ol punchlist and we can just start knocking them out. I have most
of the phidgets except the stepper controller, power bar, dc
controller, and the new 0/16/16. I think thats correct, right
Chester..?
And yay I finally got my linux dev. box back from dead. I was beginning
to worry, I was not going to be able to do anything, and Sean would
have finished it all. :-) But hopefully by tonight I will have
everything ready to go.
Bryan Small
On Apr 28, 2004, at 11:10 PM, Greg KH wrote:
> On Wed, Apr 28, 2004 at 10:18:28PM -0400, Bryan Small wrote:
>> Really cool Sean. I am currently working on the IfKit for both the
>> 8/8/8 and the 0/8/8 on the textlcds. Also working on the textlcds
>> output.
>
> What kernel/userspace interface were you going to use? Or are you
> using
> a pure libusb/usbfs type interface?
>
>> If I get these done soon was thinking of moving to RFID, so that we
>> can
>> also possibly throw in Chesters PAM auth module idea in.
>
> That is a cool project, again, what kind of kernel interaction would
> you
> want for it?
>
> thanks,
>
> greg k-h
>
Am Donnerstag, 29. April 2004 20:49 schrieb Bryan Small:
> The IFkit ( both 8/8/8 and 0/8/8) and the TextLCD will work nearly the
> same as Sean's servo control. They will use sysfs also. They will be
I don't want to spoil the party, but in which way is using sysfs in this
way different from using it as a form of devfs?
Regards
Oliver
On Thu, Apr 29, 2004 at 09:10:21PM +0200, Oliver Neukum wrote:
> Am Donnerstag, 29. April 2004 20:49 schrieb Bryan Small:
> > The IFkit ( both 8/8/8 and 0/8/8) and the TextLCD will work nearly the
> > same as Sean's servo control. They will use sysfs also. They will be
>
> I don't want to spoil the party, but in which way is using sysfs in this
> way different from using it as a form of devfs?
- one value per file, no char or block nodes
- devices can export many different files depending on their
needs (no ioctl crud needed.)
- you can use a script or libsysfs a web browser, or anything
else that reads directory trees and files to access the device
info.
- you can have as many devices as you want in the system, no
limitations on minor numbers, or anything else.
- no unsolvable kernel race and locking issues
- no horribly formatted code from a developer who is no longer
maintaining it.
Shall I go on? :)
thanks,
greg k-h
Am Donnerstag, 29. April 2004 21:16 schrieb Greg KH:
> On Thu, Apr 29, 2004 at 09:10:21PM +0200, Oliver Neukum wrote:
> > Am Donnerstag, 29. April 2004 20:49 schrieb Bryan Small:
> > > The IFkit ( both 8/8/8 and 0/8/8) and the TextLCD will work nearly the
> > > same as Sean's servo control. They will use sysfs also. They will be
> >
> > I don't want to spoil the party, but in which way is using sysfs in this
> > way different from using it as a form of devfs?
>
> - one value per file, no char or block nodes
> - devices can export many different files depending on their
> needs (no ioctl crud needed.)
> - you can use a script or libsysfs a web browser, or anything
> else that reads directory trees and files to access the device
> info.
> - you can have as many devices as you want in the system, no
> limitations on minor numbers, or anything else.
> - no unsolvable kernel race and locking issues
> - no horribly formatted code from a developer who is no longer
> maintaining it.
>
> Shall I go on? :)
Yes. You are describing a better devfs, but still devfs. Why not go
the whole way?
Regards
Oliver
On Thu, Apr 29, 2004 at 09:35:42PM +0200, Oliver Neukum wrote:
> Am Donnerstag, 29. April 2004 21:16 schrieb Greg KH:
> > On Thu, Apr 29, 2004 at 09:10:21PM +0200, Oliver Neukum wrote:
> > > Am Donnerstag, 29. April 2004 20:49 schrieb Bryan Small:
> > > > The IFkit ( both 8/8/8 and 0/8/8) and the TextLCD will work nearly the
> > > > same as Sean's servo control. They will use sysfs also. They will be
> > >
> > > I don't want to spoil the party, but in which way is using sysfs in this
> > > way different from using it as a form of devfs?
> >
> > - one value per file, no char or block nodes
> > - devices can export many different files depending on their
> > needs (no ioctl crud needed.)
> > - you can use a script or libsysfs a web browser, or anything
> > else that reads directory trees and files to access the device
> > info.
> > - you can have as many devices as you want in the system, no
> > limitations on minor numbers, or anything else.
> > - no unsolvable kernel race and locking issues
> > - no horribly formatted code from a developer who is no longer
> > maintaining it.
> >
> > Shall I go on? :)
>
> Yes. You are describing a better devfs, but still devfs. Why not go
> the whole way?
{sigh} Not again. Come on Oliver... Go read the archives for why
Linus does not want us to put device nodes in sysfs.
Putting device attributes such as led colors, servo motor controls, etc,
look to be much the same idea, are much different from the traditional
block and character nodes we are used to in Unix.
Think of them as mini device specific file systems instead, mounted in a
device tree.
I don't want to discuss this again...
thanks,
greg k-h
> > Yes. You are describing a better devfs, but still devfs. Why not go
> > the whole way?
>
> {sigh} Not again. Come on Oliver... Go read the archives for why
> Linus does not want us to put device nodes in sysfs.
Neither do I. Sorry for being unclear.
> Putting device attributes such as led colors, servo motor controls, etc,
> look to be much the same idea, are much different from the traditional
> block and character nodes we are used to in Unix.
>
> Think of them as mini device specific file systems instead, mounted in a
> device tree.
That I see as the problem. It's mission creep in sysfs. Reminds me of
procfs. IMHO, if you do io, use a device node.
Regards
Oliver