{"id":438,"date":"2012-11-16T19:19:38","date_gmt":"2012-11-16T17:19:38","guid":{"rendered":"http:\/\/www.martoni.fr\/wordpress\/?p=438"},"modified":"2013-10-07T17:06:08","modified_gmt":"2013-10-07T15:06:08","slug":"genese-dun-pilote-linux-part3","status":"publish","type":"post","link":"https:\/\/www.fabienm.eu\/wordpress\/2012\/11\/16\/genese-dun-pilote-linux-part3\/","title":{"rendered":"Gen\u00e8se d&rsquo;un pilote Linux (Part3)"},"content":{"rendered":"<p>Nous voici dans l&rsquo;\u00e9criture proprement dite du driver. Comme expliqu\u00e9<br \/>\nauparavant, nous allons nous inspirer du driver du ds1374. La strat\u00e9gie<br \/>\nconsiste \u00e0 copier\/coller le code rtc-ds1374.c puis en modifier le code:<\/p>\n<pre><code>$ cp linux-2.6.38.8\/drivers\/rtc\/rtc-ds1374.c\r\nlinux-2.6.38.8\/drivers\/rtc\/rtc-mcp7940x.c <\/code><\/pre>\n<p>Puis chercher\/remplacer tous les <em>ds1374<\/em> par <em>mcp7940x<\/em> dans<br \/>\nle fichier rtc-mcp7940x.c nouvellement cr\u00e9\u00e9. Ce qui se fait dans vim par la<br \/>\ncommande <samp>:%s\/ds1374\/mcp7940x\/g<\/samp><\/p>\n<p>Une fois cela fait on va pouvoir commencer \u00e0 adapter notre driver.\u202f<\/p>\n<p>Notre chip mcp7940x est un composant qui se connecte sur le bus I\u00b2C pour<br \/>\n\u00abexporter\u00bb une interface d&rsquo;horloge nomm\u00e9 RTC. Le r\u00f4le du driver est donc de<br \/>\nfaire le liens entre le bus i\u00b2c et la RTC. On peut r\u00e9sumer cela visuellement<br \/>\navec l&rsquo;image suivante<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/www.martoni.fr\/documents\/images_articles\/dessin_driver.jpg\" alt=\"Dessin repr\u00e9sentant le driver du mcp79400\" \/><\/p>\n<p>On commence toujours la lecture d&rsquo;un driver par la fin, passons sur le nom<br \/>\nde l&rsquo;auteur et la licence. Et int\u00e9ressons nous \u00e0 la connexion au bus.<\/p>\n<p>On charge le driver en le connectant au bus i\u00b2c, pour se faire, on utilise<br \/>\nla structure <samp>i2c_driver<\/samp> que l&rsquo;on ajoute sur le bus au moment du<br \/>\nchargement du driver:<\/p>\n<pre><code>\r\nstatic int __init mcp7940x_init(void)\r\n{\r\n\treturn i2c_add_driver(&mcp7940x_driver);\r\n}\r\n<\/code><\/pre>\n<p>Structure que l&rsquo;on supprime au d\u00e9chargement du driver bien \u00e9videmment:<\/p>\n<pre><code>\r\nstatic void __exit mcp7940x_exit(void)\r\n{\r\n\ti2c_del_driver(&mcp7940x_driver);\r\n}\r\n<\/code><\/pre>\n<p>Cette structure d\u00e9clare un certain nombre de fonctions et de structures<br \/>\npropres au mcp7940x:<\/p>\n<pre><code>\r\nstatic struct i2c_driver mcp7940x_driver = {\r\n\t.driver = {\r\n\t\t.name = \"rtc-mcp7940x\",\r\n\t\t.owner = THIS_MODULE,\r\n\t},\r\n\t.probe = mcp7940x_probe,\r\n\t.suspend = mcp7940x_suspend,\r\n\t.resume = mcp7940x_resume,\r\n\t.remove = __devexit_p(mcp7940x_remove),\r\n\t.id_table = mcp7940x_id,\r\n};\r\n<\/code><\/pre>\n<p>Ce qui nous int\u00e9resse particuli\u00e8rement pour l&rsquo;instant c&rsquo;est le<br \/>\n<samp>probe<\/samp> et le <samp>driver.name<\/samp>. En effet c&rsquo;est tout<br \/>\nsimplement le point d&rsquo;entr\u00e9e de notre driver. Une fois le driver charg\u00e9 dans<br \/>\nle kernel, Linux observe le bus i\u00b2c et guette le nom des p\u00e9riph\u00e9riques (struct<br \/>\ndevices) que l&rsquo;on charge dessus. D\u00e8s qu&rsquo;un p\u00e9riph\u00e9riques avec le nom<br \/>\n\u00ab\u00a0rtc-mcp7940x\u00a0\u00bb se pointe, le kernel appel la fonction de probe<br \/>\n<samp>mcp7940x_probe<\/samp>.<\/p>\n<p>La structure de description du p\u00e9riph\u00e9rique pr\u00e9sent physiquement sur le bus<br \/>\nest quelques chose qui doit \u00eatre charg\u00e9 dans le noyau. En r\u00e8gle g\u00e9n\u00e9rale les<br \/>\np\u00e9riph\u00e9riques du bus i\u00b2c ne sont pas plug&#038;play et sont donc pr\u00e9sent sur la<br \/>\ncarte \u00e9lectronique d\u00e8s le d\u00e9marrage du kernel.<\/p>\n<p>C&rsquo;est pour cette raison qu&rsquo;on a l&rsquo;habitude de charger les devices dans le<br \/>\nfichier dit de \u00abplate-forme\u00bb. Dans le cas de l&rsquo;apf51dev, ce fichier de<br \/>\nplate-forme se nomme <samp>apf51dev-baseboard.c<\/samp> et se trouve dans le<br \/>\nr\u00e9pertoire <\/p>\n<pre><code>buildroot\/output\/build\/linux-2.6.38.8\/arch\/arm\/mach-mx5\/<\/code><\/pre>\n<p>Dans ce fichier, le device se d\u00e9clare simplement avec sont adresse sur le<br \/>\nbus, et son nom bien sur:<\/p>\n<pre><code>\r\nstatic struct i2c_board_info apf51dev_i2c2_devices[] __initdata = {\r\n\t{\r\n\t\tI2C_BOARD_INFO(\"mcp79400\", 0x6f),\r\n\t},\r\n};\r\n<\/code><\/pre>\n<p>Revenons \u00e0 notre probe <\/p>\n<pre><code> static int mcp7940x_probe(struct i2c_client *client,\r\n\t\t\tconst struct i2c_device_id *id)\r\n{\r\n\tstruct mcp7940x *mcp7940x;\r\n\tint ret;\r\n\r\n\tmcp7940x = kzalloc(sizeof(struct mcp7940x), GFP_KERNEL);\r\n\tif (!mcp7940x)\r\n\t\treturn -ENOMEM;\r\n\r\n<\/code><\/pre>\n<p>C&rsquo;est la fonction d&rsquo;initialisation de notre driver. C&rsquo;est dans cette<br \/>\nfonction que l&rsquo;on va d\u00e9marrer le mcp7940x et allouer la m\u00e9moire pour la<br \/>\nstructure mcp7940x. C&rsquo;est aussi dans cette fonction que l&rsquo;on va rattacher<br \/>\nnotre driver \u00e0 l&rsquo;interface RTC.<\/p>\n<p>L&rsquo;interface Linux pour la RTC est extr\u00eamement simpliste dans notre cas. En<br \/>\neffet on cherche juste \u00e0 lire l&rsquo;heure et la date dans le composant et \u00e0<br \/>\nl&rsquo;\u00e9crire (Le mcp7940x \u00e0 plein d&rsquo;autre fonctionnalit\u00e9s mais mon besoin n&rsquo;est<br \/>\nque l&rsquo;heure). Nous n&rsquo;aurons donc que les fonctions <samp>read_time<\/samp> et<br \/>\n<samp>set_time<\/samp> \u00e0 \u00e9crire dans la structure <a href=\"http:\/\/lxr.free-electrons.com\/source\/include\/linux\/rtc.h?a=blackfin#L146\">rtc_class_ops<\/a><br \/>\ndu driver:<\/p>\n<pre><code>\r\nstatic const struct rtc_class_ops mcp7940x_rtc_ops = {\r\n\t.read_time = mcp7940x_read_time,\r\n\t.set_time = mcp7940x_set_time,\r\n};\r\n<\/code><\/pre>\n<p>Structure que l&rsquo;on enregistre \u00e0 la fin de la fonction probe:<\/p>\n<pre><code>\r\n\tmcp7940x->rtc = rtc_device_register(client->name, &client->dev,\r\n\t                                  &mcp7940x_rtc_ops, THIS_MODULE);\r\n\tif (IS_ERR(mcp7940x->rtc)) {\r\n\t\tret = PTR_ERR(mcp7940x->rtc);\r\n\t\tdev_err(&client->dev, \"unable to register the class device\\n\");\r\n\t\tgoto out_free;\r\n\t}\r\n<\/code><\/pre>\n<p>L&rsquo;\u00e9criture de la fonction <samp>read_time<\/samp> se r\u00e9sume ensuite \u00e0 lire<br \/>\nles valeurs des registres du composant au moyen de la fonction<br \/>\n<samp>i2c_smbus_read_byte_data()<\/samp> et de renseigner la structure <a href=\"http:\/\/lxr.free-electrons.com\/source\/include\/linux\/rtc.h?a=blackfin#L20\"><br \/>\nrtc_time<\/a> pass\u00e9e en param\u00e8tre.<\/p>\n<p>Et pour la fonction <samp>set_time<\/samp> on fait l&rsquo;inverse. On r\u00e9cup\u00e8re<br \/>\nles valeurs se trouvant dans la structure <a href=\"http:\/\/lxr.free-electrons.com\/source\/include\/linux\/rtc.h?a=blackfin#L20\">rtc_time<\/a><br \/>\npass\u00e9e en param\u00e8tre et on \u00e9crit les valeurs dans le composant au moyen de la<br \/>\nfonction <samp>i2c_smbus_write_byte_data()<\/samp>.<\/p>\n<h3>Et voila !<\/h3>\n<p>En r\u00e9alit\u00e9 le driver n&rsquo;est que partiellement \u00e9crit vu que le composant<br \/>\ndispose de bien plus de fonctionnalit\u00e9s que simplement lire\/\u00e9crire l&rsquo;heure.<br \/>\nLe driver pourrait notament \u00eatre \u00e9tendu pour g\u00e9rer les deux alarmes, la<br \/>\ngestion du signal de clock de sortie et surtout la SRAM, qui permet de stocker<br \/>\ndes variables quand l&rsquo;appareil est \u00e9teint.<\/p>\n<p>Et non \u00e7a n&rsquo;est pas termin\u00e9 ! Il reste encore \u00e0 publier ce driver pour<br \/>\narmadeus, et plus (mainline kernel) si affinit\u00e9 !<\/p>\n<h3>Maintenant publions le tout<\/h3>\n<p>Maintenant que nous avons \u00e9crit notre driver, l&rsquo;objectif va \u00eatre de le publier sur le git Armadeus. Pour cela il faut que nous finalisions le patch que nous avions commenc\u00e9 dans la partie 2 de cet article. Si tout a bien \u00e9t\u00e9 fait dans la partie 2 il n&rsquo;y a plus qu&rsquo;\u00e0 rafraichir le patch quilt:<\/p>\n<pre><code>$ cd buildroot\/output\/build\/linux-2.6.38.8\/\r\n$ quilt refresh\r\nRefreshed patch 450-armadeus-add_mcp7940x_rtc_driver.patch\r\n<\/pre>\n<p><\/code><\/p>\n<p>Puis modifier l'ent\u00eate du patch pour y mettre son nom (pour qu'on connaisse le coupable ;):<\/p>\n<pre><code>\r\n$ vim patches\/450-armadeus-add_mcp7940x_rtc_driver.patch\r\n<\/pre>\n<p><\/code><\/p>\n<p>et <\/p>\n<pre><code>\r\nAdd mcp79400 Linux driver\r\n\r\nSigned-off-by: Fabien Marteau <fabien.marteau@armadeus.com>\r\n\r\n---\r\n\r\nIndex: linux-2.6.38.8\/drivers\/rtc\/Kconfig\r\n...\r\n<\/pre>\n<p><\/code><\/p>\n<p>Notre patch est pr\u00eat, on peut maintenant le proposer \u00e0 la communaut\u00e9 Armadeus en le postant sur la liste de diffusion<br \/>\n<a href=\"https:\/\/sourceforge.net\/mailarchive\/forum.php?forum_name=armadeus-forum\">armadeus-forum@lists.sourceforge.net<\/a>.<\/p>\n<p>Bon j'avoue, vu que j'ai les droits sur le git je l'ai commit\u00e9 directement \ud83d\ude09 Mais, pour ceux qui n'ont pas les droits, c'est la proc\u00e9dure qu'il faudrait respecter. Du coup pour voir le code complet du driver c'est par <a href=\"http:\/\/armadeus.git.sourceforge.net\/git\/gitweb.cgi?p=armadeus\/armadeus;a=blob_plain;f=patches\/linux\/2.6.38\/450-armadeus-add_mcp7940x_rtc_driver.patch;hb=HEAD\">l\u00e0<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Nous voici dans l&rsquo;\u00e9criture proprement dite du driver. Comme expliqu\u00e9 auparavant, nous allons nous inspirer du driver du ds1374. La strat\u00e9gie consiste \u00e0 copier\/coller le code rtc-ds1374.c puis en modifier le code: $ cp linux-2.6.38.8\/drivers\/rtc\/rtc-ds1374.c linux-2.6.38.8\/drivers\/rtc\/rtc-mcp7940x.c Puis chercher\/remplacer tous les &hellip; <a href=\"https:\/\/www.fabienm.eu\/wordpress\/2012\/11\/16\/genese-dun-pilote-linux-part3\/\">Continuer la lecture <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[106,10,123],"tags":[],"class_list":["post-438","post","type-post","status-publish","format-standard","hentry","category-embarque","category-informatique","category-kernel-programmation"],"_links":{"self":[{"href":"https:\/\/www.fabienm.eu\/wordpress\/wp-json\/wp\/v2\/posts\/438","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.fabienm.eu\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fabienm.eu\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fabienm.eu\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fabienm.eu\/wordpress\/wp-json\/wp\/v2\/comments?post=438"}],"version-history":[{"count":14,"href":"https:\/\/www.fabienm.eu\/wordpress\/wp-json\/wp\/v2\/posts\/438\/revisions"}],"predecessor-version":[{"id":582,"href":"https:\/\/www.fabienm.eu\/wordpress\/wp-json\/wp\/v2\/posts\/438\/revisions\/582"}],"wp:attachment":[{"href":"https:\/\/www.fabienm.eu\/wordpress\/wp-json\/wp\/v2\/media?parent=438"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fabienm.eu\/wordpress\/wp-json\/wp\/v2\/categories?post=438"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fabienm.eu\/wordpress\/wp-json\/wp\/v2\/tags?post=438"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}