commit c63a224e2eab7df1c769649854108fb4f2c4a922 Author: Arthur 'Grizzly' Grisel-Davy Date: Tue Feb 16 15:48:46 2021 +0100 Initial Commit diff --git a/adxl345.c b/adxl345.c new file mode 100644 index 0000000..b701112 --- /dev/null +++ b/adxl345.c @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); diff --git a/test_adxl345.c b/test_adxl345.c new file mode 100644 index 0000000..a97b596 --- /dev/null +++ b/test_adxl345.c @@ -0,0 +1,36 @@ +#include +#include +#include + +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