139
م خدا به ناورها در لینوکس دیوایس درایز سایت دیوایس درایور اوعه مقالجمآوری شده از م گرد. linuxforu com ویسنده : نAnil Kumar Pugalia میل : ای@ - . email sarika pugs com ید مزرعه ملی ترجمه : حمیمیل : ای@ . hmmolaie gmail com بهار۱۳۹۲ 1 صفحه

دیوایس درایورها در لینوکس

Embed Size (px)

Citation preview

به نام خدا

دیوایس درایورها در لینوکس

linuxforu.گردآوری شده از مجموعه مقال ت دیوایس درایور از سایت com

Anil نویسنده : Kumar Pugalia @ایمیل : - .email sarika pugs com

ترجمه : حمید مزرعه ملیی@ایمیل : .hmmolaie gmail com

۱۳۹۲بهار

صفحه1

سرفصل3.............................................................................................................................................فصل اول : آشنایی با درایور

7........................................................................................................................فصل دوم : نوشتن اولین دیوایس درایور13..................................... برای نوشتن دیوایس درایور در لینوکس به ما میدهدcفصل سوم : امکانا ت اضافی که

18.........................................................................................................فصل چهارم : درایورهای کاراکتری در لینوکس24.........................................................................................................فصل پنجم: ساختن فایل دستگاههای کاراکتری

31............................................................................فصل ششم : خواندن و نوشتن بروی فایل دستگاههای کاراکتری36......................................................................................................فصل هفتم : دسترسی به سخت افزار در لینوکس

86x............................................................44فصل هشتم : دسترسی به پور ت ورودی/خروجی خاص در معماری 49..........................................................................................................فصل نهم : کنترل ورودی/خروجی در لینوکس

64...............................................................................................................فصل دهم : دیباگ کردن کرنل در لینوکس68...........................................................................................درلینوکس-قسمت اولUSBفصل یازدهم :درایورهاید‌لدوازدهم:درایورهای 76...........................................................................................درلینوکس-قسمت دومUSBفص

د‌لسیزدهم:انتقال اطلعا ت از/به دستگاه USB.....................................................................................................84فصد‌لچهاردهم:آشنایی با مبانی دیسک سخت و پارتیشن 92..........................................................................................فص

د‌لپانزدهم:ساخت یک دیسک در حافظه RAM...............................................................................................100فصد‌لشانزدهم:کاوش در کرنل و آشنایی با proc...............................................................................................126/فص

د‌لهفدهم:چگونگی تعامل بین ماژولها 132....................................................................................................................فص

صفحه2

فصل اول : آشنایی با درایورمقدمه

همانگونه که یک راننده، اتومبیل خود را میراند و کنترل میکند، یک دیوایس درایور نیز همین کار را بببا قسمتی از سخت افزار (مانند ماوس،صفحه کلید، وب کم و...) انجام میدهببد.هببر قسببمت از یببک سببخت افببزار میتواند با قطعه کدی که به آن «دیوایس درایور» گویندکنترل شود ویا حتی یک سببخت افببزار میتوانببد توسببط

