বিক্রেতা মডিউল নির্দেশিকা

আপনার ভেন্ডর মডিউলগুলোর দৃঢ়তা এবং নির্ভরযোগ্যতা বাড়াতে নিম্নলিখিত নির্দেশিকাগুলো ব্যবহার করুন। অনেক নির্দেশিকা অনুসরণ করা হলে, সঠিক মডিউল লোড অর্ডার এবং ড্রাইভারদের কোন ক্রমে ডিভাইসগুলোর জন্য প্রোব করতে হবে, তা নির্ধারণ করা সহজ হয়ে যায়।

একটি মডিউল একটি লাইব্রেরি বা একটি ড্রাইভার হতে পারে।

  • লাইব্রেরি মডিউল হলো এমন লাইব্রেরি যা অন্যান্য মডিউলকে ব্যবহারের জন্য এপিআই (API) প্রদান করে। এই ধরনের মডিউলগুলো সাধারণত কোনো হার্ডওয়্যার-নির্দিষ্ট হয় না। লাইব্রেরি মডিউলের উদাহরণ হলো একটি AES এনক্রিপশন মডিউল, মডিউল হিসেবে কম্পাইল করা remoteproc ফ্রেমওয়ার্ক এবং একটি লগবাফার (logbuffer) মডিউল। module_init() ভেতরের মডিউল কোড ডেটা স্ট্রাকচার সেট আপ করার জন্য চলে, কিন্তু কোনো বাহ্যিক মডিউল দ্বারা ট্রিগার না হলে অন্য কোনো কোড চলে না।

  • ড্রাইভার মডিউল হলো এমন ড্রাইভার যা কোনো নির্দিষ্ট ধরনের ডিভাইসকে খুঁজে বের করে বা তার সাথে সংযুক্ত হয়। এই ধরনের মডিউলগুলো হার্ডওয়্যার-নির্দিষ্ট হয়ে থাকে। ড্রাইভার মডিউলের উদাহরণ হলো UART, PCIe, এবং ভিডিও এনকোডার হার্ডওয়্যার। ড্রাইভার মডিউলগুলো কেবল তখনই সক্রিয় হয় যখন তাদের সংশ্লিষ্ট ডিভাইসটি সিস্টেমে উপস্থিত থাকে।

    • ডিভাইসটি উপস্থিত না থাকলে, শুধুমাত্র module_init() কোডটিই চলে, যা ড্রাইভার কোর ফ্রেমওয়ার্কের সাথে ড্রাইভারটিকে রেজিস্টার করে।

    • যদি ডিভাইসটি উপস্থিত থাকে এবং ড্রাইভারটি সফলভাবে সেই ডিভাইসটিকে খুঁজে বের করে বা তার সাথে সংযুক্ত হয়, তাহলে অন্যান্য মডিউলের কোড চলতে পারে।

মডিউল init সঠিকভাবে ব্যবহার করুন এবং প্রস্থান করুন।

ড্রাইভার মডিউলগুলোকে অবশ্যই module_init() ফাংশনে একটি ড্রাইভার রেজিস্টার করতে হবে এবং module_exit() ফাংশনে একটি ড্রাইভার আনরেজিস্টার করতে হবে। এই সীমাবদ্ধতাগুলো কার্যকর করার একটি উপায় হলো র‍্যাপার ম্যাক্রো ব্যবহার করা, যা module_init() , *_initcall() , বা module_exit() ম্যাক্রোগুলোর সরাসরি ব্যবহার এড়িয়ে চলে।

  • যেসব মডিউল আনলোড করা যায়, সেগুলোর জন্য module_ subsystem _driver() ব্যবহার করুন। উদাহরণ: module_platform_driver() , module_i2c_driver() , এবং module_pci_driver()

  • যেসব মডিউল আনলোড করা যায় না, সেগুলোর জন্য builtin_ subsystem _driver() ব্যবহার করুন। উদাহরণ: builtin_platform_driver() , builtin_i2c_driver() , এবং builtin_pci_driver()

কিছু ড্রাইভার মডিউল একাধিক ড্রাইভার রেজিস্টার করার জন্য module_init() এবং module_exit() ব্যবহার করে। যে ড্রাইভার মডিউলটি একাধিক ড্রাইভার রেজিস্টার করার জন্য module_init() এবং module_exit() ব্যবহার করে, সেটির ক্ষেত্রে ড্রাইভারগুলোকে একটিমাত্র ড্রাইভারে একত্রিত করার চেষ্টা করুন। উদাহরণস্বরূপ, আলাদা ড্রাইভার রেজিস্টার করার পরিবর্তে আপনি ডিভাইসের compatible স্ট্রিং বা অক্স ডেটা ব্যবহার করে পার্থক্য করতে পারেন। বিকল্পভাবে, আপনি ড্রাইভার মডিউলটিকে দুটি মডিউলে বিভক্ত করতে পারেন।

ইনিট এবং এক্সিট ফাংশন ব্যতিক্রম

লাইব্রেরি মডিউলগুলো ড্রাইভার রেজিস্টার করে না এবং module_init()module_exit() ফাংশনের উপর আরোপিত বিধিনিষেধ থেকে অব্যাহতিপ্রাপ্ত, কারণ ডেটা স্ট্রাকচার, ওয়ার্ক কিউ বা কার্নেল থ্রেড সেট আপ করার জন্য তাদের এই ফাংশনগুলোর প্রয়োজন হতে পারে।

