{"id":18007015,"url":"https://github.com/yne/modio_driver","last_synced_at":"2025-03-26T12:31:13.432Z","repository":{"id":27944139,"uuid":"31436757","full_name":"yne/modio_driver","owner":"yne","description":"kernel module for the olimex mod-io","archived":false,"fork":false,"pushed_at":"2015-12-19T01:06:41.000Z","size":54,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"gh-pages","last_synced_at":"2023-08-02T14:56:07.682Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yne.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-02-27T20:02:50.000Z","updated_at":"2023-08-02T14:56:07.683Z","dependencies_parsed_at":"2022-09-04T09:10:26.773Z","dependency_job_id":null,"html_url":"https://github.com/yne/modio_driver","commit_stats":null,"previous_names":[],"tags_count":0,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yne%2Fmodio_driver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yne%2Fmodio_driver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yne%2Fmodio_driver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yne%2Fmodio_driver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yne","download_url":"https://codeload.github.com/yne/modio_driver/tar.gz/refs/heads/gh-pages","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222145578,"owners_count":16938477,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-30T01:11:39.020Z","updated_at":"2024-10-30T01:11:39.465Z","avatar_url":"https://github.com/yne.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"Travail effectué\n================\n\n- [Source](https://github.com/yne/modio_driver)\n- [Archive](https://github.com/yne/modio_driver/archive/gh-pages.zip)\n\nGestion des entrées / sorties\n--------\n\n- lire les états des relais (4 devices REL0 à REL3). **OK**\n- écrire dans les relais (0 ouvre et 1 ferme le relai). **OK**\n- lire les entrées optocouplées (4 devices OPT0 à OPT3). **OK**\n- lire les entrées analogiques (4 devices ANL0 à ANL3). **OK**\n\nModification de l'adresse\n---------\n\n- permettre la prise en charge d'adresses différentes. **OK**\n- L'adresse à utiliser sera reçu en paramètre au chargement du module. **OK**\n- Si aucun module ne répond à cette adresse, une commande de changement d'adresse sera envoyée à l'adresse par défaut (0x58) pour la modifier en la nouvelle adresse. **KO**\n\nCross compilation\n=================\n\nAfin de pouvoir exécuter le module sur notre cible (olimex A20)\nil faut mettre en place plusieurs choses :\n\n[compilation du noyau](build_kernel.sh)\n--------------------\nCette étape a été la plus complexe car elle nécessite le bon déroulement de plusieurs processus qui dépendent parfois d'utilitaires qui n'étaient pas présent sur les machines (makeinfo).\nVoici le script utilisé pour compiler le noyau ainsi que les outils de cross compilation.\nOn obtient en sortie, un fichier image (.img) contenant un linux complet (Kernel,binutils,environnement graphique) a flasher sur la SDcard.\n\nOn commence tout d'abord par récupérer l'ensemble des sources depuis différentes sources :\n```\nwget http://ftp.gnu.org/gnu/gcc/gcc-4.9.2/gcc-4.9.2.tar.gz              -q -O - | tar xz \u0026\nwget http://ftp.gnu.org/gnu/binutils/binutils-2.24.tar.gz               -q -O - | tar xz \u0026\nwget ftp://sourceware.org/pub/newlib/newlib-2.1.0.tar.gz                -q -O - | tar xz \u0026\nwget http://www.mpfr.org/mpfr-current/mpfr-3.1.2.tar.gz                 -q -O - | tar xz \u0026\nwget http://gmplib.org/download/gmp/gmp-6.0.0a.tar.xz                   -q -O - | tar xJ \u0026\nwget http://ftp.gnu.org/gnu/mpc/mpc-1.0.2.tar.gz                        -q -O - | tar xz \u0026\nwget http://ftp.gnu.org/gnu/gdb/gdb-7.8.tar.gz                          -q -O - | tar xz \u0026\nwget http://github.com/linux-sunxi/linux-sunxi/archive/sunxi-3.4.tar.gz -q -O - | tar xz \u0026\nwget http://github.com/linux-sunxi/u-boot-sunxi/archive/sunxi.tar.gz    -q -O - | tar xz \u0026\u0026 mv u-boot* uboot \u0026\nwget http://github.com/linux-sunxi/sunxi-tools/archive/master.tar.gz    -q -O - | tar xz \u0026\u0026 mv sunxi-tools* tools \u0026\nwget http://github.com/linux-sunxi/sunxi-boards/archive/master.tar.gz   -q -O - | tar xz \u0026\u0026 mv sunxi-boards* board \u0026\nwait\n```\nChaque téléchargement est fait en parallèle et est décompressé à la volé (pour éviter de se retrouver avec le double de la taille des sources sur le disque à un instant T)\n\nJ'ai dû par la suite, patcher le makefile de la newlib car cette dernière avait une variable `tooldir` qui pointait vers un mauvais chemin :\n```\nsed -i 's|^tooldir = .*$|tooldir = $(exec_prefix)/$(target_alias)\\nhost_makefile_frag = $(srcdir)/../../config/default.mh\\n|g' $PREFIX_SRC/newlib/libgloss/arm/cpu-init/Makefile.in\n```\n\nPuis on compile successivement :\n* binutils : qui est un ensemble d'outils de développement logiciel\n\t\n\t```\n\tcd $PREFIX_BUILD/binutils\n\t$PREFIX_SRC/binutils/configure -q --prefix=$PREFIX_BUILD/binutils --target=$TARGET\n\tmake all -s\n\tmake install -s\n\t```\n\n* gmp : qui est une libraire de calcul arithmétique\n\t\n\t```\n\tcd $PREFIX_BUILD/gmp\n\t$PREFIX_SRC/gmp/configure -q --prefix=$PREFIX_BUILD/gmp\n\tmake all -s\n\tmake install -s\n\t```\n* mpfr : qui est une autre libraire de calcul multi precision\n\t\n\t```\n\tcd $PREFIX_BUILD/mpfr\n\t$PREFIX_SRC/mpfr/configure -q --prefix=$PREFIX_BUILD/mpfr --target=$TARGET \\\n\t--with-gmp-include=$PREFIX_BUILD/gmp/include \\\n\t--with-gmp-lib=$PREFIX_BUILD/gmp/lib\n\tmake all -s\n\tmake install -s\n\t```\n\n* mpc : qui est une libraire de calcul haute precision\n\t\n\t```\n\tcd $PREFIX_BUILD/mpc\n\t$PREFIX_SRC/mpc/configure -q --prefix=$PREFIX_BUILD/mpc --target=$TARGET \\\n\t--with-gmp-include=$PREFIX_BUILD/gmp/include \\\n\t--with-gmp-lib=$PREFIX_BUILD/gmp/lib \\\n\t--with-mpfr-include=$PREFIX_BUILD/mpfr/include \\\n\t--with-mpfr-lib=$PREFIX_BUILD/mpfr/lib\n\tmake all -s\n\tmake install -s\n\t```\n\t\n* gcc : le GNU Compiler of Compiler, qui est le compilateur C utilisé par la suite\n\t\n\t```\n\tcd $PREFIX_BUILD/gcc\n\t$PREFIX_SRC/gcc/configure -q --prefix=$PREFIX_BUILD/gcc --target=$TARGET \\\n\t  --without-headers \\\n\t  --with-newlib \\\n\t  --with-gnu-as \\\n\t  --with-gnu-ld \\\n\t  --with-gmp=$PREFIX_BUILD/gmp \\\n\t  --with-mpfr=$PREFIX_BUILD/mpfr \\\n\t  --with-mpc=$PREFIX_BUILD/mpc\n\tmake all-gcc -s\n\tmake install-gcc -s\n\t```\n\t\n* newlib : une libraire standard C conçue pour les systèmes embarqués\n\t\n\t```\n\tcd $PREFIX_BUILD/newlib\n\t$PREFIX_SRC/newlib/configure -q --prefix=$PREFIX_BUILD/newlib --target=$TARGET\n\t#disable etc building (wich require makeinfo that i don't have)\n\tsed -i 's|all-host: maybe-all-etc|#all-host: maybe-all-etc|g' $PREFIX_BUILD/newlib/Makefile\n\tmake -s\n\t#disable etc install (since they don't haven't been built)\n\tsed -i 's|    maybe-install-etc|#maybe-install-etc|g' $PREFIX_BUILD/newlib/Makefile\n\tmake install -s\n\t```\n\t\n* gcc+newlib : compilation de GCC en utilisant la newlib\n\t\n\t```\n\texport LD_LIBRARY_PATH=$PREFIX_BUILD/gmp/lib:$PREFIX_BUILD/mpc/lib:$PREFIX_BUILD/mpfr/lib:$PREFIX_BUILD/newlib/arm-eabi/lib\n\tcd $PREFIX_BUILD/gcc\n\t$PREFIX_SRC/gcc/configure -q --prefix=$PREFIX_BUILD/gcc --target=$TARGET\\\n\t  --without-headers \\\n\t  --with-newlib \\\n\t  --with-gnu-as \\\n\t  --with-gnu-ld \\\n\t  --with-gmp=$PREFIX_BUILD/gmp \\\n\t  --with-mpfr=$PREFIX_BUILD/mpfr \\\n\t  --with-mpc=$PREFIX_BUILD/mpc \\\n\t  --enable-languages=c,c++ \\\n\t  --disable-shared \\\n\t  --disable-libssp \\\n\t  --disable-nls\n\tmake all -s\n\tmake install-gcc -s\n\t```\n\t\n* gdb : le debuger de GNU\n\t\n\t```\n\tcd $PREFIX_BUILD/gdb\n\trm -rf *\n\t$PREFIX_SRC/gdb/configure -q --prefix=$PREFIX_BUILD/gdb --target=$TARGET\\\n\t  --enable-sim-arm\\\n\t  --enable-sim-stdio\n\tmake -s\n\tmake install\n\t```\n\t\n* kernel : le noyeau linux lui même\n\t\n\t```\n\tcd $PREFIX_SRC/linux\n\tmake ARCH=arm CROSS_COMPILE=arm-eabi- sun5i_defconfig\n\tmake ARCH=arm CROSS_COMPILE=arm-eabi- uImage modules -j4\n\tmake ARCH=arm CROSS_COMPILE=arm-eabi- INSTALL_MOD_PATH=$PREFIX_BUILD modules_install\n\t```\n\t\n* Uboot : l'utilitaire de boot utilisé par les olimex\n\t\n\t```\n\tcd $PREFIX_SRC/uboot\n\tmake CROSS_COMPILE=arm-eabi- A13-OLinuXino_config \n\tmake CROSS_COMPILE=arm-eabi-\n\t```\n\nCependant, personne n'avons pas réussi a faire booter l'A13.\nJ'ai donc travaillé sur l'A20 pour la suite en utilisant la toolchain d'un étudiant qui avait commencé pour l'A20.\n\nImplémentation du driver\n========================\n\nPour mener à bien l'implémentation, j'ai séparer le code en 4 parties :\n\n### [Partie GPIO](driver.pio.c)\n\nCette partie contient les fonctions d'accès basiques au PIO :\n\n* pio_io : met un port en input ou en output\n\t\n\t```\n\tvoid pio_io(int port,Pio_way_t way){\n\t\tpioB-\u003ecfg2 \u0026= ~(0b111 \u003c\u003c port);//mise a 0 des 3 bits du port\n\t\tif(way==OUT)\n\t\t\tpioB-\u003ecfg2 |= (1 \u003c\u003c port);//remise a 1 si en mode out\n\t\tpio_wait();\n\t}\n\t```\n\t\n* pio_set : envoie une valeur sur le port\n\t\n\t```\n\tvoid pio_set(int port,Pio_level_t lv){//SDA_DAT,SCL_DAT\n\t\tif(lv==HIGH)pioB-\u003edata |= (1\u003c\u003c port);\n\t\tif(lv==LOW )pioB-\u003edata \u0026=~(1\u003c\u003c port);\n\t\tpio_wait();\n\t}\t\n\t```\n\t\n* pio_get : récupère une valeur sur le port\n\t\n\t```\n\tunsigned char pio_get(int port){\n\t\treturn !!(pioB-\u003edata \u0026 (1\u003c\u003c port));\n\t}\n\t```\n\t\n* pio_ack : envoie un acquittement sur le port\n\t\n\t```\n\tvoid pio_ack(Pio_ack_t type){\n\t\tpio_io (SDA_CFG,OUT);\n\t\tpio_set(SDA_DAT,type);\n\t\tpio_set(SCL_DAT,HIGH);\n\t\tpio_set(SCL_DAT,LOW);\n\t}\n\t```\n\nAinsi que 2 fonctions de plus haut niveau qui offrent une interface de lecture/écriture sur le PIO.\n\n* pio_recv : récupère 8 bits de donnée depuis le bus.\n\t\n\t```\n\tunsigned char pio_recv(Pio_ack_t ack){\n\t\tint i;\n\t\tunsigned char res = 0;\n\t\tpio_io(SDA_CFG,IN);\n\t\tfor(i=7;i\u003e=0;i--){\n\t\t\tpio_set(SCL_DAT,HIGH);\n\t\t\tres |= pio_get(SDA_DAT) \u003c\u003c i;\n\t\t\tpio_set(SCL_DAT,LOW);\n\t\t}\n\t\tpio_io(SDA_CFG,OUT);\n\t\tpio_ack(ack);\n\t\treturn res;\n\t}\n\t```\n\t\n* pio_send : envoie 8 bits de données sur le bus.\n\t\n\t```\n\tvoid pio_send(unsigned char data){\n\t\tint i;\n\t\tfor(i=7;i \u003e =0;i--){\n\t\t\tpio_set(SDA_DAT,data \u0026 (1 \u003c\u003c i) ? HIGH : LOW);\n\t\t\tpio_set(SCL_DAT,HIGH);\n\t\t\tpio_set(SCL_DAT,LOW);\n\t\t}\n\t}\n\t```\n\t\n\nEnfin les 2 fonctions d'initialisation et de finalisation :\n\n* pio_start : map l'adresse du GPIO dans une adresse virtuelle via `ioremap`\n\t\n\t```\n\tvoid pio_start(void){\n\t\tpioB = ioremap(PIO_BASE(PB),sizeof(Pio_reg_t));\n\t\tpio_io(SDA_CFG,OUT);\n\t\tpio_io(SCL_CFG,OUT);\n\t}\n\t```\n\t\n* pio_stop : libère l'adresse virtuel avec `iounmap`\n\t\n\t```\n\tvoid pio_stop(void){\n\t\tiounmap(pioB);\n\t}\n\t```\n\n### [Partie I2C](driver.pio.c)\n\nCette partie contient l'implémentation du protocole i2c.\n\n* i2c_wait_ack  : Lis l'acquittement i2c\n\t\n\t```\n\tint i2c_wait_ack(void){\n\t\tint val;\n\t\tpio_set(SDA_DAT,HIGH);\n\t\tpio_io(SDA_CFG,IN);\n\t\tpio_set(SCL_DAT,HIGH);\n\t\tval=pio_get(SDA_DAT);\n\t\tpio_set(SCL_DAT,LOW);\n\t\tpio_io(SDA_CFG,OUT);\n\t\tif(val)printk(KERN_ALERT \"ACK ERROR\\n\");\n\t\treturn -val;\n\t}\n\t```\n\t\n\t![](https://upload.wikimedia.org/wikipedia/commons/d/d8/I2C_ACK.svg)\n* i2c_sendStart : Envoie la trame de start\n\t\n\t```\n\tvoid i2c_sendStart(void){\n\t\tpio_set(SDA_DAT,HIGH);\n\t\tpio_set(SCL_DAT,HIGH);\n\t\tpio_set(SDA_DAT,LOW);\n\t\tpio_set(SCL_DAT,LOW);\n\t}\n\t```\n\t\n\t![](https://upload.wikimedia.org/wikipedia/commons/e/e8/I2C_START.svg)\n* i2c_sendStop : Envoie la trame de stop\n\t\n\t```\n\tvoid i2c_sendStop(void){\n\t\tpio_set(SDA_DAT,LOW);\n\t\tpio_set(SCL_DAT,HIGH);\n\t\tpio_set(SDA_DAT,HIGH);\n\t}\n\t```\n\t\n\t![](https://upload.wikimedia.org/wikipedia/commons/b/b9/I2C_STOP.svg)\n* i2c_send : Envoie une donnée sur le bus\n\t\n\t```\n\tvoid i2c_send(unsigned char data){\n\t\tpio_send(data);\n\t\ti2c_wait_ack();\n\t}\n\t```\n\t\n\t![](https://upload.wikimedia.org/wikipedia/commons/4/4d/I2C_Adresse10bitsEcriture.svg)\n\nJe me suis créé une fonction dans le but d'alléger les couches supérieurs de la partie protocolaire (envoi du Start, envoi de l'ack ... envoi du stop)\n* i2c_bloc : en charge de l'envoi ou de réception d'une trame i2c entière.\n\t\n\t```\n\tvoid i2c_bloc(unsigned char addr,unsigned char cmd,int size,unsigned char*data){\n\t\tint i;\n\t\ti2c_sendStart();\n\t\ti2c_send(addr);\n\t\tif((addr\u00261)==WR)i2c_send(cmd);\n\t\tfor(i=0;i \u003c size;i++){\n\t\t\tif((addr\u00261)==RD)data[i]=pio_recv(i \u003c size-1?ACK:NACK);//ACK ACK ACK... NACK\n\t\t\tif((addr\u00261)==WR)i2c_send(data[i]);\n\t\t}\n\t\ti2c_sendStop();\n\t}\n\t```\n\n### [Partie applicative](driver.app.c)\n\nLa partie applicative fait appel aux fonctions i2c et fournie une sur-couche simple pour le driver :\n\n* read_opt : Permet de lire l'état d'un optocoupleur\n\t\n\t```\n\tunsigned char read_opt(int num){\n\t\tunsigned char states;\n\t\ti2c_bloc(SLAVE_ADDR(p_addr,WR),MODIO_ADDR_OPT,0,NULL);\n\t\ti2c_bloc(SLAVE_ADDR(p_addr,RD),MODIO_ADDR_OPT,sizeof(states),\u0026states);\n\t\treturn !!(states\u0026(1 \u003c\u003c num));\n\t}\n\t```\n\t\n* read_anl : Lire sur l'entrée analogique\n\t\n\t```\n\tunsigned char read_anl(int num){\n\t\tunsigned char i,res=0,val[2];//low,high\n\t\ti2c_bloc(SLAVE_ADDR(p_addr,WR),MODIO_ADDR_ANL+num,0,NULL);\n\t\ti2c_bloc(SLAVE_ADDR(p_addr,RD),MODIO_ADDR_ANL+num,sizeof(val),val);\n\t\t//inverse les bits 0 a 9\n\t\tfor(i=0;i\u003c 8;i++){//sur les 8 premiers\n\t\t\tres |= (!!(val[0] \u0026 0x80)) \u003c\u003c i;\n\t\t\tval[0] \u003c\u003c= 1;\n\t\t}\n\t\t//et sur les 2 derniers\n\t\tres |= (!!(val[1] \u0026 0b10)) \u003c\u003c 8;\n\t\tres |= (!!(val[1] \u0026 0b01)) \u003c\u003c 9;\n\t\n\t\treturn res\u003e\u003e2;\n\t}\n\t```\n\t\n* write_rel : Active/désactive un relais et met a jours le cache d'état des relais.\n\t\n\t```\n\tvoid write_rel(unsigned char num,Pio_level_t lv){\n\t\tif(lv==HIGH)rel_state |=  (1\u003c\u003c num);\n\t\tif(lv==LOW )rel_state \u0026= ~(1\u003c\u003c num);\n\t\t//for(i=0;i\u003c 4;i++)printk(\"%c\", (rel_state\u00261\u003c\u003c i)?'#':'_');printk(\"\\n\");\n\t\ti2c_bloc(SLAVE_ADDR(p_addr,WR),MODIO_ADDR_REL,sizeof(rel_state),\u0026rel_state);\n\t}\n\t```\n\t\n* read_rel : permet de lire l'état d'un relais à partir du cache d'état des relais.\n\t\n\t```\n\tunsigned char read_rel(unsigned char num){\n\t\treturn (rel_state \u003e\u003enum)\u00261;\n\t}\n\t```\n\t\n* rel_state : permet de connaitre l'état des relais afin d'en renvoyer l'état si demandé\n\t\n\t```\n\tunsigned char rel_state=0;\n\t```\n\t\n* set_addr : permet de changer l'adresse du modio\n\t\n\t```\n\tvoid set_addr(unsigned char addr){\n\t\ti2c_bloc(SLAVE_ADDR(p_addr,WR),MODIO_ADDR_CHG,sizeof(addr),\u0026addr);\n\t\tp_addr=addr;\n\t}\n\t```\n\nEnfin, pour récupérer l'adresse demandée par l'utilisateur au lancement du module j'utilise :\n```\nstatic int p_addr=MODIO_ID;\nmodule_param(p_addr, int, S_IRUGO);\n```\n\n### [Partie driver (File Operations)](driver.fop.c)\n\nCette partie est la couche de plus haut niveau, elle fournie les interfaces avec le kernel pour répondre aux appels open/read/write/close de l'utilisateur.\n\n* my_open : met à jours les pointeurs vers les fonctions de read et de write selon le type de périphérique demandé (car certain périphérique sont en lecture seulement)\n\t\n\t```\n\tint my_open(struct inode *in, struct file *f){\n\t\tvoid*all_read []={my_read,my_read,my_read};\n\t\tvoid*all_write[]={my_write,NULL,NULL};\n\t\t\n\t\tif(iminor(in)\u003eCOUNT(Devices))return -1;\n\t\tmy_fops.read  = all_read [Devices[iminor(in)].type];\n\t\tmy_fops.write = all_write[Devices[iminor(in)].type];\n\t\tprintk(KERN_ALERT \"%s(%s%i) [%c%c%c]\\n\",__FUNCTION__,NAME(iminor(in)),\n\t\t\tmy_fops.read?'r':'-',my_fops.write?'w':'-',my_fops.unlocked_ioctl?'x':'-');\n\t\treturn 0;\n\t}\n\t```\n\t\n* my_read : se base sur le mineur (0~11) pour savoir quel est le type de périphérique visé et appel la fonction associée (read_opt, read_anl, read_rel)\n\t\n\t```\n\tssize_t my_read(struct file *f, char *buf, size_t size, loff_t *offset){\n\t\tint i,minor=iminor(f-\u003ef_path.dentry-\u003ed_inode);\n\t\tunsigned char val=0,num=Devices[minor].num;\n\t\t\n\t\tprintk(KERN_ALERT \"%s(%s%i)\\n\",__FUNCTION__,NAME(minor));\n\t\tfor(i=0;i \u003c size;i++){\n\t\t\tswitch(Devices[minor].type){\n\t\t\t\tcase REL:val= read_rel(num);break;\n\t\t\t\tcase OPT:val= read_opt(num);break;\n\t\t\t\tcase ANL:val= read_anl(num);break;\n\t\t\t\tdefault:break;\n\t\t\t}\n\t\t\tif(copy_to_user(buf+i, \u0026val, sizeof(val)))break;\n\t\t}\n\t\treturn i;\n\t}\n\t```\n\t\n* my_write : appel la fonction d'écriture associée au type de device (donc seulement write_rel puisque il n'y a que le relais qui supporte l'écriture)\n\t\n\t```\n\tssize_t my_write(struct file *f, const char *buf, size_t size, loff_t *offset){\n\t\tint i,minor=iminor(f-\u003ef_path.dentry-\u003ed_inode);\n\t\tchar val=0;\n\t\t\n\t\tfor(i=0;i \u003c size;i++){\n\t\t\tif(copy_from_user(\u0026val, buf+i, sizeof(val)))break;\n\t\t\tprintk(KERN_ALERT \"%s(%s%i,%i)\\n\",__FUNCTION__, NAME(minor),val);\n\t\t\tswitch(Devices[minor].type){\n\t\t\t\tcase REL:write_rel(Devices[minor].num,val?HIGH:LOW);break;\n\t\t\t\tdefault:break;\n\t\t\t}\n\t\t}\n\t\treturn i;\n\t}\n\t```\n\t\n* my_ioctl : permet de faire des action spéciales tel qu'un changement d'adresse.\n\t\n\t```\n\tlong my_ioctl(struct file *f, unsigned int cmd, unsigned long arg){\n\t\tint minor=iminor(f-\u003ef_path.dentry-\u003ed_inode);\n\t\tprintk(KERN_ALERT \"%s(%s%i,%i,%lu)\\n\",__FUNCTION__, NAME(minor),cmd,arg);\n\t\tswitch(cmd){\n\t\t\tcase SET_ADDR:set_addr(arg);break;\n\t\t\tdefault:break;\n\t\t}\n\t\treturn 0;\n\t}\n\t```\n\t\n* my_close : ne fait rien.\n\t\n\t```\n\tint my_close(struct inode *in, struct file *f){\n\t\tprintk(KERN_ALERT \"%s(%s%i)\\n\",__FUNCTION__,NAME(iminor(in)));\n\t\treturn 0;\n\t}\n\t```\n\t\n\n### [Partie driver (Module)](driver_remy.c)\n\nCette partie initialise et finalise le module\n\n* init_module : appel la fonction d'initialisation du pio, crée les cdev,les classes et les devices.\n\t\n\t```\n\tint init_module(void){\n\t\tint i;\n\t\tpio_start();\n\t\t\n\t\tif (alloc_chrdev_region(\u0026dev,0,COUNT(Devices),MY_NAME\"dev\") \u003c 0)\n\t\t\treturn printk(KERN_ALERT \"alloc_chrdev_region error\\n\");//cat /proc/devices\n\t\t\n\t\tmy_cdev = cdev_alloc();\n\t\tmy_fops=(struct file_operations){.owner = THIS_MODULE,\n\t\t\t.open = my_open,.release = my_close,.unlocked_ioctl = my_ioctl};\n\t\tmy_cdev-\u003eops = \u0026my_fops;\n\t\tcdev_add(my_cdev,dev,COUNT(Devices)); \n\t\tmy_class = class_create(THIS_MODULE , MY_NAME\"class\"); //ls /sys/class\n\t\t\n\t\tfor(i=0;i \u003c COUNT(Devices);i++)//ls /dev/\n\t\t\tdevice_create(my_class, NULL, MKDEV(MAJOR(dev), MINOR(dev)+i), NULL,\n\t\t\t\tMY_NAME\"%s%i\",DevType[Devices[i].type],Devices[i].num);\n\t\t\n\t\treturn 0;\n\t}\n\t```\n\t\n* cleanup_module : des-alloues les cdev,les classes et les devices et referme le PIO\n\t\n\t```\n\tvoid cleanup_module(void){\n\t\tint i;\n\t\tfor(i=0;i \u003c 12;i++)\n\t\t\tdevice_destroy(my_class, MKDEV(MAJOR(dev), MINOR(dev)+i));\n\t\tclass_destroy(my_class);\n\t\tunregister_chrdev_region(dev,COUNT(Devices));\n\t\tcdev_del(my_cdev);\n\t\tpio_stop();\n\t}\n\t```\n\n### [Programme de test](bench.c)\n\nCe programme de test permet de vérifier si le driver répond correctement aux requêtes d'accès de l'utilisateur.\nIl appel donc les différentes fonctions du driver (open/read/write/close/ioctl)\nIl permet aussi de lister les périphériques disponibles.\n\nLa fonction main utilise les arguments reçus en paramètres (argv)\npour savoir l'enchainement des opérations à effectuer.\nIl est donc possible de l'appeler de la sorte :\n```\n./bench open 1 read close open 2 read write 1 close\n```\n\nou bien des scripts plus complexes\n\n```\ndmesg -c \u003e /dev/null\ninsmod remy_driver.ko\ngcc bench.c -std=c99 -o bench\n\n# chenillard\n./bench \\\n\topen 0 write 1 close sleep 250\\\n\topen 0 write 0 close sleep 250\\\n\topen 1 write 1 close sleep 250\\\n\topen 2 write 1 close sleep 250\\\n\topen 3 write 1 close sleep 250\\\n\topen 0 write 0 close sleep 250\\\n\topen 1 write 0 close sleep 250\\\n\topen 2 write 0 close sleep 250\\\n\topen 3 write 0 close sleep 250\\\n\topen 0 write 1 close sleep 250\\\n\topen 1 write 1 close sleep 250\\\n\topen 2 write 1 close sleep 250\\\n\topen 3 write 1 close sleep 250\\\n\topen 0 write 0 close sleep 250\\\n\topen 1 write 0 close sleep 250\\\n\topen 2 write 0 close sleep 250\\\n\topen 3 write 0 close sleep 250\n\n#./bench \\\n#\topen 1 ioctl 0 42 close sleep 250\\\n#\topen 1 write 1 close sleep 250\\\n#\topen 1 write 0 close sleep 250\n\n\n\nrmmod remy_driver.ko\ndmesg\n```\n\nLe fichier [`bench.sh`](bench.sh) regroupe la compilation du [`bench.c`](bench.c) et son lancement.\n\nDocuments utilisés\n==================\n\n* http://linux-sunxi.org/Linux_Kernel\n* https://www.olimex.com/Products/Modules/IO/MOD-IO/open-source-hardware\n* https://github.com/allwinner-zh/documents/blob/master/A20/A20%20user%20manual%20v1.3%2020141010.pdf?raw=true\n* http://www.py6zgp.com/download/A20-GPIO.pdf\n* http://lwn.net/images/pdf/LDD3/ch09.pdf\n* https://www.olimex.com/Products/Modules/IO/MOD-IO/resources/MOD-IO.pdf\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyne%2Fmodio_driver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyne%2Fmodio_driver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyne%2Fmodio_driver/lists"}