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