Initial Commit
This commit is contained in:
commit
c63a224e2e
2 changed files with 385 additions and 0 deletions
349
adxl345.c
Normal file
349
adxl345.c
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
#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");
|
36
test_adxl345.c
Normal file
36
test_adxl345.c
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
const
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
char buf[32];
|
||||||
|
size_t count = 1;
|
||||||
|
ssize_t ret_size;
|
||||||
|
|
||||||
|
if((fd = open("/dev/adxl345", O_RDWR)) == -1){
|
||||||
|
printf("/dev/adxl345 opening failed\n");
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if((ret_size = read(fd, &buf, count)) < 0){
|
||||||
|
printf("Error while reading the device file.\n");
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
printf("Data read successfully. Asked %i byte, received %i byte\n",count, ret_size);
|
||||||
|
printf("Data:\n");
|
||||||
|
for(int i = 0; i<ret_size; i++){
|
||||||
|
printf("%i",buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue