#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/uaccess.h>
struct ioexp_dev {
struct i2c_client * client;
struct miscdevice ioexp_miscdevice;
char name[8];
};
static ssize_t ioexp_read_file(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
int ret, recv_size, size;
char buf[10] = {0,};
char recv_buf[10] ={0,};
struct ioexp_dev * ioexp;
ioexp = container_of(file->private_data,
struct ioexp_dev,
ioexp_miscdevice);
/*
ioexp->client를 사용하여 ioexp로 부터 data를 읽는다.
이를 위해 prob() 함수에서 "ioexp->client = client;" 코드가 적용
*/
/*
int i2c_master_recv(const struct i2c_client *client, char *buf, int count);
client: 데이터를 받을 I2C 슬레이브 장치를 나타내는 i2c_client 구조체의 포인터입니다.
buf: 수신한 데이터를 저장할 버퍼입니다.
count: 버퍼에 저장할 데이터의 바이트 수를 나타냅니다.
함수는 성공적으로 수신된 바이트 수를 반환하거나, 에러가 발생하면 음수 errno 코드를 반환
*/
ret = i2c_master_recv(ioexp->client,recv_buf, 1 );
if (ret < 0)
return -EFAULT;
/*만약 cat /proc/ioexp 할 경우 ppos를 사용하여 처음 읽었을 때만 user 영역에 데이타 전달*/
if(*ppos == 0)
{
pr_info("read data from i2c device : [%x]\n",recv_buf[0] );
/*
읽은 값은 char 정수 값이다
하지만 user 에 data를 전달하기 위해서 문자로 변환한다
*/
size = sprintf(buf, "%02x", recv_buf[0]);
buf[size] = '\n';
if(copy_to_user(userbuf, buf, size+1)){
pr_info("Failed to send data to user space\n");
return -EFAULT;
}
*ppos+=(size+1);
return size+1;
}
/*만약 cat /proc/ioexp 할 경우 2번째 읽을때 종료하기 위해 0을 return 한다.*/
return 0;
}
static ssize_t ioexp_write_file(struct file *file, const char __user *userbuf,
size_t count, loff_t *ppos)
{
int ret;
unsigned long val;
char buf[4] ={0,};
char send_buf[2] = {0,};
struct ioexp_dev * ioexp;
ioexp = container_of(file->private_data,
struct ioexp_dev,
ioexp_miscdevice);
dev_info(&ioexp->client->dev,
"ioexp_write_file entered on %s\n", ioexp->name);
dev_info(&ioexp->client->dev,
"we have written %zu characters\n", count);
if(copy_from_user(buf, userbuf, count)) {
dev_err(&ioexp->client->dev, "Bad copied value\n");
return -EFAULT;
}
buf[count-1] = '\0';
/*
int kstrtoul(const char *s(from), unsigned int base, unsigned long *res(to));
문자열을 숫자로 변환
- s는 변환할 문자열입니다.
- base는 문자열의 기수입니다. (예를 들어, 10은 십진수, 16은 16진수 등)
- res는 결과 unsigned long 값을 저장할 변수의 포인터
kstrtoul 함수는 성공 시 0을 반환하며, 에러 발생 시 에러 코드를 반환합니다.
변환된 unsigned long 값은 res가 가리키는 변수에 저장
*/
ret = kstrtoul(buf, 0, &val);
if (ret)
return -EINVAL;
dev_info(&ioexp->client->dev, "the value is %x\n", val);
send_buf[0] = val;
/*
int i2c_master_send(const struct i2c_client *client, const char *buf, int count);
client: 데이터를 보낼 I2C 슬레이브 장치를 나타내는 i2c_client 구조체의 포인터입니다.
buf: 전송할 데이터를 가지고 있는 버퍼입니다.
count: 버퍼에 있는 데이터의 바이트 수를 나타냅니다.
*/
ret = i2c_master_send(ioexp->client,send_buf,1);
if (ret < 0)
dev_err(&ioexp->client->dev, "failed to write data\n");
dev_info(&ioexp->client->dev,
"ioexp_write_file exited on %s\n", ioexp->name);
return count;
}
static const struct file_operations ioexp_fops = {
.owner = THIS_MODULE,
.read = ioexp_read_file,
.write = ioexp_write_file,
};
static int ioexp_probe(struct i2c_client * client,
const struct i2c_device_id * id)
{
static int counter = 0;
int ret =0;
struct ioexp_dev * ioexp;
/*void *devm_kzalloc(struct device *dev, size_t size, gfp_t flags)*/
ioexp = devm_kzalloc(&client->dev, sizeof(struct ioexp_dev), GFP_KERNEL);
/*
i2c_set_clientdata(to, from) 함수는 from data를 to(i2c_client)->dev.driver_data 에 저장한다
dev_set_drvdata() 함수와 동일
*/
i2c_set_clientdata(client,ioexp);
/*
struct ioexp_dev에서도 이 I2C 클라이언트를 참조
struct ioexp_dev와 관련된 코드 어디에서든 I2C 클라이언트에 대한 액세스를 쉽게 접근 가능
*/
ioexp->client = client;
sprintf(ioexp->name, "ioexp%02d", counter++);
dev_info(&client->dev,
"ioexp_probe is entered on %s\n", ioexp->name);
ioexp->ioexp_miscdevice.name = ioexp->name;
ioexp->ioexp_miscdevice.minor = MISC_DYNAMIC_MINOR;
ioexp->ioexp_miscdevice.fops = &ioexp_fops;
ret = misc_register(&ioexp->ioexp_miscdevice);
if(ret==0)
dev_info(&client->dev,
"ioexp_probe is exited on %s\n", ioexp->name);
else
dev_info(&client->dev,
"ioexp_probe is error on %s\n", ioexp->name);
return 0;
}
static int ioexp_remove(struct i2c_client * client)
{
struct ioexp_dev * ioexp;
/* dev_get_drvdata() 함수와 동일 */
ioexp = i2c_get_clientdata(client);
dev_info(&client->dev,
"ioexp_remove is entered on %s\n", ioexp->name);
misc_deregister(&ioexp->ioexp_miscdevice);
dev_info(&client->dev,
"ioexp_remove is exited on %s\n", ioexp->name);
return 0;
}
static const struct of_device_id ioexp_dt_ids[] = {
{ .compatible = "arrow,ioexp", },
{ }
};
MODULE_DEVICE_TABLE(of, ioexp_dt_ids);
static const struct i2c_device_id i2c_ids[] = {
{ .name = "ioexp_ids_name", },
{ }
};
/* i2c device 이므로 i2c로 작성*/
MODULE_DEVICE_TABLE(i2c, i2c_ids);
static struct i2c_driver ioexp_driver = {
.driver = {
.name = "ioexp_driver_name",
.owner = THIS_MODULE,
.of_match_table = ioexp_dt_ids,
},
.probe = ioexp_probe,
.remove = ioexp_remove,
.id_table = i2c_ids,
};
/* insmod 시 i2c를 바로 등록 */
module_i2c_driver(ioexp_driver);
MODULE_LICENSE("GPL");
device tree
&i2c1 {
pinctrl-names = "default";
pinctrl-0 = <&i2c1_pins>;
clock-frequency = <100000>;
status = "okay";
ioexp@26 {
compatible = "arrow,ioexp";
reg = <0x26>;
};
예제에서 사용한 i2d device는 PCF8574 이다
output 단자는 8개이다
data 8bit 로 output 출력이 결정된다
ex)
0b10010001 -> output port : 1,5,8
i2cdetect -y 명령어는 사용하면 현재 i2c bus의 상태를 확인 할 수 있다
board@raspberrypi:~ $ i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
i2c device가 연결되어 있다면 아래처럼 숫자로 표시가 된다.
root@raspberrypi:/home/board# i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- 26 -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
insmod 1_io_expander.ko
insmod를 하면 module에서 사용하는 addres에 UU(Userland Usage)로 표시된다.
root@raspberrypi:/home/board# i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- UU -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
insmod 1_io_expander.ko
[ 188.700887] ioexp_driver_name 1-0026: ioexp_probe is entered on ioexp00
[ 188.704149] ioexp_driver_name 1-0026: ioexp_probe is exited on ioexp00
echo 200 > /dev/ioexp00
[ 230.281363] ioexp_driver_name 1-0026: ioexp_write_file entered on ioexp00
[ 230.281386] ioexp_driver_name 1-0026: we have written 4 characters
[ 230.281404] ioexp_driver_name 1-0026: the value is c8
[ 230.281673] ioexp_driver_name 1-0026: ioexp_write_file exited on ioexp00
cat /dev/ioexp00
c8
root@raspberrypi:/home/board# ls -l /dev/ioexp00
crw------- 1 root root 10, 60 May 27 19:44 /dev/ioexp00
rmmod 1_io_expander.ko
[ 391.122952] ioexp_driver_name 1-0026: ioexp_remove is entered on ioexp00
[ 391.126103] ioexp_driver_name 1-0026: ioexp_remove is exited on ioexp00
root@raspberrypi:/# find ./ -name *ioexp_driver_name*
./sys/bus/i2c/drivers/ioexp_driver_name
./sys/module/1_io_expander/drivers/i2c:ioexp_driver_name
root@raspberrypi:/# find ./ -name *ioexp*
./sys/devices/virtual/misc/ioexp00
./sys/class/misc/ioexp00
./sys/firmware/devicetree/base/soc/i2c@7e804000/ioexp@26
./sys/bus/i2c/drivers/ioexp_driver_name
./sys/module/1_io_expander/drivers/i2c:ioexp_driver_name
./dev/ioexp00
'device driver' 카테고리의 다른 글
Waiting queues (0) | 2023.06.01 |
---|---|
charter device driver - 8. interrupt platform driver 만들기 (0) | 2023.05.30 |
charter device driver - 6. led_class_platform 만들기 (0) | 2023.05.26 |
charter device driver - 5. platform_driver로 led control 해보자 (0) | 2023.05.22 |
charter device driver - 4. platform driver module 만들기 (0) | 2023.05.21 |
댓글