#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 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){ char nsample; char rest; unsigned char res[FIFO_SIZE*6]; // Not res[count] to avoid C99 warning int i=0; int16_t direction_sample; struct Sample sample; int ret; // 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"); // Compute the number of complete samples (6 bytes) to get nsample = count/6; rest = count%6; // Adjust what to get depending on the number of sample in the fifo if(fifo_len(&(d->fifo))<=nsample){ nsample = fifo_len(&(d->fifo)); rest = 0; } //pr_info("%i sample in fifo\nGetting %i full sample and %i more bytes\n",fifo_len(&(d->fifo)),nsample,rest); // Get the full samples for(i=0;ififo)); res[i*6+1] = (unsigned char) (sample.x>>8); res[i*6+0] = (unsigned char) (sample.x & 0x00ff); res[i*6+3] = (unsigned char) (sample.y>>8); res[i*6+2] = (unsigned char) (sample.y & 0x00ff); res[i*6+5] = (unsigned char) (sample.z>>8); res[i*6+4] = (unsigned char) (sample.z & 0x00ff); //pr_info("%i %i %i\n",sample.x,sample.y,sample.z); } // Get the rest if(rest>0){ sample = fifo_pop(&(d->fifo)); } for(i=nsample*6;i 0){ pr_info("%i bytes not copied to user\n",ret); return(-1); } return (ssize_t)nsample*6+rest; } irq_handler_t adxl345_int(int irq, struct adxl345_device * d){ struct Sample sample; char question = 0x39; 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 = 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 (void *)IRQ_HANDLED; } static int adxl345_probe(struct i2c_client *client,const struct i2c_device_id *id){ // i2c variables char question[2]; char response[1]; char ret_code; // Interuption variables void *int_ptr = &adxl345_int; const char *device_name = "adxl345"; int code; // Init 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"); // Init FIFO fifo_init(&(d->fifo)); pr_info("Fifo Initialized\n"); // Init i2c_client i2c_set_clientdata(client,d); pr_info("i2c_clientdata set\n"); // Populate miscdev fields d->miscdev.parent = &(client->dev); d->miscdev.minor = MISC_DYNAMIC_MINOR; d->miscdev.name = "adxl345"; d->miscdev.fops = &adxl345_fops; //pr_info("miscdev fields set\n"); // Registering to misc misc_register(&(d->miscdev)); //pr_info("misc_register set\n"); //pr_info("Misc minor is: %i\n",d->miscdev.minor); // 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 device 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: 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); pr_info("ADXL345 device setup done.\n"); return(0); } static int adxl345_remove(struct i2c_client *client) { // Allocate question(address), response, and return code. All Char struct adxl345_device *d; char question[2]; char ret_code; // 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); return(1); } else{ pr_info("ADXL345 Device put to standby mode."); } // récupération de l'instance de adxl345_device d = i2c_get_clientdata(client); // Désenregistrement de misc misc_deregister(&(d->miscdev)); pr_info("ADXL345 Device unregistered.\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");