proc.c
1 /* 2 * Copyright (C) 2005 Jan Kiszka <jan.kiszka@web.de>. 3 * Copyright (C) 2005 Joerg Langenberg <joerg.langenberg@gmx.net>. 4 * 5 * adapted to RTAI by Paolo Mantegazza <mantegazza@aero.polimi.it> 6 * 7 * RTAI is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * RTAI is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with RTAI; if not, write to the Free Software Foundation, 19 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 */ 21 22 23 #include "rtdm/internal.h" 24 #include <rtdm/vfile.h> 25 26 struct xnvfile_directory rtdm_vfroot; /* /proc/rtai/rtdm */ 27 28 struct vfile_device_data { 29 int h; 30 int hmax; 31 struct list_head *devmap; 32 struct list_head *curr; 33 }; 34 35 static int get_nrt_lock(struct xnvfile *vfile) 36 { 37 return down_interruptible(&nrt_dev_lock) ? -ERESTARTSYS : 0; 38 } 39 40 static void put_nrt_lock(struct xnvfile *vfile) 41 { 42 up(&nrt_dev_lock); 43 } 44 45 static struct xnvfile_lock_ops lockops = { 46 .get = get_nrt_lock, 47 .put = put_nrt_lock, 48 }; 49 50 static struct list_head *next_devlist(struct vfile_device_data *priv) 51 { 52 struct list_head *head; 53 54 while (priv->h < priv->hmax) { 55 head = priv->devmap + priv->h; 56 if (!list_empty(head)) 57 return head; 58 priv->h++; 59 } 60 61 return NULL; 62 } 63 64 static void *next_dev(struct xnvfile_regular_iterator *it) 65 { 66 struct vfile_device_data *priv = xnvfile_iterator_priv(it); 67 struct list_head *next; 68 69 next = priv->curr->next; 70 seek: 71 if (next == priv->devmap + priv->h) { 72 /* Done with the current hash slot, let's progress. */ 73 if (priv->h >= priv->hmax) { 74 next = NULL; /* all done. */ 75 goto out; 76 } 77 78 priv->h++; 79 next = next_devlist(priv); 80 if (next) { 81 next = next->next; /* skip head. */ 82 goto seek; 83 } 84 } 85 out: 86 priv->curr = next; 87 88 return next; 89 } 90 91 static void *named_begin(struct xnvfile_regular_iterator *it) 92 { 93 struct vfile_device_data *priv = xnvfile_iterator_priv(it); 94 struct list_head *devlist; 95 loff_t pos = 0; 96 97 priv->devmap = rtdm_named_devices; 98 priv->hmax = devname_hashtab_size; 99 priv->h = 0; 100 101 devlist = next_devlist(priv); 102 if (devlist == NULL) 103 return NULL; /* All devlists empty. */ 104 105 priv->curr = devlist->next; /* Skip head. */ 106 107 /* 108 * priv->curr now points to the first device; advance to the requested 109 * position from there. 110 */ 111 while (priv->curr && pos++ < it->pos) 112 priv->curr = next_dev(it); 113 114 if (pos == 1) 115 /* Output the header once, only if some device follows. */ 116 xnvfile_puts(it, "Hash\tName\t\t\t\tDriver\t\t/proc\n"); 117 118 return priv->curr; 119 } 120 121 static int named_show(struct xnvfile_regular_iterator *it, void *data) 122 { 123 struct vfile_device_data *priv = xnvfile_iterator_priv(it); 124 struct list_head *curr = data; 125 struct rtdm_device *device; 126 127 device = list_entry(curr, struct rtdm_device, reserved.entry); 128 xnvfile_printf(it, "%02X\t%-31s\t%-15s\t%s\n", 129 priv->h, device->device_name, 130 device->driver_name, 131 device->proc_name); 132 133 return 0; 134 } 135 136 static struct xnvfile_regular_ops named_vfile_ops = { 137 .begin = named_begin, 138 .next = next_dev, 139 .show = named_show, 140 }; 141 142 static struct xnvfile_regular named_vfile = { 143 .privsz = sizeof(struct vfile_device_data), 144 .ops = &named_vfile_ops, 145 .entry = { .lockops = &lockops } 146 }; 147 148 static void *proto_begin(struct xnvfile_regular_iterator *it) 149 { 150 151 struct vfile_device_data *priv = xnvfile_iterator_priv(it); 152 struct list_head *devlist; 153 loff_t pos = 0; 154 155 priv->devmap = rtdm_protocol_devices; 156 priv->hmax = protocol_hashtab_size; 157 priv->h = 0; 158 159 devlist = next_devlist(priv); 160 if (devlist == NULL) 161 return NULL; /* All devlists empty. */ 162 163 priv->curr = devlist->next; /* Skip head. */ 164 165 /* 166 * priv->curr now points to the first device; advance to the requested 167 * position from there. 168 */ 169 while (priv->curr && pos++ < it->pos) 170 priv->curr = next_dev(it); 171 172 if (pos == 1) 173 /* Output the header once, only if some device follows. */ 174 xnvfile_puts(it, "Hash\tName\t\t\t\tDriver\t\t/proc\n"); 175 176 return priv->curr; 177 } 178 179 static int proto_show(struct xnvfile_regular_iterator *it, void *data) 180 { 181 struct vfile_device_data *priv = xnvfile_iterator_priv(it); 182 struct list_head *curr = data; 183 struct rtdm_device *device; 184 char pnum[32]; 185 186 device = list_entry(curr, struct rtdm_device, reserved.entry); 187 188 snprintf(pnum, sizeof(pnum), "%u:%u", 189 device->protocol_family, device->socket_type); 190 191 xnvfile_printf(it, "%02X\t%-31s\t%-15s\t%s\n", 192 priv->h, 193 pnum, device->driver_name, 194 device->proc_name); 195 return 0; 196 } 197 198 static struct xnvfile_regular_ops proto_vfile_ops = { 199 .begin = proto_begin, 200 .next = next_dev, 201 .show = proto_show, 202 }; 203 204 static struct xnvfile_regular proto_vfile = { 205 .privsz = sizeof(struct vfile_device_data), 206 .ops = &proto_vfile_ops, 207 .entry = { .lockops = &lockops } 208 }; 209 210 static void *openfd_begin(struct xnvfile_regular_iterator *it) 211 { 212 if (it->pos == 0) 213 return VFILE_SEQ_START; 214 215 return it->pos <= RTDM_FD_MAX ? it : NULL; 216 } 217 218 static void *openfd_next(struct xnvfile_regular_iterator *it) 219 { 220 if (it->pos > RTDM_FD_MAX) 221 return NULL; 222 223 return it; 224 } 225 226 static int openfd_show(struct xnvfile_regular_iterator *it, void *data) 227 { 228 struct rtdm_dev_context *context; 229 struct rtdm_device *device; 230 struct rtdm_process owner; 231 int close_lock_count, fd; 232 spl_t s; 233 234 if (data == NULL) { 235 xnvfile_puts(it, "Index\tLocked\tDevice\t\t\t\tOwner [PID]\n"); 236 return 0; 237 } 238 239 fd = (int)it->pos - 1; 240 241 xnlock_get_irqsave(&rt_fildes_lock, s); 242 243 context = fildes_table[fd].context; 244 if (context == NULL) { 245 xnlock_put_irqrestore(&rt_fildes_lock, s); 246 return VFILE_SEQ_SKIP; 247 } 248 249 close_lock_count = atomic_read(&context->close_lock_count); 250 device = context->device; 251 if (context->reserved.owner) 252 memcpy(&owner, context->reserved.owner, sizeof(owner)); 253 else { 254 strcpy(owner.name, "<kernel>"); 255 owner.pid = -1; 256 } 257 258 xnlock_put_irqrestore(&rt_fildes_lock, s); 259 260 xnvfile_printf(it, "%d\t%d\t%-31s %s [%d]\n", fd, 261 close_lock_count, 262 (device->device_flags & RTDM_NAMED_DEVICE) ? 263 device->device_name : device->proc_name, 264 owner.name, owner.pid); 265 return 0; 266 } 267 268 static ssize_t openfd_store(struct xnvfile_input *input) 269 { 270 ssize_t ret, cret; 271 long val; 272 273 ret = xnvfile_get_integer(input, &val); 274 if (ret < 0) 275 return ret; 276 277 cret = __rt_dev_close(current, (int)val); 278 if (cret < 0) 279 return cret; 280 281 return ret; 282 } 283 284 static struct xnvfile_regular_ops openfd_vfile_ops = { 285 .begin = openfd_begin, 286 .next = openfd_next, 287 .show = openfd_show, 288 .store = openfd_store, 289 }; 290 291 static struct xnvfile_regular openfd_vfile = { 292 .ops = &openfd_vfile_ops, 293 .entry = { .lockops = &lockops } 294 }; 295 296 static int allfd_vfile_show(struct xnvfile_regular_iterator *it, void *data) 297 { 298 xnvfile_printf(it, "total=%d:open=%d:free=%d\n", RTDM_FD_MAX, 299 open_fildes, RTDM_FD_MAX - open_fildes); 300 return 0; 301 } 302 303 static struct xnvfile_regular_ops allfd_vfile_ops = { 304 .show = allfd_vfile_show, 305 }; 306 307 static struct xnvfile_regular allfd_vfile = { 308 .ops = &allfd_vfile_ops, 309 }; 310 311 static int devinfo_vfile_show(struct xnvfile_regular_iterator *it, void *data) 312 { 313 struct rtdm_device *device; 314 int i; 315 316 if (down_interruptible(&nrt_dev_lock)) 317 return -ERESTARTSYS; 318 319 /* 320 * As the device may have disappeared while the handler was called, 321 * first match the pointer against registered devices. 322 */ 323 for (i = 0; i < devname_hashtab_size; i++) 324 list_for_each_entry(device, &rtdm_named_devices[i], 325 reserved.entry) 326 if (device == xnvfile_priv(it->vfile)) 327 goto found; 328 329 for (i = 0; i < protocol_hashtab_size; i++) 330 list_for_each_entry(device, &rtdm_protocol_devices[i], 331 reserved.entry) 332 if (device == xnvfile_priv(it->vfile)) 333 goto found; 334 335 up(&nrt_dev_lock); 336 return -ENODEV; 337 338 found: 339 xnvfile_printf(it, "driver:\t\t%s\nversion:\t%d.%d.%d\n", 340 device->driver_name, 341 RTDM_DRIVER_MAJOR_VER(device->driver_version), 342 RTDM_DRIVER_MINOR_VER(device->driver_version), 343 RTDM_DRIVER_PATCH_VER(device->driver_version)); 344 345 xnvfile_printf(it, "peripheral:\t%s\nprovider:\t%s\n", 346 device->peripheral_name, device->provider_name); 347 348 xnvfile_printf(it, "class:\t\t%d\nsub-class:\t%d\n", 349 device->device_class, device->device_sub_class); 350 351 xnvfile_printf(it, "flags:\t\t%s%s%s\n", 352 (device->device_flags & RTDM_EXCLUSIVE) ? 353 "EXCLUSIVE " : "", 354 (device->device_flags & RTDM_NAMED_DEVICE) ? 355 "NAMED_DEVICE " : "", 356 (device->device_flags & RTDM_PROTOCOL_DEVICE) ? 357 "PROTOCOL_DEVICE " : ""); 358 359 xnvfile_printf(it, "lock count:\t%d\n", 360 atomic_read(&device->reserved.refcount)); 361 362 up(&nrt_dev_lock); 363 return 0; 364 } 365 366 static struct xnvfile_regular_ops devinfo_vfile_ops = { 367 .show = devinfo_vfile_show, 368 }; 369 370 int rtdm_proc_register_device(struct rtdm_device *device) 371 { 372 int ret; 373 374 ret = xnvfile_init_dir(device->proc_name, 375 &device->vfroot, &rtdm_vfroot); 376 if (ret) 377 goto err_out; 378 379 memset(&device->info_vfile, 0, sizeof(device->info_vfile)); 380 device->info_vfile.ops = &devinfo_vfile_ops; 381 382 ret = xnvfile_init_regular("information", &device->info_vfile, 383 &device->vfroot); 384 if (ret) { 385 xnvfile_destroy_dir(&device->vfroot); 386 goto err_out; 387 } 388 389 xnvfile_priv(&device->info_vfile) = device; 390 391 return 0; 392 393 err_out: 394 xnlogerr("RTDM: error while creating device vfile\n"); 395 return ret; 396 } 397 398 void rtdm_proc_unregister_device(struct rtdm_device *device) 399 { 400 xnvfile_destroy_regular(&device->info_vfile); 401 xnvfile_destroy_dir(&device->vfroot); 402 } 403 404 int __init rtdm_proc_init(void) 405 { 406 int ret; 407 408 /* Initialise vfiles */ 409 // ret = xnvfile_init_root(); /proc/rtai is initted elsewhere 410 ret = xnvfile_init_dir("rtai/rtdm", &rtdm_vfroot, &nkvfroot); 411 if (ret) 412 goto error; 413 414 ret = xnvfile_init_regular("named_devices", &named_vfile, &rtdm_vfroot); 415 if (ret) 416 goto error; 417 418 ret = xnvfile_init_regular("protocol_devices", &proto_vfile, &rtdm_vfroot); 419 if (ret) 420 goto error; 421 422 ret = xnvfile_init_regular("open_fildes", &openfd_vfile, &rtdm_vfroot); 423 if (ret) 424 goto error; 425 426 ret = xnvfile_init_regular("fildes", &allfd_vfile, &rtdm_vfroot); 427 if (ret) 428 goto error; 429 430 return 0; 431 432 error: 433 rtdm_proc_cleanup(); 434 return ret; 435 } 436 437 void rtdm_proc_cleanup(void) 438 { 439 xnvfile_destroy_regular(&allfd_vfile); 440 xnvfile_destroy_regular(&openfd_vfile); 441 xnvfile_destroy_regular(&proto_vfile); 442 xnvfile_destroy_regular(&named_vfile); 443 xnvfile_destroy_dir(&rtdm_vfroot); 444 // xnvfile_destroy_root(); /proc/rtai is destroyed elsewhere 445 }