MODULE_DEVICE_TABLE ম্যাক্রো ব্যবহার করুন

ড্রাইভার মডিউলগুলিতে অবশ্যই MODULE_DEVICE_TABLE ম্যাক্রো অন্তর্ভুক্ত থাকতে হবে, যা ইউজার স্পেসকে মডিউল লোড করার আগে ড্রাইভার মডিউল দ্বারা সমর্থিত ডিভাইসগুলি নির্ধারণ করতে দেয়। অ্যান্ড্রয়েড এই ডেটা ব্যবহার করে মডিউল লোডিং অপ্টিমাইজ করতে পারে, যেমন সিস্টেমে উপস্থিত নেই এমন ডিভাইসের জন্য মডিউল লোড করা এড়িয়ে চলা। ম্যাক্রো ব্যবহারের উদাহরণের জন্য, আপস্ট্রিম কোড দেখুন।

ফরওয়ার্ড-ডিক্লেয়ার্ড ডেটা টাইপের কারণে CRC অমিল এড়িয়ে চলুন

ফরওয়ার্ড-ডিক্লেয়ার্ড ডেটা টাইপগুলো দেখার জন্য হেডার ফাইল অন্তর্ভুক্ত করবেন না। একটি হেডার ফাইলে ( header-Ah ) সংজ্ঞায়িত কিছু স্ট্রাক্ট, ইউনিয়ন এবং অন্যান্য ডেটা টাইপ অন্য একটি হেডার ফাইলে ( header-Bh ) ফরওয়ার্ড ডিক্লেয়ার করা যেতে পারে, যে ফাইলটি সাধারণত ঐ ডেটা টাইপগুলোর পয়েন্টার ব্যবহার করে। এই কোড প্যাটার্নটির অর্থ হলো, কার্নেল ইচ্ছাকৃতভাবে ডেটা স্ট্রাকচারটিকে header-Bh ফাইলের ব্যবহারকারীদের কাছে গোপন রাখতে চাইছে।

header-Bh ব্যবহারকারীদের এই ফরওয়ার্ড-ডিক্লেয়ার্ড ডেটা স্ট্রাকচারগুলোর অভ্যন্তরীণ বিষয় সরাসরি অ্যাক্সেস করার জন্য header-Ah অন্তর্ভুক্ত করা উচিত নয়। এমনটা করলে CONFIG_MODVERSIONS CRC অমিলের সমস্যা দেখা দেয় (যা ABI কমপ্লায়েন্স সংক্রান্ত সমস্যা তৈরি করে), যখন অন্য কোনো কার্নেল (যেমন GKI কার্নেল) মডিউলটি লোড করার চেষ্টা করে।

উদাহরণস্বরূপ, struct fwnode_handle include/linux/fwnode.h এ সংজ্ঞায়িত করা হয়েছে, কিন্তু include/linux/device.h এ এটিকে struct fwnode_handle; হিসেবে ফরওয়ার্ড ডিক্লেয়ার করা হয়েছে, কারণ কার্নেল include/linux/device.h এর ব্যবহারকারীদের কাছ থেকে struct fwnode_handle এর বিস্তারিত তথ্য গোপন রাখতে চায়। এই পরিস্থিতিতে, struct fwnode_handle এর মেম্বারদের অ্যাক্সেস করার জন্য কোনো মডিউলে #include <linux/fwnode.h> যোগ করবেন না। যেকোনো ডিজাইন যেখানে আপনাকে এই ধরনের হেডার ফাইল অন্তর্ভুক্ত করতে হয়, তা একটি খারাপ ডিজাইন প্যাটার্ন নির্দেশ করে।

কোর কার্নেল স্ট্রাকচার সরাসরি অ্যাক্সেস করবেন না।

কোর কার্নেল ডেটা স্ট্রাকচার সরাসরি অ্যাক্সেস বা পরিবর্তন করলে মেমরি লিক, ক্র্যাশ এবং ভবিষ্যতের কার্নেল রিলিজের সাথে সামঞ্জস্যহীনতার মতো অনাকাঙ্ক্ষিত ঘটনা ঘটতে পারে। একটি ডেটা স্ট্রাকচারকে কোর কার্নেল ডেটা স্ট্রাকচার বলা হয় যখন এটি নিম্নলিখিত শর্তগুলির মধ্যে যেকোনো একটি পূরণ করে:

  • ডেটা স্ট্রাকচারটি KERNEL-DIR /include/ অধীনে সংজ্ঞায়িত করা হয়। উদাহরণস্বরূপ, struct device এবং struct dev_links_infoinclude/linux/soc এ সংজ্ঞায়িত ডেটা স্ট্রাকচারগুলো এর আওতামুক্ত।

  • ডেটা স্ট্রাকচারটি মডিউল দ্বারা বরাদ্দ বা ইনিশিয়ালাইজ করা হয়, কিন্তু এটিকে কার্নেলের কাছে দৃশ্যমান করা হয় কার্নেল দ্বারা এক্সপোর্ট করা কোনো ফাংশনে ইনপুট হিসেবে পরোক্ষভাবে (একটি struct-এর পয়েন্টারের মাধ্যমে) অথবা সরাসরি পাস করার মাধ্যমে। উদাহরণস্বরূপ, একটি cpufreq ড্রাইভার মডিউল struct cpufreq_driver ইনিশিয়ালাইজ করে এবং তারপর এটিকে cpufreq_register_driver() ফাংশনে ইনপুট হিসেবে পাস করে। এই পর্যায় থেকে, cpufreq ড্রাইভার মডিউলটির আর সরাসরি struct cpufreq_driver পরিবর্তন করা উচিত নয়, কারণ cpufreq_register_driver() ফাংশনটি কল করার ফলেই struct cpufreq_driver কার্নেলের কাছে দৃশ্যমান হয়ে যায়।

  • ডেটা স্ট্রাকচারটি আপনার মডিউল দ্বারা ইনিশিয়ালাইজ করা হয় না। উদাহরণস্বরূপ, regulator_register() দ্বারা রিটার্ন করা struct regulator_dev

