Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.18-rc7 336 lines 8.5 kB view raw
1/* 2 * USB PhidgetServo driver 1.0 3 * 4 * Copyright (C) 2004 Sean Young <sean@mess.org> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This is a driver for the USB PhidgetServo version 2.0 and 3.0 servo 12 * controllers available at: http://www.phidgets.com/ 13 * 14 * Note that the driver takes input as: degrees.minutes 15 * 16 * CAUTION: Generally you should use 0 < degrees < 180 as anything else 17 * is probably beyond the range of your servo and may damage it. 18 * 19 * Jun 16, 2004: Sean Young <sean@mess.org> 20 * - cleanups 21 * - was using memory after kfree() 22 * Aug 8, 2004: Sean Young <sean@mess.org> 23 * - set the highest angle as high as the hardware allows, there are 24 * some odd servos out there 25 * 26 */ 27 28#include <linux/kernel.h> 29#include <linux/errno.h> 30#include <linux/init.h> 31#include <linux/slab.h> 32#include <linux/module.h> 33#include <linux/usb.h> 34 35#define DRIVER_AUTHOR "Sean Young <sean@mess.org>" 36#define DRIVER_DESC "USB PhidgetServo Driver" 37 38#define VENDOR_ID_GLAB 0x06c2 39#define DEVICE_ID_GLAB_PHIDGETSERVO_QUAD 0x0038 40#define DEVICE_ID_GLAB_PHIDGETSERVO_UNI 0x0039 41 42#define VENDOR_ID_WISEGROUP 0x0925 43#define VENDOR_ID_WISEGROUP_PHIDGETSERVO_QUAD 0x8101 44#define VENDOR_ID_WISEGROUP_PHIDGETSERVO_UNI 0x8104 45 46#define SERVO_VERSION_30 0x01 47#define SERVO_COUNT_QUAD 0x02 48 49static struct usb_device_id id_table[] = { 50 { 51 USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_GLAB_PHIDGETSERVO_QUAD), 52 .driver_info = SERVO_VERSION_30 | SERVO_COUNT_QUAD 53 }, 54 { 55 USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_GLAB_PHIDGETSERVO_UNI), 56 .driver_info = SERVO_VERSION_30 57 }, 58 { 59 USB_DEVICE(VENDOR_ID_WISEGROUP, 60 VENDOR_ID_WISEGROUP_PHIDGETSERVO_QUAD), 61 .driver_info = SERVO_COUNT_QUAD 62 }, 63 { 64 USB_DEVICE(VENDOR_ID_WISEGROUP, 65 VENDOR_ID_WISEGROUP_PHIDGETSERVO_UNI), 66 .driver_info = 0 67 }, 68 {} 69}; 70 71MODULE_DEVICE_TABLE(usb, id_table); 72 73struct phidget_servo { 74 struct usb_device *udev; 75 ulong type; 76 int pulse[4]; 77 int degrees[4]; 78 int minutes[4]; 79}; 80 81static int 82change_position_v30(struct phidget_servo *servo, int servo_no, int degrees, 83 int minutes) 84{ 85 int retval; 86 unsigned char *buffer; 87 88 if (degrees < -23 || degrees > 362) 89 return -EINVAL; 90 91 buffer = kmalloc(6, GFP_KERNEL); 92 if (!buffer) { 93 dev_err(&servo->udev->dev, "%s - out of memory\n", 94 __FUNCTION__); 95 return -ENOMEM; 96 } 97 98 /* 99 * pulse = 0 - 4095 100 * angle = 0 - 180 degrees 101 * 102 * pulse = angle * 10.6 + 243.8 103 */ 104 servo->pulse[servo_no] = ((degrees*60 + minutes)*106 + 2438*60)/600; 105 servo->degrees[servo_no]= degrees; 106 servo->minutes[servo_no]= minutes; 107 108 /* 109 * The PhidgetServo v3.0 is controlled by sending 6 bytes, 110 * 4 * 12 bits for each servo. 111 * 112 * low = lower 8 bits pulse 113 * high = higher 4 bits pulse 114 * 115 * offset bits 116 * +---+-----------------+ 117 * | 0 | low 0 | 118 * +---+--------+--------+ 119 * | 1 | high 1 | high 0 | 120 * +---+--------+--------+ 121 * | 2 | low 1 | 122 * +---+-----------------+ 123 * | 3 | low 2 | 124 * +---+--------+--------+ 125 * | 4 | high 3 | high 2 | 126 * +---+--------+--------+ 127 * | 5 | low 3 | 128 * +---+-----------------+ 129 */ 130 131 buffer[0] = servo->pulse[0] & 0xff; 132 buffer[1] = (servo->pulse[0] >> 8 & 0x0f) 133 | (servo->pulse[1] >> 4 & 0xf0); 134 buffer[2] = servo->pulse[1] & 0xff; 135 buffer[3] = servo->pulse[2] & 0xff; 136 buffer[4] = (servo->pulse[2] >> 8 & 0x0f) 137 | (servo->pulse[3] >> 4 & 0xf0); 138 buffer[5] = servo->pulse[3] & 0xff; 139 140 dev_dbg(&servo->udev->dev, 141 "data: %02x %02x %02x %02x %02x %02x\n", 142 buffer[0], buffer[1], buffer[2], 143 buffer[3], buffer[4], buffer[5]); 144 145 retval = usb_control_msg(servo->udev, 146 usb_sndctrlpipe(servo->udev, 0), 147 0x09, 0x21, 0x0200, 0x0000, buffer, 6, 2000); 148 149 kfree(buffer); 150 151 return retval; 152} 153 154static int 155change_position_v20(struct phidget_servo *servo, int servo_no, int degrees, 156 int minutes) 157{ 158 int retval; 159 unsigned char *buffer; 160 161 if (degrees < -23 || degrees > 278) 162 return -EINVAL; 163 164 buffer = kmalloc(2, GFP_KERNEL); 165 if (!buffer) { 166 dev_err(&servo->udev->dev, "%s - out of memory\n", 167 __FUNCTION__); 168 return -ENOMEM; 169 } 170 171 /* 172 * angle = 0 - 180 degrees 173 * pulse = angle + 23 174 */ 175 servo->pulse[servo_no]= degrees + 23; 176 servo->degrees[servo_no]= degrees; 177 servo->minutes[servo_no]= 0; 178 179 /* 180 * The PhidgetServo v2.0 is controlled by sending two bytes. The 181 * first byte is the servo number xor'ed with 2: 182 * 183 * servo 0 = 2 184 * servo 1 = 3 185 * servo 2 = 0 186 * servo 3 = 1 187 * 188 * The second byte is the position. 189 */ 190 191 buffer[0] = servo_no ^ 2; 192 buffer[1] = servo->pulse[servo_no]; 193 194 dev_dbg(&servo->udev->dev, "data: %02x %02x\n", buffer[0], buffer[1]); 195 196 retval = usb_control_msg(servo->udev, 197 usb_sndctrlpipe(servo->udev, 0), 198 0x09, 0x21, 0x0200, 0x0000, buffer, 2, 2000); 199 200 kfree(buffer); 201 202 return retval; 203} 204 205#define show_set(value) \ 206static ssize_t set_servo##value (struct device *dev, struct device_attribute *attr, \ 207 const char *buf, size_t count) \ 208{ \ 209 int degrees, minutes, retval; \ 210 struct usb_interface *intf = to_usb_interface (dev); \ 211 struct phidget_servo *servo = usb_get_intfdata (intf); \ 212 \ 213 minutes = 0; \ 214 /* must at least convert degrees */ \ 215 if (sscanf (buf, "%d.%d", &degrees, &minutes) < 1) { \ 216 return -EINVAL; \ 217 } \ 218 \ 219 if (minutes < 0 || minutes > 59) \ 220 return -EINVAL; \ 221 \ 222 if (servo->type & SERVO_VERSION_30) \ 223 retval = change_position_v30 (servo, value, degrees, \ 224 minutes); \ 225 else \ 226 retval = change_position_v20 (servo, value, degrees, \ 227 minutes); \ 228 \ 229 return retval < 0 ? retval : count; \ 230} \ 231 \ 232static ssize_t show_servo##value (struct device *dev, struct device_attribute *attr, char *buf) \ 233{ \ 234 struct usb_interface *intf = to_usb_interface (dev); \ 235 struct phidget_servo *servo = usb_get_intfdata (intf); \ 236 \ 237 return sprintf (buf, "%d.%02d\n", servo->degrees[value], \ 238 servo->minutes[value]); \ 239} \ 240static DEVICE_ATTR(servo##value, S_IWUGO | S_IRUGO, \ 241 show_servo##value, set_servo##value); 242 243show_set(0); 244show_set(1); 245show_set(2); 246show_set(3); 247 248static int 249servo_probe(struct usb_interface *interface, const struct usb_device_id *id) 250{ 251 struct usb_device *udev = interface_to_usbdev(interface); 252 struct phidget_servo *dev; 253 254 dev = kzalloc(sizeof (struct phidget_servo), GFP_KERNEL); 255 if (dev == NULL) { 256 dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); 257 return -ENOMEM; 258 } 259 260 dev->udev = usb_get_dev(udev); 261 dev->type = id->driver_info; 262 usb_set_intfdata(interface, dev); 263 264 device_create_file(&interface->dev, &dev_attr_servo0); 265 if (dev->type & SERVO_COUNT_QUAD) { 266 device_create_file(&interface->dev, &dev_attr_servo1); 267 device_create_file(&interface->dev, &dev_attr_servo2); 268 device_create_file(&interface->dev, &dev_attr_servo3); 269 } 270 271 dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n", 272 dev->type & SERVO_COUNT_QUAD ? 4 : 1, 273 dev->type & SERVO_VERSION_30 ? 3 : 2); 274 275 if(!(dev->type & SERVO_VERSION_30)) 276 dev_info(&interface->dev, 277 "WARNING: v2.0 not tested! Please report if it works.\n"); 278 279 return 0; 280} 281 282static void 283servo_disconnect(struct usb_interface *interface) 284{ 285 struct phidget_servo *dev; 286 287 dev = usb_get_intfdata(interface); 288 usb_set_intfdata(interface, NULL); 289 290 device_remove_file(&interface->dev, &dev_attr_servo0); 291 if (dev->type & SERVO_COUNT_QUAD) { 292 device_remove_file(&interface->dev, &dev_attr_servo1); 293 device_remove_file(&interface->dev, &dev_attr_servo2); 294 device_remove_file(&interface->dev, &dev_attr_servo3); 295 } 296 297 usb_put_dev(dev->udev); 298 299 dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n", 300 dev->type & SERVO_COUNT_QUAD ? 4 : 1, 301 dev->type & SERVO_VERSION_30 ? 3 : 2); 302 303 kfree(dev); 304} 305 306static struct usb_driver servo_driver = { 307 .name = "phidgetservo", 308 .probe = servo_probe, 309 .disconnect = servo_disconnect, 310 .id_table = id_table 311}; 312 313static int __init 314phidget_servo_init(void) 315{ 316 int retval; 317 318 retval = usb_register(&servo_driver); 319 if (retval) 320 err("usb_register failed. Error number %d", retval); 321 322 return retval; 323} 324 325static void __exit 326phidget_servo_exit(void) 327{ 328 usb_deregister(&servo_driver); 329} 330 331module_init(phidget_servo_init); 332module_exit(phidget_servo_exit); 333 334MODULE_AUTHOR(DRIVER_AUTHOR); 335MODULE_DESCRIPTION(DRIVER_DESC); 336MODULE_LICENSE("GPL");