This commit is contained in:
Loic Guegan 2023-07-12 16:07:20 +02:00
parent f0869aa5ba
commit 9acb2d7635
4 changed files with 476 additions and 86 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# Ignore all
*
# Include the following
!Makefile
!.gitignore
!ina260.c
!README.md

24
Makefile Normal file
View file

@ -0,0 +1,24 @@
# Linux Makefile Location:
#LML="/usr/src/linux-headers-$(shell uname -r)/"
LML="/lib/modules/$(shell uname -r)/build/" # Change if required on your system
obj-m += ina260.o
all: ina260.c
make -C $(LML) M=$(PWD) modules
run: ina260.c
-echo 0x40 > /sys/bus/i2c/devices/i2c-2/delete_device
-rmmod ina260
make clean
make
insmod ina260.ko
echo ina260 0x40 > /sys/bus/i2c/devices/i2c-2/new_device
read: read.c
gcc $^ -o read
clean:
rm -f ina260*.o ina260.ko ina260.mod* Module.symvers modules.order .ina260* .Module* .modules*
.PHONY: clean run

View file

@ -1,92 +1,12 @@
# ina260-sysfs-driver
## Getting started
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
## Add your files
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
```
cd existing_repo
git remote add origin https://gitlab.com/manzerbredes/ina260-sysfs-driver.git
git branch -M main
git push -uf origin main
```
## Integrate with your tools
- [ ] [Set up project integrations](https://gitlab.com/manzerbredes/ina260-sysfs-driver/-/settings/integrations)
## Collaborate with your team
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
## Test and Deploy
Use the built-in continuous integration in GitLab.
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
***
# Editing this README
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
## Suggestions for a good README
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
## Name
Choose a self-explaining name for your project.
## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
# INA260-sysfs-driver
## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
$ make
$ sudo insmod ina260.ko
## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
- Add device: `echo ina260 <devaddr> > /sys/bus/i2c/devices/<busid>/new_device`
## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.
## Contributing
State if you are open to contributions and what your requirements are for accepting them.
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.
## License
For open source projects, say how it is licensed.
## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
- Next, run: `tree /sys/kernel/ina260`

438
ina260.c Normal file
View file

@ -0,0 +1,438 @@
#include "linux/module.h" // to access basic module functions (module_init etc..)
#include "linux/uaccess.h" // copy_from_user() etc...
#include "linux/i2c.h"
#include "linux/kobject.h"
#include "linux/slab.h" // kzalloc()
#include "linux/kernel.h" // Kernel utility e.g kstrtol
#include <linux/sysfs.h>
#define INA260_REG_CONFIGURATION 0x00
#define INA260_REG_CURRENT 0x01
#define INA260_REG_VOLTAGE 0x02
#define INA260_REG_POWER 0x03
#define INA260_REG_MASKENABLE 0x06
#define INA260_REG_ALERTLIMIT 0x07
#define INA260_REG_MANUFACTURER 0xFE
#define INA260_REG_DIE 0xFF
#define INA260_IS_ATTR(_name) (strcmp(attr->attr.name, #_name) == 0)
static int ina260_avgs[8]={
1,4,16,64,128,256,512,1024
};
static char ina260_modes[8][42]={
"Power-Down (or Shutdown)","Shunt Current, Triggered",
"Bus Voltage, Triggered","Shunt Current and Bus Voltage, Triggered",
"Power-Down (or Shutdown)","Shunt Current, Continuous","Bus Voltage, Continuous",
"Shunt Current and Bus Voltage, Continuous"
};
static char ina260_ishcts_vbusct[8][9]={
"140 µs", "204 µs","332 µs", "588 µs","1.1 ms", "2.116 ms","4.156 ms","8.244 ms"
};
static struct kobject* ina260_kobj;
struct client_data {
struct kobject kobj;
struct i2c_client *client;
unsigned char reg; // Client selected register
};
static const struct i2c_device_id ina260_ids[] = {
{ "ina260", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c,ina260_ids); // Allow i2c subsystem to probe
static int ina260_read_register(struct client_data* cdata, unsigned char reg, int *value){
unsigned char bytes[2];
if(cdata->reg == reg){
//printk("Cache\n");
if(i2c_master_recv(cdata->client,bytes,2)<0){
return 1;
}
} else {
//printk("No cache\n");
if(i2c_master_send(cdata->client,&reg,1)<0){
return 1;
}
cdata->reg = reg;
if(i2c_master_recv(cdata->client,bytes,2)<0){
return 1;
}
}
*value=(bytes[0]<<8) | bytes[1];
return 0;
}
static int ina260_write_register(struct client_data* cdata, unsigned char reg, int value){
unsigned char data[3];
data[0]=reg;
data[1]=(value>>8) & 0xFF; // MSB
data[2]=value & 0xFF; // LSB
if(i2c_master_send(cdata->client,data,3)<0){
return 1;
}
return 0;
}
static ssize_t attr_show(struct kobject *_kobj,
struct kobj_attribute *attr,
char *buf)
{
int value;
struct client_data *data=container_of(_kobj,struct client_data,kobj);
unsigned char reg=INA260_REG_POWER;
if(!INA260_IS_ATTR(power)){
if (INA260_IS_ATTR(current)){
reg=INA260_REG_CURRENT;
} else if(INA260_IS_ATTR(bus_voltage)) {
reg=INA260_REG_VOLTAGE;
} else if(INA260_IS_ATTR(mask_enable)) {
reg=INA260_REG_MASKENABLE;
} else if(INA260_IS_ATTR(alert_limit)) {
reg=INA260_REG_ALERTLIMIT;
} else if(INA260_IS_ATTR(configuration)) {
reg=INA260_REG_CONFIGURATION;
} else if(INA260_IS_ATTR(manufacturer_id)) {
reg=INA260_REG_MANUFACTURER;
} else {
reg=INA260_REG_DIE;
}
}
if(ina260_read_register(data,reg,&value))
return -1;
return sprintf(buf, "%x\n", value);
}
static ssize_t attr_store(struct kobject *_kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct client_data *cdata=container_of(_kobj,struct client_data,kobj);
int data;
unsigned char reg=INA260_REG_CONFIGURATION;
if(kstrtoint(buf, 10,&data))
return -EINVAL;
if (INA260_IS_ATTR(mask_enable)){
reg=INA260_REG_MASKENABLE;
} else if (INA260_IS_ATTR(alert_limit)){
reg=INA260_REG_ALERTLIMIT;
}
if(ina260_write_register(cdata, reg, data))
return -1;
return count;
}
static ssize_t attr_field_show(struct kobject *_kobj,
struct kobj_attribute *attr,
char *buf)
{
struct client_data *cdata=container_of(_kobj,struct client_data,kobj);
int value;
if(INA260_IS_ATTR(avg)){
if(ina260_read_register(cdata, INA260_REG_CONFIGURATION,&value))
return -1;
return sprintf(buf, "%d\n", ina260_avgs[(value>>9)&0x7]);
} else if (INA260_IS_ATTR(mode)){
if(ina260_read_register(cdata, INA260_REG_CONFIGURATION,&value))
return -1;
return sprintf(buf, "%s\n", ina260_modes[value&0x7]);
} else if (INA260_IS_ATTR(ishct)){
if(ina260_read_register(cdata, INA260_REG_CONFIGURATION,&value))
return -1;
return sprintf(buf, "%s\n", ina260_ishcts_vbusct[(value>>3)&0x7]);
} else if (INA260_IS_ATTR(vbusct)){
if(ina260_read_register(cdata, INA260_REG_CONFIGURATION,&value))
return -1;
return sprintf(buf, "%s\n", ina260_ishcts_vbusct[(value>>6)&0x7]);
} else if (INA260_IS_ATTR(aff)){
if(ina260_read_register(cdata, INA260_REG_MASKENABLE,&value))
return -1;
return sprintf(buf, "%d\n", (value>>4)&0x1);
} else if (INA260_IS_ATTR(cvrf)){
if(ina260_read_register(cdata, INA260_REG_MASKENABLE,&value))
return -1;
return sprintf(buf, "%d\n", (value>>3)&0x1);
} else if (INA260_IS_ATTR(ocl)){
if(ina260_read_register(cdata, INA260_REG_MASKENABLE,&value))
return -1;
return sprintf(buf, "%d\n", (value>>15)&0x1);
}else if (INA260_IS_ATTR(ucl)){
if(ina260_read_register(cdata, INA260_REG_MASKENABLE,&value))
return -1;
return sprintf(buf, "%d\n", (value>>14)&0x1);
}else if (INA260_IS_ATTR(bol)){
if(ina260_read_register(cdata, INA260_REG_MASKENABLE,&value))
return -1;
return sprintf(buf, "%d\n", (value>>13)&0x1);
}else if (INA260_IS_ATTR(bul)){
if(ina260_read_register(cdata, INA260_REG_MASKENABLE,&value))
return -1;
return sprintf(buf, "%d\n", (value>>12)&0x1);
}else if (INA260_IS_ATTR(pol)){
if(ina260_read_register(cdata, INA260_REG_MASKENABLE,&value))
return -1;
return sprintf(buf, "%d\n", (value>>11)&0x1);
}else if (INA260_IS_ATTR(cnvr)){
if(ina260_read_register(cdata, INA260_REG_MASKENABLE,&value))
return -1;
return sprintf(buf, "%d\n", (value>>10)&0x1);
}else if (INA260_IS_ATTR(ovf)){
if(ina260_read_register(cdata, INA260_REG_MASKENABLE,&value))
return -1;
return sprintf(buf, "%d\n", (value>>2)&0x1);
}else if (INA260_IS_ATTR(apol)){
if(ina260_read_register(cdata, INA260_REG_MASKENABLE,&value))
return -1;
return sprintf(buf, "%d\n", (value>>1)&0x1);
}else if (INA260_IS_ATTR(len)){
if(ina260_read_register(cdata, INA260_REG_MASKENABLE,&value))
return -1;
return sprintf(buf, "%d\n", value&0x1);
}else if (INA260_IS_ATTR(did)){
if(ina260_read_register(cdata, INA260_REG_DIE,&value))
return -1;
return sprintf(buf, "0x%x\n", (value>>4));
}else if (INA260_IS_ATTR(rid)){
if(ina260_read_register(cdata, INA260_REG_DIE,&value))
return -1;
return sprintf(buf, "0x%x\n", value&0x7);
}else if (INA260_IS_ATTR(reset)){
return sprintf(buf, "0\n");
}
return 0;
}
static int ina260_set_register_3bits(struct client_data *cdata, unsigned char reg, unsigned char n, int value3bits){
int mask, value;
if(value3bits>=0 && value3bits <8){
if(ina260_read_register(cdata,reg,&value)){
return 1;
}
mask=~(0x111 << n);
value &= mask; // clear bits
value |= value3bits << n;
if(ina260_write_register(cdata,reg,value)){
return 1;
}
return 0;
}
return -EINVAL;
}
static ssize_t attr_field_store(struct kobject *_kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct client_data *cdata=container_of(_kobj,struct client_data,kobj);
int data=0;
unsigned char reg=INA260_REG_CONFIGURATION;
if(kstrtoint(buf, 10,&data))
return -EINVAL;
if(INA260_IS_ATTR(reset) && data!=0){
if(ina260_write_register(cdata, reg, 0xFFFF))
return -1;
} else if(INA260_IS_ATTR(avg)){
if(ina260_set_register_3bits(cdata,reg,9,data))
return -EINVAL;
} else if(INA260_IS_ATTR(mode)){
if(ina260_set_register_3bits(cdata,reg,0,data))
return -EINVAL;
} else if(INA260_IS_ATTR(ishct)){
if(ina260_set_register_3bits(cdata,reg,3,data))
return -EINVAL;
}
return count;
}
// ----- Registers -----
static struct kobj_attribute configuration_attribute =
__ATTR(configuration, 0664, attr_show, attr_store);
static struct kobj_attribute current_attribute = {
.attr = {.name = "current", // current is a defined macro so need to do it manually
.mode = VERIFY_OCTAL_PERMISSIONS(0444) },
.show = attr_show,
.store = attr_store,
};
static struct kobj_attribute bus_voltage_attribute =
__ATTR(bus_voltage, 0444, attr_show, attr_store);
static struct kobj_attribute power_attribute =
__ATTR(power, 0444, attr_show, attr_store);
static struct kobj_attribute mask_enable_attribute =
__ATTR(mask_enable, 0664, attr_show, attr_store);
static struct kobj_attribute alert_limit_attribute =
__ATTR(alert_limit, 0664, attr_show, attr_store);
static struct kobj_attribute manufacturer_id_attribute =
__ATTR(manufacturer_id, 0664, attr_show, attr_store);
static struct kobj_attribute die_id_attribute=
__ATTR(die_id, 0664, attr_show, attr_store);
static struct attribute *registers_attrs[] = {
&configuration_attribute.attr,
&current_attribute.attr,
&bus_voltage_attribute.attr,
&power_attribute.attr,
&mask_enable_attribute.attr,
&alert_limit_attribute.attr,
&manufacturer_id_attribute.attr,
&die_id_attribute.attr,
NULL,
};
static const struct attribute_group registers_group = {
.attrs = registers_attrs,
.name = "registers"
};
// ----- Fields -----
static struct kobj_attribute reset_field_attribute =
__ATTR(reset, 0664, attr_field_show, attr_field_store);
static struct kobj_attribute avg_field_attribute =
__ATTR(avg, 0664, attr_field_show, attr_field_store);
static struct kobj_attribute mode_field_attribute =
__ATTR(mode, 0664, attr_field_show, attr_field_store);
static struct kobj_attribute ishct_field_attribute =
__ATTR(ishct, 0664, attr_field_show, attr_field_store);
static struct kobj_attribute vbusct_field_attribute =
__ATTR(vbusct, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute aff_field_attribute =
__ATTR(aff, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute cvrf_field_attribute =
__ATTR(cvrf, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute ocl_field_attribute =
__ATTR(ocl, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute ucl_field_attribute =
__ATTR(ucl, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute bol_field_attribute =
__ATTR(bol, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute bul_field_attribute =
__ATTR(bul, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute pol_field_attribute =
__ATTR(pol, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute cnvr_field_attribute =
__ATTR(cnvr, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute ovf_field_attribute =
__ATTR(ovf, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute apol_field_attribute =
__ATTR(apol, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute len_field_attribute =
__ATTR(len, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute did_field_attribute =
__ATTR(did, 0444, attr_field_show, attr_field_store);
static struct kobj_attribute rid_field_attribute =
__ATTR(rid, 0444, attr_field_show, attr_field_store);
static struct attribute *fields_attrs[] = {
&reset_field_attribute.attr,
&avg_field_attribute.attr,
&mode_field_attribute.attr,
&ishct_field_attribute.attr,
&vbusct_field_attribute.attr,
&aff_field_attribute.attr,
&cvrf_field_attribute.attr,
&ocl_field_attribute.attr,
&ucl_field_attribute.attr,
&bol_field_attribute.attr,
&bul_field_attribute.attr,
&pol_field_attribute.attr,
&cnvr_field_attribute.attr,
&ovf_field_attribute.attr,
&apol_field_attribute.attr,
&len_field_attribute.attr,
&did_field_attribute.attr,
&rid_field_attribute.attr,
NULL,
};
static const struct attribute_group fields_group = {
.attrs = fields_attrs,
};
static struct kobj_type ina260_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
};
static int ina260_probe_register(struct i2c_client *client, unsigned char reg, int value){
unsigned char bytes[2];
if(i2c_master_send(client,&reg,1)<0){
return 1;
}
if(i2c_master_recv(client,bytes,2)<0){
return 1;
}
return ((bytes[0]<<8) | bytes[1])!=value;
}
static int ina260_probe_new(struct i2c_client *client){
struct client_data *p;
if(ina260_probe_register(client,INA260_REG_MANUFACTURER,0x5449) ||
ina260_probe_register(client,INA260_REG_DIE,0x2270)){
printk("INA260 chip probe failed on i2c bus %d\n",client->adapter->nr);
return 1;
}
printk("New INA260 chip addr=0x%x detected on i2c bus %d\n",client->addr,client->adapter->nr);
p=kzalloc(sizeof(struct client_data),GFP_KERNEL);
kobject_init(&p->kobj,&ina260_ktype);
if(kobject_add(&p->kobj,ina260_kobj,client->dev.kobj.name)){
kobject_put(&p->kobj);
kfree(p);
return 1;
}
p->client=client;
p->reg=INA260_REG_DIE; // Last reg to be used
i2c_set_clientdata(client,p);
if(sysfs_create_group(&p->kobj,&registers_group)||
sysfs_create_group(&p->kobj,&fields_group)){
kobject_put(&p->kobj);
kfree(p);
return 1;
}
return 0;
}
static void ina260_remove(struct i2c_client *client){
struct client_data *p=i2c_get_clientdata(client);
kobject_put(&p->kobj);
kfree(p);
printk("INA260 addr=0x%x remove from i2c bus %d\n",client->addr,client->adapter->nr);
}
static struct i2c_driver ina260_driver = {
.driver = {
.name = "ina260"
},
.probe_new = ina260_probe_new,
.remove = ina260_remove,
.id_table = ina260_ids
};
static int __init ina260_init(void){
ina260_kobj = kobject_create_and_add("ina260",kernel_kobj);
i2c_add_driver(&ina260_driver);
return 0;
}
static void __exit ina260_exit(void){
i2c_del_driver(&ina260_driver);
kobject_put(ina260_kobj);
}
module_init(ina260_init);
module_exit(ina260_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Loïc Guegan");
MODULE_DESCRIPTION("INA260 Texas Instruments");
MODULE_VERSION("1.0");