কোর কার্নেল ডেটা স্ট্রাকচার শুধুমাত্র কার্নেল দ্বারা এক্সপোর্ট করা ফাংশনের মাধ্যমে অথবা ভেন্ডর হুক-এ ইনপুট হিসেবে স্পষ্টভাবে পাস করা প্যারামিটারের মাধ্যমে অ্যাক্সেস করুন। যদি কোনো কোর কার্নেল ডেটা স্ট্রাকচারের অংশবিশেষ পরিবর্তন করার জন্য আপনার কাছে কোনো এপিআই বা ভেন্ডর হুক না থাকে, তবে সম্ভবত এটি ইচ্ছাকৃত এবং আপনার মডিউল থেকে সেই ডেটা স্ট্রাকচারটি পরিবর্তন করা উচিত নয়। উদাহরণস্বরূপ, struct device বা struct device.links ভেতরের কোনো ফিল্ড পরিবর্তন করবেন না।

  • device.devres_head পরিবর্তন করতে, devm_clk_get() , devm_regulator_get() , বা devm_kzalloc() এর মতো একটি devm_*() ফাংশন ব্যবহার করুন।

  • struct device.links ভেতরের ফিল্ডগুলো পরিবর্তন করতে, device_link_add() বা device_link_del() এর মতো ডিভাইস লিঙ্ক এপিআই ব্যবহার করুন।

কম্প্যাটিবল প্রপার্টিযুক্ত ডিভাইসট্রি নোড পার্স করবেন না।

যদি কোনো ডিভাইস ট্রি (DT) নোডের একটি compatible প্রপার্টি থাকে, তবে এর জন্য একটি struct device স্বয়ংক্রিয়ভাবে বরাদ্দ করা হয়, অথবা যখন প্যারেন্ট DT নোডে of_platform_populate() কল করা হয় (সাধারণত প্যারেন্ট ডিভাইসের ডিভাইস ড্রাইভার দ্বারা)। ডিফল্ট প্রত্যাশা হলো (শিডিউলারের জন্য আগেভাগে ইনিশিয়ালাইজ করা কিছু ডিভাইস ছাড়া) যে, compatible প্রপার্টিযুক্ত একটি DT নোডের একটি struct device এবং একটি উপযুক্ত ডিভাইস ড্রাইভার থাকবে। অন্য সব ব্যতিক্রম আপস্ট্রিম কোড দ্বারা ইতোমধ্যেই হ্যান্ডেল করা হয়ে গেছে।

এছাড়াও, fw_devlink (পূর্বে যার নাম ছিল of_devlink ) compatible প্রপার্টিযুক্ত DT নোডগুলোকে এমন ডিভাইস হিসেবে বিবেচনা করে, যেগুলোর একটি struct device বরাদ্দ করা আছে এবং যা কোনো ড্রাইভার দ্বারা প্রোব করা হয়। যদি কোনো DT নোডের compatible ' প্রপার্টি থাকে কিন্তু বরাদ্দকৃত struct device প্রোব করা না হয়, তাহলে fw_devlink তার কনজিউমার ডিভাইসগুলোকে প্রোব করা থেকে অথবা তার সাপ্লায়ার ডিভাইসগুলোর জন্য sync_state() কল হওয়া থেকে আটকাতে পারে।

যদি আপনার ড্রাইভার সরাসরি একটি compatible ডিটি নোড খুঁজে বের করতে এবং তারপর সেই ডিটি নোডটি পার্স করতে of_find_*() ফাংশন (যেমন of_find_node_by_name() বা of_find_compatible_node() ) ব্যবহার করে, তাহলে এমন একটি ডিভাইস ড্রাইভার লিখে মডিউলটি ঠিক করুন যা ডিভাইসটিকে প্রোব করতে পারে অথবা compatible প্রপার্টিটি সরিয়ে দিন (এটি কেবল তখনই সম্ভব যদি এটি আপস্ট্রিম করা না হয়ে থাকে)। বিকল্প নিয়ে আলোচনা করতে, kernel-team@android.com- এ অ্যান্ড্রয়েড কার্নেল টিমের সাথে যোগাযোগ করুন এবং আপনার ব্যবহারের ক্ষেত্রগুলো ব্যাখ্যা করার জন্য প্রস্তুত থাকুন।

সরবরাহকারীদের খুঁজে বের করতে ডিটি পিহ্যান্ডেল ব্যবহার করুন।

যখনই সম্ভব, DT-তে একটি phandle (DT নোডের একটি রেফারেন্স বা পয়েন্টার) ব্যবহার করে কোনো সরবরাহকারীকে উল্লেখ করুন। সরবরাহকারীদের উল্লেখ করার জন্য স্ট্যান্ডার্ড DT বাইন্ডিং এবং phandle ব্যবহার করলে fw_devlink (যা পূর্বে of_devlink ) রানটাইমে DT পার্স করার মাধ্যমে স্বয়ংক্রিয়ভাবে ডিভাইসগুলোর মধ্যকার নির্ভরশীলতা নির্ধারণ করতে পারে। এরপর কার্নেল স্বয়ংক্রিয়ভাবে সঠিক ক্রমে ডিভাইসগুলো প্রোব করতে পারে, যার ফলে মডিউল লোড অর্ডারিং বা MODULE_SOFTDEP() এর প্রয়োজনীয়তা দূর হয়।

লিগ্যাসি সিনারিও (ARM কার্নেলে DT সাপোর্ট নেই)

পূর্বে, ARM কার্নেলে DT সাপোর্ট যুক্ত হওয়ার আগে, টাচ ডিভাইসের মতো ব্যবহারকারীরা রেগুলেটরের মতো সরবরাহকারীদের খুঁজে বের করার জন্য বিশ্বব্যাপী অনন্য স্ট্রিং ব্যবহার করত। উদাহরণস্বরূপ, ACME PMIC ড্রাইভার একাধিক রেগুলেটর রেজিস্টার বা অ্যাডভার্টাইজ করতে পারত (যেমন acme-pmic-ldo1 থেকে acme-pmic-ldo10 ) এবং একটি টাচ ড্রাইভার regulator_get(dev, "acme-pmic-ldo10") ব্যবহার করে একটি রেগুলেটর খুঁজে বের করতে পারত। কিন্তু, অন্য কোনো বোর্ডে LDO8 টাচ ডিভাইসটিকে সাপ্লাই দিতে পারত, যা একটি জটিল সিস্টেম তৈরি করত, যেখানে একই টাচ ড্রাইভারকে প্রতিটি বোর্ডের জন্য রেগুলেটরের সঠিক লুক-আপ স্ট্রিং নির্ধারণ করতে হতো যেখানে টাচ ডিভাইসটি ব্যবহৃত হচ্ছে।

বর্তমান পরিস্থিতি (ARM কার্নেলে DT সমর্থন)

ARM কার্নেলে DT সাপোর্ট যুক্ত হওয়ার পর, কনজিউমাররা একটি phandle ব্যবহার করে সাপ্লায়ারের ডিভাইস ট্রি নোড উল্লেখ করার মাধ্যমে DT-তে সাপ্লায়ারদের শনাক্ত করতে পারে। কনজিউমাররা কে রিসোর্সটি সরবরাহ করে তার পরিবর্তে এটি কীসের জন্য ব্যবহৃত হয় তার উপর ভিত্তি করেও রিসোর্সটির নামকরণ করতে পারে। উদাহরণস্বরূপ, পূর্ববর্তী উদাহরণের টাচ ড্রাইভারটি টাচ ডিভাইসের কোর এবং সেন্সরকে শক্তি প্রদানকারী সাপ্লায়ারদের পেতে regulator_get(dev, "core") এবং regulator_get(dev, "sensor") ব্যবহার করতে পারে। এই ধরনের ডিভাইসের জন্য সংশ্লিষ্ট DT নিম্নলিখিত কোড স্যাম্পলের অনুরূপ:

touch-device {
    compatible = "fizz,touch";
    ...
    core-supply = <&acme_pmic_ldo4>;
    sensor-supply = <&acme_pmic_ldo10>;
};

acme-pmic {
    compatible = "acme,super-pmic";
    ...
    acme_pmic_ldo4: ldo4 {
        ...
    };
    ...
    acme_pmic_ldo10: ldo10 {
        ...
    };
};

উভয় সংকটময় পরিস্থিতি

পুরানো কার্নেল থেকে পোর্ট করা কিছু ড্রাইভারের DT-তে লিগ্যাসি আচরণ অন্তর্ভুক্ত থাকে, যা লিগ্যাসি স্কিমের সবচেয়ে খারাপ দিকটিকে গ্রহণ করে এবং সেটিকে নতুন স্কিমের উপর চাপিয়ে দেয়, যে নতুন স্কিমটি আসলে কাজকে সহজ করার জন্য তৈরি। এই ধরনের ড্রাইভারগুলিতে, কনজিউমার ড্রাইভার একটি ডিভাইস-নির্দিষ্ট DT প্রপার্টি ব্যবহার করে লুকআপের জন্য স্ট্রিংটি পড়ে, সাপ্লায়ার সাপ্লায়ার রিসোর্স রেজিস্টার করার জন্য ব্যবহৃত নামটি নির্ধারণ করতে আরেকটি সাপ্লায়ার-নির্দিষ্ট প্রপার্টি ব্যবহার করে, এবং তারপর কনজিউমার ও সাপ্লায়ার উভয়েই সাপ্লায়ারকে লুকআপ করার জন্য স্ট্রিং ব্যবহারের সেই একই পুরানো স্কিম ব্যবহার করতে থাকে। এই উভয় সংকটের পরিস্থিতিতে:

  • টাচ ড্রাইভারটি নিম্নলিখিত কোডের অনুরূপ কোড ব্যবহার করে:

    str = of_property_read(np, "fizz,core-regulator");
    core_reg = regulator_get(dev, str);
    str = of_property_read(np, "fizz,sensor-regulator");
    sensor_reg = regulator_get(dev, str);
    
  • DT নিম্নলিখিতের অনুরূপ কোড ব্যবহার করে:

    touch-device {
      compatible = "fizz,touch";
      ...
      fizz,core-regulator = "acme-pmic-ldo4";
      fizz,sensor-regulator = "acme-pmic-ldo4";
    };
    acme-pmic {
      compatible = "acme,super-pmic";
      ...
      ldo4 {
        regulator-name = "acme-pmic-ldo4"
        ...
      };
      ...
      acme_pmic_ldo10: ldo10 {
        ...
        regulator-name = "acme-pmic-ldo10"
      };
    };
    

