dtcs033.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. /*
  2. * Subdriver for Scopium astro-camera (DTCS033, 0547:7303)
  3. *
  4. * Copyright (C) 2014 Robert Butora (robert.butora.fi@gmail.com)
  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. * any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. */
  16. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  17. #define MODULE_NAME "dtcs033"
  18. #include "gspca.h"
  19. MODULE_AUTHOR("Robert Butora <robert.butora.fi@gmail.com>");
  20. MODULE_DESCRIPTION("Scopium DTCS033 astro-cam USB Camera Driver");
  21. MODULE_LICENSE("GPL");
  22. struct dtcs033_usb_requests {
  23. u8 bRequestType;
  24. u8 bRequest;
  25. u16 wValue;
  26. u16 wIndex;
  27. u16 wLength;
  28. };
  29. /* send a usb request */
  30. static void reg_rw(struct gspca_dev *gspca_dev,
  31. u8 bRequestType, u8 bRequest,
  32. u16 wValue, u16 wIndex, u16 wLength)
  33. {
  34. struct usb_device *udev = gspca_dev->dev;
  35. int ret;
  36. if (gspca_dev->usb_err < 0)
  37. return;
  38. ret = usb_control_msg(udev,
  39. usb_rcvctrlpipe(udev, 0),
  40. bRequest,
  41. bRequestType,
  42. wValue, wIndex,
  43. gspca_dev->usb_buf, wLength, 500);
  44. if (ret < 0) {
  45. gspca_dev->usb_err = ret;
  46. pr_err("usb_control_msg error %d\n", ret);
  47. }
  48. return;
  49. }
  50. /* send several usb in/out requests */
  51. static int reg_reqs(struct gspca_dev *gspca_dev,
  52. const struct dtcs033_usb_requests *preqs, int n_reqs)
  53. {
  54. int i = 0;
  55. const struct dtcs033_usb_requests *preq;
  56. while ((i < n_reqs) && (gspca_dev->usb_err >= 0)) {
  57. preq = &preqs[i];
  58. reg_rw(gspca_dev, preq->bRequestType, preq->bRequest,
  59. preq->wValue, preq->wIndex, preq->wLength);
  60. if (gspca_dev->usb_err < 0) {
  61. PERR("usb error request no: %d / %d\n",
  62. i, n_reqs);
  63. } else if (preq->bRequestType & USB_DIR_IN) {
  64. PDEBUG(D_STREAM,
  65. "USB IN (%d) returned[%d] %02X %02X %02X %s",
  66. i,
  67. preq->wLength,
  68. gspca_dev->usb_buf[0],
  69. gspca_dev->usb_buf[1],
  70. gspca_dev->usb_buf[2],
  71. preq->wLength > 3 ? "...\n" : "\n");
  72. }
  73. i++;
  74. }
  75. return gspca_dev->usb_err;
  76. }
  77. /* -- subdriver interface implementation -- */
  78. #define DT_COLS (640)
  79. static const struct v4l2_pix_format dtcs033_mode[] = {
  80. /* raw Bayer patterned output */
  81. {DT_COLS, 480, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
  82. .bytesperline = DT_COLS,
  83. .sizeimage = DT_COLS*480,
  84. .colorspace = V4L2_COLORSPACE_SRGB,
  85. },
  86. /* this mode will demosaic the Bayer pattern */
  87. {DT_COLS, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
  88. .bytesperline = DT_COLS,
  89. .sizeimage = DT_COLS*480,
  90. .colorspace = V4L2_COLORSPACE_SRGB,
  91. }
  92. };
  93. /* config called at probe time */
  94. static int sd_config(struct gspca_dev *gspca_dev,
  95. const struct usb_device_id *id)
  96. {
  97. gspca_dev->cam.cam_mode = dtcs033_mode;
  98. gspca_dev->cam.nmodes = ARRAY_SIZE(dtcs033_mode);
  99. gspca_dev->cam.bulk = 1;
  100. gspca_dev->cam.bulk_nurbs = 1;
  101. gspca_dev->cam.bulk_size = DT_COLS*512;
  102. return 0;
  103. }
  104. /* init called at probe and resume time */
  105. static int sd_init(struct gspca_dev *gspca_dev)
  106. {
  107. return 0;
  108. }
  109. /* start stop the camera */
  110. static int dtcs033_start(struct gspca_dev *gspca_dev);
  111. static void dtcs033_stopN(struct gspca_dev *gspca_dev);
  112. /* intercept camera image data */
  113. static void dtcs033_pkt_scan(struct gspca_dev *gspca_dev,
  114. u8 *data, /* packet data */
  115. int len) /* packet data length */
  116. {
  117. /* drop incomplete frames */
  118. if (len != DT_COLS*512) {
  119. gspca_dev->last_packet_type = DISCARD_PACKET;
  120. /* gspca.c: discard invalidates the whole frame. */
  121. return;
  122. }
  123. /* forward complete frames */
  124. gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
  125. gspca_frame_add(gspca_dev, INTER_PACKET,
  126. data + 16*DT_COLS,
  127. len - 32*DT_COLS); /* skip first & last 16 lines */
  128. gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
  129. return;
  130. }
  131. /* -- controls: exposure and gain -- */
  132. static void dtcs033_setexposure(struct gspca_dev *gspca_dev,
  133. s32 expo, s32 gain)
  134. {
  135. /* gain [dB] encoding */
  136. u16 sGain = (u16)gain;
  137. u16 gainVal = 224+(sGain-14)*(768-224)/(33-14);
  138. u16 wIndex = 0x0100|(0x00FF&gainVal);
  139. u16 wValue = (0xFF00&gainVal)>>8;
  140. /* exposure time [msec] encoding */
  141. u16 sXTime = (u16)expo;
  142. u16 xtimeVal = (524*(150-(sXTime-1)))/150;
  143. const u8 bRequestType =
  144. USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
  145. const u8 bRequest = 0x18;
  146. reg_rw(gspca_dev,
  147. bRequestType, bRequest, wValue, wIndex, 0);
  148. if (gspca_dev->usb_err < 0)
  149. PERR("usb error in setexposure(gain) sequence.\n");
  150. reg_rw(gspca_dev,
  151. bRequestType, bRequest, (xtimeVal<<4), 0x6300, 0);
  152. if (gspca_dev->usb_err < 0)
  153. PERR("usb error in setexposure(time) sequence.\n");
  154. }
  155. /* specific webcam descriptor */
  156. struct sd {
  157. struct gspca_dev gspca_dev;/* !! must be the first item */
  158. /* exposure & gain controls */
  159. struct {
  160. struct v4l2_ctrl *exposure;
  161. struct v4l2_ctrl *gain;
  162. };
  163. };
  164. static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
  165. {
  166. struct gspca_dev *gspca_dev =
  167. container_of(ctrl->handler,
  168. struct gspca_dev, ctrl_handler);
  169. struct sd *sd = (struct sd *) gspca_dev;
  170. gspca_dev->usb_err = 0;
  171. if (!gspca_dev->streaming)
  172. return 0;
  173. switch (ctrl->id) {
  174. case V4L2_CID_EXPOSURE:
  175. dtcs033_setexposure(gspca_dev,
  176. ctrl->val, sd->gain->val);
  177. break;
  178. case V4L2_CID_GAIN:
  179. dtcs033_setexposure(gspca_dev,
  180. sd->exposure->val, ctrl->val);
  181. break;
  182. }
  183. return gspca_dev->usb_err;
  184. }
  185. static const struct v4l2_ctrl_ops sd_ctrl_ops = {
  186. .s_ctrl = sd_s_ctrl,
  187. };
  188. static int dtcs033_init_controls(struct gspca_dev *gspca_dev)
  189. {
  190. struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
  191. struct sd *sd = (struct sd *) gspca_dev;
  192. gspca_dev->vdev.ctrl_handler = hdl;
  193. v4l2_ctrl_handler_init(hdl, 2);
  194. /* min max step default */
  195. sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
  196. V4L2_CID_EXPOSURE,
  197. 1, 150, 1, 75);/* [msec] */
  198. sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
  199. V4L2_CID_GAIN,
  200. 14, 33, 1, 24);/* [dB] */
  201. if (hdl->error) {
  202. PERR("Could not initialize controls: %d\n",
  203. hdl->error);
  204. return hdl->error;
  205. }
  206. v4l2_ctrl_cluster(2, &sd->exposure);
  207. return 0;
  208. }
  209. /* sub-driver description */
  210. static const struct sd_desc sd_desc = {
  211. .name = MODULE_NAME,
  212. .config = sd_config,
  213. .init = sd_init,
  214. .start = dtcs033_start,
  215. .stopN = dtcs033_stopN,
  216. .pkt_scan = dtcs033_pkt_scan,
  217. .init_controls = dtcs033_init_controls,
  218. };
  219. /* -- module initialisation -- */
  220. static const struct usb_device_id device_table[] = {
  221. {USB_DEVICE(0x0547, 0x7303)},
  222. {}
  223. };
  224. MODULE_DEVICE_TABLE(usb, device_table);
  225. /* device connect */
  226. static int sd_probe(struct usb_interface *intf,
  227. const struct usb_device_id *id)
  228. {
  229. return gspca_dev_probe(intf, id,
  230. &sd_desc, sizeof(struct sd),
  231. THIS_MODULE);
  232. }
  233. static struct usb_driver sd_driver = {
  234. .name = MODULE_NAME,
  235. .id_table = device_table,
  236. .probe = sd_probe,
  237. .disconnect = gspca_disconnect,
  238. #ifdef CONFIG_PM
  239. .suspend = gspca_suspend,
  240. .resume = gspca_resume,
  241. .reset_resume = gspca_resume,
  242. #endif
  243. };
  244. module_usb_driver(sd_driver);
  245. /* ---------------------------------------------------------
  246. USB requests to start/stop the camera [USB 2.0 spec Ch.9].
  247. bRequestType :
  248. 0x40 = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
  249. 0xC0 = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
  250. */
  251. static const struct dtcs033_usb_requests dtcs033_start_reqs[] = {
  252. /* -- bRequest,wValue,wIndex,wLength */
  253. { 0x40, 0x01, 0x0001, 0x000F, 0x0000 },
  254. { 0x40, 0x01, 0x0000, 0x000F, 0x0000 },
  255. { 0x40, 0x01, 0x0001, 0x000F, 0x0000 },
  256. { 0x40, 0x18, 0x0000, 0x7F00, 0x0000 },
  257. { 0x40, 0x18, 0x0000, 0x1001, 0x0000 },
  258. { 0x40, 0x18, 0x0000, 0x0004, 0x0000 },
  259. { 0x40, 0x18, 0x0000, 0x7F01, 0x0000 },
  260. { 0x40, 0x18, 0x30E0, 0x0009, 0x0000 },
  261. { 0x40, 0x18, 0x0500, 0x012C, 0x0000 },
  262. { 0x40, 0x18, 0x0380, 0x0200, 0x0000 },
  263. { 0x40, 0x18, 0x0000, 0x035C, 0x0000 },
  264. { 0x40, 0x18, 0x05C0, 0x0438, 0x0000 },
  265. { 0x40, 0x18, 0x0440, 0x0500, 0x0000 },
  266. { 0x40, 0x18, 0x0000, 0x0668, 0x0000 },
  267. { 0x40, 0x18, 0x0000, 0x0700, 0x0000 },
  268. { 0x40, 0x18, 0x0000, 0x0800, 0x0000 },
  269. { 0x40, 0x18, 0x0000, 0x0900, 0x0000 },
  270. { 0x40, 0x18, 0x0000, 0x0A00, 0x0000 },
  271. { 0x40, 0x18, 0x0000, 0x0B00, 0x0000 },
  272. { 0x40, 0x18, 0x30E0, 0x6009, 0x0000 },
  273. { 0x40, 0x18, 0x0500, 0x612C, 0x0000 },
  274. { 0x40, 0x18, 0x2090, 0x6274, 0x0000 },
  275. { 0x40, 0x18, 0x05C0, 0x6338, 0x0000 },
  276. { 0x40, 0x18, 0x0000, 0x6400, 0x0000 },
  277. { 0x40, 0x18, 0x05C0, 0x6538, 0x0000 },
  278. { 0x40, 0x18, 0x0000, 0x6600, 0x0000 },
  279. { 0x40, 0x18, 0x0680, 0x6744, 0x0000 },
  280. { 0x40, 0x18, 0x0000, 0x6800, 0x0000 },
  281. { 0x40, 0x18, 0x0000, 0x6900, 0x0000 },
  282. { 0x40, 0x18, 0x0000, 0x6A00, 0x0000 },
  283. { 0x40, 0x18, 0x0000, 0x6B00, 0x0000 },
  284. { 0x40, 0x18, 0x0000, 0x6C00, 0x0000 },
  285. { 0x40, 0x18, 0x0000, 0x6D00, 0x0000 },
  286. { 0x40, 0x18, 0x0000, 0x6E00, 0x0000 },
  287. { 0x40, 0x18, 0x0000, 0x808C, 0x0000 },
  288. { 0x40, 0x18, 0x0010, 0x8101, 0x0000 },
  289. { 0x40, 0x18, 0x30E0, 0x8200, 0x0000 },
  290. { 0x40, 0x18, 0x0810, 0x832C, 0x0000 },
  291. { 0x40, 0x18, 0x0680, 0x842B, 0x0000 },
  292. { 0x40, 0x18, 0x0000, 0x8500, 0x0000 },
  293. { 0x40, 0x18, 0x0000, 0x8600, 0x0000 },
  294. { 0x40, 0x18, 0x0280, 0x8715, 0x0000 },
  295. { 0x40, 0x18, 0x0000, 0x880C, 0x0000 },
  296. { 0x40, 0x18, 0x0010, 0x8901, 0x0000 },
  297. { 0x40, 0x18, 0x30E0, 0x8A00, 0x0000 },
  298. { 0x40, 0x18, 0x0810, 0x8B2C, 0x0000 },
  299. { 0x40, 0x18, 0x0680, 0x8C2B, 0x0000 },
  300. { 0x40, 0x18, 0x0000, 0x8D00, 0x0000 },
  301. { 0x40, 0x18, 0x0000, 0x8E00, 0x0000 },
  302. { 0x40, 0x18, 0x0280, 0x8F15, 0x0000 },
  303. { 0x40, 0x18, 0x0010, 0xD040, 0x0000 },
  304. { 0x40, 0x18, 0x0000, 0xD100, 0x0000 },
  305. { 0x40, 0x18, 0x00B0, 0xD20A, 0x0000 },
  306. { 0x40, 0x18, 0x0000, 0xD300, 0x0000 },
  307. { 0x40, 0x18, 0x30E2, 0xD40D, 0x0000 },
  308. { 0x40, 0x18, 0x0001, 0xD5C0, 0x0000 },
  309. { 0x40, 0x18, 0x00A0, 0xD60A, 0x0000 },
  310. { 0x40, 0x18, 0x0000, 0xD700, 0x0000 },
  311. { 0x40, 0x18, 0x0000, 0x7F00, 0x0000 },
  312. { 0x40, 0x18, 0x0000, 0x1501, 0x0000 },
  313. { 0x40, 0x18, 0x0001, 0x01FF, 0x0000 },
  314. { 0x40, 0x18, 0x0000, 0x0200, 0x0000 },
  315. { 0x40, 0x18, 0x0000, 0x0304, 0x0000 },
  316. { 0x40, 0x18, 0x0000, 0x1101, 0x0000 },
  317. { 0x40, 0x18, 0x0000, 0x1201, 0x0000 },
  318. { 0x40, 0x18, 0x0000, 0x1300, 0x0000 },
  319. { 0x40, 0x18, 0x0000, 0x1400, 0x0000 },
  320. { 0x40, 0x18, 0x0000, 0x1601, 0x0000 },
  321. { 0x40, 0x18, 0x0000, 0x1800, 0x0000 },
  322. { 0x40, 0x18, 0x0000, 0x1900, 0x0000 },
  323. { 0x40, 0x18, 0x0000, 0x1A00, 0x0000 },
  324. { 0x40, 0x18, 0x2000, 0x1B00, 0x0000 },
  325. { 0x40, 0x18, 0x0000, 0x1C00, 0x0000 },
  326. { 0x40, 0x18, 0x0000, 0x2100, 0x0000 },
  327. { 0x40, 0x18, 0x00C0, 0x228E, 0x0000 },
  328. { 0x40, 0x18, 0x0000, 0x3001, 0x0000 },
  329. { 0x40, 0x18, 0x0010, 0x3101, 0x0000 },
  330. { 0x40, 0x18, 0x0008, 0x3301, 0x0000 },
  331. { 0x40, 0x18, 0x0000, 0x3400, 0x0000 },
  332. { 0x40, 0x18, 0x0012, 0x3549, 0x0000 },
  333. { 0x40, 0x18, 0x0000, 0x3620, 0x0000 },
  334. { 0x40, 0x18, 0x0001, 0x3700, 0x0000 },
  335. { 0x40, 0x18, 0x0000, 0x4000, 0x0000 },
  336. { 0x40, 0x18, 0xFFFF, 0x41FF, 0x0000 },
  337. { 0x40, 0x18, 0xFFFF, 0x42FF, 0x0000 },
  338. { 0x40, 0x18, 0x0000, 0x500F, 0x0000 },
  339. { 0x40, 0x18, 0x2272, 0x5108, 0x0000 },
  340. { 0x40, 0x18, 0x2272, 0x5208, 0x0000 },
  341. { 0x40, 0x18, 0xFFFF, 0x53FF, 0x0000 },
  342. { 0x40, 0x18, 0xFFFF, 0x54FF, 0x0000 },
  343. { 0x40, 0x18, 0x0000, 0x6000, 0x0000 },
  344. { 0x40, 0x18, 0x0000, 0x6102, 0x0000 },
  345. { 0x40, 0x18, 0x0010, 0x6214, 0x0000 },
  346. { 0x40, 0x18, 0x0C80, 0x6300, 0x0000 },
  347. { 0x40, 0x18, 0x0000, 0x6401, 0x0000 },
  348. { 0x40, 0x18, 0x0680, 0x6551, 0x0000 },
  349. { 0x40, 0x18, 0xFFFF, 0x66FF, 0x0000 },
  350. { 0x40, 0x18, 0x0000, 0x6702, 0x0000 },
  351. { 0x40, 0x18, 0x0010, 0x6800, 0x0000 },
  352. { 0x40, 0x18, 0x0000, 0x6900, 0x0000 },
  353. { 0x40, 0x18, 0x0000, 0x6A00, 0x0000 },
  354. { 0x40, 0x18, 0x0000, 0x6B00, 0x0000 },
  355. { 0x40, 0x18, 0x0000, 0x6C00, 0x0000 },
  356. { 0x40, 0x18, 0x0000, 0x6D01, 0x0000 },
  357. { 0x40, 0x18, 0x0000, 0x6E00, 0x0000 },
  358. { 0x40, 0x18, 0x0000, 0x6F00, 0x0000 },
  359. { 0x40, 0x18, 0x0000, 0x7000, 0x0000 },
  360. { 0x40, 0x18, 0x0001, 0x7118, 0x0000 },
  361. { 0x40, 0x18, 0x0000, 0x2001, 0x0000 },
  362. { 0x40, 0x18, 0x0000, 0x1101, 0x0000 },
  363. { 0x40, 0x18, 0x0000, 0x1301, 0x0000 },
  364. { 0x40, 0x18, 0x0000, 0x1300, 0x0000 },
  365. { 0x40, 0x18, 0x0000, 0x1501, 0x0000 },
  366. { 0xC0, 0x11, 0x0000, 0x24C0, 0x0003 },
  367. { 0x40, 0x18, 0x0000, 0x3000, 0x0000 },
  368. { 0x40, 0x18, 0x0000, 0x3620, 0x0000 },
  369. { 0x40, 0x18, 0x0000, 0x1501, 0x0000 },
  370. { 0x40, 0x18, 0x0010, 0x6300, 0x0000 },
  371. { 0x40, 0x18, 0x0002, 0x01F0, 0x0000 },
  372. { 0x40, 0x01, 0x0003, 0x000F, 0x0000 }
  373. };
  374. static const struct dtcs033_usb_requests dtcs033_stop_reqs[] = {
  375. /* -- bRequest,wValue,wIndex,wLength */
  376. { 0x40, 0x01, 0x0001, 0x000F, 0x0000 },
  377. { 0x40, 0x01, 0x0000, 0x000F, 0x0000 },
  378. { 0x40, 0x18, 0x0000, 0x0003, 0x0000 }
  379. };
  380. static int dtcs033_start(struct gspca_dev *gspca_dev)
  381. {
  382. return reg_reqs(gspca_dev, dtcs033_start_reqs,
  383. ARRAY_SIZE(dtcs033_start_reqs));
  384. }
  385. static void dtcs033_stopN(struct gspca_dev *gspca_dev)
  386. {
  387. reg_reqs(gspca_dev, dtcs033_stop_reqs,
  388. ARRAY_SIZE(dtcs033_stop_reqs));
  389. return;
  390. }