#include #include #include #include #include #include #include #include #include #include #include #define DRIVER_NAME "lcd2002" #define LCD_I2C_ADDR_DEFAULT 0x3E /* 控制字节定义 */ #define LCD_CONTROL_BYTE 0x00 #define LCD_DATA_BYTE 0x40 /* 基本指令 */ #define LCD_CLEAR_DISPLAY 0x01 #define LCD_RETURN_HOME 0x02 #define LCD_ENTRY_MODE_SET 0x04 #define LCD_DISPLAY_CONTROL 0x08 #define LCD_CURSOR_SHIFT 0x10 #define LCD_FUNCTION_SET 0x20 #define LCD_SET_CGRAM_ADDR 0x40 #define LCD_SET_DDRAM_ADDR 0x80 /* Entry Mode */ #define LCD_ENTRY_RIGHT 0x00 #define LCD_ENTRY_LEFT 0x02 /* Display Control */ #define LCD_DISPLAY_ON 0x04 #define LCD_DISPLAY_OFF 0x00 #define LCD_CURSOR_ON 0x02 #define LCD_CURSOR_OFF 0x00 #define LCD_BLINK_ON 0x01 #define LCD_BLINK_OFF 0x00 #define LCD_LINE1_ADDR 0x00 #define LCD_LINE2_ADDR 0x40 #define LCD_COLS 20 #define LCD_ROWS 2 /* IOCTL */ #define LCD_IOCTL_MAGIC 'L' #define LCD_IOCTL_CLEAR _IO(LCD_IOCTL_MAGIC, 0) #define LCD_IOCTL_HOME _IO(LCD_IOCTL_MAGIC, 1) #define LCD_IOCTL_SET_CURSOR _IOW(LCD_IOCTL_MAGIC, 2, struct lcd_pos) #define LCD_IOCTL_DISPLAY_CTRL _IOW(LCD_IOCTL_MAGIC, 3, unsigned char[3]) struct lcd_pos { unsigned char col; unsigned char row; }; struct lcd2002_dev { struct i2c_adapter *adapter; struct mutex lock; unsigned char addr; }; static struct lcd2002_dev *lcd_dev = NULL; /* 模块参数 - 多种匹配方式 */ static char *pci_addr = "0000:00:15.0"; module_param(pci_addr, charp, 0444); MODULE_PARM_DESC(pci_addr, "PCI address (e.g., 0000:00:15.0)"); static char *adapter_name = NULL; /* 如: "Synopsys DesignWare I2C adapter" */ module_param(adapter_name, charp, 0444); MODULE_PARM_DESC(adapter_name, "I2C adapter name (e.g., Synopsys DesignWare)"); static int i2c_bus = -1; /* 直接指定总线号,如 1 对应 /dev/i2c-1 */ module_param(i2c_bus, int, 0444); MODULE_PARM_DESC(i2c_bus, "I2C bus number (e.g., 1 for /dev/i2c-1)"); static unsigned char i2c_address = LCD_I2C_ADDR_DEFAULT; module_param(i2c_address, byte, 0444); MODULE_PARM_DESC(i2c_address, "LCD I2C address (default: 0x3E)"); static int debug = 1; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0=none, 1=normal, 2=verbose)"); /* 延时 */ static inline void lcd_delay_ms(unsigned int ms) { msleep(ms); } /* I2C写 */ static int lcd_i2c_write(unsigned char *buf, int len) { struct i2c_msg msg; int ret; if (!lcd_dev || !lcd_dev->adapter) return -ENODEV; msg.addr = lcd_dev->addr; msg.flags = 0; msg.len = len; msg.buf = buf; ret = i2c_transfer(lcd_dev->adapter, &msg, 1); if (ret != 1) { dev_err(&lcd_dev->adapter->dev, "I2C write failed: %d\n", ret); return ret < 0 ? ret : -EIO; } return 0; } /* 发送指令 */ static int lcd_send_command(unsigned char cmd) { unsigned char buf[2]; int ret; buf[0] = LCD_CONTROL_BYTE; buf[1] = cmd; mutex_lock(&lcd_dev->lock); ret = lcd_i2c_write(buf, 2); mutex_unlock(&lcd_dev->lock); if (ret == 0) lcd_delay_ms(2); return ret; } /* 发送数据 */ static int lcd_send_data(unsigned char data) { unsigned char buf[2]; int ret; buf[0] = LCD_DATA_BYTE; buf[1] = data; mutex_lock(&lcd_dev->lock); ret = lcd_i2c_write(buf, 2); mutex_unlock(&lcd_dev->lock); if (ret == 0) lcd_delay_ms(1); return ret; } /* 初始化LCD */ static int lcd_init_display(void) { int ret; lcd_delay_ms(50); /* 功能设置: 2行, 5x8点阵 (0x28) */ ret = lcd_send_command(LCD_FUNCTION_SET | 0x08); if (ret < 0) return ret; lcd_delay_ms(5); /* 显示开,光标关,闪烁关 (0x0C) */ ret = lcd_send_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON | LCD_CURSOR_OFF | LCD_BLINK_OFF); if (ret < 0) return ret; /* 清屏 */ ret = lcd_send_command(LCD_CLEAR_DISPLAY); if (ret < 0) return ret; lcd_delay_ms(3); /* 输入模式: 光标右移 (0x06) */ ret = lcd_send_command(LCD_ENTRY_MODE_SET | LCD_ENTRY_RIGHT); return ret; } /* 设置光标 */ static int lcd_set_cursor(unsigned char col, unsigned char row) { unsigned char addr; if (col >= LCD_COLS || row >= LCD_ROWS) return -EINVAL; addr = (row == 0) ? (LCD_LINE1_ADDR + col) : (LCD_LINE2_ADDR + col); return lcd_send_command(LCD_SET_DDRAM_ADDR | addr); } /* 清屏 */ static int lcd_clear(void) { int ret = lcd_send_command(LCD_CLEAR_DISPLAY); if (ret == 0) lcd_delay_ms(2); return ret; } /* 显示控制 */ static int lcd_display_control(unsigned char display, unsigned char cursor, unsigned char blink) { return lcd_send_command(LCD_DISPLAY_CONTROL | display | cursor | blink); } /* 写字符串 */ static int lcd_write_string(const char *str, size_t len) { size_t i; int ret = 0; unsigned char row = 0, col = 0; for (i = 0; i < len; i++) { char c = str[i]; if (c == '\n') { row = (row + 1) % LCD_ROWS; col = 0; ret = lcd_set_cursor(col, row); } else if (c == '\r') { col = 0; ret = lcd_set_cursor(col, row); } else if (c == '\f') { ret = lcd_clear(); row = col = 0; } else { if (col >= LCD_COLS) { row = (row + 1) % LCD_ROWS; col = 0; ret = lcd_set_cursor(col, row); if (ret < 0) break; } ret = lcd_send_data(c); col++; } if (ret < 0) break; } return ret; } /* 调试:打印适配器信息 */ static void print_adapter_info(struct i2c_adapter *adap, int nr) { struct device *parent; char pci_str[64] = "N/A"; if (!debug) return; pr_info(" [%d] %s (class: 0x%lx)\n", nr, adap->name, adap->class); parent = adap->dev.parent; while (parent) { if (parent->bus == &pci_bus_type) { struct pci_dev *pdev = to_pci_dev(parent); snprintf(pci_str, sizeof(pci_str), "%04x:%02x:%02x.%x", pci_domain_nr(pdev->bus), pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); pr_info(" -> PCI: %s [%s]\n", pci_str, dev_name(parent)); break; } else { pr_info(" -> %s [%s]\n", dev_name(parent), parent->bus ? parent->bus->name : "no-bus"); } parent = parent->parent; } } /* 方式1: 通过PCI地址查找 */ static struct i2c_adapter *find_by_pci(const char *target_pci) { struct i2c_adapter *adapter; int nr = 0; bool found = false; pr_info("Searching for PCI %s...\n", target_pci); while ((adapter = i2c_get_adapter(nr))) { struct device *parent = adapter->dev.parent; char pci_buf[32] = ""; if (debug >= 2) print_adapter_info(adapter, nr); /* 递归向上查找PCI设备 */ while (parent && !found) { if (parent->bus == &pci_bus_type) { struct pci_dev *pdev = to_pci_dev(parent); snprintf(pci_buf, sizeof(pci_buf), "%04x:%02x:%02x.%x", pci_domain_nr(pdev->bus), pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); if (strcmp(pci_buf, target_pci) == 0) { pr_info("Found match at i2c-%d: %s\n", nr, adapter->name); found = true; break; } break; /* 只检查第一级PCI父设备 */ } parent = parent->parent; } if (found) return adapter; i2c_put_adapter(adapter); nr++; } return NULL; } /* 方式2: 通过名称查找 */ static struct i2c_adapter *find_by_name(const char *name) { struct i2c_adapter *adapter; int nr = 0; pr_info("Searching for adapter name '%s'...\n", name); while ((adapter = i2c_get_adapter(nr))) { if (debug >= 2) print_adapter_info(adapter, nr); if (strstr(adapter->name, name)) { pr_info("Found match at i2c-%d: %s\n", nr, adapter->name); return adapter; } i2c_put_adapter(adapter); nr++; } return NULL; } /* 方式3: 通过总线号 */ static struct i2c_adapter *find_by_bus(int bus) { pr_info("Trying i2c bus %d...\n", bus); return i2c_get_adapter(bus); } /* 查找适配器主函数 */ static struct i2c_adapter *find_adapter(void) { struct i2c_adapter *adapter = NULL; /* 优先级1: 直接指定总线号 */ if (i2c_bus >= 0) { adapter = find_by_bus(i2c_bus); if (adapter) goto done; pr_warn("Specified i2c_bus %d not found\n", i2c_bus); } /* 优先级2: PCI地址 */ if (pci_addr && strlen(pci_addr) > 0) { adapter = find_by_pci(pci_addr); if (adapter) goto done; pr_warn("PCI address %s not found\n", pci_addr); } /* 优先级3: 适配器名称 */ if (adapter_name && strlen(adapter_name) > 0) { adapter = find_by_name(adapter_name); if (adapter) goto done; pr_warn("Adapter name '%s' not found\n", adapter_name); } /* 回退: 自动检测常见名称 (Synopsys DesignWare) */ pr_info("Trying auto-detect (Synopsys DesignWare)...\n"); adapter = find_by_name("Synopsys DesignWare"); if (adapter) goto done; /* 最后回退: 使用 i2c-1 (根据用户环境) */ pr_info("Trying fallback i2c-1...\n"); adapter = find_by_bus(1); done: return adapter; } /* 文件操作 */ static int lcd_open(struct inode *inode, struct file *file) { return lcd_dev ? 0 : -ENODEV; } static int lcd_release(struct inode *inode, struct file *file) { return 0; } static ssize_t lcd_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char *kbuf; int ret; if (count == 0) return 0; if (count > 256) count = 256; kbuf = kmalloc(count + 1, GFP_KERNEL); if (!kbuf) return -ENOMEM; if (copy_from_user(kbuf, buf, count)) { kfree(kbuf); return -EFAULT; } kbuf[count] = '\0'; ret = lcd_write_string(kbuf, count); kfree(kbuf); return ret < 0 ? ret : count; } static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; switch (cmd) { case LCD_IOCTL_CLEAR: ret = lcd_clear(); break; case LCD_IOCTL_HOME: ret = lcd_send_command(LCD_RETURN_HOME); if (ret == 0) lcd_delay_ms(2); break; case LCD_IOCTL_SET_CURSOR: { struct lcd_pos pos; if (copy_from_user(&pos, (void __user *)arg, sizeof(pos))) return -EFAULT; ret = lcd_set_cursor(pos.col, pos.row); break; } case LCD_IOCTL_DISPLAY_CTRL: { unsigned char ctrl[3]; if (copy_from_user(ctrl, (void __user *)arg, sizeof(ctrl))) return -EFAULT; ret = lcd_display_control(ctrl[0], ctrl[1], ctrl[2]); break; } default: ret = -EINVAL; } return ret; } static const struct file_operations lcd_fops = { .owner = THIS_MODULE, .open = lcd_open, .release = lcd_release, .write = lcd_write, .unlocked_ioctl = lcd_ioctl, .compat_ioctl = lcd_ioctl, }; static struct miscdevice lcd_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = "lcd2002", .fops = &lcd_fops, .mode = 0666, }; int lcd2x20_init(void) { int ret; pr_info("LCD2002 driver loading (PCI:%s, Bus:%d, Addr:0x%02X)...\n", pci_addr ? pci_addr : "none", i2c_bus, i2c_address); lcd_dev = kzalloc(sizeof(*lcd_dev), GFP_KERNEL); if (!lcd_dev) return -ENOMEM; mutex_init(&lcd_dev->lock); lcd_dev->addr = i2c_address; /* 查找适配器 */ lcd_dev->adapter = find_adapter(); if (!lcd_dev->adapter) { pr_err("Failed to find any suitable I2C adapter!\n"); pr_err("Available adapters (from i2cdetect -l):\n"); { int nr = 0; struct i2c_adapter *a; while ((a = i2c_get_adapter(nr++))) { pr_err(" i2c-%d: %s\n", nr-1, a->name); i2c_put_adapter(a); } } ret = -ENODEV; goto err_free; } pr_info("Using I2C adapter: %s\n", lcd_dev->adapter->name); /* 初始化LCD */ ret = lcd_init_display(); if (ret < 0) { pr_err("LCD init failed: %d\n", ret); goto err_put; } ret = misc_register(&lcd_miscdev); if (ret < 0) { pr_err("Misc register failed: %d\n", ret); goto err_put; } pr_info("LCD2002 ready at /dev/%s\n", lcd_miscdev.name); /* 欢迎信息 */ lcd_clear(); lcd_write_string("LCD2002 Driver OK", 17); lcd_set_cursor(0, 1); lcd_write_string("Intel ADL-N97", 13); return 0; err_put: i2c_put_adapter(lcd_dev->adapter); err_free: kfree(lcd_dev); lcd_dev = NULL; return ret; } void lcd2x20_exit(void) { if (!lcd_dev) return; misc_deregister(&lcd_miscdev); lcd_send_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_OFF); if (lcd_dev->adapter) i2c_put_adapter(lcd_dev->adapter); kfree(lcd_dev); lcd_dev = NULL; pr_info("LCD2002 driver unloaded\n"); }