ফ্রেমওয়ার্ক API ত্রুটিগুলি পরিবর্তন করবেন না

ফ্রেমওয়ার্ক এপিআই, যেমন regulator , clocks , irq , gpio , phys , এবং extcon , একটি ত্রুটি রিটার্ন মান হিসাবে -EPROBE_DEFER ফেরত দেয়। এটি নির্দেশ করে যে একটি ডিভাইস প্রোব করার চেষ্টা করছে কিন্তু এই মুহূর্তে পারছে না, এবং কার্নেলের উচিত পরে আবার প্রোব করার চেষ্টা করা। এই ধরনের ক্ষেত্রে আপনার ডিভাইসের .probe() ফাংশনটি যাতে প্রত্যাশিতভাবে ব্যর্থ হয়, তা নিশ্চিত করতে ত্রুটির মানটি প্রতিস্থাপন বা রিম্যাপ করবেন না। ত্রুটির মান প্রতিস্থাপন বা রিম্যাপ করলে -EPROBE_DEFER বাদ পড়ে যেতে পারে এবং এর ফলে আপনার ডিভাইসটি কখনোই প্রোব করা হবে না।

devm_*() API ভ্যারিয়েন্ট ব্যবহার করুন

যখন কোনো ডিভাইস devm_*() API ব্যবহার করে কোনো রিসোর্স অধিগ্রহণ করে, তখন যদি ডিভাইসটি প্রোব করতে ব্যর্থ হয়, অথবা সফলভাবে প্রোব করার পর আনবাউন্ড হয়ে যায়, তাহলে কার্নেল স্বয়ংক্রিয়ভাবে রিসোর্সটি রিলিজ করে দেয়। এই সক্ষমতাটি probe() ফাংশনের এরর হ্যান্ডলিং কোডকে আরও পরিচ্ছন্ন করে তোলে, কারণ devm_*() দ্বারা অধিগ্রহণ করা রিসোর্সগুলো রিলিজ করার জন্য goto জাম্পের প্রয়োজন হয় না এবং এটি ড্রাইভার আনবাইন্ডিং অপারেশনগুলোকে সরল করে।

ডিভাইস-ড্রাইভার আনবাইন্ডিং পরিচালনা করুন

ডিভাইস ড্রাইভার আনবাইন্ডিং করার ব্যাপারে সচেতন হোন এবং আনবাইন্ডিংকে অনির্ধারিত রাখবেন না, কারণ অনির্ধারিত মানেই নিষিদ্ধ নয়। আপনাকে হয় ডিভাইস-ড্রাইভার আনবাইন্ডিং সম্পূর্ণরূপে প্রয়োগ করতে হবে , অথবা স্পষ্টভাবে তা নিষ্ক্রিয় করতে হবে।

ডিভাইস-ড্রাইভার আনবাইন্ডিং বাস্তবায়ন করুন

ডিভাইস-ড্রাইভার আনবাইন্ডিং সম্পূর্ণরূপে প্রয়োগ করার সময়, মেমরি বা রিসোর্স লিক এবং নিরাপত্তা সংক্রান্ত সমস্যা এড়াতে ডিভাইস ড্রাইভারগুলোকে পরিষ্কারভাবে আনবাইন্ড করুন। আপনি একটি ড্রাইভারের probe() ফাংশন কল করে একটি ডিভাইসকে ড্রাইভারের সাথে বাইন্ড করতে পারেন এবং ড্রাইভারের remove() ফাংশন কল করে ডিভাইসটিকে আনবাইন্ড করতে পারেন। যদি কোনো remove() ফাংশন না থাকে, তবুও কার্নেল ডিভাইসটিকে আনবাইন্ড করতে পারে; ড্রাইভার কোর ধরে নেয় যে ডিভাইস থেকে আনবাইন্ড করার সময় ড্রাইভারের কোনো পরিষ্করণমূলক কাজের প্রয়োজন নেই। যখন নিম্নলিখিত দুটি শর্তই সত্য হয়, তখন একটি ডিভাইস থেকে আনবাইন্ড করা ড্রাইভারকে কোনো সুস্পষ্ট পরিষ্করণমূলক কাজ করতে হয় না:

  • ড্রাইভারের probe() ফাংশন দ্বারা অর্জিত সমস্ত রিসোর্স devm_*() API-এর মাধ্যমে হয়ে থাকে।

  • হার্ডওয়্যার ডিভাইসটির জন্য কোনো শাটডাউন বা নিষ্ক্রিয়করণ অনুক্রমের প্রয়োজন নেই।

