#include #include #include #define to_ipaq_option_driver(x) \ container_of(x, struct ipaq_option_driver, driver) static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipaq_option_device *idev = to_ipaq_option_device(dev); return snprintf(buf, PAGE_SIZE, "%04x\n", idev->id.vendor); } static DEVICE_ATTR_RO(vendor); static ssize_t device_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipaq_option_device *idev = to_ipaq_option_device(dev); return snprintf(buf, PAGE_SIZE, "%04x\n", idev->id.device); } static DEVICE_ATTR_RO(device); static struct attribute *ipaq_option_sleeve_attrs[] = { &dev_attr_vendor.attr, &dev_attr_device.attr, NULL, }; static const struct attribute_group ipaq_option_sleeve_dev_group = { .attrs = ipaq_option_sleeve_attrs, }; static const struct attribute_group *ipaq_option_sleeve_dev_groups[] = { &ipaq_option_sleeve_dev_group, NULL, }; static int ipaq_option_sleeve_match(struct device *dev, struct device_driver *drv) { struct ipaq_option_device *idev = to_ipaq_option_device(dev); struct ipaq_option_driver *idrv = to_ipaq_option_driver(drv); const struct ipaq_option_id *id; for (id = idrv->id_table; id->vendor || id->device; id++) if (id->vendor == idev->id.vendor && id->device == idev->id.device) return 1; return 0; } static int ipaq_option_sleeve_uevent(struct device *dev, struct kobj_uevent_env *env) { struct ipaq_option_device *idev = to_ipaq_option_device(dev); if (!dev) return -ENODEV; if (add_uevent_var(env, "MODALIAS=ipaq:v%04Xd%04X", idev->id.vendor, idev->id.device)) return -ENOMEM; return 0; } struct bus_type ipaq_option_sleeve_bus = { .name = "ipaq-sleeve", .dev_groups = ipaq_option_sleeve_dev_groups, .match = ipaq_option_sleeve_match, .uevent = ipaq_option_sleeve_uevent, }; EXPORT_SYMBOL_GPL(ipaq_option_sleeve_bus); static void ipaq_option_release(struct device *dev) { struct ipaq_option_device *opt_dev = to_ipaq_option_device(dev); kfree(opt_dev); } int ipaq_option_device_add(struct device *parent, struct ipaq_option_id id) { struct ipaq_option_device *idev; idev = kzalloc(sizeof(*idev), GFP_KERNEL); if (!idev) return -ENOMEM; idev->id = id; device_initialize(&idev->dev); dev_set_name(&idev->dev, "sleeve"); idev->dev.parent = parent; idev->dev.release = ipaq_option_release; idev->dev.bus = &ipaq_option_sleeve_bus; return device_add(&idev->dev); } EXPORT_SYMBOL_GPL(ipaq_option_device_add); static int ipaq_option_device_remove(struct device *dev, void *data) { device_unregister(dev); return 0; } void ipaq_option_device_del(void) { bus_for_each_dev(&ipaq_option_sleeve_bus, NULL, NULL, ipaq_option_device_remove); } EXPORT_SYMBOL_GPL(ipaq_option_device_del); static int ipaq_option_sleeve_init(void) { return bus_register(&ipaq_option_sleeve_bus); } core_initcall(ipaq_option_sleeve_init); static void ipaq_option_sleeve_exit(void) { bus_unregister(&ipaq_option_sleeve_bus); } module_exit(ipaq_option_sleeve_exit); MODULE_LICENSE("GPL");