如何在内核线程中获取 struct i2c_client *client 结构

How to get struct i2c_client *client structure inside kernel thread

我现在正在开发一个内核模块,通过 RaspberryPi4 和 RaspberryPiOS 上的 I2C 设备处理两位数的 7 段 LED 指示灯。

本模块使用内核线程处理七段LED显示,改变数字位置。

向i2c设备发送命令需要i2c_client*结构,可以通过probe函数的形式参数获取。

内核线程没有i2c_client结构的形式参数。

我的解决方法是把一个i2c_client*结构的指针存入一个全局变量中,在内核线程中使用这个指针,用这个指针调用i2c函数。

我的内核模块运行良好,至少现在...

在内核线程中使用i2c函数,你有更好的解决方案吗?或者存在更好的解决方案?

我的全部代码如下...

//i2c_7seg2_udev.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>
#include <linux/workqueue.h>
#include <linux/delay.h> 
#include <linux/kthread.h>

MODULE_LICENSE("GPL v2");

#define DEVICE_NAME "i2c_7seg2_udev"

#define MINOR_BASE 0
#define MINOR_NUM  1

static unsigned int major_num;

struct cdev i2c_7seg2_udev_cdev;

void init_i2c_gpio(void);
void turn_off_7seg(void);
void turn_off_7seg(void);
void set_7seg(unsigned int number);
void change_7seg_keta(void);



#define LOOP_SLEEP_US (5000)

static struct task_struct *kthread;
static int dynamic_7seg_kthread(void *data);
static void init_kthread(void)
;

static int dynamic_7seg_kthread(void *data)
{
    while(!kthread_should_stop()){

        change_7seg_keta();

        usleep_range(LOOP_SLEEP_US, LOOP_SLEEP_US * 2);
    }
    
    return 0;
}

static void init_kthread(void)
{
    kthread = kthread_create(dynamic_7seg_kthread, NULL, "dynamic_7seg_kthread");

    wake_up_process(kthread);
}


static int init_regist_device(void);

static struct i2c_client *i2c_client_data;

#define DRIVER_NAME "i2c_7seg2_udev"   

static struct i2c_device_id i2c_7seg2_udev_device_idtable[] = {
    {"I2C_7SEG2_UDEV", 0},
    { }
};

MODULE_DEVICE_TABLE(i2c, i2c_7seg2_udev_device_idtable);

static int i2c_7seg2_udev_device_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    if(init_regist_device() < 0){
        printk(KERN_DEBUG "i2c_7seg2_udev: initialize failed.\n");
        return -1;
    }



    printk(KERN_DEBUG "i2c_7seg2_udev_device connected.\n");
    printk(KERN_DEBUG "id.name = %s, id.driver_data = %ld", id->name, id->driver_data);
    printk(KERN_DEBUG "device address is: 0x%02X\n", client->addr);
    
    i2c_client_data = client;
    
    init_i2c_gpio();
    
    set_7seg(12);
    
    init_kthread();
    return 0;
}


static int i2c_7seg2_udev_device_remove(struct i2c_client *client)
{
    dev_t dev;

    turn_off_7seg();


    printk(KERN_DEBUG "i2c_7seg2_udev: wait for thread to be terminated.\n");
    kthread_stop(kthread);
    printk(KERN_DEBUG "i2c_7seg2_udev: thread terminated.\n");
    
    dev = MKDEV(major_num, MINOR_BASE);

    cdev_del(&i2c_7seg2_udev_cdev);

    unregister_chrdev_region(dev, MINOR_NUM);
    

    printk(KERN_DEBUG "i2c_7seg2_udev_device disconnected.\n");
    return 0;
}

static struct i2c_driver i2c_7seg2_udev_device_driver = {
    .driver = {
        .name  = DRIVER_NAME,
        .owner = THIS_MODULE,
    },
    .id_table  = i2c_7seg2_udev_device_idtable,  
    .probe     = i2c_7seg2_udev_device_probe,    
    .remove    = i2c_7seg2_udev_device_remove,   
};


#define MAX_7SEG_KETA (2)
//NOTE: "KETA" means digit position, in Japanese.

static const unsigned int seg7_pattern[] =
//--gfedcba
{0b00111111, //0 
 0b00000110, //1 
 0b01011011, //2 
 0b01001111, //3 
 0b01100110, //4 
 0b01101101, //5 
 0b01111100, //6 
 0b00100111, //7 
 0b01111111, //8 
 0b01100111  //9 
};


unsigned char value_7seg[MAX_7SEG_KETA];


DEFINE_MUTEX(__mutex_value_7seg);


void set_7seg(unsigned int number){
    unsigned int i;
    printk(KERN_DEBUG "i2c_7seg2_udev: value %d .\n", number);
    for( i = 0; i < MAX_7SEG_KETA; i++){
        value_7seg[MAX_7SEG_KETA - i - 1] = (number % 10) + '0';
        number = number / 10;
    }
    


}