এই পরিস্থিতিতে, ড্রাইভার কোর devm_*() API-এর মাধ্যমে অর্জিত সমস্ত রিসোর্স মুক্ত করার কাজটি পরিচালনা করে। যদি পূর্ববর্তী বিবৃতিগুলির মধ্যে কোনোটি অসত্য হয়, তাহলে কোনো ডিভাইস থেকে আনবাইন্ড করার সময় ড্রাইভারকে ক্লিনআপ (রিসোর্স মুক্ত করা এবং হার্ডওয়্যার শাট ডাউন বা কুইয়েস করা) করতে হবে। একটি ডিভাইস যাতে ড্রাইভার মডিউলকে সুষ্ঠুভাবে আনবাইন্ড করতে পারে, তা নিশ্চিত করতে নিম্নলিখিত বিকল্পগুলির মধ্যে একটি ব্যবহার করুন:

  • যদি হার্ডওয়্যারটির শাটডাউন বা কুইসিং সিকোয়েন্সের প্রয়োজন না হয়, তাহলে devm_*() API ব্যবহার করে রিসোর্স অধিগ্রহণ করার জন্য ডিভাইস মডিউলটি পরিবর্তন করুন।

  • probe() ফাংশনের সাথে একই স্ট্রাকচারে remove() ড্রাইভার অপারেশনটি প্রয়োগ করুন, তারপর remove() ফাংশন ব্যবহার করে পরিষ্করণ ধাপগুলো সম্পন্ন করুন।

ডিভাইস-ড্রাইভার আনবাইন্ডিং স্পষ্টভাবে নিষ্ক্রিয় করুন (সুপারিশ করা হয় না)

ডিভাইস-ড্রাইভার আনবাইন্ডিং স্পষ্টভাবে নিষ্ক্রিয় করার সময়, আপনাকে আনবাইন্ডিং এবং মডিউল আনলোডিং উভয়ই নিষিদ্ধ করতে হবে।

  • আনবাইন্ডিং বন্ধ করতে, ড্রাইভারের struct device_driversuppress_bind_attrs ফ্ল্যাগটিকে true তে সেট করুন; এই সেটিংটি ড্রাইভারের sysfs ডিরেক্টরিতে bind এবং unbind ফাইলগুলিকে প্রদর্শিত হতে বাধা দেয়। unbind ফাইলটিই ইউজার স্পেসকে কোনো ড্রাইভারকে তার ডিভাইস থেকে আনবাইন্ডিং করার প্রক্রিয়া শুরু করার সুযোগ দেয়।

  • মডিউল আনলোডিং বন্ধ করতে, নিশ্চিত করুন যে lsmod এ মডিউলটির [permanent] অপশন রয়েছে। module_exit() বা module_XXX_driver() ব্যবহার না করার মাধ্যমে মডিউলটি [permanent] হিসেবে চিহ্নিত হয়।

প্রোব ফাংশনের ভেতর থেকে ফার্মওয়্যার লোড করবেন না।

ড্রাইভারের .probe() ফাংশনের ভেতর থেকে ফার্মওয়্যার লোড করা উচিত নয়, কারণ ফ্ল্যাশ বা পার্মানেন্ট স্টোরেজ ভিত্তিক ফাইল সিস্টেম মাউন্ট হওয়ার আগে ড্রাইভার প্রোব করলে তারা ফার্মওয়্যারটি অ্যাক্সেস নাও পেতে পারে। এমন ক্ষেত্রে, request_firmware*() API দীর্ঘ সময়ের জন্য ব্লক হয়ে যেতে পারে এবং তারপর ব্যর্থ হতে পারে, যা বুট প্রক্রিয়াকে অপ্রয়োজনীয়ভাবে ধীর করে দিতে পারে। এর পরিবর্তে, ক্লায়েন্ট যখন ডিভাইসটি ব্যবহার করা শুরু করে, তখন ফার্মওয়্যার লোড করার কাজটি স্থগিত রাখুন। উদাহরণস্বরূপ, একটি ডিসপ্লে ড্রাইভার ডিসপ্লে ডিভাইসটি খোলার সময় ফার্মওয়্যারটি লোড করতে পারে।

কিছু ক্ষেত্রে ফার্মওয়্যার লোড করার জন্য .probe() ব্যবহার করা ঠিক হতে পারে, যেমন এমন একটি ক্লক ড্রাইভারের ক্ষেত্রে যার কাজ করার জন্য ফার্মওয়্যার প্রয়োজন কিন্তু ডিভাইসটি ইউজার স্পেসে উন্মুক্ত নয়। অন্যান্য উপযুক্ত ব্যবহারের ক্ষেত্রও সম্ভব।

অ্যাসিঙ্ক্রোনাস প্রোবিং বাস্তবায়ন করুন

ভবিষ্যতের রিলিজগুলিতে অ্যান্ড্রয়েডে যোগ করা হতে পারে এমন নতুন উন্নত বৈশিষ্ট্য, যেমন প্যারালাল মডিউল লোডিং বা বুট টাইম দ্রুত করার জন্য ডিভাইস প্রোবিং-এর সুবিধা নিতে অ্যাসিঙ্ক্রোনাস প্রোবিং সমর্থন করুন এবং ব্যবহার করুন। যে ড্রাইভার মডিউলগুলি অ্যাসিঙ্ক্রোনাস প্রোবিং ব্যবহার করে না, সেগুলি এই ধরনের অপ্টিমাইজেশনের কার্যকারিতা কমিয়ে দিতে পারে।

