/* * Copyright 2019 Broadcom. * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * A pddf kernel module to create access-data attributes for client creation */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pddf_client_defs.h" NEW_DEV_ATTR pddf_data={0}; EXPORT_SYMBOL(pddf_data); int showall = 0; /* CLIENT CREATION DATA ATTR LIST */ PDDF_DATA_ATTR(i2c_type, S_IWUSR|S_IRUGO, show_pddf_data, store_pddf_data, PDDF_CHAR, 32, (void*)&pddf_data.i2c_type, NULL); PDDF_DATA_ATTR(i2c_name, S_IWUSR|S_IRUGO, show_pddf_data, store_pddf_data, PDDF_CHAR, 32, (void*)&pddf_data.i2c_name, NULL); PDDF_DATA_ATTR(parent_bus, S_IWUSR|S_IRUGO, show_pddf_data, store_pddf_data, PDDF_INT_HEX, sizeof(int), (void*)&pddf_data.parent_bus, NULL); PDDF_DATA_ATTR(dev_type, S_IWUSR|S_IRUGO, show_pddf_data, store_pddf_data, PDDF_CHAR, 32, (void*)&pddf_data.dev_type, NULL); PDDF_DATA_ATTR(dev_id, S_IWUSR|S_IRUGO, show_pddf_data, store_pddf_data, PDDF_INT_DEC, sizeof(int), (void*)&pddf_data.dev_id, NULL); PDDF_DATA_ATTR(dev_addr, S_IWUSR|S_IRUGO, show_pddf_data, store_pddf_data, PDDF_INT_HEX, sizeof(int), (void*)&pddf_data.dev_addr, NULL); PDDF_DATA_ATTR(error, S_IRUGO, show_error_code, NULL, PDDF_INT_DEC, sizeof(int), (void*)&pddf_data.error, (void*)&pddf_data.errstr); static struct attribute *pddf_clients_data_attributes[] = { &attr_i2c_type.dev_attr.attr, &attr_i2c_name.dev_attr.attr, &attr_parent_bus.dev_attr.attr, &attr_dev_type.dev_attr.attr, &attr_dev_id.dev_attr.attr, &attr_dev_addr.dev_attr.attr, &attr_error.dev_attr.attr, NULL }; struct attribute_group pddf_clients_data_group = { .attrs = pddf_clients_data_attributes, }; EXPORT_SYMBOL(pddf_clients_data_group); PDDF_DATA_ATTR(showall, S_IRUGO, show_all_devices, NULL, PDDF_INT_DEC, sizeof(int), (void *)&showall, NULL); static struct attribute *pddf_allclients_data_attributes[] = { &attr_showall.dev_attr.attr, NULL }; struct attribute_group pddf_allclients_data_group = { .attrs = pddf_allclients_data_attributes, }; void set_attr_data(void * ptr) { pddf_data.data=ptr; } ssize_t show_all_devices(struct device *dev, struct device_attribute *da, char *buf) { int ret = 0; PDDF_ATTR *pptr = (PDDF_ATTR *)da; int *ptr = (int *)pptr->addr; traverse_device_table(); ret = sprintf(buf, "Total Devices: %d\n", *ptr); return ret; } ssize_t show_error_code(struct device *dev, struct device_attribute *da, char *buf) { int ret = 0; PDDF_ATTR *pptr = (PDDF_ATTR *)da; NEW_DEV_ATTR *ptr = ( NEW_DEV_ATTR *)pptr->addr; ret = sprintf(buf, "0x%x:%s\n", (ptr->error), ptr->errstr); return ret; } void set_error_code(int ecode, char *estr) { pddf_data.error = ecode; strcpy(pddf_data.errstr, estr); return; } EXPORT_SYMBOL(set_error_code); ssize_t show_pddf_data(struct device *dev, struct device_attribute *da, char *buf) { int ret = 0; PDDF_ATTR *ptr = (PDDF_ATTR *)da; /*pddf_dbg(KERN_ERR "[ READ ] DATA ATTR PTR TYPE:%d, ADDR=%p\n", ptr->type, ptr->addr);*/ switch(ptr->type) { case PDDF_CHAR: ret = sprintf(buf, "%s\n", ptr->addr); break; case PDDF_UCHAR: ret = sprintf(buf, "%d\n", *(unsigned char*)(ptr->addr)); break; case PDDF_INT_DEC: ret = sprintf(buf, "%d\n", *(int*)(ptr->addr)); break; case PDDF_INT_HEX: ret = sprintf(buf, "0x%x\n", *(int*)(ptr->addr)); break; case PDDF_USHORT: ret = sprintf(buf, "0x%x\n", *(unsigned short *)(ptr->addr)); break; case PDDF_UINT32: ret = sprintf(buf, "0x%x\n", *(uint32_t *)(ptr->addr)); break; default: break; } return ret; } EXPORT_SYMBOL(show_pddf_data); ssize_t store_pddf_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { int ret = 0, num = 0; PDDF_ATTR *ptr = (PDDF_ATTR *)da; switch(ptr->type) { case PDDF_CHAR: strncpy(ptr->addr, buf, strlen(buf)-1); // to discard newline char form buf ptr->addr[strlen(buf)-1] = '\0'; break; case PDDF_UCHAR: ret = kstrtoint(buf,10,&num); if (ret==0) *(unsigned char *)(ptr->addr) = (unsigned char)num; break; case PDDF_INT_DEC: ret = kstrtoint(buf,10,&num); if (ret==0) *(int *)(ptr->addr) = num; break; case PDDF_INT_HEX: ret = kstrtoint(buf,16,&num); if (ret==0) *(int *)(ptr->addr) = num; break; case PDDF_USHORT: ret = kstrtoint(buf,16,&num); if (ret==0) *(unsigned short *)(ptr->addr) = (unsigned short)num; break; case PDDF_UINT32: ret = kstrtoint(buf,16,&num); if (ret==0) *(uint32_t *)(ptr->addr) = (uint32_t)num; break; default: break; } return count; } EXPORT_SYMBOL(store_pddf_data); DEFINE_HASHTABLE(htable, 8); int get_hash(char *name) { int i=0; int hash=0; for(i=0; iname, name); hdev->data = ptr; pddf_dbg(CLIENT, KERN_ERR "%s: Adding ptr 0x%p to the hash table\n", __FUNCTION__, ptr); hash_add(htable, &hdev->node, get_hash(hdev->name)); } EXPORT_SYMBOL(add_device_table); void* get_device_table(char *name) { PDEVICE *dev=NULL; int i=0; hash_for_each(htable, i, dev, node) { if(strcmp(dev->name, name)==0) { return (void *)dev->data; } } return NULL; } EXPORT_SYMBOL(get_device_table); void delete_device_table(char *name) { PDEVICE *dev=NULL; int i=0; hash_for_each(htable, i, dev, node) { if(strcmp(dev->name, name)==0) { pddf_dbg(CLIENT, KERN_ERR "found entry to delete: %s 0x%p\n", dev->name, dev->data); hash_del(&(dev->node)); } } return; } EXPORT_SYMBOL(delete_device_table); void traverse_device_table(void ) { PDEVICE *dev=NULL; int i=0; hash_for_each(htable, i, dev, node) { pddf_dbg(CLIENT, KERN_ERR "Entry[%d]: %s : 0x%p\n", i, dev->name, dev->data); } showall = i; } EXPORT_SYMBOL(traverse_device_table); struct kobject *device_kobj; static struct kobject *pddf_kobj; struct kobject *get_device_i2c_kobj(void) { return device_kobj; } EXPORT_SYMBOL(get_device_i2c_kobj); int __init pddf_data_init(void) { int ret = 0; pddf_dbg(CLIENT, "PDDF_DATA MODULE.. init\n"); pddf_kobj = kobject_create_and_add("pddf", kernel_kobj); if(!pddf_kobj) { return -ENOMEM; } device_kobj = kobject_create_and_add("devices", pddf_kobj); if(!device_kobj) { return -ENOMEM; } init_device_table(); ret = sysfs_create_group(device_kobj, &pddf_allclients_data_group); if (ret) { kobject_put(device_kobj); return ret; } pddf_dbg(CLIENT, "CREATED PDDF ALLCLIENTS CREATION SYSFS GROUP\n"); return ret; } void __exit pddf_data_exit(void) { pddf_dbg(CLIENT, "PDDF_DATA MODULE.. exit\n"); sysfs_remove_group(device_kobj, &pddf_allclients_data_group); kobject_put(device_kobj); kobject_put(pddf_kobj); pddf_dbg(CLIENT, KERN_ERR "%s: Removed the kernle object for 'pddf' and 'device' \n", __FUNCTION__); return; } module_init(pddf_data_init); module_exit(pddf_data_exit); MODULE_AUTHOR("Broadcom"); MODULE_DESCRIPTION("pddf data"); MODULE_LICENSE("GPL");