phidgetservo.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /*
  2. * USB PhidgetServo driver 1.0
  3. *
  4. * Copyright (C) 2004, 2006 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. #include <linux/kernel.h>
  20. #include <linux/errno.h>
  21. #include <linux/init.h>
  22. #include <linux/slab.h>
  23. #include <linux/module.h>
  24. #include <linux/usb.h>
  25. #include "phidget.h"
  26. #define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
  27. #define DRIVER_DESC "USB PhidgetServo Driver"
  28. #define VENDOR_ID_GLAB 0x06c2
  29. #define DEVICE_ID_GLAB_PHIDGETSERVO_QUAD 0x0038
  30. #define DEVICE_ID_GLAB_PHIDGETSERVO_UNI 0x0039
  31. #define VENDOR_ID_WISEGROUP 0x0925
  32. #define VENDOR_ID_WISEGROUP_PHIDGETSERVO_QUAD 0x8101
  33. #define VENDOR_ID_WISEGROUP_PHIDGETSERVO_UNI 0x8104
  34. #define SERVO_VERSION_30 0x01
  35. #define SERVO_COUNT_QUAD 0x02
  36. static struct usb_device_id id_table[] = {
  37. {
  38. USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_GLAB_PHIDGETSERVO_QUAD),
  39. .driver_info = SERVO_VERSION_30 | SERVO_COUNT_QUAD
  40. },
  41. {
  42. USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_GLAB_PHIDGETSERVO_UNI),
  43. .driver_info = SERVO_VERSION_30
  44. },
  45. {
  46. USB_DEVICE(VENDOR_ID_WISEGROUP,
  47. VENDOR_ID_WISEGROUP_PHIDGETSERVO_QUAD),
  48. .driver_info = SERVO_COUNT_QUAD
  49. },
  50. {
  51. USB_DEVICE(VENDOR_ID_WISEGROUP,
  52. VENDOR_ID_WISEGROUP_PHIDGETSERVO_UNI),
  53. .driver_info = 0
  54. },
  55. {}
  56. };
  57. MODULE_DEVICE_TABLE(usb, id_table);
  58. static int unsigned long device_no;
  59. struct phidget_servo {
  60. struct usb_device *udev;
  61. struct device *dev;
  62. int dev_no;
  63. ulong type;
  64. int pulse[4];
  65. int degrees[4];
  66. int minutes[4];
  67. };
  68. static int
  69. change_position_v30(struct phidget_servo *servo, int servo_no, int degrees,
  70. int minutes)
  71. {
  72. int retval;
  73. unsigned char *buffer;
  74. if (degrees < -23 || degrees > 362)
  75. return -EINVAL;
  76. buffer = kmalloc(6, GFP_KERNEL);
  77. if (!buffer) {
  78. dev_err(&servo->udev->dev, "%s - out of memory\n",
  79. __func__);
  80. return -ENOMEM;
  81. }
  82. /*
  83. * pulse = 0 - 4095
  84. * angle = 0 - 180 degrees
  85. *
  86. * pulse = angle * 10.6 + 243.8
  87. */
  88. servo->pulse[servo_no] = ((degrees*60 + minutes)*106 + 2438*60)/600;
  89. servo->degrees[servo_no]= degrees;
  90. servo->minutes[servo_no]= minutes;
  91. /*
  92. * The PhidgetServo v3.0 is controlled by sending 6 bytes,
  93. * 4 * 12 bits for each servo.
  94. *
  95. * low = lower 8 bits pulse
  96. * high = higher 4 bits pulse
  97. *
  98. * offset bits
  99. * +---+-----------------+
  100. * | 0 | low 0 |
  101. * +---+--------+--------+
  102. * | 1 | high 1 | high 0 |
  103. * +---+--------+--------+
  104. * | 2 | low 1 |
  105. * +---+-----------------+
  106. * | 3 | low 2 |
  107. * +---+--------+--------+
  108. * | 4 | high 3 | high 2 |
  109. * +---+--------+--------+
  110. * | 5 | low 3 |
  111. * +---+-----------------+
  112. */
  113. buffer[0] = servo->pulse[0] & 0xff;
  114. buffer[1] = (servo->pulse[0] >> 8 & 0x0f)
  115. | (servo->pulse[1] >> 4 & 0xf0);
  116. buffer[2] = servo->pulse[1] & 0xff;
  117. buffer[3] = servo->pulse[2] & 0xff;
  118. buffer[4] = (servo->pulse[2] >> 8 & 0x0f)
  119. | (servo->pulse[3] >> 4 & 0xf0);
  120. buffer[5] = servo->pulse[3] & 0xff;
  121. dev_dbg(&servo->udev->dev,
  122. "data: %02x %02x %02x %02x %02x %02x\n",
  123. buffer[0], buffer[1], buffer[2],
  124. buffer[3], buffer[4], buffer[5]);
  125. retval = usb_control_msg(servo->udev,
  126. usb_sndctrlpipe(servo->udev, 0),
  127. 0x09, 0x21, 0x0200, 0x0000, buffer, 6, 2000);
  128. kfree(buffer);
  129. return retval;
  130. }
  131. static int
  132. change_position_v20(struct phidget_servo *servo, int servo_no, int degrees,
  133. int minutes)
  134. {
  135. int retval;
  136. unsigned char *buffer;
  137. if (degrees < -23 || degrees > 278)
  138. return -EINVAL;
  139. buffer = kmalloc(2, GFP_KERNEL);
  140. if (!buffer) {
  141. dev_err(&servo->udev->dev, "%s - out of memory\n",
  142. __func__);
  143. return -ENOMEM;
  144. }
  145. /*
  146. * angle = 0 - 180 degrees
  147. * pulse = angle + 23
  148. */
  149. servo->pulse[servo_no]= degrees + 23;
  150. servo->degrees[servo_no]= degrees;
  151. servo->minutes[servo_no]= 0;
  152. /*
  153. * The PhidgetServo v2.0 is controlled by sending two bytes. The
  154. * first byte is the servo number xor'ed with 2:
  155. *
  156. * servo 0 = 2
  157. * servo 1 = 3
  158. * servo 2 = 0
  159. * servo 3 = 1
  160. *
  161. * The second byte is the position.
  162. */
  163. buffer[0] = servo_no ^ 2;
  164. buffer[1] = servo->pulse[servo_no];
  165. dev_dbg(&servo->udev->dev, "data: %02x %02x\n", buffer[0], buffer[1]);
  166. retval = usb_control_msg(servo->udev,
  167. usb_sndctrlpipe(servo->udev, 0),
  168. 0x09, 0x21, 0x0200, 0x0000, buffer, 2, 2000);
  169. kfree(buffer);
  170. return retval;
  171. }
  172. #define show_set(value) \
  173. static ssize_t set_servo##value (struct device *dev, \
  174. struct device_attribute *attr, \
  175. const char *buf, size_t count) \
  176. { \
  177. int degrees, minutes, retval; \
  178. struct phidget_servo *servo = dev_get_drvdata(dev); \
  179. \
  180. minutes = 0; \
  181. /* must at least convert degrees */ \
  182. if (sscanf(buf, "%d.%d", &degrees, &minutes) < 1) { \
  183. return -EINVAL; \
  184. } \
  185. \
  186. if (minutes < 0 || minutes > 59) \
  187. return -EINVAL; \
  188. \
  189. if (servo->type & SERVO_VERSION_30) \
  190. retval = change_position_v30(servo, value, degrees, \
  191. minutes); \
  192. else \
  193. retval = change_position_v20(servo, value, degrees, \
  194. minutes); \
  195. \
  196. return retval < 0 ? retval : count; \
  197. } \
  198. \
  199. static ssize_t show_servo##value (struct device *dev, \
  200. struct device_attribute *attr, \
  201. char *buf) \
  202. { \
  203. struct phidget_servo *servo = dev_get_drvdata(dev); \
  204. \
  205. return sprintf(buf, "%d.%02d\n", servo->degrees[value], \
  206. servo->minutes[value]); \
  207. }
  208. #define servo_attr(value) \
  209. __ATTR(servo##value, S_IWUGO | S_IRUGO, \
  210. show_servo##value, set_servo##value)
  211. show_set(0);
  212. show_set(1);
  213. show_set(2);
  214. show_set(3);
  215. static struct device_attribute dev_attrs[] = {
  216. servo_attr(0), servo_attr(1), servo_attr(2), servo_attr(3)
  217. };
  218. static int
  219. servo_probe(struct usb_interface *interface, const struct usb_device_id *id)
  220. {
  221. struct usb_device *udev = interface_to_usbdev(interface);
  222. struct phidget_servo *dev;
  223. int bit, value, rc;
  224. int servo_count, i;
  225. dev = kzalloc(sizeof (struct phidget_servo), GFP_KERNEL);
  226. if (dev == NULL) {
  227. dev_err(&interface->dev, "%s - out of memory\n", __func__);
  228. rc = -ENOMEM;
  229. goto out;
  230. }
  231. dev->udev = usb_get_dev(udev);
  232. dev->type = id->driver_info;
  233. dev->dev_no = -1;
  234. usb_set_intfdata(interface, dev);
  235. do {
  236. bit = find_first_zero_bit(&device_no, sizeof(device_no));
  237. value = test_and_set_bit(bit, &device_no);
  238. } while (value);
  239. dev->dev_no = bit;
  240. dev->dev = device_create_drvdata(phidget_class, &dev->udev->dev,
  241. MKDEV(0, 0), dev,
  242. "servo%d", dev->dev_no);
  243. if (IS_ERR(dev->dev)) {
  244. rc = PTR_ERR(dev->dev);
  245. dev->dev = NULL;
  246. goto out;
  247. }
  248. servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1;
  249. for (i=0; i<servo_count; i++) {
  250. rc = device_create_file(dev->dev, &dev_attrs[i]);
  251. if (rc)
  252. goto out2;
  253. }
  254. dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n",
  255. servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2);
  256. if (!(dev->type & SERVO_VERSION_30))
  257. dev_info(&interface->dev,
  258. "WARNING: v2.0 not tested! Please report if it works.\n");
  259. return 0;
  260. out2:
  261. while (i-- > 0)
  262. device_remove_file(dev->dev, &dev_attrs[i]);
  263. out:
  264. if (dev) {
  265. if (dev->dev)
  266. device_unregister(dev->dev);
  267. if (dev->dev_no >= 0)
  268. clear_bit(dev->dev_no, &device_no);
  269. kfree(dev);
  270. }
  271. return rc;
  272. }
  273. static void
  274. servo_disconnect(struct usb_interface *interface)
  275. {
  276. struct phidget_servo *dev;
  277. int servo_count, i;
  278. dev = usb_get_intfdata(interface);
  279. usb_set_intfdata(interface, NULL);
  280. if (!dev)
  281. return;
  282. servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1;
  283. for (i=0; i<servo_count; i++)
  284. device_remove_file(dev->dev, &dev_attrs[i]);
  285. device_unregister(dev->dev);
  286. usb_put_dev(dev->udev);
  287. dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n",
  288. servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2);
  289. clear_bit(dev->dev_no, &device_no);
  290. kfree(dev);
  291. }
  292. static struct usb_driver servo_driver = {
  293. .name = "phidgetservo",
  294. .probe = servo_probe,
  295. .disconnect = servo_disconnect,
  296. .id_table = id_table
  297. };
  298. static int __init
  299. phidget_servo_init(void)
  300. {
  301. int retval;
  302. retval = usb_register(&servo_driver);
  303. if (retval)
  304. err("usb_register failed. Error number %d", retval);
  305. return retval;
  306. }
  307. static void __exit
  308. phidget_servo_exit(void)
  309. {
  310. usb_deregister(&servo_driver);
  311. }
  312. module_init(phidget_servo_init);
  313. module_exit(phidget_servo_exit);
  314. MODULE_AUTHOR(DRIVER_AUTHOR);
  315. MODULE_DESCRIPTION(DRIVER_DESC);
  316. MODULE_LICENSE("GPL");