انجمنهای فارسی اوبونتو
کمک و پشتیبانی => سختافزار => نویسنده: A.Hossein در 02 بهمن 1401، 04:53 بظ
-
سلام.
دوستان تا اونجایی که من فهمیدم برای ارتباط با سخت افزار براساس نوع cpu تنها دو راه هست.
۱- آدرس پورت میدیم یا ioport
۲- بخشی از رم رو معرف اون سخت افزار میدونیم io memory
ولی من توی کلیپ های آموزشی دیدم که روی یه سخت افزار از جفتش استفاده میکنه.چطوری میشه؟
یا مثلا توی همین لینوکس خودمون که پردازنده من intel هست قاعدتا باید از روش پورتها استفاده کنه یعنی چیزی که
cat /porc/iports
داره میگه .حالا این
cat /proc/iomem
یعنی چی؟
این که داره میگه بخشی از رم به دیوایس ها تخصیص داده شده.
-
سلام
چیزهای مربوط به io و نحوه دسترسی به اون، خیلی به معماری پردازنده بستگی داره. معمولا هر معماری هم یه راه متفاوتی داره.
اما بیشتر وقتها روش کلی اینجوریه که چیزی که ممکنه به عنوان io استفاده بشه (مثل پینهای gpio) به یه آدرسی از حافظه اصلی سیستم (ram) نگاشته (mapped) شده.
خوندن و نوشتن اطلاعات از یا توی اون آدرس، موجب میشه اطلاعات از اون آدرس خونده یا داخلش نوشته بشه.
مثلا پین شماره ۴ gpio به آدرس 0x00007e4a و پیشن ۱۷ به آدرس 0x00006ff2 نگاشت شده.
سیستمعامل یه چیزی توی آدرس 0x00007e4a حافظه مینویسه. در واقعیت، اون اطلاعات توی ram ذخیره نمیشه، بلکه به عنوان خروجی توی پین شماره ۴ gpio قرار میگیره.
برای خوندن هم همینجوره، مثلا آدرس 0x00006ff2 حافظه خونده میشه، ولی در واقیعیت اطلاعات پین شماره ۱۷ gpio خونده میشه.
اینکه چه بازهای از آدرسهای حافظه برای نگاشت استفاده میشن، به معماری پردازنده مربوطه.
اینکه دقیقا چه سختافزاری به چه آدرسی نگاشت میشه، به سختافزارهای متصل و مدل اونها مربوطه.
این آدرسهایی که بالا گفتم برای مثال بود و نمیدونم اصلا جایی این آدرسها برای نگاشت استفاده میشن یا نه.
از اونجایی که هر معماری یه شکلی این نگاشتها رو انجام میده، نوشتن درایور و کرنلی که بتونه با معماریهای مختلف سازگار باشه، سخته.
به همین دلیل توی کرنل لینوکس، یه سازوکاری ایجاد شده که دسترسی به io توی معماریهای مختلف تا مقدار زیادی یکسان و استاندارد باشه.
نحوه کار اون اینجوری هست که آدرس واقعی، به یه آدرس مجازی نگاشت میشه. بعد شما از اون آدرس مجازی به همراه توابع مرتبط استفاده میکنید تا خوندن و نوشتن رو انجام بدید.
اینکه اطلاعات دقیقا از کدوم آدرس خونده بشن و توی کدوم آدرس نوشته بشن، توسط خود کرنل انجام میشه. شما فقط کافیه از اون آدرس مجازی استفاده کنید. لازم نیست بدونید دقیقا چه چه آدرسی به این آدرس مجازی نگاشت شده.
شما اول با فراخوانی تابع ()ioremap، یه آدرس مجازی برای سختافزار مورد نظر میگیرید.
بعد هر چیزی که خواستید بنویسید، تابع ()iowrite8 یا ()iowrite16 یا ()iowrite32 رو فراخوانی میکنید. اولی ۸ بیت
مینویسه، دومی ۱۶ و آخری ۳۲ بیت.
این تابعها دوتا آرگومان میگیرن، اولی چیزی هست که میخواهید بنویسید و دومی آدرس مجازی.
این سه تا تابع چیزی بر نمیگردونند.
اگه کرنل برای یه معماری ۶۴ بیتی کامپایل شده باشه، تابع ()iowrite64 هم هست که ۶۴ بیت مینویسه.
برای خوندن هم ()ioread8 و ()ioread16 و ()ioread32 و ()ioread64 هست. که به ترتیب ۸ و ۱۶ و ۳۲ و ۶۴ بیت اطلاعات میخونند و برمیگردونند.
()ioread64 فقط توی کرنلهای ۶۴ بیتی موجوده.
این تابعها فقط یه آرگومان ورودی دارند که اون هم آدرس مجازی هست.
ممکنه آدرس مجازی هم توی توابع نوشتن و هم توابع خوندن، با یه offset همراه باشه تا پورت، رجیستر و ... مورد نظر رو هم مشخص کنه.
با تابع ()iounmap هم میشه اون نگاشت آدرس مجازی به آدرس مربوط به io رو پاک کرد.
این تابع فقط یه آدرس مجازی میگیره و چیزی هم بر نمیگردونه.
توی معماری x86، علاوه بر نگاشت io به آدرسهای ram، یه سری دستورالعمل هم وجود داره که بتونید بدون نیاز به استفاده از اون نگاشتها، مستقیم با io کار کنید. توابع ()inb و ()inw و ()inl به ترتیب ۸، ۱۶ و ۳۲ بیت اطلاعات میخونند و بر میگردونند.
توابع ()outb و ()outw و ()outl هم به ترتیب ۸، ۱۶ و ۳۲ بیت اطلاعات مینویسند.
این ۶ تا تابع از دشتورالعملهای مختص معماری x86 استفاده میکنند.
اگه دارید ماژولی مینویسید که فقط توی معماری x86 استفاده میشه، میتونید از هر دو راه برای کار با io استفاده کنید.
اینکه از کدوم راه استفاده کنید، بستگی به این داره چیزی که دارید مینویسید، قراره برای معماریهای دیگه هم استفاده بشه یا نه.
اگه قراره برای معماری دیگهای به غیر از x86 هم استفاده بشه، روش نگاشت حافظه گزینه بهتریه.
اگه قراره فقط برای معماری x86 استفاده بشه، نمیدونم کدوم راه بهتره.