• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * osal_cdev.c
3  *
4  * osal driver
5  *
6  * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
7  *
8  * This software is licensed under the terms of the GNU General Public
9  * License version 2, as published by the Free Software Foundation, and
10  * may be copied, distributed, and modified under those terms.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  */
18 #include <linux/cdev.h>
19 #include <linux/device.h>
20 #include <linux/fs.h>
21 #include "osal_cdev.h"
22 #include "hdf_log.h"
23 #include "osal_file.h"
24 #include "osal_mem.h"
25 #include "osal_uaccess.h"
26 
27 #define HDF_LOG_TAG osal_cdev
28 
29 #define HDF_MAJOR 231
30 #define HDF_MINOR_START 0
31 #define HDF_MAX_CHAR_DEVICES 1024
32 
33 static DEFINE_IDA(hdf_vnode_ids);
34 
35 struct OsalCdev {
36     struct device dev;
37     struct cdev cdev;
38     struct file_operations fops;
39     const struct OsalCdevOps* opsImpl;
40     void* priv;
41 };
42 
StringRfindChar(const char * str,char chr)43 static const char* StringRfindChar(const char* str, char chr)
44 {
45     const char* p = NULL;
46     if (str == NULL) {
47         return NULL;
48     }
49 
50     p = str + strlen(str);
51     while (p >= str) {
52         if (*p == chr) {
53             return p;
54         }
55         p--;
56     }
57 
58     return NULL;
59 }
60 
hdfDevnode(struct device * dev,umode_t * mode)61 static char* hdfDevnode(struct device* dev, umode_t* mode)
62 {
63     (void)mode;
64     return kasprintf(GFP_KERNEL, "hdf/%s", dev_name(dev));
65 }
66 
67 static bool g_hdfClassInitted = false;
68 
69 struct class g_hdfClass = {
70     .name = "hdf",
71     .devnode = hdfDevnode,
72 };
73 
74 static dev_t g_hdfDevt = 0;
75 
HdfClassInit(void)76 static int HdfClassInit(void)
77 {
78     int ret;
79 
80     if (g_hdfClassInitted) {
81         return HDF_SUCCESS;
82     }
83 
84     ret = class_register(&g_hdfClass);
85     if (ret) {
86         HDF_LOGE("failed to register hdf class");
87         return ret;
88     }
89     HDF_LOGI("register hdf class success");
90 
91     ret = alloc_chrdev_region(&g_hdfDevt, HDF_MINOR_START, HDF_MAX_CHAR_DEVICES, "hdf");
92     if (ret) {
93         HDF_LOGE("failed to alloc hdf char major");
94         class_unregister(&g_hdfClass);
95         return ret;
96     }
97 
98     g_hdfClassInitted = true;
99     return HDF_SUCCESS;
100 }
101 
HdfVnodeDevRelease(struct device * dev)102 static void HdfVnodeDevRelease(struct device* dev)
103 {
104     (void)dev;
105 }
106 
RegisterDev(struct OsalCdev * cdev,const char * devName)107 static int RegisterDev(struct OsalCdev* cdev, const char* devName)
108 {
109     int devMinor;
110     int ret;
111 
112     ret = HdfClassInit();
113     if (ret != HDF_SUCCESS) {
114         return ret;
115     }
116 
117     devMinor = ida_simple_get(&hdf_vnode_ids, HDF_MINOR_START, HDF_MAX_CHAR_DEVICES, GFP_KERNEL);
118     if (devMinor < 0) {
119         HDF_LOGE("failed to get hdf dev minor");
120         return HDF_DEV_ERR_NO_DEVICE;
121     }
122 
123     dev_set_name(&cdev->dev, "%s", devName);
124     cdev->dev.devt = MKDEV(MAJOR(g_hdfDevt), devMinor);
125     cdev->dev.class = &g_hdfClass;
126     cdev->dev.parent = NULL;
127     cdev->dev.release = HdfVnodeDevRelease;
128     device_initialize(&cdev->dev);
129 
130     cdev_init(&cdev->cdev, &cdev->fops);
131 
132     ret = cdev_add(&cdev->cdev, cdev->dev.devt, 1);
133     if (ret) {
134         ida_simple_remove(&hdf_vnode_ids, devMinor);
135         HDF_LOGE("failed to add hdf cdev(%s)\n", devName);
136         return ret;
137     }
138 
139     ret = device_add(&cdev->dev);
140     if (ret) {
141         HDF_LOGE("device(%s) add failed\n", devName);
142         cdev_del(&cdev->cdev);
143         ida_simple_remove(&hdf_vnode_ids, devMinor);
144         return ret;
145     }
146 
147     HDF_LOGI("add cdev %s success\n", devName);
148     return 0;
149 }
150 
OsalCdevSeek(struct file * filep,loff_t offset,int whence)151 static loff_t OsalCdevSeek(struct file* filep, loff_t offset, int whence)
152 {
153     struct OsalCdev* dev = container_of(filep->f_inode->i_cdev, struct OsalCdev, cdev);
154     return dev->opsImpl->seek(filep, offset, whence);
155 }
156 
OsalCdevRead(struct file * filep,char __user * buf,size_t buflen,loff_t * offset)157 static ssize_t OsalCdevRead(struct file* filep, char __user* buf, size_t buflen, loff_t* offset)
158 {
159     struct OsalCdev* dev = container_of(filep->f_inode->i_cdev, struct OsalCdev, cdev);
160     return dev->opsImpl->read(filep, buf, buflen, offset);
161 }
162 
OsalCdevWrite(struct file * filep,const char __user * buf,size_t buflen,loff_t * offset)163 static ssize_t OsalCdevWrite(struct file* filep, const char __user* buf, size_t buflen, loff_t* offset)
164 {
165     struct OsalCdev* dev = container_of(filep->f_inode->i_cdev, struct OsalCdev, cdev);
166     return dev->opsImpl->write(filep, buf, buflen, offset);
167 }
168 
OsalCdevPoll(struct file * filep,struct poll_table_struct * pollTable)169 static unsigned int OsalCdevPoll(struct file* filep, struct poll_table_struct* pollTable)
170 {
171     struct OsalCdev* dev = container_of(filep->f_inode->i_cdev, struct OsalCdev, cdev);
172     return dev->opsImpl->poll(filep, pollTable);
173 }
174 
OsalCdevIoctl(struct file * filep,unsigned int cmd,unsigned long arg)175 static long OsalCdevIoctl(struct file* filep, unsigned int cmd, unsigned long arg)
176 {
177     struct OsalCdev* dev = container_of(filep->f_inode->i_cdev, struct OsalCdev, cdev);
178     return dev->opsImpl->ioctl(filep, cmd, arg);
179 }
180 
OsalCdevOpen(struct inode * inode,struct file * filep)181 static int OsalCdevOpen(struct inode* inode, struct file* filep)
182 {
183     struct OsalCdev* dev = container_of(inode->i_cdev, struct OsalCdev, cdev);
184     return dev->opsImpl->open(dev, filep);
185 }
186 
OsalCdevRelease(struct inode * inode,struct file * filep)187 static int OsalCdevRelease(struct inode* inode, struct file* filep)
188 {
189     struct OsalCdev* dev = container_of(inode->i_cdev, struct OsalCdev, cdev);
190     return dev->opsImpl->release(dev, filep);
191 }
192 
AssignFileOps(struct file_operations * fops,const struct OsalCdevOps * src)193 static void AssignFileOps(struct file_operations* fops, const struct OsalCdevOps* src)
194 {
195     fops->llseek = src->seek != NULL ? OsalCdevSeek : NULL;
196     fops->read = src->read != NULL ? OsalCdevRead : NULL;
197     fops->write = src->write != NULL ? OsalCdevWrite : NULL;
198     fops->poll = src->poll != NULL ? OsalCdevPoll : NULL;
199     fops->unlocked_ioctl = src->ioctl != NULL ? OsalCdevIoctl : NULL;
200     fops->open = src->open != NULL ? OsalCdevOpen : NULL;
201     fops->release = src->release != NULL ? OsalCdevRelease : NULL;
202 #ifdef CONFIG_COMPAT
203     fops->compat_ioctl = src->ioctl != NULL ? OsalCdevIoctl : NULL;
204 #endif
205 }
206 
OsalAllocCdev(const struct OsalCdevOps * fops)207 struct OsalCdev* OsalAllocCdev(const struct OsalCdevOps* fops)
208 {
209     struct OsalCdev* cdev = OsalMemCalloc(sizeof(struct OsalCdev));
210     if (cdev == NULL) {
211         return NULL;
212     }
213 
214     AssignFileOps(&cdev->fops, fops);
215     cdev->opsImpl = fops;
216 
217     return cdev;
218 }
219 
OsalRegisterCdev(struct OsalCdev * cdev,const char * name,unsigned int mode,void * priv)220 int OsalRegisterCdev(struct OsalCdev* cdev, const char* name, unsigned int mode, void* priv)
221 {
222     const char* lastSlash;
223     int ret;
224     (void)mode;
225     if (cdev == NULL || name == NULL) {
226         return HDF_ERR_INVALID_PARAM;
227     }
228 
229     lastSlash = StringRfindChar(name, '/');
230     ret = RegisterDev(cdev, (lastSlash == NULL) ? name : (lastSlash + 1));
231     if (ret == HDF_SUCCESS) {
232         cdev->priv = priv;
233     }
234 
235     return ret;
236 }
237 
OsalUnregisterCdev(struct OsalCdev * cdev)238 void OsalUnregisterCdev(struct OsalCdev* cdev)
239 {
240     if (cdev == NULL) {
241         return;
242     }
243     device_del(&cdev->dev);
244     cdev_del(&cdev->cdev);
245     ida_simple_remove(&hdf_vnode_ids, MINOR(cdev->dev.devt));
246 }
247 
OsalFreeCdev(struct OsalCdev * cdev)248 void OsalFreeCdev(struct OsalCdev* cdev)
249 {
250     if (cdev != NULL) {
251         OsalMemFree(cdev);
252     }
253 }
254 
OsalGetCdevPriv(struct OsalCdev * cdev)255 void* OsalGetCdevPriv(struct OsalCdev* cdev)
256 {
257     return cdev != NULL ? cdev->priv : NULL;
258 }
259 
OsalSetFilePriv(struct file * filep,void * priv)260 void OsalSetFilePriv(struct file* filep, void* priv)
261 {
262     if (filep != NULL) {
263         filep->private_data = priv;
264     }
265 }
OsalGetFilePriv(struct file * filep)266 void* OsalGetFilePriv(struct file* filep)
267 {
268     return filep != NULL ? filep->private_data : NULL;
269 }
270