কোনো ড্রাইভারকে অ্যাসিঙ্ক্রোনাস প্রোবিং সমর্থনকারী ও পছন্দকারী হিসেবে চিহ্নিত করতে, ড্রাইভারটির struct device_driver মেম্বারে probe_type ফিল্ডটি সেট করুন। নিম্নলিখিত উদাহরণটি একটি প্ল্যাটফর্ম ড্রাইভারের জন্য এই ধরনের সমর্থন সক্রিয় করা দেখাচ্ছে:

static struct platform_driver acme_driver = {
        .probe          = acme_probe,
        ...
        .driver         = {
                .name   = "acme",
                ...
                .probe_type = PROBE_PREFER_ASYNCHRONOUS,
        },
};

অ্যাসিঙ্ক্রোনাস প্রোবিংয়ের সাথে একটি ড্রাইভারকে কাজ করাতে বিশেষ কোডের প্রয়োজন হয় না। তবে, অ্যাসিঙ্ক্রোনাস প্রোবিং সাপোর্ট যোগ করার সময় নিম্নলিখিত বিষয়গুলো মনে রাখবেন।

  • পূর্বে পরীক্ষিত নির্ভরতা সম্পর্কে অনুমান করবেন না। সরাসরি বা পরোক্ষভাবে (বেশিরভাগ ফ্রেমওয়ার্ক কলে) পরীক্ষা করুন এবং যদি এক বা একাধিক সরবরাহকারী এখনও প্রস্তুত না থাকে, তাহলে -EPROBE_DEFER রিটার্ন করুন।

  • যদি আপনি কোনো প্যারেন্ট ডিভাইসের প্রোব ফাংশনে চাইল্ড ডিভাইস যোগ করেন, তবে ধরে নেবেন না যে চাইল্ড ডিভাইসটি সঙ্গে সঙ্গে প্রোব হয়ে যাবে।

  • যদি কোনো প্রোব ব্যর্থ হয়, তাহলে যথাযথ ত্রুটি পরিচালনা এবং পরিষ্করণ করুন (দেখুন `use devm_*() API variants` )।

ডিভাইস প্রোব অর্ডার করার জন্য MODULE_SOFTDEP ব্যবহার করবেন না।

MODULE_SOFTDEP() ফাংশনটি ডিভাইস প্রোবগুলোর ক্রম নিশ্চিত করার জন্য একটি নির্ভরযোগ্য সমাধান নয় এবং নিম্নলিখিত কারণগুলোর জন্য এটি ব্যবহার করা উচিত নয়।

  • বিলম্বিত প্রোব। যখন একটি মডিউল লোড হয়, তখন ডিভাইস প্রোবটি বিলম্বিত হতে পারে কারণ এর সরবরাহকারীদের মধ্যে একজন প্রস্তুত নাও থাকতে পারে। এর ফলে মডিউল লোড অর্ডার এবং ডিভাইস প্রোব অর্ডারের মধ্যে অমিল দেখা দিতে পারে।

  • একটি ড্রাইভার, অনেক ডিভাইস। একটি ড্রাইভার মডিউল একটি নির্দিষ্ট ডিভাইসের ধরন পরিচালনা করতে পারে। যদি সিস্টেমে কোনো একটি ডিভাইসের ধরনের একাধিক ইনস্ট্যান্স থাকে এবং সেই ডিভাইসগুলোর প্রতিটির প্রোব অর্ডারের প্রয়োজনীয়তা ভিন্ন হয়, তবে আপনি মডিউল লোড অর্ডারিং ব্যবহার করে সেই প্রয়োজনীয়তাগুলো পূরণ করতে পারবেন না।

  • অ্যাসিঙ্ক্রোনাস প্রোবিং। যে ড্রাইভার মডিউলগুলো অ্যাসিঙ্ক্রোনাস প্রোবিং করে, সেগুলো লোড হওয়ার সাথে সাথেই কোনো ডিভাইসকে প্রোব করে না। এর পরিবর্তে, একটি প্যারালাল থ্রেড ডিভাইস প্রোবিংয়ের কাজটি পরিচালনা করে, যার ফলে মডিউল লোড হওয়ার ক্রম এবং ডিভাইস প্রোব হওয়ার ক্রমের মধ্যে অমিল দেখা দিতে পারে। উদাহরণস্বরূপ, যখন একটি I2C প্রধান ড্রাইভার মডিউল অ্যাসিঙ্ক্রোনাস প্রোবিং করে এবং একটি টাচ ড্রাইভার মডিউল I2C বাসে থাকা PMIC-এর উপর নির্ভরশীল থাকে, তখন টাচ ড্রাইভার এবং PMIC ড্রাইভার সঠিক ক্রমে লোড হলেও, PMIC ড্রাইভারের প্রোবের আগে টাচ ড্রাইভারের প্রোব করার চেষ্টা করা হতে পারে।

আপনার ড্রাইভার মডিউলগুলো যদি MODULE_SOFTDEP() ফাংশন ব্যবহার করে থাকে, তবে সেগুলোকে এমনভাবে ঠিক করুন যাতে তারা ঐ ফাংশনটি ব্যবহার না করে। আপনাকে সাহায্য করার জন্য, অ্যান্ড্রয়েড টিম এমন কিছু পরিবর্তন আপস্ট্রিম করেছে যা কার্নেলকে MODULE_SOFTDEP() ব্যবহার না করেই অর্ডারিং সমস্যাগুলো সমাধান করতে সক্ষম করে। বিশেষত, আপনি প্রোব অর্ডারিং নিশ্চিত করতে fw_devlink ব্যবহার করতে পারেন এবং (একটি ডিভাইসের সমস্ত কনজিউমার প্রোব করার পরে) প্রয়োজনীয় যেকোনো কাজ সম্পাদন করতে sync_state() কলব্যাক ব্যবহার করতে পারেন।

