ভেন্ডর মডিউল সংক্রান্ত নির্দেশিকা

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

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

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

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

    • ডিভাইস না থাকলে, শুধুমাত্র module_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 স্ট্রিং বা ডিভাইসের অক্স ডেটা ব্যবহার করে পার্থক্য করতে পারেন। অথবা, আপনি ড্রাইভার মডিউলকে দুটি মডিউলে ভাগ করতে পারেন।

Init ও exit ফাংশনের ব্যতিক্রম

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

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

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

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

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

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

যেমন, include/linux/fwnode.h-এ struct fwnode_handle-এর সংজ্ঞা দেওয়া আছে, কিন্তু 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_info। include/linux/soc-এ সংজ্ঞায়িত ডেটা স্ট্রাকচার ছাড়প্রাপ্ত।

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

  • আপনার মডিউল ডেটা স্ট্রাকচার ইনিশিয়ালাইজ করেনি। যেমন, regulator_register()-এর মাধ্যমে struct regulator_dev ফেরত দেওয়া হয়েছে।

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

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

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

মানানসই প্রপার্টি সহ devicetree নোড পার্স করবেন না

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

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

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

সাপ্লায়ার খুঁজতে DT phandles ব্যবহার করুন

সম্ভব হলে DT-তে phandle (DT নোডের রেফারেন্স বা পয়েন্টার) ব্যবহার করে সাপ্লায়ারকে রেফার করুন। সাপ্লায়ারদের উল্লেখ করার জন্য স্ট্যান্ডার্ড DT বাইন্ডিং ও ফ্যান্ডেল ব্যবহার করলে 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-এর মতো ফ্রেমওয়ার্ক API, -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_driver-এ suppress_bind_attrs ফ্ল্যাগ true-এ সেট করুন; এই সেটিং ড্রাইভারের sysfs ডিরেক্টরিতে bind এবং unbind ফাইল দেখানো বন্ধ করে। unbind ফাইলটি ব্যবহারকারীর স্পেসকে তার ডিভাইস থেকে ড্রাইভারের আনবাইন্ডিং ট্রিগার করার অনুমতি দেয়।

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

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

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

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

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

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

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

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

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

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

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

  • প্রোব ব্যর্থ হলে, সঠিকভাবে সমস্যা সমাধান ও ক্লিন-আপ করুন (devm_*() API ভেরিয়েন্ট ব্যবহার করুন দেখুন)।

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

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

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

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

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

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

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

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

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

  • CONFIG_XXX বিল্ট-ইন (=y) হিসেবে সেট করা থাকলে, #ifdef CONFIG_XXX-এর মান true হয়, কিন্তু 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 ব্যবহার করতে হবে।