adxl345_driver/adxl345.c
2021-02-16 15:48:46 +01:00

350 lines
9.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#define FIFO_SIZE 64
// TODO: Déclarer toutes les variables en début de code pour faire plaisir à GCC90.
// TODO: Changer la déclaration des variables utiles à l'i2c
// Prototypes +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ssize_t adxl345_read(struct file *, char __user *, size_t, loff_t *);
// Structures +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
struct Sample {
int16_t x;
int16_t y;
int16_t z;
};
struct Fifo {
struct Sample content[FIFO_SIZE];
int read_idx;
int write_idx;
};
struct adxl345_device{
struct Fifo fifo;
struct miscdevice miscdev;
};
struct file_operations adxl345_fops = {
.owner = THIS_MODULE,
.read = adxl345_read,
};
// FIFO management functions ++++++++++++++++++++++++++++++++++++++++++
static void fifo_init(struct Fifo *f) {
f->read_idx = 0;
f->write_idx = 0;
}
static int fifo_len(struct Fifo *f) {
if (f->write_idx >= f->read_idx) {
return f->write_idx - f->read_idx;
} else {
return FIFO_SIZE - f->read_idx + f->write_idx + 1;
}
}
static int fifo_empty(struct Fifo *f) {
return fifo_len(f) == 0;
}
static int fifo_full(struct Fifo *f) {
return fifo_len(f) == FIFO_SIZE;
}
static void fifo_push(struct Fifo *f, struct Sample *s) {
f->content[f->write_idx] = *s;
f->write_idx = (f->write_idx + 1) % FIFO_SIZE;
}
static struct Sample fifo_pop(struct Fifo *f) {
struct Sample res = f->content[f->read_idx];
f->read_idx = (f->read_idx + 1) % FIFO_SIZE;
return res;
}
// Fonctions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ssize_t adxl345_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos){
// TODO: permetre de choisir l'axe de lecture
pr_info("In adxl345_read function\n");
// Recuperation de la structure adxl345
struct adxl345_device *d = container_of(file->private_data,struct adxl345_device,miscdev);
pr_info("Got pointer to adxl345_device structure\n");
int nsample = fifo_len(&(d->fifo));
pr_info("Got %i sample in the FIFO\n",nsample);
struct Sample sample;
int i;
for(i=count; i<0; i--){
sample = fifo_pop(&(d->fifo));
pr_info("Sample %i: (%i,%i,%i)\n",i,sample.x,sample.y,sample.z);
}
// Ecriture du resultat dans le buffer de retour
//copy_to_user(buf, response, 1);
return (ssize_t)1;
}
irq_handler_t adxl345_int(int irq, struct adxl345_device * d){
int16_t x;
int16_t y;
int16_t z;
struct Sample sample = {
.x = x,
.y = y,
.z = z
};
char question = 0x00;
char response;
char ret_code[2];
int nentries;
int nsample;
struct miscdevice miscdev = d->miscdev;
struct i2c_client *ptrclient = container_of(miscdev.parent,struct i2c_client,dev);
struct i2c_client *client;
copy_from_user(client,ptrclient,sizeof(ptrclient));
ret_code[0] = i2c_master_send(client,question,1);
ret_code[1] = i2c_master_recv(client,response,1);
if(ret_code[0] < 0 || ret_code[1] < 0){
pr_info("Error in reading FIFO STATUS: %i %i\n",ret_code[0], ret_code[1]);
}
nentries = response >>2;
pr_info("HARD FIFO entries: %x\n",nentries);
fifo_push(&(d->fifo), &sample);
nsample = fifo_len(&(d->fifo));
pr_info("%i samples in SOFT FIFO\n",nsample);
return IRQ_HANDLED;
}
static int adxl345_probe(struct i2c_client *client,const struct i2c_device_id *id){
// Initialize structures
// Instantiation de adxl345_device
struct adxl345_device *d;
d = (struct adxl345_device *) kzalloc(sizeof(struct adxl345_device), GFP_KERNEL);
pr_info("adxl_345_device and file_operation struct allocated\n");
// Initialisation de la fifo
fifo_init(&(d->fifo));
pr_info("Fifo Initialized\n");
// Association avec i2c_client
i2c_set_clientdata(client,d);
pr_info("i2c_clientdata set\n");
// Remplissage des champs de miscdev
d->miscdev.parent = &(client->dev);
d->miscdev.minor = MISC_DYNAMIC_MINOR;
d->miscdev.name = "adxl345";
d->miscdev.fops = &adxl345_fops;
// Erreur associée: adxl345_fops has an incomplete type struct file_operation
pr_info("miscdev fields set\n");
// Enregistrement aupres de misc
misc_register(&(d->miscdev));
pr_info("misc_register set\n");
pr_info("Misc minor is: %i\n",d->miscdev.minor);
// Allocate question(address), response, and return code. All Char
char question[2];
char response[1];
char ret_code;
// Verify device ID
question[0] = 0x00;
question[1] = 0x00;
i2c_master_send(client,question,1);
ret_code = i2c_master_recv(client,response,1);
if(ret_code < 0){
pr_info("i2c return code: %i\n",ret_code);
}
pr_info("DEVID: %x\n",*response);
// Set parameters
// Power control measure mode
question[0] = 0x2D;
question[1] = 0x08;
ret_code = i2c_master_send(client,question,2);
if(ret_code < 0){
pr_info("Error when setting Power Control parameter.\nReturn code: %i\n", ret_code);
}
// BW_RATE = 100 Hz
question[0] = 0x2C;
question[1] = 0x0A;
ret_code = i2c_master_send(client,question,2);
if(ret_code < 0){
pr_info("Error when setting Data Rate parameter.\nReturn code: %i\n", ret_code);
}
// Data Format
question[0] = 0x31;
question[1] = 0x00;
ret_code = i2c_master_send(client,question,2);
if(ret_code < 0){
pr_info("Error when setting Data Format parameter.\nReturn code: %i\n", ret_code);
}
// Set FIFO to Stream mode with 20 sample
question[0] = 0x38;
question[1] = 0x94;
ret_code = i2c_master_send(client,question,2);
if(ret_code < 0){
pr_info("Error when setting FIFO mode parameter.\nReturn code: %i\n", ret_code);
}
// Watermark Interruption Enabled
question[0] = 0x2e;
question[1] = 0x02;
ret_code = i2c_master_send(client,question,2);
if(ret_code < 0){
pr_info("Error when setting Interuptions parameter.\nReturn code: %i\n", ret_code);
}
pr_info("Device Setting done.\n");
// Interruption Handler:
void *int_ptr = &adxl345_int;
const char *device_name = "adxl345";
int code;
code = devm_request_threaded_irq(&(client->dev),client->irq, NULL, int_ptr, IRQF_ONESHOT, device_name, d);
pr_info("irq declaration done. Irq number: %i\n",client->irq);
pr_info("irq request return code: %i\n",code);
return(0);
}
static int adxl345_remove(struct i2c_client *client)
{
// Allocate question(address), response, and return code. All Char
char *question;
char *response;
char *ret_code;
question = kmalloc(2,GFP_KERNEL);
response = kmalloc(1,GFP_KERNEL);
ret_code = kmalloc(1,GFP_KERNEL);
if (!question && !response && !ret_code){
pr_info("Unable to get allocate memory (question)\n");
kfree(question);
kfree(response);
kfree(ret_code);
return(-1);
}
// Power control measure mode
question[0] = 0x2D;
question[1] = 0x00;
ret_code = i2c_master_send(client,question,2);
if(ret_code < 0){
pr_info("Error when setting device in standby mode.\nReturn code: %i\n", ret_code);
}
else{
pr_info("Device put to Standby mode");
}
// Free memory
kfree(question);
kfree(response);
kfree(ret_code);
// récupération de l'instance de adxl345_device
struct adxl345_device *d;
d = i2c_get_clientdata(client);
// Désenregistrement de misc
misc_deregister(&(d->miscdev));
kfree(d);
pr_info("ADXL345 Removed\n");
return(0);
}
/* La liste suivante permet l'association entre un périphérique et son
pilote dans le cas d'une initialisation statique sans utilisation de
device tree.
Chaque entrée contient une chaîne de caractère utilisée pour
faire l'association et un entier qui peut être utilisé par le
pilote pour effectuer des traitements différents en fonction
du périphérique physique détecté (cas d'un pilote pouvant gérer
différents modèles de périphérique).
*/
static struct i2c_device_id adxl345_idtable[] = {
{ "adxl345", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adxl345_idtable);
#ifdef CONFIG_OF
/* Si le support des device trees est disponible, la liste suivante
permet de faire l'association à l'aide du device tree.
Chaque entrée contient une structure de type of_device_id. Le champ
compatible est une chaîne qui est utilisée pour faire l'association
avec les champs compatible dans le device tree. Le champ data est
un pointeur void* qui peut être utilisé par le pilote pour
effectuer des traitements différents en fonction du périphérique
physique détecté.
*/
static const struct of_device_id adxl345_of_match[] = {
{ .compatible = "tp,testi2cdev",
.data = NULL },
{}
};
MODULE_DEVICE_TABLE(of, adxl345_of_match);
#endif
static struct i2c_driver adxl345_driver = {
.driver = {
/* Le champ name doit correspondre au nom du module
et ne doit pas contenir d'espace */
.name = "adxl345",
.of_match_table = of_match_ptr(adxl345_of_match),
},
.id_table = adxl345_idtable,
.probe = adxl345_probe,
.remove = adxl345_remove,
};
module_i2c_driver(adxl345_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ADXL345 driver");
MODULE_AUTHOR("Arthur Grisel-Davy");