如何在内核线程中获取 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;
}
从而从代码中消除全局变量。
我现在正在开发一个内核模块,通过 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;
}
从而从代码中消除全局变量。