void change_7seg_keta(void)
{
   static unsigned int keta = 0;
   unsigned char keta_shift;
   unsigned int number;

   keta_shift = 0x01 << keta;


   i2c_smbus_write_byte_data(i2c_client_data, 0x03, ~keta_shift); //P1に出力

   number = value_7seg[MAX_7SEG_KETA - keta - 1] - '0';

   
   i2c_smbus_write_byte_data(i2c_client_data, 0x02, ~seg7_pattern[number]); //P0に出力
   
   keta ++;
   if( keta >= MAX_7SEG_KETA ){
         keta = 0;
    }
}

void turn_off_7seg(void){
    i2c_smbus_write_byte_data(i2c_client_data, 0x02, 0x7F); //P0に出力
}

void init_i2c_gpio(void){
    i2c_smbus_write_byte_data(i2c_client_data, 0x06, 0x00);
    i2c_smbus_write_byte_data(i2c_client_data, 0x07, 0x00);
    
}

static int i2c_7seg2_udev_open(struct inode *inode, struct file *file)
{
    printk(KERN_DEBUG "i2c_7seg2_udev: opened.\n");
    return 0;
}

static int i2c_7seg2_udev_close(struct inode *inode, struct file *file)
{
    printk(KERN_DEBUG "i2c_7seg2_udev: closed.\n");
    return 0;
}


static ssize_t i2c_7seg2_udev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    unsigned char read_datas[MAX_7SEG_KETA + 2];
    unsigned long bytes_left;
    const unsigned long bytes_to_send = MAX_7SEG_KETA + 2;
    unsigned int i;


    for(i = 0; i < MAX_7SEG_KETA; i++  ){
        read_datas[i] = value_7seg[i];
    }
    
    read_datas[i]     = '\n';
    read_datas[i + 1] = '[=11=]';


    bytes_left = copy_to_user(buf, read_datas, bytes_to_send);

    return (bytes_to_send - bytes_left);

}


static ssize_t i2c_7seg2_udev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    unsigned char write_values[MAX_7SEG_KETA + 2];
    unsigned long bytes_left;
    const unsigned long bytes_to_send = MAX_7SEG_KETA + 2;
    int i;

    printk(KERN_DEBUG "i2c_7seg2_udev: write.");
    printk(KERN_DEBUG "i2c_7seg2_udev: write.");
    
    bytes_left = copy_from_user(write_values, buf, bytes_to_send);

    mutex_lock(&__mutex_value_7seg);
    for(i = 0; i < MAX_7SEG_KETA; i++){
        if( (write_values[i] >= '0') && (write_values[i] <= '9') ){
            value_7seg[i] = write_values[i];
        }
    }

    mutex_unlock(&__mutex_value_7seg);
    
    return (bytes_to_send - bytes_left);
}

struct file_operations s_i2c_7seg2_udev_fops = {
    .open    = i2c_7seg2_udev_open,
    .release = i2c_7seg2_udev_close,
    .read    = i2c_7seg2_udev_read,
    .write   = i2c_7seg2_udev_write,
};


int init_regist_device(void)
{
    int device_num = 0;
    int devnum_err = 0;
    int cdev_err = 0;
    dev_t dev;

    devnum_err = alloc_chrdev_region(&dev, MINOR_BASE, MINOR_NUM, DEVICE_NAME);
    if (devnum_err != 0) {
        printk(KERN_ERR  "devnum_err = %d\n", devnum_err);
        return -1;
    }
    
    if (device_num != 0) {
        printk(KERN_ERR  "i2c_7seg2_udev: error_init_regist_device , %d\n", device_num);
        return -1;
    }

    major_num = MAJOR(dev);

    cdev_init(&i2c_7seg2_udev_cdev, &s_i2c_7seg2_udev_fops);
    i2c_7seg2_udev_cdev.owner = THIS_MODULE;

    cdev_err = cdev_add(&i2c_7seg2_udev_cdev, dev, MINOR_NUM);
    if (cdev_err != 0) {

        printk(KERN_ERR  "cdev_add = %d\n", cdev_err);
        unregister_chrdev_region(dev, MINOR_NUM);
        return -1;
    }
    
    printk(KERN_DEBUG "i2c_7seg2_udev: device registerd.\n");
    
    return 0;
}

static int i2c_7seg2_udev_device_init(void)
{
    printk(KERN_DEBUG "i2c_7seg2_udev device driver loaded.\n");

    i2c_add_driver(&i2c_7seg2_udev_device_driver);
    

    return 0;
}

static void i2c_7seg2_udev_device_exit(void)
{
    printk(KERN_DEBUG "i2c_7seg2_udev device driver unloading.\n");
    i2c_del_driver(&i2c_7seg2_udev_device_driver);

}


module_init(i2c_7seg2_udev_device_init);
module_exit(i2c_7seg2_udev_device_exit);


函数 kthread_create() 的第二个参数是 void *data。 现在您正在传递 NULL,您可以将指针传递给 struct i2c_client。 然后你可以这样做:

static int dynamic_7seg_kthread(void *data) {
    struct i2c_client *client = data;

    while (!kthread_should_stop()) {
        change_7seg_keta(client);

        usleep_range(LOOP_SLEEP_US, LOOP_SLEEP_US * 2);
    }

    return 0;
}

从而从代码中消除全局变量。