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