Hello LKML, We, HandHelds.org, for quite some time are working on generilized support for consumer handheld devices (with primary focus on ARM-based PocketPC's ans smartphones). While we made big progress in many areas, we still were no able to put all the part of riddle together, producing clean and manageable for dozens of project machines (we have ~30 ports in our kernel, with ~20 well, or sufficiuently, developed, and a hundred in queue).
One of the pressing problem we had is code duplication even to support simple features, like USB connection support, RS232 port support, BT support, etc. To the large part drivers for such features consist of the inititialization of common architecture-provided code/drivers, and activating machine-specific support hardware like transceivers, etc. This activation most of the time is handled by turning on some GPIO lines, but the problem that our target machine set has wild varying GPIO architecture. Besides CPU-builtin GPIOs, almost every model has GPIOs on some SoC-type peripheral controller or GPIO extender (in turn ranging from latches on CPU bus to connected to I2C, etc.). What we get is dozens of shim drivers, which for 30 template lines have 2-3 lines of real content. Submitting them for inclusion would treated like spamming kernel tree, right? ;-) Recently, Generic GPIO framework was proposed, and merged into 2.6.21-rc, which is big step forward. But its scope is still to provide common GPIO API for different *architectures*, not devices. It makes it possible to minipulate GPIOs on PXA, OMAP, S3Cxxxx in the same way, but does not allow other GPIO-providing devices to leverage this framework. HandHelds.org developers draw attention to this and asked to extend scope and solve all GPIO-related problems (see for example http://lkml.org/lkml/2007/1/1/111), however, it was omitted from the first round of Generic GPIO API. So, I would like to propose a solution for GPIO extension problem, and moreover, a pattern, or paradigm, to solve such kind of problem. This latter aim is probably not less important than the specific aim of solving GPIO namespace problem. I have to say from the start that proposed solution is of course not new - vice versa, it is well known and proved solution to solve polymorphism problems ("different things do something in similar ways, or with similar outcome"). That's why I use words pattern and paradigm - I'd like to propose to leverage this technique for Linux kernel driver writing, and if that would be accepted, to shape it in such way that it will be easily recognizable and people will not spend time "reinventing" it, or hesitating if it's usable or not. Well, idea is simple. For any device which provides GPIOs, we can define standard GPIO operation. We can express them via specifying function (method) signatures and describing semantics behind them. Minimal operation set for GPIO would be: int get(int gpio_no) Get value of GPIO with number gpio_no, return 0 if it is not set, non-zero if it set. void set(int gpio_no, int val) Set value of GPIO with number gpio_no: if val is non-zero, set it high, otherwise, low. In terms of object-oriented programming, this definition called Interface. So, we define interface which GPIO-provding devices should implement, and details of implementation will be different for different devices. As long as they conform to interface, all we need to perform a GPIO operation is reference to owning device and its local gpio number, that's all. Device itself will handle actual operations, and in this respect, it must implement interface using what in OOP called virtual methods. That's all for idea. The rest is just peculiarity of implementation within bounds of existing Linux Kernel infrastructure (LDM model first of all). Proposed implementation is: 1. Virtual methods are represented by function pointers (which is standard). 2. Virtual methods for some interface are groupped to structure called VTABLE (common name). 3. Methods are defined for device, so must be accessed using it. LDM uses struct device to represent a device, so methods must be accessible using a reference to struct device. 4. Relatively few devices will use virtual methods, so no talk about extending struct device itself. Instead, existing means of struct device extension will be used. 5. Methods are of course public notion of a device, so it is proposed to makes them accessible via platform_data member (and nor for example, driver_data). Thus, VTABLE will be just part of platform_data structure for a device, together with other data specific for a device. 6. What will fill actual method pointers in VTABLE? It is proposed that - of course - a driver both implements virtual methods and initializes VTABLE structure. This is of course perfect fit with LDM paradigm - device structures define actual device properties, and driver what implements handling of device behavior. This holds true for virtual methods approach, except that it allows to define polymorhic behavior with guaranteed interface across different drivers. 7. Note that in p.6 uses opposite data flow for VTABLE to what is currently mostly used for platform_data. As it is now, it is assumed that platform_data serves as "input data" for driver. It's pretty common to pass method pointers via paltform_data - but those methods are still for driver to use. Virtual methods paradigm works the other way - it's driver exports its methods for the rest of the world to use via VTABLE, a part for platform_data. Actual implementation: Standard VTABLE syntax ==== vtable.h ===== /* Virtual Method Table support - experimental */ /* A way to reference vtable */ #define VTABLE(interface) interface##_methods /* A way to have vtable definined. This should be put as the first member of enclosing struct */ #define VTABLE_DEFINE(interface) \ struct interface##_methods interface##_methods /* A way to reference method. Result is pointer to method, so it can be called, checked for NULL, etc */ #define METHOD(device, interface, method) \ (((struct interface##_methods*)((device)->platform_data))->method) ================== Example of interface definition: ===== gpiodev.h ===== // Interface struct VTABLE(gpio) { int (*get)(struct device *this, int gpio_no); void (*set)(struct device *this, int gpio_no, int val); int (*to_irq)(struct device *this, int gpio_no); }; ===================== (Note that methods are of course receive "this" pointer to owning device.) Example of adding interface for some device (ASIC3 is SoC chip): ==== asic3_base.h ==== struct asic3_platform_data { VTABLE_DEFINE(gpio); unsigned long bus_shift; ... } ====================== Example of virtual method implementation and VTABLE initialization in driver: ==== asic3_base.c ===== static int asic3_get_gpio_bit(struct device *dev, int gpio) { ... } ... static int asic3_probe(struct platform_device *pdev) { ... pdata->VTABLE(gpio).get = asic3_get_gpio_bit; pdata->VTABLE(gpio).set = asic3_set_gpio_bit; pdata->VTABLE(gpio).to_irq = asic3_gpio_to_irq; ... } ======================= Example of usage: =========== struct platform_device *asic3; int val = METHOD(&asic3.dev, gpio, get)(&asic3.dev, GPIO_IN_NO); METHOD(&asic3.dev, gpio, set)(&asic3.dev, GPIO_OUT_NO, val); =========== Arguably, the syntax is still verbose even with METHOD() macro. Convenience definitions are proposed: ====== gpiodev.h, continuation ====== // Generalized GPIO structure struct gpio { struct platform_device *gpio_dev; int gpio_no; }; // Convenience wrappers static inline int gpio_dev_get(struct gpio *gpio) { return METHOD(&gpio->gpio_dev->dev, gpio, get)(&gpio->gpio_dev->dev, gpio->gpio_no); } static inline void gpio_dev_set(struct gpio *gpio, int val) { METHOD(&gpio->gpio_dev->dev, gpio, set)(&gpio->gpio_dev->dev, gpio->gpio_no, val); } static inline int gpio_dev_to_irq(struct gpio *gpio) { return METHOD(&gpio->gpio_dev->dev, gpio, to_irq)(&gpio->gpio_dev->dev, gpio->gpio_no); } ======================================= After seeing this last part, someone may ask - why all these VTABLE, etc. then, if in the end it is just conventional API with usual function names? The asswer is simple, and was alreday given above: I would like to propose virtual method as useful and reusable pattern. There're more interfaces than "gpio" to define, and more usecases for its usage than generalizing GPIO access (for example, we used it for abstracting touchscreen drivers away from teh underlying ADC chips used). In this respect, VTABLE(), METHOD() macros serve the same purpose as container_of() and list_for_each() - they are besides offering (more) convenient syntax, also carry important annotattion and educational messages, like "it's ok, and encouraged to embed one structure into another - use it!" or "list manipulation is a trivial operation for kernel, and we want you to treat it as such and use in standard, easily distinguishable way". Thank you for your review and suggestions, -- Paul mailto:[EMAIL PROTECTED] - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/