#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; // Push read index because we keep only the {FIFO_SIZE} last samples if(f->write_idx == f->read_idx){ f->read_idx = (f->read_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){ struct Sample sample; char question[2]; // TODO: changer le format de question question[0] = 0x39; question[1] = 0x00; unsigned char response[6]; char ret_code[2]; char nentries; char nsample; char i; struct miscdevice miscdev = d->miscdev; struct device *dev = miscdev.parent; struct i2c_client *client = container_of(dev,struct i2c_client,dev); // Get number of entries in Hard FIFO 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[0] & 0x3f; //pr_info("Getting %i sample from the Hard FIFO:\n",nentries); for(i=nentries; i>0; i--){ // Get entries from Hard FIFO question[0] = 0x32; ret_code[0] = i2c_master_send(client,question,1); ret_code[1] = i2c_master_recv(client,response,6); sample.x = (int16_t)(response[1]<<8 | response[0]); sample.y = (int16_t)(response[3]<<8 | response[2]); sample.z = (int16_t)(response[5]<<8 | response[4]); //pr_info(" Nouveau Sample: X=%i Y=%i Z=%i\n",sample.x,sample.y,sample.z); 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");