Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.4 521 lines 13 kB view raw
1/* ----------------------------------------------------------------------------- 2 * Copyright (c) 2011 Ozmo Inc 3 * Released under the GNU General Public License Version 2 (GPLv2). 4 * ----------------------------------------------------------------------------- 5 */ 6#include <linux/module.h> 7#include <linux/fs.h> 8#include <linux/cdev.h> 9#include <linux/uaccess.h> 10#include <linux/netdevice.h> 11#include <linux/poll.h> 12#include <linux/sched.h> 13#include "ozconfig.h" 14#include "ozprotocol.h" 15#include "oztrace.h" 16#include "ozappif.h" 17#include "ozeltbuf.h" 18#include "ozpd.h" 19#include "ozproto.h" 20#include "ozevent.h" 21/*------------------------------------------------------------------------------ 22 */ 23#define OZ_RD_BUF_SZ 256 24struct oz_cdev { 25 dev_t devnum; 26 struct cdev cdev; 27 wait_queue_head_t rdq; 28 spinlock_t lock; 29 u8 active_addr[ETH_ALEN]; 30 struct oz_pd *active_pd; 31}; 32 33/* Per PD context for the serial service stored in the PD. */ 34struct oz_serial_ctx { 35 atomic_t ref_count; 36 u8 tx_seq_num; 37 u8 rx_seq_num; 38 u8 rd_buf[OZ_RD_BUF_SZ]; 39 int rd_in; 40 int rd_out; 41}; 42/*------------------------------------------------------------------------------ 43 */ 44int g_taction; 45/*------------------------------------------------------------------------------ 46 */ 47static struct oz_cdev g_cdev; 48/*------------------------------------------------------------------------------ 49 * Context: process and softirq 50 */ 51static struct oz_serial_ctx *oz_cdev_claim_ctx(struct oz_pd *pd) 52{ 53 struct oz_serial_ctx *ctx; 54 spin_lock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); 55 ctx = (struct oz_serial_ctx *)pd->app_ctx[OZ_APPID_SERIAL-1]; 56 if (ctx) 57 atomic_inc(&ctx->ref_count); 58 spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); 59 return ctx; 60} 61/*------------------------------------------------------------------------------ 62 * Context: softirq or process 63 */ 64static void oz_cdev_release_ctx(struct oz_serial_ctx *ctx) 65{ 66 if (atomic_dec_and_test(&ctx->ref_count)) { 67 oz_trace("Dealloc serial context.\n"); 68 kfree(ctx); 69 } 70} 71/*------------------------------------------------------------------------------ 72 * Context: process 73 */ 74int oz_cdev_open(struct inode *inode, struct file *filp) 75{ 76 struct oz_cdev *dev; 77 oz_trace("oz_cdev_open()\n"); 78 oz_trace("major = %d minor = %d\n", imajor(inode), iminor(inode)); 79 dev = container_of(inode->i_cdev, struct oz_cdev, cdev); 80 filp->private_data = dev; 81 return 0; 82} 83/*------------------------------------------------------------------------------ 84 * Context: process 85 */ 86int oz_cdev_release(struct inode *inode, struct file *filp) 87{ 88 oz_trace("oz_cdev_release()\n"); 89 return 0; 90} 91/*------------------------------------------------------------------------------ 92 * Context: process 93 */ 94ssize_t oz_cdev_read(struct file *filp, char __user *buf, size_t count, 95 loff_t *fpos) 96{ 97 int n; 98 int ix; 99 100 struct oz_pd *pd; 101 struct oz_serial_ctx *ctx = 0; 102 103 spin_lock_bh(&g_cdev.lock); 104 pd = g_cdev.active_pd; 105 if (pd) 106 oz_pd_get(pd); 107 spin_unlock_bh(&g_cdev.lock); 108 if (pd == 0) 109 return -1; 110 ctx = oz_cdev_claim_ctx(pd); 111 if (ctx == 0) 112 goto out2; 113 n = ctx->rd_in - ctx->rd_out; 114 if (n < 0) 115 n += OZ_RD_BUF_SZ; 116 if (count > n) 117 count = n; 118 ix = ctx->rd_out; 119 n = OZ_RD_BUF_SZ - ix; 120 if (n > count) 121 n = count; 122 if (copy_to_user(buf, &ctx->rd_buf[ix], n)) { 123 count = 0; 124 goto out1; 125 } 126 ix += n; 127 if (ix == OZ_RD_BUF_SZ) 128 ix = 0; 129 if (n < count) { 130 if (copy_to_user(&buf[n], ctx->rd_buf, count-n)) { 131 count = 0; 132 goto out1; 133 } 134 ix = count-n; 135 } 136 ctx->rd_out = ix; 137out1: 138 oz_cdev_release_ctx(ctx); 139out2: 140 oz_pd_put(pd); 141 return count; 142} 143/*------------------------------------------------------------------------------ 144 * Context: process 145 */ 146ssize_t oz_cdev_write(struct file *filp, const char __user *buf, size_t count, 147 loff_t *fpos) 148{ 149 struct oz_pd *pd; 150 struct oz_elt_buf *eb; 151 struct oz_elt_info *ei = 0; 152 struct oz_elt *elt; 153 struct oz_app_hdr *app_hdr; 154 struct oz_serial_ctx *ctx; 155 156 spin_lock_bh(&g_cdev.lock); 157 pd = g_cdev.active_pd; 158 if (pd) 159 oz_pd_get(pd); 160 spin_unlock_bh(&g_cdev.lock); 161 if (pd == 0) 162 return -1; 163 eb = &pd->elt_buff; 164 ei = oz_elt_info_alloc(eb); 165 if (ei == 0) { 166 count = 0; 167 goto out; 168 } 169 elt = (struct oz_elt *)ei->data; 170 app_hdr = (struct oz_app_hdr *)(elt+1); 171 elt->length = sizeof(struct oz_app_hdr) + count; 172 elt->type = OZ_ELT_APP_DATA; 173 ei->app_id = OZ_APPID_SERIAL; 174 ei->length = elt->length + sizeof(struct oz_elt); 175 app_hdr->app_id = OZ_APPID_SERIAL; 176 if (copy_from_user(app_hdr+1, buf, count)) 177 goto out; 178 spin_lock_bh(&pd->app_lock[OZ_APPID_USB-1]); 179 ctx = (struct oz_serial_ctx *)pd->app_ctx[OZ_APPID_SERIAL-1]; 180 if (ctx) { 181 app_hdr->elt_seq_num = ctx->tx_seq_num++; 182 if (ctx->tx_seq_num == 0) 183 ctx->tx_seq_num = 1; 184 spin_lock(&eb->lock); 185 if (oz_queue_elt_info(eb, 0, 0, ei) == 0) 186 ei = 0; 187 spin_unlock(&eb->lock); 188 } 189 spin_unlock_bh(&pd->app_lock[OZ_APPID_USB-1]); 190out: 191 if (ei) { 192 count = 0; 193 spin_lock_bh(&eb->lock); 194 oz_elt_info_free(eb, ei); 195 spin_unlock_bh(&eb->lock); 196 } 197 oz_pd_put(pd); 198 return count; 199} 200/*------------------------------------------------------------------------------ 201 * Context: process 202 */ 203static int oz_set_active_pd(u8 *addr) 204{ 205 int rc = 0; 206 struct oz_pd *pd; 207 struct oz_pd *old_pd; 208 pd = oz_pd_find(addr); 209 if (pd) { 210 spin_lock_bh(&g_cdev.lock); 211 memcpy(g_cdev.active_addr, addr, ETH_ALEN); 212 old_pd = g_cdev.active_pd; 213 g_cdev.active_pd = pd; 214 spin_unlock_bh(&g_cdev.lock); 215 if (old_pd) 216 oz_pd_put(old_pd); 217 } else { 218 if (!memcmp(addr, "\0\0\0\0\0\0", sizeof(addr))) { 219 spin_lock_bh(&g_cdev.lock); 220 pd = g_cdev.active_pd; 221 g_cdev.active_pd = 0; 222 memset(g_cdev.active_addr, 0, 223 sizeof(g_cdev.active_addr)); 224 spin_unlock_bh(&g_cdev.lock); 225 if (pd) 226 oz_pd_put(pd); 227 } else { 228 rc = -1; 229 } 230 } 231 return rc; 232} 233/*------------------------------------------------------------------------------ 234 * Context: process 235 */ 236long oz_cdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 237{ 238 int rc = 0; 239 if (_IOC_TYPE(cmd) != OZ_IOCTL_MAGIC) 240 return -ENOTTY; 241 if (_IOC_NR(cmd) > OZ_IOCTL_MAX) 242 return -ENOTTY; 243 if (_IOC_DIR(cmd) & _IOC_READ) 244 rc = !access_ok(VERIFY_WRITE, (void __user *)arg, 245 _IOC_SIZE(cmd)); 246 else if (_IOC_DIR(cmd) & _IOC_WRITE) 247 rc = !access_ok(VERIFY_READ, (void __user *)arg, 248 _IOC_SIZE(cmd)); 249 if (rc) 250 return -EFAULT; 251 switch (cmd) { 252 case OZ_IOCTL_GET_PD_LIST: { 253 struct oz_pd_list list; 254 oz_trace("OZ_IOCTL_GET_PD_LIST\n"); 255 list.count = oz_get_pd_list(list.addr, OZ_MAX_PDS); 256 if (copy_to_user((void __user *)arg, &list, 257 sizeof(list))) 258 return -EFAULT; 259 } 260 break; 261 case OZ_IOCTL_SET_ACTIVE_PD: { 262 u8 addr[ETH_ALEN]; 263 oz_trace("OZ_IOCTL_SET_ACTIVE_PD\n"); 264 if (copy_from_user(addr, (void __user *)arg, ETH_ALEN)) 265 return -EFAULT; 266 rc = oz_set_active_pd(addr); 267 } 268 break; 269 case OZ_IOCTL_GET_ACTIVE_PD: { 270 u8 addr[ETH_ALEN]; 271 oz_trace("OZ_IOCTL_GET_ACTIVE_PD\n"); 272 spin_lock_bh(&g_cdev.lock); 273 memcpy(addr, g_cdev.active_addr, ETH_ALEN); 274 spin_unlock_bh(&g_cdev.lock); 275 if (copy_to_user((void __user *)arg, addr, ETH_ALEN)) 276 return -EFAULT; 277 } 278 break; 279#ifdef WANT_EVENT_TRACE 280 case OZ_IOCTL_CLEAR_EVENTS: 281 oz_events_clear(); 282 break; 283 case OZ_IOCTL_GET_EVENTS: 284 rc = oz_events_copy((void __user *)arg); 285 break; 286 case OZ_IOCTL_SET_EVENT_MASK: 287 if (copy_from_user(&g_evt_mask, (void __user *)arg, 288 sizeof(unsigned long))) { 289 return -EFAULT; 290 } 291 break; 292#endif /* WANT_EVENT_TRACE */ 293 case OZ_IOCTL_ADD_BINDING: 294 case OZ_IOCTL_REMOVE_BINDING: { 295 struct oz_binding_info b; 296 if (copy_from_user(&b, (void __user *)arg, 297 sizeof(struct oz_binding_info))) { 298 return -EFAULT; 299 } 300 /* Make sure name is null terminated. */ 301 b.name[OZ_MAX_BINDING_LEN-1] = 0; 302 if (cmd == OZ_IOCTL_ADD_BINDING) 303 oz_binding_add(b.name); 304 else 305 oz_binding_remove(b.name); 306 } 307 break; 308 } 309 return rc; 310} 311/*------------------------------------------------------------------------------ 312 * Context: process 313 */ 314unsigned int oz_cdev_poll(struct file *filp, poll_table *wait) 315{ 316 unsigned int ret = 0; 317 struct oz_cdev *dev = filp->private_data; 318 oz_trace("Poll called wait = %p\n", wait); 319 spin_lock_bh(&dev->lock); 320 if (dev->active_pd) { 321 struct oz_serial_ctx *ctx = oz_cdev_claim_ctx(dev->active_pd); 322 if (ctx) { 323 if (ctx->rd_in != ctx->rd_out) 324 ret |= POLLIN | POLLRDNORM; 325 oz_cdev_release_ctx(ctx); 326 } 327 } 328 spin_unlock_bh(&dev->lock); 329 if (wait) 330 poll_wait(filp, &dev->rdq, wait); 331 return ret; 332} 333/*------------------------------------------------------------------------------ 334 */ 335const struct file_operations oz_fops = { 336 .owner = THIS_MODULE, 337 .open = oz_cdev_open, 338 .release = oz_cdev_release, 339 .read = oz_cdev_read, 340 .write = oz_cdev_write, 341 .unlocked_ioctl = oz_cdev_ioctl, 342 .poll = oz_cdev_poll 343}; 344/*------------------------------------------------------------------------------ 345 * Context: process 346 */ 347int oz_cdev_register(void) 348{ 349 int err; 350 memset(&g_cdev, 0, sizeof(g_cdev)); 351 err = alloc_chrdev_region(&g_cdev.devnum, 0, 1, "ozwpan"); 352 if (err < 0) 353 return err; 354 oz_trace("Alloc dev number %d:%d\n", MAJOR(g_cdev.devnum), 355 MINOR(g_cdev.devnum)); 356 cdev_init(&g_cdev.cdev, &oz_fops); 357 g_cdev.cdev.owner = THIS_MODULE; 358 g_cdev.cdev.ops = &oz_fops; 359 spin_lock_init(&g_cdev.lock); 360 init_waitqueue_head(&g_cdev.rdq); 361 err = cdev_add(&g_cdev.cdev, g_cdev.devnum, 1); 362 return 0; 363} 364/*------------------------------------------------------------------------------ 365 * Context: process 366 */ 367int oz_cdev_deregister(void) 368{ 369 cdev_del(&g_cdev.cdev); 370 unregister_chrdev_region(g_cdev.devnum, 1); 371 return 0; 372} 373/*------------------------------------------------------------------------------ 374 * Context: process 375 */ 376int oz_cdev_init(void) 377{ 378 oz_event_log(OZ_EVT_SERVICE, 1, OZ_APPID_SERIAL, 0, 0); 379 oz_app_enable(OZ_APPID_SERIAL, 1); 380 return 0; 381} 382/*------------------------------------------------------------------------------ 383 * Context: process 384 */ 385void oz_cdev_term(void) 386{ 387 oz_event_log(OZ_EVT_SERVICE, 2, OZ_APPID_SERIAL, 0, 0); 388 oz_app_enable(OZ_APPID_SERIAL, 0); 389} 390/*------------------------------------------------------------------------------ 391 * Context: softirq-serialized 392 */ 393int oz_cdev_start(struct oz_pd *pd, int resume) 394{ 395 struct oz_serial_ctx *ctx; 396 struct oz_serial_ctx *old_ctx = 0; 397 oz_event_log(OZ_EVT_SERVICE, 3, OZ_APPID_SERIAL, 0, resume); 398 if (resume) { 399 oz_trace("Serial service resumed.\n"); 400 return 0; 401 } 402 ctx = kzalloc(sizeof(struct oz_serial_ctx), GFP_ATOMIC); 403 if (ctx == 0) 404 return -ENOMEM; 405 atomic_set(&ctx->ref_count, 1); 406 ctx->tx_seq_num = 1; 407 spin_lock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); 408 old_ctx = pd->app_ctx[OZ_APPID_SERIAL-1]; 409 if (old_ctx) { 410 spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); 411 kfree(ctx); 412 } else { 413 pd->app_ctx[OZ_APPID_SERIAL-1] = ctx; 414 spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); 415 } 416 spin_lock(&g_cdev.lock); 417 if ((g_cdev.active_pd == 0) && 418 (memcmp(pd->mac_addr, g_cdev.active_addr, ETH_ALEN) == 0)) { 419 oz_pd_get(pd); 420 g_cdev.active_pd = pd; 421 oz_trace("Active PD arrived.\n"); 422 } 423 spin_unlock(&g_cdev.lock); 424 oz_trace("Serial service started.\n"); 425 return 0; 426} 427/*------------------------------------------------------------------------------ 428 * Context: softirq or process 429 */ 430void oz_cdev_stop(struct oz_pd *pd, int pause) 431{ 432 struct oz_serial_ctx *ctx; 433 oz_event_log(OZ_EVT_SERVICE, 4, OZ_APPID_SERIAL, 0, pause); 434 if (pause) { 435 oz_trace("Serial service paused.\n"); 436 return; 437 } 438 spin_lock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); 439 ctx = (struct oz_serial_ctx *)pd->app_ctx[OZ_APPID_SERIAL-1]; 440 pd->app_ctx[OZ_APPID_SERIAL-1] = 0; 441 spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]); 442 if (ctx) 443 oz_cdev_release_ctx(ctx); 444 spin_lock(&g_cdev.lock); 445 if (pd == g_cdev.active_pd) 446 g_cdev.active_pd = 0; 447 else 448 pd = 0; 449 spin_unlock(&g_cdev.lock); 450 if (pd) { 451 oz_pd_put(pd); 452 oz_trace("Active PD departed.\n"); 453 } 454 oz_trace("Serial service stopped.\n"); 455} 456/*------------------------------------------------------------------------------ 457 * Context: softirq-serialized 458 */ 459void oz_cdev_rx(struct oz_pd *pd, struct oz_elt *elt) 460{ 461 struct oz_serial_ctx *ctx; 462 struct oz_app_hdr *app_hdr; 463 u8 *data; 464 int len; 465 int space; 466 int copy_sz; 467 int ix; 468 469 ctx = oz_cdev_claim_ctx(pd); 470 if (ctx == 0) { 471 oz_trace("Cannot claim serial context.\n"); 472 return; 473 } 474 475 app_hdr = (struct oz_app_hdr *)(elt+1); 476 /* If sequence number is non-zero then check it is not a duplicate. 477 */ 478 if (app_hdr->elt_seq_num != 0) { 479 if (((ctx->rx_seq_num - app_hdr->elt_seq_num) & 0x80) == 0) { 480 /* Reject duplicate element. */ 481 oz_trace("Duplicate element:%02x %02x\n", 482 app_hdr->elt_seq_num, ctx->rx_seq_num); 483 goto out; 484 } 485 } 486 ctx->rx_seq_num = app_hdr->elt_seq_num; 487 len = elt->length - sizeof(struct oz_app_hdr); 488 data = ((u8 *)(elt+1)) + sizeof(struct oz_app_hdr); 489 if (len <= 0) 490 goto out; 491 space = ctx->rd_out - ctx->rd_in - 1; 492 if (space < 0) 493 space += OZ_RD_BUF_SZ; 494 if (len > space) { 495 oz_trace("Not enough space:%d %d\n", len, space); 496 len = space; 497 } 498 ix = ctx->rd_in; 499 copy_sz = OZ_RD_BUF_SZ - ix; 500 if (copy_sz > len) 501 copy_sz = len; 502 memcpy(&ctx->rd_buf[ix], data, copy_sz); 503 len -= copy_sz; 504 ix += copy_sz; 505 if (ix == OZ_RD_BUF_SZ) 506 ix = 0; 507 if (len) { 508 memcpy(ctx->rd_buf, data+copy_sz, len); 509 ix = len; 510 } 511 ctx->rd_in = ix; 512 wake_up(&g_cdev.rdq); 513out: 514 oz_cdev_release_ctx(ctx); 515} 516/*------------------------------------------------------------------------------ 517 * Context: softirq 518 */ 519void oz_cdev_heartbeat(struct oz_pd *pd) 520{ 521}