Device Driver in Linux - Ver 2.4.3
Device Driver in Linux - Ver 2.4.3
بهار ۱۳۹۲
صفحه1
سرفصل
فصل اول :آشنایی با درایور 3.............................................................................................................................................
فصل دوم :نوشتن اولین دیوایس درایور 7........................................................................................................................
فصل سوم :امکانا ت اضافی که cبرای نوشتن دیوایس درایور در لینوکس به ما میدهد13.....................................
فصل چهارم :درایورهای کاراکتری در لینوکس 18.........................................................................................................
فصل پنجم :ساختن فایل دستگاههای کاراکتری 24.........................................................................................................
فصل ششم :خواندن و نوشتن بروی فایل دستگاههای کاراکتری 31............................................................................
فصل هفتم :دسترسی به سخت افزار در لینوکس 36......................................................................................................
فصل هشتم :دسترسی به پور ت ورودی/خروجی خاص در معماری 44............................................................ x86
فصل نهم :کنترل ورودی/خروجی در لینوکس 49..........................................................................................................
فصل دهم :دیباگ کردن کرنل در لینوکس 64...............................................................................................................
فصل یازدهم :درایورهای USBدرلینوکس-قسمت اول 68...........................................................................................
لدوازدهم:درایورهای USBدرلینوکس-قسمت دوم 76...........................................................................................
فص د
لسیزدهم:انتقال اطلعا ت از/به دستگاه 84..................................................................................................... USB
فص د
لچهاردهم:آشنایی با مبانی دیسک سخت و پارتیشن 92..........................................................................................
فص د
لپانزدهم:ساخت یک دیسک در حافظه 100............................................................................................... RAM
فص د
لشانزدهم:کاوش در کرنل و آشنایی با 126............................................................................................... proc/
فص د
لهفدهم:چگونگی تعامل بین ماژولها 132....................................................................................................................
فص د
صفحه2
فصل اول :آشنایی با درایور
مقدمه
همانگونه که یک راننده ،اتومبیل خود را میراند و کنترل میکند ،یک دیوایس درایور نیز همین کار را بببا
قسمتی از سخت افزار )مانند ماوس،صفحه کلید ،وب کم و (...انجام میدهببد.هببر قسببمت از یببک سببخت افببزار
میتواند با قطعه کدی که به آن »دیوایس درایور« گویندکنترل شود ویا حتی یک سببخت افببزار میتوانببد توسببط
سخت افزار دیگری که با دیوایس درایور کنترل میشود ،کنترل شود.
همچنین هر دستگاه یک کنترل گر دارد )مانند کنترلرهارد ،کنترلر صببفحه نمببایش و (...کببه بببه آن »دیببوایس
(IDE,PCI,USBتعامببل را هببا)ماننببد SPI,L2C کنترلر« گویند که این دیوایس کنترلرها با استفاده از »باس درایور«
ایجاد میکنند.
کنترلرها معمول با استفاده از BUSمخصوص شان )مثل PCI BUSیا IDE BUSو (...به CPUمتصل
میشوند.امروزه و در دنیای دستگاههای embededمیتوان گفت که به علت کاهش هزینه و کوچک سازی،
BUSها با CPUها در یک تراشه مجتمع شده اند .البته در صورتی کببه درایببور ببباس بببه گببونه ای خبباص
طراحی نشده باشد ،ما نیازی نداریم که برای این نوع دستگاهها درایور های خود را تغییر دهیم.
صفحه3
مبندی در پایین لیه نرم افزاری یک سیستم عامل دیده میشوند .در این
افزارها را فراهم میکنند.و از لحاظ تقسی د
تقسیم بندی بالی باس درایورها ،دیوایس درایورها قرار میگیرند.
صفحه4
حوزه عمل یک درایور
یک دیوایس درایور در لینوکس ،یک System callرا برای کاربر مهیا میکندSystem call.
مرزی بین فضای کرنل و فضای کاربر میباشد
.
دربخش سیستم عامل،یک درایور به یکی از سه حوزه زیر میتواند تعلق بگیرد:
-۱حوزه شبکه یا پکت گرا
-۲حوزه رسانه های ذخیره سازی یا بل ک گرا
-۳حوزه کاراکتر یا بایت گرا
لینوکس،مانند تمام سیستم عامل ها ۵کار مدیریتی را انجام میدهد .ایببن ۵کببار عبارتنببد از مببدیریت
پردازش ،مدیریت حافظه ،مدیریت شبکه ،مدیریت رسانه های ذخیره کننده اطلعا ت و در نهایت مببدیریت
سخت افزارهای ورودی و خروجی.اگرچه میتوان پردازش و حافظه را نیز بعنوان دیوایس درایور در نظر گرفت،
لیکن بدلیل نسبی بودن آنها و دلیلی دیگر آنها را بصور ت مجزا در نظر میگیریم.
یک سیستم عامل یا بصور ت یکپارچه و غیر قابل تغییر اجرا میگردد یببا اینکببه یببک حببداقلی را دارد و
میتوان آنرا حین اجرا قبض و بسط داد.به هر ترتیب اضافه کردن کد در حین اجببرا ،در دو حببوزه پببردازش و
یهای امروزی به مراتب سخت و پیچیده میباشد.بنابراین اضافه و حذف کببردن
حافظه برای CPUها و معمار د
کد درحین اجرا برای این دو حوزه برعکس سه حوزه دیگر ،تقریبا غیر ممکن است.پببس وقببتی در آینببده مببا از
صفحه5
دیوایس درایور میگوییم ،منظور فقط سه حوزه دیگر است.
حال میخواهیم عمیق تر وارد این سه حوزه شویم.
حوزه شبکه به دو قسمت -۱درایور پروتکل استک شبکه و -۲درایور کار ت شبکه ) (NICکه منظببور
همان Wi-Fiو Ethernetاست تقسیم میشود.
حوزه رسانه های ذخیره ساز نیز به دو قسمت -۱درایور فایل سیستم برای بکارگیری فرمتهای مختلف
پارتیشنها و -۲درایور بل ک برای پروتکلهای ذخیره سازی مانند IDEو SCSIMTDتقسیم میگردد.
صفحه6
فصل دوم :نوشتن اولین دیوایس درایور
درلینوکس برخلف بسیاری از سیستم عاملهای موجود ،بببا اضببافه یببا حببذف کببردن یببک درایببور بببه
معامل میتوانببد از درایببور بصببور ت معامل ،احتیاجی به راه اندازی مجدد 1سیست د
معامل نمیباشببد و سیسببت د سیست د
بلدرنگ تاثیر بپذیرد .اصطلحا ًا به این روش dynamic loading and unloadingو به این
درایورها »ماژول« گفته میشود که این ماژولها در فایلهای مخصوصی با پسوند 2*.koقرار میگیرند.
معامل لینوکسی بصور ت استاندارد یک جایی برای تمامی ماژولها در نظر گفته شببده اسببت و آنهببا
در هر سیست د
مشببببابه سبببباختار سببببورس کرنببببل لینببببوکس سببببازماندهی شببببده انببببد.شبببباخه مببببورد نظببببر
/lib/modules/<kernel_version>/kernelمیباشد که > <kernel_versionنسببخه کرنببل
میباشد و از خروجی دستور uname -rمیتوان آنرا بدست آورد.یک نمونه از آن را در شکل زیر مشبباهده
میکنید.
reboot 1
Kernel object 2
صفحه7
معامل از دستورا ت زیرکه در شبباخه /sbinقراردارنببد اسببتفاده
برای اضافه و حذف کردن درایو ها به سیست د
معامل اضافه و حذف شود.
میشود.نکته مهم این است که یک درایور باید حتما ًا با یوزر rootبه سیست د
صفحه8
در مثال فوق ماژول vfatبببه مبباژول fatوابسببته میباشببد.بنببابراین بایببد نخسببت fat.koرا لببود
نماییم.جهت اتوماتیک کردن فرآیند unzipو لود کردن ماژول بببا تمببام وابسببتگیهای آن میتببوان از دسببتور
modprobاستفاده نمود.دقت نمایید که دستور modprobاحتیاج به مشخص کردن پسوند .koندارد
معامل
لها از سیسببت د
و نباید نام ماژول را با پسوند آن مشخص نمود.در مثال فوق در نهایت ما برای حذف ماژو د
از دستور rmmodاستفاده کردیم.
library 3
صفحه9
مانند کرنل کامپایل شود و هدرفایلهای مورد نیباز بایبد از مسبیر سبورس کرنبل معرفبی شببوند نبه از مسبیر
. /usr/include
بگر است.تابع سببازنده
هر درایور همانند برنامه نویسی شی گرا داری یک تابع سازنده و یک تابع تخری د
بگر موقعی که درایور بببا
در موقعی که درایور با موفقیت به کرنل اضافه شد اجرا میگردد و برعکس تابع تخری د
موفقیت از کرنل حذف گردید)با استفاده از دستور (rmmodاجرا میگردد .این دو تابع مانند دو تابع معمولی
میباشند با این تفاو ت که این توابع با استفاده از ماکروهای )( module_initو )( module_exitکببه در
هدرفایل کرنل module.hموجود است تعریف میشوند.
حال بیایید به کد اولین درایور نگاهی بیاندازیم:
;)module_init(ofd_init
;)module_exit(ofd_exit
;)"MODULE_LICENSE("GPL
;)">MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com
;)"MODULE_DESCRIPTION("Our First Driver
صفحه10
برنامه فوق اولین درایور میباشد و البته یک درایور کامل است.نام این برنببامه را ofd.cمیگببذاریم.در
نظر داشته باشیدکه برخلف برنامدههای معمول cدر درایورها ،خبری از هدرفایلهایی ماننببد stdio.hنیسببت
بدین علت که این هدرفایلها در فضای یوزر میباشند .بجای آنها از هدر فایلهای فضای کرنل مانند kernel.h
استفاده مگردد و به همین دلیل است که )( printkجایگزین )( printfشده است.
هدر فایل version.hجهت سازگاری نسخه درایور با نسخه کرنلببی اسببت کببه آن درایببور را لببود
میکند و همچنین ماکروهای *_ MODULEنیز اطلعا ت درایور را مشخص میکند و مشابه امضاء برای درایور
است.
صفحه11
الن فایل ofd.cو Makefileآماده است و تنها چیزی که برای ساختن درایور نیاز داریببم صببدور
دستور makeد میباشد.خروجی فرمان makeچیزی شبیه زیر خواهد بود:
$ make
make -C /usr/src/linux SUBDIRS=... modules
'make[1]: Entering directory `/usr/src/linux
CC [M] .../ofd.o
Building modules, stage 2.
MODPOST 1 modules
CC .../ofd.mod.o
LD [M] .../ofd.ko
'make[1]: Leaving directory `/usr/src/linux
اگر همه چیز به خوبی پیش رود ،فایل ofd.koساخته خواهد شد و میتوانیم با دسترسببی rootو یببا
استفاده از sudoکارهای معمول زیر را بروی این درایور انجام دهیم:
# su
# insmod ofd.ko
# lsmod | head -10
حال میتوانیم با استفاده از دستور آخر مشاهده کنیم که درایور ما به درستی به کرنل اضافه شده است.
صفحه12
فصل سوم :امکانا ت اضافی که cبرای نوشتن دیوایس
درایور در لینوکس به ما میدهد
در فصل قبل دیدیم که در برنامه نوشته شده بجای تابع )( printfاز تببابع )( printkاسببتفاده شببده
است ولی برخلف انتظار در خروجی نوشتدهای را مشاهده نکردیم.برای دیدن نوشته ی این تببابع بعببد از اضببافه
شدن به سیستم عامل میبایست دستور dmesg |tailرا صادر کنیم .اینکه چببرا نوشببتدهها بجببای کنسببول
معمولی از طریق این دستور میبایست مشاهده شود را در ادامه توضیح خواهیم داد.
deamon 4
صفحه13
همانگونه که مشاهده میکنید بین متن اصلی و KERN_INFOهیچ کاراکتری از قبیل کاما وجود ندارد .بدین
معنی که این ماکرو ،رشتدهای دیگببر یببا یببک آرگومببان مجببزا نیسببت.در سببورس کرنببل لینببوکس و در فایببل
linux/kernel.hهشت نوع دیگر از این ماکروها تعریف شده اند:
syslogپیامهببای
د حال بسته به اینکه ما از کدام یک از ماکروهای فوق استفاده کرده باشببیم ،دیمببان
یکند.یک مقصد برای هر کدام از ایببن
نها به جاهایی که تعریف شده است منتقل م د
دریافتی را بر اساس نوع آ د
شفببرض تمببام ماکروهببا
یتواند فایل /var/log/messagesباشد.از آنجایی که ایببن فایببل پی د
ماکروها م د
یشود .اگرچه شما میتوانید با تغییر تنظیما ت ،خروجی را
میباشد تمام خروجی printkنیز به آن فایل ریخته م د
ل (/ dev/ttys0یا حتی به تمام کنسولها )پیش فرض لینببوکس در موقببع بببروز خطببای
به پور ت سریال )مث ا ً
(KERN_EMERGمنتقل کنید.
از آنجاییکه تمام پیامهای نوشته شده در فایل /var/log/messagesفقط شامل پیامهای کرنل نمیباشببد
و تمام پیامها )پیامهای چندین دیمان سطح یوزر( را شامل میگردد و همچنین این فایل معموا ًل بببرای یوزرهببای
معمولی قابل خواندن نیست بنابراین یک ابزار سطح کاربر بنام dmesgد تهیه گردیده است تا فقببط پیامهببای
کرنل را تفسیر و بروی خروجی استاندارد نمایش دهد.شکل زیر طرز کار این ابزار را نشان میدهد:
صفحه14
ابزار خاص GCCبرای برنامه نویسی سطح کرنل
در این قسمت لزم به ذکر است که __initو __exitکلمببا ت کلیببدی زبببان Cنیسببتند.در حقیقببت
کرنل لینوکس تنها با استفاده از زبان Cو همچنین ابزارهای جانبی کامپایلر زبان Cیا همان GCCنوشته شده
است .و ماکروهای __initو __exitتنها دونمونه از این ابزارهای جانبی GCCهسببتند.ایببن دو مبباکرو هیببچ
ارتباطی با لود کردن درایور درکرنل و بصور ت داینامیک ندارد بلکه هر زمان که بخببواهیم کببدی بببرای کرنببل
نبویسیم از این دو ماکرو استفاده خواهیم کرد.تمام توابعی که با __initمار ک دار شده باشند در موقع کامپایل ِ
کرنل ،بصور ت اتوماتیک توسط کامپایلر GCCدر بخش initمربوط به kernel imageقرار میگیرند و
هر تابعی که با استفاده از مبباکروی __exitمببار ک دار شببده باشببد در بخببش exitمربببوط بببه kernel-
imageقرار خواهد گرفت.
نهببا
سؤالی که ممکن است مطرح شود این است که چرا به چنین ماکروهایی نیاز داریم؟ و خاصببیت آ د
چیست؟ جواب این است که تمام توابع علمت گذاری شده توسط initبب قرار است فقط یکبار و آنهببم موقببع
بال آمدن کرنل اجرا گردد و هرگز تا راه اندازی مجدد کرنل دوباره اجرا نخواهند شد.و بعداز اجببرا شببدن ایببن
توابع با آزاد شدن حافظه مخصوص ، initاین قسمت از حافظه توسط کرنل آزاد خواهد شد.و بصور ت مشببابه
صفحه15
توابعی که با __ exitعلمت گذاری شده باشند در موقع خاموش شدن سیستم اجرا خواهند گردید.
سؤال بعدی این است که سیستم بهرحال خاموش خواهد شد و چرا ما باید حافظه ای را به exitبب در
تمام مدتی که کرنل در حال اجرا است اختصاص دهیم؟ پاسخ این است که این توابع تا لحظه خبباموش شببدن
قالعاده حافظه توسط لینوکس است.
ل در حافظه وجود ندارند! این یک بهینه سازی خار د
کرنل اص ا ً
مورد فوق تنها یکی از نموندههای همکاری متقابل و زیبای بین کرنل و GCCاسببت کببه بهینببه سببازی
منابع را در بر خواهد داشت.و در آینده از این فو ت و فن ها نیز بیشتر خواهیم آموخت.برای همیببن اسببت کببه
یشببود .بببه هرترتیببب GCCو
نها GCCاست ،کامپایببل م د
کرنل لینوکس فقط توسط کامپایلرهایی که پایه آ د
کرنل به هم گره خورده اند.
صفحه16
استاندارد ،ANSIجزیی از Cخالص نیستند.
پس چگونه توسعه دهندگان کرنل از Cو بدون این کتابخاندهها استفاده میکنند؟ و آیببا برنببامه نویسببی
برای کرنل بدون این کتابخاندهها بسیار طاقت فرسا نیست؟ جواب این سؤال منفی اسببت .بببدلیل اینکببه توسببعه
دهندگان کرنل خودشان توابع و کتابخاندههای مورد نیازشان را از قبل ساختدهاند و این توابع نیز جزیی از کرنببل
نها است و مشابه آن توابعی نیز برای کار با رشتدهها ،حببافظه و …
یشود که printkنیز یکی از آ د
محسوب م د
نیز نوشته شده و جزیی از کرنل میباشد که این توابببع در جنببدین شبباخه libو ipcو kernelو نظببایر آن
نها نیز در شاخه /include/linuxمیباشد .به همین دلیل است که مببا
قرار دارند.همچنین هدر فایلهای آ د
برای نوشتن برنامه کرنل به سورس کرنل یا حداقل هدر فایلهای کرنل نیازمندیم .از آنجاییکه سورس کرنببل و
هدر فایلها ،هر دو میتوانند مورد استفاده قرار بگیرند ،بنابراین در لینوکس نیز برای نصب هر کدام بسته هببایی
ل اگر شما فدورا داریببد و بسببته سببورس کرنببل را نصببب کرددهایببد بایببد در مسببیر
متفاو ت تهیه شده است.مث ا ً
/ usr/src/kernels/<kernel-versionبتوانید آنهارا بیابید ولی اگر فقط بسته هدر فایلهای کرنل را
ب فقط هدر فایلهببا شببما
نها در مسیر /usr/src/linuxبیابید.در فدورا و برای نص ِ
نصب کرده باشید باید آ د
کافی است که دستور yum install kernel-develرا صادر کنید .اگر شما از mandrivaاستفاده
میکنید و میخواهید سورس کامل را نصب کنید کافی است که دستور urpmi kernel-sourceرا صادر
کنید و همچنین برای نصببب سببورس کرنببل در ubuntuکببافی اسببت کببه دسببتور sudo apt-get
install linux-sourceرا تایپ نمایید.
صفحه17
فصل چهارم :درایورهای کاراکتری در لینوکس
صفحه18
تافزار میسر میکنند.اگرچه برنامه یکسری عملیا ت فایلی را انجام میدهد ولببی ممکببن اسببت نتایببج آن بببا
سخ د
تافزار مورد نظر میباشببند.بطببور
نها تنها یک راه برای تعامل با سخ د
عملیا ت فایلی واقعی یکی نباشد بلکه فقط آ د
مثال اگر در یک فایل کاراکتری ابتدا مقداری را بنویسببیم سببپس بخببواهیم همیببن مقببادیر را بخببوانیم ،لزومبا ًا
خروجی ،با آن چیزی که ما از یک فایل معمولی انتظار داریم یکی نخواهد بود.بعنوان یک مثببال عملببی میتببوان
ل دستگا ِه صوتی بنویسیم ،بجای اینکه واقعا ًا در آن فایل ذخیره گردد ،منجر
گفت که اگر مقداری را در یک فای ِ
به تولید صدا از بلندگوی سیستم خواهد شد.و اگر بخواهیم صدایی را از میکروفن دستگاه دریافت کنیم بایببد از
این فایل بخوانیم .بدیهی است که داددههای نوشته شده با داددههای خوانده شده از این فایل ،ارتباطی بببا یکببدیگر
ندارند.
تافزار به چهار عنصببر -۱برنببامه -۲
بنابراین برای ارتباط کامل بین یک برنامه در فضای یوزر و سخ د
تافزار .نیازمندیم.
ل رابط -۳درایو ِر دستگاه -۴سخ د
فای ِ
نکته جالب توجه این است که هر یک از چهار عنصر فوق میتواند بروی یک کامپیوتر مجزا باشببد بببدون اینکببه
سایر عناصر در آن کامپیوتر وجود داشته باشند ولی باید به نحوی این چهار عنصر صراحتا ًا با هم متصل باشند.
ط دستگاه ) با استفاده از (system callبا آن فایل
ل راب ِ
یک برنامه ابتدا با درخواست باز کردن فای ِ
ل رابط به درایور مرتبطی کببه در کرنبل لبود شببده اسبت )از قبببل رجیسببتر
ارتباط برقرار میکند سپس آن فای ِ
تافزا ِر کباراکتری مبورد نظبر
یشود و در نهایت آن درایور ،عملیا ت سطح پایین را بروی سبخ د
گردیده( وصل م د
تافزار و برنامه برقرار میگردد .نکته قابل ذکر این اسببت
انجام میدهد .با این توضیح یک ارتباط کامل بین سخ د
تافزار و درایور میباشد.
که فایل رابط ،یک فایل واقعی نیست و تنها یک رابط بین سخ د
صفحه19
”$ ls -l /dev/ |grep “^c
>#include <linux/types.h
>#include <linux/kdev_t.h
>#include <linux/fs.h
static dev_t first; // Global variable for the first device number
صفحه20
: مینویسیمconstructor که ما برای
: مینویسیمdestructor و برای
unregister_chrdev_region(first, 3);
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
static dev_t first; // Global variable for the first device number
21صفحه
{
;)unregister_chrdev_region(first, 3
;)"printk(KERN_INFO "Alvida: ofcd unregistered
}
;)module_init(ofcd_init
;)module_exit(ofcd_exit
;)"MODULE_LICENSE("GPL
;)">MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com
;)"MODULE_DESCRIPTION("Our First Character Driver
خب پس از اینکه کد فوق را ذخیره کردیم میتوانیم کارهای معمولی که بروی هر درایور انجام میدهیم را اجرا
کنیم:
• ساخت درایور )یا همان فایل (.koبوسیله اجرای دستور make
• لود کردن درایور با استفاده از دستور insmod
• مشاهده درایور لود شده با استفاده از دستور lsmod
• حذف درایور از کرنل با استفاده از دستور rmmod
قبل از حذف درایور از کرنل ،برای دیدن شماردهی اصلی ثبت شده که با کلمه shwetaشببروع شببده اسببت
میتوانیم با دستور cat /proc/devicesنگاهی به آن داشته باشیم.میتوان دید که آن فایل واقع با ًا وجببود
دارد ولی ما نمیتوانیم فایل دستگاهی را در / devبا آن شماردهی اصلی بیابیم .البته میتوانیم با استفاده از دستور
mknodبصور ت دستی آنرا بسازیم و سعی کنیم در آن بنویسیم و یا از آن بخببوانیم .بببه شببکل زیببر تببوجه
فرمایید:
صفحه22
نکته ایجاست که شماره ۲۵۰میتواند در سیستم شما متفاو ت باشد.شکل فوق همچنین غیببر موفببق بببودن کببار
خواندن و نوشتن بروی این فایل را نیز نشان میدهد.بیاد بیاورد که گام دوم ،ارتببباط عملیببا ت فایببل بببا توابببع
درایور بود که ما در اینجا هنوز انجام نداددهایم .بنابراین هنوز به اطلعا ت بیشتری نیاز داریم که در فصببل بعببد
به آن خواهیم پرداخت.
صفحه23
فصل پنجم :ساختن فایل دستگاههای کاراکتری
در قسمت قبل دیدیم که فقط ثبت زوج شماردههای اصلی و فرعی برای کار با فایلهای دستگاه کاراکتری
کافی نیست و فایل آن در شاخه / devساخته نخواهد شببد و همببانگونه کببه دیببدیم اینکببار را بببا اسببتفاده از
mknodeو بصور ت دستی انجام دادیم.بنابراین در این فصل ما قصد داریم کببه بببا اسببتفاده از udevdv
بصور ت اتوماتیک اینکار را انجام دهیم و همچنین دیوایس درایور را با این فایل ارتباط خببواهیم داد و عملیببا ت
بروی این فایل را با توابع موجود در دیوایس درایور مرتبط خواهیم نمود.
dev_tبا > < major,minorمرتبط است و برای آزادسببازی و تخریببب آن نیببز بصببور ت معکببوس بایببد
بصور ت زیر انجام شود:
;)device_destroy(cl, first
صفحه24
;)class_destroy(cl
شکل زیر ورودی ایجاد شده در /sysتوسببط chardrvبعنببوان یببک > <device class nameو
ل دسببتگا ِه سبباخته
mynullرا بعنوان یک > <device name formatمشاهده میکنیم .همچنین فای ِ
شده در /devرا با استفاده از udevمیتوانیم مشاهده کنیم.
نکته جالب توجه این است که اگر ما چندین شماره minorداشته باشیم APIهببای )(device_create
و )( device_destroyرا با استفاده از > <device name formatمیتوانیم در یک حلقه forقرار
دهیم.بعنوان مثال )( device_createرا با ایندکس iرا میتوانیم به شکل زیر فراخوانی کنیم:
صفحه25
ل دستگاه
عملیات بروی فای ِ
تمام عملیا ت و سیستم کالهایی که جهت کار با فایل معمولی استفاده میگردد،برای فایلهببای دسببتگاه نیببز
نهببا در کرنببل
قابل استفاده میباشد.در لینوکس اغلب موارد از دیدکاربر تنها یک فایل میباشد و تنها تفبباو ت آ د
میباشد که از دید کاربر پنهان است.فایل سیستم مجازی VFSلینوکس انواع فایلها را تشخیص داده و عملیا ت
ل بببرای شبباخدهها و فایلهببای
فایل مرتبط با هر نوع فایل را به کانال مخصوص به آن فایببل ارجبباع میدهببد .مث ا ً
معمولی به ماژولهای فایل سیستم ارجاع میدهد و برای فایلهای دستگاه کاراکتری به دیوایس درایور مرتبببط بببا
آن ارجاع میدهد.که ما در این اینجا فقط در مورد مکانیزم فایلهای کاراکتری بحث میکنیم.
پس ما باید در مورد VFSکه وظیفه دارد هر عملیا ت فایل را به درایببور مربببوطه منتقببل کنببد ،بیشببتر
بدانیم.کاربا VFSدو گام دارد )کدهای داخل پرانتز برای درایور »تهی« میباشد که بعدا ًا توضیح خواهیم داد(:
گام اول ،پرکردن استراکچر عملیا ت فایل ) (struct file_operaton pugs_fopsبا تببوابعی کببه
ببببببببببرای آن عملیبببببببببا ت دلخبببببببببواه نوشبببببببببته ایبببببببببم )تبببببببببوابعی ماننبببببببببد
(...,my_open,my_close,my_read,my_writeو مقدار دهی اولیه اسببتراکچر فایببل کبباراکتری )
(struct cdev c_devبا استفاده از تابع )(. cdev_init
گام دوم معرفی این ساختار به VFSمیباشدکه با استفاده از )( cdev_addانجام میگیرد.لزم به ذکر اسببت
که )( cdev_initو )( cdev_addهر دو در هدر فایل > <linux/cdev.hتعریف شده اند.
برای شروع تا جاییکه ممکن است کار را ساده میگیریم .بنابراین میخواهیم در ابتدای کار درایور »تهی« ) (null
را بنویسیم.
درایور تهی
اولین درایورکامل کاراکتری خود را بصور ت زیر مینویسیم و اسم آنرا ofcd.cمیگذاریم:
>#include <linux/module.h
>#include <linux/version.h
>#include <linux/kernel.h
>#include <linux/types.h
>#include <linux/kdev_t.h
>#include <linux/fs.h
>#include <linux/device.h
>#include <linux/cdev.h
صفحه26
static dev_t first; // Global variable for the first device number
static struct cdev c_dev; // Global variable for the character device structure
static struct class *cl; // Global variable for the device class
static int my_open(struct inode *i, struct file *f)
{
printk(KERN_INFO "Driver: open()\n");
return 0;
}
static int my_close(struct inode *i, struct file *f)
{
printk(KERN_INFO "Driver: close()\n");
return 0;
}
static ssize_t my_read(struct file *f, char __user *buf, size_t
len, loff_t *off)
{
printk(KERN_INFO "Driver: read()\n");
return 0;
}
static ssize_t my_write(struct file *f, const char __user *buf,
size_t len, loff_t *off)
{
printk(KERN_INFO "Driver: write()\n");
return len;
}
static struct file_operations pugs_fops =
{
.owner = THIS_MODULE,
.open = my_open,
.release = my_close,
.read = my_read,
.write = my_write
};
27صفحه
static int __init ofcd_init(void) /* Constructor */
{
printk(KERN_INFO "Namaskar: ofcd registered");
if (alloc_chrdev_region(&first, 0, 1, "Shweta") < 0)
{
return -1;
}
if ((cl = class_create(THIS_MODULE, "chardrv")) == NULL)
{
unregister_chrdev_region(first, 1);
return -1;
}
if (device_create(cl, NULL, first, NULL, "mynull") == NULL)
{
class_destroy(cl);
unregister_chrdev_region(first, 1);
return -1;
}
cdev_init(&c_dev, &pugs_fops);
if (cdev_add(&c_dev, first, 1) == -1)
{
device_destroy(cl, first);
class_destroy(cl);
unregister_chrdev_region(first, 1);
return -1;
}
return 0;
}
28صفحه
;)unregister_chrdev_region(first, 1
;)"printk(KERN_INFO "Alvida: ofcd unregistered
}
;)module_init(ofcd_init
;)module_exit(ofcd_exit
;)"MODULE_LICENSE("GPL
;)">MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com
;)"MODULE_DESCRIPTION("Our First Character Driver
بعد از نوشتن و ذخیره برنامه فوق میتوانیم کارهای زیر را بروی درایور نوشته شده انجام دهیم:
• ساخت درایور )فایل (.koبا استفاده از دستور make
• لود کردن درایور در حافظه با استفاده از دستور insmod
• مشاهده درایورهای بارگذاری شده با استفاده از دستور lsmode
• مشاهده شماردههای اصلی و فرعی اختصاص داده شده به درایو با استفاده از دستور
cat /proc/devices
• امتحان کردن خصوصیا ت درایور تهی با کمک گرفتن از شکل زیر
• حذف درایور از کرنل با استفاده از rmmod
صفحه29
بعد از طی این مراحل خوشحالیم که توانستیم اولین درایور خود را نویسیم .قابل ذکر است که این درایور
ل استاندار ِد / dev/nullکار میکند.برای اینکه معنببی ایببن حببرف را در ک کنیببد کببافی اسببت
ما شبیه به فای ِ
شماردههای اصلی و فرعی فایل / dev/nullرا مشاهده کنیبد و سبعی کنیبد ببا اسبتفاده از دسبتورا ت catو
echoدر این فایل بنویسید یا بخوانید .رفتار این فایل با درایوری که ما نوشتدهایم دقیقا ًا خواهد بود.
ولبببی شببباید یبببک سبببؤال ببببرای شبببما هنبببوز مطبببرح باشبببد و آن ایبببن اسبببت کبببه مبببا تواببببع
my_read,my_write,my_open,my_closeو ...را فراخوانی کردیم ولببی اگببر بخببواهیم بصببور ت
واقعی عملیا ت خواندن یا نوشتن را داشته باشیم چگونه توسط این توابع اینکار را انجام دهیم؟ جببواب بببه ایببن
پرسش در فصل بعد بررسی خواهد شد.
صفحه30
فصل ششم :خواندن و نوشتن بروی فایل دستگاههای
کاراکتری
یبینیم که نوع داده بازگشتی توابببع )( my_openو )( my_closeاز نببوع Int
با توجه به کدهای فوق م د
میباشد ولی مقدار صفر )اطلعا ت ناچیز( را بازگشت میدهند که آن هم به معنی موفقیت آمیز بببودن عملیببا ت
این توابع است.در مقابل نوع داده بازگشتی توابع )( my_readو )( my_writeاز نوع intنبوده و مطببابق
کد فوق از نوع ssize_tمیباشد که با کاوش در میان هدر فایلهای کرنل اطلعببا ت خببوبی را در ایببن مببورد
صفحه31
میتوانیم پیدا کنیم.بازگرداندن عدد منفی معموا ًل نشانگر یک خطا است در صورتی کببه بازگردانببدن یببک عببدد
مثبت اطلعا ت بیشتری را به ما میدهد.برای خواندن از فایل ،مقدار بازگشتی باید با تعداد بایتهای خوانده شده
برابر باشد و همچنین برای نوشتن ،مقدار بازگشتی میبایست تعداد بایتهای نوشته شده را نشان دهد.
چگونگی خواندن از فایل دستگاه
با دانستن مفاهیم فوق اگر دوباره به کد نوشته شده نگاهی بیاندازیم متوجه خواهیم شدکه در عملیببا ت
ل دستگا ِه /dev/mynullمیخواند سیستم کال آنرا به فایل سیستم VFSدر لیه
خواندن ،وقتی کاربر از فای ِ
یشببود کببه بایببد تببابع )(
یآورد و متوجه م د
کرنل میرساند.سپس VFSزوج > < major,minorرا بدست م د
ل ثبت شده است را فراخوانی کند.از اینرو تابع )( my_readکه نویسنددهی درایببور آنببرا
my_readکه قب ا ً
ت خوانببدن را بازگشببت
نوشته است بدینگونه فراخوانی میشود.این تابع باید مقدار خوانده شده در هر درخواس ب ِ
دهد.درکد قبل ما مقدار صفر را بازگشت دادیم که بدین معنی است که مقببداری خوانببده نشببده اسببت یببا بببه
عبار ت دیگر در آخر فایل هستیم .از اینرو وقتی فایل دستگاه شروع به خواندن میکند همواره تهببی بازگردانببده
میشود.
سؤالی که ممکن است مطرح شود این است که اگر ما بجای بازگرداندن عدد صفر ،عدد یک را بازگردانیم آیببا
یشویم که جواب سؤال فوق مثبت اسببت ولببی در
داددهای را کسب میکنیم؟ با نگاه به پارامترهای تابع متوجه م د
نظر داشته باشیم که داددههای بازگشتی داددههای آشغال و تصادفی هسببتند بببدلیل اینکببه تببابع )(my_read
مقدار متغیر bufرا دستکاری نمیکند.توجه کنید که bufدومیببن پببارامتر تببابع )( my_readمیباشببد کببه
توسط کاربر ایجاد میشود.
درحقیقت برای عملکرد منطقی و درست تابع خواندن در فایلهای دسببتگادههای کبباراکتری )کببه در اینجببا تببابع
my_readمیباشد(،میبایست این تابع منطبق بر ورودی lenکه سومین پارامتر تابع خواندن است و تعببداد
بایتهای درخواست شده را نشان میدهد،به همان میزان داددهها را در bufبریزد.بببه عبببار ت دیگببر بایببد buf
مقدار مساوی یا کمتر از lenبایت داده داشته باشد و تعداد بایتهای خوانده شده باید توسط تببابع بازگردانببده
نتر داددهها را میخواند و در بافر قرار میدهد و آنگاه کاربر میتواند داده
شود.بدین گونه نویسنده تابع در لیه پایی د
ها را در لیه بالتر بخواند.
صفحه32
درخواستهای خواندن و نوشتن ما بروی دستگاه /dev/mynullبدون اینکه خواندن یا نوشتنی در کار باشد،
موفقیت آمیز بوده است.
صفحه33
برنامه ثبت آخرین کاراکتر نوشته شده
حال میخواهیم با دانستدههای فوق تابع )( my_readو )( my_writeرا به نحوی تغییر دهیم تا با
هر بار خواندن ،آخرین کاراکتر نوشته را بازگرداند.
به کد زیر توجه نمایید:
)static ssize_t my_read(struct file *f, char __user *buf, size_t len, loff_t *off
{
;)"printk(KERN_INFO "Driver: read()\n
;buf[0] = c
;return 1
}
)static ssize_t my_write(struct file *f, const char __user *buf, size_t len, loff_t *off
{
;)"printk(KERN_INFO "Driver: write()\n
;]c = buf[len – 1
;return len
}
کد فوق به درستی کار میکند ولی فقط یک مشکل ممکن است وجود داشته باشد و آن این است که اگر کبباربر
بافر غیر معتبری را پر کند آنگاه ما با یک خطای کرنل مواجه خواهیم شببد .بببرای رفببع ایببن مشببکل دو API
معرفی شده است تا بافرهای یوزر را برسی کندکه امن و صحیح هستند یا خیر .پس بیایید با استفاده از ایببن دو
APIدوباره کد را بازنویسی کنیم:
قابل ذکر است که این دو تابع به هدر فایل > <asm/uaccess.hنیاز دارند.
)static ssize_t my_read(struct file *f, char __user *buf, size_t len, loff_t *off
{
;)"printk(KERN_INFO "Driver: read()\n
)if (copy_to_user(buf, &c, 1) != 0
صفحه34
;return -EFAULT
else
;return 1
}
)static ssize_t my_write(struct file *f, const char __user *buf, size_t len, loff_t *off
{
;)"printk(KERN_INFO "Driver: write()\n
)if (copy_from_user(&c, buf + len – 1, 1) != 0
;return -EFAULT
else
;return len
}
یشببود کببه
با اجرای دستور cat /dev/mynullبدون وقفه ،آخرین کاراکتر نوشته شده بازگشت داده م د
برای خروج از این حالت میبایست ctrl+cفشرده شود.تابع )( my_readفقط یکبار عببدد ۱برمیگردانببد و
برای دفعا ت بعد میبایست عدد صفر بازگردانده شود .این مورد با استفاده از پارامتر چهارم تابع )متغییببر (off
قابل انجام میباشد.
صفحه35
فصل هفتم :دسترسی به سخت افزار در لینوکس
تافزار
رابط عمومی سخ ه
تافزار را تعریف کنیم.فعل کاری به جزییببا ت
برای در ک بهتر این فصل نخست باید رابط عمومی سخ د
تافزار را نشان میدهد .ایبن راببط مببوجب عببدم وابسببتگی ببه
تئوریک آن نداریم.عکس زیر رابط عمومی سخ د
ل معماری ماشین را پنهان میکند.
یشود و عم ا ً
تافزار م د
معماری سخ د
بدلیل اینکه برای معماری های مختلف شکل فوق متفاو ت میباشد ،فرض بر این است که ما در مورد معمبباری
۳۲بیتی بحث میکنیم .برای آدرس باس ۳۲بیتی ،حافظه از ) (0x00000000شروع شده و تا )(0xffffffff
ییابد.برای عدم وابستگی به معماری باید طرح ما چیزی شبیه به شکل فوق باشد.جاییکه حافظه RAM
ادامه م د
و ناحیه دستگاهها در هم تنیده شده باشند.برای معماری ۳۲بیتی معموا ًل ۳گیگابایت برای RAMآدرس دهی
یشود و یک گیگابایت باقیمانده کببه رنببج
یشوند که رنج آدرس 0x00000000تا 0xbfffffffرا شامل م د
م د
یشود به ناحیه دستگاهها اختصبباص مببی یابببد.اگببر حببافظه
آدرس 0xc0000000تا 0xffffffffرا شامل م د
ل ۲گیگابببایت( آنگبباه نبباحیه دسببتگاه میتوانببد از آدرس 0x80000000
دستگاه کمتر از ۴گیگابایت باشد)مث ا ً
شروع شود.
صفحه36
برای مشباهده ترتیبب نگاشبت حبافظه )یبا همبان آدرس دهبی( در سیسبتم خبود میتوانیببد از دسبتور cat
/proc/iomemاستفاده نمایید.همچنین برای مشاهده اندازه تقریبی حافظه RAMسیستم خود میتوانید از
دستور cat /proc/meminfoکمک بگیرید.در شکل زیر خروجی این دو دستور را در سیستم من نشان
میدهد:
قابل ذکر است که مقادیری که به حافظه RAMاشاره میکند را آدرس فیزیکی و مقادیری کببه بببه دسببتگاهها
اشاره میکنند را آدرس باس گویند.دلیل نامگذاری آدرس باس به این دلیل است که دستگاهها همیشه فارغ از
نوع معماری به باس آن سیسبتم متصببل میگردنبد.ماننببد PCIدر معمباری x86یبا AMBAدر معمباری
ARMو یا SuperHywayدر معماری SuperHو...
یشببوند ولببی در
سهای فیزیکی و باس به دوشیوه داینامیک و یا از طریق معماری پردازشببگر پیکربنببدی م د
آدر د
لینوکس به دلیل اینکه اجازه دسترسی بصور ت مستقیم به حافظه وجود ندارد میبایست ابتدا به حافظه مجببازی
نگاشت شود و سپس از طریق آن به حافظه RAMیا حافظه دستگاه دسترسی حاصبل شببود .دو APIمرتببط
جهت نگاشت یا عدم نگاشت آدرس حافظه دستگاه به حافظه مجازی به شرح زیر میباشد.نکته قابل ذکببر ایببن
است که این دو APIدر هدر فایل > <asm/io.hتعریف شده است:
صفحه37
void *ioremap(unsigned long device_bus_address, unsigned long
;)device_region_size
;)void iounmap(void *virt_addr
برای خواندن و نوشتن در حافظه نگاشت شده توابع زیر که همگی نیز در هببدر فایببل > <asm/io.hتعریببف
شددهاند در دسترس میباشد:
>#include <linux/module.h
>#include <linux/version.h
>#include <linux/kernel.h
>#include <linux/types.h
>#include <linux/kdev_t.h
>#include <linux/fs.h
صفحه38
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <asm/io.h>
39صفحه
for (i = 0; i < len; i++)
{
byte = ioread8((u8 *)vram + *off + i);
if (copy_to_user(buf + i, &byte, 1))
{
return -EFAULT;
}
}
*off += len;
return len;
}
static ssize_t my_write(struct file *f, const char __user *buf, size_t len, loff_t *off)
{
int i;
u8 byte;
40صفحه
return len;
}
41صفحه
}
cdev_init(&c_dev, &vram_fops);
if (cdev_add(&c_dev, first, 1) == -1)
{
device_destroy(cl, first);
class_destroy(cl);
unregister_chrdev_region(first, 1);
return -1;
}
return 0;
}
module_init(vram_init);
module_exit(vram_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>");
MODULE_DESCRIPTION("Video RAM Driver");
:پس از ثبت کد فوق طبق معمول کارهای زیر را برای اجرای درایورمان انجام میدهیم
(video_ram.ko میسازیم )فایلmake را با استفاده از دستورvram • درایور
insmod • لود کردن درایور با استفاده از دستور
42صفحه
• با استفاده از دستور echo -n “0123456789” > /dev/vramدر فایل /dev/vram
مینویسیم
• با استفاده از دستور od -t x1 -v /dev/vram | lessاز فایل فوق میخوانیم .ما همچنین
میتوانیم از دستور catنیز استفاده نماییم ولی مقادیر بازگشت داده شده بصور ت باینری خواهند بود
در حالی که دستور od -t x1مقادیر را بصور ت هگزادسیمال نشان میدهد.
• درایور را با استفاده از rmmod video_ramاز حافظه خارج میکنیم.
صفحه43
فصل هشتم :دسترسی به پور ت ورودی/خروجی خاص
در معماری x86
شاید برای شما سؤالی مطرح شود که کدام دستگاه ورودی/خروجی و کدام پور ت مببورد نظببر میباشببد؟
پاسخ به این پرسش ها ساده است بدلیل اینکه در معماری x86هر دستگاه استاندارد از قبل محل نگاشببت آن
تعریف گردیده است.با استفاده از خروجی فایل /proc/ioportsمیتببوانیم ایببن نگاشببت هببا کببه در کرنببل
تعریف گردیده است را در سیستم خببود مشبباهده نمبباییم.ایببن دسببتگادهها عبارتنببد از تببایمر ، DMA ،درگبباه
ینمایید:
سریال،درگاه موازی ، RTC ،رابط باس PCIوغیره میباشدکه در شکل زیر مشاهده م د
صفحه44
دسترسی به پورت سریال در x86
ل ذکر شد هر دستگاه در معماری x86از قبل به حافظه نگاشت شده اسببت.بببرای مثببال
همانطور که قب ا ً
ل بببه چببه
اولین پور ت سریال ،همیشه در آدرس 0x3F8تا 0x3FFنگاشت شده است.ولی ایببن نگاشببت عم ا ً
معنی است و این اطلعا ت چه کمکی به ما جهت دسترسی به پور ت سریال میکند و ما چه کارهایی را میتببوانیم
انجام دهیم؟
یشببود کببه بببه
همانگونه که در فصل اول اشاره شد هر درگاه سریال توسط یک دیوایس کنترلر سریال،کنترل م د
آن ) (Universal Asynchronous Receiver/Transmitterیا UARTگوینببد یببا مث ا ً
ل
برای تایمر ،ما ) (Universal Synchronous/Asynchronous Receiver/Transmitte
یا USARTد را داریم.در یک کامپیوتر معمولی کنترلر UARTاز ICمدل PC16550Dاستفاده میکنببد کببه
مشخصا ت آنرا میتوانید از نشانی های زیر دریافت نمایید:
http://www.national.com/ds/PC/PC16550D.pdf
http://esrijan.com/DDK/LDDK-Package
http://esrijan.com/index.php?pagefile=lddk
صفحه45
سها میتوانیم از ماکروهایی که تعریف شده است استفاده نمبباییم .مثلا ً بببرای پببور ت
بجای هارد کد کردن آدر د
ل:
سریال میتوانم از هدر فایل > <linux/serial_reg.hاستفاده کنیم مث ا ً
;u8 val
;)val = inb(SERIAL_PORT_BASE + UART_LCR /* 3 */
;)outb(val, SERIAL_PORT_BASE + UART_LCR /* 3 */
;u8 val
;)val = inb(SERIAL_PORT_BASE + UART_LCR /* 3 */
ساخت یک LEDواقعی
ت درایببو ِر دسببتگاه
تافزار و درایورها در لینوکس ساخت یببک کی ب ِ
یک مثال عملی برای دستیابی به سخ د
لینوکس) (LDDKمیباشد.سپس میخواهیم یک LEDد را بصور ت چشمک زن درآوریم.برای اینکار یک LED
با مقاومت ۳۳۰اهم را به پایدههای (TX) ۳و (GND) ۵متصل میکنیم.چشمک زن کردن LEDرا با قطببع و
صفحه46
میلی ثانیه یکبار وآنهم توسط لود کردن و از حافظه خارج کردن درایببور شبببیه۵۰۰ وصل کردن جریان درهر
rmmod وinsmod blink_led.ko ببببرای اینکبببار بایبببد متناوب ببا ًا از دسبببتور.سبببازی میکنیبببم
نامگببذاریblink_led.c بنابراین کد ما باید چیزی شبیه زیر باشد که آنرا. استفاده نماییمblink_led.ko
:میکنیم
#include <linux/module.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <linux/serial_reg.h>
#define SERIAL_PORT_BASE 0x3F8
47صفحه
void __exit cleanup_module()
{
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>");
MODULE_DESCRIPTION("Blinking LED Hack");
.خروجی مورد نظر را مشاهده کرد، کردن این فایل و اتصال کیت مورد نظرmake درنهایت میتوان با
48صفحه
فصل نهم :کنترل ورودی/خروجی در لینوکس
معرفی :ioctl
کنترل ورودی/خروجی یا به اختصار ioctlیک system callهمه کاره برای تمام درایورهاست.اگر
برای کار مشخصی که شما برای داریورتان در نظر دارید هیچ system callای وجود نداشته باشد بازهم
میتوانید روی ioctlحساب کنید.برای مثال با ioctlمیتوانیببد صببدا را در درایببور صببوتی کنببترل کنیببد یببا
نمایشگر را برای دستگاه گرافیک کنترل کنید و یا از دستگاه ثبت شددهای بخوانید و...
البته از ioctlنیز میتوان برای کارهایی غیر از آنچه اشاره شد ،از جمله دیباگ کردن یک درایور بببا پببرس و
جو از ساختار داددهای درایور نیز استفاده کرد.
سؤالی که ممکن است مطرح شود این است که چگونه یک تببابع ioctlبببدینگونه کارهببای متفبباوتی را انجببام
یدهد.جواب این پاسخ به دو پارامتر این تابع ،یعنی »دستور« و »آرگومان« بازمیگردد».دستور« یک عدد است
م د
که نوع عملیا ت را مشخص میکند و »آرگومان« پارامترهای مورد نیاز »دستور« را فراهم میکنببد.بصببور ت کلببی
تابع ioctlرا با استفاده از ساختار switch … caseنوشتدهاند که با توجه به ورودی »دستور« عملیببا ت
مختلفی را اجرا میکند.
در کرنل های قدیمی ioctlبدینگونه تعریف شده بود:
;)int ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long arg
;)long ioctl(struct file *f, unsigned int cmd, unsigned long arg
اگر به »آرگومان«های زیادی نیاز دارید میتوانید تمام مقادیر را در قالب یک سبباختار ) (structقراردهیببد و
نشانی این ساختار را به تابع ioctlمعرفی کنید.چه عدد ارسالی یک مقدار صحیح باشد یا یببک اشباره گبر ،در
فضای کرنل در نهایت به longتفسیر شده و پردازش میشود.
یشود و سپس یک تابع اشبباره گببری مناسببب بببا آن راه
Ioctlمعموا ًل به عنوان بخشی از درایور پیاددهسازی م د
ل برای )( readو )( writeبکاربردیم.
یگردد .دقیقا ًا مانند system callهایی که ما قب ا ً
اندازی م د
صفحه49
ل کار کردیم ،یببک فیلببد اشبباره گببر بببه تببابع
در درایورهای کاراکتری و در ساختار file_oprationکه قب ا ً
یشود )البته از کرنل نسخه ۲.۶.۳۵به بعد(.
ioctlنیز وجود دارد که با unlocked_ioctlشناخته م د
در فضای یوزر با استفاده از هدر فایل > <sys/ioctl.hو به روش زیر میتوان به ایببن تببابع نوشببته شببده در
درایور دسترسی داشت:
در اینجا cmdنظیر آن چیزی است که در تابع ioctlدرایور نوشته شده است و یک متغییر برای فرسببتادن
هر نوع آرگومان به درایور میباشد.نکته قایل توجه این است که »دستور« و نوع آرگومان »دسببتور« بایببد بیببن
فضای یوزر و کرنل مشابه باشد.بنابراین این تعاریف معموا ًل در هدر فایلهای هر فضا قرار میگیرید.
#ifndef QUERY_IOCTL_H
#define QUERY_IOCTL_H
>#include <linux/ioctl.h
typedef struct
{
;} query_arg_t
صفحه50
#define QUERY_CLR_VARIABLES _IO('q', 2)
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include "query_ioctl.h"
#define FIRST_MINOR 0
#define MINOR_CNT 1
51صفحه
static int status = 1, dignity = 3, ego = 5;
return 0;
return 0;
static int my_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long
arg)
#else
static long my_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
#endif
query_arg_t q;
switch (cmd)
case QUERY_GET_VARIABLES:
52صفحه
q.status = status;
q.dignity = dignity;
q.ego = ego;
return -EACCES;
break;
case QUERY_CLR_VARIABLES:
status = 0;
dignity = 0;
ego = 0;
break;
case QUERY_SET_VARIABLES:
return -EACCES;
status = q.status;
dignity = q.dignity;
ego = q.ego;
break;
53صفحه
default:
return -EINVAL;
return 0;
.owner = THIS_MODULE,
.open = my_open,
.release = my_close,
.ioctl = my_ioctl
#else
.unlocked_ioctl = my_ioctl
#endif
};
int ret;
54صفحه
if ((ret = alloc_chrdev_region(&dev, FIRST_MINOR, MINOR_CNT, "query_ioctl")) < 0)
return ret;
cdev_init(&c_dev, &query_fops);
return ret;
cdev_del(&c_dev);
unregister_chrdev_region(dev, MINOR_CNT);
return PTR_ERR(cl);
55صفحه
class_destroy(cl);
cdev_del(&c_dev);
unregister_chrdev_region(dev, MINOR_CNT);
return PTR_ERR(dev_ret);
return 0;
device_destroy(cl, dev);
class_destroy(cl);
cdev_del(&c_dev);
unregister_chrdev_region(dev, MINOR_CNT);
module_init(query_ioctl_init);
module_exit(query_ioctl_exit);
MODULE_LICENSE("GPL");
56صفحه
MODULE_DESCRIPTION("Query ioctl() Char Driver");
میگذاریم که درquery_app.c تابع فراخوان را در فضای کاربر مینویسیم و نام فایل آنرا، و در نهایت
:زیر مشاهده میکنید
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include "query_ioctl.h"
query_arg_t q;
else
57صفحه
printf("Status : %d\n", q.status);
int v;
query_arg_t q;
scanf("%d", &v);
getchar();
q.status = v;
58صفحه
scanf("%d", &v);
getchar();
q.dignity = v;
scanf("%d", &v);
getchar();
q.ego = v;
int fd;
enum
e_get,
e_clr,
59صفحه
e_set
} option;
if (argc == 1)
option = e_get;
else if (argc == 2)
if (strcmp(argv[1], "-g") == 0)
option = e_get;
option = e_clr;
option = e_set;
else
60صفحه
{
return 1;
else
return 1;
fd = open(file_name, O_RDWR);
if (fd == -1)
perror("query_apps open");
return 2;
switch (option)
case e_get:
get_vars(fd);
break;
61صفحه
case e_clr:
clr_vars(fd);
break;
case e_set:
set_vars(fd);
break;
default:
break;
close (fd);
return 0;
# If called directly from the command line, invoke the kernel build system.
ifeq ($(KERNELRELEASE),)
KERNEL_SOURCE := /usr/src/linux
module:
62صفحه
clean:
${RM} query_app
else
obj-m := query_ioctl.o
endif
صفحه63
فصل دهم :دیباگ کردن کرنل در لینوکس
در لینوکس و در فضای یوزر چندیدن دیباگر معروف از قبیل ddd ، gdbو … وجود دارند .همینطور
در فضای کرنل نیز دیباگری بنام kgdbاز کرنل نسخه ۲.۶.۲۶به بعد معرفی گردیده است.
صفحه64
منظورکرنل باید با سوئیچ CONFIG_KGDB=yکانفیگ شود.بعلوه جهت دیباگ کردن از طریببق پببور ت
سریال باید سوئیچ CONFIG_KGDB_SERIAL_CONSOL=yنیز بکار برده شود.همچنین برای اینکه
داددههای سمبولیک توکار kgdbبرای ما مفهوم تر باشد میتوان از سببوئیچ CONFIG_DEBUG_INFO
قتببر نشببانگرهای فریببم در کرنببل از سببوئیچ
یشببود بببرای فهببم دقی د
نیببز اسببتفاده کببرد.و پیشببنهاد م د
نها در مواقعی که از دستورا ت زیر جهت
CONFIG_FRAME_POINTER=yنیز استفاده شود.این آپش د
پیکربندی کرنل استفاده کنیم و در منوی kernel hackingدر اختیار میباشد:
البته جهت اجرا باید دستور سوم را حتما ًا با مجوز rootیا با استفاده از sudoاجرا کرد.
نها را در شکل زیرمشاهده میکنید:
آپشن ها و منو ها و چگونگی دسترسی به آ د
صفحه65
• کامپایل کرنل با استفاده از فریم اشاره گرها CONFIG_FRAME_POINTER <---
بعد از انجام تغییرا ت و ذخیره کردن آن ،کرنل را با استفاده از دستور makeساخته و سببپس بببا اسببتفاده از
دستور make installنصب نمایید و جهت بو ت کردن سیستم با کرنل جدیببد grubرا تغییببر دهیببد.
بسببته بببه اینکببه توزیببع شببما چیسببت بایببد فایببل تنظیمببا ت را در مسببیر هببای /etc/grub.confویببا
/ boot/grub/menu.lstبیابیببد .وقببتی تمببام مراحببل فببوق انجببام شببد بایبد پبارامتر کرنببل kgdb-
relatedنیز همانطور که در زیر مشاهده میکنید به grubاضافه شود:
kgdbocجهت اتصال gdbاز طریق پور ت سریال میباشد که به فرمت زیر است:
>kgdboc = <serial device> , <baud rate
• > : <serial deviceفایل ) (Portدستگاه سریال بروی دستگاهی کببه کرنببل بببروی آن دیببباگ
میشود.
• > <baud rateنرخ انتقال پور ت سریال میباشد.
پارامتر kgdbwaitبه کرنل میگوید که در هنگام بو ت ،تا زمانی که کلینببت gdbبببه آن متصببل نشببده
است باید منتظر بماند.این پارامتر حتما ًا باید بعد از kgdbocبیاید.
الن باید یک کپی از ایمیج کرنل ساخته شده یا همان vmlinuxبرای استفاده gdbبرداشته و سیستم را با
کرنل جدید راه اندازی نمایید .سپس باید منتظر بمانید تا gdbبا استفاده از پور ت سریال به آن متصل شود.
برای kgdbاطلعا ت ،در فایلی شبیه /dev/ttysoبرای اولین پور ت سریال ثبت میشود.
صفحه66
پیکربندی gdbبروی کلینت
کارهای زیر میبایست انجام شود:
اتصال کلینت به کامپیوتری که کرنل را اجرا میکند بببا اسببتفاده از کابببل null modem •
س سریال( .
)مانند کابل کرا ِ
کپی که از ایمیج کرنل ساخته شده گرفتدهاید را بروی شاخه جاری کلینت قرار دهید. •
در هنگامی که سیستم هدف در انتظار اتصال میباشد با استفاده از کلینببت دسببتورا ت زیببر را •
اجرا کنید:
• (gdb) file vmlinux
• (gdb) set remote interrupt-sequence Ctrl-C
• (gdb) set remotebaud 115200
• (gdb) target remote /dev/ttyS0
• (gdb) continue
صفحه67
فصل یازدهم :درایورهای USBدرلینوکس-قسمت اول
صفحه68
برای بدست آوردن لیستی از دستگاههای USBشناسایی شده میتوانید دسببتور lsusbرا بببا مجببوز ریشببه
صادر نمایید .شکل زیر خروجی دستور lsusbرا قبل و بعد از اتصال یببک قلببم نببوری USBبببه سیسببتم را
مشاهده میکنید:
صفحه69
رمزهگشایی از دستگاه USB
برای رمزگشایی از دستگاه USBنخست بهتر است معماری آن را بشناسیم .تمببام دسببتگاههای USB
ض رایج تریببن
شفر ِ
م پی د
دارای یک یا چند تنظیم میباشند .منظور از تنظیم ،چیزی شبیه پروفایل است که تنظی ِ
نوع را در بر دارد .لینوکس فقط از یک تنظیم برای هر دستگاه پشتیبانی میکند )اولین پیببش فببرض(.بببرای هببر
یدهد.
تنظیم ،دستگاه USBممکن است یک یا چند رابط داشته باشد که هر رابط یک کار مشخص را انجام م د
هر دستگاه USBمیتواند رابطهای مختلفی را برای کارهای مختلف داشته باشد که بببه آن MDF multi-
( function deviceیا »یک دستگاه با کارکرد چندگانه« گویند .برای مثال یک چبباپگر چنببدکاره USB
میتواند چاپ کند ،اسکن کند و فکس ارسال کند.پس ما در اینجا حداقل به سه رابط نیاز داریم .بنابراین درایو ِر
طهببا
دستگاههای USBبرعکس درایورهای دیگر بجای اینکه برای دستگاهها نوشته شببوند ،معمببوا ًل بببرای راب د
طهای مختلببف
نوشته میشوند.پس ممکن است برای هر دستگا ِه USBچندین درایور وجود داشته باشد و یا راب د
یک دستگاه USBمیتواند درایورهایی مشتر ک داشته باشند.ولی هر رابط حداکثر میتواند یک درایببور داشببته
باشد و نه بیشتر .البته بهتر است که هر دستگاه USBحتی با چندین رابط نیز تنها یک درایور داشته باشد.
صفحه70
در شکل فوق )محتوی شاخه (/procکلمه Driver=...نشان میدهد که رابببط مببورد نظببر بببه کببدام
درایور متصل شده است .کلمه noneنیز نشان میدهد برای این رابط ،درایوری تعیین نگردیده.
طها باید یک یا چند EndPointوجود داشته باشد .یک EndPointشبیه لوله بببرای انتقببال
برای تمام راب د
ط دستگاه میباشد .هر EndPointبسته به نوع اطلعا ت میتواند به
اطلعا ت از/به )بسته به نوع عملکرد( راب ِ
مبندی شود.
چهار گوندهی کنترل ،وقفه ،حجم و همزمانی تقسی د
تمام دستگاههای معتب ِر USBدارای یک EndPointای خاص مجازی صفر از نوع کنببترل میباشببند
که فقط یک EndPointدوطرفه )از/به( میباشد .شکل زیر یک نمای تصویری کامل از یک دسببتگاه USB
استاندارد با توضیحا ت بال میباشد:
قسمتهای دستگاه یو اس بی را بیاد بیاورید )تصویرصفحه قبل( ،اولین حرف از هر خط ،انواع مشخصا ت
ل حرف Dبرای دسببتگاهC ،
قسمتهای مختلف یک دستگاه ) USBکه شرح داده شد( را نمایندگی میکند .مث ا ً
برای پیکربندی I ،برای رابط و Eبرای Endpointو...
جزئیا ت هر یک از انوع مختلف در سورس کرنل و در نشانی زیر وجود دارد:
/Documentation/usb/proc_usb_info.txt
نوشتن درایور قلم نوری USB
صفحه71
ل اطلعا ت مببا در
برای نوشتن درایور USBهنوز اطلعا ت ما در مورد پروتکل USBکافی نیست .مث ا ً
طها ،خطوط انتقال اطلعا ت و چهار نوع از Endpointها که بحث شد .همچنیببن
مورد تنظیما ت دستگاه ،راب د
علیم اختصاری که در شکل دیدیم از جمله S,B,Tزیاد مفهوم نیست.
اما نگران نباشید.جزئیا ت به تدریج شرح داده خواهد شد.ولی نخست بیایید اولین رابط مرتبط با درایور دسببتگاه
قلم نوری USBبنام pen_register.koرا بنویسیم.
مشابه درایورهایی که برای دستگاههای دیگر نوشتیم ،برای درایورهای دستگاههای USBنیببز بببه تببابع
ل نوشببتیم
بگر نیاز داریم .برای سرعت بیشتر میتوانیم از الگوی درایورهای دیگببری کببه قب ا ً
سازنده و تابع تخری د
نها متفاو ت خواهد بود .تفاو ت اینجاست که در درایورهای دیگر کبار ثبببت و عببزل
استفاده کنیم ولی محتوای آ د
ل لیه مرتبط استفاده شببود.
درایور به VFSمرتبط میشود ولی در درایورهای دستگاههای USBباید از پروتک ِ
دراین حالت هستدهی USBبجای اینکه یک شبه فایل در فضای یوزر ارائه کند ،میبایست به دستگاه واقعی در
تافزار متصل گردد.
فضای سخ د
APIهای هسته USBبرای انجام ثبت و عزل درایور به ترتیببب زیببر میباشببد)ایببن APIهببا در هببدر فایببل
> <linux/usb.hتعریف شده اند(:
ساختار usb_driverشامل فیلدهای نام درایورها و جدول IDبرای شناسایی خودکار چندین دسببتگاه
USBو همچنین دو تابع جهت هندل کردن اتصال و انفصال دستگاه USBبصور ت بلدرنگ در هسته USB
میباشد .بیایید به فایل pen_register.cنگاهی بیاندازیم:
>#include <linux/module.h
>#include <linux/kernel.h
>#include <linux/usb.h
صفحه72
static void pen_disconnect(struct usb_interface *interface)
{
printk(KERN_INFO "Pen drive removed\n");
}
module_init(pen_init);
module_exit(pen_exit);
73صفحه
;)"MODULE_LICENSE("GPL
;)">MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com
;)"MODULE_DESCRIPTION("USB Pen Registration Driver
یبینم خروجی برنامه آن چیزی نیست کببه انتظببارش را داشببتیم dmesg .و زیببر
اما با کمال تعجب م د
شاخه / procرا چک کنید تا جزئیا ت بیشتری دستگیرتان شود .همانطور که در قبل اشاره شد درایورهای یببو
اس بی متفاو ت از درایورهای کاراکتری هستند .دو شکل بالتر نشان میدهد که دستگاه قلببم نببوری یببک رابببط
ل به درایور ذخیره ساز یو اس بی ) (usb-storageمتصل شده است.
دارد ) (number 0که قب ا ً
حال به منظور ارتباط درایورمان با آن رابط ،ما نیاز داریم تا ذخیره ساز یو اس بی کببه لببود شببده اسببت را بببا
استفاده از دستور rmmod usb-storageاز حافظه کرنل حذف کنیم و بجای آن درایببور قلببم نببوری
USBرا بارگذاری کنیم .به محض اینکه اینکار انجام شود نتیجه مورد انتظار ما ظاهر خواهد شببد .شببکل زیببر
یک نگاه اجمالی از لگ مورد نظر میباشد .با اتصال و انفصال بلدرنگ قلم نوری از کامپیوتر میتوانید فراخببوانی
های متناظر را نیز ببینید.
صفحه74
صفحه75
لدوازدهم:درایورهای USBدرلینوکس-قسمت
فص د
دوم
درفصل قبل دیدیم که کد یکتای کارخانه سازنده قلم نوری 0x058fو کد یکتای محصول 0x6387بود .قبل
از اینکه با هم کد فصل قبل را کامل کنیم ابتدا لزم است مفاهیم بیشتری را از پروتکل USBبدانیم.
• کنترل :برای ارسال اطلعا ت کنترلی – بعنوان مثال ریست کردن دستگاه ،گرفتن اطلعببا ت وضببعیت
شفرض Endpointکنترل بببه صببفر اشبباره
دستگاه و غیره .در تمام دستگاههای USBبصور ت پی د
میکند.
• وقفه :برای ارسال اطلعا ت کوتاه و سریع ،معموا ًل تا 8بایت از وقفه اسببتفاده میشببود .بعنببوان مثببال
انتقال اطلعا ت برای پور ت سریال یا دستگاههای رابط شخصی مانند کیبورد و ماوس و غیره.
• انبوه :برای انتقال اطلعا ت حجم انبوه و در مقایسه با وقفه کم سرعت تر استفاده میگردد .بعنوان یک
مثال میتوان به انتقال اطلعا ت به یک فلش مموری اشاره کرد .
• متقارن :برای حجم زیاد اطلعا ت با تضمین پهنای باند ثابت از این نوع استفاده میشببود.ولببی جببامعیت
اطلعا ت ممکن است در این روش تضمین نشود .بعنوان مثال میتوان به انتقبال اطلعبا ت حسباس ببه
زمان مانند صدا و تصویر اشاره کرد.
به جز Enpointکنترل ،بقیه Endpointها بصور ت ورودی و خروجی میتوانند عمببل کننببد .بببدین
معنی که اطلعا ت از دستگاه USBمیتواند به کامپیوتر منتقل گردد و برعکس .هر Endpointبا اسببتفاده
از ۸بیت شناسایی میگردد که پر ارزشترین بیت ) (MSBنشانگر مسیر اطلعا ت )صفر برای خروجببی و یببک
برای ورودی( میباشد Endpoint .د کنترل دوطرفه میباشد بدین معنی که این بیت نادیده گرفته میشود.
شکل زیر مشخصا ت دستگاه USBمتصل شده به یک کامپیوتر را نشان میدهد:
صفحه76
خطوطی که با Eشروع شددهاند Endpointهستند که در شکل فوق یببک Endpointاز نببوع
»وقفه« و دو Endpointاز نوع »انبوه« و مربوط به کنترلر مرکزی UHCIدستگاه قلم نوری میباشند.
اعدا ِد Endpointبه هگزا دسیمال بوده که ببه ترتیبب 0x81و 0x01و 0x82میباشببد .بیبت پبرارزش
Endpointاول و سببوم بببا ) ( Iنشببان داده شببده کببه بیببانگر ورودی میباشببد .همچنیببن بیببت پببرارزش
Endpointدوم با ) (0نشان داده شده که بیانگر خروجی میباشد MXPS .نیز نشانگر ماکزیمم سایز پکت
د
یشود را تعیین میکند .همببانطور کببه در شببکل
میباشد یعنی بیشترین سایز اطلعاتی که دفعتا ًا میتواند منتقل م د
میبینیم » Endpointوقفه« ۲بیت بوده و » Endpointانبوه« ۶۴بیت میباشد Ivl .نیببز نشببانگر فاصببله
زمانی بین انتقال دو پکت اطلعا ت متوالی برای انتقال مناسب میباشدکه برای وقفدهها بسیار مهم و قابل تببوجه
است.
صفحه77
ل در مورد خطوطی که با E:مشخص شده بودند بحث کردیم ،حال زمان مناسبببی
از آنجایی که ما قب ا ً
است تا در مورد بقیه فیلدهای مرتبط نیز صحبت کنیببم .بصببور ت خلصببه ایببن خطببوط یببک دیببد کباملی از
خصوصیا ت دستگاه یو اس بی را به ما میدهند.
با مراجعه به شکل قبل ،اولین حرف از اولین خط هر بخش از دستگاه Tهست و نشببانگر مببوقعیت دسببتگاه در
درخت یو اس بی میباشدکه بوسیله سه جزء شماره باس ، USBسطح درخت USBو پور ت USBشناخته
میشود .حرف Dنشانگر توضیحا ت دستگاه است و شامل حداقل نسخه دستگاه ،دسته بندی/کلس دسبتگاه و
تعداد تنظیما ت در دسترس برای این دستگاه میباشد.
چندین حرف Cدر خطوطی از شکل فوق وجود دارد اگرچه معموا ًل یکی بیشتر نیست .خطوطی که با حببرف C
شروع میشوند شرح و شاخصی اسببت بببرای تنظیمببا ت خصوصببیا ت ایببن دسببتگاه ،حببداکثر قببدر ت )واقعببی و
فعلی(دستگاه و تعداد Interfaceها باید در این تنظیما ت نوشته شود.
باتوجه به توضیحا ت بال ،حداقل یک خط Iنیز باید وجود داشته باشد و در مببواقعی کببه Interfaceهببای
ترتیبی وجود داشته باشد میتواند تعداد بیشتری نیز باشد .مانند وقتی که شماره Interfaceها یکسان است
ولی خصوصیاتی متفاو ت دارند بعنوان یک مثال میتوان به وب کم ها اشاره کرد.
حرف Iمشخص کننده شرح Interfaceبه همراه شاخص ،شماره ترتیب ،قابلیت کلس/دسببته ایببن رابببط،
درایوری که به این رابط متصل شده و شماره Endpointاین Interfaceمیباشد.
کلس Interfaceممکن است مشابه کلس دستگاه باشد و یا نباشببد.و بسببته بببه شببماره ، Endpoint
ممکن است چندیدن خط Eوجود داشته باشدکه جزئیا ت آن بعدا ًا مورد بحث قرار خواهد گرفت.
نشانه * بعد از Cو Iبه ترتیب نشانگر تنظیمببا ت و Interfaceجبباری و فعببال اسببت .خببط Pشناسبدهی
فروشنده ،شناسدهی محصول و نسخدهی دستگاه را مشخص میکند .خطوط Sنیز شرح مشخصببا ت فروشببنده و
اطلعا ت توضیحی در مورد دستگاه را نشان میدهد.
برای کشف اینکه چه دستگاههایی شناسایی شددهاند و همچنین مشبباهده اطلعببا ت دسببتگاههای ، USBخببوب
است دستور cat /proc/bus/usb/devicesرا اجرا کنید .شاید اکثر این اطلعببا ت بببرای نوشببتن
درایور دستگاه مورد نیاز باشد اما آیا راهی برای نوشتن برنامه با کد Cو با توجه به این اطلعا ت وجود دارد؟
جواب مثبت است و قصد داریم در ادامه همین کار را انجام دهیم .آیا به خاطر مببی آوریببد بببه محببض اینکببه
دستگاه USBبه کامپیوتر وصل شد درایور کنترلر هاست USBاین اطلعا ت را در لیه هسته USBتغییببر
داد؟ و اطلعا ت را در مجموعدهای از structureها همانطور که مشخصا ت USBتعریف کرده قرار داد؟
یبینید که برای وضوح بیشتر بصور ت معکببوس آمببده
در زیر ساختار تعریف شده در > <linux/usb.hرا م د
است:
صفحه78
struct usb_device
{
…
struct usb_device_descriptor descriptor;
struct usb_host_config *config, *actconfig;
…
};
struct usb_host_config
{
struct usb_config_descriptor desc;
…
struct usb_interface *interface[USB_MAXINTERFACES];
…
};
struct usb_interface
{
struct usb_host_interface *altsetting /* array */, *cur_altsetting;
…
};
struct usb_host_interface
{
struct usb_interface_descriptor desc;
struct usb_host_endpoint *endpoint /* array */;
…
};
struct usb_host_endpoint
{
struct usb_endpoint_descriptor desc;
…
};
79صفحه
با دسترسی به هندل ساختار usb_deviceیک دسببتگاه مشببخص ،میتببوان بببه تمببام اطلعببا ت و
مشخصا ت USBمربوط به آن دستگاه ،همانگونه که در /procموجود است دست یافت.اما چگونه میتببوان
هندل ساختار usb_deviceرا پیدا کرد؟
یباشبد بلکبه هنببدل ) Interfaceاشباره گبر بببه سباختار
هندل دستگاه مستقیما ًا در درایور در دسبترس نم د
(usb_interfaceدر دسترس میباشد .بیاد بیاورید که درایورهای usbبرای Interfaceد هببا نوشببته
یشدند و نه برای کل دسببتگاه .اگببر نگبباهی دوبباره بببه تواببع probeو disconnectبرنبامدهی قبببل
م د
یبینید که هستدهی مرکزی USBبه ازای هر رابط دستگاه فراخوانی میشببد..هنببدل Interface
بیاندازید م د
نیز در اولین پارامتر موجود میباشد .به قسمتی از آن برنامه توجه کنید:
با اشاره گر ، Interfaceتمام اطلعا ت مربوط به آن Interfaceقابل دسترس میباشد و بببرای گرفتببن
هندل دستگاه ماکروی زیر مورد استفاده قرار میگیرد:
>#include <linux/module.h
>#include <linux/kernel.h
>#include <linux/usb.h
صفحه80
iface_desc = interface->cur_altsetting;
printk(KERN_INFO "Pen i/f %d now probed: (%04X:%04X)\n",
iface_desc->desc.bInterfaceNumber, id->idVendor, id->idProduct);
printk(KERN_INFO "ID->bNumEndpoints: %02X\n",
iface_desc->desc.bNumEndpoints);
printk(KERN_INFO "ID->bInterfaceClass: %02X\n",
iface_desc->desc.bInterfaceClass);
device = interface_to_usbdev(interface);
return 0;
}
81صفحه
};
MODULE_DEVICE_TABLE (usb, pen_table);
module_init(pen_init);
module_exit(pen_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <[email protected]>");
MODULE_DESCRIPTION("USB Pen Info Driver");
82صفحه
• مشاهده خروجی dmesg
• حذف درایور از حافظه با استفاده از دستور rmmod pen_info
شکل زیر مراحل فوق را در کامپیوتر من نشان میدهد .برای اطمینان بخاطر بیاورید که )با اسببتفاده از خروجببی
دستور (cat /proc/bus/usb/devicesدرایور usb-storageمعمولی هیچ اتصالی با رابط قلم
نوری نداشته باشد.
دو مکببانیزم بببرای اینکببه یببک دسببتگاه بببرای هسببتدهی مرکببزی USBو بببا اسببتفاده از سبباختار جببدول
usb_device_idشناسایی گردد برای یک درایور وجود دارد .نخست اینکه زوج شناسه فروشنده و شناسه
محصول با استفاده از ماکروی )( USB_DEVICEتعیین گردد که در کد فوق نیز استفاده شد .و دوم اینکه با
استفاده از ماکروی )( usb_device_infoکلس/دستدهی دستگاه مشخص گببردد .در حقیقببت ماکروهببای
بهای مختلف وجود دارد.علوه بر این چندین عدد از این ماکروهببا
بیشتری در> <linux/usb.hبرای ترکی د
میتوانند برای جدول ) usb_device_idکه با ورودی nullآخرین آن مشخص میشود( تعیین شوند .کببه
نها با ضوابطی تطابق پیدا میکنند و ما قادریم برای چندین دستگاه یک درایور بنویسیم.
هر کدام آ د
صفحه83
لسیزدهم:انتقال اطلعا ت از/به دستگاه USB
فص د
در این فصل به این سؤال پاسخ خواهیم داد که چگونه درایور ،انتخاب میکندکه چببه Interfaceای را
ثبت کند و از چه Interfaceای استفاده نماید؟
پاسخ به این سؤال به مقدار بازگشتی از تابع probeبازمیگردد .نکته اینجاست کببه هسببتدهی مرکببزی USB
ل ثبببت شببددهاند را
نهببایی کببه قب ا ً
باید probeهای همه Interfaceهای تمام دستگاه متصل را به جببز آ د
درخواست کند .بنابراین مقادیر بازگشتی probeها برای اولین بببار و بببرای تمببام Interfaceهببا گرفتببه
میشود .اگر مقدار بازگشتی یک probeصفر بود بدین معنی است کببه درایببور بببرای آن Interfaceقب ا ً
ل
Interfaceانجببام
د ثبت گردیده ولی اگر مقدار خطایی بازگردانده شود بدین معنی است که اینکار برای ایببن
نشده است.
قبل از آنکه در مببورد انتقببال نامحببدود اطلعببا ت بببه/از دسببتگاه USBصببحبت کنیببم ،در مببورد
MODULE_DEVICE_TABLEبحببث میکنیببم MODULE_DEVICE_TABLE .عمببدتا بببرای
depmodدر فضای یوزر میباشد Module .یک اصطلح دیگر برای درایور است کببه میتوانببد بصببور ت
داینامیک لود و یا از حافظه حذف گردد .ماکروی MODULE_DEVICE_TABLEدو متغیببر در بخببش
یشود و در فایلهببایی عمببومی در زیببر شبباخه
فقط خواندنی ماژولها میسازد که بوسیله depmodاستخراج م د
> /lib/modules/<kernel_versionپیاددهسازی شببده اسببت .فایلهببای modules.usbmapو
نها ما قادریم درایورها را
modules.pcimapبه ترتیب برای دستگاههای USBو PCIهستند که با آ د
بصور ت اتوماتیک لودکنیم .همانگونه که در لود اتوماتیک درایور usb-storageدیدیم.
بیایید بر پایه کدی که در فصل قبل برای قلم نوری با کد فروشنددهی 0x058fوکد محصببول 0x6387
نوشتیم کار انتقال اطلعا ت را توسعه دهیم.
تافزار است و معموا ًل به شکل لیه افقی در فضای کرنل است و از اینببرو بببرای اینکببه
USBیک پروتکل سخ د
یک Interfaceای را در فضای یوزر ایجاد کند به یکی از لیه های عمودی متصل میگردد.
ل در مورد آن بحث شد یک گزینه عالی برای برقببراری ارتببباط بببا لیببه افقببی
درایور عمودی کاراکتری که قب ا ً
USBاست که بتوانیم تمام جریان انتقال اطلعا ت را در ک کنیم.
صفحه84
اما ما نیازی نداریم تا شماره اصلی رزرو نشده وآزاد کاراکتری را بگیریم بلکه میتوانیم از شماره اصلی کاراکتری
180که برای فایلهای دستگاه کاراکتری رزرو شده است اسببتفاده کنیببم.بعلوه بببرای دسببتیابی کامببل درایببور
کاراکتری با لیه افقی USBوآنهم در یک مرحله API ،های زیر تعریف شده اند:
معموا ًل ما انتظار داریم این توابع را به ترتیب در سازنده و تخریببب گببر ماژولمببان فراخببوانی کنیببم ولببی بببرای
دستیابی به رفتار hot-plug-and-playبرای فایلهای دستگاه )کاراکتری( مرتبط با دستگاههای ، USB
نها باید به ترتیب در توابع probeو disconnectفراخوانی شوند.
آ د
اولین پارامتر توابع فوق اشاره گر به Interfaceاست کببه در اولیببن پببارامتر probeو disconnect
دریبببافت شبببده اسبببت .پبببارامتر دوم ،سببباختار usb_class_driverمیباشبببد ،قببببل از آنکبببه
usb_register_devفراخوانی شود نیاز است تا مجموعدهای از عملیا ت فایل دستگاه و نببام فایببل دسببتگاه
پیشنهادی پر شود .برای استفاده های واقعی ،به توابع pen_probeو pen_disconnectدر کببدی
که بعدا ًا خواهیم نوشت )( pen_driver.cرجوع کنید.
بعلوه میتوانیم از عملیا ت روی فایل )خواندن ،نوشببتن و (...اسببتفاده کنیببم و آن دقیقبا ًا چیببزی اسببت کببه مببا
نیازداریم تا داددهها را به/از USBانتقال دهیم pen_write .و pen_readکه در زیر نوشته شببده اسببت
برای این منظور است و همچنین تابع )() usb_bulk_msgکه در > <linux/usb.hآمببده اسببت( نیببز
برای انتقال حجم انبوه اطلعا ت به دستگاه قلم نوری بببا Endpointهگببز 0x01و 0x82اسببت کببه بببه
ترتیب فراخوانی میشود .برای لیست Endpointها به خطوطی که با Eدر شکل بعد شروع میشود تببوجه
کنید:
صفحه85
جهت یافتن لیست کاملی از APIهای هسته USBبرای توابع انتقال اطلعببا ت بببا Endpointمشببخص و
متفاو ت شبببیه توابببع )( usb_control_msgو )( usb_interrupt_msgو غیببره بببه هببدر فایببل
> <linux/usb.hدر سورس کرنل رجوع کنید.
)( usb_rcvbulkpipeو )( usb_sndbulkpipeو سایر ماکروهای دیگر که نیز در هدر فایل فببوق
تعریف شددهاند .بیت شاخص Endpoint ،برای اتصال به APIهای هسته USBرا محاسبه میکند.
یرود مجمببوعدهای از
نکته اینکه درایور قلم نببوری بببه کلس mass storageتعلببق دارد کببه انتظببار م د
دستورا ت شبیه SCSIبرای انتقال انبوه اطلعا ت استفاده شود .بنابراین یک خواندن/نوشتن ناقصی که در کببد
یرود داددهای را جابجا نکند .مگر اینکه دستورا ت با فرمت
زیر آمده است ممکن است واقعا ًا آنطوری که انتظار م د
مناسب باشد .اما کد زیر یک خلصدهای از جریان کلی درایور Usbاست .بببرای دسببتیابی بببه حببس واقعببی و
زیبای کار با انتقال اطلعا ت باید با دستگاههای USBمختلف شبیه آنچیزی که در نشانی زیر میباشد دست و
پنجه نرم کنید:
http://lddk.esrijan.com/
و اما کد زیر :
صفحه86
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
87صفحه
if (copy_to_user(buf, bulk_buf, MIN(cnt, read_cnt)))
{
return -EFAULT;
}
return wrote_cnt;
}
88صفحه
.write = pen_write,
};
device = interface_to_usbdev(interface);
class.name = "usb/pen%d";
class.fops = &fops;
if ((retval = usb_register_dev(interface, &class)) < 0)
{
/* Something prevented us from registering this driver */
err("Not able to get a minor for this device.");
}
else
{
printk(KERN_INFO "Minor obtained: %d\n", interface->minor);
}
return retval;
}
89صفحه
{} /* Terminatingentry */
};
MODULE_DEVICE_TABLE (usb, pen_table);
module_init(pen_init);
module_exit(pen_exit);
MODULE_LICENSE("GPL");
90صفحه
;)">MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com
;)"MODULE_DESCRIPTION("USB Pen Device Driver
صفحه91
لچهاردهم:آشنایی با مبانی دیسک سخت و پارتیشن
فص د
اگر دستور fdisk -lرا صادر کنیم شکلی شبیه زیر را خواهیم دید:
صفحه92
یشوند و معموا ًل حجم آنها 512بایت میباشد.
نها نیز با S1,S2,...S63نامگذاری م د
63عدد میباشد که آ د
با استفاده از اطلعا ت فوق و با فرمول زیر میتوانیم حجم واقعی قابل استفاده از دیسک را بدست بیاوریم:
)حجم هر سکتور(* )تعداد سکتور در هر قطاع( * )تعداد قطاع در هر دیسک(* )تعداد دیسک(=حجم واقعی قابل استفاده
ممکن است عدد شما کمی کمتر از عدد واقعی )عدد 500107862016در مثال ما( باشد و دلیل آن هم ایببن
است که فرمول ما بایتهای سیلندرهای ناقص را محاسبه نمیکند .و علت اصلی آن هم اختلف تکنولوژی امببروز
یترکه از هد و سیلندر و بخش استفاده میشده است بازمیگردد .
کها با زمانهای قدیم د
ساخت و دیس د
شها و قطاع های منطقی را برمیگرداند و نه لزوما ًا فیزیکببی.
نکته اینجاست که خروجی fdiskبه ما هدها و بخ د
سوالی که ممکن است مطرح شود این است که اگر دیسکهای امروزی مفاهیم هندسه فیزیکببی قببدیم را ندارنببد
یشوند؟
پس چرا هنوز هستند و بصور ت منطقی نمایش داده م د
دلیل اصلی این است که باز هم بتوانیم با مفهوم پارتیشن کار کنیم و همچنان جدول پارتیشن را داشببته باشببیم
مخصوصا ًا جداول پارتیشن نوع DOSکه خیلی رایج بوده و به هندسه ساده فوق نزدیک هستند.
فرمول محاسبا ت سایز سیلندر بصور ت زیر میباشد:
که در خط سوم شکل فوق مشاهده میکنیم و در خطوط بعد علمتگببذاری پارتیشببن هببا در unitهببا بصببور ت
سیلندرهای کامل را ملحظه میفرمایید.
صفحه93
های مختلف ،اطلعا ت کاربران ،داده های موقت و غیره.
مبندی منطقی برای هارد دیسک
بنابراین اگر بخواهیم پارتیشن ها را بصور ت ساده بیان کنیم آنها یک نوع تقسی د
هستند .اطلعا ت مربوط به پارتیشنها نیز باید جایی نگهداری شود که به آن جدول پارتیشن گویند .یک جببدول
پارتیشن از نوع DOSحداکثر چهار ورودی پارتیشن دارد که هرکدام 16بایت هستند .با ساختار به زبببان C
نها را مجسم کرد:
زیر میتوان آ د
typedef struct
{
)unsigned char boot_type; // 0x00 - Inactive; 0x80 - Active (Bootable
;unsigned char start_head
;unsigned char start_sec:6
;unsigned char start_cyl_hi:2
;unsigned char start_cyl
;unsigned char part_type
;unsigned char end_head
;unsigned char end_sec:6
;unsigned char end_cyl_hi:2
;unsigned char end_cyl
;unsigned long abs_start_sec
;unsigned long sec_in_part
;} PartEntry
این جدول پارتیشن ،پس از یک امضاء دو بایتی 0xAA55در انتهای اولین سکتور دیسک قرار میگیرد
که معموا ًل با نام رکورد بو ت اصلی ) ( MBRشناخته میشود .بنابراین شروع آفسببت ایببن جببدول پارتیشببن در
یشود .همچنین یک 4بایتی امضاء دیسک در آفست
داخل MBRبصور ت 446 = (2 + 16 * 4) - 512م د
440قرار میگیرد.
440بایت قبل از MBRمعموا ًل برای اولین قسمت کد بو ت که توسط بایوس برای راه اندازی سیستم از روی
دیسک سخت استفاده میگردد .سورس فایل part_info.cشامل این تعاریف مختلف برای تجزیه و تحلیل و
نمایش فرمت بندی شده جدول پارتیشن بروی خروجی نوشته شده است.
از مقادیر ساختار جدول پارتیشن ،میتوان به فیلدهای شروع و خاتمه سیلندر که فقط 10بیت طول دارند اشبباره
کردکه حداکثر فقط میتوان 1023سیلندر داشت .اما برای هارد دیسکهای حجیببم امببروزی ایببن مقببدار کببافی
نیست بنابراین در حالتهای اینچنینی ،سه تایی »هد ،سیلندر ،سکتور« در جدول پارتیشن برای حببداکثر مقببدار
صفحه94
:یشود
یشود و مقدار واقعی با استفاده از دو فیلد زیر محاسبه م د
ثبت م د
(sec_in_part) ( و تعداد سکتور در این پارتیشنabs_start_sec) تعداد سکتور شروع
: بصور ت زیر استpart_info.c سورس کد فایل
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct {
unsigned char boot_type; // 0x00 - Inactive; 0x80 - Active (Bootable)
unsigned char start_head;
unsigned char start_sec:6;
unsigned char start_cyl_hi:2;
unsigned char start_cyl;
unsigned char part_type;
unsigned char end_head;
95صفحه
unsigned char end_sec:6;
unsigned char end_cyl_hi:2;
unsigned char end_cyl;
unsigned long abs_start_sec;
unsigned long sec_in_part;
} PartEntry;
typedef struct {
unsigned char boot_code[MBR_DISK_SIGNATURE_OFFSET];
unsigned long disk_signature;
unsigned short pad;
unsigned char pt[PARTITION_TABLE_SIZE];
unsigned short signature;
} MBR;
if (argc == 2) {
dev_file = argv[1];
}
96صفحه
if ((fd = open(dev_file, O_RDONLY)) == -1) {
fprintf(stderr, "Failed opening %s: ", dev_file);
perror("");
return 1;
}
if ((rd_val = read(fd, &m, sizeof(m))) != sizeof(m)) {
fprintf(stderr, "Failed reading %s: ", dev_file);
perror("");
close(fd);
return 2;
}
close(fd);
printf("\nDOS type Partition Table of %s:\n", dev_file);
printf(" B Start (H/C/S) End (H/C/S) Type StartSec TotSec\n");
for (i = 0; i < 4; i++) {
printf("%d:%d (%3d/%4d/%2d) (%3d/%4d/%2d) %02X %10d %9d\n",
i + 1, !!(p[i].boot_type & 0x80),
p[i].start_head,
1 + ((p[i].start_cyl_hi << 8) | p[i].start_cyl),
p[i].start_sec,
p[i].end_head,
1 + ((p[i].end_cyl_hi << 8) | p[i].end_cyl),
p[i].end_sec,
p[i].part_type,
p[i].abs_start_sec, p[i].sec_in_part);
}
printf("\nRe-computed Partition Table of %s:\n", dev_file);
printf(" B Start (H/C/S) End (H/C/S) Type StartSec TotSec\n");
for (i = 0; i < 4; i++) {
printf("%d:%d ", i + 1, !!(p[i].boot_type & 0x80));
print_computed(p[i].abs_start_sec);
printf(" ");
print_computed(p[i].abs_start_sec + p[i].sec_in_part - 1);
printf(" %02X %10d %9d\n", p[i].part_type,
97صفحه
;)p[i].abs_start_sec, p[i].sec_in_part
}
;)"printf("\n
;return 0
}
کد بال یک برنامه است که میتوان با gcc part_info.c -o part_infoکامپایل کببرد و سببپس بببا
./part_info /dev/sdaآنرا اجرا نمود تا اطلعا ت پارتیشن اصلی تان یعنی /dev/sdaرا ببینید.
شکل زیر خروجی این برنامه را بروی سیستم من نمایش میدهد که میتوانید خروجی آنببرا بببا خروجببی fdisk
ای که در چند صفحه قبل دیدید ،مقایسه نمایید.
صفحه98
یشوند و از اینرو براساس سیستم عاملهایی مانند DOS, Minix, Linux,
سازندگان سیستم عاملها ابداع م د
یشوند تا
Solaris, BSD, FreeBSD, QNX, W95, Novell Netwareو غیره دسته بندی م د
برای/با چندین سیستم عامل استفاده شوند.
یکی از چهار پارتیشن اصلی میتواند چیز دیگری نامگذاری شود که به آن پارتیشن تعمیم داده شده گوینببد و از
اهمیت ویژه ای نیز برخوردار است) .(extended partitionاز نامش پیدا است که برای تعمیم بیشببتر،
نهبا
ل تقسیما ت دیسک مورد استفاده قرار میگیرد تا پارتیشنهای بیشتری داشته باشببیم.ببه عببار ت دیگبر آ د
مث ا ً
پارتیشنهای منطقی هستند که در درون یک پارتیشن اصلی تعمیم داشته شده اند .اطلعا ت این پارتیشن هببا در
یک لینک لیست حفظ میشود) ( linked-listو اجازه میدهد که تعداد بیشماری )حببداقل از نظببر تئوری( از
پارتیشنهای منطقی داشته باشیم.
اولین سکتور پارتیشن تعمیم داده شده معموا ًل رکورد بو ت نامیده میشود) (BR=boot recoardکه بببرای
ذخیره پارتیشن های منطقی مورد استفاده قرار میگیرد دقیقا ًا شبیه MBRکه برای ذخیره )ابتدای لینک لیست(
جدول پارتیشن استفاده میگردید.
گردههای بعدی لینک لیست در اولین سکتور پارتیشن های منطقی بعدی که بببه بببو ت رکببورد منطقببی )(LBR
اشاره میکند ،ذخیره میشوند .هر گره از لینک لیست 4مدخل کامل جدول پارتیشببن دارد امببا تنهببا دو مببدخل
ابتدایی مورد استفاده قرار میگیرد که اولی برای مقدار لینک لیست یعنی اطلعببا ت پارتیشببن منطقببی مجبباور ،و
دومی اشاره گر بعدی لینک لیست که به لیست پارتیشنهای منطقی باقیمانده اشاره میکند.
برای مقایسه و فهم بیشتر جزئیا ت پارتیشن اصلی بروی دیسک سخت سیستمتان ،مراحل زیر را انجببام دهیببد
)البته با دقت و با دسترسی یوزر :(root
الن شما با انتخاب و با دقت با دیسک سخت سیستم خود کلنجار رفتدهاید )البته بصور ت فقط خواندنی(.چرا بببا
دقت؟ چون در غیر اینصور ت ممکن بود سیستم شما دیگر قابل بو ت نباشد .اما هیچ یادگیری بدون اکتشببافاتی
کامل نمیشود .بنابراین در فصل بعدی ما یک دیسک مصنوعی در ramمیسازیم و خراب میکنیم.
صفحه99
RAM ساخت یک دیسک در حافظه:لپانزدهم
فص د
بسازیم که درونش شش فایل که شاملDiskOnRam در این فصل درابتدا میخواهیم شاخه ای بنام
: محتوای فایلها به قرار زیر میباشد. قرارداردMakefile دو هدر فایل و یکC ل برنامه
ِ سه فای
partition.h
#ifndef PARTITION_H
#define PARTITION_H
#include <linux/types.h>
partition.c
#include <linux/string.h>
#include "partition.h"
100صفحه
#define MBR_SIGNATURE 0xAA55
#define BR_SIZE SECTOR_SIZE
#define BR_SIGNATURE_OFFSET 510
#define BR_SIGNATURE_SIZE 2
#define BR_SIGNATURE 0xAA55
typedef struct
{
unsigned char boot_type; // 0x00 - Inactive; 0x80 - Active (Bootable)
unsigned char start_head;
unsigned char start_sec:6;
unsigned char start_cyl_hi:2;
unsigned char start_cyl;
unsigned char part_type;
unsigned char end_head;
unsigned char end_sec:6;
unsigned char end_cyl_hi:2;
unsigned char end_cyl;
unsigned long abs_start_sec;
unsigned long sec_in_part;
} PartEntry;
101صفحه
start_sec: 0x2,
start_cyl: 0x00,
part_type: 0x83,
end_head: 0x00,
end_sec: 0x20,
end_cyl: 0x09,
abs_start_sec: 0x00000001,
sec_in_part: 0x0000013F
},
{
boot_type: 0x00,
start_head: 0x00,
start_sec: 0x1,
start_cyl: 0x0A, // extended partition start cylinder (BR
location)
part_type: 0x05,
end_head: 0x00,
end_sec: 0x20,
end_cyl: 0x13,
abs_start_sec: 0x00000140,
sec_in_part: 0x00000140
},
{
boot_type: 0x00,
start_head: 0x00,
start_sec: 0x1,
start_cyl: 0x14,
part_type: 0x83,
end_head: 0x00,
102صفحه
end_sec: 0x20,
end_cyl: 0x1F,
abs_start_sec: 0x00000280,
sec_in_part: 0x00000180
},
{
}
};
static unsigned int def_log_part_br_cyl[] = {0x0A, 0x0E, 0x12};
static const PartTable def_log_part_table[] =
{
{
{
boot_type: 0x00,
start_head: 0x00,
start_sec: 0x2,
start_cyl: 0x0A,
part_type: 0x83,
end_head: 0x00,
end_sec: 0x20,
end_cyl: 0x0D,
abs_start_sec: 0x00000001,
sec_in_part: 0x0000007F
},
{
boot_type: 0x00,
start_head: 0x00,
start_sec: 0x1,
start_cyl: 0x0E,
103صفحه
part_type: 0x05,
end_head: 0x00,
end_sec: 0x20,
end_cyl: 0x11,
abs_start_sec: 0x00000080,
sec_in_part: 0x00000080
},
},
{
{
boot_type: 0x00,
start_head: 0x00,
start_sec: 0x2,
start_cyl: 0x0E,
part_type: 0x83,
end_head: 0x00,
end_sec: 0x20,
end_cyl: 0x11,
abs_start_sec: 0x00000001,
sec_in_part: 0x0000007F
},
{
boot_type: 0x00,
start_head: 0x00,
start_sec: 0x1,
start_cyl: 0x12,
part_type: 0x05,
end_head: 0x00,
end_sec: 0x20,
104صفحه
end_cyl: 0x13,
abs_start_sec: 0x00000100,
sec_in_part: 0x00000040
},
},
{
{
boot_type: 0x00,
start_head: 0x00,
start_sec: 0x2,
start_cyl: 0x12,
part_type: 0x83,
end_head: 0x00,
end_sec: 0x20,
end_cyl: 0x13,
abs_start_sec: 0x00000001,
sec_in_part: 0x0000003F
},
}
};
105صفحه
MBR_SIGNATURE;
}
static void copy_br(u8 *disk, int start_cylinder, const PartTable
*part_table)
{
disk += (start_cylinder * 32 /* sectors / cyl */ * SECTOR_SIZE);
memset(disk, 0x0, BR_SIZE);
memcpy(disk + PARTITION_TABLE_OFFSET, part_table,
PARTITION_TABLE_SIZE);
*(unsigned short *)(disk + BR_SIGNATURE_OFFSET) =
BR_SIGNATURE;
}
void copy_mbr_n_br(u8 *disk)
{
int i;
copy_mbr(disk);
for (i = 0; i < ARRAY_SIZE(def_log_part_table); i++)
{
copy_br(disk, def_log_part_br_cyl[i], &def_log_part_table[i]);
}
}
ram_device.h
#ifndef RAMDEVICE_H
#define RAMDEVICE_H
106صفحه
extern int ramdevice_init(void);
extern void ramdevice_cleanup(void);
extern void ramdevice_write(sector_t sector_off, u8 *buffer, unsigned int sectors);
extern void ramdevice_read(sector_t sector_off, u8 *buffer, unsigned int sectors);
#endif
ram_device.c
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/string.h>
#include "ram_device.h"
#include "partition.h"
int ramdevice_init(void)
{
dev_data = vmalloc(RB_DEVICE_SIZE * RB_SECTOR_SIZE);
if (dev_data == NULL)
return -ENOMEM;
/* Setup its partition table */
copy_mbr_n_br(dev_data);
return RB_DEVICE_SIZE;
}
void ramdevice_cleanup(void)
{
107صفحه
vfree(dev_data);
}
ram_block.c
/* Disk on RAM Driver */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/errno.h>
#include "ram_device.h"
#define RB_FIRST_MINOR 0
#define RB_MINOR_CNT 16
/*
* The internal structure representation of our Device
*/
108صفحه
static struct rb_device
{
/* Size is the size of the device (in sectors) */
unsigned int size;
/* For exclusive access to our request queue */
spinlock_t lock;
/* Our request queue */
struct request_queue *rb_queue;
/* This is kernel's representation of an individual disk device */
struct gendisk *rb_disk;
} rb_dev;
/*
* Actual Data transfer
*/
static int rb_transfer(struct request *req)
109صفحه
{
//struct rb_device *dev = (struct rb_device *)(req->rq_disk->private_data);
sector_t sector_offset;
unsigned int sectors;
u8 *buffer;
int ret = 0;
sector_offset = 0;
rq_for_each_segment(bv, req, iter)
{
buffer = page_address(bv->bv_page) + bv->bv_offset;
if (bv->bv_len % RB_SECTOR_SIZE != 0)
{
printk(KERN_ERR "rb: Should never happen: "
"bio size (%d) is not a multiple of RB_SECTOR_SIZE (%d).\n"
"This may lead to data truncation.\n",
bv->bv_len, RB_SECTOR_SIZE);
ret = -EIO;
}
sectors = bv->bv_len / RB_SECTOR_SIZE;
printk(KERN_DEBUG "rb: Sector Offset: %lld; Buffer: %p; Length: %d
sectors\n",
110صفحه
sector_offset, buffer, sectors);
if (dir == WRITE) /* Write to the device */
{
ramdevice_write(start_sector + sector_offset, buffer, sectors);
}
else /* Read from the device */
{
ramdevice_read(start_sector + sector_offset, buffer, sectors);
}
sector_offset += sectors;
}
if (sector_offset != sector_cnt)
{
printk(KERN_ERR "rb: bio info doesn't match with the request info");
ret = -EIO;
}
return ret;
}
/*
* Represents a block I/O request for us to execute
*/
static void rb_request(struct request_queue *q)
{
struct request *req;
int ret;
111صفحه
* - one that moves block of data
*/
if (!blk_fs_request(req))
{
printk(KERN_NOTICE "rb: Skip non-fs request\n");
/* We pass 0 to indicate that we successfully completed the request
*/
__blk_end_request_all(req, 0);
//__blk_end_request(req, 0, blk_rq_bytes(req));
continue;
}
#endif
ret = rb_transfer(req);
__blk_end_request_all(req, ret);
//__blk_end_request(req, ret, blk_rq_bytes(req));
}
}
/*
* These are the file operations that performed on the ram block device
*/
static struct block_device_operations rb_fops =
{
.owner = THIS_MODULE,
.open = rb_open,
.release = rb_close,
};
/*
* This is the registration and initialization section of the ram block device
* driver
*/
static int __init rb_init(void)
{
112صفحه
int ret;
/* Get Registered */
rb_major = register_blkdev(rb_major, "rb");
if (rb_major <= 0)
{
printk(KERN_ERR "rb: Unable to get Major Number\n");
ramdevice_cleanup();
return -EBUSY;
}
/*
* Add the gendisk structure
* By using this memory allocation is involved,
* the minor number we need to pass bcz the device
* will support this much partitions
113صفحه
*/
rb_dev.rb_disk = alloc_disk(RB_MINOR_CNT);
if (!rb_dev.rb_disk)
{
printk(KERN_ERR "rb: alloc_disk failure\n");
blk_cleanup_queue(rb_dev.rb_queue);
unregister_blkdev(rb_major, "rb");
ramdevice_cleanup();
return -ENOMEM;
}
114صفحه
return 0;
}
/*
* This is the unregistration and uninitialization section of the ram block
* device driver
*/
static void __exit rb_cleanup(void)
{
del_gendisk(rb_dev.rb_disk);
put_disk(rb_dev.rb_disk);
blk_cleanup_queue(rb_dev.rb_queue);
unregister_blkdev(rb_major, "rb");
ramdevice_cleanup();
}
module_init(rb_init);
module_exit(rb_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <[email protected]>");
MODULE_DESCRIPTION("Ram Block Driver");
MODULE_ALIAS_BLOCKDEV_MAJOR(rb_major);
( )ببباdor.ko) بنویسید و اجرا کنیدram را برای ساخت درایور دیسک در حافظهmake مانند گذشته کد
( c ترکیب به فایل
: را میتوان بصور ت زیر نوشتMakefile
Makefile
# If called directly from the command line, invoke the kernel build system.
ifeq ($(KERNELRELEASE),)
KERNEL_SOURCE := /usr/src/linux
PWD := $(shell pwd)
115صفحه
default: module
module:
$(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=$(PWD) modules
clean:
$(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=$(PWD) clean
. را طبق معمول اجرا کنیدmake clean برای حذف فایل اجرایی ساخته شده نیز میتوانید دستور
:تجربه اجرای درایور فوق در سیستم من را بصور ت شکلهای زیر مشاهده میفرمایید
116صفحه
صفحه117
صفحه118
توجه داشته باشید که تمام مراحل زیر میبایست با مجوز rootاجرا گردد:
• لود کردن درایور dor.koدر کرنل با استفاده از دستور . insmodاین دستور میبایست یک فایببل
دستگاه بلکی را ایجاد کند که نمایانگر دیسکی با ظرفیت 512کیلوبایت در حببافظه ramاسببت کببه
حاوی سه پارتیشن اصلی و سه پارتیشن منطقی میباشد.
یشوند که /dev/rbکببل دیسببک
• فایلهای دستگاه بلوکی )* (/dev/rb/بصور ت اتوماتیک ساخته م د
است که 512کیلوبایت حجم دارد rb1.و rb2و rb3سه پارتیشن اصلی اسببت کببه rb2پارتیشببن
توسعه داده شده میباشد و خود دارای سه پارتیشن منطقی rb5و rb6و rb7نیز میباشد.
• با استفاده از دستور ddکل دیسک را میتوانید بخوانید.
• اولین سکتور از اولین پارتیشن دیسک ) (/dev/rb1را با صفر پرکرده و دوببباره دسببتور ddرا اجببرا
کنید
• با استفاده از دستور catمتنی را در اولین پارتیشن ) (/dev/rb1بنویسید
• با استفاده از دستور xxdمحتوی اولیه اولین پارتیشن ) (/dev/rb1را ببینید
• با استفاده از دستور fdiskاطلعا ت پارتیشن را ببینید
صفحه119
• با استفاده از mkfs.vfatسبومین پارتیشبن اصبلی ) (/dev/rb3را از نبوع فایبل سیسبتم vfat
فرمت بندی سریع کنید)شبیه کاری که برای درایور قلم نوری انجام دادیم(
• با استفاده از دستور ، mountپارتیشن فرمت شده جدید را در /mntمونت ) (mountکنید.
• با استفاده از دستور dfحجم پارتیشن مونت شده را ببینید .شما ممکن حتی از این هم فراتببر برویببد و
فایلی را در این پارتیشن ذخیره کنید ولی یادتان باشیدکه این دیسک در حافظه RAMاست و دائمببی
نمیباشد.
• بعد از آنکه با استفاده از دستور umount /mntپارتیشن را غیر فعال کردید با استفاده از دستور
rmmod dorدرایور را از کرنل حذف کنید .با اینکار کل اطلعا ت ذخیره شده در این دیسک از
دست خواهد رفت.
صفحه120
• عملیا ت فایلی بروی فایلهای دستگاه میسر میباشد
نها وجود دارد
• مفهوم ثبت دستگاه نیز در آ د
به دلیل فوق پیاددهسازی این دو نوع درایور نیز با هم متفاو ت است .حال بیایید با هم قطعاتی از کببدهای
اصلی ram_block.cرا آنالیز کنیم .برای شروع از سازنده درایور )( rb_initشروع میکنیم.
اولین گام ثبت 8بیت )بل ک( شماره اصلی است) .که بطور ضمنی به معنای ثبت همببه 256شببماره فرعببی 8
بیتی به همراه آن است( تابع زیر این کار را انجام میدهد:
در اینجا majorشببمار اصببلی اسببت کببه ثبببت شببده اسببت و nameنببامی اسببت کببه در زیببر شبباخه
یشود .جالب توجه اینکه تابع )( register_blkdevسعی میکنببد تببا
/proc/devicesنمایش داده م د
یشود ،در حالتی کببه
یک شماره اصلی خالی را بگیرد و ثبت کند .وقتی 0برای اولین پارامتر majorارسال م د
موفقیت آمیز باشد عدد در نظر گرفته شده بازگردانده میشود .تابع معکوس ثبت نیز متناظر با تببابع ثبببت در
زیر آمده است:
صفحه121
گام دوم تعریف عملیا ت فایل است که از طریق ساختار ) block_device_operationsدر هدر فایل
لهای دستگاه ثبت انجام میگیرد.
> <linux/blkdev.hمعرفی گردیده است( برای شماره اصلی فای د
اما این عملیا ت در مقایسه با عملیا ت فایل دستگادههای کاراکتری چندین برابر کمتر میباشد و معمببوا ًل کوچببک
نیز هستند .حتی بصور ت حرفدهای نیز هیچ عملیاتی )حتی برای خواندن و نوشتن( وجود نداردکه شببگفت انگیببز
است .دلیل آن این است که درایورهای بلکی با I/Oزمانبدی شده یکپارچه شددهاند و پیاددهسببازی خوانببدن و
نوشتن از طریق درخواست فراخوانی از صف بدست می آید .همراه با انجام عملیا ت فایل دستگاه باید کارهببای
زیر انجام شود:
در خواست صف برای مرتب کردن درخواستهای خواندن/نوشتن •
ف درخواست مربوطه
ایجاد یک قفل ) (lockبرای جلوگیری از دسترسی همزمان به ص ِ •
تها در صف درخواست
ایجاد تابع درخواست برای پردازش درخواس د •
همچنین اینترفیس مجزایی برای ساخت فایلهای بلکی نیز وجود ندارد .بنابراین کارهای زیر انجام میشود:
پیشوند نام فایل دستگاه که معموا ًل به disk_nameباز میگردد ) rbدر درایور (dor •
رشته عدد فرعی برای فایلهای دستگاه که معموا ًل به first_minorباز میگردد. •
در )( add_diskقبلی چندیدن فیلد ساختار gendiskهست که مستقیما ًا یببا بببا اسببتفاده از ماکروهببا یببا
صفحه122
عای شبببیه set_capacity().major,first_minor,fops,ueue,disk_nameحببداقل
تببواب د
فیلدهای مورد نیاز آن باید مستقیما ًا مقدار دهی شوند .قبل از مقدار دهی این فیلدها ،ساختار gendiskباید
حافظه را در اختیار بگیرید که توسط تابع زیر اینکار انجام میشود:
که minorsتعداد کل پارتیشن ها برای این دیسک میباشد ،برعکس تابع فوق نیز در زیر آمده است:
ما تابع پردازش درخواست و راه اندازی قفل ) (lockو محافظت از دسترسی موازی را در پارامتر گنجاندیم.
تابع پا ک کردن صف مربوطه را در زیر مشاهده میکنید:
صفحه123
سپس یا باید پردازش شود یا اینکه یک پردازش را شروع کند .و همه چیز به جز بل ک کببردن از یببک تببابع و
یشود .بعلوه توابعی که صببف را قفببل و یببا آزاد
پس از قفل کردن صف ،درخواست غیر فرآیندی فراخوانی م د
یکند باید در تابع درخواست استفاده گردد.
نم د
یک مثال معمولی فرآیند درخواست بوسیله تابع )( rb_requestدر فایل ram_block.cمببورد نمببایش
قرار گرفته است:
/* Informing that the request has been processed with return of ret */
;)__blk_end_request_all(req, ret
}
صفحه124
چرخش کند و اطلعا ت بافر منحصببر بفببرد را در سبباختار bio_vec (bv: basic input/output
( vectorدر هر چرخش استخراج کند و سپس در هر استخراج داددههای مناسب ،بسببته بببه نببوع عملیببا ت
منتقل شوند که با استفاده از فراخوانی APIهای زیر در فایل ram_device.cاینکار انجام میشود:
در پایان پیشنهاد میگردد تمام کد )( rb_transferدر فایل ram_block.cرا حتما ًا بررسی نمایید.
صفحه125
لشانزدهم:کاوش در کرنل و آشنایی با /proc
فص د
در یک نگاه به درون پوشه /procهر زیر شاخه اطلعا ت زیر را به ما میدهد:
• /proc/modulesماژول هایی که بصور ت پویا بار گذاری شددهاند
• /proc/devicesشماره اصلی دستگادههای بلکی و کاراکتری ثبت شده
سهای باس دستگاه
• /proc/iomemحافظه اصلی و آدر د
• /proc/ioportsآدرس پورتها ورودی/خروجی بروی سیستم
• /proc/interruptsشماره های وقفه های ثبت شده
• /proc/softirqsنمایانگر IRQهای ثبت شده است
لهای لود شده
ل در حال اجرا به همراه ماژو د
لهای کرن ِ
• /proc/kallsymsسیمب د
نهایشان
• /proc/partitionsدستگاههای بلکی متصل شده به همراه پارتیش د
• /proc/filesystemsدرایور های فایل سیستم فعال فعلی
• /proc/swapsنمایانگر swapهای فعال فعلی است
• /proc/cpuinfoاطلعاتی در مورد سی پی یو )ها(ی سیستم
• /proc/meminfoاطلعاتی در مورد حافظه اصلی بروی سیستم
>#include <linux/module.h
>#include <linux/kernel.h
>#include <linux/proc_fs.h
>#include <linux/jiffies.h
صفحه126
int time_read(char *page, char **start, off_t off, int count, int *eof, void *data) {
int len, val;
unsigned long act_jiffies;
return len;
}
int time_write(struct file *file, const char __user *buffer, unsigned long count, void
*data) {
if (count > 2)
127صفحه
return count;
if ((count == 2) && (buffer[1] != '\n'))
return count;
if ((buffer[0] < '0') || ('9' < buffer[0]))
return count;
state = buffer[0] - '0';
return count;
}
128صفحه
;)module_init(proc_win_init
;)module_exit(proc_win_exit
;)"MODULE_LICENSE("GPL
;)">MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com
;)"MODULE_DESCRIPTION("Kernel window /proc Demonstration Driver
صفحه129
create_proc_entryساختیم.
• یک لینک نرم ) (Soft linkبه نام rel_time_lبه فایل rel_timeدر شاخه مشابه و با استفاده
از )( proc_symlinkساختیم.
تببابع تخریبگببر )( remove_proc_entryمرتبببط بببا چیزهببایی کببه سبباخته بببودیم و تببابع )(
proc_win_exitمد ت زمان درخواست را نمایان میسازد
برای هر مدخلی که در /procایجاد کردیم ،یک ساختار مرتبط proc_dir_entrرا هم ساختیم.
نکته :برای هر کدام چند فیلد باید بروزرسانی شود:
• modeمجوز دسترسی فایل
• uidشناسه یکتای کاربر فایل
• gidشناسه یکتای گروه فایل
بعلوه برای فایل معمولی دو اشاره گر تابع برای خواندن و نوشتن فایل باید معرفی شود :
)int (*read_proc)(char *page, char **start, off_t off, int count, int *eof, void *data
int (*write_proc)(struct file *file, const char __user *buffer, unsigned long count,
)void *data
)( write_procخیلی شبیه به )( writeدر درایورهای کاراکتری است .در پیاده سازی تببابع فببوق ،کبباربر
میتواند از عدد 0تا 9را در فایل بنویسد و مطابق آن وضعیت داخلی را تنظیم میکند.
)( read_procدر پیاددهسازی فوق ،وضعیت جاری و مد ت زمانی که از بببو ت شببدن سیسببتم میگببذارد را
بسته به وضعیت جاری در واحدهای مختلف نمایش میدهد .لحظه برای وضعیت ، 0میلی ثانیه برای وضعیت
، 1ثانیه و میلی ثانیه برای وضعیت ، 2ساعت ،دقیقه و ثانیه برای وضببعیت 3و بببرای سببایر مقببادیر چیببزی
پیاددهسازی نشده است.
دقت محاسبا ت را حتما ًا چک کنید .شکل زیر مقدار زمان روشن بودن سیستم را در خروجی دستور topنشان
میدهد.
صفحه130
تمام ساختارها و معرفی توابع مرتبط با /procدر هدر فایل > <linux/proc_fs.hتعریف شده است.
معرفی توابع و ماکروهای مرتبط با زمان سیستم هم در هدر فایل > <linux/jiffies.hتعریببف شببده اسببت.
نکتبببه :زمانهبببای واقعبببی بوسبببیله تفریبببق INITIAL_JIFFIESمحاسببببه میشبببود .در موقبببع ببببو ت
INITIAL_JIFFIESصفرمیشود و از آن به بعد زمان در INITIAL_JIFFIESقببرار میگببرد و در نهببایت
ساخت شاخه با نام anilفقط یک نام است که شما میتوانید هر نامی که دوست دارید را انتخاب کده و بجای
آن استفاده کنید.
صفحه131
لهفدهم:چگونگی تعامل بین ماژولها
فص د
در این فصل میخواهیم چگونگی تعامل بین ماژولها )هم درایورهای قابل لود و هم درایورهببای غیببر قابببل
لود( از قبیل دسترسی به متغیرها ،فراخوانی توابع و پاس کردن پارامترها را بررسی خواهیم کرد.
)EXPORT_SYMBOL(sym
)EXPORT_SYMBOL_GPL(sym
)EXPORT_SYMBOL_GPL_FUTURE(sym
شفببرض
نها در یکببی از پی د
یشود وآ د
در هر کدام ازماکروهای فوق symbol ،بعنوان پارامتر پاس م د
نهبا ببرای
شهبای _ gplیبا _ gpl_futureقبرار میگیرنبد .از اینبرو فقبط یکبی از آ د
ها ببه ترتیبب در بخ د
symbolخاص مورد نیاز است و symbolمیتواند هم نام متغییر و هم نام تابع باشد .برای مثال کد زیببر
یعنی فایل our_glob_syms.cرا بررسی میکنیم:
>#include <linux/module.h
>#include <linux/device.h
صفحه132
static struct class *cool_cl;
static struct class *get_cool_cl(void)
{
return cool_cl;
}
EXPORT_SYMBOL(cool_cl);
EXPORT_SYMBOL_GPL(get_cool_cl);
module_init(glob_sym_init);
module_exit(glob_sym_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs.com>");
MODULE_DESCRIPTION("Global Symbols exporting Driver");
شده باشد یک ساختار مرتبببط بببا خببود دارد کببه در بخشببهای جببدولexport کهsymbol هر
__) کرنببلCRC ( و جببدولkstrtab__) جببدول رشببته کرنببل، (ksymtab__) کرنببلsymbol
133صفحه
نها مقدور است قرار دارند.
(kcrctabکه بصور ت سراسری دسترسی به آ د
شکل زیر یک قطعدهای از /proc/kallsymsرا قبل و بعد از لود شدن مبباژول our_glob_syms.ko
نشان میدهد که طبق معمول با یک Makefileکامپایل شده است:
کد زیر هد فایل our_glob_syms.hرا که باید بوسیله ماژولهببایی کببه symbolهببای cool_clو
get_cool_clرا که از exportاستفاده میکند را includeنماید:
#ifndef OUR_GLOB_SYMS_H
#define OUR_GLOB_SYMS_H
__#ifdef __KERNEL
>#include <linux/device.h
صفحه134
;extern struct class *cool_cl
;)extern struct class *get_cool_cl(void
#endif
#endif
شببکل فببوق همچنیببن فایببل Module.symversرا نشببان میدهببد کببه بوسببیله کامپایببل مبباژول
our_glob_symsساخته شده است .این فایل حاوی جزئیببا ت بیشببتر تمببام symbolهببای export
شده در شاخده میباشد .به جز includeشدن هدر فایل فوق ،ماژولهایی که از sysmbolهای export
شده استفاده میکنند باید در شاخه buildخودش فایل Module.symversرا داشته باشد.
نکته :هدر فایل > <linux/device.hدر مثال فوق با includeکردن چندین توصیف و تعریف مرتبط
نها را مورد بحث قرار دادیم.
ل در درایورهای کارکتری آ د
با کلس شروع شده است که قب ا ً
پارامترهای ماژول
میدانیم که در یک برنامدهی معمولی میتوان برنامه را با پارامترهایی فراخوانی کرد و به آرگومانهببایی کببه
برنامه با آن فراخوانده شده در داخل برنامه دسترسی داشت ولی آیا یک ماژول نیز اینگونه است؟
جواب مثبت است .پارامترها میتوانند در زمان لود شدن درایورها وقتی که از insmodاستفاده میکنیم به آن
لهای کرنببل حببتی
پاس شوند و شگفت انگیزاست که بگوییم برخلف آرگومان یک برنامدهی معمولی ،در ماژو د
میتوان بوسیله تعامل با sysfsاین پارامترها را تغییر داد.
پارامترهببای مبباژول بوسببیله مبباکروی زیببر)کببه در > <linux/moduleparam.hکببه از طریببق
یشود:
> <linux/module.hتعریف گردیده است( پیکربندی م د
که nameنام پارامتر type ،نوع پارامتر و permبه مجوز sysfsفایل مربببوط بببه ایببن پببارامتر ببباز
یشببوند byte, short, ushort, int, uint, long, ulong,
میگردد .نوع هایی که پشببتیبانی م د
charp (character pointer), boolیا (invbool (inverted Booleanهستند.
کد ماژول زیر module_param.cیک نمونه از پارامتر ماژول را نمایش میدهد:
>#include <linux/module.h
>#include <linux/kernel.h
صفحه135
static int cfg_value = 3;
module_param(cfg_value, int, 0764);
module_init(mod_par_init);
module_exit(mod_par_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <[email protected]>");
MODULE_DESCRIPTION("Module Parameter demonstration Driver");
136صفحه
شکل فوق تجربه من از کار با برنامه پارامتر ماژول را نمایش میدهد.
صفحه137
شکل فوق تجربه من در کار با پارامتر ماژول را با یوزر rootنمایش میدهد.
ل باید تعریببف شببود.
نکته قابل توجه اینکه قبل از مقداردهی پارامتر ،متغییری با نام مشابه و با نوع سازگار قب ا ً
سپس برای مشاهده خروجی های نمایش داده شده کارهای زیر را باید انجام دهید:
نکته ها
شفرض وقتی که پبارامتری را ببه مباژول پباس نکنیبم مقبدار ۳ببه متغییبر
مقدار دهی ) (3بصور ت پی د
یشود .
cfg_valueپاس م د
مجوز 0764به کاربر اجرا کنند مجوز ، rwxبه گببروه آن مجببوز -rwو بببه بقیببه مجببوز --rرا بببه فایببل
صفحه138
cfg_valueتحت پارامترهای module_paramدر زیر شاخه /sys/module/میباشد میدهد .
بررسی کنید
خروجی دستور dmesg |tailدر هببر insmodو rmmodبببرای خروجببی هببای printkی
موجود در برنامه را بررسی کنید.
سعی کنید در فایببل /sys/module/module_param/parameters/cfg_valueبببا یببک
یوزر معمولی بنویسید )به غیر از یوزر (rootو نتیجه را بررسی کنید.
موفق باشید.
صفحه139