|
|
1.1 ! root 1: /* ! 2: * pcmcia-socket `device' driver ! 3: * ! 4: * Copyright (C) 2006, 2007 Free Software Foundation, Inc. ! 5: * Written by Stefan Siegl <[email protected]>. ! 6: * ! 7: * This file is part of GNU Mach. ! 8: * ! 9: * This program is free software; you can redistribute it and/or modify ! 10: * it under the terms of the GNU General Public License as published by ! 11: * the Free Software Foundation; either version 2, or (at your option) ! 12: * any later version. ! 13: * ! 14: * This program is distributed in the hope that it will be useful, ! 15: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 17: * GNU General Public License for more details. ! 18: * ! 19: * You should have received a copy of the GNU General Public License ! 20: * along with this program; if not, write to the Free Software ! 21: * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ! 22: */ ! 23: ! 24: /* This file is included from linux/pcmcia-cs/modules/ds.c. */ ! 25: ! 26: /* ! 27: * Prepare the namespace for inclusion of Mach header files. ! 28: */ ! 29: ! 30: #undef PAGE_SHIFT ! 31: ! 32: /* ! 33: * This is really ugly. But this is glue code, so... It's about the `kfree' ! 34: * symbols in <linux/malloc.h> and <kern/kalloc.h>. ! 35: */ ! 36: #undef kfree ! 37: ! 38: /* ! 39: * <kern/sched_prim.h> defines another event_t which is not used in this ! 40: * file, so name it mach_event_t to avoid a clash. ! 41: */ ! 42: #define event_t mach_event_t ! 43: #include <kern/sched_prim.h> ! 44: #undef event_t ! 45: ! 46: #include <mach/port.h> ! 47: #include <mach/notify.h> ! 48: #include <mach/mig_errors.h> ! 49: ! 50: #include <ipc/ipc_port.h> ! 51: #include <ipc/ipc_space.h> ! 52: ! 53: #include <device/device_types.h> ! 54: #include <device/device_port.h> ! 55: #include <device/io_req.h> ! 56: #include <device/ds_routines.h> ! 57: ! 58: #include <device/device_emul.h> ! 59: ! 60: #include <device/device_reply.user.h> ! 61: ! 62: /* Eliminate the queue_empty macro from Mach header files. */ ! 63: #undef queue_empty ! 64: ! 65: struct device_emulation_ops linux_pcmcia_emulation_ops; ! 66: ! 67: /* ! 68: * We have our very own device emulation stack because we need to carry a ! 69: * pointer from the open call via read until the final close call: a ! 70: * pointer to the user's event queue. ! 71: */ ! 72: struct mach_socket_device { ! 73: /* ! 74: * Pointer to the mach_device we have allocated. This must be the ! 75: * first entry in this struct, in order to be able to cast to ! 76: * mach_device. ! 77: */ ! 78: struct mach_device mach_dev; ! 79: ! 80: /* ! 81: * Pointer to the user info of pcmcia data services. ! 82: */ ! 83: user_info_t *user; ! 84: ! 85: /* ! 86: * Cache for carrying data from set_status to get_status calls. This ! 87: * is needed for write ioctls. ! 88: */ ! 89: ds_ioctl_arg_t carry; ! 90: }; ! 91: ! 92: ! 93: static void ! 94: ds_device_deallocate(void *p) ! 95: { ! 96: mach_device_t device = (mach_device_t) p; ! 97: ! 98: simple_lock(&device->ref_lock); ! 99: if (--device->ref_count > 0) ! 100: { ! 101: simple_unlock(&device->ref_lock); ! 102: return; ! 103: } ! 104: ! 105: simple_unlock(&device->ref_lock); ! 106: ! 107: /* ! 108: * do what the original ds_release would do, ... ! 109: */ ! 110: socket_t i = device->dev_number; ! 111: socket_info_t *s; ! 112: user_info_t *user, **link; ! 113: ! 114: s = &socket_table[i]; ! 115: user = ((struct mach_socket_device *) device)->user; ! 116: ! 117: /* allow to access the device again ... */ ! 118: if(device->flag & D_WRITE) ! 119: s->state &= ~SOCKET_BUSY; ! 120: ! 121: /* Unlink user data structure */ ! 122: for (link = &s->user; *link; link = &(*link)->next) ! 123: if (*link == user) break; ! 124: ! 125: if(link) ! 126: { ! 127: *link = user->next; ! 128: user->user_magic = 0; ! 129: linux_kfree(user); ! 130: } ! 131: ! 132: /* now finally reap the device */ ! 133: linux_kfree(device); ! 134: } ! 135: ! 136: /* ! 137: * Return the send right associated with this socket device incarnation. ! 138: */ ! 139: static ipc_port_t ! 140: dev_to_port(void *d) ! 141: { ! 142: struct mach_device *dev = d; ! 143: ! 144: if(! dev) ! 145: return IP_NULL; ! 146: ! 147: ipc_port_t port = ipc_port_make_send(dev->port); ! 148: ! 149: ds_device_deallocate(dev); ! 150: return port; ! 151: } ! 152: ! 153: ! 154: static inline int ! 155: atoi(const char *ptr) ! 156: { ! 157: if(! ptr) ! 158: return 0; ! 159: ! 160: int i = 0; ! 161: while(*ptr >= '0' && *ptr <= '9') ! 162: i = i * 10 + *(ptr ++) - '0'; ! 163: ! 164: return i; ! 165: } ! 166: ! 167: ! 168: /* ! 169: * Try to open the per-socket pseudo device `socket%d'. ! 170: */ ! 171: static io_return_t ! 172: device_open (ipc_port_t reply_port, mach_msg_type_name_t reply_port_type, ! 173: dev_mode_t mode, char *name, device_t *devp /* out */) ! 174: { ! 175: if(! socket_table) ! 176: return D_NO_SUCH_DEVICE; ! 177: ! 178: if(strlen(name) < 7 || strncmp(name, "socket", 6)) ! 179: return D_NO_SUCH_DEVICE; ! 180: ! 181: socket_t i = atoi(name + 6); ! 182: if(i >= MAX_SOCKS || i >= sockets) ! 183: return D_NO_SUCH_DEVICE; ! 184: ! 185: io_return_t err = D_SUCCESS; ! 186: ! 187: struct mach_device *dev; ! 188: dev = linux_kmalloc(sizeof(struct mach_socket_device), GFP_KERNEL); ! 189: if(! dev) ! 190: { ! 191: err = D_NO_MEMORY; ! 192: goto out; ! 193: } ! 194: ! 195: memset(dev, 0, sizeof(struct mach_socket_device)); ! 196: mach_device_reference(dev); ! 197: ! 198: /* now do, what ds_open would do if it would be in charge */ ! 199: socket_info_t *s = &socket_table[i]; ! 200: ! 201: if(mode & D_WRITE) ! 202: { ! 203: if(s->state & SOCKET_BUSY) ! 204: { ! 205: err = D_ALREADY_OPEN; ! 206: goto out; ! 207: } ! 208: else ! 209: s->state |= SOCKET_BUSY; ! 210: } ! 211: ! 212: user_info_t *user = linux_kmalloc(sizeof(user_info_t), GFP_KERNEL); ! 213: if(! user) ! 214: { ! 215: err = D_NO_MEMORY; ! 216: goto out; ! 217: } ! 218: ! 219: user->event_tail = user->event_head = 0; ! 220: user->next = s->user; ! 221: user->user_magic = USER_MAGIC; ! 222: s->user = user; ! 223: ! 224: ((struct mach_socket_device *) dev)->user = user; ! 225: ! 226: if(s->state & SOCKET_PRESENT) ! 227: queue_event(user, CS_EVENT_CARD_INSERTION); ! 228: ! 229: /* just set up the rest of our mach_device now ... */ ! 230: dev->dev.emul_ops = &linux_pcmcia_emulation_ops; ! 231: dev->dev.emul_data = dev; ! 232: ! 233: dev->dev_number = i; ! 234: dev->flag = mode; ! 235: ! 236: dev->port = ipc_port_alloc_kernel(); ! 237: if(dev->port == IP_NULL) ! 238: { ! 239: err = KERN_RESOURCE_SHORTAGE; ! 240: goto out; ! 241: } ! 242: ! 243: mach_device_reference(dev); ! 244: ipc_kobject_set(dev->port, (ipc_kobject_t) &dev->dev, IKOT_DEVICE); ! 245: ! 246: /* request no-senders notifications on device port */ ! 247: ipc_port_t notify = ipc_port_make_sonce(dev->port); ! 248: ip_lock(dev->port); ! 249: ipc_port_nsrequest(dev->port, 1, notify, ¬ify); ! 250: assert (notify == IP_NULL); ! 251: ! 252: out: ! 253: if(err) ! 254: { ! 255: if(dev) ! 256: { ! 257: if(dev->port != IP_NULL) ! 258: { ! 259: ipc_kobject_set(dev->port, IKO_NULL, IKOT_NONE); ! 260: ipc_port_dealloc_kernel(dev->port); ! 261: } ! 262: ! 263: linux_kfree(dev); ! 264: dev = NULL; ! 265: } ! 266: } ! 267: else ! 268: dev->state = DEV_STATE_OPEN; ! 269: ! 270: *devp = &dev->dev; ! 271: ! 272: if (IP_VALID (reply_port)) ! 273: ds_device_open_reply(reply_port, reply_port_type, ! 274: err, dev_to_port(dev)); ! 275: return MIG_NO_REPLY; ! 276: } ! 277: ! 278: ! 279: /* ! 280: * Close the device DEV. ! 281: */ ! 282: static int ! 283: device_close (void *devp) ! 284: { ! 285: struct mach_device *dev = (struct mach_device *) devp; ! 286: ! 287: dev->state = DEV_STATE_CLOSING; ! 288: ! 289: /* check whether there is a blocked read request pending, ! 290: * in that case, abort that one before closing ! 291: */ ! 292: while(dev->ref_count > 2) ! 293: { ! 294: socket_t i = dev->dev_number; ! 295: socket_info_t *s = &socket_table[i]; ! 296: wake_up_interruptible(&s->queue); ! 297: ! 298: /* wait for device_read to exit */ ! 299: return D_INVALID_OPERATION; ! 300: } ! 301: ! 302: dev_port_remove(dev); ! 303: ipc_port_dealloc_kernel(dev->port); ! 304: ! 305: return 0; ! 306: } ! 307: ! 308: static io_return_t ! 309: device_read(void *d, ipc_port_t reply_port, ! 310: mach_msg_type_name_t reply_port_type, dev_mode_t mode, ! 311: recnum_t recnum, int bytes_wanted, ! 312: io_buf_ptr_t *data, unsigned int *data_count) ! 313: { ! 314: struct mach_device *dev = (struct mach_device *) d; ! 315: ! 316: if(dev->state != DEV_STATE_OPEN) ! 317: return D_NO_SUCH_DEVICE; ! 318: ! 319: if(! IP_VALID(reply_port)) { ! 320: printk(KERN_INFO "ds: device_read: invalid reply port.\n"); ! 321: return (MIG_NO_REPLY); /* no sense in doing anything */ ! 322: } ! 323: ! 324: /* prepare an io request structure */ ! 325: io_req_t ior; ! 326: io_req_alloc(ior, 0); ! 327: ! 328: ior->io_device = dev; ! 329: ior->io_unit = dev->dev_number; ! 330: ior->io_op = IO_READ | IO_CALL; ! 331: ior->io_mode = mode; ! 332: ior->io_recnum = recnum; ! 333: ior->io_data = 0; ! 334: ior->io_count = bytes_wanted; ! 335: ior->io_alloc_size = 0; ! 336: ior->io_residual = 0; ! 337: ior->io_error = 0; ! 338: ior->io_done = ds_read_done; ! 339: ior->io_reply_port = reply_port; ! 340: ior->io_reply_port_type = reply_port_type; ! 341: ! 342: /* ! 343: * The ior keeps an extra reference for the device. ! 344: */ ! 345: mach_device_reference(dev); ! 346: ! 347: /* do the read finally */ ! 348: io_return_t result = D_SUCCESS; ! 349: ! 350: result = device_read_alloc(ior, ior->io_count); ! 351: if(result != KERN_SUCCESS) ! 352: goto out; ! 353: ! 354: socket_t i = dev->dev_number; ! 355: socket_info_t *s = &socket_table[i]; ! 356: user_info_t *user = ((struct mach_socket_device *) dev)->user; ! 357: ! 358: if(ior->io_count < 4) ! 359: return D_INVALID_SIZE; ! 360: ! 361: if(CHECK_USER(user)) ! 362: { ! 363: result = D_IO_ERROR; ! 364: goto out; ! 365: } ! 366: ! 367: while(queue_empty(user)) ! 368: { ! 369: if(ior->io_mode & D_NOWAIT) ! 370: { ! 371: result = D_WOULD_BLOCK; ! 372: goto out; ! 373: } ! 374: else ! 375: interruptible_sleep_on(&s->queue); ! 376: ! 377: if(dev->state == DEV_STATE_CLOSING) ! 378: { ! 379: result = D_DEVICE_DOWN; ! 380: goto out; ! 381: } ! 382: } ! 383: ! 384: event_t ev = get_queued_event(user); ! 385: memcpy(ior->io_data, &ev, sizeof(event_t)); ! 386: ! 387: ior->io_residual = ior->io_count - sizeof(event_t); ! 388: ! 389: out: ! 390: /* ! 391: * Return result via ds_read_done. ! 392: */ ! 393: ior->io_error = result; ! 394: (void) ds_read_done(ior); ! 395: io_req_free(ior); ! 396: ! 397: return (MIG_NO_REPLY); /* reply has already been sent. */ ! 398: } ! 399: ! 400: ! 401: static io_return_t ! 402: device_set_status(void *d, dev_flavor_t req, dev_status_t arg, ! 403: mach_msg_type_number_t sz) ! 404: { ! 405: struct mach_socket_device *dev = (struct mach_socket_device *) d; ! 406: ! 407: if(sz * sizeof(int) > sizeof(ds_ioctl_arg_t)) ! 408: return D_INVALID_OPERATION; ! 409: ! 410: if(dev->mach_dev.state != DEV_STATE_OPEN) ! 411: return D_NO_SUCH_DEVICE; ! 412: ! 413: unsigned int ioctl_sz = (req & IOCSIZE_MASK) >> IOCSIZE_SHIFT; ! 414: memcpy(&dev->carry, arg, ioctl_sz); ! 415: ! 416: return D_SUCCESS; ! 417: } ! 418: ! 419: static io_return_t ! 420: device_get_status(void *d, dev_flavor_t req, dev_status_t arg, ! 421: mach_msg_type_number_t *sz) ! 422: { ! 423: struct mach_socket_device *dev = (struct mach_socket_device *) d; ! 424: ! 425: if(dev->mach_dev.state != DEV_STATE_OPEN) ! 426: return D_NO_SUCH_DEVICE; ! 427: ! 428: struct inode inode; ! 429: inode.i_rdev = dev->mach_dev.dev_number; ! 430: int ret = ds_ioctl(&inode, NULL, req, (u_long) &dev->carry); ! 431: ! 432: if(ret) ! 433: return D_IO_ERROR; ! 434: ! 435: unsigned int ioctl_sz = (req & IOCSIZE_MASK) >> IOCSIZE_SHIFT; ! 436: if(req & IOC_OUT) memcpy(arg, &dev->carry, ioctl_sz); ! 437: ! 438: return D_SUCCESS; ! 439: } ! 440: ! 441: ! 442: struct device_emulation_ops linux_pcmcia_emulation_ops = ! 443: { ! 444: (void*) mach_device_reference, ! 445: ds_device_deallocate, ! 446: dev_to_port, ! 447: device_open, ! 448: device_close, ! 449: NULL, /* device_write */ ! 450: NULL, /* write_inband */ ! 451: device_read, ! 452: NULL, /* read_inband */ ! 453: device_set_status, ! 454: device_get_status, ! 455: NULL, /* set_filter */ ! 456: NULL, /* map */ ! 457: NULL, /* no_senders */ ! 458: NULL, /* write_trap */ ! 459: NULL /* writev_trap */ ! 460: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.