blob: ec336746b1e1edf9754d3f8ea9d36f03ccb12f88 [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30#include <linux/module.h>
31#include <linux/moduleparam.h>
32#include <linux/fs.h> /* everything... */
33#include <linux/fcntl.h> /* O_ACCMODE */
34#include <linux/aio.h>
35#include <linux/cdev.h>
36#include <asm/uaccess.h>
37#include <linux/vmalloc.h>
38
39#include <bcm_dev_log_task.h>
40#include <bcmolt_dev_log_kernel.h>
41#include <bcmolt_dev_log_ioctl.h>
42
43int dev_log_chrdev_major = 215; /* Should comply the value in targets/makeDev */
44module_param(dev_log_chrdev_major, int, S_IRUSR | S_IRGRP | S_IWGRP);
45MODULE_PARM_DESC(dev_log_chrdev_major, "devlog_major");
46
47int dev_log_buffer_size_kb = 2048; /* Buffer size in kB */
48module_param(dev_log_buffer_size_kb, int, S_IRUSR | S_IRGRP | S_IWGRP);
49MODULE_PARM_DESC(dev_log_buffer_size_kb, "devlog_bufsize_kb");
50
51int dev_log_queue_size = 256; /* Queue size */
52module_param(dev_log_queue_size, int, S_IRUSR | S_IRGRP | S_IWGRP);
53MODULE_PARM_DESC(dev_log_queue_size, "devlog_qsize");
54
55static bcmos_mutex dev_log_lock;
56static int dev_log_num_users;
57static char *dev_log_buffer;
58
59/*
60 * Open and close
61 */
62static int dev_log_chrdev_open(struct inode *inode, struct file *filp)
63{
64 BCMOS_TRACE_DEBUG("\n");
65 bcmos_mutex_lock(&dev_log_lock);
66 if (dev_log_num_users >= 1)
67 {
68 bcmos_mutex_unlock(&dev_log_lock);
69 return -EBUSY;
70 }
71 ++dev_log_num_users;
72 bcmos_mutex_unlock(&dev_log_lock);
73 return 0;
74}
75
76
77static int dev_log_chrdev_release(struct inode *inode, struct file *filp)
78{
79 BCMOS_TRACE_DEBUG("\n");
80 --dev_log_num_users;
81 return 0;
82}
83
84static ssize_t dev_log_chrdev_write(struct file *filp, const char __user *buf,
85 size_t count, loff_t *f_pos)
86{
87 return -EOPNOTSUPP;
88}
89
90static long dev_log_chrdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
91{
92 int linux_rc = 0;
93 bcmos_errno rc;
94 dev_log_io_param io_param;
95 dev_log_id id;
96
97 /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */
98 if (_IOC_TYPE(cmd) != DEV_LOG_CHRDEV_IOC_MAGIC)
99 return -ENOTTY;
100
101 /*
102 * the type is a bitmask, and VERIFY_WRITE catches R/W
103 * transfers. Note that the type is user-oriented, while
104 * verify_area is kernel-oriented, so the concept of "read" and
105 * "write" is reversed
106 */
107 if (_IOC_DIR(cmd) & _IOC_READ)
108 linux_rc = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
109 else if (_IOC_DIR(cmd) & _IOC_WRITE)
110 linux_rc = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
111 if (linux_rc)
112 return -EFAULT;
113
114 switch (cmd)
115 {
116
117 case DEV_LOG_CHRDEV_DB_READ:
118 {
119 int i;
120
121 for (i = 0; i < bcm_dev_log_get_num_of_entries(); i++)
122 {
123 dev_log_id_parm id_parm = {};
124
125 id = bcm_dev_log_id_get_by_index(i);
126 if (id == DEV_LOG_INVALID_ID)
127 {
128 break;
129 }
130 bcm_dev_log_id_get(id, &id_parm);
131 io_param.db_read.ids[i].index = i;
132 strncpy(io_param.db_read.ids[i].name, id_parm.name, sizeof(io_param.db_read.ids[i].name));
133 io_param.db_read.ids[i].default_type = id_parm.default_log_type;
134 io_param.db_read.ids[i].default_level = id_parm.default_log_level;
135 /* Reset to defaults to keep in sync with the user space */
136 bcm_dev_log_id_set_levels_and_type_to_default(id);
137 }
138 io_param.db_read.num_ids = i;
139 linux_rc = copy_to_user((char *)arg, (char *)&io_param.db_read, sizeof(io_param.db_read));
140 }
141 break;
142
143 case DEV_LOG_CHRDEV_LEVEL_SET:
144 {
145 linux_rc = copy_from_user((char *)&io_param.level_set, (char *)arg, sizeof(io_param.level_set));
146 if (linux_rc < 0)
147 break;
148 id = bcm_dev_log_id_get_by_index(io_param.level_set.index);
149 if (id == DEV_LOG_INVALID_ID)
150 {
151 BCMOS_TRACE_ERR("log_id index %u is invalid\n", io_param.level_set.index);
152 }
153 rc = bcm_dev_log_id_set_level(id, io_param.level_set.level_print, io_param.level_set.level_save);
154 if (rc)
155 {
156 BCMOS_TRACE_ERR("bcm_dev_log_id_set_level(%ld,%d,%d)->%s\n",
157 (long)id, (int)io_param.level_set.level_print, (int)io_param.level_set.level_save, bcmos_strerror(rc));
158 linux_rc = -EINVAL;
159 }
160 }
161 break;
162
163 case DEV_LOG_CHRDEV_TYPE_SET:
164 {
165 linux_rc = copy_from_user((char *)&io_param.type_set, (char *)arg, sizeof(io_param.type_set));
166 if (linux_rc < 0)
167 break;
168 /* Map index to log id */
169 id = bcm_dev_log_id_get_by_index(io_param.type_set.index);
170 if (id == DEV_LOG_INVALID_ID)
171 {
172 BCMOS_TRACE_ERR("log_id index %u is invalid\n", io_param.type_set.index);
173 }
174 rc = bcm_dev_log_id_set_type(id, io_param.type_set.type);
175 if (rc)
176 {
177 BCMOS_TRACE_ERR("bcm_dev_log_id_set_type(%ld,%d)->%s\n",
178 (long)id, (int)io_param.type_set.type, bcmos_strerror(rc));
179 linux_rc = -EINVAL;
180 }
181 }
182 break;
183
184 case DEV_LOG_CHRDEV_MSG_READ:
185 {
186 uint32_t buf_len = sizeof(io_param.msg_read.msg);
187 bcm_dev_log_file *log_file = bcm_dev_log_file_get(0);
188
189 if (!log_file)
190 {
191 linux_rc = -EINVAL;
192 break;
193 }
194
195 linux_rc = copy_from_user((char *)&io_param.msg_read, (char *)arg, sizeof(io_param.msg_read.offset));
196 if (linux_rc < 0)
197 break;
198
199 rc = bcm_dev_log_file_read(log_file, &io_param.msg_read.offset, io_param.msg_read.msg, buf_len);
200 if (rc <= 0)
201 {
202 if (rc == 0)
203 linux_rc = -EAGAIN;
204 else
205 linux_rc = -EINVAL;
206 break;
207 }
208 linux_rc = copy_to_user((char *)arg, (char *)&io_param.msg_read,
209 offsetof(dev_log_io_param, msg_read.msg) + rc);
210 }
211 break;
212
213 default: /* redundant, as cmd was checked against MAXNR */
214 rc = -ENOTTY;
215 break;
216 }
217
218 return linux_rc;
219}
220
221/*
222 * The fops
223 */
224static struct file_operations dev_log_chrdev_fops =
225{
226 .owner = THIS_MODULE,
227 .open = dev_log_chrdev_open,
228 .release = dev_log_chrdev_release,
229 .write = dev_log_chrdev_write,
230 .unlocked_ioctl = dev_log_chrdev_ioctl
231};
232
233static struct cdev dev_log_chrdev_cdev;
234static int is_chrdev_reg, is_cdev_add;
235
236static bcmos_errno dev_log_chrdev_init(void)
237{
238 dev_t dev = MKDEV(dev_log_chrdev_major, 0);
239 int linux_rc;
240
241 is_chrdev_reg = 0;
242 is_cdev_add = 0;
243 /*
244 * Register your major, and accept a dynamic number.
245 */
246 if (!dev_log_chrdev_major)
247 return -1;
248 linux_rc = register_chrdev_region(dev, 0, "dev_log");
249 if (linux_rc < 0)
250 {
251 BCMOS_TRACE_RETURN(BCM_ERR_IO, "register_chrdev_region()->%d\n", linux_rc);
252 }
253 is_chrdev_reg = 1;
254
255 cdev_init(&dev_log_chrdev_cdev, &dev_log_chrdev_fops);
256 linux_rc = cdev_add(&dev_log_chrdev_cdev, dev, 1);
257 if (linux_rc < 0)
258 {
259 BCMOS_TRACE_RETURN(BCM_ERR_IO, "cdev_add()->%d\n", linux_rc);
260 }
261 is_cdev_add = 1;
262
263 return BCM_ERR_OK;
264}
265
266static void dev_log_chrdev_exit(void)
267{
268 if (is_cdev_add)
269 cdev_del(&dev_log_chrdev_cdev);
270 if (is_chrdev_reg)
271 unregister_chrdev_region(MKDEV(dev_log_chrdev_major, 0), 1);
272}
273
274static int dev_log_time_to_str_cb(uint32_t time_stamp, char *time_str, int time_str_size)
275{
276 return snprintf(time_str, time_str_size, "%u", time_stamp / 1000); /* Convert from usec to msec. */
277}
278
279/** Initialize dev_log linux kernel support */
280bcmos_errno bcm_dev_log_linux_init(void)
281{
282 void *addresses[1];
283 uint32_t sizes[1] = { dev_log_buffer_size_kb * 1024 };
284 uint32_t flags[1] = { BCM_DEV_LOG_FILE_FLAG_WRAP_AROUND };
285 bcmos_errno rc;
286
287 if (!dev_log_queue_size || !dev_log_buffer_size_kb)
288 return BCM_ERR_PARM;
289
290 rc = dev_log_chrdev_init();
291 if (rc)
292 return rc;
293
294 /* Initialize logger */
295 dev_log_buffer = vmalloc(dev_log_buffer_size_kb * 1024);
296 if (!dev_log_buffer)
297 {
298 dev_log_chrdev_exit();
299 BCMOS_TRACE_ERR("Can't allocate dev_log buffer (%d bytes)\n", dev_log_buffer_size_kb * 1024);
300 return BCM_ERR_NOMEM;
301 }
302 addresses[0] = dev_log_buffer;
303 rc = bcm_dev_log_init_default_logger(addresses, sizes, flags, BCM_SIZEOFARRAY(addresses), 0x4000, TASK_PRIORITY_DEV_LOG, dev_log_queue_size);
304 if (rc)
305 {
306 dev_log_chrdev_exit();
307 vfree(dev_log_buffer);
308 BCMOS_TRACE_ERR("Failed to create logger. Error %s\n", bcmos_strerror(rc));
309 return rc;
310 }
311
312 bcm_dev_log_set_time_to_str_cb(dev_log_time_to_str_cb);
313
314 bcmos_mutex_create(&dev_log_lock, 0, NULL);
315
316 return BCM_ERR_OK;
317}
318
319/** Clean-up dev_log linux kernel support */
320void bcm_dev_log_linux_exit(void)
321{
322 dev_log_chrdev_exit();
323 if (dev_log_buffer)
324 {
325 vfree(dev_log_buffer);
326 }
327 bcmos_mutex_destroy(&dev_log_lock);
328}