سخت افزار دیگری که با دیوایس درایور کنترل میشود ، کنترل شود. همچنین هر دستگاه یک کنترل گر دارد (مانند کنترلرهارد، کنترلر صببفحه نمببایش و...) کببه بببه آن «دیببوایس

) تعامببل راIDE,PCI,USB SPI,L2Cکنترلر» گویند که این دیوایس کنترلرها با استفاده از «باس درایور» هببا(ماننببد ایجاد میکنند.

PCI مخصوص شان (مثل BUSکنترلرها معمول با استفاده از BUS یا IDE BUS و...) به CPUمتصل میتوان گفت که به علت کاهش هزینه و کوچک سازی،embededمیشوند.امروزه و در دنیای دستگاههای

BUS ها با CPUها در یک تراشه مجتمع شده اند. البته در صورتی کببه درایببور ببباس بببه گببونه ای خبباص طراحی نشده باشد ، ما نیازی نداریم که برای این نوع دستگاهها درایور های خود را تغییر دهیم.

قسمتهای مختلف یک درایورد‌طهای سخت افزاریی هستند که با استفاده از پروتکل مخصوص، ارتببباط بیببن سببخت باس داریورها راب

صفحه3

د‌مبندی در پایین لیه نرم افزاری یک سیستم عامل دیده میشوند. در این افزارها را فراهم میکنند.و از لحاظ تقسیتقسیم بندی بالی باس درایورها، دیوایس درایورها قرار میگیرند.

یک دیوایس درایور به دو بخش تقسیم میشود:د‌معاملهای مختلف مشابه میباشد و بیشتر کار ترجمه و تفسیر۱ - بخش سخت افزار: این بخش در سیست

د‌هها را انجام میدهد.یک دیتاشیت شامل مستندا ت و توضیحا ت فنببی د‌ت افزار به برنام د‌تهای سخ د‌هها و دیتاشی داد برای یک دستگاه سخت افزاری است که عملکرد،کارایی،چگونگی برنامه نویسی و... درمورد آن دسببتگاه سببخت

افزاری را توضیح میدهد. درادامه مثالهایی از چگونگی تفسیر این دیتاشیت ها را خواهیم آموخت. - بخش سیستم عامل:این بخش وظیفه برقراری ارتباط بین بخش اول (بخش سخت افزار) و سیستم۲

د‌لهبای عامل را بعهده دارد.با توجه به تفاوتهای بنیادین سیستم عاملها، بصور ت طبیعی این بخش در سیستم عام مختلف (از جمله ویندوز یا لینوکس ...) با یکدیگر متفاو ت خواهند بود.بنابراین بعنوان مثال نمیتوان یک داریببور

ویندوزی را در لینوکس و بالعکس اجرا کرد.

صفحه4

حوزه عمل یک درایورSystem یک دیوایس درایور در لینوکس، یک call.را برای کاربر مهیا میکند System call مرزی بین فضای کرنل و فضای کاربر میباشد

.

دربخش سیستم عامل،یک درایور به یکی از سه حوزه زیر میتواند تعلق بگیرد:- حوزه شبکه یا پکت گرا۱- حوزه رسانه های ذخیره سازی یا بل ک گرا۲- حوزه کاراکتر یا بایت گرا۳

کببار عبارتنببد از مببدیریت۵ کار مدیریتی را انجام میدهد. ایببن ۵لینوکس،مانند تمام سیستم عامل ها پردازش ، مدیریت حافظه ، مدیریت شبکه ، مدیریت رسانه های ذخیره کننده اطلعا ت و در نهایت مببدیریت سخت افزارهای ورودی و خروجی.اگرچه میتوان پردازش و حافظه را نیز بعنوان دیوایس درایور در نظر گرفت،

لیکن بدلیل نسبی بودن آنها و دلیلی دیگر آنها را بصور ت مجزا در نظر میگیریم. یک سیستم عامل یا بصور ت یکپارچه و غیر قابل تغییر اجرا میگردد یببا اینکببه یببک حببداقلی را دارد و میتوان آنرا حین اجرا قبض و بسط داد.به هر ترتیب اضافه کردن کد در حین اجببرا ، در دو حببوزه پببردازش و

د‌یهای امروزی به مراتب سخت و پیچیده میباشد.بنابراین اضافه و حذف کببردنCPUحافظه برای ها و معمار کد درحین اجرا برای این دو حوزه برعکس سه حوزه دیگر، تقریبا غیر ممکن است.پببس وقببتی در آینببده مببا از

صفحه5

دیوایس درایور میگوییم، منظور فقط سه حوزه دیگر است.حال میخواهیم عمیق تر وارد این سه حوزه شویم.

) که منظببورNIC- درایور کار ت شبکه (۲- درایور پروتکل استک شبکه و ۱حوزه شبکه به دو قسمت Wi-همان Fi و Ethernet.است تقسیم میشود

- درایور فایل سیستم برای بکارگیری فرمتهای مختلف۱حوزه رسانه های ذخیره ساز نیز به دو قسمت تقسیم میگردد.SCSIMTD و IDE- درایور بل ک برای پروتکلهای ذخیره سازی مانند ۲پارتیشنها و

درایور های چند حوزه ای USBبعضی سخت افزارها بجای استفاده از یک حوزه، همزمان میتوانند از چندین حوزه استفاده کنند.

WiFi برای کار با USB نمونه هایی از این سخت افزارها میباشند. بطور مثال شما میتوانید یک دانگ ِل PCIو USB برای تبدیل پور ت موازی به سریال داشته باشید.همه آنهببا USB ، یا حتی یک USB، یک قلم نور ِی

هستند ولی در سه حوزه مختلف کار میکنند.ا ًل این نوع سخت افزارها به دو بخش یا به دو درایور مختلف تقسببیم میشببوند. بخببش درلینوکس معمو اول کنترل کننده سخت افزار و بخش دوم یک لیه انتزاعی که بالی حببوزه مببورد نظببر قرارمیگیببرد و هسببته

نامیده میشود.

جمع بندی فصل اول یک دیوایس درایور یک تکه برنامه است که یک سخت افزار را کنترل میکند.بببا وجببود ایببن بببه چنببدا ًل درایببور فایببل د‌یگببوییم مث دسته تقسیم میشود.اگر فقط یببک سببخت افببزار را کنببترل کنببد بببه آن درایببور م

سیستم.بنابراین تمام دیوایس درایورها ، درایور هستند ولی هر درایوری لزوما ًا دیوایس درایور نیست.

صفحه6

فصل دوم : نوشتن اولین دیوایس درایور

درلینوکس برخلف بسیاری از سیستم عاملهای موجود، بببا اضببافه یببا حببذف کببردن یببک درایببور بببهد‌معامل، احتیاجی به راه اندازی مجدد د‌معامل میتوانببد از درایببور بصببور ت1سیست د‌معامل نمیباشببد و سیسببت سیست

این روش به اصطلحا ًا بپذیرد. تاثیر dynamic بلدرنگ loading and unloading این به و قرار میگیرند.ko*.2درایورها «ماژول» گفته میشود که این ماژولها در فایلهای مخصوصی با پسوند

د‌معامل لینوکسی بصور ت استاندارد یک جایی برای تمامی ماژولها در نظر گفته شببده اسببت و آنهببا در هر سیستمشببببابه سبببباختار سببببورس کرنببببل لینببببوکس سببببازماندهی شببببده انببببد.شبببباخه مببببورد نظببببر

/ /< _ >/lib modules kernel version kernelکه </ میباشد _kernel versionنسببخه کرنببل < uname- میباشد و از خروجی دستور rمیتوان آنرا بدست آورد.یک نمونه از آن را در شکل زیر مشبباهده

میکنید.

1reboot

2 Kernel object

صفحه7

د‌معامل از دستورا ت زیرکه در شبباخه / قراردارنببد اسببتفادهsbinبرای اضافه و حذف کردن درایو ها به سیستد‌معامل اضافه و حذف شود.rootمیشود.نکته مهم این است که یک درایور باید حتما ًا با یوزر به سیست

دستورات کار با درایورهاlsmodد‌معامل : جهت لیست کردن کلیه ماژولهای اضافه شده به سیست

> <insmod moduleد‌معامل : جهت اضافه کردن یک ماژول به سیست> <rmsmod moduleد‌معامل : جهت حذف کردن یک ماژول از سیست> <modprobe moduleد‌معامل : برای اضافه کردن یک ماژول به همراه تمام وابستگیهای آن به سیست

د‌ه انداز فایل سیستم د‌معامل را خببواهیمFATبعنوان مثال در شکل زیر مراحل اضافه کردن درایور را بببه سیسببتfat.دهید.توجه کنید که این درایور بنام ko د‌یتر لینوکس د‌ههای قدیم vfat. یا در نسخ koمیباشدو در مسیر

/ /` - `/ /lib modules uname r kernel fsمیتوان آنرا پیدا کرد.نکته مهم این است که ممکن است / از حالت فشببردهgunzip.* باشدکه میبایست نخست بوسیله برنامه gzاین فایل بصور ت فشرده و با پسوند

خارج گردد.

صفحه8

fat. وابسببته میباشببد.بنببابراین بایببد نخسببت fat بببه مبباژول vfatدر مثال فوق ماژول koرا لببود و لود کردن ماژول بببا تمببام وابسببتگیهای آن میتببوان از دسببتورunzipنماییم.جهت اتوماتیک کردن فرآیند

modprob استفاده نمود.دقت نمایید که دستور modprob احتیاج به مشخص کردن پسوند koندارد . د‌معامل د‌لها از سیسببت و نباید نام ماژول را با پسوند آن مشخص نمود.در مثال فوق در نهایت ما برای حذف ماژو

استفاده کردیم.rmmodاز دستور

نوشتن اولین درایور برای لینوکس قبل از اینکه نوشتن اولین درایور خود را شروع کنیم لزم است که نکبباتی را در مببورد برنببامه نویسببی درایور بدانیم.نخست اینکه یک درایور نمیتواند خودش را فراخوانی کند و درست ماننببد یببک تبباب ِع درون یببک

د‌یشببود ولببیC ،باید از بیرون فراخوانی شود.دوم اینکه یک درایور در لینوکس به زبببان 3کتابخانه کد نوشببته مد‌ههای معمولی ندارد.سوم اینکه درایور به کرنل الصبباق میشببودولزم اسببت کببهmain تابع Cبرخلف برنام

3library

صفحه9

مانند کرنل کامپایل شود و هدرفایلهای مورد نیباز بایبد از مسبیر سبورس کرنبل معرفبی شببوند نبه از مسبیر/usr include. /

د‌بگر است.تابع سببازنده هر درایور همانند برنامه نویسی شی گرا داری یک تابع سازنده و یک تابع تخرید‌بگر موقعی که درایور بببا در موقعی که درایور با موفقیت به کرنل اضافه شد اجرا میگردد و برعکس تابع تخری

) اجرا میگردد. این دو تابع مانند دو تابع معمولیrmmodموفقیت از کرنل حذف گردید(با استفاده از دستور module_میباشند با این تفاو ت که این توابع با استفاده از ماکروهای () init() و _module exitکببه در

موجود است تعریف میشوند.moduleh.هدرفایل کرنل حال بیایید به کد اولین درایور نگاهی بیاندازیم:

/* . */ ofd c – Our First Driver code

# < / . > include linux module h

# < / . > include linux version h

# < / . > include linux kernelh

__ _ ( ) /* */ static int init ofd init void Constructor

{ ( _ " : "); printk KERN INFO Namaskar ofd registered

0; return

}

__ _ ( ) /* */ static void exit ofd exit void Destructor

{ ( _ " : "); printk KERN INFO Alvida ofd unregistered

}

_ ( _ ); 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میگببذاریم.در د‌ههای معمول stdio. در درایورها، خبری از هدرفایلهایی ماننببد cنظر داشته باشیدکه برخلف برنام hنیسببت

kernel.بدین علت که این هدرفایلها در فضای یوزر میباشند. بجای آنها از هدر فایلهای فضای کرنل مانند h شده است.printf جایگزین ()printkاستفاده مگردد و به همین دلیل است که ()

جهت سازگاری نسخه درایور با نسخه کرنلببی اسببت کببه آن درایببور را لببودversionh.هدر فایل نیز اطلعا ت درایور را مشخص میکند و مشابه امضاء برای درایورMODULEمیکند و همچنین ماکروهای *_

است.

ساختن اولین درایور در لینوکس داریم که آنرا کامپایل کببرده وcدر قسمت قبل ما اولین درایور را نوشتیم و حال ما یک فایل به زبان

زیر سورس لزم جهت ساخت درایور را به ما میدهدو با اسببتفادهMakefile را بسازیم. فایل ofdko.فایل از آن میتوانیم اولین درایور خود را بسازیم.برای ساخت درایببور هببا مببا نیبباز بببه سببورس کرنببل و یببا حببداقل

/هدرفایلهای کرنل داریم. فرض بر این است که سورس یا هدر فایلهببای کرنببل در مسببیر /usr src linux/ قرار دارد. اگر در سیستم شما سورس کرنل یا هدر فایلهای کرنل در مسیر دیگری قراردارد میبایست در فایببل

Makefile د‌ متغییر_KERNEL SOURCE را تغییر دهید.در زیر سور ِس فایل Makefile:آمده است

# Makefile – makefile of our first driver

# , ' if KERNELRELEASE is defined we ve been invoked from the

# . kernel build system and can use its language

(${ },) ifneq KERNELRELEASE

- := . obj m ofd o

# . Otherwise we were called directly from the command line

# . Invoke the kernel build system

else

_ :=/ / / KERNEL SOURCE usr src linux

:= $( ) PWD shell pwd

: default

${ } - ${ _ } =${ } MAKE C KERNEL SOURCE SUBDIRS PWD modules

: clean

${ } - ${ _ } =${ } MAKE C KERNEL SOURCE SUBDIRS PWD clean

endif

صفحه11

ofd.الن فایل c و Makefileآماده است و تنها چیزی که برای ساختن درایور نیاز داریببم صببدور چیزی شبیه زیر خواهد بود:makeد‌ میباشد.خروجی فرمان makeدستور

$ make

- / / / =... make C usr src linux SUBDIRS modules

[1]: `/ / / 'make Entering directory usr src linux

[ ]CC M .../ .ofd o

, 2.Building modules stage

1 MODPOST modules

CC .../ . .ofdmodo

[ ]LD M .../ .ofdko

[1]: `/ / / 'make Leaving directory usr src linux

و یبباroot ساخته خواهد شد و میتوانیم با دسترسببی ofdko.اگر همه چیز به خوبی پیش رود، فایل کارهای معمول زیر را بروی این درایور انجام دهیم:sudoاستفاده از

# su# .insmod ofdko

# | -10lsmod head

حال میتوانیم با استفاده از دستور آخر مشاهده کنیم که درایور ما به درستی به کرنل اضافه شده است.

صفحه12

برای نوشتن دیوایسcفصل سوم : امکانا ت اضافی کهدرایور در لینوکس به ما میدهد

اسببتفاده شببدهprintk از تببابع ()printfدر فصل قبل دیدیم که در برنامه نوشته شده بجای تابع ()د‌های را مشاهده نکردیم.برای دیدن نوشته ی این تببابع بعببد از اضببافه است ولی برخلف انتظار در خروجی نوشت

dmesg| شدن به سیستم عامل میبایست دستور tailد‌هها بجببای کنسببول را صادر کنیم. اینکه چببرا نوشببتمعمولی از طریق این دستور میبایست مشاهده شود را در ادامه توضیح خواهیم داد.

مسیج لهگهای کرنلد‌یشبویم کبه پارامترهببای () و ()printfدر یک بررسببی و جسببتجوی کوچبک در اینببترنت متببوجه م

printk() د‌نها این است کببه درprintk در فضببای یببوزر و ()printf مشابه یکدیگر میباشند ولی تفاو ت آ % و...if% و fفضای کرنل مورد استفاده قرار میگیرد.همچنین در فضای کرنببل دیگببر مببا از فرمتهببایی نظیببر

برای چاپ خروجببی بببروی کنسببول طراحببیprintf برعکس ()printkاستفاده نمیکنیم.مهم این است که ()ا ًل نمی تواند انجام دهد و دلیل آن هم این است که بعضی فرآینببدها نشده است. درحقیقت همچین کاری را اصد‌ههای کد،که وقتی در سطح یوزر یا کرنل فراخوانی می شوند، اجرا د‌یشوند. مانند کتابخان در پشت صحنه انجام م

د‌یگردند ولی خروجی مورد مشاهده ای را تولید نمیکنند. م نیز همین گونه است که با اجببرای آن ،خروجببی بجببای کنسببول در لگ سیسببتمprintkدر مورد ()

است که در سطح یوزر اجرا شده و پیامهای ریخته شببده در لگ سیسببتم4 نیز دیمانیsyslogریخته میشود./را گرفته وبعد به جایی که در فایل .etc syslog conf.مشخص گردیده میفرستد /

KERN_شببما در برنببامه فصببل قبببل از مبباکروی INFO() در پببارامتر دسببتور printkد‌ اسببتفاده کردید.این ماکرو یک رشته ثابت است که به ابتببدای رشببته بعببد متصببل گردیببده و رشببته نهببایی را تشببکیل

د‌یبینیم: میدهد.برای یاد آوری یک خط از برنامه فصل قبل را دوباره با هم م

( _ " : "); printk KERN INFO Namaskar ofd registered

4deamon

صفحه13

KERN_همانگونه که مشاهده میکنید بین متن اصلی و INFOهیچ کاراکتری از قبیل کاما وجود ندارد. بدین د‌های دیگببر یببا یببک آرگومببان مجببزا نیسببت.در سببورس کرنببل لینببوکس و در فایببل معنی که این ماکرو، رشت

/ .linux kernel h:هشت نوع دیگر از این ماکروها تعریف شده اند

# _ "<0>"define KERN EMERG /* system is unusable */# _ "<1>"define KERN ALERT /* action must be taken immediately */# _ "<2>"define KERN CRIT /* critical conditions */# _ "<3>"define KERN ERR /* error conditions */# _ "<4>" /* define KERN WARNING warning conditions */# _ "<5>" /* define KERN NOTICE normal but significant condition */# _ "<6>"define KERN INFO /* informational */# _ "<7>"define KERN DEBUG /* - debug level messages */

د‌ پیامهببایsyslogحال بسته به اینکه ما از کدام یک از ماکروهای فوق استفاده کرده باشببیم، دیمببان د‌یکند.یک مقصد برای هر کدام از ایببن د‌نها به جاهایی که تعریف شده است منتقل م دریافتی را بر اساس نوع آ

د‌یتواند فایل /ماکروها م /var log messagesد‌شفببرض تمببام ماکروهببا / باشد.از آنجایی که ایببن فایببل پید‌یشود. اگرچه شما میتوانید با تغییر تنظیما ت، خروجی راprintkمیباشد تمام خروجی نیز به آن فایل ریخته م

ا ًل /به پور ت سریال (مث 0dev ttysیا حتی به تمام کنسولها (پیش فرض لینببوکس در موقببع بببروز خطببای (/ _KERN EMERG.منتقل کنید (

/از آنجاییکه تمام پیامهای نوشته شده در فایل /var log messagesفقط شامل پیامهای کرنل نمیباشببد / ا ًل بببرای یوزرهببای و تمام پیامها (پیامهای چندین دیمان سطح یوزر) را شامل میگردد و همچنین این فایل معمو

د‌ تهیه گردیده است تا فقببط پیامهببایdmesgمعمولی قابل خواندن نیست بنابراین یک ابزار سطح کاربر بنام کرنل را تفسیر و بروی خروجی استاندارد نمایش دهد.شکل زیر طرز کار این ابزار را نشان میدهد:

صفحه14

برای برنامه نویسی سطح کرنلGCCابزار خاص

نیسببتند.در حقیقببتC__ کلمببا ت کلیببدی زبببان exit__ و initدر این قسمت لزم به ذکر است که نوشته شدهGCC یا همان C و همچنین ابزارهای جانبی کامپایلر زبان Cکرنل لینوکس تنها با استفاده از زبان

هسببتند.ایببن دو مبباکرو هیببچGCC__ تنها دونمونه از این ابزارهای جانبی exit__ و initاست. و ماکروهای ارتباطی با لود کردن درایور درکرنل و بصور ت داینامیک ندارد بلکه هر زمان که بخببواهیم کببدی بببرای کرنببل

__ مار ک دار شده باشند در موقع کامپای ِلinitنبویسیم از این دو ماکرو استفاده خواهیم کرد.تمام توابعی که با kernel مربوط به init در بخش GCCکرنل، بصور ت اتوماتیک توسط کامپایلر imageقرار میگیرند و

kernel- مربببوط بببه exit__ مببار ک دار شببده باشببد در بخببش exitهر تابعی که با استفاده از مبباکروی

image.قرار خواهد گرفت د‌نهببا سؤالی که ممکن است مطرح شود این است که چرا به چنین ماکروهایی نیاز داریم؟ و خاصببیت آ

بب قرار است فقط یکبار و آنهببم موقببعinitچیست؟ جواب این است که تمام توابع علمت گذاری شده توسط بال آمدن کرنل اجرا گردد و هرگز تا راه اندازی مجدد کرنل دوباره اجرا نخواهند شد.و بعداز اجببرا شببدن ایببن

، این قسمت از حافظه توسط کرنل آزاد خواهد شد.و بصور ت مشببابهinitتوابع با آزاد شدن حافظه مخصوص

صفحه15

__ علمت گذاری شده باشند در موقع خاموش شدن سیستم اجرا خواهند گردید.exitتوابعی که با بب درexitسؤال بعدی این است که سیستم بهرحال خاموش خواهد شد و چرا ما باید حافظه ای را به

تمام مدتی که کرنل در حال اجرا است اختصاص دهیم؟ پاسخ این است که این توابع تا لحظه خبباموش شببدند‌قالعاده حافظه توسط لینوکس است. ا ًل در حافظه وجود ندارند! این یک بهینه سازی خار کرنل اص

د‌ههای همکاری متقابل و زیبای بین کرنل و اسببت کببه بهینببه سببازیGCCمورد فوق تنها یکی از نمون منابع را در بر خواهد داشت.و در آینده از این فو ت و فن ها نیز بیشتر خواهیم آموخت.برای همیببن اسببت کببه

د‌نها د‌یشببود. بببه هرترتیببب GCCکرنل لینوکس فقط توسط کامپایلرهایی که پایه آ وGCC است ،کامپایببل مکرنل به هم گره خورده اند.

توابع کرنلی که خطا را مدیریت میکند آیا میدانید چگونه برنامه نویسان مختلف بروی بزرگترین پروژه لینوکس یعنی کرنل ، بببدون هیچگببونهد‌نها این اسببت کببه تداخلی با هم کار میکنند؟ برای این سؤال دلیل مختلفی وجود دارد.یکی و شاید مهمترین آ تمام برنامه نویسان کرنل لینببوکس، دسببتورالعمل و شببیوه کببد نویسببی در کرنببل را میداننببد و بببه آن پایبنببد هستند.برای مثال میتوان به دستورالعمل چگونگی بازگرداندن مقدار از یک تابع اشاره کرد. هر تابع در کرنل به

error یک مدیریت کننده خطا ( handlingا ًل یک عدد صحیح ) نیاز دارد و این مدیریت کننده خطا، معموا ًل عدد صفر بازگشت را برمیگرداند که مقدارش طبق دستورالعمل میباشد. در صور ت اجرای صحیح تابع، معموا ًل مقدار خروجی میتواند تعببداد بایتهببای د‌یشود مگر آنجایی که به اطلعا ت بیشتری نیاز داشته باشیم. مث داده م ارسال شده باشد ولی توجه داشته باشید که این مقدار حتما ًا باید مثبت باشد. در صببورتی کببه تببابع بببا خطببایی

/مواجه شود، کد خطا به ماکروهایی که در هدر فایل .linux errnohد‌یشود کببه د‌هاند منتقل م / مشخص شددر آنجا توسط این ماکروها علمت منفی به آن اضافه میشود.چندین هدر فایل برای اعداد خطا وجود دارند که

- / - .asm generic errno baseh و //- / .asm generic errnoh و // .asm errnoh/ د‌نها میباشند. د‌ههایی از آ نمون

Cرابطه کرنل لینوکس با

usr/اگر بخاطر داشته باشید نمیتوانستیم از includeد‌ههای کرنل استفاده کنیببم.از طببرف / در برنامد‌هاند. پس دلیل ایببنGCC استاندارد به اضافه چند ابزار Cدیگر میدانیم که کرنل لینوکس را نیز با زبان نوشت

خببالص اسببت و فقببط یببک زبببانC اسببتاندارد همببان Cتناقض چیست؟در حقیقت تناقضی در کببار نیسببت. د‌ههببای اسببتاندارد زبببان د‌نها جببزء کتابخان د‌هنویسی است و هدر فایلها، جزء زبان برنامه نویسی نیستند. آ Cبرنام

د‌ههای استاندارد و همچنین تمببام توابببع برای برنامه نویسی میباشند.این مطلب بدین معنی است که تمام کتابخان

صفحه16

خالص نیستند.C، جزیی از ANSIاستاندارد د‌هها استفاده میکنند؟ و آیببا برنببامه نویسببیCپس چگونه توسعه دهندگان کرنل از و بدون این کتابخان

د‌هها بسیار طاقت فرسا نیست؟ جواب این سؤال منفی اسببت. بببدلیل اینکببه توسببعه برای کرنل بدون این کتابخاند‌هاند و این توابع نیز جزیی از کرنببل د‌ههای مورد نیازشان را از قبل ساخت دهندگان کرنل خودشان توابع و کتابخان

د‌یشود که د‌هها ، حببافظه و …printkمحسوب م د‌نها است و مشابه آن توابعی نیز برای کار با رشت نیز یکی از آ و نظببایر آنkernel و ipc و libنیز نوشته شده و جزیی از کرنل میباشد که این توابببع در جنببدین شبباخه

د‌نها نیز در شاخه include/قرار دارند.همچنین هدر فایلهای آ linuxمیباشد. به همین دلیل است که مببا / برای نوشتن برنامه کرنل به سورس کرنل یا حداقل هدر فایلهای کرنل نیازمندیم. از آنجاییکه سورس کرنببل و هدر فایلها ، هر دو میتوانند مورد استفاده قرار بگیرند، بنابراین در لینوکس نیز برای نصب هر کدام بسته هببایید‌هایببد بایببد در مسببیر ا ًل اگر شما فدورا داریببد و بسببته سببورس کرنببل را نصببب کرد متفاو ت تهیه شده است.مث

/ / /< -usr src kernels kernel versionبتوانید آنهارا بیابید ولی اگر فقط بسته هدر فایلهای کرنل را / د‌نها در مسیر /نصب کرده باشید باید آ /usr src linuxبیابید.در فدورا و برای نص ِب فقط هدر فایلهببا شببما/

yum- کافی است که دستور install kernel devel را صادر کنید. اگر شما از mandrivaاستفاده urpmi- میکنید و میخواهید سورس کامل را نصب کنید کافی است که دستور kernel sourceرا صادر

sudo- کببافی اسببت کببه دسببتور ubuntuکنید و همچنین برای نصببب سببورس کرنببل در apt get -install linux source .را تایپ نمایید

صفحه17

در لینوکسکاراکتریفصل چهارم : درایورهای

د‌نهببا نیازمنببدیم.همچنیببن بببا دراین فصل ما خواهیم آموخت که درایورها چیستند و برای چه مببا بببه آویژگیهای درایورهای کاراکتری نیز آشنا خواهیم شد.

به کاراکتر معروف است) را انجام دهد آنگبباه مبباCاگر ما درایوری را بنویسیم که عملیا ت بایت گرا (در زبان یک درایور کاراکتری نوشته ایم. اکثر دستگاههای موجود،کاراکتری هستند. بنببابراین درایورهببای نوشببته شببدهد‌نها نیز کاراکتری میباشند.برای مثال میتوان به درایورهای سریال ، دوربین ، صدا و تصویر اشاره کرد. به برای آ

را) نیسببتندNetwork) و یا شبببکه ای (Storageعبار ت دیگر تمام درایورهایی که به نوعی ذخیره ساز (میتوان درایورهای کاراکتری نامید.حال نگاهی به اشتراکا ت دستگاههای کاراکتری می اندازیم:

د‌یکنید برای هر عملیا ت بروی دسببتگاههای بببایت گببرا در حببوزه همانطور که در تصویر فوق مشاهده مد‌های که در فضای یببوزر د‌تافزار، میبایست یک درایورکاراکتری در فضای کرنل لینوکس بین دستگاه و برنام سخد‌یشود، وجود داشته باشد.همچنین درایورهای کاراکتری از یکسری راب ِط فایلی آن دسببتگاه کببه بببه فایببل اجرا ما ًل یکسببری عملیببا ت فببایلی د‌ههبا معمببو سیست ِم مجازی لینک شده است اسببتفاده میکنببد.بببدین معنببی کببه برنام (خواندن،نوشتن و...) بروی این فایلهای دستگاهها انجام میدهند و فایل سیستم مجازی این عملیا ت را بببه توابببع مرتبط در درایورهای دستگاه کاراکتری منتقل میکنببد و در نهببایت ایببن توابببع، دسترسببی سببطح پببایین را بببه

صفحه18

د‌تافزار میسر میکنند.اگرچه برنامه یکسری عملیا ت فایلی را انجام میدهد ولببی ممکببن اسببت نتایببج آن بببا سخد‌تافزار مورد نظر میباشببند.بطببور د‌نها تنها یک راه برای تعامل با سخ عملیا ت فایلی واقعی یکی نباشد بلکه فقط آ مثال اگر در یک فایل کاراکتری ابتدا مقداری را بنویسببیم سببپس بخببواهیم همیببن مقببادیر را بخببوانیم ، لزومببا ًا خروجی ،با آن چیزی که ما از یک فایل معمولی انتظار داریم یکی نخواهد بود.بعنوان یک مثببال عملببی میتببوان گفت که اگر مقداری را در یک فای ِل دستگا ِه صوتی بنویسیم، بجای اینکه واقعا ًا در آن فایل ذخیره گردد ، منجر به تولید صدا از بلندگوی سیستم خواهد شد.و اگر بخواهیم صدایی را از میکروفن دستگاه دریافت کنیم بایببد ازد‌ههای خوانده شده از این فایل، ارتباطی بببا یکببدیگر د‌ههای نوشته شده با داد این فایل بخوانیم. بدیهی است که داد

ندارند.د‌تافزار به چهار عنصببر -۲- برنببامه ۱بنابراین برای ارتباط کامل بین یک برنامه در فضای یوزر و سخ

د‌تافزار. نیازمندیم.۴- درایو ِر دستگاه ۳فای ِل رابط - سخ نکته جالب توجه این است که هر یک از چهار عنصر فوق میتواند بروی یک کامپیوتر مجزا باشببد بببدون اینکببهسایر عناصر در آن کامپیوتر وجود داشته باشند ولی باید به نحوی این چهار عنصر صراحتا ًا با هم متصل باشند.

system یک برنامه ابتدا با درخواست باز کردن فای ِل راب ِط دستگاه ( با استفاده از callبا آن فایل ( ارتباط برقرار میکند سپس آن فای ِل رابط به درایور مرتبطی کببه در کرنبل لبود شببده اسبت (از قبببل رجیسببترد‌تافزا ِر کباراکتری مبورد نظبر د‌یشود و در نهایت آن درایور، عملیا ت سطح پایین را بروی سبخ گردیده) وصل مد‌تافزار و برنامه برقرار میگردد. نکته قابل ذکر این اسببت انجام میدهد. با این توضیح یک ارتباط کامل بین سخ

د‌تافزار و درایور میباشد. که فایل رابط ، یک فایل واقعی نیست و تنها یک رابط بین سخ

شماره های اصلی و فرعی برنامه سطح یوزر با استفاده از نام فایل به فای ِل رابط دسترسی پیدا میکند ولببی ارتببباط فایبب ِل رابببط ببباد‌هی فای ِل رابط میباشد و نه نام فای ِل رابط.بنابراین کاربر میتواند هببر نببامی را بببرای فایبب ِل درایور بر اساس شمارد‌هی فایل با درایور مورد نظر ارتباط برقرار میکند.ایببن شببماره رابط خود انتخاب نماید و کرنل با استفاده از شمار

د‌هی فرعی میباشد. د‌هی اصلی و شمار ا ًل دارای یک زوج شمار فای ِل رابط معمود‌تافزار۲.۶تا قبل از کرنل د‌هی فرعی نیز به ویژگیهای آن سخ د‌تافزار و شمار د‌هی اصلی به نوع سخ شمارد‌مبندی دیگببر اجببباری نیسببت و میتببوان چنببدین درایببور را بببا۲.۶مربوط بود ولی از کرنل به بعد ایببن تقسببی

د‌ههای فرعی متفبباو ت اسببتفاده نمببود.لیکببن هنببوز بببرای سببخت افزارهببای د‌ههای اصلی یکسان ولی با شمار شمارد‌هی د‌طهای سریال از شببمار د‌یشود.مثل برای راب د‌هی اصلی مخصوص به خود استفاده م ، رابببط۴استاندارد از شمار

د‌هی د‌هی ۱۳میکروفن از شمار د‌یشود. دستور زیر دستگاهها به همراه۱۴ ، دستگاههای صوتی از شمار و... استفاده مشماره های اصلی و فرعی آنرا بروی کامپیوتر شما نشان میدهد:

صفحه19

$ - / / | ^ls l dev grep “ c”

ه‌ههای اصلی و فرعی در کرنل به بعد۲.۶ارتباط شمار/نوع دستگاهی که در هدر فایل .linux typesh تعریف شده است شامل _dev tاست که معرف

د‌هی اصلی و فرعی میباشد.همچنین ماکروهای زیر نیز در هببدر فایبل /هر دو شمار _ .linux kdev t hتعریبف شده است:

)( _ MAJOR dev t dev.د‌هی اصلی دستگاه را برمیگرداند که شمار)( _ MINOR dev t dev.د‌هی فرعی دستگاه را برمیگرداند که شمار)( , MKDEV int major int minor.د‌ههای اصلی و فرعی میسازد که یک دستگاه را بر اساس شمار

همچنین جهت اتصال به فایل دستگاه دو مرحله لزم است که انجام گیرد:د‌ههای اصلی و فرعی برای دستگاه.۱ - ثبت رنج شمار - ارتباط بین هر عملیا ت بروی فایل دستگاه (اعم از خواندن،نوشتن و...) و تابعی در درایور کببه وظیفببه هنببدل۲

کردن آن عملیا ت را دارد./ زیرکه در هدر فایل APIگام اول با یکی از دو .linux fs h:وجود دارد قابل انجام است

_ _ ( _ , , * );int register chrdev region dev t first unsigned int cnt char name

_ _ ( _ * , , , int alloc chrdev region dev t first unsigned int firstminor unsigned int cnt char * );name

د‌یباشد. این شماره با نامی کببه بببا متغیببر cntد‌ ی اول ، مقدار APIدر د‌هی مربوط به فایل دستگاه م first شمار )،major ی دوم بصور ت داینامیک اولیببن شببماره اصببل ِی آزاد (APIتعریف شده است آغاز میگردد.ولی در

د‌یشود و د‌ههای اصلی و فرعی انتخاب شدهcntبرگشت داده م د‌هی دستگاه که با ترکیب شمار ی مربوط به شمارfree , است که با < major first minor د‌یشود ، ایجاد میگردد.از هر کدام از های فوقAPI> شروع م

proc/که استفاده نماییم در devicesما میتوانیم نام با شماره اصلی ثبت شببده را ببببینیم.اکنببون بببا ایببن / اطلعا ت ما میتوانیم اولین درایور خود را اینگونه بنویسیم:

# < / . >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که ما برای

if ( _ _ (& , 0, 3, alloc chrdev region first " "Shweta ) < 0){ return -1;}

( _ printk KERN INFO "< , >: <% , % >\ "Major Minor d d n , ( ), ( ));MAJOR first MINOR first

مینویسیم:destructorو برای

_ _ ( , 3);unregister chrdev region first

بنابراین برنامه کامل چیزی شبیه کد زیر خواهد بود:

# < / . >include linux moduleh

# < / . >include linux versionh

# < / . >include linux kernelh

# < / . >include linux typesh

# < / _ . >include linux kdev t h

# < / . >include linux fs h

static _ ; dev t first // Global variable for the first device number

static int __ _ (init ofcd init void) /* */Constructor

{ ( _ printk KERN INFO " : "Namaskar ofcd registered ); if ( _ _ (& , 0, 3, alloc chrdev region first " "Shweta ) < 0) { return -1; } ( _ printk KERN INFO "< , >: <% , % >\ "Major Minor d d n , ( ), ( ));MAJOR first MINOR first

return 0;} static void __ _ (exit ofcd exit void) /* */Destructor

صفحه21

{ _ _ ( , 3);unregister chrdev region first

( _ 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 );

خب پس از اینکه کد فوق را ذخیره کردیم میتوانیم کارهای معمولی که بروی هر درایور انجام میدهیم را اجراکنیم:make.) بوسیله اجرای دستور koساخت درایور (یا همان فایل •

insmodلود کردن درایور با استفاده از دستور •

lsmodمشاهده درایور لود شده با استفاده از دستور •

rmmodحذف درایور از کرنل با استفاده از دستور •

د‌هی اصلی ثبت شده که با کلمه شببروع شببده اسببتshwetaقبل از حذف درایور از کرنل، برای دیدن شمار/ میتوانیم با دستور /cat proc devicesنگاهی به آن داشته باشیم.میتوان دید که آن فایل واقعببا ًا وجببود

د‌هی اصلی بیابیم. البته میتوانیم با استفاده از دستورdevدارد ولی ما نمیتوانیم فایل دستگاهی را در / با آن شمارmknodبصور ت دستی آنرا بسازیم و سعی کنیم در آن بنویسیم و یا از آن بخببوانیم. بببه شببکل زیببر تببوجه

فرمایید:

صفحه22

میتواند در سیستم شما متفاو ت باشد.شکل فوق همچنین غیببر موفببق بببودن کببار۲۵۰نکته ایجاست که شماره خواندن و نوشتن بروی این فایل را نیز نشان میدهد.بیاد بیاورد که گام دوم ، ارتببباط عملیببا ت فایببل بببا توابببعد‌هایم. بنابراین هنوز به اطلعا ت بیشتری نیاز داریم که در فصببل بعببد درایور بود که ما در اینجا هنوز انجام نداد

به آن خواهیم پرداخت.

صفحه23

فصل پنجم: ساختن فایل دستگاههای کاراکترید‌ههای اصلی و فرعی برای کار با فایلهای دستگاه کاراکتری در قسمت قبل دیدیم که فقط ثبت زوج شمار

/ ساخته نخواهد شببد و همببانگونه کببه دیببدیم اینکببار را بببا اسببتفاده ازdevکافی نیست و فایل آن در شاخه mknode و بصور ت دستی انجام دادیم.بنابراین در این فصل ما قصد داریم کببه بببا اسببتفاده از udevdv

بصور ت اتوماتیک اینکار را انجام دهیم و همچنین دیوایس درایور را با این فایل ارتباط خببواهیم داد و عملیببا تبروی این فایل را با توابع موجود در دیوایس درایور مرتبط خواهیم نمود.

ساخت فای ِل دستگاه بصورت اتوماتیک devfs هببای مرتبببط بببا API کار ساخت فای ِل دستگاه بببا اسببتفاده از فراخببوانی ۲.۴تا قبل از کرنل

بصور ت اتوماتیک توسط کرنل انجام میگرفبت.بببا توسبعه و تکامببل کرنببل، توسبعه دهنببدگان بببه ایبن نببتیجه رسیداندکه فایلهای دستگاه، بیشتر به فضای کاربر نزدیک است تا به فضای کرنل .به همین دلیببل در کرنلهببای

د‌یشود و بقیه کار از جمله تفسببیر اطلعببا ت وsysامروزی فقط کلس دستگاه و اطلعا ت دستگاه در / انجام م اطلعبا ت راudevانجام رفتار مناسب در فضای کاربر انجام میگیببرد.در اکبثر لینوکسبهای رومیببزی،دیمبان

د‌هی فایل پیکربنببدی میتوانببد نببام فایببلudevمیگیرد و بر اساس آن فای ِل دستگاه را میسازد. همچنین بوسیل /sysدستگاه ، دسترسی ها و انواع آن و... را تعیین نماید. درایور، لزم است تا اطلعا ت و ورودیهای مناسببب

/ هایی که در هدر فایل APIرا با استفاده از .linux devicehد‌هی کارهببا / موجود اسببت را پببر نمایببد و بقی انجام میگیرد.کلس دستگاه بصور ت زیر ایجاد میگردد:udevتوسط

struct class * = _ ( _ , cl class create THIS MODULE "< >"device class name );

) تحت این کلس بوسیله کد زیر پر میشود:majorminor,سپس اطلعا ت دستگاه (

_ ( , , , , device create cl NULL first NULL "< >"device name format , ...);

_dev t> با ,majorminorمرتبط است و برای آزادسببازی و تخریببب آن نیببز بصببور ت معکببوس بایببد < بصور ت زیر انجام شود:

_ ( , );device destroy cl first

صفحه24

_ ( );class destroy cl

device بعنببوان یببک <chardrv/ توسببط sysشکل زیر ورودی ایجاد شده در class nameو < mynull> را بعنوان یک device name formatمشاهده میکنیم. همچنین فای ِل دسببتگا ِه سبباخته <

میتوانیم مشاهده کنیم.udev/ را با استفاده از devشده در

device_ هببای ()API داشته باشیم minorنکته جالب توجه این است که اگر ما چندین شماره create device_و () destroy> را با استفاده از device name format میتوانیم در یک حلقه <forقرار

device_دهیم.بعنوان مثال () create را با ایندکس i:را میتوانیم به شکل زیر فراخوانی کنیم

_ ( , , ( ( ), ( ) + ), , device create cl NULL MKNOD MAJOR first MINOR first i NULL " % "mynull d , );i

صفحه25

عملیات بروی فای ِل دستگاه تمام عملیا ت و سیستم کالهایی که جهت کار با فایل معمولی استفاده میگردد،برای فایلهببای دسببتگاه نیببزد‌نهببا در کرنببل قابل استفاده میباشد.در لینوکس اغلب موارد از دیدکاربر تنها یک فایل میباشد و تنها تفبباو ت آ

لینوکس انواع فایلها را تشخیص داده و عملیا تVFSمیباشد که از دید کاربر پنهان است.فایل سیستم مجازی د‌هها و فایلهببای ا ًل بببرای شبباخ فایل مرتبط با هر نوع فایل را به کانال مخصوص به آن فایببل ارجبباع میدهببد. مث معمولی به ماژولهای فایل سیستم ارجاع میدهد و برای فایلهای دستگاه کاراکتری به دیوایس درایور مرتبببط بببا

آن ارجاع میدهد.که ما در این اینجا فقط در مورد مکانیزم فایلهای کاراکتری بحث میکنیم. که وظیفه دارد هر عملیا ت فایل را به درایببور مربببوطه منتقببل کنببد، بیشببترVFSپس ما باید در مورد

دو گام دارد (کدهای داخل پرانتز برای درایور «تهی» میباشد که بعدا ًا توضیح خواهیم داد): VFSبدانیم.کاربا _ گام اول ، پرکردن استراکچر عملیا ت فایل ( _struct file operaton pugs fopsبا تببوابعی کببه (

ببببببببببرای آن عملیبببببببببا ت دلخبببببببببواه نوشبببببببببته ایبببببببببم (تبببببببببوابعی ماننبببببببببد_ , _ , _ , _my openmy closemy readmy write) و مقدار دهی اولیه اسببتراکچر فایببل کبباراکتری(...,

_struct cdev c dev() با استفاده از تابع (_cdev init. cdev_ میباشدکه با استفاده از ()VFSگام دوم معرفی این ساختار به addانجام میگیرد.لزم به ذکر اسببت

cdev_که () init() و _cdev add> هر دو در هدر فایل / .linux cdevh.تعریف شده اند < )nullبرای شروع تا جاییکه ممکن است کار را ساده میگیریم. بنابراین میخواهیم در ابتدای کار درایور «تهی» (

را بنویسیم.

درایور تهیofcd.اولین درایورکامل کاراکتری خود را بصور ت زیر مینویسیم و اسم آنرا c:میگذاریم

# < / . >include linux module h

# < / . >include linux version h

# < / . >include linux kernelh

# < / . >include linux types h

# < / _ . >include linux kdev t h

# < / . >include linux fs h

# < / . >include linux deviceh

# < / . >include linux cdevh

صفحه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 ( _ _ (& , 0, 1, alloc chrdev region first " "Shweta ) < 0) { return -1; } if (( = _ ( _ , cl class create THIS MODULE " "chardrv )) == )NULL

{ _ _ ( , 1);unregister chrdev region first

return -1; } if ( _ ( , , , , device create cl NULL first NULL " "mynull ) == )NULL

{ _ ( );class destroy cl

_ _ ( , 1);unregister chrdev region first

return -1; } _ (& _ , & _ );cdev init c dev pugs fops

if ( _ (& _ , , 1) == -1)cdev add c dev first

{ _ ( , );device destroy cl first

_ ( );class destroy cl

_ _ ( , 1);unregister chrdev region first

return -1; } return 0;} static void __ _ (exit ofcd exit void) /* */Destructor

{ _ (& _ );cdev del c dev

_ ( , );device destroy cl first

_ ( );class destroy cl

صفحه28

_ _ ( , 1);unregister chrdev region first

( _ 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 );

بعد از نوشتن و ذخیره برنامه فوق میتوانیم کارهای زیر را بروی درایور نوشته شده انجام دهیم:make.) با استفاده از دستور koساخت درایور (فایل•

insmodلود کردن درایور در حافظه با استفاده از دستور •

lsmodeمشاهده درایورهای بارگذاری شده با استفاده از دستور •

د‌ههای اصلی و فرعی اختصاص داده شده به درایو با استفاده از دستور • مشاهده شمار / /cat proc devices

امتحان کردن خصوصیا ت درایور تهی با کمک گرفتن از شکل زیر•rmmodحذف درایور از کرنل با استفاده از •

صفحه29

بعد از طی این مراحل خوشحالیم که توانستیم اولین درایور خود را نویسیم. قابل ذکر است که این درایورdev/ما شبیه به فای ِل استاندار ِد nullکار میکند.برای اینکه معنببی ایببن حببرف را در ک کنیببد کببافی اسببت /

د‌ههای اصلی و فرعی فایل dev/شمار null را مشاهده کنیبد و سبعی کنیبد ببا اسبتفاده از دسبتورا ت /catو echo.د‌هایم دقیقا ًا خواهد بود در این فایل بنویسید یا بخوانید. رفتار این فایل با درایوری که ما نوشت

ولبببی شببباید یبببک سبببؤال ببببرای شبببما هنبببوز مطبببرح باشبببد و آن ایبببن اسبببت کبببه مبببا تواببببع_ , _ , _ , _my readmy writemy openmy closeو... را فراخوانی کردیم ولببی اگببر بخببواهیم بصببور ت

واقعی عملیا ت خواندن یا نوشتن را داشته باشیم چگونه توسط این توابع اینکار را انجام دهیم؟ جببواب بببه ایببنپرسش در فصل بعد بررسی خواهد شد.

صفحه30

فصل ششم : خواندن و نوشتن بروی فایل دستگاههایکاراکتری

د‌نپذیر نبود. در این فصل میخواهیم در فصل قبل خواندن و نوشتن واقعی بروی فایلهای کاراکتری امکااین مهم را انجام دهیم.برای این کار بیایید دوباره به بخشهایی از کد نوشته شده در فصل قبل نگاهی بیاندازیم:

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;}

د‌یبینیم که نوع داده بازگشتی توابببع () my_با توجه به کدهای فوق م open() و _my close از نببوع Int میباشد ولی مقدار صفر (اطلعا ت ناچیز) را بازگشت میدهند که آن هم به معنی موفقیت آمیز بببودن عملیببا ت

my_این توابع است.در مقابل نوع داده بازگشتی توابع () read() و _my write از نوع intنبوده و مطببابق ssize_کد فوق از نوع tمیباشد که با کاوش در میان هدر فایلهای کرنل اطلعببا ت خببوبی را در ایببن مببورد

صفحه31

ا ًل نشانگر یک خطا است در صورتی کببه بازگردانببدن یببک عببدد میتوانیم پیدا کنیم.بازگرداندن عدد منفی معمو مثبت اطلعا ت بیشتری را به ما میدهد.برای خواندن از فایل ، مقدار بازگشتی باید با تعداد بایتهای خوانده شده

برابر باشد و همچنین برای نوشتن ، مقدار بازگشتی میبایست تعداد بایتهای نوشته شده را نشان دهد. چگونگی خواندن از فایل دستگاه

با دانستن مفاهیم فوق اگر دوباره به کد نوشته شده نگاهی بیاندازیم متوجه خواهیم شدکه در عملیببا تdev/خواندن، وقتی کاربر از فای ِل دستگا ِه mynull میخواند سیستم کال آنرا به فایل سیستم /VFSدر لیه

د‌یشببود کببه بایببد تببابع ()majorminor, زوج <VFSکرنل میرساند.سپس د‌یآورد و متوجه م > را بدست م‌ _my read() ا ًل ثبت شده است را فراخوانی کند.از اینرو تابع my_ که قب readد‌هی درایببور آنببرا که نویسند

نوشته است بدینگونه فراخوانی میشود.این تابع باید مقدار خوانده شده در هر درخواسبب ِت خوانببدن را بازگشببت دهد.درکد قبل ما مقدار صفر را بازگشت دادیم که بدین معنی است که مقببداری خوانببده نشببده اسببت یببا بببه عبار ت دیگر در آخر فایل هستیم. از اینرو وقتی فایل دستگاه شروع به خواندن میکند همواره تهببی بازگردانببده

میشود. سؤالی که ممکن است مطرح شود این است که اگر ما بجای بازگرداندن عدد صفر ، عدد یک را بازگردانیم آیبباد‌یشویم که جواب سؤال فوق مثبت اسببت ولببی در د‌های را کسب میکنیم؟ با نگاه به پارامترهای تابع متوجه م داد

د‌ههای آشغال و تصادفی هسببتند بببدلیل اینکببه تببابع () د‌ههای بازگشتی داد my_نظر داشته باشیم که داد read my_ دومیببن پببارامتر تببابع ()buf را دستکاری نمیکند.توجه کنید که bufمقدار متغیر readمیباشببد کببه

توسط کاربر ایجاد میشود.د‌ههای کبباراکتری (کببه در اینجببا تببابع درحقیقت برای عملکرد منطقی و درست تابع خواندن در فایلهای دسببتگا

_my read میباشد)،میبایست این تابع منطبق بر ورودی lenکه سومین پارامتر تابع خواندن است و تعببداد د‌هها را در buf بریزد.بببه عبببار ت دیگببر بایببد bufبایتهای درخواست شده را نشان میدهد،به همان میزان داد

بایت داده داشته باشد و تعداد بایتهای خوانده شده باید توسط تببابع بازگردانببدهlenمقدار مساوی یا کمتر از د‌هها را میخواند و در بافر قرار میدهد و آنگاه کاربر میتواند داده د‌نتر داد شود.بدین گونه نویسنده تابع در لیه پایی

ها را در لیه بالتر بخواند.

چگونگی نوشتن در فایل دستگاه بببایتlenعملیا ت نوشتن در فای ِل دستگاه دقیقا ًا برعکس عملیا ت خواندن میباشد.بدین ترتیب که کاربر تعداد

my_(سومین پارامتر تابع () write که میبایسببت نوشببته شببود را در بببافر ( buf() دومیببن پببارامتر تببابع) _my write() قرار میدهد سپس تابع ( _my writeد‌هها را خوانده و در صور ت امکببان در دسببتگاه این داد

لیه زیرین بنویسد و تعداد بایتی را که با موفقیت نوشته است را بازگردانببد.بببه همیببن دلیببل اسببت کببه تمببام

صفحه32

dev/درخواستهای خواندن و نوشتن ما بروی دستگاه mynull،بدون اینکه خواندن یا نوشتنی در کار باشد / موفقیت آمیز بوده است.

صفحه33

برنامه ثبت آخرین کاراکتر نوشته شدهد‌ههای فوق تابع () my_حال میخواهیم با دانست read() و _my writeرا به نحوی تغییر دهیم تا با

هر بار خواندن ، آخرین کاراکتر نوشته را بازگرداند.به کد زیر توجه نمایید:

static char ;c static _ _ (ssize t my read struct * , file f char __ * , user buf _size t , _ * )len loff t off

{ ( _ printk KERN INFO " : ()\ "Driver read n ); [0] = ;buf 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 ); = [ 1];c buf len –

return ;len

}

کد فوق به درستی کار میکند ولی فقط یک مشکل ممکن است وجود داشته باشد و آن این است که اگر کبباربر APIبافر غیر معتبری را پر کند آنگاه ما با یک خطای کرنل مواجه خواهیم شببد. بببرای رفببع ایببن مشببکل دو

معرفی شده است تا بافرهای یوزر را برسی کندکه امن و صحیح هستند یا خیر. پس بیایید با استفاده از ایببن دوAPI:دوباره کد را بازنویسی کنیم

/قابل ذکر است که این دو تابع به هدر فایل < .asm uaccessh.نیاز دارند <

static char ;c static _ _ (ssize t my read struct * , file f char __ * , user buf _size t , _ * )len loff t off

{ ( _ printk KERN INFO " : ()\ "Driver read n ); if ( _ _ ( , & , 1) != 0)copy to user buf c

صفحه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 ( _ _ (& , + 1, 1) != 0)copy from user c buf len –

return - ;EFAULT

else return ;len

}

برای ساخت درایورمان دوباره مراحل زیر را انجام میدهیم:د‌هایم و بوسیله دستور • makeساخت درایور با استفاده از تغییراتی که انجام داد

insmodلود کردن درایور با استفاده از دستور •

dev/نوشتن در فایل • mynull با استفاده از دستور / - < / /echo n “Pugs” dev mynull

/ خواندن از فایل فوق بببا اسببتفاده از دسببتور • /cat dev mynull بببا اسببتفاده از ) +ctrl cاتمببام میشود)

rmmodحذف درایور از کرنل با استفاده از دستور •

/ با اجرای دستور /cat dev mynullد‌یشببود کببه بدون وقفه ، آخرین کاراکتر نوشته شده بازگشت داده مctrl+برای خروج از این حالت میبایست c() فشرده شود.تابع _my read برمیگردانببد و۱ فقط یکبار عببدد

)offبرای دفعا ت بعد میبایست عدد صفر بازگردانده شود. این مورد با استفاده از پارامتر چهارم تابع (متغییببر قابل انجام میباشد.

صفحه35

فصل هفتم : دسترسی به سخت افزار در لینوکس

ه‌تافزار رابط عمومی سخد‌تافزار را تعریف کنیم.فعل کاری به جزییببا ت برای در ک بهتر این فصل نخست باید رابط عمومی سخد‌تافزار را نشان میدهد. ایبن راببط مببوجب عببدم وابسببتگی ببه تئوریک آن نداریم.عکس زیر رابط عمومی سخ

ا ًل معماری ماشین را پنهان میکند. د‌یشود و عم د‌تافزار م معماری سخ

بدلیل اینکه برای معماری های مختلف شکل فوق متفاو ت میباشد، فرض بر این است که ما در مورد معمبباری0 بیتی، حافظه از (۳۲ بیتی بحث میکنیم. برای آدرس باس۳۲ 00000000x) 0) شروع شده و تاxffffffff(

د‌ییابد.برای عدم وابستگی به معماری باید طرح ما چیزی شبیه به شکل فوق باشد.جاییکه حافظه RAMادامه ما ًل ۳۲و ناحیه دستگاهها در هم تنیده شده باشند.برای معماری آدرس دهیRAM گیگابایت برای ۳ بیتی معمو

د‌یشوند که رنج آدرس 0م 00000000x 0 تاxbfffffffد‌یشود و یک گیگابایت باقیمانده کببه رنببج را شامل م0آدرس 0000000xc 0 تاxffffffffد‌یشود به ناحیه دستگاهها اختصبباص مببی یابببد.اگببر حببافظه را شامل م

ا ًل ۴دستگاه کمتر از 0 گیگابببایت) آنگبباه نبباحیه دسببتگاه میتوانببد از آدرس ۲ گیگابایت باشد(مث 80000000x شروع شود.

صفحه36

catبرای مشباهده ترتیبب نگاشبت حبافظه (یبا همبان آدرس دهبی) در سیسبتم خبود میتوانیببد از دسبتور / /proc iomem استفاده نمایید.همچنین برای مشاهده اندازه تقریبی حافظه RAMسیستم خود میتوانید از

/ دستور /cat proc meminfoکمک بگیرید.در شکل زیر خروجی این دو دستور را در سیستم من نشان میدهد:

اشاره میکند را آدرس فیزیکی و مقادیری کببه بببه دسببتگاههاRAMقابل ذکر است که مقادیری که به حافظه اشاره میکنند را آدرس باس گویند.دلیل نامگذاری آدرس باس به این دلیل است که دستگاهها همیشه فارغ از

در معمباریAMBA یبا 86x در معمباری PCIنوع معماری به باس آن سیسبتم متصببل میگردنبد.ماننببد ARM و یا SuperHyway در معماری SuperH...و

د‌یشببوند ولببی در د‌سهای فیزیکی و باس به دوشیوه داینامیک و یا از طریق معماری پردازشببگر پیکربنببدی م آدر لینوکس به دلیل اینکه اجازه دسترسی بصور ت مستقیم به حافظه وجود ندارد میبایست ابتدا به حافظه مجببازی

مرتببطAPI یا حافظه دستگاه دسترسی حاصبل شببود. دو RAMنگاشت شود و سپس از طریق آن به حافظه جهت نگاشت یا عدم نگاشت آدرس حافظه دستگاه به حافظه مجازی به شرح زیر میباشد.نکته قابل ذکببر ایببن

/ در هدر فایل <APIاست که این دو .asm io h:تعریف شده است <

صفحه37

void * ( ioremap unsigned long _ _ , device bus address unsigned long _ _ );device region size

void (iounmap void * _ );virt addr

/برای خواندن و نوشتن در حافظه نگاشت شده توابع زیر که همگی نیز در هببدر فایببل < .asm iohتعریببف < د‌هاند در دسترس میباشد: شد

unsigned int 8(ioread void * _ );virt addr

unsigned int 16(ioread void * _ );virt addr

unsigned int 32(ioread void * _ );virt addr

unsigned int 8( 8 , iowrite u value void * _ );virt addr

unsigned int 16( 16 , iowrite u value void * _ );virt addr

unsigned int 32( 32 , iowrite u value void * _ );virt addr

:DOSیادی از سیستم عامل قدیمی کار کرده باشید در آن موقع میتوانستید بصور ت آزادنببه بببه حببافظهDOSاگر با سیستم عامل قدیمی

ویدئو (گرافیک) دسترسی داشته باشید. الن هم با اطلعاتی که در دست داریم میتوانیم مشابه آن کار را انجام دیر موجود در فایلدهیم یعنی میخواهیم به حافظه گرافیک دسترسی داشته باشیم. برای اینکار با استفاده از مقا

/proc iomemرنج آدرس حافظه گرافیک سیستم خود را بدست آوریببد. بببرای سیسببتم مببن ایببن رنببج / 0آدرس بین 000 0000x a 0 تا 000x bffff میباشد.ما میخواهیم از APIهای فوق استفاده کرده و پارامترها

ا ًل نوشته بودیم اضببافه کنیببم و آنببرا درایببور vramرا به توابع سازنده و تخریب گر برنامه درایور تهی که قبد‌ینامیم سپس با استفاده از خواندن و نوشتن در سطح یوزر و با استفاده از درایور د‌هیvramم میتوانیم به حافظ

_گرافیک دسترسی داشته باشیم .بنابراین کد جدید ما که با نام .video ramcذخیره میکنیم شبیه کد زیببر خواهد بود:

# < / . >include linux module h

# < / . >include linux version h

# < / . >include linux kernelh

# < / . >include linux types h

# < / _ . >include linux kdev t h

# < / . >include linux fs h

صفحه38

# < / . >include linux deviceh

# < / . >include linux cdevh

# < / . >include linux uaccessh

# < / . >include asm io h

# _ 0 000 0000define VRAM BASE x A

# _ 0 00020000define VRAM SIZE x

static void __ * ;iomem vram

static _ ;dev t first

static struct _ ;cdev c dev

static struct class * ;cl

static int _ (my open struct * , inode i struct * )file f

{ return 0;}static int _ (my close struct * , inode i struct * )file f

{ return 0;}static _ _ (ssize t my read struct * , file f char __ * , user buf _size t , _ * )len loff t off

{ int ;i 8 ;u byte

if (* >= _ )off VRAM SIZE

{ return 0; } if (* + > _ )off len VRAM SIZE

{ = _ - * ;len VRAM SIZE off

}

صفحه39

for ( = 0; < ; ++)i i len i

{ = 8(( 8 *) + * + );byte ioread u vram off i

if ( _ _ ( + , & , 1))copy to user buf i byte

{ 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 8 ;u byte

if (* >= _ )off VRAM SIZE

{ return 0; } if (* + > _ )off len VRAM SIZE

{ = _ - * ;len VRAM SIZE off

} for ( = 0; < ; ++)i i len i

{ if ( _ _ (& , + , 1))copy from user byte buf i

{ return - ;EFAULT

} 8( , ( 8 *) + * + );iowrite byte u vram off i

} * += ;off len

صفحه40

return ;len

} static struct _ _ =file operations vram fops

{ . = _ ,owner THIS MODULE

. = _ ,open my open

. = _ ,release my close

. = _ ,read my read

. = _write my write

}; static int __ _ (init vram init void) /* */Constructor

{ if (( = ( _ , _ )) == )vram ioremap VRAM BASE VRAM SIZE NULL

{ ( _ printk KERN ERR " \ "Mapping video RAM failed n ); return -1; } if ( _ _ (& , 0, 1, alloc chrdev region first " "vram ) < 0) { return -1; } if (( = _ ( _ , cl class create THIS MODULE " "chardrv )) == )NULL

{ _ _ ( , 1);unregister chrdev region first

return -1; } if ( _ ( , , , , device create cl NULL first NULL " "vram ) == )NULL

{ _ ( );class destroy cl

_ _ ( , 1);unregister chrdev region first

return -1;

صفحه41

} _ (& _ , & _ );cdev init c dev vram fops

if ( _ (& _ , , 1) == -1)cdev add c dev first

{ _ ( , );device destroy cl first

_ ( );class destroy cl

_ _ ( , 1);unregister chrdev region first

return -1; } return 0;} static void __ _ (exit vram exit void) /* */Destructor

{ _ (& _ );cdev del c dev

_ ( , );device destroy cl first

_ ( );class destroy cl

_ _ ( , 1);unregister chrdev region first

( );iounmap vram

}

_ ( _ );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 RAMDriver );

پس از ثبت کد فوق طبق معمول کارهای زیر را برای اجرای درایورمان انجام میدهیم:_ میسازیم (فایل make را با استفاده از دستور vramدرایور • .video ramko(insmodلود کردن درایور با استفاده از دستور •

صفحه42

- با استفاده از دستور • 0123456789 > / /echo n “ ” dev vram در فایل /dev vram/ مینویسیم

- با استفاده از دستور • 1 - / / | od t x v dev vram lessاز فایل فوق میخوانیم . ما همچنین نیز استفاده نماییم ولی مقادیر بازگشت داده شده بصور ت باینری خواهند بودcatمیتوانیم از دستور

- در حالی که دستور 1od t x.مقادیر را بصور ت هگزادسیمال نشان میدهد rmmod_ درایور را با استفاده از • video ram.از حافظه خارج میکنیم

صفحه43

: دسترسی به پور ت ورودی/خروجی خاصفصل هشتم86xدر معماری

86xرابط سخت افزاری در معماری د‌تافزار ، بجببای نگاشببت حببافظه86xبرخلف معماری های دیگر در معماری برای دسترسببی بببه سببخ

بیببتی۱۶دستگاه به حافظه مجازی، یک مکانیزم دیگری در نظبر گرفتببه شببده اسبت و آن یبک آدرس دهبی مستقیم بوده که به آن آدرس پور ت نیز گفته میشود.از آنجاییکه این مکانیزم یک روش اضافه تببر نسبببت بببه معماری های دیگر میباشد،برای کار با آن نیز دستورا ت اضافی (کببدهای اسببمبلی/ کببد ماشببین) آمبباده شببده

بیببت (یببک۱۶ بیت (یببک بببایت) ، ۸ به ترتیب جهت خواندن inl و inw و inbاست.برای مثال دستورا ت outw و outb بیت (یک کلمه بزرگ) از ورودی یک دستگاه نگاشت شده و متناظرا ًا دسببتورا ت ۳۲کلمه) و

جهت نوشتن در یک دستگاه نگاشت شده از طریق پور ت یا پورتها در دسترس میباشببد.لزم بببه ذکببرoutlو /است که دستورا ت فوق بصور ت زیر و در هدر فایل < .asm io h: تعریف گردیده اند <

;( 8 ( u inb unsigned long port16 ( u inw unsigned long );port

32 ( u inl unsigned long );port

void ( 8 , outb u value unsigned long );port

void ( 16 , outw u value unsigned long );port

void ( 32 , outl u value unsigned long );port

شاید برای شما سؤالی مطرح شود که کدام دستگاه ورودی/خروجی و کدام پور ت مببورد نظببر میباشببد؟ هر دستگاه استاندارد از قبل محل نگاشببت آن86xپاسخ به این پرسش ها ساده است بدلیل اینکه در معماری

proc/تعریف گردیده است.با استفاده از خروجی فایل ioportsمیتببوانیم ایببن نگاشببت هببا کببه در کرنببل / د‌هها عبارتنببد از تببایمر ، ،درگبباهDMAتعریف گردیده است را در سیستم خببود مشبباهده نمبباییم.ایببن دسببتگا

د‌ینمایید:PCI ، رابط باس RTCسریال،درگاه موازی ، وغیره میباشدکه در شکل زیر مشاهده م

صفحه44

86xدسترسی به پورت سریال در

ا ًل ذکر شد هر دستگاه در معماری از قبل به حافظه نگاشت شده اسببت.بببرای مثببال86xهمانطور که قب0اولین پور ت سریال ،همیشه در آدرس 3 8x F 0 تا 3x FFا ًل بببه چببه نگاشت شده است.ولی ایببن نگاشببت عم

معنی است و این اطلعا ت چه کمکی به ما جهت دسترسی به پور ت سریال میکند و ما چه کارهایی را میتببوانیمانجام دهیم؟

د‌یشببود کببه بببه همانگونه که در فصل اول اشاره شد هر درگاه سریال توسط یک دیوایس کنترلر سریال،کنترل مUniversal/ ( آن Asynchronous Receiver Transmitter( یا UARTا ًل گوینببد یببا مث

/ ا (برای تایمر، م /Universal Synchronous Asynchronous Receiver Transmitte( 16550PC مدل IC از UARTد‌ را داریم.در یک کامپیوتر معمولی کنترلر USARTیا Dاستفاده میکنببد کببه

مشخصا ت آنرا میتوانید از نشانی های زیر دریافت نمایید:

:// . . / / / 16550 .http wwwnational com ds PC PC Dpdf

:// . / / -http esrijan com DDK LDDK Package

:// . / . ? =http esrijan com indexphp pagefile lddk

صفحه45

ا ًل بببرای پببور ت د‌سها میتوانیم از ماکروهایی که تعریف شده است استفاده نمبباییم. مث بجای هارد کد کردن آدر/سریال میتوانم از هدر فایل < _ .linux serial regh:ا ًل > استفاده کنیم مث

# _ _ 0 3 8define SERIAL PORT BASE x F

ا ًل برای خواندن ونوشتن بروی پور ت سریال از کد زیر استفاده میشود: مث

8 ;u val = ( _ _ + _ val inb SERIAL PORT BASE UART LCR /* 3 */);

( , _ _ + _ outb val SERIAL PORT BASE UART LCR /* 3 */);

Line همان LCRکه منظور از Control Register.میباشد و برای یک یا صفر کردن تمام بیتها میتوان از کد زیر استفاده کرد:

8 ;u val

= ( _ _ + _ val inb SERIAL PORT BASE UART LCR /* 3 */); /* */Setting DLAB

|= _ _ val UART LCR DLAB /* 0 80 */x ;( , _ _ + _ outb val SERIAL PORT BASE UART LCR /* 3 */);

/* */Clearing DLAB

&= ~ _ _ val UART LCR DLAB /* 0 80 */x ;( , _ _ + _ outb val SERIAL PORT BASE UART LCR /* 3 */);

Divisor همان DLABکه منظور از Latch Access Bit.میباشد

واقعیLEDساخت یک د‌تافزار و درایورها در لینوکس ساخت یببک کیبب ِت درایببو ِر دسببتگاه یک مثال عملی برای دستیابی به سخ

‌LEDد‌ را بصور ت چشمک زن درآوریم.برای اینکار یک LED) میباشد.سپس میخواهیم یک LDDKلینوکس(د‌ههای ۳۳۰با مقاومت را با قطببع وLED) متصل میکنیم.چشمک زن کردن GND (۵) و TX (۳ اهم را به پای

صفحه46

میلی ثانیه یکبار وآنهم توسط لود کردن و از حافظه خارج کردن درایببور شبببیه۵۰۰وصل کردن جریان درهر _ سبببازی میکنیبببم.ببببرای اینکبببار بایبببد متناوببببا ًا از دسبببتور .insmod blink ledko و rmmod

_ .blink ledko استفاده نماییم.بنابراین کد ما باید چیزی شبیه زیر باشد که آنرا _ .blink led cنامگببذاری میکنیم:

# < / . >include linux module h

# < / . >include linux version h

# < / . >include linux types h

# < / . >include linux delayh

# < / . >include asm io h

# < / _ . >include linux serial regh

# _ _ 0 3 8define SERIAL PORT BASE x F

int __ _ ()init init module

{ int ;i 8 ;u data

= ( _ _ + _ );data inb SERIAL PORT BASE UART LCR

for ( = 0; < 5; ++)i i i

{ /* */Pulling the Tx line low

|= _ _ ;data UART LCR SBC

( , _ _ + _ );outb data SERIAL PORT BASE UART LCR

(500);msleep

/* */Defaulting the Tx line high

&= ~ _ _ ;data UART LCR SBC

( , _ _ + _ );outb data SERIAL PORT BASE UART LCR

(500);msleep

} return 0;}

صفحه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معرفی system یک ioctlکنترل ورودی/خروجی یا به اختصار 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د‌یشود و سپس یک تابع اشبباره گببری مناسببب بببا آن راه د‌هسازی م ا ًل به عنوان بخشی از درایور پیاد معمو

د‌یگردد. دقیقا ًا مانند system اندازی م call() ا ًل برای بکاربردیم.write و ()read هایی که ما قب

صفحه49

file_در درایورهای کاراکتری و در ساختار oprationا ًل کار کردیم، یببک فیلببد اشبباره گببر بببه تببابع که قبioctl نیز وجود دارد که با _unlocked ioctl د‌یشود (البته از کرنل نسخه به بعد).۲.۶.۳۵ شناخته م

/در فضای یوزر با استفاده از هدر فایل < .sys ioctl hو به روش زیر میتوان به ایببن تببابع نوشببته شببده در < درایور دسترسی داشت:

( , , ...);int ioctl int fd int cmd

درایور نوشته شده است و یک متغییر برای فرسببتادنioctl نظیر آن چیزی است که در تابع cmdدر اینجا هر نوع آرگومان به درایور میباشد.نکته قایل توجه این است که «دستور» و نوع آرگومان «دسببتور» بایببد بیببن

ا ًل در هدر فایلهای هر فضا قرار میگیرید. فضای یوزر و کرنل مشابه باشد.بنابراین این تعاریف معمو

دسترسی به متغیر داخل درایورد‌هکننده بال، بهتر است یک مثالی بزنیم.بیایید مجمببوعه کببد «دیببباگ یببک برای در ک بهتره تئوری خست

دارد کببهego و status و dignityدرایور» را بررسی نماییم.این درایور سه متغیر استاتیک بببه نامهببای د‌یشوند و عملیاتی را در درایور انجام میدهند.هببدر فایببل < _مقداردهی م .query ioctl hکببه مببا تعریببف <

د‌یشود مانند کد زیر: میکنیم شامل «دستور» و نوع آرگومان «دستور» را شامل م

# _ _ifndef QUERY IOCTL H

# _ _define QUERY IOCTL H

# < / . >include linux ioctl h

typedef struct

{

, , ;int status dignity ego

} _ _ ;query arg t

# _ _ _ (' ', 1, _ _ *)define QUERY GET VARIABLES IOR q query arg t

صفحه50

# _ _ _ (' ', 2)define QUERY CLR VARIABLES IO q

# _ _ _ (' ', 3, _ _ *)define QUERY SET VARIABLES IOW q query arg t

#endif

_ را در فایلioctlدرایور () .query ioctl c :نوشتیم که در زیر آمده

# < / . >include linux module h

# < / . >include linux kernelh

# < / . >include linux version h

# < / . >include linux fs h

# < / . >include linux cdevh

# < / . >include linux deviceh

# < / . >include linux errnoh

# < / . >include asm uaccessh

# " _ . "include query ioctl h

# _ 0define FIRST MINOR

# _ 1define MINOR CNT

_ ;static dev t dev

_ ;static struct cdev c dev

* ;static struct class cl

صفحه51

= 1, = 3, = 5;static int status dignity ego

_ ( * , * )static int my open struct inode i struct file f

{

0;return

}

_ ( * , * )static int my close struct inode i struct file f

{

0;return

}

# ( _ _ < _ (2,6,35))if LINUX VERSION CODE KERNEL VERSION

_ ( * , * , , 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

. = ;qstatus status

. = ;qdignity dignity

. = ;qego ego

( _ _ (( _ _ *) , & , ( _ _ )))if copy to user query arg t arg q sizeof query arg t

{

- ;return EACCES

}

;break

_ _ :case QUERY CLR VARIABLES

= 0;status

= 0;dignity

= 0;ego

;break

_ _ :case QUERY SET VARIABLES

( _ _ (& , ( _ _ *) , ( _ _ )))if copy from user q query arg t arg sizeof query arg t

{

- ;return EACCES

}

= . ;status q status

= . ;dignity qdignity

= . ;ego qego

;break

صفحه53

:default

- ;return EINVAL

}

0;return

}

_ _ =static struct file operations query fops

{

. = _ ,owner THIS MODULE

. = _ ,open my open

. = _ ,release my close

# ( _ _ < _ (2,6,35))if LINUX VERSION CODE KERNEL VERSION

. = _ioctl my ioctl

#else

. _ = _unlocked ioctl my ioctl

#endif

};

__ _ _ ( )static int init query ioctl init void

{

;int ret

* _ ;struct device dev ret

صفحه54

(( = _ _ (& , _ , _ , " _ ")) < 0)if ret alloc chrdev region dev FIRST MINOR MINOR CNT query ioctl

{

;return ret

}

_ (& _ , & _ );cdev init c dev query fops

(( = _ (& _ , , _ )) < 0)if ret cdev add c dev dev MINOR CNT

{

;return ret

}

( _ ( = _ ( _ , " ")))if IS ERR cl class create THIS MODULE char

{

_ (& _ );cdev del c dev

_ _ ( , _ );unregister chrdev region dev MINOR CNT

_ ( );return PTR ERR cl

}

( _ ( _ = _ ( , , , , " ")))if IS ERR dev ret device create cl NULL dev NULL query

{

صفحه55

_ ( );class destroy cl

_ (& _ );cdev del c dev

_ _ ( , _ );unregister chrdev region dev MINOR CNT

_ ( _ );return PTR ERR dev ret

}

0;return

}

__ _ _ ( )static void exit query ioctl exit void

{

_ ( , );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

_ (" < _ _ - _ _ >");MODULE AUTHOR Anil Kumar Pugalia email at sarika pugs dot com

صفحه56

_ (" () ");MODULE DESCRIPTION Query ioctl Char Driver

_ و در نهایت ، تابع فراخوان را در فضای کاربر مینویسیم و نام فایل آنرا .query app cمیگذاریم که در زیر مشاهده میکنید:

# < . >include stdioh

# < / . >include sys types h

# < . >include fcntl h

# < . >include unistdh

# < . >include stringh

# < / . >include sys ioctl h

# " _ . "include query ioctl h

_ ( )void get vars int fd

{

_ _ ;query arg t q

( ( , _ _ , & ) == -1)if ioctl fd QUERY GET VARIABLES q

{

(" _ ");perror query apps ioctl get

}

else

{

صفحه57

(" : % \ ", . );printf Status d n qstatus

(" : % \ ", . );printf Dignity d n qdignity

(" : % \ ", . );printf Ego d n qego

}

}

_ ( )void clr vars int fd

{

( ( , _ _ ) == -1)if ioctl fd QUERY CLR VARIABLES

{

(" _ ");perror query apps ioctl clr

}

}

_ ( )void set vars int fd

{

;int v

_ _ ;query arg t q

(" : ");printf Enter Status

("% ", & );scanf d v

();getchar

. = ;qstatus v

(" : ");printf Enter Dignity

صفحه58

("% ", & );scanf d v

();getchar

. = ;qdignity v

(" : ");printf Enter Ego

("% ", & );scanf d v

();getchar

. = ;qego v

( ( , _ _ , & ) == -1)if ioctl fd QUERY SET VARIABLES q

{

(" _ ");perror query apps ioctl set

}

}

( , * [])int main int argc char argv

{

* _ = "/ / ";char file name dev query

;int fd

enum

{

_ ,e get

_ ,e clr

صفحه59

_e set

} ;option

( == 1)if argc

{

= _ ;option e get

}

( == 2)else if argc

{

( ( [1], "- ") == 0)if strcmp argv g

{

= _ ;option e get

}

( ( [1], "- ") == 0)else if strcmp argv c

{

= _ ;option e clr

}

( ( [1], "- ") == 0)else if strcmp argv s

{

= _ ;option e set

}

else

صفحه60

{

( , " : % [- | - | - ]\ ", [0]);fprintf stderr Usage s g c s n argv

1;return

}

}

else

{

( , " : % [- | - | - ]\ ", [0]);fprintf stderr Usage s g c s n argv

1;return

}

= ( _ , _ );fd open file name O RDWR

( == -1)if fd

{

(" _ ");perror query apps open

2;return

}

( )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

0;return

}

query_درایور ioctl فایل) _ .query ioctl ko را بسازید و برنامه (فایل ( _ .query app cرا با ( کنید .make زیر Makefileاستفاده از

# , .If called directly from the command line invoke the kernel build system

($( ),)ifeq KERNELRELEASE

_ := / / /KERNEL SOURCE usr src linux

:= $( )PWD shell pwd

: _default module query app

:module

$( ) - $( _ ) =$( ) MAKE C KERNEL SOURCE SUBDIRS PWD modules

صفحه62

:clean

$( ) - $( _ ) =$( ) MAKE C KERNEL SOURCE SUBDIRS PWD clean

${ } _RM query app

# ; ' Otherwise KERNELRELEASE is defined we ve been invoked from the

# .kernel build system and can use its language

else

- := _ .obj m query ioctl o

endif

_حال .query app c و _ .query ioctl c:را با عملیا ت زیر امتحان کنید

_ درایور را با استفاده از دستور • .insmod query ioctl koدر کرنل لود کنید

query_برنامه • app:را با استفاده از «دستور» و آرگومانهای خط دستور زیر اجرا نمایید

query_اجرای بدون آرگومان • appبرای مشاهده متغیرهای درایور

query_اجرای برنامه • app با آرگومان cبرای پا ک کردن متغییرهای درایور -

query_اجرای برنامه • app با آرگومان gبرای مشاهده متغیرهای درایور -

query_اجرای برنامه • app با آرگومان sبرای مقداردهی متغیرهای درایور -

rmmod_ حذف درایور از کرنل با استفاده از دستور • query ioctl

صفحه63

فصل دهم : دیباگ کردن کرنل در لینوکسddd در لینوکس و در فضای یوزر چندیدن دیباگر معروف از قبیل ، gdbو … وجود دارند. همینطور

به بعد معرفی گردیده است.۲.۶.۲۶ از کرنل نسخه kgdbدر فضای کرنل نیز دیباگری بنام

چالش دیباگ کردن در فضای کرنل همانگونه که برای دیباگ کردن در فضای یوزر به یک اینترفیس (رابط کاربری) احتیبباج داریببم بببرای

فضای کرنل نیز این اینترفیس به دو طریق در اختیار ما خواهد بود: روش نخست قراردادن دیباگر در خود کرنل میباشد که طبیعتا ًا اینترفیس نیز از طریق کنسول معمولی•

(تببا قبببل از آن۲.۶.۳۵در اختیار ما خواهد بود.برای انجام اینکار باید برای نسخه های کرنببل بعببد از بصبببور ت غیبببر رسبببمی نیبببز وجبببود داشبببت و میبایسبببت سبببورس دیبببباگر را از سبببایت

ftp://oss.sgi.com/projects/kdb/downloadدانلود کرده و پببچ لزم را بببه کرنببل اضببافه را باید در سورس کرنل فعال کرد و سپس کرنل را کامپایل و راه اندازی نمببود.در هنگببامkdbکرد)

بو ت شدن، خود کرنل اینترفیس دیباگ را در اختیار ما قرار میدهد. روش دوم راه اندازی سرو ِر دیباگ ِر کرنل میباشد که با استفاده از یک کلین ِت راه دور یا از طریق رابط•

و ازkgdb و یا gdbشبکه یا پور ت سریال میتوان به این سرور متصل شد و دیباگ را با استفاده از اتصال از طریق پور ت سریال در کرنل گنجانببده شببده اسببت۲.۶.۲۶سمت کلینت انجام داد.از نسخه

بهkgdbولی اگر شما علقمند به اتصال از طریق شبکه میباشید میبایست یک پچ را از سایت پروژه //:نشانی . / /http sourceforgenet projects kgdbدانلببود کببرده و بببه سببورس کرنببل

اضافه نمایید. را در سورس کرنل فعال کبرده ، کرنببل را کامپایبل و نصبب نمببوده وkgdbدرهردوحالت شما نیاز دارید تا

سیستم را با کرنل جدید راه اندازی نمایید.توجه داشته باشید که در هر دو حالت وبر خلف ساخت ماژول هببا ، شما به تمام سورس کرنل نیاز دارید و تنها در اختیار داشتن هدر فایلهببا کببافی نمیباشببد.در ادامببه فقببط روش

دیباگ از طریق پور ت سریال آموزش داده میشود.

kgdbپیکربندی کرنل لینوکس با برای پیکربندی کرنل جهت دیباگ کردن نخست باید سورس کامل کرنل را در اختیار داشته باشببید و

kernel.یا اینکه آنرا از سایت org دانلود نماییببد.قبببل از هببر چیببز بایببد kgdbفعببال شببود. بببرای ایببن

صفحه64

_منظورکرنل باید با سوئیچ =CONFIG KGDB yکانفیگ شود.بعلوه جهت دیباگ کردن از طریببق پببور ت _سریال باید سوئیچ _ _ =CONFIG KGDB SERIAL CONSOL yنیز بکار برده شود.همچنین برای اینکه

د‌ههای سمبولیک توکار _ برای ما مفهوم تر باشد میتوان از سببوئیچ kgdbداد _CONFIG DEBUG INFO د‌قتببر نشببانگرهای فریببم در کرنببل از سببوئیچ د‌یشببود بببرای فهببم دقی نیببز اسببتفاده کببرد.و پیشببنهاد م

_ _ =CONFIG FRAME POINTER yد‌نها در مواقعی که از دستورا ت زیر جهت نیز استفاده شود.این آپشkernel پیکربندی کرنل استفاده کنیم و در منوی hacking:در اختیار میباشد

# make mrproper To clean up properly

# make oldconfig Configure the kernel same as the current running one

# make menuconfig Start the ncurses based menu for further configuration

اجرا کرد.sudo یا با استفاده از rootالبته جهت اجرا باید دستور سوم را حتما ًا با مجوز د‌نها را در شکل زیرمشاهده میکنید: آپشن ها و منو ها و چگونگی دسترسی به آ

CONFIG_ از راه دور ---> gdb دیباگ کرنل با • KGDB _ از طریق سریال ---> kgdb استفاده از • _ _CONFIG KGDB SERIAL CONSOLE debug کامپایل کرنل با استفاده از • infoإ <--- _ _CONFIG DEBUG INFO

صفحه65

_کامپایل کرنل با استفاده از فریم اشاره گرها ---> • _CONFIG FRAME POINTER ساخته و سببپس بببا اسببتفاده ازmakeبعد از انجام تغییرا ت و ذخیره کردن آن، کرنل را با استفاده از دستور

make دستور install نصب نمایید و جهت بو ت کردن سیستم با کرنل جدیببد grub.را تغییببر دهیببد /بسببته بببه اینکببه توزیببع شببما چیسببت بایببد فایببل تنظیمببا ت را در مسببیر هببای .etc grubconfویببا /

/ / .boot grub menu lst بیابیببد. وقببتی تمببام مراحببل فببوق انجببام شببد بایبد پبارامتر کرنببل /-kgdb

related نیز همانطور که در زیر مشاهده میکنید به grub:اضافه شود

kgdboc جهت اتصال gdb:از طریق پور ت سریال میباشد که به فرمت زیر است = < > , < >kgdboc serial device baud rate

•> serial device) فایل : <Portدستگاه سریال بروی دستگاهی کببه کرنببل بببروی آن دیببباگ ( میشود.

•> baud rate.نرخ انتقال پور ت سریال میباشد <

بببه آن متصببل نشببدهgdb به کرنل میگوید که در هنگام بو ت ، تا زمانی که کلینببت kgdbwaitپارامتر بیاید.kgdbocاست باید منتظر بماند.این پارامتر حتما ًا باید بعد از

برداشته و سیستم را باgdb برای استفاده vmlinuxالن باید یک کپی از ایمیج کرنل ساخته شده یا همان با استفاده از پور ت سریال به آن متصل شود.gdbکرنل جدید راه اندازی نمایید. سپس باید منتظر بمانید تا

dev/ اطلعا ت، در فایلی شبیه kgdbبرای ttyso.برای اولین پور ت سریال ثبت میشود /

صفحه66

بروی کلینتgdbپیکربندی کارهای زیر میبایست انجام شود:

null اتصال کلینت به کامپیوتری که کرنل را اجرا میکند بببا اسببتفاده از کابببل • modem (مانند کابل کرا ِس سریال) .

د‌هاید را بروی شاخه جاری کلینت قرار دهید.• کپی که از ایمیج کرنل ساخته شده گرفت در هنگامی که سیستم هدف در انتظار اتصال میباشد با استفاده از کلینببت دسببتورا ت زیببر را•

اجرا کنید:• ( ) gdb file vmlinux

• ( ) - -gdb set remote interrupt sequence Ctrl C

• ( ) 115200gdb set remotebaud

• ( ) / / 0gdb target remote dev ttyS

• ( ) gdb continue

. همان کپی ایمیج میباشدvmlinuxدر دستور فوق

صفحه67

درلینوکس-قسمت اولUSBفصل یازدهم :درایورهای

در لینوکسUSBتشخیص یک دستگاه یUSB نیز در کرنل لود نشده باشد، باز لینوکس باید دستگاه USBدر لینوکس حتی اگر هیچ درایو ِر

USB-استاندارد شما را با استفاده از قسمت enabledد‌تافزار شناسایی کند.دلیل آن در فضای کرنل و سخد‌هاند. فضببایUSB با توجه به پروتکل USBاین است که تمام دستگاههای استاندار ِد طراحببی و سبباخته شببد

د‌تافزار با استفاده از کنترل ِر میزبا ِن ) بایببد دسببتگاه86X در معماری PCI (یک با ِس دیوایس شبیه USBسخ میبایست فعال شود تا کار ترجمببه اطلعببا تUSBرا شناسایی کند.برای کار با دستگاه، درایو ِر کنترل ِر میزبا ِن

دربارهUSB سطح بال را انجام دهد.مشخصا ت پروتکل USBسطح پایین فیزیکی ، به اطلعاتی با مشخصا ت د‌هسببازی شببده اسببت بنببابراین تشببخیصusbcore در یک لیه مرکزی کرنل بنببام USBدستگاههای پیاد حتی بدون وجود درایورشان در فضای کرنل قابل انجام است.USBدستگاههای

د‌ههای مختلفی (بسته به توزیع لینوکس) وجود دارد تببا نمببایی از دسببتگاه د‌طها و برنام در مرحله بعد، درایورها رابUSBشناسایی شده را به فضای کاربر منتقل نمایند.شکل زیر یک شمای کلی بال به پایین از زیر سیسببتمهای USB:لینوکس را نشان میدهد

صفحه68

را بببا مجببوز ریشببهlsusb شناسایی شده میتوانید دسببتور USBبرای بدست آوردن لیستی از دستگاههای بببه سیسببتم راUSB را قبل و بعد از اتصال یببک قلببم نببوریlsusbصادر نمایید. شکل زیر خروجی دستور

مشاهده میکنید:

د‌شفرض لببود میگببردد.ایببن درایببورusbfsدر توزیعهایی مانند فدورا ، مندریوا و … درایور بصور ت پی / و ببباproc شناسببایی شببده را از راه USBکمک میکند تا اطلعا ت جزئی تر و تکنیکی تری از دسببتگاههای

/ استفاده از دستور / / /cat proc bus usb devisesداشته باشیم. شکل زیر یک قسمت از همان دستگاه ا ًل شامل یک قسمت از هر دستگاه شناسببایی شببده توسببط لینببوکس قلم نوری را نشان میدهد. این لیست معمو

شماست:

صفحه69

USBرمزهگشایی از دستگاه

USB نخست بهتر است معماری آن را بشناسیم. تمببام دسببتگاههای USBبرای رمزگشایی از دستگاه د‌شفر ِض رایج تریببن دارای یک یا چند تنظیم میباشند. منظور از تنظیم ، چیزی شبیه پروفایل است که تنظی ِم پی نوع را در بر دارد. لینوکس فقط از یک تنظیم برای هر دستگاه پشتیبانی میکند (اولین پیببش فببرض).بببرای هببر

د‌یدهد.USBتنظیم ، دستگاه ممکن است یک یا چند رابط داشته باشد که هر رابط یک کار مشخص را انجام مMDF- میتواند رابطهای مختلفی را برای کارهای مختلف داشته باشد که بببه آن USBهر دستگاه multi

function device یا «یک دستگاه با کارکرد چندگانه» گویند. برای مثال یک چبباپگر چنببدکاره (USB میتواند چاپ کند ، اسکن کند و فکس ارسال کند.پس ما در اینجا حداقل به سه رابط نیاز داریم. بنابراین درایو ِر

د‌طهبباUSBدستگاههای ا ًل بببرای راب برعکس درایورهای دیگر بجای اینکه برای دستگاهها نوشته شببوند، معمببود‌طهای مختلببفUSBنوشته میشوند.پس ممکن است برای هر دستگا ِه چندین درایور وجود داشته باشد و یا راب

میتواند درایورهایی مشتر ک داشته باشند.ولی هر رابط حداکثر میتواند یک درایببور داشببتهUSBیک دستگاه حتی با چندین رابط نیز تنها یک درایور داشته باشد.USBباشد و نه بیشتر. البته بهتر است که هر دستگاه

صفحه70

نشان میدهد که رابببط مببورد نظببر بببه کببدامDriver/) کلمه ...=procدر شکل فوق (محتوی شاخه نیز نشان میدهد برای این رابط، درایوری تعیین نگردیده.noneدرایور متصل شده است . کلمه

د‌طها باید یک یا چند شبیه لوله بببرای انتقببالEndPoint وجود داشته باشد. یک EndPointبرای تمام راب بسته به نوع اطلعا ت میتواند بهEndPointاطلعا ت از/به (بسته به نوع عملکرد) راب ِط دستگاه میباشد. هر

د‌مبندی شود. د‌هی کنترل، وقفه، حجم و همزمانی تقسی چهار گون ای خاص مجازی صفر از نوع کنببترل میباشببندEndPoint دارای یک USBتمام دستگاههای معتب ِر

USB دوطرفه (از/به) میباشد. شکل زیر یک نمای تصویری کامل از یک دسببتگاه EndPointکه فقط یک استاندارد با توضیحا ت بال میباشد:

قسمتهای دستگاه یو اس بی را بیاد بیاورید (تصویرصفحه قبل) ، اولین حرف از هر خط ، انواع مشخصا تا ًل حرف USBقسمتهای مختلف یک دستگاه C برای دسببتگاه، D (که شرح داده شد) را نمایندگی میکند. مث

و... Endpoint برای E برای رابط و Iبرای پیکربندی، جزئیا ت هر یک از انوع مختلف در سورس کرنل و در نشانی زیر وجود دارد:

/ / / _ _ .Documentation usb proc usb info txt

USBنوشتن درایور قلم نوری

صفحه71

ا ًل اطلعا ت مببا درUSB هنوز اطلعا ت ما در مورد پروتکل USBبرای نوشتن درایور کافی نیست. مثد‌طها، خطوط انتقال اطلعا ت و چهار نوع از ها که بحث شد. همچنیببنEndpointمورد تنظیما ت دستگاه، راب

,علیم اختصاری که در شکل دیدیم از جمله ,SBT.زیاد مفهوم نیست اما نگران نباشید.جزئیا ت به تدریج شرح داده خواهد شد.ولی نخست بیایید اولین رابط مرتبط با درایور دسببتگاه

_ بنام USBقلم نوری .pen register ko.را بنویسیم نیببز بببه تببابعUSBمشابه درایورهایی که برای دستگاههای دیگر نوشتیم، برای درایورهای دستگاههای

ا ًل نوشببتیم د‌بگر نیاز داریم. برای سرعت بیشتر میتوانیم از الگوی درایورهای دیگببری کببه قب سازنده و تابع تخرید‌نها متفاو ت خواهد بود. تفاو ت اینجاست که در درایورهای دیگر کبار ثبببت و عببزل استفاده کنیم ولی محتوای آ

باید از پروتک ِل لیه مرتبط استفاده شببود.USB مرتبط میشود ولی در درایورهای دستگاههای VFSدرایور به د‌هی بجای اینکه یک شبه فایل در فضای یوزر ارائه کند، میبایست به دستگاه واقعی درUSBدراین حالت هست

د‌تافزار متصل گردد. فضای سخAPI های هسته USB برای انجام ثبت و عزل درایور به ترتیببب زیببر میباشببد(ایببن APIهببا در هببدر فایببل

>/ .linux usbh:(تعریف شده اند <

_ ( _ * );int usb register struct usb driver driver

_ ( _ *);void usb deregister struct usb driver

usb_ساختار driver شامل فیلدهای نام درایورها و جدول IDبرای شناسایی خودکار چندین دسببتگاه USB و همچنین دو تابع جهت هندل کردن اتصال و انفصال دستگاه USB بصور ت بلدرنگ در هسته USB

_میباشد. بیایید به فایل .pen register c:نگاهی بیاندازیم

# < / . >include linux module h

# < / . >include linux kernelh

# < / . >include linux usbh

_ ( _ * , _ _ * )static int pen probe struct usb interface interface const struct usb device id id

{ ( _ " (%04 :%04 ) \ ", -> , -printk KERN INFO Pen drive X X plugged n id idVendor id

> );idProduct

0;return

}

صفحه72

_ ( _ * )static void pen disconnect struct usb interface interface

{ ( _ " \ ");printk KERN INFO Pen drive removed n

}

_ _ _ [] =static struct usb device id pen table

{ { _ (0 058 , 0 6387) },USB DEVICE x F x

{} /* */Terminatingentry

};_ _ ( , _ );MODULE DEVICE TABLE usb pen table

_ _ =static struct usb driver pen driver

{ . = " _ ",name pen driver

. _ = _ ,id table pen table

. = _ ,probe pen probe

. = _ ,disconnect pen disconnect

};

__ _ ( )static int init pen init void

{ _ (& _ );return usb register pen driver

}

__ _ ( )static void exit pen exit void

{ _ (& _ );usb deregister pen driver

}

_ ( _ );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

سپس موارد معمول برای ساخت یک درایور را تکرار میکنیم:_ ساخت درایور (• .file pen register ko بوسیله دستور (make

insmodلود کردن درایور با استفاده از دستور •

lsmodنمایش لیست درایور ها با استفاده از دستور •

rmmodحذف درایور لود شده از حافظه با استفاده از دستور •

د‌یبینم خروجی برنامه آن چیزی نیست کببه انتظببارش را داشببتیم. و زیببرdmesgاما با کمال تعجب م / را چک کنید تا جزئیا ت بیشتری دستگیرتان شود. همانطور که در قبل اشاره شد درایورهای یببوprocشاخه

اس بی متفاو ت از درایورهای کاراکتری هستند. دو شکل بالتر نشان میدهد که دستگاه قلببم نببوری یببک رابببطا ًل به درایور ذخیره ساز یو اس بی (0number دارد ( usb-) که قب storage.متصل شده است (

حال به منظور ارتباط درایورمان با آن رابط ، ما نیاز داریم تا ذخیره ساز یو اس بی کببه لببود شببده اسببت را ببباrmmod- استفاده از دستور usb storageاز حافظه کرنل حذف کنیم و بجای آن درایببور قلببم نببوری

USBرا بارگذاری کنیم. به محض اینکه اینکار انجام شود نتیجه مورد انتظار ما ظاهر خواهد شببد. شببکل زیببر یک نگاه اجمالی از لگ مورد نظر میباشد. با اتصال و انفصال بلدرنگ قلم نوری از کامپیوتر میتوانید فراخببوانی

های متناظر را نیز ببینید.

صفحه74

صفحه75

د‌لدوازدهم:درایورهای درلینوکس-قسمتUSBفصدوم

0درفصل قبل دیدیم که کد یکتای کارخانه سازنده قلم نوری 058x f 0 و کد یکتای محصول 6387xبود. قبل بدانیم.USBاز اینکه با هم کد فصل قبل را کامل کنیم ابتدا لزم است مفاهیم بیشتری را از پروتکل

Endpoint ها در USBو انواع آن میتواند یک یا چنببدUSBبسته به نوع و خصوصیا ت اطلعاتی که میبایست منتقل شود، یک دستگاه

Endpoint داشته باشد و هر Endpoint:میتواند یکی از انواع زیر باشد

–کنترل : برای ارسال اطلعا ت کنترلی بعنوان مثال ریست کردن دستگاه ، گرفتن اطلعببا ت وضببعیت•د‌شفرض USBیدستگاه و غیره. در تمام دستگاهها کنترل بببه صببفر اشببارهEndpoint بصور ت پی

میکند.ا ًل تا • بایت از وقفه اسببتفاده میشببود . بعنببوان مثببال8وقفه : برای ارسال اطلعا ت کوتاه و سریع ، معمو

انتقال اطلعا ت برای پور ت سریال یا دستگاههای رابط شخصی مانند کیبورد و ماوس و غیره. انبوه : برای انتقال اطلعا ت حجم انبوه و در مقایسه با وقفه کم سرعت تر استفاده میگردد . بعنوان یک•

مثال میتوان به انتقال اطلعا ت به یک فلش مموری اشاره کرد . متقارن :برای حجم زیاد اطلعا ت با تضمین پهنای باند ثابت از این نوع استفاده میشببود.ولببی جببامعیت•

اطلعا ت ممکن است در این روش تضمین نشود. بعنوان مثال میتوان به انتقبال اطلعبا ت حسباس ببهزمان مانند صدا و تصویر اشاره کرد.

ها بصور ت ورودی و خروجی میتوانند عمببل کننببد. بببدینEndpoint کنترل، بقیه Enpoint به جز با اسببتفادهEndpoint میتواند به کامپیوتر منتقل گردد و برعکس. هر USBمعنی که اطلعا ت از دستگاه

) نشانگر مسیر اطلعا ت (صفر برای خروجببی و یببکMSB بیت شناسایی میگردد که پر ارزشترین بیت (۸از د‌ کنترل دوطرفه میباشد بدین معنی که این بیت نادیده گرفته میشود.Endpointبرای ورودی) میباشد.

متصل شده به یک کامپیوتر را نشان میدهد:USBشکل زیر مشخصا ت دستگاه

صفحه76

د‌هاند Eخطوطی که با از نببوعEndpoint هستند که در شکل فوق یببک Endpoint شروع شد دستگاه قلم نوری میباشند.UHCI از نوع «انبوه» و مربوط به کنترلر مرکزی Endpoint«وقفه» و دو

0 به هگزا دسیمال بوده که ببه ترتیبب Endpointاعدا ِد 81x 0 و 01x 0 و 82xمیباشببد. بیبت پبرارزش Endpoint) اول و سببوم بببا Iنشببان داده شببده کببه بیببانگر ورودی میباشببد. همچنیببن بیببت پببرارزش ( Endpoint) نشان داده شده که بیانگر خروجی میباشد. 0د‌ دوم با (MXPSنیز نشانگر ماکزیمم سایز پکت

د‌یشود را تعیین میکند. همببانطور کببه در شببکل میباشد یعنی بیشترین سایز اطلعاتی که دفعتا ًا میتواند منتقل م نیببز نشببانگر فاصببلهIvl بیت میباشد. ۶۴ «انبوه» Endpoint بیت بوده و ۲ «وقفه» Endpointمیبینیم

د‌هها بسیار مهم و قابل تببوجه زمانی بین انتقال دو پکت اطلعا ت متوالی برای انتقال مناسب میباشدکه برای وقفاست.

USBآنالیز قسمتهای مختلف یک دستگاه

صفحه77

ا ًل در مورد خطوطی که با : مشخص شده بودند بحث کردیم، حال زمان مناسبببیEاز آنجایی که ما قب است تا در مورد بقیه فیلدهای مرتبط نیز صحبت کنیببم. بصببور ت خلصببه ایببن خطببوط یببک دیببد کباملی از

.خصوصیا ت دستگاه یو اس بی را به ما میدهند هست و نشببانگر مببوقعیت دسببتگاه درTبا مراجعه به شکل قبل، اولین حرف از اولین خط هر بخش از دستگاه

شناختهUSB و پور ت USB ، سطح درخت USBدرخت یو اس بی میباشدکه بوسیله سه جزء شماره باس نشانگر توضیحا ت دستگاه است و شامل حداقل نسخه دستگاه ، دسته بندی/کلس دسبتگاه وDمیشود. حرف

تعداد تنظیما ت در دسترس برای این دستگاه میباشد.ا ًل یکی بیشتر نیست. خطوطی که با حببرف Cچندین حرف C در خطوطی از شکل فوق وجود دارد اگرچه معمو

حببداکثر قببدر ت (واقعببی وشروع میشوند شرح و شاخصی اسببت بببرای تنظیمببا ت خصوصببیا ت ایببن دسببتگاه، ها باید در این تنظیما ت نوشته شود. Interfaceفعلی)دستگاه و تعداد

هببایInterface نیز باید وجود داشته باشد و در مببواقعی کببه Iباتوجه به توضیحا ت بال ، حداقل یک خط ها یکسان استInterface مانند وقتی که شماره ترتیبی وجود داشته باشد میتواند تعداد بیشتری نیز باشد.

ولی خصوصیاتی متفاو ت دارند بعنوان یک مثال میتوان به وب کم ها اشاره کرد. به همراه شاخص، شماره ترتیب، قابلیت کلس/دسببته ایببن رابببط،Interface مشخص کننده شرح Iحرف

میباشد.Interface این Endpointدرایوری که به این رابط متصل شده و شماره ،Endpoint ممکن است مشابه کلس دستگاه باشد و یا نباشببد.و بسببته بببه شببماره Interfaceکلس

وجود داشته باشدکه جزئیا ت آن بعدا ًا مورد بحث قرار خواهد گرفت.Eممکن است چندیدن خط د‌هیP جبباری و فعببال اسببت . خببط Interface به ترتیب نشانگر تنظیمببا ت و I و Cنشانه * بعد از شناسبب

د‌هی دستگاه را مشخص میکند . خطوط د‌هی محصول و نسخ نیز شرح مشخصببا ت فروشببنده وSفروشنده ، شناساطلعا ت توضیحی در مورد دستگاه را نشان میدهد.

د‌هاند و همچنین مشبباهده اطلعببا ت دسببتگاههای ،خببوبUSBبرای کشف اینکه چه دستگاههایی شناسایی شدcat/ است دستور / / /proc bus usb devices را اجرا کنید. شاید اکثر این اطلعببا ت بببرای نوشببتن

و با توجه به این اطلعا ت وجود دارد؟Cدرایور دستگاه مورد نیاز باشد اما آیا راهی برای نوشتن برنامه با کد جواب مثبت است و قصد داریم در ادامه همین کار را انجام دهیم. آیا به خاطر مببی آوریببد بببه محببض اینکببه

تغییببرUSB این اطلعا ت را در لیه هستهUSB به کامپیوتر وصل شد درایور کنترلر هاست USBدستگاه د‌های از تعریف کرده قرار داد؟USB ها همانطور که مشخصا ت structureداد؟ و اطلعا ت را در مجموع

/< در زیر ساختار تعریف شده در .linux usbh<د‌یبینید که برای وضوح بیشتر بصور ت معکببوس آمببده را ماست:

صفحه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یک دسببتگاه مشببخص، میتببوان بببه تمببام اطلعببا ت و / موجود است دست یافت.اما چگونه میتببوانproc مربوط به آن دستگاه، همانگونه که در USBمشخصا ت

usb_هندل ساختار deviceرا پیدا کرد؟ د‌یباشبد بلکبه هنببدل (اشباره گبر بببه سباختارInterfaceهندل دستگاه مستقیما ًا در درایور در دسبترس نم

_usb interface در دسترس میباشد. بیاد بیاورید که درایورهای (usb برای Interfaceد‌ هببا نوشببته د‌یشدند و نه برای کل دسببتگاه. اگببر نگبباهی دوبباره بببه تواببع د‌هی قبببلdisconnect و probeم برنبام

د‌هی مرکزی د‌یبینید که هست Interface به ازای هر رابط دستگاه فراخوانی میشببد..هنببدل USBبیاندازید منیز در اولین پارامتر موجود میباشد. به قسمتی از آن برنامه توجه کنید:

int (* )(probe struct _ * , usb interface interface const struct _ _ * );usb device id id

void (* )(disconnect struct _ * );usb interface interface

قابل دسترس میباشد و بببرای گرفتببنInterface ، تمام اطلعا ت مربوط به آن Interfaceبا اشاره گر هندل دستگاه ماکروی زیر مورد استفاده قرار میگیرد:

struct _ = _ _ ( );usb device device interface to usbdev interface

_ یلحال با آموخته هایمان ، کد زیر را در فا .pen info c:مینویسیم

# < / . >include linux module h

# < / . >include linux kernel h

# < / . >include linux usbh

static struct _ * ;usb device device

static int _ (pen probe struct _ * , usb interface interface const struct _ _usb device id * )id

{ struct _ _ * _ ;usb host interface iface desc

struct _ _ * ;usb endpoint descriptor endpoint

int ;i

صفحه80

_ = -> _ ;iface desc interface cur altsetting

( _ printk KERN INFO " / % : (%04 :%04 )\ "Pen i f d now probed X X n , _ -> . , -> , -> );iface desc descbInterfaceNumber id idVendor id idProduct

( _ printk KERN INFO " -> : %02 \ "ID bNumEndpoints X n , _ -> . );iface desc descbNumEndpoints

( _ printk KERN INFO " -> : %02 \ "ID bInterfaceClass X n , _ -> . );iface desc descbInterfaceClass

for ( = 0; < _ -> . ; ++)i i iface desc descbNumEndpoints i

{ = & _ -> [ ]. ;endpoint iface desc endpoint i desc

( _ printk KERN INFO " [% ]-> : 0 %02 \ "ED d bEndpointAddress x X n , , -> );i endpoint bEndpointAddress

( _ printk KERN INFO " [% ]-> : 0 %02 \ "ED d bmAttributes x X n , , -> );i endpoint bmAttributes

( _ printk KERN INFO " [% ]-> : 0 %04 (% )\ "ED d wMaxPacketSize x X d n , , -> , -> );i endpoint wMaxPacketSize endpoint wMaxPacketSize

} = _ _ ( );device interface to usbdev interface

return 0;} static void _ (pen disconnect struct _ * )usb interface interface

{ ( _ printk KERN INFO " / % \ "Pen i f d now disconnected n , -> _ -> . );interface cur altsetting descbInterfaceNumber

} static struct _ _ _ [] =usb device id pen table

{ { _ (0 058 , 0 6387) },USB DEVICE x F x

{} /* */Terminatingentry

صفحه81

};_ _ ( , _ );MODULE DEVICE TABLE usb pen table

static struct _ _ =usb driver pen driver

{ . = name " _ "pen driver , . = _ ,probe pen probe

. = _ ,disconnect pen disconnect

. _ = _ ,id table pen table

}; static int __ _ (init pen init void){ return _ (& _ );usb register pen driver

} static void __ _ (exit pen exit void){ _ (& _ );usb deregister pen driver

}

_ ( _ );module init pen init

_ ( _ );module exit pen exit

_ (MODULE LICENSE " "GPL );_ (MODULE AUTHOR " < @ - . >"Anil Kumar Pugalia email sarika pugs com );_ (MODULE DESCRIPTION " "USB Pen Info Driver );

سپس مراحل تکراری را برای ساخت داریور دوباره انجام میدهیم:_ساخت درایور (فایل • .pen infoko با استفاده از دستور ( make

_ لود کردن درایور با استفاده از دستور • .insmod pen infoko usb-اتصال قلم نوری (بعد از آنکه مطمئن شدید که • storage(در حافظه قرار ندارد انفصال قلم نوری•

صفحه82

dmesgمشاهده خروجی •

rmmod_ حذف درایور از حافظه با استفاده از دستور • pen info

شکل زیر مراحل فوق را در کامپیوتر من نشان میدهد. برای اطمینان بخاطر بیاورید که (با اسببتفاده از خروجببی/ دستور / / /cat proc bus usb devices درایور (-usb storageمعمولی هیچ اتصالی با رابط قلم

نوری نداشته باشد.

د‌هی مرکببزی و بببا اسببتفاده از سبباختار جببدولUSBدو مکببانیزم بببرای اینکببه یببک دسببتگاه بببرای هسببت_ _usb device idشناسایی گردد برای یک درایور وجود دارد. نخست اینکه زوج شناسه فروشنده و شناسه

USB_کروی ()محصول با استفاده از ما DEVICEکه در کد فوق نیز استفاده شد. و دوم اینکه با تعیین گردد _()استفاده از ماکروی _usb device infoد‌هی دستگاه مشخص گببردد. در حقیقببت ماکروهببای کلس/دست

/<بیشتری در .linux usbh<د‌بهای مختلف وجود دارد.علوه بر این چندین عدد از این ماکروهببا برای ترکی_میتوانند برای جدول _usb device id که با ورودی) nullآخرین آن مشخص میشود) تعیین شوند. کببه

د‌نها با ضوابطی تطابق پیدا میکنند و ما قادریم برای چندین دستگاه یک درایور بنویسیم. هر کدام آ

صفحه83

د‌لسیزدهم USB:انتقال اطلعا ت از/به دستگاهفص ای راInterfaceدر این فصل به این سؤال پاسخ خواهیم داد که چگونه درایور، انتخاب میکندکه چببه

ای استفاده نماید؟ Interfaceثبت کند و از چه د‌هی مرکببزی probeپاسخ به این سؤال به مقدار بازگشتی از تابع USB بازمیگردد. نکته اینجاست کببه هسببت

د‌هاند راInterface های همه probeباید ا ًل ثبببت شببد د‌نهببایی کببه قب های تمام دستگاه متصل را به جببز آ هببا گرفتببهInterface ها برای اولین بببار و بببرای تمببام probeدرخواست کند. بنابراین مقادیر بازگشتی

ا ًلInterface صفر بود بدین معنی است کببه درایببور بببرای آن probeمیشود. اگر مقدار بازگشتی یک قب د‌ انجببامInterfaceثبت گردیده ولی اگر مقدار خطایی بازگردانده شود بدین معنی است که اینکار برای ایببن

نشده است. صببحبت کنیببم ، در مببوردUSB قبل از آنکه در مببورد انتقببال نامحببدود اطلعببا ت بببه/از دسببتگاه

_ _MODULE DEVICE TABLE .بحببث میکنیببم _ _MODULE DEVICE TABLEعمببدتا بببرای depmod .در فضای یوزر میباشد Moduleیک اصطلح دیگر برای درایور است کببه میتوانببد بصببور ت

_داینامیک لود و یا از حافظه حذف گردد. ماکروی _MODULE DEVICE TABLEدو متغیببر در بخببش د‌یشود و در فایلهببایی عمببومی در زیببر شبباخهdepmodفقط خواندنی ماژولها میسازد که بوسیله استخراج م

>/ /< _lib modules kernel version د‌هسازی شببده اسببت. فایلهببای وmodulesusbmap./ پیاد.modulespcimap به ترتیب برای دستگاههای USB و PCIد‌نها ما قادریم درایورها را هستند که با آ

usb-بصور ت اتوماتیک لودکنیم. همانگونه که در لود اتوماتیک درایور storage.دیدیم

انتقال اطلعات در یواس بی:

د‌هی بیایید بر پایه کدی که در فصل قبل برای قلم نوری با کد فروشن 0د 058x f 0 وکد محصببول 6387x نوشتیم کار انتقال اطلعا ت را توسعه دهیم.

USBا ًل به شکل لیه افقی در فضای کرنل است و از اینببرو بببرای اینکببه د‌تافزار است و معمو یک پروتکل سخ ای را در فضای یوزر ایجاد کند به یکی از لیه های عمودی متصل میگردد. Interfaceیک

ا ًل در مورد آن بحث شد یک گزینه عالی برای برقببراری ارتببباط بببا لیببه افقببی درایور عمودی کاراکتری که قبUSB.است که بتوانیم تمام جریان انتقال اطلعا ت را در ک کنیم

صفحه84

اما ما نیازی نداریم تا شماره اصلی رزرو نشده وآزاد کاراکتری را بگیریم بلکه میتوانیم از شماره اصلی کاراکتری که برای فایلهای دستگاه کاراکتری رزرو شده است اسببتفاده کنیببم.بعلوه بببرای دسببتیابی کامببل درایببور180

های زیر تعریف شده اند:API وآنهم در یک مرحله، USBکاراکتری با لیه افقی

int _ _ (usb register dev struct _ * , usb interface intf struct _ _usb class driver * _ );class driver

void _ _ (usb deregister dev struct _ * , usb interface intf struct _ _usb class driver * _ );class driver

ا ًل ما انتظار داریم این توابع را به ترتیب در سازنده و تخریببب گببر ماژولمببان فراخببوانی کنیببم ولببی بببرای معمو-دستیابی به رفتار - -hot plug and play برای فایلهای دستگاه (کاراکتری) مرتبط با دستگاههای USB،

د‌نها باید به ترتیب در توابع فراخوانی شوند.disconnect و probeآ disconnect و probe است کببه در اولیببن پببارامتر Interfaceاولین پارامتر توابع فوق اشاره گر به

_دریبببافت شبببده اسبببت. پبببارامتر دوم ، سببباختار _usb class driverمیباشبببد ، قببببل از آنکبببه _ _usb register devد‌های از عملیا ت فایل دستگاه و نببام فایببل دسببتگاه فراخوانی شود نیاز است تا مجموع

pen_پیشنهادی پر شود. برای استفاده های واقعی ، به توابع probe و _pen disconnectدر کببدی _که بعدا ًا خواهیم نوشت ( .pen driver c.رجوع کنید(

بعلوه میتوانیم از عملیا ت روی فایل (خواندن ، نوشببتن و...) اسببتفاده کنیببم و آن دقیقببا ًا چیببزی اسببت کببه مبباد‌هها را به/از pen_ انتقال دهیم. USBنیازداریم تا داد write و _pen readکه در زیر نوشته شببده اسببت

_برای این منظور است و همچنین تابع () _usb bulk msg> که در) / .linux usbhآمببده اسببت) نیببز < 0 هگببز Endpointبرای انتقال حجم انبوه اطلعا ت به دستگاه قلم نوری بببا 01x0 و 82xاسببت کببه بببه

در شکل بعد شروع میشود تببوجهE ها به خطوطی که با Endpointترتیب فراخوانی میشود. برای لیست کنید:

صفحه85

مشببخص وEndpoint برای توابع انتقال اطلعببا ت بببا USB های هسته APIجهت یافتن لیست کاملی از _متفاو ت شبببیه توابببع () _usb control msg() و _ _usb interrupt msgو غیببره بببه هببدر فایببل

>/ .linux usbh<.در سورس کرنل رجوع کنید ()_usb rcvbulkpipe() و _usb sndbulkpipeو سایر ماکروهای دیگر که نیز در هدر فایل فببوق

د‌هاند. بیت شاخص، را محاسبه میکند.USB های هستهAPI برای اتصال به Endpointتعریف شدmass نکته اینکه درایور قلم نببوری بببه کلس storageد‌های از د‌یرود مجمببوع تعلببق دارد کببه انتظببار م

برای انتقال انبوه اطلعا ت استفاده شود. بنابراین یک خواندن/نوشتن ناقصی که در کببدSCSIدستورا ت شبیه د‌های را جابجا نکند. مگر اینکه دستورا ت با فرمت د‌یرود داد زیر آمده است ممکن است واقعا ًا آنطوری که انتظار م

د‌های از جریان کلی درایور است. بببرای دسببتیابی بببه حببس واقعببی وUsbمناسب باشد. اما کد زیر یک خلص مختلف شبیه آنچیزی که در نشانی زیر میباشد دست وUSBزیبای کار با انتقال اطلعا ت باید با دستگاههای

پنجه نرم کنید:

:// . . /http lddkesrijan com

و اما کد زیر :

صفحه86

# < / . >include linux module h

# < / . >include linux kernelh

# < / . >include linux usbh

# ( , ) ((( ) <= ( )) ? ( ) : ( ))define MIN ab a b a b

# _ _ 0 01define BULK EP OUT x

# _ _ 0 82define BULK EP IN x

# _ _ 512define MAX PKT SIZE

static struct _ * ;usb device device

static struct _ _ usb class driver class;static unsigned char _ [ _ _ ];bulk buf MAX PKT SIZE

static int _ (pen open struct * , inode i struct * )file f

{ return 0;}static int _ (pen close struct * , inode i struct * )file f

{ return 0;}static _ _ (ssize t pen read struct * , file f char __ * , user buf _size t , _ * )cnt loff t off

{ int ;retval

int _ ;read cnt

/* */Read the data from the bulk endpoint

= _ _ ( , _ ( , _ _ ),retval usb bulk msg device usb rcvbulkpipe device BULK EP IN

_ , _ _ , & _ , 5000);bulk buf MAX PKT SIZE read cnt

if ( )retval

{ ( _ printk KERN ERR " % \ "Bulk message returned d n , );retval

return ;retval

}

صفحه87

if ( _ _ ( , _ , ( , _ )))copy to user buf bulk buf MIN cnt read cnt

{ return - ;EFAULT

} return ( , _ );MIN cnt read cnt

}static _ _ (ssize t pen write struct * , file f const char __ * , user buf _size t , _ * )cnt loff t off

{ int ;retval

int _ = ( , _ _ );wrote cnt MIN cnt MAX PKT SIZE

if ( _ _ ( _ , , ( , _ _ )))copy from user bulk buf buf MIN cnt MAX PKT SIZE

{ return - ;EFAULT

} /* */Write the data into the bulk endpoint

= _ _ ( , _ ( , _ _ ),retval usb bulk msg device usb sndbulkpipe device BULK EP OUT

_ , ( , _ _ ), & _ , 5000);bulk buf MIN cnt MAX PKT SIZE wrote cnt

if ( )retval

{ ( _ printk KERN ERR " % \ "Bulk message returned d n , );retval

return ;retval

} return _ ;wrote cnt

} static struct _ =file operations fops

{ . = _ ,open pen open

. = _ ,release pen close

. = _ ,read pen read

صفحه88

. = _ ,write pen write

}; static int _ (pen probe struct _ * , usb interface interface const struct _ _usb device id * )id

{ int ;retval

= _ _ ( );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

} static void _ (pen disconnect struct _ * )usb interface interface

{ _ _ ( , &usb deregister dev interface class);} /* */Tableof devices that work with this driver

static struct _ _ _ [] =usb device id pen table

{ { _ (0 058 , 0 6387) },USB DEVICE x F x

صفحه89

{} /* */Terminatingentry

};_ _ ( , _ );MODULE DEVICE TABLE usb pen table

static struct _ _ =usb driver pen driver

{ . = name " _ "pen driver , . = _ ,probe pen probe

. = _ ,disconnect pen disconnect

. _ = _ ,id table pen table

}; static int __ _ (init pen init void){ int ;result

/* */Register this driver with the USB subsystem

if (( = _ (& _ )))result usb register pen driver

{ (err " _ . % "usb register failed Error number d , );result

} return ;result

} static void __ _ (exit pen exit void){ /* */Deregister this driver with the USB subsystem

_ (& _ );usb deregister pen driver

}

_ ( _ );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 );

دوباره کارهایی که برای ساخت درایور هست را انجام میدهیم:_(ساختن درایور • .pen driver ko( با استفاده از دستور make

_ لود کردن درایور با استفاده از دستور • .insmod pen driver ko usb-قلم نوری را به دستگاه وصل کنید (بعد از آنکه مطمئن شدید که درایور • storageدر

حافظه نیست)/مسیر • 0dev pen/د‌یشود را چک کنید (صفر نشانگر شماره فرعی که بصور ت داینامیک ایجاد م

را برای مقادیر سیستم خودتان چک کنید)Dmesgدرخواست شده است . /در صور ت امکان ، در فایل • 0dev pen/بنویسید یا بخوانید (شما به احتمال زیاد به دلیل عدم

برخواهید خورد)pipe قطع ارتباط خواهید شد یا اینکه به خطای SCSIاستفاده از دستورا ت /قلم نوری را از سیستم جدا کنید و دوباره • 0dev pen/را ببینید rmmod_ با استفاده از دستور• pen driver درایور را از حافظه خارج کنید

صفحه91

د‌لچهاردهم:آشنایی با مبانی دیسک سخت و پارتیشن فصfdisk- اگر دستور l:را صادر کنیم شکلی شبیه زیر را خواهیم دید

د‌متر و همچنیببن بببه بببایت همانطور که مشاهده میکنید اولین خط، سایز هارد دیسک را به فرمت قابل فه نمایش میدهد و دومین خط تعداد هدهای منطقی ، تعببداد سببکتورهای منطقببی در هببر قطبباع و تعببداد واقعببی

سیلندرها در هر دیسک را نمایش میدهد که با هم هندسه هارد را به نمایش میگذارند. هد نمایانگر تعداد صفحا ت یا دیسکها است بطوری که یک هد خواندن/نوشتن بببرای یببک دیسببک255تعداد

1Dمورد نیاز است که شماره های آنهببا ، 2,... 255D Dمیباشببد. هببر دیسببک بایببد تعببداد مشببابهی از دوایببر د‌ییابد. در مثال بببال د‌یشود و به درونی ترین خاتمه م متحدالمرکز (قطاع) داشته باشد که از بیرونی ترین شروع م

شماره گببذاری شببده اسببت.60801T تا 1T قطاع در هر دیسک وجود داشت که شماره های آنها از 60801ا ًل د‌کها را سیلندرگویند مث 1 از 2Tقطاع های هم شماره بروی دیس , 2 , … 255D D Dرا با هم سبیلندر

می نامند. هر قطاع دارای تعداد مشابهی واحد منطقی بنام بخش یا سکتور میباشد که در مثال فببوق2Cشماره

صفحه92

د‌نها نیز با 63 ,1 عدد میباشد که آ 2,... 63S S Sا ًل حجم آنها د‌یشوند و معمو بایت میباشد.512 نامگذاری مبا استفاده از اطلعا ت فوق و با فرمول زیر میتوانیم حجم واقعی قابل استفاده از دیسک را بدست بیاوریم:

(حجم هر سکتور)* (تعداد سکتور در هر قطاع) * (تعداد قطاع در هر دیسک)* (تعداد دیسک)=حجم واقعی قابل استفاده

که در مثالی که در شکل بال داریم میشود:

255 * 60801 * 63 * 512 = 500105249280 bytes bytes.

در مثال ما) باشد و دلیل آن هم ایببن500107862016ممکن است عدد شما کمی کمتر از عدد واقعی (عدد است که فرمول ما بایتهای سیلندرهای ناقص را محاسبه نمیکند. و علت اصلی آن هم اختلف تکنولوژی امببروز

د‌یترکه از هد و سیلندر و بخش استفاده میشده است بازمیگردد . د‌کها با زمانهای قدیم ساخت و دیسد‌شها و قطاع های منطقی را برمیگرداند و نه لزوما ًا فیزیکببی.fdiskنکته اینجاست که خروجی به ما هدها و بخ

سوالی که ممکن است مطرح شود این است که اگر دیسکهای امروزی مفاهیم هندسه فیزیکببی قببدیم را ندارنببدد‌یشوند؟ پس چرا هنوز هستند و بصور ت منطقی نمایش داده م

دلیل اصلی این است که باز هم بتوانیم با مفهوم پارتیشن کار کنیم و همچنان جدول پارتیشن را داشببته باشببیم که خیلی رایج بوده و به هندسه ساده فوق نزدیک هستند. DOSمخصوصا ًا جداول پارتیشن نوع

فرمول محاسبا ت سایز سیلندر بصور ت زیر میباشد:

(255 * 63 / * 512 / = 8225280 heads sectors track bytes sector bytes)

هببا بصببور تunitکه در خط سوم شکل فوق مشاهده میکنیم و در خطوط بعد علمتگببذاری پارتیشببن هببا در سیلندرهای کامل را ملحظه میفرمایید.

DOSجداول پارتیشن از نوع خیلی مبحث مهمی است ولی قبل از آن باید مفهوم پارتیشن را متببوجهDOSآموختن جداول پارتیشن

ا ًل پارتیشن داشته باشیم؟ شویم. سوالی که ممکن است مطرح شود این است که برای چه ما باید اصد‌نها یک پارتیشببن نامیببده یک هارد دیسک میتواند به یک یا چند دیسک منطقی تقسیم شود که هر کدام از آد‌هها مورد استفاده قرار میگیرد. برای مثال اطلعا ت سیسببتم عامببل د‌یشود و برای سازماندهی انواع مختلف داد م

صفحه93

های مختلف، اطلعا ت کاربران، داده های موقت و غیره.د‌مبندی منطقی برای هارد دیسک بنابراین اگر بخواهیم پارتیشن ها را بصور ت ساده بیان کنیم آنها یک نوع تقسی هستند. اطلعا ت مربوط به پارتیشنها نیز باید جایی نگهداری شود که به آن جدول پارتیشن گویند. یک جببدول

C بایت هستند. با ساختار به زبببان 16 حداکثر چهار ورودی پارتیشن دارد که هرکدام DOSپارتیشن از نوع د‌نها را مجسم کرد: زیر میتوان آ

typedef struct{

unsigned char _ ; // 0 00 - ; 0 80 - ( )boot type x Inactive x Active Bootable

unsigned char _ ;start head

unsigned char _ :6;start sec

unsigned char _ _ :2;start cyl hi

unsigned char _ ;start cyl

unsigned char _ ;part type

unsigned char _ ;end head

unsigned char _ :6;end sec

unsigned char _ _ :2;end cyl hi

unsigned char _ ;end cyl

unsigned long _ _ ;abs start sec

unsigned long _ _ ;sec in part

} ;PartEntry

0این جدول پارتیشن ، پس از یک امضاء دو بایتی 55xAA در انتهای اولین سکتور دیسک قرار میگیرد ا ًل با نام رکورد بو ت اصلی ) شناخته میشود. بنابراین شروع آفسببت ایببن جببدول پارتیشببن درMBR(که معمو

د‌یشود. همچنین یک 446) = 2 + 16 * 4 - (512 بصور تMBRداخل بایتی امضاء دیسک در آفست4 م قرار میگیرد.440ا ًل برای اولین قسمت کد بو ت که توسط بایوس برای راه اندازی سیستم از رویMBR بایت قبل از440 معمو

_دیسک سخت استفاده میگردد. سورس فایل .part info cشامل این تعاریف مختلف برای تجزیه و تحلیل و نمایش فرمت بندی شده جدول پارتیشن بروی خروجی نوشته شده است.

بیت طول دارند اشبباره10از مقادیر ساختار جدول پارتیشن، میتوان به فیلدهای شروع و خاتمه سیلندر که فقط سیلندر داشت. اما برای هارد دیسکهای حجیببم امببروزی ایببن مقببدار کببافی1023کردکه حداکثر فقط میتوان

نیست بنابراین در حالتهای اینچنینی ، سه تایی «هد ، سیلندر ، سکتور» در جدول پارتیشن برای حببداکثر مقببدار

صفحه94

د‌یشود: د‌یشود و مقدار واقعی با استفاده از دو فیلد زیر محاسبه م ثبت م_( تعداد سکتور شروع _abs start sec( و تعداد سکتور در این پارتیشن )_ _sec in part(

_سورس کد فایل .part info c:بصور ت زیر است

# < . >include stdio h

# < / . >include sys types h

# < / . >include sys stat h

# < . >include fcntl h

# < . >include unistdh

# _ 512define SECTOR SIZE

# _ _define MBR SIZE SECTOR SIZE

# _ _ _ 440define MBR DISK SIGNATURE OFFSET

# _ _ _ 4define MBR DISK SIGNATURE SIZE

# _ _ 446define PARTITION TABLE OFFSET

# _ _ 16 // ( )define PARTITION ENTRY SIZE sizeof PartEntry

# _ _ 64 // ( )define PARTITION TABLE SIZE sizeof PartTable

# _ _ 510define MBR SIGNATURE OFFSET

# _ _ 2define MBR SIGNATURE SIZE

# _ 0 55define MBR SIGNATURE xAA

# _ _define BR SIZE SECTOR SIZE

# _ _ 510define BR SIGNATURE OFFSET

# _ _ 2define BR SIGNATURE SIZE

# _ 0 55define BR SIGNATURE xAA

{typedef struct

_ ; // 0 00 - ; 0 80 - ( )unsigned char boot type x Inactive x Active Bootable

_ ;unsigned char start head

_ :6;unsigned char start sec

_ _ :2;unsigned char start cyl hi

_ ;unsigned char start cyl

_ ;unsigned char part type

_ ;unsigned char end head

صفحه95

_ :6;unsigned char end sec

_ _ :2;unsigned char end cyl hi

_ ;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

_ ( ) {void print computed unsigned long sector

, , , ;unsigned long heads cyls tracks sectors

= % 63 + 1 /* 1 */;sectors sector As indexed from

= / 63;tracks sector

= / 255 + 1 /* 1 */;cyls tracks As indexed from

= % 255;heads tracks

("(%3 /%5 /%1 )", , , );printf d d d heads cyls sectors

}

( , * []) {int main int argc char argv

* _ = "/ / ";char dev file dev sda

, , _ ;int fd i rd val

;MBRm

* = ( *)( . );PartEntry p PartEntry mpt

( == 2) {if argc

_ = [1];dev file argv

}

صفحه96

(( = ( _ , _ )) == -1) {if fd open dev file O RDONLY

( , " % : ", _ );fprintf stderr Failed opening s dev file

("");perror

1;return

} (( _ = ( , & , ( ))) != ( )) {if rd val read fd m sizeof m sizeof m

( , " % : ", _ );fprintf stderr Failed reading s dev file

("");perror

( );close fd

2;return

} ( );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

( = 0; < 4; ++) {for i i i

("% :% (%3 /%4 /%2 ) (%3 /%4 /%2 ) %02 %10 %9 \ ",printf d d d d d d d d X d d n

+ 1, !!( [ ]. _ & 0 80),i p i boot type x

[ ]. _ ,p i start head

1 + (( [ ]. _ _ << 8) | [ ]. _ ),p i start cyl hi p i start cyl

[ ]. _ ,p i start sec

[ ]. _ ,p i end head

1 + (( [ ]. _ _ << 8) | [ ]. _ ),p i end cyl hi 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

( = 0; < 4; ++) {for i i i

("% :% ", + 1, !!( [ ]. _ & 0 80));printf d d i p i boot type x

_ ( [ ]. _ _ );print computed p i abs start sec

(" ");printf

_ ( [ ]. _ _ + [ ]. _ _ - 1);print computed p i abs start sec p i sec in part

(" %02 %10 %9 \ ", [ ]. _ ,printf X d d n p i part type

صفحه97

[ ]. _ _ , [ ]. _ _ );p i abs start sec p i sec in part

} ("\ ");printf n

0;return

}

_ کد بال یک برنامه است که میتوان با . - _gcc part info c o part infoکامپایل کببرد و سببپس بببا _ / /part info dev sda آنرا اجرا نمود تا اطلعا ت پارتیشن اصلی تان یعنی .//dev sda/را ببینید .

fdiskشکل زیر خروجی این برنامه را بروی سیستم من نمایش میدهد که میتوانید خروجی آنببرا بببا خروجببی ای که در چند صفحه قبل دیدید، مقایسه نمایید.

انواع پارتیشن و رکوردهای بوت ورودی4از آنجاییکه جدول پارتیشن طوری طراحی شده (به عبار ت دیگر هاردکد شده) است که تنها

داشته باشد، و شما حداکثر این تعداد پارتیشن را میتوانید داشته باشید که به آنها پارتیشن های اصببلی گوینببد.ا ًل توسببط د‌نها یک نوع ارتباط به ورود ِی جدول پارتیشببن مربببوطه دارنببدکه ایببن نببوع هببا معمببو هر کدام از آ

صفحه98

د‌یشوند و از اینرو براساس سیستم عاملهایی مانند DOS, , ,سازندگان سیستم عاملها ابداع م Minix Linux , , , , 95, Solaris BSD FreeBSD QNX W Novell Netwareتا د‌یشوند م بندی دسته غیره و

برای/با چندین سیستم عامل استفاده شوند. یکی از چهار پارتیشن اصلی میتواند چیز دیگری نامگذاری شود که به آن پارتیشن تعمیم داده شده گوینببد و از

extended اهمیت ویژه ای نیز برخوردار است( partition،از نامش پیدا است که برای تعمیم بیشببتر .( د‌نهبا ا ًل تقسیما ت دیسک مورد استفاده قرار میگیرد تا پارتیشنهای بیشتری داشته باشببیم.ببه عببار ت دیگبر آ مث پارتیشنهای منطقی هستند که در درون یک پارتیشن اصلی تعمیم داشته شده اند. اطلعا ت این پارتیشن هببا در

linked-یک لینک لیست حفظ میشود( listو اجازه میدهد که تعداد بیشماری (حببداقل از نظببر تئوری) از ( پارتیشنهای منطقی داشته باشیم.

ا ًل رکورد بو ت نامیده میشود( =اولین سکتور پارتیشن تعمیم داده شده معمو BR boot recoardکه بببرای ( که برای ذخیره (ابتدای لینک لیست)MBRذخیره پارتیشن های منطقی مورد استفاده قرار میگیرد دقیقا ًا شبیه

جدول پارتیشن استفاده میگردید.د‌ههای بعدی لینک لیست در اولین سکتور پارتیشن های منطقی بعدی که بببه بببو ت رکببورد منطقببی ( )LBRگر

مدخل کامل جدول پارتیشببن دارد امببا تنهببا دو مببدخل4اشاره میکند، ذخیره میشوند. هر گره از لینک لیست ابتدایی مورد استفاده قرار میگیرد که اولی برای مقدار لینک لیست یعنی اطلعببا ت پارتیشببن منطقببی مجبباور، و

دومی اشاره گر بعدی لینک لیست که به لیست پارتیشنهای منطقی باقیمانده اشاره میکند. برای مقایسه و فهم بیشتر جزئیا ت پارتیشن اصلی بروی دیسک سخت سیستمتان ، مراحل زیر را انجببام دهیببد

):root(البته با دقت و با دسترسی یوزر

./ _ / / ## / /part info dev sda Displays the partition table on dev sda

- / / ## fdisk l dev sda Todisplay and compare the partition table entries with the above

د‌هاید (البته بصور ت فقط خواندنی).چرا بببا الن شما با انتخاب و با دقت با دیسک سخت سیستم خود کلنجار رفت دقت؟ چون در غیر اینصور ت ممکن بود سیستم شما دیگر قابل بو ت نباشد. اما هیچ یادگیری بدون اکتشببافاتی

میسازیم و خراب میکنیم.ramکامل نمیشود. بنابراین در فصل بعدی ما یک دیسک مصنوعی در

صفحه99

د‌لپانزدهم:ساخت یک دیسک در حافظه RAMفص بسازیم که درونش شش فایل که شاملDiskOnRamدر این فصل درابتدا میخواهیم شاخه ای بنام

قراردارد. محتوای فایلها به قرار زیر میباشد:Makefile دو هدر فایل و یک Cسه فای ِل برنامه

.partitionh# _ifndef PARTITION H

# _define PARTITION H

# < / . >include linux types h

_ _ _ ( 8 * );extern void copy mbr n br u disk

#endif

.partition c

# < / . >include linux stringh

# " . "include partitionh

# _ ( ) ( ( ) / (* ))define ARRAY SIZE a sizeof a sizeof a

# _ 512define SECTOR SIZE

# _ _define MBR SIZE SECTOR SIZE

# _ _ _ 440define MBR DISK SIGNATURE OFFSET

# _ _ _ 4define MBR DISK SIGNATURE SIZE

# _ _ 446define PARTITIONTABLE OFFSET

# _ _ 16 // ( )define PARTITION ENTRY SIZE sizeof PartEntry

# _ _ 64 // ( )define PARTITIONTABLE SIZE sizeof PartTable

# _ _ 510define MBR SIGNATURE OFFSET

# _ _ 2define MBR SIGNATURE SIZE

صفحه100

# _ 0 55define MBR SIGNATURE xAA

# _ _define BR SIZE SECTOR SIZE

# _ _ 510define BR SIGNATURE OFFSET

# _ _ 2define BR SIGNATURE SIZE

# _ 0 55define BR SIGNATURE xAA

typedef struct

{ _ ; // 0 00 - ; 0 80 - ( )unsigned char boot type x Inactive x Active Bootable

_ ;unsigned char start head

_ :6;unsigned char start sec

_ _ :2;unsigned char start cyl hi

_ ;unsigned char start cyl

_ ;unsigned char part type

_ ;unsigned char end head

_ :6;unsigned char end sec

_ _ :2;unsigned char end cyl hi

_ ;unsigned char end cyl

_ _ ;unsigned long abs start sec

_ _ ;unsigned long sec in part

} ;PartEntry

[4];typedef PartEntry PartTable

_ _ =static PartTable def part table

{ { _ : 0 00,boot type x

_ : 0 00,start head x

صفحه101

_ : 0 2,start sec x

_ : 0 00,start cyl x

_ : 0 83,part type x

_ : 0 00,end head x

_ : 0 20,end sec x

_ : 0 09,end cyl x

_ _ : 0 00000001,abs start sec x

_ _ : 0 0000013sec in part x F

}, { _ : 0 00,boot type x

_ : 0 00,start head x

_ : 0 1,start sec x

_ : 0 0 , // (start cyl x A extended partition start cylinder BR )location

_ : 0 05,part type x

_ : 0 00,end head x

_ : 0 20,end sec x

_ : 0 13,end cyl x

_ _ : 0 00000140,abs start sec x

_ _ : 0 00000140sec in part x

}, { _ : 0 00,boot type x

_ : 0 00,start head x

_ : 0 1,start sec x

_ : 0 14,start cyl x

_ : 0 83,part type x

_ : 0 00,end head x

صفحه102

_ : 0 20,end sec x

_ : 0 1 ,end cyl x F

_ _ : 0 00000280,abs start sec x

_ _ : 0 00000180sec in part x

}, { }};

_ _ _ _ [] = {0 0 , 0 0 , 0 12};static unsigned int def log part br cyl x A x E x

_ _ _ [] =static const PartTable def log part table

{ { { _ : 0 00,boot type x

_ : 0 00,start head x

_ : 0 2,start sec x

_ : 0 0 ,start cyl x A

_ : 0 83,part type x

_ : 0 00,end head x

_ : 0 20,end sec x

_ : 0 0 ,end cyl x D

_ _ : 0 00000001,abs start sec x

_ _ : 0 0000007sec in part x F

}, { _ : 0 00,boot type x

_ : 0 00,start head x

_ : 0 1,start sec x

_ : 0 0 ,start cyl x E

صفحه103

_ : 0 05,part type x

_ : 0 00,end head x

_ : 0 20,end sec x

_ : 0 11,end cyl x

_ _ : 0 00000080,abs start sec x

_ _ : 0 00000080sec in part x

}, }, { { _ : 0 00,boot type x

_ : 0 00,start head x

_ : 0 2,start sec x

_ : 0 0 ,start cyl x E

_ : 0 83,part type x

_ : 0 00,end head x

_ : 0 20,end sec x

_ : 0 11,end cyl x

_ _ : 0 00000001,abs start sec x

_ _ : 0 0000007sec in part x F

}, { _ : 0 00,boot type x

_ : 0 00,start head x

_ : 0 1,start sec x

_ : 0 12,start cyl x

_ : 0 05,part type x

_ : 0 00,end head x

_ : 0 20,end sec x

صفحه104

_ : 0 13,end cyl x

_ _ : 0 00000100,abs start sec x

_ _ : 0 00000040sec in part x

}, }, { { _ : 0 00,boot type x

_ : 0 00,start head x

_ : 0 2,start sec x

_ : 0 12,start cyl x

_ : 0 83,part type x

_ : 0 00,end head x

_ : 0 20,end sec x

_ : 0 13,end cyl x

_ _ : 0 00000001,abs start sec x

_ _ : 0 0000003sec in part x F

}, }};

_ ( 8 * )static void copy mbr u disk

{ ( , 0 0, _ );memset disk x MBR SIZE

*( *)( + _ _ _ ) =unsigned long disk MBR DISK SIGNATURE OFFSET 0 36 5756 ;x E D

( + _ _ , & _ _ ,memcpy disk PARTITION TABLE OFFSET def part table _ _ );PARTITION TABLE SIZE

*( *)( + _ _ ) =unsigned short disk MBR SIGNATURE OFFSET

صفحه105

_ ;MBR SIGNATURE

} _ ( 8 * , _ , static void copy br u disk int start cylinder const PartTable

* _ )part table

{ += ( _ * 32 /* / */ * _ );disk start cylinder sectors cyl SECTOR SIZE

( , 0 0, _ );memset disk x BR SIZE

( + _ _ , _ ,memcpy disk PARTITION TABLE OFFSET part table

_ _ );PARTITION TABLE SIZE

*( *)( + _ _ ) =unsigned short disk BR SIGNATURE OFFSET _ ;BR SIGNATURE

} _ _ _ ( 8 * )void copy mbr n br u disk

{ ;int i

_ ( );copy mbr disk

( = 0; < _ ( _ _ _ ); ++)for i i ARRAY SIZE def log part table i

{ _ ( , _ _ _ _ [ ], & _ _ _ [ ]);copy br disk def log part br cyl i def log part table i

}}

_ .ram deviceh

# _ifndef RAMDEVICE H

# _define RAMDEVICE H

# _ _ 512define RB SECTOR SIZE

صفحه106

_ ( );extern int ramdevice init void

_ ( );extern void ramdevice cleanup void

_ ( _ _ , 8 * , );extern void ramdevice write sector t sector off u buffer unsigned int sectors

_ ( _ _ , 8 * , );extern void ramdevice read sector t sector off u buffer unsigned int sectors

#endif

_ .ram device c# < / . >include linux types h

# < / . >include linux vmalloc h

# < / . >include linux string h

# " _ . "include ram deviceh

# " . "include partition h

# _ _ 1024 /* */define RB DEVICE SIZE sectors

/* , = 1024 * 512 = 512 */So total device size bytes KiB

/* */Array where the disk stores its data

8 * _ ;static u dev data

_ ( )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

}

_ ( _ _ , 8 * , )void ramdevice write sector t sector off u buffer unsigned int sectors

{ ( _ + _ * _ _ , ,memcpy dev data sector off RB SECTOR SIZE buffer

* _ _ );sectors RB SECTOR SIZE

} _ ( _ _ , 8 * , )void ramdevice read sector t sector off u buffer unsigned int sectors

{ ( , _ + _ * _ _ ,memcpy buffer dev data sector off RB SECTOR SIZE

* _ _ );sectors RB SECTOR SIZE

}

_ .ram blockc/* */Disk on RAMDriver

# < / . >include linux module h

# < / . >include linux kernelh

# < / . >include linux fs h

# < / . >include linux types h

# < / . >include linux genhdh

# < / . >include linux blkdevh

# < / . >include linux errnoh

# " _ . "include ram deviceh

# _ _ 0define RB FIRST MINOR

# _ _ 16define RB MINOR CNT

_ _ = 0;static u int rb major

/* * 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 kernels representation of an individual disk device

* _ ;struct gendisk rb disk

} _ ;rb dev

_ ( _ * , _ )static int rb open struct block device bdev fmode t mode

{ = ( -> _ );unsigned unit iminor bdev bd inode

( _ " : \ ");printk KERN INFO rb Device is opened n

( _ " : % \ ", );printk KERN INFO rb Inode number is d n unit

( > _ _ )if unit RB MINOR CNT

- ;return ENODEV

0;return

}

_ ( * , _ )static int rb close struct gendisk disk fmode t mode

{ ( _ " : \ ");printk KERN INFO rb Device is closed n

0;return

}

/* * Actual Data transfer

*/ _ ( * )static int rb transfer struct request req

صفحه109

{ // _ * = ( _ *)( -> _ -> _ );struct rb device dev struct rb device req rq disk private data

= _ _ ( );int dir rq data dir req

_ _ = _ _ ( );sector t start sector blk rq pos req

_ = _ _ ( );unsigned int sector cnt blk rq sectors req

_ * ;struct bio vec bv

_ ;struct req iterator iter

_ _ ;sector t sector offset

;unsigned int sectors

8 * ;u buffer

= 0;int ret

// ( _ " : :% ; :% ; :% \ ", , _ ,printk KERN DEBUG rb Dir d Sec lld Cnt d n dir start sector _ );sector cnt

_ = 0;sector offset

_ _ _ ( , , )rq for each segment bv req iter

{ = _ ( -> _ ) + -> _ ;buffer page address bv bv page bv bv offset

( -> _ % _ _ != 0)if bv bv len RB SECTOR SIZE

{ ( _ " : : "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

/* */Gets the current request from the dispatch queue

(( = _ _ ( )) != )while req blk fetch request q NULL

{# 0if

/* * This function tells us whether we are looking at a filesystem request

صفحه111

* - one that moves block of data

*/ (! _ _ ( ))if blk fs request req

{ ( _ " : - \ ");printk KERN NOTICE rb Skip non fs request n

/* 0 Wepass to indicate that we successfully completed the request */ __ _ _ _ ( , 0);blk end request all req

//__ _ _ ( , 0, _ _ ( ));blk end request req 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

/* */Set up our RAMDevice

(( = _ ()) < 0)if ret ramdevice init

{ ;return ret

} _ . = ;rb dev size ret

/* */Get Registered

_ = _ ( _ , " ");rb major register blkdev rb major rb

( _ <= 0)if rb major

{ ( _ " : \ ");printk KERN ERR rb Unable to get Major Number n

_ ();ramdevice cleanup

- ;return EBUSY

}

/* ( ) */Get a request queue here queue is created

_ _ (& _ . );spin lock init rb dev lock

_ . _ = _ _ ( _ , & _ . );rb dev rb queue blk init queue rb request rb dev lock

( _ . _ == )if rb dev rb queue NULL

{ ( _ " : _ _ \ ");printk KERN ERR rb blk init queue failure n

_ ( _ , " ");unregister blkdev rb major rb

_ ();ramdevice cleanup

- ;return ENOMEM

} /* * 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

}

/* */Setting the major number

_ . _ -> = _ ;rb dev rb disk major rb major

/* */Setting the first mior number

_ . _ -> _ = _ _ ;rb dev rb disk first minor RB FIRST MINOR

/* */Initializing the device operations

_ . _ -> = & _ ;rb dev rb disk fops rb fops

/* - */Driver specific own internal data

_ . _ -> _ = & _ ;rb dev rb disk private data rb dev

_ . _ -> = _ . _ ;rb dev rb disk queue rb dev rb queue

/* * Youdo not want partition information to show up in

* / / cat proc partitions set this flags

*/ // _ . _ -> = _ _ _ _ ;rb dev rb disk flags GENHD FL SUPPRESS PARTITION INFO

( _ . _ -> _ , " ");sprintf rb dev rb disk disk name rb

/* */Setting the capacity of the device in its gendisk structure

_ ( _ . _ , _ . );set capacity rb dev rb disk rb dev size

/* */Adding the disk to the system

_ ( _ . _ );add disk rb dev rb disk

/* " " */Now the disk is live

( _ " : (% ; % )\ ",printk KERN INFO rb Ram Block driver initialised d sectors d bytes n

_ . , _ . * _ _ );rb dev size rb dev size RB SECTOR SIZE

صفحه114

0;return

}/* * 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 sarika pugs com

_ (" ");MODULE DESCRIPTION Ram Block Driver

_ _ _ ( _ );MODULE ALIAS BLOCKDEV MAJOR rb major

) (ببباdorko. بنویسید و اجرا کنید(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

# ; ' Otherwise KERNELRELEASE is defined we ve been invoked from the

# .kernel build system and can use its language

else

- := .obj m doro

- := _ . _ . .dor y ram blocko ram device o partition o

endif

make برای حذف فایل اجرایی ساخته شده نیز میتوانید دستور clean.را طبق معمول اجرا کنید تجربه اجرای درایور فوق در سیستم من را بصور ت شکلهای زیر مشاهده میفرمایید:

صفحه116

صفحه117

صفحه118

اجرا گردد:rootتوجه داشته باشید که تمام مراحل زیر میبایست با مجوز . این دستور میبایست یک فایببلinsmod در کرنل با استفاده از دستورdorko.لود کردن درایور •

اسببت کببهram کیلوبایت در حببافظه512دستگاه بلکی را ایجاد کند که نمایانگر دیسکی با ظرفیت حاوی سه پارتیشن اصلی و سه پارتیشن منطقی میباشد.

dev/(*/فایلهای دستگاه بلوکی • rb/د‌یشوند که ) بصور ت اتوماتیک dev/ ساخته م rb/کببل دیسببک پارتیشببن2rb سه پارتیشن اصلی اسببت کببه 3rb و 2rb و 1rb کیلوبایت حجم دارد.512است که

نیز میباشد.7rb و 6rb و 5rbتوسعه داده شده میباشد و خود دارای سه پارتیشن منطقی کل دیسک را میتوانید بخوانید.ddبا استفاده از دستور •/(اولین سکتور از اولین پارتیشن دیسک • 1dev rb/( را با صفر پرکرده و دوببباره دسببتور ddرا اجببرا

کنید/( متنی را در اولین پارتیشن catبا استفاده از دستور • 1dev rb/(بنویسید /( محتوی اولیه اولین پارتیشن xxdبا استفاده از دستور • 1dev rb/(را ببینید اطلعا ت پارتیشن را ببینیدfdiskبا استفاده از دستور •

صفحه119

mkfs.با استفاده از • vfat سبومین پارتیشبن اصبلی )/ 3dev rb/( را از نبوع فایبل سیسبتم vfat بندی سریع کنید(شبیه کاری که برای درایور قلم نوری انجام دادیم)فرمت

) کنید.mount/ مونت (mnt ، پارتیشن فرمت شده جدید را درmountبا استفاده از دستور • حجم پارتیشن مونت شده را ببینید. شما ممکن حتی از این هم فراتببر برویببد وdfبا استفاده از دستور •

است و دائمببیRAMفایلی را در این پارتیشن ذخیره کنید ولی یادتان باشیدکه این دیسک در حافظه نمیباشد.

umount/ بعد از آنکه با استفاده از دستور • mntپارتیشن را غیر فعال کردید با استفاده از دستور rmmod dorدرایور را از کرنل حذف کنید. با اینکار کل اطلعا ت ذخیره شده در این دیسک از

دست خواهد رفت.

حال بیایید قوانین کار را بیاموزیم: ساختیم و کمی هم بببا آن کببارRAMما بدون اینکه قوانین این بازی را بدانیم فقط دیسکی را در حافظه

نمایببانگر قسببمتcکردیم. حال بیایید در مورد این درایور کنکاشببی داشببته باشببیم. هببر یببک از سببه فایببل *._مخصوصببی از درایببور اسببت. .ram device c و _ .ram deviceh عملیببا ت اساسببی ramشبببیه

vmalloc/vfree و memcpy و... را تعریف میکند که APIهایی برای عملیاتی شبیه شروع/خبباتمه و خواندن/نوشتن و … را ممکن سازد.

.partition c و .partitionhد‌نها جداول پارتیشببن مختلببف را میسببازد و توابعی است که با استفاده از آ تعریف میکند. برای جزئیا ت مفاهیم پارتیشن میتوانید فصل قبل را دوباره و با دقت بخوانید. کدهای ایببن فایببل

د‌یشود را بببرfdiskمسئولیت اطلعا ت پارتیشن ها از قبیل تعداد ، نوع ، حجم و غیره که توسط نمایش داده معهده دارد.

_فایل .ram block cد‌هی درایور بلکی را انجام میدهد کببه درایببور کار د‌هسازی هست را در سببطحDOR پیادdev//(*فضای یوزر و توسط فایلهای دستگاه بلوکی rb/(نمایان میکند. به عبار ت دیگببر چهببار فایببل از پنببج

ram_فایل *. device.* و partition از لیه افقی و _ .ram block cاز لیه عمودی درایببور دسببتگاه میباشد.

مقدمات درایور بلکیدرایورهای بلکی بصور ت مفهومی بسیار شبیه درایورهای کاراکتری هستند مخصوصا ًا اینکه:

از فایلهای دستگاه بلکی استفاده میکنند•دارای شماره های اصلی و فرعی میباشند•

صفحه120

عملیا ت فایلی بروی فایلهای دستگاه میسر میباشد •د‌نها وجود دارد• مفهوم ثبت دستگاه نیز در آ

د‌تتر میتببوانیم درایببور هببای بلکببی را نیببز د‌هسازی دستگاه کاراکتری را میدانیم راح بدین ترتیب اگر پیادبفهمیم.

د‌نها دقیقا ًا یکسان نیستند و تفاوتهای کلیدی نیز باهم دارند که در ذیل آمده است: اما آدستگاه بل ک گرا در مقابل دستگاه بایت گرا•I/دستکاههای بلکی برای • Oد‌هاند. کببه د‌هریزی شده و برای کببارایی بهینببه طراحببی شببد های زیاد برنام

د‌نها را با دستگاههای کاراکتری که از استفاده میکنند مقایسه کنیدVFSمیتوانید آ دستگاههای بلکی برای یکپارچه شدن با مکانیزم بافرکش لینوکس که دسترسی به اطلعا ت را کبباراتر•

میکند طراحی گردیده است. درایورهببای کبباراکتری درایورهببایی مسببتقیم هسببتند یعنببی مسببتقیما ًا ببباد‌تافزار دسترسی دارند. سخ

د‌هسازی این دو نوع درایور نیز با هم متفاو ت است. حال بیایید با هم قطعاتی از کببدهای به دلیل فوق پیاد_اصلی .ram block c() را آنالیز کنیم. برای شروع از سازنده درایور _rb init.شروع میکنیم

8 شببماره فرعببی 256 بیت (بل ک) شماره اصلی است. (که بطور ضمنی به معنای ثبت همببه 8اولین گام ثبت بیتی به همراه آن است) تابع زیر این کار را انجام میدهد:

_ ( , * );int register blkdev unsigned int major const char name

نببامی اسببت کببه در زیببر شبباخهname شببمار اصببلی اسببت کببه ثبببت شببده اسببت و majorدر اینجا /proc devices/() د‌یشود. جالب توجه اینکه تابع register_ نمایش داده م blkdevسعی میکنببد تببا

د‌یشود، در حالتی کببهmajor برای اولین پارامتر 0یک شماره اصلی خالی را بگیرد و ثبت کند. وقتی ارسال م موفقیت آمیز باشد عدد در نظر گرفته شده بازگردانده میشود. تابع معکوس ثبت نیز متناظر با تببابع ثبببت در

زیر آمده است:

_ ( , * );void unregister blkdev unsigned int major const char name

/<که هر دو تابع در هدر فایل .linux fs h<.معرفی شده اند

صفحه121

_گام دوم تعریف عملیا ت فایل است که از طریق ساختار _block device operationsدر هدر فایل ) >/ .linux blkdevh<.د‌لهای دستگاه ثبت انجام میگیرد معرفی گردیده است) برای شماره اصلی فای

ا ًل کوچببک د‌ههای کاراکتری چندین برابر کمتر میباشد و معمببو اما این عملیا ت در مقایسه با عملیا ت فایل دستگاد‌های نیز هیچ عملیاتی (حتی برای خواندن و نوشتن) وجود نداردکه شببگفت انگیببز نیز هستند. حتی بصور ت حرف

I/است .دلیل آن این است که درایورهای بلکی با Oد‌هسببازی خوانببدن و د‌هاند و پیاد زمانبدی شده یکپارچه شد نوشتن از طریق درخواست فراخوانی از صف بدست می آید. همراه با انجام عملیا ت فایل دستگاه باید کارهببای

زیر انجام شود:در خواست صف برای مرتب کردن درخواستهای خواندن/نوشتن•) برای جلوگیری از دسترسی همزمان به ص ِف درخواست مربوطهlockایجاد یک قفل (•د‌تها در صف درخواست• ایجاد تابع درخواست برای پردازش درخواس

همچنین اینترفیس مجزایی برای ساخت فایلهای بلکی نیز وجود ندارد. بنابراین کارهای زیر انجام میشود:ا ًل به • disk_پیشوند نام فایل دستگاه که معمو name) باز میگردد rb در درایور dor(ا ًل به • first_رشته عدد فرعی برای فایلهای دستگاه که معمو minor.باز میگردد

در نهایت دو مشخصه دستگاه بلکی بصور ت زیر محیا میگردد: حداکثر تعداد پارتیشنهای پشتیانی شده توسط دستگاه بلکببی کببه بوسببیله جمببع شببماره هببای فرعببی•

مشخص شده است.د‌مبندی سایز دستگاه در سکتورهایی • د‌ کها.512تقسی بایتی برای دستیابی منطقی به بل

د‌هاند:gendiskتمام این چیزها در ساختار در تابع زیر تعریف شد

_ ( * );void add disk struct gendisk disk

مرتبط با آن در خط زیر آمده است:deleteتایع

_ ( * );void del gendisk struct gendisk disk

add_در () disk قبلی چندیدن فیلد ساختار gendiskهست که مستقیما ًا یببا بببا اسببتفاده از ماکروهببا یببا

صفحه122

د‌عای شبببیه _تببواب ()set capacity .major, _first minor,fops,ueue, _disk nameحببداقل بایدgendiskفیلدهای مورد نیاز آن باید مستقیما ًا مقدار دهی شوند. قبل از مقدار دهی این فیلدها، ساختار

حافظه را در اختیار بگیرید که توسط تابع زیر اینکار انجام میشود:

* _ ( );struct gendisk alloc disk int minors

تعداد کل پارتیشن ها برای این دیسک میباشد، برعکس تابع فوق نیز در زیر آمده است:minorsکه

_ ( * );void put disk struct gendisk disk

د‌نها در فایل /<که تمام ای .linux genhdh<.معرفی شده اند

صف درخواست و تابع درخواستadd_ و قبببل از ()gendiskصف درخواسببت نیببز نیبباز دارد تببا مقببدار دهببی شببود و در سبباختار disk

جاگذاری شود. صف درخواست بوسیله فراخوانی تابع زیر مقدار دهی میشود:

_ * _ _ ( _ _ *, _ *);struct request queue blk init queue request fn proc spinlock t

) و محافظت از دسترسی موازی را در پارامتر گنجاندیم. lockما تابع پردازش درخواست و راه اندازی قفل (تابع پا ک کردن صف مربوطه را در زیر مشاهده میکنید:

_ _ ( _ *);void blk cleanup queue struct request queue

تابع درخواست (پردازش) باید طبق زیر تعریف شود:

_ ( _ * );void request fn struct request queue q

واکشی کند که ما از کد زیر استفاده میکنم:qکد آن باید یک درخواست را از پارامتر

* _ _ ( _ * );struct request blk fetch request struct request queue q

صفحه123

سپس یا باید پردازش شود یا اینکه یک پردازش را شروع کند. و همه چیز به جز بل ک کببردن از یببک تببابع ود‌یشود. بعلوه توابعی که صببف را قفببل و یببا آزاد پس از قفل کردن صف، درخواست غیر فرآیندی فراخوانی م

د‌یکند باید در تابع درخواست استفاده گردد. نمrb_یک مثال معمولی فرآیند درخواست بوسیله تابع () request در فایل _ .ram block cمببورد نمببایش

قرار گرفته است:

(( = _ _ ( )) != ) /* */while req blk fetch request q NULL Fetching a request

{

/* : */Processing the request the actual data transfer

= _ ( ); /* */ret rb transfer req Our custom function

/* */Informing that the request has been processed with return of ret

__ _ _ _ ( , );blk end request all req ret

}

درخواستها و پردازش آنهاrb_تابع کلیدی ما () transfer هست که سبباختار requestرا بررسببی میکنببد و برایببن اسبباس

د‌ههای واقعی را منتقل میکند .ساختار اولیه شامل جه ِت انتقال داده داده ، سببکتو ِر شببروع بببرایrequestداد انتقال داده ، جمع تعداد سکتور برای انتقال ، بافر فرستنده/گیرنده انتقال داده است. چندین ماکرو برای واکشببی

در زیر لیست شده است:requestاز ساختار

_ _ ( ); /* : 0 - ; - rq data dir req Operation type read from device otherwise write to */device

_ _ ( ); /* */blk req pos req Starting sector to process

_ _ ( ); /* */blk req sectors req Total sectors to process

_ _ _ ( , , ) /* */rq for each segment bv req iter Iterator to extract individual buffers

()_ _ _rq for each segment مخصوص این است که با استفاده از iter) در ساختار (request req

صفحه124

_چرخش کند و اطلعا ت بافر منحصببر بفببرد را در سبباختار ( : /bio vec bv basic input output vectorد‌ههای مناسب، بسببته بببه نببوع عملیببا ت ) در هر چرخش استخراج کند و سپس در هر استخراج داد

_ های زیر در فایل APIمنتقل شوند که با استفاده از فراخوانی .ram device c:اینکار انجام میشود

_ ( _ _ , 8 * , );void ramdevice write sector t sector off u buffer unsigned int sectors

_ ( _ _ , 8 * , );void ramdevice read sector t sector off u buffer unsigned int sectors

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/اطلعاتی در مورد حافظه اصلی بروی سیستم

نمایی سنتی از کرنل شناختن کرنل کمک زیادی به ما در جهت فهمیدن و رفع عیب درایورهای دستگاه لینوکس میکند.اما آیا

/ داشته باشیم؟ جواب مثبت است و آنهببم فقببط بببه سببادگی اسببتفاده ازprocما میتوانیم یک شناختی نیز از د‌های نمونه را جهت کار با APIچندین / مشاهده میکنید:proc است. در زیر برنام

# < / . >include linux module h

# < / . >include linux kernelh

# < / _ . >include linux proc fs h

# < / . >include linux jiffies h

_ _ * , * , * ;static struct proc dir entry parent file link

= 0;static int state

صفحه126

_ ( * , ** , _ , , * , * ) {int time read char page char start off t off int count int eof void data

, ;int len val

_ ;unsigned long act jiffies

= ( , " = % \ ", );len sprintf page state d n state

_ = - _ ;act jiffies jiffies INITIAL JIFFIES

= _ _ ( _ );val jiffies to msecs act jiffies

( ) {switch state

0:case

+= ( + , " = % \ ", _ );len sprintf page len time ld jiffies n act jiffies

;break

1:case

+= ( + , " = % \ ", );len sprintf page len time d msecs n val

;break

2:case

+= ( + , " = % % \ ",len sprintf page len time ds dms n

/ 1000, % 1000);val val

;break

3:case

/= 1000;val

+= ( + , " = %02 :%02 :%02 \ ",len sprintf page len time d d d n

/ 3600, ( / 60) % 60, % 60);val val val

;break

:default

+= ( + , "< >\ ");len sprintf page len not implemented n

;break

} += ( + , "{ = % ; = % ;}\ ", , );len sprintf page len offset ld count d n off count

;return len

} _ ( * , __ * , , int time write struct file file const char user buffer unsigned long count void

* ) {data

( > 2)if count

صفحه127

;return count

(( == 2) && ( [1] != '\ '))if count buffer n

;return count

(( [0] < '0') || ('9' < [0]))if buffer buffer

;return count

= [0] - '0';state buffer

;return count

}

__ _ _ ( ) {static int init proc win init void

(( = _ (" ", )) == ) {if parent proc mkdir anil NULL NULL

-1;return

} (( = _ _ (" _ ", 0666, )) == ) {if file create proc entry rel time parent NULL

_ _ (" ", );remove proc entry anil NULL

-1;return

}-> _ = _ ;file read proc time read

-> _ = _ ;file write proc time write

(( = _ (" _ _ ", , " _ ")) == ) {if link proc symlink rel time l parent rel time NULL

_ _ (" _ ", );remove proc entry rel time parent

_ _ (" ", );remove proc entry anil NULL

-1;return

}-> = 0;link uid

-> = 100;link gid

0;return

}

__ _ _ ( ) {static void exit proc win exit void

_ _ (" _ _ ", );remove proc entry rel time l parent

_ _ (" _ ", );remove proc entry rel time parent

_ _ (" ", );remove proc entry anil NULL

}

صفحه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

_ معمولی درایور Makefileبا استفاده از • .proc windowko.را بسازید درایور را لود کنید. insmodبا استفاده از •با استفاده از شکل زیر چندین آزمایش را انجام دهید.••()_ _proc win init.و در نهایت درایور را از حافظه خارج کنید

بررسی جزئیا ت برنامه :

_از سازنده () _proc win init شروع میکنیم که در آن سبه مبدخل procرا بصبور ت زیبر ایجباد کردیم:

ا ًل با پدر proc در زیر شاخه anilشاخه • و بببا اسببتفاده از ()0755) با مجوز دسترسببی null/ (مث_proc mkdir.آنرا ساختیم

rel_یبببک فایبببل معمبببولی • time و ببببا اسبببتفاده از()0666 در شببباخه فبببوق ببببا مجبببوز

صفحه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در درایورهای کاراکتری است. در پیاده سازی تببابع فببوق ، کبباربر را در فایل بنویسد و مطابق آن وضعیت داخلی را تنظیم میکند.9 تا 0میتواند از عدد

()_read procد‌هسازی فوق ، وضعیت جاری و مد ت زمانی که از بببو ت شببدن سیسببتم میگببذارد را در پیاد ، میلی ثانیه برای وضعیت0بسته به وضعیت جاری در واحدهای مختلف نمایش میدهد .لحظه برای وضعیت

و بببرای سببایر مقببادیر چیببزی3 ، ساعت ، دقیقه و ثانیه برای وضببعیت 2 ، ثانیه و میلی ثانیه برای وضعیت 1د‌هسازی نشده است. پیاد

نشان topدقت محاسبا ت را حتما ًا چک کنید. شکل زیر مقدار زمان روشن بودن سیستم را در خروجی دستور میدهد.

صفحه130

/</ در هدر فایل procتمام ساختارها و معرفی توابع مرتبط با _ .linux proc fs h<.تعریف شده است /معرفی توابع و ماکروهای مرتبط با زمان سیستم هم در هدر فایل < .linux jiffies h.تعریببف شببده اسببت <

INITIAL_نکتبببه : زمانهبببای واقعبببی بوسبببیله تفریبببق JIFFIESمحاسببببه میشبببود. در موقبببع ببببو ت _INITIAL JIFFIES صفرمیشود و از آن به بعد زمان در _INITIAL JIFFIESقببرار میگببرد و در نهببایت

فقط یک نام است که شما میتوانید هر نامی که دوست دارید را انتخاب کده و بجایanilساخت شاخه با نام آن استفاده کنید.

صفحه131

د‌لهفدهم:چگونگی تعامل بین ماژولها فص در این فصل میخواهیم چگونگی تعامل بین ماژولها (هم درایورهای قابل لود و هم درایورهببای غیببر قابببل

لود) از قبیل دسترسی به متغیرها، فراخوانی توابع و پاس کردن پارامترها را بررسی خواهیم کرد.

توابع و متغییرهای سراسری یکی از مبحثی که شاید شما از آن متعجب شوید امکان دسترسی به متغییرها و توابع مبباژول از خببارج از

در هدرفایل تعریف کنیم و بعببدexternآنها میباشد. سوال اینجاست که اگر فقط متغییرها را سراسری و با د‌ههببای معمببولی دقیقببا ًا بببهincludeاین هدر فایلها را د‌هسببازی برنام د‌نها دسترسی داریم؟ برای پیاد کنیم به آ

همین سادگی است. اما در توسعه کرنل اینکه همه چیز را استاتیک تعریف کنیم خلف توصیه هاست. بصببور تد‌شفرض نیز مواردی هست که ممکن است به متغیرهای سراسری و غیر استاتیک نیز مورد نیاز باشد. پی

بعنوان مثال یک درایور در چندین فایل نیاز دارد تا یک تابع از یک فایل دیگبر را فراخبوانی کنبد. حبال ببرای جلوگیری از تداخل هر فضای نام کرنل در هر حالت باید هر ماژول فضای نام مخصوص به خود را داشته باشد.د‌شفرض هیچ تداخلی پیببش د‌مزمان لود شوند. بنابراین بصور ت پی میدانیم که دو ماژول کرن ِل هم نام نمیتوانند هد‌شفرض هیچ چیز بصور ت سراسری در کرنببل سباخته نمیشبود نمی آید. البته به این علت است که بصور ت پی

/<حتی اگر ما بخواهیم. بنابراین برای چنین حالتی ماکروهای زیر در هدر فایل .linux moduleh< تعریف شده است:

_ ( )EXPORT SYMBOL sym

_ _ ( )EXPORT SYMBOL GPL sym

_ _ _ ( )EXPORT SYMBOL GPL FUTURE sym

د‌شفببرضsymbolکدام ازماکروهای فوق ، در هر د‌نها در یکببی از پی د‌یشود وآ بعنوان پارامتر پاس مد‌شهب gpl__ یبا gpl_ای ها ببه ترتیبب در بخ futureد‌نهبا ببرای قبرار میگیرنبد. از اینبرو فقبط یکبی از آ

symbol خاص مورد نیاز است و symbolمیتواند هم نام متغییر و هم نام تابع باشد. برای مثال کد زیببر _یعنی فایل _ .our glob symsc:را بررسی میکنیم

# < / . >include linux module h

# < / . >include linux deviceh

صفحه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

static int __ _ _ (init glob sym init void){

if ( _ ( _ = _ ( _ , " ")))IS ERR cool cl class create THIS MODULE cool

/* / / / / */Creates sys class cool

{return _ ( _ );PTR ERR cool cl

}return 0;}

static void __ _ _ (exit glob sym exit void){

/* / / / / */Removes sys class cool

_ ( _ );class destroy 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هر symbol کرنببل )__ksymtab( جببدول رشببته کرنببل ، )__kstrtab( و جببدول CRC کرنببل )__

صفحه133

kcrctab(.د‌نها مقدور است قرار دارند که بصور ت سراسری دسترسی به آ

د‌های از proc/شکل زیر یک قطع kallsyms/ را قبل و بعد از لود شدن مبباژول _ _ .our glob symsko کامپایل شده است:Makefileنشان میدهد که طبق معمول با یک

_کد زیر هد فایل _ .our glob symsh را که باید بوسیله ماژولهببایی کببهsymbol هببای _cool clو _ _get cool cl از را که export استفاده میکند را include:نماید

# _ _ _ifndef OUR GLOB SYMS H

# _ _ _define OUR GLOB SYMS H

# __ __ifdef KERNEL

# < / . >include linux deviceh

صفحه134

extern struct class * _ ;cool cl

extern struct class * _ _ (get cool cl void);#endif#endif

Module.شببکل فببوق همچنیببن فایببل symversرا نشببان میدهببد کببه بوسببیله کامپایببل مبباژول _ _our glob syms ساخته شده است. این فایل حاوی جزئیببا ت بیشببتر تمببام symbol هببای export

د‌ه میباشد. به جز export های sysmbol شدن هدر فایل فوق ، ماژولهایی که از includeشده در شاخModule. خودش فایل buildشده استفاده میکنند باید در شاخه symvers.را داشته باشد

/<نکته : هدر فایل .linux deviceh< در مثال فوق با includeکردن چندین توصیف و تعریف مرتبط د‌نها را مورد بحث قرار دادیم. ا ًل در درایورهای کارکتری آ با کلس شروع شده است که قب

پارامترهای ماژولد‌هی معمولی میتوان برنامه را با پارامترهایی فراخوانی کرد و به آرگومانهببایی کببه میدانیم که در یک برنام

برنامه با آن فراخوانده شده در داخل برنامه دسترسی داشت ولی آیا یک ماژول نیز اینگونه است؟ استفاده میکنیم به آنinsmodجواب مثبت است. پارامترها میتوانند در زمان لود شدن درایورها وقتی که از

د‌لهای کرنببل حببتی د‌هی معمولی ، در ماژو پاس شوند و شگفت انگیزاست که بگوییم برخلف آرگومان یک برنام این پارامترها را تغییر داد.sysfsمیتوان بوسیله تعامل با

/<پارامترهببای مبباژول بوسببیله مبباکروی زیببر(کببه در .linux moduleparamh<کببه از طریببق >/ .linux moduleh<:د‌یشود تعریف گردیده است) پیکربندی م

_ ( , , )module param name type perm

فایل مربببوط بببه ایببن پببارامتر بببازsysfs به مجوز perm نوع پارامتر و type نام پارامتر ، nameکه د‌یشببوند ,میگردد. نوع هایی که پشببتیبانی م , , , , , ,byte short ushort int uint long ulong

( ), charp character pointer bool یا ( invbool inverted Boolean.هستند (_ کد ماژول زیر .module paramc:یک نمونه از پارامتر ماژول را نمایش میدهد

# < / . >include linux module h

# < / . >include linux kernelh

صفحه135

static int _ = 3;cfg value

_ ( _ , module param cfg value int, 0764); static int __ _ _ (init mod par init void){ ( _ printk KERN INFO " % \ "Loaded with d n , _ );cfg value

return 0;} static void __ _ _ (exit mod par exit void){ ( _ printk KERN INFO " : % \ "Unloaded cfg value d n , _ );cfg value

}

_ ( _ _ );module init mod par init

_ ( _ _ );module exit mod par exit

_ (MODULE LICENSE " "GPL );_ (MODULE AUTHOR " < @ - . >"Anil Kumar Pugalia email sarika pugs com );_ (MODULE DESCRIPTION " "Module Parameter demonstration Driver );

صفحه136

شکل فوق تجربه من از کار با برنامه پارامتر ماژول را نمایش میدهد.

صفحه137

نمایش میدهد.rootشکل فوق تجربه من در کار با پارامتر ماژول را با یوزر ا ًل باید تعریببف شببود. نکته قابل توجه اینکه قبل از مقداردهی پارامتر ، متغییری با نام مشابه و با نوع سازگار قب

سپس برای مشاهده خروجی های نمایش داده شده کارهای زیر را باید انجام دهید:

_ساخت درایور (فایل • .module paramko با استفاده از ( Makefile (با و بدون پارامتر)insmodلود کردن درایور با استفاده از •/ مرتبط با ماژولsysکار با مدخل •و در نهایت خارج کردن ماژول از حافظه•

نکته هاد‌شفرض وقتی که پبارامتری را ببه مباژول پباس نکنیبم مقبدار )3مقدار دهی ( ببه متغییبر۳ بصور ت پی

_cfg value. د‌یشود پاس م را بببه فایببل--r- و بببه بقیببه مجببوز rw ، به گببروه آن مجببوز rwx به کاربر اجرا کنند مجوز 0764مجوز

صفحه138

_cfg value تحت پارامترهای _module param/ در زیر شاخه /sys module. میباشد میدهد /بررسی کنید

dmesg| خروجی دستور tail در هببر insmod و rmmod بببرای خروجببی هببای printkی موجود در برنامه را بررسی کنید.

/سعی کنید در فایببل / _ / / _sys module module param parameters cfg valueبببا یببک / ) و نتیجه را بررسی کنید.rootیوزر معمولی بنویسید (به غیر از یوزر

موفق باشید.

صفحه139