corsair-cpro.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * corsair-cpro.c - Linux driver for Corsair Commander Pro
  4. * Copyright (C) 2020 Marius Zachmann <mail@mariuszachmann.de>
  5. *
  6. * This driver uses hid reports to communicate with the device to allow hidraw userspace drivers
  7. * still being used. The device does not use report ids. When using hidraw and this driver
  8. * simultaniously, reports could be switched.
  9. */
  10. #include <linux/bitops.h>
  11. #include <linux/completion.h>
  12. #include <linux/debugfs.h>
  13. #include <linux/hid.h>
  14. #include <linux/hwmon.h>
  15. #include <linux/kernel.h>
  16. #include <linux/module.h>
  17. #include <linux/mutex.h>
  18. #include <linux/seq_file.h>
  19. #include <linux/slab.h>
  20. #include <linux/spinlock.h>
  21. #include <linux/types.h>
  22. #define USB_VENDOR_ID_CORSAIR 0x1b1c
  23. #define USB_PRODUCT_ID_CORSAIR_COMMANDERPRO 0x0c10
  24. #define USB_PRODUCT_ID_CORSAIR_1000D 0x1d00
  25. #define OUT_BUFFER_SIZE 63
  26. #define IN_BUFFER_SIZE 16
  27. #define LABEL_LENGTH 11
  28. #define REQ_TIMEOUT 300
  29. #define CTL_GET_FW_VER 0x02 /* returns the firmware version in bytes 1-3 */
  30. #define CTL_GET_BL_VER 0x06 /* returns the bootloader version in bytes 1-2 */
  31. #define CTL_GET_TMP_CNCT 0x10 /*
  32. * returns in bytes 1-4 for each temp sensor:
  33. * 0 not connected
  34. * 1 connected
  35. */
  36. #define CTL_GET_TMP 0x11 /*
  37. * send: byte 1 is channel, rest zero
  38. * rcv: returns temp for channel in centi-degree celsius
  39. * in bytes 1 and 2 as a two's complement value
  40. * returns 0x11 in byte 0 if no sensor is connected
  41. */
  42. #define CTL_GET_VOLT 0x12 /*
  43. * send: byte 1 is rail number: 0 = 12v, 1 = 5v, 2 = 3.3v
  44. * rcv: returns millivolt in bytes 1,2
  45. * returns error 0x10 if request is invalid
  46. */
  47. #define CTL_GET_FAN_CNCT 0x20 /*
  48. * returns in bytes 1-6 for each fan:
  49. * 0 not connected
  50. * 1 3pin
  51. * 2 4pin
  52. */
  53. #define CTL_GET_FAN_RPM 0x21 /*
  54. * send: byte 1 is channel, rest zero
  55. * rcv: returns rpm in bytes 1,2
  56. */
  57. #define CTL_GET_FAN_PWM 0x22 /*
  58. * send: byte 1 is channel, rest zero
  59. * rcv: returns pwm in byte 1 if it was set
  60. * returns error 0x12 if fan is controlled via
  61. * fan_target or fan curve
  62. */
  63. #define CTL_SET_FAN_FPWM 0x23 /*
  64. * set fixed pwm
  65. * send: byte 1 is fan number
  66. * send: byte 2 is percentage from 0 - 100
  67. */
  68. #define CTL_SET_FAN_TARGET 0x24 /*
  69. * set target rpm
  70. * send: byte 1 is fan number
  71. * send: byte 2-3 is target
  72. * device accepts all values from 0x00 - 0xFFFF
  73. */
  74. #define NUM_FANS 6
  75. #define NUM_TEMP_SENSORS 4
  76. struct ccp_device {
  77. struct hid_device *hdev;
  78. struct device *hwmon_dev;
  79. struct dentry *debugfs;
  80. /* For reinitializing the completion below */
  81. spinlock_t wait_input_report_lock;
  82. struct completion wait_input_report;
  83. struct mutex mutex; /* whenever buffer is used, lock before send_usb_cmd */
  84. u8 *cmd_buffer;
  85. u8 *buffer;
  86. int buffer_recv_size; /* number of received bytes in buffer */
  87. int target[NUM_FANS];
  88. DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS);
  89. DECLARE_BITMAP(fan_cnct, NUM_FANS);
  90. char fan_label[NUM_FANS][LABEL_LENGTH];
  91. u8 firmware_ver[3];
  92. u8 bootloader_ver[2];
  93. };
  94. /* converts response error in buffer to errno */
  95. static int ccp_get_errno(struct ccp_device *ccp)
  96. {
  97. switch (ccp->buffer[0]) {
  98. case 0x00: /* success */
  99. return 0;
  100. case 0x01: /* called invalid command */
  101. return -EOPNOTSUPP;
  102. case 0x10: /* called GET_VOLT / GET_TMP with invalid arguments */
  103. return -EINVAL;
  104. case 0x11: /* requested temps of disconnected sensors */
  105. case 0x12: /* requested pwm of not pwm controlled channels */
  106. return -ENODATA;
  107. default:
  108. hid_dbg(ccp->hdev, "unknown device response error: %d", ccp->buffer[0]);
  109. return -EIO;
  110. }
  111. }
  112. /* send command, check for error in response, response in ccp->buffer */
  113. static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2, u8 byte3)
  114. {
  115. unsigned long t;
  116. int ret;
  117. memset(ccp->cmd_buffer, 0x00, OUT_BUFFER_SIZE);
  118. ccp->cmd_buffer[0] = command;
  119. ccp->cmd_buffer[1] = byte1;
  120. ccp->cmd_buffer[2] = byte2;
  121. ccp->cmd_buffer[3] = byte3;
  122. /*
  123. * Disable raw event parsing for a moment to safely reinitialize the
  124. * completion. Reinit is done because hidraw could have triggered
  125. * the raw event parsing and marked the ccp->wait_input_report
  126. * completion as done.
  127. */
  128. spin_lock_bh(&ccp->wait_input_report_lock);
  129. reinit_completion(&ccp->wait_input_report);
  130. spin_unlock_bh(&ccp->wait_input_report_lock);
  131. ret = hid_hw_output_report(ccp->hdev, ccp->cmd_buffer, OUT_BUFFER_SIZE);
  132. if (ret < 0)
  133. return ret;
  134. t = wait_for_completion_timeout(&ccp->wait_input_report, msecs_to_jiffies(REQ_TIMEOUT));
  135. if (!t)
  136. return -ETIMEDOUT;
  137. if (ccp->buffer_recv_size != IN_BUFFER_SIZE)
  138. return -EPROTO;
  139. return ccp_get_errno(ccp);
  140. }
  141. static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
  142. {
  143. struct ccp_device *ccp = hid_get_drvdata(hdev);
  144. /* only copy buffer when requested */
  145. spin_lock(&ccp->wait_input_report_lock);
  146. if (!completion_done(&ccp->wait_input_report)) {
  147. memcpy(ccp->buffer, data, min(IN_BUFFER_SIZE, size));
  148. ccp->buffer_recv_size = size;
  149. complete_all(&ccp->wait_input_report);
  150. }
  151. spin_unlock(&ccp->wait_input_report_lock);
  152. return 0;
  153. }
  154. /* requests and returns single data values depending on channel */
  155. static int get_data(struct ccp_device *ccp, int command, int channel, bool two_byte_data)
  156. {
  157. int ret;
  158. mutex_lock(&ccp->mutex);
  159. ret = send_usb_cmd(ccp, command, channel, 0, 0);
  160. if (ret)
  161. goto out_unlock;
  162. ret = ccp->buffer[1];
  163. if (two_byte_data)
  164. ret = (ret << 8) + ccp->buffer[2];
  165. out_unlock:
  166. mutex_unlock(&ccp->mutex);
  167. return ret;
  168. }
  169. static int set_pwm(struct ccp_device *ccp, int channel, long val)
  170. {
  171. int ret;
  172. if (val < 0 || val > 255)
  173. return -EINVAL;
  174. /* The Corsair Commander Pro uses values from 0-100 */
  175. val = DIV_ROUND_CLOSEST(val * 100, 255);
  176. mutex_lock(&ccp->mutex);
  177. ret = send_usb_cmd(ccp, CTL_SET_FAN_FPWM, channel, val, 0);
  178. if (!ret)
  179. ccp->target[channel] = -ENODATA;
  180. mutex_unlock(&ccp->mutex);
  181. return ret;
  182. }
  183. static int set_target(struct ccp_device *ccp, int channel, long val)
  184. {
  185. int ret;
  186. val = clamp_val(val, 0, 0xFFFF);
  187. ccp->target[channel] = val;
  188. mutex_lock(&ccp->mutex);
  189. ret = send_usb_cmd(ccp, CTL_SET_FAN_TARGET, channel, val >> 8, val);
  190. mutex_unlock(&ccp->mutex);
  191. return ret;
  192. }
  193. static int ccp_read_string(struct device *dev, enum hwmon_sensor_types type,
  194. u32 attr, int channel, const char **str)
  195. {
  196. struct ccp_device *ccp = dev_get_drvdata(dev);
  197. switch (type) {
  198. case hwmon_fan:
  199. switch (attr) {
  200. case hwmon_fan_label:
  201. *str = ccp->fan_label[channel];
  202. return 0;
  203. default:
  204. break;
  205. }
  206. break;
  207. default:
  208. break;
  209. }
  210. return -EOPNOTSUPP;
  211. }
  212. static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
  213. u32 attr, int channel, long *val)
  214. {
  215. struct ccp_device *ccp = dev_get_drvdata(dev);
  216. int ret;
  217. switch (type) {
  218. case hwmon_temp:
  219. switch (attr) {
  220. case hwmon_temp_input:
  221. ret = get_data(ccp, CTL_GET_TMP, channel, true);
  222. if (ret < 0)
  223. return ret;
  224. *val = (s16)ret * 10;
  225. return 0;
  226. default:
  227. break;
  228. }
  229. break;
  230. case hwmon_fan:
  231. switch (attr) {
  232. case hwmon_fan_input:
  233. ret = get_data(ccp, CTL_GET_FAN_RPM, channel, true);
  234. if (ret < 0)
  235. return ret;
  236. *val = ret;
  237. return 0;
  238. case hwmon_fan_target:
  239. /* how to read target values from the device is unknown */
  240. /* driver returns last set value or 0 */
  241. if (ccp->target[channel] < 0)
  242. return -ENODATA;
  243. *val = ccp->target[channel];
  244. return 0;
  245. default:
  246. break;
  247. }
  248. break;
  249. case hwmon_pwm:
  250. switch (attr) {
  251. case hwmon_pwm_input:
  252. ret = get_data(ccp, CTL_GET_FAN_PWM, channel, false);
  253. if (ret < 0)
  254. return ret;
  255. *val = DIV_ROUND_CLOSEST(ret * 255, 100);
  256. return 0;
  257. default:
  258. break;
  259. }
  260. break;
  261. case hwmon_in:
  262. switch (attr) {
  263. case hwmon_in_input:
  264. ret = get_data(ccp, CTL_GET_VOLT, channel, true);
  265. if (ret < 0)
  266. return ret;
  267. *val = ret;
  268. return 0;
  269. default:
  270. break;
  271. }
  272. break;
  273. default:
  274. break;
  275. }
  276. return -EOPNOTSUPP;
  277. };
  278. static int ccp_write(struct device *dev, enum hwmon_sensor_types type,
  279. u32 attr, int channel, long val)
  280. {
  281. struct ccp_device *ccp = dev_get_drvdata(dev);
  282. switch (type) {
  283. case hwmon_pwm:
  284. switch (attr) {
  285. case hwmon_pwm_input:
  286. return set_pwm(ccp, channel, val);
  287. default:
  288. break;
  289. }
  290. break;
  291. case hwmon_fan:
  292. switch (attr) {
  293. case hwmon_fan_target:
  294. return set_target(ccp, channel, val);
  295. default:
  296. break;
  297. }
  298. break;
  299. default:
  300. break;
  301. }
  302. return -EOPNOTSUPP;
  303. };
  304. static umode_t ccp_is_visible(const void *data, enum hwmon_sensor_types type,
  305. u32 attr, int channel)
  306. {
  307. const struct ccp_device *ccp = data;
  308. switch (type) {
  309. case hwmon_temp:
  310. if (!test_bit(channel, ccp->temp_cnct))
  311. break;
  312. switch (attr) {
  313. case hwmon_temp_input:
  314. return 0444;
  315. case hwmon_temp_label:
  316. return 0444;
  317. default:
  318. break;
  319. }
  320. break;
  321. case hwmon_fan:
  322. if (!test_bit(channel, ccp->fan_cnct))
  323. break;
  324. switch (attr) {
  325. case hwmon_fan_input:
  326. return 0444;
  327. case hwmon_fan_label:
  328. return 0444;
  329. case hwmon_fan_target:
  330. return 0644;
  331. default:
  332. break;
  333. }
  334. break;
  335. case hwmon_pwm:
  336. if (!test_bit(channel, ccp->fan_cnct))
  337. break;
  338. switch (attr) {
  339. case hwmon_pwm_input:
  340. return 0644;
  341. default:
  342. break;
  343. }
  344. break;
  345. case hwmon_in:
  346. switch (attr) {
  347. case hwmon_in_input:
  348. return 0444;
  349. default:
  350. break;
  351. }
  352. break;
  353. default:
  354. break;
  355. }
  356. return 0;
  357. };
  358. static const struct hwmon_ops ccp_hwmon_ops = {
  359. .is_visible = ccp_is_visible,
  360. .read = ccp_read,
  361. .read_string = ccp_read_string,
  362. .write = ccp_write,
  363. };
  364. static const struct hwmon_channel_info * const ccp_info[] = {
  365. HWMON_CHANNEL_INFO(chip,
  366. HWMON_C_REGISTER_TZ),
  367. HWMON_CHANNEL_INFO(temp,
  368. HWMON_T_INPUT,
  369. HWMON_T_INPUT,
  370. HWMON_T_INPUT,
  371. HWMON_T_INPUT
  372. ),
  373. HWMON_CHANNEL_INFO(fan,
  374. HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET,
  375. HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET,
  376. HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET,
  377. HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET,
  378. HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET,
  379. HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET
  380. ),
  381. HWMON_CHANNEL_INFO(pwm,
  382. HWMON_PWM_INPUT,
  383. HWMON_PWM_INPUT,
  384. HWMON_PWM_INPUT,
  385. HWMON_PWM_INPUT,
  386. HWMON_PWM_INPUT,
  387. HWMON_PWM_INPUT
  388. ),
  389. HWMON_CHANNEL_INFO(in,
  390. HWMON_I_INPUT,
  391. HWMON_I_INPUT,
  392. HWMON_I_INPUT
  393. ),
  394. NULL
  395. };
  396. static const struct hwmon_chip_info ccp_chip_info = {
  397. .ops = &ccp_hwmon_ops,
  398. .info = ccp_info,
  399. };
  400. /* read fan connection status and set labels */
  401. static int get_fan_cnct(struct ccp_device *ccp)
  402. {
  403. int channel;
  404. int mode;
  405. int ret;
  406. ret = send_usb_cmd(ccp, CTL_GET_FAN_CNCT, 0, 0, 0);
  407. if (ret)
  408. return ret;
  409. for (channel = 0; channel < NUM_FANS; channel++) {
  410. mode = ccp->buffer[channel + 1];
  411. if (mode == 0)
  412. continue;
  413. set_bit(channel, ccp->fan_cnct);
  414. ccp->target[channel] = -ENODATA;
  415. switch (mode) {
  416. case 1:
  417. scnprintf(ccp->fan_label[channel], LABEL_LENGTH,
  418. "fan%d 3pin", channel + 1);
  419. break;
  420. case 2:
  421. scnprintf(ccp->fan_label[channel], LABEL_LENGTH,
  422. "fan%d 4pin", channel + 1);
  423. break;
  424. default:
  425. scnprintf(ccp->fan_label[channel], LABEL_LENGTH,
  426. "fan%d other", channel + 1);
  427. break;
  428. }
  429. }
  430. return 0;
  431. }
  432. /* read temp sensor connection status */
  433. static int get_temp_cnct(struct ccp_device *ccp)
  434. {
  435. int channel;
  436. int mode;
  437. int ret;
  438. ret = send_usb_cmd(ccp, CTL_GET_TMP_CNCT, 0, 0, 0);
  439. if (ret)
  440. return ret;
  441. for (channel = 0; channel < NUM_TEMP_SENSORS; channel++) {
  442. mode = ccp->buffer[channel + 1];
  443. if (mode == 0)
  444. continue;
  445. set_bit(channel, ccp->temp_cnct);
  446. }
  447. return 0;
  448. }
  449. /* read firmware version */
  450. static int get_fw_version(struct ccp_device *ccp)
  451. {
  452. int ret;
  453. ret = send_usb_cmd(ccp, CTL_GET_FW_VER, 0, 0, 0);
  454. if (ret) {
  455. hid_notice(ccp->hdev, "Failed to read firmware version.\n");
  456. return ret;
  457. }
  458. ccp->firmware_ver[0] = ccp->buffer[1];
  459. ccp->firmware_ver[1] = ccp->buffer[2];
  460. ccp->firmware_ver[2] = ccp->buffer[3];
  461. return 0;
  462. }
  463. /* read bootloader version */
  464. static int get_bl_version(struct ccp_device *ccp)
  465. {
  466. int ret;
  467. ret = send_usb_cmd(ccp, CTL_GET_BL_VER, 0, 0, 0);
  468. if (ret) {
  469. hid_notice(ccp->hdev, "Failed to read bootloader version.\n");
  470. return ret;
  471. }
  472. ccp->bootloader_ver[0] = ccp->buffer[1];
  473. ccp->bootloader_ver[1] = ccp->buffer[2];
  474. return 0;
  475. }
  476. static int firmware_show(struct seq_file *seqf, void *unused)
  477. {
  478. struct ccp_device *ccp = seqf->private;
  479. seq_printf(seqf, "%d.%d.%d\n",
  480. ccp->firmware_ver[0],
  481. ccp->firmware_ver[1],
  482. ccp->firmware_ver[2]);
  483. return 0;
  484. }
  485. DEFINE_SHOW_ATTRIBUTE(firmware);
  486. static int bootloader_show(struct seq_file *seqf, void *unused)
  487. {
  488. struct ccp_device *ccp = seqf->private;
  489. seq_printf(seqf, "%d.%d\n",
  490. ccp->bootloader_ver[0],
  491. ccp->bootloader_ver[1]);
  492. return 0;
  493. }
  494. DEFINE_SHOW_ATTRIBUTE(bootloader);
  495. static void ccp_debugfs_init(struct ccp_device *ccp)
  496. {
  497. char name[32];
  498. int ret;
  499. scnprintf(name, sizeof(name), "corsaircpro-%s", dev_name(&ccp->hdev->dev));
  500. ccp->debugfs = debugfs_create_dir(name, NULL);
  501. ret = get_fw_version(ccp);
  502. if (!ret)
  503. debugfs_create_file("firmware_version", 0444,
  504. ccp->debugfs, ccp, &firmware_fops);
  505. ret = get_bl_version(ccp);
  506. if (!ret)
  507. debugfs_create_file("bootloader_version", 0444,
  508. ccp->debugfs, ccp, &bootloader_fops);
  509. }
  510. static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
  511. {
  512. struct ccp_device *ccp;
  513. int ret;
  514. ccp = devm_kzalloc(&hdev->dev, sizeof(*ccp), GFP_KERNEL);
  515. if (!ccp)
  516. return -ENOMEM;
  517. ccp->cmd_buffer = devm_kmalloc(&hdev->dev, OUT_BUFFER_SIZE, GFP_KERNEL);
  518. if (!ccp->cmd_buffer)
  519. return -ENOMEM;
  520. ccp->buffer = devm_kmalloc(&hdev->dev, IN_BUFFER_SIZE, GFP_KERNEL);
  521. if (!ccp->buffer)
  522. return -ENOMEM;
  523. ret = hid_parse(hdev);
  524. if (ret)
  525. return ret;
  526. ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
  527. if (ret)
  528. return ret;
  529. ret = hid_hw_open(hdev);
  530. if (ret)
  531. goto out_hw_stop;
  532. ccp->hdev = hdev;
  533. hid_set_drvdata(hdev, ccp);
  534. mutex_init(&ccp->mutex);
  535. spin_lock_init(&ccp->wait_input_report_lock);
  536. init_completion(&ccp->wait_input_report);
  537. hid_device_io_start(hdev);
  538. /* temp and fan connection status only updates when device is powered on */
  539. ret = get_temp_cnct(ccp);
  540. if (ret)
  541. goto out_hw_close;
  542. ret = get_fan_cnct(ccp);
  543. if (ret)
  544. goto out_hw_close;
  545. ccp_debugfs_init(ccp);
  546. ccp->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsaircpro",
  547. ccp, &ccp_chip_info, NULL);
  548. if (IS_ERR(ccp->hwmon_dev)) {
  549. ret = PTR_ERR(ccp->hwmon_dev);
  550. goto out_hw_close;
  551. }
  552. return 0;
  553. out_hw_close:
  554. hid_hw_close(hdev);
  555. out_hw_stop:
  556. hid_hw_stop(hdev);
  557. return ret;
  558. }
  559. static void ccp_remove(struct hid_device *hdev)
  560. {
  561. struct ccp_device *ccp = hid_get_drvdata(hdev);
  562. debugfs_remove_recursive(ccp->debugfs);
  563. hwmon_device_unregister(ccp->hwmon_dev);
  564. hid_hw_close(hdev);
  565. hid_hw_stop(hdev);
  566. }
  567. static const struct hid_device_id ccp_devices[] = {
  568. { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_PRODUCT_ID_CORSAIR_COMMANDERPRO) },
  569. { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_PRODUCT_ID_CORSAIR_1000D) },
  570. { }
  571. };
  572. static struct hid_driver ccp_driver = {
  573. .name = "corsair-cpro",
  574. .id_table = ccp_devices,
  575. .probe = ccp_probe,
  576. .remove = ccp_remove,
  577. .raw_event = ccp_raw_event,
  578. };
  579. MODULE_DEVICE_TABLE(hid, ccp_devices);
  580. MODULE_DESCRIPTION("Corsair Commander Pro controller driver");
  581. MODULE_LICENSE("GPL");
  582. static int __init ccp_init(void)
  583. {
  584. return hid_register_driver(&ccp_driver);
  585. }
  586. static void __exit ccp_exit(void)
  587. {
  588. hid_unregister_driver(&ccp_driver);
  589. }
  590. /*
  591. * When compiling this driver as built-in, hwmon initcalls will get called before the
  592. * hid driver and this driver would fail to register. late_initcall solves this.
  593. */
  594. late_initcall(ccp_init);
  595. module_exit(ccp_exit);