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