কনফিগারেশনের জন্য #ifdef এর পরিবর্তে #if IS_ENABLED() ব্যবহার করুন।

ভবিষ্যতে কনফিগারেশনটি ট্রাইস্টেট কনফিগারেশনে পরিবর্তিত হলেও, #if ব্লকের ভেতরের কোডটি যেন কম্পাইল হতে থাকে তা নিশ্চিত করতে #ifdef CONFIG_XXX এর পরিবর্তে #if IS_ENABLED(CONFIG_XXX) ব্যবহার করুন। পার্থক্যগুলো নিম্নরূপ:

  • #if IS_ENABLED(CONFIG_XXX) এর মান true হয় যখন CONFIG_XXX মডিউল ( =m ) বা বিল্ট-ইন ( =y ) হিসেবে সেট করা থাকে।

  • #ifdef CONFIG_XXX তখনই true হয় যখন CONFIG_XXX বিল্ট-ইন ( =y ) হিসেবে সেট করা থাকে, কিন্তু যখন CONFIG_XXX মডিউল ( =m ) হিসেবে সেট করা থাকে তখন তা হয় না। এটি কেবল তখনই ব্যবহার করুন যখন আপনি নিশ্চিত যে কনফিগটি মডিউল বা ডিজেবল করা থাকলেও আপনি একই কাজ করতে চান।

শর্তসাপেক্ষ কম্পাইলের জন্য সঠিক ম্যাক্রো ব্যবহার করুন।

যদি CONFIG_XXX কে মডিউল ( =m ) হিসেবে সেট করা হয়, তাহলে বিল্ড সিস্টেম স্বয়ংক্রিয়ভাবে CONFIG_XXX_MODULE নির্ধারণ করে। যদি আপনার ড্রাইভারটি CONFIG_XXX দ্বারা নিয়ন্ত্রিত হয় এবং আপনি পরীক্ষা করতে চান যে আপনার ড্রাইভারটি একটি মডিউল হিসেবে কম্পাইল করা হচ্ছে কিনা, তাহলে নিম্নলিখিত নির্দেশিকাগুলো ব্যবহার করুন:

  • আপনার ড্রাইভারের জন্য C ফাইলে (বা হেডার ফাইল নয় এমন যেকোনো সোর্স ফাইলে) #ifdef CONFIG_XXX_MODULE ব্যবহার করবেন না, কারণ এটি অপ্রয়োজনীয়ভাবে সীমাবদ্ধ এবং কনফিগারেশন ফাইলের নাম পরিবর্তন করে CONFIG_XYZ রাখা হলে এটি কাজ করে না। মডিউলে কম্পাইল করা হয় এমন যেকোনো নন-হেডার সোর্স ফাইলের জন্য, বিল্ড সিস্টেম স্বয়ংক্রিয়ভাবে সেই ফাইলের স্কোপের জন্য MODULE নির্ধারণ করে দেয়। অতএব, কোনো C ফাইল (বা যেকোনো নন-হেডার সোর্স ফাইল) একটি মডিউলের অংশ হিসেবে কম্পাইল করা হচ্ছে কিনা তা পরীক্ষা করতে, #ifdef MODULE ( CONFIG_ প্রিফিক্স ছাড়া) ব্যবহার করুন।

  • হেডার ফাইলের ক্ষেত্রে একই যাচাই প্রক্রিয়াটি আরও জটিল, কারণ হেডার ফাইলগুলো সরাসরি বাইনারিতে কম্পাইল না হয়ে, বরং একটি C ফাইল (বা অন্যান্য সোর্স ফাইল)-এর অংশ হিসেবে কম্পাইল হয়। হেডার ফাইলের জন্য নিম্নলিখিত নিয়মগুলো ব্যবহার করুন:

    • যে হেডার ফাইলে #ifdef MODULE ব্যবহৃত হয়, তার ফলাফল নির্ভর করে কোন সোর্স ফাইলটি এটি ব্যবহার করছে তার উপর। এর মানে হলো, একই বিল্ডের মধ্যে থাকা একই হেডার ফাইলের কোডের ভিন্ন ভিন্ন অংশ বিভিন্ন সোর্স ফাইলের জন্য (মডিউল বনাম বিল্ট-ইন বা নিষ্ক্রিয়) কম্পাইল হতে পারে। এটি তখন কাজে আসতে পারে যখন আপনি এমন একটি ম্যাক্রো সংজ্ঞায়িত করতে চান যা বিল্ট-ইন কোডের জন্য একভাবে এবং একটি মডিউলের জন্য ভিন্নভাবে প্রসারিত হবে।

    • যে হেডার ফাইলে একটি নির্দিষ্ট CONFIG_XXX মডিউল হিসেবে সেট করা হলে কোনো কোড কম্পাইল করার প্রয়োজন হয় (সেটিকে অন্তর্ভুক্তকারী সোর্স ফাইলটি মডিউল হোক বা না হোক), সেই হেডার ফাইলে অবশ্যই #ifdef CONFIG_XXX_MODULE ব্যবহার করতে হবে।