lcd_2x20.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/fs.h>
  4. #include <linux/i2c.h>
  5. #include <linux/uaccess.h>
  6. #include <linux/miscdevice.h>
  7. #include <linux/pci.h>
  8. #include <linux/mutex.h>
  9. #include <linux/delay.h>
  10. #include <linux/slab.h>
  11. #include <linux/string.h>
  12. #define DRIVER_NAME "lcd2002"
  13. #define LCD_I2C_ADDR_DEFAULT 0x3E
  14. /* 控制字节定义 */
  15. #define LCD_CONTROL_BYTE 0x00
  16. #define LCD_DATA_BYTE 0x40
  17. /* 基本指令 */
  18. #define LCD_CLEAR_DISPLAY 0x01
  19. #define LCD_RETURN_HOME 0x02
  20. #define LCD_ENTRY_MODE_SET 0x04
  21. #define LCD_DISPLAY_CONTROL 0x08
  22. #define LCD_CURSOR_SHIFT 0x10
  23. #define LCD_FUNCTION_SET 0x20
  24. #define LCD_SET_CGRAM_ADDR 0x40
  25. #define LCD_SET_DDRAM_ADDR 0x80
  26. /* Entry Mode */
  27. #define LCD_ENTRY_RIGHT 0x00
  28. #define LCD_ENTRY_LEFT 0x02
  29. /* Display Control */
  30. #define LCD_DISPLAY_ON 0x04
  31. #define LCD_DISPLAY_OFF 0x00
  32. #define LCD_CURSOR_ON 0x02
  33. #define LCD_CURSOR_OFF 0x00
  34. #define LCD_BLINK_ON 0x01
  35. #define LCD_BLINK_OFF 0x00
  36. #define LCD_LINE1_ADDR 0x00
  37. #define LCD_LINE2_ADDR 0x40
  38. #define LCD_COLS 20
  39. #define LCD_ROWS 2
  40. /* IOCTL */
  41. #define LCD_IOCTL_MAGIC 'L'
  42. #define LCD_IOCTL_CLEAR _IO(LCD_IOCTL_MAGIC, 0)
  43. #define LCD_IOCTL_HOME _IO(LCD_IOCTL_MAGIC, 1)
  44. #define LCD_IOCTL_SET_CURSOR _IOW(LCD_IOCTL_MAGIC, 2, struct lcd_pos)
  45. #define LCD_IOCTL_DISPLAY_CTRL _IOW(LCD_IOCTL_MAGIC, 3, unsigned char[3])
  46. struct lcd_pos {
  47. unsigned char col;
  48. unsigned char row;
  49. };
  50. struct lcd2002_dev {
  51. struct i2c_adapter *adapter;
  52. struct mutex lock;
  53. unsigned char addr;
  54. };
  55. static struct lcd2002_dev *lcd_dev = NULL;
  56. /* 模块参数 - 多种匹配方式 */
  57. static char *pci_addr = "0000:00:15.0";
  58. module_param(pci_addr, charp, 0444);
  59. MODULE_PARM_DESC(pci_addr, "PCI address (e.g., 0000:00:15.0)");
  60. static char *adapter_name = NULL; /* 如: "Synopsys DesignWare I2C adapter" */
  61. module_param(adapter_name, charp, 0444);
  62. MODULE_PARM_DESC(adapter_name, "I2C adapter name (e.g., Synopsys DesignWare)");
  63. static int i2c_bus = -1; /* 直接指定总线号,如 1 对应 /dev/i2c-1 */
  64. module_param(i2c_bus, int, 0444);
  65. MODULE_PARM_DESC(i2c_bus, "I2C bus number (e.g., 1 for /dev/i2c-1)");
  66. static unsigned char i2c_address = LCD_I2C_ADDR_DEFAULT;
  67. module_param(i2c_address, byte, 0444);
  68. MODULE_PARM_DESC(i2c_address, "LCD I2C address (default: 0x3E)");
  69. static int debug = 1;
  70. module_param(debug, int, 0644);
  71. MODULE_PARM_DESC(debug, "Debug level (0=none, 1=normal, 2=verbose)");
  72. /* 延时 */
  73. static inline void lcd_delay_ms(unsigned int ms)
  74. {
  75. msleep(ms);
  76. }
  77. /* I2C写 */
  78. static int lcd_i2c_write(unsigned char *buf, int len)
  79. {
  80. struct i2c_msg msg;
  81. int ret;
  82. if (!lcd_dev || !lcd_dev->adapter)
  83. return -ENODEV;
  84. msg.addr = lcd_dev->addr;
  85. msg.flags = 0;
  86. msg.len = len;
  87. msg.buf = buf;
  88. ret = i2c_transfer(lcd_dev->adapter, &msg, 1);
  89. if (ret != 1) {
  90. dev_err(&lcd_dev->adapter->dev, "I2C write failed: %d\n", ret);
  91. return ret < 0 ? ret : -EIO;
  92. }
  93. return 0;
  94. }
  95. /* 发送指令 */
  96. static int lcd_send_command(unsigned char cmd)
  97. {
  98. unsigned char buf[2];
  99. int ret;
  100. buf[0] = LCD_CONTROL_BYTE;
  101. buf[1] = cmd;
  102. mutex_lock(&lcd_dev->lock);
  103. ret = lcd_i2c_write(buf, 2);
  104. mutex_unlock(&lcd_dev->lock);
  105. if (ret == 0)
  106. lcd_delay_ms(2);
  107. return ret;
  108. }
  109. /* 发送数据 */
  110. static int lcd_send_data(unsigned char data)
  111. {
  112. unsigned char buf[2];
  113. int ret;
  114. buf[0] = LCD_DATA_BYTE;
  115. buf[1] = data;
  116. mutex_lock(&lcd_dev->lock);
  117. ret = lcd_i2c_write(buf, 2);
  118. mutex_unlock(&lcd_dev->lock);
  119. if (ret == 0)
  120. lcd_delay_ms(1);
  121. return ret;
  122. }
  123. /* 初始化LCD */
  124. static int lcd_init_display(void)
  125. {
  126. int ret;
  127. lcd_delay_ms(50);
  128. /* 功能设置: 2行, 5x8点阵 (0x28) */
  129. ret = lcd_send_command(LCD_FUNCTION_SET | 0x08);
  130. if (ret < 0) return ret;
  131. lcd_delay_ms(5);
  132. /* 显示开,光标关,闪烁关 (0x0C) */
  133. ret = lcd_send_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON | LCD_CURSOR_OFF | LCD_BLINK_OFF);
  134. if (ret < 0) return ret;
  135. /* 清屏 */
  136. ret = lcd_send_command(LCD_CLEAR_DISPLAY);
  137. if (ret < 0) return ret;
  138. lcd_delay_ms(3);
  139. /* 输入模式: 光标右移 (0x06) */
  140. ret = lcd_send_command(LCD_ENTRY_MODE_SET | LCD_ENTRY_RIGHT);
  141. return ret;
  142. }
  143. /* 设置光标 */
  144. static int lcd_set_cursor(unsigned char col, unsigned char row)
  145. {
  146. unsigned char addr;
  147. if (col >= LCD_COLS || row >= LCD_ROWS)
  148. return -EINVAL;
  149. addr = (row == 0) ? (LCD_LINE1_ADDR + col) : (LCD_LINE2_ADDR + col);
  150. return lcd_send_command(LCD_SET_DDRAM_ADDR | addr);
  151. }
  152. /* 清屏 */
  153. static int lcd_clear(void)
  154. {
  155. int ret = lcd_send_command(LCD_CLEAR_DISPLAY);
  156. if (ret == 0) lcd_delay_ms(2);
  157. return ret;
  158. }
  159. /* 显示控制 */
  160. static int lcd_display_control(unsigned char display, unsigned char cursor, unsigned char blink)
  161. {
  162. return lcd_send_command(LCD_DISPLAY_CONTROL | display | cursor | blink);
  163. }
  164. /* 写字符串 */
  165. static int lcd_write_string(const char *str, size_t len)
  166. {
  167. size_t i;
  168. int ret = 0;
  169. unsigned char row = 0, col = 0;
  170. for (i = 0; i < len; i++) {
  171. char c = str[i];
  172. if (c == '\n') {
  173. row = (row + 1) % LCD_ROWS;
  174. col = 0;
  175. ret = lcd_set_cursor(col, row);
  176. } else if (c == '\r') {
  177. col = 0;
  178. ret = lcd_set_cursor(col, row);
  179. } else if (c == '\f') {
  180. ret = lcd_clear();
  181. row = col = 0;
  182. } else {
  183. if (col >= LCD_COLS) {
  184. row = (row + 1) % LCD_ROWS;
  185. col = 0;
  186. ret = lcd_set_cursor(col, row);
  187. if (ret < 0) break;
  188. }
  189. ret = lcd_send_data(c);
  190. col++;
  191. }
  192. if (ret < 0) break;
  193. }
  194. return ret;
  195. }
  196. /* 调试:打印适配器信息 */
  197. static void print_adapter_info(struct i2c_adapter *adap, int nr)
  198. {
  199. struct device *parent;
  200. char pci_str[64] = "N/A";
  201. if (!debug) return;
  202. pr_info(" [%d] %s (class: 0x%lx)\n", nr, adap->name, adap->class);
  203. parent = adap->dev.parent;
  204. while (parent) {
  205. if (parent->bus == &pci_bus_type) {
  206. struct pci_dev *pdev = to_pci_dev(parent);
  207. snprintf(pci_str, sizeof(pci_str), "%04x:%02x:%02x.%x",
  208. pci_domain_nr(pdev->bus),
  209. pdev->bus->number,
  210. PCI_SLOT(pdev->devfn),
  211. PCI_FUNC(pdev->devfn));
  212. pr_info(" -> PCI: %s [%s]\n", pci_str, dev_name(parent));
  213. break;
  214. } else {
  215. pr_info(" -> %s [%s]\n", dev_name(parent), parent->bus ? parent->bus->name : "no-bus");
  216. }
  217. parent = parent->parent;
  218. }
  219. }
  220. /* 方式1: 通过PCI地址查找 */
  221. static struct i2c_adapter *find_by_pci(const char *target_pci)
  222. {
  223. struct i2c_adapter *adapter;
  224. int nr = 0;
  225. bool found = false;
  226. pr_info("Searching for PCI %s...\n", target_pci);
  227. while ((adapter = i2c_get_adapter(nr))) {
  228. struct device *parent = adapter->dev.parent;
  229. char pci_buf[32] = "";
  230. if (debug >= 2)
  231. print_adapter_info(adapter, nr);
  232. /* 递归向上查找PCI设备 */
  233. while (parent && !found) {
  234. if (parent->bus == &pci_bus_type) {
  235. struct pci_dev *pdev = to_pci_dev(parent);
  236. snprintf(pci_buf, sizeof(pci_buf), "%04x:%02x:%02x.%x",
  237. pci_domain_nr(pdev->bus),
  238. pdev->bus->number,
  239. PCI_SLOT(pdev->devfn),
  240. PCI_FUNC(pdev->devfn));
  241. if (strcmp(pci_buf, target_pci) == 0) {
  242. pr_info("Found match at i2c-%d: %s\n", nr, adapter->name);
  243. found = true;
  244. break;
  245. }
  246. break; /* 只检查第一级PCI父设备 */
  247. }
  248. parent = parent->parent;
  249. }
  250. if (found)
  251. return adapter;
  252. i2c_put_adapter(adapter);
  253. nr++;
  254. }
  255. return NULL;
  256. }
  257. /* 方式2: 通过名称查找 */
  258. static struct i2c_adapter *find_by_name(const char *name)
  259. {
  260. struct i2c_adapter *adapter;
  261. int nr = 0;
  262. pr_info("Searching for adapter name '%s'...\n", name);
  263. while ((adapter = i2c_get_adapter(nr))) {
  264. if (debug >= 2)
  265. print_adapter_info(adapter, nr);
  266. if (strstr(adapter->name, name)) {
  267. pr_info("Found match at i2c-%d: %s\n", nr, adapter->name);
  268. return adapter;
  269. }
  270. i2c_put_adapter(adapter);
  271. nr++;
  272. }
  273. return NULL;
  274. }
  275. /* 方式3: 通过总线号 */
  276. static struct i2c_adapter *find_by_bus(int bus)
  277. {
  278. pr_info("Trying i2c bus %d...\n", bus);
  279. return i2c_get_adapter(bus);
  280. }
  281. /* 查找适配器主函数 */
  282. static struct i2c_adapter *find_adapter(void)
  283. {
  284. struct i2c_adapter *adapter = NULL;
  285. /* 优先级1: 直接指定总线号 */
  286. if (i2c_bus >= 0) {
  287. adapter = find_by_bus(i2c_bus);
  288. if (adapter) goto done;
  289. pr_warn("Specified i2c_bus %d not found\n", i2c_bus);
  290. }
  291. /* 优先级2: PCI地址 */
  292. if (pci_addr && strlen(pci_addr) > 0) {
  293. adapter = find_by_pci(pci_addr);
  294. if (adapter) goto done;
  295. pr_warn("PCI address %s not found\n", pci_addr);
  296. }
  297. /* 优先级3: 适配器名称 */
  298. if (adapter_name && strlen(adapter_name) > 0) {
  299. adapter = find_by_name(adapter_name);
  300. if (adapter) goto done;
  301. pr_warn("Adapter name '%s' not found\n", adapter_name);
  302. }
  303. /* 回退: 自动检测常见名称 (Synopsys DesignWare) */
  304. pr_info("Trying auto-detect (Synopsys DesignWare)...\n");
  305. adapter = find_by_name("Synopsys DesignWare");
  306. if (adapter) goto done;
  307. /* 最后回退: 使用 i2c-1 (根据用户环境) */
  308. pr_info("Trying fallback i2c-1...\n");
  309. adapter = find_by_bus(1);
  310. done:
  311. return adapter;
  312. }
  313. /* 文件操作 */
  314. static int lcd_open(struct inode *inode, struct file *file)
  315. {
  316. return lcd_dev ? 0 : -ENODEV;
  317. }
  318. static int lcd_release(struct inode *inode, struct file *file)
  319. {
  320. return 0;
  321. }
  322. static ssize_t lcd_write(struct file *file, const char __user *buf,
  323. size_t count, loff_t *ppos)
  324. {
  325. char *kbuf;
  326. int ret;
  327. if (count == 0) return 0;
  328. if (count > 256) count = 256;
  329. kbuf = kmalloc(count + 1, GFP_KERNEL);
  330. if (!kbuf) return -ENOMEM;
  331. if (copy_from_user(kbuf, buf, count)) {
  332. kfree(kbuf);
  333. return -EFAULT;
  334. }
  335. kbuf[count] = '\0';
  336. ret = lcd_write_string(kbuf, count);
  337. kfree(kbuf);
  338. return ret < 0 ? ret : count;
  339. }
  340. static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  341. {
  342. int ret = 0;
  343. switch (cmd) {
  344. case LCD_IOCTL_CLEAR:
  345. ret = lcd_clear();
  346. break;
  347. case LCD_IOCTL_HOME:
  348. ret = lcd_send_command(LCD_RETURN_HOME);
  349. if (ret == 0) lcd_delay_ms(2);
  350. break;
  351. case LCD_IOCTL_SET_CURSOR: {
  352. struct lcd_pos pos;
  353. if (copy_from_user(&pos, (void __user *)arg, sizeof(pos)))
  354. return -EFAULT;
  355. ret = lcd_set_cursor(pos.col, pos.row);
  356. break;
  357. }
  358. case LCD_IOCTL_DISPLAY_CTRL: {
  359. unsigned char ctrl[3];
  360. if (copy_from_user(ctrl, (void __user *)arg, sizeof(ctrl)))
  361. return -EFAULT;
  362. ret = lcd_display_control(ctrl[0], ctrl[1], ctrl[2]);
  363. break;
  364. }
  365. default:
  366. ret = -EINVAL;
  367. }
  368. return ret;
  369. }
  370. static const struct file_operations lcd_fops = {
  371. .owner = THIS_MODULE,
  372. .open = lcd_open,
  373. .release = lcd_release,
  374. .write = lcd_write,
  375. .unlocked_ioctl = lcd_ioctl,
  376. .compat_ioctl = lcd_ioctl,
  377. };
  378. static struct miscdevice lcd_miscdev = {
  379. .minor = MISC_DYNAMIC_MINOR,
  380. .name = "lcd2002",
  381. .fops = &lcd_fops,
  382. .mode = 0666,
  383. };
  384. int lcd2x20_init(void)
  385. {
  386. int ret;
  387. pr_info("LCD2002 driver loading (PCI:%s, Bus:%d, Addr:0x%02X)...\n",
  388. pci_addr ? pci_addr : "none", i2c_bus, i2c_address);
  389. lcd_dev = kzalloc(sizeof(*lcd_dev), GFP_KERNEL);
  390. if (!lcd_dev)
  391. return -ENOMEM;
  392. mutex_init(&lcd_dev->lock);
  393. lcd_dev->addr = i2c_address;
  394. /* 查找适配器 */
  395. lcd_dev->adapter = find_adapter();
  396. if (!lcd_dev->adapter) {
  397. pr_err("Failed to find any suitable I2C adapter!\n");
  398. pr_err("Available adapters (from i2cdetect -l):\n");
  399. {
  400. int nr = 0;
  401. struct i2c_adapter *a;
  402. while ((a = i2c_get_adapter(nr++))) {
  403. pr_err(" i2c-%d: %s\n", nr-1, a->name);
  404. i2c_put_adapter(a);
  405. }
  406. }
  407. ret = -ENODEV;
  408. goto err_free;
  409. }
  410. pr_info("Using I2C adapter: %s\n", lcd_dev->adapter->name);
  411. /* 初始化LCD */
  412. ret = lcd_init_display();
  413. if (ret < 0) {
  414. pr_err("LCD init failed: %d\n", ret);
  415. goto err_put;
  416. }
  417. ret = misc_register(&lcd_miscdev);
  418. if (ret < 0) {
  419. pr_err("Misc register failed: %d\n", ret);
  420. goto err_put;
  421. }
  422. pr_info("LCD2002 ready at /dev/%s\n", lcd_miscdev.name);
  423. /* 欢迎信息 */
  424. lcd_clear();
  425. lcd_write_string("LCD2002 Driver OK", 17);
  426. lcd_set_cursor(0, 1);
  427. lcd_write_string("Intel ADL-N97", 13);
  428. return 0;
  429. err_put:
  430. i2c_put_adapter(lcd_dev->adapter);
  431. err_free:
  432. kfree(lcd_dev);
  433. lcd_dev = NULL;
  434. return ret;
  435. }
  436. void lcd2x20_exit(void)
  437. {
  438. if (!lcd_dev) return;
  439. misc_deregister(&lcd_miscdev);
  440. lcd_send_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_OFF);
  441. if (lcd_dev->adapter)
  442. i2c_put_adapter(lcd_dev->adapter);
  443. kfree(lcd_dev);
  444. lcd_dev = NULL;
  445. pr_info("LCD2002 driver unloaded\n");
  446. }